closes #2040
upstream commit: acfa601240
tags/v1.2.0-rc1
@@ -1,9 +1,10 @@ | |||||
go-sqlite3 | go-sqlite3 | ||||
========== | ========== | ||||
[](http://godoc.org/github.com/mattn/go-sqlite3) | |||||
[](https://travis-ci.org/mattn/go-sqlite3) | [](https://travis-ci.org/mattn/go-sqlite3) | ||||
[](https://coveralls.io/r/mattn/go-sqlite3?branch=master) | [](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 | Description | ||||
----------- | ----------- | ||||
@@ -16,11 +17,11 @@ Installation | |||||
This package can be installed with the go get command: | This package can be installed with the go get command: | ||||
go get github.com/mattn/go-sqlite3 | go get github.com/mattn/go-sqlite3 | ||||
_go-sqlite3_ is *cgo* package. | _go-sqlite3_ is *cgo* package. | ||||
If you want to build your app using go-sqlite3, you need gcc. | 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. | 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 | Documentation | ||||
------------- | ------------- | ||||
@@ -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. | |||||
> 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. | * 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. | |||||
// 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) { | 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) { | |||||
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 := 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) | ||||
@@ -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. | |||||
// 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{ | 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()) | |||||
return libVersion, libVersionNumber, sourceId | |||||
sourceID = C.GoString(C.sqlite3_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)), | |||||
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) { | 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 { | 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)) | |||||
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) | 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 | |||||
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: | 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(&b[0])), C.int(0)) | |||||
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&placeHolder[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) | |||||
} 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: | 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) | |||||
} |
@@ -2,7 +2,7 @@ | |||||
// | // | ||||
// 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. | ||||
// +build icu | |||||
// +build icu | |||||
package sqlite3 | package sqlite3 | ||||
@@ -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 | |||||
} |
@@ -13,7 +13,7 @@ | |||||
** This header file defines the SQLite interface for use by | ** This header file defines the SQLite interface for use by | ||||
** shared libraries that want to be imported as extensions into | ** shared libraries that want to be imported as extensions into | ||||
** an SQLite instance. Shared libraries that intend to be loaded | ** 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. | ** sqlite3.h. | ||||
*/ | */ | ||||
#ifndef SQLITE3EXT_H | #ifndef SQLITE3EXT_H | ||||
@@ -544,14 +544,14 @@ typedef int (*sqlite3_loadext_entry)( | |||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ | #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ | ||||
#if !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 */ | ** extension */ | ||||
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; | # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; | ||||
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; | # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; | ||||
# define SQLITE_EXTENSION_INIT3 \ | # define SQLITE_EXTENSION_INIT3 \ | ||||
extern const sqlite3_api_routines *sqlite3_api; | extern const sqlite3_api_routines *sqlite3_api; | ||||
#else | #else | ||||
/* This case when the file is being statically linked into the | |||||
/* This case when the file is being statically linked into the | |||||
** application */ | ** application */ | ||||
# define SQLITE_EXTENSION_INIT1 /*no-op*/ | # define SQLITE_EXTENSION_INIT1 /*no-op*/ | ||||
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ | # define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */ | ||||
@@ -562,4 +562,4 @@ typedef int (*sqlite3_loadext_entry)( | |||||
#else // USE_LIBSQLITE3 | #else // USE_LIBSQLITE3 | ||||
// If users really want to link against the system sqlite3 we | // If users really want to link against the system sqlite3 we | ||||
// need to make this file a noop. | // 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" | "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", | |||||
"revisionTime": "2016-10-28T14:22:18Z" | |||||
"revision": "acfa60124032040b9f5a9406f5a772ee16fe845e", | |||||
"revisionTime": "2017-07-05T08:25:03Z" | |||||
}, | }, | ||||
{ | { | ||||
"checksumSHA1": "9mjAkIoPrJdiGcR0pBa81NoFY4U=", | "checksumSHA1": "9mjAkIoPrJdiGcR0pBa81NoFY4U=", | ||||