From ae0a12444b3234bb1a8c3d73a71c996e66b8f654 Mon Sep 17 00:00:00 2001 From: Ivan Ukhov Date: Fri, 29 May 2015 13:08:02 -0400 Subject: [PATCH] Implement column reading --- src/database.rs | 13 ++----------- src/lib.rs | 7 +++++++ src/statement.rs | 36 ++++++++++++++++++++++++++++++++++++ tests/lib.rs | 44 +++++++++++++++++++++++++++++--------------- 4 files changed, 74 insertions(+), 26 deletions(-) diff --git a/src/database.rs b/src/database.rs index 5738c50..baf77eb 100644 --- a/src/database.rs +++ b/src/database.rs @@ -67,21 +67,12 @@ impl<'l> Drop for Database<'l> { extern fn execute_callback(callback: *mut c_void, count: c_int, values: *mut *mut c_char, columns: *mut *mut c_char) -> c_int { - macro_rules! c_str_to_string( - ($string:expr) => ( - match ::std::str::from_utf8(::std::ffi::CStr::from_ptr($string).to_bytes()) { - Ok(string) => String::from(string), - Err(_) => return 1, - } - ); - ); - unsafe { let mut pairs = Vec::with_capacity(count as usize); for i in 0..(count as isize) { - let column = c_str_to_string!(*columns.offset(i) as *const _); - let value = c_str_to_string!(*values.offset(i) as *const _); + let column = c_str_to_string!(*columns.offset(i)); + let value = c_str_to_string!(*values.offset(i)); pairs.push((column, value)); } diff --git a/src/lib.rs b/src/lib.rs index c25fe76..8433da4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,13 @@ macro_rules! str_to_c_str( ); ); +macro_rules! c_str_to_string( + ($cstr:expr) => ( + String::from_utf8_lossy(::std::ffi::CStr::from_ptr($cstr as *const _).to_bytes()) + .into_owned() + ); +); + mod database; mod statement; diff --git a/src/statement.rs b/src/statement.rs index d246a7e..f30358b 100644 --- a/src/statement.rs +++ b/src/statement.rs @@ -17,6 +17,12 @@ pub enum Binding<'l> { Text(usize, &'l str), } +/// A value stored in a column. +pub trait Value { + /// Read the value from a prepared statement at a specific position. + fn read(statement: &mut Statement, i: usize) -> Result; +} + impl<'l> Statement<'l> { /// Assign values to the placeholders. pub fn bind(&mut self, bindings: &[Binding]) -> Result<()> { @@ -38,6 +44,12 @@ impl<'l> Statement<'l> { Ok(()) } + /// Return the value of a column. + #[inline] + pub fn column(&mut self, i: usize) -> Result { + ::read(self, i) + } + /// Take a step. #[inline] pub fn step(&mut self) -> ResultCode { @@ -59,6 +71,30 @@ impl<'l> Drop for Statement<'l> { } } +impl Value for f64 { + fn read(statement: &mut Statement, i: usize) -> Result { + Ok(unsafe { ::raw::sqlite3_column_double(statement.raw, i as c_int) as f64 }) + } +} + +impl Value for i64 { + fn read(statement: &mut Statement, i: usize) -> Result { + Ok(unsafe { ::raw::sqlite3_column_int64(statement.raw, i as c_int) as i64 }) + } +} + +impl Value for String { + fn read(statement: &mut Statement, i: usize) -> Result { + unsafe { + let pointer = ::raw::sqlite3_column_text(statement.raw, i as c_int); + if pointer.is_null() { + raise!("cannot read a TEXT column"); + } + Ok(c_str_to_string!(pointer)) + } + } +} + #[inline] pub fn from_raw<'l>(raw: *mut raw::sqlite3_stmt) -> Statement<'l> { Statement { raw: raw, _phantom: PhantomData } diff --git a/tests/lib.rs b/tests/lib.rs index 8379f79..af37c95 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -23,22 +23,36 @@ fn workflow() { let sql = r#"CREATE TABLE `users` (id INTEGER, name VARCHAR(255), age REAL);"#; ok!(database.execute(sql, None)); - let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#; - let mut statement = ok!(database.statement(sql)); - ok!(statement.bind(&[Integer(1, 1), Text(2, "Alice"), Float(3, 20.99)])); - assert!(statement.step() == ResultCode::Done); + { + let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#; + let mut statement = ok!(database.statement(sql)); + ok!(statement.bind(&[Integer(1, 1), Text(2, "Alice"), Float(3, 20.99)])); + assert!(statement.step() == ResultCode::Done); + } - let mut done = false; - let sql = r#"SELECT * FROM `users`;"#; - ok!(database.execute(sql, Some(&mut |pairs: Vec<(String, String)>| -> bool { - 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); + { + let mut done = false; + let sql = r#"SELECT * FROM `users`;"#; + ok!(database.execute(sql, Some(&mut |pairs: Vec<(String, String)>| -> bool { + 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); + } + + { + let sql = r#"SELECT * FROM `users`;"#; + let mut statement = ok!(database.statement(sql)); + assert!(statement.step() == ResultCode::Row); + assert!(ok!(statement.column::(0)) == 1); + assert!(ok!(statement.column::(1)) == String::from("Alice")); + assert!(ok!(statement.column::(2)) == 20.99); + assert!(statement.step() == ResultCode::Done); + } } fn setup() -> (PathBuf, Directory) {