updated vendor
This commit is contained in:
+481
-163
@@ -69,13 +69,49 @@ _sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zV
|
||||
}
|
||||
|
||||
static int
|
||||
_sqlite3_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
|
||||
return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
|
||||
_sqlite3_bind_text(sqlite3_stmt *stmt, int n, char *p, sqlite3_uint64 np) {
|
||||
return sqlite3_bind_text64(stmt, n, p, np, SQLITE_TRANSIENT, SQLITE_UTF8);
|
||||
}
|
||||
|
||||
static int
|
||||
_sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
|
||||
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
|
||||
_sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, sqlite3_uint64 np) {
|
||||
return sqlite3_bind_blob64(stmt, n, p, np, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int typ;
|
||||
sqlite3_int64 i64;
|
||||
double f64;
|
||||
const void *ptr;
|
||||
int n;
|
||||
} sqlite3_go_col;
|
||||
|
||||
static void
|
||||
_sqlite3_column_values(sqlite3_stmt *stmt, int ncol, sqlite3_go_col *cols) {
|
||||
for (int i = 0; i < ncol; i++) {
|
||||
sqlite3_go_col *col = &cols[i];
|
||||
col->typ = sqlite3_column_type(stmt, i);
|
||||
col->ptr = 0;
|
||||
col->n = 0;
|
||||
switch (col->typ) {
|
||||
case SQLITE_INTEGER:
|
||||
col->i64 = sqlite3_column_int64(stmt, i);
|
||||
break;
|
||||
case SQLITE_FLOAT:
|
||||
col->f64 = sqlite3_column_double(stmt, i);
|
||||
break;
|
||||
case SQLITE_BLOB:
|
||||
col->ptr = sqlite3_column_blob(stmt, i);
|
||||
col->n = sqlite3_column_bytes(stmt, i);
|
||||
break;
|
||||
case SQLITE_TEXT:
|
||||
col->ptr = sqlite3_column_text(stmt, i);
|
||||
col->n = sqlite3_column_bytes(stmt, i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -90,10 +126,57 @@ _sqlite3_exec(sqlite3* db, const char* pcmd, long long* rowid, long long* change
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Combined reset + clear_bindings in a single C call to reduce CGO crossings.
|
||||
static int
|
||||
_sqlite3_reset_clear(sqlite3_stmt* stmt)
|
||||
{
|
||||
int rv = sqlite3_reset(stmt);
|
||||
sqlite3_clear_bindings(stmt);
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
extern int _sqlite3_step_blocking(sqlite3_stmt *stmt);
|
||||
extern int _sqlite3_step_row_blocking(sqlite3_stmt* stmt, long long* rowid, long long* changes);
|
||||
extern int _sqlite3_prepare_v2_blocking(sqlite3 *db, const char *zSql, int nBytes, sqlite3_stmt **ppStmt, const char **pzTail);
|
||||
#endif
|
||||
|
||||
// Combined prepare+step+finalize for simple exec without parameters.
|
||||
// Reduces CGO crossings from ~6 to 1 for the common no-args exec case.
|
||||
static int
|
||||
_sqlite3_exec_no_args(sqlite3* db, const char* zSql, int nBytes, long long* rowid, long long* changes, const char** pzTail)
|
||||
{
|
||||
sqlite3_stmt *stmt = 0;
|
||||
const char *tail = 0;
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
int rv = _sqlite3_prepare_v2_blocking(db, zSql, nBytes, &stmt, &tail);
|
||||
#else
|
||||
int rv = sqlite3_prepare_v2(db, zSql, nBytes, &stmt, &tail);
|
||||
#endif
|
||||
if (rv != SQLITE_OK) {
|
||||
*pzTail = 0;
|
||||
return rv;
|
||||
}
|
||||
if (stmt == 0) {
|
||||
// Empty statement
|
||||
*rowid = 0;
|
||||
*changes = 0;
|
||||
*pzTail = tail;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
rv = _sqlite3_step_row_blocking(stmt, rowid, changes);
|
||||
#else
|
||||
rv = sqlite3_step(stmt);
|
||||
*rowid = (long long) sqlite3_last_insert_rowid(db);
|
||||
*changes = (long long) sqlite3_changes(db);
|
||||
#endif
|
||||
sqlite3_finalize(stmt);
|
||||
*pzTail = tail;
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
|
||||
|
||||
static int
|
||||
_sqlite3_step_internal(sqlite3_stmt *stmt)
|
||||
@@ -210,6 +293,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"runtime"
|
||||
@@ -361,12 +445,20 @@ type SQLiteDriver struct {
|
||||
|
||||
// SQLiteConn implements driver.Conn.
|
||||
type SQLiteConn struct {
|
||||
mu sync.Mutex
|
||||
db *C.sqlite3
|
||||
loc *time.Location
|
||||
txlock string
|
||||
funcs []*functionInfo
|
||||
aggregators []*aggInfo
|
||||
mu sync.Mutex
|
||||
db *C.sqlite3
|
||||
loc *time.Location
|
||||
txlock string
|
||||
funcs []*functionInfo
|
||||
aggregators []*aggInfo
|
||||
// Prepared-statement cache. The slice is allocated at Open with a
|
||||
// fixed capacity equal to the configured cache size; cap bounds the
|
||||
// cache, len is the live count, and entries are ordered LRU-first
|
||||
// (index 0 is the oldest, the tail is most recently put). Access
|
||||
// requires mu; stmtCacheEnabled is immutable after Open and is the
|
||||
// only field safe to read without the lock.
|
||||
stmtCache []*SQLiteStmt
|
||||
stmtCacheEnabled bool
|
||||
}
|
||||
|
||||
// SQLiteTx implements driver.Tx.
|
||||
@@ -376,12 +468,14 @@ type SQLiteTx struct {
|
||||
|
||||
// SQLiteStmt implements driver.Stmt.
|
||||
type SQLiteStmt struct {
|
||||
mu sync.Mutex
|
||||
c *SQLiteConn
|
||||
s *C.sqlite3_stmt
|
||||
t string
|
||||
closed bool
|
||||
cls bool // True if the statement was created by SQLiteConn.Query
|
||||
mu sync.Mutex
|
||||
c *SQLiteConn
|
||||
s *C.sqlite3_stmt
|
||||
t string
|
||||
closed bool
|
||||
cls bool // True if the statement was created by SQLiteConn.Query
|
||||
namedParams map[string][3]int
|
||||
cacheKey string
|
||||
}
|
||||
|
||||
// SQLiteResult implements sql.Result.
|
||||
@@ -397,6 +491,7 @@ type SQLiteRows struct {
|
||||
cls bool // True if we need to close the parent statement in Close
|
||||
cols []string
|
||||
decltype []string
|
||||
colvals *C.sqlite3_go_col
|
||||
ctx context.Context // no better alternative to pass context into Next() method
|
||||
closemu sync.Mutex
|
||||
}
|
||||
@@ -847,45 +942,29 @@ func lastError(db *C.sqlite3) error {
|
||||
|
||||
// Exec implements Execer.
|
||||
func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
||||
list := make([]driver.NamedValue, len(args))
|
||||
for i, v := range args {
|
||||
list[i] = driver.NamedValue{
|
||||
Ordinal: i + 1,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
return c.exec(context.Background(), query, list)
|
||||
return c.exec(context.Background(), query, valueToNamedValue(args))
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) exec(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
||||
// Fast path: no args, no context cancellation → single CGO call per statement
|
||||
if len(args) == 0 && ctx.Done() == nil {
|
||||
return c.execNoArgs(query)
|
||||
}
|
||||
|
||||
start := 0
|
||||
for {
|
||||
s, err := c.prepare(ctx, query)
|
||||
s, err := c.prepareWithCache(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var res driver.Result
|
||||
if s.(*SQLiteStmt).s != nil {
|
||||
stmtArgs := make([]driver.NamedValue, 0, len(args))
|
||||
na := s.NumInput()
|
||||
if len(args)-start < na {
|
||||
s.Close()
|
||||
return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args))
|
||||
}
|
||||
// consume the number of arguments used in the current
|
||||
// statement and append all named arguments not
|
||||
// contained therein
|
||||
if na > 0 {
|
||||
stmtArgs = append(stmtArgs, args[start:start+na]...)
|
||||
for i := range args {
|
||||
if (i < start || i >= na) && args[i].Name != "" {
|
||||
stmtArgs = append(stmtArgs, args[i])
|
||||
}
|
||||
}
|
||||
for i := range stmtArgs {
|
||||
stmtArgs[i].Ordinal = i + 1
|
||||
}
|
||||
}
|
||||
stmtArgs := stmtArgs(args, start, na)
|
||||
res, err = s.(*SQLiteStmt).exec(ctx, stmtArgs)
|
||||
if err != nil && err != driver.ErrSkip {
|
||||
s.Close()
|
||||
@@ -906,56 +985,74 @@ func (c *SQLiteConn) exec(ctx context.Context, query string, args []driver.Named
|
||||
}
|
||||
}
|
||||
|
||||
// execNoArgs executes a query with no parameters in a single CGO call per statement.
|
||||
func (c *SQLiteConn) execNoArgs(query string) (driver.Result, error) {
|
||||
var res *SQLiteResult
|
||||
for len(query) > 0 {
|
||||
var rowid, changes C.longlong
|
||||
var tail *C.char
|
||||
pquery := C.CString(query)
|
||||
rv := C._sqlite3_exec_no_args(c.db, pquery, C.int(len(query)), &rowid, &changes, &tail)
|
||||
if tail != nil && *tail != '\000' {
|
||||
query = strings.TrimSpace(C.GoString(tail))
|
||||
} else {
|
||||
query = ""
|
||||
}
|
||||
C.free(unsafe.Pointer(pquery))
|
||||
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
|
||||
return nil, c.lastError()
|
||||
}
|
||||
res = &SQLiteResult{id: int64(rowid), changes: int64(changes)}
|
||||
}
|
||||
if res == nil {
|
||||
res = &SQLiteResult{0, 0}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Query implements Queryer.
|
||||
func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
||||
list := make([]driver.NamedValue, len(args))
|
||||
for i, v := range args {
|
||||
list[i] = driver.NamedValue{
|
||||
Ordinal: i + 1,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
return c.query(context.Background(), query, list)
|
||||
return c.query(context.Background(), query, valueToNamedValue(args))
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) query(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
||||
start := 0
|
||||
for {
|
||||
stmtArgs := make([]driver.NamedValue, 0, len(args))
|
||||
s, err := c.prepare(ctx, query)
|
||||
s, err := c.prepareWithCache(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.(*SQLiteStmt).cls = true
|
||||
ss := s.(*SQLiteStmt)
|
||||
ss.cls = true
|
||||
// sqlite3_prepare_v2 returns SQLITE_OK with a NULL statement handle
|
||||
// when the input is empty or contains only whitespace/comments.
|
||||
if ss.s == nil {
|
||||
tail := ss.t
|
||||
ss.Close()
|
||||
if tail == "" {
|
||||
return &SQLiteRows{cls: true, ctx: ctx}, nil
|
||||
}
|
||||
query = tail
|
||||
continue
|
||||
}
|
||||
na := s.NumInput()
|
||||
if len(args)-start < na {
|
||||
s.Close()
|
||||
ss.Close()
|
||||
return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)-start)
|
||||
}
|
||||
// consume the number of arguments used in the current
|
||||
// statement and append all named arguments not contained
|
||||
// therein
|
||||
stmtArgs = append(stmtArgs, args[start:start+na]...)
|
||||
for i := range args {
|
||||
if (i < start || i >= na) && args[i].Name != "" {
|
||||
stmtArgs = append(stmtArgs, args[i])
|
||||
}
|
||||
}
|
||||
for i := range stmtArgs {
|
||||
stmtArgs[i].Ordinal = i + 1
|
||||
}
|
||||
rows, err := s.(*SQLiteStmt).query(ctx, stmtArgs)
|
||||
stmtArgs := stmtArgs(args, start, na)
|
||||
rows, err := ss.query(ctx, stmtArgs)
|
||||
if err != nil && err != driver.ErrSkip {
|
||||
s.Close()
|
||||
ss.Close()
|
||||
return rows, err
|
||||
}
|
||||
start += na
|
||||
tail := s.(*SQLiteStmt).t
|
||||
tail := ss.t
|
||||
if tail == "" {
|
||||
return rows, nil
|
||||
}
|
||||
rows.Close()
|
||||
s.Close()
|
||||
ss.Close()
|
||||
query = tail
|
||||
}
|
||||
}
|
||||
@@ -1109,6 +1206,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
|
||||
writableSchema := -1
|
||||
vfsName := ""
|
||||
var cacheSize *int64
|
||||
stmtCacheSize := 0
|
||||
|
||||
pos := strings.IndexRune(dsn, '?')
|
||||
if pos >= 1 {
|
||||
@@ -1444,6 +1542,20 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
|
||||
cacheSize = &iv
|
||||
}
|
||||
|
||||
// _stmt_cache_size sets the maximum number of prepared statements
|
||||
// cached per connection. Note that sql.DB is a connection pool, so
|
||||
// each connection maintains its own independent cache.
|
||||
if val := params.Get("_stmt_cache_size"); val != "" {
|
||||
iv, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Invalid _stmt_cache_size: %v: %v", val, err)
|
||||
}
|
||||
if iv < 0 {
|
||||
return nil, fmt.Errorf("Invalid _stmt_cache_size: %v, expecting non-negative integer", val)
|
||||
}
|
||||
stmtCacheSize = iv
|
||||
}
|
||||
|
||||
if val := params.Get("vfs"); val != "" {
|
||||
vfsName = val
|
||||
}
|
||||
@@ -1517,6 +1629,10 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
|
||||
|
||||
// Create connection to SQLite
|
||||
conn := &SQLiteConn{db: db, loc: loc, txlock: txlock}
|
||||
if stmtCacheSize > 0 {
|
||||
conn.stmtCache = make([]*SQLiteStmt, 0, stmtCacheSize)
|
||||
conn.stmtCacheEnabled = true
|
||||
}
|
||||
|
||||
// Password Cipher has to be registered before authentication
|
||||
if len(authCrypt) > 0 {
|
||||
@@ -1789,6 +1905,7 @@ func (c *SQLiteConn) Close() error {
|
||||
return nil
|
||||
}
|
||||
runtime.SetFinalizer(c, nil)
|
||||
c.closeCachedStmtsLocked()
|
||||
rv := C.sqlite3_close_v2(c.db)
|
||||
if rv != C.SQLITE_OK {
|
||||
return lastError(c.db)
|
||||
@@ -1807,6 +1924,88 @@ func (c *SQLiteConn) dbConnOpen() bool {
|
||||
return c.db != nil
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) takeCachedStmt(query string) *SQLiteStmt {
|
||||
if c == nil || query == "" || !c.stmtCacheEnabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.db == nil {
|
||||
return nil
|
||||
}
|
||||
// Scan from the MRU end (tail) so that a stmt put just before is
|
||||
// found immediately.
|
||||
for i := len(c.stmtCache) - 1; i >= 0; i-- {
|
||||
s := c.stmtCache[i]
|
||||
if s.cacheKey != query {
|
||||
continue
|
||||
}
|
||||
n := len(c.stmtCache)
|
||||
copy(c.stmtCache[i:n-1], c.stmtCache[i+1:n])
|
||||
c.stmtCache[n-1] = nil
|
||||
c.stmtCache = c.stmtCache[:n-1]
|
||||
// The stmt was marked closed by Close before being cached, and
|
||||
// cls may have been set if Query opened it; reset both so the
|
||||
// caller gets a stmt equivalent to a fresh Prepare.
|
||||
s.closed = false
|
||||
s.cls = false
|
||||
return s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) putCachedStmt(s *SQLiteStmt) bool {
|
||||
if c == nil || s == nil || s.s == nil || s.cacheKey == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.db == nil {
|
||||
return false
|
||||
}
|
||||
rv := C._sqlite3_reset_clear(s.s)
|
||||
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
|
||||
return false
|
||||
}
|
||||
// If full, finalize the LRU entry at index 0 and shift left; the
|
||||
// freed tail slot is immediately reused by the append below.
|
||||
if len(c.stmtCache) == cap(c.stmtCache) {
|
||||
finalizeCachedStmt(c.stmtCache[0])
|
||||
copy(c.stmtCache, c.stmtCache[1:])
|
||||
c.stmtCache = c.stmtCache[:len(c.stmtCache)-1]
|
||||
}
|
||||
c.stmtCache = append(c.stmtCache, s)
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) closeCachedStmtsLocked() {
|
||||
for i, s := range c.stmtCache {
|
||||
c.stmtCache[i] = nil
|
||||
finalizeCachedStmt(s)
|
||||
}
|
||||
c.stmtCache = c.stmtCache[:0]
|
||||
}
|
||||
|
||||
// finalizeCachedStmt tears down a stmt that was sitting in the connection's
|
||||
// stmt cache. The caller must hold c.mu. It is safe to pass a nil stmt or a
|
||||
// stmt whose handle has already been released.
|
||||
func finalizeCachedStmt(s *SQLiteStmt) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
runtime.SetFinalizer(s, nil)
|
||||
if s.s != nil {
|
||||
C.sqlite3_finalize(s.s)
|
||||
s.s = nil
|
||||
}
|
||||
s.c = nil
|
||||
s.closed = true
|
||||
}
|
||||
|
||||
// Prepare the query string. Return a new statement.
|
||||
func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) {
|
||||
return c.prepare(context.Background(), query)
|
||||
@@ -1817,7 +2016,7 @@ func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, er
|
||||
defer C.free(unsafe.Pointer(pquery))
|
||||
var s *C.sqlite3_stmt
|
||||
var tail *C.char
|
||||
rv := C._sqlite3_prepare_v2_internal(c.db, pquery, C.int(-1), &s, &tail)
|
||||
rv := C._sqlite3_prepare_v2_internal(c.db, pquery, C.int(len(query)), &s, &tail)
|
||||
if rv != C.SQLITE_OK {
|
||||
return nil, c.lastError()
|
||||
}
|
||||
@@ -1830,6 +2029,21 @@ func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, er
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
func (c *SQLiteConn) prepareWithCache(ctx context.Context, query string) (driver.Stmt, error) {
|
||||
if stmt := c.takeCachedStmt(query); stmt != nil {
|
||||
return stmt, nil
|
||||
}
|
||||
stmt, err := c.prepare(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ss := stmt.(*SQLiteStmt)
|
||||
if ss.t == "" && c.stmtCacheEnabled {
|
||||
ss.cacheKey = query
|
||||
}
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
// Run-Time Limit Categories.
|
||||
// See: http://www.sqlite.org/c3ref/c_limit_attached.html
|
||||
const (
|
||||
@@ -1938,11 +2152,18 @@ func (s *SQLiteStmt) Close() error {
|
||||
runtime.SetFinalizer(s, nil)
|
||||
conn := s.c
|
||||
stmt := s.s
|
||||
s.s = nil
|
||||
s.c = nil
|
||||
if stmt == nil {
|
||||
s.c = nil
|
||||
return nil
|
||||
}
|
||||
if !conn.dbConnOpen() {
|
||||
return errors.New("sqlite statement with already closed database connection")
|
||||
}
|
||||
if s.cacheKey != "" && conn.putCachedStmt(s) {
|
||||
return nil
|
||||
}
|
||||
s.s = nil
|
||||
s.c = nil
|
||||
rv := C.sqlite3_finalize(stmt)
|
||||
if rv != C.SQLITE_OK {
|
||||
return conn.lastError()
|
||||
@@ -1957,68 +2178,141 @@ func (s *SQLiteStmt) NumInput() int {
|
||||
|
||||
var placeHolder = []byte{0}
|
||||
|
||||
func bindText(s *C.sqlite3_stmt, n C.int, v string) C.int {
|
||||
if len(v) == 0 {
|
||||
return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(&placeHolder[0])), C.sqlite3_uint64(0))
|
||||
}
|
||||
return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(unsafe.StringData(v))), C.sqlite3_uint64(len(v)))
|
||||
}
|
||||
|
||||
func bindValue(s *C.sqlite3_stmt, n C.int, value driver.Value) C.int {
|
||||
switch v := value.(type) {
|
||||
case nil:
|
||||
return C.sqlite3_bind_null(s, n)
|
||||
case string:
|
||||
return bindText(s, n, v)
|
||||
case int64:
|
||||
return C.sqlite3_bind_int64(s, n, C.sqlite3_int64(v))
|
||||
case bool:
|
||||
if v {
|
||||
return C.sqlite3_bind_int(s, n, 1)
|
||||
}
|
||||
return C.sqlite3_bind_int(s, n, 0)
|
||||
case float64:
|
||||
return C.sqlite3_bind_double(s, n, C.double(v))
|
||||
case []byte:
|
||||
if v == nil {
|
||||
return C.sqlite3_bind_null(s, n)
|
||||
}
|
||||
ln := len(v)
|
||||
if ln == 0 {
|
||||
v = placeHolder
|
||||
}
|
||||
return C._sqlite3_bind_blob(s, n, unsafe.Pointer(&v[0]), C.sqlite3_uint64(ln))
|
||||
case time.Time:
|
||||
var buf [64]byte
|
||||
b := v.AppendFormat(buf[:0], SQLiteTimestampFormats[0])
|
||||
if len(b) == 0 {
|
||||
return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(&placeHolder[0])), C.sqlite3_uint64(0))
|
||||
}
|
||||
return C._sqlite3_bind_text(s, n, (*C.char)(unsafe.Pointer(&b[0])), C.sqlite3_uint64(len(b)))
|
||||
default:
|
||||
return C.SQLITE_MISUSE
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SQLiteStmt) bindNamedIndices(name string) [3]int {
|
||||
if s.namedParams == nil {
|
||||
s.namedParams = make(map[string][3]int)
|
||||
} else if indices, ok := s.namedParams[name]; ok {
|
||||
return indices
|
||||
}
|
||||
|
||||
// Build ":name\0" once and rewrite prefix byte to avoid 3 C.CString allocs.
|
||||
buf := make([]byte, 1+len(name)+1) // prefix + name + null terminator
|
||||
copy(buf[1:], name)
|
||||
buf[len(buf)-1] = 0
|
||||
cname := (*C.char)(unsafe.Pointer(&buf[0]))
|
||||
|
||||
var indices [3]int
|
||||
prefixes := [3]byte{':', '@', '$'}
|
||||
for i, p := range prefixes {
|
||||
buf[0] = p
|
||||
indices[i] = int(C.sqlite3_bind_parameter_index(s.s, cname))
|
||||
}
|
||||
s.namedParams[name] = indices
|
||||
return indices
|
||||
}
|
||||
|
||||
func stmtArgs(args []driver.NamedValue, start, na int) []driver.NamedValue {
|
||||
if na == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
end := start + na
|
||||
hasNamedOutside := false
|
||||
for i := range args {
|
||||
if args[i].Name != "" && (i < start || i >= end) {
|
||||
hasNamedOutside = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if start == 0 && !hasNamedOutside {
|
||||
return args[start:end]
|
||||
}
|
||||
|
||||
stmtArgs := make([]driver.NamedValue, 0, len(args))
|
||||
stmtArgs = append(stmtArgs, args[start:end]...)
|
||||
for i := range args {
|
||||
if args[i].Name != "" && (i < start || i >= end) {
|
||||
stmtArgs = append(stmtArgs, args[i])
|
||||
}
|
||||
}
|
||||
for i := range stmtArgs {
|
||||
stmtArgs[i].Ordinal = i + 1
|
||||
}
|
||||
return stmtArgs
|
||||
}
|
||||
|
||||
func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
|
||||
rv := C.sqlite3_reset(s.s)
|
||||
rv := C._sqlite3_reset_clear(s.s)
|
||||
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
|
||||
return s.c.lastError()
|
||||
}
|
||||
|
||||
C.sqlite3_clear_bindings(s.s)
|
||||
|
||||
bindIndices := make([][3]int, len(args))
|
||||
prefixes := []string{":", "@", "$"}
|
||||
for i, v := range args {
|
||||
bindIndices[i][0] = v.Ordinal
|
||||
if v.Name != "" {
|
||||
for j := range prefixes {
|
||||
cname := C.CString(prefixes[j] + v.Name)
|
||||
bindIndices[i][j] = int(C.sqlite3_bind_parameter_index(s.s, cname))
|
||||
C.free(unsafe.Pointer(cname))
|
||||
}
|
||||
args[i].Ordinal = bindIndices[i][0]
|
||||
hasNamed := false
|
||||
for i := range args {
|
||||
if args[i].Name != "" {
|
||||
hasNamed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for i, arg := range args {
|
||||
for j := range bindIndices[i] {
|
||||
if bindIndices[i][j] == 0 {
|
||||
if !hasNamed {
|
||||
for _, arg := range args {
|
||||
n := C.int(arg.Ordinal)
|
||||
rv = bindValue(s.s, n, arg.Value)
|
||||
if rv != C.SQLITE_OK {
|
||||
return s.c.lastError()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, arg := range args {
|
||||
if arg.Name == "" {
|
||||
rv = bindValue(s.s, C.int(arg.Ordinal), arg.Value)
|
||||
if rv != C.SQLITE_OK {
|
||||
return s.c.lastError()
|
||||
}
|
||||
continue
|
||||
}
|
||||
indices := s.bindNamedIndices(arg.Name)
|
||||
for _, idx := range indices {
|
||||
if idx == 0 {
|
||||
continue
|
||||
}
|
||||
n := C.int(bindIndices[i][j])
|
||||
switch v := arg.Value.(type) {
|
||||
case nil:
|
||||
rv = C.sqlite3_bind_null(s.s, n)
|
||||
case string:
|
||||
if len(v) == 0 {
|
||||
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&placeHolder[0])), C.int(0))
|
||||
} else {
|
||||
b := []byte(v)
|
||||
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b)))
|
||||
}
|
||||
case int64:
|
||||
rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v))
|
||||
case bool:
|
||||
if v {
|
||||
rv = C.sqlite3_bind_int(s.s, n, 1)
|
||||
} else {
|
||||
rv = C.sqlite3_bind_int(s.s, n, 0)
|
||||
}
|
||||
case float64:
|
||||
rv = C.sqlite3_bind_double(s.s, n, C.double(v))
|
||||
case []byte:
|
||||
if v == nil {
|
||||
rv = C.sqlite3_bind_null(s.s, n)
|
||||
} else {
|
||||
ln := len(v)
|
||||
if ln == 0 {
|
||||
v = placeHolder
|
||||
}
|
||||
rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(ln))
|
||||
}
|
||||
case time.Time:
|
||||
b := []byte(v.Format(SQLiteTimestampFormats[0]))
|
||||
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b)))
|
||||
}
|
||||
rv = bindValue(s.s, C.int(idx), arg.Value)
|
||||
if rv != C.SQLITE_OK {
|
||||
return s.c.lastError()
|
||||
}
|
||||
@@ -2029,14 +2323,7 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
|
||||
|
||||
// Query the statement with arguments. Return records.
|
||||
func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||
list := make([]driver.NamedValue, len(args))
|
||||
for i, v := range args {
|
||||
list[i] = driver.NamedValue{
|
||||
Ordinal: i + 1,
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
return s.query(context.Background(), list)
|
||||
return s.query(context.Background(), valueToNamedValue(args))
|
||||
}
|
||||
|
||||
func (s *SQLiteStmt) query(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
||||
@@ -2050,8 +2337,15 @@ func (s *SQLiteStmt) query(ctx context.Context, args []driver.NamedValue) (drive
|
||||
cls: s.cls,
|
||||
cols: nil,
|
||||
decltype: nil,
|
||||
colvals: nil,
|
||||
ctx: ctx,
|
||||
}
|
||||
if rows.nc > 0 {
|
||||
rows.colvals = (*C.sqlite3_go_col)(C.malloc(C.size_t(rows.nc) * C.size_t(unsafe.Sizeof(C.sqlite3_go_col{}))))
|
||||
if rows.colvals == nil {
|
||||
return nil, errors.New("sqlite3: failed to allocate row buffer")
|
||||
}
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
}
|
||||
@@ -2068,6 +2362,10 @@ func (r *SQLiteResult) RowsAffected() (int64, error) {
|
||||
|
||||
// Exec execute the statement with arguments. Return result object.
|
||||
func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
return s.exec(context.Background(), valueToNamedValue(args))
|
||||
}
|
||||
|
||||
func valueToNamedValue(args []driver.Value) []driver.NamedValue {
|
||||
list := make([]driver.NamedValue, len(args))
|
||||
for i, v := range args {
|
||||
list[i] = driver.NamedValue{
|
||||
@@ -2075,7 +2373,7 @@ func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
return s.exec(context.Background(), list)
|
||||
return list
|
||||
}
|
||||
|
||||
func isInterruptErr(err error) bool {
|
||||
@@ -2092,38 +2390,35 @@ func (s *SQLiteStmt) exec(ctx context.Context, args []driver.NamedValue) (driver
|
||||
return s.execSync(args)
|
||||
}
|
||||
|
||||
type result struct {
|
||||
r driver.Result
|
||||
err error
|
||||
}
|
||||
resultCh := make(chan result)
|
||||
defer close(resultCh)
|
||||
sema := make(chan struct{})
|
||||
var r driver.Result
|
||||
var err error
|
||||
go func() {
|
||||
r, err := s.execSync(args)
|
||||
resultCh <- result{r, err}
|
||||
r, err = s.execSync(args)
|
||||
close(sema)
|
||||
}()
|
||||
var rv result
|
||||
select {
|
||||
case rv = <-resultCh:
|
||||
case <-sema:
|
||||
return r, err
|
||||
case <-ctx.Done():
|
||||
select {
|
||||
case rv = <-resultCh: // no need to interrupt, operation completed in db
|
||||
case <-sema: // no need to interrupt, operation completed in db
|
||||
return r, err
|
||||
default:
|
||||
// this is still racy and can be no-op if executed between sqlite3_* calls in execSync.
|
||||
C.sqlite3_interrupt(s.c.db)
|
||||
rv = <-resultCh // wait for goroutine completed
|
||||
if isInterruptErr(rv.err) {
|
||||
<-sema // wait for goroutine completed
|
||||
if isInterruptErr(err) {
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
}
|
||||
return rv.r, rv.err
|
||||
}
|
||||
|
||||
func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) {
|
||||
if err := s.bind(args); err != nil {
|
||||
C.sqlite3_reset(s.s)
|
||||
C.sqlite3_clear_bindings(s.s)
|
||||
C._sqlite3_reset_clear(s.s)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2131,8 +2426,7 @@ func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) {
|
||||
rv := C._sqlite3_step_row_internal(s.s, &rowid, &changes)
|
||||
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
|
||||
err := s.c.lastError()
|
||||
C.sqlite3_reset(s.s)
|
||||
C.sqlite3_clear_bindings(s.s)
|
||||
C._sqlite3_reset_clear(s.s)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2152,9 +2446,17 @@ func (rc *SQLiteRows) Close() error {
|
||||
defer rc.closemu.Unlock()
|
||||
s := rc.s
|
||||
if s == nil {
|
||||
if rc.colvals != nil {
|
||||
C.free(unsafe.Pointer(rc.colvals))
|
||||
rc.colvals = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
rc.s = nil // remove reference to SQLiteStmt
|
||||
if rc.colvals != nil {
|
||||
C.free(unsafe.Pointer(rc.colvals))
|
||||
rc.colvals = nil
|
||||
}
|
||||
s.mu.Lock()
|
||||
if s.closed {
|
||||
s.mu.Unlock()
|
||||
@@ -2175,6 +2477,9 @@ func (rc *SQLiteRows) Close() error {
|
||||
|
||||
// Columns return column names.
|
||||
func (rc *SQLiteRows) Columns() []string {
|
||||
if rc.s == nil {
|
||||
return rc.cols
|
||||
}
|
||||
rc.s.mu.Lock()
|
||||
defer rc.s.mu.Unlock()
|
||||
if rc.s.s != nil && int(rc.nc) != len(rc.cols) {
|
||||
@@ -2198,6 +2503,9 @@ func (rc *SQLiteRows) declTypes() []string {
|
||||
|
||||
// DeclTypes return column types.
|
||||
func (rc *SQLiteRows) DeclTypes() []string {
|
||||
if rc.s == nil {
|
||||
return rc.decltype
|
||||
}
|
||||
rc.s.mu.Lock()
|
||||
defer rc.s.mu.Unlock()
|
||||
return rc.declTypes()
|
||||
@@ -2205,6 +2513,9 @@ func (rc *SQLiteRows) DeclTypes() []string {
|
||||
|
||||
// Next move cursor to next. Attempts to honor context timeout from QueryContext call.
|
||||
func (rc *SQLiteRows) Next(dest []driver.Value) error {
|
||||
if rc.s == nil {
|
||||
return io.EOF
|
||||
}
|
||||
rc.s.mu.Lock()
|
||||
defer rc.s.mu.Unlock()
|
||||
|
||||
@@ -2215,21 +2526,22 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
|
||||
if rc.ctx.Done() == nil {
|
||||
return rc.nextSyncLocked(dest)
|
||||
}
|
||||
resultCh := make(chan error)
|
||||
defer close(resultCh)
|
||||
sema := make(chan struct{})
|
||||
var err error
|
||||
go func() {
|
||||
resultCh <- rc.nextSyncLocked(dest)
|
||||
err = rc.nextSyncLocked(dest)
|
||||
close(sema)
|
||||
}()
|
||||
select {
|
||||
case err := <-resultCh:
|
||||
case <-sema:
|
||||
return err
|
||||
case <-rc.ctx.Done():
|
||||
select {
|
||||
case <-resultCh: // no need to interrupt
|
||||
case <-sema: // no need to interrupt
|
||||
default:
|
||||
// this is still racy and can be no-op if executed between sqlite3_* calls in nextSyncLocked.
|
||||
C.sqlite3_interrupt(rc.s.c.db)
|
||||
<-resultCh // ensure goroutine completed
|
||||
<-sema // ensure goroutine completed
|
||||
}
|
||||
return rc.ctx.Err()
|
||||
}
|
||||
@@ -2250,13 +2562,19 @@ func (rc *SQLiteRows) nextSyncLocked(dest []driver.Value) error {
|
||||
}
|
||||
|
||||
rc.declTypes()
|
||||
if len(dest) == 0 {
|
||||
return nil
|
||||
}
|
||||
C._sqlite3_column_values(rc.s.s, C.int(len(dest)), rc.colvals)
|
||||
colvals := (*[(math.MaxInt32 - 1) / unsafe.Sizeof(C.sqlite3_go_col{})]C.sqlite3_go_col)(unsafe.Pointer(rc.colvals))[:len(dest):len(dest)]
|
||||
|
||||
decltype := rc.decltype
|
||||
_ = decltype[len(dest)-1]
|
||||
for i := range dest {
|
||||
switch C.sqlite3_column_type(rc.s.s, C.int(i)) {
|
||||
col := &colvals[i]
|
||||
switch col.typ {
|
||||
case C.SQLITE_INTEGER:
|
||||
val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i)))
|
||||
val := int64(col.i64)
|
||||
switch decltype[i] {
|
||||
case columnTimestamp, columnDatetime, columnDate:
|
||||
var t time.Time
|
||||
@@ -2279,14 +2597,14 @@ func (rc *SQLiteRows) nextSyncLocked(dest []driver.Value) error {
|
||||
dest[i] = val
|
||||
}
|
||||
case C.SQLITE_FLOAT:
|
||||
dest[i] = float64(C.sqlite3_column_double(rc.s.s, C.int(i)))
|
||||
dest[i] = float64(col.f64)
|
||||
case C.SQLITE_BLOB:
|
||||
p := C.sqlite3_column_blob(rc.s.s, C.int(i))
|
||||
p := col.ptr
|
||||
if p == nil {
|
||||
dest[i] = []byte{}
|
||||
continue
|
||||
}
|
||||
n := C.sqlite3_column_bytes(rc.s.s, C.int(i))
|
||||
n := col.n
|
||||
dest[i] = C.GoBytes(p, n)
|
||||
case C.SQLITE_NULL:
|
||||
dest[i] = nil
|
||||
@@ -2294,8 +2612,8 @@ func (rc *SQLiteRows) nextSyncLocked(dest []driver.Value) error {
|
||||
var err error
|
||||
var timeVal time.Time
|
||||
|
||||
n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i)))
|
||||
s := C.GoStringN((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))), C.int(n))
|
||||
n := int(col.n)
|
||||
s := C.GoStringN((*C.char)(unsafe.Pointer(col.ptr)), C.int(n))
|
||||
|
||||
switch decltype[i] {
|
||||
case columnTimestamp, columnDatetime, columnDate:
|
||||
|
||||
Reference in New Issue
Block a user