From 3ac1ee16f377d31a0fb80c8dae28b6239ac4229e Mon Sep 17 00:00:00 2001 From: Terin Stock Date: Sun, 9 Mar 2025 17:47:56 +0100 Subject: [chore] remove vendor --- vendor/modernc.org/sqlite/sqlite.go | 2470 ----------------------------------- 1 file changed, 2470 deletions(-) delete mode 100644 vendor/modernc.org/sqlite/sqlite.go (limited to 'vendor/modernc.org/sqlite/sqlite.go') diff --git a/vendor/modernc.org/sqlite/sqlite.go b/vendor/modernc.org/sqlite/sqlite.go deleted file mode 100644 index 1e7e437ab..000000000 --- a/vendor/modernc.org/sqlite/sqlite.go +++ /dev/null @@ -1,2470 +0,0 @@ -// 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" - "sort" - "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) -) - -// https://gitlab.com/cznic/sqlite/-/issues/199 -func init() { - sqlite3.PatchIssue199() -} - -// 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 - } - - var a []string - for _, v := range q["_pragma"] { - a = append(a, v) - } - // Push 'busy_timeout' first, the rest in lexicographic order, case insenstive. - // See https://gitlab.com/cznic/sqlite/-/issues/198#note_2233423463 for - // discussion. - sort.Slice(a, func(i, j int) bool { - x, y := strings.TrimSpace(strings.ToLower(a[i])), strings.TrimSpace(strings.ToLower(a[j])) - if strings.HasPrefix(x, "busy_timeout") { - return true - } - if strings.HasPrefix(y, "busy_timeout") { - return false - } - - return x < y - }) - for _, v := range a { - 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 -} - -// 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 -} - -// 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 - -} -- cgit v1.2.3