chore: ⬆️ updated deps

This commit is contained in:
2026-05-20 22:52:20 +02:00
parent d9f27c1775
commit 43f4680176
374 changed files with 295527 additions and 301467 deletions
+2 -1
View File
@@ -25,8 +25,9 @@ Michael Hoffmann <mhoffm@posteo.de>
Michael Rykov <mrykov@gmail.com>
Morgan Bazalgette <morgan@howl.moe>
Ross Light <ross@zombiezen.com>
SUSE LLC <moio@suse.com>
Saed SayedAhmed <saadmtsa@gmail.com>
Steffen Butzer <steffen(dot)butzer@outlook.com>
Toni Spets <toni.spets@beeper.com>
W. Michael Petullo <mike@flyn.org>
SUSE LLC <moio@suse.com>
Zhenghao Zhang zhangzhenghao@hotmail.com
+96 -1
View File
@@ -1,6 +1,101 @@
# Changelog
- 2026-01-19 v1.44.3: Resolves [issue 243](https://gitlab.com/cznic/sqlite/-/issues/243).
- 2026-05-10 v1.50.1:
- Upgrade to [SQLite 3.53.1](https://sqlite.org/releaselog/3_53_1.html).
- 2026-04-24 v1.50.0:
- Upgrade to sqlite-vec [v0.1.9](https://github.com/asg017/sqlite-vec/releases/tag/v0.1.9).
- Introduce `ColumnInfo`, enabling dynamic query builders and ORMs to retrieve underlying SQLite C-API metadata (`OriginName`, `TableName`, `DatabaseName`, and `DeclType`).
- This feature is exposed via the idiomatic `database/sql` escape hatch `(*sql.Conn).Raw()`, avoiding custom statement handles and keeping the standard library workflow intact.
- See [GitLab merge request #113](https://gitlab.com/cznic/sqlite/-/merge_requests/113), thanks Josh Bleecher Snyder!
- 2026-04-17 v1.49.0: Upgrade to [SQLite 3.53.0](https://sqlite.org/releaselog/3_53_0.html).
- Added `-DSQLITE_ENABLE_DBPAGE_VTAB` to the transpilation. See ["The SQLITE_DBPAGE Virtual Table"](https://www.sqlite.org/dbpage.html) for details.
- 2026-04-06 v1.48.2:
- Fix ABI mapping mismatch in the pre-update hook trampoline that caused silent truncation of large 64-bit RowIDs.
- Ensure the Go trampoline signature correctly aligns with the public `sqlite3_preupdate_hook` C API, preventing data corruption for high-entropy keys (e.g., Snowflake IDs).
- See [GitLab merge request #98](https://gitlab.com/cznic/sqlite/-/merge_requests/98), thanks Josh Bleecher Snyder!
- Fix the memory allocator used in `(*conn).Deserialize`.
- Replace `tls.Alloc` with `sqlite3_malloc64` to prevent internal allocator corruption. This ensures the buffer is safely owned by SQLite, which may resize or free it due to the `SQLITE_DESERIALIZE_RESIZEABLE` and `SQLITE_DESERIALIZE_FREEONCLOSE` flags.
- Prevent a memory leak by properly freeing the allocated buffer if fetching the main database name fails before handing ownership to SQLite.
- See [GitLab merge request #100](https://gitlab.com/cznic/sqlite/-/merge_requests/100), thanks Josh Bleecher Snyder!
- Fix `(*conn).Deserialize` to explicitly reject `nil` or empty byte slices.
- Prevent silent database disconnection and connection pool corruption caused by SQLite's default behavior when `sqlite3_deserialize` receives a 0-length buffer.
- See [GitLab merge request #101](https://gitlab.com/cznic/sqlite/-/merge_requests/101), thanks Josh Bleecher Snyder!
- Fix `commitHookTrampoline` and `rollbackHookTrampoline` signatures by removing the unused `pCsr` parameter.
- Aligns internal hook callbacks accurately with the underlying SQLite C API, cleaning up the code to prevent potential future confusion or bugs.
- See [GitLab merge request #102](https://gitlab.com/cznic/sqlite/-/merge_requests/102), thanks Josh Bleecher Snyder!
- Fix `checkptr` instrumentation failures during `go test -race` when registering and using virtual tables (`vtab`).
- Allocate `sqlite3_module` instances using the C allocator (`libc.Xcalloc`) instead of the Go heap. This ensures transpiled C code can safely perform pointer operations on the struct without tripping Go's pointer checks.
- See [GitLab merge request #103](https://gitlab.com/cznic/sqlite/-/merge_requests/103), thanks Josh Bleecher Snyder!
- Fix data race on `mutex.id` in the `mutexTry` non-recursive path.
- Ensure consistent atomic writes (`atomic.StoreInt32`) to prevent data races with atomic loads in `mutexHeld` and `mutexNotheld` during concurrent execution.
- See [GitLab merge request #104](https://gitlab.com/cznic/sqlite/-/merge_requests/104), thanks Josh Bleecher Snyder!
- Fix resource leak in `(*Backup).Commit` where the destination connection was not closed on error.
- Ensure `dstConn` is properly closed when `sqlite3_backup_finish` fails, preventing file descriptor, TLS, and memory leaks.
- See [GitLab merge request #105](https://gitlab.com/cznic/sqlite/-/merge_requests/105), thanks Josh Bleecher Snyder!
- Fix `Exec` to fully drain rows when encountering `SQLITE_ROW`, preventing silent data loss in DML statements.
- Previously, `Exec` aborted after the first row, meaning `INSERT`, `UPDATE`, or `DELETE` statements with a `RETURNING` clause would fail to process subsequent rows. The execution path now correctly loops until `SQLITE_DONE` and properly respects context cancellations during the drain loop, fully aligning with native C `sqlite3_exec` semantics.
- See [GitLab merge request #106](https://gitlab.com/cznic/sqlite/-/merge_requests/106), thanks Josh Bleecher Snyder!
- Fix "Shadowed err value (stmt.go)".
- See [GitLab issue #249](https://gitlab.com/cznic/sqlite/-/work_items/249), thanks Emrecan BATI!
- Fix silent omission of virtual table savepoint callbacks by correctly setting the sqlite3_module version.
- See [GitLab merge request #107](https://gitlab.com/cznic/sqlite/-/merge_requests/107), thanks Josh Bleecher Snyder!
- Fix `vfsRead` to properly handle partial and fragmented reads from `io.Reader`.
- Replace `f.Read` with `io.ReadFull` to ensure the buffer is fully populated, preventing premature `SQLITE_IOERR_SHORT_READ` errors on valid mid-stream partial reads. Unread tail bytes at EOF are now efficiently zero-filled using the built-in `clear` function.
- See [GitLab merge request #108](https://gitlab.com/cznic/sqlite/-/merge_requests/108), thanks Josh Bleecher Snyder!
- Refactor internal error formatting to safely handle uninitialized or closed database pointers.
- Prevent a misleading "out of memory" error message when an operation fails and the underlying SQLite database handle is `NULL` (`db == 0`).
- See [GitLab merge request #109](https://gitlab.com/cznic/sqlite/-/merge_requests/109), thanks Josh Bleecher Snyder!
- Fix error handling in database backup and restore initialization (`sqlite3_backup_init`).
- Ensure error codes and messages are accurately read from the destination database handle rather than hardcoding the source or remote handle. This prevents swallowed errors or mismatched "not an error" messages when a backup or restore operation fails to start.
- See [GitLab merge request #111](https://gitlab.com/cznic/sqlite/-/merge_requests/111), thanks Josh Bleecher Snyder!
- Fix database handle and C-heap memory leaks when `sqlite3_open_v2` fails.
- Ensure `sqlite3_close_v2` is called on the partially allocated database handle during a failed open, and explicitly close `libc.TLS` in `newConn` to prevent resource leakage.
- Prevent misleading "out of memory" error messages on failed connections by correctly extracting the exact error string from the allocated handle before it is closed.
- See [GitLab merge request #112](https://gitlab.com/cznic/sqlite/-/merge_requests/112), thanks Josh Bleecher Snyder!
- 2026-04-03 v1.48.1:
- Fix memory leaks and double-free vulnerabilities in the multi-statement query execution path.
- Ensure bind-parameter allocations are reliably freed via strict ownership transfer if an error occurs mid-loop or if multiple statements bind parameters.
- Fix a resource leak where a subsequent statement's error could orphan a previously generated `rows` object without closing it, leaking the prepared statement handle.
- See [GitLab merge request #96](https://gitlab.com/cznic/sqlite/-/merge_requests/96), thanks Josh Bleecher Snyder!
- 2026-03-27 v1.48.0:
- Add `_timezone` DSN query parameter to apply IANA timezones (e.g., "America/New_York") to both reads and writes.
- Writes will convert `time.Time` values to the target timezone before formatting as a string.
- Reads will interpret timezone-less strings as being in the target timezone.
- Does not impact `_inttotime` integer values, which will always safely evaluate as UTC.
- Add support for `_time_format=datetime` URI parameter to format `time.Time` values identically to SQLite's native `datetime()` function and `CURRENT_TIMESTAMP` (`YYYY-MM-DD HH:MM:SS`).
- See [GitLab merge request #94](https://gitlab.com/cznic/sqlite/-/merge_requests/94) and [GitLab merge request #95](https://gitlab.com/cznic/sqlite/-/merge_requests/95), thanks Josh Bleecher Snyder!
- 2026-03-17 v1.47.0: Add CGO-free version of the vector extensions from https://github.com/asg017/sqlite-vec. See `vec_test.go` for example usage. From the GitHub project page:
- **Important:** sqlite-vec is a pre-v1, so expect breaking changes!
- Store and query float, int8, and binary vectors in vec0 virtual tables
- Written in pure C, no dependencies, runs anywhere SQLite runs (Linux/MacOS/Windows, in the browser with WASM, Raspberry Pis, etc.)
- Store non-vector data in metadata, auxiliary, or partition key columns
- See [GitLab merge request #93](https://gitlab.com/cznic/sqlite/-/merge_requests/93), thanks Zhenghao Zhang!
- 2026-03-16 v1.46.2: Upgrade to [SQLite 3.51.3](https://sqlite.org/releaselog/3_51_3.html).
- 2026-02-17 v1.46.1:
- Ensure connection state is reset if Tx.Commit fails. Previously, errors like SQLITE_BUSY during COMMIT could leave the underlying connection inside a transaction, causing errors when the connection was reused by the database/sql pool. The driver now detects this state and forces a rollback internally.
- Fixes [GitHub issue #2](https://github.com/modernc-org/sqlite/issues/2), thanks Edoardo Spadolini!
- 2026-02-17 v1.46.0:
- Enable ColumnTypeScanType to report time.Time instead of string for TEXT columns declared as DATE, DATETIME, TIME, or TIMESTAMP via a new `_texttotime` URI parameter.
- See [GitHub pull request #1](https://github.com/modernc-org/sqlite/pull/1), thanks devhaozi!
- 2026-02-09 v1.45.0:
- Introduce vtab subpackage (modernc.org/sqlite/vtab) exposing Module, Table, Cursor, and IndexInfo API for Go virtual tables.
- Wire vtab registration into the driver: vtab.RegisterModule installs modules globally and each new connection calls sqlite3_create_module_v2.
- Implement vtab trampolines for xCreate/xConnect/xBestIndex/xDisconnect/xDestroy/xOpen/xClose/xFilter/xNext/xEof/xColumn/xRowid.
- Map SQLites sqlite3_index_info into vtab.IndexInfo, including constraints, ORDER BY terms, and constraint usage (ArgIndex → xFilter argv[]).
- Add an inrepo dummy vtab module and test (module_test.go) that validates registration, basic scanning, and constraint visibility.
- See [GitLab merge request #90](https://gitlab.com/cznic/sqlite/-/merge_requests/90), thanks Adrian Witas!
- 2026-01-19 v1.44.3: Resolves [GitLab issue #243](https://gitlab.com/cznic/sqlite/-/issues/243).
- 2026-01-18 v1.44.2: Upgrade to [SQLite 3.51.2](https://sqlite.org/releaselog/3_51_2.html).
+5 -3
View File
@@ -6,11 +6,11 @@
#
# Please keep the list sorted.
Adrian Witas <adrianwit@gmail.com>
Alexander Menzhinsky <amenzhinsky@gmail.com>
Alexey Palazhchenko <alexey.palazhchenko@gmail.com>
Angus Dippenaar <angusdippenaar@gmail.com>
Artyom Pervukhin <github@artyom.dev>
Adrian Witas <adrianwit@gmail.com>
Dan Kortschak <dan@kortschak.io>
Dan Peterson <danp@danp.net>
David Skinner <skinner.david@gmail.com>
@@ -19,6 +19,7 @@ Elle Mouton <elle.mouton@gmail.com>
FlyingOnion <731677080@qq.com>
Gleb Sakhnov <gleb.sakhnov@gmail.com>
Guénaël Muller <inkey@inkey-art.net>
HaoZi <haozi@loli.email>
Harald Albrecht <thediveo@gmx.eu>
Jaap Aarts <jaap.aarts1@gmail.com>
Jan Mercl <0xjnml@gmail.com>
@@ -32,8 +33,10 @@ Matthew Gabeler-Lee <fastcat@gmail.com>
Michael Hoffmann <mhoffm@posteo.de>
Michael Rykov <mrykov@gmail.com>
Morgan Bazalgette <morgan@howl.moe>
Prathyush PV <prathyush.pv@temporal.io>
Romain Le Disez <r.gitlab@ledisez.net>
Ross Light <ross@zombiezen.com>
SUSE LLC <legal@suse.de>
Saed SayedAhmed <saadmtsa@gmail.com>
Sean McGivern <sean@mcgivern.me.uk>
Steffen Butzer <steffen(dot)butzer@outlook.com>
@@ -41,5 +44,4 @@ Toni Spets <toni.spets@beeper.com>
W. Michael Petullo <mike@flyn.org>
Walter Wanderley <walterwanderley@gmail.com>
Yaacov Akiba Slama <ya@slamail.org>
Prathyush PV <prathyush.pv@temporal.io>
SUSE LLC <legal@suse.de>
Zhenghao Zhang zhangzhenghao@hotmail.com
+9
View File
@@ -0,0 +1,9 @@
# Governance & Contribution
* **Primary Maintainer:** Jan Mercl, GitLab @cznic, GitHub @j-modernc-org
* **Canonical Source:** GitLab ([cznic/sqlite](https://gitlab.com/cznic/sqlite))
* **Community Hub:** GitHub ([modernc-org/sqlite](https://github.com/modernc-org/sqlite))
* **Security Policy:** We utilize GitHub Security Advisories for vulnerability reporting. Security patches are developed on GitLab and prioritized for immediate mirroring to GitHub to ensure downstream users are protected via Dependabot.
+2 -2
View File
@@ -61,13 +61,13 @@ edit:
editor:
go test -c -o /dev/null
go build -v -o /dev/null ./...
cd vendor_libsqlite3 && go build -o /dev/null main.go
cd vendor_libs && go build -o /dev/null main.go
test:
go test -v -timeout 24h
vendor:
cd vendor_libsqlite3 && go build -o ../vendor main.go
cd vendor_libs && go build -o ../vendor main.go
./vendor
rm -f vendor
make build_all_targets
+3 -3
View File
@@ -1,6 +1,4 @@
## Important: Repository Mirroring
**This project is primarily developed on GitLab.** The repository you are currently viewing might be a mirror. Please review the guidelines below based on where you are viewing this:
The repository you are currently viewing might be a mirror. Please review the guidelines below based on where you are viewing this:
| Platform | Role | Contributing Guidelines |
| :--- | :--- | :--- |
@@ -23,6 +21,8 @@
### Startup / Small Business Tier Sponsor
![exe.dev](sponsors/boldsoftware.png "boldsoftware") [exe.dev](https://exe.dev)
![octoberswimmer](sponsors/octoberswimmer.png "osctoberswimmer") [October Swimmer](https://www.octoberswimmer.com/)
---
+1
View File
@@ -60,6 +60,7 @@ func (b *Backup) Commit() (driver.Conn, error) {
if rc == sqlite3.SQLITE_OK {
return b.dstConn, nil
} else {
b.dstConn.Close()
return nil, b.srcConn.errstr(rc)
}
}
+135 -17
View File
@@ -29,7 +29,9 @@ type conn struct {
writeTimeFormat string
beginMode string
loc *time.Location
intToTime bool
textToTime bool
integerTimeFormat string
}
@@ -62,6 +64,7 @@ func newConn(dsn string) (*conn, error) {
sqlite3.SQLITE_OPEN_URI,
)
if err != nil {
c.tls.Close()
return nil, err
}
@@ -86,12 +89,18 @@ func (c *conn) parseTime(s string) (interface{}, bool) {
return v, true
}
ts := strings.TrimSuffix(s, "Z")
ts, hadZ := strings.CutSuffix(s, "Z")
for _, f := range parseTimeFormats {
t, err := time.Parse(f, ts)
var t time.Time
var err error
if c.loc != nil && !hadZ {
t, err = time.ParseInLocation(f, ts, c.loc)
} else {
t, err = time.Parse(f, ts)
}
if err == nil {
return t, true
return c.applyTimezone(t), true
}
}
@@ -101,6 +110,8 @@ func (c *conn) parseTime(s string) (interface{}, bool) {
// 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.
// This intentionally uses time.Parse, not time.ParseInLocation,
// because the format already contains timezone information (-0700 MST).
func (c *conn) parseTimeString(s0 string, x int) (interface{}, bool) {
s := s0
if x > 0 {
@@ -108,19 +119,28 @@ func (c *conn) parseTimeString(s0 string, x int) (interface{}, bool) {
}
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 c.applyTimezone(t), true
}
return s0, false
}
func (c *conn) applyTimezone(t time.Time) time.Time {
if c.loc == nil {
return t
}
return t.In(c.loc)
}
// writeTimeFormats are the names and formats supported
// by the `_time_format` DSN query param.
var writeTimeFormats = map[string]string{
"sqlite": parseTimeFormats[0],
"sqlite": parseTimeFormats[0],
"datetime": "2006-01-02 15:04:05",
}
func (c *conn) formatTime(t time.Time) string {
t = c.applyTimezone(t)
// Before configurable write time formats were supported,
// time.Time.String was used. Maintain that default to
// keep existing driver users formatting times the same.
@@ -215,6 +235,71 @@ func (c *conn) columnName(pstmt uintptr, n int) (string, error) {
return libc.GoString(p), nil
}
// ColumnInfo returns column information for query.
// It does not execute query.
//
// For output columns that are expressions, function calls, or constants —
// or otherwise do not resolve to a single column — the DatabaseName,
// TableName, and OriginName fields of the corresponding ColumnInfo are
// empty, per the sqlite3 contract.
//
// Sample usage:
//
// err := conn.Raw(func(driverConn any) error {
// ci, ok := driverConn.(interface{ ColumnInfo(query string) ([]sqlite.ColumnInfo, error) })
// if !ok {
// return fmt.Errorf("driver does not support ColumnInfo")
// }
// info, err := ci.ColumnInfo(query)
// if err != nil {
// return err
// }
// // use info
// return nil
// })
func (c *conn) ColumnInfo(query string) (_ []ColumnInfo, err error) {
p, err := libc.CString(query)
if err != nil {
return nil, err
}
defer c.free(p)
psql := p
pstmt, err := c.prepareV2(&psql)
if err != nil {
return nil, err
}
if pstmt == 0 {
// Empty or comment-only query: no columns to describe.
return nil, nil
}
defer func() {
if e := c.finalize(pstmt); err == nil {
err = e
}
}()
n, err := c.columnCount(pstmt)
if err != nil {
return nil, err
}
info := make([]ColumnInfo, n)
for i := range n {
name, err := c.columnName(pstmt, i)
if err != nil {
return nil, err
}
info[i] = ColumnInfo{
Name: name,
DeclType: c.columnDeclType(pstmt, i),
DatabaseName: libc.GoString(sqlite3.Xsqlite3_column_database_name(c.tls, pstmt, int32(i))),
TableName: libc.GoString(sqlite3.Xsqlite3_column_table_name(c.tls, pstmt, int32(i))),
OriginName: libc.GoString(sqlite3.Xsqlite3_column_origin_name(c.tls, pstmt, int32(i))),
}
}
return info, nil
}
// C documentation
//
// int sqlite3_column_count(sqlite3_stmt *pStmt);
@@ -291,9 +376,7 @@ func (c *conn) bind(pstmt uintptr, n int, args []driver.NamedValue) (allocs []ui
return
}
for _, v := range allocs {
c.free(v)
}
c.freeAllocs(allocs)
allocs = nil
}()
@@ -600,7 +683,18 @@ func (c *conn) openV2(name, vfsName string, flags int32) (uintptr, error) {
}
if rc := sqlite3.Xsqlite3_open_v2(c.tls, s, p, flags, vfs); rc != sqlite3.SQLITE_OK {
return 0, c.errstr(rc)
dbh := *(*uintptr)(unsafe.Pointer(p))
// Per SQLite docs, sqlite3_open_v2 may allocate a handle even on
// failure. The error message is stored on that handle, and it must
// be closed to avoid leaking resources.
var err error
if dbh != 0 {
err = errstrForDB(c.tls, rc, dbh)
sqlite3.Xsqlite3_close_v2(c.tls, dbh)
} else {
err = c.errstr(rc)
}
return 0, err
}
return *(*uintptr)(unsafe.Pointer(p)), nil
@@ -620,18 +714,31 @@ func (c *conn) free(p uintptr) {
}
}
func (c *conn) freeAllocs(allocs []uintptr) {
for _, v := range allocs {
c.free(v)
}
}
// 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)
return errstrForDB(c.tls, rc, c.db)
}
func errstrForDB(tls *libc.TLS, rc int32, db uintptr) error {
pStr := sqlite3.Xsqlite3_errstr(tls, rc)
str := libc.GoString(pStr)
var s string
if rc == sqlite3.SQLITE_BUSY {
s = " (SQLITE_BUSY)"
}
switch msg := libc.GoString(p); {
if db == 0 {
return &Error{msg: fmt.Sprintf("%s (%v)%s", str, rc, s), code: int(rc)}
}
pMsg := sqlite3.Xsqlite3_errmsg(tls, db)
switch msg := libc.GoString(pMsg); {
case msg == str:
return &Error{msg: fmt.Sprintf("%s (%v)%s", str, rc, s), code: int(rc)}
default:
@@ -913,15 +1020,22 @@ func (c *conn) Serialize() (v []byte, err error) {
return v, nil
}
// Deserialize restore a database from the content returned by Serialize.
// Deserialize restores 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
if bufLen == 0 {
return fmt.Errorf("sqlite: empty buffer passed to Deserialize")
}
pBuf := sqlite3.Xsqlite3_malloc64(c.tls, uint64(bufLen))
if pBuf == 0 {
return fmt.Errorf("sqlite: cannot allocate %d bytes for deserialize", bufLen)
}
copy((*libc.RawMem)(unsafe.Pointer(pBuf))[:bufLen:bufLen], buf)
zSchema := sqlite3.Xsqlite3_db_name(c.tls, c.db, 0)
if zSchema == 0 {
sqlite3.Xsqlite3_free(c.tls, pBuf)
return fmt.Errorf("failed to get main db name")
}
@@ -978,8 +1092,12 @@ func (c *conn) backup(remoteConn *conn, restore bool) (_ *Backup, finalErr error
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)
destDb := remoteConn.db
if restore {
destDb = c.db
}
rc := sqlite3.Xsqlite3_errcode(c.tls, destDb)
return nil, errstrForDB(c.tls, rc, destDb)
}
return &Backup{srcConn: c, dstConn: remoteConn, pBackup: pBackup}, nil
+15 -16
View File
@@ -27,21 +27,21 @@
//
// OS Arch SQLite version
// ------------------------------
// darwin amd64 3.51.2
// darwin arm64 3.51.2
// freebsd amd64 3.51.2
// freebsd arm64 3.51.2
// linux 386 3.51.2
// linux amd64 3.51.2
// linux arm 3.51.2
// linux arm64 3.51.2
// linux loong64 3.51.2
// linux ppc64le 3.51.2
// linux riscv64 3.51.2
// linux s390x 3.51.2
// windows 386 3.51.2
// windows amd64 3.51.2
// windows arm64 3.51.2
// darwin amd64 3.53.1
// darwin arm64 3.53.1
// freebsd amd64 3.53.1
// freebsd arm64 3.53.1
// linux 386 3.53.1
// linux amd64 3.53.1
// linux arm 3.53.1
// linux arm64 3.53.1
// linux loong64 3.53.1
// linux ppc64le 3.53.1
// linux riscv64 3.53.1
// linux s390x 3.53.1
// windows 386 3.53.1
// windows amd64 3.53.1
// windows arm64 3.53.1
//
// # Benchmarks
//
@@ -53,7 +53,6 @@
//
// https://modern-c.appspot.com/-/builder/?importpath=modernc.org%2fsqlite
//
//
// # Connecting to a database
//
// To access a Sqlite database do something like
+19 -5
View File
@@ -12,6 +12,8 @@ import (
)
// Driver implements database/sql/driver.Driver.
//
// Registration functions and methods must be called before the first call to Open.
type Driver struct {
// user defined functions that are added to every new connection on Open
udfs map[string]*userDefinedFunction
@@ -51,11 +53,12 @@ func newDriver() *Driver { return d }
// 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.
// _time_format: The name of a format to use when writing time values to the database.
// The currently supported values are (1) "sqlite" for YYYY-MM-DD HH:MM:SS.SSS[+-]HH:MM
// (format 4 from https://www.sqlite.org/lang_datefunc.html#time_values with sub-second
// precision and timezone specifier) and (2) "datetime" for YYYY-MM-DD HH:MM:SS
// (format 3, matching the output of SQLite's datetime() function).
// If this parameter is not specified, then the default String() format will be used.
//
// _time_integer_format: The name of a integer format to use when writing time values.
// By default, the time is stored as string and the format can be set with _time_format
@@ -70,6 +73,17 @@ func newDriver() *Driver { return d }
// _inttotime: Enable conversion of time column (DATE, DATETIME,TIMESTAMP) from integer
// to time if the field contain integer (int64).
//
// _texttotime: Enable ColumnTypeScanType to report time.Time instead of string
// for TEXT columns declared as DATE, DATETIME, TIME, or TIMESTAMP.
//
// _timezone: A timezone to use for all time reads and writes, such as "UTC".
// The value is parsed by time.LoadLocation.
// Writes will convert to the timezone before formatting as a string;
// it does not impact _inttotime integer values, as they always use UTC.
// Reads will interpret timezone-less strings as being in this timezone.
// Values that are in a known timezone, such as a string with a timezone specifier
// or an integer with _inttotime (specified to be in UTC), will be converted to this timezone.
//
// _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
+17
View File
@@ -0,0 +1,17 @@
// Copyright 2026 The libsqlite3-go Authors. All rights reserved.
// Use of the source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sqlite3
import (
"modernc.org/libc"
)
func ___inline_isnanf(tls *libc.TLS, f float32) int32 {
return libc.X__inline_isnanf(tls, f)
}
func ___inline_isnan(tls *libc.TLS, f float64) int32 {
return libc.X__inline_isnand(tls, f)
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2026 The libsqlite3-go Authors. All rights reserved.
// Use of the source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sqlite3
import (
"math/bits"
"modernc.org/libc"
)
func ___umulh(tls *libc.TLS, a, b uint64) uint64 {
hi, _ := bits.Mul64(a, b)
return hi
}
+1 -1
View File
@@ -253,7 +253,7 @@ func mutexTry(tls *libc.TLS, m uintptr) int32 {
// Non-recursive mutex
if !(*mutex)(unsafe.Pointer(m)).recursive {
if (*mutex)(unsafe.Pointer(m)).TryLock() {
(*mutex)(unsafe.Pointer(m)).id = tls.ID
atomic.StoreInt32(&(*mutex)(unsafe.Pointer(m)).id, tls.ID)
return SQLITE_OK
}
+17594 -14119
View File
File diff suppressed because one or more lines are too long
+17599 -14121
View File
File diff suppressed because one or more lines are too long
+16993 -13429
View File
File diff suppressed because one or more lines are too long
+16995 -13431
View File
File diff suppressed because one or more lines are too long
+16906 -13299
View File
File diff suppressed because one or more lines are too long
+16836 -13229
View File
File diff suppressed because one or more lines are too long
+16875 -13239
View File
File diff suppressed because one or more lines are too long
+16838 -13231
View File
File diff suppressed because one or more lines are too long
+16836 -13229
View File
File diff suppressed because one or more lines are too long
+16838 -13231
View File
File diff suppressed because one or more lines are too long
+16838 -13231
View File
File diff suppressed because one or more lines are too long
+16782 -13176
View File
File diff suppressed because one or more lines are too long
+16902 -13312
View File
File diff suppressed because one or more lines are too long
+16903 -13314
View File
File diff suppressed because one or more lines are too long
+17105 -13468
View File
File diff suppressed because one or more lines are too long
+17117 -13479
View File
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -182,7 +182,7 @@ func (d *SQLitePreUpdateData) value(ppValue uintptr, i int, new bool) (any, erro
return src, nil
}
func preUpdateHookTrampoline(tls *libc.TLS, handle uintptr, pCsr uintptr, op int32, zDb uintptr, pTab uintptr, iKey1 int64, iReg int32, iBlobWrite int32) {
func preUpdateHookTrampoline(tls *libc.TLS, handle uintptr, pCsr uintptr, op int32, zDb uintptr, pTab uintptr, iKey1 int64, iKey2 int64) {
xPreUpdateHandlers.mu.RLock()
xPreUpdateHandler := xPreUpdateHandlers.m[handle]
xPreUpdateHandlers.mu.RUnlock()
@@ -197,12 +197,12 @@ func preUpdateHookTrampoline(tls *libc.TLS, handle uintptr, pCsr uintptr, op int
DatabaseName: libc.GoString(zDb),
TableName: libc.GoString(pTab),
OldRowID: iKey1,
NewRowID: int64(iReg),
NewRowID: iKey2,
}
xPreUpdateHandler(data)
}
func commitHookTrampoline(tls *libc.TLS, handle uintptr, pCsr uintptr) int32 {
func commitHookTrampoline(tls *libc.TLS, handle uintptr) int32 {
xCommitHandlers.mu.RLock()
xCommitHandler := xCommitHandlers.m[handle]
xCommitHandlers.mu.RUnlock()
@@ -214,7 +214,7 @@ func commitHookTrampoline(tls *libc.TLS, handle uintptr, pCsr uintptr) int32 {
return xCommitHandler()
}
func rollbackHookTrampoline(tls *libc.TLS, handle uintptr, pCsr uintptr) {
func rollbackHookTrampoline(tls *libc.TLS, handle uintptr) {
xRollbackHandlers.mu.RLock()
xRollbackHandler := xRollbackHandlers.m[handle]
xRollbackHandlers.mu.RUnlock()
+18 -9
View File
@@ -27,8 +27,13 @@ type rows struct {
reuseStmt bool // If true, Close() resets instead of finalizing
}
func newRows(c *conn, pstmt uintptr, allocs []uintptr, empty bool) (r *rows, err error) {
r = &rows{c: c, pstmt: pstmt, allocs: allocs, empty: empty}
func newRows(c *conn, pstmt uintptr, allocs *[]uintptr, empty bool) (r *rows, err error) {
var a []uintptr
if allocs != nil {
a = *allocs
*allocs = nil
}
r = &rows{c: c, pstmt: pstmt, allocs: a, empty: empty}
defer func() {
if err != nil {
@@ -54,9 +59,7 @@ func newRows(c *conn, pstmt uintptr, allocs []uintptr, empty bool) (r *rows, err
// Close closes the rows iterator.
func (r *rows) Close() (err error) {
for _, v := range r.allocs {
r.c.free(v)
}
r.c.freeAllocs(r.allocs)
r.allocs = nil
if r.reuseStmt {
@@ -128,10 +131,10 @@ func (r *rows) Next(dest []driver.Value) (err error) {
// without breaking the legacy heuristic for existing users.
switch r.c.integerTimeFormat {
case "unix_micro":
dest[i] = time.UnixMicro(v).UTC()
dest[i] = r.c.applyTimezone(time.UnixMicro(v).UTC())
continue
case "unix_nano":
dest[i] = time.Unix(0, v).UTC()
dest[i] = r.c.applyTimezone(time.Unix(0, v).UTC())
continue
}
@@ -143,10 +146,10 @@ func (r *rows) Next(dest []driver.Value) (err error) {
// timestamp?
if v > 1e12 || v < -1e12 {
// Milliseconds
dest[i] = time.UnixMilli(v).UTC()
dest[i] = r.c.applyTimezone(time.UnixMilli(v).UTC())
} else {
// Seconds
dest[i] = time.Unix(v, 0).UTC()
dest[i] = r.c.applyTimezone(time.Unix(v, 0).UTC())
}
default:
dest[i] = v
@@ -276,6 +279,12 @@ func (r *rows) ColumnTypeScanType(index int) reflect.Type {
case sqlite3.SQLITE_FLOAT:
return reflect.TypeOf(float64(0))
case sqlite3.SQLITE_TEXT:
if r.c.textToTime {
switch strings.ToLower(r.c.columnDeclType(r.pstmt, index)) {
case "date", "datetime", "time", "timestamp":
return reflect.TypeOf(time.Time{})
}
}
return reflect.TypeOf("")
case sqlite3.SQLITE_BLOB:
return reflect.TypeOf([]byte(nil))
+39
View File
@@ -184,6 +184,14 @@ func applyQueryParams(c *conn, query string) error {
c.integerTimeFormat = v
}
if v := q.Get("_timezone"); v != "" {
loc, err := time.LoadLocation(v)
if err != nil {
return fmt.Errorf("unknown _timezone %q: %w", v, err)
}
c.loc = loc
}
if v := q.Get("_txlock"); v != "" {
lower := strings.ToLower(v)
if lower != "deferred" && lower != "immediate" && lower != "exclusive" {
@@ -201,6 +209,15 @@ func applyQueryParams(c *conn, query string) error {
c.intToTime = onoff
}
if v := q.Get("_texttotime"); v != "" {
onoff, err := strconv.ParseBool(v)
if err != nil {
return fmt.Errorf("unknown _texttotime %q, must be 1, t, T, TRUE, true, True, 0, f, F, FALSE, false, False",
v)
}
c.textToTime = onoff
}
return nil
}
@@ -881,3 +898,25 @@ func Limit(c *sql.Conn, id int, newVal int) (r int, err error) {
return r, err
}
// ColumnInfo describes one output column of a prepared SQL statement.
//
// See https://www.sqlite.org/c3ref/column_database_name.html for more details.
type ColumnInfo struct {
// Name is the column's name, from sqlite3_column_name.
Name string
// DeclType is the declared column type, from sqlite3_column_decltype.
DeclType string
// DatabaseName is the name of the source database, from sqlite3_column_database_name.
// Empty if the column does not resolve to an unambiguous reference to a single database column,
// such as expressions, function calls, or constants.
DatabaseName string
// TableName is the name of the source table, from sqlite3_column_table_name.
// Empty if the column does not resolve to an unambiguous reference to a single database column,
// such as expressions, function calls, or constants.
TableName string
// OriginName is the name of the source column name, from sqlite3_column_origin_name.
// Empty if the column does not resolve to an unambiguous reference to a single database column,
// such as expressions, function calls, or constants.
OriginName string
}
+59 -31
View File
@@ -137,11 +137,7 @@ func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Res
}
// Free allocations after step
if len(allocs) != 0 {
defer func() {
for _, v := range allocs {
s.c.free(v)
}
}()
defer func() { s.c.freeAllocs(allocs) }()
}
}
@@ -153,12 +149,29 @@ func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Res
// Handle Result
switch rc & 0xff {
case sqlite3.SQLITE_DONE, sqlite3.SQLITE_ROW:
case sqlite3.SQLITE_DONE:
r, err = newResult(s.c)
case sqlite3.SQLITE_ROW:
// Step to completion, matching C sqlite3_exec()
// semantics. Required for DML RETURNING correctness;
// also drains SELECT results if passed to Exec.
for rc&0xff == sqlite3.SQLITE_ROW {
if atomic.LoadInt32(&done) != 0 {
return ctx.Err()
}
rc, err = s.c.step(s.pstmt)
if err != nil {
return err
}
}
if rc&0xff != sqlite3.SQLITE_DONE {
return s.c.errstr(int32(rc))
}
r, err = newResult(s.c)
default:
return s.c.errstr(int32(rc))
}
return nil
return err
}()
// RESET (Crucial: Do not finalize)
@@ -182,7 +195,7 @@ func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Res
if pstmt == 0 {
continue
}
err = func() (err error) {
err = func() error {
n, err := s.c.bindParameterCount(pstmt)
if err != nil {
return err
@@ -195,11 +208,7 @@ func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Res
}
if len(allocs) != 0 {
defer func() {
for _, v := range allocs {
s.c.free(v)
}
}()
defer func() { s.c.freeAllocs(allocs) }()
}
}
@@ -209,13 +218,30 @@ func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Res
}
switch rc & 0xff {
case sqlite3.SQLITE_DONE, sqlite3.SQLITE_ROW:
case sqlite3.SQLITE_DONE:
r, err = newResult(s.c)
case sqlite3.SQLITE_ROW:
// Step to completion, matching C sqlite3_exec()
// semantics. Required for DML RETURNING correctness;
// also drains SELECT results if passed to Exec.
for rc&0xff == sqlite3.SQLITE_ROW {
if atomic.LoadInt32(&done) != 0 {
return ctx.Err()
}
rc, err = s.c.step(pstmt)
if err != nil {
return err
}
}
if rc&0xff != sqlite3.SQLITE_DONE {
return s.c.errstr(int32(rc))
}
r, err = newResult(s.c)
default:
return s.c.errstr(int32(rc))
}
return nil
return err
}()
e := s.c.finalize(pstmt)
@@ -270,8 +296,6 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
}
}
var allocs []uintptr
defer func() {
if ctx != nil && atomic.LoadInt32(&done) != 0 {
if r != nil {
@@ -279,7 +303,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
}
r, err = nil, ctx.Err()
} else if r == nil && err == nil {
r, err = newRows(s.c, pstmt, allocs, true)
r, err = newRows(s.c, pstmt, nil, true)
}
if pstmt != 0 {
@@ -297,6 +321,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
// OPTIMIZED PATH: Single Cached Statement
if s.pstmt != 0 {
var allocs []uintptr
// Bind
n, err := s.c.bindParameterCount(s.pstmt)
if err != nil {
@@ -312,9 +337,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
rc, err := s.c.step(s.pstmt)
if err != nil {
// On error, we must free allocs manually because 'newRows' won't take ownership
for _, v := range allocs {
s.c.free(v)
}
s.c.freeAllocs(allocs)
s.c.reset(s.pstmt)
s.c.clearBindings(s.pstmt)
return nil, err
@@ -324,7 +347,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
switch rc & 0xff {
case sqlite3.SQLITE_ROW:
// Pass reuseStmt=true
if r, err = newRows(s.c, s.pstmt, allocs, false); err != nil {
if r, err = newRows(s.c, s.pstmt, &allocs, false); err != nil {
s.c.reset(s.pstmt)
s.c.clearBindings(s.pstmt)
return nil, err
@@ -341,7 +364,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
// Actually, if we pass reuseStmt=true to an empty set,
// rows.Close() will eventually reset it.
if r, err = newRows(s.c, s.pstmt, allocs, true); err != nil {
if r, err = newRows(s.c, s.pstmt, &allocs, true); err != nil {
s.c.reset(s.pstmt)
s.c.clearBindings(s.pstmt)
return nil, err
@@ -351,9 +374,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
default:
// Error case
for _, v := range allocs {
s.c.free(v)
}
s.c.freeAllocs(allocs)
s.c.reset(s.pstmt)
s.c.clearBindings(s.pstmt)
return nil, s.c.errstr(int32(rc))
@@ -363,6 +384,9 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
// FALLBACK PATH: Multi-statement script
for psql := s.psql; *(*byte)(unsafe.Pointer(psql)) != 0 && atomic.LoadInt32(&done) == 0; {
if pstmt, err = s.c.prepareV2(&psql); err != nil {
if r != nil {
r.Close()
}
return nil, err
}
@@ -371,6 +395,9 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
}
err = func() (err error) {
var allocs []uintptr
defer func() { s.c.freeAllocs(allocs) }()
n, err := s.c.bindParameterCount(pstmt)
if err != nil {
return err
@@ -392,15 +419,14 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
if r != nil {
r.Close()
}
if r, err = newRows(s.c, pstmt, allocs, false); err != nil {
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 {
if r, err = newRows(s.c, pstmt, &allocs, true); err != nil {
return err
}
pstmt = 0
@@ -416,10 +442,9 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
if r != nil {
r.Close()
}
if r, err = newRows(s.c, pstmt, allocs, true); err != nil {
if r, err = newRows(s.c, pstmt, &allocs, true); err != nil {
return err
}
pstmt = 0
}
return nil
@@ -435,6 +460,9 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro
}
if err != nil {
if r != nil {
r.Close() // r is from a previous iteration; clean up since we won't return it
}
return nil, err
}
}
+18 -1
View File
@@ -33,7 +33,24 @@ func newTx(ctx context.Context, c *conn, opts driver.TxOptions) (*tx, error) {
// Commit implements driver.Tx.
func (t *tx) Commit() (err error) {
return t.exec(context.Background(), "commit")
err = t.exec(context.Background(), "commit")
if err == nil {
return nil
}
// If Commit fails (e.g., SQLITE_BUSY), the connection might still be inside
// the transaction. database/sql expects the connection to be clean (no active
// transaction) after Commit returns.
//
// We check the low-level autocommit state. 0 means autocommit is disabled =>
// we are inside a transaction.
if t.c.db != 0 && sqlite3.Xsqlite3_get_autocommit(t.c.tls, t.c.db) == 0 {
// Force a rollback to clean up the connection. We ignore the rollback error
// because we must return the original Commit error to the user.
t.exec(context.Background(), "rollback")
}
return err
}
// Rollback implements driver.Tx.
+85 -42
View File
@@ -11,6 +11,7 @@ import (
"unsafe"
"modernc.org/libc"
"modernc.org/libc/sys/types"
sqlite3 "modernc.org/sqlite/lib"
"modernc.org/sqlite/vtab"
)
@@ -36,13 +37,14 @@ var (
name2id: make(map[string]uintptr),
}
// nativeModules holds sqlite3_module instances for registered modules. We
// keep them in Go memory so their addresses remain stable for the C layer.
// nativeModules holds sqlite3_module instances for registered modules,
// allocated via libc so that transpiled C code can access them without
// tripping Go's checkptr instrumentation.
nativeModules = struct {
mu sync.RWMutex
m map[string]*sqlite3.Sqlite3_module
m map[string]uintptr
}{
m: make(map[string]*sqlite3.Sqlite3_module),
m: make(map[string]uintptr),
}
// vtabTables maps sqlite3_vtab* (pVtab) to the corresponding Go Table.
@@ -119,39 +121,50 @@ func (c *conn) registerSingleModule(name string, m vtab.Module) error {
nativeModules.mu.Lock()
defer nativeModules.mu.Unlock()
if _, exists := nativeModules.m[name]; exists {
// Module struct already created; nothing more to do for this connection.
return nil
var modPtr uintptr
if existing, exists := nativeModules.m[name]; exists {
modPtr = existing
} else {
// Allocate with the C allocator so the transpiled SQLite code can
// access the struct without tripping Go's checkptr.
modPtr = libc.Xcalloc(c.tls, 1, types.Size_t(unsafe.Sizeof(sqlite3.Sqlite3_module{})))
if modPtr == 0 {
if !ok {
vtabModules.mu.Lock()
delete(vtabModules.name2id, name)
delete(vtabModules.m, modID)
vtabModules.mu.Unlock()
}
return fmt.Errorf("sqlite: failed to allocate module %q", name)
}
mod := (*sqlite3.Sqlite3_module)(unsafe.Pointer(modPtr))
mod.FiVersion = 2
mod.FxCreate = cFuncPointer(vtabCreateTrampoline)
mod.FxConnect = cFuncPointer(vtabConnectTrampoline)
mod.FxBestIndex = cFuncPointer(vtabBestIndexTrampoline)
mod.FxDisconnect = cFuncPointer(vtabDisconnectTrampoline)
mod.FxDestroy = cFuncPointer(vtabDestroyTrampoline)
mod.FxOpen = cFuncPointer(vtabOpenTrampoline)
mod.FxClose = cFuncPointer(vtabCloseTrampoline)
mod.FxFilter = cFuncPointer(vtabFilterTrampoline)
mod.FxNext = cFuncPointer(vtabNextTrampoline)
mod.FxEof = cFuncPointer(vtabEofTrampoline)
mod.FxColumn = cFuncPointer(vtabColumnTrampoline)
mod.FxRowid = cFuncPointer(vtabRowidTrampoline)
mod.FxFindFunction = cFuncPointer(vtabFindFunctionTrampoline)
mod.FxRename = cFuncPointer(vtabRenameTrampoline)
mod.FxUpdate = cFuncPointer(vtabUpdateTrampoline)
mod.FxBegin = cFuncPointer(vtabBeginTrampoline)
mod.FxSync = cFuncPointer(vtabSyncTrampoline)
mod.FxCommit = cFuncPointer(vtabCommitTrampoline)
mod.FxRollback = cFuncPointer(vtabRollbackTrampoline)
mod.FxSavepoint = cFuncPointer(vtabSavepointTrampoline)
mod.FxRelease = cFuncPointer(vtabReleaseTrampoline)
mod.FxRollbackTo = cFuncPointer(vtabRollbackToTrampoline)
nativeModules.m[name] = modPtr
}
// Build a sqlite3_module descriptor with trampolines.
mod := &sqlite3.Sqlite3_module{}
mod.FiVersion = 1
mod.FxCreate = cFuncPointer(vtabCreateTrampoline)
mod.FxConnect = cFuncPointer(vtabConnectTrampoline)
mod.FxBestIndex = cFuncPointer(vtabBestIndexTrampoline)
mod.FxDisconnect = cFuncPointer(vtabDisconnectTrampoline)
mod.FxDestroy = cFuncPointer(vtabDestroyTrampoline)
mod.FxOpen = cFuncPointer(vtabOpenTrampoline)
mod.FxClose = cFuncPointer(vtabCloseTrampoline)
mod.FxFilter = cFuncPointer(vtabFilterTrampoline)
mod.FxNext = cFuncPointer(vtabNextTrampoline)
mod.FxEof = cFuncPointer(vtabEofTrampoline)
mod.FxColumn = cFuncPointer(vtabColumnTrampoline)
mod.FxRowid = cFuncPointer(vtabRowidTrampoline)
mod.FxFindFunction = cFuncPointer(vtabFindFunctionTrampoline)
mod.FxRename = cFuncPointer(vtabRenameTrampoline)
mod.FxUpdate = cFuncPointer(vtabUpdateTrampoline)
mod.FxBegin = cFuncPointer(vtabBeginTrampoline)
mod.FxSync = cFuncPointer(vtabSyncTrampoline)
mod.FxCommit = cFuncPointer(vtabCommitTrampoline)
mod.FxRollback = cFuncPointer(vtabRollbackTrampoline)
mod.FxSavepoint = cFuncPointer(vtabSavepointTrampoline)
mod.FxRelease = cFuncPointer(vtabReleaseTrampoline)
mod.FxRollbackTo = cFuncPointer(vtabRollbackToTrampoline)
nativeModules.m[name] = mod
// Prepare C string for module name.
zName, err := libc.CString(name)
if err != nil {
@@ -160,12 +173,33 @@ func (c *conn) registerSingleModule(name string, m vtab.Module) error {
defer libc.Xfree(c.tls, zName)
// Register the module with this connection.
if rc := sqlite3.Xsqlite3_create_module_v2(c.tls, c.db, zName, uintptr(unsafe.Pointer(mod)), modID, 0); rc != sqlite3.SQLITE_OK {
if rc := sqlite3.Xsqlite3_create_module_v2(c.tls, c.db, zName, modPtr, modID, 0); rc != sqlite3.SQLITE_OK {
return fmt.Errorf("create_module %q: %w", name, c.errstr(rc))
}
return nil
}
func vtabConfig(tls *libc.TLS, db uintptr, op int32, args ...int32) error {
var va uintptr
if len(args) > 1 {
return fmt.Errorf("vtab_config: too many args (%d)", len(args))
}
if len(args) == 1 {
const vaSize = 8
p := sqlite3.Xsqlite3_malloc(tls, vaSize)
if p == 0 {
return fmt.Errorf("vtab: out of memory")
}
defer sqlite3.Xsqlite3_free(tls, p)
libc.VaList(p, args[0])
va = p
}
if rc := sqlite3.Xsqlite3_vtab_config(tls, db, op, va); rc != sqlite3.SQLITE_OK {
return fmt.Errorf("vtab_config op=%d: rc=%d", op, rc)
}
return nil
}
// vtabCreateTrampoline is the xCreate callback. It invokes the corresponding
// Go vtab.Module.Create method, declares a default schema based on argv, and
// allocates a sqlite3_vtab.
@@ -176,7 +210,7 @@ func vtabCreateTrampoline(tls *libc.TLS, db uintptr, pAux uintptr, argc int32, a
return sqlite3.SQLITE_ERROR
}
args := extractVtabArgs(tls, argc, argv)
ctx := vtab.NewContext(func(schema string) error {
ctx := vtab.NewContextWithConfig(func(schema string) error {
zSchema, err := libc.CString(schema)
if err != nil {
return err
@@ -186,6 +220,10 @@ func vtabCreateTrampoline(tls *libc.TLS, db uintptr, pAux uintptr, argc int32, a
return fmt.Errorf("declare_vtab failed: rc=%d", rc)
}
return nil
}, func() error {
return vtabConfig(tls, db, sqlite3.SQLITE_VTAB_CONSTRAINT_SUPPORT, 1)
}, func(op int32, args ...int32) error {
return vtabConfig(tls, db, op, args...)
})
tbl, err := gm.impl.Create(ctx, args)
if err != nil {
@@ -220,7 +258,7 @@ func vtabConnectTrampoline(tls *libc.TLS, db uintptr, pAux uintptr, argc int32,
return sqlite3.SQLITE_ERROR
}
args := extractVtabArgs(tls, argc, argv)
ctx := vtab.NewContext(func(schema string) error {
ctx := vtab.NewContextWithConfig(func(schema string) error {
zSchema, err := libc.CString(schema)
if err != nil {
return err
@@ -230,6 +268,10 @@ func vtabConnectTrampoline(tls *libc.TLS, db uintptr, pAux uintptr, argc int32,
return fmt.Errorf("declare_vtab failed: rc=%d", rc)
}
return nil
}, func() error {
return vtabConfig(tls, db, sqlite3.SQLITE_VTAB_CONSTRAINT_SUPPORT, 1)
}, func(op int32, args ...int32) error {
return vtabConfig(tls, db, op, args...)
})
tbl, err := gm.impl.Connect(ctx, args)
if err != nil {
@@ -710,18 +752,19 @@ func vtabUpdateTrampoline(tls *libc.TLS, pVtab uintptr, argc int32, argv uintptr
}
// INSERT or UPDATE: argc == N+2. argv[0]=oldRowid (NULL for insert),
// argv[1..N]=column values, argv[N+1]=newRowid (or desired rowid for insert, may be NULL).
// argv[1]=newRowid (or desired rowid for insert, may be NULL),
// argv[2..N+1]=column values.
if argc < 3 {
return sqlite3.SQLITE_MISUSE
}
nCols := argc - 2
// Extract column values
colsPtr := argv + uintptr(1)*sqliteValPtrSize
// Extract column values starting from argv[2]
colsPtr := argv + uintptr(2)*sqliteValPtrSize
cols := functionArgs(tls, nCols, colsPtr)
// Determine old/new rowid
oldPtr := *(*uintptr)(unsafe.Pointer(argv + uintptr(0)*sqliteValPtrSize))
newPtr := *(*uintptr)(unsafe.Pointer(argv + uintptr(argc-1)*sqliteValPtrSize))
newPtr := *(*uintptr)(unsafe.Pointer(argv + uintptr(1)*sqliteValPtrSize))
oldIsNull := sqlite3.Xsqlite3_value_type(tls, oldPtr) == sqlite3.SQLITE_NULL
newIsNull := sqlite3.Xsqlite3_value_type(tls, newPtr) == sqlite3.SQLITE_NULL
+6
View File
@@ -12,6 +12,12 @@ package vtab
// The driver no longer auto-declares based on USING(...) args to support
// dynamic schemas (e.g., CSV headers).
//
// - Constraint support: Modules that want MATCH/other constraints must call
// Context.EnableConstraintSupport from within Create or Connect.
//
// - Vtab config: Modules can call Context.Config to pass sqlite3_vtab_config
// options (e.g., INNOCUOUS, DIRECTONLY) from within Create or Connect.
//
// - Constraint operators: ConstraintOp includes OpUnknown for operators that
// are not recognized. The driver maps common SQLite operators including EQ,
// NE, GT, GE, LT, LE, MATCH, IS/ISNOT, ISNULL/ISNOTNULL, LIKE, GLOB, REGEXP,
+34
View File
@@ -17,6 +17,10 @@ type Value = driver.Value
// may be added in the future as needed.
type Context struct {
declare func(string) error
// constraintSupport enables constraint support (e.g. MATCH) for the module.
constraintSupport func() error
// config issues sqlite3_vtab_config calls for other vtab options.
config func(op int32, args ...int32) error
}
// Declare must be called by a module from within Create or Connect to declare
@@ -32,10 +36,40 @@ func (c Context) Declare(schema string) error {
return c.declare(schema)
}
// EnableConstraintSupport enables virtual table constraint support in SQLite.
// This must be called from within Create or Connect.
func (c Context) EnableConstraintSupport() error {
if c.constraintSupport == nil {
return errors.New("vtab: constraint support not available in this context")
}
return c.constraintSupport()
}
// Config forwards sqlite3_vtab_config options to SQLite. This must be called
// from within Create or Connect.
func (c Context) Config(op int32, args ...int32) error {
if c.config == nil {
return errors.New("vtab: config not available in this context")
}
return c.config(op, args...)
}
// NewContext is used by the engine to create a Context bound to the current
// xCreate/xConnect call. External modules should not need to call this.
func NewContext(declare func(string) error) Context { return Context{declare: declare} }
// NewContextWithConstraintSupport is used by the engine to create a Context
// that can enable constraint support.
func NewContextWithConstraintSupport(declare func(string) error, constraintSupport func() error) Context {
return Context{declare: declare, constraintSupport: constraintSupport}
}
// NewContextWithConfig is used by the engine to create a Context that can
// enable constraint support and other sqlite3_vtab_config options.
func NewContextWithConfig(declare func(string) error, constraintSupport func() error, config func(op int32, args ...int32) error) Context {
return Context{declare: declare, constraintSupport: constraintSupport, config: config}
}
// Module represents a virtual table module, analogous to sqlite3_module in
// the SQLite C API. Implementations are responsible for creating and
// connecting table instances.