Fork of ncruces/go-sqlite3 (MIT, retained). Renames module to github.com/hanzoai/sqlite3 (wasm blob stays upstream ncruces/go-sqlite3-wasm) and adds the hanzovfs package: a pure-Go, no-FUSE SQLite VFS that seals pages with ChaCha20-Poly1305 (per-tenant DEK, HIP-0302) for per-tenant SQLite ⇒ hanzoai/vfs ⇒ S3. Deprecates modernc.org/sqlite (no custom-VFS hook) and direct ncruces use. Benchmarked 3.6x faster writes / ~92x faster point-reads than FUSE; encryption ~free. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
219 lines
6.5 KiB
Go
219 lines
6.5 KiB
Go
// Package sqlite3 wraps the C SQLite API.
|
|
package sqlite3
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"math/bits"
|
|
"time"
|
|
_ "unsafe"
|
|
|
|
sqlite3_wasm "github.com/ncruces/go-sqlite3-wasm/v3"
|
|
"github.com/hanzoai/sqlite3/internal/errutil"
|
|
"github.com/hanzoai/sqlite3/internal/sqlite3_wrap"
|
|
"github.com/hanzoai/sqlite3/vfs"
|
|
"github.com/ncruces/julianday"
|
|
)
|
|
|
|
type configKey struct{}
|
|
|
|
// WithMaxMemory returns a derived context that configures
|
|
// each SQLite connection not to use more than max amount of memory.
|
|
func WithMaxMemory(ctx context.Context, max int64) context.Context {
|
|
if max < 0 || max > 65536*65536 {
|
|
panic(errutil.OOMErr)
|
|
}
|
|
return context.WithValue(ctx, configKey{}, max/65536)
|
|
}
|
|
|
|
var _ sqlite3_wasm.Xenv = &env{}
|
|
|
|
type env struct{ *sqlite3_wrap.Wrapper }
|
|
|
|
func createWrapper(ctx context.Context) (*sqlite3_wrap.Wrapper, error) {
|
|
mem := &sqlite3_wrap.Memory{Max: 4096} // 256MB
|
|
if bits.UintSize < 64 {
|
|
mem.Max = 512 // 32MB
|
|
}
|
|
mem.Grow(5, mem.Max) // 320KB
|
|
|
|
env := &env{&sqlite3_wrap.Wrapper{Memory: mem}}
|
|
env.Module = sqlite3_wasm.New(env)
|
|
env.X_initialize()
|
|
|
|
if cfg, ok := ctx.Value(configKey{}).(int64); ok {
|
|
mem.Max = max(cfg, int64(len(mem.Buf))/65536)
|
|
env.Xsqlite3_soft_heap_limit64((mem.Max - 8) * 65536)
|
|
}
|
|
return env.Wrapper, nil
|
|
}
|
|
|
|
func (e *env) Xmemory() sqlite3_wasm.Memory { return e.Memory }
|
|
|
|
// VFS functions.
|
|
|
|
func (e *env) Xgo_randomness(pVfs, nByte, zByte int32) int32 {
|
|
mem := e.Bytes(ptr_t(zByte), int64(nByte))
|
|
n, _ := rand.Reader.Read(mem)
|
|
return int32(n)
|
|
}
|
|
|
|
func (e *env) Xgo_sleep(pVfs, nMicro int32) int32 {
|
|
time.Sleep(time.Duration(nMicro) * time.Microsecond)
|
|
return _OK
|
|
}
|
|
|
|
func (e *env) Xgo_current_time_64(pVfs, nMicro int32) int32 {
|
|
day, nsec := julianday.Date(time.Now())
|
|
msec := day*86_400_000 + nsec/1_000_000
|
|
e.Write64(ptr_t(nMicro), uint64(msec))
|
|
return int32(_OK)
|
|
}
|
|
|
|
func (e *env) Xgo_vfs_find(zVfsName int32) int32 {
|
|
if vfs.Find(e.ReadString(ptr_t(zVfsName), _MAX_NAME)) != nil {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
//go:linkname vfsFullPathname github.com/hanzoai/sqlite3/vfs.vfsFullPathname
|
|
func vfsFullPathname(_ *sqlite3_wrap.Wrapper, v0, v1, v2, v3 int32) int32
|
|
|
|
func (e *env) Xgo_full_pathname(v0, v1, v2, v3 int32) int32 {
|
|
return vfsFullPathname(e.Wrapper, v0, v1, v2, v3)
|
|
}
|
|
|
|
//go:linkname vfsDelete github.com/hanzoai/sqlite3/vfs.vfsDelete
|
|
func vfsDelete(_ *sqlite3_wrap.Wrapper, v0, v1, v2 int32) int32
|
|
|
|
func (e *env) Xgo_delete(v0, v1, v2 int32) int32 {
|
|
return vfsDelete(e.Wrapper, v0, v1, v2)
|
|
}
|
|
|
|
//go:linkname vfsAccess github.com/hanzoai/sqlite3/vfs.vfsAccess
|
|
func vfsAccess(_ *sqlite3_wrap.Wrapper, v0, v1, v2, v3 int32) int32
|
|
|
|
func (e *env) Xgo_access(v0, v1, v2, v3 int32) int32 {
|
|
return vfsAccess(e.Wrapper, v0, v1, v2, v3)
|
|
}
|
|
|
|
//go:linkname vfsOpen github.com/hanzoai/sqlite3/vfs.vfsOpen
|
|
func vfsOpen(_ *sqlite3_wrap.Wrapper, v0, v1, v2, v3, v4, v5 int32) int32
|
|
|
|
func (e *env) Xgo_open(v0, v1, v2, v3, v4, v5 int32) int32 {
|
|
return vfsOpen(e.Wrapper, v0, v1, v2, v3, v4, v5)
|
|
}
|
|
|
|
//go:linkname vfsClose github.com/hanzoai/sqlite3/vfs.vfsClose
|
|
func vfsClose(_ *sqlite3_wrap.Wrapper, v0 int32) int32
|
|
|
|
func (e *env) Xgo_close(v0 int32) int32 {
|
|
return vfsClose(e.Wrapper, v0)
|
|
}
|
|
|
|
//go:linkname vfsRead github.com/hanzoai/sqlite3/vfs.vfsRead
|
|
func vfsRead(_ *sqlite3_wrap.Wrapper, v0, v1, v2 int32, v3 int64) int32
|
|
|
|
func (e *env) Xgo_read(v0, v1, v2 int32, v3 int64) int32 {
|
|
return vfsRead(e.Wrapper, v0, v1, v2, v3)
|
|
}
|
|
|
|
//go:linkname vfsWrite github.com/hanzoai/sqlite3/vfs.vfsWrite
|
|
func vfsWrite(_ *sqlite3_wrap.Wrapper, v0, v1, v2 int32, v3 int64) int32
|
|
|
|
func (e *env) Xgo_write(v0, v1, v2 int32, v3 int64) int32 {
|
|
return vfsWrite(e.Wrapper, v0, v1, v2, v3)
|
|
}
|
|
|
|
//go:linkname vfsTruncate github.com/hanzoai/sqlite3/vfs.vfsTruncate
|
|
func vfsTruncate(_ *sqlite3_wrap.Wrapper, v0 int32, v1 int64) int32
|
|
|
|
func (e *env) Xgo_truncate(v0 int32, v1 int64) int32 {
|
|
return vfsTruncate(e.Wrapper, v0, v1)
|
|
}
|
|
|
|
//go:linkname vfsSync github.com/hanzoai/sqlite3/vfs.vfsSync
|
|
func vfsSync(_ *sqlite3_wrap.Wrapper, v0, v1 int32) int32
|
|
|
|
func (e *env) Xgo_sync(v0, v1 int32) int32 {
|
|
return vfsSync(e.Wrapper, v0, v1)
|
|
}
|
|
|
|
//go:linkname vfsFileSize github.com/hanzoai/sqlite3/vfs.vfsFileSize
|
|
func vfsFileSize(_ *sqlite3_wrap.Wrapper, v0, v1 int32) int32
|
|
|
|
func (e *env) Xgo_file_size(v0, v1 int32) int32 {
|
|
return vfsFileSize(e.Wrapper, v0, v1)
|
|
}
|
|
|
|
//go:linkname vfsLock github.com/hanzoai/sqlite3/vfs.vfsLock
|
|
func vfsLock(_ *sqlite3_wrap.Wrapper, v0, v1 int32) int32
|
|
|
|
func (e *env) Xgo_lock(v0, v1 int32) int32 {
|
|
return vfsLock(e.Wrapper, v0, v1)
|
|
}
|
|
|
|
//go:linkname vfsUnlock github.com/hanzoai/sqlite3/vfs.vfsUnlock
|
|
func vfsUnlock(_ *sqlite3_wrap.Wrapper, v0, v1 int32) int32
|
|
|
|
func (e *env) Xgo_unlock(v0, v1 int32) int32 {
|
|
return vfsUnlock(e.Wrapper, v0, v1)
|
|
}
|
|
|
|
//go:linkname vfsCheckReservedLock github.com/hanzoai/sqlite3/vfs.vfsCheckReservedLock
|
|
func vfsCheckReservedLock(_ *sqlite3_wrap.Wrapper, v0, v1 int32) int32
|
|
|
|
func (e *env) Xgo_check_reserved_lock(v0, v1 int32) int32 {
|
|
return vfsCheckReservedLock(e.Wrapper, v0, v1)
|
|
}
|
|
|
|
//go:linkname vfsFileControl github.com/hanzoai/sqlite3/vfs.vfsFileControl
|
|
func vfsFileControl(_ *sqlite3_wrap.Wrapper, v0, v1, v2 int32) int32
|
|
|
|
func (e *env) Xgo_file_control(v0, v1, v2 int32) int32 {
|
|
return vfsFileControl(e.Wrapper, v0, v1, v2)
|
|
}
|
|
|
|
//go:linkname vfsSectorSize github.com/hanzoai/sqlite3/vfs.vfsSectorSize
|
|
func vfsSectorSize(_ *sqlite3_wrap.Wrapper, v0 int32) int32
|
|
|
|
func (e *env) Xgo_sector_size(v0 int32) int32 {
|
|
return vfsSectorSize(e.Wrapper, v0)
|
|
}
|
|
|
|
//go:linkname vfsDeviceCharacteristics github.com/hanzoai/sqlite3/vfs.vfsDeviceCharacteristics
|
|
func vfsDeviceCharacteristics(_ *sqlite3_wrap.Wrapper, v0 int32) int32
|
|
|
|
func (e *env) Xgo_device_characteristics(v0 int32) int32 {
|
|
return vfsDeviceCharacteristics(e.Wrapper, v0)
|
|
}
|
|
|
|
//go:linkname vfsShmBarrier github.com/hanzoai/sqlite3/vfs.vfsShmBarrier
|
|
func vfsShmBarrier(_ *sqlite3_wrap.Wrapper, v0 int32)
|
|
|
|
func (e *env) Xgo_shm_barrier(v0 int32) {
|
|
vfsShmBarrier(e.Wrapper, v0)
|
|
}
|
|
|
|
//go:linkname vfsShmMap github.com/hanzoai/sqlite3/vfs.vfsShmMap
|
|
func vfsShmMap(_ *sqlite3_wrap.Wrapper, v0, v1, v2, v3, v4 int32) int32
|
|
|
|
func (e *env) Xgo_shm_map(v0, v1, v2, v3, v4 int32) int32 {
|
|
return vfsShmMap(e.Wrapper, v0, v1, v2, v3, v4)
|
|
}
|
|
|
|
//go:linkname vfsShmLock github.com/hanzoai/sqlite3/vfs.vfsShmLock
|
|
func vfsShmLock(_ *sqlite3_wrap.Wrapper, v0, v1, v2, v3 int32) int32
|
|
|
|
func (e *env) Xgo_shm_lock(v0, v1, v2, v3 int32) int32 {
|
|
return vfsShmLock(e.Wrapper, v0, v1, v2, v3)
|
|
}
|
|
|
|
//go:linkname vfsShmUnmap github.com/hanzoai/sqlite3/vfs.vfsShmUnmap
|
|
func vfsShmUnmap(_ *sqlite3_wrap.Wrapper, v0, v1 int32) int32
|
|
|
|
func (e *env) Xgo_shm_unmap(v0, v1 int32) int32 {
|
|
return vfsShmUnmap(e.Wrapper, v0, v1)
|
|
}
|