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

This commit is contained in:
gwenn 2019-02-02 13:22:40 +01:00
commit e8e76852bd
49 changed files with 7146 additions and 2010 deletions

View File

@ -8,6 +8,7 @@ rust:
- nightly
matrix:
fast_finish: true
allow_failures:
- rust: nightly

View File

@ -1,7 +1,8 @@
[package]
name = "rusqlite"
version = "0.15.0"
version = "0.16.0"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
edition = "2018"
description = "Ergonomic wrapper for SQLite"
repository = "https://github.com/jgallagher/rusqlite"
documentation = "http://docs.rs/rusqlite/"
@ -18,6 +19,9 @@ maintenance = { status = "actively-developed" }
[lib]
name = "rusqlite"
[workspace]
members = ["libsqlite3-sys"]
[features]
load_extension = []
# hot-backup interface: 3.6.11 (2009-02-18)
@ -37,9 +41,13 @@ sqlcipher = ["libsqlite3-sys/sqlcipher"]
unlock_notify = ["libsqlite3-sys/unlock_notify"]
# xSavepoint, xRelease and xRollbackTo: 3.7.7 (2011-06-23)
vtab = ["libsqlite3-sys/min_sqlite_version_3_7_7", "lazy_static"]
# xShadowName: 3.26.0
vtab_v3 = ["vtab"]
csvtab = ["csv", "vtab"]
# pointer passing interfaces: 3.20.0
array = ["vtab"]
# session extension: 3.13.0
session = ["libsqlite3-sys/session", "hooks", "fallible-streaming-iterator"]
[dependencies]
time = "0.1.0"
@ -50,6 +58,7 @@ serde_json = { version = "1.0", optional = true }
csv = { version = "1.0", optional = true }
lazy_static = { version = "1.0", optional = true }
byteorder = { version = "1.2", features = ["i128"], optional = true }
fallible-streaming-iterator = { version = "0.1", optional = true }
[dev-dependencies]
tempdir = "0.3"
@ -58,7 +67,7 @@ regex = "1.0"
[dependencies.libsqlite3-sys]
path = "libsqlite3-sys"
version = "0.10"
version = "0.12"
[[test]]
name = "config_log"

View File

@ -10,9 +10,6 @@ Rusqlite is an ergonomic wrapper for using SQLite from Rust. It attempts to expo
an interface similar to [rust-postgres](https://github.com/sfackler/rust-postgres).
```rust
extern crate rusqlite;
extern crate time;
use rusqlite::types::ToSql;
use rusqlite::{Connection, NO_PARAMS};
use time::Timespec;
@ -77,26 +74,26 @@ newer SQLite version; see details below.
Rusqlite provides several features that are behind [Cargo
features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section). They are:
* [`load_extension`](https://docs.rs/rusqlite/0.15.0/rusqlite/struct.LoadExtensionGuard.html)
* [`load_extension`](https://docs.rs/rusqlite/0.16.0/rusqlite/struct.LoadExtensionGuard.html)
allows loading dynamic library-based SQLite extensions.
* [`backup`](https://docs.rs/rusqlite/0.15.0/rusqlite/backup/index.html)
* [`backup`](https://docs.rs/rusqlite/0.16.0/rusqlite/backup/index.html)
allows use of SQLite's online backup API. Note: This feature requires SQLite 3.6.11 or later.
* [`functions`](https://docs.rs/rusqlite/0.15.0/rusqlite/functions/index.html)
* [`functions`](https://docs.rs/rusqlite/0.16.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`](https://docs.rs/rusqlite/0.15.0/rusqlite/trace/index.html)
* [`trace`](https://docs.rs/rusqlite/0.16.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`](https://docs.rs/rusqlite/0.15.0/rusqlite/blob/index.html)
* [`blob`](https://docs.rs/rusqlite/0.16.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`](https://docs.rs/rusqlite/0.15.0/rusqlite/struct.Connection.html#method.limit)
* [`limits`](https://docs.rs/rusqlite/0.16.0/rusqlite/struct.Connection.html#method.limit)
allows you to set and retrieve SQLite's per connection limits.
* `chrono` implements [`FromSql`](https://docs.rs/rusqlite/0.15.0/rusqlite/types/trait.FromSql.html)
and [`ToSql`](https://docs.rs/rusqlite/0.15.0/rusqlite/types/trait.ToSql.html) for various
* `chrono` implements [`FromSql`](https://docs.rs/rusqlite/0.16.0/rusqlite/types/trait.FromSql.html)
and [`ToSql`](https://docs.rs/rusqlite/0.16.0/rusqlite/types/trait.ToSql.html) for various
types from the [`chrono` crate](https://crates.io/crates/chrono).
* `serde_json` implements [`FromSql`](https://docs.rs/rusqlite/0.15.0/rusqlite/types/trait.FromSql.html)
and [`ToSql`](https://docs.rs/rusqlite/0.15.0/rusqlite/types/trait.ToSql.html) for the
* `serde_json` implements [`FromSql`](https://docs.rs/rusqlite/0.16.0/rusqlite/types/trait.FromSql.html)
and [`ToSql`](https://docs.rs/rusqlite/0.16.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`.
@ -106,6 +103,7 @@ features](https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-s
* [`csvtab`](https://sqlite.org/csv.html), CSV virtual table written in Rust.
* [`array`](https://sqlite.org/carray.html), The `rarray()` Table-Valued Function.
* `i128_blob` allows storing values of type `i128` type in SQLite databases. Internally, the data is stored as a 16 byte big-endian blob, with the most significant bit flipped, which allows ordering and comparison between different blobs storing i128s to work as expected.
* [`session`](https://sqlite.org/sessionintro.html), Session module extension.
## Notes on building rusqlite and libsqlite3-sys
@ -118,11 +116,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.25.2 (as of `rusqlite` 0.15.0 / `libsqlite3-sys`
0.10.0). 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.26.0 (as of `rusqlite` 0.16.0 / `libsqlite3-sys`
0.11.0). 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.15.0"
version = "0.16.0"
features = ["bundled"]
```
* You can set the `SQLITE3_LIB_DIR` to point to directory containing the SQLite

View File

@ -5,9 +5,9 @@ environment:
- TARGET: x86_64-pc-windows-msvc
VCPKG_DEFAULT_TRIPLET: x64-windows
VCPKGRS_DYNAMIC: 1
- TARGET: x86_64-pc-windows-msvc
VCPKG_DEFAULT_TRIPLET: x64-windows-static
RUSTFLAGS: -Ctarget-feature=+crt-static
# - TARGET: x86_64-pc-windows-msvc
# VCPKG_DEFAULT_TRIPLET: x64-windows-static
# RUSTFLAGS: -Ctarget-feature=+crt-static
install:
- appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
- rustup-init.exe -y --default-host %TARGET%

View File

@ -1,8 +1,6 @@
#![feature(test)]
extern crate test;
extern crate rusqlite;
use rusqlite::Connection;
use test::Bencher;

View File

@ -1,7 +1,8 @@
[package]
name = "libsqlite3-sys"
version = "0.10.0"
version = "0.12.0"
authors = ["John Gallagher <jgallagher@bignerdranch.com>"]
edition = "2018"
repository = "https://github.com/jgallagher/rusqlite"
description = "Native bindings to the libsqlite3 library"
license = "MIT"
@ -18,11 +19,16 @@ sqlcipher = []
min_sqlite_version_3_6_8 = ["pkg-config", "vcpkg"]
min_sqlite_version_3_6_23 = ["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 = []
# 3.13.0
preupdate_hook = []
# 3.13.0
session = ["preupdate_hook"]
[build-dependencies]
bindgen = { version = "0.43", optional = true }
bindgen = { version = "0.47", optional = true }
pkg-config = { version = "0.3", optional = true }
cc = { version = "1.0", optional = true }

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,34 @@
use std::env;
use std::path::Path;
fn main() {
build::main();
let out_dir = env::var("OUT_DIR").unwrap();
let out_path = Path::new(&out_dir).join("bindgen.rs");
build::main(&out_dir, &out_path);
}
#[cfg(feature = "bundled")]
mod build {
extern crate cc;
use cc;
use std::path::Path;
use std::{env, fs};
pub fn main() {
pub fn main(out_dir: &str, out_path: &Path) {
if cfg!(feature = "sqlcipher") {
panic!("Builds with bundled SQLCipher are not supported");
}
let out_dir = env::var("OUT_DIR").unwrap();
let out_path = Path::new(&out_dir).join("bindgen.rs");
fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
.expect("Could not copy bindings to output directory");
#[cfg(feature = "buildtime_bindgen")]
{
use super::{bindings, HeaderLocation};
let header = HeaderLocation::FromPath("sqlite3/sqlite3.h".to_owned());
bindings::write_to_out_dir(header, out_path);
}
#[cfg(not(feature = "buildtime_bindgen"))]
{
use std::fs;
fs::copy("sqlite3/bindgen_bundled_version.rs", out_path)
.expect("Could not copy bindings to output directory");
}
let mut cfg = cc::Build::new();
cfg.file("sqlite3/sqlite3.c")
@ -42,62 +54,87 @@ mod build {
if cfg!(feature = "unlock_notify") {
cfg.flag("-DSQLITE_ENABLE_UNLOCK_NOTIFY");
}
if cfg!(feature = "preupdate_hook") {
cfg.flag("-DSQLITE_ENABLE_PREUPDATE_HOOK");
}
if cfg!(feature = "session") {
cfg.flag("-DSQLITE_ENABLE_SESSION");
}
cfg.compile("libsqlite3.a");
println!("cargo:lib_dir={}", out_dir);
}
}
fn env_prefix() -> &'static str {
if cfg!(feature = "sqlcipher") {
"SQLCIPHER"
} else {
"SQLITE3"
}
}
pub enum HeaderLocation {
FromEnvironment,
Wrapper,
FromPath(String),
}
impl From<HeaderLocation> for String {
fn from(header: HeaderLocation) -> String {
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
));
header.push_str("/sqlite3.h");
header
}
HeaderLocation::Wrapper => "wrapper.h".into(),
HeaderLocation::FromPath(path) => path,
}
}
}
#[cfg(not(feature = "bundled"))]
mod build {
extern crate pkg_config;
use pkg_config;
#[cfg(all(feature = "vcpkg", target_env = "msvc"))]
extern crate vcpkg;
use super::{bindings, env_prefix, HeaderLocation};
use std::env;
use std::path::Path;
pub enum HeaderLocation {
FromEnvironment,
Wrapper,
FromPath(String),
pub fn main(_out_dir: &str, out_path: &Path) {
let header = find_sqlite();
bindings::write_to_out_dir(header, out_path);
}
impl From<HeaderLocation> for String {
fn from(header: HeaderLocation) -> String {
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
));
header.push_str("/sqlite3.h");
header
}
HeaderLocation::Wrapper => "wrapper.h".into(),
HeaderLocation::FromPath(path) => path,
}
fn find_link_mode() -> &'static str {
// If the user specifies SQLITE_STATIC (or SQLCIPHER_STATIC), do static
// linking, unless it's explicitly set to 0.
match &env::var(format!("{}_STATIC", env_prefix())) {
Ok(v) if v != "0" => "static",
_ => "dylib",
}
}
pub fn main() {
let header = find_sqlite();
bindings::write_to_out_dir(header);
}
// Prints the necessary cargo link commands and returns the path to the header.
fn find_sqlite() -> HeaderLocation {
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());
println!("cargo:rerun-if-env-changed={}_STATIC", 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);
println!("cargo:rustc-link-lib={}={}", find_link_mode(), link_lib);
println!("cargo:rustc-link-search={}", dir);
return HeaderLocation::FromEnvironment;
}
@ -122,9 +159,9 @@ mod build {
Err(_) => {
// No env var set and pkg-config couldn't help; just output the link-lib
// request and hope that the library exists on the system paths. We used to
// output /usr/lib explicitly, but that can introduce other linking problems; see
// https://github.com/jgallagher/rusqlite/issues/207.
println!("cargo:rustc-link-lib={}", link_lib);
// output /usr/lib explicitly, but that can introduce other linking problems;
// see https://github.com/jgallagher/rusqlite/issues/207.
println!("cargo:rustc-link-lib={}={}", find_link_mode(), link_lib);
HeaderLocation::Wrapper
}
}
@ -147,14 +184,6 @@ mod build {
None
}
fn env_prefix() -> &'static str {
if cfg!(feature = "sqlcipher") {
"SQLCIPHER"
} else {
"SQLITE3"
}
}
fn link_lib() -> &'static str {
if cfg!(feature = "sqlcipher") {
"sqlcipher"
@ -162,88 +191,98 @@ mod build {
"sqlite3"
}
}
}
#[cfg(not(feature = "buildtime_bindgen"))]
mod bindings {
use super::HeaderLocation;
#[cfg(all(not(feature = "buildtime_bindgen"), not(feature = "bundled")))]
mod bindings {
use super::HeaderLocation;
use std::path::Path;
use std::{env, fs};
use std::fs;
use std::path::Path;
static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[
"bindgen-bindings/bindgen_3.6.8.rs",
#[cfg(feature = "min_sqlite_version_3_6_23")]
"bindgen-bindings/bindgen_3.6.23.rs",
#[cfg(feature = "min_sqlite_version_3_7_7")]
"bindgen-bindings/bindgen_3.7.7.rs",
];
static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[
"bindgen-bindings/bindgen_3.6.8.rs",
#[cfg(feature = "min_sqlite_version_3_6_23")]
"bindgen-bindings/bindgen_3.6.23.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",
];
pub fn write_to_out_dir(_header: HeaderLocation) {
let out_dir = env::var("OUT_DIR").unwrap();
let out_path = Path::new(&out_dir).join("bindgen.rs");
let in_path = PREBUILT_BINDGEN_PATHS[PREBUILT_BINDGEN_PATHS.len() - 1];
fs::copy(in_path, out_path).expect("Could not copy bindings to output directory");
}
}
#[cfg(feature = "buildtime_bindgen")]
mod bindings {
extern crate bindgen;
use self::bindgen::callbacks::{IntKind, ParseCallbacks};
use super::HeaderLocation;
use std::env;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
#[derive(Debug)]
struct SqliteTypeChooser;
impl ParseCallbacks for SqliteTypeChooser {
fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> {
if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
Some(IntKind::I32)
} else {
None
}
}
}
pub fn write_to_out_dir(header: HeaderLocation) {
let header: String = header.into();
let out_dir = env::var("OUT_DIR").unwrap();
let mut output = Vec::new();
bindgen::builder()
.header(header.clone())
.parse_callbacks(Box::new(SqliteTypeChooser))
.rustfmt_bindings(true)
.generate()
.expect(&format!("could not run bindgen on header {}", header))
.write(Box::new(&mut output))
.expect("could not write output of bindgen");
let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!");
// rusqlite's functions feature ors in the SQLITE_DETERMINISTIC flag when it can. This flag
// was added in SQLite 3.8.3, but oring it in in prior versions of SQLite is harmless. We
// don't want to not build just because this flag is missing (e.g., if we're linking against
// SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's output.
if !output.contains("pub const SQLITE_DETERMINISTIC") {
output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
}
let path = Path::new(&out_dir).join("bindgen.rs");
let mut file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(path.clone())
.expect(&format!("Could not write to {:?}", path));
file.write_all(output.as_bytes())
.expect(&format!("Could not write to {:?}", path));
}
pub fn write_to_out_dir(_header: HeaderLocation, out_path: &Path) {
let in_path = PREBUILT_BINDGEN_PATHS[PREBUILT_BINDGEN_PATHS.len() - 1];
fs::copy(in_path, out_path).expect("Could not copy bindings to output directory");
}
}
#[cfg(feature = "buildtime_bindgen")]
mod bindings {
use bindgen;
use self::bindgen::callbacks::{IntKind, ParseCallbacks};
use super::HeaderLocation;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
#[derive(Debug)]
struct SqliteTypeChooser;
impl ParseCallbacks for SqliteTypeChooser {
fn int_macro(&self, _name: &str, value: i64) -> Option<IntKind> {
if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
Some(IntKind::I32)
} else {
None
}
}
}
pub fn write_to_out_dir(header: HeaderLocation, out_path: &Path) {
let header: String = header.into();
let mut output = Vec::new();
let mut bindings = bindgen::builder()
.header(header.clone())
.parse_callbacks(Box::new(SqliteTypeChooser))
.rustfmt_bindings(true);
if cfg!(feature = "unlock_notify") {
bindings = bindings.clang_arg("-DSQLITE_ENABLE_UNLOCK_NOTIFY");
}
if cfg!(feature = "preupdate_hook") {
bindings = bindings.clang_arg("-DSQLITE_ENABLE_PREUPDATE_HOOK");
}
if cfg!(feature = "session") {
bindings = bindings.clang_arg("-DSQLITE_ENABLE_SESSION");
}
bindings
.generate()
.expect(&format!("could not run bindgen on header {}", header))
.write(Box::new(&mut output))
.expect("could not write output of bindgen");
let mut output = String::from_utf8(output).expect("bindgen output was not UTF-8?!");
// rusqlite's functions feature ors in the SQLITE_DETERMINISTIC flag when it
// can. This flag was added in SQLite 3.8.3, but oring it in in prior
// versions of SQLite is harmless. We don't want to not build just
// because this flag is missing (e.g., if we're linking against
// SQLite 3.7.x), so append the flag manually if it isn't present in bindgen's
// output.
if !output.contains("pub const SQLITE_DETERMINISTIC") {
output.push_str("\npub const SQLITE_DETERMINISTIC: i32 = 2048;\n");
}
let mut file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(out_path.clone())
.expect(&format!("Could not write to {:?}", out_path));
file.write_all(output.as_bytes())
.expect(&format!("Could not write to {:?}", out_path));
}
}

View File

@ -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.25.2\0";
pub const SQLITE_VERSION_NUMBER: i32 = 3025002;
pub const SQLITE_VERSION: &'static [u8; 7usize] = b"3.26.0\0";
pub const SQLITE_VERSION_NUMBER: i32 = 3026000;
pub const SQLITE_SOURCE_ID: &'static [u8; 85usize] =
b"2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792d1c7\0";
b"2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9\0";
pub const SQLITE_OK: i32 = 0;
pub const SQLITE_ERROR: i32 = 1;
pub const SQLITE_INTERNAL: i32 = 2;
@ -228,7 +228,8 @@ 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_RESET_DATABASE: i32 = 1009;
pub const SQLITE_DBCONFIG_MAX: i32 = 1009;
pub const SQLITE_DBCONFIG_DEFENSIVE: i32 = 1010;
pub const SQLITE_DBCONFIG_MAX: i32 = 1010;
pub const SQLITE_DENY: i32 = 1;
pub const SQLITE_IGNORE: i32 = 2;
pub const SQLITE_CREATE_INDEX: i32 = 1;
@ -282,6 +283,7 @@ pub const SQLITE_LIMIT_VARIABLE_NUMBER: i32 = 9;
pub const SQLITE_LIMIT_TRIGGER_DEPTH: i32 = 10;
pub const SQLITE_LIMIT_WORKER_THREADS: i32 = 11;
pub const SQLITE_PREPARE_PERSISTENT: i32 = 1;
pub const SQLITE_PREPARE_NORMALIZE: i32 = 2;
pub const SQLITE_INTEGER: i32 = 1;
pub const SQLITE_FLOAT: i32 = 2;
pub const SQLITE_BLOB: i32 = 4;
@ -343,6 +345,7 @@ pub const SQLITE_TESTCTRL_RESERVE: i32 = 14;
pub const SQLITE_TESTCTRL_OPTIMIZATIONS: i32 = 15;
pub const SQLITE_TESTCTRL_ISKEYWORD: i32 = 16;
pub const SQLITE_TESTCTRL_SCRATCHMALLOC: i32 = 17;
pub const SQLITE_TESTCTRL_INTERNAL_FUNCTIONS: i32 = 17;
pub const SQLITE_TESTCTRL_LOCALTIME_FAULT: i32 = 18;
pub const SQLITE_TESTCTRL_EXPLAIN_STMT: i32 = 19;
pub const SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD: i32 = 19;
@ -531,24 +534,34 @@ pub struct sqlite3_io_methods {
unsafe extern "C" fn(arg1: *mut sqlite3_file, size: sqlite3_int64) -> ::std::os::raw::c_int,
>,
pub xSync: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_file, flags: ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut sqlite3_file,
flags: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xFileSize: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_file, pSize: *mut sqlite3_int64)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut sqlite3_file,
pSize: *mut sqlite3_int64,
) -> ::std::os::raw::c_int,
>,
pub xLock: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_file, arg2: ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut sqlite3_file,
arg2: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xUnlock: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_file, arg2: ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut sqlite3_file,
arg2: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xCheckReservedLock: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_file, pResOut: *mut ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut sqlite3_file,
pResOut: *mut ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xFileControl: ::std::option::Option<
unsafe extern "C" fn(
@ -582,8 +595,10 @@ pub struct sqlite3_io_methods {
>,
pub xShmBarrier: ::std::option::Option<unsafe extern "C" fn(arg1: *mut sqlite3_file)>,
pub xShmUnmap: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_file, deleteFlag: ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut sqlite3_file,
deleteFlag: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xFetch: ::std::option::Option<
unsafe extern "C" fn(
@ -862,8 +877,10 @@ pub struct sqlite3_vfs {
) -> ::std::os::raw::c_int,
>,
pub xDlOpen: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_vfs, zFilename: *const ::std::os::raw::c_char)
-> *mut ::std::os::raw::c_void,
unsafe extern "C" fn(
arg1: *mut sqlite3_vfs,
zFilename: *const ::std::os::raw::c_char,
) -> *mut ::std::os::raw::c_void,
>,
pub xDlError: ::std::option::Option<
unsafe extern "C" fn(
@ -896,8 +913,10 @@ pub struct sqlite3_vfs {
) -> ::std::os::raw::c_int,
>,
pub xSleep: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_vfs, microseconds: ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut sqlite3_vfs,
microseconds: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xCurrentTime: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_vfs, arg2: *mut f64) -> ::std::os::raw::c_int,
@ -910,8 +929,10 @@ pub struct sqlite3_vfs {
) -> ::std::os::raw::c_int,
>,
pub xCurrentTimeInt64: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_vfs, arg2: *mut sqlite3_int64)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut sqlite3_vfs,
arg2: *mut sqlite3_int64,
) -> ::std::os::raw::c_int,
>,
pub xSetSystemCall: ::std::option::Option<
unsafe extern "C" fn(
@ -921,12 +942,16 @@ pub struct sqlite3_vfs {
) -> ::std::os::raw::c_int,
>,
pub xGetSystemCall: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_vfs, zName: *const ::std::os::raw::c_char)
-> sqlite3_syscall_ptr,
unsafe extern "C" fn(
arg1: *mut sqlite3_vfs,
zName: *const ::std::os::raw::c_char,
) -> sqlite3_syscall_ptr,
>,
pub xNextSystemCall: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_vfs, zName: *const ::std::os::raw::c_char)
-> *const ::std::os::raw::c_char,
unsafe extern "C" fn(
arg1: *mut sqlite3_vfs,
zName: *const ::std::os::raw::c_char,
) -> *const ::std::os::raw::c_char,
>,
}
#[test]
@ -1192,8 +1217,10 @@ pub struct sqlite3_mem_methods {
>,
pub xFree: ::std::option::Option<unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void)>,
pub xRealloc: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void, arg2: ::std::os::raw::c_int)
-> *mut ::std::os::raw::c_void,
unsafe extern "C" fn(
arg1: *mut ::std::os::raw::c_void,
arg2: ::std::os::raw::c_int,
) -> *mut ::std::os::raw::c_void,
>,
pub xSize: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void) -> ::std::os::raw::c_int,
@ -1331,8 +1358,10 @@ extern "C" {
pub fn sqlite3_busy_handler(
arg1: *mut sqlite3,
arg2: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void, arg2: ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut ::std::os::raw::c_void,
arg2: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
arg3: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int;
@ -1610,6 +1639,9 @@ extern "C" {
extern "C" {
pub fn sqlite3_expanded_sql(pStmt: *mut sqlite3_stmt) -> *mut ::std::os::raw::c_char;
}
extern "C" {
pub fn sqlite3_normalized_sql(pStmt: *mut sqlite3_stmt) -> *const ::std::os::raw::c_char;
}
extern "C" {
pub fn sqlite3_stmt_readonly(pStmt: *mut sqlite3_stmt) -> ::std::os::raw::c_int;
}
@ -2447,8 +2479,10 @@ pub struct sqlite3_module {
) -> ::std::os::raw::c_int,
>,
pub xBestIndex: ::std::option::Option<
unsafe extern "C" fn(pVTab: *mut sqlite3_vtab, arg1: *mut sqlite3_index_info)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
pVTab: *mut sqlite3_vtab,
arg1: *mut sqlite3_index_info,
) -> ::std::os::raw::c_int,
>,
pub xDisconnect: ::std::option::Option<
unsafe extern "C" fn(pVTab: *mut sqlite3_vtab) -> ::std::os::raw::c_int,
@ -2457,8 +2491,10 @@ pub struct sqlite3_module {
unsafe extern "C" fn(pVTab: *mut sqlite3_vtab) -> ::std::os::raw::c_int,
>,
pub xOpen: ::std::option::Option<
unsafe extern "C" fn(pVTab: *mut sqlite3_vtab, ppCursor: *mut *mut sqlite3_vtab_cursor)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
pVTab: *mut sqlite3_vtab,
ppCursor: *mut *mut sqlite3_vtab_cursor,
) -> ::std::os::raw::c_int,
>,
pub xClose: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_vtab_cursor) -> ::std::os::raw::c_int,
@ -2486,8 +2522,10 @@ pub struct sqlite3_module {
) -> ::std::os::raw::c_int,
>,
pub xRowid: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_vtab_cursor, pRowid: *mut sqlite3_int64)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut sqlite3_vtab_cursor,
pRowid: *mut sqlite3_int64,
) -> ::std::os::raw::c_int,
>,
pub xUpdate: ::std::option::Option<
unsafe extern "C" fn(
@ -2525,27 +2563,38 @@ pub struct sqlite3_module {
) -> ::std::os::raw::c_int,
>,
pub xRename: ::std::option::Option<
unsafe extern "C" fn(pVtab: *mut sqlite3_vtab, zNew: *const ::std::os::raw::c_char)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
pVtab: *mut sqlite3_vtab,
zNew: *const ::std::os::raw::c_char,
) -> ::std::os::raw::c_int,
>,
pub xSavepoint: ::std::option::Option<
unsafe extern "C" fn(pVTab: *mut sqlite3_vtab, arg1: ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
pVTab: *mut sqlite3_vtab,
arg1: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xRelease: ::std::option::Option<
unsafe extern "C" fn(pVTab: *mut sqlite3_vtab, arg1: ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
pVTab: *mut sqlite3_vtab,
arg1: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xRollbackTo: ::std::option::Option<
unsafe extern "C" fn(pVTab: *mut sqlite3_vtab, arg1: ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
pVTab: *mut sqlite3_vtab,
arg1: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xShadowName: ::std::option::Option<
unsafe extern "C" fn(arg1: *const ::std::os::raw::c_char) -> ::std::os::raw::c_int,
>,
}
#[test]
fn bindgen_test_layout_sqlite3_module() {
assert_eq!(
::std::mem::size_of::<sqlite3_module>(),
184usize,
192usize,
concat!("Size of: ", stringify!(sqlite3_module))
);
assert_eq!(
@ -2783,6 +2832,16 @@ fn bindgen_test_layout_sqlite3_module() {
stringify!(xRollbackTo)
)
);
assert_eq!(
unsafe { &(*(::std::ptr::null::<sqlite3_module>())).xShadowName as *const _ as usize },
184usize,
concat!(
"Offset of field: ",
stringify!(sqlite3_module),
"::",
stringify!(xShadowName)
)
);
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
@ -3828,8 +3887,10 @@ pub struct sqlite3_pcache_methods {
>,
pub xShutdown: ::std::option::Option<unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void)>,
pub xCreate: ::std::option::Option<
unsafe extern "C" fn(szPage: ::std::os::raw::c_int, bPurgeable: ::std::os::raw::c_int)
-> *mut sqlite3_pcache,
unsafe extern "C" fn(
szPage: ::std::os::raw::c_int,
bPurgeable: ::std::os::raw::c_int,
) -> *mut sqlite3_pcache,
>,
pub xCachesize: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut sqlite3_pcache, nCachesize: ::std::os::raw::c_int),
@ -4586,8 +4647,10 @@ pub struct Fts5ExtensionApi {
unsafe extern "C" fn(arg1: *mut Fts5Context) -> ::std::os::raw::c_int,
>,
pub xRowCount: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut Fts5Context, pnRow: *mut sqlite3_int64)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut Fts5Context,
pnRow: *mut sqlite3_int64,
) -> ::std::os::raw::c_int,
>,
pub xColumnTotalSize: ::std::option::Option<
unsafe extern "C" fn(
@ -4618,12 +4681,16 @@ pub struct Fts5ExtensionApi {
unsafe extern "C" fn(arg1: *mut Fts5Context) -> ::std::os::raw::c_int,
>,
pub xPhraseSize: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut Fts5Context, iPhrase: ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut Fts5Context,
iPhrase: ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xInstCount: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut Fts5Context, pnInst: *mut ::std::os::raw::c_int)
-> ::std::os::raw::c_int,
unsafe extern "C" fn(
arg1: *mut Fts5Context,
pnInst: *mut ::std::os::raw::c_int,
) -> ::std::os::raw::c_int,
>,
pub xInst: ::std::option::Option<
unsafe extern "C" fn(
@ -4669,14 +4736,14 @@ pub struct Fts5ExtensionApi {
unsafe extern "C" fn(
arg1: *mut Fts5Context,
pAux: *mut ::std::os::raw::c_void,
xDelete: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void),
>,
xDelete: ::std::option::Option<unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void)>,
) -> ::std::os::raw::c_int,
>,
pub xGetAuxdata: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut Fts5Context, bClear: ::std::os::raw::c_int)
-> *mut ::std::os::raw::c_void,
unsafe extern "C" fn(
arg1: *mut Fts5Context,
bClear: ::std::os::raw::c_int,
) -> *mut ::std::os::raw::c_void,
>,
pub xPhraseFirst: ::std::option::Option<
unsafe extern "C" fn(
@ -5020,9 +5087,7 @@ pub struct fts5_api {
zName: *const ::std::os::raw::c_char,
pContext: *mut ::std::os::raw::c_void,
pTokenizer: *mut fts5_tokenizer,
xDestroy: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void),
>,
xDestroy: ::std::option::Option<unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void)>,
) -> ::std::os::raw::c_int,
>,
pub xFindTokenizer: ::std::option::Option<
@ -5039,9 +5104,7 @@ pub struct fts5_api {
zName: *const ::std::os::raw::c_char,
pContext: *mut ::std::os::raw::c_void,
xFunction: fts5_extension_function,
xDestroy: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void),
>,
xDestroy: ::std::option::Option<unsafe extern "C" fn(arg1: *mut ::std::os::raw::c_void)>,
) -> ::std::os::raw::c_int,
>,
}

File diff suppressed because it is too large Load Diff

View File

@ -123,9 +123,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.25.2"
#define SQLITE_VERSION_NUMBER 3025002
#define SQLITE_SOURCE_ID "2018-09-25 19:08:10 fb90e7189ae6d62e77ba3a308ca5d683f90bbe633cf681865365b8e92792d1c7"
#define SQLITE_VERSION "3.26.0"
#define SQLITE_VERSION_NUMBER 3026000
#define SQLITE_SOURCE_ID "2018-12-01 12:34:55 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9"
/*
** CAPI3REF: Run-Time Library Version Numbers
@ -2017,6 +2017,7 @@ struct sqlite3_mem_methods {
** is invoked.
**
** <dl>
** [[SQLITE_DBCONFIG_LOOKASIDE]]
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
** <dd> ^This option takes three additional arguments that determine the
** [lookaside memory allocator] configuration for the [database connection].
@ -2039,6 +2040,7 @@ struct sqlite3_mem_methods {
** memory is in use leaves the configuration unchanged and returns
** [SQLITE_BUSY].)^</dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
** <dd> ^This option is used to enable or disable the enforcement of
** [foreign key constraints]. There should be two additional arguments.
@ -2049,6 +2051,7 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the FK enforcement setting is not reported back. </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_TRIGGER]]
** <dt>SQLITE_DBCONFIG_ENABLE_TRIGGER</dt>
** <dd> ^This option is used to enable or disable [CREATE TRIGGER | triggers].
** There should be two additional arguments.
@ -2059,6 +2062,7 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]]
** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
** <dd> ^This option is used to enable or disable the two-argument
** version of the [fts3_tokenizer()] function which is part of the
@ -2072,6 +2076,7 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the new setting is not reported back. </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]]
** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
** interface independently of the [load_extension()] SQL function.
@ -2089,7 +2094,7 @@ struct sqlite3_mem_methods {
** be a NULL pointer, in which case the new setting is not reported back.
** </dd>
**
** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
** <dd> ^This option is used to change the name of the "main" database
** schema. ^The sole argument is a pointer to a constant UTF8 string
** which will become the new schema name in place of "main". ^SQLite
@ -2098,6 +2103,7 @@ struct sqlite3_mem_methods {
** until after the database connection closes.
** </dd>
**
** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
** <dd> Usually, when a database in wal mode is closed or detached from a
** database handle, SQLite checks if this will mean that there are now no
@ -2111,7 +2117,7 @@ struct sqlite3_mem_methods {
** have been disabled - 0 if they are not disabled, 1 if they are.
** </dd>
**
** <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
** [[SQLITE_DBCONFIG_ENABLE_QPSG]] <dt>SQLITE_DBCONFIG_ENABLE_QPSG</dt>
** <dd>^(The SQLITE_DBCONFIG_ENABLE_QPSG option activates or deactivates
** the [query planner stability guarantee] (QPSG). When the QPSG is active,
** a single SQL query statement will always use the same algorithm regardless
@ -2127,7 +2133,7 @@ struct sqlite3_mem_methods {
** following this call.
** </dd>
**
** <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
** [[SQLITE_DBCONFIG_TRIGGER_EQP]] <dt>SQLITE_DBCONFIG_TRIGGER_EQP</dt>
** <dd> By default, the output of EXPLAIN QUERY PLAN commands does not
** include output for any operations performed by trigger programs. This
** option is used to set or clear (the default) a flag that governs this
@ -2139,7 +2145,7 @@ struct sqlite3_mem_methods {
** it is not disabled, 1 if it is.
** </dd>
**
** <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
** [[SQLITE_DBCONFIG_RESET_DATABASE]] <dt>SQLITE_DBCONFIG_RESET_DATABASE</dt>
** <dd> Set the SQLITE_DBCONFIG_RESET_DATABASE flag and then run
** [VACUUM] in order to reset a database back to an empty database
** with no schema and no content. The following process works even for
@ -2158,6 +2164,18 @@ struct sqlite3_mem_methods {
** Because resetting a database is destructive and irreversible, the
** process requires the use of this obscure API and multiple steps to help
** ensure that it does not happen by accident.
**
** [[SQLITE_DBCONFIG_DEFENSIVE]] <dt>SQLITE_DBCONFIG_DEFENSIVE</dt>
** <dd>The SQLITE_DBCONFIG_DEFENSIVE option activates or deactivates the
** "defensive" flag for a database connection. When the defensive
** flag is enabled, language features that allow ordinary SQL to
** deliberately corrupt the database file are disabled. The disabled
** features include but are not limited to the following:
** <ul>
** <li> The [PRAGMA writable_schema=ON] statement.
** <li> Writes to the [sqlite_dbpage] virtual table.
** <li> Direct writes to [shadow tables].
** </ul>
** </dd>
** </dl>
*/
@ -2171,7 +2189,8 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */
#define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */
#define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */
#define SQLITE_DBCONFIG_MAX 1009 /* Largest DBCONFIG */
#define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */
#define SQLITE_DBCONFIG_MAX 1010 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@ -3609,9 +3628,19 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** on this hint by avoiding the use of [lookaside memory] so as not to
** deplete the limited store of lookaside memory. Future versions of
** SQLite may act on this hint differently.
**
** [[SQLITE_PREPARE_NORMALIZE]] ^(<dt>SQLITE_PREPARE_NORMALIZE</dt>
** <dd>The SQLITE_PREPARE_NORMALIZE flag indicates that a normalized
** representation of the SQL statement should be calculated and then
** associated with the prepared statement, which can be obtained via
** the [sqlite3_normalized_sql()] interface.)^ The semantics used to
** normalize a SQL statement are unspecified and subject to change.
** At a minimum, literal values will be replaced with suitable
** placeholders.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
/*
** CAPI3REF: Compiling An SQL Statement
@ -3769,6 +3798,11 @@ SQLITE_API int sqlite3_prepare16_v3(
** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8
** string containing the SQL text of prepared statement P with
** [bound parameters] expanded.
** ^The sqlite3_normalized_sql(P) interface returns a pointer to a UTF-8
** string containing the normalized SQL text of prepared statement P. The
** semantics used to normalize a SQL statement are unspecified and subject
** to change. At a minimum, literal values will be replaced with suitable
** placeholders.
**
** ^(For example, if a prepared statement is created using the SQL
** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345
@ -3784,14 +3818,16 @@ SQLITE_API int sqlite3_prepare16_v3(
** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
** option causes sqlite3_expanded_sql() to always return NULL.
**
** ^The string returned by sqlite3_sql(P) is managed by SQLite and is
** automatically freed when the prepared statement is finalized.
** ^The strings returned by sqlite3_sql(P) and sqlite3_normalized_sql(P)
** are managed by SQLite and are automatically freed when the prepared
** statement is finalized.
** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
** is obtained from [sqlite3_malloc()] and must be free by the application
** by passing it to [sqlite3_free()].
*/
SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
SQLITE_API const char *sqlite3_normalized_sql(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Determine If An SQL Statement Writes The Database
@ -6281,6 +6317,9 @@ struct sqlite3_module {
int (*xSavepoint)(sqlite3_vtab *pVTab, int);
int (*xRelease)(sqlite3_vtab *pVTab, int);
int (*xRollbackTo)(sqlite3_vtab *pVTab, int);
/* The methods above are in versions 1 and 2 of the sqlite_module object.
** Those below are for version 3 and greater. */
int (*xShadowName)(const char*);
};
/*
@ -7203,6 +7242,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19
@ -8615,6 +8655,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3*, int op, ...);
** can use to customize and optimize their behavior.
**
** <dl>
** [[SQLITE_VTAB_CONSTRAINT_SUPPORT]]
** <dt>SQLITE_VTAB_CONSTRAINT_SUPPORT
** <dd>Calls of the form
** [sqlite3_vtab_config](db,SQLITE_VTAB_CONSTRAINT_SUPPORT,X) are supported,
@ -9384,7 +9425,7 @@ struct sqlite3_rtree_query_info {
sqlite3_int64 iRowid; /* Rowid for current entry */
sqlite3_rtree_dbl rParentScore; /* Score of parent node */
int eParentWithin; /* Visibility of parent node */
int eWithin; /* OUT: Visiblity */
int eWithin; /* OUT: Visibility */
sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
/* The following fields are only available in 3.8.11 and later */
sqlite3_value **apSqlParam; /* Original SQL values of parameters */
@ -9880,12 +9921,38 @@ SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);
** consecutively. There is no chance that the iterator will visit a change
** the applies to table X, then one for table Y, and then later on visit
** another change for table X.
**
** The behavior of sqlite3changeset_start_v2() and its streaming equivalent
** may be modified by passing a combination of
** [SQLITE_CHANGESETSTART_INVERT | supported flags] as the 4th parameter.
**
** Note that the sqlite3changeset_start_v2() API is still <b>experimental</b>
** and therefore subject to change.
*/
SQLITE_API int sqlite3changeset_start(
sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */
int nChangeset, /* Size of changeset blob in bytes */
void *pChangeset /* Pointer to blob containing changeset */
);
SQLITE_API int sqlite3changeset_start_v2(
sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */
int nChangeset, /* Size of changeset blob in bytes */
void *pChangeset, /* Pointer to blob containing changeset */
int flags /* SESSION_CHANGESETSTART_* flags */
);
/*
** CAPI3REF: Flags for sqlite3changeset_start_v2
**
** The following flags may passed via the 4th parameter to
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
**
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
** Invert the changeset while iterating through it. This is equivalent to
** inverting a changeset using sqlite3changeset_invert() before applying it.
** It is an error to specify this flag with a patchset.
*/
#define SQLITE_CHANGESETSTART_INVERT 0x0002
/*
@ -10540,7 +10607,7 @@ SQLITE_API int sqlite3changeset_apply_v2(
),
void *pCtx, /* First argument passed to xConflict */
void **ppRebase, int *pnRebase, /* OUT: Rebase data */
int flags /* Combination of SESSION_APPLY_* flags */
int flags /* SESSION_CHANGESETAPPLY_* flags */
);
/*
@ -10558,8 +10625,14 @@ SQLITE_API int sqlite3changeset_apply_v2(
** causes the sessions module to omit this savepoint. In this case, if the
** caller has an open transaction or savepoint when apply_v2() is called,
** it may revert the partially applied changeset by rolling it back.
**
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
** Invert the changeset before applying it. This is equivalent to inverting
** a changeset using sqlite3changeset_invert() before applying it. It is
** an error to specify this flag with a patchset.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
/*
** CAPI3REF: Constants Passed To The Conflict Handler
@ -10953,6 +11026,12 @@ SQLITE_API int sqlite3changeset_start_strm(
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn
);
SQLITE_API int sqlite3changeset_start_v2_strm(
sqlite3_changeset_iter **pp,
int (*xInput)(void *pIn, void *pData, int *pnData),
void *pIn,
int flags
);
SQLITE_API int sqlite3session_changeset_strm(
sqlite3_session *pSession,
int (*xOutput)(void *pOut, const void *pData, int nData),
@ -10979,6 +11058,45 @@ SQLITE_API int sqlite3rebaser_rebase_strm(
void *pOut
);
/*
** CAPI3REF: Configure global parameters
**
** The sqlite3session_config() interface is used to make global configuration
** changes to the sessions module in order to tune it to the specific needs
** of the application.
**
** The sqlite3session_config() interface is not threadsafe. If it is invoked
** while any other thread is inside any other sessions method then the
** results are undefined. Furthermore, if it is invoked after any sessions
** related objects have been created, the results are also undefined.
**
** The first argument to the sqlite3session_config() function must be one
** of the SQLITE_SESSION_CONFIG_XXX constants defined below. The
** interpretation of the (void*) value passed as the second parameter and
** the effect of calling this function depends on the value of the first
** parameter.
**
** <dl>
** <dt>SQLITE_SESSION_CONFIG_STRMSIZE<dd>
** By default, the sessions module streaming interfaces attempt to input
** and output data in approximately 1 KiB chunks. This operand may be used
** to set and query the value of this configuration setting. The pointer
** passed as the second argument must point to a value of type (int).
** If this value is greater than 0, it is used as the new streaming data
** chunk size for both input and output. Before returning, the (int) value
** pointed to by pArg is set to the final value of the streaming interface
** chunk size.
** </dl>
**
** This function returns SQLITE_OK if successful, or an SQLite error code
** otherwise.
*/
SQLITE_API int sqlite3session_config(int op, void *pArg);
/*
** CAPI3REF: Values for sqlite3session_config().
*/
#define SQLITE_SESSION_CONFIG_STRMSIZE 1
/*
** Make sure we can call this stuff from C++.

View File

@ -310,12 +310,15 @@ struct sqlite3_api_routines {
int (*str_errcode)(sqlite3_str*);
int (*str_length)(sqlite3_str*);
char *(*str_value)(sqlite3_str*);
/* Version 3.25.0 and later */
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*),
void (*xValue)(sqlite3_context*),
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
void(*xDestroy)(void*));
/* Version 3.26.0 and later */
const char *(*normalized_sql)(sqlite3_stmt*);
};
/*
@ -603,6 +606,8 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_str_value sqlite3_api->str_value
/* Version 3.25.0 and later */
#define sqlite3_create_window_function sqlite3_api->create_window_function
/* Version 3.26.0 and later */
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)

View File

@ -98,7 +98,7 @@ impl Error {
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Error code {}: {}",
@ -115,9 +115,10 @@ impl error::Error for Error {
}
// Result codes.
// Note: These are not public because our bindgen bindings export whichever constants are present
// in the current version of SQLite. We repeat them here so we don't have to worry about which
// version of SQLite added which constants, and we only use them to implement code_to_str below.
// Note: These are not public because our bindgen bindings export whichever
// constants are present in the current version of SQLite. We repeat them here
// so we don't have to worry about which version of SQLite added which
// constants, and we only use them to implement code_to_str below.
const SQLITE_NOTICE: c_int = 27;
const SQLITE_WARNING: c_int = 28;

View File

@ -16,32 +16,36 @@ pub fn SQLITE_TRANSIENT() -> sqlite3_destructor_type {
}
/// Run-Time Limit Categories
#[repr(C)]
#[repr(i32)]
pub enum Limit {
/// The maximum size of any string or BLOB or table row, in bytes.
SQLITE_LIMIT_LENGTH = SQLITE_LIMIT_LENGTH as isize,
SQLITE_LIMIT_LENGTH = SQLITE_LIMIT_LENGTH,
/// The maximum length of an SQL statement, in bytes.
SQLITE_LIMIT_SQL_LENGTH = SQLITE_LIMIT_SQL_LENGTH as isize,
/// The maximum number of columns in a table definition or in the result set of a SELECT
/// or the maximum number of columns in an index or in an ORDER BY or GROUP BY clause.
SQLITE_LIMIT_COLUMN = SQLITE_LIMIT_COLUMN as isize,
SQLITE_LIMIT_SQL_LENGTH = SQLITE_LIMIT_SQL_LENGTH,
/// The maximum number of columns in a table definition or in the result set
/// of a SELECT or the maximum number of columns in an index or in an
/// ORDER BY or GROUP BY clause.
SQLITE_LIMIT_COLUMN = SQLITE_LIMIT_COLUMN,
/// The maximum depth of the parse tree on any expression.
SQLITE_LIMIT_EXPR_DEPTH = SQLITE_LIMIT_EXPR_DEPTH as isize,
SQLITE_LIMIT_EXPR_DEPTH = SQLITE_LIMIT_EXPR_DEPTH,
/// The maximum number of terms in a compound SELECT statement.
SQLITE_LIMIT_COMPOUND_SELECT = SQLITE_LIMIT_COMPOUND_SELECT as isize,
/// The maximum number of instructions in a virtual machine program used to implement an SQL statement.
SQLITE_LIMIT_VDBE_OP = SQLITE_LIMIT_VDBE_OP as isize,
SQLITE_LIMIT_COMPOUND_SELECT = SQLITE_LIMIT_COMPOUND_SELECT,
/// The maximum number of instructions in a virtual machine program used to
/// implement an SQL statement.
SQLITE_LIMIT_VDBE_OP = SQLITE_LIMIT_VDBE_OP,
/// The maximum number of arguments on a function.
SQLITE_LIMIT_FUNCTION_ARG = SQLITE_LIMIT_FUNCTION_ARG as isize,
SQLITE_LIMIT_FUNCTION_ARG = SQLITE_LIMIT_FUNCTION_ARG,
/// The maximum number of attached databases.
SQLITE_LIMIT_ATTACHED = SQLITE_LIMIT_ATTACHED as isize,
/// The maximum length of the pattern argument to the LIKE or GLOB operators.
SQLITE_LIMIT_LIKE_PATTERN_LENGTH = SQLITE_LIMIT_LIKE_PATTERN_LENGTH as isize,
SQLITE_LIMIT_ATTACHED = SQLITE_LIMIT_ATTACHED,
/// The maximum length of the pattern argument to the LIKE or GLOB
/// operators.
SQLITE_LIMIT_LIKE_PATTERN_LENGTH = SQLITE_LIMIT_LIKE_PATTERN_LENGTH,
/// The maximum index number of any parameter in an SQL statement.
SQLITE_LIMIT_VARIABLE_NUMBER = SQLITE_LIMIT_VARIABLE_NUMBER as isize,
SQLITE_LIMIT_VARIABLE_NUMBER = SQLITE_LIMIT_VARIABLE_NUMBER,
/// The maximum depth of recursion for triggers.
SQLITE_LIMIT_TRIGGER_DEPTH = 10,
/// The maximum number of auxiliary worker threads that a single prepared statement may start.
/// The maximum number of auxiliary worker threads that a single prepared
/// statement may start.
SQLITE_LIMIT_WORKER_THREADS = 11,
}

View File

@ -4,7 +4,7 @@ cd $SCRIPT_DIR
export SQLITE3_LIB_DIR=$SCRIPT_DIR/sqlite3
# Download and extract amalgamation
SQLITE=sqlite-amalgamation-3250200
SQLITE=sqlite-amalgamation-3260000
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
@ -22,5 +22,5 @@ find $SCRIPT_DIR/target -type f -name bindgen.rs -exec cp {} $SQLITE3_LIB_DIR/bi
# Sanity check
cd $SCRIPT_DIR/..
cargo update
cargo test --features "backup blob chrono functions limits load_extension serde_json trace bundled"
cargo test --features "backup blob chrono functions limits load_extension serde_json trace vtab bundled"
echo 'You should increment the version in libsqlite3-sys/Cargo.toml'

View File

@ -23,8 +23,8 @@
//! dst: P,
//! progress: fn(backup::Progress),
//! ) -> Result<()> {
//! let mut dst = try!(Connection::open(dst));
//! let backup = try!(backup::Backup::new(src, &mut dst));
//! let mut dst = Connection::open(dst)?;
//! let backup = backup::Backup::new(src, &mut dst)?;
//! backup.run_to_completion(5, time::Duration::from_millis(250), Some(progress))
//! }
//! ```
@ -37,10 +37,10 @@ use std::os::raw::c_int;
use std::thread;
use std::time::Duration;
use ffi;
use crate::ffi;
use error::{error_from_handle, error_from_sqlite_code};
use {Connection, DatabaseName, Result};
use crate::error::{error_from_handle, error_from_sqlite_code};
use crate::{Connection, DatabaseName, Result};
impl Connection {
/// Back up the `name` database to the given destination path.
@ -57,22 +57,17 @@ impl Connection {
/// or if the backup fails.
pub fn backup<P: AsRef<Path>>(
&self,
name: DatabaseName,
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 mut dst = Connection::open(dst_path)?;
let backup = Backup::new_with_names(self, name, &mut dst, DatabaseName::Main)?;
let mut r = More;
while r == More {
r = try!(backup.step(100));
r = backup.step(100)?;
if let Some(f) = progress {
f(backup.progress());
}
@ -100,18 +95,18 @@ impl Connection {
/// or if the restore fails.
pub fn restore<P: AsRef<Path>, F: Fn(Progress)>(
&mut self,
name: DatabaseName,
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));
let src = Connection::open(src_path)?;
let restore = Backup::new_with_names(&src, DatabaseName::Main, self, name)?;
let mut r = More;
let mut busy_count = 0i32;
'restore_loop: while r == More || r == Busy {
r = try!(restore.step(100));
r = restore.step(100)?;
if let Some(ref f) = progress {
f(restore.progress());
}
@ -197,12 +192,12 @@ impl<'a, 'b> Backup<'a, 'b> {
/// `NULL`.
pub fn new_with_names(
from: &'a Connection,
from_name: DatabaseName,
from_name: DatabaseName<'_>,
to: &'b mut Connection,
to_name: DatabaseName,
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_name = to_name.to_cstring()?;
let from_name = from_name.to_cstring()?;
let to_db = to.db.borrow_mut().db;
@ -287,7 +282,7 @@ impl<'a, 'b> Backup<'a, 'b> {
assert!(pages_per_step > 0, "pages_per_step must be positive");
loop {
let r = try!(self.step(pages_per_step));
let r = self.step(pages_per_step)?;
if let Some(progress) = progress {
progress(self.progress())
}
@ -308,8 +303,8 @@ impl<'a, 'b> Drop for Backup<'a, 'b> {
#[cfg(test)]
mod test {
use super::Backup;
use crate::{Connection, DatabaseName, NO_PARAMS};
use std::time::Duration;
use {Connection, DatabaseName, NO_PARAMS};
#[test]
fn test_backup() {

View File

@ -14,9 +14,6 @@
//! ## Example
//!
//! ```rust
//! extern crate libsqlite3_sys;
//! extern crate rusqlite;
//!
//! use rusqlite::blob::ZeroBlob;
//! use rusqlite::{Connection, DatabaseName, NO_PARAMS};
//! use std::io::{Read, Seek, SeekFrom, Write};
@ -64,7 +61,7 @@ use std::ptr;
use super::ffi;
use super::types::{ToSql, ToSqlOutput};
use {Connection, DatabaseName, Result};
use crate::{Connection, DatabaseName, Result};
/// Handle to an open BLOB.
pub struct Blob<'conn> {
@ -84,7 +81,7 @@ impl Connection {
/// fails.
pub fn blob_open<'a>(
&'a self,
db: DatabaseName,
db: DatabaseName<'_>,
table: &str,
column: &str,
row_id: i64,
@ -92,9 +89,9 @@ impl Connection {
) -> 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 db = db.to_cstring()?;
let table = super::str_to_cstring(table)?;
let column = super::str_to_cstring(column)?;
let rc = unsafe {
ffi::sqlite3_blob_open(
c.db(),
@ -254,7 +251,7 @@ impl<'conn> Drop for Blob<'conn> {
pub struct ZeroBlob(pub i32);
impl ToSql for ZeroBlob {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
let ZeroBlob(length) = *self;
Ok(ToSqlOutput::ZeroBlob(length))
}
@ -262,16 +259,16 @@ impl ToSql for ZeroBlob {
#[cfg(test)]
mod test {
use crate::{Connection, DatabaseName, Result};
use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write};
use {Connection, DatabaseName, Result};
fn db_with_test_blob() -> Result<(Connection, i64)> {
let db = try!(Connection::open_in_memory());
let db = Connection::open_in_memory()?;
let sql = "BEGIN;
CREATE TABLE test (content BLOB);
INSERT INTO test VALUES (ZEROBLOB(10));
END;";
try!(db.execute_batch(sql));
db.execute_batch(sql)?;
let rowid = db.last_insert_rowid();
Ok((db, rowid))
}

View File

@ -1,11 +1,12 @@
///! Busy handler (when the database is locked)
use std::mem;
use std::os::raw::{c_int, c_void};
use std::panic::catch_unwind;
use std::ptr;
use std::time::Duration;
use ffi;
use {Connection, InnerConnection, Result};
use crate::ffi;
use crate::{Connection, InnerConnection, Result};
impl Connection {
/// Set a busy handler that sleeps for a specified amount of time when a
@ -48,7 +49,7 @@ impl Connection {
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) {
if let Ok(true) = catch_unwind(|| handler_fn(count)) {
1
} else {
0
@ -74,14 +75,14 @@ impl InnerConnection {
#[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 tempdir;
use {Connection, Error, ErrorCode, TransactionBehavior, NO_PARAMS};
use crate::{Connection, Error, ErrorCode, TransactionBehavior, NO_PARAMS};
#[test]
fn test_default_busy() {

View File

@ -1,10 +1,10 @@
//! Prepared statements cache for faster execution.
use crate::raw_statement::RawStatement;
use crate::{Connection, Result, Statement};
use lru_cache::LruCache;
use raw_statement::RawStatement;
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use {Connection, Result, Statement};
impl Connection {
/// Prepare a SQL statement for execution, returning a previously prepared
@ -16,14 +16,14 @@ impl Connection {
/// # use rusqlite::{Connection, Result};
/// fn insert_new_people(conn: &Connection) -> Result<()> {
/// {
/// let mut stmt = try!(conn.prepare_cached("INSERT INTO People (name) VALUES (?)"));
/// try!(stmt.execute(&["Joe Smith"]));
/// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?;
/// stmt.execute(&["Joe Smith"])?;
/// }
/// {
/// // This will return the same underlying SQLite statement handle without
/// // having to prepare it again.
/// let mut stmt = try!(conn.prepare_cached("INSERT INTO People (name) VALUES (?)"));
/// try!(stmt.execute(&["Bob Jones"]));
/// let mut stmt = conn.prepare_cached("INSERT INTO People (name) VALUES (?)")?;
/// stmt.execute(&["Bob Jones"])?;
/// }
/// Ok(())
/// }
@ -33,7 +33,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 prepare_cached<'a>(&'a self, sql: &str) -> Result<CachedStatement<'a>> {
pub fn prepare_cached(&self, sql: &str) -> Result<CachedStatement<'_>> {
self.cache.get(self, sql)
}
@ -152,7 +152,7 @@ impl StatementCache {
#[cfg(test)]
mod test {
use super::StatementCache;
use {Connection, NO_PARAMS};
use crate::{Connection, NO_PARAMS};
impl StatementCache {
fn clear(&self) {

View File

@ -1,69 +1,18 @@
//! 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};
use std::os::raw::{c_int, c_void};
#[cfg(feature = "array")]
use std::rc::Rc;
use ffi;
use ffi::sqlite3_context;
use ffi::sqlite3_value;
use crate::ffi;
use crate::ffi::sqlite3_context;
use str_to_cstring;
use types::{ToSqlOutput, ValueRef};
use crate::str_to_cstring;
use crate::types::{ToSqlOutput, ValueRef};
#[cfg(feature = "array")]
use vtab::array::{free_array, ARRAY_TYPE};
use crate::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>) {
pub(crate) unsafe fn set_result(ctx: *mut sqlite3_context, result: &ToSqlOutput<'_>) {
let value = match *result {
ToSqlOutput::Borrowed(v) => v,
ToSqlOutput::Owned(ref v) => ValueRef::from(v),

View File

@ -1,18 +1,14 @@
use crate::types::Type;
use crate::{errmsg_to_string, ffi};
use std::error;
use std::fmt;
use std::os::raw::c_int;
use std::path::PathBuf;
use std::str;
use types::Type;
use {errmsg_to_string, ffi};
/// Old name for `Error`. `SqliteError` is deprecated.
#[deprecated(since = "0.6.0", note = "Use Error instead")]
pub type SqliteError = Error;
/// Enum listing possible errors from rusqlite.
#[derive(Debug)]
#[allow(enum_variant_names)]
#[allow(clippy::enum_variant_names)]
pub enum Error {
/// An error from an underlying SQLite call.
SqliteFailure(ffi::Error, Option<String>),
@ -23,7 +19,7 @@ pub enum Error {
/// Error when the value of a particular column is requested, but it cannot
/// be converted to the requested Rust type.
FromSqlConversionFailure(usize, Type, Box<error::Error + Send + Sync>),
FromSqlConversionFailure(usize, Type, Box<dyn 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`).
@ -82,10 +78,10 @@ pub enum Error {
/// `create_scalar_function`).
#[cfg(feature = "functions")]
#[allow(dead_code)]
UserFunctionError(Box<error::Error + Send + Sync>),
UserFunctionError(Box<dyn error::Error + Send + Sync>),
/// Error available for the implementors of the `ToSql` trait.
ToSqlConversionFailure(Box<error::Error + Send + Sync>),
ToSqlConversionFailure(Box<dyn error::Error + Send + Sync>),
/// Error when the SQL is not a `SELECT`, is not read-only.
InvalidQuery,
@ -96,8 +92,57 @@ pub enum Error {
#[allow(dead_code)]
ModuleError(String),
#[cfg(feature = "functions")]
UnwindingPanic,
/// An error returned when `Context::get_aux` attempts to retrieve data
/// of a different type than what had been stored using `Context::set_aux`.
#[cfg(feature = "functions")]
GetAuxWrongType,
/// Error when the SQL contains multiple statements.
MultipleStatement,
MultipleStatement,}
impl PartialEq for Error {
fn eq(&self, other: &Error) -> bool {
match (self, other) {
(Error::SqliteFailure(e1, s1), Error::SqliteFailure(e2, s2)) => e1 == e2 && s1 == s2,
(Error::SqliteSingleThreadedMode, Error::SqliteSingleThreadedMode) => true,
(Error::IntegralValueOutOfRange(i1, n1), Error::IntegralValueOutOfRange(i2, n2)) => {
i1 == i2 && n1 == n2
}
(Error::Utf8Error(e1), Error::Utf8Error(e2)) => e1 == e2,
(Error::NulError(e1), Error::NulError(e2)) => e1 == e2,
(Error::InvalidParameterName(n1), Error::InvalidParameterName(n2)) => n1 == n2,
(Error::InvalidPath(p1), Error::InvalidPath(p2)) => p1 == p2,
(Error::ExecuteReturnedResults, Error::ExecuteReturnedResults) => true,
(Error::QueryReturnedNoRows, Error::QueryReturnedNoRows) => true,
(Error::InvalidColumnIndex(i1), Error::InvalidColumnIndex(i2)) => i1 == i2,
(Error::InvalidColumnName(n1), Error::InvalidColumnName(n2)) => n1 == n2,
(Error::InvalidColumnType(i1, t1), Error::InvalidColumnType(i2, t2)) => {
i1 == i2 && t1 == t2
}
(Error::StatementChangedRows(n1), Error::StatementChangedRows(n2)) => n1 == n2,
#[cfg(feature = "functions")]
(
Error::InvalidFunctionParameterType(i1, t1),
Error::InvalidFunctionParameterType(i2, t2),
) => i1 == i2 && t1 == t2,
#[cfg(feature = "vtab")]
(
Error::InvalidFilterParameterType(i1, t1),
Error::InvalidFilterParameterType(i2, t2),
) => i1 == i2 && t1 == t2,
(Error::InvalidQuery, Error::InvalidQuery) => true,
#[cfg(feature = "vtab")]
(Error::ModuleError(s1), Error::ModuleError(s2)) => s1 == s2,
#[cfg(feature = "functions")]
(Error::UnwindingPanic, Error::UnwindingPanic) => true,
#[cfg(feature = "functions")]
(Error::GetAuxWrongType, Error::GetAuxWrongType) => true,
(_, _) => false,
}
}
}
impl From<str::Utf8Error> for Error {
@ -113,7 +158,7 @@ impl From<::std::ffi::NulError> for Error {
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Error::SqliteFailure(ref err, None) => err.fmt(f),
Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s),
@ -158,6 +203,10 @@ impl fmt::Display for Error {
Error::InvalidQuery => write!(f, "Query is not read-only"),
#[cfg(feature = "vtab")]
Error::ModuleError(ref desc) => write!(f, "{}", desc),
#[cfg(feature = "functions")]
Error::UnwindingPanic => write!(f, "unwinding panic"),
#[cfg(feature = "functions")]
Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"),
Error::MultipleStatement => write!(f, "Multiple statements provided"),
}
}
@ -196,11 +245,15 @@ impl error::Error for Error {
Error::InvalidQuery => "query is not read-only",
#[cfg(feature = "vtab")]
Error::ModuleError(ref desc) => desc,
#[cfg(feature = "functions")]
Error::UnwindingPanic => "unwinding panic",
#[cfg(feature = "functions")]
Error::GetAuxWrongType => "get_aux called with wrong type",
Error::MultipleStatement => "multiple statements provided",
}
}
fn cause(&self) -> Option<&error::Error> {
fn cause(&self) -> Option<&dyn error::Error> {
match *self {
Error::SqliteFailure(ref err, _) => Some(err),
Error::Utf8Error(ref err) => Some(err),
@ -232,6 +285,12 @@ impl error::Error for Error {
#[cfg(feature = "vtab")]
Error::ModuleError(_) => None,
#[cfg(feature = "functions")]
Error::UnwindingPanic => None,
#[cfg(feature = "functions")]
Error::GetAuxWrongType => None,
}
}
}
@ -250,3 +309,12 @@ pub fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error {
};
error_from_sqlite_code(code, message)
}
macro_rules! check {
($funcall:expr) => {{
let rc = $funcall;
if rc != crate::ffi::SQLITE_OK {
Err(crate::error::error_from_sqlite_code(rc, None))?;
}
}};
}

View File

@ -9,10 +9,6 @@
//! module.
//!
//! ```rust
//! extern crate libsqlite3_sys;
//! extern crate rusqlite;
//! extern crate regex;
//!
//! use regex::Regex;
//! use rusqlite::{Connection, Error, Result, NO_PARAMS};
//! use std::collections::HashMap;
@ -20,7 +16,7 @@
//! fn add_regexp_function(db: &Connection) -> Result<()> {
//! let mut cached_regexes = HashMap::new();
//! db.create_scalar_function("regexp", 2, true, move |ctx| {
//! let regex_s = try!(ctx.get::<String>(0));
//! let regex_s = ctx.get::<String>(0)?;
//! let entry = cached_regexes.entry(regex_s.clone());
//! let regex = {
//! use std::collections::hash_map::Entry::{Occupied, Vacant};
@ -33,7 +29,7 @@
//! }
//! };
//!
//! let text = try!(ctx.get::<String>(1));
//! let text = ctx.get::<String>(1)?;
//! Ok(regex.is_match(&text))
//! })
//! }
@ -55,17 +51,18 @@
//! ```
use std::error::Error as StdError;
use std::os::raw::{c_int, c_void};
use std::panic::{catch_unwind, RefUnwindSafe, UnwindSafe};
use std::ptr;
use std::slice;
use ffi;
use ffi::sqlite3_context;
use ffi::sqlite3_value;
use crate::ffi;
use crate::ffi::sqlite3_context;
use crate::ffi::sqlite3_value;
use context::set_result;
use types::{FromSql, FromSqlError, ToSql, ValueRef};
use crate::context::set_result;
use crate::types::{FromSql, FromSqlError, ToSql, ValueRef};
use {str_to_cstring, Connection, Error, InnerConnection, Result};
use crate::{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
@ -137,6 +134,10 @@ impl<'a> Context<'a> {
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err)
}
#[cfg(feature = "i128_blob")]
FromSqlError::InvalidI128Size(_) => {
Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err))
}
})
}
@ -153,32 +154,32 @@ impl<'a> Context<'a> {
/// Sets the auxilliary data associated with a particular parameter. See
/// https://www.sqlite.org/c3ref/get_auxdata.html for a discussion of
/// this feature, or the unit tests of this module for an example.
pub fn set_aux<T>(&self, arg: c_int, value: T) {
let boxed = Box::into_raw(Box::new(value));
pub fn set_aux<T: 'static>(&self, arg: c_int, value: T) {
let boxed = Box::into_raw(Box::new((std::any::TypeId::of::<T>(), value)));
unsafe {
ffi::sqlite3_set_auxdata(
self.ctx,
arg,
boxed as *mut c_void,
Some(free_boxed_value::<T>),
Some(free_boxed_value::<(std::any::TypeId, T)>),
)
};
}
/// Gets the auxilliary data that was associated with a given parameter
/// via `set_aux`. Returns `None` if no data has been associated.
///
/// # Unsafety
///
/// This function is unsafe as there is no guarantee that the type `T`
/// requested matches the type `T` that was provided to `set_aux`. The
/// 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;
/// via `set_aux`. Returns `Ok(None)` if no data has been associated,
/// and .
pub fn get_aux<T: 'static>(&self, arg: c_int) -> Result<Option<&T>> {
let p = unsafe { ffi::sqlite3_get_auxdata(self.ctx, arg) as *mut (std::any::TypeId, T) };
if p.is_null() {
None
Ok(None)
} else {
Some(&*p)
let id_val = unsafe { &*p };
if std::any::TypeId::of::<T>() != id_val.0 {
Err(Error::GetAuxWrongType)
} else {
Ok(Some(&id_val.1))
}
}
}
}
@ -189,6 +190,7 @@ impl<'a> Context<'a> {
/// result. Implementations should be stateless.
pub trait Aggregate<A, T>
where
A: RefUnwindSafe + UnwindSafe,
T: ToSql,
{
/// Initializes the aggregation context. Will be called prior to the first
@ -198,14 +200,14 @@ where
/// "step" function called once for each row in an aggregate group. May be
/// called 0 times if there are no rows.
fn step(&self, &mut Context, &mut A) -> Result<()>;
fn step(&self, _: &mut Context<'_>, _: &mut A) -> Result<()>;
/// Computes and returns the final result. Will be called exactly once for
/// each invocation of the function. If `step()` was called at least
/// once, will be given `Some(A)` (the same `A` as was created by
/// `init` and given to `step`); if `step()` was not called (because
/// the function is running against 0 rows), will be given `None`.
fn finalize(&self, Option<A>) -> Result<T>;
fn finalize(&self, _: Option<A>) -> Result<T>;
}
impl Connection {
@ -224,12 +226,12 @@ impl Connection {
/// ```rust
/// # use rusqlite::{Connection, Result, NO_PARAMS};
/// fn scalar_function_example(db: Connection) -> Result<()> {
/// try!(db.create_scalar_function("halve", 1, true, |ctx| {
/// let value = try!(ctx.get::<f64>(0));
/// db.create_scalar_function("halve", 1, true, |ctx| {
/// let value = ctx.get::<f64>(0)?;
/// Ok(value / 2f64)
/// }));
/// })?;
///
/// let six_halved: f64 = try!(db.query_row("SELECT halve(6)", NO_PARAMS, |r| r.get(0)));
/// let six_halved: f64 = db.query_row("SELECT halve(6)", NO_PARAMS, |r| r.get(0))?;
/// assert_eq!(six_halved, 3f64);
/// Ok(())
/// }
@ -246,7 +248,7 @@ impl Connection {
x_func: F,
) -> Result<()>
where
F: FnMut(&Context) -> Result<T> + Send + 'static,
F: FnMut(&Context<'_>) -> Result<T> + Send + UnwindSafe + 'static,
T: ToSql,
{
self.db
@ -267,6 +269,7 @@ impl Connection {
aggr: D,
) -> Result<()>
where
A: RefUnwindSafe + UnwindSafe,
D: Aggregate<A, T>,
T: ToSql,
{
@ -297,7 +300,7 @@ impl InnerConnection {
x_func: F,
) -> Result<()>
where
F: FnMut(&Context) -> Result<T> + Send + 'static,
F: FnMut(&Context<'_>) -> Result<T> + Send + UnwindSafe + 'static,
T: ToSql,
{
unsafe extern "C" fn call_boxed_closure<F, T>(
@ -305,28 +308,36 @@ impl InnerConnection {
argc: c_int,
argv: *mut *mut sqlite3_value,
) where
F: FnMut(&Context) -> Result<T>,
F: FnMut(&Context<'_>) -> Result<T>,
T: ToSql,
{
let ctx = Context {
ctx,
args: slice::from_raw_parts(argv, argc as usize),
let r = catch_unwind(|| {
let boxed_f: *mut F = ffi::sqlite3_user_data(ctx) as *mut F;
assert!(!boxed_f.is_null(), "Internal error - null function pointer");
let ctx = Context {
ctx,
args: slice::from_raw_parts(argv, argc as usize),
};
(*boxed_f)(&ctx)
});
let t = match r {
Err(_) => {
report_error(ctx, &Error::UnwindingPanic);
return;
}
Ok(r) => r,
};
let boxed_f: *mut F = ffi::sqlite3_user_data(ctx.ctx) as *mut F;
assert!(!boxed_f.is_null(), "Internal error - null function pointer");
let t = (*boxed_f)(&ctx);
let t = t.as_ref().map(|t| ToSql::to_sql(t));
match t {
Ok(Ok(ref value)) => set_result(ctx.ctx, value),
Ok(Err(err)) => report_error(ctx.ctx, &err),
Err(err) => report_error(ctx.ctx, err),
Ok(Ok(ref value)) => set_result(ctx, value),
Ok(Err(err)) => report_error(ctx, &err),
Err(err) => report_error(ctx, err),
}
}
let boxed_f: *mut F = Box::into_raw(Box::new(x_func));
let c_name = try!(str_to_cstring(fn_name));
let c_name = str_to_cstring(fn_name)?;
let mut flags = ffi::SQLITE_UTF8;
if deterministic {
flags |= ffi::SQLITE_DETERMINISTIC;
@ -355,6 +366,7 @@ impl InnerConnection {
aggr: D,
) -> Result<()>
where
A: RefUnwindSafe + UnwindSafe,
D: Aggregate<A, T>,
T: ToSql,
{
@ -374,15 +386,10 @@ impl InnerConnection {
argc: c_int,
argv: *mut *mut sqlite3_value,
) where
A: RefUnwindSafe + UnwindSafe,
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"
);
let pac = match aggregate_context(ctx, ::std::mem::size_of::<*mut A>()) {
Some(pac) => pac,
None => {
@ -391,32 +398,40 @@ impl InnerConnection {
}
};
if (*pac as *mut A).is_null() {
*pac = Box::into_raw(Box::new((*boxed_aggr).init()));
}
let mut ctx = Context {
ctx,
args: slice::from_raw_parts(argv, argc as usize),
let r = catch_unwind(|| {
let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D;
assert!(
!boxed_aggr.is_null(),
"Internal error - null aggregate pointer"
);
if (*pac as *mut A).is_null() {
*pac = Box::into_raw(Box::new((*boxed_aggr).init()));
}
let mut ctx = Context {
ctx,
args: slice::from_raw_parts(argv, argc as usize),
};
(*boxed_aggr).step(&mut ctx, &mut **pac)
});
let r = match r {
Err(_) => {
report_error(ctx, &Error::UnwindingPanic);
return;
}
Ok(r) => r,
};
match (*boxed_aggr).step(&mut ctx, &mut **pac) {
match r {
Ok(_) => {}
Err(err) => report_error(ctx.ctx, &err),
Err(err) => report_error(ctx, &err),
};
}
unsafe extern "C" fn call_boxed_final<A, D, T>(ctx: *mut sqlite3_context)
where
A: RefUnwindSafe + UnwindSafe,
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"
);
// 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.
let a: Option<A> = match aggregate_context(ctx, 0) {
@ -431,7 +446,21 @@ impl InnerConnection {
None => None,
};
let t = (*boxed_aggr).finalize(a);
let r = catch_unwind(|| {
let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D;
assert!(
!boxed_aggr.is_null(),
"Internal error - null aggregate pointer"
);
(*boxed_aggr).finalize(a)
});
let t = match r {
Err(_) => {
report_error(ctx, &Error::UnwindingPanic);
return;
}
Ok(r) => r,
};
let t = t.as_ref().map(|t| ToSql::to_sql(t));
match t {
Ok(Ok(ref value)) => set_result(ctx, value),
@ -441,7 +470,7 @@ impl InnerConnection {
}
let boxed_aggr: *mut D = Box::into_raw(Box::new(aggr));
let c_name = try!(str_to_cstring(fn_name));
let c_name = str_to_cstring(fn_name)?;
let mut flags = ffi::SQLITE_UTF8;
if deterministic {
flags |= ffi::SQLITE_DETERMINISTIC;
@ -463,7 +492,7 @@ 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 c_name = str_to_cstring(fn_name)?;
let r = unsafe {
ffi::sqlite3_create_function_v2(
self.db(),
@ -483,19 +512,19 @@ impl InnerConnection {
#[cfg(test)]
mod test {
extern crate regex;
use regex;
use self::regex::Regex;
use std::collections::HashMap;
use std::f64::EPSILON;
use std::os::raw::c_double;
use functions::{Aggregate, Context};
use {Connection, Error, Result, NO_PARAMS};
use crate::functions::{Aggregate, Context};
use crate::{Connection, Error, Result, NO_PARAMS};
fn half(ctx: &Context) -> Result<c_double> {
assert!(ctx.len() == 1, "called with unexpected number of arguments");
let value = try!(ctx.get::<c_double>(0));
fn half(ctx: &Context<'_>) -> Result<c_double> {
assert_eq!(ctx.len(), 1, "called with unexpected number of arguments");
let value = ctx.get::<c_double>(0)?;
Ok(value / 2f64)
}
@ -523,13 +552,13 @@ mod test {
// This implementation of a regexp scalar function uses SQLite's auxilliary data
// (https://www.sqlite.org/c3ref/get_auxdata.html) to avoid recompiling the regular
// expression multiple times within one query.
fn regexp_with_auxilliary(ctx: &Context) -> Result<bool> {
assert!(ctx.len() == 2, "called with unexpected number of arguments");
fn regexp_with_auxilliary(ctx: &Context<'_>) -> Result<bool> {
assert_eq!(ctx.len(), 2, "called with unexpected number of arguments");
let saved_re: Option<&Regex> = unsafe { ctx.get_aux(0) };
let saved_re: Option<&Regex> = ctx.get_aux(0)?;
let new_re = match saved_re {
None => {
let s = try!(ctx.get::<String>(0));
let s = ctx.get::<String>(0)?;
match Regex::new(&s) {
Ok(r) => Some(r),
Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
@ -605,9 +634,9 @@ mod test {
// until the function is removed.
let mut cached_regexes = HashMap::new();
db.create_scalar_function("regexp", 2, true, move |ctx| {
assert!(ctx.len() == 2, "called with unexpected number of arguments");
assert_eq!(ctx.len(), 2, "called with unexpected number of arguments");
let regex_s = try!(ctx.get::<String>(0));
let regex_s = ctx.get::<String>(0)?;
let entry = cached_regexes.entry(regex_s.clone());
let regex = {
use std::collections::hash_map::Entry::{Occupied, Vacant};
@ -620,7 +649,7 @@ mod test {
}
};
let text = try!(ctx.get::<String>(1));
let text = ctx.get::<String>(1)?;
Ok(regex.is_match(&text))
})
.unwrap();
@ -648,7 +677,7 @@ mod test {
let mut ret = String::new();
for idx in 0..ctx.len() {
let s = try!(ctx.get::<String>(idx));
let s = ctx.get::<String>(idx)?;
ret.push_str(&s);
}
@ -666,6 +695,31 @@ mod test {
}
}
#[test]
fn test_get_aux_type_checking() {
let db = Connection::open_in_memory().unwrap();
db.create_scalar_function("example", 2, false, |ctx| {
if !ctx.get::<bool>(1)? {
ctx.set_aux::<i64>(0, 100);
} else {
assert_eq!(ctx.get_aux::<String>(0), Err(Error::GetAuxWrongType));
assert_eq!(ctx.get_aux::<i64>(0), Ok(Some(&100)));
}
Ok(true)
})
.unwrap();
let res: bool = db
.query_row(
"SELECT example(0, i) FROM (SELECT 0 as i UNION SELECT 1)",
NO_PARAMS,
|r| r.get(0),
)
.unwrap();
// Doesn't actually matter, we'll assert in the function if there's a problem.
assert!(res);
}
struct Sum;
struct Count;
@ -674,8 +728,8 @@ mod test {
0
}
fn step(&self, ctx: &mut Context, sum: &mut i64) -> Result<()> {
*sum += try!(ctx.get::<i64>(0));
fn step(&self, ctx: &mut Context<'_>, sum: &mut i64) -> Result<()> {
*sum += ctx.get::<i64>(0)?;
Ok(())
}
@ -689,7 +743,7 @@ mod test {
0
}
fn step(&self, _ctx: &mut Context, sum: &mut i64) -> Result<()> {
fn step(&self, _ctx: &mut Context<'_>, sum: &mut i64) -> Result<()> {
*sum += 1;
Ok(())
}

View File

@ -2,89 +2,29 @@
#![allow(non_camel_case_types)]
use std::os::raw::{c_char, c_int, c_void};
use std::panic::catch_unwind;
use std::ptr;
use ffi;
use crate::ffi;
use {Connection, InnerConnection};
use crate::{Connection, InnerConnection};
/// Authorizer Action Codes
#[derive(Debug, PartialEq)]
/// Action Codes
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(i32)]
pub enum Action {
UNKNOWN = -1,
SQLITE_CREATE_INDEX = ffi::SQLITE_CREATE_INDEX as isize,
SQLITE_CREATE_TABLE = ffi::SQLITE_CREATE_TABLE as isize,
SQLITE_CREATE_TEMP_INDEX = ffi::SQLITE_CREATE_TEMP_INDEX as isize,
SQLITE_CREATE_TEMP_TABLE = ffi::SQLITE_CREATE_TEMP_TABLE as isize,
SQLITE_CREATE_TEMP_TRIGGER = ffi::SQLITE_CREATE_TEMP_TRIGGER as isize,
SQLITE_CREATE_TEMP_VIEW = ffi::SQLITE_CREATE_TEMP_VIEW as isize,
SQLITE_CREATE_TRIGGER = ffi::SQLITE_CREATE_TRIGGER as isize,
SQLITE_CREATE_VIEW = ffi::SQLITE_CREATE_VIEW as isize,
SQLITE_DELETE = ffi::SQLITE_DELETE as isize,
SQLITE_DROP_INDEX = ffi::SQLITE_DROP_INDEX as isize,
SQLITE_DROP_TABLE = ffi::SQLITE_DROP_TABLE as isize,
SQLITE_DROP_TEMP_INDEX = ffi::SQLITE_DROP_TEMP_INDEX as isize,
SQLITE_DROP_TEMP_TABLE = ffi::SQLITE_DROP_TEMP_TABLE as isize,
SQLITE_DROP_TEMP_TRIGGER = ffi::SQLITE_DROP_TEMP_TRIGGER as isize,
SQLITE_DROP_TEMP_VIEW = ffi::SQLITE_DROP_TEMP_VIEW as isize,
SQLITE_DROP_TRIGGER = ffi::SQLITE_DROP_TRIGGER as isize,
SQLITE_DROP_VIEW = ffi::SQLITE_DROP_VIEW as isize,
SQLITE_INSERT = ffi::SQLITE_INSERT as isize,
SQLITE_PRAGMA = ffi::SQLITE_PRAGMA as isize,
SQLITE_READ = ffi::SQLITE_READ as isize,
SQLITE_SELECT = ffi::SQLITE_SELECT as isize,
SQLITE_TRANSACTION = ffi::SQLITE_TRANSACTION as isize,
SQLITE_UPDATE = ffi::SQLITE_UPDATE as isize,
SQLITE_ATTACH = ffi::SQLITE_ATTACH as isize,
SQLITE_DETACH = ffi::SQLITE_DETACH as isize,
SQLITE_ALTER_TABLE = ffi::SQLITE_ALTER_TABLE as isize,
SQLITE_REINDEX = ffi::SQLITE_REINDEX as isize,
SQLITE_ANALYZE = ffi::SQLITE_ANALYZE as isize,
SQLITE_CREATE_VTABLE = ffi::SQLITE_CREATE_VTABLE as isize,
SQLITE_DROP_VTABLE = ffi::SQLITE_DROP_VTABLE as isize,
SQLITE_FUNCTION = ffi::SQLITE_FUNCTION as isize,
SQLITE_SAVEPOINT = ffi::SQLITE_SAVEPOINT as isize,
SQLITE_COPY = ffi::SQLITE_COPY as isize,
SQLITE_RECURSIVE = 33,
SQLITE_DELETE = ffi::SQLITE_DELETE,
SQLITE_INSERT = ffi::SQLITE_INSERT,
SQLITE_UPDATE = ffi::SQLITE_UPDATE,
}
impl From<i32> for Action {
fn from(code: i32) -> Action {
match code {
ffi::SQLITE_CREATE_INDEX => Action::SQLITE_CREATE_INDEX,
ffi::SQLITE_CREATE_TABLE => Action::SQLITE_CREATE_TABLE,
ffi::SQLITE_CREATE_TEMP_INDEX => Action::SQLITE_CREATE_TEMP_INDEX,
ffi::SQLITE_CREATE_TEMP_TABLE => Action::SQLITE_CREATE_TEMP_TABLE,
ffi::SQLITE_CREATE_TEMP_TRIGGER => Action::SQLITE_CREATE_TEMP_TRIGGER,
ffi::SQLITE_CREATE_TEMP_VIEW => Action::SQLITE_CREATE_TEMP_VIEW,
ffi::SQLITE_CREATE_TRIGGER => Action::SQLITE_CREATE_TRIGGER,
ffi::SQLITE_CREATE_VIEW => Action::SQLITE_CREATE_VIEW,
ffi::SQLITE_DELETE => Action::SQLITE_DELETE,
ffi::SQLITE_DROP_INDEX => Action::SQLITE_DROP_INDEX,
ffi::SQLITE_DROP_TABLE => Action::SQLITE_DROP_TABLE,
ffi::SQLITE_DROP_TEMP_INDEX => Action::SQLITE_DROP_TEMP_INDEX,
ffi::SQLITE_DROP_TEMP_TABLE => Action::SQLITE_DROP_TEMP_TABLE,
ffi::SQLITE_DROP_TEMP_TRIGGER => Action::SQLITE_DROP_TEMP_TRIGGER,
ffi::SQLITE_DROP_TEMP_VIEW => Action::SQLITE_DROP_TEMP_VIEW,
ffi::SQLITE_DROP_TRIGGER => Action::SQLITE_DROP_TRIGGER,
ffi::SQLITE_DROP_VIEW => Action::SQLITE_DROP_VIEW,
ffi::SQLITE_INSERT => Action::SQLITE_INSERT,
ffi::SQLITE_PRAGMA => Action::SQLITE_PRAGMA,
ffi::SQLITE_READ => Action::SQLITE_READ,
ffi::SQLITE_SELECT => Action::SQLITE_SELECT,
ffi::SQLITE_TRANSACTION => Action::SQLITE_TRANSACTION,
ffi::SQLITE_UPDATE => Action::SQLITE_UPDATE,
ffi::SQLITE_ATTACH => Action::SQLITE_ATTACH,
ffi::SQLITE_DETACH => Action::SQLITE_DETACH,
ffi::SQLITE_ALTER_TABLE => Action::SQLITE_ALTER_TABLE,
ffi::SQLITE_REINDEX => Action::SQLITE_REINDEX,
ffi::SQLITE_ANALYZE => Action::SQLITE_ANALYZE,
ffi::SQLITE_CREATE_VTABLE => Action::SQLITE_CREATE_VTABLE,
ffi::SQLITE_DROP_VTABLE => Action::SQLITE_DROP_VTABLE,
ffi::SQLITE_FUNCTION => Action::SQLITE_FUNCTION,
ffi::SQLITE_SAVEPOINT => Action::SQLITE_SAVEPOINT,
ffi::SQLITE_COPY => Action::SQLITE_COPY,
33 => Action::SQLITE_RECURSIVE,
_ => Action::UNKNOWN,
}
}
@ -146,8 +86,11 @@ impl InnerConnection {
where
F: FnMut() -> bool,
{
let boxed_hook: *mut F = p_arg as *mut F;
if (*boxed_hook)() {
let r = catch_unwind(|| {
let boxed_hook: *mut F = p_arg as *mut F;
(*boxed_hook)()
});
if let Ok(true) = r {
1
} else {
0
@ -192,8 +135,10 @@ impl InnerConnection {
where
F: FnMut(),
{
let boxed_hook: *mut F = p_arg as *mut F;
(*boxed_hook)();
let _ = catch_unwind(|| {
let boxed_hook: *mut F = p_arg as *mut F;
(*boxed_hook)();
});
}
let free_rollback_hook = if hook.is_some() {
@ -239,8 +184,6 @@ impl InnerConnection {
use std::ffi::CStr;
use std::str;
let boxed_hook: *mut F = p_arg as *mut F;
let action = Action::from(action_code);
let db_name = {
let c_slice = CStr::from_ptr(db_str).to_bytes();
@ -251,7 +194,10 @@ impl InnerConnection {
str::from_utf8_unchecked(c_slice)
};
(*boxed_hook)(action, db_name, tbl_name, row_id);
let _ = catch_unwind(|| {
let boxed_hook: *mut F = p_arg as *mut F;
(*boxed_hook)(action, db_name, tbl_name, row_id);
});
}
let free_update_hook = if hook.is_some() {
@ -289,23 +235,23 @@ fn free_boxed_hook<F>(p: *mut c_void) {
#[cfg(test)]
mod test {
use super::Action;
use crate::Connection;
use std::sync::atomic::{AtomicBool, Ordering};
use Connection;
#[test]
fn test_commit_hook() {
let db = Connection::open_in_memory().unwrap();
lazy_static! {
static ref called: AtomicBool = AtomicBool::new(false);
static ref CALLED: AtomicBool = AtomicBool::new(false);
}
db.commit_hook(Some(|| {
called.store(true, Ordering::Relaxed);
CALLED.store(true, Ordering::Relaxed);
false
}));
db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;")
.unwrap();
assert!(called.load(Ordering::Relaxed));
assert!(CALLED.load(Ordering::Relaxed));
}
#[test]
@ -326,14 +272,14 @@ mod test {
let db = Connection::open_in_memory().unwrap();
lazy_static! {
static ref called: AtomicBool = AtomicBool::new(false);
static ref CALLED: AtomicBool = AtomicBool::new(false);
}
db.rollback_hook(Some(|| {
called.store(true, Ordering::Relaxed);
CALLED.store(true, Ordering::Relaxed);
}));
db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); ROLLBACK;")
.unwrap();
assert!(called.load(Ordering::Relaxed));
assert!(CALLED.load(Ordering::Relaxed));
}
#[test]
@ -341,17 +287,17 @@ mod test {
let db = Connection::open_in_memory().unwrap();
lazy_static! {
static ref called: AtomicBool = AtomicBool::new(false);
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);
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.load(Ordering::Relaxed));
assert!(CALLED.load(Ordering::Relaxed));
}
}

409
src/inner_connection.rs Normal file
View File

@ -0,0 +1,409 @@
use std::ffi::CString;
use std::mem;
use std::os::raw::c_int;
#[cfg(feature = "load_extension")]
use std::path::Path;
use std::ptr;
use std::str;
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
use std::sync::{Arc, Mutex, Once, ONCE_INIT};
use super::ffi;
use super::str_to_cstring;
use super::{Connection, InterruptHandle, OpenFlags, Result};
use crate::error::{error_from_handle, error_from_sqlite_code, Error};
use crate::raw_statement::RawStatement;
use crate::statement::Statement;
use crate::unlock_notify;
use crate::version::version_number;
pub struct InnerConnection {
pub db: *mut ffi::sqlite3,
// It's unsafe to call `sqlite3_close` while another thread is performing
// a `sqlite3_interrupt`, and vice versa, so we take this mutex during
// those functions. This protects a copy of the `db` pointer (which is
// cleared on closing), however the main copy, `db`, is unprotected.
// Otherwise, a long running query would prevent calling interrupt, as
// interrupt would only acquire the lock after the query's completion.
interrupt_lock: Arc<Mutex<*mut ffi::sqlite3>>,
#[cfg(feature = "hooks")]
pub free_commit_hook: Option<fn(*mut ::std::os::raw::c_void)>,
#[cfg(feature = "hooks")]
pub free_rollback_hook: Option<fn(*mut ::std::os::raw::c_void)>,
#[cfg(feature = "hooks")]
pub free_update_hook: Option<fn(*mut ::std::os::raw::c_void)>,
owned: bool,
}
impl InnerConnection {
#[cfg(not(feature = "hooks"))]
pub fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection {
InnerConnection {
db,
interrupt_lock: Arc::new(Mutex::new(db)),
owned,
}
}
#[cfg(feature = "hooks")]
pub fn new(db: *mut ffi::sqlite3, owned: bool) -> InnerConnection {
InnerConnection {
db,
interrupt_lock: Arc::new(Mutex::new(db)),
free_commit_hook: None,
free_rollback_hook: None,
free_update_hook: None,
owned,
}
}
pub fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result<InnerConnection> {
#[cfg(not(feature = "bundled"))]
ensure_valid_sqlite_version();
ensure_safe_sqlite_threading_mode()?;
// Replicate the check for sane open flags from SQLite, because the check in
// SQLite itself wasn't added until version 3.7.3.
debug_assert_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
);
if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
return Err(Error::SqliteFailure(
ffi::Error::new(ffi::SQLITE_MISUSE),
None,
));
}
unsafe {
let mut db: *mut ffi::sqlite3 = mem::uninitialized();
let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null());
if r != ffi::SQLITE_OK {
let e = if db.is_null() {
error_from_sqlite_code(r, None)
} else {
let e = error_from_handle(db, r);
ffi::sqlite3_close(db);
e
};
return Err(e);
}
let r = ffi::sqlite3_busy_timeout(db, 5000);
if r != ffi::SQLITE_OK {
let e = error_from_handle(db, r);
ffi::sqlite3_close(db);
return Err(e);
}
// attempt to turn on extended results code; don't fail if we can't.
ffi::sqlite3_extended_result_codes(db, 1);
Ok(InnerConnection::new(db, true))
}
}
pub fn db(&self) -> *mut ffi::sqlite3 {
self.db
}
pub fn decode_result(&mut self, code: c_int) -> Result<()> {
InnerConnection::decode_result_raw(self.db(), code)
}
fn decode_result_raw(db: *mut ffi::sqlite3, code: c_int) -> Result<()> {
if code == ffi::SQLITE_OK {
Ok(())
} else {
Err(error_from_handle(db, code))
}
}
pub fn close(&mut self) -> Result<()> {
if self.db.is_null() {
return Ok(());
}
self.remove_hooks();
let mut shared_handle = self.interrupt_lock.lock().unwrap();
assert!(
!shared_handle.is_null(),
"Bug: Somehow interrupt_lock was cleared before the DB was closed"
);
if !self.owned {
self.db = ptr::null_mut();
return Ok(());
}
unsafe {
let r = ffi::sqlite3_close(self.db);
// Need to use _raw because _guard has a reference out, and
// decode_result takes &mut self.
let r = InnerConnection::decode_result_raw(self.db, r);
if r.is_ok() {
*shared_handle = ptr::null_mut();
self.db = ptr::null_mut();
}
r
}
}
pub fn get_interrupt_handle(&self) -> InterruptHandle {
InterruptHandle {
db_lock: Arc::clone(&self.interrupt_lock),
}
}
pub fn execute_batch(&mut self, sql: &str) -> Result<()> {
let c_sql = str_to_cstring(sql)?;
unsafe {
let r = ffi::sqlite3_exec(
self.db(),
c_sql.as_ptr(),
None,
ptr::null_mut(),
ptr::null_mut(),
);
self.decode_result(r)
}
}
#[cfg(feature = "load_extension")]
pub fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> {
let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) };
self.decode_result(r)
}
#[cfg(feature = "load_extension")]
pub fn load_extension(&self, dylib_path: &Path, entry_point: Option<&str>) -> Result<()> {
use std::os::raw::c_char;
let dylib_str = super::path_to_cstring(dylib_path)?;
unsafe {
let mut errmsg: *mut c_char = mem::uninitialized();
let r = if let Some(entry_point) = entry_point {
let c_entry = str_to_cstring(entry_point)?;
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)
};
if r == ffi::SQLITE_OK {
Ok(())
} else {
let message = super::errmsg_to_string(&*errmsg);
ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void);
Err(error_from_sqlite_code(r, Some(message)))
}
}
}
pub fn last_insert_rowid(&self) -> i64 {
unsafe { ffi::sqlite3_last_insert_rowid(self.db()) }
}
pub fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) -> Result<Statement<'a>> {
if sql.len() >= ::std::i32::MAX as usize {
return Err(error_from_sqlite_code(ffi::SQLITE_TOOBIG, None));
}
let mut c_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() };
let c_sql = str_to_cstring(sql)?;
let len_with_nul = (sql.len() + 1) as c_int;
let mut c_tail = ptr::null();
let r = unsafe {
if cfg!(feature = "unlock_notify") {
let mut rc;
loop {
rc = ffi::sqlite3_prepare_v2(
self.db(),
c_sql.as_ptr(),
len_with_nul,
&mut c_stmt,
&mut c_tail,
);
if !unlock_notify::is_locked(self.db, rc) {
break;
}
rc = unlock_notify::wait_for_unlock_notify(self.db);
if rc != ffi::SQLITE_OK {
break;
}
}
rc
} else {
ffi::sqlite3_prepare_v2(
self.db(),
c_sql.as_ptr(),
len_with_nul,
&mut c_stmt,
&mut c_tail,
)
}
};
if !c_tail.is_null() && unsafe { *c_tail == 0 } {
// '\0' when there is no ';' at the end
c_tail = ptr::null(); // TODO ignore spaces, comments, ... at the end
}
self.decode_result(r)
.map(|_| Statement::new(conn, RawStatement::new(c_stmt, c_tail)))
}
pub fn changes(&mut self) -> usize {
unsafe { ffi::sqlite3_changes(self.db()) as usize }
}
pub fn is_autocommit(&self) -> bool {
unsafe { ffi::sqlite3_get_autocommit(self.db()) != 0 }
}
#[cfg(feature = "bundled")] // 3.8.6
pub 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) {}
}
impl Drop for InnerConnection {
#[allow(unused_must_use)]
fn drop(&mut self) {
use std::thread::panicking;
if let Err(e) = self.close() {
if panicking() {
eprintln!("Error while closing SQLite connection: {:?}", e);
} else {
panic!("Error while closing SQLite connection: {:?}", e);
}
}
}
}
#[cfg(not(feature = "bundled"))]
static SQLITE_VERSION_CHECK: Once = ONCE_INIT;
#[cfg(not(feature = "bundled"))]
pub static BYPASS_VERSION_CHECK: AtomicBool = ATOMIC_BOOL_INIT;
#[cfg(not(feature = "bundled"))]
fn ensure_valid_sqlite_version() {
use crate::version::version;
SQLITE_VERSION_CHECK.call_once(|| {
let version_number = version_number();
// Check our hard floor.
if version_number < 3_006_008 {
panic!("rusqlite requires SQLite 3.6.8 or newer");
}
// Check that the major version number for runtime and buildtime match.
let buildtime_major = ffi::SQLITE_VERSION_NUMBER / 1_000_000;
let runtime_major = version_number / 1_000_000;
if buildtime_major != runtime_major {
panic!(
"rusqlite was built against SQLite {} but is running with SQLite {}",
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
version()
);
}
if BYPASS_VERSION_CHECK.load(Ordering::Relaxed) {
return;
}
// Check that the runtime version number is compatible with the version number
// we found at build-time.
if version_number < ffi::SQLITE_VERSION_NUMBER {
panic!(
"\
rusqlite was built against SQLite {} but the runtime SQLite version is {}. To fix this, either:
* Recompile rusqlite and link against the SQLite version you are using at runtime, or
* Call rusqlite::bypass_sqlite_version_check() prior to your first connection attempt. Doing this
means you're sure everything will work correctly even though the runtime version is older than
the version we found at build time.",
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
version()
);
}
});
}
static SQLITE_INIT: Once = ONCE_INIT;
pub static BYPASS_SQLITE_INIT: AtomicBool = ATOMIC_BOOL_INIT;
fn ensure_safe_sqlite_threading_mode() -> Result<()> {
// Ensure SQLite was compiled in thredsafe mode.
if unsafe { ffi::sqlite3_threadsafe() == 0 } {
return Err(Error::SqliteSingleThreadedMode);
}
// Now we know SQLite is _capable_ of being in Multi-thread of Serialized mode,
// but it's possible someone configured it to be in Single-thread mode
// before calling into us. That would mean we're exposing an unsafe API via
// a safe one (in Rust terminology), which is no good. We have two options
// to protect against this, depending on the version of SQLite we're linked
// with:
//
// 1. If we're on 3.7.0 or later, we can ask SQLite for a mutex and check for
// the magic value 8. This isn't documented, but it's what SQLite
// returns for its mutex allocation function in Single-thread mode.
// 2. If we're prior to SQLite 3.7.0, AFAIK there's no way to check the
// threading mode. The check we perform for >= 3.7.0 will segfault.
// Instead, we insist on being able to call sqlite3_config and
// sqlite3_initialize ourself, ensuring we know the threading
// mode. This will fail if someone else has already initialized SQLite
// even if they initialized it safely. That's not ideal either, which is
// why we expose bypass_sqlite_initialization above.
if version_number() >= 3_007_000 {
const SQLITE_SINGLETHREADED_MUTEX_MAGIC: usize = 8;
let is_singlethreaded = unsafe {
let mutex_ptr = ffi::sqlite3_mutex_alloc(0);
let is_singlethreaded = mutex_ptr as usize == SQLITE_SINGLETHREADED_MUTEX_MAGIC;
ffi::sqlite3_mutex_free(mutex_ptr);
is_singlethreaded
};
if is_singlethreaded {
Err(Error::SqliteSingleThreadedMode)
} else {
Ok(())
}
} else {
SQLITE_INIT.call_once(|| {
if BYPASS_SQLITE_INIT.load(Ordering::Relaxed) {
return;
}
unsafe {
let msg = "\
Could not ensure safe initialization of SQLite.
To fix this, either:
* Upgrade SQLite to at least version 3.7.0
* Ensure that SQLite has been initialized in Multi-thread or Serialized mode and call
rusqlite::bypass_sqlite_initialization() prior to your first connection attempt.";
if ffi::sqlite3_config(ffi::SQLITE_CONFIG_MULTITHREAD) != ffi::SQLITE_OK {
panic!(msg);
}
if ffi::sqlite3_initialize() != ffi::SQLITE_OK {
panic!(msg);
}
}
});
Ok(())
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,10 +2,10 @@
use std::os::raw::c_int;
use ffi;
pub use ffi::Limit;
use crate::ffi;
pub use crate::ffi::Limit;
use Connection;
use crate::Connection;
impl Connection {
/// Returns the current value of a limit.
@ -23,8 +23,8 @@ impl Connection {
#[cfg(test)]
mod test {
use ffi::Limit;
use Connection;
use crate::ffi::Limit;
use crate::Connection;
#[test]
fn test_limit() {
@ -57,13 +57,13 @@ mod test {
assert_eq!(99, db.limit(Limit::SQLITE_LIMIT_VARIABLE_NUMBER));
// SQLITE_LIMIT_TRIGGER_DEPTH was added in SQLite 3.6.18.
if ::version_number() >= 3006018 {
if crate::version_number() >= 3_006_018 {
db.set_limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH, 32);
assert_eq!(32, db.limit(Limit::SQLITE_LIMIT_TRIGGER_DEPTH));
}
// SQLITE_LIMIT_WORKER_THREADS was added in SQLite 3.8.7.
if ::version_number() >= 3008007 {
if crate::version_number() >= 3_008_007 {
db.set_limit(Limit::SQLITE_LIMIT_WORKER_THREADS, 2);
assert_eq!(2, db.limit(Limit::SQLITE_LIMIT_WORKER_THREADS));
}

View File

@ -1,8 +1,4 @@
use {Connection, Result};
/// Old name for `LoadExtensionGuard`. `SqliteLoadExtensionGuard` is deprecated.
#[deprecated(since = "0.6.0", note = "Use LoadExtensionGuard instead")]
pub type SqliteLoadExtensionGuard<'conn> = LoadExtensionGuard<'conn>;
use crate::{Connection, Result};
/// RAII guard temporarily enabling SQLite extensions to be loaded.
///
@ -12,7 +8,7 @@ pub type SqliteLoadExtensionGuard<'conn> = LoadExtensionGuard<'conn>;
/// # use rusqlite::{Connection, Result, LoadExtensionGuard};
/// # use std::path::{Path};
/// fn load_my_extension(conn: &Connection) -> Result<()> {
/// let _guard = try!(LoadExtensionGuard::new(conn));
/// let _guard = LoadExtensionGuard::new(conn)?;
///
/// conn.load_extension(Path::new("my_sqlite_extension"), None)
/// }
@ -25,7 +21,7 @@ impl<'conn> LoadExtensionGuard<'conn> {
/// Attempt to enable loading extensions. Loading extensions will be
/// disabled when this guard goes out of scope. Cannot be meaningfully
/// nested.
pub fn new(conn: &Connection) -> Result<LoadExtensionGuard> {
pub fn new(conn: &Connection) -> Result<LoadExtensionGuard<'_>> {
conn.load_extension_enable()
.map(|_| LoadExtensionGuard { conn })
}

View File

@ -1,5 +1,6 @@
use super::ffi;
use super::unlock_notify;
use super::StatementStatus;
use std::ffi::CStr;
use std::os::raw::{c_char, c_int};
use std::ptr;
@ -101,6 +102,11 @@ impl RawStatement {
}
}
pub fn get_status(&self, status: StatementStatus, reset: bool) -> i32 {
assert!(!self.0.is_null());
unsafe { ffi::sqlite3_stmt_status(self.0, status as i32, reset as i32) }
}
pub fn has_tail(&self) -> bool {
!self.1.is_null()
}

View File

@ -2,7 +2,7 @@ use std::marker::PhantomData;
use std::{convert, result};
use super::{Error, Result, Statement};
use types::{FromSql, FromSqlError, ValueRef};
use crate::types::{FromSql, FromSqlError, ValueRef};
/// An handle for the resulting rows of a query.
pub struct Rows<'stmt> {
@ -27,7 +27,7 @@ impl<'stmt> Rows<'stmt> {
/// 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
#[allow(clippy::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 {
@ -73,7 +73,7 @@ pub struct MappedRows<'stmt, F> {
impl<'stmt, T, F> MappedRows<'stmt, F>
where
F: FnMut(&Row) -> T,
F: FnMut(&Row<'_, '_>) -> T,
{
pub(crate) fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
MappedRows { rows, map: f }
@ -82,7 +82,7 @@ where
impl<'conn, T, F> Iterator for MappedRows<'conn, F>
where
F: FnMut(&Row) -> T,
F: FnMut(&Row<'_, '_>) -> T,
{
type Item = Result<T>;
@ -103,7 +103,7 @@ pub struct AndThenRows<'stmt, F> {
impl<'stmt, T, E, F> AndThenRows<'stmt, F>
where
F: FnMut(&Row) -> result::Result<T, E>,
F: FnMut(&Row<'_, '_>) -> result::Result<T, E>,
{
pub(crate) fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
AndThenRows { rows, map: f }
@ -113,7 +113,7 @@ where
impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
where
E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>,
F: FnMut(&Row<'_, '_>) -> result::Result<T, E>,
{
type Item = result::Result<T, E>;
@ -165,7 +165,7 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
/// enabled), and the underlying SQLite column is a blob whose size is not
/// 16 bytes, `Error::InvalidColumnType` will also be returned.
pub fn get_checked<I: RowIndex, T: FromSql>(&self, idx: I) -> Result<T> {
let idx = try!(idx.idx(self.stmt));
let idx = 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()),
@ -231,12 +231,12 @@ 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<usize>;
fn idx(&self, stmt: &Statement<'_>) -> Result<usize>;
}
impl RowIndex for usize {
#[inline]
fn idx(&self, stmt: &Statement) -> Result<usize> {
fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
if *self >= stmt.column_count() {
Err(Error::InvalidColumnIndex(*self))
} else {
@ -247,7 +247,7 @@ impl RowIndex for usize {
impl<'a> RowIndex for &'a str {
#[inline]
fn idx(&self, stmt: &Statement) -> Result<usize> {
fn idx(&self, stmt: &Statement<'_>) -> Result<usize> {
stmt.column_index(*self)
}
}

894
src/session.rs Normal file
View File

@ -0,0 +1,894 @@
//! [Session Extension](https://sqlite.org/sessionintro.html)
#![allow(non_camel_case_types)]
use std::ffi::CStr;
use std::io::{Read, Write};
use std::marker::PhantomData;
use std::mem;
use std::os::raw::{c_char, c_int, c_uchar, c_void};
use std::panic::{catch_unwind, RefUnwindSafe};
use std::ptr;
use std::slice::{from_raw_parts, from_raw_parts_mut};
use fallible_streaming_iterator::FallibleStreamingIterator;
use crate::error::error_from_sqlite_code;
use crate::ffi;
use crate::hooks::Action;
use crate::types::ValueRef;
use crate::{errmsg_to_string, str_to_cstring, Connection, DatabaseName, Result};
// https://sqlite.org/session.html
/// An instance of this object is a session that can be used to record changes
/// to a database.
pub struct Session<'conn> {
phantom: PhantomData<&'conn ()>,
s: *mut ffi::sqlite3_session,
filter: Option<Box<dyn Fn(&str) -> bool>>,
}
impl<'conn> Session<'conn> {
/// Create a new session object
pub fn new(db: &'conn Connection) -> Result<Session<'conn>> {
Session::new_with_name(db, DatabaseName::Main)
}
/// Create a new session object
pub fn new_with_name(db: &'conn Connection, name: DatabaseName<'_>) -> Result<Session<'conn>> {
let name = name.to_cstring()?;
let db = db.db.borrow_mut().db;
let mut s: *mut ffi::sqlite3_session = unsafe { mem::uninitialized() };
check!(unsafe { ffi::sqlite3session_create(db, name.as_ptr(), &mut s) });
Ok(Session {
phantom: PhantomData,
s,
filter: None,
})
}
/// Set a table filter
pub fn table_filter<F>(&mut self, filter: Option<F>)
where
F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static,
{
unsafe extern "C" fn call_boxed_closure<F>(
p_arg: *mut c_void,
tbl_str: *const c_char,
) -> c_int
where
F: Fn(&str) -> bool + RefUnwindSafe,
{
use std::ffi::CStr;
use std::str;
let boxed_filter: *mut F = p_arg as *mut F;
let tbl_name = {
let c_slice = CStr::from_ptr(tbl_str).to_bytes();
str::from_utf8_unchecked(c_slice)
};
if let Ok(true) = catch_unwind(|| (*boxed_filter)(tbl_name)) {
1
} else {
0
}
}
match filter {
Some(filter) => {
let boxed_filter = Box::new(filter);
unsafe {
ffi::sqlite3session_table_filter(
self.s,
Some(call_boxed_closure::<F>),
&*boxed_filter as *const F as *mut _,
);
}
self.filter = Some(boxed_filter);
}
_ => {
unsafe { ffi::sqlite3session_table_filter(self.s, None, ptr::null_mut()) }
self.filter = None;
}
};
}
/// Attach a table. `None` means all tables.
pub fn attach(&mut self, table: Option<&str>) -> Result<()> {
let table = if let Some(table) = table {
str_to_cstring(table)?.as_ptr()
} else {
ptr::null()
};
unsafe { check!(ffi::sqlite3session_attach(self.s, table)) };
Ok(())
}
/// Generate a Changeset
pub fn changeset(&mut self) -> Result<Changeset> {
let mut n = 0;
let mut cs: *mut c_void = unsafe { mem::uninitialized() };
check!(unsafe { ffi::sqlite3session_changeset(self.s, &mut n, &mut cs) });
Ok(Changeset { cs, n })
}
/// Write the set of changes represented by this session to `output`.
pub fn changeset_strm(&mut self, output: &mut dyn Write) -> Result<()> {
let output_ref = &output;
check!(unsafe {
ffi::sqlite3session_changeset_strm(
self.s,
Some(x_output),
output_ref as *const &mut dyn Write as *mut c_void,
)
});
Ok(())
}
/// Generate a Patchset
pub fn patchset(&mut self) -> Result<Changeset> {
let mut n = 0;
let mut ps: *mut c_void = unsafe { mem::uninitialized() };
check!(unsafe { ffi::sqlite3session_patchset(self.s, &mut n, &mut ps) });
// TODO Validate: same struct
Ok(Changeset { cs: ps, n })
}
/// Write the set of patches represented by this session to `output`.
pub fn patchset_strm(&mut self, output: &mut dyn Write) -> Result<()> {
let output_ref = &output;
check!(unsafe {
ffi::sqlite3session_patchset_strm(
self.s,
Some(x_output),
output_ref as *const &mut dyn Write as *mut c_void,
)
});
Ok(())
}
/// Load the difference between tables.
pub fn diff(&mut self, from: DatabaseName<'_>, table: &str) -> Result<()> {
let from = from.to_cstring()?;
let table = str_to_cstring(table)?.as_ptr();
unsafe {
let mut errmsg: *mut c_char = mem::uninitialized();
let r = ffi::sqlite3session_diff(self.s, from.as_ptr(), table, &mut errmsg);
if r != ffi::SQLITE_OK {
let message = errmsg_to_string(&*errmsg);
ffi::sqlite3_free(errmsg as *mut ::std::os::raw::c_void);
return Err(error_from_sqlite_code(r, Some(message)));
}
}
Ok(())
}
/// Test if a changeset has recorded any changes
pub fn is_empty(&self) -> bool {
unsafe { ffi::sqlite3session_isempty(self.s) != 0 }
}
/// Query the current state of the session
pub fn is_enabled(&self) -> bool {
unsafe { ffi::sqlite3session_enable(self.s, -1) != 0 }
}
/// Enable or disable the recording of changes
pub fn set_enabled(&mut self, enabled: bool) {
unsafe {
ffi::sqlite3session_enable(self.s, if enabled { 1 } else { 0 });
}
}
/// Query the current state of the indirect flag
pub fn is_indirect(&self) -> bool {
unsafe { ffi::sqlite3session_indirect(self.s, -1) != 0 }
}
/// Set or clear the indirect change flag
pub fn set_indirect(&mut self, indirect: bool) {
unsafe {
ffi::sqlite3session_indirect(self.s, if indirect { 1 } else { 0 });
}
}
}
impl<'conn> Drop for Session<'conn> {
fn drop(&mut self) {
if self.filter.is_some() {
self.table_filter(None::<fn(&str) -> bool>);
}
unsafe { ffi::sqlite3session_delete(self.s) };
}
}
/// Invert a changeset
pub fn invert_strm(input: &mut dyn Read, output: &mut dyn Write) -> Result<()> {
let input_ref = &input;
let output_ref = &output;
check!(unsafe {
ffi::sqlite3changeset_invert_strm(
Some(x_input),
input_ref as *const &mut dyn Read as *mut c_void,
Some(x_output),
output_ref as *const &mut dyn Write as *mut c_void,
)
});
Ok(())
}
/// Combine two changesets
pub fn concat_strm(
input_a: &mut dyn Read,
input_b: &mut dyn Read,
output: &mut dyn Write,
) -> Result<()> {
let input_a_ref = &input_a;
let input_b_ref = &input_b;
let output_ref = &output;
check!(unsafe {
ffi::sqlite3changeset_concat_strm(
Some(x_input),
input_a_ref as *const &mut dyn Read as *mut c_void,
Some(x_input),
input_b_ref as *const &mut dyn Read as *mut c_void,
Some(x_output),
output_ref as *const &mut dyn Write as *mut c_void,
)
});
Ok(())
}
/// Changeset or Patchset
pub struct Changeset {
cs: *mut c_void,
n: c_int,
}
impl Changeset {
/// Invert a changeset
pub fn invert(&self) -> Result<Changeset> {
let mut n = 0;
let mut cs: *mut c_void = unsafe { mem::uninitialized() };
check!(unsafe { ffi::sqlite3changeset_invert(self.n, self.cs, &mut n, &mut cs) });
Ok(Changeset { cs, n })
}
/// Create an iterator to traverse a changeset
pub fn iter(&self) -> Result<ChangesetIter<'_>> {
let mut it: *mut ffi::sqlite3_changeset_iter = unsafe { mem::uninitialized() };
check!(unsafe { ffi::sqlite3changeset_start(&mut it, self.n, self.cs) });
Ok(ChangesetIter {
phantom: PhantomData,
it,
item: None,
})
}
/// Concatenate two changeset objects
pub fn concat(a: &Changeset, b: &Changeset) -> Result<Changeset> {
let mut n = 0;
let mut cs: *mut c_void = unsafe { mem::uninitialized() };
check!(unsafe { ffi::sqlite3changeset_concat(a.n, a.cs, b.n, b.cs, &mut n, &mut cs) });
Ok(Changeset { cs, n })
}
}
impl Drop for Changeset {
fn drop(&mut self) {
unsafe {
ffi::sqlite3_free(self.cs);
}
}
}
/// Cursor for iterating over the elements of a changeset or patchset.
pub struct ChangesetIter<'changeset> {
phantom: PhantomData<&'changeset ()>,
it: *mut ffi::sqlite3_changeset_iter,
item: Option<ChangesetItem>,
}
impl<'changeset> ChangesetIter<'changeset> {
/// Create an iterator on `input`
pub fn start_strm<'input>(input: &'input mut dyn Read) -> Result<ChangesetIter<'input>> {
let input_ref = &input;
let mut it: *mut ffi::sqlite3_changeset_iter = unsafe { mem::uninitialized() };
check!(unsafe {
ffi::sqlite3changeset_start_strm(
&mut it,
Some(x_input),
input_ref as *const &mut dyn Read as *mut c_void,
)
});
Ok(ChangesetIter {
phantom: PhantomData,
it,
item: None,
})
}
}
impl<'changeset> FallibleStreamingIterator for ChangesetIter<'changeset> {
type Error = crate::error::Error;
type Item = ChangesetItem;
fn advance(&mut self) -> Result<()> {
let rc = unsafe { ffi::sqlite3changeset_next(self.it) };
match rc {
ffi::SQLITE_ROW => {
self.item = Some(ChangesetItem { it: self.it });
Ok(())
}
ffi::SQLITE_DONE => {
self.item = None;
Ok(())
}
code => Err(error_from_sqlite_code(code, None)),
}
}
fn get(&self) -> Option<&ChangesetItem> {
self.item.as_ref()
}
}
pub struct Operation<'item> {
table_name: &'item str,
number_of_columns: i32,
code: Action,
indirect: bool,
}
impl<'item> Operation<'item> {
pub fn table_name(&self) -> &str {
self.table_name
}
pub fn number_of_columns(&self) -> i32 {
self.number_of_columns
}
pub fn code(&self) -> Action {
self.code
}
pub fn indirect(&self) -> bool {
self.indirect
}
}
impl<'changeset> Drop for ChangesetIter<'changeset> {
fn drop(&mut self) {
unsafe {
ffi::sqlite3changeset_finalize(self.it);
}
}
}
/// An item passed to a conflict-handler by `Connection::apply`,
/// or an item generated by `ChangesetIter::next`.
// TODO enum ? Delete, Insert, Update, ...
pub struct ChangesetItem {
it: *mut ffi::sqlite3_changeset_iter,
}
impl ChangesetItem {
/// Obtain conflicting row values
///
/// May only be called with an `SQLITE_CHANGESET_DATA` or
/// `SQLITE_CHANGESET_CONFLICT` conflict handler callback.
pub fn conflict(&self, col: usize) -> Result<ValueRef<'_>> {
unsafe {
let mut p_value: *mut ffi::sqlite3_value = mem::uninitialized();
check!(ffi::sqlite3changeset_conflict(
self.it,
col as i32,
&mut p_value
));
Ok(ValueRef::from_value(p_value))
}
}
/// Determine the number of foreign key constraint violations
///
/// May only be called with an `SQLITE_CHANGESET_FOREIGN_KEY` conflict
/// handler callback.
pub fn fk_conflicts(&self) -> Result<i32> {
unsafe {
let mut p_out = 0;
check!(ffi::sqlite3changeset_fk_conflicts(self.it, &mut p_out));
Ok(p_out)
}
}
/// Obtain new.* Values
///
/// May only be called if the type of change is either `SQLITE_UPDATE` or
/// `SQLITE_INSERT`.
pub fn new_value(&self, col: usize) -> Result<ValueRef<'_>> {
unsafe {
let mut p_value: *mut ffi::sqlite3_value = mem::uninitialized();
check!(ffi::sqlite3changeset_new(self.it, col as i32, &mut p_value));
Ok(ValueRef::from_value(p_value))
}
}
/// Obtain old.* Values
///
/// May only be called if the type of change is either `SQLITE_DELETE` or
/// `SQLITE_UPDATE`.
pub fn old_value(&self, col: usize) -> Result<ValueRef<'_>> {
unsafe {
let mut p_value: *mut ffi::sqlite3_value = mem::uninitialized();
check!(ffi::sqlite3changeset_old(self.it, col as i32, &mut p_value));
Ok(ValueRef::from_value(p_value))
}
}
/// Obtain the current operation
pub fn op(&self) -> Result<Operation<'_>> {
let mut number_of_columns = 0;
let mut code = 0;
let mut indirect = 0;
let tab = unsafe {
let mut pz_tab: *const c_char = mem::uninitialized();
check!(ffi::sqlite3changeset_op(
self.it,
&mut pz_tab,
&mut number_of_columns,
&mut code,
&mut indirect
));
CStr::from_ptr(pz_tab)
};
let table_name = tab.to_str()?;
Ok(Operation {
table_name,
number_of_columns,
code: Action::from(code),
indirect: indirect != 0,
})
}
/// Obtain the primary key definition of a table
pub fn pk(&self) -> Result<&[u8]> {
let mut number_of_columns = 0;
unsafe {
let mut pks: *mut c_uchar = mem::uninitialized();
check!(ffi::sqlite3changeset_pk(
self.it,
&mut pks,
&mut number_of_columns
));
Ok(from_raw_parts(pks, number_of_columns as usize))
}
}
}
/// Used to combine two or more changesets or
/// patchsets
pub struct Changegroup {
cg: *mut ffi::sqlite3_changegroup,
}
impl Changegroup {
pub fn new() -> Result<Self> {
let mut cg: *mut ffi::sqlite3_changegroup = unsafe { mem::uninitialized() };
check!(unsafe { ffi::sqlite3changegroup_new(&mut cg) });
Ok(Changegroup { cg })
}
/// Add a changeset
pub fn add(&mut self, cs: &Changeset) -> Result<()> {
check!(unsafe { ffi::sqlite3changegroup_add(self.cg, cs.n, cs.cs) });
Ok(())
}
/// Add a changeset read from `input` to this change group.
pub fn add_stream(&mut self, input: &mut dyn Read) -> Result<()> {
let input_ref = &input;
check!(unsafe {
ffi::sqlite3changegroup_add_strm(
self.cg,
Some(x_input),
input_ref as *const &mut dyn Read as *mut c_void,
)
});
Ok(())
}
/// Obtain a composite Changeset
pub fn output(&mut self) -> Result<Changeset> {
let mut n = 0;
let mut output: *mut c_void = unsafe { mem::uninitialized() };
check!(unsafe { ffi::sqlite3changegroup_output(self.cg, &mut n, &mut output) });
Ok(Changeset { cs: output, n })
}
/// Write the combined set of changes to `output`.
pub fn output_strm(&mut self, output: &mut dyn Write) -> Result<()> {
let output_ref = &output;
check!(unsafe {
ffi::sqlite3changegroup_output_strm(
self.cg,
Some(x_output),
output_ref as *const &mut dyn Write as *mut c_void,
)
});
Ok(())
}
}
impl Drop for Changegroup {
fn drop(&mut self) {
unsafe {
ffi::sqlite3changegroup_delete(self.cg);
}
}
}
impl Connection {
/// Apply a changeset to a database
pub fn apply<F, C>(&self, cs: &Changeset, filter: Option<F>, conflict: C) -> Result<()>
where
F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static,
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static,
{
let db = self.db.borrow_mut().db;
let filtered = filter.is_some();
let tuple = &mut (filter, conflict);
check!(unsafe {
if filtered {
ffi::sqlite3changeset_apply(
db,
cs.n,
cs.cs,
Some(call_filter::<F, C>),
Some(call_conflict::<F, C>),
tuple as *mut (Option<F>, C) as *mut c_void,
)
} else {
ffi::sqlite3changeset_apply(
db,
cs.n,
cs.cs,
None,
Some(call_conflict::<F, C>),
tuple as *mut (Option<F>, C) as *mut c_void,
)
}
});
Ok(())
}
/// Apply a changeset to a database
pub fn apply_strm<F, C>(
&self,
input: &mut dyn Read,
filter: Option<F>,
conflict: C,
) -> Result<()>
where
F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static,
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static,
{
let input_ref = &input;
let db = self.db.borrow_mut().db;
let filtered = filter.is_some();
let tuple = &mut (filter, conflict);
check!(unsafe {
if filtered {
ffi::sqlite3changeset_apply_strm(
db,
Some(x_input),
input_ref as *const &mut dyn Read as *mut c_void,
Some(call_filter::<F, C>),
Some(call_conflict::<F, C>),
tuple as *mut (Option<F>, C) as *mut c_void,
)
} else {
ffi::sqlite3changeset_apply_strm(
db,
Some(x_input),
input_ref as *const &mut dyn Read as *mut c_void,
None,
Some(call_conflict::<F, C>),
tuple as *mut (Option<F>, C) as *mut c_void,
)
}
});
Ok(())
}
}
/// Constants passed to the conflict handler
#[repr(i32)]
#[derive(Debug, PartialEq)]
pub enum ConflictType {
UNKNOWN = -1,
SQLITE_CHANGESET_DATA = ffi::SQLITE_CHANGESET_DATA,
SQLITE_CHANGESET_NOTFOUND = ffi::SQLITE_CHANGESET_NOTFOUND,
SQLITE_CHANGESET_CONFLICT = ffi::SQLITE_CHANGESET_CONFLICT,
SQLITE_CHANGESET_CONSTRAINT = ffi::SQLITE_CHANGESET_CONSTRAINT,
SQLITE_CHANGESET_FOREIGN_KEY = ffi::SQLITE_CHANGESET_FOREIGN_KEY,
}
impl From<i32> for ConflictType {
fn from(code: i32) -> ConflictType {
match code {
ffi::SQLITE_CHANGESET_DATA => ConflictType::SQLITE_CHANGESET_DATA,
ffi::SQLITE_CHANGESET_NOTFOUND => ConflictType::SQLITE_CHANGESET_NOTFOUND,
ffi::SQLITE_CHANGESET_CONFLICT => ConflictType::SQLITE_CHANGESET_CONFLICT,
ffi::SQLITE_CHANGESET_CONSTRAINT => ConflictType::SQLITE_CHANGESET_CONSTRAINT,
ffi::SQLITE_CHANGESET_FOREIGN_KEY => ConflictType::SQLITE_CHANGESET_FOREIGN_KEY,
_ => ConflictType::UNKNOWN,
}
}
}
/// Constants returned by the conflict handler
#[repr(i32)]
#[derive(Debug, PartialEq)]
pub enum ConflictAction {
SQLITE_CHANGESET_OMIT = ffi::SQLITE_CHANGESET_OMIT,
SQLITE_CHANGESET_REPLACE = ffi::SQLITE_CHANGESET_REPLACE,
SQLITE_CHANGESET_ABORT = ffi::SQLITE_CHANGESET_ABORT,
}
unsafe extern "C" fn call_filter<F, C>(p_ctx: *mut c_void, tbl_str: *const c_char) -> c_int
where
F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static,
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static,
{
use std::ffi::CStr;
use std::str;
let tuple: *mut (Option<F>, C) = p_ctx as *mut (Option<F>, C);
let tbl_name = {
let c_slice = CStr::from_ptr(tbl_str).to_bytes();
str::from_utf8_unchecked(c_slice)
};
match *tuple {
(Some(ref filter), _) => {
if let Ok(true) = catch_unwind(|| filter(tbl_name)) {
1
} else {
0
}
}
_ => unimplemented!(),
}
}
unsafe extern "C" fn call_conflict<F, C>(
p_ctx: *mut c_void,
e_conflict: c_int,
p: *mut ffi::sqlite3_changeset_iter,
) -> c_int
where
F: Fn(&str) -> bool + Send + RefUnwindSafe + 'static,
C: Fn(ConflictType, ChangesetItem) -> ConflictAction + Send + RefUnwindSafe + 'static,
{
let tuple: *mut (Option<F>, C) = p_ctx as *mut (Option<F>, C);
let conflict_type = ConflictType::from(e_conflict);
let item = ChangesetItem { it: p };
if let Ok(action) = catch_unwind(|| (*tuple).1(conflict_type, item)) {
action as c_int
} else {
ffi::SQLITE_CHANGESET_ABORT
}
}
unsafe extern "C" fn x_input(p_in: *mut c_void, data: *mut c_void, len: *mut c_int) -> c_int {
if p_in.is_null() {
return ffi::SQLITE_MISUSE;
}
let bytes: &mut [u8] = from_raw_parts_mut(data as *mut u8, len as usize);
let input = p_in as *mut &mut dyn Read;
match (*input).read(bytes) {
Ok(n) => {
*len = n as i32; // TODO Validate: n = 0 may not mean the reader will always no longer be able to
// produce bytes.
ffi::SQLITE_OK
}
Err(_) => ffi::SQLITE_IOERR_READ, // TODO check if err is a (ru)sqlite Error => propagate
}
}
unsafe extern "C" fn x_output(p_out: *mut c_void, data: *const c_void, len: c_int) -> c_int {
if p_out.is_null() {
return ffi::SQLITE_MISUSE;
}
// The sessions module never invokes an xOutput callback with the third
// parameter set to a value less than or equal to zero.
let bytes: &[u8] = from_raw_parts(data as *const u8, len as usize);
let output = p_out as *mut &mut dyn Write;
match (*output).write_all(bytes) {
Ok(_) => ffi::SQLITE_OK,
Err(_) => ffi::SQLITE_IOERR_WRITE, // TODO check if err is a (ru)sqlite Error => propagate
}
}
#[cfg(test)]
mod test {
use fallible_streaming_iterator::FallibleStreamingIterator;
use std::sync::atomic::{AtomicBool, Ordering};
use super::{Changeset, ChangesetIter, ConflictAction, ConflictType, Session};
use crate::hooks::Action;
use crate::Connection;
fn one_changeset() -> Changeset {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);")
.unwrap();
let mut session = Session::new(&db).unwrap();
assert!(session.is_empty());
session.attach(None).unwrap();
db.execute("INSERT INTO foo (t) VALUES (?);", &["bar"])
.unwrap();
session.changeset().unwrap()
}
fn one_changeset_strm() -> Vec<u8> {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);")
.unwrap();
let mut session = Session::new(&db).unwrap();
assert!(session.is_empty());
session.attach(None).unwrap();
db.execute("INSERT INTO foo (t) VALUES (?);", &["bar"])
.unwrap();
let mut output = Vec::new();
session.changeset_strm(&mut output).unwrap();
output
}
#[test]
fn test_changeset() {
let changeset = one_changeset();
let mut iter = changeset.iter().unwrap();
let item = iter.next().unwrap();
assert!(item.is_some());
let item = item.unwrap();
let op = item.op().unwrap();
assert_eq!("foo", op.table_name());
assert_eq!(1, op.number_of_columns());
assert_eq!(Action::SQLITE_INSERT, op.code());
assert_eq!(false, op.indirect());
let pk = item.pk().unwrap();
assert_eq!(&[1], pk);
let new_value = item.new_value(0).unwrap();
assert_eq!(Ok("bar"), new_value.as_str());
}
#[test]
fn test_changeset_strm() {
let output = one_changeset_strm();
assert!(!output.is_empty());
assert_eq!(14, output.len());
let mut input = output.as_slice();
let mut iter = ChangesetIter::start_strm(&mut input).unwrap();
let item = iter.next().unwrap();
assert!(item.is_some());
}
#[test]
fn test_changeset_apply() {
let changeset = one_changeset();
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);")
.unwrap();
lazy_static! {
static ref CALLED: AtomicBool = AtomicBool::new(false);
}
db.apply(
&changeset,
None::<fn(&str) -> bool>,
|_conflict_type, _item| {
CALLED.store(true, Ordering::Relaxed);
ConflictAction::SQLITE_CHANGESET_OMIT
},
)
.unwrap();
assert!(!CALLED.load(Ordering::Relaxed));
let check = db
.query_row("SELECT 1 FROM foo WHERE t = ?", &["bar"], |row| {
row.get::<_, i32>(0)
})
.unwrap();
assert_eq!(1, check);
// conflict expected when same changeset applied again on the same db
db.apply(
&changeset,
None::<fn(&str) -> bool>,
|conflict_type, item| {
CALLED.store(true, Ordering::Relaxed);
assert_eq!(ConflictType::SQLITE_CHANGESET_CONFLICT, conflict_type);
let conflict = item.conflict(0).unwrap();
assert_eq!(Ok("bar"), conflict.as_str());
ConflictAction::SQLITE_CHANGESET_OMIT
},
)
.unwrap();
assert!(CALLED.load(Ordering::Relaxed));
}
#[test]
fn test_changeset_apply_strm() {
let output = one_changeset_strm();
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);")
.unwrap();
db.apply_strm(
&mut output.as_slice(),
None::<fn(&str) -> bool>,
|_conflict_type, _item| ConflictAction::SQLITE_CHANGESET_OMIT,
)
.unwrap();
let check = db
.query_row("SELECT 1 FROM foo WHERE t = ?", &["bar"], |row| {
row.get::<_, i32>(0)
})
.unwrap();
assert_eq!(1, check);
}
#[test]
fn test_session_empty() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(t TEXT PRIMARY KEY NOT NULL);")
.unwrap();
let mut session = Session::new(&db).unwrap();
assert!(session.is_empty());
session.attach(None).unwrap();
db.execute("INSERT INTO foo (t) VALUES (?);", &["bar"])
.unwrap();
assert!(!session.is_empty());
}
#[test]
fn test_session_set_enabled() {
let db = Connection::open_in_memory().unwrap();
let mut session = Session::new(&db).unwrap();
assert!(session.is_enabled());
session.set_enabled(false);
assert!(!session.is_enabled());
}
#[test]
fn test_session_set_indirect() {
let db = Connection::open_in_memory().unwrap();
let mut session = Session::new(&db).unwrap();
assert!(!session.is_indirect());
session.set_indirect(true);
assert!(session.is_indirect());
}
}

View File

@ -11,9 +11,9 @@ use super::str_to_cstring;
use super::{
AndThenRows, Connection, Error, MappedRows, RawStatement, Result, Row, Rows, ValueRef,
};
use types::{ToSql, ToSqlOutput};
use crate::types::{ToSql, ToSqlOutput};
#[cfg(feature = "array")]
use vtab::array::{free_array, ARRAY_TYPE};
use crate::vtab::array::{free_array, ARRAY_TYPE};
/// A prepared statement.
pub struct Statement<'conn> {
@ -70,10 +70,10 @@ impl<'conn> Statement<'conn> {
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn update_rows(conn: &Connection) -> Result<()> {
/// let mut stmt = try!(conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?"));
/// let mut stmt = conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")?;
///
/// try!(stmt.execute(&[1i32]));
/// try!(stmt.execute(&[2i32]));
/// stmt.execute(&[1i32])?;
/// stmt.execute(&[2i32])?;
///
/// Ok(())
/// }
@ -89,7 +89,7 @@ impl<'conn> Statement<'conn> {
P: IntoIterator,
P::Item: ToSql,
{
try!(self.bind_parameters(params));
self.bind_parameters(params)?;
self.execute_with_bound_parameters()
}
@ -107,18 +107,29 @@ impl<'conn> Statement<'conn> {
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn insert(conn: &Connection) -> Result<usize> {
/// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)"));
/// let mut stmt = conn.prepare("INSERT INTO test (name) VALUES (:name)")?;
/// stmt.execute_named(&[(":name", &"one")])
/// }
/// ```
///
/// Note, the `named_params` macro is provided for syntactic convenience,
/// and so the above example could also be written as:
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result, named_params};
/// fn insert(conn: &Connection) -> Result<usize> {
/// let mut stmt = conn.prepare("INSERT INTO test (name) VALUES (:name)")?;
/// stmt.execute_named(named_params!{":name": "one"})
/// }
/// ```
///
/// # Failure
///
/// Will return `Err` if binding parameters fails, the executed statement
/// returns rows (in which case `query` should be used instead), or the
/// underling SQLite call fails.
pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result<usize> {
try!(self.bind_parameters_named(params));
pub fn execute_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<usize> {
self.bind_parameters_named(params)?;
self.execute_with_bound_parameters()
}
@ -140,7 +151,7 @@ impl<'conn> Statement<'conn> {
P: IntoIterator,
P::Item: ToSql,
{
let changes = try!(self.execute(params));
let changes = self.execute(params)?;
match changes {
1 => Ok(self.conn.last_insert_rowid()),
_ => Err(Error::StatementChangedRows(changes)),
@ -159,12 +170,12 @@ impl<'conn> Statement<'conn> {
/// ```rust,no_run
/// # use rusqlite::{Connection, Result, NO_PARAMS};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people"));
/// let mut rows = try!(stmt.query(NO_PARAMS));
/// let mut stmt = conn.prepare("SELECT name FROM people")?;
/// let mut rows = stmt.query(NO_PARAMS)?;
///
/// let mut names = Vec::new();
/// while let Some(result_row) = rows.next() {
/// let row = try!(result_row);
/// let row = result_row?;
/// names.push(row.get(0));
/// }
///
@ -175,13 +186,13 @@ impl<'conn> Statement<'conn> {
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query<'a, P>(&'a mut self, params: P) -> Result<Rows<'a>>
pub fn query<P>(&mut self, params: P) -> Result<Rows<'_>>
where
P: IntoIterator,
P::Item: ToSql,
{
try!(self.check_readonly());
try!(self.bind_parameters(params));
self.check_readonly()?;
self.bind_parameters(params)?;
Ok(Rows::new(self))
}
@ -196,8 +207,23 @@ impl<'conn> Statement<'conn> {
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn query(conn: &Connection) -> Result<()> {
/// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
/// let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
/// let mut stmt = conn.prepare("SELECT * FROM test where name = :name")?;
/// let mut rows = stmt.query_named(&[(":name", &"one")])?;
/// while let Some(row) = rows.next() {
/// // ...
/// }
/// Ok(())
/// }
/// ```
///
/// Note, the `named_params!` macro is provided for syntactic convenience,
/// and so the above example could also be written as:
///
/// ```rust,no_run
/// # use rusqlite::{Connection, Result, named_params};
/// fn query(conn: &Connection) -> Result<()> {
/// let mut stmt = conn.prepare("SELECT * FROM test where name = :name")?;
/// let mut rows = stmt.query_named(named_params!{ ":name": "one" })?;
/// while let Some(row) = rows.next() {
/// // ...
/// }
@ -208,9 +234,9 @@ impl<'conn> Statement<'conn> {
/// # Failure
///
/// 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));
pub fn query_named<'a>(&'a mut self, params: &[(&str, &dyn ToSql)]) -> Result<Rows<'a>> {
self.check_readonly()?;
self.bind_parameters_named(params)?;
Ok(Rows::new(self))
}
@ -222,12 +248,12 @@ impl<'conn> Statement<'conn> {
/// ```rust,no_run
/// # use rusqlite::{Connection, Result, NO_PARAMS};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people"));
/// let rows = try!(stmt.query_map(NO_PARAMS, |row| row.get(0)));
/// let mut stmt = conn.prepare("SELECT name FROM people")?;
/// let rows = stmt.query_map(NO_PARAMS, |row| row.get(0))?;
///
/// let mut names = Vec::new();
/// for name_result in rows {
/// names.push(try!(name_result));
/// names.push(name_result?);
/// }
///
/// Ok(names)
@ -237,11 +263,11 @@ impl<'conn> Statement<'conn> {
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_map<'a, T, P, F>(&'a mut self, params: P, f: F) -> Result<MappedRows<'a, F>>
pub fn query_map<T, P, F>(&mut self, params: P, f: F) -> Result<MappedRows<'_, F>>
where
P: IntoIterator,
P::Item: ToSql,
F: FnMut(&Row) -> T,
F: FnMut(&Row<'_, '_>) -> T,
{
let rows = self.query(params)?;
Ok(MappedRows::new(rows, f))
@ -259,12 +285,12 @@ impl<'conn> Statement<'conn> {
/// ```rust,no_run
/// # use rusqlite::{Connection, Result};
/// fn get_names(conn: &Connection) -> Result<Vec<String>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
/// let rows = try!(stmt.query_map_named(&[(":id", &"one")], |row| row.get(0)));
/// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?;
/// let rows = stmt.query_map_named(&[(":id", &"one")], |row| row.get(0))?;
///
/// let mut names = Vec::new();
/// for name_result in rows {
/// names.push(try!(name_result));
/// names.push(name_result?);
/// }
///
/// Ok(names)
@ -276,11 +302,11 @@ impl<'conn> Statement<'conn> {
/// Will return `Err` if binding parameters fails.
pub fn query_map_named<'a, T, F>(
&'a mut self,
params: &[(&str, &ToSql)],
params: &[(&str, &dyn ToSql)],
f: F,
) -> Result<MappedRows<'a, F>>
where
F: FnMut(&Row) -> T,
F: FnMut(&Row<'_, '_>) -> T,
{
let rows = self.query_named(params)?;
Ok(MappedRows::new(rows, f))
@ -293,16 +319,12 @@ impl<'conn> Statement<'conn> {
/// # Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_and_then<'a, T, E, P, F>(
&'a mut self,
params: P,
f: F,
) -> Result<AndThenRows<'a, F>>
pub fn query_and_then<T, E, P, F>(&mut self, params: P, f: F) -> Result<AndThenRows<'_, F>>
where
P: IntoIterator,
P::Item: ToSql,
E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>,
F: FnMut(&Row<'_, '_>) -> result::Result<T, E>,
{
let rows = self.query(params)?;
Ok(AndThenRows::new(rows, f))
@ -330,13 +352,13 @@ impl<'conn> Statement<'conn> {
/// }
///
/// fn get_names(conn: &Connection) -> Result<Vec<Person>> {
/// let mut stmt = try!(conn.prepare("SELECT name FROM people WHERE id = :id"));
/// let mut stmt = conn.prepare("SELECT name FROM people WHERE id = :id")?;
/// let rows =
/// try!(stmt.query_and_then_named(&[(":id", &"one")], |row| name_to_person(row.get(0))));
/// stmt.query_and_then_named(&[(":id", &"one")], |row| name_to_person(row.get(0)))?;
///
/// let mut persons = Vec::new();
/// for person_result in rows {
/// persons.push(try!(person_result));
/// persons.push(person_result?);
/// }
///
/// Ok(persons)
@ -348,12 +370,12 @@ impl<'conn> Statement<'conn> {
/// Will return `Err` if binding parameters fails.
pub fn query_and_then_named<'a, T, E, F>(
&'a mut self,
params: &[(&str, &ToSql)],
params: &[(&str, &dyn ToSql)],
f: F,
) -> Result<AndThenRows<'a, F>>
where
E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>,
F: FnMut(&Row<'_, '_>) -> result::Result<T, E>,
{
let rows = self.query_named(params)?;
Ok(AndThenRows::new(rows, f))
@ -366,13 +388,8 @@ impl<'conn> Statement<'conn> {
P: IntoIterator,
P::Item: ToSql,
{
let mut rows = try!(self.query(params));
let exists = {
match rows.next() {
Some(_) => true,
None => false,
}
};
let mut rows = self.query(params)?;
let exists = rows.next().is_some();
Ok(exists)
}
@ -382,6 +399,10 @@ impl<'conn> Statement<'conn> {
/// If the query returns more than one row, all rows except the first are
/// ignored.
///
/// Returns `Err(QueryReturnedNoRows)` if no results are returned. If the
/// query truly is optional, you can call `.optional()` on the result of
/// this to get a `Result<Option<T>>`.
///
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
@ -389,9 +410,9 @@ impl<'conn> Statement<'conn> {
where
P: IntoIterator,
P::Item: ToSql,
F: FnOnce(&Row) -> T,
F: FnOnce(&Row<'_, '_>) -> T,
{
let mut rows = try!(self.query(params));
let mut rows = self.query(params)?;
rows.get_expected_row().map(|r| f(&r))
}
@ -415,7 +436,7 @@ 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<usize>> {
let c_name = try!(str_to_cstring(name));
let c_name = str_to_cstring(name)?;
Ok(self.stmt.bind_parameter_index(&c_name))
}
@ -431,7 +452,7 @@ impl<'conn> Statement<'conn> {
if index > expected {
break;
}
try!(self.bind_parameter(&p, index));
self.bind_parameter(&p, index)?;
}
assert_eq!(
index, expected,
@ -442,10 +463,10 @@ impl<'conn> Statement<'conn> {
Ok(())
}
fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> {
fn bind_parameters_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<()> {
for &(name, value) in params {
if let Some(i) = try!(self.parameter_index(name)) {
try!(self.bind_parameter(value, i));
if let Some(i) = self.parameter_index(name)? {
self.bind_parameter(value, i)?;
} else {
return Err(Error::InvalidParameterName(name.into()));
}
@ -453,8 +474,8 @@ impl<'conn> Statement<'conn> {
Ok(())
}
fn bind_parameter(&self, param: &ToSql, col: usize) -> Result<()> {
let value = try!(param.to_sql());
fn bind_parameter(&self, param: &dyn ToSql, col: usize) -> Result<()> {
let value = param.to_sql()?;
let ptr = unsafe { self.stmt.ptr() };
let value = match value {
@ -489,7 +510,7 @@ impl<'conn> Statement<'conn> {
if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG
} else {
let c_str = try!(str_to_cstring(s));
let c_str = str_to_cstring(s)?;
let destructor = if length > 0 {
ffi::SQLITE_TRANSIENT()
} else {
@ -571,6 +592,17 @@ impl<'conn> Statement<'conn> {
}
}
/// Get the value for one of the status counters for this statement.
pub fn get_status(&self, status: StatementStatus) -> i32 {
self.stmt.get_status(status, false)
}
/// Reset the value of one of the status counters for this statement,
/// returning the value it had before resetting.
pub fn reset_status(&self, status: StatementStatus) -> i32 {
self.stmt.get_status(status, true)
}
pub(crate) fn check_no_tail(&self) -> Result<()> {
if self.stmt.has_tail() {
Err(Error::MultipleStatement)
@ -589,7 +621,7 @@ impl<'conn> Into<RawStatement> for Statement<'conn> {
}
impl<'conn> fmt::Debug for Statement<'conn> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let sql = str::from_utf8(self.stmt.sql().to_bytes());
f.debug_struct("Statement")
.field("conn", self.conn)
@ -607,11 +639,11 @@ impl<'conn> Drop for Statement<'conn> {
}
impl<'conn> Statement<'conn> {
pub(crate) fn new(conn: &Connection, stmt: RawStatement) -> Statement {
pub(crate) fn new(conn: &Connection, stmt: RawStatement) -> Statement<'_> {
Statement { conn, stmt }
}
pub(crate) fn value_ref(&self, col: usize) -> ValueRef {
pub(crate) fn value_ref(&self, col: usize) -> ValueRef<'_> {
let raw = unsafe { self.stmt.ptr() };
match self.stmt.column_type(col) {
@ -679,9 +711,36 @@ impl<'conn> Statement<'conn> {
}
}
/// Prepared statement status counters.
///
/// See https://www.sqlite.org/c3ref/c_stmtstatus_counter.html
/// for explanations of each.
///
/// Note that depending on your version of SQLite, all of these
/// may not be available.
#[repr(i32)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum StatementStatus {
/// Equivalent to SQLITE_STMTSTATUS_FULLSCAN_STEP
FullscanStep = 1,
/// Equivalent to SQLITE_STMTSTATUS_SORT
Sort = 2,
/// Equivalent to SQLITE_STMTSTATUS_AUTOINDEX
AutoIndex = 3,
/// Equivalent to SQLITE_STMTSTATUS_VM_STEP
VmStep = 4,
/// Equivalent to SQLITE_STMTSTATUS_REPREPARE
RePrepare = 5,
/// Equivalent to SQLITE_STMTSTATUS_RUN
Run = 6,
/// Equivalent to SQLITE_STMTSTATUS_MEMUSED
MemUsed = 99,
}
#[cfg(test)]
mod test {
use {Connection, Error, Result, NO_PARAMS};
use crate::types::ToSql;
use crate::{Connection, Error, Result, NO_PARAMS};
#[test]
fn test_execute_named() {
@ -803,6 +862,7 @@ mod test {
assert_eq!(1, doubled_id);
// second row should be Err
#[allow(clippy::match_wild_err_arm)]
match rows.next().unwrap() {
Ok(_) => panic!("invalid Ok"),
Err(Error::SqliteSingleThreadedMode) => (),
@ -963,4 +1023,40 @@ mod test {
stmt.bind_parameter(&1, 1).unwrap();
assert_eq!(Some("SELECT 1"), stmt.expanded_sql());
}
#[test]
fn test_bind_parameters() {
let db = Connection::open_in_memory().unwrap();
// dynamic slice:
db.query_row(
"SELECT ?1, ?2, ?3",
&[&1u8 as &dyn ToSql, &"one", &Some("one")],
|row| row.get::<_, u8>(0),
)
.unwrap();
// existing collection:
let data = vec![1, 2, 3];
db.query_row("SELECT ?1, ?2, ?3", &data, |row| row.get::<_, u8>(0))
.unwrap();
db.query_row("SELECT ?1, ?2, ?3", data.as_slice(), |row| {
row.get::<_, u8>(0)
})
.unwrap();
db.query_row("SELECT ?1, ?2, ?3", data, |row| row.get::<_, u8>(0))
.unwrap();
use std::collections::BTreeSet;
let data: BTreeSet<String> = ["one", "two", "three"]
.iter()
.map(|s| s.to_string())
.collect();
db.query_row("SELECT ?1, ?2, ?3", &data, |row| row.get::<_, String>(0))
.unwrap();
let data = [0; 3];
db.query_row("SELECT ?1, ?2, ?3", &data, |row| row.get::<_, u8>(0))
.unwrap();
db.query_row("SELECT ?1, ?2, ?3", data.iter(), |row| row.get::<_, u8>(0))
.unwrap();
}
}

View File

@ -3,12 +3,13 @@
use std::ffi::{CStr, CString};
use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::catch_unwind;
use std::ptr;
use std::time::Duration;
use super::ffi;
use error::error_from_sqlite_code;
use {Connection, Result};
use crate::error::error_from_sqlite_code;
use crate::{Connection, Result};
/// Set up the process-wide SQLite error logging callback.
/// This function is marked unsafe for two reasons:
@ -27,7 +28,7 @@ pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) };
let s = String::from_utf8_lossy(c_slice);
callback(err, &s);
let _ = catch_unwind(|| callback(err, &s));
}
let rc = match callback {
@ -72,7 +73,7 @@ impl Connection {
let trace_fn: fn(&str) = mem::transmute(p_arg);
let c_slice = CStr::from_ptr(z_sql).to_bytes();
let s = String::from_utf8_lossy(c_slice);
trace_fn(&s);
let _ = catch_unwind(|| trace_fn(&s));
}
let c = self.db.borrow_mut();
@ -106,7 +107,7 @@ impl Connection {
nanoseconds / NANOS_PER_SEC,
(nanoseconds % NANOS_PER_SEC) as u32,
);
profile_fn(&s, duration);
let _ = catch_unwind(|| profile_fn(&s, duration));
}
let c = self.db.borrow_mut();
@ -124,7 +125,7 @@ mod test {
use std::sync::Mutex;
use std::time::Duration;
use Connection;
use crate::Connection;
#[test]
fn test_trace() {

View File

@ -1,10 +1,5 @@
use crate::{Connection, Result};
use std::ops::Deref;
use {Connection, Result};
/// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is
/// deprecated.
#[deprecated(since = "0.6.0", note = "Use TransactionBehavior instead")]
pub type SqliteTransactionBehavior = TransactionBehavior;
/// Options for transaction behavior. See [BEGIN
/// TRANSACTION](http://www.sqlite.org/lang_transaction.html) for details.
@ -16,7 +11,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, Debug, PartialEq, Eq)]
pub enum DropBehavior {
/// Roll back the changes. This is the default.
Rollback,
@ -32,10 +27,6 @@ pub enum DropBehavior {
Panic,
}
/// Old name for `Transaction`. `SqliteTransaction` is deprecated.
#[deprecated(since = "0.6.0", note = "Use Transaction instead")]
pub type SqliteTransaction<'conn> = Transaction<'conn>;
/// Represents a transaction on a database connection.
///
/// ## Note
@ -51,14 +42,15 @@ pub type SqliteTransaction<'conn> = Transaction<'conn>;
/// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
/// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
/// let tx = try!(conn.transaction());
/// let tx = conn.transaction()?;
///
/// try!(do_queries_part_1(&tx)); // tx causes rollback if this fails
/// try!(do_queries_part_2(&tx)); // tx causes rollback if this fails
/// do_queries_part_1(&tx)?; // tx causes rollback if this fails
/// do_queries_part_2(&tx)?; // tx causes rollback if this fails
///
/// tx.commit()
/// }
/// ```
#[derive(Debug)]
pub struct Transaction<'conn> {
conn: &'conn Connection,
drop_behavior: DropBehavior,
@ -79,10 +71,10 @@ pub struct Transaction<'conn> {
/// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
/// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
/// let sp = try!(conn.savepoint());
/// let sp = conn.savepoint()?;
///
/// try!(do_queries_part_1(&sp)); // sp causes rollback if this fails
/// try!(do_queries_part_2(&sp)); // sp causes rollback if this fails
/// do_queries_part_1(&sp)?; // sp causes rollback if this fails
/// do_queries_part_2(&sp)?; // sp causes rollback if this fails
///
/// sp.commit()
/// }
@ -101,7 +93,7 @@ impl<'conn> Transaction<'conn> {
/// Even though we don't mutate the connection, we take a `&mut Connection`
/// so as to prevent nested or concurrent transactions on the same
/// connection.
pub fn new(conn: &mut Connection, behavior: TransactionBehavior) -> Result<Transaction> {
pub fn new(conn: &mut Connection, behavior: TransactionBehavior) -> Result<Transaction<'_>> {
let query = match behavior {
TransactionBehavior::Deferred => "BEGIN DEFERRED",
TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
@ -127,12 +119,12 @@ impl<'conn> Transaction<'conn> {
/// # use rusqlite::{Connection, Result};
/// # fn perform_queries_part_1_succeeds(_conn: &Connection) -> bool { true }
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
/// let mut tx = try!(conn.transaction());
/// let mut tx = conn.transaction()?;
///
/// {
/// let sp = try!(tx.savepoint());
/// let sp = tx.savepoint()?;
/// if perform_queries_part_1_succeeds(&sp) {
/// try!(sp.commit());
/// sp.commit()?;
/// }
/// // otherwise, sp will rollback
/// }
@ -140,12 +132,12 @@ impl<'conn> Transaction<'conn> {
/// tx.commit()
/// }
/// ```
pub fn savepoint(&mut self) -> Result<Savepoint> {
pub fn savepoint(&mut self) -> Result<Savepoint<'_>> {
Savepoint::with_depth(self.conn, 1)
}
/// Create a new savepoint with a custom savepoint name. See `savepoint()`.
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint> {
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint<'_>> {
Savepoint::with_depth_and_name(self.conn, 1, name)
}
@ -223,7 +215,7 @@ impl<'conn> Savepoint<'conn> {
conn: &Connection,
depth: u32,
name: T,
) -> Result<Savepoint> {
) -> Result<Savepoint<'_>> {
let name = name.into();
conn.execute_batch(&format!("SAVEPOINT {}", name))
.map(|_| Savepoint {
@ -235,28 +227,28 @@ impl<'conn> Savepoint<'conn> {
})
}
fn with_depth(conn: &Connection, depth: u32) -> Result<Savepoint> {
fn with_depth(conn: &Connection, depth: u32) -> Result<Savepoint<'_>> {
let name = format!("_rusqlite_sp_{}", depth);
Savepoint::with_depth_and_name(conn, depth, name)
}
/// Begin a new savepoint. Can be nested.
pub fn new(conn: &mut Connection) -> Result<Savepoint> {
pub fn new(conn: &mut Connection) -> Result<Savepoint<'_>> {
Savepoint::with_depth(conn, 0)
}
/// Begin a new savepoint with a user-provided savepoint name.
pub fn with_name<T: Into<String>>(conn: &mut Connection, name: T) -> Result<Savepoint> {
pub fn with_name<T: Into<String>>(conn: &mut Connection, name: T) -> Result<Savepoint<'_>> {
Savepoint::with_depth_and_name(conn, 0, name)
}
/// Begin a nested savepoint.
pub fn savepoint(&mut self) -> Result<Savepoint> {
pub fn savepoint(&mut self) -> Result<Savepoint<'_>> {
Savepoint::with_depth(self.conn, self.depth + 1)
}
/// Begin a nested savepoint with a user-provided savepoint name.
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint> {
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint<'_>> {
Savepoint::with_depth_and_name(self.conn, self.depth + 1, name)
}
@ -345,10 +337,10 @@ impl Connection {
/// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
/// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
/// let tx = try!(conn.transaction());
/// let tx = conn.transaction()?;
///
/// try!(do_queries_part_1(&tx)); // tx causes rollback if this fails
/// try!(do_queries_part_2(&tx)); // tx causes rollback if this fails
/// do_queries_part_1(&tx)?; // tx causes rollback if this fails
/// do_queries_part_2(&tx)?; // tx causes rollback if this fails
///
/// tx.commit()
/// }
@ -357,7 +349,7 @@ impl Connection {
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn transaction(&mut self) -> Result<Transaction> {
pub fn transaction(&mut self) -> Result<Transaction<'_>> {
Transaction::new(self, TransactionBehavior::Deferred)
}
@ -371,7 +363,7 @@ impl Connection {
pub fn transaction_with_behavior(
&mut self,
behavior: TransactionBehavior,
) -> Result<Transaction> {
) -> Result<Transaction<'_>> {
Transaction::new(self, behavior)
}
@ -388,10 +380,10 @@ impl Connection {
/// # fn do_queries_part_1(_conn: &Connection) -> Result<()> { Ok(()) }
/// # fn do_queries_part_2(_conn: &Connection) -> Result<()> { Ok(()) }
/// fn perform_queries(conn: &mut Connection) -> Result<()> {
/// let sp = try!(conn.savepoint());
/// let sp = conn.savepoint()?;
///
/// try!(do_queries_part_1(&sp)); // sp causes rollback if this fails
/// try!(do_queries_part_2(&sp)); // sp causes rollback if this fails
/// do_queries_part_1(&sp)?; // sp causes rollback if this fails
/// do_queries_part_2(&sp)?; // sp causes rollback if this fails
///
/// sp.commit()
/// }
@ -400,7 +392,7 @@ impl Connection {
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn savepoint(&mut self) -> Result<Savepoint> {
pub fn savepoint(&mut self) -> Result<Savepoint<'_>> {
Savepoint::new(self)
}
@ -411,7 +403,7 @@ impl Connection {
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint> {
pub fn savepoint_with_name<T: Into<String>>(&mut self, name: T) -> Result<Savepoint<'_>> {
Savepoint::with_name(self, name)
}
}
@ -419,7 +411,7 @@ impl Connection {
#[cfg(test)]
mod test {
use super::DropBehavior;
use {Connection, NO_PARAMS};
use crate::{Connection, NO_PARAMS};
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@ -564,6 +556,16 @@ mod test {
assert_current_sum(8, &db);
}
#[test]
fn test_rc() {
use std::rc::Rc;
let mut conn = Connection::open_in_memory().unwrap();
let rc_txn = Rc::new(conn.transaction().unwrap());
// This will compile only if Transaction is Debug
Rc::try_unwrap(rc_txn).unwrap();
}
fn insert(x: i32, conn: &Connection) {
conn.execute("INSERT INTO foo VALUES(?)", &[x]).unwrap();
}

View File

@ -1,16 +1,16 @@
//! Convert most of the [Time Strings](http://sqlite.org/lang_datefunc.html) to chrono types.
extern crate chrono;
use chrono;
use std::borrow::Cow;
use self::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use Result;
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use crate::Result;
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
impl ToSql for NaiveDate {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
let date_str = self.format("%Y-%m-%d").to_string();
Ok(ToSqlOutput::from(date_str))
}
@ -18,7 +18,7 @@ impl ToSql for NaiveDate {
/// "YYYY-MM-DD" => ISO 8601 calendar date without timezone.
impl FromSql for NaiveDate {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
value
.as_str()
.and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") {
@ -30,7 +30,7 @@ impl FromSql for NaiveDate {
/// ISO 8601 time without timezone => "HH:MM:SS.SSS"
impl ToSql for NaiveTime {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
let date_str = self.format("%H:%M:%S%.f").to_string();
Ok(ToSqlOutput::from(date_str))
}
@ -38,7 +38,7 @@ 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> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
value.as_str().and_then(|s| {
let fmt = match s.len() {
5 => "%H:%M",
@ -56,7 +56,7 @@ impl FromSql for NaiveTime {
/// ISO 8601 combined date and time without timezone =>
/// "YYYY-MM-DD HH:MM:SS.SSS"
impl ToSql for NaiveDateTime {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
let date_str = self.format("%Y-%m-%dT%H:%M:%S%.f").to_string();
Ok(ToSqlOutput::from(date_str))
}
@ -66,7 +66,7 @@ impl ToSql for NaiveDateTime {
/// and time without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS"
/// also supported)
impl FromSql for NaiveDateTime {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
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"
@ -85,17 +85,17 @@ impl FromSql for NaiveDateTime {
/// Date and time with time zone => UTC RFC3339 timestamp
/// ("YYYY-MM-DDTHH:MM:SS.SSS+00:00").
impl<Tz: TimeZone> ToSql for DateTime<Tz> {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self.with_timezone(&Utc).to_rfc3339()))
}
}
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into `DateTime<Utc>`.
impl FromSql for DateTime<Utc> {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
{
// Try to parse value as rfc3339 first.
let s = try!(value.as_str());
let s = value.as_str()?;
// If timestamp looks space-separated, make a copy and replace it with 'T'.
let s = if s.len() >= 11 && s.as_bytes()[10] == b' ' {
@ -121,8 +121,8 @@ impl FromSql for DateTime<Utc> {
/// RFC3339 ("YYYY-MM-DDTHH:MM:SS.SSS[+-]HH:MM") into `DateTime<Local>`.
impl FromSql for DateTime<Local> {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
let utc_dt = try!(DateTime::<Utc>::column_result(value));
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
let utc_dt = DateTime::<Utc>::column_result(value)?;
Ok(utc_dt.with_timezone(&Local))
}
}
@ -132,7 +132,7 @@ mod test {
use super::chrono::{
DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
};
use {Connection, NO_PARAMS};
use crate::{Connection, Result, NO_PARAMS};
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@ -263,4 +263,21 @@ mod test {
.unwrap();
assert_eq!(local, v);
}
#[test]
fn test_sqlite_functions() {
let db = checked_memory_handle();
let result: Result<NaiveTime> =
db.query_row("SELECT CURRENT_TIME", NO_PARAMS, |r| r.get(0));
assert!(result.is_ok());
let result: Result<NaiveDate> =
db.query_row("SELECT CURRENT_DATE", NO_PARAMS, |r| r.get(0));
assert!(result.is_ok());
let result: Result<NaiveDateTime> =
db.query_row("SELECT CURRENT_TIMESTAMP", NO_PARAMS, |r| r.get(0));
assert!(result.is_ok());
let result: Result<DateTime<Utc>> =
db.query_row("SELECT CURRENT_TIMESTAMP", NO_PARAMS, |r| r.get(0));
assert!(result.is_ok());
}
}

View File

@ -19,11 +19,23 @@ pub enum FromSqlError {
InvalidI128Size(usize),
/// An error case available for implementors of the `FromSql` trait.
Other(Box<Error + Send + Sync>),
Other(Box<dyn Error + Send + Sync>),
}
impl PartialEq for FromSqlError {
fn eq(&self, other: &FromSqlError) -> bool {
match (self, other) {
(FromSqlError::InvalidType, FromSqlError::InvalidType) => true,
(FromSqlError::OutOfRange(n1), FromSqlError::OutOfRange(n2)) => n1 == n2,
#[cfg(feature = "i128_blob")]
(FromSqlError::InvalidI128Size(s1), FromSqlError::InvalidI128Size(s2)) => s1 == s2,
(_, _) => false,
}
}
}
impl fmt::Display for FromSqlError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
FromSqlError::InvalidType => write!(f, "Invalid type"),
FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i),
@ -47,8 +59,9 @@ impl Error for FromSqlError {
}
}
#[cfg_attr(feature = "clippy", allow(match_same_arms))]
fn cause(&self) -> Option<&Error> {
#[allow(clippy::match_same_arms)]
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn Error> {
match *self {
FromSqlError::Other(ref err) => err.cause(),
FromSqlError::InvalidType | FromSqlError::OutOfRange(_) => None,
@ -73,11 +86,11 @@ pub type FromSqlResult<T> = Result<T, FromSqlError>;
/// fetching values as i64 and then doing the interpretation themselves or by
/// defining a newtype and implementing `FromSql`/`ToSql` for it.
pub trait FromSql: Sized {
fn column_result(value: ValueRef) -> FromSqlResult<Self>;
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self>;
}
impl FromSql for isize {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
i64::column_result(value).and_then(|i| {
if i < isize::min_value() as i64 || i > isize::max_value() as i64 {
Err(FromSqlError::OutOfRange(i))
@ -91,7 +104,7 @@ impl FromSql for isize {
macro_rules! from_sql_integral(
($t:ident) => (
impl FromSql for $t {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
i64::column_result(value).and_then(|i| {
if i < i64::from($t::min_value()) || i > i64::from($t::max_value()) {
Err(FromSqlError::OutOfRange(i))
@ -112,13 +125,13 @@ from_sql_integral!(u16);
from_sql_integral!(u32);
impl FromSql for i64 {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
value.as_i64()
}
}
impl FromSql for f64 {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
match value {
ValueRef::Integer(i) => Ok(i as f64),
ValueRef::Real(f) => Ok(f),
@ -128,7 +141,7 @@ impl FromSql for f64 {
}
impl FromSql for bool {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
i64::column_result(value).map(|i| match i {
0 => false,
_ => true,
@ -137,20 +150,20 @@ impl FromSql for bool {
}
impl FromSql for String {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
value.as_str().map(|s| s.to_string())
}
}
impl FromSql for Vec<u8> {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
value.as_blob().map(|b| b.to_vec())
}
}
#[cfg(feature = "i128_blob")]
impl FromSql for i128 {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
use byteorder::{BigEndian, ByteOrder};
value.as_blob().and_then(|bytes| {
@ -164,7 +177,7 @@ impl FromSql for i128 {
}
impl<T: FromSql> FromSql for Option<T> {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
match value {
ValueRef::Null => Ok(None),
_ => FromSql::column_result(value).map(Some),
@ -173,7 +186,7 @@ impl<T: FromSql> FromSql for Option<T> {
}
impl FromSql for Value {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
Ok(value.into())
}
}
@ -181,7 +194,7 @@ impl FromSql for Value {
#[cfg(test)]
mod test {
use super::FromSql;
use {Connection, Error};
use crate::{Connection, Error};
fn checked_memory_handle() -> Connection {
Connection::open_in_memory().unwrap()
@ -219,11 +232,11 @@ mod test {
check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
check_ranges::<i32>(
&db,
&[-2147483649, 2147483648],
&[-2147483648, -1, 0, 1, 2147483647],
&[-2_147_483_649, 2_147_483_648],
&[-2_147_483_648, -1, 0, 1, 2_147_483_647],
);
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]);
check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
}
}

View File

@ -19,9 +19,6 @@
//! store timespecs as `f64`s:
//!
//! ```rust
//! extern crate rusqlite;
//! extern crate time;
//!
//! use rusqlite::types::{FromSql, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
//! use rusqlite::Result;
//!
@ -77,7 +74,6 @@ mod value_ref;
/// ## Example
///
/// ```rust,no_run
/// # extern crate rusqlite;
/// # use rusqlite::{Connection, Result};
/// # use rusqlite::types::{Null};
/// fn main() {}
@ -98,7 +94,7 @@ pub enum Type {
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Type::Null => write!(f, "Null"),
Type::Integer => write!(f, "Integer"),
@ -111,12 +107,12 @@ impl fmt::Display for Type {
#[cfg(test)]
mod test {
extern crate time;
use time;
use super::Value;
use crate::{Connection, Error, NO_PARAMS};
use std::f64::EPSILON;
use std::os::raw::{c_double, c_int};
use {Connection, Error, NO_PARAMS};
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@ -227,6 +223,7 @@ mod test {
}
#[test]
#[allow(clippy::cyclomatic_complexity)]
fn test_mismatched_types() {
fn is_invalid_column_type(err: Error) -> bool {
match err {

View File

@ -1,21 +1,21 @@
//! `ToSql` and `FromSql` implementation for JSON `Value`.
extern crate serde_json;
use serde_json;
use self::serde_json::Value;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use Result;
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use crate::Result;
/// Serialize JSON `Value` to text.
impl ToSql for Value {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(serde_json::to_string(self).unwrap()))
}
}
/// Deserialize text/blob to JSON `Value`.
impl FromSql for Value {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
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),
@ -28,8 +28,8 @@ impl FromSql for Value {
#[cfg(test)]
mod test {
use super::serde_json;
use types::ToSql;
use {Connection, NO_PARAMS};
use crate::types::ToSql;
use crate::{Connection, NO_PARAMS};
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@ -46,7 +46,7 @@ mod test {
let data: serde_json::Value = serde_json::from_str(json).unwrap();
db.execute(
"INSERT INTO foo (t, b) VALUES (?, ?)",
&[&data as &ToSql, &json.as_bytes()],
&[&data as &dyn ToSql, &json.as_bytes()],
)
.unwrap();

View File

@ -1,13 +1,14 @@
extern crate time;
use time;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use Result;
use crate::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use crate::Result;
const CURRENT_TIMESTAMP_FMT: &str = "%Y-%m-%d %H:%M:%S";
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";
impl ToSql for time::Timespec {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
let time_string = time::at_utc(*self)
.strftime(SQLITE_DATETIME_FMT)
.unwrap()
@ -17,14 +18,17 @@ impl ToSql for time::Timespec {
}
impl FromSql for time::Timespec {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
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))))
})
match s.len() {
19 => time::strptime(s, CURRENT_TIMESTAMP_FMT),
_ => time::strptime(s, SQLITE_DATETIME_FMT).or_else(|err| {
time::strptime(s, SQLITE_DATETIME_FMT_LEGACY).or_else(|_| Err(err))
}),
}
.or_else(|err| Err(FromSqlError::Other(Box::new(err))))
})
.map(|tm| tm.to_timespec())
}
@ -33,7 +37,7 @@ impl FromSql for time::Timespec {
#[cfg(test)]
mod test {
use super::time;
use {Connection, NO_PARAMS};
use crate::{Connection, Result, NO_PARAMS};
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@ -50,10 +54,10 @@ mod test {
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(1_500_391_124, 1_000_000)); //July 18, 2017
ts_vec.push(time::Timespec::new(2_000_000_000, 2_000_000)); //May 18, 2033
ts_vec.push(time::Timespec::new(3_000_000_000, 999_999_999)); //January 24, 2065
ts_vec.push(time::Timespec::new(10_000_000_000, 0)); //November 20, 2286
for ts in ts_vec {
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap();
@ -67,4 +71,12 @@ mod test {
assert_eq!(from, ts);
}
}
#[test]
fn test_sqlite_functions() {
let db = checked_memory_handle();
let result: Result<time::Timespec> =
db.query_row("SELECT CURRENT_TIMESTAMP", NO_PARAMS, |r| r.get(0));
assert!(result.is_ok());
}
}

View File

@ -1,8 +1,8 @@
use super::{Null, Value, ValueRef};
use std::borrow::Cow;
#[cfg(feature = "array")]
use vtab::array::Array;
use Result;
use crate::vtab::array::Array;
use crate::Result;
use std::borrow::Cow;
/// `ToSqlOutput` represents the possible output types for implementors of the
/// `ToSql` trait.
@ -66,7 +66,7 @@ from_value!(Vec<u8>);
from_value!(i128);
impl<'a> ToSql for ToSqlOutput<'a> {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(match *self {
ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
@ -81,7 +81,7 @@ impl<'a> ToSql for ToSqlOutput<'a> {
/// A trait for types that can be converted into SQLite values.
pub trait ToSql {
fn to_sql(&self) -> Result<ToSqlOutput>;
fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
}
// We should be able to use a generic impl like this:
@ -99,7 +99,7 @@ pub trait ToSql {
macro_rules! to_sql_self(
($t:ty) => (
impl ToSql for $t {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(*self))
}
}
@ -125,43 +125,43 @@ impl<'a, T: ?Sized> ToSql for &'a T
where
T: ToSql,
{
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
(*self).to_sql()
}
}
impl ToSql for String {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self.as_str()))
}
}
impl ToSql for str {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self))
}
}
impl ToSql for Vec<u8> {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self.as_slice()))
}
}
impl ToSql for [u8] {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self))
}
}
impl ToSql for Value {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self))
}
}
impl<T: ToSql> ToSql for Option<T> {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
match *self {
None => Ok(ToSqlOutput::from(Null)),
Some(ref t) => t.to_sql(),
@ -170,7 +170,7 @@ impl<T: ToSql> ToSql for Option<T> {
}
impl<'a> ToSql for Cow<'a, str> {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self.as_ref()))
}
}
@ -207,8 +207,8 @@ mod test {
#[cfg(feature = "i128_blob")]
#[test]
fn test_i128() {
use crate::{Connection, NO_PARAMS};
use std::i128;
use {Connection, NO_PARAMS};
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")
.unwrap();

View File

@ -1,5 +1,5 @@
use super::{Type, Value};
use types::{FromSqlError, FromSqlResult};
use crate::types::{FromSqlError, FromSqlResult};
/// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the
/// memory backing this value is owned by SQLite.
@ -70,7 +70,7 @@ impl<'a> ValueRef<'a> {
}
impl<'a> From<ValueRef<'a>> for Value {
fn from(borrowed: ValueRef) -> Value {
fn from(borrowed: ValueRef<'_>) -> Value {
match borrowed {
ValueRef::Null => Value::Null,
ValueRef::Integer(i) => Value::Integer(i),
@ -82,13 +82,13 @@ impl<'a> From<ValueRef<'a>> for Value {
}
impl<'a> From<&'a str> for ValueRef<'a> {
fn from(s: &str) -> ValueRef {
fn from(s: &str) -> ValueRef<'_> {
ValueRef::Text(s)
}
}
impl<'a> From<&'a [u8]> for ValueRef<'a> {
fn from(s: &[u8]) -> ValueRef {
fn from(s: &[u8]) -> ValueRef<'_> {
ValueRef::Blob(s)
}
}
@ -104,3 +104,56 @@ impl<'a> From<&'a Value> for ValueRef<'a> {
}
}
}
#[cfg(any(feature = "functions", feature = "session", feature = "vtab"))]
impl<'a> ValueRef<'a> {
pub(crate) unsafe fn from_value(value: *mut crate::ffi::sqlite3_value) -> ValueRef<'a> {
use crate::ffi;
use std::ffi::CStr;
use std::os::raw::c_char;
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"),
}
}
}

View File

@ -4,9 +4,11 @@ use std::os::raw::c_int;
#[cfg(feature = "unlock_notify")]
use std::os::raw::c_void;
#[cfg(feature = "unlock_notify")]
use std::panic::catch_unwind;
#[cfg(feature = "unlock_notify")]
use std::sync::{Condvar, Mutex};
use ffi;
use crate::ffi;
#[cfg(feature = "unlock_notify")]
struct UnlockNotification {
@ -42,8 +44,10 @@ unsafe extern "C" fn unlock_notify_cb(ap_arg: *mut *mut c_void, n_arg: c_int) {
use std::slice::from_raw_parts;
let args = from_raw_parts(ap_arg, n_arg as usize);
for arg in args {
let un: &mut UnlockNotification = &mut *(*arg as *mut UnlockNotification);
un.fired();
let _ = catch_unwind(|| {
let un: &mut UnlockNotification = &mut *(*arg as *mut UnlockNotification);
un.fired()
});
}
}
@ -99,10 +103,10 @@ pub fn wait_for_unlock_notify(_db: *mut ffi::sqlite3) -> c_int {
#[cfg(feature = "unlock_notify")]
#[cfg(test)]
mod test {
use crate::{Connection, OpenFlags, Result, Transaction, TransactionBehavior, NO_PARAMS};
use std::sync::mpsc::sync_channel;
use std::thread;
use std::time;
use {Connection, OpenFlags, Result, Transaction, TransactionBehavior, NO_PARAMS};
#[test]
fn test_unlock_notify() {

View File

@ -1,4 +1,4 @@
use ffi;
use crate::ffi;
use std::ffi::CStr;
/// Returns the SQLite version as an integer; e.g., `3016002` for version

View File

@ -5,13 +5,13 @@ 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::{
use crate::ffi;
use crate::types::{ToSql, ToSqlOutput, Value};
use crate::vtab::{
eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection,
VTabCursor, Values,
};
use {Connection, Result};
use crate::{Connection, Result};
// http://sqlite.org/bindptr.html
@ -24,7 +24,7 @@ pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) {
pub type Array = Rc<Vec<Value>>;
impl ToSql for Array {
fn to_sql(&self) -> Result<ToSqlOutput> {
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::Array(self.clone()))
}
}
@ -129,9 +129,9 @@ impl ArrayTabCursor {
}
}
impl VTabCursor for ArrayTabCursor {
fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values) -> Result<()> {
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));
self.ptr = args.get_array(0)?;
} else {
self.ptr = None;
}
@ -169,10 +169,10 @@ impl VTabCursor for ArrayTabCursor {
#[cfg(test)]
mod test {
use crate::types::Value;
use crate::vtab::array;
use crate::Connection;
use std::rc::Rc;
use types::Value;
use vtab::array;
use Connection;
#[test]
fn test_array_module() {
@ -180,7 +180,7 @@ mod test {
array::load_module(&db).unwrap();
let v = vec![1i64, 2, 3, 4];
let values = v.into_iter().map(|i| Value::from(i)).collect();
let values = v.into_iter().map(Value::from).collect();
let ptr = Rc::new(values);
{
let mut stmt = db.prepare("SELECT value from rarray(?);").unwrap();

View File

@ -1,20 +1,20 @@
//! CSV Virtual Table.
//!
//! Port of [csv](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/csv.c) C extension.
extern crate csv;
use 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::{
use crate::ffi;
use crate::types::Null;
use crate::vtab::{
dequote, escape_double_quote, parse_boolean, read_only_module, Context, CreateVTab, IndexInfo,
Module, VTab, VTabConnection, VTabCursor, Values,
};
use {Connection, Error, Result};
use crate::{Connection, Error, Result};
/// Register the "csv" module.
/// ```sql
@ -60,7 +60,7 @@ impl CSVTab {
}
fn parameter(c_slice: &[u8]) -> Result<(&str, &str)> {
let arg = try!(str::from_utf8(c_slice)).trim();
let arg = str::from_utf8(c_slice)?.trim();
let mut split = arg.split('=');
if let Some(key) = split.next() {
if let Some(value) = split.next() {
@ -107,7 +107,7 @@ impl VTab for CSVTab {
let args = &args[3..];
for c_slice in args {
let (param, value) = try!(CSVTab::parameter(c_slice));
let (param, value) = CSVTab::parameter(c_slice)?;
match param {
"filename" => {
if !Path::new(value).exists() {
@ -189,10 +189,10 @@ impl VTab for CSVTab {
let mut cols: Vec<String> = Vec::new();
if vtab.has_headers || (n_col.is_none() && schema.is_none()) {
let mut reader = try!(vtab.reader());
let mut reader = vtab.reader()?;
if vtab.has_headers {
{
let headers = try!(reader.headers());
let headers = reader.headers()?;
// headers ignored if cols is not empty
if n_col.is_none() && schema.is_none() {
cols = headers
@ -204,7 +204,7 @@ impl VTab for CSVTab {
vtab.offset_first_row = reader.position().clone();
} else {
let mut record = csv::ByteRecord::new();
if try!(reader.read_byte_record(&mut record)) {
if reader.read_byte_record(&mut record)? {
for (i, _) in record.iter().enumerate() {
cols.push(format!("c{}", i));
}
@ -245,7 +245,7 @@ impl VTab for CSVTab {
}
fn open(&self) -> Result<CSVTabCursor> {
Ok(CSVTabCursor::new(try!(self.reader())))
Ok(CSVTabCursor::new(self.reader()?))
}
}
@ -285,10 +285,15 @@ impl CSVTabCursor {
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<()> {
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.reader.seek(offset_first_row)?;
}
self.row_number = 0;
self.next()
@ -301,7 +306,7 @@ impl VTabCursor for CSVTabCursor {
return Ok(());
}
self.eof = !try!(self.reader.read_record(&mut self.cols));
self.eof = !self.reader.read_record(&mut self.cols)?;
}
self.row_number += 1;
@ -340,8 +345,8 @@ impl From<csv::Error> for Error {
#[cfg(test)]
mod test {
use vtab::csvtab;
use {Connection, Result, NO_PARAMS};
use crate::vtab::csvtab;
use crate::{Connection, Result, NO_PARAMS};
#[test]
fn test_csv_module() {
@ -361,7 +366,7 @@ mod test {
.query_map(NO_PARAMS, |row| row.get::<_, i32>(0))
.unwrap()
.collect();
let sum = ids.unwrap().iter().fold(0, |acc, &id| acc + id);
let sum = ids.unwrap().iter().sum::<i32>();
assert_eq!(sum, 15);
}
db.execute_batch("DROP TABLE vtab").unwrap();

View File

@ -17,12 +17,12 @@ 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};
use crate::context::set_result;
use crate::error::error_from_sqlite_code;
use crate::ffi;
pub use crate::ffi::{sqlite3_vtab, sqlite3_vtab_cursor};
use crate::types::{FromSql, FromSqlError, ToSql, ValueRef};
use crate::{str_to_cstring, Connection, Error, InnerConnection, Result};
// let conn: Connection = ...;
// let mod: Module = ...; // VTab builder
@ -99,6 +99,8 @@ pub fn read_only_module<T: CreateVTab>(version: c_int) -> Module<T> {
xSavepoint: None,
xRelease: None,
xRollbackTo: None,
#[cfg(any(feature = "bundled", feature = "vtab_v3"))]
xShadowName: None,
};
Module {
base: ffi_module,
@ -137,6 +139,8 @@ pub fn eponymous_only_module<T: VTab>(version: c_int) -> Module<T> {
xSavepoint: None,
xRelease: None,
xRollbackTo: None,
#[cfg(any(feature = "bundled", feature = "vtab_v3"))]
xShadowName: None,
};
Module {
base: ffi_module,
@ -250,7 +254,7 @@ pub struct IndexInfo(*mut ffi::sqlite3_index_info);
impl IndexInfo {
/// Record WHERE clause constraints.
pub fn constraints(&self) -> IndexConstraintIter {
pub fn constraints(&self) -> IndexConstraintIter<'_> {
let constraints =
unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
IndexConstraintIter {
@ -259,7 +263,7 @@ impl IndexInfo {
}
/// Information about the ORDER BY clause.
pub fn order_bys(&self) -> OrderByIter {
pub fn order_bys(&self) -> OrderByIter<'_> {
let order_bys =
unsafe { slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize) };
OrderByIter {
@ -272,7 +276,7 @@ impl IndexInfo {
unsafe { (*self.0).nOrderBy as usize }
}
pub fn constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage {
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)
};
@ -412,7 +416,7 @@ impl<'a> OrderBy<'a> {
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<()>;
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<()>;
@ -467,6 +471,8 @@ impl<'a> Values<'a> {
Error::FromSqlConversionFailure(idx, value.data_type(), err)
}
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
#[cfg(feature = "i128_blob")]
FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(idx, value.data_type()),
})
}
@ -474,7 +480,7 @@ impl<'a> Values<'a> {
// 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;
use crate::types::Value;
let arg = self.args[idx];
let ptr = unsafe { ffi::sqlite3_value_pointer(arg, array::ARRAY_TYPE) };
if ptr.is_null() {
@ -489,7 +495,7 @@ impl<'a> Values<'a> {
}
}
pub fn iter(&self) -> ValueIter {
pub fn iter(&self) -> ValueIter<'_> {
ValueIter {
iter: self.args.iter(),
}
@ -544,7 +550,7 @@ impl InnerConnection {
module: &Module<T>,
aux: Option<T::Aux>,
) -> Result<()> {
let c_name = try!(str_to_cstring(module_name));
let c_name = str_to_cstring(module_name)?;
let r = match aux {
Some(aux) => {
let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux));
@ -573,7 +579,7 @@ impl InnerConnection {
}
/// Escape double-quote (`"`) character occurences by doubling them (`""`).
pub fn escape_double_quote(identifier: &str) -> Cow<str> {
pub fn escape_double_quote(identifier: &str) -> Cow<'_, str> {
if identifier.contains('"') {
// escape quote by doubling them
Owned(identifier.replace("\"", "\"\""))

View File

@ -4,13 +4,13 @@
use std::default::Default;
use std::os::raw::c_int;
use ffi;
use types::Type;
use vtab::{
use crate::ffi;
use crate::types::Type;
use crate::vtab::{
eponymous_only_module, Context, IndexConstraintOp, IndexInfo, Module, VTab, VTabConnection,
VTabCursor, Values,
};
use {Connection, Result};
use crate::{Connection, Result};
/// Register the "generate_series" module.
pub fn load_module(conn: &Connection) -> Result<()> {
@ -184,23 +184,23 @@ impl SeriesTabCursor {
}
}
impl VTabCursor for SeriesTabCursor {
fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values) -> Result<()> {
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));
self.min_value = args.get(i)?;
i += 1;
} else {
self.min_value = 0;
}
if idx_num.contains(QueryPlanFlags::STOP) {
self.max_value = try!(args.get(i));
self.max_value = args.get(i)?;
i += 1;
} else {
self.max_value = 0xffff_ffff;
}
if idx_num.contains(QueryPlanFlags::STEP) {
self.step = try!(args.get(i));
self.step = args.get(i)?;
if self.step < 1 {
self.step = 1;
}
@ -263,14 +263,14 @@ impl VTabCursor for SeriesTabCursor {
#[cfg(test)]
mod test {
use ffi;
use vtab::series;
use {Connection, NO_PARAMS};
use crate::ffi;
use crate::vtab::series;
use crate::{Connection, NO_PARAMS};
#[test]
fn test_series_module() {
let version = unsafe { ffi::sqlite3_libversion_number() };
if version < 3008012 {
if version < 3_008_012 {
return;
}

View File

@ -5,7 +5,6 @@
#[cfg(feature = "trace")]
#[macro_use]
extern crate lazy_static;
extern crate rusqlite;
#[cfg(feature = "trace")]
fn main() {

View File

@ -1,9 +1,7 @@
//! 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 libsqlite3_sys as ffi;
extern crate rusqlite;
use rusqlite::ffi;
use rusqlite::Connection;
#[test]

View File

@ -1,8 +1,5 @@
//! Ensure Virtual tables can be declared outside `rusqlite` crate.
#[cfg(feature = "vtab")]
extern crate rusqlite;
#[cfg(feature = "vtab")]
#[test]
fn test_dummy_module() {
@ -61,7 +58,7 @@ fn test_dummy_module() {
&mut self,
_idx_num: c_int,
_idx_str: Option<&str>,
_args: &Values,
_args: &Values<'_>,
) -> Result<()> {
self.row_id = 1;
Ok(())
@ -91,14 +88,14 @@ fn test_dummy_module() {
.unwrap();
let version = version_number();
if version < 3008012 {
if version < 3_008_012 {
return;
}
let mut s = db.prepare("SELECT * FROM dummy()").unwrap();
let dummy = s
.query_row(&[] as &[&ToSql], |row| row.get::<_, i32>(0))
.query_row(&[] as &[&dyn ToSql], |row| row.get::<_, i32>(0))
.unwrap();
assert_eq!(1, dummy);
}