vendor: update sqlite to fix "database is locked" errors (#2116)
closes #2040
upstream commit: acfa601240
tokarchuk/v1.17
parent
a52cd59727
commit
2ef33b5338
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,103 @@ |
||||
// 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.
|
||||
|
||||
package sqlite3 |
||||
|
||||
/* |
||||
|
||||
#ifndef USE_LIBSQLITE3 |
||||
#include <sqlite3-binding.h> |
||||
#else |
||||
#include <sqlite3.h> |
||||
#endif |
||||
#include <stdlib.h> |
||||
// These wrappers are necessary because SQLITE_TRANSIENT
|
||||
// is a pointer constant, and cgo doesn't translate them correctly.
|
||||
|
||||
static inline void my_result_text(sqlite3_context *ctx, char *p, int np) { |
||||
sqlite3_result_text(ctx, p, np, SQLITE_TRANSIENT); |
||||
} |
||||
|
||||
static inline void my_result_blob(sqlite3_context *ctx, void *p, int np) { |
||||
sqlite3_result_blob(ctx, p, np, SQLITE_TRANSIENT); |
||||
} |
||||
*/ |
||||
import "C" |
||||
|
||||
import ( |
||||
"math" |
||||
"reflect" |
||||
"unsafe" |
||||
) |
||||
|
||||
const i64 = unsafe.Sizeof(int(0)) > 4 |
||||
|
||||
// SQLiteContext behave sqlite3_context
|
||||
type SQLiteContext C.sqlite3_context |
||||
|
||||
// ResultBool sets the result of an SQL function.
|
||||
func (c *SQLiteContext) ResultBool(b bool) { |
||||
if b { |
||||
c.ResultInt(1) |
||||
} else { |
||||
c.ResultInt(0) |
||||
} |
||||
} |
||||
|
||||
// ResultBlob sets the result of an SQL function.
|
||||
// See: sqlite3_result_blob, http://sqlite.org/c3ref/result_blob.html
|
||||
func (c *SQLiteContext) ResultBlob(b []byte) { |
||||
if i64 && len(b) > math.MaxInt32 { |
||||
C.sqlite3_result_error_toobig((*C.sqlite3_context)(c)) |
||||
return |
||||
} |
||||
var p *byte |
||||
if len(b) > 0 { |
||||
p = &b[0] |
||||
} |
||||
C.my_result_blob((*C.sqlite3_context)(c), unsafe.Pointer(p), C.int(len(b))) |
||||
} |
||||
|
||||
// ResultDouble sets the result of an SQL function.
|
||||
// See: sqlite3_result_double, http://sqlite.org/c3ref/result_blob.html
|
||||
func (c *SQLiteContext) ResultDouble(d float64) { |
||||
C.sqlite3_result_double((*C.sqlite3_context)(c), C.double(d)) |
||||
} |
||||
|
||||
// ResultInt sets the result of an SQL function.
|
||||
// See: sqlite3_result_int, http://sqlite.org/c3ref/result_blob.html
|
||||
func (c *SQLiteContext) ResultInt(i int) { |
||||
if i64 && (i > math.MaxInt32 || i < math.MinInt32) { |
||||
C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) |
||||
} else { |
||||
C.sqlite3_result_int((*C.sqlite3_context)(c), C.int(i)) |
||||
} |
||||
} |
||||
|
||||
// ResultInt64 sets the result of an SQL function.
|
||||
// See: sqlite3_result_int64, http://sqlite.org/c3ref/result_blob.html
|
||||
func (c *SQLiteContext) ResultInt64(i int64) { |
||||
C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) |
||||
} |
||||
|
||||
// ResultNull sets the result of an SQL function.
|
||||
// See: sqlite3_result_null, http://sqlite.org/c3ref/result_blob.html
|
||||
func (c *SQLiteContext) ResultNull() { |
||||
C.sqlite3_result_null((*C.sqlite3_context)(c)) |
||||
} |
||||
|
||||
// ResultText sets the result of an SQL function.
|
||||
// See: sqlite3_result_text, http://sqlite.org/c3ref/result_blob.html
|
||||
func (c *SQLiteContext) ResultText(s string) { |
||||
h := (*reflect.StringHeader)(unsafe.Pointer(&s)) |
||||
cs, l := (*C.char)(unsafe.Pointer(h.Data)), C.int(h.Len) |
||||
C.my_result_text((*C.sqlite3_context)(c), cs, l) |
||||
} |
||||
|
||||
// ResultZeroblob sets the result of an SQL function.
|
||||
// See: sqlite3_result_zeroblob, http://sqlite.org/c3ref/result_blob.html
|
||||
func (c *SQLiteContext) ResultZeroblob(n int) { |
||||
C.sqlite3_result_zeroblob((*C.sqlite3_context)(c), C.int(n)) |
||||
} |
@ -0,0 +1,69 @@ |
||||
// 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 go1.8
|
||||
|
||||
package sqlite3 |
||||
|
||||
import ( |
||||
"database/sql/driver" |
||||
"errors" |
||||
|
||||
"context" |
||||
) |
||||
|
||||
// Ping implement Pinger.
|
||||
func (c *SQLiteConn) Ping(ctx context.Context) error { |
||||
if c.db == nil { |
||||
return errors.New("Connection was closed") |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// QueryContext implement QueryerContext.
|
||||
func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { |
||||
list := make([]namedValue, len(args)) |
||||
for i, nv := range args { |
||||
list[i] = namedValue(nv) |
||||
} |
||||
return c.query(ctx, query, list) |
||||
} |
||||
|
||||
// ExecContext implement ExecerContext.
|
||||
func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { |
||||
list := make([]namedValue, len(args)) |
||||
for i, nv := range args { |
||||
list[i] = namedValue(nv) |
||||
} |
||||
return c.exec(ctx, query, list) |
||||
} |
||||
|
||||
// PrepareContext implement ConnPrepareContext.
|
||||
func (c *SQLiteConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { |
||||
return c.prepare(ctx, query) |
||||
} |
||||
|
||||
// BeginTx implement ConnBeginTx.
|
||||
func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { |
||||
return c.begin(ctx) |
||||
} |
||||
|
||||
// QueryContext implement QueryerContext.
|
||||
func (s *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { |
||||
list := make([]namedValue, len(args)) |
||||
for i, nv := range args { |
||||
list[i] = namedValue(nv) |
||||
} |
||||
return s.query(ctx, list) |
||||
} |
||||
|
||||
// ExecContext implement ExecerContext.
|
||||
func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { |
||||
list := make([]namedValue, len(args)) |
||||
for i, nv := range args { |
||||
list[i] = namedValue(nv) |
||||
} |
||||
return s.exec(ctx, list) |
||||
} |
@ -0,0 +1,57 @@ |
||||
package sqlite3 |
||||
|
||||
/* |
||||
#ifndef USE_LIBSQLITE3 |
||||
#include <sqlite3-binding.h> |
||||
#else |
||||
#include <sqlite3.h> |
||||
#endif |
||||
*/ |
||||
import "C" |
||||
import ( |
||||
"reflect" |
||||
"time" |
||||
) |
||||
|
||||
// ColumnTypeDatabaseTypeName implement RowsColumnTypeDatabaseTypeName.
|
||||
func (rc *SQLiteRows) ColumnTypeDatabaseTypeName(i int) string { |
||||
return C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) |
||||
} |
||||
|
||||
/* |
||||
func (rc *SQLiteRows) ColumnTypeLength(index int) (length int64, ok bool) { |
||||
return 0, false |
||||
} |
||||
|
||||
func (rc *SQLiteRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { |
||||
return 0, 0, false |
||||
} |
||||
*/ |
||||
|
||||
// ColumnTypeNullable implement RowsColumnTypeNullable.
|
||||
func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) { |
||||
return true, true |
||||
} |
||||
|
||||
// ColumnTypeScanType implement RowsColumnTypeScanType.
|
||||
func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type { |
||||
switch C.sqlite3_column_type(rc.s.s, C.int(i)) { |
||||
case C.SQLITE_INTEGER: |
||||
switch C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) { |
||||
case "timestamp", "datetime", "date": |
||||
return reflect.TypeOf(time.Time{}) |
||||
case "boolean": |
||||
return reflect.TypeOf(false) |
||||
} |
||||
return reflect.TypeOf(int64(0)) |
||||
case C.SQLITE_FLOAT: |
||||
return reflect.TypeOf(float64(0)) |
||||
case C.SQLITE_BLOB: |
||||
return reflect.SliceOf(reflect.TypeOf(byte(0))) |
||||
case C.SQLITE_NULL: |
||||
return reflect.TypeOf(nil) |
||||
case C.SQLITE_TEXT: |
||||
return reflect.TypeOf("") |
||||
} |
||||
return reflect.SliceOf(reflect.TypeOf(byte(0))) |
||||
} |
@ -0,0 +1,646 @@ |
||||
// 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 |
||||
} |
@ -1,9 +0,0 @@ |
||||
// +build !trace
|
||||
|
||||
package sqlite3 |
||||
|
||||
import "errors" |
||||
|
||||
func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool) error { |
||||
return errors.New("This feature is not implemented") |
||||
} |
Loading…
Reference in new issue