mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
Documentation.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
6
conn.go
6
conn.go
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user