summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod2
-rw-r--r--go.sum4
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/config.go10
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/context.go1
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/error.go10
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/mmap.go12
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_other.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_windows.go53
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/stmt.go1
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/README.md38
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/const.go4
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/file.go13
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/lock.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go22
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_dotlk.go143
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_ofd.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/shm.go190
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go97
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/shm_copy.go84
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/shm_dotlk.go224
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/shm_ofd.go168
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go2
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/vfs/shm_windows.go182
-rw-r--r--vendor/modules.txt2
27 files changed, 963 insertions, 311 deletions
diff --git a/go.mod b/go.mod
index f136be772..7f442cce5 100644
--- a/go.mod
+++ b/go.mod
@@ -43,7 +43,7 @@ require (
github.com/miekg/dns v1.1.62
github.com/minio/minio-go/v7 v7.0.80
github.com/mitchellh/mapstructure v1.5.0
- github.com/ncruces/go-sqlite3 v0.20.0
+ github.com/ncruces/go-sqlite3 v0.20.2
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 b15a61bb8..5e6b3eba6 100644
--- a/go.sum
+++ b/go.sum
@@ -432,8 +432,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.20.0 h1:/nBLvYxj7sk9S6y57nmMFvoQ/KJtGo0pNi8J80s8oJU=
-github.com/ncruces/go-sqlite3 v0.20.0/go.mod h1:yL4ZNWGsr1/8pcLfpPW1RT1WFdvyeHonrgIwwi4rvkg=
+github.com/ncruces/go-sqlite3 v0.20.2 h1:cMLIwrLZQuCWVCEOowSqlIlpzgbag3jnYVW4NM5u01M=
+github.com/ncruces/go-sqlite3 v0.20.2/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/config.go b/vendor/github.com/ncruces/go-sqlite3/config.go
index cf72cbda5..474f960a2 100644
--- a/vendor/github.com/ncruces/go-sqlite3/config.go
+++ b/vendor/github.com/ncruces/go-sqlite3/config.go
@@ -2,6 +2,7 @@ package sqlite3
import (
"context"
+ "fmt"
"strconv"
"github.com/tetratelabs/wazero/api"
@@ -70,6 +71,15 @@ func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) {
}
}
+// Log writes a message into the error log established by [Conn.ConfigLog].
+//
+// https://sqlite.org/c3ref/log.html
+func (c *Conn) Log(code ExtendedErrorCode, format string, a ...any) {
+ if c.log != nil {
+ c.log(code, fmt.Sprintf(format, a...))
+ }
+}
+
// FileControl allows low-level control of database files.
// Only a subset of opcodes are supported.
//
diff --git a/vendor/github.com/ncruces/go-sqlite3/context.go b/vendor/github.com/ncruces/go-sqlite3/context.go
index be5dd92c5..86be214e2 100644
--- a/vendor/github.com/ncruces/go-sqlite3/context.go
+++ b/vendor/github.com/ncruces/go-sqlite3/context.go
@@ -89,6 +89,7 @@ func (ctx Context) ResultText(value string) {
}
// ResultRawText sets the text result of the function to a []byte.
+// Returning a nil slice is the same as calling [Context.ResultNull].
//
// https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultRawText(value []byte) {
diff --git a/vendor/github.com/ncruces/go-sqlite3/error.go b/vendor/github.com/ncruces/go-sqlite3/error.go
index 71238ef12..870aa3ab1 100644
--- a/vendor/github.com/ncruces/go-sqlite3/error.go
+++ b/vendor/github.com/ncruces/go-sqlite3/error.go
@@ -106,6 +106,11 @@ func (e ErrorCode) Temporary() bool {
return e == BUSY
}
+// ExtendedCode returns the extended error code for this error.
+func (e ErrorCode) ExtendedCode() ExtendedErrorCode {
+ return ExtendedErrorCode(e)
+}
+
// Error implements the error interface.
func (e ExtendedErrorCode) Error() string {
return util.ErrorCodeString(uint32(e))
@@ -136,6 +141,11 @@ func (e ExtendedErrorCode) Timeout() bool {
return e == BUSY_TIMEOUT
}
+// Code returns the primary error code for this error.
+func (e ExtendedErrorCode) Code() ErrorCode {
+ return ErrorCode(e)
+}
+
func errorCode(err error, def ErrorCode) (msg string, code uint32) {
switch code := err.(type) {
case nil:
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 5788eeb24..613bb90b1 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 && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_noshm || sqlite3_nosys)
+//go:build unix && !sqlite3_nosys
package util
@@ -55,10 +55,10 @@ type MappedRegion struct {
used bool
}
-func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32, prot int) (*MappedRegion, error) {
+func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32, readOnly bool) (*MappedRegion, error) {
s := ctx.Value(moduleKey{}).(*moduleState)
r := s.new(ctx, mod, size)
- err := r.mmap(f, offset, prot)
+ err := r.mmap(f, offset, readOnly)
if err != nil {
return nil, err
}
@@ -75,7 +75,11 @@ func (r *MappedRegion) Unmap() error {
return err
}
-func (r *MappedRegion) mmap(f *os.File, offset int64, prot int) error {
+func (r *MappedRegion) mmap(f *os.File, offset int64, readOnly bool) error {
+ prot := unix.PROT_READ
+ if !readOnly {
+ prot |= unix.PROT_WRITE
+ }
_, err := unix.MmapPtr(int(f.Fd()), offset, r.addr, uintptr(r.size),
prot, unix.MAP_SHARED|unix.MAP_FIXED)
r.used = err == nil
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 a2fbf24df..e11f953a7 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,4 +1,4 @@
-//go:build !unix || !(386 || arm || amd64 || arm64 || riscv64 || ppc64le) || sqlite3_noshm || sqlite3_nosys
+//go:build !unix || sqlite3_nosys
package util
diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_windows.go b/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_windows.go
new file mode 100644
index 000000000..fdf6f439a
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/internal/util/mmap_windows.go
@@ -0,0 +1,53 @@
+//go:build !sqlite3_nosys
+
+package util
+
+import (
+ "context"
+ "os"
+ "reflect"
+ "unsafe"
+
+ "github.com/tetratelabs/wazero/api"
+ "golang.org/x/sys/windows"
+)
+
+type MappedRegion struct {
+ windows.Handle
+ Data []byte
+ addr uintptr
+}
+
+func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32) (*MappedRegion, error) {
+ h, err := windows.CreateFileMapping(windows.Handle(f.Fd()), nil, windows.PAGE_READWRITE, 0, 0, nil)
+ if h == 0 {
+ return nil, err
+ }
+
+ a, err := windows.MapViewOfFile(h, windows.FILE_MAP_WRITE,
+ uint32(offset>>32), uint32(offset), uintptr(size))
+ if a == 0 {
+ windows.CloseHandle(h)
+ return nil, err
+ }
+
+ res := &MappedRegion{Handle: h, addr: a}
+ // SliceHeader, although deprecated, avoids a go vet warning.
+ sh := (*reflect.SliceHeader)(unsafe.Pointer(&res.Data))
+ sh.Len = int(size)
+ sh.Cap = int(size)
+ sh.Data = a
+ return res, nil
+}
+
+func (r *MappedRegion) Unmap() error {
+ if r.Data == nil {
+ return nil
+ }
+ err := windows.UnmapViewOfFile(r.addr)
+ if err != nil {
+ return err
+ }
+ r.Data = nil
+ return windows.CloseHandle(r.Handle)
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/stmt.go b/vendor/github.com/ncruces/go-sqlite3/stmt.go
index 9da2a2eaf..139dd3525 100644
--- a/vendor/github.com/ncruces/go-sqlite3/stmt.go
+++ b/vendor/github.com/ncruces/go-sqlite3/stmt.go
@@ -255,6 +255,7 @@ func (s *Stmt) BindText(param int, value string) error {
// BindRawText binds a []byte to the prepared statement as text.
// The leftmost SQL parameter has an index of 1.
+// Binding a nil slice is the same as calling [Stmt.BindNull].
//
// https://sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindRawText(param int, value []byte) error {
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/README.md b/vendor/github.com/ncruces/go-sqlite3/vfs/README.md
index 77991486b..cf0e3c30f 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/README.md
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/README.md
@@ -15,24 +15,23 @@ 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 package uses
+Instead, 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 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;
-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 package uses `LockFileEx` and `UnlockFileEx`,
like SQLite.
+You can also opt into a cross-platform locking implementation
+with the `sqlite3_dotlk` build tag.
+The only requirement is an atomic `os.Mkdir`.
+
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))
@@ -46,7 +45,7 @@ to check if your build supports file locking.
### Write-Ahead Logging
-On little-endian Unix, this package uses `mmap` to implement
+On Unix, this package may use `mmap` to implement
[shared-memory for the WAL-index](https://sqlite.org/wal.html#implementation_of_shared_memory_for_the_wal_index),
like SQLite.
@@ -55,6 +54,11 @@ 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`](https://sqlite.org/rescode.html#protocol) error code.
+On Windows, this package may use `MapViewOfFile`, like SQLite.
+
+You can also opt into a cross-platform, in-process, memory sharing implementation
+with the `sqlite3_dotlk` build tag.
+
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
@@ -67,7 +71,7 @@ to check if your build supports shared memory.
### Batch-Atomic Write
-On 64-bit Linux, this package supports
+On Linux, this package may support
[batch-atomic writes](https://sqlite.org/cgi/src/technote/714)
on the F2FS filesystem.
@@ -86,27 +90,27 @@ The implementation is compatible with SQLite's
### 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.
+- `sqlite3_flock` forces the use of BSD locks.
+- `sqlite3_dotlk` forces the use of dot-file locks.
+- `sqlite3_nosys` prevents importing [`x/sys`](https://pkg.go.dev/golang.org/x/sys).
> [!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` builds are compatible with the
-> [`unix-flock` VFS](https://sqlite.org/compile.html#enable_locking_style).
+> [`unix-flock` VFS](https://sqlite.org/compile.html#enable_locking_style);
+> `sqlite3_dotlk` builds are compatible with the
+> [`unix-dotfile` 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/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/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
+ wraps a VFS to offer encryption at rest.
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/const.go b/vendor/github.com/ncruces/go-sqlite3/vfs/const.go
index e80437be6..0a8fee621 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/const.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/const.go
@@ -234,4 +234,8 @@ const (
_SHM_LOCK _ShmFlag = 2
_SHM_SHARED _ShmFlag = 4
_SHM_EXCLUSIVE _ShmFlag = 8
+
+ _SHM_NLOCK = 8
+ _SHM_BASE = 120
+ _SHM_DMS = _SHM_BASE + _SHM_NLOCK
)
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/file.go b/vendor/github.com/ncruces/go-sqlite3/vfs/file.go
index ebd42e9ad..ba70aa14f 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/file.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/file.go
@@ -35,10 +35,10 @@ func testSymlinks(path string) error {
func (vfsOS) Delete(path string, syncDir bool) error {
err := os.Remove(path)
+ if errors.Is(err, fs.ErrNotExist) {
+ return _IOERR_DELETE_NOENT
+ }
if err != nil {
- if errors.Is(err, fs.ErrNotExist) {
- return _IOERR_DELETE_NOENT
- }
return err
}
if runtime.GOOS != "windows" && syncDir {
@@ -151,6 +151,7 @@ func (f *vfsFile) Close() error {
if f.shm != nil {
f.shm.Close()
}
+ f.Unlock(LOCK_NONE)
return f.File.Close()
}
@@ -206,10 +207,10 @@ func (f *vfsFile) HasMoved() (bool, error) {
return false, err
}
pi, err := os.Stat(f.Name())
+ if errors.Is(err, fs.ErrNotExist) {
+ return true, nil
+ }
if err != nil {
- if errors.Is(err, fs.ErrNotExist) {
- return true, nil
- }
return false, err
}
return !os.SameFile(fi, pi), nil
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go b/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
index 890684169..22e320a81 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go
@@ -1,4 +1,4 @@
-//go:build (linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys
+//go:build ((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk
package vfs
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go b/vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go
index c395f34a7..81aacc622 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/lock_other.go
@@ -1,4 +1,4 @@
-//go:build !(linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) || sqlite3_nosys
+//go:build !(((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk)
package vfs
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go
index d313b45d1..686f8e9a7 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/memdb/memdb.go
@@ -78,19 +78,15 @@ type memDB struct {
// +checklocks:dataMtx
data []*[sectorSize]byte
-
// +checklocks:dataMtx
size int64
- // +checklocks:lockMtx
- shared int32
- // +checklocks:lockMtx
- reserved bool
- // +checklocks:lockMtx
- pending bool
-
// +checklocks:memoryMtx
- refs int
+ refs int32
+
+ shared int32 // +checklocks:lockMtx
+ pending bool // +checklocks:lockMtx
+ reserved bool // +checklocks:lockMtx
lockMtx sync.Mutex
dataMtx sync.RWMutex
@@ -253,12 +249,12 @@ func (m *memFile) Unlock(lock vfs.LockLevel) error {
m.lockMtx.Lock()
defer m.lockMtx.Unlock()
- if m.pending && m.lock >= vfs.LOCK_PENDING {
- m.pending = false
- }
- if m.reserved && m.lock >= vfs.LOCK_RESERVED {
+ if m.lock >= vfs.LOCK_RESERVED {
m.reserved = false
}
+ if m.lock >= vfs.LOCK_PENDING {
+ m.pending = false
+ }
if lock < vfs.LOCK_SHARED {
m.shared--
}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
index 1f54a6929..56713e359 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
@@ -1,4 +1,4 @@
-//go:build (freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys
+//go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && !(sqlite3_dotlk || sqlite3_nosys)) || sqlite3_flock
package vfs
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_dotlk.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_dotlk.go
new file mode 100644
index 000000000..1c1a49c11
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_dotlk.go
@@ -0,0 +1,143 @@
+//go:build sqlite3_dotlk
+
+package vfs
+
+import (
+ "errors"
+ "io/fs"
+ "os"
+ "sync"
+)
+
+var (
+ // +checklocks:vfsDotLocksMtx
+ vfsDotLocks = map[string]*vfsDotLocker{}
+ vfsDotLocksMtx sync.Mutex
+)
+
+type vfsDotLocker struct {
+ shared int // +checklocks:vfsDotLocksMtx
+ pending *os.File // +checklocks:vfsDotLocksMtx
+ reserved *os.File // +checklocks:vfsDotLocksMtx
+}
+
+func osGetSharedLock(file *os.File) _ErrorCode {
+ vfsDotLocksMtx.Lock()
+ defer vfsDotLocksMtx.Unlock()
+
+ name := file.Name()
+ locker := vfsDotLocks[name]
+ if locker == nil {
+ err := os.Mkdir(name+".lock", 0777)
+ if errors.Is(err, fs.ErrExist) {
+ return _BUSY // Another process has the lock.
+ }
+ if err != nil {
+ return _IOERR_LOCK
+ }
+ locker = &vfsDotLocker{}
+ vfsDotLocks[name] = locker
+ }
+
+ if locker.pending != nil {
+ return _BUSY
+ }
+ locker.shared++
+ return _OK
+}
+
+func osGetReservedLock(file *os.File) _ErrorCode {
+ vfsDotLocksMtx.Lock()
+ defer vfsDotLocksMtx.Unlock()
+
+ name := file.Name()
+ locker := vfsDotLocks[name]
+ if locker == nil {
+ return _IOERR_LOCK
+ }
+
+ if locker.reserved != nil && locker.reserved != file {
+ return _BUSY
+ }
+ locker.reserved = file
+ return _OK
+}
+
+func osGetExclusiveLock(file *os.File, _ *LockLevel) _ErrorCode {
+ vfsDotLocksMtx.Lock()
+ defer vfsDotLocksMtx.Unlock()
+
+ name := file.Name()
+ locker := vfsDotLocks[name]
+ if locker == nil {
+ return _IOERR_LOCK
+ }
+
+ if locker.pending != nil && locker.pending != file {
+ return _BUSY
+ }
+ locker.pending = file
+ if locker.shared > 1 {
+ return _BUSY
+ }
+ return _OK
+}
+
+func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
+ vfsDotLocksMtx.Lock()
+ defer vfsDotLocksMtx.Unlock()
+
+ name := file.Name()
+ locker := vfsDotLocks[name]
+ if locker == nil {
+ return _IOERR_UNLOCK
+ }
+
+ if locker.reserved == file {
+ locker.reserved = nil
+ }
+ if locker.pending == file {
+ locker.pending = nil
+ }
+ return _OK
+}
+
+func osReleaseLock(file *os.File, state LockLevel) _ErrorCode {
+ vfsDotLocksMtx.Lock()
+ defer vfsDotLocksMtx.Unlock()
+
+ name := file.Name()
+ locker := vfsDotLocks[name]
+ if locker == nil {
+ return _IOERR_UNLOCK
+ }
+
+ if locker.shared == 1 {
+ err := os.Remove(name + ".lock")
+ if err != nil && !errors.Is(err, fs.ErrNotExist) {
+ return _IOERR_UNLOCK
+ }
+ delete(vfsDotLocks, name)
+ }
+
+ if locker.reserved == file {
+ locker.reserved = nil
+ }
+ if locker.pending == file {
+ locker.pending = nil
+ }
+ locker.shared--
+ return _OK
+}
+
+func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
+ vfsDotLocksMtx.Lock()
+ defer vfsDotLocksMtx.Unlock()
+
+ name := file.Name()
+ locker := vfsDotLocks[name]
+ if locker == nil {
+ return false, _OK
+ }
+ return locker.reserved != nil, _OK
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_ofd.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_ofd.go
index 15730fe62..b4f570f4d 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_ofd.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_ofd.go
@@ -1,4 +1,4 @@
-//go:build (linux || darwin) && !(sqlite3_flock || sqlite3_nosys)
+//go:build (linux || darwin) && !(sqlite3_flock || sqlite3_dotlk || sqlite3_nosys)
package vfs
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
index 4f6149ba3..b901f98aa 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
@@ -1,4 +1,4 @@
-//go:build !sqlite3_nosys
+//go:build !(sqlite3_dotlk || 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 402676afb..9d9dff1c4 100644
--- a/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go
@@ -1,20 +1,7 @@
-//go:build (darwin || linux) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_flock || sqlite3_noshm || sqlite3_nosys)
+//go:build ((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk
package vfs
-import (
- "context"
- "io"
- "os"
- "sync"
- "time"
-
- "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.
// To use [WAL without shared-memory], you need to set [EXCLUSIVE locking mode].
//
@@ -22,12 +9,6 @@ import (
// [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
@@ -41,172 +22,5 @@ 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,
- }
-}
-
-var _ blockingSharedMemory = &vfsShm{}
-
-type vfsShm struct {
- *os.File
- path string
- regions []*util.MappedRegion
- readOnly bool
- blocking bool
- sync.Mutex
-}
-
-func (s *vfsShm) shmOpen() _ErrorCode {
- 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 _CANTOPEN
- }
- s.File = f
- }
-
- // Dead man's switch.
- if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK {
- return _IOERR_LOCK
- } else if lock == unix.F_WRLCK {
- return _BUSY
- } else if lock == unix.F_UNLCK {
- if s.readOnly {
- return _READONLY_CANTINIT
- }
- // Do not use a blocking lock here.
- // If the lock cannot be obtained immediately,
- // it means some other connection is truncating the file.
- // And after it has done so, it will not release its lock,
- // but only downgrade it to a shared lock.
- // So no point in blocking here.
- // The call below to obtain the shared DMS lock may use a blocking lock.
- if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
- return rc
- }
- if err := s.Truncate(0); err != nil {
- return _IOERR_SHMOPEN
- }
- }
- if rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond); rc != _OK {
- return rc
- }
- return _OK
-}
-
-func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
- // Ensure size is a multiple of the OS page size.
- if int(size)&(unix.Getpagesize()-1) != 0 {
- return 0, _IOERR_SHMMAP
- }
-
- if rc := s.shmOpen(); 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, _OK
- }
- 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, _IOERR_SHMMAP
- }
- s.regions = append(s.regions, r)
- if s.readOnly {
- return r.Ptr, _READONLY
- }
- return r.Ptr, _OK
-}
-
-func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
- // 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())
- }
-
- var timeout time.Duration
- if s.blocking {
- timeout = time.Millisecond
- }
-
- 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), timeout)
- case flags&_SHM_EXCLUSIVE != 0:
- return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), timeout)
- 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.
- if delete {
- os.Remove(s.path)
- }
- s.Close()
- 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
+ return &vfsShm{path: path}
}
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 8dc6ec922..d4e046369 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) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_noshm || sqlite3_nosys)
+//go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_dotlk || sqlite3_nosys)) || sqlite3_flock
package vfs
@@ -14,44 +14,14 @@ import (
"github.com/ncruces/go-sqlite3/internal/util"
)
-// 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
-
-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 vfsShmFile struct {
*os.File
info os.FileInfo
- // +checklocks:vfsShmFilesMtx
- refs int
+ refs int // +checklocks:vfsShmFilesMtx
- // +checklocks:lockMtx
- lock [_SHM_NLOCK]int16
- lockMtx sync.Mutex
+ lock [_SHM_NLOCK]int16 // +checklocks:Mutex
+ sync.Mutex
}
var (
@@ -62,10 +32,9 @@ var (
type vfsShm struct {
*vfsShmFile
- path string
- lock [_SHM_NLOCK]bool
- regions []*util.MappedRegion
- readOnly bool
+ path string
+ lock [_SHM_NLOCK]bool
+ regions []*util.MappedRegion
}
func (s *vfsShm) Close() error {
@@ -80,7 +49,7 @@ func (s *vfsShm) Close() error {
s.shmLock(0, _SHM_NLOCK, _SHM_UNLOCK)
// Decrease reference count.
- if s.vfsShmFile.refs > 1 {
+ if s.vfsShmFile.refs > 0 {
s.vfsShmFile.refs--
s.vfsShmFile = nil
return nil
@@ -97,7 +66,7 @@ func (s *vfsShm) Close() error {
panic(util.AssertErr())
}
-func (s *vfsShm) shmOpen() (rc _ErrorCode) {
+func (s *vfsShm) shmOpen() _ErrorCode {
if s.vfsShmFile != nil {
return _OK
}
@@ -128,34 +97,29 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) {
}
}
- // Lock and truncate the file, if not readonly.
+ // Lock and truncate the file.
// The lock is only released by closing the file.
- if s.readOnly {
- rc = _READONLY_CANTINIT
- } else {
- if rc := osLock(f, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK); rc != _OK {
- return rc
- }
- if err := f.Truncate(0); err != nil {
- return _IOERR_SHMOPEN
- }
+ if rc := osLock(f, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK); rc != _OK {
+ return rc
+ }
+ if err := f.Truncate(0); err != nil {
+ return _IOERR_SHMOPEN
}
// Add the new shared file.
s.vfsShmFile = &vfsShmFile{
File: f,
info: fi,
- refs: 1,
}
f = nil // Don't close the file.
for i, g := range vfsShmFiles {
if g == nil {
vfsShmFiles[i] = s.vfsShmFile
- return rc
+ return _OK
}
}
vfsShmFiles = append(vfsShmFiles, s.vfsShmFile)
- return rc
+ return _OK
}
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
@@ -177,32 +141,22 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
if !extend {
return 0, _OK
}
- err := osAllocate(s.File, n)
- if err != nil {
+ if osAllocate(s.File, n) != 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)
+ r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, false)
if err != nil {
return 0, _IOERR_SHMMAP
}
s.regions = append(s.regions, r)
- if s.readOnly {
- return r.Ptr, _READONLY
- }
return r.Ptr, _OK
}
func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
- s.lockMtx.Lock()
- defer s.lockMtx.Unlock()
+ s.Lock()
+ defer s.Unlock()
switch {
case flags&_SHM_UNLOCK != 0:
@@ -224,7 +178,7 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
if s.lock[i] {
panic(util.AssertErr())
}
- if s.vfsShmFile.lock[i] < 0 {
+ if s.vfsShmFile.lock[i]+1 <= 0 {
return _BUSY
}
}
@@ -261,8 +215,7 @@ func (s *vfsShm) shmUnmap(delete bool) {
for _, r := range s.regions {
r.Unmap()
}
- clear(s.regions)
- s.regions = s.regions[:0]
+ s.regions = nil
// Close the file.
if delete {
@@ -272,7 +225,7 @@ func (s *vfsShm) shmUnmap(delete bool) {
}
func (s *vfsShm) shmBarrier() {
- s.lockMtx.Lock()
+ s.Lock()
//lint:ignore SA2001 memory barrier.
- s.lockMtx.Unlock()
+ s.Unlock()
}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_copy.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_copy.go
new file mode 100644
index 000000000..7a250523e
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_copy.go
@@ -0,0 +1,84 @@
+//go:build (windows && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys) || sqlite3_dotlk
+
+package vfs
+
+import (
+ "unsafe"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+const (
+ _WALINDEX_HDR_SIZE = 136
+ _WALINDEX_PGSZ = 32768
+)
+
+// This looks like a safe way of keeping the WAL-index in sync.
+//
+// The WAL-index file starts with a header,
+// and the index doesn't meaningfully change if the header doesn't change.
+//
+// The header starts with two 48 byte, checksummed, copies of the same information,
+// which are accessed independently between memory barriers.
+// The checkpoint information that follows uses 4 byte aligned words.
+//
+// Finally, we have the WAL-index hash tables,
+// which are only modified holding the exclusive WAL_WRITE_LOCK.
+//
+// Since all the data is either redundant+checksummed,
+// 4 byte aligned, or modified under an exclusive lock,
+// the copies below should correctly keep copies in sync.
+//
+// https://sqlite.org/walformat.html#the_wal_index_file_format
+
+func (s *vfsShm) shmAcquire() {
+ if len(s.ptrs) == 0 || shmUnmodified(s.shadow[0][:], s.shared[0][:]) {
+ return
+ }
+ // Copies modified words from shared to private memory.
+ for id, p := range s.ptrs {
+ shared := shmPage(s.shared[id][:])
+ shadow := shmPage(s.shadow[id][:])
+ privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
+ for i, shared := range shared {
+ if shadow[i] != shared {
+ shadow[i] = shared
+ privat[i] = shared
+ }
+ }
+ }
+}
+
+func (s *vfsShm) shmRelease() {
+ if len(s.ptrs) == 0 || shmUnmodified(s.shadow[0][:], util.View(s.mod, s.ptrs[0], _WALINDEX_HDR_SIZE)) {
+ return
+ }
+ // Copies modified words from private to shared memory.
+ for id, p := range s.ptrs {
+ shared := shmPage(s.shared[id][:])
+ shadow := shmPage(s.shadow[id][:])
+ privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
+ for i, privat := range privat {
+ if shadow[i] != privat {
+ shadow[i] = privat
+ shared[i] = privat
+ }
+ }
+ }
+}
+
+func (s *vfsShm) shmBarrier() {
+ s.Lock()
+ s.shmAcquire()
+ s.shmRelease()
+ s.Unlock()
+}
+
+func shmPage(s []byte) *[_WALINDEX_PGSZ / 4]uint32 {
+ p := (*uint32)(unsafe.Pointer(unsafe.SliceData(s)))
+ return (*[_WALINDEX_PGSZ / 4]uint32)(unsafe.Slice(p, _WALINDEX_PGSZ/4))
+}
+
+func shmUnmodified(v1, v2 []byte) bool {
+ return *(*[_WALINDEX_HDR_SIZE]byte)(v1[:]) == *(*[_WALINDEX_HDR_SIZE]byte)(v2[:])
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_dotlk.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_dotlk.go
new file mode 100644
index 000000000..36e00a1cd
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_dotlk.go
@@ -0,0 +1,224 @@
+//go:build sqlite3_dotlk
+
+package vfs
+
+import (
+ "context"
+ "errors"
+ "io/fs"
+ "os"
+ "sync"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/tetratelabs/wazero/api"
+)
+
+type vfsShmBuffer struct {
+ shared [][_WALINDEX_PGSZ]byte
+ refs int // +checklocks:vfsShmBuffersMtx
+
+ lock [_SHM_NLOCK]int16 // +checklocks:Mutex
+ sync.Mutex
+}
+
+var (
+ // +checklocks:vfsShmBuffersMtx
+ vfsShmBuffers = map[string]*vfsShmBuffer{}
+ vfsShmBuffersMtx sync.Mutex
+)
+
+type vfsShm struct {
+ *vfsShmBuffer
+ mod api.Module
+ alloc api.Function
+ free api.Function
+ path string
+ shadow [][_WALINDEX_PGSZ]byte
+ ptrs []uint32
+ stack [1]uint64
+ lock [_SHM_NLOCK]bool
+}
+
+func (s *vfsShm) Close() error {
+ if s.vfsShmBuffer == nil {
+ return nil
+ }
+
+ vfsShmBuffersMtx.Lock()
+ defer vfsShmBuffersMtx.Unlock()
+
+ // Unlock everything.
+ s.shmLock(0, _SHM_NLOCK, _SHM_UNLOCK)
+
+ // Decrease reference count.
+ if s.vfsShmBuffer.refs > 0 {
+ s.vfsShmBuffer.refs--
+ s.vfsShmBuffer = nil
+ return nil
+ }
+
+ err := os.Remove(s.path)
+ if err != nil && !errors.Is(err, fs.ErrNotExist) {
+ return _IOERR_UNLOCK
+ }
+ delete(vfsShmBuffers, s.path)
+ s.vfsShmBuffer = nil
+ return nil
+}
+
+func (s *vfsShm) shmOpen() _ErrorCode {
+ if s.vfsShmBuffer != nil {
+ return _OK
+ }
+
+ vfsShmBuffersMtx.Lock()
+ defer vfsShmBuffersMtx.Unlock()
+
+ // Find a shared buffer, increase the reference count.
+ if g, ok := vfsShmBuffers[s.path]; ok {
+ s.vfsShmBuffer = g
+ g.refs++
+ return _OK
+ }
+
+ // Create a directory on disk to ensure only this process
+ // uses this path to register a shared memory.
+ err := os.Mkdir(s.path, 0777)
+ if errors.Is(err, fs.ErrExist) {
+ return _BUSY
+ }
+ if err != nil {
+ return _IOERR_LOCK
+ }
+
+ // Add the new shared buffer.
+ s.vfsShmBuffer = &vfsShmBuffer{}
+ vfsShmBuffers[s.path] = s.vfsShmBuffer
+ return _OK
+}
+
+func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
+ if size != _WALINDEX_PGSZ {
+ return 0, _IOERR_SHMMAP
+ }
+ if s.mod == nil {
+ s.mod = mod
+ s.free = mod.ExportedFunction("sqlite3_free")
+ s.alloc = mod.ExportedFunction("sqlite3_malloc64")
+ }
+ if rc := s.shmOpen(); rc != _OK {
+ return 0, rc
+ }
+
+ s.Lock()
+ defer s.Unlock()
+ defer s.shmAcquire()
+
+ // Extend shared memory.
+ if int(id) >= len(s.shared) {
+ if !extend {
+ return 0, _OK
+ }
+ s.shared = append(s.shared, make([][_WALINDEX_PGSZ]byte, int(id)-len(s.shared)+1)...)
+ }
+
+ // Allocate shadow memory.
+ if int(id) >= len(s.shadow) {
+ s.shadow = append(s.shadow, make([][_WALINDEX_PGSZ]byte, int(id)-len(s.shadow)+1)...)
+ s.shadow[0][4] = 1 // force invalidation
+ }
+
+ // Allocate local memory.
+ for int(id) >= len(s.ptrs) {
+ s.stack[0] = uint64(size)
+ if err := s.alloc.CallWithStack(ctx, s.stack[:]); err != nil {
+ panic(err)
+ }
+ if s.stack[0] == 0 {
+ panic(util.OOMErr)
+ }
+ clear(util.View(s.mod, uint32(s.stack[0]), _WALINDEX_PGSZ))
+ s.ptrs = append(s.ptrs, uint32(s.stack[0]))
+ }
+
+ return s.ptrs[id], _OK
+}
+
+func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
+ s.Lock()
+ defer s.Unlock()
+
+ switch {
+ case flags&_SHM_LOCK != 0:
+ defer s.shmAcquire()
+ case flags&_SHM_EXCLUSIVE != 0:
+ s.shmRelease()
+ }
+
+ switch {
+ case flags&_SHM_UNLOCK != 0:
+ for i := offset; i < offset+n; i++ {
+ if s.lock[i] {
+ if s.vfsShmBuffer.lock[i] == 0 {
+ panic(util.AssertErr())
+ }
+ if s.vfsShmBuffer.lock[i] <= 0 {
+ s.vfsShmBuffer.lock[i] = 0
+ } else {
+ s.vfsShmBuffer.lock[i]--
+ }
+ s.lock[i] = false
+ }
+ }
+ case flags&_SHM_SHARED != 0:
+ for i := offset; i < offset+n; i++ {
+ if s.lock[i] {
+ panic(util.AssertErr())
+ }
+ if s.vfsShmBuffer.lock[i]+1 <= 0 {
+ return _BUSY
+ }
+ }
+ for i := offset; i < offset+n; i++ {
+ s.vfsShmBuffer.lock[i]++
+ s.lock[i] = true
+ }
+ case flags&_SHM_EXCLUSIVE != 0:
+ for i := offset; i < offset+n; i++ {
+ if s.lock[i] {
+ panic(util.AssertErr())
+ }
+ if s.vfsShmBuffer.lock[i] != 0 {
+ return _BUSY
+ }
+ }
+ for i := offset; i < offset+n; i++ {
+ s.vfsShmBuffer.lock[i] = -1
+ s.lock[i] = true
+ }
+ default:
+ panic(util.AssertErr())
+ }
+
+ return _OK
+}
+
+func (s *vfsShm) shmUnmap(delete bool) {
+ if s.vfsShmBuffer == nil {
+ return
+ }
+ defer s.Close()
+
+ s.Lock()
+ s.shmRelease()
+ defer s.Unlock()
+
+ for _, p := range s.ptrs {
+ s.stack[0] = uint64(p)
+ if err := s.free.CallWithStack(context.Background(), s.stack[:]); err != nil {
+ panic(err)
+ }
+ }
+ s.ptrs = nil
+ s.shadow = nil
+}
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_ofd.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_ofd.go
new file mode 100644
index 000000000..75c8fbcfb
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_ofd.go
@@ -0,0 +1,168 @@
+//go:build (linux || darwin) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_flock || sqlite3_dotlk || sqlite3_nosys)
+
+package vfs
+
+import (
+ "context"
+ "io"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/tetratelabs/wazero/api"
+ "golang.org/x/sys/unix"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+type vfsShm struct {
+ *os.File
+ path string
+ regions []*util.MappedRegion
+ readOnly bool
+ blocking bool
+ sync.Mutex
+}
+
+var _ blockingSharedMemory = &vfsShm{}
+
+func (s *vfsShm) shmOpen() _ErrorCode {
+ if s.File == nil {
+ f, err := os.OpenFile(s.path,
+ unix.O_RDWR|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
+ if err != nil {
+ f, err = os.OpenFile(s.path,
+ unix.O_RDONLY|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
+ s.readOnly = true
+ }
+ if err != nil {
+ return _CANTOPEN
+ }
+ s.File = f
+ }
+
+ // Dead man's switch.
+ if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK {
+ return _IOERR_LOCK
+ } else if lock == unix.F_WRLCK {
+ return _BUSY
+ } else if lock == unix.F_UNLCK {
+ if s.readOnly {
+ return _READONLY_CANTINIT
+ }
+ // Do not use a blocking lock here.
+ // If the lock cannot be obtained immediately,
+ // it means some other connection is truncating the file.
+ // And after it has done so, it will not release its lock,
+ // but only downgrade it to a shared lock.
+ // So no point in blocking here.
+ // The call below to obtain the shared DMS lock may use a blocking lock.
+ if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
+ return rc
+ }
+ if err := s.Truncate(0); err != nil {
+ return _IOERR_SHMOPEN
+ }
+ }
+ return osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
+}
+
+func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
+ // Ensure size is a multiple of the OS page size.
+ if int(size)&(unix.Getpagesize()-1) != 0 {
+ return 0, _IOERR_SHMMAP
+ }
+
+ if rc := s.shmOpen(); 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, _OK
+ }
+ if s.readOnly || osAllocate(s.File, n) != nil {
+ return 0, _IOERR_SHMSIZE
+ }
+ }
+
+ r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, s.readOnly)
+ if err != nil {
+ return 0, _IOERR_SHMMAP
+ }
+ s.regions = append(s.regions, r)
+ if s.readOnly {
+ return r.Ptr, _READONLY
+ }
+ return r.Ptr, _OK
+}
+
+func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
+ // 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())
+ }
+
+ var timeout time.Duration
+ if s.blocking {
+ timeout = time.Millisecond
+ }
+
+ 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), timeout)
+ case flags&_SHM_EXCLUSIVE != 0:
+ return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), timeout)
+ default:
+ panic(util.AssertErr())
+ }
+}
+
+func (s *vfsShm) shmUnmap(delete bool) {
+ if s.File == nil {
+ return
+ }
+
+ // Unmap regions.
+ for _, r := range s.regions {
+ r.Unmap()
+ }
+ s.regions = nil
+
+ // Close the file.
+ if delete {
+ os.Remove(s.path)
+ }
+ s.Close()
+ 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_other.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go
index 12012033e..9602dd0cd 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) || !(386 || arm || amd64 || arm64 || riscv64 || ppc64le) || sqlite3_noshm || sqlite3_nosys
+//go:build !(((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk)
package vfs
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_windows.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_windows.go
new file mode 100644
index 000000000..218d8e2c7
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_windows.go
@@ -0,0 +1,182 @@
+//go:build (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_dotlk || sqlite3_nosys)
+
+package vfs
+
+import (
+ "context"
+ "io"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/tetratelabs/wazero/api"
+ "golang.org/x/sys/windows"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+ "github.com/ncruces/go-sqlite3/util/osutil"
+)
+
+type vfsShm struct {
+ *os.File
+ mod api.Module
+ alloc api.Function
+ free api.Function
+ path string
+ regions []*util.MappedRegion
+ shared [][]byte
+ shadow [][_WALINDEX_PGSZ]byte
+ ptrs []uint32
+ stack [1]uint64
+ blocking bool
+ sync.Mutex
+}
+
+var _ blockingSharedMemory = &vfsShm{}
+
+func (s *vfsShm) Close() error {
+ // Unmap regions.
+ for _, r := range s.regions {
+ r.Unmap()
+ }
+ s.regions = nil
+
+ // Close the file.
+ return s.File.Close()
+}
+
+func (s *vfsShm) shmOpen() _ErrorCode {
+ if s.File == nil {
+ f, err := osutil.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0666)
+ if err != nil {
+ return _CANTOPEN
+ }
+ s.File = f
+ }
+
+ // Dead man's switch.
+ if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc == _OK {
+ err := s.Truncate(0)
+ osUnlock(s.File, _SHM_DMS, 1)
+ if err != nil {
+ return _IOERR_SHMOPEN
+ }
+ }
+ return osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
+}
+
+func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
+ // Ensure size is a multiple of the OS page size.
+ if size != _WALINDEX_PGSZ || (windows.Getpagesize()-1)&_WALINDEX_PGSZ != 0 {
+ return 0, _IOERR_SHMMAP
+ }
+ if s.mod == nil {
+ s.mod = mod
+ s.free = mod.ExportedFunction("sqlite3_free")
+ s.alloc = mod.ExportedFunction("sqlite3_malloc64")
+ }
+ if rc := s.shmOpen(); rc != _OK {
+ return 0, rc
+ }
+
+ defer s.shmAcquire()
+
+ // 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, _OK
+ }
+ if osAllocate(s.File, n) != nil {
+ return 0, _IOERR_SHMSIZE
+ }
+ }
+
+ // Maps regions into memory.
+ for int(id) >= len(s.shared) {
+ r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size)
+ if err != nil {
+ return 0, _IOERR_SHMMAP
+ }
+ s.regions = append(s.regions, r)
+ s.shared = append(s.shared, r.Data)
+ }
+
+ // Allocate shadow memory.
+ if int(id) >= len(s.shadow) {
+ s.shadow = append(s.shadow, make([][_WALINDEX_PGSZ]byte, int(id)-len(s.shadow)+1)...)
+ s.shadow[0][4] = 1 // force invalidation
+ }
+
+ // Allocate local memory.
+ for int(id) >= len(s.ptrs) {
+ s.stack[0] = uint64(size)
+ if err := s.alloc.CallWithStack(ctx, s.stack[:]); err != nil {
+ panic(err)
+ }
+ if s.stack[0] == 0 {
+ panic(util.OOMErr)
+ }
+ clear(util.View(s.mod, uint32(s.stack[0]), _WALINDEX_PGSZ))
+ s.ptrs = append(s.ptrs, uint32(s.stack[0]))
+ }
+
+ return s.ptrs[id], _OK
+}
+
+func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
+ switch {
+ case flags&_SHM_LOCK != 0:
+ defer s.shmAcquire()
+ case flags&_SHM_EXCLUSIVE != 0:
+ s.shmRelease()
+ }
+
+ var timeout time.Duration
+ if s.blocking {
+ timeout = time.Millisecond
+ }
+
+ switch {
+ case flags&_SHM_UNLOCK != 0:
+ return osUnlock(s.File, _SHM_BASE+uint32(offset), uint32(n))
+ case flags&_SHM_SHARED != 0:
+ return osReadLock(s.File, _SHM_BASE+uint32(offset), uint32(n), timeout)
+ case flags&_SHM_EXCLUSIVE != 0:
+ return osWriteLock(s.File, _SHM_BASE+uint32(offset), uint32(n), timeout)
+ default:
+ panic(util.AssertErr())
+ }
+}
+
+func (s *vfsShm) shmUnmap(delete bool) {
+ if s.File == nil {
+ return
+ }
+
+ s.shmRelease()
+
+ // Free local memory.
+ for _, p := range s.ptrs {
+ s.stack[0] = uint64(p)
+ if err := s.free.CallWithStack(context.Background(), s.stack[:]); err != nil {
+ panic(err)
+ }
+ }
+ s.ptrs = nil
+ s.shadow = nil
+ s.shared = nil
+
+ // Close the file.
+ s.Close()
+ s.File = nil
+ if delete {
+ os.Remove(s.path)
+ }
+}
+
+func (s *vfsShm) shmEnableBlocking(block bool) {
+ s.blocking = block
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index e11598f09..df410ca5b 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -514,7 +514,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.20.0
+# github.com/ncruces/go-sqlite3 v0.20.2
## explicit; go 1.21
github.com/ncruces/go-sqlite3
github.com/ncruces/go-sqlite3/driver