summaryrefslogtreecommitdiff
path: root/vendor/github.com/ncruces/go-sqlite3
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/ncruces/go-sqlite3')
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/.gitignore16
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/LICENSE21
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/README.md113
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/backup.go134
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/blob.go250
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/config.go164
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/conn.go426
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/const.go360
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/context.go229
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/driver/driver.go579
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/driver/savepoint.go27
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/driver/time.go31
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/driver/util.go14
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/embed/README.md27
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/embed/build.sh32
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/embed/exports.txt130
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/embed/init.go20
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasmbin0 -> 1365178 bytes
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/error.go162
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/func.go214
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/go.work6
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/go.work.sum9
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_other.go9
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_slice.go25
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_unix.go67
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_windows.go76
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/bool.go22
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/const.go117
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/error.go106
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/func.go193
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/handle.go65
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/json.go35
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/mem.go134
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go97
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_other.go21
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/module.go21
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/pointer.go11
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/reflect.go10
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/json.go11
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/pointer.go12
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/quote.go112
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/sqlite.go341
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/stmt.go639
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/time.go354
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/txn.go294
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/util/osutil/open.go16
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/util/osutil/open_windows.go112
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/util/osutil/osfs.go33
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/value.go236
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/README.md86
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/api.go175
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/const.go234
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/file.go217
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/filename.go174
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/lock.go144
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go23
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md9
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go68
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go311
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go33
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go95
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go34
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go71
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_std_access.go36
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_std_alloc.go19
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go9
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_std_mode.go14
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go9
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go9
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go33
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go106
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go186
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/registry.go48
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/shm.go173
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go21
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go459
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vtab.go663
78 files changed, 9594 insertions, 0 deletions
diff --git a/vendor/github.com/ncruces/go-sqlite3/.gitignore b/vendor/github.com/ncruces/go-sqlite3/.gitignore
new file mode 100644
index 000000000..c8b2376cd
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/.gitignore
@@ -0,0 +1,16 @@
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+tools \ No newline at end of file
diff --git a/vendor/github.com/ncruces/go-sqlite3/LICENSE b/vendor/github.com/ncruces/go-sqlite3/LICENSE
new file mode 100644
index 000000000..9bdc1df48
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 Nuno Cruces
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/ncruces/go-sqlite3/README.md b/vendor/github.com/ncruces/go-sqlite3/README.md
new file mode 100644
index 000000000..c31414724
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/README.md
@@ -0,0 +1,113 @@
+# Go bindings to SQLite using Wazero
+
+[![Go Reference](https://pkg.go.dev/badge/image)](https://pkg.go.dev/github.com/ncruces/go-sqlite3)
+[![Go Report](https://goreportcard.com/badge/github.com/ncruces/go-sqlite3)](https://goreportcard.com/report/github.com/ncruces/go-sqlite3)
+[![Go Coverage](https://github.com/ncruces/go-sqlite3/wiki/coverage.svg)](https://github.com/ncruces/go-sqlite3/wiki/Test-coverage-report)
+
+Go module `github.com/ncruces/go-sqlite3` is a `cgo`-free [SQLite](https://sqlite.org/) wrapper.\
+It provides a [`database/sql`](https://pkg.go.dev/database/sql) compatible driver,
+as well as direct access to most of the [C SQLite API](https://sqlite.org/cintro.html).
+
+It wraps a [Wasm](https://webassembly.org/) [build](embed/) of SQLite,
+and uses [wazero](https://wazero.io/) as the runtime.\
+Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ runtime dependencies [^1].
+
+### Packages
+
+- [`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3)
+ wraps the [C SQLite API](https://sqlite.org/cintro.html)
+ ([example usage](https://pkg.go.dev/github.com/ncruces/go-sqlite3#example-package)).
+- [`github.com/ncruces/go-sqlite3/driver`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/driver)
+ provides a [`database/sql`](https://pkg.go.dev/database/sql) driver
+ ([example usage](https://pkg.go.dev/github.com/ncruces/go-sqlite3/driver#example-package)).
+- [`github.com/ncruces/go-sqlite3/embed`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/embed)
+ embeds a build of SQLite into your application.
+- [`github.com/ncruces/go-sqlite3/vfs`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs)
+ wraps the [C SQLite VFS API](https://sqlite.org/vfs.html) and provides a pure Go implementation.
+- [`github.com/ncruces/go-sqlite3/gormlite`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/gormlite)
+ provides a [GORM](https://gorm.io) driver.
+
+### Extensions
+
+- [`github.com/ncruces/go-sqlite3/ext/array`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/array)
+ provides the [`array`](https://sqlite.org/carray.html) table-valued function.
+- [`github.com/ncruces/go-sqlite3/ext/blobio`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/blobio)
+ simplifies [incremental BLOB I/O](https://sqlite.org/c3ref/blob_open.html).
+- [`github.com/ncruces/go-sqlite3/ext/csv`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/csv)
+ reads [comma-separated values](https://sqlite.org/csv.html).
+- [`github.com/ncruces/go-sqlite3/ext/fileio`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/fileio)
+ reads, writes and lists files.
+- [`github.com/ncruces/go-sqlite3/ext/hash`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/hash)
+ provides cryptographic hash functions.
+- [`github.com/ncruces/go-sqlite3/ext/lines`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/lines)
+ reads data [line-by-line](https://github.com/asg017/sqlite-lines).
+- [`github.com/ncruces/go-sqlite3/ext/pivot`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/pivot)
+ creates [pivot tables](https://github.com/jakethaw/pivot_vtab).
+- [`github.com/ncruces/go-sqlite3/ext/statement`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/statement)
+ creates [parameterized views](https://github.com/0x09/sqlite-statement-vtab).
+- [`github.com/ncruces/go-sqlite3/ext/stats`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/stats)
+ provides [statistics](https://www.oreilly.com/library/view/sql-in-a/9780596155322/ch04s02.html) functions.
+- [`github.com/ncruces/go-sqlite3/ext/unicode`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/unicode)
+ provides [Unicode aware](https://sqlite.org/src/dir/ext/icu) functions.
+- [`github.com/ncruces/go-sqlite3/ext/zorder`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/zorder)
+ maps multidimensional data to one dimension.
+- [`github.com/ncruces/go-sqlite3/vfs/memdb`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/memdb)
+ implements an in-memory VFS.
+- [`github.com/ncruces/go-sqlite3/vfs/readervfs`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/readervfs)
+ implements a VFS for immutable databases.
+- [`github.com/ncruces/go-sqlite3/vfs/adiantum`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/adiantum)
+ wraps a VFS to offer encryption at rest.
+
+### Advanced features
+
+- [incremental BLOB I/O](https://sqlite.org/c3ref/blob_open.html)
+- [nested transactions](https://sqlite.org/lang_savepoint.html)
+- [custom functions](https://sqlite.org/c3ref/create_function.html)
+- [virtual tables](https://sqlite.org/vtab.html)
+- [custom VFSes](https://sqlite.org/vfs.html)
+- [online backup](https://sqlite.org/backup.html)
+- [JSON support](https://sqlite.org/json1.html)
+- [math functions](https://sqlite.org/lang_mathfunc.html)
+- [full-text search](https://sqlite.org/fts5.html)
+- [geospatial search](https://sqlite.org/geopoly.html)
+- [encryption at rest](vfs/adiantum/README.md)
+- [and more…](embed/README.md)
+
+### Caveats
+
+This module replaces the SQLite [OS Interface](https://sqlite.org/vfs.html)
+(aka VFS) with a [pure Go](vfs/) implementation,
+which has advantages and disadvantages.
+
+Read more about the Go VFS design [here](vfs/README.md).
+
+### Testing
+
+This project aims for [high test coverage](https://github.com/ncruces/go-sqlite3/wiki/Test-coverage-report).
+It also benefits greatly from [SQLite's](https://sqlite.org/testing.html) and
+[wazero's](https://tetrate.io/blog/introducing-wazero-from-tetrate/#:~:text=Rock%2Dsolid%20test%20approach) thorough testing.
+
+Every commit is [tested](.github/workflows/test.yml) on
+Linux (amd64/arm64/386/riscv64/s390x), macOS (amd64/arm64),
+Windows (amd64), FreeBSD (amd64), illumos (amd64), and Solaris (amd64).
+
+The Go VFS is tested by running SQLite's
+[mptest](https://github.com/sqlite/sqlite/blob/master/mptest/mptest.c).
+
+### Performance
+
+Perfomance of the [`database/sql`](https://pkg.go.dev/database/sql) driver is
+[competitive](https://github.com/cvilsmeier/go-sqlite-bench) with alternatives.
+
+The Wasm and VFS layers are also tested by running SQLite's
+[speedtest1](https://github.com/sqlite/sqlite/blob/master/test/speedtest1.c).
+
+### Alternatives
+
+- [`modernc.org/sqlite`](https://pkg.go.dev/modernc.org/sqlite)
+- [`crawshaw.io/sqlite`](https://pkg.go.dev/crawshaw.io/sqlite)
+- [`github.com/mattn/go-sqlite3`](https://pkg.go.dev/github.com/mattn/go-sqlite3)
+- [`github.com/zombiezen/go-sqlite`](https://pkg.go.dev/github.com/zombiezen/go-sqlite)
+
+[^1]: anything else you find in `go.mod` is either a test dependency,
+ or needed by one of the extensions.
diff --git a/vendor/github.com/ncruces/go-sqlite3/backup.go b/vendor/github.com/ncruces/go-sqlite3/backup.go
new file mode 100644
index 000000000..b16c7511e
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/backup.go
@@ -0,0 +1,134 @@
+package sqlite3
+
+// Backup is an handle to an ongoing online backup operation.
+//
+// https://sqlite.org/c3ref/backup.html
+type Backup struct {
+ c *Conn
+ handle uint32
+ otherc uint32
+}
+
+// Backup backs up srcDB on the src connection to the "main" database in dstURI.
+//
+// Backup opens the SQLite database file dstURI,
+// and blocks until the entire backup is complete.
+// Use [Conn.BackupInit] for incremental backup.
+//
+// https://sqlite.org/backup.html
+func (src *Conn) Backup(srcDB, dstURI string) error {
+ b, err := src.BackupInit(srcDB, dstURI)
+ if err != nil {
+ return err
+ }
+ defer b.Close()
+ _, err = b.Step(-1)
+ return err
+}
+
+// Restore restores dstDB on the dst connection from the "main" database in srcURI.
+//
+// Restore opens the SQLite database file srcURI,
+// and blocks until the entire restore is complete.
+//
+// https://sqlite.org/backup.html
+func (dst *Conn) Restore(dstDB, srcURI string) error {
+ src, err := dst.openDB(srcURI, OPEN_READONLY|OPEN_URI)
+ if err != nil {
+ return err
+ }
+ b, err := dst.backupInit(dst.handle, dstDB, src, "main")
+ if err != nil {
+ return err
+ }
+ defer b.Close()
+ _, err = b.Step(-1)
+ return err
+}
+
+// BackupInit initializes a backup operation to copy the content of one database into another.
+//
+// BackupInit opens the SQLite database file dstURI,
+// then initializes a backup that copies the contents of srcDB on the src connection
+// to the "main" database in dstURI.
+//
+// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupinit
+func (src *Conn) BackupInit(srcDB, dstURI string) (*Backup, error) {
+ dst, err := src.openDB(dstURI, OPEN_READWRITE|OPEN_CREATE|OPEN_URI)
+ if err != nil {
+ return nil, err
+ }
+ return src.backupInit(dst, "main", src.handle, srcDB)
+}
+
+func (c *Conn) backupInit(dst uint32, dstName string, src uint32, srcName string) (*Backup, error) {
+ defer c.arena.mark()()
+ dstPtr := c.arena.string(dstName)
+ srcPtr := c.arena.string(srcName)
+
+ other := dst
+ if c.handle == dst {
+ other = src
+ }
+
+ r := c.call("sqlite3_backup_init",
+ uint64(dst), uint64(dstPtr),
+ uint64(src), uint64(srcPtr))
+ if r == 0 {
+ defer c.closeDB(other)
+ r = c.call("sqlite3_errcode", uint64(dst))
+ return nil, c.sqlite.error(r, dst)
+ }
+
+ return &Backup{
+ c: c,
+ otherc: other,
+ handle: uint32(r),
+ }, nil
+}
+
+// Close finishes a backup operation.
+//
+// It is safe to close a nil, zero or closed Backup.
+//
+// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupfinish
+func (b *Backup) Close() error {
+ if b == nil || b.handle == 0 {
+ return nil
+ }
+
+ r := b.c.call("sqlite3_backup_finish", uint64(b.handle))
+ b.c.closeDB(b.otherc)
+ b.handle = 0
+ return b.c.error(r)
+}
+
+// Step copies up to nPage pages between the source and destination databases.
+// If nPage is negative, all remaining source pages are copied.
+//
+// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupstep
+func (b *Backup) Step(nPage int) (done bool, err error) {
+ r := b.c.call("sqlite3_backup_step", uint64(b.handle), uint64(nPage))
+ if r == _DONE {
+ return true, nil
+ }
+ return false, b.c.error(r)
+}
+
+// Remaining returns the number of pages still to be backed up
+// at the conclusion of the most recent [Backup.Step].
+//
+// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining
+func (b *Backup) Remaining() int {
+ r := b.c.call("sqlite3_backup_remaining", uint64(b.handle))
+ return int(int32(r))
+}
+
+// PageCount returns the total number of pages in the source database
+// at the conclusion of the most recent [Backup.Step].
+//
+// https://sqlite.org/c3ref/backup_finish.html#sqlite3backuppagecount
+func (b *Backup) PageCount() int {
+ r := b.c.call("sqlite3_backup_pagecount", uint64(b.handle))
+ return int(int32(r))
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/blob.go b/vendor/github.com/ncruces/go-sqlite3/blob.go
new file mode 100644
index 000000000..bb10c5fa2
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/blob.go
@@ -0,0 +1,250 @@
+package sqlite3
+
+import (
+ "io"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+// ZeroBlob represents a zero-filled, length n BLOB
+// that can be used as an argument to
+// [database/sql.DB.Exec] and similar methods.
+type ZeroBlob int64
+
+// Blob is an handle to an open BLOB.
+//
+// It implements [io.ReadWriteSeeker] for incremental BLOB I/O.
+//
+// https://sqlite.org/c3ref/blob.html
+type Blob struct {
+ c *Conn
+ bytes int64
+ offset int64
+ handle uint32
+}
+
+var _ io.ReadWriteSeeker = &Blob{}
+
+// OpenBlob opens a BLOB for incremental I/O.
+//
+// https://sqlite.org/c3ref/blob_open.html
+func (c *Conn) OpenBlob(db, table, column string, row int64, write bool) (*Blob, error) {
+ c.checkInterrupt()
+ defer c.arena.mark()()
+ blobPtr := c.arena.new(ptrlen)
+ dbPtr := c.arena.string(db)
+ tablePtr := c.arena.string(table)
+ columnPtr := c.arena.string(column)
+
+ var flags uint64
+ if write {
+ flags = 1
+ }
+
+ r := c.call("sqlite3_blob_open", uint64(c.handle),
+ uint64(dbPtr), uint64(tablePtr), uint64(columnPtr),
+ uint64(row), flags, uint64(blobPtr))
+
+ if err := c.error(r); err != nil {
+ return nil, err
+ }
+
+ blob := Blob{c: c}
+ blob.handle = util.ReadUint32(c.mod, blobPtr)
+ blob.bytes = int64(c.call("sqlite3_blob_bytes", uint64(blob.handle)))
+ return &blob, nil
+}
+
+// Close closes a BLOB handle.
+//
+// It is safe to close a nil, zero or closed Blob.
+//
+// https://sqlite.org/c3ref/blob_close.html
+func (b *Blob) Close() error {
+ if b == nil || b.handle == 0 {
+ return nil
+ }
+
+ r := b.c.call("sqlite3_blob_close", uint64(b.handle))
+
+ b.handle = 0
+ return b.c.error(r)
+}
+
+// Size returns the size of the BLOB in bytes.
+//
+// https://sqlite.org/c3ref/blob_bytes.html
+func (b *Blob) Size() int64 {
+ return b.bytes
+}
+
+// Read implements the [io.Reader] interface.
+//
+// https://sqlite.org/c3ref/blob_read.html
+func (b *Blob) Read(p []byte) (n int, err error) {
+ if b.offset >= b.bytes {
+ return 0, io.EOF
+ }
+
+ avail := b.bytes - b.offset
+ want := int64(len(p))
+ if want > avail {
+ want = avail
+ }
+
+ defer b.c.arena.mark()()
+ ptr := b.c.arena.new(uint64(want))
+
+ r := b.c.call("sqlite3_blob_read", uint64(b.handle),
+ uint64(ptr), uint64(want), uint64(b.offset))
+ err = b.c.error(r)
+ if err != nil {
+ return 0, err
+ }
+ b.offset += want
+ if b.offset >= b.bytes {
+ err = io.EOF
+ }
+
+ copy(p, util.View(b.c.mod, ptr, uint64(want)))
+ return int(want), err
+}
+
+// WriteTo implements the [io.WriterTo] interface.
+//
+// https://sqlite.org/c3ref/blob_read.html
+func (b *Blob) WriteTo(w io.Writer) (n int64, err error) {
+ if b.offset >= b.bytes {
+ return 0, nil
+ }
+
+ want := int64(1024 * 1024)
+ avail := b.bytes - b.offset
+ if want > avail {
+ want = avail
+ }
+
+ defer b.c.arena.mark()()
+ ptr := b.c.arena.new(uint64(want))
+
+ for want > 0 {
+ r := b.c.call("sqlite3_blob_read", uint64(b.handle),
+ uint64(ptr), uint64(want), uint64(b.offset))
+ err = b.c.error(r)
+ if err != nil {
+ return n, err
+ }
+
+ mem := util.View(b.c.mod, ptr, uint64(want))
+ m, err := w.Write(mem[:want])
+ b.offset += int64(m)
+ n += int64(m)
+ if err != nil {
+ return n, err
+ }
+ if int64(m) != want {
+ return n, io.ErrShortWrite
+ }
+
+ avail = b.bytes - b.offset
+ if want > avail {
+ want = avail
+ }
+ }
+ return n, nil
+}
+
+// Write implements the [io.Writer] interface.
+//
+// https://sqlite.org/c3ref/blob_write.html
+func (b *Blob) Write(p []byte) (n int, err error) {
+ defer b.c.arena.mark()()
+ ptr := b.c.arena.bytes(p)
+
+ r := b.c.call("sqlite3_blob_write", uint64(b.handle),
+ uint64(ptr), uint64(len(p)), uint64(b.offset))
+ err = b.c.error(r)
+ if err != nil {
+ return 0, err
+ }
+ b.offset += int64(len(p))
+ return len(p), nil
+}
+
+// ReadFrom implements the [io.ReaderFrom] interface.
+//
+// https://sqlite.org/c3ref/blob_write.html
+func (b *Blob) ReadFrom(r io.Reader) (n int64, err error) {
+ want := int64(1024 * 1024)
+ avail := b.bytes - b.offset
+ if l, ok := r.(*io.LimitedReader); ok && want > l.N {
+ want = l.N
+ }
+ if want > avail {
+ want = avail
+ }
+ if want < 1 {
+ want = 1
+ }
+
+ defer b.c.arena.mark()()
+ ptr := b.c.arena.new(uint64(want))
+
+ for {
+ mem := util.View(b.c.mod, ptr, uint64(want))
+ m, err := r.Read(mem[:want])
+ if m > 0 {
+ r := b.c.call("sqlite3_blob_write", uint64(b.handle),
+ uint64(ptr), uint64(m), uint64(b.offset))
+ err := b.c.error(r)
+ if err != nil {
+ return n, err
+ }
+ b.offset += int64(m)
+ n += int64(m)
+ }
+ if err == io.EOF {
+ return n, nil
+ }
+ if err != nil {
+ return n, err
+ }
+
+ avail = b.bytes - b.offset
+ if want > avail {
+ want = avail
+ }
+ if want < 1 {
+ want = 1
+ }
+ }
+}
+
+// Seek implements the [io.Seeker] interface.
+func (b *Blob) Seek(offset int64, whence int) (int64, error) {
+ switch whence {
+ default:
+ return 0, util.WhenceErr
+ case io.SeekStart:
+ break
+ case io.SeekCurrent:
+ offset += b.offset
+ case io.SeekEnd:
+ offset += b.bytes
+ }
+ if offset < 0 {
+ return 0, util.OffsetErr
+ }
+ b.offset = offset
+ return offset, nil
+}
+
+// Reopen moves a BLOB handle to a new row of the same database table.
+//
+// https://sqlite.org/c3ref/blob_reopen.html
+func (b *Blob) Reopen(row int64) error {
+ err := b.c.error(b.c.call("sqlite3_blob_reopen", uint64(b.handle), uint64(row)))
+ b.bytes = int64(b.c.call("sqlite3_blob_bytes", uint64(b.handle)))
+ b.offset = 0
+ return err
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/config.go b/vendor/github.com/ncruces/go-sqlite3/config.go
new file mode 100644
index 000000000..0342be7fb
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/config.go
@@ -0,0 +1,164 @@
+package sqlite3
+
+import (
+ "context"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/tetratelabs/wazero/api"
+)
+
+// Config makes configuration changes to a database connection.
+// Only boolean configuration options are supported.
+// Called with no arg reads the current configuration value,
+// called with one arg sets and returns the new value.
+//
+// https://sqlite.org/c3ref/db_config.html
+func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) {
+ defer c.arena.mark()()
+ argsPtr := c.arena.new(2 * ptrlen)
+
+ var flag int
+ switch {
+ case len(arg) == 0:
+ flag = -1
+ case arg[0]:
+ flag = 1
+ }
+
+ util.WriteUint32(c.mod, argsPtr+0*ptrlen, uint32(flag))
+ util.WriteUint32(c.mod, argsPtr+1*ptrlen, argsPtr)
+
+ r := c.call("sqlite3_db_config", uint64(c.handle),
+ uint64(op), uint64(argsPtr))
+ return util.ReadUint32(c.mod, argsPtr) != 0, c.error(r)
+}
+
+// ConfigLog sets up the error logging callback for the connection.
+//
+// https://sqlite.org/errlog.html
+func (c *Conn) ConfigLog(cb func(code ExtendedErrorCode, msg string)) error {
+ var enable uint64
+ if cb != nil {
+ enable = 1
+ }
+ r := c.call("sqlite3_config_log_go", enable)
+ if err := c.error(r); err != nil {
+ return err
+ }
+ c.log = cb
+ return nil
+}
+
+func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) {
+ if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.log != nil {
+ msg := util.ReadString(mod, zMsg, _MAX_LENGTH)
+ c.log(xErrorCode(iCode), msg)
+ }
+}
+
+// Limit allows the size of various constructs to be
+// limited on a connection by connection basis.
+//
+// https://sqlite.org/c3ref/limit.html
+func (c *Conn) Limit(id LimitCategory, value int) int {
+ r := c.call("sqlite3_limit", uint64(c.handle), uint64(id), uint64(value))
+ return int(int32(r))
+}
+
+// SetAuthorizer registers an authorizer callback with the database connection.
+//
+// https://sqlite.org/c3ref/set_authorizer.html
+func (c *Conn) SetAuthorizer(cb func(action AuthorizerActionCode, name3rd, name4th, schema, nameInner string) AuthorizerReturnCode) error {
+ var enable uint64
+ if cb != nil {
+ enable = 1
+ }
+ r := c.call("sqlite3_set_authorizer_go", uint64(c.handle), enable)
+ if err := c.error(r); err != nil {
+ return err
+ }
+ c.authorizer = cb
+ return nil
+
+}
+
+func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zName3rd, zName4th, zSchema, zNameInner uint32) (rc AuthorizerReturnCode) {
+ if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.authorizer != nil {
+ var name3rd, name4th, schema, nameInner string
+ if zName3rd != 0 {
+ name3rd = util.ReadString(mod, zName3rd, _MAX_NAME)
+ }
+ if zName4th != 0 {
+ name4th = util.ReadString(mod, zName4th, _MAX_NAME)
+ }
+ if zSchema != 0 {
+ schema = util.ReadString(mod, zSchema, _MAX_NAME)
+ }
+ if zNameInner != 0 {
+ nameInner = util.ReadString(mod, zNameInner, _MAX_NAME)
+ }
+ rc = c.authorizer(action, name3rd, name4th, schema, nameInner)
+ }
+ return rc
+}
+
+// WalCheckpoint checkpoints a WAL database.
+//
+// https://sqlite.org/c3ref/wal_checkpoint_v2.html
+func (c *Conn) WalCheckpoint(schema string, mode CheckpointMode) (nLog, nCkpt int, err error) {
+ defer c.arena.mark()()
+ nLogPtr := c.arena.new(ptrlen)
+ nCkptPtr := c.arena.new(ptrlen)
+ schemaPtr := c.arena.string(schema)
+ r := c.call("sqlite3_wal_checkpoint_v2",
+ uint64(c.handle), uint64(schemaPtr), uint64(mode),
+ uint64(nLogPtr), uint64(nCkptPtr))
+ nLog = int(int32(util.ReadUint32(c.mod, nLogPtr)))
+ nCkpt = int(int32(util.ReadUint32(c.mod, nCkptPtr)))
+ return nLog, nCkpt, c.error(r)
+}
+
+// WalAutoCheckpoint configures WAL auto-checkpoints.
+//
+// https://sqlite.org/c3ref/wal_autocheckpoint.html
+func (c *Conn) WalAutoCheckpoint(pages int) error {
+ r := c.call("sqlite3_wal_autocheckpoint", uint64(c.handle), uint64(pages))
+ return c.error(r)
+}
+
+// WalHook registers a callback function to be invoked
+// each time data is committed to a database in WAL mode.
+//
+// https://sqlite.org/c3ref/wal_hook.html
+func (c *Conn) WalHook(cb func(db *Conn, schema string, pages int) error) {
+ var enable uint64
+ if cb != nil {
+ enable = 1
+ }
+ c.call("sqlite3_wal_hook_go", uint64(c.handle), enable)
+ c.wal = cb
+}
+
+func walCallback(ctx context.Context, mod api.Module, _, pDB, zSchema uint32, pages int32) (rc uint32) {
+ if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.wal != nil {
+ schema := util.ReadString(mod, zSchema, _MAX_NAME)
+ err := c.wal(c, schema, int(pages))
+ _, rc = errorCode(err, ERROR)
+ }
+ return rc
+}
+
+// AutoVacuumPages registers a autovacuum compaction amount callback.
+//
+// https://sqlite.org/c3ref/autovacuum_pages.html
+func (c *Conn) AutoVacuumPages(cb func(schema string, dbPages, freePages, bytesPerPage uint) uint) error {
+ funcPtr := util.AddHandle(c.ctx, cb)
+ r := c.call("sqlite3_autovacuum_pages_go", uint64(c.handle), uint64(funcPtr))
+ return c.error(r)
+}
+
+func autoVacuumCallback(ctx context.Context, mod api.Module, pApp, zSchema, nDbPage, nFreePage, nBytePerPage uint32) uint32 {
+ fn := util.GetHandle(ctx, pApp).(func(schema string, dbPages, freePages, bytesPerPage uint) uint)
+ schema := util.ReadString(mod, zSchema, _MAX_NAME)
+ return uint32(fn(schema, uint(nDbPage), uint(nFreePage), uint(nBytePerPage)))
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/conn.go b/vendor/github.com/ncruces/go-sqlite3/conn.go
new file mode 100644
index 000000000..f170ccf57
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/conn.go
@@ -0,0 +1,426 @@
+package sqlite3
+
+import (
+ "context"
+ "fmt"
+ "math"
+ "net/url"
+ "strings"
+ "time"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/ncruces/go-sqlite3/vfs"
+ "github.com/tetratelabs/wazero/api"
+)
+
+// Conn is a database connection handle.
+// A Conn is not safe for concurrent use by multiple goroutines.
+//
+// https://sqlite.org/c3ref/sqlite3.html
+type Conn struct {
+ *sqlite
+
+ interrupt context.Context
+ pending *Stmt
+ busy func(int) bool
+ log func(xErrorCode, string)
+ collation func(*Conn, string)
+ authorizer func(AuthorizerActionCode, string, string, string, string) AuthorizerReturnCode
+ update func(AuthorizerActionCode, string, string, int64)
+ commit func() bool
+ rollback func()
+ wal func(*Conn, string, int) error
+ arena arena
+
+ handle uint32
+}
+
+// Open calls [OpenFlags] with [OPEN_READWRITE], [OPEN_CREATE], [OPEN_URI] and [OPEN_NOFOLLOW].
+func Open(filename string) (*Conn, error) {
+ return newConn(filename, OPEN_READWRITE|OPEN_CREATE|OPEN_URI|OPEN_NOFOLLOW)
+}
+
+// OpenFlags opens an SQLite database file as specified by the filename argument.
+//
+// If none of the required flags is used, a combination of [OPEN_READWRITE] and [OPEN_CREATE] is used.
+// If a URI filename is used, PRAGMA statements to execute can be specified using "_pragma":
+//
+// sqlite3.Open("file:demo.db?_pragma=busy_timeout(10000)")
+//
+// https://sqlite.org/c3ref/open.html
+func OpenFlags(filename string, flags OpenFlag) (*Conn, error) {
+ if flags&(OPEN_READONLY|OPEN_READWRITE|OPEN_CREATE) == 0 {
+ flags |= OPEN_READWRITE | OPEN_CREATE
+ }
+ return newConn(filename, flags)
+}
+
+type connKey struct{}
+
+func newConn(filename string, flags OpenFlag) (conn *Conn, err error) {
+ sqlite, err := instantiateSQLite()
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if conn == nil {
+ sqlite.close()
+ }
+ }()
+
+ c := &Conn{sqlite: sqlite}
+ c.arena = c.newArena(1024)
+ c.ctx = context.WithValue(c.ctx, connKey{}, c)
+ c.handle, err = c.openDB(filename, flags)
+ if err != nil {
+ return nil, err
+ }
+ return c, nil
+}
+
+func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) {
+ defer c.arena.mark()()
+ connPtr := c.arena.new(ptrlen)
+ namePtr := c.arena.string(filename)
+
+ flags |= OPEN_EXRESCODE
+ r := c.call("sqlite3_open_v2", uint64(namePtr), uint64(connPtr), uint64(flags), 0)
+
+ handle := util.ReadUint32(c.mod, connPtr)
+ if err := c.sqlite.error(r, handle); err != nil {
+ c.closeDB(handle)
+ return 0, err
+ }
+
+ if flags|OPEN_URI != 0 && strings.HasPrefix(filename, "file:") {
+ var pragmas strings.Builder
+ if _, after, ok := strings.Cut(filename, "?"); ok {
+ query, _ := url.ParseQuery(after)
+ for _, p := range query["_pragma"] {
+ pragmas.WriteString(`PRAGMA `)
+ pragmas.WriteString(p)
+ pragmas.WriteString(`;`)
+ }
+ }
+ if pragmas.Len() != 0 {
+ pragmaPtr := c.arena.string(pragmas.String())
+ r := c.call("sqlite3_exec", uint64(handle), uint64(pragmaPtr), 0, 0, 0)
+ if err := c.sqlite.error(r, handle, pragmas.String()); err != nil {
+ err = fmt.Errorf("sqlite3: invalid _pragma: %w", err)
+ c.closeDB(handle)
+ return 0, err
+ }
+ }
+ }
+ c.call("sqlite3_progress_handler_go", uint64(handle), 100)
+ return handle, nil
+}
+
+func (c *Conn) closeDB(handle uint32) {
+ r := c.call("sqlite3_close_v2", uint64(handle))
+ if err := c.sqlite.error(r, handle); err != nil {
+ panic(err)
+ }
+}
+
+// Close closes the database connection.
+//
+// If the database connection is associated with unfinalized prepared statements,
+// open blob handles, and/or unfinished backup objects,
+// Close will leave the database connection open and return [BUSY].
+//
+// It is safe to close a nil, zero or closed Conn.
+//
+// https://sqlite.org/c3ref/close.html
+func (c *Conn) Close() error {
+ if c == nil || c.handle == 0 {
+ return nil
+ }
+
+ c.pending.Close()
+ c.pending = nil
+
+ r := c.call("sqlite3_close", uint64(c.handle))
+ if err := c.error(r); err != nil {
+ return err
+ }
+
+ c.handle = 0
+ return c.close()
+}
+
+// Exec is a convenience function that allows an application to run
+// multiple statements of SQL without having to use a lot of code.
+//
+// https://sqlite.org/c3ref/exec.html
+func (c *Conn) Exec(sql string) error {
+ c.checkInterrupt()
+ defer c.arena.mark()()
+ sqlPtr := c.arena.string(sql)
+
+ r := c.call("sqlite3_exec", uint64(c.handle), uint64(sqlPtr), 0, 0, 0)
+ return c.error(r, sql)
+}
+
+// Prepare calls [Conn.PrepareFlags] with no flags.
+func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) {
+ return c.PrepareFlags(sql, 0)
+}
+
+// PrepareFlags compiles the first SQL statement in sql;
+// tail is left pointing to what remains uncompiled.
+// If the input text contains no SQL (if the input is an empty string or a comment),
+// both stmt and err will be nil.
+//
+// https://sqlite.org/c3ref/prepare.html
+func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) {
+ if len(sql) > _MAX_SQL_LENGTH {
+ return nil, "", TOOBIG
+ }
+
+ defer c.arena.mark()()
+ stmtPtr := c.arena.new(ptrlen)
+ tailPtr := c.arena.new(ptrlen)
+ sqlPtr := c.arena.string(sql)
+
+ r := c.call("sqlite3_prepare_v3", uint64(c.handle),
+ uint64(sqlPtr), uint64(len(sql)+1), uint64(flags),
+ uint64(stmtPtr), uint64(tailPtr))
+
+ stmt = &Stmt{c: c}
+ stmt.handle = util.ReadUint32(c.mod, stmtPtr)
+ if sql := sql[util.ReadUint32(c.mod, tailPtr)-sqlPtr:]; sql != "" {
+ tail = sql
+ }
+
+ if err := c.error(r, sql); err != nil {
+ return nil, "", err
+ }
+ if stmt.handle == 0 {
+ return nil, "", nil
+ }
+ return stmt, tail, nil
+}
+
+// DBName returns the schema name for n-th database on the database connection.
+//
+// https://sqlite.org/c3ref/db_name.html
+func (c *Conn) DBName(n int) string {
+ r := c.call("sqlite3_db_name", uint64(c.handle), uint64(n))
+
+ ptr := uint32(r)
+ if ptr == 0 {
+ return ""
+ }
+ return util.ReadString(c.mod, ptr, _MAX_NAME)
+}
+
+// Filename returns the filename for a database.
+//
+// https://sqlite.org/c3ref/db_filename.html
+func (c *Conn) Filename(schema string) *vfs.Filename {
+ var ptr uint32
+ if schema != "" {
+ defer c.arena.mark()()
+ ptr = c.arena.string(schema)
+ }
+
+ r := c.call("sqlite3_db_filename", uint64(c.handle), uint64(ptr))
+ return vfs.OpenFilename(c.ctx, c.mod, uint32(r), vfs.OPEN_MAIN_DB)
+}
+
+// ReadOnly determines if a database is read-only.
+//
+// https://sqlite.org/c3ref/db_readonly.html
+func (c *Conn) ReadOnly(schema string) (ro bool, ok bool) {
+ var ptr uint32
+ if schema != "" {
+ defer c.arena.mark()()
+ ptr = c.arena.string(schema)
+ }
+ r := c.call("sqlite3_db_readonly", uint64(c.handle), uint64(ptr))
+ return int32(r) > 0, int32(r) < 0
+}
+
+// GetAutocommit tests the connection for auto-commit mode.
+//
+// https://sqlite.org/c3ref/get_autocommit.html
+func (c *Conn) GetAutocommit() bool {
+ r := c.call("sqlite3_get_autocommit", uint64(c.handle))
+ return r != 0
+}
+
+// LastInsertRowID returns the rowid of the most recent successful INSERT
+// on the database connection.
+//
+// https://sqlite.org/c3ref/last_insert_rowid.html
+func (c *Conn) LastInsertRowID() int64 {
+ r := c.call("sqlite3_last_insert_rowid", uint64(c.handle))
+ return int64(r)
+}
+
+// SetLastInsertRowID allows the application to set the value returned by
+// [Conn.LastInsertRowID].
+//
+// https://sqlite.org/c3ref/set_last_insert_rowid.html
+func (c *Conn) SetLastInsertRowID(id int64) {
+ c.call("sqlite3_set_last_insert_rowid", uint64(c.handle), uint64(id))
+}
+
+// Changes returns the number of rows modified, inserted or deleted
+// by the most recently completed INSERT, UPDATE or DELETE statement
+// on the database connection.
+//
+// https://sqlite.org/c3ref/changes.html
+func (c *Conn) Changes() int64 {
+ r := c.call("sqlite3_changes64", uint64(c.handle))
+ return int64(r)
+}
+
+// TotalChanges returns the number of rows modified, inserted or deleted
+// by all INSERT, UPDATE or DELETE statements completed
+// since the database connection was opened.
+//
+// https://sqlite.org/c3ref/total_changes.html
+func (c *Conn) TotalChanges() int64 {
+ r := c.call("sqlite3_total_changes64", uint64(c.handle))
+ return int64(r)
+}
+
+// ReleaseMemory frees memory used by a database connection.
+//
+// https://sqlite.org/c3ref/db_release_memory.html
+func (c *Conn) ReleaseMemory() error {
+ r := c.call("sqlite3_db_release_memory", uint64(c.handle))
+ return c.error(r)
+}
+
+// GetInterrupt gets the context set with [Conn.SetInterrupt],
+// or nil if none was set.
+func (c *Conn) GetInterrupt() context.Context {
+ return c.interrupt
+}
+
+// SetInterrupt interrupts a long-running query when a context is done.
+//
+// Subsequent uses of the connection will return [INTERRUPT]
+// until the context is reset by another call to SetInterrupt.
+//
+// To associate a timeout with a connection:
+//
+// ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
+// conn.SetInterrupt(ctx)
+// defer cancel()
+//
+// SetInterrupt returns the old context assigned to the connection.
+//
+// https://sqlite.org/c3ref/interrupt.html
+func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) {
+ // Is it the same context?
+ if ctx == c.interrupt {
+ return ctx
+ }
+
+ // A busy SQL statement prevents SQLite from ignoring an interrupt
+ // that comes before any other statements are started.
+ if c.pending == nil {
+ c.pending, _, _ = c.Prepare(`WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x FROM c) SELECT x FROM c`)
+ }
+
+ old = c.interrupt
+ c.interrupt = ctx
+
+ if old != nil && old.Done() != nil && (ctx == nil || ctx.Err() == nil) {
+ c.pending.Reset()
+ }
+ if ctx != nil && ctx.Done() != nil {
+ c.pending.Step()
+ }
+ return old
+}
+
+func (c *Conn) checkInterrupt() {
+ if c.interrupt != nil && c.interrupt.Err() != nil {
+ c.call("sqlite3_interrupt", uint64(c.handle))
+ }
+}
+
+func progressCallback(ctx context.Context, mod api.Module, pDB uint32) (interrupt uint32) {
+ if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB &&
+ c.interrupt != nil && c.interrupt.Err() != nil {
+ interrupt = 1
+ }
+ return interrupt
+}
+
+// BusyTimeout sets a busy timeout.
+//
+// https://sqlite.org/c3ref/busy_timeout.html
+func (c *Conn) BusyTimeout(timeout time.Duration) error {
+ ms := min((timeout+time.Millisecond-1)/time.Millisecond, math.MaxInt32)
+ r := c.call("sqlite3_busy_timeout", uint64(c.handle), uint64(ms))
+ return c.error(r)
+}
+
+func timeoutCallback(ctx context.Context, mod api.Module, pDB uint32, count, tmout int32) (retry uint32) {
+ if c, ok := ctx.Value(connKey{}).(*Conn); ok &&
+ (c.interrupt == nil || c.interrupt.Err() == nil) {
+ const delays = "\x01\x02\x05\x0a\x0f\x14\x19\x19\x19\x32\x32\x64"
+ const totals = "\x00\x01\x03\x08\x12\x21\x35\x4e\x67\x80\xb2\xe4"
+ const ndelay = int32(len(delays) - 1)
+
+ var delay, prior int32
+ if count <= ndelay {
+ delay = int32(delays[count])
+ prior = int32(totals[count])
+ } else {
+ delay = int32(delays[ndelay])
+ prior = int32(totals[ndelay]) + delay*(count-ndelay)
+ }
+
+ if delay = min(delay, tmout-prior); delay > 0 {
+ time.Sleep(time.Duration(delay) * time.Millisecond)
+ retry = 1
+ }
+ }
+ return retry
+}
+
+// BusyHandler registers a callback to handle [BUSY] errors.
+//
+// https://sqlite.org/c3ref/busy_handler.html
+func (c *Conn) BusyHandler(cb func(count int) (retry bool)) error {
+ var enable uint64
+ if cb != nil {
+ enable = 1
+ }
+ r := c.call("sqlite3_busy_handler_go", uint64(c.handle), enable)
+ if err := c.error(r); err != nil {
+ return err
+ }
+ c.busy = cb
+ return nil
+}
+
+func busyCallback(ctx context.Context, mod api.Module, pDB uint32, count int32) (retry uint32) {
+ if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.busy != nil &&
+ (c.interrupt == nil || c.interrupt.Err() == nil) {
+ if c.busy(int(count)) {
+ retry = 1
+ }
+ }
+ return retry
+}
+
+func (c *Conn) error(rc uint64, sql ...string) error {
+ return c.sqlite.error(rc, c.handle, sql...)
+}
+
+// DriverConn is implemented by the SQLite [database/sql] driver connection.
+//
+// It can be used to access SQLite features like [online backup].
+//
+// [online backup]: https://sqlite.org/backup.html
+type DriverConn interface {
+ Raw() *Conn
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/const.go b/vendor/github.com/ncruces/go-sqlite3/const.go
new file mode 100644
index 000000000..2bb53656f
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/const.go
@@ -0,0 +1,360 @@
+package sqlite3
+
+import "strconv"
+
+const (
+ _OK = 0 /* Successful result */
+ _ROW = 100 /* sqlite3_step() has another row ready */
+ _DONE = 101 /* sqlite3_step() has finished executing */
+
+ _UTF8 = 1
+
+ _MAX_NAME = 1e6 // Self-imposed limit for most NUL terminated strings.
+ _MAX_LENGTH = 1e9
+ _MAX_SQL_LENGTH = 1e9
+ _MAX_ALLOCATION_SIZE = 0x7ffffeff
+ _MAX_FUNCTION_ARG = 100
+
+ ptrlen = 4
+)
+
+// ErrorCode is a result code that [Error.Code] might return.
+//
+// https://sqlite.org/rescode.html
+type ErrorCode uint8
+
+const (
+ ERROR ErrorCode = 1 /* Generic error */
+ INTERNAL ErrorCode = 2 /* Internal logic error in SQLite */
+ PERM ErrorCode = 3 /* Access permission denied */
+ ABORT ErrorCode = 4 /* Callback routine requested an abort */
+ BUSY ErrorCode = 5 /* The database file is locked */
+ LOCKED ErrorCode = 6 /* A table in the database is locked */
+ NOMEM ErrorCode = 7 /* A malloc() failed */
+ READONLY ErrorCode = 8 /* Attempt to write a readonly database */
+ INTERRUPT ErrorCode = 9 /* Operation terminated by sqlite3_interrupt() */
+ IOERR ErrorCode = 10 /* Some kind of disk I/O error occurred */
+ CORRUPT ErrorCode = 11 /* The database disk image is malformed */
+ NOTFOUND ErrorCode = 12 /* Unknown opcode in sqlite3_file_control() */
+ FULL ErrorCode = 13 /* Insertion failed because database is full */
+ CANTOPEN ErrorCode = 14 /* Unable to open the database file */
+ PROTOCOL ErrorCode = 15 /* Database lock protocol error */
+ EMPTY ErrorCode = 16 /* Internal use only */
+ SCHEMA ErrorCode = 17 /* The database schema changed */
+ TOOBIG ErrorCode = 18 /* String or BLOB exceeds size limit */
+ CONSTRAINT ErrorCode = 19 /* Abort due to constraint violation */
+ MISMATCH ErrorCode = 20 /* Data type mismatch */
+ MISUSE ErrorCode = 21 /* Library used incorrectly */
+ NOLFS ErrorCode = 22 /* Uses OS features not supported on host */
+ AUTH ErrorCode = 23 /* Authorization denied */
+ FORMAT ErrorCode = 24 /* Not used */
+ RANGE ErrorCode = 25 /* 2nd parameter to sqlite3_bind out of range */
+ NOTADB ErrorCode = 26 /* File opened that is not a database file */
+ NOTICE ErrorCode = 27 /* Notifications from sqlite3_log() */
+ WARNING ErrorCode = 28 /* Warnings from sqlite3_log() */
+)
+
+// ExtendedErrorCode is a result code that [Error.ExtendedCode] might return.
+//
+// https://sqlite.org/rescode.html
+type (
+ ExtendedErrorCode uint16
+ xErrorCode = ExtendedErrorCode
+)
+
+const (
+ ERROR_MISSING_COLLSEQ ExtendedErrorCode = xErrorCode(ERROR) | (1 << 8)
+ ERROR_RETRY ExtendedErrorCode = xErrorCode(ERROR) | (2 << 8)
+ ERROR_SNAPSHOT ExtendedErrorCode = xErrorCode(ERROR) | (3 << 8)
+ IOERR_READ ExtendedErrorCode = xErrorCode(IOERR) | (1 << 8)
+ IOERR_SHORT_READ ExtendedErrorCode = xErrorCode(IOERR) | (2 << 8)
+ IOERR_WRITE ExtendedErrorCode = xErrorCode(IOERR) | (3 << 8)
+ IOERR_FSYNC ExtendedErrorCode = xErrorCode(IOERR) | (4 << 8)
+ IOERR_DIR_FSYNC ExtendedErrorCode = xErrorCode(IOERR) | (5 << 8)
+ IOERR_TRUNCATE ExtendedErrorCode = xErrorCode(IOERR) | (6 << 8)
+ IOERR_FSTAT ExtendedErrorCode = xErrorCode(IOERR) | (7 << 8)
+ IOERR_UNLOCK ExtendedErrorCode = xErrorCode(IOERR) | (8 << 8)
+ IOERR_RDLOCK ExtendedErrorCode = xErrorCode(IOERR) | (9 << 8)
+ IOERR_DELETE ExtendedErrorCode = xErrorCode(IOERR) | (10 << 8)
+ IOERR_BLOCKED ExtendedErrorCode = xErrorCode(IOERR) | (11 << 8)
+ IOERR_NOMEM ExtendedErrorCode = xErrorCode(IOERR) | (12 << 8)
+ IOERR_ACCESS ExtendedErrorCode = xErrorCode(IOERR) | (13 << 8)
+ IOERR_CHECKRESERVEDLOCK ExtendedErrorCode = xErrorCode(IOERR) | (14 << 8)
+ IOERR_LOCK ExtendedErrorCode = xErrorCode(IOERR) | (15 << 8)
+ IOERR_CLOSE ExtendedErrorCode = xErrorCode(IOERR) | (16 << 8)
+ IOERR_DIR_CLOSE ExtendedErrorCode = xErrorCode(IOERR) | (17 << 8)
+ IOERR_SHMOPEN ExtendedErrorCode = xErrorCode(IOERR) | (18 << 8)
+ IOERR_SHMSIZE ExtendedErrorCode = xErrorCode(IOERR) | (19 << 8)
+ IOERR_SHMLOCK ExtendedErrorCode = xErrorCode(IOERR) | (20 << 8)
+ IOERR_SHMMAP ExtendedErrorCode = xErrorCode(IOERR) | (21 << 8)
+ IOERR_SEEK ExtendedErrorCode = xErrorCode(IOERR) | (22 << 8)
+ IOERR_DELETE_NOENT ExtendedErrorCode = xErrorCode(IOERR) | (23 << 8)
+ IOERR_MMAP ExtendedErrorCode = xErrorCode(IOERR) | (24 << 8)
+ IOERR_GETTEMPPATH ExtendedErrorCode = xErrorCode(IOERR) | (25 << 8)
+ IOERR_CONVPATH ExtendedErrorCode = xErrorCode(IOERR) | (26 << 8)
+ IOERR_VNODE ExtendedErrorCode = xErrorCode(IOERR) | (27 << 8)
+ IOERR_AUTH ExtendedErrorCode = xErrorCode(IOERR) | (28 << 8)
+ IOERR_BEGIN_ATOMIC ExtendedErrorCode = xErrorCode(IOERR) | (29 << 8)
+ IOERR_COMMIT_ATOMIC ExtendedErrorCode = xErrorCode(IOERR) | (30 << 8)
+ IOERR_ROLLBACK_ATOMIC ExtendedErrorCode = xErrorCode(IOERR) | (31 << 8)
+ IOERR_DATA ExtendedErrorCode = xErrorCode(IOERR) | (32 << 8)
+ IOERR_CORRUPTFS ExtendedErrorCode = xErrorCode(IOERR) | (33 << 8)
+ IOERR_IN_PAGE ExtendedErrorCode = xErrorCode(IOERR) | (34 << 8)
+ LOCKED_SHAREDCACHE ExtendedErrorCode = xErrorCode(LOCKED) | (1 << 8)
+ LOCKED_VTAB ExtendedErrorCode = xErrorCode(LOCKED) | (2 << 8)
+ BUSY_RECOVERY ExtendedErrorCode = xErrorCode(BUSY) | (1 << 8)
+ BUSY_SNAPSHOT ExtendedErrorCode = xErrorCode(BUSY) | (2 << 8)
+ BUSY_TIMEOUT ExtendedErrorCode = xErrorCode(BUSY) | (3 << 8)
+ CANTOPEN_NOTEMPDIR ExtendedErrorCode = xErrorCode(CANTOPEN) | (1 << 8)
+ CANTOPEN_ISDIR ExtendedErrorCode = xErrorCode(CANTOPEN) | (2 << 8)
+ CANTOPEN_FULLPATH ExtendedErrorCode = xErrorCode(CANTOPEN) | (3 << 8)
+ CANTOPEN_CONVPATH ExtendedErrorCode = xErrorCode(CANTOPEN) | (4 << 8)
+ CANTOPEN_DIRTYWAL ExtendedErrorCode = xErrorCode(CANTOPEN) | (5 << 8) /* Not Used */
+ CANTOPEN_SYMLINK ExtendedErrorCode = xErrorCode(CANTOPEN) | (6 << 8)
+ CORRUPT_VTAB ExtendedErrorCode = xErrorCode(CORRUPT) | (1 << 8)
+ CORRUPT_SEQUENCE ExtendedErrorCode = xErrorCode(CORRUPT) | (2 << 8)
+ CORRUPT_INDEX ExtendedErrorCode = xErrorCode(CORRUPT) | (3 << 8)
+ READONLY_RECOVERY ExtendedErrorCode = xErrorCode(READONLY) | (1 << 8)
+ READONLY_CANTLOCK ExtendedErrorCode = xErrorCode(READONLY) | (2 << 8)
+ READONLY_ROLLBACK ExtendedErrorCode = xErrorCode(READONLY) | (3 << 8)
+ READONLY_DBMOVED ExtendedErrorCode = xErrorCode(READONLY) | (4 << 8)
+ READONLY_CANTINIT ExtendedErrorCode = xErrorCode(READONLY) | (5 << 8)
+ READONLY_DIRECTORY ExtendedErrorCode = xErrorCode(READONLY) | (6 << 8)
+ ABORT_ROLLBACK ExtendedErrorCode = xErrorCode(ABORT) | (2 << 8)
+ CONSTRAINT_CHECK ExtendedErrorCode = xErrorCode(CONSTRAINT) | (1 << 8)
+ CONSTRAINT_COMMITHOOK ExtendedErrorCode = xErrorCode(CONSTRAINT) | (2 << 8)
+ CONSTRAINT_FOREIGNKEY ExtendedErrorCode = xErrorCode(CONSTRAINT) | (3 << 8)
+ CONSTRAINT_FUNCTION ExtendedErrorCode = xErrorCode(CONSTRAINT) | (4 << 8)
+ CONSTRAINT_NOTNULL ExtendedErrorCode = xErrorCode(CONSTRAINT) | (5 << 8)
+ CONSTRAINT_PRIMARYKEY ExtendedErrorCode = xErrorCode(CONSTRAINT) | (6 << 8)
+ CONSTRAINT_TRIGGER ExtendedErrorCode = xErrorCode(CONSTRAINT) | (7 << 8)
+ CONSTRAINT_UNIQUE ExtendedErrorCode = xErrorCode(CONSTRAINT) | (8 << 8)
+ CONSTRAINT_VTAB ExtendedErrorCode = xErrorCode(CONSTRAINT) | (9 << 8)
+ CONSTRAINT_ROWID ExtendedErrorCode = xErrorCode(CONSTRAINT) | (10 << 8)
+ CONSTRAINT_PINNED ExtendedErrorCode = xErrorCode(CONSTRAINT) | (11 << 8)
+ CONSTRAINT_DATATYPE ExtendedErrorCode = xErrorCode(CONSTRAINT) | (12 << 8)
+ NOTICE_RECOVER_WAL ExtendedErrorCode = xErrorCode(NOTICE) | (1 << 8)
+ NOTICE_RECOVER_ROLLBACK ExtendedErrorCode = xErrorCode(NOTICE) | (2 << 8)
+ NOTICE_RBU ExtendedErrorCode = xErrorCode(NOTICE) | (3 << 8)
+ WARNING_AUTOINDEX ExtendedErrorCode = xErrorCode(WARNING) | (1 << 8)
+ AUTH_USER ExtendedErrorCode = xErrorCode(AUTH) | (1 << 8)
+)
+
+// OpenFlag is a flag for the [OpenFlags] function.
+//
+// https://sqlite.org/c3ref/c_open_autoproxy.html
+type OpenFlag uint32
+
+const (
+ OPEN_READONLY OpenFlag = 0x00000001 /* Ok for sqlite3_open_v2() */
+ OPEN_READWRITE OpenFlag = 0x00000002 /* Ok for sqlite3_open_v2() */
+ OPEN_CREATE OpenFlag = 0x00000004 /* Ok for sqlite3_open_v2() */
+ OPEN_URI OpenFlag = 0x00000040 /* Ok for sqlite3_open_v2() */
+ OPEN_MEMORY OpenFlag = 0x00000080 /* Ok for sqlite3_open_v2() */
+ OPEN_NOMUTEX OpenFlag = 0x00008000 /* Ok for sqlite3_open_v2() */
+ OPEN_FULLMUTEX OpenFlag = 0x00010000 /* Ok for sqlite3_open_v2() */
+ OPEN_SHAREDCACHE OpenFlag = 0x00020000 /* Ok for sqlite3_open_v2() */
+ OPEN_PRIVATECACHE OpenFlag = 0x00040000 /* Ok for sqlite3_open_v2() */
+ OPEN_NOFOLLOW OpenFlag = 0x01000000 /* Ok for sqlite3_open_v2() */
+ OPEN_EXRESCODE OpenFlag = 0x02000000 /* Extended result codes */
+)
+
+// PrepareFlag is a flag that can be passed to [Conn.PrepareFlags].
+//
+// https://sqlite.org/c3ref/c_prepare_normalize.html
+type PrepareFlag uint32
+
+const (
+ PREPARE_PERSISTENT PrepareFlag = 0x01
+ PREPARE_NORMALIZE PrepareFlag = 0x02
+ PREPARE_NO_VTAB PrepareFlag = 0x04
+)
+
+// FunctionFlag is a flag that can be passed to
+// [Conn.CreateFunction] and [Conn.CreateWindowFunction].
+//
+// https://sqlite.org/c3ref/c_deterministic.html
+type FunctionFlag uint32
+
+const (
+ DETERMINISTIC FunctionFlag = 0x000000800
+ DIRECTONLY FunctionFlag = 0x000080000
+ SUBTYPE FunctionFlag = 0x000100000
+ INNOCUOUS FunctionFlag = 0x000200000
+ RESULT_SUBTYPE FunctionFlag = 0x001000000
+)
+
+// StmtStatus name counter values associated with the [Stmt.Status] method.
+//
+// https://sqlite.org/c3ref/c_stmtstatus_counter.html
+type StmtStatus uint32
+
+const (
+ STMTSTATUS_FULLSCAN_STEP StmtStatus = 1
+ STMTSTATUS_SORT StmtStatus = 2
+ STMTSTATUS_AUTOINDEX StmtStatus = 3
+ STMTSTATUS_VM_STEP StmtStatus = 4
+ STMTSTATUS_REPREPARE StmtStatus = 5
+ STMTSTATUS_RUN StmtStatus = 6
+ STMTSTATUS_FILTER_MISS StmtStatus = 7
+ STMTSTATUS_FILTER_HIT StmtStatus = 8
+ STMTSTATUS_MEMUSED StmtStatus = 99
+)
+
+// DBConfig are the available database connection configuration options.
+//
+// https://sqlite.org/c3ref/c_dbconfig_defensive.html
+type DBConfig uint32
+
+const (
+ // DBCONFIG_MAINDBNAME DBConfig = 1000
+ // DBCONFIG_LOOKASIDE DBConfig = 1001
+ DBCONFIG_ENABLE_FKEY DBConfig = 1002
+ DBCONFIG_ENABLE_TRIGGER DBConfig = 1003
+ DBCONFIG_ENABLE_FTS3_TOKENIZER DBConfig = 1004
+ DBCONFIG_ENABLE_LOAD_EXTENSION DBConfig = 1005
+ DBCONFIG_NO_CKPT_ON_CLOSE DBConfig = 1006
+ DBCONFIG_ENABLE_QPSG DBConfig = 1007
+ DBCONFIG_TRIGGER_EQP DBConfig = 1008
+ DBCONFIG_RESET_DATABASE DBConfig = 1009
+ DBCONFIG_DEFENSIVE DBConfig = 1010
+ DBCONFIG_WRITABLE_SCHEMA DBConfig = 1011
+ DBCONFIG_LEGACY_ALTER_TABLE DBConfig = 1012
+ DBCONFIG_DQS_DML DBConfig = 1013
+ DBCONFIG_DQS_DDL DBConfig = 1014
+ DBCONFIG_ENABLE_VIEW DBConfig = 1015
+ DBCONFIG_LEGACY_FILE_FORMAT DBConfig = 1016
+ DBCONFIG_TRUSTED_SCHEMA DBConfig = 1017
+ DBCONFIG_STMT_SCANSTATUS DBConfig = 1018
+ DBCONFIG_REVERSE_SCANORDER DBConfig = 1019
+)
+
+// LimitCategory are the available run-time limit categories.
+//
+// https://sqlite.org/c3ref/c_limit_attached.html
+type LimitCategory uint32
+
+const (
+ LIMIT_LENGTH LimitCategory = 0
+ LIMIT_SQL_LENGTH LimitCategory = 1
+ LIMIT_COLUMN LimitCategory = 2
+ LIMIT_EXPR_DEPTH LimitCategory = 3
+ LIMIT_COMPOUND_SELECT LimitCategory = 4
+ LIMIT_VDBE_OP LimitCategory = 5
+ LIMIT_FUNCTION_ARG LimitCategory = 6
+ LIMIT_ATTACHED LimitCategory = 7
+ LIMIT_LIKE_PATTERN_LENGTH LimitCategory = 8
+ LIMIT_VARIABLE_NUMBER LimitCategory = 9
+ LIMIT_TRIGGER_DEPTH LimitCategory = 10
+ LIMIT_WORKER_THREADS LimitCategory = 11
+)
+
+// AuthorizerActionCode are the integer action codes
+// that the authorizer callback may be passed.
+//
+// https://sqlite.org/c3ref/c_alter_table.html
+type AuthorizerActionCode uint32
+
+const (
+ /***************************************************** 3rd ************ 4th ***********/
+ AUTH_CREATE_INDEX AuthorizerActionCode = 1 /* Index Name Table Name */
+ AUTH_CREATE_TABLE AuthorizerActionCode = 2 /* Table Name NULL */
+ AUTH_CREATE_TEMP_INDEX AuthorizerActionCode = 3 /* Index Name Table Name */
+ AUTH_CREATE_TEMP_TABLE AuthorizerActionCode = 4 /* Table Name NULL */
+ AUTH_CREATE_TEMP_TRIGGER AuthorizerActionCode = 5 /* Trigger Name Table Name */
+ AUTH_CREATE_TEMP_VIEW AuthorizerActionCode = 6 /* View Name NULL */
+ AUTH_CREATE_TRIGGER AuthorizerActionCode = 7 /* Trigger Name Table Name */
+ AUTH_CREATE_VIEW AuthorizerActionCode = 8 /* View Name NULL */
+ AUTH_DELETE AuthorizerActionCode = 9 /* Table Name NULL */
+ AUTH_DROP_INDEX AuthorizerActionCode = 10 /* Index Name Table Name */
+ AUTH_DROP_TABLE AuthorizerActionCode = 11 /* Table Name NULL */
+ AUTH_DROP_TEMP_INDEX AuthorizerActionCode = 12 /* Index Name Table Name */
+ AUTH_DROP_TEMP_TABLE AuthorizerActionCode = 13 /* Table Name NULL */
+ AUTH_DROP_TEMP_TRIGGER AuthorizerActionCode = 14 /* Trigger Name Table Name */
+ AUTH_DROP_TEMP_VIEW AuthorizerActionCode = 15 /* View Name NULL */
+ AUTH_DROP_TRIGGER AuthorizerActionCode = 16 /* Trigger Name Table Name */
+ AUTH_DROP_VIEW AuthorizerActionCode = 17 /* View Name NULL */
+ AUTH_INSERT AuthorizerActionCode = 18 /* Table Name NULL */
+ AUTH_PRAGMA AuthorizerActionCode = 19 /* Pragma Name 1st arg or NULL */
+ AUTH_READ AuthorizerActionCode = 20 /* Table Name Column Name */
+ AUTH_SELECT AuthorizerActionCode = 21 /* NULL NULL */
+ AUTH_TRANSACTION AuthorizerActionCode = 22 /* Operation NULL */
+ AUTH_UPDATE AuthorizerActionCode = 23 /* Table Name Column Name */
+ AUTH_ATTACH AuthorizerActionCode = 24 /* Filename NULL */
+ AUTH_DETACH AuthorizerActionCode = 25 /* Database Name NULL */
+ AUTH_ALTER_TABLE AuthorizerActionCode = 26 /* Database Name Table Name */
+ AUTH_REINDEX AuthorizerActionCode = 27 /* Index Name NULL */
+ AUTH_ANALYZE AuthorizerActionCode = 28 /* Table Name NULL */
+ AUTH_CREATE_VTABLE AuthorizerActionCode = 29 /* Table Name Module Name */
+ AUTH_DROP_VTABLE AuthorizerActionCode = 30 /* Table Name Module Name */
+ AUTH_FUNCTION AuthorizerActionCode = 31 /* NULL Function Name */
+ AUTH_SAVEPOINT AuthorizerActionCode = 32 /* Operation Savepoint Name */
+ AUTH_COPY AuthorizerActionCode = 0 /* No longer used */
+ AUTH_RECURSIVE AuthorizerActionCode = 33 /* NULL NULL */
+)
+
+// AuthorizerReturnCode are the integer codes
+// that the authorizer callback may return.
+//
+// https://sqlite.org/c3ref/c_deny.html
+type AuthorizerReturnCode uint32
+
+const (
+ AUTH_OK AuthorizerReturnCode = 0
+ AUTH_DENY AuthorizerReturnCode = 1 /* Abort the SQL statement with an error */
+ AUTH_IGNORE AuthorizerReturnCode = 2 /* Don't allow access, but don't generate an error */
+)
+
+// CheckpointMode are all the checkpoint mode values.
+//
+// https://sqlite.org/c3ref/c_checkpoint_full.html
+type CheckpointMode uint32
+
+const (
+ CHECKPOINT_PASSIVE CheckpointMode = 0 /* Do as much as possible w/o blocking */
+ CHECKPOINT_FULL CheckpointMode = 1 /* Wait for writers, then checkpoint */
+ CHECKPOINT_RESTART CheckpointMode = 2 /* Like FULL but wait for readers */
+ CHECKPOINT_TRUNCATE CheckpointMode = 3 /* Like RESTART but also truncate WAL */
+)
+
+// TxnState are the allowed return values from [Conn.TxnState].
+//
+// https://sqlite.org/c3ref/c_txn_none.html
+type TxnState uint32
+
+const (
+ TXN_NONE TxnState = 0
+ TXN_READ TxnState = 1
+ TXN_WRITE TxnState = 2
+)
+
+// Datatype is a fundamental datatype of SQLite.
+//
+// https://sqlite.org/c3ref/c_blob.html
+type Datatype uint32
+
+const (
+ INTEGER Datatype = 1
+ FLOAT Datatype = 2
+ TEXT Datatype = 3
+ BLOB Datatype = 4
+ NULL Datatype = 5
+)
+
+// String implements the [fmt.Stringer] interface.
+func (t Datatype) String() string {
+ const name = "INTEGERFLOATEXTBLOBNULL"
+ switch t {
+ case INTEGER:
+ return name[0:7]
+ case FLOAT:
+ return name[7:12]
+ case TEXT:
+ return name[11:15]
+ case BLOB:
+ return name[15:19]
+ case NULL:
+ return name[19:23]
+ }
+ return strconv.FormatUint(uint64(t), 10)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/context.go b/vendor/github.com/ncruces/go-sqlite3/context.go
new file mode 100644
index 000000000..8d7604c66
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/context.go
@@ -0,0 +1,229 @@
+package sqlite3
+
+import (
+ "encoding/json"
+ "errors"
+ "math"
+ "time"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+// Context is the context in which an SQL function executes.
+// An SQLite [Context] is in no way related to a Go [context.Context].
+//
+// https://sqlite.org/c3ref/context.html
+type Context struct {
+ c *Conn
+ handle uint32
+}
+
+// Conn returns the database connection of the
+// [Conn.CreateFunction] or [Conn.CreateWindowFunction]
+// routines that originally registered the application defined function.
+//
+// https://sqlite.org/c3ref/context_db_handle.html
+func (ctx Context) Conn() *Conn {
+ return ctx.c
+}
+
+// SetAuxData saves metadata for argument n of the function.
+//
+// https://sqlite.org/c3ref/get_auxdata.html
+func (ctx Context) SetAuxData(n int, data any) {
+ ptr := util.AddHandle(ctx.c.ctx, data)
+ ctx.c.call("sqlite3_set_auxdata_go", uint64(ctx.handle), uint64(n), uint64(ptr))
+}
+
+// GetAuxData returns metadata for argument n of the function.
+//
+// https://sqlite.org/c3ref/get_auxdata.html
+func (ctx Context) GetAuxData(n int) any {
+ ptr := uint32(ctx.c.call("sqlite3_get_auxdata", uint64(ctx.handle), uint64(n)))
+ return util.GetHandle(ctx.c.ctx, ptr)
+}
+
+// ResultBool sets the result of the function to a bool.
+// SQLite does not have a separate boolean storage class.
+// Instead, boolean values are stored as integers 0 (false) and 1 (true).
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultBool(value bool) {
+ var i int64
+ if value {
+ i = 1
+ }
+ ctx.ResultInt64(i)
+}
+
+// ResultInt sets the result of the function to an int.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultInt(value int) {
+ ctx.ResultInt64(int64(value))
+}
+
+// ResultInt64 sets the result of the function to an int64.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultInt64(value int64) {
+ ctx.c.call("sqlite3_result_int64",
+ uint64(ctx.handle), uint64(value))
+}
+
+// ResultFloat sets the result of the function to a float64.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultFloat(value float64) {
+ ctx.c.call("sqlite3_result_double",
+ uint64(ctx.handle), math.Float64bits(value))
+}
+
+// ResultText sets the result of the function to a string.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultText(value string) {
+ ptr := ctx.c.newString(value)
+ ctx.c.call("sqlite3_result_text64",
+ uint64(ctx.handle), uint64(ptr), uint64(len(value)),
+ uint64(ctx.c.freer), _UTF8)
+}
+
+// ResultRawText sets the text result of the function to a []byte.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultRawText(value []byte) {
+ ptr := ctx.c.newBytes(value)
+ ctx.c.call("sqlite3_result_text64",
+ uint64(ctx.handle), uint64(ptr), uint64(len(value)),
+ uint64(ctx.c.freer), _UTF8)
+}
+
+// ResultBlob sets the result of the function to a []byte.
+// Returning a nil slice is the same as calling [Context.ResultNull].
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultBlob(value []byte) {
+ ptr := ctx.c.newBytes(value)
+ ctx.c.call("sqlite3_result_blob64",
+ uint64(ctx.handle), uint64(ptr), uint64(len(value)),
+ uint64(ctx.c.freer))
+}
+
+// ResultZeroBlob sets the result of the function to a zero-filled, length n BLOB.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultZeroBlob(n int64) {
+ ctx.c.call("sqlite3_result_zeroblob64",
+ uint64(ctx.handle), uint64(n))
+}
+
+// ResultNull sets the result of the function to NULL.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultNull() {
+ ctx.c.call("sqlite3_result_null",
+ uint64(ctx.handle))
+}
+
+// ResultTime sets the result of the function to a [time.Time].
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultTime(value time.Time, format TimeFormat) {
+ if format == TimeFormatDefault {
+ ctx.resultRFC3339Nano(value)
+ return
+ }
+ switch v := format.Encode(value).(type) {
+ case string:
+ ctx.ResultText(v)
+ case int64:
+ ctx.ResultInt64(v)
+ case float64:
+ ctx.ResultFloat(v)
+ default:
+ panic(util.AssertErr())
+ }
+}
+
+func (ctx Context) resultRFC3339Nano(value time.Time) {
+ const maxlen = uint64(len(time.RFC3339Nano)) + 5
+
+ ptr := ctx.c.new(maxlen)
+ buf := util.View(ctx.c.mod, ptr, maxlen)
+ buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
+
+ ctx.c.call("sqlite3_result_text64",
+ uint64(ctx.handle), uint64(ptr), uint64(len(buf)),
+ uint64(ctx.c.freer), _UTF8)
+}
+
+// ResultPointer sets the result of the function to NULL, just like [Context.ResultNull],
+// except that it also associates ptr with that NULL value such that it can be retrieved
+// within an application-defined SQL function using [Value.Pointer].
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultPointer(ptr any) {
+ valPtr := util.AddHandle(ctx.c.ctx, ptr)
+ ctx.c.call("sqlite3_result_pointer_go", uint64(valPtr))
+}
+
+// ResultJSON sets the result of the function to the JSON encoding of value.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultJSON(value any) {
+ data, err := json.Marshal(value)
+ if err != nil {
+ ctx.ResultError(err)
+ return
+ }
+ ctx.ResultRawText(data)
+}
+
+// ResultValue sets the result of the function to a copy of [Value].
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultValue(value Value) {
+ if value.c != ctx.c {
+ ctx.ResultError(MISUSE)
+ return
+ }
+ ctx.c.call("sqlite3_result_value",
+ uint64(ctx.handle), uint64(value.handle))
+}
+
+// ResultError sets the result of the function an error.
+//
+// https://sqlite.org/c3ref/result_blob.html
+func (ctx Context) ResultError(err error) {
+ if errors.Is(err, NOMEM) {
+ ctx.c.call("sqlite3_result_error_nomem", uint64(ctx.handle))
+ return
+ }
+
+ if errors.Is(err, TOOBIG) {
+ ctx.c.call("sqlite3_result_error_toobig", uint64(ctx.handle))
+ return
+ }
+
+ msg, code := errorCode(err, _OK)
+ if msg != "" {
+ defer ctx.c.arena.mark()()
+ ptr := ctx.c.arena.string(msg)
+ ctx.c.call("sqlite3_result_error",
+ uint64(ctx.handle), uint64(ptr), uint64(len(msg)))
+ }
+ if code != _OK {
+ ctx.c.call("sqlite3_result_error_code",
+ uint64(ctx.handle), uint64(code))
+ }
+}
+
+// VTabNoChange may return true if a column is being fetched as part
+// of an update during which the column value will not change.
+//
+// https://sqlite.org/c3ref/vtab_nochange.html
+func (ctx Context) VTabNoChange() bool {
+ r := ctx.c.call("sqlite3_vtab_nochange", uint64(ctx.handle))
+ return r != 0
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/driver/driver.go b/vendor/github.com/ncruces/go-sqlite3/driver/driver.go
new file mode 100644
index 000000000..b496f76ec
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/driver/driver.go
@@ -0,0 +1,579 @@
+// Package driver provides a database/sql driver for SQLite.
+//
+// Importing package driver registers a [database/sql] driver named "sqlite3".
+// You may also need to import package embed.
+//
+// import _ "github.com/ncruces/go-sqlite3/driver"
+// import _ "github.com/ncruces/go-sqlite3/embed"
+//
+// The data source name for "sqlite3" databases can be a filename or a "file:" [URI].
+//
+// The [TRANSACTION] mode can be specified using "_txlock":
+//
+// sql.Open("sqlite3", "file:demo.db?_txlock=immediate")
+//
+// Possible values are: "deferred", "immediate", "exclusive".
+// A [read-only] transaction is always "deferred", regardless of "_txlock".
+//
+// The time encoding/decoding format can be specified using "_timefmt":
+//
+// sql.Open("sqlite3", "file:demo.db?_timefmt=sqlite")
+//
+// Possible values are: "auto" (the default), "sqlite", "rfc3339";
+// "auto" encodes as RFC 3339 and decodes any [format] supported by SQLite;
+// "sqlite" encodes as SQLite and decodes any [format] supported by SQLite;
+// "rfc3339" encodes and decodes RFC 3339 only.
+//
+// [PRAGMA] statements can be specified using "_pragma":
+//
+// sql.Open("sqlite3", "file:demo.db?_pragma=busy_timeout(10000)")
+//
+// If no PRAGMAs are specified, a busy timeout of 1 minute is set.
+//
+// Order matters:
+// busy timeout and locking mode should be the first PRAGMAs set, in that order.
+//
+// [URI]: https://sqlite.org/uri.html
+// [PRAGMA]: https://sqlite.org/pragma.html
+// [format]: https://sqlite.org/lang_datefunc.html#time_values
+// [TRANSACTION]: https://sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
+// [read-only]: https://pkg.go.dev/database/sql#TxOptions
+package driver
+
+import (
+ "context"
+ "database/sql"
+ "database/sql/driver"
+ "errors"
+ "fmt"
+ "io"
+ "net/url"
+ "strings"
+ "time"
+ "unsafe"
+
+ "github.com/ncruces/go-sqlite3"
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+// This variable can be replaced with -ldflags:
+//
+// go build -ldflags="-X github.com/ncruces/go-sqlite3/driver.driverName=sqlite"
+var driverName = "sqlite3"
+
+func init() {
+ if driverName != "" {
+ sql.Register(driverName, &SQLite{})
+ }
+}
+
+// Open opens the SQLite database specified by dataSourceName as a [database/sql.DB].
+//
+// The init function is called by the driver on new connections.
+// The [sqlite3.Conn] can be used to execute queries, register functions, etc.
+// Any error returned closes the connection and is returned to [database/sql].
+func Open(dataSourceName string, init func(*sqlite3.Conn) error) (*sql.DB, error) {
+ c, err := (&SQLite{Init: init}).OpenConnector(dataSourceName)
+ if err != nil {
+ return nil, err
+ }
+ return sql.OpenDB(c), nil
+}
+
+// SQLite implements [database/sql/driver.Driver].
+type SQLite struct {
+ // Init function is called by the driver on new connections.
+ // The [sqlite3.Conn] can be used to execute queries, register functions, etc.
+ // Any error returned closes the connection and is returned to [database/sql].
+ Init func(*sqlite3.Conn) error
+}
+
+// Open implements [database/sql/driver.Driver].
+func (d *SQLite) Open(name string) (driver.Conn, error) {
+ c, err := d.newConnector(name)
+ if err != nil {
+ return nil, err
+ }
+ return c.Connect(context.Background())
+}
+
+// OpenConnector implements [database/sql/driver.DriverContext].
+func (d *SQLite) OpenConnector(name string) (driver.Connector, error) {
+ return d.newConnector(name)
+}
+
+func (d *SQLite) newConnector(name string) (*connector, error) {
+ c := connector{driver: d, name: name}
+
+ var txlock, timefmt string
+ if strings.HasPrefix(name, "file:") {
+ if _, after, ok := strings.Cut(name, "?"); ok {
+ query, err := url.ParseQuery(after)
+ if err != nil {
+ return nil, err
+ }
+ txlock = query.Get("_txlock")
+ timefmt = query.Get("_timefmt")
+ c.pragmas = query.Has("_pragma")
+ }
+ }
+
+ switch txlock {
+ case "":
+ c.txBegin = "BEGIN"
+ case "deferred", "immediate", "exclusive":
+ c.txBegin = "BEGIN " + txlock
+ default:
+ return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", txlock)
+ }
+
+ switch timefmt {
+ case "":
+ c.tmRead = sqlite3.TimeFormatAuto
+ c.tmWrite = sqlite3.TimeFormatDefault
+ case "sqlite":
+ c.tmRead = sqlite3.TimeFormatAuto
+ c.tmWrite = sqlite3.TimeFormat3
+ case "rfc3339":
+ c.tmRead = sqlite3.TimeFormatDefault
+ c.tmWrite = sqlite3.TimeFormatDefault
+ default:
+ c.tmRead = sqlite3.TimeFormat(timefmt)
+ c.tmWrite = sqlite3.TimeFormat(timefmt)
+ }
+ return &c, nil
+}
+
+type connector struct {
+ driver *SQLite
+ name string
+ txBegin string
+ tmRead sqlite3.TimeFormat
+ tmWrite sqlite3.TimeFormat
+ pragmas bool
+}
+
+func (n *connector) Driver() driver.Driver {
+ return n.driver
+}
+
+func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) {
+ c := &conn{
+ txBegin: n.txBegin,
+ tmRead: n.tmRead,
+ tmWrite: n.tmWrite,
+ }
+
+ c.Conn, err = sqlite3.Open(n.name)
+ if err != nil {
+ return nil, err
+ }
+ defer func() {
+ if err != nil {
+ c.Close()
+ }
+ }()
+
+ old := c.Conn.SetInterrupt(ctx)
+ defer c.Conn.SetInterrupt(old)
+
+ if !n.pragmas {
+ err = c.Conn.BusyTimeout(60 * time.Second)
+ if err != nil {
+ return nil, err
+ }
+ }
+ if n.driver.Init != nil {
+ err = n.driver.Init(c.Conn)
+ if err != nil {
+ return nil, err
+ }
+ }
+ if n.pragmas || n.driver.Init != nil {
+ s, _, err := c.Conn.Prepare(`PRAGMA query_only`)
+ if err != nil {
+ return nil, err
+ }
+ if s.Step() && s.ColumnBool(0) {
+ c.readOnly = '1'
+ } else {
+ c.readOnly = '0'
+ }
+ err = s.Close()
+ if err != nil {
+ return nil, err
+ }
+ }
+ return c, nil
+}
+
+type conn struct {
+ *sqlite3.Conn
+ txBegin string
+ txCommit string
+ txRollback string
+ tmRead sqlite3.TimeFormat
+ tmWrite sqlite3.TimeFormat
+ readOnly byte
+}
+
+var (
+ // Ensure these interfaces are implemented:
+ _ driver.ConnPrepareContext = &conn{}
+ _ driver.ExecerContext = &conn{}
+ _ driver.ConnBeginTx = &conn{}
+ _ sqlite3.DriverConn = &conn{}
+)
+
+func (c *conn) Raw() *sqlite3.Conn {
+ return c.Conn
+}
+
+func (c *conn) Begin() (driver.Tx, error) {
+ return c.BeginTx(context.Background(), driver.TxOptions{})
+}
+
+func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
+ txBegin := c.txBegin
+ c.txCommit = `COMMIT`
+ c.txRollback = `ROLLBACK`
+
+ if opts.ReadOnly {
+ txBegin = `
+ BEGIN deferred;
+ PRAGMA query_only=on`
+ c.txRollback = `
+ ROLLBACK;
+ PRAGMA query_only=` + string(c.readOnly)
+ c.txCommit = c.txRollback
+ }
+
+ switch opts.Isolation {
+ default:
+ return nil, util.IsolationErr
+ case
+ driver.IsolationLevel(sql.LevelDefault),
+ driver.IsolationLevel(sql.LevelSerializable):
+ break
+ }
+
+ old := c.Conn.SetInterrupt(ctx)
+ defer c.Conn.SetInterrupt(old)
+
+ err := c.Conn.Exec(txBegin)
+ if err != nil {
+ return nil, err
+ }
+ return c, nil
+}
+
+func (c *conn) Commit() error {
+ err := c.Conn.Exec(c.txCommit)
+ if err != nil && !c.Conn.GetAutocommit() {
+ c.Rollback()
+ }
+ return err
+}
+
+func (c *conn) Rollback() error {
+ err := c.Conn.Exec(c.txRollback)
+ if errors.Is(err, sqlite3.INTERRUPT) {
+ old := c.Conn.SetInterrupt(context.Background())
+ defer c.Conn.SetInterrupt(old)
+ err = c.Conn.Exec(c.txRollback)
+ }
+ return err
+}
+
+func (c *conn) Prepare(query string) (driver.Stmt, error) {
+ return c.PrepareContext(context.Background(), query)
+}
+
+func (c *conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
+ old := c.Conn.SetInterrupt(ctx)
+ defer c.Conn.SetInterrupt(old)
+
+ s, tail, err := c.Conn.Prepare(query)
+ if err != nil {
+ return nil, err
+ }
+ if tail != "" {
+ s.Close()
+ return nil, util.TailErr
+ }
+ return &stmt{Stmt: s, tmRead: c.tmRead, tmWrite: c.tmWrite}, nil
+}
+
+func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
+ if len(args) != 0 {
+ // Slow path.
+ return nil, driver.ErrSkip
+ }
+
+ if savept, ok := ctx.(*saveptCtx); ok {
+ // Called from driver.Savepoint.
+ savept.Savepoint = c.Conn.Savepoint()
+ return resultRowsAffected(0), nil
+ }
+
+ old := c.Conn.SetInterrupt(ctx)
+ defer c.Conn.SetInterrupt(old)
+
+ err := c.Conn.Exec(query)
+ if err != nil {
+ return nil, err
+ }
+
+ return newResult(c.Conn), nil
+}
+
+func (c *conn) CheckNamedValue(arg *driver.NamedValue) error {
+ return nil
+}
+
+type stmt struct {
+ *sqlite3.Stmt
+ tmWrite sqlite3.TimeFormat
+ tmRead sqlite3.TimeFormat
+}
+
+var (
+ // Ensure these interfaces are implemented:
+ _ driver.StmtExecContext = &stmt{}
+ _ driver.StmtQueryContext = &stmt{}
+ _ driver.NamedValueChecker = &stmt{}
+)
+
+func (s *stmt) NumInput() int {
+ n := s.Stmt.BindCount()
+ for i := 1; i <= n; i++ {
+ if s.Stmt.BindName(i) != "" {
+ return -1
+ }
+ }
+ return n
+}
+
+// Deprecated: use ExecContext instead.
+func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
+ return s.ExecContext(context.Background(), namedValues(args))
+}
+
+// Deprecated: use QueryContext instead.
+func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
+ return s.QueryContext(context.Background(), namedValues(args))
+}
+
+func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
+ err := s.setupBindings(args)
+ if err != nil {
+ return nil, err
+ }
+
+ old := s.Stmt.Conn().SetInterrupt(ctx)
+ defer s.Stmt.Conn().SetInterrupt(old)
+
+ err = s.Stmt.Exec()
+ if err != nil {
+ return nil, err
+ }
+
+ return newResult(s.Stmt.Conn()), nil
+}
+
+func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
+ err := s.setupBindings(args)
+ if err != nil {
+ return nil, err
+ }
+ return &rows{ctx: ctx, stmt: s}, nil
+}
+
+func (s *stmt) setupBindings(args []driver.NamedValue) error {
+ err := s.Stmt.ClearBindings()
+ if err != nil {
+ return err
+ }
+
+ var ids [3]int
+ for _, arg := range args {
+ ids := ids[:0]
+ if arg.Name == "" {
+ ids = append(ids, arg.Ordinal)
+ } else {
+ for _, prefix := range []string{":", "@", "$"} {
+ if id := s.Stmt.BindIndex(prefix + arg.Name); id != 0 {
+ ids = append(ids, id)
+ }
+ }
+ }
+
+ for _, id := range ids {
+ switch a := arg.Value.(type) {
+ case bool:
+ err = s.Stmt.BindBool(id, a)
+ case int:
+ err = s.Stmt.BindInt(id, a)
+ case int64:
+ err = s.Stmt.BindInt64(id, a)
+ case float64:
+ err = s.Stmt.BindFloat(id, a)
+ case string:
+ err = s.Stmt.BindText(id, a)
+ case []byte:
+ err = s.Stmt.BindBlob(id, a)
+ case sqlite3.ZeroBlob:
+ err = s.Stmt.BindZeroBlob(id, int64(a))
+ case time.Time:
+ err = s.Stmt.BindTime(id, a, s.tmWrite)
+ case util.JSON:
+ err = s.Stmt.BindJSON(id, a.Value)
+ case util.PointerUnwrap:
+ err = s.Stmt.BindPointer(id, util.UnwrapPointer(a))
+ case nil:
+ err = s.Stmt.BindNull(id)
+ default:
+ panic(util.AssertErr())
+ }
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (s *stmt) CheckNamedValue(arg *driver.NamedValue) error {
+ switch arg.Value.(type) {
+ case bool, int, int64, float64, string, []byte,
+ time.Time, sqlite3.ZeroBlob,
+ util.JSON, util.PointerUnwrap,
+ nil:
+ return nil
+ default:
+ return driver.ErrSkip
+ }
+}
+
+func newResult(c *sqlite3.Conn) driver.Result {
+ rows := c.Changes()
+ if rows != 0 {
+ id := c.LastInsertRowID()
+ if id != 0 {
+ return result{id, rows}
+ }
+ }
+ return resultRowsAffected(rows)
+}
+
+type result struct{ lastInsertId, rowsAffected int64 }
+
+func (r result) LastInsertId() (int64, error) {
+ return r.lastInsertId, nil
+}
+
+func (r result) RowsAffected() (int64, error) {
+ return r.rowsAffected, nil
+}
+
+type resultRowsAffected int64
+
+func (r resultRowsAffected) LastInsertId() (int64, error) {
+ return 0, nil
+}
+
+func (r resultRowsAffected) RowsAffected() (int64, error) {
+ return int64(r), nil
+}
+
+type rows struct {
+ ctx context.Context
+ *stmt
+ names []string
+ types []string
+}
+
+func (r *rows) Close() error {
+ r.Stmt.ClearBindings()
+ return r.Stmt.Reset()
+}
+
+func (r *rows) Columns() []string {
+ if r.names == nil {
+ count := r.Stmt.ColumnCount()
+ r.names = make([]string, count)
+ for i := range r.names {
+ r.names[i] = r.Stmt.ColumnName(i)
+ }
+ }
+ return r.names
+}
+
+func (r *rows) declType(index int) string {
+ if r.types == nil {
+ count := r.Stmt.ColumnCount()
+ r.types = make([]string, count)
+ for i := range r.types {
+ r.types[i] = strings.ToUpper(r.Stmt.ColumnDeclType(i))
+ }
+ }
+ return r.types[index]
+}
+
+func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
+ decltype := r.declType(index)
+ if len := len(decltype); len > 0 && decltype[len-1] == ')' {
+ if i := strings.LastIndexByte(decltype, '('); i >= 0 {
+ decltype = decltype[:i]
+ }
+ }
+ return strings.TrimSpace(decltype)
+}
+
+func (r *rows) Next(dest []driver.Value) error {
+ old := r.Stmt.Conn().SetInterrupt(r.ctx)
+ defer r.Stmt.Conn().SetInterrupt(old)
+
+ if !r.Stmt.Step() {
+ if err := r.Stmt.Err(); err != nil {
+ return err
+ }
+ return io.EOF
+ }
+
+ data := unsafe.Slice((*any)(unsafe.SliceData(dest)), len(dest))
+ err := r.Stmt.Columns(data)
+ for i := range dest {
+ if t, ok := r.decodeTime(i, dest[i]); ok {
+ dest[i] = t
+ continue
+ }
+ if s, ok := dest[i].(string); ok {
+ t, ok := maybeTime(s)
+ if ok {
+ dest[i] = t
+ }
+ }
+ }
+ return err
+}
+
+func (r *rows) decodeTime(i int, v any) (_ time.Time, _ bool) {
+ if r.tmRead == sqlite3.TimeFormatDefault {
+ return
+ }
+ switch r.declType(i) {
+ case "DATE", "TIME", "DATETIME", "TIMESTAMP":
+ // maybe
+ default:
+ return
+ }
+ switch v.(type) {
+ case int64, float64, string:
+ // maybe
+ default:
+ return
+ }
+ t, err := r.tmRead.Decode(v)
+ return t, err == nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/driver/savepoint.go b/vendor/github.com/ncruces/go-sqlite3/driver/savepoint.go
new file mode 100644
index 000000000..60aa6b991
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/driver/savepoint.go
@@ -0,0 +1,27 @@
+package driver
+
+import (
+ "database/sql"
+ "time"
+
+ "github.com/ncruces/go-sqlite3"
+)
+
+// Savepoint establishes a new transaction savepoint.
+//
+// https://sqlite.org/lang_savepoint.html
+func Savepoint(tx *sql.Tx) sqlite3.Savepoint {
+ var ctx saveptCtx
+ tx.ExecContext(&ctx, "")
+ return ctx.Savepoint
+}
+
+type saveptCtx struct{ sqlite3.Savepoint }
+
+func (*saveptCtx) Deadline() (deadline time.Time, ok bool) { return }
+
+func (*saveptCtx) Done() <-chan struct{} { return nil }
+
+func (*saveptCtx) Err() error { return nil }
+
+func (*saveptCtx) Value(key any) any { return nil }
diff --git a/vendor/github.com/ncruces/go-sqlite3/driver/time.go b/vendor/github.com/ncruces/go-sqlite3/driver/time.go
new file mode 100644
index 000000000..630a5b10b
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/driver/time.go
@@ -0,0 +1,31 @@
+package driver
+
+import (
+ "time"
+)
+
+// Convert a string in [time.RFC3339Nano] format into a [time.Time]
+// if it roundtrips back to the same string.
+// This way times can be persisted to, and recovered from, the database,
+// but if a string is needed, [database/sql] will recover the same string.
+func maybeTime(text string) (_ time.Time, _ bool) {
+ // Weed out (some) values that can't possibly be
+ // [time.RFC3339Nano] timestamps.
+ if len(text) < len("2006-01-02T15:04:05Z") {
+ return
+ }
+ if len(text) > len(time.RFC3339Nano) {
+ return
+ }
+ if text[4] != '-' || text[10] != 'T' || text[16] != ':' {
+ return
+ }
+
+ // Slow path.
+ var buf [len(time.RFC3339Nano)]byte
+ date, err := time.Parse(time.RFC3339Nano, text)
+ if err == nil && text == string(date.AppendFormat(buf[:0], time.RFC3339Nano)) {
+ return date, true
+ }
+ return
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/driver/util.go b/vendor/github.com/ncruces/go-sqlite3/driver/util.go
new file mode 100644
index 000000000..033841157
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/driver/util.go
@@ -0,0 +1,14 @@
+package driver
+
+import "database/sql/driver"
+
+func namedValues(args []driver.Value) []driver.NamedValue {
+ named := make([]driver.NamedValue, len(args))
+ for i, v := range args {
+ named[i] = driver.NamedValue{
+ Ordinal: i + 1,
+ Value: v,
+ }
+ }
+ return named
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/README.md b/vendor/github.com/ncruces/go-sqlite3/embed/README.md
new file mode 100644
index 000000000..400fe870a
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/embed/README.md
@@ -0,0 +1,27 @@
+# Embeddable Wasm build of SQLite
+
+This folder includes an embeddable Wasm build of SQLite 3.46.0 for use with
+[`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3).
+
+The following optional features are compiled in:
+- [math functions](https://sqlite.org/lang_mathfunc.html)
+- [FTS5](https://sqlite.org/fts5.html)
+- [JSON](https://sqlite.org/json1.html)
+- [R*Tree](https://sqlite.org/rtree.html)
+- [GeoPoly](https://sqlite.org/geopoly.html)
+- [soundex](https://sqlite.org/lang_corefunc.html#soundex)
+- [stat4](https://sqlite.org/compile.html#enable_stat4)
+- [base64](https://github.com/sqlite/sqlite/blob/master/ext/misc/base64.c)
+- [decimal](https://github.com/sqlite/sqlite/blob/master/ext/misc/decimal.c)
+- [ieee754](https://github.com/sqlite/sqlite/blob/master/ext/misc/ieee754.c)
+- [regexp](https://github.com/sqlite/sqlite/blob/master/ext/misc/regexp.c)
+- [series](https://github.com/sqlite/sqlite/blob/master/ext/misc/series.c)
+- [uint](https://github.com/sqlite/sqlite/blob/master/ext/misc/uint.c)
+- [uuid](https://github.com/sqlite/sqlite/blob/master/ext/misc/uuid.c)
+- [time](../sqlite3/time.c)
+
+See the [configuration options](../sqlite3/sqlite_cfg.h),
+and [patches](../sqlite3) applied.
+
+Built using [`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk),
+and [`binaryen`](https://github.com/WebAssembly/binaryen). \ No newline at end of file
diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/build.sh b/vendor/github.com/ncruces/go-sqlite3/embed/build.sh
new file mode 100644
index 000000000..abe5e60c4
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/embed/build.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+cd -P -- "$(dirname -- "$0")"
+
+ROOT=../
+BINARYEN="$ROOT/tools/binaryen-version_117/bin"
+WASI_SDK="$ROOT/tools/wasi-sdk-22.0/bin"
+
+"$WASI_SDK/clang" --target=wasm32-wasi -std=c17 -flto -g0 -O2 \
+ -Wall -Wextra -Wno-unused-parameter -Wno-unused-function \
+ -o sqlite3.wasm "$ROOT/sqlite3/main.c" \
+ -I"$ROOT/sqlite3" \
+ -mexec-model=reactor \
+ -msimd128 -mmutable-globals \
+ -mbulk-memory -mreference-types \
+ -mnontrapping-fptoint -msign-ext \
+ -fno-stack-protector -fno-stack-clash-protection \
+ -Wl,--initial-memory=327680 \
+ -Wl,--stack-first \
+ -Wl,--import-undefined \
+ -D_HAVE_SQLITE_CONFIG_H \
+ -DSQLITE_CUSTOM_INCLUDE=sqlite_opt.h \
+ $(awk '{print "-Wl,--export="$0}' exports.txt)
+
+trap 'rm -f sqlite3.tmp' EXIT
+"$BINARYEN/wasm-ctor-eval" -g -c _initialize sqlite3.wasm -o sqlite3.tmp
+"$BINARYEN/wasm-opt" -g --strip --strip-producers -c -O3 \
+ sqlite3.tmp -o sqlite3.wasm \
+ --enable-simd --enable-mutable-globals --enable-multivalue \
+ --enable-bulk-memory --enable-reference-types \
+ --enable-nontrapping-float-to-int --enable-sign-ext \ No newline at end of file
diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt b/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt
new file mode 100644
index 000000000..b3cb1581c
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt
@@ -0,0 +1,130 @@
+aligned_alloc
+free
+malloc
+malloc_destructor
+sqlite3_anycollseq_init
+sqlite3_autovacuum_pages_go
+sqlite3_backup_finish
+sqlite3_backup_init
+sqlite3_backup_pagecount
+sqlite3_backup_remaining
+sqlite3_backup_step
+sqlite3_bind_blob64
+sqlite3_bind_double
+sqlite3_bind_int64
+sqlite3_bind_null
+sqlite3_bind_parameter_count
+sqlite3_bind_parameter_index
+sqlite3_bind_parameter_name
+sqlite3_bind_pointer_go
+sqlite3_bind_text64
+sqlite3_bind_value
+sqlite3_bind_zeroblob64
+sqlite3_blob_bytes
+sqlite3_blob_close
+sqlite3_blob_open
+sqlite3_blob_read
+sqlite3_blob_reopen
+sqlite3_blob_write
+sqlite3_busy_handler_go
+sqlite3_busy_timeout
+sqlite3_changes64
+sqlite3_clear_bindings
+sqlite3_close
+sqlite3_close_v2
+sqlite3_collation_needed_go
+sqlite3_column_blob
+sqlite3_column_bytes
+sqlite3_column_count
+sqlite3_column_database_name
+sqlite3_column_decltype
+sqlite3_column_double
+sqlite3_column_int64
+sqlite3_column_name
+sqlite3_column_origin_name
+sqlite3_column_table_name
+sqlite3_column_text
+sqlite3_column_type
+sqlite3_column_value
+sqlite3_columns_go
+sqlite3_commit_hook_go
+sqlite3_config_log_go
+sqlite3_create_aggregate_function_go
+sqlite3_create_collation_go
+sqlite3_create_function_go
+sqlite3_create_module_go
+sqlite3_create_window_function_go
+sqlite3_database_file_object
+sqlite3_db_config
+sqlite3_db_filename
+sqlite3_db_name
+sqlite3_db_readonly
+sqlite3_db_release_memory
+sqlite3_declare_vtab
+sqlite3_errcode
+sqlite3_errmsg
+sqlite3_error_offset
+sqlite3_errstr
+sqlite3_exec
+sqlite3_filename_database
+sqlite3_filename_journal
+sqlite3_filename_wal
+sqlite3_finalize
+sqlite3_get_autocommit
+sqlite3_get_auxdata
+sqlite3_interrupt
+sqlite3_last_insert_rowid
+sqlite3_limit
+sqlite3_open_v2
+sqlite3_overload_function
+sqlite3_prepare_v3
+sqlite3_progress_handler_go
+sqlite3_reset
+sqlite3_result_blob64
+sqlite3_result_double
+sqlite3_result_error
+sqlite3_result_error_code
+sqlite3_result_error_nomem
+sqlite3_result_error_toobig
+sqlite3_result_int64
+sqlite3_result_null
+sqlite3_result_pointer_go
+sqlite3_result_text64
+sqlite3_result_value
+sqlite3_result_zeroblob64
+sqlite3_rollback_hook_go
+sqlite3_set_authorizer_go
+sqlite3_set_auxdata_go
+sqlite3_set_last_insert_rowid
+sqlite3_step
+sqlite3_stmt_busy
+sqlite3_stmt_readonly
+sqlite3_stmt_status
+sqlite3_total_changes64
+sqlite3_txn_state
+sqlite3_update_hook_go
+sqlite3_uri_key
+sqlite3_uri_parameter
+sqlite3_value_blob
+sqlite3_value_bytes
+sqlite3_value_double
+sqlite3_value_dup
+sqlite3_value_free
+sqlite3_value_int64
+sqlite3_value_nochange
+sqlite3_value_numeric_type
+sqlite3_value_pointer_go
+sqlite3_value_text
+sqlite3_value_type
+sqlite3_vtab_collation
+sqlite3_vtab_config_go
+sqlite3_vtab_distinct
+sqlite3_vtab_in
+sqlite3_vtab_in_first
+sqlite3_vtab_in_next
+sqlite3_vtab_nochange
+sqlite3_vtab_on_conflict
+sqlite3_vtab_rhs_value
+sqlite3_wal_autocheckpoint
+sqlite3_wal_checkpoint_v2
+sqlite3_wal_hook_go \ No newline at end of file
diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/init.go b/vendor/github.com/ncruces/go-sqlite3/embed/init.go
new file mode 100644
index 000000000..da527abd0
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/embed/init.go
@@ -0,0 +1,20 @@
+// Package embed embeds SQLite into your application.
+//
+// Importing package embed initializes the [sqlite3.Binary] variable
+// with an appropriate build of SQLite:
+//
+// import _ "github.com/ncruces/go-sqlite3/embed"
+package embed
+
+import (
+ _ "embed"
+
+ "github.com/ncruces/go-sqlite3"
+)
+
+//go:embed sqlite3.wasm
+var binary []byte
+
+func init() {
+ sqlite3.Binary = binary
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm b/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm
new file mode 100644
index 000000000..2689f773a
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm
Binary files differ
diff --git a/vendor/github.com/ncruces/go-sqlite3/error.go b/vendor/github.com/ncruces/go-sqlite3/error.go
new file mode 100644
index 000000000..71238ef12
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/error.go
@@ -0,0 +1,162 @@
+package sqlite3
+
+import (
+ "errors"
+ "strconv"
+ "strings"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+// Error wraps an SQLite Error Code.
+//
+// https://sqlite.org/c3ref/errcode.html
+type Error struct {
+ str string
+ msg string
+ sql string
+ code uint64
+}
+
+// Code returns the primary error code for this error.
+//
+// https://sqlite.org/rescode.html
+func (e *Error) Code() ErrorCode {
+ return ErrorCode(e.code)
+}
+
+// ExtendedCode returns the extended error code for this error.
+//
+// https://sqlite.org/rescode.html
+func (e *Error) ExtendedCode() ExtendedErrorCode {
+ return ExtendedErrorCode(e.code)
+}
+
+// Error implements the error interface.
+func (e *Error) Error() string {
+ var b strings.Builder
+ b.WriteString("sqlite3: ")
+
+ if e.str != "" {
+ b.WriteString(e.str)
+ } else {
+ b.WriteString(strconv.Itoa(int(e.code)))
+ }
+
+ if e.msg != "" {
+ b.WriteString(": ")
+ b.WriteString(e.msg)
+ }
+
+ return b.String()
+}
+
+// Is tests whether this error matches a given [ErrorCode] or [ExtendedErrorCode].
+//
+// It makes it possible to do:
+//
+// if errors.Is(err, sqlite3.BUSY) {
+// // ... handle BUSY
+// }
+func (e *Error) Is(err error) bool {
+ switch c := err.(type) {
+ case ErrorCode:
+ return c == e.Code()
+ case ExtendedErrorCode:
+ return c == e.ExtendedCode()
+ }
+ return false
+}
+
+// As converts this error to an [ErrorCode] or [ExtendedErrorCode].
+func (e *Error) As(err any) bool {
+ switch c := err.(type) {
+ case *ErrorCode:
+ *c = e.Code()
+ return true
+ case *ExtendedErrorCode:
+ *c = e.ExtendedCode()
+ return true
+ }
+ return false
+}
+
+// Temporary returns true for [BUSY] errors.
+func (e *Error) Temporary() bool {
+ return e.Code() == BUSY
+}
+
+// Timeout returns true for [BUSY_TIMEOUT] errors.
+func (e *Error) Timeout() bool {
+ return e.ExtendedCode() == BUSY_TIMEOUT
+}
+
+// SQL returns the SQL starting at the token that triggered a syntax error.
+func (e *Error) SQL() string {
+ return e.sql
+}
+
+// Error implements the error interface.
+func (e ErrorCode) Error() string {
+ return util.ErrorCodeString(uint32(e))
+}
+
+// Temporary returns true for [BUSY] errors.
+func (e ErrorCode) Temporary() bool {
+ return e == BUSY
+}
+
+// Error implements the error interface.
+func (e ExtendedErrorCode) Error() string {
+ return util.ErrorCodeString(uint32(e))
+}
+
+// Is tests whether this error matches a given [ErrorCode].
+func (e ExtendedErrorCode) Is(err error) bool {
+ c, ok := err.(ErrorCode)
+ return ok && c == ErrorCode(e)
+}
+
+// As converts this error to an [ErrorCode].
+func (e ExtendedErrorCode) As(err any) bool {
+ c, ok := err.(*ErrorCode)
+ if ok {
+ *c = ErrorCode(e)
+ }
+ return ok
+}
+
+// Temporary returns true for [BUSY] errors.
+func (e ExtendedErrorCode) Temporary() bool {
+ return ErrorCode(e) == BUSY
+}
+
+// Timeout returns true for [BUSY_TIMEOUT] errors.
+func (e ExtendedErrorCode) Timeout() bool {
+ return e == BUSY_TIMEOUT
+}
+
+func errorCode(err error, def ErrorCode) (msg string, code uint32) {
+ switch code := err.(type) {
+ case nil:
+ return "", _OK
+ case ErrorCode:
+ return "", uint32(code)
+ case xErrorCode:
+ return "", uint32(code)
+ case *Error:
+ return code.msg, uint32(code.code)
+ }
+
+ var ecode ErrorCode
+ var xcode xErrorCode
+ switch {
+ case errors.As(err, &xcode):
+ code = uint32(xcode)
+ case errors.As(err, &ecode):
+ code = uint32(ecode)
+ default:
+ code = uint32(def)
+ }
+ return err.Error(), code
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/func.go b/vendor/github.com/ncruces/go-sqlite3/func.go
new file mode 100644
index 000000000..255584a43
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/func.go
@@ -0,0 +1,214 @@
+package sqlite3
+
+import (
+ "context"
+ "sync"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/tetratelabs/wazero/api"
+)
+
+// CollationNeeded registers a callback to be invoked
+// whenever an unknown collation sequence is required.
+//
+// https://sqlite.org/c3ref/collation_needed.html
+func (c *Conn) CollationNeeded(cb func(db *Conn, name string)) error {
+ var enable uint64
+ if cb != nil {
+ enable = 1
+ }
+ r := c.call("sqlite3_collation_needed_go", uint64(c.handle), enable)
+ if err := c.error(r); err != nil {
+ return err
+ }
+ c.collation = cb
+ return nil
+}
+
+// AnyCollationNeeded uses [Conn.CollationNeeded] to register
+// a fake collating function for any unknown collating sequence.
+// The fake collating function works like BINARY.
+//
+// This can be used to load schemas that contain
+// one or more unknown collating sequences.
+func (c *Conn) AnyCollationNeeded() {
+ c.call("sqlite3_anycollseq_init", uint64(c.handle), 0, 0)
+}
+
+// CreateCollation defines a new collating sequence.
+//
+// https://sqlite.org/c3ref/create_collation.html
+func (c *Conn) CreateCollation(name string, fn func(a, b []byte) int) error {
+ defer c.arena.mark()()
+ namePtr := c.arena.string(name)
+ funcPtr := util.AddHandle(c.ctx, fn)
+ r := c.call("sqlite3_create_collation_go",
+ uint64(c.handle), uint64(namePtr), uint64(funcPtr))
+ return c.error(r)
+}
+
+// CreateFunction defines a new scalar SQL function.
+//
+// https://sqlite.org/c3ref/create_function.html
+func (c *Conn) CreateFunction(name string, nArg int, flag FunctionFlag, fn ScalarFunction) error {
+ defer c.arena.mark()()
+ namePtr := c.arena.string(name)
+ funcPtr := util.AddHandle(c.ctx, fn)
+ r := c.call("sqlite3_create_function_go",
+ uint64(c.handle), uint64(namePtr), uint64(nArg),
+ uint64(flag), uint64(funcPtr))
+ return c.error(r)
+}
+
+// ScalarFunction is the type of a scalar SQL function.
+// Implementations must not retain arg.
+type ScalarFunction func(ctx Context, arg ...Value)
+
+// CreateWindowFunction defines a new aggregate or aggregate window SQL function.
+// If fn returns a [WindowFunction], then an aggregate window function is created.
+// If fn returns an [io.Closer], it will be called to free resources.
+//
+// https://sqlite.org/c3ref/create_function.html
+func (c *Conn) CreateWindowFunction(name string, nArg int, flag FunctionFlag, fn func() AggregateFunction) error {
+ defer c.arena.mark()()
+ call := "sqlite3_create_aggregate_function_go"
+ namePtr := c.arena.string(name)
+ funcPtr := util.AddHandle(c.ctx, fn)
+ if _, ok := fn().(WindowFunction); ok {
+ call = "sqlite3_create_window_function_go"
+ }
+ r := c.call(call,
+ uint64(c.handle), uint64(namePtr), uint64(nArg),
+ uint64(flag), uint64(funcPtr))
+ return c.error(r)
+}
+
+// AggregateFunction is the interface an aggregate function should implement.
+//
+// https://sqlite.org/appfunc.html
+type AggregateFunction interface {
+ // Step is invoked to add a row to the current window.
+ // The function arguments, if any, corresponding to the row being added, are passed to Step.
+ // Implementations must not retain arg.
+ Step(ctx Context, arg ...Value)
+
+ // Value is invoked to return the current (or final) value of the aggregate.
+ Value(ctx Context)
+}
+
+// WindowFunction is the interface an aggregate window function should implement.
+//
+// https://sqlite.org/windowfunctions.html
+type WindowFunction interface {
+ AggregateFunction
+
+ // Inverse is invoked to remove the oldest presently aggregated result of Step from the current window.
+ // The function arguments, if any, are those passed to Step for the row being removed.
+ // Implementations must not retain arg.
+ Inverse(ctx Context, arg ...Value)
+}
+
+// OverloadFunction overloads a function for a virtual table.
+//
+// https://sqlite.org/c3ref/overload_function.html
+func (c *Conn) OverloadFunction(name string, nArg int) error {
+ defer c.arena.mark()()
+ namePtr := c.arena.string(name)
+ r := c.call("sqlite3_overload_function",
+ uint64(c.handle), uint64(namePtr), uint64(nArg))
+ return c.error(r)
+}
+
+func destroyCallback(ctx context.Context, mod api.Module, pApp uint32) {
+ util.DelHandle(ctx, pApp)
+}
+
+func collationCallback(ctx context.Context, mod api.Module, pArg, pDB, eTextRep, zName uint32) {
+ if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.collation != nil {
+ name := util.ReadString(mod, zName, _MAX_NAME)
+ c.collation(c, name)
+ }
+}
+
+func compareCallback(ctx context.Context, mod api.Module, pApp, nKey1, pKey1, nKey2, pKey2 uint32) uint32 {
+ fn := util.GetHandle(ctx, pApp).(func(a, b []byte) int)
+ return uint32(fn(util.View(mod, pKey1, uint64(nKey1)), util.View(mod, pKey2, uint64(nKey2))))
+}
+
+func funcCallback(ctx context.Context, mod api.Module, pCtx, pApp, nArg, pArg uint32) {
+ args := getFuncArgs()
+ defer putFuncArgs(args)
+ db := ctx.Value(connKey{}).(*Conn)
+ fn := util.GetHandle(db.ctx, pApp).(ScalarFunction)
+ callbackArgs(db, args[:nArg], pArg)
+ fn(Context{db, pCtx}, args[:nArg]...)
+}
+
+func stepCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp, nArg, pArg uint32) {
+ args := getFuncArgs()
+ defer putFuncArgs(args)
+ db := ctx.Value(connKey{}).(*Conn)
+ callbackArgs(db, args[:nArg], pArg)
+ fn, _ := callbackAggregate(db, pAgg, pApp)
+ fn.Step(Context{db, pCtx}, args[:nArg]...)
+}
+
+func finalCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp uint32) {
+ db := ctx.Value(connKey{}).(*Conn)
+ fn, handle := callbackAggregate(db, pAgg, pApp)
+ fn.Value(Context{db, pCtx})
+ util.DelHandle(ctx, handle)
+}
+
+func valueCallback(ctx context.Context, mod api.Module, pCtx, pAgg uint32) {
+ db := ctx.Value(connKey{}).(*Conn)
+ fn := util.GetHandle(db.ctx, pAgg).(AggregateFunction)
+ fn.Value(Context{db, pCtx})
+}
+
+func inverseCallback(ctx context.Context, mod api.Module, pCtx, pAgg, nArg, pArg uint32) {
+ args := getFuncArgs()
+ defer putFuncArgs(args)
+ db := ctx.Value(connKey{}).(*Conn)
+ callbackArgs(db, args[:nArg], pArg)
+ fn := util.GetHandle(db.ctx, pAgg).(WindowFunction)
+ fn.Inverse(Context{db, pCtx}, args[:nArg]...)
+}
+
+func callbackAggregate(db *Conn, pAgg, pApp uint32) (AggregateFunction, uint32) {
+ if pApp == 0 {
+ handle := util.ReadUint32(db.mod, pAgg)
+ return util.GetHandle(db.ctx, handle).(AggregateFunction), handle
+ }
+
+ // We need to create the aggregate.
+ fn := util.GetHandle(db.ctx, pApp).(func() AggregateFunction)()
+ handle := util.AddHandle(db.ctx, fn)
+ if pAgg != 0 {
+ util.WriteUint32(db.mod, pAgg, handle)
+ }
+ return fn, handle
+}
+
+func callbackArgs(db *Conn, arg []Value, pArg uint32) {
+ for i := range arg {
+ arg[i] = Value{
+ c: db,
+ handle: util.ReadUint32(db.mod, pArg+ptrlen*uint32(i)),
+ }
+ }
+}
+
+var funcArgsPool sync.Pool
+
+func putFuncArgs(p *[_MAX_FUNCTION_ARG]Value) {
+ funcArgsPool.Put(p)
+}
+
+func getFuncArgs() *[_MAX_FUNCTION_ARG]Value {
+ if p := funcArgsPool.Get(); p == nil {
+ return new([_MAX_FUNCTION_ARG]Value)
+ } else {
+ return p.(*[_MAX_FUNCTION_ARG]Value)
+ }
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/go.work b/vendor/github.com/ncruces/go-sqlite3/go.work
new file mode 100644
index 000000000..18e378592
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/go.work
@@ -0,0 +1,6 @@
+go 1.21
+
+use (
+ .
+ ./gormlite
+)
diff --git a/vendor/github.com/ncruces/go-sqlite3/go.work.sum b/vendor/github.com/ncruces/go-sqlite3/go.work.sum
new file mode 100644
index 000000000..4deb7b7f3
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/go.work.sum
@@ -0,0 +1,9 @@
+golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
+golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
+golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_other.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_other.go
new file mode 100644
index 000000000..ba16efc02
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_other.go
@@ -0,0 +1,9 @@
+//go:build !(unix || windows) || sqlite3_nosys
+
+package util
+
+import "github.com/tetratelabs/wazero/experimental"
+
+func virtualAlloc(cap, max uint64) experimental.LinearMemory {
+ return sliceAlloc(cap, max)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_slice.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_slice.go
new file mode 100644
index 000000000..b8cc1453c
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_slice.go
@@ -0,0 +1,25 @@
+//go:build !(darwin || linux) || !(amd64 || arm64 || riscv64) || sqlite3_noshm || sqlite3_nosys
+
+package util
+
+import "github.com/tetratelabs/wazero/experimental"
+
+func sliceAlloc(cap, max uint64) experimental.LinearMemory {
+ return &sliceBuffer{make([]byte, cap), max}
+}
+
+type sliceBuffer struct {
+ buf []byte
+ max uint64
+}
+
+func (b *sliceBuffer) Free() {}
+
+func (b *sliceBuffer) Reallocate(size uint64) []byte {
+ if cap := uint64(cap(b.buf)); size > cap {
+ b.buf = append(b.buf[:cap], make([]byte, size-cap)...)
+ } else {
+ b.buf = b.buf[:size]
+ }
+ return b.buf
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_unix.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_unix.go
new file mode 100644
index 000000000..2b1d3916b
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_unix.go
@@ -0,0 +1,67 @@
+//go:build unix && !sqlite3_nosys
+
+package util
+
+import (
+ "math"
+
+ "github.com/tetratelabs/wazero/experimental"
+ "golang.org/x/sys/unix"
+)
+
+func virtualAlloc(cap, max uint64) experimental.LinearMemory {
+ // Round up to the page size.
+ rnd := uint64(unix.Getpagesize() - 1)
+ max = (max + rnd) &^ rnd
+
+ if max > math.MaxInt {
+ // This ensures int(max) overflows to a negative value,
+ // and unix.Mmap returns EINVAL.
+ max = math.MaxUint64
+ }
+
+ // Reserve max bytes of address space, to ensure we won't need to move it.
+ // A protected, private, anonymous mapping should not commit memory.
+ b, err := unix.Mmap(-1, 0, int(max), unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANON)
+ if err != nil {
+ panic(err)
+ }
+ return &mmappedMemory{buf: b[:0]}
+}
+
+// The slice covers the entire mmapped memory:
+// - len(buf) is the already committed memory,
+// - cap(buf) is the reserved address space.
+type mmappedMemory struct {
+ buf []byte
+}
+
+func (m *mmappedMemory) Reallocate(size uint64) []byte {
+ com := uint64(len(m.buf))
+ res := uint64(cap(m.buf))
+ if com < size && size < res {
+ // Round up to the page size.
+ rnd := uint64(unix.Getpagesize() - 1)
+ new := (size + rnd) &^ rnd
+
+ // Commit additional memory up to new bytes.
+ err := unix.Mprotect(m.buf[com:new], unix.PROT_READ|unix.PROT_WRITE)
+ if err != nil {
+ panic(err)
+ }
+
+ // Update committed memory.
+ m.buf = m.buf[:new]
+ }
+ // Limit returned capacity because bytes beyond
+ // len(m.buf) have not yet been committed.
+ return m.buf[:size:len(m.buf)]
+}
+
+func (m *mmappedMemory) Free() {
+ err := unix.Munmap(m.buf[:cap(m.buf)])
+ if err != nil {
+ panic(err)
+ }
+ m.buf = nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_windows.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_windows.go
new file mode 100644
index 000000000..8936173b4
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/alloc_windows.go
@@ -0,0 +1,76 @@
+//go:build !sqlite3_nosys
+
+package util
+
+import (
+ "math"
+ "reflect"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/experimental"
+ "golang.org/x/sys/windows"
+)
+
+func virtualAlloc(cap, max uint64) experimental.LinearMemory {
+ // Round up to the page size.
+ rnd := uint64(windows.Getpagesize() - 1)
+ max = (max + rnd) &^ rnd
+
+ if max > math.MaxInt {
+ // This ensures uintptr(max) overflows to a large value,
+ // and windows.VirtualAlloc returns an error.
+ max = math.MaxUint64
+ }
+
+ // Reserve max bytes of address space, to ensure we won't need to move it.
+ // This does not commit memory.
+ r, err := windows.VirtualAlloc(0, uintptr(max), windows.MEM_RESERVE, windows.PAGE_READWRITE)
+ if err != nil {
+ panic(err)
+ }
+
+ mem := virtualMemory{addr: r}
+ // SliceHeader, although deprecated, avoids a go vet warning.
+ sh := (*reflect.SliceHeader)(unsafe.Pointer(&mem.buf))
+ sh.Cap = int(max) // Not a bug.
+ sh.Data = r
+ return &mem
+}
+
+// The slice covers the entire mmapped memory:
+// - len(buf) is the already committed memory,
+// - cap(buf) is the reserved address space.
+type virtualMemory struct {
+ buf []byte
+ addr uintptr
+}
+
+func (m *virtualMemory) Reallocate(size uint64) []byte {
+ com := uint64(len(m.buf))
+ res := uint64(cap(m.buf))
+ if com < size && size < res {
+ // Round up to the page size.
+ rnd := uint64(windows.Getpagesize() - 1)
+ new := (size + rnd) &^ rnd
+
+ // Commit additional memory up to new bytes.
+ _, err := windows.VirtualAlloc(m.addr, uintptr(new), windows.MEM_COMMIT, windows.PAGE_READWRITE)
+ if err != nil {
+ panic(err)
+ }
+
+ // Update committed memory.
+ m.buf = m.buf[:new]
+ }
+ // Limit returned capacity because bytes beyond
+ // len(m.buf) have not yet been committed.
+ return m.buf[:size:len(m.buf)]
+}
+
+func (m *virtualMemory) Free() {
+ err := windows.VirtualFree(m.addr, 0, windows.MEM_RELEASE)
+ if err != nil {
+ panic(err)
+ }
+ m.addr = 0
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/bool.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/bool.go
new file mode 100644
index 000000000..8427f3085
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/bool.go
@@ -0,0 +1,22 @@
+package util
+
+import "strings"
+
+func ParseBool(s string) (b, ok bool) {
+ if len(s) == 0 {
+ return false, false
+ }
+ if s[0] == '0' {
+ return false, true
+ }
+ if '1' <= s[0] && s[0] <= '9' {
+ return true, true
+ }
+ switch strings.ToLower(s) {
+ case "true", "yes", "on":
+ return true, true
+ case "false", "no", "off":
+ return false, true
+ }
+ return false, false
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/const.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/const.go
new file mode 100644
index 000000000..86bb9749d
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/const.go
@@ -0,0 +1,117 @@
+package util
+
+// https://sqlite.com/matrix/rescode.html
+const (
+ OK = 0 /* Successful result */
+
+ ERROR = 1 /* Generic error */
+ INTERNAL = 2 /* Internal logic error in SQLite */
+ PERM = 3 /* Access permission denied */
+ ABORT = 4 /* Callback routine requested an abort */
+ BUSY = 5 /* The database file is locked */
+ LOCKED = 6 /* A table in the database is locked */
+ NOMEM = 7 /* A malloc() failed */
+ READONLY = 8 /* Attempt to write a readonly database */
+ INTERRUPT = 9 /* Operation terminated by sqlite3_interrupt() */
+ IOERR = 10 /* Some kind of disk I/O error occurred */
+ CORRUPT = 11 /* The database disk image is malformed */
+ NOTFOUND = 12 /* Unknown opcode in sqlite3_file_control() */
+ FULL = 13 /* Insertion failed because database is full */
+ CANTOPEN = 14 /* Unable to open the database file */
+ PROTOCOL = 15 /* Database lock protocol error */
+ EMPTY = 16 /* Internal use only */
+ SCHEMA = 17 /* The database schema changed */
+ TOOBIG = 18 /* String or BLOB exceeds size limit */
+ CONSTRAINT = 19 /* Abort due to constraint violation */
+ MISMATCH = 20 /* Data type mismatch */
+ MISUSE = 21 /* Library used incorrectly */
+ NOLFS = 22 /* Uses OS features not supported on host */
+ AUTH = 23 /* Authorization denied */
+ FORMAT = 24 /* Not used */
+ RANGE = 25 /* 2nd parameter to sqlite3_bind out of range */
+ NOTADB = 26 /* File opened that is not a database file */
+ NOTICE = 27 /* Notifications from sqlite3_log() */
+ WARNING = 28 /* Warnings from sqlite3_log() */
+
+ ROW = 100 /* sqlite3_step() has another row ready */
+ DONE = 101 /* sqlite3_step() has finished executing */
+
+ ERROR_MISSING_COLLSEQ = ERROR | (1 << 8)
+ ERROR_RETRY = ERROR | (2 << 8)
+ ERROR_SNAPSHOT = ERROR | (3 << 8)
+ IOERR_READ = IOERR | (1 << 8)
+ IOERR_SHORT_READ = IOERR | (2 << 8)
+ IOERR_WRITE = IOERR | (3 << 8)
+ IOERR_FSYNC = IOERR | (4 << 8)
+ IOERR_DIR_FSYNC = IOERR | (5 << 8)
+ IOERR_TRUNCATE = IOERR | (6 << 8)
+ IOERR_FSTAT = IOERR | (7 << 8)
+ IOERR_UNLOCK = IOERR | (8 << 8)
+ IOERR_RDLOCK = IOERR | (9 << 8)
+ IOERR_DELETE = IOERR | (10 << 8)
+ IOERR_BLOCKED = IOERR | (11 << 8)
+ IOERR_NOMEM = IOERR | (12 << 8)
+ IOERR_ACCESS = IOERR | (13 << 8)
+ IOERR_CHECKRESERVEDLOCK = IOERR | (14 << 8)
+ IOERR_LOCK = IOERR | (15 << 8)
+ IOERR_CLOSE = IOERR | (16 << 8)
+ IOERR_DIR_CLOSE = IOERR | (17 << 8)
+ IOERR_SHMOPEN = IOERR | (18 << 8)
+ IOERR_SHMSIZE = IOERR | (19 << 8)
+ IOERR_SHMLOCK = IOERR | (20 << 8)
+ IOERR_SHMMAP = IOERR | (21 << 8)
+ IOERR_SEEK = IOERR | (22 << 8)
+ IOERR_DELETE_NOENT = IOERR | (23 << 8)
+ IOERR_MMAP = IOERR | (24 << 8)
+ IOERR_GETTEMPPATH = IOERR | (25 << 8)
+ IOERR_CONVPATH = IOERR | (26 << 8)
+ IOERR_VNODE = IOERR | (27 << 8)
+ IOERR_AUTH = IOERR | (28 << 8)
+ IOERR_BEGIN_ATOMIC = IOERR | (29 << 8)
+ IOERR_COMMIT_ATOMIC = IOERR | (30 << 8)
+ IOERR_ROLLBACK_ATOMIC = IOERR | (31 << 8)
+ IOERR_DATA = IOERR | (32 << 8)
+ IOERR_CORRUPTFS = IOERR | (33 << 8)
+ IOERR_IN_PAGE = IOERR | (34 << 8)
+ LOCKED_SHAREDCACHE = LOCKED | (1 << 8)
+ LOCKED_VTAB = LOCKED | (2 << 8)
+ BUSY_RECOVERY = BUSY | (1 << 8)
+ BUSY_SNAPSHOT = BUSY | (2 << 8)
+ BUSY_TIMEOUT = BUSY | (3 << 8)
+ CANTOPEN_NOTEMPDIR = CANTOPEN | (1 << 8)
+ CANTOPEN_ISDIR = CANTOPEN | (2 << 8)
+ CANTOPEN_FULLPATH = CANTOPEN | (3 << 8)
+ CANTOPEN_CONVPATH = CANTOPEN | (4 << 8)
+ CANTOPEN_DIRTYWAL = CANTOPEN | (5 << 8) /* Not Used */
+ CANTOPEN_SYMLINK = CANTOPEN | (6 << 8)
+ CORRUPT_VTAB = CORRUPT | (1 << 8)
+ CORRUPT_SEQUENCE = CORRUPT | (2 << 8)
+ CORRUPT_INDEX = CORRUPT | (3 << 8)
+ READONLY_RECOVERY = READONLY | (1 << 8)
+ READONLY_CANTLOCK = READONLY | (2 << 8)
+ READONLY_ROLLBACK = READONLY | (3 << 8)
+ READONLY_DBMOVED = READONLY | (4 << 8)
+ READONLY_CANTINIT = READONLY | (5 << 8)
+ READONLY_DIRECTORY = READONLY | (6 << 8)
+ ABORT_ROLLBACK = ABORT | (2 << 8)
+ CONSTRAINT_CHECK = CONSTRAINT | (1 << 8)
+ CONSTRAINT_COMMITHOOK = CONSTRAINT | (2 << 8)
+ CONSTRAINT_FOREIGNKEY = CONSTRAINT | (3 << 8)
+ CONSTRAINT_FUNCTION = CONSTRAINT | (4 << 8)
+ CONSTRAINT_NOTNULL = CONSTRAINT | (5 << 8)
+ CONSTRAINT_PRIMARYKEY = CONSTRAINT | (6 << 8)
+ CONSTRAINT_TRIGGER = CONSTRAINT | (7 << 8)
+ CONSTRAINT_UNIQUE = CONSTRAINT | (8 << 8)
+ CONSTRAINT_VTAB = CONSTRAINT | (9 << 8)
+ CONSTRAINT_ROWID = CONSTRAINT | (10 << 8)
+ CONSTRAINT_PINNED = CONSTRAINT | (11 << 8)
+ CONSTRAINT_DATATYPE = CONSTRAINT | (12 << 8)
+ NOTICE_RECOVER_WAL = NOTICE | (1 << 8)
+ NOTICE_RECOVER_ROLLBACK = NOTICE | (2 << 8)
+ NOTICE_RBU = NOTICE | (3 << 8)
+ WARNING_AUTOINDEX = WARNING | (1 << 8)
+ AUTH_USER = AUTH | (1 << 8)
+
+ OK_LOAD_PERMANENTLY = OK | (1 << 8)
+ OK_SYMLINK = OK | (2 << 8) /* internal use only */
+)
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/error.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/error.go
new file mode 100644
index 000000000..1f5555fd3
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/error.go
@@ -0,0 +1,106 @@
+package util
+
+import (
+ "runtime"
+ "strconv"
+)
+
+type ErrorString string
+
+func (e ErrorString) Error() string { return string(e) }
+
+const (
+ NilErr = ErrorString("sqlite3: invalid memory address or null pointer dereference")
+ OOMErr = ErrorString("sqlite3: out of memory")
+ RangeErr = ErrorString("sqlite3: index out of range")
+ NoNulErr = ErrorString("sqlite3: missing NUL terminator")
+ NoBinaryErr = ErrorString("sqlite3: no SQLite binary embed/set/loaded")
+ BadBinaryErr = ErrorString("sqlite3: invalid SQLite binary embed/set/loaded")
+ TimeErr = ErrorString("sqlite3: invalid time value")
+ WhenceErr = ErrorString("sqlite3: invalid whence")
+ OffsetErr = ErrorString("sqlite3: invalid offset")
+ TailErr = ErrorString("sqlite3: multiple statements")
+ IsolationErr = ErrorString("sqlite3: unsupported isolation level")
+ ValueErr = ErrorString("sqlite3: unsupported value")
+ NoVFSErr = ErrorString("sqlite3: no such vfs: ")
+)
+
+func AssertErr() ErrorString {
+ msg := "sqlite3: assertion failed"
+ if _, file, line, ok := runtime.Caller(1); ok {
+ msg += " (" + file + ":" + strconv.Itoa(line) + ")"
+ }
+ return ErrorString(msg)
+}
+
+func ErrorCodeString(rc uint32) string {
+ switch rc {
+ case ABORT_ROLLBACK:
+ return "sqlite3: abort due to ROLLBACK"
+ case ROW:
+ return "sqlite3: another row available"
+ case DONE:
+ return "sqlite3: no more rows available"
+ }
+ switch rc & 0xff {
+ case OK:
+ return "sqlite3: not an error"
+ case ERROR:
+ return "sqlite3: SQL logic error"
+ case INTERNAL:
+ break
+ case PERM:
+ return "sqlite3: access permission denied"
+ case ABORT:
+ return "sqlite3: query aborted"
+ case BUSY:
+ return "sqlite3: database is locked"
+ case LOCKED:
+ return "sqlite3: database table is locked"
+ case NOMEM:
+ return "sqlite3: out of memory"
+ case READONLY:
+ return "sqlite3: attempt to write a readonly database"
+ case INTERRUPT:
+ return "sqlite3: interrupted"
+ case IOERR:
+ return "sqlite3: disk I/O error"
+ case CORRUPT:
+ return "sqlite3: database disk image is malformed"
+ case NOTFOUND:
+ return "sqlite3: unknown operation"
+ case FULL:
+ return "sqlite3: database or disk is full"
+ case CANTOPEN:
+ return "sqlite3: unable to open database file"
+ case PROTOCOL:
+ return "sqlite3: locking protocol"
+ case FORMAT:
+ break
+ case SCHEMA:
+ return "sqlite3: database schema has changed"
+ case TOOBIG:
+ return "sqlite3: string or blob too big"
+ case CONSTRAINT:
+ return "sqlite3: constraint failed"
+ case MISMATCH:
+ return "sqlite3: datatype mismatch"
+ case MISUSE:
+ return "sqlite3: bad parameter or other API misuse"
+ case NOLFS:
+ break
+ case AUTH:
+ return "sqlite3: authorization denied"
+ case EMPTY:
+ break
+ case RANGE:
+ return "sqlite3: column index out of range"
+ case NOTADB:
+ return "sqlite3: file is not a database"
+ case NOTICE:
+ return "sqlite3: notification message"
+ case WARNING:
+ return "sqlite3: warning message"
+ }
+ return "sqlite3: unknown error"
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/func.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/func.go
new file mode 100644
index 000000000..be7a47c2f
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/func.go
@@ -0,0 +1,193 @@
+package util
+
+import (
+ "context"
+
+ "github.com/tetratelabs/wazero"
+ "github.com/tetratelabs/wazero/api"
+)
+
+type i32 interface{ ~int32 | ~uint32 }
+type i64 interface{ ~int64 | ~uint64 }
+
+type funcVI[T0 i32] func(context.Context, api.Module, T0)
+
+func (fn funcVI[T0]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ fn(ctx, mod, T0(stack[0]))
+}
+
+func ExportFuncVI[T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0)) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcVI[T0](fn),
+ []api.ValueType{api.ValueTypeI32}, nil).
+ Export(name)
+}
+
+type funcVII[T0, T1 i32] func(context.Context, api.Module, T0, T1)
+
+func (fn funcVII[T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ fn(ctx, mod, T0(stack[0]), T1(stack[1]))
+}
+
+func ExportFuncVII[T0, T1 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1)) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcVII[T0, T1](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, nil).
+ Export(name)
+}
+
+type funcVIII[T0, T1, T2 i32] func(context.Context, api.Module, T0, T1, T2)
+
+func (fn funcVIII[T0, T1, T2]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]))
+}
+
+func ExportFuncVIII[T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2)) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcVIII[T0, T1, T2](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil).
+ Export(name)
+}
+
+type funcVIIII[T0, T1, T2, T3 i32] func(context.Context, api.Module, T0, T1, T2, T3)
+
+func (fn funcVIIII[T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]))
+}
+
+func ExportFuncVIIII[T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3)) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcVIIII[T0, T1, T2, T3](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil).
+ Export(name)
+}
+
+type funcVIIIII[T0, T1, T2, T3, T4 i32] func(context.Context, api.Module, T0, T1, T2, T3, T4)
+
+func (fn funcVIIIII[T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]))
+}
+
+func ExportFuncVIIIII[T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4)) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcVIIIII[T0, T1, T2, T3, T4](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, nil).
+ Export(name)
+}
+
+type funcVIIIIJ[T0, T1, T2, T3 i32, T4 i64] func(context.Context, api.Module, T0, T1, T2, T3, T4)
+
+func (fn funcVIIIIJ[T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]))
+}
+
+func ExportFuncVIIIIJ[T0, T1, T2, T3 i32, T4 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4)) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcVIIIIJ[T0, T1, T2, T3, T4](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI64}, nil).
+ Export(name)
+}
+
+type funcII[TR, T0 i32] func(context.Context, api.Module, T0) TR
+
+func (fn funcII[TR, T0]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ stack[0] = uint64(fn(ctx, mod, T0(stack[0])))
+}
+
+func ExportFuncII[TR, T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0) TR) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcII[TR, T0](fn),
+ []api.ValueType{api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
+ Export(name)
+}
+
+type funcIII[TR, T0, T1 i32] func(context.Context, api.Module, T0, T1) TR
+
+func (fn funcIII[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1])))
+}
+
+func ExportFuncIII[TR, T0, T1 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcIII[TR, T0, T1](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
+ Export(name)
+}
+
+type funcIIII[TR, T0, T1, T2 i32] func(context.Context, api.Module, T0, T1, T2) TR
+
+func (fn funcIIII[TR, T0, T1, T2]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2])))
+}
+
+func ExportFuncIIII[TR, T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2) TR) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcIIII[TR, T0, T1, T2](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
+ Export(name)
+}
+
+type funcIIIII[TR, T0, T1, T2, T3 i32] func(context.Context, api.Module, T0, T1, T2, T3) TR
+
+func (fn funcIIIII[TR, T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3])))
+}
+
+func ExportFuncIIIII[TR, T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcIIIII[TR, T0, T1, T2, T3](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
+ Export(name)
+}
+
+type funcIIIIII[TR, T0, T1, T2, T3, T4 i32] func(context.Context, api.Module, T0, T1, T2, T3, T4) TR
+
+func (fn funcIIIIII[TR, T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4])))
+}
+
+func ExportFuncIIIIII[TR, T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4) TR) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcIIIIII[TR, T0, T1, T2, T3, T4](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
+ Export(name)
+}
+
+type funcIIIIIII[TR, T0, T1, T2, T3, T4, T5 i32] func(context.Context, api.Module, T0, T1, T2, T3, T4, T5) TR
+
+func (fn funcIIIIIII[TR, T0, T1, T2, T3, T4, T5]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4]), T5(stack[5])))
+}
+
+func ExportFuncIIIIIII[TR, T0, T1, T2, T3, T4, T5 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4, T5) TR) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcIIIIIII[TR, T0, T1, T2, T3, T4, T5](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
+ Export(name)
+}
+
+type funcIIIIJ[TR, T0, T1, T2 i32, T3 i64] func(context.Context, api.Module, T0, T1, T2, T3) TR
+
+func (fn funcIIIIJ[TR, T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3])))
+}
+
+func ExportFuncIIIIJ[TR, T0, T1, T2 i32, T3 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcIIIIJ[TR, T0, T1, T2, T3](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}).
+ Export(name)
+}
+
+type funcIIJ[TR, T0 i32, T1 i64] func(context.Context, api.Module, T0, T1) TR
+
+func (fn funcIIJ[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []uint64) {
+ stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1])))
+}
+
+func ExportFuncIIJ[TR, T0 i32, T1 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) {
+ mod.NewFunctionBuilder().
+ WithGoModuleFunction(funcIIJ[TR, T0, T1](fn),
+ []api.ValueType{api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}).
+ Export(name)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/handle.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/handle.go
new file mode 100644
index 000000000..4584324c1
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/handle.go
@@ -0,0 +1,65 @@
+package util
+
+import (
+ "context"
+ "io"
+)
+
+type handleState struct {
+ handles []any
+ holes int
+}
+
+func (s *handleState) CloseNotify(ctx context.Context, exitCode uint32) {
+ for _, h := range s.handles {
+ if c, ok := h.(io.Closer); ok {
+ c.Close()
+ }
+ }
+ s.handles = nil
+ s.holes = 0
+}
+
+func GetHandle(ctx context.Context, id uint32) any {
+ if id == 0 {
+ return nil
+ }
+ s := ctx.Value(moduleKey{}).(*moduleState)
+ return s.handles[^id]
+}
+
+func DelHandle(ctx context.Context, id uint32) error {
+ if id == 0 {
+ return nil
+ }
+ s := ctx.Value(moduleKey{}).(*moduleState)
+ a := s.handles[^id]
+ s.handles[^id] = nil
+ s.holes++
+ if c, ok := a.(io.Closer); ok {
+ return c.Close()
+ }
+ return nil
+}
+
+func AddHandle(ctx context.Context, a any) (id uint32) {
+ if a == nil {
+ panic(NilErr)
+ }
+ s := ctx.Value(moduleKey{}).(*moduleState)
+
+ // Find an empty slot.
+ if s.holes > cap(s.handles)-len(s.handles) {
+ for id, h := range s.handles {
+ if h == nil {
+ s.holes--
+ s.handles[id] = a
+ return ^uint32(id)
+ }
+ }
+ }
+
+ // Add a new slot.
+ s.handles = append(s.handles, a)
+ return -uint32(len(s.handles))
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/json.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/json.go
new file mode 100644
index 000000000..c0ba38cf0
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/json.go
@@ -0,0 +1,35 @@
+package util
+
+import (
+ "encoding/json"
+ "strconv"
+ "time"
+ "unsafe"
+)
+
+type JSON struct{ Value any }
+
+func (j JSON) Scan(value any) error {
+ var buf []byte
+
+ switch v := value.(type) {
+ case []byte:
+ buf = v
+ case string:
+ buf = unsafe.Slice(unsafe.StringData(v), len(v))
+ case int64:
+ buf = strconv.AppendInt(nil, v, 10)
+ case float64:
+ buf = strconv.AppendFloat(nil, v, 'g', -1, 64)
+ case time.Time:
+ buf = append(buf, '"')
+ buf = v.AppendFormat(buf, time.RFC3339Nano)
+ buf = append(buf, '"')
+ case nil:
+ buf = append(buf, "null"...)
+ default:
+ panic(AssertErr())
+ }
+
+ return json.Unmarshal(buf, j.Value)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/mem.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/mem.go
new file mode 100644
index 000000000..a09523fd1
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/mem.go
@@ -0,0 +1,134 @@
+package util
+
+import (
+ "bytes"
+ "math"
+
+ "github.com/tetratelabs/wazero/api"
+)
+
+func View(mod api.Module, ptr uint32, size uint64) []byte {
+ if ptr == 0 {
+ panic(NilErr)
+ }
+ if size > math.MaxUint32 {
+ panic(RangeErr)
+ }
+ if size == 0 {
+ return nil
+ }
+ buf, ok := mod.Memory().Read(ptr, uint32(size))
+ if !ok {
+ panic(RangeErr)
+ }
+ return buf
+}
+
+func ReadUint8(mod api.Module, ptr uint32) uint8 {
+ if ptr == 0 {
+ panic(NilErr)
+ }
+ v, ok := mod.Memory().ReadByte(ptr)
+ if !ok {
+ panic(RangeErr)
+ }
+ return v
+}
+
+func ReadUint32(mod api.Module, ptr uint32) uint32 {
+ if ptr == 0 {
+ panic(NilErr)
+ }
+ v, ok := mod.Memory().ReadUint32Le(ptr)
+ if !ok {
+ panic(RangeErr)
+ }
+ return v
+}
+
+func WriteUint8(mod api.Module, ptr uint32, v uint8) {
+ if ptr == 0 {
+ panic(NilErr)
+ }
+ ok := mod.Memory().WriteByte(ptr, v)
+ if !ok {
+ panic(RangeErr)
+ }
+}
+
+func WriteUint32(mod api.Module, ptr uint32, v uint32) {
+ if ptr == 0 {
+ panic(NilErr)
+ }
+ ok := mod.Memory().WriteUint32Le(ptr, v)
+ if !ok {
+ panic(RangeErr)
+ }
+}
+
+func ReadUint64(mod api.Module, ptr uint32) uint64 {
+ if ptr == 0 {
+ panic(NilErr)
+ }
+ v, ok := mod.Memory().ReadUint64Le(ptr)
+ if !ok {
+ panic(RangeErr)
+ }
+ return v
+}
+
+func WriteUint64(mod api.Module, ptr uint32, v uint64) {
+ if ptr == 0 {
+ panic(NilErr)
+ }
+ ok := mod.Memory().WriteUint64Le(ptr, v)
+ if !ok {
+ panic(RangeErr)
+ }
+}
+
+func ReadFloat64(mod api.Module, ptr uint32) float64 {
+ return math.Float64frombits(ReadUint64(mod, ptr))
+}
+
+func WriteFloat64(mod api.Module, ptr uint32, v float64) {
+ WriteUint64(mod, ptr, math.Float64bits(v))
+}
+
+func ReadString(mod api.Module, ptr, maxlen uint32) string {
+ if ptr == 0 {
+ panic(NilErr)
+ }
+ switch maxlen {
+ case 0:
+ return ""
+ case math.MaxUint32:
+ // avoid overflow
+ default:
+ maxlen = maxlen + 1
+ }
+ mem := mod.Memory()
+ buf, ok := mem.Read(ptr, maxlen)
+ if !ok {
+ buf, ok = mem.Read(ptr, mem.Size()-ptr)
+ if !ok {
+ panic(RangeErr)
+ }
+ }
+ if i := bytes.IndexByte(buf, 0); i < 0 {
+ panic(NoNulErr)
+ } else {
+ return string(buf[:i])
+ }
+}
+
+func WriteBytes(mod api.Module, ptr uint32, b []byte) {
+ buf := View(mod, ptr, uint64(len(b)))
+ copy(buf, b)
+}
+
+func WriteString(mod api.Module, ptr uint32, s string) {
+ buf := View(mod, ptr, uint64(len(s)+1))
+ buf[len(s)] = 0
+ copy(buf, s)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go
new file mode 100644
index 000000000..6783c9612
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go
@@ -0,0 +1,97 @@
+//go:build (darwin || linux) && (amd64 || arm64 || riscv64) && !(sqlite3_noshm || sqlite3_nosys)
+
+package util
+
+import (
+ "context"
+ "os"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/api"
+ "github.com/tetratelabs/wazero/experimental"
+ "golang.org/x/sys/unix"
+)
+
+func withAllocator(ctx context.Context) context.Context {
+ return experimental.WithMemoryAllocator(ctx,
+ experimental.MemoryAllocatorFunc(virtualAlloc))
+}
+
+type mmapState struct {
+ regions []*MappedRegion
+}
+
+func (s *mmapState) new(ctx context.Context, mod api.Module, size int32) *MappedRegion {
+ // Find unused region.
+ for _, r := range s.regions {
+ if !r.used && r.size == size {
+ return r
+ }
+ }
+
+ // Allocate page aligned memmory.
+ alloc := mod.ExportedFunction("aligned_alloc")
+ stack := [2]uint64{
+ uint64(unix.Getpagesize()),
+ uint64(size),
+ }
+ if err := alloc.CallWithStack(ctx, stack[:]); err != nil {
+ panic(err)
+ }
+ if stack[0] == 0 {
+ panic(OOMErr)
+ }
+
+ // Save the newly allocated region.
+ ptr := uint32(stack[0])
+ buf := View(mod, ptr, uint64(size))
+ addr := uintptr(unsafe.Pointer(&buf[0]))
+ s.regions = append(s.regions, &MappedRegion{
+ Ptr: ptr,
+ addr: addr,
+ size: size,
+ })
+ return s.regions[len(s.regions)-1]
+}
+
+type MappedRegion struct {
+ addr uintptr
+ Ptr uint32
+ size int32
+ used bool
+}
+
+func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32, prot int) (*MappedRegion, error) {
+ s := ctx.Value(moduleKey{}).(*moduleState)
+ r := s.new(ctx, mod, size)
+ err := r.mmap(f, offset, prot)
+ if err != nil {
+ return nil, err
+ }
+ return r, nil
+}
+
+func (r *MappedRegion) Unmap() error {
+ // We can't munmap the region, otherwise it could be remaped.
+ // Instead, convert it to a protected, private, anonymous mapping.
+ // If successful, it can be reused for a subsequent mmap.
+ _, err := mmap(r.addr, uintptr(r.size),
+ unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANON|unix.MAP_FIXED,
+ -1, 0)
+ r.used = err != nil
+ return err
+}
+
+func (r *MappedRegion) mmap(f *os.File, offset int64, prot int) error {
+ _, err := mmap(r.addr, uintptr(r.size),
+ prot, unix.MAP_SHARED|unix.MAP_FIXED,
+ int(f.Fd()), offset)
+ r.used = err == nil
+ return err
+}
+
+// We need the low level mmap for MAP_FIXED to work.
+// Bind the syscall version hoping that it is more stable.
+
+//go:linkname mmap syscall.mmap
+func mmap(addr, length uintptr, prot, flag, fd int, pos int64) (*byte, error)
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_other.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_other.go
new file mode 100644
index 000000000..1e81c9fd3
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_other.go
@@ -0,0 +1,21 @@
+//go:build !(darwin || linux) || !(amd64 || arm64 || riscv64) || sqlite3_noshm || sqlite3_nosys
+
+package util
+
+import (
+ "context"
+
+ "github.com/tetratelabs/wazero/experimental"
+)
+
+type mmapState struct{}
+
+func withAllocator(ctx context.Context) context.Context {
+ return experimental.WithMemoryAllocator(ctx,
+ experimental.MemoryAllocatorFunc(func(cap, max uint64) experimental.LinearMemory {
+ if cap == max {
+ return virtualAlloc(cap, max)
+ }
+ return sliceAlloc(cap, max)
+ }))
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/module.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/module.go
new file mode 100644
index 000000000..22793e972
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/module.go
@@ -0,0 +1,21 @@
+package util
+
+import (
+ "context"
+
+ "github.com/tetratelabs/wazero/experimental"
+)
+
+type moduleKey struct{}
+type moduleState struct {
+ mmapState
+ handleState
+}
+
+func NewContext(ctx context.Context) context.Context {
+ state := new(moduleState)
+ ctx = withAllocator(ctx)
+ ctx = experimental.WithCloseNotifier(ctx, state)
+ ctx = context.WithValue(ctx, moduleKey{}, state)
+ return ctx
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/pointer.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/pointer.go
new file mode 100644
index 000000000..eae4dae17
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/pointer.go
@@ -0,0 +1,11 @@
+package util
+
+type Pointer[T any] struct{ Value T }
+
+func (p Pointer[T]) unwrap() any { return p.Value }
+
+type PointerUnwrap interface{ unwrap() any }
+
+func UnwrapPointer(p PointerUnwrap) any {
+ return p.unwrap()
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/reflect.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/reflect.go
new file mode 100644
index 000000000..3104a7cf3
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/reflect.go
@@ -0,0 +1,10 @@
+package util
+
+import "reflect"
+
+func ReflectType(v reflect.Value) reflect.Type {
+ if v.Kind() != reflect.Invalid {
+ return v.Type()
+ }
+ return nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/json.go b/vendor/github.com/ncruces/go-sqlite3/json.go
new file mode 100644
index 000000000..9b2565e87
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/json.go
@@ -0,0 +1,11 @@
+package sqlite3
+
+import "github.com/ncruces/go-sqlite3/internal/util"
+
+// JSON returns a value that can be used as an argument to
+// [database/sql.DB.Exec], [database/sql.Row.Scan] and similar methods to
+// store value as JSON, or decode JSON into value.
+// JSON should NOT be used with [BindJSON] or [ResultJSON].
+func JSON(value any) any {
+ return util.JSON{Value: value}
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/pointer.go b/vendor/github.com/ncruces/go-sqlite3/pointer.go
new file mode 100644
index 000000000..611c1528c
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/pointer.go
@@ -0,0 +1,12 @@
+package sqlite3
+
+import "github.com/ncruces/go-sqlite3/internal/util"
+
+// Pointer returns a pointer to a value that can be used as an argument to
+// [database/sql.DB.Exec] and similar methods.
+// Pointer should NOT be used with [BindPointer] or [ResultPointer].
+//
+// https://sqlite.org/bindptr.html
+func Pointer[T any](value T) any {
+ return util.Pointer[T]{Value: value}
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/quote.go b/vendor/github.com/ncruces/go-sqlite3/quote.go
new file mode 100644
index 000000000..d1cd6fa87
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/quote.go
@@ -0,0 +1,112 @@
+package sqlite3
+
+import (
+ "bytes"
+ "math"
+ "strconv"
+ "strings"
+ "time"
+ "unsafe"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+// Quote escapes and quotes a value
+// making it safe to embed in SQL text.
+func Quote(value any) string {
+ switch v := value.(type) {
+ case nil:
+ return "NULL"
+ case bool:
+ if v {
+ return "1"
+ } else {
+ return "0"
+ }
+
+ case int:
+ return strconv.Itoa(v)
+ case int64:
+ return strconv.FormatInt(v, 10)
+ case float64:
+ switch {
+ case math.IsNaN(v):
+ return "NULL"
+ case math.IsInf(v, 1):
+ return "9.0e999"
+ case math.IsInf(v, -1):
+ return "-9.0e999"
+ }
+ return strconv.FormatFloat(v, 'g', -1, 64)
+ case time.Time:
+ return "'" + v.Format(time.RFC3339Nano) + "'"
+
+ case string:
+ if strings.IndexByte(v, 0) >= 0 {
+ break
+ }
+
+ buf := make([]byte, 2+len(v)+strings.Count(v, "'"))
+ buf[0] = '\''
+ i := 1
+ for _, b := range []byte(v) {
+ if b == '\'' {
+ buf[i] = b
+ i += 1
+ }
+ buf[i] = b
+ i += 1
+ }
+ buf[i] = '\''
+ return unsafe.String(&buf[0], len(buf))
+
+ case []byte:
+ buf := make([]byte, 3+2*len(v))
+ buf[0] = 'x'
+ buf[1] = '\''
+ i := 2
+ for _, b := range v {
+ const hex = "0123456789ABCDEF"
+ buf[i+0] = hex[b/16]
+ buf[i+1] = hex[b%16]
+ i += 2
+ }
+ buf[i] = '\''
+ return unsafe.String(&buf[0], len(buf))
+
+ case ZeroBlob:
+ if v > ZeroBlob(1e9-3)/2 {
+ break
+ }
+
+ buf := bytes.Repeat([]byte("0"), int(3+2*int64(v)))
+ buf[0] = 'x'
+ buf[1] = '\''
+ buf[len(buf)-1] = '\''
+ return unsafe.String(&buf[0], len(buf))
+ }
+
+ panic(util.ValueErr)
+}
+
+// QuoteIdentifier escapes and quotes an identifier
+// making it safe to embed in SQL text.
+func QuoteIdentifier(id string) string {
+ if strings.IndexByte(id, 0) >= 0 {
+ panic(util.ValueErr)
+ }
+
+ buf := make([]byte, 2+len(id)+strings.Count(id, `"`))
+ buf[0] = '"'
+ i := 1
+ for _, b := range []byte(id) {
+ if b == '"' {
+ buf[i] = b
+ i += 1
+ }
+ buf[i] = b
+ i += 1
+ }
+ buf[i] = '"'
+ return unsafe.String(&buf[0], len(buf))
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/sqlite.go b/vendor/github.com/ncruces/go-sqlite3/sqlite.go
new file mode 100644
index 000000000..61a03652f
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/sqlite.go
@@ -0,0 +1,341 @@
+// Package sqlite3 wraps the C SQLite API.
+package sqlite3
+
+import (
+ "context"
+ "math"
+ "math/bits"
+ "os"
+ "sync"
+ "unsafe"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/ncruces/go-sqlite3/vfs"
+ "github.com/tetratelabs/wazero"
+ "github.com/tetratelabs/wazero/api"
+)
+
+// Configure SQLite Wasm.
+//
+// Importing package embed initializes [Binary]
+// with an appropriate build of SQLite:
+//
+// import _ "github.com/ncruces/go-sqlite3/embed"
+var (
+ Binary []byte // Wasm binary to load.
+ Path string // Path to load the binary from.
+
+ RuntimeConfig wazero.RuntimeConfig
+)
+
+// Initialize decodes and compiles the SQLite Wasm binary.
+// This is called implicitly when the first connection is openned,
+// but is potentially slow, so you may want to call it at a more convenient time.
+func Initialize() error {
+ instance.once.Do(compileSQLite)
+ return instance.err
+}
+
+var instance struct {
+ runtime wazero.Runtime
+ compiled wazero.CompiledModule
+ err error
+ once sync.Once
+}
+
+func compileSQLite() {
+ if RuntimeConfig == nil {
+ RuntimeConfig = wazero.NewRuntimeConfig()
+ }
+
+ ctx := context.Background()
+ instance.runtime = wazero.NewRuntimeWithConfig(ctx, RuntimeConfig)
+
+ env := instance.runtime.NewHostModuleBuilder("env")
+ env = vfs.ExportHostFunctions(env)
+ env = exportCallbacks(env)
+ _, instance.err = env.Instantiate(ctx)
+ if instance.err != nil {
+ return
+ }
+
+ bin := Binary
+ if bin == nil && Path != "" {
+ bin, instance.err = os.ReadFile(Path)
+ if instance.err != nil {
+ return
+ }
+ }
+ if bin == nil {
+ instance.err = util.NoBinaryErr
+ return
+ }
+
+ instance.compiled, instance.err = instance.runtime.CompileModule(ctx, bin)
+}
+
+type sqlite struct {
+ ctx context.Context
+ mod api.Module
+ funcs struct {
+ fn [32]api.Function
+ id [32]*byte
+ mask uint32
+ }
+ stack [8]uint64
+ freer uint32
+}
+
+func instantiateSQLite() (sqlt *sqlite, err error) {
+ if err := Initialize(); err != nil {
+ return nil, err
+ }
+
+ sqlt = new(sqlite)
+ sqlt.ctx = util.NewContext(context.Background())
+
+ sqlt.mod, err = instance.runtime.InstantiateModule(sqlt.ctx,
+ instance.compiled, wazero.NewModuleConfig().WithName(""))
+ if err != nil {
+ return nil, err
+ }
+
+ global := sqlt.mod.ExportedGlobal("malloc_destructor")
+ if global == nil {
+ return nil, util.BadBinaryErr
+ }
+
+ sqlt.freer = util.ReadUint32(sqlt.mod, uint32(global.Get()))
+ if sqlt.freer == 0 {
+ return nil, util.BadBinaryErr
+ }
+ return sqlt, nil
+}
+
+func (sqlt *sqlite) close() error {
+ return sqlt.mod.Close(sqlt.ctx)
+}
+
+func (sqlt *sqlite) error(rc uint64, handle uint32, sql ...string) error {
+ if rc == _OK {
+ return nil
+ }
+
+ err := Error{code: rc}
+
+ if err.Code() == NOMEM || err.ExtendedCode() == IOERR_NOMEM {
+ panic(util.OOMErr)
+ }
+
+ if r := sqlt.call("sqlite3_errstr", rc); r != 0 {
+ err.str = util.ReadString(sqlt.mod, uint32(r), _MAX_NAME)
+ }
+
+ if handle != 0 {
+ if r := sqlt.call("sqlite3_errmsg", uint64(handle)); r != 0 {
+ err.msg = util.ReadString(sqlt.mod, uint32(r), _MAX_LENGTH)
+ }
+
+ if sql != nil {
+ if r := sqlt.call("sqlite3_error_offset", uint64(handle)); r != math.MaxUint32 {
+ err.sql = sql[0][r:]
+ }
+ }
+ }
+
+ switch err.msg {
+ case err.str, "not an error":
+ err.msg = ""
+ }
+ return &err
+}
+
+func (sqlt *sqlite) getfn(name string) api.Function {
+ c := &sqlt.funcs
+ p := unsafe.StringData(name)
+ for i := range c.id {
+ if c.id[i] == p {
+ c.id[i] = nil
+ c.mask &^= uint32(1) << i
+ return c.fn[i]
+ }
+ }
+ return sqlt.mod.ExportedFunction(name)
+}
+
+func (sqlt *sqlite) putfn(name string, fn api.Function) {
+ c := &sqlt.funcs
+ p := unsafe.StringData(name)
+ i := bits.TrailingZeros32(^c.mask)
+ if i < 32 {
+ c.id[i] = p
+ c.fn[i] = fn
+ c.mask |= uint32(1) << i
+ } else {
+ c.id[0] = p
+ c.fn[0] = fn
+ c.mask = uint32(1)
+ }
+}
+
+func (sqlt *sqlite) call(name string, params ...uint64) uint64 {
+ copy(sqlt.stack[:], params)
+ fn := sqlt.getfn(name)
+ err := fn.CallWithStack(sqlt.ctx, sqlt.stack[:])
+ if err != nil {
+ panic(err)
+ }
+ sqlt.putfn(name, fn)
+ return sqlt.stack[0]
+}
+
+func (sqlt *sqlite) free(ptr uint32) {
+ if ptr == 0 {
+ return
+ }
+ sqlt.call("free", uint64(ptr))
+}
+
+func (sqlt *sqlite) new(size uint64) uint32 {
+ if size > _MAX_ALLOCATION_SIZE {
+ panic(util.OOMErr)
+ }
+ ptr := uint32(sqlt.call("malloc", size))
+ if ptr == 0 && size != 0 {
+ panic(util.OOMErr)
+ }
+ return ptr
+}
+
+func (sqlt *sqlite) newBytes(b []byte) uint32 {
+ if (*[0]byte)(b) == nil {
+ return 0
+ }
+ ptr := sqlt.new(uint64(len(b)))
+ util.WriteBytes(sqlt.mod, ptr, b)
+ return ptr
+}
+
+func (sqlt *sqlite) newString(s string) uint32 {
+ ptr := sqlt.new(uint64(len(s) + 1))
+ util.WriteString(sqlt.mod, ptr, s)
+ return ptr
+}
+
+func (sqlt *sqlite) newArena(size uint64) arena {
+ // Ensure the arena's size is a multiple of 8.
+ size = (size + 7) &^ 7
+ return arena{
+ sqlt: sqlt,
+ size: uint32(size),
+ base: sqlt.new(size),
+ }
+}
+
+type arena struct {
+ sqlt *sqlite
+ ptrs []uint32
+ base uint32
+ next uint32
+ size uint32
+}
+
+func (a *arena) free() {
+ if a.sqlt == nil {
+ return
+ }
+ for _, ptr := range a.ptrs {
+ a.sqlt.free(ptr)
+ }
+ a.sqlt.free(a.base)
+ a.sqlt = nil
+}
+
+func (a *arena) mark() (reset func()) {
+ ptrs := len(a.ptrs)
+ next := a.next
+ return func() {
+ for _, ptr := range a.ptrs[ptrs:] {
+ a.sqlt.free(ptr)
+ }
+ a.ptrs = a.ptrs[:ptrs]
+ a.next = next
+ }
+}
+
+func (a *arena) new(size uint64) uint32 {
+ // Align the next address, to 4 or 8 bytes.
+ if size&7 != 0 {
+ a.next = (a.next + 3) &^ 3
+ } else {
+ a.next = (a.next + 7) &^ 7
+ }
+ if size <= uint64(a.size-a.next) {
+ ptr := a.base + a.next
+ a.next += uint32(size)
+ return ptr
+ }
+ ptr := a.sqlt.new(size)
+ a.ptrs = append(a.ptrs, ptr)
+ return ptr
+}
+
+func (a *arena) bytes(b []byte) uint32 {
+ if (*[0]byte)(b) == nil {
+ return 0
+ }
+ ptr := a.new(uint64(len(b)))
+ util.WriteBytes(a.sqlt.mod, ptr, b)
+ return ptr
+}
+
+func (a *arena) string(s string) uint32 {
+ ptr := a.new(uint64(len(s) + 1))
+ util.WriteString(a.sqlt.mod, ptr, s)
+ return ptr
+}
+
+func exportCallbacks(env wazero.HostModuleBuilder) wazero.HostModuleBuilder {
+ util.ExportFuncII(env, "go_progress_handler", progressCallback)
+ util.ExportFuncIIII(env, "go_busy_timeout", timeoutCallback)
+ util.ExportFuncIII(env, "go_busy_handler", busyCallback)
+ util.ExportFuncII(env, "go_commit_hook", commitCallback)
+ util.ExportFuncVI(env, "go_rollback_hook", rollbackCallback)
+ util.ExportFuncVIIIIJ(env, "go_update_hook", updateCallback)
+ util.ExportFuncIIIII(env, "go_wal_hook", walCallback)
+ util.ExportFuncIIIIII(env, "go_autovacuum_pages", autoVacuumCallback)
+ util.ExportFuncIIIIIII(env, "go_authorizer", authorizerCallback)
+ util.ExportFuncVIII(env, "go_log", logCallback)
+ util.ExportFuncVI(env, "go_destroy", destroyCallback)
+ util.ExportFuncVIIII(env, "go_func", funcCallback)
+ util.ExportFuncVIIIII(env, "go_step", stepCallback)
+ util.ExportFuncVIII(env, "go_final", finalCallback)
+ util.ExportFuncVII(env, "go_value", valueCallback)
+ util.ExportFuncVIIII(env, "go_inverse", inverseCallback)
+ util.ExportFuncVIIII(env, "go_collation_needed", collationCallback)
+ util.ExportFuncIIIIII(env, "go_compare", compareCallback)
+ util.ExportFuncIIIIII(env, "go_vtab_create", vtabModuleCallback(xCreate))
+ util.ExportFuncIIIIII(env, "go_vtab_connect", vtabModuleCallback(xConnect))
+ util.ExportFuncII(env, "go_vtab_disconnect", vtabDisconnectCallback)
+ util.ExportFuncII(env, "go_vtab_destroy", vtabDestroyCallback)
+ util.ExportFuncIII(env, "go_vtab_best_index", vtabBestIndexCallback)
+ util.ExportFuncIIIII(env, "go_vtab_update", vtabUpdateCallback)
+ util.ExportFuncIII(env, "go_vtab_rename", vtabRenameCallback)
+ util.ExportFuncIIIII(env, "go_vtab_find_function", vtabFindFuncCallback)
+ util.ExportFuncII(env, "go_vtab_begin", vtabBeginCallback)
+ util.ExportFuncII(env, "go_vtab_sync", vtabSyncCallback)
+ util.ExportFuncII(env, "go_vtab_commit", vtabCommitCallback)
+ util.ExportFuncII(env, "go_vtab_rollback", vtabRollbackCallback)
+ util.ExportFuncIII(env, "go_vtab_savepoint", vtabSavepointCallback)
+ util.ExportFuncIII(env, "go_vtab_release", vtabReleaseCallback)
+ util.ExportFuncIII(env, "go_vtab_rollback_to", vtabRollbackToCallback)
+ util.ExportFuncIIIIII(env, "go_vtab_integrity", vtabIntegrityCallback)
+ util.ExportFuncIII(env, "go_cur_open", cursorOpenCallback)
+ util.ExportFuncII(env, "go_cur_close", cursorCloseCallback)
+ util.ExportFuncIIIIII(env, "go_cur_filter", cursorFilterCallback)
+ util.ExportFuncII(env, "go_cur_next", cursorNextCallback)
+ util.ExportFuncII(env, "go_cur_eof", cursorEOFCallback)
+ util.ExportFuncIIII(env, "go_cur_column", cursorColumnCallback)
+ util.ExportFuncIII(env, "go_cur_rowid", cursorRowIDCallback)
+ return env
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/stmt.go b/vendor/github.com/ncruces/go-sqlite3/stmt.go
new file mode 100644
index 000000000..63c2085d0
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/stmt.go
@@ -0,0 +1,639 @@
+package sqlite3
+
+import (
+ "encoding/json"
+ "math"
+ "strconv"
+ "time"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+// Stmt is a prepared statement object.
+//
+// https://sqlite.org/c3ref/stmt.html
+type Stmt struct {
+ c *Conn
+ err error
+ handle uint32
+}
+
+// Close destroys the prepared statement object.
+//
+// It is safe to close a nil, zero or closed Stmt.
+//
+// https://sqlite.org/c3ref/finalize.html
+func (s *Stmt) Close() error {
+ if s == nil || s.handle == 0 {
+ return nil
+ }
+
+ r := s.c.call("sqlite3_finalize", uint64(s.handle))
+
+ s.handle = 0
+ return s.c.error(r)
+}
+
+// Conn returns the database connection to which the prepared statement belongs.
+//
+// https://sqlite.org/c3ref/db_handle.html
+func (s *Stmt) Conn() *Conn {
+ return s.c
+}
+
+// ReadOnly returns true if and only if the statement
+// makes no direct changes to the content of the database file.
+//
+// https://sqlite.org/c3ref/stmt_readonly.html
+func (s *Stmt) ReadOnly() bool {
+ r := s.c.call("sqlite3_stmt_readonly", uint64(s.handle))
+ return r != 0
+}
+
+// Reset resets the prepared statement object.
+//
+// https://sqlite.org/c3ref/reset.html
+func (s *Stmt) Reset() error {
+ r := s.c.call("sqlite3_reset", uint64(s.handle))
+ s.err = nil
+ return s.c.error(r)
+}
+
+// Busy determines if a prepared statement has been reset.
+//
+// https://sqlite.org/c3ref/stmt_busy.html
+func (s *Stmt) Busy() bool {
+ r := s.c.call("sqlite3_stmt_busy", uint64(s.handle))
+ return r != 0
+}
+
+// Step evaluates the SQL statement.
+// If the SQL statement being executed returns any data,
+// then true is returned each time a new row of data is ready for processing by the caller.
+// The values may be accessed using the Column access functions.
+// Step is called again to retrieve the next row of data.
+// If an error has occurred, Step returns false;
+// call [Stmt.Err] or [Stmt.Reset] to get the error.
+//
+// https://sqlite.org/c3ref/step.html
+func (s *Stmt) Step() bool {
+ s.c.checkInterrupt()
+ r := s.c.call("sqlite3_step", uint64(s.handle))
+ switch r {
+ case _ROW:
+ s.err = nil
+ return true
+ case _DONE:
+ s.err = nil
+ default:
+ s.err = s.c.error(r)
+ }
+ return false
+}
+
+// Err gets the last error occurred during [Stmt.Step].
+// Err returns nil after [Stmt.Reset] is called.
+//
+// https://sqlite.org/c3ref/step.html
+func (s *Stmt) Err() error {
+ return s.err
+}
+
+// Exec is a convenience function that repeatedly calls [Stmt.Step] until it returns false,
+// then calls [Stmt.Reset] to reset the statement and get any error that occurred.
+func (s *Stmt) Exec() error {
+ for s.Step() {
+ }
+ return s.Reset()
+}
+
+// Status monitors the performance characteristics of prepared statements.
+//
+// https://sqlite.org/c3ref/stmt_status.html
+func (s *Stmt) Status(op StmtStatus, reset bool) int {
+ if op > STMTSTATUS_FILTER_HIT && op != STMTSTATUS_MEMUSED {
+ return 0
+ }
+ var i uint64
+ if reset {
+ i = 1
+ }
+ r := s.c.call("sqlite3_stmt_status", uint64(s.handle),
+ uint64(op), i)
+ return int(int32(r))
+}
+
+// ClearBindings resets all bindings on the prepared statement.
+//
+// https://sqlite.org/c3ref/clear_bindings.html
+func (s *Stmt) ClearBindings() error {
+ r := s.c.call("sqlite3_clear_bindings", uint64(s.handle))
+ return s.c.error(r)
+}
+
+// BindCount returns the number of SQL parameters in the prepared statement.
+//
+// https://sqlite.org/c3ref/bind_parameter_count.html
+func (s *Stmt) BindCount() int {
+ r := s.c.call("sqlite3_bind_parameter_count",
+ uint64(s.handle))
+ return int(int32(r))
+}
+
+// BindIndex returns the index of a parameter in the prepared statement
+// given its name.
+//
+// https://sqlite.org/c3ref/bind_parameter_index.html
+func (s *Stmt) BindIndex(name string) int {
+ defer s.c.arena.mark()()
+ namePtr := s.c.arena.string(name)
+ r := s.c.call("sqlite3_bind_parameter_index",
+ uint64(s.handle), uint64(namePtr))
+ return int(int32(r))
+}
+
+// BindName returns the name of a parameter in the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_parameter_name.html
+func (s *Stmt) BindName(param int) string {
+ r := s.c.call("sqlite3_bind_parameter_name",
+ uint64(s.handle), uint64(param))
+
+ ptr := uint32(r)
+ if ptr == 0 {
+ return ""
+ }
+ return util.ReadString(s.c.mod, ptr, _MAX_NAME)
+}
+
+// BindBool binds a bool to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+// SQLite does not have a separate boolean storage class.
+// Instead, boolean values are stored as integers 0 (false) and 1 (true).
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindBool(param int, value bool) error {
+ var i int64
+ if value {
+ i = 1
+ }
+ return s.BindInt64(param, i)
+}
+
+// BindInt binds an int to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindInt(param int, value int) error {
+ return s.BindInt64(param, int64(value))
+}
+
+// BindInt64 binds an int64 to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindInt64(param int, value int64) error {
+ r := s.c.call("sqlite3_bind_int64",
+ uint64(s.handle), uint64(param), uint64(value))
+ return s.c.error(r)
+}
+
+// BindFloat binds a float64 to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindFloat(param int, value float64) error {
+ r := s.c.call("sqlite3_bind_double",
+ uint64(s.handle), uint64(param), math.Float64bits(value))
+ return s.c.error(r)
+}
+
+// BindText binds a string to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindText(param int, value string) error {
+ if len(value) > _MAX_LENGTH {
+ return TOOBIG
+ }
+ ptr := s.c.newString(value)
+ r := s.c.call("sqlite3_bind_text64",
+ uint64(s.handle), uint64(param),
+ uint64(ptr), uint64(len(value)),
+ uint64(s.c.freer), _UTF8)
+ return s.c.error(r)
+}
+
+// BindRawText binds a []byte to the prepared statement as text.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindRawText(param int, value []byte) error {
+ if len(value) > _MAX_LENGTH {
+ return TOOBIG
+ }
+ ptr := s.c.newBytes(value)
+ r := s.c.call("sqlite3_bind_text64",
+ uint64(s.handle), uint64(param),
+ uint64(ptr), uint64(len(value)),
+ uint64(s.c.freer), _UTF8)
+ return s.c.error(r)
+}
+
+// BindBlob binds a []byte to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+// Binding a nil slice is the same as calling [Stmt.BindNull].
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindBlob(param int, value []byte) error {
+ if len(value) > _MAX_LENGTH {
+ return TOOBIG
+ }
+ ptr := s.c.newBytes(value)
+ r := s.c.call("sqlite3_bind_blob64",
+ uint64(s.handle), uint64(param),
+ uint64(ptr), uint64(len(value)),
+ uint64(s.c.freer))
+ return s.c.error(r)
+}
+
+// BindZeroBlob binds a zero-filled, length n BLOB to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindZeroBlob(param int, n int64) error {
+ r := s.c.call("sqlite3_bind_zeroblob64",
+ uint64(s.handle), uint64(param), uint64(n))
+ return s.c.error(r)
+}
+
+// BindNull binds a NULL to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindNull(param int) error {
+ r := s.c.call("sqlite3_bind_null",
+ uint64(s.handle), uint64(param))
+ return s.c.error(r)
+}
+
+// BindTime binds a [time.Time] to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindTime(param int, value time.Time, format TimeFormat) error {
+ if format == TimeFormatDefault {
+ return s.bindRFC3339Nano(param, value)
+ }
+ switch v := format.Encode(value).(type) {
+ case string:
+ s.BindText(param, v)
+ case int64:
+ s.BindInt64(param, v)
+ case float64:
+ s.BindFloat(param, v)
+ default:
+ panic(util.AssertErr())
+ }
+ return nil
+}
+
+func (s *Stmt) bindRFC3339Nano(param int, value time.Time) error {
+ const maxlen = uint64(len(time.RFC3339Nano)) + 5
+
+ ptr := s.c.new(maxlen)
+ buf := util.View(s.c.mod, ptr, maxlen)
+ buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
+
+ r := s.c.call("sqlite3_bind_text64",
+ uint64(s.handle), uint64(param),
+ uint64(ptr), uint64(len(buf)),
+ uint64(s.c.freer), _UTF8)
+ return s.c.error(r)
+}
+
+// BindPointer binds a NULL to the prepared statement, just like [Stmt.BindNull],
+// but it also associates ptr with that NULL value such that it can be retrieved
+// within an application-defined SQL function using [Value.Pointer].
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindPointer(param int, ptr any) error {
+ valPtr := util.AddHandle(s.c.ctx, ptr)
+ r := s.c.call("sqlite3_bind_pointer_go",
+ uint64(s.handle), uint64(param), uint64(valPtr))
+ return s.c.error(r)
+}
+
+// BindJSON binds the JSON encoding of value to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindJSON(param int, value any) error {
+ data, err := json.Marshal(value)
+ if err != nil {
+ return err
+ }
+ return s.BindRawText(param, data)
+}
+
+// BindValue binds a copy of value to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindValue(param int, value Value) error {
+ if value.c != s.c {
+ return MISUSE
+ }
+ r := s.c.call("sqlite3_bind_value",
+ uint64(s.handle), uint64(param), uint64(value.handle))
+ return s.c.error(r)
+}
+
+// ColumnCount returns the number of columns in a result set.
+//
+// https://sqlite.org/c3ref/column_count.html
+func (s *Stmt) ColumnCount() int {
+ r := s.c.call("sqlite3_column_count",
+ uint64(s.handle))
+ return int(int32(r))
+}
+
+// ColumnName returns the name of the result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_name.html
+func (s *Stmt) ColumnName(col int) string {
+ r := s.c.call("sqlite3_column_name",
+ uint64(s.handle), uint64(col))
+ if r == 0 {
+ panic(util.OOMErr)
+ }
+ return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
+}
+
+// ColumnType returns the initial [Datatype] of the result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnType(col int) Datatype {
+ r := s.c.call("sqlite3_column_type",
+ uint64(s.handle), uint64(col))
+ return Datatype(r)
+}
+
+// ColumnDeclType returns the declared datatype of the result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_decltype.html
+func (s *Stmt) ColumnDeclType(col int) string {
+ r := s.c.call("sqlite3_column_decltype",
+ uint64(s.handle), uint64(col))
+ if r == 0 {
+ return ""
+ }
+ return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
+}
+
+// ColumnDatabaseName returns the name of the database
+// that is the origin of a particular result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_database_name.html
+func (s *Stmt) ColumnDatabaseName(col int) string {
+ r := s.c.call("sqlite3_column_database_name",
+ uint64(s.handle), uint64(col))
+ if r == 0 {
+ return ""
+ }
+ return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
+}
+
+// ColumnTableName returns the name of the table
+// that is the origin of a particular result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_database_name.html
+func (s *Stmt) ColumnTableName(col int) string {
+ r := s.c.call("sqlite3_column_table_name",
+ uint64(s.handle), uint64(col))
+ if r == 0 {
+ return ""
+ }
+ return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
+}
+
+// ColumnOriginName returns the name of the table column
+// that is the origin of a particular result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_database_name.html
+func (s *Stmt) ColumnOriginName(col int) string {
+ r := s.c.call("sqlite3_column_origin_name",
+ uint64(s.handle), uint64(col))
+ if r == 0 {
+ return ""
+ }
+ return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
+}
+
+// ColumnBool returns the value of the result column as a bool.
+// The leftmost column of the result set has the index 0.
+// SQLite does not have a separate boolean storage class.
+// Instead, boolean values are retrieved as integers,
+// with 0 converted to false and any other value to true.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnBool(col int) bool {
+ return s.ColumnInt64(col) != 0
+}
+
+// ColumnInt returns the value of the result column as an int.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnInt(col int) int {
+ return int(s.ColumnInt64(col))
+}
+
+// ColumnInt64 returns the value of the result column as an int64.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnInt64(col int) int64 {
+ r := s.c.call("sqlite3_column_int64",
+ uint64(s.handle), uint64(col))
+ return int64(r)
+}
+
+// ColumnFloat returns the value of the result column as a float64.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnFloat(col int) float64 {
+ r := s.c.call("sqlite3_column_double",
+ uint64(s.handle), uint64(col))
+ return math.Float64frombits(r)
+}
+
+// ColumnTime returns the value of the result column as a [time.Time].
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnTime(col int, format TimeFormat) time.Time {
+ var v any
+ switch s.ColumnType(col) {
+ case INTEGER:
+ v = s.ColumnInt64(col)
+ case FLOAT:
+ v = s.ColumnFloat(col)
+ case TEXT, BLOB:
+ v = s.ColumnText(col)
+ case NULL:
+ return time.Time{}
+ default:
+ panic(util.AssertErr())
+ }
+ t, err := format.Decode(v)
+ if err != nil {
+ s.err = err
+ }
+ return t
+}
+
+// ColumnText returns the value of the result column as a string.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnText(col int) string {
+ return string(s.ColumnRawText(col))
+}
+
+// ColumnBlob appends to buf and returns
+// the value of the result column as a []byte.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnBlob(col int, buf []byte) []byte {
+ return append(buf, s.ColumnRawBlob(col)...)
+}
+
+// ColumnRawText returns the value of the result column as a []byte.
+// The []byte is owned by SQLite and may be invalidated by
+// subsequent calls to [Stmt] methods.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnRawText(col int) []byte {
+ r := s.c.call("sqlite3_column_text",
+ uint64(s.handle), uint64(col))
+ return s.columnRawBytes(col, uint32(r))
+}
+
+// ColumnRawBlob returns the value of the result column as a []byte.
+// The []byte is owned by SQLite and may be invalidated by
+// subsequent calls to [Stmt] methods.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnRawBlob(col int) []byte {
+ r := s.c.call("sqlite3_column_blob",
+ uint64(s.handle), uint64(col))
+ return s.columnRawBytes(col, uint32(r))
+}
+
+func (s *Stmt) columnRawBytes(col int, ptr uint32) []byte {
+ if ptr == 0 {
+ r := s.c.call("sqlite3_errcode", uint64(s.c.handle))
+ s.err = s.c.error(r)
+ return nil
+ }
+
+ r := s.c.call("sqlite3_column_bytes",
+ uint64(s.handle), uint64(col))
+ return util.View(s.c.mod, ptr, r)
+}
+
+// ColumnJSON parses the JSON-encoded value of the result column
+// and stores it in the value pointed to by ptr.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnJSON(col int, ptr any) error {
+ var data []byte
+ switch s.ColumnType(col) {
+ case NULL:
+ data = append(data, "null"...)
+ case TEXT:
+ data = s.ColumnRawText(col)
+ case BLOB:
+ data = s.ColumnRawBlob(col)
+ case INTEGER:
+ data = strconv.AppendInt(nil, s.ColumnInt64(col), 10)
+ case FLOAT:
+ data = strconv.AppendFloat(nil, s.ColumnFloat(col), 'g', -1, 64)
+ default:
+ panic(util.AssertErr())
+ }
+ return json.Unmarshal(data, ptr)
+}
+
+// ColumnValue returns the unprotected value of the result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnValue(col int) Value {
+ r := s.c.call("sqlite3_column_value",
+ uint64(s.handle), uint64(col))
+ return Value{
+ c: s.c,
+ unprot: true,
+ handle: uint32(r),
+ }
+}
+
+// Columns populates result columns into the provided slice.
+// The slice must have [Stmt.ColumnCount] length.
+//
+// [INTEGER] columns will be retrieved as int64 values,
+// [FLOAT] as float64, [NULL] as nil,
+// [TEXT] as string, and [BLOB] as []byte.
+// Any []byte are owned by SQLite and may be invalidated by
+// subsequent calls to [Stmt] methods.
+func (s *Stmt) Columns(dest []any) error {
+ defer s.c.arena.mark()()
+ count := uint64(len(dest))
+ typePtr := s.c.arena.new(count)
+ dataPtr := s.c.arena.new(8 * count)
+
+ r := s.c.call("sqlite3_columns_go",
+ uint64(s.handle), count, uint64(typePtr), uint64(dataPtr))
+ if err := s.c.error(r); err != nil {
+ return err
+ }
+
+ types := util.View(s.c.mod, typePtr, count)
+ for i := range dest {
+ switch types[i] {
+ case byte(INTEGER):
+ dest[i] = int64(util.ReadUint64(s.c.mod, dataPtr+8*uint32(i)))
+ continue
+ case byte(FLOAT):
+ dest[i] = util.ReadFloat64(s.c.mod, dataPtr+8*uint32(i))
+ continue
+ case byte(NULL):
+ dest[i] = nil
+ continue
+ }
+ ptr := util.ReadUint32(s.c.mod, dataPtr+8*uint32(i)+0)
+ len := util.ReadUint32(s.c.mod, dataPtr+8*uint32(i)+4)
+ buf := util.View(s.c.mod, ptr, uint64(len))
+ if types[i] == byte(TEXT) {
+ dest[i] = string(buf)
+ } else {
+ dest[i] = buf
+ }
+ }
+ return nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/time.go b/vendor/github.com/ncruces/go-sqlite3/time.go
new file mode 100644
index 000000000..0164a307b
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/time.go
@@ -0,0 +1,354 @@
+package sqlite3
+
+import (
+ "math"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/ncruces/julianday"
+)
+
+// TimeFormat specifies how to encode/decode time values.
+//
+// See the documentation for the [TimeFormatDefault] constant
+// for formats recognized by SQLite.
+//
+// https://sqlite.org/lang_datefunc.html
+type TimeFormat string
+
+// TimeFormats recognized by SQLite to encode/decode time values.
+//
+// https://sqlite.org/lang_datefunc.html#time_values
+const (
+ TimeFormatDefault TimeFormat = "" // time.RFC3339Nano
+
+ // Text formats
+ TimeFormat1 TimeFormat = "2006-01-02"
+ TimeFormat2 TimeFormat = "2006-01-02 15:04"
+ TimeFormat3 TimeFormat = "2006-01-02 15:04:05"
+ TimeFormat4 TimeFormat = "2006-01-02 15:04:05.000"
+ TimeFormat5 TimeFormat = "2006-01-02T15:04"
+ TimeFormat6 TimeFormat = "2006-01-02T15:04:05"
+ TimeFormat7 TimeFormat = "2006-01-02T15:04:05.000"
+ TimeFormat8 TimeFormat = "15:04"
+ TimeFormat9 TimeFormat = "15:04:05"
+ TimeFormat10 TimeFormat = "15:04:05.000"
+
+ TimeFormat2TZ = TimeFormat2 + "Z07:00"
+ TimeFormat3TZ = TimeFormat3 + "Z07:00"
+ TimeFormat4TZ = TimeFormat4 + "Z07:00"
+ TimeFormat5TZ = TimeFormat5 + "Z07:00"
+ TimeFormat6TZ = TimeFormat6 + "Z07:00"
+ TimeFormat7TZ = TimeFormat7 + "Z07:00"
+ TimeFormat8TZ = TimeFormat8 + "Z07:00"
+ TimeFormat9TZ = TimeFormat9 + "Z07:00"
+ TimeFormat10TZ = TimeFormat10 + "Z07:00"
+
+ // Numeric formats
+ TimeFormatJulianDay TimeFormat = "julianday"
+ TimeFormatUnix TimeFormat = "unixepoch"
+ TimeFormatUnixFrac TimeFormat = "unixepoch_frac"
+ TimeFormatUnixMilli TimeFormat = "unixepoch_milli" // not an SQLite format
+ TimeFormatUnixMicro TimeFormat = "unixepoch_micro" // not an SQLite format
+ TimeFormatUnixNano TimeFormat = "unixepoch_nano" // not an SQLite format
+
+ // Auto
+ TimeFormatAuto TimeFormat = "auto"
+)
+
+// Encode encodes a time value using this format.
+//
+// [TimeFormatDefault] and [TimeFormatAuto] encode using [time.RFC3339Nano],
+// with nanosecond accuracy, and preserving any timezone offset.
+//
+// This is the format used by the [database/sql] driver:
+// [database/sql.Row.Scan] will decode as [time.Time]
+// values encoded with [time.RFC3339Nano].
+//
+// Time values encoded with [time.RFC3339Nano] cannot be sorted as strings
+// to produce a time-ordered sequence.
+//
+// Assuming that the time zones of the time values are the same (e.g., all in UTC),
+// and expressed using the same string (e.g., all "Z" or all "+00:00"),
+// use the TIME [collating sequence] to produce a time-ordered sequence.
+//
+// Otherwise, use [TimeFormat7] for time-ordered encoding.
+//
+// Formats [TimeFormat1] through [TimeFormat10]
+// convert time values to UTC before encoding.
+//
+// Returns a string for the text formats,
+// a float64 for [TimeFormatJulianDay] and [TimeFormatUnixFrac],
+// or an int64 for the other numeric formats.
+//
+// https://sqlite.org/lang_datefunc.html
+//
+// [collating sequence]: https://sqlite.org/datatype3.html#collating_sequences
+func (f TimeFormat) Encode(t time.Time) any {
+ switch f {
+ // Numeric formats
+ case TimeFormatJulianDay:
+ return julianday.Float(t)
+ case TimeFormatUnix:
+ return t.Unix()
+ case TimeFormatUnixFrac:
+ return float64(t.Unix()) + float64(t.Nanosecond())*1e-9
+ case TimeFormatUnixMilli:
+ return t.UnixMilli()
+ case TimeFormatUnixMicro:
+ return t.UnixMicro()
+ case TimeFormatUnixNano:
+ return t.UnixNano()
+ // Special formats.
+ case TimeFormatDefault, TimeFormatAuto:
+ f = time.RFC3339Nano
+ // SQLite assumes UTC if unspecified.
+ case
+ TimeFormat1, TimeFormat2,
+ TimeFormat3, TimeFormat4,
+ TimeFormat5, TimeFormat6,
+ TimeFormat7, TimeFormat8,
+ TimeFormat9, TimeFormat10:
+ t = t.UTC()
+ }
+ return t.Format(string(f))
+}
+
+// Decode decodes a time value using this format.
+//
+// The time value can be a string, an int64, or a float64.
+//
+// Formats [TimeFormat8] through [TimeFormat10]
+// (and [TimeFormat8TZ] through [TimeFormat10TZ])
+// assume a date of 2000-01-01.
+//
+// The timezone indicator and fractional seconds are always optional
+// for formats [TimeFormat2] through [TimeFormat10]
+// (and [TimeFormat2TZ] through [TimeFormat10TZ]).
+//
+// [TimeFormatAuto] implements (and extends) the SQLite auto modifier.
+// Julian day numbers are safe to use for historical dates,
+// from 4712BC through 9999AD.
+// Unix timestamps (expressed in seconds, milliseconds, microseconds, or nanoseconds)
+// are safe to use for current events, from at least 1980 through at least 2260.
+// Unix timestamps before 1980 and after 9999 may be misinterpreted as julian day numbers,
+// or have the wrong time unit.
+//
+// https://sqlite.org/lang_datefunc.html
+func (f TimeFormat) Decode(v any) (time.Time, error) {
+ switch f {
+ // Numeric formats.
+ case TimeFormatJulianDay:
+ switch v := v.(type) {
+ case string:
+ return julianday.Parse(v)
+ case float64:
+ return julianday.FloatTime(v), nil
+ case int64:
+ return julianday.Time(v, 0), nil
+ default:
+ return time.Time{}, util.TimeErr
+ }
+
+ case TimeFormatUnix, TimeFormatUnixFrac:
+ if s, ok := v.(string); ok {
+ f, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return time.Time{}, err
+ }
+ v = f
+ }
+ switch v := v.(type) {
+ case float64:
+ sec, frac := math.Modf(v)
+ nsec := math.Floor(frac * 1e9)
+ return time.Unix(int64(sec), int64(nsec)).UTC(), nil
+ case int64:
+ return time.Unix(v, 0).UTC(), nil
+ default:
+ return time.Time{}, util.TimeErr
+ }
+
+ case TimeFormatUnixMilli:
+ if s, ok := v.(string); ok {
+ i, err := strconv.ParseInt(s, 10, 64)
+ if err != nil {
+ return time.Time{}, err
+ }
+ v = i
+ }
+ switch v := v.(type) {
+ case float64:
+ return time.UnixMilli(int64(math.Floor(v))).UTC(), nil
+ case int64:
+ return time.UnixMilli(v).UTC(), nil
+ default:
+ return time.Time{}, util.TimeErr
+ }
+
+ case TimeFormatUnixMicro:
+ if s, ok := v.(string); ok {
+ i, err := strconv.ParseInt(s, 10, 64)
+ if err != nil {
+ return time.Time{}, err
+ }
+ v = i
+ }
+ switch v := v.(type) {
+ case float64:
+ return time.UnixMicro(int64(math.Floor(v))).UTC(), nil
+ case int64:
+ return time.UnixMicro(v).UTC(), nil
+ default:
+ return time.Time{}, util.TimeErr
+ }
+
+ case TimeFormatUnixNano:
+ if s, ok := v.(string); ok {
+ i, err := strconv.ParseInt(s, 10, 64)
+ if err != nil {
+ return time.Time{}, util.TimeErr
+ }
+ v = i
+ }
+ switch v := v.(type) {
+ case float64:
+ return time.Unix(0, int64(math.Floor(v))).UTC(), nil
+ case int64:
+ return time.Unix(0, v).UTC(), nil
+ default:
+ return time.Time{}, util.TimeErr
+ }
+
+ // Special formats.
+ case TimeFormatAuto:
+ switch s := v.(type) {
+ case string:
+ i, err := strconv.ParseInt(s, 10, 64)
+ if err == nil {
+ v = i
+ break
+ }
+ f, err := strconv.ParseFloat(s, 64)
+ if err == nil {
+ v = f
+ break
+ }
+
+ dates := []TimeFormat{
+ TimeFormat9, TimeFormat8,
+ TimeFormat6, TimeFormat5,
+ TimeFormat3, TimeFormat2, TimeFormat1,
+ }
+ for _, f := range dates {
+ t, err := f.Decode(s)
+ if err == nil {
+ return t, nil
+ }
+ }
+ }
+ switch v := v.(type) {
+ case float64:
+ if 0 <= v && v < 5373484.5 {
+ return TimeFormatJulianDay.Decode(v)
+ }
+ if v < 253402300800 {
+ return TimeFormatUnixFrac.Decode(v)
+ }
+ if v < 253402300800_000 {
+ return TimeFormatUnixMilli.Decode(v)
+ }
+ if v < 253402300800_000000 {
+ return TimeFormatUnixMicro.Decode(v)
+ }
+ return TimeFormatUnixNano.Decode(v)
+ case int64:
+ if 0 <= v && v < 5373485 {
+ return TimeFormatJulianDay.Decode(v)
+ }
+ if v < 253402300800 {
+ return TimeFormatUnixFrac.Decode(v)
+ }
+ if v < 253402300800_000 {
+ return TimeFormatUnixMilli.Decode(v)
+ }
+ if v < 253402300800_000000 {
+ return TimeFormatUnixMicro.Decode(v)
+ }
+ return TimeFormatUnixNano.Decode(v)
+ default:
+ return time.Time{}, util.TimeErr
+ }
+
+ case
+ TimeFormat2, TimeFormat2TZ,
+ TimeFormat3, TimeFormat3TZ,
+ TimeFormat4, TimeFormat4TZ,
+ TimeFormat5, TimeFormat5TZ,
+ TimeFormat6, TimeFormat6TZ,
+ TimeFormat7, TimeFormat7TZ:
+ s, ok := v.(string)
+ if !ok {
+ return time.Time{}, util.TimeErr
+ }
+ return f.parseRelaxed(s)
+
+ case
+ TimeFormat8, TimeFormat8TZ,
+ TimeFormat9, TimeFormat9TZ,
+ TimeFormat10, TimeFormat10TZ:
+ s, ok := v.(string)
+ if !ok {
+ return time.Time{}, util.TimeErr
+ }
+ t, err := f.parseRelaxed(s)
+ if err != nil {
+ return time.Time{}, err
+ }
+ return t.AddDate(2000, 0, 0), nil
+
+ default:
+ s, ok := v.(string)
+ if !ok {
+ return time.Time{}, util.TimeErr
+ }
+ if f == "" {
+ f = time.RFC3339Nano
+ }
+ return time.Parse(string(f), s)
+ }
+}
+
+func (f TimeFormat) parseRelaxed(s string) (time.Time, error) {
+ fs := string(f)
+ fs = strings.TrimSuffix(fs, "Z07:00")
+ fs = strings.TrimSuffix(fs, ".000")
+ t, err := time.Parse(fs+"Z07:00", s)
+ if err != nil {
+ return time.Parse(fs, s)
+ }
+ return t, nil
+}
+
+// Scanner returns a [database/sql.Scanner] that can be used as an argument to
+// [database/sql.Row.Scan] and similar methods to
+// decode a time value into dest using this format.
+func (f TimeFormat) Scanner(dest *time.Time) interface{ Scan(any) error } {
+ return timeScanner{dest, f}
+}
+
+type timeScanner struct {
+ *time.Time
+ TimeFormat
+}
+
+func (s timeScanner) Scan(src any) error {
+ var ok bool
+ var err error
+ if *s.Time, ok = src.(time.Time); !ok {
+ *s.Time, err = s.Decode(src)
+ }
+ return err
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/txn.go b/vendor/github.com/ncruces/go-sqlite3/txn.go
new file mode 100644
index 000000000..0efbc2d80
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/txn.go
@@ -0,0 +1,294 @@
+package sqlite3
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/rand"
+ "runtime"
+ "strconv"
+ "strings"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/tetratelabs/wazero/api"
+)
+
+// Txn is an in-progress database transaction.
+//
+// https://sqlite.org/lang_transaction.html
+type Txn struct {
+ c *Conn
+}
+
+// Begin starts a deferred transaction.
+//
+// https://sqlite.org/lang_transaction.html
+func (c *Conn) Begin() Txn {
+ // BEGIN even if interrupted.
+ err := c.txnExecInterrupted(`BEGIN DEFERRED`)
+ if err != nil {
+ panic(err)
+ }
+ return Txn{c}
+}
+
+// BeginImmediate starts an immediate transaction.
+//
+// https://sqlite.org/lang_transaction.html
+func (c *Conn) BeginImmediate() (Txn, error) {
+ err := c.Exec(`BEGIN IMMEDIATE`)
+ if err != nil {
+ return Txn{}, err
+ }
+ return Txn{c}, nil
+}
+
+// BeginExclusive starts an exclusive transaction.
+//
+// https://sqlite.org/lang_transaction.html
+func (c *Conn) BeginExclusive() (Txn, error) {
+ err := c.Exec(`BEGIN EXCLUSIVE`)
+ if err != nil {
+ return Txn{}, err
+ }
+ return Txn{c}, nil
+}
+
+// End calls either [Txn.Commit] or [Txn.Rollback]
+// depending on whether *error points to a nil or non-nil error.
+//
+// This is meant to be deferred:
+//
+// func doWork(db *sqlite3.Conn) (err error) {
+// tx := db.Begin()
+// defer tx.End(&err)
+//
+// // ... do work in the transaction
+// }
+//
+// https://sqlite.org/lang_transaction.html
+func (tx Txn) End(errp *error) {
+ recovered := recover()
+ if recovered != nil {
+ defer panic(recovered)
+ }
+
+ if *errp == nil && recovered == nil {
+ // Success path.
+ if tx.c.GetAutocommit() { // There is nothing to commit.
+ return
+ }
+ *errp = tx.Commit()
+ if *errp == nil {
+ return
+ }
+ // Fall through to the error path.
+ }
+
+ // Error path.
+ if tx.c.GetAutocommit() { // There is nothing to rollback.
+ return
+ }
+ err := tx.Rollback()
+ if err != nil {
+ panic(err)
+ }
+}
+
+// Commit commits the transaction.
+//
+// https://sqlite.org/lang_transaction.html
+func (tx Txn) Commit() error {
+ return tx.c.Exec(`COMMIT`)
+}
+
+// Rollback rolls back the transaction,
+// even if the connection has been interrupted.
+//
+// https://sqlite.org/lang_transaction.html
+func (tx Txn) Rollback() error {
+ return tx.c.txnExecInterrupted(`ROLLBACK`)
+}
+
+// Savepoint is a marker within a transaction
+// that allows for partial rollback.
+//
+// https://sqlite.org/lang_savepoint.html
+type Savepoint struct {
+ c *Conn
+ name string
+}
+
+// Savepoint establishes a new transaction savepoint.
+//
+// https://sqlite.org/lang_savepoint.html
+func (c *Conn) Savepoint() Savepoint {
+ // Names can be reused; this makes catching bugs more likely.
+ name := saveptName() + "_" + strconv.Itoa(int(rand.Int31()))
+
+ err := c.txnExecInterrupted(fmt.Sprintf("SAVEPOINT %q;", name))
+ if err != nil {
+ panic(err)
+ }
+ return Savepoint{c: c, name: name}
+}
+
+func saveptName() (name string) {
+ defer func() {
+ if name == "" {
+ name = "sqlite3.Savepoint"
+ }
+ }()
+
+ var pc [8]uintptr
+ n := runtime.Callers(3, pc[:])
+ if n <= 0 {
+ return ""
+ }
+ frames := runtime.CallersFrames(pc[:n])
+ frame, more := frames.Next()
+ for more && (strings.HasPrefix(frame.Function, "database/sql.") ||
+ strings.HasPrefix(frame.Function, "github.com/ncruces/go-sqlite3/driver.")) {
+ frame, more = frames.Next()
+ }
+ return frame.Function
+}
+
+// Release releases the savepoint rolling back any changes
+// if *error points to a non-nil error.
+//
+// This is meant to be deferred:
+//
+// func doWork(db *sqlite3.Conn) (err error) {
+// savept := db.Savepoint()
+// defer savept.Release(&err)
+//
+// // ... do work in the transaction
+// }
+func (s Savepoint) Release(errp *error) {
+ recovered := recover()
+ if recovered != nil {
+ defer panic(recovered)
+ }
+
+ if *errp == nil && recovered == nil {
+ // Success path.
+ if s.c.GetAutocommit() { // There is nothing to commit.
+ return
+ }
+ *errp = s.c.Exec(fmt.Sprintf("RELEASE %q;", s.name))
+ if *errp == nil {
+ return
+ }
+ // Fall through to the error path.
+ }
+
+ // Error path.
+ if s.c.GetAutocommit() { // There is nothing to rollback.
+ return
+ }
+ // ROLLBACK and RELEASE even if interrupted.
+ err := s.c.txnExecInterrupted(fmt.Sprintf(`
+ ROLLBACK TO %[1]q;
+ RELEASE %[1]q;
+ `, s.name))
+ if err != nil {
+ panic(err)
+ }
+}
+
+// Rollback rolls the transaction back to the savepoint,
+// even if the connection has been interrupted.
+// Rollback does not release the savepoint.
+//
+// https://sqlite.org/lang_transaction.html
+func (s Savepoint) Rollback() error {
+ // ROLLBACK even if interrupted.
+ return s.c.txnExecInterrupted(fmt.Sprintf("ROLLBACK TO %q;", s.name))
+}
+
+func (c *Conn) txnExecInterrupted(sql string) error {
+ err := c.Exec(sql)
+ if errors.Is(err, INTERRUPT) {
+ old := c.SetInterrupt(context.Background())
+ defer c.SetInterrupt(old)
+ err = c.Exec(sql)
+ }
+ return err
+}
+
+// TxnState starts a deferred transaction.
+//
+// https://sqlite.org/c3ref/txn_state.html
+func (c *Conn) TxnState(schema string) TxnState {
+ var ptr uint32
+ if schema != "" {
+ defer c.arena.mark()()
+ ptr = c.arena.string(schema)
+ }
+ r := c.call("sqlite3_txn_state", uint64(c.handle), uint64(ptr))
+ return TxnState(r)
+}
+
+// CommitHook registers a callback function to be invoked
+// whenever a transaction is committed.
+// Return true to allow the commit operation to continue normally.
+//
+// https://sqlite.org/c3ref/commit_hook.html
+func (c *Conn) CommitHook(cb func() (ok bool)) {
+ var enable uint64
+ if cb != nil {
+ enable = 1
+ }
+ c.call("sqlite3_commit_hook_go", uint64(c.handle), enable)
+ c.commit = cb
+}
+
+// RollbackHook registers a callback function to be invoked
+// whenever a transaction is rolled back.
+//
+// https://sqlite.org/c3ref/commit_hook.html
+func (c *Conn) RollbackHook(cb func()) {
+ var enable uint64
+ if cb != nil {
+ enable = 1
+ }
+ c.call("sqlite3_rollback_hook_go", uint64(c.handle), enable)
+ c.rollback = cb
+}
+
+// UpdateHook registers a callback function to be invoked
+// whenever a row is updated, inserted or deleted in a rowid table.
+//
+// https://sqlite.org/c3ref/update_hook.html
+func (c *Conn) UpdateHook(cb func(action AuthorizerActionCode, schema, table string, rowid int64)) {
+ var enable uint64
+ if cb != nil {
+ enable = 1
+ }
+ c.call("sqlite3_update_hook_go", uint64(c.handle), enable)
+ c.update = cb
+}
+
+func commitCallback(ctx context.Context, mod api.Module, pDB uint32) (rollback uint32) {
+ if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.commit != nil {
+ if !c.commit() {
+ rollback = 1
+ }
+ }
+ return rollback
+}
+
+func rollbackCallback(ctx context.Context, mod api.Module, pDB uint32) {
+ if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.rollback != nil {
+ c.rollback()
+ }
+}
+
+func updateCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zSchema, zTabName uint32, rowid uint64) {
+ if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.update != nil {
+ schema := util.ReadString(mod, zSchema, _MAX_NAME)
+ table := util.ReadString(mod, zTabName, _MAX_NAME)
+ c.update(action, schema, table, int64(rowid))
+ }
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/util/osutil/open.go b/vendor/github.com/ncruces/go-sqlite3/util/osutil/open.go
new file mode 100644
index 000000000..0242ad032
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/util/osutil/open.go
@@ -0,0 +1,16 @@
+//go:build !windows
+
+package osutil
+
+import (
+ "io/fs"
+ "os"
+)
+
+// OpenFile behaves the same as [os.OpenFile],
+// except on Windows it sets [syscall.FILE_SHARE_DELETE].
+//
+// See: https://go.dev/issue/32088#issuecomment-502850674
+func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
+ return os.OpenFile(name, flag, perm)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/util/osutil/open_windows.go b/vendor/github.com/ncruces/go-sqlite3/util/osutil/open_windows.go
new file mode 100644
index 000000000..277f58bc3
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/util/osutil/open_windows.go
@@ -0,0 +1,112 @@
+package osutil
+
+import (
+ "io/fs"
+ "os"
+ . "syscall"
+ "unsafe"
+)
+
+// OpenFile behaves the same as [os.OpenFile],
+// except on Windows it sets [syscall.FILE_SHARE_DELETE].
+//
+// See: https://go.dev/issue/32088#issuecomment-502850674
+func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
+ if name == "" {
+ return nil, &os.PathError{Op: "open", Path: name, Err: ENOENT}
+ }
+ r, e := syscallOpen(name, flag, uint32(perm.Perm()))
+ if e != nil {
+ return nil, &os.PathError{Op: "open", Path: name, Err: e}
+ }
+ return os.NewFile(uintptr(r), name), nil
+}
+
+// syscallOpen is a copy of [syscall.Open]
+// that uses [syscall.FILE_SHARE_DELETE].
+//
+// https://go.dev/src/syscall/syscall_windows.go
+func syscallOpen(path string, mode int, perm uint32) (fd Handle, err error) {
+ if len(path) == 0 {
+ return InvalidHandle, ERROR_FILE_NOT_FOUND
+ }
+ pathp, err := UTF16PtrFromString(path)
+ if err != nil {
+ return InvalidHandle, err
+ }
+ var access uint32
+ switch mode & (O_RDONLY | O_WRONLY | O_RDWR) {
+ case O_RDONLY:
+ access = GENERIC_READ
+ case O_WRONLY:
+ access = GENERIC_WRITE
+ case O_RDWR:
+ access = GENERIC_READ | GENERIC_WRITE
+ }
+ if mode&O_CREAT != 0 {
+ access |= GENERIC_WRITE
+ }
+ if mode&O_APPEND != 0 {
+ access &^= GENERIC_WRITE
+ access |= FILE_APPEND_DATA
+ }
+ sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
+ var sa *SecurityAttributes
+ if mode&O_CLOEXEC == 0 {
+ sa = makeInheritSa()
+ }
+ var createmode uint32
+ switch {
+ case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
+ createmode = CREATE_NEW
+ case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
+ createmode = CREATE_ALWAYS
+ case mode&O_CREAT == O_CREAT:
+ createmode = OPEN_ALWAYS
+ case mode&O_TRUNC == O_TRUNC:
+ createmode = TRUNCATE_EXISTING
+ default:
+ createmode = OPEN_EXISTING
+ }
+ var attrs uint32 = FILE_ATTRIBUTE_NORMAL
+ if perm&S_IWRITE == 0 {
+ attrs = FILE_ATTRIBUTE_READONLY
+ if createmode == CREATE_ALWAYS {
+ const _ERROR_BAD_NETPATH = Errno(53)
+ // We have been asked to create a read-only file.
+ // If the file already exists, the semantics of
+ // the Unix open system call is to preserve the
+ // existing permissions. If we pass CREATE_ALWAYS
+ // and FILE_ATTRIBUTE_READONLY to CreateFile,
+ // and the file already exists, CreateFile will
+ // change the file permissions.
+ // Avoid that to preserve the Unix semantics.
+ h, e := CreateFile(pathp, access, sharemode, sa, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
+ switch e {
+ case ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, ERROR_PATH_NOT_FOUND:
+ // File does not exist. These are the same
+ // errors as Errno.Is checks for ErrNotExist.
+ // Carry on to create the file.
+ default:
+ // Success or some different error.
+ return h, e
+ }
+ }
+ }
+ if createmode == OPEN_EXISTING && access == GENERIC_READ {
+ // Necessary for opening directory handles.
+ attrs |= FILE_FLAG_BACKUP_SEMANTICS
+ }
+ if mode&O_SYNC != 0 {
+ const _FILE_FLAG_WRITE_THROUGH = 0x80000000
+ attrs |= _FILE_FLAG_WRITE_THROUGH
+ }
+ return CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
+}
+
+func makeInheritSa() *SecurityAttributes {
+ var sa SecurityAttributes
+ sa.Length = uint32(unsafe.Sizeof(sa))
+ sa.InheritHandle = 1
+ return &sa
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/util/osutil/osfs.go b/vendor/github.com/ncruces/go-sqlite3/util/osutil/osfs.go
new file mode 100644
index 000000000..2e1195934
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/util/osutil/osfs.go
@@ -0,0 +1,33 @@
+package osutil
+
+import (
+ "io/fs"
+ "os"
+)
+
+// FS implements [fs.FS], [fs.StatFS], and [fs.ReadFileFS]
+// using package [os].
+//
+// This filesystem does not respect [fs.ValidPath] rules,
+// and fails [testing/fstest.TestFS]!
+//
+// Still, it can be a useful tool to unify implementations
+// that can access either the [os] filesystem or an [fs.FS].
+// It's OK to use this to open files, but you should avoid
+// opening directories, resolving paths, or walking the file system.
+type FS struct{}
+
+// Open implements [fs.FS].
+func (FS) Open(name string) (fs.File, error) {
+ return OpenFile(name, os.O_RDONLY, 0)
+}
+
+// ReadFileFS implements [fs.StatFS].
+func (FS) Stat(name string) (fs.FileInfo, error) {
+ return os.Stat(name)
+}
+
+// ReadFile implements [fs.ReadFileFS].
+func (FS) ReadFile(name string) ([]byte, error) {
+ return os.ReadFile(name)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go b/vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go
new file mode 100644
index 000000000..7fbd04787
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go
@@ -0,0 +1,2 @@
+// Package osutil implements operating system utility functions.
+package osutil
diff --git a/vendor/github.com/ncruces/go-sqlite3/value.go b/vendor/github.com/ncruces/go-sqlite3/value.go
new file mode 100644
index 000000000..61d3cbf70
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/value.go
@@ -0,0 +1,236 @@
+package sqlite3
+
+import (
+ "encoding/json"
+ "math"
+ "strconv"
+ "time"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+// Value is any value that can be stored in a database table.
+//
+// https://sqlite.org/c3ref/value.html
+type Value struct {
+ c *Conn
+ handle uint32
+ unprot bool
+ copied bool
+}
+
+func (v Value) protected() uint64 {
+ if v.unprot {
+ panic(util.ValueErr)
+ }
+ return uint64(v.handle)
+}
+
+// Dup makes a copy of the SQL value and returns a pointer to that copy.
+//
+// https://sqlite.org/c3ref/value_dup.html
+func (v Value) Dup() *Value {
+ r := v.c.call("sqlite3_value_dup", uint64(v.handle))
+ return &Value{
+ c: v.c,
+ copied: true,
+ handle: uint32(r),
+ }
+}
+
+// Close frees an SQL value previously obtained by [Value.Dup].
+//
+// https://sqlite.org/c3ref/value_dup.html
+func (dup *Value) Close() error {
+ if !dup.copied {
+ panic(util.ValueErr)
+ }
+ dup.c.call("sqlite3_value_free", uint64(dup.handle))
+ dup.handle = 0
+ return nil
+}
+
+// Type returns the initial datatype of the value.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) Type() Datatype {
+ r := v.c.call("sqlite3_value_type", v.protected())
+ return Datatype(r)
+}
+
+// Type returns the numeric datatype of the value.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) NumericType() Datatype {
+ r := v.c.call("sqlite3_value_numeric_type", v.protected())
+ return Datatype(r)
+}
+
+// Bool returns the value as a bool.
+// SQLite does not have a separate boolean storage class.
+// Instead, boolean values are retrieved as integers,
+// with 0 converted to false and any other value to true.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) Bool() bool {
+ return v.Int64() != 0
+}
+
+// Int returns the value as an int.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) Int() int {
+ return int(v.Int64())
+}
+
+// Int64 returns the value as an int64.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) Int64() int64 {
+ r := v.c.call("sqlite3_value_int64", v.protected())
+ return int64(r)
+}
+
+// Float returns the value as a float64.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) Float() float64 {
+ r := v.c.call("sqlite3_value_double", v.protected())
+ return math.Float64frombits(r)
+}
+
+// Time returns the value as a [time.Time].
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) Time(format TimeFormat) time.Time {
+ var a any
+ switch v.Type() {
+ case INTEGER:
+ a = v.Int64()
+ case FLOAT:
+ a = v.Float()
+ case TEXT, BLOB:
+ a = v.Text()
+ case NULL:
+ return time.Time{}
+ default:
+ panic(util.AssertErr())
+ }
+ t, _ := format.Decode(a)
+ return t
+}
+
+// Text returns the value as a string.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) Text() string {
+ return string(v.RawText())
+}
+
+// Blob appends to buf and returns
+// the value as a []byte.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) Blob(buf []byte) []byte {
+ return append(buf, v.RawBlob()...)
+}
+
+// RawText returns the value as a []byte.
+// The []byte is owned by SQLite and may be invalidated by
+// subsequent calls to [Value] methods.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) RawText() []byte {
+ r := v.c.call("sqlite3_value_text", v.protected())
+ return v.rawBytes(uint32(r))
+}
+
+// RawBlob returns the value as a []byte.
+// The []byte is owned by SQLite and may be invalidated by
+// subsequent calls to [Value] methods.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) RawBlob() []byte {
+ r := v.c.call("sqlite3_value_blob", v.protected())
+ return v.rawBytes(uint32(r))
+}
+
+func (v Value) rawBytes(ptr uint32) []byte {
+ if ptr == 0 {
+ return nil
+ }
+
+ r := v.c.call("sqlite3_value_bytes", v.protected())
+ return util.View(v.c.mod, ptr, r)
+}
+
+// Pointer gets the pointer associated with this value,
+// or nil if it has no associated pointer.
+func (v Value) Pointer() any {
+ r := v.c.call("sqlite3_value_pointer_go", v.protected())
+ return util.GetHandle(v.c.ctx, uint32(r))
+}
+
+// JSON parses a JSON-encoded value
+// and stores the result in the value pointed to by ptr.
+func (v Value) JSON(ptr any) error {
+ var data []byte
+ switch v.Type() {
+ case NULL:
+ data = append(data, "null"...)
+ case TEXT:
+ data = v.RawText()
+ case BLOB:
+ data = v.RawBlob()
+ case INTEGER:
+ data = strconv.AppendInt(nil, v.Int64(), 10)
+ case FLOAT:
+ data = strconv.AppendFloat(nil, v.Float(), 'g', -1, 64)
+ default:
+ panic(util.AssertErr())
+ }
+ return json.Unmarshal(data, ptr)
+}
+
+// NoChange returns true if and only if the value is unchanged
+// in a virtual table update operatiom.
+//
+// https://sqlite.org/c3ref/value_blob.html
+func (v Value) NoChange() bool {
+ r := v.c.call("sqlite3_value_nochange", v.protected())
+ return r != 0
+}
+
+// InFirst returns the first element
+// on the right-hand side of an IN constraint.
+//
+// https://sqlite.org/c3ref/vtab_in_first.html
+func (v Value) InFirst() (Value, error) {
+ defer v.c.arena.mark()()
+ valPtr := v.c.arena.new(ptrlen)
+ r := v.c.call("sqlite3_vtab_in_first", uint64(v.handle), uint64(valPtr))
+ if err := v.c.error(r); err != nil {
+ return Value{}, err
+ }
+ return Value{
+ c: v.c,
+ handle: util.ReadUint32(v.c.mod, valPtr),
+ }, nil
+}
+
+// InNext returns the next element
+// on the right-hand side of an IN constraint.
+//
+// https://sqlite.org/c3ref/vtab_in_first.html
+func (v Value) InNext() (Value, error) {
+ defer v.c.arena.mark()()
+ valPtr := v.c.arena.new(ptrlen)
+ r := v.c.call("sqlite3_vtab_in_next", uint64(v.handle), uint64(valPtr))
+ if err := v.c.error(r); err != nil {
+ return Value{}, err
+ }
+ return Value{
+ c: v.c,
+ handle: util.ReadUint32(v.c.mod, valPtr),
+ }, nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/README.md b/vendor/github.com/ncruces/go-sqlite3/vfs/README.md
new file mode 100644
index 000000000..88059a41b
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/README.md
@@ -0,0 +1,86 @@
+# Go SQLite VFS API
+
+This package implements the SQLite [OS Interface](https://sqlite.org/vfs.html) (aka VFS).
+
+It replaces the default SQLite VFS with a **pure Go** implementation,
+and exposes [interfaces](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#VFS)
+that should allow you to implement your own custom VFSes.
+
+Since it is a from scratch reimplementation,
+there are naturally some ways it deviates from the original.
+
+The main differences are [file locking](#file-locking) and [WAL mode](#write-ahead-logging) support.
+
+### File Locking
+
+POSIX advisory locks, which SQLite uses on Unix, are
+[broken by design](https://github.com/sqlite/sqlite/blob/b74eb0/src/os_unix.c#L1073-L1161).
+
+On Linux and macOS, this module uses
+[OFD locks](https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html)
+to synchronize access to database files.
+OFD locks are fully compatible with POSIX advisory locks.
+
+This module can also use
+[BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2),
+albeit with reduced concurrency (`BEGIN IMMEDIATE` behaves like `BEGIN EXCLUSIVE`).
+On BSD, macOS, and illumos, BSD locks are fully compatible with POSIX advisory locks;
+on Linux and z/OS, they are fully functional, but incompatible;
+elsewhere, they are very likely broken.
+BSD locks are the default on BSD and illumos,
+but you can opt into them with the `sqlite3_flock` build tag.
+
+On Windows, this module uses `LockFileEx` and `UnlockFileEx`,
+like SQLite.
+
+Otherwise, file locking is not supported, and you must use
+[`nolock=1`](https://sqlite.org/uri.html#urinolock)
+(or [`immutable=1`](https://sqlite.org/uri.html#uriimmutable))
+to open database files.
+To use the [`database/sql`](https://pkg.go.dev/database/sql) driver
+with `nolock=1` you must disable connection pooling by calling
+[`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns).
+
+You can use [`vfs.SupportsFileLocking`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#SupportsFileLocking)
+to check if your build supports file locking.
+
+### Write-Ahead Logging
+
+On 64-bit Linux and macOS, this module uses `mmap` to implement
+[shared-memory for the WAL-index](https://sqlite.org/wal.html#implementation_of_shared_memory_for_the_wal_index),
+like SQLite.
+
+To allow `mmap` to work, each connection needs to reserve up to 4GB of address space.
+To limit the address space each connection reserves,
+use [`WithMemoryLimitPages`](../tests/testcfg/testcfg.go).
+
+Otherwise, [WAL support is limited](https://sqlite.org/wal.html#noshm),
+and `EXCLUSIVE` locking mode must be set to create, read, and write WAL databases.
+To use `EXCLUSIVE` locking mode with the
+[`database/sql`](https://pkg.go.dev/database/sql) driver
+you must disable connection pooling by calling
+[`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns).
+
+You can use [`vfs.SupportsSharedMemory`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#SupportsSharedMemory)
+to check if your build supports shared memory.
+
+### Batch-Atomic Write
+
+On 64-bit Linux, this module supports [batch-atomic writes](https://sqlite.org/cgi/src/technote/714)
+on the F2FS filesystem.
+
+### Build Tags
+
+The VFS can be customized with a few build tags:
+- `sqlite3_flock` forces the use of BSD locks; it can be used on z/OS to enable locking,
+ and elsewhere to test BSD locks.
+- `sqlite3_nosys` prevents importing [`x/sys`](https://pkg.go.dev/golang.org/x/sys);
+ disables locking _and_ shared memory on all platforms.
+- `sqlite3_noshm` disables shared memory on all platforms.
+
+> [!IMPORTANT]
+> The default configuration of this package is compatible with
+> the standard [Unix and Windows SQLite VFSes](https://sqlite.org/vfs.html#multiple_vfses);
+> `sqlite3_flock` is compatible with the [`unix-flock` VFS](https://sqlite.org/compile.html#enable_locking_style).
+> If incompatible file locking is used, accessing databases concurrently with _other_ SQLite libraries
+> will eventually corrupt data.
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/api.go b/vendor/github.com/ncruces/go-sqlite3/vfs/api.go
new file mode 100644
index 000000000..19c22ae8f
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/api.go
@@ -0,0 +1,175 @@
+// Package vfs wraps the C SQLite VFS API.
+package vfs
+
+import (
+ "context"
+ "io"
+
+ "github.com/tetratelabs/wazero/api"
+)
+
+// A VFS defines the interface between the SQLite core and the underlying operating system.
+//
+// Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite.
+//
+// https://sqlite.org/c3ref/vfs.html
+type VFS interface {
+ Open(name string, flags OpenFlag) (File, OpenFlag, error)
+ Delete(name string, syncDir bool) error
+ Access(name string, flags AccessFlag) (bool, error)
+ FullPathname(name string) (string, error)
+}
+
+// VFSFilename extends VFS with the ability to use Filename
+// objects for opening files.
+//
+// https://sqlite.org/c3ref/filename.html
+type VFSFilename interface {
+ VFS
+ OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error)
+}
+
+// A File represents an open file in the OS interface layer.
+//
+// Use sqlite3.ErrorCode or sqlite3.ExtendedErrorCode to return specific error codes to SQLite.
+// In particular, sqlite3.BUSY is necessary to correctly implement lock methods.
+//
+// https://sqlite.org/c3ref/io_methods.html
+type File interface {
+ Close() error
+ ReadAt(p []byte, off int64) (n int, err error)
+ WriteAt(p []byte, off int64) (n int, err error)
+ Truncate(size int64) error
+ Sync(flags SyncFlag) error
+ Size() (int64, error)
+ Lock(lock LockLevel) error
+ Unlock(lock LockLevel) error
+ CheckReservedLock() (bool, error)
+ SectorSize() int
+ DeviceCharacteristics() DeviceCharacteristic
+}
+
+// FileLockState extends File to implement the
+// SQLITE_FCNTL_LOCKSTATE file control opcode.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntllockstate
+type FileLockState interface {
+ File
+ LockState() LockLevel
+}
+
+// FileChunkSize extends File to implement the
+// SQLITE_FCNTL_CHUNK_SIZE file control opcode.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlchunksize
+type FileChunkSize interface {
+ File
+ ChunkSize(size int)
+}
+
+// FileSizeHint extends File to implement the
+// SQLITE_FCNTL_SIZE_HINT file control opcode.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlsizehint
+type FileSizeHint interface {
+ File
+ SizeHint(size int64) error
+}
+
+// FileHasMoved extends File to implement the
+// SQLITE_FCNTL_HAS_MOVED file control opcode.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlhasmoved
+type FileHasMoved interface {
+ File
+ HasMoved() (bool, error)
+}
+
+// FileOverwrite extends File to implement the
+// SQLITE_FCNTL_OVERWRITE file control opcode.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntloverwrite
+type FileOverwrite interface {
+ File
+ Overwrite() error
+}
+
+// FilePersistentWAL extends File to implement the
+// SQLITE_FCNTL_PERSIST_WAL file control opcode.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpersistwal
+type FilePersistentWAL interface {
+ File
+ PersistentWAL() bool
+ SetPersistentWAL(bool)
+}
+
+// FilePowersafeOverwrite extends File to implement the
+// SQLITE_FCNTL_POWERSAFE_OVERWRITE file control opcode.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpowersafeoverwrite
+type FilePowersafeOverwrite interface {
+ File
+ PowersafeOverwrite() bool
+ SetPowersafeOverwrite(bool)
+}
+
+// FileCommitPhaseTwo extends File to implement the
+// SQLITE_FCNTL_COMMIT_PHASETWO file control opcode.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlcommitphasetwo
+type FileCommitPhaseTwo interface {
+ File
+ CommitPhaseTwo() error
+}
+
+// FileBatchAtomicWrite extends File to implement the
+// SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE
+// and SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE file control opcodes.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlbeginatomicwrite
+type FileBatchAtomicWrite interface {
+ File
+ BeginAtomicWrite() error
+ CommitAtomicWrite() error
+ RollbackAtomicWrite() error
+}
+
+// FilePragma extends File to implement the
+// SQLITE_FCNTL_PRAGMA file control opcode.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpragma
+type FilePragma interface {
+ File
+ Pragma(name, value string) (string, error)
+}
+
+// FileCheckpoint extends File to implement the
+// SQLITE_FCNTL_CKPT_START and SQLITE_FCNTL_CKPT_DONE
+// file control opcodes.
+//
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlckptstart
+type FileCheckpoint interface {
+ File
+ CheckpointDone() error
+ CheckpointStart() error
+}
+
+// FileSharedMemory extends File to possibly implement
+// shared-memory for the WAL-index.
+// The same shared-memory instance must be returned
+// for the entire life of the file.
+// It's OK for SharedMemory to return nil.
+type FileSharedMemory interface {
+ File
+ SharedMemory() SharedMemory
+}
+
+// SharedMemory is a shared-memory WAL-index implementation.
+// Use [NewSharedMemory] to create a shared-memory.
+type SharedMemory interface {
+ shmMap(context.Context, api.Module, int32, int32, bool) (uint32, error)
+ shmLock(int32, int32, _ShmFlag) error
+ shmUnmap(bool)
+ io.Closer
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/const.go b/vendor/github.com/ncruces/go-sqlite3/vfs/const.go
new file mode 100644
index 000000000..7f409f35f
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/const.go
@@ -0,0 +1,234 @@
+package vfs
+
+import "github.com/ncruces/go-sqlite3/internal/util"
+
+const (
+ _MAX_NAME = 1e6 // Self-imposed limit for most NUL terminated strings.
+ _MAX_SQL_LENGTH = 1e9
+ _MAX_PATHNAME = 1024
+ _DEFAULT_SECTOR_SIZE = 4096
+
+ ptrlen = 4
+)
+
+// https://sqlite.org/rescode.html
+type _ErrorCode uint32
+
+func (e _ErrorCode) Error() string {
+ return util.ErrorCodeString(uint32(e))
+}
+
+const (
+ _OK _ErrorCode = util.OK
+ _ERROR _ErrorCode = util.ERROR
+ _PERM _ErrorCode = util.PERM
+ _BUSY _ErrorCode = util.BUSY
+ _READONLY _ErrorCode = util.READONLY
+ _IOERR _ErrorCode = util.IOERR
+ _NOTFOUND _ErrorCode = util.NOTFOUND
+ _CANTOPEN _ErrorCode = util.CANTOPEN
+ _IOERR_READ _ErrorCode = util.IOERR_READ
+ _IOERR_SHORT_READ _ErrorCode = util.IOERR_SHORT_READ
+ _IOERR_WRITE _ErrorCode = util.IOERR_WRITE
+ _IOERR_FSYNC _ErrorCode = util.IOERR_FSYNC
+ _IOERR_DIR_FSYNC _ErrorCode = util.IOERR_DIR_FSYNC
+ _IOERR_TRUNCATE _ErrorCode = util.IOERR_TRUNCATE
+ _IOERR_FSTAT _ErrorCode = util.IOERR_FSTAT
+ _IOERR_UNLOCK _ErrorCode = util.IOERR_UNLOCK
+ _IOERR_RDLOCK _ErrorCode = util.IOERR_RDLOCK
+ _IOERR_DELETE _ErrorCode = util.IOERR_DELETE
+ _IOERR_ACCESS _ErrorCode = util.IOERR_ACCESS
+ _IOERR_CHECKRESERVEDLOCK _ErrorCode = util.IOERR_CHECKRESERVEDLOCK
+ _IOERR_LOCK _ErrorCode = util.IOERR_LOCK
+ _IOERR_CLOSE _ErrorCode = util.IOERR_CLOSE
+ _IOERR_SHMOPEN _ErrorCode = util.IOERR_SHMOPEN
+ _IOERR_SHMSIZE _ErrorCode = util.IOERR_SHMSIZE
+ _IOERR_SHMLOCK _ErrorCode = util.IOERR_SHMLOCK
+ _IOERR_SHMMAP _ErrorCode = util.IOERR_SHMMAP
+ _IOERR_SEEK _ErrorCode = util.IOERR_SEEK
+ _IOERR_DELETE_NOENT _ErrorCode = util.IOERR_DELETE_NOENT
+ _IOERR_BEGIN_ATOMIC _ErrorCode = util.IOERR_BEGIN_ATOMIC
+ _IOERR_COMMIT_ATOMIC _ErrorCode = util.IOERR_COMMIT_ATOMIC
+ _IOERR_ROLLBACK_ATOMIC _ErrorCode = util.IOERR_ROLLBACK_ATOMIC
+ _CANTOPEN_FULLPATH _ErrorCode = util.CANTOPEN_FULLPATH
+ _CANTOPEN_ISDIR _ErrorCode = util.CANTOPEN_ISDIR
+ _READONLY_CANTINIT _ErrorCode = util.READONLY_CANTINIT
+ _OK_SYMLINK _ErrorCode = util.OK_SYMLINK
+)
+
+// OpenFlag is a flag for the [VFS] Open method.
+//
+// https://sqlite.org/c3ref/c_open_autoproxy.html
+type OpenFlag uint32
+
+const (
+ OPEN_READONLY OpenFlag = 0x00000001 /* Ok for sqlite3_open_v2() */
+ OPEN_READWRITE OpenFlag = 0x00000002 /* Ok for sqlite3_open_v2() */
+ OPEN_CREATE OpenFlag = 0x00000004 /* Ok for sqlite3_open_v2() */
+ OPEN_DELETEONCLOSE OpenFlag = 0x00000008 /* VFS only */
+ OPEN_EXCLUSIVE OpenFlag = 0x00000010 /* VFS only */
+ OPEN_AUTOPROXY OpenFlag = 0x00000020 /* VFS only */
+ OPEN_URI OpenFlag = 0x00000040 /* Ok for sqlite3_open_v2() */
+ OPEN_MEMORY OpenFlag = 0x00000080 /* Ok for sqlite3_open_v2() */
+ OPEN_MAIN_DB OpenFlag = 0x00000100 /* VFS only */
+ OPEN_TEMP_DB OpenFlag = 0x00000200 /* VFS only */
+ OPEN_TRANSIENT_DB OpenFlag = 0x00000400 /* VFS only */
+ OPEN_MAIN_JOURNAL OpenFlag = 0x00000800 /* VFS only */
+ OPEN_TEMP_JOURNAL OpenFlag = 0x00001000 /* VFS only */
+ OPEN_SUBJOURNAL OpenFlag = 0x00002000 /* VFS only */
+ OPEN_SUPER_JOURNAL OpenFlag = 0x00004000 /* VFS only */
+ OPEN_NOMUTEX OpenFlag = 0x00008000 /* Ok for sqlite3_open_v2() */
+ OPEN_FULLMUTEX OpenFlag = 0x00010000 /* Ok for sqlite3_open_v2() */
+ OPEN_SHAREDCACHE OpenFlag = 0x00020000 /* Ok for sqlite3_open_v2() */
+ OPEN_PRIVATECACHE OpenFlag = 0x00040000 /* Ok for sqlite3_open_v2() */
+ OPEN_WAL OpenFlag = 0x00080000 /* VFS only */
+ OPEN_NOFOLLOW OpenFlag = 0x01000000 /* Ok for sqlite3_open_v2() */
+)
+
+// AccessFlag is a flag for the [VFS] Access method.
+//
+// https://sqlite.org/c3ref/c_access_exists.html
+type AccessFlag uint32
+
+const (
+ ACCESS_EXISTS AccessFlag = 0
+ ACCESS_READWRITE AccessFlag = 1 /* Used by PRAGMA temp_store_directory */
+ ACCESS_READ AccessFlag = 2 /* Unused */
+)
+
+// SyncFlag is a flag for the [File] Sync method.
+//
+// https://sqlite.org/c3ref/c_sync_dataonly.html
+type SyncFlag uint32
+
+const (
+ SYNC_NORMAL SyncFlag = 0x00002
+ SYNC_FULL SyncFlag = 0x00003
+ SYNC_DATAONLY SyncFlag = 0x00010
+)
+
+// LockLevel is a value used with [File] Lock and Unlock methods.
+//
+// https://sqlite.org/c3ref/c_lock_exclusive.html
+type LockLevel uint32
+
+const (
+ // No locks are held on the database.
+ // The database may be neither read nor written.
+ // Any internally cached data is considered suspect and subject to
+ // verification against the database file before being used.
+ // Other processes can read or write the database as their own locking
+ // states permit.
+ // This is the default state.
+ LOCK_NONE LockLevel = 0 /* xUnlock() only */
+
+ // The database may be read but not written.
+ // Any number of processes can hold SHARED locks at the same time,
+ // hence there can be many simultaneous readers.
+ // But no other thread or process is allowed to write to the database file
+ // while one or more SHARED locks are active.
+ LOCK_SHARED LockLevel = 1 /* xLock() or xUnlock() */
+
+ // A RESERVED lock means that the process is planning on writing to the
+ // database file at some point in the future but that it is currently just
+ // reading from the file.
+ // Only a single RESERVED lock may be active at one time,
+ // though multiple SHARED locks can coexist with a single RESERVED lock.
+ // RESERVED differs from PENDING in that new SHARED locks can be acquired
+ // while there is a RESERVED lock.
+ LOCK_RESERVED LockLevel = 2 /* xLock() only */
+
+ // A PENDING lock means that the process holding the lock wants to write to
+ // the database as soon as possible and is just waiting on all current
+ // SHARED locks to clear so that it can get an EXCLUSIVE lock.
+ // No new SHARED locks are permitted against the database if a PENDING lock
+ // is active, though existing SHARED locks are allowed to continue.
+ LOCK_PENDING LockLevel = 3 /* internal use only */
+
+ // An EXCLUSIVE lock is needed in order to write to the database file.
+ // Only one EXCLUSIVE lock is allowed on the file and no other locks of any
+ // kind are allowed to coexist with an EXCLUSIVE lock.
+ // In order to maximize concurrency, SQLite works to minimize the amount of
+ // time that EXCLUSIVE locks are held.
+ LOCK_EXCLUSIVE LockLevel = 4 /* xLock() only */
+)
+
+// DeviceCharacteristic is a flag retuned by the [File] DeviceCharacteristics method.
+//
+// https://sqlite.org/c3ref/c_iocap_atomic.html
+type DeviceCharacteristic uint32
+
+const (
+ IOCAP_ATOMIC DeviceCharacteristic = 0x00000001
+ IOCAP_ATOMIC512 DeviceCharacteristic = 0x00000002
+ IOCAP_ATOMIC1K DeviceCharacteristic = 0x00000004
+ IOCAP_ATOMIC2K DeviceCharacteristic = 0x00000008
+ IOCAP_ATOMIC4K DeviceCharacteristic = 0x00000010
+ IOCAP_ATOMIC8K DeviceCharacteristic = 0x00000020
+ IOCAP_ATOMIC16K DeviceCharacteristic = 0x00000040
+ IOCAP_ATOMIC32K DeviceCharacteristic = 0x00000080
+ IOCAP_ATOMIC64K DeviceCharacteristic = 0x00000100
+ IOCAP_SAFE_APPEND DeviceCharacteristic = 0x00000200
+ IOCAP_SEQUENTIAL DeviceCharacteristic = 0x00000400
+ IOCAP_UNDELETABLE_WHEN_OPEN DeviceCharacteristic = 0x00000800
+ IOCAP_POWERSAFE_OVERWRITE DeviceCharacteristic = 0x00001000
+ IOCAP_IMMUTABLE DeviceCharacteristic = 0x00002000
+ IOCAP_BATCH_ATOMIC DeviceCharacteristic = 0x00004000
+)
+
+// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
+type _FcntlOpcode uint32
+
+const (
+ _FCNTL_LOCKSTATE _FcntlOpcode = 1
+ _FCNTL_GET_LOCKPROXYFILE _FcntlOpcode = 2
+ _FCNTL_SET_LOCKPROXYFILE _FcntlOpcode = 3
+ _FCNTL_LAST_ERRNO _FcntlOpcode = 4
+ _FCNTL_SIZE_HINT _FcntlOpcode = 5
+ _FCNTL_CHUNK_SIZE _FcntlOpcode = 6
+ _FCNTL_FILE_POINTER _FcntlOpcode = 7
+ _FCNTL_SYNC_OMITTED _FcntlOpcode = 8
+ _FCNTL_WIN32_AV_RETRY _FcntlOpcode = 9
+ _FCNTL_PERSIST_WAL _FcntlOpcode = 10
+ _FCNTL_OVERWRITE _FcntlOpcode = 11
+ _FCNTL_VFSNAME _FcntlOpcode = 12
+ _FCNTL_POWERSAFE_OVERWRITE _FcntlOpcode = 13
+ _FCNTL_PRAGMA _FcntlOpcode = 14
+ _FCNTL_BUSYHANDLER _FcntlOpcode = 15
+ _FCNTL_TEMPFILENAME _FcntlOpcode = 16
+ _FCNTL_MMAP_SIZE _FcntlOpcode = 18
+ _FCNTL_TRACE _FcntlOpcode = 19
+ _FCNTL_HAS_MOVED _FcntlOpcode = 20
+ _FCNTL_SYNC _FcntlOpcode = 21
+ _FCNTL_COMMIT_PHASETWO _FcntlOpcode = 22
+ _FCNTL_WIN32_SET_HANDLE _FcntlOpcode = 23
+ _FCNTL_WAL_BLOCK _FcntlOpcode = 24
+ _FCNTL_ZIPVFS _FcntlOpcode = 25
+ _FCNTL_RBU _FcntlOpcode = 26
+ _FCNTL_VFS_POINTER _FcntlOpcode = 27
+ _FCNTL_JOURNAL_POINTER _FcntlOpcode = 28
+ _FCNTL_WIN32_GET_HANDLE _FcntlOpcode = 29
+ _FCNTL_PDB _FcntlOpcode = 30
+ _FCNTL_BEGIN_ATOMIC_WRITE _FcntlOpcode = 31
+ _FCNTL_COMMIT_ATOMIC_WRITE _FcntlOpcode = 32
+ _FCNTL_ROLLBACK_ATOMIC_WRITE _FcntlOpcode = 33
+ _FCNTL_LOCK_TIMEOUT _FcntlOpcode = 34
+ _FCNTL_DATA_VERSION _FcntlOpcode = 35
+ _FCNTL_SIZE_LIMIT _FcntlOpcode = 36
+ _FCNTL_CKPT_DONE _FcntlOpcode = 37
+ _FCNTL_RESERVE_BYTES _FcntlOpcode = 38
+ _FCNTL_CKPT_START _FcntlOpcode = 39
+ _FCNTL_EXTERNAL_READER _FcntlOpcode = 40
+ _FCNTL_CKSM_FILE _FcntlOpcode = 41
+ _FCNTL_RESET_CACHE _FcntlOpcode = 42
+)
+
+// https://sqlite.org/c3ref/c_shm_exclusive.html
+type _ShmFlag uint32
+
+const (
+ _SHM_UNLOCK _ShmFlag = 1
+ _SHM_LOCK _ShmFlag = 2
+ _SHM_SHARED _ShmFlag = 4
+ _SHM_EXCLUSIVE _ShmFlag = 8
+)
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/file.go b/vendor/github.com/ncruces/go-sqlite3/vfs/file.go
new file mode 100644
index 000000000..ca8cf84f3
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/file.go
@@ -0,0 +1,217 @@
+package vfs
+
+import (
+ "errors"
+ "io"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "runtime"
+ "syscall"
+
+ "github.com/ncruces/go-sqlite3/util/osutil"
+)
+
+type vfsOS struct{}
+
+func (vfsOS) FullPathname(path string) (string, error) {
+ path, err := filepath.Abs(path)
+ if err != nil {
+ return "", err
+ }
+ fi, err := os.Lstat(path)
+ if err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ return path, nil
+ }
+ return "", err
+ }
+ if fi.Mode()&fs.ModeSymlink != 0 {
+ err = _OK_SYMLINK
+ }
+ return path, err
+}
+
+func (vfsOS) Delete(path string, syncDir bool) error {
+ err := os.Remove(path)
+ if err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ return _IOERR_DELETE_NOENT
+ }
+ return err
+ }
+ if runtime.GOOS != "windows" && syncDir {
+ f, err := os.Open(filepath.Dir(path))
+ if err != nil {
+ return _OK
+ }
+ defer f.Close()
+ err = osSync(f, false, false)
+ if err != nil {
+ return _IOERR_DIR_FSYNC
+ }
+ }
+ return nil
+}
+
+func (vfsOS) Access(name string, flags AccessFlag) (bool, error) {
+ err := osAccess(name, flags)
+ if flags == ACCESS_EXISTS {
+ if errors.Is(err, fs.ErrNotExist) {
+ return false, nil
+ }
+ } else {
+ if errors.Is(err, fs.ErrPermission) {
+ return false, nil
+ }
+ }
+ return err == nil, err
+}
+
+func (vfsOS) Open(name string, flags OpenFlag) (File, OpenFlag, error) {
+ return nil, 0, _CANTOPEN
+}
+
+func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) {
+ var oflags int
+ if flags&OPEN_EXCLUSIVE != 0 {
+ oflags |= os.O_EXCL
+ }
+ if flags&OPEN_CREATE != 0 {
+ oflags |= os.O_CREATE
+ }
+ if flags&OPEN_READONLY != 0 {
+ oflags |= os.O_RDONLY
+ }
+ if flags&OPEN_READWRITE != 0 {
+ oflags |= os.O_RDWR
+ }
+
+ var err error
+ var f *os.File
+ if name == nil {
+ f, err = os.CreateTemp("", "*.db")
+ } else {
+ f, err = osutil.OpenFile(name.String(), oflags, 0666)
+ }
+ if err != nil {
+ if errors.Is(err, syscall.EISDIR) {
+ return nil, flags, _CANTOPEN_ISDIR
+ }
+ return nil, flags, err
+ }
+
+ if modeof := name.URIParameter("modeof"); modeof != "" {
+ if err = osSetMode(f, modeof); err != nil {
+ f.Close()
+ return nil, flags, _IOERR_FSTAT
+ }
+ }
+ if flags&OPEN_DELETEONCLOSE != 0 {
+ os.Remove(f.Name())
+ }
+
+ file := vfsFile{
+ File: f,
+ psow: true,
+ readOnly: flags&OPEN_READONLY != 0,
+ syncDir: runtime.GOOS != "windows" &&
+ flags&(OPEN_CREATE) != 0 &&
+ flags&(OPEN_MAIN_JOURNAL|OPEN_SUPER_JOURNAL|OPEN_WAL) != 0,
+ shm: NewSharedMemory(name.String()+"-shm", flags),
+ }
+ return &file, flags, nil
+}
+
+type vfsFile struct {
+ *os.File
+ shm SharedMemory
+ lock LockLevel
+ readOnly bool
+ keepWAL bool
+ syncDir bool
+ psow bool
+}
+
+var (
+ // Ensure these interfaces are implemented:
+ _ FileLockState = &vfsFile{}
+ _ FileHasMoved = &vfsFile{}
+ _ FileSizeHint = &vfsFile{}
+ _ FilePersistentWAL = &vfsFile{}
+ _ FilePowersafeOverwrite = &vfsFile{}
+)
+
+func (f *vfsFile) Close() error {
+ if f.shm != nil {
+ f.shm.Close()
+ }
+ return f.File.Close()
+}
+
+func (f *vfsFile) Sync(flags SyncFlag) error {
+ dataonly := (flags & SYNC_DATAONLY) != 0
+ fullsync := (flags & 0x0f) == SYNC_FULL
+
+ err := osSync(f.File, fullsync, dataonly)
+ if err != nil {
+ return err
+ }
+ if runtime.GOOS != "windows" && f.syncDir {
+ f.syncDir = false
+ d, err := os.Open(filepath.Dir(f.File.Name()))
+ if err != nil {
+ return nil
+ }
+ defer d.Close()
+ err = osSync(d, false, false)
+ if err != nil {
+ return _IOERR_DIR_FSYNC
+ }
+ }
+ return nil
+}
+
+func (f *vfsFile) Size() (int64, error) {
+ return f.Seek(0, io.SeekEnd)
+}
+
+func (f *vfsFile) SectorSize() int {
+ return _DEFAULT_SECTOR_SIZE
+}
+
+func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic {
+ var res DeviceCharacteristic
+ if osBatchAtomic(f.File) {
+ res |= IOCAP_BATCH_ATOMIC
+ }
+ if f.psow {
+ res |= IOCAP_POWERSAFE_OVERWRITE
+ }
+ return res
+}
+
+func (f *vfsFile) SizeHint(size int64) error {
+ return osAllocate(f.File, size)
+}
+
+func (f *vfsFile) HasMoved() (bool, error) {
+ fi, err := f.Stat()
+ if err != nil {
+ return false, err
+ }
+ pi, err := os.Stat(f.Name())
+ if err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ return true, nil
+ }
+ return false, err
+ }
+ return !os.SameFile(fi, pi), nil
+}
+
+func (f *vfsFile) LockState() LockLevel { return f.lock }
+func (f *vfsFile) PowersafeOverwrite() bool { return f.psow }
+func (f *vfsFile) PersistentWAL() bool { return f.keepWAL }
+func (f *vfsFile) SetPowersafeOverwrite(psow bool) { f.psow = psow }
+func (f *vfsFile) SetPersistentWAL(keepWAL bool) { f.keepWAL = keepWAL }
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/filename.go b/vendor/github.com/ncruces/go-sqlite3/vfs/filename.go
new file mode 100644
index 000000000..e23575bbb
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/filename.go
@@ -0,0 +1,174 @@
+package vfs
+
+import (
+ "context"
+ "net/url"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/tetratelabs/wazero/api"
+)
+
+// Filename is used by SQLite to pass filenames
+// to the Open method of a VFS.
+//
+// https://sqlite.org/c3ref/filename.html
+type Filename struct {
+ ctx context.Context
+ mod api.Module
+ zPath uint32
+ flags OpenFlag
+ stack [2]uint64
+}
+
+// OpenFilename is an internal API users should not call directly.
+func OpenFilename(ctx context.Context, mod api.Module, id uint32, flags OpenFlag) *Filename {
+ if id == 0 {
+ return nil
+ }
+ return &Filename{
+ ctx: ctx,
+ mod: mod,
+ zPath: id,
+ flags: flags,
+ }
+}
+
+// String returns this filename as a string.
+func (n *Filename) String() string {
+ if n == nil || n.zPath == 0 {
+ return ""
+ }
+ return util.ReadString(n.mod, n.zPath, _MAX_PATHNAME)
+}
+
+// Database returns the name of the corresponding database file.
+//
+// https://sqlite.org/c3ref/filename_database.html
+func (n *Filename) Database() string {
+ return n.path("sqlite3_filename_database")
+}
+
+// Journal returns the name of the corresponding rollback journal file.
+//
+// https://sqlite.org/c3ref/filename_database.html
+func (n *Filename) Journal() string {
+ return n.path("sqlite3_filename_journal")
+}
+
+// Journal returns the name of the corresponding WAL file.
+//
+// https://sqlite.org/c3ref/filename_database.html
+func (n *Filename) WAL() string {
+ return n.path("sqlite3_filename_wal")
+}
+
+func (n *Filename) path(method string) string {
+ if n == nil || n.zPath == 0 {
+ return ""
+ }
+ n.stack[0] = uint64(n.zPath)
+ fn := n.mod.ExportedFunction(method)
+ if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil {
+ panic(err)
+ }
+ return util.ReadString(n.mod, uint32(n.stack[0]), _MAX_PATHNAME)
+}
+
+// DatabaseFile returns the main database [File] corresponding to a journal.
+//
+// https://sqlite.org/c3ref/database_file_object.html
+func (n *Filename) DatabaseFile() File {
+ if n == nil || n.zPath == 0 {
+ return nil
+ }
+ if n.flags&(OPEN_MAIN_DB|OPEN_MAIN_JOURNAL|OPEN_WAL) == 0 {
+ return nil
+ }
+
+ n.stack[0] = uint64(n.zPath)
+ fn := n.mod.ExportedFunction("sqlite3_database_file_object")
+ if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil {
+ panic(err)
+ }
+ file, _ := vfsFileGet(n.ctx, n.mod, uint32(n.stack[0])).(File)
+ return file
+}
+
+// URIParameter returns the value of a URI parameter.
+//
+// https://sqlite.org/c3ref/uri_boolean.html
+func (n *Filename) URIParameter(key string) string {
+ if n == nil || n.zPath == 0 {
+ return ""
+ }
+
+ uriKey := n.mod.ExportedFunction("sqlite3_uri_key")
+ n.stack[0] = uint64(n.zPath)
+ n.stack[1] = uint64(0)
+ if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil {
+ panic(err)
+ }
+
+ ptr := uint32(n.stack[0])
+ if ptr == 0 {
+ return ""
+ }
+
+ // Parse the format from:
+ // https://github.com/sqlite/sqlite/blob/b74eb0/src/pager.c#L4797-L4840
+ // This avoids having to alloc/free the key just to find a value.
+ for {
+ k := util.ReadString(n.mod, ptr, _MAX_NAME)
+ if k == "" {
+ return ""
+ }
+ ptr += uint32(len(k)) + 1
+
+ v := util.ReadString(n.mod, ptr, _MAX_NAME)
+ if k == key {
+ return v
+ }
+ ptr += uint32(len(v)) + 1
+ }
+}
+
+// URIParameters obtains values for URI parameters.
+//
+// https://sqlite.org/c3ref/uri_boolean.html
+func (n *Filename) URIParameters() url.Values {
+ if n == nil || n.zPath == 0 {
+ return nil
+ }
+
+ uriKey := n.mod.ExportedFunction("sqlite3_uri_key")
+ n.stack[0] = uint64(n.zPath)
+ n.stack[1] = uint64(0)
+ if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil {
+ panic(err)
+ }
+
+ ptr := uint32(n.stack[0])
+ if ptr == 0 {
+ return nil
+ }
+
+ var params url.Values
+
+ // Parse the format from:
+ // https://github.com/sqlite/sqlite/blob/b74eb0/src/pager.c#L4797-L4840
+ // This is the only way to support multiple valued keys.
+ for {
+ k := util.ReadString(n.mod, ptr, _MAX_NAME)
+ if k == "" {
+ return params
+ }
+ ptr += uint32(len(k)) + 1
+
+ v := util.ReadString(n.mod, ptr, _MAX_NAME)
+ if params == nil {
+ params = url.Values{}
+ }
+ params.Add(k, v)
+ ptr += uint32(len(v)) + 1
+ }
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go b/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
new file mode 100644
index 000000000..86a988ae8
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
@@ -0,0 +1,144 @@
+//go:build (linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys
+
+package vfs
+
+import "github.com/ncruces/go-sqlite3/internal/util"
+
+// SupportsFileLocking is false on platforms that do not support file locking.
+// To open a database file on those platforms,
+// you need to use the [nolock] or [immutable] URI parameters.
+//
+// [nolock]: https://sqlite.org/uri.html#urinolock
+// [immutable]: https://sqlite.org/uri.html#uriimmutable
+const SupportsFileLocking = true
+
+const (
+ _PENDING_BYTE = 0x40000000
+ _RESERVED_BYTE = (_PENDING_BYTE + 1)
+ _SHARED_FIRST = (_PENDING_BYTE + 2)
+ _SHARED_SIZE = 510
+)
+
+func (f *vfsFile) Lock(lock LockLevel) error {
+ // Argument check. SQLite never explicitly requests a pending lock.
+ if lock != LOCK_SHARED && lock != LOCK_RESERVED && lock != LOCK_EXCLUSIVE {
+ panic(util.AssertErr())
+ }
+
+ switch {
+ case f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE:
+ // Connection state check.
+ panic(util.AssertErr())
+ case f.lock == LOCK_NONE && lock > LOCK_SHARED:
+ // We never move from unlocked to anything higher than a shared lock.
+ panic(util.AssertErr())
+ case f.lock != LOCK_SHARED && lock == LOCK_RESERVED:
+ // A shared lock is always held when a reserved lock is requested.
+ panic(util.AssertErr())
+ }
+
+ // If we already have an equal or more restrictive lock, do nothing.
+ if f.lock >= lock {
+ return nil
+ }
+
+ // Do not allow any kind of write-lock on a read-only database.
+ if f.readOnly && lock >= LOCK_RESERVED {
+ return _IOERR_LOCK
+ }
+
+ switch lock {
+ case LOCK_SHARED:
+ // Must be unlocked to get SHARED.
+ if f.lock != LOCK_NONE {
+ panic(util.AssertErr())
+ }
+ if rc := osGetSharedLock(f.File); rc != _OK {
+ return rc
+ }
+ f.lock = LOCK_SHARED
+ return nil
+
+ case LOCK_RESERVED:
+ // Must be SHARED to get RESERVED.
+ if f.lock != LOCK_SHARED {
+ panic(util.AssertErr())
+ }
+ if rc := osGetReservedLock(f.File); rc != _OK {
+ return rc
+ }
+ f.lock = LOCK_RESERVED
+ return nil
+
+ case LOCK_EXCLUSIVE:
+ // Must be SHARED, RESERVED or PENDING to get EXCLUSIVE.
+ if f.lock <= LOCK_NONE || f.lock >= LOCK_EXCLUSIVE {
+ panic(util.AssertErr())
+ }
+ reserved := f.lock == LOCK_RESERVED
+ // A PENDING lock is needed before acquiring an EXCLUSIVE lock.
+ if f.lock < LOCK_PENDING {
+ // If we're already RESERVED, we can block indefinitely,
+ // since only new readers may briefly hold the PENDING lock.
+ if rc := osGetPendingLock(f.File, reserved /* block */); rc != _OK {
+ return rc
+ }
+ f.lock = LOCK_PENDING
+ }
+ // We already have PENDING, so we're just waiting for readers to leave.
+ // If we were RESERVED, we can wait for a little while, before invoking
+ // the busy handler; we will only do this once.
+ if rc := osGetExclusiveLock(f.File, reserved /* wait */); rc != _OK {
+ return rc
+ }
+ f.lock = LOCK_EXCLUSIVE
+ return nil
+
+ default:
+ panic(util.AssertErr())
+ }
+}
+
+func (f *vfsFile) Unlock(lock LockLevel) error {
+ // Argument check.
+ if lock != LOCK_NONE && lock != LOCK_SHARED {
+ panic(util.AssertErr())
+ }
+
+ // Connection state check.
+ if f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE {
+ panic(util.AssertErr())
+ }
+
+ // If we don't have a more restrictive lock, do nothing.
+ if f.lock <= lock {
+ return nil
+ }
+
+ switch lock {
+ case LOCK_SHARED:
+ rc := osDowngradeLock(f.File, f.lock)
+ f.lock = LOCK_SHARED
+ return rc
+
+ case LOCK_NONE:
+ rc := osReleaseLock(f.File, f.lock)
+ f.lock = LOCK_NONE
+ return rc
+
+ default:
+ panic(util.AssertErr())
+ }
+}
+
+func (f *vfsFile) CheckReservedLock() (bool, error) {
+ // Connection state check.
+ if f.lock < LOCK_NONE || f.lock > LOCK_EXCLUSIVE {
+ panic(util.AssertErr())
+ }
+
+ if f.lock >= LOCK_RESERVED {
+ return true, nil
+ }
+ return osCheckReservedLock(f.File)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go b/vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go
new file mode 100644
index 000000000..c395f34a7
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go
@@ -0,0 +1,23 @@
+//go:build !(linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) || sqlite3_nosys
+
+package vfs
+
+// SupportsFileLocking is false on platforms that do not support file locking.
+// To open a database file on those platforms,
+// you need to use the [nolock] or [immutable] URI parameters.
+//
+// [nolock]: https://sqlite.org/uri.html#urinolock
+// [immutable]: https://sqlite.org/uri.html#uriimmutable
+const SupportsFileLocking = false
+
+func (f *vfsFile) Lock(LockLevel) error {
+ return _IOERR_LOCK
+}
+
+func (f *vfsFile) Unlock(LockLevel) error {
+ return _IOERR_UNLOCK
+}
+
+func (f *vfsFile) CheckReservedLock() (bool, error) {
+ return false, _IOERR_CHECKRESERVEDLOCK
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md
new file mode 100644
index 000000000..193e29d98
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/README.md
@@ -0,0 +1,9 @@
+# Go `"memdb"` SQLite VFS
+
+This package implements the [`"memdb"`](https://sqlite.org/src/doc/tip/src/memdb.c)
+SQLite VFS in pure Go.
+
+It has some benefits over the C version:
+- the memory backing the database needs not be contiguous,
+- the database can grow/shrink incrementally without copying,
+- reader-writer concurrency is slightly improved. \ No newline at end of file
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go
new file mode 100644
index 000000000..5a2b84c71
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/api.go
@@ -0,0 +1,68 @@
+// Package memdb implements the "memdb" SQLite VFS.
+//
+// The "memdb" [vfs.VFS] allows the same in-memory database to be shared
+// among multiple database connections in the same process,
+// as long as the database name begins with "/".
+//
+// Importing package memdb registers the VFS:
+//
+// import _ "github.com/ncruces/go-sqlite3/vfs/memdb"
+package memdb
+
+import (
+ "sync"
+
+ "github.com/ncruces/go-sqlite3/vfs"
+)
+
+func init() {
+ vfs.Register("memdb", memVFS{})
+}
+
+var (
+ memoryMtx sync.Mutex
+ // +checklocks:memoryMtx
+ memoryDBs = map[string]*memDB{}
+)
+
+// Create creates a shared memory database,
+// using data as its initial contents.
+// The new database takes ownership of data,
+// and the caller should not use data after this call.
+func Create(name string, data []byte) {
+ memoryMtx.Lock()
+ defer memoryMtx.Unlock()
+
+ db := &memDB{
+ refs: 1,
+ name: name,
+ size: int64(len(data)),
+ }
+
+ // Convert data from WAL to rollback journal.
+ if len(data) >= 20 && data[18] == 2 && data[19] == 2 {
+ data[18] = 1
+ data[19] = 1
+ }
+
+ sectors := divRoundUp(db.size, sectorSize)
+ db.data = make([]*[sectorSize]byte, sectors)
+ for i := range db.data {
+ sector := data[i*sectorSize:]
+ if len(sector) >= sectorSize {
+ db.data[i] = (*[sectorSize]byte)(sector)
+ } else {
+ db.data[i] = new([sectorSize]byte)
+ copy((*db.data[i])[:], sector)
+ }
+ }
+
+ memoryDBs[name] = db
+}
+
+// Delete deletes a shared memory database.
+func Delete(name string) {
+ memoryMtx.Lock()
+ defer memoryMtx.Unlock()
+ delete(memoryDBs, name)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go
new file mode 100644
index 000000000..8dc57ab9c
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go
@@ -0,0 +1,311 @@
+package memdb
+
+import (
+ "io"
+ "runtime"
+ "sync"
+ "time"
+
+ "github.com/ncruces/go-sqlite3"
+ "github.com/ncruces/go-sqlite3/vfs"
+)
+
+// Must be a multiple of 64K (the largest page size).
+const sectorSize = 65536
+
+type memVFS struct{}
+
+func (memVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) {
+ // For simplicity, we do not support reading or writing data
+ // across "sector" boundaries.
+ //
+ // This is not a problem for most SQLite file types:
+ // - databases, which only do page aligned reads/writes;
+ // - temp journals, as used by the sorter, which does the same:
+ // https://github.com/sqlite/sqlite/blob/b74eb0/src/vdbesort.c#L409-L412
+ //
+ // We refuse to open all other file types,
+ // but returning OPEN_MEMORY means SQLite won't ask us to.
+ const types = vfs.OPEN_MAIN_DB |
+ vfs.OPEN_TEMP_DB |
+ vfs.OPEN_TEMP_JOURNAL
+ if flags&types == 0 {
+ return nil, flags, sqlite3.CANTOPEN
+ }
+
+ // A shared database has a name that begins with "/".
+ shared := len(name) > 1 && name[0] == '/'
+
+ var db *memDB
+ if shared {
+ name = name[1:]
+ memoryMtx.Lock()
+ defer memoryMtx.Unlock()
+ db = memoryDBs[name]
+ }
+ if db == nil {
+ if flags&vfs.OPEN_CREATE == 0 {
+ return nil, flags, sqlite3.CANTOPEN
+ }
+ db = &memDB{name: name}
+ }
+ if shared {
+ db.refs++ // +checklocksforce: memoryMtx is held
+ memoryDBs[name] = db
+ }
+
+ return &memFile{
+ memDB: db,
+ readOnly: flags&vfs.OPEN_READONLY != 0,
+ }, flags | vfs.OPEN_MEMORY, nil
+}
+
+func (memVFS) Delete(name string, dirSync bool) error {
+ return sqlite3.IOERR_DELETE
+}
+
+func (memVFS) Access(name string, flag vfs.AccessFlag) (bool, error) {
+ return false, nil
+}
+
+func (memVFS) FullPathname(name string) (string, error) {
+ return name, nil
+}
+
+type memDB struct {
+ name string
+
+ // +checklocks:lockMtx
+ pending *memFile
+ // +checklocks:lockMtx
+ reserved *memFile
+
+ // +checklocks:dataMtx
+ data []*[sectorSize]byte
+
+ // +checklocks:dataMtx
+ size int64
+
+ // +checklocks:lockMtx
+ shared int
+
+ // +checklocks:memoryMtx
+ refs int
+
+ lockMtx sync.Mutex
+ dataMtx sync.RWMutex
+}
+
+func (m *memDB) release() {
+ memoryMtx.Lock()
+ defer memoryMtx.Unlock()
+ if m.refs--; m.refs == 0 && m == memoryDBs[m.name] {
+ delete(memoryDBs, m.name)
+ }
+}
+
+type memFile struct {
+ *memDB
+ lock vfs.LockLevel
+ readOnly bool
+}
+
+var (
+ // Ensure these interfaces are implemented:
+ _ vfs.FileLockState = &memFile{}
+ _ vfs.FileSizeHint = &memFile{}
+)
+
+func (m *memFile) Close() error {
+ m.release()
+ return m.Unlock(vfs.LOCK_NONE)
+}
+
+func (m *memFile) ReadAt(b []byte, off int64) (n int, err error) {
+ m.dataMtx.RLock()
+ defer m.dataMtx.RUnlock()
+
+ if off >= m.size {
+ return 0, io.EOF
+ }
+
+ base := off / sectorSize
+ rest := off % sectorSize
+ have := int64(sectorSize)
+ if base == int64(len(m.data))-1 {
+ have = modRoundUp(m.size, sectorSize)
+ }
+ n = copy(b, (*m.data[base])[rest:have])
+ if n < len(b) {
+ // Assume reads are page aligned.
+ return 0, io.ErrNoProgress
+ }
+ return n, nil
+}
+
+func (m *memFile) WriteAt(b []byte, off int64) (n int, err error) {
+ m.dataMtx.Lock()
+ defer m.dataMtx.Unlock()
+
+ base := off / sectorSize
+ rest := off % sectorSize
+ for base >= int64(len(m.data)) {
+ m.data = append(m.data, new([sectorSize]byte))
+ }
+ n = copy((*m.data[base])[rest:], b)
+ if n < len(b) {
+ // Assume writes are page aligned.
+ return n, io.ErrShortWrite
+ }
+ if size := off + int64(len(b)); size > m.size {
+ m.size = size
+ }
+ return n, nil
+}
+
+func (m *memFile) Truncate(size int64) error {
+ m.dataMtx.Lock()
+ defer m.dataMtx.Unlock()
+ return m.truncate(size)
+}
+
+// +checklocks:m.dataMtx
+func (m *memFile) truncate(size int64) error {
+ if size < m.size {
+ base := size / sectorSize
+ rest := size % sectorSize
+ if rest != 0 {
+ clear((*m.data[base])[rest:])
+ }
+ }
+ sectors := divRoundUp(size, sectorSize)
+ for sectors > int64(len(m.data)) {
+ m.data = append(m.data, new([sectorSize]byte))
+ }
+ clear(m.data[sectors:])
+ m.data = m.data[:sectors]
+ m.size = size
+ return nil
+}
+
+func (m *memFile) Sync(flag vfs.SyncFlag) error {
+ return nil
+}
+
+func (m *memFile) Size() (int64, error) {
+ m.dataMtx.RLock()
+ defer m.dataMtx.RUnlock()
+ return m.size, nil
+}
+
+const spinWait = 25 * time.Microsecond
+
+func (m *memFile) Lock(lock vfs.LockLevel) error {
+ if m.lock >= lock {
+ return nil
+ }
+
+ if m.readOnly && lock >= vfs.LOCK_RESERVED {
+ return sqlite3.IOERR_LOCK
+ }
+
+ m.lockMtx.Lock()
+ defer m.lockMtx.Unlock()
+
+ switch lock {
+ case vfs.LOCK_SHARED:
+ if m.pending != nil {
+ return sqlite3.BUSY
+ }
+ m.shared++
+
+ case vfs.LOCK_RESERVED:
+ if m.reserved != nil {
+ return sqlite3.BUSY
+ }
+ m.reserved = m
+
+ case vfs.LOCK_EXCLUSIVE:
+ if m.lock < vfs.LOCK_PENDING {
+ if m.pending != nil {
+ return sqlite3.BUSY
+ }
+ m.lock = vfs.LOCK_PENDING
+ m.pending = m
+ }
+
+ for before := time.Now(); m.shared > 1; {
+ if time.Since(before) > spinWait {
+ return sqlite3.BUSY
+ }
+ m.lockMtx.Unlock()
+ runtime.Gosched()
+ m.lockMtx.Lock()
+ }
+ }
+
+ m.lock = lock
+ return nil
+}
+
+func (m *memFile) Unlock(lock vfs.LockLevel) error {
+ if m.lock <= lock {
+ return nil
+ }
+
+ m.lockMtx.Lock()
+ defer m.lockMtx.Unlock()
+
+ if m.pending == m {
+ m.pending = nil
+ }
+ if m.reserved == m {
+ m.reserved = nil
+ }
+ if lock < vfs.LOCK_SHARED {
+ m.shared--
+ }
+ m.lock = lock
+ return nil
+}
+
+func (m *memFile) CheckReservedLock() (bool, error) {
+ if m.lock >= vfs.LOCK_RESERVED {
+ return true, nil
+ }
+ m.lockMtx.Lock()
+ defer m.lockMtx.Unlock()
+ return m.reserved != nil, nil
+}
+
+func (m *memFile) SectorSize() int {
+ return sectorSize
+}
+
+func (m *memFile) DeviceCharacteristics() vfs.DeviceCharacteristic {
+ return vfs.IOCAP_ATOMIC |
+ vfs.IOCAP_SEQUENTIAL |
+ vfs.IOCAP_SAFE_APPEND |
+ vfs.IOCAP_POWERSAFE_OVERWRITE
+}
+
+func (m *memFile) SizeHint(size int64) error {
+ m.dataMtx.Lock()
+ defer m.dataMtx.Unlock()
+ if size > m.size {
+ return m.truncate(size)
+ }
+ return nil
+}
+
+func (m *memFile) LockState() vfs.LockLevel {
+ return m.lock
+}
+
+func divRoundUp(a, b int64) int64 {
+ return (a + b - 1) / b
+}
+
+func modRoundUp(a, b int64) int64 {
+ return b - (b-a%b)%b
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
new file mode 100644
index 000000000..48ac5c9c9
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
@@ -0,0 +1,33 @@
+//go:build (freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys
+
+package vfs
+
+import (
+ "os"
+ "time"
+
+ "golang.org/x/sys/unix"
+)
+
+func osUnlock(file *os.File, start, len int64) _ErrorCode {
+ if start == 0 && len == 0 {
+ err := unix.Flock(int(file.Fd()), unix.LOCK_UN)
+ if err != nil {
+ return _IOERR_UNLOCK
+ }
+ }
+ return _OK
+}
+
+func osLock(file *os.File, how int, def _ErrorCode) _ErrorCode {
+ err := unix.Flock(int(file.Fd()), how)
+ return osLockErrorCode(err, def)
+}
+
+func osReadLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time.Duration) _ErrorCode {
+ return osLock(file, unix.LOCK_SH|unix.LOCK_NB, _IOERR_RDLOCK)
+}
+
+func osWriteLock(file *os.File, _ /*start*/, _ /*len*/ int64, _ /*timeout*/ time.Duration) _ErrorCode {
+ return osLock(file, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
new file mode 100644
index 000000000..8bfe96bb1
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
@@ -0,0 +1,95 @@
+//go:build !(sqlite3_flock || sqlite3_nosys)
+
+package vfs
+
+import (
+ "io"
+ "os"
+ "time"
+
+ "golang.org/x/sys/unix"
+)
+
+const (
+ // https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h
+ _F_OFD_SETLK = 90
+ _F_OFD_SETLKW = 91
+ _F_OFD_SETLKWTIMEOUT = 93
+)
+
+type flocktimeout_t struct {
+ fl unix.Flock_t
+ timeout unix.Timespec
+}
+
+func osSync(file *os.File, fullsync, _ /*dataonly*/ bool) error {
+ if fullsync {
+ return file.Sync()
+ }
+ return unix.Fsync(int(file.Fd()))
+}
+
+func osAllocate(file *os.File, size int64) error {
+ off, err := file.Seek(0, io.SeekEnd)
+ if err != nil {
+ return err
+ }
+ if size <= off {
+ return nil
+ }
+
+ store := unix.Fstore_t{
+ Flags: unix.F_ALLOCATEALL | unix.F_ALLOCATECONTIG,
+ Posmode: unix.F_PEOFPOSMODE,
+ Offset: 0,
+ Length: size - off,
+ }
+
+ // Try to get a continuous chunk of disk space.
+ err = unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store)
+ if err != nil {
+ // OK, perhaps we are too fragmented, allocate non-continuous.
+ store.Flags = unix.F_ALLOCATEALL
+ unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store)
+ }
+ return file.Truncate(size)
+}
+
+func osUnlock(file *os.File, start, len int64) _ErrorCode {
+ err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &unix.Flock_t{
+ Type: unix.F_UNLCK,
+ Start: start,
+ Len: len,
+ })
+ if err != nil {
+ return _IOERR_UNLOCK
+ }
+ return _OK
+}
+
+func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode {
+ lock := flocktimeout_t{fl: unix.Flock_t{
+ Type: typ,
+ Start: start,
+ Len: len,
+ }}
+ var err error
+ switch {
+ case timeout == 0:
+ err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock.fl)
+ case timeout < 0:
+ err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKW, &lock.fl)
+ default:
+ lock.timeout = unix.NsecToTimespec(int64(timeout / time.Nanosecond))
+ err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKWTIMEOUT, &lock.fl)
+ }
+ return osLockErrorCode(err, def)
+}
+
+func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
+ return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK)
+}
+
+func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
+ return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go
new file mode 100644
index 000000000..a9f0e333c
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go
@@ -0,0 +1,34 @@
+//go:build (amd64 || arm64 || riscv64) && !sqlite3_nosys
+
+package vfs
+
+import (
+ "os"
+
+ "golang.org/x/sys/unix"
+)
+
+const (
+ _F2FS_IOC_START_ATOMIC_WRITE = 62721
+ _F2FS_IOC_COMMIT_ATOMIC_WRITE = 62722
+ _F2FS_IOC_ABORT_ATOMIC_WRITE = 62725
+ _F2FS_IOC_GET_FEATURES = 2147808524
+ _F2FS_FEATURE_ATOMIC_WRITE = 4
+)
+
+func osBatchAtomic(file *os.File) bool {
+ flags, err := unix.IoctlGetInt(int(file.Fd()), _F2FS_IOC_GET_FEATURES)
+ return err == nil && flags&_F2FS_FEATURE_ATOMIC_WRITE != 0
+}
+
+func (f *vfsFile) BeginAtomicWrite() error {
+ return unix.IoctlSetInt(int(f.Fd()), _F2FS_IOC_START_ATOMIC_WRITE, 0)
+}
+
+func (f *vfsFile) CommitAtomicWrite() error {
+ return unix.IoctlSetInt(int(f.Fd()), _F2FS_IOC_COMMIT_ATOMIC_WRITE, 0)
+}
+
+func (f *vfsFile) RollbackAtomicWrite() error {
+ return unix.IoctlSetInt(int(f.Fd()), _F2FS_IOC_ABORT_ATOMIC_WRITE, 0)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
new file mode 100644
index 000000000..11e683a04
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
@@ -0,0 +1,71 @@
+//go:build !(sqlite3_flock || sqlite3_nosys)
+
+package vfs
+
+import (
+ "math/rand"
+ "os"
+ "time"
+
+ "golang.org/x/sys/unix"
+)
+
+func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error {
+ // SQLite trusts Linux's fdatasync for all fsync's.
+ return unix.Fdatasync(int(file.Fd()))
+}
+
+func osAllocate(file *os.File, size int64) error {
+ if size == 0 {
+ return nil
+ }
+ return unix.Fallocate(int(file.Fd()), 0, 0, size)
+}
+
+func osUnlock(file *os.File, start, len int64) _ErrorCode {
+ err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &unix.Flock_t{
+ Type: unix.F_UNLCK,
+ Start: start,
+ Len: len,
+ })
+ if err != nil {
+ return _IOERR_UNLOCK
+ }
+ return _OK
+}
+
+func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode {
+ lock := unix.Flock_t{
+ Type: typ,
+ Start: start,
+ Len: len,
+ }
+ var err error
+ switch {
+ case timeout == 0:
+ err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
+ case timeout < 0:
+ err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLKW, &lock)
+ default:
+ before := time.Now()
+ for {
+ err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
+ if errno, _ := err.(unix.Errno); errno != unix.EAGAIN {
+ break
+ }
+ if timeout < time.Since(before) {
+ break
+ }
+ osSleep(time.Duration(rand.Int63n(int64(time.Millisecond))))
+ }
+ }
+ return osLockErrorCode(err, def)
+}
+
+func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
+ return osLock(file, unix.F_RDLCK, start, len, timeout, _IOERR_RDLOCK)
+}
+
+func osWriteLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
+ return osLock(file, unix.F_WRLCK, start, len, timeout, _IOERR_LOCK)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_access.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_access.go
new file mode 100644
index 000000000..1621c0998
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_access.go
@@ -0,0 +1,36 @@
+//go:build !unix || sqlite3_nosys
+
+package vfs
+
+import (
+ "io/fs"
+ "os"
+)
+
+func osAccess(path string, flags AccessFlag) error {
+ fi, err := os.Stat(path)
+ if err != nil {
+ return err
+ }
+ if flags == ACCESS_EXISTS {
+ return nil
+ }
+
+ const (
+ S_IREAD = 0400
+ S_IWRITE = 0200
+ S_IEXEC = 0100
+ )
+
+ var want fs.FileMode = S_IREAD
+ if flags == ACCESS_READWRITE {
+ want |= S_IWRITE
+ }
+ if fi.IsDir() {
+ want |= S_IEXEC
+ }
+ if fi.Mode()&want != want {
+ return fs.ErrPermission
+ }
+ return nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_alloc.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_alloc.go
new file mode 100644
index 000000000..60c92182c
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_alloc.go
@@ -0,0 +1,19 @@
+//go:build !(linux || darwin) || sqlite3_flock || sqlite3_nosys
+
+package vfs
+
+import (
+ "io"
+ "os"
+)
+
+func osAllocate(file *os.File, size int64) error {
+ off, err := file.Seek(0, io.SeekEnd)
+ if err != nil {
+ return err
+ }
+ if size <= off {
+ return nil
+ }
+ return file.Truncate(size)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go
new file mode 100644
index 000000000..ecaff0245
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go
@@ -0,0 +1,9 @@
+//go:build !linux || !(amd64 || arm64 || riscv64) || sqlite3_nosys
+
+package vfs
+
+import "os"
+
+func osBatchAtomic(*os.File) bool {
+ return false
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_mode.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_mode.go
new file mode 100644
index 000000000..ac4904773
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_mode.go
@@ -0,0 +1,14 @@
+//go:build !unix || sqlite3_nosys
+
+package vfs
+
+import "os"
+
+func osSetMode(file *os.File, modeof string) error {
+ fi, err := os.Stat(modeof)
+ if err != nil {
+ return err
+ }
+ file.Chmod(fi.Mode())
+ return nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go
new file mode 100644
index 000000000..c6bc40769
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go
@@ -0,0 +1,9 @@
+//go:build !windows || sqlite3_nosys
+
+package vfs
+
+import "time"
+
+func osSleep(d time.Duration) {
+ time.Sleep(d)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go
new file mode 100644
index 000000000..84dbd23bc
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sync.go
@@ -0,0 +1,9 @@
+//go:build !(linux || darwin) || sqlite3_flock || sqlite3_nosys
+
+package vfs
+
+import "os"
+
+func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error {
+ return file.Sync()
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go
new file mode 100644
index 000000000..bf4b44efd
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go
@@ -0,0 +1,33 @@
+//go:build unix && !sqlite3_nosys
+
+package vfs
+
+import (
+ "os"
+ "syscall"
+
+ "golang.org/x/sys/unix"
+)
+
+func osAccess(path string, flags AccessFlag) error {
+ var access uint32 // unix.F_OK
+ switch flags {
+ case ACCESS_READWRITE:
+ access = unix.R_OK | unix.W_OK
+ case ACCESS_READ:
+ access = unix.R_OK
+ }
+ return unix.Access(path, access)
+}
+
+func osSetMode(file *os.File, modeof string) error {
+ fi, err := os.Stat(modeof)
+ if err != nil {
+ return err
+ }
+ file.Chmod(fi.Mode())
+ if sys, ok := fi.Sys().(*syscall.Stat_t); ok {
+ file.Chown(int(sys.Uid), int(sys.Gid))
+ }
+ return nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go
new file mode 100644
index 000000000..d04c1f6a0
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go
@@ -0,0 +1,106 @@
+//go:build (linux || darwin || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys
+
+package vfs
+
+import (
+ "os"
+ "time"
+
+ "golang.org/x/sys/unix"
+)
+
+func osGetSharedLock(file *os.File) _ErrorCode {
+ // Test the PENDING lock before acquiring a new SHARED lock.
+ if lock, _ := osGetLock(file, _PENDING_BYTE, 1); lock == unix.F_WRLCK {
+ return _BUSY
+ }
+ // Acquire the SHARED lock.
+ return osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
+}
+
+func osGetReservedLock(file *os.File) _ErrorCode {
+ // Acquire the RESERVED lock.
+ return osWriteLock(file, _RESERVED_BYTE, 1, 0)
+}
+
+func osGetPendingLock(file *os.File, block bool) _ErrorCode {
+ var timeout time.Duration
+ if block {
+ timeout = -1
+ }
+ // Acquire the PENDING lock.
+ return osWriteLock(file, _PENDING_BYTE, 1, timeout)
+}
+
+func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode {
+ var timeout time.Duration
+ if wait {
+ timeout = time.Millisecond
+ }
+ // Acquire the EXCLUSIVE lock.
+ return osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
+}
+
+func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
+ if state >= LOCK_EXCLUSIVE {
+ // Downgrade to a SHARED lock.
+ if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
+ // In theory, the downgrade to a SHARED cannot fail because another
+ // process is holding an incompatible lock. If it does, this
+ // indicates that the other process is not following the locking
+ // protocol. If this happens, return _IOERR_RDLOCK. Returning
+ // BUSY would confuse the upper layer.
+ return _IOERR_RDLOCK
+ }
+ }
+ // Release the PENDING and RESERVED locks.
+ return osUnlock(file, _PENDING_BYTE, 2)
+}
+
+func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
+ // Release all locks.
+ return osUnlock(file, 0, 0)
+}
+
+func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
+ // Test the RESERVED lock.
+ lock, rc := osGetLock(file, _RESERVED_BYTE, 1)
+ return lock == unix.F_WRLCK, rc
+}
+
+func osGetLock(file *os.File, start, len int64) (int16, _ErrorCode) {
+ lock := unix.Flock_t{
+ Type: unix.F_WRLCK,
+ Start: start,
+ Len: len,
+ }
+ if unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock) != nil {
+ return 0, _IOERR_CHECKRESERVEDLOCK
+ }
+ return lock.Type, _OK
+}
+
+func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
+ if err == nil {
+ return _OK
+ }
+ if errno, ok := err.(unix.Errno); ok {
+ switch errno {
+ case
+ unix.EACCES,
+ unix.EAGAIN,
+ unix.EBUSY,
+ unix.EINTR,
+ unix.ENOLCK,
+ unix.EDEADLK,
+ unix.ETIMEDOUT:
+ return _BUSY
+ case unix.EPERM:
+ return _PERM
+ }
+ if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN {
+ return _BUSY
+ }
+ }
+ return def
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
new file mode 100644
index 000000000..5c68754f8
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
@@ -0,0 +1,186 @@
+//go:build !sqlite3_nosys
+
+package vfs
+
+import (
+ "math/rand"
+ "os"
+ "time"
+
+ "golang.org/x/sys/windows"
+)
+
+func osGetSharedLock(file *os.File) _ErrorCode {
+ // Acquire the PENDING lock temporarily before acquiring a new SHARED lock.
+ rc := osReadLock(file, _PENDING_BYTE, 1, 0)
+ if rc == _OK {
+ // Acquire the SHARED lock.
+ rc = osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
+
+ // Release the PENDING lock.
+ osUnlock(file, _PENDING_BYTE, 1)
+ }
+ return rc
+}
+
+func osGetReservedLock(file *os.File) _ErrorCode {
+ // Acquire the RESERVED lock.
+ return osWriteLock(file, _RESERVED_BYTE, 1, 0)
+}
+
+func osGetPendingLock(file *os.File, block bool) _ErrorCode {
+ var timeout time.Duration
+ if block {
+ timeout = -1
+ }
+
+ // Acquire the PENDING lock.
+ return osWriteLock(file, _PENDING_BYTE, 1, timeout)
+}
+
+func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode {
+ var timeout time.Duration
+ if wait {
+ timeout = time.Millisecond
+ }
+
+ // Release the SHARED lock.
+ osUnlock(file, _SHARED_FIRST, _SHARED_SIZE)
+
+ // Acquire the EXCLUSIVE lock.
+ rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
+
+ if rc != _OK {
+ // Reacquire the SHARED lock.
+ osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
+ }
+ return rc
+}
+
+func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
+ if state >= LOCK_EXCLUSIVE {
+ // Release the EXCLUSIVE lock.
+ osUnlock(file, _SHARED_FIRST, _SHARED_SIZE)
+
+ // Reacquire the SHARED lock.
+ if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
+ // This should never happen.
+ // We should always be able to reacquire the read lock.
+ return _IOERR_RDLOCK
+ }
+ }
+
+ // Release the PENDING and RESERVED locks.
+ if state >= LOCK_RESERVED {
+ osUnlock(file, _RESERVED_BYTE, 1)
+ }
+ if state >= LOCK_PENDING {
+ osUnlock(file, _PENDING_BYTE, 1)
+ }
+ return _OK
+}
+
+func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
+ // Release all locks.
+ if state >= LOCK_RESERVED {
+ osUnlock(file, _RESERVED_BYTE, 1)
+ }
+ if state >= LOCK_SHARED {
+ osUnlock(file, _SHARED_FIRST, _SHARED_SIZE)
+ }
+ if state >= LOCK_PENDING {
+ osUnlock(file, _PENDING_BYTE, 1)
+ }
+ return _OK
+}
+
+func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
+ // Test the RESERVED lock.
+ rc := osLock(file, 0, _RESERVED_BYTE, 1, 0, _IOERR_CHECKRESERVEDLOCK)
+ if rc == _BUSY {
+ return true, _OK
+ }
+ if rc == _OK {
+ // Release the RESERVED lock.
+ osUnlock(file, _RESERVED_BYTE, 1)
+ }
+ return false, rc
+}
+
+func osUnlock(file *os.File, start, len uint32) _ErrorCode {
+ err := windows.UnlockFileEx(windows.Handle(file.Fd()),
+ 0, len, 0, &windows.Overlapped{Offset: start})
+ if err == windows.ERROR_NOT_LOCKED {
+ return _OK
+ }
+ if err != nil {
+ return _IOERR_UNLOCK
+ }
+ return _OK
+}
+
+func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def _ErrorCode) _ErrorCode {
+ var err error
+ switch {
+ case timeout == 0:
+ err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len)
+ case timeout < 0:
+ err = osLockEx(file, flags, start, len)
+ default:
+ before := time.Now()
+ for {
+ err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len)
+ if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION {
+ break
+ }
+ if timeout < time.Since(before) {
+ break
+ }
+ osSleep(time.Duration(rand.Int63n(int64(time.Millisecond))))
+ }
+ }
+ return osLockErrorCode(err, def)
+}
+
+func osLockEx(file *os.File, flags, start, len uint32) error {
+ return windows.LockFileEx(windows.Handle(file.Fd()), flags,
+ 0, len, 0, &windows.Overlapped{Offset: start})
+}
+
+func osReadLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode {
+ return osLock(file, 0, start, len, timeout, _IOERR_RDLOCK)
+}
+
+func osWriteLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode {
+ return osLock(file, windows.LOCKFILE_EXCLUSIVE_LOCK, start, len, timeout, _IOERR_LOCK)
+}
+
+func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
+ if err == nil {
+ return _OK
+ }
+ if errno, ok := err.(windows.Errno); ok {
+ // https://devblogs.microsoft.com/oldnewthing/20140905-00/?p=63
+ switch errno {
+ case
+ windows.ERROR_LOCK_VIOLATION,
+ windows.ERROR_IO_PENDING,
+ windows.ERROR_OPERATION_ABORTED:
+ return _BUSY
+ }
+ }
+ return def
+}
+
+func osSleep(d time.Duration) {
+ if d > 0 {
+ period := max(1, d/(5*time.Millisecond))
+ if period < 16 {
+ windows.TimeBeginPeriod(uint32(period))
+ }
+ time.Sleep(d)
+ if period < 16 {
+ windows.TimeEndPeriod(uint32(period))
+ }
+ }
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/registry.go b/vendor/github.com/ncruces/go-sqlite3/vfs/registry.go
new file mode 100644
index 000000000..42a2106fb
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/registry.go
@@ -0,0 +1,48 @@
+package vfs
+
+import "sync"
+
+var (
+ // +checklocks:vfsRegistryMtx
+ vfsRegistry map[string]VFS
+ vfsRegistryMtx sync.RWMutex
+)
+
+// Find returns a VFS given its name.
+// If there is no match, nil is returned.
+// If name is empty, the default VFS is returned.
+//
+// https://sqlite.org/c3ref/vfs_find.html
+func Find(name string) VFS {
+ if name == "" || name == "os" {
+ return vfsOS{}
+ }
+ vfsRegistryMtx.RLock()
+ defer vfsRegistryMtx.RUnlock()
+ return vfsRegistry[name]
+}
+
+// Register registers a VFS.
+// Empty and "os" are reserved names.
+//
+// https://sqlite.org/c3ref/vfs_find.html
+func Register(name string, vfs VFS) {
+ if name == "" || name == "os" {
+ return
+ }
+ vfsRegistryMtx.Lock()
+ defer vfsRegistryMtx.Unlock()
+ if vfsRegistry == nil {
+ vfsRegistry = map[string]VFS{}
+ }
+ vfsRegistry[name] = vfs
+}
+
+// Unregister unregisters a VFS.
+//
+// https://sqlite.org/c3ref/vfs_find.html
+func Unregister(name string) {
+ vfsRegistryMtx.Lock()
+ defer vfsRegistryMtx.Unlock()
+ delete(vfsRegistry, name)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go
new file mode 100644
index 000000000..2b76dd5dc
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go
@@ -0,0 +1,173 @@
+//go:build (darwin || linux) && (amd64 || arm64 || riscv64) && !(sqlite3_flock || sqlite3_noshm || sqlite3_nosys)
+
+package vfs
+
+import (
+ "context"
+ "io"
+ "os"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/tetratelabs/wazero/api"
+ "golang.org/x/sys/unix"
+)
+
+// SupportsSharedMemory is false on platforms that do not support shared memory.
+// To use [WAL without shared-memory], you need to set [EXCLUSIVE locking mode].
+//
+// [WAL without shared-memory]: https://sqlite.org/wal.html#noshm
+// [EXCLUSIVE locking mode]: https://sqlite.org/pragma.html#pragma_locking_mode
+const SupportsSharedMemory = true
+
+const (
+ _SHM_NLOCK = 8
+ _SHM_BASE = 120
+ _SHM_DMS = _SHM_BASE + _SHM_NLOCK
+)
+
+func (f *vfsFile) SharedMemory() SharedMemory { return f.shm }
+
+// NewSharedMemory returns a shared-memory WAL-index
+// backed by a file with the given path.
+// It will return nil if shared-memory is not supported,
+// or not appropriate for the given flags.
+// Only [OPEN_MAIN_DB] databases may need a WAL-index.
+// You must ensure all concurrent accesses to a database
+// use shared-memory instances created with the same path.
+func NewSharedMemory(path string, flags OpenFlag) SharedMemory {
+ if flags&OPEN_MAIN_DB == 0 || flags&(OPEN_DELETEONCLOSE|OPEN_MEMORY) != 0 {
+ return nil
+ }
+ return &vfsShm{
+ path: path,
+ readOnly: flags&OPEN_READONLY != 0,
+ }
+}
+
+type vfsShm struct {
+ *os.File
+ path string
+ regions []*util.MappedRegion
+ readOnly bool
+}
+
+func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, error) {
+ // Ensure size is a multiple of the OS page size.
+ if int(size)&(unix.Getpagesize()-1) != 0 {
+ return 0, _IOERR_SHMMAP
+ }
+
+ if s.File == nil {
+ var flag int
+ if s.readOnly {
+ flag = unix.O_RDONLY
+ } else {
+ flag = unix.O_RDWR
+ }
+ f, err := os.OpenFile(s.path,
+ flag|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
+ if err != nil {
+ return 0, _CANTOPEN
+ }
+ s.File = f
+ }
+
+ // Dead man's switch.
+ if lock, rc := osGetLock(s.File, _SHM_DMS, 1); rc != _OK {
+ return 0, _IOERR_LOCK
+ } else if lock == unix.F_WRLCK {
+ return 0, _BUSY
+ } else if lock == unix.F_UNLCK {
+ if s.readOnly {
+ return 0, _READONLY_CANTINIT
+ }
+ if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
+ return 0, rc
+ }
+ if err := s.Truncate(0); err != nil {
+ return 0, _IOERR_SHMOPEN
+ }
+ }
+ if rc := osReadLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
+ return 0, rc
+ }
+
+ // Check if file is big enough.
+ o, err := s.Seek(0, io.SeekEnd)
+ if err != nil {
+ return 0, _IOERR_SHMSIZE
+ }
+ if n := (int64(id) + 1) * int64(size); n > o {
+ if !extend {
+ return 0, nil
+ }
+ err := osAllocate(s.File, n)
+ if err != nil {
+ return 0, _IOERR_SHMSIZE
+ }
+ }
+
+ var prot int
+ if s.readOnly {
+ prot = unix.PROT_READ
+ } else {
+ prot = unix.PROT_READ | unix.PROT_WRITE
+ }
+ r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, prot)
+ if err != nil {
+ return 0, err
+ }
+ s.regions = append(s.regions, r)
+ return r.Ptr, nil
+}
+
+func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error {
+ // Argument check.
+ if n <= 0 || offset < 0 || offset+n > _SHM_NLOCK {
+ panic(util.AssertErr())
+ }
+ switch flags {
+ case
+ _SHM_LOCK | _SHM_SHARED,
+ _SHM_LOCK | _SHM_EXCLUSIVE,
+ _SHM_UNLOCK | _SHM_SHARED,
+ _SHM_UNLOCK | _SHM_EXCLUSIVE:
+ //
+ default:
+ panic(util.AssertErr())
+ }
+ if n != 1 && flags&_SHM_EXCLUSIVE == 0 {
+ panic(util.AssertErr())
+ }
+
+ switch {
+ case flags&_SHM_UNLOCK != 0:
+ return osUnlock(s.File, _SHM_BASE+int64(offset), int64(n))
+ case flags&_SHM_SHARED != 0:
+ return osReadLock(s.File, _SHM_BASE+int64(offset), int64(n), 0)
+ case flags&_SHM_EXCLUSIVE != 0:
+ return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), 0)
+ default:
+ panic(util.AssertErr())
+ }
+}
+
+func (s *vfsShm) shmUnmap(delete bool) {
+ if s.File == nil {
+ return
+ }
+
+ // Unmap regions.
+ for _, r := range s.regions {
+ r.Unmap()
+ }
+ clear(s.regions)
+ s.regions = s.regions[:0]
+
+ // Close the file.
+ defer s.Close()
+ if delete {
+ os.Remove(s.Name())
+ }
+ s.File = nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go
new file mode 100644
index 000000000..21191979e
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go
@@ -0,0 +1,21 @@
+//go:build !(darwin || linux) || !(amd64 || arm64 || riscv64) || sqlite3_flock || sqlite3_noshm || sqlite3_nosys
+
+package vfs
+
+// SupportsSharedMemory is false on platforms that do not support shared memory.
+// To use [WAL without shared-memory], you need to set [EXCLUSIVE locking mode].
+//
+// [WAL without shared-memory]: https://sqlite.org/wal.html#noshm
+// [EXCLUSIVE locking mode]: https://sqlite.org/pragma.html#pragma_locking_mode
+const SupportsSharedMemory = false
+
+// NewSharedMemory returns a shared-memory WAL-index
+// backed by a file with the given path.
+// It will return nil if shared-memory is not supported,
+// or not appropriate for the given flags.
+// Only [OPEN_MAIN_DB] databases may need a WAL-index.
+// You must ensure all concurrent accesses to a database
+// use shared-memory instances created with the same path.
+func NewSharedMemory(path string, flags OpenFlag) SharedMemory {
+ return nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go b/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go
new file mode 100644
index 000000000..1887e9f22
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go
@@ -0,0 +1,459 @@
+package vfs
+
+import (
+ "context"
+ "crypto/rand"
+ "io"
+ "reflect"
+ "sync"
+ "time"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/ncruces/julianday"
+ "github.com/tetratelabs/wazero"
+ "github.com/tetratelabs/wazero/api"
+)
+
+// ExportHostFunctions is an internal API users need not call directly.
+//
+// ExportHostFunctions registers the required VFS host functions
+// with the provided env module.
+func ExportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder {
+ util.ExportFuncII(env, "go_vfs_find", vfsFind)
+ util.ExportFuncIIJ(env, "go_localtime", vfsLocaltime)
+ util.ExportFuncIIII(env, "go_randomness", vfsRandomness)
+ util.ExportFuncIII(env, "go_sleep", vfsSleep)
+ util.ExportFuncIII(env, "go_current_time_64", vfsCurrentTime64)
+ util.ExportFuncIIIII(env, "go_full_pathname", vfsFullPathname)
+ util.ExportFuncIIII(env, "go_delete", vfsDelete)
+ util.ExportFuncIIIII(env, "go_access", vfsAccess)
+ util.ExportFuncIIIIIII(env, "go_open", vfsOpen)
+ util.ExportFuncII(env, "go_close", vfsClose)
+ util.ExportFuncIIIIJ(env, "go_read", vfsRead)
+ util.ExportFuncIIIIJ(env, "go_write", vfsWrite)
+ util.ExportFuncIIJ(env, "go_truncate", vfsTruncate)
+ util.ExportFuncIII(env, "go_sync", vfsSync)
+ util.ExportFuncIII(env, "go_file_size", vfsFileSize)
+ util.ExportFuncIIII(env, "go_file_control", vfsFileControl)
+ util.ExportFuncII(env, "go_sector_size", vfsSectorSize)
+ util.ExportFuncII(env, "go_device_characteristics", vfsDeviceCharacteristics)
+ util.ExportFuncIII(env, "go_lock", vfsLock)
+ util.ExportFuncIII(env, "go_unlock", vfsUnlock)
+ util.ExportFuncIII(env, "go_check_reserved_lock", vfsCheckReservedLock)
+ util.ExportFuncIIIIII(env, "go_shm_map", vfsShmMap)
+ util.ExportFuncIIIII(env, "go_shm_lock", vfsShmLock)
+ util.ExportFuncIII(env, "go_shm_unmap", vfsShmUnmap)
+ util.ExportFuncVI(env, "go_shm_barrier", vfsShmBarrier)
+ return env
+}
+
+func vfsFind(ctx context.Context, mod api.Module, zVfsName uint32) uint32 {
+ name := util.ReadString(mod, zVfsName, _MAX_NAME)
+ if vfs := Find(name); vfs != nil && vfs != (vfsOS{}) {
+ return 1
+ }
+ return 0
+}
+
+func vfsLocaltime(ctx context.Context, mod api.Module, pTm uint32, t int64) _ErrorCode {
+ tm := time.Unix(t, 0)
+ var isdst int
+ if tm.IsDST() {
+ isdst = 1
+ }
+
+ const size = 32 / 8
+ // https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html
+ util.WriteUint32(mod, pTm+0*size, uint32(tm.Second()))
+ util.WriteUint32(mod, pTm+1*size, uint32(tm.Minute()))
+ util.WriteUint32(mod, pTm+2*size, uint32(tm.Hour()))
+ util.WriteUint32(mod, pTm+3*size, uint32(tm.Day()))
+ util.WriteUint32(mod, pTm+4*size, uint32(tm.Month()-time.January))
+ util.WriteUint32(mod, pTm+5*size, uint32(tm.Year()-1900))
+ util.WriteUint32(mod, pTm+6*size, uint32(tm.Weekday()-time.Sunday))
+ util.WriteUint32(mod, pTm+7*size, uint32(tm.YearDay()-1))
+ util.WriteUint32(mod, pTm+8*size, uint32(isdst))
+ return _OK
+}
+
+func vfsRandomness(ctx context.Context, mod api.Module, pVfs uint32, nByte int32, zByte uint32) uint32 {
+ mem := util.View(mod, zByte, uint64(nByte))
+ n, _ := rand.Reader.Read(mem)
+ return uint32(n)
+}
+
+func vfsSleep(ctx context.Context, mod api.Module, pVfs uint32, nMicro int32) _ErrorCode {
+ osSleep(time.Duration(nMicro) * time.Microsecond)
+ return _OK
+}
+
+func vfsCurrentTime64(ctx context.Context, mod api.Module, pVfs, piNow uint32) _ErrorCode {
+ day, nsec := julianday.Date(time.Now())
+ msec := day*86_400_000 + nsec/1_000_000
+ util.WriteUint64(mod, piNow, uint64(msec))
+ return _OK
+}
+
+func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative uint32, nFull int32, zFull uint32) _ErrorCode {
+ vfs := vfsGet(mod, pVfs)
+ path := util.ReadString(mod, zRelative, _MAX_PATHNAME)
+
+ path, err := vfs.FullPathname(path)
+
+ if len(path) >= int(nFull) {
+ return _CANTOPEN_FULLPATH
+ }
+ util.WriteString(mod, zFull, path)
+
+ return vfsErrorCode(err, _CANTOPEN_FULLPATH)
+}
+
+func vfsDelete(ctx context.Context, mod api.Module, pVfs, zPath, syncDir uint32) _ErrorCode {
+ vfs := vfsGet(mod, pVfs)
+ path := util.ReadString(mod, zPath, _MAX_PATHNAME)
+
+ err := vfs.Delete(path, syncDir != 0)
+ return vfsErrorCode(err, _IOERR_DELETE)
+}
+
+func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath uint32, flags AccessFlag, pResOut uint32) _ErrorCode {
+ vfs := vfsGet(mod, pVfs)
+ path := util.ReadString(mod, zPath, _MAX_PATHNAME)
+
+ ok, err := vfs.Access(path, flags)
+ var res uint32
+ if ok {
+ res = 1
+ }
+
+ util.WriteUint32(mod, pResOut, res)
+ return vfsErrorCode(err, _IOERR_ACCESS)
+}
+
+func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, flags OpenFlag, pOutFlags, pOutVFS uint32) _ErrorCode {
+ vfs := vfsGet(mod, pVfs)
+
+ var path string
+ if zPath != 0 {
+ path = util.ReadString(mod, zPath, _MAX_PATHNAME)
+ }
+
+ var file File
+ var err error
+ if ffs, ok := vfs.(VFSFilename); ok {
+ name := OpenFilename(ctx, mod, zPath, flags)
+ file, flags, err = ffs.OpenFilename(name, flags)
+ } else {
+ file, flags, err = vfs.Open(path, flags)
+ }
+ if err != nil {
+ return vfsErrorCode(err, _CANTOPEN)
+ }
+
+ if file, ok := file.(FilePowersafeOverwrite); ok {
+ name := OpenFilename(ctx, mod, zPath, flags)
+ if b, ok := util.ParseBool(name.URIParameter("psow")); ok {
+ file.SetPowersafeOverwrite(b)
+ }
+ }
+ if file, ok := file.(FileSharedMemory); ok &&
+ pOutVFS != 0 && file.SharedMemory() != nil {
+ util.WriteUint32(mod, pOutVFS, 1)
+ }
+ if pOutFlags != 0 {
+ util.WriteUint32(mod, pOutFlags, uint32(flags))
+ }
+ vfsFileRegister(ctx, mod, pFile, file)
+ return _OK
+}
+
+func vfsClose(ctx context.Context, mod api.Module, pFile uint32) _ErrorCode {
+ err := vfsFileClose(ctx, mod, pFile)
+ if err != nil {
+ return vfsErrorCode(err, _IOERR_CLOSE)
+ }
+ return _OK
+}
+
+func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+ buf := util.View(mod, zBuf, uint64(iAmt))
+
+ n, err := file.ReadAt(buf, iOfst)
+ if n == int(iAmt) {
+ return _OK
+ }
+ if err != io.EOF {
+ return vfsErrorCode(err, _IOERR_READ)
+ }
+ clear(buf[n:])
+ return _IOERR_SHORT_READ
+}
+
+func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+ buf := util.View(mod, zBuf, uint64(iAmt))
+
+ _, err := file.WriteAt(buf, iOfst)
+ if err != nil {
+ return vfsErrorCode(err, _IOERR_WRITE)
+ }
+ return _OK
+}
+
+func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte int64) _ErrorCode {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+ err := file.Truncate(nByte)
+ return vfsErrorCode(err, _IOERR_TRUNCATE)
+}
+
+func vfsSync(ctx context.Context, mod api.Module, pFile uint32, flags SyncFlag) _ErrorCode {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+ err := file.Sync(flags)
+ return vfsErrorCode(err, _IOERR_FSYNC)
+}
+
+func vfsFileSize(ctx context.Context, mod api.Module, pFile, pSize uint32) _ErrorCode {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+ size, err := file.Size()
+ util.WriteUint64(mod, pSize, uint64(size))
+ return vfsErrorCode(err, _IOERR_SEEK)
+}
+
+func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+ err := file.Lock(eLock)
+ return vfsErrorCode(err, _IOERR_LOCK)
+}
+
+func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+ err := file.Unlock(eLock)
+ return vfsErrorCode(err, _IOERR_UNLOCK)
+}
+
+func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) _ErrorCode {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+ locked, err := file.CheckReservedLock()
+
+ var res uint32
+ if locked {
+ res = 1
+ }
+
+ util.WriteUint32(mod, pResOut, res)
+ return vfsErrorCode(err, _IOERR_CHECKRESERVEDLOCK)
+}
+
+func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _FcntlOpcode, pArg uint32) _ErrorCode {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+
+ switch op {
+ case _FCNTL_LOCKSTATE:
+ if file, ok := file.(FileLockState); ok {
+ util.WriteUint32(mod, pArg, uint32(file.LockState()))
+ return _OK
+ }
+
+ case _FCNTL_PERSIST_WAL:
+ if file, ok := file.(FilePersistentWAL); ok {
+ if i := util.ReadUint32(mod, pArg); int32(i) >= 0 {
+ file.SetPersistentWAL(i != 0)
+ } else if file.PersistentWAL() {
+ util.WriteUint32(mod, pArg, 1)
+ } else {
+ util.WriteUint32(mod, pArg, 0)
+ }
+ return _OK
+ }
+
+ case _FCNTL_POWERSAFE_OVERWRITE:
+ if file, ok := file.(FilePowersafeOverwrite); ok {
+ if i := util.ReadUint32(mod, pArg); int32(i) >= 0 {
+ file.SetPowersafeOverwrite(i != 0)
+ } else if file.PowersafeOverwrite() {
+ util.WriteUint32(mod, pArg, 1)
+ } else {
+ util.WriteUint32(mod, pArg, 0)
+ }
+ return _OK
+ }
+
+ case _FCNTL_CHUNK_SIZE:
+ if file, ok := file.(FileChunkSize); ok {
+ size := util.ReadUint32(mod, pArg)
+ file.ChunkSize(int(size))
+ return _OK
+ }
+
+ case _FCNTL_SIZE_HINT:
+ if file, ok := file.(FileSizeHint); ok {
+ size := util.ReadUint64(mod, pArg)
+ err := file.SizeHint(int64(size))
+ return vfsErrorCode(err, _IOERR_TRUNCATE)
+ }
+
+ case _FCNTL_HAS_MOVED:
+ if file, ok := file.(FileHasMoved); ok {
+ moved, err := file.HasMoved()
+ var res uint32
+ if moved {
+ res = 1
+ }
+ util.WriteUint32(mod, pArg, res)
+ return vfsErrorCode(err, _IOERR_FSTAT)
+ }
+
+ case _FCNTL_OVERWRITE:
+ if file, ok := file.(FileOverwrite); ok {
+ err := file.Overwrite()
+ return vfsErrorCode(err, _IOERR)
+ }
+
+ case _FCNTL_COMMIT_PHASETWO:
+ if file, ok := file.(FileCommitPhaseTwo); ok {
+ err := file.CommitPhaseTwo()
+ return vfsErrorCode(err, _IOERR)
+ }
+
+ case _FCNTL_BEGIN_ATOMIC_WRITE:
+ if file, ok := file.(FileBatchAtomicWrite); ok {
+ err := file.BeginAtomicWrite()
+ return vfsErrorCode(err, _IOERR_BEGIN_ATOMIC)
+ }
+ case _FCNTL_COMMIT_ATOMIC_WRITE:
+ if file, ok := file.(FileBatchAtomicWrite); ok {
+ err := file.CommitAtomicWrite()
+ return vfsErrorCode(err, _IOERR_COMMIT_ATOMIC)
+ }
+ case _FCNTL_ROLLBACK_ATOMIC_WRITE:
+ if file, ok := file.(FileBatchAtomicWrite); ok {
+ err := file.RollbackAtomicWrite()
+ return vfsErrorCode(err, _IOERR_ROLLBACK_ATOMIC)
+ }
+
+ case _FCNTL_CKPT_DONE:
+ if file, ok := file.(FileCheckpoint); ok {
+ err := file.CheckpointDone()
+ return vfsErrorCode(err, _IOERR)
+ }
+ case _FCNTL_CKPT_START:
+ if file, ok := file.(FileCheckpoint); ok {
+ err := file.CheckpointStart()
+ return vfsErrorCode(err, _IOERR)
+ }
+
+ case _FCNTL_PRAGMA:
+ if file, ok := file.(FilePragma); ok {
+ ptr := util.ReadUint32(mod, pArg+1*ptrlen)
+ name := util.ReadString(mod, ptr, _MAX_SQL_LENGTH)
+ var value string
+ if ptr := util.ReadUint32(mod, pArg+2*ptrlen); ptr != 0 {
+ value = util.ReadString(mod, ptr, _MAX_SQL_LENGTH)
+ }
+
+ out, err := file.Pragma(name, value)
+
+ ret := vfsErrorCode(err, _ERROR)
+ if ret == _ERROR {
+ out = err.Error()
+ }
+ if out != "" {
+ fn := mod.ExportedFunction("malloc")
+ stack := [...]uint64{uint64(len(out) + 1)}
+ if err := fn.CallWithStack(ctx, stack[:]); err != nil {
+ panic(err)
+ }
+ util.WriteUint32(mod, pArg, uint32(stack[0]))
+ util.WriteString(mod, uint32(stack[0]), out)
+ }
+ return ret
+ }
+ }
+
+ // Consider also implementing these opcodes (in use by SQLite):
+ // _FCNTL_BUSYHANDLER
+ // _FCNTL_LAST_ERRNO
+ // _FCNTL_SYNC
+ return _NOTFOUND
+}
+
+func vfsSectorSize(ctx context.Context, mod api.Module, pFile uint32) uint32 {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+ return uint32(file.SectorSize())
+}
+
+func vfsDeviceCharacteristics(ctx context.Context, mod api.Module, pFile uint32) DeviceCharacteristic {
+ file := vfsFileGet(ctx, mod, pFile).(File)
+ return file.DeviceCharacteristics()
+}
+
+var shmBarrier sync.Mutex
+
+func vfsShmBarrier(ctx context.Context, mod api.Module, pFile uint32) {
+ shmBarrier.Lock()
+ defer shmBarrier.Unlock()
+}
+
+func vfsShmMap(ctx context.Context, mod api.Module, pFile uint32, iRegion, szRegion int32, bExtend, pp uint32) _ErrorCode {
+ shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory()
+ p, err := shm.shmMap(ctx, mod, iRegion, szRegion, bExtend != 0)
+ if err != nil {
+ return vfsErrorCode(err, _IOERR_SHMMAP)
+ }
+ util.WriteUint32(mod, pp, p)
+ return _OK
+}
+
+func vfsShmLock(ctx context.Context, mod api.Module, pFile uint32, offset, n int32, flags _ShmFlag) _ErrorCode {
+ shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory()
+ err := shm.shmLock(offset, n, flags)
+ return vfsErrorCode(err, _IOERR_SHMLOCK)
+}
+
+func vfsShmUnmap(ctx context.Context, mod api.Module, pFile, bDelete uint32) _ErrorCode {
+ shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory()
+ shm.shmUnmap(bDelete != 0)
+ return _OK
+}
+
+func vfsGet(mod api.Module, pVfs uint32) VFS {
+ var name string
+ if pVfs != 0 {
+ const zNameOffset = 16
+ name = util.ReadString(mod, util.ReadUint32(mod, pVfs+zNameOffset), _MAX_NAME)
+ }
+ if vfs := Find(name); vfs != nil {
+ return vfs
+ }
+ panic(util.NoVFSErr + util.ErrorString(name))
+}
+
+func vfsFileRegister(ctx context.Context, mod api.Module, pFile uint32, file File) {
+ const fileHandleOffset = 4
+ id := util.AddHandle(ctx, file)
+ util.WriteUint32(mod, pFile+fileHandleOffset, id)
+}
+
+func vfsFileGet(ctx context.Context, mod api.Module, pFile uint32) any {
+ const fileHandleOffset = 4
+ id := util.ReadUint32(mod, pFile+fileHandleOffset)
+ return util.GetHandle(ctx, id)
+}
+
+func vfsFileClose(ctx context.Context, mod api.Module, pFile uint32) error {
+ const fileHandleOffset = 4
+ id := util.ReadUint32(mod, pFile+fileHandleOffset)
+ return util.DelHandle(ctx, id)
+}
+
+func vfsErrorCode(err error, def _ErrorCode) _ErrorCode {
+ if err == nil {
+ return _OK
+ }
+ switch v := reflect.ValueOf(err); v.Kind() {
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32:
+ return _ErrorCode(v.Uint())
+ }
+ return def
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vtab.go b/vendor/github.com/ncruces/go-sqlite3/vtab.go
new file mode 100644
index 000000000..a330c98ff
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vtab.go
@@ -0,0 +1,663 @@
+package sqlite3
+
+import (
+ "context"
+ "reflect"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/tetratelabs/wazero/api"
+)
+
+// CreateModule registers a new virtual table module name.
+// If create is nil, the virtual table is eponymous.
+//
+// https://sqlite.org/c3ref/create_module.html
+func CreateModule[T VTab](db *Conn, name string, create, connect VTabConstructor[T]) error {
+ var flags int
+
+ const (
+ VTAB_CREATOR = 0x01
+ VTAB_DESTROYER = 0x02
+ VTAB_UPDATER = 0x04
+ VTAB_RENAMER = 0x08
+ VTAB_OVERLOADER = 0x10
+ VTAB_CHECKER = 0x20
+ VTAB_TXN = 0x40
+ VTAB_SAVEPOINTER = 0x80
+ )
+
+ if create != nil {
+ flags |= VTAB_CREATOR
+ }
+
+ vtab := reflect.TypeOf(connect).Out(0)
+ if implements[VTabDestroyer](vtab) {
+ flags |= VTAB_DESTROYER
+ }
+ if implements[VTabUpdater](vtab) {
+ flags |= VTAB_UPDATER
+ }
+ if implements[VTabRenamer](vtab) {
+ flags |= VTAB_RENAMER
+ }
+ if implements[VTabOverloader](vtab) {
+ flags |= VTAB_OVERLOADER
+ }
+ if implements[VTabChecker](vtab) {
+ flags |= VTAB_CHECKER
+ }
+ if implements[VTabTxn](vtab) {
+ flags |= VTAB_TXN
+ }
+ if implements[VTabSavepointer](vtab) {
+ flags |= VTAB_SAVEPOINTER
+ }
+
+ defer db.arena.mark()()
+ namePtr := db.arena.string(name)
+ modulePtr := util.AddHandle(db.ctx, module[T]{create, connect})
+ r := db.call("sqlite3_create_module_go", uint64(db.handle),
+ uint64(namePtr), uint64(flags), uint64(modulePtr))
+ return db.error(r)
+}
+
+func implements[T any](typ reflect.Type) bool {
+ var ptr *T
+ return typ.Implements(reflect.TypeOf(ptr).Elem())
+}
+
+// DeclareVTab declares the schema of a virtual table.
+//
+// https://sqlite.org/c3ref/declare_vtab.html
+func (c *Conn) DeclareVTab(sql string) error {
+ defer c.arena.mark()()
+ sqlPtr := c.arena.string(sql)
+ r := c.call("sqlite3_declare_vtab", uint64(c.handle), uint64(sqlPtr))
+ return c.error(r)
+}
+
+// VTabConflictMode is a virtual table conflict resolution mode.
+//
+// https://sqlite.org/c3ref/c_fail.html
+type VTabConflictMode uint8
+
+const (
+ VTAB_ROLLBACK VTabConflictMode = 1
+ VTAB_IGNORE VTabConflictMode = 2
+ VTAB_FAIL VTabConflictMode = 3
+ VTAB_ABORT VTabConflictMode = 4
+ VTAB_REPLACE VTabConflictMode = 5
+)
+
+// VTabOnConflict determines the virtual table conflict policy.
+//
+// https://sqlite.org/c3ref/vtab_on_conflict.html
+func (c *Conn) VTabOnConflict() VTabConflictMode {
+ r := c.call("sqlite3_vtab_on_conflict", uint64(c.handle))
+ return VTabConflictMode(r)
+}
+
+// VTabConfigOption is a virtual table configuration option.
+//
+// https://sqlite.org/c3ref/c_vtab_constraint_support.html
+type VTabConfigOption uint8
+
+const (
+ VTAB_CONSTRAINT_SUPPORT VTabConfigOption = 1
+ VTAB_INNOCUOUS VTabConfigOption = 2
+ VTAB_DIRECTONLY VTabConfigOption = 3
+ VTAB_USES_ALL_SCHEMAS VTabConfigOption = 4
+)
+
+// VTabConfig configures various facets of the virtual table interface.
+//
+// https://sqlite.org/c3ref/vtab_config.html
+func (c *Conn) VTabConfig(op VTabConfigOption, args ...any) error {
+ var i uint64
+ if op == VTAB_CONSTRAINT_SUPPORT && len(args) > 0 {
+ if b, ok := args[0].(bool); ok && b {
+ i = 1
+ }
+ }
+ r := c.call("sqlite3_vtab_config_go", uint64(c.handle), uint64(op), i)
+ return c.error(r)
+}
+
+// VTabConstructor is a virtual table constructor function.
+type VTabConstructor[T VTab] func(db *Conn, module, schema, table string, arg ...string) (T, error)
+
+type module[T VTab] [2]VTabConstructor[T]
+
+type vtabConstructor int
+
+const (
+ xCreate vtabConstructor = 0
+ xConnect vtabConstructor = 1
+)
+
+// A VTab describes a particular instance of the virtual table.
+// A VTab may optionally implement [io.Closer] to free resources.
+//
+// https://sqlite.org/c3ref/vtab.html
+type VTab interface {
+ // https://sqlite.org/vtab.html#xbestindex
+ BestIndex(*IndexInfo) error
+ // https://sqlite.org/vtab.html#xopen
+ Open() (VTabCursor, error)
+}
+
+// A VTabDestroyer allows a virtual table to drop persistent state.
+type VTabDestroyer interface {
+ VTab
+ // https://sqlite.org/vtab.html#sqlite3_module.xDestroy
+ Destroy() error
+}
+
+// A VTabUpdater allows a virtual table to be updated.
+type VTabUpdater interface {
+ VTab
+ // https://sqlite.org/vtab.html#xupdate
+ Update(arg ...Value) (rowid int64, err error)
+}
+
+// A VTabRenamer allows a virtual table to be renamed.
+type VTabRenamer interface {
+ VTab
+ // https://sqlite.org/vtab.html#xrename
+ Rename(new string) error
+}
+
+// A VTabOverloader allows a virtual table to overload SQL functions.
+type VTabOverloader interface {
+ VTab
+ // https://sqlite.org/vtab.html#xfindfunction
+ FindFunction(arg int, name string) (ScalarFunction, IndexConstraintOp)
+}
+
+// A VTabChecker allows a virtual table to report errors
+// to the PRAGMA integrity_check and PRAGMA quick_check commands.
+//
+// Integrity should return an error if it finds problems in the content of the virtual table,
+// but should avoid returning a (wrapped) [Error], [ErrorCode] or [ExtendedErrorCode],
+// as those indicate the Integrity method itself encountered problems
+// while trying to evaluate the virtual table content.
+type VTabChecker interface {
+ VTab
+ // https://sqlite.org/vtab.html#xintegrity
+ Integrity(schema, table string, flags int) error
+}
+
+// A VTabTxn allows a virtual table to implement
+// transactions with two-phase commit.
+//
+// Anything that is required as part of a commit that may fail
+// should be performed in the Sync() callback.
+// Current versions of SQLite ignore any errors
+// returned by Commit() and Rollback().
+type VTabTxn interface {
+ VTab
+ // https://sqlite.org/vtab.html#xBegin
+ Begin() error
+ // https://sqlite.org/vtab.html#xsync
+ Sync() error
+ // https://sqlite.org/vtab.html#xcommit
+ Commit() error
+ // https://sqlite.org/vtab.html#xrollback
+ Rollback() error
+}
+
+// A VTabSavepointer allows a virtual table to implement
+// nested transactions.
+//
+// https://sqlite.org/vtab.html#xsavepoint
+type VTabSavepointer interface {
+ VTabTxn
+ Savepoint(id int) error
+ Release(id int) error
+ RollbackTo(id int) error
+}
+
+// A VTabCursor describes cursors that point
+// into the virtual table and are used
+// to loop through the virtual table.
+// A VTabCursor may optionally implement
+// [io.Closer] to free resources.
+//
+// http://sqlite.org/c3ref/vtab_cursor.html
+type VTabCursor interface {
+ // https://sqlite.org/vtab.html#xfilter
+ Filter(idxNum int, idxStr string, arg ...Value) error
+ // https://sqlite.org/vtab.html#xnext
+ Next() error
+ // https://sqlite.org/vtab.html#xeof
+ EOF() bool
+ // https://sqlite.org/vtab.html#xcolumn
+ Column(ctx *Context, n int) error
+ // https://sqlite.org/vtab.html#xrowid
+ RowID() (int64, error)
+}
+
+// An IndexInfo describes virtual table indexing information.
+//
+// https://sqlite.org/c3ref/index_info.html
+type IndexInfo struct {
+ // Inputs
+ Constraint []IndexConstraint
+ OrderBy []IndexOrderBy
+ ColumnsUsed int64
+ // Outputs
+ ConstraintUsage []IndexConstraintUsage
+ IdxNum int
+ IdxStr string
+ IdxFlags IndexScanFlag
+ OrderByConsumed bool
+ EstimatedCost float64
+ EstimatedRows int64
+ // Internal
+ c *Conn
+ handle uint32
+}
+
+// An IndexConstraint describes virtual table indexing constraint information.
+//
+// https://sqlite.org/c3ref/index_info.html
+type IndexConstraint struct {
+ Column int
+ Op IndexConstraintOp
+ Usable bool
+}
+
+// An IndexOrderBy describes virtual table indexing order by information.
+//
+// https://sqlite.org/c3ref/index_info.html
+type IndexOrderBy struct {
+ Column int
+ Desc bool
+}
+
+// An IndexConstraintUsage describes how virtual table indexing constraints will be used.
+//
+// https://sqlite.org/c3ref/index_info.html
+type IndexConstraintUsage struct {
+ ArgvIndex int
+ Omit bool
+}
+
+// RHSValue returns the value of the right-hand operand of a constraint
+// if the right-hand operand is known.
+//
+// https://sqlite.org/c3ref/vtab_rhs_value.html
+func (idx *IndexInfo) RHSValue(column int) (Value, error) {
+ defer idx.c.arena.mark()()
+ valPtr := idx.c.arena.new(ptrlen)
+ r := idx.c.call("sqlite3_vtab_rhs_value", uint64(idx.handle),
+ uint64(column), uint64(valPtr))
+ if err := idx.c.error(r); err != nil {
+ return Value{}, err
+ }
+ return Value{
+ c: idx.c,
+ handle: util.ReadUint32(idx.c.mod, valPtr),
+ }, nil
+}
+
+// Collation returns the name of the collation for a virtual table constraint.
+//
+// https://sqlite.org/c3ref/vtab_collation.html
+func (idx *IndexInfo) Collation(column int) string {
+ r := idx.c.call("sqlite3_vtab_collation", uint64(idx.handle),
+ uint64(column))
+ return util.ReadString(idx.c.mod, uint32(r), _MAX_NAME)
+}
+
+// Distinct determines if a virtual table query is DISTINCT.
+//
+// https://sqlite.org/c3ref/vtab_distinct.html
+func (idx *IndexInfo) Distinct() int {
+ r := idx.c.call("sqlite3_vtab_distinct", uint64(idx.handle))
+ return int(r)
+}
+
+// In identifies and handles IN constraints.
+//
+// https://sqlite.org/c3ref/vtab_in.html
+func (idx *IndexInfo) In(column, handle int) bool {
+ r := idx.c.call("sqlite3_vtab_in", uint64(idx.handle),
+ uint64(column), uint64(handle))
+ return r != 0
+}
+
+func (idx *IndexInfo) load() {
+ // https://sqlite.org/c3ref/index_info.html
+ mod := idx.c.mod
+ ptr := idx.handle
+
+ idx.Constraint = make([]IndexConstraint, util.ReadUint32(mod, ptr+0))
+ idx.ConstraintUsage = make([]IndexConstraintUsage, util.ReadUint32(mod, ptr+0))
+ idx.OrderBy = make([]IndexOrderBy, util.ReadUint32(mod, ptr+8))
+
+ constraintPtr := util.ReadUint32(mod, ptr+4)
+ for i := range idx.Constraint {
+ idx.Constraint[i] = IndexConstraint{
+ Column: int(int32(util.ReadUint32(mod, constraintPtr+0))),
+ Op: IndexConstraintOp(util.ReadUint8(mod, constraintPtr+4)),
+ Usable: util.ReadUint8(mod, constraintPtr+5) != 0,
+ }
+ constraintPtr += 12
+ }
+
+ orderByPtr := util.ReadUint32(mod, ptr+12)
+ for i := range idx.OrderBy {
+ idx.OrderBy[i] = IndexOrderBy{
+ Column: int(int32(util.ReadUint32(mod, orderByPtr+0))),
+ Desc: util.ReadUint8(mod, orderByPtr+4) != 0,
+ }
+ orderByPtr += 8
+ }
+
+ idx.EstimatedCost = util.ReadFloat64(mod, ptr+40)
+ idx.EstimatedRows = int64(util.ReadUint64(mod, ptr+48))
+ idx.ColumnsUsed = int64(util.ReadUint64(mod, ptr+64))
+}
+
+func (idx *IndexInfo) save() {
+ // https://sqlite.org/c3ref/index_info.html
+ mod := idx.c.mod
+ ptr := idx.handle
+
+ usagePtr := util.ReadUint32(mod, ptr+16)
+ for _, usage := range idx.ConstraintUsage {
+ util.WriteUint32(mod, usagePtr+0, uint32(usage.ArgvIndex))
+ if usage.Omit {
+ util.WriteUint8(mod, usagePtr+4, 1)
+ }
+ usagePtr += 8
+ }
+
+ util.WriteUint32(mod, ptr+20, uint32(idx.IdxNum))
+ if idx.IdxStr != "" {
+ util.WriteUint32(mod, ptr+24, idx.c.newString(idx.IdxStr))
+ util.WriteUint32(mod, ptr+28, 1) // needToFreeIdxStr
+ }
+ if idx.OrderByConsumed {
+ util.WriteUint32(mod, ptr+32, 1)
+ }
+ util.WriteFloat64(mod, ptr+40, idx.EstimatedCost)
+ util.WriteUint64(mod, ptr+48, uint64(idx.EstimatedRows))
+ util.WriteUint32(mod, ptr+56, uint32(idx.IdxFlags))
+}
+
+// IndexConstraintOp is a virtual table constraint operator code.
+//
+// https://sqlite.org/c3ref/c_index_constraint_eq.html
+type IndexConstraintOp uint8
+
+const (
+ INDEX_CONSTRAINT_EQ IndexConstraintOp = 2
+ INDEX_CONSTRAINT_GT IndexConstraintOp = 4
+ INDEX_CONSTRAINT_LE IndexConstraintOp = 8
+ INDEX_CONSTRAINT_LT IndexConstraintOp = 16
+ INDEX_CONSTRAINT_GE IndexConstraintOp = 32
+ INDEX_CONSTRAINT_MATCH IndexConstraintOp = 64
+ INDEX_CONSTRAINT_LIKE IndexConstraintOp = 65
+ INDEX_CONSTRAINT_GLOB IndexConstraintOp = 66
+ INDEX_CONSTRAINT_REGEXP IndexConstraintOp = 67
+ INDEX_CONSTRAINT_NE IndexConstraintOp = 68
+ INDEX_CONSTRAINT_ISNOT IndexConstraintOp = 69
+ INDEX_CONSTRAINT_ISNOTNULL IndexConstraintOp = 70
+ INDEX_CONSTRAINT_ISNULL IndexConstraintOp = 71
+ INDEX_CONSTRAINT_IS IndexConstraintOp = 72
+ INDEX_CONSTRAINT_LIMIT IndexConstraintOp = 73
+ INDEX_CONSTRAINT_OFFSET IndexConstraintOp = 74
+ INDEX_CONSTRAINT_FUNCTION IndexConstraintOp = 150
+)
+
+// IndexScanFlag is a virtual table scan flag.
+//
+// https://sqlite.org/c3ref/c_index_scan_unique.html
+type IndexScanFlag uint32
+
+const (
+ INDEX_SCAN_UNIQUE IndexScanFlag = 1
+)
+
+func vtabModuleCallback(i vtabConstructor) func(_ context.Context, _ api.Module, _, _, _, _, _ uint32) uint32 {
+ return func(ctx context.Context, mod api.Module, pMod, nArg, pArg, ppVTab, pzErr uint32) uint32 {
+ arg := make([]reflect.Value, 1+nArg)
+ arg[0] = reflect.ValueOf(ctx.Value(connKey{}))
+
+ for i := uint32(0); i < nArg; i++ {
+ ptr := util.ReadUint32(mod, pArg+i*ptrlen)
+ arg[i+1] = reflect.ValueOf(util.ReadString(mod, ptr, _MAX_SQL_LENGTH))
+ }
+
+ module := vtabGetHandle(ctx, mod, pMod)
+ res := reflect.ValueOf(module).Index(int(i)).Call(arg)
+ err, _ := res[1].Interface().(error)
+ if err == nil {
+ vtabPutHandle(ctx, mod, ppVTab, res[0].Interface())
+ }
+
+ return vtabError(ctx, mod, pzErr, _PTR_ERROR, err)
+ }
+}
+
+func vtabDisconnectCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
+ err := vtabDelHandle(ctx, mod, pVTab)
+ return vtabError(ctx, mod, 0, _PTR_ERROR, err)
+}
+
+func vtabDestroyCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabDestroyer)
+ err := vtab.Destroy()
+ if cerr := vtabDelHandle(ctx, mod, pVTab); err == nil {
+ err = cerr
+ }
+ return vtabError(ctx, mod, 0, _PTR_ERROR, err)
+}
+
+func vtabBestIndexCallback(ctx context.Context, mod api.Module, pVTab, pIdxInfo uint32) uint32 {
+ var info IndexInfo
+ info.handle = pIdxInfo
+ info.c = ctx.Value(connKey{}).(*Conn)
+ info.load()
+
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTab)
+ err := vtab.BestIndex(&info)
+
+ info.save()
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func vtabUpdateCallback(ctx context.Context, mod api.Module, pVTab, nArg, pArg, pRowID uint32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabUpdater)
+
+ db := ctx.Value(connKey{}).(*Conn)
+ args := make([]Value, nArg)
+ callbackArgs(db, args, pArg)
+ rowID, err := vtab.Update(args...)
+ if err == nil {
+ util.WriteUint64(mod, pRowID, uint64(rowID))
+ }
+
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func vtabRenameCallback(ctx context.Context, mod api.Module, pVTab, zNew uint32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabRenamer)
+ err := vtab.Rename(util.ReadString(mod, zNew, _MAX_NAME))
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func vtabFindFuncCallback(ctx context.Context, mod api.Module, pVTab uint32, nArg int32, zName, pxFunc uint32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabOverloader)
+ f, op := vtab.FindFunction(int(nArg), util.ReadString(mod, zName, _MAX_NAME))
+ if op != 0 {
+ var wrapper uint32
+ wrapper = util.AddHandle(ctx, func(c Context, arg ...Value) {
+ defer util.DelHandle(ctx, wrapper)
+ f(c, arg...)
+ })
+ util.WriteUint32(mod, pxFunc, wrapper)
+ }
+ return uint32(op)
+}
+
+func vtabIntegrityCallback(ctx context.Context, mod api.Module, pVTab, zSchema, zTabName, mFlags, pzErr uint32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabChecker)
+ schema := util.ReadString(mod, zSchema, _MAX_NAME)
+ table := util.ReadString(mod, zTabName, _MAX_NAME)
+ err := vtab.Integrity(schema, table, int(mFlags))
+ // xIntegrity should return OK - even if it finds problems in the content of the virtual table.
+ // https://sqlite.org/vtab.html#xintegrity
+ vtabError(ctx, mod, pzErr, _PTR_ERROR, err)
+ _, code := errorCode(err, _OK)
+ return code
+}
+
+func vtabBeginCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn)
+ err := vtab.Begin()
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func vtabSyncCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn)
+ err := vtab.Sync()
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func vtabCommitCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn)
+ err := vtab.Commit()
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func vtabRollbackCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn)
+ err := vtab.Rollback()
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func vtabSavepointCallback(ctx context.Context, mod api.Module, pVTab uint32, id int32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabSavepointer)
+ err := vtab.Savepoint(int(id))
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func vtabReleaseCallback(ctx context.Context, mod api.Module, pVTab uint32, id int32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabSavepointer)
+ err := vtab.Release(int(id))
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func vtabRollbackToCallback(ctx context.Context, mod api.Module, pVTab uint32, id int32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTabSavepointer)
+ err := vtab.RollbackTo(int(id))
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func cursorOpenCallback(ctx context.Context, mod api.Module, pVTab, ppCur uint32) uint32 {
+ vtab := vtabGetHandle(ctx, mod, pVTab).(VTab)
+
+ cursor, err := vtab.Open()
+ if err == nil {
+ vtabPutHandle(ctx, mod, ppCur, cursor)
+ }
+
+ return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
+}
+
+func cursorCloseCallback(ctx context.Context, mod api.Module, pCur uint32) uint32 {
+ err := vtabDelHandle(ctx, mod, pCur)
+ return vtabError(ctx, mod, 0, _VTAB_ERROR, err)
+}
+
+func cursorFilterCallback(ctx context.Context, mod api.Module, pCur uint32, idxNum int32, idxStr, nArg, pArg uint32) uint32 {
+ cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
+ db := ctx.Value(connKey{}).(*Conn)
+ args := make([]Value, nArg)
+ callbackArgs(db, args, pArg)
+ var idxName string
+ if idxStr != 0 {
+ idxName = util.ReadString(mod, idxStr, _MAX_LENGTH)
+ }
+ err := cursor.Filter(int(idxNum), idxName, args...)
+ return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
+}
+
+func cursorEOFCallback(ctx context.Context, mod api.Module, pCur uint32) uint32 {
+ cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
+ if cursor.EOF() {
+ return 1
+ }
+ return 0
+}
+
+func cursorNextCallback(ctx context.Context, mod api.Module, pCur uint32) uint32 {
+ cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
+ err := cursor.Next()
+ return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
+}
+
+func cursorColumnCallback(ctx context.Context, mod api.Module, pCur, pCtx uint32, n int32) uint32 {
+ cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
+ db := ctx.Value(connKey{}).(*Conn)
+ err := cursor.Column(&Context{db, pCtx}, int(n))
+ return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
+}
+
+func cursorRowIDCallback(ctx context.Context, mod api.Module, pCur, pRowID uint32) uint32 {
+ cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
+
+ rowID, err := cursor.RowID()
+ if err == nil {
+ util.WriteUint64(mod, pRowID, uint64(rowID))
+ }
+
+ return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
+}
+
+const (
+ _PTR_ERROR = iota
+ _VTAB_ERROR
+ _CURSOR_ERROR
+)
+
+func vtabError(ctx context.Context, mod api.Module, ptr, kind uint32, err error) uint32 {
+ const zErrMsgOffset = 8
+ msg, code := errorCode(err, ERROR)
+ if msg != "" && ptr != 0 {
+ switch kind {
+ case _VTAB_ERROR:
+ ptr = ptr + zErrMsgOffset // zErrMsg
+ case _CURSOR_ERROR:
+ ptr = util.ReadUint32(mod, ptr) + zErrMsgOffset // pVTab->zErrMsg
+ }
+ db := ctx.Value(connKey{}).(*Conn)
+ if ptr := util.ReadUint32(mod, ptr); ptr != 0 {
+ db.free(ptr)
+ }
+ util.WriteUint32(mod, ptr, db.newString(msg))
+ }
+ return code
+}
+
+func vtabGetHandle(ctx context.Context, mod api.Module, ptr uint32) any {
+ const handleOffset = 4
+ handle := util.ReadUint32(mod, ptr-handleOffset)
+ return util.GetHandle(ctx, handle)
+}
+
+func vtabDelHandle(ctx context.Context, mod api.Module, ptr uint32) error {
+ const handleOffset = 4
+ handle := util.ReadUint32(mod, ptr-handleOffset)
+ return util.DelHandle(ctx, handle)
+}
+
+func vtabPutHandle(ctx context.Context, mod api.Module, pptr uint32, val any) {
+ const handleOffset = 4
+ handle := util.AddHandle(ctx, val)
+ ptr := util.ReadUint32(mod, pptr)
+ util.WriteUint32(mod, ptr-handleOffset, handle)
+}