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 }