Commit f282917c authored by gwenn's avatar gwenn

Another try to support custom aggregate functions.

parent 02b7f855
Yet another SQLite binding based on:
- original [Russ Cox's](http://code.google.com/p/gosqlite/) implementation,
- the [Patrick Crosby's](https://github.com/patrickxb/fgosqlite/) fork.
This binding implements the "exp/sql/driver" interface but leaves dangling statements (see drivert_test).
This binding implements the "exp/sql/driver" interface.
Open supports flags.
Conn#Exec handles multiple statements (separated by semicolons) properly.
......@@ -15,7 +15,7 @@ Stmt#Scan uses native sqlite3_column_x methods.
Stmt#NamedScan is added. It's compliant with [go-dbi](https://github.com/thomaslee/go-dbi/).
Stmt#ScanByIndex/ScanByName are added to test NULL value.
Currently, the weak point of the binding is the *Scan methods:
Currently, the weak point of the binding is the *Scan* methods:
The original implementation is using this strategy:
- convert the stored value to a []byte by calling sqlite3_column_bytes,
- convert the bytes to the desired Go type with correct feedback in case of illegal conversion,
......@@ -23,9 +23,7 @@ The original implementation is using this strategy:
Using the native sqlite3_column_x implies:
- optimal conversion from the storage type to Go type (when they match),
- loosy conversion when types mismatch (select cast('M' as int); --> 0),
- NULL value cannot be returned, default value (0, false, "") is returned instead.
Maybe we should let the caller do the conversion.
- NULL value can be returned only for **type, otherwise a default value (0, false, "") is returned.
Misc:
Conn#Exists
......@@ -42,7 +40,7 @@ Conn#EnableLoadExtension/LoadExtension
Conn#IntegrityCheck
Stmt#ExecInsert/ExecUpdate
Stmt#BindParameterCount/BindParameterIndex/BindParameterName
Stmt#BindParameterCount/BindParameterIndex(name)/BindParameterName(index)
Stmt#ClearBindings
Stmt#ColumnCount/ColumnNames/ColumnIndex(name)/ColumnName(index)/ColumnType(index)
Stmt#ReadOnly
......
......@@ -326,16 +326,12 @@ func (c *Context) Value(i int) (value interface{}) {
return
}
type ScalarFunction func(ctx *Context, nArg int)
type FuncOrStep func(ctx *Context, nArg int)
type FinalFunction func(ctx *Context)
type DestroyFunctionData func(pApp interface{})
/*
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
*/
type sqliteFunction struct {
funcOrStep ScalarFunction
funcOrStep FuncOrStep
final FinalFunction
d DestroyFunctionData
pApp interface{}
......@@ -373,15 +369,26 @@ func goXFunc(scp, udfp, ctxp unsafe.Pointer, argc int, argv unsafe.Pointer) {
//export goXStep
func goXStep(scp, udfp unsafe.Pointer, argc int, argv unsafe.Pointer) {
//udf := (*sqliteFunction)(udfp)
//c := nil // FIXME
udf := (*sqliteFunction)(udfp)
var c *Context
c = (*Context)(C.sqlite3_aggregate_context((*C.sqlite3_context)(scp), C.int(unsafe.Sizeof(c))))
if c != nil {
c.sc = (*C.sqlite3_context)(scp)
c.argv = (**C.sqlite3_value)(argv)
udf.funcOrStep(c, argc)
c.argv = nil
}
}
//export goXFinal
func goXFinal(scp, udfp unsafe.Pointer) {
//udf := (*sqliteFunction)(udfp)
//c := nil // FIXME (*C.sqlite3_context)(scp)
//udf.final(c)
udf := (*sqliteFunction)(udfp)
c := (*Context)(C.sqlite3_aggregate_context((*C.sqlite3_context)(scp), 0))
if c != nil {
//c.sc = (*C.sqlite3_context)(scp)
udf.final(c)
}
}
//export goXDestroy
......@@ -395,7 +402,7 @@ func goXDestroy(pApp unsafe.Pointer) {
// Create or redefine SQL functions
// TODO Make possible to specify the preferred encoding
// Calls http://sqlite.org/c3ref/create_function.html
func (c *Conn) CreateScalarFunction(functionName string, nArg int, pApp interface{}, f ScalarFunction, d DestroyFunctionData) error {
func (c *Conn) CreateScalarFunction(functionName string, nArg int, pApp interface{}, f FuncOrStep, d DestroyFunctionData) error {
fname := C.CString(functionName)
defer C.free(unsafe.Pointer(fname))
if f == nil {
......@@ -413,16 +420,11 @@ func (c *Conn) CreateScalarFunction(functionName string, nArg int, pApp interfac
return c.error(C.goSqlite3CreateScalarFunction(c.db, fname, C.int(nArg), C.SQLITE_UTF8, unsafe.Pointer(udf)))
}
// Calls http://sqlite.org/c3ref/aggregate_context.html
func (c *Context) AggregateContext(nBytes int) interface{} {
return C.sqlite3_aggregate_context(c.sc, C.int(nBytes))
}
// Create or redefine SQL functions
// TODO Make possible to specify the preferred encoding
// Calls http://sqlite.org/c3ref/create_function.html
func (c *Conn) CreateAggregateFunction(functionName string, nArg int, pApp interface{},
step ScalarFunction, final FinalFunction, d DestroyFunctionData) error {
step FuncOrStep, final FinalFunction, d DestroyFunctionData) error {
fname := C.CString(functionName)
defer C.free(unsafe.Pointer(fname))
if step == nil {
......
......@@ -88,7 +88,7 @@ func TestRegexpFunction(t *testing.T) {
}
}
/*
func sumStep(ctx *Context, nArg int) {
nt := ctx.NumericType(0)
if nt == Integer || nt == Float {
......@@ -122,7 +122,6 @@ func TestSumFunction(t *testing.T) {
t.Errorf("Expected %d but got %d", 4, i)
}
}
*/
func randomFill(db *Conn, n int) {
db.Exec("DROP TABLE IF EXISTS test")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment