Refactor the error handling

This commit is contained in:
Ivan Ukhov 2015-06-08 17:43:31 -04:00
parent dd47f92a1f
commit bde720bddc
5 changed files with 74 additions and 66 deletions

View File

@ -30,8 +30,8 @@ impl<'l> Database<'l> {
#[inline] #[inline]
pub fn execute(&self, sql: &str) -> Result<()> { pub fn execute(&self, sql: &str) -> Result<()> {
unsafe { unsafe {
success!(self, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), None, 0 as *mut _, success!(self.raw, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), None, 0 as *mut _,
0 as *mut _)); 0 as *mut _));
} }
Ok(()) Ok(())
} }
@ -46,10 +46,10 @@ impl<'l> Database<'l> {
{ {
unsafe { unsafe {
let callback = Box::new(callback); let callback = Box::new(callback);
success!(self, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), success!(self.raw, raw::sqlite3_exec(self.raw, str_to_c_str!(sql),
Some(execute_callback::<F>), Some(execute_callback::<F>),
&*callback as *const _ as *mut _ as *mut _, &*callback as *const _ as *mut _ as *mut _,
0 as *mut _)); 0 as *mut _));
} }
Ok(()) Ok(())
} }
@ -57,7 +57,7 @@ impl<'l> Database<'l> {
/// Create a prepared statement. /// Create a prepared statement.
#[inline] #[inline]
pub fn prepare(&'l self, sql: &str) -> Result<Statement<'l>> { pub fn prepare(&'l self, sql: &str) -> Result<Statement<'l>> {
::statement::new(self, sql) ::statement::new(self.raw, sql)
} }
/// Set a callback for handling busy events. /// Set a callback for handling busy events.
@ -74,7 +74,7 @@ impl<'l> Database<'l> {
let result = raw::sqlite3_busy_handler(self.raw, Some(busy_callback::<F>), let result = raw::sqlite3_busy_handler(self.raw, Some(busy_callback::<F>),
&*callback as *const _ as *mut _ as *mut _); &*callback as *const _ as *mut _ as *mut _);
self.busy_callback = Some(callback); self.busy_callback = Some(callback);
success!(result); success!(self.raw, result);
} }
Ok(()) Ok(())
} }
@ -83,7 +83,7 @@ impl<'l> Database<'l> {
/// rejected operations until a timeout expires. /// rejected operations until a timeout expires.
#[inline] #[inline]
pub fn set_busy_timeout(&mut self, milliseconds: usize) -> Result<()> { pub fn set_busy_timeout(&mut self, milliseconds: usize) -> Result<()> {
unsafe { success!(self, raw::sqlite3_busy_timeout(self.raw, milliseconds as c_int)) }; unsafe { success!(self.raw, raw::sqlite3_busy_timeout(self.raw, milliseconds as c_int)) };
Ok(()) Ok(())
} }
@ -91,7 +91,7 @@ impl<'l> Database<'l> {
#[inline] #[inline]
pub fn remove_busy_handler(&mut self) -> Result<()> { pub fn remove_busy_handler(&mut self) -> Result<()> {
::std::mem::replace(&mut self.busy_callback, None); ::std::mem::replace(&mut self.busy_callback, None);
unsafe { success!(raw::sqlite3_busy_handler(self.raw, None, 0 as *mut _)) }; unsafe { success!(self.raw, raw::sqlite3_busy_handler(self.raw, None, 0 as *mut _)) };
Ok(()) Ok(())
} }
} }
@ -105,11 +105,6 @@ impl<'l> Drop for Database<'l> {
} }
} }
#[inline]
pub fn as_raw(database: &Database) -> *mut raw::sqlite3 {
database.raw
}
extern fn busy_callback<F>(callback: *mut c_void, attempts: c_int) -> c_int extern fn busy_callback<F>(callback: *mut c_void, attempts: c_int) -> c_int
where F: FnMut(usize) -> bool where F: FnMut(usize) -> bool
{ {

View File

@ -2,7 +2,7 @@ use raw;
use std::convert::{From, Into}; use std::convert::{From, Into};
use std::fmt::{self, Display, Formatter}; use std::fmt::{self, Display, Formatter};
use {Database, ResultCode}; use ResultCode;
/// An error. /// An error.
#[derive(Debug)] #[derive(Debug)]
@ -11,26 +11,6 @@ pub struct Error {
pub message: Option<String>, pub message: Option<String>,
} }
impl Error {
/// Return the last occurred error if any.
pub fn last(database: &Database) -> Option<Error> {
unsafe {
let code = raw::sqlite3_errcode(::database::as_raw(database));
if code == raw::SQLITE_OK {
return None;
}
let message = raw::sqlite3_errmsg(::database::as_raw(database));
if message.is_null() {
return None;
}
Some(Error {
code: ::result::code_from_raw(code),
message: Some(c_str_to_string!(message)),
})
}
}
}
impl<T> From<T> for Error where T: Into<String> { impl<T> From<T> for Error where T: Into<String> {
#[inline] #[inline]
fn from(message: T) -> Error { fn from(message: T) -> Error {
@ -59,3 +39,20 @@ impl Display for Error {
} }
} }
} }
pub fn last(raw: *mut raw::sqlite3) -> Option<Error> {
unsafe {
let code = raw::sqlite3_errcode(raw);
if code == raw::SQLITE_OK {
return None;
}
let message = raw::sqlite3_errmsg(raw);
if message.is_null() {
return None;
}
Some(Error {
code: ::result::code_from_raw(code),
message: Some(c_str_to_string!(message)),
})
}
}

View File

@ -10,14 +10,20 @@ macro_rules! raise(
($message:expr) => (return Err(::Error::from($message))); ($message:expr) => (return Err(::Error::from($message)));
); );
macro_rules! failure(
($database:expr, $code:expr) => (
match ::error::last($database) {
Some(error) => return Err(error),
None => return Err(::Error::from(::result::code_from_raw($code))),
}
);
);
macro_rules! success( macro_rules! success(
($database:expr, $result:expr) => ( ($database:expr, $result:expr) => (
match $result { match $result {
::raw::SQLITE_OK => {}, ::raw::SQLITE_OK => {},
code => match ::Error::last($database) { code => failure!($database, code),
Some(error) => return Err(error),
None => return Err(::Error::from(::result::code_from_raw(code))),
},
} }
); );
($result:expr) => ( ($result:expr) => (
@ -64,7 +70,7 @@ mod statement;
pub use error::Error; pub use error::Error;
pub use database::Database; pub use database::Database;
pub use result::{Result, ResultCode}; pub use result::{Result, ResultCode};
pub use statement::{Statement, Binding, Value}; pub use statement::{Statement, Binding, Value, State};
/// Open a database. /// Open a database.
#[inline] #[inline]

View File

@ -2,12 +2,12 @@ use libc::{c_double, c_int};
use raw; use raw;
use std::marker::PhantomData; use std::marker::PhantomData;
use {Database, Result, ResultCode}; use Result;
/// A prepared statement. /// A prepared statement.
pub struct Statement<'l> { pub struct Statement<'l> {
raw: *mut raw::sqlite3_stmt, raw: (*mut raw::sqlite3_stmt, *mut raw::sqlite3),
phantom: PhantomData<(&'l raw::sqlite3, raw::sqlite3_stmt)>, phantom: PhantomData<(raw::sqlite3_stmt, &'l raw::sqlite3)>,
} }
/// A binding of a parameter of a prepared statement. /// A binding of a parameter of a prepared statement.
@ -23,6 +23,13 @@ pub trait Value {
fn read(statement: &Statement, i: usize) -> Result<Self>; fn read(statement: &Statement, i: usize) -> Result<Self>;
} }
/// A state of a prepared statement.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum State {
Done,
Row,
}
impl<'l> Statement<'l> { impl<'l> Statement<'l> {
/// Bind values to the parameters. /// Bind values to the parameters.
/// ///
@ -32,17 +39,18 @@ impl<'l> Statement<'l> {
match *binding { match *binding {
Binding::Float(i, value) => unsafe { Binding::Float(i, value) => unsafe {
debug_assert!(i > 0, "the indexing starts from 1"); debug_assert!(i > 0, "the indexing starts from 1");
success!(raw::sqlite3_bind_double(self.raw, i as c_int, value as c_double)); success!(self.raw.1, raw::sqlite3_bind_double(self.raw.0, i as c_int,
value as c_double));
}, },
Binding::Integer(i, value) => unsafe { Binding::Integer(i, value) => unsafe {
debug_assert!(i > 0, "the indexing starts from 1"); debug_assert!(i > 0, "the indexing starts from 1");
success!(raw::sqlite3_bind_int64(self.raw, i as c_int, success!(self.raw.1, raw::sqlite3_bind_int64(self.raw.0, i as c_int,
value as raw::sqlite3_int64)); value as raw::sqlite3_int64));
}, },
Binding::Text(i, value) => unsafe { Binding::Text(i, value) => unsafe {
debug_assert!(i > 0, "the indexing starts from 1"); debug_assert!(i > 0, "the indexing starts from 1");
success!(raw::sqlite3_bind_text(self.raw, i as c_int, str_to_c_str!(value), -1, success!(self.raw.1, raw::sqlite3_bind_text(self.raw.0, i as c_int,
None)); str_to_c_str!(value), -1, None));
}, },
} }
} }
@ -58,15 +66,18 @@ impl<'l> Statement<'l> {
} }
/// Evaluate the statement. /// Evaluate the statement.
#[inline] pub fn step(&mut self) -> Result<State> {
pub fn step(&mut self) -> ResultCode { match unsafe { raw::sqlite3_step(self.raw.0) } {
unsafe { ::result::code_from_raw(raw::sqlite3_step(self.raw)) } raw::SQLITE_DONE => Ok(State::Done),
raw::SQLITE_ROW => Ok(State::Row),
code => failure!(self.raw.1, code),
}
} }
/// Reset the statement. /// Reset the statement.
#[inline] #[inline]
pub fn reset(&mut self) -> Result<()> { pub fn reset(&mut self) -> Result<()> {
unsafe { success!(raw::sqlite3_reset(self.raw)) }; unsafe { success!(self.raw.1, raw::sqlite3_reset(self.raw.0)) };
Ok(()) Ok(())
} }
} }
@ -74,26 +85,26 @@ impl<'l> Statement<'l> {
impl<'l> Drop for Statement<'l> { impl<'l> Drop for Statement<'l> {
#[inline] #[inline]
fn drop(&mut self) { fn drop(&mut self) {
unsafe { ::raw::sqlite3_finalize(self.raw) }; unsafe { raw::sqlite3_finalize(self.raw.0) };
} }
} }
impl Value for f64 { impl Value for f64 {
fn read(statement: &Statement, i: usize) -> Result<f64> { fn read(statement: &Statement, i: usize) -> Result<f64> {
Ok(unsafe { ::raw::sqlite3_column_double(statement.raw, i as c_int) as f64 }) Ok(unsafe { raw::sqlite3_column_double(statement.raw.0, i as c_int) as f64 })
} }
} }
impl Value for i64 { impl Value for i64 {
fn read(statement: &Statement, i: usize) -> Result<i64> { fn read(statement: &Statement, i: usize) -> Result<i64> {
Ok(unsafe { ::raw::sqlite3_column_int64(statement.raw, i as c_int) as i64 }) Ok(unsafe { raw::sqlite3_column_int64(statement.raw.0, i as c_int) as i64 })
} }
} }
impl Value for String { impl Value for String {
fn read(statement: &Statement, i: usize) -> Result<String> { fn read(statement: &Statement, i: usize) -> Result<String> {
unsafe { unsafe {
let pointer = ::raw::sqlite3_column_text(statement.raw, i as c_int); let pointer = raw::sqlite3_column_text(statement.raw.0, i as c_int);
if pointer.is_null() { if pointer.is_null() {
raise!("cannot read a TEXT column"); raise!("cannot read a TEXT column");
} }
@ -103,11 +114,10 @@ impl Value for String {
} }
#[inline] #[inline]
pub fn new<'l>(database: &'l Database, sql: &str) -> Result<Statement<'l>> { pub fn new<'l>(raw1: *mut raw::sqlite3, sql: &str) -> Result<Statement<'l>> {
let mut raw = 0 as *mut _; let mut raw0 = 0 as *mut _;
unsafe { unsafe {
success!(database, raw::sqlite3_prepare(::database::as_raw(database), str_to_c_str!(sql), success!(raw1, raw::sqlite3_prepare(raw1, str_to_c_str!(sql), -1, &mut raw0, 0 as *mut _));
-1, &mut raw, 0 as *mut _));
} }
Ok(Statement { raw: raw, phantom: PhantomData }) Ok(Statement { raw: (raw0, raw1), phantom: PhantomData })
} }

View File

@ -11,7 +11,7 @@ macro_rules! ok(
#[test] #[test]
fn workflow() { fn workflow() {
use sqlite::Binding::*; use sqlite::Binding::*;
use sqlite::ResultCode; use sqlite::State;
macro_rules! pair( macro_rules! pair(
($one:expr, $two:expr) => ((String::from($one), String::from($two))); ($one:expr, $two:expr) => ((String::from($one), String::from($two)));
@ -27,7 +27,7 @@ fn workflow() {
let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#; let sql = r#"INSERT INTO `users` (id, name, age) VALUES (?, ?, ?);"#;
let mut statement = ok!(database.prepare(sql)); let mut statement = ok!(database.prepare(sql));
ok!(statement.bind(&[Integer(1, 1), Text(2, "Alice"), Float(3, 20.99)])); ok!(statement.bind(&[Integer(1, 1), Text(2, "Alice"), Float(3, 20.99)]));
assert!(statement.step() == ResultCode::Done); assert!(ok!(statement.step()) == State::Done);
} }
{ {
@ -47,11 +47,11 @@ fn workflow() {
{ {
let sql = r#"SELECT * FROM `users`;"#; let sql = r#"SELECT * FROM `users`;"#;
let mut statement = ok!(database.prepare(sql)); let mut statement = ok!(database.prepare(sql));
assert!(statement.step() == ResultCode::Row); assert!(ok!(statement.step()) == State::Row);
assert!(ok!(statement.column::<i64>(0)) == 1); assert!(ok!(statement.column::<i64>(0)) == 1);
assert!(ok!(statement.column::<String>(1)) == String::from("Alice")); assert!(ok!(statement.column::<String>(1)) == String::from("Alice"));
assert!(ok!(statement.column::<f64>(2)) == 20.99); assert!(ok!(statement.column::<f64>(2)) == 20.99);
assert!(statement.step() == ResultCode::Done); assert!(ok!(statement.step()) == State::Done);
} }
} }