aboutsummaryrefslogtreecommitdiff
path: root/sqlite3.go
diff options
context:
space:
mode:
Diffstat (limited to 'sqlite3.go')
-rw-r--r--sqlite3.go292
1 files changed, 266 insertions, 26 deletions
diff --git a/sqlite3.go b/sqlite3.go
index 3273da4..58bdab1 100644
--- a/sqlite3.go
+++ b/sqlite3.go
@@ -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,7 +9,8 @@ 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 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_DISABLE_INTRINSIC
@@ -100,12 +103,43 @@ int _sqlite3_create_function(
}
void callbackTrampoline(sqlite3_context*, int, sqlite3_value**);
+void stepTrampoline(sqlite3_context*, int, sqlite3_value**);
+void doneTrampoline(sqlite3_context*);
+
+int compareTrampoline(void*, int, char*, int, char*);
int commitHookTrampoline(void*);
void rollbackHookTrampoline(void*);
void updateHookTrampoline(void*, int, char*, char*, sqlite3_int64);
+
+#ifdef SQLITE_LIMIT_WORKER_THREADS
+# define _SQLITE_HAS_LIMIT
+# define SQLITE_LIMIT_LENGTH 0
+# define SQLITE_LIMIT_SQL_LENGTH 1
+# define SQLITE_LIMIT_COLUMN 2
+# define SQLITE_LIMIT_EXPR_DEPTH 3
+# define SQLITE_LIMIT_COMPOUND_SELECT 4
+# define SQLITE_LIMIT_VDBE_OP 5
+# define SQLITE_LIMIT_FUNCTION_ARG 6
+# define SQLITE_LIMIT_ATTACHED 7
+# define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8
+# define SQLITE_LIMIT_VARIABLE_NUMBER 9
+# define SQLITE_LIMIT_TRIGGER_DEPTH 10
+# define SQLITE_LIMIT_WORKER_THREADS 11
+# else
+# define SQLITE_LIMIT_WORKER_THREADS 11
+#endif
+
+static int _sqlite3_limit(sqlite3* db, int limitId, int newLimit) {
+#ifndef _SQLITE_HAS_LIMIT
+ return -1;
+#else
+ return sqlite3_limit(db, limitId, newLimit);
+#endif
+}
*/
import "C"
import (
+ "context"
"database/sql"
"database/sql/driver"
"errors"
@@ -119,8 +153,6 @@ import (
"sync"
"time"
"unsafe"
-
- "golang.org/x/net/context"
)
// SQLiteTimestampFormats is timestamp formats understood by both this module
@@ -188,6 +220,7 @@ type SQLiteTx struct {
// SQLiteStmt implement sql.Stmt.
type SQLiteStmt struct {
+ mu sync.Mutex
c *SQLiteConn
s *C.sqlite3_stmt
t string
@@ -331,6 +364,29 @@ func (tx *SQLiteTx) Rollback() error {
return err
}
+// RegisterCollation makes a Go function available as a collation.
+//
+// cmp receives two UTF-8 strings, a and b. The result should be 0 if
+// a==b, -1 if a < b, and +1 if a > b.
+//
+// cmp must always return the same result given the same
+// inputs. Additionally, it must have the following properties for all
+// strings A, B and C: if A==B then B==A; if A==B and B==C then A==C;
+// if A<B then B>A; if A<B and B<C then A<C.
+//
+// If cmp does not obey these constraints, sqlite3's behavior is
+// undefined when the collation is used.
+func (c *SQLiteConn) RegisterCollation(name string, cmp func(string, string) int) error {
+ handle := newHandle(c, cmp)
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+ rv := C.sqlite3_create_collation(c.db, cname, C.SQLITE_UTF8, unsafe.Pointer(handle), (*[0]byte)(unsafe.Pointer(C.compareTrampoline)))
+ if rv != C.SQLITE_OK {
+ return c.lastError()
+ }
+ return nil
+}
+
// RegisterCommitHook sets the commit hook for a connection.
//
// If the callback returns non-zero the transaction will become a rollback.
@@ -457,6 +513,131 @@ func sqlite3CreateFunction(db *C.sqlite3, zFunctionName *C.char, nArg C.int, eTe
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.
+//
+// Because aggregation is incremental, it's implemented in Go with a
+// type that has 2 methods: func Step(values) accumulates one row of
+// data into the accumulator, and func Done() ret finalizes and
+// returns the aggregate value. "values" and "ret" may be any type
+// supported by RegisterFunc.
+//
+// RegisterAggregator takes as implementation a constructor function
+// that constructs an instance of the aggregator type each time an
+// aggregation begins. The constructor must return a pointer to a
+// type, or an interface that implements Step() and Done().
+//
+// The constructor function and the Step/Done methods may optionally
+// return an error in addition to their other return values.
+//
+// See _example/go_custom_funcs for a detailed example.
+func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool) error {
+ var ai aggInfo
+ ai.constructor = reflect.ValueOf(impl)
+ t := ai.constructor.Type()
+ if t.Kind() != reflect.Func {
+ return errors.New("non-function passed to RegisterAggregator")
+ }
+ if t.NumOut() != 1 && t.NumOut() != 2 {
+ return errors.New("SQLite aggregator constructors must return 1 or 2 values")
+ }
+ if t.NumOut() == 2 && !t.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
+ return errors.New("Second return value of SQLite function must be error")
+ }
+ if t.NumIn() != 0 {
+ return errors.New("SQLite aggregator constructors must not have arguments")
+ }
+
+ agg := t.Out(0)
+ switch agg.Kind() {
+ case reflect.Ptr, reflect.Interface:
+ default:
+ return errors.New("SQlite aggregator constructor must return a pointer object")
+ }
+ stepFn, found := agg.MethodByName("Step")
+ if !found {
+ return errors.New("SQlite aggregator doesn't have a Step() function")
+ }
+ step := stepFn.Type
+ if step.NumOut() != 0 && step.NumOut() != 1 {
+ return errors.New("SQlite aggregator Step() function must return 0 or 1 values")
+ }
+ if step.NumOut() == 1 && !step.Out(0).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
+ return errors.New("type of SQlite aggregator Step() return value must be error")
+ }
+
+ stepNArgs := step.NumIn()
+ start := 0
+ if agg.Kind() == reflect.Ptr {
+ // Skip over the method receiver
+ stepNArgs--
+ start++
+ }
+ if step.IsVariadic() {
+ stepNArgs--
+ }
+ for i := start; i < start+stepNArgs; i++ {
+ conv, err := callbackArg(step.In(i))
+ if err != nil {
+ return err
+ }
+ ai.stepArgConverters = append(ai.stepArgConverters, conv)
+ }
+ if step.IsVariadic() {
+ conv, err := callbackArg(t.In(start + stepNArgs).Elem())
+ if err != nil {
+ return err
+ }
+ ai.stepVariadicConverter = conv
+ // Pass -1 to sqlite so that it allows any number of
+ // arguments. The call helper verifies that the minimum number
+ // of arguments is present for variadic functions.
+ stepNArgs = -1
+ }
+
+ doneFn, found := agg.MethodByName("Done")
+ if !found {
+ return errors.New("SQlite aggregator doesn't have a Done() function")
+ }
+ done := doneFn.Type
+ doneNArgs := done.NumIn()
+ if agg.Kind() == reflect.Ptr {
+ // Skip over the method receiver
+ doneNArgs--
+ }
+ if doneNArgs != 0 {
+ return errors.New("SQlite aggregator Done() function must have no arguments")
+ }
+ if done.NumOut() != 1 && done.NumOut() != 2 {
+ return errors.New("SQLite aggregator Done() function must return 1 or 2 values")
+ }
+ if done.NumOut() == 2 && !done.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
+ return errors.New("second return value of SQLite aggregator Done() function must be error")
+ }
+
+ conv, err := callbackRet(done.Out(0))
+ if err != nil {
+ return err
+ }
+ ai.doneRetConverter = conv
+ ai.active = make(map[int64]reflect.Value)
+ ai.next = 1
+
+ // ai must outlast the database connection, or we'll have dangling pointers.
+ c.aggregators = append(c.aggregators, &ai)
+
+ cname := C.CString(name)
+ defer C.free(unsafe.Pointer(cname))
+ opts := C.SQLITE_UTF8
+ if pure {
+ opts |= C.SQLITE_DETERMINISTIC
+ }
+ rv := sqlite3CreateFunction(c.db, cname, C.int(stepNArgs), C.int(opts), newHandle(c, &ai), nil, C.stepTrampoline, C.doneTrampoline)
+ if rv != C.SQLITE_OK {
+ return c.lastError()
+ }
+ return nil
+}
+
// AutoCommit return which currently auto commit or not.
func (c *SQLiteConn) AutoCommit() bool {
return int(C.sqlite3_get_autocommit(c.db)) != 0
@@ -807,8 +988,40 @@ func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, er
return ss, nil
}
+// Run-Time Limit Categories.
+// See: http://www.sqlite.org/c3ref/c_limit_attached.html
+const (
+ SQLITE_LIMIT_LENGTH = C.SQLITE_LIMIT_LENGTH
+ SQLITE_LIMIT_SQL_LENGTH = C.SQLITE_LIMIT_SQL_LENGTH
+ SQLITE_LIMIT_COLUMN = C.SQLITE_LIMIT_COLUMN
+ SQLITE_LIMIT_EXPR_DEPTH = C.SQLITE_LIMIT_EXPR_DEPTH
+ SQLITE_LIMIT_COMPOUND_SELECT = C.SQLITE_LIMIT_COMPOUND_SELECT
+ SQLITE_LIMIT_VDBE_OP = C.SQLITE_LIMIT_VDBE_OP
+ SQLITE_LIMIT_FUNCTION_ARG = C.SQLITE_LIMIT_FUNCTION_ARG
+ SQLITE_LIMIT_ATTACHED = C.SQLITE_LIMIT_ATTACHED
+ SQLITE_LIMIT_LIKE_PATTERN_LENGTH = C.SQLITE_LIMIT_LIKE_PATTERN_LENGTH
+ SQLITE_LIMIT_VARIABLE_NUMBER = C.SQLITE_LIMIT_VARIABLE_NUMBER
+ SQLITE_LIMIT_TRIGGER_DEPTH = C.SQLITE_LIMIT_TRIGGER_DEPTH
+ SQLITE_LIMIT_WORKER_THREADS = C.SQLITE_LIMIT_WORKER_THREADS
+)
+
+// GetLimit returns the current value of a run-time limit.
+// See: sqlite3_limit, http://www.sqlite.org/c3ref/limit.html
+func (c *SQLiteConn) GetLimit(id int) int {
+ return int(C._sqlite3_limit(c.db, C.int(id), -1))
+}
+
+// SetLimit changes the value of a run-time limits.
+// Then this method returns the prior value of the limit.
+// See: sqlite3_limit, http://www.sqlite.org/c3ref/limit.html
+func (c *SQLiteConn) SetLimit(id int, newVal int) int {
+ return int(C._sqlite3_limit(c.db, C.int(id), C.int(newVal)))
+}
+
// Close the statement.
func (s *SQLiteStmt) Close() error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
if s.closed {
return nil
}
@@ -817,6 +1030,7 @@ func (s *SQLiteStmt) Close() error {
return errors.New("sqlite statement with already closed database connection")
}
rv := C.sqlite3_finalize(s.s)
+ s.s = nil
if rv != C.SQLITE_OK {
return s.c.lastError()
}
@@ -916,18 +1130,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
}
@@ -961,15 +1177,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 <-ctx.Done():
- C.sqlite3_interrupt(db)
- case <-done:
- }
- }(s.c.db)
+ if ctxdone := ctx.Done(); ctxdone != nil {
+ done := make(chan struct{})
+ defer close(done)
+ go func(db *C.sqlite3) {
+ select {
+ case <-done:
+ case <-ctxdone:
+ select {
+ case <-done:
+ default:
+ C.sqlite3_interrupt(db)
+ }
+ }
+ }(s.c.db)
+ }
var rowid, changes C.longlong
rv := C._sqlite3_step(s.s, &rowid, &changes)
@@ -985,7 +1207,9 @@ func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result
// Close the rows.
func (rc *SQLiteRows) Close() error {
+ rc.s.mu.Lock()
if rc.s.closed || rc.closed {
+ rc.s.mu.Unlock()
return nil
}
rc.closed = true
@@ -993,18 +1217,23 @@ func (rc *SQLiteRows) Close() error {
close(rc.done)
}
if rc.cls {
+ rc.s.mu.Unlock()
return rc.s.Close()
}
rv := C.sqlite3_reset(rc.s.s)
if rv != C.SQLITE_OK {
+ rc.s.mu.Unlock()
return rc.s.c.lastError()
}
+ rc.s.mu.Unlock()
return nil
}
// Columns return column names.
func (rc *SQLiteRows) Columns() []string {
- if rc.nc != len(rc.cols) {
+ rc.s.mu.Lock()
+ defer rc.s.mu.Unlock()
+ if rc.s.s != nil && rc.nc != len(rc.cols) {
rc.cols = make([]string, rc.nc)
for i := 0; i < rc.nc; i++ {
rc.cols[i] = C.GoString(C.sqlite3_column_name(rc.s.s, C.int(i)))
@@ -1013,9 +1242,8 @@ func (rc *SQLiteRows) Columns() []string {
return rc.cols
}
-// DeclTypes return column types.
-func (rc *SQLiteRows) DeclTypes() []string {
- if rc.decltype == nil {
+func (rc *SQLiteRows) declTypes() []string {
+ if rc.s.s != nil && rc.decltype == nil {
rc.decltype = make([]string, rc.nc)
for i := 0; i < rc.nc; i++ {
rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))))
@@ -1024,8 +1252,20 @@ func (rc *SQLiteRows) DeclTypes() []string {
return rc.decltype
}
+// DeclTypes return column types.
+func (rc *SQLiteRows) DeclTypes() []string {
+ rc.s.mu.Lock()
+ defer rc.s.mu.Unlock()
+ return rc.declTypes()
+}
+
// Next move cursor to next.
func (rc *SQLiteRows) Next(dest []driver.Value) error {
+ if rc.s.closed {
+ return io.EOF
+ }
+ rc.s.mu.Lock()
+ defer rc.s.mu.Unlock()
rv := C.sqlite3_step(rc.s.s)
if rv == C.SQLITE_DONE {
return io.EOF
@@ -1038,7 +1278,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
return nil
}
- rc.DeclTypes()
+ rc.declTypes()
for i := range dest {
switch C.sqlite3_column_type(rc.s.s, C.int(i)) {