summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go')
-rw-r--r--vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go650
1 files changed, 650 insertions, 0 deletions
diff --git a/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go
new file mode 100644
index 0000000000..73d66c00d7
--- /dev/null
+++ b/vendor/github.com/mattn/go-sqlite3/sqlite3_opt_vtable.go
@@ -0,0 +1,650 @@
+// 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
+}