diff --git a/go.mod b/go.mod index c3e1c914f..d57278d74 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/miekg/dns v1.1.62 github.com/minio/minio-go/v7 v7.0.78 github.com/mitchellh/mapstructure v1.5.0 - github.com/ncruces/go-sqlite3 v0.19.0 + github.com/ncruces/go-sqlite3 v0.20.0 github.com/oklog/ulid v1.3.1 github.com/prometheus/client_golang v1.20.5 github.com/spf13/cobra v1.8.1 diff --git a/go.sum b/go.sum index df5a17d71..6a29eaada 100644 --- a/go.sum +++ b/go.sum @@ -434,8 +434,8 @@ github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/ncruces/go-sqlite3 v0.19.0 h1:yebbD/cP8Gf+7nKoUin2ATjnqJK2VvyS30d3xsjRp5k= -github.com/ncruces/go-sqlite3 v0.19.0/go.mod h1:yL4ZNWGsr1/8pcLfpPW1RT1WFdvyeHonrgIwwi4rvkg= +github.com/ncruces/go-sqlite3 v0.20.0 h1:/nBLvYxj7sk9S6y57nmMFvoQ/KJtGo0pNi8J80s8oJU= +github.com/ncruces/go-sqlite3 v0.20.0/go.mod h1:yL4ZNWGsr1/8pcLfpPW1RT1WFdvyeHonrgIwwi4rvkg= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M= diff --git a/vendor/github.com/ncruces/go-sqlite3/README.md b/vendor/github.com/ncruces/go-sqlite3/README.md index 2ba19ccd5..935b9f254 100644 --- a/vendor/github.com/ncruces/go-sqlite3/README.md +++ b/vendor/github.com/ncruces/go-sqlite3/README.md @@ -1,4 +1,4 @@ -# Go bindings to SQLite using Wazero +# 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) @@ -41,45 +41,6 @@ db.QueryRow(`SELECT sqlite_version()`).Scan(&version) - [`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/bloom`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/bloom) - provides a [Bloom filter](https://github.com/nalgeon/sqlean/issues/27#issuecomment-1002267134) virtual table. -- [`github.com/ncruces/go-sqlite3/ext/closure`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/closure) - provides a transitive closure virtual table. -- [`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/regexp`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/regexp) - provides regular expression functions. -- [`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/uuid`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/uuid) - generates [UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier). -- [`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/adiantum`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/adiantum) - wraps a VFS to offer encryption at rest. -- [`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. - ### Advanced features - [incremental BLOB I/O](https://sqlite.org/c3ref/blob_open.html) @@ -92,7 +53,11 @@ db.QueryRow(`SELECT sqlite_version()`).Scan(&version) - [math functions](https://sqlite.org/lang_mathfunc.html) - [full-text search](https://sqlite.org/fts5.html) - [geospatial search](https://sqlite.org/geopoly.html) +- [Unicode support](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/unicode) +- [statistics functions](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/stats) - [encryption at rest](vfs/adiantum/README.md) +- [many extensions](ext/README.md) +- [custom VFSes](vfs/README.md#custom-vfses) - [and moreā€¦](embed/README.md) ### Caveats diff --git a/vendor/github.com/ncruces/go-sqlite3/blob.go b/vendor/github.com/ncruces/go-sqlite3/blob.go index 268dfab0f..a0969eb69 100644 --- a/vendor/github.com/ncruces/go-sqlite3/blob.go +++ b/vendor/github.com/ncruces/go-sqlite3/blob.go @@ -253,6 +253,7 @@ func (b *Blob) Seek(offset int64, whence int) (int64, error) { // // https://sqlite.org/c3ref/blob_reopen.html func (b *Blob) Reopen(row int64) error { + b.c.checkInterrupt(b.c.handle) 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 diff --git a/vendor/github.com/ncruces/go-sqlite3/config.go b/vendor/github.com/ncruces/go-sqlite3/config.go index 6876ba50c..cf72cbda5 100644 --- a/vendor/github.com/ncruces/go-sqlite3/config.go +++ b/vendor/github.com/ncruces/go-sqlite3/config.go @@ -2,10 +2,12 @@ import ( "context" + "strconv" + + "github.com/tetratelabs/wazero/api" "github.com/ncruces/go-sqlite3/internal/util" "github.com/ncruces/go-sqlite3/vfs" - "github.com/tetratelabs/wazero/api" ) // Config makes configuration changes to a database connection. @@ -15,8 +17,19 @@ // // https://sqlite.org/c3ref/db_config.html func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) { + if op < DBCONFIG_ENABLE_FKEY || op > DBCONFIG_REVERSE_SCANORDER { + return false, MISUSE + } + + // We need to call sqlite3_db_config, a variadic function. + // We only support the `int int*` variants. + // The int is a three-valued bool: -1 queries, 0/1 sets false/true. + // The int* points to where new state will be written to. + // The vararg is a pointer to an array containing these arguments: + // an int and an int* pointing to that int. + defer c.arena.mark()() - argsPtr := c.arena.new(2 * ptrlen) + argsPtr := c.arena.new(intlen + ptrlen) var flag int switch { @@ -63,18 +76,23 @@ func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) { // https://sqlite.org/c3ref/file_control.html func (c *Conn) FileControl(schema string, op FcntlOpcode, arg ...any) (any, error) { defer c.arena.mark()() + ptr := c.arena.new(max(ptrlen, intlen)) var schemaPtr uint32 if schema != "" { schemaPtr = c.arena.string(schema) } + var rc uint64 + var res any switch op { + default: + return nil, MISUSE + case FCNTL_RESET_CACHE: - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), 0) - return nil, c.error(r) case FCNTL_PERSIST_WAL, FCNTL_POWERSAFE_OVERWRITE: var flag int @@ -84,70 +102,69 @@ func (c *Conn) FileControl(schema string, op FcntlOpcode, arg ...any) (any, erro case arg[0]: flag = 1 } - ptr := c.arena.new(4) util.WriteUint32(c.mod, ptr, uint32(flag)) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - return util.ReadUint32(c.mod, ptr) != 0, c.error(r) + res = util.ReadUint32(c.mod, ptr) != 0 case FCNTL_CHUNK_SIZE: - ptr := c.arena.new(4) util.WriteUint32(c.mod, ptr, uint32(arg[0].(int))) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - return nil, c.error(r) case FCNTL_RESERVE_BYTES: bytes := -1 if len(arg) > 0 { bytes = arg[0].(int) } - ptr := c.arena.new(4) util.WriteUint32(c.mod, ptr, uint32(bytes)) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - return int(util.ReadUint32(c.mod, ptr)), c.error(r) + res = int(util.ReadUint32(c.mod, ptr)) case FCNTL_DATA_VERSION: - ptr := c.arena.new(4) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - return util.ReadUint32(c.mod, ptr), c.error(r) + res = util.ReadUint32(c.mod, ptr) case FCNTL_LOCKSTATE: - ptr := c.arena.new(4) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - return vfs.LockLevel(util.ReadUint32(c.mod, ptr)), c.error(r) + res = vfs.LockLevel(util.ReadUint32(c.mod, ptr)) case FCNTL_VFS_POINTER: - ptr := c.arena.new(4) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - const zNameOffset = 16 - ptr = util.ReadUint32(c.mod, ptr) - ptr = util.ReadUint32(c.mod, ptr+zNameOffset) - name := util.ReadString(c.mod, ptr, _MAX_NAME) - return vfs.Find(name), c.error(r) + if rc == _OK { + const zNameOffset = 16 + ptr = util.ReadUint32(c.mod, ptr) + ptr = util.ReadUint32(c.mod, ptr+zNameOffset) + name := util.ReadString(c.mod, ptr, _MAX_NAME) + res = vfs.Find(name) + } case FCNTL_FILE_POINTER, FCNTL_JOURNAL_POINTER: - ptr := c.arena.new(4) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - const fileHandleOffset = 4 - ptr = util.ReadUint32(c.mod, ptr) - ptr = util.ReadUint32(c.mod, ptr+fileHandleOffset) - return util.GetHandle(c.ctx, ptr), c.error(r) + if rc == _OK { + const fileHandleOffset = 4 + ptr = util.ReadUint32(c.mod, ptr) + ptr = util.ReadUint32(c.mod, ptr+fileHandleOffset) + res = util.GetHandle(c.ctx, ptr) + } } - return nil, MISUSE + if err := c.error(rc); err != nil { + return nil, err + } + return res, nil } // Limit allows the size of various constructs to be @@ -234,10 +251,10 @@ func traceCallback(ctx context.Context, mod api.Module, evt TraceEvent, pDB, pAr return rc } -// WalCheckpoint checkpoints a WAL database. +// 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) { +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) @@ -250,19 +267,19 @@ func (c *Conn) WalCheckpoint(schema string, mode CheckpointMode) (nLog, nCkpt in return nLog, nCkpt, c.error(r) } -// WalAutoCheckpoint configures WAL auto-checkpoints. +// WALAutoCheckpoint configures WAL auto-checkpoints. // // https://sqlite.org/c3ref/wal_autocheckpoint.html -func (c *Conn) WalAutoCheckpoint(pages int) error { +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 +// 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) { +func (c *Conn) WALHook(cb func(db *Conn, schema string, pages int) error) { var enable uint64 if cb != nil { enable = 1 @@ -311,3 +328,46 @@ func (c *Conn) SoftHeapLimit(n int64) int64 { func (c *Conn) HardHeapLimit(n int64) int64 { return int64(c.call("sqlite3_hard_heap_limit64", uint64(n))) } + +// EnableChecksums enables checksums on a database. +// +// https://sqlite.org/cksumvfs.html +func (c *Conn) EnableChecksums(schema string) error { + r, err := c.FileControl(schema, FCNTL_RESERVE_BYTES) + if err != nil { + return err + } + if r == 8 { + // Correct value, enabled. + return nil + } + if r == 0 { + // Default value, enable. + _, err = c.FileControl(schema, FCNTL_RESERVE_BYTES, 8) + if err != nil { + return err + } + r, err = c.FileControl(schema, FCNTL_RESERVE_BYTES) + if err != nil { + return err + } + } + if r != 8 { + // Invalid value. + return util.ErrorString("sqlite3: reserve bytes must be 8, is: " + strconv.Itoa(r.(int))) + } + + // VACUUM the database. + if schema != "" { + err = c.Exec(`VACUUM ` + QuoteIdentifier(schema)) + } else { + err = c.Exec(`VACUUM`) + } + if err != nil { + return err + } + + // Checkpoint the WAL. + _, _, err = c.WALCheckpoint(schema, CHECKPOINT_RESTART) + return err +} diff --git a/vendor/github.com/ncruces/go-sqlite3/conn.go b/vendor/github.com/ncruces/go-sqlite3/conn.go index 5dfaf9e5c..3ba4375b4 100644 --- a/vendor/github.com/ncruces/go-sqlite3/conn.go +++ b/vendor/github.com/ncruces/go-sqlite3/conn.go @@ -8,9 +8,10 @@ "strings" "time" + "github.com/tetratelabs/wazero/api" + "github.com/ncruces/go-sqlite3/internal/util" "github.com/ncruces/go-sqlite3/vfs" - "github.com/tetratelabs/wazero/api" ) // Conn is a database connection handle. @@ -204,6 +205,7 @@ func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail str tailPtr := c.arena.new(ptrlen) sqlPtr := c.arena.string(sql) + c.checkInterrupt(c.handle) r := c.call("sqlite3_prepare_v3", uint64(c.handle), uint64(sqlPtr), uint64(len(sql)+1), uint64(flags), uint64(stmtPtr), uint64(tailPtr)) @@ -457,8 +459,8 @@ func busyCallback(ctx context.Context, mod api.Module, pDB uint32, count int32) // https://sqlite.org/c3ref/db_status.html func (c *Conn) Status(op DBStatus, reset bool) (current, highwater int, err error) { defer c.arena.mark()() - hiPtr := c.arena.new(4) - curPtr := c.arena.new(4) + hiPtr := c.arena.new(intlen) + curPtr := c.arena.new(intlen) var i uint64 if reset { @@ -484,8 +486,8 @@ func (c *Conn) TableColumnMetadata(schema, table, column string) (declType, coll declTypePtr := c.arena.new(ptrlen) collSeqPtr := c.arena.new(ptrlen) notNullPtr := c.arena.new(ptrlen) - primaryKeyPtr := c.arena.new(ptrlen) autoIncPtr := c.arena.new(ptrlen) + primaryKeyPtr := c.arena.new(ptrlen) if schema != "" { schemaPtr = c.arena.string(schema) } @@ -519,10 +521,3 @@ func (c *Conn) stmtsIter(yield func(*Stmt) bool) { } } } - -// DriverConn is implemented by the SQLite [database/sql] driver connection. -// -// Deprecated: use [github.com/ncruces/go-sqlite3/driver.Conn] instead. -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 index e4c7d728e..3a6a8cdb9 100644 --- a/vendor/github.com/ncruces/go-sqlite3/const.go +++ b/vendor/github.com/ncruces/go-sqlite3/const.go @@ -13,6 +13,7 @@ _MAX_FUNCTION_ARG = 100 ptrlen = 4 + intlen = 4 ) // ErrorCode is a result code that [Error.Code] might return. @@ -177,6 +178,7 @@ DETERMINISTIC FunctionFlag = 0x000000800 DIRECTONLY FunctionFlag = 0x000080000 INNOCUOUS FunctionFlag = 0x000200000 + SELFORDER1 FunctionFlag = 0x002000000 // SUBTYPE FunctionFlag = 0x000100000 // RESULT_SUBTYPE FunctionFlag = 0x001000000 ) @@ -245,6 +247,7 @@ DBCONFIG_TRUSTED_SCHEMA DBConfig = 1017 DBCONFIG_STMT_SCANSTATUS DBConfig = 1018 DBCONFIG_REVERSE_SCANORDER DBConfig = 1019 + // DBCONFIG_MAX DBConfig = 1019 ) // FcntlOpcode are the available opcodes for [Conn.FileControl]. diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/README.md b/vendor/github.com/ncruces/go-sqlite3/embed/README.md index b2074a71c..b7b25c461 100644 --- a/vendor/github.com/ncruces/go-sqlite3/embed/README.md +++ b/vendor/github.com/ncruces/go-sqlite3/embed/README.md @@ -1,6 +1,6 @@ # Embeddable Wasm build of SQLite -This folder includes an embeddable Wasm build of SQLite 3.46.1 for use with +This folder includes an embeddable Wasm build of SQLite 3.47.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: @@ -36,6 +36,6 @@ You can use your own custom build of SQLite. Examples of custom builds of SQLite are: - [`github.com/ncruces/go-sqlite3/embed/bcw2`](https://github.com/ncruces/go-sqlite3/tree/main/embed/bcw2) built from a branch supporting [`BEGIN CONCURRENT`](https://sqlite.org/src/doc/begin-concurrent/doc/begin_concurrent.md) - and [Wal2](https://www.sqlite.org/cgi/src/doc/wal2/doc/wal2.md). + and [Wal2](https://sqlite.org/cgi/src/doc/wal2/doc/wal2.md). - [`github.com/asg017/sqlite-vec-go-bindings/ncruces`](https://github.com/asg017/sqlite-vec-go-bindings) which includes the [`sqlite-vec`](https://github.com/asg017/sqlite-vec) vector search extension. \ No newline at end of file diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm b/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm index 749a6edba..173ad0e08 100644 Binary files a/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm and b/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm differ diff --git a/vendor/github.com/ncruces/go-sqlite3/func.go b/vendor/github.com/ncruces/go-sqlite3/func.go index 7ff740df2..621c0957d 100644 --- a/vendor/github.com/ncruces/go-sqlite3/func.go +++ b/vendor/github.com/ncruces/go-sqlite3/func.go @@ -4,8 +4,9 @@ "context" "sync" - "github.com/ncruces/go-sqlite3/internal/util" "github.com/tetratelabs/wazero/api" + + "github.com/ncruces/go-sqlite3/internal/util" ) // CollationNeeded registers a callback to be invoked diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_other.go b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_other.go index ded8da108..d9a3de224 100644 --- a/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_other.go +++ b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_other.go @@ -4,6 +4,21 @@ import "github.com/tetratelabs/wazero/experimental" -func Virtual(cap, max uint64) experimental.LinearMemory { - return Slice(cap, max) +func NewMemory(cap, max uint64) experimental.LinearMemory { + return &sliceMemory{make([]byte, 0, cap)} +} + +type sliceMemory struct { + buf []byte +} + +func (b *sliceMemory) Free() {} + +func (b *sliceMemory) 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/alloc/alloc_slice.go b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_slice.go deleted file mode 100644 index 5fc725c65..000000000 --- a/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_slice.go +++ /dev/null @@ -1,24 +0,0 @@ -//go:build !(darwin || linux) || !(amd64 || arm64 || riscv64 || ppc64le) || sqlite3_noshm || sqlite3_nosys - -package alloc - -import "github.com/tetratelabs/wazero/experimental" - -func Slice(cap, _ uint64) experimental.LinearMemory { - return &sliceMemory{make([]byte, 0, cap)} -} - -type sliceMemory struct { - buf []byte -} - -func (b *sliceMemory) Free() {} - -func (b *sliceMemory) 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/alloc/alloc_unix.go b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_unix.go index c05cfa735..2948487f6 100644 --- a/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_unix.go +++ b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_unix.go @@ -9,7 +9,7 @@ "golang.org/x/sys/unix" ) -func Virtual(_, max uint64) experimental.LinearMemory { +func NewMemory(_, max uint64) experimental.LinearMemory { // Round up to the page size. rnd := uint64(unix.Getpagesize() - 1) max = (max + rnd) &^ rnd diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_windows.go b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_windows.go index 46181b118..8e67e0319 100644 --- a/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_windows.go +++ b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_windows.go @@ -11,7 +11,7 @@ "golang.org/x/sys/windows" ) -func Virtual(_, max uint64) experimental.LinearMemory { +func NewMemory(_, max uint64) experimental.LinearMemory { // Round up to the page size. rnd := uint64(windows.Getpagesize() - 1) max = (max + rnd) &^ rnd diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/bool.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/bool.go deleted file mode 100644 index 8427f3085..000000000 --- a/vendor/github.com/ncruces/go-sqlite3/internal/util/bool.go +++ /dev/null @@ -1,22 +0,0 @@ -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/math.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/math.go new file mode 100644 index 000000000..a95f73764 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/math.go @@ -0,0 +1,29 @@ +package util + +import "math" + +func abs(n int) int { + if n < 0 { + return -n + } + return n +} + +func GCD(m, n int) int { + for n != 0 { + m, n = n, m%n + } + return abs(m) +} + +func LCM(m, n int) int { + if n == 0 { + return 0 + } + return abs(n) * (abs(m) / GCD(m, n)) +} + +// https://developer.nvidia.com/blog/lerp-faster-cuda/ +func Lerp(v0, v1, t float64) float64 { + return math.FMA(t, v1, math.FMA(-t, v0, v0)) +} diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go index 813772679..5788eeb24 100644 --- a/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go +++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go @@ -1,4 +1,4 @@ -//go:build unix && (amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_noshm || sqlite3_nosys) +//go:build unix && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_noshm || sqlite3_nosys) package util @@ -7,17 +7,10 @@ "os" "unsafe" - "github.com/ncruces/go-sqlite3/internal/alloc" "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(alloc.Virtual)) -} - type mmapState struct { regions []*MappedRegion } 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 index b6cd4c551..a2fbf24df 100644 --- a/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_other.go +++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_other.go @@ -1,22 +1,5 @@ -//go:build !unix || !(amd64 || arm64 || riscv64 || ppc64le) || sqlite3_noshm || sqlite3_nosys +//go:build !unix || !(386 || arm || amd64 || arm64 || riscv64 || ppc64le) || sqlite3_noshm || sqlite3_nosys package util -import ( - "context" - - "github.com/ncruces/go-sqlite3/internal/alloc" - "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 alloc.Virtual(cap, max) - } - return alloc.Slice(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 index 22793e972..4089dcab9 100644 --- a/vendor/github.com/ncruces/go-sqlite3/internal/util/module.go +++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/module.go @@ -4,6 +4,8 @@ "context" "github.com/tetratelabs/wazero/experimental" + + "github.com/ncruces/go-sqlite3/internal/alloc" ) type moduleKey struct{} @@ -14,7 +16,7 @@ type moduleState struct { func NewContext(ctx context.Context) context.Context { state := new(moduleState) - ctx = withAllocator(ctx) + ctx = experimental.WithMemoryAllocator(ctx, experimental.MemoryAllocatorFunc(alloc.NewMemory)) ctx = experimental.WithCloseNotifier(ctx, state) ctx = context.WithValue(ctx, moduleKey{}, state) return ctx diff --git a/vendor/github.com/ncruces/go-sqlite3/sqlite.go b/vendor/github.com/ncruces/go-sqlite3/sqlite.go index a5ff1363b..2afe9971c 100644 --- a/vendor/github.com/ncruces/go-sqlite3/sqlite.go +++ b/vendor/github.com/ncruces/go-sqlite3/sqlite.go @@ -9,11 +9,12 @@ "sync" "unsafe" - "github.com/ncruces/go-sqlite3/internal/util" - "github.com/ncruces/go-sqlite3/vfs" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/experimental" + + "github.com/ncruces/go-sqlite3/internal/util" + "github.com/ncruces/go-sqlite3/vfs" ) // Configure SQLite Wasm. @@ -49,10 +50,15 @@ func compileSQLite() { cfg := RuntimeConfig if cfg == nil { cfg = wazero.NewRuntimeConfig() + if bits.UintSize >= 64 { + cfg = cfg.WithMemoryLimitPages(4096) // 256MB + } else { + cfg = cfg.WithMemoryLimitPages(512) // 32MB + } } + cfg = cfg.WithCoreFeatures(api.CoreFeaturesV2 | experimental.CoreFeaturesThreads) - instance.runtime = wazero.NewRuntimeWithConfig(ctx, - cfg.WithCoreFeatures(api.CoreFeaturesV2|experimental.CoreFeaturesThreads)) + instance.runtime = wazero.NewRuntimeWithConfig(ctx, cfg) env := instance.runtime.NewHostModuleBuilder("env") env = vfs.ExportHostFunctions(env) diff --git a/vendor/github.com/ncruces/go-sqlite3/txn.go b/vendor/github.com/ncruces/go-sqlite3/txn.go index bd24724ea..57ba979aa 100644 --- a/vendor/github.com/ncruces/go-sqlite3/txn.go +++ b/vendor/github.com/ncruces/go-sqlite3/txn.go @@ -8,8 +8,9 @@ "strconv" "strings" - "github.com/ncruces/go-sqlite3/internal/util" "github.com/tetratelabs/wazero/api" + + "github.com/ncruces/go-sqlite3/internal/util" ) // Txn is an in-progress database transaction. @@ -142,7 +143,7 @@ func (c *Conn) Savepoint() Savepoint { // Names can be reused, but this makes catching bugs more likely. name = QuoteIdentifier(name + "_" + strconv.Itoa(int(rand.Int31()))) - err := c.txnExecInterrupted("SAVEPOINT " + name) + err := c.txnExecInterrupted(`SAVEPOINT ` + name) if err != nil { panic(err) } @@ -186,7 +187,7 @@ func (s Savepoint) Release(errp *error) { if s.c.GetAutocommit() { // There is nothing to commit. return } - *errp = s.c.Exec("RELEASE " + s.name) + *errp = s.c.Exec(`RELEASE ` + s.name) if *errp == nil { return } @@ -198,8 +199,7 @@ func (s Savepoint) Release(errp *error) { return } // ROLLBACK and RELEASE even if interrupted. - err := s.c.txnExecInterrupted("ROLLBACK TO " + - s.name + "; RELEASE " + s.name) + err := s.c.txnExecInterrupted(`ROLLBACK TO ` + s.name + `; RELEASE ` + s.name) if err != nil { panic(err) } @@ -212,7 +212,7 @@ func (s Savepoint) Release(errp *error) { // https://sqlite.org/lang_transaction.html func (s Savepoint) Rollback() error { // ROLLBACK even if interrupted. - return s.c.txnExecInterrupted("ROLLBACK TO " + s.name) + return s.c.txnExecInterrupted(`ROLLBACK TO ` + s.name) } func (c *Conn) txnExecInterrupted(sql string) error { diff --git a/vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go b/vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go index 7fbd04787..83444e906 100644 --- a/vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go +++ b/vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go @@ -1,2 +1,2 @@ -// Package osutil implements operating system utility functions. +// Package osutil implements operating system utilities. package osutil diff --git a/vendor/github.com/ncruces/go-sqlite3/util/sql3util/README.md b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/README.md new file mode 100644 index 000000000..9f47f5a9f --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/README.md @@ -0,0 +1,9 @@ +# SQLite utility functions + +This package implements assorted SQLite utilities +useful to extension writers. + +It also wraps a [parser](https://github.com/marcobambini/sqlite-createtable-parser) +for the [`CREATE`](https://sqlite.org/lang_createtable.html) and +[`ALTER TABLE`](https://sqlite.org/lang_altertable.html) commands, +created by [Marco Bambini](https://github.com/marcobambini). \ No newline at end of file diff --git a/vendor/github.com/ncruces/go-sqlite3/util/sql3util/arg.go b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/arg.go new file mode 100644 index 000000000..3e8c728b0 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/arg.go @@ -0,0 +1,65 @@ +package sql3util + +import "strings" + +// NamedArg splits an named arg into a key and value, +// around an equals sign. +// Spaces are trimmed around both key and value. +func NamedArg(arg string) (key, val string) { + key, val, _ = strings.Cut(arg, "=") + key = strings.TrimSpace(key) + val = strings.TrimSpace(val) + return +} + +// Unquote unquotes a string. +// +// https://sqlite.org/lang_keywords.html +func Unquote(val string) string { + if len(val) < 2 { + return val + } + fst := val[0] + lst := val[len(val)-1] + rst := val[1 : len(val)-1] + if fst == '[' && lst == ']' { + return rst + } + if fst != lst { + return val + } + var old, new string + switch fst { + default: + return val + case '`': + old, new = "``", "`" + case '"': + old, new = `""`, `"` + case '\'': + old, new = `''`, `'` + } + return strings.ReplaceAll(rst, old, new) +} + +// ParseBool parses a boolean. +// +// https://sqlite.org/pragma.html#syntax +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/util/sql3util/const.go b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/const.go new file mode 100644 index 000000000..10e8af35a --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/const.go @@ -0,0 +1,61 @@ +package sql3util + +const ( + _NONE = iota + _MEMORY + _SYNTAX + _UNSUPPORTEDSQL +) + +type ConflictClause uint32 + +const ( + CONFLICT_NONE ConflictClause = iota + CONFLICT_ROLLBACK + CONFLICT_ABORT + CONFLICT_FAIL + CONFLICT_IGNORE + CONFLICT_REPLACE +) + +type OrderClause uint32 + +const ( + ORDER_NONE OrderClause = iota + ORDER_ASC + ORDER_DESC +) + +type FKAction uint32 + +const ( + FKACTION_NONE FKAction = iota + FKACTION_SETNULL + FKACTION_SETDEFAULT + FKACTION_CASCADE + FKACTION_RESTRICT + FKACTION_NOACTION +) + +type FKDefType uint32 + +const ( + DEFTYPE_NONE FKDefType = iota + DEFTYPE_DEFERRABLE + DEFTYPE_DEFERRABLE_INITIALLY_DEFERRED + DEFTYPE_DEFERRABLE_INITIALLY_IMMEDIATE + DEFTYPE_NOTDEFERRABLE + DEFTYPE_NOTDEFERRABLE_INITIALLY_DEFERRED + DEFTYPE_NOTDEFERRABLE_INITIALLY_IMMEDIATE +) + +type StatementType uint32 + +const ( + CREATE_UNKNOWN StatementType = iota + CREATE_TABLE + ALTER_RENAME_TABLE + ALTER_RENAME_COLUMN + ALTER_ADD_COLUMN + ALTER_DROP_COLUMN +) diff --git a/vendor/github.com/ncruces/go-sqlite3/util/sql3util/parse.go b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/parse.go new file mode 100644 index 000000000..7326f7dbb --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/parse.go @@ -0,0 +1,210 @@ +package sql3util + +import ( + "context" + _ "embed" + "sync" + + "github.com/tetratelabs/wazero" + "github.com/tetratelabs/wazero/api" + + "github.com/ncruces/go-sqlite3/internal/util" +) + +const ( + errp = 4 + sqlp = 8 +) + +var ( + //go:embed parse/sql3parse_table.wasm + binary []byte + once sync.Once + runtime wazero.Runtime + compiled wazero.CompiledModule +) + +// ParseTable parses a [CREATE] or [ALTER TABLE] command. +// +// [CREATE]: https://sqlite.org/lang_createtable.html +// [ALTER TABLE]: https://sqlite.org/lang_altertable.html +func ParseTable(sql string) (_ *Table, err error) { + once.Do(func() { + ctx := context.Background() + cfg := wazero.NewRuntimeConfigInterpreter() + runtime = wazero.NewRuntimeWithConfig(ctx, cfg) + compiled, err = runtime.CompileModule(ctx, binary) + }) + if err != nil { + return nil, err + } + + ctx := context.Background() + mod, err := runtime.InstantiateModule(ctx, compiled, wazero.NewModuleConfig().WithName("")) + if err != nil { + return nil, err + } + defer mod.Close(ctx) + + if buf, ok := mod.Memory().Read(sqlp, uint32(len(sql))); ok { + copy(buf, sql) + } + + stack := [...]uint64{sqlp, uint64(len(sql)), errp} + err = mod.ExportedFunction("sql3parse_table").CallWithStack(ctx, stack[:]) + if err != nil { + return nil, err + } + + c, _ := mod.Memory().ReadUint32Le(errp) + switch c { + case _MEMORY: + panic(util.OOMErr) + case _SYNTAX: + return nil, util.ErrorString("sql3parse: invalid syntax") + case _UNSUPPORTEDSQL: + return nil, util.ErrorString("sql3parse: unsupported SQL") + } + + var tab Table + tab.load(mod, uint32(stack[0]), sql) + return &tab, nil +} + +// Table holds metadata about a table. +type Table struct { + Name string + Schema string + Comment string + IsTemporary bool + IsIfNotExists bool + IsWithoutRowID bool + IsStrict bool + Columns []Column + Type StatementType + CurrentName string + NewName string +} + +func (t *Table) load(mod api.Module, ptr uint32, sql string) { + t.Name = loadString(mod, ptr+0, sql) + t.Schema = loadString(mod, ptr+8, sql) + t.Comment = loadString(mod, ptr+16, sql) + + t.IsTemporary = loadBool(mod, ptr+24) + t.IsIfNotExists = loadBool(mod, ptr+25) + t.IsWithoutRowID = loadBool(mod, ptr+26) + t.IsStrict = loadBool(mod, ptr+27) + + t.Columns = loadSlice(mod, ptr+28, func(ptr uint32, res *Column) { + p, _ := mod.Memory().ReadUint32Le(ptr) + res.load(mod, p, sql) + }) + + t.Type = loadEnum[StatementType](mod, ptr+44) + t.CurrentName = loadString(mod, ptr+48, sql) + t.NewName = loadString(mod, ptr+56, sql) +} + +// Column holds metadata about a column. +type Column struct { + Name string + Type string + Length string + ConstraintName string + Comment string + IsPrimaryKey bool + IsAutoIncrement bool + IsNotNull bool + IsUnique bool + PKOrder OrderClause + PKConflictClause ConflictClause + NotNullConflictClause ConflictClause + UniqueConflictClause ConflictClause + CheckExpr string + DefaultExpr string + CollateName string + ForeignKeyClause *ForeignKey +} + +func (c *Column) load(mod api.Module, ptr uint32, sql string) { + c.Name = loadString(mod, ptr+0, sql) + c.Type = loadString(mod, ptr+8, sql) + c.Length = loadString(mod, ptr+16, sql) + c.ConstraintName = loadString(mod, ptr+24, sql) + c.Comment = loadString(mod, ptr+32, sql) + + c.IsPrimaryKey = loadBool(mod, ptr+40) + c.IsAutoIncrement = loadBool(mod, ptr+41) + c.IsNotNull = loadBool(mod, ptr+42) + c.IsUnique = loadBool(mod, ptr+43) + + c.PKOrder = loadEnum[OrderClause](mod, ptr+44) + c.PKConflictClause = loadEnum[ConflictClause](mod, ptr+48) + c.NotNullConflictClause = loadEnum[ConflictClause](mod, ptr+52) + c.UniqueConflictClause = loadEnum[ConflictClause](mod, ptr+56) + + c.CheckExpr = loadString(mod, ptr+60, sql) + c.DefaultExpr = loadString(mod, ptr+68, sql) + c.CollateName = loadString(mod, ptr+76, sql) + + if ptr, _ := mod.Memory().ReadUint32Le(ptr + 84); ptr != 0 { + c.ForeignKeyClause = &ForeignKey{} + c.ForeignKeyClause.load(mod, ptr, sql) + } +} + +type ForeignKey struct { + Table string + Columns []string + OnDelete FKAction + OnUpdate FKAction + Match string + Deferrable FKDefType +} + +func (f *ForeignKey) load(mod api.Module, ptr uint32, sql string) { + f.Table = loadString(mod, ptr+0, sql) + + f.Columns = loadSlice(mod, ptr+8, func(ptr uint32, res *string) { + *res = loadString(mod, ptr, sql) + }) + + f.OnDelete = loadEnum[FKAction](mod, ptr+16) + f.OnUpdate = loadEnum[FKAction](mod, ptr+20) + f.Match = loadString(mod, ptr+24, sql) + f.Deferrable = loadEnum[FKDefType](mod, ptr+32) +} + +func loadString(mod api.Module, ptr uint32, sql string) string { + off, _ := mod.Memory().ReadUint32Le(ptr + 0) + if off == 0 { + return "" + } + len, _ := mod.Memory().ReadUint32Le(ptr + 4) + return sql[off-sqlp : off+len-sqlp] +} + +func loadSlice[T any](mod api.Module, ptr uint32, fn func(uint32, *T)) []T { + ref, _ := mod.Memory().ReadUint32Le(ptr + 4) + if ref == 0 { + return nil + } + len, _ := mod.Memory().ReadUint32Le(ptr + 0) + res := make([]T, len) + for i := range res { + fn(ref, &res[i]) + ref += 4 + } + return res +} + +func loadEnum[T ~uint32](mod api.Module, ptr uint32) T { + val, _ := mod.Memory().ReadUint32Le(ptr) + return T(val) +} + +func loadBool(mod api.Module, ptr uint32) bool { + val, _ := mod.Memory().ReadByte(ptr) + return val != 0 +} diff --git a/vendor/github.com/ncruces/go-sqlite3/util/sql3util/parse/sql3parse_table.wasm b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/parse/sql3parse_table.wasm new file mode 100644 index 000000000..f0b3819c8 Binary files /dev/null and b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/parse/sql3parse_table.wasm differ diff --git a/vendor/github.com/ncruces/go-sqlite3/util/sql3util/sql3util.go b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/sql3util.go new file mode 100644 index 000000000..6be61927d --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/util/sql3util/sql3util.go @@ -0,0 +1,9 @@ +// Package sql3util implements SQLite utilities. +package sql3util + +// ValidPageSize returns true if s is a valid page size. +// +// https://sqlite.org/fileformat.html#pages +func ValidPageSize(s int) bool { + return 512 <= s && s <= 65536 && s&(s-1) == 0 +} diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/README.md b/vendor/github.com/ncruces/go-sqlite3/vfs/README.md index b1d9ea227..77991486b 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/README.md +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/README.md @@ -4,7 +4,7 @@ This package implements the SQLite [OS Interface](https://sqlite.org/vfs.html) ( 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. +that should allow you to implement your own [custom VFSes](#custom-vfses). Since it is a from scratch reimplementation, there are naturally some ways it deviates from the original. @@ -16,12 +16,12 @@ The main differences are [file locking](#file-locking) and [WAL mode](#write-ahe 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 +On Linux and macOS, this package 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 +This package 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; @@ -30,7 +30,7 @@ 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`, +On Windows, this package uses `LockFileEx` and `UnlockFileEx`, like SQLite. Otherwise, file locking is not supported, and you must use @@ -46,18 +46,14 @@ to check if your build supports file locking. ### Write-Ahead Logging -On 64-bit little-endian Unix, this module uses `mmap` to implement +On little-endian Unix, this package 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). - With [BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2) a WAL database can only be accessed by a single proccess. Other processes that attempt to access a database locked with BSD locks, -will fail with the `SQLITE_PROTOCOL` error code. +will fail with the [`SQLITE_PROTOCOL`](https://sqlite.org/rescode.html#protocol) error code. 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. @@ -71,9 +67,22 @@ 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 64-bit Linux, this package supports +[batch-atomic writes](https://sqlite.org/cgi/src/technote/714) on the F2FS filesystem. +### Checksums + +This package can be [configured](https://pkg.go.dev/github.com/ncruces/go-sqlite3#Conn.EnableChecksums) +to add an 8-byte checksum to the end of every page in an SQLite database. +The checksum is added as each page is written +and verified as each page is read.\ +The checksum is intended to help detect database corruption +caused by random bit-flips in the mass storage device. + +The implementation is compatible with SQLite's +[Checksum VFS Shim](https://sqlite.org/cksumvfs.html). + ### Build Tags The VFS can be customized with a few build tags: @@ -90,3 +99,14 @@ The VFS can be customized with a few build tags: > [`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. + +### Custom VFSes + +- [`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. +- [`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/xts`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/xts) + wraps a VFS to offer encryption at rest. \ No newline at end of file diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/api.go b/vendor/github.com/ncruces/go-sqlite3/vfs/api.go index e133e8be9..330e8a2b1 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/api.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/api.go @@ -49,6 +49,13 @@ type File interface { DeviceCharacteristics() DeviceCharacteristic } +// FileUnwrap should be implemented by a File +// that wraps another File implementation. +type FileUnwrap interface { + File + Unwrap() File +} + // FileLockState extends File to implement the // SQLITE_FCNTL_LOCKSTATE file control opcode. // @@ -58,6 +65,26 @@ type FileLockState interface { LockState() LockLevel } +// 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) +} + // FileChunkSize extends File to implement the // SQLITE_FCNTL_CHUNK_SIZE file control opcode. // @@ -94,26 +121,6 @@ type FileOverwrite interface { 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. // @@ -135,15 +142,6 @@ type FileBatchAtomicWrite interface { 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. @@ -151,8 +149,17 @@ type FilePragma interface { // https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlckptstart type FileCheckpoint interface { File - CheckpointDone() error - CheckpointStart() error + CheckpointStart() + CheckpointDone() +} + +// 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) } // FileSharedMemory extends File to possibly implement @@ -171,5 +178,16 @@ type SharedMemory interface { shmMap(context.Context, api.Module, int32, int32, bool) (uint32, _ErrorCode) shmLock(int32, int32, _ShmFlag) _ErrorCode shmUnmap(bool) + shmBarrier() io.Closer } + +type blockingSharedMemory interface { + SharedMemory + shmEnableBlocking(block bool) +} + +type fileControl interface { + File + fileControl(ctx context.Context, mod api.Module, op _FcntlOpcode, pArg uint32) _ErrorCode +} diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/cksm.go b/vendor/github.com/ncruces/go-sqlite3/vfs/cksm.go new file mode 100644 index 000000000..900fa0952 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/cksm.go @@ -0,0 +1,149 @@ +package vfs + +import ( + "bytes" + "context" + _ "embed" + "encoding/binary" + "strconv" + + "github.com/tetratelabs/wazero/api" + + "github.com/ncruces/go-sqlite3/internal/util" + "github.com/ncruces/go-sqlite3/util/sql3util" +) + +func cksmWrapFile(name *Filename, flags OpenFlag, file File) File { + // Checksum only main databases and WALs. + if flags&(OPEN_MAIN_DB|OPEN_WAL) == 0 { + return file + } + + cksm := cksmFile{File: file} + + if flags&OPEN_WAL != 0 { + main, _ := name.DatabaseFile().(cksmFile) + cksm.cksmFlags = main.cksmFlags + } else { + cksm.cksmFlags = new(cksmFlags) + cksm.isDB = true + } + + return cksm +} + +type cksmFile struct { + File + *cksmFlags + isDB bool +} + +type cksmFlags struct { + computeCksm bool + verifyCksm bool + inCkpt bool + pageSize int +} + +func (c cksmFile) ReadAt(p []byte, off int64) (n int, err error) { + n, err = c.File.ReadAt(p, off) + + // SQLite is reading the header of a database file. + if c.isDB && off == 0 && len(p) >= 100 && + bytes.HasPrefix(p, []byte("SQLite format 3\000")) { + c.init(p) + } + + // Verify checksums. + if c.verifyCksm && !c.inCkpt && len(p) == c.pageSize { + cksm1 := cksmCompute(p[:len(p)-8]) + cksm2 := *(*[8]byte)(p[len(p)-8:]) + if cksm1 != cksm2 { + return 0, _IOERR_DATA + } + } + return n, err +} + +func (c cksmFile) WriteAt(p []byte, off int64) (n int, err error) { + // SQLite is writing the first page of a database file. + if c.isDB && off == 0 && len(p) >= 100 && + bytes.HasPrefix(p, []byte("SQLite format 3\000")) { + c.init(p) + } + + // Compute checksums. + if c.computeCksm && !c.inCkpt && len(p) == c.pageSize { + *(*[8]byte)(p[len(p)-8:]) = cksmCompute(p[:len(p)-8]) + } + + return c.File.WriteAt(p, off) +} + +func (c cksmFile) Pragma(name string, value string) (string, error) { + switch name { + case "checksum_verification": + b, ok := sql3util.ParseBool(value) + if ok { + c.verifyCksm = b && c.computeCksm + } + if !c.verifyCksm { + return "0", nil + } + return "1", nil + + case "page_size": + if c.computeCksm { + // Do not allow page size changes on a checksum database. + return strconv.Itoa(c.pageSize), nil + } + } + return "", _NOTFOUND +} + +func (c cksmFile) fileControl(ctx context.Context, mod api.Module, op _FcntlOpcode, pArg uint32) _ErrorCode { + switch op { + case _FCNTL_CKPT_START: + c.inCkpt = true + case _FCNTL_CKPT_DONE: + c.inCkpt = false + } + if rc := vfsFileControlImpl(ctx, mod, c, op, pArg); rc != _NOTFOUND { + return rc + } + return vfsFileControlImpl(ctx, mod, c.File, op, pArg) +} + +func (f *cksmFlags) init(header []byte) { + f.pageSize = 256 * int(binary.LittleEndian.Uint16(header[16:18])) + if r := header[20] == 8; r != f.computeCksm { + f.computeCksm = r + f.verifyCksm = r + } +} + +func cksmCompute(a []byte) (cksm [8]byte) { + var s1, s2 uint32 + for len(a) >= 8 { + s1 += binary.LittleEndian.Uint32(a[0:4]) + s2 + s2 += binary.LittleEndian.Uint32(a[4:8]) + s1 + a = a[8:] + } + if len(a) != 0 { + panic(util.AssertErr()) + } + binary.LittleEndian.PutUint32(cksm[0:4], s1) + binary.LittleEndian.PutUint32(cksm[4:8], s2) + return +} + +func (c cksmFile) SharedMemory() SharedMemory { + if f, ok := c.File.(FileSharedMemory); ok { + return f.SharedMemory() + } + return nil +} + +func (c cksmFile) Unwrap() File { + return c.File +} diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/const.go b/vendor/github.com/ncruces/go-sqlite3/vfs/const.go index 2fc934f33..e80437be6 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/const.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/const.go @@ -51,6 +51,7 @@ func (e _ErrorCode) Error() string { _IOERR_BEGIN_ATOMIC _ErrorCode = util.IOERR_BEGIN_ATOMIC _IOERR_COMMIT_ATOMIC _ErrorCode = util.IOERR_COMMIT_ATOMIC _IOERR_ROLLBACK_ATOMIC _ErrorCode = util.IOERR_ROLLBACK_ATOMIC + _IOERR_DATA _ErrorCode = util.IOERR_DATA _BUSY_SNAPSHOT _ErrorCode = util.BUSY_SNAPSHOT _CANTOPEN_FULLPATH _ErrorCode = util.CANTOPEN_FULLPATH _CANTOPEN_ISDIR _ErrorCode = util.CANTOPEN_ISDIR diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/filename.go b/vendor/github.com/ncruces/go-sqlite3/vfs/filename.go index 51d0b8dda..d9a29cd47 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/filename.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/filename.go @@ -4,8 +4,9 @@ "context" "net/url" - "github.com/ncruces/go-sqlite3/internal/util" "github.com/tetratelabs/wazero/api" + + "github.com/ncruces/go-sqlite3/internal/util" ) // Filename is used by SQLite to pass filenames 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 index 6ecb60edb..07bf0a047 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_f2fs_linux.go @@ -1,4 +1,4 @@ -//go:build (amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys +//go:build (amd64 || arm64 || riscv64) && !sqlite3_nosys package vfs @@ -13,7 +13,7 @@ _F2FS_IOC_START_ATOMIC_WRITE = 62721 _F2FS_IOC_COMMIT_ATOMIC_WRITE = 62722 _F2FS_IOC_ABORT_ATOMIC_WRITE = 62725 - _F2FS_IOC_GET_FEATURES = 2147808524 + _F2FS_IOC_GET_FEATURES = 2147808524 // -2147158772 _F2FS_FEATURE_ATOMIC_WRITE = 4 ) 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 index c3590a7d5..ecaff0245 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_atomic.go @@ -1,4 +1,4 @@ -//go:build !linux || !(amd64 || arm64 || riscv64 || ppc64le) || sqlite3_nosys +//go:build !linux || !(amd64 || arm64 || riscv64) || sqlite3_nosys package vfs diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go index 0fbd09d0a..402676afb 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go @@ -1,4 +1,4 @@ -//go:build (darwin || linux) && (amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_flock || sqlite3_noshm || sqlite3_nosys) +//go:build (darwin || linux) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_flock || sqlite3_noshm || sqlite3_nosys) package vfs @@ -6,11 +6,13 @@ "context" "io" "os" + "sync" "time" - "github.com/ncruces/go-sqlite3/internal/util" "github.com/tetratelabs/wazero/api" "golang.org/x/sys/unix" + + "github.com/ncruces/go-sqlite3/internal/util" ) // SupportsSharedMemory is false on platforms that do not support shared memory. @@ -45,12 +47,15 @@ func NewSharedMemory(path string, flags OpenFlag) SharedMemory { } } +var _ blockingSharedMemory = &vfsShm{} + type vfsShm struct { *os.File path string regions []*util.MappedRegion readOnly bool blocking bool + sync.Mutex } func (s *vfsShm) shmOpen() _ErrorCode { @@ -196,6 +201,12 @@ func (s *vfsShm) shmUnmap(delete bool) { s.File = nil } +func (s *vfsShm) shmBarrier() { + s.Lock() + //lint:ignore SA2001 memory barrier. + s.Unlock() +} + func (s *vfsShm) shmEnableBlocking(block bool) { s.blocking = block } diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go index 52ffeacb5..8dc6ec922 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go @@ -1,4 +1,4 @@ -//go:build (freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && (amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_noshm || sqlite3_nosys) +//go:build (freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_noshm || sqlite3_nosys) package vfs @@ -8,9 +8,10 @@ "os" "sync" - "github.com/ncruces/go-sqlite3/internal/util" "github.com/tetratelabs/wazero/api" "golang.org/x/sys/unix" + + "github.com/ncruces/go-sqlite3/internal/util" ) // SupportsSharedMemory is false on platforms that do not support shared memory. @@ -269,3 +270,9 @@ func (s *vfsShm) shmUnmap(delete bool) { } s.Close() } + +func (s *vfsShm) shmBarrier() { + s.lockMtx.Lock() + //lint:ignore SA2001 memory barrier. + s.lockMtx.Unlock() +} diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go index 4d0f6a2ca..12012033e 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go @@ -1,4 +1,4 @@ -//go:build !(darwin || linux || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) || !(amd64 || arm64 || riscv64 || ppc64le) || sqlite3_noshm || sqlite3_nosys +//go:build !(darwin || linux || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) || !(386 || arm || amd64 || arm64 || riscv64 || ppc64le) || sqlite3_noshm || sqlite3_nosys package vfs diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go b/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go index eb606bf88..83c95d08d 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go @@ -5,13 +5,15 @@ "crypto/rand" "io" "reflect" - "sync" + "strings" "time" - "github.com/ncruces/go-sqlite3/internal/util" - "github.com/ncruces/julianday" "github.com/tetratelabs/wazero" "github.com/tetratelabs/wazero/api" + + "github.com/ncruces/go-sqlite3/internal/util" + "github.com/ncruces/go-sqlite3/util/sql3util" + "github.com/ncruces/julianday" ) // ExportHostFunctions is an internal API users need not call directly. @@ -146,7 +148,7 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, fla } if file, ok := file.(FilePowersafeOverwrite); ok { - if b, ok := util.ParseBool(name.URIParameter("psow")); ok { + if b, ok := sql3util.ParseBool(name.URIParameter("psow")); ok { file.SetPowersafeOverwrite(b) } } @@ -157,6 +159,7 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, fla if pOutFlags != 0 { util.WriteUint32(mod, pOutFlags, uint32(flags)) } + file = cksmWrapFile(name, flags, file) vfsFileRegister(ctx, mod, pFile, file) return _OK } @@ -235,20 +238,19 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _FcntlOpcode, pArg uint32) _ErrorCode { file := vfsFileGet(ctx, mod, pFile).(File) + if file, ok := file.(fileControl); ok { + return file.fileControl(ctx, mod, op, pArg) + } + return vfsFileControlImpl(ctx, mod, file, op, pArg) +} +func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _FcntlOpcode, pArg uint32) _ErrorCode { switch op { case _FCNTL_LOCKSTATE: if file, ok := file.(FileLockState); ok { - util.WriteUint32(mod, pArg, uint32(file.LockState())) - return _OK - } - - case _FCNTL_LOCK_TIMEOUT: - if file, ok := file.(FileSharedMemory); ok { - if iface, ok := file.SharedMemory().(interface{ shmEnableBlocking(bool) }); ok { - if i := util.ReadUint32(mod, pArg); i == 0 || i == 1 { - iface.shmEnableBlocking(i != 0) - } + if lk := file.LockState(); lk <= LOCK_EXCLUSIVE { + util.WriteUint32(mod, pArg, uint32(lk)) + return _OK } } @@ -329,15 +331,15 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl 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) + file.CheckpointStart() + return _OK + } + case _FCNTL_CKPT_DONE: + if file, ok := file.(FileCheckpoint); ok { + file.CheckpointDone() + return _OK } case _FCNTL_PRAGMA: @@ -349,7 +351,7 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl value = util.ReadString(mod, ptr, _MAX_SQL_LENGTH) } - out, err := file.Pragma(name, value) + out, err := file.Pragma(strings.ToLower(name), value) ret := vfsErrorCode(err, _ERROR) if ret == _ERROR { @@ -366,6 +368,14 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl } return ret } + + case _FCNTL_LOCK_TIMEOUT: + if file, ok := file.(FileSharedMemory); ok { + if shm, ok := file.SharedMemory().(blockingSharedMemory); ok { + shm.shmEnableBlocking(util.ReadUint32(mod, pArg) != 0) + return _OK + } + } } // Consider also implementing these opcodes (in use by SQLite): @@ -385,11 +395,9 @@ func vfsDeviceCharacteristics(ctx context.Context, mod api.Module, pFile uint32) return file.DeviceCharacteristics() } -var shmBarrier sync.Mutex - func vfsShmBarrier(ctx context.Context, mod api.Module, pFile uint32) { - shmBarrier.Lock() - defer shmBarrier.Unlock() + shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory() + shm.shmBarrier() } func vfsShmMap(ctx context.Context, mod api.Module, pFile uint32, iRegion, szRegion int32, bExtend, pp uint32) _ErrorCode { diff --git a/vendor/github.com/ncruces/go-sqlite3/vtab.go b/vendor/github.com/ncruces/go-sqlite3/vtab.go index 2bb294ba3..983486230 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vtab.go +++ b/vendor/github.com/ncruces/go-sqlite3/vtab.go @@ -4,8 +4,9 @@ "context" "reflect" - "github.com/ncruces/go-sqlite3/internal/util" "github.com/tetratelabs/wazero/api" + + "github.com/ncruces/go-sqlite3/internal/util" ) // CreateModule registers a new virtual table module name. diff --git a/vendor/modules.txt b/vendor/modules.txt index 430b5ead0..9846a4c01 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -518,7 +518,7 @@ github.com/modern-go/reflect2 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 ## explicit github.com/munnerz/goautoneg -# github.com/ncruces/go-sqlite3 v0.19.0 +# github.com/ncruces/go-sqlite3 v0.20.0 ## explicit; go 1.21 github.com/ncruces/go-sqlite3 github.com/ncruces/go-sqlite3/driver @@ -526,6 +526,7 @@ github.com/ncruces/go-sqlite3/embed github.com/ncruces/go-sqlite3/internal/alloc github.com/ncruces/go-sqlite3/internal/util github.com/ncruces/go-sqlite3/util/osutil +github.com/ncruces/go-sqlite3/util/sql3util github.com/ncruces/go-sqlite3/vfs github.com/ncruces/go-sqlite3/vfs/memdb # github.com/ncruces/go-strftime v0.1.9