sqlite3_opt_vtable.go (20734B)
1 // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>. 2 // 3 // Use of this source code is governed by an MIT-style 4 // license that can be found in the LICENSE file. 5 6 //go:build sqlite_vtable || vtable 7 // +build sqlite_vtable vtable 8 9 package sqlite3 10 11 /* 12 #cgo CFLAGS: -std=gnu99 13 #cgo CFLAGS: -DSQLITE_ENABLE_RTREE 14 #cgo CFLAGS: -DSQLITE_THREADSAFE 15 #cgo CFLAGS: -DSQLITE_ENABLE_FTS3 16 #cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS 17 #cgo CFLAGS: -DSQLITE_ENABLE_FTS4_UNICODE61 18 #cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 19 #cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA=1 20 #cgo CFLAGS: -Wno-deprecated-declarations 21 22 #ifndef USE_LIBSQLITE3 23 #include "sqlite3-binding.h" 24 #else 25 #include <sqlite3.h> 26 #endif 27 #include <stdlib.h> 28 #include <stdint.h> 29 #include <memory.h> 30 31 static inline char *_sqlite3_mprintf(char *zFormat, char *arg) { 32 return sqlite3_mprintf(zFormat, arg); 33 } 34 35 typedef struct goVTab goVTab; 36 37 struct goVTab { 38 sqlite3_vtab base; 39 void *vTab; 40 }; 41 42 uintptr_t goMInit(void *db, void *pAux, int argc, char **argv, char **pzErr, int isCreate); 43 44 static int cXInit(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr, int isCreate) { 45 void *vTab = (void *)goMInit(db, pAux, argc, (char**)argv, pzErr, isCreate); 46 if (!vTab || *pzErr) { 47 return SQLITE_ERROR; 48 } 49 goVTab *pvTab = (goVTab *)sqlite3_malloc(sizeof(goVTab)); 50 if (!pvTab) { 51 *pzErr = sqlite3_mprintf("%s", "Out of memory"); 52 return SQLITE_NOMEM; 53 } 54 memset(pvTab, 0, sizeof(goVTab)); 55 pvTab->vTab = vTab; 56 57 *ppVTab = (sqlite3_vtab *)pvTab; 58 *pzErr = 0; 59 return SQLITE_OK; 60 } 61 62 static inline int cXCreate(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { 63 return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 1); 64 } 65 static inline int cXConnect(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { 66 return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 0); 67 } 68 69 char* goVBestIndex(void *pVTab, void *icp); 70 71 static inline int cXBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *info) { 72 char *pzErr = goVBestIndex(((goVTab*)pVTab)->vTab, info); 73 if (pzErr) { 74 if (pVTab->zErrMsg) 75 sqlite3_free(pVTab->zErrMsg); 76 pVTab->zErrMsg = pzErr; 77 return SQLITE_ERROR; 78 } 79 return SQLITE_OK; 80 } 81 82 char* goVRelease(void *pVTab, int isDestroy); 83 84 static int cXRelease(sqlite3_vtab *pVTab, int isDestroy) { 85 char *pzErr = goVRelease(((goVTab*)pVTab)->vTab, isDestroy); 86 if (pzErr) { 87 if (pVTab->zErrMsg) 88 sqlite3_free(pVTab->zErrMsg); 89 pVTab->zErrMsg = pzErr; 90 return SQLITE_ERROR; 91 } 92 if (pVTab->zErrMsg) 93 sqlite3_free(pVTab->zErrMsg); 94 sqlite3_free(pVTab); 95 return SQLITE_OK; 96 } 97 98 static inline int cXDisconnect(sqlite3_vtab *pVTab) { 99 return cXRelease(pVTab, 0); 100 } 101 static inline int cXDestroy(sqlite3_vtab *pVTab) { 102 return cXRelease(pVTab, 1); 103 } 104 105 typedef struct goVTabCursor goVTabCursor; 106 107 struct goVTabCursor { 108 sqlite3_vtab_cursor base; 109 void *vTabCursor; 110 }; 111 112 uintptr_t goVOpen(void *pVTab, char **pzErr); 113 114 static int cXOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) { 115 void *vTabCursor = (void *)goVOpen(((goVTab*)pVTab)->vTab, &(pVTab->zErrMsg)); 116 goVTabCursor *pCursor = (goVTabCursor *)sqlite3_malloc(sizeof(goVTabCursor)); 117 if (!pCursor) { 118 return SQLITE_NOMEM; 119 } 120 memset(pCursor, 0, sizeof(goVTabCursor)); 121 pCursor->vTabCursor = vTabCursor; 122 *ppCursor = (sqlite3_vtab_cursor *)pCursor; 123 return SQLITE_OK; 124 } 125 126 static int setErrMsg(sqlite3_vtab_cursor *pCursor, char *pzErr) { 127 if (pCursor->pVtab->zErrMsg) 128 sqlite3_free(pCursor->pVtab->zErrMsg); 129 pCursor->pVtab->zErrMsg = pzErr; 130 return SQLITE_ERROR; 131 } 132 133 char* goVClose(void *pCursor); 134 135 static int cXClose(sqlite3_vtab_cursor *pCursor) { 136 char *pzErr = goVClose(((goVTabCursor*)pCursor)->vTabCursor); 137 if (pzErr) { 138 return setErrMsg(pCursor, pzErr); 139 } 140 sqlite3_free(pCursor); 141 return SQLITE_OK; 142 } 143 144 char* goVFilter(void *pCursor, int idxNum, char* idxName, int argc, sqlite3_value **argv); 145 146 static int cXFilter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) { 147 char *pzErr = goVFilter(((goVTabCursor*)pCursor)->vTabCursor, idxNum, (char*)idxStr, argc, argv); 148 if (pzErr) { 149 return setErrMsg(pCursor, pzErr); 150 } 151 return SQLITE_OK; 152 } 153 154 char* goVNext(void *pCursor); 155 156 static int cXNext(sqlite3_vtab_cursor *pCursor) { 157 char *pzErr = goVNext(((goVTabCursor*)pCursor)->vTabCursor); 158 if (pzErr) { 159 return setErrMsg(pCursor, pzErr); 160 } 161 return SQLITE_OK; 162 } 163 164 int goVEof(void *pCursor); 165 166 static inline int cXEof(sqlite3_vtab_cursor *pCursor) { 167 return goVEof(((goVTabCursor*)pCursor)->vTabCursor); 168 } 169 170 char* goVColumn(void *pCursor, void *cp, int col); 171 172 static int cXColumn(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i) { 173 char *pzErr = goVColumn(((goVTabCursor*)pCursor)->vTabCursor, ctx, i); 174 if (pzErr) { 175 return setErrMsg(pCursor, pzErr); 176 } 177 return SQLITE_OK; 178 } 179 180 char* goVRowid(void *pCursor, sqlite3_int64 *pRowid); 181 182 static int cXRowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid) { 183 char *pzErr = goVRowid(((goVTabCursor*)pCursor)->vTabCursor, pRowid); 184 if (pzErr) { 185 return setErrMsg(pCursor, pzErr); 186 } 187 return SQLITE_OK; 188 } 189 190 char* goVUpdate(void *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid); 191 192 static int cXUpdate(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid) { 193 char *pzErr = goVUpdate(((goVTab*)pVTab)->vTab, argc, argv, pRowid); 194 if (pzErr) { 195 if (pVTab->zErrMsg) 196 sqlite3_free(pVTab->zErrMsg); 197 pVTab->zErrMsg = pzErr; 198 return SQLITE_ERROR; 199 } 200 return SQLITE_OK; 201 } 202 203 static sqlite3_module goModule = { 204 0, // iVersion 205 cXCreate, // xCreate - create a table 206 cXConnect, // xConnect - connect to an existing table 207 cXBestIndex, // xBestIndex - Determine search strategy 208 cXDisconnect, // xDisconnect - Disconnect from a table 209 cXDestroy, // xDestroy - Drop a table 210 cXOpen, // xOpen - open a cursor 211 cXClose, // xClose - close a cursor 212 cXFilter, // xFilter - configure scan constraints 213 cXNext, // xNext - advance a cursor 214 cXEof, // xEof 215 cXColumn, // xColumn - read data 216 cXRowid, // xRowid - read data 217 cXUpdate, // xUpdate - write data 218 // Not implemented 219 0, // xBegin - begin transaction 220 0, // xSync - sync transaction 221 0, // xCommit - commit transaction 222 0, // xRollback - rollback transaction 223 0, // xFindFunction - function overloading 224 0, // xRename - rename the table 225 0, // xSavepoint 226 0, // xRelease 227 0 // xRollbackTo 228 }; 229 230 // See https://sqlite.org/vtab.html#eponymous_only_virtual_tables 231 static sqlite3_module goModuleEponymousOnly = { 232 0, // iVersion 233 0, // xCreate - create a table, which here is null 234 cXConnect, // xConnect - connect to an existing table 235 cXBestIndex, // xBestIndex - Determine search strategy 236 cXDisconnect, // xDisconnect - Disconnect from a table 237 cXDestroy, // xDestroy - Drop a table 238 cXOpen, // xOpen - open a cursor 239 cXClose, // xClose - close a cursor 240 cXFilter, // xFilter - configure scan constraints 241 cXNext, // xNext - advance a cursor 242 cXEof, // xEof 243 cXColumn, // xColumn - read data 244 cXRowid, // xRowid - read data 245 cXUpdate, // xUpdate - write data 246 // Not implemented 247 0, // xBegin - begin transaction 248 0, // xSync - sync transaction 249 0, // xCommit - commit transaction 250 0, // xRollback - rollback transaction 251 0, // xFindFunction - function overloading 252 0, // xRename - rename the table 253 0, // xSavepoint 254 0, // xRelease 255 0 // xRollbackTo 256 }; 257 258 void goMDestroy(void*); 259 260 static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pClientData) { 261 return sqlite3_create_module_v2(db, zName, &goModule, (void*) pClientData, goMDestroy); 262 } 263 264 static int _sqlite3_create_module_eponymous_only(sqlite3 *db, const char *zName, uintptr_t pClientData) { 265 return sqlite3_create_module_v2(db, zName, &goModuleEponymousOnly, (void*) pClientData, goMDestroy); 266 } 267 */ 268 import "C" 269 270 import ( 271 "fmt" 272 "math" 273 "reflect" 274 "unsafe" 275 ) 276 277 type sqliteModule struct { 278 c *SQLiteConn 279 name string 280 module Module 281 } 282 283 type sqliteVTab struct { 284 module *sqliteModule 285 vTab VTab 286 } 287 288 type sqliteVTabCursor struct { 289 vTab *sqliteVTab 290 vTabCursor VTabCursor 291 } 292 293 // Op is type of operations. 294 type Op uint8 295 296 // Op mean identity of operations. 297 const ( 298 OpEQ Op = 2 299 OpGT = 4 300 OpLE = 8 301 OpLT = 16 302 OpGE = 32 303 OpMATCH = 64 304 OpLIKE = 65 /* 3.10.0 and later only */ 305 OpGLOB = 66 /* 3.10.0 and later only */ 306 OpREGEXP = 67 /* 3.10.0 and later only */ 307 OpNE = 68 /* 3.21.0 and later only */ 308 OpISNOT = 69 /* 3.21.0 and later */ 309 OpISNOTNULL = 70 /* 3.21.0 and later */ 310 OpISNULL = 71 /* 3.21.0 and later */ 311 OpIS = 72 /* 3.21.0 and later */ 312 OpLIMIT = 73 /* 3.38.0 and later */ 313 OpOFFSET = 74 /* 3.38.0 and later */ 314 OpFUNCTION = 150 /* 3.25.0 and later */ 315 OpScanUnique = 1 /* Scan visits at most 1 row */ 316 ) 317 318 // InfoConstraint give information of constraint. 319 type InfoConstraint struct { 320 Column int 321 Op Op 322 Usable bool 323 } 324 325 // InfoOrderBy give information of order-by. 326 type InfoOrderBy struct { 327 Column int 328 Desc bool 329 } 330 331 func constraints(info *C.sqlite3_index_info) []InfoConstraint { 332 slice := *(*[]C.struct_sqlite3_index_constraint)(unsafe.Pointer(&reflect.SliceHeader{ 333 Data: uintptr(unsafe.Pointer(info.aConstraint)), 334 Len: int(info.nConstraint), 335 Cap: int(info.nConstraint), 336 })) 337 338 cst := make([]InfoConstraint, 0, len(slice)) 339 for _, c := range slice { 340 var usable bool 341 if c.usable > 0 { 342 usable = true 343 } 344 cst = append(cst, InfoConstraint{ 345 Column: int(c.iColumn), 346 Op: Op(c.op), 347 Usable: usable, 348 }) 349 } 350 return cst 351 } 352 353 func orderBys(info *C.sqlite3_index_info) []InfoOrderBy { 354 slice := *(*[]C.struct_sqlite3_index_orderby)(unsafe.Pointer(&reflect.SliceHeader{ 355 Data: uintptr(unsafe.Pointer(info.aOrderBy)), 356 Len: int(info.nOrderBy), 357 Cap: int(info.nOrderBy), 358 })) 359 360 ob := make([]InfoOrderBy, 0, len(slice)) 361 for _, c := range slice { 362 var desc bool 363 if c.desc > 0 { 364 desc = true 365 } 366 ob = append(ob, InfoOrderBy{ 367 Column: int(c.iColumn), 368 Desc: desc, 369 }) 370 } 371 return ob 372 } 373 374 // IndexResult is a Go struct representation of what eventually ends up in the 375 // output fields for `sqlite3_index_info` 376 // See: https://www.sqlite.org/c3ref/index_info.html 377 type IndexResult struct { 378 Used []bool // aConstraintUsage 379 IdxNum int 380 IdxStr string 381 AlreadyOrdered bool // orderByConsumed 382 EstimatedCost float64 383 EstimatedRows float64 384 } 385 386 // mPrintf is a utility wrapper around sqlite3_mprintf 387 func mPrintf(format, arg string) *C.char { 388 cf := C.CString(format) 389 defer C.free(unsafe.Pointer(cf)) 390 ca := C.CString(arg) 391 defer C.free(unsafe.Pointer(ca)) 392 return C._sqlite3_mprintf(cf, ca) 393 } 394 395 //export goMInit 396 func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr **C.char, isCreate C.int) C.uintptr_t { 397 m := lookupHandle(pClientData).(*sqliteModule) 398 if m.c.db != (*C.sqlite3)(db) { 399 *pzErr = mPrintf("%s", "Inconsistent db handles") 400 return 0 401 } 402 args := make([]string, argc) 403 var A []*C.char 404 slice := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(argv)), Len: int(argc), Cap: int(argc)} 405 a := reflect.NewAt(reflect.TypeOf(A), unsafe.Pointer(&slice)).Elem().Interface() 406 for i, s := range a.([]*C.char) { 407 args[i] = C.GoString(s) 408 } 409 var vTab VTab 410 var err error 411 if isCreate == 1 { 412 vTab, err = m.module.Create(m.c, args) 413 } else { 414 vTab, err = m.module.Connect(m.c, args) 415 } 416 417 if err != nil { 418 *pzErr = mPrintf("%s", err.Error()) 419 return 0 420 } 421 vt := sqliteVTab{m, vTab} 422 *pzErr = nil 423 return C.uintptr_t(uintptr(newHandle(m.c, &vt))) 424 } 425 426 //export goVRelease 427 func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char { 428 vt := lookupHandle(pVTab).(*sqliteVTab) 429 var err error 430 if isDestroy == 1 { 431 err = vt.vTab.Destroy() 432 } else { 433 err = vt.vTab.Disconnect() 434 } 435 if err != nil { 436 return mPrintf("%s", err.Error()) 437 } 438 return nil 439 } 440 441 //export goVOpen 442 func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t { 443 vt := lookupHandle(pVTab).(*sqliteVTab) 444 vTabCursor, err := vt.vTab.Open() 445 if err != nil { 446 *pzErr = mPrintf("%s", err.Error()) 447 return 0 448 } 449 vtc := sqliteVTabCursor{vt, vTabCursor} 450 *pzErr = nil 451 return C.uintptr_t(uintptr(newHandle(vt.module.c, &vtc))) 452 } 453 454 //export goVBestIndex 455 func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char { 456 vt := lookupHandle(pVTab).(*sqliteVTab) 457 info := (*C.sqlite3_index_info)(icp) 458 csts := constraints(info) 459 res, err := vt.vTab.BestIndex(csts, orderBys(info)) 460 if err != nil { 461 return mPrintf("%s", err.Error()) 462 } 463 if len(res.Used) != len(csts) { 464 return mPrintf("Result.Used != expected value", "") 465 } 466 467 // Get a pointer to constraint_usage struct so we can update in place. 468 469 slice := *(*[]C.struct_sqlite3_index_constraint_usage)(unsafe.Pointer(&reflect.SliceHeader{ 470 Data: uintptr(unsafe.Pointer(info.aConstraintUsage)), 471 Len: int(info.nConstraint), 472 Cap: int(info.nConstraint), 473 })) 474 index := 1 475 for i := range slice { 476 if res.Used[i] { 477 slice[i].argvIndex = C.int(index) 478 slice[i].omit = C.uchar(1) 479 index++ 480 } 481 } 482 483 info.idxNum = C.int(res.IdxNum) 484 info.idxStr = (*C.char)(C.sqlite3_malloc(C.int(len(res.IdxStr) + 1))) 485 if info.idxStr == nil { 486 // C.malloc and C.CString ordinarily do this for you. See https://golang.org/cmd/cgo/ 487 panic("out of memory") 488 } 489 info.needToFreeIdxStr = C.int(1) 490 491 idxStr := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ 492 Data: uintptr(unsafe.Pointer(info.idxStr)), 493 Len: len(res.IdxStr) + 1, 494 Cap: len(res.IdxStr) + 1, 495 })) 496 copy(idxStr, res.IdxStr) 497 idxStr[len(idxStr)-1] = 0 // null-terminated string 498 499 if res.AlreadyOrdered { 500 info.orderByConsumed = C.int(1) 501 } 502 info.estimatedCost = C.double(res.EstimatedCost) 503 info.estimatedRows = C.sqlite3_int64(res.EstimatedRows) 504 505 return nil 506 } 507 508 //export goVClose 509 func goVClose(pCursor unsafe.Pointer) *C.char { 510 vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 511 err := vtc.vTabCursor.Close() 512 if err != nil { 513 return mPrintf("%s", err.Error()) 514 } 515 return nil 516 } 517 518 //export goMDestroy 519 func goMDestroy(pClientData unsafe.Pointer) { 520 m := lookupHandle(pClientData).(*sqliteModule) 521 m.module.DestroyModule() 522 } 523 524 //export goVFilter 525 func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int, argv **C.sqlite3_value) *C.char { 526 vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 527 args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] 528 vals := make([]any, 0, argc) 529 for _, v := range args { 530 conv, err := callbackArgGeneric(v) 531 if err != nil { 532 return mPrintf("%s", err.Error()) 533 } 534 vals = append(vals, conv.Interface()) 535 } 536 err := vtc.vTabCursor.Filter(int(idxNum), C.GoString(idxName), vals) 537 if err != nil { 538 return mPrintf("%s", err.Error()) 539 } 540 return nil 541 } 542 543 //export goVNext 544 func goVNext(pCursor unsafe.Pointer) *C.char { 545 vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 546 err := vtc.vTabCursor.Next() 547 if err != nil { 548 return mPrintf("%s", err.Error()) 549 } 550 return nil 551 } 552 553 //export goVEof 554 func goVEof(pCursor unsafe.Pointer) C.int { 555 vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 556 err := vtc.vTabCursor.EOF() 557 if err { 558 return 1 559 } 560 return 0 561 } 562 563 //export goVColumn 564 func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char { 565 vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 566 c := (*SQLiteContext)(cp) 567 err := vtc.vTabCursor.Column(c, int(col)) 568 if err != nil { 569 return mPrintf("%s", err.Error()) 570 } 571 return nil 572 } 573 574 //export goVRowid 575 func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char { 576 vtc := lookupHandle(pCursor).(*sqliteVTabCursor) 577 rowid, err := vtc.vTabCursor.Rowid() 578 if err != nil { 579 return mPrintf("%s", err.Error()) 580 } 581 *pRowid = C.sqlite3_int64(rowid) 582 return nil 583 } 584 585 //export goVUpdate 586 func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char { 587 vt := lookupHandle(pVTab).(*sqliteVTab) 588 589 var tname string 590 if n, ok := vt.vTab.(interface { 591 TableName() string 592 }); ok { 593 tname = n.TableName() + " " 594 } 595 596 err := fmt.Errorf("virtual %s table %sis read-only", vt.module.name, tname) 597 if v, ok := vt.vTab.(VTabUpdater); ok { 598 // convert argv 599 args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] 600 vals := make([]any, 0, argc) 601 for _, v := range args { 602 conv, err := callbackArgGeneric(v) 603 if err != nil { 604 return mPrintf("%s", err.Error()) 605 } 606 607 // work around for SQLITE_NULL 608 x := conv.Interface() 609 if z, ok := x.([]byte); ok && z == nil { 610 x = nil 611 } 612 613 vals = append(vals, x) 614 } 615 616 switch { 617 case argc == 1: 618 err = v.Delete(vals[0]) 619 620 case argc > 1 && vals[0] == nil: 621 var id int64 622 id, err = v.Insert(vals[1], vals[2:]) 623 if err == nil { 624 *pRowid = C.sqlite3_int64(id) 625 } 626 627 case argc > 1: 628 err = v.Update(vals[1], vals[2:]) 629 } 630 } 631 632 if err != nil { 633 return mPrintf("%s", err.Error()) 634 } 635 636 return nil 637 } 638 639 // Module is a "virtual table module", it defines the implementation of a 640 // virtual tables. See: http://sqlite.org/c3ref/module.html 641 type Module interface { 642 // http://sqlite.org/vtab.html#xcreate 643 Create(c *SQLiteConn, args []string) (VTab, error) 644 // http://sqlite.org/vtab.html#xconnect 645 Connect(c *SQLiteConn, args []string) (VTab, error) 646 // http://sqlite.org/c3ref/create_module.html 647 DestroyModule() 648 } 649 650 // EponymousOnlyModule is a "virtual table module" (as above), but 651 // for defining "eponymous only" virtual tables See: https://sqlite.org/vtab.html#eponymous_only_virtual_tables 652 type EponymousOnlyModule interface { 653 Module 654 EponymousOnlyModule() 655 } 656 657 // VTab describes a particular instance of the virtual table. 658 // See: http://sqlite.org/c3ref/vtab.html 659 type VTab interface { 660 // http://sqlite.org/vtab.html#xbestindex 661 BestIndex([]InfoConstraint, []InfoOrderBy) (*IndexResult, error) 662 // http://sqlite.org/vtab.html#xdisconnect 663 Disconnect() error 664 // http://sqlite.org/vtab.html#sqlite3_module.xDestroy 665 Destroy() error 666 // http://sqlite.org/vtab.html#xopen 667 Open() (VTabCursor, error) 668 } 669 670 // VTabUpdater is a type that allows a VTab to be inserted, updated, or 671 // deleted. 672 // See: https://sqlite.org/vtab.html#xupdate 673 type VTabUpdater interface { 674 Delete(any) error 675 Insert(any, []any) (int64, error) 676 Update(any, []any) error 677 } 678 679 // VTabCursor describes cursors that point into the virtual table and are used 680 // to loop through the virtual table. See: http://sqlite.org/c3ref/vtab_cursor.html 681 type VTabCursor interface { 682 // http://sqlite.org/vtab.html#xclose 683 Close() error 684 // http://sqlite.org/vtab.html#xfilter 685 Filter(idxNum int, idxStr string, vals []any) error 686 // http://sqlite.org/vtab.html#xnext 687 Next() error 688 // http://sqlite.org/vtab.html#xeof 689 EOF() bool 690 // http://sqlite.org/vtab.html#xcolumn 691 Column(c *SQLiteContext, col int) error 692 // http://sqlite.org/vtab.html#xrowid 693 Rowid() (int64, error) 694 } 695 696 // DeclareVTab declares the Schema of a virtual table. 697 // See: http://sqlite.org/c3ref/declare_vtab.html 698 func (c *SQLiteConn) DeclareVTab(sql string) error { 699 zSQL := C.CString(sql) 700 defer C.free(unsafe.Pointer(zSQL)) 701 rv := C.sqlite3_declare_vtab(c.db, zSQL) 702 if rv != C.SQLITE_OK { 703 return c.lastError() 704 } 705 return nil 706 } 707 708 // CreateModule registers a virtual table implementation. 709 // See: http://sqlite.org/c3ref/create_module.html 710 func (c *SQLiteConn) CreateModule(moduleName string, module Module) error { 711 mname := C.CString(moduleName) 712 defer C.free(unsafe.Pointer(mname)) 713 udm := sqliteModule{c, moduleName, module} 714 switch module.(type) { 715 case EponymousOnlyModule: 716 rv := C._sqlite3_create_module_eponymous_only(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm)))) 717 if rv != C.SQLITE_OK { 718 return c.lastError() 719 } 720 return nil 721 case Module: 722 rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm)))) 723 if rv != C.SQLITE_OK { 724 return c.lastError() 725 } 726 return nil 727 } 728 return nil 729 }