Upgrade the SQLCipher to the latest version.

This commit is contained in:
xeodou 2020-05-04 18:33:26 +08:00
commit 40fb3713eb
57 changed files with 15243 additions and 8995 deletions

8
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,8 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: mattn # Replace with a single Patreon username
open_collective: mattn # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
custom: # Replace with a single custom sponsorship URL

View File

@ -11,6 +11,8 @@ go:
- 1.9.x - 1.9.x
- 1.10.x - 1.10.x
- 1.11.x - 1.11.x
- 1.12.x
- 1.13.x
- master - master
before_install: before_install:
@ -25,6 +27,5 @@ before_install:
script: script:
- $HOME/gopath/bin/goveralls -repotoken PfqH9iFyzW3daUxflzllSougjTxvFwQZE - $HOME/gopath/bin/goveralls -repotoken PfqH9iFyzW3daUxflzllSougjTxvFwQZE
- go test -race -v . -tags "" - 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_vacuum_incr sqlite_vtable sqlite_unlock_notify"
- go test -race -v . -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json 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" - go test -race -v . -tags "sqlite_vacuum_full"

109
README.md
View File

@ -4,12 +4,20 @@ go-sqlcipher
SQLCipher driver conforming to the built-in database/sql interface and using the latest sqlite3 code. SQLCipher driver conforming to the built-in database/sql interface and using the latest sqlite3 code.
[![GoDoc Reference](https://godoc.org/github.com/xeodou/go-sqlcipher?status.svg)](http://godoc.org/github.com/xeodou/go-sqlcipher)
[![Build Status](https://travis-ci.org/xeodou/go-sqlcipher.svg?branch=master)](https://travis-ci.org/xeodou/go-sqlcipher)
[![Coverage Status](https://coveralls.io/repos/xeodou/go-sqlcipher/badge.svg?branch=master)](https://coveralls.io/r/xeodou/go-sqlcipher?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/xeodou/go-sqlcipher)](https://goreportcard.com/report/github.com/xeodou/go-sqlcipher)
NOTE: v2.0.1 or higher is unfortunatal release. So there are no big changes. And does not provide v2 feature.
# Description
which is which is
`3.26.0` `3.31.0`
Working with sqlcipher version which is Working with sqlcipher version which is
`4.0.1` `4.3.0`
It's wrapper with It's wrapper with
* [go-sqlite3](https://github.com/mattn/go-sqlite3) sqlite3 driver for go that using database/sql. * [go-sqlite3](https://github.com/mattn/go-sqlite3) sqlite3 driver for go that using database/sql.
@ -34,10 +42,16 @@ To upgrade SQLCipher from 3.x to 4.x, please take a look of:
### Overview ### Overview
- [go-sqlite3](#go-sqlite3)
- [Description](#description)
- [Overview](#overview)
- [Installation](#installation) - [Installation](#installation)
- [API Reference](#api-reference) - [API Reference](#api-reference)
- [Connection String](#connection-string) - [Connection String](#connection-string)
- [DSN Examples](#dsn-examples)
- [Features](#features) - [Features](#features)
- [Usage](#usage)
- [Feature / Extension List](#feature--extension-list)
- [Compilation](#compilation) - [Compilation](#compilation)
- [Android](#android) - [Android](#android)
- [ARM](#arm) - [ARM](#arm)
@ -52,11 +66,22 @@ To upgrade SQLCipher from 3.x to 4.x, please take a look of:
- [Errors](#errors) - [Errors](#errors)
- [User Authentication](#user-authentication) - [User Authentication](#user-authentication)
- [Compile](#compile) - [Compile](#compile)
- [Usage](#usage) - [Usage](#usage-1)
- [Create protected database](#create-protected-database)
- [Password Encoding](#password-encoding)
- [Available Encoders](#available-encoders)
- [Restrictions](#restrictions)
- [Support](#support)
- [User Management](#user-management)
- [SQL](#sql)
- [Examples](#examples)
- [*SQLiteConn](#sqliteconn)
- [Attached database](#attached-database)
- [Extensions](#extensions) - [Extensions](#extensions)
- [Spatialite](#spatialite) - [Spatialite](#spatialite)
- [FAQ](#faq) - [FAQ](#faq)
- [License](#license) - [License](#license)
- [Author](#author)
# Installation # Installation
@ -134,6 +159,9 @@ This package allows additional configuration of features available within SQLite
[Click here for more information about build tags / constraints.](https://golang.org/pkg/go/build/#hdr-Build_Constraints) [Click here for more information about build tags / constraints.](https://golang.org/pkg/go/build/#hdr-Build_Constraints)
**Please notice**
The `userAuthentication` extention is not support the library, since the SQLCipher is already let you create the encrypted database.
### Usage ### Usage
If you wish to build this library with additional extensions / features. If you wish to build this library with additional extensions / features.
@ -145,7 +173,7 @@ go build --tags "<FEATURE>"
If you want to build the project without the `libcrypto`, you could specific the openssl library by using the command. If you want to build the project without the `libcrypto`, you could specific the openssl library by using the command.
```bash ```bash
CGO_ENABLE=0 CGO_LDFLAGS="-L/usr/local/opt/openssl/lib" CGO_CPPFLAGS="-I/usr/local/opt/openssl/include" go build build _example/encrypto/encrypto.go CGO_ENABLE=1 CGO_LDFLAGS="-L/usr/local/opt/openssl/lib" CGO_CPPFLAGS="-I/usr/local/opt/openssl/include" go build _example/encrypto/encrypto.go
``` ```
For available features see the extension list. For available features see the extension list.
@ -172,6 +200,7 @@ go build --tags "icu json1 fts5 secure_delete"
| International Components for Unicode | sqlite_icu | This option causes the International Components for Unicode or "ICU" extension to SQLite to be added to the build | | International Components for Unicode | sqlite_icu | This option causes the International Components for Unicode or "ICU" extension to SQLite to be added to the build |
| Introspect PRAGMAS | sqlite_introspect | This option adds some extra PRAGMA statements. <ul><li>PRAGMA function_list</li><li>PRAGMA module_list</li><li>PRAGMA pragma_list</li></ul> | | Introspect PRAGMAS | sqlite_introspect | This option adds some extra PRAGMA statements. <ul><li>PRAGMA function_list</li><li>PRAGMA module_list</li><li>PRAGMA pragma_list</li></ul> |
| JSON SQL Functions | sqlite_json | When this option is defined in the amalgamation, the JSON SQL functions are added to the build automatically | | JSON SQL Functions | sqlite_json | When this option is defined in the amalgamation, the JSON SQL functions are added to the build automatically |
| Pre Update Hook | sqlite_preupdate_hook | Registers a callback function that is invoked prior to each INSERT, UPDATE, and DELETE operation on a database table. |
| Secure Delete | sqlite_secure_delete | This compile-time option changes the default setting of the secure_delete pragma.<br><br>When this option is not used, secure_delete defaults to off. When this option is present, secure_delete defaults to on.<br><br>The secure_delete setting causes deleted content to be overwritten with zeros. There is a small performance penalty since additional I/O must occur.<br><br>On the other hand, secure_delete can prevent fragments of sensitive information from lingering in unused parts of the database file after it has been deleted. See the documentation on the secure_delete pragma for additional information | | Secure Delete | sqlite_secure_delete | This compile-time option changes the default setting of the secure_delete pragma.<br><br>When this option is not used, secure_delete defaults to off. When this option is present, secure_delete defaults to on.<br><br>The secure_delete setting causes deleted content to be overwritten with zeros. There is a small performance penalty since additional I/O must occur.<br><br>On the other hand, secure_delete can prevent fragments of sensitive information from lingering in unused parts of the database file after it has been deleted. See the documentation on the secure_delete pragma for additional information |
| Secure Delete (FAST) | sqlite_secure_delete_fast | For more information see [PRAGMA secure_delete](https://www.sqlite.org/pragma.html#pragma_secure_delete) | | Secure Delete (FAST) | sqlite_secure_delete_fast | For more information see [PRAGMA secure_delete](https://www.sqlite.org/pragma.html#pragma_secure_delete) |
| Tracing / Debug | sqlite_trace | Activate trace functions | | Tracing / Debug | sqlite_trace | Activate trace functions |
@ -270,7 +299,7 @@ Required dependency
brew install sqlite3 brew install sqlite3
``` ```
For OSX there is an additional package install which is required if you whish to build the `icu` extension. For OSX there is an additional package install which is required if you wish to build the `icu` extension.
This additional package can be installed with `homebrew`. This additional package can be installed with `homebrew`.
@ -460,6 +489,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.
@ -484,15 +523,19 @@ For an example see [shaxbee/go-spatialite](https://github.com/shaxbee/go-spatial
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?
Each connection to :memory: opens a brand new in-memory sql database, so if 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 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 specified `":memory:"`, that connection will see a brand new database. A
workaround is to use "file::memory:?mode=memory&cache=shared". Every workaround is to use `"file::memory:?cache=shared"` (or `"file:foobar?mode=memory&cache=shared"`). Every
connection to this string will point to the same in-memory database. connection to this string will point to the same in-memory database.
Note that if the last database connection in the pool closes, the in-memory database is deleted. Make sure the [max idle connection limit](https://golang.org/pkg/database/sql/#DB.SetMaxIdleConns) is > 0, and the [connection lifetime](https://golang.org/pkg/database/sql/#DB.SetConnMaxLifetime) is infinite.
For more information see For more information see
* [#204](https://github.com/mattn/go-sqlite3/issues/204) * [#204](https://github.com/mattn/go-sqlite3/issues/204)
* [#511](https://github.com/mattn/go-sqlite3/issues/511) * [#511](https://github.com/mattn/go-sqlite3/issues/511)
* https://www.sqlite.org/sharedcache.html#shared_cache_and_in_memory_databases
* https://www.sqlite.org/inmemorydb.html#sharedmemdb
- Reading from database with large amount of goroutines fails on OSX. - Reading from database with large amount of goroutines fails on OSX.
@ -507,12 +550,56 @@ For an example see [shaxbee/go-spatialite](https://github.com/shaxbee/go-spatial
You need to implement the feature or call the sqlite3 cli. You need to implement the feature or call the sqlite3 cli.
* Print some waring messages like `warning: 'RAND_add' is deprecated: first deprecated in OS X 10.7` More information see [#305](https://github.com/mattn/go-sqlite3/issues/305)
- Error: `database is locked`
You can ignore these messages. You can ignore these messages.
License Example:
------- ```go
db, err := sql.Open("sqlite3", "file:locked.sqlite?cache=shared")
```
Second please set the database connections of the SQL package to 1.
```go
db.SetMaxOpenConns(1)
```
More information see [#209](https://github.com/mattn/go-sqlite3/issues/209)
## Contributors
### Code Contributors
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
<a href="https://github.com/mattn/go-sqlite3/graphs/contributors"><img src="https://opencollective.com/mattn-go-sqlite3/contributors.svg?width=890&button=false" /></a>
### Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/mattn-go-sqlite3/contribute)]
#### Individuals
<a href="https://opencollective.com/mattn-go-sqlite3"><img src="https://opencollective.com/mattn-go-sqlite3/individuals.svg?width=890"></a>
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/mattn-go-sqlite3/contribute)]
<a href="https://opencollective.com/mattn-go-sqlite3/organization/0/website"><img src="https://opencollective.com/mattn-go-sqlite3/organization/0/avatar.svg"></a>
<a href="https://opencollective.com/mattn-go-sqlite3/organization/1/website"><img src="https://opencollective.com/mattn-go-sqlite3/organization/1/avatar.svg"></a>
<a href="https://opencollective.com/mattn-go-sqlite3/organization/2/website"><img src="https://opencollective.com/mattn-go-sqlite3/organization/2/avatar.svg"></a>
<a href="https://opencollective.com/mattn-go-sqlite3/organization/3/website"><img src="https://opencollective.com/mattn-go-sqlite3/organization/3/avatar.svg"></a>
<a href="https://opencollective.com/mattn-go-sqlite3/organization/4/website"><img src="https://opencollective.com/mattn-go-sqlite3/organization/4/avatar.svg"></a>
<a href="https://opencollective.com/mattn-go-sqlite3/organization/5/website"><img src="https://opencollective.com/mattn-go-sqlite3/organization/5/avatar.svg"></a>
<a href="https://opencollective.com/mattn-go-sqlite3/organization/6/website"><img src="https://opencollective.com/mattn-go-sqlite3/organization/6/avatar.svg"></a>
<a href="https://opencollective.com/mattn-go-sqlite3/organization/7/website"><img src="https://opencollective.com/mattn-go-sqlite3/organization/7/avatar.svg"></a>
<a href="https://opencollective.com/mattn-go-sqlite3/organization/8/website"><img src="https://opencollective.com/mattn-go-sqlite3/organization/8/avatar.svg"></a>
<a href="https://opencollective.com/mattn-go-sqlite3/organization/9/website"><img src="https://opencollective.com/mattn-go-sqlite3/organization/9/avatar.svg"></a>
# License
MIT: MIT:

View File

@ -27,7 +27,7 @@ func createBulkInsertQuery(n int, start int) (query string, args []interface{})
return return
} }
func bukInsert(db *sql.DB, query string, args []interface{}) (err error) { func bulkInsert(db *sql.DB, query string, args []interface{}) (err error) {
stmt, err := db.Prepare(query) stmt, err := db.Prepare(query)
if err != nil { if err != nil {
return return
@ -76,7 +76,7 @@ func main() {
num := 400 num := 400
query, args := createBulkInsertQuery(num, 0) query, args := createBulkInsertQuery(num, 0)
err = bukInsert(db, query, args) err = bulkInsert(db, query, args)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -88,7 +88,7 @@ func main() {
log.Printf("updated SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber) log.Printf("updated SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber)
query, args = createBulkInsertQuery(num, num) query, args = createBulkInsertQuery(num, num)
err = bukInsert(db, query, args) err = bulkInsert(db, query, args)
if err != nil { if err != nil {
if err != nil { if err != nil {
log.Printf("expect failed since SQLITE_LIMIT_VARIABLE_NUMBER is too small: %v", err) log.Printf("expect failed since SQLITE_LIMIT_VARIABLE_NUMBER is too small: %v", err)
@ -102,7 +102,7 @@ func main() {
log.Printf("updated SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber) log.Printf("updated SQLITE_LIMIT_VARIABLE_NUMBER: %d", limitVariableNumber)
query, args = createBulkInsertQuery(500, num+num) query, args = createBulkInsertQuery(500, num+num)
err = bukInsert(db, query, args) err = bulkInsert(db, query, args)
if err != nil { if err != nil {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -1,22 +1,27 @@
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
EXE=extension.exe EXE=extension.exe
EXT=sqlite3_mod_regexp.dll LIB_EXT=dll
RM=cmd /c del RM=cmd /c del
LDFLAG= LDFLAG=
else else
EXE=extension EXE=extension
EXT=sqlite3_mod_regexp.so ifeq ($(shell uname -s),Darwin)
RM=rm LIB_EXT=dylib
else
LIB_EXT=so
endif
RM=rm -f
LDFLAG=-fPIC LDFLAG=-fPIC
endif endif
LIB=sqlite3_mod_regexp.$(LIB_EXT)
all : $(EXE) $(EXT) all : $(EXE) $(LIB)
$(EXE) : extension.go $(EXE) : extension.go
go build $< go build $<
$(EXT) : sqlite3_mod_regexp.c $(LIB) : sqlite3_mod_regexp.c
gcc $(LDFLAG) -shared -o $@ $< -lsqlite3 -lpcre gcc $(LDFLAG) -shared -o $@ $< -lsqlite3 -lpcre
clean : clean :
@-$(RM) $(EXE) $(EXT) @-$(RM) $(EXE) $(LIB)

View File

@ -13,9 +13,13 @@ static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv
int vec[500]; int vec[500];
int n, rc; int n, rc;
pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL); pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL);
if (!re) {
sqlite3_result_error(context, errstr, 0);
return;
}
rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500); rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500);
if (rc <= 0) { if (rc <= 0) {
sqlite3_result_error(context, errstr, 0); sqlite3_result_int(context, 0);
return; return;
} }
sqlite3_result_int(context, 1); sqlite3_result_int(context, 1);

View File

@ -1,24 +1,29 @@
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
EXE=extension.exe EXE=extension.exe
EXT=sqlite3_mod_vtable.dll LIB_EXT=dll
RM=cmd /c del RM=cmd /c del
LIBCURL=-lcurldll LIBCURL=-lcurldll
LDFLAG= LDFLAG=
else else
EXE=extension EXE=extension
EXT=sqlite3_mod_vtable.so ifeq ($(shell uname -s),Darwin)
RM=rm LIB_EXT=dylib
else
LIB_EXT=so
endif
RM=rm -f
LDFLAG=-fPIC LDFLAG=-fPIC
LIBCURL=-lcurl LIBCURL=-lcurl
endif endif
LIB=sqlite3_mod_vtable.$(LIB_EXT)
all : $(EXE) $(EXT) all : $(EXE) $(LIB)
$(EXE) : extension.go $(EXE) : extension.go
go build $< go build $<
$(EXT) : sqlite3_mod_vtable.cc $(LIB) : sqlite3_mod_vtable.cc
g++ $(LDFLAG) -shared -o $@ $< -lsqlite3 $(LIBCURL) g++ $(LDFLAG) -shared -o $@ $< -lsqlite3 $(LIBCURL)
clean : clean :
@-$(RM) $(EXE) $(EXT) @-$(RM) $(EXE) $(LIB)

View File

@ -1,6 +1,6 @@
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <sqlite3-binding.h> #include <sqlite3.h>
#include <sqlite3ext.h> #include <sqlite3ext.h>
#include <curl/curl.h> #include <curl/curl.h>
#include "picojson.h" #include "picojson.h"

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -25,18 +25,18 @@ type SQLiteBackup struct {
} }
// Backup make backup from src to dest. // Backup make backup from src to dest.
func (c *SQLiteConn) Backup(dest string, conn *SQLiteConn, src string) (*SQLiteBackup, error) { func (destConn *SQLiteConn) Backup(dest string, srcConn *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))
srcptr := C.CString(src) srcptr := C.CString(src)
defer C.free(unsafe.Pointer(srcptr)) defer C.free(unsafe.Pointer(srcptr))
if b := C.sqlite3_backup_init(c.db, destptr, conn.db, srcptr); b != nil { if b := C.sqlite3_backup_init(destConn.db, destptr, srcConn.db, srcptr); b != nil {
bb := &SQLiteBackup{b: b} bb := &SQLiteBackup{b: b}
runtime.SetFinalizer(bb, (*SQLiteBackup).Finish) runtime.SetFinalizer(bb, (*SQLiteBackup).Finish)
return bb, nil return bb, nil
} }
return nil, c.lastError() return nil, destConn.lastError()
} }
// Step to backs up for one step. Calls the underlying `sqlite3_backup_step` // Step to backs up for one step. Calls the underlying `sqlite3_backup_step`

View File

@ -1,6 +1,10 @@
// Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build cgo
package sqlite3 package sqlite3
import ( import (

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -83,8 +83,22 @@ func authorizerTrampoline(handle uintptr, op int, arg1 *C.char, arg2 *C.char, ar
return callback(op, C.GoString(arg1), C.GoString(arg2), C.GoString(arg3)) return callback(op, C.GoString(arg1), C.GoString(arg2), C.GoString(arg3))
} }
// Use handles to avoid passing Go pointers to C. //export preUpdateHookTrampoline
func preUpdateHookTrampoline(handle uintptr, dbHandle uintptr, op int, db *C.char, table *C.char, oldrowid int64, newrowid int64) {
hval := lookupHandleVal(handle)
data := SQLitePreUpdateData{
Conn: hval.db,
Op: op,
DatabaseName: C.GoString(db),
TableName: C.GoString(table),
OldRowID: oldrowid,
NewRowID: newrowid,
}
callback := hval.val.(func(SQLitePreUpdateData))
callback(data)
}
// Use handles to avoid passing Go pointers to C.
type handleVal struct { type handleVal struct {
db *SQLiteConn db *SQLiteConn
val interface{} val interface{}
@ -103,7 +117,7 @@ func newHandle(db *SQLiteConn, v interface{}) uintptr {
return i return i
} }
func lookupHandle(handle uintptr) interface{} { func lookupHandleVal(handle uintptr) handleVal {
handleLock.Lock() handleLock.Lock()
defer handleLock.Unlock() defer handleLock.Unlock()
r, ok := handleVals[handle] r, ok := handleVals[handle]
@ -114,7 +128,11 @@ func lookupHandle(handle uintptr) interface{} {
panic("invalid handle") panic("invalid handle")
} }
} }
return r.val return r
}
func lookupHandle(handle uintptr) interface{} {
return lookupHandleVal(handle).val
} }
func deleteHandles(db *SQLiteConn) { func deleteHandles(db *SQLiteConn) {

View File

@ -1,3 +1,10 @@
// Copyright (C) 2019 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 cgo
package sqlite3 package sqlite3
import ( import (

299
convert.go Normal file
View File

@ -0,0 +1,299 @@
// Extracted from Go database/sql source code
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Type conversions for Scan.
package sqlite3
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"reflect"
"strconv"
"time"
)
var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error
// convertAssign copies to dest the value in src, converting it if possible.
// An error is returned if the copy would result in loss of information.
// dest should be a pointer type.
func convertAssign(dest, src interface{}) error {
// Common cases, without reflect.
switch s := src.(type) {
case string:
switch d := dest.(type) {
case *string:
if d == nil {
return errNilPtr
}
*d = s
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = []byte(s)
return nil
case *sql.RawBytes:
if d == nil {
return errNilPtr
}
*d = append((*d)[:0], s...)
return nil
}
case []byte:
switch d := dest.(type) {
case *string:
if d == nil {
return errNilPtr
}
*d = string(s)
return nil
case *interface{}:
if d == nil {
return errNilPtr
}
*d = cloneBytes(s)
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = cloneBytes(s)
return nil
case *sql.RawBytes:
if d == nil {
return errNilPtr
}
*d = s
return nil
}
case time.Time:
switch d := dest.(type) {
case *time.Time:
*d = s
return nil
case *string:
*d = s.Format(time.RFC3339Nano)
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = []byte(s.Format(time.RFC3339Nano))
return nil
case *sql.RawBytes:
if d == nil {
return errNilPtr
}
*d = s.AppendFormat((*d)[:0], time.RFC3339Nano)
return nil
}
case nil:
switch d := dest.(type) {
case *interface{}:
if d == nil {
return errNilPtr
}
*d = nil
return nil
case *[]byte:
if d == nil {
return errNilPtr
}
*d = nil
return nil
case *sql.RawBytes:
if d == nil {
return errNilPtr
}
*d = nil
return nil
}
}
var sv reflect.Value
switch d := dest.(type) {
case *string:
sv = reflect.ValueOf(src)
switch sv.Kind() {
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
*d = asString(src)
return nil
}
case *[]byte:
sv = reflect.ValueOf(src)
if b, ok := asBytes(nil, sv); ok {
*d = b
return nil
}
case *sql.RawBytes:
sv = reflect.ValueOf(src)
if b, ok := asBytes([]byte(*d)[:0], sv); ok {
*d = sql.RawBytes(b)
return nil
}
case *bool:
bv, err := driver.Bool.ConvertValue(src)
if err == nil {
*d = bv.(bool)
}
return err
case *interface{}:
*d = src
return nil
}
if scanner, ok := dest.(sql.Scanner); ok {
return scanner.Scan(src)
}
dpv := reflect.ValueOf(dest)
if dpv.Kind() != reflect.Ptr {
return errors.New("destination not a pointer")
}
if dpv.IsNil() {
return errNilPtr
}
if !sv.IsValid() {
sv = reflect.ValueOf(src)
}
dv := reflect.Indirect(dpv)
if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) {
switch b := src.(type) {
case []byte:
dv.Set(reflect.ValueOf(cloneBytes(b)))
default:
dv.Set(sv)
}
return nil
}
if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) {
dv.Set(sv.Convert(dv.Type()))
return nil
}
// The following conversions use a string value as an intermediate representation
// to convert between various numeric types.
//
// This also allows scanning into user defined types such as "type Int int64".
// For symmetry, also check for string destination types.
switch dv.Kind() {
case reflect.Ptr:
if src == nil {
dv.Set(reflect.Zero(dv.Type()))
return nil
}
dv.Set(reflect.New(dv.Type().Elem()))
return convertAssign(dv.Interface(), src)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
s := asString(src)
i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())
if err != nil {
err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
}
dv.SetInt(i64)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
s := asString(src)
u64, err := strconv.ParseUint(s, 10, dv.Type().Bits())
if err != nil {
err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
}
dv.SetUint(u64)
return nil
case reflect.Float32, reflect.Float64:
s := asString(src)
f64, err := strconv.ParseFloat(s, dv.Type().Bits())
if err != nil {
err = strconvErr(err)
return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
}
dv.SetFloat(f64)
return nil
case reflect.String:
switch v := src.(type) {
case string:
dv.SetString(v)
return nil
case []byte:
dv.SetString(string(v))
return nil
}
}
return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest)
}
func strconvErr(err error) error {
if ne, ok := err.(*strconv.NumError); ok {
return ne.Err
}
return err
}
func cloneBytes(b []byte) []byte {
if b == nil {
return nil
}
c := make([]byte, len(b))
copy(c, b)
return c
}
func asString(src interface{}) string {
switch v := src.(type) {
case string:
return v
case []byte:
return string(v)
}
rv := reflect.ValueOf(src)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(rv.Int(), 10)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(rv.Uint(), 10)
case reflect.Float64:
return strconv.FormatFloat(rv.Float(), 'g', -1, 64)
case reflect.Float32:
return strconv.FormatFloat(rv.Float(), 'g', -1, 32)
case reflect.Bool:
return strconv.FormatBool(rv.Bool())
}
return fmt.Sprintf("%v", src)
}
func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) {
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.AppendInt(buf, rv.Int(), 10), true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.AppendUint(buf, rv.Uint(), 10), true
case reflect.Float32:
return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true
case reflect.Float64:
return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true
case reflect.Bool:
return strconv.AppendBool(buf, rv.Bool()), true
case reflect.String:
s := rv.String()
return append(buf, s...), true
}
return
}

View File

@ -1,11 +1,19 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
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

View File

@ -1,8 +1,10 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build cgo
package sqlite3 package sqlite3
import ( import (
@ -238,5 +240,36 @@ func TestExtendedErrorCodes_Unique(t *testing.T) {
extended, expected) extended, expected)
} }
} }
}
func TestError_SystemErrno(t *testing.T) {
_, n, _ := Version()
if n < 3012000 {
t.Skip("sqlite3_system_errno requires sqlite3 >= 3.12.0")
}
// open a non-existent database in read-only mode so we get an IO error.
db, err := sql.Open("sqlite3", "file:nonexistent.db?mode=ro")
if err != nil {
t.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err == nil {
t.Fatal("expected error pinging read-only non-existent database, but got nil")
}
serr, ok := err.(Error)
if !ok {
t.Fatalf("expected error to be of type Error, but got %[1]T %[1]v", err)
}
if serr.SystemErrno == 0 {
t.Fatal("expected SystemErrno to be set")
}
if !os.IsNotExist(serr.SystemErrno) {
t.Errorf("expected SystemErrno to be a not exists error, but got %v", serr.SystemErrno)
}
} }

8
go.mod Normal file
View File

@ -0,0 +1,8 @@
module github.com/xeodou/go-sqlcipher
go 1.10
require (
github.com/PuerkitoBio/goquery v1.5.1
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
)

15
go.sum Normal file
View File

@ -0,0 +1,15 @@
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/xeodou/go-sqlcipher v0.0.0-20190224185451-e2b0cbb2d94a h1:Dan/bP5w78V6xPfsFnrBaWO838yN63ldodwqIKam60Q=
github.com/xeodou/go-sqlcipher v0.0.0-20190224185451-e2b0cbb2d94a/go.mod h1:vxgnuyTPQm6z5pRcHDe/jkd/XzIfvBplp1FGWcUvGus=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

File diff suppressed because it is too large Load Diff

View File

@ -123,9 +123,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()]. ** [sqlite_version()] and [sqlite_source_id()].
*/ */
#define SQLITE_VERSION "3.26.0" #define SQLITE_VERSION "3.30.1"
#define SQLITE_VERSION_NUMBER 3026000 #define SQLITE_VERSION_NUMBER 3030001
#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238alt1" #define SQLITE_SOURCE_ID "2019-10-10 20:19:45 18db032d058f1436ce3dea84081f4ee5a0f2259ad97301d43c426bc7f3dfalt1"
/* /*
** CAPI3REF: Run-Time Library Version Numbers ** CAPI3REF: Run-Time Library Version Numbers
@ -189,6 +189,9 @@ SQLITE_API int sqlite3_libversion_number(void);
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
SQLITE_API int sqlite3_compileoption_used(const char *zOptName); SQLITE_API int sqlite3_compileoption_used(const char *zOptName);
SQLITE_API const char *sqlite3_compileoption_get(int N); SQLITE_API const char *sqlite3_compileoption_get(int N);
#else
# define sqlite3_compileoption_used(X) 0
# define sqlite3_compileoption_get(X) ((void*)0)
#endif #endif
/* /*
@ -823,6 +826,15 @@ struct sqlite3_io_methods {
** file space based on this hint in order to help writes to the database ** file space based on this hint in order to help writes to the database
** file run faster. ** file run faster.
** **
** <li>[[SQLITE_FCNTL_SIZE_LIMIT]]
** The [SQLITE_FCNTL_SIZE_LIMIT] opcode is used by in-memory VFS that
** implements [sqlite3_deserialize()] to set an upper bound on the size
** of the in-memory database. The argument is a pointer to a [sqlite3_int64].
** If the integer pointed to is negative, then it is filled in with the
** current limit. Otherwise the limit is set to the larger of the value
** of the integer pointed to and the current database size. The integer
** pointed to is set to the new limit.
**
** <li>[[SQLITE_FCNTL_CHUNK_SIZE]] ** <li>[[SQLITE_FCNTL_CHUNK_SIZE]]
** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
** extends and truncates the database file in chunks of a size specified ** extends and truncates the database file in chunks of a size specified
@ -1131,6 +1143,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
#define SQLITE_FCNTL_LOCK_TIMEOUT 34 #define SQLITE_FCNTL_LOCK_TIMEOUT 34
#define SQLITE_FCNTL_DATA_VERSION 35 #define SQLITE_FCNTL_DATA_VERSION 35
#define SQLITE_FCNTL_SIZE_LIMIT 36
/* deprecated names */ /* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@ -1283,8 +1296,14 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS] ** ^The flags argument to xAccess() may be [SQLITE_ACCESS_EXISTS]
** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to ** to test for the existence of a file, or [SQLITE_ACCESS_READWRITE] to
** test whether a file is readable and writable, or [SQLITE_ACCESS_READ] ** test whether a file is readable and writable, or [SQLITE_ACCESS_READ]
** to test whether a file is at least readable. The file can be a ** to test whether a file is at least readable. The SQLITE_ACCESS_READ
** directory. ** flag is never actually used and is not implemented in the built-in
** VFSes of SQLite. The file is named by the second argument and can be a
** directory. The xAccess method returns [SQLITE_OK] on success or some
** non-zero error code if there is an I/O error or if the name of
** the file given in the second argument is illegal. If SQLITE_OK
** is returned, then non-zero or zero is written into *pResOut to indicate
** whether or not the file is accessible.
** **
** ^SQLite will always allocate at least mxPathname+1 bytes for the ** ^SQLite will always allocate at least mxPathname+1 bytes for the
** output buffer xFullPathname. The exact size of the output buffer ** output buffer xFullPathname. The exact size of the output buffer
@ -1972,6 +1991,17 @@ struct sqlite3_mem_methods {
** negative value for this option restores the default behaviour. ** negative value for this option restores the default behaviour.
** This option is only available if SQLite is compiled with the ** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
**
** [[SQLITE_CONFIG_MEMDB_MAXSIZE]]
** <dt>SQLITE_CONFIG_MEMDB_MAXSIZE
** <dd>The SQLITE_CONFIG_MEMDB_MAXSIZE option accepts a single parameter
** [sqlite3_int64] parameter which is the default maximum size for an in-memory
** database created using [sqlite3_deserialize()]. This default maximum
** size can be adjusted up or down for individual databases using the
** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
** </dl> ** </dl>
*/ */
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@ -2002,6 +2032,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
/* /*
** CAPI3REF: Database Connection Configuration Options ** CAPI3REF: Database Connection Configuration Options
@ -2062,10 +2093,21 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in ** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd> ** which case the trigger setting is not reported back. </dd>
** **
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
** There should be two additional arguments.
** The first argument is an integer which is 0 to disable views,
** positive to enable views or negative to leave the setting unchanged.
** The second parameter is a pointer to an integer into which
** is written 0 or 1 to indicate whether views are disabled or enabled
** following this call. The second parameter may be a NULL pointer, in
** which case the view setting is not reported back. </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt> ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
** <dd> ^This option is used to enable or disable the two-argument ** <dd> ^This option is used to enable or disable the
** version of the [fts3_tokenizer()] function which is part of the ** [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension. ** [FTS3] full-text search engine extension.
** There should be two additional arguments. ** There should be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or ** The first argument is an integer which is 0 to disable fts3_tokenizer() or
@ -2173,10 +2215,50 @@ struct sqlite3_mem_methods {
** features include but are not limited to the following: ** features include but are not limited to the following:
** <ul> ** <ul>
** <li> The [PRAGMA writable_schema=ON] statement. ** <li> The [PRAGMA writable_schema=ON] statement.
** <li> The [PRAGMA journal_mode=OFF] statement.
** <li> Writes to the [sqlite_dbpage] virtual table. ** <li> Writes to the [sqlite_dbpage] virtual table.
** <li> Direct writes to [shadow tables]. ** <li> Direct writes to [shadow tables].
** </ul> ** </ul>
** </dd> ** </dd>
**
** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]] <dt>SQLITE_DBCONFIG_WRITABLE_SCHEMA</dt>
** <dd>The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the
** "writable_schema" flag. This has the same effect and is logically equivalent
** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF].
** The first argument to this setting is an integer which is 0 to disable
** the writable_schema, positive to enable writable_schema, or negative to
** leave the setting unchanged. The second parameter is a pointer to an
** integer into which is written 0 or 1 to indicate whether the writable_schema
** is enabled or disabled following this call.
** </dd>
**
** [[SQLITE_DBCONFIG_LEGACY_ALTER_TABLE]]
** <dt>SQLITE_DBCONFIG_LEGACY_ALTER_TABLE</dt>
** <dd>The SQLITE_DBCONFIG_LEGACY_ALTER_TABLE option activates or deactivates
** the legacy behavior of the [ALTER TABLE RENAME] command such it
** behaves as it did prior to [version 3.24.0] (2018-06-04). See the
** "Compatibility Notice" on the [ALTER TABLE RENAME documentation] for
** additional information. This feature can also be turned on and off
** using the [PRAGMA legacy_alter_table] statement.
** </dd>
**
** [[SQLITE_DBCONFIG_DQS_DML]]
** <dt>SQLITE_DBCONFIG_DQS_DML</td>
** <dd>The SQLITE_DBCONFIG_DQS_DML option activates or deactivates
** the legacy [double-quoted string literal] misfeature for DML statement
** only, that is DELETE, INSERT, SELECT, and UPDATE statements. The
** default value of this setting is determined by the [-DSQLITE_DQS]
** compile-time option.
** </dd>
**
** [[SQLITE_DBCONFIG_DQS_DDL]]
** <dt>SQLITE_DBCONFIG_DQS_DDL</td>
** <dd>The SQLITE_DBCONFIG_DQS option activates or deactivates
** the legacy [double-quoted string literal] misfeature for DDL statements,
** such as CREATE TABLE and CREATE INDEX. The
** default value of this setting is determined by the [-DSQLITE_DQS]
** compile-time option.
** </dd>
** </dl> ** </dl>
*/ */
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
@ -2190,7 +2272,12 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ #define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */ #define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */
#define SQLITE_DBCONFIG_LEGACY_ALTER_TABLE 1012 /* int int* */
#define SQLITE_DBCONFIG_DQS_DML 1013 /* int int* */
#define SQLITE_DBCONFIG_DQS_DDL 1014 /* int int* */
#define SQLITE_DBCONFIG_ENABLE_VIEW 1015 /* int int* */
#define SQLITE_DBCONFIG_MAX 1015 /* Largest DBCONFIG */
/* /*
** CAPI3REF: Enable Or Disable Extended Result Codes ** CAPI3REF: Enable Or Disable Extended Result Codes
@ -2347,7 +2434,7 @@ SQLITE_API int sqlite3_changes(sqlite3*);
** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
** are not counted. ** are not counted.
** **
** This the [sqlite3_total_changes(D)] interface only reports the number ** The [sqlite3_total_changes(D)] interface only reports the number
** of rows that changed due to SQL statement run against database ** of rows that changed due to SQL statement run against database
** connection D. Any changes by other database connections are ignored. ** connection D. Any changes by other database connections are ignored.
** To detect changes against a database file from other database ** To detect changes against a database file from other database
@ -2991,9 +3078,9 @@ SQLITE_API int sqlite3_set_authorizer(
** time is in units of nanoseconds, however the current implementation ** time is in units of nanoseconds, however the current implementation
** is only capable of millisecond resolution so the six least significant ** is only capable of millisecond resolution so the six least significant
** digits in the time are meaningless. Future versions of SQLite ** digits in the time are meaningless. Future versions of SQLite
** might provide greater resolution on the profiler callback. The ** might provide greater resolution on the profiler callback. Invoking
** sqlite3_profile() function is considered experimental and is ** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the
** subject to change in future versions of SQLite. ** profile callback.
*/ */
SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*, SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*,
void(*xTrace)(void*,const char*), void*); void(*xTrace)(void*,const char*), void*);
@ -3407,6 +3494,8 @@ SQLITE_API int sqlite3_open_v2(
** is not a database file pathname pointer that SQLite passed into the xOpen ** is not a database file pathname pointer that SQLite passed into the xOpen
** VFS method, then the behavior of this routine is undefined and probably ** VFS method, then the behavior of this routine is undefined and probably
** undesirable. ** undesirable.
**
** See the [URI filename] documentation for additional information.
*/ */
SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
@ -3629,18 +3718,23 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** deplete the limited store of lookaside memory. Future versions of ** deplete the limited store of lookaside memory. Future versions of
** SQLite may act on this hint differently. ** SQLite may act on this hint differently.
** **
** [[SQLITE_PREPARE_NORMALIZE]] ^(<dt>SQLITE_PREPARE_NORMALIZE</dt> ** [[SQLITE_PREPARE_NORMALIZE]] <dt>SQLITE_PREPARE_NORMALIZE</dt>
** <dd>The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized ** <dd>The SQLITE_PREPARE_NORMALIZE flag is a no-op. This flag used
** representation of the SQL statement should be calculated and then ** to be required for any prepared statement that wanted to use the
** associated with the prepared statement, which can be obtained via ** [sqlite3_normalized_sql()] interface. However, the
** the [sqlite3_normalized_sql()] interface.)^ The semantics used to ** [sqlite3_normalized_sql()] interface is now available to all
** normalize a SQL statement are unspecified and subject to change. ** prepared statements, regardless of whether or not they use this
** At a minimum, literal values will be replaced with suitable ** flag.
** placeholders. **
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
** </dl> ** </dl>
*/ */
#define SQLITE_PREPARE_PERSISTENT 0x01 #define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02 #define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
/* /*
** CAPI3REF: Compiling An SQL Statement ** CAPI3REF: Compiling An SQL Statement
@ -3732,7 +3826,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** ^The specific value of WHERE-clause [parameter] might influence the ** ^The specific value of WHERE-clause [parameter] might influence the
** choice of query plan if the parameter is the left-hand side of a [LIKE] ** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column ** or [GLOB] operator or if the parameter is compared to an indexed column
** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled. ** and the [SQLITE_ENABLE_STAT4] compile-time option is enabled.
** </li> ** </li>
** </ol> ** </ol>
** **
@ -3865,6 +3959,18 @@ SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
*/ */
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement
** METHOD: sqlite3_stmt
**
** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the
** prepared statement S is an EXPLAIN statement, or 2 if the
** statement S is an EXPLAIN QUERY PLAN.
** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is
** an ordinary statement or a NULL pointer.
*/
SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt);
/* /*
** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** CAPI3REF: Determine If A Prepared Statement Has Been Reset
** METHOD: sqlite3_stmt ** METHOD: sqlite3_stmt
@ -4004,7 +4110,9 @@ typedef struct sqlite3_context sqlite3_context;
** ^The fifth argument to the BLOB and string binding interfaces ** ^The fifth argument to the BLOB and string binding interfaces
** is a destructor used to dispose of the BLOB or ** is a destructor used to dispose of the BLOB or
** string after SQLite has finished with it. ^The destructor is called ** string after SQLite has finished with it. ^The destructor is called
** to dispose of the BLOB or string even if the call to bind API fails. ** to dispose of the BLOB or string even if the call to the bind API fails,
** except the destructor is not called if the third parameter is a NULL
** pointer or the fourth parameter is negative.
** ^If the fifth argument is ** ^If the fifth argument is
** the special value [SQLITE_STATIC], then SQLite assumes that the ** the special value [SQLITE_STATIC], then SQLite assumes that the
** information is in static, unmanaged space and does not need to be freed. ** information is in static, unmanaged space and does not need to be freed.
@ -4753,6 +4861,12 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** perform additional optimizations on deterministic functions, so use ** perform additional optimizations on deterministic functions, so use
** of the [SQLITE_DETERMINISTIC] flag is recommended where possible. ** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
** **
** ^The fourth parameter may also optionally include the [SQLITE_DIRECTONLY]
** flag, which if present prevents the function from being invoked from
** within VIEWs or TRIGGERs. For security reasons, the [SQLITE_DIRECTONLY]
** flag is recommended for any application-defined SQL function that has
** side-effects.
**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the ** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^ ** function can gain access to this pointer using [sqlite3_user_data()].)^
** **
@ -4869,8 +4983,30 @@ SQLITE_API int sqlite3_create_window_function(
** [SQLITE_UTF8 | preferred text encoding] as the fourth argument ** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
** to [sqlite3_create_function()], [sqlite3_create_function16()], or ** to [sqlite3_create_function()], [sqlite3_create_function16()], or
** [sqlite3_create_function_v2()]. ** [sqlite3_create_function_v2()].
**
** The SQLITE_DETERMINISTIC flag means that the new function will always
** maps the same inputs into the same output. The abs() function is
** deterministic, for example, but randomblob() is not.
**
** The SQLITE_DIRECTONLY flag means that the function may only be invoked
** from top-level SQL, and cannot be used in VIEWs or TRIGGERs. This is
** a security feature which is recommended for all
** [application-defined SQL functions] that have side-effects. This flag
** prevents an attacker from adding triggers and views to a schema then
** tricking a high-privilege application into causing unintended side-effects
** while performing ordinary queries.
**
** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
** Specifying this flag makes no difference for scalar or aggregate user
** functions. However, if it is not specified for a user-defined window
** function, then any sub-types belonging to arguments passed to the window
** function may be discarded before the window function is called (i.e.
** sqlite3_value_subtype() will always return 0).
*/ */
#define SQLITE_DETERMINISTIC 0x800 #define SQLITE_DETERMINISTIC 0x000000800
#define SQLITE_DIRECTONLY 0x000080000
#define SQLITE_SUBTYPE 0x000100000
/* /*
** CAPI3REF: Deprecated Functions ** CAPI3REF: Deprecated Functions
@ -4921,6 +5057,8 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** <tr><td><b>sqlite3_value_nochange&nbsp;&nbsp;</b> ** <tr><td><b>sqlite3_value_nochange&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<td>True if the column is unchanged in an UPDATE ** <td>&rarr;&nbsp;&nbsp;<td>True if the column is unchanged in an UPDATE
** against a virtual table. ** against a virtual table.
** <tr><td><b>sqlite3_value_frombind&nbsp;&nbsp;</b>
** <td>&rarr;&nbsp;&nbsp;<td>True if value originated from a [bound parameter]
** </table></blockquote> ** </table></blockquote>
** **
** <b>Details:</b> ** <b>Details:</b>
@ -4982,6 +5120,11 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** than within an [xUpdate] method call for an UPDATE statement, then ** than within an [xUpdate] method call for an UPDATE statement, then
** the return value is arbitrary and meaningless. ** the return value is arbitrary and meaningless.
** **
** ^The sqlite3_value_frombind(X) interface returns non-zero if the
** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()]
** interfaces. ^If X comes from an SQL literal value, or a table column,
** and expression, then sqlite3_value_frombind(X) returns zero.
**
** Please pay particular attention to the fact that the pointer returned ** Please pay particular attention to the fact that the pointer returned
** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** from [sqlite3_value_blob()], [sqlite3_value_text()], or
** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_text16()] can be invalidated by a subsequent call to
@ -5027,6 +5170,7 @@ SQLITE_API int sqlite3_value_bytes16(sqlite3_value*);
SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*);
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
SQLITE_API int sqlite3_value_nochange(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*);
SQLITE_API int sqlite3_value_frombind(sqlite3_value*);
/* /*
** CAPI3REF: Finding The Subtype Of SQL Values ** CAPI3REF: Finding The Subtype Of SQL Values
@ -5539,6 +5683,22 @@ SQLITE_API int sqlite3_key_v2(
** The code to implement this API is not available in the public release ** The code to implement this API is not available in the public release
** of SQLite. ** of SQLite.
*/ */
/* BEGIN SQLCIPHER
SQLCipher usage note:
If the current database is plaintext SQLCipher will NOT encrypt it.
If the current database is encrypted and pNew==0 or nNew==0, SQLCipher
will NOT decrypt it.
This routine will ONLY work on an already encrypted database in order
to change the key.
Conversion from plaintext-to-encrypted or encrypted-to-plaintext should
use an ATTACHed database and the sqlcipher_export() convenience function
as per the SQLCipher Documentation.
END SQLCIPHER
*/
SQLITE_API int sqlite3_rekey( SQLITE_API int sqlite3_rekey(
sqlite3 *db, /* Database to be rekeyed */ sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The new key */ const void *pKey, int nKey /* The new key */
@ -5762,7 +5922,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
** associated with database N of connection D. ^The main database file ** associated with database N of connection D. ^The main database file
** has the name "main". If there is no attached database N on the database ** has the name "main". If there is no attached database N on the database
** connection D, or if database N is a temporary or in-memory database, then ** connection D, or if database N is a temporary or in-memory database, then
** a NULL pointer is returned. ** this function will return either a NULL pointer or an empty string.
** **
** ^The filename returned by this function is the output of the ** ^The filename returned by this function is the output of the
** xFullPathname method of the [VFS]. ^In other words, the filename ** xFullPathname method of the [VFS]. ^In other words, the filename
@ -6508,6 +6668,12 @@ struct sqlite3_index_info {
** ^The sqlite3_create_module() ** ^The sqlite3_create_module()
** interface is equivalent to sqlite3_create_module_v2() with a NULL ** interface is equivalent to sqlite3_create_module_v2() with a NULL
** destructor. ** destructor.
**
** ^If the third parameter (the pointer to the sqlite3_module object) is
** NULL then no new module is create and any existing modules with the
** same name are dropped.
**
** See also: [sqlite3_drop_modules()]
*/ */
SQLITE_API int sqlite3_create_module( SQLITE_API int sqlite3_create_module(
sqlite3 *db, /* SQLite connection to register module with */ sqlite3 *db, /* SQLite connection to register module with */
@ -6523,6 +6689,23 @@ SQLITE_API int sqlite3_create_module_v2(
void(*xDestroy)(void*) /* Module destructor function */ void(*xDestroy)(void*) /* Module destructor function */
); );
/*
** CAPI3REF: Remove Unnecessary Virtual Table Implementations
** METHOD: sqlite3
**
** ^The sqlite3_drop_modules(D,L) interface removes all virtual
** table modules from database connection D except those named on list L.
** The L parameter must be either NULL or a pointer to an array of pointers
** to strings where the array is terminated by a single NULL pointer.
** ^If the L parameter is NULL, then all virtual table modules are removed.
**
** See also: [sqlite3_create_module()]
*/
SQLITE_API int sqlite3_drop_modules(
sqlite3 *db, /* Remove modules from this connection */
const char **azKeep /* Except, do not remove the ones named here */
);
/* /*
** CAPI3REF: Virtual Table Instance Object ** CAPI3REF: Virtual Table Instance Object
** KEYWORDS: sqlite3_vtab ** KEYWORDS: sqlite3_vtab
@ -7231,7 +7414,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_FIRST 5 #define SQLITE_TESTCTRL_FIRST 5
#define SQLITE_TESTCTRL_PRNG_SAVE 5 #define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6 #define SQLITE_TESTCTRL_PRNG_RESTORE 6
#define SQLITE_TESTCTRL_PRNG_RESET 7 #define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
#define SQLITE_TESTCTRL_BITVEC_TEST 8 #define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9 #define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@ -7253,7 +7436,10 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_SORTER_MMAP 24 #define SQLITE_TESTCTRL_SORTER_MMAP 24
#define SQLITE_TESTCTRL_IMPOSTER 25 #define SQLITE_TESTCTRL_IMPOSTER 25
#define SQLITE_TESTCTRL_PARSER_COVERAGE 26 #define SQLITE_TESTCTRL_PARSER_COVERAGE 26
#define SQLITE_TESTCTRL_LAST 26 /* Largest TESTCTRL */ #define SQLITE_TESTCTRL_RESULT_INTREAL 27
#define SQLITE_TESTCTRL_PRNG_SEED 28
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS 29
#define SQLITE_TESTCTRL_LAST 29 /* Largest TESTCTRL */
/* /*
** CAPI3REF: SQL Keyword Checking ** CAPI3REF: SQL Keyword Checking
@ -9996,7 +10182,7 @@ SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
** sqlite3changeset_next() is called on the iterator or until the ** sqlite3changeset_next() is called on the iterator or until the
** conflict-handler function returns. If pnCol is not NULL, then *pnCol is ** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
** set to the number of columns in the table affected by the change. If ** set to the number of columns in the table affected by the change. If
** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change ** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change
** is an indirect change, or false (0) otherwise. See the documentation for ** is an indirect change, or false (0) otherwise. See the documentation for
** [sqlite3session_indirect()] for a description of direct and indirect ** [sqlite3session_indirect()] for a description of direct and indirect
** changes. Finally, if pOp is not NULL, then *pOp is set to one of ** changes. Finally, if pOp is not NULL, then *pOp is set to one of
@ -10863,7 +11049,7 @@ SQLITE_API int sqlite3rebaser_configure(
** in size. This function allocates and populates a buffer with a copy ** in size. This function allocates and populates a buffer with a copy
** of the changeset rebased rebased according to the configuration of the ** of the changeset rebased rebased according to the configuration of the
** rebaser object passed as the first argument. If successful, (*ppOut) ** rebaser object passed as the first argument. If successful, (*ppOut)
** is set to point to the new buffer containing the rebased changset and ** is set to point to the new buffer containing the rebased changeset and
** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the ** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the
** responsibility of the caller to eventually free the new buffer using ** responsibility of the caller to eventually free the new buffer using
** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) ** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut)
@ -11230,12 +11416,8 @@ struct Fts5PhraseIter {
** **
** Usually, output parameter *piPhrase is set to the phrase number, *piCol ** Usually, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the ** to the column in which it occurs and *piOff the token offset of the
** first token of the phrase. The exception is if the table was created ** first token of the phrase. Returns SQLITE_OK if successful, or an error
** with the offsets=0 option specified. In this case *piOff is always ** code (i.e. SQLITE_NOMEM) if an error occurs.
** set to -1.
**
** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
** if an error occurs.
** **
** This API can be quite slow if used with an FTS5 table created with the ** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option. ** "detail=none" or "detail=column" option.
@ -11276,7 +11458,7 @@ struct Fts5PhraseIter {
** Save the pointer passed as the second argument as the extension functions ** Save the pointer passed as the second argument as the extension functions
** "auxiliary data". The pointer may then be retrieved by the current or any ** "auxiliary data". The pointer may then be retrieved by the current or any
** future invocation of the same fts5 extension function made as part of ** future invocation of the same fts5 extension function made as part of
** of the same MATCH query using the xGetAuxdata() API. ** the same MATCH query using the xGetAuxdata() API.
** **
** Each extension function is allocated a single auxiliary data slot for ** Each extension function is allocated a single auxiliary data slot for
** each FTS query (MATCH expression). If the extension function is invoked ** each FTS query (MATCH expression). If the extension function is invoked
@ -11291,7 +11473,7 @@ struct Fts5PhraseIter {
** The xDelete callback, if one is specified, is also invoked on the ** The xDelete callback, if one is specified, is also invoked on the
** auxiliary data pointer after the FTS5 query has finished. ** auxiliary data pointer after the FTS5 query has finished.
** **
** If an error (e.g. an OOM condition) occurs within this function, an ** If an error (e.g. an OOM condition) occurs within this function,
** the auxiliary data is set to NULL and an error code returned. If the ** the auxiliary data is set to NULL and an error code returned. If the
** xDelete parameter was not NULL, it is invoked on the auxiliary data ** xDelete parameter was not NULL, it is invoked on the auxiliary data
** pointer before returning. ** pointer before returning.
@ -11524,11 +11706,11 @@ struct Fts5ExtensionApi {
** the tokenizer substitutes "first" for "1st" and the query works ** the tokenizer substitutes "first" for "1st" and the query works
** as expected. ** as expected.
** **
** <li> By adding multiple synonyms for a single term to the FTS index. ** <li> By querying the index for all synonyms of each query term
** In this case, when tokenizing query text, the tokenizer may ** separately. In this case, when tokenizing query text, the
** provide multiple synonyms for a single term within the document. ** tokenizer may provide multiple synonyms for a single term
** FTS5 then queries the index for each synonym individually. For ** within the document. FTS5 then queries the index for each
** example, faced with the query: ** synonym individually. For example, faced with the query:
** **
** <codeblock> ** <codeblock>
** ... MATCH 'first place'</codeblock> ** ... MATCH 'first place'</codeblock>
@ -11552,7 +11734,7 @@ struct Fts5ExtensionApi {
** "place". ** "place".
** **
** This way, even if the tokenizer does not provide synonyms ** This way, even if the tokenizer does not provide synonyms
** when tokenizing query text (it should not - to do would be ** when tokenizing query text (it should not - to do so would be
** inefficient), it doesn't matter if the user queries for ** inefficient), it doesn't matter if the user queries for
** 'first + place' or '1st + place', as there are entries in the ** 'first + place' or '1st + place', as there are entries in the
** FTS index corresponding to both forms of the first token. ** FTS index corresponding to both forms of the first token.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. // Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
// //
// Use of this source code is governed by an MIT-style // Use of this source code is governed by an MIT-style
@ -188,6 +188,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 (
@ -203,6 +209,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
"unsafe" "unsafe"
) )
@ -333,7 +340,7 @@ type SQLiteRows struct {
decltype []string decltype []string
cls bool cls bool
closed bool closed bool
done chan struct{} ctx context.Context // no better alternative to pass context into Next() method
} }
type functionInfo struct { type functionInfo struct {
@ -688,7 +695,7 @@ func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool
ai.stepArgConverters = append(ai.stepArgConverters, conv) ai.stepArgConverters = append(ai.stepArgConverters, conv)
} }
if step.IsVariadic() { if step.IsVariadic() {
conv, err := callbackArg(t.In(start + stepNArgs).Elem()) conv, err := callbackArg(step.In(start + stepNArgs).Elem())
if err != nil { if err != nil {
return err return err
} }
@ -754,15 +761,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,
} }
} }
@ -874,10 +894,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.
@ -902,7 +918,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:
@ -1006,7 +1022,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
deferForeignKeys := -1 deferForeignKeys := -1
foreignKeys := -1 foreignKeys := -1
ignoreCheckConstraints := -1 ignoreCheckConstraints := -1
journalMode := "DELETE" var journalMode string
lockingMode := "NORMAL" lockingMode := "NORMAL"
queryOnly := -1 queryOnly := -1
recursiveTriggers := -1 recursiveTriggers := -1
@ -1243,7 +1259,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
if _, ok := params["_locking"]; ok { if _, ok := params["_locking"]; ok {
pkey = "_locking" pkey = "_locking"
} }
if val := params.Get("_locking"); val != "" { if val := params.Get(pkey); val != "" {
switch strings.ToUpper(val) { switch strings.ToUpper(val) {
case "NORMAL", "EXCLUSIVE": case "NORMAL", "EXCLUSIVE":
lockingMode = strings.ToUpper(val) lockingMode = strings.ToUpper(val)
@ -1353,10 +1369,13 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
mutex|C.SQLITE_OPEN_READWRITE|C.SQLITE_OPEN_CREATE, mutex|C.SQLITE_OPEN_READWRITE|C.SQLITE_OPEN_CREATE,
nil) nil)
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")
@ -1542,10 +1561,10 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
// Before going any further, we need to check that the user // Before going any further, we need to check that the user
// has provided an username and password within the DSN. // has provided an username and password within the DSN.
// We are not allowed to continue. // We are not allowed to continue.
if len(authUser) < 0 { if len(authUser) == 0 {
return nil, fmt.Errorf("Missing '_auth_user' while user authentication was requested with '_auth'") return nil, fmt.Errorf("Missing '_auth_user' while user authentication was requested with '_auth'")
} }
if len(authPass) < 0 { if len(authPass) == 0 {
return nil, fmt.Errorf("Missing '_auth_pass' while user authentication was requested with '_auth'") return nil, fmt.Errorf("Missing '_auth_pass' while user authentication was requested with '_auth'")
} }
@ -1591,11 +1610,12 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
} }
// Journal Mode // Journal Mode
// Because default Journal Mode is DELETE this PRAGMA can always be executed. if journalMode != "" {
if err := exec(fmt.Sprintf("PRAGMA journal_mode = %s;", journalMode)); err != nil { if err := exec(fmt.Sprintf("PRAGMA journal_mode = %s;", journalMode)); err != nil {
C.sqlite3_close_v2(db) C.sqlite3_close_v2(db)
return nil, err return nil, err
} }
}
// Locking Mode // Locking Mode
// Because the default is NORMAL and this is not changed in this package // Because the default is NORMAL and this is not changed in this package
@ -1866,28 +1886,13 @@ func (s *SQLiteStmt) query(ctx context.Context, args []namedValue) (driver.Rows,
decltype: nil, decltype: nil,
cls: s.cls, cls: s.cls,
closed: false, closed: false,
done: make(chan struct{}), ctx: ctx,
}
if ctxdone := ctx.Done(); ctxdone != nil {
go func(db *C.sqlite3) {
select {
case <-ctxdone:
select {
case <-rows.done:
default:
C.sqlite3_interrupt(db)
rows.Close()
}
case <-rows.done:
}
}(s.c.db)
} }
return rows, nil return rows, nil
} }
// LastInsertId teturn last inserted ID. // LastInsertId return last inserted ID.
func (r *SQLiteResult) LastInsertId() (int64, error) { func (r *SQLiteResult) LastInsertId() (int64, error) {
return r.id, nil return r.id, nil
} }
@ -1909,29 +1914,43 @@ func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) {
return s.exec(context.Background(), list) return s.exec(context.Background(), list)
} }
// 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 {
return s.execSync(args)
}
type result struct {
r driver.Result
err error
}
resultCh := make(chan result)
go func() {
r, err := s.execSync(args)
resultCh <- result{r, err}
}()
select {
case rv := <-resultCh:
return rv.r, rv.err
case <-ctx.Done():
select {
case <-resultCh: // no need to interrupt
default:
// this is still racy and can be no-op if executed between sqlite3_* calls in execSync.
C.sqlite3_interrupt(s.c.db)
<-resultCh // ensure goroutine completed
}
return nil, ctx.Err()
}
}
func (s *SQLiteStmt) execSync(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
} }
if ctxdone := ctx.Done(); ctxdone != nil {
done := make(chan struct{})
defer close(done)
go func(db *C.sqlite3) {
select {
case <-done:
case <-ctxdone:
select {
case <-done:
default:
C.sqlite3_interrupt(db)
}
}
}(s.c.db)
}
var rowid, changes C.longlong var rowid, changes C.longlong
rv := C._sqlite3_step_row_internal(s.s, &rowid, &changes) rv := C._sqlite3_step_row_internal(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 {
@ -1952,9 +1971,6 @@ func (rc *SQLiteRows) Close() error {
return nil return nil
} }
rc.closed = true rc.closed = true
if rc.done != nil {
close(rc.done)
}
if rc.cls { if rc.cls {
rc.s.mu.Unlock() rc.s.mu.Unlock()
return rc.s.Close() return rc.s.Close()
@ -1998,13 +2014,39 @@ func (rc *SQLiteRows) DeclTypes() []string {
return rc.declTypes() return rc.declTypes()
} }
// Next move cursor to next. // Next move cursor to next. Attempts to honor context timeout from QueryContext call.
func (rc *SQLiteRows) Next(dest []driver.Value) error { func (rc *SQLiteRows) Next(dest []driver.Value) error {
rc.s.mu.Lock() rc.s.mu.Lock()
defer rc.s.mu.Unlock() defer rc.s.mu.Unlock()
if rc.s.closed { if rc.s.closed {
return io.EOF return io.EOF
} }
if rc.ctx.Done() == nil {
return rc.nextSyncLocked(dest)
}
resultCh := make(chan error)
go func() {
resultCh <- rc.nextSyncLocked(dest)
}()
select {
case err := <-resultCh:
return err
case <-rc.ctx.Done():
select {
case <-resultCh: // no need to interrupt
default:
// this is still racy and can be no-op if executed between sqlite3_* calls in nextSyncLocked.
C.sqlite3_interrupt(rc.s.c.db)
<-resultCh // ensure goroutine completed
}
return rc.ctx.Err()
}
}
// nextSyncLocked moves cursor to next; must be called with locked mutex.
func (rc *SQLiteRows) nextSyncLocked(dest []driver.Value) error {
rv := C._sqlite3_step_internal(rc.s.s) rv := C._sqlite3_step_internal(rc.s.s)
if rv == C.SQLITE_DONE { if rv == C.SQLITE_DONE {
return io.EOF return io.EOF

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -10,7 +10,6 @@ package sqlite3
import ( import (
"database/sql/driver" "database/sql/driver"
"errors"
"context" "context"
) )
@ -18,7 +17,8 @@ import (
// Ping implement Pinger. // Ping implement Pinger.
func (c *SQLiteConn) Ping(ctx context.Context) error { func (c *SQLiteConn) Ping(ctx context.Context) error {
if c.db == nil { if c.db == nil {
return errors.New("Connection was closed") // must be ErrBadConn for sql to close the database
return driver.ErrBadConn
} }
return nil return nil
} }

View File

@ -1,9 +1,9 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build go1.8 // +build go1.8,cgo
package sqlite3 package sqlite3
@ -11,8 +11,10 @@ import (
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
"io/ioutil"
"math/rand" "math/rand"
"os" "os"
"sync"
"testing" "testing"
"time" "time"
) )
@ -134,6 +136,93 @@ func TestShortTimeout(t *testing.T) {
} }
} }
func TestQueryRowContextCancel(t *testing.T) {
srcTempFilename := TempFilename(t)
defer os.Remove(srcTempFilename)
db, err := sql.Open("sqlite3", srcTempFilename)
if err != nil {
t.Fatal(err)
}
defer db.Close()
initDatabase(t, db, 100)
const query = `SELECT key_id FROM test_table ORDER BY key2 ASC`
var keyID string
unexpectedErrors := make(map[string]int)
for i := 0; i < 10000; i++ {
ctx, cancel := context.WithCancel(context.Background())
row := db.QueryRowContext(ctx, query)
cancel()
// it is fine to get "nil" as context cancellation can be handled with delay
if err := row.Scan(&keyID); err != nil && err != context.Canceled {
if err.Error() == "sql: Rows are closed" {
// see https://github.com/golang/go/issues/24431
// fixed in 1.11.1 to properly return context error
continue
}
unexpectedErrors[err.Error()]++
}
}
for errText, count := range unexpectedErrors {
t.Error(errText, count)
}
}
func TestQueryRowContextCancelParallel(t *testing.T) {
srcTempFilename := TempFilename(t)
defer os.Remove(srcTempFilename)
db, err := sql.Open("sqlite3", srcTempFilename)
if err != nil {
t.Fatal(err)
}
db.SetMaxOpenConns(10)
db.SetMaxIdleConns(5)
defer db.Close()
initDatabase(t, db, 100)
const query = `SELECT key_id FROM test_table ORDER BY key2 ASC`
wg := sync.WaitGroup{}
defer wg.Wait()
testCtx, cancel := context.WithCancel(context.Background())
defer cancel()
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
defer wg.Done()
var keyID string
for {
select {
case <-testCtx.Done():
return
default:
}
ctx, cancel := context.WithCancel(context.Background())
row := db.QueryRowContext(ctx, query)
cancel()
_ = row.Scan(&keyID) // see TestQueryRowContextCancel
}
}()
}
var keyID string
for i := 0; i < 10000; i++ {
// note that testCtx is not cancelled during query execution
row := db.QueryRowContext(testCtx, query)
if err := row.Scan(&keyID); err != nil {
t.Fatal(i, err)
}
}
}
func TestExecCancel(t *testing.T) { func TestExecCancel(t *testing.T) {
db, err := sql.Open("sqlite3", ":memory:") db, err := sql.Open("sqlite3", ":memory:")
if err != nil { if err != nil {
@ -154,3 +243,217 @@ func TestExecCancel(t *testing.T) {
} }
} }
} }
func doTestOpenContext(t *testing.T, option string) (string, error) {
tempFilename := TempFilename(t)
url := tempFilename + option
defer func() {
err := os.Remove(tempFilename)
if err != nil {
t.Error("temp file remove error:", err)
}
}()
db, err := sql.Open("sqlite3", url)
if err != nil {
return "Failed to open database:", err
}
defer func() {
err = db.Close()
if err != nil {
t.Error("db close error:", err)
}
}()
ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
err = db.PingContext(ctx)
cancel()
if err != nil {
return "ping error:", err
}
ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
_, err = db.ExecContext(ctx, "drop table foo")
cancel()
ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
_, err = db.ExecContext(ctx, "create table foo (id integer)")
cancel()
if err != nil {
return "Failed to create table:", err
}
if stat, err := os.Stat(tempFilename); err != nil || stat.IsDir() {
return "Failed to create ./foo.db", nil
}
return "", nil
}
func TestOpenContext(t *testing.T) {
cases := map[string]bool{
"": true,
"?_txlock=immediate": true,
"?_txlock=deferred": true,
"?_txlock=exclusive": true,
"?_txlock=bogus": false,
}
for option, expectedPass := range cases {
result, err := doTestOpenContext(t, option)
if result == "" {
if !expectedPass {
errmsg := fmt.Sprintf("_txlock error not caught at dbOpen with option: %s", option)
t.Fatal(errmsg)
}
} else if expectedPass {
if err == nil {
t.Fatal(result)
} else {
t.Fatal(result, err)
}
}
}
}
func TestFileCopyTruncate(t *testing.T) {
var err error
tempFilename := TempFilename(t)
defer func() {
err = os.Remove(tempFilename)
if err != nil {
t.Error("temp file remove error:", err)
}
}()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("open error:", err)
}
defer func() {
err = db.Close()
if err != nil {
t.Error("db close error:", err)
}
}()
ctx, cancel := context.WithTimeout(context.Background(), 55*time.Second)
err = db.PingContext(ctx)
cancel()
if err != nil {
t.Fatal("ping error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
_, err = db.ExecContext(ctx, "drop table foo")
cancel()
ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
_, err = db.ExecContext(ctx, "create table foo (id integer)")
cancel()
if err != nil {
t.Fatal("create table error:", err)
}
// copy db to new file
var data []byte
data, err = ioutil.ReadFile(tempFilename)
if err != nil {
t.Fatal("read file error:", err)
}
var f *os.File
f, err = os.Create(tempFilename + "-db-copy")
if err != nil {
t.Fatal("create file error:", err)
}
defer func() {
err = os.Remove(tempFilename + "-db-copy")
if err != nil {
t.Error("temp file moved remove error:", err)
}
}()
_, err = f.Write(data)
if err != nil {
f.Close()
t.Fatal("write file error:", err)
}
err = f.Close()
if err != nil {
t.Fatal("close file error:", err)
}
// truncate current db file
f, err = os.OpenFile(tempFilename, os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
t.Fatal("open file error:", err)
}
err = f.Close()
if err != nil {
t.Fatal("close file error:", err)
}
// test db after file truncate
ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
err = db.PingContext(ctx)
cancel()
if err != nil {
t.Fatal("ping error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
_, err = db.ExecContext(ctx, "drop table foo")
cancel()
if err == nil {
t.Fatal("drop table no error")
}
ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
_, err = db.ExecContext(ctx, "create table foo (id integer)")
cancel()
if err != nil {
t.Fatal("create table error:", err)
}
err = db.Close()
if err != nil {
t.Error("db close error:", err)
}
// test copied file
db, err = sql.Open("sqlite3", tempFilename+"-db-copy")
if err != nil {
t.Fatal("open error:", err)
}
defer func() {
err = db.Close()
if err != nil {
t.Error("db close error:", err)
}
}()
ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
err = db.PingContext(ctx)
cancel()
if err != nil {
t.Fatal("ping error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
_, err = db.ExecContext(ctx, "drop table foo")
cancel()
if err != nil {
t.Fatal("drop table error:", err)
}
ctx, cancel = context.WithTimeout(context.Background(), 55*time.Second)
_, err = db.ExecContext(ctx, "create table foo (id integer)")
cancel()
if err != nil {
t.Fatal("create table error:", err)
}
}

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -11,6 +11,7 @@ package sqlite3
#cgo CFLAGS: -DUSE_LIBSQLITE3 #cgo CFLAGS: -DUSE_LIBSQLITE3
#cgo linux LDFLAGS: -lsqlite3 #cgo linux LDFLAGS: -lsqlite3
#cgo darwin LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3 #cgo darwin LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3
#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
*/ */

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE 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

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -0,0 +1,63 @@
// Copyright (C) 2019 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 !sqlite_omit_load_extension
package sqlite3
import (
"database/sql"
"testing"
)
func TestExtensionsError(t *testing.T) {
sql.Register("sqlite3_TestExtensionsError",
&SQLiteDriver{
Extensions: []string{
"foobar",
},
},
)
db, err := sql.Open("sqlite3_TestExtensionsError", ":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err == nil {
t.Fatal("expected error loading non-existent extension")
}
if err.Error() == "not an error" {
t.Fatal("expected error from sqlite3_enable_load_extension to be returned")
}
}
func TestLoadExtensionError(t *testing.T) {
sql.Register("sqlite3_TestLoadExtensionError",
&SQLiteDriver{
ConnectHook: func(c *SQLiteConn) error {
return c.LoadExtension("foobar", "")
},
},
)
db, err := sql.Open("sqlite3_TestLoadExtensionError", ":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err == nil {
t.Fatal("expected error loading non-existent extension")
}
if err.Error() == "not an error" {
t.Fatal("expected error from sqlite3_enable_load_extension to be returned")
}
}

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. // Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
// //
// Use of this source code is governed by an MIT-style // Use of this source code is governed by an MIT-style

View File

@ -1,6 +1,6 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. // Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
//
// 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.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. // Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
// //
// Use of this source code is governed by an MIT-style // Use of this source code is governed by an MIT-style

View File

@ -1,8 +1,10 @@
// Copyright (C) 2015 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build cgo
package sqlite3 package sqlite3
import ( import (

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. // Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
// Use of this source code is governed by an MIT-style // Use of this source code is governed by an MIT-style

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

20
sqlite3_opt_preupdate.go Normal file
View File

@ -0,0 +1,20 @@
// Copyright (C) 2019 G.J.R. Timmer <gjr.timmer@gmail.com>.
// Copyright (C) 2018 segment.com <friends@segment.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// +build cgo
package sqlite3
// SQLitePreUpdateData represents all of the data available during a
// pre-update hook call.
type SQLitePreUpdateData struct {
Conn *SQLiteConn
Op int
DatabaseName string
TableName string
OldRowID int64
NewRowID int64
}

View File

@ -0,0 +1,112 @@
// Copyright (C) 2019 G.J.R. Timmer <gjr.timmer@gmail.com>.
// Copyright (C) 2018 segment.com <friends@segment.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// +build sqlite_preupdate_hook
package sqlite3
/*
#cgo CFLAGS: -DSQLITE_ENABLE_PREUPDATE_HOOK
#cgo LDFLAGS: -lm
#ifndef USE_LIBSQLITE3
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
#include <stdlib.h>
#include <string.h>
void preUpdateHookTrampoline(void*, sqlite3 *, int, char *, char *, sqlite3_int64, sqlite3_int64);
*/
import "C"
import (
"errors"
"unsafe"
)
// RegisterPreUpdateHook sets the pre-update hook for a connection.
//
// The callback is passed a SQLitePreUpdateData struct with the data for
// the update, as well as methods for fetching copies of impacted data.
//
// If there is an existing update hook for this connection, it will be
// removed. If callback is nil the existing hook (if any) will be removed
// without creating a new one.
func (c *SQLiteConn) RegisterPreUpdateHook(callback func(SQLitePreUpdateData)) {
if callback == nil {
C.sqlite3_preupdate_hook(c.db, nil, nil)
} else {
C.sqlite3_preupdate_hook(c.db, (*[0]byte)(unsafe.Pointer(C.preUpdateHookTrampoline)), unsafe.Pointer(newHandle(c, callback)))
}
}
// Depth returns the source path of the write, see sqlite3_preupdate_depth()
func (d *SQLitePreUpdateData) Depth() int {
return int(C.sqlite3_preupdate_depth(d.Conn.db))
}
// Count returns the number of columns in the row
func (d *SQLitePreUpdateData) Count() int {
return int(C.sqlite3_preupdate_count(d.Conn.db))
}
func (d *SQLitePreUpdateData) row(dest []interface{}, new bool) error {
for i := 0; i < d.Count() && i < len(dest); i++ {
var val *C.sqlite3_value
var src interface{}
// Initially I tried making this just a function pointer argument, but
// it's absurdly complicated to pass C function pointers.
if new {
C.sqlite3_preupdate_new(d.Conn.db, C.int(i), &val)
} else {
C.sqlite3_preupdate_old(d.Conn.db, C.int(i), &val)
}
switch C.sqlite3_value_type(val) {
case C.SQLITE_INTEGER:
src = int64(C.sqlite3_value_int64(val))
case C.SQLITE_FLOAT:
src = float64(C.sqlite3_value_double(val))
case C.SQLITE_BLOB:
len := C.sqlite3_value_bytes(val)
blobptr := C.sqlite3_value_blob(val)
src = C.GoBytes(blobptr, len)
case C.SQLITE_TEXT:
len := C.sqlite3_value_bytes(val)
cstrptr := unsafe.Pointer(C.sqlite3_value_text(val))
src = C.GoBytes(cstrptr, len)
case C.SQLITE_NULL:
src = nil
}
err := convertAssign(&dest[i], src)
if err != nil {
return err
}
}
return nil
}
// Old populates dest with the row data to be replaced. This works similar to
// database/sql's Rows.Scan()
func (d *SQLitePreUpdateData) Old(dest ...interface{}) error {
if d.Op == SQLITE_INSERT {
return errors.New("There is no old row for INSERT operations")
}
return d.row(dest, false)
}
// New populates dest with the replacement row data. This works similar to
// database/sql's Rows.Scan()
func (d *SQLitePreUpdateData) New(dest ...interface{}) error {
if d.Op == SQLITE_DELETE {
return errors.New("There is no new row for DELETE operations")
}
return d.row(dest, true)
}

View File

@ -0,0 +1,128 @@
// Copyright (C) 2019 G.J.R. Timmer <gjr.timmer@gmail.com>.
// Copyright (C) 2018 segment.com <friends@segment.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// +build sqlite_preupdate_hook
package sqlite3
import (
"database/sql"
"testing"
)
type preUpdateHookDataForTest struct {
databaseName string
tableName string
count int
op int
oldRow []interface{}
newRow []interface{}
}
func TestPreUpdateHook(t *testing.T) {
var events []preUpdateHookDataForTest
sql.Register("sqlite3_PreUpdateHook", &SQLiteDriver{
ConnectHook: func(conn *SQLiteConn) error {
conn.RegisterPreUpdateHook(func(data SQLitePreUpdateData) {
eval := -1
oldRow := []interface{}{eval}
if data.Op != SQLITE_INSERT {
err := data.Old(oldRow...)
if err != nil {
t.Fatalf("Unexpected error calling SQLitePreUpdateData.Old: %v", err)
}
}
eval2 := -1
newRow := []interface{}{eval2}
if data.Op != SQLITE_DELETE {
err := data.New(newRow...)
if err != nil {
t.Fatalf("Unexpected error calling SQLitePreUpdateData.New: %v", err)
}
}
// tests dest bound checks in loop
var tooSmallRow []interface{}
if data.Op != SQLITE_INSERT {
err := data.Old(tooSmallRow...)
if err != nil {
t.Fatalf("Unexpected error calling SQLitePreUpdateData.Old: %v", err)
}
if len(tooSmallRow) != 0 {
t.Errorf("Expected tooSmallRow to be empty, got: %v", tooSmallRow)
}
}
events = append(events, preUpdateHookDataForTest{
databaseName: data.DatabaseName,
tableName: data.TableName,
count: data.Count(),
op: data.Op,
oldRow: oldRow,
newRow: newRow,
})
})
return nil
},
})
db, err := sql.Open("sqlite3_PreUpdateHook", ":memory:")
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer db.Close()
statements := []string{
"create table foo (id integer primary key)",
"insert into foo values (9)",
"update foo set id = 99 where id = 9",
"delete from foo where id = 99",
}
for _, statement := range statements {
_, err = db.Exec(statement)
if err != nil {
t.Fatalf("Unable to prepare test data [%v]: %v", statement, err)
}
}
if len(events) != 3 {
t.Errorf("Events should be 3 entries, got: %d", len(events))
}
if events[0].op != SQLITE_INSERT {
t.Errorf("Op isn't as expected: %v", events[0].op)
}
if events[1].op != SQLITE_UPDATE {
t.Errorf("Op isn't as expected: %v", events[1].op)
}
if events[1].count != 1 {
t.Errorf("Expected event row 1 to have 1 column, had: %v", events[1].count)
}
newRow_0_0 := events[0].newRow[0].(int64)
if newRow_0_0 != 9 {
t.Errorf("Expected event row 0 new column 0 to be == 9, got: %v", newRow_0_0)
}
oldRow_1_0 := events[1].oldRow[0].(int64)
if oldRow_1_0 != 9 {
t.Errorf("Expected event row 1 old column 0 to be == 9, got: %v", oldRow_1_0)
}
newRow_1_0 := events[1].newRow[0].(int64)
if newRow_1_0 != 99 {
t.Errorf("Expected event row 1 new column 0 to be == 99, got: %v", newRow_1_0)
}
oldRow_2_0 := events[2].oldRow[0].(int64)
if oldRow_2_0 != 99 {
t.Errorf("Expected event row 1 new column 0 to be == 99, got: %v", oldRow_2_0)
}
}

View File

@ -0,0 +1,21 @@
// Copyright (C) 2019 G.J.R. Timmer <gjr.timmer@gmail.com>.
// Copyright (C) 2018 segment.com <friends@segment.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
// +build !sqlite_preupdate_hook,cgo
package sqlite3
// RegisterPreUpdateHook sets the pre-update hook for a connection.
//
// The callback is passed a SQLitePreUpdateData struct with the data for
// the update, as well as methods for fetching copies of impacted data.
//
// If there is an existing update hook for this connection, it will be
// removed. If callback is nil the existing hook (if any) will be removed
// without creating a new one.
func (c *SQLiteConn) RegisterPreUpdateHook(callback func(SQLitePreUpdateData)) {
// NOOP
}

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. // Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
// //
// Use of this source code is governed by an MIT-style // Use of this source code is governed by an MIT-style

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. // Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
// //
// Use of this source code is governed by an MIT-style // Use of this source code is governed by an MIT-style

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. // Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
// //
// Use of this source code is governed by an MIT-style // Use of this source code is governed by an MIT-style

View File

@ -1,4 +1,4 @@
// Copyright (C) 2018 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2018 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -60,7 +60,14 @@ func init() {
file = TempFilename(t) file = TempFilename(t)
} }
db, err = sql.Open("sqlite3_with_conn", "file:"+file+fmt.Sprintf("?_auth&_auth_user=%s&_auth_pass=%s", username, password)) params := "?_auth"
if len(username) > 0 {
params = fmt.Sprintf("%s&_auth_user=%s", params, username)
}
if len(password) > 0 {
params = fmt.Sprintf("%s&_auth_pass=%s", params, password)
}
db, err = sql.Open("sqlite3_with_conn", "file:"+file+params)
if err != nil { if err != nil {
defer os.Remove(file) defer os.Remove(file)
return file, nil, nil, err return file, nil, nil, err
@ -164,6 +171,23 @@ func TestUserAuthCreateDatabase(t *testing.T) {
} }
} }
func TestUserAuthCreateDatabaseWithoutArgs(t *testing.T) {
_, db, c, err := connect(t, "", "", "")
if err == nil && c == nil && db == nil {
t.Fatal("Should have failed due to missing _auth_* parameters")
}
_, db, c, err = connect(t, "", "", "admin")
if err == nil && c == nil && db == nil {
t.Fatal("Should have failed due to missing _auth_user parameter")
}
_, db, c, err = connect(t, "", "admin", "")
if err == nil && c == nil && db == nil {
t.Fatal("Should have failed due to missing _auth_pass parameter")
}
}
func TestUserAuthLogin(t *testing.T) { func TestUserAuthLogin(t *testing.T) {
f1, err := create(t, "admin", "admin") f1, err := create(t, "admin", "admin")
if err != nil { if err != nil {

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. // Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
// //
// Use of this source code is governed by an MIT-style // Use of this source code is governed by an MIT-style

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
// Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>. // Copyright (C) 2018 G.J.R. Timmer <gjr.timmer@gmail.com>.
// //
// Use of this source code is governed by an MIT-style // Use of this source code is governed by an MIT-style

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,4 +1,4 @@
// Copyright (C) 2018 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -1,8 +1,10 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// +build cgo
package sqlite3 package sqlite3
import ( import (
@ -34,20 +36,32 @@ func TempFilename(t *testing.T) string {
} }
func doTestOpen(t *testing.T, option string) (string, error) { func doTestOpen(t *testing.T, option string) (string, error) {
var url string
tempFilename := TempFilename(t) tempFilename := TempFilename(t)
defer os.Remove(tempFilename) url := tempFilename + option
if option != "" {
url = tempFilename + option defer func() {
} else { err := os.Remove(tempFilename)
url = tempFilename if err != nil {
t.Error("temp file remove error:", err)
} }
}()
db, err := sql.Open("sqlite3", url) db, err := sql.Open("sqlite3", url)
if err != nil { if err != nil {
return "Failed to open database:", err return "Failed to open database:", err
} }
defer os.Remove(tempFilename)
defer db.Close() defer func() {
err = db.Close()
if err != nil {
t.Error("db close error:", err)
}
}()
err = db.Ping()
if err != nil {
return "ping error:", err
}
_, err = db.Exec("drop table foo") _, err = db.Exec("drop table foo")
_, err = db.Exec("create table foo (id integer)") _, err = db.Exec("create table foo (id integer)")
@ -291,8 +305,8 @@ func TestInsert(t *testing.T) {
func TestUpsert(t *testing.T) { func TestUpsert(t *testing.T) {
_, n, _ := Version() _, n, _ := Version()
if !(n >= 3024000) { if n < 3024000 {
t.Skip("UPSERT requires sqlite3 => 3.24.0") t.Skip("UPSERT requires sqlite3 >= 3.24.0")
} }
tempFilename := TempFilename(t) tempFilename := TempFilename(t)
defer os.Remove(tempFilename) defer os.Remove(tempFilename)

View File

@ -1,4 +1,4 @@
// Copyright (C) 2016 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -89,6 +89,7 @@ func fillExpandedSQL(info *TraceInfo, db *C.sqlite3, pStmt unsafe.Pointer) {
} }
expSQLiteCStr := C.sqlite3_expanded_sql((*C.sqlite3_stmt)(pStmt)) expSQLiteCStr := C.sqlite3_expanded_sql((*C.sqlite3_stmt)(pStmt))
defer C.sqlite3_free(unsafe.Pointer(expSQLiteCStr))
if expSQLiteCStr == nil { if expSQLiteCStr == nil {
fillDBError(&info.DBError, db) fillDBError(&info.DBError, db)
return return
@ -214,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) {
@ -233,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

@ -1,3 +1,8 @@
// Copyright (C) 2019 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 package sqlite3
/* /*

View File

@ -1,4 +1,4 @@
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. // Copyright (C) 2019 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
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.

View File

@ -320,6 +320,11 @@ struct sqlite3_api_routines {
void(*xDestroy)(void*)); void(*xDestroy)(void*));
/* Version 3.26.0 and later */ /* Version 3.26.0 and later */
const char *(*normalized_sql)(sqlite3_stmt*); const char *(*normalized_sql)(sqlite3_stmt*);
/* Version 3.28.0 and later */
int (*stmt_isexplain)(sqlite3_stmt*);
int (*value_frombind)(sqlite3_value*);
/* Version 3.30.0 and later */
int (*drop_modules)(sqlite3*,const char**);
}; };
/* /*
@ -609,6 +614,11 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_create_window_function sqlite3_api->create_window_function #define sqlite3_create_window_function sqlite3_api->create_window_function
/* 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 */
#define sqlite3_stmt_isexplain sqlite3_api->isexplain
#define sqlite3_value_frombind sqlite3_api->frombind
/* Version 3.30.0 and later */
#define sqlite3_drop_modules sqlite3_api->drop_modules
#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

@ -1,3 +1,8 @@
// Copyright (C) 2019 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 !cgo // +build !cgo
package sqlite3 package sqlite3