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