Files
sqlite3/sqlite.go

350 lines
8.6 KiB
Go
Raw Permalink Normal View History

2023-03-06 18:28:50 +00:00
// Package sqlite3 wraps the C SQLite API.
2023-01-12 13:43:35 +00:00
package sqlite3
import (
"context"
"math/bits"
2023-01-12 13:43:35 +00:00
"os"
"sync"
"unsafe"
2023-01-12 13:43:35 +00:00
"github.com/tetratelabs/wazero"
2023-01-28 12:47:39 +00:00
"github.com/tetratelabs/wazero/api"
2024-10-18 12:20:32 +01:00
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/vfs"
2023-01-12 13:43:35 +00:00
)
2024-03-25 15:14:24 +00:00
// Configure SQLite Wasm.
2023-03-01 10:34:08 +00:00
//
2024-03-25 15:14:24 +00:00
// Importing package embed initializes [Binary]
2023-03-01 10:34:08 +00:00
// with an appropriate build of SQLite:
//
// import _ "github.com/ncruces/go-sqlite3/embed"
2023-01-12 13:43:35 +00:00
var (
2024-03-25 15:14:24 +00:00
Binary []byte // Wasm binary to load.
2023-01-12 13:43:35 +00:00
Path string // Path to load the binary from.
2023-10-18 12:45:49 +01:00
RuntimeConfig wazero.RuntimeConfig
2023-01-12 13:43:35 +00:00
)
2024-05-03 12:38:40 +01:00
// Initialize decodes and compiles the SQLite Wasm binary.
// This is called implicitly when the first connection is openned,
// but is potentially slow, so you may want to call it at a more convenient time.
func Initialize() error {
instance.once.Do(compileSQLite)
return instance.err
}
2023-07-03 17:21:35 +01:00
var instance struct {
2023-03-24 21:17:30 +00:00
runtime wazero.Runtime
compiled wazero.CompiledModule
err error
2023-04-11 15:33:38 +01:00
once sync.Once
2023-01-28 12:47:39 +00:00
}
2023-01-12 13:43:35 +00:00
2023-07-03 17:21:35 +01:00
func compileSQLite() {
ctx := context.Background()
cfg := RuntimeConfig
if cfg == nil {
2025-02-18 16:36:22 +00:00
cfg = wazero.NewRuntimeConfig()
2025-01-06 18:22:36 +00:00
if bits.UintSize < 64 {
2024-10-17 13:04:23 +01:00
cfg = cfg.WithMemoryLimitPages(512) // 32MB
2025-01-06 18:22:36 +00:00
} else {
cfg = cfg.WithMemoryLimitPages(4096) // 256MB
2024-10-17 13:04:23 +01:00
}
2025-01-16 17:21:36 +00:00
cfg = cfg.WithCoreFeatures(api.CoreFeaturesV2)
2023-10-18 12:45:49 +01:00
}
2024-10-17 13:04:23 +01:00
instance.runtime = wazero.NewRuntimeWithConfig(ctx, cfg)
2023-04-22 00:15:44 +01:00
2023-07-03 17:21:35 +01:00
env := instance.runtime.NewHostModuleBuilder("env")
2023-06-28 17:32:55 +01:00
env = vfs.ExportHostFunctions(env)
2023-10-25 12:56:52 +01:00
env = exportCallbacks(env)
2023-07-03 17:21:35 +01:00
_, instance.err = env.Instantiate(ctx)
if instance.err != nil {
2023-04-22 00:15:44 +01:00
return
}
2023-01-12 13:43:35 +00:00
2023-01-28 12:47:39 +00:00
bin := Binary
if bin == nil && Path != "" {
2023-07-03 17:21:35 +01:00
bin, instance.err = os.ReadFile(Path)
if instance.err != nil {
2023-01-28 12:47:39 +00:00
return
2023-01-12 13:43:35 +00:00
}
}
2023-02-20 13:38:03 +00:00
if bin == nil {
2023-11-30 17:52:35 +00:00
instance.err = util.NoBinaryErr
2023-02-20 13:38:03 +00:00
return
}
2023-01-12 13:43:35 +00:00
2023-07-03 17:21:35 +01:00
instance.compiled, instance.err = instance.runtime.CompileModule(ctx, bin)
2023-01-12 13:43:35 +00:00
}
2023-03-06 12:22:17 +00:00
2023-07-03 17:21:35 +01:00
type sqlite struct {
2023-07-11 12:30:09 +01:00
ctx context.Context
mod api.Module
funcs struct {
fn [32]api.Function
id [32]*byte
mask uint32
}
2025-01-21 01:42:57 +00:00
stack [9]stk_t
2023-03-06 12:22:17 +00:00
}
2023-03-06 17:51:25 +00:00
2023-07-11 12:30:09 +01:00
func instantiateSQLite() (sqlt *sqlite, err error) {
2024-05-03 12:38:40 +01:00
if err := Initialize(); err != nil {
return nil, err
2023-07-11 12:30:09 +01:00
}
2023-07-03 17:21:35 +01:00
sqlt = new(sqlite)
2024-04-26 16:25:45 +01:00
sqlt.ctx = util.NewContext(context.Background())
2023-07-11 12:30:09 +01:00
sqlt.mod, err = instance.runtime.InstantiateModule(sqlt.ctx,
2024-04-16 01:06:38 +01:00
instance.compiled, wazero.NewModuleConfig().WithName(""))
2023-07-11 12:30:09 +01:00
if err != nil {
return nil, err
}
2024-09-09 13:21:33 +01:00
if sqlt.getfn("sqlite3_progress_handler_go") == nil {
return nil, util.BadBinaryErr
2023-03-06 18:28:50 +00:00
}
2023-07-03 17:21:35 +01:00
return sqlt, nil
2023-03-06 18:28:50 +00:00
}
2023-07-03 17:21:35 +01:00
func (sqlt *sqlite) close() error {
2023-07-11 12:30:09 +01:00
return sqlt.mod.Close(sqlt.ctx)
2023-03-06 23:41:54 +00:00
}
2025-01-21 01:42:57 +00:00
func (sqlt *sqlite) error(rc res_t, handle ptr_t, sql ...string) error {
2023-03-06 17:51:25 +00:00
if rc == _OK {
return nil
}
err := Error{code: rc}
if err.Code() == NOMEM || err.ExtendedCode() == IOERR_NOMEM {
2023-03-29 15:01:25 +01:00
panic(util.OOMErr)
2023-03-06 17:51:25 +00:00
}
2025-01-21 01:42:57 +00:00
if ptr := ptr_t(sqlt.call("sqlite3_errstr", stk_t(rc))); ptr != 0 {
err.str = util.ReadString(sqlt.mod, ptr, _MAX_NAME)
2023-03-06 17:51:25 +00:00
}
2023-10-13 17:06:05 +01:00
if handle != 0 {
2025-01-21 01:42:57 +00:00
if ptr := ptr_t(sqlt.call("sqlite3_errmsg", stk_t(handle))); ptr != 0 {
err.msg = util.ReadString(sqlt.mod, ptr, _MAX_LENGTH)
2023-10-13 17:06:05 +01:00
}
2023-03-06 17:51:25 +00:00
2024-09-28 10:37:47 +01:00
if len(sql) != 0 {
2025-01-21 01:42:57 +00:00
if i := int32(sqlt.call("sqlite3_error_offset", stk_t(handle))); i != -1 {
err.sql = sql[0][i:]
2023-10-13 17:06:05 +01:00
}
2023-03-06 17:51:25 +00:00
}
}
switch err.msg {
case err.str, "not an error":
err.msg = ""
}
return &err
}
2023-03-06 18:28:50 +00:00
func (sqlt *sqlite) getfn(name string) api.Function {
c := &sqlt.funcs
p := unsafe.StringData(name)
2023-12-15 00:42:12 +00:00
for i := range c.id {
if c.id[i] == p {
c.id[i] = nil
c.mask &^= uint32(1) << i
2023-12-15 00:42:12 +00:00
return c.fn[i]
2023-12-01 01:36:48 +00:00
}
}
return sqlt.mod.ExportedFunction(name)
}
2023-12-01 01:36:48 +00:00
func (sqlt *sqlite) putfn(name string, fn api.Function) {
c := &sqlt.funcs
p := unsafe.StringData(name)
i := bits.TrailingZeros32(^c.mask)
if i < 32 {
c.id[i] = p
c.fn[i] = fn
c.mask |= uint32(1) << i
2023-12-01 01:36:48 +00:00
} else {
c.id[0] = p
c.fn[0] = fn
c.mask = uint32(1)
2023-12-01 01:36:48 +00:00
}
}
2025-01-21 01:42:57 +00:00
func (sqlt *sqlite) call(name string, params ...stk_t) stk_t {
2023-07-03 17:21:35 +01:00
copy(sqlt.stack[:], params)
fn := sqlt.getfn(name)
2023-07-03 17:21:35 +01:00
err := fn.CallWithStack(sqlt.ctx, sqlt.stack[:])
2023-03-06 19:03:00 +00:00
if err != nil {
panic(err)
}
sqlt.putfn(name, fn)
2025-01-21 01:42:57 +00:00
return stk_t(sqlt.stack[0])
2023-03-06 19:03:00 +00:00
}
2025-01-21 01:42:57 +00:00
func (sqlt *sqlite) free(ptr ptr_t) {
2023-03-06 19:03:00 +00:00
if ptr == 0 {
return
}
2025-01-21 01:42:57 +00:00
sqlt.call("sqlite3_free", stk_t(ptr))
2023-03-06 19:03:00 +00:00
}
2025-01-21 01:42:57 +00:00
func (sqlt *sqlite) new(size int64) ptr_t {
ptr := ptr_t(sqlt.call("sqlite3_malloc64", stk_t(size)))
2024-09-10 07:41:35 +01:00
if ptr == 0 && size != 0 {
panic(util.OOMErr)
}
return ptr
}
2025-01-21 01:42:57 +00:00
func (sqlt *sqlite) realloc(ptr ptr_t, size int64) ptr_t {
ptr = ptr_t(sqlt.call("sqlite3_realloc64", stk_t(ptr), stk_t(size)))
2024-09-10 07:41:35 +01:00
if ptr == 0 && size != 0 {
2023-03-29 15:01:25 +01:00
panic(util.OOMErr)
2023-03-06 19:03:00 +00:00
}
return ptr
}
2025-01-21 01:42:57 +00:00
func (sqlt *sqlite) newBytes(b []byte) ptr_t {
2023-10-13 17:06:05 +01:00
if (*[0]byte)(b) == nil {
2023-03-06 19:03:00 +00:00
return 0
}
2024-09-10 07:41:35 +01:00
size := len(b)
if size == 0 {
size = 1
}
2025-01-21 01:42:57 +00:00
ptr := sqlt.new(int64(size))
2023-07-03 17:21:35 +01:00
util.WriteBytes(sqlt.mod, ptr, b)
2023-03-06 19:03:00 +00:00
return ptr
}
2025-01-21 01:42:57 +00:00
func (sqlt *sqlite) newString(s string) ptr_t {
ptr := sqlt.new(int64(len(s)) + 1)
2023-07-03 17:21:35 +01:00
util.WriteString(sqlt.mod, ptr, s)
2023-03-06 19:03:00 +00:00
return ptr
}
2025-01-21 01:42:57 +00:00
const arenaSize = 4096
func (sqlt *sqlite) newArena() arena {
2023-03-06 19:03:00 +00:00
return arena{
2023-07-03 17:21:35 +01:00
sqlt: sqlt,
2025-01-21 01:42:57 +00:00
base: sqlt.new(arenaSize),
2023-03-06 19:03:00 +00:00
}
}
type arena struct {
2023-07-03 17:21:35 +01:00
sqlt *sqlite
2025-01-21 01:42:57 +00:00
ptrs []ptr_t
base ptr_t
next int32
2023-03-06 19:03:00 +00:00
}
func (a *arena) free() {
2023-07-03 17:21:35 +01:00
if a.sqlt == nil {
2023-03-06 19:03:00 +00:00
return
}
2023-11-29 10:38:03 +00:00
for _, ptr := range a.ptrs {
a.sqlt.free(ptr)
}
2023-07-03 17:21:35 +01:00
a.sqlt.free(a.base)
a.sqlt = nil
2023-03-06 19:03:00 +00:00
}
2023-11-29 10:38:03 +00:00
func (a *arena) mark() (reset func()) {
ptrs := len(a.ptrs)
next := a.next
return func() {
2024-12-19 14:00:46 +00:00
rest := a.ptrs[ptrs:]
for _, ptr := range a.ptrs[:ptrs] {
2023-11-29 10:38:03 +00:00
a.sqlt.free(ptr)
}
2024-12-19 14:00:46 +00:00
a.ptrs = rest
2023-11-29 10:38:03 +00:00
a.next = next
2023-03-06 19:03:00 +00:00
}
}
2025-01-21 01:42:57 +00:00
func (a *arena) new(size int64) ptr_t {
2024-01-13 21:24:02 +00:00
// Align the next address, to 4 or 8 bytes.
if size&7 != 0 {
a.next = (a.next + 3) &^ 3
} else {
a.next = (a.next + 7) &^ 7
}
2025-01-21 01:42:57 +00:00
if size <= arenaSize-int64(a.next) {
ptr := a.base + ptr_t(a.next)
a.next += int32(size)
return ptr_t(ptr)
2023-03-06 19:03:00 +00:00
}
2023-07-03 17:21:35 +01:00
ptr := a.sqlt.new(size)
2023-03-06 19:03:00 +00:00
a.ptrs = append(a.ptrs, ptr)
2025-01-21 01:42:57 +00:00
return ptr_t(ptr)
2023-03-06 19:03:00 +00:00
}
2025-01-21 01:42:57 +00:00
func (a *arena) bytes(b []byte) ptr_t {
2024-03-25 15:14:24 +00:00
if (*[0]byte)(b) == nil {
2023-04-21 13:31:45 +01:00
return 0
}
2025-01-21 01:42:57 +00:00
ptr := a.new(int64(len(b)))
2023-07-03 17:21:35 +01:00
util.WriteBytes(a.sqlt.mod, ptr, b)
2023-04-21 13:31:45 +01:00
return ptr
}
2025-01-21 01:42:57 +00:00
func (a *arena) string(s string) ptr_t {
ptr := a.new(int64(len(s)) + 1)
2023-07-03 17:21:35 +01:00
util.WriteString(a.sqlt.mod, ptr, s)
2023-03-06 19:03:00 +00:00
return ptr
}
2023-10-25 12:56:52 +01:00
func exportCallbacks(env wazero.HostModuleBuilder) wazero.HostModuleBuilder {
2024-02-02 23:41:34 +00:00
util.ExportFuncII(env, "go_progress_handler", progressCallback)
2024-10-04 13:31:53 +01:00
util.ExportFuncIII(env, "go_busy_timeout", timeoutCallback)
2024-05-19 01:04:56 +01:00
util.ExportFuncIII(env, "go_busy_handler", busyCallback)
2024-01-26 15:41:36 +00:00
util.ExportFuncII(env, "go_commit_hook", commitCallback)
util.ExportFuncVI(env, "go_rollback_hook", rollbackCallback)
util.ExportFuncVIIIIJ(env, "go_update_hook", updateCallback)
2024-04-12 14:57:13 +01:00
util.ExportFuncIIIII(env, "go_wal_hook", walCallback)
util.ExportFuncIIIII(env, "go_trace", traceCallback)
2024-04-12 14:57:13 +01:00
util.ExportFuncIIIIII(env, "go_autovacuum_pages", autoVacuumCallback)
2024-01-26 15:41:36 +00:00
util.ExportFuncIIIIIII(env, "go_authorizer", authorizerCallback)
2023-12-27 14:06:44 +00:00
util.ExportFuncVIII(env, "go_log", logCallback)
2023-11-16 01:16:38 +00:00
util.ExportFuncVI(env, "go_destroy", destroyCallback)
2024-01-11 02:18:12 +00:00
util.ExportFuncVIIII(env, "go_func", funcCallback)
util.ExportFuncVIIIII(env, "go_step", stepCallback)
util.ExportFuncVIII(env, "go_final", finalCallback)
util.ExportFuncVII(env, "go_value", valueCallback)
util.ExportFuncVIIII(env, "go_inverse", inverseCallback)
2024-01-26 15:41:36 +00:00
util.ExportFuncVIIII(env, "go_collation_needed", collationCallback)
2023-11-16 01:16:38 +00:00
util.ExportFuncIIIIII(env, "go_compare", compareCallback)
2024-01-26 15:41:36 +00:00
util.ExportFuncIIIIII(env, "go_vtab_create", vtabModuleCallback(xCreate))
util.ExportFuncIIIIII(env, "go_vtab_connect", vtabModuleCallback(xConnect))
2023-11-16 01:16:38 +00:00
util.ExportFuncII(env, "go_vtab_disconnect", vtabDisconnectCallback)
2023-11-17 19:06:10 +00:00
util.ExportFuncII(env, "go_vtab_destroy", vtabDestroyCallback)
2023-11-16 01:16:38 +00:00
util.ExportFuncIII(env, "go_vtab_best_index", vtabBestIndexCallback)
2023-11-18 15:43:39 +00:00
util.ExportFuncIIIII(env, "go_vtab_update", vtabUpdateCallback)
util.ExportFuncIII(env, "go_vtab_rename", vtabRenameCallback)
util.ExportFuncIIIII(env, "go_vtab_find_function", vtabFindFuncCallback)
util.ExportFuncII(env, "go_vtab_begin", vtabBeginCallback)
util.ExportFuncII(env, "go_vtab_sync", vtabSyncCallback)
util.ExportFuncII(env, "go_vtab_commit", vtabCommitCallback)
util.ExportFuncII(env, "go_vtab_rollback", vtabRollbackCallback)
util.ExportFuncIII(env, "go_vtab_savepoint", vtabSavepointCallback)
util.ExportFuncIII(env, "go_vtab_release", vtabReleaseCallback)
util.ExportFuncIII(env, "go_vtab_rollback_to", vtabRollbackToCallback)
2023-11-16 01:16:38 +00:00
util.ExportFuncIIIIII(env, "go_vtab_integrity", vtabIntegrityCallback)
util.ExportFuncIII(env, "go_cur_open", cursorOpenCallback)
2023-11-17 22:39:00 +00:00
util.ExportFuncII(env, "go_cur_close", cursorCloseCallback)
2023-11-16 01:16:38 +00:00
util.ExportFuncIIIIII(env, "go_cur_filter", cursorFilterCallback)
2023-11-17 22:39:00 +00:00
util.ExportFuncII(env, "go_cur_next", cursorNextCallback)
util.ExportFuncII(env, "go_cur_eof", cursorEOFCallback)
2023-11-16 01:16:38 +00:00
util.ExportFuncIIII(env, "go_cur_column", cursorColumnCallback)
2023-11-17 22:39:00 +00:00
util.ExportFuncIII(env, "go_cur_rowid", cursorRowIDCallback)
2023-10-25 12:56:52 +01:00
return env
}