// Copyright 2017 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. //go:generate go run generator.go -full-path-comments package sqlite // import "modernc.org/sqlite" import ( "context" "database/sql" "database/sql/driver" "errors" "fmt" "io" "math" "math/bits" "net/url" "reflect" "runtime" "strconv" "strings" "sync" "time" "unsafe" "modernc.org/libc" "modernc.org/libc/sys/types" sqlite3 "modernc.org/sqlite/lib" ) var ( _ driver.Conn = (*conn)(nil) _ driver.Driver = (*Driver)(nil) //lint:ignore SA1019 TODO implement ExecerContext _ driver.Execer = (*conn)(nil) //lint:ignore SA1019 TODO implement QueryerContext _ driver.Queryer = (*conn)(nil) _ driver.Result = (*result)(nil) _ driver.Rows = (*rows)(nil) _ driver.RowsColumnTypeDatabaseTypeName = (*rows)(nil) _ driver.RowsColumnTypeLength = (*rows)(nil) _ driver.RowsColumnTypeNullable = (*rows)(nil) _ driver.RowsColumnTypePrecisionScale = (*rows)(nil) _ driver.RowsColumnTypeScanType = (*rows)(nil) _ driver.Stmt = (*stmt)(nil) _ driver.Tx = (*tx)(nil) _ error = (*Error)(nil) ) const ( driverName = "sqlite" ptrSize = unsafe.Sizeof(uintptr(0)) sqliteLockedSharedcache = sqlite3.SQLITE_LOCKED | (1 << 8) ) // Error represents sqlite library error code. type Error struct { msg string code int } // Error implements error. func (e *Error) Error() string { return e.msg } // Code returns the sqlite result code for this error. func (e *Error) Code() int { return e.code } var ( // ErrorCodeString maps Error.Code() to its string representation. ErrorCodeString = map[int]string{ sqlite3.SQLITE_ABORT: "Callback routine requested an abort (SQLITE_ABORT)", sqlite3.SQLITE_AUTH: "Authorization denied (SQLITE_AUTH)", sqlite3.SQLITE_BUSY: "The database file is locked (SQLITE_BUSY)", sqlite3.SQLITE_CANTOPEN: "Unable to open the database file (SQLITE_CANTOPEN)", sqlite3.SQLITE_CONSTRAINT: "Abort due to constraint violation (SQLITE_CONSTRAINT)", sqlite3.SQLITE_CORRUPT: "The database disk image is malformed (SQLITE_CORRUPT)", sqlite3.SQLITE_DONE: "sqlite3_step() has finished executing (SQLITE_DONE)", sqlite3.SQLITE_EMPTY: "Internal use only (SQLITE_EMPTY)", sqlite3.SQLITE_ERROR: "Generic error (SQLITE_ERROR)", sqlite3.SQLITE_FORMAT: "Not used (SQLITE_FORMAT)", sqlite3.SQLITE_FULL: "Insertion failed because database is full (SQLITE_FULL)", sqlite3.SQLITE_INTERNAL: "Internal logic error in SQLite (SQLITE_INTERNAL)", sqlite3.SQLITE_INTERRUPT: "Operation terminated by sqlite3_interrupt()(SQLITE_INTERRUPT)", sqlite3.SQLITE_IOERR | (1 << 8): "(SQLITE_IOERR_READ)", sqlite3.SQLITE_IOERR | (10 << 8): "(SQLITE_IOERR_DELETE)", sqlite3.SQLITE_IOERR | (11 << 8): "(SQLITE_IOERR_BLOCKED)", sqlite3.SQLITE_IOERR | (12 << 8): "(SQLITE_IOERR_NOMEM)", sqlite3.SQLITE_IOERR | (13 << 8): "(SQLITE_IOERR_ACCESS)", sqlite3.SQLITE_IOERR | (14 << 8): "(SQLITE_IOERR_CHECKRESERVEDLOCK)", sqlite3.SQLITE_IOERR | (15 << 8): "(SQLITE_IOERR_LOCK)", sqlite3.SQLITE_IOERR | (16 << 8): "(SQLITE_IOERR_CLOSE)", sqlite3.SQLITE_IOERR | (17 << 8): "(SQLITE_IOERR_DIR_CLOSE)", sqlite3.SQLITE_IOERR | (2 << 8): "(SQLITE_IOERR_SHORT_READ)", sqlite3.SQLITE_IOERR | (3 << 8): "(SQLITE_IOERR_WRITE)", sqlite3.SQLITE_IOERR | (4 << 8): "(SQLITE_IOERR_FSYNC)", sqlite3.SQLITE_IOERR | (5 << 8): "(SQLITE_IOERR_DIR_FSYNC)", sqlite3.SQLITE_IOERR | (6 << 8): "(SQLITE_IOERR_TRUNCATE)", sqlite3.SQLITE_IOERR | (7 << 8): "(SQLITE_IOERR_FSTAT)", sqlite3.SQLITE_IOERR | (8 << 8): "(SQLITE_IOERR_UNLOCK)", sqlite3.SQLITE_IOERR | (9 << 8): "(SQLITE_IOERR_RDLOCK)", sqlite3.SQLITE_IOERR: "Some kind of disk I/O error occurred (SQLITE_IOERR)", sqlite3.SQLITE_LOCKED | (1 << 8): "(SQLITE_LOCKED_SHAREDCACHE)", sqlite3.SQLITE_LOCKED: "A table in the database is locked (SQLITE_LOCKED)", sqlite3.SQLITE_MISMATCH: "Data type mismatch (SQLITE_MISMATCH)", sqlite3.SQLITE_MISUSE: "Library used incorrectly (SQLITE_MISUSE)", sqlite3.SQLITE_NOLFS: "Uses OS features not supported on host (SQLITE_NOLFS)", sqlite3.SQLITE_NOMEM: "A malloc() failed (SQLITE_NOMEM)", sqlite3.SQLITE_NOTADB: "File opened that is not a database file (SQLITE_NOTADB)", sqlite3.SQLITE_NOTFOUND: "Unknown opcode in sqlite3_file_control() (SQLITE_NOTFOUND)", sqlite3.SQLITE_NOTICE: "Notifications from sqlite3_log() (SQLITE_NOTICE)", sqlite3.SQLITE_PERM: "Access permission denied (SQLITE_PERM)", sqlite3.SQLITE_PROTOCOL: "Database lock protocol error (SQLITE_PROTOCOL)", sqlite3.SQLITE_RANGE: "2nd parameter to sqlite3_bind out of range (SQLITE_RANGE)", sqlite3.SQLITE_READONLY: "Attempt to write a readonly database (SQLITE_READONLY)", sqlite3.SQLITE_ROW: "sqlite3_step() has another row ready (SQLITE_ROW)", sqlite3.SQLITE_SCHEMA: "The database schema changed (SQLITE_SCHEMA)", sqlite3.SQLITE_TOOBIG: "String or BLOB exceeds size limit (SQLITE_TOOBIG)", sqlite3.SQLITE_WARNING: "Warnings from sqlite3_log() (SQLITE_WARNING)", } ) func init() { sql.Register(driverName, newDriver()) } type result struct { lastInsertID int64 rowsAffected int } func newResult(c *conn) (_ *result, err error) { r := &result{} if r.rowsAffected, err = c.changes(); err != nil { return nil, err } if r.lastInsertID, err = c.lastInsertRowID(); err != nil { return nil, err } return r, nil } // LastInsertId returns the database's auto-generated ID after, for example, an // INSERT into a table with primary key. func (r *result) LastInsertId() (int64, error) { if r == nil { return 0, nil } return r.lastInsertID, nil } // RowsAffected returns the number of rows affected by the query. func (r *result) RowsAffected() (int64, error) { if r == nil { return 0, nil } return int64(r.rowsAffected), nil } type rows struct { allocs []uintptr c *conn columns []string pstmt uintptr doStep bool empty bool } func newRows(c *conn, pstmt uintptr, allocs []uintptr, empty bool) (r *rows, err error) { r = &rows{c: c, pstmt: pstmt, allocs: allocs, empty: empty} defer func() { if err != nil { r.Close() r = nil } }() n, err := c.columnCount(pstmt) if err != nil { return nil, err } r.columns = make([]string, n) for i := range r.columns { if r.columns[i], err = r.c.columnName(pstmt, i); err != nil { return nil, err } } return r, nil } // Close closes the rows iterator. func (r *rows) Close() (err error) { for _, v := range r.allocs { r.c.free(v) } r.allocs = nil return r.c.finalize(r.pstmt) } // Columns returns the names of the columns. The number of columns of the // result is inferred from the length of the slice. If a particular column name // isn't known, an empty string should be returned for that entry. func (r *rows) Columns() (c []string) { return r.columns } // Next is called to populate the next row of data into the provided slice. The // provided slice will be the same size as the Columns() are wide. // // Next should return io.EOF when there are no more rows. func (r *rows) Next(dest []driver.Value) (err error) { if r.empty { return io.EOF } rc := sqlite3.SQLITE_ROW if r.doStep { if rc, err = r.c.step(r.pstmt); err != nil { return err } } r.doStep = true switch rc { case sqlite3.SQLITE_ROW: if g, e := len(dest), len(r.columns); g != e { return fmt.Errorf("sqlite: Next: have %v destination values, expected %v", g, e) } for i := range dest { ct, err := r.c.columnType(r.pstmt, i) if err != nil { return err } switch ct { case sqlite3.SQLITE_INTEGER: v, err := r.c.columnInt64(r.pstmt, i) if err != nil { return err } dest[i] = v case sqlite3.SQLITE_FLOAT: v, err := r.c.columnDouble(r.pstmt, i) if err != nil { return err } dest[i] = v case sqlite3.SQLITE_TEXT: v, err := r.c.columnText(r.pstmt, i) if err != nil { return err } switch r.ColumnTypeDatabaseTypeName(i) { case "DATE", "DATETIME", "TIMESTAMP": dest[i], _ = r.c.parseTime(v) default: dest[i] = v } case sqlite3.SQLITE_BLOB: v, err := r.c.columnBlob(r.pstmt, i) if err != nil { return err } dest[i] = v case sqlite3.SQLITE_NULL: dest[i] = nil default: return fmt.Errorf("internal error: rc %d", rc) } } return nil case sqlite3.SQLITE_DONE: return io.EOF default: return r.c.errstr(int32(rc)) } } // Inspired by mattn/go-sqlite3: https://github.com/mattn/go-sqlite3/blob/ab91e934/sqlite3.go#L210-L226 // // These time.Parse formats handle formats 1 through 7 listed at https://www.sqlite.org/lang_datefunc.html. var parseTimeFormats = []string{ "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", "2006-01-02T15:04", "2006-01-02", } // 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) } // RowsColumnTypeDatabaseTypeName may be implemented by Rows. It should return // the database system type name without the length. Type names should be // uppercase. Examples of returned types: "VARCHAR", "NVARCHAR", "VARCHAR2", // "CHAR", "TEXT", "DECIMAL", "SMALLINT", "INT", "BIGINT", "BOOL", "[]BIGINT", // "JSONB", "XML", "TIMESTAMP". func (r *rows) ColumnTypeDatabaseTypeName(index int) string { return strings.ToUpper(r.c.columnDeclType(r.pstmt, index)) } // RowsColumnTypeLength may be implemented by Rows. It should return the length // of the column type if the column is a variable length type. If the column is // not a variable length type ok should return false. If length is not limited // other than system limits, it should return math.MaxInt64. The following are // examples of returned values for various types: // // TEXT (math.MaxInt64, true) // varchar(10) (10, true) // nvarchar(10) (10, true) // decimal (0, false) // int (0, false) // bytea(30) (30, true) func (r *rows) ColumnTypeLength(index int) (length int64, ok bool) { t, err := r.c.columnType(r.pstmt, index) if err != nil { return 0, false } switch t { case sqlite3.SQLITE_INTEGER: return 0, false case sqlite3.SQLITE_FLOAT: return 0, false case sqlite3.SQLITE_TEXT: return math.MaxInt64, true case sqlite3.SQLITE_BLOB: return math.MaxInt64, true case sqlite3.SQLITE_NULL: return 0, false default: return 0, false } } // RowsColumnTypeNullable may be implemented by Rows. The nullable value should // be true if it is known the column may be null, or false if the column is // known to be not nullable. If the column nullability is unknown, ok should be // false. func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) { return true, true } // RowsColumnTypePrecisionScale may be implemented by Rows. It should return // the precision and scale for decimal types. If not applicable, ok should be // false. The following are examples of returned values for various types: // // decimal(38, 4) (38, 4, true) // int (0, 0, false) // decimal (math.MaxInt64, math.MaxInt64, true) func (r *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { return 0, 0, false } // RowsColumnTypeScanType may be implemented by Rows. It should return the // value type that can be used to scan types into. For example, the database // column type "bigint" this should return "reflect.TypeOf(int64(0))". func (r *rows) ColumnTypeScanType(index int) reflect.Type { t, err := r.c.columnType(r.pstmt, index) if err != nil { return reflect.TypeOf("") } switch t { case sqlite3.SQLITE_INTEGER: switch strings.ToLower(r.c.columnDeclType(r.pstmt, index)) { case "boolean": return reflect.TypeOf(false) case "date", "datetime", "time", "timestamp": return reflect.TypeOf(time.Time{}) default: return reflect.TypeOf(int64(0)) } case sqlite3.SQLITE_FLOAT: return reflect.TypeOf(float64(0)) case sqlite3.SQLITE_TEXT: return reflect.TypeOf("") case sqlite3.SQLITE_BLOB: return reflect.TypeOf([]byte(nil)) case sqlite3.SQLITE_NULL: return reflect.TypeOf(nil) default: return reflect.TypeOf("") } } type stmt struct { c *conn psql uintptr } func newStmt(c *conn, sql string) (*stmt, error) { p, err := libc.CString(sql) if err != nil { return nil, err } stm := stmt{c: c, psql: p} return &stm, nil } // Close closes the statement. // // As of Go 1.1, a Stmt will not be closed if it's in use by any queries. func (s *stmt) Close() (err error) { s.c.free(s.psql) s.psql = 0 return nil } // Exec executes a query that doesn't return rows, such as an INSERT or UPDATE. // // Deprecated: Drivers should implement StmtExecContext instead (or // additionally). func (s *stmt) Exec(args []driver.Value) (driver.Result, error) { //TODO StmtExecContext return s.exec(context.Background(), toNamedValues(args)) } // toNamedValues converts []driver.Value to []driver.NamedValue func toNamedValues(vals []driver.Value) (r []driver.NamedValue) { r = make([]driver.NamedValue, len(vals)) for i, val := range vals { r[i] = driver.NamedValue{Value: val, Ordinal: i + 1} } return r } func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Result, err error) { var pstmt uintptr defer func() { if pstmt != 0 { // ensure stmt finalized. e := s.c.finalize(pstmt) if err == nil && e != nil { // prioritize original // returned error. err = e } } }() for psql := s.psql; *(*byte)(unsafe.Pointer(psql)) != 0; { if err := ctx.Err(); err != nil { return nil, err } if pstmt, err = s.c.prepareV2(&psql); err != nil { return nil, err } if pstmt == 0 { continue } err = func() (err error) { n, err := s.c.bindParameterCount(pstmt) if err != nil { return err } if n != 0 { allocs, err := s.c.bind(pstmt, n, args) if err != nil { return err } if len(allocs) != 0 { defer func() { for _, v := range allocs { s.c.free(v) } }() } } rc, err := s.c.step(pstmt) if err != nil { return err } switch rc & 0xff { case sqlite3.SQLITE_DONE, sqlite3.SQLITE_ROW: r, err = newResult(s.c) default: return s.c.errstr(int32(rc)) } return nil }() e := s.c.finalize(pstmt) pstmt = 0 // done with if err == nil && e != nil { // prioritize original // returned error. err = e } if err != nil { return nil, err } } return r, err } // NumInput returns the number of placeholder parameters. // // If NumInput returns >= 0, the sql package will sanity check argument counts // from callers and return errors to the caller before the statement's Exec or // Query methods are called. // // NumInput may also return -1, if the driver doesn't know its number of // placeholders. In that case, the sql package will not sanity check Exec or // Query argument counts. func (s *stmt) NumInput() (n int) { return -1 } // Query executes a query that may return rows, such as a // SELECT. // // Deprecated: Drivers should implement StmtQueryContext instead (or // additionally). func (s *stmt) Query(args []driver.Value) (driver.Rows, error) { //TODO StmtQueryContext return s.query(context.Background(), toNamedValues(args)) } func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Rows, err error) { var pstmt uintptr var allocs []uintptr defer func() { if pstmt != 0 { // ensure stmt finalized. e := s.c.finalize(pstmt) if err == nil && e != nil { // prioritize original // returned error. err = e } } if r == nil && err == nil { r, err = newRows(s.c, pstmt, allocs, true) } }() for psql := s.psql; *(*byte)(unsafe.Pointer(psql)) != 0; { if err := ctx.Err(); err != nil { return nil, err } if pstmt, err = s.c.prepareV2(&psql); err != nil { return nil, err } if pstmt == 0 { continue } err = func() (err error) { n, err := s.c.bindParameterCount(pstmt) if err != nil { return err } if n != 0 { if allocs, err = s.c.bind(pstmt, n, args); err != nil { return err } } rc, err := s.c.step(pstmt) if err != nil { return err } switch rc & 0xff { case sqlite3.SQLITE_ROW: if r != nil { r.Close() } if r, err = newRows(s.c, pstmt, allocs, false); err != nil { return err } pstmt = 0 return nil case sqlite3.SQLITE_DONE: if r == nil { if r, err = newRows(s.c, pstmt, allocs, true); err != nil { return err } pstmt = 0 return nil } // nop default: return s.c.errstr(int32(rc)) } if *(*byte)(unsafe.Pointer(psql)) == 0 { if r != nil { r.Close() } if r, err = newRows(s.c, pstmt, allocs, true); err != nil { return err } pstmt = 0 } return nil }() e := s.c.finalize(pstmt) pstmt = 0 // done with if err == nil && e != nil { // prioritize original // returned error. err = e } if err != nil { return nil, err } } return r, err } type tx struct { c *conn } func newTx(ctx context.Context, c *conn, opts driver.TxOptions) (*tx, error) { r := &tx{c: c} sql := "begin" if !opts.ReadOnly && c.beginMode != "" { sql = "begin " + c.beginMode } if err := r.exec(ctx, sql); err != nil { return nil, err } return r, nil } // Commit implements driver.Tx. func (t *tx) Commit() (err error) { return t.exec(context.Background(), "commit") } // Rollback implements driver.Tx. func (t *tx) Rollback() (err error) { return t.exec(context.Background(), "rollback") } func (t *tx) exec(ctx context.Context, sql string) (err error) { psql, err := libc.CString(sql) if err != nil { return err } defer t.c.free(psql) //TODO use t.conn.ExecContext() instead if rc := sqlite3.Xsqlite3_exec(t.c.tls, t.c.db, psql, 0, 0, 0); rc != sqlite3.SQLITE_OK { return t.c.errstr(rc) } return nil } type conn struct { db uintptr // *sqlite3.Xsqlite3 tls *libc.TLS writeTimeFormat string beginMode 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 } func getVFSName(query string) (r string, err error) { q, err := url.ParseQuery(query) if err != nil { return "", err } for _, v := range q["vfs"] { if r != "" && r != v { return "", fmt.Errorf("conflicting vfs query parameters: %v", q["vfs"]) } r = v } return r, nil } func applyQueryParams(c *conn, query string) error { q, err := url.ParseQuery(query) if err != nil { return err } for _, v := range q["_pragma"] { cmd := "pragma " + v _, err := c.exec(context.Background(), cmd, nil) if err != nil { return err } } if v := q.Get("_time_format"); v != "" { f, ok := writeTimeFormats[v] if !ok { return fmt.Errorf("unknown _time_format %q", v) } c.writeTimeFormat = f } if v := q.Get("_txlock"); v != "" { lower := strings.ToLower(v) if lower != "deferred" && lower != "immediate" && lower != "exclusive" { return fmt.Errorf("unknown _txlock %q", v) } c.beginMode = v } return nil } // 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 unlockNotify(t *libc.TLS, ppArg uintptr, nArg int32) { for i := int32(0); i < nArg; i++ { mu := *(*uintptr)(unsafe.Pointer(ppArg)) (*mutex)(unsafe.Pointer(mu)).Unlock() ppArg += ptrSize } } 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: 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 { 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) { 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) }() } 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 } // FunctionImpl describes an [application-defined SQL function]. If Scalar is // set, it is treated as a scalar function; otherwise, it is treated as an // aggregate function using MakeAggregate. // // [application-defined SQL function]: https://sqlite.org/appfunc.html type FunctionImpl struct { // NArgs is the required number of arguments that the function accepts. // If NArgs is negative, then the function is variadic. NArgs int32 // If Deterministic is true, the function must always give the same // output when the input parameters are the same. This enables functions // to be used in additional contexts like the WHERE clause of partial // indexes and enables additional optimizations. // // See https://sqlite.org/c3ref/c_deterministic.html#sqlitedeterministic // for more details. Deterministic bool // Scalar is called when a scalar function is invoked in SQL. The // argument Values are not valid past the return of the function. Scalar func(ctx *FunctionContext, args []driver.Value) (driver.Value, error) // MakeAggregate is called at the beginning of each evaluation of an // aggregate function. MakeAggregate func(ctx FunctionContext) (AggregateFunction, error) } // An AggregateFunction is an invocation of an aggregate or window function. See // the documentation for [aggregate function callbacks] and [application-defined // window functions] for an overview. // // [aggregate function callbacks]: https://www.sqlite.org/appfunc.html#the_aggregate_function_callbacks // [application-defined window functions]: https://www.sqlite.org/windowfunctions.html#user_defined_aggregate_window_functions type AggregateFunction interface { // Step is called for each row of an aggregate function's SQL // invocation. The argument Values are not valid past the return of the // function. Step(ctx *FunctionContext, rowArgs []driver.Value) error // WindowInverse is called to remove the oldest presently aggregated // result of Step from the current window. The arguments are those // passed to Step for the row being removed. The argument Values are not // valid past the return of the function. WindowInverse(ctx *FunctionContext, rowArgs []driver.Value) error // WindowValue is called to get the current value of an aggregate // function. This is used to return the final value of the function, // whether it is used as a window function or not. WindowValue(ctx *FunctionContext) (driver.Value, error) // Final is called after all of the aggregate function's input rows have // been stepped through. No other methods will be called on the // AggregateFunction after calling Final. WindowValue returns the value // from the function. Final(ctx *FunctionContext) } 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 } type collation struct { zName uintptr pApp uintptr enc int32 } // RegisterCollationUtf8 makes a Go function available as a collation named zName. // impl receives two UTF-8 strings: left and right. // The result needs to be: // // - 0 if left == right // - 1 if left < right // - +1 if left > right // // impl 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 { fns = fns[x+1:] } } return fmt.Sprintf("%s:%d:%s", fn, fl, fns) } func errorResultFunction(tls *libc.TLS, ctx uintptr) func(error) { return func(res error) { errmsg, cerr := libc.CString(res.Error()) if cerr != nil { panic(cerr) } defer libc.Xfree(tls, errmsg) sqlite3.Xsqlite3_result_error(tls, ctx, errmsg, -1) sqlite3.Xsqlite3_result_error_code(tls, ctx, sqlite3.SQLITE_ERROR) } } func functionArgs(tls *libc.TLS, argc int32, argv uintptr) []driver.Value { args := make([]driver.Value, argc) for i := int32(0); i < argc; i++ { valPtr := *(*uintptr)(unsafe.Pointer(argv + uintptr(i)*sqliteValPtrSize)) switch valType := sqlite3.Xsqlite3_value_type(tls, valPtr); valType { case sqlite3.SQLITE_TEXT: args[i] = libc.GoString(sqlite3.Xsqlite3_value_text(tls, valPtr)) case sqlite3.SQLITE_INTEGER: args[i] = sqlite3.Xsqlite3_value_int64(tls, valPtr) case sqlite3.SQLITE_FLOAT: args[i] = sqlite3.Xsqlite3_value_double(tls, valPtr) case sqlite3.SQLITE_NULL: args[i] = nil case sqlite3.SQLITE_BLOB: size := sqlite3.Xsqlite3_value_bytes(tls, valPtr) blobPtr := sqlite3.Xsqlite3_value_blob(tls, valPtr) v := make([]byte, size) copy(v, (*libc.RawMem)(unsafe.Pointer(blobPtr))[:size:size]) args[i] = v default: panic(fmt.Sprintf("unexpected argument type %q passed by sqlite", valType)) } } return args } func functionReturnValue(tls *libc.TLS, ctx uintptr, res driver.Value) error { switch resTyped := res.(type) { case nil: sqlite3.Xsqlite3_result_null(tls, ctx) case int64: sqlite3.Xsqlite3_result_int64(tls, ctx, resTyped) case float64: sqlite3.Xsqlite3_result_double(tls, ctx, resTyped) case bool: sqlite3.Xsqlite3_result_int(tls, ctx, libc.Bool32(resTyped)) case time.Time: sqlite3.Xsqlite3_result_int64(tls, ctx, resTyped.Unix()) case string: size := int32(len(resTyped)) cstr, err := libc.CString(resTyped) if err != nil { panic(err) } defer libc.Xfree(tls, cstr) sqlite3.Xsqlite3_result_text(tls, ctx, cstr, size, sqlite3.SQLITE_TRANSIENT) case []byte: size := int32(len(resTyped)) if size == 0 { sqlite3.Xsqlite3_result_zeroblob(tls, ctx, 0) return nil } p := libc.Xmalloc(tls, types.Size_t(size)) if p == 0 { panic(fmt.Sprintf("unable to allocate space for blob: %d", size)) } defer libc.Xfree(tls, p) copy((*libc.RawMem)(unsafe.Pointer(p))[:size:size], resTyped) sqlite3.Xsqlite3_result_blob(tls, ctx, p, size, sqlite3.SQLITE_TRANSIENT) default: return fmt.Errorf("function did not return a valid driver.Value: %T", resTyped) } return nil } // The below is all taken from zombiezen.com/go/sqlite. Aggregate functions need // to maintain state (for instance, the count of values seen so far). We give // each aggregate function an ID, generated by idGen, and put that in the pApp // argument to sqlite3_create_function. We track this on the Go side in // xAggregateFactories. // // When (if) the function is called is called by a query, we call the // MakeAggregate factory function to set it up, and track that in // xAggregateContext, retrieving it via sqlite3_aggregate_context. // // We also need to ensure that, for both aggregate and scalar functions, the // function pointer we pass to SQLite meets certain rules on the Go side, so // that the pointer remains valid. var ( xFuncs = struct { mu sync.RWMutex m map[uintptr]func(*FunctionContext, []driver.Value) (driver.Value, error) ids idGen }{ m: make(map[uintptr]func(*FunctionContext, []driver.Value) (driver.Value, error)), } xAggregateFactories = struct { mu sync.RWMutex m map[uintptr]func(FunctionContext) (AggregateFunction, error) ids idGen }{ m: make(map[uintptr]func(FunctionContext) (AggregateFunction, error)), } xAggregateContext = struct { mu sync.RWMutex m map[uintptr]AggregateFunction ids idGen }{ m: make(map[uintptr]AggregateFunction), } xCollations = struct { mu sync.RWMutex m map[uintptr]func(string, string) int ids idGen }{ m: make(map[uintptr]func(string, string) int), } ) type idGen struct { bitset []uint64 } func (gen *idGen) next() uintptr { base := uintptr(1) for i := 0; i < len(gen.bitset); i, base = i+1, base+64 { b := gen.bitset[i] if b != 1<<64-1 { n := uintptr(bits.TrailingZeros64(^b)) gen.bitset[i] |= 1 << n return base + n } } gen.bitset = append(gen.bitset, 1) return base } func (gen *idGen) reclaim(id uintptr) { bit := id - 1 gen.bitset[bit/64] &^= 1 << (bit % 64) } func makeAggregate(tls *libc.TLS, ctx uintptr) (AggregateFunction, uintptr) { goCtx := FunctionContext{tls: tls, ctx: ctx} aggCtx := (*uintptr)(unsafe.Pointer(sqlite3.Xsqlite3_aggregate_context(tls, ctx, int32(ptrSize)))) setErrorResult := errorResultFunction(tls, ctx) if aggCtx == nil { setErrorResult(errors.New("insufficient memory for aggregate")) return nil, 0 } if *aggCtx != 0 { // Already created. xAggregateContext.mu.RLock() f := xAggregateContext.m[*aggCtx] xAggregateContext.mu.RUnlock() return f, *aggCtx } factoryID := sqlite3.Xsqlite3_user_data(tls, ctx) xAggregateFactories.mu.RLock() factory := xAggregateFactories.m[factoryID] xAggregateFactories.mu.RUnlock() f, err := factory(goCtx) if err != nil { setErrorResult(err) return nil, 0 } if f == nil { setErrorResult(errors.New("MakeAggregate function returned nil")) return nil, 0 } xAggregateContext.mu.Lock() *aggCtx = xAggregateContext.ids.next() xAggregateContext.m[*aggCtx] = f xAggregateContext.mu.Unlock() return f, *aggCtx } // cFuncPointer converts a function defined by a function declaration to a C pointer. // The result of using cFuncPointer on closures is undefined. func cFuncPointer[T any](f T) uintptr { // This assumes the memory representation described in https://golang.org/s/go11func. // // cFuncPointer does its conversion by doing the following in order: // 1) Create a Go struct containing a pointer to a pointer to // the function. It is assumed that the pointer to the function will be // stored in the read-only data section and thus will not move. // 2) Convert the pointer to the Go struct to a pointer to uintptr through // unsafe.Pointer. This is permitted via Rule #1 of unsafe.Pointer. // 3) Dereference the pointer to uintptr to obtain the function value as a // uintptr. This is safe as long as function values are passed as pointers. return *(*uintptr)(unsafe.Pointer(&struct{ f T }{f})) } func funcTrampoline(tls *libc.TLS, ctx uintptr, argc int32, argv uintptr) { id := sqlite3.Xsqlite3_user_data(tls, ctx) xFuncs.mu.RLock() xFunc := xFuncs.m[id] xFuncs.mu.RUnlock() setErrorResult := errorResultFunction(tls, ctx) res, err := xFunc(&FunctionContext{}, functionArgs(tls, argc, argv)) if err != nil { setErrorResult(err) return } err = functionReturnValue(tls, ctx, res) if err != nil { setErrorResult(err) } } func stepTrampoline(tls *libc.TLS, ctx uintptr, argc int32, argv uintptr) { impl, _ := makeAggregate(tls, ctx) if impl == nil { return } setErrorResult := errorResultFunction(tls, ctx) err := impl.Step(&FunctionContext{}, functionArgs(tls, argc, argv)) if err != nil { setErrorResult(err) } } func inverseTrampoline(tls *libc.TLS, ctx uintptr, argc int32, argv uintptr) { impl, _ := makeAggregate(tls, ctx) if impl == nil { return } setErrorResult := errorResultFunction(tls, ctx) err := impl.WindowInverse(&FunctionContext{}, functionArgs(tls, argc, argv)) if err != nil { setErrorResult(err) } } func valueTrampoline(tls *libc.TLS, ctx uintptr) { impl, _ := makeAggregate(tls, ctx) if impl == nil { return } setErrorResult := errorResultFunction(tls, ctx) res, err := impl.WindowValue(&FunctionContext{}) if err != nil { setErrorResult(err) } else { err = functionReturnValue(tls, ctx, res) if err != nil { setErrorResult(err) } } } func finalTrampoline(tls *libc.TLS, ctx uintptr) { impl, id := makeAggregate(tls, ctx) if impl == nil { return } setErrorResult := errorResultFunction(tls, ctx) res, err := impl.WindowValue(&FunctionContext{}) if err != nil { setErrorResult(err) } else { err = functionReturnValue(tls, ctx, res) if err != nil { setErrorResult(err) } } impl.Final(&FunctionContext{}) xAggregateContext.mu.Lock() defer xAggregateContext.mu.Unlock() delete(xAggregateContext.m, id) xAggregateContext.ids.reclaim(id) } func collationTrampoline(tls *libc.TLS, pApp uintptr, nLeft int32, zLeft uintptr, nRight int32, zRight uintptr) int32 { xCollations.mu.RLock() xCollation := xCollations.m[pApp] xCollations.mu.RUnlock() left := string(libc.GoBytes(zLeft, int(nLeft))) right := string(libc.GoBytes(zRight, int(nRight))) // res is of type int, which can be 64-bit wide // Since we just need to know if the value is positive, negative, or zero, we can ensure it's -1, 0, +1 res := xCollation(left, right) switch { case res < 0: return -1 case res == 0: return 0 case res > 0: return 1 default: // Should never hit here, make the compiler happy return 0 } } // 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))) } // Limit calls sqlite3_limit, see the docs at // https://www.sqlite.org/c3ref/limit.html for details. // // To get a sql.Conn from a *sql.DB, use (*sql.DB).Conn(). Limits are bound to // the particular instance of 'c', so getting a new connection only to pass it // to Limit is possibly not useful above querying what are the various // configured default values. func Limit(c *sql.Conn, id int, newVal int) (r int, err error) { err = c.Raw(func(driverConn any) error { switch dc := driverConn.(type) { case *conn: r = dc.limit(id, newVal) return nil default: return fmt.Errorf("unexpected driverConn type: %T", driverConn) } }) return r, err }