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"
|
2023-03-06 17:51:25 +00:00
|
|
|
"math"
|
2023-12-14 11:28:06 +00:00
|
|
|
"math/bits"
|
2023-01-12 13:43:35 +00:00
|
|
|
"os"
|
|
|
|
|
"sync"
|
|
|
|
|
|
2023-03-29 15:01:25 +01:00
|
|
|
"github.com/ncruces/go-sqlite3/internal/util"
|
2023-06-01 18:11:37 +01:00
|
|
|
"github.com/ncruces/go-sqlite3/vfs"
|
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"
|
2023-01-12 13:43:35 +00:00
|
|
|
)
|
|
|
|
|
|
2023-03-01 10:34:08 +00:00
|
|
|
// Configure SQLite WASM.
|
|
|
|
|
//
|
|
|
|
|
// Importing package embed initializes these
|
|
|
|
|
// with an appropriate build of SQLite:
|
|
|
|
|
//
|
|
|
|
|
// import _ "github.com/ncruces/go-sqlite3/embed"
|
2023-01-12 13:43:35 +00:00
|
|
|
var (
|
2023-03-01 10:34:08 +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
|
|
|
)
|
|
|
|
|
|
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() {
|
2023-10-18 12:45:49 +01:00
|
|
|
if RuntimeConfig == nil {
|
|
|
|
|
RuntimeConfig = wazero.NewRuntimeConfig()
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 18:28:50 +00:00
|
|
|
ctx := context.Background()
|
2023-10-18 12:45:49 +01:00
|
|
|
instance.runtime = wazero.NewRuntimeWithConfig(ctx, RuntimeConfig)
|
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
|
2023-12-14 11:28:06 +00:00
|
|
|
funcs struct {
|
|
|
|
|
fn [32]api.Function
|
|
|
|
|
id [32]string
|
|
|
|
|
mask uint32
|
|
|
|
|
}
|
2023-07-11 12:30:09 +01:00
|
|
|
stack [8]uint64
|
2023-12-01 01:36:48 +00:00
|
|
|
freer uint32
|
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) {
|
|
|
|
|
instance.once.Do(compileSQLite)
|
|
|
|
|
if instance.err != nil {
|
|
|
|
|
return nil, instance.err
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-03 17:21:35 +01:00
|
|
|
sqlt = new(sqlite)
|
2023-07-11 12:30:09 +01:00
|
|
|
sqlt.ctx = util.NewContext(context.Background())
|
|
|
|
|
|
|
|
|
|
sqlt.mod, err = instance.runtime.InstantiateModule(sqlt.ctx,
|
|
|
|
|
instance.compiled, wazero.NewModuleConfig())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2023-03-06 23:41:54 +00:00
|
|
|
|
2023-11-30 17:52:35 +00:00
|
|
|
global := sqlt.mod.ExportedGlobal("malloc_destructor")
|
|
|
|
|
if global == nil {
|
|
|
|
|
return nil, util.BadBinaryErr
|
2023-03-06 18:28:50 +00:00
|
|
|
}
|
|
|
|
|
|
2023-11-30 17:52:35 +00:00
|
|
|
sqlt.freer = util.ReadUint32(sqlt.mod, uint32(global.Get()))
|
2023-03-06 18:28:50 +00:00
|
|
|
if err != nil {
|
2023-03-07 03:51:07 +00:00
|
|
|
return nil, err
|
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
|
|
|
}
|
|
|
|
|
|
2023-07-03 17:21:35 +01:00
|
|
|
func (sqlt *sqlite) error(rc uint64, handle uint32, 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
|
|
|
}
|
|
|
|
|
|
2023-11-30 17:52:35 +00:00
|
|
|
if r := sqlt.call("sqlite3_errstr", rc); r != 0 {
|
2023-11-24 17:25:02 +00:00
|
|
|
err.str = util.ReadString(sqlt.mod, uint32(r), _MAX_NAME)
|
2023-03-06 17:51:25 +00:00
|
|
|
}
|
|
|
|
|
|
2023-10-13 17:06:05 +01:00
|
|
|
if handle != 0 {
|
2023-11-30 17:52:35 +00:00
|
|
|
if r := sqlt.call("sqlite3_errmsg", uint64(handle)); r != 0 {
|
2023-11-24 17:25:02 +00:00
|
|
|
err.msg = util.ReadString(sqlt.mod, uint32(r), _MAX_NAME)
|
2023-10-13 17:06:05 +01:00
|
|
|
}
|
2023-03-06 17:51:25 +00:00
|
|
|
|
2023-10-13 17:06:05 +01:00
|
|
|
if sql != nil {
|
2023-11-30 17:52:35 +00:00
|
|
|
if r := sqlt.call("sqlite3_error_offset", uint64(handle)); r != math.MaxUint32 {
|
2023-10-13 17:06:05 +01:00
|
|
|
err.sql = sql[0][r:]
|
|
|
|
|
}
|
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
|
|
|
|
2023-12-14 11:28:06 +00:00
|
|
|
func (sqlt *sqlite) getfn(name string) api.Function {
|
|
|
|
|
for i, id := range sqlt.funcs.id {
|
|
|
|
|
if id == name {
|
|
|
|
|
c := &sqlt.funcs
|
|
|
|
|
f := c.fn[i]
|
|
|
|
|
c.fn[i] = nil
|
|
|
|
|
c.id[i] = ""
|
|
|
|
|
c.mask &^= uint32(1) << i
|
|
|
|
|
return f
|
2023-12-01 01:36:48 +00:00
|
|
|
}
|
2023-12-14 11:28:06 +00:00
|
|
|
}
|
|
|
|
|
return sqlt.mod.ExportedFunction(name)
|
|
|
|
|
}
|
2023-12-01 01:36:48 +00:00
|
|
|
|
2023-12-14 11:28:06 +00:00
|
|
|
func (sqlt *sqlite) putfn(name string, fn api.Function) {
|
|
|
|
|
c := &sqlt.funcs
|
|
|
|
|
i := bits.TrailingZeros32(^c.mask)
|
|
|
|
|
if i < 32 {
|
|
|
|
|
c.fn[i] = fn
|
|
|
|
|
c.id[i] = name
|
|
|
|
|
c.mask |= uint32(1) << i
|
2023-12-01 01:36:48 +00:00
|
|
|
} else {
|
2023-12-14 11:28:06 +00:00
|
|
|
c.fn[0] = fn
|
|
|
|
|
c.id[0] = name
|
|
|
|
|
c.mask = uint32(1)
|
2023-12-01 01:36:48 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-30 17:52:35 +00:00
|
|
|
func (sqlt *sqlite) call(name string, params ...uint64) uint64 {
|
2023-07-03 17:21:35 +01:00
|
|
|
copy(sqlt.stack[:], params)
|
2023-12-14 11:28:06 +00:00
|
|
|
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)
|
|
|
|
|
}
|
2023-12-14 11:28:06 +00:00
|
|
|
sqlt.putfn(name, fn)
|
2023-07-03 17:21:35 +01:00
|
|
|
return sqlt.stack[0]
|
2023-03-06 19:03:00 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-03 17:21:35 +01:00
|
|
|
func (sqlt *sqlite) free(ptr uint32) {
|
2023-03-06 19:03:00 +00:00
|
|
|
if ptr == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
2023-11-30 17:52:35 +00:00
|
|
|
sqlt.call("free", uint64(ptr))
|
2023-03-06 19:03:00 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-03 17:21:35 +01:00
|
|
|
func (sqlt *sqlite) new(size uint64) uint32 {
|
2023-03-06 19:03:00 +00:00
|
|
|
if size > _MAX_ALLOCATION_SIZE {
|
2023-03-29 15:01:25 +01:00
|
|
|
panic(util.OOMErr)
|
2023-03-06 19:03:00 +00:00
|
|
|
}
|
2023-11-30 17:52:35 +00:00
|
|
|
ptr := uint32(sqlt.call("malloc", size))
|
2023-03-06 19:03:00 +00: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
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-03 17:21:35 +01:00
|
|
|
func (sqlt *sqlite) newBytes(b []byte) uint32 {
|
2023-10-13 17:06:05 +01:00
|
|
|
if (*[0]byte)(b) == nil {
|
2023-03-06 19:03:00 +00:00
|
|
|
return 0
|
|
|
|
|
}
|
2023-07-03 17:21:35 +01:00
|
|
|
ptr := sqlt.new(uint64(len(b)))
|
|
|
|
|
util.WriteBytes(sqlt.mod, ptr, b)
|
2023-03-06 19:03:00 +00:00
|
|
|
return ptr
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-03 17:21:35 +01:00
|
|
|
func (sqlt *sqlite) newString(s string) uint32 {
|
|
|
|
|
ptr := sqlt.new(uint64(len(s) + 1))
|
|
|
|
|
util.WriteString(sqlt.mod, ptr, s)
|
2023-03-06 19:03:00 +00:00
|
|
|
return ptr
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-03 17:21:35 +01:00
|
|
|
func (sqlt *sqlite) newArena(size uint64) arena {
|
2023-03-06 19:03:00 +00:00
|
|
|
return arena{
|
2023-07-03 17:21:35 +01:00
|
|
|
sqlt: sqlt,
|
2023-03-06 19:03:00 +00:00
|
|
|
size: uint32(size),
|
2023-07-03 17:21:35 +01:00
|
|
|
base: sqlt.new(size),
|
2023-03-06 19:03:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type arena struct {
|
2023-07-03 17:21:35 +01:00
|
|
|
sqlt *sqlite
|
2023-04-11 15:33:38 +01:00
|
|
|
ptrs []uint32
|
2023-03-06 19:03:00 +00:00
|
|
|
base uint32
|
|
|
|
|
next uint32
|
|
|
|
|
size uint32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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() {
|
|
|
|
|
for _, ptr := range a.ptrs[ptrs:] {
|
|
|
|
|
a.sqlt.free(ptr)
|
|
|
|
|
}
|
|
|
|
|
a.ptrs = a.ptrs[:ptrs]
|
|
|
|
|
a.next = next
|
2023-03-06 19:03:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *arena) new(size uint64) uint32 {
|
|
|
|
|
if size <= uint64(a.size-a.next) {
|
|
|
|
|
ptr := a.base + a.next
|
|
|
|
|
a.next += uint32(size)
|
|
|
|
|
return ptr
|
|
|
|
|
}
|
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)
|
|
|
|
|
return ptr
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-21 13:31:45 +01:00
|
|
|
func (a *arena) bytes(b []byte) uint32 {
|
|
|
|
|
if b == nil {
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
ptr := a.new(uint64(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
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 19:03:00 +00:00
|
|
|
func (a *arena) string(s string) uint32 {
|
|
|
|
|
ptr := a.new(uint64(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 {
|
2023-11-16 01:16:38 +00:00
|
|
|
util.ExportFuncII(env, "go_progress", progressCallback)
|
|
|
|
|
util.ExportFuncVI(env, "go_destroy", destroyCallback)
|
|
|
|
|
util.ExportFuncVIII(env, "go_func", funcCallback)
|
|
|
|
|
util.ExportFuncVIII(env, "go_step", stepCallback)
|
|
|
|
|
util.ExportFuncVI(env, "go_final", finalCallback)
|
|
|
|
|
util.ExportFuncVI(env, "go_value", valueCallback)
|
|
|
|
|
util.ExportFuncVIII(env, "go_inverse", inverseCallback)
|
|
|
|
|
util.ExportFuncIIIIII(env, "go_compare", compareCallback)
|
2023-11-22 13:11:23 +00:00
|
|
|
util.ExportFuncIIIIII(env, "go_vtab_create", vtabModuleCallback(0))
|
|
|
|
|
util.ExportFuncIIIIII(env, "go_vtab_connect", vtabModuleCallback(1))
|
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
|
|
|
|
|
}
|