sqlite3_opt_vtable.go raw

   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  	OpScanUnique    = 1  /* Scan visits at most 1 row */
 308  )
 309  
 310  // InfoConstraint give information of constraint.
 311  type InfoConstraint struct {
 312  	Column int
 313  	Op     Op
 314  	Usable bool
 315  }
 316  
 317  // InfoOrderBy give information of order-by.
 318  type InfoOrderBy struct {
 319  	Column int
 320  	Desc   bool
 321  }
 322  
 323  func constraints(info *C.sqlite3_index_info) []InfoConstraint {
 324  	slice := *(*[]C.struct_sqlite3_index_constraint)(unsafe.Pointer(&reflect.SliceHeader{
 325  		Data: uintptr(unsafe.Pointer(info.aConstraint)),
 326  		Len:  int(info.nConstraint),
 327  		Cap:  int(info.nConstraint),
 328  	}))
 329  
 330  	cst := make([]InfoConstraint, 0, len(slice))
 331  	for _, c := range slice {
 332  		var usable bool
 333  		if c.usable > 0 {
 334  			usable = true
 335  		}
 336  		cst = append(cst, InfoConstraint{
 337  			Column: int(c.iColumn),
 338  			Op:     Op(c.op),
 339  			Usable: usable,
 340  		})
 341  	}
 342  	return cst
 343  }
 344  
 345  func orderBys(info *C.sqlite3_index_info) []InfoOrderBy {
 346  	slice := *(*[]C.struct_sqlite3_index_orderby)(unsafe.Pointer(&reflect.SliceHeader{
 347  		Data: uintptr(unsafe.Pointer(info.aOrderBy)),
 348  		Len:  int(info.nOrderBy),
 349  		Cap:  int(info.nOrderBy),
 350  	}))
 351  
 352  	ob := make([]InfoOrderBy, 0, len(slice))
 353  	for _, c := range slice {
 354  		var desc bool
 355  		if c.desc > 0 {
 356  			desc = true
 357  		}
 358  		ob = append(ob, InfoOrderBy{
 359  			Column: int(c.iColumn),
 360  			Desc:   desc,
 361  		})
 362  	}
 363  	return ob
 364  }
 365  
 366  // IndexResult is a Go struct representation of what eventually ends up in the
 367  // output fields for `sqlite3_index_info`
 368  // See: https://www.sqlite.org/c3ref/index_info.html
 369  type IndexResult struct {
 370  	Used           []bool // aConstraintUsage
 371  	IdxNum         int
 372  	IdxStr         string
 373  	AlreadyOrdered bool // orderByConsumed
 374  	EstimatedCost  float64
 375  	EstimatedRows  float64
 376  }
 377  
 378  // mPrintf is a utility wrapper around sqlite3_mprintf
 379  func mPrintf(format, arg string) *C.char {
 380  	cf := C.CString(format)
 381  	defer C.free(unsafe.Pointer(cf))
 382  	ca := C.CString(arg)
 383  	defer C.free(unsafe.Pointer(ca))
 384  	return C._sqlite3_mprintf(cf, ca)
 385  }
 386  
 387  //export goMInit
 388  func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr **C.char, isCreate C.int) C.uintptr_t {
 389  	m := lookupHandle(pClientData).(*sqliteModule)
 390  	if m.c.db != (*C.sqlite3)(db) {
 391  		*pzErr = mPrintf("%s", "Inconsistent db handles")
 392  		return 0
 393  	}
 394  	args := make([]string, argc)
 395  	var A []*C.char
 396  	slice := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(argv)), Len: int(argc), Cap: int(argc)}
 397  	a := reflect.NewAt(reflect.TypeOf(A), unsafe.Pointer(&slice)).Elem().Interface()
 398  	for i, s := range a.([]*C.char) {
 399  		args[i] = C.GoString(s)
 400  	}
 401  	var vTab VTab
 402  	var err error
 403  	if isCreate == 1 {
 404  		vTab, err = m.module.Create(m.c, args)
 405  	} else {
 406  		vTab, err = m.module.Connect(m.c, args)
 407  	}
 408  
 409  	if err != nil {
 410  		*pzErr = mPrintf("%s", err.Error())
 411  		return 0
 412  	}
 413  	vt := sqliteVTab{m, vTab}
 414  	*pzErr = nil
 415  	return C.uintptr_t(uintptr(newHandle(m.c, &vt)))
 416  }
 417  
 418  //export goVRelease
 419  func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char {
 420  	vt := lookupHandle(pVTab).(*sqliteVTab)
 421  	var err error
 422  	if isDestroy == 1 {
 423  		err = vt.vTab.Destroy()
 424  	} else {
 425  		err = vt.vTab.Disconnect()
 426  	}
 427  	if err != nil {
 428  		return mPrintf("%s", err.Error())
 429  	}
 430  	return nil
 431  }
 432  
 433  //export goVOpen
 434  func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t {
 435  	vt := lookupHandle(pVTab).(*sqliteVTab)
 436  	vTabCursor, err := vt.vTab.Open()
 437  	if err != nil {
 438  		*pzErr = mPrintf("%s", err.Error())
 439  		return 0
 440  	}
 441  	vtc := sqliteVTabCursor{vt, vTabCursor}
 442  	*pzErr = nil
 443  	return C.uintptr_t(uintptr(newHandle(vt.module.c, &vtc)))
 444  }
 445  
 446  //export goVBestIndex
 447  func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char {
 448  	vt := lookupHandle(pVTab).(*sqliteVTab)
 449  	info := (*C.sqlite3_index_info)(icp)
 450  	csts := constraints(info)
 451  	res, err := vt.vTab.BestIndex(csts, orderBys(info))
 452  	if err != nil {
 453  		return mPrintf("%s", err.Error())
 454  	}
 455  	if len(res.Used) != len(csts) {
 456  		return mPrintf("Result.Used != expected value", "")
 457  	}
 458  
 459  	// Get a pointer to constraint_usage struct so we can update in place.
 460  
 461  	slice := *(*[]C.struct_sqlite3_index_constraint_usage)(unsafe.Pointer(&reflect.SliceHeader{
 462  		Data: uintptr(unsafe.Pointer(info.aConstraintUsage)),
 463  		Len:  int(info.nConstraint),
 464  		Cap:  int(info.nConstraint),
 465  	}))
 466  	index := 1
 467  	for i := range slice {
 468  		if res.Used[i] {
 469  			slice[i].argvIndex = C.int(index)
 470  			slice[i].omit = C.uchar(1)
 471  			index++
 472  		}
 473  	}
 474  
 475  	info.idxNum = C.int(res.IdxNum)
 476  	info.idxStr = (*C.char)(C.sqlite3_malloc(C.int(len(res.IdxStr) + 1)))
 477  	if info.idxStr == nil {
 478  		// C.malloc and C.CString ordinarily do this for you. See https://golang.org/cmd/cgo/
 479  		panic("out of memory")
 480  	}
 481  	info.needToFreeIdxStr = C.int(1)
 482  
 483  	idxStr := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
 484  		Data: uintptr(unsafe.Pointer(info.idxStr)),
 485  		Len:  len(res.IdxStr) + 1,
 486  		Cap:  len(res.IdxStr) + 1,
 487  	}))
 488  	copy(idxStr, res.IdxStr)
 489  	idxStr[len(idxStr)-1] = 0 // null-terminated string
 490  
 491  	if res.AlreadyOrdered {
 492  		info.orderByConsumed = C.int(1)
 493  	}
 494  	info.estimatedCost = C.double(res.EstimatedCost)
 495  	info.estimatedRows = C.sqlite3_int64(res.EstimatedRows)
 496  
 497  	return nil
 498  }
 499  
 500  //export goVClose
 501  func goVClose(pCursor unsafe.Pointer) *C.char {
 502  	vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
 503  	err := vtc.vTabCursor.Close()
 504  	if err != nil {
 505  		return mPrintf("%s", err.Error())
 506  	}
 507  	return nil
 508  }
 509  
 510  //export goMDestroy
 511  func goMDestroy(pClientData unsafe.Pointer) {
 512  	m := lookupHandle(pClientData).(*sqliteModule)
 513  	m.module.DestroyModule()
 514  }
 515  
 516  //export goVFilter
 517  func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int, argv **C.sqlite3_value) *C.char {
 518  	vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
 519  	args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
 520  	vals := make([]any, 0, argc)
 521  	for _, v := range args {
 522  		conv, err := callbackArgGeneric(v)
 523  		if err != nil {
 524  			return mPrintf("%s", err.Error())
 525  		}
 526  		vals = append(vals, conv.Interface())
 527  	}
 528  	err := vtc.vTabCursor.Filter(int(idxNum), C.GoString(idxName), vals)
 529  	if err != nil {
 530  		return mPrintf("%s", err.Error())
 531  	}
 532  	return nil
 533  }
 534  
 535  //export goVNext
 536  func goVNext(pCursor unsafe.Pointer) *C.char {
 537  	vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
 538  	err := vtc.vTabCursor.Next()
 539  	if err != nil {
 540  		return mPrintf("%s", err.Error())
 541  	}
 542  	return nil
 543  }
 544  
 545  //export goVEof
 546  func goVEof(pCursor unsafe.Pointer) C.int {
 547  	vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
 548  	err := vtc.vTabCursor.EOF()
 549  	if err {
 550  		return 1
 551  	}
 552  	return 0
 553  }
 554  
 555  //export goVColumn
 556  func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char {
 557  	vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
 558  	c := (*SQLiteContext)(cp)
 559  	err := vtc.vTabCursor.Column(c, int(col))
 560  	if err != nil {
 561  		return mPrintf("%s", err.Error())
 562  	}
 563  	return nil
 564  }
 565  
 566  //export goVRowid
 567  func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char {
 568  	vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
 569  	rowid, err := vtc.vTabCursor.Rowid()
 570  	if err != nil {
 571  		return mPrintf("%s", err.Error())
 572  	}
 573  	*pRowid = C.sqlite3_int64(rowid)
 574  	return nil
 575  }
 576  
 577  //export goVUpdate
 578  func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char {
 579  	vt := lookupHandle(pVTab).(*sqliteVTab)
 580  
 581  	var tname string
 582  	if n, ok := vt.vTab.(interface {
 583  		TableName() string
 584  	}); ok {
 585  		tname = n.TableName() + " "
 586  	}
 587  
 588  	err := fmt.Errorf("virtual %s table %sis read-only", vt.module.name, tname)
 589  	if v, ok := vt.vTab.(VTabUpdater); ok {
 590  		// convert argv
 591  		args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
 592  		vals := make([]any, 0, argc)
 593  		for _, v := range args {
 594  			conv, err := callbackArgGeneric(v)
 595  			if err != nil {
 596  				return mPrintf("%s", err.Error())
 597  			}
 598  
 599  			// work around for SQLITE_NULL
 600  			x := conv.Interface()
 601  			if z, ok := x.([]byte); ok && z == nil {
 602  				x = nil
 603  			}
 604  
 605  			vals = append(vals, x)
 606  		}
 607  
 608  		switch {
 609  		case argc == 1:
 610  			err = v.Delete(vals[0])
 611  
 612  		case argc > 1 && vals[0] == nil:
 613  			var id int64
 614  			id, err = v.Insert(vals[1], vals[2:])
 615  			if err == nil {
 616  				*pRowid = C.sqlite3_int64(id)
 617  			}
 618  
 619  		case argc > 1:
 620  			err = v.Update(vals[1], vals[2:])
 621  		}
 622  	}
 623  
 624  	if err != nil {
 625  		return mPrintf("%s", err.Error())
 626  	}
 627  
 628  	return nil
 629  }
 630  
 631  // Module is a "virtual table module", it defines the implementation of a
 632  // virtual tables. See: http://sqlite.org/c3ref/module.html
 633  type Module interface {
 634  	// http://sqlite.org/vtab.html#xcreate
 635  	Create(c *SQLiteConn, args []string) (VTab, error)
 636  	// http://sqlite.org/vtab.html#xconnect
 637  	Connect(c *SQLiteConn, args []string) (VTab, error)
 638  	// http://sqlite.org/c3ref/create_module.html
 639  	DestroyModule()
 640  }
 641  
 642  // EponymousOnlyModule is a "virtual table module" (as above), but
 643  // for defining "eponymous only" virtual tables See: https://sqlite.org/vtab.html#eponymous_only_virtual_tables
 644  type EponymousOnlyModule interface {
 645  	Module
 646  	EponymousOnlyModule()
 647  }
 648  
 649  // VTab describes a particular instance of the virtual table.
 650  // See: http://sqlite.org/c3ref/vtab.html
 651  type VTab interface {
 652  	// http://sqlite.org/vtab.html#xbestindex
 653  	BestIndex([]InfoConstraint, []InfoOrderBy) (*IndexResult, error)
 654  	// http://sqlite.org/vtab.html#xdisconnect
 655  	Disconnect() error
 656  	// http://sqlite.org/vtab.html#sqlite3_module.xDestroy
 657  	Destroy() error
 658  	// http://sqlite.org/vtab.html#xopen
 659  	Open() (VTabCursor, error)
 660  }
 661  
 662  // VTabUpdater is a type that allows a VTab to be inserted, updated, or
 663  // deleted.
 664  // See: https://sqlite.org/vtab.html#xupdate
 665  type VTabUpdater interface {
 666  	Delete(any) error
 667  	Insert(any, []any) (int64, error)
 668  	Update(any, []any) error
 669  }
 670  
 671  // VTabCursor describes cursors that point into the virtual table and are used
 672  // to loop through the virtual table. See: http://sqlite.org/c3ref/vtab_cursor.html
 673  type VTabCursor interface {
 674  	// http://sqlite.org/vtab.html#xclose
 675  	Close() error
 676  	// http://sqlite.org/vtab.html#xfilter
 677  	Filter(idxNum int, idxStr string, vals []any) error
 678  	// http://sqlite.org/vtab.html#xnext
 679  	Next() error
 680  	// http://sqlite.org/vtab.html#xeof
 681  	EOF() bool
 682  	// http://sqlite.org/vtab.html#xcolumn
 683  	Column(c *SQLiteContext, col int) error
 684  	// http://sqlite.org/vtab.html#xrowid
 685  	Rowid() (int64, error)
 686  }
 687  
 688  // DeclareVTab declares the Schema of a virtual table.
 689  // See: http://sqlite.org/c3ref/declare_vtab.html
 690  func (c *SQLiteConn) DeclareVTab(sql string) error {
 691  	zSQL := C.CString(sql)
 692  	defer C.free(unsafe.Pointer(zSQL))
 693  	rv := C.sqlite3_declare_vtab(c.db, zSQL)
 694  	if rv != C.SQLITE_OK {
 695  		return c.lastError()
 696  	}
 697  	return nil
 698  }
 699  
 700  // CreateModule registers a virtual table implementation.
 701  // See: http://sqlite.org/c3ref/create_module.html
 702  func (c *SQLiteConn) CreateModule(moduleName string, module Module) error {
 703  	mname := C.CString(moduleName)
 704  	defer C.free(unsafe.Pointer(mname))
 705  	udm := sqliteModule{c, moduleName, module}
 706  	switch module.(type) {
 707  	case EponymousOnlyModule:
 708  		rv := C._sqlite3_create_module_eponymous_only(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm))))
 709  		if rv != C.SQLITE_OK {
 710  			return c.lastError()
 711  		}
 712  		return nil
 713  	case Module:
 714  		rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm))))
 715  		if rv != C.SQLITE_OK {
 716  			return c.lastError()
 717  		}
 718  		return nil
 719  	}
 720  	return nil
 721  }
 722