// Package sqlite3 wraps the C SQLite API.
package sqlite3 import ( ) // Configure SQLite Wasm. // // Importing package embed initializes [Binary] // with an appropriate build of SQLite: // // import _ "github.com/ncruces/go-sqlite3/embed" var ( Binary []byte // Wasm binary to load. Path string // Path to load the binary from. RuntimeConfig wazero.RuntimeConfig ) // 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 () error { instance.once.Do(compileSQLite) return instance.err } var instance struct { runtime wazero.Runtime compiled wazero.CompiledModule err error once sync.Once } func compileSQLite() { := context.Background() := RuntimeConfig if == nil { = wazero.NewRuntimeConfig() if bits.UintSize < 64 { = .WithMemoryLimitPages(512) // 32MB } else { = .WithMemoryLimitPages(4096) // 256MB } = .WithCoreFeatures(api.CoreFeaturesV2) } instance.runtime = wazero.NewRuntimeWithConfig(, ) := instance.runtime.NewHostModuleBuilder("env") = vfs.ExportHostFunctions() = exportCallbacks() _, instance.err = .Instantiate() if instance.err != nil { return } := Binary if == nil && Path != "" { , instance.err = os.ReadFile(Path) if instance.err != nil { return } } if == nil { instance.err = util.NoBinaryErr return } instance.compiled, instance.err = instance.runtime.CompileModule(, ) } type sqlite struct { ctx context.Context mod api.Module funcs struct { fn [32]api.Function id [32]*byte mask uint32 } stack [9]stk_t } func instantiateSQLite() ( *sqlite, error) { if := Initialize(); != nil { return nil, } = new(sqlite) .ctx = util.NewContext(context.Background()) .mod, = instance.runtime.InstantiateModule(.ctx, instance.compiled, wazero.NewModuleConfig().WithName("")) if != nil { return nil, } if .getfn("sqlite3_progress_handler_go") == nil { return nil, util.BadBinaryErr } return , nil } func ( *sqlite) () error { return .mod.Close(.ctx) } func ( *sqlite) ( res_t, ptr_t, ...string) error { if == _OK { return nil } if ErrorCode() == NOMEM || xErrorCode() == IOERR_NOMEM { panic(util.OOMErr) } if != 0 { var , string if := ptr_t(.call("sqlite3_errmsg", stk_t())); != 0 { = util.ReadString(.mod, , _MAX_LENGTH) if == "not an error" { = "" } else { = strings.TrimPrefix(, util.ErrorCodeString(uint32())[len("sqlite3: "):]) } } if len() != 0 { if := int32(.call("sqlite3_error_offset", stk_t())); != -1 { = [0][:] } } if != "" || != "" { return &Error{code: , msg: , sql: } } } return xErrorCode() } func ( *sqlite) ( string) api.Function { := &.funcs := unsafe.StringData() for := range .id { if .id[] == { .id[] = nil .mask &^= uint32(1) << return .fn[] } } return .mod.ExportedFunction() } func ( *sqlite) ( string, api.Function) { := &.funcs := unsafe.StringData() := bits.TrailingZeros32(^.mask) if < 32 { .id[] = .fn[] = .mask |= uint32(1) << } else { .id[0] = .fn[0] = .mask = uint32(1) } } func ( *sqlite) ( string, ...stk_t) stk_t { copy(.stack[:], ) := .getfn() := .CallWithStack(.ctx, .stack[:]) if != nil { panic() } .putfn(, ) return stk_t(.stack[0]) } func ( *sqlite) ( ptr_t) { if == 0 { return } .call("sqlite3_free", stk_t()) } func ( *sqlite) ( int64) ptr_t { := ptr_t(.call("sqlite3_malloc64", stk_t())) if == 0 && != 0 { panic(util.OOMErr) } return } func ( *sqlite) ( ptr_t, int64) ptr_t { = ptr_t(.call("sqlite3_realloc64", stk_t(), stk_t())) if == 0 && != 0 { panic(util.OOMErr) } return } func ( *sqlite) ( []byte) ptr_t { if len() == 0 { return 0 } := .new(int64(len())) util.WriteBytes(.mod, , ) return } func ( *sqlite) ( string) ptr_t { := .new(int64(len()) + 1) util.WriteString(.mod, , ) return } const arenaSize = 4096 func ( *sqlite) () arena { return arena{ sqlt: , base: .new(arenaSize), } } type arena struct { sqlt *sqlite ptrs []ptr_t base ptr_t next int32 } func ( *arena) () { if .sqlt == nil { return } for , := range .ptrs { .sqlt.free() } .sqlt.free(.base) .sqlt = nil } func ( *arena) () ( func()) { := len(.ptrs) := .next return func() { := .ptrs[:] for , := range .ptrs[:] { .sqlt.free() } .ptrs = .next = } } func ( *arena) ( int64) ptr_t { // Align the next address, to 4 or 8 bytes. if &7 != 0 { .next = (.next + 3) &^ 3 } else { .next = (.next + 7) &^ 7 } if <= arenaSize-int64(.next) { := .base + ptr_t(.next) .next += int32() return ptr_t() } := .sqlt.new() .ptrs = append(.ptrs, ) return ptr_t() } func ( *arena) ( []byte) ptr_t { if len() == 0 { return 0 } := .new(int64(len())) util.WriteBytes(.sqlt.mod, , ) return } func ( *arena) ( string) ptr_t { := .new(int64(len()) + 1) util.WriteString(.sqlt.mod, , ) return } func exportCallbacks( wazero.HostModuleBuilder) wazero.HostModuleBuilder { util.ExportFuncII(, "go_progress_handler", progressCallback) util.ExportFuncIII(, "go_busy_timeout", timeoutCallback) util.ExportFuncIII(, "go_busy_handler", busyCallback) util.ExportFuncII(, "go_commit_hook", commitCallback) util.ExportFuncVI(, "go_rollback_hook", rollbackCallback) util.ExportFuncVIIIIJ(, "go_update_hook", updateCallback) util.ExportFuncIIIII(, "go_wal_hook", walCallback) util.ExportFuncIIIII(, "go_trace", traceCallback) util.ExportFuncIIIIII(, "go_autovacuum_pages", autoVacuumCallback) util.ExportFuncIIIIIII(, "go_authorizer", authorizerCallback) util.ExportFuncVIII(, "go_log", logCallback) util.ExportFuncVI(, "go_destroy", destroyCallback) util.ExportFuncVIIII(, "go_func", funcCallback) util.ExportFuncVIIIII(, "go_step", stepCallback) util.ExportFuncVIIII(, "go_value", valueCallback) util.ExportFuncVIIII(, "go_inverse", inverseCallback) util.ExportFuncVIIII(, "go_collation_needed", collationCallback) util.ExportFuncIIIIII(, "go_compare", compareCallback) util.ExportFuncIIIIII(, "go_vtab_create", vtabModuleCallback(xCreate)) util.ExportFuncIIIIII(, "go_vtab_connect", vtabModuleCallback(xConnect)) util.ExportFuncII(, "go_vtab_disconnect", vtabDisconnectCallback) util.ExportFuncII(, "go_vtab_destroy", vtabDestroyCallback) util.ExportFuncIII(, "go_vtab_best_index", vtabBestIndexCallback) util.ExportFuncIIIII(, "go_vtab_update", vtabUpdateCallback) util.ExportFuncIII(, "go_vtab_rename", vtabRenameCallback) util.ExportFuncIIIII(, "go_vtab_find_function", vtabFindFuncCallback) util.ExportFuncII(, "go_vtab_begin", vtabBeginCallback) util.ExportFuncII(, "go_vtab_sync", vtabSyncCallback) util.ExportFuncII(, "go_vtab_commit", vtabCommitCallback) util.ExportFuncII(, "go_vtab_rollback", vtabRollbackCallback) util.ExportFuncIII(, "go_vtab_savepoint", vtabSavepointCallback) util.ExportFuncIII(, "go_vtab_release", vtabReleaseCallback) util.ExportFuncIII(, "go_vtab_rollback_to", vtabRollbackToCallback) util.ExportFuncIIIIII(, "go_vtab_integrity", vtabIntegrityCallback) util.ExportFuncIII(, "go_cur_open", cursorOpenCallback) util.ExportFuncII(, "go_cur_close", cursorCloseCallback) util.ExportFuncIIIIII(, "go_cur_filter", cursorFilterCallback) util.ExportFuncII(, "go_cur_next", cursorNextCallback) util.ExportFuncII(, "go_cur_eof", cursorEOFCallback) util.ExportFuncIIII(, "go_cur_column", cursorColumnCallback) util.ExportFuncIII(, "go_cur_rowid", cursorRowIDCallback) return }