github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/vtab.go (about)

     1  package sqlite3
     2  
     3  import (
     4  	"context"
     5  	"reflect"
     6  
     7  	"github.com/ncruces/go-sqlite3/internal/util"
     8  	"github.com/tetratelabs/wazero/api"
     9  )
    10  
    11  // CreateModule registers a new virtual table module name.
    12  // If create is nil, the virtual table is eponymous.
    13  //
    14  // https://sqlite.org/c3ref/create_module.html
    15  func CreateModule[T VTab](db *Conn, name string, create, connect VTabConstructor[T]) error {
    16  	var flags int
    17  
    18  	const (
    19  		VTAB_CREATOR     = 0x01
    20  		VTAB_DESTROYER   = 0x02
    21  		VTAB_UPDATER     = 0x04
    22  		VTAB_RENAMER     = 0x08
    23  		VTAB_OVERLOADER  = 0x10
    24  		VTAB_CHECKER     = 0x20
    25  		VTAB_TXN         = 0x40
    26  		VTAB_SAVEPOINTER = 0x80
    27  	)
    28  
    29  	if create != nil {
    30  		flags |= VTAB_CREATOR
    31  	}
    32  
    33  	vtab := reflect.TypeOf(connect).Out(0)
    34  	if implements[VTabDestroyer](vtab) {
    35  		flags |= VTAB_DESTROYER
    36  	}
    37  	if implements[VTabUpdater](vtab) {
    38  		flags |= VTAB_UPDATER
    39  	}
    40  	if implements[VTabRenamer](vtab) {
    41  		flags |= VTAB_RENAMER
    42  	}
    43  	if implements[VTabOverloader](vtab) {
    44  		flags |= VTAB_OVERLOADER
    45  	}
    46  	if implements[VTabChecker](vtab) {
    47  		flags |= VTAB_CHECKER
    48  	}
    49  	if implements[VTabTxn](vtab) {
    50  		flags |= VTAB_TXN
    51  	}
    52  	if implements[VTabSavepointer](vtab) {
    53  		flags |= VTAB_SAVEPOINTER
    54  	}
    55  
    56  	defer db.arena.mark()()
    57  	namePtr := db.arena.string(name)
    58  	modulePtr := util.AddHandle(db.ctx, module[T]{create, connect})
    59  	r := db.call("sqlite3_create_module_go", uint64(db.handle),
    60  		uint64(namePtr), uint64(flags), uint64(modulePtr))
    61  	return db.error(r)
    62  }
    63  
    64  func implements[T any](typ reflect.Type) bool {
    65  	var ptr *T
    66  	return typ.Implements(reflect.TypeOf(ptr).Elem())
    67  }
    68  
    69  // DeclareVTab declares the schema of a virtual table.
    70  //
    71  // https://sqlite.org/c3ref/declare_vtab.html
    72  func (c *Conn) DeclareVTab(sql string) error {
    73  	defer c.arena.mark()()
    74  	sqlPtr := c.arena.string(sql)
    75  	r := c.call("sqlite3_declare_vtab", uint64(c.handle), uint64(sqlPtr))
    76  	return c.error(r)
    77  }
    78  
    79  // VTabConflictMode is a virtual table conflict resolution mode.
    80  //
    81  // https://sqlite.org/c3ref/c_fail.html
    82  type VTabConflictMode uint8
    83  
    84  const (
    85  	VTAB_ROLLBACK VTabConflictMode = 1
    86  	VTAB_IGNORE   VTabConflictMode = 2
    87  	VTAB_FAIL     VTabConflictMode = 3
    88  	VTAB_ABORT    VTabConflictMode = 4
    89  	VTAB_REPLACE  VTabConflictMode = 5
    90  )
    91  
    92  // VTabOnConflict determines the virtual table conflict policy.
    93  //
    94  // https://sqlite.org/c3ref/vtab_on_conflict.html
    95  func (c *Conn) VTabOnConflict() VTabConflictMode {
    96  	r := c.call("sqlite3_vtab_on_conflict", uint64(c.handle))
    97  	return VTabConflictMode(r)
    98  }
    99  
   100  // VTabConfigOption is a virtual table configuration option.
   101  //
   102  // https://sqlite.org/c3ref/c_vtab_constraint_support.html
   103  type VTabConfigOption uint8
   104  
   105  const (
   106  	VTAB_CONSTRAINT_SUPPORT VTabConfigOption = 1
   107  	VTAB_INNOCUOUS          VTabConfigOption = 2
   108  	VTAB_DIRECTONLY         VTabConfigOption = 3
   109  	VTAB_USES_ALL_SCHEMAS   VTabConfigOption = 4
   110  )
   111  
   112  // VTabConfig configures various facets of the virtual table interface.
   113  //
   114  // https://sqlite.org/c3ref/vtab_config.html
   115  func (c *Conn) VTabConfig(op VTabConfigOption, args ...any) error {
   116  	var i uint64
   117  	if op == VTAB_CONSTRAINT_SUPPORT && len(args) > 0 {
   118  		if b, ok := args[0].(bool); ok && b {
   119  			i = 1
   120  		}
   121  	}
   122  	r := c.call("sqlite3_vtab_config_go", uint64(c.handle), uint64(op), i)
   123  	return c.error(r)
   124  }
   125  
   126  // VTabConstructor is a virtual table constructor function.
   127  type VTabConstructor[T VTab] func(db *Conn, module, schema, table string, arg ...string) (T, error)
   128  
   129  type module[T VTab] [2]VTabConstructor[T]
   130  
   131  type vtabConstructor int
   132  
   133  const (
   134  	xCreate  vtabConstructor = 0
   135  	xConnect vtabConstructor = 1
   136  )
   137  
   138  // A VTab describes a particular instance of the virtual table.
   139  // A VTab may optionally implement [io.Closer] to free resources.
   140  //
   141  // https://sqlite.org/c3ref/vtab.html
   142  type VTab interface {
   143  	// https://sqlite.org/vtab.html#xbestindex
   144  	BestIndex(*IndexInfo) error
   145  	// https://sqlite.org/vtab.html#xopen
   146  	Open() (VTabCursor, error)
   147  }
   148  
   149  // A VTabDestroyer allows a virtual table to drop persistent state.
   150  type VTabDestroyer interface {
   151  	VTab
   152  	// https://sqlite.org/vtab.html#sqlite3_module.xDestroy
   153  	Destroy() error
   154  }
   155  
   156  // A VTabUpdater allows a virtual table to be updated.
   157  type VTabUpdater interface {
   158  	VTab
   159  	// https://sqlite.org/vtab.html#xupdate
   160  	Update(arg ...Value) (rowid int64, err error)
   161  }
   162  
   163  // A VTabRenamer allows a virtual table to be renamed.
   164  type VTabRenamer interface {
   165  	VTab
   166  	// https://sqlite.org/vtab.html#xrename
   167  	Rename(new string) error
   168  }
   169  
   170  // A VTabOverloader allows a virtual table to overload SQL functions.
   171  type VTabOverloader interface {
   172  	VTab
   173  	// https://sqlite.org/vtab.html#xfindfunction
   174  	FindFunction(arg int, name string) (ScalarFunction, IndexConstraintOp)
   175  }
   176  
   177  // A VTabChecker allows a virtual table to report errors
   178  // to the PRAGMA integrity_check and PRAGMA quick_check commands.
   179  //
   180  // Integrity should return an error if it finds problems in the content of the virtual table,
   181  // but should avoid returning a (wrapped) [Error], [ErrorCode] or [ExtendedErrorCode],
   182  // as those indicate the Integrity method itself encountered problems
   183  // while trying to evaluate the virtual table content.
   184  type VTabChecker interface {
   185  	VTab
   186  	// https://sqlite.org/vtab.html#xintegrity
   187  	Integrity(schema, table string, flags int) error
   188  }
   189  
   190  // A VTabTxn allows a virtual table to implement
   191  // transactions with two-phase commit.
   192  //
   193  // Anything that is required as part of a commit that may fail
   194  // should be performed in the Sync() callback.
   195  // Current versions of SQLite ignore any errors
   196  // returned by Commit() and Rollback().
   197  type VTabTxn interface {
   198  	VTab
   199  	// https://sqlite.org/vtab.html#xBegin
   200  	Begin() error
   201  	// https://sqlite.org/vtab.html#xsync
   202  	Sync() error
   203  	// https://sqlite.org/vtab.html#xcommit
   204  	Commit() error
   205  	// https://sqlite.org/vtab.html#xrollback
   206  	Rollback() error
   207  }
   208  
   209  // A VTabSavepointer allows a virtual table to implement
   210  // nested transactions.
   211  //
   212  // https://sqlite.org/vtab.html#xsavepoint
   213  type VTabSavepointer interface {
   214  	VTabTxn
   215  	Savepoint(id int) error
   216  	Release(id int) error
   217  	RollbackTo(id int) error
   218  }
   219  
   220  // A VTabCursor describes cursors that point
   221  // into the virtual table and are used
   222  // to loop through the virtual table.
   223  // A VTabCursor may optionally implement
   224  // [io.Closer] to free resources.
   225  //
   226  // http://sqlite.org/c3ref/vtab_cursor.html
   227  type VTabCursor interface {
   228  	// https://sqlite.org/vtab.html#xfilter
   229  	Filter(idxNum int, idxStr string, arg ...Value) error
   230  	// https://sqlite.org/vtab.html#xnext
   231  	Next() error
   232  	// https://sqlite.org/vtab.html#xeof
   233  	EOF() bool
   234  	// https://sqlite.org/vtab.html#xcolumn
   235  	Column(ctx *Context, n int) error
   236  	// https://sqlite.org/vtab.html#xrowid
   237  	RowID() (int64, error)
   238  }
   239  
   240  // An IndexInfo describes virtual table indexing information.
   241  //
   242  // https://sqlite.org/c3ref/index_info.html
   243  type IndexInfo struct {
   244  	// Inputs
   245  	Constraint  []IndexConstraint
   246  	OrderBy     []IndexOrderBy
   247  	ColumnsUsed int64
   248  	// Outputs
   249  	ConstraintUsage []IndexConstraintUsage
   250  	IdxNum          int
   251  	IdxStr          string
   252  	IdxFlags        IndexScanFlag
   253  	OrderByConsumed bool
   254  	EstimatedCost   float64
   255  	EstimatedRows   int64
   256  	// Internal
   257  	c      *Conn
   258  	handle uint32
   259  }
   260  
   261  // An IndexConstraint describes virtual table indexing constraint information.
   262  //
   263  // https://sqlite.org/c3ref/index_info.html
   264  type IndexConstraint struct {
   265  	Column int
   266  	Op     IndexConstraintOp
   267  	Usable bool
   268  }
   269  
   270  // An IndexOrderBy describes virtual table indexing order by information.
   271  //
   272  // https://sqlite.org/c3ref/index_info.html
   273  type IndexOrderBy struct {
   274  	Column int
   275  	Desc   bool
   276  }
   277  
   278  // An IndexConstraintUsage describes how virtual table indexing constraints will be used.
   279  //
   280  // https://sqlite.org/c3ref/index_info.html
   281  type IndexConstraintUsage struct {
   282  	ArgvIndex int
   283  	Omit      bool
   284  }
   285  
   286  // RHSValue returns the value of the right-hand operand of a constraint
   287  // if the right-hand operand is known.
   288  //
   289  // https://sqlite.org/c3ref/vtab_rhs_value.html
   290  func (idx *IndexInfo) RHSValue(column int) (Value, error) {
   291  	defer idx.c.arena.mark()()
   292  	valPtr := idx.c.arena.new(ptrlen)
   293  	r := idx.c.call("sqlite3_vtab_rhs_value", uint64(idx.handle),
   294  		uint64(column), uint64(valPtr))
   295  	if err := idx.c.error(r); err != nil {
   296  		return Value{}, err
   297  	}
   298  	return Value{
   299  		c:      idx.c,
   300  		handle: util.ReadUint32(idx.c.mod, valPtr),
   301  	}, nil
   302  }
   303  
   304  // Collation returns the name of the collation for a virtual table constraint.
   305  //
   306  // https://sqlite.org/c3ref/vtab_collation.html
   307  func (idx *IndexInfo) Collation(column int) string {
   308  	r := idx.c.call("sqlite3_vtab_collation", uint64(idx.handle),
   309  		uint64(column))
   310  	return util.ReadString(idx.c.mod, uint32(r), _MAX_NAME)
   311  }
   312  
   313  // Distinct determines if a virtual table query is DISTINCT.
   314  //
   315  // https://sqlite.org/c3ref/vtab_distinct.html
   316  func (idx *IndexInfo) Distinct() int {
   317  	r := idx.c.call("sqlite3_vtab_distinct", uint64(idx.handle))
   318  	return int(r)
   319  }
   320  
   321  // In identifies and handles IN constraints.
   322  //
   323  // https://sqlite.org/c3ref/vtab_in.html
   324  func (idx *IndexInfo) In(column, handle int) bool {
   325  	r := idx.c.call("sqlite3_vtab_in", uint64(idx.handle),
   326  		uint64(column), uint64(handle))
   327  	return r != 0
   328  }
   329  
   330  func (idx *IndexInfo) load() {
   331  	// https://sqlite.org/c3ref/index_info.html
   332  	mod := idx.c.mod
   333  	ptr := idx.handle
   334  
   335  	idx.Constraint = make([]IndexConstraint, util.ReadUint32(mod, ptr+0))
   336  	idx.ConstraintUsage = make([]IndexConstraintUsage, util.ReadUint32(mod, ptr+0))
   337  	idx.OrderBy = make([]IndexOrderBy, util.ReadUint32(mod, ptr+8))
   338  
   339  	constraintPtr := util.ReadUint32(mod, ptr+4)
   340  	for i := range idx.Constraint {
   341  		idx.Constraint[i] = IndexConstraint{
   342  			Column: int(int32(util.ReadUint32(mod, constraintPtr+0))),
   343  			Op:     IndexConstraintOp(util.ReadUint8(mod, constraintPtr+4)),
   344  			Usable: util.ReadUint8(mod, constraintPtr+5) != 0,
   345  		}
   346  		constraintPtr += 12
   347  	}
   348  
   349  	orderByPtr := util.ReadUint32(mod, ptr+12)
   350  	for i := range idx.OrderBy {
   351  		idx.OrderBy[i] = IndexOrderBy{
   352  			Column: int(int32(util.ReadUint32(mod, orderByPtr+0))),
   353  			Desc:   util.ReadUint8(mod, orderByPtr+4) != 0,
   354  		}
   355  		orderByPtr += 8
   356  	}
   357  
   358  	idx.EstimatedCost = util.ReadFloat64(mod, ptr+40)
   359  	idx.EstimatedRows = int64(util.ReadUint64(mod, ptr+48))
   360  	idx.ColumnsUsed = int64(util.ReadUint64(mod, ptr+64))
   361  }
   362  
   363  func (idx *IndexInfo) save() {
   364  	// https://sqlite.org/c3ref/index_info.html
   365  	mod := idx.c.mod
   366  	ptr := idx.handle
   367  
   368  	usagePtr := util.ReadUint32(mod, ptr+16)
   369  	for _, usage := range idx.ConstraintUsage {
   370  		util.WriteUint32(mod, usagePtr+0, uint32(usage.ArgvIndex))
   371  		if usage.Omit {
   372  			util.WriteUint8(mod, usagePtr+4, 1)
   373  		}
   374  		usagePtr += 8
   375  	}
   376  
   377  	util.WriteUint32(mod, ptr+20, uint32(idx.IdxNum))
   378  	if idx.IdxStr != "" {
   379  		util.WriteUint32(mod, ptr+24, idx.c.newString(idx.IdxStr))
   380  		util.WriteUint32(mod, ptr+28, 1) // needToFreeIdxStr
   381  	}
   382  	if idx.OrderByConsumed {
   383  		util.WriteUint32(mod, ptr+32, 1)
   384  	}
   385  	util.WriteFloat64(mod, ptr+40, idx.EstimatedCost)
   386  	util.WriteUint64(mod, ptr+48, uint64(idx.EstimatedRows))
   387  	util.WriteUint32(mod, ptr+56, uint32(idx.IdxFlags))
   388  }
   389  
   390  // IndexConstraintOp is a virtual table constraint operator code.
   391  //
   392  // https://sqlite.org/c3ref/c_index_constraint_eq.html
   393  type IndexConstraintOp uint8
   394  
   395  const (
   396  	INDEX_CONSTRAINT_EQ        IndexConstraintOp = 2
   397  	INDEX_CONSTRAINT_GT        IndexConstraintOp = 4
   398  	INDEX_CONSTRAINT_LE        IndexConstraintOp = 8
   399  	INDEX_CONSTRAINT_LT        IndexConstraintOp = 16
   400  	INDEX_CONSTRAINT_GE        IndexConstraintOp = 32
   401  	INDEX_CONSTRAINT_MATCH     IndexConstraintOp = 64
   402  	INDEX_CONSTRAINT_LIKE      IndexConstraintOp = 65
   403  	INDEX_CONSTRAINT_GLOB      IndexConstraintOp = 66
   404  	INDEX_CONSTRAINT_REGEXP    IndexConstraintOp = 67
   405  	INDEX_CONSTRAINT_NE        IndexConstraintOp = 68
   406  	INDEX_CONSTRAINT_ISNOT     IndexConstraintOp = 69
   407  	INDEX_CONSTRAINT_ISNOTNULL IndexConstraintOp = 70
   408  	INDEX_CONSTRAINT_ISNULL    IndexConstraintOp = 71
   409  	INDEX_CONSTRAINT_IS        IndexConstraintOp = 72
   410  	INDEX_CONSTRAINT_LIMIT     IndexConstraintOp = 73
   411  	INDEX_CONSTRAINT_OFFSET    IndexConstraintOp = 74
   412  	INDEX_CONSTRAINT_FUNCTION  IndexConstraintOp = 150
   413  )
   414  
   415  // IndexScanFlag is a virtual table scan flag.
   416  //
   417  // https://sqlite.org/c3ref/c_index_scan_unique.html
   418  type IndexScanFlag uint32
   419  
   420  const (
   421  	INDEX_SCAN_UNIQUE IndexScanFlag = 1
   422  )
   423  
   424  func vtabModuleCallback(i vtabConstructor) func(_ context.Context, _ api.Module, _, _, _, _, _ uint32) uint32 {
   425  	return func(ctx context.Context, mod api.Module, pMod, nArg, pArg, ppVTab, pzErr uint32) uint32 {
   426  		arg := make([]reflect.Value, 1+nArg)
   427  		arg[0] = reflect.ValueOf(ctx.Value(connKey{}))
   428  
   429  		for i := uint32(0); i < nArg; i++ {
   430  			ptr := util.ReadUint32(mod, pArg+i*ptrlen)
   431  			arg[i+1] = reflect.ValueOf(util.ReadString(mod, ptr, _MAX_SQL_LENGTH))
   432  		}
   433  
   434  		module := vtabGetHandle(ctx, mod, pMod)
   435  		res := reflect.ValueOf(module).Index(int(i)).Call(arg)
   436  		err, _ := res[1].Interface().(error)
   437  		if err == nil {
   438  			vtabPutHandle(ctx, mod, ppVTab, res[0].Interface())
   439  		}
   440  
   441  		return vtabError(ctx, mod, pzErr, _PTR_ERROR, err)
   442  	}
   443  }
   444  
   445  func vtabDisconnectCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
   446  	err := vtabDelHandle(ctx, mod, pVTab)
   447  	return vtabError(ctx, mod, 0, _PTR_ERROR, err)
   448  }
   449  
   450  func vtabDestroyCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
   451  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabDestroyer)
   452  	err := vtab.Destroy()
   453  	if cerr := vtabDelHandle(ctx, mod, pVTab); err == nil {
   454  		err = cerr
   455  	}
   456  	return vtabError(ctx, mod, 0, _PTR_ERROR, err)
   457  }
   458  
   459  func vtabBestIndexCallback(ctx context.Context, mod api.Module, pVTab, pIdxInfo uint32) uint32 {
   460  	var info IndexInfo
   461  	info.handle = pIdxInfo
   462  	info.c = ctx.Value(connKey{}).(*Conn)
   463  	info.load()
   464  
   465  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTab)
   466  	err := vtab.BestIndex(&info)
   467  
   468  	info.save()
   469  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   470  }
   471  
   472  func vtabUpdateCallback(ctx context.Context, mod api.Module, pVTab, nArg, pArg, pRowID uint32) uint32 {
   473  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabUpdater)
   474  
   475  	db := ctx.Value(connKey{}).(*Conn)
   476  	args := make([]Value, nArg)
   477  	callbackArgs(db, args, pArg)
   478  	rowID, err := vtab.Update(args...)
   479  	if err == nil {
   480  		util.WriteUint64(mod, pRowID, uint64(rowID))
   481  	}
   482  
   483  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   484  }
   485  
   486  func vtabRenameCallback(ctx context.Context, mod api.Module, pVTab, zNew uint32) uint32 {
   487  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabRenamer)
   488  	err := vtab.Rename(util.ReadString(mod, zNew, _MAX_NAME))
   489  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   490  }
   491  
   492  func vtabFindFuncCallback(ctx context.Context, mod api.Module, pVTab uint32, nArg int32, zName, pxFunc uint32) uint32 {
   493  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabOverloader)
   494  	f, op := vtab.FindFunction(int(nArg), util.ReadString(mod, zName, _MAX_NAME))
   495  	if op != 0 {
   496  		var wrapper uint32
   497  		wrapper = util.AddHandle(ctx, func(c Context, arg ...Value) {
   498  			defer util.DelHandle(ctx, wrapper)
   499  			f(c, arg...)
   500  		})
   501  		util.WriteUint32(mod, pxFunc, wrapper)
   502  	}
   503  	return uint32(op)
   504  }
   505  
   506  func vtabIntegrityCallback(ctx context.Context, mod api.Module, pVTab, zSchema, zTabName, mFlags, pzErr uint32) uint32 {
   507  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabChecker)
   508  	schema := util.ReadString(mod, zSchema, _MAX_NAME)
   509  	table := util.ReadString(mod, zTabName, _MAX_NAME)
   510  	err := vtab.Integrity(schema, table, int(mFlags))
   511  	// xIntegrity should return OK - even if it finds problems in the content of the virtual table.
   512  	// https://sqlite.org/vtab.html#xintegrity
   513  	vtabError(ctx, mod, pzErr, _PTR_ERROR, err)
   514  	_, code := errorCode(err, _OK)
   515  	return code
   516  }
   517  
   518  func vtabBeginCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
   519  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn)
   520  	err := vtab.Begin()
   521  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   522  }
   523  
   524  func vtabSyncCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
   525  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn)
   526  	err := vtab.Sync()
   527  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   528  }
   529  
   530  func vtabCommitCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
   531  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn)
   532  	err := vtab.Commit()
   533  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   534  }
   535  
   536  func vtabRollbackCallback(ctx context.Context, mod api.Module, pVTab uint32) uint32 {
   537  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabTxn)
   538  	err := vtab.Rollback()
   539  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   540  }
   541  
   542  func vtabSavepointCallback(ctx context.Context, mod api.Module, pVTab uint32, id int32) uint32 {
   543  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabSavepointer)
   544  	err := vtab.Savepoint(int(id))
   545  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   546  }
   547  
   548  func vtabReleaseCallback(ctx context.Context, mod api.Module, pVTab uint32, id int32) uint32 {
   549  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabSavepointer)
   550  	err := vtab.Release(int(id))
   551  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   552  }
   553  
   554  func vtabRollbackToCallback(ctx context.Context, mod api.Module, pVTab uint32, id int32) uint32 {
   555  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTabSavepointer)
   556  	err := vtab.RollbackTo(int(id))
   557  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   558  }
   559  
   560  func cursorOpenCallback(ctx context.Context, mod api.Module, pVTab, ppCur uint32) uint32 {
   561  	vtab := vtabGetHandle(ctx, mod, pVTab).(VTab)
   562  
   563  	cursor, err := vtab.Open()
   564  	if err == nil {
   565  		vtabPutHandle(ctx, mod, ppCur, cursor)
   566  	}
   567  
   568  	return vtabError(ctx, mod, pVTab, _VTAB_ERROR, err)
   569  }
   570  
   571  func cursorCloseCallback(ctx context.Context, mod api.Module, pCur uint32) uint32 {
   572  	err := vtabDelHandle(ctx, mod, pCur)
   573  	return vtabError(ctx, mod, 0, _VTAB_ERROR, err)
   574  }
   575  
   576  func cursorFilterCallback(ctx context.Context, mod api.Module, pCur uint32, idxNum int32, idxStr, nArg, pArg uint32) uint32 {
   577  	cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
   578  	db := ctx.Value(connKey{}).(*Conn)
   579  	args := make([]Value, nArg)
   580  	callbackArgs(db, args, pArg)
   581  	var idxName string
   582  	if idxStr != 0 {
   583  		idxName = util.ReadString(mod, idxStr, _MAX_LENGTH)
   584  	}
   585  	err := cursor.Filter(int(idxNum), idxName, args...)
   586  	return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
   587  }
   588  
   589  func cursorEOFCallback(ctx context.Context, mod api.Module, pCur uint32) uint32 {
   590  	cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
   591  	if cursor.EOF() {
   592  		return 1
   593  	}
   594  	return 0
   595  }
   596  
   597  func cursorNextCallback(ctx context.Context, mod api.Module, pCur uint32) uint32 {
   598  	cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
   599  	err := cursor.Next()
   600  	return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
   601  }
   602  
   603  func cursorColumnCallback(ctx context.Context, mod api.Module, pCur, pCtx uint32, n int32) uint32 {
   604  	cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
   605  	db := ctx.Value(connKey{}).(*Conn)
   606  	err := cursor.Column(&Context{db, pCtx}, int(n))
   607  	return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
   608  }
   609  
   610  func cursorRowIDCallback(ctx context.Context, mod api.Module, pCur, pRowID uint32) uint32 {
   611  	cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
   612  
   613  	rowID, err := cursor.RowID()
   614  	if err == nil {
   615  		util.WriteUint64(mod, pRowID, uint64(rowID))
   616  	}
   617  
   618  	return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
   619  }
   620  
   621  const (
   622  	_PTR_ERROR = iota
   623  	_VTAB_ERROR
   624  	_CURSOR_ERROR
   625  )
   626  
   627  func vtabError(ctx context.Context, mod api.Module, ptr, kind uint32, err error) uint32 {
   628  	const zErrMsgOffset = 8
   629  	msg, code := errorCode(err, ERROR)
   630  	if msg != "" && ptr != 0 {
   631  		switch kind {
   632  		case _VTAB_ERROR:
   633  			ptr = ptr + zErrMsgOffset // zErrMsg
   634  		case _CURSOR_ERROR:
   635  			ptr = util.ReadUint32(mod, ptr) + zErrMsgOffset // pVTab->zErrMsg
   636  		}
   637  		db := ctx.Value(connKey{}).(*Conn)
   638  		if ptr := util.ReadUint32(mod, ptr); ptr != 0 {
   639  			db.free(ptr)
   640  		}
   641  		util.WriteUint32(mod, ptr, db.newString(msg))
   642  	}
   643  	return code
   644  }
   645  
   646  func vtabGetHandle(ctx context.Context, mod api.Module, ptr uint32) any {
   647  	const handleOffset = 4
   648  	handle := util.ReadUint32(mod, ptr-handleOffset)
   649  	return util.GetHandle(ctx, handle)
   650  }
   651  
   652  func vtabDelHandle(ctx context.Context, mod api.Module, ptr uint32) error {
   653  	const handleOffset = 4
   654  	handle := util.ReadUint32(mod, ptr-handleOffset)
   655  	return util.DelHandle(ctx, handle)
   656  }
   657  
   658  func vtabPutHandle(ctx context.Context, mod api.Module, pptr uint32, val any) {
   659  	const handleOffset = 4
   660  	handle := util.AddHandle(ctx, val)
   661  	ptr := util.ReadUint32(mod, pptr)
   662  	util.WriteUint32(mod, ptr-handleOffset, handle)
   663  }