mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-11-04 08:08:55 +08:00 
			
		
		
		
	Merge remote-tracking branch 'jgallagher/master' into 0.14
This commit is contained in:
		
							
								
								
									
										10
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,5 +1,4 @@
 | 
			
		||||
sudo: false
 | 
			
		||||
dist: trusty
 | 
			
		||||
 | 
			
		||||
language: rust
 | 
			
		||||
 | 
			
		||||
@@ -39,7 +38,8 @@ script:
 | 
			
		||||
  - cargo test --features bundled
 | 
			
		||||
  - cargo test --features sqlcipher
 | 
			
		||||
  - cargo test --features "unlock_notify bundled"
 | 
			
		||||
  - cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace"
 | 
			
		||||
  - cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace buildtime_bindgen"
 | 
			
		||||
  - cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace bundled"
 | 
			
		||||
  - cargo test --features "backup blob chrono functions hooks limits load_extension serde_json trace bundled buildtime_bindgen"
 | 
			
		||||
  - cargo test --features "array csvtab vtab"
 | 
			
		||||
  - cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab"
 | 
			
		||||
  - cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab buildtime_bindgen"
 | 
			
		||||
  - cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled"
 | 
			
		||||
  - cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled buildtime_bindgen"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								Cargo.toml
									
									
									
									
									
								
							@@ -29,6 +29,10 @@ limits = []
 | 
			
		||||
hooks = []
 | 
			
		||||
sqlcipher = ["libsqlite3-sys/sqlcipher"]
 | 
			
		||||
unlock_notify = ["libsqlite3-sys/unlock_notify"]
 | 
			
		||||
vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7", "lazy_static"]
 | 
			
		||||
csvtab = ["csv", "vtab"]
 | 
			
		||||
# pointer passing interfaces: 3.20.0
 | 
			
		||||
array = ["vtab", "bundled"]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
time = "0.1.0"
 | 
			
		||||
@@ -36,6 +40,8 @@ bitflags = "1.0"
 | 
			
		||||
lru-cache = "0.1"
 | 
			
		||||
chrono = { version = "0.4", optional = true }
 | 
			
		||||
serde_json = { version = "1.0", optional = true }
 | 
			
		||||
csv = { version = "1.0", optional = true }
 | 
			
		||||
lazy_static = { version = "1.0", optional = true }
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
tempdir = "0.3"
 | 
			
		||||
@@ -53,8 +59,11 @@ harness = false
 | 
			
		||||
[[test]]
 | 
			
		||||
name = "deny_single_threaded_sqlite_config"
 | 
			
		||||
 | 
			
		||||
[[test]]
 | 
			
		||||
name = "vtab"
 | 
			
		||||
 | 
			
		||||
[package.metadata.docs.rs]
 | 
			
		||||
features = [ "backup", "blob", "chrono", "functions", "limits", "load_extension", "serde_json", "trace" ]
 | 
			
		||||
features = [ "backup", "blob", "chrono", "functions", "limits", "load_extension", "serde_json", "trace", "vtab" ]
 | 
			
		||||
all-features = false
 | 
			
		||||
no-default-features = true
 | 
			
		||||
default-target = "x86_64-unknown-linux-gnu"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								README.md
									
									
									
									
									
								
							@@ -4,10 +4,10 @@
 | 
			
		||||
[](https://ci.appveyor.com/project/jgallagher/rusqlite)
 | 
			
		||||
[](https://deps.rs/repo/github/jgallagher/rusqlite)
 | 
			
		||||
[](https://crates.io/crates/rusqlite)
 | 
			
		||||
[](https://docs.rs/rusqlite)
 | 
			
		||||
 | 
			
		||||
Rusqlite is an ergonomic wrapper for using SQLite from Rust. It attempts to expose
 | 
			
		||||
an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres). View the full
 | 
			
		||||
[API documentation](http://docs.rs/rusqlite/).
 | 
			
		||||
an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres).
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
extern crate rusqlite;
 | 
			
		||||
@@ -68,31 +68,36 @@ newer SQLite version; see details below.
 | 
			
		||||
### Optional Features
 | 
			
		||||
 | 
			
		||||
Rusqlite provides several features that are behind [Cargo
 | 
			
		||||
features](http://doc.crates.io/manifest.html#the-features-section). They are:
 | 
			
		||||
features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section). They are:
 | 
			
		||||
 | 
			
		||||
* [`load_extension`](http://jgallagher.github.io/rusqlite/rusqlite/struct.LoadExtensionGuard.html)
 | 
			
		||||
* [`load_extension`](https://docs.rs/rusqlite/0.13.0/rusqlite/struct.LoadExtensionGuard.html)
 | 
			
		||||
  allows loading dynamic library-based SQLite extensions.
 | 
			
		||||
* [`backup`](http://jgallagher.github.io/rusqlite/rusqlite/backup/index.html)
 | 
			
		||||
* [`backup`](https://docs.rs/rusqlite/0.13.0/rusqlite/backup/index.html)
 | 
			
		||||
  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`](https://docs.rs/rusqlite/0.13.0/rusqlite/functions/index.html)
 | 
			
		||||
  allows you to load Rust closures into SQLite connections for use in queries.
 | 
			
		||||
  Note: This feature requires SQLite 3.7.3 or later.
 | 
			
		||||
* [`trace`](http://jgallagher.github.io/rusqlite/rusqlite/trace/index.html)
 | 
			
		||||
* [`trace`](https://docs.rs/rusqlite/0.13.0/rusqlite/trace/index.html)
 | 
			
		||||
  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`](https://docs.rs/rusqlite/0.13.0/rusqlite/blob/index.html)
 | 
			
		||||
  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)
 | 
			
		||||
* [`limits`](https://docs.rs/rusqlite/0.13.0/rusqlite/struct.Connection.html#method.limit)
 | 
			
		||||
  allows you to set and retrieve SQLite's per connection limits.
 | 
			
		||||
* `chrono` implements [`FromSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.FromSql.html)
 | 
			
		||||
  and [`ToSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.ToSql.html) for various
 | 
			
		||||
* `chrono` implements [`FromSql`](https://docs.rs/rusqlite/0.13.0/rusqlite/types/trait.FromSql.html)
 | 
			
		||||
  and [`ToSql`](https://docs.rs/rusqlite/0.13.0/rusqlite/types/trait.ToSql.html) for various
 | 
			
		||||
  types from the [`chrono` crate](https://crates.io/crates/chrono).
 | 
			
		||||
* `serde_json` implements [`FromSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.FromSql.html)
 | 
			
		||||
  and [`ToSql`](http://jgallagher.github.io/rusqlite/rusqlite/types/trait.ToSql.html) for the
 | 
			
		||||
* `serde_json` implements [`FromSql`](https://docs.rs/rusqlite/0.13.0/rusqlite/types/trait.FromSql.html)
 | 
			
		||||
  and [`ToSql`](https://docs.rs/rusqlite/0.13.0/rusqlite/types/trait.ToSql.html) for the
 | 
			
		||||
  `Value` type from the [`serde_json` crate](https://crates.io/crates/serde_json).
 | 
			
		||||
* `bundled` uses a bundled version of sqlite3.  This is a good option for cases where linking to sqlite3 is complicated, such as Windows.
 | 
			
		||||
* `sqlcipher` looks for the SQLCipher library to link against instead of SQLite. This feature is mutually exclusive with `bundled`.
 | 
			
		||||
* `hooks` for [Commit, Rollback](http://sqlite.org/c3ref/commit_hook.html) and [Data Change](http://sqlite.org/c3ref/update_hook.html) notification callbacks.
 | 
			
		||||
* `unlock_notify` for [Unlock](https://sqlite.org/unlock_notify.html) notification.
 | 
			
		||||
* `vtab` for [virtual table](https://sqlite.org/vtab.html) support (allows you to write virtual table implemntations in Rust). Currently, only read-only virtual tables are supported.
 | 
			
		||||
* [`csvtab`](https://sqlite.org/csv.html), CSV virtual table written in Rust.
 | 
			
		||||
* [`array`](https://sqlite.org/carray.html), The `rarray()` Table-Valued Function.
 | 
			
		||||
 | 
			
		||||
## Notes on building rusqlite and libsqlite3-sys
 | 
			
		||||
 | 
			
		||||
@@ -105,11 +110,11 @@ You can adjust this behavior in a number of ways:
 | 
			
		||||
* If you use the `bundled` feature, `libsqlite3-sys` will use the
 | 
			
		||||
  [gcc](https://crates.io/crates/gcc) crate to compile SQLite from source and
 | 
			
		||||
  link against that. This source is embedded in the `libsqlite3-sys` crate and
 | 
			
		||||
  is currently SQLite 3.17.0 (as of `rusqlite` 0.10.1 / `libsqlite3-sys`
 | 
			
		||||
  0.7.1).  This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file:
 | 
			
		||||
  is currently SQLite 3.24.0 (as of `rusqlite` 0.14.0 / `libsqlite3-sys`
 | 
			
		||||
  0.9.3).  This is probably the simplest solution to any build problems. You can enable this by adding the following in your `Cargo.toml` file:
 | 
			
		||||
  ```
 | 
			
		||||
  [dependencies.rusqlite]
 | 
			
		||||
  version = "0.11.0"
 | 
			
		||||
  version = "0.14.0"
 | 
			
		||||
  features = ["bundled"]
 | 
			
		||||
  ```
 | 
			
		||||
* You can set the `SQLITE3_LIB_DIR` to point to directory containing the SQLite
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								appveyor.yml
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								appveyor.yml
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
			
		||||
environment:
 | 
			
		||||
  matrix:
 | 
			
		||||
  - TARGET: 1.25.0-x86_64-pc-windows-gnu
 | 
			
		||||
  - TARGET: 1.27.2-x86_64-pc-windows-gnu
 | 
			
		||||
    MSYS2_BITS: 64
 | 
			
		||||
  - TARGET: 1.25.0-x86_64-pc-windows-msvc
 | 
			
		||||
  - TARGET: 1.27.2-x86_64-pc-windows-msvc
 | 
			
		||||
    VCPKG_DEFAULT_TRIPLET: x64-windows
 | 
			
		||||
    VCPKGRS_DYNAMIC: 1
 | 
			
		||||
  - TARGET: nightly-x86_64-pc-windows-msvc
 | 
			
		||||
@@ -34,10 +34,10 @@ build: false
 | 
			
		||||
test_script:
 | 
			
		||||
  - cargo test --lib --verbose
 | 
			
		||||
  - cargo test --lib --verbose --features bundled
 | 
			
		||||
  - cargo test --lib --features "backup blob chrono functions hooks limits load_extension serde_json trace"
 | 
			
		||||
  - cargo test --lib --features "backup blob chrono functions hooks limits load_extension serde_json trace buildtime_bindgen"
 | 
			
		||||
  - cargo test --lib --features "backup blob chrono functions hooks limits load_extension serde_json trace bundled"
 | 
			
		||||
  - cargo test --lib --features "backup blob chrono functions hooks limits load_extension serde_json trace bundled buildtime_bindgen"
 | 
			
		||||
  - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab"
 | 
			
		||||
  - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab buildtime_bindgen"
 | 
			
		||||
  - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled"
 | 
			
		||||
  - cargo test --lib --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled buildtime_bindgen"
 | 
			
		||||
 | 
			
		||||
cache:
 | 
			
		||||
  - C:\Users\appveyor\.cargo
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "libsqlite3-sys"
 | 
			
		||||
version = "0.9.2"
 | 
			
		||||
version = "0.9.3"
 | 
			
		||||
authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
 | 
			
		||||
repository = "https://github.com/jgallagher/rusqlite"
 | 
			
		||||
description = "Native bindings to the libsqlite3 library"
 | 
			
		||||
@@ -20,12 +20,13 @@ min_sqlite_version_3_6_11 = ["pkg-config", "vcpkg"]
 | 
			
		||||
min_sqlite_version_3_6_23 = ["pkg-config", "vcpkg"]
 | 
			
		||||
min_sqlite_version_3_7_3 = ["pkg-config", "vcpkg"]
 | 
			
		||||
min_sqlite_version_3_7_4 = ["pkg-config", "vcpkg"]
 | 
			
		||||
min_sqlite_version_3_7_7 = ["pkg-config", "vcpkg"]
 | 
			
		||||
min_sqlite_version_3_7_16 = ["pkg-config", "vcpkg"]
 | 
			
		||||
# sqlite3_unlock_notify >= 3.6.12
 | 
			
		||||
unlock_notify = []
 | 
			
		||||
 | 
			
		||||
[build-dependencies]
 | 
			
		||||
bindgen = { version = "0.36", optional = true }
 | 
			
		||||
bindgen = { version = "0.38", optional = true }
 | 
			
		||||
pkg-config = { version = "0.3", optional = true }
 | 
			
		||||
cc = { version = "1.0", optional = true }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2145
									
								
								libsqlite3-sys/bindgen-bindings/bindgen_3.7.7.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2145
									
								
								libsqlite3-sys/bindgen-bindings/bindgen_3.7.7.rs
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -5,8 +5,8 @@ fn main() {
 | 
			
		||||
#[cfg(feature = "bundled")]
 | 
			
		||||
mod build {
 | 
			
		||||
    extern crate cc;
 | 
			
		||||
    use std::{env, fs};
 | 
			
		||||
    use std::path::Path;
 | 
			
		||||
    use std::{env, fs};
 | 
			
		||||
 | 
			
		||||
    pub fn main() {
 | 
			
		||||
        if cfg!(feature = "sqlcipher") {
 | 
			
		||||
@@ -43,6 +43,8 @@ mod build {
 | 
			
		||||
            cfg.flag("-DSQLITE_ENABLE_UNLOCK_NOTIFY");
 | 
			
		||||
        }
 | 
			
		||||
        cfg.compile("libsqlite3.a");
 | 
			
		||||
 | 
			
		||||
        println!("cargo:lib_dir={}", out_dir);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -66,8 +68,10 @@ mod build {
 | 
			
		||||
            match header {
 | 
			
		||||
                HeaderLocation::FromEnvironment => {
 | 
			
		||||
                    let prefix = env_prefix();
 | 
			
		||||
                    let mut header = env::var(format!("{}_INCLUDE_DIR", prefix))
 | 
			
		||||
                        .expect(&format!("{}_INCLUDE_DIR must be set if {}_LIB_DIR is set", prefix, prefix));
 | 
			
		||||
                    let mut header = env::var(format!("{}_INCLUDE_DIR", prefix)).expect(&format!(
 | 
			
		||||
                        "{}_INCLUDE_DIR must be set if {}_LIB_DIR is set",
 | 
			
		||||
                        prefix, prefix
 | 
			
		||||
                    ));
 | 
			
		||||
                    header.push_str("/sqlite3.h");
 | 
			
		||||
                    header
 | 
			
		||||
                }
 | 
			
		||||
@@ -86,6 +90,11 @@ mod build {
 | 
			
		||||
    fn find_sqlite() -> HeaderLocation {
 | 
			
		||||
        let link_lib = link_lib();
 | 
			
		||||
 | 
			
		||||
        println!("cargo:rerun-if-env-changed={}_INCLUDE_DIR", env_prefix());
 | 
			
		||||
        println!("cargo:rerun-if-env-changed={}_LIB_DIR", env_prefix());
 | 
			
		||||
        if cfg!(target_os = "windows") {
 | 
			
		||||
            println!("cargo:rerun-if-env-changed=PATH");
 | 
			
		||||
        }
 | 
			
		||||
        // Allow users to specify where to find SQLite.
 | 
			
		||||
        if let Ok(dir) = env::var(format!("{}_LIB_DIR", env_prefix())) {
 | 
			
		||||
            println!("cargo:rustc-link-lib={}", link_lib);
 | 
			
		||||
@@ -98,7 +107,10 @@ mod build {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // See if pkg-config can do everything for us.
 | 
			
		||||
        match pkg_config::Config::new().print_system_libs(false).probe(link_lib) {
 | 
			
		||||
        match pkg_config::Config::new()
 | 
			
		||||
            .print_system_libs(false)
 | 
			
		||||
            .probe(link_lib)
 | 
			
		||||
        {
 | 
			
		||||
            Ok(mut lib) => {
 | 
			
		||||
                if let Some(mut header) = lib.include_paths.pop() {
 | 
			
		||||
                    header.push("sqlite3.h");
 | 
			
		||||
@@ -155,25 +167,21 @@ mod build {
 | 
			
		||||
    mod bindings {
 | 
			
		||||
        use super::HeaderLocation;
 | 
			
		||||
 | 
			
		||||
        use std::{env, fs};
 | 
			
		||||
        use std::path::Path;
 | 
			
		||||
        use std::{env, fs};
 | 
			
		||||
 | 
			
		||||
        #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
        static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[
 | 
			
		||||
            "bindgen-bindings/bindgen_3.6.8.rs",
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "min_sqlite_version_3_6_11")]
 | 
			
		||||
            "bindgen-bindings/bindgen_3.6.11.rs",
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "min_sqlite_version_3_6_23")]
 | 
			
		||||
            "bindgen-bindings/bindgen_3.6.23.rs",
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "min_sqlite_version_3_7_3")]
 | 
			
		||||
            "bindgen-bindings/bindgen_3.7.3.rs",
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "min_sqlite_version_3_7_4")]
 | 
			
		||||
            "bindgen-bindings/bindgen_3.7.4.rs",
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "min_sqlite_version_3_7_7")]
 | 
			
		||||
            "bindgen-bindings/bindgen_3.7.7.rs",
 | 
			
		||||
            #[cfg(feature = "min_sqlite_version_3_7_16")]
 | 
			
		||||
            "bindgen-bindings/bindgen_3.7.16.rs",
 | 
			
		||||
        ];
 | 
			
		||||
@@ -190,12 +198,12 @@ mod build {
 | 
			
		||||
    mod bindings {
 | 
			
		||||
        extern crate bindgen;
 | 
			
		||||
 | 
			
		||||
        use self::bindgen::callbacks::{ParseCallbacks, IntKind};
 | 
			
		||||
        use self::bindgen::callbacks::{IntKind, ParseCallbacks};
 | 
			
		||||
        use super::HeaderLocation;
 | 
			
		||||
 | 
			
		||||
        use std::env;
 | 
			
		||||
        use std::io::Write;
 | 
			
		||||
        use std::fs::OpenOptions;
 | 
			
		||||
        use std::io::Write;
 | 
			
		||||
        use std::path::Path;
 | 
			
		||||
 | 
			
		||||
        #[derive(Debug)]
 | 
			
		||||
@@ -242,7 +250,8 @@ mod build {
 | 
			
		||||
                .open(path.clone())
 | 
			
		||||
                .expect(&format!("Could not write to {:?}", path));
 | 
			
		||||
 | 
			
		||||
            file.write_all(output.as_bytes()).expect(&format!("Could not write to {:?}", path));
 | 
			
		||||
            file.write_all(output.as_bytes())
 | 
			
		||||
                .expect(&format!("Could not write to {:?}", path));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										124
									
								
								libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										124
									
								
								libsqlite3-sys/sqlite3/bindgen_bundled_version.rs
									
									
									
									
										vendored
									
									
								
							@@ -1,10 +1,10 @@
 | 
			
		||||
/* automatically generated by rust-bindgen */
 | 
			
		||||
 | 
			
		||||
pub const __GNUC_VA_LIST: i32 = 1;
 | 
			
		||||
pub const SQLITE_VERSION: &'static [u8; 7usize] = b"3.22.0\0";
 | 
			
		||||
pub const SQLITE_VERSION_NUMBER: i32 = 3022000;
 | 
			
		||||
pub const SQLITE_VERSION: &'static [u8; 7usize] = b"3.24.0\0";
 | 
			
		||||
pub const SQLITE_VERSION_NUMBER: i32 = 3024000;
 | 
			
		||||
pub const SQLITE_SOURCE_ID: &'static [u8; 85usize] =
 | 
			
		||||
    b"2018-01-22 18:45:57 0c55d179733b46d8d0ba4d88e01a25e10677046ee3da1d5b1581e86726f2171d\0";
 | 
			
		||||
    b"2018-06-04 19:24:41 c7ee0833225bfd8c5ec2f9bf62b97c4e04d03bd9566366d5221ac8fb199a87ca\0";
 | 
			
		||||
pub const SQLITE_OK: i32 = 0;
 | 
			
		||||
pub const SQLITE_ERROR: i32 = 1;
 | 
			
		||||
pub const SQLITE_INTERNAL: i32 = 2;
 | 
			
		||||
@@ -70,6 +70,7 @@ pub const SQLITE_IOERR_BEGIN_ATOMIC: i32 = 7434;
 | 
			
		||||
pub const SQLITE_IOERR_COMMIT_ATOMIC: i32 = 7690;
 | 
			
		||||
pub const SQLITE_IOERR_ROLLBACK_ATOMIC: i32 = 7946;
 | 
			
		||||
pub const SQLITE_LOCKED_SHAREDCACHE: i32 = 262;
 | 
			
		||||
pub const SQLITE_LOCKED_VTAB: i32 = 518;
 | 
			
		||||
pub const SQLITE_BUSY_RECOVERY: i32 = 261;
 | 
			
		||||
pub const SQLITE_BUSY_SNAPSHOT: i32 = 517;
 | 
			
		||||
pub const SQLITE_CANTOPEN_NOTEMPDIR: i32 = 270;
 | 
			
		||||
@@ -77,6 +78,7 @@ pub const SQLITE_CANTOPEN_ISDIR: i32 = 526;
 | 
			
		||||
pub const SQLITE_CANTOPEN_FULLPATH: i32 = 782;
 | 
			
		||||
pub const SQLITE_CANTOPEN_CONVPATH: i32 = 1038;
 | 
			
		||||
pub const SQLITE_CORRUPT_VTAB: i32 = 267;
 | 
			
		||||
pub const SQLITE_CORRUPT_SEQUENCE: i32 = 523;
 | 
			
		||||
pub const SQLITE_READONLY_RECOVERY: i32 = 264;
 | 
			
		||||
pub const SQLITE_READONLY_CANTLOCK: i32 = 520;
 | 
			
		||||
pub const SQLITE_READONLY_ROLLBACK: i32 = 776;
 | 
			
		||||
@@ -174,6 +176,7 @@ pub const SQLITE_FCNTL_PDB: i32 = 30;
 | 
			
		||||
pub const SQLITE_FCNTL_BEGIN_ATOMIC_WRITE: i32 = 31;
 | 
			
		||||
pub const SQLITE_FCNTL_COMMIT_ATOMIC_WRITE: i32 = 32;
 | 
			
		||||
pub const SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: i32 = 33;
 | 
			
		||||
pub const SQLITE_FCNTL_LOCK_TIMEOUT: i32 = 34;
 | 
			
		||||
pub const SQLITE_GET_LOCKPROXYFILE: i32 = 2;
 | 
			
		||||
pub const SQLITE_SET_LOCKPROXYFILE: i32 = 3;
 | 
			
		||||
pub const SQLITE_LAST_ERRNO: i32 = 4;
 | 
			
		||||
@@ -211,6 +214,7 @@ pub const SQLITE_CONFIG_PCACHE_HDRSZ: i32 = 24;
 | 
			
		||||
pub const SQLITE_CONFIG_PMASZ: i32 = 25;
 | 
			
		||||
pub const SQLITE_CONFIG_STMTJRNL_SPILL: i32 = 26;
 | 
			
		||||
pub const SQLITE_CONFIG_SMALL_MALLOC: i32 = 27;
 | 
			
		||||
pub const SQLITE_CONFIG_SORTERREF_SIZE: i32 = 28;
 | 
			
		||||
pub const SQLITE_DBCONFIG_MAINDBNAME: i32 = 1000;
 | 
			
		||||
pub const SQLITE_DBCONFIG_LOOKASIDE: i32 = 1001;
 | 
			
		||||
pub const SQLITE_DBCONFIG_ENABLE_FKEY: i32 = 1002;
 | 
			
		||||
@@ -220,7 +224,8 @@ pub const SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION: i32 = 1005;
 | 
			
		||||
pub const SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE: i32 = 1006;
 | 
			
		||||
pub const SQLITE_DBCONFIG_ENABLE_QPSG: i32 = 1007;
 | 
			
		||||
pub const SQLITE_DBCONFIG_TRIGGER_EQP: i32 = 1008;
 | 
			
		||||
pub const SQLITE_DBCONFIG_MAX: i32 = 1008;
 | 
			
		||||
pub const SQLITE_DBCONFIG_RESET_DATABASE: i32 = 1009;
 | 
			
		||||
pub const SQLITE_DBCONFIG_MAX: i32 = 1009;
 | 
			
		||||
pub const SQLITE_DENY: i32 = 1;
 | 
			
		||||
pub const SQLITE_IGNORE: i32 = 2;
 | 
			
		||||
pub const SQLITE_CREATE_INDEX: i32 = 1;
 | 
			
		||||
@@ -287,6 +292,8 @@ pub const SQLITE_UTF16: i32 = 4;
 | 
			
		||||
pub const SQLITE_ANY: i32 = 5;
 | 
			
		||||
pub const SQLITE_UTF16_ALIGNED: i32 = 8;
 | 
			
		||||
pub const SQLITE_DETERMINISTIC: i32 = 2048;
 | 
			
		||||
pub const SQLITE_WIN32_DATA_DIRECTORY_TYPE: i32 = 1;
 | 
			
		||||
pub const SQLITE_WIN32_TEMP_DIRECTORY_TYPE: i32 = 2;
 | 
			
		||||
pub const SQLITE_INDEX_SCAN_UNIQUE: i32 = 1;
 | 
			
		||||
pub const SQLITE_INDEX_CONSTRAINT_EQ: i32 = 2;
 | 
			
		||||
pub const SQLITE_INDEX_CONSTRAINT_GT: i32 = 4;
 | 
			
		||||
@@ -365,7 +372,8 @@ pub const SQLITE_DBSTATUS_CACHE_MISS: i32 = 8;
 | 
			
		||||
pub const SQLITE_DBSTATUS_CACHE_WRITE: i32 = 9;
 | 
			
		||||
pub const SQLITE_DBSTATUS_DEFERRED_FKS: i32 = 10;
 | 
			
		||||
pub const SQLITE_DBSTATUS_CACHE_USED_SHARED: i32 = 11;
 | 
			
		||||
pub const SQLITE_DBSTATUS_MAX: i32 = 11;
 | 
			
		||||
pub const SQLITE_DBSTATUS_CACHE_SPILL: i32 = 12;
 | 
			
		||||
pub const SQLITE_DBSTATUS_MAX: i32 = 12;
 | 
			
		||||
pub const SQLITE_STMTSTATUS_FULLSCAN_STEP: i32 = 1;
 | 
			
		||||
pub const SQLITE_STMTSTATUS_SORT: i32 = 2;
 | 
			
		||||
pub const SQLITE_STMTSTATUS_AUTOINDEX: i32 = 3;
 | 
			
		||||
@@ -387,6 +395,10 @@ pub const SQLITE_SCANSTAT_EST: i32 = 2;
 | 
			
		||||
pub const SQLITE_SCANSTAT_NAME: i32 = 3;
 | 
			
		||||
pub const SQLITE_SCANSTAT_EXPLAIN: i32 = 4;
 | 
			
		||||
pub const SQLITE_SCANSTAT_SELECTID: i32 = 5;
 | 
			
		||||
pub const SQLITE_SERIALIZE_NOCOPY: i32 = 1;
 | 
			
		||||
pub const SQLITE_DESERIALIZE_FREEONCLOSE: i32 = 1;
 | 
			
		||||
pub const SQLITE_DESERIALIZE_RESIZEABLE: i32 = 2;
 | 
			
		||||
pub const SQLITE_DESERIALIZE_READONLY: i32 = 4;
 | 
			
		||||
pub const NOT_WITHIN: i32 = 0;
 | 
			
		||||
pub const PARTLY_WITHIN: i32 = 1;
 | 
			
		||||
pub const FULLY_WITHIN: i32 = 2;
 | 
			
		||||
@@ -2255,6 +2267,24 @@ extern "C" {
 | 
			
		||||
    #[link_name = "\u{1}sqlite3_data_directory"]
 | 
			
		||||
    pub static mut sqlite3_data_directory: *mut ::std::os::raw::c_char;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_win32_set_directory(
 | 
			
		||||
        type_: ::std::os::raw::c_ulong,
 | 
			
		||||
        zValue: *mut ::std::os::raw::c_void,
 | 
			
		||||
    ) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_win32_set_directory8(
 | 
			
		||||
        type_: ::std::os::raw::c_ulong,
 | 
			
		||||
        zValue: *const ::std::os::raw::c_char,
 | 
			
		||||
    ) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_win32_set_directory16(
 | 
			
		||||
        type_: ::std::os::raw::c_ulong,
 | 
			
		||||
        zValue: *const ::std::os::raw::c_void,
 | 
			
		||||
    ) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_get_autocommit(arg1: *mut sqlite3) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
@@ -3412,6 +3442,72 @@ extern "C" {
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_test_control(op: ::std::os::raw::c_int, ...) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_keyword_count() -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_keyword_name(
 | 
			
		||||
        arg1: ::std::os::raw::c_int,
 | 
			
		||||
        arg2: *mut *const ::std::os::raw::c_char,
 | 
			
		||||
        arg3: *mut ::std::os::raw::c_int,
 | 
			
		||||
    ) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_keyword_check(
 | 
			
		||||
        arg1: *const ::std::os::raw::c_char,
 | 
			
		||||
        arg2: ::std::os::raw::c_int,
 | 
			
		||||
    ) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
#[derive(Debug, Copy, Clone)]
 | 
			
		||||
pub struct sqlite3_str {
 | 
			
		||||
    _unused: [u8; 0],
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_new(arg1: *mut sqlite3) -> *mut sqlite3_str;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_finish(arg1: *mut sqlite3_str) -> *mut ::std::os::raw::c_char;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_appendf(arg1: *mut sqlite3_str, zFormat: *const ::std::os::raw::c_char, ...);
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_vappendf(
 | 
			
		||||
        arg1: *mut sqlite3_str,
 | 
			
		||||
        zFormat: *const ::std::os::raw::c_char,
 | 
			
		||||
        arg2: *mut __va_list_tag,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_append(
 | 
			
		||||
        arg1: *mut sqlite3_str,
 | 
			
		||||
        zIn: *const ::std::os::raw::c_char,
 | 
			
		||||
        N: ::std::os::raw::c_int,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_appendall(arg1: *mut sqlite3_str, zIn: *const ::std::os::raw::c_char);
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_appendchar(
 | 
			
		||||
        arg1: *mut sqlite3_str,
 | 
			
		||||
        N: ::std::os::raw::c_int,
 | 
			
		||||
        C: ::std::os::raw::c_char,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_reset(arg1: *mut sqlite3_str);
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_errcode(arg1: *mut sqlite3_str) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_length(arg1: *mut sqlite3_str) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_str_value(arg1: *mut sqlite3_str) -> *mut ::std::os::raw::c_char;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_status(
 | 
			
		||||
        op: ::std::os::raw::c_int,
 | 
			
		||||
@@ -4070,6 +4166,24 @@ extern "C" {
 | 
			
		||||
        zDb: *const ::std::os::raw::c_char,
 | 
			
		||||
    ) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_serialize(
 | 
			
		||||
        db: *mut sqlite3,
 | 
			
		||||
        zSchema: *const ::std::os::raw::c_char,
 | 
			
		||||
        piSize: *mut sqlite3_int64,
 | 
			
		||||
        mFlags: ::std::os::raw::c_uint,
 | 
			
		||||
    ) -> *mut ::std::os::raw::c_uchar;
 | 
			
		||||
}
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_deserialize(
 | 
			
		||||
        db: *mut sqlite3,
 | 
			
		||||
        zSchema: *const ::std::os::raw::c_char,
 | 
			
		||||
        pData: *mut ::std::os::raw::c_uchar,
 | 
			
		||||
        szDb: sqlite3_int64,
 | 
			
		||||
        szBuf: sqlite3_int64,
 | 
			
		||||
        mFlags: ::std::os::raw::c_uint,
 | 
			
		||||
    ) -> ::std::os::raw::c_int;
 | 
			
		||||
}
 | 
			
		||||
pub type sqlite3_rtree_dbl = f64;
 | 
			
		||||
extern "C" {
 | 
			
		||||
    pub fn sqlite3_rtree_geometry_callback(
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12848
									
								
								libsqlite3-sys/sqlite3/sqlite3.c
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12848
									
								
								libsqlite3-sys/sqlite3/sqlite3.c
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										799
									
								
								libsqlite3-sys/sqlite3/sqlite3.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										799
									
								
								libsqlite3-sys/sqlite3/sqlite3.h
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										34
									
								
								libsqlite3-sys/sqlite3/sqlite3ext.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								libsqlite3-sys/sqlite3/sqlite3ext.h
									
									
									
									
										vendored
									
									
								
							@@ -295,6 +295,21 @@ struct sqlite3_api_routines {
 | 
			
		||||
  int (*vtab_nochange)(sqlite3_context*);
 | 
			
		||||
  int (*value_nochange)(sqlite3_value*);
 | 
			
		||||
  const char *(*vtab_collation)(sqlite3_index_info*,int);
 | 
			
		||||
  /* Version 3.24.0 and later */
 | 
			
		||||
  int (*keyword_count)(void);
 | 
			
		||||
  int (*keyword_name)(int,const char**,int*);
 | 
			
		||||
  int (*keyword_check)(const char*,int);
 | 
			
		||||
  sqlite3_str *(*str_new)(sqlite3*);
 | 
			
		||||
  char *(*str_finish)(sqlite3_str*);
 | 
			
		||||
  void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
 | 
			
		||||
  void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
 | 
			
		||||
  void (*str_append)(sqlite3_str*, const char *zIn, int N);
 | 
			
		||||
  void (*str_appendall)(sqlite3_str*, const char *zIn);
 | 
			
		||||
  void (*str_appendchar)(sqlite3_str*, int N, char C);
 | 
			
		||||
  void (*str_reset)(sqlite3_str*);
 | 
			
		||||
  int (*str_errcode)(sqlite3_str*);
 | 
			
		||||
  int (*str_length)(sqlite3_str*);
 | 
			
		||||
  char *(*str_value)(sqlite3_str*);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -563,8 +578,23 @@ typedef int (*sqlite3_loadext_entry)(
 | 
			
		||||
#define sqlite3_value_pointer          sqlite3_api->value_pointer
 | 
			
		||||
/* Version 3.22.0 and later */
 | 
			
		||||
#define sqlite3_vtab_nochange          sqlite3_api->vtab_nochange
 | 
			
		||||
#define sqlite3_value_nochange         sqltie3_api->value_nochange
 | 
			
		||||
#define sqlite3_vtab_collation         sqltie3_api->vtab_collation
 | 
			
		||||
#define sqlite3_value_nochange         sqlite3_api->value_nochange
 | 
			
		||||
#define sqlite3_vtab_collation         sqlite3_api->vtab_collation
 | 
			
		||||
/* Version 3.24.0 and later */
 | 
			
		||||
#define sqlite3_keyword_count          sqlite3_api->keyword_count
 | 
			
		||||
#define sqlite3_keyword_name           sqlite3_api->keyword_name
 | 
			
		||||
#define sqlite3_keyword_check          sqlite3_api->keyword_check
 | 
			
		||||
#define sqlite3_str_new                sqlite3_api->str_new
 | 
			
		||||
#define sqlite3_str_finish             sqlite3_api->str_finish
 | 
			
		||||
#define sqlite3_str_appendf            sqlite3_api->str_appendf
 | 
			
		||||
#define sqlite3_str_vappendf           sqlite3_api->str_vappendf
 | 
			
		||||
#define sqlite3_str_append             sqlite3_api->str_append
 | 
			
		||||
#define sqlite3_str_appendall          sqlite3_api->str_appendall
 | 
			
		||||
#define sqlite3_str_appendchar         sqlite3_api->str_appendchar
 | 
			
		||||
#define sqlite3_str_reset              sqlite3_api->str_reset
 | 
			
		||||
#define sqlite3_str_errcode            sqlite3_api->str_errcode
 | 
			
		||||
#define sqlite3_str_length             sqlite3_api->str_length
 | 
			
		||||
#define sqlite3_str_value              sqlite3_api->str_value
 | 
			
		||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
 | 
			
		||||
 | 
			
		||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
use std::os::raw::c_int;
 | 
			
		||||
use std::error;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::os::raw::c_int;
 | 
			
		||||
 | 
			
		||||
/// Error Codes
 | 
			
		||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
			
		||||
@@ -64,30 +64,30 @@ pub struct Error {
 | 
			
		||||
impl Error {
 | 
			
		||||
    pub fn new(result_code: c_int) -> Error {
 | 
			
		||||
        let code = match result_code & 0xff {
 | 
			
		||||
            super::SQLITE_INTERNAL  => ErrorCode::InternalMalfunction,
 | 
			
		||||
            super::SQLITE_PERM      => ErrorCode::PermissionDenied,
 | 
			
		||||
            super::SQLITE_ABORT     => ErrorCode::OperationAborted,
 | 
			
		||||
            super::SQLITE_BUSY      => ErrorCode::DatabaseBusy,
 | 
			
		||||
            super::SQLITE_LOCKED    => ErrorCode::DatabaseLocked,
 | 
			
		||||
            super::SQLITE_NOMEM     => ErrorCode::OutOfMemory,
 | 
			
		||||
            super::SQLITE_READONLY  => ErrorCode::ReadOnly,
 | 
			
		||||
            super::SQLITE_INTERNAL => ErrorCode::InternalMalfunction,
 | 
			
		||||
            super::SQLITE_PERM => ErrorCode::PermissionDenied,
 | 
			
		||||
            super::SQLITE_ABORT => ErrorCode::OperationAborted,
 | 
			
		||||
            super::SQLITE_BUSY => ErrorCode::DatabaseBusy,
 | 
			
		||||
            super::SQLITE_LOCKED => ErrorCode::DatabaseLocked,
 | 
			
		||||
            super::SQLITE_NOMEM => ErrorCode::OutOfMemory,
 | 
			
		||||
            super::SQLITE_READONLY => ErrorCode::ReadOnly,
 | 
			
		||||
            super::SQLITE_INTERRUPT => ErrorCode::OperationInterrupted,
 | 
			
		||||
            super::SQLITE_IOERR     => ErrorCode::SystemIOFailure,
 | 
			
		||||
            super::SQLITE_CORRUPT   => ErrorCode::DatabaseCorrupt,
 | 
			
		||||
            super::SQLITE_NOTFOUND  => ErrorCode::NotFound,
 | 
			
		||||
            super::SQLITE_FULL      => ErrorCode::DiskFull,
 | 
			
		||||
            super::SQLITE_CANTOPEN  => ErrorCode::CannotOpen,
 | 
			
		||||
            super::SQLITE_PROTOCOL  => ErrorCode::FileLockingProtocolFailed,
 | 
			
		||||
            super::SQLITE_SCHEMA    => ErrorCode::SchemaChanged,
 | 
			
		||||
            super::SQLITE_TOOBIG    => ErrorCode::TooBig,
 | 
			
		||||
            super::SQLITE_CONSTRAINT=> ErrorCode::ConstraintViolation,
 | 
			
		||||
            super::SQLITE_MISMATCH  => ErrorCode::TypeMismatch,
 | 
			
		||||
            super::SQLITE_MISUSE    => ErrorCode::APIMisuse,
 | 
			
		||||
            super::SQLITE_NOLFS     => ErrorCode::NoLargeFileSupport,
 | 
			
		||||
            super::SQLITE_AUTH      => ErrorCode::AuthorizationForStatementDenied,
 | 
			
		||||
            super::SQLITE_RANGE     => ErrorCode::ParameterOutOfRange,
 | 
			
		||||
            super::SQLITE_NOTADB    => ErrorCode::NotADatabase,
 | 
			
		||||
            _                => ErrorCode::Unknown,
 | 
			
		||||
            super::SQLITE_IOERR => ErrorCode::SystemIOFailure,
 | 
			
		||||
            super::SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt,
 | 
			
		||||
            super::SQLITE_NOTFOUND => ErrorCode::NotFound,
 | 
			
		||||
            super::SQLITE_FULL => ErrorCode::DiskFull,
 | 
			
		||||
            super::SQLITE_CANTOPEN => ErrorCode::CannotOpen,
 | 
			
		||||
            super::SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed,
 | 
			
		||||
            super::SQLITE_SCHEMA => ErrorCode::SchemaChanged,
 | 
			
		||||
            super::SQLITE_TOOBIG => ErrorCode::TooBig,
 | 
			
		||||
            super::SQLITE_CONSTRAINT => ErrorCode::ConstraintViolation,
 | 
			
		||||
            super::SQLITE_MISMATCH => ErrorCode::TypeMismatch,
 | 
			
		||||
            super::SQLITE_MISUSE => ErrorCode::APIMisuse,
 | 
			
		||||
            super::SQLITE_NOLFS => ErrorCode::NoLargeFileSupport,
 | 
			
		||||
            super::SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied,
 | 
			
		||||
            super::SQLITE_RANGE => ErrorCode::ParameterOutOfRange,
 | 
			
		||||
            super::SQLITE_NOTADB => ErrorCode::NotADatabase,
 | 
			
		||||
            _ => ErrorCode::Unknown,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Error {
 | 
			
		||||
@@ -99,7 +99,12 @@ impl Error {
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Error {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        write!(f, "Error code {}: {}", self.extended_code, code_to_str(self.extended_code))
 | 
			
		||||
        write!(
 | 
			
		||||
            f,
 | 
			
		||||
            "Error code {}: {}",
 | 
			
		||||
            self.extended_code,
 | 
			
		||||
            code_to_str(self.extended_code)
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -114,48 +119,48 @@ impl error::Error for Error {
 | 
			
		||||
// in the current version of SQLite. We repeat them here so we don't have to worry about which
 | 
			
		||||
// version of SQLite added which constants, and we only use them to implement code_to_str below.
 | 
			
		||||
 | 
			
		||||
const SQLITE_NOTICE    : c_int =  27;
 | 
			
		||||
const SQLITE_WARNING   : c_int =  28;
 | 
			
		||||
const SQLITE_NOTICE: c_int = 27;
 | 
			
		||||
const SQLITE_WARNING: c_int = 28;
 | 
			
		||||
 | 
			
		||||
// Extended result codes.
 | 
			
		||||
 | 
			
		||||
const SQLITE_IOERR_SHMOPEN           : c_int = (super::SQLITE_IOERR | (18<<8));
 | 
			
		||||
const SQLITE_IOERR_SHMSIZE           : c_int = (super::SQLITE_IOERR | (19<<8));
 | 
			
		||||
const SQLITE_IOERR_SHMLOCK           : c_int = (super::SQLITE_IOERR | (20<<8));
 | 
			
		||||
const SQLITE_IOERR_SHMMAP            : c_int = (super::SQLITE_IOERR | (21<<8));
 | 
			
		||||
const SQLITE_IOERR_SEEK              : c_int = (super::SQLITE_IOERR | (22<<8));
 | 
			
		||||
const SQLITE_IOERR_DELETE_NOENT      : c_int = (super::SQLITE_IOERR | (23<<8));
 | 
			
		||||
const SQLITE_IOERR_MMAP              : c_int = (super::SQLITE_IOERR | (24<<8));
 | 
			
		||||
const SQLITE_IOERR_GETTEMPPATH       : c_int = (super::SQLITE_IOERR | (25<<8));
 | 
			
		||||
const SQLITE_IOERR_CONVPATH          : c_int = (super::SQLITE_IOERR | (26<<8));
 | 
			
		||||
const SQLITE_IOERR_VNODE             : c_int = (super::SQLITE_IOERR | (27<<8));
 | 
			
		||||
const SQLITE_LOCKED_SHAREDCACHE      : c_int = (super::SQLITE_LOCKED |  (1<<8));
 | 
			
		||||
const SQLITE_BUSY_RECOVERY           : c_int = (super::SQLITE_BUSY   |  (1<<8));
 | 
			
		||||
const SQLITE_BUSY_SNAPSHOT           : c_int = (super::SQLITE_BUSY   |  (2<<8));
 | 
			
		||||
const SQLITE_CANTOPEN_NOTEMPDIR      : c_int = (super::SQLITE_CANTOPEN | (1<<8));
 | 
			
		||||
const SQLITE_CANTOPEN_ISDIR          : c_int = (super::SQLITE_CANTOPEN | (2<<8));
 | 
			
		||||
const SQLITE_CANTOPEN_FULLPATH       : c_int = (super::SQLITE_CANTOPEN | (3<<8));
 | 
			
		||||
const SQLITE_CANTOPEN_CONVPATH       : c_int = (super::SQLITE_CANTOPEN | (4<<8));
 | 
			
		||||
const SQLITE_CORRUPT_VTAB            : c_int = (super::SQLITE_CORRUPT | (1<<8));
 | 
			
		||||
const SQLITE_READONLY_RECOVERY       : c_int = (super::SQLITE_READONLY | (1<<8));
 | 
			
		||||
const SQLITE_READONLY_CANTLOCK       : c_int = (super::SQLITE_READONLY | (2<<8));
 | 
			
		||||
const SQLITE_READONLY_ROLLBACK       : c_int = (super::SQLITE_READONLY | (3<<8));
 | 
			
		||||
const SQLITE_READONLY_DBMOVED        : c_int = (super::SQLITE_READONLY | (4<<8));
 | 
			
		||||
const SQLITE_ABORT_ROLLBACK          : c_int = (super::SQLITE_ABORT | (2<<8));
 | 
			
		||||
const SQLITE_CONSTRAINT_CHECK        : c_int = (super::SQLITE_CONSTRAINT | (1<<8));
 | 
			
		||||
const SQLITE_CONSTRAINT_COMMITHOOK   : c_int = (super::SQLITE_CONSTRAINT | (2<<8));
 | 
			
		||||
const SQLITE_CONSTRAINT_FOREIGNKEY   : c_int = (super::SQLITE_CONSTRAINT | (3<<8));
 | 
			
		||||
const SQLITE_CONSTRAINT_FUNCTION     : c_int = (super::SQLITE_CONSTRAINT | (4<<8));
 | 
			
		||||
const SQLITE_CONSTRAINT_NOTNULL      : c_int = (super::SQLITE_CONSTRAINT | (5<<8));
 | 
			
		||||
const SQLITE_CONSTRAINT_PRIMARYKEY   : c_int = (super::SQLITE_CONSTRAINT | (6<<8));
 | 
			
		||||
const SQLITE_CONSTRAINT_TRIGGER      : c_int = (super::SQLITE_CONSTRAINT | (7<<8));
 | 
			
		||||
const SQLITE_CONSTRAINT_UNIQUE       : c_int = (super::SQLITE_CONSTRAINT | (8<<8));
 | 
			
		||||
const SQLITE_CONSTRAINT_VTAB         : c_int = (super::SQLITE_CONSTRAINT | (9<<8));
 | 
			
		||||
const SQLITE_CONSTRAINT_ROWID        : c_int = (super::SQLITE_CONSTRAINT |(10<<8));
 | 
			
		||||
const SQLITE_NOTICE_RECOVER_WAL      : c_int = (SQLITE_NOTICE | (1<<8));
 | 
			
		||||
const SQLITE_NOTICE_RECOVER_ROLLBACK : c_int = (SQLITE_NOTICE | (2<<8));
 | 
			
		||||
const SQLITE_WARNING_AUTOINDEX       : c_int = (SQLITE_WARNING | (1<<8));
 | 
			
		||||
const SQLITE_AUTH_USER               : c_int = (super::SQLITE_AUTH | (1<<8));
 | 
			
		||||
const SQLITE_IOERR_SHMOPEN: c_int = (super::SQLITE_IOERR | (18 << 8));
 | 
			
		||||
const SQLITE_IOERR_SHMSIZE: c_int = (super::SQLITE_IOERR | (19 << 8));
 | 
			
		||||
const SQLITE_IOERR_SHMLOCK: c_int = (super::SQLITE_IOERR | (20 << 8));
 | 
			
		||||
const SQLITE_IOERR_SHMMAP: c_int = (super::SQLITE_IOERR | (21 << 8));
 | 
			
		||||
const SQLITE_IOERR_SEEK: c_int = (super::SQLITE_IOERR | (22 << 8));
 | 
			
		||||
const SQLITE_IOERR_DELETE_NOENT: c_int = (super::SQLITE_IOERR | (23 << 8));
 | 
			
		||||
const SQLITE_IOERR_MMAP: c_int = (super::SQLITE_IOERR | (24 << 8));
 | 
			
		||||
const SQLITE_IOERR_GETTEMPPATH: c_int = (super::SQLITE_IOERR | (25 << 8));
 | 
			
		||||
const SQLITE_IOERR_CONVPATH: c_int = (super::SQLITE_IOERR | (26 << 8));
 | 
			
		||||
const SQLITE_IOERR_VNODE: c_int = (super::SQLITE_IOERR | (27 << 8));
 | 
			
		||||
const SQLITE_LOCKED_SHAREDCACHE: c_int = (super::SQLITE_LOCKED | (1 << 8));
 | 
			
		||||
const SQLITE_BUSY_RECOVERY: c_int = (super::SQLITE_BUSY | (1 << 8));
 | 
			
		||||
const SQLITE_BUSY_SNAPSHOT: c_int = (super::SQLITE_BUSY | (2 << 8));
 | 
			
		||||
const SQLITE_CANTOPEN_NOTEMPDIR: c_int = (super::SQLITE_CANTOPEN | (1 << 8));
 | 
			
		||||
const SQLITE_CANTOPEN_ISDIR: c_int = (super::SQLITE_CANTOPEN | (2 << 8));
 | 
			
		||||
const SQLITE_CANTOPEN_FULLPATH: c_int = (super::SQLITE_CANTOPEN | (3 << 8));
 | 
			
		||||
const SQLITE_CANTOPEN_CONVPATH: c_int = (super::SQLITE_CANTOPEN | (4 << 8));
 | 
			
		||||
const SQLITE_CORRUPT_VTAB: c_int = (super::SQLITE_CORRUPT | (1 << 8));
 | 
			
		||||
const SQLITE_READONLY_RECOVERY: c_int = (super::SQLITE_READONLY | (1 << 8));
 | 
			
		||||
const SQLITE_READONLY_CANTLOCK: c_int = (super::SQLITE_READONLY | (2 << 8));
 | 
			
		||||
const SQLITE_READONLY_ROLLBACK: c_int = (super::SQLITE_READONLY | (3 << 8));
 | 
			
		||||
const SQLITE_READONLY_DBMOVED: c_int = (super::SQLITE_READONLY | (4 << 8));
 | 
			
		||||
const SQLITE_ABORT_ROLLBACK: c_int = (super::SQLITE_ABORT | (2 << 8));
 | 
			
		||||
const SQLITE_CONSTRAINT_CHECK: c_int = (super::SQLITE_CONSTRAINT | (1 << 8));
 | 
			
		||||
const SQLITE_CONSTRAINT_COMMITHOOK: c_int = (super::SQLITE_CONSTRAINT | (2 << 8));
 | 
			
		||||
const SQLITE_CONSTRAINT_FOREIGNKEY: c_int = (super::SQLITE_CONSTRAINT | (3 << 8));
 | 
			
		||||
const SQLITE_CONSTRAINT_FUNCTION: c_int = (super::SQLITE_CONSTRAINT | (4 << 8));
 | 
			
		||||
const SQLITE_CONSTRAINT_NOTNULL: c_int = (super::SQLITE_CONSTRAINT | (5 << 8));
 | 
			
		||||
const SQLITE_CONSTRAINT_PRIMARYKEY: c_int = (super::SQLITE_CONSTRAINT | (6 << 8));
 | 
			
		||||
const SQLITE_CONSTRAINT_TRIGGER: c_int = (super::SQLITE_CONSTRAINT | (7 << 8));
 | 
			
		||||
const SQLITE_CONSTRAINT_UNIQUE: c_int = (super::SQLITE_CONSTRAINT | (8 << 8));
 | 
			
		||||
const SQLITE_CONSTRAINT_VTAB: c_int = (super::SQLITE_CONSTRAINT | (9 << 8));
 | 
			
		||||
const SQLITE_CONSTRAINT_ROWID: c_int = (super::SQLITE_CONSTRAINT | (10 << 8));
 | 
			
		||||
const SQLITE_NOTICE_RECOVER_WAL: c_int = (SQLITE_NOTICE | (1 << 8));
 | 
			
		||||
const SQLITE_NOTICE_RECOVER_ROLLBACK: c_int = (SQLITE_NOTICE | (2 << 8));
 | 
			
		||||
const SQLITE_WARNING_AUTOINDEX: c_int = (SQLITE_WARNING | (1 << 8));
 | 
			
		||||
const SQLITE_AUTH_USER: c_int = (super::SQLITE_AUTH | (1 << 8));
 | 
			
		||||
 | 
			
		||||
pub fn code_to_str(code: c_int) -> &'static str {
 | 
			
		||||
    match code {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
pub use self::error::*;
 | 
			
		||||
 | 
			
		||||
use std::default::Default;
 | 
			
		||||
use std::mem;
 | 
			
		||||
 | 
			
		||||
mod error;
 | 
			
		||||
@@ -45,3 +46,18 @@ pub enum Limit {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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() }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ cd $SCRIPT_DIR
 | 
			
		||||
SQLITE3_LIB_DIR=$SCRIPT_DIR/sqlite3
 | 
			
		||||
 | 
			
		||||
# Download and extract amalgamation
 | 
			
		||||
SQLITE=sqlite-amalgamation-3220000
 | 
			
		||||
SQLITE=sqlite-amalgamation-3240000
 | 
			
		||||
curl -O http://sqlite.org/2018/$SQLITE.zip
 | 
			
		||||
unzip -p $SQLITE.zip $SQLITE/sqlite3.c > $SQLITE3_LIB_DIR/sqlite3.c
 | 
			
		||||
unzip -p $SQLITE.zip $SQLITE/sqlite3.h > $SQLITE3_LIB_DIR/sqlite3.h
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										168
									
								
								src/backup.rs
									
									
									
									
									
								
							
							
						
						
									
										168
									
								
								src/backup.rs
									
									
									
									
									
								
							@@ -36,8 +36,8 @@ use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
use ffi;
 | 
			
		||||
 | 
			
		||||
use {DatabaseName, Connection, Result};
 | 
			
		||||
use error::{error_from_sqlite_code, error_from_handle};
 | 
			
		||||
use error::{error_from_handle, error_from_sqlite_code};
 | 
			
		||||
use {Connection, DatabaseName, Result};
 | 
			
		||||
 | 
			
		||||
impl Connection {
 | 
			
		||||
    /// Back up the `name` database to the given destination path.
 | 
			
		||||
@@ -52,14 +52,20 @@ impl Connection {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if the destination path cannot be opened
 | 
			
		||||
    /// or if the backup fails.
 | 
			
		||||
    pub fn backup<P: AsRef<Path>>(&self,
 | 
			
		||||
                                  name: DatabaseName,
 | 
			
		||||
                                  dst_path: P,
 | 
			
		||||
                                  progress: Option<fn(Progress)>)
 | 
			
		||||
                                  -> Result<()> {
 | 
			
		||||
        use self::StepResult::{More, Done, Busy, Locked};
 | 
			
		||||
    pub fn backup<P: AsRef<Path>>(
 | 
			
		||||
        &self,
 | 
			
		||||
        name: DatabaseName,
 | 
			
		||||
        dst_path: P,
 | 
			
		||||
        progress: Option<fn(Progress)>,
 | 
			
		||||
    ) -> Result<()> {
 | 
			
		||||
        use self::StepResult::{Busy, Done, Locked, More};
 | 
			
		||||
        let mut dst = try!(Connection::open(dst_path));
 | 
			
		||||
        let backup = try!(Backup::new_with_names(self, name, &mut dst, DatabaseName::Main));
 | 
			
		||||
        let backup = try!(Backup::new_with_names(
 | 
			
		||||
            self,
 | 
			
		||||
            name,
 | 
			
		||||
            &mut dst,
 | 
			
		||||
            DatabaseName::Main
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        let mut r = More;
 | 
			
		||||
        while r == More {
 | 
			
		||||
@@ -89,12 +95,13 @@ impl Connection {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if the destination path cannot be opened
 | 
			
		||||
    /// or if the restore fails.
 | 
			
		||||
    pub fn restore<P: AsRef<Path>>(&mut self,
 | 
			
		||||
                                   name: DatabaseName,
 | 
			
		||||
                                   src_path: P,
 | 
			
		||||
                                   progress: Option<fn(Progress)>)
 | 
			
		||||
                                   -> Result<()> {
 | 
			
		||||
        use self::StepResult::{More, Done, Busy, Locked};
 | 
			
		||||
    pub fn restore<P: AsRef<Path>, F: Fn(Progress)>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        name: DatabaseName,
 | 
			
		||||
        src_path: P,
 | 
			
		||||
        progress: Option<F>,
 | 
			
		||||
    ) -> Result<()> {
 | 
			
		||||
        use self::StepResult::{Busy, Done, Locked, More};
 | 
			
		||||
        let src = try!(Connection::open(src_path));
 | 
			
		||||
        let restore = try!(Backup::new_with_names(&src, DatabaseName::Main, self, name));
 | 
			
		||||
 | 
			
		||||
@@ -102,7 +109,7 @@ impl Connection {
 | 
			
		||||
        let mut busy_count = 0i32;
 | 
			
		||||
        'restore_loop: while r == More || r == Busy {
 | 
			
		||||
            r = try!(restore.step(100));
 | 
			
		||||
            if let Some(f) = progress {
 | 
			
		||||
            if let Some(ref f) = progress {
 | 
			
		||||
                f(restore.progress());
 | 
			
		||||
            }
 | 
			
		||||
            if r == Busy {
 | 
			
		||||
@@ -124,7 +131,7 @@ impl Connection {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Possible successful results of calling `Backup::step`.
 | 
			
		||||
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
 | 
			
		||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 | 
			
		||||
pub enum StepResult {
 | 
			
		||||
    /// The backup is complete.
 | 
			
		||||
    Done,
 | 
			
		||||
@@ -146,7 +153,7 @@ pub enum StepResult {
 | 
			
		||||
/// backup is as of the last call to `step` - if the source database is
 | 
			
		||||
/// modified after a call to `step`, the progress value will become outdated
 | 
			
		||||
/// and potentially incorrect.
 | 
			
		||||
#[derive(Copy,Clone,Debug)]
 | 
			
		||||
#[derive(Copy, Clone, Debug)]
 | 
			
		||||
pub struct Progress {
 | 
			
		||||
    /// Number of pages in the source database that still need to be backed up.
 | 
			
		||||
    pub remaining: c_int,
 | 
			
		||||
@@ -184,21 +191,24 @@ impl<'a, 'b> Backup<'a, 'b> {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if the underlying `sqlite3_backup_init` call returns
 | 
			
		||||
    /// `NULL`.
 | 
			
		||||
    pub fn new_with_names(from: &'a Connection,
 | 
			
		||||
                          from_name: DatabaseName,
 | 
			
		||||
                          to: &'b mut Connection,
 | 
			
		||||
                          to_name: DatabaseName)
 | 
			
		||||
                          -> Result<Backup<'a, 'b>> {
 | 
			
		||||
    pub fn new_with_names(
 | 
			
		||||
        from: &'a Connection,
 | 
			
		||||
        from_name: DatabaseName,
 | 
			
		||||
        to: &'b mut Connection,
 | 
			
		||||
        to_name: DatabaseName,
 | 
			
		||||
    ) -> Result<Backup<'a, 'b>> {
 | 
			
		||||
        let to_name = try!(to_name.to_cstring());
 | 
			
		||||
        let from_name = try!(from_name.to_cstring());
 | 
			
		||||
 | 
			
		||||
        let to_db = to.db.borrow_mut().db;
 | 
			
		||||
 | 
			
		||||
        let b = unsafe {
 | 
			
		||||
            let b = ffi::sqlite3_backup_init(to_db,
 | 
			
		||||
                                             to_name.as_ptr(),
 | 
			
		||||
                                             from.db.borrow_mut().db,
 | 
			
		||||
                                             from_name.as_ptr());
 | 
			
		||||
            let b = ffi::sqlite3_backup_init(
 | 
			
		||||
                to_db,
 | 
			
		||||
                to_name.as_ptr(),
 | 
			
		||||
                from.db.borrow_mut().db,
 | 
			
		||||
                from_name.as_ptr(),
 | 
			
		||||
            );
 | 
			
		||||
            if b.is_null() {
 | 
			
		||||
                return Err(error_from_handle(to_db, ffi::sqlite3_errcode(to_db)));
 | 
			
		||||
            }
 | 
			
		||||
@@ -206,10 +216,10 @@ impl<'a, 'b> Backup<'a, 'b> {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        Ok(Backup {
 | 
			
		||||
               phantom_from: PhantomData,
 | 
			
		||||
               phantom_to: PhantomData,
 | 
			
		||||
               b,
 | 
			
		||||
           })
 | 
			
		||||
            phantom_from: PhantomData,
 | 
			
		||||
            phantom_to: PhantomData,
 | 
			
		||||
            b,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Gets the progress of the backup as of the last call to `step`.
 | 
			
		||||
@@ -235,7 +245,7 @@ impl<'a, 'b> Backup<'a, 'b> {
 | 
			
		||||
    /// `LOCKED` are transient errors and are therefore returned as possible
 | 
			
		||||
    /// `Ok` values.
 | 
			
		||||
    pub fn step(&self, num_pages: c_int) -> Result<StepResult> {
 | 
			
		||||
        use self::StepResult::{Done, More, Busy, Locked};
 | 
			
		||||
        use self::StepResult::{Busy, Done, Locked, More};
 | 
			
		||||
 | 
			
		||||
        let rc = unsafe { ffi::sqlite3_backup_step(self.b, num_pages) };
 | 
			
		||||
        match rc {
 | 
			
		||||
@@ -262,12 +272,13 @@ impl<'a, 'b> Backup<'a, 'b> {
 | 
			
		||||
    /// # Failure
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if any of the calls to `step` return `Err`.
 | 
			
		||||
    pub fn run_to_completion(&self,
 | 
			
		||||
                             pages_per_step: c_int,
 | 
			
		||||
                             pause_between_pages: Duration,
 | 
			
		||||
                             progress: Option<fn(Progress)>)
 | 
			
		||||
                             -> Result<()> {
 | 
			
		||||
        use self::StepResult::{Done, More, Busy, Locked};
 | 
			
		||||
    pub fn run_to_completion(
 | 
			
		||||
        &self,
 | 
			
		||||
        pages_per_step: c_int,
 | 
			
		||||
        pause_between_pages: Duration,
 | 
			
		||||
        progress: Option<fn(Progress)>,
 | 
			
		||||
    ) -> Result<()> {
 | 
			
		||||
        use self::StepResult::{Busy, Done, Locked, More};
 | 
			
		||||
 | 
			
		||||
        assert!(pages_per_step > 0, "pages_per_step must be positive");
 | 
			
		||||
 | 
			
		||||
@@ -292,12 +303,11 @@ impl<'a, 'b> Drop for Backup<'a, 'b> {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use {Connection, DatabaseName};
 | 
			
		||||
    use std::time::Duration;
 | 
			
		||||
    use super::Backup;
 | 
			
		||||
    use std::time::Duration;
 | 
			
		||||
    use {Connection, DatabaseName};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
    fn test_backup() {
 | 
			
		||||
        let src = Connection::open_in_memory().unwrap();
 | 
			
		||||
        let sql = "BEGIN;
 | 
			
		||||
@@ -313,22 +323,27 @@ mod test {
 | 
			
		||||
            backup.step(-1).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let the_answer: i64 = dst.query_row("SELECT x FROM foo", &[], |r| r.get(0)).unwrap();
 | 
			
		||||
        let the_answer: i64 = dst
 | 
			
		||||
            .query_row("SELECT x FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(42, the_answer);
 | 
			
		||||
 | 
			
		||||
        src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let backup = Backup::new(&src, &mut dst).unwrap();
 | 
			
		||||
            backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
 | 
			
		||||
            backup
 | 
			
		||||
                .run_to_completion(5, Duration::from_millis(250), None)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
 | 
			
		||||
        let the_answer: i64 = dst
 | 
			
		||||
            .query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(42 + 43, the_answer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
    fn test_backup_temp() {
 | 
			
		||||
        let src = Connection::open_in_memory().unwrap();
 | 
			
		||||
        let sql = "BEGIN;
 | 
			
		||||
@@ -340,34 +355,35 @@ mod test {
 | 
			
		||||
        let mut dst = Connection::open_in_memory().unwrap();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let backup = Backup::new_with_names(&src,
 | 
			
		||||
                                                DatabaseName::Temp,
 | 
			
		||||
                                                &mut dst,
 | 
			
		||||
                                                DatabaseName::Main)
 | 
			
		||||
                             .unwrap();
 | 
			
		||||
            let backup =
 | 
			
		||||
                Backup::new_with_names(&src, DatabaseName::Temp, &mut dst, DatabaseName::Main)
 | 
			
		||||
                    .unwrap();
 | 
			
		||||
            backup.step(-1).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let the_answer: i64 = dst.query_row("SELECT x FROM foo", &[], |r| r.get(0)).unwrap();
 | 
			
		||||
        let the_answer: i64 = dst
 | 
			
		||||
            .query_row("SELECT x FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(42, the_answer);
 | 
			
		||||
 | 
			
		||||
        src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let backup = Backup::new_with_names(&src,
 | 
			
		||||
                                                DatabaseName::Temp,
 | 
			
		||||
                                                &mut dst,
 | 
			
		||||
                                                DatabaseName::Main)
 | 
			
		||||
                             .unwrap();
 | 
			
		||||
            backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
 | 
			
		||||
            let backup =
 | 
			
		||||
                Backup::new_with_names(&src, DatabaseName::Temp, &mut dst, DatabaseName::Main)
 | 
			
		||||
                    .unwrap();
 | 
			
		||||
            backup
 | 
			
		||||
                .run_to_completion(5, Duration::from_millis(250), None)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
 | 
			
		||||
        let the_answer: i64 = dst
 | 
			
		||||
            .query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(42 + 43, the_answer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
    fn test_backup_attached() {
 | 
			
		||||
        let src = Connection::open_in_memory().unwrap();
 | 
			
		||||
        let sql = "ATTACH DATABASE ':memory:' AS my_attached;
 | 
			
		||||
@@ -380,29 +396,37 @@ mod test {
 | 
			
		||||
        let mut dst = Connection::open_in_memory().unwrap();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let backup = Backup::new_with_names(&src,
 | 
			
		||||
                                                DatabaseName::Attached("my_attached"),
 | 
			
		||||
                                                &mut dst,
 | 
			
		||||
                                                DatabaseName::Main)
 | 
			
		||||
                             .unwrap();
 | 
			
		||||
            let backup = Backup::new_with_names(
 | 
			
		||||
                &src,
 | 
			
		||||
                DatabaseName::Attached("my_attached"),
 | 
			
		||||
                &mut dst,
 | 
			
		||||
                DatabaseName::Main,
 | 
			
		||||
            ).unwrap();
 | 
			
		||||
            backup.step(-1).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let the_answer: i64 = dst.query_row("SELECT x FROM foo", &[], |r| r.get(0)).unwrap();
 | 
			
		||||
        let the_answer: i64 = dst
 | 
			
		||||
            .query_row("SELECT x FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(42, the_answer);
 | 
			
		||||
 | 
			
		||||
        src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let backup = Backup::new_with_names(&src,
 | 
			
		||||
                                                DatabaseName::Attached("my_attached"),
 | 
			
		||||
                                                &mut dst,
 | 
			
		||||
                                                DatabaseName::Main)
 | 
			
		||||
                             .unwrap();
 | 
			
		||||
            backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
 | 
			
		||||
            let backup = Backup::new_with_names(
 | 
			
		||||
                &src,
 | 
			
		||||
                DatabaseName::Attached("my_attached"),
 | 
			
		||||
                &mut dst,
 | 
			
		||||
                DatabaseName::Main,
 | 
			
		||||
            ).unwrap();
 | 
			
		||||
            backup
 | 
			
		||||
                .run_to_completion(5, Duration::from_millis(250), None)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
 | 
			
		||||
        let the_answer: i64 = dst
 | 
			
		||||
            .query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(42 + 43, the_answer);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										107
									
								
								src/blob.rs
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								src/blob.rs
									
									
									
									
									
								
							@@ -48,13 +48,13 @@
 | 
			
		||||
//!     assert_eq!(blob.size(), 64);
 | 
			
		||||
//! }
 | 
			
		||||
//! ```
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::cmp::min;
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::ptr;
 | 
			
		||||
 | 
			
		||||
use super::ffi;
 | 
			
		||||
use super::types::{ToSql, ToSqlOutput};
 | 
			
		||||
use {Result, Connection, DatabaseName};
 | 
			
		||||
use {Connection, DatabaseName, Result};
 | 
			
		||||
 | 
			
		||||
/// Handle to an open BLOB.
 | 
			
		||||
pub struct Blob<'conn> {
 | 
			
		||||
@@ -70,35 +70,35 @@ impl Connection {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if `db`/`table`/`column` cannot be converted to a C-compatible string
 | 
			
		||||
    /// or if the underlying SQLite BLOB open call fails.
 | 
			
		||||
    pub fn blob_open<'a>(&'a self,
 | 
			
		||||
                         db: DatabaseName,
 | 
			
		||||
                         table: &str,
 | 
			
		||||
                         column: &str,
 | 
			
		||||
                         row_id: i64,
 | 
			
		||||
                         read_only: bool)
 | 
			
		||||
                         -> Result<Blob<'a>> {
 | 
			
		||||
    pub fn blob_open<'a>(
 | 
			
		||||
        &'a self,
 | 
			
		||||
        db: DatabaseName,
 | 
			
		||||
        table: &str,
 | 
			
		||||
        column: &str,
 | 
			
		||||
        row_id: i64,
 | 
			
		||||
        read_only: bool,
 | 
			
		||||
    ) -> Result<Blob<'a>> {
 | 
			
		||||
        let mut c = self.db.borrow_mut();
 | 
			
		||||
        let mut blob = ptr::null_mut();
 | 
			
		||||
        let db = try!(db.to_cstring());
 | 
			
		||||
        let table = try!(super::str_to_cstring(table));
 | 
			
		||||
        let column = try!(super::str_to_cstring(column));
 | 
			
		||||
        let rc = unsafe {
 | 
			
		||||
            ffi::sqlite3_blob_open(c.db(),
 | 
			
		||||
                                   db.as_ptr(),
 | 
			
		||||
                                   table.as_ptr(),
 | 
			
		||||
                                   column.as_ptr(),
 | 
			
		||||
                                   row_id,
 | 
			
		||||
                                   if read_only { 0 } else { 1 },
 | 
			
		||||
                                   &mut blob)
 | 
			
		||||
            ffi::sqlite3_blob_open(
 | 
			
		||||
                c.db(),
 | 
			
		||||
                db.as_ptr(),
 | 
			
		||||
                table.as_ptr(),
 | 
			
		||||
                column.as_ptr(),
 | 
			
		||||
                row_id,
 | 
			
		||||
                if read_only { 0 } else { 1 },
 | 
			
		||||
                &mut blob,
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
        c.decode_result(rc)
 | 
			
		||||
            .map(|_| {
 | 
			
		||||
                     Blob {
 | 
			
		||||
                         conn: self,
 | 
			
		||||
                         blob,
 | 
			
		||||
                         pos: 0,
 | 
			
		||||
                     }
 | 
			
		||||
                 })
 | 
			
		||||
        c.decode_result(rc).map(|_| Blob {
 | 
			
		||||
            conn: self,
 | 
			
		||||
            blob,
 | 
			
		||||
            pos: 0,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -154,15 +154,13 @@ impl<'conn> io::Read for Blob<'conn> {
 | 
			
		||||
        if n <= 0 {
 | 
			
		||||
            return Ok(0);
 | 
			
		||||
        }
 | 
			
		||||
        let rc =
 | 
			
		||||
            unsafe { ffi::sqlite3_blob_read(self.blob, buf.as_ptr() as *mut _, n, self.pos) };
 | 
			
		||||
        let rc = unsafe { ffi::sqlite3_blob_read(self.blob, buf.as_ptr() as *mut _, n, self.pos) };
 | 
			
		||||
        self.conn
 | 
			
		||||
            .decode_result(rc)
 | 
			
		||||
            .map(|_| {
 | 
			
		||||
                     self.pos += n;
 | 
			
		||||
                     n as usize
 | 
			
		||||
                 })
 | 
			
		||||
            .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
 | 
			
		||||
                self.pos += n;
 | 
			
		||||
                n as usize
 | 
			
		||||
            }).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -183,16 +181,13 @@ impl<'conn> io::Write for Blob<'conn> {
 | 
			
		||||
        if n <= 0 {
 | 
			
		||||
            return Ok(0);
 | 
			
		||||
        }
 | 
			
		||||
        let rc = unsafe {
 | 
			
		||||
            ffi::sqlite3_blob_write(self.blob, buf.as_ptr() as *mut _, n, self.pos)
 | 
			
		||||
        };
 | 
			
		||||
        let rc = unsafe { ffi::sqlite3_blob_write(self.blob, buf.as_ptr() as *mut _, n, self.pos) };
 | 
			
		||||
        self.conn
 | 
			
		||||
            .decode_result(rc)
 | 
			
		||||
            .map(|_| {
 | 
			
		||||
                     self.pos += n;
 | 
			
		||||
                     n as usize
 | 
			
		||||
                 })
 | 
			
		||||
            .map_err(|err| io::Error::new(io::ErrorKind::Other, err))
 | 
			
		||||
                self.pos += n;
 | 
			
		||||
                n as usize
 | 
			
		||||
            }).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn flush(&mut self) -> io::Result<()> {
 | 
			
		||||
@@ -210,11 +205,15 @@ impl<'conn> io::Seek for Blob<'conn> {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if pos < 0 {
 | 
			
		||||
            Err(io::Error::new(io::ErrorKind::InvalidInput,
 | 
			
		||||
                               "invalid seek to negative position"))
 | 
			
		||||
            Err(io::Error::new(
 | 
			
		||||
                io::ErrorKind::InvalidInput,
 | 
			
		||||
                "invalid seek to negative position",
 | 
			
		||||
            ))
 | 
			
		||||
        } else if pos > i64::from(self.size()) {
 | 
			
		||||
            Err(io::Error::new(io::ErrorKind::InvalidInput,
 | 
			
		||||
                               "invalid seek to position past end of blob"))
 | 
			
		||||
            Err(io::Error::new(
 | 
			
		||||
                io::ErrorKind::InvalidInput,
 | 
			
		||||
                "invalid seek to position past end of blob",
 | 
			
		||||
            ))
 | 
			
		||||
        } else {
 | 
			
		||||
            self.pos = pos as i32;
 | 
			
		||||
            Ok(pos as u64)
 | 
			
		||||
@@ -235,7 +234,7 @@ impl<'conn> Drop for Blob<'conn> {
 | 
			
		||||
/// incremental BLOB I/O routines.
 | 
			
		||||
///
 | 
			
		||||
/// A negative value for the zeroblob results in a zero-length BLOB.
 | 
			
		||||
#[derive(Copy,Clone)]
 | 
			
		||||
#[derive(Copy, Clone)]
 | 
			
		||||
pub struct ZeroBlob(pub i32);
 | 
			
		||||
 | 
			
		||||
impl ToSql for ZeroBlob {
 | 
			
		||||
@@ -247,10 +246,9 @@ impl ToSql for ZeroBlob {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use std::io::{BufReader, BufRead, BufWriter, Read, Write, Seek, SeekFrom};
 | 
			
		||||
    use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write};
 | 
			
		||||
    use {Connection, DatabaseName, Result};
 | 
			
		||||
 | 
			
		||||
    #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
    fn db_with_test_blob() -> Result<(Connection, i64)> {
 | 
			
		||||
        let db = try!(Connection::open_in_memory());
 | 
			
		||||
        let sql = "BEGIN;
 | 
			
		||||
@@ -266,7 +264,8 @@ mod test {
 | 
			
		||||
    fn test_blob() {
 | 
			
		||||
        let (db, rowid) = db_with_test_blob().unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
        let mut blob = db
 | 
			
		||||
            .blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(4, blob.write(b"Clob").unwrap());
 | 
			
		||||
        assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10
 | 
			
		||||
@@ -275,7 +274,8 @@ mod test {
 | 
			
		||||
        blob.reopen(rowid).unwrap();
 | 
			
		||||
        blob.close().unwrap();
 | 
			
		||||
 | 
			
		||||
        blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true)
 | 
			
		||||
        blob = db
 | 
			
		||||
            .blob_open(DatabaseName::Main, "test", "content", rowid, true)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        let mut bytes = [0u8; 5];
 | 
			
		||||
        assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
 | 
			
		||||
@@ -316,7 +316,8 @@ mod test {
 | 
			
		||||
    fn test_blob_in_bufreader() {
 | 
			
		||||
        let (db, rowid) = db_with_test_blob().unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
        let mut blob = db
 | 
			
		||||
            .blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(8, blob.write(b"one\ntwo\n").unwrap());
 | 
			
		||||
 | 
			
		||||
@@ -341,7 +342,8 @@ mod test {
 | 
			
		||||
        let (db, rowid) = db_with_test_blob().unwrap();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
            let blob = db
 | 
			
		||||
                .blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            let mut writer = BufWriter::new(blob);
 | 
			
		||||
 | 
			
		||||
@@ -353,7 +355,8 @@ mod test {
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            // ... but it should've written the first 10 bytes
 | 
			
		||||
            let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
            let mut blob = db
 | 
			
		||||
                .blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            let mut bytes = [0u8; 10];
 | 
			
		||||
            assert_eq!(10, blob.read(&mut bytes[..]).unwrap());
 | 
			
		||||
@@ -361,7 +364,8 @@ mod test {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
            let blob = db
 | 
			
		||||
                .blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            let mut writer = BufWriter::new(blob);
 | 
			
		||||
 | 
			
		||||
@@ -372,7 +376,8 @@ mod test {
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            // ... but it should've written the first 10 bytes
 | 
			
		||||
            let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
            let mut blob = db
 | 
			
		||||
                .blob_open(DatabaseName::Main, "test", "content", rowid, false)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            let mut bytes = [0u8; 10];
 | 
			
		||||
            assert_eq!(10, blob.read(&mut bytes[..]).unwrap());
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										162
									
								
								src/busy.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								src/busy.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,162 @@
 | 
			
		||||
///! Busy handler (when the database is locked)
 | 
			
		||||
use std::mem;
 | 
			
		||||
use std::os::raw::{c_int, c_void};
 | 
			
		||||
use std::ptr;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
use ffi;
 | 
			
		||||
use {Connection, InnerConnection, Result};
 | 
			
		||||
 | 
			
		||||
impl Connection {
 | 
			
		||||
    /// Set a busy handler that sleeps for a specified amount of time when a table is locked.
 | 
			
		||||
    /// The handler will sleep multiple times until at least "ms" milliseconds of sleeping have accumulated.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Calling this routine with an argument equal to zero turns off all busy handlers.
 | 
			
		||||
    //
 | 
			
		||||
    /// There can only be a single busy handler for a particular database connection at any given moment.
 | 
			
		||||
    /// If another busy handler was defined (using `busy_handler`) prior to calling this routine, that other busy handler is cleared.
 | 
			
		||||
    pub fn busy_timeout(&self, timeout: Duration) -> Result<()> {
 | 
			
		||||
        let ms = timeout
 | 
			
		||||
            .as_secs()
 | 
			
		||||
            .checked_mul(1000)
 | 
			
		||||
            .and_then(|t| t.checked_add(timeout.subsec_millis().into()))
 | 
			
		||||
            .expect("too big");
 | 
			
		||||
        self.db.borrow_mut().busy_timeout(ms as i32)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Register a callback to handle `SQLITE_BUSY` errors.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If the busy callback is `None`, then `SQLITE_BUSY is returned immediately upon encountering the lock.`
 | 
			
		||||
    /// The argument to the busy handler callback is the number of times that the busy handler has been invoked previously for the same locking event.
 | 
			
		||||
    /// If the busy callback returns `false`, then no additional attempts are made to access the database and `SQLITE_BUSY` is returned to the application.
 | 
			
		||||
    /// If the callback returns `true`, then another attempt is made to access the database and the cycle repeats.
 | 
			
		||||
    ///
 | 
			
		||||
    /// There can only be a single busy handler defined for each database connection.
 | 
			
		||||
    /// Setting a new busy handler clears any previously set handler.
 | 
			
		||||
    /// Note that calling `busy_timeout()` or evaluating `PRAGMA busy_timeout=N` will change the busy handler and thus clear any previously set busy handler.
 | 
			
		||||
    pub fn busy_handler(&self, callback: Option<fn(i32) -> bool>) -> Result<()> {
 | 
			
		||||
        unsafe extern "C" fn busy_handler_callback(p_arg: *mut c_void, count: c_int) -> c_int {
 | 
			
		||||
            let handler_fn: fn(i32) -> bool = mem::transmute(p_arg);
 | 
			
		||||
            if handler_fn(count) {
 | 
			
		||||
                1
 | 
			
		||||
            } else {
 | 
			
		||||
                0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        let mut c = self.db.borrow_mut();
 | 
			
		||||
        let r = match callback {
 | 
			
		||||
            Some(f) => unsafe {
 | 
			
		||||
                ffi::sqlite3_busy_handler(c.db(), Some(busy_handler_callback), mem::transmute(f))
 | 
			
		||||
            },
 | 
			
		||||
            None => unsafe { ffi::sqlite3_busy_handler(c.db(), None, ptr::null_mut()) },
 | 
			
		||||
        };
 | 
			
		||||
        c.decode_result(r)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl InnerConnection {
 | 
			
		||||
    fn busy_timeout(&mut self, timeout: c_int) -> Result<()> {
 | 
			
		||||
        let r = unsafe { ffi::sqlite3_busy_timeout(self.db, timeout) };
 | 
			
		||||
        self.decode_result(r)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    extern crate tempdir;
 | 
			
		||||
    use self::tempdir::TempDir;
 | 
			
		||||
    use std::sync::atomic::{AtomicBool, Ordering};
 | 
			
		||||
    use std::sync::mpsc::sync_channel;
 | 
			
		||||
    use std::thread;
 | 
			
		||||
    use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
    use {Connection, Error, ErrorCode, TransactionBehavior};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_default_busy() {
 | 
			
		||||
        let temp_dir = TempDir::new("test_default_busy").unwrap();
 | 
			
		||||
        let path = temp_dir.path().join("test.db3");
 | 
			
		||||
 | 
			
		||||
        let mut db1 = Connection::open(&path).unwrap();
 | 
			
		||||
        let tx1 = db1
 | 
			
		||||
            .transaction_with_behavior(TransactionBehavior::Exclusive)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        let db2 = Connection::open(&path).unwrap();
 | 
			
		||||
        let r = db2.query_row("PRAGMA schema_version", &[], |_| unreachable!());
 | 
			
		||||
        match r.unwrap_err() {
 | 
			
		||||
            Error::SqliteFailure(err, _) => {
 | 
			
		||||
                assert_eq!(err.code, ErrorCode::DatabaseBusy);
 | 
			
		||||
            }
 | 
			
		||||
            err => panic!("Unexpected error {}", err),
 | 
			
		||||
        }
 | 
			
		||||
        tx1.rollback().unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[ignore] // FIXME: unstable
 | 
			
		||||
    fn test_busy_timeout() {
 | 
			
		||||
        let temp_dir = TempDir::new("test_busy_timeout").unwrap();
 | 
			
		||||
        let path = temp_dir.path().join("test.db3");
 | 
			
		||||
 | 
			
		||||
        let db2 = Connection::open(&path).unwrap();
 | 
			
		||||
        db2.busy_timeout(Duration::from_secs(1)).unwrap();
 | 
			
		||||
 | 
			
		||||
        let (rx, tx) = sync_channel(0);
 | 
			
		||||
        let child = thread::spawn(move || {
 | 
			
		||||
            let mut db1 = Connection::open(&path).unwrap();
 | 
			
		||||
            let tx1 = db1
 | 
			
		||||
                .transaction_with_behavior(TransactionBehavior::Exclusive)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            rx.send(1).unwrap();
 | 
			
		||||
            thread::sleep(Duration::from_millis(100));
 | 
			
		||||
            tx1.rollback().unwrap();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        assert_eq!(tx.recv().unwrap(), 1);
 | 
			
		||||
        let _ = db2
 | 
			
		||||
            .query_row("PRAGMA schema_version", &[], |row| {
 | 
			
		||||
                row.get_checked::<_, i32>(0)
 | 
			
		||||
            }).expect("unexpected error");
 | 
			
		||||
 | 
			
		||||
        child.join().unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[ignore] // FIXME: unstable
 | 
			
		||||
    fn test_busy_handler() {
 | 
			
		||||
        lazy_static! {
 | 
			
		||||
            static ref CALLED: AtomicBool = AtomicBool::new(false);
 | 
			
		||||
        }
 | 
			
		||||
        fn busy_handler(_: i32) -> bool {
 | 
			
		||||
            CALLED.store(true, Ordering::Relaxed);
 | 
			
		||||
            thread::sleep(Duration::from_millis(100));
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let temp_dir = TempDir::new("test_busy_handler").unwrap();
 | 
			
		||||
        let path = temp_dir.path().join("test.db3");
 | 
			
		||||
 | 
			
		||||
        let db2 = Connection::open(&path).unwrap();
 | 
			
		||||
        db2.busy_handler(Some(busy_handler)).unwrap();
 | 
			
		||||
 | 
			
		||||
        let (rx, tx) = sync_channel(0);
 | 
			
		||||
        let child = thread::spawn(move || {
 | 
			
		||||
            let mut db1 = Connection::open(&path).unwrap();
 | 
			
		||||
            let tx1 = db1
 | 
			
		||||
                .transaction_with_behavior(TransactionBehavior::Exclusive)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            rx.send(1).unwrap();
 | 
			
		||||
            thread::sleep(Duration::from_millis(100));
 | 
			
		||||
            tx1.rollback().unwrap();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        assert_eq!(tx.recv().unwrap(), 1);
 | 
			
		||||
        let _ = db2
 | 
			
		||||
            .query_row("PRAGMA schema_version", &[], |row| {
 | 
			
		||||
                row.get_checked::<_, i32>(0)
 | 
			
		||||
            }).expect("unexpected error");
 | 
			
		||||
        assert_eq!(CALLED.load(Ordering::Relaxed), true);
 | 
			
		||||
 | 
			
		||||
        child.join().unwrap();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										83
									
								
								src/cache.rs
									
									
									
									
									
								
							
							
						
						
									
										83
									
								
								src/cache.rs
									
									
									
									
									
								
							@@ -1,11 +1,10 @@
 | 
			
		||||
//! Prepared statements cache for faster execution.
 | 
			
		||||
 | 
			
		||||
use lru_cache::LruCache;
 | 
			
		||||
use raw_statement::RawStatement;
 | 
			
		||||
use std::cell::RefCell;
 | 
			
		||||
use std::ops::{Deref, DerefMut};
 | 
			
		||||
use lru_cache::LruCache;
 | 
			
		||||
use {Result, Connection, Statement};
 | 
			
		||||
use raw_statement::RawStatement;
 | 
			
		||||
use statement::StatementCrateImpl;
 | 
			
		||||
use {Connection, Result, Statement};
 | 
			
		||||
 | 
			
		||||
impl Connection {
 | 
			
		||||
    /// Prepare a SQL statement for execution, returning a previously prepared (but
 | 
			
		||||
@@ -120,10 +119,11 @@ impl StatementCache {
 | 
			
		||||
    //
 | 
			
		||||
    // Will return `Err` if no cached statement can be found and the underlying SQLite prepare
 | 
			
		||||
    // call fails.
 | 
			
		||||
    fn get<'conn>(&'conn self,
 | 
			
		||||
                  conn: &'conn Connection,
 | 
			
		||||
                  sql: &str)
 | 
			
		||||
                  -> Result<CachedStatement<'conn>> {
 | 
			
		||||
    fn get<'conn>(
 | 
			
		||||
        &'conn self,
 | 
			
		||||
        conn: &'conn Connection,
 | 
			
		||||
        sql: &str,
 | 
			
		||||
    ) -> Result<CachedStatement<'conn>> {
 | 
			
		||||
        let mut cache = self.0.borrow_mut();
 | 
			
		||||
        let stmt = match cache.remove(sql.trim()) {
 | 
			
		||||
            Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)),
 | 
			
		||||
@@ -136,7 +136,9 @@ impl StatementCache {
 | 
			
		||||
    fn cache_stmt(&self, stmt: RawStatement) {
 | 
			
		||||
        let mut cache = self.0.borrow_mut();
 | 
			
		||||
        stmt.clear_bindings();
 | 
			
		||||
        let sql = String::from_utf8_lossy(stmt.sql().to_bytes()).trim().to_string();
 | 
			
		||||
        let sql = String::from_utf8_lossy(stmt.sql().to_bytes())
 | 
			
		||||
            .trim()
 | 
			
		||||
            .to_string();
 | 
			
		||||
        cache.insert(sql, stmt);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -148,8 +150,8 @@ impl StatementCache {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use Connection;
 | 
			
		||||
    use super::StatementCache;
 | 
			
		||||
    use Connection;
 | 
			
		||||
 | 
			
		||||
    impl StatementCache {
 | 
			
		||||
        fn clear(&self) {
 | 
			
		||||
@@ -177,14 +179,14 @@ mod test {
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare_cached(sql).unwrap();
 | 
			
		||||
            assert_eq!(0, cache.len());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<_, i64>(0)).unwrap());
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(1, cache.len());
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare_cached(sql).unwrap();
 | 
			
		||||
            assert_eq!(0, cache.len());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<_, i64>(0)).unwrap());
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(1, cache.len());
 | 
			
		||||
 | 
			
		||||
@@ -202,7 +204,7 @@ mod test {
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare_cached(sql).unwrap();
 | 
			
		||||
            assert_eq!(0, cache.len());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<_, i64>(0)).unwrap());
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(1, cache.len());
 | 
			
		||||
 | 
			
		||||
@@ -212,7 +214,7 @@ mod test {
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare_cached(sql).unwrap();
 | 
			
		||||
            assert_eq!(0, cache.len());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<_, i64>(0)).unwrap());
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(0, cache.len());
 | 
			
		||||
 | 
			
		||||
@@ -220,7 +222,7 @@ mod test {
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare_cached(sql).unwrap();
 | 
			
		||||
            assert_eq!(0, cache.len());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<_, i64>(0)).unwrap());
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(1, cache.len());
 | 
			
		||||
    }
 | 
			
		||||
@@ -234,7 +236,7 @@ mod test {
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare_cached(sql).unwrap();
 | 
			
		||||
            assert_eq!(0, cache.len());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<_, i64>(0)).unwrap());
 | 
			
		||||
            stmt.discard();
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(0, cache.len());
 | 
			
		||||
@@ -243,46 +245,51 @@ mod test {
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_ddl() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
        db.execute_batch(r#"
 | 
			
		||||
        db.execute_batch(
 | 
			
		||||
            r#"
 | 
			
		||||
            CREATE TABLE foo (x INT);
 | 
			
		||||
            INSERT INTO foo VALUES (1);
 | 
			
		||||
        "#)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        "#,
 | 
			
		||||
        ).unwrap();
 | 
			
		||||
 | 
			
		||||
        let sql = "SELECT * FROM foo";
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare_cached(sql).unwrap();
 | 
			
		||||
            assert_eq!(1i32,
 | 
			
		||||
                       stmt.query_map::<i32, _>(&[], |r| r.get(0))
 | 
			
		||||
                           .unwrap()
 | 
			
		||||
                           .next()
 | 
			
		||||
                           .unwrap()
 | 
			
		||||
                           .unwrap());
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                1i32,
 | 
			
		||||
                stmt.query_map::<i32, _>(&[], |r| r.get(0))
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .next()
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        db.execute_batch(r#"
 | 
			
		||||
        db.execute_batch(
 | 
			
		||||
            r#"
 | 
			
		||||
            ALTER TABLE foo ADD COLUMN y INT;
 | 
			
		||||
            UPDATE foo SET y = 2;
 | 
			
		||||
        "#)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        "#,
 | 
			
		||||
        ).unwrap();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare_cached(sql).unwrap();
 | 
			
		||||
            assert_eq!((1i32, 2i32),
 | 
			
		||||
                       stmt.query_map(&[], |r| (r.get(0), r.get(1)))
 | 
			
		||||
                           .unwrap()
 | 
			
		||||
                           .next()
 | 
			
		||||
                           .unwrap()
 | 
			
		||||
                           .unwrap());
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                (1i32, 2i32),
 | 
			
		||||
                stmt.query_map(&[], |r| (r.get(0), r.get(1)))
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .next()
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_connection_close() {
 | 
			
		||||
        let conn = Connection::open_in_memory().unwrap();
 | 
			
		||||
        conn.prepare_cached("SELECT * FROM sqlite_master;")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        conn.prepare_cached("SELECT * FROM sqlite_master;").unwrap();
 | 
			
		||||
 | 
			
		||||
        conn.close().expect("connection not closed");
 | 
			
		||||
    }
 | 
			
		||||
@@ -298,14 +305,14 @@ mod test {
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare_cached(sql).unwrap();
 | 
			
		||||
            assert_eq!(0, cache.len());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<_, i64>(0)).unwrap());
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(1, cache.len());
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare_cached(sql).unwrap();
 | 
			
		||||
            assert_eq!(0, cache.len());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<i32, i64>(0)).unwrap());
 | 
			
		||||
            assert_eq!(0, stmt.query_row(&[], |r| r.get::<_, i64>(0)).unwrap());
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(1, cache.len());
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										124
									
								
								src/context.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/context.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
			
		||||
//! Code related to `sqlite3_context` common to `functions` and `vtab` modules.
 | 
			
		||||
 | 
			
		||||
use std::ffi::CStr;
 | 
			
		||||
use std::os::raw::{c_char, c_int, c_void};
 | 
			
		||||
#[cfg(feature = "array")]
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
 | 
			
		||||
use ffi;
 | 
			
		||||
use ffi::sqlite3_context;
 | 
			
		||||
use ffi::sqlite3_value;
 | 
			
		||||
 | 
			
		||||
use str_to_cstring;
 | 
			
		||||
use types::{ToSqlOutput, ValueRef};
 | 
			
		||||
#[cfg(feature = "array")]
 | 
			
		||||
use vtab::array::{free_array, ARRAY_TYPE};
 | 
			
		||||
 | 
			
		||||
impl<'a> ValueRef<'a> {
 | 
			
		||||
    pub(crate) unsafe fn from_value(value: *mut sqlite3_value) -> ValueRef<'a> {
 | 
			
		||||
        use std::slice::from_raw_parts;
 | 
			
		||||
 | 
			
		||||
        match ffi::sqlite3_value_type(value) {
 | 
			
		||||
            ffi::SQLITE_NULL => ValueRef::Null,
 | 
			
		||||
            ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_value_int64(value)),
 | 
			
		||||
            ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)),
 | 
			
		||||
            ffi::SQLITE_TEXT => {
 | 
			
		||||
                let text = ffi::sqlite3_value_text(value);
 | 
			
		||||
                assert!(
 | 
			
		||||
                    !text.is_null(),
 | 
			
		||||
                    "unexpected SQLITE_TEXT value type with NULL data"
 | 
			
		||||
                );
 | 
			
		||||
                let s = CStr::from_ptr(text as *const c_char);
 | 
			
		||||
 | 
			
		||||
                // sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
 | 
			
		||||
                let s = s
 | 
			
		||||
                    .to_str()
 | 
			
		||||
                    .expect("sqlite3_value_text returned invalid UTF-8");
 | 
			
		||||
                ValueRef::Text(s)
 | 
			
		||||
            }
 | 
			
		||||
            ffi::SQLITE_BLOB => {
 | 
			
		||||
                let (blob, len) = (
 | 
			
		||||
                    ffi::sqlite3_value_blob(value),
 | 
			
		||||
                    ffi::sqlite3_value_bytes(value),
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
                assert!(
 | 
			
		||||
                    len >= 0,
 | 
			
		||||
                    "unexpected negative return from sqlite3_value_bytes"
 | 
			
		||||
                );
 | 
			
		||||
                if len > 0 {
 | 
			
		||||
                    assert!(
 | 
			
		||||
                        !blob.is_null(),
 | 
			
		||||
                        "unexpected SQLITE_BLOB value type with NULL data"
 | 
			
		||||
                    );
 | 
			
		||||
                    ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize))
 | 
			
		||||
                } else {
 | 
			
		||||
                    // The return value from sqlite3_value_blob() for a zero-length BLOB
 | 
			
		||||
                    // is a NULL pointer.
 | 
			
		||||
                    ValueRef::Blob(&[])
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => unreachable!("sqlite3_value_type returned invalid value"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
 | 
			
		||||
    let value = match *result {
 | 
			
		||||
        ToSqlOutput::Borrowed(v) => v,
 | 
			
		||||
        ToSqlOutput::Owned(ref v) => ValueRef::from(v),
 | 
			
		||||
 | 
			
		||||
        #[cfg(feature = "blob")]
 | 
			
		||||
        ToSqlOutput::ZeroBlob(len) => {
 | 
			
		||||
            return ffi::sqlite3_result_zeroblob(ctx, len);
 | 
			
		||||
        }
 | 
			
		||||
        #[cfg(feature = "array")]
 | 
			
		||||
        ToSqlOutput::Array(ref a) => {
 | 
			
		||||
            return ffi::sqlite3_result_pointer(
 | 
			
		||||
                ctx,
 | 
			
		||||
                Rc::into_raw(a.clone()) as *mut c_void,
 | 
			
		||||
                ARRAY_TYPE,
 | 
			
		||||
                Some(free_array),
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    match value {
 | 
			
		||||
        ValueRef::Null => ffi::sqlite3_result_null(ctx),
 | 
			
		||||
        ValueRef::Integer(i) => ffi::sqlite3_result_int64(ctx, i),
 | 
			
		||||
        ValueRef::Real(r) => ffi::sqlite3_result_double(ctx, r),
 | 
			
		||||
        ValueRef::Text(s) => {
 | 
			
		||||
            let length = s.len();
 | 
			
		||||
            if length > ::std::i32::MAX as usize {
 | 
			
		||||
                ffi::sqlite3_result_error_toobig(ctx);
 | 
			
		||||
            } else {
 | 
			
		||||
                let c_str = match str_to_cstring(s) {
 | 
			
		||||
                    Ok(c_str) => c_str,
 | 
			
		||||
                    // TODO sqlite3_result_error
 | 
			
		||||
                    Err(_) => return ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE),
 | 
			
		||||
                };
 | 
			
		||||
                let destructor = if length > 0 {
 | 
			
		||||
                    ffi::SQLITE_TRANSIENT()
 | 
			
		||||
                } else {
 | 
			
		||||
                    ffi::SQLITE_STATIC()
 | 
			
		||||
                };
 | 
			
		||||
                ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, destructor);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ValueRef::Blob(b) => {
 | 
			
		||||
            let length = b.len();
 | 
			
		||||
            if length > ::std::i32::MAX as usize {
 | 
			
		||||
                ffi::sqlite3_result_error_toobig(ctx);
 | 
			
		||||
            } else if length == 0 {
 | 
			
		||||
                ffi::sqlite3_result_zeroblob(ctx, 0)
 | 
			
		||||
            } else {
 | 
			
		||||
                ffi::sqlite3_result_blob(
 | 
			
		||||
                    ctx,
 | 
			
		||||
                    b.as_ptr() as *const c_void,
 | 
			
		||||
                    length as c_int,
 | 
			
		||||
                    ffi::SQLITE_TRANSIENT(),
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										97
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										97
									
								
								src/error.rs
									
									
									
									
									
								
							@@ -1,10 +1,10 @@
 | 
			
		||||
use std::error;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::os::raw::c_int;
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::str;
 | 
			
		||||
use std::os::raw::c_int;
 | 
			
		||||
use {ffi, errmsg_to_string};
 | 
			
		||||
use types::Type;
 | 
			
		||||
use {errmsg_to_string, ffi};
 | 
			
		||||
 | 
			
		||||
/// Old name for `Error`. `SqliteError` is deprecated.
 | 
			
		||||
#[deprecated(since = "0.6.0", note = "Use Error instead")]
 | 
			
		||||
@@ -26,9 +26,9 @@ pub enum Error {
 | 
			
		||||
    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
 | 
			
		||||
    /// trying to get the value 1000 into a `u8`).  The associated `usize` is the column index, and
 | 
			
		||||
    /// the associated `i64` is the value returned by SQLite.
 | 
			
		||||
    IntegralValueOutOfRange(c_int, i64),
 | 
			
		||||
    IntegralValueOutOfRange(usize, i64),
 | 
			
		||||
 | 
			
		||||
    /// Error converting a string to UTF-8.
 | 
			
		||||
    Utf8Error(str::Utf8Error),
 | 
			
		||||
@@ -51,7 +51,7 @@ pub enum Error {
 | 
			
		||||
 | 
			
		||||
    /// Error when the value of a particular column is requested, but the index is out of range
 | 
			
		||||
    /// for the statement.
 | 
			
		||||
    InvalidColumnIndex(c_int),
 | 
			
		||||
    InvalidColumnIndex(usize),
 | 
			
		||||
 | 
			
		||||
    /// Error when the value of a named column is requested, but no column matches the name
 | 
			
		||||
    /// for the statement.
 | 
			
		||||
@@ -59,15 +59,19 @@ pub enum Error {
 | 
			
		||||
 | 
			
		||||
    /// Error when the value of a particular column is requested, but the type of the result in
 | 
			
		||||
    /// that column cannot be converted to the requested Rust type.
 | 
			
		||||
    InvalidColumnType(c_int, Type),
 | 
			
		||||
    InvalidColumnType(usize, Type),
 | 
			
		||||
 | 
			
		||||
    /// Error when a query that was expected to insert one row did not insert any or insert many.
 | 
			
		||||
    StatementChangedRows(c_int),
 | 
			
		||||
    StatementChangedRows(usize),
 | 
			
		||||
 | 
			
		||||
    /// Error returned by `functions::Context::get` when the function argument cannot be converted
 | 
			
		||||
    /// to the requested type.
 | 
			
		||||
    #[cfg(feature = "functions")]
 | 
			
		||||
    InvalidFunctionParameterType(usize, Type),
 | 
			
		||||
    /// Error returned by `vtab::Values::get` when the filter argument cannot be converted
 | 
			
		||||
    /// to the requested type.
 | 
			
		||||
    #[cfg(feature = "vtab")]
 | 
			
		||||
    InvalidFilterParameterType(usize, Type),
 | 
			
		||||
 | 
			
		||||
    /// An error case available for implementors of custom user functions (e.g.,
 | 
			
		||||
    /// `create_scalar_function`).
 | 
			
		||||
@@ -77,6 +81,15 @@ pub enum Error {
 | 
			
		||||
 | 
			
		||||
    /// Error available for the implementors of the `ToSql` trait.
 | 
			
		||||
    ToSqlConversionFailure(Box<error::Error + Send + Sync>),
 | 
			
		||||
 | 
			
		||||
    /// Error when the SQL is not a `SELECT`, is not read-only.
 | 
			
		||||
    InvalidQuery,
 | 
			
		||||
 | 
			
		||||
    /// An error case available for implementors of custom modules (e.g.,
 | 
			
		||||
    /// `create_module`).
 | 
			
		||||
    #[cfg(feature = "vtab")]
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    ModuleError(String),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<str::Utf8Error> for Error {
 | 
			
		||||
@@ -96,17 +109,15 @@ impl fmt::Display for Error {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Error::SqliteFailure(ref err, None) => err.fmt(f),
 | 
			
		||||
            Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s),
 | 
			
		||||
            Error::SqliteSingleThreadedMode => {
 | 
			
		||||
                write!(f,
 | 
			
		||||
                       "SQLite was compiled or configured for single-threaded use only")
 | 
			
		||||
            }
 | 
			
		||||
            Error::FromSqlConversionFailure(i, ref t, ref err) => {
 | 
			
		||||
                write!(f,
 | 
			
		||||
                       "Conversion error from type {} at index: {}, {}",
 | 
			
		||||
                       t,
 | 
			
		||||
                       i,
 | 
			
		||||
                       err)
 | 
			
		||||
            }
 | 
			
		||||
            Error::SqliteSingleThreadedMode => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "SQLite was compiled or configured for single-threaded use only"
 | 
			
		||||
            ),
 | 
			
		||||
            Error::FromSqlConversionFailure(i, ref t, ref err) => write!(
 | 
			
		||||
                f,
 | 
			
		||||
                "Conversion error from type {} at index: {}, {}",
 | 
			
		||||
                t, i, err
 | 
			
		||||
            ),
 | 
			
		||||
            Error::IntegralValueOutOfRange(col, val) => {
 | 
			
		||||
                write!(f, "Integer {} out of range at index {}", val, col)
 | 
			
		||||
            }
 | 
			
		||||
@@ -129,9 +140,16 @@ impl fmt::Display for Error {
 | 
			
		||||
            Error::InvalidFunctionParameterType(i, ref t) => {
 | 
			
		||||
                write!(f, "Invalid function parameter type {} at index {}", t, i)
 | 
			
		||||
            }
 | 
			
		||||
            #[cfg(feature = "vtab")]
 | 
			
		||||
            Error::InvalidFilterParameterType(i, ref t) => {
 | 
			
		||||
                write!(f, "Invalid filter parameter type {} at index {}", t, i)
 | 
			
		||||
            }
 | 
			
		||||
            #[cfg(feature = "functions")]
 | 
			
		||||
            Error::UserFunctionError(ref err) => err.fmt(f),
 | 
			
		||||
            Error::ToSqlConversionFailure(ref err) => err.fmt(f),
 | 
			
		||||
            Error::InvalidQuery => write!(f, "Query is not read-only"),
 | 
			
		||||
            #[cfg(feature = "vtab")]
 | 
			
		||||
            Error::ModuleError(ref desc) => write!(f, "{}", desc),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -141,14 +159,18 @@ impl error::Error for Error {
 | 
			
		||||
        match *self {
 | 
			
		||||
            Error::SqliteFailure(ref err, None) => err.description(),
 | 
			
		||||
            Error::SqliteFailure(_, Some(ref s)) => s,
 | 
			
		||||
            Error::SqliteSingleThreadedMode => "SQLite was compiled or configured for single-threaded use only",
 | 
			
		||||
            Error::SqliteSingleThreadedMode => {
 | 
			
		||||
                "SQLite was compiled or configured for single-threaded use only"
 | 
			
		||||
            }
 | 
			
		||||
            Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
 | 
			
		||||
            Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type",
 | 
			
		||||
            Error::Utf8Error(ref err) => err.description(),
 | 
			
		||||
            Error::InvalidParameterName(_) => "invalid parameter name",
 | 
			
		||||
            Error::NulError(ref err) => err.description(),
 | 
			
		||||
            Error::InvalidPath(_) => "invalid path",
 | 
			
		||||
            Error::ExecuteReturnedResults => "execute returned results - did you mean to call query?",
 | 
			
		||||
            Error::ExecuteReturnedResults => {
 | 
			
		||||
                "execute returned results - did you mean to call query?"
 | 
			
		||||
            }
 | 
			
		||||
            Error::QueryReturnedNoRows => "query returned no rows",
 | 
			
		||||
            Error::InvalidColumnIndex(_) => "invalid column index",
 | 
			
		||||
            Error::InvalidColumnName(_) => "invalid column name",
 | 
			
		||||
@@ -157,9 +179,14 @@ impl error::Error for Error {
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "functions")]
 | 
			
		||||
            Error::InvalidFunctionParameterType(_, _) => "invalid function parameter type",
 | 
			
		||||
            #[cfg(feature = "vtab")]
 | 
			
		||||
            Error::InvalidFilterParameterType(_, _) => "invalid filter parameter type",
 | 
			
		||||
            #[cfg(feature = "functions")]
 | 
			
		||||
            Error::UserFunctionError(ref err) => err.description(),
 | 
			
		||||
            Error::ToSqlConversionFailure(ref err) => err.description(),
 | 
			
		||||
            Error::InvalidQuery => "query is not read-only",
 | 
			
		||||
            #[cfg(feature = "vtab")]
 | 
			
		||||
            Error::ModuleError(ref desc) => desc,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -169,25 +196,31 @@ impl error::Error for Error {
 | 
			
		||||
            Error::Utf8Error(ref err) => Some(err),
 | 
			
		||||
            Error::NulError(ref err) => Some(err),
 | 
			
		||||
 | 
			
		||||
            Error::IntegralValueOutOfRange(_, _) |
 | 
			
		||||
            Error::SqliteSingleThreadedMode |
 | 
			
		||||
            Error::InvalidParameterName(_) |
 | 
			
		||||
            Error::ExecuteReturnedResults |
 | 
			
		||||
            Error::QueryReturnedNoRows |
 | 
			
		||||
            Error::InvalidColumnIndex(_) |
 | 
			
		||||
            Error::InvalidColumnName(_) |
 | 
			
		||||
            Error::InvalidColumnType(_, _) |
 | 
			
		||||
            Error::InvalidPath(_) |
 | 
			
		||||
            Error::StatementChangedRows(_) => None,
 | 
			
		||||
            Error::IntegralValueOutOfRange(_, _)
 | 
			
		||||
            | Error::SqliteSingleThreadedMode
 | 
			
		||||
            | Error::InvalidParameterName(_)
 | 
			
		||||
            | Error::ExecuteReturnedResults
 | 
			
		||||
            | Error::QueryReturnedNoRows
 | 
			
		||||
            | Error::InvalidColumnIndex(_)
 | 
			
		||||
            | Error::InvalidColumnName(_)
 | 
			
		||||
            | Error::InvalidColumnType(_, _)
 | 
			
		||||
            | Error::InvalidPath(_)
 | 
			
		||||
            | Error::StatementChangedRows(_)
 | 
			
		||||
            | Error::InvalidQuery => None,
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "functions")]
 | 
			
		||||
            Error::InvalidFunctionParameterType(_, _) => None,
 | 
			
		||||
            #[cfg(feature = "vtab")]
 | 
			
		||||
            Error::InvalidFilterParameterType(_, _) => None,
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "functions")]
 | 
			
		||||
            Error::UserFunctionError(ref err) => Some(&**err),
 | 
			
		||||
 | 
			
		||||
            Error::FromSqlConversionFailure(_, _, ref err) |
 | 
			
		||||
            Error::ToSqlConversionFailure(ref err) => Some(&**err),
 | 
			
		||||
            Error::FromSqlConversionFailure(_, _, ref err)
 | 
			
		||||
            | Error::ToSqlConversionFailure(ref err) => Some(&**err),
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "vtab")]
 | 
			
		||||
            Error::ModuleError(_) => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										414
									
								
								src/functions.rs
									
									
									
									
									
								
							
							
						
						
									
										414
									
								
								src/functions.rs
									
									
									
									
									
								
							@@ -50,67 +50,18 @@
 | 
			
		||||
//! }
 | 
			
		||||
//! ```
 | 
			
		||||
use std::error::Error as StdError;
 | 
			
		||||
use std::ffi::CStr;
 | 
			
		||||
use std::os::raw::{c_int, c_void};
 | 
			
		||||
use std::ptr;
 | 
			
		||||
use std::slice;
 | 
			
		||||
use std::os::raw::{c_int, c_char, c_void};
 | 
			
		||||
 | 
			
		||||
use ffi;
 | 
			
		||||
use ffi::sqlite3_context;
 | 
			
		||||
use ffi::sqlite3_value;
 | 
			
		||||
 | 
			
		||||
use types::{ToSql, ToSqlOutput, FromSql, FromSqlError, ValueRef};
 | 
			
		||||
use context::set_result;
 | 
			
		||||
use types::{FromSql, FromSqlError, ToSql, ValueRef};
 | 
			
		||||
 | 
			
		||||
use {Result, Error, Connection, str_to_cstring, InnerConnection};
 | 
			
		||||
 | 
			
		||||
fn set_result<'a>(ctx: *mut sqlite3_context, result: &ToSqlOutput<'a>) {
 | 
			
		||||
    let value = match *result {
 | 
			
		||||
        ToSqlOutput::Borrowed(v) => v,
 | 
			
		||||
        ToSqlOutput::Owned(ref v) => ValueRef::from(v),
 | 
			
		||||
 | 
			
		||||
        #[cfg(feature = "blob")]
 | 
			
		||||
        ToSqlOutput::ZeroBlob(len) => {
 | 
			
		||||
            return unsafe { ffi::sqlite3_result_zeroblob(ctx, len) };
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    match value {
 | 
			
		||||
        ValueRef::Null => unsafe { ffi::sqlite3_result_null(ctx) },
 | 
			
		||||
        ValueRef::Integer(i) => unsafe { ffi::sqlite3_result_int64(ctx, i) },
 | 
			
		||||
        ValueRef::Real(r) => unsafe { ffi::sqlite3_result_double(ctx, r) },
 | 
			
		||||
        ValueRef::Text(s) => unsafe {
 | 
			
		||||
            let length = s.len();
 | 
			
		||||
            if length > ::std::i32::MAX as usize {
 | 
			
		||||
                ffi::sqlite3_result_error_toobig(ctx);
 | 
			
		||||
            } else {
 | 
			
		||||
                let c_str = match str_to_cstring(s) {
 | 
			
		||||
                    Ok(c_str) => c_str,
 | 
			
		||||
                    // TODO sqlite3_result_error
 | 
			
		||||
                    Err(_) => return ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE),
 | 
			
		||||
                };
 | 
			
		||||
                let destructor = if length > 0 {
 | 
			
		||||
                    ffi::SQLITE_TRANSIENT()
 | 
			
		||||
                } else {
 | 
			
		||||
                    ffi::SQLITE_STATIC()
 | 
			
		||||
                };
 | 
			
		||||
                ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, destructor);
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        ValueRef::Blob(b) => unsafe {
 | 
			
		||||
            let length = b.len();
 | 
			
		||||
            if length > ::std::i32::MAX as usize {
 | 
			
		||||
                ffi::sqlite3_result_error_toobig(ctx);
 | 
			
		||||
            } else if length == 0 {
 | 
			
		||||
                ffi::sqlite3_result_zeroblob(ctx, 0)
 | 
			
		||||
            } else {
 | 
			
		||||
                ffi::sqlite3_result_blob(ctx,
 | 
			
		||||
                                         b.as_ptr() as *const c_void,
 | 
			
		||||
                                         length as c_int,
 | 
			
		||||
                                         ffi::SQLITE_TRANSIENT());
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
use {str_to_cstring, Connection, Error, InnerConnection, Result};
 | 
			
		||||
 | 
			
		||||
unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
 | 
			
		||||
    // Extended constraint error codes were added in SQLite 3.7.16. We don't have an explicit
 | 
			
		||||
@@ -142,47 +93,8 @@ unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> ValueRef<'a> {
 | 
			
		||||
    unsafe fn from_value(value: *mut sqlite3_value) -> ValueRef<'a> {
 | 
			
		||||
        use std::slice::from_raw_parts;
 | 
			
		||||
 | 
			
		||||
        match ffi::sqlite3_value_type(value) {
 | 
			
		||||
            ffi::SQLITE_NULL => ValueRef::Null,
 | 
			
		||||
            ffi::SQLITE_INTEGER => ValueRef::Integer(ffi::sqlite3_value_int64(value)),
 | 
			
		||||
            ffi::SQLITE_FLOAT => ValueRef::Real(ffi::sqlite3_value_double(value)),
 | 
			
		||||
            ffi::SQLITE_TEXT => {
 | 
			
		||||
                let text = ffi::sqlite3_value_text(value);
 | 
			
		||||
                assert!(!text.is_null(),
 | 
			
		||||
                        "unexpected SQLITE_TEXT value type with NULL data");
 | 
			
		||||
                let s = CStr::from_ptr(text as *const c_char);
 | 
			
		||||
 | 
			
		||||
                // sqlite3_value_text returns UTF8 data, so our unwrap here should be fine.
 | 
			
		||||
                let s = s.to_str()
 | 
			
		||||
                    .expect("sqlite3_value_text returned invalid UTF-8");
 | 
			
		||||
                ValueRef::Text(s)
 | 
			
		||||
            }
 | 
			
		||||
            ffi::SQLITE_BLOB => {
 | 
			
		||||
                let (blob, len) = (ffi::sqlite3_value_blob(value), ffi::sqlite3_value_bytes(value));
 | 
			
		||||
 | 
			
		||||
                assert!(len >= 0,
 | 
			
		||||
                        "unexpected negative return from sqlite3_value_bytes");
 | 
			
		||||
                if len > 0 {
 | 
			
		||||
                    assert!(!blob.is_null(),
 | 
			
		||||
                            "unexpected SQLITE_BLOB value type with NULL data");
 | 
			
		||||
                    ValueRef::Blob(from_raw_parts(blob as *const u8, len as usize))
 | 
			
		||||
                } else {
 | 
			
		||||
                    // The return value from sqlite3_value_blob() for a zero-length BLOB
 | 
			
		||||
                    // is a NULL pointer.
 | 
			
		||||
                    ValueRef::Blob(&[])
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => unreachable!("sqlite3_value_type returned invalid value"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
 | 
			
		||||
    let _: Box<T> = Box::from_raw(p as *mut T);
 | 
			
		||||
    drop(Box::from_raw(p as *mut T));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Context is a wrapper for the SQLite function evaluation context.
 | 
			
		||||
@@ -212,17 +124,14 @@ impl<'a> Context<'a> {
 | 
			
		||||
        let arg = self.args[idx];
 | 
			
		||||
        let value = unsafe { ValueRef::from_value(arg) };
 | 
			
		||||
        FromSql::column_result(value).map_err(|err| match err {
 | 
			
		||||
                                                  FromSqlError::InvalidType => {
 | 
			
		||||
            FromSqlError::InvalidType => {
 | 
			
		||||
                Error::InvalidFunctionParameterType(idx, value.data_type())
 | 
			
		||||
            }
 | 
			
		||||
                                                  FromSqlError::OutOfRange(i) => {
 | 
			
		||||
                                                      Error::IntegralValueOutOfRange(idx as c_int,
 | 
			
		||||
                                                                                     i)
 | 
			
		||||
                                                  }
 | 
			
		||||
                                                  FromSqlError::Other(err) => {
 | 
			
		||||
            FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
 | 
			
		||||
            FromSqlError::Other(err) => {
 | 
			
		||||
                Error::FromSqlConversionFailure(idx, value.data_type(), err)
 | 
			
		||||
            }
 | 
			
		||||
                                              })
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sets the auxilliary data associated with a particular parameter. See
 | 
			
		||||
@@ -231,10 +140,12 @@ impl<'a> Context<'a> {
 | 
			
		||||
    pub fn set_aux<T>(&self, arg: c_int, value: T) {
 | 
			
		||||
        let boxed = Box::into_raw(Box::new(value));
 | 
			
		||||
        unsafe {
 | 
			
		||||
            ffi::sqlite3_set_auxdata(self.ctx,
 | 
			
		||||
                                     arg,
 | 
			
		||||
                                     boxed as *mut c_void,
 | 
			
		||||
                                     Some(free_boxed_value::<T>))
 | 
			
		||||
            ffi::sqlite3_set_auxdata(
 | 
			
		||||
                self.ctx,
 | 
			
		||||
                arg,
 | 
			
		||||
                boxed as *mut c_void,
 | 
			
		||||
                Some(free_boxed_value::<T>),
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -248,7 +159,11 @@ impl<'a> Context<'a> {
 | 
			
		||||
    /// types must be identical.
 | 
			
		||||
    pub unsafe fn get_aux<T>(&self, arg: c_int) -> Option<&T> {
 | 
			
		||||
        let p = ffi::sqlite3_get_auxdata(self.ctx, arg) as *mut T;
 | 
			
		||||
        if p.is_null() { None } else { Some(&*p) }
 | 
			
		||||
        if p.is_null() {
 | 
			
		||||
            None
 | 
			
		||||
        } else {
 | 
			
		||||
            Some(&*p)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -257,11 +172,12 @@ impl<'a> Context<'a> {
 | 
			
		||||
/// `A` is the type of the aggregation context and `T` is the type of the final result.
 | 
			
		||||
/// Implementations should be stateless.
 | 
			
		||||
pub trait Aggregate<A, T>
 | 
			
		||||
    where T: ToSql
 | 
			
		||||
where
 | 
			
		||||
    T: ToSql,
 | 
			
		||||
{
 | 
			
		||||
    /// Initializes the aggregation context. Will be called prior to the first call
 | 
			
		||||
    /// to `step()` to set up the context for an invocation of the function. (Note:
 | 
			
		||||
    /// `init()` will not be called if the there are no rows.)
 | 
			
		||||
    /// `init()` will not be called if there are no rows.)
 | 
			
		||||
    fn init(&self) -> A;
 | 
			
		||||
 | 
			
		||||
    /// "step" function called once for each row in an aggregate group. May be called
 | 
			
		||||
@@ -306,14 +222,16 @@ impl Connection {
 | 
			
		||||
    /// # Failure
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return Err if the function could not be attached to the connection.
 | 
			
		||||
    pub fn create_scalar_function<F, T>(&self,
 | 
			
		||||
                                        fn_name: &str,
 | 
			
		||||
                                        n_arg: c_int,
 | 
			
		||||
                                        deterministic: bool,
 | 
			
		||||
                                        x_func: F)
 | 
			
		||||
                                        -> Result<()>
 | 
			
		||||
        where F: FnMut(&Context) -> Result<T>,
 | 
			
		||||
              T: ToSql
 | 
			
		||||
    pub fn create_scalar_function<F, T>(
 | 
			
		||||
        &self,
 | 
			
		||||
        fn_name: &str,
 | 
			
		||||
        n_arg: c_int,
 | 
			
		||||
        deterministic: bool,
 | 
			
		||||
        x_func: F,
 | 
			
		||||
    ) -> Result<()>
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut(&Context) -> Result<T> + Send + 'static,
 | 
			
		||||
        T: ToSql,
 | 
			
		||||
    {
 | 
			
		||||
        self.db
 | 
			
		||||
            .borrow_mut()
 | 
			
		||||
@@ -325,14 +243,16 @@ impl Connection {
 | 
			
		||||
    /// # Failure
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return Err if the function could not be attached to the connection.
 | 
			
		||||
    pub fn create_aggregate_function<A, D, T>(&self,
 | 
			
		||||
                                              fn_name: &str,
 | 
			
		||||
                                              n_arg: c_int,
 | 
			
		||||
                                              deterministic: bool,
 | 
			
		||||
                                              aggr: D)
 | 
			
		||||
                                              -> Result<()>
 | 
			
		||||
        where D: Aggregate<A, T>,
 | 
			
		||||
              T: ToSql
 | 
			
		||||
    pub fn create_aggregate_function<A, D, T>(
 | 
			
		||||
        &self,
 | 
			
		||||
        fn_name: &str,
 | 
			
		||||
        n_arg: c_int,
 | 
			
		||||
        deterministic: bool,
 | 
			
		||||
        aggr: D,
 | 
			
		||||
    ) -> Result<()>
 | 
			
		||||
    where
 | 
			
		||||
        D: Aggregate<A, T>,
 | 
			
		||||
        T: ToSql,
 | 
			
		||||
    {
 | 
			
		||||
        self.db
 | 
			
		||||
            .borrow_mut()
 | 
			
		||||
@@ -353,20 +273,24 @@ impl Connection {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl InnerConnection {
 | 
			
		||||
    fn create_scalar_function<F, T>(&mut self,
 | 
			
		||||
                                    fn_name: &str,
 | 
			
		||||
                                    n_arg: c_int,
 | 
			
		||||
                                    deterministic: bool,
 | 
			
		||||
                                    x_func: F)
 | 
			
		||||
                                    -> Result<()>
 | 
			
		||||
        where F: FnMut(&Context) -> Result<T>,
 | 
			
		||||
              T: ToSql
 | 
			
		||||
    fn create_scalar_function<F, T>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        fn_name: &str,
 | 
			
		||||
        n_arg: c_int,
 | 
			
		||||
        deterministic: bool,
 | 
			
		||||
        x_func: F,
 | 
			
		||||
    ) -> Result<()>
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut(&Context) -> Result<T> + Send + 'static,
 | 
			
		||||
        T: ToSql,
 | 
			
		||||
    {
 | 
			
		||||
        unsafe extern "C" fn call_boxed_closure<F, T>(ctx: *mut sqlite3_context,
 | 
			
		||||
                                                      argc: c_int,
 | 
			
		||||
                                                      argv: *mut *mut sqlite3_value)
 | 
			
		||||
            where F: FnMut(&Context) -> Result<T>,
 | 
			
		||||
                  T: ToSql
 | 
			
		||||
        unsafe extern "C" fn call_boxed_closure<F, T>(
 | 
			
		||||
            ctx: *mut sqlite3_context,
 | 
			
		||||
            argc: c_int,
 | 
			
		||||
            argv: *mut *mut sqlite3_value,
 | 
			
		||||
        ) where
 | 
			
		||||
            F: FnMut(&Context) -> Result<T>,
 | 
			
		||||
            T: ToSql,
 | 
			
		||||
        {
 | 
			
		||||
            let ctx = Context {
 | 
			
		||||
                ctx,
 | 
			
		||||
@@ -392,31 +316,36 @@ impl InnerConnection {
 | 
			
		||||
            flags |= ffi::SQLITE_DETERMINISTIC;
 | 
			
		||||
        }
 | 
			
		||||
        let r = unsafe {
 | 
			
		||||
            ffi::sqlite3_create_function_v2(self.db(),
 | 
			
		||||
                                            c_name.as_ptr(),
 | 
			
		||||
                                            n_arg,
 | 
			
		||||
                                            flags,
 | 
			
		||||
                                            boxed_f as *mut c_void,
 | 
			
		||||
                                            Some(call_boxed_closure::<F, T>),
 | 
			
		||||
                                            None,
 | 
			
		||||
                                            None,
 | 
			
		||||
                                            Some(free_boxed_value::<F>))
 | 
			
		||||
            ffi::sqlite3_create_function_v2(
 | 
			
		||||
                self.db(),
 | 
			
		||||
                c_name.as_ptr(),
 | 
			
		||||
                n_arg,
 | 
			
		||||
                flags,
 | 
			
		||||
                boxed_f as *mut c_void,
 | 
			
		||||
                Some(call_boxed_closure::<F, T>),
 | 
			
		||||
                None,
 | 
			
		||||
                None,
 | 
			
		||||
                Some(free_boxed_value::<F>),
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
        self.decode_result(r)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn create_aggregate_function<A, D, T>(&mut self,
 | 
			
		||||
                                          fn_name: &str,
 | 
			
		||||
                                          n_arg: c_int,
 | 
			
		||||
                                          deterministic: bool,
 | 
			
		||||
                                          aggr: D)
 | 
			
		||||
                                          -> Result<()>
 | 
			
		||||
        where D: Aggregate<A, T>,
 | 
			
		||||
              T: ToSql
 | 
			
		||||
    fn create_aggregate_function<A, D, T>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        fn_name: &str,
 | 
			
		||||
        n_arg: c_int,
 | 
			
		||||
        deterministic: bool,
 | 
			
		||||
        aggr: D,
 | 
			
		||||
    ) -> Result<()>
 | 
			
		||||
    where
 | 
			
		||||
        D: Aggregate<A, T>,
 | 
			
		||||
        T: ToSql,
 | 
			
		||||
    {
 | 
			
		||||
        unsafe fn aggregate_context<A>(ctx: *mut sqlite3_context,
 | 
			
		||||
                                       bytes: usize)
 | 
			
		||||
                                       -> Option<*mut *mut A> {
 | 
			
		||||
        unsafe fn aggregate_context<A>(
 | 
			
		||||
            ctx: *mut sqlite3_context,
 | 
			
		||||
            bytes: usize,
 | 
			
		||||
        ) -> Option<*mut *mut A> {
 | 
			
		||||
            let pac = ffi::sqlite3_aggregate_context(ctx, bytes as c_int) as *mut *mut A;
 | 
			
		||||
            if pac.is_null() {
 | 
			
		||||
                return None;
 | 
			
		||||
@@ -424,15 +353,19 @@ impl InnerConnection {
 | 
			
		||||
            Some(pac)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        unsafe extern "C" fn call_boxed_step<A, D, T>(ctx: *mut sqlite3_context,
 | 
			
		||||
                                                      argc: c_int,
 | 
			
		||||
                                                      argv: *mut *mut sqlite3_value)
 | 
			
		||||
            where D: Aggregate<A, T>,
 | 
			
		||||
                  T: ToSql
 | 
			
		||||
        unsafe extern "C" fn call_boxed_step<A, D, T>(
 | 
			
		||||
            ctx: *mut sqlite3_context,
 | 
			
		||||
            argc: c_int,
 | 
			
		||||
            argv: *mut *mut sqlite3_value,
 | 
			
		||||
        ) where
 | 
			
		||||
            D: Aggregate<A, T>,
 | 
			
		||||
            T: ToSql,
 | 
			
		||||
        {
 | 
			
		||||
            let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D;
 | 
			
		||||
            assert!(!boxed_aggr.is_null(),
 | 
			
		||||
                    "Internal error - null aggregate pointer");
 | 
			
		||||
            assert!(
 | 
			
		||||
                !boxed_aggr.is_null(),
 | 
			
		||||
                "Internal error - null aggregate pointer"
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            let pac = match aggregate_context(ctx, ::std::mem::size_of::<*mut A>()) {
 | 
			
		||||
                Some(pac) => pac,
 | 
			
		||||
@@ -458,12 +391,15 @@ impl InnerConnection {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        unsafe extern "C" fn call_boxed_final<A, D, T>(ctx: *mut sqlite3_context)
 | 
			
		||||
            where D: Aggregate<A, T>,
 | 
			
		||||
                  T: ToSql
 | 
			
		||||
        where
 | 
			
		||||
            D: Aggregate<A, T>,
 | 
			
		||||
            T: ToSql,
 | 
			
		||||
        {
 | 
			
		||||
            let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D;
 | 
			
		||||
            assert!(!boxed_aggr.is_null(),
 | 
			
		||||
                    "Internal error - null aggregate pointer");
 | 
			
		||||
            assert!(
 | 
			
		||||
                !boxed_aggr.is_null(),
 | 
			
		||||
                "Internal error - null aggregate pointer"
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            // Within the xFinal callback, it is customary to set N=0 in calls to
 | 
			
		||||
            // sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur.
 | 
			
		||||
@@ -495,15 +431,17 @@ impl InnerConnection {
 | 
			
		||||
            flags |= ffi::SQLITE_DETERMINISTIC;
 | 
			
		||||
        }
 | 
			
		||||
        let r = unsafe {
 | 
			
		||||
            ffi::sqlite3_create_function_v2(self.db(),
 | 
			
		||||
                                            c_name.as_ptr(),
 | 
			
		||||
                                            n_arg,
 | 
			
		||||
                                            flags,
 | 
			
		||||
                                            boxed_aggr as *mut c_void,
 | 
			
		||||
                                            None,
 | 
			
		||||
                                            Some(call_boxed_step::<A, D, T>),
 | 
			
		||||
                                            Some(call_boxed_final::<A, D, T>),
 | 
			
		||||
                                            Some(free_boxed_value::<D>))
 | 
			
		||||
            ffi::sqlite3_create_function_v2(
 | 
			
		||||
                self.db(),
 | 
			
		||||
                c_name.as_ptr(),
 | 
			
		||||
                n_arg,
 | 
			
		||||
                flags,
 | 
			
		||||
                boxed_aggr as *mut c_void,
 | 
			
		||||
                None,
 | 
			
		||||
                Some(call_boxed_step::<A, D, T>),
 | 
			
		||||
                Some(call_boxed_final::<A, D, T>),
 | 
			
		||||
                Some(free_boxed_value::<D>),
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
        self.decode_result(r)
 | 
			
		||||
    }
 | 
			
		||||
@@ -511,15 +449,17 @@ impl InnerConnection {
 | 
			
		||||
    fn remove_function(&mut self, fn_name: &str, n_arg: c_int) -> Result<()> {
 | 
			
		||||
        let c_name = try!(str_to_cstring(fn_name));
 | 
			
		||||
        let r = unsafe {
 | 
			
		||||
            ffi::sqlite3_create_function_v2(self.db(),
 | 
			
		||||
                                            c_name.as_ptr(),
 | 
			
		||||
                                            n_arg,
 | 
			
		||||
                                            ffi::SQLITE_UTF8,
 | 
			
		||||
                                            ptr::null_mut(),
 | 
			
		||||
                                            None,
 | 
			
		||||
                                            None,
 | 
			
		||||
                                            None,
 | 
			
		||||
                                            None)
 | 
			
		||||
            ffi::sqlite3_create_function_v2(
 | 
			
		||||
                self.db(),
 | 
			
		||||
                c_name.as_ptr(),
 | 
			
		||||
                n_arg,
 | 
			
		||||
                ffi::SQLITE_UTF8,
 | 
			
		||||
                ptr::null_mut(),
 | 
			
		||||
                None,
 | 
			
		||||
                None,
 | 
			
		||||
                None,
 | 
			
		||||
                None,
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
        self.decode_result(r)
 | 
			
		||||
    }
 | 
			
		||||
@@ -529,13 +469,13 @@ impl InnerConnection {
 | 
			
		||||
mod test {
 | 
			
		||||
    extern crate regex;
 | 
			
		||||
 | 
			
		||||
    use std::collections::HashMap;
 | 
			
		||||
    use std::os::raw::c_double;
 | 
			
		||||
    use self::regex::Regex;
 | 
			
		||||
    use std::collections::HashMap;
 | 
			
		||||
    use std::f64::EPSILON;
 | 
			
		||||
    use std::os::raw::c_double;
 | 
			
		||||
 | 
			
		||||
    use {Connection, Error, Result};
 | 
			
		||||
    use functions::{Aggregate, Context};
 | 
			
		||||
    use {Connection, Error, Result};
 | 
			
		||||
 | 
			
		||||
    fn half(ctx: &Context) -> Result<c_double> {
 | 
			
		||||
        assert!(ctx.len() == 1, "called with unexpected number of arguments");
 | 
			
		||||
@@ -597,41 +537,44 @@ mod test {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
    fn test_function_regexp_with_auxilliary() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
        db.execute_batch("BEGIN;
 | 
			
		||||
                         CREATE TABLE foo (x string);
 | 
			
		||||
                         INSERT INTO foo VALUES ('lisa');
 | 
			
		||||
                         INSERT INTO foo VALUES ('lXsi');
 | 
			
		||||
                         INSERT INTO foo VALUES ('lisX');
 | 
			
		||||
                         END;").unwrap();
 | 
			
		||||
        db.create_scalar_function("regexp", 2, true, regexp_with_auxilliary).unwrap();
 | 
			
		||||
        db.execute_batch(
 | 
			
		||||
            "BEGIN;
 | 
			
		||||
             CREATE TABLE foo (x string);
 | 
			
		||||
             INSERT INTO foo VALUES ('lisa');
 | 
			
		||||
             INSERT INTO foo VALUES ('lXsi');
 | 
			
		||||
             INSERT INTO foo VALUES ('lisX');
 | 
			
		||||
             END;",
 | 
			
		||||
        ).unwrap();
 | 
			
		||||
        db.create_scalar_function("regexp", 2, true, regexp_with_auxilliary)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let result: Result<bool> = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')",
 | 
			
		||||
                                  &[],
 | 
			
		||||
                                  |r| r.get(0));
 | 
			
		||||
        let result: Result<bool> =
 | 
			
		||||
            db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", &[], |r| r.get(0));
 | 
			
		||||
 | 
			
		||||
        assert_eq!(true, result.unwrap());
 | 
			
		||||
 | 
			
		||||
        let result: Result<i64> =
 | 
			
		||||
            db.query_row("SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
 | 
			
		||||
                         &[],
 | 
			
		||||
                         |r| r.get(0));
 | 
			
		||||
        let result: Result<i64> = db.query_row(
 | 
			
		||||
            "SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
 | 
			
		||||
            &[],
 | 
			
		||||
            |r| r.get(0),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert_eq!(2, result.unwrap());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
    fn test_function_regexp_with_hashmap_cache() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
        db.execute_batch("BEGIN;
 | 
			
		||||
                         CREATE TABLE foo (x string);
 | 
			
		||||
                         INSERT INTO foo VALUES ('lisa');
 | 
			
		||||
                         INSERT INTO foo VALUES ('lXsi');
 | 
			
		||||
                         INSERT INTO foo VALUES ('lisX');
 | 
			
		||||
                         END;").unwrap();
 | 
			
		||||
        db.execute_batch(
 | 
			
		||||
            "BEGIN;
 | 
			
		||||
             CREATE TABLE foo (x string);
 | 
			
		||||
             INSERT INTO foo VALUES ('lisa');
 | 
			
		||||
             INSERT INTO foo VALUES ('lXsi');
 | 
			
		||||
             INSERT INTO foo VALUES ('lisX');
 | 
			
		||||
             END;",
 | 
			
		||||
        ).unwrap();
 | 
			
		||||
 | 
			
		||||
        // This implementation of a regexp scalar function uses a captured HashMap
 | 
			
		||||
        // to keep cached regular expressions around (even across multiple queries)
 | 
			
		||||
@@ -646,12 +589,10 @@ mod test {
 | 
			
		||||
                use std::collections::hash_map::Entry::{Occupied, Vacant};
 | 
			
		||||
                match entry {
 | 
			
		||||
                    Occupied(occ) => occ.into_mut(),
 | 
			
		||||
                    Vacant(vac) => {
 | 
			
		||||
                        match Regex::new(®ex_s) {
 | 
			
		||||
                            Ok(r) => vac.insert(r),
 | 
			
		||||
                            Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    Vacant(vac) => match Regex::new(®ex_s) {
 | 
			
		||||
                        Ok(r) => vac.insert(r),
 | 
			
		||||
                        Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
 | 
			
		||||
                    },
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
@@ -659,16 +600,16 @@ mod test {
 | 
			
		||||
            Ok(regex.is_match(&text))
 | 
			
		||||
        }).unwrap();
 | 
			
		||||
 | 
			
		||||
        let result: Result<bool> = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')",
 | 
			
		||||
                                  &[],
 | 
			
		||||
                                  |r| r.get(0));
 | 
			
		||||
        let result: Result<bool> =
 | 
			
		||||
            db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", &[], |r| r.get(0));
 | 
			
		||||
 | 
			
		||||
        assert_eq!(true, result.unwrap());
 | 
			
		||||
 | 
			
		||||
        let result: Result<i64> =
 | 
			
		||||
            db.query_row("SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
 | 
			
		||||
                         &[],
 | 
			
		||||
                         |r| r.get(0));
 | 
			
		||||
        let result: Result<i64> = db.query_row(
 | 
			
		||||
            "SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
 | 
			
		||||
            &[],
 | 
			
		||||
            |r| r.get(0),
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert_eq!(2, result.unwrap());
 | 
			
		||||
    }
 | 
			
		||||
@@ -677,21 +618,21 @@ mod test {
 | 
			
		||||
    fn test_varargs_function() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
        db.create_scalar_function("my_concat", -1, true, |ctx| {
 | 
			
		||||
                let mut ret = String::new();
 | 
			
		||||
            let mut ret = String::new();
 | 
			
		||||
 | 
			
		||||
                for idx in 0..ctx.len() {
 | 
			
		||||
                    let s = try!(ctx.get::<String>(idx));
 | 
			
		||||
                    ret.push_str(&s);
 | 
			
		||||
                }
 | 
			
		||||
            for idx in 0..ctx.len() {
 | 
			
		||||
                let s = try!(ctx.get::<String>(idx));
 | 
			
		||||
                ret.push_str(&s);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                Ok(ret)
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap();
 | 
			
		||||
            Ok(ret)
 | 
			
		||||
        }).unwrap();
 | 
			
		||||
 | 
			
		||||
        for &(expected, query) in
 | 
			
		||||
            &[("", "SELECT my_concat()"),
 | 
			
		||||
              ("onetwo", "SELECT my_concat('one', 'two')"),
 | 
			
		||||
              ("abc", "SELECT my_concat('a', 'b', 'c')")] {
 | 
			
		||||
        for &(expected, query) in &[
 | 
			
		||||
            ("", "SELECT my_concat()"),
 | 
			
		||||
            ("onetwo", "SELECT my_concat('one', 'two')"),
 | 
			
		||||
            ("abc", "SELECT my_concat('a', 'b', 'c')"),
 | 
			
		||||
        ] {
 | 
			
		||||
            let result: String = db.query_row(query, &[], |r| r.get(0)).unwrap();
 | 
			
		||||
            assert_eq!(expected, result);
 | 
			
		||||
        }
 | 
			
		||||
@@ -747,7 +688,8 @@ mod test {
 | 
			
		||||
 | 
			
		||||
        let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \
 | 
			
		||||
                        2, 1)";
 | 
			
		||||
        let result: (i64, i64) = db.query_row(dual_sum, &[], |r| (r.get(0), r.get(1)))
 | 
			
		||||
        let result: (i64, i64) = db
 | 
			
		||||
            .query_row(dual_sum, &[], |r| (r.get(0), r.get(1)))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!((4, 2), result);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										263
									
								
								src/hooks.rs
									
									
									
									
									
								
							
							
						
						
									
										263
									
								
								src/hooks.rs
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
			
		||||
//! Commit, Data Change and Rollback Notification Callbacks
 | 
			
		||||
#![allow(non_camel_case_types)]
 | 
			
		||||
 | 
			
		||||
use std::os::raw::{c_char, c_int, c_void};
 | 
			
		||||
use std::ptr;
 | 
			
		||||
use std::os::raw::{c_int, c_char, c_void};
 | 
			
		||||
 | 
			
		||||
use ffi;
 | 
			
		||||
 | 
			
		||||
@@ -94,8 +94,9 @@ impl Connection {
 | 
			
		||||
    /// Register a callback function to be invoked whenever a transaction is committed.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The callback returns `true` to rollback.
 | 
			
		||||
    pub fn commit_hook<F>(&self, hook: F)
 | 
			
		||||
        where F: FnMut() -> bool
 | 
			
		||||
    pub fn commit_hook<F>(&self, hook: Option<F>)
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut() -> bool + Send + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        self.db.borrow_mut().commit_hook(hook);
 | 
			
		||||
    }
 | 
			
		||||
@@ -103,8 +104,9 @@ impl Connection {
 | 
			
		||||
    /// Register a callback function to be invoked whenever a transaction is committed.
 | 
			
		||||
    ///
 | 
			
		||||
    /// The callback returns `true` to rollback.
 | 
			
		||||
    pub fn rollback_hook<F>(&self, hook: F)
 | 
			
		||||
        where F: FnMut()
 | 
			
		||||
    pub fn rollback_hook<F>(&self, hook: Option<F>)
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut() + Send + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        self.db.borrow_mut().rollback_hook(hook);
 | 
			
		||||
    }
 | 
			
		||||
@@ -118,99 +120,122 @@ impl Connection {
 | 
			
		||||
    ///   - the name of the database ("main", "temp", ...),
 | 
			
		||||
    ///   - the name of the table that is updated,
 | 
			
		||||
    ///   - the ROWID of the row that is updated.
 | 
			
		||||
    pub fn update_hook<F>(&self, hook: F)
 | 
			
		||||
        where F: FnMut(Action, &str, &str, i64)
 | 
			
		||||
    pub fn update_hook<F>(&self, hook: Option<F>)
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut(Action, &str, &str, i64) + Send + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        self.db.borrow_mut().update_hook(hook);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Remove hook installed by `update_hook`.
 | 
			
		||||
    pub fn remove_update_hook(&self) {
 | 
			
		||||
        self.db.borrow_mut().remove_update_hook();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Remove hook installed by `commit_hook`.
 | 
			
		||||
    pub fn remove_commit_hook(&self) {
 | 
			
		||||
        self.db.borrow_mut().remove_commit_hook();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Remove hook installed by `rollback_hook`.
 | 
			
		||||
    pub fn remove_rollback_hook(&self) {
 | 
			
		||||
        self.db.borrow_mut().remove_rollback_hook();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl InnerConnection {
 | 
			
		||||
    pub fn remove_hooks(&mut self) {
 | 
			
		||||
        self.remove_update_hook();
 | 
			
		||||
        self.remove_commit_hook();
 | 
			
		||||
        self.remove_rollback_hook();
 | 
			
		||||
        self.update_hook(None::<fn(Action, &str, &str, i64)>);
 | 
			
		||||
        self.commit_hook(None::<fn() -> bool>);
 | 
			
		||||
        self.rollback_hook(None::<fn()>);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn commit_hook<F>(&self, hook: F)
 | 
			
		||||
        where F: FnMut() -> bool
 | 
			
		||||
    fn commit_hook<F>(&mut self, hook: Option<F>)
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut() -> bool + Send + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void) -> c_int
 | 
			
		||||
            where F: FnMut() -> bool
 | 
			
		||||
        where
 | 
			
		||||
            F: FnMut() -> bool,
 | 
			
		||||
        {
 | 
			
		||||
            let boxed_hook: *mut F = p_arg as *mut F;
 | 
			
		||||
            assert!(!boxed_hook.is_null(),
 | 
			
		||||
                    "Internal error - null function pointer");
 | 
			
		||||
 | 
			
		||||
            if (*boxed_hook)() { 1 } else { 0 }
 | 
			
		||||
            if (*boxed_hook)() {
 | 
			
		||||
                1
 | 
			
		||||
            } else {
 | 
			
		||||
                0
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let previous_hook = {
 | 
			
		||||
            let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
 | 
			
		||||
            unsafe {
 | 
			
		||||
                ffi::sqlite3_commit_hook(self.db(),
 | 
			
		||||
                                         Some(call_boxed_closure::<F>),
 | 
			
		||||
                                         boxed_hook as *mut _)
 | 
			
		||||
            }
 | 
			
		||||
        // unlike `sqlite3_create_function_v2`, we cannot specify a `xDestroy` with `sqlite3_commit_hook`.
 | 
			
		||||
        // so we keep the `xDestroy` function in `InnerConnection.free_boxed_hook`.
 | 
			
		||||
        let free_commit_hook = if hook.is_some() {
 | 
			
		||||
            Some(free_boxed_hook::<F> as fn(*mut c_void))
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        };
 | 
			
		||||
        free_boxed_hook(previous_hook);
 | 
			
		||||
 | 
			
		||||
        let previous_hook = match hook {
 | 
			
		||||
            Some(hook) => {
 | 
			
		||||
                let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    ffi::sqlite3_commit_hook(
 | 
			
		||||
                        self.db(),
 | 
			
		||||
                        Some(call_boxed_closure::<F>),
 | 
			
		||||
                        boxed_hook as *mut _,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => unsafe { ffi::sqlite3_commit_hook(self.db(), None, ptr::null_mut()) },
 | 
			
		||||
        };
 | 
			
		||||
        if !previous_hook.is_null() {
 | 
			
		||||
            if let Some(free_boxed_hook) = self.free_commit_hook {
 | 
			
		||||
                free_boxed_hook(previous_hook);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        self.free_commit_hook = free_commit_hook;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn rollback_hook<F>(&self, hook: F)
 | 
			
		||||
        where F: FnMut()
 | 
			
		||||
    fn rollback_hook<F>(&mut self, hook: Option<F>)
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut() + Send + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void)
 | 
			
		||||
            where F: FnMut()
 | 
			
		||||
        where
 | 
			
		||||
            F: FnMut(),
 | 
			
		||||
        {
 | 
			
		||||
            let boxed_hook: *mut F = p_arg as *mut F;
 | 
			
		||||
            assert!(!boxed_hook.is_null(),
 | 
			
		||||
                    "Internal error - null function pointer");
 | 
			
		||||
 | 
			
		||||
            (*boxed_hook)();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let previous_hook = {
 | 
			
		||||
            let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
 | 
			
		||||
            unsafe {
 | 
			
		||||
                ffi::sqlite3_rollback_hook(self.db(),
 | 
			
		||||
                                           Some(call_boxed_closure::<F>),
 | 
			
		||||
                                           boxed_hook as *mut _)
 | 
			
		||||
            }
 | 
			
		||||
        let free_rollback_hook = if hook.is_some() {
 | 
			
		||||
            Some(free_boxed_hook::<F> as fn(*mut c_void))
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        };
 | 
			
		||||
        free_boxed_hook(previous_hook);
 | 
			
		||||
 | 
			
		||||
        let previous_hook = match hook {
 | 
			
		||||
            Some(hook) => {
 | 
			
		||||
                let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    ffi::sqlite3_rollback_hook(
 | 
			
		||||
                        self.db(),
 | 
			
		||||
                        Some(call_boxed_closure::<F>),
 | 
			
		||||
                        boxed_hook as *mut _,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => unsafe { ffi::sqlite3_rollback_hook(self.db(), None, ptr::null_mut()) },
 | 
			
		||||
        };
 | 
			
		||||
        if !previous_hook.is_null() {
 | 
			
		||||
            if let Some(free_boxed_hook) = self.free_rollback_hook {
 | 
			
		||||
                free_boxed_hook(previous_hook);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        self.free_rollback_hook = free_rollback_hook;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn update_hook<F>(&mut self, hook: F)
 | 
			
		||||
        where F: FnMut(Action, &str, &str, i64)
 | 
			
		||||
    fn update_hook<F>(&mut self, hook: Option<F>)
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut(Action, &str, &str, i64) + Send + 'static,
 | 
			
		||||
    {
 | 
			
		||||
        unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void,
 | 
			
		||||
                                                   action_code: c_int,
 | 
			
		||||
                                                   db_str: *const c_char,
 | 
			
		||||
                                                   tbl_str: *const c_char,
 | 
			
		||||
                                                   row_id: i64)
 | 
			
		||||
            where F: FnMut(Action, &str, &str, i64)
 | 
			
		||||
        unsafe extern "C" fn call_boxed_closure<F>(
 | 
			
		||||
            p_arg: *mut c_void,
 | 
			
		||||
            action_code: c_int,
 | 
			
		||||
            db_str: *const c_char,
 | 
			
		||||
            tbl_str: *const c_char,
 | 
			
		||||
            row_id: i64,
 | 
			
		||||
        ) where
 | 
			
		||||
            F: FnMut(Action, &str, &str, i64),
 | 
			
		||||
        {
 | 
			
		||||
            use std::ffi::CStr;
 | 
			
		||||
            use std::str;
 | 
			
		||||
 | 
			
		||||
            let boxed_hook: *mut F = p_arg as *mut F;
 | 
			
		||||
            assert!(!boxed_hook.is_null(),
 | 
			
		||||
                    "Internal error - null function pointer");
 | 
			
		||||
 | 
			
		||||
            let action = Action::from(action_code);
 | 
			
		||||
            let db_name = {
 | 
			
		||||
@@ -225,84 +250,104 @@ impl InnerConnection {
 | 
			
		||||
            (*boxed_hook)(action, db_name, tbl_name, row_id);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let previous_hook = {
 | 
			
		||||
            let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
 | 
			
		||||
            unsafe {
 | 
			
		||||
                ffi::sqlite3_update_hook(self.db(),
 | 
			
		||||
                                         Some(call_boxed_closure::<F>),
 | 
			
		||||
                                         boxed_hook as *mut _)
 | 
			
		||||
            }
 | 
			
		||||
        let free_update_hook = if hook.is_some() {
 | 
			
		||||
            Some(free_boxed_hook::<F> as fn(*mut c_void))
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        };
 | 
			
		||||
        free_boxed_hook(previous_hook);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn remove_update_hook(&mut self) {
 | 
			
		||||
        let previous_hook = unsafe { ffi::sqlite3_update_hook(self.db(), None, ptr::null_mut()) };
 | 
			
		||||
        free_boxed_hook(previous_hook);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn remove_commit_hook(&mut self) {
 | 
			
		||||
        let previous_hook = unsafe { ffi::sqlite3_commit_hook(self.db(), None, ptr::null_mut()) };
 | 
			
		||||
        free_boxed_hook(previous_hook);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn remove_rollback_hook(&mut self) {
 | 
			
		||||
        let previous_hook = unsafe { ffi::sqlite3_rollback_hook(self.db(), None, ptr::null_mut()) };
 | 
			
		||||
        free_boxed_hook(previous_hook);
 | 
			
		||||
        let previous_hook = match hook {
 | 
			
		||||
            Some(hook) => {
 | 
			
		||||
                let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    ffi::sqlite3_update_hook(
 | 
			
		||||
                        self.db(),
 | 
			
		||||
                        Some(call_boxed_closure::<F>),
 | 
			
		||||
                        boxed_hook as *mut _,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            _ => unsafe { ffi::sqlite3_update_hook(self.db(), None, ptr::null_mut()) },
 | 
			
		||||
        };
 | 
			
		||||
        if !previous_hook.is_null() {
 | 
			
		||||
            if let Some(free_boxed_hook) = self.free_update_hook {
 | 
			
		||||
                free_boxed_hook(previous_hook);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        self.free_update_hook = free_update_hook;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn free_boxed_hook(hook: *mut c_void) {
 | 
			
		||||
    if !hook.is_null() {
 | 
			
		||||
        // TODO make sure that size_of::<*mut F>() is always equal to size_of::<*mut c_void>()
 | 
			
		||||
        let _: Box<*mut c_void> = unsafe { Box::from_raw(hook as *mut _) };
 | 
			
		||||
    }
 | 
			
		||||
fn free_boxed_hook<F>(p: *mut c_void) {
 | 
			
		||||
    drop(unsafe { Box::from_raw(p as *mut F) });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::Action;
 | 
			
		||||
    use std::sync::atomic::{AtomicBool, Ordering};
 | 
			
		||||
    use Connection;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_commit_hook() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut called = false;
 | 
			
		||||
        db.commit_hook(|| {
 | 
			
		||||
                           called = true;
 | 
			
		||||
                           false
 | 
			
		||||
                       });
 | 
			
		||||
        lazy_static! {
 | 
			
		||||
            static ref called: AtomicBool = AtomicBool::new(false);
 | 
			
		||||
        }
 | 
			
		||||
        db.commit_hook(Some(|| {
 | 
			
		||||
            called.store(true, Ordering::Relaxed);
 | 
			
		||||
            false
 | 
			
		||||
        }));
 | 
			
		||||
        db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert!(called);
 | 
			
		||||
        assert!(called.load(Ordering::Relaxed));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_fn_commit_hook() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
 | 
			
		||||
        fn hook() -> bool {
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        db.commit_hook(Some(hook));
 | 
			
		||||
        db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;")
 | 
			
		||||
            .unwrap_err();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_rollback_hook() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut called = false;
 | 
			
		||||
        db.rollback_hook(|| { called = true; });
 | 
			
		||||
        lazy_static! {
 | 
			
		||||
            static ref called: AtomicBool = AtomicBool::new(false);
 | 
			
		||||
        }
 | 
			
		||||
        db.rollback_hook(Some(|| {
 | 
			
		||||
            called.store(true, Ordering::Relaxed);
 | 
			
		||||
        }));
 | 
			
		||||
        db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); ROLLBACK;")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert!(called);
 | 
			
		||||
        assert!(called.load(Ordering::Relaxed));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_update_hook() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut called = false;
 | 
			
		||||
        db.update_hook(|action, db, tbl, row_id| {
 | 
			
		||||
                           assert_eq!(Action::SQLITE_INSERT, action);
 | 
			
		||||
                           assert_eq!("main", db);
 | 
			
		||||
                           assert_eq!("foo", tbl);
 | 
			
		||||
                           assert_eq!(1, row_id);
 | 
			
		||||
                           called = true;
 | 
			
		||||
                       });
 | 
			
		||||
        lazy_static! {
 | 
			
		||||
            static ref called: AtomicBool = AtomicBool::new(false);
 | 
			
		||||
        }
 | 
			
		||||
        db.update_hook(Some(|action, db: &str, tbl: &str, row_id| {
 | 
			
		||||
            assert_eq!(Action::SQLITE_INSERT, action);
 | 
			
		||||
            assert_eq!("main", db);
 | 
			
		||||
            assert_eq!("foo", tbl);
 | 
			
		||||
            assert_eq!(1, row_id);
 | 
			
		||||
            called.store(true, Ordering::Relaxed);
 | 
			
		||||
        }));
 | 
			
		||||
        db.execute_batch("CREATE TABLE foo (t TEXT)").unwrap();
 | 
			
		||||
        db.execute_batch("INSERT INTO foo VALUES ('lisa')").unwrap();
 | 
			
		||||
        assert!(called);
 | 
			
		||||
        assert!(called.load(Ordering::Relaxed));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										483
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										483
									
								
								src/lib.rs
									
									
									
									
									
								
							@@ -56,76 +56,80 @@ extern crate libsqlite3_sys as ffi;
 | 
			
		||||
extern crate lru_cache;
 | 
			
		||||
#[macro_use]
 | 
			
		||||
extern crate bitflags;
 | 
			
		||||
#[cfg(all(test, feature = "trace"))]
 | 
			
		||||
#[cfg(any(test, feature = "vtab"))]
 | 
			
		||||
#[macro_use]
 | 
			
		||||
extern crate lazy_static;
 | 
			
		||||
 | 
			
		||||
use std::default::Default;
 | 
			
		||||
use std::convert;
 | 
			
		||||
use std::mem;
 | 
			
		||||
use std::ptr;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::path::{Path, PathBuf};
 | 
			
		||||
use std::cell::RefCell;
 | 
			
		||||
use std::convert;
 | 
			
		||||
use std::default::Default;
 | 
			
		||||
use std::ffi::{CStr, CString};
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::mem;
 | 
			
		||||
use std::os::raw::{c_char, c_int};
 | 
			
		||||
 | 
			
		||||
use std::path::{Path, PathBuf};
 | 
			
		||||
use std::ptr;
 | 
			
		||||
use std::result;
 | 
			
		||||
use std::str;
 | 
			
		||||
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
 | 
			
		||||
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, ValueRef};
 | 
			
		||||
use error::{error_from_sqlite_code, error_from_handle};
 | 
			
		||||
use raw_statement::RawStatement;
 | 
			
		||||
use cache::StatementCache;
 | 
			
		||||
use error::{error_from_handle, error_from_sqlite_code};
 | 
			
		||||
use raw_statement::RawStatement;
 | 
			
		||||
use types::{ToSql, ValueRef};
 | 
			
		||||
 | 
			
		||||
pub use statement::Statement;
 | 
			
		||||
use statement::StatementCrateImpl;
 | 
			
		||||
 | 
			
		||||
pub use row::{Row, Rows, MappedRows, AndThenRows, RowIndex};
 | 
			
		||||
use row::RowsCrateImpl;
 | 
			
		||||
pub use row::{AndThenRows, MappedRows, Row, RowIndex, Rows};
 | 
			
		||||
 | 
			
		||||
pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
 | 
			
		||||
#[allow(deprecated)]
 | 
			
		||||
pub use transaction::{SqliteTransaction, SqliteTransactionBehavior};
 | 
			
		||||
pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
 | 
			
		||||
 | 
			
		||||
pub use error::Error;
 | 
			
		||||
#[allow(deprecated)]
 | 
			
		||||
pub use error::SqliteError;
 | 
			
		||||
pub use error::Error;
 | 
			
		||||
pub use ffi::ErrorCode;
 | 
			
		||||
 | 
			
		||||
pub use cache::CachedStatement;
 | 
			
		||||
pub use version::*;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "hooks")]
 | 
			
		||||
pub use hooks::*;
 | 
			
		||||
#[cfg(feature = "load_extension")]
 | 
			
		||||
#[allow(deprecated)]
 | 
			
		||||
pub use load_extension_guard::{SqliteLoadExtensionGuard, LoadExtensionGuard};
 | 
			
		||||
pub use load_extension_guard::{LoadExtensionGuard, SqliteLoadExtensionGuard};
 | 
			
		||||
 | 
			
		||||
pub mod types;
 | 
			
		||||
mod version;
 | 
			
		||||
mod transaction;
 | 
			
		||||
#[cfg(feature = "backup")]
 | 
			
		||||
pub mod backup;
 | 
			
		||||
#[cfg(feature = "blob")]
 | 
			
		||||
pub mod blob;
 | 
			
		||||
mod busy;
 | 
			
		||||
mod cache;
 | 
			
		||||
#[cfg(any(feature = "functions", feature = "vtab"))]
 | 
			
		||||
mod context;
 | 
			
		||||
mod error;
 | 
			
		||||
#[cfg(feature = "functions")]
 | 
			
		||||
pub mod functions;
 | 
			
		||||
#[cfg(feature = "hooks")]
 | 
			
		||||
mod hooks;
 | 
			
		||||
#[cfg(feature = "limits")]
 | 
			
		||||
pub mod limits;
 | 
			
		||||
#[cfg(feature = "load_extension")]
 | 
			
		||||
mod load_extension_guard;
 | 
			
		||||
mod raw_statement;
 | 
			
		||||
mod row;
 | 
			
		||||
mod statement;
 | 
			
		||||
#[cfg(feature = "load_extension")]
 | 
			
		||||
mod load_extension_guard;
 | 
			
		||||
#[cfg(feature = "trace")]
 | 
			
		||||
pub mod trace;
 | 
			
		||||
#[cfg(feature = "backup")]
 | 
			
		||||
pub mod backup;
 | 
			
		||||
#[cfg(feature = "functions")]
 | 
			
		||||
pub mod functions;
 | 
			
		||||
#[cfg(feature = "blob")]
 | 
			
		||||
pub mod blob;
 | 
			
		||||
#[cfg(feature = "limits")]
 | 
			
		||||
pub mod limits;
 | 
			
		||||
#[cfg(feature = "hooks")]
 | 
			
		||||
mod hooks;
 | 
			
		||||
#[cfg(feature = "hooks")]
 | 
			
		||||
pub use hooks::*;
 | 
			
		||||
mod transaction;
 | 
			
		||||
pub mod types;
 | 
			
		||||
mod unlock_notify;
 | 
			
		||||
mod version;
 | 
			
		||||
#[cfg(feature = "vtab")]
 | 
			
		||||
pub mod vtab;
 | 
			
		||||
 | 
			
		||||
// Number of cached prepared statements we'll hold on to.
 | 
			
		||||
const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
 | 
			
		||||
@@ -152,6 +156,7 @@ fn path_to_cstring(p: &Path) -> Result<CString> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Name for a database within a SQLite connection.
 | 
			
		||||
#[derive(Copy, Clone)]
 | 
			
		||||
pub enum DatabaseName<'a> {
 | 
			
		||||
    /// The main database.
 | 
			
		||||
    Main,
 | 
			
		||||
@@ -168,7 +173,7 @@ pub enum DatabaseName<'a> {
 | 
			
		||||
#[cfg(any(feature = "backup", feature = "blob"))]
 | 
			
		||||
impl<'a> DatabaseName<'a> {
 | 
			
		||||
    fn to_cstring(&self) -> Result<CString> {
 | 
			
		||||
        use self::DatabaseName::{Main, Temp, Attached};
 | 
			
		||||
        use self::DatabaseName::{Attached, Main, Temp};
 | 
			
		||||
        match *self {
 | 
			
		||||
            Main => str_to_cstring("main"),
 | 
			
		||||
            Temp => str_to_cstring("temp"),
 | 
			
		||||
@@ -207,7 +212,7 @@ impl Connection {
 | 
			
		||||
    /// Will return `Err` if `path` cannot be converted to a C-compatible string or if the
 | 
			
		||||
    /// underlying SQLite open call fails.
 | 
			
		||||
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Connection> {
 | 
			
		||||
        let flags = Default::default();
 | 
			
		||||
        let flags = OpenFlags::default();
 | 
			
		||||
        Connection::open_with_flags(path, flags)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -217,7 +222,7 @@ impl Connection {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if the underlying SQLite open call fails.
 | 
			
		||||
    pub fn open_in_memory() -> Result<Connection> {
 | 
			
		||||
        let flags = Default::default();
 | 
			
		||||
        let flags = OpenFlags::default();
 | 
			
		||||
        Connection::open_in_memory_with_flags(flags)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -232,13 +237,11 @@ impl Connection {
 | 
			
		||||
    /// underlying SQLite open call fails.
 | 
			
		||||
    pub fn open_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Result<Connection> {
 | 
			
		||||
        let c_path = try!(path_to_cstring(path.as_ref()));
 | 
			
		||||
        InnerConnection::open_with_flags(&c_path, flags).map(|db| {
 | 
			
		||||
                                                                 Connection {
 | 
			
		||||
                db: RefCell::new(db),
 | 
			
		||||
                cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
 | 
			
		||||
                path: Some(path.as_ref().to_path_buf()),
 | 
			
		||||
            }
 | 
			
		||||
                                                             })
 | 
			
		||||
        InnerConnection::open_with_flags(&c_path, flags).map(|db| Connection {
 | 
			
		||||
            db: RefCell::new(db),
 | 
			
		||||
            cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
 | 
			
		||||
            path: Some(path.as_ref().to_path_buf()),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Open a new connection to an in-memory SQLite database.
 | 
			
		||||
@@ -251,13 +254,11 @@ impl Connection {
 | 
			
		||||
    /// Will return `Err` if the underlying SQLite open call fails.
 | 
			
		||||
    pub fn open_in_memory_with_flags(flags: OpenFlags) -> Result<Connection> {
 | 
			
		||||
        let c_memory = try!(str_to_cstring(":memory:"));
 | 
			
		||||
        InnerConnection::open_with_flags(&c_memory, flags).map(|db| {
 | 
			
		||||
                                                                   Connection {
 | 
			
		||||
                db: RefCell::new(db),
 | 
			
		||||
                cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
 | 
			
		||||
                path: None,
 | 
			
		||||
            }
 | 
			
		||||
                                                               })
 | 
			
		||||
        InnerConnection::open_with_flags(&c_memory, flags).map(|db| Connection {
 | 
			
		||||
            db: RefCell::new(db),
 | 
			
		||||
            cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
 | 
			
		||||
            path: None,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Convenience method to run multiple SQL statements (that cannot take any parameters).
 | 
			
		||||
@@ -305,9 +306,8 @@ impl Connection {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
 | 
			
		||||
    /// underlying SQLite call fails.
 | 
			
		||||
    pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result<c_int> {
 | 
			
		||||
        self.prepare(sql)
 | 
			
		||||
            .and_then(|mut stmt| stmt.execute(params))
 | 
			
		||||
    pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result<usize> {
 | 
			
		||||
        self.prepare(sql).and_then(|mut stmt| stmt.execute(params))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Convenience method to prepare and execute a single SQL statement with named parameter(s).
 | 
			
		||||
@@ -319,7 +319,7 @@ impl Connection {
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```rust,no_run
 | 
			
		||||
    /// # use rusqlite::{Connection, Result};
 | 
			
		||||
    /// fn insert(conn: &Connection) -> Result<i32> {
 | 
			
		||||
    /// fn insert(conn: &Connection) -> Result<usize> {
 | 
			
		||||
    ///     conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")])
 | 
			
		||||
    /// }
 | 
			
		||||
    /// ```
 | 
			
		||||
@@ -328,7 +328,7 @@ impl Connection {
 | 
			
		||||
    ///
 | 
			
		||||
    /// 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> {
 | 
			
		||||
    pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result<usize> {
 | 
			
		||||
        self.prepare(sql)
 | 
			
		||||
            .and_then(|mut stmt| stmt.execute_named(params))
 | 
			
		||||
    }
 | 
			
		||||
@@ -361,7 +361,8 @@ impl Connection {
 | 
			
		||||
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
 | 
			
		||||
    /// underlying SQLite call fails.
 | 
			
		||||
    pub fn query_row<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T>
 | 
			
		||||
        where F: FnOnce(&Row) -> T
 | 
			
		||||
    where
 | 
			
		||||
        F: FnOnce(&Row) -> T,
 | 
			
		||||
    {
 | 
			
		||||
        let mut stmt = try!(self.prepare(sql));
 | 
			
		||||
        stmt.query_row(params, f)
 | 
			
		||||
@@ -377,7 +378,8 @@ impl Connection {
 | 
			
		||||
    /// 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
 | 
			
		||||
    where
 | 
			
		||||
        F: FnOnce(&Row) -> T,
 | 
			
		||||
    {
 | 
			
		||||
        let mut stmt = try!(self.prepare(sql));
 | 
			
		||||
        let mut rows = try!(stmt.query_named(params));
 | 
			
		||||
@@ -408,20 +410,20 @@ impl Connection {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
 | 
			
		||||
    /// underlying SQLite call fails.
 | 
			
		||||
    pub fn query_row_and_then<T, E, F>(&self,
 | 
			
		||||
                                       sql: &str,
 | 
			
		||||
                                       params: &[&ToSql],
 | 
			
		||||
                                       f: F)
 | 
			
		||||
                                       -> result::Result<T, E>
 | 
			
		||||
        where F: FnOnce(&Row) -> result::Result<T, E>,
 | 
			
		||||
              E: convert::From<Error>
 | 
			
		||||
    pub fn query_row_and_then<T, E, F>(
 | 
			
		||||
        &self,
 | 
			
		||||
        sql: &str,
 | 
			
		||||
        params: &[&ToSql],
 | 
			
		||||
        f: F,
 | 
			
		||||
    ) -> result::Result<T, E>
 | 
			
		||||
    where
 | 
			
		||||
        F: FnOnce(&Row) -> result::Result<T, E>,
 | 
			
		||||
        E: convert::From<Error>,
 | 
			
		||||
    {
 | 
			
		||||
        let mut stmt = try!(self.prepare(sql));
 | 
			
		||||
        let mut rows = try!(stmt.query(params));
 | 
			
		||||
 | 
			
		||||
        rows.get_expected_row()
 | 
			
		||||
            .map_err(E::from)
 | 
			
		||||
            .and_then(|r| f(&r))
 | 
			
		||||
        rows.get_expected_row().map_err(E::from).and_then(|r| f(&r))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Convenience method to execute a query that is expected to return a single row.
 | 
			
		||||
@@ -445,7 +447,8 @@ impl Connection {
 | 
			
		||||
    /// does exactly the same thing.
 | 
			
		||||
    #[deprecated(since = "0.1.0", note = "Use query_row instead")]
 | 
			
		||||
    pub fn query_row_safe<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T>
 | 
			
		||||
        where F: FnOnce(&Row) -> T
 | 
			
		||||
    where
 | 
			
		||||
        F: FnOnce(&Row) -> T,
 | 
			
		||||
    {
 | 
			
		||||
        self.query_row(sql, params, f)
 | 
			
		||||
    }
 | 
			
		||||
@@ -545,10 +548,11 @@ impl Connection {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if the underlying SQLite call fails.
 | 
			
		||||
    #[cfg(feature = "load_extension")]
 | 
			
		||||
    pub fn load_extension<P: AsRef<Path>>(&self,
 | 
			
		||||
                                          dylib_path: P,
 | 
			
		||||
                                          entry_point: Option<&str>)
 | 
			
		||||
                                          -> Result<()> {
 | 
			
		||||
    pub fn load_extension<P: AsRef<Path>>(
 | 
			
		||||
        &self,
 | 
			
		||||
        dylib_path: P,
 | 
			
		||||
        entry_point: Option<&str>,
 | 
			
		||||
    ) -> Result<()> {
 | 
			
		||||
        self.db
 | 
			
		||||
            .borrow_mut()
 | 
			
		||||
            .load_extension(dylib_path.as_ref(), entry_point)
 | 
			
		||||
@@ -570,9 +574,21 @@ impl Connection {
 | 
			
		||||
        self.db.borrow_mut().decode_result(code)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn changes(&self) -> c_int {
 | 
			
		||||
    fn changes(&self) -> usize {
 | 
			
		||||
        self.db.borrow_mut().changes()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Test for auto-commit mode.
 | 
			
		||||
    /// Autocommit mode is on by default.
 | 
			
		||||
    pub fn is_autocommit(&self) -> bool {
 | 
			
		||||
        self.db.borrow().is_autocommit()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Determine if all associated prepared statements have been reset.
 | 
			
		||||
    #[cfg(feature = "bundled")]
 | 
			
		||||
    pub fn is_busy(&self) -> bool {
 | 
			
		||||
        self.db.borrow().is_busy()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Debug for Connection {
 | 
			
		||||
@@ -585,6 +601,12 @@ impl fmt::Debug for Connection {
 | 
			
		||||
 | 
			
		||||
struct InnerConnection {
 | 
			
		||||
    db: *mut ffi::sqlite3,
 | 
			
		||||
    #[cfg(feature = "hooks")]
 | 
			
		||||
    free_commit_hook: Option<fn(*mut ::std::os::raw::c_void)>,
 | 
			
		||||
    #[cfg(feature = "hooks")]
 | 
			
		||||
    free_rollback_hook: Option<fn(*mut ::std::os::raw::c_void)>,
 | 
			
		||||
    #[cfg(feature = "hooks")]
 | 
			
		||||
    free_update_hook: Option<fn(*mut ::std::os::raw::c_void)>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Old name for `OpenFlags`. `SqliteOpenFlags` is deprecated.
 | 
			
		||||
@@ -610,8 +632,10 @@ bitflags! {
 | 
			
		||||
 | 
			
		||||
impl Default for OpenFlags {
 | 
			
		||||
    fn default() -> OpenFlags {
 | 
			
		||||
        OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE | 
 | 
			
		||||
        OpenFlags::SQLITE_OPEN_NO_MUTEX | OpenFlags::SQLITE_OPEN_URI
 | 
			
		||||
        OpenFlags::SQLITE_OPEN_READ_WRITE
 | 
			
		||||
            | OpenFlags::SQLITE_OPEN_CREATE
 | 
			
		||||
            | OpenFlags::SQLITE_OPEN_NO_MUTEX
 | 
			
		||||
            | OpenFlags::SQLITE_OPEN_URI
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -658,9 +682,11 @@ fn ensure_valid_sqlite_version() {
 | 
			
		||||
        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());
 | 
			
		||||
            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) {
 | 
			
		||||
@@ -670,14 +696,16 @@ fn ensure_valid_sqlite_version() {
 | 
			
		||||
        // 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!("\
 | 
			
		||||
            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());
 | 
			
		||||
                str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
 | 
			
		||||
                version()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
@@ -743,6 +771,20 @@ To fix this, either:
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl InnerConnection {
 | 
			
		||||
    #[cfg(not(feature = "hooks"))]
 | 
			
		||||
    fn new(db: *mut ffi::sqlite3) -> InnerConnection {
 | 
			
		||||
        InnerConnection { db }
 | 
			
		||||
    }
 | 
			
		||||
    #[cfg(feature = "hooks")]
 | 
			
		||||
    fn new(db: *mut ffi::sqlite3) -> InnerConnection {
 | 
			
		||||
        InnerConnection {
 | 
			
		||||
            db,
 | 
			
		||||
            free_commit_hook: None,
 | 
			
		||||
            free_rollback_hook: None,
 | 
			
		||||
            free_update_hook: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result<InnerConnection> {
 | 
			
		||||
        ensure_valid_sqlite_version();
 | 
			
		||||
        ensure_safe_sqlite_threading_mode()?;
 | 
			
		||||
@@ -751,9 +793,15 @@ impl InnerConnection {
 | 
			
		||||
        // wasn't added until version 3.7.3.
 | 
			
		||||
        debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02);
 | 
			
		||||
        debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04);
 | 
			
		||||
        debug_assert_eq!(1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits, 0x40);
 | 
			
		||||
        debug_assert_eq!(
 | 
			
		||||
            1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits,
 | 
			
		||||
            0x40
 | 
			
		||||
        );
 | 
			
		||||
        if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
 | 
			
		||||
            return Err(Error::SqliteFailure(ffi::Error::new(ffi::SQLITE_MISUSE), None));
 | 
			
		||||
            return Err(Error::SqliteFailure(
 | 
			
		||||
                ffi::Error::new(ffi::SQLITE_MISUSE),
 | 
			
		||||
                None,
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        unsafe {
 | 
			
		||||
@@ -780,7 +828,7 @@ impl InnerConnection {
 | 
			
		||||
            // attempt to turn on extended results code; don't fail if we can't.
 | 
			
		||||
            ffi::sqlite3_extended_result_codes(db, 1);
 | 
			
		||||
 | 
			
		||||
            Ok(InnerConnection { db })
 | 
			
		||||
            Ok(InnerConnection::new(db))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -814,11 +862,13 @@ impl InnerConnection {
 | 
			
		||||
    fn execute_batch(&mut self, sql: &str) -> Result<()> {
 | 
			
		||||
        let c_sql = try!(str_to_cstring(sql));
 | 
			
		||||
        unsafe {
 | 
			
		||||
            let r = ffi::sqlite3_exec(self.db(),
 | 
			
		||||
                                      c_sql.as_ptr(),
 | 
			
		||||
                                      None,
 | 
			
		||||
                                      ptr::null_mut(),
 | 
			
		||||
                                      ptr::null_mut());
 | 
			
		||||
            let r = ffi::sqlite3_exec(
 | 
			
		||||
                self.db(),
 | 
			
		||||
                c_sql.as_ptr(),
 | 
			
		||||
                None,
 | 
			
		||||
                ptr::null_mut(),
 | 
			
		||||
                ptr::null_mut(),
 | 
			
		||||
            );
 | 
			
		||||
            self.decode_result(r)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -836,10 +886,12 @@ impl InnerConnection {
 | 
			
		||||
            let mut errmsg: *mut c_char = mem::uninitialized();
 | 
			
		||||
            let r = if let Some(entry_point) = entry_point {
 | 
			
		||||
                let c_entry = try!(str_to_cstring(entry_point));
 | 
			
		||||
                ffi::sqlite3_load_extension(self.db,
 | 
			
		||||
                                            dylib_str.as_ptr(),
 | 
			
		||||
                                            c_entry.as_ptr(),
 | 
			
		||||
                                            &mut errmsg)
 | 
			
		||||
                ffi::sqlite3_load_extension(
 | 
			
		||||
                    self.db,
 | 
			
		||||
                    dylib_str.as_ptr(),
 | 
			
		||||
                    c_entry.as_ptr(),
 | 
			
		||||
                    &mut errmsg,
 | 
			
		||||
                )
 | 
			
		||||
            } else {
 | 
			
		||||
                ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), ptr::null(), &mut errmsg)
 | 
			
		||||
            };
 | 
			
		||||
@@ -894,18 +946,35 @@ impl InnerConnection {
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        self.decode_result(r).map(|_| {
 | 
			
		||||
            Statement::new(conn, RawStatement::new(c_stmt))
 | 
			
		||||
        })
 | 
			
		||||
        self.decode_result(r)
 | 
			
		||||
            .map(|_| Statement::new(conn, RawStatement::new(c_stmt)))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn changes(&mut self) -> c_int {
 | 
			
		||||
        unsafe { ffi::sqlite3_changes(self.db()) }
 | 
			
		||||
    fn changes(&mut self) -> usize {
 | 
			
		||||
        unsafe { ffi::sqlite3_changes(self.db()) as usize }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn is_autocommit(&self) -> bool {
 | 
			
		||||
        unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "bundled")] // 3.8.6
 | 
			
		||||
    fn is_busy(&self) -> bool {
 | 
			
		||||
        let db = self.db();
 | 
			
		||||
        unsafe {
 | 
			
		||||
            let mut stmt = ffi::sqlite3_next_stmt(db, ptr::null_mut());
 | 
			
		||||
            while !stmt.is_null() {
 | 
			
		||||
                if ffi::sqlite3_stmt_busy(stmt) != 0 {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                stmt = ffi::sqlite3_next_stmt(db, stmt);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(not(feature = "hooks"))]
 | 
			
		||||
    fn remove_hooks(&mut self) {
 | 
			
		||||
    }
 | 
			
		||||
    fn remove_hooks(&mut self) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for InnerConnection {
 | 
			
		||||
@@ -931,18 +1000,16 @@ pub type SqliteStatement<'conn> = Statement<'conn>;
 | 
			
		||||
#[deprecated(since = "0.6.0", note = "Use Rows instead")]
 | 
			
		||||
pub type SqliteRows<'stmt> = Rows<'stmt>;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Old name for `Row`. `SqliteRow` is deprecated.
 | 
			
		||||
#[deprecated(since = "0.6.0", note = "Use Row instead")]
 | 
			
		||||
pub type SqliteRow<'a, 'stmt> = Row<'a, 'stmt>;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    extern crate tempdir;
 | 
			
		||||
    use self::tempdir::TempDir;
 | 
			
		||||
    pub use super::*;
 | 
			
		||||
    use ffi;
 | 
			
		||||
    use self::tempdir::TempDir;
 | 
			
		||||
    pub use std::error::Error as StdError;
 | 
			
		||||
    pub use std::fmt;
 | 
			
		||||
 | 
			
		||||
@@ -959,7 +1026,53 @@ mod test {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
    fn test_concurrent_transactions_busy_commit() {
 | 
			
		||||
        use std::time::Duration;
 | 
			
		||||
        let tmp = TempDir::new("locked").unwrap();
 | 
			
		||||
        let path = tmp.path().join("transactions.db3");
 | 
			
		||||
 | 
			
		||||
        Connection::open(&path)
 | 
			
		||||
            .expect("create temp db")
 | 
			
		||||
            .execute_batch(
 | 
			
		||||
                "
 | 
			
		||||
            BEGIN; CREATE TABLE foo(x INTEGER);
 | 
			
		||||
            INSERT INTO foo VALUES(42); END;",
 | 
			
		||||
            ).expect("create temp db");
 | 
			
		||||
 | 
			
		||||
        let mut db1 = Connection::open(&path).unwrap();
 | 
			
		||||
        let mut db2 = Connection::open(&path).unwrap();
 | 
			
		||||
 | 
			
		||||
        db1.busy_timeout(Duration::from_millis(0)).unwrap();
 | 
			
		||||
        db2.busy_timeout(Duration::from_millis(0)).unwrap();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let tx1 = db1.transaction().unwrap();
 | 
			
		||||
            let tx2 = db2.transaction().unwrap();
 | 
			
		||||
 | 
			
		||||
            // SELECT first makes sqlite lock with a shared lock
 | 
			
		||||
            let _ = tx1
 | 
			
		||||
                .query_row("SELECT x FROM foo LIMIT 1", &[], |_| ())
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            let _ = tx2
 | 
			
		||||
                .query_row("SELECT x FROM foo LIMIT 1", &[], |_| ())
 | 
			
		||||
                .unwrap();
 | 
			
		||||
 | 
			
		||||
            tx1.execute("INSERT INTO foo VALUES(?1)", &[&1]).unwrap();
 | 
			
		||||
            let _ = tx2.execute("INSERT INTO foo VALUES(?1)", &[&2]);
 | 
			
		||||
 | 
			
		||||
            let _ = tx1.commit();
 | 
			
		||||
            let _ = tx2.commit();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let _ = db1
 | 
			
		||||
            .transaction()
 | 
			
		||||
            .expect("commit should have closed transaction");
 | 
			
		||||
        let _ = db2
 | 
			
		||||
            .transaction()
 | 
			
		||||
            .expect("commit should have closed transaction");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_persistence() {
 | 
			
		||||
        let temp_dir = TempDir::new("test_open_file").unwrap();
 | 
			
		||||
        let path = temp_dir.path().join("test.db3");
 | 
			
		||||
@@ -995,20 +1108,22 @@ mod test {
 | 
			
		||||
        // force the DB to be busy by preparing a statement; this must be done at the FFI
 | 
			
		||||
        // level to allow us to call .close() without dropping the prepared statement first.
 | 
			
		||||
        let raw_stmt = {
 | 
			
		||||
            use std::mem;
 | 
			
		||||
            use std::ptr;
 | 
			
		||||
            use std::os::raw::c_int;
 | 
			
		||||
            use super::str_to_cstring;
 | 
			
		||||
            use std::mem;
 | 
			
		||||
            use std::os::raw::c_int;
 | 
			
		||||
            use std::ptr;
 | 
			
		||||
 | 
			
		||||
            let raw_db = db.db.borrow_mut().db;
 | 
			
		||||
            let sql = "SELECT 1";
 | 
			
		||||
            let mut raw_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() };
 | 
			
		||||
            let rc = unsafe {
 | 
			
		||||
                ffi::sqlite3_prepare_v2(raw_db,
 | 
			
		||||
                                        str_to_cstring(sql).unwrap().as_ptr(),
 | 
			
		||||
                                        (sql.len() + 1) as c_int,
 | 
			
		||||
                                        &mut raw_stmt,
 | 
			
		||||
                                        ptr::null_mut())
 | 
			
		||||
                ffi::sqlite3_prepare_v2(
 | 
			
		||||
                    raw_db,
 | 
			
		||||
                    str_to_cstring(sql).unwrap().as_ptr(),
 | 
			
		||||
                    (sql.len() + 1) as c_int,
 | 
			
		||||
                    &mut raw_stmt,
 | 
			
		||||
                    ptr::null_mut(),
 | 
			
		||||
                )
 | 
			
		||||
            };
 | 
			
		||||
            assert_eq!(rc, ffi::SQLITE_OK);
 | 
			
		||||
            raw_stmt
 | 
			
		||||
@@ -1027,15 +1142,16 @@ mod test {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_open_with_flags() {
 | 
			
		||||
        for bad_flags in &[OpenFlags::empty(),
 | 
			
		||||
                           OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE,
 | 
			
		||||
                           OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE] {
 | 
			
		||||
        for bad_flags in &[
 | 
			
		||||
            OpenFlags::empty(),
 | 
			
		||||
            OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE,
 | 
			
		||||
            OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE,
 | 
			
		||||
        ] {
 | 
			
		||||
            assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
    fn test_execute_batch() {
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
        let sql = "BEGIN;
 | 
			
		||||
@@ -1047,7 +1163,8 @@ mod test {
 | 
			
		||||
                   END;";
 | 
			
		||||
        db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
        db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3").unwrap();
 | 
			
		||||
        db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        assert!(db.execute_batch("INVALID SQL").is_err());
 | 
			
		||||
    }
 | 
			
		||||
@@ -1057,16 +1174,22 @@ mod test {
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
        db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(1,
 | 
			
		||||
                   db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32])
 | 
			
		||||
                       .unwrap());
 | 
			
		||||
        assert_eq!(1,
 | 
			
		||||
                   db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32])
 | 
			
		||||
                       .unwrap());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            1,
 | 
			
		||||
            db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32])
 | 
			
		||||
                .unwrap()
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            1,
 | 
			
		||||
            db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32])
 | 
			
		||||
                .unwrap()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        assert_eq!(3i32,
 | 
			
		||||
                   db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                       .unwrap());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            3i32,
 | 
			
		||||
            db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@@ -1123,7 +1246,8 @@ mod test {
 | 
			
		||||
        assert_eq!(insert_stmt.execute(&[&2i32]).unwrap(), 1);
 | 
			
		||||
        assert_eq!(insert_stmt.execute(&[&3i32]).unwrap(), 1);
 | 
			
		||||
 | 
			
		||||
        let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")
 | 
			
		||||
        let mut query = db
 | 
			
		||||
            .prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        {
 | 
			
		||||
            let mut rows = query.query(&[&4i32]).unwrap();
 | 
			
		||||
@@ -1149,7 +1273,6 @@ mod test {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
    fn test_query_map() {
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
        let sql = "BEGIN;
 | 
			
		||||
@@ -1162,15 +1285,13 @@ mod test {
 | 
			
		||||
        db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
 | 
			
		||||
        let results: Result<Vec<String>> = query.query_map(&[], |row| row.get(1))
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            .collect();
 | 
			
		||||
        let results: Result<Vec<String>> =
 | 
			
		||||
            query.query_map(&[], |row| row.get(1)).unwrap().collect();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(results.unwrap().concat(), "hello, world!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
    fn test_query_row() {
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
        let sql = "BEGIN;
 | 
			
		||||
@@ -1182,9 +1303,11 @@ mod test {
 | 
			
		||||
                   END;";
 | 
			
		||||
        db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(10i64,
 | 
			
		||||
                   db.query_row::<i64, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                   .unwrap());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            10i64,
 | 
			
		||||
            db.query_row::<i64, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", &[], |r| r.get(0));
 | 
			
		||||
        match result.unwrap_err() {
 | 
			
		||||
@@ -1211,8 +1334,7 @@ mod test {
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
        db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        db.execute_batch("INSERT INTO foo DEFAULT VALUES")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        db.execute_batch("INSERT INTO foo DEFAULT VALUES").unwrap();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(db.last_insert_rowid(), 1);
 | 
			
		||||
 | 
			
		||||
@@ -1223,6 +1345,32 @@ mod test {
 | 
			
		||||
        assert_eq!(db.last_insert_rowid(), 10);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_is_autocommit() {
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
        assert!(
 | 
			
		||||
            db.is_autocommit(),
 | 
			
		||||
            "autocommit expected to be active by default"
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg(feature = "bundled")]
 | 
			
		||||
    fn test_is_busy() {
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
        assert!(!db.is_busy());
 | 
			
		||||
        let mut stmt = db.prepare("PRAGMA schema_version").unwrap();
 | 
			
		||||
        assert!(!db.is_busy());
 | 
			
		||||
        {
 | 
			
		||||
            let mut rows = stmt.query(&[]).unwrap();
 | 
			
		||||
            assert!(!db.is_busy());
 | 
			
		||||
            let row = rows.next();
 | 
			
		||||
            assert!(db.is_busy());
 | 
			
		||||
            assert!(row.is_some());
 | 
			
		||||
        }
 | 
			
		||||
        assert!(!db.is_busy());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_statement_debugging() {
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
@@ -1308,7 +1456,6 @@ mod test {
 | 
			
		||||
        type CustomResult<T> = ::std::result::Result<T, CustomError>;
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
        fn test_query_and_then() {
 | 
			
		||||
            let db = checked_memory_handle();
 | 
			
		||||
            let sql = "BEGIN;
 | 
			
		||||
@@ -1321,8 +1468,8 @@ mod test {
 | 
			
		||||
            db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
            let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
 | 
			
		||||
            let results: Result<Vec<String>> = query.query_and_then(&[],
 | 
			
		||||
                                                                          |row| row.get_checked(1))
 | 
			
		||||
            let results: Result<Vec<String>> = query
 | 
			
		||||
                .query_and_then(&[], |row| row.get_checked(1))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .collect();
 | 
			
		||||
 | 
			
		||||
@@ -1330,7 +1477,6 @@ mod test {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
        fn test_query_and_then_fails() {
 | 
			
		||||
            let db = checked_memory_handle();
 | 
			
		||||
            let sql = "BEGIN;
 | 
			
		||||
@@ -1343,7 +1489,8 @@ mod test {
 | 
			
		||||
            db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
            let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
 | 
			
		||||
            let bad_type: Result<Vec<f64>> = query.query_and_then(&[], |row| row.get_checked(1))
 | 
			
		||||
            let bad_type: Result<Vec<f64>> = query
 | 
			
		||||
                .query_and_then(&[], |row| row.get_checked(1))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .collect();
 | 
			
		||||
 | 
			
		||||
@@ -1352,7 +1499,8 @@ mod test {
 | 
			
		||||
                err => panic!("Unexpected error {}", err),
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let bad_idx: Result<Vec<String>> = query.query_and_then(&[], |row| row.get_checked(3))
 | 
			
		||||
            let bad_idx: Result<Vec<String>> = query
 | 
			
		||||
                .query_and_then(&[], |row| row.get_checked(3))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .collect();
 | 
			
		||||
 | 
			
		||||
@@ -1363,7 +1511,6 @@ mod test {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
        fn test_query_and_then_custom_error() {
 | 
			
		||||
            let db = checked_memory_handle();
 | 
			
		||||
            let sql = "BEGIN;
 | 
			
		||||
@@ -1376,18 +1523,15 @@ mod test {
 | 
			
		||||
            db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
            let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
 | 
			
		||||
            let results: CustomResult<Vec<String>> = query.query_and_then(&[], |row| {
 | 
			
		||||
                row.get_checked(1)
 | 
			
		||||
                .map_err(CustomError::Sqlite)
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            let results: CustomResult<Vec<String>> = query
 | 
			
		||||
                .query_and_then(&[], |row| row.get_checked(1).map_err(CustomError::Sqlite))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .collect();
 | 
			
		||||
 | 
			
		||||
            assert_eq!(results.unwrap().concat(), "hello, world!");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
        fn test_query_and_then_custom_error_fails() {
 | 
			
		||||
            let db = checked_memory_handle();
 | 
			
		||||
            let sql = "BEGIN;
 | 
			
		||||
@@ -1400,11 +1544,9 @@ mod test {
 | 
			
		||||
            db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
            let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
 | 
			
		||||
            let bad_type: CustomResult<Vec<f64>> = query.query_and_then(&[], |row| {
 | 
			
		||||
                row.get_checked(1)
 | 
			
		||||
                .map_err(CustomError::Sqlite)
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            let bad_type: CustomResult<Vec<f64>> = query
 | 
			
		||||
                .query_and_then(&[], |row| row.get_checked(1).map_err(CustomError::Sqlite))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .collect();
 | 
			
		||||
 | 
			
		||||
            match bad_type.unwrap_err() {
 | 
			
		||||
@@ -1412,11 +1554,9 @@ mod test {
 | 
			
		||||
                err => panic!("Unexpected error {}", err),
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let bad_idx: CustomResult<Vec<String>> = query.query_and_then(&[], |row| {
 | 
			
		||||
                row.get_checked(3)
 | 
			
		||||
                .map_err(CustomError::Sqlite)
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            let bad_idx: CustomResult<Vec<String>> = query
 | 
			
		||||
                .query_and_then(&[], |row| row.get_checked(3).map_err(CustomError::Sqlite))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .collect();
 | 
			
		||||
 | 
			
		||||
            match bad_idx.unwrap_err() {
 | 
			
		||||
@@ -1424,10 +1564,9 @@ mod test {
 | 
			
		||||
                err => panic!("Unexpected error {}", err),
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let non_sqlite_err: CustomResult<Vec<String>> = query.query_and_then(&[], |_| {
 | 
			
		||||
                Err(CustomError::SomeError)
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            let non_sqlite_err: CustomResult<Vec<String>> = query
 | 
			
		||||
                .query_and_then(&[], |_| Err(CustomError::SomeError))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .collect();
 | 
			
		||||
 | 
			
		||||
            match non_sqlite_err.unwrap_err() {
 | 
			
		||||
@@ -1437,7 +1576,6 @@ mod test {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
        fn test_query_row_and_then_custom_error() {
 | 
			
		||||
            let db = checked_memory_handle();
 | 
			
		||||
            let sql = "BEGIN;
 | 
			
		||||
@@ -1455,7 +1593,6 @@ mod test {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
        fn test_query_row_and_then_custom_error_fails() {
 | 
			
		||||
            let db = checked_memory_handle();
 | 
			
		||||
            let sql = "BEGIN;
 | 
			
		||||
@@ -1483,9 +1620,8 @@ mod test {
 | 
			
		||||
                err => panic!("Unexpected error {}", err),
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let non_sqlite_err: CustomResult<String> = db.query_row_and_then(query, &[], |_| {
 | 
			
		||||
                Err(CustomError::SomeError)
 | 
			
		||||
            });
 | 
			
		||||
            let non_sqlite_err: CustomResult<String> =
 | 
			
		||||
                db.query_row_and_then(query, &[], |_| Err(CustomError::SomeError));
 | 
			
		||||
 | 
			
		||||
            match non_sqlite_err.unwrap_err() {
 | 
			
		||||
                CustomError::SomeError => (),
 | 
			
		||||
@@ -1494,7 +1630,6 @@ mod test {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[test]
 | 
			
		||||
        #[cfg_attr(rustfmt, rustfmt_skip)]
 | 
			
		||||
        fn test_dynamic() {
 | 
			
		||||
            let db = checked_memory_handle();
 | 
			
		||||
            let sql = "BEGIN;
 | 
			
		||||
@@ -1503,7 +1638,9 @@ mod test {
 | 
			
		||||
                       END;";
 | 
			
		||||
            db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
            db.query_row("SELECT * FROM foo", &[], |r| assert_eq!(2, r.column_count())).unwrap();
 | 
			
		||||
            db.query_row("SELECT * FROM foo", &[], |r| {
 | 
			
		||||
                assert_eq!(2, r.column_count())
 | 
			
		||||
            }).unwrap();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use {Result, Connection};
 | 
			
		||||
use {Connection, Result};
 | 
			
		||||
 | 
			
		||||
/// Old name for `LoadExtensionGuard`. `SqliteLoadExtensionGuard` is deprecated.
 | 
			
		||||
#[deprecated(since = "0.6.0", note = "Use LoadExtensionGuard instead")]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
use std::ffi::CStr;
 | 
			
		||||
use std::ptr;
 | 
			
		||||
use std::os::raw::c_int;
 | 
			
		||||
use super::ffi;
 | 
			
		||||
use super::unlock_notify;
 | 
			
		||||
use std::ffi::CStr;
 | 
			
		||||
use std::os::raw::c_int;
 | 
			
		||||
use std::ptr;
 | 
			
		||||
 | 
			
		||||
// Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
@@ -17,16 +17,16 @@ impl RawStatement {
 | 
			
		||||
        self.0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn column_count(&self) -> c_int {
 | 
			
		||||
        unsafe { ffi::sqlite3_column_count(self.0) }
 | 
			
		||||
    pub fn column_count(&self) -> usize {
 | 
			
		||||
        unsafe { ffi::sqlite3_column_count(self.0) as usize }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn column_type(&self, idx: c_int) -> c_int {
 | 
			
		||||
        unsafe { ffi::sqlite3_column_type(self.0, idx) }
 | 
			
		||||
    pub fn column_type(&self, idx: usize) -> c_int {
 | 
			
		||||
        unsafe { ffi::sqlite3_column_type(self.0, idx as c_int) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn column_name(&self, idx: c_int) -> &CStr {
 | 
			
		||||
        unsafe { CStr::from_ptr(ffi::sqlite3_column_name(self.0, idx)) }
 | 
			
		||||
    pub fn column_name(&self, idx: usize) -> &CStr {
 | 
			
		||||
        unsafe { CStr::from_ptr(ffi::sqlite3_column_name(self.0, idx as c_int)) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn step(&self) -> c_int {
 | 
			
		||||
@@ -54,15 +54,15 @@ impl RawStatement {
 | 
			
		||||
        unsafe { ffi::sqlite3_reset(self.0) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn bind_parameter_count(&self) -> c_int {
 | 
			
		||||
        unsafe { ffi::sqlite3_bind_parameter_count(self.0) }
 | 
			
		||||
    pub fn bind_parameter_count(&self) -> usize {
 | 
			
		||||
        unsafe { ffi::sqlite3_bind_parameter_count(self.0) as usize }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn bind_parameter_index(&self, name: &CStr) -> Option<c_int> {
 | 
			
		||||
    pub fn bind_parameter_index(&self, name: &CStr) -> Option<usize> {
 | 
			
		||||
        let r = unsafe { ffi::sqlite3_bind_parameter_index(self.0, name.as_ptr()) };
 | 
			
		||||
        match r {
 | 
			
		||||
            0 => None,
 | 
			
		||||
            i => Some(i),
 | 
			
		||||
            i => Some(i as usize),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -83,6 +83,23 @@ impl RawStatement {
 | 
			
		||||
        self.0 = ptr::null_mut();
 | 
			
		||||
        r
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "bundled")]
 | 
			
		||||
    pub fn readonly(&self) -> bool {
 | 
			
		||||
        unsafe { ffi::sqlite3_stmt_readonly(self.0) != 0 }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "bundled")]
 | 
			
		||||
    pub fn expanded_sql(&self) -> Option<&CStr> {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            let ptr = ffi::sqlite3_expanded_sql(self.0);
 | 
			
		||||
            if ptr.is_null() {
 | 
			
		||||
                None
 | 
			
		||||
            } else {
 | 
			
		||||
                Some(CStr::from_ptr(ptr))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Drop for RawStatement {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										109
									
								
								src/row.rs
									
									
									
									
									
								
							
							
						
						
									
										109
									
								
								src/row.rs
									
									
									
									
									
								
							@@ -1,9 +1,8 @@
 | 
			
		||||
use std::{convert, result};
 | 
			
		||||
use std::marker::PhantomData;
 | 
			
		||||
use std::{convert, result};
 | 
			
		||||
 | 
			
		||||
use super::{Statement, Error, Result};
 | 
			
		||||
use super::{Error, Result, Statement};
 | 
			
		||||
use types::{FromSql, FromSqlError};
 | 
			
		||||
use statement::StatementCrateImpl;
 | 
			
		||||
 | 
			
		||||
/// An handle for the resulting rows of a query.
 | 
			
		||||
pub struct Rows<'stmt> {
 | 
			
		||||
@@ -27,40 +26,31 @@ impl<'stmt> Rows<'stmt> {
 | 
			
		||||
    /// lifetime of the returned row is tied to the lifetime of `self`. This is a
 | 
			
		||||
    /// "streaming iterator". For a more natural interface, consider using `query_map`
 | 
			
		||||
    /// or `query_and_then` instead, which return types that implement `Iterator`.
 | 
			
		||||
    #[cfg_attr(feature = "cargo-clippy", allow(should_implement_trait))] // cannot implement Iterator
 | 
			
		||||
    pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
 | 
			
		||||
        self.stmt
 | 
			
		||||
            .and_then(|stmt| match stmt.step() {
 | 
			
		||||
                Ok(true) => {
 | 
			
		||||
                    Some(Ok(Row {
 | 
			
		||||
                        stmt,
 | 
			
		||||
                        phantom: PhantomData,
 | 
			
		||||
                    }))
 | 
			
		||||
                }
 | 
			
		||||
                Ok(false) => {
 | 
			
		||||
                    self.reset();
 | 
			
		||||
                    None
 | 
			
		||||
                }
 | 
			
		||||
                Err(err) => {
 | 
			
		||||
                    self.reset();
 | 
			
		||||
                    Some(Err(err))
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        self.stmt.and_then(|stmt| match stmt.step() {
 | 
			
		||||
            Ok(true) => Some(Ok(Row {
 | 
			
		||||
                stmt,
 | 
			
		||||
                phantom: PhantomData,
 | 
			
		||||
            })),
 | 
			
		||||
            Ok(false) => {
 | 
			
		||||
                self.reset();
 | 
			
		||||
                None
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                self.reset();
 | 
			
		||||
                Some(Err(err))
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: This trait lets us have "pub(crate)" visibility on some methods. Remove this
 | 
			
		||||
// once pub(crate) is stable.
 | 
			
		||||
pub trait RowsCrateImpl<'stmt> {
 | 
			
		||||
    fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt>;
 | 
			
		||||
    fn get_expected_row<'a>(&'a mut self) -> Result<Row<'a, 'stmt>>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'stmt> RowsCrateImpl<'stmt> for Rows<'stmt> {
 | 
			
		||||
    fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> {
 | 
			
		||||
impl<'stmt> Rows<'stmt> {
 | 
			
		||||
    pub(crate) fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> {
 | 
			
		||||
        Rows { stmt: Some(stmt) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_expected_row<'a>(&'a mut self) -> Result<Row<'a, 'stmt>> {
 | 
			
		||||
    pub(crate) fn get_expected_row<'a>(&'a mut self) -> Result<Row<'a, 'stmt>> {
 | 
			
		||||
        match self.next() {
 | 
			
		||||
            Some(row) => row,
 | 
			
		||||
            None => Err(Error::QueryReturnedNoRows),
 | 
			
		||||
@@ -80,22 +70,18 @@ pub struct MappedRows<'stmt, F> {
 | 
			
		||||
    map: F,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: This trait lets us have "pub(crate)" visibility on some methods. Remove this
 | 
			
		||||
// once pub(crate) is stable.
 | 
			
		||||
pub trait MappedRowsCrateImpl<'stmt, T, F> {
 | 
			
		||||
    fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'stmt, T, F> MappedRowsCrateImpl<'stmt, T, F> for MappedRows<'stmt, F>
 | 
			
		||||
    where F: FnMut(&Row) -> T
 | 
			
		||||
impl<'stmt, T, F> MappedRows<'stmt, F>
 | 
			
		||||
where
 | 
			
		||||
    F: FnMut(&Row) -> T,
 | 
			
		||||
{
 | 
			
		||||
    fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
 | 
			
		||||
    pub(crate) fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
 | 
			
		||||
        MappedRows { rows, map: f }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'conn, T, F> Iterator for MappedRows<'conn, F>
 | 
			
		||||
    where F: FnMut(&Row) -> T
 | 
			
		||||
where
 | 
			
		||||
    F: FnMut(&Row) -> T,
 | 
			
		||||
{
 | 
			
		||||
    type Item = Result<T>;
 | 
			
		||||
 | 
			
		||||
@@ -114,23 +100,19 @@ pub struct AndThenRows<'stmt, F> {
 | 
			
		||||
    map: F,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: This trait lets us have "pub(crate)" visibility on some methods. Remove this
 | 
			
		||||
// once pub(crate) is stable.
 | 
			
		||||
pub trait AndThenRowsCrateImpl<'stmt, T, E, F> {
 | 
			
		||||
    fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'stmt, T, E, F> AndThenRowsCrateImpl<'stmt, T, E, F> for AndThenRows<'stmt, F>
 | 
			
		||||
    where F: FnMut(&Row) -> result::Result<T, E>
 | 
			
		||||
impl<'stmt, T, E, F> AndThenRows<'stmt, F>
 | 
			
		||||
where
 | 
			
		||||
    F: FnMut(&Row) -> result::Result<T, E>,
 | 
			
		||||
{
 | 
			
		||||
    fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
 | 
			
		||||
    pub(crate) fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
 | 
			
		||||
        AndThenRows { rows, map: f }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
 | 
			
		||||
    where E: convert::From<Error>,
 | 
			
		||||
          F: FnMut(&Row) -> result::Result<T, E>
 | 
			
		||||
where
 | 
			
		||||
    E: convert::From<Error>,
 | 
			
		||||
    F: FnMut(&Row) -> result::Result<T, E>,
 | 
			
		||||
{
 | 
			
		||||
    type Item = result::Result<T, E>;
 | 
			
		||||
 | 
			
		||||
@@ -178,21 +160,16 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
 | 
			
		||||
        let idx = try!(idx.idx(self.stmt));
 | 
			
		||||
        let value = self.stmt.value_ref(idx);
 | 
			
		||||
        FromSql::column_result(value).map_err(|err| match err {
 | 
			
		||||
                                                  FromSqlError::InvalidType => {
 | 
			
		||||
                                                      Error::InvalidColumnType(idx,
 | 
			
		||||
                                                                               value.data_type())
 | 
			
		||||
                                                  }
 | 
			
		||||
                                                  FromSqlError::OutOfRange(i) => {
 | 
			
		||||
                                                      Error::IntegralValueOutOfRange(idx, i)
 | 
			
		||||
                                                  }
 | 
			
		||||
                                                  FromSqlError::Other(err) => {
 | 
			
		||||
            FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()),
 | 
			
		||||
            FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
 | 
			
		||||
            FromSqlError::Other(err) => {
 | 
			
		||||
                Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
 | 
			
		||||
            }
 | 
			
		||||
                                              })
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return the number of columns in the current row.
 | 
			
		||||
    pub fn column_count(&self) -> i32 {
 | 
			
		||||
    pub fn column_count(&self) -> usize {
 | 
			
		||||
        self.stmt.column_count()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -201,13 +178,13 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
 | 
			
		||||
pub trait RowIndex {
 | 
			
		||||
    /// Returns the index of the appropriate column, or `None` if no such
 | 
			
		||||
    /// column exists.
 | 
			
		||||
    fn idx(&self, stmt: &Statement) -> Result<i32>;
 | 
			
		||||
    fn idx(&self, stmt: &Statement) -> Result<usize>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RowIndex for i32 {
 | 
			
		||||
impl RowIndex for usize {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn idx(&self, stmt: &Statement) -> Result<i32> {
 | 
			
		||||
        if *self < 0 || *self >= stmt.column_count() {
 | 
			
		||||
    fn idx(&self, stmt: &Statement) -> Result<usize> {
 | 
			
		||||
        if *self >= stmt.column_count() {
 | 
			
		||||
            Err(Error::InvalidColumnIndex(*self))
 | 
			
		||||
        } else {
 | 
			
		||||
            Ok(*self)
 | 
			
		||||
@@ -217,7 +194,7 @@ impl RowIndex for i32 {
 | 
			
		||||
 | 
			
		||||
impl<'a> RowIndex for &'a str {
 | 
			
		||||
    #[inline]
 | 
			
		||||
    fn idx(&self, stmt: &Statement) -> Result<i32> {
 | 
			
		||||
    fn idx(&self, stmt: &Statement) -> Result<usize> {
 | 
			
		||||
        stmt.column_index(*self)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										358
									
								
								src/statement.rs
									
									
									
									
									
								
							
							
						
						
									
										358
									
								
								src/statement.rs
									
									
									
									
									
								
							@@ -1,13 +1,18 @@
 | 
			
		||||
use std::{convert, fmt, mem, ptr, result, str};
 | 
			
		||||
use std::ffi::CStr;
 | 
			
		||||
use std::os::raw::{c_char, c_int, c_void};
 | 
			
		||||
#[cfg(feature = "array")]
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
use std::slice::from_raw_parts;
 | 
			
		||||
use std::{convert, fmt, mem, ptr, result, str};
 | 
			
		||||
 | 
			
		||||
use super::ffi;
 | 
			
		||||
use super::{Connection, RawStatement, Result, Error, ValueRef, Row, Rows, AndThenRows, MappedRows};
 | 
			
		||||
use super::str_to_cstring;
 | 
			
		||||
use super::{
 | 
			
		||||
    AndThenRows, Connection, Error, MappedRows, RawStatement, Result, Row, Rows, ValueRef,
 | 
			
		||||
};
 | 
			
		||||
use types::{ToSql, ToSqlOutput};
 | 
			
		||||
use row::{RowsCrateImpl, MappedRowsCrateImpl, AndThenRowsCrateImpl};
 | 
			
		||||
#[cfg(feature = "array")]
 | 
			
		||||
use vtab::array::{free_array, ARRAY_TYPE};
 | 
			
		||||
 | 
			
		||||
/// A prepared statement.
 | 
			
		||||
pub struct Statement<'conn> {
 | 
			
		||||
@@ -29,7 +34,7 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return the number of columns in the result set returned by the prepared statement.
 | 
			
		||||
    pub fn column_count(&self) -> i32 {
 | 
			
		||||
    pub fn column_count(&self) -> usize {
 | 
			
		||||
        self.stmt.column_count()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +46,7 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    /// # Failure
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return an `Error::InvalidColumnName` when there is no column with the specified `name`.
 | 
			
		||||
    pub fn column_index(&self, name: &str) -> Result<i32> {
 | 
			
		||||
    pub fn column_index(&self, name: &str) -> Result<usize> {
 | 
			
		||||
        let bytes = name.as_bytes();
 | 
			
		||||
        let n = self.column_count();
 | 
			
		||||
        for i in 0..n {
 | 
			
		||||
@@ -75,7 +80,7 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    ///
 | 
			
		||||
    /// 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> {
 | 
			
		||||
    pub fn execute(&mut self, params: &[&ToSql]) -> Result<usize> {
 | 
			
		||||
        try!(self.bind_parameters(params));
 | 
			
		||||
        self.execute_with_bound_parameters()
 | 
			
		||||
    }
 | 
			
		||||
@@ -92,7 +97,7 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```rust,no_run
 | 
			
		||||
    /// # use rusqlite::{Connection, Result};
 | 
			
		||||
    /// fn insert(conn: &Connection) -> Result<i32> {
 | 
			
		||||
    /// fn insert(conn: &Connection) -> Result<usize> {
 | 
			
		||||
    ///     let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)"));
 | 
			
		||||
    ///     stmt.execute_named(&[(":name", &"one")])
 | 
			
		||||
    /// }
 | 
			
		||||
@@ -102,7 +107,7 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    ///
 | 
			
		||||
    /// 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> {
 | 
			
		||||
    pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result<usize> {
 | 
			
		||||
        try!(self.bind_parameters_named(params));
 | 
			
		||||
        self.execute_with_bound_parameters()
 | 
			
		||||
    }
 | 
			
		||||
@@ -155,6 +160,7 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if binding parameters fails.
 | 
			
		||||
    pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result<Rows<'a>> {
 | 
			
		||||
        try!(self.check_readonly());
 | 
			
		||||
        try!(self.bind_parameters(params));
 | 
			
		||||
        Ok(Rows::new(self))
 | 
			
		||||
    }
 | 
			
		||||
@@ -182,6 +188,7 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if binding parameters fails.
 | 
			
		||||
    pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> Result<Rows<'a>> {
 | 
			
		||||
        try!(self.check_readonly());
 | 
			
		||||
        try!(self.bind_parameters_named(params));
 | 
			
		||||
        Ok(Rows::new(self))
 | 
			
		||||
    }
 | 
			
		||||
@@ -210,7 +217,8 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    ///
 | 
			
		||||
    /// 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
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut(&Row) -> T,
 | 
			
		||||
    {
 | 
			
		||||
        let rows = self.query(params)?;
 | 
			
		||||
        Ok(MappedRows::new(rows, f))
 | 
			
		||||
@@ -242,11 +250,13 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    /// ## 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
 | 
			
		||||
    pub fn query_map_named<'a, T, F>(
 | 
			
		||||
        &'a mut self,
 | 
			
		||||
        params: &[(&str, &ToSql)],
 | 
			
		||||
        f: F,
 | 
			
		||||
    ) -> Result<MappedRows<'a, F>>
 | 
			
		||||
    where
 | 
			
		||||
        F: FnMut(&Row) -> T,
 | 
			
		||||
    {
 | 
			
		||||
        let rows = self.query_named(params)?;
 | 
			
		||||
        Ok(MappedRows::new(rows, f))
 | 
			
		||||
@@ -259,12 +269,14 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    /// # 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>
 | 
			
		||||
    pub fn query_and_then<'a, T, E, F>(
 | 
			
		||||
        &'a mut self,
 | 
			
		||||
        params: &[&ToSql],
 | 
			
		||||
        f: F,
 | 
			
		||||
    ) -> Result<AndThenRows<'a, F>>
 | 
			
		||||
    where
 | 
			
		||||
        E: convert::From<Error>,
 | 
			
		||||
        F: FnMut(&Row) -> result::Result<T, E>,
 | 
			
		||||
    {
 | 
			
		||||
        let rows = self.query(params)?;
 | 
			
		||||
        Ok(AndThenRows::new(rows, f))
 | 
			
		||||
@@ -305,12 +317,14 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    /// ## 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>
 | 
			
		||||
    pub fn query_and_then_named<'a, T, E, F>(
 | 
			
		||||
        &'a mut self,
 | 
			
		||||
        params: &[(&str, &ToSql)],
 | 
			
		||||
        f: F,
 | 
			
		||||
    ) -> Result<AndThenRows<'a, F>>
 | 
			
		||||
    where
 | 
			
		||||
        E: convert::From<Error>,
 | 
			
		||||
        F: FnMut(&Row) -> result::Result<T, E>,
 | 
			
		||||
    {
 | 
			
		||||
        let rows = self.query_named(params)?;
 | 
			
		||||
        Ok(AndThenRows::new(rows, f))
 | 
			
		||||
@@ -337,7 +351,8 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    ///
 | 
			
		||||
    /// 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
 | 
			
		||||
    where
 | 
			
		||||
        F: FnOnce(&Row) -> T,
 | 
			
		||||
    {
 | 
			
		||||
        let mut rows = try!(self.query(params));
 | 
			
		||||
 | 
			
		||||
@@ -362,19 +377,22 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
    ///
 | 
			
		||||
    /// 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>> {
 | 
			
		||||
    pub fn parameter_index(&self, name: &str) -> Result<Option<usize>> {
 | 
			
		||||
        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_eq!(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());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            params.len(),
 | 
			
		||||
            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));
 | 
			
		||||
            try!(self.bind_parameter(*p, i + 1));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
@@ -391,7 +409,7 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn bind_parameter(&self, param: &ToSql, col: c_int) -> Result<()> {
 | 
			
		||||
    fn bind_parameter(&self, param: &ToSql, col: usize) -> Result<()> {
 | 
			
		||||
        let value = try!(param.to_sql());
 | 
			
		||||
 | 
			
		||||
        let ptr = unsafe { self.stmt.ptr() };
 | 
			
		||||
@@ -401,20 +419,28 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
 | 
			
		||||
            #[cfg(feature = "blob")]
 | 
			
		||||
            ToSqlOutput::ZeroBlob(len) => {
 | 
			
		||||
                return self.conn
 | 
			
		||||
                           .decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col, len) });
 | 
			
		||||
                return self
 | 
			
		||||
                    .conn
 | 
			
		||||
                    .decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col as c_int, len) });
 | 
			
		||||
            }
 | 
			
		||||
            #[cfg(feature = "array")]
 | 
			
		||||
            ToSqlOutput::Array(a) => {
 | 
			
		||||
                return self.conn.decode_result(unsafe {
 | 
			
		||||
                    ffi::sqlite3_bind_pointer(
 | 
			
		||||
                        ptr,
 | 
			
		||||
                        col as c_int,
 | 
			
		||||
                        Rc::into_raw(a) as *mut c_void,
 | 
			
		||||
                        ARRAY_TYPE,
 | 
			
		||||
                        Some(free_array),
 | 
			
		||||
                    )
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        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 {
 | 
			
		||||
        self.conn.decode_result(match value {
 | 
			
		||||
            ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col as c_int) },
 | 
			
		||||
            ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col as c_int, i) },
 | 
			
		||||
            ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col as c_int, r) },
 | 
			
		||||
            ValueRef::Text(s) => unsafe {
 | 
			
		||||
                let length = s.len();
 | 
			
		||||
                if length > ::std::i32::MAX as usize {
 | 
			
		||||
                    ffi::SQLITE_TOOBIG
 | 
			
		||||
@@ -425,27 +451,35 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
                    } else {
 | 
			
		||||
                        ffi::SQLITE_STATIC()
 | 
			
		||||
                    };
 | 
			
		||||
                    ffi::sqlite3_bind_text(ptr, col, c_str.as_ptr(), length as c_int, destructor)
 | 
			
		||||
                    ffi::sqlite3_bind_text(
 | 
			
		||||
                        ptr,
 | 
			
		||||
                        col as c_int,
 | 
			
		||||
                        c_str.as_ptr(),
 | 
			
		||||
                        length as c_int,
 | 
			
		||||
                        destructor,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
                               ValueRef::Blob(b) => unsafe {
 | 
			
		||||
            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)
 | 
			
		||||
                    ffi::sqlite3_bind_zeroblob(ptr, col as c_int, 0)
 | 
			
		||||
                } else {
 | 
			
		||||
                    ffi::sqlite3_bind_blob(ptr,
 | 
			
		||||
                                           col,
 | 
			
		||||
                                           b.as_ptr() as *const c_void,
 | 
			
		||||
                                           length as c_int,
 | 
			
		||||
                                           ffi::SQLITE_TRANSIENT())
 | 
			
		||||
                    ffi::sqlite3_bind_blob(
 | 
			
		||||
                        ptr,
 | 
			
		||||
                        col as c_int,
 | 
			
		||||
                        b.as_ptr() as *const c_void,
 | 
			
		||||
                        length as c_int,
 | 
			
		||||
                        ffi::SQLITE_TRANSIENT(),
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
                           })
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn execute_with_bound_parameters(&mut self) -> Result<c_int> {
 | 
			
		||||
    fn execute_with_bound_parameters(&mut self) -> Result<usize> {
 | 
			
		||||
        let r = self.stmt.step();
 | 
			
		||||
        self.stmt.reset();
 | 
			
		||||
        match r {
 | 
			
		||||
@@ -466,6 +500,29 @@ impl<'conn> Statement<'conn> {
 | 
			
		||||
        mem::swap(&mut stmt, &mut self.stmt);
 | 
			
		||||
        self.conn.decode_result(stmt.finalize())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(not(feature = "bundled"))]
 | 
			
		||||
    fn check_readonly(&self) -> Result<()> {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "bundled")]
 | 
			
		||||
    fn check_readonly(&self) -> Result<()> {
 | 
			
		||||
        if !self.stmt.readonly() {
 | 
			
		||||
            return Err(Error::InvalidQuery);
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns a string containing the SQL text of prepared statement with bound parameters expanded.
 | 
			
		||||
    #[cfg(feature = "bundled")]
 | 
			
		||||
    pub fn expanded_sql(&self) -> Option<&str> {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            self.stmt
 | 
			
		||||
                .expanded_sql()
 | 
			
		||||
                .map(|s| str::from_utf8_unchecked(s.to_bytes()))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'conn> Into<RawStatement> for Statement<'conn> {
 | 
			
		||||
@@ -494,55 +551,55 @@ impl<'conn> Drop for Statement<'conn> {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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,
 | 
			
		||||
            stmt,
 | 
			
		||||
        }
 | 
			
		||||
impl<'conn> Statement<'conn> {
 | 
			
		||||
    pub(crate) fn new(conn: &Connection, stmt: RawStatement) -> Statement {
 | 
			
		||||
        Statement { conn, stmt }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn value_ref(&self, col: c_int) -> ValueRef {
 | 
			
		||||
    pub(crate) fn value_ref(&self, col: usize) -> 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) })
 | 
			
		||||
                ValueRef::Integer(unsafe { ffi::sqlite3_column_int64(raw, col as c_int) })
 | 
			
		||||
            }
 | 
			
		||||
            ffi::SQLITE_FLOAT => {
 | 
			
		||||
                ValueRef::Real(unsafe { ffi::sqlite3_column_double(raw, col as c_int) })
 | 
			
		||||
            }
 | 
			
		||||
            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");
 | 
			
		||||
                    let text = ffi::sqlite3_column_text(raw, col as c_int);
 | 
			
		||||
                    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()
 | 
			
		||||
                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))
 | 
			
		||||
                    (
 | 
			
		||||
                        ffi::sqlite3_column_blob(raw, col as c_int),
 | 
			
		||||
                        ffi::sqlite3_column_bytes(raw, col as c_int),
 | 
			
		||||
                    )
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                assert!(len >= 0,
 | 
			
		||||
                        "unexpected negative return from sqlite3_column_bytes");
 | 
			
		||||
                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");
 | 
			
		||||
                    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
 | 
			
		||||
@@ -554,7 +611,7 @@ impl<'conn> StatementCrateImpl<'conn> for Statement<'conn> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn step(&self) -> Result<bool> {
 | 
			
		||||
    pub(crate) fn step(&self) -> Result<bool> {
 | 
			
		||||
        match self.stmt.step() {
 | 
			
		||||
            ffi::SQLITE_ROW => Ok(true),
 | 
			
		||||
            ffi::SQLITE_DONE => Ok(false),
 | 
			
		||||
@@ -562,7 +619,7 @@ impl<'conn> StatementCrateImpl<'conn> for Statement<'conn> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn reset(&self) -> c_int {
 | 
			
		||||
    pub(crate) fn reset(&self) -> c_int {
 | 
			
		||||
        self.stmt.reset()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -576,18 +633,25 @@ mod test {
 | 
			
		||||
        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!(
 | 
			
		||||
            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());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            3i32,
 | 
			
		||||
            db.query_row_named::<i32, _>(
 | 
			
		||||
                "SELECT SUM(x) FROM foo WHERE x > :x",
 | 
			
		||||
                &[(":x", &0i32)],
 | 
			
		||||
                |r| r.get(0)
 | 
			
		||||
            ).unwrap()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@@ -597,15 +661,19 @@ mod test {
 | 
			
		||||
                   INTEGER)";
 | 
			
		||||
        db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")
 | 
			
		||||
        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());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            1i32,
 | 
			
		||||
            db.query_row_named::<i32, _>(
 | 
			
		||||
                "SELECT COUNT(*) FROM test WHERE name = :name",
 | 
			
		||||
                &[(":name", &"one")],
 | 
			
		||||
                |r| r.get(0)
 | 
			
		||||
            ).unwrap()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@@ -617,7 +685,8 @@ mod test {
 | 
			
		||||
        "#;
 | 
			
		||||
        db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut stmt = db.prepare("SELECT id FROM test where name = :name")
 | 
			
		||||
        let mut stmt = db
 | 
			
		||||
            .prepare("SELECT id FROM test where name = :name")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap();
 | 
			
		||||
 | 
			
		||||
@@ -634,13 +703,14 @@ mod test {
 | 
			
		||||
        "#;
 | 
			
		||||
        db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut stmt = db.prepare("SELECT id FROM test where name = :name")
 | 
			
		||||
        let mut stmt = db
 | 
			
		||||
            .prepare("SELECT id FROM test where name = :name")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| {
 | 
			
		||||
        let mut rows = stmt
 | 
			
		||||
            .query_map_named(&[(":name", &"one")], |row| {
 | 
			
		||||
                let id: i32 = row.get(0);
 | 
			
		||||
                2 * id
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap();
 | 
			
		||||
            }).unwrap();
 | 
			
		||||
 | 
			
		||||
        let doubled_id: i32 = rows.next().unwrap().unwrap();
 | 
			
		||||
        assert_eq!(2, doubled_id);
 | 
			
		||||
@@ -648,7 +718,6 @@ mod test {
 | 
			
		||||
 | 
			
		||||
    #[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);
 | 
			
		||||
@@ -657,17 +726,18 @@ mod test {
 | 
			
		||||
        "#;
 | 
			
		||||
        db.execute_batch(sql).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")
 | 
			
		||||
        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 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();
 | 
			
		||||
            }).unwrap();
 | 
			
		||||
 | 
			
		||||
        // first row should be Ok
 | 
			
		||||
        let doubled_id: i32 = rows.next().unwrap().unwrap();
 | 
			
		||||
@@ -687,13 +757,14 @@ mod test {
 | 
			
		||||
        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)")
 | 
			
		||||
        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();
 | 
			
		||||
        let result: Option<String> = db
 | 
			
		||||
            .query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert!(result.is_none());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -703,14 +774,15 @@ mod test {
 | 
			
		||||
        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)")
 | 
			
		||||
        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();
 | 
			
		||||
        let result: String = db
 | 
			
		||||
            .query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(result, "one");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -719,7 +791,8 @@ mod test {
 | 
			
		||||
        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 (?)")
 | 
			
		||||
        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);
 | 
			
		||||
@@ -727,7 +800,8 @@ mod test {
 | 
			
		||||
            Error::StatementChangedRows(0) => (),
 | 
			
		||||
            err => panic!("Unexpected error {}", err),
 | 
			
		||||
        }
 | 
			
		||||
        let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
 | 
			
		||||
        let mut multi = db
 | 
			
		||||
            .prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        match multi.insert(&[]).unwrap_err() {
 | 
			
		||||
            Error::StatementChangedRows(2) => (),
 | 
			
		||||
@@ -739,22 +813,27 @@ mod 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"
 | 
			
		||||
        db.execute_batch(
 | 
			
		||||
            r"
 | 
			
		||||
            CREATE TABLE foo(x INTEGER);
 | 
			
		||||
            CREATE TABLE bar(x INTEGER);
 | 
			
		||||
        ")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        ",
 | 
			
		||||
        ).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);
 | 
			
		||||
        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]
 | 
			
		||||
@@ -811,4 +890,13 @@ mod test {
 | 
			
		||||
        let y: Result<i64> = stmt.query_row(&[], |r| r.get("y"));
 | 
			
		||||
        assert_eq!(3i64, y.unwrap());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[cfg(feature = "bundled")]
 | 
			
		||||
    fn test_expanded_sql() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
        let stmt = db.prepare("SELECT ?").unwrap();
 | 
			
		||||
        stmt.bind_parameter(&1, 1).unwrap();
 | 
			
		||||
        assert_eq!(Some("SELECT 1"), stmt.expanded_sql());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								src/trace.rs
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								src/trace.rs
									
									
									
									
									
								
							@@ -1,14 +1,14 @@
 | 
			
		||||
//! Tracing and profiling functions. Error and warning log.
 | 
			
		||||
 | 
			
		||||
use std::os::raw::{c_char, c_int, c_void};
 | 
			
		||||
use std::ffi::{CStr, CString};
 | 
			
		||||
use std::mem;
 | 
			
		||||
use std::os::raw::{c_char, c_int, c_void};
 | 
			
		||||
use std::ptr;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
use super::ffi;
 | 
			
		||||
use {Result, Connection};
 | 
			
		||||
use error::error_from_sqlite_code;
 | 
			
		||||
use {Connection, Result};
 | 
			
		||||
 | 
			
		||||
/// Set up the process-wide SQLite error logging callback.
 | 
			
		||||
/// This function is marked unsafe for two reasons:
 | 
			
		||||
@@ -33,9 +33,11 @@ pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
 | 
			
		||||
    let rc = match callback {
 | 
			
		||||
        Some(f) => {
 | 
			
		||||
            let p_arg: *mut c_void = mem::transmute(f);
 | 
			
		||||
            ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG,
 | 
			
		||||
                                log_callback as extern "C" fn(_, _, _),
 | 
			
		||||
                                p_arg)
 | 
			
		||||
            ffi::sqlite3_config(
 | 
			
		||||
                ffi::SQLITE_CONFIG_LOG,
 | 
			
		||||
                log_callback as extern "C" fn(_, _, _),
 | 
			
		||||
                p_arg,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
        None => {
 | 
			
		||||
            let nullptr: *mut c_void = ptr::null_mut();
 | 
			
		||||
@@ -90,16 +92,20 @@ impl Connection {
 | 
			
		||||
    /// There can only be a single profiler defined for each database connection.
 | 
			
		||||
    /// Setting a new profiler clears the old one.
 | 
			
		||||
    pub fn profile(&mut self, profile_fn: Option<fn(&str, Duration)>) {
 | 
			
		||||
        unsafe extern "C" fn profile_callback(p_arg: *mut c_void,
 | 
			
		||||
                                              z_sql: *const c_char,
 | 
			
		||||
                                              nanoseconds: u64) {
 | 
			
		||||
        unsafe extern "C" fn profile_callback(
 | 
			
		||||
            p_arg: *mut c_void,
 | 
			
		||||
            z_sql: *const c_char,
 | 
			
		||||
            nanoseconds: u64,
 | 
			
		||||
        ) {
 | 
			
		||||
            let profile_fn: fn(&str, Duration) = mem::transmute(p_arg);
 | 
			
		||||
            let c_slice = CStr::from_ptr(z_sql).to_bytes();
 | 
			
		||||
            let s = String::from_utf8_lossy(c_slice);
 | 
			
		||||
            const NANOS_PER_SEC: u64 = 1_000_000_000;
 | 
			
		||||
 | 
			
		||||
            let duration = Duration::new(nanoseconds / NANOS_PER_SEC,
 | 
			
		||||
                                         (nanoseconds % NANOS_PER_SEC) as u32);
 | 
			
		||||
            let duration = Duration::new(
 | 
			
		||||
                nanoseconds / NANOS_PER_SEC,
 | 
			
		||||
                (nanoseconds % NANOS_PER_SEC) as u32,
 | 
			
		||||
            );
 | 
			
		||||
            profile_fn(&s, duration);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
use std::ops::Deref;
 | 
			
		||||
use {Result, Connection};
 | 
			
		||||
use {Connection, Result};
 | 
			
		||||
 | 
			
		||||
/// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is deprecated.
 | 
			
		||||
#[deprecated(since = "0.6.0", note = "Use TransactionBehavior instead")]
 | 
			
		||||
@@ -7,7 +7,7 @@ pub type SqliteTransactionBehavior = TransactionBehavior;
 | 
			
		||||
 | 
			
		||||
/// Options for transaction behavior. See [BEGIN
 | 
			
		||||
/// TRANSACTION](http://www.sqlite.org/lang_transaction.html) for details.
 | 
			
		||||
#[derive(Copy,Clone)]
 | 
			
		||||
#[derive(Copy, Clone)]
 | 
			
		||||
pub enum TransactionBehavior {
 | 
			
		||||
    Deferred,
 | 
			
		||||
    Immediate,
 | 
			
		||||
@@ -15,7 +15,7 @@ pub enum TransactionBehavior {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Options for how a Transaction or Savepoint should behave when it is dropped.
 | 
			
		||||
#[derive(Copy,Clone,PartialEq,Eq)]
 | 
			
		||||
#[derive(Copy, Clone, PartialEq, Eq)]
 | 
			
		||||
pub enum DropBehavior {
 | 
			
		||||
    /// Roll back the changes. This is the default.
 | 
			
		||||
    Rollback,
 | 
			
		||||
@@ -61,7 +61,6 @@ pub type SqliteTransaction<'conn> = Transaction<'conn>;
 | 
			
		||||
pub struct Transaction<'conn> {
 | 
			
		||||
    conn: &'conn Connection,
 | 
			
		||||
    drop_behavior: DropBehavior,
 | 
			
		||||
    committed: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Represents a savepoint on a database connection.
 | 
			
		||||
@@ -106,14 +105,10 @@ impl<'conn> Transaction<'conn> {
 | 
			
		||||
            TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
 | 
			
		||||
            TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
 | 
			
		||||
        };
 | 
			
		||||
        conn.execute_batch(query)
 | 
			
		||||
            .map(move |_| {
 | 
			
		||||
                     Transaction {
 | 
			
		||||
                         conn,
 | 
			
		||||
                         drop_behavior: DropBehavior::Rollback,
 | 
			
		||||
                         committed: false,
 | 
			
		||||
                     }
 | 
			
		||||
                 })
 | 
			
		||||
        conn.execute_batch(query).map(move |_| Transaction {
 | 
			
		||||
            conn,
 | 
			
		||||
            drop_behavior: DropBehavior::Rollback,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Starts a new [savepoint](http://www.sqlite.org/lang_savepoint.html), allowing nested
 | 
			
		||||
@@ -167,8 +162,8 @@ impl<'conn> Transaction<'conn> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn commit_(&mut self) -> Result<()> {
 | 
			
		||||
        self.committed = true;
 | 
			
		||||
        self.conn.execute_batch("COMMIT")
 | 
			
		||||
        self.conn.execute_batch("COMMIT")?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// A convenience method which consumes and rolls back a transaction.
 | 
			
		||||
@@ -177,8 +172,8 @@ impl<'conn> Transaction<'conn> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn rollback_(&mut self) -> Result<()> {
 | 
			
		||||
        self.committed = true;
 | 
			
		||||
        self.conn.execute_batch("ROLLBACK")
 | 
			
		||||
        self.conn.execute_batch("ROLLBACK")?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Consumes the transaction, committing or rolling back according to the current setting
 | 
			
		||||
@@ -191,11 +186,11 @@ impl<'conn> Transaction<'conn> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn finish_(&mut self) -> Result<()> {
 | 
			
		||||
        if self.committed {
 | 
			
		||||
        if self.conn.is_autocommit() {
 | 
			
		||||
            return Ok(());
 | 
			
		||||
        }
 | 
			
		||||
        match self.drop_behavior() {
 | 
			
		||||
            DropBehavior::Commit => self.commit_(),
 | 
			
		||||
            DropBehavior::Commit => self.commit_().or_else(|_| self.rollback_()),
 | 
			
		||||
            DropBehavior::Rollback => self.rollback_(),
 | 
			
		||||
            DropBehavior::Ignore => Ok(()),
 | 
			
		||||
            DropBehavior::Panic => panic!("Transaction dropped unexpectedly."),
 | 
			
		||||
@@ -219,20 +214,19 @@ impl<'conn> Drop for Transaction<'conn> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'conn> Savepoint<'conn> {
 | 
			
		||||
    fn with_depth_and_name<T: Into<String>>(conn: &Connection,
 | 
			
		||||
                                            depth: u32,
 | 
			
		||||
                                            name: T)
 | 
			
		||||
                                            -> Result<Savepoint> {
 | 
			
		||||
    fn with_depth_and_name<T: Into<String>>(
 | 
			
		||||
        conn: &Connection,
 | 
			
		||||
        depth: u32,
 | 
			
		||||
        name: T,
 | 
			
		||||
    ) -> Result<Savepoint> {
 | 
			
		||||
        let name = name.into();
 | 
			
		||||
        conn.execute_batch(&format!("SAVEPOINT {}", name))
 | 
			
		||||
            .map(|_| {
 | 
			
		||||
                Savepoint {
 | 
			
		||||
                    conn,
 | 
			
		||||
                    name,
 | 
			
		||||
                    depth,
 | 
			
		||||
                    drop_behavior: DropBehavior::Rollback,
 | 
			
		||||
                    committed: false,
 | 
			
		||||
                }
 | 
			
		||||
            .map(|_| Savepoint {
 | 
			
		||||
                conn,
 | 
			
		||||
                name,
 | 
			
		||||
                depth,
 | 
			
		||||
                drop_behavior: DropBehavior::Rollback,
 | 
			
		||||
                committed: false,
 | 
			
		||||
            })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -277,9 +271,9 @@ impl<'conn> Savepoint<'conn> {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn commit_(&mut self) -> Result<()> {
 | 
			
		||||
        self.conn.execute_batch(&format!("RELEASE {}", self.name))?;
 | 
			
		||||
        self.committed = true;
 | 
			
		||||
        self.conn
 | 
			
		||||
            .execute_batch(&format!("RELEASE {}", self.name))
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// A convenience method which rolls back a savepoint.
 | 
			
		||||
@@ -307,7 +301,7 @@ impl<'conn> Savepoint<'conn> {
 | 
			
		||||
            return Ok(());
 | 
			
		||||
        }
 | 
			
		||||
        match self.drop_behavior() {
 | 
			
		||||
            DropBehavior::Commit => self.commit_(),
 | 
			
		||||
            DropBehavior::Commit => self.commit_().or_else(|_| self.rollback()),
 | 
			
		||||
            DropBehavior::Rollback => self.rollback(),
 | 
			
		||||
            DropBehavior::Ignore => Ok(()),
 | 
			
		||||
            DropBehavior::Panic => panic!("Savepoint dropped unexpectedly."),
 | 
			
		||||
@@ -366,9 +360,10 @@ impl Connection {
 | 
			
		||||
    /// # Failure
 | 
			
		||||
    ///
 | 
			
		||||
    /// Will return `Err` if the underlying SQLite call fails.
 | 
			
		||||
    pub fn transaction_with_behavior(&mut self,
 | 
			
		||||
                                     behavior: TransactionBehavior)
 | 
			
		||||
                                     -> Result<Transaction> {
 | 
			
		||||
    pub fn transaction_with_behavior(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        behavior: TransactionBehavior,
 | 
			
		||||
    ) -> Result<Transaction> {
 | 
			
		||||
        Transaction::new(self, behavior)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -414,8 +409,8 @@ impl Connection {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use Connection;
 | 
			
		||||
    use super::DropBehavior;
 | 
			
		||||
    use Connection;
 | 
			
		||||
 | 
			
		||||
    fn checked_memory_handle() -> Connection {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
@@ -438,9 +433,11 @@ mod test {
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            let tx = db.transaction().unwrap();
 | 
			
		||||
            assert_eq!(2i32,
 | 
			
		||||
                       tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                           .unwrap());
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                2i32,
 | 
			
		||||
                tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -465,9 +462,11 @@ mod test {
 | 
			
		||||
        }
 | 
			
		||||
        {
 | 
			
		||||
            let tx = db.transaction().unwrap();
 | 
			
		||||
            assert_eq!(6i32,
 | 
			
		||||
                       tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                           .unwrap());
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                6i32,
 | 
			
		||||
                tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -561,7 +560,8 @@ mod test {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn assert_current_sum(x: i32, conn: &Connection) {
 | 
			
		||||
        let i = conn.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let i = conn
 | 
			
		||||
            .query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(x, i);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,10 @@ extern crate chrono;
 | 
			
		||||
 | 
			
		||||
use std::borrow::Cow;
 | 
			
		||||
 | 
			
		||||
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, Utc, Local};
 | 
			
		||||
use self::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
 | 
			
		||||
 | 
			
		||||
use Result;
 | 
			
		||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
 | 
			
		||||
use Result;
 | 
			
		||||
 | 
			
		||||
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
 | 
			
		||||
impl ToSql for NaiveDate {
 | 
			
		||||
@@ -22,9 +22,9 @@ impl FromSql for NaiveDate {
 | 
			
		||||
        value
 | 
			
		||||
            .as_str()
 | 
			
		||||
            .and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") {
 | 
			
		||||
                          Ok(dt) => Ok(dt),
 | 
			
		||||
                          Err(err) => Err(FromSqlError::Other(Box::new(err))),
 | 
			
		||||
                      })
 | 
			
		||||
                Ok(dt) => Ok(dt),
 | 
			
		||||
                Err(err) => Err(FromSqlError::Other(Box::new(err))),
 | 
			
		||||
            })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -39,19 +39,17 @@ impl ToSql for NaiveTime {
 | 
			
		||||
/// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone.
 | 
			
		||||
impl FromSql for NaiveTime {
 | 
			
		||||
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
 | 
			
		||||
        value
 | 
			
		||||
            .as_str()
 | 
			
		||||
            .and_then(|s| {
 | 
			
		||||
                let fmt = match s.len() {
 | 
			
		||||
                    5 => "%H:%M",
 | 
			
		||||
                    8 => "%H:%M:%S",
 | 
			
		||||
                    _ => "%H:%M:%S%.f",
 | 
			
		||||
                };
 | 
			
		||||
                match NaiveTime::parse_from_str(s, fmt) {
 | 
			
		||||
                    Ok(dt) => Ok(dt),
 | 
			
		||||
                    Err(err) => Err(FromSqlError::Other(Box::new(err))),
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        value.as_str().and_then(|s| {
 | 
			
		||||
            let fmt = match s.len() {
 | 
			
		||||
                5 => "%H:%M",
 | 
			
		||||
                8 => "%H:%M:%S",
 | 
			
		||||
                _ => "%H:%M:%S%.f",
 | 
			
		||||
            };
 | 
			
		||||
            match NaiveTime::parse_from_str(s, fmt) {
 | 
			
		||||
                Ok(dt) => Ok(dt),
 | 
			
		||||
                Err(err) => Err(FromSqlError::Other(Box::new(err))),
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -67,20 +65,18 @@ impl ToSql for NaiveDateTime {
 | 
			
		||||
/// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported)
 | 
			
		||||
impl FromSql for NaiveDateTime {
 | 
			
		||||
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
 | 
			
		||||
        value
 | 
			
		||||
            .as_str()
 | 
			
		||||
            .and_then(|s| {
 | 
			
		||||
                let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
 | 
			
		||||
                    "%Y-%m-%dT%H:%M:%S%.f"
 | 
			
		||||
                } else {
 | 
			
		||||
                    "%Y-%m-%d %H:%M:%S%.f"
 | 
			
		||||
                };
 | 
			
		||||
        value.as_str().and_then(|s| {
 | 
			
		||||
            let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
 | 
			
		||||
                "%Y-%m-%dT%H:%M:%S%.f"
 | 
			
		||||
            } else {
 | 
			
		||||
                "%Y-%m-%d %H:%M:%S%.f"
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
                match NaiveDateTime::parse_from_str(s, fmt) {
 | 
			
		||||
                    Ok(dt) => Ok(dt),
 | 
			
		||||
                    Err(err) => Err(FromSqlError::Other(Box::new(err))),
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
            match NaiveDateTime::parse_from_str(s, fmt) {
 | 
			
		||||
                Ok(dt) => Ok(dt),
 | 
			
		||||
                Err(err) => Err(FromSqlError::Other(Box::new(err))),
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -130,9 +126,10 @@ impl FromSql for DateTime<Local> {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::chrono::{
 | 
			
		||||
        DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
 | 
			
		||||
    };
 | 
			
		||||
    use Connection;
 | 
			
		||||
    use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
 | 
			
		||||
                        Duration};
 | 
			
		||||
 | 
			
		||||
    fn checked_memory_handle() -> Connection {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
@@ -148,10 +145,12 @@ mod test {
 | 
			
		||||
        db.execute("INSERT INTO foo (t) VALUES (?)", &[&date])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let s: String = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!("2016-02-23", s);
 | 
			
		||||
        let t: NaiveDate = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let t: NaiveDate = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(date, t);
 | 
			
		||||
    }
 | 
			
		||||
@@ -163,10 +162,12 @@ mod test {
 | 
			
		||||
        db.execute("INSERT INTO foo (t) VALUES (?)", &[&time])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let s: String = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!("23:56:04", s);
 | 
			
		||||
        let v: NaiveTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let v: NaiveTime = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(time, v);
 | 
			
		||||
    }
 | 
			
		||||
@@ -181,16 +182,18 @@ mod test {
 | 
			
		||||
        db.execute("INSERT INTO foo (t) VALUES (?)", &[&dt])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let s: String = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!("2016-02-23T23:56:04", s);
 | 
			
		||||
        let v: NaiveDateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let v: NaiveDateTime = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(dt, v);
 | 
			
		||||
 | 
			
		||||
        db.execute("UPDATE foo set b = datetime(t)", &[])
 | 
			
		||||
            .unwrap(); // "YYYY-MM-DD HH:MM:SS"
 | 
			
		||||
        let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        db.execute("UPDATE foo set b = datetime(t)", &[]).unwrap(); // "YYYY-MM-DD HH:MM:SS"
 | 
			
		||||
        let hms: NaiveDateTime = db
 | 
			
		||||
            .query_row("SELECT b FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(dt, hms);
 | 
			
		||||
    }
 | 
			
		||||
@@ -206,25 +209,29 @@ mod test {
 | 
			
		||||
        db.execute("INSERT INTO foo (t) VALUES (?)", &[&utc])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let s: String = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!("2016-02-23T23:56:04.789+00:00", s);
 | 
			
		||||
 | 
			
		||||
        let v1: DateTime<Utc> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let v1: DateTime<Utc> = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(utc, v1);
 | 
			
		||||
 | 
			
		||||
        let v2: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
 | 
			
		||||
        let v2: DateTime<Utc> = db
 | 
			
		||||
            .query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(utc, v2);
 | 
			
		||||
 | 
			
		||||
        let v3: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0))
 | 
			
		||||
        let v3: DateTime<Utc> = db
 | 
			
		||||
            .query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(utc - Duration::milliseconds(789), v3);
 | 
			
		||||
 | 
			
		||||
        let v4: DateTime<Utc> =
 | 
			
		||||
            db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0))
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        let v4: DateTime<Utc> = db
 | 
			
		||||
            .query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(utc, v4);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -240,11 +247,13 @@ mod test {
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        // Stored string should be in UTC
 | 
			
		||||
        let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let s: String = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert!(s.ends_with("+00:00"));
 | 
			
		||||
 | 
			
		||||
        let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let v: DateTime<Local> = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(local, v);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
use super::{ValueRef, Value};
 | 
			
		||||
use super::{Value, ValueRef};
 | 
			
		||||
use std::error::Error;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
@@ -35,12 +35,11 @@ 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> {
 | 
			
		||||
        match *self {
 | 
			
		||||
            FromSqlError::Other(ref err) => err.cause(),
 | 
			
		||||
            FromSqlError::InvalidType |
 | 
			
		||||
            FromSqlError::OutOfRange(_) => None,
 | 
			
		||||
            FromSqlError::InvalidType | FromSqlError::OutOfRange(_) => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -115,9 +114,9 @@ impl FromSql for f64 {
 | 
			
		||||
impl FromSql for bool {
 | 
			
		||||
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
 | 
			
		||||
        i64::column_result(value).map(|i| match i {
 | 
			
		||||
                                          0 => false,
 | 
			
		||||
                                          _ => true,
 | 
			
		||||
                                      })
 | 
			
		||||
            0 => false,
 | 
			
		||||
            _ => true,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -150,8 +149,8 @@ impl FromSql for Value {
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use {Connection, Error};
 | 
			
		||||
    use super::FromSql;
 | 
			
		||||
    use {Connection, Error};
 | 
			
		||||
 | 
			
		||||
    fn checked_memory_handle() -> Connection {
 | 
			
		||||
        Connection::open_in_memory().unwrap()
 | 
			
		||||
@@ -162,10 +161,12 @@ mod test {
 | 
			
		||||
        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
 | 
			
		||||
        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))
 | 
			
		||||
                let err = db
 | 
			
		||||
                    .query_row("SELECT ?", &[n], |r| r.get_checked::<_, T>(0))
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .unwrap_err();
 | 
			
		||||
                match err {
 | 
			
		||||
@@ -174,18 +175,22 @@ mod test {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            for n in in_range {
 | 
			
		||||
                assert_eq!(*n,
 | 
			
		||||
                           db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
 | 
			
		||||
                               .unwrap()
 | 
			
		||||
                               .into());
 | 
			
		||||
                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::<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]);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										211
									
								
								src/types/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										211
									
								
								src/types/mod.rs
									
									
									
									
									
								
							@@ -59,15 +59,15 @@ pub use self::value_ref::ValueRef;
 | 
			
		||||
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
mod value;
 | 
			
		||||
mod value_ref;
 | 
			
		||||
mod from_sql;
 | 
			
		||||
mod to_sql;
 | 
			
		||||
mod time;
 | 
			
		||||
#[cfg(feature = "chrono")]
 | 
			
		||||
mod chrono;
 | 
			
		||||
mod from_sql;
 | 
			
		||||
#[cfg(feature = "serde_json")]
 | 
			
		||||
mod serde_json;
 | 
			
		||||
mod time;
 | 
			
		||||
mod to_sql;
 | 
			
		||||
mod value;
 | 
			
		||||
mod value_ref;
 | 
			
		||||
 | 
			
		||||
/// Empty struct that can be used to fill in a query parameter as `NULL`.
 | 
			
		||||
///
 | 
			
		||||
@@ -77,17 +77,16 @@ mod serde_json;
 | 
			
		||||
/// # extern crate rusqlite;
 | 
			
		||||
/// # use rusqlite::{Connection, Result};
 | 
			
		||||
/// # use rusqlite::types::{Null};
 | 
			
		||||
/// # use std::os::raw::{c_int};
 | 
			
		||||
/// fn main() {
 | 
			
		||||
/// }
 | 
			
		||||
/// fn insert_null(conn: &Connection) -> Result<c_int> {
 | 
			
		||||
/// fn insert_null(conn: &Connection) -> Result<usize> {
 | 
			
		||||
///     conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null])
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
#[derive(Copy,Clone)]
 | 
			
		||||
#[derive(Copy, Clone)]
 | 
			
		||||
pub struct Null;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone,Debug,PartialEq)]
 | 
			
		||||
#[derive(Clone, Debug, PartialEq)]
 | 
			
		||||
pub enum Type {
 | 
			
		||||
    Null,
 | 
			
		||||
    Integer,
 | 
			
		||||
@@ -112,11 +111,11 @@ impl fmt::Display for Type {
 | 
			
		||||
mod test {
 | 
			
		||||
    extern crate time;
 | 
			
		||||
 | 
			
		||||
    use super::Value;
 | 
			
		||||
    use std::f64::EPSILON;
 | 
			
		||||
    use std::os::raw::{c_double, c_int};
 | 
			
		||||
    use Connection;
 | 
			
		||||
    use Error;
 | 
			
		||||
    use std::os::raw::{c_int, c_double};
 | 
			
		||||
    use std::f64::EPSILON;
 | 
			
		||||
    use super::Value;
 | 
			
		||||
 | 
			
		||||
    fn checked_memory_handle() -> Connection {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
@@ -133,7 +132,8 @@ mod test {
 | 
			
		||||
        db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let v: Vec<u8> = db
 | 
			
		||||
            .query_row("SELECT b FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(v, v1234);
 | 
			
		||||
    }
 | 
			
		||||
@@ -146,7 +146,8 @@ mod test {
 | 
			
		||||
        db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let v: Vec<u8> = db
 | 
			
		||||
            .query_row("SELECT b FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(v, empty);
 | 
			
		||||
    }
 | 
			
		||||
@@ -156,10 +157,10 @@ mod test {
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
 | 
			
		||||
        let s = "hello, world!";
 | 
			
		||||
        db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
 | 
			
		||||
 | 
			
		||||
        let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let from: String = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(from, s);
 | 
			
		||||
    }
 | 
			
		||||
@@ -172,7 +173,8 @@ mod test {
 | 
			
		||||
        db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let from: String = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(from, s);
 | 
			
		||||
    }
 | 
			
		||||
@@ -184,9 +186,11 @@ mod test {
 | 
			
		||||
        db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(10i64,
 | 
			
		||||
                   db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                       .unwrap());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            10i64,
 | 
			
		||||
            db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@@ -196,12 +200,11 @@ mod test {
 | 
			
		||||
        let s = Some("hello, world!");
 | 
			
		||||
        let b = Some(vec![1u8, 2, 3, 4]);
 | 
			
		||||
 | 
			
		||||
        db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        db.execute("INSERT INTO foo(b) VALUES (?)", &[&b])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
 | 
			
		||||
        db.execute("INSERT INTO foo(b) VALUES (?)", &[&b]).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")
 | 
			
		||||
        let mut stmt = db
 | 
			
		||||
            .prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        let mut rows = stmt.query(&[]).unwrap();
 | 
			
		||||
 | 
			
		||||
@@ -233,9 +236,10 @@ mod test {
 | 
			
		||||
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
 | 
			
		||||
        db.execute("INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
 | 
			
		||||
                     &[])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        db.execute(
 | 
			
		||||
            "INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
 | 
			
		||||
            &[],
 | 
			
		||||
        ).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap();
 | 
			
		||||
        let mut rows = stmt.query(&[]).unwrap();
 | 
			
		||||
@@ -243,57 +247,103 @@ mod test {
 | 
			
		||||
        let row = rows.next().unwrap().unwrap();
 | 
			
		||||
 | 
			
		||||
        // check the correct types come back as expected
 | 
			
		||||
        assert_eq!(vec![1, 2], row.get_checked::<i32, Vec<u8>>(0).unwrap());
 | 
			
		||||
        assert_eq!("text", row.get_checked::<i32, String>(1).unwrap());
 | 
			
		||||
        assert_eq!(1, row.get_checked::<i32, c_int>(2).unwrap());
 | 
			
		||||
        assert!((1.5 - row.get_checked::<i32, c_double>(3).unwrap()).abs() < EPSILON);
 | 
			
		||||
        assert!(row.get_checked::<i32, Option<c_int>>(4)
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .is_none());
 | 
			
		||||
        assert!(row.get_checked::<i32, Option<c_double>>(4)
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .is_none());
 | 
			
		||||
        assert!(row.get_checked::<i32, Option<String>>(4)
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
                    .is_none());
 | 
			
		||||
        assert_eq!(vec![1, 2], row.get_checked::<_, Vec<u8>>(0).unwrap());
 | 
			
		||||
        assert_eq!("text", row.get_checked::<_, String>(1).unwrap());
 | 
			
		||||
        assert_eq!(1, row.get_checked::<_, c_int>(2).unwrap());
 | 
			
		||||
        assert!((1.5 - row.get_checked::<_, c_double>(3).unwrap()).abs() < EPSILON);
 | 
			
		||||
        assert!(row.get_checked::<_, Option<c_int>>(4).unwrap().is_none());
 | 
			
		||||
        assert!(row.get_checked::<_, Option<c_double>>(4).unwrap().is_none());
 | 
			
		||||
        assert!(row.get_checked::<_, Option<String>>(4).unwrap().is_none());
 | 
			
		||||
 | 
			
		||||
        // check some invalid types
 | 
			
		||||
 | 
			
		||||
        // 0 is actually a blob (Vec<u8>)
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, c_int>(0).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, c_int>(0).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, i64>(0).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, c_double>(0).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, String>(0).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, time::Timespec>(0).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, Option<c_int>>(0).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, c_int>(0).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, c_int>(0).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, i64>(0).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, c_double>(0).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, String>(0).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, time::Timespec>(0).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, Option<c_int>>(0).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        // 1 is actually a text (String)
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, c_int>(1).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, i64>(1).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, c_double>(1).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, Vec<u8>>(1).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, Option<c_int>>(1).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, c_int>(1).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, i64>(1).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, c_double>(1).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, Vec<u8>>(1).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, Option<c_int>>(1).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        // 2 is actually an integer
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, String>(2).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, Vec<u8>>(2).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, Option<String>>(2).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, String>(2).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, Vec<u8>>(2).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, Option<String>>(2).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        // 3 is actually a float (c_double)
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, c_int>(3).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, i64>(3).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, String>(3).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, Vec<u8>>(3).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, Option<c_int>>(3).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, c_int>(3).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, i64>(3).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, String>(3).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, Vec<u8>>(3).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, Option<c_int>>(3).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        // 4 is actually NULL
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, c_int>(4).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, i64>(4).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, c_double>(4).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, String>(4).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, Vec<u8>>(4).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(row.get_checked::<i32, time::Timespec>(4).err().unwrap()));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, c_int>(4).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, i64>(4).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, c_double>(4).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, String>(4).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, Vec<u8>>(4).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
        assert!(is_invalid_column_type(
 | 
			
		||||
            row.get_checked::<_, time::Timespec>(4).err().unwrap()
 | 
			
		||||
        ));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@@ -301,23 +351,28 @@ mod test {
 | 
			
		||||
        use super::Value;
 | 
			
		||||
        let db = checked_memory_handle();
 | 
			
		||||
 | 
			
		||||
        db.execute("INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
 | 
			
		||||
                     &[])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        db.execute(
 | 
			
		||||
            "INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
 | 
			
		||||
            &[],
 | 
			
		||||
        ).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap();
 | 
			
		||||
        let mut rows = stmt.query(&[]).unwrap();
 | 
			
		||||
 | 
			
		||||
        let row = rows.next().unwrap().unwrap();
 | 
			
		||||
        assert_eq!(Value::Blob(vec![1, 2]),
 | 
			
		||||
                   row.get_checked::<i32, Value>(0).unwrap());
 | 
			
		||||
        assert_eq!(Value::Text(String::from("text")),
 | 
			
		||||
                   row.get_checked::<i32, Value>(1).unwrap());
 | 
			
		||||
        assert_eq!(Value::Integer(1), row.get_checked::<i32, Value>(2).unwrap());
 | 
			
		||||
        match row.get_checked::<i32, Value>(3).unwrap() {
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Value::Blob(vec![1, 2]),
 | 
			
		||||
            row.get_checked::<_, Value>(0).unwrap()
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            Value::Text(String::from("text")),
 | 
			
		||||
            row.get_checked::<_, Value>(1).unwrap()
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(Value::Integer(1), row.get_checked::<_, Value>(2).unwrap());
 | 
			
		||||
        match row.get_checked::<_, Value>(3).unwrap() {
 | 
			
		||||
            Value::Real(val) => assert!((1.5 - val).abs() < EPSILON),
 | 
			
		||||
            x => panic!("Invalid Value {:?}", x),
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(Value::Null, row.get_checked::<i32, Value>(4).unwrap());
 | 
			
		||||
        assert_eq!(Value::Null, row.get_checked::<_, Value>(4).unwrap());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,8 +3,8 @@ extern crate serde_json;
 | 
			
		||||
 | 
			
		||||
use self::serde_json::Value;
 | 
			
		||||
 | 
			
		||||
use Result;
 | 
			
		||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
 | 
			
		||||
use Result;
 | 
			
		||||
 | 
			
		||||
/// Serialize JSON `Value` to text.
 | 
			
		||||
impl ToSql for Value {
 | 
			
		||||
@@ -17,18 +17,17 @@ impl ToSql for Value {
 | 
			
		||||
impl FromSql for Value {
 | 
			
		||||
    fn column_result(value: ValueRef) -> FromSqlResult<Self> {
 | 
			
		||||
        match value {
 | 
			
		||||
                ValueRef::Text(s) => serde_json::from_str(s),
 | 
			
		||||
                ValueRef::Blob(b) => serde_json::from_slice(b),
 | 
			
		||||
                _ => return Err(FromSqlError::InvalidType),
 | 
			
		||||
            }
 | 
			
		||||
            .map_err(|err| FromSqlError::Other(Box::new(err)))
 | 
			
		||||
            ValueRef::Text(s) => serde_json::from_str(s),
 | 
			
		||||
            ValueRef::Blob(b) => serde_json::from_slice(b),
 | 
			
		||||
            _ => return Err(FromSqlError::InvalidType),
 | 
			
		||||
        }.map_err(|err| FromSqlError::Other(Box::new(err)))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use Connection;
 | 
			
		||||
    use super::serde_json;
 | 
			
		||||
    use Connection;
 | 
			
		||||
 | 
			
		||||
    fn checked_memory_handle() -> Connection {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
@@ -43,14 +42,17 @@ mod test {
 | 
			
		||||
 | 
			
		||||
        let json = r#"{"foo": 13, "bar": "baz"}"#;
 | 
			
		||||
        let data: serde_json::Value = serde_json::from_str(json).unwrap();
 | 
			
		||||
        db.execute("INSERT INTO foo (t, b) VALUES (?, ?)",
 | 
			
		||||
                     &[&data, &json.as_bytes()])
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        db.execute(
 | 
			
		||||
            "INSERT INTO foo (t, b) VALUES (?, ?)",
 | 
			
		||||
            &[&data, &json.as_bytes()],
 | 
			
		||||
        ).unwrap();
 | 
			
		||||
 | 
			
		||||
        let t: serde_json::Value = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let t: serde_json::Value = db
 | 
			
		||||
            .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(data, t);
 | 
			
		||||
        let b: serde_json::Value = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
 | 
			
		||||
        let b: serde_json::Value = db
 | 
			
		||||
            .query_row("SELECT b FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        assert_eq!(data, b);
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
extern crate time;
 | 
			
		||||
 | 
			
		||||
use Result;
 | 
			
		||||
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
 | 
			
		||||
use Result;
 | 
			
		||||
 | 
			
		||||
const SQLITE_DATETIME_FMT: &str = "%Y-%m-%dT%H:%M:%S.%fZ";
 | 
			
		||||
const SQLITE_DATETIME_FMT_LEGACY: &str = "%Y-%m-%d %H:%M:%S:%f %Z";
 | 
			
		||||
@@ -21,18 +21,18 @@ impl FromSql for time::Timespec {
 | 
			
		||||
        value
 | 
			
		||||
            .as_str()
 | 
			
		||||
            .and_then(|s| {
 | 
			
		||||
                time::strptime(s, SQLITE_DATETIME_FMT)
 | 
			
		||||
                    .or_else(|err| {
 | 
			
		||||
                        time::strptime(s, SQLITE_DATETIME_FMT_LEGACY)
 | 
			
		||||
                            .or_else(|_| Err(FromSqlError::Other(Box::new(err))))})})
 | 
			
		||||
            .map(|tm| tm.to_timespec())
 | 
			
		||||
                time::strptime(s, SQLITE_DATETIME_FMT).or_else(|err| {
 | 
			
		||||
                    time::strptime(s, SQLITE_DATETIME_FMT_LEGACY)
 | 
			
		||||
                        .or_else(|_| Err(FromSqlError::Other(Box::new(err))))
 | 
			
		||||
                })
 | 
			
		||||
            }).map(|tm| tm.to_timespec())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use Connection;
 | 
			
		||||
    use super::time;
 | 
			
		||||
    use Connection;
 | 
			
		||||
 | 
			
		||||
    fn checked_memory_handle() -> Connection {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
@@ -47,25 +47,23 @@ mod test {
 | 
			
		||||
 | 
			
		||||
        let mut ts_vec = vec![];
 | 
			
		||||
 | 
			
		||||
        ts_vec.push(time::Timespec::new(10_000, 0));//January 1, 1970 2:46:40 AM
 | 
			
		||||
        ts_vec.push(time::Timespec::new(10_000, 1000));//January 1, 1970 2:46:40 AM (and one microsecond)
 | 
			
		||||
        ts_vec.push(time::Timespec::new(1500391124, 1_000_000));//July 18, 2017
 | 
			
		||||
        ts_vec.push(time::Timespec::new(2000000000, 2_000_000));//May 18, 2033
 | 
			
		||||
        ts_vec.push(time::Timespec::new(3000000000, 999_999_999));//January 24, 2065
 | 
			
		||||
        ts_vec.push(time::Timespec::new(10000000000, 0));//November 20, 2286
 | 
			
		||||
        ts_vec.push(time::Timespec::new(10_000, 0)); //January 1, 1970 2:46:40 AM
 | 
			
		||||
        ts_vec.push(time::Timespec::new(10_000, 1000)); //January 1, 1970 2:46:40 AM (and one microsecond)
 | 
			
		||||
        ts_vec.push(time::Timespec::new(1500391124, 1_000_000)); //July 18, 2017
 | 
			
		||||
        ts_vec.push(time::Timespec::new(2000000000, 2_000_000)); //May 18, 2033
 | 
			
		||||
        ts_vec.push(time::Timespec::new(3000000000, 999_999_999)); //January 24, 2065
 | 
			
		||||
        ts_vec.push(time::Timespec::new(10000000000, 0)); //November 20, 2286
 | 
			
		||||
 | 
			
		||||
        for ts in ts_vec {
 | 
			
		||||
            db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap();
 | 
			
		||||
 | 
			
		||||
            db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
 | 
			
		||||
                .unwrap();
 | 
			
		||||
 | 
			
		||||
            let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
            let from: time::Timespec = db
 | 
			
		||||
                .query_row("SELECT t FROM foo", &[], |r| r.get(0))
 | 
			
		||||
                .unwrap();
 | 
			
		||||
 | 
			
		||||
            db.execute("DELETE FROM foo", &[]).unwrap();
 | 
			
		||||
 | 
			
		||||
            assert_eq!(from, ts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,11 @@
 | 
			
		||||
use super::{Null, Value, ValueRef};
 | 
			
		||||
use std::borrow::Cow;
 | 
			
		||||
#[cfg(feature = "array")]
 | 
			
		||||
use vtab::array::Array;
 | 
			
		||||
use Result;
 | 
			
		||||
 | 
			
		||||
/// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait.
 | 
			
		||||
#[derive(Clone,Debug,PartialEq)]
 | 
			
		||||
#[derive(Clone, Debug, PartialEq)]
 | 
			
		||||
pub enum ToSqlOutput<'a> {
 | 
			
		||||
    /// A borrowed SQLite-representable value.
 | 
			
		||||
    Borrowed(ValueRef<'a>),
 | 
			
		||||
@@ -13,12 +16,16 @@ pub enum ToSqlOutput<'a> {
 | 
			
		||||
    /// A BLOB of the given length that is filled with zeroes.
 | 
			
		||||
    #[cfg(feature = "blob")]
 | 
			
		||||
    ZeroBlob(i32),
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "array")]
 | 
			
		||||
    Array(Array),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Generically allow any type that can be converted into a ValueRef
 | 
			
		||||
// to be converted into a ToSqlOutput as well.
 | 
			
		||||
impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
 | 
			
		||||
    where &'a T: Into<ValueRef<'a>>
 | 
			
		||||
where
 | 
			
		||||
    &'a T: Into<ValueRef<'a>>,
 | 
			
		||||
{
 | 
			
		||||
    fn from(t: &'a T) -> Self {
 | 
			
		||||
        ToSqlOutput::Borrowed(t.into())
 | 
			
		||||
@@ -54,12 +61,14 @@ from_value!(Vec<u8>);
 | 
			
		||||
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)),
 | 
			
		||||
            ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
 | 
			
		||||
            ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
 | 
			
		||||
 | 
			
		||||
               #[cfg(feature = "blob")]
 | 
			
		||||
            #[cfg(feature = "blob")]
 | 
			
		||||
            ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
 | 
			
		||||
           })
 | 
			
		||||
            #[cfg(feature = "array")]
 | 
			
		||||
            ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -103,7 +112,8 @@ to_sql_self!(u32);
 | 
			
		||||
to_sql_self!(f64);
 | 
			
		||||
 | 
			
		||||
impl<'a, T: ?Sized> ToSql for &'a T
 | 
			
		||||
    where &'a T: Into<ToSqlOutput<'a>>
 | 
			
		||||
where
 | 
			
		||||
    &'a T: Into<ToSqlOutput<'a>>,
 | 
			
		||||
{
 | 
			
		||||
    fn to_sql(&self) -> Result<ToSqlOutput> {
 | 
			
		||||
        Ok((*self).into())
 | 
			
		||||
@@ -149,6 +159,12 @@ impl<T: ToSql> ToSql for Option<T> {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> ToSql for Cow<'a, str> {
 | 
			
		||||
    fn to_sql(&self) -> Result<ToSqlOutput> {
 | 
			
		||||
        Ok(ToSqlOutput::from(self.as_ref()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use super::ToSql;
 | 
			
		||||
@@ -165,4 +181,16 @@ mod test {
 | 
			
		||||
        is_to_sql::<u16>();
 | 
			
		||||
        is_to_sql::<u32>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_cow_str() {
 | 
			
		||||
        use std::borrow::Cow;
 | 
			
		||||
        let s = "str";
 | 
			
		||||
        let cow = Cow::Borrowed(s);
 | 
			
		||||
        let r = cow.to_sql();
 | 
			
		||||
        assert!(r.is_ok());
 | 
			
		||||
        let cow = Cow::Owned::<str>(String::from(s));
 | 
			
		||||
        let r = cow.to_sql();
 | 
			
		||||
        assert!(r.is_ok());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ use super::{Null, Type};
 | 
			
		||||
/// dictated by SQLite (not by the caller).
 | 
			
		||||
///
 | 
			
		||||
/// See [`ValueRef`](enum.ValueRef.html) for a non-owning dynamic type value.
 | 
			
		||||
#[derive(Clone,Debug,PartialEq)]
 | 
			
		||||
#[derive(Clone, Debug, PartialEq)]
 | 
			
		||||
pub enum Value {
 | 
			
		||||
    /// The value is a `NULL` value.
 | 
			
		||||
    Null,
 | 
			
		||||
@@ -31,7 +31,9 @@ impl From<bool> for Value {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<isize> for Value {
 | 
			
		||||
    fn from(i: isize) -> Value { Value::Integer(i as i64) }
 | 
			
		||||
    fn from(i: isize) -> Value {
 | 
			
		||||
        Value::Integer(i as i64)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! from_i64(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
use super::{Type, Value};
 | 
			
		||||
use types::{FromSqlError, FromSqlResult};
 | 
			
		||||
use super::{Value, Type};
 | 
			
		||||
 | 
			
		||||
/// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the
 | 
			
		||||
/// memory backing this value is owned by SQLite.
 | 
			
		||||
///
 | 
			
		||||
/// See [`Value`](enum.Value.html) for an owning dynamic type value.
 | 
			
		||||
#[derive(Copy,Clone,Debug,PartialEq)]
 | 
			
		||||
#[derive(Copy, Clone, Debug, PartialEq)]
 | 
			
		||||
pub enum ValueRef<'a> {
 | 
			
		||||
    /// The value is a `NULL` value.
 | 
			
		||||
    Null,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
//! [Unlock Notification](http://sqlite.org/unlock_notify.html)
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "unlock_notify")]
 | 
			
		||||
use std::sync::{Condvar, Mutex};
 | 
			
		||||
use std::os::raw::c_int;
 | 
			
		||||
#[cfg(feature = "unlock_notify")]
 | 
			
		||||
use std::os::raw::c_void;
 | 
			
		||||
#[cfg(feature = "unlock_notify")]
 | 
			
		||||
use std::sync::{Condvar, Mutex};
 | 
			
		||||
 | 
			
		||||
use ffi;
 | 
			
		||||
 | 
			
		||||
@@ -49,10 +49,9 @@ unsafe extern "C" fn unlock_notify_cb(ap_arg: *mut *mut c_void, n_arg: c_int) {
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "unlock_notify")]
 | 
			
		||||
pub fn is_locked(db: *mut ffi::sqlite3, rc: c_int) -> bool {
 | 
			
		||||
    rc == ffi::SQLITE_LOCKED_SHAREDCACHE || (rc & 0xFF) == ffi::SQLITE_LOCKED && unsafe {
 | 
			
		||||
        ffi::sqlite3_extended_errcode(db)
 | 
			
		||||
    }
 | 
			
		||||
        == ffi::SQLITE_LOCKED_SHAREDCACHE
 | 
			
		||||
    rc == ffi::SQLITE_LOCKED_SHAREDCACHE
 | 
			
		||||
        || (rc & 0xFF) == ffi::SQLITE_LOCKED
 | 
			
		||||
            && unsafe { ffi::sqlite3_extended_errcode(db) } == ffi::SQLITE_LOCKED_SHAREDCACHE
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// This function assumes that an SQLite API call (either `sqlite3_prepare_v2()`
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										194
									
								
								src/vtab/array.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								src/vtab/array.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,194 @@
 | 
			
		||||
//! Array Virtual Table.
 | 
			
		||||
//!
 | 
			
		||||
//! Port of [carray](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/carray.c) C extension.
 | 
			
		||||
use std::default::Default;
 | 
			
		||||
use std::os::raw::{c_char, c_int, c_void};
 | 
			
		||||
use std::rc::Rc;
 | 
			
		||||
 | 
			
		||||
use ffi;
 | 
			
		||||
use types::{ToSql, ToSqlOutput, Value};
 | 
			
		||||
use vtab::{
 | 
			
		||||
    eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection,
 | 
			
		||||
    VTabCursor, Values,
 | 
			
		||||
};
 | 
			
		||||
use {Connection, Result};
 | 
			
		||||
 | 
			
		||||
// http://sqlite.org/bindptr.html
 | 
			
		||||
 | 
			
		||||
pub(crate) const ARRAY_TYPE: *const c_char = b"rarray\0" as *const u8 as *const c_char;
 | 
			
		||||
 | 
			
		||||
pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
 | 
			
		||||
    let _: Array = Rc::from_raw(p as *const Vec<Value>);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type Array = Rc<Vec<Value>>;
 | 
			
		||||
 | 
			
		||||
impl ToSql for Array {
 | 
			
		||||
    fn to_sql(&self) -> Result<ToSqlOutput> {
 | 
			
		||||
        Ok(ToSqlOutput::Array(self.clone()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Register the "rarray" module.
 | 
			
		||||
pub fn load_module(conn: &Connection) -> Result<()> {
 | 
			
		||||
    let aux: Option<()> = None;
 | 
			
		||||
    conn.create_module("rarray", &ARRAY_MODULE, aux)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lazy_static! {
 | 
			
		||||
    static ref ARRAY_MODULE: Module<ArrayTab> = eponymous_only_module::<ArrayTab>(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Column numbers
 | 
			
		||||
// const CARRAY_COLUMN_VALUE : c_int = 0;
 | 
			
		||||
const CARRAY_COLUMN_POINTER: c_int = 1;
 | 
			
		||||
 | 
			
		||||
/// An instance of the Array virtual table
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
struct ArrayTab {
 | 
			
		||||
    /// Base class. Must be first
 | 
			
		||||
    base: ffi::sqlite3_vtab,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VTab for ArrayTab {
 | 
			
		||||
    type Aux = ();
 | 
			
		||||
    type Cursor = ArrayTabCursor;
 | 
			
		||||
 | 
			
		||||
    fn connect(
 | 
			
		||||
        _: &mut VTabConnection,
 | 
			
		||||
        _aux: Option<&()>,
 | 
			
		||||
        _args: &[&[u8]],
 | 
			
		||||
    ) -> Result<(String, ArrayTab)> {
 | 
			
		||||
        let vtab = ArrayTab {
 | 
			
		||||
            base: ffi::sqlite3_vtab::default(),
 | 
			
		||||
        };
 | 
			
		||||
        Ok(("CREATE TABLE x(value,pointer hidden)".to_owned(), vtab))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
 | 
			
		||||
        // Index of the pointer= constraint
 | 
			
		||||
        let mut ptr_idx = None;
 | 
			
		||||
        for (i, constraint) in info.constraints().enumerate() {
 | 
			
		||||
            if !constraint.is_usable() {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            if let CARRAY_COLUMN_POINTER = constraint.column() {
 | 
			
		||||
                ptr_idx = Some(i);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(ptr_idx) = ptr_idx {
 | 
			
		||||
            {
 | 
			
		||||
                let mut constraint_usage = info.constraint_usage(ptr_idx);
 | 
			
		||||
                constraint_usage.set_argv_index(1);
 | 
			
		||||
                constraint_usage.set_omit(true);
 | 
			
		||||
            }
 | 
			
		||||
            info.set_estimated_cost(1f64);
 | 
			
		||||
            info.set_estimated_rows(100);
 | 
			
		||||
            info.set_idx_num(1);
 | 
			
		||||
        } else {
 | 
			
		||||
            info.set_estimated_cost(2_147_483_647f64);
 | 
			
		||||
            info.set_estimated_rows(2_147_483_647);
 | 
			
		||||
            info.set_idx_num(0);
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn open(&self) -> Result<ArrayTabCursor> {
 | 
			
		||||
        Ok(ArrayTabCursor::new())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A cursor for the Array virtual table
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
struct ArrayTabCursor {
 | 
			
		||||
    /// Base class. Must be first
 | 
			
		||||
    base: ffi::sqlite3_vtab_cursor,
 | 
			
		||||
    /// The rowid
 | 
			
		||||
    row_id: i64,
 | 
			
		||||
    /// Pointer to the array of values ("pointer")
 | 
			
		||||
    ptr: Option<Array>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ArrayTabCursor {
 | 
			
		||||
    fn new() -> ArrayTabCursor {
 | 
			
		||||
        ArrayTabCursor {
 | 
			
		||||
            base: ffi::sqlite3_vtab_cursor::default(),
 | 
			
		||||
            row_id: 0,
 | 
			
		||||
            ptr: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn len(&self) -> i64 {
 | 
			
		||||
        match self.ptr {
 | 
			
		||||
            Some(ref a) => a.len() as i64,
 | 
			
		||||
            _ => 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl VTabCursor for ArrayTabCursor {
 | 
			
		||||
    fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values) -> Result<()> {
 | 
			
		||||
        if idx_num > 0 {
 | 
			
		||||
            self.ptr = try!(args.get_array(0));
 | 
			
		||||
        } else {
 | 
			
		||||
            self.ptr = None;
 | 
			
		||||
        }
 | 
			
		||||
        self.row_id = 1;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    fn next(&mut self) -> Result<()> {
 | 
			
		||||
        self.row_id += 1;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    fn eof(&self) -> bool {
 | 
			
		||||
        self.row_id > self.len()
 | 
			
		||||
    }
 | 
			
		||||
    fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
 | 
			
		||||
        match i {
 | 
			
		||||
            CARRAY_COLUMN_POINTER => Ok(()),
 | 
			
		||||
            _ => {
 | 
			
		||||
                if let Some(ref array) = self.ptr {
 | 
			
		||||
                    let value = &array[(self.row_id - 1) as usize];
 | 
			
		||||
                    ctx.set_result(&value)
 | 
			
		||||
                } else {
 | 
			
		||||
                    Ok(())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn rowid(&self) -> Result<i64> {
 | 
			
		||||
        Ok(self.row_id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use std::rc::Rc;
 | 
			
		||||
    use types::Value;
 | 
			
		||||
    use vtab::array;
 | 
			
		||||
    use Connection;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_array_module() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
        array::load_module(&db).unwrap();
 | 
			
		||||
 | 
			
		||||
        let v = vec![1i64, 2, 3, 4];
 | 
			
		||||
        let values = v.into_iter().map(|i| Value::from(i)).collect();
 | 
			
		||||
        let ptr = Rc::new(values);
 | 
			
		||||
        {
 | 
			
		||||
            let mut stmt = db.prepare("SELECT value from rarray(?);").unwrap();
 | 
			
		||||
 | 
			
		||||
            let rows = stmt.query_map(&[&ptr], |row| row.get::<_, i64>(0)).unwrap();
 | 
			
		||||
            assert_eq!(2, Rc::strong_count(&ptr));
 | 
			
		||||
            let mut count = 0;
 | 
			
		||||
            for (i, value) in rows.enumerate() {
 | 
			
		||||
                assert_eq!(i as i64, value.unwrap() - 1);
 | 
			
		||||
                count += 1;
 | 
			
		||||
            }
 | 
			
		||||
            assert_eq!(4, count);
 | 
			
		||||
        }
 | 
			
		||||
        assert_eq!(1, Rc::strong_count(&ptr));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										386
									
								
								src/vtab/csvtab.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								src/vtab/csvtab.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,386 @@
 | 
			
		||||
//! CSV Virtual Table.
 | 
			
		||||
//!
 | 
			
		||||
//! Port of [csv](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/csv.c) C extension.
 | 
			
		||||
extern crate csv;
 | 
			
		||||
use std::fs::File;
 | 
			
		||||
use std::os::raw::c_int;
 | 
			
		||||
use std::path::Path;
 | 
			
		||||
use std::result;
 | 
			
		||||
use std::str;
 | 
			
		||||
 | 
			
		||||
use ffi;
 | 
			
		||||
use types::Null;
 | 
			
		||||
use vtab::{
 | 
			
		||||
    dequote, escape_double_quote, parse_boolean, read_only_module, Context, CreateVTab, IndexInfo,
 | 
			
		||||
    Module, VTab, VTabConnection, VTabCursor, Values,
 | 
			
		||||
};
 | 
			
		||||
use {Connection, Error, Result};
 | 
			
		||||
 | 
			
		||||
/// Register the "csv" module.
 | 
			
		||||
/// ```sql
 | 
			
		||||
/// CREATE VIRTUAL TABLE vtab USING csv(
 | 
			
		||||
///   filename=FILENAME -- Name of file containing CSV content
 | 
			
		||||
///   [, schema=SCHEMA] -- Alternative CSV schema. 'CREATE TABLE x(col1 TEXT NOT NULL, col2 INT, ...);'
 | 
			
		||||
///   [, header=YES|NO] -- First row of CSV defines the names of columns if "yes". Default "no".
 | 
			
		||||
///   [, columns=N] -- Assume the CSV file contains N columns.
 | 
			
		||||
///   [, delimiter=C] -- CSV delimiter. Default ','.
 | 
			
		||||
///   [, quote=C] -- CSV quote. Default '"'. 0 means no quote.
 | 
			
		||||
/// );
 | 
			
		||||
/// ```
 | 
			
		||||
pub fn load_module(conn: &Connection) -> Result<()> {
 | 
			
		||||
    let aux: Option<()> = None;
 | 
			
		||||
    conn.create_module("csv", &CSV_MODULE, aux)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lazy_static! {
 | 
			
		||||
    static ref CSV_MODULE: Module<CSVTab> = read_only_module::<CSVTab>(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// An instance of the CSV virtual table
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
struct CSVTab {
 | 
			
		||||
    /// Base class. Must be first
 | 
			
		||||
    base: ffi::sqlite3_vtab,
 | 
			
		||||
    /// Name of the CSV file
 | 
			
		||||
    filename: String,
 | 
			
		||||
    has_headers: bool,
 | 
			
		||||
    delimiter: u8,
 | 
			
		||||
    quote: u8,
 | 
			
		||||
    /// Offset to start of data
 | 
			
		||||
    offset_first_row: csv::Position,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CSVTab {
 | 
			
		||||
    fn reader(&self) -> result::Result<csv::Reader<File>, csv::Error> {
 | 
			
		||||
        csv::ReaderBuilder::new()
 | 
			
		||||
            .has_headers(self.has_headers)
 | 
			
		||||
            .delimiter(self.delimiter)
 | 
			
		||||
            .quote(self.quote)
 | 
			
		||||
            .from_path(&self.filename)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn parameter(c_slice: &[u8]) -> Result<(&str, &str)> {
 | 
			
		||||
        let arg = try!(str::from_utf8(c_slice)).trim();
 | 
			
		||||
        let mut split = arg.split('=');
 | 
			
		||||
        if let Some(key) = split.next() {
 | 
			
		||||
            if let Some(value) = split.next() {
 | 
			
		||||
                let param = key.trim();
 | 
			
		||||
                let value = dequote(value);
 | 
			
		||||
                return Ok((param, value));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Err(Error::ModuleError(format!("illegal argument: '{}'", arg)))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn parse_byte(arg: &str) -> Option<u8> {
 | 
			
		||||
        if arg.len() == 1 {
 | 
			
		||||
            arg.bytes().next()
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VTab for CSVTab {
 | 
			
		||||
    type Aux = ();
 | 
			
		||||
    type Cursor = CSVTabCursor;
 | 
			
		||||
 | 
			
		||||
    fn connect(
 | 
			
		||||
        _: &mut VTabConnection,
 | 
			
		||||
        _aux: Option<&()>,
 | 
			
		||||
        args: &[&[u8]],
 | 
			
		||||
    ) -> Result<(String, CSVTab)> {
 | 
			
		||||
        if args.len() < 4 {
 | 
			
		||||
            return Err(Error::ModuleError("no CSV file specified".to_owned()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut vtab = CSVTab {
 | 
			
		||||
            base: ffi::sqlite3_vtab::default(),
 | 
			
		||||
            filename: "".to_owned(),
 | 
			
		||||
            has_headers: false,
 | 
			
		||||
            delimiter: b',',
 | 
			
		||||
            quote: b'"',
 | 
			
		||||
            offset_first_row: csv::Position::new(),
 | 
			
		||||
        };
 | 
			
		||||
        let mut schema = None;
 | 
			
		||||
        let mut n_col = None;
 | 
			
		||||
 | 
			
		||||
        let args = &args[3..];
 | 
			
		||||
        for c_slice in args {
 | 
			
		||||
            let (param, value) = try!(CSVTab::parameter(c_slice));
 | 
			
		||||
            match param {
 | 
			
		||||
                "filename" => {
 | 
			
		||||
                    if !Path::new(value).exists() {
 | 
			
		||||
                        return Err(Error::ModuleError(format!(
 | 
			
		||||
                            "file '{}' does not exist",
 | 
			
		||||
                            value
 | 
			
		||||
                        )));
 | 
			
		||||
                    }
 | 
			
		||||
                    vtab.filename = value.to_owned();
 | 
			
		||||
                }
 | 
			
		||||
                "schema" => {
 | 
			
		||||
                    schema = Some(value.to_owned());
 | 
			
		||||
                }
 | 
			
		||||
                "columns" => {
 | 
			
		||||
                    if let Ok(n) = value.parse::<u16>() {
 | 
			
		||||
                        if n_col.is_some() {
 | 
			
		||||
                            return Err(Error::ModuleError(
 | 
			
		||||
                                "more than one 'columns' parameter".to_owned(),
 | 
			
		||||
                            ));
 | 
			
		||||
                        } else if n == 0 {
 | 
			
		||||
                            return Err(Error::ModuleError(
 | 
			
		||||
                                "must have at least one column".to_owned(),
 | 
			
		||||
                            ));
 | 
			
		||||
                        }
 | 
			
		||||
                        n_col = Some(n);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return Err(Error::ModuleError(format!(
 | 
			
		||||
                            "unrecognized argument to 'columns': {}",
 | 
			
		||||
                            value
 | 
			
		||||
                        )));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                "header" => {
 | 
			
		||||
                    if let Some(b) = parse_boolean(value) {
 | 
			
		||||
                        vtab.has_headers = b;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return Err(Error::ModuleError(format!(
 | 
			
		||||
                            "unrecognized argument to 'header': {}",
 | 
			
		||||
                            value
 | 
			
		||||
                        )));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                "delimiter" => {
 | 
			
		||||
                    if let Some(b) = CSVTab::parse_byte(value) {
 | 
			
		||||
                        vtab.delimiter = b;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return Err(Error::ModuleError(format!(
 | 
			
		||||
                            "unrecognized argument to 'delimiter': {}",
 | 
			
		||||
                            value
 | 
			
		||||
                        )));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                "quote" => {
 | 
			
		||||
                    if let Some(b) = CSVTab::parse_byte(value) {
 | 
			
		||||
                        if b == b'0' {
 | 
			
		||||
                            vtab.quote = 0;
 | 
			
		||||
                        } else {
 | 
			
		||||
                            vtab.quote = b;
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return Err(Error::ModuleError(format!(
 | 
			
		||||
                            "unrecognized argument to 'quote': {}",
 | 
			
		||||
                            value
 | 
			
		||||
                        )));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                _ => {
 | 
			
		||||
                    return Err(Error::ModuleError(format!(
 | 
			
		||||
                        "unrecognized parameter '{}'",
 | 
			
		||||
                        param
 | 
			
		||||
                    )));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if vtab.filename.is_empty() {
 | 
			
		||||
            return Err(Error::ModuleError("no CSV file specified".to_owned()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut cols: Vec<String> = Vec::new();
 | 
			
		||||
        if vtab.has_headers || (n_col.is_none() && schema.is_none()) {
 | 
			
		||||
            let mut reader = try!(vtab.reader());
 | 
			
		||||
            if vtab.has_headers {
 | 
			
		||||
                {
 | 
			
		||||
                    let headers = try!(reader.headers());
 | 
			
		||||
                    // headers ignored if cols is not empty
 | 
			
		||||
                    if n_col.is_none() && schema.is_none() {
 | 
			
		||||
                        cols = headers
 | 
			
		||||
                            .into_iter()
 | 
			
		||||
                            .map(|header| escape_double_quote(&header).into_owned())
 | 
			
		||||
                            .collect();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                vtab.offset_first_row = reader.position().clone();
 | 
			
		||||
            } else {
 | 
			
		||||
                let mut record = csv::ByteRecord::new();
 | 
			
		||||
                if try!(reader.read_byte_record(&mut record)) {
 | 
			
		||||
                    for (i, _) in record.iter().enumerate() {
 | 
			
		||||
                        cols.push(format!("c{}", i));
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else if let Some(n_col) = n_col {
 | 
			
		||||
            for i in 0..n_col {
 | 
			
		||||
                cols.push(format!("c{}", i));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if cols.is_empty() && schema.is_none() {
 | 
			
		||||
            return Err(Error::ModuleError("no column specified".to_owned()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if schema.is_none() {
 | 
			
		||||
            let mut sql = String::from("CREATE TABLE x(");
 | 
			
		||||
            for (i, col) in cols.iter().enumerate() {
 | 
			
		||||
                sql.push('"');
 | 
			
		||||
                sql.push_str(col);
 | 
			
		||||
                sql.push_str("\" TEXT");
 | 
			
		||||
                if i == cols.len() - 1 {
 | 
			
		||||
                    sql.push_str(");");
 | 
			
		||||
                } else {
 | 
			
		||||
                    sql.push_str(", ");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            schema = Some(sql);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok((schema.unwrap().to_owned(), vtab))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Only a forward full table scan is supported.
 | 
			
		||||
    fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
 | 
			
		||||
        info.set_estimated_cost(1_000_000.);
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn open(&self) -> Result<CSVTabCursor> {
 | 
			
		||||
        Ok(CSVTabCursor::new(try!(self.reader())))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CreateVTab for CSVTab {}
 | 
			
		||||
 | 
			
		||||
/// A cursor for the CSV virtual table
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
struct CSVTabCursor {
 | 
			
		||||
    /// Base class. Must be first
 | 
			
		||||
    base: ffi::sqlite3_vtab_cursor,
 | 
			
		||||
    /// The CSV reader object
 | 
			
		||||
    reader: csv::Reader<File>,
 | 
			
		||||
    /// Current cursor position used as rowid
 | 
			
		||||
    row_number: usize,
 | 
			
		||||
    /// Values of the current row
 | 
			
		||||
    cols: csv::StringRecord,
 | 
			
		||||
    eof: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl CSVTabCursor {
 | 
			
		||||
    fn new(reader: csv::Reader<File>) -> CSVTabCursor {
 | 
			
		||||
        CSVTabCursor {
 | 
			
		||||
            base: ffi::sqlite3_vtab_cursor::default(),
 | 
			
		||||
            reader,
 | 
			
		||||
            row_number: 0,
 | 
			
		||||
            cols: csv::StringRecord::new(),
 | 
			
		||||
            eof: false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Accessor to the associated virtual table.
 | 
			
		||||
    fn vtab(&self) -> &CSVTab {
 | 
			
		||||
        unsafe { &*(self.base.pVtab as *const CSVTab) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VTabCursor for CSVTabCursor {
 | 
			
		||||
    // Only a full table scan is supported.  So `filter` simply rewinds to
 | 
			
		||||
    // the beginning.
 | 
			
		||||
    fn filter(&mut self, _idx_num: c_int, _idx_str: Option<&str>, _args: &Values) -> Result<()> {
 | 
			
		||||
        {
 | 
			
		||||
            let offset_first_row = self.vtab().offset_first_row.clone();
 | 
			
		||||
            try!(self.reader.seek(offset_first_row));
 | 
			
		||||
        }
 | 
			
		||||
        self.row_number = 0;
 | 
			
		||||
        self.next()
 | 
			
		||||
    }
 | 
			
		||||
    fn next(&mut self) -> Result<()> {
 | 
			
		||||
        {
 | 
			
		||||
            self.eof = self.reader.is_done();
 | 
			
		||||
            if self.eof {
 | 
			
		||||
                return Ok(());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            self.eof = !try!(self.reader.read_record(&mut self.cols));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        self.row_number += 1;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    fn eof(&self) -> bool {
 | 
			
		||||
        self.eof
 | 
			
		||||
    }
 | 
			
		||||
    fn column(&self, ctx: &mut Context, col: c_int) -> Result<()> {
 | 
			
		||||
        if col < 0 || col as usize >= self.cols.len() {
 | 
			
		||||
            return Err(Error::ModuleError(format!(
 | 
			
		||||
                "column index out of bounds: {}",
 | 
			
		||||
                col
 | 
			
		||||
            )));
 | 
			
		||||
        }
 | 
			
		||||
        if self.cols.is_empty() {
 | 
			
		||||
            return ctx.set_result(&Null);
 | 
			
		||||
        }
 | 
			
		||||
        // TODO Affinity
 | 
			
		||||
        ctx.set_result(&self.cols[col as usize].to_owned())
 | 
			
		||||
    }
 | 
			
		||||
    fn rowid(&self) -> Result<i64> {
 | 
			
		||||
        Ok(self.row_number as i64)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<csv::Error> for Error {
 | 
			
		||||
    fn from(err: csv::Error) -> Error {
 | 
			
		||||
        use std::error::Error as StdError;
 | 
			
		||||
        Error::ModuleError(String::from(err.description()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use vtab::csvtab;
 | 
			
		||||
    use {Connection, Result};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_csv_module() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
        csvtab::load_module(&db).unwrap();
 | 
			
		||||
        db.execute_batch("CREATE VIRTUAL TABLE vtab USING csv(filename='test.csv', header=yes)")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let mut s = db.prepare("SELECT rowid, * FROM vtab").unwrap();
 | 
			
		||||
            {
 | 
			
		||||
                let headers = s.column_names();
 | 
			
		||||
                assert_eq!(vec!["rowid", "colA", "colB", "colC"], headers);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            let ids: Result<Vec<i32>> = s
 | 
			
		||||
                .query_map(&[], |row| row.get::<_, i32>(0))
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .collect();
 | 
			
		||||
            let sum = ids.unwrap().iter().fold(0, |acc, &id| acc + id);
 | 
			
		||||
            assert_eq!(sum, 15);
 | 
			
		||||
        }
 | 
			
		||||
        db.execute_batch("DROP TABLE vtab").unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_csv_cursor() {
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
        csvtab::load_module(&db).unwrap();
 | 
			
		||||
        db.execute_batch("CREATE VIRTUAL TABLE vtab USING csv(filename='test.csv', header=yes)")
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            let mut s = db
 | 
			
		||||
                .prepare(
 | 
			
		||||
                    "SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \
 | 
			
		||||
                     v1.rowid < v2.rowid",
 | 
			
		||||
                ).unwrap();
 | 
			
		||||
 | 
			
		||||
            let mut rows = s.query(&[]).unwrap();
 | 
			
		||||
            let row = rows.next().unwrap().unwrap();
 | 
			
		||||
            assert_eq!(row.get::<_, i32>(0), 2);
 | 
			
		||||
        }
 | 
			
		||||
        db.execute_batch("DROP TABLE vtab").unwrap();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										991
									
								
								src/vtab/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										991
									
								
								src/vtab/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,991 @@
 | 
			
		||||
//! Create virtual tables.
 | 
			
		||||
//!
 | 
			
		||||
//! Follow these steps to create your own virtual table:
 | 
			
		||||
//! 1. Write implemenation of `VTab` and `VTabCursor` traits.
 | 
			
		||||
//! 2. Create an instance of the `Module` structure specialized for `VTab` impl. from step 1.
 | 
			
		||||
//! 3. Register your `Module` structure using `Connection.create_module`.
 | 
			
		||||
//! 4. Run a `CREATE VIRTUAL TABLE` command that specifies the new module in the `USING` clause.
 | 
			
		||||
//!
 | 
			
		||||
//! (See [SQLite doc](http://sqlite.org/vtab.html))
 | 
			
		||||
use std::borrow::Cow::{self, Borrowed, Owned};
 | 
			
		||||
use std::ffi::CString;
 | 
			
		||||
use std::marker::PhantomData;
 | 
			
		||||
use std::marker::Sync;
 | 
			
		||||
use std::os::raw::{c_char, c_int, c_void};
 | 
			
		||||
use std::ptr;
 | 
			
		||||
use std::slice;
 | 
			
		||||
 | 
			
		||||
use context::set_result;
 | 
			
		||||
use error::error_from_sqlite_code;
 | 
			
		||||
use ffi;
 | 
			
		||||
pub use ffi::{sqlite3_vtab, sqlite3_vtab_cursor};
 | 
			
		||||
use types::{FromSql, FromSqlError, ToSql, ValueRef};
 | 
			
		||||
use {str_to_cstring, Connection, Error, InnerConnection, Result};
 | 
			
		||||
 | 
			
		||||
// let conn: Connection = ...;
 | 
			
		||||
// let mod: Module = ...; // VTab builder
 | 
			
		||||
// conn.create_module("module", mod);
 | 
			
		||||
//
 | 
			
		||||
// conn.execute("CREATE VIRTUAL TABLE foo USING module(...)");
 | 
			
		||||
// \-> Module::xcreate
 | 
			
		||||
//  |-> let vtab: VTab = ...; // on the heap
 | 
			
		||||
//  \-> conn.declare_vtab("CREATE TABLE foo (...)");
 | 
			
		||||
// conn = Connection::open(...);
 | 
			
		||||
// \-> Module::xconnect
 | 
			
		||||
//  |-> let vtab: VTab = ...; // on the heap
 | 
			
		||||
//  \-> conn.declare_vtab("CREATE TABLE foo (...)");
 | 
			
		||||
//
 | 
			
		||||
// conn.close();
 | 
			
		||||
// \-> vtab.xdisconnect
 | 
			
		||||
// conn.execute("DROP TABLE foo");
 | 
			
		||||
// \-> vtab.xDestroy
 | 
			
		||||
//
 | 
			
		||||
// let stmt = conn.prepare("SELECT ... FROM foo WHERE ...");
 | 
			
		||||
// \-> vtab.xbestindex
 | 
			
		||||
// stmt.query().next();
 | 
			
		||||
// \-> vtab.xopen
 | 
			
		||||
//  |-> let cursor: VTabCursor = ...; // on the heap
 | 
			
		||||
//  |-> cursor.xfilter or xnext
 | 
			
		||||
//  |-> cursor.xeof
 | 
			
		||||
//  \-> if not eof { cursor.column or xrowid } else { cursor.xclose }
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
// db: *mut ffi::sqlite3 => VTabConnection
 | 
			
		||||
// module: *const ffi::sqlite3_module => Module
 | 
			
		||||
// aux: *mut c_void => Module::Aux
 | 
			
		||||
// ffi::sqlite3_vtab => VTab
 | 
			
		||||
// ffi::sqlite3_vtab_cursor => VTabCursor
 | 
			
		||||
 | 
			
		||||
/// Virtual table module
 | 
			
		||||
///
 | 
			
		||||
/// (See [SQLite doc](https://sqlite.org/c3ref/module.html))
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
pub struct Module<T: VTab> {
 | 
			
		||||
    base: ffi::sqlite3_module,
 | 
			
		||||
    phantom: PhantomData<T>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe impl<T: VTab> Sync for Module<T> {}
 | 
			
		||||
 | 
			
		||||
/// Create a read-only virtual table implementation.
 | 
			
		||||
///
 | 
			
		||||
/// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
 | 
			
		||||
pub fn read_only_module<T: CreateVTab>(version: c_int) -> Module<T> {
 | 
			
		||||
    // The xConnect and xCreate methods do the same thing, but they must be
 | 
			
		||||
    // different so that the virtual table is not an eponymous virtual table.
 | 
			
		||||
    let ffi_module = ffi::sqlite3_module {
 | 
			
		||||
        iVersion: version,
 | 
			
		||||
        xCreate: Some(rust_create::<T>),
 | 
			
		||||
        xConnect: Some(rust_connect::<T>),
 | 
			
		||||
        xBestIndex: Some(rust_best_index::<T>),
 | 
			
		||||
        xDisconnect: Some(rust_disconnect::<T>),
 | 
			
		||||
        xDestroy: Some(rust_destroy::<T>),
 | 
			
		||||
        xOpen: Some(rust_open::<T>),
 | 
			
		||||
        xClose: Some(rust_close::<T::Cursor>),
 | 
			
		||||
        xFilter: Some(rust_filter::<T::Cursor>),
 | 
			
		||||
        xNext: Some(rust_next::<T::Cursor>),
 | 
			
		||||
        xEof: Some(rust_eof::<T::Cursor>),
 | 
			
		||||
        xColumn: Some(rust_column::<T::Cursor>),
 | 
			
		||||
        xRowid: Some(rust_rowid::<T::Cursor>),
 | 
			
		||||
        xUpdate: None,
 | 
			
		||||
        xBegin: None,
 | 
			
		||||
        xSync: None,
 | 
			
		||||
        xCommit: None,
 | 
			
		||||
        xRollback: None,
 | 
			
		||||
        xFindFunction: None,
 | 
			
		||||
        xRename: None,
 | 
			
		||||
        xSavepoint: None,
 | 
			
		||||
        xRelease: None,
 | 
			
		||||
        xRollbackTo: None,
 | 
			
		||||
    };
 | 
			
		||||
    Module {
 | 
			
		||||
        base: ffi_module,
 | 
			
		||||
        phantom: PhantomData::<T>,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create an eponymous only virtual table implementation.
 | 
			
		||||
///
 | 
			
		||||
/// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
 | 
			
		||||
pub fn eponymous_only_module<T: VTab>(version: c_int) -> Module<T> {
 | 
			
		||||
    // A virtual table is eponymous if its xCreate method is the exact same function as the xConnect method
 | 
			
		||||
    // For eponymous-only virtual tables, the xCreate method is NULL
 | 
			
		||||
    let ffi_module = ffi::sqlite3_module {
 | 
			
		||||
        iVersion: version,
 | 
			
		||||
        xCreate: None,
 | 
			
		||||
        xConnect: Some(rust_connect::<T>),
 | 
			
		||||
        xBestIndex: Some(rust_best_index::<T>),
 | 
			
		||||
        xDisconnect: Some(rust_disconnect::<T>),
 | 
			
		||||
        xDestroy: None,
 | 
			
		||||
        xOpen: Some(rust_open::<T>),
 | 
			
		||||
        xClose: Some(rust_close::<T::Cursor>),
 | 
			
		||||
        xFilter: Some(rust_filter::<T::Cursor>),
 | 
			
		||||
        xNext: Some(rust_next::<T::Cursor>),
 | 
			
		||||
        xEof: Some(rust_eof::<T::Cursor>),
 | 
			
		||||
        xColumn: Some(rust_column::<T::Cursor>),
 | 
			
		||||
        xRowid: Some(rust_rowid::<T::Cursor>),
 | 
			
		||||
        xUpdate: None,
 | 
			
		||||
        xBegin: None,
 | 
			
		||||
        xSync: None,
 | 
			
		||||
        xCommit: None,
 | 
			
		||||
        xRollback: None,
 | 
			
		||||
        xFindFunction: None,
 | 
			
		||||
        xRename: None,
 | 
			
		||||
        xSavepoint: None,
 | 
			
		||||
        xRelease: None,
 | 
			
		||||
        xRollbackTo: None,
 | 
			
		||||
    };
 | 
			
		||||
    Module {
 | 
			
		||||
        base: ffi_module,
 | 
			
		||||
        phantom: PhantomData::<T>,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct VTabConnection(*mut ffi::sqlite3);
 | 
			
		||||
 | 
			
		||||
impl VTabConnection {
 | 
			
		||||
    // TODO sqlite3_vtab_config (http://sqlite.org/c3ref/vtab_config.html)
 | 
			
		||||
 | 
			
		||||
    // TODO sqlite3_vtab_on_conflict (http://sqlite.org/c3ref/vtab_on_conflict.html)
 | 
			
		||||
 | 
			
		||||
    /// Get access to the underlying SQLite database connection handle.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Warning
 | 
			
		||||
    ///
 | 
			
		||||
    /// You should not need to use this function. If you do need to, please [open an issue
 | 
			
		||||
    /// 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
 | 
			
		||||
    /// connection, and what you do with it could impact the safety of this `Connection`.
 | 
			
		||||
    pub unsafe fn handle(&mut self) -> *mut ffi::sqlite3 {
 | 
			
		||||
        self.0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Virtual table instance trait.
 | 
			
		||||
///
 | 
			
		||||
/// Implementations must be like:
 | 
			
		||||
/// ```rust,ignore
 | 
			
		||||
/// #[repr(C)]
 | 
			
		||||
/// struct MyTab {
 | 
			
		||||
///    /// Base class. Must be first
 | 
			
		||||
///    base: ffi::sqlite3_vtab,
 | 
			
		||||
///    /* Virtual table implementations will typically add additional fields */
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html))
 | 
			
		||||
pub trait VTab: Sized {
 | 
			
		||||
    type Aux;
 | 
			
		||||
    type Cursor: VTabCursor;
 | 
			
		||||
 | 
			
		||||
    /// Establish a new connection to an existing virtual table.
 | 
			
		||||
    ///
 | 
			
		||||
    /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xconnect_method))
 | 
			
		||||
    fn connect(
 | 
			
		||||
        db: &mut VTabConnection,
 | 
			
		||||
        aux: Option<&Self::Aux>,
 | 
			
		||||
        args: &[&[u8]],
 | 
			
		||||
    ) -> Result<(String, Self)>;
 | 
			
		||||
 | 
			
		||||
    /// Determine the best way to access the virtual table.
 | 
			
		||||
    /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xbestindex_method))
 | 
			
		||||
    fn best_index(&self, info: &mut IndexInfo) -> Result<()>;
 | 
			
		||||
 | 
			
		||||
    /// Create a new cursor used for accessing a virtual table.
 | 
			
		||||
    /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xopen_method))
 | 
			
		||||
    fn open(&self) -> Result<Self::Cursor>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Non-eponymous virtual table instance trait.
 | 
			
		||||
///
 | 
			
		||||
/// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html))
 | 
			
		||||
pub trait CreateVTab: VTab {
 | 
			
		||||
    /// 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 CREATE VIRTUAL TABLE statement.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Call `connect` by default.
 | 
			
		||||
    /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xcreate_method))
 | 
			
		||||
    fn create(
 | 
			
		||||
        db: &mut VTabConnection,
 | 
			
		||||
        aux: Option<&Self::Aux>,
 | 
			
		||||
        args: &[&[u8]],
 | 
			
		||||
    ) -> Result<(String, Self)> {
 | 
			
		||||
        Self::connect(db, aux, args)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Destroy the underlying table implementation. This method undoes the work of `create`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Do nothing by default.
 | 
			
		||||
    /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xdestroy_method))
 | 
			
		||||
    fn destroy(&self) -> Result<()> {
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bitflags! {
 | 
			
		||||
    #[doc = "Index constraint operator."]
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    pub struct IndexConstraintOp: ::std::os::raw::c_uchar {
 | 
			
		||||
        const SQLITE_INDEX_CONSTRAINT_EQ    = 2;
 | 
			
		||||
        const SQLITE_INDEX_CONSTRAINT_GT    = 4;
 | 
			
		||||
        const SQLITE_INDEX_CONSTRAINT_LE    = 8;
 | 
			
		||||
        const SQLITE_INDEX_CONSTRAINT_LT    = 16;
 | 
			
		||||
        const SQLITE_INDEX_CONSTRAINT_GE    = 32;
 | 
			
		||||
        const SQLITE_INDEX_CONSTRAINT_MATCH = 64;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Pass information into and receive the reply from the `VTab.best_index` method.
 | 
			
		||||
///
 | 
			
		||||
/// (See [SQLite doc](http://sqlite.org/c3ref/index_info.html))
 | 
			
		||||
pub struct IndexInfo(*mut ffi::sqlite3_index_info);
 | 
			
		||||
 | 
			
		||||
impl IndexInfo {
 | 
			
		||||
    /// Record WHERE clause constraints.
 | 
			
		||||
    pub fn constraints(&self) -> IndexConstraintIter {
 | 
			
		||||
        let constraints =
 | 
			
		||||
            unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
 | 
			
		||||
        IndexConstraintIter {
 | 
			
		||||
            iter: constraints.iter(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Information about the ORDER BY clause.
 | 
			
		||||
    pub fn order_bys(&self) -> OrderByIter {
 | 
			
		||||
        let order_bys =
 | 
			
		||||
            unsafe { slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize) };
 | 
			
		||||
        OrderByIter {
 | 
			
		||||
            iter: order_bys.iter(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Number of terms in the ORDER BY clause
 | 
			
		||||
    pub fn num_of_order_by(&self) -> usize {
 | 
			
		||||
        unsafe { (*self.0).nOrderBy as usize }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage {
 | 
			
		||||
        let constraint_usages = unsafe {
 | 
			
		||||
            slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize)
 | 
			
		||||
        };
 | 
			
		||||
        IndexConstraintUsage(&mut constraint_usages[constraint_idx])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Number used to identify the index
 | 
			
		||||
    pub fn set_idx_num(&mut self, idx_num: c_int) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            (*self.0).idxNum = idx_num;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// True if output is already ordered
 | 
			
		||||
    pub fn set_order_by_consumed(&mut self, order_by_consumed: bool) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            (*self.0).orderByConsumed = if order_by_consumed { 1 } else { 0 };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /// Estimated cost of using this index
 | 
			
		||||
    pub fn set_estimated_cost(&mut self, estimated_ost: f64) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            (*self.0).estimatedCost = estimated_ost;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Estimated number of rows returned
 | 
			
		||||
    #[cfg(feature = "bundled")] // SQLite >= 3.8.2
 | 
			
		||||
    pub fn set_estimated_rows(&mut self, estimated_rows: i64) {
 | 
			
		||||
        unsafe {
 | 
			
		||||
            (*self.0).estimatedRows = estimated_rows;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO idxFlags
 | 
			
		||||
    // TODO colUsed
 | 
			
		||||
 | 
			
		||||
    // TODO sqlite3_vtab_collation (http://sqlite.org/c3ref/vtab_collation.html)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct IndexConstraintIter<'a> {
 | 
			
		||||
    iter: slice::Iter<'a, ffi::sqlite3_index_constraint>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Iterator for IndexConstraintIter<'a> {
 | 
			
		||||
    type Item = IndexConstraint<'a>;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<IndexConstraint<'a>> {
 | 
			
		||||
        self.iter.next().map(|raw| IndexConstraint(raw))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
			
		||||
        self.iter.size_hint()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// WHERE clause constraint
 | 
			
		||||
pub struct IndexConstraint<'a>(&'a ffi::sqlite3_index_constraint);
 | 
			
		||||
 | 
			
		||||
impl<'a> IndexConstraint<'a> {
 | 
			
		||||
    /// Column constrained.  -1 for ROWID
 | 
			
		||||
    pub fn column(&self) -> c_int {
 | 
			
		||||
        self.0.iColumn
 | 
			
		||||
    }
 | 
			
		||||
    /// Constraint operator
 | 
			
		||||
    pub fn operator(&self) -> IndexConstraintOp {
 | 
			
		||||
        IndexConstraintOp::from_bits_truncate(self.0.op)
 | 
			
		||||
    }
 | 
			
		||||
    /// True if this constraint is usable
 | 
			
		||||
    pub fn is_usable(&self) -> bool {
 | 
			
		||||
        self.0.usable != 0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Information about what parameters to pass to `VTabCursor.filter`.
 | 
			
		||||
pub struct IndexConstraintUsage<'a>(&'a mut ffi::sqlite3_index_constraint_usage);
 | 
			
		||||
 | 
			
		||||
impl<'a> IndexConstraintUsage<'a> {
 | 
			
		||||
    /// if `argv_index` > 0, constraint is part of argv to `VTabCursor.filter`
 | 
			
		||||
    pub fn set_argv_index(&mut self, argv_index: c_int) {
 | 
			
		||||
        self.0.argvIndex = argv_index;
 | 
			
		||||
    }
 | 
			
		||||
    /// if `omit`, do not code a test for this constraint
 | 
			
		||||
    pub fn set_omit(&mut self, omit: bool) {
 | 
			
		||||
        self.0.omit = if omit { 1 } else { 0 };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct OrderByIter<'a> {
 | 
			
		||||
    iter: slice::Iter<'a, ffi::sqlite3_index_info_sqlite3_index_orderby>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Iterator for OrderByIter<'a> {
 | 
			
		||||
    type Item = OrderBy<'a>;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<OrderBy<'a>> {
 | 
			
		||||
        self.iter.next().map(|raw| OrderBy(raw))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
			
		||||
        self.iter.size_hint()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A column of the ORDER BY clause.
 | 
			
		||||
pub struct OrderBy<'a>(&'a ffi::sqlite3_index_info_sqlite3_index_orderby);
 | 
			
		||||
 | 
			
		||||
impl<'a> OrderBy<'a> {
 | 
			
		||||
    /// Column number
 | 
			
		||||
    pub fn column(&self) -> c_int {
 | 
			
		||||
        self.0.iColumn
 | 
			
		||||
    }
 | 
			
		||||
    /// True for DESC.  False for ASC.
 | 
			
		||||
    pub fn is_order_by_desc(&self) -> bool {
 | 
			
		||||
        self.0.desc != 0
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Virtual table cursor trait.
 | 
			
		||||
///
 | 
			
		||||
/// Implementations must be like:
 | 
			
		||||
/// ```rust,ignore
 | 
			
		||||
/// #[repr(C)]
 | 
			
		||||
/// struct MyTabCursor {
 | 
			
		||||
///    /// Base class. Must be first
 | 
			
		||||
///    base: ffi::sqlite3_vtab_cursor,
 | 
			
		||||
///    /* Virtual table implementations will typically add additional fields */
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// (See [SQLite doc](https://sqlite.org/c3ref/vtab_cursor.html))
 | 
			
		||||
pub trait VTabCursor: Sized {
 | 
			
		||||
    /// Begin a search of a virtual table.
 | 
			
		||||
    /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xfilter_method))
 | 
			
		||||
    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`.
 | 
			
		||||
    /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xnext_method))
 | 
			
		||||
    fn next(&mut self) -> Result<()>;
 | 
			
		||||
    /// Must return `false` if the cursor currently points to a valid row of data,
 | 
			
		||||
    /// or `true` otherwise.
 | 
			
		||||
    /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xeof_method))
 | 
			
		||||
    fn eof(&self) -> bool;
 | 
			
		||||
    /// Find the value for the `i`-th column of the current row.
 | 
			
		||||
    /// `i` is zero-based so the first column is numbered 0.
 | 
			
		||||
    /// May return its result back to SQLite using one of the specified `ctx`.
 | 
			
		||||
    /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xcolumn_method))
 | 
			
		||||
    fn column(&self, ctx: &mut Context, i: c_int) -> Result<()>;
 | 
			
		||||
    /// Return the rowid of row that the cursor is currently pointing at.
 | 
			
		||||
    /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xrowid_method))
 | 
			
		||||
    fn rowid(&self) -> Result<i64>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Context is used by `VTabCursor.column`` to specify the cell value.
 | 
			
		||||
pub struct Context(*mut ffi::sqlite3_context);
 | 
			
		||||
 | 
			
		||||
impl Context {
 | 
			
		||||
    pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
 | 
			
		||||
        let t = value.to_sql()?;
 | 
			
		||||
        unsafe { set_result(self.0, &t) };
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO sqlite3_vtab_nochange (http://sqlite.org/c3ref/vtab_nochange.html)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrapper to `VTabCursor.filter` arguments, the values requested by `VTab.best_index`.
 | 
			
		||||
pub struct Values<'a> {
 | 
			
		||||
    args: &'a [*mut ffi::sqlite3_value],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Values<'a> {
 | 
			
		||||
    pub fn len(&self) -> usize {
 | 
			
		||||
        self.args.len()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_empty(&self) -> bool {
 | 
			
		||||
        self.args.is_empty()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> {
 | 
			
		||||
        let arg = self.args[idx];
 | 
			
		||||
        let value = unsafe { ValueRef::from_value(arg) };
 | 
			
		||||
        FromSql::column_result(value).map_err(|err| match err {
 | 
			
		||||
            FromSqlError::InvalidType => Error::InvalidFilterParameterType(idx, value.data_type()),
 | 
			
		||||
            FromSqlError::Other(err) => {
 | 
			
		||||
                Error::FromSqlConversionFailure(idx, value.data_type(), err)
 | 
			
		||||
            }
 | 
			
		||||
            FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // `sqlite3_value_type` returns `SQLITE_NULL` for pointer.
 | 
			
		||||
    // So it seems not possible to enhance `ValueRef::from_value`.
 | 
			
		||||
    #[cfg(feature = "array")]
 | 
			
		||||
    pub(crate) fn get_array(&self, idx: usize) -> Result<Option<array::Array>> {
 | 
			
		||||
        use types::Value;
 | 
			
		||||
        let arg = self.args[idx];
 | 
			
		||||
        let ptr = unsafe { ffi::sqlite3_value_pointer(arg, array::ARRAY_TYPE) };
 | 
			
		||||
        if ptr.is_null() {
 | 
			
		||||
            Ok(None)
 | 
			
		||||
        } else {
 | 
			
		||||
            Ok(Some(unsafe {
 | 
			
		||||
                let rc = array::Array::from_raw(ptr as *const Vec<Value>);
 | 
			
		||||
                let array = rc.clone();
 | 
			
		||||
                array::Array::into_raw(rc); // don't consume it
 | 
			
		||||
                array
 | 
			
		||||
            }))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn iter(&self) -> ValueIter {
 | 
			
		||||
        ValueIter {
 | 
			
		||||
            iter: self.args.iter(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> IntoIterator for &'a Values<'a> {
 | 
			
		||||
    type Item = ValueRef<'a>;
 | 
			
		||||
    type IntoIter = ValueIter<'a>;
 | 
			
		||||
 | 
			
		||||
    fn into_iter(self) -> ValueIter<'a> {
 | 
			
		||||
        self.iter()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct ValueIter<'a> {
 | 
			
		||||
    iter: slice::Iter<'a, *mut ffi::sqlite3_value>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> Iterator for ValueIter<'a> {
 | 
			
		||||
    type Item = ValueRef<'a>;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<ValueRef<'a>> {
 | 
			
		||||
        self.iter
 | 
			
		||||
            .next()
 | 
			
		||||
            .map(|&raw| unsafe { ValueRef::from_value(raw) })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn size_hint(&self) -> (usize, Option<usize>) {
 | 
			
		||||
        self.iter.size_hint()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Connection {
 | 
			
		||||
    /// Register a virtual table implementation.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Step 3 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
 | 
			
		||||
    pub fn create_module<T: VTab>(
 | 
			
		||||
        &self,
 | 
			
		||||
        module_name: &str,
 | 
			
		||||
        module: &Module<T>,
 | 
			
		||||
        aux: Option<T::Aux>,
 | 
			
		||||
    ) -> Result<()> {
 | 
			
		||||
        self.db.borrow_mut().create_module(module_name, module, aux)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl InnerConnection {
 | 
			
		||||
    fn create_module<T: VTab>(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        module_name: &str,
 | 
			
		||||
        module: &Module<T>,
 | 
			
		||||
        aux: Option<T::Aux>,
 | 
			
		||||
    ) -> Result<()> {
 | 
			
		||||
        let c_name = try!(str_to_cstring(module_name));
 | 
			
		||||
        let r = match aux {
 | 
			
		||||
            Some(aux) => {
 | 
			
		||||
                let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux));
 | 
			
		||||
                unsafe {
 | 
			
		||||
                    ffi::sqlite3_create_module_v2(
 | 
			
		||||
                        self.db(),
 | 
			
		||||
                        c_name.as_ptr(),
 | 
			
		||||
                        &module.base,
 | 
			
		||||
                        boxed_aux as *mut c_void,
 | 
			
		||||
                        Some(free_boxed_value::<T::Aux>),
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            None => unsafe {
 | 
			
		||||
                ffi::sqlite3_create_module_v2(
 | 
			
		||||
                    self.db(),
 | 
			
		||||
                    c_name.as_ptr(),
 | 
			
		||||
                    &module.base,
 | 
			
		||||
                    ptr::null_mut(),
 | 
			
		||||
                    None,
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
        self.decode_result(r)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Escape double-quote (`"`) character occurences by doubling them (`""`).
 | 
			
		||||
pub fn escape_double_quote(identifier: &str) -> Cow<str> {
 | 
			
		||||
    if identifier.contains('"') {
 | 
			
		||||
        // escape quote by doubling them
 | 
			
		||||
        Owned(identifier.replace("\"", "\"\""))
 | 
			
		||||
    } else {
 | 
			
		||||
        Borrowed(identifier)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/// Dequote string
 | 
			
		||||
pub fn dequote(s: &str) -> &str {
 | 
			
		||||
    if s.len() < 2 {
 | 
			
		||||
        return s;
 | 
			
		||||
    }
 | 
			
		||||
    match s.bytes().next() {
 | 
			
		||||
        Some(b) if b == b'"' || b == b'\'' => match s.bytes().rev().next() {
 | 
			
		||||
            Some(e) if e == b => &s[1..s.len() - 1],
 | 
			
		||||
            _ => s,
 | 
			
		||||
        },
 | 
			
		||||
        _ => s,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
/// The boolean can be one of:
 | 
			
		||||
/// ```text
 | 
			
		||||
/// 1 yes true on
 | 
			
		||||
/// 0 no false off
 | 
			
		||||
/// ```
 | 
			
		||||
pub fn parse_boolean(s: &str) -> Option<bool> {
 | 
			
		||||
    if s.eq_ignore_ascii_case("yes")
 | 
			
		||||
        || s.eq_ignore_ascii_case("on")
 | 
			
		||||
        || s.eq_ignore_ascii_case("true")
 | 
			
		||||
        || s.eq("1")
 | 
			
		||||
    {
 | 
			
		||||
        Some(true)
 | 
			
		||||
    } else if s.eq_ignore_ascii_case("no")
 | 
			
		||||
        || s.eq_ignore_ascii_case("off")
 | 
			
		||||
        || s.eq_ignore_ascii_case("false")
 | 
			
		||||
        || s.eq("0")
 | 
			
		||||
    {
 | 
			
		||||
        Some(false)
 | 
			
		||||
    } else {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FIXME copy/paste from function.rs
 | 
			
		||||
unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
 | 
			
		||||
    let _: Box<T> = Box::from_raw(p as *mut T);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_create<T>(
 | 
			
		||||
    db: *mut ffi::sqlite3,
 | 
			
		||||
    aux: *mut c_void,
 | 
			
		||||
    argc: c_int,
 | 
			
		||||
    argv: *const *const c_char,
 | 
			
		||||
    pp_vtab: *mut *mut ffi::sqlite3_vtab,
 | 
			
		||||
    err_msg: *mut *mut c_char,
 | 
			
		||||
) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    T: CreateVTab,
 | 
			
		||||
{
 | 
			
		||||
    use std::error::Error as StdError;
 | 
			
		||||
    use std::ffi::CStr;
 | 
			
		||||
    use std::slice;
 | 
			
		||||
 | 
			
		||||
    let mut conn = VTabConnection(db);
 | 
			
		||||
    let aux = aux as *mut T::Aux;
 | 
			
		||||
    let args = slice::from_raw_parts(argv, argc as usize);
 | 
			
		||||
    let vec = args
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|&cs| CStr::from_ptr(cs).to_bytes()) // FIXME .to_str() -> Result<&str, Utf8Error>
 | 
			
		||||
        .collect::<Vec<_>>();
 | 
			
		||||
    match T::create(&mut conn, aux.as_ref(), &vec[..]) {
 | 
			
		||||
        Ok((sql, vtab)) => match ::std::ffi::CString::new(sql) {
 | 
			
		||||
            Ok(c_sql) => {
 | 
			
		||||
                let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
 | 
			
		||||
                if rc == ffi::SQLITE_OK {
 | 
			
		||||
                    let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
 | 
			
		||||
                    *pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab;
 | 
			
		||||
                    ffi::SQLITE_OK
 | 
			
		||||
                } else {
 | 
			
		||||
                    let err = error_from_sqlite_code(rc, None);
 | 
			
		||||
                    *err_msg = mprintf(err.description());
 | 
			
		||||
                    rc
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                *err_msg = mprintf(err.description());
 | 
			
		||||
                ffi::SQLITE_ERROR
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        Err(Error::SqliteFailure(err, s)) => {
 | 
			
		||||
            if let Some(s) = s {
 | 
			
		||||
                *err_msg = mprintf(&s);
 | 
			
		||||
            }
 | 
			
		||||
            err.extended_code
 | 
			
		||||
        }
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            *err_msg = mprintf(err.description());
 | 
			
		||||
            ffi::SQLITE_ERROR
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_connect<T>(
 | 
			
		||||
    db: *mut ffi::sqlite3,
 | 
			
		||||
    aux: *mut c_void,
 | 
			
		||||
    argc: c_int,
 | 
			
		||||
    argv: *const *const c_char,
 | 
			
		||||
    pp_vtab: *mut *mut ffi::sqlite3_vtab,
 | 
			
		||||
    err_msg: *mut *mut c_char,
 | 
			
		||||
) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    T: VTab,
 | 
			
		||||
{
 | 
			
		||||
    use std::error::Error as StdError;
 | 
			
		||||
    use std::ffi::CStr;
 | 
			
		||||
    use std::slice;
 | 
			
		||||
 | 
			
		||||
    let mut conn = VTabConnection(db);
 | 
			
		||||
    let aux = aux as *mut T::Aux;
 | 
			
		||||
    let args = slice::from_raw_parts(argv, argc as usize);
 | 
			
		||||
    let vec = args
 | 
			
		||||
        .iter()
 | 
			
		||||
        .map(|&cs| CStr::from_ptr(cs).to_bytes()) // FIXME .to_str() -> Result<&str, Utf8Error>
 | 
			
		||||
        .collect::<Vec<_>>();
 | 
			
		||||
    match T::connect(&mut conn, aux.as_ref(), &vec[..]) {
 | 
			
		||||
        Ok((sql, vtab)) => match ::std::ffi::CString::new(sql) {
 | 
			
		||||
            Ok(c_sql) => {
 | 
			
		||||
                let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
 | 
			
		||||
                if rc == ffi::SQLITE_OK {
 | 
			
		||||
                    let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
 | 
			
		||||
                    *pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab;
 | 
			
		||||
                    ffi::SQLITE_OK
 | 
			
		||||
                } else {
 | 
			
		||||
                    let err = error_from_sqlite_code(rc, None);
 | 
			
		||||
                    *err_msg = mprintf(err.description());
 | 
			
		||||
                    rc
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                *err_msg = mprintf(err.description());
 | 
			
		||||
                ffi::SQLITE_ERROR
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        Err(Error::SqliteFailure(err, s)) => {
 | 
			
		||||
            if let Some(s) = s {
 | 
			
		||||
                *err_msg = mprintf(&s);
 | 
			
		||||
            }
 | 
			
		||||
            err.extended_code
 | 
			
		||||
        }
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            *err_msg = mprintf(err.description());
 | 
			
		||||
            ffi::SQLITE_ERROR
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_best_index<T>(
 | 
			
		||||
    vtab: *mut ffi::sqlite3_vtab,
 | 
			
		||||
    info: *mut ffi::sqlite3_index_info,
 | 
			
		||||
) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    T: VTab,
 | 
			
		||||
{
 | 
			
		||||
    use std::error::Error as StdError;
 | 
			
		||||
    let vt = vtab as *mut T;
 | 
			
		||||
    let mut idx_info = IndexInfo(info);
 | 
			
		||||
    match (*vt).best_index(&mut idx_info) {
 | 
			
		||||
        Ok(_) => ffi::SQLITE_OK,
 | 
			
		||||
        Err(Error::SqliteFailure(err, s)) => {
 | 
			
		||||
            if let Some(err_msg) = s {
 | 
			
		||||
                set_err_msg(vtab, &err_msg);
 | 
			
		||||
            }
 | 
			
		||||
            err.extended_code
 | 
			
		||||
        }
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            set_err_msg(vtab, err.description());
 | 
			
		||||
            ffi::SQLITE_ERROR
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_disconnect<T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    T: VTab,
 | 
			
		||||
{
 | 
			
		||||
    if vtab.is_null() {
 | 
			
		||||
        return ffi::SQLITE_OK;
 | 
			
		||||
    }
 | 
			
		||||
    let vtab = vtab as *mut T;
 | 
			
		||||
    let _: Box<T> = Box::from_raw(vtab);
 | 
			
		||||
    ffi::SQLITE_OK
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_destroy<T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    T: CreateVTab,
 | 
			
		||||
{
 | 
			
		||||
    use std::error::Error as StdError;
 | 
			
		||||
    if vtab.is_null() {
 | 
			
		||||
        return ffi::SQLITE_OK;
 | 
			
		||||
    }
 | 
			
		||||
    let vt = vtab as *mut T;
 | 
			
		||||
    match (*vt).destroy() {
 | 
			
		||||
        Ok(_) => {
 | 
			
		||||
            let _: Box<T> = Box::from_raw(vt);
 | 
			
		||||
            ffi::SQLITE_OK
 | 
			
		||||
        }
 | 
			
		||||
        Err(Error::SqliteFailure(err, s)) => {
 | 
			
		||||
            if let Some(err_msg) = s {
 | 
			
		||||
                set_err_msg(vtab, &err_msg);
 | 
			
		||||
            }
 | 
			
		||||
            err.extended_code
 | 
			
		||||
        }
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            set_err_msg(vtab, err.description());
 | 
			
		||||
            ffi::SQLITE_ERROR
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_open<T>(
 | 
			
		||||
    vtab: *mut ffi::sqlite3_vtab,
 | 
			
		||||
    pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor,
 | 
			
		||||
) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    T: VTab,
 | 
			
		||||
{
 | 
			
		||||
    use std::error::Error as StdError;
 | 
			
		||||
    let vt = vtab as *mut T;
 | 
			
		||||
    match (*vt).open() {
 | 
			
		||||
        Ok(cursor) => {
 | 
			
		||||
            let boxed_cursor: *mut T::Cursor = Box::into_raw(Box::new(cursor));
 | 
			
		||||
            *pp_cursor = boxed_cursor as *mut ffi::sqlite3_vtab_cursor;
 | 
			
		||||
            ffi::SQLITE_OK
 | 
			
		||||
        }
 | 
			
		||||
        Err(Error::SqliteFailure(err, s)) => {
 | 
			
		||||
            if let Some(err_msg) = s {
 | 
			
		||||
                set_err_msg(vtab, &err_msg);
 | 
			
		||||
            }
 | 
			
		||||
            err.extended_code
 | 
			
		||||
        }
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            set_err_msg(vtab, err.description());
 | 
			
		||||
            ffi::SQLITE_ERROR
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_close<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    C: VTabCursor,
 | 
			
		||||
{
 | 
			
		||||
    let cr = cursor as *mut C;
 | 
			
		||||
    let _: Box<C> = Box::from_raw(cr);
 | 
			
		||||
    ffi::SQLITE_OK
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_filter<C>(
 | 
			
		||||
    cursor: *mut ffi::sqlite3_vtab_cursor,
 | 
			
		||||
    idx_num: c_int,
 | 
			
		||||
    idx_str: *const c_char,
 | 
			
		||||
    argc: c_int,
 | 
			
		||||
    argv: *mut *mut ffi::sqlite3_value,
 | 
			
		||||
) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    C: VTabCursor,
 | 
			
		||||
{
 | 
			
		||||
    use std::ffi::CStr;
 | 
			
		||||
    use std::slice;
 | 
			
		||||
    use std::str;
 | 
			
		||||
    let idx_name = if idx_str.is_null() {
 | 
			
		||||
        None
 | 
			
		||||
    } else {
 | 
			
		||||
        let c_slice = CStr::from_ptr(idx_str).to_bytes();
 | 
			
		||||
        Some(str::from_utf8_unchecked(c_slice))
 | 
			
		||||
    };
 | 
			
		||||
    let args = slice::from_raw_parts_mut(argv, argc as usize);
 | 
			
		||||
    let values = Values { args };
 | 
			
		||||
    let cr = cursor as *mut C;
 | 
			
		||||
    cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_next<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    C: VTabCursor,
 | 
			
		||||
{
 | 
			
		||||
    let cr = cursor as *mut C;
 | 
			
		||||
    cursor_error(cursor, (*cr).next())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_eof<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    C: VTabCursor,
 | 
			
		||||
{
 | 
			
		||||
    let cr = cursor as *mut C;
 | 
			
		||||
    (*cr).eof() as c_int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_column<C>(
 | 
			
		||||
    cursor: *mut ffi::sqlite3_vtab_cursor,
 | 
			
		||||
    ctx: *mut ffi::sqlite3_context,
 | 
			
		||||
    i: c_int,
 | 
			
		||||
) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    C: VTabCursor,
 | 
			
		||||
{
 | 
			
		||||
    let cr = cursor as *mut C;
 | 
			
		||||
    let mut ctxt = Context(ctx);
 | 
			
		||||
    result_error(ctx, (*cr).column(&mut ctxt, i))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
unsafe extern "C" fn rust_rowid<C>(
 | 
			
		||||
    cursor: *mut ffi::sqlite3_vtab_cursor,
 | 
			
		||||
    p_rowid: *mut ffi::sqlite3_int64,
 | 
			
		||||
) -> c_int
 | 
			
		||||
where
 | 
			
		||||
    C: VTabCursor,
 | 
			
		||||
{
 | 
			
		||||
    let cr = cursor as *mut C;
 | 
			
		||||
    match (*cr).rowid() {
 | 
			
		||||
        Ok(rowid) => {
 | 
			
		||||
            *p_rowid = rowid;
 | 
			
		||||
            ffi::SQLITE_OK
 | 
			
		||||
        }
 | 
			
		||||
        err => cursor_error(cursor, err),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Virtual table cursors can set an error message by assigning a string to `zErrMsg`.
 | 
			
		||||
unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result<T>) -> c_int {
 | 
			
		||||
    use std::error::Error as StdError;
 | 
			
		||||
    match result {
 | 
			
		||||
        Ok(_) => ffi::SQLITE_OK,
 | 
			
		||||
        Err(Error::SqliteFailure(err, s)) => {
 | 
			
		||||
            if let Some(err_msg) = s {
 | 
			
		||||
                set_err_msg((*cursor).pVtab, &err_msg);
 | 
			
		||||
            }
 | 
			
		||||
            err.extended_code
 | 
			
		||||
        }
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            set_err_msg((*cursor).pVtab, err.description());
 | 
			
		||||
            ffi::SQLITE_ERROR
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Virtual tables methods can set an error message by assigning a string to `zErrMsg`.
 | 
			
		||||
unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
 | 
			
		||||
    if !(*vtab).zErrMsg.is_null() {
 | 
			
		||||
        ffi::sqlite3_free((*vtab).zErrMsg as *mut c_void);
 | 
			
		||||
    }
 | 
			
		||||
    (*vtab).zErrMsg = mprintf(err_msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// To raise an error, the `column` method should use this method to set the error message
 | 
			
		||||
/// and return the error code.
 | 
			
		||||
unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
 | 
			
		||||
    use std::error::Error as StdError;
 | 
			
		||||
    match result {
 | 
			
		||||
        Ok(_) => ffi::SQLITE_OK,
 | 
			
		||||
        Err(Error::SqliteFailure(err, s)) => {
 | 
			
		||||
            match err.extended_code {
 | 
			
		||||
                ffi::SQLITE_TOOBIG => {
 | 
			
		||||
                    ffi::sqlite3_result_error_toobig(ctx);
 | 
			
		||||
                }
 | 
			
		||||
                ffi::SQLITE_NOMEM => {
 | 
			
		||||
                    ffi::sqlite3_result_error_nomem(ctx);
 | 
			
		||||
                }
 | 
			
		||||
                code => {
 | 
			
		||||
                    ffi::sqlite3_result_error_code(ctx, code);
 | 
			
		||||
                    if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
 | 
			
		||||
                        ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            err.extended_code
 | 
			
		||||
        }
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_ERROR);
 | 
			
		||||
            if let Ok(cstr) = str_to_cstring(err.description()) {
 | 
			
		||||
                ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
 | 
			
		||||
            }
 | 
			
		||||
            ffi::SQLITE_ERROR
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Space to hold this error message string must be obtained
 | 
			
		||||
// from an SQLite memory allocation function.
 | 
			
		||||
fn mprintf(err_msg: &str) -> *mut c_char {
 | 
			
		||||
    let c_format = CString::new("%s").unwrap();
 | 
			
		||||
    let c_err = CString::new(err_msg).unwrap();
 | 
			
		||||
    unsafe { ffi::sqlite3_mprintf(c_format.as_ptr(), c_err.as_ptr()) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "array")]
 | 
			
		||||
pub mod array;
 | 
			
		||||
#[cfg(feature = "csvtab")]
 | 
			
		||||
pub mod csvtab;
 | 
			
		||||
#[cfg(feature = "bundled")]
 | 
			
		||||
pub mod series; // SQLite >= 3.9.0
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_dequote() {
 | 
			
		||||
        assert_eq!("", super::dequote(""));
 | 
			
		||||
        assert_eq!("'", super::dequote("'"));
 | 
			
		||||
        assert_eq!("\"", super::dequote("\""));
 | 
			
		||||
        assert_eq!("'\"", super::dequote("'\""));
 | 
			
		||||
        assert_eq!("", super::dequote("''"));
 | 
			
		||||
        assert_eq!("", super::dequote("\"\""));
 | 
			
		||||
        assert_eq!("x", super::dequote("'x'"));
 | 
			
		||||
        assert_eq!("x", super::dequote("\"x\""));
 | 
			
		||||
        assert_eq!("x", super::dequote("x"));
 | 
			
		||||
    }
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_parse_boolean() {
 | 
			
		||||
        assert_eq!(None, super::parse_boolean(""));
 | 
			
		||||
        assert_eq!(Some(true), super::parse_boolean("1"));
 | 
			
		||||
        assert_eq!(Some(true), super::parse_boolean("yes"));
 | 
			
		||||
        assert_eq!(Some(true), super::parse_boolean("on"));
 | 
			
		||||
        assert_eq!(Some(true), super::parse_boolean("true"));
 | 
			
		||||
        assert_eq!(Some(false), super::parse_boolean("0"));
 | 
			
		||||
        assert_eq!(Some(false), super::parse_boolean("no"));
 | 
			
		||||
        assert_eq!(Some(false), super::parse_boolean("off"));
 | 
			
		||||
        assert_eq!(Some(false), super::parse_boolean("false"));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										286
									
								
								src/vtab/series.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										286
									
								
								src/vtab/series.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,286 @@
 | 
			
		||||
//! generate series virtual table.
 | 
			
		||||
//!
 | 
			
		||||
//! Port of C [generate series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c).
 | 
			
		||||
use std::default::Default;
 | 
			
		||||
use std::os::raw::c_int;
 | 
			
		||||
 | 
			
		||||
use ffi;
 | 
			
		||||
use types::Type;
 | 
			
		||||
use vtab::{
 | 
			
		||||
    eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection,
 | 
			
		||||
    VTabCursor, Values,
 | 
			
		||||
};
 | 
			
		||||
use {Connection, Result};
 | 
			
		||||
 | 
			
		||||
/// Register the "generate_series" module.
 | 
			
		||||
pub fn load_module(conn: &Connection) -> Result<()> {
 | 
			
		||||
    let aux: Option<()> = None;
 | 
			
		||||
    conn.create_module("generate_series", &SERIES_MODULE, aux)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lazy_static! {
 | 
			
		||||
    static ref SERIES_MODULE: Module<SeriesTab> = eponymous_only_module::<SeriesTab>(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Column numbers
 | 
			
		||||
// const SERIES_COLUMN_VALUE : c_int = 0;
 | 
			
		||||
const SERIES_COLUMN_START: c_int = 1;
 | 
			
		||||
const SERIES_COLUMN_STOP: c_int = 2;
 | 
			
		||||
const SERIES_COLUMN_STEP: c_int = 3;
 | 
			
		||||
 | 
			
		||||
bitflags! {
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    struct QueryPlanFlags: ::std::os::raw::c_int {
 | 
			
		||||
        // start = $value  -- constraint exists
 | 
			
		||||
        const START = 1;
 | 
			
		||||
        // stop = $value   -- constraint exists
 | 
			
		||||
        const STOP  = 2;
 | 
			
		||||
        // step = $value   -- constraint exists
 | 
			
		||||
        const STEP  = 4;
 | 
			
		||||
        // output in descending order
 | 
			
		||||
        const DESC  = 8;
 | 
			
		||||
        // Both start and stop
 | 
			
		||||
        const BOTH  = QueryPlanFlags::START.bits | QueryPlanFlags::STOP.bits;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// An instance of the Series virtual table
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
struct SeriesTab {
 | 
			
		||||
    /// Base class. Must be first
 | 
			
		||||
    base: ffi::sqlite3_vtab,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl VTab for SeriesTab {
 | 
			
		||||
    type Aux = ();
 | 
			
		||||
    type Cursor = SeriesTabCursor;
 | 
			
		||||
 | 
			
		||||
    fn connect(
 | 
			
		||||
        _: &mut VTabConnection,
 | 
			
		||||
        _aux: Option<&()>,
 | 
			
		||||
        _args: &[&[u8]],
 | 
			
		||||
    ) -> Result<(String, SeriesTab)> {
 | 
			
		||||
        let vtab = SeriesTab {
 | 
			
		||||
            base: ffi::sqlite3_vtab::default(),
 | 
			
		||||
        };
 | 
			
		||||
        Ok((
 | 
			
		||||
            "CREATE TABLE x(value,start hidden,stop hidden,step hidden)".to_owned(),
 | 
			
		||||
            vtab,
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
 | 
			
		||||
        // The query plan bitmask
 | 
			
		||||
        let mut idx_num: QueryPlanFlags = QueryPlanFlags::empty();
 | 
			
		||||
        // Index of the start= constraint
 | 
			
		||||
        let mut start_idx = None;
 | 
			
		||||
        // Index of the stop= constraint
 | 
			
		||||
        let mut stop_idx = None;
 | 
			
		||||
        // Index of the step= constraint
 | 
			
		||||
        let mut step_idx = None;
 | 
			
		||||
        for (i, constraint) in info.constraints().enumerate() {
 | 
			
		||||
            if !constraint.is_usable() {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            if constraint.operator() != IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            match constraint.column() {
 | 
			
		||||
                SERIES_COLUMN_START => {
 | 
			
		||||
                    start_idx = Some(i);
 | 
			
		||||
                    idx_num |= QueryPlanFlags::START;
 | 
			
		||||
                }
 | 
			
		||||
                SERIES_COLUMN_STOP => {
 | 
			
		||||
                    stop_idx = Some(i);
 | 
			
		||||
                    idx_num |= QueryPlanFlags::STOP;
 | 
			
		||||
                }
 | 
			
		||||
                SERIES_COLUMN_STEP => {
 | 
			
		||||
                    step_idx = Some(i);
 | 
			
		||||
                    idx_num |= QueryPlanFlags::STEP;
 | 
			
		||||
                }
 | 
			
		||||
                _ => {}
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut num_of_arg = 0;
 | 
			
		||||
        if let Some(start_idx) = start_idx {
 | 
			
		||||
            num_of_arg += 1;
 | 
			
		||||
            let mut constraint_usage = info.constraint_usage(start_idx);
 | 
			
		||||
            constraint_usage.set_argv_index(num_of_arg);
 | 
			
		||||
            constraint_usage.set_omit(true);
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(stop_idx) = stop_idx {
 | 
			
		||||
            num_of_arg += 1;
 | 
			
		||||
            let mut constraint_usage = info.constraint_usage(stop_idx);
 | 
			
		||||
            constraint_usage.set_argv_index(num_of_arg);
 | 
			
		||||
            constraint_usage.set_omit(true);
 | 
			
		||||
        }
 | 
			
		||||
        if let Some(step_idx) = step_idx {
 | 
			
		||||
            num_of_arg += 1;
 | 
			
		||||
            let mut constraint_usage = info.constraint_usage(step_idx);
 | 
			
		||||
            constraint_usage.set_argv_index(num_of_arg);
 | 
			
		||||
            constraint_usage.set_omit(true);
 | 
			
		||||
        }
 | 
			
		||||
        if idx_num.contains(QueryPlanFlags::BOTH) {
 | 
			
		||||
            // Both start= and stop= boundaries are available.
 | 
			
		||||
            info.set_estimated_cost(f64::from(
 | 
			
		||||
                2 - if idx_num.contains(QueryPlanFlags::STEP) {
 | 
			
		||||
                    1
 | 
			
		||||
                } else {
 | 
			
		||||
                    0
 | 
			
		||||
                },
 | 
			
		||||
            ));
 | 
			
		||||
            info.set_estimated_rows(1000);
 | 
			
		||||
            let order_by_consumed = {
 | 
			
		||||
                let mut order_bys = info.order_bys();
 | 
			
		||||
                if let Some(order_by) = order_bys.next() {
 | 
			
		||||
                    if order_by.is_order_by_desc() {
 | 
			
		||||
                        idx_num |= QueryPlanFlags::DESC;
 | 
			
		||||
                    }
 | 
			
		||||
                    true
 | 
			
		||||
                } else {
 | 
			
		||||
                    false
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            if order_by_consumed {
 | 
			
		||||
                info.set_order_by_consumed(true);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            info.set_estimated_cost(2_147_483_647f64);
 | 
			
		||||
            info.set_estimated_rows(2_147_483_647);
 | 
			
		||||
        }
 | 
			
		||||
        info.set_idx_num(idx_num.bits());
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn open(&self) -> Result<SeriesTabCursor> {
 | 
			
		||||
        Ok(SeriesTabCursor::new())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A cursor for the Series virtual table
 | 
			
		||||
#[derive(Default)]
 | 
			
		||||
#[repr(C)]
 | 
			
		||||
struct SeriesTabCursor {
 | 
			
		||||
    /// Base class. Must be first
 | 
			
		||||
    base: ffi::sqlite3_vtab_cursor,
 | 
			
		||||
    /// True to count down rather than up
 | 
			
		||||
    is_desc: bool,
 | 
			
		||||
    /// The rowid
 | 
			
		||||
    row_id: i64,
 | 
			
		||||
    /// Current value ("value")
 | 
			
		||||
    value: i64,
 | 
			
		||||
    /// Mimimum value ("start")
 | 
			
		||||
    min_value: i64,
 | 
			
		||||
    /// Maximum value ("stop")
 | 
			
		||||
    max_value: i64,
 | 
			
		||||
    /// Increment ("step")
 | 
			
		||||
    step: i64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SeriesTabCursor {
 | 
			
		||||
    fn new() -> SeriesTabCursor {
 | 
			
		||||
        SeriesTabCursor::default()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl VTabCursor for SeriesTabCursor {
 | 
			
		||||
    fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values) -> Result<()> {
 | 
			
		||||
        let idx_num = QueryPlanFlags::from_bits_truncate(idx_num);
 | 
			
		||||
        let mut i = 0;
 | 
			
		||||
        if idx_num.contains(QueryPlanFlags::START) {
 | 
			
		||||
            self.min_value = try!(args.get(i));
 | 
			
		||||
            i += 1;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.min_value = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if idx_num.contains(QueryPlanFlags::STOP) {
 | 
			
		||||
            self.max_value = try!(args.get(i));
 | 
			
		||||
            i += 1;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.max_value = 0xffff_ffff;
 | 
			
		||||
        }
 | 
			
		||||
        if idx_num.contains(QueryPlanFlags::STEP) {
 | 
			
		||||
            self.step = try!(args.get(i));
 | 
			
		||||
            if self.step < 1 {
 | 
			
		||||
                self.step = 1;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            self.step = 1;
 | 
			
		||||
        };
 | 
			
		||||
        for arg in args.iter() {
 | 
			
		||||
            if arg.data_type() == Type::Null {
 | 
			
		||||
                // If any of the constraints have a NULL value, then return no rows.
 | 
			
		||||
                self.min_value = 1;
 | 
			
		||||
                self.max_value = 0;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        self.is_desc = idx_num.contains(QueryPlanFlags::DESC);
 | 
			
		||||
        if self.is_desc {
 | 
			
		||||
            self.value = self.max_value;
 | 
			
		||||
            if self.step > 0 {
 | 
			
		||||
                self.value -= (self.max_value - self.min_value) % self.step;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            self.value = self.min_value;
 | 
			
		||||
        }
 | 
			
		||||
        self.row_id = 1;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    fn next(&mut self) -> Result<()> {
 | 
			
		||||
        if self.is_desc {
 | 
			
		||||
            self.value -= self.step;
 | 
			
		||||
        } else {
 | 
			
		||||
            self.value += self.step;
 | 
			
		||||
        }
 | 
			
		||||
        self.row_id += 1;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    fn eof(&self) -> bool {
 | 
			
		||||
        if self.is_desc {
 | 
			
		||||
            self.value < self.min_value
 | 
			
		||||
        } else {
 | 
			
		||||
            self.value > self.max_value
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
 | 
			
		||||
        let x = match i {
 | 
			
		||||
            SERIES_COLUMN_START => self.min_value,
 | 
			
		||||
            SERIES_COLUMN_STOP => self.max_value,
 | 
			
		||||
            SERIES_COLUMN_STEP => self.step,
 | 
			
		||||
            _ => self.value,
 | 
			
		||||
        };
 | 
			
		||||
        ctx.set_result(&x)
 | 
			
		||||
    }
 | 
			
		||||
    fn rowid(&self) -> Result<i64> {
 | 
			
		||||
        Ok(self.row_id)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use ffi;
 | 
			
		||||
    use vtab::series;
 | 
			
		||||
    use Connection;
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_series_module() {
 | 
			
		||||
        let version = unsafe { ffi::sqlite3_libversion_number() };
 | 
			
		||||
        if version < 3008012 {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
        series::load_module(&db).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut s = db.prepare("SELECT * FROM generate_series(0,20,5)").unwrap();
 | 
			
		||||
 | 
			
		||||
        let series = s.query_map(&[], |row| row.get::<_, i32>(0)).unwrap();
 | 
			
		||||
 | 
			
		||||
        let mut expected = 0;
 | 
			
		||||
        for value in series {
 | 
			
		||||
            assert_eq!(expected, value.unwrap());
 | 
			
		||||
            expected += 5;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								test.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								test.csv
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
"colA","colB","colC"
 | 
			
		||||
1,2,3
 | 
			
		||||
a,b,c
 | 
			
		||||
a,"b",c
 | 
			
		||||
"a","b","c .. z"
 | 
			
		||||
"a","b","c,d"
 | 
			
		||||
		
		
			
  | 
@@ -1,8 +1,8 @@
 | 
			
		||||
//! Ensure we reject connections when SQLite is in single-threaded mode, as it
 | 
			
		||||
//! would violate safety if multiple Rust threads tried to use connections.
 | 
			
		||||
 | 
			
		||||
extern crate rusqlite;
 | 
			
		||||
extern crate libsqlite3_sys as ffi;
 | 
			
		||||
extern crate rusqlite;
 | 
			
		||||
 | 
			
		||||
use rusqlite::Connection;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										97
									
								
								tests/vtab.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								tests/vtab.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
//! Ensure Virtual tables can be declared outside `rusqlite` crate.
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "vtab")]
 | 
			
		||||
extern crate rusqlite;
 | 
			
		||||
 | 
			
		||||
#[cfg(feature = "vtab")]
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_dummy_module() {
 | 
			
		||||
    use rusqlite::vtab::{
 | 
			
		||||
        eponymous_only_module, sqlite3_vtab, sqlite3_vtab_cursor, Context, IndexInfo, VTab,
 | 
			
		||||
        VTabConnection, VTabCursor, Values,
 | 
			
		||||
    };
 | 
			
		||||
    use rusqlite::{version_number, Connection, Result};
 | 
			
		||||
    use std::os::raw::c_int;
 | 
			
		||||
 | 
			
		||||
    let module = eponymous_only_module::<DummyTab>(1);
 | 
			
		||||
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    struct DummyTab {
 | 
			
		||||
        /// Base class. Must be first
 | 
			
		||||
        base: sqlite3_vtab,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl VTab for DummyTab {
 | 
			
		||||
        type Aux = ();
 | 
			
		||||
        type Cursor = DummyTabCursor;
 | 
			
		||||
 | 
			
		||||
        fn connect(
 | 
			
		||||
            _: &mut VTabConnection,
 | 
			
		||||
            _aux: Option<&()>,
 | 
			
		||||
            _args: &[&[u8]],
 | 
			
		||||
        ) -> Result<(String, DummyTab)> {
 | 
			
		||||
            let vtab = DummyTab {
 | 
			
		||||
                base: sqlite3_vtab::default(),
 | 
			
		||||
            };
 | 
			
		||||
            Ok(("CREATE TABLE x(value)".to_owned(), vtab))
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
 | 
			
		||||
            info.set_estimated_cost(1.);
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fn open(&self) -> Result<DummyTabCursor> {
 | 
			
		||||
            Ok(DummyTabCursor::default())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[derive(Default)]
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
    struct DummyTabCursor {
 | 
			
		||||
        /// Base class. Must be first
 | 
			
		||||
        base: sqlite3_vtab_cursor,
 | 
			
		||||
        /// The rowid
 | 
			
		||||
        row_id: i64,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    impl VTabCursor for DummyTabCursor {
 | 
			
		||||
        fn filter(
 | 
			
		||||
            &mut self,
 | 
			
		||||
            _idx_num: c_int,
 | 
			
		||||
            _idx_str: Option<&str>,
 | 
			
		||||
            _args: &Values,
 | 
			
		||||
        ) -> Result<()> {
 | 
			
		||||
            self.row_id = 1;
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
        fn next(&mut self) -> Result<()> {
 | 
			
		||||
            self.row_id += 1;
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
        fn eof(&self) -> bool {
 | 
			
		||||
            self.row_id > 1
 | 
			
		||||
        }
 | 
			
		||||
        fn column(&self, ctx: &mut Context, _: c_int) -> Result<()> {
 | 
			
		||||
            ctx.set_result(&self.row_id)
 | 
			
		||||
        }
 | 
			
		||||
        fn rowid(&self) -> Result<i64> {
 | 
			
		||||
            Ok(self.row_id)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let db = Connection::open_in_memory().unwrap();
 | 
			
		||||
 | 
			
		||||
    db.create_module::<DummyTab>("dummy", &module, None)
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    let version = version_number();
 | 
			
		||||
    if version < 3008012 {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut s = db.prepare("SELECT * FROM dummy()").unwrap();
 | 
			
		||||
 | 
			
		||||
    let dummy = s.query_row(&[], |row| row.get::<_, i32>(0)).unwrap();
 | 
			
		||||
    assert_eq!(1, dummy);
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user