gemini-search

A simple search engine for Geminispace
git clone git://git.laack.co/gemini-search.git
Log | Files | Refs | README

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 }