diff options
Diffstat (limited to 'vendor')
25 files changed, 960 insertions, 308 deletions
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  | 
