vendor: update sqlite to fix "database is locked" errors (#2116)

closes #2040

upstream commit: acfa601240
tokarchuk/v1.17
Andrey Nering 7 years ago committed by Lunny Xiao
parent a52cd59727
commit 2ef33b5338
  1. 24
      vendor/github.com/mattn/go-sqlite3/README.md
  2. 14
      vendor/github.com/mattn/go-sqlite3/backup.go
  3. 4
      vendor/github.com/mattn/go-sqlite3/callback.go
  4. 2
      vendor/github.com/mattn/go-sqlite3/doc.go
  5. 9
      vendor/github.com/mattn/go-sqlite3/error.go
  6. 14381
      vendor/github.com/mattn/go-sqlite3/sqlite3-binding.c
  7. 782
      vendor/github.com/mattn/go-sqlite3/sqlite3-binding.h
  8. 334
      vendor/github.com/mattn/go-sqlite3/sqlite3.go
  9. 103
      vendor/github.com/mattn/go-sqlite3/sqlite3_context.go
  10. 69
      vendor/github.com/mattn/go-sqlite3/sqlite3_go18.go
  11. 2
      vendor/github.com/mattn/go-sqlite3/sqlite3_load_extension.go
  12. 15
      vendor/github.com/mattn/go-sqlite3/sqlite3_trace.go
  13. 57
      vendor/github.com/mattn/go-sqlite3/sqlite3_type.go
  14. 646
      vendor/github.com/mattn/go-sqlite3/sqlite3_vtable.go
  15. 9
      vendor/github.com/mattn/go-sqlite3/tracecallback_noimpl.go
  16. 6
      vendor/vendor.json

@ -1,9 +1,10 @@
go-sqlite3 go-sqlite3
========== ==========
[![GoDoc Reference](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3)
[![Build Status](https://travis-ci.org/mattn/go-sqlite3.svg?branch=master)](https://travis-ci.org/mattn/go-sqlite3) [![Build Status](https://travis-ci.org/mattn/go-sqlite3.svg?branch=master)](https://travis-ci.org/mattn/go-sqlite3)
[![Coverage Status](https://coveralls.io/repos/mattn/go-sqlite3/badge.svg?branch=master)](https://coveralls.io/r/mattn/go-sqlite3?branch=master) [![Coverage Status](https://coveralls.io/repos/mattn/go-sqlite3/badge.svg?branch=master)](https://coveralls.io/r/mattn/go-sqlite3?branch=master)
[![GoDoc](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3) [![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-sqlite3)](https://goreportcard.com/report/github.com/mattn/go-sqlite3)
Description Description
----------- -----------
@ -45,25 +46,40 @@ FAQ
Use `go build --tags "icu"` Use `go build --tags "icu"`
Available extensions: `json1`, `fts5`, `icu`
* Can't build go-sqlite3 on windows 64bit. * 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. > 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 > See: [#27](https://github.com/mattn/go-sqlite3/issues/27)
* Getting insert error while query is opened. * Getting insert error while query is opened.
> You can pass some arguments into the connection string, for example, a URI. > 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? * 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 > See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html
* Want to get time.Time with current locale * Want to get time.Time with current locale
Use `loc=auto` in SQLite3 filename schema like `file:foo.db?loc=auto`. 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 License
------- -------

@ -19,10 +19,12 @@ import (
"unsafe" "unsafe"
) )
// SQLiteBackup implement interface of Backup.
type SQLiteBackup struct { type SQLiteBackup struct {
b *C.sqlite3_backup b *C.sqlite3_backup
} }
// Backup make backup from src to dest.
func (c *SQLiteConn) Backup(dest string, conn *SQLiteConn, src string) (*SQLiteBackup, error) { func (c *SQLiteConn) Backup(dest string, conn *SQLiteConn, src string) (*SQLiteBackup, error) {
destptr := C.CString(dest) destptr := C.CString(dest)
defer C.free(unsafe.Pointer(destptr)) 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() return nil, c.lastError()
} }
// Backs up for one step. Calls the underlying `sqlite3_backup_step` function. // Step to backs up for one step. Calls the underlying `sqlite3_backup_step`
// This function returns a boolean indicating if the backup is done and // function. This function returns a boolean indicating if the backup is done
// an error signalling any other error. Done is returned if the underlying C // and an error signalling any other error. Done is returned if the underlying
// function returns SQLITE_DONE (Code 101) // C function returns SQLITE_DONE (Code 101)
func (b *SQLiteBackup) Step(p int) (bool, error) { func (b *SQLiteBackup) Step(p int) (bool, error) {
ret := C.sqlite3_backup_step(b.b, C.int(p)) ret := C.sqlite3_backup_step(b.b, C.int(p))
if ret == C.SQLITE_DONE { if ret == C.SQLITE_DONE {
@ -51,18 +53,22 @@ func (b *SQLiteBackup) Step(p int) (bool, error) {
return false, nil return false, nil
} }
// Remaining return whether have the rest for backup.
func (b *SQLiteBackup) Remaining() int { func (b *SQLiteBackup) Remaining() int {
return int(C.sqlite3_backup_remaining(b.b)) return int(C.sqlite3_backup_remaining(b.b))
} }
// PageCount return count of pages.
func (b *SQLiteBackup) PageCount() int { func (b *SQLiteBackup) PageCount() int {
return int(C.sqlite3_backup_pagecount(b.b)) return int(C.sqlite3_backup_pagecount(b.b))
} }
// Finish close backup.
func (b *SQLiteBackup) Finish() error { func (b *SQLiteBackup) Finish() error {
return b.Close() return b.Close()
} }
// Close close backup.
func (b *SQLiteBackup) Close() error { func (b *SQLiteBackup) Close() error {
ret := C.sqlite3_backup_finish(b.b) 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 //export stepTrampoline
func stepTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) { 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))[:argc:argc] 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 := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*aggInfo)
ai.Step(ctx, args) ai.Step(ctx, args)
} }

@ -110,5 +110,3 @@ See the documentation of RegisterFunc for more details.
*/ */
package sqlite3 package sqlite3
import "C"

@ -7,12 +7,16 @@ package sqlite3
import "C" import "C"
// ErrNo inherit errno.
type ErrNo int type ErrNo int
// ErrNoMask is mask code.
const ErrNoMask C.int = 0xff const ErrNoMask C.int = 0xff
// ErrNoExtended is extended errno.
type ErrNoExtended int type ErrNoExtended int
// Error implement sqlite error code.
type Error struct { type Error struct {
Code ErrNo /* The error code returned by SQLite */ Code ErrNo /* The error code returned by SQLite */
ExtendedCode ErrNoExtended /* The extended 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() */ ErrWarning = ErrNo(28) /* Warnings from sqlite3_log() */
) )
// Error return error message from errno.
func (err ErrNo) Error() string { func (err ErrNo) Error() string {
return Error{Code: err}.Error() return Error{Code: err}.Error()
} }
// Extend return extended errno.
func (err ErrNo) Extend(by int) ErrNoExtended { func (err ErrNo) Extend(by int) ErrNoExtended {
return ErrNoExtended(int(err) | (by << 8)) return ErrNoExtended(int(err) | (by << 8))
} }
// Error return error message that is extended code.
func (err ErrNoExtended) Error() string { func (err ErrNoExtended) Error() string {
return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error() return Error{Code: ErrNo(C.int(err) & ErrNoMask), ExtendedCode: err}.Error()
} }
@ -121,7 +128,7 @@ var (
ErrConstraintTrigger = ErrConstraint.Extend(7) ErrConstraintTrigger = ErrConstraint.Extend(7)
ErrConstraintUnique = ErrConstraint.Extend(8) ErrConstraintUnique = ErrConstraint.Extend(8)
ErrConstraintVTab = ErrConstraint.Extend(9) ErrConstraintVTab = ErrConstraint.Extend(9)
ErrConstraintRowId = ErrConstraint.Extend(10) ErrConstraintRowID = ErrConstraint.Extend(10)
ErrNoticeRecoverWAL = ErrNotice.Extend(1) ErrNoticeRecoverWAL = ErrNotice.Extend(1)
ErrNoticeRecoverRollback = ErrNotice.Extend(2) ErrNoticeRecoverRollback = ErrNotice.Extend(2)
ErrWarningAutoIndex = ErrWarning.Extend(1) ErrWarningAutoIndex = ErrWarning.Extend(1)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -7,9 +7,10 @@ package sqlite3
/* /*
#cgo CFLAGS: -std=gnu99 #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_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4_UNICODE61
#cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 #cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15
#cgo CFLAGS: -DSQLITE_DISABLE_INTRINSIC
#cgo CFLAGS: -Wno-deprecated-declarations #cgo CFLAGS: -Wno-deprecated-declarations
#ifndef USE_LIBSQLITE3 #ifndef USE_LIBSQLITE3
#include <sqlite3-binding.h> #include <sqlite3-binding.h>
@ -112,14 +113,17 @@ import (
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"unsafe" "unsafe"
"golang.org/x/net/context"
) )
// Timestamp formats understood by both this module and SQLite. // SQLiteTimestampFormats is timestamp formats understood by both this module
// The first format in the slice will be used when saving time values // and SQLite. The first format in the slice will be used when saving time
// into the database. When parsing a string from a timestamp or // values into the database. When parsing a string from a timestamp or datetime
// datetime column, the formats are tried in order. // column, the formats are tried in order.
var SQLiteTimestampFormats = []string{ var SQLiteTimestampFormats = []string{
// By default, store timestamps with whatever timezone they come with. // By default, store timestamps with whatever timezone they come with.
// When parsed, they will be returned with the same timezone. // When parsed, they will be returned with the same timezone.
@ -139,21 +143,22 @@ func init() {
} }
// Version returns SQLite library version information. // 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()) libVersion = C.GoString(C.sqlite3_libversion())
libVersionNumber = int(C.sqlite3_libversion_number()) libVersionNumber = int(C.sqlite3_libversion_number())
sourceId = C.GoString(C.sqlite3_sourceid()) sourceID = C.GoString(C.sqlite3_sourceid())
return libVersion, libVersionNumber, sourceId return libVersion, libVersionNumber, sourceID
} }
// Driver struct. // SQLiteDriver implement sql.Driver.
type SQLiteDriver struct { type SQLiteDriver struct {
Extensions []string Extensions []string
ConnectHook func(*SQLiteConn) error ConnectHook func(*SQLiteConn) error
} }
// Conn struct. // SQLiteConn implement sql.Conn.
type SQLiteConn struct { type SQLiteConn struct {
dbMu sync.Mutex
db *C.sqlite3 db *C.sqlite3
loc *time.Location loc *time.Location
txlock string txlock string
@ -161,35 +166,34 @@ type SQLiteConn struct {
aggregators []*aggInfo aggregators []*aggInfo
} }
// Tx struct. // SQLiteTx implemen sql.Tx.
type SQLiteTx struct { type SQLiteTx struct {
c *SQLiteConn c *SQLiteConn
} }
// Stmt struct. // SQLiteStmt implement sql.Stmt.
type SQLiteStmt struct { type SQLiteStmt struct {
c *SQLiteConn c *SQLiteConn
s *C.sqlite3_stmt s *C.sqlite3_stmt
nv int
nn []string
t string t string
closed bool closed bool
cls bool cls bool
} }
// Result struct. // SQLiteResult implement sql.Result.
type SQLiteResult struct { type SQLiteResult struct {
id int64 id int64
changes int64 changes int64
} }
// Rows struct. // SQLiteRows implement sql.Rows.
type SQLiteRows struct { type SQLiteRows struct {
s *SQLiteStmt s *SQLiteStmt
nc int nc int
cols []string cols []string
decltype []string decltype []string
cls bool cls bool
done chan struct{}
} }
type functionInfo struct { type functionInfo struct {
@ -295,19 +299,19 @@ func (ai *aggInfo) Done(ctx *C.sqlite3_context) {
// Commit transaction. // Commit transaction.
func (tx *SQLiteTx) Commit() error { 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 { if err != nil && err.(Error).Code == C.SQLITE_BUSY {
// sqlite3 will leave the transaction open in this scenario. // sqlite3 will leave the transaction open in this scenario.
// However, database/sql considers the transaction complete once we // However, database/sql considers the transaction complete once we
// return from Commit() - we must clean up to honour its semantics. // return from Commit() - we must clean up to honour its semantics.
tx.c.exec("ROLLBACK") tx.c.exec(context.Background(), "ROLLBACK", nil)
} }
return err return err
} }
// Rollback transaction. // Rollback transaction.
func (tx *SQLiteTx) Rollback() error { func (tx *SQLiteTx) Rollback() error {
_, err := tx.c.exec("ROLLBACK") _, err := tx.c.exec(context.Background(), "ROLLBACK", nil)
return err return err
} }
@ -381,34 +385,54 @@ func (c *SQLiteConn) RegisterFunc(name string, impl interface{}, pure bool) erro
if pure { if pure {
opts |= C.SQLITE_DETERMINISTIC 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 { if rv != C.SQLITE_OK {
return c.lastError() return c.lastError()
} }
return nil 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. // AutoCommit return which currently auto commit or not.
func (c *SQLiteConn) AutoCommit() bool { func (c *SQLiteConn) AutoCommit() bool {
return int(C.sqlite3_get_autocommit(c.db)) != 0 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{ return Error{
Code: ErrNo(C.sqlite3_errcode(c.db)), Code: ErrNo(rv),
ExtendedCode: ErrNoExtended(C.sqlite3_extended_errcode(c.db)), ExtendedCode: ErrNoExtended(C.sqlite3_extended_errcode(db)),
err: C.GoString(C.sqlite3_errmsg(c.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) { func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, error) {
if len(args) == 0 { list := make([]namedValue, len(args))
return c.exec(query) 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 { for {
s, err := c.Prepare(query) s, err := c.prepare(ctx, query)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -416,14 +440,19 @@ func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, err
if s.(*SQLiteStmt).s != nil { if s.(*SQLiteStmt).s != nil {
na := s.NumInput() na := s.NumInput()
if len(args) < na { 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 { if err != nil && err != driver.ErrSkip {
s.Close() s.Close()
return nil, err return nil, err
} }
args = args[na:] args = args[na:]
start += na
} }
tail := s.(*SQLiteStmt).t tail := s.(*SQLiteStmt).t
s.Close() 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) { 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 { for {
s, err := c.Prepare(query) s, err := c.prepare(ctx, query)
if err != nil { if err != nil {
return nil, err return nil, err
} }
s.(*SQLiteStmt).cls = true s.(*SQLiteStmt).cls = true
na := s.NumInput() na := s.NumInput()
if len(args) < na { 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 { if err != nil && err != driver.ErrSkip {
s.Close() s.Close()
return nil, err return rows, err
} }
args = args[na:] args = args[na:]
start += na
tail := s.(*SQLiteStmt).t tail := s.(*SQLiteStmt).t
if tail == "" { if tail == "" {
return rows, nil 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. // Begin transaction.
func (c *SQLiteConn) Begin() (driver.Tx, error) { 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 nil, err
} }
return &SQLiteTx{c}, nil return &SQLiteTx{c}, nil
@ -500,6 +543,8 @@ func errorString(err Error) string {
// _txlock=XXX // _txlock=XXX
// Specify locking behavior for transactions. XXX can be "immediate", // Specify locking behavior for transactions. XXX can be "immediate",
// "deferred", "exclusive". // "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) { func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
if C.sqlite3_threadsafe() == 0 { if C.sqlite3_threadsafe() == 0 {
return nil, errors.New("sqlite library was not compiled for thread-safe operation") 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 var loc *time.Location
txlock := "BEGIN" txlock := "BEGIN"
busy_timeout := 5000 busyTimeout := 5000
foreignKeys := -1
pos := strings.IndexRune(dsn, '?') pos := strings.IndexRune(dsn, '?')
if pos >= 1 { if pos >= 1 {
params, err := url.ParseQuery(dsn[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 { if err != nil {
return nil, fmt.Errorf("Invalid _busy_timeout: %v: %v", val, err) return nil, fmt.Errorf("Invalid _busy_timeout: %v: %v", val, err)
} }
busy_timeout = int(iv) busyTimeout = int(iv)
} }
// _txlock // _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:") { if !strings.HasPrefix(dsn, "file:") {
dsn = dsn[:pos] 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") 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 { if rv != C.SQLITE_OK {
C.sqlite3_close_v2(db)
return nil, Error{Code: ErrNo(rv)} 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} conn := &SQLiteConn{db: db, loc: loc, txlock: txlock}
if len(d.Extensions) > 0 { if len(d.Extensions) > 0 {
if err := conn.loadExtensions(d.Extensions); err != nil { if err := conn.loadExtensions(d.Extensions); err != nil {
conn.Close()
return nil, err return nil, err
} }
} }
if d.ConnectHook != nil { if d.ConnectHook != nil {
if err := d.ConnectHook(conn); err != nil { if err := d.ConnectHook(conn); err != nil {
conn.Close()
return nil, err return nil, err
} }
} }
@ -594,18 +676,33 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
// Close the connection. // Close the connection.
func (c *SQLiteConn) Close() error { func (c *SQLiteConn) Close() error {
deleteHandles(c)
rv := C.sqlite3_close_v2(c.db) rv := C.sqlite3_close_v2(c.db)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return c.lastError() return c.lastError()
} }
deleteHandles(c)
c.dbMu.Lock()
c.db = nil c.db = nil
c.dbMu.Unlock()
runtime.SetFinalizer(c, nil) runtime.SetFinalizer(c, nil)
return 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. // Prepare the query string. Return a new statement.
func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) { 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) pquery := C.CString(query)
defer C.free(unsafe.Pointer(pquery)) defer C.free(unsafe.Pointer(pquery))
var s *C.sqlite3_stmt var s *C.sqlite3_stmt
@ -618,15 +715,7 @@ func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) {
if tail != nil && *tail != '\000' { if tail != nil && *tail != '\000' {
t = strings.TrimSpace(C.GoString(tail)) t = strings.TrimSpace(C.GoString(tail))
} }
nv := int(C.sqlite3_bind_parameter_count(s)) ss := &SQLiteStmt{c: c, s: s, t: t}
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}
runtime.SetFinalizer(ss, (*SQLiteStmt).Close) runtime.SetFinalizer(ss, (*SQLiteStmt).Close)
return ss, nil return ss, nil
} }
@ -637,7 +726,7 @@ func (s *SQLiteStmt) Close() error {
return nil return nil
} }
s.closed = true 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") return errors.New("sqlite statement with already closed database connection")
} }
rv := C.sqlite3_finalize(s.s) rv := C.sqlite3_finalize(s.s)
@ -648,9 +737,9 @@ func (s *SQLiteStmt) Close() error {
return nil return nil
} }
// Return a number of parameters. // NumInput return a number of parameters.
func (s *SQLiteStmt) NumInput() int { func (s *SQLiteStmt) NumInput() int {
return s.nv return int(C.sqlite3_bind_parameter_count(s.s))
} }
type bindArg struct { type bindArg struct {
@ -658,37 +747,30 @@ type bindArg struct {
v driver.Value 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) rv := C.sqlite3_reset(s.s)
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
return s.c.lastError() return s.c.lastError()
} }
var vargs []bindArg for i, v := range args {
narg := len(args) if v.Name != "" {
vargs = make([]bindArg, narg) cname := C.CString(":" + v.Name)
if len(s.nn) > 0 { args[i].Ordinal = int(C.sqlite3_bind_parameter_index(s.s, cname))
for i, v := range s.nn { C.free(unsafe.Pointer(cname))
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 _, varg := range vargs { for _, arg := range args {
n := C.int(varg.n) n := C.int(arg.Ordinal)
v := varg.v switch v := arg.Value.(type) {
switch v := v.(type) {
case nil: case nil:
rv = C.sqlite3_bind_null(s.s, n) rv = C.sqlite3_bind_null(s.s, n)
case string: case string:
if len(v) == 0 { if len(v) == 0 {
b := []byte{0} rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&placeHolder[0])), C.int(0))
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(0))
} else { } else {
b := []byte(v) b := []byte(v)
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b))) 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)) rv = C.sqlite3_bind_double(s.s, n, C.double(v))
case []byte: case []byte:
if len(v) == 0 { if len(v) == 0 {
rv = C._sqlite3_bind_blob(s.s, n, nil, 0) v = placeHolder
} else {
rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(len(v)))
} }
rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(len(v)))
case time.Time: case time.Time:
b := []byte(v.Format(SQLiteTimestampFormats[0])) b := []byte(v.Format(SQLiteTimestampFormats[0]))
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b))) 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. // Query the statement with arguments. Return records.
func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) { 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 { if err := s.bind(args); err != nil {
return nil, err 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) { func (r *SQLiteResult) LastInsertId() (int64, error) {
return r.id, nil return r.id, nil
} }
// Return how many rows affected. // RowsAffected return how many rows affected.
func (r *SQLiteResult) RowsAffected() (int64, error) { func (r *SQLiteResult) RowsAffected() (int64, error) {
return r.changes, nil 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) { 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 { if err := s.bind(args); err != nil {
C.sqlite3_reset(s.s) C.sqlite3_reset(s.s)
C.sqlite3_clear_bindings(s.s) C.sqlite3_clear_bindings(s.s)
return nil, err 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 var rowid, changes C.longlong
rv := C._sqlite3_step(s.s, &rowid, &changes) rv := C._sqlite3_step(s.s, &rowid, &changes)
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE { 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) C.sqlite3_clear_bindings(s.s)
return nil, err return nil, err
} }
return &SQLiteResult{int64(rowid), int64(changes)}, nil
return &SQLiteResult{id: int64(rowid), changes: int64(changes)}, nil
} }
// Close the rows. // Close the rows.
@ -761,6 +899,9 @@ func (rc *SQLiteRows) Close() error {
if rc.s.closed { if rc.s.closed {
return nil return nil
} }
if rc.done != nil {
close(rc.done)
}
if rc.cls { if rc.cls {
return rc.s.Close() return rc.s.Close()
} }
@ -771,7 +912,7 @@ func (rc *SQLiteRows) Close() error {
return nil return nil
} }
// Return column names. // Columns return column names.
func (rc *SQLiteRows) Columns() []string { func (rc *SQLiteRows) Columns() []string {
if rc.nc != len(rc.cols) { if rc.nc != len(rc.cols) {
rc.cols = make([]string, rc.nc) rc.cols = make([]string, rc.nc)
@ -782,7 +923,7 @@ func (rc *SQLiteRows) Columns() []string {
return rc.cols return rc.cols
} }
// Return column types. // DeclTypes return column types.
func (rc *SQLiteRows) DeclTypes() []string { func (rc *SQLiteRows) DeclTypes() []string {
if rc.decltype == nil { if rc.decltype == nil {
rc.decltype = make([]string, rc.nc) rc.decltype = make([]string, rc.nc)
@ -793,7 +934,7 @@ func (rc *SQLiteRows) DeclTypes() []string {
return rc.decltype return rc.decltype
} }
// Move cursor to next. // Next move cursor to next.
func (rc *SQLiteRows) Next(dest []driver.Value) error { func (rc *SQLiteRows) Next(dest []driver.Value) error {
rv := C.sqlite3_step(rc.s.s) rv := C.sqlite3_step(rc.s.s)
if rv == C.SQLITE_DONE { 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. // large to be a reasonable timestamp in seconds.
if val > 1e12 || val < -1e12 { if val > 1e12 || val < -1e12 {
val *= int64(time.Millisecond) // convert ms to nsec val *= int64(time.Millisecond) // convert ms to nsec
t = time.Unix(0, val)
} else { } 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 { if rc.s.c.loc != nil {
t = t.In(rc.s.c.loc) 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)
}

@ -31,6 +31,7 @@ func (c *SQLiteConn) loadExtensions(extensions []string) error {
defer C.free(unsafe.Pointer(cext)) defer C.free(unsafe.Pointer(cext))
rv = C.sqlite3_load_extension(c.db, cext, nil, nil) rv = C.sqlite3_load_extension(c.db, cext, nil, nil)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
C.sqlite3_enable_load_extension(c.db, 0)
return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
} }
} }
@ -42,6 +43,7 @@ func (c *SQLiteConn) loadExtensions(extensions []string) error {
return nil return nil
} }
// LoadExtension load the sqlite3 extension.
func (c *SQLiteConn) LoadExtension(lib string, entry string) error { func (c *SQLiteConn) LoadExtension(lib string, entry string) error {
rv := C.sqlite3_enable_load_extension(c.db, 1) rv := C.sqlite3_enable_load_extension(c.db, 1)
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {

@ -1,5 +1,4 @@
// Copyright (C) 2016 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // 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 // Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -17,7 +16,7 @@ package sqlite3
void stepTrampoline(sqlite3_context*, int, sqlite3_value**); void stepTrampoline(sqlite3_context*, int, sqlite3_value**);
void doneTrampoline(sqlite3_context*); 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" import "C"
@ -76,7 +75,7 @@ type TraceUserCallback func(TraceInfo) int
type TraceConfig struct { type TraceConfig struct {
Callback TraceUserCallback Callback TraceUserCallback
EventMask uint EventMask C.uint
WantExpandedSQL bool WantExpandedSQL bool
} }
@ -102,13 +101,13 @@ func fillExpandedSQL(info *TraceInfo, db *C.sqlite3, pStmt unsafe.Pointer) {
//export traceCallbackTrampoline //export traceCallbackTrampoline
func traceCallbackTrampoline( func traceCallbackTrampoline(
traceEventCode uint, traceEventCode C.uint,
// Parameter named 'C' in SQLite docs = Context given at registration: // Parameter named 'C' in SQLite docs = Context given at registration:
ctx unsafe.Pointer, ctx unsafe.Pointer,
// Parameter named 'P' in SQLite docs (Primary event data?): // Parameter named 'P' in SQLite docs (Primary event data?):
p unsafe.Pointer, p unsafe.Pointer,
// Parameter named 'X' in SQLite docs (eXtra event data?): // Parameter named 'X' in SQLite docs (eXtra event data?):
xValue unsafe.Pointer) int { xValue unsafe.Pointer) C.int {
if ctx == nil { if ctx == nil {
panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode)) panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode))
@ -196,7 +195,7 @@ func traceCallbackTrampoline(
if traceConf.Callback != nil { if traceConf.Callback != nil {
r = traceConf.Callback(info) r = traceConf.Callback(info)
} }
return r return C.int(r)
} }
type traceMapEntry struct { type traceMapEntry struct {
@ -358,7 +357,7 @@ func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool
if pure { if pure {
opts |= C.SQLITE_DETERMINISTIC 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 { if rv != C.SQLITE_OK {
return c.lastError() return c.lastError()
} }
@ -396,7 +395,7 @@ func (c *SQLiteConn) SetTrace(requested *TraceConfig) error {
// The callback trampoline function does cleanup on Close event, // The callback trampoline function does cleanup on Close event,
// regardless of the presence or absence of the user callback. // regardless of the presence or absence of the user callback.
// Therefore it needs the Close event to be selected: // Therefore it needs the Close event to be selected:
actualEventMask := reqCopy.EventMask | TraceClose actualEventMask := uint(reqCopy.EventMask | TraceClose)
err := c.setSQLiteTrace(actualEventMask) err := c.setSQLiteTrace(actualEventMask)
return err 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
}

@ -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" "revisionTime": "2017-02-23T14:12:10Z"
}, },
{ {
"checksumSHA1": "9FJUwn3EIgASVki+p8IHgWVC5vQ=", "checksumSHA1": "61HNjGetaBoMp8HBOpuEZRSim8g=",
"path": "github.com/mattn/go-sqlite3", "path": "github.com/mattn/go-sqlite3",
"revision": "86681de00adef4f8040947b7d35f97000fc5a230", "revision": "acfa60124032040b9f5a9406f5a772ee16fe845e",
"revisionTime": "2016-10-28T14:22:18Z" "revisionTime": "2017-07-05T08:25:03Z"
}, },
{ {
"checksumSHA1": "9mjAkIoPrJdiGcR0pBa81NoFY4U=", "checksumSHA1": "9mjAkIoPrJdiGcR0pBa81NoFY4U=",

Loading…
Cancel
Save