mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 14:09:13 +00:00
Documentation.
This commit is contained in:
@@ -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