|
- // 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 sqlite_vtable vtable
-
- package sqlite3
-
- /*
- #cgo CFLAGS: -std=gnu99
- #cgo CFLAGS: -DSQLITE_ENABLE_RTREE
- #cgo CFLAGS: -DSQLITE_THREADSAFE
- #cgo CFLAGS: -DSQLITE_ENABLE_FTS3
- #cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS
- #cgo CFLAGS: -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
- }
|