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

This commit is contained in:
gwenn 2018-08-11 13:37:56 +02:00
commit 0ccf98d214
29 changed files with 1421 additions and 1075 deletions

View File

@ -38,7 +38,7 @@ script:
- cargo test --features bundled
- cargo test --features sqlcipher
- cargo test --features "unlock_notify bundled"
- cargo test --features "csvtab vtab"
- cargo test --features "array csvtab vtab"
- cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab"
- cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab buildtime_bindgen"
- cargo test --features "backup blob chrono csvtab functions hooks limits load_extension serde_json trace vtab bundled"

View File

@ -5,8 +5,8 @@ fn main() {
#[cfg(feature = "bundled")]
mod build {
extern crate cc;
use std::{env, fs};
use std::path::Path;
use std::{env, fs};
pub fn main() {
if cfg!(feature = "sqlcipher") {
@ -68,8 +68,10 @@ mod build {
match header {
HeaderLocation::FromEnvironment => {
let prefix = env_prefix();
let mut header = env::var(format!("{}_INCLUDE_DIR", prefix))
.expect(&format!("{}_INCLUDE_DIR must be set if {}_LIB_DIR is set", prefix, prefix));
let mut header = env::var(format!("{}_INCLUDE_DIR", prefix)).expect(&format!(
"{}_INCLUDE_DIR must be set if {}_LIB_DIR is set",
prefix, prefix
));
header.push_str("/sqlite3.h");
header
}
@ -90,7 +92,7 @@ mod build {
println!("cargo:rerun-if-env-changed={}_INCLUDE_DIR", env_prefix());
println!("cargo:rerun-if-env-changed={}_LIB_DIR", env_prefix());
if cfg!(target_os="windows") {
if cfg!(target_os = "windows") {
println!("cargo:rerun-if-env-changed=PATH");
}
// Allow users to specify where to find SQLite.
@ -105,7 +107,10 @@ mod build {
}
// See if pkg-config can do everything for us.
match pkg_config::Config::new().print_system_libs(false).probe(link_lib) {
match pkg_config::Config::new()
.print_system_libs(false)
.probe(link_lib)
{
Ok(mut lib) => {
if let Some(mut header) = lib.include_paths.pop() {
header.push("sqlite3.h");
@ -162,28 +167,21 @@ mod build {
mod bindings {
use super::HeaderLocation;
use std::{env, fs};
use std::path::Path;
use std::{env, fs};
#[cfg_attr(rustfmt, rustfmt_skip)]
static PREBUILT_BINDGEN_PATHS: &'static [&'static str] = &[
"bindgen-bindings/bindgen_3.6.8.rs",
#[cfg(feature = "min_sqlite_version_3_6_11")]
"bindgen-bindings/bindgen_3.6.11.rs",
#[cfg(feature = "min_sqlite_version_3_6_23")]
"bindgen-bindings/bindgen_3.6.23.rs",
#[cfg(feature = "min_sqlite_version_3_7_3")]
"bindgen-bindings/bindgen_3.7.3.rs",
#[cfg(feature = "min_sqlite_version_3_7_4")]
"bindgen-bindings/bindgen_3.7.4.rs",
#[cfg(feature = "min_sqlite_version_3_7_7")]
"bindgen-bindings/bindgen_3.7.7.rs",
#[cfg(feature = "min_sqlite_version_3_7_16")]
"bindgen-bindings/bindgen_3.7.16.rs",
];
@ -200,12 +198,12 @@ mod build {
mod bindings {
extern crate bindgen;
use self::bindgen::callbacks::{ParseCallbacks, IntKind};
use self::bindgen::callbacks::{IntKind, ParseCallbacks};
use super::HeaderLocation;
use std::env;
use std::io::Write;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
#[derive(Debug)]
@ -252,7 +250,8 @@ mod build {
.open(path.clone())
.expect(&format!("Could not write to {:?}", path));
file.write_all(output.as_bytes()).expect(&format!("Could not write to {:?}", path));
file.write_all(output.as_bytes())
.expect(&format!("Could not write to {:?}", path));
}
}
}

View File

@ -1,6 +1,6 @@
use std::os::raw::c_int;
use std::error;
use std::fmt;
use std::os::raw::c_int;
/// Error Codes
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -64,30 +64,30 @@ pub struct Error {
impl Error {
pub fn new(result_code: c_int) -> Error {
let code = match result_code & 0xff {
super::SQLITE_INTERNAL => ErrorCode::InternalMalfunction,
super::SQLITE_PERM => ErrorCode::PermissionDenied,
super::SQLITE_ABORT => ErrorCode::OperationAborted,
super::SQLITE_BUSY => ErrorCode::DatabaseBusy,
super::SQLITE_LOCKED => ErrorCode::DatabaseLocked,
super::SQLITE_NOMEM => ErrorCode::OutOfMemory,
super::SQLITE_READONLY => ErrorCode::ReadOnly,
super::SQLITE_INTERNAL => ErrorCode::InternalMalfunction,
super::SQLITE_PERM => ErrorCode::PermissionDenied,
super::SQLITE_ABORT => ErrorCode::OperationAborted,
super::SQLITE_BUSY => ErrorCode::DatabaseBusy,
super::SQLITE_LOCKED => ErrorCode::DatabaseLocked,
super::SQLITE_NOMEM => ErrorCode::OutOfMemory,
super::SQLITE_READONLY => ErrorCode::ReadOnly,
super::SQLITE_INTERRUPT => ErrorCode::OperationInterrupted,
super::SQLITE_IOERR => ErrorCode::SystemIOFailure,
super::SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt,
super::SQLITE_NOTFOUND => ErrorCode::NotFound,
super::SQLITE_FULL => ErrorCode::DiskFull,
super::SQLITE_CANTOPEN => ErrorCode::CannotOpen,
super::SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed,
super::SQLITE_SCHEMA => ErrorCode::SchemaChanged,
super::SQLITE_TOOBIG => ErrorCode::TooBig,
super::SQLITE_CONSTRAINT=> ErrorCode::ConstraintViolation,
super::SQLITE_MISMATCH => ErrorCode::TypeMismatch,
super::SQLITE_MISUSE => ErrorCode::APIMisuse,
super::SQLITE_NOLFS => ErrorCode::NoLargeFileSupport,
super::SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied,
super::SQLITE_RANGE => ErrorCode::ParameterOutOfRange,
super::SQLITE_NOTADB => ErrorCode::NotADatabase,
_ => ErrorCode::Unknown,
super::SQLITE_IOERR => ErrorCode::SystemIOFailure,
super::SQLITE_CORRUPT => ErrorCode::DatabaseCorrupt,
super::SQLITE_NOTFOUND => ErrorCode::NotFound,
super::SQLITE_FULL => ErrorCode::DiskFull,
super::SQLITE_CANTOPEN => ErrorCode::CannotOpen,
super::SQLITE_PROTOCOL => ErrorCode::FileLockingProtocolFailed,
super::SQLITE_SCHEMA => ErrorCode::SchemaChanged,
super::SQLITE_TOOBIG => ErrorCode::TooBig,
super::SQLITE_CONSTRAINT => ErrorCode::ConstraintViolation,
super::SQLITE_MISMATCH => ErrorCode::TypeMismatch,
super::SQLITE_MISUSE => ErrorCode::APIMisuse,
super::SQLITE_NOLFS => ErrorCode::NoLargeFileSupport,
super::SQLITE_AUTH => ErrorCode::AuthorizationForStatementDenied,
super::SQLITE_RANGE => ErrorCode::ParameterOutOfRange,
super::SQLITE_NOTADB => ErrorCode::NotADatabase,
_ => ErrorCode::Unknown,
};
Error {
@ -99,7 +99,12 @@ impl Error {
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Error code {}: {}", self.extended_code, code_to_str(self.extended_code))
write!(
f,
"Error code {}: {}",
self.extended_code,
code_to_str(self.extended_code)
)
}
}
@ -114,48 +119,48 @@ impl error::Error for Error {
// in the current version of SQLite. We repeat them here so we don't have to worry about which
// version of SQLite added which constants, and we only use them to implement code_to_str below.
const SQLITE_NOTICE : c_int = 27;
const SQLITE_WARNING : c_int = 28;
const SQLITE_NOTICE: c_int = 27;
const SQLITE_WARNING: c_int = 28;
// Extended result codes.
const SQLITE_IOERR_SHMOPEN : c_int = (super::SQLITE_IOERR | (18<<8));
const SQLITE_IOERR_SHMSIZE : c_int = (super::SQLITE_IOERR | (19<<8));
const SQLITE_IOERR_SHMLOCK : c_int = (super::SQLITE_IOERR | (20<<8));
const SQLITE_IOERR_SHMMAP : c_int = (super::SQLITE_IOERR | (21<<8));
const SQLITE_IOERR_SEEK : c_int = (super::SQLITE_IOERR | (22<<8));
const SQLITE_IOERR_DELETE_NOENT : c_int = (super::SQLITE_IOERR | (23<<8));
const SQLITE_IOERR_MMAP : c_int = (super::SQLITE_IOERR | (24<<8));
const SQLITE_IOERR_GETTEMPPATH : c_int = (super::SQLITE_IOERR | (25<<8));
const SQLITE_IOERR_CONVPATH : c_int = (super::SQLITE_IOERR | (26<<8));
const SQLITE_IOERR_VNODE : c_int = (super::SQLITE_IOERR | (27<<8));
const SQLITE_LOCKED_SHAREDCACHE : c_int = (super::SQLITE_LOCKED | (1<<8));
const SQLITE_BUSY_RECOVERY : c_int = (super::SQLITE_BUSY | (1<<8));
const SQLITE_BUSY_SNAPSHOT : c_int = (super::SQLITE_BUSY | (2<<8));
const SQLITE_CANTOPEN_NOTEMPDIR : c_int = (super::SQLITE_CANTOPEN | (1<<8));
const SQLITE_CANTOPEN_ISDIR : c_int = (super::SQLITE_CANTOPEN | (2<<8));
const SQLITE_CANTOPEN_FULLPATH : c_int = (super::SQLITE_CANTOPEN | (3<<8));
const SQLITE_CANTOPEN_CONVPATH : c_int = (super::SQLITE_CANTOPEN | (4<<8));
const SQLITE_CORRUPT_VTAB : c_int = (super::SQLITE_CORRUPT | (1<<8));
const SQLITE_READONLY_RECOVERY : c_int = (super::SQLITE_READONLY | (1<<8));
const SQLITE_READONLY_CANTLOCK : c_int = (super::SQLITE_READONLY | (2<<8));
const SQLITE_READONLY_ROLLBACK : c_int = (super::SQLITE_READONLY | (3<<8));
const SQLITE_READONLY_DBMOVED : c_int = (super::SQLITE_READONLY | (4<<8));
const SQLITE_ABORT_ROLLBACK : c_int = (super::SQLITE_ABORT | (2<<8));
const SQLITE_CONSTRAINT_CHECK : c_int = (super::SQLITE_CONSTRAINT | (1<<8));
const SQLITE_CONSTRAINT_COMMITHOOK : c_int = (super::SQLITE_CONSTRAINT | (2<<8));
const SQLITE_CONSTRAINT_FOREIGNKEY : c_int = (super::SQLITE_CONSTRAINT | (3<<8));
const SQLITE_CONSTRAINT_FUNCTION : c_int = (super::SQLITE_CONSTRAINT | (4<<8));
const SQLITE_CONSTRAINT_NOTNULL : c_int = (super::SQLITE_CONSTRAINT | (5<<8));
const SQLITE_CONSTRAINT_PRIMARYKEY : c_int = (super::SQLITE_CONSTRAINT | (6<<8));
const SQLITE_CONSTRAINT_TRIGGER : c_int = (super::SQLITE_CONSTRAINT | (7<<8));
const SQLITE_CONSTRAINT_UNIQUE : c_int = (super::SQLITE_CONSTRAINT | (8<<8));
const SQLITE_CONSTRAINT_VTAB : c_int = (super::SQLITE_CONSTRAINT | (9<<8));
const SQLITE_CONSTRAINT_ROWID : c_int = (super::SQLITE_CONSTRAINT |(10<<8));
const SQLITE_NOTICE_RECOVER_WAL : c_int = (SQLITE_NOTICE | (1<<8));
const SQLITE_NOTICE_RECOVER_ROLLBACK : c_int = (SQLITE_NOTICE | (2<<8));
const SQLITE_WARNING_AUTOINDEX : c_int = (SQLITE_WARNING | (1<<8));
const SQLITE_AUTH_USER : c_int = (super::SQLITE_AUTH | (1<<8));
const SQLITE_IOERR_SHMOPEN: c_int = (super::SQLITE_IOERR | (18 << 8));
const SQLITE_IOERR_SHMSIZE: c_int = (super::SQLITE_IOERR | (19 << 8));
const SQLITE_IOERR_SHMLOCK: c_int = (super::SQLITE_IOERR | (20 << 8));
const SQLITE_IOERR_SHMMAP: c_int = (super::SQLITE_IOERR | (21 << 8));
const SQLITE_IOERR_SEEK: c_int = (super::SQLITE_IOERR | (22 << 8));
const SQLITE_IOERR_DELETE_NOENT: c_int = (super::SQLITE_IOERR | (23 << 8));
const SQLITE_IOERR_MMAP: c_int = (super::SQLITE_IOERR | (24 << 8));
const SQLITE_IOERR_GETTEMPPATH: c_int = (super::SQLITE_IOERR | (25 << 8));
const SQLITE_IOERR_CONVPATH: c_int = (super::SQLITE_IOERR | (26 << 8));
const SQLITE_IOERR_VNODE: c_int = (super::SQLITE_IOERR | (27 << 8));
const SQLITE_LOCKED_SHAREDCACHE: c_int = (super::SQLITE_LOCKED | (1 << 8));
const SQLITE_BUSY_RECOVERY: c_int = (super::SQLITE_BUSY | (1 << 8));
const SQLITE_BUSY_SNAPSHOT: c_int = (super::SQLITE_BUSY | (2 << 8));
const SQLITE_CANTOPEN_NOTEMPDIR: c_int = (super::SQLITE_CANTOPEN | (1 << 8));
const SQLITE_CANTOPEN_ISDIR: c_int = (super::SQLITE_CANTOPEN | (2 << 8));
const SQLITE_CANTOPEN_FULLPATH: c_int = (super::SQLITE_CANTOPEN | (3 << 8));
const SQLITE_CANTOPEN_CONVPATH: c_int = (super::SQLITE_CANTOPEN | (4 << 8));
const SQLITE_CORRUPT_VTAB: c_int = (super::SQLITE_CORRUPT | (1 << 8));
const SQLITE_READONLY_RECOVERY: c_int = (super::SQLITE_READONLY | (1 << 8));
const SQLITE_READONLY_CANTLOCK: c_int = (super::SQLITE_READONLY | (2 << 8));
const SQLITE_READONLY_ROLLBACK: c_int = (super::SQLITE_READONLY | (3 << 8));
const SQLITE_READONLY_DBMOVED: c_int = (super::SQLITE_READONLY | (4 << 8));
const SQLITE_ABORT_ROLLBACK: c_int = (super::SQLITE_ABORT | (2 << 8));
const SQLITE_CONSTRAINT_CHECK: c_int = (super::SQLITE_CONSTRAINT | (1 << 8));
const SQLITE_CONSTRAINT_COMMITHOOK: c_int = (super::SQLITE_CONSTRAINT | (2 << 8));
const SQLITE_CONSTRAINT_FOREIGNKEY: c_int = (super::SQLITE_CONSTRAINT | (3 << 8));
const SQLITE_CONSTRAINT_FUNCTION: c_int = (super::SQLITE_CONSTRAINT | (4 << 8));
const SQLITE_CONSTRAINT_NOTNULL: c_int = (super::SQLITE_CONSTRAINT | (5 << 8));
const SQLITE_CONSTRAINT_PRIMARYKEY: c_int = (super::SQLITE_CONSTRAINT | (6 << 8));
const SQLITE_CONSTRAINT_TRIGGER: c_int = (super::SQLITE_CONSTRAINT | (7 << 8));
const SQLITE_CONSTRAINT_UNIQUE: c_int = (super::SQLITE_CONSTRAINT | (8 << 8));
const SQLITE_CONSTRAINT_VTAB: c_int = (super::SQLITE_CONSTRAINT | (9 << 8));
const SQLITE_CONSTRAINT_ROWID: c_int = (super::SQLITE_CONSTRAINT | (10 << 8));
const SQLITE_NOTICE_RECOVER_WAL: c_int = (SQLITE_NOTICE | (1 << 8));
const SQLITE_NOTICE_RECOVER_ROLLBACK: c_int = (SQLITE_NOTICE | (2 << 8));
const SQLITE_WARNING_AUTOINDEX: c_int = (SQLITE_WARNING | (1 << 8));
const SQLITE_AUTH_USER: c_int = (super::SQLITE_AUTH | (1 << 8));
pub fn code_to_str(code: c_int) -> &'static str {
match code {

View File

@ -51,9 +51,13 @@ pub type sqlite3_index_constraint = sqlite3_index_info_sqlite3_index_constraint;
pub type sqlite3_index_constraint_usage = sqlite3_index_info_sqlite3_index_constraint_usage;
impl Default for sqlite3_vtab {
fn default() -> Self { unsafe { mem::zeroed() } }
fn default() -> Self {
unsafe { mem::zeroed() }
}
}
impl Default for sqlite3_vtab_cursor {
fn default() -> Self { unsafe { mem::zeroed() } }
}
fn default() -> Self {
unsafe { mem::zeroed() }
}
}

View File

@ -36,8 +36,8 @@ use std::time::Duration;
use ffi;
use {DatabaseName, Connection, Result};
use error::{error_from_sqlite_code, error_from_handle};
use error::{error_from_handle, error_from_sqlite_code};
use {Connection, DatabaseName, Result};
impl Connection {
/// Back up the `name` database to the given destination path.
@ -52,14 +52,20 @@ impl Connection {
///
/// Will return `Err` if the destination path cannot be opened
/// or if the backup fails.
pub fn backup<P: AsRef<Path>>(&self,
name: DatabaseName,
dst_path: P,
progress: Option<fn(Progress)>)
-> Result<()> {
use self::StepResult::{More, Done, Busy, Locked};
pub fn backup<P: AsRef<Path>>(
&self,
name: DatabaseName,
dst_path: P,
progress: Option<fn(Progress)>,
) -> Result<()> {
use self::StepResult::{Busy, Done, Locked, More};
let mut dst = try!(Connection::open(dst_path));
let backup = try!(Backup::new_with_names(self, name, &mut dst, DatabaseName::Main));
let backup = try!(Backup::new_with_names(
self,
name,
&mut dst,
DatabaseName::Main
));
let mut r = More;
while r == More {
@ -89,12 +95,13 @@ impl Connection {
///
/// Will return `Err` if the destination path cannot be opened
/// or if the restore fails.
pub fn restore<P: AsRef<Path>>(&mut self,
name: DatabaseName,
src_path: P,
progress: Option<fn(Progress)>)
-> Result<()> {
use self::StepResult::{More, Done, Busy, Locked};
pub fn restore<P: AsRef<Path>>(
&mut self,
name: DatabaseName,
src_path: P,
progress: Option<fn(Progress)>,
) -> 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));
@ -124,7 +131,7 @@ impl Connection {
}
/// Possible successful results of calling `Backup::step`.
#[derive(Copy,Clone,Debug,PartialEq,Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum StepResult {
/// The backup is complete.
Done,
@ -146,7 +153,7 @@ pub enum StepResult {
/// backup is as of the last call to `step` - if the source database is
/// modified after a call to `step`, the progress value will become outdated
/// and potentially incorrect.
#[derive(Copy,Clone,Debug)]
#[derive(Copy, Clone, Debug)]
pub struct Progress {
/// Number of pages in the source database that still need to be backed up.
pub remaining: c_int,
@ -184,21 +191,24 @@ impl<'a, 'b> Backup<'a, 'b> {
///
/// Will return `Err` if the underlying `sqlite3_backup_init` call returns
/// `NULL`.
pub fn new_with_names(from: &'a Connection,
from_name: DatabaseName,
to: &'b mut Connection,
to_name: DatabaseName)
-> Result<Backup<'a, 'b>> {
pub fn new_with_names(
from: &'a Connection,
from_name: DatabaseName,
to: &'b mut Connection,
to_name: DatabaseName,
) -> Result<Backup<'a, 'b>> {
let to_name = try!(to_name.to_cstring());
let from_name = try!(from_name.to_cstring());
let to_db = to.db.borrow_mut().db;
let b = unsafe {
let b = ffi::sqlite3_backup_init(to_db,
to_name.as_ptr(),
from.db.borrow_mut().db,
from_name.as_ptr());
let b = ffi::sqlite3_backup_init(
to_db,
to_name.as_ptr(),
from.db.borrow_mut().db,
from_name.as_ptr(),
);
if b.is_null() {
return Err(error_from_handle(to_db, ffi::sqlite3_errcode(to_db)));
}
@ -206,10 +216,10 @@ impl<'a, 'b> Backup<'a, 'b> {
};
Ok(Backup {
phantom_from: PhantomData,
phantom_to: PhantomData,
b,
})
phantom_from: PhantomData,
phantom_to: PhantomData,
b,
})
}
/// Gets the progress of the backup as of the last call to `step`.
@ -235,7 +245,7 @@ impl<'a, 'b> Backup<'a, 'b> {
/// `LOCKED` are transient errors and are therefore returned as possible
/// `Ok` values.
pub fn step(&self, num_pages: c_int) -> Result<StepResult> {
use self::StepResult::{Done, More, Busy, Locked};
use self::StepResult::{Busy, Done, Locked, More};
let rc = unsafe { ffi::sqlite3_backup_step(self.b, num_pages) };
match rc {
@ -262,12 +272,13 @@ impl<'a, 'b> Backup<'a, 'b> {
/// # Failure
///
/// Will return `Err` if any of the calls to `step` return `Err`.
pub fn run_to_completion(&self,
pages_per_step: c_int,
pause_between_pages: Duration,
progress: Option<fn(Progress)>)
-> Result<()> {
use self::StepResult::{Done, More, Busy, Locked};
pub fn run_to_completion(
&self,
pages_per_step: c_int,
pause_between_pages: Duration,
progress: Option<fn(Progress)>,
) -> Result<()> {
use self::StepResult::{Busy, Done, Locked, More};
assert!(pages_per_step > 0, "pages_per_step must be positive");
@ -292,12 +303,11 @@ impl<'a, 'b> Drop for Backup<'a, 'b> {
#[cfg(test)]
mod test {
use {Connection, DatabaseName};
use std::time::Duration;
use super::Backup;
use std::time::Duration;
use {Connection, DatabaseName};
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_backup() {
let src = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
@ -313,22 +323,27 @@ mod test {
backup.step(-1).unwrap();
}
let the_answer: i64 = dst.query_row("SELECT x FROM foo", &[], |r| r.get(0)).unwrap();
let the_answer: i64 = dst
.query_row("SELECT x FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(42, the_answer);
src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();
{
let backup = Backup::new(&src, &mut dst).unwrap();
backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
backup
.run_to_completion(5, Duration::from_millis(250), None)
.unwrap();
}
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
let the_answer: i64 = dst
.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(42 + 43, the_answer);
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_backup_temp() {
let src = Connection::open_in_memory().unwrap();
let sql = "BEGIN;
@ -340,34 +355,35 @@ mod test {
let mut dst = Connection::open_in_memory().unwrap();
{
let backup = Backup::new_with_names(&src,
DatabaseName::Temp,
&mut dst,
DatabaseName::Main)
.unwrap();
let backup =
Backup::new_with_names(&src, DatabaseName::Temp, &mut dst, DatabaseName::Main)
.unwrap();
backup.step(-1).unwrap();
}
let the_answer: i64 = dst.query_row("SELECT x FROM foo", &[], |r| r.get(0)).unwrap();
let the_answer: i64 = dst
.query_row("SELECT x FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(42, the_answer);
src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();
{
let backup = Backup::new_with_names(&src,
DatabaseName::Temp,
&mut dst,
DatabaseName::Main)
.unwrap();
backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
let backup =
Backup::new_with_names(&src, DatabaseName::Temp, &mut dst, DatabaseName::Main)
.unwrap();
backup
.run_to_completion(5, Duration::from_millis(250), None)
.unwrap();
}
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
let the_answer: i64 = dst
.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(42 + 43, the_answer);
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_backup_attached() {
let src = Connection::open_in_memory().unwrap();
let sql = "ATTACH DATABASE ':memory:' AS my_attached;
@ -380,29 +396,37 @@ mod test {
let mut dst = Connection::open_in_memory().unwrap();
{
let backup = Backup::new_with_names(&src,
DatabaseName::Attached("my_attached"),
&mut dst,
DatabaseName::Main)
.unwrap();
let backup = Backup::new_with_names(
&src,
DatabaseName::Attached("my_attached"),
&mut dst,
DatabaseName::Main,
).unwrap();
backup.step(-1).unwrap();
}
let the_answer: i64 = dst.query_row("SELECT x FROM foo", &[], |r| r.get(0)).unwrap();
let the_answer: i64 = dst
.query_row("SELECT x FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(42, the_answer);
src.execute_batch("INSERT INTO foo VALUES(43)").unwrap();
{
let backup = Backup::new_with_names(&src,
DatabaseName::Attached("my_attached"),
&mut dst,
DatabaseName::Main)
.unwrap();
backup.run_to_completion(5, Duration::from_millis(250), None).unwrap();
let backup = Backup::new_with_names(
&src,
DatabaseName::Attached("my_attached"),
&mut dst,
DatabaseName::Main,
).unwrap();
backup
.run_to_completion(5, Duration::from_millis(250), None)
.unwrap();
}
let the_answer: i64 = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap();
let the_answer: i64 = dst
.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(42 + 43, the_answer);
}
}

View File

@ -48,13 +48,13 @@
//! assert_eq!(blob.size(), 64);
//! }
//! ```
use std::io;
use std::cmp::min;
use std::io;
use std::ptr;
use super::ffi;
use super::types::{ToSql, ToSqlOutput};
use {Result, Connection, DatabaseName};
use {Connection, DatabaseName, Result};
/// Handle to an open BLOB.
pub struct Blob<'conn> {
@ -70,35 +70,35 @@ impl Connection {
///
/// Will return `Err` if `db`/`table`/`column` cannot be converted to a C-compatible string
/// or if the underlying SQLite BLOB open call fails.
pub fn blob_open<'a>(&'a self,
db: DatabaseName,
table: &str,
column: &str,
row_id: i64,
read_only: bool)
-> Result<Blob<'a>> {
pub fn blob_open<'a>(
&'a self,
db: DatabaseName,
table: &str,
column: &str,
row_id: i64,
read_only: bool,
) -> Result<Blob<'a>> {
let mut c = self.db.borrow_mut();
let mut blob = ptr::null_mut();
let db = try!(db.to_cstring());
let table = try!(super::str_to_cstring(table));
let column = try!(super::str_to_cstring(column));
let rc = unsafe {
ffi::sqlite3_blob_open(c.db(),
db.as_ptr(),
table.as_ptr(),
column.as_ptr(),
row_id,
if read_only { 0 } else { 1 },
&mut blob)
ffi::sqlite3_blob_open(
c.db(),
db.as_ptr(),
table.as_ptr(),
column.as_ptr(),
row_id,
if read_only { 0 } else { 1 },
&mut blob,
)
};
c.decode_result(rc)
.map(|_| {
Blob {
conn: self,
blob,
pos: 0,
}
})
c.decode_result(rc).map(|_| Blob {
conn: self,
blob,
pos: 0,
})
}
}
@ -154,15 +154,13 @@ impl<'conn> io::Read for Blob<'conn> {
if n <= 0 {
return Ok(0);
}
let rc =
unsafe { ffi::sqlite3_blob_read(self.blob, buf.as_ptr() as *mut _, n, self.pos) };
let rc = unsafe { ffi::sqlite3_blob_read(self.blob, buf.as_ptr() as *mut _, n, self.pos) };
self.conn
.decode_result(rc)
.map(|_| {
self.pos += n;
n as usize
})
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
self.pos += n;
n as usize
}).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
}
}
@ -183,16 +181,13 @@ impl<'conn> io::Write for Blob<'conn> {
if n <= 0 {
return Ok(0);
}
let rc = unsafe {
ffi::sqlite3_blob_write(self.blob, buf.as_ptr() as *mut _, n, self.pos)
};
let rc = unsafe { ffi::sqlite3_blob_write(self.blob, buf.as_ptr() as *mut _, n, self.pos) };
self.conn
.decode_result(rc)
.map(|_| {
self.pos += n;
n as usize
})
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
self.pos += n;
n as usize
}).map_err(|err| io::Error::new(io::ErrorKind::Other, err))
}
fn flush(&mut self) -> io::Result<()> {
@ -210,11 +205,15 @@ impl<'conn> io::Seek for Blob<'conn> {
};
if pos < 0 {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"invalid seek to negative position"))
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid seek to negative position",
))
} else if pos > i64::from(self.size()) {
Err(io::Error::new(io::ErrorKind::InvalidInput,
"invalid seek to position past end of blob"))
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid seek to position past end of blob",
))
} else {
self.pos = pos as i32;
Ok(pos as u64)
@ -235,7 +234,7 @@ impl<'conn> Drop for Blob<'conn> {
/// incremental BLOB I/O routines.
///
/// A negative value for the zeroblob results in a zero-length BLOB.
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct ZeroBlob(pub i32);
impl ToSql for ZeroBlob {
@ -247,10 +246,9 @@ impl ToSql for ZeroBlob {
#[cfg(test)]
mod test {
use std::io::{BufReader, BufRead, BufWriter, Read, Write, Seek, SeekFrom};
use std::io::{BufRead, BufReader, BufWriter, Read, Seek, SeekFrom, Write};
use {Connection, DatabaseName, Result};
#[cfg_attr(rustfmt, rustfmt_skip)]
fn db_with_test_blob() -> Result<(Connection, i64)> {
let db = try!(Connection::open_in_memory());
let sql = "BEGIN;
@ -266,7 +264,8 @@ mod test {
fn test_blob() {
let (db, rowid) = db_with_test_blob().unwrap();
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
let mut blob = db
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
assert_eq!(4, blob.write(b"Clob").unwrap());
assert_eq!(6, blob.write(b"567890xxxxxx").unwrap()); // cannot write past 10
@ -275,7 +274,8 @@ mod test {
blob.reopen(rowid).unwrap();
blob.close().unwrap();
blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, true)
blob = db
.blob_open(DatabaseName::Main, "test", "content", rowid, true)
.unwrap();
let mut bytes = [0u8; 5];
assert_eq!(5, blob.read(&mut bytes[..]).unwrap());
@ -316,7 +316,8 @@ mod test {
fn test_blob_in_bufreader() {
let (db, rowid) = db_with_test_blob().unwrap();
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
let mut blob = db
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
assert_eq!(8, blob.write(b"one\ntwo\n").unwrap());
@ -341,7 +342,8 @@ mod test {
let (db, rowid) = db_with_test_blob().unwrap();
{
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
let blob = db
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
let mut writer = BufWriter::new(blob);
@ -353,7 +355,8 @@ mod test {
{
// ... but it should've written the first 10 bytes
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
let mut blob = db
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
let mut bytes = [0u8; 10];
assert_eq!(10, blob.read(&mut bytes[..]).unwrap());
@ -361,7 +364,8 @@ mod test {
}
{
let blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
let blob = db
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
let mut writer = BufWriter::new(blob);
@ -372,7 +376,8 @@ mod test {
{
// ... but it should've written the first 10 bytes
let mut blob = db.blob_open(DatabaseName::Main, "test", "content", rowid, false)
let mut blob = db
.blob_open(DatabaseName::Main, "test", "content", rowid, false)
.unwrap();
let mut bytes = [0u8; 10];
assert_eq!(10, blob.read(&mut bytes[..]).unwrap());

View File

@ -1,8 +1,8 @@
///! Busy handler (when the database is locked)
use std::time::Duration;
use std::mem;
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::time::Duration;
use ffi;
use {Connection, InnerConnection, Result};
@ -67,8 +67,8 @@ mod test {
use self::tempdir::TempDir;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::sync_channel;
use std::time::Duration;
use std::thread;
use std::time::Duration;
use {Connection, Error, ErrorCode, TransactionBehavior};
@ -113,8 +113,8 @@ mod test {
});
assert_eq!(tx.recv().unwrap(), 1);
let _ =
db2.query_row("PRAGMA schema_version", &[], |row| {
let _ = db2
.query_row("PRAGMA schema_version", &[], |row| {
row.get_checked::<_, i32>(0)
}).expect("unexpected error");
@ -151,8 +151,8 @@ mod test {
});
assert_eq!(tx.recv().unwrap(), 1);
let _ =
db2.query_row("PRAGMA schema_version", &[], |row| {
let _ = db2
.query_row("PRAGMA schema_version", &[], |row| {
row.get_checked::<_, i32>(0)
}).expect("unexpected error");
assert_eq!(CALLED.load(Ordering::Relaxed), true);

View File

@ -1,10 +1,10 @@
//! Prepared statements cache for faster execution.
use lru_cache::LruCache;
use raw_statement::RawStatement;
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use lru_cache::LruCache;
use {Result, Connection, Statement};
use raw_statement::RawStatement;
use {Connection, Result, Statement};
impl Connection {
/// Prepare a SQL statement for execution, returning a previously prepared (but
@ -119,10 +119,11 @@ impl StatementCache {
//
// Will return `Err` if no cached statement can be found and the underlying SQLite prepare
// call fails.
fn get<'conn>(&'conn self,
conn: &'conn Connection,
sql: &str)
-> Result<CachedStatement<'conn>> {
fn get<'conn>(
&'conn self,
conn: &'conn Connection,
sql: &str,
) -> Result<CachedStatement<'conn>> {
let mut cache = self.0.borrow_mut();
let stmt = match cache.remove(sql.trim()) {
Some(raw_stmt) => Ok(Statement::new(conn, raw_stmt)),
@ -135,7 +136,9 @@ impl StatementCache {
fn cache_stmt(&self, stmt: RawStatement) {
let mut cache = self.0.borrow_mut();
stmt.clear_bindings();
let sql = String::from_utf8_lossy(stmt.sql().to_bytes()).trim().to_string();
let sql = String::from_utf8_lossy(stmt.sql().to_bytes())
.trim()
.to_string();
cache.insert(sql, stmt);
}
@ -147,8 +150,8 @@ impl StatementCache {
#[cfg(test)]
mod test {
use Connection;
use super::StatementCache;
use Connection;
impl StatementCache {
fn clear(&self) {
@ -242,46 +245,51 @@ mod test {
#[test]
fn test_ddl() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch(r#"
db.execute_batch(
r#"
CREATE TABLE foo (x INT);
INSERT INTO foo VALUES (1);
"#)
.unwrap();
"#,
).unwrap();
let sql = "SELECT * FROM foo";
{
let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!(1i32,
stmt.query_map::<i32, _>(&[], |r| r.get(0))
.unwrap()
.next()
.unwrap()
.unwrap());
assert_eq!(
1i32,
stmt.query_map::<i32, _>(&[], |r| r.get(0))
.unwrap()
.next()
.unwrap()
.unwrap()
);
}
db.execute_batch(r#"
db.execute_batch(
r#"
ALTER TABLE foo ADD COLUMN y INT;
UPDATE foo SET y = 2;
"#)
.unwrap();
"#,
).unwrap();
{
let mut stmt = db.prepare_cached(sql).unwrap();
assert_eq!((1i32, 2i32),
stmt.query_map(&[], |r| (r.get(0), r.get(1)))
.unwrap()
.next()
.unwrap()
.unwrap());
assert_eq!(
(1i32, 2i32),
stmt.query_map(&[], |r| (r.get(0), r.get(1)))
.unwrap()
.next()
.unwrap()
.unwrap()
);
}
}
#[test]
fn test_connection_close() {
let conn = Connection::open_in_memory().unwrap();
conn.prepare_cached("SELECT * FROM sqlite_master;")
.unwrap();
conn.prepare_cached("SELECT * FROM sqlite_master;").unwrap();
conn.close().expect("connection not closed");
}

View File

@ -1,10 +1,10 @@
use std::error;
use std::fmt;
use std::os::raw::c_int;
use std::path::PathBuf;
use std::str;
use std::os::raw::c_int;
use {ffi, errmsg_to_string};
use types::Type;
use {errmsg_to_string, ffi};
/// Old name for `Error`. `SqliteError` is deprecated.
#[deprecated(since = "0.6.0", note = "Use Error instead")]
@ -82,6 +82,9 @@ pub enum Error {
/// Error available for the implementors of the `ToSql` trait.
ToSqlConversionFailure(Box<error::Error + Send + Sync>),
/// Error when the SQL is not a `SELECT`, is not read-only.
InvalidQuery,
/// An error case available for implementors of custom modules (e.g.,
/// `create_module`).
#[cfg(feature = "vtab")]
@ -106,17 +109,15 @@ impl fmt::Display for Error {
match *self {
Error::SqliteFailure(ref err, None) => err.fmt(f),
Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s),
Error::SqliteSingleThreadedMode => {
write!(f,
"SQLite was compiled or configured for single-threaded use only")
}
Error::FromSqlConversionFailure(i, ref t, ref err) => {
write!(f,
"Conversion error from type {} at index: {}, {}",
t,
i,
err)
}
Error::SqliteSingleThreadedMode => write!(
f,
"SQLite was compiled or configured for single-threaded use only"
),
Error::FromSqlConversionFailure(i, ref t, ref err) => write!(
f,
"Conversion error from type {} at index: {}, {}",
t, i, err
),
Error::IntegralValueOutOfRange(col, val) => {
write!(f, "Integer {} out of range at index {}", val, col)
}
@ -146,6 +147,7 @@ impl fmt::Display for Error {
#[cfg(feature = "functions")]
Error::UserFunctionError(ref err) => err.fmt(f),
Error::ToSqlConversionFailure(ref err) => err.fmt(f),
Error::InvalidQuery => write!(f, "Query is not read-only"),
#[cfg(feature = "vtab")]
Error::ModuleError(ref desc) => write!(f, "{}", desc),
}
@ -157,14 +159,18 @@ impl error::Error for Error {
match *self {
Error::SqliteFailure(ref err, None) => err.description(),
Error::SqliteFailure(_, Some(ref s)) => s,
Error::SqliteSingleThreadedMode => "SQLite was compiled or configured for single-threaded use only",
Error::SqliteSingleThreadedMode => {
"SQLite was compiled or configured for single-threaded use only"
}
Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
Error::IntegralValueOutOfRange(_, _) => "integral value out of range of requested type",
Error::Utf8Error(ref err) => err.description(),
Error::InvalidParameterName(_) => "invalid parameter name",
Error::NulError(ref err) => err.description(),
Error::InvalidPath(_) => "invalid path",
Error::ExecuteReturnedResults => "execute returned results - did you mean to call query?",
Error::ExecuteReturnedResults => {
"execute returned results - did you mean to call query?"
}
Error::QueryReturnedNoRows => "query returned no rows",
Error::InvalidColumnIndex(_) => "invalid column index",
Error::InvalidColumnName(_) => "invalid column name",
@ -178,6 +184,7 @@ impl error::Error for Error {
#[cfg(feature = "functions")]
Error::UserFunctionError(ref err) => err.description(),
Error::ToSqlConversionFailure(ref err) => err.description(),
Error::InvalidQuery => "query is not read-only",
#[cfg(feature = "vtab")]
Error::ModuleError(ref desc) => desc,
}
@ -189,16 +196,17 @@ impl error::Error for Error {
Error::Utf8Error(ref err) => Some(err),
Error::NulError(ref err) => Some(err),
Error::IntegralValueOutOfRange(_, _) |
Error::SqliteSingleThreadedMode |
Error::InvalidParameterName(_) |
Error::ExecuteReturnedResults |
Error::QueryReturnedNoRows |
Error::InvalidColumnIndex(_) |
Error::InvalidColumnName(_) |
Error::InvalidColumnType(_, _) |
Error::InvalidPath(_) |
Error::StatementChangedRows(_) => None,
Error::IntegralValueOutOfRange(_, _)
| Error::SqliteSingleThreadedMode
| Error::InvalidParameterName(_)
| Error::ExecuteReturnedResults
| Error::QueryReturnedNoRows
| Error::InvalidColumnIndex(_)
| Error::InvalidColumnName(_)
| Error::InvalidColumnType(_, _)
| Error::InvalidPath(_)
| Error::StatementChangedRows(_)
| Error::InvalidQuery => None,
#[cfg(feature = "functions")]
Error::InvalidFunctionParameterType(_, _) => None,
@ -208,8 +216,9 @@ impl error::Error for Error {
#[cfg(feature = "functions")]
Error::UserFunctionError(ref err) => Some(&**err),
Error::FromSqlConversionFailure(_, _, ref err) |
Error::ToSqlConversionFailure(ref err) => Some(&**err),
Error::FromSqlConversionFailure(_, _, ref err)
| Error::ToSqlConversionFailure(ref err) => Some(&**err),
#[cfg(feature = "vtab")]
Error::ModuleError(_) => None,
}

View File

@ -50,18 +50,18 @@
//! }
//! ```
use std::error::Error as StdError;
use std::os::raw::{c_int, c_void};
use std::ptr;
use std::slice;
use std::os::raw::{c_int, c_void};
use ffi;
use ffi::sqlite3_context;
use ffi::sqlite3_value;
use context::{set_result};
use types::{ToSql, FromSql, FromSqlError, ValueRef};
use context::set_result;
use types::{FromSql, FromSqlError, ToSql, ValueRef};
use {Result, Error, Connection, str_to_cstring, InnerConnection};
use {str_to_cstring, Connection, Error, InnerConnection, Result};
unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
// Extended constraint error codes were added in SQLite 3.7.16. We don't have an explicit
@ -94,7 +94,7 @@ unsafe fn report_error(ctx: *mut sqlite3_context, err: &Error) {
}
unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
let _: Box<T> = Box::from_raw(p as *mut T);
drop(Box::from_raw(p as *mut T));
}
/// Context is a wrapper for the SQLite function evaluation context.
@ -124,17 +124,14 @@ impl<'a> Context<'a> {
let arg = self.args[idx];
let value = unsafe { ValueRef::from_value(arg) };
FromSql::column_result(value).map_err(|err| match err {
FromSqlError::InvalidType => {
FromSqlError::InvalidType => {
Error::InvalidFunctionParameterType(idx, value.data_type())
}
FromSqlError::OutOfRange(i) => {
Error::IntegralValueOutOfRange(idx,
i)
}
FromSqlError::Other(err) => {
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx, value.data_type(), err)
}
})
})
}
/// Sets the auxilliary data associated with a particular parameter. See
@ -143,10 +140,12 @@ impl<'a> Context<'a> {
pub fn set_aux<T>(&self, arg: c_int, value: T) {
let boxed = Box::into_raw(Box::new(value));
unsafe {
ffi::sqlite3_set_auxdata(self.ctx,
arg,
boxed as *mut c_void,
Some(free_boxed_value::<T>))
ffi::sqlite3_set_auxdata(
self.ctx,
arg,
boxed as *mut c_void,
Some(free_boxed_value::<T>),
)
};
}
@ -160,7 +159,11 @@ impl<'a> Context<'a> {
/// types must be identical.
pub unsafe fn get_aux<T>(&self, arg: c_int) -> Option<&T> {
let p = ffi::sqlite3_get_auxdata(self.ctx, arg) as *mut T;
if p.is_null() { None } else { Some(&*p) }
if p.is_null() {
None
} else {
Some(&*p)
}
}
}
@ -169,7 +172,8 @@ impl<'a> Context<'a> {
/// `A` is the type of the aggregation context and `T` is the type of the final result.
/// Implementations should be stateless.
pub trait Aggregate<A, T>
where T: ToSql
where
T: ToSql,
{
/// Initializes the aggregation context. Will be called prior to the first call
/// to `step()` to set up the context for an invocation of the function. (Note:
@ -218,14 +222,16 @@ impl Connection {
/// # Failure
///
/// Will return Err if the function could not be attached to the connection.
pub fn create_scalar_function<F, T>(&self,
fn_name: &str,
n_arg: c_int,
deterministic: bool,
x_func: F)
-> Result<()>
where F: FnMut(&Context) -> Result<T>,
T: ToSql
pub fn create_scalar_function<F, T>(
&self,
fn_name: &str,
n_arg: c_int,
deterministic: bool,
x_func: F,
) -> Result<()>
where
F: FnMut(&Context) -> Result<T>,
T: ToSql,
{
self.db
.borrow_mut()
@ -237,14 +243,16 @@ impl Connection {
/// # Failure
///
/// Will return Err if the function could not be attached to the connection.
pub fn create_aggregate_function<A, D, T>(&self,
fn_name: &str,
n_arg: c_int,
deterministic: bool,
aggr: D)
-> Result<()>
where D: Aggregate<A, T>,
T: ToSql
pub fn create_aggregate_function<A, D, T>(
&self,
fn_name: &str,
n_arg: c_int,
deterministic: bool,
aggr: D,
) -> Result<()>
where
D: Aggregate<A, T>,
T: ToSql,
{
self.db
.borrow_mut()
@ -265,20 +273,24 @@ impl Connection {
}
impl InnerConnection {
fn create_scalar_function<F, T>(&mut self,
fn_name: &str,
n_arg: c_int,
deterministic: bool,
x_func: F)
-> Result<()>
where F: FnMut(&Context) -> Result<T>,
T: ToSql
fn create_scalar_function<F, T>(
&mut self,
fn_name: &str,
n_arg: c_int,
deterministic: bool,
x_func: F,
) -> Result<()>
where
F: FnMut(&Context) -> Result<T>,
T: ToSql,
{
unsafe extern "C" fn call_boxed_closure<F, T>(ctx: *mut sqlite3_context,
argc: c_int,
argv: *mut *mut sqlite3_value)
where F: FnMut(&Context) -> Result<T>,
T: ToSql
unsafe extern "C" fn call_boxed_closure<F, T>(
ctx: *mut sqlite3_context,
argc: c_int,
argv: *mut *mut sqlite3_value,
) where
F: FnMut(&Context) -> Result<T>,
T: ToSql,
{
let ctx = Context {
ctx,
@ -304,31 +316,36 @@ impl InnerConnection {
flags |= ffi::SQLITE_DETERMINISTIC;
}
let r = unsafe {
ffi::sqlite3_create_function_v2(self.db(),
c_name.as_ptr(),
n_arg,
flags,
boxed_f as *mut c_void,
Some(call_boxed_closure::<F, T>),
None,
None,
Some(free_boxed_value::<F>))
ffi::sqlite3_create_function_v2(
self.db(),
c_name.as_ptr(),
n_arg,
flags,
boxed_f as *mut c_void,
Some(call_boxed_closure::<F, T>),
None,
None,
Some(free_boxed_value::<F>),
)
};
self.decode_result(r)
}
fn create_aggregate_function<A, D, T>(&mut self,
fn_name: &str,
n_arg: c_int,
deterministic: bool,
aggr: D)
-> Result<()>
where D: Aggregate<A, T>,
T: ToSql
fn create_aggregate_function<A, D, T>(
&mut self,
fn_name: &str,
n_arg: c_int,
deterministic: bool,
aggr: D,
) -> Result<()>
where
D: Aggregate<A, T>,
T: ToSql,
{
unsafe fn aggregate_context<A>(ctx: *mut sqlite3_context,
bytes: usize)
-> Option<*mut *mut A> {
unsafe fn aggregate_context<A>(
ctx: *mut sqlite3_context,
bytes: usize,
) -> Option<*mut *mut A> {
let pac = ffi::sqlite3_aggregate_context(ctx, bytes as c_int) as *mut *mut A;
if pac.is_null() {
return None;
@ -336,15 +353,19 @@ impl InnerConnection {
Some(pac)
}
unsafe extern "C" fn call_boxed_step<A, D, T>(ctx: *mut sqlite3_context,
argc: c_int,
argv: *mut *mut sqlite3_value)
where D: Aggregate<A, T>,
T: ToSql
unsafe extern "C" fn call_boxed_step<A, D, T>(
ctx: *mut sqlite3_context,
argc: c_int,
argv: *mut *mut sqlite3_value,
) where
D: Aggregate<A, T>,
T: ToSql,
{
let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D;
assert!(!boxed_aggr.is_null(),
"Internal error - null aggregate pointer");
assert!(
!boxed_aggr.is_null(),
"Internal error - null aggregate pointer"
);
let pac = match aggregate_context(ctx, ::std::mem::size_of::<*mut A>()) {
Some(pac) => pac,
@ -370,12 +391,15 @@ impl InnerConnection {
}
unsafe extern "C" fn call_boxed_final<A, D, T>(ctx: *mut sqlite3_context)
where D: Aggregate<A, T>,
T: ToSql
where
D: Aggregate<A, T>,
T: ToSql,
{
let boxed_aggr: *mut D = ffi::sqlite3_user_data(ctx) as *mut D;
assert!(!boxed_aggr.is_null(),
"Internal error - null aggregate pointer");
assert!(
!boxed_aggr.is_null(),
"Internal error - null aggregate pointer"
);
// Within the xFinal callback, it is customary to set N=0 in calls to
// sqlite3_aggregate_context(C,N) so that no pointless memory allocations occur.
@ -407,15 +431,17 @@ impl InnerConnection {
flags |= ffi::SQLITE_DETERMINISTIC;
}
let r = unsafe {
ffi::sqlite3_create_function_v2(self.db(),
c_name.as_ptr(),
n_arg,
flags,
boxed_aggr as *mut c_void,
None,
Some(call_boxed_step::<A, D, T>),
Some(call_boxed_final::<A, D, T>),
Some(free_boxed_value::<D>))
ffi::sqlite3_create_function_v2(
self.db(),
c_name.as_ptr(),
n_arg,
flags,
boxed_aggr as *mut c_void,
None,
Some(call_boxed_step::<A, D, T>),
Some(call_boxed_final::<A, D, T>),
Some(free_boxed_value::<D>),
)
};
self.decode_result(r)
}
@ -423,15 +449,17 @@ impl InnerConnection {
fn remove_function(&mut self, fn_name: &str, n_arg: c_int) -> Result<()> {
let c_name = try!(str_to_cstring(fn_name));
let r = unsafe {
ffi::sqlite3_create_function_v2(self.db(),
c_name.as_ptr(),
n_arg,
ffi::SQLITE_UTF8,
ptr::null_mut(),
None,
None,
None,
None)
ffi::sqlite3_create_function_v2(
self.db(),
c_name.as_ptr(),
n_arg,
ffi::SQLITE_UTF8,
ptr::null_mut(),
None,
None,
None,
None,
)
};
self.decode_result(r)
}
@ -441,13 +469,13 @@ impl InnerConnection {
mod test {
extern crate regex;
use std::collections::HashMap;
use std::os::raw::c_double;
use self::regex::Regex;
use std::collections::HashMap;
use std::f64::EPSILON;
use std::os::raw::c_double;
use {Connection, Error, Result};
use functions::{Aggregate, Context};
use {Connection, Error, Result};
fn half(ctx: &Context) -> Result<c_double> {
assert!(ctx.len() == 1, "called with unexpected number of arguments");
@ -509,41 +537,44 @@ mod test {
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_function_regexp_with_auxilliary() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("BEGIN;
CREATE TABLE foo (x string);
INSERT INTO foo VALUES ('lisa');
INSERT INTO foo VALUES ('lXsi');
INSERT INTO foo VALUES ('lisX');
END;").unwrap();
db.create_scalar_function("regexp", 2, true, regexp_with_auxilliary).unwrap();
db.execute_batch(
"BEGIN;
CREATE TABLE foo (x string);
INSERT INTO foo VALUES ('lisa');
INSERT INTO foo VALUES ('lXsi');
INSERT INTO foo VALUES ('lisX');
END;",
).unwrap();
db.create_scalar_function("regexp", 2, true, regexp_with_auxilliary)
.unwrap();
let result: Result<bool> = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')",
&[],
|r| r.get(0));
let result: Result<bool> =
db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", &[], |r| r.get(0));
assert_eq!(true, result.unwrap());
let result: Result<i64> =
db.query_row("SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
&[],
|r| r.get(0));
let result: Result<i64> = db.query_row(
"SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
&[],
|r| r.get(0),
);
assert_eq!(2, result.unwrap());
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_function_regexp_with_hashmap_cache() {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("BEGIN;
CREATE TABLE foo (x string);
INSERT INTO foo VALUES ('lisa');
INSERT INTO foo VALUES ('lXsi');
INSERT INTO foo VALUES ('lisX');
END;").unwrap();
db.execute_batch(
"BEGIN;
CREATE TABLE foo (x string);
INSERT INTO foo VALUES ('lisa');
INSERT INTO foo VALUES ('lXsi');
INSERT INTO foo VALUES ('lisX');
END;",
).unwrap();
// This implementation of a regexp scalar function uses a captured HashMap
// to keep cached regular expressions around (even across multiple queries)
@ -558,12 +589,10 @@ mod test {
use std::collections::hash_map::Entry::{Occupied, Vacant};
match entry {
Occupied(occ) => occ.into_mut(),
Vacant(vac) => {
match Regex::new(&regex_s) {
Ok(r) => vac.insert(r),
Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
}
}
Vacant(vac) => match Regex::new(&regex_s) {
Ok(r) => vac.insert(r),
Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
},
}
};
@ -571,16 +600,16 @@ mod test {
Ok(regex.is_match(&text))
}).unwrap();
let result: Result<bool> = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')",
&[],
|r| r.get(0));
let result: Result<bool> =
db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", &[], |r| r.get(0));
assert_eq!(true, result.unwrap());
let result: Result<i64> =
db.query_row("SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
&[],
|r| r.get(0));
let result: Result<i64> = db.query_row(
"SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1",
&[],
|r| r.get(0),
);
assert_eq!(2, result.unwrap());
}
@ -589,21 +618,21 @@ mod test {
fn test_varargs_function() {
let db = Connection::open_in_memory().unwrap();
db.create_scalar_function("my_concat", -1, true, |ctx| {
let mut ret = String::new();
let mut ret = String::new();
for idx in 0..ctx.len() {
let s = try!(ctx.get::<String>(idx));
ret.push_str(&s);
}
for idx in 0..ctx.len() {
let s = try!(ctx.get::<String>(idx));
ret.push_str(&s);
}
Ok(ret)
})
.unwrap();
Ok(ret)
}).unwrap();
for &(expected, query) in
&[("", "SELECT my_concat()"),
("onetwo", "SELECT my_concat('one', 'two')"),
("abc", "SELECT my_concat('a', 'b', 'c')")] {
for &(expected, query) in &[
("", "SELECT my_concat()"),
("onetwo", "SELECT my_concat('one', 'two')"),
("abc", "SELECT my_concat('a', 'b', 'c')"),
] {
let result: String = db.query_row(query, &[], |r| r.get(0)).unwrap();
assert_eq!(expected, result);
}
@ -659,7 +688,8 @@ mod test {
let dual_sum = "SELECT my_sum(i), my_sum(j) FROM (SELECT 2 AS i, 1 AS j UNION ALL SELECT \
2, 1)";
let result: (i64, i64) = db.query_row(dual_sum, &[], |r| (r.get(0), r.get(1)))
let result: (i64, i64) = db
.query_row(dual_sum, &[], |r| (r.get(0), r.get(1)))
.unwrap();
assert_eq!((4, 2), result);
}

View File

@ -1,8 +1,8 @@
//! Commit, Data Change and Rollback Notification Callbacks
#![allow(non_camel_case_types)]
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use std::os::raw::{c_int, c_char, c_void};
use ffi;
@ -94,8 +94,9 @@ impl Connection {
/// Register a callback function to be invoked whenever a transaction is committed.
///
/// The callback returns `true` to rollback.
pub fn commit_hook<F>(&self, hook: F)
where F: FnMut() -> bool
pub fn commit_hook<F>(&self, hook: Option<F>)
where
F: FnMut() -> bool,
{
self.db.borrow_mut().commit_hook(hook);
}
@ -103,8 +104,9 @@ impl Connection {
/// Register a callback function to be invoked whenever a transaction is committed.
///
/// The callback returns `true` to rollback.
pub fn rollback_hook<F>(&self, hook: F)
where F: FnMut()
pub fn rollback_hook<F>(&self, hook: Option<F>)
where
F: FnMut(),
{
self.db.borrow_mut().rollback_hook(hook);
}
@ -118,99 +120,122 @@ impl Connection {
/// - the name of the database ("main", "temp", ...),
/// - the name of the table that is updated,
/// - the ROWID of the row that is updated.
pub fn update_hook<F>(&self, hook: F)
where F: FnMut(Action, &str, &str, i64)
pub fn update_hook<F>(&self, hook: Option<F>)
where
F: FnMut(Action, &str, &str, i64),
{
self.db.borrow_mut().update_hook(hook);
}
/// Remove hook installed by `update_hook`.
pub fn remove_update_hook(&self) {
self.db.borrow_mut().remove_update_hook();
}
/// Remove hook installed by `commit_hook`.
pub fn remove_commit_hook(&self) {
self.db.borrow_mut().remove_commit_hook();
}
/// Remove hook installed by `rollback_hook`.
pub fn remove_rollback_hook(&self) {
self.db.borrow_mut().remove_rollback_hook();
}
}
impl InnerConnection {
pub fn remove_hooks(&mut self) {
self.remove_update_hook();
self.remove_commit_hook();
self.remove_rollback_hook();
self.update_hook(None::<fn(Action, &str, &str, i64)>);
self.commit_hook(None::<fn() -> bool>);
self.rollback_hook(None::<fn()>);
}
fn commit_hook<F>(&self, hook: F)
where F: FnMut() -> bool
fn commit_hook<F>(&mut self, hook: Option<F>)
where
F: FnMut() -> bool,
{
unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void) -> c_int
where F: FnMut() -> bool
where
F: FnMut() -> bool,
{
let boxed_hook: *mut F = p_arg as *mut F;
assert!(!boxed_hook.is_null(),
"Internal error - null function pointer");
if (*boxed_hook)() { 1 } else { 0 }
if (*boxed_hook)() {
1
} else {
0
}
}
let previous_hook = {
let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
unsafe {
ffi::sqlite3_commit_hook(self.db(),
Some(call_boxed_closure::<F>),
boxed_hook as *mut _)
}
// unlike `sqlite3_create_function_v2`, we cannot specify a `xDestroy` with `sqlite3_commit_hook`.
// so we keep the `xDestroy` function in `InnerConnection.free_boxed_hook`.
let free_commit_hook = if hook.is_some() {
Some(free_boxed_hook::<F> as fn(*mut c_void))
} else {
None
};
free_boxed_hook(previous_hook);
let previous_hook = match hook {
Some(hook) => {
let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
unsafe {
ffi::sqlite3_commit_hook(
self.db(),
Some(call_boxed_closure::<F>),
boxed_hook as *mut _,
)
}
}
_ => unsafe { ffi::sqlite3_commit_hook(self.db(), None, ptr::null_mut()) },
};
if !previous_hook.is_null() {
if let Some(free_boxed_hook) = self.free_commit_hook {
free_boxed_hook(previous_hook);
}
}
self.free_commit_hook = free_commit_hook;
}
fn rollback_hook<F>(&self, hook: F)
where F: FnMut()
fn rollback_hook<F>(&mut self, hook: Option<F>)
where
F: FnMut(),
{
unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void)
where F: FnMut()
where
F: FnMut(),
{
let boxed_hook: *mut F = p_arg as *mut F;
assert!(!boxed_hook.is_null(),
"Internal error - null function pointer");
(*boxed_hook)();
}
let previous_hook = {
let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
unsafe {
ffi::sqlite3_rollback_hook(self.db(),
Some(call_boxed_closure::<F>),
boxed_hook as *mut _)
}
let free_rollback_hook = if hook.is_some() {
Some(free_boxed_hook::<F> as fn(*mut c_void))
} else {
None
};
free_boxed_hook(previous_hook);
let previous_hook = match hook {
Some(hook) => {
let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
unsafe {
ffi::sqlite3_rollback_hook(
self.db(),
Some(call_boxed_closure::<F>),
boxed_hook as *mut _,
)
}
}
_ => unsafe { ffi::sqlite3_rollback_hook(self.db(), None, ptr::null_mut()) },
};
if !previous_hook.is_null() {
if let Some(free_boxed_hook) = self.free_rollback_hook {
free_boxed_hook(previous_hook);
}
}
self.free_rollback_hook = free_rollback_hook;
}
fn update_hook<F>(&mut self, hook: F)
where F: FnMut(Action, &str, &str, i64)
fn update_hook<F>(&mut self, hook: Option<F>)
where
F: FnMut(Action, &str, &str, i64),
{
unsafe extern "C" fn call_boxed_closure<F>(p_arg: *mut c_void,
action_code: c_int,
db_str: *const c_char,
tbl_str: *const c_char,
row_id: i64)
where F: FnMut(Action, &str, &str, i64)
unsafe extern "C" fn call_boxed_closure<F>(
p_arg: *mut c_void,
action_code: c_int,
db_str: *const c_char,
tbl_str: *const c_char,
row_id: i64,
) where
F: FnMut(Action, &str, &str, i64),
{
use std::ffi::CStr;
use std::str;
let boxed_hook: *mut F = p_arg as *mut F;
assert!(!boxed_hook.is_null(),
"Internal error - null function pointer");
let action = Action::from(action_code);
let db_name = {
@ -225,38 +250,36 @@ impl InnerConnection {
(*boxed_hook)(action, db_name, tbl_name, row_id);
}
let previous_hook = {
let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
unsafe {
ffi::sqlite3_update_hook(self.db(),
Some(call_boxed_closure::<F>),
boxed_hook as *mut _)
}
let free_update_hook = if hook.is_some() {
Some(free_boxed_hook::<F> as fn(*mut c_void))
} else {
None
};
free_boxed_hook(previous_hook);
}
fn remove_update_hook(&mut self) {
let previous_hook = unsafe { ffi::sqlite3_update_hook(self.db(), None, ptr::null_mut()) };
free_boxed_hook(previous_hook);
}
fn remove_commit_hook(&mut self) {
let previous_hook = unsafe { ffi::sqlite3_commit_hook(self.db(), None, ptr::null_mut()) };
free_boxed_hook(previous_hook);
}
fn remove_rollback_hook(&mut self) {
let previous_hook = unsafe { ffi::sqlite3_rollback_hook(self.db(), None, ptr::null_mut()) };
free_boxed_hook(previous_hook);
let previous_hook = match hook {
Some(hook) => {
let boxed_hook: *mut F = Box::into_raw(Box::new(hook));
unsafe {
ffi::sqlite3_update_hook(
self.db(),
Some(call_boxed_closure::<F>),
boxed_hook as *mut _,
)
}
}
_ => unsafe { ffi::sqlite3_update_hook(self.db(), None, ptr::null_mut()) },
};
if !previous_hook.is_null() {
if let Some(free_boxed_hook) = self.free_update_hook {
free_boxed_hook(previous_hook);
}
}
self.free_update_hook = free_update_hook;
}
}
fn free_boxed_hook(hook: *mut c_void) {
if !hook.is_null() {
// TODO make sure that size_of::<*mut F>() is always equal to size_of::<*mut c_void>()
let _: Box<*mut c_void> = unsafe { Box::from_raw(hook as *mut _) };
}
fn free_boxed_hook<F>(p: *mut c_void) {
drop(unsafe { Box::from_raw(p as *mut F) });
}
#[cfg(test)]
@ -269,21 +292,36 @@ mod test {
let db = Connection::open_in_memory().unwrap();
let mut called = false;
db.commit_hook(|| {
called = true;
false
});
db.commit_hook(Some(|| {
called = true;
false
}));
db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;")
.unwrap();
assert!(called);
}
#[test]
fn test_fn_commit_hook() {
let db = Connection::open_in_memory().unwrap();
fn hook() -> bool {
true
}
db.commit_hook(Some(hook));
db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); COMMIT;")
.unwrap_err();
}
#[test]
fn test_rollback_hook() {
let db = Connection::open_in_memory().unwrap();
let mut called = false;
db.rollback_hook(|| { called = true; });
db.rollback_hook(Some(|| {
called = true;
}));
db.execute_batch("BEGIN; CREATE TABLE foo (t TEXT); ROLLBACK;")
.unwrap();
assert!(called);
@ -294,13 +332,13 @@ mod test {
let db = Connection::open_in_memory().unwrap();
let mut called = false;
db.update_hook(|action, db, tbl, row_id| {
assert_eq!(Action::SQLITE_INSERT, action);
assert_eq!("main", db);
assert_eq!("foo", tbl);
assert_eq!(1, row_id);
called = true;
});
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 = true;
}));
db.execute_batch("CREATE TABLE foo (t TEXT)").unwrap();
db.execute_batch("INSERT INTO foo VALUES ('lisa')").unwrap();
assert!(called);

View File

@ -60,75 +60,76 @@ extern crate bitflags;
#[macro_use]
extern crate lazy_static;
use std::default::Default;
use std::convert;
use std::mem;
use std::ptr;
use std::fmt;
use std::path::{Path, PathBuf};
use std::cell::RefCell;
use std::convert;
use std::default::Default;
use std::ffi::{CStr, CString};
use std::fmt;
use std::mem;
use std::os::raw::{c_char, c_int};
use std::path::{Path, PathBuf};
use std::ptr;
use std::result;
use std::str;
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
use std::sync::{Once, ONCE_INIT};
use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
use std::os::raw::{c_int, c_char};
use types::{ToSql, ValueRef};
use error::{error_from_sqlite_code, error_from_handle};
use raw_statement::RawStatement;
use cache::StatementCache;
use error::{error_from_handle, error_from_sqlite_code};
use raw_statement::RawStatement;
use types::{ToSql, ValueRef};
pub use statement::Statement;
pub use row::{Row, Rows, MappedRows, AndThenRows, RowIndex};
pub use row::{AndThenRows, MappedRows, Row, RowIndex, Rows};
pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
#[allow(deprecated)]
pub use transaction::{SqliteTransaction, SqliteTransactionBehavior};
pub use transaction::{DropBehavior, Savepoint, Transaction, TransactionBehavior};
pub use error::Error;
#[allow(deprecated)]
pub use error::SqliteError;
pub use error::Error;
pub use ffi::ErrorCode;
pub use cache::CachedStatement;
pub use version::*;
#[cfg(feature = "hooks")]
pub use hooks::*;
#[cfg(feature = "load_extension")]
#[allow(deprecated)]
pub use load_extension_guard::{SqliteLoadExtensionGuard, LoadExtensionGuard};
pub use load_extension_guard::{LoadExtensionGuard, SqliteLoadExtensionGuard};
pub mod types;
mod version;
mod transaction;
#[cfg(feature = "backup")]
pub mod backup;
#[cfg(feature = "blob")]
pub mod blob;
mod busy;
mod cache;
#[cfg(any(feature = "functions", feature = "vtab"))]
mod context;
mod error;
#[cfg(feature = "functions")]
pub mod functions;
#[cfg(feature = "hooks")]
mod hooks;
#[cfg(feature = "limits")]
pub mod limits;
#[cfg(feature = "load_extension")]
mod load_extension_guard;
mod raw_statement;
mod row;
mod statement;
#[cfg(feature = "load_extension")]
mod load_extension_guard;
#[cfg(feature = "trace")]
pub mod trace;
#[cfg(feature = "backup")]
pub mod backup;
#[cfg(feature = "functions")]
pub mod functions;
#[cfg(feature = "blob")]
pub mod blob;
#[cfg(feature = "limits")]
pub mod limits;
#[cfg(feature = "hooks")]
mod hooks;
#[cfg(feature = "hooks")]
pub use hooks::*;
mod transaction;
pub mod types;
mod unlock_notify;
mod busy;
mod version;
#[cfg(feature = "vtab")]
pub mod vtab;
#[cfg(any(feature = "functions", feature = "vtab"))]
mod context;
// Number of cached prepared statements we'll hold on to.
const STATEMENT_CACHE_DEFAULT_CAPACITY: usize = 16;
@ -155,7 +156,7 @@ fn path_to_cstring(p: &Path) -> Result<CString> {
}
/// Name for a database within a SQLite connection.
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub enum DatabaseName<'a> {
/// The main database.
Main,
@ -172,7 +173,7 @@ pub enum DatabaseName<'a> {
#[cfg(any(feature = "backup", feature = "blob"))]
impl<'a> DatabaseName<'a> {
fn to_cstring(&self) -> Result<CString> {
use self::DatabaseName::{Main, Temp, Attached};
use self::DatabaseName::{Attached, Main, Temp};
match *self {
Main => str_to_cstring("main"),
Temp => str_to_cstring("temp"),
@ -236,13 +237,11 @@ impl Connection {
/// underlying SQLite open call fails.
pub fn open_with_flags<P: AsRef<Path>>(path: P, flags: OpenFlags) -> Result<Connection> {
let c_path = try!(path_to_cstring(path.as_ref()));
InnerConnection::open_with_flags(&c_path, flags).map(|db| {
Connection {
db: RefCell::new(db),
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
path: Some(path.as_ref().to_path_buf()),
}
})
InnerConnection::open_with_flags(&c_path, flags).map(|db| Connection {
db: RefCell::new(db),
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
path: Some(path.as_ref().to_path_buf()),
})
}
/// Open a new connection to an in-memory SQLite database.
@ -255,13 +254,11 @@ impl Connection {
/// Will return `Err` if the underlying SQLite open call fails.
pub fn open_in_memory_with_flags(flags: OpenFlags) -> Result<Connection> {
let c_memory = try!(str_to_cstring(":memory:"));
InnerConnection::open_with_flags(&c_memory, flags).map(|db| {
Connection {
db: RefCell::new(db),
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
path: None,
}
})
InnerConnection::open_with_flags(&c_memory, flags).map(|db| Connection {
db: RefCell::new(db),
cache: StatementCache::with_capacity(STATEMENT_CACHE_DEFAULT_CAPACITY),
path: None,
})
}
/// Convenience method to run multiple SQL statements (that cannot take any parameters).
@ -310,8 +307,7 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails.
pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result<usize> {
self.prepare(sql)
.and_then(|mut stmt| stmt.execute(params))
self.prepare(sql).and_then(|mut stmt| stmt.execute(params))
}
/// Convenience method to prepare and execute a single SQL statement with named parameter(s).
@ -365,7 +361,8 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails.
pub fn query_row<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
where
F: FnOnce(&Row) -> T,
{
let mut stmt = try!(self.prepare(sql));
stmt.query_row(params, f)
@ -381,7 +378,8 @@ impl Connection {
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails.
pub fn query_row_named<T, F>(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
where
F: FnOnce(&Row) -> T,
{
let mut stmt = try!(self.prepare(sql));
let mut rows = try!(stmt.query_named(params));
@ -412,20 +410,20 @@ impl Connection {
///
/// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
/// underlying SQLite call fails.
pub fn query_row_and_then<T, E, F>(&self,
sql: &str,
params: &[&ToSql],
f: F)
-> result::Result<T, E>
where F: FnOnce(&Row) -> result::Result<T, E>,
E: convert::From<Error>
pub fn query_row_and_then<T, E, F>(
&self,
sql: &str,
params: &[&ToSql],
f: F,
) -> result::Result<T, E>
where
F: FnOnce(&Row) -> result::Result<T, E>,
E: convert::From<Error>,
{
let mut stmt = try!(self.prepare(sql));
let mut rows = try!(stmt.query(params));
rows.get_expected_row()
.map_err(E::from)
.and_then(|r| f(&r))
rows.get_expected_row().map_err(E::from).and_then(|r| f(&r))
}
/// Convenience method to execute a query that is expected to return a single row.
@ -449,7 +447,8 @@ impl Connection {
/// does exactly the same thing.
#[deprecated(since = "0.1.0", note = "Use query_row instead")]
pub fn query_row_safe<T, F>(&self, sql: &str, params: &[&ToSql], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
where
F: FnOnce(&Row) -> T,
{
self.query_row(sql, params, f)
}
@ -549,10 +548,11 @@ impl Connection {
///
/// Will return `Err` if the underlying SQLite call fails.
#[cfg(feature = "load_extension")]
pub fn load_extension<P: AsRef<Path>>(&self,
dylib_path: P,
entry_point: Option<&str>)
-> Result<()> {
pub fn load_extension<P: AsRef<Path>>(
&self,
dylib_path: P,
entry_point: Option<&str>,
) -> Result<()> {
self.db
.borrow_mut()
.load_extension(dylib_path.as_ref(), entry_point)
@ -601,6 +601,12 @@ impl fmt::Debug for Connection {
struct InnerConnection {
db: *mut ffi::sqlite3,
#[cfg(feature = "hooks")]
free_commit_hook: Option<fn(*mut ::std::os::raw::c_void)>,
#[cfg(feature = "hooks")]
free_rollback_hook: Option<fn(*mut ::std::os::raw::c_void)>,
#[cfg(feature = "hooks")]
free_update_hook: Option<fn(*mut ::std::os::raw::c_void)>,
}
/// Old name for `OpenFlags`. `SqliteOpenFlags` is deprecated.
@ -626,8 +632,10 @@ bitflags! {
impl Default for OpenFlags {
fn default() -> OpenFlags {
OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE |
OpenFlags::SQLITE_OPEN_NO_MUTEX | OpenFlags::SQLITE_OPEN_URI
OpenFlags::SQLITE_OPEN_READ_WRITE
| OpenFlags::SQLITE_OPEN_CREATE
| OpenFlags::SQLITE_OPEN_NO_MUTEX
| OpenFlags::SQLITE_OPEN_URI
}
}
@ -674,9 +682,11 @@ fn ensure_valid_sqlite_version() {
let buildtime_major = ffi::SQLITE_VERSION_NUMBER / 1_000_000;
let runtime_major = version_number / 1_000_000;
if buildtime_major != runtime_major {
panic!("rusqlite was built against SQLite {} but is running with SQLite {}",
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
version());
panic!(
"rusqlite was built against SQLite {} but is running with SQLite {}",
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
version()
);
}
if BYPASS_VERSION_CHECK.load(Ordering::Relaxed) {
@ -686,14 +696,16 @@ fn ensure_valid_sqlite_version() {
// Check that the runtime version number is compatible with the version number we found at
// build-time.
if version_number < ffi::SQLITE_VERSION_NUMBER {
panic!("\
panic!(
"\
rusqlite was built against SQLite {} but the runtime SQLite version is {}. To fix this, either:
* Recompile rusqlite and link against the SQLite version you are using at runtime, or
* Call rusqlite::bypass_sqlite_version_check() prior to your first connection attempt. Doing this
means you're sure everything will work correctly even though the runtime version is older than
the version we found at build time.",
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
version());
str::from_utf8(ffi::SQLITE_VERSION).unwrap(),
version()
);
}
});
}
@ -759,6 +771,20 @@ To fix this, either:
}
impl InnerConnection {
#[cfg(not(feature = "hooks"))]
fn new(db: *mut ffi::sqlite3) -> InnerConnection {
InnerConnection { db }
}
#[cfg(feature = "hooks")]
fn new(db: *mut ffi::sqlite3) -> InnerConnection {
InnerConnection {
db,
free_commit_hook: None,
free_rollback_hook: None,
free_update_hook: None,
}
}
fn open_with_flags(c_path: &CString, flags: OpenFlags) -> Result<InnerConnection> {
ensure_valid_sqlite_version();
ensure_safe_sqlite_threading_mode()?;
@ -767,9 +793,15 @@ impl InnerConnection {
// wasn't added until version 3.7.3.
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_ONLY.bits, 0x02);
debug_assert_eq!(1 << OpenFlags::SQLITE_OPEN_READ_WRITE.bits, 0x04);
debug_assert_eq!(1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits, 0x40);
debug_assert_eq!(
1 << (OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE).bits,
0x40
);
if (1 << (flags.bits & 0x7)) & 0x46 == 0 {
return Err(Error::SqliteFailure(ffi::Error::new(ffi::SQLITE_MISUSE), None));
return Err(Error::SqliteFailure(
ffi::Error::new(ffi::SQLITE_MISUSE),
None,
));
}
unsafe {
@ -796,7 +828,7 @@ impl InnerConnection {
// attempt to turn on extended results code; don't fail if we can't.
ffi::sqlite3_extended_result_codes(db, 1);
Ok(InnerConnection { db })
Ok(InnerConnection::new(db))
}
}
@ -830,11 +862,13 @@ impl InnerConnection {
fn execute_batch(&mut self, sql: &str) -> Result<()> {
let c_sql = try!(str_to_cstring(sql));
unsafe {
let r = ffi::sqlite3_exec(self.db(),
c_sql.as_ptr(),
None,
ptr::null_mut(),
ptr::null_mut());
let r = ffi::sqlite3_exec(
self.db(),
c_sql.as_ptr(),
None,
ptr::null_mut(),
ptr::null_mut(),
);
self.decode_result(r)
}
}
@ -852,10 +886,12 @@ impl InnerConnection {
let mut errmsg: *mut c_char = mem::uninitialized();
let r = if let Some(entry_point) = entry_point {
let c_entry = try!(str_to_cstring(entry_point));
ffi::sqlite3_load_extension(self.db,
dylib_str.as_ptr(),
c_entry.as_ptr(),
&mut errmsg)
ffi::sqlite3_load_extension(
self.db,
dylib_str.as_ptr(),
c_entry.as_ptr(),
&mut errmsg,
)
} else {
ffi::sqlite3_load_extension(self.db, dylib_str.as_ptr(), ptr::null(), &mut errmsg)
};
@ -910,9 +946,8 @@ impl InnerConnection {
)
}
};
self.decode_result(r).map(|_| {
Statement::new(conn, RawStatement::new(c_stmt))
})
self.decode_result(r)
.map(|_| Statement::new(conn, RawStatement::new(c_stmt)))
}
fn changes(&mut self) -> usize {
@ -939,8 +974,7 @@ impl InnerConnection {
}
#[cfg(not(feature = "hooks"))]
fn remove_hooks(&mut self) {
}
fn remove_hooks(&mut self) {}
}
impl Drop for InnerConnection {
@ -966,18 +1000,16 @@ pub type SqliteStatement<'conn> = Statement<'conn>;
#[deprecated(since = "0.6.0", note = "Use Rows instead")]
pub type SqliteRows<'stmt> = Rows<'stmt>;
/// Old name for `Row`. `SqliteRow` is deprecated.
#[deprecated(since = "0.6.0", note = "Use Row instead")]
pub type SqliteRow<'a, 'stmt> = Row<'a, 'stmt>;
#[cfg(test)]
mod test {
extern crate tempdir;
use self::tempdir::TempDir;
pub use super::*;
use ffi;
use self::tempdir::TempDir;
pub use std::error::Error as StdError;
pub use std::fmt;
@ -999,10 +1031,13 @@ mod test {
let tmp = TempDir::new("locked").unwrap();
let path = tmp.path().join("transactions.db3");
Connection::open(&path).expect("create temp db").execute_batch("
Connection::open(&path)
.expect("create temp db")
.execute_batch(
"
BEGIN; CREATE TABLE foo(x INTEGER);
INSERT INTO foo VALUES(42); END;")
.expect("create temp db");
INSERT INTO foo VALUES(42); END;",
).expect("create temp db");
let mut db1 = Connection::open(&path).unwrap();
let mut db2 = Connection::open(&path).unwrap();
@ -1015,8 +1050,12 @@ mod test {
let tx2 = db2.transaction().unwrap();
// SELECT first makes sqlite lock with a shared lock
let _ = tx1.query_row("SELECT x FROM foo LIMIT 1", &[], |_| ()).unwrap();
let _ = tx2.query_row("SELECT x FROM foo LIMIT 1", &[], |_| ()).unwrap();
let _ = tx1
.query_row("SELECT x FROM foo LIMIT 1", &[], |_| ())
.unwrap();
let _ = tx2
.query_row("SELECT x FROM foo LIMIT 1", &[], |_| ())
.unwrap();
tx1.execute("INSERT INTO foo VALUES(?1)", &[&1]).unwrap();
let _ = tx2.execute("INSERT INTO foo VALUES(?1)", &[&2]);
@ -1025,12 +1064,15 @@ mod test {
let _ = tx2.commit();
}
let _ = db1.transaction().expect("commit should have closed transaction");
let _ = db2.transaction().expect("commit should have closed transaction");
let _ = db1
.transaction()
.expect("commit should have closed transaction");
let _ = db2
.transaction()
.expect("commit should have closed transaction");
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_persistence() {
let temp_dir = TempDir::new("test_open_file").unwrap();
let path = temp_dir.path().join("test.db3");
@ -1066,20 +1108,22 @@ mod test {
// force the DB to be busy by preparing a statement; this must be done at the FFI
// level to allow us to call .close() without dropping the prepared statement first.
let raw_stmt = {
use std::mem;
use std::ptr;
use std::os::raw::c_int;
use super::str_to_cstring;
use std::mem;
use std::os::raw::c_int;
use std::ptr;
let raw_db = db.db.borrow_mut().db;
let sql = "SELECT 1";
let mut raw_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() };
let rc = unsafe {
ffi::sqlite3_prepare_v2(raw_db,
str_to_cstring(sql).unwrap().as_ptr(),
(sql.len() + 1) as c_int,
&mut raw_stmt,
ptr::null_mut())
ffi::sqlite3_prepare_v2(
raw_db,
str_to_cstring(sql).unwrap().as_ptr(),
(sql.len() + 1) as c_int,
&mut raw_stmt,
ptr::null_mut(),
)
};
assert_eq!(rc, ffi::SQLITE_OK);
raw_stmt
@ -1098,15 +1142,16 @@ mod test {
#[test]
fn test_open_with_flags() {
for bad_flags in &[OpenFlags::empty(),
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE,
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE] {
for bad_flags in &[
OpenFlags::empty(),
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_READ_WRITE,
OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_CREATE,
] {
assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err());
}
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_execute_batch() {
let db = checked_memory_handle();
let sql = "BEGIN;
@ -1118,7 +1163,8 @@ mod test {
END;";
db.execute_batch(sql).unwrap();
db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3").unwrap();
db.execute_batch("UPDATE foo SET x = 3 WHERE x < 3")
.unwrap();
assert!(db.execute_batch("INVALID SQL").is_err());
}
@ -1128,16 +1174,22 @@ mod test {
let db = checked_memory_handle();
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
assert_eq!(1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32])
.unwrap());
assert_eq!(1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32])
.unwrap());
assert_eq!(
1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32])
.unwrap()
);
assert_eq!(
1,
db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32])
.unwrap()
);
assert_eq!(3i32,
db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap());
assert_eq!(
3i32,
db.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap()
);
}
#[test]
@ -1194,7 +1246,8 @@ mod test {
assert_eq!(insert_stmt.execute(&[&2i32]).unwrap(), 1);
assert_eq!(insert_stmt.execute(&[&3i32]).unwrap(), 1);
let mut query = db.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")
let mut query = db
.prepare("SELECT x FROM foo WHERE x < ? ORDER BY x DESC")
.unwrap();
{
let mut rows = query.query(&[&4i32]).unwrap();
@ -1220,7 +1273,6 @@ mod test {
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_query_map() {
let db = checked_memory_handle();
let sql = "BEGIN;
@ -1233,15 +1285,13 @@ mod test {
db.execute_batch(sql).unwrap();
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
let results: Result<Vec<String>> = query.query_map(&[], |row| row.get(1))
.unwrap()
.collect();
let results: Result<Vec<String>> =
query.query_map(&[], |row| row.get(1)).unwrap().collect();
assert_eq!(results.unwrap().concat(), "hello, world!");
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_query_row() {
let db = checked_memory_handle();
let sql = "BEGIN;
@ -1253,9 +1303,11 @@ mod test {
END;";
db.execute_batch(sql).unwrap();
assert_eq!(10i64,
db.query_row::<i64, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap());
assert_eq!(
10i64,
db.query_row::<i64, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap()
);
let result: Result<i64> = db.query_row("SELECT x FROM foo WHERE x > 5", &[], |r| r.get(0));
match result.unwrap_err() {
@ -1282,8 +1334,7 @@ mod test {
let db = checked_memory_handle();
db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")
.unwrap();
db.execute_batch("INSERT INTO foo DEFAULT VALUES")
.unwrap();
db.execute_batch("INSERT INTO foo DEFAULT VALUES").unwrap();
assert_eq!(db.last_insert_rowid(), 1);
@ -1297,8 +1348,10 @@ mod test {
#[test]
fn test_is_autocommit() {
let db = checked_memory_handle();
assert!(db.is_autocommit(),
"autocommit expected to be active by default");
assert!(
db.is_autocommit(),
"autocommit expected to be active by default"
);
}
#[test]
@ -1403,7 +1456,6 @@ mod test {
type CustomResult<T> = ::std::result::Result<T, CustomError>;
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_query_and_then() {
let db = checked_memory_handle();
let sql = "BEGIN;
@ -1416,8 +1468,8 @@ mod test {
db.execute_batch(sql).unwrap();
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
let results: Result<Vec<String>> = query.query_and_then(&[],
|row| row.get_checked(1))
let results: Result<Vec<String>> = query
.query_and_then(&[], |row| row.get_checked(1))
.unwrap()
.collect();
@ -1425,7 +1477,6 @@ mod test {
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_query_and_then_fails() {
let db = checked_memory_handle();
let sql = "BEGIN;
@ -1438,7 +1489,8 @@ mod test {
db.execute_batch(sql).unwrap();
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
let bad_type: Result<Vec<f64>> = query.query_and_then(&[], |row| row.get_checked(1))
let bad_type: Result<Vec<f64>> = query
.query_and_then(&[], |row| row.get_checked(1))
.unwrap()
.collect();
@ -1447,7 +1499,8 @@ mod test {
err => panic!("Unexpected error {}", err),
}
let bad_idx: Result<Vec<String>> = query.query_and_then(&[], |row| row.get_checked(3))
let bad_idx: Result<Vec<String>> = query
.query_and_then(&[], |row| row.get_checked(3))
.unwrap()
.collect();
@ -1458,7 +1511,6 @@ mod test {
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_query_and_then_custom_error() {
let db = checked_memory_handle();
let sql = "BEGIN;
@ -1471,18 +1523,15 @@ mod test {
db.execute_batch(sql).unwrap();
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
let results: CustomResult<Vec<String>> = query.query_and_then(&[], |row| {
row.get_checked(1)
.map_err(CustomError::Sqlite)
})
.unwrap()
let results: CustomResult<Vec<String>> = query
.query_and_then(&[], |row| row.get_checked(1).map_err(CustomError::Sqlite))
.unwrap()
.collect();
assert_eq!(results.unwrap().concat(), "hello, world!");
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_query_and_then_custom_error_fails() {
let db = checked_memory_handle();
let sql = "BEGIN;
@ -1495,11 +1544,9 @@ mod test {
db.execute_batch(sql).unwrap();
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
let bad_type: CustomResult<Vec<f64>> = query.query_and_then(&[], |row| {
row.get_checked(1)
.map_err(CustomError::Sqlite)
})
.unwrap()
let bad_type: CustomResult<Vec<f64>> = query
.query_and_then(&[], |row| row.get_checked(1).map_err(CustomError::Sqlite))
.unwrap()
.collect();
match bad_type.unwrap_err() {
@ -1507,11 +1554,9 @@ mod test {
err => panic!("Unexpected error {}", err),
}
let bad_idx: CustomResult<Vec<String>> = query.query_and_then(&[], |row| {
row.get_checked(3)
.map_err(CustomError::Sqlite)
})
.unwrap()
let bad_idx: CustomResult<Vec<String>> = query
.query_and_then(&[], |row| row.get_checked(3).map_err(CustomError::Sqlite))
.unwrap()
.collect();
match bad_idx.unwrap_err() {
@ -1519,10 +1564,9 @@ mod test {
err => panic!("Unexpected error {}", err),
}
let non_sqlite_err: CustomResult<Vec<String>> = query.query_and_then(&[], |_| {
Err(CustomError::SomeError)
})
.unwrap()
let non_sqlite_err: CustomResult<Vec<String>> = query
.query_and_then(&[], |_| Err(CustomError::SomeError))
.unwrap()
.collect();
match non_sqlite_err.unwrap_err() {
@ -1532,7 +1576,6 @@ mod test {
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_query_row_and_then_custom_error() {
let db = checked_memory_handle();
let sql = "BEGIN;
@ -1550,7 +1593,6 @@ mod test {
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_query_row_and_then_custom_error_fails() {
let db = checked_memory_handle();
let sql = "BEGIN;
@ -1578,9 +1620,8 @@ mod test {
err => panic!("Unexpected error {}", err),
}
let non_sqlite_err: CustomResult<String> = db.query_row_and_then(query, &[], |_| {
Err(CustomError::SomeError)
});
let non_sqlite_err: CustomResult<String> =
db.query_row_and_then(query, &[], |_| Err(CustomError::SomeError));
match non_sqlite_err.unwrap_err() {
CustomError::SomeError => (),
@ -1589,7 +1630,6 @@ mod test {
}
#[test]
#[cfg_attr(rustfmt, rustfmt_skip)]
fn test_dynamic() {
let db = checked_memory_handle();
let sql = "BEGIN;
@ -1598,7 +1638,9 @@ mod test {
END;";
db.execute_batch(sql).unwrap();
db.query_row("SELECT * FROM foo", &[], |r| assert_eq!(2, r.column_count())).unwrap();
db.query_row("SELECT * FROM foo", &[], |r| {
assert_eq!(2, r.column_count())
}).unwrap();
}
}
}

View File

@ -1,4 +1,4 @@
use {Result, Connection};
use {Connection, Result};
/// Old name for `LoadExtensionGuard`. `SqliteLoadExtensionGuard` is deprecated.
#[deprecated(since = "0.6.0", note = "Use LoadExtensionGuard instead")]

View File

@ -1,8 +1,8 @@
use std::ffi::CStr;
use std::ptr;
use std::os::raw::c_int;
use super::ffi;
use super::unlock_notify;
use std::ffi::CStr;
use std::os::raw::c_int;
use std::ptr;
// Private newtype for raw sqlite3_stmts that finalize themselves when dropped.
#[derive(Debug)]
@ -83,6 +83,23 @@ impl RawStatement {
self.0 = ptr::null_mut();
r
}
#[cfg(feature = "bundled")]
pub fn readonly(&self) -> bool {
unsafe { ffi::sqlite3_stmt_readonly(self.0) != 0 }
}
#[cfg(feature = "bundled")]
pub fn expanded_sql(&self) -> Option<&CStr> {
unsafe {
let ptr = ffi::sqlite3_expanded_sql(self.0);
if ptr.is_null() {
None
} else {
Some(CStr::from_ptr(ptr))
}
}
}
}
impl Drop for RawStatement {

View File

@ -1,7 +1,7 @@
use std::{convert, result};
use std::marker::PhantomData;
use std::{convert, result};
use super::{Statement, Error, Result};
use super::{Error, Result, Statement};
use types::{FromSql, FromSqlError};
/// An handle for the resulting rows of a query.
@ -28,23 +28,20 @@ impl<'stmt> Rows<'stmt> {
/// or `query_and_then` instead, which return types that implement `Iterator`.
#[cfg_attr(feature = "cargo-clippy", allow(should_implement_trait))] // cannot implement Iterator
pub fn next<'a>(&'a mut self) -> Option<Result<Row<'a, 'stmt>>> {
self.stmt
.and_then(|stmt| match stmt.step() {
Ok(true) => {
Some(Ok(Row {
stmt,
phantom: PhantomData,
}))
}
Ok(false) => {
self.reset();
None
}
Err(err) => {
self.reset();
Some(Err(err))
}
})
self.stmt.and_then(|stmt| match stmt.step() {
Ok(true) => Some(Ok(Row {
stmt,
phantom: PhantomData,
})),
Ok(false) => {
self.reset();
None
}
Err(err) => {
self.reset();
Some(Err(err))
}
})
}
}
@ -74,7 +71,8 @@ pub struct MappedRows<'stmt, F> {
}
impl<'stmt, T, F> MappedRows<'stmt, F>
where F: FnMut(&Row) -> T
where
F: FnMut(&Row) -> T,
{
pub(crate) fn new(rows: Rows<'stmt>, f: F) -> MappedRows<'stmt, F> {
MappedRows { rows, map: f }
@ -82,7 +80,8 @@ impl<'stmt, T, F> MappedRows<'stmt, F>
}
impl<'conn, T, F> Iterator for MappedRows<'conn, F>
where F: FnMut(&Row) -> T
where
F: FnMut(&Row) -> T,
{
type Item = Result<T>;
@ -102,7 +101,8 @@ pub struct AndThenRows<'stmt, F> {
}
impl<'stmt, T, E, F> AndThenRows<'stmt, F>
where F: FnMut(&Row) -> result::Result<T, E>
where
F: FnMut(&Row) -> result::Result<T, E>,
{
pub(crate) fn new(rows: Rows<'stmt>, f: F) -> AndThenRows<'stmt, F> {
AndThenRows { rows, map: f }
@ -110,8 +110,9 @@ impl<'stmt, T, E, F> AndThenRows<'stmt, F>
}
impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F>
where E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>
where
E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>,
{
type Item = result::Result<T, E>;
@ -159,17 +160,12 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
let idx = try!(idx.idx(self.stmt));
let value = self.stmt.value_ref(idx);
FromSql::column_result(value).map_err(|err| match err {
FromSqlError::InvalidType => {
Error::InvalidColumnType(idx,
value.data_type())
}
FromSqlError::OutOfRange(i) => {
Error::IntegralValueOutOfRange(idx, i)
}
FromSqlError::Other(err) => {
FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()),
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
FromSqlError::Other(err) => {
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
}
})
})
}
/// Return the number of columns in the current row.

View File

@ -1,16 +1,18 @@
use std::{convert, fmt, mem, ptr, result, str};
use std::ffi::CStr;
use std::os::raw::{c_char, c_int, c_void};
#[cfg(feature = "array")]
use std::rc::Rc;
use std::slice::from_raw_parts;
use std::{convert, fmt, mem, ptr, result, str};
use super::ffi;
use super::{Connection, RawStatement, Result, Error, ValueRef, Row, Rows, AndThenRows, MappedRows};
use super::str_to_cstring;
use super::{
AndThenRows, Connection, Error, MappedRows, RawStatement, Result, Row, Rows, ValueRef,
};
use types::{ToSql, ToSqlOutput};
#[cfg(feature = "array")]
use vtab::array::{ARRAY_TYPE, free_array};
use vtab::array::{free_array, ARRAY_TYPE};
/// A prepared statement.
pub struct Statement<'conn> {
@ -158,6 +160,7 @@ impl<'conn> Statement<'conn> {
///
/// Will return `Err` if binding parameters fails.
pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result<Rows<'a>> {
try!(self.check_readonly());
try!(self.bind_parameters(params));
Ok(Rows::new(self))
}
@ -185,6 +188,7 @@ impl<'conn> Statement<'conn> {
///
/// Will return `Err` if binding parameters fails.
pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> Result<Rows<'a>> {
try!(self.check_readonly());
try!(self.bind_parameters_named(params));
Ok(Rows::new(self))
}
@ -213,7 +217,8 @@ impl<'conn> Statement<'conn> {
///
/// Will return `Err` if binding parameters fails.
pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) -> Result<MappedRows<'a, F>>
where F: FnMut(&Row) -> T
where
F: FnMut(&Row) -> T,
{
let rows = self.query(params)?;
Ok(MappedRows::new(rows, f))
@ -245,11 +250,13 @@ impl<'conn> Statement<'conn> {
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_map_named<'a, T, F>(&'a mut self,
params: &[(&str, &ToSql)],
f: F)
-> Result<MappedRows<'a, F>>
where F: FnMut(&Row) -> T
pub fn query_map_named<'a, T, F>(
&'a mut self,
params: &[(&str, &ToSql)],
f: F,
) -> Result<MappedRows<'a, F>>
where
F: FnMut(&Row) -> T,
{
let rows = self.query_named(params)?;
Ok(MappedRows::new(rows, f))
@ -262,12 +269,14 @@ impl<'conn> Statement<'conn> {
/// # Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_and_then<'a, T, E, F>(&'a mut self,
params: &[&ToSql],
f: F)
-> Result<AndThenRows<'a, F>>
where E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>
pub fn query_and_then<'a, T, E, F>(
&'a mut self,
params: &[&ToSql],
f: F,
) -> Result<AndThenRows<'a, F>>
where
E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>,
{
let rows = self.query(params)?;
Ok(AndThenRows::new(rows, f))
@ -308,12 +317,14 @@ impl<'conn> Statement<'conn> {
/// ## Failure
///
/// Will return `Err` if binding parameters fails.
pub fn query_and_then_named<'a, T, E, F>(&'a mut self,
params: &[(&str, &ToSql)],
f: F)
-> Result<AndThenRows<'a, F>>
where E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>
pub fn query_and_then_named<'a, T, E, F>(
&'a mut self,
params: &[(&str, &ToSql)],
f: F,
) -> Result<AndThenRows<'a, F>>
where
E: convert::From<Error>,
F: FnMut(&Row) -> result::Result<T, E>,
{
let rows = self.query_named(params)?;
Ok(AndThenRows::new(rows, f))
@ -340,7 +351,8 @@ impl<'conn> Statement<'conn> {
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn query_row<T, F>(&mut self, params: &[&ToSql], f: F) -> Result<T>
where F: FnOnce(&Row) -> T
where
F: FnOnce(&Row) -> T,
{
let mut rows = try!(self.query(params));
@ -371,10 +383,13 @@ impl<'conn> Statement<'conn> {
}
fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> {
assert_eq!(params.len(), self.stmt.bind_parameter_count(),
"incorrect number of parameters to query(): expected {}, got {}",
self.stmt.bind_parameter_count(),
params.len());
assert_eq!(
params.len(),
self.stmt.bind_parameter_count(),
"incorrect number of parameters to query(): expected {}, got {}",
self.stmt.bind_parameter_count(),
params.len()
);
for (i, p) in params.iter().enumerate() {
try!(self.bind_parameter(*p, i + 1));
@ -404,25 +419,28 @@ impl<'conn> Statement<'conn> {
#[cfg(feature = "blob")]
ToSqlOutput::ZeroBlob(len) => {
return self.conn
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col as c_int, len) });
return self
.conn
.decode_result(unsafe { ffi::sqlite3_bind_zeroblob(ptr, col as c_int, len) });
}
#[cfg(feature = "array")]
ToSqlOutput::Array(a) => {
return self.conn
.decode_result(unsafe { ffi::sqlite3_bind_pointer(ptr, col as c_int, Rc::into_raw(a) as *mut c_void, ARRAY_TYPE, Some(free_array)) });
return self.conn.decode_result(unsafe {
ffi::sqlite3_bind_pointer(
ptr,
col as c_int,
Rc::into_raw(a) as *mut c_void,
ARRAY_TYPE,
Some(free_array),
)
});
}
};
self.conn
.decode_result(match value {
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col as c_int) },
ValueRef::Integer(i) => unsafe {
ffi::sqlite3_bind_int64(ptr, col as c_int, i)
},
ValueRef::Real(r) => unsafe {
ffi::sqlite3_bind_double(ptr, col as c_int, r)
},
ValueRef::Text(s) => unsafe {
self.conn.decode_result(match value {
ValueRef::Null => unsafe { ffi::sqlite3_bind_null(ptr, col as c_int) },
ValueRef::Integer(i) => unsafe { ffi::sqlite3_bind_int64(ptr, col as c_int, i) },
ValueRef::Real(r) => unsafe { ffi::sqlite3_bind_double(ptr, col as c_int, r) },
ValueRef::Text(s) => unsafe {
let length = s.len();
if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG
@ -433,21 +451,29 @@ impl<'conn> Statement<'conn> {
} else {
ffi::SQLITE_STATIC()
};
ffi::sqlite3_bind_text(ptr, col as c_int, c_str.as_ptr(), length as c_int, destructor)
ffi::sqlite3_bind_text(
ptr,
col as c_int,
c_str.as_ptr(),
length as c_int,
destructor,
)
}
},
ValueRef::Blob(b) => unsafe {
ValueRef::Blob(b) => unsafe {
let length = b.len();
if length > ::std::i32::MAX as usize {
ffi::SQLITE_TOOBIG
} else if length == 0 {
ffi::sqlite3_bind_zeroblob(ptr, col as c_int, 0)
} else {
ffi::sqlite3_bind_blob(ptr,
col as c_int,
b.as_ptr() as *const c_void,
length as c_int,
ffi::SQLITE_TRANSIENT())
ffi::sqlite3_bind_blob(
ptr,
col as c_int,
b.as_ptr() as *const c_void,
length as c_int,
ffi::SQLITE_TRANSIENT(),
)
}
},
})
@ -474,6 +500,29 @@ impl<'conn> Statement<'conn> {
mem::swap(&mut stmt, &mut self.stmt);
self.conn.decode_result(stmt.finalize())
}
#[cfg(not(feature = "bundled"))]
fn check_readonly(&self) -> Result<()> {
Ok(())
}
#[cfg(feature = "bundled")]
fn check_readonly(&self) -> Result<()> {
if !self.stmt.readonly() {
return Err(Error::InvalidQuery);
}
Ok(())
}
/// Returns a string containing the SQL text of prepared statement with bound parameters expanded.
#[cfg(feature = "bundled")]
pub fn expanded_sql(&self) -> Option<&str> {
unsafe {
self.stmt
.expanded_sql()
.map(|s| str::from_utf8_unchecked(s.to_bytes()))
}
}
}
impl<'conn> Into<RawStatement> for Statement<'conn> {
@ -504,10 +553,7 @@ impl<'conn> Drop for Statement<'conn> {
impl<'conn> Statement<'conn> {
pub(crate) fn new(conn: &Connection, stmt: RawStatement) -> Statement {
Statement {
conn,
stmt,
}
Statement { conn, stmt }
}
pub(crate) fn value_ref(&self, col: usize) -> ValueRef {
@ -518,30 +564,42 @@ impl<'conn> Statement<'conn> {
ffi::SQLITE_INTEGER => {
ValueRef::Integer(unsafe { ffi::sqlite3_column_int64(raw, col as c_int) })
}
ffi::SQLITE_FLOAT => ValueRef::Real(unsafe { ffi::sqlite3_column_double(raw, col as c_int) }),
ffi::SQLITE_FLOAT => {
ValueRef::Real(unsafe { ffi::sqlite3_column_double(raw, col as c_int) })
}
ffi::SQLITE_TEXT => {
let s = unsafe {
let text = ffi::sqlite3_column_text(raw, col as c_int);
assert!(!text.is_null(),
"unexpected SQLITE_TEXT column type with NULL data");
assert!(
!text.is_null(),
"unexpected SQLITE_TEXT column type with NULL data"
);
CStr::from_ptr(text as *const c_char)
};
// sqlite3_column_text returns UTF8 data, so our unwrap here should be fine.
let s = s.to_str()
let s = s
.to_str()
.expect("sqlite3_column_text returned invalid UTF-8");
ValueRef::Text(s)
}
ffi::SQLITE_BLOB => {
let (blob, len) = unsafe {
(ffi::sqlite3_column_blob(raw, col as c_int), ffi::sqlite3_column_bytes(raw, col as c_int))
(
ffi::sqlite3_column_blob(raw, col as c_int),
ffi::sqlite3_column_bytes(raw, col as c_int),
)
};
assert!(len >= 0,
"unexpected negative return from sqlite3_column_bytes");
assert!(
len >= 0,
"unexpected negative return from sqlite3_column_bytes"
);
if len > 0 {
assert!(!blob.is_null(),
"unexpected SQLITE_BLOB column type with NULL data");
assert!(
!blob.is_null(),
"unexpected SQLITE_BLOB column type with NULL data"
);
ValueRef::Blob(unsafe { from_raw_parts(blob as *const u8, len as usize) })
} else {
// The return value from sqlite3_column_blob() for a zero-length BLOB
@ -575,18 +633,25 @@ mod test {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])
.unwrap(),
1);
assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
.unwrap(),
1);
assert_eq!(
db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)])
.unwrap(),
1
);
assert_eq!(
db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)])
.unwrap(),
1
);
assert_eq!(3i32,
db.query_row_named::<i32, _>("SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &0i32)],
|r| r.get(0))
.unwrap());
assert_eq!(
3i32,
db.query_row_named::<i32, _>(
"SELECT SUM(x) FROM foo WHERE x > :x",
&[(":x", &0i32)],
|r| r.get(0)
).unwrap()
);
}
#[test]
@ -596,15 +661,19 @@ mod test {
INTEGER)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)")
let mut stmt = db
.prepare("INSERT INTO test (name) VALUES (:name)")
.unwrap();
stmt.execute_named(&[(":name", &"one")]).unwrap();
assert_eq!(1i32,
db.query_row_named::<i32, _>("SELECT COUNT(*) FROM test WHERE name = :name",
&[(":name", &"one")],
|r| r.get(0))
.unwrap());
assert_eq!(
1i32,
db.query_row_named::<i32, _>(
"SELECT COUNT(*) FROM test WHERE name = :name",
&[(":name", &"one")],
|r| r.get(0)
).unwrap()
);
}
#[test]
@ -616,7 +685,8 @@ mod test {
"#;
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name")
let mut stmt = db
.prepare("SELECT id FROM test where name = :name")
.unwrap();
let mut rows = stmt.query_named(&[(":name", &"one")]).unwrap();
@ -633,13 +703,14 @@ mod test {
"#;
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name")
let mut stmt = db
.prepare("SELECT id FROM test where name = :name")
.unwrap();
let mut rows = stmt.query_map_named(&[(":name", &"one")], |row| {
let mut rows = stmt
.query_map_named(&[(":name", &"one")], |row| {
let id: i32 = row.get(0);
2 * id
})
.unwrap();
}).unwrap();
let doubled_id: i32 = rows.next().unwrap().unwrap();
assert_eq!(2, doubled_id);
@ -647,7 +718,6 @@ mod test {
#[test]
fn test_query_and_then_named() {
let db = Connection::open_in_memory().unwrap();
let sql = r#"
CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER);
@ -656,17 +726,18 @@ mod test {
"#;
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")
let mut stmt = db
.prepare("SELECT id FROM test where name = :name ORDER BY id ASC")
.unwrap();
let mut rows = stmt.query_and_then_named(&[(":name", &"one")], |row| {
let mut rows = stmt
.query_and_then_named(&[(":name", &"one")], |row| {
let id: i32 = row.get(0);
if id == 1 {
Ok(id)
} else {
Err(Error::SqliteSingleThreadedMode)
}
})
.unwrap();
}).unwrap();
// first row should be Ok
let doubled_id: i32 = rows.next().unwrap().unwrap();
@ -686,13 +757,14 @@ mod test {
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
let mut stmt = db
.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
.unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap();
let result: Option<String> =
db.query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0))
.unwrap();
let result: Option<String> = db
.query_row("SELECT y FROM test WHERE x = 'one'", &[], |row| row.get(0))
.unwrap();
assert!(result.is_none());
}
@ -702,14 +774,15 @@ mod test {
let sql = "CREATE TABLE test (x TEXT, y TEXT)";
db.execute_batch(sql).unwrap();
let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
let mut stmt = db
.prepare("INSERT INTO test (x, y) VALUES (:x, :y)")
.unwrap();
stmt.execute_named(&[(":x", &"one")]).unwrap();
stmt.execute_named(&[(":y", &"two")]).unwrap();
let result: String =
db.query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0))
.unwrap();
let result: String = db
.query_row("SELECT x FROM test WHERE y = 'two'", &[], |row| row.get(0))
.unwrap();
assert_eq!(result, "one");
}
@ -718,7 +791,8 @@ mod test {
let db = Connection::open_in_memory().unwrap();
db.execute_batch("CREATE TABLE foo(x INTEGER UNIQUE)")
.unwrap();
let mut stmt = db.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")
let mut stmt = db
.prepare("INSERT OR IGNORE INTO foo (x) VALUES (?)")
.unwrap();
assert_eq!(stmt.insert(&[&1i32]).unwrap(), 1);
assert_eq!(stmt.insert(&[&2i32]).unwrap(), 2);
@ -726,7 +800,8 @@ mod test {
Error::StatementChangedRows(0) => (),
err => panic!("Unexpected error {}", err),
}
let mut multi = db.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
let mut multi = db
.prepare("INSERT INTO foo (x) SELECT 3 UNION ALL SELECT 4")
.unwrap();
match multi.insert(&[]).unwrap_err() {
Error::StatementChangedRows(2) => (),
@ -738,22 +813,27 @@ mod test {
fn test_insert_different_tables() {
// Test for https://github.com/jgallagher/rusqlite/issues/171
let db = Connection::open_in_memory().unwrap();
db.execute_batch(r"
db.execute_batch(
r"
CREATE TABLE foo(x INTEGER);
CREATE TABLE bar(x INTEGER);
")
.unwrap();
",
).unwrap();
assert_eq!(db.prepare("INSERT INTO foo VALUES (10)")
.unwrap()
.insert(&[])
.unwrap(),
1);
assert_eq!(db.prepare("INSERT INTO bar VALUES (10)")
.unwrap()
.insert(&[])
.unwrap(),
1);
assert_eq!(
db.prepare("INSERT INTO foo VALUES (10)")
.unwrap()
.insert(&[])
.unwrap(),
1
);
assert_eq!(
db.prepare("INSERT INTO bar VALUES (10)")
.unwrap()
.insert(&[])
.unwrap(),
1
);
}
#[test]
@ -810,4 +890,13 @@ mod test {
let y: Result<i64> = stmt.query_row(&[], |r| r.get("y"));
assert_eq!(3i64, y.unwrap());
}
#[test]
#[cfg(feature = "bundled")]
fn test_expanded_sql() {
let db = Connection::open_in_memory().unwrap();
let stmt = db.prepare("SELECT ?").unwrap();
stmt.bind_parameter(&1, 1).unwrap();
assert_eq!(Some("SELECT 1"), stmt.expanded_sql());
}
}

View File

@ -1,14 +1,14 @@
//! Tracing and profiling functions. Error and warning log.
use std::os::raw::{c_char, c_int, c_void};
use std::ffi::{CStr, CString};
use std::mem;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use std::time::Duration;
use super::ffi;
use {Result, Connection};
use error::error_from_sqlite_code;
use {Connection, Result};
/// Set up the process-wide SQLite error logging callback.
/// This function is marked unsafe for two reasons:
@ -33,9 +33,11 @@ pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
let rc = match callback {
Some(f) => {
let p_arg: *mut c_void = mem::transmute(f);
ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG,
log_callback as extern "C" fn(_, _, _),
p_arg)
ffi::sqlite3_config(
ffi::SQLITE_CONFIG_LOG,
log_callback as extern "C" fn(_, _, _),
p_arg,
)
}
None => {
let nullptr: *mut c_void = ptr::null_mut();
@ -90,16 +92,20 @@ impl Connection {
/// There can only be a single profiler defined for each database connection.
/// Setting a new profiler clears the old one.
pub fn profile(&mut self, profile_fn: Option<fn(&str, Duration)>) {
unsafe extern "C" fn profile_callback(p_arg: *mut c_void,
z_sql: *const c_char,
nanoseconds: u64) {
unsafe extern "C" fn profile_callback(
p_arg: *mut c_void,
z_sql: *const c_char,
nanoseconds: u64,
) {
let profile_fn: fn(&str, Duration) = mem::transmute(p_arg);
let c_slice = CStr::from_ptr(z_sql).to_bytes();
let s = String::from_utf8_lossy(c_slice);
const NANOS_PER_SEC: u64 = 1_000_000_000;
let duration = Duration::new(nanoseconds / NANOS_PER_SEC,
(nanoseconds % NANOS_PER_SEC) as u32);
let duration = Duration::new(
nanoseconds / NANOS_PER_SEC,
(nanoseconds % NANOS_PER_SEC) as u32,
);
profile_fn(&s, duration);
}

View File

@ -1,5 +1,5 @@
use std::ops::Deref;
use {Result, Connection};
use {Connection, Result};
/// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is deprecated.
#[deprecated(since = "0.6.0", note = "Use TransactionBehavior instead")]
@ -7,7 +7,7 @@ pub type SqliteTransactionBehavior = TransactionBehavior;
/// Options for transaction behavior. See [BEGIN
/// TRANSACTION](http://www.sqlite.org/lang_transaction.html) for details.
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub enum TransactionBehavior {
Deferred,
Immediate,
@ -15,7 +15,7 @@ pub enum TransactionBehavior {
}
/// Options for how a Transaction or Savepoint should behave when it is dropped.
#[derive(Copy,Clone,PartialEq,Eq)]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum DropBehavior {
/// Roll back the changes. This is the default.
Rollback,
@ -105,13 +105,10 @@ impl<'conn> Transaction<'conn> {
TransactionBehavior::Immediate => "BEGIN IMMEDIATE",
TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE",
};
conn.execute_batch(query)
.map(move |_| {
Transaction {
conn,
drop_behavior: DropBehavior::Rollback,
}
})
conn.execute_batch(query).map(move |_| Transaction {
conn,
drop_behavior: DropBehavior::Rollback,
})
}
/// Starts a new [savepoint](http://www.sqlite.org/lang_savepoint.html), allowing nested
@ -217,20 +214,19 @@ impl<'conn> Drop for Transaction<'conn> {
}
impl<'conn> Savepoint<'conn> {
fn with_depth_and_name<T: Into<String>>(conn: &Connection,
depth: u32,
name: T)
-> Result<Savepoint> {
fn with_depth_and_name<T: Into<String>>(
conn: &Connection,
depth: u32,
name: T,
) -> Result<Savepoint> {
let name = name.into();
conn.execute_batch(&format!("SAVEPOINT {}", name))
.map(|_| {
Savepoint {
conn,
name,
depth,
drop_behavior: DropBehavior::Rollback,
committed: false,
}
.map(|_| Savepoint {
conn,
name,
depth,
drop_behavior: DropBehavior::Rollback,
committed: false,
})
}
@ -275,8 +271,7 @@ impl<'conn> Savepoint<'conn> {
}
fn commit_(&mut self) -> Result<()> {
self.conn
.execute_batch(&format!("RELEASE {}", self.name))?;
self.conn.execute_batch(&format!("RELEASE {}", self.name))?;
self.committed = true;
Ok(())
}
@ -365,9 +360,10 @@ impl Connection {
/// # Failure
///
/// Will return `Err` if the underlying SQLite call fails.
pub fn transaction_with_behavior(&mut self,
behavior: TransactionBehavior)
-> Result<Transaction> {
pub fn transaction_with_behavior(
&mut self,
behavior: TransactionBehavior,
) -> Result<Transaction> {
Transaction::new(self, behavior)
}
@ -413,8 +409,8 @@ impl Connection {
#[cfg(test)]
mod test {
use Connection;
use super::DropBehavior;
use Connection;
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@ -437,9 +433,11 @@ mod test {
}
{
let tx = db.transaction().unwrap();
assert_eq!(2i32,
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap());
assert_eq!(
2i32,
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap()
);
}
}
@ -464,9 +462,11 @@ mod test {
}
{
let tx = db.transaction().unwrap();
assert_eq!(6i32,
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap());
assert_eq!(
6i32,
tx.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap()
);
}
}
@ -560,7 +560,8 @@ mod test {
}
fn assert_current_sum(x: i32, conn: &Connection) {
let i = conn.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
let i = conn
.query_row::<i32, _>("SELECT SUM(x) FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(x, i);
}

View File

@ -3,10 +3,10 @@ extern crate chrono;
use std::borrow::Cow;
use self::chrono::{NaiveDate, NaiveTime, NaiveDateTime, DateTime, TimeZone, Utc, Local};
use self::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc};
use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use Result;
/// ISO 8601 calendar date without timezone => "YYYY-MM-DD"
impl ToSql for NaiveDate {
@ -22,9 +22,9 @@ impl FromSql for NaiveDate {
value
.as_str()
.and_then(|s| match NaiveDate::parse_from_str(s, "%Y-%m-%d") {
Ok(dt) => Ok(dt),
Err(err) => Err(FromSqlError::Other(Box::new(err))),
})
Ok(dt) => Ok(dt),
Err(err) => Err(FromSqlError::Other(Box::new(err))),
})
}
}
@ -39,19 +39,17 @@ impl ToSql for NaiveTime {
/// "HH:MM"/"HH:MM:SS"/"HH:MM:SS.SSS" => ISO 8601 time without timezone.
impl FromSql for NaiveTime {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value
.as_str()
.and_then(|s| {
let fmt = match s.len() {
5 => "%H:%M",
8 => "%H:%M:%S",
_ => "%H:%M:%S%.f",
};
match NaiveTime::parse_from_str(s, fmt) {
Ok(dt) => Ok(dt),
Err(err) => Err(FromSqlError::Other(Box::new(err))),
}
})
value.as_str().and_then(|s| {
let fmt = match s.len() {
5 => "%H:%M",
8 => "%H:%M:%S",
_ => "%H:%M:%S%.f",
};
match NaiveTime::parse_from_str(s, fmt) {
Ok(dt) => Ok(dt),
Err(err) => Err(FromSqlError::Other(Box::new(err))),
}
})
}
}
@ -67,20 +65,18 @@ impl ToSql for NaiveDateTime {
/// without timezone. ("YYYY-MM-DDTHH:MM:SS"/"YYYY-MM-DDTHH:MM:SS.SSS" also supported)
impl FromSql for NaiveDateTime {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
value
.as_str()
.and_then(|s| {
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
"%Y-%m-%dT%H:%M:%S%.f"
} else {
"%Y-%m-%d %H:%M:%S%.f"
};
value.as_str().and_then(|s| {
let fmt = if s.len() >= 11 && s.as_bytes()[10] == b'T' {
"%Y-%m-%dT%H:%M:%S%.f"
} else {
"%Y-%m-%d %H:%M:%S%.f"
};
match NaiveDateTime::parse_from_str(s, fmt) {
Ok(dt) => Ok(dt),
Err(err) => Err(FromSqlError::Other(Box::new(err))),
}
})
match NaiveDateTime::parse_from_str(s, fmt) {
Ok(dt) => Ok(dt),
Err(err) => Err(FromSqlError::Other(Box::new(err))),
}
})
}
}
@ -130,9 +126,10 @@ impl FromSql for DateTime<Local> {
#[cfg(test)]
mod test {
use super::chrono::{
DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
};
use Connection;
use super::chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc,
Duration};
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@ -148,10 +145,12 @@ mod test {
db.execute("INSERT INTO foo (t) VALUES (?)", &[&date])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let s: String = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("2016-02-23", s);
let t: NaiveDate = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let t: NaiveDate = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(date, t);
}
@ -163,10 +162,12 @@ mod test {
db.execute("INSERT INTO foo (t) VALUES (?)", &[&time])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let s: String = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("23:56:04", s);
let v: NaiveTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let v: NaiveTime = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(time, v);
}
@ -181,16 +182,18 @@ mod test {
db.execute("INSERT INTO foo (t) VALUES (?)", &[&dt])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let s: String = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("2016-02-23T23:56:04", s);
let v: NaiveDateTime = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let v: NaiveDateTime = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(dt, v);
db.execute("UPDATE foo set b = datetime(t)", &[])
.unwrap(); // "YYYY-MM-DD HH:MM:SS"
let hms: NaiveDateTime = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
db.execute("UPDATE foo set b = datetime(t)", &[]).unwrap(); // "YYYY-MM-DD HH:MM:SS"
let hms: NaiveDateTime = db
.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(dt, hms);
}
@ -206,25 +209,29 @@ mod test {
db.execute("INSERT INTO foo (t) VALUES (?)", &[&utc])
.unwrap();
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let s: String = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!("2016-02-23T23:56:04.789+00:00", s);
let v1: DateTime<Utc> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let v1: DateTime<Utc> = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc, v1);
let v2: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
let v2: DateTime<Utc> = db
.query_row("SELECT '2016-02-23 23:56:04.789'", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc, v2);
let v3: DateTime<Utc> = db.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0))
let v3: DateTime<Utc> = db
.query_row("SELECT '2016-02-23 23:56:04'", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc - Duration::milliseconds(789), v3);
let v4: DateTime<Utc> =
db.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0))
.unwrap();
let v4: DateTime<Utc> = db
.query_row("SELECT '2016-02-23 23:56:04.789+00:00'", &[], |r| r.get(0))
.unwrap();
assert_eq!(utc, v4);
}
@ -240,11 +247,13 @@ mod test {
.unwrap();
// Stored string should be in UTC
let s: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let s: String = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert!(s.ends_with("+00:00"));
let v: DateTime<Local> = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let v: DateTime<Local> = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(local, v);
}

View File

@ -1,4 +1,4 @@
use super::{ValueRef, Value};
use super::{Value, ValueRef};
use std::error::Error;
use std::fmt;
@ -35,12 +35,11 @@ impl Error for FromSqlError {
}
}
#[cfg_attr(feature="clippy", allow(match_same_arms))]
#[cfg_attr(feature = "clippy", allow(match_same_arms))]
fn cause(&self) -> Option<&Error> {
match *self {
FromSqlError::Other(ref err) => err.cause(),
FromSqlError::InvalidType |
FromSqlError::OutOfRange(_) => None,
FromSqlError::InvalidType | FromSqlError::OutOfRange(_) => None,
}
}
}
@ -115,9 +114,9 @@ impl FromSql for f64 {
impl FromSql for bool {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
i64::column_result(value).map(|i| match i {
0 => false,
_ => true,
})
0 => false,
_ => true,
})
}
}
@ -150,8 +149,8 @@ impl FromSql for Value {
#[cfg(test)]
mod test {
use {Connection, Error};
use super::FromSql;
use {Connection, Error};
fn checked_memory_handle() -> Connection {
Connection::open_in_memory().unwrap()
@ -162,10 +161,12 @@ mod test {
let db = checked_memory_handle();
fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64])
where T: Into<i64> + FromSql + ::std::fmt::Debug
where
T: Into<i64> + FromSql + ::std::fmt::Debug,
{
for n in out_of_range {
let err = db.query_row("SELECT ?", &[n], |r| r.get_checked::<_, T>(0))
let err = db
.query_row("SELECT ?", &[n], |r| r.get_checked::<_, T>(0))
.unwrap()
.unwrap_err();
match err {
@ -174,18 +175,22 @@ mod test {
}
}
for n in in_range {
assert_eq!(*n,
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
.unwrap()
.into());
assert_eq!(
*n,
db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0))
.unwrap()
.into()
);
}
}
check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]);
check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
check_ranges::<i32>(&db,
&[-2147483649, 2147483648],
&[-2147483648, -1, 0, 1, 2147483647]);
check_ranges::<i32>(
&db,
&[-2147483649, 2147483648],
&[-2147483648, -1, 0, 1, 2147483647],
);
check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]);
check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]);
check_ranges::<u32>(&db, &[-2, -1, 4294967296], &[0, 1, 4294967295]);

View File

@ -59,15 +59,15 @@ pub use self::value_ref::ValueRef;
use std::fmt;
mod value;
mod value_ref;
mod from_sql;
mod to_sql;
mod time;
#[cfg(feature = "chrono")]
mod chrono;
mod from_sql;
#[cfg(feature = "serde_json")]
mod serde_json;
mod time;
mod to_sql;
mod value;
mod value_ref;
/// Empty struct that can be used to fill in a query parameter as `NULL`.
///
@ -83,10 +83,10 @@ mod serde_json;
/// conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null])
/// }
/// ```
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct Null;
#[derive(Clone,Debug,PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum Type {
Null,
Integer,
@ -111,11 +111,11 @@ impl fmt::Display for Type {
mod test {
extern crate time;
use super::Value;
use std::f64::EPSILON;
use std::os::raw::{c_double, c_int};
use Connection;
use Error;
use std::os::raw::{c_int, c_double};
use std::f64::EPSILON;
use super::Value;
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@ -132,7 +132,8 @@ mod test {
db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234])
.unwrap();
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
let v: Vec<u8> = db
.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(v, v1234);
}
@ -145,7 +146,8 @@ mod test {
db.execute("INSERT INTO foo(b) VALUES (?)", &[&empty])
.unwrap();
let v: Vec<u8> = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
let v: Vec<u8> = db
.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(v, empty);
}
@ -155,10 +157,10 @@ mod test {
let db = checked_memory_handle();
let s = "hello, world!";
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
.unwrap();
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let from: String = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(from, s);
}
@ -171,7 +173,8 @@ mod test {
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s.to_owned()])
.unwrap();
let from: String = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let from: String = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(from, s);
}
@ -183,9 +186,11 @@ mod test {
db.execute("INSERT INTO foo(i) VALUES (?)", &[&Value::Integer(10)])
.unwrap();
assert_eq!(10i64,
db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0))
.unwrap());
assert_eq!(
10i64,
db.query_row::<i64, _>("SELECT i FROM foo", &[], |r| r.get(0))
.unwrap()
);
}
#[test]
@ -195,12 +200,11 @@ mod test {
let s = Some("hello, world!");
let b = Some(vec![1u8, 2, 3, 4]);
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s])
.unwrap();
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b])
.unwrap();
db.execute("INSERT INTO foo(t) VALUES (?)", &[&s]).unwrap();
db.execute("INSERT INTO foo(b) VALUES (?)", &[&b]).unwrap();
let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")
let mut stmt = db
.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")
.unwrap();
let mut rows = stmt.query(&[]).unwrap();
@ -232,9 +236,10 @@ mod test {
let db = checked_memory_handle();
db.execute("INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
&[])
.unwrap();
db.execute(
"INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
&[],
).unwrap();
let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap();
let mut rows = stmt.query(&[]).unwrap();
@ -246,53 +251,99 @@ mod test {
assert_eq!("text", row.get_checked::<_, String>(1).unwrap());
assert_eq!(1, row.get_checked::<_, c_int>(2).unwrap());
assert!((1.5 - row.get_checked::<_, c_double>(3).unwrap()).abs() < EPSILON);
assert!(row.get_checked::<_, Option<c_int>>(4)
.unwrap()
.is_none());
assert!(row.get_checked::<_, Option<c_double>>(4)
.unwrap()
.is_none());
assert!(row.get_checked::<_, Option<String>>(4)
.unwrap()
.is_none());
assert!(row.get_checked::<_, Option<c_int>>(4).unwrap().is_none());
assert!(row.get_checked::<_, Option<c_double>>(4).unwrap().is_none());
assert!(row.get_checked::<_, Option<String>>(4).unwrap().is_none());
// check some invalid types
// 0 is actually a blob (Vec<u8>)
assert!(is_invalid_column_type(row.get_checked::<_, c_int>(0).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, c_int>(0).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, i64>(0).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, c_double>(0).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, String>(0).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, time::Timespec>(0).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, Option<c_int>>(0).err().unwrap()));
assert!(is_invalid_column_type(
row.get_checked::<_, c_int>(0).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, c_int>(0).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, i64>(0).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, c_double>(0).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, String>(0).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, time::Timespec>(0).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, Option<c_int>>(0).err().unwrap()
));
// 1 is actually a text (String)
assert!(is_invalid_column_type(row.get_checked::<_, c_int>(1).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, i64>(1).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, c_double>(1).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, Vec<u8>>(1).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, Option<c_int>>(1).err().unwrap()));
assert!(is_invalid_column_type(
row.get_checked::<_, c_int>(1).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, i64>(1).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, c_double>(1).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, Vec<u8>>(1).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, Option<c_int>>(1).err().unwrap()
));
// 2 is actually an integer
assert!(is_invalid_column_type(row.get_checked::<_, String>(2).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, Vec<u8>>(2).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, Option<String>>(2).err().unwrap()));
assert!(is_invalid_column_type(
row.get_checked::<_, String>(2).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, Vec<u8>>(2).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, Option<String>>(2).err().unwrap()
));
// 3 is actually a float (c_double)
assert!(is_invalid_column_type(row.get_checked::<_, c_int>(3).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, i64>(3).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, String>(3).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, Vec<u8>>(3).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, Option<c_int>>(3).err().unwrap()));
assert!(is_invalid_column_type(
row.get_checked::<_, c_int>(3).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, i64>(3).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, String>(3).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, Vec<u8>>(3).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, Option<c_int>>(3).err().unwrap()
));
// 4 is actually NULL
assert!(is_invalid_column_type(row.get_checked::<_, c_int>(4).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, i64>(4).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, c_double>(4).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, String>(4).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, Vec<u8>>(4).err().unwrap()));
assert!(is_invalid_column_type(row.get_checked::<_, time::Timespec>(4).err().unwrap()));
assert!(is_invalid_column_type(
row.get_checked::<_, c_int>(4).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, i64>(4).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, c_double>(4).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, String>(4).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, Vec<u8>>(4).err().unwrap()
));
assert!(is_invalid_column_type(
row.get_checked::<_, time::Timespec>(4).err().unwrap()
));
}
#[test]
@ -300,18 +351,23 @@ mod test {
use super::Value;
let db = checked_memory_handle();
db.execute("INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
&[])
.unwrap();
db.execute(
"INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
&[],
).unwrap();
let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo").unwrap();
let mut rows = stmt.query(&[]).unwrap();
let row = rows.next().unwrap().unwrap();
assert_eq!(Value::Blob(vec![1, 2]),
row.get_checked::<_, Value>(0).unwrap());
assert_eq!(Value::Text(String::from("text")),
row.get_checked::<_, Value>(1).unwrap());
assert_eq!(
Value::Blob(vec![1, 2]),
row.get_checked::<_, Value>(0).unwrap()
);
assert_eq!(
Value::Text(String::from("text")),
row.get_checked::<_, Value>(1).unwrap()
);
assert_eq!(Value::Integer(1), row.get_checked::<_, Value>(2).unwrap());
match row.get_checked::<_, Value>(3).unwrap() {
Value::Real(val) => assert!((1.5 - val).abs() < EPSILON),

View File

@ -3,8 +3,8 @@ extern crate serde_json;
use self::serde_json::Value;
use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use Result;
/// Serialize JSON `Value` to text.
impl ToSql for Value {
@ -17,18 +17,17 @@ impl ToSql for Value {
impl FromSql for Value {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
match value {
ValueRef::Text(s) => serde_json::from_str(s),
ValueRef::Blob(b) => serde_json::from_slice(b),
_ => return Err(FromSqlError::InvalidType),
}
.map_err(|err| FromSqlError::Other(Box::new(err)))
ValueRef::Text(s) => serde_json::from_str(s),
ValueRef::Blob(b) => serde_json::from_slice(b),
_ => return Err(FromSqlError::InvalidType),
}.map_err(|err| FromSqlError::Other(Box::new(err)))
}
}
#[cfg(test)]
mod test {
use Connection;
use super::serde_json;
use Connection;
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@ -43,14 +42,17 @@ mod test {
let json = r#"{"foo": 13, "bar": "baz"}"#;
let data: serde_json::Value = serde_json::from_str(json).unwrap();
db.execute("INSERT INTO foo (t, b) VALUES (?, ?)",
&[&data, &json.as_bytes()])
.unwrap();
db.execute(
"INSERT INTO foo (t, b) VALUES (?, ?)",
&[&data, &json.as_bytes()],
).unwrap();
let t: serde_json::Value = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let t: serde_json::Value = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(data, t);
let b: serde_json::Value = db.query_row("SELECT b FROM foo", &[], |r| r.get(0))
let b: serde_json::Value = db
.query_row("SELECT b FROM foo", &[], |r| r.get(0))
.unwrap();
assert_eq!(data, b);
}

View File

@ -1,7 +1,7 @@
extern crate time;
use Result;
use types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
use Result;
const SQLITE_DATETIME_FMT: &str = "%Y-%m-%dT%H:%M:%S.%fZ";
const SQLITE_DATETIME_FMT_LEGACY: &str = "%Y-%m-%d %H:%M:%S:%f %Z";
@ -21,18 +21,18 @@ impl FromSql for time::Timespec {
value
.as_str()
.and_then(|s| {
time::strptime(s, SQLITE_DATETIME_FMT)
.or_else(|err| {
time::strptime(s, SQLITE_DATETIME_FMT_LEGACY)
.or_else(|_| Err(FromSqlError::Other(Box::new(err))))})})
.map(|tm| tm.to_timespec())
time::strptime(s, SQLITE_DATETIME_FMT).or_else(|err| {
time::strptime(s, SQLITE_DATETIME_FMT_LEGACY)
.or_else(|_| Err(FromSqlError::Other(Box::new(err))))
})
}).map(|tm| tm.to_timespec())
}
}
#[cfg(test)]
mod test {
use Connection;
use super::time;
use Connection;
fn checked_memory_handle() -> Connection {
let db = Connection::open_in_memory().unwrap();
@ -47,25 +47,23 @@ mod test {
let mut ts_vec = vec![];
ts_vec.push(time::Timespec::new(10_000, 0));//January 1, 1970 2:46:40 AM
ts_vec.push(time::Timespec::new(10_000, 1000));//January 1, 1970 2:46:40 AM (and one microsecond)
ts_vec.push(time::Timespec::new(1500391124, 1_000_000));//July 18, 2017
ts_vec.push(time::Timespec::new(2000000000, 2_000_000));//May 18, 2033
ts_vec.push(time::Timespec::new(3000000000, 999_999_999));//January 24, 2065
ts_vec.push(time::Timespec::new(10000000000, 0));//November 20, 2286
ts_vec.push(time::Timespec::new(10_000, 0)); //January 1, 1970 2:46:40 AM
ts_vec.push(time::Timespec::new(10_000, 1000)); //January 1, 1970 2:46:40 AM (and one microsecond)
ts_vec.push(time::Timespec::new(1500391124, 1_000_000)); //July 18, 2017
ts_vec.push(time::Timespec::new(2000000000, 2_000_000)); //May 18, 2033
ts_vec.push(time::Timespec::new(3000000000, 999_999_999)); //January 24, 2065
ts_vec.push(time::Timespec::new(10000000000, 0)); //November 20, 2286
for ts in ts_vec {
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap();
db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts])
.unwrap();
let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0))
let from: time::Timespec = db
.query_row("SELECT t FROM foo", &[], |r| r.get(0))
.unwrap();
db.execute("DELETE FROM foo", &[]).unwrap();
assert_eq!(from, ts);
}
}
}

View File

@ -1,11 +1,11 @@
use std::borrow::Cow;
use super::{Null, Value, ValueRef};
use std::borrow::Cow;
#[cfg(feature = "array")]
use vtab::array::Array;
use Result;
/// `ToSqlOutput` represents the possible output types for implementors of the `ToSql` trait.
#[derive(Clone,Debug,PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum ToSqlOutput<'a> {
/// A borrowed SQLite-representable value.
Borrowed(ValueRef<'a>),
@ -18,13 +18,14 @@ pub enum ToSqlOutput<'a> {
ZeroBlob(i32),
#[cfg(feature = "array")]
Array(Array)
Array(Array),
}
// Generically allow any type that can be converted into a ValueRef
// to be converted into a ToSqlOutput as well.
impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
where &'a T: Into<ValueRef<'a>>
where
&'a T: Into<ValueRef<'a>>,
{
fn from(t: &'a T) -> Self {
ToSqlOutput::Borrowed(t.into())
@ -60,14 +61,14 @@ from_value!(Vec<u8>);
impl<'a> ToSql for ToSqlOutput<'a> {
fn to_sql(&self) -> Result<ToSqlOutput> {
Ok(match *self {
ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
#[cfg(feature = "blob")]
#[cfg(feature = "blob")]
ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
#[cfg(feature = "array")]
#[cfg(feature = "array")]
ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
})
})
}
}
@ -111,7 +112,8 @@ to_sql_self!(u32);
to_sql_self!(f64);
impl<'a, T: ?Sized> ToSql for &'a T
where &'a T: Into<ToSqlOutput<'a>>
where
&'a T: Into<ToSqlOutput<'a>>,
{
fn to_sql(&self) -> Result<ToSqlOutput> {
Ok((*self).into())

View File

@ -4,7 +4,7 @@ use super::{Null, Type};
/// dictated by SQLite (not by the caller).
///
/// See [`ValueRef`](enum.ValueRef.html) for a non-owning dynamic type value.
#[derive(Clone,Debug,PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
/// The value is a `NULL` value.
Null,
@ -31,7 +31,9 @@ impl From<bool> for Value {
}
impl From<isize> for Value {
fn from(i: isize) -> Value { Value::Integer(i as i64) }
fn from(i: isize) -> Value {
Value::Integer(i as i64)
}
}
macro_rules! from_i64(

View File

@ -1,11 +1,11 @@
use super::{Type, Value};
use types::{FromSqlError, FromSqlResult};
use super::{Value, Type};
/// A non-owning [dynamic type value](http://sqlite.org/datatype3.html). Typically the
/// memory backing this value is owned by SQLite.
///
/// See [`Value`](enum.Value.html) for an owning dynamic type value.
#[derive(Copy,Clone,Debug,PartialEq)]
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ValueRef<'a> {
/// The value is a `NULL` value.
Null,

View File

@ -1,10 +1,10 @@
//! [Unlock Notification](http://sqlite.org/unlock_notify.html)
#[cfg(feature = "unlock_notify")]
use std::sync::{Condvar, Mutex};
use std::os::raw::c_int;
#[cfg(feature = "unlock_notify")]
use std::os::raw::c_void;
#[cfg(feature = "unlock_notify")]
use std::sync::{Condvar, Mutex};
use ffi;
@ -49,10 +49,9 @@ unsafe extern "C" fn unlock_notify_cb(ap_arg: *mut *mut c_void, n_arg: c_int) {
#[cfg(feature = "unlock_notify")]
pub fn is_locked(db: *mut ffi::sqlite3, rc: c_int) -> bool {
rc == ffi::SQLITE_LOCKED_SHAREDCACHE || (rc & 0xFF) == ffi::SQLITE_LOCKED && unsafe {
ffi::sqlite3_extended_errcode(db)
}
== ffi::SQLITE_LOCKED_SHAREDCACHE
rc == ffi::SQLITE_LOCKED_SHAREDCACHE
|| (rc & 0xFF) == ffi::SQLITE_LOCKED
&& unsafe { ffi::sqlite3_extended_errcode(db) } == ffi::SQLITE_LOCKED_SHAREDCACHE
}
/// This function assumes that an SQLite API call (either `sqlite3_prepare_v2()`

View File

@ -371,8 +371,8 @@ mod test {
.unwrap();
{
let mut s =
db.prepare(
let mut s = db
.prepare(
"SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \
v1.rowid < v2.rowid",
).unwrap();

View File

@ -1,8 +1,8 @@
//! Ensure we reject connections when SQLite is in single-threaded mode, as it
//! would violate safety if multiple Rust threads tried to use connections.
extern crate rusqlite;
extern crate libsqlite3_sys as ffi;
extern crate rusqlite;
use rusqlite::Connection;