gotosocial/vendor/modernc.org/sqlite/sqlite.go

2466 lines
66 KiB
Go
Raw Normal View History

// 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)
)
// 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
}
2021-11-27 17:26:58 +03:00
// 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}
2023-01-22 14:26:47 +03:00
sql := "begin"
if !opts.ReadOnly && c.beginMode != "" {
sql = "begin " + c.beginMode
}
2023-01-22 14:26:47 +03:00
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
2021-11-27 17:26:58 +03:00
writeTimeFormat string
beginMode string
}
func newConn(dsn string) (*conn, error) {
2023-01-22 14:26:47 +03:00
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:]
2023-01-22 14:26:47 +03:00
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,
2023-01-22 14:26:47 +03:00
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
}
2021-11-27 17:26:58 +03:00
if err = applyQueryParams(c, query); err != nil {
c.Close()
return nil, err
}
return c, nil
}
2023-01-22 14:26:47 +03:00
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
}
2021-11-27 17:26:58 +03:00
func applyQueryParams(c *conn, query string) error {
q, err := url.ParseQuery(query)
if err != nil {
return err
}
2021-11-27 17:26:58 +03:00
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
}
}
2021-11-27 17:26:58 +03:00
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 {
2021-11-27 17:26:58 +03:00
case sqliteLockedSharedcache:
if err := c.retry(pstmt); err != nil {
return sqlite3.SQLITE_LOCKED, err
}
2021-11-27 17:26:58 +03:00
case
sqlite3.SQLITE_DONE,
sqlite3.SQLITE_ROW:
return int(rc), nil
2021-11-27 17:26:58 +03:00
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:
2021-11-27 17:26:58 +03:00
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
2021-11-27 17:26:58 +03:00
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 */
// );
2023-01-22 14:26:47 +03:00
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)
}
2023-01-22 14:26:47 +03:00
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
}
2023-01-22 14:26:47 +03:00
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)
2021-11-27 17:26:58 +03:00
var s string
if rc == sqlite3.SQLITE_BUSY {
s = " (SQLITE_BUSY)"
}
switch msg := libc.GoString(p); {
case msg == str:
2021-11-27 17:26:58 +03:00
return &Error{msg: fmt.Sprintf("%s (%v)%s", str, rc, s), code: int(rc)}
default:
2021-11-27 17:26:58 +03:00
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 A<B, then B>A
// - if A<B and B<C, then A<C.
//
// The new collation will be available to all new connections opened after
// executing RegisterCollationUtf8.
func RegisterCollationUtf8(
zName string,
impl func(left, right string) int,
) error {
return registerCollation(zName, impl, sqlite3.SQLITE_UTF8)
}
// MustRegisterCollationUtf8 is like RegisterCollationUtf8 but panics on error.
func MustRegisterCollationUtf8(
zName string,
impl func(left, right string) int,
) {
if err := RegisterCollationUtf8(zName, impl); err != nil {
panic(err)
}
}
func registerCollation(
zName string,
impl func(left, right string) int,
enc int32,
) error {
if _, ok := d.collations[zName]; ok {
return fmt.Errorf("a collation %q is already registered", zName)
}
// dont free, collations registered on the driver live as long as the program
name, err := libc.CString(zName)
if err != nil {
return err
}
xCollations.mu.Lock()
id := xCollations.ids.next()
xCollations.m[id] = impl
xCollations.mu.Unlock()
d.collations[zName] = &collation{
zName: name,
pApp: id,
enc: enc,
}
return nil
}
func (c *conn) createCollationInternal(coll *collation) error {
rc := sqlite3.Xsqlite3_create_collation_v2(
c.tls,
c.db,
coll.zName,
coll.enc,
coll.pApp,
cFuncPointer(collationTrampoline),
0,
)
if rc != sqlite3.SQLITE_OK {
return c.errstr(rc)
}
return nil
}
// Execer is an optional interface that may be implemented by a Conn.
//
// If a Conn does not implement Execer, the sql package's DB.Exec will first
// prepare a query, execute the statement, and then close the statement.
//
// Exec may return ErrSkip.
//
// Deprecated: Drivers should implement ExecerContext instead.
func (c *conn) Exec(query string, args []driver.Value) (dr driver.Result, err error) {
if dmesgs {
defer func() {
dmesg("conn %p, query %q, args %v: (driver.Result %p, err %v)", c, query, args, dr, err)
}()
}
return c.exec(context.Background(), query, toNamedValues(args))
}
func (c *conn) exec(ctx context.Context, query string, args []driver.NamedValue) (r driver.Result, err error) {
s, err := c.prepare(ctx, query)
if err != nil {
return nil, err
}
defer func() {
if err2 := s.Close(); err2 != nil && err == nil {
err = err2
}
}()
return s.(*stmt).exec(ctx, args)
}
// Prepare returns a prepared statement, bound to this connection.
func (c *conn) Prepare(query string) (ds driver.Stmt, err error) {
if dmesgs {
defer func() {
dmesg("conn %p, query %q: (driver.Stmt %p, err %v)", c, query, ds, err)
}()
}
return c.prepare(context.Background(), query)
}
func (c *conn) prepare(ctx context.Context, query string) (s driver.Stmt, err error) {
//TODO use ctx
return newStmt(c, query)
}
// Queryer is an optional interface that may be implemented by a Conn.
//
// If a Conn does not implement Queryer, the sql package's DB.Query will first
// prepare a query, execute the statement, and then close the statement.
//
// Query may return ErrSkip.
//
// Deprecated: Drivers should implement QueryerContext instead.
func (c *conn) Query(query string, args []driver.Value) (dr driver.Rows, err error) {
if dmesgs {
defer func() {
dmesg("conn %p, query %q, args %v: (driver.Rows %p, err %v)", c, query, args, dr, err)
}()
}
return c.query(context.Background(), query, toNamedValues(args))
}
func (c *conn) query(ctx context.Context, query string, args []driver.NamedValue) (r driver.Rows, err error) {
s, err := c.prepare(ctx, query)
if err != nil {
return nil, err
}
defer func() {
if err2 := s.Close(); err2 != nil && err == nil {
err = err2
}
}()
return s.(*stmt).query(ctx, args)
}
// Serialize returns a serialization of the main database. For an ordinary on-disk
// database file, the serialization is just a copy of the disk file. For an in-memory
// database or a "TEMP" database, the serialization is the same sequence of bytes
// which would be written to disk if that database where backed up to disk.
func (c *conn) Serialize() (v []byte, err error) {
pLen := c.tls.Alloc(8)
defer c.tls.Free(8)
zSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0)
if zSchema == 0 {
return nil, fmt.Errorf("failed to get main db name")
}
pBuf := sqlite3.Xsqlite3_serialize(c.tls, c.db, zSchema, pLen, 0)
bufLen := *(*sqlite3.Sqlite3_int64)(unsafe.Pointer(pLen))
if pBuf != 0 {
defer sqlite3.Xsqlite3_free(c.tls, pBuf)
}
if bufLen <= 0 {
return nil, fmt.Errorf("invalid length returned: %d", bufLen)
} else if pBuf == 0 || bufLen == 0 {
return nil, nil
}
v = make([]byte, bufLen)
copy(v, (*libc.RawMem)(unsafe.Pointer(pBuf))[:bufLen:bufLen])
return v, nil
}
// Deserialize restore a database from the content returned by Serialize.
func (c *conn) Deserialize(buf []byte) (err error) {
bufLen := len(buf)
pBuf := c.tls.Alloc(bufLen) // free will be done if it fails or on close, must not be freed here
copy((*libc.RawMem)(unsafe.Pointer(pBuf))[:bufLen:bufLen], buf)
zSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0)
if zSchema == 0 {
return fmt.Errorf("failed to get main db name")
}
rc := sqlite3.Xsqlite3_deserialize(c.tls, c.db, zSchema, pBuf, int64(bufLen), int64(bufLen), sqlite3.SQLITE_DESERIALIZE_RESIZEABLE|sqlite3.SQLITE_DESERIALIZE_FREEONCLOSE)
if rc != sqlite3.SQLITE_OK {
return c.errstr(rc)
}
return nil
}
// Backup object is used to manage progress and cleanup an online backup. It
// is returned by NewBackup or NewRestore.
type Backup struct {
srcConn *conn // source database connection
dstConn *conn // destination database connection
pBackup uintptr // sqlite3_backup object pointer
}
// NewBackup returns a Backup object that will create an online backup of
// current database to the databased pointed by the passed URI.
func (c *conn) NewBackup(dstUri string) (*Backup, error) {
dstConn, err := newConn(dstUri)
if err != nil {
return nil, err
}
backup, err := c.backup(dstConn, false)
if err != nil {
dstConn.Close()
}
return backup, err
}
// NewRestore returns a Backup object that will restore a backup to current
// database from the databased pointed by the passed URI.
func (c *conn) NewRestore(srcUri string) (*Backup, error) {
srcConn, err := newConn(srcUri)
if err != nil {
return nil, err
}
backup, err := c.backup(srcConn, true)
if err != nil {
srcConn.Close()
}
return backup, err
}
func (c *conn) backup(remoteConn *conn, restore bool) (_ *Backup, finalErr error) {
srcSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0)
if srcSchema == 0 {
return nil, fmt.Errorf("failed to get main source db name")
}
dstSchema := sqlite3.Xsqlite3_db_name(remoteConn.tls, remoteConn.db, 0)
if dstSchema == 0 {
return nil, fmt.Errorf("failed to get main destination db name")
}
var pBackup uintptr
if restore {
pBackup = sqlite3.Xsqlite3_backup_init(c.tls, c.db, srcSchema, remoteConn.db, dstSchema)
} else {
pBackup = sqlite3.Xsqlite3_backup_init(c.tls, remoteConn.db, dstSchema, c.db, srcSchema)
}
if pBackup <= 0 {
rc := sqlite3.Xsqlite3_errcode(c.tls, remoteConn.db)
return nil, c.errstr(rc)
}
return &Backup{srcConn: c, dstConn: remoteConn, pBackup: pBackup}, nil
}
// Step will copy up to n pages between the source and destination databases
// specified by the backup object. If n is negative, all remaining source
// pages are copied.
// If it successfully copies n pages and there are still more pages to be
// copied, then the function returns true with no error. If it successfully
// finishes copying all pages from source to destination, then it returns
// false with no error. If an error occurs while running, then an error is
// returned.
func (b *Backup) Step(n int32) (bool, error) {
rc := sqlite3.Xsqlite3_backup_step(b.srcConn.tls, b.pBackup, n)
if rc == sqlite3.SQLITE_OK {
return true, nil
} else if rc == sqlite3.SQLITE_DONE {
return false, nil
} else {
return false, b.srcConn.errstr(rc)
}
}
// Finish releases all resources associated with the Backup object. The Backup
// object is invalid and may not be used following a call to Finish.
func (b *Backup) Finish() error {
rc := sqlite3.Xsqlite3_backup_finish(b.srcConn.tls, b.pBackup)
b.dstConn.Close()
if rc == sqlite3.SQLITE_OK {
return nil
} else {
return b.srcConn.errstr(rc)
}
}
type ExecQuerierContext interface {
driver.ExecerContext
driver.QueryerContext
}
// Commit releases all resources associated with the Backup object but does not
// close the destination database connection.
//
// The destination database connection is returned to the caller or an error if raised.
// It is the responsibility of the caller to handle the connection closure.
func (b *Backup) Commit() (driver.Conn, error) {
rc := sqlite3.Xsqlite3_backup_finish(b.srcConn.tls, b.pBackup)
if rc == sqlite3.SQLITE_OK {
return b.dstConn, nil
} else {
return nil, b.srcConn.errstr(rc)
}
}
// ConnectionHookFn function type for a connection hook on the Driver. Connection
// hooks are called after the connection has been set up.
type ConnectionHookFn func(
conn ExecQuerierContext,
dsn string,
) error
// Driver implements database/sql/driver.Driver.
type Driver struct {
// user defined functions that are added to every new connection on Open
udfs map[string]*userDefinedFunction
// collations that are added to every new connection on Open
collations map[string]*collation
// connection hooks are called after a connection is opened
connectionHooks []ConnectionHookFn
}
var d = &Driver{
udfs: make(map[string]*userDefinedFunction, 0),
collations: make(map[string]*collation, 0),
connectionHooks: make([]ConnectionHookFn, 0),
}
func newDriver() *Driver { return d }
// Open returns a new connection to the database. The name is a string in a
// driver-specific format.
//
// Open may return a cached connection (one previously closed), but doing so is
// unnecessary; the sql package maintains a pool of idle connections for
// efficient re-use.
//
// The returned connection is only used by one goroutine at a time.
//
// The name may be a filename, e.g., "/tmp/mydata.sqlite", or a URI, in which
// case it may include a '?' followed by one or more query parameters.
// For example, "file:///tmp/mydata.sqlite?_pragma=foreign_keys(1)&_time_format=sqlite".
// The supported query parameters are:
//
// _pragma: Each value will be run as a "PRAGMA ..." statement (with the PRAGMA
// keyword added for you). May be specified more than once, '&'-separated. For more
// information on supported PRAGMAs see: https://www.sqlite.org/pragma.html
//
// _time_format: The name of a format to use when writing time values to the
// database. Currently the only supported value is "sqlite", which corresponds
// to format 7 from https://www.sqlite.org/lang_datefunc.html#time_values,
// including the timezone specifier. If this parameter is not specified, then
// the default String() format will be used.
//
// _txlock: The locking behavior to use when beginning a transaction. May be
// "deferred" (the default), "immediate", or "exclusive" (case insensitive). See:
// https://www.sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
func (d *Driver) Open(name string) (conn driver.Conn, err error) {
if dmesgs {
defer func() {
dmesg("name %q: (driver.Conn %p, err %v)", name, conn, err)
}()
}
c, err := newConn(name)
if err != nil {
return nil, err
}
for _, udf := range d.udfs {
if err = c.createFunctionInternal(udf); err != nil {
c.Close()
return nil, err
}
}
for _, coll := range d.collations {
if err = c.createCollationInternal(coll); err != nil {
c.Close()
return nil, err
}
}
for _, connHookFn := range d.connectionHooks {
if err = connHookFn(c, name); err != nil {
c.Close()
return nil, fmt.Errorf("connection hook: %w", err)
}
}
return c, nil
}
// FunctionContext represents the context user defined functions execute in.
// Fields and/or methods of this type may get addedd in the future.
type FunctionContext struct {
tls *libc.TLS
ctx uintptr
}
const sqliteValPtrSize = unsafe.Sizeof(&sqlite3.Sqlite3_value{})
// RegisterFunction registers a function named zFuncName with nArg arguments.
// Passing -1 for nArg indicates the function is variadic. The FunctionImpl
// determines whether the function is deterministic or not, and whether it is a
// scalar function (when Scalar is defined) or an aggregate function (when
// Scalar is not defined and MakeAggregate is defined).
//
// The new function will be available to all new connections opened after
// executing RegisterFunction.
func RegisterFunction(
zFuncName string,
impl *FunctionImpl,
) error {
return registerFunction(zFuncName, impl)
}
// MustRegisterFunction is like RegisterFunction but panics on error.
func MustRegisterFunction(
zFuncName string,
impl *FunctionImpl,
) {
if err := RegisterFunction(zFuncName, impl); err != nil {
panic(err)
}
}
// RegisterScalarFunction registers a scalar function named zFuncName with nArg
// arguments. Passing -1 for nArg indicates the function is variadic.
//
// The new function will be available to all new connections opened after
// executing RegisterScalarFunction.
func RegisterScalarFunction(
zFuncName string,
nArg int32,
xFunc func(ctx *FunctionContext, args []driver.Value) (driver.Value, error),
) (err error) {
if dmesgs {
defer func() {
dmesg("zFuncName %q, nArg %v, xFunc %p: err %v", zFuncName, nArg, xFunc, err)
}()
}
return registerFunction(zFuncName, &FunctionImpl{NArgs: nArg, Scalar: xFunc, Deterministic: false})
}
// MustRegisterScalarFunction is like RegisterScalarFunction but panics on
// error.
func MustRegisterScalarFunction(
zFuncName string,
nArg int32,
xFunc func(ctx *FunctionContext, args []driver.Value) (driver.Value, error),
) {
if dmesgs {
dmesg("zFuncName %q, nArg %v, xFunc %p", zFuncName, nArg, xFunc)
}
if err := RegisterScalarFunction(zFuncName, nArg, xFunc); err != nil {
panic(err)
}
}
// MustRegisterDeterministicScalarFunction is like
// RegisterDeterministicScalarFunction but panics on error.
func MustRegisterDeterministicScalarFunction(
zFuncName string,
nArg int32,
xFunc func(ctx *FunctionContext, args []driver.Value) (driver.Value, error),
) {
if dmesgs {
dmesg("zFuncName %q, nArg %v, xFunc %p", zFuncName, nArg, xFunc)
}
if err := RegisterDeterministicScalarFunction(zFuncName, nArg, xFunc); err != nil {
panic(err)
}
}
// RegisterDeterministicScalarFunction registers a deterministic scalar
// function named zFuncName with nArg arguments. Passing -1 for nArg indicates
// the function is variadic. A deterministic function means that the function
// always gives the same output when the input parameters are the same.
//
// The new function will be available to all new connections opened after
// executing RegisterDeterministicScalarFunction.
func RegisterDeterministicScalarFunction(
zFuncName string,
nArg int32,
xFunc func(ctx *FunctionContext, args []driver.Value) (driver.Value, error),
) (err error) {
if dmesgs {
defer func() {
dmesg("zFuncName %q, nArg %v, xFunc %p: err %v", zFuncName, nArg, xFunc, err)
}()
}
return registerFunction(zFuncName, &FunctionImpl{NArgs: nArg, Scalar: xFunc, Deterministic: true})
}
func registerFunction(
zFuncName string,
impl *FunctionImpl,
) error {
if _, ok := d.udfs[zFuncName]; ok {
return fmt.Errorf("a function named %q is already registered", zFuncName)
}
// dont free, functions registered on the driver live as long as the program
name, err := libc.CString(zFuncName)
if err != nil {
return err
}
var textrep int32 = sqlite3.SQLITE_UTF8
if impl.Deterministic {
textrep |= sqlite3.SQLITE_DETERMINISTIC
}
udf := &userDefinedFunction{
zFuncName: name,
nArg: impl.NArgs,
eTextRep: textrep,
}
if impl.Scalar != nil {
xFuncs.mu.Lock()
id := xFuncs.ids.next()
xFuncs.m[id] = impl.Scalar
xFuncs.mu.Unlock()
udf.scalar = true
udf.pApp = id
} else {
xAggregateFactories.mu.Lock()
id := xAggregateFactories.ids.next()
xAggregateFactories.m[id] = impl.MakeAggregate
xAggregateFactories.mu.Unlock()
udf.pApp = id
}
d.udfs[zFuncName] = udf
return nil
}
// RegisterConnectionHook registers a function to be called after each connection
// is opened. This is called after all the connection has been set up.
func (d *Driver) RegisterConnectionHook(fn ConnectionHookFn) {
d.connectionHooks = append(d.connectionHooks, fn)
}
// RegisterConnectionHook registers a function to be called after each connection
// is opened. This is called after all the connection has been set up.
func RegisterConnectionHook(fn ConnectionHookFn) {
d.RegisterConnectionHook(fn)
}
func origin(skip int) string {
pc, fn, fl, _ := runtime.Caller(skip)
f := runtime.FuncForPC(pc)
var fns string
if f != nil {
fns = f.Name()
if x := strings.LastIndex(fns, "."); x > 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
}