Files
sqlite3/module.go

402 lines
10 KiB
Go
Raw 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"
2023-03-07 03:51:07 +00:00
"io"
2023-03-06 17:51:25 +00:00
"math"
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-03-06 18:28:50 +00:00
var sqlite3 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-03-06 18:28:50 +00:00
func instantiateModule() (*module, error) {
2023-03-06 12:22:17 +00:00
ctx := context.Background()
2023-03-06 18:28:50 +00:00
sqlite3.once.Do(compileModule)
2023-03-06 12:22:17 +00:00
if sqlite3.err != nil {
return nil, sqlite3.err
2023-01-28 12:47:39 +00:00
}
2023-01-18 01:30:11 +00:00
2023-04-04 18:32:56 +01:00
cfg := wazero.NewModuleConfig()
2023-03-06 12:22:17 +00:00
mod, err := sqlite3.runtime.InstantiateModule(ctx, sqlite3.compiled, cfg)
if err != nil {
return nil, err
}
2023-03-06 18:28:50 +00:00
return newModule(mod)
2023-01-28 12:47:39 +00:00
}
2023-03-06 18:28:50 +00:00
func compileModule() {
ctx := context.Background()
2023-06-15 15:57:39 +01:00
sqlite3.runtime = wazero.NewRuntime(ctx)
2023-04-22 00:15:44 +01:00
2023-06-28 17:32:55 +01:00
env := sqlite3.runtime.NewHostModuleBuilder("env")
env = vfs.ExportHostFunctions(env)
env = exportHostFunctions(env)
2023-04-22 00:15:44 +01:00
_, sqlite3.err = env.Instantiate(ctx)
if sqlite3.err != nil {
return
}
2023-01-12 13:43:35 +00:00
2023-01-28 12:47:39 +00:00
bin := Binary
if bin == nil && Path != "" {
2023-03-06 18:28:50 +00:00
bin, sqlite3.err = os.ReadFile(Path)
if sqlite3.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-03-29 15:01:25 +01:00
sqlite3.err = util.BinaryErr
2023-02-20 13:38:03 +00:00
return
}
2023-01-12 13:43:35 +00:00
2023-03-06 18:28:50 +00:00
sqlite3.compiled, sqlite3.err = sqlite3.runtime.CompileModule(ctx, bin)
2023-01-12 13:43:35 +00:00
}
2023-03-06 12:22:17 +00:00
type module struct {
2023-06-30 01:52:18 +01:00
ctx context.Context
mod api.Module
closer io.Closer
api sqliteAPI
stack [8]uint64
2023-03-06 12:22:17 +00:00
}
2023-03-06 17:51:25 +00:00
2023-07-01 00:15:28 +01:00
type moduleKey struct{}
2023-03-06 18:28:50 +00:00
func newModule(mod api.Module) (m *module, err error) {
2023-05-31 15:47:28 +01:00
m = new(module)
2023-06-30 01:52:18 +01:00
m.ctx, m.closer = util.NewContext(context.Background())
2023-07-01 00:15:28 +01:00
m.ctx = context.WithValue(m.ctx, moduleKey{}, m)
m.mod = mod
2023-03-06 23:41:54 +00:00
2023-03-06 18:28:50 +00:00
getFun := func(name string) api.Function {
2023-03-06 23:41:54 +00:00
f := mod.ExportedFunction(name)
2023-03-06 18:28:50 +00:00
if f == nil {
2023-03-29 15:01:25 +01:00
err = util.NoFuncErr + util.ErrorString(name)
2023-03-06 18:28:50 +00:00
return nil
}
return f
}
getVal := func(name string) uint32 {
2023-04-11 15:33:38 +01:00
g := mod.ExportedGlobal(name)
if g == nil {
2023-03-29 15:01:25 +01:00
err = util.NoGlobalErr + util.ErrorString(name)
2023-03-06 18:28:50 +00:00
return 0
}
2023-04-11 15:33:38 +01:00
return util.ReadUint32(mod, uint32(g.Get()))
2023-03-06 18:28:50 +00:00
}
m.api = sqliteAPI{
free: getFun("free"),
malloc: getFun("malloc"),
2023-04-11 15:33:38 +01:00
destructor: getVal("malloc_destructor"),
2023-03-06 18:28:50 +00:00
errcode: getFun("sqlite3_errcode"),
errstr: getFun("sqlite3_errstr"),
errmsg: getFun("sqlite3_errmsg"),
erroff: getFun("sqlite3_error_offset"),
open: getFun("sqlite3_open_v2"),
close: getFun("sqlite3_close"),
closeZombie: getFun("sqlite3_close_v2"),
prepare: getFun("sqlite3_prepare_v3"),
finalize: getFun("sqlite3_finalize"),
reset: getFun("sqlite3_reset"),
step: getFun("sqlite3_step"),
exec: getFun("sqlite3_exec"),
clearBindings: getFun("sqlite3_clear_bindings"),
bindCount: getFun("sqlite3_bind_parameter_count"),
bindIndex: getFun("sqlite3_bind_parameter_index"),
bindName: getFun("sqlite3_bind_parameter_name"),
bindNull: getFun("sqlite3_bind_null"),
bindInteger: getFun("sqlite3_bind_int64"),
bindFloat: getFun("sqlite3_bind_double"),
bindText: getFun("sqlite3_bind_text64"),
bindBlob: getFun("sqlite3_bind_blob64"),
bindZeroBlob: getFun("sqlite3_bind_zeroblob64"),
columnCount: getFun("sqlite3_column_count"),
columnName: getFun("sqlite3_column_name"),
columnType: getFun("sqlite3_column_type"),
columnInteger: getFun("sqlite3_column_int64"),
columnFloat: getFun("sqlite3_column_double"),
columnText: getFun("sqlite3_column_text"),
columnBlob: getFun("sqlite3_column_blob"),
columnBytes: getFun("sqlite3_column_bytes"),
blobOpen: getFun("sqlite3_blob_open"),
blobClose: getFun("sqlite3_blob_close"),
blobReopen: getFun("sqlite3_blob_reopen"),
blobBytes: getFun("sqlite3_blob_bytes"),
blobRead: getFun("sqlite3_blob_read"),
blobWrite: getFun("sqlite3_blob_write"),
backupInit: getFun("sqlite3_backup_init"),
backupStep: getFun("sqlite3_backup_step"),
backupFinish: getFun("sqlite3_backup_finish"),
backupRemaining: getFun("sqlite3_backup_remaining"),
backupPageCount: getFun("sqlite3_backup_pagecount"),
2023-05-25 15:46:15 +01:00
changes: getFun("sqlite3_changes64"),
lastRowid: getFun("sqlite3_last_insert_rowid"),
autocommit: getFun("sqlite3_get_autocommit"),
2023-06-30 02:49:21 +01:00
createCollation: getFun("sqlite3_create_go_collation"),
2023-07-01 00:15:28 +01:00
createFunction: getFun("sqlite3_create_go_function"),
2023-07-01 15:14:45 +01:00
createAggregate: getFun("sqlite3_create_go_aggregate_function"),
createWindow: getFun("sqlite3_create_go_window_function"),
2023-07-03 15:45:16 +01:00
aggregateCtx: getFun("sqlite3_aggregate_context"),
2023-07-01 00:15:28 +01:00
userData: getFun("sqlite3_user_data"),
2023-06-30 10:45:16 +01:00
valueType: getFun("sqlite3_value_type"),
valueInteger: getFun("sqlite3_value_int64"),
valueFloat: getFun("sqlite3_value_double"),
valueText: getFun("sqlite3_value_text"),
valueBlob: getFun("sqlite3_value_blob"),
valueBytes: getFun("sqlite3_value_bytes"),
2023-06-30 11:48:54 +01:00
resultNull: getFun("sqlite3_result_null"),
resultInteger: getFun("sqlite3_result_int64"),
resultFloat: getFun("sqlite3_result_double"),
resultText: getFun("sqlite3_result_text64"),
resultBlob: getFun("sqlite3_result_blob64"),
resultZeroBlob: getFun("sqlite3_result_zeroblob64"),
resultError: getFun("sqlite3_result_error"),
resultErrorCode: getFun("sqlite3_result_error_code"),
resultErrorMem: getFun("sqlite3_result_error_nomem"),
resultErrorBig: getFun("sqlite3_result_error_toobig"),
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-03-07 03:51:07 +00:00
return m, nil
2023-03-06 18:28:50 +00:00
}
2023-03-06 23:41:54 +00:00
func (m *module) close() error {
2023-03-29 15:01:25 +01:00
err := m.mod.Close(m.ctx)
2023-06-30 01:52:18 +01:00
m.closer.Close()
2023-03-07 03:51:07 +00:00
return err
2023-03-06 23:41:54 +00:00
}
2023-03-06 18:28:50 +00:00
func (m *module) 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-05-25 13:17:44 +01:00
if r := m.call(m.api.errstr, rc); r != 0 {
err.str = util.ReadString(m.mod, uint32(r), _MAX_STRING)
2023-03-06 17:51:25 +00:00
}
2023-05-25 13:17:44 +01:00
if r := m.call(m.api.errmsg, uint64(handle)); r != 0 {
err.msg = util.ReadString(m.mod, uint32(r), _MAX_STRING)
2023-03-06 17:51:25 +00:00
}
if sql != nil {
2023-05-25 13:17:44 +01:00
if r := m.call(m.api.erroff, uint64(handle)); r != math.MaxUint32 {
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-05-25 13:17:44 +01:00
func (m *module) call(fn api.Function, params ...uint64) uint64 {
2023-06-30 01:52:18 +01:00
copy(m.stack[:], params)
err := fn.CallWithStack(m.ctx, m.stack[:])
2023-03-06 19:03:00 +00:00
if err != nil {
2023-03-07 14:19:22 +00:00
// The module closed or panicked; release resources.
2023-06-30 01:52:18 +01:00
m.closer.Close()
2023-03-06 19:03:00 +00:00
panic(err)
}
2023-06-30 01:52:18 +01:00
return m.stack[0]
2023-03-06 19:03:00 +00:00
}
func (m *module) free(ptr uint32) {
if ptr == 0 {
return
}
m.call(m.api.free, uint64(ptr))
}
func (m *module) new(size uint64) uint32 {
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-05-25 13:17:44 +01:00
ptr := uint32(m.call(m.api.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
}
func (m *module) newBytes(b []byte) uint32 {
if b == nil {
return 0
}
ptr := m.new(uint64(len(b)))
2023-03-29 15:01:25 +01:00
util.WriteBytes(m.mod, ptr, b)
2023-03-06 19:03:00 +00:00
return ptr
}
func (m *module) newString(s string) uint32 {
ptr := m.new(uint64(len(s) + 1))
2023-03-29 15:01:25 +01:00
util.WriteString(m.mod, ptr, s)
2023-03-06 19:03:00 +00:00
return ptr
}
func (m *module) newArena(size uint64) arena {
return arena{
m: m,
base: m.new(size),
size: uint32(size),
}
}
type arena struct {
m *module
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() {
if a.m == nil {
return
}
a.reset()
a.m.free(a.base)
a.m = nil
}
func (a *arena) reset() {
for _, ptr := range a.ptrs {
a.m.free(ptr)
}
a.ptrs = nil
a.next = 0
}
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
}
ptr := a.m.new(size)
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)))
util.WriteBytes(a.m.mod, ptr, b)
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-03-29 15:01:25 +01:00
util.WriteString(a.m.mod, ptr, s)
2023-03-06 19:03:00 +00:00
return ptr
}
2023-03-06 18:28:50 +00:00
type sqliteAPI struct {
free api.Function
malloc api.Function
errcode api.Function
errstr api.Function
errmsg api.Function
erroff api.Function
open api.Function
close api.Function
closeZombie api.Function
prepare api.Function
finalize api.Function
reset api.Function
step api.Function
exec api.Function
clearBindings api.Function
bindCount api.Function
bindIndex api.Function
bindName api.Function
2023-06-30 11:48:54 +01:00
bindNull api.Function
2023-03-06 18:28:50 +00:00
bindInteger api.Function
bindFloat api.Function
bindText api.Function
bindBlob api.Function
bindZeroBlob api.Function
columnCount api.Function
columnName api.Function
columnType api.Function
columnInteger api.Function
columnFloat api.Function
columnText api.Function
columnBlob api.Function
columnBytes api.Function
blobOpen api.Function
blobClose api.Function
blobReopen api.Function
blobBytes api.Function
blobRead api.Function
blobWrite api.Function
backupInit api.Function
backupStep api.Function
backupFinish api.Function
backupRemaining api.Function
backupPageCount api.Function
2023-05-25 15:46:15 +01:00
changes api.Function
lastRowid api.Function
autocommit api.Function
2023-06-30 02:49:21 +01:00
createCollation api.Function
2023-07-01 00:15:28 +01:00
createFunction api.Function
2023-07-01 15:14:45 +01:00
createAggregate api.Function
createWindow api.Function
2023-07-03 15:45:16 +01:00
aggregateCtx api.Function
2023-07-01 00:15:28 +01:00
userData api.Function
2023-06-30 10:45:16 +01:00
valueType api.Function
valueInteger api.Function
valueFloat api.Function
valueText api.Function
valueBlob api.Function
valueBytes api.Function
2023-06-30 11:48:54 +01:00
resultNull api.Function
resultInteger api.Function
resultFloat api.Function
resultText api.Function
resultBlob api.Function
resultZeroBlob api.Function
resultError api.Function
resultErrorCode api.Function
resultErrorMem api.Function
resultErrorBig api.Function
2023-04-11 15:33:38 +01:00
destructor uint32
2023-03-06 18:28:50 +00:00
}