diff options
author | 2024-05-27 15:46:15 +0000 | |
---|---|---|
committer | 2024-05-27 17:46:15 +0200 | |
commit | 1e7b32490dfdccddd04f46d4b0416b48d749d51b (patch) | |
tree | 62a11365933a5a11e0800af64cbdf9172e5e6e7a /vendor/github.com/ncruces/go-sqlite3/vtab.go | |
parent | [chore] Small styling + link issues (#2933) (diff) | |
download | gotosocial-1e7b32490dfdccddd04f46d4b0416b48d749d51b.tar.xz |
[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
Diffstat (limited to 'vendor/github.com/ncruces/go-sqlite3/vtab.go')
-rw-r--r-- | vendor/github.com/ncruces/go-sqlite3/vtab.go | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/vendor/github.com/ncruces/go-sqlite3/vtab.go b/vendor/github.com/ncruces/go-sqlite3/vtab.go new file mode 100644 index 000000000..a330c98ff --- /dev/null +++ b/vendor/github.com/ncruces/go-sqlite3/vtab.go @@ -0,0 +1,663 @@ +package sqlite3 + +import ( + "context" + "reflect" + + "github.com/ncruces/go-sqlite3/internal/util" + "github.com/tetratelabs/wazero/api" +) + +// CreateModule registers a new virtual table module name. +// If create is nil, the virtual table is eponymous. +// +// https://sqlite.org/c3ref/create_module.html +func CreateModule[T VTab](db *Conn, name string, create, connect VTabConstructor[T]) error { + var flags int + + const ( + VTAB_CREATOR = 0x01 + VTAB_DESTROYER = 0x02 + VTAB_UPDATER = 0x04 + VTAB_RENAMER = 0x08 + VTAB_OVERLOADER = 0x10 + VTAB_CHECKER = 0x20 + VTAB_TXN = 0x40 + VTAB_SAVEPOINTER = 0x80 + ) + + if create != nil { + flags |= VTAB_CREATOR + } + + vtab := reflect.TypeOf(connect).Out(0) + if implements[VTabDestroyer](vtab) { + flags |= VTAB_DESTROYER + } + if implements[VTabUpdater](vtab) { + flags |= VTAB_UPDATER + } + if implements[VTabRenamer](vtab) { + flags |= VTAB_RENAMER + } + if implements[VTabOverloader](vtab) { + flags |= VTAB_OVERLOADER + } + if implements[VTabChecker](vtab) { + flags |= VTAB_CHECKER + } + if implements[VTabTxn](vtab) { + flags |= VTAB_TXN + } + if implements[VTabSavepointer](vtab) { + flags |= VTAB_SAVEPOINTER + } + + defer db.arena.mark()() + namePtr := db.arena.string(name) + modulePtr := util.AddHandle(db.ctx, module[T]{create, connect}) + r := db.call("sqlite3_create_module_go", uint64(db.handle), + uint64(namePtr), uint64(flags), uint64(modulePtr)) + return db.error(r) +} + +func implements[T any](typ reflect.Type) bool { + var ptr *T + return typ.Implements(reflect.TypeOf(ptr).Elem()) +} + +// DeclareVTab declares the schema of a virtual table. +// +// https://sqlite.org/c3ref/declare_vtab.html +func (c *Conn) DeclareVTab(sql string) error { + defer c.arena.mark()() + sqlPtr := c.arena.string(sql) + r := c.call("sqlite3_declare_vtab", uint64(c.handle), uint64(sqlPtr)) + return c.error(r) +} + +// VTabConflictMode is a virtual table conflict resolution mode. +// +// https://sqlite.org/c3ref/c_fail.html +type VTabConflictMode uint8 + +const ( + VTAB_ROLLBACK VTabConflictMode = 1 + VTAB_IGNORE VTabConflictMode = 2 + VTAB_FAIL VTabConflictMode = 3 + VTAB_ABORT VTabConflictMode = 4 + VTAB_REPLACE VTabConflictMode = 5 +) + +// VTabOnConflict determines the virtual table conflict policy. +// +// https://sqlite.org/c3ref/vtab_on_conflict.html +func (c *Conn) VTabOnConflict() VTabConflictMode { + r := c.call("sqlite3_vtab_on_conflict", uint64(c.handle)) + return VTabConflictMode(r) +} + +// VTabConfigOption is a virtual table configuration option. +// +// https://sqlite.org/c3ref/c_vtab_constraint_support.html +type VTabConfigOption uint8 + +const ( + VTAB_CONSTRAINT_SUPPORT VTabConfigOption = 1 + VTAB_INNOCUOUS VTabConfigOption = 2 + VTAB_DIRECTONLY VTabConfigOption = 3 + VTAB_USES_ALL_SCHEMAS VTabConfigOption = 4 +) + +// VTabConfig configures various facets of the virtual table interface. +// +// https://sqlite.org/c3ref/vtab_config.html +func (c *Conn) VTabConfig(op VTabConfigOption, args ...any) error { + var i uint64 + if op == VTAB_CONSTRAINT_SUPPORT && len(args) > 0 { + if b, ok := args[0].(bool); ok && b { + i = 1 + } + } + r := c.call("sqlite3_vtab_config_go", uint64(c.handle), uint64(op), i) + return c.error(r) +} + +// VTabConstructor is a virtual table constructor function. +type VTabConstructor[T VTab] func(db *Conn, module, schema, table string, arg ...string) (T, error) + +type module[T VTab] [2]VTabConstructor[T] + +type vtabConstructor int + +const ( + xCreate vtabConstructor = 0 + xConnect vtabConstructor = 1 +) + +// A VTab describes a particular instance of the virtual table. +// A VTab may optionally implement [io.Closer] to free resources. +// +// https://sqlite.org/c3ref/vtab.html +type VTab interface { + // https://sqlite.org/vtab.html#xbestindex + BestIndex(*IndexInfo) error + // https://sqlite.org/vtab.html#xopen + Open() (VTabCursor, error) +} + +// A VTabDestroyer allows a virtual table to drop persistent state. +type VTabDestroyer interface { + VTab + // https://sqlite.org/vtab.html#sqlite3_module.xDestroy + Destroy() error +} + +// A VTabUpdater allows a virtual table to be updated. +type VTabUpdater interface { + VTab + // https://sqlite.org/vtab.html#xupdate + Update(arg ...Value) (rowid int64, err error) +} + +// A VTabRenamer allows a virtual table to be renamed. +type VTabRenamer interface { + VTab + // https://sqlite.org/vtab.html#xrename + Rename(new string) error +} + +// A VTabOverloader allows a virtual table to overload SQL functions. +type VTabOverloader interface { + VTab + // https://sqlite.org/vtab.html#xfindfunction + FindFunction(arg int, name string) (ScalarFunction, IndexConstraintOp) +} + +// A VTabChecker allows a virtual table to report errors +// to the PRAGMA integrity_check and PRAGMA quick_check commands. +// +// Integrity should return an error if it finds problems in the content of the virtual table, +// but should avoid returning a (wrapped) [Error], [ErrorCode] or [ExtendedErrorCode], +// as those indicate the Integrity method itself encountered problems +// while trying to evaluate the virtual table content. +type VTabChecker interface { + VTab + // https://sqlite.org/vtab.html#xintegrity + Integrity(schema, table string, flags int) error +} + +// A VTabTxn allows a virtual table to implement +// transactions with two-phase commit. +// +// Anything that is required as part of a commit that may fail +// should be performed in the Sync() callback. +// Current versions of SQLite ignore any errors +// returned by Commit() and Rollback(). +type VTabTxn interface { + VTab + // https://sqlite.org/vtab.html#xBegin + Begin() error + // https://sqlite.org/vtab.html#xsync + Sync() error + // https://sqlite.org/vtab.html#xcommit + Commit() error + // https://sqlite.org/vtab.html#xrollback + Rollback() error +} + +// A VTabSavepointer allows a virtual table to implement +// nested transactions. +// +// https://sqlite.org/vtab.html#xsavepoint +type VTabSavepointer interface { + VTabTxn + Savepoint(id int) error + Release(id int) error + RollbackTo(id int) error +} + +// A VTabCursor describes cursors that point +// into the virtual table and are used +// to loop through the virtual table. +// A VTabCursor may optionally implement +// [io.Closer] to free resources. +// +// http://sqlite.org/c3ref/vtab_cursor.html +type VTabCursor interface { + // https://sqlite.org/vtab.html#xfilter + Filter(idxNum int, idxStr string, arg ...Value) error + // https://sqlite.org/vtab.html#xnext + Next() error + // https://sqlite.org/vtab.html#xeof + EOF() bool + // https://sqlite.org/vtab.html#xcolumn + Column(ctx *Context, n int) error + // https://sqlite.org/vtab.html#xrowid + RowID() (int64, error) +} + +// An IndexInfo describes virtual table indexing information. +// +// https://sqlite.org/c3ref/index_info.html +type IndexInfo struct { + // Inputs + Constraint []IndexConstraint + OrderBy []IndexOrderBy + ColumnsUsed int64 + // Outputs + ConstraintUsage []IndexConstraintUsage + IdxNum int + IdxStr string + IdxFlags IndexScanFlag + OrderByConsumed bool + EstimatedCost float64 + EstimatedRows int64 + // Internal + c *Conn + handle uint32 +} + +// An IndexConstraint describes virtual table indexing constraint information. +// +// https://sqlite.org/c3ref/index_info.html +type IndexConstraint struct { + Column int + Op IndexConstraintOp + Usable bool +} + +// An IndexOrderBy describes virtual table indexing order by information. +// +// https://sqlite.org/c3ref/index_info.html +type IndexOrderBy struct { + Column int + Desc bool +} + +// An IndexConstraintUsage describes how virtual table indexing constraints will be used. +// +// https://sqlite.org/c3ref/index_info.html +type IndexConstraintUsage struct { + ArgvIndex int + Omit bool +} + +// RHSValue returns the value of the right-hand operand of a constraint +// if the right-hand operand is known. +// +// https://sqlite.org/c3ref/vtab_rhs_value.html +func (idx *IndexInfo) RHSValue(column int) (Value, error) { + defer idx.c.arena.mark()() + valPtr := idx.c.arena.new(ptrlen) + r := idx.c.call("sqlite3_vtab_rhs_value", uint64(idx.handle), + uint64(column), uint64(valPtr)) + if err := idx.c.error(r); err != nil { + return Value{}, err + } + return Value{ + c: idx.c, + handle: util.ReadUint32(idx.c.mod, valPtr), + }, nil +} + +// Collation returns the name of the collation for a virtual table constraint. +// +// https://sqlite.org/c3ref/vtab_collation.html +func (idx *IndexInfo) Collation(column int) string { + r := idx.c.call("sqlite3_vtab_collation", uint64(idx.handle), + uint64(column)) + return util.ReadString(idx.c.mod, uint32(r), _MAX_NAME) +} + +// Distinct determines if a virtual table query is DISTINCT. +// +// https://sqlite.org/c3ref/vtab_distinct.html +func (idx *IndexInfo) Distinct() int { + r := idx.c.call("sqlite3_vtab_distinct", uint64(idx.handle)) + return int(r) +} + +// In identifies and handles IN constraints. +// +// https://sqlite.org/c3ref/vtab_in.html +func (idx *IndexInfo) In(column, handle int) bool { + r := idx.c.call("sqlite3_vtab_in", uint64(idx.handle), + uint64(column), uint64(handle)) + return r != 0 +} + +func (idx *IndexInfo) load() { + // https://sqlite.org/c3ref/index_info.html + mod := idx.c.mod + ptr := idx.handle + + idx.Constraint = make([]IndexConstraint, util.ReadUint32(mod, ptr+0)) + idx.ConstraintUsage = make([]IndexConstraintUsage, util.ReadUint32(mod, ptr+0)) + idx.OrderBy = make([]IndexOrderBy, util.ReadUint32(mod, ptr+8)) + + constraintPtr := util.ReadUint32(mod, ptr+4) + for i := range idx.Constraint { + idx.Constraint[i] = IndexConstraint{ + Column: int(int32(util.ReadUint32(mod, constraintPtr+0))), + Op: IndexConstraintOp(util.ReadUint8(mod, constraintPtr+4)), + Usable: util.ReadUint8(mod, constraintPtr+5) != 0, + } + constraintPtr += 12 + } + + orderByPtr := util.ReadUint32(mod, ptr+12) + for i := range idx.OrderBy { + idx.OrderBy[i] = IndexOrderBy{ + Column: int(int32(util.ReadUint32(mod, orderByPtr+0))), + Desc: util.ReadUint8(mod, orderByPtr+4) != 0, + } + orderByPtr += 8 + } + + idx.EstimatedCost = util.ReadFloat64(mod, ptr+40) + idx.EstimatedRows = int64(util.ReadUint64(mod, ptr+48)) + idx.ColumnsUsed = int64(util.ReadUint64(mod, ptr+64)) +} + +func (idx *IndexInfo) save() { + // https://sqlite.org/c3ref/index_info.html + mod := idx.c.mod + ptr := idx.handle + + usagePtr := util.ReadUint32(mod, ptr+16) + for _, usage := range idx.ConstraintUsage { + util.WriteUint32(mod, usagePtr+0, uint32(usage.ArgvIndex)) + if usage.Omit { + util.WriteUint8(mod, usagePtr+4, 1) + } + usagePtr += 8 + } + + util.WriteUint32(mod, ptr+20, uint32(idx.IdxNum)) + if idx.IdxStr != "" { + util.WriteUint32(mod, ptr+24, idx.c.newString(idx.IdxStr)) + util.WriteUint32(mod, ptr+28, 1) // needToFreeIdxStr + } + if idx.OrderByConsumed { + util.WriteUint32(mod, ptr+32, 1) + } + util.WriteFloat64(mod, ptr+40, idx.EstimatedCost) + util.WriteUint64(mod, ptr+48, uint64(idx.EstimatedRows)) + util.WriteUint32(mod, ptr+56, uint32(idx.IdxFlags)) +} + +// IndexConstraintOp is a virtual table constraint operator code. +// +// https://sqlite.org/c3ref/c_index_constraint_eq.html +type IndexConstraintOp uint8 + +const ( + INDEX_CONSTRAINT_EQ IndexConstraintOp = 2 + INDEX_CONSTRAINT_GT IndexConstraintOp = 4 + INDEX_CONSTRAINT_LE IndexConstraintOp = 8 + INDEX_CONSTRAINT_LT IndexConstraintOp = 16 + INDEX_CONSTRAINT_GE IndexConstraintOp = 32 + INDEX_CONSTRAINT_MATCH IndexConstraintOp = 64 + INDEX_CONSTRAINT_LIKE IndexConstraintOp = 65 + INDEX_CONSTRAINT_GLOB IndexConstraintOp = 66 + INDEX_CONSTRAINT_REGEXP IndexConstraintOp = 67 + INDEX_CONSTRAINT_NE IndexConstraintOp = 68 + INDEX_CONSTRAINT_ISNOT IndexConstraintOp = 69 + INDEX_CONSTRAINT_ISNOTNULL IndexConstraintOp = 70 + INDEX_CONSTRAINT_ISNULL IndexConstraintOp = 71 + INDEX_CONSTRAINT_IS IndexConstraintOp = 72 + INDEX_CONSTRAINT_LIMIT IndexConstraintOp = 73 + INDEX_CONSTRAINT_OFFSET IndexConstraintOp = 74 + INDEX_CONSTRAINT_FUNCTION IndexConstraintOp = 150 +) + +// IndexScanFlag is a virtual table scan flag. +// +// https://sqlite.org/c3ref/c_index_scan_unique.html +type IndexScanFlag uint32 + +const ( + INDEX_SCAN_UNIQUE IndexScanFlag = 1 +) + +func vtabModuleCallback(i vtabConstructor) func(_ context.Context, _ api.Module, _, _, _, _, _ uint32) uint32 { + return func(ctx context.Context, mod api.Module, pMod, nArg, pArg, ppVTab, pzErr uint32) uint32 { + arg := make([]reflect.Value, 1+nArg) + arg[0] = reflect.ValueOf(ctx.Value(connKey{})) + + for i := uint32(0); i < nArg; i++ { + ptr := util.ReadUint32(mod, pArg+i*ptrlen) + arg[i+1] = reflect.ValueOf(util.ReadString(mod, ptr, _MAX_SQL_LENGTH)) + } + + module := vtabGetHandle(ctx, mod, pMod) + res := reflect.ValueOf(module).Index(int(i)).Call(arg) + err, _ := res[1].Interface().(error) + if err == nil { + vtabPutHandle(ctx, mod, ppVTab, res[0].Interface()) + } + + return vtabError(ctx, mod, pzErr, _PTR_ERROR, err) + } +} + +func vtabDisconnectCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 { + err := vtabDelHandle(ctx, mod, pVTab) + return vtabError(ctx, mod, 0, _PTR_ERROR, err) +} + +func vtabDestroyCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabDestroyer) + err := vtab.Destroy() + if cerr := vtabDelHandle(ctx, mod, pVTab); err == nil { + err = cerr + } + return vtabError(ctx, mod, 0, _PTR_ERROR, err) +} + +func vtabBestIndexCallback(ctx context.Context, mod api.Module, pVTab, pIdxInfo uint32) uint32 { + var info IndexInfo + info.handle = pIdxInfo + info.c = ctx.Value(connKey{}).(*Conn) + info.load() + + vtab := vtabGetHandle(ctx, mod, pVTab).(VTab) + err := vtab.BestIndex(&info) + + info.save() + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func vtabUpdateCallback(ctx context.Context, mod api.Module, pVTab, nArg, pArg, pRowID uint32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabUpdater) + + db := ctx.Value(connKey{}).(*Conn) + args := make([]Value, nArg) + callbackArgs(db, args, pArg) + rowID, err := vtab.Update(args...) + if err == nil { + util.WriteUint64(mod, pRowID, uint64(rowID)) + } + + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func vtabRenameCallback(ctx context.Context, mod api.Module, pVTab, zNew uint32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabRenamer) + err := vtab.Rename(util.ReadString(mod, zNew, _MAX_NAME)) + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func vtabFindFuncCallback(ctx context.Context, mod api.Module, pVTab uint32, nArg int32, zName, pxFunc uint32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabOverloader) + f, op := vtab.FindFunction(int(nArg), util.ReadString(mod, zName, _MAX_NAME)) + if op != 0 { + var wrapper uint32 + wrapper = util.AddHandle(ctx, func(c Context, arg ...Value) { + defer util.DelHandle(ctx, wrapper) + f(c, arg...) + }) + util.WriteUint32(mod, pxFunc, wrapper) + } + return uint32(op) +} + +func vtabIntegrityCallback(ctx context.Context, mod api.Module, pVTab, zSchema, zTabName, mFlags, pzErr uint32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabChecker) + schema := util.ReadString(mod, zSchema, _MAX_NAME) + table := util.ReadString(mod, zTabName, _MAX_NAME) + err := vtab.Integrity(schema, table, int(mFlags)) + // xIntegrity should return OK - even if it finds problems in the content of the virtual table. + // https://sqlite.org/vtab.html#xintegrity + vtabError(ctx, mod, pzErr, _PTR_ERROR, err) + _, code := errorCode(err, _OK) + return code +} + +func vtabBeginCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn) + err := vtab.Begin() + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func vtabSyncCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn) + err := vtab.Sync() + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func vtabCommitCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn) + err := vtab.Commit() + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func vtabRollbackCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn) + err := vtab.Rollback() + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func vtabSavepointCallback(ctx context.Context, mod api.Module, pVTab uint32, id int32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabSavepointer) + err := vtab.Savepoint(int(id)) + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func vtabReleaseCallback(ctx context.Context, mod api.Module, pVTab uint32, id int32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabSavepointer) + err := vtab.Release(int(id)) + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func vtabRollbackToCallback(ctx context.Context, mod api.Module, pVTab uint32, id int32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTabSavepointer) + err := vtab.RollbackTo(int(id)) + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func cursorOpenCallback(ctx context.Context, mod api.Module, pVTab, ppCur uint32) uint32 { + vtab := vtabGetHandle(ctx, mod, pVTab).(VTab) + + cursor, err := vtab.Open() + if err == nil { + vtabPutHandle(ctx, mod, ppCur, cursor) + } + + return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err) +} + +func cursorCloseCallback(ctx context.Context, mod api.Module, pCur uint32) uint32 { + err := vtabDelHandle(ctx, mod, pCur) + return vtabError(ctx, mod, 0, _VTAB_ERROR, err) +} + +func cursorFilterCallback(ctx context.Context, mod api.Module, pCur uint32, idxNum int32, idxStr, nArg, pArg uint32) uint32 { + cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor) + db := ctx.Value(connKey{}).(*Conn) + args := make([]Value, nArg) + callbackArgs(db, args, pArg) + var idxName string + if idxStr != 0 { + idxName = util.ReadString(mod, idxStr, _MAX_LENGTH) + } + err := cursor.Filter(int(idxNum), idxName, args...) + return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err) +} + +func cursorEOFCallback(ctx context.Context, mod api.Module, pCur uint32) uint32 { + cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor) + if cursor.EOF() { + return 1 + } + return 0 +} + +func cursorNextCallback(ctx context.Context, mod api.Module, pCur uint32) uint32 { + cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor) + err := cursor.Next() + return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err) +} + +func cursorColumnCallback(ctx context.Context, mod api.Module, pCur, pCtx uint32, n int32) uint32 { + cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor) + db := ctx.Value(connKey{}).(*Conn) + err := cursor.Column(&Context{db, pCtx}, int(n)) + return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err) +} + +func cursorRowIDCallback(ctx context.Context, mod api.Module, pCur, pRowID uint32) uint32 { + cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor) + + rowID, err := cursor.RowID() + if err == nil { + util.WriteUint64(mod, pRowID, uint64(rowID)) + } + + return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err) +} + +const ( + _PTR_ERROR = iota + _VTAB_ERROR + _CURSOR_ERROR +) + +func vtabError(ctx context.Context, mod api.Module, ptr, kind uint32, err error) uint32 { + const zErrMsgOffset = 8 + msg, code := errorCode(err, ERROR) + if msg != "" && ptr != 0 { + switch kind { + case _VTAB_ERROR: + ptr = ptr + zErrMsgOffset // zErrMsg + case _CURSOR_ERROR: + ptr = util.ReadUint32(mod, ptr) + zErrMsgOffset // pVTab->zErrMsg + } + db := ctx.Value(connKey{}).(*Conn) + if ptr := util.ReadUint32(mod, ptr); ptr != 0 { + db.free(ptr) + } + util.WriteUint32(mod, ptr, db.newString(msg)) + } + return code +} + +func vtabGetHandle(ctx context.Context, mod api.Module, ptr uint32) any { + const handleOffset = 4 + handle := util.ReadUint32(mod, ptr-handleOffset) + return util.GetHandle(ctx, handle) +} + +func vtabDelHandle(ctx context.Context, mod api.Module, ptr uint32) error { + const handleOffset = 4 + handle := util.ReadUint32(mod, ptr-handleOffset) + return util.DelHandle(ctx, handle) +} + +func vtabPutHandle(ctx context.Context, mod api.Module, pptr uint32, val any) { + const handleOffset = 4 + handle := util.AddHandle(ctx, val) + ptr := util.ReadUint32(mod, pptr) + util.WriteUint32(mod, ptr-handleOffset, handle) +} |