| // Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. |
| // |
| // Use of this source code is governed by an MIT-style |
| // license that can be found in the LICENSE file. |
| |
| package sqlite3 |
| |
| // You can't export a Go function to C and have definitions in the C |
| // preamble in the same file, so we have to have callbackTrampoline in |
| // its own file. Because we need a separate file anyway, the support |
| // code for SQLite custom functions is in here. |
| |
| /* |
| #ifndef USE_LIBSQLITE3 |
| #include <sqlite3-binding.h> |
| #else |
| #include <sqlite3.h> |
| #endif |
| #include <stdlib.h> |
| |
| void _sqlite3_result_text(sqlite3_context* ctx, const char* s); |
| void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l); |
| */ |
| import "C" |
| |
| import ( |
| "errors" |
| "fmt" |
| "math" |
| "reflect" |
| "sync" |
| "unsafe" |
| ) |
| |
| //export callbackTrampoline |
| func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) { |
| args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] |
| fi := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*functionInfo) |
| fi.Call(ctx, args) |
| } |
| |
| //export stepTrampoline |
| func stepTrampoline(ctx *C.sqlite3_context, argc C.int, argv **C.sqlite3_value) { |
| args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:int(argc):int(argc)] |
| ai := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*aggInfo) |
| ai.Step(ctx, args) |
| } |
| |
| //export doneTrampoline |
| func doneTrampoline(ctx *C.sqlite3_context) { |
| handle := uintptr(C.sqlite3_user_data(ctx)) |
| ai := lookupHandle(handle).(*aggInfo) |
| ai.Done(ctx) |
| } |
| |
| // Use handles to avoid passing Go pointers to C. |
| |
| type handleVal struct { |
| db *SQLiteConn |
| val interface{} |
| } |
| |
| var handleLock sync.Mutex |
| var handleVals = make(map[uintptr]handleVal) |
| var handleIndex uintptr = 100 |
| |
| func newHandle(db *SQLiteConn, v interface{}) uintptr { |
| handleLock.Lock() |
| defer handleLock.Unlock() |
| i := handleIndex |
| handleIndex++ |
| handleVals[i] = handleVal{db, v} |
| return i |
| } |
| |
| func lookupHandle(handle uintptr) interface{} { |
| handleLock.Lock() |
| defer handleLock.Unlock() |
| r, ok := handleVals[handle] |
| if !ok { |
| if handle >= 100 && handle < handleIndex { |
| panic("deleted handle") |
| } else { |
| panic("invalid handle") |
| } |
| } |
| return r.val |
| } |
| |
| func deleteHandles(db *SQLiteConn) { |
| handleLock.Lock() |
| defer handleLock.Unlock() |
| for handle, val := range handleVals { |
| if val.db == db { |
| delete(handleVals, handle) |
| } |
| } |
| } |
| |
| // This is only here so that tests can refer to it. |
| type callbackArgRaw C.sqlite3_value |
| |
| type callbackArgConverter func(*C.sqlite3_value) (reflect.Value, error) |
| |
| type callbackArgCast struct { |
| f callbackArgConverter |
| typ reflect.Type |
| } |
| |
| func (c callbackArgCast) Run(v *C.sqlite3_value) (reflect.Value, error) { |
| val, err := c.f(v) |
| if err != nil { |
| return reflect.Value{}, err |
| } |
| if !val.Type().ConvertibleTo(c.typ) { |
| return reflect.Value{}, fmt.Errorf("cannot convert %s to %s", val.Type(), c.typ) |
| } |
| return val.Convert(c.typ), nil |
| } |
| |
| func callbackArgInt64(v *C.sqlite3_value) (reflect.Value, error) { |
| if C.sqlite3_value_type(v) != C.SQLITE_INTEGER { |
| return reflect.Value{}, fmt.Errorf("argument must be an INTEGER") |
| } |
| return reflect.ValueOf(int64(C.sqlite3_value_int64(v))), nil |
| } |
| |
| func callbackArgBool(v *C.sqlite3_value) (reflect.Value, error) { |
| if C.sqlite3_value_type(v) != C.SQLITE_INTEGER { |
| return reflect.Value{}, fmt.Errorf("argument must be an INTEGER") |
| } |
| i := int64(C.sqlite3_value_int64(v)) |
| val := false |
| if i != 0 { |
| val = true |
| } |
| return reflect.ValueOf(val), nil |
| } |
| |
| func callbackArgFloat64(v *C.sqlite3_value) (reflect.Value, error) { |
| if C.sqlite3_value_type(v) != C.SQLITE_FLOAT { |
| return reflect.Value{}, fmt.Errorf("argument must be a FLOAT") |
| } |
| return reflect.ValueOf(float64(C.sqlite3_value_double(v))), nil |
| } |
| |
| func callbackArgBytes(v *C.sqlite3_value) (reflect.Value, error) { |
| switch C.sqlite3_value_type(v) { |
| case C.SQLITE_BLOB: |
| l := C.sqlite3_value_bytes(v) |
| p := C.sqlite3_value_blob(v) |
| return reflect.ValueOf(C.GoBytes(p, l)), nil |
| case C.SQLITE_TEXT: |
| l := C.sqlite3_value_bytes(v) |
| c := unsafe.Pointer(C.sqlite3_value_text(v)) |
| return reflect.ValueOf(C.GoBytes(c, l)), nil |
| default: |
| return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") |
| } |
| } |
| |
| func callbackArgString(v *C.sqlite3_value) (reflect.Value, error) { |
| switch C.sqlite3_value_type(v) { |
| case C.SQLITE_BLOB: |
| l := C.sqlite3_value_bytes(v) |
| p := (*C.char)(C.sqlite3_value_blob(v)) |
| return reflect.ValueOf(C.GoStringN(p, l)), nil |
| case C.SQLITE_TEXT: |
| c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v))) |
| return reflect.ValueOf(C.GoString(c)), nil |
| default: |
| return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT") |
| } |
| } |
| |
| func callbackArgGeneric(v *C.sqlite3_value) (reflect.Value, error) { |
| switch C.sqlite3_value_type(v) { |
| case C.SQLITE_INTEGER: |
| return callbackArgInt64(v) |
| case C.SQLITE_FLOAT: |
| return callbackArgFloat64(v) |
| case C.SQLITE_TEXT: |
| return callbackArgString(v) |
| case C.SQLITE_BLOB: |
| return callbackArgBytes(v) |
| case C.SQLITE_NULL: |
| // Interpret NULL as a nil byte slice. |
| var ret []byte |
| return reflect.ValueOf(ret), nil |
| default: |
| panic("unreachable") |
| } |
| } |
| |
| func callbackArg(typ reflect.Type) (callbackArgConverter, error) { |
| switch typ.Kind() { |
| case reflect.Interface: |
| if typ.NumMethod() != 0 { |
| return nil, errors.New("the only supported interface type is interface{}") |
| } |
| return callbackArgGeneric, nil |
| case reflect.Slice: |
| if typ.Elem().Kind() != reflect.Uint8 { |
| return nil, errors.New("the only supported slice type is []byte") |
| } |
| return callbackArgBytes, nil |
| case reflect.String: |
| return callbackArgString, nil |
| case reflect.Bool: |
| return callbackArgBool, nil |
| case reflect.Int64: |
| return callbackArgInt64, nil |
| case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: |
| c := callbackArgCast{callbackArgInt64, typ} |
| return c.Run, nil |
| case reflect.Float64: |
| return callbackArgFloat64, nil |
| case reflect.Float32: |
| c := callbackArgCast{callbackArgFloat64, typ} |
| return c.Run, nil |
| default: |
| return nil, fmt.Errorf("don't know how to convert to %s", typ) |
| } |
| } |
| |
| func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) { |
| var args []reflect.Value |
| |
| if len(argv) < len(converters) { |
| return nil, fmt.Errorf("function requires at least %d arguments", len(converters)) |
| } |
| |
| for i, arg := range argv[:len(converters)] { |
| v, err := converters[i](arg) |
| if err != nil { |
| return nil, err |
| } |
| args = append(args, v) |
| } |
| |
| if variadic != nil { |
| for _, arg := range argv[len(converters):] { |
| v, err := variadic(arg) |
| if err != nil { |
| return nil, err |
| } |
| args = append(args, v) |
| } |
| } |
| return args, nil |
| } |
| |
| type callbackRetConverter func(*C.sqlite3_context, reflect.Value) error |
| |
| func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error { |
| switch v.Type().Kind() { |
| case reflect.Int64: |
| case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: |
| v = v.Convert(reflect.TypeOf(int64(0))) |
| case reflect.Bool: |
| b := v.Interface().(bool) |
| if b { |
| v = reflect.ValueOf(int64(1)) |
| } else { |
| v = reflect.ValueOf(int64(0)) |
| } |
| default: |
| return fmt.Errorf("cannot convert %s to INTEGER", v.Type()) |
| } |
| |
| C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Interface().(int64))) |
| return nil |
| } |
| |
| func callbackRetFloat(ctx *C.sqlite3_context, v reflect.Value) error { |
| switch v.Type().Kind() { |
| case reflect.Float64: |
| case reflect.Float32: |
| v = v.Convert(reflect.TypeOf(float64(0))) |
| default: |
| return fmt.Errorf("cannot convert %s to FLOAT", v.Type()) |
| } |
| |
| C.sqlite3_result_double(ctx, C.double(v.Interface().(float64))) |
| return nil |
| } |
| |
| func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error { |
| if v.Type().Kind() != reflect.Slice || v.Type().Elem().Kind() != reflect.Uint8 { |
| return fmt.Errorf("cannot convert %s to BLOB", v.Type()) |
| } |
| i := v.Interface() |
| if i == nil || len(i.([]byte)) == 0 { |
| C.sqlite3_result_null(ctx) |
| } else { |
| bs := i.([]byte) |
| C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs))) |
| } |
| return nil |
| } |
| |
| func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error { |
| if v.Type().Kind() != reflect.String { |
| return fmt.Errorf("cannot convert %s to TEXT", v.Type()) |
| } |
| C._sqlite3_result_text(ctx, C.CString(v.Interface().(string))) |
| return nil |
| } |
| |
| func callbackRet(typ reflect.Type) (callbackRetConverter, error) { |
| switch typ.Kind() { |
| case reflect.Slice: |
| if typ.Elem().Kind() != reflect.Uint8 { |
| return nil, errors.New("the only supported slice type is []byte") |
| } |
| return callbackRetBlob, nil |
| case reflect.String: |
| return callbackRetText, nil |
| case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint: |
| return callbackRetInteger, nil |
| case reflect.Float32, reflect.Float64: |
| return callbackRetFloat, nil |
| default: |
| return nil, fmt.Errorf("don't know how to convert to %s", typ) |
| } |
| } |
| |
| func callbackError(ctx *C.sqlite3_context, err error) { |
| cstr := C.CString(err.Error()) |
| defer C.free(unsafe.Pointer(cstr)) |
| C.sqlite3_result_error(ctx, cstr, -1) |
| } |
| |
| // Test support code. Tests are not allowed to import "C", so we can't |
| // declare any functions that use C.sqlite3_value. |
| func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter { |
| return func(*C.sqlite3_value) (reflect.Value, error) { |
| return v, err |
| } |
| } |