mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			647 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			647 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
 | 
						|
//
 | 
						|
// Use of this source code is governed by an MIT-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
// +build vtable
 | 
						|
 | 
						|
package sqlite3
 | 
						|
 | 
						|
/*
 | 
						|
#cgo CFLAGS: -std=gnu99
 | 
						|
#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE
 | 
						|
#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4_UNICODE61
 | 
						|
#cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15
 | 
						|
#cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA=1
 | 
						|
#cgo CFLAGS: -Wno-deprecated-declarations
 | 
						|
 | 
						|
#ifndef USE_LIBSQLITE3
 | 
						|
#include <sqlite3-binding.h>
 | 
						|
#else
 | 
						|
#include <sqlite3.h>
 | 
						|
#endif
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdint.h>
 | 
						|
#include <memory.h>
 | 
						|
 | 
						|
static inline char *_sqlite3_mprintf(char *zFormat, char *arg) {
 | 
						|
  return sqlite3_mprintf(zFormat, arg);
 | 
						|
}
 | 
						|
 | 
						|
typedef struct goVTab goVTab;
 | 
						|
 | 
						|
struct goVTab {
 | 
						|
	sqlite3_vtab base;
 | 
						|
	void *vTab;
 | 
						|
};
 | 
						|
 | 
						|
uintptr_t goMInit(void *db, void *pAux, int argc, char **argv, char **pzErr, int isCreate);
 | 
						|
 | 
						|
static int cXInit(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr, int isCreate) {
 | 
						|
	void *vTab = (void *)goMInit(db, pAux, argc, (char**)argv, pzErr, isCreate);
 | 
						|
	if (!vTab || *pzErr) {
 | 
						|
		return SQLITE_ERROR;
 | 
						|
	}
 | 
						|
	goVTab *pvTab = (goVTab *)sqlite3_malloc(sizeof(goVTab));
 | 
						|
	if (!pvTab) {
 | 
						|
		*pzErr = sqlite3_mprintf("%s", "Out of memory");
 | 
						|
		return SQLITE_NOMEM;
 | 
						|
	}
 | 
						|
	memset(pvTab, 0, sizeof(goVTab));
 | 
						|
	pvTab->vTab = vTab;
 | 
						|
 | 
						|
	*ppVTab = (sqlite3_vtab *)pvTab;
 | 
						|
	*pzErr = 0;
 | 
						|
	return SQLITE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static inline int cXCreate(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) {
 | 
						|
	return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 1);
 | 
						|
}
 | 
						|
static inline int cXConnect(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) {
 | 
						|
	return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 0);
 | 
						|
}
 | 
						|
 | 
						|
char* goVBestIndex(void *pVTab, void *icp);
 | 
						|
 | 
						|
static inline int cXBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *info) {
 | 
						|
	char *pzErr = goVBestIndex(((goVTab*)pVTab)->vTab, info);
 | 
						|
	if (pzErr) {
 | 
						|
		if (pVTab->zErrMsg)
 | 
						|
			sqlite3_free(pVTab->zErrMsg);
 | 
						|
		pVTab->zErrMsg = pzErr;
 | 
						|
		return SQLITE_ERROR;
 | 
						|
	}
 | 
						|
	return SQLITE_OK;
 | 
						|
}
 | 
						|
 | 
						|
char* goVRelease(void *pVTab, int isDestroy);
 | 
						|
 | 
						|
static int cXRelease(sqlite3_vtab *pVTab, int isDestroy) {
 | 
						|
	char *pzErr = goVRelease(((goVTab*)pVTab)->vTab, isDestroy);
 | 
						|
	if (pzErr) {
 | 
						|
		if (pVTab->zErrMsg)
 | 
						|
			sqlite3_free(pVTab->zErrMsg);
 | 
						|
		pVTab->zErrMsg = pzErr;
 | 
						|
		return SQLITE_ERROR;
 | 
						|
	}
 | 
						|
	if (pVTab->zErrMsg)
 | 
						|
		sqlite3_free(pVTab->zErrMsg);
 | 
						|
	sqlite3_free(pVTab);
 | 
						|
	return SQLITE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static inline int cXDisconnect(sqlite3_vtab *pVTab) {
 | 
						|
	return cXRelease(pVTab, 0);
 | 
						|
}
 | 
						|
static inline int cXDestroy(sqlite3_vtab *pVTab) {
 | 
						|
	return cXRelease(pVTab, 1);
 | 
						|
}
 | 
						|
 | 
						|
typedef struct goVTabCursor goVTabCursor;
 | 
						|
 | 
						|
struct goVTabCursor {
 | 
						|
	sqlite3_vtab_cursor base;
 | 
						|
	void *vTabCursor;
 | 
						|
};
 | 
						|
 | 
						|
uintptr_t goVOpen(void *pVTab, char **pzErr);
 | 
						|
 | 
						|
static int cXOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) {
 | 
						|
	void *vTabCursor = (void *)goVOpen(((goVTab*)pVTab)->vTab, &(pVTab->zErrMsg));
 | 
						|
	goVTabCursor *pCursor = (goVTabCursor *)sqlite3_malloc(sizeof(goVTabCursor));
 | 
						|
	if (!pCursor) {
 | 
						|
		return SQLITE_NOMEM;
 | 
						|
	}
 | 
						|
	memset(pCursor, 0, sizeof(goVTabCursor));
 | 
						|
	pCursor->vTabCursor = vTabCursor;
 | 
						|
	*ppCursor = (sqlite3_vtab_cursor *)pCursor;
 | 
						|
	return SQLITE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static int setErrMsg(sqlite3_vtab_cursor *pCursor, char *pzErr) {
 | 
						|
	if (pCursor->pVtab->zErrMsg)
 | 
						|
		sqlite3_free(pCursor->pVtab->zErrMsg);
 | 
						|
	pCursor->pVtab->zErrMsg = pzErr;
 | 
						|
	return SQLITE_ERROR;
 | 
						|
}
 | 
						|
 | 
						|
char* goVClose(void *pCursor);
 | 
						|
 | 
						|
static int cXClose(sqlite3_vtab_cursor *pCursor) {
 | 
						|
	char *pzErr = goVClose(((goVTabCursor*)pCursor)->vTabCursor);
 | 
						|
	if (pzErr) {
 | 
						|
		return setErrMsg(pCursor, pzErr);
 | 
						|
	}
 | 
						|
	sqlite3_free(pCursor);
 | 
						|
	return SQLITE_OK;
 | 
						|
}
 | 
						|
 | 
						|
char* goVFilter(void *pCursor, int idxNum, char* idxName, int argc, sqlite3_value **argv);
 | 
						|
 | 
						|
static int cXFilter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) {
 | 
						|
	char *pzErr = goVFilter(((goVTabCursor*)pCursor)->vTabCursor, idxNum, (char*)idxStr, argc, argv);
 | 
						|
	if (pzErr) {
 | 
						|
		return setErrMsg(pCursor, pzErr);
 | 
						|
	}
 | 
						|
	return SQLITE_OK;
 | 
						|
}
 | 
						|
 | 
						|
char* goVNext(void *pCursor);
 | 
						|
 | 
						|
static int cXNext(sqlite3_vtab_cursor *pCursor) {
 | 
						|
	char *pzErr = goVNext(((goVTabCursor*)pCursor)->vTabCursor);
 | 
						|
	if (pzErr) {
 | 
						|
		return setErrMsg(pCursor, pzErr);
 | 
						|
	}
 | 
						|
	return SQLITE_OK;
 | 
						|
}
 | 
						|
 | 
						|
int goVEof(void *pCursor);
 | 
						|
 | 
						|
static inline int cXEof(sqlite3_vtab_cursor *pCursor) {
 | 
						|
	return goVEof(((goVTabCursor*)pCursor)->vTabCursor);
 | 
						|
}
 | 
						|
 | 
						|
char* goVColumn(void *pCursor, void *cp, int col);
 | 
						|
 | 
						|
static int cXColumn(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i) {
 | 
						|
	char *pzErr = goVColumn(((goVTabCursor*)pCursor)->vTabCursor, ctx, i);
 | 
						|
	if (pzErr) {
 | 
						|
		return setErrMsg(pCursor, pzErr);
 | 
						|
	}
 | 
						|
	return SQLITE_OK;
 | 
						|
}
 | 
						|
 | 
						|
char* goVRowid(void *pCursor, sqlite3_int64 *pRowid);
 | 
						|
 | 
						|
static int cXRowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid) {
 | 
						|
	char *pzErr = goVRowid(((goVTabCursor*)pCursor)->vTabCursor, pRowid);
 | 
						|
	if (pzErr) {
 | 
						|
		return setErrMsg(pCursor, pzErr);
 | 
						|
	}
 | 
						|
	return SQLITE_OK;
 | 
						|
}
 | 
						|
 | 
						|
char* goVUpdate(void *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid);
 | 
						|
 | 
						|
static int cXUpdate(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid) {
 | 
						|
	char *pzErr = goVUpdate(((goVTab*)pVTab)->vTab, argc, argv, pRowid);
 | 
						|
	if (pzErr) {
 | 
						|
		if (pVTab->zErrMsg)
 | 
						|
			sqlite3_free(pVTab->zErrMsg);
 | 
						|
		pVTab->zErrMsg = pzErr;
 | 
						|
		return SQLITE_ERROR;
 | 
						|
	}
 | 
						|
	return SQLITE_OK;
 | 
						|
}
 | 
						|
 | 
						|
static sqlite3_module goModule = {
 | 
						|
	0,                       // iVersion
 | 
						|
	cXCreate,                // xCreate - create a table
 | 
						|
	cXConnect,               // xConnect - connect to an existing table
 | 
						|
	cXBestIndex,             // xBestIndex - Determine search strategy
 | 
						|
	cXDisconnect,            // xDisconnect - Disconnect from a table
 | 
						|
	cXDestroy,               // xDestroy - Drop a table
 | 
						|
	cXOpen,                  // xOpen - open a cursor
 | 
						|
	cXClose,                 // xClose - close a cursor
 | 
						|
	cXFilter,                // xFilter - configure scan constraints
 | 
						|
	cXNext,                  // xNext - advance a cursor
 | 
						|
	cXEof,                   // xEof
 | 
						|
	cXColumn,                // xColumn - read data
 | 
						|
	cXRowid,                 // xRowid - read data
 | 
						|
	cXUpdate,                // xUpdate - write data
 | 
						|
// Not implemented
 | 
						|
	0,                       // xBegin - begin transaction
 | 
						|
	0,                       // xSync - sync transaction
 | 
						|
	0,                       // xCommit - commit transaction
 | 
						|
	0,                       // xRollback - rollback transaction
 | 
						|
	0,                       // xFindFunction - function overloading
 | 
						|
	0,                       // xRename - rename the table
 | 
						|
	0,                       // xSavepoint
 | 
						|
	0,                       // xRelease
 | 
						|
	0	                     // xRollbackTo
 | 
						|
};
 | 
						|
 | 
						|
void goMDestroy(void*);
 | 
						|
 | 
						|
static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pClientData) {
 | 
						|
  return sqlite3_create_module_v2(db, zName, &goModule, (void*) pClientData, goMDestroy);
 | 
						|
}
 | 
						|
*/
 | 
						|
import "C"
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"math"
 | 
						|
	"reflect"
 | 
						|
	"unsafe"
 | 
						|
)
 | 
						|
 | 
						|
type sqliteModule struct {
 | 
						|
	c      *SQLiteConn
 | 
						|
	name   string
 | 
						|
	module Module
 | 
						|
}
 | 
						|
 | 
						|
type sqliteVTab struct {
 | 
						|
	module *sqliteModule
 | 
						|
	vTab   VTab
 | 
						|
}
 | 
						|
 | 
						|
type sqliteVTabCursor struct {
 | 
						|
	vTab       *sqliteVTab
 | 
						|
	vTabCursor VTabCursor
 | 
						|
}
 | 
						|
 | 
						|
// Op is type of operations.
 | 
						|
type Op uint8
 | 
						|
 | 
						|
// Op mean identity of operations.
 | 
						|
const (
 | 
						|
	OpEQ         Op = 2
 | 
						|
	OpGT            = 4
 | 
						|
	OpLE            = 8
 | 
						|
	OpLT            = 16
 | 
						|
	OpGE            = 32
 | 
						|
	OpMATCH         = 64
 | 
						|
	OpLIKE          = 65 /* 3.10.0 and later only */
 | 
						|
	OpGLOB          = 66 /* 3.10.0 and later only */
 | 
						|
	OpREGEXP        = 67 /* 3.10.0 and later only */
 | 
						|
	OpScanUnique    = 1  /* Scan visits at most 1 row */
 | 
						|
)
 | 
						|
 | 
						|
// InfoConstraint give information of constraint.
 | 
						|
type InfoConstraint struct {
 | 
						|
	Column int
 | 
						|
	Op     Op
 | 
						|
	Usable bool
 | 
						|
}
 | 
						|
 | 
						|
// InfoOrderBy give information of order-by.
 | 
						|
type InfoOrderBy struct {
 | 
						|
	Column int
 | 
						|
	Desc   bool
 | 
						|
}
 | 
						|
 | 
						|
func constraints(info *C.sqlite3_index_info) []InfoConstraint {
 | 
						|
	l := info.nConstraint
 | 
						|
	slice := (*[1 << 30]C.struct_sqlite3_index_constraint)(unsafe.Pointer(info.aConstraint))[:l:l]
 | 
						|
 | 
						|
	cst := make([]InfoConstraint, 0, l)
 | 
						|
	for _, c := range slice {
 | 
						|
		var usable bool
 | 
						|
		if c.usable > 0 {
 | 
						|
			usable = true
 | 
						|
		}
 | 
						|
		cst = append(cst, InfoConstraint{
 | 
						|
			Column: int(c.iColumn),
 | 
						|
			Op:     Op(c.op),
 | 
						|
			Usable: usable,
 | 
						|
		})
 | 
						|
	}
 | 
						|
	return cst
 | 
						|
}
 | 
						|
 | 
						|
func orderBys(info *C.sqlite3_index_info) []InfoOrderBy {
 | 
						|
	l := info.nOrderBy
 | 
						|
	slice := (*[1 << 30]C.struct_sqlite3_index_orderby)(unsafe.Pointer(info.aOrderBy))[:l:l]
 | 
						|
 | 
						|
	ob := make([]InfoOrderBy, 0, l)
 | 
						|
	for _, c := range slice {
 | 
						|
		var desc bool
 | 
						|
		if c.desc > 0 {
 | 
						|
			desc = true
 | 
						|
		}
 | 
						|
		ob = append(ob, InfoOrderBy{
 | 
						|
			Column: int(c.iColumn),
 | 
						|
			Desc:   desc,
 | 
						|
		})
 | 
						|
	}
 | 
						|
	return ob
 | 
						|
}
 | 
						|
 | 
						|
// IndexResult is a Go struct representation of what eventually ends up in the
 | 
						|
// output fields for `sqlite3_index_info`
 | 
						|
// See: https://www.sqlite.org/c3ref/index_info.html
 | 
						|
type IndexResult struct {
 | 
						|
	Used           []bool // aConstraintUsage
 | 
						|
	IdxNum         int
 | 
						|
	IdxStr         string
 | 
						|
	AlreadyOrdered bool // orderByConsumed
 | 
						|
	EstimatedCost  float64
 | 
						|
	EstimatedRows  float64
 | 
						|
}
 | 
						|
 | 
						|
// mPrintf is a utility wrapper around sqlite3_mprintf
 | 
						|
func mPrintf(format, arg string) *C.char {
 | 
						|
	cf := C.CString(format)
 | 
						|
	defer C.free(unsafe.Pointer(cf))
 | 
						|
	ca := C.CString(arg)
 | 
						|
	defer C.free(unsafe.Pointer(ca))
 | 
						|
	return C._sqlite3_mprintf(cf, ca)
 | 
						|
}
 | 
						|
 | 
						|
//export goMInit
 | 
						|
func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr **C.char, isCreate C.int) C.uintptr_t {
 | 
						|
	m := lookupHandle(uintptr(pClientData)).(*sqliteModule)
 | 
						|
	if m.c.db != (*C.sqlite3)(db) {
 | 
						|
		*pzErr = mPrintf("%s", "Inconsistent db handles")
 | 
						|
		return 0
 | 
						|
	}
 | 
						|
	args := make([]string, argc)
 | 
						|
	var A []*C.char
 | 
						|
	slice := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(argv)), Len: int(argc), Cap: int(argc)}
 | 
						|
	a := reflect.NewAt(reflect.TypeOf(A), unsafe.Pointer(&slice)).Elem().Interface()
 | 
						|
	for i, s := range a.([]*C.char) {
 | 
						|
		args[i] = C.GoString(s)
 | 
						|
	}
 | 
						|
	var vTab VTab
 | 
						|
	var err error
 | 
						|
	if isCreate == 1 {
 | 
						|
		vTab, err = m.module.Create(m.c, args)
 | 
						|
	} else {
 | 
						|
		vTab, err = m.module.Connect(m.c, args)
 | 
						|
	}
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		*pzErr = mPrintf("%s", err.Error())
 | 
						|
		return 0
 | 
						|
	}
 | 
						|
	vt := sqliteVTab{m, vTab}
 | 
						|
	*pzErr = nil
 | 
						|
	return C.uintptr_t(newHandle(m.c, &vt))
 | 
						|
}
 | 
						|
 | 
						|
//export goVRelease
 | 
						|
func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char {
 | 
						|
	vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab)
 | 
						|
	var err error
 | 
						|
	if isDestroy == 1 {
 | 
						|
		err = vt.vTab.Destroy()
 | 
						|
	} else {
 | 
						|
		err = vt.vTab.Disconnect()
 | 
						|
	}
 | 
						|
	if err != nil {
 | 
						|
		return mPrintf("%s", err.Error())
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
//export goVOpen
 | 
						|
func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t {
 | 
						|
	vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab)
 | 
						|
	vTabCursor, err := vt.vTab.Open()
 | 
						|
	if err != nil {
 | 
						|
		*pzErr = mPrintf("%s", err.Error())
 | 
						|
		return 0
 | 
						|
	}
 | 
						|
	vtc := sqliteVTabCursor{vt, vTabCursor}
 | 
						|
	*pzErr = nil
 | 
						|
	return C.uintptr_t(newHandle(vt.module.c, &vtc))
 | 
						|
}
 | 
						|
 | 
						|
//export goVBestIndex
 | 
						|
func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char {
 | 
						|
	vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab)
 | 
						|
	info := (*C.sqlite3_index_info)(icp)
 | 
						|
	csts := constraints(info)
 | 
						|
	res, err := vt.vTab.BestIndex(csts, orderBys(info))
 | 
						|
	if err != nil {
 | 
						|
		return mPrintf("%s", err.Error())
 | 
						|
	}
 | 
						|
	if len(res.Used) != len(csts) {
 | 
						|
		return mPrintf("Result.Used != expected value", "")
 | 
						|
	}
 | 
						|
 | 
						|
	// Get a pointer to constraint_usage struct so we can update in place.
 | 
						|
	l := info.nConstraint
 | 
						|
	s := (*[1 << 30]C.struct_sqlite3_index_constraint_usage)(unsafe.Pointer(info.aConstraintUsage))[:l:l]
 | 
						|
	index := 1
 | 
						|
	for i := C.int(0); i < info.nConstraint; i++ {
 | 
						|
		if res.Used[i] {
 | 
						|
			s[i].argvIndex = C.int(index)
 | 
						|
			s[i].omit = C.uchar(1)
 | 
						|
			index++
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	info.idxNum = C.int(res.IdxNum)
 | 
						|
	idxStr := C.CString(res.IdxStr)
 | 
						|
	defer C.free(unsafe.Pointer(idxStr))
 | 
						|
	info.idxStr = idxStr
 | 
						|
	info.needToFreeIdxStr = C.int(0)
 | 
						|
	if res.AlreadyOrdered {
 | 
						|
		info.orderByConsumed = C.int(1)
 | 
						|
	}
 | 
						|
	info.estimatedCost = C.double(res.EstimatedCost)
 | 
						|
	info.estimatedRows = C.sqlite3_int64(res.EstimatedRows)
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
//export goVClose
 | 
						|
func goVClose(pCursor unsafe.Pointer) *C.char {
 | 
						|
	vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor)
 | 
						|
	err := vtc.vTabCursor.Close()
 | 
						|
	if err != nil {
 | 
						|
		return mPrintf("%s", err.Error())
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
//export goMDestroy
 | 
						|
func goMDestroy(pClientData unsafe.Pointer) {
 | 
						|
	m := lookupHandle(uintptr(pClientData)).(*sqliteModule)
 | 
						|
	m.module.DestroyModule()
 | 
						|
}
 | 
						|
 | 
						|
//export goVFilter
 | 
						|
func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int, argv **C.sqlite3_value) *C.char {
 | 
						|
	vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor)
 | 
						|
	args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
 | 
						|
	vals := make([]interface{}, 0, argc)
 | 
						|
	for _, v := range args {
 | 
						|
		conv, err := callbackArgGeneric(v)
 | 
						|
		if err != nil {
 | 
						|
			return mPrintf("%s", err.Error())
 | 
						|
		}
 | 
						|
		vals = append(vals, conv.Interface())
 | 
						|
	}
 | 
						|
	err := vtc.vTabCursor.Filter(int(idxNum), C.GoString(idxName), vals)
 | 
						|
	if err != nil {
 | 
						|
		return mPrintf("%s", err.Error())
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
//export goVNext
 | 
						|
func goVNext(pCursor unsafe.Pointer) *C.char {
 | 
						|
	vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor)
 | 
						|
	err := vtc.vTabCursor.Next()
 | 
						|
	if err != nil {
 | 
						|
		return mPrintf("%s", err.Error())
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
//export goVEof
 | 
						|
func goVEof(pCursor unsafe.Pointer) C.int {
 | 
						|
	vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor)
 | 
						|
	err := vtc.vTabCursor.EOF()
 | 
						|
	if err {
 | 
						|
		return 1
 | 
						|
	}
 | 
						|
	return 0
 | 
						|
}
 | 
						|
 | 
						|
//export goVColumn
 | 
						|
func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char {
 | 
						|
	vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor)
 | 
						|
	c := (*SQLiteContext)(cp)
 | 
						|
	err := vtc.vTabCursor.Column(c, int(col))
 | 
						|
	if err != nil {
 | 
						|
		return mPrintf("%s", err.Error())
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
//export goVRowid
 | 
						|
func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char {
 | 
						|
	vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor)
 | 
						|
	rowid, err := vtc.vTabCursor.Rowid()
 | 
						|
	if err != nil {
 | 
						|
		return mPrintf("%s", err.Error())
 | 
						|
	}
 | 
						|
	*pRowid = C.sqlite3_int64(rowid)
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
//export goVUpdate
 | 
						|
func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char {
 | 
						|
	vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab)
 | 
						|
 | 
						|
	var tname string
 | 
						|
	if n, ok := vt.vTab.(interface {
 | 
						|
		TableName() string
 | 
						|
	}); ok {
 | 
						|
		tname = n.TableName() + " "
 | 
						|
	}
 | 
						|
 | 
						|
	err := fmt.Errorf("virtual %s table %sis read-only", vt.module.name, tname)
 | 
						|
	if v, ok := vt.vTab.(VTabUpdater); ok {
 | 
						|
		// convert argv
 | 
						|
		args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
 | 
						|
		vals := make([]interface{}, 0, argc)
 | 
						|
		for _, v := range args {
 | 
						|
			conv, err := callbackArgGeneric(v)
 | 
						|
			if err != nil {
 | 
						|
				return mPrintf("%s", err.Error())
 | 
						|
			}
 | 
						|
 | 
						|
			// work around for SQLITE_NULL
 | 
						|
			x := conv.Interface()
 | 
						|
			if z, ok := x.([]byte); ok && z == nil {
 | 
						|
				x = nil
 | 
						|
			}
 | 
						|
 | 
						|
			vals = append(vals, x)
 | 
						|
		}
 | 
						|
 | 
						|
		switch {
 | 
						|
		case argc == 1:
 | 
						|
			err = v.Delete(vals[0])
 | 
						|
 | 
						|
		case argc > 1 && vals[0] == nil:
 | 
						|
			var id int64
 | 
						|
			id, err = v.Insert(vals[1], vals[2:])
 | 
						|
			if err == nil {
 | 
						|
				*pRowid = C.sqlite3_int64(id)
 | 
						|
			}
 | 
						|
 | 
						|
		case argc > 1:
 | 
						|
			err = v.Update(vals[1], vals[2:])
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if err != nil {
 | 
						|
		return mPrintf("%s", err.Error())
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// Module is a "virtual table module", it defines the implementation of a
 | 
						|
// virtual tables. See: http://sqlite.org/c3ref/module.html
 | 
						|
type Module interface {
 | 
						|
	// http://sqlite.org/vtab.html#xcreate
 | 
						|
	Create(c *SQLiteConn, args []string) (VTab, error)
 | 
						|
	// http://sqlite.org/vtab.html#xconnect
 | 
						|
	Connect(c *SQLiteConn, args []string) (VTab, error)
 | 
						|
	// http://sqlite.org/c3ref/create_module.html
 | 
						|
	DestroyModule()
 | 
						|
}
 | 
						|
 | 
						|
// VTab describes a particular instance of the virtual table.
 | 
						|
// See: http://sqlite.org/c3ref/vtab.html
 | 
						|
type VTab interface {
 | 
						|
	// http://sqlite.org/vtab.html#xbestindex
 | 
						|
	BestIndex([]InfoConstraint, []InfoOrderBy) (*IndexResult, error)
 | 
						|
	// http://sqlite.org/vtab.html#xdisconnect
 | 
						|
	Disconnect() error
 | 
						|
	// http://sqlite.org/vtab.html#sqlite3_module.xDestroy
 | 
						|
	Destroy() error
 | 
						|
	// http://sqlite.org/vtab.html#xopen
 | 
						|
	Open() (VTabCursor, error)
 | 
						|
}
 | 
						|
 | 
						|
// VTabUpdater is a type that allows a VTab to be inserted, updated, or
 | 
						|
// deleted.
 | 
						|
// See: https://sqlite.org/vtab.html#xupdate
 | 
						|
type VTabUpdater interface {
 | 
						|
	Delete(interface{}) error
 | 
						|
	Insert(interface{}, []interface{}) (int64, error)
 | 
						|
	Update(interface{}, []interface{}) error
 | 
						|
}
 | 
						|
 | 
						|
// VTabCursor describes cursors that point into the virtual table and are used
 | 
						|
// to loop through the virtual table. See: http://sqlite.org/c3ref/vtab_cursor.html
 | 
						|
type VTabCursor interface {
 | 
						|
	// http://sqlite.org/vtab.html#xclose
 | 
						|
	Close() error
 | 
						|
	// http://sqlite.org/vtab.html#xfilter
 | 
						|
	Filter(idxNum int, idxStr string, vals []interface{}) error
 | 
						|
	// http://sqlite.org/vtab.html#xnext
 | 
						|
	Next() error
 | 
						|
	// http://sqlite.org/vtab.html#xeof
 | 
						|
	EOF() bool
 | 
						|
	// http://sqlite.org/vtab.html#xcolumn
 | 
						|
	Column(c *SQLiteContext, col int) error
 | 
						|
	// http://sqlite.org/vtab.html#xrowid
 | 
						|
	Rowid() (int64, error)
 | 
						|
}
 | 
						|
 | 
						|
// DeclareVTab declares the Schema of a virtual table.
 | 
						|
// See: http://sqlite.org/c3ref/declare_vtab.html
 | 
						|
func (c *SQLiteConn) DeclareVTab(sql string) error {
 | 
						|
	zSQL := C.CString(sql)
 | 
						|
	defer C.free(unsafe.Pointer(zSQL))
 | 
						|
	rv := C.sqlite3_declare_vtab(c.db, zSQL)
 | 
						|
	if rv != C.SQLITE_OK {
 | 
						|
		return c.lastError()
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// CreateModule registers a virtual table implementation.
 | 
						|
// See: http://sqlite.org/c3ref/create_module.html
 | 
						|
func (c *SQLiteConn) CreateModule(moduleName string, module Module) error {
 | 
						|
	mname := C.CString(moduleName)
 | 
						|
	defer C.free(unsafe.Pointer(mname))
 | 
						|
	udm := sqliteModule{c, moduleName, module}
 | 
						|
	rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(newHandle(c, &udm)))
 | 
						|
	if rv != C.SQLITE_OK {
 | 
						|
		return c.lastError()
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 |