mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
Improved WAL API.
This commit is contained in:
@@ -3,6 +3,7 @@ package vfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
@@ -154,17 +155,19 @@ type FileCheckpoint interface {
|
||||
CheckpointStart() error
|
||||
}
|
||||
|
||||
// FileSharedMemory extends File to possibly implement shared memory.
|
||||
// FileSharedMemory extends File to possibly implement
|
||||
// shared-memory for the WAL-index.
|
||||
// It's OK for SharedMemory to return nil.
|
||||
type FileSharedMemory interface {
|
||||
File
|
||||
SharedMemory() SharedMemory
|
||||
}
|
||||
|
||||
// SharedMemory is a shared memory implementation.
|
||||
// SharedMemory is a shared-memory WAL-index implementation.
|
||||
// This cannot be externally implemented.
|
||||
type SharedMemory interface {
|
||||
shmMap(context.Context, api.Module, int32, int32, bool) (uint32, error)
|
||||
shmLock(int32, int32, _ShmFlag) error
|
||||
shmUnmap(bool)
|
||||
io.Closer
|
||||
}
|
||||
|
||||
@@ -118,13 +118,14 @@ func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error
|
||||
syncDir: runtime.GOOS != "windows" &&
|
||||
flags&(OPEN_CREATE) != 0 &&
|
||||
flags&(OPEN_MAIN_JOURNAL|OPEN_SUPER_JOURNAL|OPEN_WAL) != 0,
|
||||
shm: NewSharedMemory(name.String()+"-shm", flags),
|
||||
}
|
||||
return &file, flags, nil
|
||||
}
|
||||
|
||||
type vfsFile struct {
|
||||
*os.File
|
||||
shm vfsShm
|
||||
shm SharedMemory
|
||||
lock LockLevel
|
||||
readOnly bool
|
||||
keepWAL bool
|
||||
@@ -142,7 +143,9 @@ var (
|
||||
)
|
||||
|
||||
func (f *vfsFile) Close() error {
|
||||
f.shm.Close()
|
||||
if f.shm != nil {
|
||||
f.shm.Close()
|
||||
}
|
||||
return f.File.Close()
|
||||
}
|
||||
|
||||
|
||||
85
vfs/shm.go
85
vfs/shm.go
@@ -22,90 +22,107 @@ import (
|
||||
// [EXCLUSIVE locking mode]: https://sqlite.org/pragma.html#pragma_locking_mode
|
||||
const SupportsSharedMemory = true
|
||||
|
||||
type vfsShm struct {
|
||||
*os.File
|
||||
regions []*util.MappedRegion
|
||||
}
|
||||
|
||||
const (
|
||||
_SHM_NLOCK = 8
|
||||
_SHM_BASE = 120
|
||||
_SHM_DMS = _SHM_BASE + _SHM_NLOCK
|
||||
)
|
||||
|
||||
func (f *vfsFile) SharedMemory() SharedMemory { return f }
|
||||
func (f *vfsFile) SharedMemory() SharedMemory { return f.shm }
|
||||
|
||||
func (f *vfsFile) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, error) {
|
||||
// NewSharedMemory returns a shared-memory WAL-index
|
||||
// backed a file with the given path.
|
||||
// It may return nil if shared-memory is not supported,
|
||||
// or not appropriate for the given flags.
|
||||
// Only [OPEN_MAIN_DB] databases support WAL mode.
|
||||
func NewSharedMemory(path string, flags OpenFlag) SharedMemory {
|
||||
if flags&OPEN_MAIN_DB == 0 {
|
||||
return nil
|
||||
}
|
||||
return &vfsShm{
|
||||
path: path,
|
||||
readOnly: flags&OPEN_READONLY != 0,
|
||||
}
|
||||
}
|
||||
|
||||
type vfsShm struct {
|
||||
*os.File
|
||||
regions []*util.MappedRegion
|
||||
path string
|
||||
readOnly bool
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, error) {
|
||||
// Ensure size is a multiple of the OS page size.
|
||||
if int(size)&(unix.Getpagesize()-1) != 0 {
|
||||
return 0, _IOERR_SHMMAP
|
||||
}
|
||||
|
||||
if f.shm.File == nil {
|
||||
if s.File == nil {
|
||||
var flag int
|
||||
if f.readOnly {
|
||||
if s.readOnly {
|
||||
flag = unix.O_RDONLY
|
||||
} else {
|
||||
flag = unix.O_RDWR
|
||||
}
|
||||
s, err := os.OpenFile(f.Name()+"-shm",
|
||||
f, err := os.OpenFile(s.path,
|
||||
flag|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
|
||||
if err != nil {
|
||||
return 0, _CANTOPEN
|
||||
}
|
||||
f.shm.File = s
|
||||
s.File = f
|
||||
}
|
||||
|
||||
// Dead man's switch.
|
||||
if lock, rc := osGetLock(f.shm.File, _SHM_DMS, 1); rc != _OK {
|
||||
if lock, rc := osGetLock(s.File, _SHM_DMS, 1); rc != _OK {
|
||||
return 0, _IOERR_LOCK
|
||||
} else if lock == unix.F_WRLCK {
|
||||
return 0, _BUSY
|
||||
} else if lock == unix.F_UNLCK {
|
||||
if f.readOnly {
|
||||
if s.readOnly {
|
||||
return 0, _READONLY_CANTINIT
|
||||
}
|
||||
if rc := osWriteLock(f.shm.File, _SHM_DMS, 1, 0); rc != _OK {
|
||||
if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
|
||||
return 0, rc
|
||||
}
|
||||
if err := f.shm.Truncate(0); err != nil {
|
||||
if err := s.Truncate(0); err != nil {
|
||||
return 0, _IOERR_SHMOPEN
|
||||
}
|
||||
}
|
||||
if rc := osReadLock(f.shm.File, _SHM_DMS, 1, 0); rc != _OK {
|
||||
if rc := osReadLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
|
||||
return 0, rc
|
||||
}
|
||||
|
||||
// Check if file is big enough.
|
||||
s, err := f.shm.Seek(0, io.SeekEnd)
|
||||
o, err := s.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return 0, _IOERR_SHMSIZE
|
||||
}
|
||||
if n := (int64(id) + 1) * int64(size); n > s {
|
||||
if n := (int64(id) + 1) * int64(size); n > o {
|
||||
if !extend {
|
||||
return 0, nil
|
||||
}
|
||||
err := osAllocate(f.shm.File, n)
|
||||
err := osAllocate(s.File, n)
|
||||
if err != nil {
|
||||
return 0, _IOERR_SHMSIZE
|
||||
}
|
||||
}
|
||||
|
||||
var prot int
|
||||
if f.readOnly {
|
||||
if s.readOnly {
|
||||
prot = unix.PROT_READ
|
||||
} else {
|
||||
prot = unix.PROT_READ | unix.PROT_WRITE
|
||||
}
|
||||
r, err := util.MapRegion(ctx, mod, f.shm.File, int64(id)*int64(size), size, prot)
|
||||
r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, prot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
f.shm.regions = append(f.shm.regions, r)
|
||||
s.regions = append(s.regions, r)
|
||||
return r.Ptr, nil
|
||||
}
|
||||
|
||||
func (f *vfsFile) shmLock(offset, n int32, flags _ShmFlag) error {
|
||||
func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error {
|
||||
// Argument check.
|
||||
if n <= 0 || offset < 0 || offset+n > _SHM_NLOCK {
|
||||
panic(util.AssertErr())
|
||||
@@ -126,28 +143,28 @@ func (f *vfsFile) shmLock(offset, n int32, flags _ShmFlag) error {
|
||||
|
||||
switch {
|
||||
case flags&_SHM_UNLOCK != 0:
|
||||
return osUnlock(f.shm.File, _SHM_BASE+int64(offset), int64(n))
|
||||
return osUnlock(s.File, _SHM_BASE+int64(offset), int64(n))
|
||||
case flags&_SHM_SHARED != 0:
|
||||
return osReadLock(f.shm.File, _SHM_BASE+int64(offset), int64(n), 0)
|
||||
return osReadLock(s.File, _SHM_BASE+int64(offset), int64(n), 0)
|
||||
case flags&_SHM_EXCLUSIVE != 0:
|
||||
return osWriteLock(f.shm.File, _SHM_BASE+int64(offset), int64(n), 0)
|
||||
return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), 0)
|
||||
default:
|
||||
panic(util.AssertErr())
|
||||
}
|
||||
}
|
||||
|
||||
func (f *vfsFile) shmUnmap(delete bool) {
|
||||
func (s *vfsShm) shmUnmap(delete bool) {
|
||||
// Unmap regions.
|
||||
for _, r := range f.shm.regions {
|
||||
for _, r := range s.regions {
|
||||
r.Unmap()
|
||||
}
|
||||
clear(f.shm.regions)
|
||||
f.shm.regions = f.shm.regions[:0]
|
||||
clear(s.regions)
|
||||
s.regions = s.regions[:0]
|
||||
|
||||
// Close the file.
|
||||
if delete && f.shm.File != nil {
|
||||
os.Remove(f.shm.Name())
|
||||
if delete && s.File != nil {
|
||||
os.Remove(s.Name())
|
||||
}
|
||||
f.shm.Close()
|
||||
f.shm.File = nil
|
||||
s.Close()
|
||||
s.File = nil
|
||||
}
|
||||
|
||||
@@ -12,6 +12,11 @@ package vfs
|
||||
// [EXCLUSIVE locking mode]: https://sqlite.org/pragma.html#pragma_locking_mode
|
||||
const SupportsSharedMemory = false
|
||||
|
||||
type vfsShm struct{}
|
||||
|
||||
func (vfsShm) Close() error { return nil }
|
||||
// NewSharedMemory returns a shared-memory WAL-index
|
||||
// backed a file with the given path.
|
||||
// It may return nil if shared-memory is not supported,
|
||||
// or not appropriate for the given flags.
|
||||
// Only [OPEN_MAIN_DB] databases support WAL mode.
|
||||
func NewSharedMemory(path string, flags OpenFlag) SharedMemory {
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user