diff --git a/src/functions.rs b/src/functions.rs index 8c8723e..b261fce 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -54,7 +54,6 @@ 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}; use ffi; @@ -63,7 +62,7 @@ pub use ffi::sqlite3_value; pub use ffi::sqlite3_value_type; pub use ffi::sqlite3_value_numeric_type; -use types::Null; +use types::{Null, FromSql, BorrowedValue}; use {Result, Error, Connection, str_to_cstring, InnerConnection}; @@ -157,108 +156,35 @@ 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) -> 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 - /// 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 - } -} - - -macro_rules! raw_from_impl( - ($t:ty, $f:ident, $c:expr) => ( - impl FromValue for $t { - unsafe fn parameter_value(v: *mut sqlite3_value) -> Result<$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 bool { - unsafe fn parameter_value(v: *mut sqlite3_value) -> Result { - 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) -> Result { - 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) -> Result { - let c_text = ffi::sqlite3_value_text(v); - if c_text.is_null() { - Ok("".to_owned()) - } else { - let c_slice = CStr::from_ptr(c_text as *const c_char).to_bytes(); - let utf8_str = try!(str::from_utf8(c_slice)); - Ok(utf8_str.into()) - } - } - - 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) -> Result> { +impl<'a> BorrowedValue<'a> { + unsafe fn from_value(value: *mut sqlite3_value) -> BorrowedValue<'a> { 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; + match ffi::sqlite3_value_type(value) { + ffi::SQLITE_NULL => BorrowedValue::Null, + ffi::SQLITE_INTEGER => BorrowedValue::Integer(ffi::sqlite3_value_int64(value)), + ffi::SQLITE_FLOAT => BorrowedValue::Real(ffi::sqlite3_value_double(value)), + ffi::SQLITE_TEXT => { + let text = ffi::sqlite3_value_text(value); + assert!(!text.is_null(), "unexpected SQLITE_TEXT value type with NULL data"); + let s = CStr::from_ptr(text as *const c_char); - Ok(from_raw_parts(mem::transmute(c_blob), len).to_vec()) - } + // sqlite3_value_text returns UTF8 data, so our unwrap here should be fine. + let s = s.to_str().expect("sqlite3_value_text returned invalid UTF-8"); + BorrowedValue::Text(s) + } + ffi::SQLITE_BLOB => { + let blob = ffi::sqlite3_value_blob(value); + assert!(!blob.is_null(), "unexpected SQLITE_BLOB value type with NULL data"); - unsafe fn parameter_has_valid_sqlite_type(v: *mut sqlite3_value) -> bool { - sqlite3_value_type(v) == ffi::SQLITE_BLOB - } -} + let len = ffi::sqlite3_value_bytes(value); + assert!(len >= 0, "unexpected negative return from sqlite3_value_bytes"); -impl FromValue for Option { - unsafe fn parameter_value(v: *mut sqlite3_value) -> Result> { - if sqlite3_value_type(v) == ffi::SQLITE_NULL { - Ok(None) - } else { - FromValue::parameter_value(v).map(Some) + BorrowedValue::Blob(from_raw_parts(blob as *const u8, len as usize)) + } + _ => unreachable!("sqlite3_value_type returned invalid value") } } - - 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) - } } unsafe extern "C" fn free_boxed_value(p: *mut c_void) { @@ -288,15 +214,13 @@ 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) -> Result { + pub fn get(&self, idx: usize) -> Result { let arg = self.args[idx]; - unsafe { - if T::parameter_has_valid_sqlite_type(arg) { - T::parameter_value(arg) - } else { - Err(Error::InvalidFunctionParameterType) - } - } + let value = unsafe { BorrowedValue::from_value(arg) }; + FromSql::column_result(value).map_err(|err| match err { + Error::InvalidColumnType => Error::InvalidFunctionParameterType, + _ => err + }) } /// Sets the auxilliary data associated with a particular parameter. See