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