// Copyright 2025 The Sqlite Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package sqlite // import "modernc.org/sqlite" import ( "context" "database/sql/driver" "fmt" "strconv" "strings" "sync" "time" "unsafe" "modernc.org/libc" "modernc.org/libc/sys/types" sqlite3 "modernc.org/sqlite/lib" ) type conn struct { db uintptr // *sqlite3.Xsqlite3 tls *libc.TLS // Context handling can cause conn.Close and conn.interrupt to be invoked // concurrently. sync.Mutex writeTimeFormat string beginMode string intToTime bool integerTimeFormat string } func newConn(dsn string) (*conn, error) { var query, vfsName string // Parse the query parameters from the dsn and them from the dsn if not prefixed by file: // https://github.com/mattn/go-sqlite3/blob/3392062c729d77820afc1f5cae3427f0de39e954/sqlite3.go#L1046 // https://github.com/mattn/go-sqlite3/blob/3392062c729d77820afc1f5cae3427f0de39e954/sqlite3.go#L1383 pos := strings.IndexRune(dsn, '?') if pos >= 1 { query = dsn[pos+1:] var err error vfsName, err = getVFSName(query) if err != nil { return nil, err } if !strings.HasPrefix(dsn, "file:") { dsn = dsn[:pos] } } c := &conn{tls: libc.NewTLS()} db, err := c.openV2( dsn, vfsName, sqlite3.SQLITE_OPEN_READWRITE|sqlite3.SQLITE_OPEN_CREATE| sqlite3.SQLITE_OPEN_FULLMUTEX| sqlite3.SQLITE_OPEN_URI, ) if err != nil { return nil, err } c.db = db if err = c.extendedResultCodes(true); err != nil { c.Close() return nil, err } if err = applyQueryParams(c, query); err != nil { c.Close() return nil, err } return c, nil } // Attempt to parse s as a time. Return (s, false) if s is not // recognized as a valid time encoding. func (c *conn) parseTime(s string) (interface{}, bool) { if v, ok := c.parseTimeString(s, strings.Index(s, "m=")); ok { return v, true } ts := strings.TrimSuffix(s, "Z") for _, f := range parseTimeFormats { t, err := time.Parse(f, ts) if err == nil { return t, true } } return s, false } // Attempt to parse s as a time string produced by t.String(). If x > 0 it's // the index of substring "m=" within s. Return (s, false) if s is // not recognized as a valid time encoding. func (c *conn) parseTimeString(s0 string, x int) (interface{}, bool) { s := s0 if x > 0 { s = s[:x] // "2006-01-02 15:04:05.999999999 -0700 MST m=+9999" -> "2006-01-02 15:04:05.999999999 -0700 MST " } s = strings.TrimSpace(s) if t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", s); err == nil { return t, true } return s0, false } // writeTimeFormats are the names and formats supported // by the `_time_format` DSN query param. var writeTimeFormats = map[string]string{ "sqlite": parseTimeFormats[0], } func (c *conn) formatTime(t time.Time) string { // Before configurable write time formats were supported, // time.Time.String was used. Maintain that default to // keep existing driver users formatting times the same. if c.writeTimeFormat == "" { return t.String() } return t.Format(c.writeTimeFormat) } // C documentation // // const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); func (c *conn) columnBlob(pstmt uintptr, iCol int) (v []byte, err error) { p := sqlite3.Xsqlite3_column_blob(c.tls, pstmt, int32(iCol)) len, err := c.columnBytes(pstmt, iCol) if err != nil { return nil, err } if p == 0 || len == 0 { return nil, nil } v = make([]byte, len) copy(v, (*libc.RawMem)(unsafe.Pointer(p))[:len:len]) return v, nil } // C documentation // // int sqlite3_column_bytes(sqlite3_stmt*, int iCol); func (c *conn) columnBytes(pstmt uintptr, iCol int) (_ int, err error) { v := sqlite3.Xsqlite3_column_bytes(c.tls, pstmt, int32(iCol)) return int(v), nil } // C documentation // // const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); func (c *conn) columnText(pstmt uintptr, iCol int) (v string, err error) { p := sqlite3.Xsqlite3_column_text(c.tls, pstmt, int32(iCol)) len, err := c.columnBytes(pstmt, iCol) if err != nil { return "", err } if p == 0 || len == 0 { return "", nil } b := make([]byte, len) copy(b, (*libc.RawMem)(unsafe.Pointer(p))[:len:len]) return string(b), nil } // C documentation // // double sqlite3_column_double(sqlite3_stmt*, int iCol); func (c *conn) columnDouble(pstmt uintptr, iCol int) (v float64, err error) { v = sqlite3.Xsqlite3_column_double(c.tls, pstmt, int32(iCol)) return v, nil } // C documentation // // sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); func (c *conn) columnInt64(pstmt uintptr, iCol int) (v int64, err error) { v = sqlite3.Xsqlite3_column_int64(c.tls, pstmt, int32(iCol)) return v, nil } // C documentation // // int sqlite3_column_type(sqlite3_stmt*, int iCol); func (c *conn) columnType(pstmt uintptr, iCol int) (_ int, err error) { v := sqlite3.Xsqlite3_column_type(c.tls, pstmt, int32(iCol)) return int(v), nil } // C documentation // // const char *sqlite3_column_decltype(sqlite3_stmt*,int); func (c *conn) columnDeclType(pstmt uintptr, iCol int) string { return libc.GoString(sqlite3.Xsqlite3_column_decltype(c.tls, pstmt, int32(iCol))) } // C documentation // // const char *sqlite3_column_name(sqlite3_stmt*, int N); func (c *conn) columnName(pstmt uintptr, n int) (string, error) { p := sqlite3.Xsqlite3_column_name(c.tls, pstmt, int32(n)) return libc.GoString(p), nil } // C documentation // // int sqlite3_column_count(sqlite3_stmt *pStmt); func (c *conn) columnCount(pstmt uintptr) (_ int, err error) { v := sqlite3.Xsqlite3_column_count(c.tls, pstmt) return int(v), nil } // C documentation // // sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); func (c *conn) lastInsertRowID() (v int64, _ error) { return sqlite3.Xsqlite3_last_insert_rowid(c.tls, c.db), nil } // C documentation // // int sqlite3_changes(sqlite3*); func (c *conn) changes() (int, error) { v := sqlite3.Xsqlite3_changes(c.tls, c.db) return int(v), nil } // C documentation // // int sqlite3_step(sqlite3_stmt*); func (c *conn) step(pstmt uintptr) (int, error) { for { switch rc := sqlite3.Xsqlite3_step(c.tls, pstmt); rc { case sqliteLockedSharedcache: if err := c.retry(pstmt); err != nil { return sqlite3.SQLITE_LOCKED, err } case sqlite3.SQLITE_DONE, sqlite3.SQLITE_ROW: return int(rc), nil default: return int(rc), c.errstr(rc) } } } func (c *conn) retry(pstmt uintptr) error { mu := mutexAlloc(c.tls) (*mutex)(unsafe.Pointer(mu)).Lock() rc := sqlite3.Xsqlite3_unlock_notify( c.tls, c.db, *(*uintptr)(unsafe.Pointer(&struct { f func(*libc.TLS, uintptr, int32) }{unlockNotify})), mu, ) if rc == sqlite3.SQLITE_LOCKED { // Deadlock, see https://www.sqlite.org/c3ref/unlock_notify.html (*mutex)(unsafe.Pointer(mu)).Unlock() mutexFree(c.tls, mu) return c.errstr(rc) } (*mutex)(unsafe.Pointer(mu)).Lock() (*mutex)(unsafe.Pointer(mu)).Unlock() mutexFree(c.tls, mu) if pstmt != 0 { sqlite3.Xsqlite3_reset(c.tls, pstmt) } return nil } func (c *conn) bind(pstmt uintptr, n int, args []driver.NamedValue) (allocs []uintptr, err error) { defer func() { if err == nil { return } for _, v := range allocs { c.free(v) } allocs = nil }() for i := 1; i <= n; i++ { name, err := c.bindParameterName(pstmt, i) if err != nil { return allocs, err } var found bool var v driver.NamedValue for _, v = range args { if name != "" { // For ?NNN and $NNN params, match if NNN == v.Ordinal. // // Supporting this for $NNN is a special case that makes eg // `select $1, $2, $3 ...` work without needing to use // sql.Named. if (name[0] == '?' || name[0] == '$') && name[1:] == strconv.Itoa(v.Ordinal) { found = true break } // sqlite supports '$', '@' and ':' prefixes for string // identifiers and '?' for numeric, so we cannot // combine different prefixes with the same name // because `database/sql` requires variable names // to start with a letter if name[1:] == v.Name[:] { found = true break } } else { if v.Ordinal == i { found = true break } } } if !found { if name != "" { return allocs, fmt.Errorf("missing named argument %q", name[1:]) } return allocs, fmt.Errorf("missing argument with index %d", i) } var p uintptr switch x := v.Value.(type) { case int64: if err := c.bindInt64(pstmt, i, x); err != nil { return allocs, err } case float64: if err := c.bindDouble(pstmt, i, x); err != nil { return allocs, err } case bool: v := 0 if x { v = 1 } if err := c.bindInt(pstmt, i, v); err != nil { return allocs, err } case []byte: if p, err = c.bindBlob(pstmt, i, x); err != nil { return allocs, err } case string: if p, err = c.bindText(pstmt, i, x); err != nil { return allocs, err } case time.Time: switch c.integerTimeFormat { case "unix": if err := c.bindInt64(pstmt, i, x.Unix()); err != nil { return allocs, err } case "unix_milli": if err := c.bindInt64(pstmt, i, x.UnixMilli()); err != nil { return allocs, err } case "unix_micro": if err := c.bindInt64(pstmt, i, x.UnixMicro()); err != nil { return allocs, err } case "unix_nano": if err := c.bindInt64(pstmt, i, x.UnixNano()); err != nil { return allocs, err } default: if p, err = c.bindText(pstmt, i, c.formatTime(x)); err != nil { return allocs, err } } case nil: if p, err = c.bindNull(pstmt, i); err != nil { return allocs, err } default: return allocs, fmt.Errorf("sqlite: invalid driver.Value type %T", x) } if p != 0 { allocs = append(allocs, p) } } return allocs, nil } // C documentation // // int sqlite3_bind_null(sqlite3_stmt*, int); func (c *conn) bindNull(pstmt uintptr, idx1 int) (uintptr, error) { if rc := sqlite3.Xsqlite3_bind_null(c.tls, pstmt, int32(idx1)); rc != sqlite3.SQLITE_OK { return 0, c.errstr(rc) } return 0, nil } // C documentation // // int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*)); func (c *conn) bindText(pstmt uintptr, idx1 int, value string) (uintptr, error) { p, err := libc.CString(value) if err != nil { return 0, err } if rc := sqlite3.Xsqlite3_bind_text(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != sqlite3.SQLITE_OK { c.free(p) return 0, c.errstr(rc) } return p, nil } // C documentation // // int sqlite3_bind_int(sqlite3_stmt*, int, int); func (c *conn) bindInt(pstmt uintptr, idx1, value int) (err error) { if rc := sqlite3.Xsqlite3_bind_int(c.tls, pstmt, int32(idx1), int32(value)); rc != sqlite3.SQLITE_OK { return c.errstr(rc) } return nil } // C documentation // // int sqlite3_bind_double(sqlite3_stmt*, int, double); func (c *conn) bindDouble(pstmt uintptr, idx1 int, value float64) (err error) { if rc := sqlite3.Xsqlite3_bind_double(c.tls, pstmt, int32(idx1), value); rc != 0 { return c.errstr(rc) } return nil } // C documentation // // int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); func (c *conn) bindInt64(pstmt uintptr, idx1 int, value int64) (err error) { if rc := sqlite3.Xsqlite3_bind_int64(c.tls, pstmt, int32(idx1), value); rc != sqlite3.SQLITE_OK { return c.errstr(rc) } return nil } // C documentation // // const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); func (c *conn) bindParameterName(pstmt uintptr, i int) (string, error) { p := sqlite3.Xsqlite3_bind_parameter_name(c.tls, pstmt, int32(i)) return libc.GoString(p), nil } // C documentation // // int sqlite3_bind_parameter_count(sqlite3_stmt*); func (c *conn) bindParameterCount(pstmt uintptr) (_ int, err error) { r := sqlite3.Xsqlite3_bind_parameter_count(c.tls, pstmt) return int(r), nil } // C documentation // // int sqlite3_finalize(sqlite3_stmt *pStmt); func (c *conn) finalize(pstmt uintptr) error { if rc := sqlite3.Xsqlite3_finalize(c.tls, pstmt); rc != sqlite3.SQLITE_OK { return c.errstr(rc) } return nil } // C documentation // // int sqlite3_prepare_v2( // sqlite3 *db, /* Database handle */ // const char *zSql, /* SQL statement, UTF-8 encoded */ // int nByte, /* Maximum length of zSql in bytes. */ // sqlite3_stmt **ppStmt, /* OUT: Statement handle */ // const char **pzTail /* OUT: Pointer to unused portion of zSql */ // ); func (c *conn) prepareV2(zSQL *uintptr) (pstmt uintptr, err error) { var ppstmt, pptail uintptr defer func() { c.free(ppstmt) c.free(pptail) }() if ppstmt, err = c.malloc(int(ptrSize)); err != nil { return 0, err } if pptail, err = c.malloc(int(ptrSize)); err != nil { return 0, err } for { // https://gitlab.com/cznic/sqlite/-/issues/236 // trc("Xsqlite3_prepare_v2(`%s`)", libc.GoString(*zSQL)) switch rc := sqlite3.Xsqlite3_prepare_v2(c.tls, c.db, *zSQL, -1, ppstmt, pptail); rc { case sqlite3.SQLITE_OK: *zSQL = *(*uintptr)(unsafe.Pointer(pptail)) return *(*uintptr)(unsafe.Pointer(ppstmt)), nil case sqliteLockedSharedcache: if err := c.retry(0); err != nil { return 0, err } default: return 0, c.errstr(rc) } } } // C documentation // // void sqlite3_interrupt(sqlite3*); func (c *conn) interrupt(pdb uintptr) (err error) { c.Lock() // Defend against race with .Close invoked by context handling. defer c.Unlock() if c.tls != nil { sqlite3.Xsqlite3_interrupt(c.tls, pdb) } return nil } // C documentation // // int sqlite3_extended_result_codes(sqlite3*, int onoff); func (c *conn) extendedResultCodes(on bool) error { if rc := sqlite3.Xsqlite3_extended_result_codes(c.tls, c.db, libc.Bool32(on)); rc != sqlite3.SQLITE_OK { return c.errstr(rc) } return nil } // C documentation // // int sqlite3_open_v2( // const char *filename, /* Database filename (UTF-8) */ // sqlite3 **ppDb, /* OUT: SQLite db handle */ // int flags, /* Flags */ // const char *zVfs /* Name of VFS module to use */ // ); func (c *conn) openV2(name, vfsName string, flags int32) (uintptr, error) { var p, s, vfs uintptr defer func() { if p != 0 { c.free(p) } if s != 0 { c.free(s) } if vfs != 0 { c.free(vfs) } }() p, err := c.malloc(int(ptrSize)) if err != nil { return 0, err } if s, err = libc.CString(name); err != nil { return 0, err } if vfsName != "" { if vfs, err = libc.CString(vfsName); err != nil { return 0, err } } if rc := sqlite3.Xsqlite3_open_v2(c.tls, s, p, flags, vfs); rc != sqlite3.SQLITE_OK { return 0, c.errstr(rc) } return *(*uintptr)(unsafe.Pointer(p)), nil } func (c *conn) malloc(n int) (uintptr, error) { if p := libc.Xmalloc(c.tls, types.Size_t(n)); p != 0 || n == 0 { return p, nil } return 0, fmt.Errorf("sqlite: cannot allocate %d bytes of memory", n) } func (c *conn) free(p uintptr) { if p != 0 { libc.Xfree(c.tls, p) } } // C documentation // // const char *sqlite3_errstr(int); func (c *conn) errstr(rc int32) error { p := sqlite3.Xsqlite3_errstr(c.tls, rc) str := libc.GoString(p) p = sqlite3.Xsqlite3_errmsg(c.tls, c.db) var s string if rc == sqlite3.SQLITE_BUSY { s = " (SQLITE_BUSY)" } switch msg := libc.GoString(p); { case msg == str: return &Error{msg: fmt.Sprintf("%s (%v)%s", str, rc, s), code: int(rc)} default: return &Error{msg: fmt.Sprintf("%s: %s (%v)%s", str, msg, rc, s), code: int(rc)} } } // Begin starts a transaction. // // Deprecated: Drivers should implement ConnBeginTx instead (or additionally). func (c *conn) Begin() (dt driver.Tx, err error) { if dmesgs { defer func() { dmesg("conn %p: (driver.Tx %p, err %v)", c, dt, err) }() } return c.begin(context.Background(), driver.TxOptions{}) } func (c *conn) begin(ctx context.Context, opts driver.TxOptions) (t driver.Tx, err error) { return newTx(ctx, c, opts) } // Close invalidates and potentially stops any current prepared statements and // transactions, marking this connection as no longer in use. // // Because the sql package maintains a free pool of connections and only calls // Close when there's a surplus of idle connections, it shouldn't be necessary // for drivers to do their own connection caching. func (c *conn) Close() (err error) { if dmesgs { defer func() { dmesg("conn %p: err %v", c, err) }() } c.Lock() // Defend against race with .interrupt invoked by context handling. defer c.Unlock() if c.db != 0 { if err := c.closeV2(c.db); err != nil { return err } c.db = 0 } if c.tls != nil { c.tls.Close() c.tls = nil } return nil } // C documentation // // int sqlite3_close_v2(sqlite3*); func (c *conn) closeV2(db uintptr) error { if rc := sqlite3.Xsqlite3_close_v2(c.tls, db); rc != sqlite3.SQLITE_OK { return c.errstr(rc) } return nil } // ResetSession is called prior to executing a query on the connection if the // connection has been used before. If the driver returns ErrBadConn the // connection is discarded. func (c *conn) ResetSession(ctx context.Context) error { if !c.usable() { return driver.ErrBadConn } return nil } // IsValid is called prior to placing the connection into the connection pool. // The connection will be discarded if false is returned. func (c *conn) IsValid() bool { return c.usable() } func (c *conn) usable() bool { return c.db != 0 && sqlite3.Xsqlite3_is_interrupted(c.tls, c.db) == 0 } type userDefinedFunction struct { zFuncName uintptr nArg int32 eTextRep int32 pApp uintptr scalar bool freeOnce sync.Once } func (c *conn) createFunctionInternal(fun *userDefinedFunction) error { var rc int32 if fun.scalar { rc = sqlite3.Xsqlite3_create_function( c.tls, c.db, fun.zFuncName, fun.nArg, fun.eTextRep, fun.pApp, cFuncPointer(funcTrampoline), 0, 0, ) } else { rc = sqlite3.Xsqlite3_create_window_function( c.tls, c.db, fun.zFuncName, fun.nArg, fun.eTextRep, fun.pApp, cFuncPointer(stepTrampoline), cFuncPointer(finalTrampoline), cFuncPointer(valueTrampoline), cFuncPointer(inverseTrampoline), 0, ) } if rc != sqlite3.SQLITE_OK { return c.errstr(rc) } return nil } func (c *conn) createCollationInternal(coll *collation) error { rc := sqlite3.Xsqlite3_create_collation_v2( c.tls, c.db, coll.zName, coll.enc, coll.pApp, cFuncPointer(collationTrampoline), 0, ) if rc != sqlite3.SQLITE_OK { return c.errstr(rc) } return nil } // Execer is an optional interface that may be implemented by a Conn. // // If a Conn does not implement Execer, the sql package's DB.Exec will first // prepare a query, execute the statement, and then close the statement. // // Exec may return ErrSkip. // // Deprecated: Drivers should implement ExecerContext instead. func (c *conn) Exec(query string, args []driver.Value) (dr driver.Result, err error) { if dmesgs { defer func() { dmesg("conn %p, query %q, args %v: (driver.Result %p, err %v)", c, query, args, dr, err) }() } return c.exec(context.Background(), query, toNamedValues(args)) } func (c *conn) exec(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) { s, err := c.prepare(ctx, query) if err != nil { return nil, err } defer func() { if err2 := s.Close(); err2 != nil && err == nil { err = err2 } }() return s.(*stmt).exec(ctx, args) } // Prepare returns a prepared statement, bound to this connection. func (c *conn) Prepare(query string) (ds driver.Stmt, err error) { if dmesgs { defer func() { dmesg("conn %p, query %q: (driver.Stmt %p, err %v)", c, query, ds, err) }() } return c.prepare(context.Background(), query) } func (c *conn) prepare(ctx context.Context, query string) (s driver.Stmt, err error) { //TODO use ctx return newStmt(c, query) } // Queryer is an optional interface that may be implemented by a Conn. // // If a Conn does not implement Queryer, the sql package's DB.Query will first // prepare a query, execute the statement, and then close the statement. // // Query may return ErrSkip. // // Deprecated: Drivers should implement QueryerContext instead. func (c *conn) Query(query string, args []driver.Value) (dr driver.Rows, err error) { if dmesgs { defer func() { dmesg("conn %p, query %q, args %v: (driver.Rows %p, err %v)", c, query, args, dr, err) }() } return c.query(context.Background(), query, toNamedValues(args)) } func (c *conn) query(ctx context.Context, query string, args []driver.NamedValue) (r driver.Rows, err error) { // Use newStmt directly. c.prepare wraps this, but we need the concrete *stmt type // to manipulate the handle ownership below. s, err := newStmt(c, query) if err != nil { return nil, err } r, err = s.query(ctx, args) if err != nil { s.Close() return nil, err } // Fix for TestIssue118 / One-Shot Query Crash: // If the statement was cached (optimized path in newStmt), s.pstmt is valid. // s.query() sets r.reuseStmt = true by default for cached statements. // // However, since this is a transient statement created just for this query (db.Query), // s.Close() will be called immediately below. We must transfer ownership of the // sqlite statement handle to the rows object so it isn't finalized yet. if s.pstmt != 0 { // Steal the handle from the statement so s.Close() doesn't finalize it. s.pstmt = 0 // Instruct rows to finalize the statement when done, rather than resetting it. r.(*rows).reuseStmt = false } // s.Close() now only frees the C-string allocation (psql), skipping the pstmt finalize // because we set s.pstmt to 0. if err := s.Close(); err != nil { r.Close() return nil, err } return r, nil } // Serialize returns a serialization of the main database. For an ordinary on-disk // database file, the serialization is just a copy of the disk file. For an in-memory // database or a "TEMP" database, the serialization is the same sequence of bytes // which would be written to disk if that database where backed up to disk. func (c *conn) Serialize() (v []byte, err error) { pLen := c.tls.Alloc(8) defer c.tls.Free(8) zSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0) if zSchema == 0 { return nil, fmt.Errorf("failed to get main db name") } pBuf := sqlite3.Xsqlite3_serialize(c.tls, c.db, zSchema, pLen, 0) bufLen := *(*sqlite3.Sqlite3_int64)(unsafe.Pointer(pLen)) if pBuf != 0 { defer sqlite3.Xsqlite3_free(c.tls, pBuf) } if bufLen <= 0 { return nil, fmt.Errorf("invalid length returned: %d", bufLen) } else if pBuf == 0 || bufLen == 0 { return nil, nil } v = make([]byte, bufLen) copy(v, (*libc.RawMem)(unsafe.Pointer(pBuf))[:bufLen:bufLen]) return v, nil } // Deserialize restore a database from the content returned by Serialize. func (c *conn) Deserialize(buf []byte) (err error) { bufLen := len(buf) pBuf := c.tls.Alloc(bufLen) // free will be done if it fails or on close, must not be freed here copy((*libc.RawMem)(unsafe.Pointer(pBuf))[:bufLen:bufLen], buf) zSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0) if zSchema == 0 { return fmt.Errorf("failed to get main db name") } rc := sqlite3.Xsqlite3_deserialize(c.tls, c.db, zSchema, pBuf, int64(bufLen), int64(bufLen), sqlite3.SQLITE_DESERIALIZE_RESIZEABLE|sqlite3.SQLITE_DESERIALIZE_FREEONCLOSE) if rc != sqlite3.SQLITE_OK { return c.errstr(rc) } return nil } // NewBackup returns a Backup object that will create an online backup of // current database to the databased pointed by the passed URI. func (c *conn) NewBackup(dstUri string) (*Backup, error) { dstConn, err := newConn(dstUri) if err != nil { return nil, err } backup, err := c.backup(dstConn, false) if err != nil { dstConn.Close() } return backup, err } // NewRestore returns a Backup object that will restore a backup to current // database from the databased pointed by the passed URI. func (c *conn) NewRestore(srcUri string) (*Backup, error) { srcConn, err := newConn(srcUri) if err != nil { return nil, err } backup, err := c.backup(srcConn, true) if err != nil { srcConn.Close() } return backup, err } func (c *conn) backup(remoteConn *conn, restore bool) (_ *Backup, finalErr error) { srcSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0) if srcSchema == 0 { return nil, fmt.Errorf("failed to get main source db name") } dstSchema := sqlite3.Xsqlite3_db_name(remoteConn.tls, remoteConn.db, 0) if dstSchema == 0 { return nil, fmt.Errorf("failed to get main destination db name") } var pBackup uintptr if restore { pBackup = sqlite3.Xsqlite3_backup_init(c.tls, c.db, srcSchema, remoteConn.db, dstSchema) } else { pBackup = sqlite3.Xsqlite3_backup_init(c.tls, remoteConn.db, dstSchema, c.db, srcSchema) } if pBackup <= 0 { rc := sqlite3.Xsqlite3_errcode(c.tls, remoteConn.db) return nil, c.errstr(rc) } return &Backup{srcConn: c, dstConn: remoteConn, pBackup: pBackup}, nil } // C documentation // // int sqlite3_limit(sqlite3*, int id, int newVal); func (c *conn) limit(id int, newVal int) int { return int(sqlite3.Xsqlite3_limit(c.tls, c.db, int32(id), int32(newVal))) } // C documentation // // int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); func (c *conn) bindBlob(pstmt uintptr, idx1 int, value []byte) (uintptr, error) { if value == nil { if rc := sqlite3.Xsqlite3_bind_null(c.tls, pstmt, int32(idx1)); rc != sqlite3.SQLITE_OK { return 0, c.errstr(rc) } return 0, nil } p, err := c.malloc(len(value)) if err != nil { return 0, err } if len(value) != 0 { copy((*libc.RawMem)(unsafe.Pointer(p))[:len(value):len(value)], value) } if rc := sqlite3.Xsqlite3_bind_blob(c.tls, pstmt, int32(idx1), p, int32(len(value)), 0); rc != sqlite3.SQLITE_OK { c.free(p) return 0, c.errstr(rc) } return p, nil } // Ping implements driver.Pinger func (c *conn) Ping(ctx context.Context) (err error) { if dmesgs { defer func() { dmesg("conn %p, ctx %p: err %v", c, ctx, err) }() } _, err = c.ExecContext(ctx, "select 1", nil) return err } // BeginTx implements driver.ConnBeginTx func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (dt driver.Tx, err error) { if dmesgs { defer func() { dmesg("conn %p, ctx %p, opts %+v: (driver.Tx %v, err %v)", c, ctx, opts, dt, err) }() } return c.begin(ctx, opts) } // PrepareContext implements driver.ConnPrepareContext func (c *conn) PrepareContext(ctx context.Context, query string) (ds driver.Stmt, err error) { if dmesgs { defer func() { dmesg("conn %p, ctx %p, query %q: (driver.Stmt %v, err %v)", c, ctx, query, ds, err) }() } return c.prepare(ctx, query) } // ExecContext implements driver.ExecerContext func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (dr driver.Result, err error) { if dmesgs { defer func() { dmesg("conn %p, ctx %p, query %q, args %v: (driver.Result %p, err %v)", c, ctx, query, args, dr, err) }() } return c.exec(ctx, query, args) } // QueryContext implements driver.QueryerContext func (c *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (dr driver.Rows, err error) { if dmesgs { defer func() { dmesg("conn %p, ctx %p, query %q, args %v: (driver.Rows %p, err %v)", c, ctx, query, args, dr, err) }() } return c.query(ctx, query, args) } // IsReadOnly reports whether the database schema specified by dbName is read-only. // // dbName is the internal name of the attached database, not the filename. // Use "main" for the primary database, "temp" for the temporary database, // or the name used in an ATTACH statement. func (c *conn) IsReadOnly(schema string) (bool, error) { if dmesgs { defer func() { dmesg("conn %p", c) }() } cs, err := libc.CString(schema) if err != nil { return false, err } defer libc.Xfree(c.tls, cs) switch r := sqlite3.Xsqlite3_db_readonly(c.tls, c.db, cs); r { case 1: return true, nil case 0: return false, nil case -1: return false, fmt.Errorf("not a name of a database on connection: '%s'", schema) default: return false, fmt.Errorf("unexpected sqlite3_db_readonly(%q) return value: %v", schema, r) } }