package golite import ( "context" "database/sql" "database/sql/driver" "errors" "fmt" "io" "math" "net/url" "reflect" "runtime" "strconv" "strings" "sync" "time" "unsafe" "syscall" ) /* Premises: - FTS5 - (default) foreign keys - ICU - (default) AUTOVACUUM - SQLITE_THREADSAFE - column metadata */ /* #include #include 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 #include #include #include 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" ) var driverName = "sqlite3" 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 txlock string 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 AA; if A 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, c.txlock, 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: // // mode // Access mode of the database. // https://www.sqlite.org/c3ref/open.html // Values: // - ro // - rw // - rwc // - memory // // cache // SQLite Shared-Cache Mode // https://www.sqlite.org/sharedcache.html // Values: // - shared // - private // // immutable=Boolean // The immutable parameter is a boolean query parameter that indicates // that the database file is stored on read-only media. When immutable is set, // SQLite assumes that the database file cannot be changed, // even by a process with higher privilege, // and so the database is opened read-only and all locking and change detection is disabled. // Caution: Setting the immutable property on a database file that // does in fact change can result in incorrect query results and/or SQLITE_CORRUPT errors. // // 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". // // _mutex=XXX // Specify mutex mode. XXX can be "no", "full". // // _txlock=XXX // Specify locking behavior for transactions. XXX can be "immediate", // "deferred", "exclusive". // // _auto_vacuum=X | _vacuum=X // 0 | none - Auto Vacuum disabled // 1 | full - Auto Vacuum FULL // 2 | incremental - Auto Vacuum Incremental // // _busy_timeout=XXX"| _timeout=XXX // Specify value for sqlite3_busy_timeout. // // _case_sensitive_like=Boolean | _cslike=Boolean // https://www.sqlite.org/pragma.html#pragma_case_sensitive_like // Default or disabled the LIKE operation is case-insensitive. // When enabling this options behaviour of LIKE will become case-sensitive. // // _defer_foreign_keys=Boolean | _defer_fk=Boolean // Defer Foreign Keys until outermost transaction is committed. // // _ignore_check_constraints=Boolean // This pragma enables or disables the enforcement of CHECK constraints. // The default setting is off, meaning that CHECK constraints are enforced by default. // // _locking_mode=X | _locking=X // Sets the database connection locking-mode. // The locking-mode is either NORMAL or EXCLUSIVE. // https://www.sqlite.org/pragma.html#pragma_locking_mode // // _query_only=Boolean // The query_only pragma prevents all changes to database files when enabled. // // _recursive_triggers=Boolean | _rt=Boolean // Enable or disable recursive triggers. // // _writable_schema=Boolean // When this pragma is on, the SQLITE_MASTER tables in which database // can be changed using ordinary UPDATE, INSERT, and DELETE statements. // Warning: misuse of this pragma can easily result in a corrupt database file. func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { if C.sqlite3_threadsafe() == 0 { return nil, errors.New("sqlite library was not compiled for thread-safe operation") } var pkey string // Options var loc *time.Location mutex := C.int(C.SQLITE_OPEN_FULLMUTEX) txlock := "BEGIN" // PRAGMA's autoVacuum := -1 busyTimeout := 5000 caseSensitiveLike := -1 deferForeignKeys := -1 ignoreCheckConstraints := -1 lockingMode := "NORMAL" queryOnly := -1 recursiveTriggers := -1 writableSchema := -1 vfsName := "" var cacheSize *int64 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) } } } // _mutex if val := params.Get("_mutex"); val != "" { switch strings.ToLower(val) { case "no": mutex = C.SQLITE_OPEN_NOMUTEX case "full": mutex = C.SQLITE_OPEN_FULLMUTEX default: return nil, fmt.Errorf("Invalid _mutex: %v", val) } } // _txlock if val := params.Get("_txlock"); val != "" { switch strings.ToLower(val) { case "immediate": txlock = "BEGIN IMMEDIATE" case "exclusive": txlock = "BEGIN EXCLUSIVE" case "deferred": txlock = "BEGIN" default: return nil, fmt.Errorf("Invalid _txlock: %v", val) } } // Auto Vacuum (_vacuum) // // https://www.sqlite.org/pragma.html#pragma_auto_vacuum // pkey = "" // Reset pkey if _, ok := params["_auto_vacuum"]; ok { pkey = "_auto_vacuum" } if _, ok := params["_vacuum"]; ok { pkey = "_vacuum" } if val := params.Get(pkey); val != "" { switch strings.ToLower(val) { case "0", "none": autoVacuum = 0 case "1", "full": autoVacuum = 1 case "2", "incremental": autoVacuum = 2 default: return nil, fmt.Errorf("Invalid _auto_vacuum: %v, expecting value of '0 NONE 1 FULL 2 INCREMENTAL'", val) } } // Busy Timeout (_busy_timeout) // // https://www.sqlite.org/pragma.html#pragma_busy_timeout // pkey = "" // Reset pkey if _, ok := params["_busy_timeout"]; ok { pkey = "_busy_timeout" } if _, ok := params["_timeout"]; ok { pkey = "_timeout" } if val := params.Get(pkey); val != "" { iv, err := strconv.ParseInt(val, 10, 64) if err != nil { return nil, fmt.Errorf("Invalid _busy_timeout: %v: %v", val, err) } busyTimeout = int(iv) } // Case Sensitive Like (_cslike) // // https://www.sqlite.org/pragma.html#pragma_case_sensitive_like // pkey = "" // Reset pkey if _, ok := params["_case_sensitive_like"]; ok { pkey = "_case_sensitive_like" } if _, ok := params["_cslike"]; ok { pkey = "_cslike" } if val := params.Get(pkey); val != "" { switch strings.ToLower(val) { case "0", "no", "false", "off": caseSensitiveLike = 0 case "1", "yes", "true", "on": caseSensitiveLike = 1 default: return nil, fmt.Errorf("Invalid _case_sensitive_like: %v, expecting boolean value of '0 1 false true no yes off on'", val) } } // Defer Foreign Keys (_defer_foreign_keys | _defer_fk) // // https://www.sqlite.org/pragma.html#pragma_defer_foreign_keys // pkey = "" // Reset pkey if _, ok := params["_defer_foreign_keys"]; ok { pkey = "_defer_foreign_keys" } if _, ok := params["_defer_fk"]; ok { pkey = "_defer_fk" } if val := params.Get(pkey); val != "" { switch strings.ToLower(val) { case "0", "no", "false", "off": deferForeignKeys = 0 case "1", "yes", "true", "on": deferForeignKeys = 1 default: return nil, fmt.Errorf("Invalid _defer_foreign_keys: %v, expecting boolean value of '0 1 false true no yes off on'", val) } } // Ignore CHECK Constrains (_ignore_check_constraints) // // https://www.sqlite.org/pragma.html#pragma_ignore_check_constraints // if val := params.Get("_ignore_check_constraints"); val != "" { switch strings.ToLower(val) { case "0", "no", "false", "off": ignoreCheckConstraints = 0 case "1", "yes", "true", "on": ignoreCheckConstraints = 1 default: return nil, fmt.Errorf("Invalid _ignore_check_constraints: %v, expecting boolean value of '0 1 false true no yes off on'", val) } } // Locking Mode (_locking) // // https://www.sqlite.org/pragma.html#pragma_locking_mode // pkey = "" // Reset pkey if _, ok := params["_locking_mode"]; ok { pkey = "_locking_mode" } if _, ok := params["_locking"]; ok { pkey = "_locking" } if val := params.Get(pkey); val != "" { switch strings.ToUpper(val) { case "NORMAL", "EXCLUSIVE": lockingMode = strings.ToUpper(val) default: return nil, fmt.Errorf("Invalid _locking_mode: %v, expecting value of 'NORMAL EXCLUSIVE", val) } } // Query Only (_query_only) // // https://www.sqlite.org/pragma.html#pragma_query_only // if val := params.Get("_query_only"); val != "" { switch strings.ToLower(val) { case "0", "no", "false", "off": queryOnly = 0 case "1", "yes", "true", "on": queryOnly = 1 default: return nil, fmt.Errorf("Invalid _query_only: %v, expecting boolean value of '0 1 false true no yes off on'", val) } } // Recursive Triggers (_recursive_triggers) // // https://www.sqlite.org/pragma.html#pragma_recursive_triggers // pkey = "" // Reset pkey if _, ok := params["_recursive_triggers"]; ok { pkey = "_recursive_triggers" } if _, ok := params["_rt"]; ok { pkey = "_rt" } if val := params.Get(pkey); val != "" { switch strings.ToLower(val) { case "0", "no", "false", "off": recursiveTriggers = 0 case "1", "yes", "true", "on": recursiveTriggers = 1 default: return nil, fmt.Errorf("Invalid _recursive_triggers: %v, expecting boolean value of '0 1 false true no yes off on'", val) } } // Writable Schema (_writeable_schema) // // https://www.sqlite.org/pragma.html#pragma_writeable_schema // if val := params.Get("_writable_schema"); val != "" { switch strings.ToLower(val) { case "0", "no", "false", "off": writableSchema = 0 case "1", "yes", "true", "on": writableSchema = 1 default: return nil, fmt.Errorf("Invalid _writable_schema: %v, expecting boolean value of '0 1 false true no yes off on'", val) } } // Cache size (_cache_size) // // https://sqlite.org/pragma.html#pragma_cache_size // if val := params.Get("_cache_size"); val != "" { iv, err := strconv.ParseInt(val, 10, 64) if err != nil { return nil, fmt.Errorf("Invalid _cache_size: %v: %v", val, err) } cacheSize = &iv } if val := params.Get("vfs"); val != "" { vfsName = val } if !strings.HasPrefix(dsn, "file:") { dsn = dsn[:pos] } } var db *C.sqlite3 name := C.CString(dsn) defer C.free(unsafe.Pointer(name)) var vfs *C.char if vfsName != "" { vfs = C.CString(vfsName) defer C.free(unsafe.Pointer(vfs)) } openFlags := C.SQLITE_OPEN_READWRITE | C.SQLITE_OPEN_CREATE | C.SQLITE_OPEN_URI | mutex rv := C.sqlite3_open_v2(name, &db, openFlags, vfs) 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") } exec := func(s string) error { cs := C.CString(s) rv := C.sqlite3_exec(db, cs, nil, nil, nil) C.free(unsafe.Pointer(cs)) if rv != C.SQLITE_OK { return lastError(db) } return nil } // FIXME: these should be compiled into SQLite // Busy timeout if err := exec(fmt.Sprintf("PRAGMA busy_timeout = %d;", busyTimeout)); err != nil { C.sqlite3_close_v2(db) return nil, err } // Foreign Keys if err := exec("PRAGMA foreign_keys = ON;"); err != nil { C.sqlite3_close_v2(db) return nil, err } // Create connection to SQLite conn := &SQLiteConn{db: db, loc: loc, txlock: txlock} // Auto Vacuum // Moved auto_vacuum command, the user preference for auto_vacuum needs to be implemented directly after // the authentication and before the sqlite_user table gets created if the user // decides to activate User Authentication because // auto_vacuum needs to be set before any tables are created // and activating user authentication creates the internal table `sqlite_user`. if autoVacuum > -1 { if err := exec(fmt.Sprintf("PRAGMA auto_vacuum = %d;", autoVacuum)); err != nil { C.sqlite3_close_v2(db) return nil, err } } // Case Sensitive LIKE if caseSensitiveLike > -1 { if err := exec(fmt.Sprintf("PRAGMA case_sensitive_like = %d;", caseSensitiveLike)); err != nil { C.sqlite3_close_v2(db) return nil, err } } // Defer Foreign Keys if deferForeignKeys > -1 { if err := exec(fmt.Sprintf("PRAGMA defer_foreign_keys = %d;", deferForeignKeys)); err != nil { C.sqlite3_close_v2(db) return nil, err } } // Ignore CHECK Constraints if ignoreCheckConstraints > -1 { if err := exec(fmt.Sprintf("PRAGMA ignore_check_constraints = %d;", ignoreCheckConstraints)); err != nil { C.sqlite3_close_v2(db) return nil, err } } if err := exec("PRAGMA journal_mode = WAL;"); err != nil { C.sqlite3_close_v2(db) return nil, err } // Locking Mode // Because the default is NORMAL and this is not changed in this package // by using the compile time SQLITE_DEFAULT_LOCKING_MODE this PRAGMA can always be executed if err := exec(fmt.Sprintf("PRAGMA locking_mode = %s;", lockingMode)); err != nil { C.sqlite3_close_v2(db) return nil, err } // Query Only if queryOnly > -1 { if err := exec(fmt.Sprintf("PRAGMA query_only = %d;", queryOnly)); err != nil { C.sqlite3_close_v2(db) return nil, err } } // Recursive Triggers if recursiveTriggers > -1 { if err := exec(fmt.Sprintf("PRAGMA recursive_triggers = %d;", recursiveTriggers)); err != nil { C.sqlite3_close_v2(db) return nil, err } } // FIXME: see ~/dev/papo/STUFF/napiqlite/src/napiqlite.c if err := exec("PRAGMA synchronous = EXTRA;"); err != nil { conn.Close() return nil, err } // Writable Schema if writableSchema > -1 { if err := exec(fmt.Sprintf("PRAGMA writable_schema = %d;", writableSchema)); err != nil { C.sqlite3_close_v2(db) return nil, err } } // Cache Size if cacheSize != nil { if err := exec(fmt.Sprintf("PRAGMA cache_size = %d;", *cacheSize)); err != nil { C.sqlite3_close_v2(db) return nil, err } } 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 }