// RegisterFunc makes a Go function available as a SQLite function. // // The Go function can have arguments of the following types: any // numeric type except complex, bool, []byte, string and // interface{}. interface{} arguments are given the direct translation // of the SQLite data type: int64 for INTEGER, float64 for FLOAT, // []byte for BLOB, string for TEXT. // // The function can additionally be variadic, as long as the type of // the variadic argument is one of the above. // // If pure is true. SQLite will assume that the function's return // value depends only on its inputs, and make more aggressive // optimizations in its queries. // // See _example/go_custom_funcs for a detailed example. func (c *SQLiteConn) RegisterFunc(name string, impl interface{}, pure bool) error { var fi functionInfo fi.f = reflect.ValueOf(impl) t := fi.f.Type() if t.Kind() != reflect.Func { return errors.New("Non-function passed to RegisterFunc") } if t.NumOut() != 1 && t.NumOut() != 2 { return errors.New("SQLite functions must return 1 or 2 values") } if t.NumOut() == 2 && !t.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { return errors.New("Second return value of SQLite function must be error") } numArgs := t.NumIn() if t.IsVariadic() { numArgs-- } for i := 0; i < numArgs; i++ { conv, err := callbackArg(t.In(i)) if err != nil { return err } fi.argConverters = append(fi.argConverters, conv) } if t.IsVariadic() { conv, err := callbackArg(t.In(numArgs).Elem()) if err != nil { return err } fi.variadicConverter = conv // Pass -1 to sqlite so that it allows any number of // arguments. The call helper verifies that the minimum number // of arguments is present for variadic functions. numArgs = -1 } conv, err := callbackRet(t.Out(0)) if err != nil { return err } fi.retConverter = conv // fi must outlast the database connection, or we'll have dangling pointers. c.funcs = append(c.funcs, &fi) cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) opts := C.SQLITE_UTF8 if pure { opts |= C.SQLITE_DETERMINISTIC } rv := C._sqlite3_create_function(c.db, cname, C.int(numArgs), C.int(opts), C.uintptr_t(newHandle(c, &fi)), (*[0]byte)(unsafe.Pointer(C.callbackTrampoline)), nil, nil) if rv != C.SQLITE_OK { return c.lastError() } return nil }
// RegisterAggregator makes a Go type available as a SQLite aggregation function. // // Because aggregation is incremental, it's implemented in Go with a // type that has 2 methods: func Step(values) accumulates one row of // data into the accumulator, and func Done() ret finalizes and // returns the aggregate value. "values" and "ret" may be any type // supported by RegisterFunc. // // RegisterAggregator takes as implementation a constructor function // that constructs an instance of the aggregator type each time an // aggregation begins. The constructor must return a pointer to a // type, or an interface that implements Step() and Done(). // // The constructor function and the Step/Done methods may optionally // return an error in addition to their other return values. // // See _example/go_custom_funcs for a detailed example. func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool) error { var ai aggInfo ai.constructor = reflect.ValueOf(impl) t := ai.constructor.Type() if t.Kind() != reflect.Func { return errors.New("non-function passed to RegisterAggregator") } if t.NumOut() != 1 && t.NumOut() != 2 { return errors.New("SQLite aggregator constructors must return 1 or 2 values") } if t.NumOut() == 2 && !t.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { return errors.New("Second return value of SQLite function must be error") } if t.NumIn() != 0 { return errors.New("SQLite aggregator constructors must not have arguments") } agg := t.Out(0) switch agg.Kind() { case reflect.Ptr, reflect.Interface: default: return errors.New("SQlite aggregator constructor must return a pointer object") } stepFn, found := agg.MethodByName("Step") if !found { return errors.New("SQlite aggregator doesn't have a Step() function") } step := stepFn.Type if step.NumOut() != 0 && step.NumOut() != 1 { return errors.New("SQlite aggregator Step() function must return 0 or 1 values") } if step.NumOut() == 1 && !step.Out(0).Implements(reflect.TypeOf((*error)(nil)).Elem()) { return errors.New("type of SQlite aggregator Step() return value must be error") } stepNArgs := step.NumIn() start := 0 if agg.Kind() == reflect.Ptr { // Skip over the method receiver stepNArgs-- start++ } if step.IsVariadic() { stepNArgs-- } for i := start; i < start+stepNArgs; i++ { conv, err := callbackArg(step.In(i)) if err != nil { return err } ai.stepArgConverters = append(ai.stepArgConverters, conv) } if step.IsVariadic() { conv, err := callbackArg(t.In(start + stepNArgs).Elem()) if err != nil { return err } ai.stepVariadicConverter = conv // Pass -1 to sqlite so that it allows any number of // arguments. The call helper verifies that the minimum number // of arguments is present for variadic functions. stepNArgs = -1 } doneFn, found := agg.MethodByName("Done") if !found { return errors.New("SQlite aggregator doesn't have a Done() function") } done := doneFn.Type doneNArgs := done.NumIn() if agg.Kind() == reflect.Ptr { // Skip over the method receiver doneNArgs-- } if doneNArgs != 0 { return errors.New("SQlite aggregator Done() function must have no arguments") } if done.NumOut() != 1 && done.NumOut() != 2 { return errors.New("SQLite aggregator Done() function must return 1 or 2 values") } if done.NumOut() == 2 && !done.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) { return errors.New("second return value of SQLite aggregator Done() function must be error") } conv, err := callbackRet(done.Out(0)) if err != nil { return err } ai.doneRetConverter = conv ai.active = make(map[int64]reflect.Value) ai.next = 1 // ai must outlast the database connection, or we'll have dangling pointers. c.aggregators = append(c.aggregators, &ai) cname := C.CString(name) defer C.free(unsafe.Pointer(cname)) opts := C.SQLITE_UTF8 if pure { opts |= C.SQLITE_DETERMINISTIC } rv := C._sqlite3_create_function(c.db, cname, C.int(stepNArgs), C.int(opts), C.uintptr_t(newHandle(c, &ai)), nil, (*[0]byte)(unsafe.Pointer(C.stepTrampoline)), (*[0]byte)(unsafe.Pointer(C.doneTrampoline))) if rv != C.SQLITE_OK { return c.lastError() } return nil }
func sqlite3_create_function(db *C.sqlite3, zFunctionName *C.char, nArg C.int, eTextRep C.int, pApp uintptr, xFunc unsafe.Pointer, xStep unsafe.Pointer, xFinal unsafe.Pointer) C.int { return C._sqlite3_create_function(db, zFunctionName, nArg, eTextRep, C.uintptr_t(pApp), (*[0]byte)(unsafe.Pointer(xFunc)), (*[0]byte)(unsafe.Pointer(xStep)), (*[0]byte)(unsafe.Pointer(xFinal))) }