From 8bb624ccccf9d194521b922a652b34b9d4235f88 Mon Sep 17 00:00:00 2001 From: Gwenael Treguier Date: Tue, 4 Aug 2015 18:52:57 +0200 Subject: [PATCH 01/53] Factorize code --- src/lib.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f98780b..3dd84ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -614,14 +614,7 @@ impl<'conn> SqliteStatement<'conn> { self.reset_if_needed(); unsafe { - assert!(params.len() as c_int == ffi::sqlite3_bind_parameter_count(self.stmt), - "incorrect number of parameters to execute(): expected {}, got {}", - ffi::sqlite3_bind_parameter_count(self.stmt), - params.len()); - - for (i, p) in params.iter().enumerate() { - try!(self.conn.decode_result(p.bind_parameter(self.stmt, (i + 1) as c_int))); - } + try!(self.bind_parameters(params)); self.needs_reset = true; let r = ffi::sqlite3_step(self.stmt); @@ -660,11 +653,12 @@ impl<'conn> SqliteStatement<'conn> { try!(self.bind_parameters(params)); } + self.needs_reset = true; Ok(SqliteRows::new(self)) } /// Executes the prepared statement and maps a function over the resulting - /// rows. + /// rows. /// /// Unlike the iterator produced by `query`, the returned iterator does not expose the possibility /// for accessing stale rows. @@ -698,8 +692,6 @@ impl<'conn> SqliteStatement<'conn> { try!(self.conn.decode_result(p.bind_parameter(self.stmt, (i + 1) as c_int))); } - self.needs_reset = true; - Ok(()) } From e81757e86d6da929b7458e3282ca7e007fe50d8f Mon Sep 17 00:00:00 2001 From: Gwenael Treguier Date: Tue, 4 Aug 2015 21:48:54 +0200 Subject: [PATCH 02/53] Add Stmt::named_execute and Stmt::parameter_index. --- Cargo.toml | 1 + src/lib.rs | 20 ++++++++----- src/named_params.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 src/named_params.rs diff --git a/Cargo.toml b/Cargo.toml index 5d05ed5..18c2bd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ name = "rusqlite" [features] load_extension = ["libsqlite3-sys/load_extension"] +named_params = [] [dependencies] time = "~0.1.0" diff --git a/src/lib.rs b/src/lib.rs index 3dd84ae..f2d1791 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,6 +79,7 @@ pub use transaction::{SqliteTransactionBehavior, pub mod types; mod transaction; #[cfg(feature = "load_extension")] mod load_extension_guard; +#[cfg(feature = "named_params")] pub mod named_params; /// A typedef of the result returned by many methods. pub type SqliteResult = Result; @@ -615,15 +616,18 @@ impl<'conn> SqliteStatement<'conn> { unsafe { try!(self.bind_parameters(params)); + self.execute_() + } + } - self.needs_reset = true; - let r = ffi::sqlite3_step(self.stmt); - match r { - ffi::SQLITE_DONE => Ok(self.conn.changes()), - ffi::SQLITE_ROW => Err(SqliteError{ code: r, - message: "Unexpected row result - did you mean to call query?".to_string() }), - _ => Err(self.conn.decode_result(r).unwrap_err()), - } + unsafe fn execute_(&mut self) -> SqliteResult { + let r = ffi::sqlite3_step(self.stmt); + ffi::sqlite3_reset(self.stmt); + match r { + ffi::SQLITE_DONE => Ok(self.conn.changes()), + ffi::SQLITE_ROW => Err(SqliteError{ code: r, + message: "Unexpected row result - did you mean to call query?".to_string() }), + _ => Err(self.conn.decode_result(r).unwrap_err()), } } diff --git a/src/named_params.rs b/src/named_params.rs new file mode 100644 index 0000000..b8e78fa --- /dev/null +++ b/src/named_params.rs @@ -0,0 +1,72 @@ +//use std::collections::HashMap; +use std::ffi::{CString}; +use libc::{c_int}; + +use super::ffi; + +use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement}; +use types::{ToSql}; + +impl SqliteConnection { +} + +impl<'conn> SqliteStatement<'conn> { + /*pub fn parameter_names(&self) -> HashMap { + let n = unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) }; + let mut index_by_name = HashMap::with_capacity(n as usize); + for i in 1..n+1 { + let c_name = unsafe { ffi::sqlite3_bind_parameter_name(self.stmt, i) }; + if !c_name.is_null() { + let c_slice = unsafe { CStr::from_ptr(c_name).to_bytes() }; + index_by_name.insert(str::from_utf8(c_slice).unwrap().to_string(), n); + } + } + index_by_name + }*/ + + /// Return the index of an SQL parameter given its name. + /// Return None if `name` is invalid (NulError) or if no matching parameter is found. + pub fn parameter_index(&self, name: &str) -> Option { + unsafe { + CString::new(name).ok().and_then(|c_name| + match ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) { + 0 => None, // A zero is returned if no matching parameter is found. + n => Some(n) + } + ) + + } + } + + /// Execute the prepared statement with named parameter(s). + /// + /// On success, returns the number of rows that were changed or inserted or deleted (via + /// `sqlite3_changes`). + pub fn named_execute(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult { + unsafe { + for &(name, value) in params { + let i = try!(self.parameter_index(name).ok_or(SqliteError{ + code: ffi::SQLITE_MISUSE, + message: format!("Invalid parameter name: {}", name) + })); + try!(self.conn.decode_result(value.bind_parameter(self.stmt, i))); + } + self.execute_() + } + } +} + +#[cfg(test)] +mod test { + use SqliteConnection; + + #[test] + fn test_named_execute() { + let db = SqliteConnection::open_in_memory().unwrap(); + let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER)"; + db.execute_batch(sql).unwrap(); + + let mut stmt = db.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)").unwrap(); + stmt.named_execute(&[(":name", &"one")]).unwrap(); + } +} \ No newline at end of file From 7ff9d4056ba612b0014ee28c2386ceb7f8557428 Mon Sep 17 00:00:00 2001 From: Gwenael Treguier Date: Wed, 5 Aug 2015 22:07:49 +0200 Subject: [PATCH 03/53] Add SqliteStatement.named_query. --- src/named_params.rs | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index b8e78fa..e196830 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -4,7 +4,7 @@ use libc::{c_int}; use super::ffi; -use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement}; +use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows}; use types::{ToSql}; impl SqliteConnection { @@ -44,16 +44,33 @@ impl<'conn> SqliteStatement<'conn> { /// `sqlite3_changes`). pub fn named_execute(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult { unsafe { - for &(name, value) in params { - let i = try!(self.parameter_index(name).ok_or(SqliteError{ - code: ffi::SQLITE_MISUSE, - message: format!("Invalid parameter name: {}", name) - })); - try!(self.conn.decode_result(value.bind_parameter(self.stmt, i))); - } + try!(self.bind_named_parameters(params)); self.execute_() } } + + /// Execute the prepared statement with named parameter(s), returning an iterator over the resulting rows. + pub fn named_query<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> SqliteResult> { + self.reset_if_needed(); + + unsafe { + try!(self.bind_named_parameters(params)); + } + + self.needs_reset = true; + Ok(SqliteRows::new(self)) + } + + unsafe fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> { + for &(name, value) in params { + let i = try!(self.parameter_index(name).ok_or(SqliteError{ + code: ffi::SQLITE_MISUSE, + message: format!("Invalid parameter name: {}", name) + })); + try!(self.conn.decode_result(value.bind_parameter(self.stmt, i))); + } + Ok(()) + } } #[cfg(test)] @@ -69,4 +86,14 @@ mod test { let mut stmt = db.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)").unwrap(); stmt.named_execute(&[(":name", &"one")]).unwrap(); } + + #[test] + fn test_named_query() { + let db = SqliteConnection::open_in_memory().unwrap(); + let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER)"; + db.execute_batch(sql).unwrap(); + + let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap(); + stmt.named_query(&[(":name", &"one")]).unwrap(); + } } \ No newline at end of file From 3b673a74b2c80f7520d56f5cc7eda322023aabe1 Mon Sep 17 00:00:00 2001 From: Gwenael Treguier Date: Fri, 7 Aug 2015 02:19:57 +0200 Subject: [PATCH 04/53] Add SqliteConnection.execute_named,query_named_row. --- src/named_params.rs | 46 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index e196830..b9a5410 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -4,10 +4,31 @@ use libc::{c_int}; use super::ffi; -use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows}; +use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows, SqliteRow}; use types::{ToSql}; impl SqliteConnection { + /// Convenience method to prepare and execute a single SQL statement with named parameter(s). + pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> SqliteResult { + self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params)) + } + + /// Convenience method to execute a query with named parameter(s) that is expected to return a single row. + /// + /// If the query returns more than one row, all rows except the first are ignored. + pub fn query_named_row(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> SqliteResult + where F: FnOnce(SqliteRow) -> T { + let mut stmt = try!(self.prepare(sql)); + let mut rows = try!(stmt.query_named(params)); + + match rows.next() { + Some(row) => row.map(f), + None => Err(SqliteError{ + code: ffi::SQLITE_NOTICE, + message: "Query did not return a row".to_string(), + }) + } + } } impl<'conn> SqliteStatement<'conn> { @@ -42,7 +63,7 @@ impl<'conn> SqliteStatement<'conn> { /// /// On success, returns the number of rows that were changed or inserted or deleted (via /// `sqlite3_changes`). - pub fn named_execute(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult { + pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult { unsafe { try!(self.bind_named_parameters(params)); self.execute_() @@ -50,7 +71,7 @@ impl<'conn> SqliteStatement<'conn> { } /// Execute the prepared statement with named parameter(s), returning an iterator over the resulting rows. - pub fn named_query<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> SqliteResult> { + pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> SqliteResult> { self.reset_if_needed(); unsafe { @@ -77,23 +98,34 @@ impl<'conn> SqliteStatement<'conn> { mod test { use SqliteConnection; + #[test] + fn test_execute_named() { + let db = SqliteConnection::open_in_memory().unwrap(); + db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); + + assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(), 1); + assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(), 1); + + assert_eq!(3i32, db.query_named_row("SELECT SUM(x) FROM foo WHERE x > :x", &[(":x", &0i32)], |r| r.get(0)).unwrap()); + } + #[test] - fn test_named_execute() { + fn test_stmt_execute_named() { let db = SqliteConnection::open_in_memory().unwrap(); let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER)"; db.execute_batch(sql).unwrap(); let mut stmt = db.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)").unwrap(); - stmt.named_execute(&[(":name", &"one")]).unwrap(); + stmt.execute_named(&[(":name", &"one")]).unwrap(); } #[test] - fn test_named_query() { + fn test_query_named() { let db = SqliteConnection::open_in_memory().unwrap(); let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER)"; db.execute_batch(sql).unwrap(); let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap(); - stmt.named_query(&[(":name", &"one")]).unwrap(); + stmt.query_named(&[(":name", &"one")]).unwrap(); } } \ No newline at end of file From eb7f670ce1bc2a231419c77328619abb554c436a Mon Sep 17 00:00:00 2001 From: Gwenael Treguier Date: Sat, 8 Aug 2015 16:19:05 +0200 Subject: [PATCH 05/53] Make named_params module private --- src/lib.rs | 2 +- src/named_params.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index f2d1791..82c7604 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ pub use transaction::{SqliteTransactionBehavior, pub mod types; mod transaction; #[cfg(feature = "load_extension")] mod load_extension_guard; -#[cfg(feature = "named_params")] pub mod named_params; +#[cfg(feature = "named_params")] mod named_params; /// A typedef of the result returned by many methods. pub type SqliteResult = Result; diff --git a/src/named_params.rs b/src/named_params.rs index b9a5410..32f9732 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -46,6 +46,8 @@ impl<'conn> SqliteStatement<'conn> { }*/ /// Return the index of an SQL parameter given its name. + /// + /// ## Failures /// Return None if `name` is invalid (NulError) or if no matching parameter is found. pub fn parameter_index(&self, name: &str) -> Option { unsafe { From 0a454eed795020bebd4d8c420171798b921646e3 Mon Sep 17 00:00:00 2001 From: Gwenael Treguier Date: Sun, 9 Aug 2015 09:52:53 +0200 Subject: [PATCH 06/53] Add support to user defined scalar functions --- Cargo.toml | 1 + libsqlite3-sys/src/lib.rs | 3 + src/functions.rs | 250 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 255 insertions(+) create mode 100644 src/functions.rs diff --git a/Cargo.toml b/Cargo.toml index 5d05ed5..3241faf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ name = "rusqlite" [features] load_extension = ["libsqlite3-sys/load_extension"] +functions = [] [dependencies] time = "~0.1.0" diff --git a/libsqlite3-sys/src/lib.rs b/libsqlite3-sys/src/lib.rs index 5d37276..3261a7d 100644 --- a/libsqlite3-sys/src/lib.rs +++ b/libsqlite3-sys/src/lib.rs @@ -92,3 +92,6 @@ pub fn code_to_str(code: c_int) -> &'static str { _ => "Unknown error code", } } + +pub const SQLITE_UTF8 : c_int = 1; +pub const SQLITE_DETERMINISTIC : c_int = 0x800; diff --git a/src/functions.rs b/src/functions.rs new file mode 100644 index 0000000..d96f859 --- /dev/null +++ b/src/functions.rs @@ -0,0 +1,250 @@ +//! Create or redefine SQL functions +use std::ffi::{CStr}; +use std::mem; +use std::ptr; +use std::str; +use libc::{c_int, c_double, c_char}; + +use ffi; +pub use ffi::sqlite3_context as sqlite3_context; +pub use ffi::sqlite3_value as sqlite3_value; +pub use ffi::sqlite3_value_type as sqlite3_value_type; +pub use ffi::sqlite3_value_numeric_type as sqlite3_value_numeric_type; + +use types::Null; + +use {SqliteResult, SqliteError, SqliteConnection, str_to_cstring, InnerSqliteConnection}; + +/// A trait for types that can be converted into the result of an SQL function. +pub trait ToResult { + unsafe fn result(&self, ctx: *mut sqlite3_context); +} + +macro_rules! raw_to_impl( + ($t:ty, $f:ident) => ( + impl ToResult for $t { + unsafe fn result(&self, ctx: *mut sqlite3_context) { + ffi::$f(ctx, *self) + } + } + ) +); + +raw_to_impl!(c_int, sqlite3_result_int); +raw_to_impl!(i64, sqlite3_result_int64); +raw_to_impl!(c_double, sqlite3_result_double); + + +impl<'a> ToResult for &'a str { + unsafe fn result(&self, ctx: *mut sqlite3_context) { + let length = self.len(); + if length > ::std::i32::MAX as usize { + ffi::sqlite3_result_error_toobig(ctx); + return + } + match str_to_cstring(self) { + Ok(c_str) => ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, + ffi::SQLITE_TRANSIENT()), + Err(_) => ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE), // TODO sqlite3_result_error + } + } +} + +impl ToResult for String { + unsafe fn result(&self, ctx: *mut sqlite3_context) { + (&self[..]).result(ctx) + } +} + +impl<'a> ToResult for &'a [u8] { + unsafe fn result(&self, ctx: *mut sqlite3_context) { + if self.len() > ::std::i32::MAX as usize { + ffi::sqlite3_result_error_toobig(ctx); + return + } + ffi::sqlite3_result_blob( + ctx, mem::transmute(self.as_ptr()), self.len() as c_int, ffi::SQLITE_TRANSIENT()) + } +} + +impl ToResult for Vec { + unsafe fn result(&self, ctx: *mut sqlite3_context) { + (&self[..]).result(ctx) + } +} + +impl ToResult for Option { + unsafe fn result(&self, ctx: *mut sqlite3_context) { + match *self { + None => ffi::sqlite3_result_null(ctx), + Some(ref t) => t.result(ctx), + } + } +} + +impl ToResult for Null { + unsafe fn result(&self, ctx: *mut sqlite3_context) { + ffi::sqlite3_result_null(ctx) + } +} + + +// sqlite3_result_error_code, c_int +// sqlite3_result_error_nomem +// sqlite3_result_error_toobig +// sqlite3_result_error, *const c_char, c_int +// sqlite3_result_zeroblob +// sqlite3_result_value + +/// A trait for types that can be created from a SQLite function parameter value. +pub trait FromValue { + unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult; + + /// FromValue types can implement this method and use sqlite3_value_type to check that + /// the type reported by SQLite matches a type suitable for Self. This method is used + /// by `???` to confirm that the parameter contains a valid type before + /// attempting to retrieve the value. + unsafe fn parameter_has_valid_sqlite_type(_: *mut sqlite3_value) -> bool { + true + } +} + + +macro_rules! raw_from_impl( + ($t:ty, $f:ident, $c:expr) => ( + impl FromValue for $t { + unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult<$t> { + Ok(ffi::$f(v)) + } + + unsafe fn parameter_has_valid_sqlite_type(v: *mut sqlite3_value) -> bool { + sqlite3_value_numeric_type(v) == $c + } + } + ) +); + +raw_from_impl!(c_int, sqlite3_value_int, ffi::SQLITE_INTEGER); +raw_from_impl!(i64, sqlite3_value_int64, ffi::SQLITE_INTEGER); + +impl FromValue for c_double { + unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult { + Ok(ffi::sqlite3_value_double(v)) + } + + unsafe fn parameter_has_valid_sqlite_type(v: *mut sqlite3_value) -> bool { + sqlite3_value_numeric_type(v) == ffi::SQLITE_FLOAT || sqlite3_value_numeric_type(v) == ffi::SQLITE_INTEGER + } +} + +impl FromValue for String { + unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult { + let c_text = ffi::sqlite3_value_text(v); + if c_text.is_null() { + Ok("".to_string()) + } else { + let c_slice = CStr::from_ptr(c_text as *const c_char).to_bytes(); + let utf8_str = str::from_utf8(c_slice); + utf8_str + .map(|s| { s.to_string() }) + .map_err(|e| { SqliteError{code: 0, message: e.to_string()} }) + } + } + + unsafe fn parameter_has_valid_sqlite_type(v: *mut sqlite3_value) -> bool { + sqlite3_value_type(v) == ffi::SQLITE_TEXT + } +} + +impl FromValue for Vec { + unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult> { + use std::slice::from_raw_parts; + let c_blob = ffi::sqlite3_value_blob(v); + let len = ffi::sqlite3_value_bytes(v); + + assert!(len >= 0, "unexpected negative return from sqlite3_value_bytes"); + let len = len as usize; + + Ok(from_raw_parts(mem::transmute(c_blob), len).to_vec()) + } + + unsafe fn parameter_has_valid_sqlite_type(v: *mut sqlite3_value) -> bool { + sqlite3_value_type(v) == ffi::SQLITE_BLOB + } +} + +impl FromValue for Option { + unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult> { + if sqlite3_value_type(v) == ffi::SQLITE_NULL { + Ok(None) + } else { + FromValue::parameter_value(v).map(|t| Some(t)) + } + } + + unsafe fn parameter_has_valid_sqlite_type(v: *mut sqlite3_value) -> bool { + sqlite3_value_type(v) == ffi::SQLITE_NULL || + T::parameter_has_valid_sqlite_type(v) + } +} + +// sqlite3_user_data +// sqlite3_get_auxdata +// sqlite3_set_auxdata + +pub type ScalarFunc = + Option; + +impl SqliteConnection { + // TODO pApp + pub fn create_scalar_function(&self, fn_name: &str, n_arg: c_int, deterministic: bool, x_func: ScalarFunc) -> SqliteResult<()> { + self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) + } +} + +impl InnerSqliteConnection { + pub fn create_scalar_function(&mut self, fn_name: &str, n_arg: c_int, deterministic: bool, x_func: ScalarFunc) -> SqliteResult<()> { + let c_name = try!(str_to_cstring(fn_name)); + let mut flags = ffi::SQLITE_UTF8; + if deterministic { + flags |= ffi::SQLITE_DETERMINISTIC; + } + let r = unsafe { + ffi::sqlite3_create_function_v2(self.db(), c_name.as_ptr(), n_arg, flags, ptr::null_mut(), x_func, None, None, None) + }; + self.decode_result(r) + } +} + +#[cfg(test)] +mod test { + use libc::{c_int, c_double}; + use SqliteConnection; + use ffi; + use ffi::sqlite3_context as sqlite3_context; + use ffi::sqlite3_value as sqlite3_value; + use functions::{FromValue,ToResult}; + + extern "C" fn half(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { + unsafe { + let arg = *argv.offset(0); + if c_double::parameter_has_valid_sqlite_type(arg) { + let value = c_double::parameter_value(arg).unwrap() / 2f64; + value.result(ctx); + } else { + ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISMATCH); + } + } + } + + #[test] + fn test_half() { + let db = SqliteConnection::open_in_memory().unwrap(); + db.create_scalar_function("half", 1, true, Some(half)).unwrap(); + let result = db.query_row("SELECT half(6)", + &[], + |r| r.get::(0)); + + assert_eq!(3f64, result.unwrap()); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index f98780b..b63df2a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,6 +79,7 @@ pub use transaction::{SqliteTransactionBehavior, pub mod types; mod transaction; #[cfg(feature = "load_extension")] mod load_extension_guard; +#[cfg(feature = "functions")] pub mod functions; /// A typedef of the result returned by many methods. pub type SqliteResult = Result; From b9ab3350eaea7a861b861e3e92e7fb6c0aaf2290 Mon Sep 17 00:00:00 2001 From: Gwenael Treguier Date: Sun, 9 Aug 2015 13:06:23 +0200 Subject: [PATCH 07/53] Add regexp() function implementation --- Cargo.toml | 1 + src/functions.rs | 106 +++++++++++++++++++++++++++++++++++++++++------ src/lib.rs | 1 + src/types.rs | 22 ++++++++++ 4 files changed, 117 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3241faf..366a38e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ libc = "~0.1" [dev-dependencies] tempdir = "~0.3.4" +regex = "~0.1.41" [dependencies.libsqlite3-sys] path = "libsqlite3-sys" diff --git a/src/functions.rs b/src/functions.rs index d96f859..763c7e6 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -17,13 +17,13 @@ use {SqliteResult, SqliteError, SqliteConnection, str_to_cstring, InnerSqliteCon /// A trait for types that can be converted into the result of an SQL function. pub trait ToResult { - unsafe fn result(&self, ctx: *mut sqlite3_context); + unsafe fn set_result(&self, ctx: *mut sqlite3_context); } macro_rules! raw_to_impl( ($t:ty, $f:ident) => ( impl ToResult for $t { - unsafe fn result(&self, ctx: *mut sqlite3_context) { + unsafe fn set_result(&self, ctx: *mut sqlite3_context) { ffi::$f(ctx, *self) } } @@ -34,9 +34,18 @@ raw_to_impl!(c_int, sqlite3_result_int); raw_to_impl!(i64, sqlite3_result_int64); raw_to_impl!(c_double, sqlite3_result_double); +impl<'a> ToResult for bool { + unsafe fn set_result(&self, ctx: *mut sqlite3_context) { + match *self { + true => ffi::sqlite3_result_int(ctx, 1), + _ => ffi::sqlite3_result_int(ctx, 0), + } + } +} + impl<'a> ToResult for &'a str { - unsafe fn result(&self, ctx: *mut sqlite3_context) { + unsafe fn set_result(&self, ctx: *mut sqlite3_context) { let length = self.len(); if length > ::std::i32::MAX as usize { ffi::sqlite3_result_error_toobig(ctx); @@ -51,13 +60,13 @@ impl<'a> ToResult for &'a str { } impl ToResult for String { - unsafe fn result(&self, ctx: *mut sqlite3_context) { - (&self[..]).result(ctx) + unsafe fn set_result(&self, ctx: *mut sqlite3_context) { + (&self[..]).set_result(ctx) } } impl<'a> ToResult for &'a [u8] { - unsafe fn result(&self, ctx: *mut sqlite3_context) { + unsafe fn set_result(&self, ctx: *mut sqlite3_context) { if self.len() > ::std::i32::MAX as usize { ffi::sqlite3_result_error_toobig(ctx); return @@ -68,22 +77,22 @@ impl<'a> ToResult for &'a [u8] { } impl ToResult for Vec { - unsafe fn result(&self, ctx: *mut sqlite3_context) { - (&self[..]).result(ctx) + unsafe fn set_result(&self, ctx: *mut sqlite3_context) { + (&self[..]).set_result(ctx) } } impl ToResult for Option { - unsafe fn result(&self, ctx: *mut sqlite3_context) { + unsafe fn set_result(&self, ctx: *mut sqlite3_context) { match *self { None => ffi::sqlite3_result_null(ctx), - Some(ref t) => t.result(ctx), + Some(ref t) => t.set_result(ctx), } } } impl ToResult for Null { - unsafe fn result(&self, ctx: *mut sqlite3_context) { + unsafe fn set_result(&self, ctx: *mut sqlite3_context) { ffi::sqlite3_result_null(ctx) } } @@ -127,6 +136,19 @@ macro_rules! raw_from_impl( raw_from_impl!(c_int, sqlite3_value_int, ffi::SQLITE_INTEGER); raw_from_impl!(i64, sqlite3_value_int64, ffi::SQLITE_INTEGER); +impl FromValue for bool { + unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult { + match ffi::sqlite3_value_int(v) { + 0 => Ok(false), + _ => Ok(true), + } + } + + unsafe fn parameter_has_valid_sqlite_type(v: *mut sqlite3_value) -> bool { + sqlite3_value_numeric_type(v) == ffi::SQLITE_INTEGER + } +} + impl FromValue for c_double { unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult { Ok(ffi::sqlite3_value_double(v)) @@ -218,7 +240,14 @@ impl InnerSqliteConnection { #[cfg(test)] mod test { - use libc::{c_int, c_double}; + extern crate regex; + + use std::boxed::Box; + use std::ffi::{CString}; + use std::mem; + use libc::{c_int, c_double, c_void}; + use self::regex::Regex; + use SqliteConnection; use ffi; use ffi::sqlite3_context as sqlite3_context; @@ -230,7 +259,7 @@ mod test { let arg = *argv.offset(0); if c_double::parameter_has_valid_sqlite_type(arg) { let value = c_double::parameter_value(arg).unwrap() / 2f64; - value.result(ctx); + value.set_result(ctx); } else { ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISMATCH); } @@ -247,4 +276,55 @@ mod test { assert_eq!(3f64, result.unwrap()); } + + extern "C" fn regexp_free(raw: *mut c_void) { + unsafe { + Box::from_raw(raw); + } + } + + extern "C" fn regexp(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { + unsafe { + let mut re_ptr = ffi::sqlite3_get_auxdata(ctx, 0) as *const Regex; + let mut re_opt = None; + if re_ptr.is_null() { + let raw = String::parameter_value(*argv.offset(0)); + if raw.is_err() { + let msg = CString::new(format!("{}", raw.unwrap_err())).unwrap(); + ffi::sqlite3_result_error(ctx, msg.as_ptr(), -1); + return + } + let comp = Regex::new(raw.unwrap().as_ref()); + if comp.is_err() { + let msg = CString::new(format!("{}", comp.unwrap_err())).unwrap(); + ffi::sqlite3_result_error(ctx, msg.as_ptr(), -1); + return + } + let re = comp.unwrap(); + re_ptr = &re as *const Regex; + re_opt = Some(re); + } + + let text = String::parameter_value(*argv.offset(1)); + if text.is_ok() { + let text = text.unwrap(); + (*re_ptr).is_match(text.as_ref()).set_result(ctx); + } + + if re_opt.is_some() { + ffi::sqlite3_set_auxdata(ctx, 0, mem::transmute(Box::into_raw(Box::new(re_opt.unwrap()))), Some(regexp_free)); + } + } + } + + #[test] + fn test_regexp() { + let db = SqliteConnection::open_in_memory().unwrap(); + db.create_scalar_function("regexp", 2, true, Some(regexp)).unwrap(); + let result = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", + &[], + |r| r.get::(0)); + + assert_eq!(true, result.unwrap()); + } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b63df2a..ce31617 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,7 @@ //! } //! } //! ``` +#![cfg_attr(test, feature(box_raw))] extern crate libc; extern crate libsqlite3_sys as ffi; #[macro_use] extern crate bitflags; diff --git a/src/types.rs b/src/types.rs index 1d30b30..ea8c6ac 100644 --- a/src/types.rs +++ b/src/types.rs @@ -100,6 +100,15 @@ raw_to_impl!(c_int, sqlite3_bind_int); raw_to_impl!(i64, sqlite3_bind_int64); raw_to_impl!(c_double, sqlite3_bind_double); +impl ToSql for bool { + unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { + match *self { + true => ffi::sqlite3_bind_int(stmt, col, 1), + _ => ffi::sqlite3_bind_int(stmt, col, 0), + } + } +} + impl<'a> ToSql for &'a str { unsafe fn bind_parameter(&self, stmt: *mut sqlite3_stmt, col: c_int) -> c_int { match str_to_cstring(self) { @@ -188,6 +197,19 @@ raw_from_impl!(c_int, sqlite3_column_int, ffi::SQLITE_INTEGER); raw_from_impl!(i64, sqlite3_column_int64, ffi::SQLITE_INTEGER); raw_from_impl!(c_double, sqlite3_column_double, ffi::SQLITE_FLOAT); +impl FromSql for bool { + unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> SqliteResult { + match ffi::sqlite3_column_int(stmt, col) { + 0 => Ok(false), + _ => Ok(true), + } + } + + unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { + sqlite3_column_type(stmt, col) == ffi::SQLITE_INTEGER + } +} + impl FromSql for String { unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> SqliteResult { let c_text = ffi::sqlite3_column_text(stmt, col); From ddd976c158ed456d078ebf1066bba4768fbc4f70 Mon Sep 17 00:00:00 2001 From: Gwenael Treguier Date: Wed, 11 Nov 2015 14:39:54 +0100 Subject: [PATCH 08/53] Cleanup use statements. --- src/named_params.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index 32f9732..e103bf7 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -1,11 +1,11 @@ //use std::collections::HashMap; -use std::ffi::{CString}; -use libc::{c_int}; +use std::ffi::CString; +use libc::c_int; use super::ffi; use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows, SqliteRow}; -use types::{ToSql}; +use types::ToSql; impl SqliteConnection { /// Convenience method to prepare and execute a single SQL statement with named parameter(s). @@ -130,4 +130,4 @@ mod test { let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap(); stmt.query_named(&[(":name", &"one")]).unwrap(); } -} \ No newline at end of file +} From 7fbe1172e2e71c7f993bf70d4756679e67ac1155 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Tue, 1 Dec 2015 11:33:48 -0500 Subject: [PATCH 09/53] Make libsqlite3-sys's build script slightly more intelligent. * If SQLITE3_LIB_DIR is present in the environment, we use that. * If SQLITE3_LIB_DIR is not present, we try to use pkg-config. * If SQLITE3_LIB_DIR is not present and pkg-config fails, we fall back to /usr/lib (if it exists). --- CONTRIBUTORS.md | 1 + Changelog.md | 2 ++ libsqlite3-sys/build.rs | 23 ++++++++++++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index eca404b..aa0ffeb 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -9,3 +9,4 @@ rusqlite contributors (sorted alphabetically) * [Patrick Fernie](https://github.com/pfernie) * [Steve Klabnik](https://github.com/steveklabnik) * [krdln](https://github.com/krdln) +* [Andrew Straw](https://github.com/astraw) diff --git a/Changelog.md b/Changelog.md index 0af134a..4b341b4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,8 @@ * Slight change to the closure types passed to `query_map` and `query_and_then`: * Remove the `'static` requirement on the closure's output type. * Give the closure a `&SqliteRow` instead of a `SqliteRow`. +* When building, the environment variable `SQLITE3_LIB_DIR` now takes precedence over pkg-config. +* If `pkg-config` is not available, we will try to find `libsqlite3` in `/usr/lib`. # Version 0.4.0 (2015-11-03) diff --git a/libsqlite3-sys/build.rs b/libsqlite3-sys/build.rs index c5e1e47..a1cdad4 100644 --- a/libsqlite3-sys/build.rs +++ b/libsqlite3-sys/build.rs @@ -1,5 +1,26 @@ extern crate pkg_config; +use std::env; +use std::fs; + fn main() { - pkg_config::find_library("sqlite3").unwrap(); + // Allow users to specify where to find SQLite. + let lib_dir = match env::var("SQLITE3_LIB_DIR") { + Ok(dir) => dir, + Err(_) => { + // See if pkg-config can do everything for us. + if pkg_config::find_library("sqlite3").is_ok() { + return + } + + // Try to fall back to /usr/lib if pkg-config failed. + match fs::metadata("/usr/lib") { + Ok(ref attr) if attr.is_dir() => "/usr/lib".to_owned(), + _ => panic!("Could not find sqlite3. Try setting SQLITE3_LIB_DIR."), + } + }, + }; + + println!("cargo:rustc-link-lib=sqlite3"); + println!("cargo:rustc-link-search={}", lib_dir); } From da69584b9f26c2191aecfd6332358df70ba4244b Mon Sep 17 00:00:00 2001 From: Gwenael Treguier Date: Sat, 5 Dec 2015 13:43:03 +0100 Subject: [PATCH 10/53] Add Example and Failure documentation --- src/named_params.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/named_params.rs b/src/named_params.rs index e103bf7..eb70989 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -9,6 +9,20 @@ use types::ToSql; impl SqliteConnection { /// Convenience method to prepare and execute a single SQL statement with named parameter(s). + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{SqliteConnection, SqliteResult}; + /// fn insert(conn: &SqliteConnection) -> SqliteResult { + /// conn.execute_named("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)", &[(":name", &"one")]) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the + /// underlying SQLite call fails. pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> SqliteResult { self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params)) } @@ -16,6 +30,11 @@ impl SqliteConnection { /// Convenience method to execute a query with named parameter(s) that is expected to return a single row. /// /// If the query returns more than one row, all rows except the first are ignored. + /// + /// # Failure + /// + /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the + /// underlying SQLite call fails. pub fn query_named_row(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> SqliteResult where F: FnOnce(SqliteRow) -> T { let mut stmt = try!(self.prepare(sql)); @@ -47,7 +66,8 @@ impl<'conn> SqliteStatement<'conn> { /// Return the index of an SQL parameter given its name. /// - /// ## Failures + /// # Failure + /// /// Return None if `name` is invalid (NulError) or if no matching parameter is found. pub fn parameter_index(&self, name: &str) -> Option { unsafe { @@ -65,6 +85,21 @@ impl<'conn> SqliteStatement<'conn> { /// /// On success, returns the number of rows that were changed or inserted or deleted (via /// `sqlite3_changes`). + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{SqliteConnection, SqliteResult}; + /// fn insert(conn: &SqliteConnection) -> SqliteResult { + /// let mut stmt = try!(conn.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)")); + /// return stmt.execute_named(&[(":name", &"one")]); + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if binding parameters fails, the executed statement returns rows (in + /// which case `query` should be used instead), or the underling SQLite call fails. pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult { unsafe { try!(self.bind_named_parameters(params)); @@ -73,6 +108,24 @@ impl<'conn> SqliteStatement<'conn> { } /// Execute the prepared statement with named parameter(s), returning an iterator over the resulting rows. + /// + /// ## Example + /// + /// ```rust,no_run + /// # use rusqlite::{SqliteConnection, SqliteResult, SqliteRows}; + /// fn query(conn: &SqliteConnection) -> SqliteResult<()> { + /// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name")); + /// let mut rows = try!(stmt.query_named(&[(":name", &"one")])); + /// for row in rows { + /// // ... + /// } + /// return Ok(()) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return `Err` if binding parameters fails. pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> SqliteResult> { self.reset_if_needed(); From 51a542c0f7427cdf8c1ec159731fd7dc18ac7d3d Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Tue, 8 Dec 2015 21:15:23 -0500 Subject: [PATCH 11/53] Fix broken load_extension feature --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 6332b45..885ead2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -484,13 +484,14 @@ impl SqliteConnection { /// /// conn.load_extension("my_sqlite_extension", None) /// } + /// ``` /// /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. #[cfg(feature = "load_extension")] pub fn load_extension>(&self, dylib_path: P, entry_point: Option<&str>) -> SqliteResult<()> { - self.db.borrow_mut().load_extension(dylib_path, entry_point) + self.db.borrow_mut().load_extension(dylib_path.as_ref(), entry_point) } fn decode_result(&self, code: c_int) -> SqliteResult<()> { From e71104e6c30da967bb67e49628d82ad25ebc931c Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Tue, 8 Dec 2015 21:30:19 -0500 Subject: [PATCH 12/53] Bump version to 0.5.0 --- Cargo.toml | 4 ++-- Changelog.md | 3 ++- libsqlite3-sys/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 673c8ea..9025a95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusqlite" -version = "0.4.0" +version = "0.5.0" authors = ["John Gallagher "] description = "Ergonomic wrapper for SQLite" repository = "https://github.com/jgallagher/rusqlite" @@ -27,7 +27,7 @@ lazy_static = "~0.1" [dependencies.libsqlite3-sys] path = "libsqlite3-sys" -version = "0.2.0" +version = "0.3.0" [[test]] name = "config_log" diff --git a/Changelog.md b/Changelog.md index 4efd2e8..436e5d7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -# Version UPCOMING (TBD) +# Version 0.5.0 (2015-12-08) * Adds `trace` feature that allows the use of SQLite's logging, tracing, and profiling hooks. * Slight change to the closure types passed to `query_map` and `query_and_then`: @@ -7,6 +7,7 @@ * When building, the environment variable `SQLITE3_LIB_DIR` now takes precedence over pkg-config. * If `pkg-config` is not available, we will try to find `libsqlite3` in `/usr/lib`. * Add more documentation for failure modes of functions that return `SqliteResult`s. +* Updates `libc` dependency to 0.2, fixing builds on ARM for Rust 1.6 or newer. # Version 0.4.0 (2015-11-03) diff --git a/libsqlite3-sys/Cargo.toml b/libsqlite3-sys/Cargo.toml index cafa632..6be5973 100644 --- a/libsqlite3-sys/Cargo.toml +++ b/libsqlite3-sys/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "libsqlite3-sys" -version = "0.2.0" +version = "0.3.0" authors = ["John Gallagher "] repository = "https://github.com/jgallagher/rusqlite" description = "Native bindings to the libsqlite3 library" From e2f8e73635fa8b26ea2d8a043e960f00e12aecf5 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Wed, 9 Dec 2015 00:19:59 -0500 Subject: [PATCH 13/53] Initial implementation of the online backup API. --- Cargo.toml | 1 + Changelog.md | 4 + src/backup.rs | 325 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 4 files changed, 331 insertions(+) create mode 100644 src/backup.rs diff --git a/Cargo.toml b/Cargo.toml index 9025a95..7cdf204 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ name = "rusqlite" [features] load_extension = ["libsqlite3-sys/load_extension"] +backup = [] trace = [] [dependencies] diff --git a/Changelog.md b/Changelog.md index 436e5d7..822668b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# Version UPCOMING (TBD) + +* Adds `backup` feature that exposes SQLite's online backup API. + # Version 0.5.0 (2015-12-08) * Adds `trace` feature that allows the use of SQLite's logging, tracing, and profiling hooks. diff --git a/src/backup.rs b/src/backup.rs new file mode 100644 index 0000000..666e31b --- /dev/null +++ b/src/backup.rs @@ -0,0 +1,325 @@ +//! Online SQLite backup API. +//! +//! To create a `Backup`, you must have two distinct `SqliteConnection`s - one +//! for the source (which can be used while the backup is running) and one for +//! the destination (which cannot). A `Backup` handle exposes three methods: +//! `step` will attempt to back up a specified number of pages, `progress` gets +//! the current progress of the backup as of the last call to `step`, and +//! `run_to_completion` will attempt to back up the entire source database, +//! allowing you to specify how many pages are backed up at a time and how long +//! the thread should sleep between chunks of pages. +//! +//! The following example is equivalent to "Example 2: Online Backup of a +//! Running Database" from [SQLite's Online Backup API +//! documentation](https://www.sqlite.org/backup.html). +//! +//! ```rust,no_run +//! # use rusqlite::{backup, SqliteConnection, SqliteResult}; +//! # use std::path::Path; +//! # use std::time; +//! +//! fn backupDb>(src: &SqliteConnection, dst: P, progress: fn(backup::Progress)) +//! -> SqliteResult<()> { +//! let mut dst = try!(SqliteConnection::open(dst)); +//! let backup = try!(backup::Backup::new(src, &mut dst)); +//! backup.run_to_completion(5, time::Duration::from_millis(250), Some(progress)) +//! } +//! ``` + +use std::ffi::CString; +use std::marker::PhantomData; + +use libc::c_int; +use std::thread; +use std::time::Duration; + +use ffi; + +use {SqliteConnection, SqliteError, SqliteResult, str_to_cstring}; + +/// Possible successful results of calling `Backup::step`. +pub enum StepResult { + /// The backup is complete. + Done, + + /// The step was successful but there are still more pages that need to be backed up. + More, + + /// The step failed because appropriate locks could not be aquired. This is + /// not a fatal error - the step can be retried. + Busy, + + /// The step failed because the source connection was writing to the + /// database. This is not a fatal error - the step can be retried. + Locked, +} + +/// Name for the database to back up. Can be specified for both the source and +/// destination. +pub enum BackupName { + /// The main database. This is typically what you want. + Main, + /// Back up the temporary database (e.g., any "CREATE TEMPORARY TABLE" tables). + Temp, + /// Backup a database that has been attached via "ATTACH DATABASE ...". + Attached(String), +} + +impl BackupName { + fn to_cstring(self) -> SqliteResult { + match self { + BackupName::Main => str_to_cstring("main"), + BackupName::Temp => str_to_cstring("temp"), + BackupName::Attached(s) => str_to_cstring(&s), + } + } +} + +/// Struct specifying the progress of a backup. The percentage completion can +/// be calculated as `(pagecount - remaining) / pagecount`. The progress of a +/// 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)] +pub struct Progress { + /// Number of pages in the source database that still need to be backed up. + pub remaining: c_int, + /// Total number of pages in the source database. + pub pagecount: c_int, +} + +/// A handle to an online backup. +pub struct Backup<'a, 'b> { + phantom_from: PhantomData<&'a ()>, + phantom_to: PhantomData<&'b ()>, + b: *mut ffi::sqlite3_backup, +} + +impl<'a, 'b> Backup<'a, 'b> { + /// Attempt to create a new handle that will allow backups from `from` to + /// `to`. Note that `to` is a `&mut` - this is because SQLite forbids any + /// API calls on the destination of a backup while the backup is taking + /// place. + /// + /// # Failure + /// + /// Will return `Err` if the underlying `sqlite3_backup_init` call returns + /// `NULL`. + pub fn new(from: &'a SqliteConnection, + to: &'b mut SqliteConnection) -> SqliteResult> { + Backup::new_with_names(from, BackupName::Main, to, BackupName::Main) + } + + /// Attempt to create a new handle that will allow backups from the + /// `from_name` database of `from` to the `to_name` database of `to`. Note + /// that `to` is a `&mut` - this is because SQLite forbids any API calls on + /// the destination of a backup while the backup is taking place. + /// + /// # Failure + /// + /// Will return `Err` if the underlying `sqlite3_backup_init` call returns + /// `NULL`. + pub fn new_with_names(from: &'a SqliteConnection, from_name: BackupName, + to: &'b mut SqliteConnection, to_name: BackupName) + -> SqliteResult> + { + 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()); + if b.is_null() { + return Err(SqliteError::from_handle(to_db, ffi::sqlite3_errcode(to_db))); + } + b + }; + + Ok(Backup{ + phantom_from: PhantomData, + phantom_to: PhantomData, + b: b, + }) + } + + /// Gets the progress of the backup as of the last call to `step`. + pub fn progress(&self) -> Progress { + unsafe { + Progress{ + remaining: ffi::sqlite3_backup_remaining(self.b), + pagecount: ffi::sqlite3_backup_pagecount(self.b), + } + } + } + + /// Attempts to back up the given number of pages. If `num_pages` is + /// negative, will attempt to back up all remaining pages. This will hold a + /// lock on the source database for the duration, so it is probably not + /// what you want for databases that are currently active (see + /// `run_to_completion` for a better alternative). + /// + /// # Failure + /// + /// Will return `Err` if the underlying `sqlite3_backup_step` call returns + /// an error code other than `DONE`, `OK`, `BUSY`, or `LOCKED`. `BUSY` and + /// `LOCKED` are transient errors and are therefore returned as possible + /// `Ok` values. + pub fn step(&self, num_pages: c_int) -> SqliteResult { + use self::StepResult::{Done, More, Busy, Locked}; + + let rc = unsafe { + ffi::sqlite3_backup_step(self.b, num_pages) + }; + match rc { + ffi::SQLITE_DONE => Ok(Done), + ffi::SQLITE_OK => Ok(More), + ffi::SQLITE_BUSY => Ok(Busy), + ffi::SQLITE_LOCKED => Ok(Locked), + rc => + Err(SqliteError{ code: rc, message: ffi::code_to_str(rc).into() }) + } + } + + /// Attempts to run the entire backup. Will call `step(pages_per_step)` as + /// many times as necessary, sleeping for `pause_between_pages` between + /// each call to give the source database time to process any pending + /// queries. This is a direct implementation of "Example 2: Online Backup + /// of a Running Database" from [SQLite's Online Backup API + /// documentation](https://www.sqlite.org/backup.html). + /// + /// If `progress` is not `None`, it will be called after each step with the + /// current progress of the backup. Note that is possible the progress may + /// not change if the step returns `Busy` or `Locked` even though the + /// backup is still running. + /// + /// # 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) -> SqliteResult<()> { + use self::StepResult::{Done, More, Busy, Locked}; + + assert!(pages_per_step > 0, "pages_per_step must be positive"); + + loop { + let r = try!(self.step(pages_per_step)); + if let Some(progress) = progress { + progress(self.progress()) + } + match r { + More | Busy | Locked => thread::sleep(pause_between_pages), + Done => return Ok(()), + } + } + } +} + +impl<'a, 'b> Drop for Backup<'a, 'b> { + fn drop(&mut self) { + unsafe { ffi::sqlite3_backup_finish(self.b) }; + } +} + +#[cfg(test)] +mod test { + use SqliteConnection; + use std::time::Duration; + use super::{Backup, BackupName}; + + #[test] + fn test_backup() { + let src = SqliteConnection::open_in_memory().unwrap(); + let sql = "BEGIN; + CREATE TABLE foo(x INTEGER); + INSERT INTO foo VALUES(42); + END;"; + src.execute_batch(sql).unwrap(); + + let mut dst = SqliteConnection::open_in_memory().unwrap(); + + { + let backup = Backup::new(&src, &mut dst).unwrap(); + backup.step(-1).unwrap(); + } + + let the_answer = 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(); + } + + let the_answer = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get::(0)).unwrap(); + assert_eq!(42 + 43, the_answer); + } + + #[test] + fn test_backup_temp() { + let src = SqliteConnection::open_in_memory().unwrap(); + let sql = "BEGIN; + CREATE TEMPORARY TABLE foo(x INTEGER); + INSERT INTO foo VALUES(42); + END;"; + src.execute_batch(sql).unwrap(); + + let mut dst = SqliteConnection::open_in_memory().unwrap(); + + { + let backup = Backup::new_with_names(&src, BackupName::Temp, &mut dst, BackupName::Main) + .unwrap(); + backup.step(-1).unwrap(); + } + + let the_answer = 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, BackupName::Temp, &mut dst, BackupName::Main) + .unwrap(); + backup.run_to_completion(5, Duration::from_millis(250), None).unwrap(); + } + + let the_answer = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get::(0)).unwrap(); + assert_eq!(42 + 43, the_answer); + } + + #[test] + fn test_backup_attached() { + let src = SqliteConnection::open_in_memory().unwrap(); + let sql = "ATTACH DATABASE ':memory:' AS my_attached; + BEGIN; + CREATE TABLE my_attached.foo(x INTEGER); + INSERT INTO my_attached.foo VALUES(42); + END;"; + src.execute_batch(sql).unwrap(); + + let mut dst = SqliteConnection::open_in_memory().unwrap(); + + { + let backup = Backup::new_with_names(&src, BackupName::Attached("my_attached".into()), + &mut dst, BackupName::Main).unwrap(); + backup.step(-1).unwrap(); + } + + let the_answer = 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, BackupName::Attached("my_attached".into()), + &mut dst, BackupName::Main).unwrap(); + backup.run_to_completion(5, Duration::from_millis(250), None).unwrap(); + } + + let the_answer = dst.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get::(0)).unwrap(); + assert_eq!(42 + 43, the_answer); + } +} diff --git a/src/lib.rs b/src/lib.rs index 885ead2..cbd50e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,6 +82,7 @@ pub mod types; mod transaction; #[cfg(feature = "load_extension")] mod load_extension_guard; #[cfg(feature = "trace")] pub mod trace; +#[cfg(feature = "backup")] pub mod backup; /// A typedef of the result returned by many methods. pub type SqliteResult = Result; From 5ebbf5a1a2d1b7d7c66930376b494b0f06463f71 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Wed, 9 Dec 2015 00:48:40 -0500 Subject: [PATCH 14/53] Use &str instead of String inside BackupName --- src/backup.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/backup.rs b/src/backup.rs index 666e31b..9fceeac 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -56,21 +56,21 @@ pub enum StepResult { /// Name for the database to back up. Can be specified for both the source and /// destination. -pub enum BackupName { +pub enum BackupName<'a> { /// The main database. This is typically what you want. Main, /// Back up the temporary database (e.g., any "CREATE TEMPORARY TABLE" tables). Temp, /// Backup a database that has been attached via "ATTACH DATABASE ...". - Attached(String), + Attached(&'a str), } -impl BackupName { +impl<'a> BackupName<'a> { fn to_cstring(self) -> SqliteResult { match self { BackupName::Main => str_to_cstring("main"), BackupName::Temp => str_to_cstring("temp"), - BackupName::Attached(s) => str_to_cstring(&s), + BackupName::Attached(s) => str_to_cstring(s), } } } @@ -303,7 +303,7 @@ mod test { let mut dst = SqliteConnection::open_in_memory().unwrap(); { - let backup = Backup::new_with_names(&src, BackupName::Attached("my_attached".into()), + let backup = Backup::new_with_names(&src, BackupName::Attached("my_attached"), &mut dst, BackupName::Main).unwrap(); backup.step(-1).unwrap(); } @@ -314,7 +314,7 @@ mod test { src.execute_batch("INSERT INTO foo VALUES(43)").unwrap(); { - let backup = Backup::new_with_names(&src, BackupName::Attached("my_attached".into()), + let backup = Backup::new_with_names(&src, BackupName::Attached("my_attached"), &mut dst, BackupName::Main).unwrap(); backup.run_to_completion(5, Duration::from_millis(250), None).unwrap(); } From 8c2e0a0da7674870dda1c0d2d3777bb6bfc4de90 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Wed, 9 Dec 2015 16:27:18 -0500 Subject: [PATCH 15/53] Rename BackupName -> DatabaseName --- src/backup.rs | 46 ++++++++++++---------------------------------- src/lib.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/src/backup.rs b/src/backup.rs index 9fceeac..03e425c 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -26,7 +26,6 @@ //! } //! ``` -use std::ffi::CString; use std::marker::PhantomData; use libc::c_int; @@ -35,7 +34,7 @@ use std::time::Duration; use ffi; -use {SqliteConnection, SqliteError, SqliteResult, str_to_cstring}; +use {DatabaseName, SqliteConnection, SqliteError, SqliteResult}; /// Possible successful results of calling `Backup::step`. pub enum StepResult { @@ -54,27 +53,6 @@ pub enum StepResult { Locked, } -/// Name for the database to back up. Can be specified for both the source and -/// destination. -pub enum BackupName<'a> { - /// The main database. This is typically what you want. - Main, - /// Back up the temporary database (e.g., any "CREATE TEMPORARY TABLE" tables). - Temp, - /// Backup a database that has been attached via "ATTACH DATABASE ...". - Attached(&'a str), -} - -impl<'a> BackupName<'a> { - fn to_cstring(self) -> SqliteResult { - match self { - BackupName::Main => str_to_cstring("main"), - BackupName::Temp => str_to_cstring("temp"), - BackupName::Attached(s) => str_to_cstring(s), - } - } -} - /// Struct specifying the progress of a backup. The percentage completion can /// be calculated as `(pagecount - remaining) / pagecount`. The progress of a /// backup is as of the last call to `step` - if the source database is @@ -107,7 +85,7 @@ impl<'a, 'b> Backup<'a, 'b> { /// `NULL`. pub fn new(from: &'a SqliteConnection, to: &'b mut SqliteConnection) -> SqliteResult> { - Backup::new_with_names(from, BackupName::Main, to, BackupName::Main) + Backup::new_with_names(from, DatabaseName::Main, to, DatabaseName::Main) } /// Attempt to create a new handle that will allow backups from the @@ -119,8 +97,8 @@ 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 SqliteConnection, from_name: BackupName, - to: &'b mut SqliteConnection, to_name: BackupName) + pub fn new_with_names(from: &'a SqliteConnection, from_name: DatabaseName, + to: &'b mut SqliteConnection, to_name: DatabaseName) -> SqliteResult> { let to_name = try!(to_name.to_cstring()); @@ -224,9 +202,9 @@ impl<'a, 'b> Drop for Backup<'a, 'b> { #[cfg(test)] mod test { - use SqliteConnection; + use {SqliteConnection, DatabaseName}; use std::time::Duration; - use super::{Backup, BackupName}; + use super::Backup; #[test] fn test_backup() { @@ -270,7 +248,7 @@ mod test { let mut dst = SqliteConnection::open_in_memory().unwrap(); { - let backup = Backup::new_with_names(&src, BackupName::Temp, &mut dst, BackupName::Main) + let backup = Backup::new_with_names(&src, DatabaseName::Temp, &mut dst, DatabaseName::Main) .unwrap(); backup.step(-1).unwrap(); } @@ -281,7 +259,7 @@ mod test { src.execute_batch("INSERT INTO foo VALUES(43)").unwrap(); { - let backup = Backup::new_with_names(&src, BackupName::Temp, &mut dst, BackupName::Main) + 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(); } @@ -303,8 +281,8 @@ mod test { let mut dst = SqliteConnection::open_in_memory().unwrap(); { - let backup = Backup::new_with_names(&src, BackupName::Attached("my_attached"), - &mut dst, BackupName::Main).unwrap(); + let backup = Backup::new_with_names(&src, DatabaseName::Attached("my_attached"), + &mut dst, DatabaseName::Main).unwrap(); backup.step(-1).unwrap(); } @@ -314,8 +292,8 @@ mod test { src.execute_batch("INSERT INTO foo VALUES(43)").unwrap(); { - let backup = Backup::new_with_names(&src, BackupName::Attached("my_attached"), - &mut dst, BackupName::Main).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(); } diff --git a/src/lib.rs b/src/lib.rs index cbd50e1..b43c6be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,6 +143,32 @@ fn path_to_cstring(p: &Path) -> SqliteResult { str_to_cstring(s) } +/// Name for a database within a SQLite connection. +pub enum DatabaseName<'a> { + /// The main database. + Main, + + /// The temporary database (e.g., any "CREATE TEMPORARY TABLE" tables). + Temp, + + /// A database that has been attached via "ATTACH DATABASE ...". + Attached(&'a str), +} + +// Currently DatabaseName is only used by the backup mod, so hide this (private) +// impl to avoid dead code warnings. +#[cfg(feature = "backup")] +impl<'a> DatabaseName<'a> { + fn to_cstring(self) -> SqliteResult { + use self::DatabaseName::{Main, Temp, Attached}; + match self { + Main => str_to_cstring("main"), + Temp => str_to_cstring("temp"), + Attached(s) => str_to_cstring(s), + } + } +} + /// A connection to a SQLite database. pub struct SqliteConnection { db: RefCell, From 3781b8f47f1478fed64ae32a6b754e49086be53e Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 13:13:15 -0500 Subject: [PATCH 16/53] rustfmt backup.rs --- src/backup.rs | 78 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/src/backup.rs b/src/backup.rs index 03e425c..5e7ddf0 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -84,7 +84,8 @@ impl<'a, 'b> Backup<'a, 'b> { /// Will return `Err` if the underlying `sqlite3_backup_init` call returns /// `NULL`. pub fn new(from: &'a SqliteConnection, - to: &'b mut SqliteConnection) -> SqliteResult> { + to: &'b mut SqliteConnection) + -> SqliteResult> { Backup::new_with_names(from, DatabaseName::Main, to, DatabaseName::Main) } @@ -97,25 +98,28 @@ 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 SqliteConnection, from_name: DatabaseName, - to: &'b mut SqliteConnection, to_name: DatabaseName) - -> SqliteResult> - { + pub fn new_with_names(from: &'a SqliteConnection, + from_name: DatabaseName, + to: &'b mut SqliteConnection, + to_name: DatabaseName) + -> SqliteResult> { 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(SqliteError::from_handle(to_db, ffi::sqlite3_errcode(to_db))); } b }; - Ok(Backup{ + Ok(Backup { phantom_from: PhantomData, phantom_to: PhantomData, b: b, @@ -125,7 +129,7 @@ impl<'a, 'b> Backup<'a, 'b> { /// Gets the progress of the backup as of the last call to `step`. pub fn progress(&self) -> Progress { unsafe { - Progress{ + Progress { remaining: ffi::sqlite3_backup_remaining(self.b), pagecount: ffi::sqlite3_backup_pagecount(self.b), } @@ -147,16 +151,18 @@ impl<'a, 'b> Backup<'a, 'b> { pub fn step(&self, num_pages: c_int) -> SqliteResult { use self::StepResult::{Done, More, Busy, Locked}; - let rc = unsafe { - ffi::sqlite3_backup_step(self.b, num_pages) - }; + let rc = unsafe { ffi::sqlite3_backup_step(self.b, num_pages) }; match rc { - ffi::SQLITE_DONE => Ok(Done), - ffi::SQLITE_OK => Ok(More), - ffi::SQLITE_BUSY => Ok(Busy), + ffi::SQLITE_DONE => Ok(Done), + ffi::SQLITE_OK => Ok(More), + ffi::SQLITE_BUSY => Ok(Busy), ffi::SQLITE_LOCKED => Ok(Locked), - rc => - Err(SqliteError{ code: rc, message: ffi::code_to_str(rc).into() }) + rc => { + Err(SqliteError { + code: rc, + message: ffi::code_to_str(rc).into(), + }) + } } } @@ -175,8 +181,11 @@ 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) -> SqliteResult<()> { + pub fn run_to_completion(&self, + pages_per_step: c_int, + pause_between_pages: Duration, + progress: Option) + -> SqliteResult<()> { use self::StepResult::{Done, More, Busy, Locked}; assert!(pages_per_step > 0, "pages_per_step must be positive"); @@ -207,6 +216,7 @@ mod test { use super::Backup; #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_backup() { let src = SqliteConnection::open_in_memory().unwrap(); let sql = "BEGIN; @@ -237,6 +247,7 @@ mod test { } #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_backup_temp() { let src = SqliteConnection::open_in_memory().unwrap(); let sql = "BEGIN; @@ -248,8 +259,11 @@ mod test { let mut dst = SqliteConnection::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(); } @@ -259,8 +273,11 @@ mod test { src.execute_batch("INSERT INTO foo VALUES(43)").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.run_to_completion(5, Duration::from_millis(250), None).unwrap(); } @@ -269,6 +286,7 @@ mod test { } #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_backup_attached() { let src = SqliteConnection::open_in_memory().unwrap(); let sql = "ATTACH DATABASE ':memory:' AS my_attached; @@ -281,8 +299,11 @@ mod test { let mut dst = SqliteConnection::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(); } @@ -292,8 +313,11 @@ mod test { 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(); + 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(); } From 49951ca3be09b3ab375c92d683297e804218c29e Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 16:06:16 -0500 Subject: [PATCH 17/53] Add backup and restore methods to SqliteConnection (behind `backup` Cargo feature) --- src/backup.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/backup.rs b/src/backup.rs index 5e7ddf0..e50ad20 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -27,6 +27,8 @@ //! ``` use std::marker::PhantomData; +use std::path::Path; +use std::ptr; use libc::c_int; use std::thread; @@ -36,7 +38,92 @@ use ffi; use {DatabaseName, SqliteConnection, SqliteError, SqliteResult}; +impl SqliteConnection { + /// Back up the `name` database to the given destination path. + /// If `progress` is not `None`, it will be called periodically + /// until the backup completes. + /// + /// For more fine-grained control over the backup process (e.g., + /// to sleep periodically during the backup or to back up to an + /// already-open database connection), see the `backup` module. + /// + /// # Failure + /// + /// Will return `Err` if the destination path cannot be opened + /// or if the backup fails. + pub fn backup>(&self, + name: DatabaseName, + dst_path: P, + progress: Option) + -> SqliteResult<()> { + use self::StepResult::{More, Done, Busy, Locked}; + let mut dst = try!(SqliteConnection::open(dst_path)); + let backup = try!(Backup::new_with_names(self, name, &mut dst, DatabaseName::Main)); + + let mut r = More; + while r == More { + r = try!(backup.step(100)); + if let Some(f) = progress { + f(backup.progress()); + } + } + + match r { + Done => Ok(()), + Busy => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_BUSY)), + Locked => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_LOCKED)), + More => unreachable!(), + } + } + + /// Restore the given source path into the `name` database. + /// If `progress` is not `None`, it will be called periodically + /// until the restore completes. + /// + /// For more fine-grained control over the restore process (e.g., + /// to sleep periodically during the restore or to restore from an + /// already-open database connection), see the `backup` module. + /// + /// # Failure + /// + /// Will return `Err` if the destination path cannot be opened + /// or if the restore fails. + pub fn restore>(&mut self, + name: DatabaseName, + src_path: P, + progress: Option) + -> SqliteResult<()> { + use self::StepResult::{More, Done, Busy, Locked}; + let src = try!(SqliteConnection::open(src_path)); + let restore = try!(Backup::new_with_names(&src, DatabaseName::Main, self, name)); + + let mut r = More; + let mut busy_count = 0i32; + 'restore_loop: while r == More || r == Busy { + r = try!(restore.step(100)); + if let Some(f) = progress { + f(restore.progress()); + } + if r == Busy { + busy_count += 1; + if busy_count >= 3 { + break 'restore_loop; + } + thread::sleep(Duration::from_millis(100)); + } + } + + match r { + Done => Ok(()), + Busy => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_BUSY)), + Locked => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_LOCKED)), + More => unreachable!(), + } + } +} + /// Possible successful results of calling `Backup::step`. +#[derive(Copy,Clone,Debug,PartialEq,Eq)] pub enum StepResult { /// The backup is complete. Done, From 09825dbd90c4b9320b6e7550e104978df4e75966 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 16:22:01 -0500 Subject: [PATCH 18/53] Add feature tests to Travis --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 837d9f4..a0743ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,11 @@ env: script: - cargo build - cargo test - - cargo doc --no-deps + - cargo test --features backup + - cargo test --features load_extension + - cargo test --features trace + - cargo test --features "backup load_extension trace" + - cargo doc --no-deps --features "backup load_extension trace" after_success: | [ $TRAVIS_BRANCH = master ] && From a1f1480b1895b4f9dec65c1c35e16385964bc7a6 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 16:48:09 -0500 Subject: [PATCH 19/53] Run rustfmt on all crate files --- src/lib.rs | 572 +++++++++++++++++++++--------------- src/load_extension_guard.rs | 2 +- src/trace.rs | 27 +- src/transaction.rs | 44 ++- src/types.rs | 160 ++++++---- 5 files changed, 488 insertions(+), 317 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b43c6be..397d615 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,17 +52,20 @@ //! ``` extern crate libc; extern crate libsqlite3_sys as ffi; -#[macro_use] extern crate bitflags; -#[cfg(test)] #[macro_use] extern crate lazy_static; +#[macro_use] +extern crate bitflags; +#[cfg(test)] +#[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::path::{Path, PathBuf}; use std::error; -use std::rc::{Rc}; +use std::rc::Rc; use std::cell::{RefCell, Cell}; use std::ffi::{CStr, CString}; use std::str; @@ -70,19 +73,18 @@ use libc::{c_int, c_void, c_char}; use types::{ToSql, FromSql}; -pub use transaction::{SqliteTransaction}; -pub use transaction::{SqliteTransactionBehavior, - SqliteTransactionDeferred, - SqliteTransactionImmediate, - SqliteTransactionExclusive}; +pub use transaction::SqliteTransaction; +pub use transaction::{SqliteTransactionBehavior, SqliteTransactionDeferred, +SqliteTransactionImmediate, SqliteTransactionExclusive}; -#[cfg(feature = "load_extension")] pub use load_extension_guard::{SqliteLoadExtensionGuard}; +#[cfg(feature = "load_extension")] +pub use load_extension_guard::SqliteLoadExtensionGuard; pub mod types; mod transaction; -#[cfg(feature = "load_extension")] mod load_extension_guard; -#[cfg(feature = "trace")] pub mod trace; -#[cfg(feature = "backup")] pub mod backup; +#[cfg(feature = "load_extension")]mod load_extension_guard; +#[cfg(feature = "trace")]pub mod trace; +#[cfg(feature = "backup")]pub mod backup; /// A typedef of the result returned by many methods. pub type SqliteResult = Result; @@ -124,21 +126,27 @@ impl SqliteError { } else { unsafe { errmsg_to_string(ffi::sqlite3_errmsg(db)) } }; - SqliteError{ code: code, message: message } + SqliteError { + code: code, + message: message, + } } } fn str_to_cstring(s: &str) -> SqliteResult { - CString::new(s).map_err(|_| SqliteError{ - code: ffi::SQLITE_MISUSE, - message: format!("Could not convert string {} to C-combatible string", s), + CString::new(s).map_err(|_| { + SqliteError { + code: ffi::SQLITE_MISUSE, + message: format!("Could not convert string {} to C-combatible string", s), + } }) } fn path_to_cstring(p: &Path) -> SqliteResult { - let s = try!(p.to_str().ok_or(SqliteError{ + let s = try!(p.to_str().ok_or(SqliteError { code: ffi::SQLITE_MISUSE, - message: format!("Could not convert path {} to UTF-8 string", p.to_string_lossy()), + message: format!("Could not convert path {} to UTF-8 string", + p.to_string_lossy()), })); str_to_cstring(s) } @@ -162,8 +170,8 @@ impl<'a> DatabaseName<'a> { fn to_cstring(self) -> SqliteResult { use self::DatabaseName::{Main, Temp, Attached}; match self { - Main => str_to_cstring("main"), - Temp => str_to_cstring("temp"), + Main => str_to_cstring("main"), + Temp => str_to_cstring("temp"), Attached(s) => str_to_cstring(s), } } @@ -211,13 +219,17 @@ impl SqliteConnection { /// /// Will return `Err` if `path` cannot be converted to a C-compatible string or if the /// underlying SQLite open call fails. - pub fn open_with_flags>(path: P, flags: SqliteOpenFlags) - -> SqliteResult { - let c_path = try!(path_to_cstring(path.as_ref())); - InnerSqliteConnection::open_with_flags(&c_path, flags).map(|db| { - SqliteConnection{ db: RefCell::new(db), path: Some(path.as_ref().to_path_buf()) } - }) - } + pub fn open_with_flags>(path: P, + flags: SqliteOpenFlags) + -> SqliteResult { + let c_path = try!(path_to_cstring(path.as_ref())); + InnerSqliteConnection::open_with_flags(&c_path, flags).map(|db| { + SqliteConnection { + db: RefCell::new(db), + path: Some(path.as_ref().to_path_buf()), + } + }) + } /// Open a new connection to an in-memory SQLite database. /// @@ -230,7 +242,10 @@ impl SqliteConnection { pub fn open_in_memory_with_flags(flags: SqliteOpenFlags) -> SqliteResult { let c_memory = try!(str_to_cstring(":memory:")); InnerSqliteConnection::open_with_flags(&c_memory, flags).map(|db| { - SqliteConnection{ db: RefCell::new(db), path: None } + SqliteConnection { + db: RefCell::new(db), + path: None, + } }) } @@ -269,10 +284,11 @@ impl SqliteConnection { /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. - pub fn transaction_with_behavior<'a>(&'a self, behavior: SqliteTransactionBehavior) - -> SqliteResult> { - SqliteTransaction::new(self, behavior) - } + pub fn transaction_with_behavior<'a>(&'a self, + behavior: SqliteTransactionBehavior) + -> SqliteResult> { + SqliteTransaction::new(self, behavior) + } /// Convenience method to run multiple SQL statements (that cannot take any parameters). /// @@ -351,18 +367,21 @@ impl SqliteConnection { /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// underlying SQLite call fails. pub fn query_row(&self, sql: &str, params: &[&ToSql], f: F) -> SqliteResult - where F: FnOnce(SqliteRow) -> T { - let mut stmt = try!(self.prepare(sql)); - let mut rows = try!(stmt.query(params)); + where F: FnOnce(SqliteRow) -> T + { + let mut stmt = try!(self.prepare(sql)); + let mut rows = try!(stmt.query(params)); - match rows.next() { - Some(row) => row.map(f), - None => Err(SqliteError{ - code: ffi::SQLITE_NOTICE, - message: "Query did not return a row".to_string(), - }) + match rows.next() { + Some(row) => row.map(f), + None => { + Err(SqliteError { + code: ffi::SQLITE_NOTICE, + message: "Query did not return a row".to_string(), + }) + } + } } - } /// Convenience method to execute a query that is expected to return a single row, /// and execute a mapping via `f` on that returned row with the possibility of failure. @@ -386,19 +405,22 @@ impl SqliteConnection { /// 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(&self, sql: &str, params: &[&ToSql], f: F) -> Result - where F: FnOnce(SqliteRow) -> Result, - E: convert::From { - let mut stmt = try!(self.prepare(sql)); - let mut rows = try!(stmt.query(params)); + where F: FnOnce(SqliteRow) -> Result, + E: convert::From + { + let mut stmt = try!(self.prepare(sql)); + let mut rows = try!(stmt.query(params)); - match rows.next() { - Some(row) => row.map_err(E::from).and_then(f), - None => Err(E::from(SqliteError{ - code: ffi::SQLITE_NOTICE, - message: "Query did not return a row".to_string(), - })) - } - } + match rows.next() { + Some(row) => row.map_err(E::from).and_then(f), + None => { + Err(E::from(SqliteError { + code: ffi::SQLITE_NOTICE, + message: "Query did not return a row".to_string(), + })) + } + } + } /// Convenience method to execute a query that is expected to return a single row. /// @@ -420,9 +442,10 @@ impl SqliteConnection { /// This method should be considered deprecated. Use `query_row` instead, which now /// does exactly the same thing. pub fn query_row_safe(&self, sql: &str, params: &[&ToSql], f: F) -> SqliteResult - where F: FnOnce(SqliteRow) -> T { - self.query_row(sql, params, f) - } + where F: FnOnce(SqliteRow) -> T + { + self.query_row(sql, params, f) + } /// Prepare a SQL statement for execution. /// @@ -517,9 +540,12 @@ impl SqliteConnection { /// /// Will return `Err` if the underlying SQLite call fails. #[cfg(feature = "load_extension")] - pub fn load_extension>(&self, dylib_path: P, entry_point: Option<&str>) -> SqliteResult<()> { - self.db.borrow_mut().load_extension(dylib_path.as_ref(), entry_point) - } + pub fn load_extension>(&self, + dylib_path: P, + entry_point: Option<&str>) + -> SqliteResult<()> { + self.db.borrow_mut().load_extension(dylib_path.as_ref(), entry_point) + } fn decode_result(&self, code: c_int) -> SqliteResult<()> { self.db.borrow_mut().decode_result(code) @@ -561,40 +587,40 @@ bitflags! { impl Default for SqliteOpenFlags { fn default() -> SqliteOpenFlags { - SQLITE_OPEN_READ_WRITE - | SQLITE_OPEN_CREATE - | SQLITE_OPEN_NO_MUTEX - | SQLITE_OPEN_URI + SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NO_MUTEX | SQLITE_OPEN_URI } } impl InnerSqliteConnection { - fn open_with_flags(c_path: &CString, flags: SqliteOpenFlags) - -> SqliteResult { - unsafe { - let mut db: *mut ffi::sqlite3 = mem::uninitialized(); - let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null()); - if r != ffi::SQLITE_OK { - let e = if db.is_null() { - SqliteError{ code: r, - message: ffi::code_to_str(r).to_string() } - } else { + fn open_with_flags(c_path: &CString, + flags: SqliteOpenFlags) + -> SqliteResult { + unsafe { + let mut db: *mut ffi::sqlite3 = mem::uninitialized(); + let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null()); + if r != ffi::SQLITE_OK { + let e = if db.is_null() { + SqliteError { + code: r, + message: ffi::code_to_str(r).to_string(), + } + } else { + let e = SqliteError::from_handle(db, r); + ffi::sqlite3_close(db); + e + }; + + return Err(e); + } + let r = ffi::sqlite3_busy_timeout(db, 5000); + if r != ffi::SQLITE_OK { let e = SqliteError::from_handle(db, r); ffi::sqlite3_close(db); - e - }; - - return Err(e); + return Err(e); + } + Ok(InnerSqliteConnection { db: db }) } - let r = ffi::sqlite3_busy_timeout(db, 5000); - if r != ffi::SQLITE_OK { - let e = SqliteError::from_handle(db, r); - ffi::sqlite3_close(db); - return Err(e); - } - Ok(InnerSqliteConnection{ db: db }) } - } fn db(&self) -> *mut ffi::Struct_sqlite3 { self.db @@ -608,15 +634,21 @@ impl InnerSqliteConnection { } } - unsafe fn decode_result_with_errmsg(&self, code: c_int, errmsg: *mut c_char) -> SqliteResult<()> { - if code == ffi::SQLITE_OK { - Ok(()) - } else { - let message = errmsg_to_string(&*errmsg); - ffi::sqlite3_free(errmsg as *mut c_void); - Err(SqliteError{ code: code, message: message }) + unsafe fn decode_result_with_errmsg(&self, + code: c_int, + errmsg: *mut c_char) + -> SqliteResult<()> { + if code == ffi::SQLITE_OK { + Ok(()) + } else { + let message = errmsg_to_string(&*errmsg); + ffi::sqlite3_free(errmsg as *mut c_void); + Err(SqliteError { + code: code, + message: message, + }) + } } - } fn close(&mut self) -> SqliteResult<()> { unsafe { @@ -630,7 +662,11 @@ impl InnerSqliteConnection { let c_sql = try!(str_to_cstring(sql)); unsafe { let mut errmsg: *mut c_char = mem::uninitialized(); - let r = ffi::sqlite3_exec(self.db(), c_sql.as_ptr(), None, ptr::null_mut(), &mut errmsg); + let r = ffi::sqlite3_exec(self.db(), + c_sql.as_ptr(), + None, + ptr::null_mut(), + &mut errmsg); self.decode_result_with_errmsg(r, errmsg) } } @@ -648,7 +684,10 @@ impl InnerSqliteConnection { 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) }; @@ -657,34 +696,34 @@ impl InnerSqliteConnection { } fn last_insert_rowid(&self) -> i64 { - unsafe { - ffi::sqlite3_last_insert_rowid(self.db()) - } + unsafe { ffi::sqlite3_last_insert_rowid(self.db()) } } fn prepare<'a>(&mut self, conn: &'a SqliteConnection, - sql: &str) -> SqliteResult> { - if sql.len() >= ::std::i32::MAX as usize { - return Err(SqliteError { - code: ffi::SQLITE_TOOBIG, - message: "statement too long".to_string() - }); + sql: &str) + -> SqliteResult> { + if sql.len() >= ::std::i32::MAX as usize { + return Err(SqliteError { + code: ffi::SQLITE_TOOBIG, + message: "statement too long".to_string(), + }); + } + let mut c_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() }; + let c_sql = try!(str_to_cstring(sql)); + let r = unsafe { + let len_with_nul = (sql.len() + 1) as c_int; + ffi::sqlite3_prepare_v2(self.db(), + c_sql.as_ptr(), + len_with_nul, + &mut c_stmt, + ptr::null_mut()) + }; + self.decode_result(r).map(|_| SqliteStatement::new(conn, c_stmt)) } - let mut c_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() }; - let c_sql = try!(str_to_cstring(sql)); - let r = unsafe { - let len_with_nul = (sql.len() + 1) as c_int; - ffi::sqlite3_prepare_v2(self.db(), c_sql.as_ptr(), len_with_nul, &mut c_stmt, - ptr::null_mut()) - }; - self.decode_result(r).map(|_| { - SqliteStatement::new(conn, c_stmt) - }) - } fn changes(&mut self) -> c_int { - unsafe{ ffi::sqlite3_changes(self.db()) } + unsafe { ffi::sqlite3_changes(self.db()) } } } @@ -705,8 +744,12 @@ pub struct SqliteStatement<'conn> { impl<'conn> SqliteStatement<'conn> { fn new(conn: &SqliteConnection, stmt: *mut ffi::sqlite3_stmt) -> SqliteStatement { - SqliteStatement{ conn: conn, stmt: stmt, needs_reset: false, - column_count: unsafe { ffi::sqlite3_column_count(stmt) }} + SqliteStatement { + conn: conn, + stmt: stmt, + needs_reset: false, + column_count: unsafe { ffi::sqlite3_column_count(stmt) }, + } } /// Get all the column names in the result set of the prepared statement. @@ -714,9 +757,7 @@ impl<'conn> SqliteStatement<'conn> { let n = self.column_count; let mut cols = Vec::with_capacity(n as usize); for i in 0..n { - let slice = unsafe { - CStr::from_ptr(ffi::sqlite3_column_name(self.stmt, i)) - }; + let slice = unsafe { CStr::from_ptr(ffi::sqlite3_column_name(self.stmt, i)) }; let s = str::from_utf8(slice.to_bytes()).unwrap(); cols.push(s); } @@ -755,14 +796,21 @@ impl<'conn> SqliteStatement<'conn> { match r { ffi::SQLITE_DONE => { if self.column_count != 0 { - Err(SqliteError{ code: ffi::SQLITE_MISUSE, - message: "Unexpected column count - did you mean to call query?".to_string() }) + Err(SqliteError { + code: ffi::SQLITE_MISUSE, + message: "Unexpected column count - did you mean to call query?" + .to_string(), + }) } else { Ok(self.conn.changes()) } - }, - ffi::SQLITE_ROW => Err(SqliteError{ code: r, - message: "Unexpected row result - did you mean to call query?".to_string() }), + } + ffi::SQLITE_ROW => { + Err(SqliteError { + code: r, + message: "Unexpected row result - did you mean to call query?".to_string(), + }) + } _ => Err(self.conn.decode_result(r).unwrap_err()), } } @@ -811,16 +859,19 @@ impl<'conn> SqliteStatement<'conn> { /// # Failure /// /// Will return `Err` if binding parameters fails. - pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) - -> SqliteResult> - where F: FnMut(&SqliteRow) -> T { - let row_iter = try!(self.query(params)); + pub fn query_map<'a, T, F>(&'a mut self, + params: &[&ToSql], + f: F) + -> SqliteResult> + where F: FnMut(&SqliteRow) -> T + { + let row_iter = try!(self.query(params)); - Ok(MappedRows{ - rows: row_iter, - map: f, - }) - } + Ok(MappedRows { + rows: row_iter, + map: f, + }) + } /// Executes the prepared statement and maps a function over the resulting /// rows, where the function returns a `Result` with `Error` type implementing @@ -832,17 +883,20 @@ impl<'conn> SqliteStatement<'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) - -> SqliteResult> - where E: convert::From, - F: FnMut(&SqliteRow) -> Result { - let row_iter = try!(self.query(params)); + pub fn query_and_then<'a, T, E, F>(&'a mut self, + params: &[&ToSql], + f: F) + -> SqliteResult> + where E: convert::From, + F: FnMut(&SqliteRow) -> Result + { + let row_iter = try!(self.query(params)); - Ok(AndThenRows{ - rows: row_iter, - map: f, - }) - } + Ok(AndThenRows { + rows: row_iter, + map: f, + }) + } /// Consumes the statement. /// @@ -858,9 +912,9 @@ impl<'conn> SqliteStatement<'conn> { unsafe fn bind_parameters(&mut self, params: &[&ToSql]) -> SqliteResult<()> { assert!(params.len() as c_int == ffi::sqlite3_bind_parameter_count(self.stmt), - "incorrect number of parameters to query(): expected {}, got {}", - ffi::sqlite3_bind_parameter_count(self.stmt), - params.len()); + "incorrect number of parameters to query(): expected {}, got {}", + ffi::sqlite3_bind_parameter_count(self.stmt), + params.len()); for (i, p) in params.iter().enumerate() { try!(self.conn.decode_result(p.bind_parameter(self.stmt, (i + 1) as c_int))); @@ -871,7 +925,9 @@ impl<'conn> SqliteStatement<'conn> { fn reset_if_needed(&mut self) { if self.needs_reset { - unsafe { ffi::sqlite3_reset(self.stmt); }; + unsafe { + ffi::sqlite3_reset(self.stmt); + }; self.needs_reset = false; } } @@ -910,8 +966,8 @@ pub struct MappedRows<'stmt, F> { map: F, } -impl<'stmt, T, F> Iterator for MappedRows<'stmt, F> - where F: FnMut(&SqliteRow) -> T { +impl<'stmt, T, F> Iterator for MappedRows<'stmt, F> where F: FnMut(&SqliteRow) -> T +{ type Item = SqliteResult; fn next(&mut self) -> Option> { @@ -927,14 +983,16 @@ pub struct AndThenRows<'stmt, F> { } impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F> - where E: convert::From, - F: FnMut(&SqliteRow) -> Result { +where E: convert::From, + F: FnMut(&SqliteRow) -> Result +{ type Item = Result; fn next(&mut self) -> Option { - self.rows.next().map(|row_result| row_result - .map_err(E::from) - .and_then(|row| (self.map)(&row))) + self.rows.next().map(|row_result| { + row_result.map_err(E::from) + .and_then(|row| (self.map)(&row)) + }) } } @@ -981,7 +1039,11 @@ pub struct SqliteRows<'stmt> { impl<'stmt> SqliteRows<'stmt> { fn new(stmt: &'stmt SqliteStatement<'stmt>) -> SqliteRows<'stmt> { - SqliteRows{ stmt: stmt, current_row: Rc::new(Cell::new(0)), failed: false } + SqliteRows { + stmt: stmt, + current_row: Rc::new(Cell::new(0)), + failed: false, + } } } @@ -996,12 +1058,12 @@ impl<'stmt> Iterator for SqliteRows<'stmt> { ffi::SQLITE_ROW => { let current_row = self.current_row.get() + 1; self.current_row.set(current_row); - Some(Ok(SqliteRow{ + Some(Ok(SqliteRow { stmt: self.stmt, current_row: self.current_row.clone(), row_idx: current_row, })) - }, + } ffi::SQLITE_DONE => None, code => { self.failed = true; @@ -1062,19 +1124,23 @@ impl<'stmt> SqliteRow<'stmt> { /// for this row or if this row is stale. pub fn get_checked(&self, idx: c_int) -> SqliteResult { if self.row_idx != self.current_row.get() { - return Err(SqliteError{ code: ffi::SQLITE_MISUSE, - message: "Cannot get values from a row after advancing to next row".to_string() }); + return Err(SqliteError { + code: ffi::SQLITE_MISUSE, + message: "Cannot get values from a row after advancing to next row".to_string(), + }); } unsafe { if idx < 0 || idx >= self.stmt.column_count { - return Err(SqliteError{ code: ffi::SQLITE_MISUSE, - message: "Invalid column index".to_string() }); + return Err(SqliteError { + code: ffi::SQLITE_MISUSE, + message: "Invalid column index".to_string(), + }); } if T::column_has_valid_sqlite_type(self.stmt.stmt, idx) { FromSql::column_result(self.stmt.stmt, idx) } else { - Err(SqliteError{ + Err(SqliteError { code: ffi::SQLITE_MISMATCH, message: "Invalid column type".to_string(), }) @@ -1105,6 +1171,7 @@ mod test { } #[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"); @@ -1115,14 +1182,12 @@ mod test { CREATE TABLE foo(x INTEGER); INSERT INTO foo VALUES(42); END;"; - db.execute_batch(sql).unwrap(); + db.execute_batch(sql).unwrap(); } let path_string = path.to_str().unwrap(); let db = SqliteConnection::open(&path_string).unwrap(); - let the_answer = db.query_row("SELECT x FROM foo", - &[], - |r| r.get::(0)); + let the_answer = db.query_row("SELECT x FROM foo", &[], |r| r.get::(0)); assert_eq!(42i64, the_answer.unwrap()); } @@ -1137,16 +1202,16 @@ mod test { #[test] fn test_open_with_flags() { - for bad_flags in [ - SqliteOpenFlags::empty(), - SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_READ_WRITE, - SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_CREATE, - ].iter() { - assert!(SqliteConnection::open_in_memory_with_flags(*bad_flags).is_err()); - } + for bad_flags in [SqliteOpenFlags::empty(), + SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_READ_WRITE, + SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_CREATE] + .iter() { + assert!(SqliteConnection::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; @@ -1168,10 +1233,13 @@ mod test { let db = checked_memory_handle(); db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); - assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32]).unwrap(), 1); - assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32]).unwrap(), 1); + assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32]).unwrap(), + 1); + assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32]).unwrap(), + 1); - assert_eq!(3i32, db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); + assert_eq!(3i32, + db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); } #[test] @@ -1240,6 +1308,7 @@ mod test { } #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_query_map() { let db = checked_memory_handle(); let sql = "BEGIN; @@ -1252,8 +1321,7 @@ mod test { db.execute_batch(sql).unwrap(); let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap(); - let results: SqliteResult> = query - .query_map(&[], |row| row.get(1)) + let results: SqliteResult> = query.query_map(&[], |row| row.get(1)) .unwrap() .collect(); @@ -1261,6 +1329,7 @@ mod test { } #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_query_row() { let db = checked_memory_handle(); let sql = "BEGIN; @@ -1272,9 +1341,9 @@ mod test { END;"; db.execute_batch(sql).unwrap(); - assert_eq!(10i64, db.query_row("SELECT SUM(x) FROM foo", &[], |r| { - r.get::(0) - }).unwrap()); + assert_eq!(10i64, + db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get::(0)) + .unwrap()); let result = db.query_row("SELECT x FROM foo WHERE x > 5", &[], |r| r.get::(0)); let error = result.unwrap_err(); @@ -1323,7 +1392,7 @@ mod test { assert_eq!(db.last_insert_rowid(), 1); let mut stmt = db.prepare("INSERT INTO foo DEFAULT VALUES").unwrap(); - for _ in 0i32 .. 9 { + for _ in 0i32..9 { stmt.execute(&[]).unwrap(); } assert_eq!(db.last_insert_rowid(), 10); @@ -1351,17 +1420,19 @@ mod test { impl fmt::Display for CustomError { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { - CustomError::SomeError => write!(f, "{}", self.description()), + CustomError::SomeError => write!(f, "{}", self.description()), CustomError::Sqlite(ref se) => write!(f, "{}: {}", self.description(), se), } } } impl StdError for CustomError { - fn description(&self) -> &str { "my custom error" } + fn description(&self) -> &str { + "my custom error" + } fn cause(&self) -> Option<&StdError> { match *self { - CustomError::SomeError => None, + CustomError::SomeError => None, CustomError::Sqlite(ref se) => Some(se), } } @@ -1376,6 +1447,7 @@ mod test { type CustomResult = Result; #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_query_and_then() { let db = checked_memory_handle(); let sql = "BEGIN; @@ -1388,8 +1460,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: SqliteResult> = query - .query_and_then(&[], |row| row.get_checked(1)) + let results: SqliteResult> = query.query_and_then(&[], + |row| row.get_checked(1)) .unwrap() .collect(); @@ -1397,6 +1469,7 @@ mod test { } #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_query_and_then_fails() { let db = checked_memory_handle(); let sql = "BEGIN; @@ -1409,28 +1482,31 @@ 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: SqliteResult> = query - .query_and_then(&[], |row| row.get_checked(1)) + let bad_type: SqliteResult> = query.query_and_then(&[], + |row| row.get_checked(1)) .unwrap() .collect(); - assert_eq!(bad_type, Err(SqliteError{ - code: ffi::SQLITE_MISMATCH, - message: "Invalid column type".to_owned(), - })); + assert_eq!(bad_type, + Err(SqliteError { + code: ffi::SQLITE_MISMATCH, + message: "Invalid column type".to_owned(), + })); - let bad_idx: SqliteResult> = query - .query_and_then(&[], |row| row.get_checked(3)) + let bad_idx: SqliteResult> = query.query_and_then(&[], + |row| row.get_checked(3)) .unwrap() .collect(); - assert_eq!(bad_idx, Err(SqliteError{ - code: ffi::SQLITE_MISUSE, - message: "Invalid column index".to_owned(), - })); + assert_eq!(bad_idx, + Err(SqliteError { + code: ffi::SQLITE_MISUSE, + message: "Invalid column index".to_owned(), + })); } #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_query_and_then_custom_error() { let db = checked_memory_handle(); let sql = "BEGIN; @@ -1443,15 +1519,18 @@ 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> = query - .query_and_then(&[], |row| row.get_checked(1).map_err(CustomError::Sqlite)) - .unwrap() + let results: CustomResult> = 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; @@ -1464,35 +1543,43 @@ 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> = query - .query_and_then(&[], |row| row.get_checked(1).map_err(CustomError::Sqlite)) - .unwrap() + let bad_type: CustomResult> = query.query_and_then(&[], |row| { + row.get_checked(1) + .map_err(CustomError::Sqlite) + }) + .unwrap() .collect(); - assert_eq!(bad_type, Err(CustomError::Sqlite(SqliteError{ - code: ffi::SQLITE_MISMATCH, - message: "Invalid column type".to_owned(), - }))); + assert_eq!(bad_type, + Err(CustomError::Sqlite(SqliteError { + code: ffi::SQLITE_MISMATCH, + message: "Invalid column type".to_owned(), + }))); - let bad_idx: CustomResult> = query - .query_and_then(&[], |row| row.get_checked(3).map_err(CustomError::Sqlite)) - .unwrap() + let bad_idx: CustomResult> = query.query_and_then(&[], |row| { + row.get_checked(3) + .map_err(CustomError::Sqlite) + }) + .unwrap() .collect(); - assert_eq!(bad_idx, Err(CustomError::Sqlite(SqliteError{ - code: ffi::SQLITE_MISUSE, - message: "Invalid column index".to_owned(), - }))); + assert_eq!(bad_idx, + Err(CustomError::Sqlite(SqliteError { + code: ffi::SQLITE_MISUSE, + message: "Invalid column index".to_owned(), + }))); - let non_sqlite_err: CustomResult> = query - .query_and_then(&[], |_| Err(CustomError::SomeError)) - .unwrap() + let non_sqlite_err: CustomResult> = query.query_and_then(&[], |_| { + Err(CustomError::SomeError) + }) + .unwrap() .collect(); assert_eq!(non_sqlite_err, Err(CustomError::SomeError)); } #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_query_row_and_then_custom_error() { let db = checked_memory_handle(); let sql = "BEGIN; @@ -1502,13 +1589,15 @@ mod test { db.execute_batch(sql).unwrap(); let query = "SELECT x, y FROM foo ORDER BY x DESC"; - let results: CustomResult = db - .query_row_and_then(query, &[], |row| row.get_checked(1).map_err(CustomError::Sqlite)); + let results: CustomResult = db.query_row_and_then(query, &[], |row| { + row.get_checked(1).map_err(CustomError::Sqlite) + }); assert_eq!(results.unwrap(), "hello"); } #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_query_row_and_then_custom_error_fails() { let db = checked_memory_handle(); let sql = "BEGIN; @@ -1518,24 +1607,29 @@ mod test { db.execute_batch(sql).unwrap(); let query = "SELECT x, y FROM foo ORDER BY x DESC"; - let bad_type: CustomResult = db - .query_row_and_then(query, &[], |row| row.get_checked(1).map_err(CustomError::Sqlite)); + let bad_type: CustomResult = db.query_row_and_then(query, &[], |row| { + row.get_checked(1).map_err(CustomError::Sqlite) + }); - assert_eq!(bad_type, Err(CustomError::Sqlite(SqliteError{ - code: ffi::SQLITE_MISMATCH, - message: "Invalid column type".to_owned(), - }))); + assert_eq!(bad_type, + Err(CustomError::Sqlite(SqliteError { + code: ffi::SQLITE_MISMATCH, + message: "Invalid column type".to_owned(), + }))); - let bad_idx: CustomResult = db - .query_row_and_then(query, &[], |row| row.get_checked(3).map_err(CustomError::Sqlite)); + let bad_idx: CustomResult = db.query_row_and_then(query, &[], |row| { + row.get_checked(3).map_err(CustomError::Sqlite) + }); - assert_eq!(bad_idx, Err(CustomError::Sqlite(SqliteError{ - code: ffi::SQLITE_MISUSE, - message: "Invalid column index".to_owned(), - }))); + assert_eq!(bad_idx, + Err(CustomError::Sqlite(SqliteError { + code: ffi::SQLITE_MISUSE, + message: "Invalid column index".to_owned(), + }))); - let non_sqlite_err: CustomResult = db - .query_row_and_then(query, &[], |_| Err(CustomError::SomeError)); + let non_sqlite_err: CustomResult = db.query_row_and_then(query, &[], |_| { + Err(CustomError::SomeError) + }); assert_eq!(non_sqlite_err, Err(CustomError::SomeError)); } diff --git a/src/load_extension_guard.rs b/src/load_extension_guard.rs index 5b36512..ecc93e5 100644 --- a/src/load_extension_guard.rs +++ b/src/load_extension_guard.rs @@ -21,7 +21,7 @@ impl<'conn> SqliteLoadExtensionGuard<'conn> { /// Attempt to enable loading extensions. Loading extensions will be disabled when this /// guard goes out of scope. Cannot be meaningfully nested. pub fn new(conn: &SqliteConnection) -> SqliteResult { - conn.load_extension_enable().map(|_| SqliteLoadExtensionGuard{ conn: conn }) + conn.load_extension_enable().map(|_| SqliteLoadExtensionGuard { conn: conn }) } } diff --git a/src/trace.rs b/src/trace.rs index 4296ddc..682662c 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -35,7 +35,7 @@ pub unsafe fn config_log(callback: Option) -> SqliteResult<()> Some(f) => { let p_arg: *mut c_void = mem::transmute(f); ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, Some(log_callback), p_arg) - }, + } None => { let nullptr: *mut c_void = ptr::null_mut(); ffi::sqlite3_config(ffi::SQLITE_CONFIG_LOG, nullptr, nullptr) @@ -43,7 +43,10 @@ pub unsafe fn config_log(callback: Option) -> SqliteResult<()> }; if rc != ffi::SQLITE_OK { - return Err(SqliteError{ code: rc, message: "sqlite3_config(SQLITE_CONFIG_LOG, ...)".to_string() }); + return Err(SqliteError { + code: rc, + message: "sqlite3_config(SQLITE_CONFIG_LOG, ...)".to_string(), + }); } Ok(()) @@ -64,7 +67,7 @@ impl SqliteConnection { /// There can only be a single tracer defined for each database connection. /// Setting a new tracer clears the old one. pub fn trace(&mut self, trace_fn: Option) { - extern "C" fn trace_callback (p_arg: *mut c_void, z_sql: *const c_char) { + extern "C" fn trace_callback(p_arg: *mut c_void, z_sql: *const c_char) { let trace_fn: fn(&str) = unsafe { mem::transmute(p_arg) }; let c_slice = unsafe { CStr::from_ptr(z_sql).to_bytes() }; if let Ok(s) = str::from_utf8(c_slice) { @@ -74,8 +77,12 @@ impl SqliteConnection { let c = self.db.borrow_mut(); match trace_fn { - Some(f) => unsafe { ffi::sqlite3_trace(c.db(), Some(trace_callback), mem::transmute(f)); }, - None => unsafe { ffi::sqlite3_trace(c.db(), None, ptr::null_mut()); }, + Some(f) => unsafe { + ffi::sqlite3_trace(c.db(), Some(trace_callback), mem::transmute(f)); + }, + None => unsafe { + ffi::sqlite3_trace(c.db(), None, ptr::null_mut()); + }, } } @@ -84,7 +91,9 @@ impl SqliteConnection { /// 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) { - extern "C" fn profile_callback(p_arg: *mut c_void, z_sql: *const c_char, nanoseconds: u64) { + extern "C" fn profile_callback(p_arg: *mut c_void, + z_sql: *const c_char, + nanoseconds: u64) { let profile_fn: fn(&str, Duration) = unsafe { mem::transmute(p_arg) }; let c_slice = unsafe { CStr::from_ptr(z_sql).to_bytes() }; if let Ok(s) = str::from_utf8(c_slice) { @@ -98,8 +107,10 @@ impl SqliteConnection { let c = self.db.borrow_mut(); match profile_fn { - Some(f) => unsafe { ffi::sqlite3_profile(c.db(), Some(profile_callback), mem::transmute(f)) }, - None => unsafe { ffi::sqlite3_profile(c.db(), None, ptr::null_mut()) }, + Some(f) => unsafe { + ffi::sqlite3_profile(c.db(), Some(profile_callback), mem::transmute(f)) + }, + None => unsafe { ffi::sqlite3_profile(c.db(), None, ptr::null_mut()) }, }; } } diff --git a/src/transaction.rs b/src/transaction.rs index 30f4c0e..1981f24 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,9 +1,7 @@ use {SqliteResult, SqliteConnection}; -pub use SqliteTransactionBehavior::{ - SqliteTransactionDeferred, - SqliteTransactionImmediate, - SqliteTransactionExclusive}; +pub use SqliteTransactionBehavior::{SqliteTransactionDeferred, SqliteTransactionImmediate, + SqliteTransactionExclusive}; /// Options for transaction behavior. See [BEGIN /// TRANSACTION](http://www.sqlite.org/lang_transaction.html) for details. @@ -46,14 +44,20 @@ pub struct SqliteTransaction<'conn> { impl<'conn> SqliteTransaction<'conn> { /// Begin a new transaction. Cannot be nested; see `savepoint` for nested transactions. pub fn new(conn: &SqliteConnection, - behavior: SqliteTransactionBehavior) -> SqliteResult { + behavior: SqliteTransactionBehavior) + -> SqliteResult { let query = match behavior { SqliteTransactionDeferred => "BEGIN DEFERRED", SqliteTransactionImmediate => "BEGIN IMMEDIATE", SqliteTransactionExclusive => "BEGIN EXCLUSIVE", }; conn.execute_batch(query).map(|_| { - SqliteTransaction{ conn: conn, depth: 0, commit: false, finished: false } + SqliteTransaction { + conn: conn, + depth: 0, + commit: false, + finished: false, + } }) } @@ -85,8 +89,11 @@ impl<'conn> SqliteTransaction<'conn> { /// ``` pub fn savepoint<'a>(&'a self) -> SqliteResult> { self.conn.execute_batch("SAVEPOINT sp").map(|_| { - SqliteTransaction{ - conn: self.conn, depth: self.depth + 1, commit: false, finished: false + SqliteTransaction { + conn: self.conn, + depth: self.depth + 1, + commit: false, + finished: false, } }) } @@ -118,7 +125,11 @@ impl<'conn> SqliteTransaction<'conn> { fn commit_(&mut self) -> SqliteResult<()> { self.finished = true; - self.conn.execute_batch(if self.depth == 0 { "COMMIT" } else { "RELEASE sp" }) + self.conn.execute_batch(if self.depth == 0 { + "COMMIT" + } else { + "RELEASE sp" + }) } /// A convenience method which consumes and rolls back a transaction. @@ -128,7 +139,11 @@ impl<'conn> SqliteTransaction<'conn> { fn rollback_(&mut self) -> SqliteResult<()> { self.finished = true; - self.conn.execute_batch(if self.depth == 0 { "ROLLBACK" } else { "ROLLBACK TO sp" }) + self.conn.execute_batch(if self.depth == 0 { + "ROLLBACK" + } else { + "ROLLBACK TO sp" + }) } /// Consumes the transaction, committing or rolling back according to the current setting @@ -181,7 +196,8 @@ mod test { } { let _tx = db.transaction().unwrap(); - assert_eq!(2i32, db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); + assert_eq!(2i32, + db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); } } @@ -200,7 +216,8 @@ mod test { } { let _tx = db.transaction().unwrap(); - assert_eq!(2i32, db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); + assert_eq!(2i32, + db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); } } @@ -228,6 +245,7 @@ mod test { } } } - assert_eq!(3i32, db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); + assert_eq!(3i32, + db.query_row("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); } } diff --git a/src/types.rs b/src/types.rs index a1d041f..f102f8a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -55,14 +55,14 @@ extern crate time; use libc::{c_int, c_double, c_char}; -use std::ffi::{CStr}; +use std::ffi::CStr; use std::mem; use std::str; use super::ffi; use super::{SqliteResult, SqliteError, str_to_cstring}; -pub use ffi::sqlite3_stmt as sqlite3_stmt; -pub use ffi::sqlite3_column_type as sqlite3_column_type; +pub use ffi::sqlite3_stmt; +pub use ffi::sqlite3_column_type; pub use ffi::{SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_TEXT, SQLITE_BLOB, SQLITE_NULL}; @@ -107,9 +107,14 @@ impl<'a> ToSql for &'a str { return ffi::SQLITE_TOOBIG; } match str_to_cstring(self) { - Ok(c_str) => ffi::sqlite3_bind_text(stmt, col, c_str.as_ptr(), length as c_int, - ffi::SQLITE_TRANSIENT()), - Err(_) => ffi::SQLITE_MISUSE, + Ok(c_str) => { + ffi::sqlite3_bind_text(stmt, + col, + c_str.as_ptr(), + length as c_int, + ffi::SQLITE_TRANSIENT()) + } + Err(_) => ffi::SQLITE_MISUSE, } } } @@ -125,8 +130,11 @@ impl<'a> ToSql for &'a [u8] { if self.len() > ::std::i32::MAX as usize { return ffi::SQLITE_TOOBIG; } - ffi::sqlite3_bind_blob( - stmt, col, mem::transmute(self.as_ptr()), self.len() as c_int, ffi::SQLITE_TRANSIENT()) + ffi::sqlite3_bind_blob(stmt, + col, + mem::transmute(self.as_ptr()), + self.len() as c_int, + ffi::SQLITE_TRANSIENT()) } } @@ -203,9 +211,13 @@ impl FromSql for String { } else { let c_slice = CStr::from_ptr(c_text as *const c_char).to_bytes(); let utf8_str = str::from_utf8(c_slice); - utf8_str - .map(|s| { s.to_string() }) - .map_err(|e| { SqliteError{code: 0, message: e.to_string()} }) + utf8_str.map(|s| s.to_string()) + .map_err(|e| { + SqliteError { + code: 0, + message: e.to_string(), + } + }) } } @@ -222,7 +234,8 @@ impl FromSql for Vec { // The documentation for sqlite3_column_bytes indicates it is always non-negative, // but we should assert here just to be sure. - assert!(len >= 0, "unexpected negative return from sqlite3_column_bytes"); + assert!(len >= 0, + "unexpected negative return from sqlite3_column_bytes"); let len = len as usize; Ok(from_raw_parts(mem::transmute(c_blob), len).to_vec()) @@ -234,15 +247,17 @@ impl FromSql for Vec { } impl FromSql for time::Timespec { - unsafe fn column_result(stmt: *mut sqlite3_stmt, - col: c_int) -> SqliteResult { + unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> SqliteResult { let col_str = FromSql::column_result(stmt, col); col_str.and_then(|txt: String| { - time::strptime(&txt, SQLITE_DATETIME_FMT).map(|tm| { - tm.to_timespec() - }).map_err(|parse_error| { - SqliteError{ code: ffi::SQLITE_MISMATCH, message: format!("{}", parse_error) } - }) + time::strptime(&txt, SQLITE_DATETIME_FMT) + .map(|tm| tm.to_timespec()) + .map_err(|parse_error| { + SqliteError { + code: ffi::SQLITE_MISMATCH, + message: format!("{}", parse_error), + } + }) }) } @@ -262,7 +277,7 @@ impl FromSql for Option { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { sqlite3_column_type(stmt, col) == ffi::SQLITE_NULL || - T::column_has_valid_sqlite_type(stmt, col) + T::column_has_valid_sqlite_type(stmt, col) } } @@ -283,7 +298,7 @@ mod test { fn test_blob() { let db = checked_memory_handle(); - let v1234 = vec![1u8,2,3,4]; + let v1234 = vec![1u8, 2, 3, 4]; db.execute("INSERT INTO foo(b) VALUES (?)", &[&v1234]).unwrap(); let v: Vec = db.query_row("SELECT b FROM foo", &[], |r| r.get(0)).unwrap(); @@ -305,7 +320,10 @@ mod test { fn test_timespec() { let db = checked_memory_handle(); - let ts = time::Timespec{sec: 10_000, nsec: 0 }; + let ts = time::Timespec { + sec: 10_000, + nsec: 0, + }; db.execute("INSERT INTO foo(t) VALUES (?)", &[&ts]).unwrap(); let from: time::Timespec = db.query_row("SELECT t FROM foo", &[], |r| r.get(0)).unwrap(); @@ -317,7 +335,7 @@ mod test { let db = checked_memory_handle(); let s = Some("hello, world!"); - let b = Some(vec![1u8,2,3,4]); + 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(); @@ -342,7 +360,9 @@ mod test { fn test_mismatched_types() { 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(); @@ -350,10 +370,10 @@ mod test { let row = rows.next().unwrap().unwrap(); // check the correct types come back as expected - assert_eq!(vec![1,2], row.get_checked::>(0).unwrap()); - assert_eq!("text", row.get_checked::(1).unwrap()); - assert_eq!(1, row.get_checked::(2).unwrap()); - assert_eq!(1.5, row.get_checked::(3).unwrap()); + assert_eq!(vec![1, 2], row.get_checked::>(0).unwrap()); + assert_eq!("text", row.get_checked::(1).unwrap()); + assert_eq!(1, row.get_checked::(2).unwrap()); + assert_eq!(1.5, row.get_checked::(3).unwrap()); assert!(row.get_checked::>(4).unwrap().is_none()); assert!(row.get_checked::>(4).unwrap().is_none()); assert!(row.get_checked::>(4).unwrap().is_none()); @@ -361,41 +381,69 @@ mod test { // check some invalid types // 0 is actually a blob (Vec) - assert_eq!(row.get_checked::(0).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(0).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(0).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(0).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(0).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::>(0).err().unwrap().code, ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(0).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(0).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(0).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(0).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(0).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::>(0).err().unwrap().code, + ffi::SQLITE_MISMATCH); // 1 is actually a text (String) - assert_eq!(row.get_checked::(1).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(1).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(1).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::>(1).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::>(1).err().unwrap().code, ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(1).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(1).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(1).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::>(1).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::>(1).err().unwrap().code, + ffi::SQLITE_MISMATCH); // 2 is actually an integer - assert_eq!(row.get_checked::(2).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(2).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::>(2).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(2).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::>(2).err().unwrap().code, ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(2).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(2).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::>(2).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(2).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::>(2).err().unwrap().code, + ffi::SQLITE_MISMATCH); // 3 is actually a float (c_double) - assert_eq!(row.get_checked::(3).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(3).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(3).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::>(3).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(3).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::>(3).err().unwrap().code, ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(3).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(3).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(3).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::>(3).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(3).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::>(3).err().unwrap().code, + ffi::SQLITE_MISMATCH); // 4 is actually NULL - assert_eq!(row.get_checked::(4).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(4).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(4).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(4).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::>(4).err().unwrap().code, ffi::SQLITE_MISMATCH); - assert_eq!(row.get_checked::(4).err().unwrap().code, ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(4).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(4).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(4).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(4).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::>(4).err().unwrap().code, + ffi::SQLITE_MISMATCH); + assert_eq!(row.get_checked::(4).err().unwrap().code, + ffi::SQLITE_MISMATCH); } } From ab6ab3b2e90c25608c81f38d7913d28ecde084db Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 17:01:19 -0500 Subject: [PATCH 20/53] Run rustfmt on named_params.rs --- src/named_params.rs | 84 +++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index eb70989..dd9cc2f 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -1,4 +1,4 @@ -//use std::collections::HashMap; +// use std::collections::HashMap; use std::ffi::CString; use libc::c_int; @@ -35,34 +35,41 @@ impl SqliteConnection { /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// underlying SQLite call fails. - pub fn query_named_row(&self, sql: &str, params: &[(&str, &ToSql)], f: F) -> SqliteResult - where F: FnOnce(SqliteRow) -> T { + pub fn query_named_row(&self, + sql: &str, + params: &[(&str, &ToSql)], + f: F) + -> SqliteResult + where F: FnOnce(SqliteRow) -> T + { let mut stmt = try!(self.prepare(sql)); let mut rows = try!(stmt.query_named(params)); match rows.next() { Some(row) => row.map(f), - None => Err(SqliteError{ - code: ffi::SQLITE_NOTICE, - message: "Query did not return a row".to_string(), - }) + None => { + Err(SqliteError { + code: ffi::SQLITE_NOTICE, + message: "Query did not return a row".to_string(), + }) + } } } } impl<'conn> SqliteStatement<'conn> { - /*pub fn parameter_names(&self) -> HashMap { - let n = unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) }; - let mut index_by_name = HashMap::with_capacity(n as usize); - for i in 1..n+1 { - let c_name = unsafe { ffi::sqlite3_bind_parameter_name(self.stmt, i) }; - if !c_name.is_null() { - let c_slice = unsafe { CStr::from_ptr(c_name).to_bytes() }; - index_by_name.insert(str::from_utf8(c_slice).unwrap().to_string(), n); - } - } - index_by_name - }*/ + // pub fn parameter_names(&self) -> HashMap { + // let n = unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) }; + // let mut index_by_name = HashMap::with_capacity(n as usize); + // for i in 1..n+1 { + // let c_name = unsafe { ffi::sqlite3_bind_parameter_name(self.stmt, i) }; + // if !c_name.is_null() { + // let c_slice = unsafe { CStr::from_ptr(c_name).to_bytes() }; + // index_by_name.insert(str::from_utf8(c_slice).unwrap().to_string(), n); + // } + // } + // index_by_name + // } /// Return the index of an SQL parameter given its name. /// @@ -71,12 +78,12 @@ impl<'conn> SqliteStatement<'conn> { /// Return None if `name` is invalid (NulError) or if no matching parameter is found. pub fn parameter_index(&self, name: &str) -> Option { unsafe { - CString::new(name).ok().and_then(|c_name| + CString::new(name).ok().and_then(|c_name| { match ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) { 0 => None, // A zero is returned if no matching parameter is found. - n => Some(n) + n => Some(n), } - ) + }) } } @@ -126,7 +133,9 @@ impl<'conn> SqliteStatement<'conn> { /// # Failure /// /// Will return `Err` if binding parameters fails. - pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) -> SqliteResult> { + pub fn query_named<'a>(&'a mut self, + params: &[(&str, &ToSql)]) + -> SqliteResult> { self.reset_if_needed(); unsafe { @@ -139,9 +148,9 @@ impl<'conn> SqliteStatement<'conn> { unsafe fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> { for &(name, value) in params { - let i = try!(self.parameter_index(name).ok_or(SqliteError{ + let i = try!(self.parameter_index(name).ok_or(SqliteError { code: ffi::SQLITE_MISUSE, - message: format!("Invalid parameter name: {}", name) + message: format!("Invalid parameter name: {}", name), })); try!(self.conn.decode_result(value.bind_parameter(self.stmt, i))); } @@ -158,26 +167,35 @@ mod test { let db = SqliteConnection::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_named_row("SELECT SUM(x) FROM foo WHERE x > :x", &[(":x", &0i32)], |r| r.get(0)).unwrap()); + assert_eq!(3i32, + db.query_named_row("SELECT SUM(x) FROM foo WHERE x > :x", + &[(":x", &0i32)], + |r| r.get(0)) + .unwrap()); } - #[test] + #[test] fn test_stmt_execute_named() { let db = SqliteConnection::open_in_memory().unwrap(); - let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER)"; + let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \ + INTEGER)"; db.execute_batch(sql).unwrap(); - let mut stmt = db.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)").unwrap(); + let mut stmt = db.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)") + .unwrap(); stmt.execute_named(&[(":name", &"one")]).unwrap(); } - #[test] + #[test] fn test_query_named() { let db = SqliteConnection::open_in_memory().unwrap(); - let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag INTEGER)"; + let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \ + INTEGER)"; db.execute_batch(sql).unwrap(); let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap(); From 2f220161a5c55be8d9abe205010b78cca2d9c47c Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 20:16:46 -0500 Subject: [PATCH 21/53] Add extra check to named-parameter insertion unit test --- src/named_params.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/named_params.rs b/src/named_params.rs index dd9cc2f..ecafe56 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -189,6 +189,12 @@ mod test { let mut stmt = db.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)") .unwrap(); stmt.execute_named(&[(":name", &"one")]).unwrap(); + + assert_eq!(1i32, + db.query_named_row("SELECT COUNT(*) FROM test WHERE name = :name", + &[(":name", &"one")], + |r| r.get(0)) + .unwrap()); } #[test] From 7338f23d4b25002573ced0dd35c7febaf190f78d Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 20:27:09 -0500 Subject: [PATCH 22/53] Add extra assertions around binding named parameters --- src/named_params.rs | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index ecafe56..743918e 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -147,6 +147,25 @@ impl<'conn> SqliteStatement<'conn> { } unsafe fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> { + // Always check that the number of parameters is correct. + assert!(params.len() as c_int == ffi::sqlite3_bind_parameter_count(self.stmt), + "incorrect number of parameters to query(): expected {}, got {}", + ffi::sqlite3_bind_parameter_count(self.stmt), + params.len()); + + // In debug, also sanity check that we got distinct parameter names. + debug_assert!({ + use std::collections::HashSet; + + let mut s = HashSet::with_capacity(params.len()); + for &(name, _) in params { + s.insert(name); + } + + s.len() == params.len() + }, + "named parameters must be unique"); + for &(name, value) in params { let i = try!(self.parameter_index(name).ok_or(SqliteError { code: ffi::SQLITE_MISUSE, @@ -186,8 +205,7 @@ mod test { INTEGER)"; db.execute_batch(sql).unwrap(); - let mut stmt = db.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)") - .unwrap(); + let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap(); stmt.execute_named(&[(":name", &"one")]).unwrap(); assert_eq!(1i32, @@ -207,4 +225,23 @@ mod test { let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap(); stmt.query_named(&[(":name", &"one")]).unwrap(); } + + #[test] + #[should_panic] + fn test_panic_on_incorrect_number_of_parameters() { + let db = SqliteConnection::open_in_memory().unwrap(); + + let mut stmt = db.prepare("SELECT 1 WHERE 1 = :one AND 2 = :two").unwrap(); + let _ = stmt.query_named(&[(":one", &1i32)]); + } + + #[test] + #[cfg(debug_assertions)] + #[should_panic] + fn test_debug_panic_on_incorrect_parameter_names() { + let db = SqliteConnection::open_in_memory().unwrap(); + + let mut stmt = db.prepare("SELECT 1 WHERE 1 = :one AND 2 = :two").unwrap(); + let _ = stmt.query_named(&[(":one", &1i32), (":one", &2i32)]); + } } From 21528452d768e46790d53208f8106a06117f1258 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 20:30:05 -0500 Subject: [PATCH 23/53] Clean up comments on named parameter methods --- src/named_params.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index 743918e..55ce06f 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -10,12 +10,15 @@ use types::ToSql; impl SqliteConnection { /// Convenience method to prepare and execute a single SQL statement with named parameter(s). /// + /// On success, returns the number of rows that were changed or inserted or deleted (via + /// `sqlite3_changes`). + /// /// ## Example /// /// ```rust,no_run /// # use rusqlite::{SqliteConnection, SqliteResult}; /// fn insert(conn: &SqliteConnection) -> SqliteResult { - /// conn.execute_named("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)", &[(":name", &"one")]) + /// conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")]) /// } /// ``` /// @@ -27,7 +30,8 @@ impl SqliteConnection { self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params)) } - /// Convenience method to execute a query with named parameter(s) that is expected to return a single row. + /// Convenience method to execute a query with named parameter(s) that is expected to return + /// a single row. /// /// If the query returns more than one row, all rows except the first are ignored. /// @@ -75,7 +79,7 @@ impl<'conn> SqliteStatement<'conn> { /// /// # Failure /// - /// Return None if `name` is invalid (NulError) or if no matching parameter is found. + /// Return None if `name` is invalid or if no matching parameter is found. pub fn parameter_index(&self, name: &str) -> Option { unsafe { CString::new(name).ok().and_then(|c_name| { @@ -98,8 +102,8 @@ impl<'conn> SqliteStatement<'conn> { /// ```rust,no_run /// # use rusqlite::{SqliteConnection, SqliteResult}; /// fn insert(conn: &SqliteConnection) -> SqliteResult { - /// let mut stmt = try!(conn.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)")); - /// return stmt.execute_named(&[(":name", &"one")]); + /// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)")); + /// stmt.execute_named(&[(":name", &"one")]) /// } /// ``` /// @@ -114,19 +118,20 @@ impl<'conn> SqliteStatement<'conn> { } } - /// Execute the prepared statement with named parameter(s), returning an iterator over the resulting rows. + /// Execute the prepared statement with named parameter(s), returning an iterator over the + /// resulting rows. /// /// ## Example /// /// ```rust,no_run /// # use rusqlite::{SqliteConnection, SqliteResult, SqliteRows}; /// fn query(conn: &SqliteConnection) -> SqliteResult<()> { - /// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name")); - /// let mut rows = try!(stmt.query_named(&[(":name", &"one")])); - /// for row in rows { - /// // ... - /// } - /// return Ok(()) + /// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name")); + /// let mut rows = try!(stmt.query_named(&[(":name", &"one")])); + /// for row in rows { + /// // ... + /// } + /// Ok(()) /// } /// ``` /// From 8d4b3e6a31637e01a04a371a10510075b0fe407d Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 20:30:43 -0500 Subject: [PATCH 24/53] Remove commented-out code --- src/named_params.rs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index 55ce06f..8df8e96 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -1,4 +1,3 @@ -// use std::collections::HashMap; use std::ffi::CString; use libc::c_int; @@ -62,19 +61,6 @@ impl SqliteConnection { } impl<'conn> SqliteStatement<'conn> { - // pub fn parameter_names(&self) -> HashMap { - // let n = unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) }; - // let mut index_by_name = HashMap::with_capacity(n as usize); - // for i in 1..n+1 { - // let c_name = unsafe { ffi::sqlite3_bind_parameter_name(self.stmt, i) }; - // if !c_name.is_null() { - // let c_slice = unsafe { CStr::from_ptr(c_name).to_bytes() }; - // index_by_name.insert(str::from_utf8(c_slice).unwrap().to_string(), n); - // } - // } - // index_by_name - // } - /// Return the index of an SQL parameter given its name. /// /// # Failure From 186cb5893bae2d338d6bc5f018d46433d9a40d7e Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 20:31:07 -0500 Subject: [PATCH 25/53] Rename query_named_row -> query_row_named. I think this is more consistent with Rust's tendency to group similar methods like `iter`, `iter_mut` by appending the difference as a suffix. --- src/named_params.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index 8df8e96..5a8e7b7 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -38,7 +38,7 @@ impl SqliteConnection { /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// underlying SQLite call fails. - pub fn query_named_row(&self, + pub fn query_row_named(&self, sql: &str, params: &[(&str, &ToSql)], f: F) @@ -183,7 +183,7 @@ mod test { 1); assert_eq!(3i32, - db.query_named_row("SELECT SUM(x) FROM foo WHERE x > :x", + db.query_row_named("SELECT SUM(x) FROM foo WHERE x > :x", &[(":x", &0i32)], |r| r.get(0)) .unwrap()); @@ -200,7 +200,7 @@ mod test { stmt.execute_named(&[(":name", &"one")]).unwrap(); assert_eq!(1i32, - db.query_named_row("SELECT COUNT(*) FROM test WHERE name = :name", + db.query_row_named("SELECT COUNT(*) FROM test WHERE name = :name", &[(":name", &"one")], |r| r.get(0)) .unwrap()); From b7468b2c4bde0386d065108d1e64f0e28e6d3c46 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 20:41:31 -0500 Subject: [PATCH 26/53] Make parameter_index return a Result> instead of squashing string conversion errors into None. --- src/named_params.rs | 50 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index 5a8e7b7..f40abaa 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -1,9 +1,9 @@ -use std::ffi::CString; use libc::c_int; use super::ffi; -use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows, SqliteRow}; +use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows, SqliteRow, + str_to_cstring}; use types::ToSql; impl SqliteConnection { @@ -65,17 +65,15 @@ impl<'conn> SqliteStatement<'conn> { /// /// # Failure /// - /// Return None if `name` is invalid or if no matching parameter is found. - pub fn parameter_index(&self, name: &str) -> Option { - unsafe { - CString::new(name).ok().and_then(|c_name| { - match ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) { - 0 => None, // A zero is returned if no matching parameter is found. - n => Some(n), - } - }) - - } + /// Will return Err if `name` is invalid. Will return Ok(None) if the name + /// is valid but not a bound parameter of this statement. + pub fn parameter_index(&self, name: &str) -> SqliteResult> { + let c_name = try!(str_to_cstring(name)); + let c_index = unsafe { ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) }; + Ok(match c_index { + 0 => None, // A zero is returned if no matching parameter is found. + n => Some(n), + }) } /// Execute the prepared statement with named parameter(s). @@ -98,8 +96,8 @@ impl<'conn> SqliteStatement<'conn> { /// Will return `Err` if binding parameters fails, the executed statement returns rows (in /// which case `query` should be used instead), or the underling SQLite call fails. pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult { + try!(self.bind_named_parameters(params)); unsafe { - try!(self.bind_named_parameters(params)); self.execute_() } } @@ -128,20 +126,17 @@ impl<'conn> SqliteStatement<'conn> { params: &[(&str, &ToSql)]) -> SqliteResult> { self.reset_if_needed(); - - unsafe { - try!(self.bind_named_parameters(params)); - } + try!(self.bind_named_parameters(params)); self.needs_reset = true; Ok(SqliteRows::new(self)) } - unsafe fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> { + fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> { // Always check that the number of parameters is correct. - assert!(params.len() as c_int == ffi::sqlite3_bind_parameter_count(self.stmt), + assert!(params.len() as c_int == unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) }, "incorrect number of parameters to query(): expected {}, got {}", - ffi::sqlite3_bind_parameter_count(self.stmt), + unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) }, params.len()); // In debug, also sanity check that we got distinct parameter names. @@ -158,11 +153,14 @@ impl<'conn> SqliteStatement<'conn> { "named parameters must be unique"); for &(name, value) in params { - let i = try!(self.parameter_index(name).ok_or(SqliteError { - code: ffi::SQLITE_MISUSE, - message: format!("Invalid parameter name: {}", name), - })); - try!(self.conn.decode_result(value.bind_parameter(self.stmt, i))); + if let Some(i) = try!(self.parameter_index(name)) { + try!(self.conn.decode_result(unsafe { value.bind_parameter(self.stmt, i) })); + } else { + return Err(SqliteError { + code: ffi::SQLITE_MISUSE, + message: format!("Invalid parameter name: {}", name), + }); + } } Ok(()) } From 0051ff47a6cc79c96490a1073b151e75801750c6 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 20:48:38 -0500 Subject: [PATCH 27/53] Refactor: Extract match to get an expected row into its own method. --- src/lib.rs | 48 +++++++++++++++++++++------------------------ src/named_params.rs | 10 +--------- 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2832900..520faba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -369,20 +369,12 @@ impl SqliteConnection { /// underlying SQLite call fails. pub fn query_row(&self, sql: &str, params: &[&ToSql], f: F) -> SqliteResult where F: FnOnce(SqliteRow) -> T - { - let mut stmt = try!(self.prepare(sql)); - let mut rows = try!(stmt.query(params)); + { + let mut stmt = try!(self.prepare(sql)); + let mut rows = try!(stmt.query(params)); - match rows.next() { - Some(row) => row.map(f), - None => { - Err(SqliteError { - code: ffi::SQLITE_NOTICE, - message: "Query did not return a row".to_string(), - }) - } - } - } + rows.get_expected_row().map(f) + } /// Convenience method to execute a query that is expected to return a single row, /// and execute a mapping via `f` on that returned row with the possibility of failure. @@ -408,20 +400,12 @@ impl SqliteConnection { pub fn query_row_and_then(&self, sql: &str, params: &[&ToSql], f: F) -> Result where F: FnOnce(SqliteRow) -> Result, E: convert::From - { - let mut stmt = try!(self.prepare(sql)); - let mut rows = try!(stmt.query(params)); + { + let mut stmt = try!(self.prepare(sql)); + let mut rows = try!(stmt.query(params)); - match rows.next() { - Some(row) => row.map_err(E::from).and_then(f), - None => { - Err(E::from(SqliteError { - code: ffi::SQLITE_NOTICE, - message: "Query did not return a row".to_string(), - })) - } - } - } + rows.get_expected_row().map_err(E::from).and_then(f) + } /// Convenience method to execute a query that is expected to return a single row. /// @@ -1049,6 +1033,18 @@ impl<'stmt> SqliteRows<'stmt> { failed: false, } } + + fn get_expected_row(&mut self) -> SqliteResult> { + match self.next() { + Some(row) => row, + None => { + Err(SqliteError { + code: ffi::SQLITE_NOTICE, + message: "Query did not return a row".to_string(), + }) + } + } + } } impl<'stmt> Iterator for SqliteRows<'stmt> { diff --git a/src/named_params.rs b/src/named_params.rs index f40abaa..80a43a3 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -48,15 +48,7 @@ impl SqliteConnection { let mut stmt = try!(self.prepare(sql)); let mut rows = try!(stmt.query_named(params)); - match rows.next() { - Some(row) => row.map(f), - None => { - Err(SqliteError { - code: ffi::SQLITE_NOTICE, - message: "Query did not return a row".to_string(), - }) - } - } + rows.get_expected_row().map(f) } } From 599e30f3724f0113b294882571eea0932d86f650 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 20:49:47 -0500 Subject: [PATCH 28/53] Rename bind_named_parameters -> bind_parameters_named. --- src/named_params.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index 80a43a3..f46b77c 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -88,7 +88,7 @@ impl<'conn> SqliteStatement<'conn> { /// Will return `Err` if binding parameters fails, the executed statement returns rows (in /// which case `query` should be used instead), or the underling SQLite call fails. pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult { - try!(self.bind_named_parameters(params)); + try!(self.bind_parameters_named(params)); unsafe { self.execute_() } @@ -118,13 +118,13 @@ impl<'conn> SqliteStatement<'conn> { params: &[(&str, &ToSql)]) -> SqliteResult> { self.reset_if_needed(); - try!(self.bind_named_parameters(params)); + try!(self.bind_parameters_named(params)); self.needs_reset = true; Ok(SqliteRows::new(self)) } - fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> { + fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> { // Always check that the number of parameters is correct. assert!(params.len() as c_int == unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) }, "incorrect number of parameters to query(): expected {}, got {}", From 489b7df4510c225b7fe07632e06d226c23ddae5c Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Thu, 10 Dec 2015 20:51:16 -0500 Subject: [PATCH 29/53] Add named parameters to Changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 822668b..b29a517 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ # Version UPCOMING (TBD) * Adds `backup` feature that exposes SQLite's online backup API. +* Adds a variety of `..._named` methods for executing queries using named placeholder parameters. # Version 0.5.0 (2015-12-08) From 0c3575e845bd49c4d44da2ed9aa4631f7f964709 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 11:40:53 -0500 Subject: [PATCH 30/53] Fix segfault in regexp user function test --- src/functions.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index fa6220e..3f21889 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -278,16 +278,16 @@ mod test { } extern "C" fn regexp_free(raw: *mut c_void) { - unsafe { - Box::from_raw(raw); - } + let _: Box = unsafe { + Box::from_raw(mem::transmute(raw)) + }; } extern "C" fn regexp(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { unsafe { let mut re_ptr = ffi::sqlite3_get_auxdata(ctx, 0) as *const Regex; - let mut re_opt = None; - if re_ptr.is_null() { + let need_re = re_ptr.is_null(); + if need_re { let raw = String::parameter_value(*argv.offset(0)); if raw.is_err() { let msg = CString::new(format!("{}", raw.unwrap_err())).unwrap(); @@ -300,9 +300,8 @@ mod test { ffi::sqlite3_result_error(ctx, msg.as_ptr(), -1); return } - let re = comp.unwrap(); - re_ptr = &re as *const Regex; - re_opt = Some(re); + let re = Box::new(comp.unwrap()); + re_ptr = Box::into_raw(re); } let text = String::parameter_value(*argv.offset(1)); @@ -311,8 +310,8 @@ mod test { (*re_ptr).is_match(text.as_ref()).set_result(ctx); } - if re_opt.is_some() { - ffi::sqlite3_set_auxdata(ctx, 0, mem::transmute(Box::into_raw(Box::new(re_opt.unwrap()))), Some(regexp_free)); + if need_re { + ffi::sqlite3_set_auxdata(ctx, 0, mem::transmute(re_ptr), Some(regexp_free)); } } } From aae431760ecf95742507653aa5718ea91ef90f6d Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 11:41:40 -0500 Subject: [PATCH 31/53] rustfmt - no code changes --- src/functions.rs | 103 ++++++++++++++++++++++++++++++----------------- 1 file changed, 65 insertions(+), 38 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index 3f21889..52ed7ca 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -1,15 +1,15 @@ //! Create or redefine SQL functions -use std::ffi::{CStr}; +use std::ffi::CStr; use std::mem; use std::ptr; use std::str; use libc::{c_int, c_double, c_char}; use ffi; -pub use ffi::sqlite3_context as sqlite3_context; -pub use ffi::sqlite3_value as sqlite3_value; -pub use ffi::sqlite3_value_type as sqlite3_value_type; -pub use ffi::sqlite3_value_numeric_type as sqlite3_value_numeric_type; +pub use ffi::sqlite3_context; +pub use ffi::sqlite3_value; +pub use ffi::sqlite3_value_type; +pub use ffi::sqlite3_value_numeric_type; use types::Null; @@ -49,12 +49,16 @@ impl<'a> ToResult for &'a str { let length = self.len(); if length > ::std::i32::MAX as usize { ffi::sqlite3_result_error_toobig(ctx); - return + return; } match str_to_cstring(self) { - Ok(c_str) => ffi::sqlite3_result_text(ctx, c_str.as_ptr(), length as c_int, - ffi::SQLITE_TRANSIENT()), - Err(_) => ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE), // TODO sqlite3_result_error + Ok(c_str) => { + ffi::sqlite3_result_text(ctx, + c_str.as_ptr(), + length as c_int, + ffi::SQLITE_TRANSIENT()) + } + Err(_) => ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISUSE), // TODO sqlite3_result_error } } } @@ -69,10 +73,12 @@ impl<'a> ToResult for &'a [u8] { unsafe fn set_result(&self, ctx: *mut sqlite3_context) { if self.len() > ::std::i32::MAX as usize { ffi::sqlite3_result_error_toobig(ctx); - return + return; } - ffi::sqlite3_result_blob( - ctx, mem::transmute(self.as_ptr()), self.len() as c_int, ffi::SQLITE_TRANSIENT()) + ffi::sqlite3_result_blob(ctx, + mem::transmute(self.as_ptr()), + self.len() as c_int, + ffi::SQLITE_TRANSIENT()) } } @@ -155,7 +161,8 @@ impl FromValue for c_double { } unsafe fn parameter_has_valid_sqlite_type(v: *mut sqlite3_value) -> bool { - sqlite3_value_numeric_type(v) == ffi::SQLITE_FLOAT || sqlite3_value_numeric_type(v) == ffi::SQLITE_INTEGER + sqlite3_value_numeric_type(v) == ffi::SQLITE_FLOAT || + sqlite3_value_numeric_type(v) == ffi::SQLITE_INTEGER } } @@ -167,9 +174,13 @@ impl FromValue for String { } else { let c_slice = CStr::from_ptr(c_text as *const c_char).to_bytes(); let utf8_str = str::from_utf8(c_slice); - utf8_str - .map(|s| { s.to_string() }) - .map_err(|e| { SqliteError{code: 0, message: e.to_string()} }) + utf8_str.map(|s| s.to_string()) + .map_err(|e| { + SqliteError { + code: 0, + message: e.to_string(), + } + }) } } @@ -184,7 +195,8 @@ impl FromValue for Vec { let c_blob = ffi::sqlite3_value_blob(v); let len = ffi::sqlite3_value_bytes(v); - assert!(len >= 0, "unexpected negative return from sqlite3_value_bytes"); + assert!(len >= 0, + "unexpected negative return from sqlite3_value_bytes"); let len = len as usize; Ok(from_raw_parts(mem::transmute(c_blob), len).to_vec()) @@ -205,8 +217,7 @@ impl FromValue for Option { } unsafe fn parameter_has_valid_sqlite_type(v: *mut sqlite3_value) -> bool { - sqlite3_value_type(v) == ffi::SQLITE_NULL || - T::parameter_has_valid_sqlite_type(v) + sqlite3_value_type(v) == ffi::SQLITE_NULL || T::parameter_has_valid_sqlite_type(v) } } @@ -214,25 +225,45 @@ impl FromValue for Option { // sqlite3_get_auxdata // sqlite3_set_auxdata -pub type ScalarFunc = - Option; +pub type ScalarFunc = Option; impl SqliteConnection { // TODO pApp - pub fn create_scalar_function(&self, fn_name: &str, n_arg: c_int, deterministic: bool, x_func: ScalarFunc) -> SqliteResult<()> { + pub fn create_scalar_function(&self, + fn_name: &str, + n_arg: c_int, + deterministic: bool, + x_func: ScalarFunc) + -> SqliteResult<()> { self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) } } impl InnerSqliteConnection { - pub fn create_scalar_function(&mut self, fn_name: &str, n_arg: c_int, deterministic: bool, x_func: ScalarFunc) -> SqliteResult<()> { + pub fn create_scalar_function(&mut self, + fn_name: &str, + n_arg: c_int, + deterministic: bool, + x_func: ScalarFunc) + -> SqliteResult<()> { let c_name = try!(str_to_cstring(fn_name)); let mut flags = ffi::SQLITE_UTF8; if deterministic { flags |= ffi::SQLITE_DETERMINISTIC; } let r = unsafe { - ffi::sqlite3_create_function_v2(self.db(), c_name.as_ptr(), n_arg, flags, ptr::null_mut(), x_func, None, None, None) + ffi::sqlite3_create_function_v2(self.db(), + c_name.as_ptr(), + n_arg, + flags, + ptr::null_mut(), + x_func, + None, + None, + None) }; self.decode_result(r) } @@ -243,16 +274,16 @@ mod test { extern crate regex; use std::boxed::Box; - use std::ffi::{CString}; + use std::ffi::CString; use std::mem; use libc::{c_int, c_double, c_void}; use self::regex::Regex; use SqliteConnection; use ffi; - use ffi::sqlite3_context as sqlite3_context; - use ffi::sqlite3_value as sqlite3_value; - use functions::{FromValue,ToResult}; + use ffi::sqlite3_context; + use ffi::sqlite3_value; + use functions::{FromValue, ToResult}; extern "C" fn half(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { unsafe { @@ -270,17 +301,13 @@ mod test { fn test_half() { let db = SqliteConnection::open_in_memory().unwrap(); db.create_scalar_function("half", 1, true, Some(half)).unwrap(); - let result = db.query_row("SELECT half(6)", - &[], - |r| r.get::(0)); + let result = db.query_row("SELECT half(6)", &[], |r| r.get::(0)); assert_eq!(3f64, result.unwrap()); } extern "C" fn regexp_free(raw: *mut c_void) { - let _: Box = unsafe { - Box::from_raw(mem::transmute(raw)) - }; + let _: Box = unsafe { Box::from_raw(mem::transmute(raw)) }; } extern "C" fn regexp(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { @@ -292,13 +319,13 @@ mod test { if raw.is_err() { let msg = CString::new(format!("{}", raw.unwrap_err())).unwrap(); ffi::sqlite3_result_error(ctx, msg.as_ptr(), -1); - return + return; } let comp = Regex::new(raw.unwrap().as_ref()); if comp.is_err() { let msg = CString::new(format!("{}", comp.unwrap_err())).unwrap(); ffi::sqlite3_result_error(ctx, msg.as_ptr(), -1); - return + return; } let re = Box::new(comp.unwrap()); re_ptr = Box::into_raw(re); @@ -321,8 +348,8 @@ mod test { let db = SqliteConnection::open_in_memory().unwrap(); db.create_scalar_function("regexp", 2, true, Some(regexp)).unwrap(); let result = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", - &[], - |r| r.get::(0)); + &[], + |r| r.get::(0)); assert_eq!(true, result.unwrap()); } From 29494f46f6cf7528caed429048f1a60afe8e7134 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 12:01:05 -0500 Subject: [PATCH 32/53] Let create_scalar_function take an FnMut instead of a extern "C" fn. --- src/functions.rs | 75 ++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index 52ed7ca..bb386a6 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -1,9 +1,8 @@ //! Create or redefine SQL functions use std::ffi::CStr; use std::mem; -use std::ptr; use std::str; -use libc::{c_int, c_double, c_char}; +use libc::{c_int, c_double, c_char, c_void}; use ffi; pub use ffi::sqlite3_context; @@ -225,30 +224,50 @@ impl FromValue for Option { // sqlite3_get_auxdata // sqlite3_set_auxdata -pub type ScalarFunc = Option; +pub trait ScalarFunction: FnMut(*mut sqlite3_context, c_int, *mut *mut sqlite3_value) {} +impl ScalarFunction for F {} impl SqliteConnection { - // TODO pApp - pub fn create_scalar_function(&self, - fn_name: &str, - n_arg: c_int, - deterministic: bool, - x_func: ScalarFunc) - -> SqliteResult<()> { + pub fn create_scalar_function(&self, + fn_name: &str, + n_arg: c_int, + deterministic: bool, + x_func: F) + -> SqliteResult<()> + where F: ScalarFunction + { self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) } } impl InnerSqliteConnection { - pub fn create_scalar_function(&mut self, - fn_name: &str, - n_arg: c_int, - deterministic: bool, - x_func: ScalarFunc) - -> SqliteResult<()> { + pub fn create_scalar_function(&mut self, + fn_name: &str, + n_arg: c_int, + deterministic: bool, + x_func: F) + -> SqliteResult<()> + where F: ScalarFunction + { + extern "C" fn free_boxed_closure(p: *mut c_void) + where F: ScalarFunction + { + let _: Box = unsafe { Box::from_raw(mem::transmute(p)) }; + } + + extern "C" fn call_boxed_closure(ctx: *mut sqlite3_context, + argc: c_int, + argv: *mut *mut sqlite3_value) + where F: ScalarFunction + { + unsafe { + let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx)); + assert!(!boxed_f.is_null(), "Internal error - null function pointer"); + (*boxed_f)(ctx, argc, argv); + } + } + + let boxed_f: *mut F = Box::into_raw(Box::new(x_func)); let c_name = try!(str_to_cstring(fn_name)); let mut flags = ffi::SQLITE_UTF8; if deterministic { @@ -259,11 +278,11 @@ impl InnerSqliteConnection { c_name.as_ptr(), n_arg, flags, - ptr::null_mut(), - x_func, + mem::transmute(boxed_f), + Some(call_boxed_closure::), None, None, - None) + Some(free_boxed_closure::)) }; self.decode_result(r) } @@ -285,7 +304,7 @@ mod test { use ffi::sqlite3_value; use functions::{FromValue, ToResult}; - extern "C" fn half(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { + fn half(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { unsafe { let arg = *argv.offset(0); if c_double::parameter_has_valid_sqlite_type(arg) { @@ -298,9 +317,9 @@ mod test { } #[test] - fn test_half() { + fn test_function_half() { let db = SqliteConnection::open_in_memory().unwrap(); - db.create_scalar_function("half", 1, true, Some(half)).unwrap(); + db.create_scalar_function("half", 1, true, half).unwrap(); let result = db.query_row("SELECT half(6)", &[], |r| r.get::(0)); assert_eq!(3f64, result.unwrap()); @@ -310,7 +329,7 @@ mod test { let _: Box = unsafe { Box::from_raw(mem::transmute(raw)) }; } - extern "C" fn regexp(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { + fn regexp(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { unsafe { let mut re_ptr = ffi::sqlite3_get_auxdata(ctx, 0) as *const Regex; let need_re = re_ptr.is_null(); @@ -344,9 +363,9 @@ mod test { } #[test] - fn test_regexp() { + fn test_function_regexp() { let db = SqliteConnection::open_in_memory().unwrap(); - db.create_scalar_function("regexp", 2, true, Some(regexp)).unwrap(); + db.create_scalar_function("regexp", 2, true, regexp).unwrap(); let result = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", &[], |r| r.get::(0)); From 94d40c41c78038c2faedb18c5d748395e5e64038 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 13:54:08 -0500 Subject: [PATCH 33/53] Introduce Context wrapper for user-defined functions. This commit adds get/set auxilliary data for arguments; more to come. --- src/functions.rs | 104 ++++++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 38 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index bb386a6..4c024b2 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -224,8 +224,37 @@ impl FromValue for Option { // sqlite3_get_auxdata // sqlite3_set_auxdata -pub trait ScalarFunction: FnMut(*mut sqlite3_context, c_int, *mut *mut sqlite3_value) {} -impl ScalarFunction for F {} +unsafe extern "C" fn free_boxed_value(p: *mut c_void) { + let _: Box = Box::from_raw(mem::transmute(p)); +} + +pub struct Context { + pub ctx: *mut sqlite3_context, +} + +impl Context { + pub fn set_aux(&self, arg: c_int, value: T) { + let boxed = Box::into_raw(Box::new(value)); + unsafe { + ffi::sqlite3_set_auxdata(self.ctx, + arg, + mem::transmute(boxed), + Some(mem::transmute(free_boxed_value::))) + }; + } + + pub unsafe fn get_aux(&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) + } + } +} + +pub trait ScalarFunction: FnMut(&Context, c_int, *mut *mut sqlite3_value) {} +impl ScalarFunction for F {} impl SqliteConnection { pub fn create_scalar_function(&self, @@ -249,21 +278,16 @@ impl InnerSqliteConnection { -> SqliteResult<()> where F: ScalarFunction { - extern "C" fn free_boxed_closure(p: *mut c_void) - where F: ScalarFunction - { - let _: Box = unsafe { Box::from_raw(mem::transmute(p)) }; - } - extern "C" fn call_boxed_closure(ctx: *mut sqlite3_context, argc: c_int, argv: *mut *mut sqlite3_value) where F: ScalarFunction { + let ctx = Context { ctx: ctx }; unsafe { - let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx)); + let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx.ctx)); assert!(!boxed_f.is_null(), "Internal error - null function pointer"); - (*boxed_f)(ctx, argc, argv); + (*boxed_f)(&ctx, argc, argv); } } @@ -282,7 +306,7 @@ impl InnerSqliteConnection { Some(call_boxed_closure::), None, None, - Some(free_boxed_closure::)) + Some(mem::transmute(free_boxed_value::))) }; self.decode_result(r) } @@ -292,26 +316,23 @@ impl InnerSqliteConnection { mod test { extern crate regex; - use std::boxed::Box; use std::ffi::CString; - use std::mem; - use libc::{c_int, c_double, c_void}; + use libc::{c_int, c_double}; use self::regex::Regex; use SqliteConnection; use ffi; - use ffi::sqlite3_context; use ffi::sqlite3_value; - use functions::{FromValue, ToResult}; + use functions::{Context, FromValue, ToResult}; - fn half(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { + fn half(ctx: &Context, _: c_int, argv: *mut *mut sqlite3_value) { unsafe { let arg = *argv.offset(0); if c_double::parameter_has_valid_sqlite_type(arg) { let value = c_double::parameter_value(arg).unwrap() / 2f64; - value.set_result(ctx); + value.set_result(ctx.ctx); } else { - ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_MISMATCH); + ffi::sqlite3_result_error_code(ctx.ctx, ffi::SQLITE_MISMATCH); } } } @@ -325,40 +346,47 @@ mod test { assert_eq!(3f64, result.unwrap()); } - extern "C" fn regexp_free(raw: *mut c_void) { - let _: Box = unsafe { Box::from_raw(mem::transmute(raw)) }; - } - - fn regexp(ctx: *mut sqlite3_context, _: c_int, argv: *mut *mut sqlite3_value) { - unsafe { - let mut re_ptr = ffi::sqlite3_get_auxdata(ctx, 0) as *const Regex; - let need_re = re_ptr.is_null(); - if need_re { + fn regexp(ctx: &Context, _: c_int, argv: *mut *mut sqlite3_value) { + let saved_re: Option<&Regex> = unsafe { ctx.get_aux(0) }; + let new_re = match saved_re { + None => unsafe { let raw = String::parameter_value(*argv.offset(0)); if raw.is_err() { let msg = CString::new(format!("{}", raw.unwrap_err())).unwrap(); - ffi::sqlite3_result_error(ctx, msg.as_ptr(), -1); + ffi::sqlite3_result_error(ctx.ctx, msg.as_ptr(), -1); return; } let comp = Regex::new(raw.unwrap().as_ref()); if comp.is_err() { let msg = CString::new(format!("{}", comp.unwrap_err())).unwrap(); - ffi::sqlite3_result_error(ctx, msg.as_ptr(), -1); + ffi::sqlite3_result_error(ctx.ctx, msg.as_ptr(), -1); return; } - let re = Box::new(comp.unwrap()); - re_ptr = Box::into_raw(re); - } + Some(comp.unwrap()) + }, + Some(_) => None, + }; - let text = String::parameter_value(*argv.offset(1)); + { + let re = saved_re.unwrap_or_else(|| new_re.as_ref().unwrap()); + + let text = unsafe { String::parameter_value(*argv.offset(1)) }; if text.is_ok() { let text = text.unwrap(); - (*re_ptr).is_match(text.as_ref()).set_result(ctx); + unsafe { + re.is_match(text.as_ref()).set_result(ctx.ctx); + } + } else { + let msg = CString::new(format!("{}", text.unwrap_err())).unwrap(); + unsafe { + ffi::sqlite3_result_error(ctx.ctx, msg.as_ptr(), -1); + } + return; } + } - if need_re { - ffi::sqlite3_set_auxdata(ctx, 0, mem::transmute(re_ptr), Some(regexp_free)); - } + if let Some(re) = new_re { + ctx.set_aux(0, re); } } From 81ec7fe7cd12bf2bb28e07170984df0adcdfa47e Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 14:46:28 -0500 Subject: [PATCH 34/53] Add `get` to function::Context. This allows user-defined functions to now only accept a `Context`, as it embeds the arguments inside itself. --- src/functions.rs | 93 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index 4c024b2..6481b8a 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -1,6 +1,7 @@ //! Create or redefine SQL functions use std::ffi::CStr; use std::mem; +use std::slice; use std::str; use libc::{c_int, c_double, c_char, c_void}; @@ -228,11 +229,30 @@ unsafe extern "C" fn free_boxed_value(p: *mut c_void) { let _: Box = Box::from_raw(mem::transmute(p)); } -pub struct Context { +pub struct Context<'a> { pub ctx: *mut sqlite3_context, + args: &'a [*mut sqlite3_value], } -impl Context { +impl<'a> Context<'a> { + pub fn len(&self) -> usize { + self.args.len() + } + + pub fn get(&self, idx: usize) -> SqliteResult { + let arg = self.args[idx]; + unsafe { + if T::parameter_has_valid_sqlite_type(arg) { + T::parameter_value(arg) + } else { + Err(SqliteError { + code: ffi::SQLITE_MISMATCH, + message: "Invalid value type".to_string(), + }) + } + } + } + pub fn set_aux(&self, arg: c_int, value: T) { let boxed = Box::into_raw(Box::new(value)); unsafe { @@ -253,9 +273,6 @@ impl Context { } } -pub trait ScalarFunction: FnMut(&Context, c_int, *mut *mut sqlite3_value) {} -impl ScalarFunction for F {} - impl SqliteConnection { pub fn create_scalar_function(&self, fn_name: &str, @@ -263,31 +280,34 @@ impl SqliteConnection { deterministic: bool, x_func: F) -> SqliteResult<()> - where F: ScalarFunction + where F: FnMut(&Context) { self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) } } impl InnerSqliteConnection { - pub fn create_scalar_function(&mut self, - fn_name: &str, - n_arg: c_int, - deterministic: bool, - x_func: F) - -> SqliteResult<()> - where F: ScalarFunction + fn create_scalar_function(&mut self, + fn_name: &str, + n_arg: c_int, + deterministic: bool, + x_func: F) + -> SqliteResult<()> + where F: FnMut(&Context) { extern "C" fn call_boxed_closure(ctx: *mut sqlite3_context, argc: c_int, argv: *mut *mut sqlite3_value) - where F: ScalarFunction + where F: FnMut(&Context) { - let ctx = Context { ctx: ctx }; unsafe { + let ctx = Context { + ctx: ctx, + args: slice::from_raw_parts(argv, argc as usize), + }; let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx.ctx)); assert!(!boxed_f.is_null(), "Internal error - null function pointer"); - (*boxed_f)(&ctx, argc, argv); + (*boxed_f)(&ctx); } } @@ -317,23 +337,18 @@ mod test { extern crate regex; use std::ffi::CString; - use libc::{c_int, c_double}; + use libc::c_double; use self::regex::Regex; use SqliteConnection; use ffi; - use ffi::sqlite3_value; - use functions::{Context, FromValue, ToResult}; + use functions::{Context, ToResult}; - fn half(ctx: &Context, _: c_int, argv: *mut *mut sqlite3_value) { - unsafe { - let arg = *argv.offset(0); - if c_double::parameter_has_valid_sqlite_type(arg) { - let value = c_double::parameter_value(arg).unwrap() / 2f64; - value.set_result(ctx.ctx); - } else { - ffi::sqlite3_result_error_code(ctx.ctx, ffi::SQLITE_MISMATCH); - } + fn half(ctx: &Context) { + assert!(ctx.len() == 1, "called with unexpected number of arguments"); + match ctx.get::(0) { + Ok(value) => unsafe { (value / 2f64).set_result(ctx.ctx) }, + Err(err) => unsafe { ffi::sqlite3_result_error_code(ctx.ctx, err.code) }, } } @@ -346,11 +361,13 @@ mod test { assert_eq!(3f64, result.unwrap()); } - fn regexp(ctx: &Context, _: c_int, argv: *mut *mut sqlite3_value) { + fn regexp(ctx: &Context) { + assert!(ctx.len() == 2, "called with unexpected number of arguments"); + let saved_re: Option<&Regex> = unsafe { ctx.get_aux(0) }; let new_re = match saved_re { None => unsafe { - let raw = String::parameter_value(*argv.offset(0)); + let raw = ctx.get::(0); if raw.is_err() { let msg = CString::new(format!("{}", raw.unwrap_err())).unwrap(); ffi::sqlite3_result_error(ctx.ctx, msg.as_ptr(), -1); @@ -370,7 +387,7 @@ mod test { { let re = saved_re.unwrap_or_else(|| new_re.as_ref().unwrap()); - let text = unsafe { String::parameter_value(*argv.offset(1)) }; + let text = ctx.get::(1); if text.is_ok() { let text = text.unwrap(); unsafe { @@ -391,13 +408,27 @@ mod test { } #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] fn test_function_regexp() { let db = SqliteConnection::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).unwrap(); + let result = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", &[], |r| r.get::(0)); assert_eq!(true, result.unwrap()); + + let result = db.query_row("SELECT COUNT(*) FROM foo WHERE regexp('l.s[aeiouy]', x) == 1", + &[], + |r| r.get::(0)); + + assert_eq!(2, result.unwrap()); } } From 3913e89f9447d75cef5adffeda8e1808e418e88c Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 15:08:40 -0500 Subject: [PATCH 35/53] Allow user scalar functions to return results. This removes the need for scalar functions to have direct access to the context (in order to set the return value). --- src/functions.rs | 114 +++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 63 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index 6481b8a..0552dc2 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -221,16 +221,12 @@ impl FromValue for Option { } } -// sqlite3_user_data -// sqlite3_get_auxdata -// sqlite3_set_auxdata - unsafe extern "C" fn free_boxed_value(p: *mut c_void) { let _: Box = Box::from_raw(mem::transmute(p)); } pub struct Context<'a> { - pub ctx: *mut sqlite3_context, + ctx: *mut sqlite3_context, args: &'a [*mut sqlite3_value], } @@ -274,31 +270,34 @@ impl<'a> Context<'a> { } impl SqliteConnection { - pub fn create_scalar_function(&self, - fn_name: &str, - n_arg: c_int, - deterministic: bool, - x_func: F) - -> SqliteResult<()> - where F: FnMut(&Context) + pub fn create_scalar_function(&self, + fn_name: &str, + n_arg: c_int, + deterministic: bool, + x_func: F) + -> SqliteResult<()> + where F: FnMut(&Context) -> SqliteResult, + T: ToResult { self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) } } impl InnerSqliteConnection { - fn create_scalar_function(&mut self, - fn_name: &str, - n_arg: c_int, - deterministic: bool, - x_func: F) - -> SqliteResult<()> - where F: FnMut(&Context) + fn create_scalar_function(&mut self, + fn_name: &str, + n_arg: c_int, + deterministic: bool, + x_func: F) + -> SqliteResult<()> + where F: FnMut(&Context) -> SqliteResult, + T: ToResult { - extern "C" fn call_boxed_closure(ctx: *mut sqlite3_context, - argc: c_int, - argv: *mut *mut sqlite3_value) - where F: FnMut(&Context) + extern "C" fn call_boxed_closure(ctx: *mut sqlite3_context, + argc: c_int, + argv: *mut *mut sqlite3_value) + where F: FnMut(&Context) -> SqliteResult, + T: ToResult { unsafe { let ctx = Context { @@ -307,7 +306,15 @@ impl InnerSqliteConnection { }; let boxed_f: *mut F = mem::transmute(ffi::sqlite3_user_data(ctx.ctx)); assert!(!boxed_f.is_null(), "Internal error - null function pointer"); - (*boxed_f)(&ctx); + match (*boxed_f)(&ctx) { + Ok(r) => r.set_result(ctx.ctx), + Err(e) => { + ffi::sqlite3_result_error_code(ctx.ctx, e.code); + if let Ok(cstr) = str_to_cstring(&e.message) { + ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1); + } + }, + } } } @@ -323,7 +330,7 @@ impl InnerSqliteConnection { n_arg, flags, mem::transmute(boxed_f), - Some(call_boxed_closure::), + Some(call_boxed_closure::), None, None, Some(mem::transmute(free_boxed_value::))) @@ -336,20 +343,17 @@ impl InnerSqliteConnection { mod test { extern crate regex; - use std::ffi::CString; use libc::c_double; use self::regex::Regex; - use SqliteConnection; + use {SqliteConnection, SqliteError, SqliteResult}; use ffi; - use functions::{Context, ToResult}; + use functions::Context; - fn half(ctx: &Context) { + fn half(ctx: &Context) -> SqliteResult { assert!(ctx.len() == 1, "called with unexpected number of arguments"); - match ctx.get::(0) { - Ok(value) => unsafe { (value / 2f64).set_result(ctx.ctx) }, - Err(err) => unsafe { ffi::sqlite3_result_error_code(ctx.ctx, err.code) }, - } + let value = try!(ctx.get::(0)); + Ok(value / 2f64) } #[test] @@ -361,50 +365,34 @@ mod test { assert_eq!(3f64, result.unwrap()); } - fn regexp(ctx: &Context) { + fn regexp(ctx: &Context) -> SqliteResult { assert!(ctx.len() == 2, "called with unexpected number of arguments"); let saved_re: Option<&Regex> = unsafe { ctx.get_aux(0) }; let new_re = match saved_re { - None => unsafe { - let raw = ctx.get::(0); - if raw.is_err() { - let msg = CString::new(format!("{}", raw.unwrap_err())).unwrap(); - ffi::sqlite3_result_error(ctx.ctx, msg.as_ptr(), -1); - return; - } - let comp = Regex::new(raw.unwrap().as_ref()); - if comp.is_err() { - let msg = CString::new(format!("{}", comp.unwrap_err())).unwrap(); - ffi::sqlite3_result_error(ctx.ctx, msg.as_ptr(), -1); - return; - } - Some(comp.unwrap()) + None => { + let s = try!(ctx.get::(0)); + let r = try!(Regex::new(&s).map_err(|e| SqliteError { + code: ffi::SQLITE_ERROR, + message: format!("Invalid regular expression: {}", e), + })); + Some(r) }, Some(_) => None, }; - { + let is_match = { let re = saved_re.unwrap_or_else(|| new_re.as_ref().unwrap()); - let text = ctx.get::(1); - if text.is_ok() { - let text = text.unwrap(); - unsafe { - re.is_match(text.as_ref()).set_result(ctx.ctx); - } - } else { - let msg = CString::new(format!("{}", text.unwrap_err())).unwrap(); - unsafe { - ffi::sqlite3_result_error(ctx.ctx, msg.as_ptr(), -1); - } - return; - } - } + let text = try!(ctx.get::(1)); + re.is_match(&text) + }; if let Some(re) = new_re { ctx.set_aux(0, re); } + + Ok(is_match) } #[test] From 3baf7b10f81befb6d338e9867dfa29150aa83e0d Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 15:35:59 -0500 Subject: [PATCH 36/53] Add unit test demonstrating a closure-based UDF. --- src/functions.rs | 73 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index 0552dc2..2809f47 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -313,7 +313,7 @@ impl InnerSqliteConnection { if let Ok(cstr) = str_to_cstring(&e.message) { ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1); } - }, + } } } } @@ -343,6 +343,7 @@ impl InnerSqliteConnection { mod test { extern crate regex; + use std::collections::HashMap; use libc::c_double; use self::regex::Regex; @@ -365,19 +366,24 @@ mod test { assert_eq!(3f64, result.unwrap()); } - fn regexp(ctx: &Context) -> SqliteResult { + // This implementation of a regexp scalar function uses SQLite's auxilliary data + // (https://www.sqlite.org/c3ref/get_auxdata.html) to avoid recompiling the regular + // expression multiple times within one query. + fn regexp_with_auxilliary(ctx: &Context) -> SqliteResult { assert!(ctx.len() == 2, "called with unexpected number of arguments"); let saved_re: Option<&Regex> = unsafe { ctx.get_aux(0) }; let new_re = match saved_re { None => { let s = try!(ctx.get::(0)); - let r = try!(Regex::new(&s).map_err(|e| SqliteError { - code: ffi::SQLITE_ERROR, - message: format!("Invalid regular expression: {}", e), + let r = try!(Regex::new(&s).map_err(|e| { + SqliteError { + code: ffi::SQLITE_ERROR, + message: format!("Invalid regular expression: {}", e), + } })); Some(r) - }, + } Some(_) => None, }; @@ -397,7 +403,7 @@ mod test { #[test] #[cfg_attr(rustfmt, rustfmt_skip)] - fn test_function_regexp() { + fn test_function_regexp_with_auxilliary() { let db = SqliteConnection::open_in_memory().unwrap(); db.execute_batch("BEGIN; CREATE TABLE foo (x string); @@ -405,7 +411,58 @@ mod test { INSERT INTO foo VALUES ('lXsi'); INSERT INTO foo VALUES ('lisX'); END;").unwrap(); - db.create_scalar_function("regexp", 2, true, regexp).unwrap(); + db.create_scalar_function("regexp", 2, true, regexp_with_auxilliary).unwrap(); + + let result = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", + &[], + |r| r.get::(0)); + + assert_eq!(true, result.unwrap()); + + let result = 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 = SqliteConnection::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(); + + // This implementation of a regexp scalar function uses a captured HashMap + // to keep cached regular expressions around (even across multiple queries) + // until the function is removed. + let mut cached_regexes = HashMap::new(); + db.create_scalar_function("regexp", 2, true, move |ctx| { + assert!(ctx.len() == 2, "called with unexpected number of arguments"); + + let regex_s = try!(ctx.get::(0)); + let entry = cached_regexes.entry(regex_s.clone()); + let regex = { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + match entry { + Occupied(occ) => occ.into_mut(), + Vacant(vac) => { + let r = try!(Regex::new(®ex_s).map_err(|e| SqliteError { + code: ffi::SQLITE_ERROR, + message: format!("Invalid regular expression: {}", e), + })); + vac.insert(r) + } + } + }; + + let text = try!(ctx.get::(1)); + Ok(regex.is_match(&text)) + }).unwrap(); let result = db.query_row("SELECT regexp('l.s[aeiouy]', 'lisa')", &[], From ecef092303c872eb0a5dcff426b1f4ecdbd74b4e Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 15:47:52 -0500 Subject: [PATCH 37/53] Add `remove_function` to clear a user-defined function. --- src/functions.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/functions.rs b/src/functions.rs index 2809f47..9fc2bc2 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -1,6 +1,7 @@ //! Create or redefine SQL functions use std::ffi::CStr; use std::mem; +use std::ptr; use std::slice; use std::str; use libc::{c_int, c_double, c_char, c_void}; @@ -281,6 +282,10 @@ impl SqliteConnection { { self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) } + + pub fn remove_function(&self, fn_name: &str, n_arg: c_int) -> SqliteResult<()> { + self.db.borrow_mut().remove_function(fn_name, n_arg) + } } impl InnerSqliteConnection { @@ -337,6 +342,22 @@ impl InnerSqliteConnection { }; self.decode_result(r) } + + fn remove_function(&mut self, fn_name: &str, n_arg: c_int) -> SqliteResult<()> { + 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) + }; + self.decode_result(r) + } } #[cfg(test)] @@ -366,6 +387,18 @@ mod test { assert_eq!(3f64, result.unwrap()); } + #[test] + fn test_remove_function() { + let db = SqliteConnection::open_in_memory().unwrap(); + db.create_scalar_function("half", 1, true, half).unwrap(); + let result = db.query_row("SELECT half(6)", &[], |r| r.get::(0)); + assert_eq!(3f64, result.unwrap()); + + db.remove_function("half", 1).unwrap(); + let result = db.query_row("SELECT half(6)", &[], |r| r.get::(0)); + assert!(result.is_err()); + } + // This implementation of a regexp scalar function uses SQLite's auxilliary data // (https://www.sqlite.org/c3ref/get_auxdata.html) to avoid recompiling the regular // expression multiple times within one query. From 3bcde498bda089020f70ff3bc0d71e879b3c7caf Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 16:27:39 -0500 Subject: [PATCH 38/53] Expand comments. --- src/functions.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) diff --git a/src/functions.rs b/src/functions.rs index 9fc2bc2..6826cf6 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -1,4 +1,55 @@ -//! Create or redefine SQL functions +//! Create or redefine SQL functions. +//! +//! # Example +//! +//! Adding a `regexp` function to a connection in which compiled regular expressions +//! are cached in a `HashMap`. For an alternative implementation that uses SQLite's +//! [Function Auxilliary Data](https://www.sqlite.org/c3ref/get_auxdata.html) interface +//! to avoid recompiling regular expressions, see the unit tests for this module. +//! +//! ```rust +//! extern crate libsqlite3_sys; +//! extern crate rusqlite; +//! extern crate regex; +//! +//! use rusqlite::{SqliteConnection, SqliteError, SqliteResult}; +//! use std::collections::HashMap; +//! use regex::Regex; +//! +//! fn add_regexp_function(db: &SqliteConnection) -> SqliteResult<()> { +//! let mut cached_regexes = HashMap::new(); +//! db.create_scalar_function("regexp", 2, true, move |ctx| { +//! let regex_s = try!(ctx.get::(0)); +//! let entry = cached_regexes.entry(regex_s.clone()); +//! let regex = { +//! use std::collections::hash_map::Entry::{Occupied, Vacant}; +//! match entry { +//! Occupied(occ) => occ.into_mut(), +//! Vacant(vac) => { +//! let r = try!(Regex::new(®ex_s).map_err(|e| SqliteError { +//! code: libsqlite3_sys::SQLITE_ERROR, +//! message: format!("Invalid regular expression: {}", e), +//! })); +//! vac.insert(r) +//! } +//! } +//! }; +//! +//! let text = try!(ctx.get::(1)); +//! Ok(regex.is_match(&text)) +//! }) +//! } +//! +//! fn main() { +//! let db = SqliteConnection::open_in_memory().unwrap(); +//! add_regexp_function(&db).unwrap(); +//! +//! let is_match = db.query_row("SELECT regexp('[aeiou]*', 'aaaaeeeiii')", &[], +//! |row| row.get::(0)).unwrap(); +//! +//! assert!(is_match); +//! } +//! ``` use std::ffi::CStr; use std::mem; use std::ptr; @@ -118,7 +169,7 @@ pub trait FromValue: Sized { /// FromValue types can implement this method and use sqlite3_value_type to check that /// the type reported by SQLite matches a type suitable for Self. This method is used - /// by `???` to confirm that the parameter contains a valid type before + /// by `Context::get` to confirm that the parameter contains a valid type before /// attempting to retrieve the value. unsafe fn parameter_has_valid_sqlite_type(_: *mut sqlite3_value) -> bool { true @@ -226,16 +277,25 @@ unsafe extern "C" fn free_boxed_value(p: *mut c_void) { let _: Box = Box::from_raw(mem::transmute(p)); } +/// Context is a wrapper for the SQLite function evaluation context. pub struct Context<'a> { ctx: *mut sqlite3_context, args: &'a [*mut sqlite3_value], } impl<'a> Context<'a> { + /// Returns the number of arguments to the function. pub fn len(&self) -> usize { self.args.len() } + /// Returns the `idx`th argument as a `T`. + /// + /// # Failure + /// + /// Will panic if `idx` is greater than or equal to `self.len()`. + /// + /// Will return Err if the underlying SQLite type cannot be converted to a `T`. pub fn get(&self, idx: usize) -> SqliteResult { let arg = self.args[idx]; unsafe { @@ -250,6 +310,9 @@ impl<'a> Context<'a> { } } + /// Sets the auxilliary data associated with a particular parameter. See + /// https://www.sqlite.org/c3ref/get_auxdata.html for a discussion of + /// this feature, or the unit tests of this module for an example. pub fn set_aux(&self, arg: c_int, value: T) { let boxed = Box::into_raw(Box::new(value)); unsafe { @@ -260,6 +323,14 @@ impl<'a> Context<'a> { }; } + /// Gets the auxilliary data that was associated with a given parameter + /// via `set_aux`. Returns `None` if no data has been associated. + /// + /// # Unsafety + /// + /// This function is unsafe as there is no guarantee that the type `T` + /// requested matches the type `T` that was provided to `set_aux`. The + /// types must be identical. pub unsafe fn get_aux(&self, arg: c_int) -> Option<&T> { let p = ffi::sqlite3_get_auxdata(self.ctx, arg) as *mut T; if p.is_null() { @@ -271,6 +342,36 @@ impl<'a> Context<'a> { } impl SqliteConnection { + /// Attach a user-defined scalar function to this database connection. + /// + /// `fn_name` is the name the function will be accessible from SQL. + /// `n_arg` is the number of arguments to the function. Use `-1` for a variable + /// number. If the function always returns the same value given the same + /// input, `deterministic` should be `true`. + /// + /// The function will remain available until the connection is closed or + /// until it is explicitly removed via `remove_function`. + /// + /// # Example + /// + /// ```rust + /// # use rusqlite::{SqliteConnection, SqliteResult}; + /// # type c_double = f64; + /// fn scalar_function_example(db: SqliteConnection) -> SqliteResult<()> { + /// try!(db.create_scalar_function("halve", 1, true, |ctx| { + /// let value = try!(ctx.get::(0)); + /// Ok(value / 2f64) + /// })); + /// + /// let six_halved = try!(db.query_row("SELECT halve(6)", &[], |r| r.get::(0))); + /// assert_eq!(six_halved, 3f64); + /// Ok(()) + /// } + /// ``` + /// + /// # Failure + /// + /// Will return Err if the function could not be attached to the connection. pub fn create_scalar_function(&self, fn_name: &str, n_arg: c_int, @@ -283,6 +384,14 @@ impl SqliteConnection { self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) } + /// Removes a user-defined function from this database connection. + /// + /// `fn_name` and `n_arg` should match the name and number of arguments + /// given to `create_scalar_function`. + /// + /// # Failure + /// + /// Will return Err if the function could not be removed. pub fn remove_function(&self, fn_name: &str, n_arg: c_int) -> SqliteResult<()> { self.db.borrow_mut().remove_function(fn_name, n_arg) } From caf1e95e314ad2602279661d3bf826eda5810a96 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 16:28:46 -0500 Subject: [PATCH 39/53] Add functions feature to travis and Changelog. --- .travis.yml | 5 +++-- Changelog.md | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a0743ea..2bd2336 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,9 @@ script: - cargo test --features backup - cargo test --features load_extension - cargo test --features trace - - cargo test --features "backup load_extension trace" - - cargo doc --no-deps --features "backup load_extension trace" + - cargo test --features functions + - cargo test --features "backup functions load_extension trace" + - cargo doc --no-deps --features "backup functions load_extension trace" after_success: | [ $TRAVIS_BRANCH = master ] && diff --git a/Changelog.md b/Changelog.md index 822668b..77315c2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,8 @@ # Version UPCOMING (TBD) * Adds `backup` feature that exposes SQLite's online backup API. +* Adds `functions` feature that allows user-defined scalar functions to be added to + open `SqliteConnection`s. # Version 0.5.0 (2015-12-08) From 5fdb2e1fdaea36ab8fa20e0116d41b79d691dd7b Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Fri, 11 Dec 2015 16:34:58 -0500 Subject: [PATCH 40/53] Allow named parameters to be omitted. If the parameters have never been bound, they default to `NULL`. If they have previously been bound, they keep the existing value. --- src/named_params.rs | 56 +++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/named_params.rs b/src/named_params.rs index f46b77c..49305e7 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -68,7 +68,10 @@ impl<'conn> SqliteStatement<'conn> { }) } - /// Execute the prepared statement with named parameter(s). + /// Execute the prepared statement with named parameter(s). If any parameters + /// that were in the prepared statement are not included in `params`, they + /// will continue to use the most-recently bound value from a previous call + /// to `execute_named`, or `NULL` if they have never been bound. /// /// On success, returns the number of rows that were changed or inserted or deleted (via /// `sqlite3_changes`). @@ -95,7 +98,9 @@ impl<'conn> SqliteStatement<'conn> { } /// Execute the prepared statement with named parameter(s), returning an iterator over the - /// resulting rows. + /// resulting rows. If any parameters that were in the prepared statement are not included in + /// `params`, they will continue to use the most-recently bound value from a previous call to + /// `query_named`, or `NULL` if they have never been bound. /// /// ## Example /// @@ -125,25 +130,6 @@ impl<'conn> SqliteStatement<'conn> { } fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> { - // Always check that the number of parameters is correct. - assert!(params.len() as c_int == unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) }, - "incorrect number of parameters to query(): expected {}, got {}", - unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) }, - params.len()); - - // In debug, also sanity check that we got distinct parameter names. - debug_assert!({ - use std::collections::HashSet; - - let mut s = HashSet::with_capacity(params.len()); - for &(name, _) in params { - s.insert(name); - } - - s.len() == params.len() - }, - "named parameters must be unique"); - for &(name, value) in params { if let Some(i) = try!(self.parameter_index(name)) { try!(self.conn.decode_result(unsafe { value.bind_parameter(self.stmt, i) })); @@ -208,21 +194,31 @@ mod test { } #[test] - #[should_panic] - fn test_panic_on_incorrect_number_of_parameters() { + fn test_unbound_parameters_are_null() { let db = SqliteConnection::open_in_memory().unwrap(); + let sql = "CREATE TABLE test (x TEXT, y TEXT)"; + db.execute_batch(sql).unwrap(); - let mut stmt = db.prepare("SELECT 1 WHERE 1 = :one AND 2 = :two").unwrap(); - let _ = stmt.query_named(&[(":one", &1i32)]); + let mut stmt = db.prepare("INSERT INTO test (x, y) VALUES (:x, :y)").unwrap(); + stmt.execute_named(&[(":x", &"one")]).unwrap(); + + let result = db.query_row("SELECT y FROM test WHERE x = 'one'", &[], + |row| row.get::>(0)).unwrap(); + assert!(result.is_none()); } #[test] - #[cfg(debug_assertions)] - #[should_panic] - fn test_debug_panic_on_incorrect_parameter_names() { + fn test_unbound_parameters_are_reused() { let db = SqliteConnection::open_in_memory().unwrap(); + let sql = "CREATE TABLE test (x TEXT, y TEXT)"; + db.execute_batch(sql).unwrap(); - let mut stmt = db.prepare("SELECT 1 WHERE 1 = :one AND 2 = :two").unwrap(); - let _ = stmt.query_named(&[(":one", &1i32), (":one", &2i32)]); + 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 = db.query_row("SELECT x FROM test WHERE y = 'two'", &[], + |row| row.get::(0)).unwrap(); + assert_eq!(result, "one"); } } From 4830b0a64815d5c20eb55f225ef824db98679477 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 10:44:08 -0500 Subject: [PATCH 41/53] Add unit test for function with variable number of arguments --- src/functions.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/functions.rs b/src/functions.rs index 6826cf6..d8df619 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -618,4 +618,27 @@ mod test { assert_eq!(2, result.unwrap()); } + + #[test] + fn test_varargs_function() { + let db = SqliteConnection::open_in_memory().unwrap(); + db.create_scalar_function("my_concat", -1, true, |ctx| { + let mut ret = String::new(); + + for idx in 0..ctx.len() { + let s = try!(ctx.get::(idx)); + ret.push_str(&s); + } + + Ok(ret) + }) + .unwrap(); + + 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); + } + } } From 4327a84edb2773ef740891efad8ec11e38d3b774 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 13:50:12 -0500 Subject: [PATCH 42/53] Rename SqliteConnection -> Connection. Leave old name in as a (deprecated) typealias. --- Changelog.md | 1 + README.md | 6 +- src/backup.rs | 38 +++++------ src/functions.rs | 28 ++++---- src/lib.rs | 129 ++++++++++++++++++------------------ src/load_extension_guard.rs | 10 +-- src/named_params.rs | 28 ++++---- src/trace.rs | 10 +-- src/transaction.rs | 26 ++++---- src/types.rs | 10 +-- 10 files changed, 145 insertions(+), 141 deletions(-) diff --git a/Changelog.md b/Changelog.md index d3c78b4..e1d2597 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # Version UPCOMING (TBD) +* Renamed `SqliteConnection` to `Connection`. The old name remains as a typealias. * Adds a variety of `..._named` methods for executing queries using named placeholder parameters. * Adds `backup` feature that exposes SQLite's online backup API. * Adds `functions` feature that allows user-defined scalar functions to be added to diff --git a/README.md b/README.md index 1bc6742..96f0fd3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ extern crate rusqlite; extern crate time; use time::Timespec; -use rusqlite::SqliteConnection; +use rusqlite::Connection; #[derive(Debug)] struct Person { @@ -22,7 +22,7 @@ struct Person { } fn main() { - let conn = SqliteConnection::open_in_memory().unwrap(); + let conn = Connection::open_in_memory().unwrap(); conn.execute("CREATE TABLE person ( id INTEGER PRIMARY KEY, @@ -72,7 +72,7 @@ performing checks at runtime to ensure you do not try to retrieve the values of will panic if you do so. A specific example that will panic: ```rust -fn bad_function_will_panic(conn: &SqliteConnection) -> SqliteResult { +fn bad_function_will_panic(conn: &Connection) -> SqliteResult { let mut stmt = try!(conn.prepare("SELECT id FROM my_table")); let mut rows = try!(stmt.query(&[])); diff --git a/src/backup.rs b/src/backup.rs index e50ad20..5d9c2c2 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -1,6 +1,6 @@ //! Online SQLite backup API. //! -//! To create a `Backup`, you must have two distinct `SqliteConnection`s - one +//! To create a `Backup`, you must have two distinct `Connection`s - one //! for the source (which can be used while the backup is running) and one for //! the destination (which cannot). A `Backup` handle exposes three methods: //! `step` will attempt to back up a specified number of pages, `progress` gets @@ -14,13 +14,13 @@ //! documentation](https://www.sqlite.org/backup.html). //! //! ```rust,no_run -//! # use rusqlite::{backup, SqliteConnection, SqliteResult}; +//! # use rusqlite::{backup, Connection, SqliteResult}; //! # use std::path::Path; //! # use std::time; //! -//! fn backupDb>(src: &SqliteConnection, dst: P, progress: fn(backup::Progress)) +//! fn backupDb>(src: &Connection, dst: P, progress: fn(backup::Progress)) //! -> SqliteResult<()> { -//! let mut dst = try!(SqliteConnection::open(dst)); +//! let mut dst = try!(Connection::open(dst)); //! let backup = try!(backup::Backup::new(src, &mut dst)); //! backup.run_to_completion(5, time::Duration::from_millis(250), Some(progress)) //! } @@ -36,9 +36,9 @@ use std::time::Duration; use ffi; -use {DatabaseName, SqliteConnection, SqliteError, SqliteResult}; +use {DatabaseName, Connection, SqliteError, SqliteResult}; -impl SqliteConnection { +impl Connection { /// Back up the `name` database to the given destination path. /// If `progress` is not `None`, it will be called periodically /// until the backup completes. @@ -57,7 +57,7 @@ impl SqliteConnection { progress: Option) -> SqliteResult<()> { use self::StepResult::{More, Done, Busy, Locked}; - let mut dst = try!(SqliteConnection::open(dst_path)); + let mut dst = try!(Connection::open(dst_path)); let backup = try!(Backup::new_with_names(self, name, &mut dst, DatabaseName::Main)); let mut r = More; @@ -94,7 +94,7 @@ impl SqliteConnection { progress: Option) -> SqliteResult<()> { use self::StepResult::{More, Done, Busy, Locked}; - let src = try!(SqliteConnection::open(src_path)); + let src = try!(Connection::open(src_path)); let restore = try!(Backup::new_with_names(&src, DatabaseName::Main, self, name)); let mut r = More; @@ -170,8 +170,8 @@ impl<'a, 'b> Backup<'a, 'b> { /// /// Will return `Err` if the underlying `sqlite3_backup_init` call returns /// `NULL`. - pub fn new(from: &'a SqliteConnection, - to: &'b mut SqliteConnection) + pub fn new(from: &'a Connection, + to: &'b mut Connection) -> SqliteResult> { Backup::new_with_names(from, DatabaseName::Main, to, DatabaseName::Main) } @@ -185,9 +185,9 @@ 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 SqliteConnection, + pub fn new_with_names(from: &'a Connection, from_name: DatabaseName, - to: &'b mut SqliteConnection, + to: &'b mut Connection, to_name: DatabaseName) -> SqliteResult> { let to_name = try!(to_name.to_cstring()); @@ -298,21 +298,21 @@ impl<'a, 'b> Drop for Backup<'a, 'b> { #[cfg(test)] mod test { - use {SqliteConnection, DatabaseName}; + use {Connection, DatabaseName}; use std::time::Duration; use super::Backup; #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn test_backup() { - let src = SqliteConnection::open_in_memory().unwrap(); + let src = Connection::open_in_memory().unwrap(); let sql = "BEGIN; CREATE TABLE foo(x INTEGER); INSERT INTO foo VALUES(42); END;"; src.execute_batch(sql).unwrap(); - let mut dst = SqliteConnection::open_in_memory().unwrap(); + let mut dst = Connection::open_in_memory().unwrap(); { let backup = Backup::new(&src, &mut dst).unwrap(); @@ -336,14 +336,14 @@ mod test { #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn test_backup_temp() { - let src = SqliteConnection::open_in_memory().unwrap(); + let src = Connection::open_in_memory().unwrap(); let sql = "BEGIN; CREATE TEMPORARY TABLE foo(x INTEGER); INSERT INTO foo VALUES(42); END;"; src.execute_batch(sql).unwrap(); - let mut dst = SqliteConnection::open_in_memory().unwrap(); + let mut dst = Connection::open_in_memory().unwrap(); { let backup = Backup::new_with_names(&src, @@ -375,7 +375,7 @@ mod test { #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn test_backup_attached() { - let src = SqliteConnection::open_in_memory().unwrap(); + let src = Connection::open_in_memory().unwrap(); let sql = "ATTACH DATABASE ':memory:' AS my_attached; BEGIN; CREATE TABLE my_attached.foo(x INTEGER); @@ -383,7 +383,7 @@ mod test { END;"; src.execute_batch(sql).unwrap(); - let mut dst = SqliteConnection::open_in_memory().unwrap(); + let mut dst = Connection::open_in_memory().unwrap(); { let backup = Backup::new_with_names(&src, diff --git a/src/functions.rs b/src/functions.rs index d8df619..3ebe3d0 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -12,11 +12,11 @@ //! extern crate rusqlite; //! extern crate regex; //! -//! use rusqlite::{SqliteConnection, SqliteError, SqliteResult}; +//! use rusqlite::{Connection, SqliteError, SqliteResult}; //! use std::collections::HashMap; //! use regex::Regex; //! -//! fn add_regexp_function(db: &SqliteConnection) -> SqliteResult<()> { +//! fn add_regexp_function(db: &Connection) -> SqliteResult<()> { //! let mut cached_regexes = HashMap::new(); //! db.create_scalar_function("regexp", 2, true, move |ctx| { //! let regex_s = try!(ctx.get::(0)); @@ -41,7 +41,7 @@ //! } //! //! fn main() { -//! let db = SqliteConnection::open_in_memory().unwrap(); +//! let db = Connection::open_in_memory().unwrap(); //! add_regexp_function(&db).unwrap(); //! //! let is_match = db.query_row("SELECT regexp('[aeiou]*', 'aaaaeeeiii')", &[], @@ -65,7 +65,7 @@ pub use ffi::sqlite3_value_numeric_type; use types::Null; -use {SqliteResult, SqliteError, SqliteConnection, str_to_cstring, InnerSqliteConnection}; +use {SqliteResult, SqliteError, Connection, str_to_cstring, InnerConnection}; /// A trait for types that can be converted into the result of an SQL function. pub trait ToResult { @@ -341,7 +341,7 @@ impl<'a> Context<'a> { } } -impl SqliteConnection { +impl Connection { /// Attach a user-defined scalar function to this database connection. /// /// `fn_name` is the name the function will be accessible from SQL. @@ -355,9 +355,9 @@ impl SqliteConnection { /// # Example /// /// ```rust - /// # use rusqlite::{SqliteConnection, SqliteResult}; + /// # use rusqlite::{Connection, SqliteResult}; /// # type c_double = f64; - /// fn scalar_function_example(db: SqliteConnection) -> SqliteResult<()> { + /// fn scalar_function_example(db: Connection) -> SqliteResult<()> { /// try!(db.create_scalar_function("halve", 1, true, |ctx| { /// let value = try!(ctx.get::(0)); /// Ok(value / 2f64) @@ -397,7 +397,7 @@ impl SqliteConnection { } } -impl InnerSqliteConnection { +impl InnerConnection { fn create_scalar_function(&mut self, fn_name: &str, n_arg: c_int, @@ -477,7 +477,7 @@ mod test { use libc::c_double; use self::regex::Regex; - use {SqliteConnection, SqliteError, SqliteResult}; + use {Connection, SqliteError, SqliteResult}; use ffi; use functions::Context; @@ -489,7 +489,7 @@ mod test { #[test] fn test_function_half() { - let db = SqliteConnection::open_in_memory().unwrap(); + let db = Connection::open_in_memory().unwrap(); db.create_scalar_function("half", 1, true, half).unwrap(); let result = db.query_row("SELECT half(6)", &[], |r| r.get::(0)); @@ -498,7 +498,7 @@ mod test { #[test] fn test_remove_function() { - let db = SqliteConnection::open_in_memory().unwrap(); + let db = Connection::open_in_memory().unwrap(); db.create_scalar_function("half", 1, true, half).unwrap(); let result = db.query_row("SELECT half(6)", &[], |r| r.get::(0)); assert_eq!(3f64, result.unwrap()); @@ -546,7 +546,7 @@ mod test { #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn test_function_regexp_with_auxilliary() { - let db = SqliteConnection::open_in_memory().unwrap(); + let db = Connection::open_in_memory().unwrap(); db.execute_batch("BEGIN; CREATE TABLE foo (x string); INSERT INTO foo VALUES ('lisa'); @@ -571,7 +571,7 @@ mod test { #[test] #[cfg_attr(rustfmt, rustfmt_skip)] fn test_function_regexp_with_hashmap_cache() { - let db = SqliteConnection::open_in_memory().unwrap(); + let db = Connection::open_in_memory().unwrap(); db.execute_batch("BEGIN; CREATE TABLE foo (x string); INSERT INTO foo VALUES ('lisa'); @@ -621,7 +621,7 @@ mod test { #[test] fn test_varargs_function() { - let db = SqliteConnection::open_in_memory().unwrap(); + let db = Connection::open_in_memory().unwrap(); db.create_scalar_function("my_concat", -1, true, |ctx| { let mut ret = String::new(); diff --git a/src/lib.rs b/src/lib.rs index 0af8ff5..773a333 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ //! extern crate time; //! //! use time::Timespec; -//! use rusqlite::SqliteConnection; +//! use rusqlite::Connection; //! //! #[derive(Debug)] //! struct Person { @@ -17,7 +17,7 @@ //! } //! //! fn main() { -//! let conn = SqliteConnection::open_in_memory().unwrap(); +//! let conn = Connection::open_in_memory().unwrap(); //! //! conn.execute("CREATE TABLE person ( //! id INTEGER PRIMARY KEY, @@ -179,27 +179,30 @@ impl<'a> DatabaseName<'a> { } } +/// Old name for `Connection`. `SqliteConnection` is deprecated. +pub type SqliteConnection = Connection; + /// A connection to a SQLite database. -pub struct SqliteConnection { - db: RefCell, +pub struct Connection { + db: RefCell, path: Option, } -unsafe impl Send for SqliteConnection {} +unsafe impl Send for Connection {} -impl SqliteConnection { +impl Connection { /// Open a new connection to a SQLite database. /// - /// `SqliteConnection::open(path)` is equivalent to `SqliteConnection::open_with_flags(path, + /// `Connection::open(path)` is equivalent to `Connection::open_with_flags(path, /// SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE)`. /// /// # Failure /// /// Will return `Err` if `path` cannot be converted to a C-compatible string or if the /// underlying SQLite open call fails. - pub fn open>(path: P) -> SqliteResult { + pub fn open>(path: P) -> SqliteResult { let flags = Default::default(); - SqliteConnection::open_with_flags(path, flags) + Connection::open_with_flags(path, flags) } /// Open a new connection to an in-memory SQLite database. @@ -207,9 +210,9 @@ impl SqliteConnection { /// # Failure /// /// Will return `Err` if the underlying SQLite open call fails. - pub fn open_in_memory() -> SqliteResult { + pub fn open_in_memory() -> SqliteResult { let flags = Default::default(); - SqliteConnection::open_in_memory_with_flags(flags) + Connection::open_in_memory_with_flags(flags) } /// Open a new connection to a SQLite database. @@ -223,10 +226,10 @@ impl SqliteConnection { /// underlying SQLite open call fails. pub fn open_with_flags>(path: P, flags: SqliteOpenFlags) - -> SqliteResult { + -> SqliteResult { let c_path = try!(path_to_cstring(path.as_ref())); - InnerSqliteConnection::open_with_flags(&c_path, flags).map(|db| { - SqliteConnection { + InnerConnection::open_with_flags(&c_path, flags).map(|db| { + Connection { db: RefCell::new(db), path: Some(path.as_ref().to_path_buf()), } @@ -241,10 +244,10 @@ impl SqliteConnection { /// # Failure /// /// Will return `Err` if the underlying SQLite open call fails. - pub fn open_in_memory_with_flags(flags: SqliteOpenFlags) -> SqliteResult { + pub fn open_in_memory_with_flags(flags: SqliteOpenFlags) -> SqliteResult { let c_memory = try!(str_to_cstring(":memory:")); - InnerSqliteConnection::open_with_flags(&c_memory, flags).map(|db| { - SqliteConnection { + InnerConnection::open_with_flags(&c_memory, flags).map(|db| { + Connection { db: RefCell::new(db), path: None, } @@ -259,10 +262,10 @@ impl SqliteConnection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult}; - /// # fn do_queries_part_1(conn: &SqliteConnection) -> SqliteResult<()> { Ok(()) } - /// # fn do_queries_part_2(conn: &SqliteConnection) -> SqliteResult<()> { Ok(()) } - /// fn perform_queries(conn: &SqliteConnection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, SqliteResult}; + /// # fn do_queries_part_1(conn: &Connection) -> SqliteResult<()> { Ok(()) } + /// # fn do_queries_part_2(conn: &Connection) -> SqliteResult<()> { Ok(()) } + /// fn perform_queries(conn: &Connection) -> SqliteResult<()> { /// let tx = try!(conn.transaction()); /// /// try!(do_queries_part_1(conn)); // tx causes rollback if this fails @@ -299,8 +302,8 @@ impl SqliteConnection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult}; - /// fn create_tables(conn: &SqliteConnection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, SqliteResult}; + /// fn create_tables(conn: &Connection) -> SqliteResult<()> { /// conn.execute_batch("BEGIN; /// CREATE TABLE foo(x INTEGER); /// CREATE TABLE bar(y TEXT); @@ -324,8 +327,8 @@ impl SqliteConnection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection}; - /// fn update_rows(conn: &SqliteConnection) { + /// # use rusqlite::{Connection}; + /// fn update_rows(conn: &Connection) { /// match conn.execute("UPDATE foo SET bar = 'baz' WHERE qux = ?", &[&1i32]) { /// Ok(updated) => println!("{} rows were updated", updated), /// Err(err) => println!("update failed: {}", err), @@ -354,8 +357,8 @@ impl SqliteConnection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteResult,SqliteConnection}; - /// fn preferred_locale(conn: &SqliteConnection) -> SqliteResult { + /// # use rusqlite::{SqliteResult,Connection}; + /// fn preferred_locale(conn: &Connection) -> SqliteResult { /// conn.query_row("SELECT value FROM preferences WHERE name='locale'", &[], |row| { /// row.get(0) /// }) @@ -384,8 +387,8 @@ impl SqliteConnection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteResult,SqliteConnection}; - /// fn preferred_locale(conn: &SqliteConnection) -> SqliteResult { + /// # use rusqlite::{SqliteResult,Connection}; + /// fn preferred_locale(conn: &Connection) -> SqliteResult { /// conn.query_row_and_then("SELECT value FROM preferences WHERE name='locale'", &[], |row| { /// row.get_checked(0) /// }) @@ -413,8 +416,8 @@ impl SqliteConnection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteResult,SqliteConnection}; - /// fn preferred_locale(conn: &SqliteConnection) -> SqliteResult { + /// # use rusqlite::{SqliteResult,Connection}; + /// fn preferred_locale(conn: &Connection) -> SqliteResult { /// conn.query_row_safe("SELECT value FROM preferences WHERE name='locale'", &[], |row| { /// row.get(0) /// }) @@ -438,8 +441,8 @@ impl SqliteConnection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult}; - /// fn insert_new_people(conn: &SqliteConnection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, SqliteResult}; + /// fn insert_new_people(conn: &Connection) -> SqliteResult<()> { /// let mut stmt = try!(conn.prepare("INSERT INTO People (name) VALUES (?)")); /// try!(stmt.execute(&[&"Joe Smith"])); /// try!(stmt.execute(&[&"Bob Jones"])); @@ -457,7 +460,7 @@ impl SqliteConnection { /// Close the SQLite connection. /// - /// This is functionally equivalent to the `Drop` implementation for `SqliteConnection` except + /// This is functionally equivalent to the `Drop` implementation for `Connection` except /// that it returns any error encountered to the caller. /// /// # Failure @@ -474,9 +477,9 @@ impl SqliteConnection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult}; + /// # use rusqlite::{Connection, SqliteResult}; /// # use std::path::{Path}; - /// fn load_my_extension(conn: &SqliteConnection) -> SqliteResult<()> { + /// fn load_my_extension(conn: &Connection) -> SqliteResult<()> { /// try!(conn.load_extension_enable()); /// try!(conn.load_extension(Path::new("my_sqlite_extension"), None)); /// conn.load_extension_disable() @@ -513,9 +516,9 @@ impl SqliteConnection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult, SqliteLoadExtensionGuard}; + /// # use rusqlite::{Connection, SqliteResult, SqliteLoadExtensionGuard}; /// # use std::path::{Path}; - /// fn load_my_extension(conn: &SqliteConnection) -> SqliteResult<()> { + /// fn load_my_extension(conn: &Connection) -> SqliteResult<()> { /// let _guard = try!(SqliteLoadExtensionGuard::new(conn)); /// /// conn.load_extension("my_sqlite_extension", None) @@ -542,15 +545,15 @@ impl SqliteConnection { } } -impl fmt::Debug for SqliteConnection { +impl fmt::Debug for Connection { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("SqliteConnection") + f.debug_struct("Connection") .field("path", &self.path) .finish() } } -struct InnerSqliteConnection { +struct InnerConnection { db: *mut ffi::Struct_sqlite3, } @@ -577,10 +580,10 @@ impl Default for SqliteOpenFlags { } } -impl InnerSqliteConnection { +impl InnerConnection { fn open_with_flags(c_path: &CString, flags: SqliteOpenFlags) - -> SqliteResult { + -> SqliteResult { unsafe { let mut db: *mut ffi::sqlite3 = mem::uninitialized(); let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null()); @@ -604,7 +607,7 @@ impl InnerSqliteConnection { ffi::sqlite3_close(db); return Err(e); } - Ok(InnerSqliteConnection { db: db }) + Ok(InnerConnection { db: db }) } } @@ -686,7 +689,7 @@ impl InnerSqliteConnection { } fn prepare<'a>(&mut self, - conn: &'a SqliteConnection, + conn: &'a Connection, sql: &str) -> SqliteResult> { if sql.len() >= ::std::i32::MAX as usize { @@ -713,7 +716,7 @@ impl InnerSqliteConnection { } } -impl Drop for InnerSqliteConnection { +impl Drop for InnerConnection { #[allow(unused_must_use)] fn drop(&mut self) { self.close(); @@ -722,14 +725,14 @@ impl Drop for InnerSqliteConnection { /// A prepared statement. pub struct SqliteStatement<'conn> { - conn: &'conn SqliteConnection, + conn: &'conn Connection, stmt: *mut ffi::sqlite3_stmt, needs_reset: bool, column_count: c_int, } impl<'conn> SqliteStatement<'conn> { - fn new(conn: &SqliteConnection, stmt: *mut ffi::sqlite3_stmt) -> SqliteStatement { + fn new(conn: &Connection, stmt: *mut ffi::sqlite3_stmt) -> SqliteStatement { SqliteStatement { conn: conn, stmt: stmt, @@ -758,8 +761,8 @@ impl<'conn> SqliteStatement<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult}; - /// fn update_rows(conn: &SqliteConnection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, SqliteResult}; + /// fn update_rows(conn: &Connection) -> SqliteResult<()> { /// let mut stmt = try!(conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")); /// /// try!(stmt.execute(&[&1i32])); @@ -810,8 +813,8 @@ impl<'conn> SqliteStatement<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult}; - /// fn get_names(conn: &SqliteConnection) -> SqliteResult> { + /// # use rusqlite::{Connection, SqliteResult}; + /// fn get_names(conn: &Connection) -> SqliteResult> { /// let mut stmt = try!(conn.prepare("SELECT name FROM people")); /// let mut rows = try!(stmt.query(&[])); /// @@ -994,8 +997,8 @@ where E: convert::From, /// iterator). For example: /// /// ```rust,no_run -/// # use rusqlite::{SqliteConnection, SqliteResult}; -/// fn bad_function_will_panic(conn: &SqliteConnection) -> SqliteResult { +/// # use rusqlite::{Connection, SqliteResult}; +/// fn bad_function_will_panic(conn: &Connection) -> SqliteResult { /// let mut stmt = try!(conn.prepare("SELECT id FROM my_table")); /// let mut rows = try!(stmt.query(&[])); /// @@ -1090,8 +1093,8 @@ impl<'stmt> SqliteRow<'stmt> { /// for example) this isn't an issue, but it means you cannot do something like this: /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult}; - /// fn bad_function_will_panic(conn: &SqliteConnection) -> SqliteResult { + /// # use rusqlite::{Connection, SqliteResult}; + /// fn bad_function_will_panic(conn: &Connection) -> SqliteResult { /// let mut stmt = try!(conn.prepare("SELECT id FROM my_table")); /// let mut rows = try!(stmt.query(&[])); /// @@ -1164,11 +1167,11 @@ mod test { // that those types are `Send`. #[allow(dead_code, unconditional_recursion)] fn ensure_send() { - ensure_send::(); + ensure_send::(); } - pub fn checked_memory_handle() -> SqliteConnection { - SqliteConnection::open_in_memory().unwrap() + pub fn checked_memory_handle() -> Connection { + Connection::open_in_memory().unwrap() } #[test] @@ -1178,7 +1181,7 @@ mod test { let path = temp_dir.path().join("test.db3"); { - let db = SqliteConnection::open(&path).unwrap(); + let db = Connection::open(&path).unwrap(); let sql = "BEGIN; CREATE TABLE foo(x INTEGER); INSERT INTO foo VALUES(42); @@ -1187,7 +1190,7 @@ mod test { } let path_string = path.to_str().unwrap(); - let db = SqliteConnection::open(&path_string).unwrap(); + let db = Connection::open(&path_string).unwrap(); let the_answer = db.query_row("SELECT x FROM foo", &[], |r| r.get::(0)); assert_eq!(42i64, the_answer.unwrap()); @@ -1195,7 +1198,7 @@ mod test { #[test] fn test_open() { - assert!(SqliteConnection::open_in_memory().is_ok()); + assert!(Connection::open_in_memory().is_ok()); let db = checked_memory_handle(); assert!(db.close().is_ok()); @@ -1207,7 +1210,7 @@ mod test { SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_READ_WRITE, SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_CREATE] .iter() { - assert!(SqliteConnection::open_in_memory_with_flags(*bad_flags).is_err()); + assert!(Connection::open_in_memory_with_flags(*bad_flags).is_err()); } } diff --git a/src/load_extension_guard.rs b/src/load_extension_guard.rs index ecc93e5..2c02798 100644 --- a/src/load_extension_guard.rs +++ b/src/load_extension_guard.rs @@ -1,26 +1,26 @@ -use {SqliteResult, SqliteConnection}; +use {SqliteResult, Connection}; /// RAII guard temporarily enabling SQLite extensions to be loaded. /// /// ## Example /// /// ```rust,no_run -/// # use rusqlite::{SqliteConnection, SqliteResult, SqliteLoadExtensionGuard}; +/// # use rusqlite::{Connection, SqliteResult, SqliteLoadExtensionGuard}; /// # use std::path::{Path}; -/// fn load_my_extension(conn: &SqliteConnection) -> SqliteResult<()> { +/// fn load_my_extension(conn: &Connection) -> SqliteResult<()> { /// let _guard = try!(SqliteLoadExtensionGuard::new(conn)); /// /// conn.load_extension(Path::new("my_sqlite_extension"), None) /// } /// ``` pub struct SqliteLoadExtensionGuard<'conn> { - conn: &'conn SqliteConnection, + conn: &'conn Connection, } impl<'conn> SqliteLoadExtensionGuard<'conn> { /// Attempt to enable loading extensions. Loading extensions will be disabled when this /// guard goes out of scope. Cannot be meaningfully nested. - pub fn new(conn: &SqliteConnection) -> SqliteResult { + pub fn new(conn: &Connection) -> SqliteResult { conn.load_extension_enable().map(|_| SqliteLoadExtensionGuard { conn: conn }) } } diff --git a/src/named_params.rs b/src/named_params.rs index 49305e7..60e99d8 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -2,11 +2,11 @@ use libc::c_int; use super::ffi; -use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows, SqliteRow, +use {SqliteResult, SqliteError, Connection, SqliteStatement, SqliteRows, SqliteRow, str_to_cstring}; use types::ToSql; -impl SqliteConnection { +impl Connection { /// Convenience method to prepare and execute a single SQL statement with named parameter(s). /// /// On success, returns the number of rows that were changed or inserted or deleted (via @@ -15,8 +15,8 @@ impl SqliteConnection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult}; - /// fn insert(conn: &SqliteConnection) -> SqliteResult { + /// # use rusqlite::{Connection, SqliteResult}; + /// fn insert(conn: &Connection) -> SqliteResult { /// conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")]) /// } /// ``` @@ -79,8 +79,8 @@ impl<'conn> SqliteStatement<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult}; - /// fn insert(conn: &SqliteConnection) -> SqliteResult { + /// # use rusqlite::{Connection, SqliteResult}; + /// fn insert(conn: &Connection) -> SqliteResult { /// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)")); /// stmt.execute_named(&[(":name", &"one")]) /// } @@ -105,8 +105,8 @@ impl<'conn> SqliteStatement<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult, SqliteRows}; - /// fn query(conn: &SqliteConnection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, SqliteResult, SqliteRows}; + /// fn query(conn: &Connection) -> SqliteResult<()> { /// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name")); /// let mut rows = try!(stmt.query_named(&[(":name", &"one")])); /// for row in rows { @@ -146,11 +146,11 @@ impl<'conn> SqliteStatement<'conn> { #[cfg(test)] mod test { - use SqliteConnection; + use Connection; #[test] fn test_execute_named() { - let db = SqliteConnection::open_in_memory().unwrap(); + 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(), @@ -167,7 +167,7 @@ mod test { #[test] fn test_stmt_execute_named() { - let db = SqliteConnection::open_in_memory().unwrap(); + let db = Connection::open_in_memory().unwrap(); let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \ INTEGER)"; db.execute_batch(sql).unwrap(); @@ -184,7 +184,7 @@ mod test { #[test] fn test_query_named() { - let db = SqliteConnection::open_in_memory().unwrap(); + let db = Connection::open_in_memory().unwrap(); let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \ INTEGER)"; db.execute_batch(sql).unwrap(); @@ -195,7 +195,7 @@ mod test { #[test] fn test_unbound_parameters_are_null() { - let db = SqliteConnection::open_in_memory().unwrap(); + let db = Connection::open_in_memory().unwrap(); let sql = "CREATE TABLE test (x TEXT, y TEXT)"; db.execute_batch(sql).unwrap(); @@ -209,7 +209,7 @@ mod test { #[test] fn test_unbound_parameters_are_reused() { - let db = SqliteConnection::open_in_memory().unwrap(); + let db = Connection::open_in_memory().unwrap(); let sql = "CREATE TABLE test (x TEXT, y TEXT)"; db.execute_batch(sql).unwrap(); diff --git a/src/trace.rs b/src/trace.rs index 682662c..f79f619 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -8,7 +8,7 @@ use std::str; use std::time::Duration; use super::ffi; -use {SqliteError, SqliteResult, SqliteConnection}; +use {SqliteError, SqliteResult, Connection}; /// Set up the process-wide SQLite error logging callback. /// This function is marked unsafe for two reasons: @@ -60,7 +60,7 @@ pub fn log(err_code: c_int, msg: &str) { } } -impl SqliteConnection { +impl Connection { /// Register or clear a callback function that can be used for tracing the execution of SQL statements. /// /// Prepared statement placeholders are replaced/logged with their assigned values. @@ -120,7 +120,7 @@ mod test { use std::sync::Mutex; use std::time::Duration; - use SqliteConnection; + use Connection; #[test] fn test_trace() { @@ -132,7 +132,7 @@ mod test { traced_stmts.push(s.to_owned()); } - let mut db = SqliteConnection::open_in_memory().unwrap(); + let mut db = Connection::open_in_memory().unwrap(); db.trace(Some(tracer)); { let _ = db.query_row("SELECT ?", &[&1i32], |_| {}); @@ -160,7 +160,7 @@ mod test { profiled.push((s.to_owned(), d)); } - let mut db = SqliteConnection::open_in_memory().unwrap(); + let mut db = Connection::open_in_memory().unwrap(); db.profile(Some(profiler)); db.execute_batch("PRAGMA application_id = 1").unwrap(); db.profile(None); diff --git a/src/transaction.rs b/src/transaction.rs index 1981f24..a26cc9d 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,4 +1,4 @@ -use {SqliteResult, SqliteConnection}; +use {SqliteResult, Connection}; pub use SqliteTransactionBehavior::{SqliteTransactionDeferred, SqliteTransactionImmediate, SqliteTransactionExclusive}; @@ -22,10 +22,10 @@ pub enum SqliteTransactionBehavior { /// ## Example /// /// ```rust,no_run -/// # use rusqlite::{SqliteConnection, SqliteResult}; -/// # fn do_queries_part_1(conn: &SqliteConnection) -> SqliteResult<()> { Ok(()) } -/// # fn do_queries_part_2(conn: &SqliteConnection) -> SqliteResult<()> { Ok(()) } -/// fn perform_queries(conn: &SqliteConnection) -> SqliteResult<()> { +/// # use rusqlite::{Connection, SqliteResult}; +/// # fn do_queries_part_1(conn: &Connection) -> SqliteResult<()> { Ok(()) } +/// # fn do_queries_part_2(conn: &Connection) -> SqliteResult<()> { Ok(()) } +/// fn perform_queries(conn: &Connection) -> SqliteResult<()> { /// let tx = try!(conn.transaction()); /// /// try!(do_queries_part_1(conn)); // tx causes rollback if this fails @@ -35,7 +35,7 @@ pub enum SqliteTransactionBehavior { /// } /// ``` pub struct SqliteTransaction<'conn> { - conn: &'conn SqliteConnection, + conn: &'conn Connection, depth: u32, commit: bool, finished: bool, @@ -43,7 +43,7 @@ pub struct SqliteTransaction<'conn> { impl<'conn> SqliteTransaction<'conn> { /// Begin a new transaction. Cannot be nested; see `savepoint` for nested transactions. - pub fn new(conn: &SqliteConnection, + pub fn new(conn: &Connection, behavior: SqliteTransactionBehavior) -> SqliteResult { let query = match behavior { @@ -71,9 +71,9 @@ impl<'conn> SqliteTransaction<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteConnection, SqliteResult}; - /// # fn perform_queries_part_1_succeeds(conn: &SqliteConnection) -> bool { true } - /// fn perform_queries(conn: &SqliteConnection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, SqliteResult}; + /// # fn perform_queries_part_1_succeeds(conn: &Connection) -> bool { true } + /// fn perform_queries(conn: &Connection) -> SqliteResult<()> { /// let tx = try!(conn.transaction()); /// /// { @@ -173,10 +173,10 @@ impl<'conn> Drop for SqliteTransaction<'conn> { #[cfg(test)] mod test { - use SqliteConnection; + use Connection; - fn checked_memory_handle() -> SqliteConnection { - let db = SqliteConnection::open_in_memory().unwrap(); + fn checked_memory_handle() -> Connection { + let db = Connection::open_in_memory().unwrap(); db.execute_batch("CREATE TABLE foo (x INTEGER)").unwrap(); db } diff --git a/src/types.rs b/src/types.rs index ddc0234..bf5f86c 100644 --- a/src/types.rs +++ b/src/types.rs @@ -176,12 +176,12 @@ impl ToSql for Option { /// ```rust,no_run /// # extern crate libc; /// # extern crate rusqlite; -/// # use rusqlite::{SqliteConnection, SqliteResult}; +/// # use rusqlite::{Connection, SqliteResult}; /// # use rusqlite::types::{Null}; /// # use libc::{c_int}; /// fn main() { /// } -/// fn insert_null(conn: &SqliteConnection) -> SqliteResult { +/// fn insert_null(conn: &Connection) -> SqliteResult { /// conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null]) /// } /// ``` @@ -305,13 +305,13 @@ impl FromSql for Option { #[cfg(test)] mod test { - use SqliteConnection; + use Connection; use ffi; use super::time; use libc::{c_int, c_double}; - fn checked_memory_handle() -> SqliteConnection { - let db = SqliteConnection::open_in_memory().unwrap(); + fn checked_memory_handle() -> Connection { + let db = Connection::open_in_memory().unwrap(); db.execute_batch("CREATE TABLE foo (b BLOB, t TEXT, i INTEGER, f FLOAT, n)").unwrap(); db } From f0b6bf9152f717955df2bcfa7a16187271746f67 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 13:53:58 -0500 Subject: [PATCH 43/53] Rename SqliteError -> Error. --- Changelog.md | 1 + src/backup.rs | 14 ++++---- src/functions.rs | 16 ++++----- src/lib.rs | 79 +++++++++++++++++++++++---------------------- src/named_params.rs | 4 +-- src/trace.rs | 4 +-- src/types.rs | 6 ++-- 7 files changed, 64 insertions(+), 60 deletions(-) diff --git a/Changelog.md b/Changelog.md index e1d2597..d23ea20 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ # Version UPCOMING (TBD) * Renamed `SqliteConnection` to `Connection`. The old name remains as a typealias. +* Renamed `SqliteError` to `Error`. The old name remains as a typealias. * Adds a variety of `..._named` methods for executing queries using named placeholder parameters. * Adds `backup` feature that exposes SQLite's online backup API. * Adds `functions` feature that allows user-defined scalar functions to be added to diff --git a/src/backup.rs b/src/backup.rs index 5d9c2c2..02181f2 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -36,7 +36,7 @@ use std::time::Duration; use ffi; -use {DatabaseName, Connection, SqliteError, SqliteResult}; +use {DatabaseName, Connection, Error, SqliteResult}; impl Connection { /// Back up the `name` database to the given destination path. @@ -70,8 +70,8 @@ impl Connection { match r { Done => Ok(()), - Busy => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_BUSY)), - Locked => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_LOCKED)), + Busy => Err(Error::from_handle(ptr::null_mut(), ffi::SQLITE_BUSY)), + Locked => Err(Error::from_handle(ptr::null_mut(), ffi::SQLITE_LOCKED)), More => unreachable!(), } } @@ -115,8 +115,8 @@ impl Connection { match r { Done => Ok(()), - Busy => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_BUSY)), - Locked => Err(SqliteError::from_handle(ptr::null_mut(), ffi::SQLITE_LOCKED)), + Busy => Err(Error::from_handle(ptr::null_mut(), ffi::SQLITE_BUSY)), + Locked => Err(Error::from_handle(ptr::null_mut(), ffi::SQLITE_LOCKED)), More => unreachable!(), } } @@ -201,7 +201,7 @@ impl<'a, 'b> Backup<'a, 'b> { from.db.borrow_mut().db, from_name.as_ptr()); if b.is_null() { - return Err(SqliteError::from_handle(to_db, ffi::sqlite3_errcode(to_db))); + return Err(Error::from_handle(to_db, ffi::sqlite3_errcode(to_db))); } b }; @@ -245,7 +245,7 @@ impl<'a, 'b> Backup<'a, 'b> { ffi::SQLITE_BUSY => Ok(Busy), ffi::SQLITE_LOCKED => Ok(Locked), rc => { - Err(SqliteError { + Err(Error { code: rc, message: ffi::code_to_str(rc).into(), }) diff --git a/src/functions.rs b/src/functions.rs index 3ebe3d0..aa0fdf4 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -12,7 +12,7 @@ //! extern crate rusqlite; //! extern crate regex; //! -//! use rusqlite::{Connection, SqliteError, SqliteResult}; +//! use rusqlite::{Connection, Error, SqliteResult}; //! use std::collections::HashMap; //! use regex::Regex; //! @@ -26,7 +26,7 @@ //! match entry { //! Occupied(occ) => occ.into_mut(), //! Vacant(vac) => { -//! let r = try!(Regex::new(®ex_s).map_err(|e| SqliteError { +//! let r = try!(Regex::new(®ex_s).map_err(|e| Error { //! code: libsqlite3_sys::SQLITE_ERROR, //! message: format!("Invalid regular expression: {}", e), //! })); @@ -65,7 +65,7 @@ pub use ffi::sqlite3_value_numeric_type; use types::Null; -use {SqliteResult, SqliteError, Connection, str_to_cstring, InnerConnection}; +use {SqliteResult, Error, Connection, str_to_cstring, InnerConnection}; /// A trait for types that can be converted into the result of an SQL function. pub trait ToResult { @@ -228,7 +228,7 @@ impl FromValue for String { let utf8_str = str::from_utf8(c_slice); utf8_str.map(|s| s.to_string()) .map_err(|e| { - SqliteError { + Error { code: 0, message: e.to_string(), } @@ -302,7 +302,7 @@ impl<'a> Context<'a> { if T::parameter_has_valid_sqlite_type(arg) { T::parameter_value(arg) } else { - Err(SqliteError { + Err(Error { code: ffi::SQLITE_MISMATCH, message: "Invalid value type".to_string(), }) @@ -477,7 +477,7 @@ mod test { use libc::c_double; use self::regex::Regex; - use {Connection, SqliteError, SqliteResult}; + use {Connection, Error, SqliteResult}; use ffi; use functions::Context; @@ -519,7 +519,7 @@ mod test { None => { let s = try!(ctx.get::(0)); let r = try!(Regex::new(&s).map_err(|e| { - SqliteError { + Error { code: ffi::SQLITE_ERROR, message: format!("Invalid regular expression: {}", e), } @@ -593,7 +593,7 @@ mod test { match entry { Occupied(occ) => occ.into_mut(), Vacant(vac) => { - let r = try!(Regex::new(®ex_s).map_err(|e| SqliteError { + let r = try!(Regex::new(®ex_s).map_err(|e| Error { code: ffi::SQLITE_ERROR, message: format!("Invalid regular expression: {}", e), })); diff --git a/src/lib.rs b/src/lib.rs index 773a333..b718ac6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,7 +89,7 @@ mod named_params; #[cfg(feature = "functions")] pub mod functions; /// A typedef of the result returned by many methods. -pub type SqliteResult = Result; +pub type SqliteResult = Result; unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { let c_slice = CStr::from_ptr(errmsg).to_bytes(); @@ -97,9 +97,12 @@ unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { utf8_str.unwrap_or("Invalid string encoding").to_string() } +/// Old name for `Error`. `SqliteError` is deprecated. +pub type SqliteError = Error; + /// Encompasses an error result from a call to the SQLite C API. #[derive(Debug, PartialEq)] -pub struct SqliteError { +pub struct Error { /// The error code returned by a SQLite C API call. See [SQLite Result /// Codes](http://www.sqlite.org/rescode.html) for details. pub code: c_int, @@ -109,26 +112,26 @@ pub struct SqliteError { pub message: String, } -impl fmt::Display for SqliteError { +impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} (SQLite error {})", self.message, self.code) } } -impl error::Error for SqliteError { +impl error::Error for Error { fn description(&self) -> &str { &self.message } } -impl SqliteError { - fn from_handle(db: *mut ffi::Struct_sqlite3, code: c_int) -> SqliteError { +impl Error { + fn from_handle(db: *mut ffi::Struct_sqlite3, code: c_int) -> Error { let message = if db.is_null() { ffi::code_to_str(code).to_string() } else { unsafe { errmsg_to_string(ffi::sqlite3_errmsg(db)) } }; - SqliteError { + Error { code: code, message: message, } @@ -137,7 +140,7 @@ impl SqliteError { fn str_to_cstring(s: &str) -> SqliteResult { CString::new(s).map_err(|_| { - SqliteError { + Error { code: ffi::SQLITE_MISUSE, message: format!("Could not convert string {} to C-combatible string", s), } @@ -145,7 +148,7 @@ fn str_to_cstring(s: &str) -> SqliteResult { } fn path_to_cstring(p: &Path) -> SqliteResult { - let s = try!(p.to_str().ok_or(SqliteError { + let s = try!(p.to_str().ok_or(Error { code: ffi::SQLITE_MISUSE, message: format!("Could not convert path {} to UTF-8 string", p.to_string_lossy()), @@ -382,7 +385,7 @@ impl Connection { /// Convenience method to execute a query that is expected to return a single row, /// and execute a mapping via `f` on that returned row with the possibility of failure. - /// The `Result` type of `f` must implement `std::convert::From`. + /// The `Result` type of `f` must implement `std::convert::From`. /// /// ## Example /// @@ -403,7 +406,7 @@ impl Connection { /// underlying SQLite call fails. pub fn query_row_and_then(&self, sql: &str, params: &[&ToSql], f: F) -> Result where F: FnOnce(SqliteRow) -> Result, - E: convert::From + E: convert::From { let mut stmt = try!(self.prepare(sql)); let mut rows = try!(stmt.query(params)); @@ -589,12 +592,12 @@ impl InnerConnection { let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null()); if r != ffi::SQLITE_OK { let e = if db.is_null() { - SqliteError { + Error { code: r, message: ffi::code_to_str(r).to_string(), } } else { - let e = SqliteError::from_handle(db, r); + let e = Error::from_handle(db, r); ffi::sqlite3_close(db); e }; @@ -603,7 +606,7 @@ impl InnerConnection { } let r = ffi::sqlite3_busy_timeout(db, 5000); if r != ffi::SQLITE_OK { - let e = SqliteError::from_handle(db, r); + let e = Error::from_handle(db, r); ffi::sqlite3_close(db); return Err(e); } @@ -619,7 +622,7 @@ impl InnerConnection { if code == ffi::SQLITE_OK { Ok(()) } else { - Err(SqliteError::from_handle(self.db(), code)) + Err(Error::from_handle(self.db(), code)) } } @@ -632,7 +635,7 @@ impl InnerConnection { } else { let message = errmsg_to_string(&*errmsg); ffi::sqlite3_free(errmsg as *mut c_void); - Err(SqliteError { + Err(Error { code: code, message: message, }) @@ -693,7 +696,7 @@ impl InnerConnection { sql: &str) -> SqliteResult> { if sql.len() >= ::std::i32::MAX as usize { - return Err(SqliteError { + return Err(Error { code: ffi::SQLITE_TOOBIG, message: "statement too long".to_string(), }); @@ -789,7 +792,7 @@ impl<'conn> SqliteStatement<'conn> { match r { ffi::SQLITE_DONE => { if self.column_count != 0 { - Err(SqliteError { + Err(Error { code: ffi::SQLITE_MISUSE, message: "Unexpected column count - did you mean to call query?" .to_string(), @@ -799,7 +802,7 @@ impl<'conn> SqliteStatement<'conn> { } } ffi::SQLITE_ROW => { - Err(SqliteError { + Err(Error { code: r, message: "Unexpected row result - did you mean to call query?".to_string(), }) @@ -867,7 +870,7 @@ impl<'conn> SqliteStatement<'conn> { /// Executes the prepared statement and maps a function over the resulting /// rows, where the function returns a `Result` with `Error` type implementing - /// `std::convert::From` (so errors can be unified). + /// `std::convert::From` (so errors can be unified). /// /// Unlike the iterator produced by `query`, the returned iterator does not expose the possibility /// for accessing stale rows. @@ -879,7 +882,7 @@ impl<'conn> SqliteStatement<'conn> { params: &[&ToSql], f: F) -> SqliteResult> - where E: convert::From, + where E: convert::From, F: FnMut(&SqliteRow) -> Result { let row_iter = try!(self.query(params)); @@ -968,14 +971,14 @@ impl<'stmt, T, F> Iterator for MappedRows<'stmt, F> where F: FnMut(&SqliteRow) - } /// An iterator over the mapped resulting rows of a query, with an Error type -/// unifying with SqliteError. +/// unifying with Error. pub struct AndThenRows<'stmt, F> { rows: SqliteRows<'stmt>, map: F, } impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F> -where E: convert::From, +where E: convert::From, F: FnMut(&SqliteRow) -> Result { type Item = Result; @@ -1042,7 +1045,7 @@ impl<'stmt> SqliteRows<'stmt> { match self.next() { Some(row) => row, None => { - Err(SqliteError { + Err(Error { code: ffi::SQLITE_NOTICE, message: "Query did not return a row".to_string(), }) @@ -1121,21 +1124,21 @@ impl<'stmt> SqliteRow<'stmt> { /// /// ## Failure /// - /// Returns a `SQLITE_MISMATCH`-coded `SqliteError` if the underlying SQLite column + /// Returns a `SQLITE_MISMATCH`-coded `Error` if the underlying SQLite column /// type is not a valid type as a source for `T`. /// - /// Returns a `SQLITE_MISUSE`-coded `SqliteError` if `idx` is outside the valid column range + /// Returns a `SQLITE_MISUSE`-coded `Error` if `idx` is outside the valid column range /// for this row or if this row is stale. pub fn get_checked(&self, idx: c_int) -> SqliteResult { if self.row_idx != self.current_row.get() { - return Err(SqliteError { + return Err(Error { code: ffi::SQLITE_MISUSE, message: "Cannot get values from a row after advancing to next row".to_string(), }); } unsafe { if idx < 0 || idx >= self.stmt.column_count { - return Err(SqliteError { + return Err(Error { code: ffi::SQLITE_MISUSE, message: "Invalid column index".to_string(), }); @@ -1144,7 +1147,7 @@ impl<'stmt> SqliteRow<'stmt> { if T::column_has_valid_sqlite_type(self.stmt.stmt, idx) { FromSql::column_result(self.stmt.stmt, idx) } else { - Err(SqliteError { + Err(Error { code: ffi::SQLITE_MISMATCH, message: "Invalid column type".to_string(), }) @@ -1418,7 +1421,7 @@ mod test { #[derive(Debug, PartialEq)] enum CustomError { SomeError, - Sqlite(SqliteError), + Sqlite(Error), } impl fmt::Display for CustomError { @@ -1442,8 +1445,8 @@ mod test { } } - impl From for CustomError { - fn from(se: SqliteError) -> CustomError { + impl From for CustomError { + fn from(se: Error) -> CustomError { CustomError::Sqlite(se) } } @@ -1492,7 +1495,7 @@ mod test { .collect(); assert_eq!(bad_type, - Err(SqliteError { + Err(Error { code: ffi::SQLITE_MISMATCH, message: "Invalid column type".to_owned(), })); @@ -1503,7 +1506,7 @@ mod test { .collect(); assert_eq!(bad_idx, - Err(SqliteError { + Err(Error { code: ffi::SQLITE_MISUSE, message: "Invalid column index".to_owned(), })); @@ -1555,7 +1558,7 @@ mod test { .collect(); assert_eq!(bad_type, - Err(CustomError::Sqlite(SqliteError { + Err(CustomError::Sqlite(Error { code: ffi::SQLITE_MISMATCH, message: "Invalid column type".to_owned(), }))); @@ -1568,7 +1571,7 @@ mod test { .collect(); assert_eq!(bad_idx, - Err(CustomError::Sqlite(SqliteError { + Err(CustomError::Sqlite(Error { code: ffi::SQLITE_MISUSE, message: "Invalid column index".to_owned(), }))); @@ -1616,7 +1619,7 @@ mod test { }); assert_eq!(bad_type, - Err(CustomError::Sqlite(SqliteError { + Err(CustomError::Sqlite(Error { code: ffi::SQLITE_MISMATCH, message: "Invalid column type".to_owned(), }))); @@ -1626,7 +1629,7 @@ mod test { }); assert_eq!(bad_idx, - Err(CustomError::Sqlite(SqliteError { + Err(CustomError::Sqlite(Error { code: ffi::SQLITE_MISUSE, message: "Invalid column index".to_owned(), }))); diff --git a/src/named_params.rs b/src/named_params.rs index 60e99d8..0247c7f 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -2,7 +2,7 @@ use libc::c_int; use super::ffi; -use {SqliteResult, SqliteError, Connection, SqliteStatement, SqliteRows, SqliteRow, +use {SqliteResult, Error, Connection, SqliteStatement, SqliteRows, SqliteRow, str_to_cstring}; use types::ToSql; @@ -134,7 +134,7 @@ impl<'conn> SqliteStatement<'conn> { if let Some(i) = try!(self.parameter_index(name)) { try!(self.conn.decode_result(unsafe { value.bind_parameter(self.stmt, i) })); } else { - return Err(SqliteError { + return Err(Error { code: ffi::SQLITE_MISUSE, message: format!("Invalid parameter name: {}", name), }); diff --git a/src/trace.rs b/src/trace.rs index f79f619..9894d3f 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -8,7 +8,7 @@ use std::str; use std::time::Duration; use super::ffi; -use {SqliteError, SqliteResult, Connection}; +use {Error, SqliteResult, Connection}; /// Set up the process-wide SQLite error logging callback. /// This function is marked unsafe for two reasons: @@ -43,7 +43,7 @@ pub unsafe fn config_log(callback: Option) -> SqliteResult<()> }; if rc != ffi::SQLITE_OK { - return Err(SqliteError { + return Err(Error { code: rc, message: "sqlite3_config(SQLITE_CONFIG_LOG, ...)".to_string(), }); diff --git a/src/types.rs b/src/types.rs index bf5f86c..274e990 100644 --- a/src/types.rs +++ b/src/types.rs @@ -59,7 +59,7 @@ use std::ffi::CStr; use std::mem; use std::str; use super::ffi; -use super::{SqliteResult, SqliteError, str_to_cstring}; +use super::{SqliteResult, Error, str_to_cstring}; pub use ffi::sqlite3_stmt; pub use ffi::sqlite3_column_type; @@ -235,7 +235,7 @@ impl FromSql for String { let utf8_str = str::from_utf8(c_slice); utf8_str.map(|s| s.to_string()) .map_err(|e| { - SqliteError { + Error { code: 0, message: e.to_string(), } @@ -275,7 +275,7 @@ impl FromSql for time::Timespec { time::strptime(&txt, SQLITE_DATETIME_FMT) .map(|tm| tm.to_timespec()) .map_err(|parse_error| { - SqliteError { + Error { code: ffi::SQLITE_MISMATCH, message: format!("{}", parse_error), } From eb60bb3111382a3cefa2e6dbd517a5693867a6ff Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 14:06:03 -0500 Subject: [PATCH 44/53] Rename `SqliteResult` -> `Result`. --- Changelog.md | 7 +- README.md | 2 +- src/backup.rs | 18 ++-- src/functions.rs | 46 +++++----- src/lib.rs | 170 ++++++++++++++++++------------------ src/load_extension_guard.rs | 8 +- src/named_params.rs | 26 +++--- src/trace.rs | 4 +- src/transaction.rs | 30 +++---- src/types.rs | 24 ++--- 10 files changed, 171 insertions(+), 164 deletions(-) diff --git a/Changelog.md b/Changelog.md index d23ea20..e9d7d56 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,10 @@ # Version UPCOMING (TBD) -* Renamed `SqliteConnection` to `Connection`. The old name remains as a typealias. -* Renamed `SqliteError` to `Error`. The old name remains as a typealias. +* Removed `Sqlite` prefix on many types: + * `SqliteConnection` is now `Connection` + * `SqliteError` is now `Error` + * `SqliteResult` is now `Result` + The old, prefixed names are still exported should be considered deprecated. * Adds a variety of `..._named` methods for executing queries using named placeholder parameters. * Adds `backup` feature that exposes SQLite's online backup API. * Adds `functions` feature that allows user-defined scalar functions to be added to diff --git a/README.md b/README.md index 96f0fd3..05c8d98 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ performing checks at runtime to ensure you do not try to retrieve the values of will panic if you do so. A specific example that will panic: ```rust -fn bad_function_will_panic(conn: &Connection) -> SqliteResult { +fn bad_function_will_panic(conn: &Connection) -> Result { let mut stmt = try!(conn.prepare("SELECT id FROM my_table")); let mut rows = try!(stmt.query(&[])); diff --git a/src/backup.rs b/src/backup.rs index 02181f2..8eb5625 100644 --- a/src/backup.rs +++ b/src/backup.rs @@ -14,12 +14,12 @@ //! documentation](https://www.sqlite.org/backup.html). //! //! ```rust,no_run -//! # use rusqlite::{backup, Connection, SqliteResult}; +//! # use rusqlite::{backup, Connection, Result}; //! # use std::path::Path; //! # use std::time; //! //! fn backupDb>(src: &Connection, dst: P, progress: fn(backup::Progress)) -//! -> SqliteResult<()> { +//! -> Result<()> { //! let mut dst = try!(Connection::open(dst)); //! let backup = try!(backup::Backup::new(src, &mut dst)); //! backup.run_to_completion(5, time::Duration::from_millis(250), Some(progress)) @@ -36,7 +36,7 @@ use std::time::Duration; use ffi; -use {DatabaseName, Connection, Error, SqliteResult}; +use {DatabaseName, Connection, Error, Result}; impl Connection { /// Back up the `name` database to the given destination path. @@ -55,7 +55,7 @@ impl Connection { name: DatabaseName, dst_path: P, progress: Option) - -> SqliteResult<()> { + -> Result<()> { use self::StepResult::{More, Done, Busy, Locked}; let mut dst = try!(Connection::open(dst_path)); let backup = try!(Backup::new_with_names(self, name, &mut dst, DatabaseName::Main)); @@ -92,7 +92,7 @@ impl Connection { name: DatabaseName, src_path: P, progress: Option) - -> SqliteResult<()> { + -> Result<()> { use self::StepResult::{More, Done, Busy, Locked}; let src = try!(Connection::open(src_path)); let restore = try!(Backup::new_with_names(&src, DatabaseName::Main, self, name)); @@ -172,7 +172,7 @@ impl<'a, 'b> Backup<'a, 'b> { /// `NULL`. pub fn new(from: &'a Connection, to: &'b mut Connection) - -> SqliteResult> { + -> Result> { Backup::new_with_names(from, DatabaseName::Main, to, DatabaseName::Main) } @@ -189,7 +189,7 @@ impl<'a, 'b> Backup<'a, 'b> { from_name: DatabaseName, to: &'b mut Connection, to_name: DatabaseName) - -> SqliteResult> { + -> Result> { let to_name = try!(to_name.to_cstring()); let from_name = try!(from_name.to_cstring()); @@ -235,7 +235,7 @@ impl<'a, 'b> Backup<'a, 'b> { /// an error code other than `DONE`, `OK`, `BUSY`, or `LOCKED`. `BUSY` and /// `LOCKED` are transient errors and are therefore returned as possible /// `Ok` values. - pub fn step(&self, num_pages: c_int) -> SqliteResult { + pub fn step(&self, num_pages: c_int) -> Result { use self::StepResult::{Done, More, Busy, Locked}; let rc = unsafe { ffi::sqlite3_backup_step(self.b, num_pages) }; @@ -272,7 +272,7 @@ impl<'a, 'b> Backup<'a, 'b> { pages_per_step: c_int, pause_between_pages: Duration, progress: Option) - -> SqliteResult<()> { + -> Result<()> { use self::StepResult::{Done, More, Busy, Locked}; assert!(pages_per_step > 0, "pages_per_step must be positive"); diff --git a/src/functions.rs b/src/functions.rs index aa0fdf4..502b83a 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -12,11 +12,11 @@ //! extern crate rusqlite; //! extern crate regex; //! -//! use rusqlite::{Connection, Error, SqliteResult}; +//! use rusqlite::{Connection, Error, Result}; //! use std::collections::HashMap; //! use regex::Regex; //! -//! fn add_regexp_function(db: &Connection) -> SqliteResult<()> { +//! fn add_regexp_function(db: &Connection) -> Result<()> { //! let mut cached_regexes = HashMap::new(); //! db.create_scalar_function("regexp", 2, true, move |ctx| { //! let regex_s = try!(ctx.get::(0)); @@ -65,7 +65,7 @@ pub use ffi::sqlite3_value_numeric_type; use types::Null; -use {SqliteResult, Error, Connection, str_to_cstring, InnerConnection}; +use {Result, Error, Connection, str_to_cstring, InnerConnection}; /// A trait for types that can be converted into the result of an SQL function. pub trait ToResult { @@ -165,7 +165,7 @@ impl ToResult for Null { /// A trait for types that can be created from a SQLite function parameter value. pub trait FromValue: Sized { - unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult; + unsafe fn parameter_value(v: *mut sqlite3_value) -> Result; /// FromValue types can implement this method and use sqlite3_value_type to check that /// the type reported by SQLite matches a type suitable for Self. This method is used @@ -180,7 +180,7 @@ pub trait FromValue: Sized { macro_rules! raw_from_impl( ($t:ty, $f:ident, $c:expr) => ( impl FromValue for $t { - unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult<$t> { + unsafe fn parameter_value(v: *mut sqlite3_value) -> Result<$t> { Ok(ffi::$f(v)) } @@ -195,7 +195,7 @@ raw_from_impl!(c_int, sqlite3_value_int, ffi::SQLITE_INTEGER); raw_from_impl!(i64, sqlite3_value_int64, ffi::SQLITE_INTEGER); impl FromValue for bool { - unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult { + unsafe fn parameter_value(v: *mut sqlite3_value) -> Result { match ffi::sqlite3_value_int(v) { 0 => Ok(false), _ => Ok(true), @@ -208,7 +208,7 @@ impl FromValue for bool { } impl FromValue for c_double { - unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult { + unsafe fn parameter_value(v: *mut sqlite3_value) -> Result { Ok(ffi::sqlite3_value_double(v)) } @@ -219,7 +219,7 @@ impl FromValue for c_double { } impl FromValue for String { - unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult { + unsafe fn parameter_value(v: *mut sqlite3_value) -> Result { let c_text = ffi::sqlite3_value_text(v); if c_text.is_null() { Ok("".to_string()) @@ -242,7 +242,7 @@ impl FromValue for String { } impl FromValue for Vec { - unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult> { + unsafe fn parameter_value(v: *mut sqlite3_value) -> Result> { use std::slice::from_raw_parts; let c_blob = ffi::sqlite3_value_blob(v); let len = ffi::sqlite3_value_bytes(v); @@ -260,7 +260,7 @@ impl FromValue for Vec { } impl FromValue for Option { - unsafe fn parameter_value(v: *mut sqlite3_value) -> SqliteResult> { + unsafe fn parameter_value(v: *mut sqlite3_value) -> Result> { if sqlite3_value_type(v) == ffi::SQLITE_NULL { Ok(None) } else { @@ -296,7 +296,7 @@ impl<'a> Context<'a> { /// Will panic if `idx` is greater than or equal to `self.len()`. /// /// Will return Err if the underlying SQLite type cannot be converted to a `T`. - pub fn get(&self, idx: usize) -> SqliteResult { + pub fn get(&self, idx: usize) -> Result { let arg = self.args[idx]; unsafe { if T::parameter_has_valid_sqlite_type(arg) { @@ -355,9 +355,9 @@ impl Connection { /// # Example /// /// ```rust - /// # use rusqlite::{Connection, SqliteResult}; + /// # use rusqlite::{Connection, Result}; /// # type c_double = f64; - /// fn scalar_function_example(db: Connection) -> SqliteResult<()> { + /// fn scalar_function_example(db: Connection) -> Result<()> { /// try!(db.create_scalar_function("halve", 1, true, |ctx| { /// let value = try!(ctx.get::(0)); /// Ok(value / 2f64) @@ -377,8 +377,8 @@ impl Connection { n_arg: c_int, deterministic: bool, x_func: F) - -> SqliteResult<()> - where F: FnMut(&Context) -> SqliteResult, + -> Result<()> + where F: FnMut(&Context) -> Result, T: ToResult { self.db.borrow_mut().create_scalar_function(fn_name, n_arg, deterministic, x_func) @@ -392,7 +392,7 @@ impl Connection { /// # Failure /// /// Will return Err if the function could not be removed. - pub fn remove_function(&self, fn_name: &str, n_arg: c_int) -> SqliteResult<()> { + pub fn remove_function(&self, fn_name: &str, n_arg: c_int) -> Result<()> { self.db.borrow_mut().remove_function(fn_name, n_arg) } } @@ -403,14 +403,14 @@ impl InnerConnection { n_arg: c_int, deterministic: bool, x_func: F) - -> SqliteResult<()> - where F: FnMut(&Context) -> SqliteResult, + -> Result<()> + where F: FnMut(&Context) -> Result, T: ToResult { extern "C" fn call_boxed_closure(ctx: *mut sqlite3_context, argc: c_int, argv: *mut *mut sqlite3_value) - where F: FnMut(&Context) -> SqliteResult, + where F: FnMut(&Context) -> Result, T: ToResult { unsafe { @@ -452,7 +452,7 @@ impl InnerConnection { self.decode_result(r) } - fn remove_function(&mut self, fn_name: &str, n_arg: c_int) -> SqliteResult<()> { + 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(), @@ -477,11 +477,11 @@ mod test { use libc::c_double; use self::regex::Regex; - use {Connection, Error, SqliteResult}; + use {Connection, Error, Result}; use ffi; use functions::Context; - fn half(ctx: &Context) -> SqliteResult { + fn half(ctx: &Context) -> Result { assert!(ctx.len() == 1, "called with unexpected number of arguments"); let value = try!(ctx.get::(0)); Ok(value / 2f64) @@ -511,7 +511,7 @@ mod test { // This implementation of a regexp scalar function uses SQLite's auxilliary data // (https://www.sqlite.org/c3ref/get_auxdata.html) to avoid recompiling the regular // expression multiple times within one query. - fn regexp_with_auxilliary(ctx: &Context) -> SqliteResult { + fn regexp_with_auxilliary(ctx: &Context) -> Result { assert!(ctx.len() == 2, "called with unexpected number of arguments"); let saved_re: Option<&Regex> = unsafe { ctx.get_aux(0) }; diff --git a/src/lib.rs b/src/lib.rs index b718ac6..eb43b04 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,6 +68,7 @@ use std::error; use std::rc::Rc; use std::cell::{RefCell, Cell}; use std::ffi::{CStr, CString}; +use std::result; use std::str; use libc::{c_int, c_void, c_char}; @@ -88,8 +89,11 @@ mod named_params; #[cfg(feature = "backup")]pub mod backup; #[cfg(feature = "functions")] pub mod functions; +/// Old name for `Result`. `SqliteResult` is deprecated. +pub type SqliteResult = Result; + /// A typedef of the result returned by many methods. -pub type SqliteResult = Result; +pub type Result = result::Result; unsafe fn errmsg_to_string(errmsg: *const c_char) -> String { let c_slice = CStr::from_ptr(errmsg).to_bytes(); @@ -138,7 +142,7 @@ impl Error { } } -fn str_to_cstring(s: &str) -> SqliteResult { +fn str_to_cstring(s: &str) -> Result { CString::new(s).map_err(|_| { Error { code: ffi::SQLITE_MISUSE, @@ -147,7 +151,7 @@ fn str_to_cstring(s: &str) -> SqliteResult { }) } -fn path_to_cstring(p: &Path) -> SqliteResult { +fn path_to_cstring(p: &Path) -> Result { let s = try!(p.to_str().ok_or(Error { code: ffi::SQLITE_MISUSE, message: format!("Could not convert path {} to UTF-8 string", @@ -172,7 +176,7 @@ pub enum DatabaseName<'a> { // impl to avoid dead code warnings. #[cfg(feature = "backup")] impl<'a> DatabaseName<'a> { - fn to_cstring(self) -> SqliteResult { + fn to_cstring(self) -> Result { use self::DatabaseName::{Main, Temp, Attached}; match self { Main => str_to_cstring("main"), @@ -203,7 +207,7 @@ impl Connection { /// /// Will return `Err` if `path` cannot be converted to a C-compatible string or if the /// underlying SQLite open call fails. - pub fn open>(path: P) -> SqliteResult { + pub fn open>(path: P) -> Result { let flags = Default::default(); Connection::open_with_flags(path, flags) } @@ -213,7 +217,7 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite open call fails. - pub fn open_in_memory() -> SqliteResult { + pub fn open_in_memory() -> Result { let flags = Default::default(); Connection::open_in_memory_with_flags(flags) } @@ -229,7 +233,7 @@ impl Connection { /// underlying SQLite open call fails. pub fn open_with_flags>(path: P, flags: SqliteOpenFlags) - -> SqliteResult { + -> Result { let c_path = try!(path_to_cstring(path.as_ref())); InnerConnection::open_with_flags(&c_path, flags).map(|db| { Connection { @@ -247,7 +251,7 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite open call fails. - pub fn open_in_memory_with_flags(flags: SqliteOpenFlags) -> SqliteResult { + pub fn open_in_memory_with_flags(flags: SqliteOpenFlags) -> Result { let c_memory = try!(str_to_cstring(":memory:")); InnerConnection::open_with_flags(&c_memory, flags).map(|db| { Connection { @@ -265,10 +269,10 @@ impl Connection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult}; - /// # fn do_queries_part_1(conn: &Connection) -> SqliteResult<()> { Ok(()) } - /// # fn do_queries_part_2(conn: &Connection) -> SqliteResult<()> { Ok(()) } - /// fn perform_queries(conn: &Connection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, Result}; + /// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) } + /// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) } + /// fn perform_queries(conn: &Connection) -> Result<()> { /// let tx = try!(conn.transaction()); /// /// try!(do_queries_part_1(conn)); // tx causes rollback if this fails @@ -281,7 +285,7 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. - pub fn transaction<'a>(&'a self) -> SqliteResult> { + pub fn transaction<'a>(&'a self) -> Result> { SqliteTransaction::new(self, SqliteTransactionDeferred) } @@ -294,7 +298,7 @@ impl Connection { /// Will return `Err` if the underlying SQLite call fails. pub fn transaction_with_behavior<'a>(&'a self, behavior: SqliteTransactionBehavior) - -> SqliteResult> { + -> Result> { SqliteTransaction::new(self, behavior) } @@ -305,8 +309,8 @@ impl Connection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult}; - /// fn create_tables(conn: &Connection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, Result}; + /// fn create_tables(conn: &Connection) -> Result<()> { /// conn.execute_batch("BEGIN; /// CREATE TABLE foo(x INTEGER); /// CREATE TABLE bar(y TEXT); @@ -318,7 +322,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_batch(&self, sql: &str) -> SqliteResult<()> { + pub fn execute_batch(&self, sql: &str) -> Result<()> { self.db.borrow_mut().execute_batch(sql) } @@ -343,7 +347,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]) -> SqliteResult { + pub fn execute(&self, sql: &str, params: &[&ToSql]) -> Result { self.prepare(sql).and_then(|mut stmt| stmt.execute(params)) } @@ -360,8 +364,8 @@ impl Connection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteResult,Connection}; - /// fn preferred_locale(conn: &Connection) -> SqliteResult { + /// # use rusqlite::{Result,Connection}; + /// fn preferred_locale(conn: &Connection) -> Result { /// conn.query_row("SELECT value FROM preferences WHERE name='locale'", &[], |row| { /// row.get(0) /// }) @@ -374,7 +378,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 query_row(&self, sql: &str, params: &[&ToSql], f: F) -> SqliteResult + pub fn query_row(&self, sql: &str, params: &[&ToSql], f: F) -> Result where F: FnOnce(SqliteRow) -> T { let mut stmt = try!(self.prepare(sql)); @@ -390,8 +394,8 @@ impl Connection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteResult,Connection}; - /// fn preferred_locale(conn: &Connection) -> SqliteResult { + /// # use rusqlite::{Result,Connection}; + /// fn preferred_locale(conn: &Connection) -> Result { /// conn.query_row_and_then("SELECT value FROM preferences WHERE name='locale'", &[], |row| { /// row.get_checked(0) /// }) @@ -404,8 +408,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_and_then(&self, sql: &str, params: &[&ToSql], f: F) -> Result - where F: FnOnce(SqliteRow) -> Result, + pub fn query_row_and_then(&self, sql: &str, params: &[&ToSql], f: F) -> result::Result + where F: FnOnce(SqliteRow) -> result::Result, E: convert::From { let mut stmt = try!(self.prepare(sql)); @@ -419,8 +423,8 @@ impl Connection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{SqliteResult,Connection}; - /// fn preferred_locale(conn: &Connection) -> SqliteResult { + /// # use rusqlite::{Result,Connection}; + /// fn preferred_locale(conn: &Connection) -> Result { /// conn.query_row_safe("SELECT value FROM preferences WHERE name='locale'", &[], |row| { /// row.get(0) /// }) @@ -433,7 +437,7 @@ impl Connection { /// /// This method should be considered deprecated. Use `query_row` instead, which now /// does exactly the same thing. - pub fn query_row_safe(&self, sql: &str, params: &[&ToSql], f: F) -> SqliteResult + pub fn query_row_safe(&self, sql: &str, params: &[&ToSql], f: F) -> Result where F: FnOnce(SqliteRow) -> T { self.query_row(sql, params, f) @@ -444,8 +448,8 @@ impl Connection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult}; - /// fn insert_new_people(conn: &Connection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, Result}; + /// fn insert_new_people(conn: &Connection) -> Result<()> { /// let mut stmt = try!(conn.prepare("INSERT INTO People (name) VALUES (?)")); /// try!(stmt.execute(&[&"Joe Smith"])); /// try!(stmt.execute(&[&"Bob Jones"])); @@ -457,7 +461,7 @@ impl Connection { /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// underlying SQLite call fails. - pub fn prepare<'a>(&'a self, sql: &str) -> SqliteResult> { + pub fn prepare<'a>(&'a self, sql: &str) -> Result> { self.db.borrow_mut().prepare(self, sql) } @@ -469,7 +473,7 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. - pub fn close(self) -> SqliteResult<()> { + pub fn close(self) -> Result<()> { let mut db = self.db.borrow_mut(); db.close() } @@ -480,9 +484,9 @@ impl Connection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult}; + /// # use rusqlite::{Connection, Result}; /// # use std::path::{Path}; - /// fn load_my_extension(conn: &Connection) -> SqliteResult<()> { + /// fn load_my_extension(conn: &Connection) -> Result<()> { /// try!(conn.load_extension_enable()); /// try!(conn.load_extension(Path::new("my_sqlite_extension"), None)); /// conn.load_extension_disable() @@ -493,7 +497,7 @@ impl Connection { /// /// Will return `Err` if the underlying SQLite call fails. #[cfg(feature = "load_extension")] - pub fn load_extension_enable(&self) -> SqliteResult<()> { + pub fn load_extension_enable(&self) -> Result<()> { self.db.borrow_mut().enable_load_extension(1) } @@ -505,7 +509,7 @@ impl Connection { /// /// Will return `Err` if the underlying SQLite call fails. #[cfg(feature = "load_extension")] - pub fn load_extension_disable(&self) -> SqliteResult<()> { + pub fn load_extension_disable(&self) -> Result<()> { self.db.borrow_mut().enable_load_extension(0) } @@ -519,9 +523,9 @@ impl Connection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult, SqliteLoadExtensionGuard}; + /// # use rusqlite::{Connection, Result, SqliteLoadExtensionGuard}; /// # use std::path::{Path}; - /// fn load_my_extension(conn: &Connection) -> SqliteResult<()> { + /// fn load_my_extension(conn: &Connection) -> Result<()> { /// let _guard = try!(SqliteLoadExtensionGuard::new(conn)); /// /// conn.load_extension("my_sqlite_extension", None) @@ -535,11 +539,11 @@ impl Connection { pub fn load_extension>(&self, dylib_path: P, entry_point: Option<&str>) - -> SqliteResult<()> { + -> Result<()> { self.db.borrow_mut().load_extension(dylib_path.as_ref(), entry_point) } - fn decode_result(&self, code: c_int) -> SqliteResult<()> { + fn decode_result(&self, code: c_int) -> Result<()> { self.db.borrow_mut().decode_result(code) } @@ -586,7 +590,7 @@ impl Default for SqliteOpenFlags { impl InnerConnection { fn open_with_flags(c_path: &CString, flags: SqliteOpenFlags) - -> SqliteResult { + -> Result { unsafe { let mut db: *mut ffi::sqlite3 = mem::uninitialized(); let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null()); @@ -618,7 +622,7 @@ impl InnerConnection { self.db } - fn decode_result(&mut self, code: c_int) -> SqliteResult<()> { + fn decode_result(&mut self, code: c_int) -> Result<()> { if code == ffi::SQLITE_OK { Ok(()) } else { @@ -629,7 +633,7 @@ impl InnerConnection { unsafe fn decode_result_with_errmsg(&self, code: c_int, errmsg: *mut c_char) - -> SqliteResult<()> { + -> Result<()> { if code == ffi::SQLITE_OK { Ok(()) } else { @@ -642,7 +646,7 @@ impl InnerConnection { } } - fn close(&mut self) -> SqliteResult<()> { + fn close(&mut self) -> Result<()> { unsafe { let r = ffi::sqlite3_close(self.db()); self.db = ptr::null_mut(); @@ -650,7 +654,7 @@ impl InnerConnection { } } - fn execute_batch(&mut self, sql: &str) -> SqliteResult<()> { + fn execute_batch(&mut self, sql: &str) -> Result<()> { let c_sql = try!(str_to_cstring(sql)); unsafe { let mut errmsg: *mut c_char = mem::uninitialized(); @@ -664,13 +668,13 @@ impl InnerConnection { } #[cfg(feature = "load_extension")] - fn enable_load_extension(&mut self, onoff: c_int) -> SqliteResult<()> { + fn enable_load_extension(&mut self, onoff: c_int) -> Result<()> { let r = unsafe { ffi::sqlite3_enable_load_extension(self.db, onoff) }; self.decode_result(r) } #[cfg(feature = "load_extension")] - fn load_extension(&self, dylib_path: &Path, entry_point: Option<&str>) -> SqliteResult<()> { + fn load_extension(&self, dylib_path: &Path, entry_point: Option<&str>) -> Result<()> { let dylib_str = try!(path_to_cstring(dylib_path)); unsafe { let mut errmsg: *mut c_char = mem::uninitialized(); @@ -694,7 +698,7 @@ impl InnerConnection { fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) - -> SqliteResult> { + -> Result> { if sql.len() >= ::std::i32::MAX as usize { return Err(Error { code: ffi::SQLITE_TOOBIG, @@ -764,8 +768,8 @@ impl<'conn> SqliteStatement<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult}; - /// fn update_rows(conn: &Connection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, Result}; + /// fn update_rows(conn: &Connection) -> Result<()> { /// let mut stmt = try!(conn.prepare("UPDATE foo SET bar = 'baz' WHERE qux = ?")); /// /// try!(stmt.execute(&[&1i32])); @@ -779,14 +783,14 @@ impl<'conn> SqliteStatement<'conn> { /// /// Will return `Err` if binding parameters fails, the executed statement returns rows (in /// which case `query` should be used instead), or the underling SQLite call fails. - pub fn execute(&mut self, params: &[&ToSql]) -> SqliteResult { + pub fn execute(&mut self, params: &[&ToSql]) -> Result { unsafe { try!(self.bind_parameters(params)); self.execute_() } } - unsafe fn execute_(&mut self) -> SqliteResult { + unsafe fn execute_(&mut self) -> Result { let r = ffi::sqlite3_step(self.stmt); ffi::sqlite3_reset(self.stmt); match r { @@ -816,8 +820,8 @@ impl<'conn> SqliteStatement<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult}; - /// fn get_names(conn: &Connection) -> SqliteResult> { + /// # use rusqlite::{Connection, Result}; + /// fn get_names(conn: &Connection) -> Result> { /// let mut stmt = try!(conn.prepare("SELECT name FROM people")); /// let mut rows = try!(stmt.query(&[])); /// @@ -834,7 +838,7 @@ impl<'conn> SqliteStatement<'conn> { /// # Failure /// /// Will return `Err` if binding parameters fails. - pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> SqliteResult> { + pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result> { self.reset_if_needed(); unsafe { @@ -857,7 +861,7 @@ impl<'conn> SqliteStatement<'conn> { pub fn query_map<'a, T, F>(&'a mut self, params: &[&ToSql], f: F) - -> SqliteResult> + -> Result> where F: FnMut(&SqliteRow) -> T { let row_iter = try!(self.query(params)); @@ -881,9 +885,9 @@ impl<'conn> SqliteStatement<'conn> { pub fn query_and_then<'a, T, E, F>(&'a mut self, params: &[&ToSql], f: F) - -> SqliteResult> + -> Result> where E: convert::From, - F: FnMut(&SqliteRow) -> Result + F: FnMut(&SqliteRow) -> result::Result { let row_iter = try!(self.query(params)); @@ -901,11 +905,11 @@ impl<'conn> SqliteStatement<'conn> { /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. - pub fn finalize(mut self) -> SqliteResult<()> { + pub fn finalize(mut self) -> Result<()> { self.finalize_() } - unsafe fn bind_parameters(&mut self, params: &[&ToSql]) -> SqliteResult<()> { + unsafe fn bind_parameters(&mut self, params: &[&ToSql]) -> Result<()> { assert!(params.len() as c_int == ffi::sqlite3_bind_parameter_count(self.stmt), "incorrect number of parameters to query(): expected {}, got {}", ffi::sqlite3_bind_parameter_count(self.stmt), @@ -927,7 +931,7 @@ impl<'conn> SqliteStatement<'conn> { } } - fn finalize_(&mut self) -> SqliteResult<()> { + fn finalize_(&mut self) -> Result<()> { let r = unsafe { ffi::sqlite3_finalize(self.stmt) }; self.stmt = ptr::null_mut(); self.conn.decode_result(r) @@ -963,9 +967,9 @@ pub struct MappedRows<'stmt, F> { impl<'stmt, T, F> Iterator for MappedRows<'stmt, F> where F: FnMut(&SqliteRow) -> T { - type Item = SqliteResult; + type Item = Result; - fn next(&mut self) -> Option> { + fn next(&mut self) -> Option> { self.rows.next().map(|row_result| row_result.map(|row| (self.map)(&row))) } } @@ -979,9 +983,9 @@ pub struct AndThenRows<'stmt, F> { impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F> where E: convert::From, - F: FnMut(&SqliteRow) -> Result + F: FnMut(&SqliteRow) -> result::Result { - type Item = Result; + type Item = result::Result; fn next(&mut self) -> Option { self.rows.next().map(|row_result| { @@ -1000,8 +1004,8 @@ where E: convert::From, /// iterator). For example: /// /// ```rust,no_run -/// # use rusqlite::{Connection, SqliteResult}; -/// fn bad_function_will_panic(conn: &Connection) -> SqliteResult { +/// # use rusqlite::{Connection, Result}; +/// fn bad_function_will_panic(conn: &Connection) -> Result { /// let mut stmt = try!(conn.prepare("SELECT id FROM my_table")); /// let mut rows = try!(stmt.query(&[])); /// @@ -1041,7 +1045,7 @@ impl<'stmt> SqliteRows<'stmt> { } } - fn get_expected_row(&mut self) -> SqliteResult> { + fn get_expected_row(&mut self) -> Result> { match self.next() { Some(row) => row, None => { @@ -1055,9 +1059,9 @@ impl<'stmt> SqliteRows<'stmt> { } impl<'stmt> Iterator for SqliteRows<'stmt> { - type Item = SqliteResult>; + type Item = Result>; - fn next(&mut self) -> Option>> { + fn next(&mut self) -> Option>> { if self.failed { return None; } @@ -1096,8 +1100,8 @@ impl<'stmt> SqliteRow<'stmt> { /// for example) this isn't an issue, but it means you cannot do something like this: /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult}; - /// fn bad_function_will_panic(conn: &Connection) -> SqliteResult { + /// # use rusqlite::{Connection, Result}; + /// fn bad_function_will_panic(conn: &Connection) -> Result { /// let mut stmt = try!(conn.prepare("SELECT id FROM my_table")); /// let mut rows = try!(stmt.query(&[])); /// @@ -1129,7 +1133,7 @@ impl<'stmt> SqliteRow<'stmt> { /// /// Returns a `SQLITE_MISUSE`-coded `Error` if `idx` is outside the valid column range /// for this row or if this row is stale. - pub fn get_checked(&self, idx: c_int) -> SqliteResult { + pub fn get_checked(&self, idx: c_int) -> Result { if self.row_idx != self.current_row.get() { return Err(Error { code: ffi::SQLITE_MISUSE, @@ -1240,10 +1244,10 @@ mod test { let db = checked_memory_handle(); db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap(); - assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", &[&1i32]).unwrap(), - 1); - assert_eq!(db.execute("INSERT INTO foo(x) VALUES (?)", &[&2i32]).unwrap(), - 1); + 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("SELECT SUM(x) FROM foo", &[], |r| r.get(0)).unwrap()); @@ -1328,7 +1332,7 @@ mod test { db.execute_batch(sql).unwrap(); let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap(); - let results: SqliteResult> = query.query_map(&[], |row| row.get(1)) + let results: Result> = query.query_map(&[], |row| row.get(1)) .unwrap() .collect(); @@ -1425,7 +1429,7 @@ mod test { } impl fmt::Display for CustomError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { match *self { CustomError::SomeError => write!(f, "{}", self.description()), CustomError::Sqlite(ref se) => write!(f, "{}: {}", self.description(), se), @@ -1451,7 +1455,7 @@ mod test { } } - type CustomResult = Result; + type CustomResult = ::std::result::Result; #[test] #[cfg_attr(rustfmt, rustfmt_skip)] @@ -1467,7 +1471,7 @@ mod test { db.execute_batch(sql).unwrap(); let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap(); - let results: SqliteResult> = query.query_and_then(&[], + let results: Result> = query.query_and_then(&[], |row| row.get_checked(1)) .unwrap() .collect(); @@ -1489,7 +1493,7 @@ 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: SqliteResult> = query.query_and_then(&[], + let bad_type: Result> = query.query_and_then(&[], |row| row.get_checked(1)) .unwrap() .collect(); @@ -1500,7 +1504,7 @@ mod test { message: "Invalid column type".to_owned(), })); - let bad_idx: SqliteResult> = query.query_and_then(&[], + let bad_idx: Result> = query.query_and_then(&[], |row| row.get_checked(3)) .unwrap() .collect(); diff --git a/src/load_extension_guard.rs b/src/load_extension_guard.rs index 2c02798..bec82d1 100644 --- a/src/load_extension_guard.rs +++ b/src/load_extension_guard.rs @@ -1,13 +1,13 @@ -use {SqliteResult, Connection}; +use {Result, Connection}; /// RAII guard temporarily enabling SQLite extensions to be loaded. /// /// ## Example /// /// ```rust,no_run -/// # use rusqlite::{Connection, SqliteResult, SqliteLoadExtensionGuard}; +/// # use rusqlite::{Connection, Result, SqliteLoadExtensionGuard}; /// # use std::path::{Path}; -/// fn load_my_extension(conn: &Connection) -> SqliteResult<()> { +/// fn load_my_extension(conn: &Connection) -> Result<()> { /// let _guard = try!(SqliteLoadExtensionGuard::new(conn)); /// /// conn.load_extension(Path::new("my_sqlite_extension"), None) @@ -20,7 +20,7 @@ pub struct SqliteLoadExtensionGuard<'conn> { impl<'conn> SqliteLoadExtensionGuard<'conn> { /// Attempt to enable loading extensions. Loading extensions will be disabled when this /// guard goes out of scope. Cannot be meaningfully nested. - pub fn new(conn: &Connection) -> SqliteResult { + pub fn new(conn: &Connection) -> Result { conn.load_extension_enable().map(|_| SqliteLoadExtensionGuard { conn: conn }) } } diff --git a/src/named_params.rs b/src/named_params.rs index 0247c7f..9dd08a2 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -2,7 +2,7 @@ use libc::c_int; use super::ffi; -use {SqliteResult, Error, Connection, SqliteStatement, SqliteRows, SqliteRow, +use {Result, Error, Connection, SqliteStatement, SqliteRows, SqliteRow, str_to_cstring}; use types::ToSql; @@ -15,8 +15,8 @@ impl Connection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult}; - /// fn insert(conn: &Connection) -> SqliteResult { + /// # use rusqlite::{Connection, Result}; + /// fn insert(conn: &Connection) -> Result { /// conn.execute_named("INSERT INTO test (name) VALUES (:name)", &[(":name", &"one")]) /// } /// ``` @@ -25,7 +25,7 @@ impl Connection { /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// underlying SQLite call fails. - pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> SqliteResult { + pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> Result { self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params)) } @@ -42,7 +42,7 @@ impl Connection { sql: &str, params: &[(&str, &ToSql)], f: F) - -> SqliteResult + -> Result where F: FnOnce(SqliteRow) -> T { let mut stmt = try!(self.prepare(sql)); @@ -59,7 +59,7 @@ impl<'conn> SqliteStatement<'conn> { /// /// Will return Err if `name` is invalid. Will return Ok(None) if the name /// is valid but not a bound parameter of this statement. - pub fn parameter_index(&self, name: &str) -> SqliteResult> { + pub fn parameter_index(&self, name: &str) -> Result> { let c_name = try!(str_to_cstring(name)); let c_index = unsafe { ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) }; Ok(match c_index { @@ -79,8 +79,8 @@ impl<'conn> SqliteStatement<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult}; - /// fn insert(conn: &Connection) -> SqliteResult { + /// # use rusqlite::{Connection, Result}; + /// fn insert(conn: &Connection) -> Result { /// let mut stmt = try!(conn.prepare("INSERT INTO test (name) VALUES (:name)")); /// stmt.execute_named(&[(":name", &"one")]) /// } @@ -90,7 +90,7 @@ impl<'conn> SqliteStatement<'conn> { /// /// Will return `Err` if binding parameters fails, the executed statement returns rows (in /// which case `query` should be used instead), or the underling SQLite call fails. - pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult { + pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> Result { try!(self.bind_parameters_named(params)); unsafe { self.execute_() @@ -105,8 +105,8 @@ impl<'conn> SqliteStatement<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult, SqliteRows}; - /// fn query(conn: &Connection) -> SqliteResult<()> { + /// # use rusqlite::{Connection, Result, SqliteRows}; + /// fn query(conn: &Connection) -> Result<()> { /// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name")); /// let mut rows = try!(stmt.query_named(&[(":name", &"one")])); /// for row in rows { @@ -121,7 +121,7 @@ impl<'conn> SqliteStatement<'conn> { /// Will return `Err` if binding parameters fails. pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) - -> SqliteResult> { + -> Result> { self.reset_if_needed(); try!(self.bind_parameters_named(params)); @@ -129,7 +129,7 @@ impl<'conn> SqliteStatement<'conn> { Ok(SqliteRows::new(self)) } - fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> { + fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> { for &(name, value) in params { if let Some(i) = try!(self.parameter_index(name)) { try!(self.conn.decode_result(unsafe { value.bind_parameter(self.stmt, i) })); diff --git a/src/trace.rs b/src/trace.rs index 9894d3f..eac7fc3 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -8,7 +8,7 @@ use std::str; use std::time::Duration; use super::ffi; -use {Error, SqliteResult, Connection}; +use {Error, Result, Connection}; /// Set up the process-wide SQLite error logging callback. /// This function is marked unsafe for two reasons: @@ -21,7 +21,7 @@ use {Error, SqliteResult, Connection}; /// * It must be threadsafe if SQLite is used in a multithreaded way. /// /// cf [The Error And Warning Log](http://sqlite.org/errlog.html). -pub unsafe fn config_log(callback: Option) -> SqliteResult<()> { +pub unsafe fn config_log(callback: Option) -> Result<()> { extern "C" fn log_callback(p_arg: *mut c_void, err: c_int, msg: *const c_char) { let c_slice = unsafe { CStr::from_ptr(msg).to_bytes() }; let callback: fn(c_int, &str) = unsafe { mem::transmute(p_arg) }; diff --git a/src/transaction.rs b/src/transaction.rs index a26cc9d..425535f 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,4 +1,4 @@ -use {SqliteResult, Connection}; +use {Result, Connection}; pub use SqliteTransactionBehavior::{SqliteTransactionDeferred, SqliteTransactionImmediate, SqliteTransactionExclusive}; @@ -22,10 +22,10 @@ pub enum SqliteTransactionBehavior { /// ## Example /// /// ```rust,no_run -/// # use rusqlite::{Connection, SqliteResult}; -/// # fn do_queries_part_1(conn: &Connection) -> SqliteResult<()> { Ok(()) } -/// # fn do_queries_part_2(conn: &Connection) -> SqliteResult<()> { Ok(()) } -/// fn perform_queries(conn: &Connection) -> SqliteResult<()> { +/// # use rusqlite::{Connection, Result}; +/// # fn do_queries_part_1(conn: &Connection) -> Result<()> { Ok(()) } +/// # fn do_queries_part_2(conn: &Connection) -> Result<()> { Ok(()) } +/// fn perform_queries(conn: &Connection) -> Result<()> { /// let tx = try!(conn.transaction()); /// /// try!(do_queries_part_1(conn)); // tx causes rollback if this fails @@ -45,7 +45,7 @@ impl<'conn> SqliteTransaction<'conn> { /// Begin a new transaction. Cannot be nested; see `savepoint` for nested transactions. pub fn new(conn: &Connection, behavior: SqliteTransactionBehavior) - -> SqliteResult { + -> Result { let query = match behavior { SqliteTransactionDeferred => "BEGIN DEFERRED", SqliteTransactionImmediate => "BEGIN IMMEDIATE", @@ -71,9 +71,9 @@ impl<'conn> SqliteTransaction<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, SqliteResult}; + /// # use rusqlite::{Connection, Result}; /// # fn perform_queries_part_1_succeeds(conn: &Connection) -> bool { true } - /// fn perform_queries(conn: &Connection) -> SqliteResult<()> { + /// fn perform_queries(conn: &Connection) -> Result<()> { /// let tx = try!(conn.transaction()); /// /// { @@ -87,7 +87,7 @@ impl<'conn> SqliteTransaction<'conn> { /// tx.commit() /// } /// ``` - pub fn savepoint<'a>(&'a self) -> SqliteResult> { + pub fn savepoint<'a>(&'a self) -> Result> { self.conn.execute_batch("SAVEPOINT sp").map(|_| { SqliteTransaction { conn: self.conn, @@ -119,11 +119,11 @@ impl<'conn> SqliteTransaction<'conn> { } /// A convenience method which consumes and commits a transaction. - pub fn commit(mut self) -> SqliteResult<()> { + pub fn commit(mut self) -> Result<()> { self.commit_() } - fn commit_(&mut self) -> SqliteResult<()> { + fn commit_(&mut self) -> Result<()> { self.finished = true; self.conn.execute_batch(if self.depth == 0 { "COMMIT" @@ -133,11 +133,11 @@ impl<'conn> SqliteTransaction<'conn> { } /// A convenience method which consumes and rolls back a transaction. - pub fn rollback(mut self) -> SqliteResult<()> { + pub fn rollback(mut self) -> Result<()> { self.rollback_() } - fn rollback_(&mut self) -> SqliteResult<()> { + fn rollback_(&mut self) -> Result<()> { self.finished = true; self.conn.execute_batch(if self.depth == 0 { "ROLLBACK" @@ -151,11 +151,11 @@ impl<'conn> SqliteTransaction<'conn> { /// /// Functionally equivalent to the `Drop` implementation, but allows callers to see any /// errors that occur. - pub fn finish(mut self) -> SqliteResult<()> { + pub fn finish(mut self) -> Result<()> { self.finish_() } - fn finish_(&mut self) -> SqliteResult<()> { + fn finish_(&mut self) -> Result<()> { match (self.finished, self.commit) { (true, _) => Ok(()), (false, true) => self.commit_(), diff --git a/src/types.rs b/src/types.rs index 274e990..2e857f5 100644 --- a/src/types.rs +++ b/src/types.rs @@ -26,7 +26,7 @@ //! extern crate libc; //! //! use rusqlite::types::{FromSql, ToSql, sqlite3_stmt}; -//! use rusqlite::{SqliteResult}; +//! use rusqlite::{Result}; //! use libc::c_int; //! use time; //! @@ -34,7 +34,7 @@ //! //! impl FromSql for TimespecSql { //! unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -//! -> SqliteResult { +//! -> Result { //! let as_f64_result = FromSql::column_result(stmt, col); //! as_f64_result.map(|as_f64: f64| { //! TimespecSql(time::Timespec{ sec: as_f64.trunc() as i64, @@ -59,7 +59,7 @@ use std::ffi::CStr; use std::mem; use std::str; use super::ffi; -use super::{SqliteResult, Error, str_to_cstring}; +use super::{Result, Error, str_to_cstring}; pub use ffi::sqlite3_stmt; pub use ffi::sqlite3_column_type; @@ -75,7 +75,7 @@ pub trait ToSql { /// A trait for types that can be created from a SQLite value. pub trait FromSql: Sized { - unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> SqliteResult; + unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result; /// FromSql types can implement this method and use sqlite3_column_type to check that /// the type reported by SQLite matches a type suitable for Self. This method is used @@ -176,12 +176,12 @@ impl ToSql for Option { /// ```rust,no_run /// # extern crate libc; /// # extern crate rusqlite; -/// # use rusqlite::{Connection, SqliteResult}; +/// # use rusqlite::{Connection, Result}; /// # use rusqlite::types::{Null}; /// # use libc::{c_int}; /// fn main() { /// } -/// fn insert_null(conn: &Connection) -> SqliteResult { +/// fn insert_null(conn: &Connection) -> Result { /// conn.execute("INSERT INTO people (name) VALUES (?)", &[&Null]) /// } /// ``` @@ -197,7 +197,7 @@ impl ToSql for Null { macro_rules! raw_from_impl( ($t:ty, $f:ident, $c:expr) => ( impl FromSql for $t { - unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> SqliteResult<$t> { + unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result<$t> { Ok(ffi::$f(stmt, col)) } @@ -213,7 +213,7 @@ raw_from_impl!(i64, sqlite3_column_int64, ffi::SQLITE_INTEGER); raw_from_impl!(c_double, sqlite3_column_double, ffi::SQLITE_FLOAT); impl FromSql for bool { - unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> SqliteResult { + unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result { match ffi::sqlite3_column_int(stmt, col) { 0 => Ok(false), _ => Ok(true), @@ -226,7 +226,7 @@ impl FromSql for bool { } impl FromSql for String { - unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> SqliteResult { + unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result { let c_text = ffi::sqlite3_column_text(stmt, col); if c_text.is_null() { Ok("".to_string()) @@ -249,7 +249,7 @@ impl FromSql for String { } impl FromSql for Vec { - unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> SqliteResult> { + unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result> { use std::slice::from_raw_parts; let c_blob = ffi::sqlite3_column_blob(stmt, col); let len = ffi::sqlite3_column_bytes(stmt, col); @@ -269,7 +269,7 @@ impl FromSql for Vec { } impl FromSql for time::Timespec { - unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> SqliteResult { + unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result { let col_str = FromSql::column_result(stmt, col); col_str.and_then(|txt: String| { time::strptime(&txt, SQLITE_DATETIME_FMT) @@ -289,7 +289,7 @@ impl FromSql for time::Timespec { } impl FromSql for Option { - unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> SqliteResult> { + unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result> { if sqlite3_column_type(stmt, col) == ffi::SQLITE_NULL { Ok(None) } else { From ec654352d97facaad7f7e2957968e0ba6caf56af Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 14:08:04 -0500 Subject: [PATCH 45/53] Rename `SqliteStatement` -> `Statement`. --- Changelog.md | 1 + src/lib.rs | 29 ++++++++++++++++------------- src/named_params.rs | 5 ++--- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Changelog.md b/Changelog.md index e9d7d56..bbb595a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ * `SqliteConnection` is now `Connection` * `SqliteError` is now `Error` * `SqliteResult` is now `Result` + * `SqliteStatement` is now `Statement` The old, prefixed names are still exported should be considered deprecated. * Adds a variety of `..._named` methods for executing queries using named placeholder parameters. * Adds `backup` feature that exposes SQLite's online backup API. diff --git a/src/lib.rs b/src/lib.rs index eb43b04..b3c4a67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -461,7 +461,7 @@ impl Connection { /// /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the /// underlying SQLite call fails. - pub fn prepare<'a>(&'a self, sql: &str) -> Result> { + pub fn prepare<'a>(&'a self, sql: &str) -> Result> { self.db.borrow_mut().prepare(self, sql) } @@ -698,7 +698,7 @@ impl InnerConnection { fn prepare<'a>(&mut self, conn: &'a Connection, sql: &str) - -> Result> { + -> Result> { if sql.len() >= ::std::i32::MAX as usize { return Err(Error { code: ffi::SQLITE_TOOBIG, @@ -715,7 +715,7 @@ impl InnerConnection { &mut c_stmt, ptr::null_mut()) }; - self.decode_result(r).map(|_| SqliteStatement::new(conn, c_stmt)) + self.decode_result(r).map(|_| Statement::new(conn, c_stmt)) } fn changes(&mut self) -> c_int { @@ -730,17 +730,20 @@ impl Drop for InnerConnection { } } +/// Old name for `Statement`. `SqliteStatement` is deprecated. +pub type SqliteStatement<'conn> = Statement<'conn>; + /// A prepared statement. -pub struct SqliteStatement<'conn> { +pub struct Statement<'conn> { conn: &'conn Connection, stmt: *mut ffi::sqlite3_stmt, needs_reset: bool, column_count: c_int, } -impl<'conn> SqliteStatement<'conn> { - fn new(conn: &Connection, stmt: *mut ffi::sqlite3_stmt) -> SqliteStatement { - SqliteStatement { +impl<'conn> Statement<'conn> { + fn new(conn: &Connection, stmt: *mut ffi::sqlite3_stmt) -> Statement { + Statement { conn: conn, stmt: stmt, needs_reset: false, @@ -938,13 +941,13 @@ impl<'conn> SqliteStatement<'conn> { } } -impl<'conn> fmt::Debug for SqliteStatement<'conn> { +impl<'conn> fmt::Debug for Statement<'conn> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let sql = unsafe { let c_slice = CStr::from_ptr(ffi::sqlite3_sql(self.stmt)).to_bytes(); str::from_utf8(c_slice) }; - f.debug_struct("SqliteStatement") + f.debug_struct("Statement") .field("conn", self.conn) .field("stmt", &self.stmt) .field("sql", &sql) @@ -952,7 +955,7 @@ impl<'conn> fmt::Debug for SqliteStatement<'conn> { } } -impl<'conn> Drop for SqliteStatement<'conn> { +impl<'conn> Drop for Statement<'conn> { #[allow(unused_must_use)] fn drop(&mut self) { self.finalize_(); @@ -1031,13 +1034,13 @@ where E: convert::From, /// functions which are useful (such as support for `for ... in ...` looping, `map`, `filter`, /// etc.). pub struct SqliteRows<'stmt> { - stmt: &'stmt SqliteStatement<'stmt>, + stmt: &'stmt Statement<'stmt>, current_row: Rc>, failed: bool, } impl<'stmt> SqliteRows<'stmt> { - fn new(stmt: &'stmt SqliteStatement<'stmt>) -> SqliteRows<'stmt> { + fn new(stmt: &'stmt Statement<'stmt>) -> SqliteRows<'stmt> { SqliteRows { stmt: stmt, current_row: Rc::new(Cell::new(0)), @@ -1086,7 +1089,7 @@ impl<'stmt> Iterator for SqliteRows<'stmt> { /// A single result row of a query. pub struct SqliteRow<'stmt> { - stmt: &'stmt SqliteStatement<'stmt>, + stmt: &'stmt Statement<'stmt>, current_row: Rc>, row_idx: c_int, } diff --git a/src/named_params.rs b/src/named_params.rs index 9dd08a2..64d8bf1 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -2,8 +2,7 @@ use libc::c_int; use super::ffi; -use {Result, Error, Connection, SqliteStatement, SqliteRows, SqliteRow, - str_to_cstring}; +use {Result, Error, Connection, Statement, SqliteRows, SqliteRow, str_to_cstring}; use types::ToSql; impl Connection { @@ -52,7 +51,7 @@ impl Connection { } } -impl<'conn> SqliteStatement<'conn> { +impl<'conn> Statement<'conn> { /// Return the index of an SQL parameter given its name. /// /// # Failure From b9326401816398c7661fc613dd85c974889a4f68 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 14:09:37 -0500 Subject: [PATCH 46/53] Rename `SqliteRows` -> `Rows`. --- Changelog.md | 1 + src/lib.rs | 23 +++++++++++++---------- src/named_params.rs | 8 ++++---- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Changelog.md b/Changelog.md index bbb595a..e76d415 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ * `SqliteError` is now `Error` * `SqliteResult` is now `Result` * `SqliteStatement` is now `Statement` + * `SqliteRows` is now `Rows` The old, prefixed names are still exported should be considered deprecated. * Adds a variety of `..._named` methods for executing queries using named placeholder parameters. * Adds `backup` feature that exposes SQLite's online backup API. diff --git a/src/lib.rs b/src/lib.rs index b3c4a67..a639fce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -841,7 +841,7 @@ impl<'conn> Statement<'conn> { /// # Failure /// /// Will return `Err` if binding parameters fails. - pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result> { + pub fn query<'a>(&'a mut self, params: &[&ToSql]) -> Result> { self.reset_if_needed(); unsafe { @@ -849,7 +849,7 @@ impl<'conn> Statement<'conn> { } self.needs_reset = true; - Ok(SqliteRows::new(self)) + Ok(Rows::new(self)) } /// Executes the prepared statement and maps a function over the resulting @@ -964,7 +964,7 @@ impl<'conn> Drop for Statement<'conn> { /// An iterator over the mapped resulting rows of a query. pub struct MappedRows<'stmt, F> { - rows: SqliteRows<'stmt>, + rows: Rows<'stmt>, map: F, } @@ -980,7 +980,7 @@ impl<'stmt, T, F> Iterator for MappedRows<'stmt, F> where F: FnMut(&SqliteRow) - /// An iterator over the mapped resulting rows of a query, with an Error type /// unifying with Error. pub struct AndThenRows<'stmt, F> { - rows: SqliteRows<'stmt>, + rows: Rows<'stmt>, map: F, } @@ -998,12 +998,15 @@ where E: convert::From, } } +/// Old name for `Rows`. `SqliteRows` is deprecated. +pub type SqliteRows<'stmt> = Rows<'stmt>; + /// An iterator over the resulting rows of a query. /// /// ## Warning /// /// Due to the way SQLite returns result rows of a query, it is not safe to attempt to get values -/// from a row after it has become stale (i.e., `next()` has been called again on the `SqliteRows` +/// from a row after it has become stale (i.e., `next()` has been called again on the `Rows` /// iterator). For example: /// /// ```rust,no_run @@ -1033,15 +1036,15 @@ where E: convert::From, /// no longer implement `Iterator`, and therefore you would lose access to the majority of /// functions which are useful (such as support for `for ... in ...` looping, `map`, `filter`, /// etc.). -pub struct SqliteRows<'stmt> { +pub struct Rows<'stmt> { stmt: &'stmt Statement<'stmt>, current_row: Rc>, failed: bool, } -impl<'stmt> SqliteRows<'stmt> { - fn new(stmt: &'stmt Statement<'stmt>) -> SqliteRows<'stmt> { - SqliteRows { +impl<'stmt> Rows<'stmt> { + fn new(stmt: &'stmt Statement<'stmt>) -> Rows<'stmt> { + Rows { stmt: stmt, current_row: Rc::new(Cell::new(0)), failed: false, @@ -1061,7 +1064,7 @@ impl<'stmt> SqliteRows<'stmt> { } } -impl<'stmt> Iterator for SqliteRows<'stmt> { +impl<'stmt> Iterator for Rows<'stmt> { type Item = Result>; fn next(&mut self) -> Option>> { diff --git a/src/named_params.rs b/src/named_params.rs index 64d8bf1..dccb531 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -2,7 +2,7 @@ use libc::c_int; use super::ffi; -use {Result, Error, Connection, Statement, SqliteRows, SqliteRow, str_to_cstring}; +use {Result, Error, Connection, Statement, Rows, SqliteRow, str_to_cstring}; use types::ToSql; impl Connection { @@ -104,7 +104,7 @@ impl<'conn> Statement<'conn> { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, Result, SqliteRows}; + /// # use rusqlite::{Connection, Result, Rows}; /// fn query(conn: &Connection) -> Result<()> { /// let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name")); /// let mut rows = try!(stmt.query_named(&[(":name", &"one")])); @@ -120,12 +120,12 @@ impl<'conn> Statement<'conn> { /// Will return `Err` if binding parameters fails. pub fn query_named<'a>(&'a mut self, params: &[(&str, &ToSql)]) - -> Result> { + -> Result> { self.reset_if_needed(); try!(self.bind_parameters_named(params)); self.needs_reset = true; - Ok(SqliteRows::new(self)) + Ok(Rows::new(self)) } fn bind_parameters_named(&mut self, params: &[(&str, &ToSql)]) -> Result<()> { From ea5cb41bbf8dd83e94b7ea9a278f3bd27e685523 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 14:11:24 -0500 Subject: [PATCH 47/53] Rename `SqliteRow` -> `Row`. --- Changelog.md | 1 + README.md | 6 +++--- src/lib.rs | 31 +++++++++++++++++-------------- src/named_params.rs | 4 ++-- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/Changelog.md b/Changelog.md index e76d415..85e04df 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ * `SqliteResult` is now `Result` * `SqliteStatement` is now `Statement` * `SqliteRows` is now `Rows` + * `SqliteRow` is now `Row` The old, prefixed names are still exported should be considered deprecated. * Adds a variety of `..._named` methods for executing queries using named placeholder parameters. * Adds `backup` feature that exposes SQLite's online backup API. diff --git a/README.md b/README.md index 05c8d98..9d78640 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ fn main() { } ``` -### Design of SqliteRows and SqliteRow +### Design of Rows and Row To retrieve the result rows from a query, SQLite requires you to call [sqlite3_step()](https://www.sqlite.org/c3ref/step.html) on a prepared statement. You can only @@ -67,7 +67,7 @@ satisfy the [Iterator](http://doc.rust-lang.org/std/iter/trait.Iterator.html) tr you cannot (as easily) loop over the rows, or use many of the helpful Iterator methods like `map` and `filter`. -Instead, Rusqlite's `SqliteRows` handle does conform to `Iterator`. It ensures safety by +Instead, Rusqlite's `Rows` handle does conform to `Iterator`. It ensures safety by performing checks at runtime to ensure you do not try to retrieve the values of a "stale" row, and will panic if you do so. A specific example that will panic: @@ -88,7 +88,7 @@ fn bad_function_will_panic(conn: &Connection) -> Result { ``` There are other, less obvious things that may result in a panic as well, such as calling -`collect()` on a `SqliteRows` and then trying to use the collected rows. +`collect()` on a `Rows` and then trying to use the collected rows. Strongly consider using the method `query_map()` instead, if you can. `query_map()` returns an iterator over rows-mapped-to-some-type. This diff --git a/src/lib.rs b/src/lib.rs index a639fce..2e7d78b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -379,7 +379,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 query_row(&self, sql: &str, params: &[&ToSql], f: F) -> Result - where F: FnOnce(SqliteRow) -> T + where F: FnOnce(Row) -> T { let mut stmt = try!(self.prepare(sql)); let mut rows = try!(stmt.query(params)); @@ -409,7 +409,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 query_row_and_then(&self, sql: &str, params: &[&ToSql], f: F) -> result::Result - where F: FnOnce(SqliteRow) -> result::Result, + where F: FnOnce(Row) -> result::Result, E: convert::From { let mut stmt = try!(self.prepare(sql)); @@ -438,7 +438,7 @@ impl Connection { /// This method should be considered deprecated. Use `query_row` instead, which now /// does exactly the same thing. pub fn query_row_safe(&self, sql: &str, params: &[&ToSql], f: F) -> Result - where F: FnOnce(SqliteRow) -> T + where F: FnOnce(Row) -> T { self.query_row(sql, params, f) } @@ -865,7 +865,7 @@ impl<'conn> Statement<'conn> { params: &[&ToSql], f: F) -> Result> - where F: FnMut(&SqliteRow) -> T + where F: FnMut(&Row) -> T { let row_iter = try!(self.query(params)); @@ -890,7 +890,7 @@ impl<'conn> Statement<'conn> { f: F) -> Result> where E: convert::From, - F: FnMut(&SqliteRow) -> result::Result + F: FnMut(&Row) -> result::Result { let row_iter = try!(self.query(params)); @@ -968,7 +968,7 @@ pub struct MappedRows<'stmt, F> { map: F, } -impl<'stmt, T, F> Iterator for MappedRows<'stmt, F> where F: FnMut(&SqliteRow) -> T +impl<'stmt, T, F> Iterator for MappedRows<'stmt, F> where F: FnMut(&Row) -> T { type Item = Result; @@ -986,7 +986,7 @@ pub struct AndThenRows<'stmt, F> { impl<'stmt, T, E, F> Iterator for AndThenRows<'stmt, F> where E: convert::From, - F: FnMut(&SqliteRow) -> result::Result + F: FnMut(&Row) -> result::Result { type Item = result::Result; @@ -1051,7 +1051,7 @@ impl<'stmt> Rows<'stmt> { } } - fn get_expected_row(&mut self) -> Result> { + fn get_expected_row(&mut self) -> Result> { match self.next() { Some(row) => row, None => { @@ -1065,9 +1065,9 @@ impl<'stmt> Rows<'stmt> { } impl<'stmt> Iterator for Rows<'stmt> { - type Item = Result>; + type Item = Result>; - fn next(&mut self) -> Option>> { + fn next(&mut self) -> Option>> { if self.failed { return None; } @@ -1075,7 +1075,7 @@ impl<'stmt> Iterator for Rows<'stmt> { ffi::SQLITE_ROW => { let current_row = self.current_row.get() + 1; self.current_row.set(current_row); - Some(Ok(SqliteRow { + Some(Ok(Row { stmt: self.stmt, current_row: self.current_row.clone(), row_idx: current_row, @@ -1090,17 +1090,20 @@ impl<'stmt> Iterator for Rows<'stmt> { } } +/// Old name for `Row`. `SqliteRow` is deprecated. +pub type SqliteRow<'stmt> = Row<'stmt>; + /// A single result row of a query. -pub struct SqliteRow<'stmt> { +pub struct Row<'stmt> { stmt: &'stmt Statement<'stmt>, current_row: Rc>, row_idx: c_int, } -impl<'stmt> SqliteRow<'stmt> { +impl<'stmt> Row<'stmt> { /// Get the value of a particular column of the result row. /// - /// Note that `SqliteRow` can panic at runtime if you use it incorrectly. When you are + /// Note that `Row` can panic at runtime if you use it incorrectly. When you are /// retrieving the rows of a query, a row becomes stale once you have requested the next row, /// and the values can no longer be retrieved. In general (when using looping over the rows, /// for example) this isn't an issue, but it means you cannot do something like this: diff --git a/src/named_params.rs b/src/named_params.rs index dccb531..d1dcdc9 100644 --- a/src/named_params.rs +++ b/src/named_params.rs @@ -2,7 +2,7 @@ use libc::c_int; use super::ffi; -use {Result, Error, Connection, Statement, Rows, SqliteRow, str_to_cstring}; +use {Result, Error, Connection, Statement, Rows, Row, str_to_cstring}; use types::ToSql; impl Connection { @@ -42,7 +42,7 @@ impl Connection { params: &[(&str, &ToSql)], f: F) -> Result - where F: FnOnce(SqliteRow) -> T + where F: FnOnce(Row) -> T { let mut stmt = try!(self.prepare(sql)); let mut rows = try!(stmt.query_named(params)); From 9cac56d6a2cd3bf3fef0864675e2f0d4f67771c7 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 14:13:29 -0500 Subject: [PATCH 48/53] Rename `SqliteOpenFlags` -> `OpenFlags`. --- Changelog.md | 1 + src/lib.rs | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Changelog.md b/Changelog.md index 85e04df..0a72e5e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ * `SqliteStatement` is now `Statement` * `SqliteRows` is now `Rows` * `SqliteRow` is now `Row` + * `SqliteOpenFlags` is now `OpenFlags` The old, prefixed names are still exported should be considered deprecated. * Adds a variety of `..._named` methods for executing queries using named placeholder parameters. * Adds `backup` feature that exposes SQLite's online backup API. diff --git a/src/lib.rs b/src/lib.rs index 2e7d78b..df6cc21 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -232,7 +232,7 @@ impl Connection { /// Will return `Err` if `path` cannot be converted to a C-compatible string or if the /// underlying SQLite open call fails. pub fn open_with_flags>(path: P, - flags: SqliteOpenFlags) + flags: OpenFlags) -> Result { let c_path = try!(path_to_cstring(path.as_ref())); InnerConnection::open_with_flags(&c_path, flags).map(|db| { @@ -251,7 +251,7 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite open call fails. - pub fn open_in_memory_with_flags(flags: SqliteOpenFlags) -> Result { + pub fn open_in_memory_with_flags(flags: OpenFlags) -> Result { let c_memory = try!(str_to_cstring(":memory:")); InnerConnection::open_with_flags(&c_memory, flags).map(|db| { Connection { @@ -564,11 +564,14 @@ struct InnerConnection { db: *mut ffi::Struct_sqlite3, } +/// Old name for `OpenFlags`. `SqliteOpenFlags` is deprecated. +pub type SqliteOpenFlags = OpenFlags; + bitflags! { #[doc = "Flags for opening SQLite database connections."] #[doc = "See [sqlite3_open_v2](http://www.sqlite.org/c3ref/open.html) for details."] #[repr(C)] - flags SqliteOpenFlags: c_int { + flags OpenFlags: c_int { const SQLITE_OPEN_READ_ONLY = 0x00000001, const SQLITE_OPEN_READ_WRITE = 0x00000002, const SQLITE_OPEN_CREATE = 0x00000004, @@ -581,15 +584,15 @@ bitflags! { } } -impl Default for SqliteOpenFlags { - fn default() -> SqliteOpenFlags { +impl Default for OpenFlags { + fn default() -> OpenFlags { SQLITE_OPEN_READ_WRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NO_MUTEX | SQLITE_OPEN_URI } } impl InnerConnection { fn open_with_flags(c_path: &CString, - flags: SqliteOpenFlags) + flags: OpenFlags) -> Result { unsafe { let mut db: *mut ffi::sqlite3 = mem::uninitialized(); @@ -1222,7 +1225,7 @@ mod test { #[test] fn test_open_with_flags() { - for bad_flags in [SqliteOpenFlags::empty(), + for bad_flags in [OpenFlags::empty(), SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_READ_WRITE, SQLITE_OPEN_READ_ONLY | SQLITE_OPEN_CREATE] .iter() { From 3d15a8a15ce2a400c165dd3900db2b8f5621f17f Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 14:17:43 -0500 Subject: [PATCH 49/53] Rename `SqliteTransaction*` -> `Transaction*`. --- Changelog.md | 4 +++- src/lib.rs | 16 ++++++++-------- src/transaction.rs | 40 +++++++++++++++++++++++----------------- src/types.rs | 2 +- 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/Changelog.md b/Changelog.md index 0a72e5e..771f3e4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,7 +8,9 @@ * `SqliteRows` is now `Rows` * `SqliteRow` is now `Row` * `SqliteOpenFlags` is now `OpenFlags` - The old, prefixed names are still exported should be considered deprecated. + * `SqliteTransaction` is now `Transaction`. + * `SqliteTransactionBehavior` is now `TransactionBehavior`. + The old, prefixed names are still exported but are deprecated. * Adds a variety of `..._named` methods for executing queries using named placeholder parameters. * Adds `backup` feature that exposes SQLite's online backup API. * Adds `functions` feature that allows user-defined scalar functions to be added to diff --git a/src/lib.rs b/src/lib.rs index df6cc21..500e88a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,9 +74,9 @@ use libc::{c_int, c_void, c_char}; use types::{ToSql, FromSql}; -pub use transaction::SqliteTransaction; -pub use transaction::{SqliteTransactionBehavior, SqliteTransactionDeferred, -SqliteTransactionImmediate, SqliteTransactionExclusive}; +pub use transaction::Transaction; +pub use transaction::{TransactionBehavior, TransactionDeferred, + TransactionImmediate, TransactionExclusive}; #[cfg(feature = "load_extension")] pub use load_extension_guard::SqliteLoadExtensionGuard; @@ -285,8 +285,8 @@ impl Connection { /// # Failure /// /// Will return `Err` if the underlying SQLite call fails. - pub fn transaction<'a>(&'a self) -> Result> { - SqliteTransaction::new(self, SqliteTransactionDeferred) + pub fn transaction<'a>(&'a self) -> Result> { + Transaction::new(self, TransactionDeferred) } /// Begin a new transaction with a specified behavior. @@ -297,9 +297,9 @@ impl Connection { /// /// Will return `Err` if the underlying SQLite call fails. pub fn transaction_with_behavior<'a>(&'a self, - behavior: SqliteTransactionBehavior) - -> Result> { - SqliteTransaction::new(self, behavior) + behavior: TransactionBehavior) + -> Result> { + Transaction::new(self, behavior) } /// Convenience method to run multiple SQL statements (that cannot take any parameters). diff --git a/src/transaction.rs b/src/transaction.rs index 425535f..2ed671f 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,17 +1,23 @@ use {Result, Connection}; -pub use SqliteTransactionBehavior::{SqliteTransactionDeferred, SqliteTransactionImmediate, - SqliteTransactionExclusive}; +pub use TransactionBehavior::{TransactionDeferred, TransactionImmediate, TransactionExclusive}; + +/// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is deprecated. +pub type SqliteTransactionBehavior = TransactionBehavior; /// Options for transaction behavior. See [BEGIN /// TRANSACTION](http://www.sqlite.org/lang_transaction.html) for details. #[derive(Copy,Clone)] -pub enum SqliteTransactionBehavior { - SqliteTransactionDeferred, - SqliteTransactionImmediate, - SqliteTransactionExclusive, +pub enum TransactionBehavior { + TransactionDeferred, + TransactionImmediate, + TransactionExclusive, } +/// Old name for `Transaction`. `SqliteTransaction` is deprecated. +pub type SqliteTransaction<'conn> = Transaction<'conn>; + +/// /// Represents a transaction on a database connection. /// /// ## Note @@ -34,25 +40,25 @@ pub enum SqliteTransactionBehavior { /// tx.commit() /// } /// ``` -pub struct SqliteTransaction<'conn> { +pub struct Transaction<'conn> { conn: &'conn Connection, depth: u32, commit: bool, finished: bool, } -impl<'conn> SqliteTransaction<'conn> { +impl<'conn> Transaction<'conn> { /// Begin a new transaction. Cannot be nested; see `savepoint` for nested transactions. pub fn new(conn: &Connection, - behavior: SqliteTransactionBehavior) - -> Result { + behavior: TransactionBehavior) + -> Result { let query = match behavior { - SqliteTransactionDeferred => "BEGIN DEFERRED", - SqliteTransactionImmediate => "BEGIN IMMEDIATE", - SqliteTransactionExclusive => "BEGIN EXCLUSIVE", + TransactionDeferred => "BEGIN DEFERRED", + TransactionImmediate => "BEGIN IMMEDIATE", + TransactionExclusive => "BEGIN EXCLUSIVE", }; conn.execute_batch(query).map(|_| { - SqliteTransaction { + Transaction { conn: conn, depth: 0, commit: false, @@ -87,9 +93,9 @@ impl<'conn> SqliteTransaction<'conn> { /// tx.commit() /// } /// ``` - pub fn savepoint<'a>(&'a self) -> Result> { + pub fn savepoint<'a>(&'a self) -> Result> { self.conn.execute_batch("SAVEPOINT sp").map(|_| { - SqliteTransaction { + Transaction { conn: self.conn, depth: self.depth + 1, commit: false, @@ -165,7 +171,7 @@ impl<'conn> SqliteTransaction<'conn> { } #[allow(unused_must_use)] -impl<'conn> Drop for SqliteTransaction<'conn> { +impl<'conn> Drop for Transaction<'conn> { fn drop(&mut self) { self.finish_(); } diff --git a/src/types.rs b/src/types.rs index 2e857f5..f0852ab 100644 --- a/src/types.rs +++ b/src/types.rs @@ -79,7 +79,7 @@ pub trait FromSql: Sized { /// FromSql types can implement this method and use sqlite3_column_type to check that /// the type reported by SQLite matches a type suitable for Self. This method is used - /// by `SqliteRow::get_checked` to confirm that the column contains a valid type before + /// by `Row::get_checked` to confirm that the column contains a valid type before /// attempting to retrieve the value. unsafe fn column_has_valid_sqlite_type(_: *mut sqlite3_stmt, _: c_int) -> bool { true From b1cde705be7df222719f8b7b3ff55ae364b166f8 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 14:20:11 -0500 Subject: [PATCH 50/53] Rename `SqliteLoadExtensionGuard` -> `LoadExtensionGuard`. --- Changelog.md | 1 + src/lib.rs | 10 +++++----- src/load_extension_guard.rs | 17 ++++++++++------- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Changelog.md b/Changelog.md index 771f3e4..b593e07 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ * `SqliteOpenFlags` is now `OpenFlags` * `SqliteTransaction` is now `Transaction`. * `SqliteTransactionBehavior` is now `TransactionBehavior`. + * `SqliteLoadExtensionGuard` is now `LoadExtensionGuard`. The old, prefixed names are still exported but are deprecated. * Adds a variety of `..._named` methods for executing queries using named placeholder parameters. * Adds `backup` feature that exposes SQLite's online backup API. diff --git a/src/lib.rs b/src/lib.rs index 500e88a..666342d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,12 +74,12 @@ use libc::{c_int, c_void, c_char}; use types::{ToSql, FromSql}; -pub use transaction::Transaction; +pub use transaction::{SqliteTransaction, Transaction}; pub use transaction::{TransactionBehavior, TransactionDeferred, TransactionImmediate, TransactionExclusive}; #[cfg(feature = "load_extension")] -pub use load_extension_guard::SqliteLoadExtensionGuard; +pub use load_extension_guard::{SqliteLoadExtensionGuard, LoadExtensionGuard}; pub mod types; mod transaction; @@ -478,7 +478,7 @@ impl Connection { db.close() } - /// Enable loading of SQLite extensions. Strongly consider using `SqliteLoadExtensionGuard` + /// Enable loading of SQLite extensions. Strongly consider using `LoadExtensionGuard` /// instead of this function. /// /// ## Example @@ -523,10 +523,10 @@ impl Connection { /// ## Example /// /// ```rust,no_run - /// # use rusqlite::{Connection, Result, SqliteLoadExtensionGuard}; + /// # use rusqlite::{Connection, Result, LoadExtensionGuard}; /// # use std::path::{Path}; /// fn load_my_extension(conn: &Connection) -> Result<()> { - /// let _guard = try!(SqliteLoadExtensionGuard::new(conn)); + /// let _guard = try!(LoadExtensionGuard::new(conn)); /// /// conn.load_extension("my_sqlite_extension", None) /// } diff --git a/src/load_extension_guard.rs b/src/load_extension_guard.rs index bec82d1..7a02db4 100644 --- a/src/load_extension_guard.rs +++ b/src/load_extension_guard.rs @@ -1,32 +1,35 @@ use {Result, Connection}; +/// Old name for `LoadExtensionGuard`. `SqliteLoadExtensionGuard` is deprecated. +pub type SqliteLoadExtensionGuard<'conn> = LoadExtensionGuard<'conn>; + /// RAII guard temporarily enabling SQLite extensions to be loaded. /// /// ## Example /// /// ```rust,no_run -/// # use rusqlite::{Connection, Result, SqliteLoadExtensionGuard}; +/// # use rusqlite::{Connection, Result, LoadExtensionGuard}; /// # use std::path::{Path}; /// fn load_my_extension(conn: &Connection) -> Result<()> { -/// let _guard = try!(SqliteLoadExtensionGuard::new(conn)); +/// let _guard = try!(LoadExtensionGuard::new(conn)); /// /// conn.load_extension(Path::new("my_sqlite_extension"), None) /// } /// ``` -pub struct SqliteLoadExtensionGuard<'conn> { +pub struct LoadExtensionGuard<'conn> { conn: &'conn Connection, } -impl<'conn> SqliteLoadExtensionGuard<'conn> { +impl<'conn> LoadExtensionGuard<'conn> { /// Attempt to enable loading extensions. Loading extensions will be disabled when this /// guard goes out of scope. Cannot be meaningfully nested. - pub fn new(conn: &Connection) -> Result { - conn.load_extension_enable().map(|_| SqliteLoadExtensionGuard { conn: conn }) + pub fn new(conn: &Connection) -> Result { + conn.load_extension_enable().map(|_| LoadExtensionGuard { conn: conn }) } } #[allow(unused_must_use)] -impl<'conn> Drop for SqliteLoadExtensionGuard<'conn> { +impl<'conn> Drop for LoadExtensionGuard<'conn> { fn drop(&mut self) { self.conn.load_extension_disable(); } From 6bcc3edccdc5befa0fc715ff966a98c819a9a55c Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 14:22:50 -0500 Subject: [PATCH 51/53] BREAKING CHANGE: Remove common prefix on TransactionBehavior case names. --- Changelog.md | 4 ++++ src/lib.rs | 6 ++---- src/transaction.rs | 14 ++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Changelog.md b/Changelog.md index b593e07..00f2b20 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ # Version UPCOMING (TBD) +* BREAKING CHANGE: `SqliteTransactionDeferred`, `SqliteTransactionImmediate`, and + `SqliteTransactionExclusive` are no longer exported. Instead, use + `TransactionBehavior::Deferred`, `TransactionBehavior::Immediate`, and + `TransactionBehavior::Exclusive`. * Removed `Sqlite` prefix on many types: * `SqliteConnection` is now `Connection` * `SqliteError` is now `Error` diff --git a/src/lib.rs b/src/lib.rs index 666342d..67fc19f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,9 +74,7 @@ use libc::{c_int, c_void, c_char}; use types::{ToSql, FromSql}; -pub use transaction::{SqliteTransaction, Transaction}; -pub use transaction::{TransactionBehavior, TransactionDeferred, - TransactionImmediate, TransactionExclusive}; +pub use transaction::{SqliteTransaction, Transaction, TransactionBehavior}; #[cfg(feature = "load_extension")] pub use load_extension_guard::{SqliteLoadExtensionGuard, LoadExtensionGuard}; @@ -286,7 +284,7 @@ impl Connection { /// /// Will return `Err` if the underlying SQLite call fails. pub fn transaction<'a>(&'a self) -> Result> { - Transaction::new(self, TransactionDeferred) + Transaction::new(self, TransactionBehavior::Deferred) } /// Begin a new transaction with a specified behavior. diff --git a/src/transaction.rs b/src/transaction.rs index 2ed671f..3adbdb2 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -1,7 +1,5 @@ use {Result, Connection}; -pub use TransactionBehavior::{TransactionDeferred, TransactionImmediate, TransactionExclusive}; - /// Old name for `TransactionBehavior`. `SqliteTransactionBehavior` is deprecated. pub type SqliteTransactionBehavior = TransactionBehavior; @@ -9,9 +7,9 @@ pub type SqliteTransactionBehavior = TransactionBehavior; /// TRANSACTION](http://www.sqlite.org/lang_transaction.html) for details. #[derive(Copy,Clone)] pub enum TransactionBehavior { - TransactionDeferred, - TransactionImmediate, - TransactionExclusive, + Deferred, + Immediate, + Exclusive, } /// Old name for `Transaction`. `SqliteTransaction` is deprecated. @@ -53,9 +51,9 @@ impl<'conn> Transaction<'conn> { behavior: TransactionBehavior) -> Result { let query = match behavior { - TransactionDeferred => "BEGIN DEFERRED", - TransactionImmediate => "BEGIN IMMEDIATE", - TransactionExclusive => "BEGIN EXCLUSIVE", + TransactionBehavior::Deferred => "BEGIN DEFERRED", + TransactionBehavior::Immediate => "BEGIN IMMEDIATE", + TransactionBehavior::Exclusive => "BEGIN EXCLUSIVE", }; conn.execute_batch(query).map(|_| { Transaction { From 71459ed03b124cd0b82efcb97a8503e11f86f259 Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 15:50:44 -0500 Subject: [PATCH 52/53] Add to CONTRIBUTORS --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 39264b7..3fbb310 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -11,3 +11,4 @@ rusqlite contributors (sorted alphabetically) * [krdln](https://github.com/krdln) * [Ben Striegel](https://github.com/bstrie) * [Andrew Straw](https://github.com/astraw) +* [Ronald Kinard](https://github.com/Furyhunter) From 38e630f23129ec4c75146dce180d054d5b77366c Mon Sep 17 00:00:00 2001 From: John Gallagher Date: Sat, 12 Dec 2015 16:04:11 -0500 Subject: [PATCH 53/53] Add description of features to README --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 9d78640..30e91c7 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,20 @@ fn main() { } ``` +### Optional Features + +Rusqlite provides several features that are behind [Cargo +features](http://doc.crates.io/manifest.html#the-features-section). They are: + +* [`load_extension`](http://jgallagher.github.io/rusqlite/rusqlite/struct.LoadExtensionGuard.html) + allows loading dynamic library-based SQLite extensions. +* [`backup`](http://jgallagher.github.io/rusqlite/rusqlite/backup/index.html) + allows use of SQLite's online backup API. +* [`functions`](http://jgallagher.github.io/rusqlite/rusqlite/functions/index.html) + allows you to load Rust closures into SQLite connections for use in queries. +* [`trace`](http://jgallagher.github.io/rusqlite/rusqlite/trace/index.html) + allows hooks into SQLite's tracing and profiling APIs. + ### Design of Rows and Row To retrieve the result rows from a query, SQLite requires you to call