Compare commits

...

22 Commits

Author SHA1 Message Date
Charlie Vieth 7ce62b2ade
Replace namedValue with driver.NamedValue to avoid copying exec/query args (#1128) 2023-02-11 17:14:42 -05:00
Philip O'Toole 1603038a4d
Add Serialize and Deserialize support (#1089)
Add support for Serialize and Deserialize, which wrap sqlite3_serialize and sqlite3_deserialize.
2022-11-17 08:03:02 -05:00
Philip O'Toole bce3773726 Update expected test output
Broken in https://github.com/mattn/go-sqlite3/pull/1085
2022-10-26 22:03:24 +09:00
Yasuhiro Matsumoto 31c761827c Update amalgamation code 2022-10-26 22:03:24 +09:00
David Vassallo 4b8633cceb
Updating vtable example, "BestIndex" method (#1099)
As it was, the vtable example will give an error when adding any kind of SQL constraint in the SQL statement. Updating the BestIndex method will ensure that adding SQL constraints will not result in errors

Signed-off-by: David Vassallo <davevassallo@gmail.com>

Signed-off-by: David Vassallo <davevassallo@gmail.com>
2022-10-18 19:04:52 -04:00
David Vassallo 0b3708425e
Update README.md to include vtable feature (#1100)
Added documentation for sqlite_vtable build tag to the README.
2022-10-18 18:31:09 -04:00
Luca Guidi 90900be5db Cross Compiling for Mac OS via `musl-cross`
# Enhancement

Update `README.md` with new instructions for Mac OS cross compiling.

# Why?

The current suggested solution `xgo` is no longer maintained (GitHub archived repository).

# Credits

Credits go to Pieter Claerhout (@pieterclaerhout) and his blog post: https://www.yellowduck.be/posts/cross-compile-a-go-package-which-uses-sqlite3
2022-09-18 22:34:48 +09:00
kkqy be28dec3df Golang's linker add mingwex and mingw32 automatically,so we don't need add them manually. 2022-09-18 22:20:58 +09:00
Levi Gruspe 17f6553f94
Add support for sqlite_math_functions tag (#1059)
Add support for SQLITE_ENABLE_MATH_FUNCTIONS compile-time option via the sqlite_math_functions build tag.

Co-authored-by: Dominik Kraus <dominik.kraus@nktek.de>
2022-09-17 10:45:46 -04:00
KiYugadgeter 7476442ed6 こんにちわ is wrong Japanse. The correct word is こんにちは 2022-09-04 18:00:08 +09:00
RewardedIvan da62659c58
Fix "ennviroment" (#1077)
Fix typo in README
2022-09-01 22:47:55 -04:00
Joshua Hull 4ef63c9c0d
Rollback on constraint failure (#1071)
Always rollback on a commit error
2022-09-01 22:45:11 -04:00
Joshua Hull f92b6bb2a1 Fix TestQueryer test to use exec for multistatement insertion 2022-09-02 00:14:06 +09:00
Joshua Hull d5355d86f9 Fix TestQueryer test 2022-09-02 00:14:06 +09:00
Yoshiki Shibukawa c8a114388a Update README to fix reference URL
R.I.P. godoc.org
2022-09-01 11:56:18 +09:00
Oliver Giles f1eef49b3f TestQueryer: actually check Rows returned
Fixes a test which did not correctly exercise the multi-statement
Queryer functionality
2022-08-16 21:24:19 +09:00
Ben Johnson a2e94c9d58 Add build tag to enable OSTRACE() logging
This commit adds the `sqlite_os_trace` build tag which sets the
`SQLITE_FORCE_OS_TRACE` and `SQLITE_DEBUG_OS_TRACE` compilation
flags. This produces verbose debugging output of every operating
system call made by SQLite.
2022-08-16 21:21:55 +09:00
Yasuhiro Matsumoto d8e192b752 Update amalgamation code 2022-08-16 16:16:48 +09:00
Bryan C. Mills 595e13285d Retract +incompatible releases
(For #965.)

This retraction will take effect when this commit is included in the
latest v1 release (presumably v1.14.11).
2022-08-16 14:22:03 +09:00
Aoang fd616a2f47 Update supported Go version to Go 1.19 2022-08-15 15:12:08 +09:00
Phil Eaton 3ccccfb4c9
Support returning any from callbacks (#1046)
Support returning any from callbacks
2022-05-29 21:06:43 -04:00
Kristóf Havasi b819467576 Add error checking in simple example for tx.Commit
Based on https://golang.org/pkg/database/sql/#Tx.Commit this function returns an error type.
So why not check it.
2022-05-28 23:40:45 +09:00
23 changed files with 6398 additions and 3709 deletions

View File

@ -19,4 +19,4 @@ jobs:
run: |
cd ./_example/simple
docker build -t simple .
docker run simple | grep 99\ こんにち世界099
docker run simple | grep 99\ こんにち世界099

View File

@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
go: ['1.16', '1.17', '1.18']
go: ['1.17', '1.18', '1.19']
fail-fast: false
env:
OS: ${{ matrix.os }}
@ -44,7 +44,7 @@ jobs:
run: go-acc . -- -race -v -tags "libsqlite3"
- name: 'Tags: full'
run: go-acc . -- -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth sqlite_vacuum_incr sqlite_vtable sqlite_unlock_notify sqlite_column_metadata"
run: go-acc . -- -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_os_trace sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable"
- name: 'Tags: vacuum'
run: go-acc . -- -race -v -tags "sqlite_vacuum_full"
@ -64,7 +64,7 @@ jobs:
strategy:
matrix:
go: ['1.16', '1.17', '1.18']
go: ['1.17', '1.18', '1.19']
fail-fast: false
env:
OS: windows-latest
@ -99,7 +99,7 @@ jobs:
- name: 'Tags: full'
run: |
echo 'skip this test'
echo go build -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_userauth sqlite_vacuum_incr sqlite_vtable sqlite_unlock_notify"
echo go build -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable"
shell: msys2 {0}
- name: 'Tags: vacuum'

View File

@ -1,7 +1,7 @@
go-sqlite3
==========
[![GoDoc Reference](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3)
[![Go Reference](https://pkg.go.dev/badge/github.com/mattn/go-sqlite3.svg)](https://pkg.go.dev/github.com/mattn/go-sqlite3)
[![GitHub Actions](https://github.com/mattn/go-sqlite3/workflows/Go/badge.svg)](https://github.com/mattn/go-sqlite3/actions?query=workflow%3AGo)
[![Financial Contributors on Open Collective](https://opencollective.com/mattn-go-sqlite3/all/badge.svg?label=financial+contributors)](https://opencollective.com/mattn-go-sqlite3)
[![codecov](https://codecov.io/gh/mattn/go-sqlite3/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-sqlite3)
@ -165,6 +165,7 @@ go build --tags "icu json1 fts5 secure_delete"
| Allow URI Authority | sqlite_allow_uri_authority | URI filenames normally throws an error if the authority section is not either empty or "localhost".<br><br>However, if SQLite is compiled with the SQLITE_ALLOW_URI_AUTHORITY compile-time option, then the URI is converted into a Uniform Naming Convention (UNC) filename and passed down to the underlying operating system that way |
| App Armor | sqlite_app_armor | When defined, this C-preprocessor macro activates extra code that attempts to detect misuse of the SQLite API, such as passing in NULL pointers to required parameters or using objects after they have been destroyed. <br><br>App Armor is not available under `Windows`. |
| Disable Load Extensions | sqlite_omit_load_extension | Loading of external extensions is enabled by default.<br><br>To disable extension loading add the build tag `sqlite_omit_load_extension`. |
| Enable Serialization with `libsqlite3` | sqlite_serialize | Serialization and deserialization of a SQLite database is available by default, unless the build tag `libsqlite3` is set.<br><br>To enable this functionality even if `libsqlite3` is set, add the build tag `sqlite_serialize`. |
| Foreign Keys | sqlite_foreign_keys | This macro determines whether enforcement of foreign key constraints is enabled or disabled by default for new database connections.<br><br>Each database connection can always turn enforcement of foreign key constraints on and off and run-time using the foreign_keys pragma.<br><br>Enforcement of foreign key constraints is normally off by default, but if this compile-time parameter is set to 1, enforcement of foreign key constraints will be on by default |
| Full Auto Vacuum | sqlite_vacuum_full | Set the default auto vacuum to full |
| Incremental Auto Vacuum | sqlite_vacuum_incr | Set the default auto vacuum to incremental |
@ -172,15 +173,18 @@ 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 |
| 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 |
| Math Functions | sqlite_math_functions | This compile-time option enables built-in scalar math functions. For more information see [Built-In Mathematical SQL Functions](https://www.sqlite.org/lang_mathfunc.html) |
| OS Trace | sqlite_os_trace | This option enables OSTRACE() debug logging. This can be verbose and should not be used in production. |
| 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 (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 |
| User Authentication | sqlite_userauth | SQLite User Authentication see [User Authentication](#user-authentication) for more information. |
| Virtual Tables | sqlite_vtable | SQLite Virtual Tables see [SQLite Official VTABLE Documentation](https://www.sqlite.org/vtab.html) for more information, and a [full example here](https://github.com/mattn/go-sqlite3/tree/master/_example/vtable) |
# Compilation
This package requires the `CGO_ENABLED=1` ennvironment variable if not set by default, and the presence of the `gcc` compiler.
This package requires the `CGO_ENABLED=1` environment variable if not set by default, and the presence of the `gcc` compiler.
If you need to add additional CFLAGS or LDFLAGS to the build command, and do not want to modify this package, then this can be achieved by using the `CGO_CFLAGS` and `CGO_LDFLAGS` environment variables.
@ -216,14 +220,13 @@ This library can be cross-compiled.
In some cases you are required to the `CC` environment variable with the cross compiler.
## Cross Compiling from MAC OSX
The simplest way to cross compile from OSX is to use [xgo](https://github.com/karalabe/xgo).
The simplest way to cross compile from OSX is to use [musl-cross](https://github.com/FiloSottile/homebrew-musl-cross).
Steps:
- Install [xgo](https://github.com/karalabe/xgo) (`go get github.com/karalabe/xgo`).
- Ensure that your project is within your `GOPATH`.
- Run `xgo local/path/to/project`.
- Install [musl-cross](https://github.com/FiloSottile/homebrew-musl-cross) (`brew install FiloSottile/musl-cross/musl-cross`).
- Run `CC=x86_64-linux-musl-gcc CXX=x86_64-linux-musl-g++ GOARCH=amd64 GOOS=linux CGO_ENABLED=1 go build -ldflags "-linkmode external -extldflags -static"`.
Please refer to the project's [README](https://github.com/karalabe/xgo/blob/master/README.md) for further information.
Please refer to the project's [README](https://github.com/FiloSottile/homebrew-musl-cross#readme) for further information.
# Google Cloud Platform

View File

@ -17,7 +17,7 @@ func createBulkInsertQuery(n int, start int) (query string, args []interface{})
for i := 0; i < n; i++ {
values[i] = "(?, ?)"
args[pos] = start + i
args[pos+1] = fmt.Sprintf("こんにち世界%03d", i)
args[pos+1] = fmt.Sprintf("こんにち世界%03d", i)
pos += 2
}
query = fmt.Sprintf(

View File

@ -33,7 +33,7 @@ RUN \
RUN \
# Smoke test
set -o pipefail; \
/go/bin/simple | grep 99\ こんにち世界099
/go/bin/simple | grep 99\ こんにち世界099
# -----------------------------------------------------------------------------
# Main Stage

View File

@ -37,12 +37,15 @@ func main() {
}
defer stmt.Close()
for i := 0; i < 100; i++ {
_, err = stmt.Exec(i, fmt.Sprintf("こんにち世界%03d", i))
_, err = stmt.Exec(i, fmt.Sprintf("こんにち世界%03d", i))
if err != nil {
log.Fatal(err)
}
}
tx.Commit()
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
rows, err := db.Query("select id, name from foo")
if err != nil {

View File

@ -63,7 +63,12 @@ func (v *ghRepoTable) Open() (sqlite3.VTabCursor, error) {
}
func (v *ghRepoTable) BestIndex(cst []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
return &sqlite3.IndexResult{}, nil
used := make([]bool, len(csts))
return &sqlite3.IndexResult{
IdxNum: 0,
IdxStr: "default",
Used: used,
}, nil
}
func (v *ghRepoTable) Disconnect() error { return nil }

View File

@ -353,6 +353,20 @@ func callbackRetNil(ctx *C.sqlite3_context, v reflect.Value) error {
return nil
}
func callbackRetGeneric(ctx *C.sqlite3_context, v reflect.Value) error {
if v.IsNil() {
C.sqlite3_result_null(ctx)
return nil
}
cb, err := callbackRet(v.Elem().Type())
if err != nil {
return err
}
return cb(ctx, v.Elem())
}
func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
switch typ.Kind() {
case reflect.Interface:
@ -360,6 +374,11 @@ func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
if typ.Implements(errorInterface) {
return callbackRetNil, nil
}
if typ.NumMethod() == 0 {
return callbackRetGeneric, nil
}
fallthrough
case reflect.Slice:
if typ.Elem().Kind() != reflect.Uint8 {

View File

@ -102,3 +102,15 @@ func TestCallbackConverters(t *testing.T) {
}
}
}
func TestCallbackReturnAny(t *testing.T) {
udf := func() interface{} {
return 1
}
typ := reflect.TypeOf(udf)
_, err := callbackRet(typ.Out(0))
if err != nil {
t.Errorf("Expected valid callback for any return type, got: %s", err)
}
}

6
go.mod
View File

@ -1,3 +1,7 @@
module github.com/mattn/go-sqlite3
go 1.12
go 1.16
retract (
[v2.0.0+incompatible, v2.0.6+incompatible] // Accidental; no major changes or features.
)

File diff suppressed because it is too large Load Diff

View File

@ -147,9 +147,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.38.5"
#define SQLITE_VERSION_NUMBER 3038005
#define SQLITE_SOURCE_ID "2022-05-06 15:25:27 78d9c993d404cdfaa7fdd2973fa1052e3da9f66215cff9c5540ebe55c407d9fe"
#define SQLITE_VERSION "3.39.4"
#define SQLITE_VERSION_NUMBER 3039004
#define SQLITE_SOURCE_ID "2022-09-29 15:55:41 a29f9949895322123f7c38fbe94c649a9d6e6c9cd0c3b41c96d694552f26b309"
/*
** CAPI3REF: Run-Time Library Version Numbers
@ -5594,7 +5594,8 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
** is a [protected sqlite3_value] object even if the input is not.
** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
** memory allocation fails.
** memory allocation fails. ^If V is a [pointer value], then the result
** of sqlite3_value_dup(V) is a NULL value.
**
** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
@ -6276,6 +6277,28 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*);
*/
SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
/*
** CAPI3REF: Return The Schema Name For A Database Connection
** METHOD: sqlite3
**
** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
** for the N-th database on database connection D, or a NULL pointer of N is
** out of range. An N value of 0 means the main database file. An N of 1 is
** the "temp" schema. Larger values of N correspond to various ATTACH-ed
** databases.
**
** Space to hold the string that is returned by sqlite3_db_name() is managed
** by SQLite itself. The string might be deallocated by any operation that
** changes the schema, including [ATTACH] or [DETACH] or calls to
** [sqlite3_serialize()] or [sqlite3_deserialize()], even operations that
** occur on a different thread. Applications that need to
** remember the string long-term should make their own copy. Applications that
** are accessing the same database connection simultaneously on multiple
** threads should mutex-protect calls to this API and should make their own
** private copy of the result prior to releasing the mutex.
*/
SQLITE_API const char *sqlite3_db_name(sqlite3 *db, int N);
/*
** CAPI3REF: Return The Filename For A Database Connection
** METHOD: sqlite3
@ -9555,8 +9578,8 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
** of a [virtual table] implementation. The result of calling this
** interface from outside of xBestIndex() is undefined and probably harmful.
**
** ^The sqlite3_vtab_distinct() interface returns an integer that is
** either 0, 1, or 2. The integer returned by sqlite3_vtab_distinct()
** ^The sqlite3_vtab_distinct() interface returns an integer between 0 and
** 3. The integer returned by sqlite3_vtab_distinct()
** gives the virtual table additional information about how the query
** planner wants the output to be ordered. As long as the virtual table
** can meet the ordering requirements of the query planner, it may set
@ -9588,6 +9611,13 @@ SQLITE_API SQLITE_EXPERIMENTAL const char *sqlite3_vtab_collation(sqlite3_index_
** that have the same value for all columns identified by "aOrderBy".
** ^However omitting the extra rows is optional.
** This mode is used for a DISTINCT query.
** <li value="3"><p>
** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
** that the query planner needs only distinct rows but it does need the
** rows to be sorted.)^ ^The virtual table implementation is free to omit
** rows that are identical in all aOrderBy columns, if it wants to, but
** it is not required to omit any rows. This mode is used for queries
** that have both DISTINCT and ORDER BY clauses.
** </ol>
**
** ^For the purposes of comparing virtual table output values to see if the

View File

@ -494,10 +494,12 @@ func (ai *aggInfo) Done(ctx *C.sqlite3_context) {
// Commit transaction.
func (tx *SQLiteTx) Commit() error {
_, err := tx.c.exec(context.Background(), "COMMIT", nil)
if err != nil && err.(Error).Code == C.SQLITE_BUSY {
// sqlite3 will leave the transaction open in this scenario.
if err != nil {
// sqlite3 may leave the transaction open in this scenario.
// However, database/sql considers the transaction complete once we
// return from Commit() - we must clean up to honour its semantics.
// We don't know if the ROLLBACK is strictly necessary, but according
// to sqlite's docs, there is no harm in calling ROLLBACK unnecessarily.
tx.c.exec(context.Background(), "ROLLBACK", nil)
}
return err
@ -835,9 +837,9 @@ func lastError(db *C.sqlite3) error {
// Exec implements Execer.
func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, error) {
list := make([]namedValue, len(args))
list := make([]driver.NamedValue, len(args))
for i, v := range args {
list[i] = namedValue{
list[i] = driver.NamedValue{
Ordinal: i + 1,
Value: v,
}
@ -845,7 +847,7 @@ func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, err
return c.exec(context.Background(), query, list)
}
func (c *SQLiteConn) exec(ctx context.Context, query string, args []namedValue) (driver.Result, error) {
func (c *SQLiteConn) exec(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
start := 0
for {
s, err := c.prepare(ctx, query)
@ -854,7 +856,7 @@ func (c *SQLiteConn) exec(ctx context.Context, query string, args []namedValue)
}
var res driver.Result
if s.(*SQLiteStmt).s != nil {
stmtArgs := make([]namedValue, 0, len(args))
stmtArgs := make([]driver.NamedValue, 0, len(args))
na := s.NumInput()
if len(args)-start < na {
s.Close()
@ -892,17 +894,11 @@ func (c *SQLiteConn) exec(ctx context.Context, query string, args []namedValue)
}
}
type namedValue struct {
Name string
Ordinal int
Value driver.Value
}
// Query implements Queryer.
func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, error) {
list := make([]namedValue, len(args))
list := make([]driver.NamedValue, len(args))
for i, v := range args {
list[i] = namedValue{
list[i] = driver.NamedValue{
Ordinal: i + 1,
Value: v,
}
@ -910,10 +906,10 @@ func (c *SQLiteConn) Query(query string, args []driver.Value) (driver.Rows, erro
return c.query(context.Background(), query, list)
}
func (c *SQLiteConn) query(ctx context.Context, query string, args []namedValue) (driver.Rows, error) {
func (c *SQLiteConn) query(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
start := 0
for {
stmtArgs := make([]namedValue, 0, len(args))
stmtArgs := make([]driver.NamedValue, 0, len(args))
s, err := c.prepare(ctx, query)
if err != nil {
return nil, err
@ -1910,7 +1906,7 @@ func (s *SQLiteStmt) NumInput() int {
var placeHolder = []byte{0}
func (s *SQLiteStmt) bind(args []namedValue) error {
func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
rv := C.sqlite3_reset(s.s)
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
return s.c.lastError()
@ -1980,9 +1976,9 @@ func (s *SQLiteStmt) bind(args []namedValue) error {
// Query the statement with arguments. Return records.
func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) {
list := make([]namedValue, len(args))
list := make([]driver.NamedValue, len(args))
for i, v := range args {
list[i] = namedValue{
list[i] = driver.NamedValue{
Ordinal: i + 1,
Value: v,
}
@ -1990,7 +1986,7 @@ func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) {
return s.query(context.Background(), list)
}
func (s *SQLiteStmt) query(ctx context.Context, args []namedValue) (driver.Rows, error) {
func (s *SQLiteStmt) query(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
if err := s.bind(args); err != nil {
return nil, err
}
@ -2020,9 +2016,9 @@ func (r *SQLiteResult) RowsAffected() (int64, error) {
// Exec execute the statement with arguments. Return result object.
func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) {
list := make([]namedValue, len(args))
list := make([]driver.NamedValue, len(args))
for i, v := range args {
list[i] = namedValue{
list[i] = driver.NamedValue{
Ordinal: i + 1,
Value: v,
}
@ -2039,7 +2035,7 @@ func isInterruptErr(err error) bool {
}
// 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 []driver.NamedValue) (driver.Result, error) {
if ctx.Done() == nil {
return s.execSync(args)
}
@ -2071,7 +2067,7 @@ func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result
return rv.r, rv.err
}
func (s *SQLiteStmt) execSync(args []namedValue) (driver.Result, error) {
func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) {
if err := s.bind(args); err != nil {
C.sqlite3_reset(s.s)
C.sqlite3_clear_bindings(s.s)

View File

@ -25,20 +25,12 @@ func (c *SQLiteConn) Ping(ctx context.Context) error {
// QueryContext implement QueryerContext.
func (c *SQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
list := make([]namedValue, len(args))
for i, nv := range args {
list[i] = namedValue(nv)
}
return c.query(ctx, query, list)
return c.query(ctx, query, args)
}
// ExecContext implement ExecerContext.
func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
list := make([]namedValue, len(args))
for i, nv := range args {
list[i] = namedValue(nv)
}
return c.exec(ctx, query, list)
return c.exec(ctx, query, args)
}
// PrepareContext implement ConnPrepareContext.
@ -53,18 +45,10 @@ func (c *SQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver
// QueryContext implement QueryerContext.
func (s *SQLiteStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
list := make([]namedValue, len(args))
for i, nv := range args {
list[i] = namedValue(nv)
}
return s.query(ctx, list)
return s.query(ctx, args)
}
// ExecContext implement ExecerContext.
func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
list := make([]namedValue, len(args))
for i, nv := range args {
list[i] = namedValue(nv)
}
return s.exec(ctx, list)
return s.exec(ctx, args)
}

View File

@ -0,0 +1,14 @@
// Copyright (C) 2022 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_math_functions
package sqlite3
/*
#cgo CFLAGS: -DSQLITE_ENABLE_MATH_FUNCTIONS
#cgo LDFLAGS: -lm
*/
import "C"

View File

@ -0,0 +1,29 @@
// +build sqlite_math_functions
package sqlite3
import (
"database/sql"
"testing"
)
func TestMathFunctions(t *testing.T) {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer db.Close()
queries := []string{
`SELECT acos(1)`,
`SELECT log(10, 100)`,
`SELECT power(2, 2)`,
}
for _, query := range queries {
var result float64
if err := db.QueryRow(query).Scan(&result); err != nil {
t.Errorf("invoking math function query %q: %v", query, err)
}
}
}

15
sqlite3_opt_os_trace.go Normal file
View File

@ -0,0 +1,15 @@
// Copyright (C) 2022 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.
//go:build sqlite_os_trace
// +build sqlite_os_trace
package sqlite3
/*
#cgo CFLAGS: -DSQLITE_FORCE_OS_TRACE=1
#cgo CFLAGS: -DSQLITE_DEBUG_OS_TRACE=1
*/
import "C"

82
sqlite3_opt_serialize.go Normal file
View File

@ -0,0 +1,82 @@
// +build !libsqlite3 sqlite_serialize
package sqlite3
/*
#ifndef USE_LIBSQLITE3
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
#include <stdlib.h>
#include <stdint.h>
*/
import "C"
import (
"fmt"
"math"
"reflect"
"unsafe"
)
// Serialize returns a byte slice that is a serialization of the database.
//
// See https://www.sqlite.org/c3ref/serialize.html
func (c *SQLiteConn) Serialize(schema string) ([]byte, error) {
if schema == "" {
schema = "main"
}
var zSchema *C.char
zSchema = C.CString(schema)
defer C.free(unsafe.Pointer(zSchema))
var sz C.sqlite3_int64
ptr := C.sqlite3_serialize(c.db, zSchema, &sz, 0)
if ptr == nil {
return nil, fmt.Errorf("serialize failed")
}
defer C.sqlite3_free(unsafe.Pointer(ptr))
if sz > C.sqlite3_int64(math.MaxInt) {
return nil, fmt.Errorf("serialized database is too large (%d bytes)", sz)
}
cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(ptr)),
Len: int(sz),
Cap: int(sz),
}))
res := make([]byte, int(sz))
copy(res, cBuf)
return res, nil
}
// Deserialize causes the connection to disconnect from the current database and
// then re-open as an in-memory database based on the contents of the byte slice.
//
// See https://www.sqlite.org/c3ref/deserialize.html
func (c *SQLiteConn) Deserialize(b []byte, schema string) error {
if schema == "" {
schema = "main"
}
var zSchema *C.char
zSchema = C.CString(schema)
defer C.free(unsafe.Pointer(zSchema))
tmpBuf := (*C.uchar)(C.sqlite3_malloc64(C.sqlite3_uint64(len(b))))
cBuf := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(tmpBuf)),
Len: len(b),
Cap: len(b),
}))
copy(cBuf, b)
rc := C.sqlite3_deserialize(c.db, zSchema, tmpBuf, C.sqlite3_int64(len(b)),
C.sqlite3_int64(len(b)), C.SQLITE_DESERIALIZE_FREEONCLOSE)
if rc != C.SQLITE_OK {
return fmt.Errorf("deserialize failed with return %v", rc)
}
return nil
}

View File

@ -0,0 +1,20 @@
// +build libsqlite3,!sqlite_serialize
package sqlite3
import (
"errors"
)
/*
#cgo CFLAGS: -DSQLITE_OMIT_DESERIALIZE
*/
import "C"
func (c *SQLiteConn) Serialize(schema string) ([]byte, error) {
return nil, errors.New("sqlite3: Serialize requires the sqlite_serialize build tag when using the libsqlite3 build tag")
}
func (c *SQLiteConn) Deserialize(b []byte, schema string) error {
return errors.New("sqlite3: Deserialize requires the sqlite_serialize build tag when using the libsqlite3 build tag")
}

View File

@ -0,0 +1,99 @@
// +build !libsqlite3 sqlite_serialize
package sqlite3
import (
"context"
"database/sql"
"os"
"testing"
)
func TestSerializeDeserialize(t *testing.T) {
// Connect to the source database.
srcTempFilename := TempFilename(t)
defer os.Remove(srcTempFilename)
srcDb, err := sql.Open(driverName, srcTempFilename)
if err != nil {
t.Fatal("Failed to open the source database:", err)
}
defer srcDb.Close()
err = srcDb.Ping()
if err != nil {
t.Fatal("Failed to connect to the source database:", err)
}
// Connect to the destination database.
destTempFilename := TempFilename(t)
defer os.Remove(destTempFilename)
destDb, err := sql.Open(driverName, destTempFilename)
if err != nil {
t.Fatal("Failed to open the destination database:", err)
}
defer destDb.Close()
err = destDb.Ping()
if err != nil {
t.Fatal("Failed to connect to the destination database:", err)
}
// Write data to source database.
_, err = srcDb.Exec(`CREATE TABLE foo (name string)`)
if err != nil {
t.Fatal("Failed to create table in source database:", err)
}
_, err = srcDb.Exec(`INSERT INTO foo(name) VALUES("alice")`)
if err != nil {
t.Fatal("Failed to insert data into source database", err)
}
// Serialize the source database
srcConn, err := srcDb.Conn(context.Background())
if err != nil {
t.Fatal("Failed to get connection to source database:", err)
}
defer srcConn.Close()
var serialized []byte
if err := srcConn.Raw(func(raw interface{}) error {
var err error
serialized, err = raw.(*SQLiteConn).Serialize("")
return err
}); err != nil {
t.Fatal("Failed to serialize source database:", err)
}
srcConn.Close()
// Confirm that the destination database is initially empty.
var destTableCount int
err = destDb.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type = 'table'").Scan(&destTableCount)
if err != nil {
t.Fatal("Failed to check the destination table count:", err)
}
if destTableCount != 0 {
t.Fatalf("The destination database is not empty; %v table(s) found.", destTableCount)
}
// Deserialize to destination database
destConn, err := destDb.Conn(context.Background())
if err != nil {
t.Fatal("Failed to get connection to destination database:", err)
}
defer destConn.Close()
if err := destConn.Raw(func(raw interface{}) error {
return raw.(*SQLiteConn).Deserialize(serialized, "")
}); err != nil {
t.Fatal("Failed to deserialize source database:", err)
}
destConn.Close()
// Confirm that destination database has been loaded correctly.
var destRowCount int
err = destDb.QueryRow(`SELECT COUNT(*) FROM foo`).Scan(&destRowCount)
if err != nil {
t.Fatal("Failed to count rows in destination database table", err)
}
if destRowCount != 1 {
t.Fatalf("Destination table does not have the expected records")
}
}

View File

@ -248,6 +248,43 @@ func TestForeignKeys(t *testing.T) {
}
}
func TestDeferredForeignKey(t *testing.T) {
fname := TempFilename(t)
uri := "file:" + fname + "?_foreign_keys=1"
db, err := sql.Open("sqlite3", uri)
if err != nil {
os.Remove(fname)
t.Errorf("sql.Open(\"sqlite3\", %q): %v", uri, err)
}
_, err = db.Exec("CREATE TABLE bar (id INTEGER PRIMARY KEY)")
if err != nil {
t.Errorf("failed creating tables: %v", err)
}
_, err = db.Exec("CREATE TABLE foo (bar_id INTEGER, FOREIGN KEY(bar_id) REFERENCES bar(id) DEFERRABLE INITIALLY DEFERRED)")
if err != nil {
t.Errorf("failed creating tables: %v", err)
}
tx, err := db.Begin()
if err != nil {
t.Errorf("Failed to begin transaction: %v", err)
}
_, err = tx.Exec("INSERT INTO foo (bar_id) VALUES (123)")
if err != nil {
t.Errorf("Failed to insert row: %v", err)
}
err = tx.Commit()
if err == nil {
t.Errorf("Expected an error: %v", err)
}
_, err = db.Begin()
if err != nil {
t.Errorf("Failed to begin transaction: %v", err)
}
db.Close()
os.Remove(fname)
}
func TestRecursiveTriggers(t *testing.T) {
cases := map[string]bool{
"?_recursive_triggers=1": true,
@ -1063,34 +1100,44 @@ func TestQueryer(t *testing.T) {
defer db.Close()
_, err = db.Exec(`
create table foo (id integer);
create table foo (id integer);
`)
if err != nil {
t.Error("Failed to call db.Query:", err)
}
rows, err := db.Query(`
insert into foo(id) values(?);
insert into foo(id) values(?);
insert into foo(id) values(?);
select id from foo order by id;
_, err = db.Exec(`
insert into foo(id) values(?);
insert into foo(id) values(?);
insert into foo(id) values(?);
`, 3, 2, 1)
if err != nil {
t.Error("Failed to call db.Exec:", err)
}
rows, err := db.Query(`
select id from foo order by id;
`)
if err != nil {
t.Error("Failed to call db.Query:", err)
}
defer rows.Close()
n := 1
if rows != nil {
for rows.Next() {
var id int
err = rows.Scan(&id)
if err != nil {
t.Error("Failed to db.Query:", err)
}
if id != n {
t.Error("Failed to db.Query: not matched results")
}
n := 0
for rows.Next() {
var id int
err = rows.Scan(&id)
if err != nil {
t.Error("Failed to db.Query:", err)
}
if id != n + 1 {
t.Error("Failed to db.Query: not matched results")
}
n = n + 1
}
if err := rows.Err(); err != nil {
t.Errorf("Post-scan failed: %v\n", err)
}
if n != 3 {
t.Errorf("Expected 3 rows but retrieved %v", n)
}
}
@ -1449,6 +1496,63 @@ func TestAggregatorRegistration(t *testing.T) {
}
}
type mode struct {
counts map[interface{}]int
top interface{}
topCount int
}
func newMode() *mode {
return &mode{
counts: map[interface{}]int{},
}
}
func (m *mode) Step(x interface{}) {
m.counts[x]++
c := m.counts[x]
if c > m.topCount {
m.top = x
m.topCount = c
}
}
func (m *mode) Done() interface{} {
return m.top
}
func TestAggregatorRegistration_GenericReturn(t *testing.T) {
sql.Register("sqlite3_AggregatorRegistration_GenericReturn", &SQLiteDriver{
ConnectHook: func(conn *SQLiteConn) error {
return conn.RegisterAggregator("mode", newMode, true)
},
})
db, err := sql.Open("sqlite3_AggregatorRegistration_GenericReturn", ":memory:")
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer db.Close()
_, err = db.Exec("create table foo (department integer, profits integer)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
_, err = db.Exec("insert into foo values (1, 10), (1, 20), (1, 45), (2, 42), (2, 115), (2, 20)")
if err != nil {
t.Fatal("Failed to insert records:", err)
}
var mode int
err = db.QueryRow("select mode(profits) from foo").Scan(&mode)
if err != nil {
t.Fatal("MODE query error:", err)
}
if mode != 20 {
t.Fatal("Got incorrect mode. Wanted 20, got: ", mode)
}
}
func rot13(r rune) rune {
switch {
case r >= 'A' && r <= 'Z':

View File

@ -12,7 +12,6 @@ package sqlite3
#cgo CFLAGS: -fno-stack-check
#cgo CFLAGS: -fno-stack-protector
#cgo CFLAGS: -mno-stack-arg-probe
#cgo LDFLAGS: -lmingwex -lmingw32
#cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T
*/
import "C"

View File

@ -356,6 +356,12 @@ struct sqlite3_api_routines {
int (*vtab_in)(sqlite3_index_info*,int,int);
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
/* Version 3.39.0 and later */
int (*deserialize)(sqlite3*,const char*,unsigned char*,
sqlite3_int64,sqlite3_int64,unsigned);
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
unsigned int);
const char *(*db_name)(sqlite3*,int);
};
/*
@ -674,6 +680,12 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_vtab_in sqlite3_api->vtab_in
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
/* Version 3.39.0 and later */
#ifndef SQLITE_OMIT_DESERIALIZE
#define sqlite3_deserialize sqlite3_api->deserialize
#define sqlite3_serialize sqlite3_api->serialize
#endif
#define sqlite3_db_name sqlite3_api->db_name
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)