musiclink/vendor/modernc.org/sqlite/conn.go

1101 lines
28 KiB
Go

// 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)
}
}