mirror of https://github.com/mattn/go-sqlite3.git
Merge branch 'master' into see
This commit is contained in:
commit
47ea5383fd
|
@ -20,7 +20,7 @@ This package can be installed with the go get command:
|
||||||
|
|
||||||
_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, after you have built and installed _go-sqlite3_ with `go install github.com/mattn/go-sqlite3` (which requires gcc), you can build your app without relying on gcc in future.
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
|
@ -69,7 +69,7 @@ FAQ
|
||||||
|
|
||||||
* Can I use this in multiple routines concurrently?
|
* 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).
|
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), [#274](https://github.com/mattn/go-sqlite3/issues/274).
|
||||||
|
|
||||||
* Why is it racy if I use a `sql.Open("sqlite3", ":memory:")` database?
|
* Why is it racy if I use a `sql.Open("sqlite3", ":memory:")` database?
|
||||||
|
|
||||||
|
|
|
@ -95,7 +95,7 @@ func main() {
|
||||||
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
|
||||||
err := conn.SetTrace(&sqlite3.TraceConfig{
|
err := conn.SetTrace(&sqlite3.TraceConfig{
|
||||||
Callback: traceCallback,
|
Callback: traceCallback,
|
||||||
EventMask: uint(eventMask),
|
EventMask: eventMask,
|
||||||
WantExpandedSQL: true,
|
WantExpandedSQL: true,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -15,8 +15,8 @@ import (
|
||||||
|
|
||||||
func TestSimpleError(t *testing.T) {
|
func TestSimpleError(t *testing.T) {
|
||||||
e := ErrError.Error()
|
e := ErrError.Error()
|
||||||
if e != "SQL logic error or missing database" {
|
if e != "SQL logic error or missing database" && e != "SQL logic error" {
|
||||||
t.Error("wrong error code:" + e)
|
t.Error("wrong error code: " + e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23460
sqlite3-binding.c
23460
sqlite3-binding.c
File diff suppressed because it is too large
Load Diff
1217
sqlite3-binding.h
1217
sqlite3-binding.h
File diff suppressed because it is too large
Load Diff
65
sqlite3.go
65
sqlite3.go
|
@ -1,3 +1,5 @@
|
||||||
|
// +build cgo
|
||||||
|
|
||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by an MIT-style
|
// Use of this source code is governed by an MIT-style
|
||||||
|
@ -7,7 +9,8 @@ package sqlite3
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo CFLAGS: -std=gnu99
|
#cgo CFLAGS: -std=gnu99
|
||||||
#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE=1
|
#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE=1 -DHAVE_USLEEP=1
|
||||||
|
#cgo linux,!android CFLAGS: -DHAVE_PREAD64=1 -DHAVE_PWRITE64=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: -DSQLITE_DISABLE_INTRINSIC
|
||||||
|
@ -136,6 +139,7 @@ static int _sqlite3_limit(sqlite3* db, int limitId, int newLimit) {
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -149,8 +153,6 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SQLiteTimestampFormats is timestamp formats understood by both this module
|
// SQLiteTimestampFormats is timestamp formats understood by both this module
|
||||||
|
@ -171,6 +173,12 @@ var SQLiteTimestampFormats = []string{
|
||||||
"2006-01-02",
|
"2006-01-02",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
columnDate string = "date"
|
||||||
|
columnDatetime string = "datetime"
|
||||||
|
columnTimestamp string = "timestamp"
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
sql.Register("sqlite3", &SQLiteDriver{})
|
sql.Register("sqlite3", &SQLiteDriver{})
|
||||||
}
|
}
|
||||||
|
@ -390,7 +398,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)(unsafe.Pointer(C.commitHookTrampoline)), unsafe.Pointer(newHandle(c, callback)))
|
C.sqlite3_commit_hook(c.db, (*[0]byte)(C.commitHookTrampoline), unsafe.Pointer(newHandle(c, callback)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,7 +411,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)(unsafe.Pointer(C.rollbackHookTrampoline)), unsafe.Pointer(newHandle(c, callback)))
|
C.sqlite3_rollback_hook(c.db, (*[0]byte)(C.rollbackHookTrampoline), unsafe.Pointer(newHandle(c, callback)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,7 +428,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)(unsafe.Pointer(C.updateHookTrampoline)), unsafe.Pointer(newHandle(c, callback)))
|
C.sqlite3_update_hook(c.db, (*[0]byte)(C.updateHookTrampoline), unsafe.Pointer(newHandle(c, callback)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +510,7 @@ func (c *SQLiteConn) RegisterFunc(name string, impl interface{}, pure bool) erro
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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)))
|
return C._sqlite3_create_function(db, zFunctionName, nArg, eTextRep, C.uintptr_t(pApp), (*[0]byte)(xFunc), (*[0]byte)(xStep), (*[0]byte)(xFinal))
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterAggregator makes a Go type available as a SQLite aggregation function.
|
// RegisterAggregator makes a Go type available as a SQLite aggregation function.
|
||||||
|
@ -783,6 +791,8 @@ func errorString(err Error) string {
|
||||||
// Enable or disable recursive triggers. X can be 1 or 0.
|
// Enable or disable recursive triggers. X can be 1 or 0.
|
||||||
// _crypto_key=XXX
|
// _crypto_key=XXX
|
||||||
// Specify symmetric crypto key for use by SEE. X must be text key without quotes.
|
// Specify symmetric crypto key for use by SEE. X must be text key without quotes.
|
||||||
|
// _mutex=XXX
|
||||||
|
// Specify mutex mode. XXX can be "no", "full".
|
||||||
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")
|
||||||
|
@ -794,6 +804,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
|
||||||
foreignKeys := -1
|
foreignKeys := -1
|
||||||
recursiveTriggers := -1
|
recursiveTriggers := -1
|
||||||
cryptoKey := ""
|
cryptoKey := ""
|
||||||
|
mutex := C.int(C.SQLITE_OPEN_FULLMUTEX)
|
||||||
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:])
|
||||||
|
@ -863,6 +874,18 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
|
||||||
// _crypto_key
|
// _crypto_key
|
||||||
cryptoKey = params.Get("_crypto_key")
|
cryptoKey = params.Get("_crypto_key")
|
||||||
|
|
||||||
|
// _mutex
|
||||||
|
if val := params.Get("_mutex"); val != "" {
|
||||||
|
switch val {
|
||||||
|
case "no":
|
||||||
|
mutex = C.SQLITE_OPEN_NOMUTEX
|
||||||
|
case "full":
|
||||||
|
mutex = C.SQLITE_OPEN_FULLMUTEX
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Invalid _mutex: %v", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(dsn, "file:") {
|
if !strings.HasPrefix(dsn, "file:") {
|
||||||
dsn = dsn[:pos]
|
dsn = dsn[:pos]
|
||||||
}
|
}
|
||||||
|
@ -872,9 +895,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
|
||||||
name := C.CString(dsn)
|
name := C.CString(dsn)
|
||||||
defer C.free(unsafe.Pointer(name))
|
defer C.free(unsafe.Pointer(name))
|
||||||
rv := C._sqlite3_open_v2(name, &db,
|
rv := C._sqlite3_open_v2(name, &db,
|
||||||
C.SQLITE_OPEN_FULLMUTEX|
|
mutex|C.SQLITE_OPEN_READWRITE|C.SQLITE_OPEN_CREATE,
|
||||||
C.SQLITE_OPEN_READWRITE|
|
|
||||||
C.SQLITE_OPEN_CREATE,
|
|
||||||
nil)
|
nil)
|
||||||
if rv != 0 {
|
if rv != 0 {
|
||||||
return nil, Error{Code: ErrNo(rv)}
|
return nil, Error{Code: ErrNo(rv)}
|
||||||
|
@ -1087,7 +1108,7 @@ func (s *SQLiteStmt) bind(args []namedValue) error {
|
||||||
case int64:
|
case int64:
|
||||||
rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v))
|
rv = C.sqlite3_bind_int64(s.s, n, C.sqlite3_int64(v))
|
||||||
case bool:
|
case bool:
|
||||||
if bool(v) {
|
if v {
|
||||||
rv = C.sqlite3_bind_int(s.s, n, 1)
|
rv = C.sqlite3_bind_int(s.s, n, 1)
|
||||||
} else {
|
} else {
|
||||||
rv = C.sqlite3_bind_int(s.s, n, 0)
|
rv = C.sqlite3_bind_int(s.s, n, 0)
|
||||||
|
@ -1138,9 +1159,10 @@ func (s *SQLiteStmt) query(ctx context.Context, args []namedValue) (driver.Rows,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctxdone := ctx.Done(); ctxdone != nil {
|
||||||
go func(db *C.sqlite3) {
|
go func(db *C.sqlite3) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctxdone:
|
||||||
select {
|
select {
|
||||||
case <-rows.done:
|
case <-rows.done:
|
||||||
default:
|
default:
|
||||||
|
@ -1150,6 +1172,7 @@ func (s *SQLiteStmt) query(ctx context.Context, args []namedValue) (driver.Rows,
|
||||||
case <-rows.done:
|
case <-rows.done:
|
||||||
}
|
}
|
||||||
}(s.c.db)
|
}(s.c.db)
|
||||||
|
}
|
||||||
|
|
||||||
return rows, nil
|
return rows, nil
|
||||||
}
|
}
|
||||||
|
@ -1183,15 +1206,21 @@ func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctxdone := ctx.Done(); ctxdone != nil {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
defer close(done)
|
defer close(done)
|
||||||
go func(db *C.sqlite3) {
|
go func(db *C.sqlite3) {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
|
||||||
C.sqlite3_interrupt(db)
|
|
||||||
case <-done:
|
case <-done:
|
||||||
|
case <-ctxdone:
|
||||||
|
select {
|
||||||
|
case <-done:
|
||||||
|
default:
|
||||||
|
C.sqlite3_interrupt(db)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}(s.c.db)
|
}(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)
|
||||||
|
@ -1285,7 +1314,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
|
||||||
case C.SQLITE_INTEGER:
|
case C.SQLITE_INTEGER:
|
||||||
val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i)))
|
val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i)))
|
||||||
switch rc.decltype[i] {
|
switch rc.decltype[i] {
|
||||||
case "timestamp", "datetime", "date":
|
case columnTimestamp, columnDatetime, columnDate:
|
||||||
var t time.Time
|
var t time.Time
|
||||||
// Assume a millisecond unix timestamp if it's 13 digits -- too
|
// Assume a millisecond unix timestamp if it's 13 digits -- too
|
||||||
// large to be a reasonable timestamp in seconds.
|
// large to be a reasonable timestamp in seconds.
|
||||||
|
@ -1316,10 +1345,10 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
|
||||||
n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i)))
|
n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i)))
|
||||||
switch dest[i].(type) {
|
switch dest[i].(type) {
|
||||||
case sql.RawBytes:
|
case sql.RawBytes:
|
||||||
dest[i] = (*[1 << 30]byte)(unsafe.Pointer(p))[0:n]
|
dest[i] = (*[1 << 30]byte)(p)[0:n]
|
||||||
default:
|
default:
|
||||||
slice := make([]byte, n)
|
slice := make([]byte, n)
|
||||||
copy(slice[:], (*[1 << 30]byte)(unsafe.Pointer(p))[0:n])
|
copy(slice[:], (*[1 << 30]byte)(p)[0:n])
|
||||||
dest[i] = slice
|
dest[i] = slice
|
||||||
}
|
}
|
||||||
case C.SQLITE_NULL:
|
case C.SQLITE_NULL:
|
||||||
|
@ -1332,7 +1361,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
|
||||||
s := C.GoStringN((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))), C.int(n))
|
s := C.GoStringN((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))), C.int(n))
|
||||||
|
|
||||||
switch rc.decltype[i] {
|
switch rc.decltype[i] {
|
||||||
case "timestamp", "datetime", "date":
|
case columnTimestamp, columnDatetime, columnDate:
|
||||||
var t time.Time
|
var t time.Time
|
||||||
s = strings.TrimSuffix(s, "Z")
|
s = strings.TrimSuffix(s, "Z")
|
||||||
for _, format := range SQLiteTimestampFormats {
|
for _, format := range SQLiteTimestampFormats {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build cgo
|
||||||
|
|
||||||
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
||||||
//
|
//
|
||||||
// Use of this source code is governed by an MIT-style
|
// Use of this source code is governed by an MIT-style
|
||||||
|
|
|
@ -134,3 +134,24 @@ func TestShortTimeout(t *testing.T) {
|
||||||
t.Fatal(ctx.Err())
|
t.Fatal(ctx.Err())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExecCancel(t *testing.T) {
|
||||||
|
db, err := sql.Open("sqlite3", ":memory:")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
if _, err = db.Exec("create table foo (id integer primary key)"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for n := 0; n < 100; n++ {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
_, err = db.ExecContext(ctx, "insert into foo (id) values (?)", n)
|
||||||
|
cancel()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (C) 2018 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 solaris
|
||||||
|
|
||||||
|
package sqlite3
|
||||||
|
|
||||||
|
/*
|
||||||
|
#cgo CFLAGS: -D__EXTENSIONS__=1
|
||||||
|
*/
|
||||||
|
import "C"
|
|
@ -563,7 +563,7 @@ func TestBoolean(t *testing.T) {
|
||||||
t.Fatalf("Expected 1 row but %v", counter)
|
t.Fatalf("Expected 1 row but %v", counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
if id != 1 && fbool != true {
|
if id != 1 && !fbool {
|
||||||
t.Fatalf("Value for id 1 should be %v, not %v", bool1, fbool)
|
t.Fatalf("Value for id 1 should be %v, not %v", bool1, fbool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,7 +585,7 @@ func TestBoolean(t *testing.T) {
|
||||||
t.Fatalf("Expected 1 row but %v", counter)
|
t.Fatalf("Expected 1 row but %v", counter)
|
||||||
}
|
}
|
||||||
|
|
||||||
if id != 2 && fbool != false {
|
if id != 2 && fbool {
|
||||||
t.Fatalf("Value for id 2 should be %v, not %v", bool2, fbool)
|
t.Fatalf("Value for id 2 should be %v, not %v", bool2, fbool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1591,10 +1591,7 @@ func BenchmarkCustomFunctions(b *testing.B) {
|
||||||
sql.Register("sqlite3_BenchmarkCustomFunctions", &SQLiteDriver{
|
sql.Register("sqlite3_BenchmarkCustomFunctions", &SQLiteDriver{
|
||||||
ConnectHook: func(conn *SQLiteConn) error {
|
ConnectHook: func(conn *SQLiteConn) error {
|
||||||
// Impure function to force sqlite to reexecute it each time.
|
// Impure function to force sqlite to reexecute it each time.
|
||||||
if err := conn.RegisterFunc("custom_add", customAdd, false); err != nil {
|
return conn.RegisterFunc("custom_add", customAdd, false)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1701,7 +1698,7 @@ func (db *TestDB) tearDown() {
|
||||||
// q replaces ? parameters if needed
|
// q replaces ? parameters if needed
|
||||||
func (db *TestDB) q(sql string) string {
|
func (db *TestDB) q(sql string) string {
|
||||||
switch db.dialect {
|
switch db.dialect {
|
||||||
case POSTGRESQL: // repace with $1, $2, ..
|
case POSTGRESQL: // replace with $1, $2, ..
|
||||||
qrx := regexp.MustCompile(`\?`)
|
qrx := regexp.MustCompile(`\?`)
|
||||||
n := 0
|
n := 0
|
||||||
return qrx.ReplaceAllStringFunc(sql, func(string) string {
|
return qrx.ReplaceAllStringFunc(sql, func(string) string {
|
||||||
|
|
|
@ -28,10 +28,10 @@ import (
|
||||||
// Trace... constants identify the possible events causing callback invocation.
|
// Trace... constants identify the possible events causing callback invocation.
|
||||||
// Values are same as the corresponding SQLite Trace Event Codes.
|
// Values are same as the corresponding SQLite Trace Event Codes.
|
||||||
const (
|
const (
|
||||||
TraceStmt = C.SQLITE_TRACE_STMT
|
TraceStmt = uint32(C.SQLITE_TRACE_STMT)
|
||||||
TraceProfile = C.SQLITE_TRACE_PROFILE
|
TraceProfile = uint32(C.SQLITE_TRACE_PROFILE)
|
||||||
TraceRow = C.SQLITE_TRACE_ROW
|
TraceRow = uint32(C.SQLITE_TRACE_ROW)
|
||||||
TraceClose = C.SQLITE_TRACE_CLOSE
|
TraceClose = uint32(C.SQLITE_TRACE_CLOSE)
|
||||||
)
|
)
|
||||||
|
|
||||||
type TraceInfo struct {
|
type TraceInfo struct {
|
||||||
|
@ -71,7 +71,7 @@ type TraceUserCallback func(TraceInfo) int
|
||||||
|
|
||||||
type TraceConfig struct {
|
type TraceConfig struct {
|
||||||
Callback TraceUserCallback
|
Callback TraceUserCallback
|
||||||
EventMask C.uint
|
EventMask uint32
|
||||||
WantExpandedSQL bool
|
WantExpandedSQL bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +105,8 @@ func traceCallbackTrampoline(
|
||||||
// Parameter named 'X' in SQLite docs (eXtra event data?):
|
// Parameter named 'X' in SQLite docs (eXtra event data?):
|
||||||
xValue unsafe.Pointer) C.int {
|
xValue unsafe.Pointer) C.int {
|
||||||
|
|
||||||
|
eventCode := uint32(traceEventCode)
|
||||||
|
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode))
|
panic(fmt.Sprintf("No context (ev 0x%x)", traceEventCode))
|
||||||
}
|
}
|
||||||
|
@ -114,7 +116,7 @@ func traceCallbackTrampoline(
|
||||||
|
|
||||||
var traceConf TraceConfig
|
var traceConf TraceConfig
|
||||||
var found bool
|
var found bool
|
||||||
if traceEventCode == TraceClose {
|
if eventCode == TraceClose {
|
||||||
// clean up traceMap: 'pop' means get and delete
|
// clean up traceMap: 'pop' means get and delete
|
||||||
traceConf, found = popTraceMapping(connHandle)
|
traceConf, found = popTraceMapping(connHandle)
|
||||||
} else {
|
} else {
|
||||||
|
@ -123,16 +125,16 @@ func traceCallbackTrampoline(
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
panic(fmt.Sprintf("Mapping not found for handle 0x%x (ev 0x%x)",
|
panic(fmt.Sprintf("Mapping not found for handle 0x%x (ev 0x%x)",
|
||||||
connHandle, traceEventCode))
|
connHandle, eventCode))
|
||||||
}
|
}
|
||||||
|
|
||||||
var info TraceInfo
|
var info TraceInfo
|
||||||
|
|
||||||
info.EventCode = uint32(traceEventCode)
|
info.EventCode = eventCode
|
||||||
info.AutoCommit = (int(C.sqlite3_get_autocommit(contextDB)) != 0)
|
info.AutoCommit = (int(C.sqlite3_get_autocommit(contextDB)) != 0)
|
||||||
info.ConnHandle = connHandle
|
info.ConnHandle = connHandle
|
||||||
|
|
||||||
switch traceEventCode {
|
switch eventCode {
|
||||||
case TraceStmt:
|
case TraceStmt:
|
||||||
info.StmtHandle = uintptr(p)
|
info.StmtHandle = uintptr(p)
|
||||||
|
|
||||||
|
@ -183,7 +185,7 @@ func traceCallbackTrampoline(
|
||||||
// registering this callback trampoline with SQLite --- for cleanup.
|
// registering this callback trampoline with SQLite --- for cleanup.
|
||||||
// In the future there may be more events forced to "selected" in SQLite
|
// In the future there may be more events forced to "selected" in SQLite
|
||||||
// for the driver's needs.
|
// for the driver's needs.
|
||||||
if traceConf.EventMask&traceEventCode == 0 {
|
if traceConf.EventMask&eventCode == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
37
sqlite3ext.h
37
sqlite3ext.h
|
@ -18,7 +18,7 @@
|
||||||
*/
|
*/
|
||||||
#ifndef SQLITE3EXT_H
|
#ifndef SQLITE3EXT_H
|
||||||
#define SQLITE3EXT_H
|
#define SQLITE3EXT_H
|
||||||
#include "sqlite3.h"
|
#include "sqlite3-binding.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** The following structure holds pointers to all of the SQLite API
|
** The following structure holds pointers to all of the SQLite API
|
||||||
|
@ -135,7 +135,7 @@ struct sqlite3_api_routines {
|
||||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||||
const char*,const char*),void*);
|
const char*,const char*),void*);
|
||||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||||
char * (*snprintf)(int,char*,const char*,...);
|
char * (*xsnprintf)(int,char*,const char*,...);
|
||||||
int (*step)(sqlite3_stmt*);
|
int (*step)(sqlite3_stmt*);
|
||||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||||
char const**,char const**,int*,int*,int*);
|
char const**,char const**,int*,int*,int*);
|
||||||
|
@ -247,7 +247,7 @@ struct sqlite3_api_routines {
|
||||||
int (*uri_boolean)(const char*,const char*,int);
|
int (*uri_boolean)(const char*,const char*,int);
|
||||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||||
const char *(*uri_parameter)(const char*,const char*);
|
const char *(*uri_parameter)(const char*,const char*);
|
||||||
char *(*vsnprintf)(int,char*,const char*,va_list);
|
char *(*xvsnprintf)(int,char*,const char*,va_list);
|
||||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||||
/* Version 3.8.7 and later */
|
/* Version 3.8.7 and later */
|
||||||
int (*auto_extension)(void(*)(void));
|
int (*auto_extension)(void(*)(void));
|
||||||
|
@ -283,6 +283,19 @@ struct sqlite3_api_routines {
|
||||||
/* Version 3.14.0 and later */
|
/* Version 3.14.0 and later */
|
||||||
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
||||||
char *(*expanded_sql)(sqlite3_stmt*);
|
char *(*expanded_sql)(sqlite3_stmt*);
|
||||||
|
/* Version 3.18.0 and later */
|
||||||
|
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
|
||||||
|
/* Version 3.20.0 and later */
|
||||||
|
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
|
||||||
|
sqlite3_stmt**,const char**);
|
||||||
|
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
|
||||||
|
sqlite3_stmt**,const void**);
|
||||||
|
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
|
||||||
|
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
|
||||||
|
void *(*value_pointer)(sqlite3_value*,const char*);
|
||||||
|
int (*vtab_nochange)(sqlite3_context*);
|
||||||
|
int (*value_nochange)(sqlite3_value*);
|
||||||
|
const char *(*vtab_collation)(sqlite3_index_info*,int);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -409,7 +422,7 @@ typedef int (*sqlite3_loadext_entry)(
|
||||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||||
#define sqlite3_snprintf sqlite3_api->snprintf
|
#define sqlite3_snprintf sqlite3_api->xsnprintf
|
||||||
#define sqlite3_step sqlite3_api->step
|
#define sqlite3_step sqlite3_api->step
|
||||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||||
|
@ -433,7 +446,7 @@ typedef int (*sqlite3_loadext_entry)(
|
||||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||||
#define sqlite3_value_type sqlite3_api->value_type
|
#define sqlite3_value_type sqlite3_api->value_type
|
||||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||||
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
|
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
||||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||||
|
@ -509,7 +522,7 @@ typedef int (*sqlite3_loadext_entry)(
|
||||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||||
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
|
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
||||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||||
/* Version 3.8.7 and later */
|
/* Version 3.8.7 and later */
|
||||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
||||||
|
@ -541,6 +554,18 @@ typedef int (*sqlite3_loadext_entry)(
|
||||||
/* Version 3.14.0 and later */
|
/* Version 3.14.0 and later */
|
||||||
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
||||||
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
||||||
|
/* Version 3.18.0 and later */
|
||||||
|
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
|
||||||
|
/* Version 3.20.0 and later */
|
||||||
|
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
|
||||||
|
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
|
||||||
|
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
|
||||||
|
#define sqlite3_result_pointer sqlite3_api->result_pointer
|
||||||
|
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
||||||
|
/* Version 3.22.0 and later */
|
||||||
|
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
||||||
|
#define sqlite3_value_nochange sqlite3_api->value_nochange
|
||||||
|
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
||||||
#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)
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// +build !cgo
|
||||||
|
|
||||||
|
package sqlite3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"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")
|
||||||
|
|
||||||
|
func (SQLiteDriverMock) Open(s string) (driver.Conn, error) {
|
||||||
|
return nil, errorMsg
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
@ -28,7 +29,7 @@ func main() {
|
||||||
var url string
|
var url string
|
||||||
doc.Find("a").Each(func(_ int, s *goquery.Selection) {
|
doc.Find("a").Each(func(_ int, s *goquery.Selection) {
|
||||||
if url == "" && strings.HasPrefix(s.Text(), "sqlite-amalgamation-") {
|
if url == "" && strings.HasPrefix(s.Text(), "sqlite-amalgamation-") {
|
||||||
url = "https://www.sqlite.org/2017/" + s.Text()
|
url = "https://www.sqlite.org/2018/" + s.Text()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if url == "" {
|
if url == "" {
|
||||||
|
@ -80,7 +81,18 @@ func main() {
|
||||||
f.Close()
|
f.Close()
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
_, err = io.Copy(f, zr)
|
scanner := bufio.NewScanner(zr)
|
||||||
|
for scanner.Scan() {
|
||||||
|
text := scanner.Text()
|
||||||
|
if text == `#include "sqlite3.h"` {
|
||||||
|
text = `#include "sqlite3-binding.h"`
|
||||||
|
}
|
||||||
|
_, err = fmt.Fprintln(f, text)
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = scanner.Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
zr.Close()
|
zr.Close()
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
Loading…
Reference in New Issue