summaryrefslogtreecommitdiff
path: root/vendor/github.com/ncruces/go-sqlite3/vfs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/ncruces/go-sqlite3/vfs')
-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
27 files changed, 2796 insertions, 0 deletions
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
+}