aboutsummaryrefslogtreecommitdiff
path: root/src/golite.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/golite.go')
-rw-r--r--src/golite.go2616
1 files changed, 2616 insertions, 0 deletions
diff --git a/src/golite.go b/src/golite.go
new file mode 100644
index 0000000..ab3e596
--- /dev/null
+++ b/src/golite.go
@@ -0,0 +1,2616 @@
+package golite
+
+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 <sqlite3.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 = "acude"
+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
+}