Update go-sqlite3 to 1.14.6 (#1209)

This commit is contained in:
InfiniteTF
2021-03-17 01:17:27 +01:00
committed by GitHub
parent 58243cded0
commit 6369a500b3
22 changed files with 24453 additions and 17158 deletions

2
go.mod
View File

@@ -18,7 +18,7 @@ require (
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
github.com/jmoiron/sqlx v1.2.0 github.com/jmoiron/sqlx v1.2.0
github.com/json-iterator/go v1.1.9 github.com/json-iterator/go v1.1.9
github.com/mattn/go-sqlite3 v1.13.0 github.com/mattn/go-sqlite3 v1.14.6
github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007 github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007
github.com/remeh/sizedwaitgroup v1.0.0 github.com/remeh/sizedwaitgroup v1.0.0
github.com/rs/cors v1.6.0 github.com/rs/cors v1.6.0

2
go.sum
View File

@@ -540,6 +540,8 @@ github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK86
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c= github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c=
github.com/mattn/go-sqlite3 v1.13.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.13.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=

4
vendor/github.com/mattn/go-sqlite3/.codecov.yml generated vendored Normal file
View File

@@ -0,0 +1,4 @@
coverage:
status:
project: off
patch: off

View File

@@ -1,33 +0,0 @@
language: go
os:
- linux
- osx
addons:
apt:
update: true
go:
- 1.9.x
- 1.10.x
- 1.11.x
- 1.12.x
- 1.13.x
- master
before_install:
- |
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
brew update
fi
- go get github.com/smartystreets/goconvey
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx
- go test -race -v . -tags ""
- go test -race -v . -tags "libsqlite3"
- go test -race -v . -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth sqlite_vacuum_incr sqlite_vtable sqlite_unlock_notify"
- go test -race -v . -tags "sqlite_vacuum_full"

View File

@@ -2,16 +2,20 @@ go-sqlite3
========== ==========
[![GoDoc Reference](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/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) [![GitHub Actions](https://github.com/mattn/go-sqlite3/workflows/Go/badge.svg)](https://github.com/mattn/go-sqlite3/actions?query=workflow%3AGo)
[![Financial Contributors on Open Collective](https://opencollective.com/mattn-go-sqlite3/all/badge.svg?label=financial+contributors)](https://opencollective.com/mattn-go-sqlite3) [![Financial Contributors on Open Collective](https://opencollective.com/mattn-go-sqlite3/all/badge.svg?label=financial+contributors)](https://opencollective.com/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) [![codecov](https://codecov.io/gh/mattn/go-sqlite3/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-sqlite3)
[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-sqlite3)](https://goreportcard.com/report/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)
Latest stable version is v1.14 or later not v2.
~~**NOTE:** The increase to v2 was an accident. There were no major changes or features.~~
# Description # Description
sqlite3 driver conforming to the built-in database/sql interface sqlite3 driver conforming to the built-in database/sql interface
Supported Golang version: See .travis.yml Supported Golang version: See [.github/workflows/go.yaml](./.github/workflows/go.yaml)
[This package follows the official Golang Release Policy.](https://golang.org/doc/devel/release.html#policy) [This package follows the official Golang Release Policy.](https://golang.org/doc/devel/release.html#policy)
@@ -121,6 +125,8 @@ Boolean values can be one of:
| Time Zone Location | `_loc` | auto | Specify location of time format. | | Time Zone Location | `_loc` | auto | Specify location of time format. |
| Transaction Lock | `_txlock` | <ul><li>immediate</li><li>deferred</li><li>exclusive</li></ul> | Specify locking behavior for transactions. | | Transaction Lock | `_txlock` | <ul><li>immediate</li><li>deferred</li><li>exclusive</li></ul> | Specify locking behavior for transactions. |
| Writable Schema | `_writable_schema` | `Boolean` | When this pragma is on, the SQLITE_MASTER tables in which database can be changed using ordinary UPDATE, INSERT, and DELETE statements. Warning: misuse of this pragma can easily result in a corrupt database file. | | Writable Schema | `_writable_schema` | `Boolean` | When this pragma is on, the SQLITE_MASTER tables in which database can be changed using ordinary UPDATE, INSERT, and DELETE statements. Warning: misuse of this pragma can easily result in a corrupt database file. |
| Cache Size | `_cache_size` | `int` | Maximum cache size; default is 2000K (2M). See [PRAGMA cache_size](https://sqlite.org/pragma.html#pragma_cache_size) |
## DSN Examples ## DSN Examples
@@ -210,9 +216,15 @@ This library can be cross-compiled.
In some cases you are required to the `CC` environment variable with the cross compiler. In some cases you are required to the `CC` environment variable with the cross compiler.
Additional information: ## Cross Compiling from MAC OSX
- [#491](https://github.com/mattn/go-sqlite3/issues/491) The simplest way to cross compile from OSX is to use [xgo](https://github.com/karalabe/xgo).
- [#560](https://github.com/mattn/go-sqlite3/issues/560)
Steps:
- Install [xgo](https://github.com/karalabe/xgo) (`go get github.com/karalabe/xgo`).
- Ensure that your project is within your `GOPATH`.
- Run `xgo local/path/to/project`.
Please refer to the project's [README](https://github.com/karalabe/xgo/blob/master/README.md) for further information.
# Google Cloud Platform # Google Cloud Platform
@@ -451,6 +463,16 @@ If you want your own extension to be listed here or you want to add a reference
Spatialite is available as an extension to SQLite, and can be used in combination with this repository. Spatialite is available as an extension to SQLite, and can be used in combination with this repository.
For an example see [shaxbee/go-spatialite](https://github.com/shaxbee/go-spatialite). For an example see [shaxbee/go-spatialite](https://github.com/shaxbee/go-spatialite).
## extension-functions.c from SQLite3 Contrib
extension-functions.c is available as an extension to SQLite, and provides the following functions:
- Math: acos, asin, atan, atn2, atan2, acosh, asinh, atanh, difference, degrees, radians, cos, sin, tan, cot, cosh, sinh, tanh, coth, exp, log, log10, power, sign, sqrt, square, ceil, floor, pi.
- String: replicate, charindex, leftstr, rightstr, ltrim, rtrim, trim, replace, reverse, proper, padl, padr, padc, strfilter.
- Aggregate: stdev, variance, mode, median, lower_quartile, upper_quartile
For an example see [dinedal/go-sqlite3-extension-functions](https://github.com/dinedal/go-sqlite3-extension-functions).
# FAQ # FAQ
- Getting insert error while query is opened. - Getting insert error while query is opened.

View File

@@ -35,56 +35,55 @@ import (
//export callbackTrampoline //export callbackTrampoline
func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) { func callbackTrampoline(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] args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
fi := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*functionInfo) fi := lookupHandle(C.sqlite3_user_data(ctx)).(*functionInfo)
fi.Call(ctx, args) fi.Call(ctx, args)
} }
//export stepTrampoline //export stepTrampoline
func stepTrampoline(ctx *C.sqlite3_context, argc C.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))[:int(argc):int(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(C.sqlite3_user_data(ctx)).(*aggInfo)
ai.Step(ctx, args) ai.Step(ctx, args)
} }
//export doneTrampoline //export doneTrampoline
func doneTrampoline(ctx *C.sqlite3_context) { func doneTrampoline(ctx *C.sqlite3_context) {
handle := uintptr(C.sqlite3_user_data(ctx)) ai := lookupHandle(C.sqlite3_user_data(ctx)).(*aggInfo)
ai := lookupHandle(handle).(*aggInfo)
ai.Done(ctx) ai.Done(ctx)
} }
//export compareTrampoline //export compareTrampoline
func compareTrampoline(handlePtr uintptr, la C.int, a *C.char, lb C.int, b *C.char) C.int { func compareTrampoline(handlePtr unsafe.Pointer, la C.int, a *C.char, lb C.int, b *C.char) C.int {
cmp := lookupHandle(handlePtr).(func(string, string) int) cmp := lookupHandle(handlePtr).(func(string, string) int)
return C.int(cmp(C.GoStringN(a, la), C.GoStringN(b, lb))) return C.int(cmp(C.GoStringN(a, la), C.GoStringN(b, lb)))
} }
//export commitHookTrampoline //export commitHookTrampoline
func commitHookTrampoline(handle uintptr) int { func commitHookTrampoline(handle unsafe.Pointer) int {
callback := lookupHandle(handle).(func() int) callback := lookupHandle(handle).(func() int)
return callback() return callback()
} }
//export rollbackHookTrampoline //export rollbackHookTrampoline
func rollbackHookTrampoline(handle uintptr) { func rollbackHookTrampoline(handle unsafe.Pointer) {
callback := lookupHandle(handle).(func()) callback := lookupHandle(handle).(func())
callback() callback()
} }
//export updateHookTrampoline //export updateHookTrampoline
func updateHookTrampoline(handle uintptr, op int, db *C.char, table *C.char, rowid int64) { func updateHookTrampoline(handle unsafe.Pointer, op int, db *C.char, table *C.char, rowid int64) {
callback := lookupHandle(handle).(func(int, string, string, int64)) callback := lookupHandle(handle).(func(int, string, string, int64))
callback(op, C.GoString(db), C.GoString(table), rowid) callback(op, C.GoString(db), C.GoString(table), rowid)
} }
//export authorizerTrampoline //export authorizerTrampoline
func authorizerTrampoline(handle uintptr, op int, arg1 *C.char, arg2 *C.char, arg3 *C.char) int { func authorizerTrampoline(handle unsafe.Pointer, op int, arg1 *C.char, arg2 *C.char, arg3 *C.char) int {
callback := lookupHandle(handle).(func(int, string, string, string) int) callback := lookupHandle(handle).(func(int, string, string, string) int)
return callback(op, C.GoString(arg1), C.GoString(arg2), C.GoString(arg3)) return callback(op, C.GoString(arg1), C.GoString(arg2), C.GoString(arg3))
} }
//export preUpdateHookTrampoline //export preUpdateHookTrampoline
func preUpdateHookTrampoline(handle uintptr, dbHandle uintptr, op int, db *C.char, table *C.char, oldrowid int64, newrowid int64) { func preUpdateHookTrampoline(handle unsafe.Pointer, dbHandle uintptr, op int, db *C.char, table *C.char, oldrowid int64, newrowid int64) {
hval := lookupHandleVal(handle) hval := lookupHandleVal(handle)
data := SQLitePreUpdateData{ data := SQLitePreUpdateData{
Conn: hval.db, Conn: hval.db,
@@ -105,33 +104,27 @@ type handleVal struct {
} }
var handleLock sync.Mutex var handleLock sync.Mutex
var handleVals = make(map[uintptr]handleVal) var handleVals = make(map[unsafe.Pointer]handleVal)
var handleIndex uintptr = 100
func newHandle(db *SQLiteConn, v interface{}) uintptr { func newHandle(db *SQLiteConn, v interface{}) unsafe.Pointer {
handleLock.Lock() handleLock.Lock()
defer handleLock.Unlock() defer handleLock.Unlock()
i := handleIndex val := handleVal{db: db, val: v}
handleIndex++ var p unsafe.Pointer = C.malloc(C.size_t(1))
handleVals[i] = handleVal{db, v} if p == nil {
return i panic("can't allocate 'cgo-pointer hack index pointer': ptr == nil")
}
handleVals[p] = val
return p
} }
func lookupHandleVal(handle uintptr) handleVal { func lookupHandleVal(handle unsafe.Pointer) handleVal {
handleLock.Lock() handleLock.Lock()
defer handleLock.Unlock() defer handleLock.Unlock()
r, ok := handleVals[handle] return handleVals[handle]
if !ok {
if handle >= 100 && handle < handleIndex {
panic("deleted handle")
} else {
panic("invalid handle")
}
}
return r
} }
func lookupHandle(handle uintptr) interface{} { func lookupHandle(handle unsafe.Pointer) interface{} {
return lookupHandleVal(handle).val return lookupHandleVal(handle).val
} }
@@ -141,6 +134,7 @@ func deleteHandles(db *SQLiteConn) {
for handle, val := range handleVals { for handle, val := range handleVals {
if val.db == db { if val.db == db {
delete(handleVals, handle) delete(handleVals, handle)
C.free(handle)
} }
} }
} }

View File

@@ -79,9 +79,8 @@ Then, you can use this extension.
Connection Hook Connection Hook
You can hook and inject your code when the connection is established. database/sql You can hook and inject your code when the connection is established by setting
doesn't provide a way to get native go-sqlite3 interfaces. So if you want, ConnectHook to get the SQLiteConn.
you need to set ConnectHook and get the SQLiteConn.
sql.Register("sqlite3_with_hook_example", sql.Register("sqlite3_with_hook_example",
&sqlite3.SQLiteDriver{ &sqlite3.SQLiteDriver{
@@ -91,21 +90,45 @@ you need to set ConnectHook and get the SQLiteConn.
}, },
}) })
You can also use database/sql.Conn.Raw (Go >= 1.13):
conn, err := db.Conn(context.Background())
// if err != nil { ... }
defer conn.Close()
err = conn.Raw(func (driverConn interface{}) error {
sqliteConn := driverConn.(*sqlite3.SQLiteConn)
// ... use sqliteConn
})
// if err != nil { ... }
Go SQlite3 Extensions Go SQlite3 Extensions
If you want to register Go functions as SQLite extension functions, If you want to register Go functions as SQLite extension functions
call RegisterFunction from ConnectHook. you can make a custom driver by calling RegisterFunction from
ConnectHook.
regex = func(re, s string) (bool, error) { regex = func(re, s string) (bool, error) {
return regexp.MatchString(re, s) return regexp.MatchString(re, s)
} }
sql.Register("sqlite3_with_go_func", sql.Register("sqlite3_extended",
&sqlite3.SQLiteDriver{ &sqlite3.SQLiteDriver{
ConnectHook: func(conn *sqlite3.SQLiteConn) error { ConnectHook: func(conn *sqlite3.SQLiteConn) error {
return conn.RegisterFunc("regexp", regex, true) return conn.RegisterFunc("regexp", regex, true)
}, },
}) })
You can then use the custom driver by passing its name to sql.Open.
var i int
conn, err := sql.Open("sqlite3_extended", "./foo.db")
if err != nil {
panic(err)
}
err = db.QueryRow(`SELECT regexp("foo.*", "seafood")`).Scan(&i)
if err != nil {
panic(err)
}
See the documentation of RegisterFunc for more details. See the documentation of RegisterFunc for more details.
*/ */

View File

@@ -5,7 +5,15 @@
package sqlite3 package sqlite3
/*
#ifndef USE_LIBSQLITE3
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
*/
import "C" import "C"
import "syscall"
// ErrNo inherit errno. // ErrNo inherit errno.
type ErrNo int type ErrNo int
@@ -20,6 +28,7 @@ type ErrNoExtended int
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 */
SystemErrno syscall.Errno /* The system errno returned by the OS through SQLite, if applicable */
err string /* The error string returned by sqlite3_errmsg(), err string /* The error string returned by sqlite3_errmsg(),
this usually contains more specific details. */ this usually contains more specific details. */
} }
@@ -72,10 +81,16 @@ func (err ErrNoExtended) Error() string {
} }
func (err Error) Error() string { func (err Error) Error() string {
var str string
if err.err != "" { if err.err != "" {
return err.err str = err.err
} else {
str = C.GoString(C.sqlite3_errstr(C.int(err.Code)))
} }
return errorString(err) if err.SystemErrno != 0 {
str += ": " + err.SystemErrno.Error()
}
return str
} }
// result codes from http://www.sqlite.org/c3ref/c_abort_rollback.html // result codes from http://www.sqlite.org/c3ref/c_abort_rollback.html

3
vendor/github.com/mattn/go-sqlite3/go.mod generated vendored Normal file
View File

@@ -0,0 +1,3 @@
module github.com/mattn/go-sqlite3
go 1.12

0
vendor/github.com/mattn/go-sqlite3/go.sum generated vendored Normal file
View File

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -15,10 +15,8 @@ package sqlite3
#cgo CFLAGS: -DHAVE_USLEEP=1 #cgo CFLAGS: -DHAVE_USLEEP=1
#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 #cgo CFLAGS: -DSQLITE_ENABLE_FTS3
#cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS #cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS
#cgo CFLAGS: -DSQLITE_ENABLE_FTS4_UNICODE61
#cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15 #cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15
#cgo CFLAGS: -DSQLITE_OMIT_DEPRECATED #cgo CFLAGS: -DSQLITE_OMIT_DEPRECATED
#cgo CFLAGS: -DSQLITE_DISABLE_INTRINSIC
#cgo CFLAGS: -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 #cgo CFLAGS: -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1
#cgo CFLAGS: -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT #cgo CFLAGS: -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT
#cgo CFLAGS: -Wno-deprecated-declarations #cgo CFLAGS: -Wno-deprecated-declarations
@@ -183,6 +181,12 @@ static int _sqlite3_limit(sqlite3* db, int limitId, int newLimit) {
return sqlite3_limit(db, limitId, newLimit); return sqlite3_limit(db, limitId, newLimit);
#endif #endif
} }
#if SQLITE_VERSION_NUMBER < 3012000
static int sqlite3_system_errno(sqlite3 *db) {
return 0;
}
#endif
*/ */
import "C" import "C"
import ( import (
@@ -198,6 +202,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
"unsafe" "unsafe"
) )
@@ -466,7 +471,7 @@ func (c *SQLiteConn) RegisterCollation(name string, cmp func(string, string) int
handle := newHandle(c, cmp) handle := newHandle(c, cmp)
cname := C.CString(name) cname := C.CString(name)
defer C.free(unsafe.Pointer(cname)) defer C.free(unsafe.Pointer(cname))
rv := C.sqlite3_create_collation(c.db, cname, C.SQLITE_UTF8, unsafe.Pointer(handle), (*[0]byte)(unsafe.Pointer(C.compareTrampoline))) rv := C.sqlite3_create_collation(c.db, cname, C.SQLITE_UTF8, handle, (*[0]byte)(unsafe.Pointer(C.compareTrampoline)))
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return c.lastError() return c.lastError()
} }
@@ -484,7 +489,7 @@ func (c *SQLiteConn) RegisterCommitHook(callback func() int) {
if callback == nil { if callback == nil {
C.sqlite3_commit_hook(c.db, nil, nil) C.sqlite3_commit_hook(c.db, nil, nil)
} else { } else {
C.sqlite3_commit_hook(c.db, (*[0]byte)(C.commitHookTrampoline), unsafe.Pointer(newHandle(c, callback))) C.sqlite3_commit_hook(c.db, (*[0]byte)(C.commitHookTrampoline), newHandle(c, callback))
} }
} }
@@ -497,7 +502,7 @@ func (c *SQLiteConn) RegisterRollbackHook(callback func()) {
if callback == nil { if callback == nil {
C.sqlite3_rollback_hook(c.db, nil, nil) C.sqlite3_rollback_hook(c.db, nil, nil)
} else { } else {
C.sqlite3_rollback_hook(c.db, (*[0]byte)(C.rollbackHookTrampoline), unsafe.Pointer(newHandle(c, callback))) C.sqlite3_rollback_hook(c.db, (*[0]byte)(C.rollbackHookTrampoline), newHandle(c, callback))
} }
} }
@@ -514,7 +519,7 @@ func (c *SQLiteConn) RegisterUpdateHook(callback func(int, string, string, int64
if callback == nil { if callback == nil {
C.sqlite3_update_hook(c.db, nil, nil) C.sqlite3_update_hook(c.db, nil, nil)
} else { } else {
C.sqlite3_update_hook(c.db, (*[0]byte)(C.updateHookTrampoline), unsafe.Pointer(newHandle(c, callback))) C.sqlite3_update_hook(c.db, (*[0]byte)(C.updateHookTrampoline), newHandle(c, callback))
} }
} }
@@ -528,7 +533,7 @@ func (c *SQLiteConn) RegisterAuthorizer(callback func(int, string, string, strin
if callback == nil { if callback == nil {
C.sqlite3_set_authorizer(c.db, nil, nil) C.sqlite3_set_authorizer(c.db, nil, nil)
} else { } else {
C.sqlite3_set_authorizer(c.db, (*[0]byte)(C.authorizerTrampoline), unsafe.Pointer(newHandle(c, callback))) C.sqlite3_set_authorizer(c.db, (*[0]byte)(C.authorizerTrampoline), newHandle(c, callback))
} }
} }
@@ -609,8 +614,8 @@ func (c *SQLiteConn) RegisterFunc(name string, impl interface{}, pure bool) erro
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 { func sqlite3CreateFunction(db *C.sqlite3, zFunctionName *C.char, nArg C.int, eTextRep C.int, pApp unsafe.Pointer, 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)(xFunc), (*[0]byte)(xStep), (*[0]byte)(xFinal)) return C._sqlite3_create_function(db, zFunctionName, nArg, eTextRep, C.uintptr_t(uintptr(pApp)), (*[0]byte)(xFunc), (*[0]byte)(xStep), (*[0]byte)(xFinal))
} }
// RegisterAggregator makes a Go type available as a SQLite aggregation function. // RegisterAggregator makes a Go type available as a SQLite aggregation function.
@@ -749,15 +754,28 @@ func (c *SQLiteConn) lastError() error {
return lastError(c.db) return lastError(c.db)
} }
// Note: may be called with db == nil
func lastError(db *C.sqlite3) error { func lastError(db *C.sqlite3) error {
rv := C.sqlite3_errcode(db) rv := C.sqlite3_errcode(db) // returns SQLITE_NOMEM if db == nil
if rv == C.SQLITE_OK { if rv == C.SQLITE_OK {
return nil return nil
} }
extrv := C.sqlite3_extended_errcode(db) // returns SQLITE_NOMEM if db == nil
errStr := C.GoString(C.sqlite3_errmsg(db)) // returns "out of memory" if db == nil
// https://www.sqlite.org/c3ref/system_errno.html
// sqlite3_system_errno is only meaningful if the error code was SQLITE_CANTOPEN,
// or it was SQLITE_IOERR and the extended code was not SQLITE_IOERR_NOMEM
var systemErrno syscall.Errno
if rv == C.SQLITE_CANTOPEN || (rv == C.SQLITE_IOERR && extrv != C.SQLITE_IOERR_NOMEM) {
systemErrno = syscall.Errno(C.sqlite3_system_errno(db))
}
return Error{ return Error{
Code: ErrNo(rv), Code: ErrNo(rv),
ExtendedCode: ErrNoExtended(C.sqlite3_extended_errcode(db)), ExtendedCode: ErrNoExtended(extrv),
err: C.GoString(C.sqlite3_errmsg(db)), SystemErrno: systemErrno,
err: errStr,
} }
} }
@@ -782,20 +800,29 @@ func (c *SQLiteConn) exec(ctx context.Context, query string, args []namedValue)
} }
var res driver.Result var res driver.Result
if s.(*SQLiteStmt).s != nil { if s.(*SQLiteStmt).s != nil {
stmtArgs := make([]namedValue, 0, len(args))
na := s.NumInput() na := s.NumInput()
if len(args) < na { if len(args)-start < na {
s.Close() s.Close()
return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)) return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args))
} }
for i := 0; i < na; i++ { // consume the number of arguments used in the current
args[i].Ordinal -= start // statement and append all named arguments not
// contained therein
stmtArgs = append(stmtArgs, args[start:start+na]...)
for i := range args {
if (i < start || i >= na) && args[i].Name != "" {
stmtArgs = append(stmtArgs, args[i])
} }
res, err = s.(*SQLiteStmt).exec(ctx, args[:na]) }
for i := range stmtArgs {
stmtArgs[i].Ordinal = i + 1
}
res, err = s.(*SQLiteStmt).exec(ctx, stmtArgs)
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:]
start += na start += na
} }
tail := s.(*SQLiteStmt).t tail := s.(*SQLiteStmt).t
@@ -828,24 +855,33 @@ func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, erro
func (c *SQLiteConn) query(ctx context.Context, query string, args []namedValue) (driver.Rows, error) { func (c *SQLiteConn) query(ctx context.Context, query string, args []namedValue) (driver.Rows, error) {
start := 0 start := 0
for { for {
stmtArgs := make([]namedValue, 0, len(args))
s, err := c.prepare(ctx, 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)-start < na {
return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)) return nil, fmt.Errorf("not enough args to execute query: want %d got %d", na, len(args)-start)
} }
for i := 0; i < na; i++ { // consume the number of arguments used in the current
args[i].Ordinal -= start // statement and append all named arguments not contained
// therein
stmtArgs = append(stmtArgs, args[start:start+na]...)
for i := range args {
if (i < start || i >= na) && args[i].Name != "" {
stmtArgs = append(stmtArgs, args[i])
} }
rows, err := s.(*SQLiteStmt).query(ctx, args[:na]) }
for i := range stmtArgs {
stmtArgs[i].Ordinal = i + 1
}
rows, err := s.(*SQLiteStmt).query(ctx, stmtArgs)
if err != nil && err != driver.ErrSkip { if err != nil && err != driver.ErrSkip {
s.Close() s.Close()
return rows, err return rows, err
} }
args = args[na:]
start += na start += na
tail := s.(*SQLiteStmt).t tail := s.(*SQLiteStmt).t
if tail == "" { if tail == "" {
@@ -869,10 +905,6 @@ func (c *SQLiteConn) begin(ctx context.Context) (driver.Tx, error) {
return &SQLiteTx{c}, nil return &SQLiteTx{c}, nil
} }
func errorString(err Error) string {
return C.GoString(C.sqlite3_errstr(C.int(err.Code)))
}
// Open database and return a new connection. // Open database and return a new connection.
// //
// A pragma can take either zero or one argument. // A pragma can take either zero or one argument.
@@ -897,7 +929,7 @@ func errorString(err Error) string {
// - rwc // - rwc
// - memory // - memory
// //
// shared // cache
// SQLite Shared-Cache Mode // SQLite Shared-Cache Mode
// https://www.sqlite.org/sharedcache.html // https://www.sqlite.org/sharedcache.html
// Values: // Values:
@@ -1007,6 +1039,8 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
secureDelete := "DEFAULT" secureDelete := "DEFAULT"
synchronousMode := "NORMAL" synchronousMode := "NORMAL"
writableSchema := -1 writableSchema := -1
vfsName := ""
var cacheSize *int64
pos := strings.IndexRune(dsn, '?') pos := strings.IndexRune(dsn, '?')
if pos >= 1 { if pos >= 1 {
@@ -1330,6 +1364,22 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
} }
} }
// Cache size (_cache_size)
//
// https://sqlite.org/pragma.html#pragma_cache_size
//
if val := params.Get("_cache_size"); val != "" {
iv, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return nil, fmt.Errorf("Invalid _cache_size: %v: %v", val, err)
}
cacheSize = &iv
}
if val := params.Get("vfs"); val != "" {
vfsName = val
}
if !strings.HasPrefix(dsn, "file:") { if !strings.HasPrefix(dsn, "file:") {
dsn = dsn[:pos] dsn = dsn[:pos]
} }
@@ -1338,14 +1388,22 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
var db *C.sqlite3 var db *C.sqlite3
name := C.CString(dsn) name := C.CString(dsn)
defer C.free(unsafe.Pointer(name)) defer C.free(unsafe.Pointer(name))
var vfs *C.char
if vfsName != "" {
vfs = C.CString(vfsName)
defer C.free(unsafe.Pointer(vfs))
}
rv := C._sqlite3_open_v2(name, &db, rv := C._sqlite3_open_v2(name, &db,
mutex|C.SQLITE_OPEN_READWRITE|C.SQLITE_OPEN_CREATE, mutex|C.SQLITE_OPEN_READWRITE|C.SQLITE_OPEN_CREATE,
nil) vfs)
if rv != 0 { if rv != 0 {
// Save off the error _before_ closing the database.
// This is safe even if db is nil.
err := lastError(db)
if db != nil { if db != nil {
C.sqlite3_close_v2(db) C.sqlite3_close_v2(db)
} }
return nil, Error{Code: ErrNo(rv)} return nil, err
} }
if db == nil { if db == nil {
return nil, errors.New("sqlite succeeded without returning a database") return nil, errors.New("sqlite succeeded without returning a database")
@@ -1630,6 +1688,14 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
} }
} }
// Cache Size
if cacheSize != nil {
if err := exec(fmt.Sprintf("PRAGMA cache_size = %d;", *cacheSize)); err != nil {
C.sqlite3_close_v2(db)
return nil, err
}
}
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() conn.Close()
@@ -1759,11 +1825,6 @@ func (s *SQLiteStmt) NumInput() int {
return int(C.sqlite3_bind_parameter_count(s.s)) return int(C.sqlite3_bind_parameter_count(s.s))
} }
type bindArg struct {
n int
v driver.Value
}
var placeHolder = []byte{0} var placeHolder = []byte{0}
func (s *SQLiteStmt) bind(args []namedValue) error { func (s *SQLiteStmt) bind(args []namedValue) error {
@@ -1772,16 +1833,26 @@ func (s *SQLiteStmt) bind(args []namedValue) error {
return s.c.lastError() return s.c.lastError()
} }
bindIndices := make([][3]int, len(args))
prefixes := []string{":", "@", "$"}
for i, v := range args { for i, v := range args {
bindIndices[i][0] = args[i].Ordinal
if v.Name != "" { if v.Name != "" {
cname := C.CString(":" + v.Name) for j := range prefixes {
args[i].Ordinal = int(C.sqlite3_bind_parameter_index(s.s, cname)) cname := C.CString(prefixes[j] + v.Name)
bindIndices[i][j] = int(C.sqlite3_bind_parameter_index(s.s, cname))
C.free(unsafe.Pointer(cname)) C.free(unsafe.Pointer(cname))
} }
args[i].Ordinal = bindIndices[i][0]
}
} }
for _, arg := range args { for i, arg := range args {
n := C.int(arg.Ordinal) for j := range bindIndices[i] {
if bindIndices[i][j] == 0 {
continue
}
n := C.int(bindIndices[i][j])
switch v := arg.Value.(type) { 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)
@@ -1820,6 +1891,7 @@ func (s *SQLiteStmt) bind(args []namedValue) error {
return s.c.lastError() return s.c.lastError()
} }
} }
}
return nil return nil
} }
@@ -1875,6 +1947,14 @@ func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) {
return s.exec(context.Background(), list) return s.exec(context.Background(), list)
} }
func isInterruptErr(err error) bool {
sqliteErr, ok := err.(Error)
if ok {
return sqliteErr.Code == ErrInterrupt
}
return false
}
// exec executes a query that doesn't return rows. Attempts to honor context timeout. // exec executes a query that doesn't return rows. Attempts to honor context timeout.
func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result, error) { func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result, error) {
if ctx.Done() == nil { if ctx.Done() == nil {
@@ -1890,20 +1970,23 @@ func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result
r, err := s.execSync(args) r, err := s.execSync(args)
resultCh <- result{r, err} resultCh <- result{r, err}
}() }()
var rv result
select { select {
case rv := <- resultCh: case rv = <-resultCh:
return rv.r, rv.err
case <-ctx.Done(): case <-ctx.Done():
select { select {
case <-resultCh: // no need to interrupt case rv = <-resultCh: // no need to interrupt, operation completed in db
default: default:
// this is still racy and can be no-op if executed between sqlite3_* calls in execSync. // this is still racy and can be no-op if executed between sqlite3_* calls in execSync.
C.sqlite3_interrupt(s.c.db) C.sqlite3_interrupt(s.c.db)
<-resultCh // ensure goroutine completed rv = <-resultCh // wait for goroutine completed
} if isInterruptErr(rv.err) {
return nil, ctx.Err() return nil, ctx.Err()
} }
} }
}
return rv.r, rv.err
}
func (s *SQLiteStmt) execSync(args []namedValue) (driver.Result, error) { func (s *SQLiteStmt) execSync(args []namedValue) (driver.Result, error) {
if err := s.bind(args); err != nil { if err := s.bind(args); err != nil {

View File

@@ -14,5 +14,6 @@ package sqlite3
#cgo darwin CFLAGS: -I/usr/local/opt/sqlite/include #cgo darwin CFLAGS: -I/usr/local/opt/sqlite/include
#cgo openbsd LDFLAGS: -lsqlite3 #cgo openbsd LDFLAGS: -lsqlite3
#cgo solaris LDFLAGS: -lsqlite3 #cgo solaris LDFLAGS: -lsqlite3
#cgo windows LDFLAGS: -lsqlite3
*/ */
import "C" import "C"

View File

@@ -28,12 +28,9 @@ func (c *SQLiteConn) loadExtensions(extensions []string) error {
} }
for _, extension := range extensions { for _, extension := range extensions {
cext := C.CString(extension) if err := c.loadExtension(extension, nil); err != nil {
defer C.free(unsafe.Pointer(cext))
rv = C.sqlite3_load_extension(c.db, cext, nil, nil)
if rv != C.SQLITE_OK {
C.sqlite3_enable_load_extension(c.db, 0) C.sqlite3_enable_load_extension(c.db, 0)
return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) return err
} }
} }
@@ -41,6 +38,7 @@ func (c *SQLiteConn) loadExtensions(extensions []string) error {
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
} }
return nil return nil
} }
@@ -51,14 +49,9 @@ func (c *SQLiteConn) LoadExtension(lib string, entry string) error {
return errors.New(C.GoString(C.sqlite3_errmsg(c.db))) return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
} }
clib := C.CString(lib) if err := c.loadExtension(lib, &entry); err != nil {
defer C.free(unsafe.Pointer(clib)) C.sqlite3_enable_load_extension(c.db, 0)
centry := C.CString(entry) return err
defer C.free(unsafe.Pointer(centry))
rv = C.sqlite3_load_extension(c.db, clib, centry, nil)
if rv != C.SQLITE_OK {
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
} }
rv = C.sqlite3_enable_load_extension(c.db, 0) rv = C.sqlite3_enable_load_extension(c.db, 0)
@@ -68,3 +61,24 @@ func (c *SQLiteConn) LoadExtension(lib string, entry string) error {
return nil return nil
} }
func (c *SQLiteConn) loadExtension(lib string, entry *string) error {
clib := C.CString(lib)
defer C.free(unsafe.Pointer(clib))
var centry *C.char
if entry != nil {
centry = C.CString(*entry)
defer C.free(unsafe.Pointer(centry))
}
var errMsg *C.char
defer C.sqlite3_free(unsafe.Pointer(errMsg))
rv := C.sqlite3_load_extension(c.db, clib, centry, &errMsg)
if rv != C.SQLITE_OK {
return errors.New(C.GoString(errMsg))
}
return nil
}

View File

@@ -226,11 +226,43 @@ static sqlite3_module goModule = {
0 // xRollbackTo 0 // xRollbackTo
}; };
// See https://sqlite.org/vtab.html#eponymous_only_virtual_tables
static sqlite3_module goModuleEponymousOnly = {
0, // iVersion
0, // xCreate - create a table, which here is null
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*); void goMDestroy(void*);
static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pClientData) { static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pClientData) {
return sqlite3_create_module_v2(db, zName, &goModule, (void*) pClientData, goMDestroy); return sqlite3_create_module_v2(db, zName, &goModule, (void*) pClientData, goMDestroy);
} }
static int _sqlite3_create_module_eponymous_only(sqlite3 *db, const char *zName, uintptr_t pClientData) {
return sqlite3_create_module_v2(db, zName, &goModuleEponymousOnly, (void*) pClientData, goMDestroy);
}
*/ */
import "C" import "C"
@@ -288,10 +320,13 @@ type InfoOrderBy struct {
} }
func constraints(info *C.sqlite3_index_info) []InfoConstraint { func constraints(info *C.sqlite3_index_info) []InfoConstraint {
l := info.nConstraint slice := *(*[]C.struct_sqlite3_index_constraint)(unsafe.Pointer(&reflect.SliceHeader{
slice := (*[1 << 30]C.struct_sqlite3_index_constraint)(unsafe.Pointer(info.aConstraint))[:l:l] Data: uintptr(unsafe.Pointer(info.aConstraint)),
Len: int(info.nConstraint),
Cap: int(info.nConstraint),
}))
cst := make([]InfoConstraint, 0, l) cst := make([]InfoConstraint, 0, len(slice))
for _, c := range slice { for _, c := range slice {
var usable bool var usable bool
if c.usable > 0 { if c.usable > 0 {
@@ -307,10 +342,13 @@ func constraints(info *C.sqlite3_index_info) []InfoConstraint {
} }
func orderBys(info *C.sqlite3_index_info) []InfoOrderBy { func orderBys(info *C.sqlite3_index_info) []InfoOrderBy {
l := info.nOrderBy slice := *(*[]C.struct_sqlite3_index_orderby)(unsafe.Pointer(&reflect.SliceHeader{
slice := (*[1 << 30]C.struct_sqlite3_index_orderby)(unsafe.Pointer(info.aOrderBy))[:l:l] Data: uintptr(unsafe.Pointer(info.aOrderBy)),
Len: int(info.nOrderBy),
Cap: int(info.nOrderBy),
}))
ob := make([]InfoOrderBy, 0, l) ob := make([]InfoOrderBy, 0, len(slice))
for _, c := range slice { for _, c := range slice {
var desc bool var desc bool
if c.desc > 0 { if c.desc > 0 {
@@ -347,7 +385,7 @@ func mPrintf(format, arg string) *C.char {
//export goMInit //export goMInit
func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr **C.char, isCreate C.int) C.uintptr_t { 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) m := lookupHandle(pClientData).(*sqliteModule)
if m.c.db != (*C.sqlite3)(db) { if m.c.db != (*C.sqlite3)(db) {
*pzErr = mPrintf("%s", "Inconsistent db handles") *pzErr = mPrintf("%s", "Inconsistent db handles")
return 0 return 0
@@ -373,12 +411,12 @@ func goMInit(db, pClientData unsafe.Pointer, argc C.int, argv **C.char, pzErr **
} }
vt := sqliteVTab{m, vTab} vt := sqliteVTab{m, vTab}
*pzErr = nil *pzErr = nil
return C.uintptr_t(newHandle(m.c, &vt)) return C.uintptr_t(uintptr(newHandle(m.c, &vt)))
} }
//export goVRelease //export goVRelease
func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char { func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char {
vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) vt := lookupHandle(pVTab).(*sqliteVTab)
var err error var err error
if isDestroy == 1 { if isDestroy == 1 {
err = vt.vTab.Destroy() err = vt.vTab.Destroy()
@@ -393,7 +431,7 @@ func goVRelease(pVTab unsafe.Pointer, isDestroy C.int) *C.char {
//export goVOpen //export goVOpen
func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t { func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t {
vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) vt := lookupHandle(pVTab).(*sqliteVTab)
vTabCursor, err := vt.vTab.Open() vTabCursor, err := vt.vTab.Open()
if err != nil { if err != nil {
*pzErr = mPrintf("%s", err.Error()) *pzErr = mPrintf("%s", err.Error())
@@ -401,12 +439,12 @@ func goVOpen(pVTab unsafe.Pointer, pzErr **C.char) C.uintptr_t {
} }
vtc := sqliteVTabCursor{vt, vTabCursor} vtc := sqliteVTabCursor{vt, vTabCursor}
*pzErr = nil *pzErr = nil
return C.uintptr_t(newHandle(vt.module.c, &vtc)) return C.uintptr_t(uintptr(newHandle(vt.module.c, &vtc)))
} }
//export goVBestIndex //export goVBestIndex
func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char { func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char {
vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) vt := lookupHandle(pVTab).(*sqliteVTab)
info := (*C.sqlite3_index_info)(icp) info := (*C.sqlite3_index_info)(icp)
csts := constraints(info) csts := constraints(info)
res, err := vt.vTab.BestIndex(csts, orderBys(info)) res, err := vt.vTab.BestIndex(csts, orderBys(info))
@@ -418,13 +456,17 @@ func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char {
} }
// Get a pointer to constraint_usage struct so we can update in place. // 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] slice := *(*[]C.struct_sqlite3_index_constraint_usage)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(info.aConstraintUsage)),
Len: int(info.nConstraint),
Cap: int(info.nConstraint),
}))
index := 1 index := 1
for i := C.int(0); i < info.nConstraint; i++ { for i := range slice {
if res.Used[i] { if res.Used[i] {
s[i].argvIndex = C.int(index) slice[i].argvIndex = C.int(index)
s[i].omit = C.uchar(1) slice[i].omit = C.uchar(1)
index++ index++
} }
} }
@@ -445,7 +487,7 @@ func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char {
//export goVClose //export goVClose
func goVClose(pCursor unsafe.Pointer) *C.char { func goVClose(pCursor unsafe.Pointer) *C.char {
vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
err := vtc.vTabCursor.Close() err := vtc.vTabCursor.Close()
if err != nil { if err != nil {
return mPrintf("%s", err.Error()) return mPrintf("%s", err.Error())
@@ -455,13 +497,13 @@ func goVClose(pCursor unsafe.Pointer) *C.char {
//export goMDestroy //export goMDestroy
func goMDestroy(pClientData unsafe.Pointer) { func goMDestroy(pClientData unsafe.Pointer) {
m := lookupHandle(uintptr(pClientData)).(*sqliteModule) m := lookupHandle(pClientData).(*sqliteModule)
m.module.DestroyModule() m.module.DestroyModule()
} }
//export goVFilter //export goVFilter
func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int, argv **C.sqlite3_value) *C.char { 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) vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
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))[:argc:argc]
vals := make([]interface{}, 0, argc) vals := make([]interface{}, 0, argc)
for _, v := range args { for _, v := range args {
@@ -480,7 +522,7 @@ func goVFilter(pCursor unsafe.Pointer, idxNum C.int, idxName *C.char, argc C.int
//export goVNext //export goVNext
func goVNext(pCursor unsafe.Pointer) *C.char { func goVNext(pCursor unsafe.Pointer) *C.char {
vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
err := vtc.vTabCursor.Next() err := vtc.vTabCursor.Next()
if err != nil { if err != nil {
return mPrintf("%s", err.Error()) return mPrintf("%s", err.Error())
@@ -490,7 +532,7 @@ func goVNext(pCursor unsafe.Pointer) *C.char {
//export goVEof //export goVEof
func goVEof(pCursor unsafe.Pointer) C.int { func goVEof(pCursor unsafe.Pointer) C.int {
vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
err := vtc.vTabCursor.EOF() err := vtc.vTabCursor.EOF()
if err { if err {
return 1 return 1
@@ -500,7 +542,7 @@ func goVEof(pCursor unsafe.Pointer) C.int {
//export goVColumn //export goVColumn
func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char { func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char {
vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
c := (*SQLiteContext)(cp) c := (*SQLiteContext)(cp)
err := vtc.vTabCursor.Column(c, int(col)) err := vtc.vTabCursor.Column(c, int(col))
if err != nil { if err != nil {
@@ -511,7 +553,7 @@ func goVColumn(pCursor, cp unsafe.Pointer, col C.int) *C.char {
//export goVRowid //export goVRowid
func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char { func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char {
vtc := lookupHandle(uintptr(pCursor)).(*sqliteVTabCursor) vtc := lookupHandle(pCursor).(*sqliteVTabCursor)
rowid, err := vtc.vTabCursor.Rowid() rowid, err := vtc.vTabCursor.Rowid()
if err != nil { if err != nil {
return mPrintf("%s", err.Error()) return mPrintf("%s", err.Error())
@@ -522,7 +564,7 @@ func goVRowid(pCursor unsafe.Pointer, pRowid *C.sqlite3_int64) *C.char {
//export goVUpdate //export goVUpdate
func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char { func goVUpdate(pVTab unsafe.Pointer, argc C.int, argv **C.sqlite3_value, pRowid *C.sqlite3_int64) *C.char {
vt := lookupHandle(uintptr(pVTab)).(*sqliteVTab) vt := lookupHandle(pVTab).(*sqliteVTab)
var tname string var tname string
if n, ok := vt.vTab.(interface { if n, ok := vt.vTab.(interface {
@@ -585,6 +627,13 @@ type Module interface {
DestroyModule() DestroyModule()
} }
// EponymousOnlyModule is a "virtual table module" (as above), but
// for defining "eponymous only" virtual tables See: https://sqlite.org/vtab.html#eponymous_only_virtual_tables
type EponymousOnlyModule interface {
Module
EponymousOnlyModule()
}
// VTab describes a particular instance of the virtual table. // VTab describes a particular instance of the virtual table.
// See: http://sqlite.org/c3ref/vtab.html // See: http://sqlite.org/c3ref/vtab.html
type VTab interface { type VTab interface {
@@ -642,9 +691,19 @@ func (c *SQLiteConn) CreateModule(moduleName string, module Module) error {
mname := C.CString(moduleName) mname := C.CString(moduleName)
defer C.free(unsafe.Pointer(mname)) defer C.free(unsafe.Pointer(mname))
udm := sqliteModule{c, moduleName, module} udm := sqliteModule{c, moduleName, module}
rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(newHandle(c, &udm))) switch module.(type) {
case EponymousOnlyModule:
rv := C._sqlite3_create_module_eponymous_only(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm))))
if rv != C.SQLITE_OK {
return c.lastError()
}
return nil
case Module:
rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm))))
if rv != C.SQLITE_OK { if rv != C.SQLITE_OK {
return c.lastError() return c.lastError()
} }
return nil return nil
} }
return nil
}

View File

@@ -215,7 +215,6 @@ func addTraceMapping(connHandle uintptr, traceConf TraceConfig) {
traceConf, connHandle, oldEntryCopy.config)) traceConf, connHandle, oldEntryCopy.config))
} }
traceMap[connHandle] = traceMapEntry{config: traceConf} traceMap[connHandle] = traceMapEntry{config: traceConf}
fmt.Printf("Added trace config %v: handle 0x%x.\n", traceConf, connHandle)
} }
func lookupTraceMapping(connHandle uintptr) (TraceConfig, bool) { func lookupTraceMapping(connHandle uintptr) (TraceConfig, bool) {
@@ -234,7 +233,6 @@ func popTraceMapping(connHandle uintptr) (TraceConfig, bool) {
entryCopy, found := traceMap[connHandle] entryCopy, found := traceMap[connHandle]
if found { if found {
delete(traceMap, connHandle) delete(traceMap, connHandle)
fmt.Printf("Pop handle 0x%x: deleted trace config %v.\n", connHandle, entryCopy.config)
} }
return entryCopy.config, found return entryCopy.config, found
} }

View File

@@ -31,12 +31,12 @@ func (rc *SQLiteRows) ColumnTypeLength(index int) (length int64, ok bool) {
func (rc *SQLiteRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { func (rc *SQLiteRows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) {
return 0, 0, false return 0, 0, false
} }
*/
// ColumnTypeNullable implement RowsColumnTypeNullable. // ColumnTypeNullable implement RowsColumnTypeNullable.
func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) { func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) {
return true, true return false, false
} }
*/
// ColumnTypeScanType implement RowsColumnTypeScanType. // ColumnTypeScanType implement RowsColumnTypeScanType.
func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type { func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type {

View File

@@ -15,7 +15,9 @@ package sqlite3
// This code should improve performance on windows because // This code should improve performance on windows because
// without the presence of usleep SQLite waits 1 second. // without the presence of usleep SQLite waits 1 second.
// //
// Source: https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa // Source: https://github.com/php/php-src/blob/PHP-5.0/win32/time.c
// License: https://github.com/php/php-src/blob/PHP-5.0/LICENSE
// Details: https://stackoverflow.com/questions/5801813/c-usleep-is-obsolete-workarounds-for-windows-mingw?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
/* /*
#include <windows.h> #include <windows.h>

View File

@@ -325,6 +325,19 @@ struct sqlite3_api_routines {
int (*value_frombind)(sqlite3_value*); int (*value_frombind)(sqlite3_value*);
/* Version 3.30.0 and later */ /* Version 3.30.0 and later */
int (*drop_modules)(sqlite3*,const char**); int (*drop_modules)(sqlite3*,const char**);
/* Version 3.31.0 and later */
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
const char *(*uri_key)(const char*,int);
const char *(*filename_database)(const char*);
const char *(*filename_journal)(const char*);
const char *(*filename_wal)(const char*);
/* Version 3.32.0 and later */
char *(*create_filename)(const char*,const char*,const char*,
int,const char**);
void (*free_filename)(char*);
sqlite3_file *(*database_file_object)(const char*);
/* Version 3.34.0 and later */
int (*txn_state)(sqlite3*,const char*);
}; };
/* /*
@@ -615,10 +628,22 @@ typedef int (*sqlite3_loadext_entry)(
/* Version 3.26.0 and later */ /* Version 3.26.0 and later */
#define sqlite3_normalized_sql sqlite3_api->normalized_sql #define sqlite3_normalized_sql sqlite3_api->normalized_sql
/* Version 3.28.0 and later */ /* Version 3.28.0 and later */
#define sqlite3_stmt_isexplain sqlite3_api->isexplain #define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
#define sqlite3_value_frombind sqlite3_api->frombind #define sqlite3_value_frombind sqlite3_api->value_frombind
/* Version 3.30.0 and later */ /* Version 3.30.0 and later */
#define sqlite3_drop_modules sqlite3_api->drop_modules #define sqlite3_drop_modules sqlite3_api->drop_modules
/* Version 3.31.0 and later */
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
#define sqlite3_uri_key sqlite3_api->uri_key
#define sqlite3_filename_database sqlite3_api->filename_database
#define sqlite3_filename_journal sqlite3_api->filename_journal
#define sqlite3_filename_wal sqlite3_api->filename_wal
/* Version 3.32.0 and later */
#define sqlite3_create_filename sqlite3_api->create_filename
#define sqlite3_free_filename sqlite3_api->free_filename
#define sqlite3_database_file_object sqlite3_api->database_file_object
/* Version 3.34.0 and later */
#define sqlite3_txn_state sqlite3_api->txn_state
#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)

View File

@@ -13,14 +13,25 @@ import (
"errors" "errors"
) )
func init() {
sql.Register("sqlite3", &SQLiteDriverMock{})
}
type SQLiteDriverMock struct{}
var errorMsg = errors.New("Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub") var errorMsg = errors.New("Binary was compiled with 'CGO_ENABLED=0', go-sqlite3 requires cgo to work. This is a stub")
func (SQLiteDriverMock) Open(s string) (driver.Conn, error) { func init() {
return nil, errorMsg sql.Register("sqlite3", &SQLiteDriver{})
} }
type (
SQLiteDriver struct {
Extensions []string
ConnectHook func(*SQLiteConn) error
}
SQLiteConn struct{}
)
func (SQLiteDriver) Open(s string) (driver.Conn, error) { return nil, errorMsg }
func (c *SQLiteConn) RegisterAggregator(string, interface{}, bool) error { return errorMsg }
func (c *SQLiteConn) RegisterAuthorizer(func(int, string, string, string) int) {}
func (c *SQLiteConn) RegisterCollation(string, func(string, string) int) error { return errorMsg }
func (c *SQLiteConn) RegisterCommitHook(func() int) {}
func (c *SQLiteConn) RegisterFunc(string, interface{}, bool) error { return errorMsg }
func (c *SQLiteConn) RegisterRollbackHook(func()) {}
func (c *SQLiteConn) RegisterUpdateHook(func(int, string, string, int64)) {}

2
vendor/modules.txt vendored
View File

@@ -214,7 +214,7 @@ github.com/markbates/safe
# github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 # github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007
github.com/matryer/moq github.com/matryer/moq
github.com/matryer/moq/pkg/moq github.com/matryer/moq/pkg/moq
# github.com/mattn/go-sqlite3 v1.13.0 # github.com/mattn/go-sqlite3 v1.14.6
github.com/mattn/go-sqlite3 github.com/mattn/go-sqlite3
# github.com/mitchellh/go-homedir v1.1.0 # github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/go-homedir github.com/mitchellh/go-homedir