Compare commits

..

1 Commits

Author SHA1 Message Date
Yasuhiro Matsumoto 02fa52d678
Add build tag ignore for upgrade script 2020-09-11 15:46:33 +09:00
52 changed files with 13011 additions and 26217 deletions

View File

@ -1,29 +0,0 @@
name: CIFuzz
on: [pull_request]
jobs:
Fuzzing:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sanitizer: [address]
steps:
- name: Build Fuzzers (${{ matrix.sanitizer }})
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'go-sqlite3'
dry-run: false
sanitizer: ${{ matrix.sanitizer }}
- name: Run Fuzzers (${{ matrix.sanitizer }})
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'go-sqlite3'
fuzz-seconds: 600
dry-run: false
sanitizer: ${{ matrix.sanitizer }}
- name: Upload Crash
uses: actions/upload-artifact@v1
if: failure()
with:
name: ${{ matrix.sanitizer }}-artifacts
path: ./out/artifacts

View File

@ -1,22 +0,0 @@
name: dockerfile
on:
workflow_dispatch:
push:
tags:
- 'v*'
pull_request:
branches: [ master ]
jobs:
dockerfile:
name: Run Dockerfiles in examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run example - simple
run: |
cd ./_example/simple
docker build -t simple .
docker run simple | grep 99\ こんにちは世界099

View File

@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
go: ['1.17', '1.18', '1.19']
go: ['1.11.13', '1.12.17', '1.13.15', '1.14.7', '1.15']
fail-fast: false
env:
OS: ${{ matrix.os }}
@ -29,11 +29,11 @@ jobs:
- name: Get Build Tools
run: |
GO111MODULE=on go install github.com/ory/go-acc@latest
go get github.com/ory/go-acc
- name: Add $GOPATH/bin to $PATH
run: |
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
echo "::add-path::$(go env GOPATH)/bin"
- uses: actions/checkout@v2
@ -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_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"
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"
- name: 'Tags: vacuum'
run: go-acc . -- -race -v -tags "sqlite_vacuum_full"
@ -64,7 +64,7 @@ jobs:
strategy:
matrix:
go: ['1.17', '1.18', '1.19']
go: ['1.11.13', '1.12.17', '1.13.15', '1.14.7', '1.15']
fail-fast: false
env:
OS: windows-latest
@ -81,33 +81,38 @@ jobs:
with:
go-version: ${{ matrix.go }}
- name: Get Build Tools
run: |
go get -u github.com/ory/go-acc
shell: msys2 {0}
- name: Add $GOPATH/bin to $PATH
run: |
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
echo "::add-path::$(go env GOPATH)/bin"
shell: msys2 {0}
- uses: actions/checkout@v2
- name: 'Tags: default'
run: go build -race -v -tags ""
run: go-acc . -- -race -v -tags ""
shell: msys2 {0}
- name: 'Tags: libsqlite3'
run: go build -race -v -tags "libsqlite3"
run: go-acc . -- -race -v -tags "libsqlite3"
shell: msys2 {0}
- name: 'Tags: full'
run: |
echo 'skip this test'
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"
echo 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"
shell: msys2 {0}
- name: 'Tags: vacuum'
run: go build -race -v -tags "sqlite_vacuum_full"
run: go-acc . -- -race -v -tags "sqlite_vacuum_full"
shell: msys2 {0}
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v1
with:
env_vars: OS,GO
file: coverage.txt

128
README.md
View File

@ -1,23 +1,23 @@
go-sqlite3
==========
[![Go Reference](https://pkg.go.dev/badge/github.com/mattn/go-sqlite3.svg)](https://pkg.go.dev/github.com/mattn/go-sqlite3)
[![GoDoc Reference](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3)
[![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)
[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-sqlite3)](https://goreportcard.com/report/github.com/mattn/go-sqlite3)
Latest stable version is v1.14 or later, not v2.
Latest stable version is v1.14 or later not v2.
~~**NOTE:** The increase to v2 was an accident. There were no major changes or features.~~
# Description
A sqlite3 driver that conforms to the built-in database/sql interface.
sqlite3 driver conforming to the built-in database/sql interface
Supported Golang version: See [.github/workflows/go.yaml](./.github/workflows/go.yaml).
Supported Golang version: See .github/workflows/go.yaml
This package follows the official [Golang Release Policy](https://golang.org/doc/devel/release.html#policy).
[This package follows the official Golang Release Policy.](https://golang.org/doc/devel/release.html#policy)
### Overview
@ -64,7 +64,7 @@ This package follows the official [Golang Release Policy](https://golang.org/doc
# Installation
This package can be installed with the `go get` command:
This package can be installed with the go get command:
go get github.com/mattn/go-sqlite3
@ -72,28 +72,28 @@ _go-sqlite3_ is *cgo* package.
If you want to build your app using go-sqlite3, you need gcc.
However, after you have built and installed _go-sqlite3_ with `go install github.com/mattn/go-sqlite3` (which requires gcc), you can build your app without relying on gcc in future.
***Important: because this is a `CGO` enabled package, you are required to set the environment variable `CGO_ENABLED=1` and have a `gcc` compile present within your path.***
***Important: because this is a `CGO` enabled package you are required to set the environment variable `CGO_ENABLED=1` and have a `gcc` compile present within your path.***
# API Reference
API documentation can be found [here](http://godoc.org/github.com/mattn/go-sqlite3).
API documentation can be found here: http://godoc.org/github.com/mattn/go-sqlite3
Examples can be found under the [examples](./_example) directory.
Examples can be found under the [examples](./_example) directory
# Connection String
When creating a new SQLite database or connection to an existing one, with the file name additional options can be given.
This is also known as a DSN (Data Source Name) string.
This is also known as a DSN string. (Data Source Name).
Options are append after the filename of the SQLite database.
The database filename and options are separated by an `?` (Question Mark).
The database filename and options are seperated by an `?` (Question Mark).
Options should be URL-encoded (see [url.QueryEscape](https://golang.org/pkg/net/url/#QueryEscape)).
This also applies when using an in-memory database instead of a file.
Options can be given using the following format: `KEYWORD=VALUE` and multiple options can be combined with the `&` ampersand.
This library supports DSN options of SQLite itself and provides additional options.
This library supports dsn options of SQLite itself and provides additional options.
Boolean values can be one of:
* `0` `no` `false` `off`
@ -125,8 +125,6 @@ Boolean values can be one of:
| Time Zone Location | `_loc` | auto | Specify location of time format. |
| Transaction Lock | `_txlock` | <ul><li>immediate</li><li>deferred</li><li>exclusive</li></ul> | Specify locking behavior for transactions. |
| Writable Schema | `_writable_schema` | `Boolean` | When this pragma is on, the SQLITE_MASTER tables in which database can be changed using ordinary UPDATE, INSERT, and DELETE statements. Warning: misuse of this pragma can easily result in a corrupt database file. |
| Cache Size | `_cache_size` | `int` | Maximum cache size; default is 2000K (2M). See [PRAGMA cache_size](https://sqlite.org/pragma.html#pragma_cache_size) |
## DSN Examples
@ -138,18 +136,19 @@ file:test.db?cache=shared&mode=memory
This package allows additional configuration of features available within SQLite3 to be enabled or disabled by golang build constraints also known as build `tags`.
Click [here](https://golang.org/pkg/go/build/#hdr-Build_Constraints) for more information about build tags / constraints.
[Click here for more information about build tags / constraints.](https://golang.org/pkg/go/build/#hdr-Build_Constraints)
### Usage
If you wish to build this library with additional extensions / features, use the following command:
If you wish to build this library with additional extensions / features.
Use the following command.
```bash
go build --tags "<FEATURE>"
```
For available features, see the extension list.
When using multiple build tags, all the different tags should be space delimited.
For available features see the extension list.
When using multiple build tags, all the different tags should be space delimted.
Example:
@ -165,7 +164,6 @@ 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 |
@ -173,20 +171,17 @@ 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` environment variable if not set by default, and the presence of the `gcc` compiler.
This package requires `CGO_ENABLED=1` ennvironment 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.
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.
## Android
@ -201,7 +196,7 @@ For more information see [#201](https://github.com/mattn/go-sqlite3/issues/201)
# ARM
To compile for `ARM` use the following environment:
To compile for `ARM` use the following environment.
```bash
env CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ \
@ -220,13 +215,14 @@ 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 [musl-cross](https://github.com/FiloSottile/homebrew-musl-cross).
The simplest way to cross compile from OSX is to use [xgo](https://github.com/karalabe/xgo).
Steps:
- 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"`.
- Install [xgo](https://github.com/karalabe/xgo) (`go get github.com/karalabe/xgo`).
- Ensure that your project is within your `GOPATH`.
- Run `xgo local/path/to/project`.
Please refer to the project's [README](https://github.com/FiloSottile/homebrew-musl-cross#readme) for further information.
Please refer to the project's [README](https://github.com/karalabe/xgo/blob/master/README.md) for further information.
# Google Cloud Platform
@ -236,7 +232,7 @@ Please work only with compiled final binaries.
## Linux
To compile this package on Linux, you must install the development tools for your linux distribution.
To compile this package on Linux you must install the development tools for your linux distribution.
To compile under linux use the build tag `linux`.
@ -252,7 +248,7 @@ go build --tags "libsqlite3 linux"
### Alpine
When building in an `alpine` container run the following command before building:
When building in an `alpine` container run the following command before building.
```
apk add --update gcc musl-dev
@ -272,29 +268,29 @@ sudo apt-get install build-essential
## Mac OSX
OSX should have all the tools present to compile this package. If not, install XCode to add all the developers tools.
OSX should have all the tools present to compile this package, if not install XCode this will add all the developers tools.
Required dependency:
Required dependency
```bash
brew install sqlite3
```
For OSX, there is an additional package to install which is required if you wish 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`.
```bash
brew upgrade icu4c
```
To compile for Mac OSX:
To compile for Mac OSX.
```bash
go build --tags "darwin"
```
If you wish to link directly to libsqlite3, use the `libsqlite3` build tag:
If you wish to link directly to libsqlite3 then you can use the `libsqlite3` build tag.
```
go build --tags "libsqlite3 darwin"
@ -306,14 +302,14 @@ Additional information:
## Windows
To compile this package on Windows, you must have the `gcc` compiler installed.
To compile this package on Windows OS you must have the `gcc` compiler installed.
1) Install a Windows `gcc` toolchain.
2) Add the `bin` folder to the Windows path, if the installer did not do this by default.
3) Open a terminal for the TDM-GCC toolchain, which can be found in the Windows Start menu.
2) Add the `bin` folders to the Windows path if the installer did not do this by default.
3) Open a terminal for the TDM-GCC toolchain, can be found in the Windows Start menu.
4) Navigate to your project folder and run the `go build ...` command for this package.
For example the TDM-GCC Toolchain can be found [here](https://jmeubank.github.io/tdm-gcc/).
For example the TDM-GCC Toolchain can be found [here](https://sourceforge.net/projects/tdm-gcc/).
## Errors
@ -351,28 +347,28 @@ This package supports the SQLite User Authentication module.
## Compile
To use the User authentication module, the package has to be compiled with the tag `sqlite_userauth`. See [Features](#features).
To use the User authentication module the package has to be compiled with the tag `sqlite_userauth`. See [Features](#features).
## Usage
### Create protected database
To create a database protected by user authentication, provide the following argument to the connection string `_auth`.
To create a database protected by user authentication provide the following argument to the connection string `_auth`.
This will enable user authentication within the database. This option however requires two additional arguments:
- `_auth_user`
- `_auth_pass`
When `_auth` is present in the connection string user authentication will be enabled and the provided user will be created
When `_auth` is present on the connection string user authentication will be enabled and the provided user will be created
as an `admin` user. After initial creation, the parameter `_auth` has no effect anymore and can be omitted from the connection string.
Example connection strings:
Example connection string:
Create an user authentication database with user `admin` and password `admin`:
Create an user authentication database with user `admin` and password `admin`.
`file:test.s3db?_auth&_auth_user=admin&_auth_pass=admin`
Create an user authentication database with user `admin` and password `admin` and use `SHA1` for the password encoding:
Create an user authentication database with user `admin` and password `admin` and use `SHA1` for the password encoding.
`file:test.s3db?_auth&_auth_user=admin&_auth_pass=admin&_auth_crypt=sha1`
@ -398,11 +394,11 @@ salt this can be configured with `_auth_salt`.
### Restrictions
Operations on the database regarding user management can only be preformed by an administrator user.
Operations on the database regarding to user management can only be preformed by an administrator user.
### Support
The user authentication supports two kinds of users:
The user authentication supports two kinds of users
- administrators
- regular users
@ -413,7 +409,7 @@ User management can be done by directly using the `*SQLiteConn` or by SQL.
#### SQL
The following sql functions are available for user management:
The following sql functions are available for user management.
| Function | Arguments | Description |
|----------|-----------|-------------|
@ -422,7 +418,7 @@ The following sql functions are available for user management:
| `auth_user_change` | username `string`, password `string`, admin `int` | Function to modify an user. Users can change their own password, but only an administrator can change the administrator flag. |
| `authUserDelete` | username `string` | Delete an user from the database. Can only be used by an administrator. The current logged in administrator cannot be deleted. This is to make sure their is always an administrator remaining. |
These functions will return an integer:
These functions will return an integer.
- 0 (SQLITE_OK)
- 23 (SQLITE_AUTH) Failed to perform due to authentication or insufficient privileges
@ -443,7 +439,7 @@ SELECT user_delete('user');
#### *SQLiteConn
The following functions are available for User authentication from the `*SQLiteConn`:
The following functions are available for User authentication from the `*SQLiteConn`.
| Function | Description |
|----------|-------------|
@ -454,16 +450,16 @@ The following functions are available for User authentication from the `*SQLiteC
### Attached database
When using attached databases, SQLite will use the authentication from the `main` database for the attached database(s).
When using attached databases. SQLite will use the authentication from the `main` database for the attached database(s).
# Extensions
If you want your own extension to be listed here, or you want to add a reference to an extension; please submit an Issue for this.
If you want your own extension to be listed here or you want to add a reference to an extension; please submit an Issue for this.
## Spatialite
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
@ -473,7 +469,7 @@ extension-functions.c is available as an extension to SQLite, and provides the f
- 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).
For an example see [dinedal/go-sqlite3-extension-functions](https://github.com/dinedal/go-sqlite3-extension-functions).
# FAQ
@ -493,7 +489,7 @@ For an example, see [dinedal/go-sqlite3-extension-functions](https://github.com/
- Can I use this in multiple routines concurrently?
Yes for readonly. But not for writable. See [#50](https://github.com/mattn/go-sqlite3/issues/50), [#51](https://github.com/mattn/go-sqlite3/issues/51), [#209](https://github.com/mattn/go-sqlite3/issues/209), [#274](https://github.com/mattn/go-sqlite3/issues/274).
Yes for readonly. But, No for writable. See [#50](https://github.com/mattn/go-sqlite3/issues/50), [#51](https://github.com/mattn/go-sqlite3/issues/51), [#209](https://github.com/mattn/go-sqlite3/issues/209), [#274](https://github.com/mattn/go-sqlite3/issues/274).
- Why I'm getting `no such table` error?
@ -507,7 +503,7 @@ For an example, see [dinedal/go-sqlite3-extension-functions](https://github.com/
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)
* [#511](https://github.com/mattn/go-sqlite3/issues/511)
* https://www.sqlite.org/sharedcache.html#shared_cache_and_in_memory_databases
@ -517,20 +513,20 @@ For an example, see [dinedal/go-sqlite3-extension-functions](https://github.com/
OS X limits OS-wide to not have more than 1000 files open simultaneously by default.
For more information, see [#289](https://github.com/mattn/go-sqlite3/issues/289)
For more information see [#289](https://github.com/mattn/go-sqlite3/issues/289)
- Trying to execute a `.` (dot) command throws an error.
Error: `Error: near ".": syntax error`
Dot command are part of SQLite3 CLI, not of this library.
Dot command are part of SQLite3 CLI not of this library.
You need to implement the feature or call the sqlite3 cli.
More information see [#305](https://github.com/mattn/go-sqlite3/issues/305).
More information see [#305](https://github.com/mattn/go-sqlite3/issues/305)
- Error: `database is locked`
When you get a database is locked, please use the following options.
When you get a database is locked. Please use the following options.
Add to DSN: `cache=shared`
@ -539,24 +535,24 @@ For an example, see [dinedal/go-sqlite3-extension-functions](https://github.com/
db, err := sql.Open("sqlite3", "file:locked.sqlite?cache=shared")
```
Next, please set the database connections of the SQL package to 1:
Second please set the database connections of the SQL package to 1.
```go
db.SetMaxOpenConns(1)
```
For more information, see [#209](https://github.com/mattn/go-sqlite3/issues/209).
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](CONTRIBUTING.md)].
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 here](https://opencollective.com/mattn-go-sqlite3/contribute)].
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/mattn-go-sqlite3/contribute)]
#### Individuals

View File

@ -1,12 +0,0 @@
TARGET = custom_driver_name
ifeq ($(OS),Windows_NT)
TARGET := $(TARGET).exe
endif
all : $(TARGET)
$(TARGET) : main.go
go build -ldflags="-X 'github.com/mattn/go-sqlite3.driverName=my-sqlite3'"
clean :
rm -f $(TARGET)

View File

@ -1,13 +0,0 @@
package main
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
func main() {
for _, driver := range sql.Drivers() {
println(driver)
}
}

View File

@ -1,30 +0,0 @@
package sqlite3_fuzz
import (
"bytes"
"database/sql"
"io/ioutil"
_ "github.com/mattn/go-sqlite3"
)
func FuzzOpenExec(data []byte) int {
sep := bytes.IndexByte(data, 0)
if sep <= 0 {
return 0
}
err := ioutil.WriteFile("/tmp/fuzz.db", data[sep+1:], 0644)
if err != nil {
return 0
}
db, err := sql.Open("sqlite3", "/tmp/fuzz.db")
if err != nil {
return 0
}
defer db.Close()
_, err = db.Exec(string(data[:sep-1]))
if err != nil {
return 0
}
return 1
}

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

@ -1,45 +0,0 @@
# =============================================================================
# Multi-stage Dockerfile Example
# =============================================================================
# This is a simple Dockerfile that will build an image of scratch-base image.
# Usage:
# docker build -t simple:local . && docker run --rm simple:local
# =============================================================================
# -----------------------------------------------------------------------------
# Build Stage
# -----------------------------------------------------------------------------
FROM golang:alpine AS build
# Important:
# Because this is a CGO enabled package, you are required to set it as 1.
ENV CGO_ENABLED=1
RUN apk add --no-cache \
# Important: required for go-sqlite3
gcc \
# Required for Alpine
musl-dev
WORKDIR /workspace
COPY . /workspace/
RUN \
go mod init github.com/mattn/sample && \
go mod tidy && \
go install -ldflags='-s -w -extldflags "-static"' ./simple.go
RUN \
# Smoke test
set -o pipefail; \
/go/bin/simple | grep 99\ こんにちは世界099
# -----------------------------------------------------------------------------
# Main Stage
# -----------------------------------------------------------------------------
FROM scratch
COPY --from=build /go/bin/simple /usr/local/bin/simple
ENTRYPOINT [ "/usr/local/bin/simple" ]

View File

@ -37,15 +37,12 @@ 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)
}
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
tx.Commit()
rows, err := db.Query("select id, name from foo")
if err != nil {

View File

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

View File

@ -1,33 +0,0 @@
package main
import (
"database/sql"
"fmt"
"log"
"github.com/mattn/go-sqlite3"
)
func main() {
sql.Register("sqlite3_with_extensions", &sqlite3.SQLiteDriver{
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
return conn.CreateModule("series", &seriesModule{})
},
})
db, err := sql.Open("sqlite3_with_extensions", ":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()
rows, err := db.Query("select * from series")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var value int
rows.Scan(&value)
fmt.Printf("value: %d\n", value)
}
}

View File

@ -1,118 +0,0 @@
package main
import (
"fmt"
"github.com/mattn/go-sqlite3"
)
type seriesModule struct{}
func (m *seriesModule) EponymousOnlyModule() {}
func (m *seriesModule) Create(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
err := c.DeclareVTab(fmt.Sprintf(`
CREATE TABLE %s (
value INT,
start HIDDEN,
stop HIDDEN,
step HIDDEN
)`, args[0]))
if err != nil {
return nil, err
}
return &seriesTable{0, 0, 1}, nil
}
func (m *seriesModule) Connect(c *sqlite3.SQLiteConn, args []string) (sqlite3.VTab, error) {
return m.Create(c, args)
}
func (m *seriesModule) DestroyModule() {}
type seriesTable struct {
start int64
stop int64
step int64
}
func (v *seriesTable) Open() (sqlite3.VTabCursor, error) {
return &seriesCursor{v, 0}, nil
}
func (v *seriesTable) BestIndex(csts []sqlite3.InfoConstraint, ob []sqlite3.InfoOrderBy) (*sqlite3.IndexResult, error) {
used := make([]bool, len(csts))
for c, cst := range csts {
if cst.Usable && cst.Op == sqlite3.OpEQ {
used[c] = true
}
}
return &sqlite3.IndexResult{
IdxNum: 0,
IdxStr: "default",
Used: used,
}, nil
}
func (v *seriesTable) Disconnect() error { return nil }
func (v *seriesTable) Destroy() error { return nil }
type seriesCursor struct {
*seriesTable
value int64
}
func (vc *seriesCursor) Column(c *sqlite3.SQLiteContext, col int) error {
switch col {
case 0:
c.ResultInt64(vc.value)
case 1:
c.ResultInt64(vc.seriesTable.start)
case 2:
c.ResultInt64(vc.seriesTable.stop)
case 3:
c.ResultInt64(vc.seriesTable.step)
}
return nil
}
func (vc *seriesCursor) Filter(idxNum int, idxStr string, vals []interface{}) error {
switch {
case len(vals) < 1:
vc.seriesTable.start = 0
vc.seriesTable.stop = 1000
vc.value = vc.seriesTable.start
case len(vals) < 2:
vc.seriesTable.start = vals[0].(int64)
vc.seriesTable.stop = 1000
vc.value = vc.seriesTable.start
case len(vals) < 3:
vc.seriesTable.start = vals[0].(int64)
vc.seriesTable.stop = vals[1].(int64)
vc.value = vc.seriesTable.start
case len(vals) < 4:
vc.seriesTable.start = vals[0].(int64)
vc.seriesTable.stop = vals[1].(int64)
vc.seriesTable.step = vals[2].(int64)
}
return nil
}
func (vc *seriesCursor) Next() error {
vc.value += vc.step
return nil
}
func (vc *seriesCursor) EOF() bool {
return vc.value > vc.stop
}
func (vc *seriesCursor) Rowid() (int64, error) {
return int64(vc.value), nil
}
func (vc *seriesCursor) Close() error {
return nil
}

View File

@ -7,7 +7,7 @@ package sqlite3
/*
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif

View File

@ -12,7 +12,7 @@ package sqlite3
/*
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
@ -353,20 +353,6 @@ 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:
@ -374,11 +360,6 @@ 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,15 +102,3 @@ 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)
}
}

35
doc.go
View File

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

View File

@ -7,7 +7,7 @@ package sqlite3
/*
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif

6
go.mod
View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
//go:build cgo
// +build cgo
package sqlite3
@ -16,16 +15,16 @@ package sqlite3
#cgo CFLAGS: -DHAVE_USLEEP=1
#cgo CFLAGS: -DSQLITE_ENABLE_FTS3
#cgo CFLAGS: -DSQLITE_ENABLE_FTS3_PARENTHESIS
#cgo CFLAGS: -DSQLITE_ENABLE_FTS4_UNICODE61
#cgo CFLAGS: -DSQLITE_TRACE_SIZE_LIMIT=15
#cgo CFLAGS: -DSQLITE_OMIT_DEPRECATED
#cgo CFLAGS: -DSQLITE_DISABLE_INTRINSIC
#cgo CFLAGS: -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1
#cgo CFLAGS: -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT
#cgo CFLAGS: -Wno-deprecated-declarations
#cgo linux,!android CFLAGS: -DHAVE_PREAD64=1 -DHAVE_PWRITE64=1
#cgo openbsd CFLAGS: -I/usr/local/include
#cgo openbsd LDFLAGS: -L/usr/local/lib
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
@ -234,14 +233,8 @@ const (
columnTimestamp string = "timestamp"
)
// This variable can be replaced with -ldflags like below:
// go build -ldflags="-X 'github.com/mattn/go-sqlite3.driverName=my-sqlite3'"
var driverName = "sqlite3"
func init() {
if driverName != "" {
sql.Register(driverName, &SQLiteDriver{})
}
sql.Register("sqlite3", &SQLiteDriver{})
}
// Version returns SQLite library version information.
@ -297,51 +290,6 @@ const (
/*SQLITE_RECURSIVE = C.SQLITE_RECURSIVE*/
)
// Standard File Control Opcodes
// See: https://www.sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
const (
SQLITE_FCNTL_LOCKSTATE = int(1)
SQLITE_FCNTL_GET_LOCKPROXYFILE = int(2)
SQLITE_FCNTL_SET_LOCKPROXYFILE = int(3)
SQLITE_FCNTL_LAST_ERRNO = int(4)
SQLITE_FCNTL_SIZE_HINT = int(5)
SQLITE_FCNTL_CHUNK_SIZE = int(6)
SQLITE_FCNTL_FILE_POINTER = int(7)
SQLITE_FCNTL_SYNC_OMITTED = int(8)
SQLITE_FCNTL_WIN32_AV_RETRY = int(9)
SQLITE_FCNTL_PERSIST_WAL = int(10)
SQLITE_FCNTL_OVERWRITE = int(11)
SQLITE_FCNTL_VFSNAME = int(12)
SQLITE_FCNTL_POWERSAFE_OVERWRITE = int(13)
SQLITE_FCNTL_PRAGMA = int(14)
SQLITE_FCNTL_BUSYHANDLER = int(15)
SQLITE_FCNTL_TEMPFILENAME = int(16)
SQLITE_FCNTL_MMAP_SIZE = int(18)
SQLITE_FCNTL_TRACE = int(19)
SQLITE_FCNTL_HAS_MOVED = int(20)
SQLITE_FCNTL_SYNC = int(21)
SQLITE_FCNTL_COMMIT_PHASETWO = int(22)
SQLITE_FCNTL_WIN32_SET_HANDLE = int(23)
SQLITE_FCNTL_WAL_BLOCK = int(24)
SQLITE_FCNTL_ZIPVFS = int(25)
SQLITE_FCNTL_RBU = int(26)
SQLITE_FCNTL_VFS_POINTER = int(27)
SQLITE_FCNTL_JOURNAL_POINTER = int(28)
SQLITE_FCNTL_WIN32_GET_HANDLE = int(29)
SQLITE_FCNTL_PDB = int(30)
SQLITE_FCNTL_BEGIN_ATOMIC_WRITE = int(31)
SQLITE_FCNTL_COMMIT_ATOMIC_WRITE = int(32)
SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE = int(33)
SQLITE_FCNTL_LOCK_TIMEOUT = int(34)
SQLITE_FCNTL_DATA_VERSION = int(35)
SQLITE_FCNTL_SIZE_LIMIT = int(36)
SQLITE_FCNTL_CKPT_DONE = int(37)
SQLITE_FCNTL_RESERVE_BYTES = int(38)
SQLITE_FCNTL_CKPT_START = int(39)
SQLITE_FCNTL_EXTERNAL_READER = int(40)
SQLITE_FCNTL_CKSM_FILE = int(41)
)
// SQLiteDriver implements driver.Driver.
type SQLiteDriver struct {
Extensions []string
@ -494,12 +442,10 @@ 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 {
// sqlite3 may leave the transaction open in this scenario.
if err != nil && err.(Error).Code == C.SQLITE_BUSY {
// sqlite3 will 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
@ -837,9 +783,9 @@ func lastError(db *C.sqlite3) error {
// Exec implements Execer.
func (c *SQLiteConn) Exec(query string, args []driver.Value) (driver.Result, error) {
list := make([]driver.NamedValue, len(args))
list := make([]namedValue, len(args))
for i, v := range args {
list[i] = driver.NamedValue{
list[i] = namedValue{
Ordinal: i + 1,
Value: v,
}
@ -847,7 +793,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 []driver.NamedValue) (driver.Result, error) {
func (c *SQLiteConn) exec(ctx context.Context, query string, args []namedValue) (driver.Result, error) {
start := 0
for {
s, err := c.prepare(ctx, query)
@ -856,7 +802,7 @@ func (c *SQLiteConn) exec(ctx context.Context, query string, args []driver.Named
}
var res driver.Result
if s.(*SQLiteStmt).s != nil {
stmtArgs := make([]driver.NamedValue, 0, len(args))
stmtArgs := make([]namedValue, 0, len(args))
na := s.NumInput()
if len(args)-start < na {
s.Close()
@ -884,21 +830,23 @@ func (c *SQLiteConn) exec(ctx context.Context, query string, args []driver.Named
tail := s.(*SQLiteStmt).t
s.Close()
if tail == "" {
if res == nil {
// https://github.com/mattn/go-sqlite3/issues/963
res = &SQLiteResult{0, 0}
}
return res, nil
}
query = tail
}
}
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([]driver.NamedValue, len(args))
list := make([]namedValue, len(args))
for i, v := range args {
list[i] = driver.NamedValue{
list[i] = namedValue{
Ordinal: i + 1,
Value: v,
}
@ -906,10 +854,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 []driver.NamedValue) (driver.Rows, error) {
func (c *SQLiteConn) query(ctx context.Context, query string, args []namedValue) (driver.Rows, error) {
start := 0
for {
stmtArgs := make([]driver.NamedValue, 0, len(args))
stmtArgs := make([]namedValue, 0, len(args))
s, err := c.prepare(ctx, query)
if err != nil {
return nil, err
@ -1093,8 +1041,6 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
secureDelete := "DEFAULT"
synchronousMode := "NORMAL"
writableSchema := -1
vfsName := ""
var cacheSize *int64
pos := strings.IndexRune(dsn, '?')
if pos >= 1 {
@ -1418,22 +1364,6 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
}
}
// Cache size (_cache_size)
//
// https://sqlite.org/pragma.html#pragma_cache_size
//
if val := params.Get("_cache_size"); val != "" {
iv, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return nil, fmt.Errorf("Invalid _cache_size: %v: %v", val, err)
}
cacheSize = &iv
}
if val := params.Get("vfs"); val != "" {
vfsName = val
}
if !strings.HasPrefix(dsn, "file:") {
dsn = dsn[:pos]
}
@ -1442,14 +1372,9 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
var db *C.sqlite3
name := C.CString(dsn)
defer C.free(unsafe.Pointer(name))
var vfs *C.char
if vfsName != "" {
vfs = C.CString(vfsName)
defer C.free(unsafe.Pointer(vfs))
}
rv := C._sqlite3_open_v2(name, &db,
mutex|C.SQLITE_OPEN_READWRITE|C.SQLITE_OPEN_CREATE,
vfs)
nil)
if rv != 0 {
// Save off the error _before_ closing the database.
// This is safe even if db is nil.
@ -1463,6 +1388,12 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
return nil, errors.New("sqlite succeeded without returning a database")
}
rv = C.sqlite3_busy_timeout(db, C.int(busyTimeout))
if rv != C.SQLITE_OK {
C.sqlite3_close_v2(db)
return nil, Error{Code: ErrNo(rv)}
}
exec := func(s string) error {
cs := C.CString(s)
rv := C.sqlite3_exec(db, cs, nil, nil, nil)
@ -1473,12 +1404,6 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
return nil
}
// Busy timeout
if err := exec(fmt.Sprintf("PRAGMA busy_timeout = %d;", busyTimeout)); err != nil {
C.sqlite3_close_v2(db)
return nil, err
}
// USER AUTHENTICATION
//
// User Authentication is always performed even when
@ -1730,7 +1655,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
//
// Because default is NORMAL this statement is always executed
if err := exec(fmt.Sprintf("PRAGMA synchronous = %s;", synchronousMode)); err != nil {
conn.Close()
C.sqlite3_close_v2(db)
return nil, err
}
@ -1742,14 +1667,6 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
}
}
// Cache Size
if cacheSize != nil {
if err := exec(fmt.Sprintf("PRAGMA cache_size = %d;", *cacheSize)); err != nil {
C.sqlite3_close_v2(db)
return nil, err
}
}
if len(d.Extensions) > 0 {
if err := conn.loadExtensions(d.Extensions); err != nil {
conn.Close()
@ -1854,31 +1771,6 @@ func (c *SQLiteConn) SetLimit(id int, newVal int) int {
return int(C._sqlite3_limit(c.db, C.int(id), C.int(newVal)))
}
// SetFileControlInt invokes the xFileControl method on a given database. The
// dbName is the name of the database. It will default to "main" if left blank.
// The op is one of the opcodes prefixed by "SQLITE_FCNTL_". The arg argument
// and return code are both opcode-specific. Please see the SQLite documentation.
//
// This method is not thread-safe as the returned error code can be changed by
// another call if invoked concurrently.
//
// See: sqlite3_file_control, https://www.sqlite.org/c3ref/file_control.html
func (c *SQLiteConn) SetFileControlInt(dbName string, op int, arg int) error {
if dbName == "" {
dbName = "main"
}
cDBName := C.CString(dbName)
defer C.free(unsafe.Pointer(cDBName))
cArg := C.int(arg)
rv := C.sqlite3_file_control(c.db, cDBName, C.int(op), unsafe.Pointer(&cArg))
if rv != C.SQLITE_OK {
return c.lastError()
}
return nil
}
// Close the statement.
func (s *SQLiteStmt) Close() error {
s.mu.Lock()
@ -1906,7 +1798,7 @@ func (s *SQLiteStmt) NumInput() int {
var placeHolder = []byte{0}
func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
func (s *SQLiteStmt) bind(args []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()
@ -1976,9 +1868,9 @@ func (s *SQLiteStmt) bind(args []driver.NamedValue) error {
// Query the statement with arguments. Return records.
func (s *SQLiteStmt) Query(args []driver.Value) (driver.Rows, error) {
list := make([]driver.NamedValue, len(args))
list := make([]namedValue, len(args))
for i, v := range args {
list[i] = driver.NamedValue{
list[i] = namedValue{
Ordinal: i + 1,
Value: v,
}
@ -1986,7 +1878,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 []driver.NamedValue) (driver.Rows, error) {
func (s *SQLiteStmt) query(ctx context.Context, args []namedValue) (driver.Rows, error) {
if err := s.bind(args); err != nil {
return nil, err
}
@ -2016,9 +1908,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([]driver.NamedValue, len(args))
list := make([]namedValue, len(args))
for i, v := range args {
list[i] = driver.NamedValue{
list[i] = namedValue{
Ordinal: i + 1,
Value: v,
}
@ -2035,7 +1927,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 []driver.NamedValue) (driver.Result, error) {
func (s *SQLiteStmt) exec(ctx context.Context, args []namedValue) (driver.Result, error) {
if ctx.Done() == nil {
return s.execSync(args)
}
@ -2067,7 +1959,7 @@ func (s *SQLiteStmt) exec(ctx context.Context, args []driver.NamedValue) (driver
return rv.r, rv.err
}
func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) {
func (s *SQLiteStmt) execSync(args []namedValue) (driver.Result, error) {
if err := s.bind(args); err != nil {
C.sqlite3_reset(s.s)
C.sqlite3_clear_bindings(s.s)
@ -2086,13 +1978,6 @@ func (s *SQLiteStmt) execSync(args []driver.NamedValue) (driver.Result, error) {
return &SQLiteResult{id: int64(rowid), changes: int64(changes)}, nil
}
// Readonly reports if this statement is considered readonly by SQLite.
//
// See: https://sqlite.org/c3ref/stmt_readonly.html
func (s *SQLiteStmt) Readonly() bool {
return C.sqlite3_stmt_readonly(s.s) == 1
}
// Close the rows.
func (rc *SQLiteRows) Close() error {
rc.s.mu.Lock()

View File

@ -8,7 +8,7 @@ package sqlite3
/*
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif

View File

@ -11,7 +11,6 @@ import (
"context"
"database/sql"
"database/sql/driver"
"errors"
"os"
"testing"
)
@ -50,10 +49,6 @@ func TestBeginTxCancel(t *testing.T) {
if !ok {
t.Fatal("unexpected: wrong type")
}
// checks that conn.Raw can be used to get *SQLiteConn
if _, ok = driverConn.(*SQLiteConn); !ok {
t.Fatalf("conn.Raw() driverConn type=%T, expected *SQLiteConn", driverConn)
}
go cancel() // make it cancel concurrently with exec("BEGIN");
tx, err := d.BeginTx(ctx, driver.TxOptions{})
@ -77,43 +72,3 @@ func TestBeginTxCancel(t *testing.T) {
}()
}
}
func TestStmtReadonly(t *testing.T) {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatal(err)
}
_, err = db.Exec("CREATE TABLE t (count INT)")
if err != nil {
t.Fatal(err)
}
isRO := func(query string) bool {
c, err := db.Conn(context.Background())
if err != nil {
return false
}
var ro bool
c.Raw(func(dc interface{}) error {
stmt, err := dc.(*SQLiteConn).Prepare(query)
if err != nil {
return err
}
if stmt == nil {
return errors.New("stmt is nil")
}
ro = stmt.(*SQLiteStmt).Readonly()
return nil
})
return ro // On errors ro will remain false.
}
if !isRO(`select * from t`) {
t.Error("select not seen as read-only")
}
if isRO(`insert into t values (1), (2)`) {
t.Error("insert seen as read-only")
}
}

View File

@ -25,12 +25,20 @@ 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) {
return c.query(ctx, query, args)
list := make([]namedValue, len(args))
for i, nv := range args {
list[i] = namedValue(nv)
}
return c.query(ctx, query, list)
}
// ExecContext implement ExecerContext.
func (c *SQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
return c.exec(ctx, query, args)
list := make([]namedValue, len(args))
for i, nv := range args {
list[i] = namedValue(nv)
}
return c.exec(ctx, query, list)
}
// PrepareContext implement ConnPrepareContext.
@ -45,10 +53,18 @@ 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) {
return s.query(ctx, args)
list := make([]namedValue, len(args))
for i, nv := range args {
list[i] = namedValue(nv)
}
return s.query(ctx, list)
}
// ExecContext implement ExecerContext.
func (s *SQLiteStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
return s.exec(ctx, args)
list := make([]namedValue, len(args))
for i, nv := range args {
list[i] = namedValue(nv)
}
return s.exec(ctx, list)
}

View File

@ -150,11 +150,8 @@ func TestExecContextCancel(t *testing.T) {
ts := time.Now()
initDatabase(t, db, 1000)
spent := time.Since(ts)
const minTestTime = 100 * time.Millisecond
if spent < minTestTime {
t.Skipf("test will be too racy (spent=%s < min=%s) as ExecContext below will be too fast.",
spent.String(), minTestTime.String(),
)
if spent < 100*time.Millisecond {
t.Skip("test will be too racy, as ExecContext below will be too fast.")
}
// expected to be extremely slow query
@ -163,17 +160,14 @@ INSERT INTO test_table (key1, key_id, key2, key3, key4, key5, key6, data)
SELECT t1.key1 || t2.key1, t1.key_id || t2.key_id, t1.key2 || t2.key2, t1.key3 || t2.key3, t1.key4 || t2.key4, t1.key5 || t2.key5, t1.key6 || t2.key6, t1.data || t2.data
FROM test_table t1 LEFT OUTER JOIN test_table t2`
// expect query above take ~ same time as setup above
// This is racy: the context must be valid so sql/db.ExecContext calls the sqlite3 driver.
// It starts the query, the context expires, then calls sqlite3_interrupt
ctx, cancel := context.WithTimeout(context.Background(), minTestTime/2)
ctx, cancel := context.WithTimeout(context.Background(), spent/2)
defer cancel()
ts = time.Now()
r, err := db.ExecContext(ctx, q)
// racy check
if r != nil {
n, err := r.RowsAffected()
t.Logf("query should not have succeeded: rows=%d; err=%v; duration=%s",
n, err, time.Since(ts).String())
t.Log(n, err, time.Since(ts))
}
if err != context.DeadlineExceeded {
t.Fatal(err, ctx.Err())

View File

@ -9,7 +9,7 @@ package sqlite3
/*
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
@ -68,7 +68,7 @@ func (c *SQLiteConn) loadExtension(lib string, entry *string) error {
var centry *C.char
if entry != nil {
centry = C.CString(*entry)
centry := C.CString(*entry)
defer C.free(unsafe.Pointer(centry))
}

View File

@ -1,21 +0,0 @@
// +build sqlite_column_metadata
package sqlite3
/*
#ifndef USE_LIBSQLITE3
#cgo CFLAGS: -DSQLITE_ENABLE_COLUMN_METADATA
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
*/
import "C"
// ColumnTableName returns the table that is the origin of a particular result
// column in a SELECT statement.
//
// See https://www.sqlite.org/c3ref/column_database_name.html
func (s *SQLiteStmt) ColumnTableName(n int) string {
return C.GoString(C.sqlite3_column_table_name(s.s, C.int(n)))
}

View File

@ -1,39 +0,0 @@
// +build sqlite_column_metadata
package sqlite3
import "testing"
func TestColumnTableName(t *testing.T) {
d := SQLiteDriver{}
conn, err := d.Open(":memory:")
if err != nil {
t.Fatal("failed to get database connection:", err)
}
defer conn.Close()
sqlite3conn := conn.(*SQLiteConn)
_, err = sqlite3conn.Exec(`CREATE TABLE foo (name string)`, nil)
if err != nil {
t.Fatal("Failed to create table:", err)
}
_, err = sqlite3conn.Exec(`CREATE TABLE bar (name string)`, nil)
if err != nil {
t.Fatal("Failed to create table:", err)
}
stmt, err := sqlite3conn.Prepare(`SELECT * FROM foo JOIN bar ON foo.name = bar.name`)
if err != nil {
t.Fatal(err)
}
if exp, got := "foo", stmt.(*SQLiteStmt).ColumnTableName(0); exp != got {
t.Fatalf("Incorrect table name returned expected: %s, got: %s", exp, got)
}
if exp, got := "bar", stmt.(*SQLiteStmt).ColumnTableName(1); exp != got {
t.Fatalf("Incorrect table name returned expected: %s, got: %s", exp, got)
}
if exp, got := "", stmt.(*SQLiteStmt).ColumnTableName(2); exp != got {
t.Fatalf("Incorrect table name returned expected: %s, got: %s", exp, got)
}
}

13
sqlite3_opt_json1.go Normal file
View File

@ -0,0 +1,13 @@
// 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_json sqlite_json1 json1
package sqlite3
/*
#cgo CFLAGS: -DSQLITE_ENABLE_JSON1
*/
import "C"

View File

@ -1,14 +0,0 @@
// 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

@ -1,29 +0,0 @@
// +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)
}
}
}

View File

@ -1,15 +0,0 @@
// 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"

View File

@ -13,7 +13,7 @@ package sqlite3
#cgo LDFLAGS: -lm
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
@ -33,7 +33,7 @@ import (
// 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 preupdate hook for this connection, it will be
// 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)) {

View File

@ -13,7 +13,7 @@ package sqlite3
// 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 preupdate hook for this connection, it will be
// 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)) {

View File

@ -1,82 +0,0 @@
// +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

@ -1,20 +0,0 @@
// +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

@ -1,99 +0,0 @@
// +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

@ -5,7 +5,7 @@
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
#include <stdio.h>
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
extern int unlock_notify_wait(sqlite3 *db);

View File

@ -12,7 +12,7 @@ package sqlite3
#cgo CFLAGS: -DSQLITE_ENABLE_UNLOCK_NOTIFY
#include <stdlib.h>
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
extern void unlock_notify_callback(void *arg, int argc);
*/

View File

@ -11,7 +11,7 @@ package sqlite3
#cgo CFLAGS: -DSQLITE_USER_AUTHENTICATION
#cgo LDFLAGS: -lm
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif

View File

@ -19,7 +19,7 @@ package sqlite3
#cgo CFLAGS: -Wno-deprecated-declarations
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
@ -226,43 +226,11 @@ static sqlite3_module goModule = {
0 // xRollbackTo
};
// See https://sqlite.org/vtab.html#eponymous_only_virtual_tables
static sqlite3_module goModuleEponymousOnly = {
0, // iVersion
0, // xCreate - create a table, which here is null
cXConnect, // xConnect - connect to an existing table
cXBestIndex, // xBestIndex - Determine search strategy
cXDisconnect, // xDisconnect - Disconnect from a table
cXDestroy, // xDestroy - Drop a table
cXOpen, // xOpen - open a cursor
cXClose, // xClose - close a cursor
cXFilter, // xFilter - configure scan constraints
cXNext, // xNext - advance a cursor
cXEof, // xEof
cXColumn, // xColumn - read data
cXRowid, // xRowid - read data
cXUpdate, // xUpdate - write data
// Not implemented
0, // xBegin - begin transaction
0, // xSync - sync transaction
0, // xCommit - commit transaction
0, // xRollback - rollback transaction
0, // xFindFunction - function overloading
0, // xRename - rename the table
0, // xSavepoint
0, // xRelease
0 // xRollbackTo
};
void goMDestroy(void*);
static int _sqlite3_create_module(sqlite3 *db, const char *zName, uintptr_t pClientData) {
return sqlite3_create_module_v2(db, zName, &goModule, (void*) pClientData, goMDestroy);
}
static int _sqlite3_create_module_eponymous_only(sqlite3 *db, const char *zName, uintptr_t pClientData) {
return sqlite3_create_module_v2(db, zName, &goModuleEponymousOnly, (void*) pClientData, goMDestroy);
}
*/
import "C"
@ -472,21 +440,10 @@ func goVBestIndex(pVTab unsafe.Pointer, icp unsafe.Pointer) *C.char {
}
info.idxNum = C.int(res.IdxNum)
info.idxStr = (*C.char)(C.sqlite3_malloc(C.int(len(res.IdxStr) + 1)))
if info.idxStr == nil {
// C.malloc and C.CString ordinarily do this for you. See https://golang.org/cmd/cgo/
panic("out of memory")
}
info.needToFreeIdxStr = C.int(1)
idxStr := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(info.idxStr)),
Len: len(res.IdxStr) + 1,
Cap: len(res.IdxStr) + 1,
}))
copy(idxStr, res.IdxStr)
idxStr[len(idxStr)-1] = 0 // null-terminated string
idxStr := C.CString(res.IdxStr)
defer C.free(unsafe.Pointer(idxStr))
info.idxStr = idxStr
info.needToFreeIdxStr = C.int(0)
if res.AlreadyOrdered {
info.orderByConsumed = C.int(1)
}
@ -638,13 +595,6 @@ type Module interface {
DestroyModule()
}
// EponymousOnlyModule is a "virtual table module" (as above), but
// for defining "eponymous only" virtual tables See: https://sqlite.org/vtab.html#eponymous_only_virtual_tables
type EponymousOnlyModule interface {
Module
EponymousOnlyModule()
}
// VTab describes a particular instance of the virtual table.
// See: http://sqlite.org/c3ref/vtab.html
type VTab interface {
@ -702,19 +652,9 @@ func (c *SQLiteConn) CreateModule(moduleName string, module Module) error {
mname := C.CString(moduleName)
defer C.free(unsafe.Pointer(mname))
udm := sqliteModule{c, moduleName, module}
switch module.(type) {
case EponymousOnlyModule:
rv := C._sqlite3_create_module_eponymous_only(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm))))
if rv != C.SQLITE_OK {
return c.lastError()
}
return nil
case Module:
rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm))))
if rv != C.SQLITE_OK {
return c.lastError()
}
return nil
rv := C._sqlite3_create_module(c.db, mname, C.uintptr_t(uintptr(newHandle(c, &udm))))
if rv != C.SQLITE_OK {
return c.lastError()
}
return nil
}

View File

@ -3,7 +3,6 @@
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
//go:build sqlite_vtable || vtable
// +build sqlite_vtable vtable
package sqlite3
@ -485,125 +484,3 @@ func (c *vtabUpdateCursor) Rowid() (int64, error) {
func (c *vtabUpdateCursor) Close() error {
return nil
}
type testModuleEponymousOnly struct {
t *testing.T
intarray []int
}
type testVTabEponymousOnly struct {
intarray []int
}
type testVTabCursorEponymousOnly struct {
vTab *testVTabEponymousOnly
index int
}
func (m testModuleEponymousOnly) EponymousOnlyModule() {}
func (m testModuleEponymousOnly) Create(c *SQLiteConn, args []string) (VTab, error) {
err := c.DeclareVTab("CREATE TABLE x(test INT)")
if err != nil {
return nil, err
}
return &testVTabEponymousOnly{m.intarray}, nil
}
func (m testModuleEponymousOnly) Connect(c *SQLiteConn, args []string) (VTab, error) {
return m.Create(c, args)
}
func (m testModuleEponymousOnly) DestroyModule() {}
func (v *testVTabEponymousOnly) BestIndex(cst []InfoConstraint, ob []InfoOrderBy) (*IndexResult, error) {
used := make([]bool, 0, len(cst))
for range cst {
used = append(used, false)
}
return &IndexResult{
Used: used,
IdxNum: 0,
IdxStr: "test-index",
AlreadyOrdered: true,
EstimatedCost: 100,
EstimatedRows: 200,
}, nil
}
func (v *testVTabEponymousOnly) Disconnect() error {
return nil
}
func (v *testVTabEponymousOnly) Destroy() error {
return nil
}
func (v *testVTabEponymousOnly) Open() (VTabCursor, error) {
return &testVTabCursorEponymousOnly{v, 0}, nil
}
func (vc *testVTabCursorEponymousOnly) Close() error {
return nil
}
func (vc *testVTabCursorEponymousOnly) Filter(idxNum int, idxStr string, vals []interface{}) error {
vc.index = 0
return nil
}
func (vc *testVTabCursorEponymousOnly) Next() error {
vc.index++
return nil
}
func (vc *testVTabCursorEponymousOnly) EOF() bool {
return vc.index >= len(vc.vTab.intarray)
}
func (vc *testVTabCursorEponymousOnly) Column(c *SQLiteContext, col int) error {
if col != 0 {
return fmt.Errorf("column index out of bounds: %d", col)
}
c.ResultInt(vc.vTab.intarray[vc.index])
return nil
}
func (vc *testVTabCursorEponymousOnly) Rowid() (int64, error) {
return int64(vc.index), nil
}
func TestCreateModuleEponymousOnly(t *testing.T) {
tempFilename := TempFilename(t)
defer os.Remove(tempFilename)
intarray := []int{1, 2, 3}
sql.Register("sqlite3_TestCreateModuleEponymousOnly", &SQLiteDriver{
ConnectHook: func(conn *SQLiteConn) error {
return conn.CreateModule("test", testModuleEponymousOnly{t, intarray})
},
})
db, err := sql.Open("sqlite3_TestCreateModuleEponymousOnly", tempFilename)
if err != nil {
t.Fatalf("could not open db: %v", err)
}
var i, value int
rows, err := db.Query("SELECT rowid, * FROM test")
if err != nil {
t.Fatalf("couldn't select from virtual table: %v", err)
}
for rows.Next() {
err := rows.Scan(&i, &value)
if err != nil {
t.Fatal(err)
}
if intarray[i] != value {
t.Fatalf("want %v but %v", intarray[i], value)
}
}
_, err = db.Exec("DROP TABLE test")
if err != nil {
t.Logf("couldn't drop virtual table: %v", err)
}
}

View File

@ -3,7 +3,6 @@
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
//go:build cgo
// +build cgo
package sqlite3
@ -20,7 +19,6 @@ import (
"os"
"reflect"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
@ -28,7 +26,7 @@ import (
"time"
)
func TempFilename(t testing.TB) string {
func TempFilename(t *testing.T) string {
f, err := ioutil.TempFile("", "go-sqlite3-test-")
if err != nil {
t.Fatal(err)
@ -103,43 +101,6 @@ func TestOpen(t *testing.T) {
}
}
func TestOpenWithVFS(t *testing.T) {
filename := t.Name() + ".sqlite"
if err := os.Remove(filename); err != nil && !os.IsNotExist(err) {
t.Fatal(err)
}
defer os.Remove(filename)
db, err := sql.Open("sqlite3", fmt.Sprintf("file:%s?vfs=hello", filename))
if err != nil {
t.Fatal("Failed to open", err)
}
err = db.Ping()
if err == nil {
t.Fatal("Failed to open", err)
}
db.Close()
defer os.Remove(filename)
var vfs string
if runtime.GOOS == "windows" {
vfs = "win32-none"
} else {
vfs = "unix-none"
}
db, err = sql.Open("sqlite3", fmt.Sprintf("file:%s?vfs=%s", filename, vfs))
if err != nil {
t.Fatal("Failed to open", err)
}
err = db.Ping()
if err != nil {
t.Fatal("Failed to ping", err)
}
db.Close()
}
func TestOpenNoCreate(t *testing.T) {
filename := t.Name() + ".sqlite"
@ -248,43 +209,6 @@ 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,
@ -1100,44 +1024,34 @@ 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)
}
_, 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;
`)
insert into foo(id) values(?);
insert into foo(id) values(?);
insert into foo(id) values(?);
select id from foo order by id;
`, 3, 2, 1)
if err != nil {
t.Error("Failed to call db.Query:", err)
}
defer rows.Close()
n := 0
for rows.Next() {
var id int
err = rows.Scan(&id)
if err != nil {
t.Error("Failed to db.Query:", err)
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")
}
}
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)
}
}
@ -1416,16 +1330,16 @@ func TestFunctionRegistration(t *testing.T) {
{"SELECT addf32_64(1.5,1.5)", float64(3)},
{"SELECT not(1)", false},
{"SELECT not(0)", true},
{`SELECT regex('^foo.*', 'foobar')`, true},
{`SELECT regex('^foo.*', 'barfoobar')`, false},
{`SELECT regex("^foo.*", "foobar")`, true},
{`SELECT regex("^foo.*", "barfoobar")`, false},
{"SELECT generic(1)", int64(1)},
{"SELECT generic(1.1)", int64(2)},
{`SELECT generic(NULL)`, int64(3)},
{`SELECT generic('foo')`, int64(4)},
{`SELECT generic("foo")`, int64(4)},
{"SELECT variadic(1,2)", int64(3)},
{"SELECT variadic(1,2,3,4)", int64(10)},
{"SELECT variadic(1,1,1,1,1,1,1,1,1,1)", int64(10)},
{`SELECT variadicGeneric(1,'foo',2.3, NULL)`, int64(4)},
{`SELECT variadicGeneric(1,"foo",2.3, NULL)`, int64(4)},
}
for _, op := range ops {
@ -1496,63 +1410,6 @@ 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':
@ -1592,11 +1449,11 @@ func TestCollationRegistration(t *testing.T) {
populate := []string{
`CREATE TABLE test (s TEXT)`,
`INSERT INTO test VALUES ('aaaa')`,
`INSERT INTO test VALUES ('ffff')`,
`INSERT INTO test VALUES ('qqqq')`,
`INSERT INTO test VALUES ('tttt')`,
`INSERT INTO test VALUES ('zzzz')`,
`INSERT INTO test VALUES ("aaaa")`,
`INSERT INTO test VALUES ("ffff")`,
`INSERT INTO test VALUES ("qqqq")`,
`INSERT INTO test VALUES ("tttt")`,
`INSERT INTO test VALUES ("zzzz")`,
}
for _, stmt := range populate {
if _, err := db.Exec(stmt); err != nil {
@ -1691,7 +1548,7 @@ func TestDeclTypes(t *testing.T) {
t.Fatal("Failed to create table:", err)
}
_, err = sqlite3conn.Exec("insert into foo(name) values('bar')", nil)
_, err = sqlite3conn.Exec("insert into foo(name) values(\"bar\")", nil)
if err != nil {
t.Fatal("Failed to insert:", err)
}
@ -1827,43 +1684,6 @@ func TestAuthorizer(t *testing.T) {
}
}
func TestSetFileControlInt(t *testing.T) {
t.Run("PERSIST_WAL", func(t *testing.T) {
tempFilename := TempFilename(t)
defer os.Remove(tempFilename)
sql.Register("sqlite3_FCNTL_PERSIST_WAL", &SQLiteDriver{
ConnectHook: func(conn *SQLiteConn) error {
if err := conn.SetFileControlInt("", SQLITE_FCNTL_PERSIST_WAL, 1); err != nil {
return fmt.Errorf("Unexpected error from SetFileControlInt(): %w", err)
}
return nil
},
})
db, err := sql.Open("sqlite3_FCNTL_PERSIST_WAL", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer db.Close()
// Set to WAL mode & write a page.
if _, err := db.Exec(`PRAGMA journal_mode = wal`); err != nil {
t.Fatal("Failed to set journal mode:", err)
} else if _, err := db.Exec(`CREATE TABLE t (x)`); err != nil {
t.Fatal("Failed to create table:", err)
}
if err := db.Close(); err != nil {
t.Fatal("Failed to close database", err)
}
// Ensure WAL file persists after close.
if _, err := os.Stat(tempFilename + "-wal"); err != nil {
t.Fatal("Expected WAL file to be persisted after close", err)
}
})
}
func TestNonColumnString(t *testing.T) {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
@ -2030,21 +1850,28 @@ func BenchmarkCustomFunctions(b *testing.B) {
}
func TestSuite(t *testing.T) {
initializeTestDB(t)
defer freeTestDB()
for _, test := range tests {
t.Run(test.Name, test.F)
tempFilename := TempFilename(t)
defer os.Remove(tempFilename)
d, err := sql.Open("sqlite3", tempFilename+"?_busy_timeout=99999")
if err != nil {
t.Fatal(err)
}
}
defer d.Close()
func BenchmarkSuite(b *testing.B) {
initializeTestDB(b)
defer freeTestDB()
for _, benchmark := range benchmarks {
b.Run(benchmark.Name, benchmark.F)
db = &TestDB{t, d, SQLITE, sync.Once{}}
ok := testing.RunTests(func(string, string) (bool, error) { return true, nil }, tests)
if !ok {
t.Fatal("A subtest failed")
}
if !testing.Short() {
for _, b := range benchmarks {
fmt.Printf("%-20s", b.Name)
r := testing.Benchmark(b.F)
fmt.Printf("%10d %10.0f req/s\n", r.N, float64(r.N)/r.T.Seconds())
}
}
db.tearDown()
}
// Dialect is a type of dialect of databases.
@ -2059,37 +1886,14 @@ const (
// DB provide context for the tests
type TestDB struct {
testing.TB
*testing.T
*sql.DB
dialect Dialect
once sync.Once
tempFilename string
dialect Dialect
once sync.Once
}
var db *TestDB
func initializeTestDB(t testing.TB) {
tempFilename := TempFilename(t)
d, err := sql.Open("sqlite3", tempFilename+"?_busy_timeout=99999")
if err != nil {
os.Remove(tempFilename)
t.Fatal(err)
}
db = &TestDB{t, d, SQLITE, sync.Once{}, tempFilename}
}
func freeTestDB() {
err := db.DB.Close()
if err != nil {
panic(err)
}
err = os.Remove(db.tempFilename)
if err != nil {
panic(err)
}
}
// the following tables will be created and dropped during the test
var testTables = []string{"foo", "bar", "t", "bench"}
@ -2101,7 +1905,6 @@ var tests = []testing.InternalTest{
{Name: "TestManyQueryRow", F: testManyQueryRow},
{Name: "TestTxQuery", F: testTxQuery},
{Name: "TestPreparedStmt", F: testPreparedStmt},
{Name: "TestExecEmptyQuery", F: testExecEmptyQuery},
}
var benchmarks = []testing.InternalBenchmark{
@ -2432,25 +2235,6 @@ func testPreparedStmt(t *testing.T) {
wg.Wait()
}
// testEmptyQuery is test for validating the API in case of empty query
func testExecEmptyQuery(t *testing.T) {
db.tearDown()
res, err := db.Exec(" -- this is just a comment ")
if err != nil {
t.Fatalf("empty query err: %v", err)
}
_, err = res.LastInsertId()
if err != nil {
t.Fatalf("LastInsertId returned an error: %v", err)
}
_, err = res.RowsAffected()
if err != nil {
t.Fatalf("RowsAffected returned an error: %v", err)
}
}
// Benchmarks need to use panic() since b.Error errors are lost when
// running via testing.Benchmark() I would like to run these via go
// test -bench but calling Benchmark() from a benchmark test

View File

@ -9,7 +9,7 @@ package sqlite3
/*
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif

View File

@ -1,4 +1,5 @@
// 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.
@ -6,16 +7,15 @@ package sqlite3
/*
#ifndef USE_LIBSQLITE3
#include "sqlite3-binding.h"
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
*/
import "C"
import (
"database/sql"
"reflect"
"strings"
"time"
)
// ColumnTypeDatabaseTypeName implement RowsColumnTypeDatabaseTypeName.
@ -40,69 +40,23 @@ func (rc *SQLiteRows) ColumnTypeNullable(i int) (nullable, ok bool) {
// ColumnTypeScanType implement RowsColumnTypeScanType.
func (rc *SQLiteRows) ColumnTypeScanType(i int) reflect.Type {
//ct := C.sqlite3_column_type(rc.s.s, C.int(i)) // Always returns 5
return scanType(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))))
}
const (
SQLITE_INTEGER = iota
SQLITE_TEXT
SQLITE_BLOB
SQLITE_REAL
SQLITE_NUMERIC
SQLITE_TIME
SQLITE_BOOL
SQLITE_NULL
)
func scanType(cdt string) reflect.Type {
t := strings.ToUpper(cdt)
i := databaseTypeConvSqlite(t)
switch i {
case SQLITE_INTEGER:
return reflect.TypeOf(sql.NullInt64{})
case SQLITE_TEXT:
return reflect.TypeOf(sql.NullString{})
case SQLITE_BLOB:
return reflect.TypeOf(sql.RawBytes{})
case SQLITE_REAL:
return reflect.TypeOf(sql.NullFloat64{})
case SQLITE_NUMERIC:
return reflect.TypeOf(sql.NullFloat64{})
case SQLITE_BOOL:
return reflect.TypeOf(sql.NullBool{})
case SQLITE_TIME:
return reflect.TypeOf(sql.NullTime{})
}
return reflect.TypeOf(new(interface{}))
}
func databaseTypeConvSqlite(t string) int {
if strings.Contains(t, "INT") {
return SQLITE_INTEGER
}
if t == "CLOB" || t == "TEXT" ||
strings.Contains(t, "CHAR") {
return SQLITE_TEXT
}
if t == "BLOB" {
return SQLITE_BLOB
}
if t == "REAL" || t == "FLOAT" ||
strings.Contains(t, "DOUBLE") {
return SQLITE_REAL
}
if t == "DATE" || t == "DATETIME" ||
t == "TIMESTAMP" {
return SQLITE_TIME
}
if t == "NUMERIC" ||
strings.Contains(t, "DECIMAL") {
return SQLITE_NUMERIC
}
if t == "BOOLEAN" {
return SQLITE_BOOL
}
return SQLITE_NULL
switch C.sqlite3_column_type(rc.s.s, C.int(i)) {
case C.SQLITE_INTEGER:
switch C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))) {
case "timestamp", "datetime", "date":
return reflect.TypeOf(time.Time{})
case "boolean":
return reflect.TypeOf(false)
}
return reflect.TypeOf(int64(0))
case C.SQLITE_FLOAT:
return reflect.TypeOf(float64(0))
case C.SQLITE_BLOB:
return reflect.SliceOf(reflect.TypeOf(byte(0)))
case C.SQLITE_NULL:
return reflect.TypeOf(nil)
case C.SQLITE_TEXT:
return reflect.TypeOf("")
}
return reflect.SliceOf(reflect.TypeOf(byte(0)))
}

View File

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

View File

@ -12,6 +12,7 @@ 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

@ -19,10 +19,6 @@
#ifndef SQLITE3EXT_H
#define SQLITE3EXT_H
#include "sqlite3-binding.h"
#ifdef __clang__
#define assert(condition) ((void)0)
#endif
/*
** The following structure holds pointers to all of the SQLite API
@ -340,28 +336,6 @@ struct sqlite3_api_routines {
int,const char**);
void (*free_filename)(char*);
sqlite3_file *(*database_file_object)(const char*);
/* Version 3.34.0 and later */
int (*txn_state)(sqlite3*,const char*);
/* Version 3.36.1 and later */
sqlite3_int64 (*changes64)(sqlite3*);
sqlite3_int64 (*total_changes64)(sqlite3*);
/* Version 3.37.0 and later */
int (*autovacuum_pages)(sqlite3*,
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
void*, void(*)(void*));
/* Version 3.38.0 and later */
int (*error_offset)(sqlite3*);
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
int (*vtab_distinct)(sqlite3_index_info*);
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);
};
/*
@ -666,26 +640,6 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_create_filename sqlite3_api->create_filename
#define sqlite3_free_filename sqlite3_api->free_filename
#define sqlite3_database_file_object sqlite3_api->database_file_object
/* Version 3.34.0 and later */
#define sqlite3_txn_state sqlite3_api->txn_state
/* Version 3.36.1 and later */
#define sqlite3_changes64 sqlite3_api->changes64
#define sqlite3_total_changes64 sqlite3_api->total_changes64
/* Version 3.37.0 and later */
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
/* Version 3.38.0 and later */
#define sqlite3_error_offset sqlite3_api->error_offset
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
#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)

View File

@ -1,5 +0,0 @@
module github.com/mattn/go-sqlite3/upgrade
go 1.16
require github.com/PuerkitoBio/goquery v1.7.1 // indirect

View File

@ -1,12 +0,0 @@
github.com/PuerkitoBio/goquery v1.7.1 h1:oE+T06D+1T7LNrn91B4aERsRIeCLJ/oPSa6xB9FPnz4=
github.com/PuerkitoBio/goquery v1.7.1/go.mod h1:XY0pP4kfraEmmV1O7Uf6XyjoslwsneBbgeDjLYuN8xY=
github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE=
github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -1,5 +1,5 @@
//go:build !cgo && upgrade && ignore
// +build !cgo,upgrade,ignore
// +build !cgo
// +build upgrade,ignore
package main
@ -98,25 +98,16 @@ func mergeFile(src string, dst string) error {
func main() {
fmt.Println("Go-SQLite3 Upgrade Tool")
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
if filepath.Base(wd) != "upgrade" {
log.Printf("Current directory is %q but should run in upgrade directory", wd)
os.Exit(1)
}
// Download Amalgamation
_, amalgamation, err := download("sqlite-amalgamation-")
if err != nil {
log.Fatalf("Failed to download: sqlite-amalgamation; %s", err)
fmt.Println("Failed to download: sqlite-amalgamation; %s", err)
}
// Download Source
_, source, err := download("sqlite-src-")
if err != nil {
log.Fatalf("Failed to download: sqlite-src; %s", err)
fmt.Println("Failed to download: sqlite-src; %s", err)
}
// Create Amalgamation Zip Reader
@ -136,11 +127,11 @@ func main() {
var f *os.File
switch path.Base(zf.Name) {
case "sqlite3.c":
f, err = os.Create("../sqlite3-binding.c")
f, err = os.Create("sqlite3-binding.c")
case "sqlite3.h":
f, err = os.Create("../sqlite3-binding.h")
f, err = os.Create("sqlite3-binding.h")
case "sqlite3ext.h":
f, err = os.Create("../sqlite3ext.h")
f, err = os.Create("sqlite3ext.h")
default:
continue
}
@ -162,11 +153,7 @@ func main() {
for scanner.Scan() {
text := scanner.Text()
if text == `#include "sqlite3.h"` {
text = `#include "sqlite3-binding.h"
#ifdef __clang__
#define assert(condition) ((void)0)
#endif
`
text = `#include "sqlite3-binding.h"`
}
_, err = fmt.Fprintln(f, text)
if err != nil {
@ -195,9 +182,9 @@ func main() {
var f *os.File
switch path.Base(zf.Name) {
case "userauth.c":
f, err = os.Create("../userauth.c")
f, err = os.Create("userauth.c")
case "sqlite3userauth.h":
f, err = os.Create("../userauth.h")
f, err = os.Create("userauth.h")
default:
continue
}
@ -220,10 +207,10 @@ func main() {
}
// Merge SQLite User Authentication into amalgamation
if err := mergeFile("../userauth.c", "../sqlite3-binding.c"); err != nil {
if err := mergeFile("userauth.c", "sqlite3-binding.c"); err != nil {
log.Fatal(err)
}
if err := mergeFile("../userauth.h", "../sqlite3-binding.h"); err != nil {
if err := mergeFile("userauth.h", "sqlite3-binding.h"); err != nil {
log.Fatal(err)
}