diff options
Diffstat (limited to 'src/acudego.go')
-rw-r--r-- | src/acudego.go | 2616 |
1 files changed, 2616 insertions, 0 deletions
diff --git a/src/acudego.go b/src/acudego.go new file mode 100644 index 0000000..8820356 --- /dev/null +++ b/src/acudego.go @@ -0,0 +1,2616 @@ +package acudego + +import ( + "context" + "database/sql" + "database/sql/driver" + "errors" + "fmt" + "io" + "math" + "net/url" + "reflect" + "runtime" + "strconv" + "strings" + "sync" + "time" + "unsafe" + "syscall" +) + + + +/* +#include <stdlib.h> +#include <acude.h> + +void stepTrampoline(sqlite3_context *, int, sqlite3_value **); +void doneTrampoline(sqlite3_context *); +int compareTrampoline(void *, int, char *, int, char *); +int commitHookTrampoline(void *); +void rollbackHookTrampoline(void *); +void updateHookTrampoline(void *, int, char *, char *, sqlite3_int64); +*/ +import "C" + + + +// SQLiteBackup implement interface of Backup. +type SQLiteBackup struct { + b *C.sqlite3_backup +} + +// Backup make backup from src to dest. +func (destConn *SQLiteConn) Backup(dest string, srcConn *SQLiteConn, src string) (*SQLiteBackup, error) { + destptr := C.CString(dest) + defer C.free(unsafe.Pointer(destptr)) + srcptr := C.CString(src) + defer C.free(unsafe.Pointer(srcptr)) + + if b := C.sqlite3_backup_init(destConn.db, destptr, srcConn.db, srcptr); b != nil { + bb := &SQLiteBackup{b: b} + runtime.SetFinalizer(bb, (*SQLiteBackup).Finish) + return bb, nil + } + return nil, destConn.lastError() +} + +// Step to backs up for one step. Calls the underlying `sqlite3_backup_step` +// function. This function returns a boolean indicating if the backup is done +// and an error signalling any other error. Done is returned if the underlying +// C function returns SQLITE_DONE (Code 101) +func (b *SQLiteBackup) Step(p int) (bool, error) { + ret := C.sqlite3_backup_step(b.b, C.int(p)) + if ret == C.SQLITE_DONE { + return true, nil + } else if ret != 0 && ret != C.SQLITE_LOCKED && ret != C.SQLITE_BUSY { + return false, Error{Code: ErrNo(ret)} + } + return false, nil +} + +// Remaining return whether have the rest for backup. +func (b *SQLiteBackup) Remaining() int { + return int(C.sqlite3_backup_remaining(b.b)) +} + +// PageCount return count of pages. +func (b *SQLiteBackup) PageCount() int { + return int(C.sqlite3_backup_pagecount(b.b)) +} + +// Finish close backup. +func (b *SQLiteBackup) Finish() error { + return b.Close() +} + +// Close close backup. +func (b *SQLiteBackup) Close() error { + ret := C.sqlite3_backup_finish(b.b) + + // sqlite3_backup_finish() never fails, it just returns the + // error code from previous operations, so clean up before + // checking and returning an error + b.b = nil + runtime.SetFinalizer(b, nil) + + if ret != 0 { + return Error{Code: ErrNo(ret)} + } + return nil +} + +//export stepTrampoline +func stepTrampoline( + ctx *C.sqlite3_context, + argc C.int, + argv **C.sqlite3_value, +) { + const size = (math.MaxInt32 - 1) / + unsafe.Sizeof((*C.sqlite3_value)(nil)) + args := (*[size]*C.sqlite3_value)(unsafe.Pointer(argv)) + slice := args[:int(argc):int(argc)] + lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo).Step(ctx, slice) +} + +//export doneTrampoline +func doneTrampoline(ctx *C.sqlite3_context) { + lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo).Done(ctx) +} + +//export compareTrampoline +func compareTrampoline( + handlePtr unsafe.Pointer, + la C.int, + a *C.char, + lb C.int, + b *C.char, +) C.int { + cmpFn := lookupHandle(handlePtr).(func(string, string) int) + return C.int(cmpFn(C.GoStringN(a, la), C.GoStringN(b, lb))) +} + +//export commitHookTrampoline +func commitHookTrampoline(handle unsafe.Pointer) C.int { + return C.int(lookupHandle(handle).(func() int)()) +} + +//export rollbackHookTrampoline +func rollbackHookTrampoline(handle unsafe.Pointer) { + lookupHandle(handle).(func())() +} + +//export updateHookTrampoline +func updateHookTrampoline( + handle unsafe.Pointer, + op C.int, + db *C.char, + table *C.char, + rowid int64, +) { + lookupHandle(handle).(func(int, string, string, int64))( + int(op), + C.GoString(db), + C.GoString(table), + int64(rowid), + ) +} + +// Use handles to avoid passing Go pointers to C. +type handleVal struct { + db *SQLiteConn + val any +} + +var handleLock sync.Mutex +var handleVals = make(map[unsafe.Pointer]handleVal) + +func newHandle(db *SQLiteConn, v any) unsafe.Pointer { + val := handleVal{db: db, val: v} + p := C.malloc(C.size_t(1)) + if p == nil { + panic("can't allocate 'cgo-pointer hack index pointer': ptr == nil") + } + { + handleLock.Lock() + defer handleLock.Unlock() + handleVals[p] = val + } + return p +} + +func lookupHandleVal(handle unsafe.Pointer) handleVal { + handleLock.Lock() + defer handleLock.Unlock() + return handleVals[handle] +} + +func lookupHandle(handle unsafe.Pointer) any { + return lookupHandleVal(handle).val +} + +func deleteHandles(db *SQLiteConn) { + handleLock.Lock() + defer handleLock.Unlock() + for handle, val := range handleVals { + if val.db == db { + delete(handleVals, handle) + C.free(handle) + } + } +} + +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 any") + } + 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)), + C.SQLITE_TRANSIENT, + ) + } + 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)), + -1, + (*[0]byte)(unsafe.Pointer(C.free)), + ) + return nil +} + +func callbackRetNil(ctx *C.sqlite3_context, v reflect.Value) error { + return nil +} + +func callbackRetGeneric(ctx *C.sqlite3_context, v reflect.Value) error { + if v.IsNil() { + C.sqlite3_result_null(ctx) + return nil + } + + cb, err := callbackRet(v.Elem().Type()) + if err != nil { + return err + } + + return cb(ctx, v.Elem()) +} + +func callbackRet(typ reflect.Type) (callbackRetConverter, error) { + switch typ.Kind() { + case reflect.Interface: + errorInterface := reflect.TypeOf((*error)(nil)).Elem() + if typ.Implements(errorInterface) { + return callbackRetNil, nil + } + + if typ.NumMethod() == 0 { + return callbackRetGeneric, nil + } + + fallthrough + 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, C.int(-1)) +} + +// FIXME: remove this +// 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 + } +} + +// Extracted from Go database/sql source code +// Type conversions for Scan. + +var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error + +// convertAssign copies to dest the value in src, converting it if possible. +// An error is returned if the copy would result in loss of information. +// dest should be a pointer type. +func convertAssign(dest, src any) error { + // Common cases, without reflect. + switch s := src.(type) { + case string: + switch d := dest.(type) { + case *string: + if d == nil { + return errNilPtr + } + *d = s + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = []byte(s) + return nil + case *sql.RawBytes: + if d == nil { + return errNilPtr + } + *d = append((*d)[:0], s...) + return nil + } + case []byte: + switch d := dest.(type) { + case *string: + if d == nil { + return errNilPtr + } + *d = string(s) + return nil + case *any: + if d == nil { + return errNilPtr + } + *d = cloneBytes(s) + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = cloneBytes(s) + return nil + case *sql.RawBytes: + if d == nil { + return errNilPtr + } + *d = s + return nil + } + case time.Time: + switch d := dest.(type) { + case *time.Time: + *d = s + return nil + case *string: + *d = s.Format(time.RFC3339Nano) + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = []byte(s.Format(time.RFC3339Nano)) + return nil + case *sql.RawBytes: + if d == nil { + return errNilPtr + } + *d = s.AppendFormat((*d)[:0], time.RFC3339Nano) + return nil + } + case nil: + switch d := dest.(type) { + case *any: + if d == nil { + return errNilPtr + } + *d = nil + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = nil + return nil + case *sql.RawBytes: + if d == nil { + return errNilPtr + } + *d = nil + return nil + } + } + + var sv reflect.Value + + switch d := dest.(type) { + case *string: + sv = reflect.ValueOf(src) + switch sv.Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + *d = asString(src) + return nil + } + case *[]byte: + sv = reflect.ValueOf(src) + if b, ok := asBytes(nil, sv); ok { + *d = b + return nil + } + case *sql.RawBytes: + sv = reflect.ValueOf(src) + if b, ok := asBytes([]byte(*d)[:0], sv); ok { + *d = sql.RawBytes(b) + return nil + } + case *bool: + bv, err := driver.Bool.ConvertValue(src) + if err == nil { + *d = bv.(bool) + } + return err + case *any: + *d = src + return nil + } + + if scanner, ok := dest.(sql.Scanner); ok { + return scanner.Scan(src) + } + + dpv := reflect.ValueOf(dest) + if dpv.Kind() != reflect.Ptr { + return errors.New("destination not a pointer") + } + if dpv.IsNil() { + return errNilPtr + } + + if !sv.IsValid() { + sv = reflect.ValueOf(src) + } + + dv := reflect.Indirect(dpv) + if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) { + switch b := src.(type) { + case []byte: + dv.Set(reflect.ValueOf(cloneBytes(b))) + default: + dv.Set(sv) + } + return nil + } + + if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) { + dv.Set(sv.Convert(dv.Type())) + return nil + } + + // The following conversions use a string value as an intermediate representation + // to convert between various numeric types. + // + // This also allows scanning into user defined types such as "type Int int64". + // For symmetry, also check for string destination types. + switch dv.Kind() { + case reflect.Ptr: + if src == nil { + dv.Set(reflect.Zero(dv.Type())) + return nil + } + dv.Set(reflect.New(dv.Type().Elem())) + return convertAssign(dv.Interface(), src) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s := asString(src) + i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetInt(i64) + return nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + s := asString(src) + u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetUint(u64) + return nil + case reflect.Float32, reflect.Float64: + s := asString(src) + f64, err := strconv.ParseFloat(s, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetFloat(f64) + return nil + case reflect.String: + switch v := src.(type) { + case string: + dv.SetString(v) + return nil + case []byte: + dv.SetString(string(v)) + return nil + } + } + + return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest) +} + +func strconvErr(err error) error { + if ne, ok := err.(*strconv.NumError); ok { + return ne.Err + } + return err +} + +func cloneBytes(b []byte) []byte { + if b == nil { + return nil + } + c := make([]byte, len(b)) + copy(c, b) + return c +} + +func asString(src any) string { + switch v := src.(type) { + case string: + return v + case []byte: + return string(v) + } + rv := reflect.ValueOf(src) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(rv.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.FormatUint(rv.Uint(), 10) + case reflect.Float64: + return strconv.FormatFloat(rv.Float(), 'g', -1, 64) + case reflect.Float32: + return strconv.FormatFloat(rv.Float(), 'g', -1, 32) + case reflect.Bool: + return strconv.FormatBool(rv.Bool()) + } + return fmt.Sprintf("%v", src) +} + +func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.AppendInt(buf, rv.Int(), 10), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.AppendUint(buf, rv.Uint(), 10), true + case reflect.Float32: + return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true + case reflect.Float64: + return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true + case reflect.Bool: + return strconv.AppendBool(buf, rv.Bool()), true + case reflect.String: + s := rv.String() + return append(buf, s...), true + } + return +} + +/* +Package sqlite3 provides interface to SQLite3 databases. + +This works as a driver for database/sql. + +Installation + + go get github.com/mattn/go-sqlite3 + +# Supported Types + +Currently, go-sqlite3 supports the following data types. + + +------------------------------+ + |go | sqlite3 | + |----------|-------------------| + |nil | null | + |int | integer | + |int64 | integer | + |float64 | float | + |bool | integer | + |[]byte | blob | + |string | text | + |time.Time | timestamp/datetime| + +------------------------------+ + +# SQLite3 Extension + +You can write your own extension module for sqlite3. For example, below is an +extension for a Regexp matcher operation. + + #include <pcre.h> + #include <string.h> + #include <stdio.h> + #include <sqlite3ext.h> + + SQLITE_EXTENSION_INIT1 + static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) { + if (argc >= 2) { + const char *target = (const char *)sqlite3_value_text(argv[1]); + const char *pattern = (const char *)sqlite3_value_text(argv[0]); + const char* errstr = NULL; + int erroff = 0; + int vec[500]; + int n, rc; + pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL); + rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500); + if (rc <= 0) { + sqlite3_result_error(context, errstr, 0); + return; + } + sqlite3_result_int(context, 1); + } + } + + int sqlite3_extension_init(sqlite3 *db, char **errmsg, + const sqlite3_api_routines *api) { + SQLITE_EXTENSION_INIT2(api); + return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, + (void*)db, regexp_func, NULL, NULL); + } + +It needs to be built as a so/dll shared library. And you need to register +the extension module like below. + + sql.Register("sqlite3_with_extensions", + &sqlite3.SQLiteDriver{ + Extensions: []string{ + "sqlite3_mod_regexp", + }, + }) + +Then, you can use this extension. + + rows, err := db.Query("select text from mytable where name regexp '^golang'") + +# Connection Hook + +You can hook and inject your code when the connection is established by setting +ConnectHook to get the SQLiteConn. + + sql.Register("sqlite3_with_hook_example", + &sqlite3.SQLiteDriver{ + ConnectHook: func(conn *sqlite3.SQLiteConn) error { + sqlite3conn = append(sqlite3conn, conn) + return nil + }, + }) + +You can also use database/sql.Conn.Raw (Go >= 1.13): + + conn, err := db.Conn(context.Background()) + // if err != nil { ... } + defer conn.Close() + err = conn.Raw(func (driverConn any) error { + sqliteConn := driverConn.(*sqlite3.SQLiteConn) + // ... use sqliteConn + }) + // if err != nil { ... } + +# Go SQlite3 Extensions + +If you want to register Go functions as SQLite extension functions +you can make a custom driver by calling RegisterFunction from +ConnectHook. + + regex = func(re, s string) (bool, error) { + return regexp.MatchString(re, s) + } + sql.Register("sqlite3_extended", + &sqlite3.SQLiteDriver{ + ConnectHook: func(conn *sqlite3.SQLiteConn) error { + return conn.RegisterFunc("regexp", regex, true) + }, + }) + +You can then use the custom driver by passing its name to sql.Open. + + var i int + conn, err := sql.Open("sqlite3_extended", "./foo.db") + if err != nil { + panic(err) + } + err = db.QueryRow(`SELECT regexp("foo.*", "seafood")`).Scan(&i) + if err != nil { + panic(err) + } + +See the documentation of RegisterFunc for more details. +*/ + +// ErrNo inherit errno. +type ErrNo int + +// ErrNoMask is mask code. +const ErrNoMask C.int = 0xff + +// ErrNoExtended is extended errno. +type ErrNoExtended int + +// Error implement sqlite error code. +type Error struct { + Code ErrNo /* The error code returned by SQLite */ + ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */ + SystemErrno syscall.Errno /* The system errno returned by the OS through SQLite, if applicable */ + err string /* The error string returned by sqlite3_errmsg(), + this usually contains more specific details. */ +} + +// result codes from http://www.sqlite.org/c3ref/c_abort.html +var ( + ErrError = ErrNo(1) /* SQL error or missing database */ + ErrInternal = ErrNo(2) /* Internal logic error in SQLite */ + ErrPerm = ErrNo(3) /* Access permission denied */ + ErrAbort = ErrNo(4) /* Callback routine requested an abort */ + ErrBusy = ErrNo(5) /* The database file is locked */ + ErrLocked = ErrNo(6) /* A table in the database is locked */ + ErrNomem = ErrNo(7) /* A malloc() failed */ + ErrReadonly = ErrNo(8) /* Attempt to write a readonly database */ + ErrInterrupt = ErrNo(9) /* Operation terminated by sqlite3_interrupt() */ + ErrIoErr = ErrNo(10) /* Some kind of disk I/O error occurred */ + ErrCorrupt = ErrNo(11) /* The database disk image is malformed */ + ErrNotFound = ErrNo(12) /* Unknown opcode in sqlite3_file_control() */ + ErrFull = ErrNo(13) /* Insertion failed because database is full */ + ErrCantOpen = ErrNo(14) /* Unable to open the database file */ + ErrProtocol = ErrNo(15) /* Database lock protocol error */ + ErrEmpty = ErrNo(16) /* Database is empty */ + ErrSchema = ErrNo(17) /* The database schema changed */ + ErrTooBig = ErrNo(18) /* String or BLOB exceeds size limit */ + ErrConstraint = ErrNo(19) /* Abort due to constraint violation */ + ErrMismatch = ErrNo(20) /* Data type mismatch */ + ErrMisuse = ErrNo(21) /* Library used incorrectly */ + ErrNoLFS = ErrNo(22) /* Uses OS features not supported on host */ + ErrAuth = ErrNo(23) /* Authorization denied */ + ErrFormat = ErrNo(24) /* Auxiliary database format error */ + ErrRange = ErrNo(25) /* 2nd parameter to sqlite3_bind out of range */ + ErrNotADB = ErrNo(26) /* File opened that is not a database file */ + ErrNotice = ErrNo(27) /* Notifications from sqlite3_log() */ + ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */ +) + +// Error return error message from errno. +func (err ErrNo) Error() string { + return Error{Code: err}.Error() +} + +// Extend return extended errno. +func (err ErrNo) Extend(by int) ErrNoExtended { + return ErrNoExtended(int(err) | (by << 8)) +} + +// Error return error message that is extended code. +func (err ErrNoExtended) Error() string { + return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error() +} + +func (err Error) Error() string { + var str string + if err.err != "" { + str = err.err + } else { + str = C.GoString(C.sqlite3_errstr(C.int(err.Code))) + } + if err.SystemErrno != 0 { + str += ": " + err.SystemErrno.Error() + } + return str +} + +// result codes from http://www.sqlite.org/c3ref/c_abort_rollback.html +var ( + ErrIoErrRead = ErrIoErr.Extend(1) + ErrIoErrShortRead = ErrIoErr.Extend(2) + ErrIoErrWrite = ErrIoErr.Extend(3) + ErrIoErrFsync = ErrIoErr.Extend(4) + ErrIoErrDirFsync = ErrIoErr.Extend(5) + ErrIoErrTruncate = ErrIoErr.Extend(6) + ErrIoErrFstat = ErrIoErr.Extend(7) + ErrIoErrUnlock = ErrIoErr.Extend(8) + ErrIoErrRDlock = ErrIoErr.Extend(9) + ErrIoErrDelete = ErrIoErr.Extend(10) + ErrIoErrBlocked = ErrIoErr.Extend(11) + ErrIoErrNoMem = ErrIoErr.Extend(12) + ErrIoErrAccess = ErrIoErr.Extend(13) + ErrIoErrCheckReservedLock = ErrIoErr.Extend(14) + ErrIoErrLock = ErrIoErr.Extend(15) + ErrIoErrClose = ErrIoErr.Extend(16) + ErrIoErrDirClose = ErrIoErr.Extend(17) + ErrIoErrSHMOpen = ErrIoErr.Extend(18) + ErrIoErrSHMSize = ErrIoErr.Extend(19) + ErrIoErrSHMLock = ErrIoErr.Extend(20) + ErrIoErrSHMMap = ErrIoErr.Extend(21) + ErrIoErrSeek = ErrIoErr.Extend(22) + ErrIoErrDeleteNoent = ErrIoErr.Extend(23) + ErrIoErrMMap = ErrIoErr.Extend(24) + ErrIoErrGetTempPath = ErrIoErr.Extend(25) + ErrIoErrConvPath = ErrIoErr.Extend(26) + ErrLockedSharedCache = ErrLocked.Extend(1) + ErrBusyRecovery = ErrBusy.Extend(1) + ErrBusySnapshot = ErrBusy.Extend(2) + ErrCantOpenNoTempDir = ErrCantOpen.Extend(1) + ErrCantOpenIsDir = ErrCantOpen.Extend(2) + ErrCantOpenFullPath = ErrCantOpen.Extend(3) + ErrCantOpenConvPath = ErrCantOpen.Extend(4) + ErrCorruptVTab = ErrCorrupt.Extend(1) + ErrReadonlyRecovery = ErrReadonly.Extend(1) + ErrReadonlyCantLock = ErrReadonly.Extend(2) + ErrReadonlyRollback = ErrReadonly.Extend(3) + ErrReadonlyDbMoved = ErrReadonly.Extend(4) + ErrAbortRollback = ErrAbort.Extend(2) + ErrConstraintCheck = ErrConstraint.Extend(1) + ErrConstraintCommitHook = ErrConstraint.Extend(2) + ErrConstraintForeignKey = ErrConstraint.Extend(3) + ErrConstraintFunction = ErrConstraint.Extend(4) + ErrConstraintNotNull = ErrConstraint.Extend(5) + ErrConstraintPrimaryKey = ErrConstraint.Extend(6) + ErrConstraintTrigger = ErrConstraint.Extend(7) + ErrConstraintUnique = ErrConstraint.Extend(8) + ErrConstraintVTab = ErrConstraint.Extend(9) + ErrConstraintRowID = ErrConstraint.Extend(10) + ErrNoticeRecoverWAL = ErrNotice.Extend(1) + ErrNoticeRecoverRollback = ErrNotice.Extend(2) + ErrWarningAutoIndex = ErrWarning.Extend(1) +) + +// FIXME: remove this +// SQLiteTimestampFormats is timestamp formats understood by both this module +// and SQLite. The first format in the slice will be used when saving time +// values into the database. When parsing a string from a timestamp or datetime +// column, the formats are tried in order. +var SQLiteTimestampFormats = []string{ + // By default, store timestamps with whatever timezone they come with. + // When parsed, they will be returned with the same timezone. + "2006-01-02 15:04:05.999999999-07:00", + "2006-01-02T15:04:05.999999999-07:00", + "2006-01-02 15:04:05.999999999", + "2006-01-02T15:04:05.999999999", + "2006-01-02 15:04:05", + "2006-01-02T15:04:05", + "2006-01-02 15:04", + "2006-01-02T15:04", + "2006-01-02", +} + +const ( + columnDate string = "date" + columnDatetime string = "datetime" + columnTimestamp string = "timestamp" +) + +const driverName = "acudego" +func init() { + sql.Register(driverName, &SQLiteDriver{}) +} + +// Version returns SQLite library version information. +func LibVersion() (libVersion string, libVersionNumber int, sourceID string) { + libVersion = C.GoString(C.sqlite3_libversion()) + libVersionNumber = int(C.sqlite3_libversion_number()) + sourceID = C.GoString(C.sqlite3_sourceid()) + return libVersion, libVersionNumber, sourceID +} + +const ( + // used by update hook + SQLITE_DELETE = C.SQLITE_DELETE + SQLITE_INSERT = C.SQLITE_INSERT + SQLITE_UPDATE = C.SQLITE_UPDATE +) + +// Standard File Control Opcodes +// See: https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html +const ( + SQLITE_FCNTL_LOCKSTATE = int(1) + SQLITE_FCNTL_GET_LOCKPROXYFILE = int(2) + SQLITE_FCNTL_SET_LOCKPROXYFILE = int(3) + SQLITE_FCNTL_LAST_ERRNO = int(4) + SQLITE_FCNTL_SIZE_HINT = int(5) + SQLITE_FCNTL_CHUNK_SIZE = int(6) + SQLITE_FCNTL_FILE_POINTER = int(7) + SQLITE_FCNTL_SYNC_OMITTED = int(8) + SQLITE_FCNTL_WIN32_AV_RETRY = int(9) + SQLITE_FCNTL_PERSIST_WAL = int(10) + SQLITE_FCNTL_OVERWRITE = int(11) + SQLITE_FCNTL_VFSNAME = int(12) + SQLITE_FCNTL_POWERSAFE_OVERWRITE = int(13) + SQLITE_FCNTL_PRAGMA = int(14) + SQLITE_FCNTL_BUSYHANDLER = int(15) + SQLITE_FCNTL_TEMPFILENAME = int(16) + SQLITE_FCNTL_MMAP_SIZE = int(18) + SQLITE_FCNTL_TRACE = int(19) + SQLITE_FCNTL_HAS_MOVED = int(20) + SQLITE_FCNTL_SYNC = int(21) + SQLITE_FCNTL_COMMIT_PHASETWO = int(22) + SQLITE_FCNTL_WIN32_SET_HANDLE = int(23) + SQLITE_FCNTL_WAL_BLOCK = int(24) + SQLITE_FCNTL_ZIPVFS = int(25) + SQLITE_FCNTL_RBU = int(26) + SQLITE_FCNTL_VFS_POINTER = int(27) + SQLITE_FCNTL_JOURNAL_POINTER = int(28) + SQLITE_FCNTL_WIN32_GET_HANDLE = int(29) + SQLITE_FCNTL_PDB = int(30) + SQLITE_FCNTL_BEGIN_ATOMIC_WRITE = int(31) + SQLITE_FCNTL_COMMIT_ATOMIC_WRITE = int(32) + SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE = int(33) + SQLITE_FCNTL_LOCK_TIMEOUT = int(34) + SQLITE_FCNTL_DATA_VERSION = int(35) + SQLITE_FCNTL_SIZE_LIMIT = int(36) + SQLITE_FCNTL_CKPT_DONE = int(37) + SQLITE_FCNTL_RESERVE_BYTES = int(38) + SQLITE_FCNTL_CKPT_START = int(39) + SQLITE_FCNTL_EXTERNAL_READER = int(40) + SQLITE_FCNTL_CKSM_FILE = int(41) +) + +// SQLiteDriver implements driver.Driver. +type SQLiteDriver struct { + ConnectHook func(*SQLiteConn) error +} + +// SQLiteConn implements driver.Conn. +type SQLiteConn struct { + mu sync.Mutex + db *C.sqlite3 + loc *time.Location + funcs []*functionInfo + aggregators []*aggInfo +} + +// SQLiteTx implements driver.Tx. +type SQLiteTx struct { + c *SQLiteConn +} + +// SQLiteStmt implements driver.Stmt. +type SQLiteStmt struct { + mu sync.Mutex + c *SQLiteConn + s *C.sqlite3_stmt + t string + closed bool + cls bool +} + +// SQLiteResult implements sql.Result. +type SQLiteResult struct { + id int64 + changes int64 +} + +// SQLiteRows implements driver.Rows. +type SQLiteRows struct { + s *SQLiteStmt + nc int + cols []string + decltype []string + cls bool + closed bool + ctx context.Context // no better alternative to pass context into Next() method +} + +type functionInfo struct { + f reflect.Value + argConverters []callbackArgConverter + variadicConverter callbackArgConverter + retConverter callbackRetConverter +} + +func (fi *functionInfo) Call(ctx *C.sqlite3_context, argv []*C.sqlite3_value) { + args, err := callbackConvertArgs(argv, fi.argConverters, fi.variadicConverter) + if err != nil { + callbackError(ctx, err) + return + } + + ret := fi.f.Call(args) + + if len(ret) == 2 && ret[1].Interface() != nil { + callbackError(ctx, ret[1].Interface().(error)) + return + } + + err = fi.retConverter(ctx, ret[0]) + if err != nil { + callbackError(ctx, err) + return + } +} + +type aggInfo struct { + constructor reflect.Value + + // Active aggregator objects for aggregations in flight. The + // aggregators are indexed by a counter stored in the aggregation + // user data space provided by sqlite. + active map[int64]reflect.Value + next int64 + + stepArgConverters []callbackArgConverter + stepVariadicConverter callbackArgConverter + + doneRetConverter callbackRetConverter +} + +func (ai *aggInfo) agg(ctx *C.sqlite3_context) (int64, reflect.Value, error) { + aggIdx := (*int64)(C.sqlite3_aggregate_context(ctx, C.int(8))) + if *aggIdx == 0 { + *aggIdx = ai.next + ret := ai.constructor.Call(nil) + if len(ret) == 2 && ret[1].Interface() != nil { + return 0, reflect.Value{}, ret[1].Interface().(error) + } + if ret[0].IsNil() { + return 0, reflect.Value{}, errors.New("aggregator constructor returned nil state") + } + ai.next++ + ai.active[*aggIdx] = ret[0] + } + return *aggIdx, ai.active[*aggIdx], nil +} + +func (ai *aggInfo) Step(ctx *C.sqlite3_context, argv []*C.sqlite3_value) { + _, agg, err := ai.agg(ctx) + if err != nil { + callbackError(ctx, err) + return + } + + args, err := callbackConvertArgs(argv, ai.stepArgConverters, ai.stepVariadicConverter) + if err != nil { + callbackError(ctx, err) + return + } + + ret := agg.MethodByName("Step").Call(args) + if len(ret) == 1 && ret[0].Interface() != nil { + callbackError(ctx, ret[0].Interface().(error)) + return + } +} + +func (ai *aggInfo) Done(ctx *C.sqlite3_context) { + idx, agg, err := ai.agg(ctx) + if err != nil { + callbackError(ctx, err) + return + } + defer func() { delete(ai.active, idx) }() + + ret := agg.MethodByName("Done").Call(nil) + if len(ret) == 2 && ret[1].Interface() != nil { + callbackError(ctx, ret[1].Interface().(error)) + return + } + + err = ai.doneRetConverter(ctx, ret[0]) + if err != nil { + callbackError(ctx, err) + return + } +} + +// Commit transaction. +func (tx *SQLiteTx) Commit() error { + _, err := tx.c.exec(context.Background(), "COMMIT", nil) + if err != nil { + // sqlite3 may leave the transaction open in this scenario. + // However, database/sql considers the transaction complete once we + // return from Commit() - we must clean up to honour its semantics. + // We don't know if the ROLLBACK is strictly necessary, but according + // to sqlite's docs, there is no harm in calling ROLLBACK unnecessarily. + tx.c.exec(context.Background(), "ROLLBACK", nil) + } + return err +} + +// Rollback transaction. +func (tx *SQLiteTx) Rollback() error { + _, err := tx.c.exec(context.Background(), "ROLLBACK", nil) + return err +} + +// RegisterCollation makes a Go function available as a collation. +// +// cmp receives two UTF-8 strings, a and b. The result should be 0 if +// a==b, -1 if a < b, and +1 if a > b. +// +// cmp must always return the same result given the same +// inputs. Additionally, it must have the following properties for all +// strings A, B and C: if A==B then B==A; if A==B and B==C then A==C; +// if A<B then B>A; if A<B and B<C then A<C. +// +// If cmp does not obey these constraints, sqlite3's behavior is +// undefined when the collation is used. +func (c *SQLiteConn) RegisterCollation(name string, cmp func(string, string) int) error { + handle := newHandle(c, cmp) + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + rv := C.sqlite3_create_collation(c.db, cname, C.SQLITE_UTF8, handle, (*[0]byte)(unsafe.Pointer(C.compareTrampoline))) + if rv != C.SQLITE_OK { + return c.lastError() + } + return nil +} + +func (c *SQLiteConn) RegisterCommitHook(callback func() int) { + if callback == nil { + C.sqlite3_commit_hook(c.db, nil, nil) + } else { + C.sqlite3_commit_hook(c.db, (*[0]byte)(C.commitHookTrampoline), newHandle(c, callback)) + } +} + +func (c *SQLiteConn) RegisterRollbackHook(callback func()) { + if callback == nil { + C.sqlite3_rollback_hook(c.db, nil, nil) + } else { + C.sqlite3_rollback_hook(c.db, (*[0]byte)(C.rollbackHookTrampoline), newHandle(c, callback)) + } +} + +func (c *SQLiteConn) RegisterUpdateHook(callback func(int, string, string, int64)) { + if callback == nil { + C.sqlite3_update_hook(c.db, nil, nil) + } else { + C.sqlite3_update_hook(c.db, (*[0]byte)(C.updateHookTrampoline), newHandle(c, callback)) + } +} + +// 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 any, 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(step.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), + newHandle(c, &ai), + nil, + (*[0]byte)(C.stepTrampoline), + (*[0]byte)(C.doneTrampoline), + ) + if rv != C.SQLITE_OK { + return c.lastError() + } + return nil +} + +// AutoCommit return which currently auto commit or not. +func (c *SQLiteConn) AutoCommit() bool { + c.mu.Lock() + defer c.mu.Unlock() + return int(C.sqlite3_get_autocommit(c.db)) != 0 +} + +func (c *SQLiteConn) lastError() error { + return lastError(c.db) +} + +// Note: may be called with db == nil +func lastError(db *C.sqlite3) error { + rv := C.sqlite3_errcode(db) // returns SQLITE_NOMEM if db == nil + if rv == C.SQLITE_OK { + return nil + } + extrv := C.sqlite3_extended_errcode(db) // returns SQLITE_NOMEM if db == nil + errStr := C.GoString(C.sqlite3_errmsg(db)) // returns "out of memory" if db == nil + + // https://www.sqlite.org/c3ref/system_errno.html + // sqlite3_system_errno is only meaningful if the error code was SQLITE_CANTOPEN, + // or it was SQLITE_IOERR and the extended code was not SQLITE_IOERR_NOMEM + var systemErrno syscall.Errno + if rv == C.SQLITE_CANTOPEN || (rv == C.SQLITE_IOERR && extrv != C.SQLITE_IOERR_NOMEM) { + systemErrno = syscall.Errno(C.sqlite3_system_errno(db)) + } + + return Error{ + Code: ErrNo(rv), + ExtendedCode: ErrNoExtended(extrv), + SystemErrno: systemErrno, + err: errStr, + } +} + +// Exec implements Execer. +func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, error) { + list := make([]driver.NamedValue, len(args)) + for i, v := range args { + list[i] = driver.NamedValue{ + Ordinal: i + 1, + Value: v, + } + } + return c.exec(context.Background(), query, list) +} + +func (c *SQLiteConn) exec(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { + start := 0 + for { + s, err := c.prepare(ctx, query) + if err != nil { + return nil, err + } + var res driver.Result + if s.(*SQLiteStmt).s != nil { + stmtArgs := make([]driver.NamedValue, 0, len(args)) + na := s.NumInput() + if len(args)-start < na { + s.Close() + return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)) + } + // consume the number of arguments used in the current + // statement and append all named arguments not + // contained therein + if len(args[start:start+na]) > 0 { + stmtArgs = append(stmtArgs, args[start:start+na]...) + for i := range args { + if (i < start || i >= na) && args[i].Name != "" { + stmtArgs = append(stmtArgs, args[i]) + } + } + for i := range stmtArgs { + stmtArgs[i].Ordinal = i + 1 + } + } + res, err = s.(*SQLiteStmt).exec(ctx, stmtArgs) + if err != nil && err != driver.ErrSkip { + s.Close() + return nil, err + } + start += na + } + tail := s.(*SQLiteStmt).t + s.Close() + if tail == "" { + if res == nil { + // https://github.com/mattn/go-sqlite3/issues/963 + res = &SQLiteResult{0, 0} + } + return res, nil + } + query = tail + } +} + +// Query implements Queryer. +func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, error) { + list := make([]driver.NamedValue, len(args)) + for i, v := range args { + list[i] = driver.NamedValue{ + Ordinal: i + 1, + Value: v, + } + } + return c.query(context.Background(), query, list) +} + +func (c *SQLiteConn) query(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { + start := 0 + for { + stmtArgs := make([]driver.NamedValue, 0, len(args)) + s, err := c.prepare(ctx, query) + if err != nil { + return nil, err + } + s.(*SQLiteStmt).cls = true + na := s.NumInput() + if len(args)-start < na { + return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)-start) + } + // consume the number of arguments used in the current + // statement and append all named arguments not contained + // therein + stmtArgs = append(stmtArgs, args[start:start+na]...) + for i := range args { + if (i < start || i >= na) && args[i].Name != "" { + stmtArgs = append(stmtArgs, args[i]) + } + } + for i := range stmtArgs { + stmtArgs[i].Ordinal = i + 1 + } + rows, err := s.(*SQLiteStmt).query(ctx, stmtArgs) + if err != nil && err != driver.ErrSkip { + s.Close() + return rows, err + } + start += na + tail := s.(*SQLiteStmt).t + if tail == "" { + return rows, nil + } + rows.Close() + s.Close() + query = tail + } +} + +// Begin transaction. +func (c *SQLiteConn) Begin() (driver.Tx, error) { + return c.begin(context.Background()) +} + +func (c *SQLiteConn) begin(ctx context.Context) (driver.Tx, error) { + if _, err := c.exec(ctx, "BEGIN", nil); err != nil { + return nil, err + } + return &SQLiteTx{c}, nil +} + +// Open database and return a new connection. +// +// A pragma can take either zero or one argument. +// The argument is may be either in parentheses or it may be separated from +// the pragma name by an equal sign. The two syntaxes yield identical results. +// In many pragmas, the argument is a boolean. The boolean can be one of: +// +// 1 yes true on +// 0 no false off +// +// You can specify a DSN string using a URI as the filename. +// +// test.db +// file:test.db?cache=shared&mode=memory +// :memory: +// file::memory: +// +// cache +// SQLite Shared-Cache Mode +// https://www.sqlite.org/sharedcache.html +// Values: +// - shared +// - private +// +// go-sqlite3 adds the following query parameters to those used by SQLite: +// +// _loc=XXX +// Specify location of time format. It's possible to specify "auto". +func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { + // Options + var loc *time.Location + + pos := strings.IndexRune(dsn, '?') + if pos >= 1 { + params, err := url.ParseQuery(dsn[pos+1:]) + if err != nil { + return nil, err + } + + // _loc + if val := params.Get("_loc"); val != "" { + switch strings.ToLower(val) { + case "auto": + loc = time.Local + default: + loc, err = time.LoadLocation(val) + if err != nil { + return nil, fmt.Errorf("Invalid _loc: %v: %v", val, err) + } + } + } + + if !strings.HasPrefix(dsn, "file:") { + dsn = dsn[:pos] + } + } + + var db *C.sqlite3 + name := C.CString(dsn) + defer C.free(unsafe.Pointer(name)) + + var openFlags C.int = + C.SQLITE_OPEN_READWRITE | // FIXME: fails if RO FS? + C.SQLITE_OPEN_CREATE | + C.SQLITE_OPEN_URI | + C.SQLITE_OPEN_FULLMUTEX + rv := C.sqlite3_open_v2(name, &db, openFlags, nil) + if rv != 0 { + // Save off the error _before_ closing the database. + // This is safe even if db is nil. + err := lastError(db) + if db != nil { + C.sqlite3_close_v2(db) + } + return nil, err + } + if db == nil { + return nil, errors.New("sqlite succeeded without returning a database") + } + + const setup = ` + PRAGMA journal_mode = WAL; + PRAGMA busy_timeout = 10000; + ` + setupCStr := C.CString(setup) + defer C.free(unsafe.Pointer(setupCStr)) + rv = C.sqlite3_exec(db, setupCStr, nil, nil, nil) + if rv != C.SQLITE_OK { + err := lastError(db) + C.sqlite3_close_v2(db) + return nil, err + } + + // Create connection to SQLite + conn := &SQLiteConn{db: db, loc: loc} + + if d.ConnectHook != nil { + if err := d.ConnectHook(conn); err != nil { + conn.Close() + return nil, err + } + } + + runtime.SetFinalizer(conn, (*SQLiteConn).Close) + return conn, nil +} + +// Close the connection. +func (c *SQLiteConn) Close() error { + rv := C.sqlite3_close_v2(c.db) + if rv != C.SQLITE_OK { + return c.lastError() + } + deleteHandles(c) + c.mu.Lock() + c.db = nil + c.mu.Unlock() + runtime.SetFinalizer(c, nil) + return nil +} + +func (c *SQLiteConn) dbConnOpen() bool { + if c == nil { + return false + } + c.mu.Lock() + defer c.mu.Unlock() + return c.db != nil +} + +// Prepare the query string. Return a new statement. +func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { + return c.prepare(context.Background(), query) +} + +func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, error) { + pquery := C.CString(query) + defer C.free(unsafe.Pointer(pquery)) + var s *C.sqlite3_stmt + var tail *C.char + rv := C.sqlite3_prepare_v2(c.db, pquery, C.int(-1), &s, &tail) + if rv != C.SQLITE_OK { + return nil, c.lastError() + } + var t string + if tail != nil && *tail != '\000' { + t = strings.TrimSpace(C.GoString(tail)) + } + ss := &SQLiteStmt{c: c, s: s, t: t} + runtime.SetFinalizer(ss, (*SQLiteStmt).Close) + return ss, nil +} + +// Run-Time Limit Categories. +// See: http://www.sqlite.org/c3ref/c_limit_attached.html +const ( + SQLITE_LIMIT_LENGTH = C.SQLITE_LIMIT_LENGTH + SQLITE_LIMIT_SQL_LENGTH = C.SQLITE_LIMIT_SQL_LENGTH + SQLITE_LIMIT_COLUMN = C.SQLITE_LIMIT_COLUMN + SQLITE_LIMIT_EXPR_DEPTH = C.SQLITE_LIMIT_EXPR_DEPTH + SQLITE_LIMIT_COMPOUND_SELECT = C.SQLITE_LIMIT_COMPOUND_SELECT + SQLITE_LIMIT_VDBE_OP = C.SQLITE_LIMIT_VDBE_OP + SQLITE_LIMIT_FUNCTION_ARG = C.SQLITE_LIMIT_FUNCTION_ARG + SQLITE_LIMIT_ATTACHED = C.SQLITE_LIMIT_ATTACHED + SQLITE_LIMIT_LIKE_PATTERN_LENGTH = C.SQLITE_LIMIT_LIKE_PATTERN_LENGTH + SQLITE_LIMIT_VARIABLE_NUMBER = C.SQLITE_LIMIT_VARIABLE_NUMBER + SQLITE_LIMIT_TRIGGER_DEPTH = C.SQLITE_LIMIT_TRIGGER_DEPTH + SQLITE_LIMIT_WORKER_THREADS = C.SQLITE_LIMIT_WORKER_THREADS +) + +// GetFilename returns the absolute path to the file containing +// the requested schema. When passed an empty string, it will +// instead use the database's default schema: "main". +// See: sqlite3_db_filename, https://www.sqlite.org/c3ref/db_filename.html +func (c *SQLiteConn) GetFilename(schemaName string) string { + if schemaName == "" { + schemaName = "main" + } + return C.GoString(C.sqlite3_db_filename(c.db, C.CString(schemaName))) +} + +func (c *SQLiteConn) GetLimit(id int) int { + return int(C.sqlite3_limit(c.db, C.int(id), C.int(-1))) +} + +func (c *SQLiteConn) SetLimit(id int, newVal int) int { + return int(C.sqlite3_limit(c.db, C.int(id), C.int(newVal))) +} + +// SetFileControlInt invokes the xFileControl method on a given database. The +// dbName is the name of the database. It will default to "main" if left blank. +// The op is one of the opcodes prefixed by "SQLITE_FCNTL_". The arg argument +// and return code are both opcode-specific. Please see the SQLite documentation. +// +// This method is not thread-safe as the returned error code can be changed by +// another call if invoked concurrently. +// +// See: sqlite3_file_control, https://www.sqlite.org/c3ref/file_control.html +func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error { + if dbName == "" { + dbName = "main" + } + + cDBName := C.CString(dbName) + defer C.free(unsafe.Pointer(cDBName)) + + cArg := C.int(arg) + rv := C.sqlite3_file_control(c.db, cDBName, C.int(op), unsafe.Pointer(&cArg)) + if rv != C.SQLITE_OK { + return c.lastError() + } + return nil +} + +func (s *SQLiteStmt) Close() error { + s.mu.Lock() + defer s.mu.Unlock() + if s.closed { + return nil + } + s.closed = true + if !s.c.dbConnOpen() { + return errors.New("sqlite statement with already closed database connection") + } + rv := C.sqlite3_finalize(s.s) + s.s = nil + if rv != C.SQLITE_OK { + return s.c.lastError() + } + s.c = nil + runtime.SetFinalizer(s, nil) + return nil +} + +func (s *SQLiteStmt) NumInput() int { + return int(C.sqlite3_bind_parameter_count(s.s)) +} + +var placeHolder = []byte{0} + +func (s *SQLiteStmt) bind(args []driver.NamedValue) error { + rv := C.sqlite3_reset(s.s) + if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { + return s.c.lastError() + } + + bindIndices := make([][3]int, len(args)) + prefixes := []string{":", "@", "$"} + for i, v := range args { + bindIndices[i][0] = args[i].Ordinal + if v.Name != "" { + for j := range prefixes { + cname := C.CString(prefixes[j] + v.Name) + bindIndices[i][j] = int(C.sqlite3_bind_parameter_index(s.s, cname)) + C.free(unsafe.Pointer(cname)) + } + args[i].Ordinal = bindIndices[i][0] + } + } + + for i, arg := range args { + for j := range bindIndices[i] { + if bindIndices[i][j] == 0 { + continue + } + n := C.int(bindIndices[i][j]) + switch v := arg.Value.(type) { + case nil: + rv = C.sqlite3_bind_null(s.s, n) + case string: + if len(v) == 0 { + rv = C.sqlite3_bind_text( + s.s, + n, + (*C.char)(unsafe.Pointer(&placeHolder[0])), + C.int(0), + C.SQLITE_TRANSIENT, + ) + } else { + b := []byte(v) + rv = C.sqlite3_bind_text( + s.s, + n, + (*C.char)(unsafe.Pointer(&b[0])), + C.int(len(b)), + C.SQLITE_TRANSIENT, + ) + } + case int64: + rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v)) + case bool: + if v { + rv = C.sqlite3_bind_int(s.s, n, 1) + } else { + rv = C.sqlite3_bind_int(s.s, n, 0) + } + case float64: + rv = C.sqlite3_bind_double(s.s, n, C.double(v)) + case []byte: + if v == nil { + rv = C.sqlite3_bind_null(s.s, n) + } else { + ln := len(v) + if ln == 0 { + v = placeHolder + } + rv = C.sqlite3_bind_blob( + s.s, + n, + unsafe.Pointer(&v[0]), + C.int(ln), + C.SQLITE_TRANSIENT, + ) + } + case time.Time: + b := []byte(v.Format(SQLiteTimestampFormats[0])) + rv = C.sqlite3_bind_text( + s.s, + n, + (*C.char)(unsafe.Pointer(&b[0])), + C.int(len(b)), + C.SQLITE_TRANSIENT, + ) + } + if rv != C.SQLITE_OK { + return s.c.lastError() + } + } + } + return nil +} + +// Query the statement with arguments. Return records. +func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) { + list := make([]driver.NamedValue, len(args)) + for i, v := range args { + list[i] = driver.NamedValue{ + Ordinal: i + 1, + Value: v, + } + } + return s.query(context.Background(), list) +} + +func (s *SQLiteStmt) query(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { + if err := s.bind(args); err != nil { + return nil, err + } + + rows := &SQLiteRows{ + s: s, + nc: int(C.sqlite3_column_count(s.s)), + cols: nil, + decltype: nil, + cls: s.cls, + closed: false, + ctx: ctx, + } + runtime.SetFinalizer(rows, (*SQLiteRows).Close) + + return rows, nil +} + +// LastInsertId return last inserted ID. +func (r *SQLiteResult) LastInsertId() (int64, error) { + return r.id, nil +} + +// RowsAffected return how many rows affected. +func (r *SQLiteResult) RowsAffected() (int64, error) { + return r.changes, nil +} + +// Exec execute the statement with arguments. Return result object. +func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) { + list := make([]driver.NamedValue, len(args)) + for i, v := range args { + list[i] = driver.NamedValue{ + Ordinal: i + 1, + Value: v, + } + } + return s.exec(context.Background(), list) +} + +func isInterruptErr(err error) bool { + sqliteErr, ok := err.(Error) + if ok { + return sqliteErr.Code == ErrInterrupt + } + return false +} + +// exec executes a query that doesn't return rows. Attempts to honor context timeout. +func (s *SQLiteStmt) exec(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { + if ctx.Done() == nil { + return s.execSync(args) + } + + type result struct { + r driver.Result + err error + } + resultCh := make(chan result) + defer close(resultCh) + go func() { + r, err := s.execSync(args) + resultCh <- result{r, err} + }() + var rv result + select { + case rv = <-resultCh: + case <-ctx.Done(): + select { + case rv = <-resultCh: // no need to interrupt, operation completed in db + default: + // this is still racy and can be no-op if executed between sqlite3_* calls in execSync. + C.sqlite3_interrupt(s.c.db) + rv = <-resultCh // wait for goroutine completed + if isInterruptErr(rv.err) { + return nil, ctx.Err() + } + } + } + return rv.r, rv.err +} + +func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) { + if err := s.bind(args); err != nil { + C.sqlite3_reset(s.s) + C.sqlite3_clear_bindings(s.s) + return nil, err + } + + rv := C.sqlite3_step(s.s) + if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { + err := s.c.lastError() + C.sqlite3_reset(s.s) + C.sqlite3_clear_bindings(s.s) + return nil, err + } + + db := C.sqlite3_db_handle(s.s) + id := int64(C.sqlite3_last_insert_rowid(db)) + changes := int64(C.sqlite3_changes(db)) + + return &SQLiteResult{ + id: id, + changes: changes, + }, nil +} + +// Readonly reports if this statement is considered readonly by SQLite. +// +// See: https://sqlite.org/c3ref/stmt_readonly.html +func (s *SQLiteStmt) Readonly() bool { + return C.sqlite3_stmt_readonly(s.s) == 1 +} + +func (rc *SQLiteRows) Close() error { + rc.s.mu.Lock() + if rc.s.closed || rc.closed { + rc.s.mu.Unlock() + return nil + } + rc.closed = true + if rc.cls { + rc.s.mu.Unlock() + return rc.s.Close() + } + rv := C.sqlite3_reset(rc.s.s) + if rv != C.SQLITE_OK { + rc.s.mu.Unlock() + return rc.s.c.lastError() + } + rc.s.mu.Unlock() + rc.s = nil + runtime.SetFinalizer(rc, nil) + return nil +} + +func (rc *SQLiteRows) Columns() []string { + rc.s.mu.Lock() + defer rc.s.mu.Unlock() + if rc.s.s != nil && rc.nc != len(rc.cols) { + rc.cols = make([]string, rc.nc) + for i := 0; i < rc.nc; i++ { + rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i))) + } + } + return rc.cols +} + +func (rc *SQLiteRows) declTypes() []string { + if rc.s.s != nil && rc.decltype == nil { + rc.decltype = make([]string, rc.nc) + for i := 0; i < rc.nc; i++ { + rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i)))) + } + } + return rc.decltype +} + +// DeclTypes return column types. +func (rc *SQLiteRows) DeclTypes() []string { + rc.s.mu.Lock() + defer rc.s.mu.Unlock() + return rc.declTypes() +} + +// Next move cursor to next. Attempts to honor context timeout from QueryContext call. +func (rc *SQLiteRows) Next(dest []driver.Value) error { + rc.s.mu.Lock() + defer rc.s.mu.Unlock() + + if rc.s.closed { + return io.EOF + } + + if rc.ctx.Done() == nil { + return rc.nextSyncLocked(dest) + } + resultCh := make(chan error) + defer close(resultCh) + go func() { + resultCh <- rc.nextSyncLocked(dest) + }() + select { + case err := <-resultCh: + return err + case <-rc.ctx.Done(): + select { + case <-resultCh: // no need to interrupt + default: + // this is still racy and can be no-op if executed between sqlite3_* calls in nextSyncLocked. + C.sqlite3_interrupt(rc.s.c.db) + <-resultCh // ensure goroutine completed + } + return rc.ctx.Err() + } +} + +// nextSyncLocked moves cursor to next; must be called with locked mutex. +func (rc *SQLiteRows) nextSyncLocked(dest []driver.Value) error { + rv := C.sqlite3_step(rc.s.s) + if rv == C.SQLITE_DONE { + return io.EOF + } + if rv != C.SQLITE_ROW { + rv = C.sqlite3_reset(rc.s.s) + if rv != C.SQLITE_OK { + return rc.s.c.lastError() + } + return nil + } + + rc.declTypes() + + for i := range dest { + switch C.sqlite3_column_type(rc.s.s, C.int(i)) { + case C.SQLITE_INTEGER: + val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i))) + switch rc.decltype[i] { + case columnTimestamp, columnDatetime, columnDate: + var t time.Time + // Assume a millisecond unix timestamp if it's 13 digits -- too + // large to be a reasonable timestamp in seconds. + if val > 1e12 || val < -1e12 { + val *= int64(time.Millisecond) // convert ms to nsec + t = time.Unix(0, val) + } else { + t = time.Unix(val, 0) + } + t = t.UTC() + if rc.s.c.loc != nil { + t = t.In(rc.s.c.loc) + } + dest[i] = t + case "boolean": + dest[i] = val > 0 + default: + dest[i] = val + } + case C.SQLITE_FLOAT: + dest[i] = float64(C.sqlite3_column_double(rc.s.s, C.int(i))) + case C.SQLITE_BLOB: + p := C.sqlite3_column_blob(rc.s.s, C.int(i)) + if p == nil { + dest[i] = []byte{} + continue + } + n := C.sqlite3_column_bytes(rc.s.s, C.int(i)) + dest[i] = C.GoBytes(p, n) + case C.SQLITE_NULL: + dest[i] = nil + case C.SQLITE_TEXT: + var err error + var timeVal time.Time + + n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i))) + s := C.GoStringN((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))), C.int(n)) + + switch rc.decltype[i] { + case columnTimestamp, columnDatetime, columnDate: + var t time.Time + s = strings.TrimSuffix(s, "Z") + for _, format := range SQLiteTimestampFormats { + if timeVal, err = time.ParseInLocation(format, s, time.UTC); err == nil { + t = timeVal + break + } + } + if err != nil { + // The column is a time value, so return the zero time on parse failure. + t = time.Time{} + } + if rc.s.c.loc != nil { + t = t.In(rc.s.c.loc) + } + dest[i] = t + default: + dest[i] = s + } + } + } + return nil +} + +const i64 = unsafe.Sizeof(int(0)) > 4 + +// SQLiteContext behave sqlite3_context +type SQLiteContext C.sqlite3_context + +// ResultBool sets the result of an SQL function. +func (c *SQLiteContext) ResultBool(b bool) { + if b { + c.ResultInt(1) + } else { + c.ResultInt(0) + } +} + +// ResultBlob sets the result of an SQL function. +// See: sqlite3_result_blob, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultBlob(b []byte) { + if i64 && len(b) > math.MaxInt32 { + C.sqlite3_result_error_toobig((*C.sqlite3_context)(c)) + return + } + var p *byte + if len(b) > 0 { + p = &b[0] + } + C.sqlite3_result_blob( + (*C.sqlite3_context)(c), + unsafe.Pointer(p), + C.int(len(b)), + C.SQLITE_TRANSIENT, + ) +} + +// ResultDouble sets the result of an SQL function. +// See: sqlite3_result_double, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultDouble(d float64) { + C.sqlite3_result_double((*C.sqlite3_context)(c), C.double(d)) +} + +// ResultInt sets the result of an SQL function. +// See: sqlite3_result_int, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultInt(i int) { + if i64 && (i > math.MaxInt32 || i < math.MinInt32) { + C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) + } else { + C.sqlite3_result_int((*C.sqlite3_context)(c), C.int(i)) + } +} + +// ResultInt64 sets the result of an SQL function. +// See: sqlite3_result_int64, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultInt64(i int64) { + C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) +} + +// ResultNull sets the result of an SQL function. +// See: sqlite3_result_null, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultNull() { + C.sqlite3_result_null((*C.sqlite3_context)(c)) +} + +// ResultText sets the result of an SQL function. +// See: sqlite3_result_text, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultText(s string) { + h := (*reflect.StringHeader)(unsafe.Pointer(&s)) + cs, l := (*C.char)(unsafe.Pointer(h.Data)), C.int(h.Len) + C.sqlite3_result_text( + (*C.sqlite3_context)(c), + cs, + l, + C.SQLITE_TRANSIENT, + ) +} + +// ResultZeroblob sets the result of an SQL function. +// See: sqlite3_result_zeroblob, http://sqlite.org/c3ref/result_blob.html +func (c *SQLiteContext) ResultZeroblob(n int) { + C.sqlite3_result_zeroblob((*C.sqlite3_context)(c), C.int(n)) +} + +// Ping implement Pinger. +func (c *SQLiteConn) Ping(ctx context.Context) error { + if c.db == nil { + // must be ErrBadConn for sql to close the database + return driver.ErrBadConn + } + return nil +} + +// QueryContext implement QueryerContext. +func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { + return c.query(ctx, query, args) +} + +// ExecContext implement ExecerContext. +func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { + return c.exec(ctx, query, args) +} + +// PrepareContext implement ConnPrepareContext. +func (c *SQLiteConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { + return c.prepare(ctx, query) +} + +// BeginTx implement ConnBeginTx. +func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { + return c.begin(ctx) +} + +// QueryContext implement QueryerContext. +func (s *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { + return s.query(ctx, args) +} + +// ExecContext implement ExecerContext. +func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { + return s.exec(ctx, args) +} + +// ColumnTableName returns the table that is the origin of a particular result +// column in a SELECT statement. +// +// See https://www.sqlite.org/c3ref/column_database_name.html +func (s *SQLiteStmt) ColumnTableName(n int) string { + return C.GoString(C.sqlite3_column_table_name(s.s, C.int(n))) +} + +// Serialize returns a byte slice that is a serialization of the database. +// +// See https://www.sqlite.org/c3ref/serialize.html +func (c *SQLiteConn) Serialize(schema string) ([]byte, error) { + if schema == "" { + schema = "main" + } + var zSchema *C.char + zSchema = C.CString(schema) + defer C.free(unsafe.Pointer(zSchema)) + + var sz C.sqlite3_int64 + ptr := C.sqlite3_serialize(c.db, zSchema, &sz, 0) + if ptr == nil { + return nil, fmt.Errorf("serialize failed") + } + defer C.sqlite3_free(unsafe.Pointer(ptr)) + + if sz > C.sqlite3_int64(math.MaxInt) { + return nil, fmt.Errorf("serialized database is too large (%d bytes)", sz) + } + + cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(ptr)), + Len: int(sz), + Cap: int(sz), + })) + + res := make([]byte, int(sz)) + copy(res, cBuf) + return res, nil +} + +// Deserialize causes the connection to disconnect from the current database and +// then re-open as an in-memory database based on the contents of the byte slice. +// +// See https://www.sqlite.org/c3ref/deserialize.html +func (c *SQLiteConn) Deserialize(b []byte, schema string) error { + if schema == "" { + schema = "main" + } + var zSchema *C.char + zSchema = C.CString(schema) + defer C.free(unsafe.Pointer(zSchema)) + + tmpBuf := (*C.uchar)(C.sqlite3_malloc64(C.sqlite3_uint64(len(b)))) + cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(tmpBuf)), + Len: len(b), + Cap: len(b), + })) + copy(cBuf, b) + + rc := C.sqlite3_deserialize(c.db, zSchema, tmpBuf, C.sqlite3_int64(len(b)), + C.sqlite3_int64(len(b)), C.SQLITE_DESERIALIZE_FREEONCLOSE) + if rc != C.SQLITE_OK { + return fmt.Errorf("deserialize failed with return %v", rc) + } + return nil +} + +// Op is type of operations. +type Op uint8 + +// Op mean identity of operations. +const ( + OpEQ Op = 2 + OpGT = 4 + OpLE = 8 + OpLT = 16 + OpGE = 32 + OpMATCH = 64 + OpLIKE = 65 /* 3.10.0 and later only */ + OpGLOB = 66 /* 3.10.0 and later only */ + OpREGEXP = 67 /* 3.10.0 and later only */ + OpScanUnique = 1 /* Scan visits at most 1 row */ +) + +// InfoConstraint give information of constraint. +type InfoConstraint struct { + Column int + Op Op + Usable bool +} + +// InfoOrderBy give information of order-by. +type InfoOrderBy struct { + Column int + Desc bool +} + +func constraints(info *C.sqlite3_index_info) []InfoConstraint { + slice := *(*[]C.struct_sqlite3_index_constraint)(unsafe.Pointer(&reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(info.aConstraint)), + Len: int(info.nConstraint), + Cap: int(info.nConstraint), + })) + + cst := make([]InfoConstraint, 0, len(slice)) + for _, c := range slice { + var usable bool + if c.usable > 0 { + usable = true + } + cst = append(cst, InfoConstraint{ + Column: int(c.iColumn), + Op: Op(c.op), + Usable: usable, + }) + } + return cst +} + +func orderBys(info *C.sqlite3_index_info) []InfoOrderBy { + slice := *(*[]C.struct_sqlite3_index_orderby)(unsafe.Pointer(&reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(info.aOrderBy)), + Len: int(info.nOrderBy), + Cap: int(info.nOrderBy), + })) + + ob := make([]InfoOrderBy, 0, len(slice)) + for _, c := range slice { + var desc bool + if c.desc > 0 { + desc = true + } + ob = append(ob, InfoOrderBy{ + Column: int(c.iColumn), + Desc: desc, + }) + } + return ob +} + +// IndexResult is a Go struct representation of what eventually ends up in the +// output fields for `sqlite3_index_info` +// See: https://www.sqlite.org/c3ref/index_info.html +type IndexResult struct { + Used []bool // aConstraintUsage + IdxNum int + IdxStr string + AlreadyOrdered bool // orderByConsumed + EstimatedCost float64 + EstimatedRows float64 +} + +func fillDBError(dbErr *Error, db *C.sqlite3) { + // See SQLiteConn.lastError(), in file 'sqlite3.go' at the time of writing (Sept 5, 2016) + dbErr.Code = ErrNo(C.sqlite3_errcode(db)) + dbErr.ExtendedCode = ErrNoExtended(C.sqlite3_extended_errcode(db)) + dbErr.err = C.GoString(C.sqlite3_errmsg(db)) +} + +// ColumnTypeDatabaseTypeName implement RowsColumnTypeDatabaseTypeName. +func (rc *SQLiteRows) ColumnTypeDatabaseTypeName(i int) string { + return C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) +} + +// ColumnTypeNullable implement RowsColumnTypeNullable. +func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) { + return true, true +} + +// ColumnTypeScanType implement RowsColumnTypeScanType. +func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type { + //ct := C.sqlite3_column_type(rc.s.s, C.int(i)) // Always returns 5 + return scanType(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i)))) +} + +const ( + SQLITE_INTEGER = iota + SQLITE_TEXT + SQLITE_BLOB + SQLITE_REAL + SQLITE_NUMERIC + SQLITE_TIME + SQLITE_BOOL + SQLITE_NULL +) + +func scanType(cdt string) reflect.Type { + t := strings.ToUpper(cdt) + i := databaseTypeConvSqlite(t) + switch i { + case SQLITE_INTEGER: + return reflect.TypeOf(sql.NullInt64{}) + case SQLITE_TEXT: + return reflect.TypeOf(sql.NullString{}) + case SQLITE_BLOB: + return reflect.TypeOf(sql.RawBytes{}) + case SQLITE_REAL: + return reflect.TypeOf(sql.NullFloat64{}) + case SQLITE_NUMERIC: + return reflect.TypeOf(sql.NullFloat64{}) + case SQLITE_BOOL: + return reflect.TypeOf(sql.NullBool{}) + case SQLITE_TIME: + return reflect.TypeOf(sql.NullTime{}) + } + return reflect.TypeOf(new(any)) +} + +func databaseTypeConvSqlite(t string) int { + if strings.Contains(t, "INT") { + return SQLITE_INTEGER + } + if t == "CLOB" || t == "TEXT" || + strings.Contains(t, "CHAR") { + return SQLITE_TEXT + } + if t == "BLOB" { + return SQLITE_BLOB + } + if t == "REAL" || t == "FLOAT" || + strings.Contains(t, "DOUBLE") { + return SQLITE_REAL + } + if t == "DATE" || t == "DATETIME" || + t == "TIMESTAMP" { + return SQLITE_TIME + } + if t == "NUMERIC" || + strings.Contains(t, "DECIMAL") { + return SQLITE_NUMERIC + } + if t == "BOOLEAN" { + return SQLITE_BOOL + } + + return SQLITE_NULL +} |