Split execute into instruct and iterate

This commit is contained in:
Ivan Ukhov 2015-06-08 13:27:07 -04:00
parent 81dcb73b8f
commit 4f97dda5d7
5 changed files with 78 additions and 66 deletions

View File

@ -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 {

View File

@ -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 {

View File

@ -11,12 +11,8 @@ pub struct Database {
phantom: PhantomData<raw::sqlite3>,
}
/// 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<Database> {
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<F>(&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::<F>),
transmute::<_, *mut c_void>(callback),
0 as *mut _));
}
Ok(())
}
/// Create a prepared statement.
#[inline]
pub fn prepare<'l>(&'l self, sql: &str) -> Result<Statement<'l>> {
::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<F: FnMut(usize) -> bool>(&mut self, callback: Option<F>)
-> 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::<F>),
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<F>(&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::<F>),
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<F: FnMut(usize) -> 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<F>(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<F>(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));
}
}

View File

@ -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};

View File

@ -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);
}