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]
pub fn execute(&self, sql: &str) -> Result<()> {
unsafe {
success!(self, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), None, 0 as *mut _,
0 as *mut _));
success!(self.raw, raw::sqlite3_exec(self.raw, str_to_c_str!(sql), None, 0 as *mut _,
0 as *mut _));
}
Ok(())
}
@ -46,10 +46,10 @@ impl<'l> Database<'l> {
{
unsafe {
let callback = Box::new(callback);
success!(self, raw::sqlite3_exec(self.raw, str_to_c_str!(sql),
Some(execute_callback::<F>),
&*callback as *const _ as *mut _ as *mut _,
0 as *mut _));
success!(self.raw, raw::sqlite3_exec(self.raw, str_to_c_str!(sql),
Some(execute_callback::<F>),
&*callback as *const _ as *mut _ as *mut _,
0 as *mut _));
}
Ok(())
}
@ -57,7 +57,7 @@ impl<'l> Database<'l> {
/// Create a prepared statement.
#[inline]
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.
@ -74,7 +74,7 @@ impl<'l> Database<'l> {
let result = raw::sqlite3_busy_handler(self.raw, Some(busy_callback::<F>),
&*callback as *const _ as *mut _ as *mut _);
self.busy_callback = Some(callback);
success!(result);
success!(self.raw, result);
}
Ok(())
}
@ -83,7 +83,7 @@ impl<'l> Database<'l> {
/// 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)) };
unsafe { success!(self.raw, raw::sqlite3_busy_timeout(self.raw, milliseconds as c_int)) };
Ok(())
}
@ -91,7 +91,7 @@ impl<'l> Database<'l> {
#[inline]
pub fn remove_busy_handler(&mut self) -> Result<()> {
::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(())
}
}
@ -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
where F: FnMut(usize) -> bool
{

View File

@ -2,7 +2,7 @@ use raw;
use std::convert::{From, Into};
use std::fmt::{self, Display, Formatter};
use {Database, ResultCode};
use ResultCode;
/// An error.
#[derive(Debug)]
@ -11,26 +11,6 @@ pub struct Error {
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> {
#[inline]
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)));
);
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(
($database:expr, $result:expr) => (
match $result {
::raw::SQLITE_OK => {},
code => match ::Error::last($database) {
Some(error) => return Err(error),
None => return Err(::Error::from(::result::code_from_raw(code))),
},
code => failure!($database, code),
}
);
($result:expr) => (
@ -64,7 +70,7 @@ mod statement;
pub use error::Error;
pub use database::Database;
pub use result::{Result, ResultCode};
pub use statement::{Statement, Binding, Value};
pub use statement::{Statement, Binding, Value, State};
/// Open a database.
#[inline]

View File

@ -2,12 +2,12 @@ use libc::{c_double, c_int};
use raw;
use std::marker::PhantomData;
use {Database, Result, ResultCode};
use Result;
/// A prepared statement.
pub struct Statement<'l> {
raw: *mut raw::sqlite3_stmt,
phantom: PhantomData<(&'l raw::sqlite3, raw::sqlite3_stmt)>,
raw: (*mut raw::sqlite3_stmt, *mut raw::sqlite3),
phantom: PhantomData<(raw::sqlite3_stmt, &'l raw::sqlite3)>,
}
/// A binding of a parameter of a prepared statement.
@ -23,6 +23,13 @@ pub trait Value {
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> {
/// Bind values to the parameters.
///
@ -32,17 +39,18 @@ impl<'l> Statement<'l> {
match *binding {
Binding::Float(i, value) => unsafe {
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 {
debug_assert!(i > 0, "the indexing starts from 1");
success!(raw::sqlite3_bind_int64(self.raw, i as c_int,
value as raw::sqlite3_int64));
success!(self.raw.1, raw::sqlite3_bind_int64(self.raw.0, i as c_int,
value as raw::sqlite3_int64));
},
Binding::Text(i, value) => unsafe {
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,
None));
success!(self.raw.1, raw::sqlite3_bind_text(self.raw.0, i as c_int,
str_to_c_str!(value), -1, None));
},
}
}
@ -58,15 +66,18 @@ impl<'l> Statement<'l> {
}
/// Evaluate the statement.
#[inline]
pub fn step(&mut self) -> ResultCode {
unsafe { ::result::code_from_raw(raw::sqlite3_step(self.raw)) }
pub fn step(&mut self) -> Result<State> {
match unsafe { raw::sqlite3_step(self.raw.0) } {
raw::SQLITE_DONE => Ok(State::Done),
raw::SQLITE_ROW => Ok(State::Row),
code => failure!(self.raw.1, code),
}
}
/// Reset the statement.
#[inline]
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(())
}
}
@ -74,26 +85,26 @@ impl<'l> Statement<'l> {
impl<'l> Drop for Statement<'l> {
#[inline]
fn drop(&mut self) {
unsafe { ::raw::sqlite3_finalize(self.raw) };
unsafe { raw::sqlite3_finalize(self.raw.0) };
}
}
impl Value for 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 {
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 {
fn read(statement: &Statement, i: usize) -> Result<String> {
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() {
raise!("cannot read a TEXT column");
}
@ -103,11 +114,10 @@ impl Value for String {
}
#[inline]
pub fn new<'l>(database: &'l Database, sql: &str) -> Result<Statement<'l>> {
let mut raw = 0 as *mut _;
pub fn new<'l>(raw1: *mut raw::sqlite3, sql: &str) -> Result<Statement<'l>> {
let mut raw0 = 0 as *mut _;
unsafe {
success!(database, raw::sqlite3_prepare(::database::as_raw(database), str_to_c_str!(sql),
-1, &mut raw, 0 as *mut _));
success!(raw1, raw::sqlite3_prepare(raw1, str_to_c_str!(sql), -1, &mut raw0, 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]
fn workflow() {
use sqlite::Binding::*;
use sqlite::ResultCode;
use sqlite::State;
macro_rules! pair(
($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 mut statement = ok!(database.prepare(sql));
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 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::<String>(1)) == String::from("Alice"));
assert!(ok!(statement.column::<f64>(2)) == 20.99);
assert!(statement.step() == ResultCode::Done);
assert!(ok!(statement.step()) == State::Done);
}
}