Commit f282917c authored by gwenn's avatar gwenn

Another try to support custom aggregate functions.

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