closes #2040
upstream commit: acfa601240
tags/v1.21.12.1
| @@ -1,9 +1,10 @@ | |||
| go-sqlite3 | |||
| ========== | |||
| [](http://godoc.org/github.com/mattn/go-sqlite3) | |||
| [](https://travis-ci.org/mattn/go-sqlite3) | |||
| [](https://coveralls.io/r/mattn/go-sqlite3?branch=master) | |||
| [](http://godoc.org/github.com/mattn/go-sqlite3) | |||
| [](https://goreportcard.com/report/github.com/mattn/go-sqlite3) | |||
| Description | |||
| ----------- | |||
| @@ -16,11 +17,11 @@ Installation | |||
| This package can be installed with the go get command: | |||
| go get github.com/mattn/go-sqlite3 | |||
| _go-sqlite3_ is *cgo* package. | |||
| If you want to build your app using go-sqlite3, you need gcc. | |||
| However, if you install _go-sqlite3_ with `go install github.com/mattn/go-sqlite3`, you don't need gcc to build your app anymore. | |||
| Documentation | |||
| ------------- | |||
| @@ -45,25 +46,40 @@ FAQ | |||
| Use `go build --tags "icu"` | |||
| Available extensions: `json1`, `fts5`, `icu` | |||
| * Can't build go-sqlite3 on windows 64bit. | |||
| > Probably, you are using go 1.0, go1.0 has a problem when it comes to compiling/linking on windows 64bit. | |||
| > See: https://github.com/mattn/go-sqlite3/issues/27 | |||
| > Probably, you are using go 1.0, go1.0 has a problem when it comes to compiling/linking on windows 64bit. | |||
| > See: [#27](https://github.com/mattn/go-sqlite3/issues/27) | |||
| * Getting insert error while query is opened. | |||
| > You can pass some arguments into the connection string, for example, a URI. | |||
| > See: https://github.com/mattn/go-sqlite3/issues/39 | |||
| > See: [#39](https://github.com/mattn/go-sqlite3/issues/39) | |||
| * Do you want to cross compile? mingw on Linux or Mac? | |||
| > See: https://github.com/mattn/go-sqlite3/issues/106 | |||
| > See: [#106](https://github.com/mattn/go-sqlite3/issues/106) | |||
| > See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html | |||
| * Want to get time.Time with current locale | |||
| Use `loc=auto` in SQLite3 filename schema like `file:foo.db?loc=auto`. | |||
| * Can I use this in multiple routines concurrently? | |||
| Yes for readonly. But, No for writable. See [#50](https://github.com/mattn/go-sqlite3/issues/50), [#51](https://github.com/mattn/go-sqlite3/issues/51), [#209](https://github.com/mattn/go-sqlite3/issues/209). | |||
| * Why is it racy if I use a `sql.Open("sqlite3", ":memory:")` database? | |||
| Each connection to :memory: opens a brand new in-memory sql database, so if | |||
| the stdlib's sql engine happens to open another connection and you've only | |||
| specified ":memory:", that connection will see a brand new database. A | |||
| workaround is to use "file::memory:?mode=memory&cache=shared". Every | |||
| connection to this string will point to the same in-memory database. See | |||
| [#204](https://github.com/mattn/go-sqlite3/issues/204) for more info. | |||
| License | |||
| ------- | |||
| @@ -19,10 +19,12 @@ import ( | |||
| "unsafe" | |||
| ) | |||
| // SQLiteBackup implement interface of Backup. | |||
| type SQLiteBackup struct { | |||
| b *C.sqlite3_backup | |||
| } | |||
| // Backup make backup from src to dest. | |||
| func (c *SQLiteConn) Backup(dest string, conn *SQLiteConn, src string) (*SQLiteBackup, error) { | |||
| destptr := C.CString(dest) | |||
| defer C.free(unsafe.Pointer(destptr)) | |||
| @@ -37,10 +39,10 @@ func (c *SQLiteConn) Backup(dest string, conn *SQLiteConn, src string) (*SQLiteB | |||
| return nil, c.lastError() | |||
| } | |||
| // Backs up for one step. Calls the underlying `sqlite3_backup_step` function. | |||
| // This function returns a boolean indicating if the backup is done and | |||
| // an error signalling any other error. Done is returned if the underlying C | |||
| // function returns SQLITE_DONE (Code 101) | |||
| // Step to backs up for one step. Calls the underlying `sqlite3_backup_step` | |||
| // function. This function returns a boolean indicating if the backup is done | |||
| // and an error signalling any other error. Done is returned if the underlying | |||
| // C function returns SQLITE_DONE (Code 101) | |||
| func (b *SQLiteBackup) Step(p int) (bool, error) { | |||
| ret := C.sqlite3_backup_step(b.b, C.int(p)) | |||
| if ret == C.SQLITE_DONE { | |||
| @@ -51,18 +53,22 @@ func (b *SQLiteBackup) Step(p int) (bool, error) { | |||
| return false, nil | |||
| } | |||
| // Remaining return whether have the rest for backup. | |||
| func (b *SQLiteBackup) Remaining() int { | |||
| return int(C.sqlite3_backup_remaining(b.b)) | |||
| } | |||
| // PageCount return count of pages. | |||
| func (b *SQLiteBackup) PageCount() int { | |||
| return int(C.sqlite3_backup_pagecount(b.b)) | |||
| } | |||
| // Finish close backup. | |||
| func (b *SQLiteBackup) Finish() error { | |||
| return b.Close() | |||
| } | |||
| // Close close backup. | |||
| func (b *SQLiteBackup) Close() error { | |||
| ret := C.sqlite3_backup_finish(b.b) | |||
| @@ -40,8 +40,8 @@ func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value | |||
| } | |||
| //export stepTrampoline | |||
| func stepTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) { | |||
| args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] | |||
| func stepTrampoline(ctx *C.sqlite3_context, argc C.int, argv **C.sqlite3_value) { | |||
| args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:int(argc):int(argc)] | |||
| ai := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*aggInfo) | |||
| ai.Step(ctx, args) | |||
| } | |||
| @@ -110,5 +110,3 @@ See the documentation of RegisterFunc for more details. | |||
| */ | |||
| package sqlite3 | |||
| import "C" | |||
| @@ -7,12 +7,16 @@ package sqlite3 | |||
| import "C" | |||
| // ErrNo inherit errno. | |||
| type ErrNo int | |||
| // ErrNoMask is mask code. | |||
| const ErrNoMask C.int = 0xff | |||
| // ErrNoExtended is extended errno. | |||
| type ErrNoExtended int | |||
| // Error implement sqlite error code. | |||
| type Error struct { | |||
| Code ErrNo /* The error code returned by SQLite */ | |||
| ExtendedCode ErrNoExtended /* The extended error code returned by SQLite */ | |||
| @@ -52,14 +56,17 @@ var ( | |||
| ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */ | |||
| ) | |||
| // Error return error message from errno. | |||
| func (err ErrNo) Error() string { | |||
| return Error{Code: err}.Error() | |||
| } | |||
| // Extend return extended errno. | |||
| func (err ErrNo) Extend(by int) ErrNoExtended { | |||
| return ErrNoExtended(int(err) | (by << 8)) | |||
| } | |||
| // Error return error message that is extended code. | |||
| func (err ErrNoExtended) Error() string { | |||
| return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error() | |||
| } | |||
| @@ -121,7 +128,7 @@ var ( | |||
| ErrConstraintTrigger = ErrConstraint.Extend(7) | |||
| ErrConstraintUnique = ErrConstraint.Extend(8) | |||
| ErrConstraintVTab = ErrConstraint.Extend(9) | |||
| ErrConstraintRowId = ErrConstraint.Extend(10) | |||
| ErrConstraintRowID = ErrConstraint.Extend(10) | |||
| ErrNoticeRecoverWAL = ErrNotice.Extend(1) | |||
| ErrNoticeRecoverRollback = ErrNotice.Extend(2) | |||
| ErrWarningAutoIndex = ErrWarning.Extend(1) | |||
| @@ -7,9 +7,10 @@ package sqlite3 | |||
| /* | |||
| #cgo CFLAGS: -std=gnu99 | |||
| #cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE | |||
| #cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE=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 | |||
| #cgo CFLAGS: -Wno-deprecated-declarations | |||
| #ifndef USE_LIBSQLITE3 | |||
| #include <sqlite3-binding.h> | |||
| @@ -112,14 +113,17 @@ import ( | |||
| "runtime" | |||
| "strconv" | |||
| "strings" | |||
| "sync" | |||
| "time" | |||
| "unsafe" | |||
| "golang.org/x/net/context" | |||
| ) | |||
| // Timestamp formats understood by both this module and SQLite. | |||
| // The first format in the slice will be used when saving time values | |||
| // into the database. When parsing a string from a timestamp or | |||
| // datetime column, the formats are tried in order. | |||
| // SQLiteTimestampFormats is timestamp formats understood by both this module | |||
| // and SQLite. The first format in the slice will be used when saving time | |||
| // values into the database. When parsing a string from a timestamp or datetime | |||
| // column, the formats are tried in order. | |||
| var SQLiteTimestampFormats = []string{ | |||
| // By default, store timestamps with whatever timezone they come with. | |||
| // When parsed, they will be returned with the same timezone. | |||
| @@ -139,21 +143,22 @@ func init() { | |||
| } | |||
| // Version returns SQLite library version information. | |||
| func Version() (libVersion string, libVersionNumber int, sourceId string) { | |||
| func Version() (libVersion string, libVersionNumber int, sourceID string) { | |||
| libVersion = C.GoString(C.sqlite3_libversion()) | |||
| libVersionNumber = int(C.sqlite3_libversion_number()) | |||
| sourceId = C.GoString(C.sqlite3_sourceid()) | |||
| return libVersion, libVersionNumber, sourceId | |||
| sourceID = C.GoString(C.sqlite3_sourceid()) | |||
| return libVersion, libVersionNumber, sourceID | |||
| } | |||
| // Driver struct. | |||
| // SQLiteDriver implement sql.Driver. | |||
| type SQLiteDriver struct { | |||
| Extensions []string | |||
| ConnectHook func(*SQLiteConn) error | |||
| } | |||
| // Conn struct. | |||
| // SQLiteConn implement sql.Conn. | |||
| type SQLiteConn struct { | |||
| dbMu sync.Mutex | |||
| db *C.sqlite3 | |||
| loc *time.Location | |||
| txlock string | |||
| @@ -161,35 +166,34 @@ type SQLiteConn struct { | |||
| aggregators []*aggInfo | |||
| } | |||
| // Tx struct. | |||
| // SQLiteTx implemen sql.Tx. | |||
| type SQLiteTx struct { | |||
| c *SQLiteConn | |||
| } | |||
| // Stmt struct. | |||
| // SQLiteStmt implement sql.Stmt. | |||
| type SQLiteStmt struct { | |||
| c *SQLiteConn | |||
| s *C.sqlite3_stmt | |||
| nv int | |||
| nn []string | |||
| t string | |||
| closed bool | |||
| cls bool | |||
| } | |||
| // Result struct. | |||
| // SQLiteResult implement sql.Result. | |||
| type SQLiteResult struct { | |||
| id int64 | |||
| changes int64 | |||
| } | |||
| // Rows struct. | |||
| // SQLiteRows implement sql.Rows. | |||
| type SQLiteRows struct { | |||
| s *SQLiteStmt | |||
| nc int | |||
| cols []string | |||
| decltype []string | |||
| cls bool | |||
| done chan struct{} | |||
| } | |||
| type functionInfo struct { | |||
| @@ -295,19 +299,19 @@ func (ai *aggInfo) Done(ctx *C.sqlite3_context) { | |||
| // Commit transaction. | |||
| func (tx *SQLiteTx) Commit() error { | |||
| _, err := tx.c.exec("COMMIT") | |||
| _, err := tx.c.exec(context.Background(), "COMMIT", nil) | |||
| if err != nil && err.(Error).Code == C.SQLITE_BUSY { | |||
| // sqlite3 will leave the transaction open in this scenario. | |||
| // However, database/sql considers the transaction complete once we | |||
| // return from Commit() - we must clean up to honour its semantics. | |||
| tx.c.exec("ROLLBACK") | |||
| tx.c.exec(context.Background(), "ROLLBACK", nil) | |||
| } | |||
| return err | |||
| } | |||
| // Rollback transaction. | |||
| func (tx *SQLiteTx) Rollback() error { | |||
| _, err := tx.c.exec("ROLLBACK") | |||
| _, err := tx.c.exec(context.Background(), "ROLLBACK", nil) | |||
| return err | |||
| } | |||
| @@ -381,34 +385,54 @@ func (c *SQLiteConn) RegisterFunc(name string, impl interface{}, pure bool) erro | |||
| if pure { | |||
| opts |= C.SQLITE_DETERMINISTIC | |||
| } | |||
| rv := C._sqlite3_create_function(c.db, cname, C.int(numArgs), C.int(opts), C.uintptr_t(newHandle(c, &fi)), (*[0]byte)(unsafe.Pointer(C.callbackTrampoline)), nil, nil) | |||
| rv := sqlite3CreateFunction(c.db, cname, C.int(numArgs), C.int(opts), newHandle(c, &fi), C.callbackTrampoline, nil, nil) | |||
| if rv != C.SQLITE_OK { | |||
| return c.lastError() | |||
| } | |||
| return nil | |||
| } | |||
| 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))) | |||
| } | |||
| // AutoCommit return which currently auto commit or not. | |||
| func (c *SQLiteConn) AutoCommit() bool { | |||
| return int(C.sqlite3_get_autocommit(c.db)) != 0 | |||
| } | |||
| func (c *SQLiteConn) lastError() Error { | |||
| func (c *SQLiteConn) lastError() error { | |||
| return lastError(c.db) | |||
| } | |||
| func lastError(db *C.sqlite3) error { | |||
| rv := C.sqlite3_errcode(db) | |||
| if rv == C.SQLITE_OK { | |||
| return nil | |||
| } | |||
| return Error{ | |||
| Code: ErrNo(C.sqlite3_errcode(c.db)), | |||
| ExtendedCode: ErrNoExtended(C.sqlite3_extended_errcode(c.db)), | |||
| err: C.GoString(C.sqlite3_errmsg(c.db)), | |||
| Code: ErrNo(rv), | |||
| ExtendedCode: ErrNoExtended(C.sqlite3_extended_errcode(db)), | |||
| err: C.GoString(C.sqlite3_errmsg(db)), | |||
| } | |||
| } | |||
| // Implements Execer | |||
| // Exec implements Execer. | |||
| func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, error) { | |||
| if len(args) == 0 { | |||
| return c.exec(query) | |||
| list := make([]namedValue, len(args)) | |||
| for i, v := range args { | |||
| list[i] = namedValue{ | |||
| Ordinal: i + 1, | |||
| Value: v, | |||
| } | |||
| } | |||
| return c.exec(context.Background(), query, list) | |||
| } | |||
| func (c *SQLiteConn) exec(ctx context.Context, query string, args []namedValue) (driver.Result, error) { | |||
| start := 0 | |||
| for { | |||
| s, err := c.Prepare(query) | |||
| s, err := c.prepare(ctx, query) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -416,14 +440,19 @@ func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, err | |||
| if s.(*SQLiteStmt).s != nil { | |||
| na := s.NumInput() | |||
| if len(args) < na { | |||
| return nil, fmt.Errorf("Not enough args to execute query. Expected %d, got %d.", na, len(args)) | |||
| s.Close() | |||
| return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)) | |||
| } | |||
| res, err = s.Exec(args[:na]) | |||
| for i := 0; i < na; i++ { | |||
| args[i].Ordinal -= start | |||
| } | |||
| res, err = s.(*SQLiteStmt).exec(ctx, args[:na]) | |||
| if err != nil && err != driver.ErrSkip { | |||
| s.Close() | |||
| return nil, err | |||
| } | |||
| args = args[na:] | |||
| start += na | |||
| } | |||
| tail := s.(*SQLiteStmt).t | |||
| s.Close() | |||
| @@ -434,24 +463,46 @@ func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, err | |||
| } | |||
| } | |||
| // Implements Queryer | |||
| type namedValue struct { | |||
| Name string | |||
| Ordinal int | |||
| Value driver.Value | |||
| } | |||
| // Query implements Queryer. | |||
| func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, error) { | |||
| list := make([]namedValue, len(args)) | |||
| for i, v := range args { | |||
| list[i] = namedValue{ | |||
| Ordinal: i + 1, | |||
| Value: v, | |||
| } | |||
| } | |||
| return c.query(context.Background(), query, list) | |||
| } | |||
| func (c *SQLiteConn) query(ctx context.Context, query string, args []namedValue) (driver.Rows, error) { | |||
| start := 0 | |||
| for { | |||
| s, err := c.Prepare(query) | |||
| s, err := c.prepare(ctx, query) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| s.(*SQLiteStmt).cls = true | |||
| na := s.NumInput() | |||
| if len(args) < na { | |||
| return nil, fmt.Errorf("Not enough args to execute query. Expected %d, got %d.", na, len(args)) | |||
| return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)) | |||
| } | |||
| rows, err := s.Query(args[:na]) | |||
| for i := 0; i < na; i++ { | |||
| args[i].Ordinal -= start | |||
| } | |||
| rows, err := s.(*SQLiteStmt).query(ctx, args[:na]) | |||
| if err != nil && err != driver.ErrSkip { | |||
| s.Close() | |||
| return nil, err | |||
| return rows, err | |||
| } | |||
| args = args[na:] | |||
| start += na | |||
| tail := s.(*SQLiteStmt).t | |||
| if tail == "" { | |||
| return rows, nil | |||
| @@ -462,21 +513,13 @@ func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, erro | |||
| } | |||
| } | |||
| func (c *SQLiteConn) exec(cmd string) (driver.Result, error) { | |||
| pcmd := C.CString(cmd) | |||
| defer C.free(unsafe.Pointer(pcmd)) | |||
| var rowid, changes C.longlong | |||
| rv := C._sqlite3_exec(c.db, pcmd, &rowid, &changes) | |||
| if rv != C.SQLITE_OK { | |||
| return nil, c.lastError() | |||
| } | |||
| return &SQLiteResult{int64(rowid), int64(changes)}, nil | |||
| } | |||
| // Begin transaction. | |||
| func (c *SQLiteConn) Begin() (driver.Tx, error) { | |||
| if _, err := c.exec(c.txlock); err != nil { | |||
| return c.begin(context.Background()) | |||
| } | |||
| func (c *SQLiteConn) begin(ctx context.Context) (driver.Tx, error) { | |||
| if _, err := c.exec(ctx, c.txlock, nil); err != nil { | |||
| return nil, err | |||
| } | |||
| return &SQLiteTx{c}, nil | |||
| @@ -500,6 +543,8 @@ func errorString(err Error) string { | |||
| // _txlock=XXX | |||
| // Specify locking behavior for transactions. XXX can be "immediate", | |||
| // "deferred", "exclusive". | |||
| // _foreign_keys=X | |||
| // Enable or disable enforcement of foreign keys. X can be 1 or 0. | |||
| 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") | |||
| @@ -507,7 +552,8 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { | |||
| var loc *time.Location | |||
| txlock := "BEGIN" | |||
| busy_timeout := 5000 | |||
| busyTimeout := 5000 | |||
| foreignKeys := -1 | |||
| pos := strings.IndexRune(dsn, '?') | |||
| if pos >= 1 { | |||
| params, err := url.ParseQuery(dsn[pos+1:]) | |||
| @@ -533,7 +579,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { | |||
| if err != nil { | |||
| return nil, fmt.Errorf("Invalid _busy_timeout: %v: %v", val, err) | |||
| } | |||
| busy_timeout = int(iv) | |||
| busyTimeout = int(iv) | |||
| } | |||
| // _txlock | |||
| @@ -550,6 +596,18 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { | |||
| } | |||
| } | |||
| // _foreign_keys | |||
| if val := params.Get("_foreign_keys"); val != "" { | |||
| switch val { | |||
| case "1": | |||
| foreignKeys = 1 | |||
| case "0": | |||
| foreignKeys = 0 | |||
| default: | |||
| return nil, fmt.Errorf("Invalid _foreign_keys: %v", val) | |||
| } | |||
| } | |||
| if !strings.HasPrefix(dsn, "file:") { | |||
| dsn = dsn[:pos] | |||
| } | |||
| @@ -570,21 +628,45 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { | |||
| return nil, errors.New("sqlite succeeded without returning a database") | |||
| } | |||
| rv = C.sqlite3_busy_timeout(db, C.int(busy_timeout)) | |||
| rv = C.sqlite3_busy_timeout(db, C.int(busyTimeout)) | |||
| if rv != C.SQLITE_OK { | |||
| C.sqlite3_close_v2(db) | |||
| return nil, Error{Code: ErrNo(rv)} | |||
| } | |||
| exec := func(s string) error { | |||
| cs := C.CString(s) | |||
| rv := C.sqlite3_exec(db, cs, nil, nil, nil) | |||
| C.free(unsafe.Pointer(cs)) | |||
| if rv != C.SQLITE_OK { | |||
| return lastError(db) | |||
| } | |||
| return nil | |||
| } | |||
| if foreignKeys == 0 { | |||
| if err := exec("PRAGMA foreign_keys = OFF;"); err != nil { | |||
| C.sqlite3_close_v2(db) | |||
| return nil, err | |||
| } | |||
| } else if foreignKeys == 1 { | |||
| if err := exec("PRAGMA foreign_keys = ON;"); err != nil { | |||
| C.sqlite3_close_v2(db) | |||
| return nil, err | |||
| } | |||
| } | |||
| conn := &SQLiteConn{db: db, loc: loc, txlock: txlock} | |||
| if len(d.Extensions) > 0 { | |||
| if err := conn.loadExtensions(d.Extensions); err != nil { | |||
| conn.Close() | |||
| return nil, err | |||
| } | |||
| } | |||
| if d.ConnectHook != nil { | |||
| if err := d.ConnectHook(conn); err != nil { | |||
| conn.Close() | |||
| return nil, err | |||
| } | |||
| } | |||
| @@ -594,18 +676,33 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) { | |||
| // Close the connection. | |||
| func (c *SQLiteConn) Close() error { | |||
| deleteHandles(c) | |||
| rv := C.sqlite3_close_v2(c.db) | |||
| if rv != C.SQLITE_OK { | |||
| return c.lastError() | |||
| } | |||
| deleteHandles(c) | |||
| c.dbMu.Lock() | |||
| c.db = nil | |||
| c.dbMu.Unlock() | |||
| runtime.SetFinalizer(c, nil) | |||
| return nil | |||
| } | |||
| func (c *SQLiteConn) dbConnOpen() bool { | |||
| if c == nil { | |||
| return false | |||
| } | |||
| c.dbMu.Lock() | |||
| defer c.dbMu.Unlock() | |||
| return c.db != nil | |||
| } | |||
| // Prepare the query string. Return a new statement. | |||
| func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { | |||
| return c.prepare(context.Background(), query) | |||
| } | |||
| func (c *SQLiteConn) prepare(ctx context.Context, query string) (driver.Stmt, error) { | |||
| pquery := C.CString(query) | |||
| defer C.free(unsafe.Pointer(pquery)) | |||
| var s *C.sqlite3_stmt | |||
| @@ -618,15 +715,7 @@ func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { | |||
| if tail != nil && *tail != '\000' { | |||
| t = strings.TrimSpace(C.GoString(tail)) | |||
| } | |||
| nv := int(C.sqlite3_bind_parameter_count(s)) | |||
| var nn []string | |||
| for i := 0; i < nv; i++ { | |||
| pn := C.GoString(C.sqlite3_bind_parameter_name(s, C.int(i+1))) | |||
| if len(pn) > 1 && pn[0] == '$' && 48 <= pn[1] && pn[1] <= 57 { | |||
| nn = append(nn, C.GoString(C.sqlite3_bind_parameter_name(s, C.int(i+1)))) | |||
| } | |||
| } | |||
| ss := &SQLiteStmt{c: c, s: s, nv: nv, nn: nn, t: t} | |||
| ss := &SQLiteStmt{c: c, s: s, t: t} | |||
| runtime.SetFinalizer(ss, (*SQLiteStmt).Close) | |||
| return ss, nil | |||
| } | |||
| @@ -637,7 +726,7 @@ func (s *SQLiteStmt) Close() error { | |||
| return nil | |||
| } | |||
| s.closed = true | |||
| if s.c == nil || s.c.db == nil { | |||
| if !s.c.dbConnOpen() { | |||
| return errors.New("sqlite statement with already closed database connection") | |||
| } | |||
| rv := C.sqlite3_finalize(s.s) | |||
| @@ -648,9 +737,9 @@ func (s *SQLiteStmt) Close() error { | |||
| return nil | |||
| } | |||
| // Return a number of parameters. | |||
| // NumInput return a number of parameters. | |||
| func (s *SQLiteStmt) NumInput() int { | |||
| return s.nv | |||
| return int(C.sqlite3_bind_parameter_count(s.s)) | |||
| } | |||
| type bindArg struct { | |||
| @@ -658,37 +747,30 @@ type bindArg struct { | |||
| v driver.Value | |||
| } | |||
| func (s *SQLiteStmt) bind(args []driver.Value) error { | |||
| var placeHolder = []byte{0} | |||
| func (s *SQLiteStmt) bind(args []namedValue) error { | |||
| rv := C.sqlite3_reset(s.s) | |||
| if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { | |||
| return s.c.lastError() | |||
| } | |||
| var vargs []bindArg | |||
| narg := len(args) | |||
| vargs = make([]bindArg, narg) | |||
| if len(s.nn) > 0 { | |||
| for i, v := range s.nn { | |||
| if pi, err := strconv.Atoi(v[1:]); err == nil { | |||
| vargs[i] = bindArg{pi, args[i]} | |||
| } | |||
| } | |||
| } else { | |||
| for i, v := range args { | |||
| vargs[i] = bindArg{i + 1, v} | |||
| for i, v := range args { | |||
| if v.Name != "" { | |||
| cname := C.CString(":" + v.Name) | |||
| args[i].Ordinal = int(C.sqlite3_bind_parameter_index(s.s, cname)) | |||
| C.free(unsafe.Pointer(cname)) | |||
| } | |||
| } | |||
| for _, varg := range vargs { | |||
| n := C.int(varg.n) | |||
| v := varg.v | |||
| switch v := v.(type) { | |||
| for _, arg := range args { | |||
| n := C.int(arg.Ordinal) | |||
| switch v := arg.Value.(type) { | |||
| case nil: | |||
| rv = C.sqlite3_bind_null(s.s, n) | |||
| case string: | |||
| if len(v) == 0 { | |||
| b := []byte{0} | |||
| rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(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))) | |||
| @@ -705,10 +787,9 @@ func (s *SQLiteStmt) bind(args []driver.Value) error { | |||
| rv = C.sqlite3_bind_double(s.s, n, C.double(v)) | |||
| case []byte: | |||
| if len(v) == 0 { | |||
| rv = C._sqlite3_bind_blob(s.s, n, nil, 0) | |||
| } else { | |||
| rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(len(v))) | |||
| v = placeHolder | |||
| } | |||
| rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(len(v))) | |||
| 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))) | |||
| @@ -722,29 +803,85 @@ func (s *SQLiteStmt) bind(args []driver.Value) error { | |||
| // Query the statement with arguments. Return records. | |||
| func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) { | |||
| list := make([]namedValue, len(args)) | |||
| for i, v := range args { | |||
| list[i] = namedValue{ | |||
| Ordinal: i + 1, | |||
| Value: v, | |||
| } | |||
| } | |||
| return s.query(context.Background(), list) | |||
| } | |||
| func (s *SQLiteStmt) query(ctx context.Context, args []namedValue) (driver.Rows, error) { | |||
| if err := s.bind(args); err != nil { | |||
| return nil, err | |||
| } | |||
| return &SQLiteRows{s, int(C.sqlite3_column_count(s.s)), nil, nil, s.cls}, nil | |||
| rows := &SQLiteRows{ | |||
| s: s, | |||
| nc: int(C.sqlite3_column_count(s.s)), | |||
| cols: nil, | |||
| decltype: nil, | |||
| cls: s.cls, | |||
| done: make(chan struct{}), | |||
| } | |||
| go func(db *C.sqlite3) { | |||
| select { | |||
| case <-ctx.Done(): | |||
| select { | |||
| case <-rows.done: | |||
| default: | |||
| C.sqlite3_interrupt(db) | |||
| rows.Close() | |||
| } | |||
| case <-rows.done: | |||
| } | |||
| }(s.c.db) | |||
| return rows, nil | |||
| } | |||
| // Return last inserted ID. | |||
| // LastInsertId teturn last inserted ID. | |||
| func (r *SQLiteResult) LastInsertId() (int64, error) { | |||
| return r.id, nil | |||
| } | |||
| // Return how many rows affected. | |||
| // RowsAffected return how many rows affected. | |||
| func (r *SQLiteResult) RowsAffected() (int64, error) { | |||
| return r.changes, nil | |||
| } | |||
| // Execute the statement with arguments. Return result object. | |||
| // Exec execute the statement with arguments. Return result object. | |||
| func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) { | |||
| list := make([]namedValue, len(args)) | |||
| for i, v := range args { | |||
| list[i] = namedValue{ | |||
| Ordinal: i + 1, | |||
| Value: v, | |||
| } | |||
| } | |||
| return s.exec(context.Background(), list) | |||
| } | |||
| func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result, error) { | |||
| if err := s.bind(args); err != nil { | |||
| C.sqlite3_reset(s.s) | |||
| C.sqlite3_clear_bindings(s.s) | |||
| 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) | |||
| var rowid, changes C.longlong | |||
| rv := C._sqlite3_step(s.s, &rowid, &changes) | |||
| if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { | |||
| @@ -753,7 +890,8 @@ func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) { | |||
| C.sqlite3_clear_bindings(s.s) | |||
| return nil, err | |||
| } | |||
| return &SQLiteResult{int64(rowid), int64(changes)}, nil | |||
| return &SQLiteResult{id: int64(rowid), changes: int64(changes)}, nil | |||
| } | |||
| // Close the rows. | |||
| @@ -761,6 +899,9 @@ func (rc *SQLiteRows) Close() error { | |||
| if rc.s.closed { | |||
| return nil | |||
| } | |||
| if rc.done != nil { | |||
| close(rc.done) | |||
| } | |||
| if rc.cls { | |||
| return rc.s.Close() | |||
| } | |||
| @@ -771,7 +912,7 @@ func (rc *SQLiteRows) Close() error { | |||
| return nil | |||
| } | |||
| // Return column names. | |||
| // Columns return column names. | |||
| func (rc *SQLiteRows) Columns() []string { | |||
| if rc.nc != len(rc.cols) { | |||
| rc.cols = make([]string, rc.nc) | |||
| @@ -782,7 +923,7 @@ func (rc *SQLiteRows) Columns() []string { | |||
| return rc.cols | |||
| } | |||
| // Return column types. | |||
| // DeclTypes return column types. | |||
| func (rc *SQLiteRows) DeclTypes() []string { | |||
| if rc.decltype == nil { | |||
| rc.decltype = make([]string, rc.nc) | |||
| @@ -793,7 +934,7 @@ func (rc *SQLiteRows) DeclTypes() []string { | |||
| return rc.decltype | |||
| } | |||
| // Move cursor to next. | |||
| // Next move cursor to next. | |||
| func (rc *SQLiteRows) Next(dest []driver.Value) error { | |||
| rv := C.sqlite3_step(rc.s.s) | |||
| if rv == C.SQLITE_DONE { | |||
| @@ -820,10 +961,11 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error { | |||
| // large to be a reasonable timestamp in seconds. | |||
| if val > 1e12 || val < -1e12 { | |||
| val *= int64(time.Millisecond) // convert ms to nsec | |||
| t = time.Unix(0, val) | |||
| } else { | |||
| val *= int64(time.Second) // convert sec to nsec | |||
| t = time.Unix(val, 0) | |||
| } | |||
| t = time.Unix(0, val).UTC() | |||
| t = t.UTC() | |||
| if rc.s.c.loc != nil { | |||
| t = t.In(rc.s.c.loc) | |||
| } | |||
| @@ -0,0 +1,103 @@ | |||
| // Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. | |||
| // | |||
| // Use of this source code is governed by an MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| package sqlite3 | |||
| /* | |||
| #ifndef USE_LIBSQLITE3 | |||
| #include <sqlite3-binding.h> | |||
| #else | |||
| #include <sqlite3.h> | |||
| #endif | |||
| #include <stdlib.h> | |||
| // These wrappers are necessary because SQLITE_TRANSIENT | |||
| // is a pointer constant, and cgo doesn't translate them correctly. | |||
| static inline void my_result_text(sqlite3_context *ctx, char *p, int np) { | |||
| sqlite3_result_text(ctx, p, np, SQLITE_TRANSIENT); | |||
| } | |||
| static inline void my_result_blob(sqlite3_context *ctx, void *p, int np) { | |||
| sqlite3_result_blob(ctx, p, np, SQLITE_TRANSIENT); | |||
| } | |||
| */ | |||
| import "C" | |||
| import ( | |||
| "math" | |||
| "reflect" | |||
| "unsafe" | |||
| ) | |||
| const i64 = unsafe.Sizeof(int(0)) > 4 | |||
| // SQLiteContext behave sqlite3_context | |||
| type SQLiteContext C.sqlite3_context | |||
| // ResultBool sets the result of an SQL function. | |||
| func (c *SQLiteContext) ResultBool(b bool) { | |||
| if b { | |||
| c.ResultInt(1) | |||
| } else { | |||
| c.ResultInt(0) | |||
| } | |||
| } | |||
| // ResultBlob sets the result of an SQL function. | |||
| // See: sqlite3_result_blob, http://sqlite.org/c3ref/result_blob.html | |||
| func (c *SQLiteContext) ResultBlob(b []byte) { | |||
| if i64 && len(b) > math.MaxInt32 { | |||
| C.sqlite3_result_error_toobig((*C.sqlite3_context)(c)) | |||
| return | |||
| } | |||
| var p *byte | |||
| if len(b) > 0 { | |||
| p = &b[0] | |||
| } | |||
| C.my_result_blob((*C.sqlite3_context)(c), unsafe.Pointer(p), C.int(len(b))) | |||
| } | |||
| // ResultDouble sets the result of an SQL function. | |||
| // See: sqlite3_result_double, http://sqlite.org/c3ref/result_blob.html | |||
| func (c *SQLiteContext) ResultDouble(d float64) { | |||
| C.sqlite3_result_double((*C.sqlite3_context)(c), C.double(d)) | |||
| } | |||
| // ResultInt sets the result of an SQL function. | |||
| // See: sqlite3_result_int, http://sqlite.org/c3ref/result_blob.html | |||
| func (c *SQLiteContext) ResultInt(i int) { | |||
| if i64 && (i > math.MaxInt32 || i < math.MinInt32) { | |||
| C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) | |||
| } else { | |||
| C.sqlite3_result_int((*C.sqlite3_context)(c), C.int(i)) | |||
| } | |||
| } | |||
| // ResultInt64 sets the result of an SQL function. | |||
| // See: sqlite3_result_int64, http://sqlite.org/c3ref/result_blob.html | |||
| func (c *SQLiteContext) ResultInt64(i int64) { | |||
| C.sqlite3_result_int64((*C.sqlite3_context)(c), C.sqlite3_int64(i)) | |||
| } | |||
| // ResultNull sets the result of an SQL function. | |||
| // See: sqlite3_result_null, http://sqlite.org/c3ref/result_blob.html | |||
| func (c *SQLiteContext) ResultNull() { | |||
| C.sqlite3_result_null((*C.sqlite3_context)(c)) | |||
| } | |||
| // ResultText sets the result of an SQL function. | |||
| // See: sqlite3_result_text, http://sqlite.org/c3ref/result_blob.html | |||
| func (c *SQLiteContext) ResultText(s string) { | |||
| h := (*reflect.StringHeader)(unsafe.Pointer(&s)) | |||
| cs, l := (*C.char)(unsafe.Pointer(h.Data)), C.int(h.Len) | |||
| C.my_result_text((*C.sqlite3_context)(c), cs, l) | |||
| } | |||
| // ResultZeroblob sets the result of an SQL function. | |||
| // See: sqlite3_result_zeroblob, http://sqlite.org/c3ref/result_blob.html | |||
| func (c *SQLiteContext) ResultZeroblob(n int) { | |||
| C.sqlite3_result_zeroblob((*C.sqlite3_context)(c), C.int(n)) | |||
| } | |||
| @@ -0,0 +1,69 @@ | |||
| // Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. | |||
| // | |||
| // Use of this source code is governed by an MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| // +build go1.8 | |||
| package sqlite3 | |||
| import ( | |||
| "database/sql/driver" | |||
| "errors" | |||
| "context" | |||
| ) | |||
| // Ping implement Pinger. | |||
| func (c *SQLiteConn) Ping(ctx context.Context) error { | |||
| if c.db == nil { | |||
| return errors.New("Connection was closed") | |||
| } | |||
| return nil | |||
| } | |||
| // QueryContext implement QueryerContext. | |||
| func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { | |||
| list := make([]namedValue, len(args)) | |||
| for i, nv := range args { | |||
| list[i] = namedValue(nv) | |||
| } | |||
| return c.query(ctx, query, list) | |||
| } | |||
| // ExecContext implement ExecerContext. | |||
| func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { | |||
| list := make([]namedValue, len(args)) | |||
| for i, nv := range args { | |||
| list[i] = namedValue(nv) | |||
| } | |||
| return c.exec(ctx, query, list) | |||
| } | |||
| // PrepareContext implement ConnPrepareContext. | |||
| func (c *SQLiteConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { | |||
| return c.prepare(ctx, query) | |||
| } | |||
| // BeginTx implement ConnBeginTx. | |||
| func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { | |||
| return c.begin(ctx) | |||
| } | |||
| // QueryContext implement QueryerContext. | |||
| func (s *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { | |||
| list := make([]namedValue, len(args)) | |||
| for i, nv := range args { | |||
| list[i] = namedValue(nv) | |||
| } | |||
| return s.query(ctx, list) | |||
| } | |||
| // ExecContext implement ExecerContext. | |||
| func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { | |||
| list := make([]namedValue, len(args)) | |||
| for i, nv := range args { | |||
| list[i] = namedValue(nv) | |||
| } | |||
| return s.exec(ctx, list) | |||
| } | |||
| @@ -2,7 +2,7 @@ | |||
| // | |||
| // Use of this source code is governed by an MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| // +build icu | |||
| // +build icu | |||
| package sqlite3 | |||
| @@ -31,6 +31,7 @@ func (c *SQLiteConn) loadExtensions(extensions []string) error { | |||
| defer C.free(unsafe.Pointer(cext)) | |||
| rv = C.sqlite3_load_extension(c.db, cext, nil, nil) | |||
| if rv != C.SQLITE_OK { | |||
| C.sqlite3_enable_load_extension(c.db, 0) | |||
| return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) | |||
| } | |||
| } | |||
| @@ -42,6 +43,7 @@ func (c *SQLiteConn) loadExtensions(extensions []string) error { | |||
| return nil | |||
| } | |||
| // LoadExtension load the sqlite3 extension. | |||
| func (c *SQLiteConn) LoadExtension(lib string, entry string) error { | |||
| rv := C.sqlite3_enable_load_extension(c.db, 1) | |||
| if rv != C.SQLITE_OK { | |||
| @@ -1,5 +1,4 @@ | |||
| // Copyright (C) 2016 Yasuhiro Matsumoto <mattn.jp@gmail.com>. | |||
| // TODO: add "Gimpl do foo" team? | |||
| // | |||
| // Use of this source code is governed by an MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| @@ -17,7 +16,7 @@ package sqlite3 | |||
| void stepTrampoline(sqlite3_context*, int, sqlite3_value**); | |||
| void doneTrampoline(sqlite3_context*); | |||
| void traceCallbackTrampoline(unsigned traceEventCode, void *ctx, void *p, void *x); | |||
| int traceCallbackTrampoline(unsigned int traceEventCode, void *ctx, void *p, void *x); | |||
| */ | |||
| import "C" | |||
| @@ -76,7 +75,7 @@ type TraceUserCallback func(TraceInfo) int | |||
| type TraceConfig struct { | |||
| Callback TraceUserCallback | |||
| EventMask uint | |||
| EventMask C.uint | |||
| WantExpandedSQL bool | |||
| } | |||
| @@ -102,13 +101,13 @@ func fillExpandedSQL(info *TraceInfo, db *C.sqlite3, pStmt unsafe.Pointer) { | |||
| //export traceCallbackTrampoline | |||
| func traceCallbackTrampoline( | |||
| traceEventCode uint, | |||
| traceEventCode C.uint, | |||
| // Parameter named 'C' in SQLite docs = Context given at registration: | |||
| ctx unsafe.Pointer, | |||
| // Parameter named 'P' in SQLite docs (Primary event data?): | |||
| p unsafe.Pointer, | |||
| // Parameter named 'X' in SQLite docs (eXtra event data?): | |||
| xValue unsafe.Pointer) int { | |||
| xValue unsafe.Pointer) C.int { | |||
| if ctx == nil { | |||
| panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode)) | |||
| @@ -196,7 +195,7 @@ func traceCallbackTrampoline( | |||
| if traceConf.Callback != nil { | |||
| r = traceConf.Callback(info) | |||
| } | |||
| return r | |||
| return C.int(r) | |||
| } | |||
| type traceMapEntry struct { | |||
| @@ -358,7 +357,7 @@ func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool | |||
| if pure { | |||
| opts |= C.SQLITE_DETERMINISTIC | |||
| } | |||
| rv := C._sqlite3_create_function(c.db, cname, C.int(stepNArgs), C.int(opts), C.uintptr_t(newHandle(c, &ai)), nil, (*[0]byte)(unsafe.Pointer(C.stepTrampoline)), (*[0]byte)(unsafe.Pointer(C.doneTrampoline))) | |||
| 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() | |||
| } | |||
| @@ -396,7 +395,7 @@ func (c *SQLiteConn) SetTrace(requested *TraceConfig) error { | |||
| // The callback trampoline function does cleanup on Close event, | |||
| // regardless of the presence or absence of the user callback. | |||
| // Therefore it needs the Close event to be selected: | |||
| actualEventMask := reqCopy.EventMask | TraceClose | |||
| actualEventMask := uint(reqCopy.EventMask | TraceClose) | |||
| err := c.setSQLiteTrace(actualEventMask) | |||
| return err | |||
| } | |||
| @@ -0,0 +1,57 @@ | |||
| package sqlite3 | |||
| /* | |||
| #ifndef USE_LIBSQLITE3 | |||
| #include <sqlite3-binding.h> | |||
| #else | |||
| #include <sqlite3.h> | |||
| #endif | |||
| */ | |||
| import "C" | |||
| import ( | |||
| "reflect" | |||
| "time" | |||
| ) | |||
| // ColumnTypeDatabaseTypeName implement RowsColumnTypeDatabaseTypeName. | |||
| func (rc *SQLiteRows) ColumnTypeDatabaseTypeName(i int) string { | |||
| return C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) | |||
| } | |||
| /* | |||
| func (rc *SQLiteRows) ColumnTypeLength(index int) (length int64, ok bool) { | |||
| return 0, false | |||
| } | |||
| func (rc *SQLiteRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { | |||
| return 0, 0, false | |||
| } | |||
| */ | |||
| // ColumnTypeNullable implement RowsColumnTypeNullable. | |||
| func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) { | |||
| return true, true | |||
| } | |||
| // ColumnTypeScanType implement RowsColumnTypeScanType. | |||
| func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type { | |||
| switch C.sqlite3_column_type(rc.s.s, C.int(i)) { | |||
| case C.SQLITE_INTEGER: | |||
| switch C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) { | |||
| case "timestamp", "datetime", "date": | |||
| return reflect.TypeOf(time.Time{}) | |||
| case "boolean": | |||
| return reflect.TypeOf(false) | |||
| } | |||
| return reflect.TypeOf(int64(0)) | |||
| case C.SQLITE_FLOAT: | |||
| return reflect.TypeOf(float64(0)) | |||
| case C.SQLITE_BLOB: | |||
| return reflect.SliceOf(reflect.TypeOf(byte(0))) | |||
| case C.SQLITE_NULL: | |||
| return reflect.TypeOf(nil) | |||
| case C.SQLITE_TEXT: | |||
| return reflect.TypeOf("") | |||
| } | |||
| return reflect.SliceOf(reflect.TypeOf(byte(0))) | |||
| } | |||
| @@ -0,0 +1,646 @@ | |||
| // Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. | |||
| // | |||
| // Use of this source code is governed by an MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| // +build vtable | |||
| package sqlite3 | |||
| /* | |||
| #cgo CFLAGS: -std=gnu99 | |||
| #cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE | |||
| #cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4_UNICODE61 | |||
| #cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 | |||
| #cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA=1 | |||
| #cgo CFLAGS: -Wno-deprecated-declarations | |||
| #ifndef USE_LIBSQLITE3 | |||
| #include <sqlite3-binding.h> | |||
| #else | |||
| #include <sqlite3.h> | |||
| #endif | |||
| #include <stdlib.h> | |||
| #include <stdint.h> | |||
| #include <memory.h> | |||
| static inline char *_sqlite3_mprintf(char *zFormat, char *arg) { | |||
| return sqlite3_mprintf(zFormat, arg); | |||
| } | |||
| typedef struct goVTab goVTab; | |||
| struct goVTab { | |||
| sqlite3_vtab base; | |||
| void *vTab; | |||
| }; | |||
| uintptr_t goMInit(void *db, void *pAux, int argc, char **argv, char **pzErr, int isCreate); | |||
| static int cXInit(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr, int isCreate) { | |||
| void *vTab = (void *)goMInit(db, pAux, argc, (char**)argv, pzErr, isCreate); | |||
| if (!vTab || *pzErr) { | |||
| return SQLITE_ERROR; | |||
| } | |||
| goVTab *pvTab = (goVTab *)sqlite3_malloc(sizeof(goVTab)); | |||
| if (!pvTab) { | |||
| *pzErr = sqlite3_mprintf("%s", "Out of memory"); | |||
| return SQLITE_NOMEM; | |||
| } | |||
| memset(pvTab, 0, sizeof(goVTab)); | |||
| pvTab->vTab = vTab; | |||
| *ppVTab = (sqlite3_vtab *)pvTab; | |||
| *pzErr = 0; | |||
| return SQLITE_OK; | |||
| } | |||
| static inline int cXCreate(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { | |||
| return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 1); | |||
| } | |||
| static inline int cXConnect(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { | |||
| return cXInit(db, pAux, argc, argv, ppVTab, pzErr, 0); | |||
| } | |||
| char* goVBestIndex(void *pVTab, void *icp); | |||
| static inline int cXBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *info) { | |||
| char *pzErr = goVBestIndex(((goVTab*)pVTab)->vTab, info); | |||
| if (pzErr) { | |||
| if (pVTab->zErrMsg) | |||
| sqlite3_free(pVTab->zErrMsg); | |||
| pVTab->zErrMsg = pzErr; | |||
| return SQLITE_ERROR; | |||
| } | |||
| return SQLITE_OK; | |||
| } | |||
| char* goVRelease(void *pVTab, int isDestroy); | |||
| static int cXRelease(sqlite3_vtab *pVTab, int isDestroy) { | |||
| char *pzErr = goVRelease(((goVTab*)pVTab)->vTab, isDestroy); | |||
| if (pzErr) { | |||
| if (pVTab->zErrMsg) | |||
| sqlite3_free(pVTab->zErrMsg); | |||
| pVTab->zErrMsg = pzErr; | |||
| return SQLITE_ERROR; | |||
| } | |||
| if (pVTab->zErrMsg) | |||
| sqlite3_free(pVTab->zErrMsg); | |||
| sqlite3_free(pVTab); | |||
| return SQLITE_OK; | |||
| } | |||
| static inline int cXDisconnect(sqlite3_vtab *pVTab) { | |||
| return cXRelease(pVTab, 0); | |||
| } | |||
| static inline int cXDestroy(sqlite3_vtab *pVTab) { | |||
| return cXRelease(pVTab, 1); | |||
| } | |||
| typedef struct goVTabCursor goVTabCursor; | |||
| struct goVTabCursor { | |||
| sqlite3_vtab_cursor base; | |||
| void *vTabCursor; | |||
| }; | |||
| uintptr_t goVOpen(void *pVTab, char **pzErr); | |||
| static int cXOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) { | |||
| void *vTabCursor = (void *)goVOpen(((goVTab*)pVTab)->vTab, &(pVTab->zErrMsg)); | |||
| goVTabCursor *pCursor = (goVTabCursor *)sqlite3_malloc(sizeof(goVTabCursor)); | |||
| if (!pCursor) { | |||
| return SQLITE_NOMEM; | |||
| } | |||
| memset(pCursor, 0, sizeof(goVTabCursor)); | |||
| pCursor->vTabCursor = vTabCursor; | |||
| *ppCursor = (sqlite3_vtab_cursor *)pCursor; | |||
| return SQLITE_OK; | |||
| } | |||
| static int setErrMsg(sqlite3_vtab_cursor *pCursor, char *pzErr) { | |||
| if (pCursor->pVtab->zErrMsg) | |||
| sqlite3_free(pCursor->pVtab->zErrMsg); | |||
| pCursor->pVtab->zErrMsg = pzErr; | |||
| return SQLITE_ERROR; | |||
| } | |||
| char* goVClose(void *pCursor); | |||
| static int cXClose(sqlite3_vtab_cursor *pCursor) { | |||
| char *pzErr = goVClose(((goVTabCursor*)pCursor)->vTabCursor); | |||
| if (pzErr) { | |||
| return setErrMsg(pCursor, pzErr); | |||
| } | |||
| sqlite3_free(pCursor); | |||
| return SQLITE_OK; | |||
| } | |||
| char* goVFilter(void *pCursor, int idxNum, char* idxName, int argc, sqlite3_value **argv); | |||
| static int cXFilter(sqlite3_vtab_cursor *pCursor, int idxNum, const char *idxStr, int argc, sqlite3_value **argv) { | |||
| char *pzErr = goVFilter(((goVTabCursor*)pCursor)->vTabCursor, idxNum, (char*)idxStr, argc, argv); | |||
| if (pzErr) { | |||
| return setErrMsg(pCursor, pzErr); | |||
| } | |||
| return SQLITE_OK; | |||
| } | |||
| char* goVNext(void *pCursor); | |||
| static int cXNext(sqlite3_vtab_cursor *pCursor) { | |||
| char *pzErr = goVNext(((goVTabCursor*)pCursor)->vTabCursor); | |||
| if (pzErr) { | |||
| return setErrMsg(pCursor, pzErr); | |||
| } | |||
| return SQLITE_OK; | |||
| } | |||
| int goVEof(void *pCursor); | |||
| static inline int cXEof(sqlite3_vtab_cursor *pCursor) { | |||
| return goVEof(((goVTabCursor*)pCursor)->vTabCursor); | |||
| } | |||
| char* goVColumn(void *pCursor, void *cp, int col); | |||
| static int cXColumn(sqlite3_vtab_cursor *pCursor, sqlite3_context *ctx, int i) { | |||
| char *pzErr = goVColumn(((goVTabCursor*)pCursor)->vTabCursor, ctx, i); | |||
| if (pzErr) { | |||
| return setErrMsg(pCursor, pzErr); | |||
| } | |||
| return SQLITE_OK; | |||
| } | |||
| char* goVRowid(void *pCursor, sqlite3_int64 *pRowid); | |||
| static int cXRowid(sqlite3_vtab_cursor *pCursor, sqlite3_int64 *pRowid) { | |||
| char *pzErr = goVRowid(((goVTabCursor*)pCursor)->vTabCursor, pRowid); | |||
| if (pzErr) { | |||
| return setErrMsg(pCursor, pzErr); | |||
| } | |||
| return SQLITE_OK; | |||
| } | |||
| char* goVUpdate(void *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid); | |||
| static int cXUpdate(sqlite3_vtab *pVTab, int argc, sqlite3_value **argv, sqlite3_int64 *pRowid) { | |||
| char *pzErr = goVUpdate(((goVTab*)pVTab)->vTab, argc, argv, pRowid); | |||
| if (pzErr) { | |||
| if (pVTab->zErrMsg) | |||
| sqlite3_free(pVTab->zErrMsg); | |||
| pVTab->zErrMsg = pzErr; | |||
| return SQLITE_ERROR; | |||
| } | |||
| return SQLITE_OK; | |||
| } | |||
| static sqlite3_module goModule = { | |||
| 0, // iVersion | |||
| cXCreate, // xCreate - create a table | |||
| cXConnect, // xConnect - connect to an existing table | |||
| cXBestIndex, // xBestIndex - Determine search strategy | |||
| cXDisconnect, // xDisconnect - Disconnect from a table | |||
| cXDestroy, // xDestroy - Drop a table | |||
| cXOpen, // xOpen - open a cursor | |||
| cXClose, // xClose - close a cursor | |||
| cXFilter, // xFilter - configure scan constraints | |||
| cXNext, // xNext - advance a cursor | |||
| cXEof, // xEof | |||
| cXColumn, // xColumn - read data | |||
| cXRowid, // xRowid - read data | |||
| cXUpdate, // xUpdate - write data | |||
| // Not implemented | |||
| 0, // xBegin - begin transaction | |||
| 0, // xSync - sync transaction | |||
| 0, // xCommit - commit transaction | |||
| 0, // xRollback - rollback transaction | |||
| 0, // xFindFunction - function overloading | |||
| 0, // xRename - rename the table | |||
| 0, // xSavepoint | |||
| 0, // xRelease | |||
| 0 // xRollbackTo | |||
| }; | |||
| void goMDestroy(void*); | |||
| static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pClientData) { | |||
| return sqlite3_create_module_v2(db, zName, &goModule, (void*) pClientData, goMDestroy); | |||
| } | |||
| */ | |||
| import "C" | |||
| import ( | |||
| "fmt" | |||
| "math" | |||
| "reflect" | |||
| "unsafe" | |||
| ) | |||
| type sqliteModule struct { | |||
| c *SQLiteConn | |||
| name string | |||
| module Module | |||
| } | |||
| type sqliteVTab struct { | |||
| module *sqliteModule | |||
| vTab VTab | |||
| } | |||
| type sqliteVTabCursor struct { | |||
| vTab *sqliteVTab | |||
| vTabCursor VTabCursor | |||
| } | |||
| // Op is type of operations. | |||
| type Op uint8 | |||
| // Op mean identity of operations. | |||
| const ( | |||
| OpEQ Op = 2 | |||
| OpGT = 4 | |||
| OpLE = 8 | |||
| OpLT = 16 | |||
| OpGE = 32 | |||
| OpMATCH = 64 | |||
| OpLIKE = 65 /* 3.10.0 and later only */ | |||
| OpGLOB = 66 /* 3.10.0 and later only */ | |||
| OpREGEXP = 67 /* 3.10.0 and later only */ | |||
| OpScanUnique = 1 /* Scan visits at most 1 row */ | |||
| ) | |||
| // InfoConstraint give information of constraint. | |||
| type InfoConstraint struct { | |||
| Column int | |||
| Op Op | |||
| Usable bool | |||
| } | |||
| // InfoOrderBy give information of order-by. | |||
| type InfoOrderBy struct { | |||
| Column int | |||
| Desc bool | |||
| } | |||
| func constraints(info *C.sqlite3_index_info) []InfoConstraint { | |||
| l := info.nConstraint | |||
| slice := (*[1 << 30]C.struct_sqlite3_index_constraint)(unsafe.Pointer(info.aConstraint))[:l:l] | |||
| cst := make([]InfoConstraint, 0, l) | |||
| for _, c := range slice { | |||
| var usable bool | |||
| if c.usable > 0 { | |||
| usable = true | |||
| } | |||
| cst = append(cst, InfoConstraint{ | |||
| Column: int(c.iColumn), | |||
| Op: Op(c.op), | |||
| Usable: usable, | |||
| }) | |||
| } | |||
| return cst | |||
| } | |||
| func orderBys(info *C.sqlite3_index_info) []InfoOrderBy { | |||
| l := info.nOrderBy | |||
| slice := (*[1 << 30]C.struct_sqlite3_index_orderby)(unsafe.Pointer(info.aOrderBy))[:l:l] | |||
| ob := make([]InfoOrderBy, 0, l) | |||
| for _, c := range slice { | |||
| var desc bool | |||
| if c.desc > 0 { | |||
| desc = true | |||
| } | |||
| ob = append(ob, InfoOrderBy{ | |||
| Column: int(c.iColumn), | |||
| Desc: desc, | |||
| }) | |||
| } | |||
| return ob | |||
| } | |||
| // IndexResult is a Go struct representation of what eventually ends up in the | |||
| // output fields for `sqlite3_index_info` | |||
| // See: https://www.sqlite.org/c3ref/index_info.html | |||
| type IndexResult struct { | |||
| Used []bool // aConstraintUsage | |||
| IdxNum int | |||
| IdxStr string | |||
| AlreadyOrdered bool // orderByConsumed | |||
| EstimatedCost float64 | |||
| EstimatedRows float64 | |||
| } | |||
| // mPrintf is a utility wrapper around sqlite3_mprintf | |||
| func mPrintf(format, arg string) *C.char { | |||
| cf := C.CString(format) | |||
| defer C.free(unsafe.Pointer(cf)) | |||
| ca := C.CString(arg) | |||
| defer C.free(unsafe.Pointer(ca)) | |||
| return C._sqlite3_mprintf(cf, ca) | |||
| } | |||
| //export goMInit | |||
| func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr **C.char, isCreate C.int) C.uintptr_t { | |||
| m := lookupHandle(uintptr(pClientData)).(*sqliteModule) | |||
| if m.c.db != (*C.sqlite3)(db) { | |||
| *pzErr = mPrintf("%s", "Inconsistent db handles") | |||
| return 0 | |||
| } | |||
| args := make([]string, argc) | |||
| var A []*C.char | |||
| slice := reflect.SliceHeader{Data: uintptr(unsafe.Pointer(argv)), Len: int(argc), Cap: int(argc)} | |||
| a := reflect.NewAt(reflect.TypeOf(A), unsafe.Pointer(&slice)).Elem().Interface() | |||
| for i, s := range a.([]*C.char) { | |||
| args[i] = C.GoString(s) | |||
| } | |||
| var vTab VTab | |||
| var err error | |||
| if isCreate == 1 { | |||
| vTab, err = m.module.Create(m.c, args) | |||
| } else { | |||
| vTab, err = m.module.Connect(m.c, args) | |||
| } | |||
| if err != nil { | |||
| *pzErr = mPrintf("%s", err.Error()) | |||
| return 0 | |||
| } | |||
| vt := sqliteVTab{m, vTab} | |||
| *pzErr = nil | |||
| return C.uintptr_t(newHandle(m.c, &vt)) | |||
| } | |||
| //export goVRelease | |||
| func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char { | |||
| vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) | |||
| var err error | |||
| if isDestroy == 1 { | |||
| err = vt.vTab.Destroy() | |||
| } else { | |||
| err = vt.vTab.Disconnect() | |||
| } | |||
| if err != nil { | |||
| return mPrintf("%s", err.Error()) | |||
| } | |||
| return nil | |||
| } | |||
| //export goVOpen | |||
| func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t { | |||
| vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) | |||
| vTabCursor, err := vt.vTab.Open() | |||
| if err != nil { | |||
| *pzErr = mPrintf("%s", err.Error()) | |||
| return 0 | |||
| } | |||
| vtc := sqliteVTabCursor{vt, vTabCursor} | |||
| *pzErr = nil | |||
| return C.uintptr_t(newHandle(vt.module.c, &vtc)) | |||
| } | |||
| //export goVBestIndex | |||
| func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char { | |||
| vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) | |||
| info := (*C.sqlite3_index_info)(icp) | |||
| csts := constraints(info) | |||
| res, err := vt.vTab.BestIndex(csts, orderBys(info)) | |||
| if err != nil { | |||
| return mPrintf("%s", err.Error()) | |||
| } | |||
| if len(res.Used) != len(csts) { | |||
| return mPrintf("Result.Used != expected value", "") | |||
| } | |||
| // Get a pointer to constraint_usage struct so we can update in place. | |||
| l := info.nConstraint | |||
| s := (*[1 << 30]C.struct_sqlite3_index_constraint_usage)(unsafe.Pointer(info.aConstraintUsage))[:l:l] | |||
| index := 1 | |||
| for i := C.int(0); i < info.nConstraint; i++ { | |||
| if res.Used[i] { | |||
| s[i].argvIndex = C.int(index) | |||
| s[i].omit = C.uchar(1) | |||
| index++ | |||
| } | |||
| } | |||
| info.idxNum = C.int(res.IdxNum) | |||
| idxStr := C.CString(res.IdxStr) | |||
| defer C.free(unsafe.Pointer(idxStr)) | |||
| info.idxStr = idxStr | |||
| info.needToFreeIdxStr = C.int(0) | |||
| if res.AlreadyOrdered { | |||
| info.orderByConsumed = C.int(1) | |||
| } | |||
| info.estimatedCost = C.double(res.EstimatedCost) | |||
| info.estimatedRows = C.sqlite3_int64(res.EstimatedRows) | |||
| return nil | |||
| } | |||
| //export goVClose | |||
| func goVClose(pCursor unsafe.Pointer) *C.char { | |||
| vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) | |||
| err := vtc.vTabCursor.Close() | |||
| if err != nil { | |||
| return mPrintf("%s", err.Error()) | |||
| } | |||
| return nil | |||
| } | |||
| //export goMDestroy | |||
| func goMDestroy(pClientData unsafe.Pointer) { | |||
| m := lookupHandle(uintptr(pClientData)).(*sqliteModule) | |||
| m.module.DestroyModule() | |||
| } | |||
| //export goVFilter | |||
| func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int, argv **C.sqlite3_value) *C.char { | |||
| vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) | |||
| args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] | |||
| vals := make([]interface{}, 0, argc) | |||
| for _, v := range args { | |||
| conv, err := callbackArgGeneric(v) | |||
| if err != nil { | |||
| return mPrintf("%s", err.Error()) | |||
| } | |||
| vals = append(vals, conv.Interface()) | |||
| } | |||
| err := vtc.vTabCursor.Filter(int(idxNum), C.GoString(idxName), vals) | |||
| if err != nil { | |||
| return mPrintf("%s", err.Error()) | |||
| } | |||
| return nil | |||
| } | |||
| //export goVNext | |||
| func goVNext(pCursor unsafe.Pointer) *C.char { | |||
| vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) | |||
| err := vtc.vTabCursor.Next() | |||
| if err != nil { | |||
| return mPrintf("%s", err.Error()) | |||
| } | |||
| return nil | |||
| } | |||
| //export goVEof | |||
| func goVEof(pCursor unsafe.Pointer) C.int { | |||
| vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) | |||
| err := vtc.vTabCursor.EOF() | |||
| if err { | |||
| return 1 | |||
| } | |||
| return 0 | |||
| } | |||
| //export goVColumn | |||
| func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char { | |||
| vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) | |||
| c := (*SQLiteContext)(cp) | |||
| err := vtc.vTabCursor.Column(c, int(col)) | |||
| if err != nil { | |||
| return mPrintf("%s", err.Error()) | |||
| } | |||
| return nil | |||
| } | |||
| //export goVRowid | |||
| func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char { | |||
| vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) | |||
| rowid, err := vtc.vTabCursor.Rowid() | |||
| if err != nil { | |||
| return mPrintf("%s", err.Error()) | |||
| } | |||
| *pRowid = C.sqlite3_int64(rowid) | |||
| return nil | |||
| } | |||
| //export goVUpdate | |||
| func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char { | |||
| vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) | |||
| var tname string | |||
| if n, ok := vt.vTab.(interface { | |||
| TableName() string | |||
| }); ok { | |||
| tname = n.TableName() + " " | |||
| } | |||
| err := fmt.Errorf("virtual %s table %sis read-only", vt.module.name, tname) | |||
| if v, ok := vt.vTab.(VTabUpdater); ok { | |||
| // convert argv | |||
| args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc] | |||
| vals := make([]interface{}, 0, argc) | |||
| for _, v := range args { | |||
| conv, err := callbackArgGeneric(v) | |||
| if err != nil { | |||
| return mPrintf("%s", err.Error()) | |||
| } | |||
| // work around for SQLITE_NULL | |||
| x := conv.Interface() | |||
| if z, ok := x.([]byte); ok && z == nil { | |||
| x = nil | |||
| } | |||
| vals = append(vals, x) | |||
| } | |||
| switch { | |||
| case argc == 1: | |||
| err = v.Delete(vals[0]) | |||
| case argc > 1 && vals[0] == nil: | |||
| var id int64 | |||
| id, err = v.Insert(vals[1], vals[2:]) | |||
| if err == nil { | |||
| *pRowid = C.sqlite3_int64(id) | |||
| } | |||
| case argc > 1: | |||
| err = v.Update(vals[1], vals[2:]) | |||
| } | |||
| } | |||
| if err != nil { | |||
| return mPrintf("%s", err.Error()) | |||
| } | |||
| return nil | |||
| } | |||
| // Module is a "virtual table module", it defines the implementation of a | |||
| // virtual tables. See: http://sqlite.org/c3ref/module.html | |||
| type Module interface { | |||
| // http://sqlite.org/vtab.html#xcreate | |||
| Create(c *SQLiteConn, args []string) (VTab, error) | |||
| // http://sqlite.org/vtab.html#xconnect | |||
| Connect(c *SQLiteConn, args []string) (VTab, error) | |||
| // http://sqlite.org/c3ref/create_module.html | |||
| DestroyModule() | |||
| } | |||
| // VTab describes a particular instance of the virtual table. | |||
| // See: http://sqlite.org/c3ref/vtab.html | |||
| type VTab interface { | |||
| // http://sqlite.org/vtab.html#xbestindex | |||
| BestIndex([]InfoConstraint, []InfoOrderBy) (*IndexResult, error) | |||
| // http://sqlite.org/vtab.html#xdisconnect | |||
| Disconnect() error | |||
| // http://sqlite.org/vtab.html#sqlite3_module.xDestroy | |||
| Destroy() error | |||
| // http://sqlite.org/vtab.html#xopen | |||
| Open() (VTabCursor, error) | |||
| } | |||
| // VTabUpdater is a type that allows a VTab to be inserted, updated, or | |||
| // deleted. | |||
| // See: https://sqlite.org/vtab.html#xupdate | |||
| type VTabUpdater interface { | |||
| Delete(interface{}) error | |||
| Insert(interface{}, []interface{}) (int64, error) | |||
| Update(interface{}, []interface{}) error | |||
| } | |||
| // VTabCursor describes cursors that point into the virtual table and are used | |||
| // to loop through the virtual table. See: http://sqlite.org/c3ref/vtab_cursor.html | |||
| type VTabCursor interface { | |||
| // http://sqlite.org/vtab.html#xclose | |||
| Close() error | |||
| // http://sqlite.org/vtab.html#xfilter | |||
| Filter(idxNum int, idxStr string, vals []interface{}) error | |||
| // http://sqlite.org/vtab.html#xnext | |||
| Next() error | |||
| // http://sqlite.org/vtab.html#xeof | |||
| EOF() bool | |||
| // http://sqlite.org/vtab.html#xcolumn | |||
| Column(c *SQLiteContext, col int) error | |||
| // http://sqlite.org/vtab.html#xrowid | |||
| Rowid() (int64, error) | |||
| } | |||
| // DeclareVTab declares the Schema of a virtual table. | |||
| // See: http://sqlite.org/c3ref/declare_vtab.html | |||
| func (c *SQLiteConn) DeclareVTab(sql string) error { | |||
| zSQL := C.CString(sql) | |||
| defer C.free(unsafe.Pointer(zSQL)) | |||
| rv := C.sqlite3_declare_vtab(c.db, zSQL) | |||
| if rv != C.SQLITE_OK { | |||
| return c.lastError() | |||
| } | |||
| return nil | |||
| } | |||
| // CreateModule registers a virtual table implementation. | |||
| // See: http://sqlite.org/c3ref/create_module.html | |||
| func (c *SQLiteConn) CreateModule(moduleName string, module Module) error { | |||
| mname := C.CString(moduleName) | |||
| defer C.free(unsafe.Pointer(mname)) | |||
| udm := sqliteModule{c, moduleName, module} | |||
| rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(newHandle(c, &udm))) | |||
| if rv != C.SQLITE_OK { | |||
| return c.lastError() | |||
| } | |||
| return nil | |||
| } | |||
| @@ -13,7 +13,7 @@ | |||
| ** This header file defines the SQLite interface for use by | |||
| ** shared libraries that want to be imported as extensions into | |||
| ** an SQLite instance. Shared libraries that intend to be loaded | |||
| ** as extensions by SQLite should #include this file instead of | |||
| ** as extensions by SQLite should #include this file instead of | |||
| ** sqlite3.h. | |||
| */ | |||
| #ifndef SQLITE3EXT_H | |||
| @@ -544,14 +544,14 @@ typedef int (*sqlite3_loadext_entry)( | |||
| #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ | |||
| #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) | |||
| /* This case when the file really is being compiled as a loadable | |||
| /* This case when the file really is being compiled as a loadable | |||
| ** extension */ | |||
| # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; | |||
| # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; | |||
| # define SQLITE_EXTENSION_INIT3 \ | |||
| extern const sqlite3_api_routines *sqlite3_api; | |||
| #else | |||
| /* This case when the file is being statically linked into the | |||
| /* This case when the file is being statically linked into the | |||
| ** application */ | |||
| # define SQLITE_EXTENSION_INIT1 /*no-op*/ | |||
| # define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ | |||
| @@ -562,4 +562,4 @@ typedef int (*sqlite3_loadext_entry)( | |||
| #else // USE_LIBSQLITE3 | |||
| // If users really want to link against the system sqlite3 we | |||
| // need to make this file a noop. | |||
| #endif | |||
| #endif | |||
| @@ -1,9 +0,0 @@ | |||
| // +build !trace | |||
| package sqlite3 | |||
| import "errors" | |||
| func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool) error { | |||
| return errors.New("This feature is not implemented") | |||
| } | |||
| @@ -690,10 +690,10 @@ | |||
| "revisionTime": "2017-02-23T14:12:10Z" | |||
| }, | |||
| { | |||
| "checksumSHA1": "9FJUwn3EIgASVki+p8IHgWVC5vQ=", | |||
| "checksumSHA1": "61HNjGetaBoMp8HBOpuEZRSim8g=", | |||
| "path": "github.com/mattn/go-sqlite3", | |||
| "revision": "86681de00adef4f8040947b7d35f97000fc5a230", | |||
| "revisionTime": "2016-10-28T14:22:18Z" | |||
| "revision": "acfa60124032040b9f5a9406f5a772ee16fe845e", | |||
| "revisionTime": "2017-07-05T08:25:03Z" | |||
| }, | |||
| { | |||
| "checksumSHA1": "9mjAkIoPrJdiGcR0pBa81NoFY4U=", | |||