diff --git a/README.md b/README.md index 031ca5d..76b5f86 100644 --- a/README.md +++ b/README.md @@ -21,17 +21,17 @@ fn main() { let path = setup(); let database = sqlite::open(&path).unwrap(); - database.execute(r#" + database.instruct(r#" CREATE TABLE `users` (id INTEGER, name VARCHAR(255)); INSERT INTO `users` (id, name) VALUES (1, 'Alice'); - "#, None).unwrap(); + "#).unwrap(); - database.execute("SELECT * FROM `users`;", Some(&mut |pairs| -> bool { + database.iterate("SELECT * FROM `users`;", |pairs| { for (ref column, ref value) in pairs { println!("{} = {}", column, value); } true - })).unwrap(); + }).unwrap(); } fn setup() -> PathBuf { diff --git a/examples/workflow.rs b/examples/workflow.rs index 87597a9..3bfe4f7 100644 --- a/examples/workflow.rs +++ b/examples/workflow.rs @@ -6,17 +6,17 @@ fn main() { let path = setup(); let database = sqlite::open(&path).unwrap(); - database.execute(r#" + database.instruct(r#" CREATE TABLE `users` (id INTEGER, name VARCHAR(255)); INSERT INTO `users` (id, name) VALUES (1, 'Alice'); - "#, None).unwrap(); + "#).unwrap(); - database.execute("SELECT * FROM `users`;", Some(&mut |pairs| -> bool { + database.iterate("SELECT * FROM `users`;", |pairs| { for (ref column, ref value) in pairs { println!("{} = {}", column, value); } true - })).unwrap(); + }).unwrap(); } fn setup() -> PathBuf { diff --git a/src/database.rs b/src/database.rs index b3dd4e2..1e70c36 100644 --- a/src/database.rs +++ b/src/database.rs @@ -11,12 +11,8 @@ pub struct Database { phantom: PhantomData, } -/// A callback triggered for each row of an executed SQL query. If the callback -/// returns `false`, no more rows will be processed. -pub type ExecuteCallback<'l> = FnMut(Vec<(String, String)>) -> bool + 'l; - impl Database { - /// Open a database connect. + /// Establish a database connect. pub fn open(path: &Path) -> Result { let mut raw = 0 as *mut _; unsafe { @@ -25,60 +21,75 @@ impl Database { Ok(Database { raw: raw, phantom: PhantomData }) } - /// Execute an SQL query. - pub fn execute<'l>(&self, sql: &str, callback: Option<&mut ExecuteCallback<'l>>) - -> Result<()> { - - match callback { - Some(callback) => unsafe { - let mut callback = Box::new(callback); - success!(self, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), - Some(execute_callback), - &mut callback as *mut _ as *mut _, - 0 as *mut _)); - }, - None => unsafe { - success!(self, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), None, - 0 as *mut _, 0 as *mut _)); - }, + /// Execute a query without processing the resulting rows if any. + #[inline] + pub fn instruct(&self, sql: &str) -> Result<()> { + unsafe { + success!(self, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), None, 0 as *mut _, + 0 as *mut _)); } Ok(()) } - /// Compile an SQL statement. + /// Execute a query and process the resulting rows if any. + /// + /// The callback is triggered for each row. If the callback returns `false`, + /// no more rows will be processed. + #[inline] + pub fn iterate(&self, sql: &str, callback: F) -> Result<()> + where F: FnMut(Vec<(String, String)>) -> bool + { + use std::mem::transmute; + let callback = Box::new(callback); + unsafe { + success!(self, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), + Some(execute_callback::), + transmute::<_, *mut c_void>(callback), + 0 as *mut _)); + } + Ok(()) + } + + /// Create a prepared statement. #[inline] pub fn prepare<'l>(&'l self, sql: &str) -> Result> { ::statement::new(self, sql) } - /// Set a callback to handle rejected operations when the database is busy. + /// Set a callback for handling busy events. /// /// The callback is triggered when the database cannot perform an operation - /// due to processing of a request from another client. If the callback - /// returns `true`, the operation will be repeated. - pub fn set_busy_handler bool>(&mut self, callback: Option) - -> Result<()> { - - match callback { - Some(callback) => unsafe { - use std::mem::transmute; - let callback = Box::new(callback); - success!(raw::sqlite3_busy_handler(self.raw, Some(busy_callback::), - transmute::<_, *mut c_void>(callback))); - }, - None => unsafe { - success!(raw::sqlite3_busy_handler(self.raw, None, 0 as *mut _)); - }, + /// due to processing of some other request. If the callback returns `true`, + /// the operation will be repeated. + #[inline] + pub fn set_busy_handler(&mut self, callback: F) -> Result<()> + where F: FnMut(usize) -> bool + { + use std::mem::transmute; + let callback = Box::new(callback); + unsafe { + success!(raw::sqlite3_busy_handler(self.raw, Some(busy_callback::), + transmute::<_, *mut c_void>(callback))); } Ok(()) } - /// Set a timeout before rejecting an operation when the database is busy. + /// Set an implicit callback for handling busy events that tries to repeat + /// rejected operations until a timeout expires. #[inline] pub fn set_busy_timeout(&mut self, milliseconds: usize) -> Result<()> { unsafe { success!(self, raw::sqlite3_busy_timeout(self.raw, milliseconds as c_int)) }; Ok(()) } + + /// Remove the callback handling busy events. + #[inline] + pub fn remove_busy_handler(&mut self) -> Result<()> { + unsafe { + success!(raw::sqlite3_busy_handler(self.raw, None, 0 as *mut _)); + } + Ok(()) + } } impl Drop for Database { @@ -93,13 +104,18 @@ pub fn as_raw(database: &Database) -> *mut raw::sqlite3 { database.raw } -extern fn busy_callback bool>(callback: *mut c_void, attempts: c_int) -> c_int { - if unsafe { (*(callback as *mut F))(attempts as usize) } { 1 } else { 0 } +extern fn busy_callback(callback: *mut c_void, attempts: c_int) -> c_int + where F: FnMut(usize) -> bool +{ + unsafe { + if (*(callback as *mut F))(attempts as usize) { 1 } else { 0 } + } } -extern fn execute_callback(callback: *mut c_void, count: c_int, values: *mut *mut c_char, - columns: *mut *mut c_char) -> c_int { - +extern fn execute_callback(callback: *mut c_void, count: c_int, values: *mut *mut c_char, + columns: *mut *mut c_char) -> c_int + where F: FnMut(Vec<(String, String)>) -> bool +{ unsafe { let mut pairs = Vec::with_capacity(count as usize); @@ -109,8 +125,7 @@ extern fn execute_callback(callback: *mut c_void, count: c_int, values: *mut *mu pairs.push((column, value)); } - let ref mut callback = *(callback as *mut Box<&mut ExecuteCallback>); - if callback(pairs) { 0 } else { 1 } + if (*(callback as *mut F))(pairs) { 0 } else { 1 } } } @@ -124,11 +139,12 @@ mod tests { ); #[test] - fn execute() { + fn iterate() { let (path, _directory) = setup(); let database = ok!(Database::open(&path)); - match database.execute(":)", None) { - Err(error) => assert_eq!(error.message, Some(String::from(r#"unrecognized token: ":""#))), + match database.instruct(":)") { + Err(error) => assert_eq!(error.message, + Some(String::from(r#"unrecognized token: ":""#))), _ => assert!(false), } } @@ -137,10 +153,6 @@ mod tests { fn set_busy_handler() { let (path, _directory) = setup(); let mut database = ok!(Database::open(&path)); - do_set_busy_handler(&mut database); - } - - fn do_set_busy_handler(database: &mut Database) { - ok!(database.set_busy_handler(Some(|_| true))); + ok!(database.set_busy_handler(|_| true)); } } diff --git a/src/lib.rs b/src/lib.rs index 5f18ff3..9a48fc1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -62,7 +62,7 @@ mod result; mod statement; pub use error::Error; -pub use database::{Database, ExecuteCallback}; +pub use database::Database; pub use result::{Result, ResultCode}; pub use statement::{Statement, Binding, Value}; diff --git a/tests/lib.rs b/tests/lib.rs index 0a7d9a7..5cb97ca 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -21,7 +21,7 @@ fn workflow() { let database = ok!(sqlite::open(&path)); let sql = r#"CREATE TABLE `users` (id INTEGER, name VARCHAR(255), age REAL);"#; - ok!(database.execute(sql, None)); + ok!(database.instruct(sql)); { let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#; @@ -33,14 +33,14 @@ fn workflow() { { let mut done = false; let sql = r#"SELECT * FROM `users`;"#; - ok!(database.execute(sql, Some(&mut |pairs: Vec<(String, String)>| -> bool { + ok!(database.iterate(sql, |pairs| { assert!(pairs.len() == 3); assert!(pairs[0] == pair!("id", "1")); assert!(pairs[1] == pair!("name", "Alice")); assert!(pairs[2] == pair!("age", "20.99")); done = true; true - }))); + })); assert!(done); }