Merge remote-tracking branch 'jgallagher/master' into vtab

This commit is contained in:
gwenn 2017-03-08 20:35:07 +01:00
commit 7f0082e44e
49 changed files with 25702 additions and 9488 deletions

View File

@ -1,19 +1,43 @@
language: rust
sudo: false 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: script:
- cargo build - cargo build
- cargo build --features bundled - cargo build --features bundled
- cargo test - cargo test
- cargo test --features backup - cargo test --features backup
- cargo test --features blob - cargo test --features blob
- cargo test --features functions
- cargo test --features limits
- cargo test --features load_extension - cargo test --features load_extension
- cargo test --features trace - cargo test --features trace
- cargo test --features functions
- cargo test --features chrono - cargo test --features chrono
- cargo test --features serde_json - cargo test --features serde_json
- cargo test --features bundled - cargo test --features bundled
- cargo test --features "backup blob chrono functions load_extension serde_json trace"
- cargo test --features "backup blob chrono functions load_extension serde_json trace bundled"
- cargo test --features "csvtab functions vtab" - cargo test --features "csvtab functions vtab"
- cargo test --features "backup blob chrono csvtab functions load_extension serde_json trace vtab" - cargo test --features "backup blob chrono csvtab functions limits load_extension serde_json trace vtab"
- cargo test --features "backup blob chrono csvtab functions limits load_extension serde_json trace vtab buildtime_bindgen"
- cargo test --features "backup blob chrono csvtab functions limits load_extension serde_json trace vtab bundled"
- cargo test --features "backup blob chrono csvtab functions limits load_extension serde_json trace vtab bundled buildtime_bindgen"

View File

@ -16,3 +16,5 @@ rusqlite contributors
* [Xidorn Quan](https://github.com/upsuper) * [Xidorn Quan](https://github.com/upsuper)
* [Chip Collier](https://github.com/photex) * [Chip Collier](https://github.com/photex)
* [Omar Ferrer](https://github.com/chamakits) * [Omar Ferrer](https://github.com/chamakits)
* [Lee Jenkins](https://github.com/reddraggone9)
* [miedzinski](https://github.com/miedzinski)

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rusqlite" name = "rusqlite"
version = "0.8.0" version = "0.10.1"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"] authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
description = "Ergonomic wrapper for SQLite" description = "Ergonomic wrapper for SQLite"
repository = "https://github.com/jgallagher/rusqlite" repository = "https://github.com/jgallagher/rusqlite"
@ -8,17 +8,24 @@ documentation = "http://jgallagher.github.io/rusqlite/rusqlite/index.html"
readme = "README.md" readme = "README.md"
keywords = ["sqlite", "database", "ffi"] keywords = ["sqlite", "database", "ffi"]
license = "MIT" license = "MIT"
categories = ["database"]
[badges]
travis-ci = { repository = "jgallagher/rusqlite" }
appveyor = { repository = "jgallagher/rusqlite" }
[lib] [lib]
name = "rusqlite" name = "rusqlite"
[features] [features]
load_extension = [] load_extension = []
backup = [] backup = ["libsqlite3-sys/min_sqlite_version_3_6_11"]
blob = [] blob = ["libsqlite3-sys/min_sqlite_version_3_7_4"]
functions = [] functions = ["libsqlite3-sys/min_sqlite_version_3_7_3"]
trace = [] trace = ["libsqlite3-sys/min_sqlite_version_3_6_23"]
bundled = ["libsqlite3-sys/bundled"] bundled = ["libsqlite3-sys/bundled"]
buildtime_bindgen = ["libsqlite3-sys/buildtime_bindgen"]
limits = []
vtab = [] vtab = []
csvtab = ["csv"] csvtab = ["csv"]
@ -26,9 +33,8 @@ csvtab = ["csv"]
time = "0.1" time = "0.1"
bitflags = "0.7" bitflags = "0.7"
lru-cache = "0.1" lru-cache = "0.1"
libc = "0.2" chrono = { version = "0.3", optional = true }
chrono = { version = "0.2", optional = true } serde_json = { version = "0.9", optional = true }
serde_json = { version = "0.8", optional = true }
[dev-dependencies] [dev-dependencies]
tempdir = "0.3" tempdir = "0.3"
@ -37,7 +43,7 @@ regex = "0.2"
[dependencies.libsqlite3-sys] [dependencies.libsqlite3-sys]
path = "libsqlite3-sys" path = "libsqlite3-sys"
version = "0.6.0" version = "0.7"
[dependencies.csv] [dependencies.csv]
version = "0.14" version = "0.14"

View File

@ -1,4 +1,53 @@
# Version UPCOMING (TBD) # 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 * 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. of a `Result<(), Error>` so callers get the still-open connection back on failure.

View File

@ -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 ### Optional Features
Rusqlite provides several features that are behind [Cargo Rusqlite provides several features that are behind [Cargo
@ -65,13 +71,18 @@ features](http://doc.crates.io/manifest.html#the-features-section). They are:
* [`load_extension`](http://jgallagher.github.io/rusqlite/rusqlite/struct.LoadExtensionGuard.html) * [`load_extension`](http://jgallagher.github.io/rusqlite/rusqlite/struct.LoadExtensionGuard.html)
allows loading dynamic library-based SQLite extensions. allows loading dynamic library-based SQLite extensions.
* [`backup`](http://jgallagher.github.io/rusqlite/rusqlite/backup/index.html) * [`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) * [`functions`](http://jgallagher.github.io/rusqlite/rusqlite/functions/index.html)
allows you to load Rust closures into SQLite connections for use in queries. 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) * [`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) * [`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) * `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 and [`ToSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.ToSql.html) for various
types from the [`chrono` crate](https://crates.io/crates/chrono). types from the [`chrono` crate](https://crates.io/crates/chrono).
@ -80,6 +91,53 @@ features](http://doc.crates.io/manifest.html#the-features-section). They are:
`Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json). `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. * `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 use
pkg-config to find a SQLite library that already exists on your system. You can
adjust this behavior in a couple 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 set the `SQLITE3_LIB_DIR` to point to directory containing the SQLite
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 ## Author
John Gallagher, johnkgallagher@gmail.com John Gallagher, johnkgallagher@gmail.com

View File

@ -1,5 +1,5 @@
environment: environment:
TARGET: 1.9.0-x86_64-pc-windows-gnu TARGET: 1.15.0-x86_64-pc-windows-gnu
MSYS2_BITS: 64 MSYS2_BITS: 64
install: install:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe" - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-${env:TARGET}.exe"
@ -8,18 +8,22 @@ install:
- if defined MSYS2_BITS set PATH=%PATH%;C:\msys64\mingw%MSYS2_BITS%\bin - if defined MSYS2_BITS set PATH=%PATH%;C:\msys64\mingw%MSYS2_BITS%\bin
- rustc -V - rustc -V
- cargo -V - cargo -V
- ps: Start-FileDownload 'http://sqlite.org/2016/sqlite-dll-win64-x64-3100200.zip' - 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)
- cmd: 7z e sqlite-dll-win64-x64-3100200.zip -y > nul - cmd: 7z e sqlite-dll-win64-x64-3170000.zip -y > nul
- SET SQLITE3_LIB_DIR=%APPVEYOR_BUILD_FOLDER% - ps: Start-FileDownload 'https://sqlite.org/2017/sqlite-amalgamation-3170000.zip' # download SQLite headers (useful only when the `bundled` feature is not set)
- cmd: 7z e sqlite-amalgamation-3170000.zip -y > nul
- SET SQLITE3_LIB_DIR=%APPVEYOR_BUILD_FOLDER% # specify where the SQLite dll has been downloaded (useful only when the `bundled` feature is not set)
- SET SQLITE3_INCLUDE_DIR=%APPVEYOR_BUILD_FOLDER% # specify where the SQLite headers have been downloaded (useful only when the `bundled` feature is not set)
build: false build: false
test_script: test_script:
- cargo test --lib --verbose - cargo test --lib --verbose
- cargo test --lib --verbose --features bundled - cargo test --lib --verbose --features bundled
- cargo test --lib --features "backup blob chrono functions load_extension serde_json trace" - cargo test --lib --features "backup blob chrono csvtab functions limits load_extension serde_json trace vtab"
- cargo test --lib --features "backup blob chrono functions load_extension serde_json trace bundled" - cargo test --lib --features "backup blob chrono csvtab functions limits load_extension serde_json trace vtab buildtime_bindgen"
- cargo test --lib --features "backup blob chrono functions load_extension serde_json trace vtab" - cargo test --lib --features "backup blob chrono csvtab functions limits load_extension serde_json trace vtab bundled"
- cargo test --lib --features "backup blob chrono csvtab functions limits load_extension serde_json trace vtab bundled buildtime_bindgen"
cache: cache:
- C:\Users\appveyor\.cargo - C:\Users\appveyor\.cargo

View File

@ -4,12 +4,12 @@ extern crate test;
extern crate rusqlite; extern crate rusqlite;
use rusqlite::Connection; use rusqlite::Connection;
use rusqlite::cache::StatementCache;
use test::Bencher; use test::Bencher;
#[bench] #[bench]
fn bench_no_cache(b: &mut Bencher) { fn bench_no_cache(b: &mut Bencher) {
let db = Connection::open_in_memory().unwrap(); 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"; let sql = "SELECT 1, 'test', 3.14 UNION SELECT 2, 'exp', 2.71";
b.iter(|| db.prepare(sql).unwrap()); b.iter(|| db.prepare(sql).unwrap());
} }
@ -17,7 +17,6 @@ fn bench_no_cache(b: &mut Bencher) {
#[bench] #[bench]
fn bench_cache(b: &mut Bencher) { fn bench_cache(b: &mut Bencher) {
let db = Connection::open_in_memory().unwrap(); 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"; 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,19 +1,26 @@
[package] [package]
name = "libsqlite3-sys" name = "libsqlite3-sys"
version = "0.6.0" version = "0.7.1"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"] authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
repository = "https://github.com/jgallagher/rusqlite" repository = "https://github.com/jgallagher/rusqlite"
description = "Native bindings to the libsqlite3 library" description = "Native bindings to the libsqlite3 library"
license = "MIT" license = "MIT"
links = "sqlite3" links = "sqlite3"
build = "build.rs" build = "build.rs"
keywords = ["sqlite", "database", "ffi"]
categories = ["database", "external-ffi-bindings"]
[features] [features]
bundled = [] default = ["min_sqlite_version_3_6_8"]
bundled = ["gcc"]
buildtime_bindgen = ["bindgen", "pkg-config"]
min_sqlite_version_3_6_8 = ["pkg-config"]
min_sqlite_version_3_6_11 = ["pkg-config"]
min_sqlite_version_3_6_23 = ["pkg-config"]
min_sqlite_version_3_7_3 = ["pkg-config"]
min_sqlite_version_3_7_4 = ["pkg-config"]
[build-dependencies] [build-dependencies]
pkg-config = "~0.3" bindgen = { version = "0.21", optional = true }
gcc = "~0.3" pkg-config = { version = "0.3", optional = true }
gcc = { version = "0.3", optional = true }
[dependencies]
libc = "~0.2"

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,34 +1,19 @@
extern crate gcc;
extern crate pkg_config;
#[cfg(not(feature = "bundled"))]
fn main() { fn main() {
use std::env; build::main();
use std::fs;
// 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);
} }
#[cfg(feature = "bundled")] #[cfg(feature = "bundled")]
fn main() { 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() gcc::Config::new()
.file("sqlite3/sqlite3.c") .file("sqlite3/sqlite3.c")
.flag("-DSQLITE_CORE") .flag("-DSQLITE_CORE")
@ -51,3 +36,158 @@ fn main() {
.flag("-DSQLITE_USE_URI") .flag("-DSQLITE_USE_URI")
.compile("libsqlite3.a"); .compile("libsqlite3.a");
} }
}
#[cfg(not(feature = "bundled"))]
mod build {
extern crate pkg_config;
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;
}
// 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(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",
];
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::chooser::{TypeChooser, IntKind};
use super::HeaderLocation;
use std::env;
use std::io::Write;
use std::fs::OpenOptions;
use std::path::Path;
#[derive(Debug)]
struct SqliteTypeChooser;
impl TypeChooser 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())
.type_chooser(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

View File

@ -15,12 +15,10 @@
** as extensions by SQLite should #include this file instead of ** as extensions by SQLite should #include this file instead of
** sqlite3.h. ** sqlite3.h.
*/ */
#ifndef _SQLITE3EXT_H_ #ifndef SQLITE3EXT_H
#define _SQLITE3EXT_H_ #define SQLITE3EXT_H
#include "sqlite3.h" #include "sqlite3.h"
typedef struct sqlite3_api_routines sqlite3_api_routines;
/* /*
** The following structure holds pointers to all of the SQLite API ** The following structure holds pointers to all of the SQLite API
** routines. ** routines.
@ -281,8 +279,21 @@ struct sqlite3_api_routines {
int (*db_cacheflush)(sqlite3*); int (*db_cacheflush)(sqlite3*);
/* Version 3.12.0 and later */ /* Version 3.12.0 and later */
int (*system_errno)(sqlite3*); 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 ** The following macros redefine the API routines so that they are
** redirected through the global sqlite3_api structure. ** redirected through the global sqlite3_api structure.
@ -526,6 +537,9 @@ struct sqlite3_api_routines {
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush #define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
/* Version 3.12.0 and later */ /* Version 3.12.0 and later */
#define sqlite3_system_errno sqlite3_api->system_errno #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) */ #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
@ -543,4 +557,4 @@ struct sqlite3_api_routines {
# define SQLITE_EXTENSION_INIT3 /*no-op*/ # define SQLITE_EXTENSION_INIT3 /*no-op*/
#endif #endif
#endif /* _SQLITE3EXT_H_ */ #endif /* SQLITE3EXT_H */

View File

@ -1,32 +1,57 @@
use libc::c_int; use std::os::raw::c_int;
use std::error; use std::error;
use std::fmt; use std::fmt;
/// Error Codes
#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ErrorCode { pub enum ErrorCode {
/// Internal logic error in SQLite
InternalMalfunction, InternalMalfunction,
/// Access permission denied
PermissionDenied, PermissionDenied,
/// Callback routine requested an abort
OperationAborted, OperationAborted,
/// The database file is locked
DatabaseBusy, DatabaseBusy,
/// A table in the database is locked
DatabaseLocked, DatabaseLocked,
/// A malloc() failed
OutOfMemory, OutOfMemory,
/// Attempt to write a readonly database
ReadOnly, ReadOnly,
/// Operation terminated by sqlite3_interrupt()
OperationInterrupted, OperationInterrupted,
/// Some kind of disk I/O error occurred
SystemIOFailure, SystemIOFailure,
/// The database disk image is malformed
DatabaseCorrupt, DatabaseCorrupt,
/// Unknown opcode in sqlite3_file_control()
NotFound, NotFound,
/// Insertion failed because database is full
DiskFull, DiskFull,
/// Unable to open the database file
CannotOpen, CannotOpen,
/// Database lock protocol error
FileLockingProtocolFailed, FileLockingProtocolFailed,
/// The database schema changed
SchemaChanged, SchemaChanged,
/// String or BLOB exceeds size limit
TooBig, TooBig,
/// Abort due to constraint violation
ConstraintViolation, ConstraintViolation,
/// Data type mismatch
TypeMismatch, TypeMismatch,
/// Library used incorrectly
APIMisuse, APIMisuse,
/// Uses OS features not supported on host
NoLargeFileSupport, NoLargeFileSupport,
/// Authorization denied
AuthorizationForStatementDenied, AuthorizationForStatementDenied,
/// 2nd parameter to sqlite3_bind out of range
ParameterOutOfRange, ParameterOutOfRange,
/// File opened that is not a database file
NotADatabase, NotADatabase,
/// SQL error or missing database
Unknown, Unknown,
} }

View File

@ -1,25 +1,12 @@
// 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::*; pub use self::error::*;
use std::default::Default;
use std::mem; use std::mem;
use libc::c_int;
mod bindgen;
mod error; 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 { pub fn SQLITE_STATIC() -> sqlite3_destructor_type {
Some(unsafe { mem::transmute(0isize) }) Some(unsafe { mem::transmute(0isize) })
} }
@ -28,6 +15,45 @@ pub fn SQLITE_TRANSIENT() -> sqlite3_destructor_type {
Some(unsafe { mem::transmute(-1isize) }) Some(unsafe { mem::transmute(-1isize) })
} }
pub const SQLITE_CONFIG_LOG : c_int = 16; /// Run-Time Limit Categories
pub const SQLITE_UTF8 : c_int = 1; #[repr(C)]
pub const SQLITE_DETERMINISTIC : c_int = 0x800; pub enum Limit {
/// The maximum size of any string or BLOB or table row, in bytes.
SQLITE_LIMIT_LENGTH = 0,
/// The maximum length of an SQL statement, in bytes.
SQLITE_LIMIT_SQL_LENGTH = 1,
/// 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 = 2,
/// The maximum depth of the parse tree on any expression.
SQLITE_LIMIT_EXPR_DEPTH = 3,
/// The maximum number of terms in a compound SELECT statement.
SQLITE_LIMIT_COMPOUND_SELECT = 4,
/// The maximum number of instructions in a virtual machine program used to implement an SQL statement.
SQLITE_LIMIT_VDBE_OP = 5,
/// The maximum number of arguments on a function.
SQLITE_LIMIT_FUNCTION_ARG = 6,
/// The maximum number of attached databases.
SQLITE_LIMIT_ATTACHED = 7,
/// The maximum length of the pattern argument to the LIKE or GLOB operators.
SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 8,
/// The maximum index number of any parameter in an SQL statement.
SQLITE_LIMIT_VARIABLE_NUMBER = 9,
/// 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"));
pub type sqlite3_index_constraint = sqlite3_index_info_sqlite3_index_constraint;
pub type sqlite3_index_constraint_usage = sqlite3_index_info_sqlite3_index_constraint_usage;
impl Default for sqlite3_vtab {
fn default() -> Self { unsafe { mem::zeroed() } }
}
impl Default for sqlite3_vtab_cursor {
fn default() -> Self { unsafe { mem::zeroed() } }
}

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) cd $(git rev-parse --show-toplevel)
rm -rf target/doc/ 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 echo '<meta http-equiv=refresh content=0;url=rusqlite/index.html>' > target/doc/index.html
ghp-import target/doc ghp-import target/doc
git push origin gh-pages:gh-pages git push origin gh-pages:gh-pages

View File

@ -30,7 +30,7 @@ use std::marker::PhantomData;
use std::path::Path; use std::path::Path;
use std::ptr; use std::ptr;
use libc::c_int; use std::os::raw::c_int;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;

View File

@ -5,6 +5,7 @@ use std::ops::{Deref, DerefMut};
use lru_cache::LruCache; use lru_cache::LruCache;
use {Result, Connection, Statement}; use {Result, Connection, Statement};
use raw_statement::RawStatement; use raw_statement::RawStatement;
use statement::StatementCrateImpl;
impl Connection { impl Connection {
/// Prepare a SQL statement for execution, returning a previously prepared (but /// Prepare a SQL statement for execution, returning a previously prepared (but
@ -258,7 +259,11 @@ mod test {
{ {
let mut stmt = db.prepare_cached(sql).unwrap(); let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!(1i32, 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#" db.execute_batch(r#"

View File

@ -1,120 +0,0 @@
use {Error, Result, Row, 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)
}
/// 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))
}
}
#[cfg(test)]
mod test {
use {Connection, Error, Result};
#[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

@ -2,7 +2,7 @@ use std::error;
use std::fmt; use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use std::str; use std::str;
use libc::c_int; use std::os::raw::c_int;
use {ffi, errmsg_to_string}; use {ffi, errmsg_to_string};
use types::Type; use types::Type;
@ -25,6 +25,11 @@ pub enum Error {
/// the requested Rust type. /// the requested Rust type.
FromSqlConversionFailure(usize, Type, Box<error::Error + Send + Sync>), 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. /// Error converting a string to UTF-8.
Utf8Error(str::Utf8Error), Utf8Error(str::Utf8Error),
@ -105,6 +110,9 @@ impl fmt::Display for Error {
i, i,
err) err)
} }
Error::IntegralValueOutOfRange(col, val) => {
write!(f, "Integer {} out of range at index {}", val, col)
}
Error::Utf8Error(ref err) => err.fmt(f), Error::Utf8Error(ref err) => err.fmt(f),
Error::NulError(ref err) => err.fmt(f), Error::NulError(ref err) => err.fmt(f),
Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name), Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name),
@ -141,6 +149,7 @@ impl error::Error for Error {
"SQLite was compiled or configured for single-threaded use only" "SQLite was compiled or configured for single-threaded use only"
} }
Error::FromSqlConversionFailure(_, _, ref err) => err.description(), Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type",
Error::Utf8Error(ref err) => err.description(), Error::Utf8Error(ref err) => err.description(),
Error::InvalidParameterName(_) => "invalid parameter name", Error::InvalidParameterName(_) => "invalid parameter name",
Error::NulError(ref err) => err.description(), Error::NulError(ref err) => err.description(),
@ -170,6 +179,7 @@ impl error::Error for Error {
Error::Utf8Error(ref err) => Some(err), Error::Utf8Error(ref err) => Some(err),
Error::NulError(ref err) => Some(err), Error::NulError(ref err) => Some(err),
Error::IntegralValueOutOfRange(_, _) |
Error::SqliteSingleThreadedMode | Error::SqliteSingleThreadedMode |
Error::InvalidParameterName(_) | Error::InvalidParameterName(_) |
Error::ExecuteReturnedResults | Error::ExecuteReturnedResults |
@ -197,7 +207,7 @@ pub fn error_from_sqlite_code(code: c_int, message: Option<String>) -> Error {
Error::SqliteFailure(ffi::Error::new(code), message) 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() { let message = if db.is_null() {
None None
} else { } else {

View File

@ -54,7 +54,7 @@ use std::ffi::CStr;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use std::slice; use std::slice;
use libc::{c_int, c_char, c_void}; use std::os::raw::{c_int, c_char, c_void};
use ffi; use ffi;
use ffi::sqlite3_context; use ffi::sqlite3_context;
@ -79,7 +79,7 @@ pub fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
ValueRef::Null => unsafe { ffi::sqlite3_result_null(ctx) }, ValueRef::Null => unsafe { ffi::sqlite3_result_null(ctx) },
ValueRef::Integer(i) => unsafe { ffi::sqlite3_result_int64(ctx, i) }, ValueRef::Integer(i) => unsafe { ffi::sqlite3_result_int64(ctx, i) },
ValueRef::Real(r) => unsafe { ffi::sqlite3_result_double(ctx, r) }, ValueRef::Real(r) => unsafe { ffi::sqlite3_result_double(ctx, r) },
ValueRef::Text(ref s) => unsafe { ValueRef::Text(s) => unsafe {
let length = s.len(); let length = s.len();
if length > ::std::i32::MAX as usize { if length > ::std::i32::MAX as usize {
ffi::sqlite3_result_error_toobig(ctx); ffi::sqlite3_result_error_toobig(ctx);
@ -97,7 +97,7 @@ pub fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, destructor); ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, destructor);
} }
}, },
ValueRef::Blob(ref b) => unsafe { ValueRef::Blob(b) => unsafe {
let length = b.len(); let length = b.len();
if length > ::std::i32::MAX as usize { if length > ::std::i32::MAX as usize {
ffi::sqlite3_result_error_toobig(ctx); ffi::sqlite3_result_error_toobig(ctx);
@ -114,8 +114,8 @@ pub fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
} }
pub unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) { pub unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
match err { match *err {
&Error::SqliteFailure(ref err, ref s) => { Error::SqliteFailure(ref err, ref s) => {
ffi::sqlite3_result_error_code(ctx, err.extended_code); ffi::sqlite3_result_error_code(ctx, err.extended_code);
if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) { if let Some(Ok(cstr)) = s.as_ref().map(|s| str_to_cstring(s)) {
ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1); ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
@ -198,6 +198,7 @@ impl<'a> Context<'a> {
FromSqlError::InvalidType => { FromSqlError::InvalidType => {
Error::InvalidFunctionParameterType(idx, value.data_type()) Error::InvalidFunctionParameterType(idx, value.data_type())
} }
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx as c_int, i),
FromSqlError::Other(err) => { FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err) Error::FromSqlConversionFailure(idx, value.data_type(), err)
} }
@ -507,7 +508,7 @@ mod test {
extern crate regex; extern crate regex;
use std::collections::HashMap; use std::collections::HashMap;
use libc::c_double; use std::os::raw::c_double;
use self::regex::Regex; use self::regex::Regex;
use std::f64::EPSILON; use std::f64::EPSILON;

View File

@ -52,7 +52,6 @@
//! ``` //! ```
#![allow(unknown_lints)] #![allow(unknown_lints)]
extern crate libc;
extern crate libsqlite3_sys as ffi; extern crate libsqlite3_sys as ffi;
extern crate lru_cache; extern crate lru_cache;
#[macro_use] #[macro_use]
@ -72,13 +71,18 @@ use std::cell::RefCell;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::result; use std::result;
use std::str; use std::str;
use libc::{c_int, c_char, c_void}; use std::sync::{Once, ONCE_INIT};
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
use std::os::raw::{c_int, c_char};
use types::{ToSql, ToSqlOutput, FromSql, FromSqlError, ValueRef}; use types::{ToSql, FromSql, FromSqlError, ValueRef};
use error::{error_from_sqlite_code, error_from_handle}; use error::{error_from_sqlite_code, error_from_handle};
use raw_statement::RawStatement; use raw_statement::RawStatement;
use cache::StatementCache; use cache::StatementCache;
pub use statement::Statement;
use statement::StatementCrateImpl;
#[allow(deprecated)] #[allow(deprecated)]
pub use transaction::{SqliteTransaction, SqliteTransactionBehavior}; pub use transaction::{SqliteTransaction, SqliteTransactionBehavior};
pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}; pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
@ -86,20 +90,22 @@ pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior}
#[allow(deprecated)] #[allow(deprecated)]
pub use error::SqliteError; pub use error::SqliteError;
pub use error::Error; pub use error::Error;
pub use ffi::ErrorCode;
pub use cache::CachedStatement; pub use cache::CachedStatement;
pub use version::*;
#[cfg(feature = "load_extension")] #[cfg(feature = "load_extension")]
#[allow(deprecated)] #[allow(deprecated)]
pub use load_extension_guard::{SqliteLoadExtensionGuard, LoadExtensionGuard}; pub use load_extension_guard::{SqliteLoadExtensionGuard, LoadExtensionGuard};
pub mod types; pub mod types;
mod version;
mod transaction; mod transaction;
mod cache; mod cache;
mod named_params;
mod error; mod error;
mod convenient;
mod raw_statement; mod raw_statement;
mod statement;
#[cfg(feature = "load_extension")] #[cfg(feature = "load_extension")]
mod load_extension_guard; mod load_extension_guard;
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
@ -110,6 +116,8 @@ pub mod backup;
pub mod functions; pub mod functions;
#[cfg(feature = "blob")] #[cfg(feature = "blob")]
pub mod blob; pub mod blob;
#[cfg(feature = "limits")]
pub mod limits;
#[cfg(all(feature = "vtab", feature = "functions"))] #[cfg(all(feature = "vtab", feature = "functions"))]
pub mod vtab; pub mod vtab;
@ -203,7 +211,7 @@ impl Connection {
/// Open a new connection to a SQLite database. /// Open a new connection to a SQLite database.
/// ///
/// Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid /// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
/// flag combinations. /// flag combinations.
/// ///
/// # Failure /// # Failure
@ -223,7 +231,7 @@ impl Connection {
/// Open a new connection to an in-memory SQLite database. /// Open a new connection to an in-memory SQLite database.
/// ///
/// Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid /// [Database Connection](http://www.sqlite.org/c3ref/open.html) for a description of valid
/// flag combinations. /// flag combinations.
/// ///
/// # Failure /// # Failure
@ -289,6 +297,28 @@ impl Connection {
self.prepare(sql).and_then(|mut stmt| stmt.execute(params)) self.prepare(sql).and_then(|mut stmt| stmt.execute(params))
} }
/// 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))
}
/// Get the SQLite rowid of the most recent successful INSERT. /// Get the SQLite rowid of the most recent successful INSERT.
/// ///
/// Uses [sqlite3_last_insert_rowid](https://www.sqlite.org/c3ref/last_insert_rowid.html) under /// Uses [sqlite3_last_insert_rowid](https://www.sqlite.org/c3ref/last_insert_rowid.html) under
@ -323,6 +353,24 @@ impl Connection {
stmt.query_row(params, f) stmt.query_row(params, f)
} }
/// 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(|r| f(&r))
}
/// Convenience method to execute a query that is expected to return a single row, /// Convenience method to execute a query that is expected to return a single row,
/// and execute a mapping via `f` on that returned row with the possibility of failure. /// and execute a mapping via `f` on that returned row with the possibility of failure.
/// The `Result` type of `f` must implement `std::convert::From<Error>`. /// The `Result` type of `f` must implement `std::convert::From<Error>`.
@ -419,11 +467,8 @@ impl Connection {
/// Will return `Err` if the underlying SQLite call fails. /// Will return `Err` if the underlying SQLite call fails.
pub fn close(self) -> std::result::Result<(), (Connection, Error)> { pub fn close(self) -> std::result::Result<(), (Connection, Error)> {
self.flush_prepared_statement_cache(); self.flush_prepared_statement_cache();
{ let r = self.db.borrow_mut().close();
let mut db = self.db.borrow_mut(); r.map_err(move |err| (self, err))
db.close()
}
.map_err(move |err| (self, err))
} }
/// Enable loading of SQLite extensions. Strongly consider using `LoadExtensionGuard` /// Enable loading of SQLite extensions. Strongly consider using `LoadExtensionGuard`
@ -499,7 +544,7 @@ impl Connection {
/// on the rusqlite repository](https://github.com/jgallagher/rusqlite/issues) and describe /// on the rusqlite repository](https://github.com/jgallagher/rusqlite/issues) and describe
/// your use case. This function is unsafe because it gives you raw access to the SQLite /// your use case. This function is unsafe because it gives you raw access to the SQLite
/// connection, and what you do with it could impact the safety of this `Connection`. /// connection, and what you do with it could impact the safety of this `Connection`.
pub unsafe fn handle(&self) -> *mut ffi::Struct_sqlite3 { pub unsafe fn handle(&self) -> *mut ffi::sqlite3 {
self.db.borrow().db() self.db.borrow().db()
} }
@ -521,7 +566,7 @@ impl fmt::Debug for Connection {
} }
struct InnerConnection { struct InnerConnection {
db: *mut ffi::Struct_sqlite3, db: *mut ffi::sqlite3,
} }
/// Old name for `OpenFlags`. `SqliteOpenFlags` is deprecated. /// Old name for `OpenFlags`. `SqliteOpenFlags` is deprecated.
@ -532,7 +577,7 @@ bitflags! {
#[doc = "Flags for opening SQLite database connections."] #[doc = "Flags for opening SQLite database connections."]
#[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."] #[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."]
#[repr(C)] #[repr(C)]
pub flags OpenFlags: ::libc::c_int { pub flags OpenFlags: ::std::os::raw::c_int {
const SQLITE_OPEN_READ_ONLY = 0x00000001, const SQLITE_OPEN_READ_ONLY = 0x00000001,
const SQLITE_OPEN_READ_WRITE = 0x00000002, const SQLITE_OPEN_READ_WRITE = 0x00000002,
const SQLITE_OPEN_CREATE = 0x00000004, const SQLITE_OPEN_CREATE = 0x00000004,
@ -551,29 +596,148 @@ impl Default for OpenFlags {
} }
} }
impl InnerConnection { static SQLITE_INIT: Once = ONCE_INIT;
fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result<InnerConnection> { static SQLITE_VERSION_CHECK: Once = ONCE_INIT;
unsafe { static BYPASS_SQLITE_INIT: AtomicBool = ATOMIC_BOOL_INIT;
// Before opening the database, we need to check that SQLite hasn't been static BYPASS_VERSION_CHECK: AtomicBool = ATOMIC_BOOL_INIT;
// compiled or configured to be in single-threaded mode. If it has, we're
// exposing a very unsafe API to Rust, so refuse to open connections at all. /// rusqlite's check for a safe SQLite threading mode requires SQLite 3.7.0 or later. If you are
// Unfortunately, the check for this is quite gross. sqlite3_threadsafe() only /// running against a SQLite older than that, rusqlite attempts to ensure safety by performing
// returns how SQLite was _compiled_; there is no public API to check whether /// configuration and initialization of SQLite itself the first time you attempt to open a
// someone called sqlite3_config() to set single-threaded mode. We can cheat /// connection. By default, rusqlite panics if that initialization fails, since that could mean
// by trying to allocate a mutex, though; in single-threaded mode due to /// SQLite has been initialized in single-thread mode.
// compilation settings, the magic value 8 is returned (see the definition of ///
// sqlite3_mutex_alloc at https://github.com/mackyle/sqlite/blob/master/src/mutex.h); /// If you are encountering that panic _and_ can ensure that SQLite has been initialized in either
// in single-threaded mode due to sqlite3_config(), the magic value 8 is also /// multi-thread or serialized mode, call this function prior to attempting to open a connection
// returned (see the definition of noopMutexAlloc at /// and rusqlite's initialization process will by skipped. This function is unsafe because if you
// https://github.com/mackyle/sqlite/blob/master/src/mutex_noop.c). /// call it and SQLite has actually been configured to run in single-thread mode, you may enounter
const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8; /// memory errors or data corruption or any number of terrible things that should not be possible
let mutex_ptr = ffi::sqlite3_mutex_alloc(0); /// when you're using Rust.
let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC; pub unsafe fn bypass_sqlite_initialization() {
ffi::sqlite3_mutex_free(mutex_ptr); BYPASS_SQLITE_INIT.store(true, Ordering::Relaxed);
if is_singlethreaded { }
/// rusqlite performs a one-time check that the runtime SQLite version is at least as new as
/// the version of SQLite found when rusqlite was built. Bypassing this check may be dangerous;
/// e.g., if you use features of SQLite that are not present in the runtime version. If you are
/// sure the runtime version is compatible with the build-time version for your usage, you can
/// bypass the version check by calling this function before your first connection attempt.
pub unsafe fn bypass_sqlite_version_check() {
BYPASS_VERSION_CHECK.store(true, Ordering::Relaxed);
}
fn ensure_valid_sqlite_version() {
SQLITE_VERSION_CHECK.call_once(|| {
let version_number = version_number();
// Check our hard floor.
if version_number < 3006008 {
panic!("rusqlite requires SQLite 3.6.8 or newer");
}
// Check that the major version number for runtime and buildtime match.
let buildtime_major = ffi::SQLITE_VERSION_NUMBER / 1_000_000;
let runtime_major = version_number / 1_000_000;
if buildtime_major != runtime_major {
panic!("rusqlite was built against SQLite {} but is running with SQLite {}",
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
version());
}
if BYPASS_VERSION_CHECK.load(Ordering::Relaxed) {
return;
}
// Check that the runtime version number is compatible with the version number we found at
// build-time.
if version_number < ffi::SQLITE_VERSION_NUMBER {
panic!("\
rusqlite was built against SQLite {} but the runtime SQLite version is {}. To fix this, either:
* Recompile rusqlite and link against the SQLite version you are using at runtime, or
* Call rusqlite::bypass_sqlite_version_check() prior to your first connection attempt. Doing this
means you're sure everything will work correctly even though the runtime version is older than
the version we found at build time.",
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
version());
}
});
}
fn ensure_safe_sqlite_threading_mode() -> Result<()> {
// Ensure SQLite was compiled in thredsafe mode.
if unsafe { ffi::sqlite3_threadsafe() == 0 } {
return Err(Error::SqliteSingleThreadedMode); return Err(Error::SqliteSingleThreadedMode);
} }
// Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode, but it's
// possible someone configured it to be in Single-thread mode before calling into us. That
// would mean we're exposing an unsafe API via a safe one (in Rust terminology), which is
// no good. We have two options to protect against this, depending on the version of SQLite
// we're linked with:
//
// 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for the magic value
// 8. This isn't documented, but it's what SQLite returns for its mutex allocation function
// in Single-thread mode.
// 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the threading mode. The
// check we perform for >= 3.7.0 will segfault. Instead, we insist on being able to call
// sqlite3_config and sqlite3_initialize ourself, ensuring we know the threading mode. This
// will fail if someone else has already initialized SQLite even if they initialized it
// safely. That's not ideal either, which is why we expose bypass_sqlite_initialization
// above.
if version_number() >= 3007000 {
const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
let is_singlethreaded = unsafe {
let mutex_ptr = ffi::sqlite3_mutex_alloc(0);
let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC;
ffi::sqlite3_mutex_free(mutex_ptr);
is_singlethreaded
};
if is_singlethreaded {
Err(Error::SqliteSingleThreadedMode)
} else {
Ok(())
}
} else {
SQLITE_INIT.call_once(|| {
if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) {
return;
}
unsafe {
let msg = "\
Could not ensure safe initialization of SQLite.
To fix this, either:
* Upgrade SQLite to at least version 3.7.0
* Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call
rusqlite::bypass_sqlite_initialization() prior to your first connection attempt.";
if ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) != ffi::SQLITE_OK {
panic!(msg);
}
if ffi::sqlite3_initialize() != ffi::SQLITE_OK {
panic!(msg);
}
}
});
Ok(())
}
}
impl InnerConnection {
fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result<InnerConnection> {
ensure_valid_sqlite_version();
ensure_safe_sqlite_threading_mode()?;
// Replicate the check for sane open flags from SQLite, because the check in SQLite itself
// wasn't added until version 3.7.3.
debug_assert!(1 << SQLITE_OPEN_READ_ONLY.bits == 0x02);
debug_assert!(1 << SQLITE_OPEN_READ_WRITE.bits == 0x04);
debug_assert!(1 << (SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE).bits == 0x40);
if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
return Err(Error::SqliteFailure(ffi::Error::new(ffi::SQLITE_MISUSE), None));
}
unsafe {
let mut db: *mut ffi::sqlite3 = mem::uninitialized(); let mut db: *mut ffi::sqlite3 = mem::uninitialized();
let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null()); let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null());
if r != ffi::SQLITE_OK { if r != ffi::SQLITE_OK {
@ -601,7 +765,7 @@ impl InnerConnection {
} }
} }
fn db(&self) -> *mut ffi::Struct_sqlite3 { fn db(&self) -> *mut ffi::sqlite3 {
self.db self.db
} }
@ -660,7 +824,7 @@ impl InnerConnection {
Ok(()) Ok(())
} else { } else {
let message = errmsg_to_string(&*errmsg); let message = errmsg_to_string(&*errmsg);
ffi::sqlite3_free(errmsg as *mut libc::c_void); ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void);
Err(error_from_sqlite_code(r, Some(message))) Err(error_from_sqlite_code(r, Some(message)))
} }
} }
@ -703,295 +867,6 @@ impl Drop for InnerConnection {
#[deprecated(since = "0.6.0", note = "Use Statement instead")] #[deprecated(since = "0.6.0", note = "Use Statement instead")]
pub type SqliteStatement<'conn> = Statement<'conn>; pub type SqliteStatement<'conn> = Statement<'conn>;
/// A prepared statement.
pub struct Statement<'conn> {
conn: &'conn Connection,
stmt: RawStatement,
}
impl<'conn> Statement<'conn> {
fn new(conn: &Connection, stmt: RawStatement) -> Statement {
Statement {
conn: conn,
stmt: stmt,
}
}
/// 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_()
}
fn execute_(&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()),
}
}
/// 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))
}
/// 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 row_iter = try!(self.query(params));
Ok(MappedRows {
rows: row_iter,
map: 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 row_iter = try!(self.query(params));
Ok(AndThenRows {
rows: row_iter,
map: f,
})
}
/// 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_()
}
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(ref 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(ref 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 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 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_();
}
}
/// An iterator over the mapped resulting rows of a query. /// An iterator over the mapped resulting rows of a query.
pub struct MappedRows<'stmt, F> { pub struct MappedRows<'stmt, F> {
rows: Rows<'stmt>, rows: Rows<'stmt>,
@ -1055,7 +930,7 @@ impl<'stmt> Rows<'stmt> {
fn reset(&mut self) { fn reset(&mut self) {
if let Some(stmt) = self.stmt.take() { if let Some(stmt) = self.stmt.take() {
stmt.stmt.reset(); stmt.reset();
} }
} }
@ -1070,22 +945,18 @@ impl<'stmt> Rows<'stmt> {
/// "streaming iterator". For a more natural interface, consider using `query_map` /// "streaming iterator". For a more natural interface, consider using `query_map`
/// or `query_and_then` instead, which return types that implement `Iterator`. /// or `query_and_then` instead, which return types that implement `Iterator`.
pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> { pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
self.stmt.and_then(|stmt| { self.stmt.and_then(|stmt| match stmt.step() {
match stmt.stmt.step() { Ok(true) => Some(Ok(Row {
ffi::SQLITE_ROW => {
Some(Ok(Row {
stmt: stmt, stmt: stmt,
phantom: PhantomData, phantom: PhantomData,
})) })),
} Ok(false) => {
ffi::SQLITE_DONE => {
self.reset(); self.reset();
None None
} }
code => { Err(err) => {
self.reset(); self.reset();
Some(Err(stmt.conn.decode_result(code).unwrap_err())) Some(Err(err))
}
} }
}) })
} }
@ -1112,9 +983,11 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
/// ///
/// ## Failure /// ## Failure
/// ///
/// Panics if the underlying SQLite column type is not a valid type as a source for `T`. /// Panics if calling `row.get_checked(idx)` would return an error, including:
/// ///
/// Panics if `idx` is outside the range of columns in the returned query. /// * 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 { pub fn get<I: RowIndex, T: FromSql>(&self, idx: I) -> T {
self.get_checked(idx).unwrap() self.get_checked(idx).unwrap()
} }
@ -1133,9 +1006,10 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
/// for this row. /// for this row.
pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> { pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
let idx = try!(idx.idx(self.stmt)); let idx = try!(idx.idx(self.stmt));
let value = unsafe { ValueRef::new(&self.stmt.stmt, idx) }; let value = self.stmt.value_ref(idx);
FromSql::column_result(value).map_err(|err| match err { FromSql::column_result(value).map_err(|err| match err {
FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()), FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()),
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
FromSqlError::Other(err) => { FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err) Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
} }
@ -1173,46 +1047,6 @@ impl<'a> RowIndex for &'a str {
} }
} }
impl<'a> ValueRef<'a> {
unsafe fn new(stmt: &RawStatement, col: c_int) -> ValueRef {
use std::slice::from_raw_parts;
let raw = stmt.ptr();
match stmt.column_type(col) {
ffi::SQLITE_NULL => ValueRef::Null,
ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_column_int64(raw, col)),
ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_column_double(raw, col)),
ffi::SQLITE_TEXT => {
let text = ffi::sqlite3_column_text(raw, col);
assert!(!text.is_null(),
"unexpected SQLITE_TEXT column type with NULL data");
let s = 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 = ffi::sqlite3_column_blob(raw, col);
let len = 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(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"),
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
extern crate tempdir; extern crate tempdir;
@ -1273,7 +1107,7 @@ mod test {
let raw_stmt = { let raw_stmt = {
use std::mem; use std::mem;
use std::ptr; use std::ptr;
use libc::c_int; use std::os::raw::c_int;
use super::str_to_cstring; use super::str_to_cstring;
let raw_db = db.db.borrow_mut().db; let raw_db = db.db.borrow_mut().db;
@ -1339,7 +1173,7 @@ mod test {
db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32]).unwrap()); db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32]).unwrap());
assert_eq!(3i32, assert_eq!(3i32,
db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap());
} }
#[test] #[test]
@ -1455,7 +1289,7 @@ mod test {
db.execute_batch(sql).unwrap(); db.execute_batch(sql).unwrap();
assert_eq!(10i64, assert_eq!(10i64,
db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)) db.query_row::<i64, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap()); .unwrap());
let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", &[], |r| r.get(0)); let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", &[], |r| r.get(0));
@ -1512,12 +1346,11 @@ mod test {
match result.unwrap_err() { match result.unwrap_err() {
Error::SqliteFailure(err, _) => { Error::SqliteFailure(err, _) => {
assert_eq!(err.code, ffi::ErrorCode::ConstraintViolation); assert_eq!(err.code, ErrorCode::ConstraintViolation);
// extended error codes for constraints were added in SQLite 3.7.16; if we're // extended error codes for constraints were added in SQLite 3.7.16; if we're
// running on a version at least that new, check for the extended code // running on a version at least that new, check for the extended code
let version = unsafe { ffi::sqlite3_libversion_number() }; if version_number() >= 3007016 {
if version >= 3007016 {
assert_eq!(err.extended_code, ffi::SQLITE_CONSTRAINT_NOTNULL) assert_eq!(err.extended_code, ffi::SQLITE_CONSTRAINT_NOTNULL)
} }
} }
@ -1525,6 +1358,16 @@ mod test {
} }
} }
#[test]
fn test_version_string() {
let n = version_number();
let major = n / 1_000_000;
let minor = (n % 1_000_000) / 1_000;
let patch = n % 1_000;
assert!(version().contains(&format!("{}.{}.{}", major, minor, patch)));
}
mod query_and_then_tests { mod query_and_then_tests {
extern crate libsqlite3_sys as ffi; extern crate libsqlite3_sys as ffi;
use super::*; use super::*;

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

@ -1,356 +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(|r| f(&r))
}
}
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};
/// 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.bind_parameter(value, 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::ffi::CStr;
use std::ptr; use std::ptr;
use libc::c_int; use std::os::raw::c_int;
use super::ffi; use super::ffi;
// Private newtype for raw sqlite3_stmts that finalize themselves when dropped. // Private newtype for raw sqlite3_stmts that finalize themselves when dropped.

779
src/statement.rs Normal file
View File

@ -0,0 +1,779 @@
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};
/// 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 row_iter = try!(self.query(params));
Ok(MappedRows {
rows: row_iter,
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};
/// 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,
})
}
/// 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 row_iter = try!(self.query(params));
Ok(AndThenRows {
rows: row_iter,
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,
})
}
/// 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. //! 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::ffi::{CStr, CString};
use std::mem; use std::mem;
use std::ptr; use std::ptr;

View File

@ -427,7 +427,8 @@ mod test {
{ {
let tx = db.transaction().unwrap(); let tx = db.transaction().unwrap();
assert_eq!(2i32, 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());
} }
} }
@ -453,7 +454,8 @@ mod test {
{ {
let tx = db.transaction().unwrap(); let tx = db.transaction().unwrap();
assert_eq!(6i32, 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());
} }
} }
@ -547,7 +549,7 @@ mod test {
} }
fn assert_current_sum(x: i32, conn: &Connection) { 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); assert_eq!(x, i);
} }
} }

View File

@ -5,7 +5,7 @@ use std::borrow::Cow;
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, UTC, Local}; use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, UTC, Local};
use ::Result; use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD" /// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
@ -104,9 +104,8 @@ impl FromSql for DateTime<UTC> {
Cow::Borrowed(s) Cow::Borrowed(s)
}; };
match DateTime::parse_from_rfc3339(&s) { if let Ok(dt) = DateTime::parse_from_rfc3339(&s) {
Ok(dt) => return Ok(dt.with_timezone(&UTC)), return Ok(dt.with_timezone(&UTC));
Err(_) => (),
} }
} }

View File

@ -5,9 +5,13 @@ use std::fmt;
/// Enum listing possible errors from `FromSql` trait. /// Enum listing possible errors from `FromSql` trait.
#[derive(Debug)] #[derive(Debug)]
pub enum FromSqlError { pub enum FromSqlError {
/// Error when an SQLite value is requested, but the type of the result cannot be converted to the /// Error when an SQLite value is requested, but the type of the result cannot be converted to
/// requested Rust type. /// the requested Rust type.
InvalidType, 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. /// An error case available for implementors of the `FromSql` trait.
Other(Box<Error + Send + Sync>), Other(Box<Error + Send + Sync>),
} }
@ -16,6 +20,7 @@ impl fmt::Display for FromSqlError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
FromSqlError::InvalidType => write!(f, "Invalid type"), FromSqlError::InvalidType => write!(f, "Invalid type"),
FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i),
FromSqlError::Other(ref err) => err.fmt(f), FromSqlError::Other(ref err) => err.fmt(f),
} }
} }
@ -25,6 +30,7 @@ impl Error for FromSqlError {
fn description(&self) -> &str { fn description(&self) -> &str {
match *self { match *self {
FromSqlError::InvalidType => "invalid type", FromSqlError::InvalidType => "invalid type",
FromSqlError::OutOfRange(_) => "value out of range",
FromSqlError::Other(ref err) => err.description(), FromSqlError::Other(ref err) => err.description(),
} }
} }
@ -32,8 +38,9 @@ impl Error for FromSqlError {
#[cfg_attr(feature="clippy", allow(match_same_arms))] #[cfg_attr(feature="clippy", allow(match_same_arms))]
fn cause(&self) -> Option<&Error> { fn cause(&self) -> Option<&Error> {
match *self { match *self {
FromSqlError::InvalidType => None,
FromSqlError::Other(ref err) => err.cause(), FromSqlError::Other(ref err) => err.cause(),
FromSqlError::InvalidType |
FromSqlError::OutOfRange(_) => None,
} }
} }
} }
@ -46,11 +53,28 @@ pub trait FromSql: Sized {
fn column_result(value: ValueRef) -> FromSqlResult<Self>; fn column_result(value: ValueRef) -> FromSqlResult<Self>;
} }
impl FromSql for i32 { macro_rules! from_sql_integral(
($t:ident) => (
impl FromSql for $t {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
i64::column_result(value).map(|i| i as i32) 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!(u8);
from_sql_integral!(u16);
from_sql_integral!(u32);
impl FromSql for i64 { impl FromSql for i64 {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
@ -103,3 +127,45 @@ impl FromSql for Value {
Ok(value.into()) 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 //! 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: //! use a newtype. For example, to store timespecs as `f64`s:
//! //!
//! ```rust,ignore //! ```rust
//! extern crate rusqlite; //! 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 rusqlite::{Result};
//! use libc::c_int;
//! use time;
//! //!
//! pub struct TimespecSql(pub time::Timespec); //! pub struct TimespecSql(pub time::Timespec);
//! //!
//! impl FromSql for TimespecSql { //! 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| { //! f64::column_result(value).map(|as_f64| {
//! TimespecSql(time::Timespec{ sec: as_f64.trunc() as i64, //! TimespecSql(time::Timespec{ sec: as_f64.trunc() as i64,
//! nsec: (as_f64.fract() * 1.0e9) as i32 }) //! nsec: (as_f64.fract() * 1.0e9) as i32 })
@ -38,12 +36,15 @@
//! } //! }
//! //!
//! impl ToSql for TimespecSql { //! 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 TimespecSql(ts) = *self;
//! let as_f64 = ts.sec as f64 + (ts.nsec as f64) / 1.0e9; //! 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 //! `ToSql` and `FromSql` are also implemented for `Option<T>` where `T` implements `ToSql` or
@ -72,11 +73,10 @@ mod serde_json;
/// ## Example /// ## Example
/// ///
/// ```rust,no_run /// ```rust,no_run
/// # extern crate libc;
/// # extern crate rusqlite; /// # extern crate rusqlite;
/// # use rusqlite::{Connection, Result}; /// # use rusqlite::{Connection, Result};
/// # use rusqlite::types::{Null}; /// # use rusqlite::types::{Null};
/// # use libc::{c_int}; /// # use std::os::raw::{c_int};
/// fn main() { /// fn main() {
/// } /// }
/// fn insert_null(conn: &Connection) -> Result<c_int> { /// fn insert_null(conn: &Connection) -> Result<c_int> {
@ -113,7 +113,7 @@ mod test {
use Connection; use Connection;
use Error; use Error;
use libc::{c_int, c_double}; use std::os::raw::{c_int, c_double};
use std::f64::EPSILON; use std::f64::EPSILON;
use super::Value; use super::Value;
@ -174,7 +174,7 @@ mod test {
db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)]).unwrap(); db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)]).unwrap();
assert_eq!(10i64, assert_eq!(10i64,
db.query_row("SELECT i FROM foo", &[], |r| r.get(0)).unwrap()); db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0)).unwrap());
} }
#[test] #[test]

View File

@ -3,7 +3,7 @@ extern crate serde_json;
use self::serde_json::Value; use self::serde_json::Value;
use ::Result; use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef}; use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
/// Serialize JSON `Value` to text. /// Serialize JSON `Value` to text.
@ -17,8 +17,8 @@ impl ToSql for Value {
impl FromSql for Value { impl FromSql for Value {
fn column_result(value: ValueRef) -> FromSqlResult<Self> { fn column_result(value: ValueRef) -> FromSqlResult<Self> {
match value { match value {
ValueRef::Text(ref s) => serde_json::from_str(s), ValueRef::Text(s) => serde_json::from_str(s),
ValueRef::Blob(ref b) => serde_json::from_slice(b), ValueRef::Blob(b) => serde_json::from_slice(b),
_ => return Err(FromSqlError::InvalidType), _ => return Err(FromSqlError::InvalidType),
} }
.map_err(|err| FromSqlError::Other(Box::new(err))) .map_err(|err| FromSqlError::Other(Box::new(err)))

View File

@ -1,7 +1,8 @@
use super::{Null, Value, ValueRef}; use super::{Null, Value, ValueRef};
use ::Result; use Result;
/// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait. /// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait.
#[derive(Clone,Debug,PartialEq)]
pub enum ToSqlOutput<'a> { pub enum ToSqlOutput<'a> {
/// A borrowed SQLite-representable value. /// A borrowed SQLite-representable value.
Borrowed(ValueRef<'a>), Borrowed(ValueRef<'a>),
@ -28,6 +29,18 @@ impl<'a, T: Into<Value>> From<T> for ToSqlOutput<'a> {
} }
} }
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. /// A trait for types that can be converted into SQLite values.
pub trait ToSql { pub trait ToSql {
fn to_sql(&self) -> Result<ToSqlOutput>; fn to_sql(&self) -> Result<ToSqlOutput>;
@ -57,8 +70,13 @@ macro_rules! to_sql_self(
to_sql_self!(Null); to_sql_self!(Null);
to_sql_self!(bool); to_sql_self!(bool);
to_sql_self!(i8);
to_sql_self!(i16);
to_sql_self!(i32); to_sql_self!(i32);
to_sql_self!(i64); to_sql_self!(i64);
to_sql_self!(u8);
to_sql_self!(u16);
to_sql_self!(u32);
to_sql_self!(f64); to_sql_self!(f64);
impl<'a, T: ?Sized> ToSql for &'a T impl<'a, T: ?Sized> ToSql for &'a T
@ -95,3 +113,21 @@ impl<T: ToSql> ToSql for Option<T> {
} }
} }
} }
#[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>();
}
}

View File

@ -30,11 +30,22 @@ impl From<bool> for Value {
} }
} }
impl From<i32> for Value { macro_rules! from_i64(
fn from(i: i32) -> Value { ($t:ty) => (
impl From<$t> for Value {
fn from(i: $t) -> Value {
Value::Integer(i as i64) Value::Integer(i as i64)
} }
} }
)
);
from_i64!(i8);
from_i64!(i16);
from_i64!(i32);
from_i64!(u8);
from_i64!(u16);
from_i64!(u32);
impl From<i64> for Value { impl From<i64> for Value {
fn from(i: i64) -> Value { fn from(i: i64) -> Value {

View File

@ -1,4 +1,4 @@
use ::types::{FromSqlError, FromSqlResult}; use types::{FromSqlError, FromSqlResult};
use super::{Value, Type}; use super::{Value, Type};
/// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the /// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the
@ -54,7 +54,7 @@ impl<'a> ValueRef<'a> {
/// `Err(Error::InvalidColumnType)`. /// `Err(Error::InvalidColumnType)`.
pub fn as_str(&self) -> FromSqlResult<&str> { pub fn as_str(&self) -> FromSqlResult<&str> {
match *self { match *self {
ValueRef::Text(ref t) => Ok(t), ValueRef::Text(t) => Ok(t),
_ => Err(FromSqlError::InvalidType), _ => Err(FromSqlError::InvalidType),
} }
} }
@ -63,7 +63,7 @@ impl<'a> ValueRef<'a> {
/// `Err(Error::InvalidColumnType)`. /// `Err(Error::InvalidColumnType)`.
pub fn as_blob(&self) -> FromSqlResult<&[u8]> { pub fn as_blob(&self) -> FromSqlResult<&[u8]> {
match *self { match *self {
ValueRef::Blob(ref b) => Ok(b), ValueRef::Blob(b) => Ok(b),
_ => Err(FromSqlError::InvalidType), _ => Err(FromSqlError::InvalidType),
} }
} }

17
src/version.rs Normal file
View File

@ -0,0 +1,17 @@
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,10 +1,10 @@
//! CSV Virtual Table //! CSV Virtual Table
extern crate csv; extern crate csv;
use std::fs::File; use std::fs::File;
use std::os::raw::{c_char, c_int, c_void};
use std::path::Path; use std::path::Path;
use std::result; use std::result;
use std::str; use std::str;
use libc;
use {Connection, Error, Result}; use {Connection, Error, Result};
use ffi; use ffi;
@ -58,7 +58,7 @@ impl CSVTab {
} }
impl VTab<CSVTabCursor> for CSVTab { impl VTab<CSVTabCursor> for CSVTab {
fn connect(db: *mut ffi::sqlite3, _aux: *mut libc::c_void, args: &[&[u8]]) -> Result<CSVTab> { fn connect(db: *mut ffi::sqlite3, _aux: *mut c_void, args: &[&[u8]]) -> Result<CSVTab> {
if args.len() < 4 { if args.len() < 4 {
return Err(Error::ModuleError("no CSV file specified".to_owned())); return Err(Error::ModuleError("no CSV file specified".to_owned()));
} }
@ -173,7 +173,7 @@ impl VTabCursor<CSVTab> for CSVTabCursor {
} }
fn filter(&mut self, fn filter(&mut self,
_idx_num: libc::c_int, _idx_num: c_int,
_idx_str: Option<&str>, _idx_str: Option<&str>,
_args: &Values) _args: &Values)
-> Result<()> { -> Result<()> {
@ -203,7 +203,7 @@ impl VTabCursor<CSVTab> for CSVTabCursor {
fn eof(&self) -> bool { fn eof(&self) -> bool {
self.eof self.eof
} }
fn column(&self, ctx: &mut Context, col: libc::c_int) -> Result<()> { fn column(&self, ctx: &mut Context, col: c_int) -> Result<()> {
if col < 0 || col as usize >= self.cols.len() { if col < 0 || col as usize >= self.cols.len() {
return Err(Error::ModuleError(format!("column index out of bounds: {}", col))); return Err(Error::ModuleError(format!("column index out of bounds: {}", col)));
} }

View File

@ -3,8 +3,8 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::default::Default; use std::default::Default;
use std::mem; use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::rc::Rc; use std::rc::Rc;
use libc;
use {Connection, Error, Result}; use {Connection, Error, Result};
use ffi; use ffi;
@ -62,7 +62,7 @@ struct IntArrayVTab {
impl VTab<IntArrayVTabCursor> for IntArrayVTab { impl VTab<IntArrayVTabCursor> for IntArrayVTab {
fn connect(db: *mut ffi::sqlite3, fn connect(db: *mut ffi::sqlite3,
aux: *mut libc::c_void, aux: *mut c_void,
_args: &[&[u8]]) _args: &[&[u8]])
-> Result<IntArrayVTab> { -> Result<IntArrayVTab> {
let array = unsafe { mem::transmute(aux) }; let array = unsafe { mem::transmute(aux) };
@ -106,7 +106,7 @@ impl VTabCursor<IntArrayVTab> for IntArrayVTabCursor {
unsafe { &mut *(self.base.pVtab as *mut IntArrayVTab) } unsafe { &mut *(self.base.pVtab as *mut IntArrayVTab) }
} }
fn filter(&mut self, fn filter(&mut self,
_idx_num: libc::c_int, _idx_num: c_int,
_idx_str: Option<&str>, _idx_str: Option<&str>,
_args: &Values) _args: &Values)
-> Result<()> { -> Result<()> {
@ -124,7 +124,7 @@ impl VTabCursor<IntArrayVTab> for IntArrayVTabCursor {
self.i >= array.len() self.i >= array.len()
} }
} }
fn column(&self, ctx: &mut Context, _i: libc::c_int) -> Result<()> { fn column(&self, ctx: &mut Context, _i: c_int) -> Result<()> {
let vtab = self.vtab(); let vtab = self.vtab();
unsafe { unsafe {
let array = (*vtab.array).borrow(); let array = (*vtab.array).borrow();
@ -170,8 +170,8 @@ mod test {
s.query_map(&[], |row| { s.query_map(&[], |row| {
let i1: i64 = row.get(0); let i1: i64 = row.get(0);
assert!(i1 == 1 || i1 == 3); assert!(i1 == 1 || i1 == 3);
assert_eq!(11, row.get(1)); assert_eq!(11, row.get::<i32, i32>(1));
assert_eq!(-5, row.get(2)); assert_eq!(-5, row.get::<i32, i32>(2));
}).unwrap(); }).unwrap();
} }
@ -185,9 +185,9 @@ mod test {
{ {
let mut rows = s.query(&[]).unwrap(); let mut rows = s.query(&[]).unwrap();
let row = rows.next().unwrap().unwrap(); let row = rows.next().unwrap().unwrap();
assert_eq!(1, row.get(0)); assert_eq!(1, row.get::<i32, i32>(0));
assert_eq!(11, row.get(1)); assert_eq!(11, row.get::<i32, i32>(1));
assert_eq!(-5, row.get(2)); assert_eq!(-5, row.get::<i32, i32>(2));
} }
p2.borrow_mut().clear(); p2.borrow_mut().clear();

View File

@ -3,9 +3,9 @@
use std::borrow::Cow::{self, Borrowed, Owned}; use std::borrow::Cow::{self, Borrowed, Owned};
use std::ffi::CString; use std::ffi::CString;
use std::mem; use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr; use std::ptr;
use std::slice; use std::slice;
use libc;
use {Connection, Error, Result, InnerConnection, str_to_cstring}; use {Connection, Error, Result, InnerConnection, str_to_cstring};
use error::error_from_sqlite_code; use error::error_from_sqlite_code;
@ -46,7 +46,7 @@ pub trait VTab<C: VTabCursor<Self>>: Sized {
/// Create a new instance of a virtual table in response to a CREATE VIRTUAL TABLE statement. /// Create a new instance of a virtual table in response to a CREATE VIRTUAL TABLE statement.
/// The `db` parameter is a pointer to the SQLite database connection that is executing /// The `db` parameter is a pointer to the SQLite database connection that is executing
/// the CREATE VIRTUAL TABLE statement. /// the CREATE VIRTUAL TABLE statement.
fn connect(db: *mut ffi::sqlite3, aux: *mut libc::c_void, args: &[&[u8]]) -> Result<Self>; fn connect(db: *mut ffi::sqlite3, aux: *mut c_void, args: &[&[u8]]) -> Result<Self>;
/// Determine the best way to access the virtual table. /// Determine the best way to access the virtual table.
fn best_index(&self, info: &mut IndexInfo) -> Result<()>; fn best_index(&self, info: &mut IndexInfo) -> Result<()>;
/// Create a new cursor used for accessing a virtual table. /// Create a new cursor used for accessing a virtual table.
@ -56,7 +56,7 @@ pub trait VTab<C: VTabCursor<Self>>: Sized {
bitflags! { bitflags! {
#[doc = "Index constraint operator."] #[doc = "Index constraint operator."]
#[repr(C)] #[repr(C)]
pub flags IndexConstraintOp: ::libc::c_uchar { pub flags IndexConstraintOp: ::std::os::raw::c_uchar {
const SQLITE_INDEX_CONSTRAINT_EQ = 2, const SQLITE_INDEX_CONSTRAINT_EQ = 2,
const SQLITE_INDEX_CONSTRAINT_GT = 4, const SQLITE_INDEX_CONSTRAINT_GT = 4,
const SQLITE_INDEX_CONSTRAINT_LE = 8, const SQLITE_INDEX_CONSTRAINT_LE = 8,
@ -80,7 +80,7 @@ impl IndexInfo {
unsafe { (*self.0).nOrderBy as usize } unsafe { (*self.0).nOrderBy as usize }
} }
/// Column number /// Column number
pub fn order_by_column(&self, order_by_idx: usize) -> libc::c_int { pub fn order_by_column(&self, order_by_idx: usize) -> c_int {
unsafe { unsafe {
let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize); let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize);
order_bys[order_by_idx].iColumn order_bys[order_by_idx].iColumn
@ -103,7 +103,7 @@ impl IndexInfo {
} }
/// Number used to identify the index /// Number used to identify the index
pub fn set_idx_num(&mut self, idx_num: libc::c_int) { pub fn set_idx_num(&mut self, idx_num: c_int) {
unsafe { unsafe {
(*self.0).idxNum = idx_num; (*self.0).idxNum = idx_num;
} }
@ -147,7 +147,7 @@ pub struct IndexConstraint<'a>(&'a ffi::sqlite3_index_constraint);
impl<'a> IndexConstraint<'a> { impl<'a> IndexConstraint<'a> {
/// Column constrained. -1 for ROWID /// Column constrained. -1 for ROWID
pub fn column(&self) -> libc::c_int { pub fn column(&self) -> c_int {
self.0.iColumn self.0.iColumn
} }
/// Constraint operator /// Constraint operator
@ -164,7 +164,7 @@ pub struct IndexConstraintUsage<'a>(&'a mut ffi::sqlite3_index_constraint_usage)
impl<'a> IndexConstraintUsage<'a> { impl<'a> IndexConstraintUsage<'a> {
/// if `argv_index` > 0, constraint is part of argv to xFilter /// if `argv_index` > 0, constraint is part of argv to xFilter
pub fn set_argv_index(&mut self, argv_index: libc::c_int) { pub fn set_argv_index(&mut self, argv_index: c_int) {
self.0.argvIndex = argv_index; self.0.argvIndex = argv_index;
} }
/// if `omit`, do not code a test for this constraint /// if `omit`, do not code a test for this constraint
@ -178,7 +178,7 @@ pub trait VTabCursor<V: VTab<Self>>: Sized {
/// Accessor to the associated virtual table. /// Accessor to the associated virtual table.
fn vtab(&self) -> &mut V; fn vtab(&self) -> &mut V;
/// Begin a search of a virtual table. /// Begin a search of a virtual table.
fn filter(&mut self, idx_num: libc::c_int, idx_str: Option<&str>, args: &Values) -> Result<()>; fn filter(&mut self, idx_num: c_int, idx_str: Option<&str>, args: &Values) -> Result<()>;
/// Advance cursor to the next row of a result set initiated by `filter`. /// Advance cursor to the next row of a result set initiated by `filter`.
fn next(&mut self) -> Result<()>; fn next(&mut self) -> Result<()>;
/// Must return `false` if the cursor currently points to a valid row of data, /// Must return `false` if the cursor currently points to a valid row of data,
@ -187,7 +187,7 @@ pub trait VTabCursor<V: VTab<Self>>: Sized {
/// Find the value for the `i`-th column of the current row. /// Find the value for the `i`-th column of the current row.
/// `i` is zero-based so the first column is numbered 0. /// `i` is zero-based so the first column is numbered 0.
/// May return its result back to SQLite using one of the specified `ctx`. /// May return its result back to SQLite using one of the specified `ctx`.
fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()>; fn column(&self, ctx: &mut Context, i: c_int) -> Result<()>;
/// Return the rowid of row that the cursor is currently pointing at. /// Return the rowid of row that the cursor is currently pointing at.
fn rowid(&self) -> Result<i64>; fn rowid(&self) -> Result<i64>;
} }
@ -228,6 +228,7 @@ impl<'a> Values<'a> {
FromSqlError::Other(err) => { FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err) Error::FromSqlConversionFailure(idx, value.data_type(), err)
} }
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx as c_int, i),
}) })
} }
@ -326,7 +327,7 @@ pub fn escape_double_quote(identifier: &str) -> Cow<str> {
} }
// FIXME copy/paste from function.rs // FIXME copy/paste from function.rs
unsafe extern "C" fn free_boxed_value<T>(p: *mut libc::c_void) { unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
let _: Box<T> = Box::from_raw(mem::transmute(p)); let _: Box<T> = Box::from_raw(mem::transmute(p));
} }
@ -367,12 +368,12 @@ static $module_name: ffi::sqlite3_module = ffi::sqlite3_module {
}; };
unsafe extern "C" fn $connect(db: *mut ffi::sqlite3, unsafe extern "C" fn $connect(db: *mut ffi::sqlite3,
aux: *mut libc::c_void, aux: *mut c_void,
argc: libc::c_int, argc: c_int,
argv: *const *const libc::c_char, argv: *const *const c_char,
pp_vtab: *mut *mut ffi::sqlite3_vtab, pp_vtab: *mut *mut ffi::sqlite3_vtab,
err_msg: *mut *mut libc::c_char) err_msg: *mut *mut c_char)
-> libc::c_int { -> c_int {
use std::error::Error as StdError; use std::error::Error as StdError;
use std::ffi::CStr; use std::ffi::CStr;
use std::slice; use std::slice;
@ -401,7 +402,7 @@ unsafe extern "C" fn $connect(db: *mut ffi::sqlite3,
} }
unsafe extern "C" fn $best_index(vtab: *mut ffi::sqlite3_vtab, unsafe extern "C" fn $best_index(vtab: *mut ffi::sqlite3_vtab,
info: *mut ffi::sqlite3_index_info) info: *mut ffi::sqlite3_index_info)
-> libc::c_int { -> c_int {
use std::error::Error as StdError; use std::error::Error as StdError;
use vtab::set_err_msg; use vtab::set_err_msg;
let vt = vtab as *mut $vtab; let vt = vtab as *mut $vtab;
@ -422,7 +423,7 @@ unsafe extern "C" fn $best_index(vtab: *mut ffi::sqlite3_vtab,
} }
} }
unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> libc::c_int { unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> c_int {
let vtab = vtab as *mut $vtab; let vtab = vtab as *mut $vtab;
let _: Box<$vtab> = Box::from_raw(vtab); let _: Box<$vtab> = Box::from_raw(vtab);
ffi::SQLITE_OK ffi::SQLITE_OK
@ -430,7 +431,7 @@ unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> libc::c_int {
unsafe extern "C" fn $open(vtab: *mut ffi::sqlite3_vtab, unsafe extern "C" fn $open(vtab: *mut ffi::sqlite3_vtab,
pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor) pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor)
-> libc::c_int { -> c_int {
use std::error::Error as StdError; use std::error::Error as StdError;
use vtab::set_err_msg; use vtab::set_err_msg;
let vt = vtab as *mut $vtab; let vt = vtab as *mut $vtab;
@ -452,18 +453,18 @@ unsafe extern "C" fn $open(vtab: *mut ffi::sqlite3_vtab,
} }
} }
} }
unsafe extern "C" fn $close(cursor: *mut ffi::sqlite3_vtab_cursor) -> libc::c_int { unsafe extern "C" fn $close(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
let cr = cursor as *mut $cursor; let cr = cursor as *mut $cursor;
let _: Box<$cursor> = Box::from_raw(cr); let _: Box<$cursor> = Box::from_raw(cr);
ffi::SQLITE_OK ffi::SQLITE_OK
} }
unsafe extern "C" fn $filter(cursor: *mut ffi::sqlite3_vtab_cursor, unsafe extern "C" fn $filter(cursor: *mut ffi::sqlite3_vtab_cursor,
idx_num: libc::c_int, idx_num: c_int,
idx_str: *const libc::c_char, idx_str: *const c_char,
argc: libc::c_int, argc: c_int,
argv: *mut *mut ffi::sqlite3_value) argv: *mut *mut ffi::sqlite3_value)
-> libc::c_int { -> c_int {
use std::ffi::CStr; use std::ffi::CStr;
use std::slice; use std::slice;
use std::str; use std::str;
@ -479,19 +480,19 @@ unsafe extern "C" fn $filter(cursor: *mut ffi::sqlite3_vtab_cursor,
let cr = cursor as *mut $cursor; let cr = cursor as *mut $cursor;
cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values)) cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
} }
unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> libc::c_int { unsafe extern "C" fn $next(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
use vtab::cursor_error; use vtab::cursor_error;
let cr = cursor as *mut $cursor; let cr = cursor as *mut $cursor;
cursor_error(cursor, (*cr).next()) cursor_error(cursor, (*cr).next())
} }
unsafe extern "C" fn $eof(cursor: *mut ffi::sqlite3_vtab_cursor) -> libc::c_int { unsafe extern "C" fn $eof(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int {
let cr = cursor as *mut $cursor; let cr = cursor as *mut $cursor;
(*cr).eof() as libc::c_int (*cr).eof() as c_int
} }
unsafe extern "C" fn $column(cursor: *mut ffi::sqlite3_vtab_cursor, unsafe extern "C" fn $column(cursor: *mut ffi::sqlite3_vtab_cursor,
ctx: *mut ffi::sqlite3_context, ctx: *mut ffi::sqlite3_context,
i: libc::c_int) i: c_int)
-> libc::c_int { -> c_int {
use vtab::{result_error, Context}; use vtab::{result_error, Context};
let cr = cursor as *mut $cursor; let cr = cursor as *mut $cursor;
let mut ctxt = Context(ctx); let mut ctxt = Context(ctx);
@ -499,7 +500,7 @@ unsafe extern "C" fn $column(cursor: *mut ffi::sqlite3_vtab_cursor,
} }
unsafe extern "C" fn $rowid(cursor: *mut ffi::sqlite3_vtab_cursor, unsafe extern "C" fn $rowid(cursor: *mut ffi::sqlite3_vtab_cursor,
p_rowid: *mut ffi::sqlite3_int64) p_rowid: *mut ffi::sqlite3_int64)
-> libc::c_int { -> c_int {
use vtab::cursor_error; use vtab::cursor_error;
let cr = cursor as *mut $cursor; let cr = cursor as *mut $cursor;
match (*cr).rowid() { match (*cr).rowid() {
@ -516,7 +517,7 @@ unsafe extern "C" fn $rowid(cursor: *mut ffi::sqlite3_vtab_cursor,
/// Virtual table cursors can set an error message by assigning a string to `zErrMsg`. /// Virtual table cursors can set an error message by assigning a string to `zErrMsg`.
pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor,
result: Result<T>) result: Result<T>)
-> libc::c_int { -> c_int {
use std::error::Error as StdError; use std::error::Error as StdError;
match result { match result {
Ok(_) => ffi::SQLITE_OK, Ok(_) => ffi::SQLITE_OK,
@ -536,14 +537,14 @@ pub unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor,
/// Virtual tables methods can set an error message by assigning a string to `zErrMsg`. /// Virtual tables methods can set an error message by assigning a string to `zErrMsg`.
pub unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) { pub unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
if !(*vtab).zErrMsg.is_null() { if !(*vtab).zErrMsg.is_null() {
ffi::sqlite3_free((*vtab).zErrMsg as *mut libc::c_void); ffi::sqlite3_free((*vtab).zErrMsg as *mut c_void);
} }
(*vtab).zErrMsg = mprintf(err_msg); (*vtab).zErrMsg = mprintf(err_msg);
} }
/// To raise an error, the `column` method should use this method to set the error message /// To raise an error, the `column` method should use this method to set the error message
/// and return the error code. /// and return the error code.
unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> libc::c_int { unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
use std::error::Error as StdError; use std::error::Error as StdError;
match result { match result {
Ok(_) => ffi::SQLITE_OK, Ok(_) => ffi::SQLITE_OK,
@ -576,7 +577,7 @@ unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) ->
// Space to hold this error message string must be obtained // Space to hold this error message string must be obtained
// from an SQLite memory allocation function. // from an SQLite memory allocation function.
pub fn mprintf(err_msg: &str) -> *mut ::libc::c_char { pub fn mprintf(err_msg: &str) -> *mut c_char {
let c_format = CString::new("%s").unwrap(); let c_format = CString::new("%s").unwrap();
let c_err = CString::new(err_msg).unwrap(); let c_err = CString::new(err_msg).unwrap();
unsafe { ffi::sqlite3_mprintf(c_format.as_ptr(), c_err.as_ptr()) } unsafe { ffi::sqlite3_mprintf(c_format.as_ptr(), c_err.as_ptr()) }

View File

@ -1,7 +1,7 @@
//! generate series virtual table. //! generate series virtual table.
//! Port of C [generate series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c). //! Port of C [generate series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c).
use std::default::Default; use std::default::Default;
use libc; use std::os::raw::{c_char, c_int, c_void};
use {Connection, Error, Result}; use {Connection, Error, Result};
use ffi; use ffi;
@ -30,14 +30,14 @@ init_module!(SERIES_MODULE,
series_rowid); series_rowid);
// Column numbers // Column numbers
// const SERIES_COLUMN_VALUE : libc::c_int = 0; // const SERIES_COLUMN_VALUE : c_int = 0;
const SERIES_COLUMN_START: libc::c_int = 1; const SERIES_COLUMN_START: c_int = 1;
const SERIES_COLUMN_STOP: libc::c_int = 2; const SERIES_COLUMN_STOP: c_int = 2;
const SERIES_COLUMN_STEP: libc::c_int = 3; const SERIES_COLUMN_STEP: c_int = 3;
bitflags! { bitflags! {
#[repr(C)] #[repr(C)]
flags QueryPlanFlags: ::libc::c_int { flags QueryPlanFlags: ::std::os::raw::c_int {
// start = $value -- constraint exists // start = $value -- constraint exists
const START = 1, const START = 1,
// stop = $value -- constraint exists // stop = $value -- constraint exists
@ -62,7 +62,7 @@ struct SeriesTab {
impl VTab<SeriesTabCursor> for SeriesTab { impl VTab<SeriesTabCursor> for SeriesTab {
fn connect(db: *mut ffi::sqlite3, fn connect(db: *mut ffi::sqlite3,
_aux: *mut libc::c_void, _aux: *mut c_void,
_args: &[&[u8]]) _args: &[&[u8]])
-> Result<SeriesTab> { -> Result<SeriesTab> {
let vtab = SeriesTab { base: Default::default() }; let vtab = SeriesTab { base: Default::default() };
@ -176,7 +176,7 @@ impl VTabCursor<SeriesTab> for SeriesTabCursor {
unsafe { &mut *(self.base.pVtab as *mut SeriesTab) } unsafe { &mut *(self.base.pVtab as *mut SeriesTab) }
} }
fn filter(&mut self, fn filter(&mut self,
idx_num: libc::c_int, idx_num: c_int,
_idx_str: Option<&str>, _idx_str: Option<&str>,
args: &Values) args: &Values)
-> Result<()> { -> Result<()> {
@ -230,7 +230,7 @@ impl VTabCursor<SeriesTab> for SeriesTabCursor {
self.value > self.max_value self.value > self.max_value
} }
} }
fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()> { fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
let x = match i { let x = match i {
SERIES_COLUMN_START => self.min_value, SERIES_COLUMN_START => self.min_value,
SERIES_COLUMN_STOP => self.max_value, SERIES_COLUMN_STOP => self.max_value,
@ -264,7 +264,7 @@ mod test {
let mut s = db.prepare("SELECT * FROM generate_series(0,20,5)").unwrap(); let mut s = db.prepare("SELECT * FROM generate_series(0,20,5)").unwrap();
let series = s.query_map(&[], |row| row.get(0)) let series = s.query_map(&[], |row| row.get::<i32, i32>(0))
.unwrap(); .unwrap();
let mut expected = 0; let mut expected = 0;

View File

@ -3,12 +3,11 @@
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate libc;
extern crate rusqlite; extern crate rusqlite;
#[cfg(feature = "trace")] #[cfg(feature = "trace")]
fn main() { fn main() {
use libc::c_int; use std::os::raw::c_int;
use std::sync::Mutex; use std::sync::Mutex;
lazy_static! { lazy_static! {

View File

@ -7,14 +7,17 @@ extern crate libsqlite3_sys as ffi;
use rusqlite::Connection; use rusqlite::Connection;
#[test] #[test]
#[should_panic]
fn test_error_when_singlethread_mode() { fn test_error_when_singlethread_mode() {
// put SQLite into single-threaded mode // put SQLite into single-threaded mode
unsafe { unsafe {
// 1 == SQLITE_CONFIG_SINGLETHREAD if ffi::sqlite3_config(ffi::SQLITE_CONFIG_SINGLETHREAD) != ffi::SQLITE_OK {
assert_eq!(ffi::sqlite3_config(1), ffi::SQLITE_OK); return;
println!("{}", ffi::sqlite3_mutex_alloc(0) as u64); }
if ffi::sqlite3_initialize() != ffi::SQLITE_OK {
return;
}
} }
let result = Connection::open_in_memory(); let _ = Connection::open_in_memory().unwrap();
assert!(result.is_err());
} }