Documentation.

This commit is contained in:
Nuno Cruces
2023-05-19 13:47:12 +01:00
parent 4e2ce6c635
commit 663b23ff3b
11 changed files with 128 additions and 70 deletions

View File

@@ -15,6 +15,8 @@ provides a [`database/sql`](https://pkg.go.dev/database/sql) driver
([example usage](https://pkg.go.dev/github.com/ncruces/go-sqlite3/driver#example-package)).
- Package [`github.com/ncruces/go-sqlite3/embed`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/embed)
embeds a build of SQLite into your application.
- Package [`github.com/ncruces/go-sqlite3/sqlite3vfs`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/sqlite3vfs)
wraps the [C SQLite VFS API](https://www.sqlite.org/vfs.html) and provides a pure Go implementation.
### Caveats
@@ -73,7 +75,7 @@ Performance is tested by running
- [ ] in-memory VFS
- [ ] read-only VFS, wrapping an [`io.ReaderAt`](https://pkg.go.dev/io#ReaderAt)
- [ ] cloud-based VFS, based on [Cloud Backed SQLite](https://sqlite.org/cloudsqlite/doc/trunk/www/index.wiki)
- [ ] custom VFS API
- [x] custom VFS API
### Alternatives

View File

@@ -11,7 +11,7 @@ type Backup struct {
// Backup backs up srcDB on the src connection to the "main" database in dstURI.
//
// Backup calls [Open] to open the SQLite database file dstURI,
// Backup opens the SQLite database file dstURI,
// and blocks until the entire backup is complete.
// Use [Conn.BackupInit] for incremental backup.
//
@@ -28,7 +28,7 @@ func (src *Conn) Backup(srcDB, dstURI string) error {
// Restore restores dstDB on the dst connection from the "main" database in srcURI.
//
// Restore calls [Open] to open the SQLite database file srcURI,
// Restore opens the SQLite database file srcURI,
// and blocks until the entire restore is complete.
//
// https://www.sqlite.org/backup.html
@@ -48,7 +48,7 @@ func (dst *Conn) Restore(dstDB, srcURI string) error {
// BackupInit initializes a backup operation to copy the content of one database into another.
//
// BackupInit calls [Open] to open the SQLite database file dstURI,
// BackupInit opens the SQLite database file dstURI,
// then initializes a backup that copies the contents of srcDB on the src connection
// to the "main" database in dstURI.
//

View File

@@ -278,7 +278,8 @@ func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) {
break
case <-ctx.Done(): // Done was closed.
buf := util.View(c.mod, c.handle+280, 4)
const isInterruptedOffset = 280
buf := util.View(c.mod, c.handle+isInterruptedOffset, 4)
(*atomic.Uint32)(unsafe.Pointer(&buf[0])).Store(1)
// Wait for the next call to SetInterrupt.
<-waiter
@@ -294,7 +295,8 @@ func (c *Conn) checkInterrupt() bool {
if c.interrupt == nil || c.interrupt.Err() == nil {
return false
}
buf := util.View(c.mod, c.handle+280, 4)
const isInterruptedOffset = 280
buf := util.View(c.mod, c.handle+isInterruptedOffset, 4)
(*atomic.Uint32)(unsafe.Pointer(&buf[0])).Store(1)
return true
}

View File

@@ -16,7 +16,7 @@
//
// sql.Open("sqlite3", "file:demo.db?_pragma=busy_timeout(10000)&_pragma=locking_mode(normal)")
//
// If no PRAGMAs are specifed, a busy timeout of 1 minute
// If no PRAGMAs are specified, a busy timeout of 1 minute
// and normal locking mode are used.
//
// [URI]: https://www.sqlite.org/uri.html

View File

@@ -23,6 +23,7 @@ const (
OffsetErr = ErrorString("sqlite3: invalid offset")
TailErr = ErrorString("sqlite3: multiple statements")
IsolationErr = ErrorString("sqlite3: unsupported isolation level")
NoVFSErr = ErrorString("sqlite3: no such vfs: ")
)
func AssertErr() ErrorString {

View File

@@ -16,7 +16,7 @@ func (fn funcII[TR, T0]) Call(ctx context.Context, mod api.Module, stack []uint6
stack[0] = uint64(fn(ctx, mod, T0(stack[0])))
}
func RegisterFuncII[TR, T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0) TR) {
func ExportFuncII[TR, T0 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcII[TR, T0](fn),
[]api.ValueType{api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
@@ -29,7 +29,7 @@ func (fn funcIII[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1])))
}
func RegisterFuncIII[TR, T0, T1 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) {
func ExportFuncIII[TR, T0, T1 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIII[TR, T0, T1](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
@@ -42,7 +42,7 @@ func (fn funcIIII[TR, T0, T1, T2]) Call(ctx context.Context, mod api.Module, sta
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2])))
}
func RegisterFuncIIII[TR, T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2) TR) {
func ExportFuncIIII[TR, T0, T1, T2 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIII[TR, T0, T1, T2](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
@@ -55,7 +55,7 @@ func (fn funcIIIII[TR, T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3])))
}
func RegisterFuncIIIII[TR, T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) {
func ExportFuncIIIII[TR, T0, T1, T2, T3 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIIII[TR, T0, T1, T2, T3](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
@@ -68,7 +68,7 @@ func (fn funcIIIIII[TR, T0, T1, T2, T3, T4]) Call(ctx context.Context, mod api.M
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4])))
}
func RegisterFuncIIIIII[TR, T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4) TR) {
func ExportFuncIIIIII[TR, T0, T1, T2, T3, T4 i32](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3, T4) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIIIII[TR, T0, T1, T2, T3, T4](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
@@ -81,7 +81,7 @@ func (fn funcIIIIJ[TR, T0, T1, T2, T3]) Call(ctx context.Context, mod api.Module
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3])))
}
func RegisterFuncIIIIJ[TR, T0, T1, T2 i32, T3 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) {
func ExportFuncIIIIJ[TR, T0, T1, T2 i32, T3 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1, T2, T3) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIIIJ[TR, T0, T1, T2, T3](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}).
@@ -94,7 +94,7 @@ func (fn funcIIJ[TR, T0, T1]) Call(ctx context.Context, mod api.Module, stack []
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1])))
}
func RegisterFuncIIJ[TR, T0 i32, T1 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) {
func ExportFuncIIJ[TR, T0 i32, T1 i64](mod wazero.HostModuleBuilder, name string, fn func(context.Context, api.Module, T0, T1) TR) {
mod.NewFunctionBuilder().
WithGoModuleFunction(funcIIJ[TR, T0, T1](fn),
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}).

View File

@@ -42,7 +42,7 @@ const (
_OK_SYMLINK _ErrorCode = util.OK_SYMLINK
)
// OpenFlag is a flag for the [VFS.Open] method.
// OpenFlag is a flag for the [VFS] Open method.
//
// https://www.sqlite.org/c3ref/c_open_autoproxy.html
type OpenFlag uint32
@@ -71,7 +71,7 @@ const (
OPEN_NOFOLLOW OpenFlag = 0x01000000 /* Ok for sqlite3_open_v2() */
)
// AccessFlag is a flag for the [VFS.Access] method.
// AccessFlag is a flag for the [VFS] Access method.
//
// https://www.sqlite.org/c3ref/c_access_exists.html
type AccessFlag uint32
@@ -82,7 +82,7 @@ const (
ACCESS_READ AccessFlag = 2 /* Unused */
)
// SyncFlag is a flag for the [File.Sync] method.
// SyncFlag is a flag for the [File] Sync method.
//
// https://www.sqlite.org/c3ref/c_sync_dataonly.html
type SyncFlag uint32
@@ -93,7 +93,7 @@ const (
SYNC_DATAONLY SyncFlag = 0x00010
)
// LockLevel is a value used with [File.Lock] and [File.Unlock] methods.
// LockLevel is a value used with [File] Lock and Unlock methods.
//
// https://www.sqlite.org/c3ref/c_lock_exclusive.html
type LockLevel uint32
@@ -139,7 +139,7 @@ const (
LOCK_EXCLUSIVE LockLevel = 4 /* xLock() only */
)
// DeviceCharacteristic is a flag retuned by the [File.DeviceCharacteristic] method.
// DeviceCharacteristic is a flag retuned by the [File] DeviceCharacteristics method.
//
// https://www.sqlite.org/c3ref/c_iocap_atomic.html
type DeviceCharacteristic uint32

View File

@@ -13,29 +13,31 @@ import (
"github.com/tetratelabs/wazero/api"
)
// ExportHostFunctions registers the required VFS host functions
// with the provided env module.
func ExportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder {
util.RegisterFuncII(env, "go_vfs_find", vfsFind)
util.RegisterFuncIIJ(env, "go_localtime", vfsLocaltime)
util.RegisterFuncIIII(env, "go_randomness", vfsRandomness)
util.RegisterFuncIII(env, "go_sleep", vfsSleep)
util.RegisterFuncIII(env, "go_current_time", vfsCurrentTime)
util.RegisterFuncIII(env, "go_current_time_64", vfsCurrentTime64)
util.RegisterFuncIIIII(env, "go_full_pathname", vfsFullPathname)
util.RegisterFuncIIII(env, "go_delete", vfsDelete)
util.RegisterFuncIIIII(env, "go_access", vfsAccess)
util.RegisterFuncIIIIII(env, "go_open", vfsOpen)
util.RegisterFuncII(env, "go_close", vfsClose)
util.RegisterFuncIIIIJ(env, "go_read", vfsRead)
util.RegisterFuncIIIIJ(env, "go_write", vfsWrite)
util.RegisterFuncIIJ(env, "go_truncate", vfsTruncate)
util.RegisterFuncIII(env, "go_sync", vfsSync)
util.RegisterFuncIII(env, "go_file_size", vfsFileSize)
util.RegisterFuncIIII(env, "go_file_control", vfsFileControl)
util.RegisterFuncII(env, "go_sector_size", vfsSectorSize)
util.RegisterFuncII(env, "go_device_characteristics", vfsDeviceCharacteristics)
util.RegisterFuncIII(env, "go_lock", vfsLock)
util.RegisterFuncIII(env, "go_unlock", vfsUnlock)
util.RegisterFuncIII(env, "go_check_reserved_lock", vfsCheckReservedLock)
util.ExportFuncII(env, "go_vfs_find", vfsFind)
util.ExportFuncIIJ(env, "go_localtime", vfsLocaltime)
util.ExportFuncIIII(env, "go_randomness", vfsRandomness)
util.ExportFuncIII(env, "go_sleep", vfsSleep)
util.ExportFuncIII(env, "go_current_time", vfsCurrentTime)
util.ExportFuncIII(env, "go_current_time_64", vfsCurrentTime64)
util.ExportFuncIIIII(env, "go_full_pathname", vfsFullPathname)
util.ExportFuncIIII(env, "go_delete", vfsDelete)
util.ExportFuncIIIII(env, "go_access", vfsAccess)
util.ExportFuncIIIIII(env, "go_open", vfsOpen)
util.ExportFuncII(env, "go_close", vfsClose)
util.ExportFuncIIIIJ(env, "go_read", vfsRead)
util.ExportFuncIIIIJ(env, "go_write", vfsWrite)
util.ExportFuncIIJ(env, "go_truncate", vfsTruncate)
util.ExportFuncIII(env, "go_sync", vfsSync)
util.ExportFuncIII(env, "go_file_size", vfsFileSize)
util.ExportFuncIIII(env, "go_file_control", vfsFileControl)
util.ExportFuncII(env, "go_sector_size", vfsSectorSize)
util.ExportFuncII(env, "go_device_characteristics", vfsDeviceCharacteristics)
util.ExportFuncIII(env, "go_lock", vfsLock)
util.ExportFuncIII(env, "go_unlock", vfsUnlock)
util.ExportFuncIII(env, "go_check_reserved_lock", vfsCheckReservedLock)
return env
}
@@ -44,6 +46,13 @@ type vfsState struct {
files []File
}
// NewContext creates a new context to hold [api.Module] specific VFS data.
//
// This context should be passed to any [api.Function] calls that might
// generate VFS host callbacks.
//
// The returned [io.Closer] should be closed after the [api.Module] is closed,
// to release any associated resources.
func NewContext(ctx context.Context) (context.Context, io.Closer) {
vfs := &vfsState{}
return context.WithValue(ctx, vfsKey{}, vfs), vfs
@@ -113,7 +122,7 @@ func vfsCurrentTime64(ctx context.Context, mod api.Module, pVfs, piNow uint32) _
}
func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative, nFull, zFull uint32) _ErrorCode {
vfs := vfsAPIGet(mod, pVfs)
vfs := vfsGet(mod, pVfs)
path := util.ReadString(mod, zRelative, _MAX_PATHNAME)
path, err := vfs.FullPathname(path)
@@ -126,19 +135,19 @@ func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative, nFull
mem[len(path)] = 0
copy(mem, path)
return vfsAPIErrorCode(err, _CANTOPEN_FULLPATH)
return vfsErrorCode(err, _CANTOPEN_FULLPATH)
}
func vfsDelete(ctx context.Context, mod api.Module, pVfs, zPath, syncDir uint32) _ErrorCode {
vfs := vfsAPIGet(mod, pVfs)
vfs := vfsGet(mod, pVfs)
path := util.ReadString(mod, zPath, _MAX_PATHNAME)
err := vfs.Delete(path, syncDir != 0)
return vfsAPIErrorCode(err, _IOERR_DELETE)
return vfsErrorCode(err, _IOERR_DELETE)
}
func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath uint32, flags AccessFlag, pResOut uint32) _ErrorCode {
vfs := vfsAPIGet(mod, pVfs)
vfs := vfsGet(mod, pVfs)
path := util.ReadString(mod, zPath, _MAX_PATHNAME)
ok, err := vfs.Access(path, flags)
@@ -148,11 +157,11 @@ func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath uint32, flags Ac
}
util.WriteUint32(mod, pResOut, res)
return vfsAPIErrorCode(err, _IOERR_ACCESS)
return vfsErrorCode(err, _IOERR_ACCESS)
}
func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, flags OpenFlag, pOutFlags uint32) _ErrorCode {
vfs := vfsAPIGet(mod, pVfs)
vfs := vfsGet(mod, pVfs)
var path string
if zPath != 0 {
@@ -161,7 +170,7 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, fla
file, flags, err := vfs.Open(path, flags)
if err != nil {
return vfsAPIErrorCode(err, _CANTOPEN)
return vfsErrorCode(err, _CANTOPEN)
}
vfsFileRegister(ctx, mod, pFile, file)
@@ -210,32 +219,32 @@ func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf, iAmt uint32, iOf
func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte int64) _ErrorCode {
file := vfsFileGet(ctx, mod, pFile)
err := file.Truncate(nByte)
return vfsAPIErrorCode(err, _IOERR_TRUNCATE)
return vfsErrorCode(err, _IOERR_TRUNCATE)
}
func vfsSync(ctx context.Context, mod api.Module, pFile uint32, flags SyncFlag) _ErrorCode {
file := vfsFileGet(ctx, mod, pFile)
err := file.Sync(flags)
return vfsAPIErrorCode(err, _IOERR_FSYNC)
return vfsErrorCode(err, _IOERR_FSYNC)
}
func vfsFileSize(ctx context.Context, mod api.Module, pFile, pSize uint32) _ErrorCode {
file := vfsFileGet(ctx, mod, pFile)
size, err := file.FileSize()
util.WriteUint64(mod, pSize, uint64(size))
return vfsAPIErrorCode(err, _IOERR_SEEK)
return vfsErrorCode(err, _IOERR_SEEK)
}
func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode {
file := vfsFileGet(ctx, mod, pFile)
err := file.Lock(eLock)
return vfsAPIErrorCode(err, _IOERR_LOCK)
return vfsErrorCode(err, _IOERR_LOCK)
}
func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode {
file := vfsFileGet(ctx, mod, pFile)
err := file.Unlock(eLock)
return vfsAPIErrorCode(err, _IOERR_UNLOCK)
return vfsErrorCode(err, _IOERR_UNLOCK)
}
func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) _ErrorCode {
@@ -248,7 +257,7 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui
}
util.WriteUint32(mod, pResOut, res)
return vfsAPIErrorCode(err, _IOERR_CHECKRESERVEDLOCK)
return vfsErrorCode(err, _IOERR_CHECKRESERVEDLOCK)
}
func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _FcntlOpcode, pArg uint32) _ErrorCode {
@@ -290,7 +299,7 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl
if file, ok := file.(FileSizeHint); ok {
size := util.ReadUint64(mod, pArg)
err := file.SizeHint(int64(size))
return vfsAPIErrorCode(err, _IOERR_TRUNCATE)
return vfsErrorCode(err, _IOERR_TRUNCATE)
}
case _FCNTL_HAS_MOVED:
@@ -303,7 +312,7 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl
}
util.WriteUint32(mod, pArg, res)
return vfsAPIErrorCode(err, _IOERR_FSTAT)
return vfsErrorCode(err, _IOERR_FSTAT)
}
}
@@ -326,17 +335,22 @@ func vfsDeviceCharacteristics(ctx context.Context, mod api.Module, pFile uint32)
return file.DeviceCharacteristics()
}
func vfsAPIGet(mod api.Module, pVfs uint32) VFS {
if pVfs != 0 {
name := util.ReadString(mod, util.ReadUint32(mod, pVfs+16), _MAX_STRING)
if vfs := Find(name); vfs != nil {
return vfs
}
func vfsGet(mod api.Module, pVfs uint32) VFS {
if pVfs == 0 {
return vfsOS{}
}
return vfsOS{}
const zNameOffset = 16
name := util.ReadString(mod, util.ReadUint32(mod, pVfs+zNameOffset), _MAX_STRING)
if name == "os" {
return vfsOS{}
}
if vfs := Find(name); vfs != nil {
return vfs
}
panic(util.NoVFSErr + util.ErrorString(name))
}
func vfsAPIErrorCode(err error, def _ErrorCode) _ErrorCode {
func vfsErrorCode(err error, def _ErrorCode) _ErrorCode {
if err == nil {
return _OK
}

View File

@@ -1,7 +1,13 @@
// Package sqlite3vfs wraps the C SQLite VFS API.
package sqlite3vfs
import "sync"
import (
"sync"
)
// A VFS defines the interface between the SQLite core and the underlying operating system.
//
// https://www.sqlite.org/c3ref/vfs.html
type VFS interface {
Open(name string, flags OpenFlag) (File, OpenFlag, error)
Delete(name string, syncDir bool) error
@@ -9,6 +15,10 @@ type VFS interface {
FullPathname(name string) (string, error)
}
// A File represents an open file in the OS interface layer.
//
// https://www.sqlite.org/c3ref/file.html
// https://www.sqlite.org/c3ref/io_methods.html
type File interface {
Close() error
ReadAt(p []byte, off int64) (n int, err error)
@@ -23,21 +33,37 @@ type File interface {
DeviceCharacteristics() DeviceCharacteristic
}
// FileLockState extends [File] to implement the
// SQLITE_FCNTL_LOCKSTATE file control opcode.
//
// https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
type FileLockState interface {
File
LockState() LockLevel
}
// FileLockState extends [File] to implement the
// SQLITE_FCNTL_SIZE_HINT file control opcode.
//
// https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
type FileSizeHint interface {
File
SizeHint(size int64) error
}
// FileLockState extends [File] to implement the
// SQLITE_FCNTL_HAS_MOVED file control opcode.
//
// https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
type FileHasMoved interface {
File
HasMoved() (bool, error)
}
// FileLockState extends [File] to implement the
// SQLITE_FCNTL_POWERSAFE_OVERWRITE file control opcode.
//
// https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
type FilePowersafeOverwrite interface {
File
PowersafeOverwrite() bool
@@ -49,12 +75,19 @@ var (
vfsRegistryMtx sync.Mutex
)
// Find returns a VFS given its name.
// If there is no match, nil is returned.
//
// https://www.sqlite.org/c3ref/vfs_find.html
func Find(name string) VFS {
vfsRegistryMtx.Lock()
defer vfsRegistryMtx.Unlock()
return vfsRegistry[name]
}
// Register registers a VFS.
//
// https://www.sqlite.org/c3ref/vfs_find.html
func Register(name string, vfs VFS) {
vfsRegistryMtx.Lock()
defer vfsRegistryMtx.Unlock()
@@ -64,6 +97,9 @@ func Register(name string, vfs VFS) {
vfsRegistry[name] = vfs
}
// Unregister unregisters a VFS.
//
// https://www.sqlite.org/c3ref/vfs_find.html
func Unregister(name string) {
vfsRegistryMtx.Lock()
defer vfsRegistryMtx.Unlock()

View File

@@ -143,19 +143,22 @@ func vfsFileNew(vfs *vfsState, file File) uint32 {
}
func vfsFileRegister(ctx context.Context, mod api.Module, pFile uint32, file File) {
const fileHandleOffset = 4
id := vfsFileNew(ctx.Value(vfsKey{}).(*vfsState), file)
util.WriteUint32(mod, pFile+4, id)
util.WriteUint32(mod, pFile+fileHandleOffset, id)
}
func vfsFileGet(ctx context.Context, mod api.Module, pFile uint32) File {
const fileHandleOffset = 4
vfs := ctx.Value(vfsKey{}).(*vfsState)
id := util.ReadUint32(mod, pFile+4)
id := util.ReadUint32(mod, pFile+fileHandleOffset)
return vfs.files[id]
}
func vfsFileClose(ctx context.Context, mod api.Module, pFile uint32) error {
const fileHandleOffset = 4
vfs := ctx.Value(vfsKey{}).(*vfsState)
id := util.ReadUint32(mod, pFile+4)
id := util.ReadUint32(mod, pFile+fileHandleOffset)
file := vfs.files[id]
vfs.files[id] = nil
return file.Close()

View File

@@ -47,7 +47,7 @@ func osAllocate(file *os.File, size int64) error {
Length: size,
}
// Try to get a continous chunk of disk space.
// Try to get a continuous chunk of disk space.
err = unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store)
if err != nil {
// OK, perhaps we are too fragmented, allocate non-continuous.