[chore] Update WASM go-sqlite3 to v0.16.1 (#2976)

This includes support for journal mode set to WAL on the BSDs.

Relates to: #1753, #2962
This commit is contained in:
Daenney
2024-06-07 15:06:43 +02:00
committed by GitHub
parent afcfa48a7d
commit cc4f773b0e
46 changed files with 618 additions and 359 deletions

View File

@ -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.

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -1,9 +0,0 @@
//go:build !windows || sqlite3_nosys
package vfs
import "time"
func osSleep(d time.Duration) {
time.Sleep(d)
}

View File

@ -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))
}
}
}

View File

@ -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
}

259
vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go generated vendored Normal file
View File

@ -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
}

View File

@ -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

View File

@ -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 {