aboutsummaryrefslogtreecommitdiff
path: root/sqlite3.go
diff options
context:
space:
mode:
authorDavid Anderson <dave@natulte.net>2015-08-21 13:38:22 -0700
committerDavid Anderson <dave@natulte.net>2015-08-21 16:37:45 -0700
commit122ddb16de825ed3d989d25d4d7b2d2e278abdf6 (patch)
tree43d4cad7936f67a709c32b80439fef32805ddebf /sqlite3.go
parentImplement support for passing Go functions as custom functions to SQLite. (diff)
downloadgolite-122ddb16de825ed3d989d25d4d7b2d2e278abdf6.tar.gz
golite-122ddb16de825ed3d989d25d4d7b2d2e278abdf6.tar.xz
Move argument converters to callback.go, and optimize return value handling.
A call now doesn't have to do any reflection, it just blindly invokes a bunch of argument and return value handlers to execute the translation, and the safety of the translation is determined at registration time.
Diffstat (limited to 'sqlite3.go')
-rw-r--r--sqlite3.go122
1 files changed, 18 insertions, 104 deletions
diff --git a/sqlite3.go b/sqlite3.go
index f995589..174a3ee 100644
--- a/sqlite3.go
+++ b/sqlite3.go
@@ -166,7 +166,8 @@ type SQLiteRows struct {
type functionInfo struct {
f reflect.Value
- argConverters []func(*C.sqlite3_value) (reflect.Value, error)
+ argConverters []callbackArgConverter
+ retConverter callbackRetConverter
}
func (fi *functionInfo) error(ctx *C.sqlite3_context, err error) {
@@ -193,58 +194,11 @@ func (fi *functionInfo) Call(ctx *C.sqlite3_context, argv []*C.sqlite3_value) {
return
}
- res := ret[0].Interface()
- // Normalize ret to one of the types sqlite knows.
- switch r := res.(type) {
- case int64, float64, []byte, string:
- // Already the right type
- case bool:
- if r {
- res = int64(1)
- } else {
- res = int64(0)
- }
- case int:
- res = int64(r)
- case uint:
- res = int64(r)
- case uint8:
- res = int64(r)
- case uint16:
- res = int64(r)
- case uint32:
- res = int64(r)
- case uint64:
- res = int64(r)
- case int8:
- res = int64(r)
- case int16:
- res = int64(r)
- case int32:
- res = int64(r)
- case float32:
- res = float64(r)
- default:
- fi.error(ctx, errors.New("cannot convert returned type to sqlite type"))
+ err := fi.retConverter(ctx, ret[0])
+ if err != nil {
+ fi.error(ctx, err)
return
}
-
- switch r := res.(type) {
- case int64:
- C.sqlite3_result_int64(ctx, C.sqlite3_int64(r))
- case float64:
- C.sqlite3_result_double(ctx, C.double(r))
- case []byte:
- if len(r) == 0 {
- C.sqlite3_result_null(ctx)
- } else {
- C._sqlite3_result_blob(ctx, unsafe.Pointer(&r[0]), C.int(len(r)))
- }
- case string:
- C._sqlite3_result_text(ctx, C.CString(r))
- default:
- panic("unreachable")
- }
}
// Commit transaction.
@@ -261,10 +215,10 @@ func (tx *SQLiteTx) Rollback() error {
// RegisterFunc makes a Go function available as a SQLite function.
//
-// The function must accept only arguments of type int64, float64,
-// []byte or string, and return one value of any numeric type except
-// complex, bool, []byte or string. Optionally, an error can be
-// provided as a second return value.
+// The function can accept arguments of any real numeric type
+// (i.e. not complex), as well as []byte and string. It must return a
+// value of one of those types, and optionally an error as a second
+// value.
//
// If pure is true. SQLite will assume that the function's return
// value depends only on its inputs, and make more aggressive
@@ -287,59 +241,19 @@ func (c *SQLiteConn) RegisterFunc(name string, impl interface{}, pure bool) erro
}
for i := 0; i < t.NumIn(); i++ {
- arg := t.In(i)
- var conv func(*C.sqlite3_value) (reflect.Value, error)
- switch arg.Kind() {
- case reflect.Int64:
- conv = func(v *C.sqlite3_value) (reflect.Value, error) {
- if C.sqlite3_value_type(v) != C.SQLITE_INTEGER {
- return reflect.Value{}, fmt.Errorf("Argument %d to %s must be an INTEGER", i+1, name)
- }
- return reflect.ValueOf(int64(C.sqlite3_value_int64(v))), nil
- }
- case reflect.Float64:
- conv = func(v *C.sqlite3_value) (reflect.Value, error) {
- if C.sqlite3_value_type(v) != C.SQLITE_FLOAT {
- return reflect.Value{}, fmt.Errorf("Argument %d to %s must be a FLOAT", i+1, name)
- }
- return reflect.ValueOf(float64(C.sqlite3_value_double(v))), nil
- }
- case reflect.Slice:
- if arg.Elem().Kind() != reflect.Uint8 {
- return errors.New("The only supported slice type is []byte")
- }
- conv = func(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 %d to %s must be BLOB or TEXT", i+1, name)
- }
- }
- case reflect.String:
- conv = func(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 %d to %s must be BLOB or TEXT", i+1, name)
- }
- }
+ conv, err := callbackArg(t.In(i))
+ if err != nil {
+ return err
}
fi.argConverters = append(fi.argConverters, conv)
}
+ 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)