Merge remote-tracking branch 'jgallagher/master' into doc-rs

This commit is contained in:
gwenn 2017-06-09 19:49:57 +02:00
commit 99ef10d599
52 changed files with 230316 additions and 3343 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
libsqlite3-sys/sqlite3 linguist-vendored

View File

@ -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"

View File

@ -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)

View File

@ -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"

View File

@ -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)

View File

@ -1,7 +1,7 @@
# Rusqlite
[![Travis Build Status](https://api.travis-ci.org/jgallagher/rusqlite.svg?branch=master)](https://travis-ci.org/jgallagher/rusqlite)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/jgallagher/rusqlite?branch=master&svg=true)](https://ci.appveyor.com/project/jgallagher/rusqlite)
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/jgallagher/rusqlite?branch=master&svg=true)](https://ci.appveyor.com/project/jgallagher/rusqlite) [![Latest Version](https://img.shields.io/crates/v/rusqlite.svg)](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

View File

@ -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

View File

@ -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());
}

View File

@ -1,5 +0,0 @@
extern crate pkg_config;
fn main() {
pkg_config::find_library("sqlite3").unwrap();
}

View File

@ -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 }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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));
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

View File

@ -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

View File

@ -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
View File

@ -0,0 +1 @@
#include "sqlite3.h"

View File

@ -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

View File

@ -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`.

View File

@ -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

View File

@ -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");
}
}

View File

@ -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());
}
}

View File

@ -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 {

View File

@ -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);
}
}

File diff suppressed because it is too large Load Diff

71
src/limits.rs Normal file
View 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));
}
}
}

View File

@ -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 })
}
}

View File

@ -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");
}
}

View File

@ -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
View 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
View 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());
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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]);
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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
View 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,
}
}
}

View File

@ -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
View 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 ?!")
}

View File

@ -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! {

View File

@ -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();
}