|
|
|
@ -1,3 +1,5 @@ |
|
|
|
|
// +build cgo
|
|
|
|
|
|
|
|
|
|
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
|
|
|
//
|
|
|
|
|
// Use of this source code is governed by an MIT-style
|
|
|
|
@ -7,10 +9,13 @@ package sqlite3 |
|
|
|
|
|
|
|
|
|
/* |
|
|
|
|
#cgo CFLAGS: -std=gnu99 |
|
|
|
|
#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE=1 |
|
|
|
|
#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE=1 -DHAVE_USLEEP=1 |
|
|
|
|
#cgo linux,!android CFLAGS: -DHAVE_PREAD64=1 -DHAVE_PWRITE64=1 |
|
|
|
|
#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4_UNICODE61 |
|
|
|
|
#cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 |
|
|
|
|
#cgo CFLAGS: -DSQLITE_OMIT_DEPRECATED |
|
|
|
|
#cgo CFLAGS: -DSQLITE_DISABLE_INTRINSIC |
|
|
|
|
#cgo CFLAGS: -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT |
|
|
|
|
#cgo CFLAGS: -Wno-deprecated-declarations |
|
|
|
|
#ifndef USE_LIBSQLITE3 |
|
|
|
|
#include <sqlite3-binding.h> |
|
|
|
@ -170,6 +175,12 @@ var SQLiteTimestampFormats = []string{ |
|
|
|
|
"2006-01-02", |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
columnDate string = "date" |
|
|
|
|
columnDatetime string = "datetime" |
|
|
|
|
columnTimestamp string = "timestamp" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func init() { |
|
|
|
|
sql.Register("sqlite3", &SQLiteDriver{}) |
|
|
|
|
} |
|
|
|
@ -389,7 +400,7 @@ func (c *SQLiteConn) RegisterCommitHook(callback func() int) { |
|
|
|
|
if callback == nil { |
|
|
|
|
C.sqlite3_commit_hook(c.db, nil, nil) |
|
|
|
|
} else { |
|
|
|
|
C.sqlite3_commit_hook(c.db, (*[0]byte)(unsafe.Pointer(C.commitHookTrampoline)), unsafe.Pointer(newHandle(c, callback))) |
|
|
|
|
C.sqlite3_commit_hook(c.db, (*[0]byte)(C.commitHookTrampoline), unsafe.Pointer(newHandle(c, callback))) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -402,7 +413,7 @@ func (c *SQLiteConn) RegisterRollbackHook(callback func()) { |
|
|
|
|
if callback == nil { |
|
|
|
|
C.sqlite3_rollback_hook(c.db, nil, nil) |
|
|
|
|
} else { |
|
|
|
|
C.sqlite3_rollback_hook(c.db, (*[0]byte)(unsafe.Pointer(C.rollbackHookTrampoline)), unsafe.Pointer(newHandle(c, callback))) |
|
|
|
|
C.sqlite3_rollback_hook(c.db, (*[0]byte)(C.rollbackHookTrampoline), unsafe.Pointer(newHandle(c, callback))) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -419,7 +430,7 @@ func (c *SQLiteConn) RegisterUpdateHook(callback func(int, string, string, int64 |
|
|
|
|
if callback == nil { |
|
|
|
|
C.sqlite3_update_hook(c.db, nil, nil) |
|
|
|
|
} else { |
|
|
|
|
C.sqlite3_update_hook(c.db, (*[0]byte)(unsafe.Pointer(C.updateHookTrampoline)), unsafe.Pointer(newHandle(c, callback))) |
|
|
|
|
C.sqlite3_update_hook(c.db, (*[0]byte)(C.updateHookTrampoline), unsafe.Pointer(newHandle(c, callback))) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -501,7 +512,7 @@ func (c *SQLiteConn) RegisterFunc(name string, impl interface{}, pure bool) erro |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func sqlite3CreateFunction(db *C.sqlite3, zFunctionName *C.char, nArg C.int, eTextRep C.int, pApp uintptr, xFunc unsafe.Pointer, xStep unsafe.Pointer, xFinal unsafe.Pointer) C.int { |
|
|
|
|
return C._sqlite3_create_function(db, zFunctionName, nArg, eTextRep, C.uintptr_t(pApp), (*[0]byte)(unsafe.Pointer(xFunc)), (*[0]byte)(unsafe.Pointer(xStep)), (*[0]byte)(unsafe.Pointer(xFinal))) |
|
|
|
|
return C._sqlite3_create_function(db, zFunctionName, nArg, eTextRep, C.uintptr_t(pApp), (*[0]byte)(xFunc), (*[0]byte)(xStep), (*[0]byte)(xFinal)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// RegisterAggregator makes a Go type available as a SQLite aggregation function.
|
|
|
|
@ -780,6 +791,8 @@ func errorString(err Error) string { |
|
|
|
|
// Enable or disable enforcement of foreign keys. X can be 1 or 0.
|
|
|
|
|
// _recursive_triggers=X
|
|
|
|
|
// Enable or disable recursive triggers. X can be 1 or 0.
|
|
|
|
|
// _mutex=XXX
|
|
|
|
|
// Specify mutex mode. XXX can be "no", "full".
|
|
|
|
|
func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { |
|
|
|
|
if C.sqlite3_threadsafe() == 0 { |
|
|
|
|
return nil, errors.New("sqlite library was not compiled for thread-safe operation") |
|
|
|
@ -790,6 +803,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { |
|
|
|
|
busyTimeout := 5000 |
|
|
|
|
foreignKeys := -1 |
|
|
|
|
recursiveTriggers := -1 |
|
|
|
|
mutex := C.int(C.SQLITE_OPEN_FULLMUTEX) |
|
|
|
|
pos := strings.IndexRune(dsn, '?') |
|
|
|
|
if pos >= 1 { |
|
|
|
|
params, err := url.ParseQuery(dsn[pos+1:]) |
|
|
|
@ -856,6 +870,18 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// _mutex
|
|
|
|
|
if val := params.Get("_mutex"); val != "" { |
|
|
|
|
switch val { |
|
|
|
|
case "no": |
|
|
|
|
mutex = C.SQLITE_OPEN_NOMUTEX |
|
|
|
|
case "full": |
|
|
|
|
mutex = C.SQLITE_OPEN_FULLMUTEX |
|
|
|
|
default: |
|
|
|
|
return nil, fmt.Errorf("Invalid _mutex: %v", val) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !strings.HasPrefix(dsn, "file:") { |
|
|
|
|
dsn = dsn[:pos] |
|
|
|
|
} |
|
|
|
@ -865,9 +891,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { |
|
|
|
|
name := C.CString(dsn) |
|
|
|
|
defer C.free(unsafe.Pointer(name)) |
|
|
|
|
rv := C._sqlite3_open_v2(name, &db, |
|
|
|
|
C.SQLITE_OPEN_FULLMUTEX| |
|
|
|
|
C.SQLITE_OPEN_READWRITE| |
|
|
|
|
C.SQLITE_OPEN_CREATE, |
|
|
|
|
mutex|C.SQLITE_OPEN_READWRITE|C.SQLITE_OPEN_CREATE, |
|
|
|
|
nil) |
|
|
|
|
if rv != 0 { |
|
|
|
|
return nil, Error{Code: ErrNo(rv)} |
|
|
|
@ -1070,7 +1094,7 @@ func (s *SQLiteStmt) bind(args []namedValue) error { |
|
|
|
|
case int64: |
|
|
|
|
rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v)) |
|
|
|
|
case bool: |
|
|
|
|
if bool(v) { |
|
|
|
|
if v { |
|
|
|
|
rv = C.sqlite3_bind_int(s.s, n, 1) |
|
|
|
|
} else { |
|
|
|
|
rv = C.sqlite3_bind_int(s.s, n, 0) |
|
|
|
@ -1121,18 +1145,20 @@ func (s *SQLiteStmt) query(ctx context.Context, args []namedValue) (driver.Rows, |
|
|
|
|
done: make(chan struct{}), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
go func(db *C.sqlite3) { |
|
|
|
|
select { |
|
|
|
|
case <-ctx.Done(): |
|
|
|
|
if ctxdone := ctx.Done(); ctxdone != nil { |
|
|
|
|
go func(db *C.sqlite3) { |
|
|
|
|
select { |
|
|
|
|
case <-ctxdone: |
|
|
|
|
select { |
|
|
|
|
case <-rows.done: |
|
|
|
|
default: |
|
|
|
|
C.sqlite3_interrupt(db) |
|
|
|
|
rows.Close() |
|
|
|
|
} |
|
|
|
|
case <-rows.done: |
|
|
|
|
default: |
|
|
|
|
C.sqlite3_interrupt(db) |
|
|
|
|
rows.Close() |
|
|
|
|
} |
|
|
|
|
case <-rows.done: |
|
|
|
|
} |
|
|
|
|
}(s.c.db) |
|
|
|
|
}(s.c.db) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return rows, nil |
|
|
|
|
} |
|
|
|
@ -1166,19 +1192,21 @@ func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result |
|
|
|
|
return nil, err |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
done := make(chan struct{}) |
|
|
|
|
defer close(done) |
|
|
|
|
go func(db *C.sqlite3) { |
|
|
|
|
select { |
|
|
|
|
case <-done: |
|
|
|
|
case <-ctx.Done(): |
|
|
|
|
if ctxdone := ctx.Done(); ctxdone != nil { |
|
|
|
|
done := make(chan struct{}) |
|
|
|
|
defer close(done) |
|
|
|
|
go func(db *C.sqlite3) { |
|
|
|
|
select { |
|
|
|
|
case <-done: |
|
|
|
|
default: |
|
|
|
|
C.sqlite3_interrupt(db) |
|
|
|
|
case <-ctxdone: |
|
|
|
|
select { |
|
|
|
|
case <-done: |
|
|
|
|
default: |
|
|
|
|
C.sqlite3_interrupt(db) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}(s.c.db) |
|
|
|
|
}(s.c.db) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var rowid, changes C.longlong |
|
|
|
|
rv := C._sqlite3_step(s.s, &rowid, &changes) |
|
|
|
@ -1272,7 +1300,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error { |
|
|
|
|
case C.SQLITE_INTEGER: |
|
|
|
|
val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i))) |
|
|
|
|
switch rc.decltype[i] { |
|
|
|
|
case "timestamp", "datetime", "date": |
|
|
|
|
case columnTimestamp, columnDatetime, columnDate: |
|
|
|
|
var t time.Time |
|
|
|
|
// Assume a millisecond unix timestamp if it's 13 digits -- too
|
|
|
|
|
// large to be a reasonable timestamp in seconds.
|
|
|
|
@ -1303,10 +1331,10 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error { |
|
|
|
|
n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i))) |
|
|
|
|
switch dest[i].(type) { |
|
|
|
|
case sql.RawBytes: |
|
|
|
|
dest[i] = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n] |
|
|
|
|
dest[i] = (*[1 << 30]byte)(p)[0:n] |
|
|
|
|
default: |
|
|
|
|
slice := make([]byte, n) |
|
|
|
|
copy(slice[:], (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]) |
|
|
|
|
copy(slice[:], (*[1 << 30]byte)(p)[0:n]) |
|
|
|
|
dest[i] = slice |
|
|
|
|
} |
|
|
|
|
case C.SQLITE_NULL: |
|
|
|
@ -1319,7 +1347,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error { |
|
|
|
|
s := C.GoStringN((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))), C.int(n)) |
|
|
|
|
|
|
|
|
|
switch rc.decltype[i] { |
|
|
|
|
case "timestamp", "datetime", "date": |
|
|
|
|
case columnTimestamp, columnDatetime, columnDate: |
|
|
|
|
var t time.Time |
|
|
|
|
s = strings.TrimSuffix(s, "Z") |
|
|
|
|
for _, format := range SQLiteTimestampFormats { |
|
|
|
|