diff options
Diffstat (limited to 'vendor/github.com/ncruces/go-sqlite3/vfs')
11 files changed, 309 insertions, 59 deletions
diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/README.md b/vendor/github.com/ncruces/go-sqlite3/vfs/README.md index 88059a41b..741a1b6a4 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/README.md +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/README.md @@ -46,7 +46,7 @@ to check if your build supports file locking. ### Write-Ahead Logging -On 64-bit Linux and macOS, this module uses `mmap` to implement +On 64-bit Unix, this module uses `mmap` to implement [shared-memory for the WAL-index](https://sqlite.org/wal.html#implementation_of_shared_memory_for_the_wal_index), like SQLite. @@ -54,6 +54,11 @@ To allow `mmap` to work, each connection needs to reserve up to 4GB of address s To limit the address space each connection reserves, use [`WithMemoryLimitPages`](../tests/testcfg/testcfg.go). +With [BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2) +a WAL database can only be accessed by a single proccess. +Other processes that attempt to access a database locked with BSD locks, +will fail with the `SQLITE_PROTOCOL` error code. + 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 @@ -79,8 +84,9 @@ The VFS can be customized with a few build tags: - `sqlite3_noshm` disables shared memory on all platforms. > [!IMPORTANT] -> The default configuration of this package is compatible with -> the standard [Unix and Windows SQLite VFSes](https://sqlite.org/vfs.html#multiple_vfses); -> `sqlite3_flock` is compatible with the [`unix-flock` VFS](https://sqlite.org/compile.html#enable_locking_style). -> If incompatible file locking is used, accessing databases concurrently with _other_ SQLite libraries -> will eventually corrupt data. +> 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). +> If incompatible file locking is used, accessing databases concurrently with +> _other_ SQLite libraries will eventually corrupt data. diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/api.go b/vendor/github.com/ncruces/go-sqlite3/vfs/api.go index 19c22ae8f..e133e8be9 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/api.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/api.go @@ -168,8 +168,8 @@ type FileSharedMemory interface { // SharedMemory is a shared-memory WAL-index implementation. // Use [NewSharedMemory] to create a shared-memory. type SharedMemory interface { - shmMap(context.Context, api.Module, int32, int32, bool) (uint32, error) - shmLock(int32, int32, _ShmFlag) error + shmMap(context.Context, api.Module, int32, int32, bool) (uint32, _ErrorCode) + shmLock(int32, int32, _ShmFlag) _ErrorCode shmUnmap(bool) io.Closer } diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/const.go b/vendor/github.com/ncruces/go-sqlite3/vfs/const.go index 7f409f35f..f7217af96 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/const.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/const.go @@ -47,6 +47,7 @@ const ( _IOERR_SHMMAP _ErrorCode = util.IOERR_SHMMAP _IOERR_SEEK _ErrorCode = util.IOERR_SEEK _IOERR_DELETE_NOENT _ErrorCode = util.IOERR_DELETE_NOENT + _IOERR_GETTEMPPATH _ErrorCode = util.IOERR_GETTEMPPATH _IOERR_BEGIN_ATOMIC _ErrorCode = util.IOERR_BEGIN_ATOMIC _IOERR_COMMIT_ATOMIC _ErrorCode = util.IOERR_COMMIT_ATOMIC _IOERR_ROLLBACK_ATOMIC _ErrorCode = util.IOERR_ROLLBACK_ATOMIC diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/file.go b/vendor/github.com/ncruces/go-sqlite3/vfs/file.go index ca8cf84f3..93a2f7ece 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/file.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/file.go @@ -95,6 +95,9 @@ func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error f, err = osutil.OpenFile(name.String(), oflags, 0666) } if err != nil { + if name == nil { + return nil, flags, _IOERR_GETTEMPPATH + } if errors.Is(err, syscall.EISDIR) { return nil, flags, _CANTOPEN_ISDIR } diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go index 11e683a04..7bb78c0af 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go @@ -56,7 +56,7 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d if timeout < time.Since(before) { break } - osSleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) + time.Sleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) } } return osLockErrorCode(err, def) diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go deleted file mode 100644 index c6bc40769..000000000 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_std_sleep.go +++ /dev/null @@ -1,9 +0,0 @@ -//go:build !windows || sqlite3_nosys - -package vfs - -import "time" - -func osSleep(d time.Duration) { - time.Sleep(d) -} diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go index 5c68754f8..83b952b16 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go @@ -136,7 +136,7 @@ func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def if timeout < time.Since(before) { break } - osSleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) + time.Sleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) } } return osLockErrorCode(err, def) @@ -171,16 +171,3 @@ func osLockErrorCode(err error, def _ErrorCode) _ErrorCode { } return def } - -func osSleep(d time.Duration) { - if d > 0 { - period := max(1, d/(5*time.Millisecond)) - if period < 16 { - windows.TimeBeginPeriod(uint32(period)) - } - time.Sleep(d) - if period < 16 { - windows.TimeEndPeriod(uint32(period)) - } - } -} diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go index 2b76dd5dc..58da34df4 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go @@ -51,12 +51,7 @@ type vfsShm struct { readOnly bool } -func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, error) { - // Ensure size is a multiple of the OS page size. - if int(size)&(unix.Getpagesize()-1) != 0 { - return 0, _IOERR_SHMMAP - } - +func (s *vfsShm) shmOpen() _ErrorCode { if s.File == nil { var flag int if s.readOnly { @@ -67,28 +62,40 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext f, err := os.OpenFile(s.path, flag|unix.O_CREAT|unix.O_NOFOLLOW, 0666) if err != nil { - return 0, _CANTOPEN + return _CANTOPEN } s.File = f } // Dead man's switch. if lock, rc := osGetLock(s.File, _SHM_DMS, 1); rc != _OK { - return 0, _IOERR_LOCK + return _IOERR_LOCK } else if lock == unix.F_WRLCK { - return 0, _BUSY + return _BUSY } else if lock == unix.F_UNLCK { if s.readOnly { - return 0, _READONLY_CANTINIT + return _READONLY_CANTINIT } if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK { - return 0, rc + return rc } if err := s.Truncate(0); err != nil { - return 0, _IOERR_SHMOPEN + return _IOERR_SHMOPEN } } if rc := osReadLock(s.File, _SHM_DMS, 1, 0); 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 } @@ -99,7 +106,7 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext } if n := (int64(id) + 1) * int64(size); n > o { if !extend { - return 0, nil + return 0, _OK } err := osAllocate(s.File, n) if err != nil { @@ -115,13 +122,13 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext } r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, prot) if err != nil { - return 0, err + return 0, _IOERR_SHMMAP } s.regions = append(s.regions, r) - return r.Ptr, nil + return r.Ptr, _OK } -func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error { +func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode { // Argument check. if n <= 0 || offset < 0 || offset+n > _SHM_NLOCK { panic(util.AssertErr()) @@ -165,9 +172,9 @@ func (s *vfsShm) shmUnmap(delete bool) { s.regions = s.regions[:0] // Close the file. - defer s.Close() if delete { - os.Remove(s.Name()) + os.Remove(s.path) } + s.Close() s.File = nil } diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go new file mode 100644 index 000000000..3b45b3087 --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go @@ -0,0 +1,259 @@ +//go:build (freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && (amd64 || arm64 || riscv64) && !(sqlite3_noshm || sqlite3_nosys) + +package vfs + +import ( + "context" + "io" + "os" + "sync" + + "github.com/ncruces/go-sqlite3/internal/util" + "github.com/tetratelabs/wazero/api" + "golang.org/x/sys/unix" +) + +// SupportsSharedMemory is false on platforms that do not support shared memory. +// To use [WAL without shared-memory], you need to set [EXCLUSIVE locking mode]. +// +// [WAL without shared-memory]: https://sqlite.org/wal.html#noshm +// [EXCLUSIVE locking mode]: https://sqlite.org/pragma.html#pragma_locking_mode +const SupportsSharedMemory = true + +const _SHM_NLOCK = 8 + +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 + + // +checklocks:lockMtx + lock [_SHM_NLOCK]int16 + lockMtx sync.Mutex +} + +var ( + // +checklocks:vfsShmFilesMtx + vfsShmFiles []*vfsShmFile + vfsShmFilesMtx sync.Mutex +) + +type vfsShm struct { + *vfsShmFile + path string + lock [_SHM_NLOCK]bool + regions []*util.MappedRegion + readOnly bool +} + +func (s *vfsShm) Close() error { + if s.vfsShmFile == nil { + return nil + } + + // Unlock everything. + s.shmLock(0, _SHM_NLOCK, _SHM_UNLOCK) + + vfsShmFilesMtx.Lock() + defer vfsShmFilesMtx.Unlock() + + // Decrease reference count. + if s.vfsShmFile.refs > 1 { + s.vfsShmFile.refs-- + s.vfsShmFile = nil + return nil + } + for i, g := range vfsShmFiles { + if g == s.vfsShmFile { + vfsShmFiles[i] = nil + break + } + } + + err := s.File.Close() + s.vfsShmFile = nil + return err +} + +func (s *vfsShm) shmOpen() (rc _ErrorCode) { + if s.vfsShmFile != nil { + return _OK + } + + // Open file read-write, as it will be shared. + f, err := os.OpenFile(s.path, + unix.O_RDWR|unix.O_CREAT|unix.O_NOFOLLOW, 0666) + if err != nil { + return _CANTOPEN + } + // Close if file if it's not nil. + defer func() { f.Close() }() + + fi, err := f.Stat() + if err != nil { + return _IOERR_FSTAT + } + + vfsShmFilesMtx.Lock() + defer vfsShmFilesMtx.Unlock() + + // Find a shared file, increase the reference count. + for _, g := range vfsShmFiles { + if g != nil && os.SameFile(fi, g.info) { + g.refs++ + s.vfsShmFile = g + return _OK + } + } + + // Lock and truncate the file, if not readonly. + if s.readOnly { + rc = _READONLY_CANTINIT + } else { + if rc := osWriteLock(f, 0, 0, 0); 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 + add := true + for i, g := range vfsShmFiles { + if g == nil { + vfsShmFiles[i] = s.vfsShmFile + add = false + } + } + if add { + vfsShmFiles = append(vfsShmFiles, s.vfsShmFile) + } + return rc +} + +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) + return r.Ptr, _OK +} + +func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode { + s.lockMtx.Lock() + defer s.lockMtx.Unlock() + + switch { + case flags&_SHM_UNLOCK != 0: + for i := offset; i < offset+n; i++ { + if s.lock[i] { + if s.vfsShmFile.lock[i] <= 0 { + s.vfsShmFile.lock[i] = 0 + } else { + s.vfsShmFile.lock[i]-- + } + } + } + case flags&_SHM_SHARED != 0: + for i := offset; i < offset+n; i++ { + if s.vfsShmFile.lock[i] < 0 { + return _BUSY + } + } + for i := offset; i < offset+n; i++ { + s.vfsShmFile.lock[i]++ + s.lock[i] = true + } + case flags&_SHM_EXCLUSIVE != 0: + for i := offset; i < offset+n; i++ { + if s.vfsShmFile.lock[i] != 0 { + return _BUSY + } + } + for i := offset; i < offset+n; i++ { + s.vfsShmFile.lock[i] = -1 + s.lock[i] = true + } + } + + return _OK +} + +func (s *vfsShm) shmUnmap(delete bool) { + if s.vfsShmFile == 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.vfsShmFile = nil +} diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm_other.go index 21191979e..7c8997581 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) || !(amd64 || arm64 || riscv64) || sqlite3_flock || sqlite3_noshm || sqlite3_nosys +//go:build !(darwin || linux || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) || !(amd64 || arm64 || riscv64) || sqlite3_noshm || sqlite3_nosys package vfs diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go b/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go index 1887e9f22..d624aa78c 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go @@ -83,7 +83,7 @@ func vfsRandomness(ctx context.Context, mod api.Module, pVfs uint32, nByte int32 } func vfsSleep(ctx context.Context, mod api.Module, pVfs uint32, nMicro int32) _ErrorCode { - osSleep(time.Duration(nMicro) * time.Microsecond) + time.Sleep(time.Duration(nMicro) * time.Microsecond) return _OK } @@ -397,18 +397,14 @@ func vfsShmBarrier(ctx context.Context, mod api.Module, pFile uint32) { func vfsShmMap(ctx context.Context, mod api.Module, pFile uint32, iRegion, szRegion int32, bExtend, pp uint32) _ErrorCode { shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory() - p, err := shm.shmMap(ctx, mod, iRegion, szRegion, bExtend != 0) - if err != nil { - return vfsErrorCode(err, _IOERR_SHMMAP) - } + p, rc := shm.shmMap(ctx, mod, iRegion, szRegion, bExtend != 0) util.WriteUint32(mod, pp, p) - return _OK + return rc } func vfsShmLock(ctx context.Context, mod api.Module, pFile uint32, offset, n int32, flags _ShmFlag) _ErrorCode { shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory() - err := shm.shmLock(offset, n, flags) - return vfsErrorCode(err, _IOERR_SHMLOCK) + return shm.shmLock(offset, n, flags) } func vfsShmUnmap(ctx context.Context, mod api.Module, pFile, bDelete uint32) _ErrorCode { |