|
|
@@ -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 {
|