diff options
Diffstat (limited to 'sqlite3.go')
-rw-r--r-- | sqlite3.go | 292 |
1 files changed, 266 insertions, 26 deletions
@@ -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)) { |