Files
sqlite3/sqlite.go

424 lines
11 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"
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-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-07-03 17:21:35 +01:00
instance.err = util.BinaryErr
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
api sqliteAPI
stack [8]uint64
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-03-06 18:28:50 +00:00
getFun := func(name string) api.Function {
2023-07-11 12:30:09 +01:00
f := sqlt.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-07-11 12:30:09 +01:00
g := sqlt.mod.ExportedGlobal(name)
2023-04-11 15:33:38 +01:00
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-07-11 12:30:09 +01:00
return util.ReadUint32(sqlt.mod, uint32(g.Get()))
2023-03-06 18:28:50 +00:00
}
2023-07-03 17:21:35 +01:00
sqlt.api = sqliteAPI{
2023-03-06 18:28:50 +00:00
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"),
2023-10-25 12:56:52 +01:00
interrupt: getFun("sqlite3_interrupt"),
progressHandler: getFun("sqlite3_progress_handler_go"),
2023-03-06 18:28:50 +00:00
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"),
2023-11-07 00:50:43 +00:00
bindPointer: getFun("sqlite3_bind_pointer_go"),
2023-03-06 18:28:50 +00:00
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-07-04 11:16:29 +01:00
anyCollation: getFun("sqlite3_anycollseq_init"),
2023-07-04 02:18:03 +01:00
createCollation: getFun("sqlite3_create_collation_go"),
createFunction: getFun("sqlite3_create_function_go"),
createAggregate: getFun("sqlite3_create_aggregate_function_go"),
createWindow: getFun("sqlite3_create_window_function_go"),
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-07-04 02:18:03 +01:00
setAuxData: getFun("sqlite3_set_auxdata_go"),
getAuxData: getFun("sqlite3_get_auxdata"),
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-11-07 00:50:43 +00:00
valuePointer: getFun("sqlite3_value_pointer_go"),
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"),
2023-11-07 00:50:43 +00:00
resultPointer: getFun("sqlite3_result_pointer_go"),
2023-10-13 17:06:05 +01:00
resultValue: getFun("sqlite3_result_value"),
2023-06-30 11:48:54 +01:00
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-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-07-03 17:21:35 +01:00
if r := sqlt.call(sqlt.api.errstr, rc); r != 0 {
err.str = util.ReadString(sqlt.mod, uint32(r), _MAX_STRING)
2023-03-06 17:51:25 +00:00
}
2023-10-13 17:06:05 +01:00
if handle != 0 {
if r := sqlt.call(sqlt.api.errmsg, uint64(handle)); r != 0 {
err.msg = util.ReadString(sqlt.mod, uint32(r), _MAX_STRING)
}
2023-03-06 17:51:25 +00:00
2023-10-13 17:06:05 +01:00
if sql != nil {
if r := sqlt.call(sqlt.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-07-03 17:21:35 +01:00
func (sqlt *sqlite) call(fn api.Function, params ...uint64) uint64 {
copy(sqlt.stack[:], params)
err := fn.CallWithStack(sqlt.ctx, sqlt.stack[:])
2023-03-06 19:03:00 +00:00
if err != nil {
panic(err)
}
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-07-03 17:21:35 +01:00
sqlt.call(sqlt.api.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-07-03 17:21:35 +01:00
ptr := uint32(sqlt.call(sqlt.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
}
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
}
a.reset()
2023-07-03 17:21:35 +01:00
a.sqlt.free(a.base)
a.sqlt = nil
2023-03-06 19:03:00 +00:00
}
func (a *arena) reset() {
for _, ptr := range a.ptrs {
2023-07-03 17:21:35 +01:00
a.sqlt.free(ptr)
2023-03-06 19:03:00 +00:00
}
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
}
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-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
2023-10-25 12:56:52 +01:00
interrupt api.Function
progressHandler api.Function
2023-03-06 18:28:50 +00:00
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
2023-11-07 00:50:43 +00:00
bindPointer api.Function
2023-03-06 18:28:50 +00:00
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-07-04 11:16:29 +01:00
anyCollation 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-07-04 02:18:03 +01:00
setAuxData api.Function
getAuxData 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-11-07 00:50:43 +00:00
valuePointer 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
2023-11-07 00:50:43 +00:00
resultPointer api.Function
2023-10-13 17:06:05 +01:00
resultValue api.Function
2023-06-30 11:48:54 +01:00
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
}
2023-10-25 12:56:52 +01:00
func exportCallbacks(env wazero.HostModuleBuilder) wazero.HostModuleBuilder {
util.ExportFuncII(env, "go_progress", callbackProgress)
util.ExportFuncVI(env, "go_destroy", callbackDestroy)
util.ExportFuncIIIIII(env, "go_compare", callbackCompare)
util.ExportFuncVIII(env, "go_func", callbackFunc)
util.ExportFuncVIII(env, "go_step", callbackStep)
util.ExportFuncVI(env, "go_final", callbackFinal)
util.ExportFuncVI(env, "go_value", callbackValue)
util.ExportFuncVIII(env, "go_inverse", callbackInverse)
return env
}