mirror of
https://github.com/isar/rusqlite.git
synced 2025-03-31 19:12:58 +08:00
Merge remote-tracking branch 'jgallagher/master' into doc-rs
This commit is contained in:
commit
99ef10d599
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
libsqlite3-sys/sqlite3 linguist-vendored
|
50
.travis.yml
50
.travis.yml
@ -1,14 +1,42 @@
|
||||
language: rust
|
||||
sudo: false
|
||||
|
||||
language: rust
|
||||
|
||||
rust:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rust: nightly
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- llvm-toolchain-precise-3.9
|
||||
- ubuntu-toolchain-r-test
|
||||
packages: # recommanded versions for rust-bindgen
|
||||
- llvm-3.9-dev
|
||||
- libclang-3.9-dev
|
||||
|
||||
env: # specify the clang path for rust-bindgen
|
||||
- LIBCLANG_PATH=/usr/lib/llvm-3.9/lib
|
||||
|
||||
script:
|
||||
- cargo build
|
||||
- cargo test
|
||||
- cargo test --features backup
|
||||
- cargo test --features blob
|
||||
- cargo test --features load_extension
|
||||
- cargo test --features trace
|
||||
- cargo test --features functions
|
||||
- cargo test --features chrono
|
||||
- cargo test --features serde_json
|
||||
- cargo test --features "backup blob chrono functions load_extension serde_json trace"
|
||||
- cargo build
|
||||
- cargo build --features bundled
|
||||
- cargo test
|
||||
- cargo test --features backup
|
||||
- cargo test --features blob
|
||||
- cargo test --features functions
|
||||
- cargo test --features limits
|
||||
- cargo test --features load_extension
|
||||
- cargo test --features trace
|
||||
- cargo test --features chrono
|
||||
- cargo test --features serde_json
|
||||
- cargo test --features bundled
|
||||
- cargo test --features "backup blob chrono functions limits load_extension serde_json trace"
|
||||
- cargo test --features "backup blob chrono functions limits load_extension serde_json trace buildtime_bindgen"
|
||||
- cargo test --features "backup blob chrono functions limits load_extension serde_json trace bundled"
|
||||
- cargo test --features "backup blob chrono functions limits load_extension serde_json trace bundled buildtime_bindgen"
|
||||
|
@ -14,3 +14,11 @@ rusqlite contributors
|
||||
* [Ronald Kinard](https://github.com/Furyhunter)
|
||||
* [maciejkula](https://github.com/maciejkula)
|
||||
* [Xidorn Quan](https://github.com/upsuper)
|
||||
* [Chip Collier](https://github.com/photex)
|
||||
* [Omar Ferrer](https://github.com/chamakits)
|
||||
* [Lee Jenkins](https://github.com/reddraggone9)
|
||||
* [miedzinski](https://github.com/miedzinski)
|
||||
* [aidanhs](https://github.com/aidanhs)
|
||||
* [Steven Fackler](https://github.com/sfackler)
|
||||
* [Davide Aversa](https://github.com/THeK3nger)
|
||||
* [mcgoo](https://github.com/mcgoo)
|
||||
|
37
Cargo.toml
37
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rusqlite"
|
||||
version = "0.7.3"
|
||||
version = "0.12.0"
|
||||
authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
|
||||
description = "Ergonomic wrapper for SQLite"
|
||||
repository = "https://github.com/jgallagher/rusqlite"
|
||||
@ -8,6 +8,11 @@ documentation = "http://docs.rs/rusqlite/"
|
||||
readme = "README.md"
|
||||
keywords = ["sqlite", "database", "ffi"]
|
||||
license = "MIT"
|
||||
categories = ["database"]
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "jgallagher/rusqlite" }
|
||||
appveyor = { repository = "jgallagher/rusqlite" }
|
||||
|
||||
[lib]
|
||||
name = "rusqlite"
|
||||
@ -15,27 +20,29 @@ name = "rusqlite"
|
||||
[features]
|
||||
default = ["backup", "blob", "chrono", "functions", "load_extension", "serde_json", "trace"]
|
||||
load_extension = []
|
||||
backup = []
|
||||
blob = []
|
||||
functions = []
|
||||
trace = []
|
||||
backup = ["libsqlite3-sys/min_sqlite_version_3_6_11"]
|
||||
blob = ["libsqlite3-sys/min_sqlite_version_3_7_4"]
|
||||
functions = ["libsqlite3-sys/min_sqlite_version_3_7_3"]
|
||||
trace = ["libsqlite3-sys/min_sqlite_version_3_6_23"]
|
||||
bundled = ["libsqlite3-sys/bundled"]
|
||||
buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"]
|
||||
limits = []
|
||||
|
||||
[dependencies]
|
||||
time = "~0.1.0"
|
||||
bitflags = "0.7"
|
||||
lru-cache = "0.0.7"
|
||||
libc = "~0.2"
|
||||
chrono = { version = "~0.2", optional = true }
|
||||
serde_json = { version = "0.6", optional = true }
|
||||
time = "0.1.0"
|
||||
bitflags = "0.9"
|
||||
lru-cache = "0.1"
|
||||
chrono = { version = "0.3", optional = true }
|
||||
serde_json = { version = "1.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "~0.3.4"
|
||||
lazy_static = "~0.1"
|
||||
regex = "~0.1.41"
|
||||
tempdir = "0.3"
|
||||
lazy_static = "0.2"
|
||||
regex = "0.2"
|
||||
|
||||
[dependencies.libsqlite3-sys]
|
||||
path = "libsqlite3-sys"
|
||||
version = "0.5.0"
|
||||
version = "0.8"
|
||||
|
||||
[[test]]
|
||||
name = "config_log"
|
||||
|
82
Changelog.md
82
Changelog.md
@ -1,10 +1,90 @@
|
||||
# Version UPCOMING (...)
|
||||
# Version 0.12.0 (2017-05-29)
|
||||
|
||||
* Defines HAVE\_USLEEP when building with a bundled SQLite (#263).
|
||||
* Updates dependencies to their latest versions, particularly serde to 1.0.
|
||||
* Adds support for vcpkg on Windows.
|
||||
* Adds `ToSql` impls for `str` and `[u8]`.
|
||||
|
||||
# Version 0.11.0 (2017-04-06)
|
||||
|
||||
* Avoid publicly exporting SQLite constants multiple times from libsqlite3-sys.
|
||||
* Adds `FromSql` and `ToSql` impls for `isize`. Documents why `usize` and `u64` are not included.
|
||||
|
||||
# Version 0.10.1 (2017-03-03)
|
||||
|
||||
* Updates the `bundled` SQLite version to 3.17.0.
|
||||
* Changes the build process to no longer require `bindgen`. This should improve
|
||||
build times and no longer require a new-ish Clang. See the README for more
|
||||
details.
|
||||
|
||||
# Version 0.10.0 (2017-02-28)
|
||||
|
||||
* Re-export the `ErrorCode` enum from `libsqlite3-sys`.
|
||||
* Adds `version()` and `version_number()` functions for querying the version of SQLite in use.
|
||||
* Adds the `limits` feature, exposing `limit()` and `set_limit()` methods on `Connection`.
|
||||
* Updates to `libsqlite3-sys` 0.7.0, which runs rust-bindgen at build-time instead of assuming the
|
||||
precense of all expected SQLite constants and functions.
|
||||
* Clarifies supported SQLite versions. Running with SQLite older than 3.6.8 now panics, and
|
||||
some features will not compile unless a sufficiently-recent SQLite version is used. See
|
||||
the README for requirements of particular features.
|
||||
* When running with SQLite 3.6.x, rusqlite attempts to perform SQLite initialization. If it fails,
|
||||
rusqlite will panic since it cannot ensure the threading mode for SQLite. This check can by
|
||||
skipped by calling the unsafe function `rusqlite::bypass_sqlite_initialization()`. This is
|
||||
technically a breaking change but is unlikely to affect anyone in practice, since prior to this
|
||||
version the check that rusqlite was using would cause a segfault if linked against a SQLite
|
||||
older than 3.7.0.
|
||||
* rusqlite now performs a one-time check (prior to the first connection attempt) that the runtime
|
||||
SQLite version is at least as new as the SQLite version found at buildtime. This check can by
|
||||
skipped by calling the unsafe function `rusqlite::bypass_sqlite_version_check()`.
|
||||
* Removes the `libc` dependency in favor of using `std::os::raw`
|
||||
|
||||
# Version 0.9.5 (2017-01-26)
|
||||
|
||||
* Add impls of `Clone`, `Debug`, and `PartialEq` to `ToSqlOutput`.
|
||||
|
||||
# Version 0.9.4 (2017-01-25)
|
||||
|
||||
* Update dependencies.
|
||||
|
||||
# Version 0.9.3 (2017-01-23)
|
||||
|
||||
* Make `ToSqlOutput` itself implement `ToSql`.
|
||||
|
||||
# Version 0.9.2 (2017-01-22)
|
||||
|
||||
* Bugfix: The `FromSql` impl for `i32` now returns an error instead of
|
||||
truncating if the underlying SQLite value is out of `i32`'s range.
|
||||
* Added `FromSql` and `ToSql` impls for `i8`, `i16`, `u8`, `u16`, and `u32`.
|
||||
`i32` and `i64` already had impls. `u64` is omitted because their range
|
||||
cannot be represented by `i64`, which is the type we use to communicate with
|
||||
SQLite.
|
||||
|
||||
# Version 0.9.1 (2017-01-20)
|
||||
|
||||
* BREAKING CHANGE: `Connection::close()` now returns a `Result<(), (Connection, Error)>` instead
|
||||
of a `Result<(), Error>` so callers get the still-open connection back on failure.
|
||||
|
||||
# Version 0.8.0 (2016-12-31)
|
||||
|
||||
* BREAKING CHANGE: The `FromSql` trait has been redesigned. It now requires a single, safe
|
||||
method instead of the previous definition which required implementing one or two unsafe
|
||||
methods.
|
||||
* BREAKING CHANGE: The `ToSql` trait has been redesigned. It can now be implemented without
|
||||
`unsafe`, and implementors can choose to return either borrowed or owned results.
|
||||
* BREAKING CHANGE: The closure passed to `query_row`, `query_row_and_then`, `query_row_safe`,
|
||||
and `query_row_named` now expects a `&Row` instead of a `Row`. The vast majority of calls
|
||||
to these functions will probably not need to change; see
|
||||
https://github.com/jgallagher/rusqlite/pull/184.
|
||||
* BREAKING CHANGE: A few cases of the `Error` enum have sprouted additional information
|
||||
(e.g., `FromSqlConversionFailure` now also includes the column index and the type returned
|
||||
by SQLite).
|
||||
* Added `#[deprecated(since = "...", note = "...")]` flags (new in Rust 1.9 for libraries) to
|
||||
all deprecated APIs.
|
||||
* Added `query_row` convenience function to `Statement`.
|
||||
* Added `bundled` feature which will build SQLite from source instead of attempting to link
|
||||
against a SQLite that already exists on the system.
|
||||
* Fixed a bug where using cached prepared statements resulted in attempting to close a connection
|
||||
failing with `DatabaseBusy`; see https://github.com/jgallagher/rusqlite/issues/186.
|
||||
|
||||
# Version 0.7.3 (2016-06-01)
|
||||
|
||||
|
81
README.md
81
README.md
@ -1,7 +1,7 @@
|
||||
# Rusqlite
|
||||
|
||||
[](https://travis-ci.org/jgallagher/rusqlite)
|
||||
[](https://ci.appveyor.com/project/jgallagher/rusqlite)
|
||||
[](https://ci.appveyor.com/project/jgallagher/rusqlite) [](https://crates.io/crates/rusqlite)
|
||||
|
||||
Rusqlite is an ergonomic wrapper for using SQLite from Rust. It attempts to expose
|
||||
an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). View the full
|
||||
@ -38,11 +38,11 @@ fn main() {
|
||||
data: None
|
||||
};
|
||||
conn.execute("INSERT INTO person (name, time_created, data)
|
||||
VALUES ($1, $2, $3)",
|
||||
VALUES (?1, ?2, ?3)",
|
||||
&[&me.name, &me.time_created, &me.data]).unwrap();
|
||||
|
||||
let mut stmt = conn.prepare("SELECT id, name, time_created, data FROM person").unwrap();
|
||||
let mut person_iter = stmt.query_map(&[], |row| {
|
||||
let person_iter = stmt.query_map(&[], |row| {
|
||||
Person {
|
||||
id: row.get(0),
|
||||
name: row.get(1),
|
||||
@ -57,6 +57,12 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
### Supported SQLite Versions
|
||||
|
||||
The base `rusqlite` package supports SQLite version 3.6.8 or newer. If you need
|
||||
support for older versions, please file an issue. Some cargo features require a
|
||||
newer SQLite version; see details below.
|
||||
|
||||
### Optional Features
|
||||
|
||||
Rusqlite provides several features that are behind [Cargo
|
||||
@ -65,19 +71,82 @@ features](http://doc.crates.io/manifest.html#the-features-section). They are:
|
||||
* [`load_extension`](http://jgallagher.github.io/rusqlite/rusqlite/struct.LoadExtensionGuard.html)
|
||||
allows loading dynamic library-based SQLite extensions.
|
||||
* [`backup`](http://jgallagher.github.io/rusqlite/rusqlite/backup/index.html)
|
||||
allows use of SQLite's online backup API.
|
||||
allows use of SQLite's online backup API. Note: This feature requires SQLite 3.6.11 or later.
|
||||
* [`functions`](http://jgallagher.github.io/rusqlite/rusqlite/functions/index.html)
|
||||
allows you to load Rust closures into SQLite connections for use in queries.
|
||||
Note: This feature requires SQLite 3.7.3 or later.
|
||||
* [`trace`](http://jgallagher.github.io/rusqlite/rusqlite/trace/index.html)
|
||||
allows hooks into SQLite's tracing and profiling APIs.
|
||||
allows hooks into SQLite's tracing and profiling APIs. Note: This feature
|
||||
requires SQLite 3.6.23 or later.
|
||||
* [`blob`](http://jgallagher.github.io/rusqlite/rusqlite/blob/index.html)
|
||||
gives `std::io::{Read, Write, Seek}` access to SQL BLOBs.
|
||||
gives `std::io::{Read, Write, Seek}` access to SQL BLOBs. Note: This feature
|
||||
requires SQLite 3.7.4 or later.
|
||||
* [`limits`](http://jgallagher.github.io/rusqlite/rusqlite/struct.Connection.html#method.limit)
|
||||
allows you to set and retrieve SQLite's per connection limits.
|
||||
* `chrono` implements [`FromSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.FromSql.html)
|
||||
and [`ToSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.ToSql.html) for various
|
||||
types from the [`chrono` crate](https://crates.io/crates/chrono).
|
||||
* `serde_json` implements [`FromSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.FromSql.html)
|
||||
and [`ToSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.ToSql.html) for the
|
||||
`Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json).
|
||||
* `bundled` uses a bundled version of sqlite3. This is a good option for cases where linking to sqlite3 is complicated, such as Windows.
|
||||
|
||||
## Notes on building rusqlite and libsqlite3-sys
|
||||
|
||||
`libsqlite3-sys` is a separate crate from `rusqlite` that provides the Rust
|
||||
declarations for SQLite's C API. By default, `libsqlite3-sys` attempts to find a SQLite library that already exists on your system using pkg-config, or a
|
||||
[Vcpkg](https://github.com/Microsoft/vcpkg) installation for MSVC ABI builds.
|
||||
|
||||
You can adjust this behavior in a number of ways:
|
||||
|
||||
* If you use the `bundled` feature, `libsqlite3-sys` will use the
|
||||
[gcc](https://crates.io/crates/gcc) crate to compile SQLite from source and
|
||||
link against that. This source is embedded in the `libsqlite3-sys` crate and
|
||||
is currently SQLite 3.17.0 (as of `rusqlite` 0.10.1 / `libsqlite3-sys`
|
||||
0.7.1). This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file:
|
||||
```
|
||||
[dependencies.rusqlite]
|
||||
version = "0.11.0"
|
||||
features = ["bundled"]
|
||||
```
|
||||
* You can set the `SQLITE3_LIB_DIR` to point to directory containing the SQLite
|
||||
library.
|
||||
* Installing the sqlite3 development packages will usually be all that is required, but
|
||||
the build helpers for [pkg-config](https://github.com/alexcrichton/pkg-config-rs)
|
||||
and [vcpkg](https://github.com/mcgoo/vcpkg-rs) have some additional configuration
|
||||
options. The default when using vcpkg is to dynamically link. `vcpkg install sqlite3:x64-windows` will install the required library.
|
||||
|
||||
### Binding generation
|
||||
|
||||
We use [bindgen](https://crates.io/crates/bindgen) to generate the Rust
|
||||
declarations from SQLite's C header file. `bindgen`
|
||||
[recommends](https://github.com/servo/rust-bindgen#library-usage-with-buildrs)
|
||||
running this as part of the build process of libraries that used this. We tried
|
||||
this briefly (`rusqlite` 0.10.0, specifically), but it had some annoyances:
|
||||
|
||||
* The build time for `libsqlite3-sys` (and therefore `rusqlite`) increased
|
||||
dramatically.
|
||||
* Running `bindgen` requires a relatively-recent version of Clang, which many
|
||||
systems do not have installed by default.
|
||||
* Running `bindgen` also requires the SQLite header file to be present.
|
||||
|
||||
As of `rusqlite` 0.10.1, we avoid running `bindgen` at build-time by shipping
|
||||
pregenerated bindings for several versions of SQLite. When compiling
|
||||
`rusqlite`, we use your selected Cargo features to pick the bindings for the
|
||||
minimum SQLite version that supports your chosen features. If you are using
|
||||
`libsqlite3-sys` directly, you can use the same features to choose which
|
||||
pregenerated bindings are chosen:
|
||||
|
||||
* `min_sqlite_version_3_6_8` - SQLite 3.6.8 bindings (this is the default)
|
||||
* `min_sqlite_version_3_6_11` - SQLite 3.6.11 bindings
|
||||
* `min_sqlite_version_3_6_23` - SQLite 3.6.23 bindings
|
||||
* `min_sqlite_version_3_7_3` - SQLite 3.7.3 bindings
|
||||
* `min_sqlite_version_3_7_4` - SQLite 3.7.4 bindings
|
||||
|
||||
If you use the `bundled` feature, you will get pregenerated bindings for the
|
||||
bundled version of SQLite. If you need other specific pregenerated binding
|
||||
versions, please file an issue. If you want to run `bindgen` at buildtime to
|
||||
produce your own bindings, use the `buildtime_bindgen` Cargo feature.
|
||||
|
||||
## Author
|
||||
|
||||
|
32
appveyor.yml
32
appveyor.yml
@ -1,6 +1,12 @@
|
||||
environment:
|
||||
TARGET: 1.9.0-x86_64-pc-windows-gnu
|
||||
MSYS2_BITS: 64
|
||||
matrix:
|
||||
- TARGET: 1.15.0-x86_64-pc-windows-gnu
|
||||
MSYS2_BITS: 64
|
||||
- TARGET: 1.15.0-x86_64-pc-windows-msvc
|
||||
VCPKG_DEFAULT_TRIPLET: x64-windows
|
||||
- TARGET: nightly-x86_64-pc-windows-msvc
|
||||
VCPKG_DEFAULT_TRIPLET: x64-windows-static
|
||||
RUSTFLAGS: -Ctarget-feature=+crt-static
|
||||
install:
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe"
|
||||
- rust-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||
@ -8,15 +14,29 @@ install:
|
||||
- if defined MSYS2_BITS set PATH=%PATH%;C:\msys64\mingw%MSYS2_BITS%\bin
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
- ps: Start-FileDownload 'http://sqlite.org/2016/sqlite-dll-win64-x64-3100200.zip'
|
||||
- cmd: 7z e sqlite-dll-win64-x64-3100200.zip -y > nul
|
||||
- SET SQLITE3_LIB_DIR=%APPVEYOR_BUILD_FOLDER%
|
||||
- ps: Start-FileDownload 'https://sqlite.org/2017/sqlite-dll-win64-x64-3170000.zip' # download SQLite dll (useful only when the `bundled` feature is not set)
|
||||
- if not defined VCPKG_DEFAULT_TRIPLET 7z e sqlite-dll-win64-x64-3170000.zip -y > nul
|
||||
- ps: Start-FileDownload 'https://sqlite.org/2017/sqlite-amalgamation-3170000.zip' # download SQLite headers (useful only when the `bundled` feature is not set)
|
||||
- if not defined VCPKG_DEFAULT_TRIPLET 7z e sqlite-amalgamation-3170000.zip -y > nul
|
||||
- if not defined VCPKG_DEFAULT_TRIPLET SET SQLITE3_LIB_DIR=%APPVEYOR_BUILD_FOLDER% # specify where the SQLite dll has been downloaded (useful only when the `bundled` feature is not set)
|
||||
- if not defined VCPKG_DEFAULT_TRIPLET SET SQLITE3_INCLUDE_DIR=%APPVEYOR_BUILD_FOLDER% # specify where the SQLite headers have been downloaded (useful only when the `bundled` feature is not set)
|
||||
# install vcpkg and the sqlite3 package
|
||||
- if defined VCPKG_DEFAULT_TRIPLET git clone https://github.com/Microsoft/vcpkg c:\projects\vcpkg
|
||||
- if defined VCPKG_DEFAULT_TRIPLET c:\projects\vcpkg\bootstrap-vcpkg.bat
|
||||
- if defined VCPKG_DEFAULT_TRIPLET set VCPKG_ROOT=c:\projects\vcpkg
|
||||
- if defined VCPKG_DEFAULT_TRIPLET %VCPKG_ROOT%\vcpkg.exe install sqlite3
|
||||
- if defined VCPKG_DEFAULT_TRIPLET appveyor DownloadFile http://releases.llvm.org/4.0.0/LLVM-4.0.0-win64.exe
|
||||
- if defined VCPKG_DEFAULT_TRIPLET LLVM-4.0.0-win64.exe /S
|
||||
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --lib --verbose
|
||||
- cargo test --lib --features "backup blob chrono functions load_extension serde_json trace"
|
||||
- cargo test --lib --verbose --features bundled
|
||||
- cargo test --lib --features "backup blob chrono functions limits load_extension serde_json trace"
|
||||
- cargo test --lib --features "backup blob chrono functions limits load_extension serde_json trace buildtime_bindgen"
|
||||
- cargo test --lib --features "backup blob chrono functions limits load_extension serde_json trace bundled"
|
||||
- cargo test --lib --features "backup blob chrono functions limits load_extension serde_json trace bundled buildtime_bindgen"
|
||||
|
||||
cache:
|
||||
- C:\Users\appveyor\.cargo
|
||||
|
@ -4,12 +4,12 @@ extern crate test;
|
||||
extern crate rusqlite;
|
||||
|
||||
use rusqlite::Connection;
|
||||
use rusqlite::cache::StatementCache;
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_no_cache(b: &mut Bencher) {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.set_prepared_statement_cache_capacity(0);
|
||||
let sql = "SELECT 1, 'test', 3.14 UNION SELECT 2, 'exp', 2.71";
|
||||
b.iter(|| db.prepare(sql).unwrap());
|
||||
}
|
||||
@ -17,7 +17,6 @@ fn bench_no_cache(b: &mut Bencher) {
|
||||
#[bench]
|
||||
fn bench_cache(b: &mut Bencher) {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let cache = StatementCache::new(&db, 15);
|
||||
let sql = "SELECT 1, 'test', 3.14 UNION SELECT 2, 'exp', 2.71";
|
||||
b.iter(|| cache.get(sql).unwrap());
|
||||
b.iter(|| db.prepare_cached(sql).unwrap());
|
||||
}
|
||||
|
5
build.rs
5
build.rs
@ -1,5 +0,0 @@
|
||||
extern crate pkg_config;
|
||||
|
||||
fn main() {
|
||||
pkg_config::find_library("sqlite3").unwrap();
|
||||
}
|
@ -1,15 +1,30 @@
|
||||
[package]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.5.0"
|
||||
version = "0.8.1"
|
||||
authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
|
||||
repository = "https://github.com/jgallagher/rusqlite"
|
||||
description = "Native bindings to the libsqlite3 library"
|
||||
license = "MIT"
|
||||
links = "sqlite3"
|
||||
build = "build.rs"
|
||||
keywords = ["sqlite", "database", "ffi"]
|
||||
categories = ["database", "external-ffi-bindings"]
|
||||
|
||||
[features]
|
||||
default = ["min_sqlite_version_3_6_8"]
|
||||
bundled = ["gcc"]
|
||||
buildtime_bindgen = ["bindgen", "pkg-config", "vcpkg"]
|
||||
min_sqlite_version_3_6_8 = ["pkg-config", "vcpkg"]
|
||||
min_sqlite_version_3_6_11 = ["pkg-config", "vcpkg"]
|
||||
min_sqlite_version_3_6_23 = ["pkg-config", "vcpkg"]
|
||||
min_sqlite_version_3_7_3 = ["pkg-config", "vcpkg"]
|
||||
min_sqlite_version_3_7_4 = ["pkg-config", "vcpkg"]
|
||||
min_sqlite_version_3_7_16 = ["pkg-config", "vcpkg"]
|
||||
|
||||
[build-dependencies]
|
||||
pkg-config = "~0.3"
|
||||
bindgen = { version = "0.23", optional = true }
|
||||
pkg-config = { version = "0.3", optional = true }
|
||||
gcc = { version = "0.3", optional = true }
|
||||
|
||||
[dependencies]
|
||||
libc = "~0.2"
|
||||
[target.'cfg(target_env = "msvc")'.build-dependencies]
|
||||
vcpkg = { version = "0.2", optional = true }
|
||||
|
1863
libsqlite3-sys/bindgen-bindings/bindgen_3.6.11.rs
Normal file
1863
libsqlite3-sys/bindgen-bindings/bindgen_3.6.11.rs
Normal file
File diff suppressed because it is too large
Load Diff
1900
libsqlite3-sys/bindgen-bindings/bindgen_3.6.23.rs
Normal file
1900
libsqlite3-sys/bindgen-bindings/bindgen_3.6.23.rs
Normal file
File diff suppressed because it is too large
Load Diff
1835
libsqlite3-sys/bindgen-bindings/bindgen_3.6.8.rs
Normal file
1835
libsqlite3-sys/bindgen-bindings/bindgen_3.6.8.rs
Normal file
File diff suppressed because it is too large
Load Diff
2297
libsqlite3-sys/bindgen-bindings/bindgen_3.7.16.rs
Normal file
2297
libsqlite3-sys/bindgen-bindings/bindgen_3.7.16.rs
Normal file
File diff suppressed because it is too large
Load Diff
2050
libsqlite3-sys/bindgen-bindings/bindgen_3.7.3.rs
Normal file
2050
libsqlite3-sys/bindgen-bindings/bindgen_3.7.3.rs
Normal file
File diff suppressed because it is too large
Load Diff
2059
libsqlite3-sys/bindgen-bindings/bindgen_3.7.4.rs
Normal file
2059
libsqlite3-sys/bindgen-bindings/bindgen_3.7.4.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,26 +1,221 @@
|
||||
extern crate pkg_config;
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
|
||||
fn main() {
|
||||
// Allow users to specify where to find SQLite.
|
||||
let lib_dir = match env::var("SQLITE3_LIB_DIR") {
|
||||
Ok(dir) => dir,
|
||||
Err(_) => {
|
||||
// See if pkg-config can do everything for us.
|
||||
if pkg_config::find_library("sqlite3").is_ok() {
|
||||
return
|
||||
}
|
||||
|
||||
// Try to fall back to /usr/lib if pkg-config failed.
|
||||
match fs::metadata("/usr/lib") {
|
||||
Ok(ref attr) if attr.is_dir() => "/usr/lib".to_owned(),
|
||||
_ => panic!("Could not find sqlite3. Try setting SQLITE3_LIB_DIR."),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
println!("cargo:rustc-link-lib=sqlite3");
|
||||
println!("cargo:rustc-link-search={}", lib_dir);
|
||||
build::main();
|
||||
}
|
||||
|
||||
#[cfg(feature = "bundled")]
|
||||
mod build {
|
||||
extern crate gcc;
|
||||
use std::{env, fs};
|
||||
use std::path::Path;
|
||||
|
||||
pub fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let out_path = Path::new(&out_dir).join("bindgen.rs");
|
||||
fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
|
||||
.expect("Could not copy bindings to output directory");
|
||||
|
||||
gcc::Config::new()
|
||||
.file("sqlite3/sqlite3.c")
|
||||
.flag("-DSQLITE_CORE")
|
||||
.flag("-DSQLITE_DEFAULT_FOREIGN_KEYS=1")
|
||||
.flag("-DSQLITE_ENABLE_API_ARMOR")
|
||||
.flag("-DSQLITE_ENABLE_COLUMN_METADATA")
|
||||
.flag("-DSQLITE_ENABLE_DBSTAT_VTAB")
|
||||
.flag("-DSQLITE_ENABLE_FTS3")
|
||||
.flag("-DSQLITE_ENABLE_FTS3_PARENTHESIS")
|
||||
.flag("-DSQLITE_ENABLE_FTS5")
|
||||
.flag("-DSQLITE_ENABLE_JSON1")
|
||||
.flag("-DSQLITE_ENABLE_LOAD_EXTENSION=1")
|
||||
.flag("-DSQLITE_ENABLE_MEMORY_MANAGEMENT")
|
||||
.flag("-DSQLITE_ENABLE_RTREE")
|
||||
.flag("-DSQLITE_ENABLE_STAT2")
|
||||
.flag("-DSQLITE_ENABLE_STAT4")
|
||||
.flag("-DSQLITE_HAVE_ISNAN")
|
||||
.flag("-DSQLITE_SOUNDEX")
|
||||
.flag("-DSQLITE_THREADSAFE=1")
|
||||
.flag("-DSQLITE_USE_URI")
|
||||
.flag("-DHAVE_USLEEP=1")
|
||||
.compile("libsqlite3.a");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "bundled"))]
|
||||
mod build {
|
||||
extern crate pkg_config;
|
||||
|
||||
#[cfg(all(feature = "vcpkg", target_env = "msvc"))]
|
||||
extern crate vcpkg;
|
||||
|
||||
use std::env;
|
||||
|
||||
pub enum HeaderLocation {
|
||||
FromEnvironment,
|
||||
Wrapper,
|
||||
FromPath(String),
|
||||
}
|
||||
|
||||
impl From<HeaderLocation> for String {
|
||||
fn from(header: HeaderLocation) -> String {
|
||||
match header {
|
||||
HeaderLocation::FromEnvironment => {
|
||||
let mut header = env::var("SQLITE3_INCLUDE_DIR")
|
||||
.expect("SQLITE3_INCLUDE_DIR must be set if SQLITE3_LIB_DIR is set");
|
||||
header.push_str("/sqlite3.h");
|
||||
header
|
||||
}
|
||||
HeaderLocation::Wrapper => "wrapper.h".into(),
|
||||
HeaderLocation::FromPath(path) => path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
let header = find_sqlite();
|
||||
bindings::write_to_out_dir(header);
|
||||
}
|
||||
|
||||
// Prints the necessary cargo link commands and returns the path to the header.
|
||||
fn find_sqlite() -> HeaderLocation {
|
||||
// Allow users to specify where to find SQLite.
|
||||
if let Ok(dir) = env::var("SQLITE3_LIB_DIR") {
|
||||
println!("cargo:rustc-link-lib=sqlite3");
|
||||
println!("cargo:rustc-link-search={}", dir);
|
||||
return HeaderLocation::FromEnvironment;
|
||||
}
|
||||
|
||||
if let Some(header) = try_vcpkg() {
|
||||
return header;
|
||||
}
|
||||
|
||||
// See if pkg-config can do everything for us.
|
||||
match pkg_config::Config::new().print_system_libs(false).probe("sqlite3") {
|
||||
Ok(mut lib) => {
|
||||
if let Some(mut header) = lib.include_paths.pop() {
|
||||
header.push("sqlite3.h");
|
||||
HeaderLocation::FromPath(header.to_string_lossy().into())
|
||||
} else {
|
||||
HeaderLocation::Wrapper
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// No env var set and pkg-config couldn't help; just output the link-lib
|
||||
// request and hope that the library exists on the system paths. We used to
|
||||
// output /usr/lib explicitly, but that can introduce other linking problems; see
|
||||
// https://github.com/jgallagher/rusqlite/issues/207.
|
||||
println!("cargo:rustc-link-lib=sqlite3");
|
||||
HeaderLocation::Wrapper
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "vcpkg", target_env = "msvc"))]
|
||||
fn try_vcpkg() -> Option<HeaderLocation> {
|
||||
// See if vcpkg can find it.
|
||||
if let Ok(mut lib) = vcpkg::Config::new().probe("sqlite3") {
|
||||
if let Some(mut header) = lib.include_paths.pop() {
|
||||
header.push("sqlite3.h");
|
||||
return Some(HeaderLocation::FromPath(header.to_string_lossy().into()));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "vcpkg", target_env = "msvc")))]
|
||||
fn try_vcpkg() -> Option<HeaderLocation> {
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "buildtime_bindgen"))]
|
||||
mod bindings {
|
||||
use super::HeaderLocation;
|
||||
|
||||
use std::{env, fs};
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[
|
||||
"bindgen-bindings/bindgen_3.6.8.rs",
|
||||
|
||||
#[cfg(feature = "min_sqlite_version_3_6_11")]
|
||||
"bindgen-bindings/bindgen_3.6.11.rs",
|
||||
|
||||
#[cfg(feature = "min_sqlite_version_3_6_23")]
|
||||
"bindgen-bindings/bindgen_3.6.23.rs",
|
||||
|
||||
#[cfg(feature = "min_sqlite_version_3_7_3")]
|
||||
"bindgen-bindings/bindgen_3.7.3.rs",
|
||||
|
||||
#[cfg(feature = "min_sqlite_version_3_7_4")]
|
||||
"bindgen-bindings/bindgen_3.7.4.rs",
|
||||
|
||||
#[cfg(feature = "min_sqlite_version_3_7_16")]
|
||||
"bindgen-bindings/bindgen_3.7.16.rs",
|
||||
];
|
||||
|
||||
pub fn write_to_out_dir(_header: HeaderLocation) {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let out_path = Path::new(&out_dir).join("bindgen.rs");
|
||||
let in_path = PREBUILT_BINDGEN_PATHS[PREBUILT_BINDGEN_PATHS.len() - 1];
|
||||
fs::copy(in_path, out_path).expect("Could not copy bindings to output directory");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "buildtime_bindgen")]
|
||||
mod bindings {
|
||||
extern crate bindgen;
|
||||
|
||||
use self::bindgen::callbacks::{ParseCallbacks, IntKind};
|
||||
use super::HeaderLocation;
|
||||
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SqliteTypeChooser;
|
||||
|
||||
impl ParseCallbacks for SqliteTypeChooser {
|
||||
fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> {
|
||||
if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
|
||||
Some(IntKind::I32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_to_out_dir(header: HeaderLocation) {
|
||||
let header: String = header.into();
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let mut output = Vec::new();
|
||||
bindgen::builder()
|
||||
.header(header.clone())
|
||||
.parse_callbacks(Box::new(SqliteTypeChooser))
|
||||
.generate()
|
||||
.expect(&format!("could not run bindgen on header {}", header))
|
||||
.write(Box::new(&mut output))
|
||||
.expect("could not write output of bindgen");
|
||||
let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!");
|
||||
|
||||
// rusqlite's functions feature ors in the SQLITE_DETERMINISTIC flag when it can. This flag
|
||||
// was added in SQLite 3.8.3, but oring it in in prior versions of SQLite is harmless. We
|
||||
// don't want to not build just because this flag is missing (e.g., if we're linking against
|
||||
// SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's output.
|
||||
if !output.contains("pub const SQLITE_DETERMINISTIC:") {
|
||||
output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
|
||||
}
|
||||
|
||||
let path = Path::new(&out_dir).join("bindgen.rs");
|
||||
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(path.clone())
|
||||
.expect(&format!("Could not write to {:?}", path));
|
||||
|
||||
file.write_all(output.as_bytes()).expect(&format!("Could not write to {:?}", path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2900
libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
Normal file
2900
libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
Normal file
File diff suppressed because it is too large
Load Diff
201402
libsqlite3-sys/sqlite3/sqlite3.c
Normal file
201402
libsqlite3-sys/sqlite3/sqlite3.c
Normal file
File diff suppressed because it is too large
Load Diff
10470
libsqlite3-sys/sqlite3/sqlite3.h
Normal file
10470
libsqlite3-sys/sqlite3/sqlite3.h
Normal file
File diff suppressed because it is too large
Load Diff
560
libsqlite3-sys/sqlite3/sqlite3ext.h
Normal file
560
libsqlite3-sys/sqlite3/sqlite3ext.h
Normal file
@ -0,0 +1,560 @@
|
||||
/*
|
||||
** 2006 June 7
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the SQLite interface for use by
|
||||
** shared libraries that want to be imported as extensions into
|
||||
** an SQLite instance. Shared libraries that intend to be loaded
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
*/
|
||||
#ifndef SQLITE3EXT_H
|
||||
#define SQLITE3EXT_H
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** The following structure holds pointers to all of the SQLite API
|
||||
** routines.
|
||||
**
|
||||
** WARNING: In order to maintain backwards compatibility, add new
|
||||
** interfaces to the end of this structure only. If you insert new
|
||||
** interfaces in the middle of this structure, then older different
|
||||
** versions of SQLite will not be able to load each other's shared
|
||||
** libraries!
|
||||
*/
|
||||
struct sqlite3_api_routines {
|
||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
||||
int (*aggregate_count)(sqlite3_context*);
|
||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
||||
int (*bind_null)(sqlite3_stmt*,int);
|
||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
||||
int (*busy_timeout)(sqlite3*,int ms);
|
||||
int (*changes)(sqlite3*);
|
||||
int (*close)(sqlite3*);
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const void*));
|
||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_count)(sqlite3_stmt*pStmt);
|
||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
||||
const char * (*column_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||
int (*complete)(const char*sql);
|
||||
int (*complete16)(const void*sql);
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||
int (*data_count)(sqlite3_stmt*pStmt);
|
||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||
int (*declare_vtab)(sqlite3*,const char*);
|
||||
int (*enable_shared_cache)(int);
|
||||
int (*errcode)(sqlite3*db);
|
||||
const char * (*errmsg)(sqlite3*);
|
||||
const void * (*errmsg16)(sqlite3*);
|
||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
||||
int (*expired)(sqlite3_stmt*);
|
||||
int (*finalize)(sqlite3_stmt*pStmt);
|
||||
void (*free)(void*);
|
||||
void (*free_table)(char**result);
|
||||
int (*get_autocommit)(sqlite3*);
|
||||
void * (*get_auxdata)(sqlite3_context*,int);
|
||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||
int (*global_recover)(void);
|
||||
void (*interruptx)(sqlite3*);
|
||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||
const char * (*libversion)(void);
|
||||
int (*libversion_number)(void);
|
||||
void *(*malloc)(int);
|
||||
char * (*mprintf)(const char*,...);
|
||||
int (*open)(const char*,sqlite3**);
|
||||
int (*open16)(const void*,sqlite3**);
|
||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
||||
void *(*realloc)(void*,int);
|
||||
int (*reset)(sqlite3_stmt*pStmt);
|
||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_double)(sqlite3_context*,double);
|
||||
void (*result_error)(sqlite3_context*,const char*,int);
|
||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
||||
void (*result_int)(sqlite3_context*,int);
|
||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
||||
void (*result_null)(sqlite3_context*);
|
||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||
const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*snprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||
char const**,char const**,int*,int*,int*);
|
||||
void (*thread_cleanup)(void);
|
||||
int (*total_changes)(sqlite3*);
|
||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
||||
sqlite_int64),void*);
|
||||
void * (*user_data)(sqlite3_context*);
|
||||
const void * (*value_blob)(sqlite3_value*);
|
||||
int (*value_bytes)(sqlite3_value*);
|
||||
int (*value_bytes16)(sqlite3_value*);
|
||||
double (*value_double)(sqlite3_value*);
|
||||
int (*value_int)(sqlite3_value*);
|
||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
||||
int (*value_numeric_type)(sqlite3_value*);
|
||||
const unsigned char * (*value_text)(sqlite3_value*);
|
||||
const void * (*value_text16)(sqlite3_value*);
|
||||
const void * (*value_text16be)(sqlite3_value*);
|
||||
const void * (*value_text16le)(sqlite3_value*);
|
||||
int (*value_type)(sqlite3_value*);
|
||||
char *(*vmprintf)(const char*,va_list);
|
||||
/* Added ??? */
|
||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
||||
/* Added by 3.3.13 */
|
||||
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
int (*clear_bindings)(sqlite3_stmt*);
|
||||
/* Added by 3.4.1 */
|
||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
||||
void (*xDestroy)(void *));
|
||||
/* Added by 3.5.0 */
|
||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
||||
int (*blob_bytes)(sqlite3_blob*);
|
||||
int (*blob_close)(sqlite3_blob*);
|
||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
||||
int,sqlite3_blob**);
|
||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*),
|
||||
void(*)(void*));
|
||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
||||
sqlite3_int64 (*memory_highwater)(int);
|
||||
sqlite3_int64 (*memory_used)(void);
|
||||
sqlite3_mutex *(*mutex_alloc)(int);
|
||||
void (*mutex_enter)(sqlite3_mutex*);
|
||||
void (*mutex_free)(sqlite3_mutex*);
|
||||
void (*mutex_leave)(sqlite3_mutex*);
|
||||
int (*mutex_try)(sqlite3_mutex*);
|
||||
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
||||
int (*release_memory)(int);
|
||||
void (*result_error_nomem)(sqlite3_context*);
|
||||
void (*result_error_toobig)(sqlite3_context*);
|
||||
int (*sleep)(int);
|
||||
void (*soft_heap_limit)(int);
|
||||
sqlite3_vfs *(*vfs_find)(const char*);
|
||||
int (*vfs_register)(sqlite3_vfs*,int);
|
||||
int (*vfs_unregister)(sqlite3_vfs*);
|
||||
int (*xthreadsafe)(void);
|
||||
void (*result_zeroblob)(sqlite3_context*,int);
|
||||
void (*result_error_code)(sqlite3_context*,int);
|
||||
int (*test_control)(int, ...);
|
||||
void (*randomness)(int,void*);
|
||||
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
||||
int (*extended_result_codes)(sqlite3*,int);
|
||||
int (*limit)(sqlite3*,int,int);
|
||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
||||
const char *(*sql)(sqlite3_stmt*);
|
||||
int (*status)(int,int*,int*,int);
|
||||
int (*backup_finish)(sqlite3_backup*);
|
||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
||||
int (*backup_pagecount)(sqlite3_backup*);
|
||||
int (*backup_remaining)(sqlite3_backup*);
|
||||
int (*backup_step)(sqlite3_backup*,int);
|
||||
const char *(*compileoption_get)(int);
|
||||
int (*compileoption_used)(const char*);
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void(*xDestroy)(void*));
|
||||
int (*db_config)(sqlite3*,int,...);
|
||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||
int (*extended_errcode)(sqlite3*);
|
||||
void (*log)(int,const char*,...);
|
||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
||||
const char *(*sourceid)(void);
|
||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
||||
int (*strnicmp)(const char*,const char*,int);
|
||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
||||
int (*vtab_config)(sqlite3*,int op,...);
|
||||
int (*vtab_on_conflict)(sqlite3*);
|
||||
/* Version 3.7.16 and later */
|
||||
int (*close_v2)(sqlite3*);
|
||||
const char *(*db_filename)(sqlite3*,const char*);
|
||||
int (*db_readonly)(sqlite3*,const char*);
|
||||
int (*db_release_memory)(sqlite3*);
|
||||
const char *(*errstr)(int);
|
||||
int (*stmt_busy)(sqlite3_stmt*);
|
||||
int (*stmt_readonly)(sqlite3_stmt*);
|
||||
int (*stricmp)(const char*,const char*);
|
||||
int (*uri_boolean)(const char*,const char*,int);
|
||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||
const char *(*uri_parameter)(const char*,const char*);
|
||||
char *(*vsnprintf)(int,char*,const char*,va_list);
|
||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||
/* Version 3.8.7 and later */
|
||||
int (*auto_extension)(void(*)(void));
|
||||
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
|
||||
void(*)(void*),unsigned char);
|
||||
int (*cancel_auto_extension)(void(*)(void));
|
||||
int (*load_extension)(sqlite3*,const char*,const char*,char**);
|
||||
void *(*malloc64)(sqlite3_uint64);
|
||||
sqlite3_uint64 (*msize)(void*);
|
||||
void *(*realloc64)(void*,sqlite3_uint64);
|
||||
void (*reset_auto_extension)(void);
|
||||
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
|
||||
void(*)(void*), unsigned char);
|
||||
int (*strglob)(const char*,const char*);
|
||||
/* Version 3.8.11 and later */
|
||||
sqlite3_value *(*value_dup)(const sqlite3_value*);
|
||||
void (*value_free)(sqlite3_value*);
|
||||
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
|
||||
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
|
||||
/* Version 3.9.0 and later */
|
||||
unsigned int (*value_subtype)(sqlite3_value*);
|
||||
void (*result_subtype)(sqlite3_context*,unsigned int);
|
||||
/* Version 3.10.0 and later */
|
||||
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
|
||||
int (*strlike)(const char*,const char*,unsigned int);
|
||||
int (*db_cacheflush)(sqlite3*);
|
||||
/* Version 3.12.0 and later */
|
||||
int (*system_errno)(sqlite3*);
|
||||
/* Version 3.14.0 and later */
|
||||
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
||||
char *(*expanded_sql)(sqlite3_stmt*);
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the function signature used for all extension entry points. It
|
||||
** is also defined in the file "loadext.c".
|
||||
*/
|
||||
typedef int (*sqlite3_loadext_entry)(
|
||||
sqlite3 *db, /* Handle to the database. */
|
||||
char **pzErrMsg, /* Used to set error string on failure. */
|
||||
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
|
||||
);
|
||||
|
||||
/*
|
||||
** The following macros redefine the API routines so that they are
|
||||
** redirected through the global sqlite3_api structure.
|
||||
**
|
||||
** This header file is also used by the loadext.c source file
|
||||
** (part of the main SQLite library - not an extension) so that
|
||||
** it can get access to the sqlite3_api_routines structure
|
||||
** definition. But the main library does not want to redefine
|
||||
** the API. So the redefinition macros are only valid if the
|
||||
** SQLITE_CORE macros is undefined.
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||
#endif
|
||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
||||
#define sqlite3_changes sqlite3_api->changes
|
||||
#define sqlite3_close sqlite3_api->close
|
||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
||||
#define sqlite3_column_count sqlite3_api->column_count
|
||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
||||
#define sqlite3_column_double sqlite3_api->column_double
|
||||
#define sqlite3_column_int sqlite3_api->column_int
|
||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
||||
#define sqlite3_column_name sqlite3_api->column_name
|
||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
||||
#define sqlite3_column_text sqlite3_api->column_text
|
||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
||||
#define sqlite3_column_type sqlite3_api->column_type
|
||||
#define sqlite3_column_value sqlite3_api->column_value
|
||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||||
#define sqlite3_complete sqlite3_api->complete
|
||||
#define sqlite3_complete16 sqlite3_api->complete16
|
||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||||
#define sqlite3_create_function sqlite3_api->create_function
|
||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||||
#define sqlite3_create_module sqlite3_api->create_module
|
||||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||||
#define sqlite3_data_count sqlite3_api->data_count
|
||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
||||
#define sqlite3_errcode sqlite3_api->errcode
|
||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||
#define sqlite3_exec sqlite3_api->exec
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_expired sqlite3_api->expired
|
||||
#endif
|
||||
#define sqlite3_finalize sqlite3_api->finalize
|
||||
#define sqlite3_free sqlite3_api->free
|
||||
#define sqlite3_free_table sqlite3_api->free_table
|
||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||
#define sqlite3_get_table sqlite3_api->get_table
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#endif
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||
#define sqlite3_malloc sqlite3_api->malloc
|
||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
||||
#define sqlite3_open sqlite3_api->open
|
||||
#define sqlite3_open16 sqlite3_api->open16
|
||||
#define sqlite3_prepare sqlite3_api->prepare
|
||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_profile sqlite3_api->profile
|
||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
||||
#define sqlite3_realloc sqlite3_api->realloc
|
||||
#define sqlite3_reset sqlite3_api->reset
|
||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
||||
#define sqlite3_result_double sqlite3_api->result_double
|
||||
#define sqlite3_result_error sqlite3_api->result_error
|
||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
||||
#define sqlite3_result_int sqlite3_api->result_int
|
||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
||||
#define sqlite3_result_null sqlite3_api->result_null
|
||||
#define sqlite3_result_text sqlite3_api->result_text
|
||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
||||
#define sqlite3_result_value sqlite3_api->result_value
|
||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||
#define sqlite3_snprintf sqlite3_api->snprintf
|
||||
#define sqlite3_step sqlite3_api->step
|
||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||
#define sqlite3_trace sqlite3_api->trace
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||
#endif
|
||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||
#define sqlite3_user_data sqlite3_api->user_data
|
||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
||||
#define sqlite3_value_double sqlite3_api->value_double
|
||||
#define sqlite3_value_int sqlite3_api->value_int
|
||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
||||
#define sqlite3_value_text sqlite3_api->value_text
|
||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
||||
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
||||
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
||||
#define sqlite3_blob_close sqlite3_api->blob_close
|
||||
#define sqlite3_blob_open sqlite3_api->blob_open
|
||||
#define sqlite3_blob_read sqlite3_api->blob_read
|
||||
#define sqlite3_blob_write sqlite3_api->blob_write
|
||||
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
||||
#define sqlite3_file_control sqlite3_api->file_control
|
||||
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
||||
#define sqlite3_memory_used sqlite3_api->memory_used
|
||||
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
||||
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
||||
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
||||
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
||||
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
||||
#define sqlite3_open_v2 sqlite3_api->open_v2
|
||||
#define sqlite3_release_memory sqlite3_api->release_memory
|
||||
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
||||
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
||||
#define sqlite3_sleep sqlite3_api->sleep
|
||||
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
||||
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
||||
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
||||
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
||||
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
||||
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
||||
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
||||
#define sqlite3_test_control sqlite3_api->test_control
|
||||
#define sqlite3_randomness sqlite3_api->randomness
|
||||
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
||||
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
||||
#define sqlite3_limit sqlite3_api->limit
|
||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
||||
#define sqlite3_sql sqlite3_api->sql
|
||||
#define sqlite3_status sqlite3_api->status
|
||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
||||
#define sqlite3_db_config sqlite3_api->db_config
|
||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
||||
#define sqlite3_db_status sqlite3_api->db_status
|
||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
||||
#define sqlite3_log sqlite3_api->log
|
||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
||||
/* Version 3.7.16 and later */
|
||||
#define sqlite3_close_v2 sqlite3_api->close_v2
|
||||
#define sqlite3_db_filename sqlite3_api->db_filename
|
||||
#define sqlite3_db_readonly sqlite3_api->db_readonly
|
||||
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
|
||||
#define sqlite3_errstr sqlite3_api->errstr
|
||||
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
|
||||
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
|
||||
#define sqlite3_stricmp sqlite3_api->stricmp
|
||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
|
||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||
/* Version 3.8.7 and later */
|
||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
||||
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
|
||||
#define sqlite3_bind_text64 sqlite3_api->bind_text64
|
||||
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
|
||||
#define sqlite3_load_extension sqlite3_api->load_extension
|
||||
#define sqlite3_malloc64 sqlite3_api->malloc64
|
||||
#define sqlite3_msize sqlite3_api->msize
|
||||
#define sqlite3_realloc64 sqlite3_api->realloc64
|
||||
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
|
||||
#define sqlite3_result_blob64 sqlite3_api->result_blob64
|
||||
#define sqlite3_result_text64 sqlite3_api->result_text64
|
||||
#define sqlite3_strglob sqlite3_api->strglob
|
||||
/* Version 3.8.11 and later */
|
||||
#define sqlite3_value_dup sqlite3_api->value_dup
|
||||
#define sqlite3_value_free sqlite3_api->value_free
|
||||
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
|
||||
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
|
||||
/* Version 3.9.0 and later */
|
||||
#define sqlite3_value_subtype sqlite3_api->value_subtype
|
||||
#define sqlite3_result_subtype sqlite3_api->result_subtype
|
||||
/* Version 3.10.0 and later */
|
||||
#define sqlite3_status64 sqlite3_api->status64
|
||||
#define sqlite3_strlike sqlite3_api->strlike
|
||||
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
||||
/* Version 3.12.0 and later */
|
||||
#define sqlite3_system_errno sqlite3_api->system_errno
|
||||
/* Version 3.14.0 and later */
|
||||
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
||||
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
/* This case when the file really is being compiled as a loadable
|
||||
** extension */
|
||||
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
|
||||
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
|
||||
# define SQLITE_EXTENSION_INIT3 \
|
||||
extern const sqlite3_api_routines *sqlite3_api;
|
||||
#else
|
||||
/* This case when the file is being statically linked into the
|
||||
** application */
|
||||
# define SQLITE_EXTENSION_INIT1 /*no-op*/
|
||||
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
|
||||
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE3EXT_H */
|
File diff suppressed because it is too large
Load Diff
@ -1,32 +1,57 @@
|
||||
use libc::c_int;
|
||||
use std::os::raw::c_int;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
/// Error Codes
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ErrorCode {
|
||||
/// Internal logic error in SQLite
|
||||
InternalMalfunction,
|
||||
/// Access permission denied
|
||||
PermissionDenied,
|
||||
/// Callback routine requested an abort
|
||||
OperationAborted,
|
||||
/// The database file is locked
|
||||
DatabaseBusy,
|
||||
/// A table in the database is locked
|
||||
DatabaseLocked,
|
||||
/// A malloc() failed
|
||||
OutOfMemory,
|
||||
/// Attempt to write a readonly database
|
||||
ReadOnly,
|
||||
/// Operation terminated by sqlite3_interrupt()
|
||||
OperationInterrupted,
|
||||
/// Some kind of disk I/O error occurred
|
||||
SystemIOFailure,
|
||||
/// The database disk image is malformed
|
||||
DatabaseCorrupt,
|
||||
/// Unknown opcode in sqlite3_file_control()
|
||||
NotFound,
|
||||
/// Insertion failed because database is full
|
||||
DiskFull,
|
||||
/// Unable to open the database file
|
||||
CannotOpen,
|
||||
/// Database lock protocol error
|
||||
FileLockingProtocolFailed,
|
||||
/// The database schema changed
|
||||
SchemaChanged,
|
||||
/// String or BLOB exceeds size limit
|
||||
TooBig,
|
||||
/// Abort due to constraint violation
|
||||
ConstraintViolation,
|
||||
/// Data type mismatch
|
||||
TypeMismatch,
|
||||
/// Library used incorrectly
|
||||
APIMisuse,
|
||||
/// Uses OS features not supported on host
|
||||
NoLargeFileSupport,
|
||||
/// Authorization denied
|
||||
AuthorizationForStatementDenied,
|
||||
/// 2nd parameter to sqlite3_bind out of range
|
||||
ParameterOutOfRange,
|
||||
/// File opened that is not a database file
|
||||
NotADatabase,
|
||||
/// SQL error or missing database
|
||||
Unknown,
|
||||
}
|
||||
|
||||
@ -39,29 +64,29 @@ pub struct Error {
|
||||
impl Error {
|
||||
pub fn new(result_code: c_int) -> Error {
|
||||
let code = match result_code & 0xff {
|
||||
SQLITE_INTERNAL => ErrorCode::InternalMalfunction,
|
||||
SQLITE_PERM => ErrorCode::PermissionDenied,
|
||||
SQLITE_ABORT => ErrorCode::OperationAborted,
|
||||
SQLITE_BUSY => ErrorCode::DatabaseBusy,
|
||||
SQLITE_LOCKED => ErrorCode::DatabaseLocked,
|
||||
SQLITE_NOMEM => ErrorCode::OutOfMemory,
|
||||
SQLITE_READONLY => ErrorCode::ReadOnly,
|
||||
SQLITE_INTERRUPT => ErrorCode::OperationInterrupted,
|
||||
SQLITE_IOERR => ErrorCode::SystemIOFailure,
|
||||
SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt,
|
||||
SQLITE_NOTFOUND => ErrorCode::NotFound,
|
||||
SQLITE_FULL => ErrorCode::DiskFull,
|
||||
SQLITE_CANTOPEN => ErrorCode::CannotOpen,
|
||||
SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed,
|
||||
SQLITE_SCHEMA => ErrorCode::SchemaChanged,
|
||||
SQLITE_TOOBIG => ErrorCode::TooBig,
|
||||
SQLITE_CONSTRAINT=> ErrorCode::ConstraintViolation,
|
||||
SQLITE_MISMATCH => ErrorCode::TypeMismatch,
|
||||
SQLITE_MISUSE => ErrorCode::APIMisuse,
|
||||
SQLITE_NOLFS => ErrorCode::NoLargeFileSupport,
|
||||
SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied,
|
||||
SQLITE_RANGE => ErrorCode::ParameterOutOfRange,
|
||||
SQLITE_NOTADB => ErrorCode::NotADatabase,
|
||||
super::SQLITE_INTERNAL => ErrorCode::InternalMalfunction,
|
||||
super::SQLITE_PERM => ErrorCode::PermissionDenied,
|
||||
super::SQLITE_ABORT => ErrorCode::OperationAborted,
|
||||
super::SQLITE_BUSY => ErrorCode::DatabaseBusy,
|
||||
super::SQLITE_LOCKED => ErrorCode::DatabaseLocked,
|
||||
super::SQLITE_NOMEM => ErrorCode::OutOfMemory,
|
||||
super::SQLITE_READONLY => ErrorCode::ReadOnly,
|
||||
super::SQLITE_INTERRUPT => ErrorCode::OperationInterrupted,
|
||||
super::SQLITE_IOERR => ErrorCode::SystemIOFailure,
|
||||
super::SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt,
|
||||
super::SQLITE_NOTFOUND => ErrorCode::NotFound,
|
||||
super::SQLITE_FULL => ErrorCode::DiskFull,
|
||||
super::SQLITE_CANTOPEN => ErrorCode::CannotOpen,
|
||||
super::SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed,
|
||||
super::SQLITE_SCHEMA => ErrorCode::SchemaChanged,
|
||||
super::SQLITE_TOOBIG => ErrorCode::TooBig,
|
||||
super::SQLITE_CONSTRAINT=> ErrorCode::ConstraintViolation,
|
||||
super::SQLITE_MISMATCH => ErrorCode::TypeMismatch,
|
||||
super::SQLITE_MISUSE => ErrorCode::APIMisuse,
|
||||
super::SQLITE_NOLFS => ErrorCode::NoLargeFileSupport,
|
||||
super::SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied,
|
||||
super::SQLITE_RANGE => ErrorCode::ParameterOutOfRange,
|
||||
super::SQLITE_NOTADB => ErrorCode::NotADatabase,
|
||||
_ => ErrorCode::Unknown,
|
||||
};
|
||||
|
||||
@ -85,147 +110,104 @@ impl error::Error for Error {
|
||||
}
|
||||
|
||||
// Result codes.
|
||||
// Note: These are not public because our bindgen bindings export whichever constants are present
|
||||
// in the current version of SQLite. We repeat them here so we don't have to worry about which
|
||||
// version of SQLite added which constants, and we only use them to implement code_to_str below.
|
||||
|
||||
pub const SQLITE_OK : c_int = 0;
|
||||
pub const SQLITE_ERROR : c_int = 1;
|
||||
pub const SQLITE_INTERNAL : c_int = 2;
|
||||
pub const SQLITE_PERM : c_int = 3;
|
||||
pub const SQLITE_ABORT : c_int = 4;
|
||||
pub const SQLITE_BUSY : c_int = 5;
|
||||
pub const SQLITE_LOCKED : c_int = 6;
|
||||
pub const SQLITE_NOMEM : c_int = 7;
|
||||
pub const SQLITE_READONLY : c_int = 8;
|
||||
pub const SQLITE_INTERRUPT : c_int = 9;
|
||||
pub const SQLITE_IOERR : c_int = 10;
|
||||
pub const SQLITE_CORRUPT : c_int = 11;
|
||||
pub const SQLITE_NOTFOUND : c_int = 12;
|
||||
pub const SQLITE_FULL : c_int = 13;
|
||||
pub const SQLITE_CANTOPEN : c_int = 14;
|
||||
pub const SQLITE_PROTOCOL : c_int = 15;
|
||||
pub const SQLITE_EMPTY : c_int = 16;
|
||||
pub const SQLITE_SCHEMA : c_int = 17;
|
||||
pub const SQLITE_TOOBIG : c_int = 18;
|
||||
pub const SQLITE_CONSTRAINT: c_int = 19;
|
||||
pub const SQLITE_MISMATCH : c_int = 20;
|
||||
pub const SQLITE_MISUSE : c_int = 21;
|
||||
pub const SQLITE_NOLFS : c_int = 22;
|
||||
pub const SQLITE_AUTH : c_int = 23;
|
||||
pub const SQLITE_FORMAT : c_int = 24;
|
||||
pub const SQLITE_RANGE : c_int = 25;
|
||||
pub const SQLITE_NOTADB : c_int = 26;
|
||||
pub const SQLITE_NOTICE : c_int = 27;
|
||||
pub const SQLITE_WARNING : c_int = 28;
|
||||
pub const SQLITE_ROW : c_int = 100;
|
||||
pub const SQLITE_DONE : c_int = 101;
|
||||
const SQLITE_NOTICE : c_int = 27;
|
||||
const SQLITE_WARNING : c_int = 28;
|
||||
|
||||
// Extended result codes.
|
||||
|
||||
pub const SQLITE_IOERR_READ : c_int = (SQLITE_IOERR | (1<<8));
|
||||
pub const SQLITE_IOERR_SHORT_READ : c_int = (SQLITE_IOERR | (2<<8));
|
||||
pub const SQLITE_IOERR_WRITE : c_int = (SQLITE_IOERR | (3<<8));
|
||||
pub const SQLITE_IOERR_FSYNC : c_int = (SQLITE_IOERR | (4<<8));
|
||||
pub const SQLITE_IOERR_DIR_FSYNC : c_int = (SQLITE_IOERR | (5<<8));
|
||||
pub const SQLITE_IOERR_TRUNCATE : c_int = (SQLITE_IOERR | (6<<8));
|
||||
pub const SQLITE_IOERR_FSTAT : c_int = (SQLITE_IOERR | (7<<8));
|
||||
pub const SQLITE_IOERR_UNLOCK : c_int = (SQLITE_IOERR | (8<<8));
|
||||
pub const SQLITE_IOERR_RDLOCK : c_int = (SQLITE_IOERR | (9<<8));
|
||||
pub const SQLITE_IOERR_DELETE : c_int = (SQLITE_IOERR | (10<<8));
|
||||
pub const SQLITE_IOERR_BLOCKED : c_int = (SQLITE_IOERR | (11<<8));
|
||||
pub const SQLITE_IOERR_NOMEM : c_int = (SQLITE_IOERR | (12<<8));
|
||||
pub const SQLITE_IOERR_ACCESS : c_int = (SQLITE_IOERR | (13<<8));
|
||||
pub const SQLITE_IOERR_CHECKRESERVEDLOCK : c_int = (SQLITE_IOERR | (14<<8));
|
||||
pub const SQLITE_IOERR_LOCK : c_int = (SQLITE_IOERR | (15<<8));
|
||||
pub const SQLITE_IOERR_CLOSE : c_int = (SQLITE_IOERR | (16<<8));
|
||||
pub const SQLITE_IOERR_DIR_CLOSE : c_int = (SQLITE_IOERR | (17<<8));
|
||||
pub const SQLITE_IOERR_SHMOPEN : c_int = (SQLITE_IOERR | (18<<8));
|
||||
pub const SQLITE_IOERR_SHMSIZE : c_int = (SQLITE_IOERR | (19<<8));
|
||||
pub const SQLITE_IOERR_SHMLOCK : c_int = (SQLITE_IOERR | (20<<8));
|
||||
pub const SQLITE_IOERR_SHMMAP : c_int = (SQLITE_IOERR | (21<<8));
|
||||
pub const SQLITE_IOERR_SEEK : c_int = (SQLITE_IOERR | (22<<8));
|
||||
pub const SQLITE_IOERR_DELETE_NOENT : c_int = (SQLITE_IOERR | (23<<8));
|
||||
pub const SQLITE_IOERR_MMAP : c_int = (SQLITE_IOERR | (24<<8));
|
||||
pub const SQLITE_IOERR_GETTEMPPATH : c_int = (SQLITE_IOERR | (25<<8));
|
||||
pub const SQLITE_IOERR_CONVPATH : c_int = (SQLITE_IOERR | (26<<8));
|
||||
pub const SQLITE_IOERR_VNODE : c_int = (SQLITE_IOERR | (27<<8));
|
||||
pub const SQLITE_LOCKED_SHAREDCACHE : c_int = (SQLITE_LOCKED | (1<<8));
|
||||
pub const SQLITE_BUSY_RECOVERY : c_int = (SQLITE_BUSY | (1<<8));
|
||||
pub const SQLITE_BUSY_SNAPSHOT : c_int = (SQLITE_BUSY | (2<<8));
|
||||
pub const SQLITE_CANTOPEN_NOTEMPDIR : c_int = (SQLITE_CANTOPEN | (1<<8));
|
||||
pub const SQLITE_CANTOPEN_ISDIR : c_int = (SQLITE_CANTOPEN | (2<<8));
|
||||
pub const SQLITE_CANTOPEN_FULLPATH : c_int = (SQLITE_CANTOPEN | (3<<8));
|
||||
pub const SQLITE_CANTOPEN_CONVPATH : c_int = (SQLITE_CANTOPEN | (4<<8));
|
||||
pub const SQLITE_CORRUPT_VTAB : c_int = (SQLITE_CORRUPT | (1<<8));
|
||||
pub const SQLITE_READONLY_RECOVERY : c_int = (SQLITE_READONLY | (1<<8));
|
||||
pub const SQLITE_READONLY_CANTLOCK : c_int = (SQLITE_READONLY | (2<<8));
|
||||
pub const SQLITE_READONLY_ROLLBACK : c_int = (SQLITE_READONLY | (3<<8));
|
||||
pub const SQLITE_READONLY_DBMOVED : c_int = (SQLITE_READONLY | (4<<8));
|
||||
pub const SQLITE_ABORT_ROLLBACK : c_int = (SQLITE_ABORT | (2<<8));
|
||||
pub const SQLITE_CONSTRAINT_CHECK : c_int = (SQLITE_CONSTRAINT | (1<<8));
|
||||
pub const SQLITE_CONSTRAINT_COMMITHOOK : c_int = (SQLITE_CONSTRAINT | (2<<8));
|
||||
pub const SQLITE_CONSTRAINT_FOREIGNKEY : c_int = (SQLITE_CONSTRAINT | (3<<8));
|
||||
pub const SQLITE_CONSTRAINT_FUNCTION : c_int = (SQLITE_CONSTRAINT | (4<<8));
|
||||
pub const SQLITE_CONSTRAINT_NOTNULL : c_int = (SQLITE_CONSTRAINT | (5<<8));
|
||||
pub const SQLITE_CONSTRAINT_PRIMARYKEY : c_int = (SQLITE_CONSTRAINT | (6<<8));
|
||||
pub const SQLITE_CONSTRAINT_TRIGGER : c_int = (SQLITE_CONSTRAINT | (7<<8));
|
||||
pub const SQLITE_CONSTRAINT_UNIQUE : c_int = (SQLITE_CONSTRAINT | (8<<8));
|
||||
pub const SQLITE_CONSTRAINT_VTAB : c_int = (SQLITE_CONSTRAINT | (9<<8));
|
||||
pub const SQLITE_CONSTRAINT_ROWID : c_int = (SQLITE_CONSTRAINT |(10<<8));
|
||||
pub const SQLITE_NOTICE_RECOVER_WAL : c_int = (SQLITE_NOTICE | (1<<8));
|
||||
pub const SQLITE_NOTICE_RECOVER_ROLLBACK : c_int = (SQLITE_NOTICE | (2<<8));
|
||||
pub const SQLITE_WARNING_AUTOINDEX : c_int = (SQLITE_WARNING | (1<<8));
|
||||
pub const SQLITE_AUTH_USER : c_int = (SQLITE_AUTH | (1<<8));
|
||||
const SQLITE_IOERR_SHMOPEN : c_int = (super::SQLITE_IOERR | (18<<8));
|
||||
const SQLITE_IOERR_SHMSIZE : c_int = (super::SQLITE_IOERR | (19<<8));
|
||||
const SQLITE_IOERR_SHMLOCK : c_int = (super::SQLITE_IOERR | (20<<8));
|
||||
const SQLITE_IOERR_SHMMAP : c_int = (super::SQLITE_IOERR | (21<<8));
|
||||
const SQLITE_IOERR_SEEK : c_int = (super::SQLITE_IOERR | (22<<8));
|
||||
const SQLITE_IOERR_DELETE_NOENT : c_int = (super::SQLITE_IOERR | (23<<8));
|
||||
const SQLITE_IOERR_MMAP : c_int = (super::SQLITE_IOERR | (24<<8));
|
||||
const SQLITE_IOERR_GETTEMPPATH : c_int = (super::SQLITE_IOERR | (25<<8));
|
||||
const SQLITE_IOERR_CONVPATH : c_int = (super::SQLITE_IOERR | (26<<8));
|
||||
const SQLITE_IOERR_VNODE : c_int = (super::SQLITE_IOERR | (27<<8));
|
||||
const SQLITE_LOCKED_SHAREDCACHE : c_int = (super::SQLITE_LOCKED | (1<<8));
|
||||
const SQLITE_BUSY_RECOVERY : c_int = (super::SQLITE_BUSY | (1<<8));
|
||||
const SQLITE_BUSY_SNAPSHOT : c_int = (super::SQLITE_BUSY | (2<<8));
|
||||
const SQLITE_CANTOPEN_NOTEMPDIR : c_int = (super::SQLITE_CANTOPEN | (1<<8));
|
||||
const SQLITE_CANTOPEN_ISDIR : c_int = (super::SQLITE_CANTOPEN | (2<<8));
|
||||
const SQLITE_CANTOPEN_FULLPATH : c_int = (super::SQLITE_CANTOPEN | (3<<8));
|
||||
const SQLITE_CANTOPEN_CONVPATH : c_int = (super::SQLITE_CANTOPEN | (4<<8));
|
||||
const SQLITE_CORRUPT_VTAB : c_int = (super::SQLITE_CORRUPT | (1<<8));
|
||||
const SQLITE_READONLY_RECOVERY : c_int = (super::SQLITE_READONLY | (1<<8));
|
||||
const SQLITE_READONLY_CANTLOCK : c_int = (super::SQLITE_READONLY | (2<<8));
|
||||
const SQLITE_READONLY_ROLLBACK : c_int = (super::SQLITE_READONLY | (3<<8));
|
||||
const SQLITE_READONLY_DBMOVED : c_int = (super::SQLITE_READONLY | (4<<8));
|
||||
const SQLITE_ABORT_ROLLBACK : c_int = (super::SQLITE_ABORT | (2<<8));
|
||||
const SQLITE_CONSTRAINT_CHECK : c_int = (super::SQLITE_CONSTRAINT | (1<<8));
|
||||
const SQLITE_CONSTRAINT_COMMITHOOK : c_int = (super::SQLITE_CONSTRAINT | (2<<8));
|
||||
const SQLITE_CONSTRAINT_FOREIGNKEY : c_int = (super::SQLITE_CONSTRAINT | (3<<8));
|
||||
const SQLITE_CONSTRAINT_FUNCTION : c_int = (super::SQLITE_CONSTRAINT | (4<<8));
|
||||
const SQLITE_CONSTRAINT_NOTNULL : c_int = (super::SQLITE_CONSTRAINT | (5<<8));
|
||||
const SQLITE_CONSTRAINT_PRIMARYKEY : c_int = (super::SQLITE_CONSTRAINT | (6<<8));
|
||||
const SQLITE_CONSTRAINT_TRIGGER : c_int = (super::SQLITE_CONSTRAINT | (7<<8));
|
||||
const SQLITE_CONSTRAINT_UNIQUE : c_int = (super::SQLITE_CONSTRAINT | (8<<8));
|
||||
const SQLITE_CONSTRAINT_VTAB : c_int = (super::SQLITE_CONSTRAINT | (9<<8));
|
||||
const SQLITE_CONSTRAINT_ROWID : c_int = (super::SQLITE_CONSTRAINT |(10<<8));
|
||||
const SQLITE_NOTICE_RECOVER_WAL : c_int = (SQLITE_NOTICE | (1<<8));
|
||||
const SQLITE_NOTICE_RECOVER_ROLLBACK : c_int = (SQLITE_NOTICE | (2<<8));
|
||||
const SQLITE_WARNING_AUTOINDEX : c_int = (SQLITE_WARNING | (1<<8));
|
||||
const SQLITE_AUTH_USER : c_int = (super::SQLITE_AUTH | (1<<8));
|
||||
|
||||
pub fn code_to_str(code: c_int) -> &'static str {
|
||||
match code {
|
||||
SQLITE_OK => "Successful result",
|
||||
SQLITE_ERROR => "SQL error or missing database",
|
||||
SQLITE_INTERNAL => "Internal logic error in SQLite",
|
||||
SQLITE_PERM => "Access permission denied",
|
||||
SQLITE_ABORT => "Callback routine requested an abort",
|
||||
SQLITE_BUSY => "The database file is locked",
|
||||
SQLITE_LOCKED => "A table in the database is locked",
|
||||
SQLITE_NOMEM => "A malloc() failed",
|
||||
SQLITE_READONLY => "Attempt to write a readonly database",
|
||||
SQLITE_INTERRUPT => "Operation terminated by sqlite3_interrupt()",
|
||||
SQLITE_IOERR => "Some kind of disk I/O error occurred",
|
||||
SQLITE_CORRUPT => "The database disk image is malformed",
|
||||
SQLITE_NOTFOUND => "Unknown opcode in sqlite3_file_control()",
|
||||
SQLITE_FULL => "Insertion failed because database is full",
|
||||
SQLITE_CANTOPEN => "Unable to open the database file",
|
||||
SQLITE_PROTOCOL => "Database lock protocol error",
|
||||
SQLITE_EMPTY => "Database is empty",
|
||||
SQLITE_SCHEMA => "The database schema changed",
|
||||
SQLITE_TOOBIG => "String or BLOB exceeds size limit",
|
||||
SQLITE_CONSTRAINT=> "Abort due to constraint violation",
|
||||
SQLITE_MISMATCH => "Data type mismatch",
|
||||
SQLITE_MISUSE => "Library used incorrectly",
|
||||
SQLITE_NOLFS => "Uses OS features not supported on host",
|
||||
SQLITE_AUTH => "Authorization denied",
|
||||
SQLITE_FORMAT => "Auxiliary database format error",
|
||||
SQLITE_RANGE => "2nd parameter to sqlite3_bind out of range",
|
||||
SQLITE_NOTADB => "File opened that is not a database file",
|
||||
super::SQLITE_OK => "Successful result",
|
||||
super::SQLITE_ERROR => "SQL error or missing database",
|
||||
super::SQLITE_INTERNAL => "Internal logic error in SQLite",
|
||||
super::SQLITE_PERM => "Access permission denied",
|
||||
super::SQLITE_ABORT => "Callback routine requested an abort",
|
||||
super::SQLITE_BUSY => "The database file is locked",
|
||||
super::SQLITE_LOCKED => "A table in the database is locked",
|
||||
super::SQLITE_NOMEM => "A malloc() failed",
|
||||
super::SQLITE_READONLY => "Attempt to write a readonly database",
|
||||
super::SQLITE_INTERRUPT => "Operation terminated by sqlite3_interrupt()",
|
||||
super::SQLITE_IOERR => "Some kind of disk I/O error occurred",
|
||||
super::SQLITE_CORRUPT => "The database disk image is malformed",
|
||||
super::SQLITE_NOTFOUND => "Unknown opcode in sqlite3_file_control()",
|
||||
super::SQLITE_FULL => "Insertion failed because database is full",
|
||||
super::SQLITE_CANTOPEN => "Unable to open the database file",
|
||||
super::SQLITE_PROTOCOL => "Database lock protocol error",
|
||||
super::SQLITE_EMPTY => "Database is empty",
|
||||
super::SQLITE_SCHEMA => "The database schema changed",
|
||||
super::SQLITE_TOOBIG => "String or BLOB exceeds size limit",
|
||||
super::SQLITE_CONSTRAINT=> "Abort due to constraint violation",
|
||||
super::SQLITE_MISMATCH => "Data type mismatch",
|
||||
super::SQLITE_MISUSE => "Library used incorrectly",
|
||||
super::SQLITE_NOLFS => "Uses OS features not supported on host",
|
||||
super::SQLITE_AUTH => "Authorization denied",
|
||||
super::SQLITE_FORMAT => "Auxiliary database format error",
|
||||
super::SQLITE_RANGE => "2nd parameter to sqlite3_bind out of range",
|
||||
super::SQLITE_NOTADB => "File opened that is not a database file",
|
||||
SQLITE_NOTICE => "Notifications from sqlite3_log()",
|
||||
SQLITE_WARNING => "Warnings from sqlite3_log()",
|
||||
SQLITE_ROW => "sqlite3_step() has another row ready",
|
||||
SQLITE_DONE => "sqlite3_step() has finished executing",
|
||||
super::SQLITE_ROW => "sqlite3_step() has another row ready",
|
||||
super::SQLITE_DONE => "sqlite3_step() has finished executing",
|
||||
|
||||
SQLITE_IOERR_READ => "Error reading from disk",
|
||||
SQLITE_IOERR_SHORT_READ => "Unable to obtain number of requested bytes (file truncated?)",
|
||||
SQLITE_IOERR_WRITE => "Error writing to disk",
|
||||
SQLITE_IOERR_FSYNC => "Error flushing data to persistent storage (fsync)",
|
||||
SQLITE_IOERR_DIR_FSYNC => "Error calling fsync on a directory",
|
||||
SQLITE_IOERR_TRUNCATE => "Error attempting to truncate file",
|
||||
SQLITE_IOERR_FSTAT => "Error invoking fstat to get file metadata",
|
||||
SQLITE_IOERR_UNLOCK => "I/O error within xUnlock of a VFS object",
|
||||
SQLITE_IOERR_RDLOCK => "I/O error within xLock of a VFS object (trying to obtain a read lock)",
|
||||
SQLITE_IOERR_DELETE => "I/O error within xDelete of a VFS object",
|
||||
SQLITE_IOERR_BLOCKED => "SQLITE_IOERR_BLOCKED", // no longer used
|
||||
SQLITE_IOERR_NOMEM => "Out of memory in I/O layer",
|
||||
SQLITE_IOERR_ACCESS => "I/O error within xAccess of a VFS object",
|
||||
SQLITE_IOERR_CHECKRESERVEDLOCK => "I/O error within then xCheckReservedLock method",
|
||||
SQLITE_IOERR_LOCK => "I/O error in the advisory file locking layer",
|
||||
SQLITE_IOERR_CLOSE => "I/O error within the xClose method",
|
||||
SQLITE_IOERR_DIR_CLOSE => "SQLITE_IOERR_DIR_CLOSE", // no longer used
|
||||
super::SQLITE_IOERR_READ => "Error reading from disk",
|
||||
super::SQLITE_IOERR_SHORT_READ => "Unable to obtain number of requested bytes (file truncated?)",
|
||||
super::SQLITE_IOERR_WRITE => "Error writing to disk",
|
||||
super::SQLITE_IOERR_FSYNC => "Error flushing data to persistent storage (fsync)",
|
||||
super::SQLITE_IOERR_DIR_FSYNC => "Error calling fsync on a directory",
|
||||
super::SQLITE_IOERR_TRUNCATE => "Error attempting to truncate file",
|
||||
super::SQLITE_IOERR_FSTAT => "Error invoking fstat to get file metadata",
|
||||
super::SQLITE_IOERR_UNLOCK => "I/O error within xUnlock of a VFS object",
|
||||
super::SQLITE_IOERR_RDLOCK => "I/O error within xLock of a VFS object (trying to obtain a read lock)",
|
||||
super::SQLITE_IOERR_DELETE => "I/O error within xDelete of a VFS object",
|
||||
super::SQLITE_IOERR_BLOCKED => "SQLITE_IOERR_BLOCKED", // no longer used
|
||||
super::SQLITE_IOERR_NOMEM => "Out of memory in I/O layer",
|
||||
super::SQLITE_IOERR_ACCESS => "I/O error within xAccess of a VFS object",
|
||||
super::SQLITE_IOERR_CHECKRESERVEDLOCK => "I/O error within then xCheckReservedLock method",
|
||||
super::SQLITE_IOERR_LOCK => "I/O error in the advisory file locking layer",
|
||||
super::SQLITE_IOERR_CLOSE => "I/O error within the xClose method",
|
||||
super::SQLITE_IOERR_DIR_CLOSE => "SQLITE_IOERR_DIR_CLOSE", // no longer used
|
||||
SQLITE_IOERR_SHMOPEN => "I/O error within the xShmMap method (trying to open a new shared-memory segment)",
|
||||
SQLITE_IOERR_SHMSIZE => "I/O error within the xShmMap method (trying to resize an existing shared-memory segment)",
|
||||
SQLITE_IOERR_SHMLOCK => "SQLITE_IOERR_SHMLOCK", // no longer used
|
||||
|
@ -1,25 +1,11 @@
|
||||
// bindgen.rs was created with bindgen 0.15.0 against sqlite3 3.8.10
|
||||
#![allow(non_snake_case, non_camel_case_types)]
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
pub use self::bindgen::*;
|
||||
pub use self::error::*;
|
||||
|
||||
use std::mem;
|
||||
use libc::c_int;
|
||||
|
||||
mod bindgen;
|
||||
mod error;
|
||||
|
||||
// SQLite datatype constants.
|
||||
pub const SQLITE_INTEGER : c_int = 1;
|
||||
pub const SQLITE_FLOAT : c_int = 2;
|
||||
pub const SQLITE_TEXT : c_int = 3;
|
||||
pub const SQLITE_BLOB : c_int = 4;
|
||||
pub const SQLITE_NULL : c_int = 5;
|
||||
|
||||
pub fn SQLITE_STATIC() -> sqlite3_destructor_type {
|
||||
Some(unsafe { mem::transmute(0isize) })
|
||||
}
|
||||
@ -28,6 +14,34 @@ pub fn SQLITE_TRANSIENT() -> sqlite3_destructor_type {
|
||||
Some(unsafe { mem::transmute(-1isize) })
|
||||
}
|
||||
|
||||
pub const SQLITE_CONFIG_LOG : c_int = 16;
|
||||
pub const SQLITE_UTF8 : c_int = 1;
|
||||
pub const SQLITE_DETERMINISTIC : c_int = 0x800;
|
||||
/// Run-Time Limit Categories
|
||||
#[repr(C)]
|
||||
pub enum Limit {
|
||||
/// The maximum size of any string or BLOB or table row, in bytes.
|
||||
SQLITE_LIMIT_LENGTH = SQLITE_LIMIT_LENGTH as isize,
|
||||
/// The maximum length of an SQL statement, in bytes.
|
||||
SQLITE_LIMIT_SQL_LENGTH = SQLITE_LIMIT_SQL_LENGTH as isize,
|
||||
/// The maximum number of columns in a table definition or in the result set of a SELECT
|
||||
/// or the maximum number of columns in an index or in an ORDER BY or GROUP BY clause.
|
||||
SQLITE_LIMIT_COLUMN = SQLITE_LIMIT_COLUMN as isize,
|
||||
/// The maximum depth of the parse tree on any expression.
|
||||
SQLITE_LIMIT_EXPR_DEPTH = SQLITE_LIMIT_EXPR_DEPTH as isize,
|
||||
/// The maximum number of terms in a compound SELECT statement.
|
||||
SQLITE_LIMIT_COMPOUND_SELECT = SQLITE_LIMIT_COMPOUND_SELECT as isize,
|
||||
/// The maximum number of instructions in a virtual machine program used to implement an SQL statement.
|
||||
SQLITE_LIMIT_VDBE_OP = SQLITE_LIMIT_VDBE_OP as isize,
|
||||
/// The maximum number of arguments on a function.
|
||||
SQLITE_LIMIT_FUNCTION_ARG = SQLITE_LIMIT_FUNCTION_ARG as isize,
|
||||
/// The maximum number of attached databases.
|
||||
SQLITE_LIMIT_ATTACHED = SQLITE_LIMIT_ATTACHED as isize,
|
||||
/// The maximum length of the pattern argument to the LIKE or GLOB operators.
|
||||
SQLITE_LIMIT_LIKE_PATTERN_LENGTH = SQLITE_LIMIT_LIKE_PATTERN_LENGTH as isize,
|
||||
/// The maximum index number of any parameter in an SQL statement.
|
||||
SQLITE_LIMIT_VARIABLE_NUMBER = SQLITE_LIMIT_VARIABLE_NUMBER as isize,
|
||||
/// The maximum depth of recursion for triggers.
|
||||
SQLITE_LIMIT_TRIGGER_DEPTH = 10,
|
||||
/// The maximum number of auxiliary worker threads that a single prepared statement may start.
|
||||
SQLITE_LIMIT_WORKER_THREADS = 11,
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/bindgen.rs"));
|
||||
|
1
libsqlite3-sys/wrapper.h
Normal file
1
libsqlite3-sys/wrapper.h
Normal file
@ -0,0 +1 @@
|
||||
#include "sqlite3.h"
|
@ -8,7 +8,7 @@ fi
|
||||
|
||||
cd $(git rev-parse --show-toplevel)
|
||||
rm -rf target/doc/
|
||||
rustup run nightly cargo doc --no-deps --features "backup blob chrono functions load_extension serde_json trace"
|
||||
rustup run nightly cargo doc --no-deps --features "backup blob chrono functions limits load_extension serde_json trace"
|
||||
echo '<meta http-equiv=refresh content=0;url=rusqlite/index.html>' > target/doc/index.html
|
||||
ghp-import target/doc
|
||||
git push origin gh-pages:gh-pages
|
||||
|
@ -18,7 +18,7 @@
|
||||
//! # use std::path::Path;
|
||||
//! # use std::time;
|
||||
//!
|
||||
//! fn backupDb<P: AsRef<Path>>(src: &Connection, dst: P, progress: fn(backup::Progress))
|
||||
//! fn backup_db<P: AsRef<Path>>(src: &Connection, dst: P, progress: fn(backup::Progress))
|
||||
//! -> Result<()> {
|
||||
//! let mut dst = try!(Connection::open(dst));
|
||||
//! let backup = try!(backup::Backup::new(src, &mut dst));
|
||||
@ -30,7 +30,7 @@ use std::marker::PhantomData;
|
||||
use std::path::Path;
|
||||
use std::ptr;
|
||||
|
||||
use libc::c_int;
|
||||
use std::os::raw::c_int;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
@ -206,10 +206,10 @@ impl<'a, 'b> Backup<'a, 'b> {
|
||||
};
|
||||
|
||||
Ok(Backup {
|
||||
phantom_from: PhantomData,
|
||||
phantom_to: PhantomData,
|
||||
b: b,
|
||||
})
|
||||
phantom_from: PhantomData,
|
||||
phantom_to: PhantomData,
|
||||
b: b,
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the progress of the backup as of the last call to `step`.
|
||||
|
57
src/blob.rs
57
src/blob.rs
@ -52,10 +52,9 @@ use std::io;
|
||||
use std::cmp::min;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use libc::c_int;
|
||||
|
||||
use super::ffi;
|
||||
use super::types::ToSql;
|
||||
use super::types::{ToSql, ToSqlOutput};
|
||||
use {Result, Connection, DatabaseName};
|
||||
|
||||
/// Handle to an open BLOB.
|
||||
@ -90,20 +89,17 @@ impl Connection {
|
||||
table.as_ptr(),
|
||||
column.as_ptr(),
|
||||
row,
|
||||
if read_only {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
},
|
||||
if read_only { 0 } else { 1 },
|
||||
&mut blob)
|
||||
};
|
||||
c.decode_result(rc).map(|_| {
|
||||
Blob {
|
||||
conn: self,
|
||||
blob: blob,
|
||||
pos: 0,
|
||||
}
|
||||
})
|
||||
c.decode_result(rc)
|
||||
.map(|_| {
|
||||
Blob {
|
||||
conn: self,
|
||||
blob: blob,
|
||||
pos: 0,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,9 +160,9 @@ impl<'conn> io::Read for Blob<'conn> {
|
||||
self.conn
|
||||
.decode_result(rc)
|
||||
.map(|_| {
|
||||
self.pos += n;
|
||||
n as usize
|
||||
})
|
||||
self.pos += n;
|
||||
n as usize
|
||||
})
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||
}
|
||||
}
|
||||
@ -194,9 +190,9 @@ impl<'conn> io::Write for Blob<'conn> {
|
||||
self.conn
|
||||
.decode_result(rc)
|
||||
.map(|_| {
|
||||
self.pos += n;
|
||||
n as usize
|
||||
})
|
||||
self.pos += n;
|
||||
n as usize
|
||||
})
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||
}
|
||||
|
||||
@ -244,9 +240,9 @@ impl<'conn> Drop for Blob<'conn> {
|
||||
pub struct ZeroBlob(pub i32);
|
||||
|
||||
impl ToSql for ZeroBlob {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut ffi::sqlite3_stmt, col: c_int) -> c_int {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
let ZeroBlob(length) = *self;
|
||||
ffi::sqlite3_bind_zeroblob(stmt, col, length)
|
||||
Ok(ToSqlOutput::ZeroBlob(length))
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,15 +267,17 @@ mod test {
|
||||
fn test_blob() {
|
||||
let (db, rowid) = db_with_test_blob().unwrap();
|
||||
|
||||
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap();
|
||||
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
.unwrap();
|
||||
assert_eq!(4, blob.write(b"Clob").unwrap());
|
||||
assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10
|
||||
assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10
|
||||
assert_eq!(0, blob.write(b"5678").unwrap()); // still cannot write past 10
|
||||
|
||||
blob.reopen(rowid).unwrap();
|
||||
blob.close().unwrap();
|
||||
|
||||
blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true).unwrap();
|
||||
blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true)
|
||||
.unwrap();
|
||||
let mut bytes = [0u8; 5];
|
||||
assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
|
||||
assert_eq!(&bytes, b"Clob5");
|
||||
@ -319,7 +317,8 @@ mod test {
|
||||
fn test_blob_in_bufreader() {
|
||||
let (db, rowid) = db_with_test_blob().unwrap();
|
||||
|
||||
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap();
|
||||
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
.unwrap();
|
||||
assert_eq!(8, blob.write(b"one\ntwo\n").unwrap());
|
||||
|
||||
blob.reopen(rowid).unwrap();
|
||||
@ -343,7 +342,8 @@ mod test {
|
||||
let (db, rowid) = db_with_test_blob().unwrap();
|
||||
|
||||
{
|
||||
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap();
|
||||
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
.unwrap();
|
||||
let mut writer = BufWriter::new(blob);
|
||||
|
||||
// trying to write too much and then flush should fail
|
||||
@ -362,7 +362,8 @@ mod test {
|
||||
}
|
||||
|
||||
{
|
||||
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false).unwrap();
|
||||
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
|
||||
.unwrap();
|
||||
let mut writer = BufWriter::new(blob);
|
||||
|
||||
// trying to write_all too much should fail
|
||||
|
43
src/cache.rs
43
src/cache.rs
@ -5,6 +5,7 @@ use std::ops::{Deref, DerefMut};
|
||||
use lru_cache::LruCache;
|
||||
use {Result, Connection, Statement};
|
||||
use raw_statement::RawStatement;
|
||||
use statement::StatementCrateImpl;
|
||||
|
||||
impl Connection {
|
||||
/// Prepare a SQL statement for execution, returning a previously prepared (but
|
||||
@ -44,6 +45,10 @@ impl Connection {
|
||||
pub fn set_prepared_statement_cache_capacity(&self, capacity: usize) {
|
||||
self.cache.set_capacity(capacity)
|
||||
}
|
||||
|
||||
pub fn flush_prepared_statement_cache(&self) {
|
||||
self.cache.flush()
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepared statements LRU cache.
|
||||
@ -133,6 +138,11 @@ impl StatementCache {
|
||||
let sql = String::from_utf8_lossy(stmt.sql().to_bytes()).to_string();
|
||||
cache.insert(sql, stmt);
|
||||
}
|
||||
|
||||
fn flush(&self) {
|
||||
let mut cache = self.0.borrow_mut();
|
||||
cache.clear()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -166,16 +176,14 @@ mod test {
|
||||
{
|
||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||
assert_eq!(0, cache.len());
|
||||
assert_eq!(0,
|
||||
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i32, i64>(0));
|
||||
assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
|
||||
}
|
||||
assert_eq!(1, cache.len());
|
||||
|
||||
{
|
||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||
assert_eq!(0, cache.len());
|
||||
assert_eq!(0,
|
||||
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i32, i64>(0));
|
||||
assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
|
||||
}
|
||||
assert_eq!(1, cache.len());
|
||||
|
||||
@ -193,8 +201,7 @@ mod test {
|
||||
{
|
||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||
assert_eq!(0, cache.len());
|
||||
assert_eq!(0,
|
||||
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i32, i64>(0));
|
||||
assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
|
||||
}
|
||||
assert_eq!(1, cache.len());
|
||||
|
||||
@ -204,8 +211,7 @@ mod test {
|
||||
{
|
||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||
assert_eq!(0, cache.len());
|
||||
assert_eq!(0,
|
||||
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i32, i64>(0));
|
||||
assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
|
||||
}
|
||||
assert_eq!(0, cache.len());
|
||||
|
||||
@ -213,8 +219,7 @@ mod test {
|
||||
{
|
||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||
assert_eq!(0, cache.len());
|
||||
assert_eq!(0,
|
||||
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i32, i64>(0));
|
||||
assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
|
||||
}
|
||||
assert_eq!(1, cache.len());
|
||||
}
|
||||
@ -228,8 +233,7 @@ mod test {
|
||||
{
|
||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||
assert_eq!(0, cache.len());
|
||||
assert_eq!(0,
|
||||
stmt.query(&[]).unwrap().get_expected_row().unwrap().get::<i32, i64>(0));
|
||||
assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
|
||||
stmt.discard();
|
||||
}
|
||||
assert_eq!(0, cache.len());
|
||||
@ -249,7 +253,11 @@ mod test {
|
||||
{
|
||||
let mut stmt = db.prepare_cached(sql).unwrap();
|
||||
assert_eq!(1i32,
|
||||
stmt.query_map(&[], |r| r.get(0)).unwrap().next().unwrap().unwrap());
|
||||
stmt.query_map::<i32, _>(&[], |r| r.get(0))
|
||||
.unwrap()
|
||||
.next()
|
||||
.unwrap()
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
db.execute_batch(r#"
|
||||
@ -268,4 +276,13 @@ mod test {
|
||||
.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_connection_close() {
|
||||
let conn = Connection::open_in_memory().unwrap();
|
||||
conn.prepare_cached("SELECT * FROM sqlite_master;")
|
||||
.unwrap();
|
||||
|
||||
conn.close().expect("connection not closed");
|
||||
}
|
||||
}
|
||||
|
@ -1,91 +0,0 @@
|
||||
use {Error, Result, Statement};
|
||||
use types::ToSql;
|
||||
|
||||
impl<'conn> Statement<'conn> {
|
||||
/// Execute an INSERT and return the ROWID.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function is a convenience wrapper around `execute()` intended for queries that
|
||||
/// insert a single item. It is possible to misuse this function in a way that it cannot
|
||||
/// detect, such as by calling it on a statement which _updates_ a single item rather than
|
||||
/// inserting one. Please don't do that.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if no row is inserted or many rows are inserted.
|
||||
pub fn insert(&mut self, params: &[&ToSql]) -> Result<i64> {
|
||||
let changes = try!(self.execute(params));
|
||||
match changes {
|
||||
1 => Ok(self.conn.last_insert_rowid()),
|
||||
_ => Err(Error::StatementChangedRows(changes)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if a query in the SQL statement it executes returns one or more rows
|
||||
/// and `false` if the SQL returns an empty set.
|
||||
pub fn exists(&mut self, params: &[&ToSql]) -> Result<bool> {
|
||||
let mut rows = try!(self.query(params));
|
||||
let exists = {
|
||||
match rows.next() {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
}
|
||||
};
|
||||
Ok(exists)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {Connection, Error};
|
||||
|
||||
#[test]
|
||||
fn test_insert() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)").unwrap();
|
||||
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)").unwrap();
|
||||
assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
|
||||
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
|
||||
match stmt.insert(&[&1i32]).unwrap_err() {
|
||||
Error::StatementChangedRows(0) => (),
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4").unwrap();
|
||||
match multi.insert(&[]).unwrap_err() {
|
||||
Error::StatementChangedRows(2) => (),
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_different_tables() {
|
||||
// Test for https://github.com/jgallagher/rusqlite/issues/171
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch(r"
|
||||
CREATE TABLE foo(x INTEGER);
|
||||
CREATE TABLE bar(x INTEGER);
|
||||
")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(db.prepare("INSERT INTO foo VALUES (10)").unwrap().insert(&[]).unwrap(),
|
||||
1);
|
||||
assert_eq!(db.prepare("INSERT INTO bar VALUES (10)").unwrap().insert(&[]).unwrap(),
|
||||
1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exists() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = "BEGIN;
|
||||
CREATE TABLE foo(x INTEGER);
|
||||
INSERT INTO foo VALUES(1);
|
||||
INSERT INTO foo VALUES(2);
|
||||
END;";
|
||||
db.execute_batch(sql).unwrap();
|
||||
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap();
|
||||
assert!(stmt.exists(&[&1i32]).unwrap());
|
||||
assert!(stmt.exists(&[&2i32]).unwrap());
|
||||
assert!(!stmt.exists(&[&0i32]).unwrap());
|
||||
}
|
||||
}
|
62
src/error.rs
62
src/error.rs
@ -2,8 +2,9 @@ use std::error;
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::str;
|
||||
use libc::c_int;
|
||||
use std::os::raw::c_int;
|
||||
use {ffi, errmsg_to_string};
|
||||
use types::Type;
|
||||
|
||||
/// Old name for `Error`. `SqliteError` is deprecated.
|
||||
#[deprecated(since = "0.6.0", note = "Use Error instead")]
|
||||
@ -11,6 +12,7 @@ pub type SqliteError = Error;
|
||||
|
||||
/// Enum listing possible errors from rusqlite.
|
||||
#[derive(Debug)]
|
||||
#[allow(enum_variant_names)]
|
||||
pub enum Error {
|
||||
/// An error from an underlying SQLite call.
|
||||
SqliteFailure(ffi::Error, Option<String>),
|
||||
@ -19,8 +21,14 @@ pub enum Error {
|
||||
/// allow single-threaded use only.
|
||||
SqliteSingleThreadedMode,
|
||||
|
||||
/// An error case available for implementors of the `FromSql` trait.
|
||||
FromSqlConversionFailure(Box<error::Error + Send + Sync>),
|
||||
/// Error when the value of a particular column is requested, but it cannot be converted to
|
||||
/// the requested Rust type.
|
||||
FromSqlConversionFailure(usize, Type, Box<error::Error + Send + Sync>),
|
||||
|
||||
/// Error when SQLite gives us an integral value outside the range of the requested type (e.g.,
|
||||
/// trying to get the value 1000 into a `u8`). The associated `c_int` is the column index, and
|
||||
/// the associated `i64` is the value returned by SQLite.
|
||||
IntegralValueOutOfRange(c_int, i64),
|
||||
|
||||
/// Error converting a string to UTF-8.
|
||||
Utf8Error(str::Utf8Error),
|
||||
@ -51,7 +59,7 @@ pub enum Error {
|
||||
|
||||
/// Error when the value of a particular column is requested, but the type of the result in
|
||||
/// that column cannot be converted to the requested Rust type.
|
||||
InvalidColumnType,
|
||||
InvalidColumnType(c_int, Type),
|
||||
|
||||
/// Error when a query that was expected to insert one row did not insert any or insert many.
|
||||
StatementChangedRows(c_int),
|
||||
@ -59,7 +67,7 @@ pub enum Error {
|
||||
/// Error returned by `functions::Context::get` when the function argument cannot be converted
|
||||
/// to the requested type.
|
||||
#[cfg(feature = "functions")]
|
||||
InvalidFunctionParameterType,
|
||||
InvalidFunctionParameterType(usize, Type),
|
||||
|
||||
/// An error case available for implementors of custom user functions (e.g.,
|
||||
/// `create_scalar_function`).
|
||||
@ -89,7 +97,16 @@ impl fmt::Display for Error {
|
||||
write!(f,
|
||||
"SQLite was compiled or configured for single-threaded use only")
|
||||
}
|
||||
Error::FromSqlConversionFailure(ref err) => err.fmt(f),
|
||||
Error::FromSqlConversionFailure(i, ref t, ref err) => {
|
||||
write!(f,
|
||||
"Conversion error from type {} at index: {}, {}",
|
||||
t,
|
||||
i,
|
||||
err)
|
||||
}
|
||||
Error::IntegralValueOutOfRange(col, val) => {
|
||||
write!(f, "Integer {} out of range at index {}", val, col)
|
||||
}
|
||||
Error::Utf8Error(ref err) => err.fmt(f),
|
||||
Error::NulError(ref err) => err.fmt(f),
|
||||
Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name),
|
||||
@ -100,11 +117,15 @@ impl fmt::Display for Error {
|
||||
Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
|
||||
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i),
|
||||
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name),
|
||||
Error::InvalidColumnType => write!(f, "Invalid column type"),
|
||||
Error::InvalidColumnType(i, ref t) => {
|
||||
write!(f, "Invalid column type {} at index: {}", t, i)
|
||||
}
|
||||
Error::StatementChangedRows(i) => write!(f, "Query changed {} rows", i),
|
||||
|
||||
#[cfg(feature = "functions")]
|
||||
Error::InvalidFunctionParameterType => write!(f, "Invalid function parameter type"),
|
||||
Error::InvalidFunctionParameterType(i, ref t) => {
|
||||
write!(f, "Invalid function parameter type {} at index {}", t, i)
|
||||
}
|
||||
#[cfg(feature = "functions")]
|
||||
Error::UserFunctionError(ref err) => err.fmt(f),
|
||||
}
|
||||
@ -116,50 +137,47 @@ impl error::Error for Error {
|
||||
match *self {
|
||||
Error::SqliteFailure(ref err, None) => err.description(),
|
||||
Error::SqliteFailure(_, Some(ref s)) => s,
|
||||
Error::SqliteSingleThreadedMode => {
|
||||
"SQLite was compiled or configured for single-threaded use only"
|
||||
}
|
||||
Error::FromSqlConversionFailure(ref err) => err.description(),
|
||||
Error::SqliteSingleThreadedMode => "SQLite was compiled or configured for single-threaded use only",
|
||||
Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
|
||||
Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type",
|
||||
Error::Utf8Error(ref err) => err.description(),
|
||||
Error::InvalidParameterName(_) => "invalid parameter name",
|
||||
Error::NulError(ref err) => err.description(),
|
||||
Error::InvalidPath(_) => "invalid path",
|
||||
Error::ExecuteReturnedResults => {
|
||||
"execute returned results - did you mean to call query?"
|
||||
}
|
||||
Error::ExecuteReturnedResults => "execute returned results - did you mean to call query?",
|
||||
Error::QueryReturnedNoRows => "query returned no rows",
|
||||
Error::InvalidColumnIndex(_) => "invalid column index",
|
||||
Error::InvalidColumnName(_) => "invalid column name",
|
||||
Error::InvalidColumnType => "invalid column type",
|
||||
Error::InvalidColumnType(_, _) => "invalid column type",
|
||||
Error::StatementChangedRows(_) => "query inserted zero or more than one row",
|
||||
|
||||
#[cfg(feature = "functions")]
|
||||
Error::InvalidFunctionParameterType => "invalid function parameter type",
|
||||
Error::InvalidFunctionParameterType(_, _) => "invalid function parameter type",
|
||||
#[cfg(feature = "functions")]
|
||||
Error::UserFunctionError(ref err) => err.description(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="clippy", allow(match_same_arms))]
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match *self {
|
||||
Error::SqliteFailure(ref err, _) => Some(err),
|
||||
Error::FromSqlConversionFailure(ref err) => Some(&**err),
|
||||
Error::FromSqlConversionFailure(_, _, ref err) => Some(&**err),
|
||||
Error::Utf8Error(ref err) => Some(err),
|
||||
Error::NulError(ref err) => Some(err),
|
||||
|
||||
Error::IntegralValueOutOfRange(_, _) |
|
||||
Error::SqliteSingleThreadedMode |
|
||||
Error::InvalidParameterName(_) |
|
||||
Error::ExecuteReturnedResults |
|
||||
Error::QueryReturnedNoRows |
|
||||
Error::InvalidColumnIndex(_) |
|
||||
Error::InvalidColumnName(_) |
|
||||
Error::InvalidColumnType |
|
||||
Error::InvalidColumnType(_, _) |
|
||||
Error::InvalidPath(_) |
|
||||
Error::StatementChangedRows(_) => None,
|
||||
|
||||
#[cfg(feature = "functions")]
|
||||
Error::InvalidFunctionParameterType => None,
|
||||
Error::InvalidFunctionParameterType(_, _) => None,
|
||||
|
||||
#[cfg(feature = "functions")]
|
||||
Error::UserFunctionError(ref err) => Some(&**err),
|
||||
@ -173,7 +191,7 @@ pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
|
||||
Error::SqliteFailure(ffi::Error::new(code), message)
|
||||
}
|
||||
|
||||
pub fn error_from_handle(db: *mut ffi::Struct_sqlite3, code: c_int) -> Error {
|
||||
pub fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
|
||||
let message = if db.is_null() {
|
||||
None
|
||||
} else {
|
||||
|
306
src/functions.rs
306
src/functions.rs
@ -54,105 +54,92 @@ use std::ffi::CStr;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use libc::{c_int, c_double, c_char, c_void};
|
||||
use std::os::raw::{c_int, c_char, c_void};
|
||||
|
||||
use ffi;
|
||||
pub use ffi::sqlite3_context;
|
||||
pub use ffi::sqlite3_value;
|
||||
pub use ffi::sqlite3_value_type;
|
||||
pub use ffi::sqlite3_value_numeric_type;
|
||||
use ffi::sqlite3_context;
|
||||
use ffi::sqlite3_value;
|
||||
|
||||
use types::{Null, FromSql, ValueRef};
|
||||
use types::{ToSql, ToSqlOutput, FromSql, FromSqlError, ValueRef};
|
||||
|
||||
use {Result, Error, Connection, str_to_cstring, InnerConnection};
|
||||
|
||||
/// A trait for types that can be converted into the result of an SQL function.
|
||||
pub trait ToResult {
|
||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context);
|
||||
}
|
||||
fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
|
||||
let value = match *result {
|
||||
ToSqlOutput::Borrowed(v) => v,
|
||||
ToSqlOutput::Owned(ref v) => ValueRef::from(v),
|
||||
|
||||
macro_rules! raw_to_impl(
|
||||
($t:ty, $f:ident) => (
|
||||
impl ToResult for $t {
|
||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
||||
ffi::$f(ctx, *self)
|
||||
#[cfg(feature = "blob")]
|
||||
ToSqlOutput::ZeroBlob(len) => {
|
||||
return unsafe { ffi::sqlite3_result_zeroblob(ctx, len) };
|
||||
}
|
||||
};
|
||||
|
||||
match value {
|
||||
ValueRef::Null => unsafe { ffi::sqlite3_result_null(ctx) },
|
||||
ValueRef::Integer(i) => unsafe { ffi::sqlite3_result_int64(ctx, i) },
|
||||
ValueRef::Real(r) => unsafe { ffi::sqlite3_result_double(ctx, r) },
|
||||
ValueRef::Text(s) => unsafe {
|
||||
let length = s.len();
|
||||
if length > ::std::i32::MAX as usize {
|
||||
ffi::sqlite3_result_error_toobig(ctx);
|
||||
} else {
|
||||
let c_str = match str_to_cstring(s) {
|
||||
Ok(c_str) => c_str,
|
||||
// TODO sqlite3_result_error
|
||||
Err(_) => return ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE),
|
||||
};
|
||||
let destructor = if length > 0 {
|
||||
ffi::SQLITE_TRANSIENT()
|
||||
} else {
|
||||
ffi::SQLITE_STATIC()
|
||||
};
|
||||
ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, destructor);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
raw_to_impl!(c_int, sqlite3_result_int);
|
||||
raw_to_impl!(i64, sqlite3_result_int64);
|
||||
raw_to_impl!(c_double, sqlite3_result_double);
|
||||
|
||||
impl<'a> ToResult for bool {
|
||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
||||
if *self {
|
||||
ffi::sqlite3_result_int(ctx, 1)
|
||||
} else {
|
||||
ffi::sqlite3_result_int(ctx, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> ToResult for &'a str {
|
||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
||||
let length = self.len();
|
||||
if length > ::std::i32::MAX as usize {
|
||||
ffi::sqlite3_result_error_toobig(ctx);
|
||||
return;
|
||||
}
|
||||
match str_to_cstring(self) {
|
||||
Ok(c_str) => {
|
||||
ffi::sqlite3_result_text(ctx,
|
||||
c_str.as_ptr(),
|
||||
},
|
||||
ValueRef::Blob(b) => unsafe {
|
||||
let length = b.len();
|
||||
if length > ::std::i32::MAX as usize {
|
||||
ffi::sqlite3_result_error_toobig(ctx);
|
||||
} else if length == 0 {
|
||||
ffi::sqlite3_result_zeroblob(ctx, 0)
|
||||
} else {
|
||||
ffi::sqlite3_result_blob(ctx,
|
||||
b.as_ptr() as *const c_void,
|
||||
length as c_int,
|
||||
ffi::SQLITE_TRANSIENT())
|
||||
ffi::SQLITE_TRANSIENT());
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
|
||||
// Extended constraint error codes were added in SQLite 3.7.16. We don't have an explicit
|
||||
// feature check for that, and this doesn't really warrant one. We'll use the extended code
|
||||
// if we're on the bundled version (since it's at least 3.17.0) and the normal constraint
|
||||
// error code if not.
|
||||
#[cfg(feature = "bundled")]
|
||||
fn constraint_error_code() -> i32 {
|
||||
ffi::SQLITE_CONSTRAINT_FUNCTION
|
||||
}
|
||||
#[cfg(not(feature = "bundled"))]
|
||||
fn constraint_error_code() -> i32 {
|
||||
ffi::SQLITE_CONSTRAINT
|
||||
}
|
||||
|
||||
match *err {
|
||||
Error::SqliteFailure(ref err, ref s) => {
|
||||
ffi::sqlite3_result_error_code(ctx, err.extended_code);
|
||||
if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) {
|
||||
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
||||
}
|
||||
// TODO sqlite3_result_error
|
||||
Err(_) => ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToResult for String {
|
||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
||||
(&self[..]).set_result(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToResult for &'a [u8] {
|
||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
||||
if self.len() > ::std::i32::MAX as usize {
|
||||
ffi::sqlite3_result_error_toobig(ctx);
|
||||
return;
|
||||
_ => {
|
||||
ffi::sqlite3_result_error_code(ctx, constraint_error_code());
|
||||
if let Ok(cstr) = str_to_cstring(err.description()) {
|
||||
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
||||
}
|
||||
}
|
||||
ffi::sqlite3_result_blob(ctx,
|
||||
mem::transmute(self.as_ptr()),
|
||||
self.len() as c_int,
|
||||
ffi::SQLITE_TRANSIENT())
|
||||
}
|
||||
}
|
||||
|
||||
impl ToResult for Vec<u8> {
|
||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
||||
(&self[..]).set_result(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToResult> ToResult for Option<T> {
|
||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
||||
match *self {
|
||||
None => ffi::sqlite3_result_null(ctx),
|
||||
Some(ref t) => t.set_result(ctx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToResult for Null {
|
||||
unsafe fn set_result(&self, ctx: *mut sqlite3_context) {
|
||||
ffi::sqlite3_result_null(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,23 +153,31 @@ impl<'a> ValueRef<'a> {
|
||||
ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)),
|
||||
ffi::SQLITE_TEXT => {
|
||||
let text = ffi::sqlite3_value_text(value);
|
||||
assert!(!text.is_null(), "unexpected SQLITE_TEXT value type with NULL data");
|
||||
assert!(!text.is_null(),
|
||||
"unexpected SQLITE_TEXT value type with NULL data");
|
||||
let s = CStr::from_ptr(text as *const c_char);
|
||||
|
||||
// sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
|
||||
let s = s.to_str().expect("sqlite3_value_text returned invalid UTF-8");
|
||||
let s = s.to_str()
|
||||
.expect("sqlite3_value_text returned invalid UTF-8");
|
||||
ValueRef::Text(s)
|
||||
}
|
||||
ffi::SQLITE_BLOB => {
|
||||
let blob = ffi::sqlite3_value_blob(value);
|
||||
assert!(!blob.is_null(), "unexpected SQLITE_BLOB value type with NULL data");
|
||||
let (blob, len) = (ffi::sqlite3_value_blob(value), ffi::sqlite3_value_bytes(value));
|
||||
|
||||
let len = ffi::sqlite3_value_bytes(value);
|
||||
assert!(len >= 0, "unexpected negative return from sqlite3_value_bytes");
|
||||
|
||||
ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize))
|
||||
assert!(len >= 0,
|
||||
"unexpected negative return from sqlite3_value_bytes");
|
||||
if len > 0 {
|
||||
assert!(!blob.is_null(),
|
||||
"unexpected SQLITE_BLOB value type with NULL data");
|
||||
ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize))
|
||||
} else {
|
||||
// The return value from sqlite3_value_blob() for a zero-length BLOB
|
||||
// is a NULL pointer.
|
||||
ValueRef::Blob(&[])
|
||||
}
|
||||
}
|
||||
_ => unreachable!("sqlite3_value_type returned invalid value")
|
||||
_ => unreachable!("sqlite3_value_type returned invalid value"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,9 +213,17 @@ impl<'a> Context<'a> {
|
||||
let arg = self.args[idx];
|
||||
let value = unsafe { ValueRef::from_value(arg) };
|
||||
FromSql::column_result(value).map_err(|err| match err {
|
||||
Error::InvalidColumnType => Error::InvalidFunctionParameterType,
|
||||
_ => err
|
||||
})
|
||||
FromSqlError::InvalidType => {
|
||||
Error::InvalidFunctionParameterType(idx, value.data_type())
|
||||
}
|
||||
FromSqlError::OutOfRange(i) => {
|
||||
Error::IntegralValueOutOfRange(idx as c_int,
|
||||
i)
|
||||
}
|
||||
FromSqlError::Other(err) => {
|
||||
Error::FromSqlConversionFailure(idx, value.data_type(), err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Sets the auxilliary data associated with a particular parameter. See
|
||||
@ -246,11 +249,7 @@ impl<'a> Context<'a> {
|
||||
/// types must be identical.
|
||||
pub unsafe fn get_aux<T>(&self, arg: c_int) -> Option<&T> {
|
||||
let p = ffi::sqlite3_get_auxdata(self.ctx, arg) as *mut T;
|
||||
if p.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(&*p)
|
||||
}
|
||||
if p.is_null() { None } else { Some(&*p) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,7 +258,7 @@ impl<'a> Context<'a> {
|
||||
/// `A` is the type of the aggregation context and `T` is the type of the final result.
|
||||
/// Implementations should be stateless.
|
||||
pub trait Aggregate<A, T>
|
||||
where T: ToResult
|
||||
where T: ToSql
|
||||
{
|
||||
/// Initializes the aggregation context. Will be called prior to the first call
|
||||
/// to `step()` to set up the context for an invocation of the function. (Note:
|
||||
@ -293,10 +292,9 @@ impl Connection {
|
||||
///
|
||||
/// ```rust
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// # type c_double = f64;
|
||||
/// fn scalar_function_example(db: Connection) -> Result<()> {
|
||||
/// try!(db.create_scalar_function("halve", 1, true, |ctx| {
|
||||
/// let value = try!(ctx.get::<c_double>(0));
|
||||
/// let value = try!(ctx.get::<f64>(0));
|
||||
/// Ok(value / 2f64)
|
||||
/// }));
|
||||
///
|
||||
@ -316,9 +314,11 @@ impl Connection {
|
||||
x_func: F)
|
||||
-> Result<()>
|
||||
where F: FnMut(&Context) -> Result<T>,
|
||||
T: ToResult
|
||||
T: ToSql
|
||||
{
|
||||
self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func)
|
||||
self.db
|
||||
.borrow_mut()
|
||||
.create_scalar_function(fn_name, n_arg, deterministic, x_func)
|
||||
}
|
||||
|
||||
/// Attach a user-defined aggregate function to this database connection.
|
||||
@ -333,7 +333,7 @@ impl Connection {
|
||||
aggr: D)
|
||||
-> Result<()>
|
||||
where D: Aggregate<A, T>,
|
||||
T: ToResult
|
||||
T: ToSql
|
||||
{
|
||||
self.db
|
||||
.borrow_mut()
|
||||
@ -361,13 +361,13 @@ impl InnerConnection {
|
||||
x_func: F)
|
||||
-> Result<()>
|
||||
where F: FnMut(&Context) -> Result<T>,
|
||||
T: ToResult
|
||||
T: ToSql
|
||||
{
|
||||
unsafe extern "C" fn call_boxed_closure<F, T>(ctx: *mut sqlite3_context,
|
||||
argc: c_int,
|
||||
argv: *mut *mut sqlite3_value)
|
||||
where F: FnMut(&Context) -> Result<T>,
|
||||
T: ToResult
|
||||
T: ToSql
|
||||
{
|
||||
let ctx = Context {
|
||||
ctx: ctx,
|
||||
@ -375,20 +375,14 @@ impl InnerConnection {
|
||||
};
|
||||
let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx.ctx));
|
||||
assert!(!boxed_f.is_null(), "Internal error - null function pointer");
|
||||
match (*boxed_f)(&ctx) {
|
||||
Ok(r) => r.set_result(ctx.ctx),
|
||||
Err(Error::SqliteFailure(err, s)) => {
|
||||
ffi::sqlite3_result_error_code(ctx.ctx, err.extended_code);
|
||||
if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
|
||||
ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
ffi::sqlite3_result_error_code(ctx.ctx, ffi::SQLITE_CONSTRAINT_FUNCTION);
|
||||
if let Ok(cstr) = str_to_cstring(err.description()) {
|
||||
ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1);
|
||||
}
|
||||
}
|
||||
|
||||
let t = (*boxed_f)(&ctx);
|
||||
let t = t.as_ref().map(|t| ToSql::to_sql(t));
|
||||
|
||||
match t {
|
||||
Ok(Ok(ref value)) => set_result(ctx.ctx, value),
|
||||
Ok(Err(err)) => report_error(ctx.ctx, &err),
|
||||
Err(err) => report_error(ctx.ctx, err),
|
||||
}
|
||||
}
|
||||
|
||||
@ -419,7 +413,7 @@ impl InnerConnection {
|
||||
aggr: D)
|
||||
-> Result<()>
|
||||
where D: Aggregate<A, T>,
|
||||
T: ToResult
|
||||
T: ToSql
|
||||
{
|
||||
unsafe fn aggregate_context<A>(ctx: *mut sqlite3_context,
|
||||
bytes: usize)
|
||||
@ -431,28 +425,11 @@ impl InnerConnection {
|
||||
Some(pac)
|
||||
}
|
||||
|
||||
unsafe fn report_aggregate_error(ctx: *mut sqlite3_context, err: Error) {
|
||||
match err {
|
||||
Error::SqliteFailure(err, s) => {
|
||||
ffi::sqlite3_result_error_code(ctx, err.extended_code);
|
||||
if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
|
||||
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_CONSTRAINT_FUNCTION);
|
||||
if let Ok(cstr) = str_to_cstring(err.description()) {
|
||||
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn call_boxed_step<A, D, T>(ctx: *mut sqlite3_context,
|
||||
argc: c_int,
|
||||
argv: *mut *mut sqlite3_value)
|
||||
where D: Aggregate<A, T>,
|
||||
T: ToResult
|
||||
T: ToSql
|
||||
{
|
||||
let boxed_aggr: *mut D = mem::transmute(ffi::sqlite3_user_data(ctx));
|
||||
assert!(!boxed_aggr.is_null(),
|
||||
@ -477,13 +454,13 @@ impl InnerConnection {
|
||||
|
||||
match (*boxed_aggr).step(&mut ctx, &mut **pac) {
|
||||
Ok(_) => {}
|
||||
Err(err) => report_aggregate_error(ctx.ctx, err),
|
||||
Err(err) => report_error(ctx.ctx, &err),
|
||||
};
|
||||
}
|
||||
|
||||
unsafe extern "C" fn call_boxed_final<A, D, T>(ctx: *mut sqlite3_context)
|
||||
where D: Aggregate<A, T>,
|
||||
T: ToResult
|
||||
T: ToSql
|
||||
{
|
||||
let boxed_aggr: *mut D = mem::transmute(ffi::sqlite3_user_data(ctx));
|
||||
assert!(!boxed_aggr.is_null(),
|
||||
@ -503,10 +480,13 @@ impl InnerConnection {
|
||||
None => None,
|
||||
};
|
||||
|
||||
match (*boxed_aggr).finalize(a) {
|
||||
Ok(r) => r.set_result(ctx),
|
||||
Err(err) => report_aggregate_error(ctx, err),
|
||||
};
|
||||
let t = (*boxed_aggr).finalize(a);
|
||||
let t = t.as_ref().map(|t| ToSql::to_sql(t));
|
||||
match t {
|
||||
Ok(Ok(ref value)) => set_result(ctx, value),
|
||||
Ok(Err(err)) => report_error(ctx, &err),
|
||||
Err(err) => report_error(ctx, err),
|
||||
}
|
||||
}
|
||||
|
||||
let boxed_aggr: *mut D = Box::into_raw(Box::new(aggr));
|
||||
@ -551,7 +531,7 @@ mod test {
|
||||
extern crate regex;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use libc::c_double;
|
||||
use std::os::raw::c_double;
|
||||
use self::regex::Regex;
|
||||
use std::f64::EPSILON;
|
||||
|
||||
@ -709,9 +689,10 @@ mod test {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
for &(expected, query) in &[("", "SELECT my_concat()"),
|
||||
("onetwo", "SELECT my_concat('one', 'two')"),
|
||||
("abc", "SELECT my_concat('a', 'b', 'c')")] {
|
||||
for &(expected, query) in
|
||||
&[("", "SELECT my_concat()"),
|
||||
("onetwo", "SELECT my_concat('one', 'two')"),
|
||||
("abc", "SELECT my_concat('a', 'b', 'c')")] {
|
||||
let result: String = db.query_row(query, &[], |r| r.get(0)).unwrap();
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
@ -753,17 +734,16 @@ mod test {
|
||||
#[test]
|
||||
fn test_sum() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.create_aggregate_function("my_sum", 1, true, Sum).unwrap();
|
||||
db.create_aggregate_function("my_sum", 1, true, Sum)
|
||||
.unwrap();
|
||||
|
||||
// sum should return NULL when given no columns (contrast with count below)
|
||||
let no_result = "SELECT my_sum(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
|
||||
let result: Option<i64> = db.query_row(no_result, &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
let result: Option<i64> = db.query_row(no_result, &[], |r| r.get(0)).unwrap();
|
||||
assert!(result.is_none());
|
||||
|
||||
let single_sum = "SELECT my_sum(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
|
||||
let result: i64 = db.query_row(single_sum, &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)).unwrap();
|
||||
assert_eq!(4, result);
|
||||
|
||||
let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \
|
||||
@ -776,7 +756,8 @@ mod test {
|
||||
#[test]
|
||||
fn test_count() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.create_aggregate_function("my_count", -1, true, Count).unwrap();
|
||||
db.create_aggregate_function("my_count", -1, true, Count)
|
||||
.unwrap();
|
||||
|
||||
// count should return 0 when given no columns (contrast with sum above)
|
||||
let no_result = "SELECT my_count(i) FROM (SELECT 2 AS i WHERE 1 <> 1)";
|
||||
@ -784,8 +765,7 @@ mod test {
|
||||
assert_eq!(result, 0);
|
||||
|
||||
let single_sum = "SELECT my_count(i) FROM (SELECT 2 AS i UNION ALL SELECT 2)";
|
||||
let result: i64 = db.query_row(single_sum, &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
let result: i64 = db.query_row(single_sum, &[], |r| r.get(0)).unwrap();
|
||||
assert_eq!(2, result);
|
||||
}
|
||||
}
|
||||
|
871
src/lib.rs
871
src/lib.rs
File diff suppressed because it is too large
Load Diff
71
src/limits.rs
Normal file
71
src/limits.rs
Normal file
@ -0,0 +1,71 @@
|
||||
//! Run-Time Limits
|
||||
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use ffi;
|
||||
pub use ffi::Limit;
|
||||
|
||||
use Connection;
|
||||
|
||||
impl Connection {
|
||||
/// Returns the current value of a limit.
|
||||
pub fn limit(&self, limit: Limit) -> i32 {
|
||||
let c = self.db.borrow();
|
||||
unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, -1) }
|
||||
}
|
||||
|
||||
/// Changes the limit to `new_val`, returning the prior value of the limit.
|
||||
pub fn set_limit(&self, limit: Limit, new_val: i32) -> i32 {
|
||||
let c = self.db.borrow_mut();
|
||||
unsafe { ffi::sqlite3_limit(c.db(), limit as c_int, new_val) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use ffi::Limit;
|
||||
use Connection;
|
||||
|
||||
#[test]
|
||||
fn test_limit() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.set_limit(Limit::SQLITE_LIMIT_LENGTH, 1024);
|
||||
assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_LENGTH));
|
||||
|
||||
db.set_limit(Limit::SQLITE_LIMIT_SQL_LENGTH, 1024);
|
||||
assert_eq!(1024, db.limit(Limit::SQLITE_LIMIT_SQL_LENGTH));
|
||||
|
||||
db.set_limit(Limit::SQLITE_LIMIT_COLUMN, 64);
|
||||
assert_eq!(64, db.limit(Limit::SQLITE_LIMIT_COLUMN));
|
||||
|
||||
db.set_limit(Limit::SQLITE_LIMIT_EXPR_DEPTH, 256);
|
||||
assert_eq!(256, db.limit(Limit::SQLITE_LIMIT_EXPR_DEPTH));
|
||||
|
||||
db.set_limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT, 32);
|
||||
assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_COMPOUND_SELECT));
|
||||
|
||||
db.set_limit(Limit::SQLITE_LIMIT_FUNCTION_ARG, 32);
|
||||
assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_FUNCTION_ARG));
|
||||
|
||||
db.set_limit(Limit::SQLITE_LIMIT_ATTACHED, 2);
|
||||
assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_ATTACHED));
|
||||
|
||||
db.set_limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH, 128);
|
||||
assert_eq!(128, db.limit(Limit::SQLITE_LIMIT_LIKE_PATTERN_LENGTH));
|
||||
|
||||
db.set_limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER, 99);
|
||||
assert_eq!(99, db.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER));
|
||||
|
||||
// SQLITE_LIMIT_TRIGGER_DEPTH was added in SQLite 3.6.18.
|
||||
if ::version_number() >= 3006018 {
|
||||
db.set_limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH, 32);
|
||||
assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH));
|
||||
}
|
||||
|
||||
// SQLITE_LIMIT_WORKER_THREADS was added in SQLite 3.8.7.
|
||||
if ::version_number() >= 3008007 {
|
||||
db.set_limit(Limit::SQLITE_LIMIT_WORKER_THREADS, 2);
|
||||
assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_WORKER_THREADS));
|
||||
}
|
||||
}
|
||||
}
|
@ -25,7 +25,8 @@ impl<'conn> LoadExtensionGuard<'conn> {
|
||||
/// Attempt to enable loading extensions. Loading extensions will be disabled when this
|
||||
/// guard goes out of scope. Cannot be meaningfully nested.
|
||||
pub fn new(conn: &Connection) -> Result<LoadExtensionGuard> {
|
||||
conn.load_extension_enable().map(|_| LoadExtensionGuard { conn: conn })
|
||||
conn.load_extension_enable()
|
||||
.map(|_| LoadExtensionGuard { conn: conn })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,358 +0,0 @@
|
||||
use std::convert;
|
||||
use std::result;
|
||||
use libc::c_int;
|
||||
|
||||
use {Result, Error, Connection, Statement, MappedRows, AndThenRows, Rows, Row, str_to_cstring};
|
||||
use types::ToSql;
|
||||
|
||||
impl Connection {
|
||||
/// Convenience method to prepare and execute a single SQL statement with named parameter(s).
|
||||
///
|
||||
/// On success, returns the number of rows that were changed or inserted or deleted (via
|
||||
/// `sqlite3_changes`).
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// fn insert(conn: &Connection) -> Result<i32> {
|
||||
/// conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")])
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
|
||||
/// underlying SQLite call fails.
|
||||
pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result<c_int> {
|
||||
self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params))
|
||||
}
|
||||
|
||||
/// Convenience method to execute a query with named parameter(s) that is expected to return
|
||||
/// a single row.
|
||||
///
|
||||
/// If the query returns more than one row, all rows except the first are ignored.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
|
||||
/// underlying SQLite call fails.
|
||||
pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result<T>
|
||||
where F: FnOnce(Row) -> T
|
||||
{
|
||||
let mut stmt = try!(self.prepare(sql));
|
||||
let mut rows = try!(stmt.query_named(params));
|
||||
|
||||
rows.get_expected_row().map(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'conn> Statement<'conn> {
|
||||
/// Return the index of an SQL parameter given its name.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return Err if `name` is invalid. Will return Ok(None) if the name
|
||||
/// is valid but not a bound parameter of this statement.
|
||||
pub fn parameter_index(&self, name: &str) -> Result<Option<i32>> {
|
||||
let c_name = try!(str_to_cstring(name));
|
||||
Ok(self.stmt.bind_parameter_index(&c_name))
|
||||
}
|
||||
|
||||
/// Execute the prepared statement with named parameter(s). If any parameters
|
||||
/// that were in the prepared statement are not included in `params`, they
|
||||
/// will continue to use the most-recently bound value from a previous call
|
||||
/// to `execute_named`, or `NULL` if they have never been bound.
|
||||
///
|
||||
/// On success, returns the number of rows that were changed or inserted or deleted (via
|
||||
/// `sqlite3_changes`).
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// fn insert(conn: &Connection) -> Result<i32> {
|
||||
/// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)"));
|
||||
/// stmt.execute_named(&[(":name", &"one")])
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails, the executed statement returns rows (in
|
||||
/// which case `query` should be used instead), or the underling SQLite call fails.
|
||||
pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result<c_int> {
|
||||
try!(self.bind_parameters_named(params));
|
||||
self.execute_()
|
||||
}
|
||||
|
||||
/// Execute the prepared statement with named parameter(s), returning a handle for the
|
||||
/// resulting rows. If any parameters that were in the prepared statement are not included in
|
||||
/// `params`, they will continue to use the most-recently bound value from a previous call to
|
||||
/// `query_named`, or `NULL` if they have never been bound.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result, Rows};
|
||||
/// fn query(conn: &Connection) -> Result<()> {
|
||||
/// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
|
||||
/// let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
|
||||
/// while let Some(row) = rows.next() {
|
||||
/// // ...
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> Result<Rows<'a>> {
|
||||
try!(self.bind_parameters_named(params));
|
||||
Ok(Rows::new(self))
|
||||
}
|
||||
|
||||
/// Execute the prepared statement with named parameter(s), returning an iterator over the
|
||||
/// result of calling the mapping function over the query's rows. If any parameters that were
|
||||
/// in the prepared statement are not included in `params`, they will continue to use the
|
||||
/// most-recently bound value from a previous call to `query_named`, or `NULL` if they have
|
||||
/// never been bound.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
|
||||
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
|
||||
/// let rows = try!(stmt.query_map_named(&[(":id", &"one")], |row| row.get(0)));
|
||||
///
|
||||
/// let mut names = Vec::new();
|
||||
/// for name_result in rows {
|
||||
/// names.push(try!(name_result));
|
||||
/// }
|
||||
///
|
||||
/// Ok(names)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_map_named<'a, T, F>(&'a mut self,
|
||||
params: &[(&str, &ToSql)],
|
||||
f: F)
|
||||
-> Result<MappedRows<'a, F>>
|
||||
where F: FnMut(&Row) -> T
|
||||
{
|
||||
let rows = try!(self.query_named(params));
|
||||
Ok(MappedRows {
|
||||
rows: rows,
|
||||
map: f,
|
||||
})
|
||||
}
|
||||
|
||||
/// Execute the prepared statement with named parameter(s), returning an iterator over the
|
||||
/// result of calling the mapping function over the query's rows. If any parameters that were
|
||||
/// in the prepared statement are not included in `params`, they will continue to use the
|
||||
/// most-recently bound value from a previous call to `query_named`, or `NULL` if they have
|
||||
/// never been bound.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// struct Person { name: String };
|
||||
///
|
||||
/// fn name_to_person(name: String) -> Result<Person> {
|
||||
/// // ... check for valid name
|
||||
/// Ok(Person{ name: name })
|
||||
/// }
|
||||
///
|
||||
/// fn get_names(conn: &Connection) -> Result<Vec<Person>> {
|
||||
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
|
||||
/// let rows = try!(stmt.query_and_then_named(&[(":id", &"one")], |row| {
|
||||
/// name_to_person(row.get(0))
|
||||
/// }));
|
||||
///
|
||||
/// let mut persons = Vec::new();
|
||||
/// for person_result in rows {
|
||||
/// persons.push(try!(person_result));
|
||||
/// }
|
||||
///
|
||||
/// Ok(persons)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_and_then_named<'a, T, E, F>(&'a mut self,
|
||||
params: &[(&str, &ToSql)],
|
||||
f: F)
|
||||
-> Result<AndThenRows<'a, F>>
|
||||
where E: convert::From<Error>,
|
||||
F: FnMut(&Row) -> result::Result<T, E>
|
||||
{
|
||||
let rows = try!(self.query_named(params));
|
||||
Ok(AndThenRows {
|
||||
rows: rows,
|
||||
map: f,
|
||||
})
|
||||
}
|
||||
|
||||
fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> {
|
||||
for &(name, value) in params {
|
||||
if let Some(i) = try!(self.parameter_index(name)) {
|
||||
try!(self.conn.decode_result(unsafe {
|
||||
value.bind_parameter(self.stmt.ptr(), i)
|
||||
}));
|
||||
} else {
|
||||
return Err(Error::InvalidParameterName(name.into()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use Connection;
|
||||
use error::Error;
|
||||
|
||||
#[test]
|
||||
fn test_execute_named() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
|
||||
|
||||
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(),
|
||||
1);
|
||||
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(),
|
||||
1);
|
||||
|
||||
assert_eq!(3i32,
|
||||
db.query_row_named("SELECT SUM(x) FROM foo WHERE x > :x",
|
||||
&[(":x", &0i32)],
|
||||
|r| r.get(0))
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stmt_execute_named() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
|
||||
INTEGER)";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap();
|
||||
stmt.execute_named(&[(":name", &"one")]).unwrap();
|
||||
|
||||
assert_eq!(1i32,
|
||||
db.query_row_named("SELECT COUNT(*) FROM test WHERE name = :name",
|
||||
&[(":name", &"one")],
|
||||
|r| r.get(0))
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_named() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = r#"
|
||||
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
|
||||
INSERT INTO test(id, name) VALUES (1, "one");
|
||||
"#;
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap();
|
||||
let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap();
|
||||
|
||||
let id: i32 = rows.next().unwrap().unwrap().get(0);
|
||||
assert_eq!(1, id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_map_named() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = r#"
|
||||
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
|
||||
INSERT INTO test(id, name) VALUES (1, "one");
|
||||
"#;
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name").unwrap();
|
||||
let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| {
|
||||
let id: i32 = row.get(0);
|
||||
2 * id
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let doubled_id: i32 = rows.next().unwrap().unwrap();
|
||||
assert_eq!(2, doubled_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_and_then_named() {
|
||||
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = r#"
|
||||
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
|
||||
INSERT INTO test(id, name) VALUES (1, "one");
|
||||
INSERT INTO test(id, name) VALUES (2, "one");
|
||||
"#;
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")
|
||||
.unwrap();
|
||||
let mut rows = stmt.query_and_then_named(&[(":name", &"one")], |row| {
|
||||
let id: i32 = row.get(0);
|
||||
if id == 1 {
|
||||
Ok(id)
|
||||
} else {
|
||||
Err(Error::SqliteSingleThreadedMode)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// first row should be Ok
|
||||
let doubled_id: i32 = rows.next().unwrap().unwrap();
|
||||
assert_eq!(1, doubled_id);
|
||||
|
||||
// second row should be Err
|
||||
match rows.next().unwrap() {
|
||||
Ok(_) => panic!("invalid Ok"),
|
||||
Err(Error::SqliteSingleThreadedMode) => (),
|
||||
Err(_) => panic!("invalid Err"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unbound_parameters_are_null() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap();
|
||||
stmt.execute_named(&[(":x", &"one")]).unwrap();
|
||||
|
||||
let result: Option<String> =
|
||||
db.query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0))
|
||||
.unwrap();
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unbound_parameters_are_reused() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap();
|
||||
stmt.execute_named(&[(":x", &"one")]).unwrap();
|
||||
stmt.execute_named(&[(":y", &"two")]).unwrap();
|
||||
|
||||
let result: String =
|
||||
db.query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(result, "one");
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
use libc::c_int;
|
||||
use std::os::raw::c_int;
|
||||
use super::ffi;
|
||||
|
||||
// Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
|
||||
|
223
src/row.rs
Normal file
223
src/row.rs
Normal file
@ -0,0 +1,223 @@
|
||||
use std::{convert, result};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::{Statement, Error, Result};
|
||||
use types::{FromSql, FromSqlError};
|
||||
use statement::StatementCrateImpl;
|
||||
|
||||
/// An handle for the resulting rows of a query.
|
||||
pub struct Rows<'stmt> {
|
||||
stmt: Option<&'stmt Statement<'stmt>>,
|
||||
}
|
||||
|
||||
impl<'stmt> Rows<'stmt> {
|
||||
fn reset(&mut self) {
|
||||
if let Some(stmt) = self.stmt.take() {
|
||||
stmt.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to get the next row from the query. Returns `Some(Ok(Row))` if there
|
||||
/// is another row, `Some(Err(...))` if there was an error getting the next
|
||||
/// row, and `None` if all rows have been retrieved.
|
||||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// This interface is not compatible with Rust's `Iterator` trait, because the
|
||||
/// lifetime of the returned row is tied to the lifetime of `self`. This is a
|
||||
/// "streaming iterator". For a more natural interface, consider using `query_map`
|
||||
/// or `query_and_then` instead, which return types that implement `Iterator`.
|
||||
pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
|
||||
self.stmt
|
||||
.and_then(|stmt| match stmt.step() {
|
||||
Ok(true) => {
|
||||
Some(Ok(Row {
|
||||
stmt: stmt,
|
||||
phantom: PhantomData,
|
||||
}))
|
||||
}
|
||||
Ok(false) => {
|
||||
self.reset();
|
||||
None
|
||||
}
|
||||
Err(err) => {
|
||||
self.reset();
|
||||
Some(Err(err))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This trait lets us have "pub(crate)" visibility on some methods. Remove this
|
||||
// once pub(crate) is stable.
|
||||
pub trait RowsCrateImpl<'stmt> {
|
||||
fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt>;
|
||||
fn get_expected_row<'a>(&'a mut self) -> Result<Row<'a, 'stmt>>;
|
||||
}
|
||||
|
||||
impl<'stmt> RowsCrateImpl<'stmt> for Rows<'stmt> {
|
||||
fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> {
|
||||
Rows { stmt: Some(stmt) }
|
||||
}
|
||||
|
||||
fn get_expected_row<'a>(&'a mut self) -> Result<Row<'a, 'stmt>> {
|
||||
match self.next() {
|
||||
Some(row) => row,
|
||||
None => Err(Error::QueryReturnedNoRows),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'stmt> Drop for Rows<'stmt> {
|
||||
fn drop(&mut self) {
|
||||
self.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the mapped resulting rows of a query.
|
||||
pub struct MappedRows<'stmt, F> {
|
||||
rows: Rows<'stmt>,
|
||||
map: F,
|
||||
}
|
||||
|
||||
// TODO: This trait lets us have "pub(crate)" visibility on some methods. Remove this
|
||||
// once pub(crate) is stable.
|
||||
pub trait MappedRowsCrateImpl<'stmt, T, F> {
|
||||
fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F>;
|
||||
}
|
||||
|
||||
impl<'stmt, T, F> MappedRowsCrateImpl<'stmt, T, F> for MappedRows<'stmt, F>
|
||||
where F: FnMut(&Row) -> T
|
||||
{
|
||||
fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
|
||||
MappedRows { rows: rows, map: f }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'conn, T, F> Iterator for MappedRows<'conn, F>
|
||||
where F: FnMut(&Row) -> T
|
||||
{
|
||||
type Item = Result<T>;
|
||||
|
||||
fn next(&mut self) -> Option<Result<T>> {
|
||||
let map = &mut self.map;
|
||||
self.rows
|
||||
.next()
|
||||
.map(|row_result| row_result.map(|row| (map)(&row)))
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the mapped resulting rows of a query, with an Error type
|
||||
/// unifying with Error.
|
||||
pub struct AndThenRows<'stmt, F> {
|
||||
rows: Rows<'stmt>,
|
||||
map: F,
|
||||
}
|
||||
|
||||
// TODO: This trait lets us have "pub(crate)" visibility on some methods. Remove this
|
||||
// once pub(crate) is stable.
|
||||
pub trait AndThenRowsCrateImpl<'stmt, T, E, F> {
|
||||
fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F>;
|
||||
}
|
||||
|
||||
impl<'stmt, T, E, F> AndThenRowsCrateImpl<'stmt, T, E, F> for AndThenRows<'stmt, F>
|
||||
where F: FnMut(&Row) -> result::Result<T, E>
|
||||
{
|
||||
fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
|
||||
AndThenRows { rows: rows, map: f }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
|
||||
where E: convert::From<Error>,
|
||||
F: FnMut(&Row) -> result::Result<T, E>
|
||||
{
|
||||
type Item = result::Result<T, E>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let map = &mut self.map;
|
||||
self.rows
|
||||
.next()
|
||||
.map(|row_result| row_result.map_err(E::from).and_then(|row| (map)(&row)))
|
||||
}
|
||||
}
|
||||
|
||||
/// A single result row of a query.
|
||||
pub struct Row<'a, 'stmt> {
|
||||
stmt: &'stmt Statement<'stmt>,
|
||||
phantom: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, 'stmt> Row<'a, 'stmt> {
|
||||
/// Get the value of a particular column of the result row.
|
||||
///
|
||||
/// ## Failure
|
||||
///
|
||||
/// Panics if calling `row.get_checked(idx)` would return an error, including:
|
||||
///
|
||||
/// * If the underlying SQLite column type is not a valid type as a source for `T`
|
||||
/// * If the underlying SQLite integral value is outside the range representable by `T`
|
||||
/// * If `idx` is outside the range of columns in the returned query
|
||||
pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
|
||||
self.get_checked(idx).unwrap()
|
||||
}
|
||||
|
||||
/// Get the value of a particular column of the result row.
|
||||
///
|
||||
/// ## Failure
|
||||
///
|
||||
/// Returns an `Error::InvalidColumnType` if the underlying SQLite column
|
||||
/// type is not a valid type as a source for `T`.
|
||||
///
|
||||
/// Returns an `Error::InvalidColumnIndex` if `idx` is outside the valid column range
|
||||
/// for this row.
|
||||
///
|
||||
/// Returns an `Error::InvalidColumnName` if `idx` is not a valid column name
|
||||
/// for this row.
|
||||
pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
|
||||
let idx = try!(idx.idx(self.stmt));
|
||||
let value = self.stmt.value_ref(idx);
|
||||
FromSql::column_result(value).map_err(|err| match err {
|
||||
FromSqlError::InvalidType => {
|
||||
Error::InvalidColumnType(idx,
|
||||
value.data_type())
|
||||
}
|
||||
FromSqlError::OutOfRange(i) => {
|
||||
Error::IntegralValueOutOfRange(idx, i)
|
||||
}
|
||||
FromSqlError::Other(err) => {
|
||||
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Return the number of columns in the current row.
|
||||
pub fn column_count(&self) -> i32 {
|
||||
self.stmt.column_count()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait implemented by types that can index into columns of a row.
|
||||
pub trait RowIndex {
|
||||
/// Returns the index of the appropriate column, or `None` if no such
|
||||
/// column exists.
|
||||
fn idx(&self, stmt: &Statement) -> Result<i32>;
|
||||
}
|
||||
|
||||
impl RowIndex for i32 {
|
||||
#[inline]
|
||||
fn idx(&self, stmt: &Statement) -> Result<i32> {
|
||||
if *self < 0 || *self >= stmt.column_count() {
|
||||
Err(Error::InvalidColumnIndex(*self))
|
||||
} else {
|
||||
Ok(*self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> RowIndex for &'a str {
|
||||
#[inline]
|
||||
fn idx(&self, stmt: &Statement) -> Result<i32> {
|
||||
stmt.column_index(*self)
|
||||
}
|
||||
}
|
788
src/statement.rs
Normal file
788
src/statement.rs
Normal file
@ -0,0 +1,788 @@
|
||||
use std::{convert, fmt, mem, ptr, result, str};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::slice::from_raw_parts;
|
||||
|
||||
use super::ffi;
|
||||
use super::{Connection, RawStatement, Result, Error, ValueRef, Row, Rows, AndThenRows, MappedRows};
|
||||
use super::str_to_cstring;
|
||||
use types::{ToSql, ToSqlOutput};
|
||||
use row::{RowsCrateImpl, MappedRowsCrateImpl, AndThenRowsCrateImpl};
|
||||
|
||||
/// A prepared statement.
|
||||
pub struct Statement<'conn> {
|
||||
conn: &'conn Connection,
|
||||
stmt: RawStatement,
|
||||
}
|
||||
|
||||
impl<'conn> Statement<'conn> {
|
||||
/// Get all the column names in the result set of the prepared statement.
|
||||
pub fn column_names(&self) -> Vec<&str> {
|
||||
let n = self.column_count();
|
||||
let mut cols = Vec::with_capacity(n as usize);
|
||||
for i in 0..n {
|
||||
let slice = self.stmt.column_name(i);
|
||||
let s = str::from_utf8(slice.to_bytes()).unwrap();
|
||||
cols.push(s);
|
||||
}
|
||||
cols
|
||||
}
|
||||
|
||||
/// Return the number of columns in the result set returned by the prepared statement.
|
||||
pub fn column_count(&self) -> i32 {
|
||||
self.stmt.column_count()
|
||||
}
|
||||
|
||||
/// Returns the column index in the result set for a given column name.
|
||||
///
|
||||
/// If there is no AS clause then the name of the column is unspecified and may change from one
|
||||
/// release of SQLite to the next.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return an `Error::InvalidColumnName` when there is no column with the specified `name`.
|
||||
pub fn column_index(&self, name: &str) -> Result<i32> {
|
||||
let bytes = name.as_bytes();
|
||||
let n = self.column_count();
|
||||
for i in 0..n {
|
||||
if bytes == self.stmt.column_name(i).to_bytes() {
|
||||
return Ok(i);
|
||||
}
|
||||
}
|
||||
Err(Error::InvalidColumnName(String::from(name)))
|
||||
}
|
||||
|
||||
/// Execute the prepared statement.
|
||||
///
|
||||
/// On success, returns the number of rows that were changed or inserted or deleted (via
|
||||
/// `sqlite3_changes`).
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// fn update_rows(conn: &Connection) -> Result<()> {
|
||||
/// let mut stmt = try!(conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?"));
|
||||
///
|
||||
/// try!(stmt.execute(&[&1i32]));
|
||||
/// try!(stmt.execute(&[&2i32]));
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails, the executed statement returns rows (in
|
||||
/// which case `query` should be used instead), or the underling SQLite call fails.
|
||||
pub fn execute(&mut self, params: &[&ToSql]) -> Result<c_int> {
|
||||
try!(self.bind_parameters(params));
|
||||
self.execute_with_bound_parameters()
|
||||
}
|
||||
|
||||
/// Execute the prepared statement with named parameter(s). If any parameters
|
||||
/// that were in the prepared statement are not included in `params`, they
|
||||
/// will continue to use the most-recently bound value from a previous call
|
||||
/// to `execute_named`, or `NULL` if they have never been bound.
|
||||
///
|
||||
/// On success, returns the number of rows that were changed or inserted or deleted (via
|
||||
/// `sqlite3_changes`).
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// fn insert(conn: &Connection) -> Result<i32> {
|
||||
/// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)"));
|
||||
/// stmt.execute_named(&[(":name", &"one")])
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails, the executed statement returns rows (in
|
||||
/// which case `query` should be used instead), or the underling SQLite call fails.
|
||||
pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result<c_int> {
|
||||
try!(self.bind_parameters_named(params));
|
||||
self.execute_with_bound_parameters()
|
||||
}
|
||||
|
||||
/// Execute an INSERT and return the ROWID.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This function is a convenience wrapper around `execute()` intended for queries that
|
||||
/// insert a single item. It is possible to misuse this function in a way that it cannot
|
||||
/// detect, such as by calling it on a statement which _updates_ a single item rather than
|
||||
/// inserting one. Please don't do that.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if no row is inserted or many rows are inserted.
|
||||
pub fn insert(&mut self, params: &[&ToSql]) -> Result<i64> {
|
||||
let changes = try!(self.execute(params));
|
||||
match changes {
|
||||
1 => Ok(self.conn.last_insert_rowid()),
|
||||
_ => Err(Error::StatementChangedRows(changes)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute the prepared statement, returning a handle to the resulting rows.
|
||||
///
|
||||
/// Due to lifetime restricts, the rows handle returned by `query` does not
|
||||
/// implement the `Iterator` trait. Consider using `query_map` or `query_and_then`
|
||||
/// instead, which do.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
|
||||
/// let mut stmt = try!(conn.prepare("SELECT name FROM people"));
|
||||
/// let mut rows = try!(stmt.query(&[]));
|
||||
///
|
||||
/// let mut names = Vec::new();
|
||||
/// while let Some(result_row) = rows.next() {
|
||||
/// let row = try!(result_row);
|
||||
/// names.push(row.get(0));
|
||||
/// }
|
||||
///
|
||||
/// Ok(names)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result<Rows<'a>> {
|
||||
try!(self.bind_parameters(params));
|
||||
Ok(Rows::new(self))
|
||||
}
|
||||
|
||||
/// Execute the prepared statement with named parameter(s), returning a handle for the
|
||||
/// resulting rows. If any parameters that were in the prepared statement are not included in
|
||||
/// `params`, they will continue to use the most-recently bound value from a previous call to
|
||||
/// `query_named`, or `NULL` if they have never been bound.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// fn query(conn: &Connection) -> Result<()> {
|
||||
/// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
|
||||
/// let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
|
||||
/// while let Some(row) = rows.next() {
|
||||
/// // ...
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> Result<Rows<'a>> {
|
||||
try!(self.bind_parameters_named(params));
|
||||
Ok(Rows::new(self))
|
||||
}
|
||||
|
||||
/// Executes the prepared statement and maps a function over the resulting rows, returning
|
||||
/// an iterator over the mapped function results.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
|
||||
/// let mut stmt = try!(conn.prepare("SELECT name FROM people"));
|
||||
/// let rows = try!(stmt.query_map(&[], |row| row.get(0)));
|
||||
///
|
||||
/// let mut names = Vec::new();
|
||||
/// for name_result in rows {
|
||||
/// names.push(try!(name_result));
|
||||
/// }
|
||||
///
|
||||
/// Ok(names)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) -> Result<MappedRows<'a, F>>
|
||||
where F: FnMut(&Row) -> T
|
||||
{
|
||||
let rows = self.query(params)?;
|
||||
Ok(MappedRows::new(rows, f))
|
||||
}
|
||||
|
||||
/// Execute the prepared statement with named parameter(s), returning an iterator over the
|
||||
/// result of calling the mapping function over the query's rows. If any parameters that were
|
||||
/// in the prepared statement are not included in `params`, they will continue to use the
|
||||
/// most-recently bound value from a previous call to `query_named`, or `NULL` if they have
|
||||
/// never been bound.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
|
||||
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
|
||||
/// let rows = try!(stmt.query_map_named(&[(":id", &"one")], |row| row.get(0)));
|
||||
///
|
||||
/// let mut names = Vec::new();
|
||||
/// for name_result in rows {
|
||||
/// names.push(try!(name_result));
|
||||
/// }
|
||||
///
|
||||
/// Ok(names)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_map_named<'a, T, F>(&'a mut self,
|
||||
params: &[(&str, &ToSql)],
|
||||
f: F)
|
||||
-> Result<MappedRows<'a, F>>
|
||||
where F: FnMut(&Row) -> T
|
||||
{
|
||||
let rows = self.query_named(params)?;
|
||||
Ok(MappedRows::new(rows, f))
|
||||
}
|
||||
|
||||
/// Executes the prepared statement and maps a function over the resulting
|
||||
/// rows, where the function returns a `Result` with `Error` type implementing
|
||||
/// `std::convert::From<Error>` (so errors can be unified).
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_and_then<'a, T, E, F>(&'a mut self,
|
||||
params: &[&ToSql],
|
||||
f: F)
|
||||
-> Result<AndThenRows<'a, F>>
|
||||
where E: convert::From<Error>,
|
||||
F: FnMut(&Row) -> result::Result<T, E>
|
||||
{
|
||||
let rows = self.query(params)?;
|
||||
Ok(AndThenRows::new(rows, f))
|
||||
}
|
||||
|
||||
/// Execute the prepared statement with named parameter(s), returning an iterator over the
|
||||
/// result of calling the mapping function over the query's rows. If any parameters that were
|
||||
/// in the prepared statement are not included in `params`, they will continue to use the
|
||||
/// most-recently bound value from a previous call to `query_named`, or `NULL` if they have
|
||||
/// never been bound.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// struct Person { name: String };
|
||||
///
|
||||
/// fn name_to_person(name: String) -> Result<Person> {
|
||||
/// // ... check for valid name
|
||||
/// Ok(Person{ name: name })
|
||||
/// }
|
||||
///
|
||||
/// fn get_names(conn: &Connection) -> Result<Vec<Person>> {
|
||||
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
|
||||
/// let rows = try!(stmt.query_and_then_named(&[(":id", &"one")], |row| {
|
||||
/// name_to_person(row.get(0))
|
||||
/// }));
|
||||
///
|
||||
/// let mut persons = Vec::new();
|
||||
/// for person_result in rows {
|
||||
/// persons.push(try!(person_result));
|
||||
/// }
|
||||
///
|
||||
/// Ok(persons)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Failure
|
||||
///
|
||||
/// Will return `Err` if binding parameters fails.
|
||||
pub fn query_and_then_named<'a, T, E, F>(&'a mut self,
|
||||
params: &[(&str, &ToSql)],
|
||||
f: F)
|
||||
-> Result<AndThenRows<'a, F>>
|
||||
where E: convert::From<Error>,
|
||||
F: FnMut(&Row) -> result::Result<T, E>
|
||||
{
|
||||
let rows = self.query_named(params)?;
|
||||
Ok(AndThenRows::new(rows, f))
|
||||
}
|
||||
|
||||
/// Return `true` if a query in the SQL statement it executes returns one or more rows
|
||||
/// and `false` if the SQL returns an empty set.
|
||||
pub fn exists(&mut self, params: &[&ToSql]) -> Result<bool> {
|
||||
let mut rows = try!(self.query(params));
|
||||
let exists = {
|
||||
match rows.next() {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
}
|
||||
};
|
||||
Ok(exists)
|
||||
}
|
||||
|
||||
/// Convenience method to execute a query that is expected to return a single row.
|
||||
///
|
||||
/// If the query returns more than one row, all rows except the first are ignored.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if the underlying SQLite call fails.
|
||||
pub fn query_row<T, F>(&mut self, params: &[&ToSql], f: F) -> Result<T>
|
||||
where F: FnOnce(&Row) -> T
|
||||
{
|
||||
let mut rows = try!(self.query(params));
|
||||
|
||||
rows.get_expected_row().map(|r| f(&r))
|
||||
}
|
||||
|
||||
/// Consumes the statement.
|
||||
///
|
||||
/// Functionally equivalent to the `Drop` implementation, but allows callers to see any errors
|
||||
/// that occur.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return `Err` if the underlying SQLite call fails.
|
||||
pub fn finalize(mut self) -> Result<()> {
|
||||
self.finalize_()
|
||||
}
|
||||
|
||||
/// Return the index of an SQL parameter given its name.
|
||||
///
|
||||
/// # Failure
|
||||
///
|
||||
/// Will return Err if `name` is invalid. Will return Ok(None) if the name
|
||||
/// is valid but not a bound parameter of this statement.
|
||||
pub fn parameter_index(&self, name: &str) -> Result<Option<i32>> {
|
||||
let c_name = try!(str_to_cstring(name));
|
||||
Ok(self.stmt.bind_parameter_index(&c_name))
|
||||
}
|
||||
|
||||
fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> {
|
||||
assert!(params.len() as c_int == self.stmt.bind_parameter_count(),
|
||||
"incorrect number of parameters to query(): expected {}, got {}",
|
||||
self.stmt.bind_parameter_count(),
|
||||
params.len());
|
||||
|
||||
for (i, p) in params.iter().enumerate() {
|
||||
try!(self.bind_parameter(*p, (i + 1) as c_int));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> {
|
||||
for &(name, value) in params {
|
||||
if let Some(i) = try!(self.parameter_index(name)) {
|
||||
try!(self.bind_parameter(value, i));
|
||||
} else {
|
||||
return Err(Error::InvalidParameterName(name.into()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bind_parameter(&self, param: &ToSql, col: c_int) -> Result<()> {
|
||||
let value = try!(param.to_sql());
|
||||
|
||||
let ptr = unsafe { self.stmt.ptr() };
|
||||
let value = match value {
|
||||
ToSqlOutput::Borrowed(v) => v,
|
||||
ToSqlOutput::Owned(ref v) => ValueRef::from(v),
|
||||
|
||||
#[cfg(feature = "blob")]
|
||||
ToSqlOutput::ZeroBlob(len) => {
|
||||
return self.conn
|
||||
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) });
|
||||
}
|
||||
};
|
||||
self.conn
|
||||
.decode_result(match value {
|
||||
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col) },
|
||||
ValueRef::Integer(i) => unsafe {
|
||||
ffi::sqlite3_bind_int64(ptr, col, i)
|
||||
},
|
||||
ValueRef::Real(r) => unsafe {
|
||||
ffi::sqlite3_bind_double(ptr, col, r)
|
||||
},
|
||||
ValueRef::Text(s) => unsafe {
|
||||
let length = s.len();
|
||||
if length > ::std::i32::MAX as usize {
|
||||
ffi::SQLITE_TOOBIG
|
||||
} else {
|
||||
let c_str = try!(str_to_cstring(s));
|
||||
let destructor = if length > 0 {
|
||||
ffi::SQLITE_TRANSIENT()
|
||||
} else {
|
||||
ffi::SQLITE_STATIC()
|
||||
};
|
||||
ffi::sqlite3_bind_text(ptr, col, c_str.as_ptr(), length as c_int, destructor)
|
||||
}
|
||||
},
|
||||
ValueRef::Blob(b) => unsafe {
|
||||
let length = b.len();
|
||||
if length > ::std::i32::MAX as usize {
|
||||
ffi::SQLITE_TOOBIG
|
||||
} else if length == 0 {
|
||||
ffi::sqlite3_bind_zeroblob(ptr, col, 0)
|
||||
} else {
|
||||
ffi::sqlite3_bind_blob(ptr,
|
||||
col,
|
||||
b.as_ptr() as *const c_void,
|
||||
length as c_int,
|
||||
ffi::SQLITE_TRANSIENT())
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn execute_with_bound_parameters(&mut self) -> Result<c_int> {
|
||||
let r = self.stmt.step();
|
||||
self.stmt.reset();
|
||||
match r {
|
||||
ffi::SQLITE_DONE => {
|
||||
if self.column_count() == 0 {
|
||||
Ok(self.conn.changes())
|
||||
} else {
|
||||
Err(Error::ExecuteReturnedResults)
|
||||
}
|
||||
}
|
||||
ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults),
|
||||
_ => Err(self.conn.decode_result(r).unwrap_err()),
|
||||
}
|
||||
}
|
||||
|
||||
fn finalize_(&mut self) -> Result<()> {
|
||||
let mut stmt = RawStatement::new(ptr::null_mut());
|
||||
mem::swap(&mut stmt, &mut self.stmt);
|
||||
self.conn.decode_result(stmt.finalize())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'conn> Into<RawStatement> for Statement<'conn> {
|
||||
fn into(mut self) -> RawStatement {
|
||||
let mut stmt = RawStatement::new(ptr::null_mut());
|
||||
mem::swap(&mut stmt, &mut self.stmt);
|
||||
stmt
|
||||
}
|
||||
}
|
||||
|
||||
impl<'conn> fmt::Debug for Statement<'conn> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let sql = str::from_utf8(self.stmt.sql().to_bytes());
|
||||
f.debug_struct("Statement")
|
||||
.field("conn", self.conn)
|
||||
.field("stmt", &self.stmt)
|
||||
.field("sql", &sql)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'conn> Drop for Statement<'conn> {
|
||||
#[allow(unused_must_use)]
|
||||
fn drop(&mut self) {
|
||||
self.finalize_();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This trait lets us have "pub(crate)" visibility on some Statement methods. Remove this
|
||||
// once pub(crate) is stable.
|
||||
pub trait StatementCrateImpl<'conn> {
|
||||
fn new(conn: &'conn Connection, stmt: RawStatement) -> Self;
|
||||
fn value_ref(&self, col: c_int) -> ValueRef;
|
||||
fn step(&self) -> Result<bool>;
|
||||
fn reset(&self) -> c_int;
|
||||
}
|
||||
|
||||
impl<'conn> StatementCrateImpl<'conn> for Statement<'conn> {
|
||||
fn new(conn: &Connection, stmt: RawStatement) -> Statement {
|
||||
Statement {
|
||||
conn: conn,
|
||||
stmt: stmt,
|
||||
}
|
||||
}
|
||||
|
||||
fn value_ref(&self, col: c_int) -> ValueRef {
|
||||
let raw = unsafe { self.stmt.ptr() };
|
||||
|
||||
match self.stmt.column_type(col) {
|
||||
ffi::SQLITE_NULL => ValueRef::Null,
|
||||
ffi::SQLITE_INTEGER => {
|
||||
ValueRef::Integer(unsafe { ffi::sqlite3_column_int64(raw, col) })
|
||||
}
|
||||
ffi::SQLITE_FLOAT => ValueRef::Real(unsafe { ffi::sqlite3_column_double(raw, col) }),
|
||||
ffi::SQLITE_TEXT => {
|
||||
let s = unsafe {
|
||||
let text = ffi::sqlite3_column_text(raw, col);
|
||||
assert!(!text.is_null(),
|
||||
"unexpected SQLITE_TEXT column type with NULL data");
|
||||
CStr::from_ptr(text as *const c_char)
|
||||
};
|
||||
|
||||
// sqlite3_column_text returns UTF8 data, so our unwrap here should be fine.
|
||||
let s = s.to_str()
|
||||
.expect("sqlite3_column_text returned invalid UTF-8");
|
||||
ValueRef::Text(s)
|
||||
}
|
||||
ffi::SQLITE_BLOB => {
|
||||
let (blob, len) = unsafe {
|
||||
(ffi::sqlite3_column_blob(raw, col), ffi::sqlite3_column_bytes(raw, col))
|
||||
};
|
||||
|
||||
assert!(len >= 0,
|
||||
"unexpected negative return from sqlite3_column_bytes");
|
||||
if len > 0 {
|
||||
assert!(!blob.is_null(),
|
||||
"unexpected SQLITE_BLOB column type with NULL data");
|
||||
ValueRef::Blob(unsafe { from_raw_parts(blob as *const u8, len as usize) })
|
||||
} else {
|
||||
// The return value from sqlite3_column_blob() for a zero-length BLOB
|
||||
// is a NULL pointer.
|
||||
ValueRef::Blob(&[])
|
||||
}
|
||||
}
|
||||
_ => unreachable!("sqlite3_column_type returned invalid value"),
|
||||
}
|
||||
}
|
||||
|
||||
fn step(&self) -> Result<bool> {
|
||||
match self.stmt.step() {
|
||||
ffi::SQLITE_ROW => Ok(true),
|
||||
ffi::SQLITE_DONE => Ok(false),
|
||||
code => Err(self.conn.decode_result(code).unwrap_err()),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&self) -> c_int {
|
||||
self.stmt.reset()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {Connection, Error, Result};
|
||||
|
||||
#[test]
|
||||
fn test_execute_named() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
|
||||
|
||||
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])
|
||||
.unwrap(),
|
||||
1);
|
||||
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
|
||||
.unwrap(),
|
||||
1);
|
||||
|
||||
assert_eq!(3i32,
|
||||
db.query_row_named::<i32, _>("SELECT SUM(x) FROM foo WHERE x > :x",
|
||||
&[(":x", &0i32)],
|
||||
|r| r.get(0))
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stmt_execute_named() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
|
||||
INTEGER)";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")
|
||||
.unwrap();
|
||||
stmt.execute_named(&[(":name", &"one")]).unwrap();
|
||||
|
||||
assert_eq!(1i32,
|
||||
db.query_row_named::<i32, _>("SELECT COUNT(*) FROM test WHERE name = :name",
|
||||
&[(":name", &"one")],
|
||||
|r| r.get(0))
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_named() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = r#"
|
||||
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
|
||||
INSERT INTO test(id, name) VALUES (1, "one");
|
||||
"#;
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name")
|
||||
.unwrap();
|
||||
let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap();
|
||||
|
||||
let id: i32 = rows.next().unwrap().unwrap().get(0);
|
||||
assert_eq!(1, id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_map_named() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = r#"
|
||||
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
|
||||
INSERT INTO test(id, name) VALUES (1, "one");
|
||||
"#;
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name")
|
||||
.unwrap();
|
||||
let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| {
|
||||
let id: i32 = row.get(0);
|
||||
2 * id
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let doubled_id: i32 = rows.next().unwrap().unwrap();
|
||||
assert_eq!(2, doubled_id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_and_then_named() {
|
||||
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = r#"
|
||||
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
|
||||
INSERT INTO test(id, name) VALUES (1, "one");
|
||||
INSERT INTO test(id, name) VALUES (2, "one");
|
||||
"#;
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")
|
||||
.unwrap();
|
||||
let mut rows = stmt.query_and_then_named(&[(":name", &"one")], |row| {
|
||||
let id: i32 = row.get(0);
|
||||
if id == 1 {
|
||||
Ok(id)
|
||||
} else {
|
||||
Err(Error::SqliteSingleThreadedMode)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// first row should be Ok
|
||||
let doubled_id: i32 = rows.next().unwrap().unwrap();
|
||||
assert_eq!(1, doubled_id);
|
||||
|
||||
// second row should be Err
|
||||
match rows.next().unwrap() {
|
||||
Ok(_) => panic!("invalid Ok"),
|
||||
Err(Error::SqliteSingleThreadedMode) => (),
|
||||
Err(_) => panic!("invalid Err"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unbound_parameters_are_null() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
|
||||
.unwrap();
|
||||
stmt.execute_named(&[(":x", &"one")]).unwrap();
|
||||
|
||||
let result: Option<String> =
|
||||
db.query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0))
|
||||
.unwrap();
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unbound_parameters_are_reused() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
|
||||
db.execute_batch(sql).unwrap();
|
||||
|
||||
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
|
||||
.unwrap();
|
||||
stmt.execute_named(&[(":x", &"one")]).unwrap();
|
||||
stmt.execute_named(&[(":y", &"two")]).unwrap();
|
||||
|
||||
let result: String =
|
||||
db.query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(result, "one");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")
|
||||
.unwrap();
|
||||
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")
|
||||
.unwrap();
|
||||
assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
|
||||
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
|
||||
match stmt.insert(&[&1i32]).unwrap_err() {
|
||||
Error::StatementChangedRows(0) => (),
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
|
||||
.unwrap();
|
||||
match multi.insert(&[]).unwrap_err() {
|
||||
Error::StatementChangedRows(2) => (),
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_different_tables() {
|
||||
// Test for https://github.com/jgallagher/rusqlite/issues/171
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch(r"
|
||||
CREATE TABLE foo(x INTEGER);
|
||||
CREATE TABLE bar(x INTEGER);
|
||||
")
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(db.prepare("INSERT INTO foo VALUES (10)")
|
||||
.unwrap()
|
||||
.insert(&[])
|
||||
.unwrap(),
|
||||
1);
|
||||
assert_eq!(db.prepare("INSERT INTO bar VALUES (10)")
|
||||
.unwrap()
|
||||
.insert(&[])
|
||||
.unwrap(),
|
||||
1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exists() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = "BEGIN;
|
||||
CREATE TABLE foo(x INTEGER);
|
||||
INSERT INTO foo VALUES(1);
|
||||
INSERT INTO foo VALUES(2);
|
||||
END;";
|
||||
db.execute_batch(sql).unwrap();
|
||||
let mut stmt = db.prepare("SELECT 1 FROM foo WHERE x = ?").unwrap();
|
||||
assert!(stmt.exists(&[&1i32]).unwrap());
|
||||
assert!(stmt.exists(&[&2i32]).unwrap());
|
||||
assert!(!stmt.exists(&[&0i32]).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_row() {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
let sql = "BEGIN;
|
||||
CREATE TABLE foo(x INTEGER, y INTEGER);
|
||||
INSERT INTO foo VALUES(1, 3);
|
||||
INSERT INTO foo VALUES(2, 4);
|
||||
END;";
|
||||
db.execute_batch(sql).unwrap();
|
||||
let mut stmt = db.prepare("SELECT y FROM foo WHERE x = ?").unwrap();
|
||||
let y: Result<i64> = stmt.query_row(&[&1i32], |r| r.get(0));
|
||||
assert_eq!(3i64, y.unwrap());
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
//! Tracing and profiling functions. Error and warning log.
|
||||
|
||||
use libc::{c_char, c_int, c_void};
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
@ -44,8 +44,8 @@ pub type SqliteTransaction<'conn> = Transaction<'conn>;
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
|
||||
/// let tx = try!(conn.transaction());
|
||||
///
|
||||
@ -73,8 +73,8 @@ pub struct Transaction<'conn> {
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
|
||||
/// let sp = try!(conn.savepoint());
|
||||
///
|
||||
@ -100,13 +100,14 @@ impl<'conn> Transaction<'conn> {
|
||||
TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
|
||||
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
|
||||
};
|
||||
conn.execute_batch(query).map(move |_| {
|
||||
Transaction {
|
||||
conn: conn,
|
||||
drop_behavior: DropBehavior::Rollback,
|
||||
committed: false,
|
||||
}
|
||||
})
|
||||
conn.execute_batch(query)
|
||||
.map(move |_| {
|
||||
Transaction {
|
||||
conn: conn,
|
||||
drop_behavior: DropBehavior::Rollback,
|
||||
committed: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Starts a new [savepoint](http://www.sqlite.org/lang_savepoint.html), allowing nested
|
||||
@ -120,7 +121,7 @@ impl<'conn> Transaction<'conn> {
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// # fn perform_queries_part_1_succeeds(conn: &Connection) -> bool { true }
|
||||
/// # fn perform_queries_part_1_succeeds(_conn: &Connection) -> bool { true }
|
||||
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
|
||||
/// let mut tx = try!(conn.transaction());
|
||||
///
|
||||
@ -216,15 +217,16 @@ impl<'conn> Savepoint<'conn> {
|
||||
name: T)
|
||||
-> Result<Savepoint> {
|
||||
let name = name.into();
|
||||
conn.execute_batch(&format!("SAVEPOINT {}", name)).map(|_| {
|
||||
Savepoint {
|
||||
conn: conn,
|
||||
name: name,
|
||||
depth: depth,
|
||||
drop_behavior: DropBehavior::Rollback,
|
||||
committed: false,
|
||||
}
|
||||
})
|
||||
conn.execute_batch(&format!("SAVEPOINT {}", name))
|
||||
.map(|_| {
|
||||
Savepoint {
|
||||
conn: conn,
|
||||
name: name,
|
||||
depth: depth,
|
||||
drop_behavior: DropBehavior::Rollback,
|
||||
committed: false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn with_depth(conn: &Connection, depth: u32) -> Result<Savepoint> {
|
||||
@ -269,7 +271,8 @@ impl<'conn> Savepoint<'conn> {
|
||||
|
||||
fn commit_(&mut self) -> Result<()> {
|
||||
self.committed = true;
|
||||
self.conn.execute_batch(&format!("RELEASE {}", self.name))
|
||||
self.conn
|
||||
.execute_batch(&format!("RELEASE {}", self.name))
|
||||
}
|
||||
|
||||
/// A convenience method which rolls back a savepoint.
|
||||
@ -279,7 +282,8 @@ impl<'conn> Savepoint<'conn> {
|
||||
/// Unlike `Transaction`s, savepoints remain active after they have been rolled back,
|
||||
/// and can be rolled back again or committed.
|
||||
pub fn rollback(&mut self) -> Result<()> {
|
||||
self.conn.execute_batch(&format!("ROLLBACK TO {}", self.name))
|
||||
self.conn
|
||||
.execute_batch(&format!("ROLLBACK TO {}", self.name))
|
||||
}
|
||||
|
||||
/// Consumes the savepoint, committing or rolling back according to the current setting
|
||||
@ -328,8 +332,8 @@ impl Connection {
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
|
||||
/// let tx = try!(conn.transaction());
|
||||
///
|
||||
@ -369,8 +373,8 @@ impl Connection {
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
|
||||
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
|
||||
/// let sp = try!(conn.savepoint());
|
||||
///
|
||||
@ -401,7 +405,6 @@ impl Connection {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg_attr(feature="clippy", allow(similar_names))]
|
||||
mod test {
|
||||
use Connection;
|
||||
use super::DropBehavior;
|
||||
@ -428,7 +431,8 @@ mod test {
|
||||
{
|
||||
let tx = db.transaction().unwrap();
|
||||
assert_eq!(2i32,
|
||||
tx.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
|
||||
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@ -454,7 +458,8 @@ mod test {
|
||||
{
|
||||
let tx = db.transaction().unwrap();
|
||||
assert_eq!(6i32,
|
||||
tx.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
|
||||
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,7 +553,8 @@ mod test {
|
||||
}
|
||||
|
||||
fn assert_current_sum(x: i32, conn: &Connection) {
|
||||
let i = conn.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let i = conn.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(x, i);
|
||||
}
|
||||
}
|
||||
|
@ -4,94 +4,96 @@ extern crate chrono;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, UTC, Local};
|
||||
use libc::c_int;
|
||||
|
||||
use {Error, Result};
|
||||
use types::{FromSql, ToSql, ValueRef};
|
||||
|
||||
use ffi::sqlite3_stmt;
|
||||
use Result;
|
||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||
|
||||
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
|
||||
impl ToSql for NaiveDate {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
let date_str = self.format("%Y-%m-%d").to_string();
|
||||
date_str.bind_parameter(stmt, col)
|
||||
Ok(ToSqlOutput::from(date_str))
|
||||
}
|
||||
}
|
||||
|
||||
/// "YYYY-MM-DD" => ISO 8601 calendar date without timezone.
|
||||
impl FromSql for NaiveDate {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
value.as_str().and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(Error::FromSqlConversionFailure(Box::new(err))),
|
||||
})
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
value
|
||||
.as_str()
|
||||
.and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(FromSqlError::Other(Box::new(err))),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// ISO 8601 time without timezone => "HH:MM:SS.SSS"
|
||||
impl ToSql for NaiveTime {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
let date_str = self.format("%H:%M:%S%.f").to_string();
|
||||
date_str.bind_parameter(stmt, col)
|
||||
Ok(ToSqlOutput::from(date_str))
|
||||
}
|
||||
}
|
||||
|
||||
/// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone.
|
||||
impl FromSql for NaiveTime {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
value.as_str().and_then(|s| {
|
||||
let fmt = match s.len() {
|
||||
5 => "%H:%M",
|
||||
8 => "%H:%M:%S",
|
||||
_ => "%H:%M:%S%.f",
|
||||
};
|
||||
match NaiveTime::parse_from_str(s, fmt) {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(Error::FromSqlConversionFailure(Box::new(err))),
|
||||
}
|
||||
})
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
value
|
||||
.as_str()
|
||||
.and_then(|s| {
|
||||
let fmt = match s.len() {
|
||||
5 => "%H:%M",
|
||||
8 => "%H:%M:%S",
|
||||
_ => "%H:%M:%S%.f",
|
||||
};
|
||||
match NaiveTime::parse_from_str(s, fmt) {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(FromSqlError::Other(Box::new(err))),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// ISO 8601 combined date and time without timezone => "YYYY-MM-DD HH:MM:SS.SSS"
|
||||
impl ToSql for NaiveDateTime {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
let date_str = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string();
|
||||
date_str.bind_parameter(stmt, col)
|
||||
Ok(ToSqlOutput::from(date_str))
|
||||
}
|
||||
}
|
||||
|
||||
/// "YYYY-MM-DD HH:MM:SS"/"YYYY-MM-DD HH:MM:SS.SSS" => ISO 8601 combined date and time
|
||||
/// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported)
|
||||
impl FromSql for NaiveDateTime {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
value.as_str().and_then(|s| {
|
||||
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
|
||||
"%Y-%m-%dT%H:%M:%S%.f"
|
||||
} else {
|
||||
"%Y-%m-%d %H:%M:%S%.f"
|
||||
};
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
value
|
||||
.as_str()
|
||||
.and_then(|s| {
|
||||
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
|
||||
"%Y-%m-%dT%H:%M:%S%.f"
|
||||
} else {
|
||||
"%Y-%m-%d %H:%M:%S%.f"
|
||||
};
|
||||
|
||||
match NaiveDateTime::parse_from_str(s, fmt) {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(Error::FromSqlConversionFailure(Box::new(err))),
|
||||
}
|
||||
})
|
||||
match NaiveDateTime::parse_from_str(s, fmt) {
|
||||
Ok(dt) => Ok(dt),
|
||||
Err(err) => Err(FromSqlError::Other(Box::new(err))),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Date and time with time zone => UTC RFC3339 timestamp ("YYYY-MM-DDTHH:MM:SS.SSS+00:00").
|
||||
impl<Tz: TimeZone> ToSql for DateTime<Tz> {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
let utc_dt = self.with_timezone(&UTC);
|
||||
utc_dt.to_rfc3339().bind_parameter(stmt, col)
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(ToSqlOutput::from(self.with_timezone(&UTC).to_rfc3339()))
|
||||
}
|
||||
}
|
||||
|
||||
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime<UTC>.
|
||||
impl FromSql for DateTime<UTC> {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
{
|
||||
// Try to parse value as rfc3339 first.
|
||||
let s = try!(value.as_str());
|
||||
@ -108,9 +110,8 @@ impl FromSql for DateTime<UTC> {
|
||||
Cow::Borrowed(s)
|
||||
};
|
||||
|
||||
match DateTime::parse_from_rfc3339(&s) {
|
||||
Ok(dt) => return Ok(dt.with_timezone(&UTC)),
|
||||
Err(_) => (),
|
||||
if let Ok(dt) = DateTime::parse_from_rfc3339(&s) {
|
||||
return Ok(dt.with_timezone(&UTC));
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,7 +122,7 @@ impl FromSql for DateTime<UTC> {
|
||||
|
||||
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into DateTime<Local>.
|
||||
impl FromSql for DateTime<Local> {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
let utc_dt = try!(DateTime::<UTC>::column_result(value));
|
||||
Ok(utc_dt.with_timezone(&Local))
|
||||
}
|
||||
@ -135,7 +136,8 @@ mod test {
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT, b BLOB)").unwrap();
|
||||
db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT, b BLOB)")
|
||||
.unwrap();
|
||||
db
|
||||
}
|
||||
|
||||
@ -143,11 +145,14 @@ mod test {
|
||||
fn test_naive_date() {
|
||||
let db = checked_memory_handle();
|
||||
let date = NaiveDate::from_ymd(2016, 2, 23);
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&date]).unwrap();
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&date])
|
||||
.unwrap();
|
||||
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!("2016-02-23", s);
|
||||
let t: NaiveDate = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let t: NaiveDate = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(date, t);
|
||||
}
|
||||
|
||||
@ -155,11 +160,14 @@ mod test {
|
||||
fn test_naive_time() {
|
||||
let db = checked_memory_handle();
|
||||
let time = NaiveTime::from_hms(23, 56, 4);
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&time]).unwrap();
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&time])
|
||||
.unwrap();
|
||||
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!("23:56:04", s);
|
||||
let v: NaiveTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let v: NaiveTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(time, v);
|
||||
}
|
||||
|
||||
@ -170,15 +178,20 @@ mod test {
|
||||
let time = NaiveTime::from_hms(23, 56, 4);
|
||||
let dt = NaiveDateTime::new(date, time);
|
||||
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&dt]).unwrap();
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&dt])
|
||||
.unwrap();
|
||||
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!("2016-02-23T23:56:04", s);
|
||||
let v: NaiveDateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let v: NaiveDateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(dt, v);
|
||||
|
||||
db.execute("UPDATE foo set b = datetime(t)", &[]).unwrap(); // "YYYY-MM-DD HH:MM:SS"
|
||||
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
db.execute("UPDATE foo set b = datetime(t)", &[])
|
||||
.unwrap(); // "YYYY-MM-DD HH:MM:SS"
|
||||
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(dt, hms);
|
||||
}
|
||||
|
||||
@ -190,12 +203,15 @@ mod test {
|
||||
let dt = NaiveDateTime::new(date, time);
|
||||
let utc = UTC.from_utc_datetime(&dt);
|
||||
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&utc]).unwrap();
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&utc])
|
||||
.unwrap();
|
||||
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!("2016-02-23T23:56:04.789+00:00", s);
|
||||
|
||||
let v1: DateTime<UTC> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let v1: DateTime<UTC> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(utc, v1);
|
||||
|
||||
let v2: DateTime<UTC> = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
|
||||
@ -207,7 +223,8 @@ mod test {
|
||||
assert_eq!(utc - Duration::milliseconds(789), v3);
|
||||
|
||||
let v4: DateTime<UTC> =
|
||||
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0)).unwrap();
|
||||
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(utc, v4);
|
||||
}
|
||||
|
||||
@ -219,13 +236,16 @@ mod test {
|
||||
let dt = NaiveDateTime::new(date, time);
|
||||
let local = Local.from_local_datetime(&dt).single().unwrap();
|
||||
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&local]).unwrap();
|
||||
db.execute("INSERT INTO foo (t) VALUES (?)", &[&local])
|
||||
.unwrap();
|
||||
|
||||
// Stored string should be in UTC
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert!(s.ends_with("+00:00"));
|
||||
|
||||
let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(local, v);
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,129 @@
|
||||
use super::{ValueRef, Value};
|
||||
use ::Result;
|
||||
use ::error::Error;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
/// A trait for types that can be created from a SQLite value.
|
||||
pub trait FromSql: Sized {
|
||||
fn column_result(value: ValueRef) -> Result<Self>;
|
||||
/// Enum listing possible errors from `FromSql` trait.
|
||||
#[derive(Debug)]
|
||||
pub enum FromSqlError {
|
||||
/// Error when an SQLite value is requested, but the type of the result cannot be converted to
|
||||
/// the requested Rust type.
|
||||
InvalidType,
|
||||
|
||||
/// Error when the i64 value returned by SQLite cannot be stored into the requested type.
|
||||
OutOfRange(i64),
|
||||
|
||||
/// An error case available for implementors of the `FromSql` trait.
|
||||
Other(Box<Error + Send + Sync>),
|
||||
}
|
||||
|
||||
impl FromSql for i32 {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
i64::column_result(value).map(|i| i as i32)
|
||||
impl fmt::Display for FromSqlError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
FromSqlError::InvalidType => write!(f, "Invalid type"),
|
||||
FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i),
|
||||
FromSqlError::Other(ref err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for FromSqlError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
FromSqlError::InvalidType => "invalid type",
|
||||
FromSqlError::OutOfRange(_) => "value out of range",
|
||||
FromSqlError::Other(ref err) => err.description(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature="clippy", allow(match_same_arms))]
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
match *self {
|
||||
FromSqlError::Other(ref err) => err.cause(),
|
||||
FromSqlError::InvalidType |
|
||||
FromSqlError::OutOfRange(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Result type for implementors of the `FromSql` trait.
|
||||
pub type FromSqlResult<T> = Result<T, FromSqlError>;
|
||||
|
||||
/// A trait for types that can be created from a SQLite value.
|
||||
///
|
||||
/// Note that `FromSql` and `ToSql` are defined for most integral types, but not `u64` or `usize`.
|
||||
/// This is intentional; SQLite returns integers as signed 64-bit values, which cannot fully
|
||||
/// represent the range of these types. Rusqlite would have to decide how to handle negative
|
||||
/// values: return an error or reinterpret as a very large postive numbers, neither of which is
|
||||
/// guaranteed to be correct for everyone. Callers can work around this by fetching values as i64
|
||||
/// and then doing the interpretation themselves or by defining a newtype and implementing
|
||||
/// `FromSql`/`ToSql` for it.
|
||||
pub trait FromSql: Sized {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self>;
|
||||
}
|
||||
|
||||
macro_rules! from_sql_integral(
|
||||
($t:ident) => (
|
||||
impl FromSql for $t {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
i64::column_result(value).and_then(|i| {
|
||||
if i < $t::min_value() as i64 || i > $t::max_value() as i64 {
|
||||
Err(FromSqlError::OutOfRange(i))
|
||||
} else {
|
||||
Ok(i as $t)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
from_sql_integral!(i8);
|
||||
from_sql_integral!(i16);
|
||||
from_sql_integral!(i32);
|
||||
from_sql_integral!(isize);
|
||||
from_sql_integral!(u8);
|
||||
from_sql_integral!(u16);
|
||||
from_sql_integral!(u32);
|
||||
|
||||
impl FromSql for i64 {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
value.as_i64()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for f64 {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
match value {
|
||||
ValueRef::Integer(i) => Ok(i as f64),
|
||||
ValueRef::Real(f) => Ok(f),
|
||||
_ => Err(Error::InvalidColumnType),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for bool {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
i64::column_result(value).map(|i| match i {
|
||||
0 => false,
|
||||
_ => true,
|
||||
})
|
||||
0 => false,
|
||||
_ => true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for String {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
value.as_str().map(|s| s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for Vec<u8> {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
value.as_blob().map(|b| b.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: FromSql> FromSql for Option<T> {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
match value {
|
||||
ValueRef::Null => Ok(None),
|
||||
_ => FromSql::column_result(value).map(Some),
|
||||
@ -60,7 +132,51 @@ impl<T: FromSql> FromSql for Option<T> {
|
||||
}
|
||||
|
||||
impl FromSql for Value {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
Ok(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {Connection, Error};
|
||||
use super::FromSql;
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
Connection::open_in_memory().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_integral_ranges() {
|
||||
let db = checked_memory_handle();
|
||||
|
||||
fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64])
|
||||
where T: Into<i64> + FromSql + ::std::fmt::Debug
|
||||
{
|
||||
for n in out_of_range {
|
||||
let err = db.query_row("SELECT ?", &[n], |r| r.get_checked::<_, T>(0))
|
||||
.unwrap()
|
||||
.unwrap_err();
|
||||
match err {
|
||||
Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
|
||||
_ => panic!("unexpected error: {}", err),
|
||||
}
|
||||
}
|
||||
for n in in_range {
|
||||
assert_eq!(*n,
|
||||
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
|
||||
.unwrap()
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]);
|
||||
check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
|
||||
check_ranges::<i32>(&db,
|
||||
&[-2147483649, 2147483648],
|
||||
&[-2147483648, -1, 0, 1, 2147483647]);
|
||||
check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]);
|
||||
check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]);
|
||||
check_ranges::<u32>(&db, &[-2, -1, 4294967296], &[0, 1, 4294967295]);
|
||||
}
|
||||
}
|
||||
|
135
src/types/mod.rs
135
src/types/mod.rs
@ -17,19 +17,17 @@
|
||||
//! truncates timespecs to the nearest second. If you want different storage for timespecs, you can
|
||||
//! use a newtype. For example, to store timespecs as `f64`s:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! ```rust
|
||||
//! extern crate rusqlite;
|
||||
//! extern crate libc;
|
||||
//! extern crate time;
|
||||
//!
|
||||
//! use rusqlite::types::{FromSql, ToSql, sqlite3_stmt};
|
||||
//! use rusqlite::types::{FromSql, FromSqlResult, ValueRef, ToSql, ToSqlOutput};
|
||||
//! use rusqlite::{Result};
|
||||
//! use libc::c_int;
|
||||
//! use time;
|
||||
//!
|
||||
//! pub struct TimespecSql(pub time::Timespec);
|
||||
//!
|
||||
//! impl FromSql for TimespecSql {
|
||||
//! fn column_result(value: ValueRef) -> Result<Self> {
|
||||
//! fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
//! f64::column_result(value).map(|as_f64| {
|
||||
//! TimespecSql(time::Timespec{ sec: as_f64.trunc() as i64,
|
||||
//! nsec: (as_f64.fract() * 1.0e9) as i32 })
|
||||
@ -38,24 +36,29 @@
|
||||
//! }
|
||||
//!
|
||||
//! impl ToSql for TimespecSql {
|
||||
//! unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
//! fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
//! let TimespecSql(ts) = *self;
|
||||
//! let as_f64 = ts.sec as f64 + (ts.nsec as f64) / 1.0e9;
|
||||
//! as_f64.bind_parameter(stmt, col)
|
||||
//! Ok(as_f64.into())
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! # // Prevent this doc test from being wrapped in a `fn main()` so that it will compile.
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! `ToSql` and `FromSql` are also implemented for `Option<T>` where `T` implements `ToSql` or
|
||||
//! `FromSql` for the cases where you want to know if a value was NULL (which gets translated to
|
||||
//! `None`).
|
||||
|
||||
pub use ffi::sqlite3_stmt;
|
||||
|
||||
pub use self::from_sql::FromSql;
|
||||
pub use self::to_sql::ToSql;
|
||||
pub use self::from_sql::{FromSql, FromSqlError, FromSqlResult};
|
||||
pub use self::to_sql::{ToSql, ToSqlOutput};
|
||||
pub use self::value::Value;
|
||||
pub use self::value_ref::ValueRef;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
mod value;
|
||||
mod value_ref;
|
||||
mod from_sql;
|
||||
mod to_sql;
|
||||
@ -70,11 +73,10 @@ mod serde_json;
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # extern crate libc;
|
||||
/// # extern crate rusqlite;
|
||||
/// # use rusqlite::{Connection, Result};
|
||||
/// # use rusqlite::types::{Null};
|
||||
/// # use libc::{c_int};
|
||||
/// # use std::os::raw::{c_int};
|
||||
/// fn main() {
|
||||
/// }
|
||||
/// fn insert_null(conn: &Connection) -> Result<c_int> {
|
||||
@ -84,37 +86,41 @@ mod serde_json;
|
||||
#[derive(Copy,Clone)]
|
||||
pub struct Null;
|
||||
|
||||
/// Owning [dynamic type value](http://sqlite.org/datatype3.html). Value's type is typically
|
||||
/// dictated by SQLite (not by the caller).
|
||||
///
|
||||
/// See [`ValueRef`](enum.ValueRef.html) for a non-owning dynamic type value.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub enum Value {
|
||||
/// The value is a `NULL` value.
|
||||
pub enum Type {
|
||||
Null,
|
||||
/// The value is a signed integer.
|
||||
Integer(i64),
|
||||
/// The value is a floating point number.
|
||||
Real(f64),
|
||||
/// The value is a text string.
|
||||
Text(String),
|
||||
/// The value is a blob of data
|
||||
Blob(Vec<u8>),
|
||||
Integer,
|
||||
Real,
|
||||
Text,
|
||||
Blob,
|
||||
}
|
||||
|
||||
impl fmt::Display for Type {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Type::Null => write!(f, "Null"),
|
||||
Type::Integer => write!(f, "Integer"),
|
||||
Type::Real => write!(f, "Real"),
|
||||
Type::Text => write!(f, "Text"),
|
||||
Type::Blob => write!(f, "Blob"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg_attr(feature="clippy", allow(similar_names))]
|
||||
mod test {
|
||||
extern crate time;
|
||||
|
||||
use Connection;
|
||||
use Error;
|
||||
use libc::{c_int, c_double};
|
||||
use std::os::raw::{c_int, c_double};
|
||||
use std::f64::EPSILON;
|
||||
use super::Value;
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo (b BLOB, t TEXT, i INTEGER, f FLOAT, n)").unwrap();
|
||||
db.execute_batch("CREATE TABLE foo (b BLOB, t TEXT, i INTEGER, f FLOAT, n)")
|
||||
.unwrap();
|
||||
db
|
||||
}
|
||||
|
||||
@ -123,9 +129,11 @@ mod test {
|
||||
let db = checked_memory_handle();
|
||||
|
||||
let v1234 = vec![1u8, 2, 3, 4];
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234]).unwrap();
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234])
|
||||
.unwrap();
|
||||
|
||||
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(v, v1234);
|
||||
}
|
||||
|
||||
@ -134,9 +142,11 @@ mod test {
|
||||
let db = checked_memory_handle();
|
||||
|
||||
let empty = vec![];
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty]).unwrap();
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty])
|
||||
.unwrap();
|
||||
|
||||
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(v, empty);
|
||||
}
|
||||
|
||||
@ -145,12 +155,39 @@ mod test {
|
||||
let db = checked_memory_handle();
|
||||
|
||||
let s = "hello, world!";
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()]).unwrap();
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
|
||||
.unwrap();
|
||||
|
||||
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(from, s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string() {
|
||||
let db = checked_memory_handle();
|
||||
|
||||
let s = "hello, world!";
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()])
|
||||
.unwrap();
|
||||
|
||||
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(from, s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value() {
|
||||
let db = checked_memory_handle();
|
||||
|
||||
db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)])
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(10i64,
|
||||
db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0))
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_option() {
|
||||
let db = checked_memory_handle();
|
||||
@ -158,10 +195,13 @@ mod test {
|
||||
let s = Some("hello, world!");
|
||||
let b = Some(vec![1u8, 2, 3, 4]);
|
||||
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b]).unwrap();
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
|
||||
.unwrap();
|
||||
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b])
|
||||
.unwrap();
|
||||
|
||||
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC").unwrap();
|
||||
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")
|
||||
.unwrap();
|
||||
let mut rows = stmt.query(&[]).unwrap();
|
||||
|
||||
{
|
||||
@ -182,11 +222,10 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(feature="clippy", allow(cyclomatic_complexity))]
|
||||
fn test_mismatched_types() {
|
||||
fn is_invalid_column_type(err: Error) -> bool {
|
||||
match err {
|
||||
Error::InvalidColumnType => true,
|
||||
Error::InvalidColumnType(_, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -207,9 +246,15 @@ mod test {
|
||||
assert_eq!("text", row.get_checked::<i32, String>(1).unwrap());
|
||||
assert_eq!(1, row.get_checked::<i32, c_int>(2).unwrap());
|
||||
assert!((1.5 - row.get_checked::<i32, c_double>(3).unwrap()).abs() < EPSILON);
|
||||
assert!(row.get_checked::<i32, Option<c_int>>(4).unwrap().is_none());
|
||||
assert!(row.get_checked::<i32, Option<c_double>>(4).unwrap().is_none());
|
||||
assert!(row.get_checked::<i32, Option<String>>(4).unwrap().is_none());
|
||||
assert!(row.get_checked::<i32, Option<c_int>>(4)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
assert!(row.get_checked::<i32, Option<c_double>>(4)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
assert!(row.get_checked::<i32, Option<String>>(4)
|
||||
.unwrap()
|
||||
.is_none());
|
||||
|
||||
// check some invalid types
|
||||
|
||||
|
@ -1,30 +1,27 @@
|
||||
//! `ToSql` and `FromSql` implementation for JSON `Value`.
|
||||
extern crate serde_json;
|
||||
|
||||
use libc::c_int;
|
||||
use self::serde_json::Value;
|
||||
|
||||
use {Error, Result};
|
||||
use types::{FromSql, ToSql, ValueRef};
|
||||
|
||||
use ffi::sqlite3_stmt;
|
||||
use Result;
|
||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||
|
||||
/// Serialize JSON `Value` to text.
|
||||
impl ToSql for Value {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
let s = serde_json::to_string(self).unwrap();
|
||||
s.bind_parameter(stmt, col)
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(ToSqlOutput::from(serde_json::to_string(self).unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserialize text/blob to JSON `Value`.
|
||||
impl FromSql for Value {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
match value {
|
||||
ValueRef::Text(ref s) => serde_json::from_str(s),
|
||||
ValueRef::Blob(ref b) => serde_json::from_slice(b),
|
||||
_ => return Err(Error::InvalidColumnType),
|
||||
}.map_err(|err| Error::FromSqlConversionFailure(Box::new(err)))
|
||||
ValueRef::Text(s) => serde_json::from_str(s),
|
||||
ValueRef::Blob(b) => serde_json::from_slice(b),
|
||||
_ => return Err(FromSqlError::InvalidType),
|
||||
}
|
||||
.map_err(|err| FromSqlError::Other(Box::new(err)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +32,8 @@ mod test {
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo (t TEXT, b BLOB)").unwrap();
|
||||
db.execute_batch("CREATE TABLE foo (t TEXT, b BLOB)")
|
||||
.unwrap();
|
||||
db
|
||||
}
|
||||
|
||||
@ -49,9 +47,11 @@ mod test {
|
||||
&[&data, &json.as_bytes()])
|
||||
.unwrap();
|
||||
|
||||
let t: serde_json::Value = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let t: serde_json::Value = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(data, t);
|
||||
let b: serde_json::Value = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let b: serde_json::Value = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(data, b);
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,28 @@
|
||||
extern crate time;
|
||||
|
||||
use libc::c_int;
|
||||
use {Error, Result};
|
||||
use types::{FromSql, ToSql, ValueRef};
|
||||
|
||||
use ffi::sqlite3_stmt;
|
||||
use Result;
|
||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
|
||||
|
||||
const SQLITE_DATETIME_FMT: &'static str = "%Y-%m-%d %H:%M:%S";
|
||||
|
||||
impl ToSql for time::Timespec {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
let time_str = time::at_utc(*self).strftime(SQLITE_DATETIME_FMT).unwrap().to_string();
|
||||
time_str.bind_parameter(stmt, col)
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
let time_string = time::at_utc(*self)
|
||||
.strftime(SQLITE_DATETIME_FMT)
|
||||
.unwrap()
|
||||
.to_string();
|
||||
Ok(ToSqlOutput::from(time_string))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for time::Timespec {
|
||||
fn column_result(value: ValueRef) -> Result<Self> {
|
||||
value.as_str().and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
|
||||
Ok(tm) => Ok(tm.to_timespec()),
|
||||
Err(err) => Err(Error::FromSqlConversionFailure(Box::new(err))),
|
||||
})
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
value
|
||||
.as_str()
|
||||
.and_then(|s| match time::strptime(s, SQLITE_DATETIME_FMT) {
|
||||
Ok(tm) => Ok(tm.to_timespec()),
|
||||
Err(err) => Err(FromSqlError::Other(Box::new(err))),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +33,8 @@ mod test {
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)").unwrap();
|
||||
db.execute_batch("CREATE TABLE foo (t TEXT, i INTEGER, f FLOAT)")
|
||||
.unwrap();
|
||||
db
|
||||
}
|
||||
|
||||
@ -43,9 +46,11 @@ mod test {
|
||||
sec: 10_000,
|
||||
nsec: 0,
|
||||
};
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap();
|
||||
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
|
||||
.unwrap();
|
||||
|
||||
let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap();
|
||||
let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
|
||||
.unwrap();
|
||||
assert_eq!(from, ts);
|
||||
}
|
||||
}
|
||||
|
@ -1,95 +1,146 @@
|
||||
use std::mem;
|
||||
use super::{Null, Value, ValueRef};
|
||||
use Result;
|
||||
|
||||
use libc::{c_double, c_int};
|
||||
/// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub enum ToSqlOutput<'a> {
|
||||
/// A borrowed SQLite-representable value.
|
||||
Borrowed(ValueRef<'a>),
|
||||
|
||||
use super::Null;
|
||||
use ::{ffi, str_to_cstring};
|
||||
use ffi::sqlite3_stmt;
|
||||
/// An owned SQLite-representable value.
|
||||
Owned(Value),
|
||||
|
||||
/// A BLOB of the given length that is filled with zeroes.
|
||||
#[cfg(feature = "blob")]
|
||||
ZeroBlob(i32),
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
|
||||
where &'a T: Into<ValueRef<'a>>
|
||||
{
|
||||
fn from(t: &'a T) -> Self {
|
||||
ToSqlOutput::Borrowed(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Into<Value>> From<T> for ToSqlOutput<'a> {
|
||||
fn from(t: T) -> Self {
|
||||
ToSqlOutput::Owned(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToSql for ToSqlOutput<'a> {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(match *self {
|
||||
ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
|
||||
ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
|
||||
|
||||
#[cfg(feature = "blob")]
|
||||
ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for types that can be converted into SQLite values.
|
||||
pub trait ToSql {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int;
|
||||
fn to_sql(&self) -> Result<ToSqlOutput>;
|
||||
}
|
||||
|
||||
macro_rules! raw_to_impl(
|
||||
($t:ty, $f:ident) => (
|
||||
// We should be able to use a generic impl like this:
|
||||
//
|
||||
// impl<T: Copy> ToSql for T where T: Into<Value> {
|
||||
// fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
// Ok(ToSqlOutput::from((*self).into()))
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// instead of the following macro, but this runs afoul of
|
||||
// https://github.com/rust-lang/rust/issues/30191 and reports conflicting
|
||||
// implementations even when there aren't any.
|
||||
|
||||
macro_rules! to_sql_self(
|
||||
($t:ty) => (
|
||||
impl ToSql for $t {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
ffi::$f(stmt, col, *self)
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(ToSqlOutput::from(*self))
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
raw_to_impl!(c_int, sqlite3_bind_int); // i32
|
||||
raw_to_impl!(i64, sqlite3_bind_int64);
|
||||
raw_to_impl!(c_double, sqlite3_bind_double);
|
||||
to_sql_self!(Null);
|
||||
to_sql_self!(bool);
|
||||
to_sql_self!(i8);
|
||||
to_sql_self!(i16);
|
||||
to_sql_self!(i32);
|
||||
to_sql_self!(i64);
|
||||
to_sql_self!(isize);
|
||||
to_sql_self!(u8);
|
||||
to_sql_self!(u16);
|
||||
to_sql_self!(u32);
|
||||
to_sql_self!(f64);
|
||||
|
||||
impl ToSql for bool {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
if *self {
|
||||
ffi::sqlite3_bind_int(stmt, col, 1)
|
||||
} else {
|
||||
ffi::sqlite3_bind_int(stmt, col, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToSql for &'a str {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
let length = self.len();
|
||||
if length > ::std::i32::MAX as usize {
|
||||
return ffi::SQLITE_TOOBIG;
|
||||
}
|
||||
match str_to_cstring(self) {
|
||||
Ok(c_str) => {
|
||||
ffi::sqlite3_bind_text(stmt,
|
||||
col,
|
||||
c_str.as_ptr(),
|
||||
length as c_int,
|
||||
ffi::SQLITE_TRANSIENT())
|
||||
}
|
||||
Err(_) => ffi::SQLITE_MISUSE,
|
||||
}
|
||||
impl<'a, T: ?Sized> ToSql for &'a T
|
||||
where &'a T: Into<ToSqlOutput<'a>>
|
||||
{
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(ToSqlOutput::from((*self).into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for String {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
(&self[..]).bind_parameter(stmt, col)
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(ToSqlOutput::from(self.as_str()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToSql for &'a [u8] {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
if self.len() > ::std::i32::MAX as usize {
|
||||
return ffi::SQLITE_TOOBIG;
|
||||
}
|
||||
ffi::sqlite3_bind_blob(stmt,
|
||||
col,
|
||||
mem::transmute(self.as_ptr()),
|
||||
self.len() as c_int,
|
||||
ffi::SQLITE_TRANSIENT())
|
||||
impl ToSql for str {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(ToSqlOutput::from(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for Vec<u8> {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
(&self[..]).bind_parameter(stmt, col)
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(ToSqlOutput::from(self.as_slice()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for [u8] {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(ToSqlOutput::from(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for Value {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
Ok(ToSqlOutput::from(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToSql> ToSql for Option<T> {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput> {
|
||||
match *self {
|
||||
None => ffi::sqlite3_bind_null(stmt, col),
|
||||
Some(ref t) => t.bind_parameter(stmt, col),
|
||||
None => Ok(ToSqlOutput::from(Null)),
|
||||
Some(ref t) => t.to_sql(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for Null {
|
||||
unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int {
|
||||
ffi::sqlite3_bind_null(stmt, col)
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::ToSql;
|
||||
|
||||
fn is_to_sql<T: ToSql>() {}
|
||||
|
||||
#[test]
|
||||
fn test_integral_types() {
|
||||
is_to_sql::<i8>();
|
||||
is_to_sql::<i16>();
|
||||
is_to_sql::<i32>();
|
||||
is_to_sql::<i64>();
|
||||
is_to_sql::<u8>();
|
||||
is_to_sql::<u16>();
|
||||
is_to_sql::<u32>();
|
||||
}
|
||||
}
|
||||
|
85
src/types/value.rs
Normal file
85
src/types/value.rs
Normal file
@ -0,0 +1,85 @@
|
||||
use super::{Null, Type};
|
||||
|
||||
/// Owning [dynamic type value](http://sqlite.org/datatype3.html). Value's type is typically
|
||||
/// dictated by SQLite (not by the caller).
|
||||
///
|
||||
/// See [`ValueRef`](enum.ValueRef.html) for a non-owning dynamic type value.
|
||||
#[derive(Clone,Debug,PartialEq)]
|
||||
pub enum Value {
|
||||
/// The value is a `NULL` value.
|
||||
Null,
|
||||
/// The value is a signed integer.
|
||||
Integer(i64),
|
||||
/// The value is a floating point number.
|
||||
Real(f64),
|
||||
/// The value is a text string.
|
||||
Text(String),
|
||||
/// The value is a blob of data
|
||||
Blob(Vec<u8>),
|
||||
}
|
||||
|
||||
impl From<Null> for Value {
|
||||
fn from(_: Null) -> Value {
|
||||
Value::Null
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
fn from(i: bool) -> Value {
|
||||
Value::Integer(i as i64)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_i64(
|
||||
($t:ty) => (
|
||||
impl From<$t> for Value {
|
||||
fn from(i: $t) -> Value {
|
||||
Value::Integer(i as i64)
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
from_i64!(i8);
|
||||
from_i64!(i16);
|
||||
from_i64!(i32);
|
||||
from_i64!(isize);
|
||||
from_i64!(u8);
|
||||
from_i64!(u16);
|
||||
from_i64!(u32);
|
||||
|
||||
impl From<i64> for Value {
|
||||
fn from(i: i64) -> Value {
|
||||
Value::Integer(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
fn from(f: f64) -> Value {
|
||||
Value::Real(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
fn from(s: String) -> Value {
|
||||
Value::Text(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Value {
|
||||
fn from(v: Vec<u8>) -> Value {
|
||||
Value::Blob(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn data_type(&self) -> Type {
|
||||
match *self {
|
||||
Value::Null => Type::Null,
|
||||
Value::Integer(_) => Type::Integer,
|
||||
Value::Real(_) => Type::Real,
|
||||
Value::Text(_) => Type::Text,
|
||||
Value::Blob(_) => Type::Blob,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
use ::Result;
|
||||
use ::error::Error;
|
||||
use super::Value;
|
||||
use types::{FromSqlError, FromSqlResult};
|
||||
use super::{Value, Type};
|
||||
|
||||
/// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the
|
||||
/// memory backing this value is owned by SQLite.
|
||||
@ -20,40 +19,52 @@ pub enum ValueRef<'a> {
|
||||
Blob(&'a [u8]),
|
||||
}
|
||||
|
||||
impl<'a> ValueRef<'a> {
|
||||
pub fn data_type(&self) -> Type {
|
||||
match *self {
|
||||
ValueRef::Null => Type::Null,
|
||||
ValueRef::Integer(_) => Type::Integer,
|
||||
ValueRef::Real(_) => Type::Real,
|
||||
ValueRef::Text(_) => Type::Text,
|
||||
ValueRef::Blob(_) => Type::Blob,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ValueRef<'a> {
|
||||
/// If `self` is case `Integer`, returns the integral value. Otherwise, returns
|
||||
/// `Err(Error::InvalidColumnType)`.
|
||||
pub fn as_i64(&self) -> Result<i64> {
|
||||
pub fn as_i64(&self) -> FromSqlResult<i64> {
|
||||
match *self {
|
||||
ValueRef::Integer(i) => Ok(i),
|
||||
_ => Err(Error::InvalidColumnType),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
|
||||
/// If `self` is case `Real`, returns the floating point value. Otherwise, returns
|
||||
/// `Err(Error::InvalidColumnType)`.
|
||||
pub fn as_f64(&self) -> Result<f64> {
|
||||
pub fn as_f64(&self) -> FromSqlResult<f64> {
|
||||
match *self {
|
||||
ValueRef::Real(f) => Ok(f),
|
||||
_ => Err(Error::InvalidColumnType),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
|
||||
/// If `self` is case `Text`, returns the string value. Otherwise, returns
|
||||
/// `Err(Error::InvalidColumnType)`.
|
||||
pub fn as_str(&self) -> Result<&str> {
|
||||
pub fn as_str(&self) -> FromSqlResult<&str> {
|
||||
match *self {
|
||||
ValueRef::Text(ref t) => Ok(t),
|
||||
_ => Err(Error::InvalidColumnType),
|
||||
ValueRef::Text(t) => Ok(t),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
|
||||
/// If `self` is case `Blob`, returns the byte slice. Otherwise, returns
|
||||
/// `Err(Error::InvalidColumnType)`.
|
||||
pub fn as_blob(&self) -> Result<&[u8]> {
|
||||
pub fn as_blob(&self) -> FromSqlResult<&[u8]> {
|
||||
match *self {
|
||||
ValueRef::Blob(ref b) => Ok(b),
|
||||
_ => Err(Error::InvalidColumnType),
|
||||
ValueRef::Blob(b) => Ok(b),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,6 +81,18 @@ impl<'a> From<ValueRef<'a>> for Value {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for ValueRef<'a> {
|
||||
fn from(s: &str) -> ValueRef {
|
||||
ValueRef::Text(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a [u8]> for ValueRef<'a> {
|
||||
fn from(s: &[u8]) -> ValueRef {
|
||||
ValueRef::Blob(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Value> for ValueRef<'a> {
|
||||
fn from(value: &'a Value) -> ValueRef<'a> {
|
||||
match *value {
|
||||
|
18
src/version.rs
Normal file
18
src/version.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use ffi;
|
||||
use std::ffi::CStr;
|
||||
|
||||
/// Returns the SQLite version as an integer; e.g., `3016002` for version 3.16.2.
|
||||
///
|
||||
/// See [sqlite3_libversion_number()](https://www.sqlite.org/c3ref/libversion.html).
|
||||
pub fn version_number() -> i32 {
|
||||
unsafe { ffi::sqlite3_libversion_number() }
|
||||
}
|
||||
|
||||
/// Returns the SQLite version as a string; e.g., `"3.16.2"` for version 3.16.2.
|
||||
///
|
||||
/// See [sqlite3_libversion()](https://www.sqlite.org/c3ref/libversion.html).
|
||||
pub fn version() -> &'static str {
|
||||
let cstr = unsafe { CStr::from_ptr(ffi::sqlite3_libversion()) };
|
||||
cstr.to_str()
|
||||
.expect("SQLite version string is not valid UTF8 ?!")
|
||||
}
|
@ -1,13 +1,14 @@
|
||||
//! This file contains unit tests for rusqlite::trace::config_log. This function affects
|
||||
//! SQLite process-wide and so is not safe to run as a normal #[test] in the library.
|
||||
|
||||
#[macro_use] extern crate lazy_static;
|
||||
extern crate libc;
|
||||
#[cfg(feature = "trace")]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate rusqlite;
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
fn main() {
|
||||
use libc::c_int;
|
||||
use std::os::raw::c_int;
|
||||
use std::sync::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
|
@ -7,14 +7,17 @@ extern crate libsqlite3_sys as ffi;
|
||||
use rusqlite::Connection;
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_error_when_singlethread_mode() {
|
||||
// put SQLite into single-threaded mode
|
||||
unsafe {
|
||||
// 1 == SQLITE_CONFIG_SINGLETHREAD
|
||||
assert_eq!(ffi::sqlite3_config(1), ffi::SQLITE_OK);
|
||||
println!("{}", ffi::sqlite3_mutex_alloc(0) as u64);
|
||||
if ffi::sqlite3_config(ffi::SQLITE_CONFIG_SINGLETHREAD) != ffi::SQLITE_OK {
|
||||
return;
|
||||
}
|
||||
if ffi::sqlite3_initialize() != ffi::SQLITE_OK {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let result = Connection::open_in_memory();
|
||||
assert!(result.is_err());
|
||||
let _ = Connection::open_in_memory().unwrap();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user