mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 00:39:20 +08:00
Change Error
from a struct to an enum (BREAKING CHANGE).
This allows us to separate out the underlying SQLite error codes from errors that occur on the Rust side.
This commit is contained in:
parent
b385ae002b
commit
aac4d59fcc
@ -1,4 +1,5 @@
|
|||||||
use libc::c_int;
|
use libc::c_int;
|
||||||
|
use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
@ -77,6 +78,12 @@ impl fmt::Display for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl error::Error for Error {
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
code_to_str(self.extended_code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Result codes.
|
// Result codes.
|
||||||
|
|
||||||
pub const SQLITE_OK : c_int = 0;
|
pub const SQLITE_OK : c_int = 0;
|
||||||
|
@ -244,12 +244,7 @@ impl<'a, 'b> Backup<'a, 'b> {
|
|||||||
ffi::SQLITE_OK => Ok(More),
|
ffi::SQLITE_OK => Ok(More),
|
||||||
ffi::SQLITE_BUSY => Ok(Busy),
|
ffi::SQLITE_BUSY => Ok(Busy),
|
||||||
ffi::SQLITE_LOCKED => Ok(Locked),
|
ffi::SQLITE_LOCKED => Ok(Locked),
|
||||||
rc => {
|
_ => Err(Error::from_sqlite_code(rc, None)),
|
||||||
Err(Error {
|
|
||||||
code: rc,
|
|
||||||
message: ffi::code_to_str(rc).into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,11 +26,10 @@
|
|||||||
//! match entry {
|
//! match entry {
|
||||||
//! Occupied(occ) => occ.into_mut(),
|
//! Occupied(occ) => occ.into_mut(),
|
||||||
//! Vacant(vac) => {
|
//! Vacant(vac) => {
|
||||||
//! let r = try!(Regex::new(®ex_s).map_err(|e| Error {
|
//! match Regex::new(®ex_s) {
|
||||||
//! code: libsqlite3_sys::SQLITE_ERROR,
|
//! Ok(r) => vac.insert(r),
|
||||||
//! message: format!("Invalid regular expression: {}", e),
|
//! Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
|
||||||
//! }));
|
//! }
|
||||||
//! vac.insert(r)
|
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//! };
|
//! };
|
||||||
@ -50,6 +49,7 @@
|
|||||||
//! assert!(is_match);
|
//! assert!(is_match);
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
|
use std::error::Error as StdError;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
@ -225,14 +225,8 @@ impl FromValue for String {
|
|||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
} else {
|
} else {
|
||||||
let c_slice = CStr::from_ptr(c_text as *const c_char).to_bytes();
|
let c_slice = CStr::from_ptr(c_text as *const c_char).to_bytes();
|
||||||
let utf8_str = str::from_utf8(c_slice);
|
let utf8_str = try!(str::from_utf8(c_slice));
|
||||||
utf8_str.map(|s| s.to_string())
|
Ok(utf8_str.into())
|
||||||
.map_err(|e| {
|
|
||||||
Error {
|
|
||||||
code: 0,
|
|
||||||
message: e.to_string(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,10 +296,7 @@ impl<'a> Context<'a> {
|
|||||||
if T::parameter_has_valid_sqlite_type(arg) {
|
if T::parameter_has_valid_sqlite_type(arg) {
|
||||||
T::parameter_value(arg)
|
T::parameter_value(arg)
|
||||||
} else {
|
} else {
|
||||||
Err(Error {
|
Err(Error::InvalidFunctionParameterType)
|
||||||
code: ffi::SQLITE_MISMATCH,
|
|
||||||
message: "Invalid value type".to_string(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,9 +412,15 @@ impl InnerConnection {
|
|||||||
assert!(!boxed_f.is_null(), "Internal error - null function pointer");
|
assert!(!boxed_f.is_null(), "Internal error - null function pointer");
|
||||||
match (*boxed_f)(&ctx) {
|
match (*boxed_f)(&ctx) {
|
||||||
Ok(r) => r.set_result(ctx.ctx),
|
Ok(r) => r.set_result(ctx.ctx),
|
||||||
Err(e) => {
|
Err(Error::SqliteFailure(err, s)) => {
|
||||||
ffi::sqlite3_result_error_code(ctx.ctx, e.code);
|
ffi::sqlite3_result_error_code(ctx.ctx, err.extended_code);
|
||||||
if let Ok(cstr) = str_to_cstring(&e.message) {
|
if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
|
||||||
|
ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
ffi::sqlite3_result_error_code(ctx.ctx, ffi::SQLITE_CONSTRAINT_FUNCTION);
|
||||||
|
if let Ok(cstr) = str_to_cstring(err.description()) {
|
||||||
ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1);
|
ffi::sqlite3_result_error(ctx.ctx, cstr.as_ptr(), -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -476,7 +473,6 @@ mod test {
|
|||||||
use self::regex::Regex;
|
use self::regex::Regex;
|
||||||
|
|
||||||
use {Connection, Error, Result};
|
use {Connection, Error, Result};
|
||||||
use ffi;
|
|
||||||
use functions::Context;
|
use functions::Context;
|
||||||
|
|
||||||
fn half(ctx: &Context) -> Result<c_double> {
|
fn half(ctx: &Context) -> Result<c_double> {
|
||||||
@ -516,13 +512,10 @@ mod test {
|
|||||||
let new_re = match saved_re {
|
let new_re = match saved_re {
|
||||||
None => {
|
None => {
|
||||||
let s = try!(ctx.get::<String>(0));
|
let s = try!(ctx.get::<String>(0));
|
||||||
let r = try!(Regex::new(&s).map_err(|e| {
|
match Regex::new(&s) {
|
||||||
Error {
|
Ok(r) => Some(r),
|
||||||
code: ffi::SQLITE_ERROR,
|
Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
|
||||||
message: format!("Invalid regular expression: {}", e),
|
}
|
||||||
}
|
|
||||||
}));
|
|
||||||
Some(r)
|
|
||||||
}
|
}
|
||||||
Some(_) => None,
|
Some(_) => None,
|
||||||
};
|
};
|
||||||
@ -591,11 +584,10 @@ mod test {
|
|||||||
match entry {
|
match entry {
|
||||||
Occupied(occ) => occ.into_mut(),
|
Occupied(occ) => occ.into_mut(),
|
||||||
Vacant(vac) => {
|
Vacant(vac) => {
|
||||||
let r = try!(Regex::new(®ex_s).map_err(|e| Error {
|
match Regex::new(®ex_s) {
|
||||||
code: ffi::SQLITE_ERROR,
|
Ok(r) => vac.insert(r),
|
||||||
message: format!("Invalid regular expression: {}", e),
|
Err(err) => return Err(Error::UserFunctionError(Box::new(err))),
|
||||||
}));
|
}
|
||||||
vac.insert(r)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
278
src/lib.rs
278
src/lib.rs
@ -103,59 +103,129 @@ unsafe fn errmsg_to_string(errmsg: *const c_char) -> String {
|
|||||||
/// Old name for `Error`. `SqliteError` is deprecated.
|
/// Old name for `Error`. `SqliteError` is deprecated.
|
||||||
pub type SqliteError = Error;
|
pub type SqliteError = Error;
|
||||||
|
|
||||||
/// Encompasses an error result from a call to the SQLite C API.
|
#[derive(Debug)]
|
||||||
#[derive(Debug, PartialEq)]
|
pub enum Error {
|
||||||
pub struct Error {
|
SqliteFailure(ffi::Error, Option<String>),
|
||||||
/// The error code returned by a SQLite C API call. See [SQLite Result
|
FromSqlConversionFailure(Box<error::Error>),
|
||||||
/// Codes](http://www.sqlite.org/rescode.html) for details.
|
Utf8Error(str::Utf8Error),
|
||||||
pub code: c_int,
|
NulError(std::ffi::NulError),
|
||||||
|
InvalidParameterName(String),
|
||||||
|
InvalidPath(PathBuf),
|
||||||
|
ExecuteReturnedResults,
|
||||||
|
QueryReturnedNoRows,
|
||||||
|
GetFromStaleRow,
|
||||||
|
InvalidColumnIndex(c_int),
|
||||||
|
InvalidColumnType,
|
||||||
|
|
||||||
/// The error message provided by [sqlite3_errmsg](http://www.sqlite.org/c3ref/errcode.html),
|
#[cfg(feature = "functions")]
|
||||||
/// if possible, or a generic error message based on `code` otherwise.
|
InvalidFunctionParameterType,
|
||||||
pub message: String,
|
#[cfg(feature = "functions")]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
UserFunctionError(Box<std::error::Error>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<str::Utf8Error> for Error {
|
||||||
|
fn from(err: str::Utf8Error) -> Error {
|
||||||
|
Error::Utf8Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::ffi::NulError> for Error {
|
||||||
|
fn from(err: std::ffi::NulError) -> Error {
|
||||||
|
Error::NulError(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{} (SQLite error {})", self.message, self.code)
|
match self {
|
||||||
|
&Error::SqliteFailure(ref err, None) => err.fmt(f),
|
||||||
|
&Error::SqliteFailure(_, Some(ref s)) => write!(f, "{}", s),
|
||||||
|
&Error::FromSqlConversionFailure(ref err) => err.fmt(f),
|
||||||
|
&Error::Utf8Error(ref err) => err.fmt(f),
|
||||||
|
&Error::NulError(ref err) => err.fmt(f),
|
||||||
|
&Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name),
|
||||||
|
&Error::InvalidPath(ref p) => write!(f, "Invalid path: {}", p.to_string_lossy()),
|
||||||
|
&Error::ExecuteReturnedResults => write!(f, "Execute returned results - did you mean to call query?"),
|
||||||
|
&Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
|
||||||
|
&Error::GetFromStaleRow => write!(f, "Attempted to get a value from a stale row"),
|
||||||
|
&Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i),
|
||||||
|
&Error::InvalidColumnType => write!(f, "Invalid column type"),
|
||||||
|
|
||||||
|
#[cfg(feature = "functions")]
|
||||||
|
&Error::InvalidFunctionParameterType => write!(f, "Invalid function parameter type"),
|
||||||
|
#[cfg(feature = "functions")]
|
||||||
|
&Error::UserFunctionError(ref err) => err.fmt(f),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for Error {
|
impl error::Error for Error {
|
||||||
fn description(&self) -> &str {
|
fn description(&self) -> &str {
|
||||||
&self.message
|
match self {
|
||||||
|
&Error::SqliteFailure(ref err, None) => err.description(),
|
||||||
|
&Error::SqliteFailure(_, Some(ref s)) => s,
|
||||||
|
&Error::FromSqlConversionFailure(ref err) => err.description(),
|
||||||
|
&Error::Utf8Error(ref err) => err.description(),
|
||||||
|
&Error::InvalidParameterName(_) => "invalid parameter name",
|
||||||
|
&Error::NulError(ref err) => err.description(),
|
||||||
|
&Error::InvalidPath(_) => "invalid path",
|
||||||
|
&Error::ExecuteReturnedResults => "execute returned results - did you mean to call query?",
|
||||||
|
&Error::QueryReturnedNoRows => "query returned no rows",
|
||||||
|
&Error::GetFromStaleRow => "attempted to get a value from a stale row",
|
||||||
|
&Error::InvalidColumnIndex(_) => "invalid column index",
|
||||||
|
&Error::InvalidColumnType => "invalid column type",
|
||||||
|
|
||||||
|
#[cfg(feature = "functions")]
|
||||||
|
&Error::InvalidFunctionParameterType => "invalid function parameter type",
|
||||||
|
#[cfg(feature = "functions")]
|
||||||
|
&Error::UserFunctionError(ref err) => err.description(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&error::Error> {
|
||||||
|
match self {
|
||||||
|
&Error::SqliteFailure(ref err, _) => Some(err),
|
||||||
|
&Error::FromSqlConversionFailure(ref err) => Some(&**err),
|
||||||
|
&Error::Utf8Error(ref err) => Some(err),
|
||||||
|
&Error::NulError(ref err) => Some(err),
|
||||||
|
&Error::InvalidParameterName(_) => None,
|
||||||
|
&Error::InvalidPath(_) => None,
|
||||||
|
&Error::ExecuteReturnedResults => None,
|
||||||
|
&Error::QueryReturnedNoRows => None,
|
||||||
|
&Error::GetFromStaleRow => None,
|
||||||
|
&Error::InvalidColumnIndex(_) => None,
|
||||||
|
&Error::InvalidColumnType => None,
|
||||||
|
|
||||||
|
#[cfg(feature = "functions")]
|
||||||
|
&Error::InvalidFunctionParameterType => None,
|
||||||
|
#[cfg(feature = "functions")]
|
||||||
|
&Error::UserFunctionError(ref err) => Some(&**err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
fn from_sqlite_code(code: c_int, message: Option<String>) -> Error {
|
||||||
|
Error::SqliteFailure(ffi::Error::new(code), message)
|
||||||
|
}
|
||||||
|
|
||||||
fn from_handle(db: *mut ffi::Struct_sqlite3, code: c_int) -> Error {
|
fn from_handle(db: *mut ffi::Struct_sqlite3, code: c_int) -> Error {
|
||||||
let message = if db.is_null() {
|
let message = if db.is_null() {
|
||||||
ffi::code_to_str(code).to_string()
|
None
|
||||||
} else {
|
} else {
|
||||||
unsafe { errmsg_to_string(ffi::sqlite3_errmsg(db)) }
|
Some(unsafe { errmsg_to_string(ffi::sqlite3_errmsg(db)) })
|
||||||
};
|
};
|
||||||
Error {
|
Error::from_sqlite_code(code, message)
|
||||||
code: code,
|
|
||||||
message: message,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn str_to_cstring(s: &str) -> Result<CString> {
|
fn str_to_cstring(s: &str) -> Result<CString> {
|
||||||
CString::new(s).map_err(|_| {
|
Ok(try!(CString::new(s)))
|
||||||
Error {
|
|
||||||
code: ffi::SQLITE_MISUSE,
|
|
||||||
message: format!("Could not convert string {} to C-combatible string", s),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_to_cstring(p: &Path) -> Result<CString> {
|
fn path_to_cstring(p: &Path) -> Result<CString> {
|
||||||
let s = try!(p.to_str().ok_or(Error {
|
let s = try!(p.to_str().ok_or(Error::InvalidPath(p.to_owned())));
|
||||||
code: ffi::SQLITE_MISUSE,
|
|
||||||
message: format!("Could not convert path {} to UTF-8 string",
|
|
||||||
p.to_string_lossy()),
|
|
||||||
}));
|
|
||||||
str_to_cstring(s)
|
str_to_cstring(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,10 +668,7 @@ impl InnerConnection {
|
|||||||
let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null());
|
let r = ffi::sqlite3_open_v2(c_path.as_ptr(), &mut db, flags.bits(), ptr::null());
|
||||||
if r != ffi::SQLITE_OK {
|
if r != ffi::SQLITE_OK {
|
||||||
let e = if db.is_null() {
|
let e = if db.is_null() {
|
||||||
Error {
|
Error::from_sqlite_code(r, None)
|
||||||
code: r,
|
|
||||||
message: ffi::code_to_str(r).to_string(),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
let e = Error::from_handle(db, r);
|
let e = Error::from_handle(db, r);
|
||||||
ffi::sqlite3_close(db);
|
ffi::sqlite3_close(db);
|
||||||
@ -681,10 +748,7 @@ impl InnerConnection {
|
|||||||
} else {
|
} else {
|
||||||
let message = errmsg_to_string(&*errmsg);
|
let message = errmsg_to_string(&*errmsg);
|
||||||
ffi::sqlite3_free(errmsg as *mut libc::c_void);
|
ffi::sqlite3_free(errmsg as *mut libc::c_void);
|
||||||
Err(Error {
|
Err(Error::from_sqlite_code(r, Some(message)))
|
||||||
code: r,
|
|
||||||
message: message,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -698,10 +762,7 @@ impl InnerConnection {
|
|||||||
sql: &str)
|
sql: &str)
|
||||||
-> Result<Statement<'a>> {
|
-> Result<Statement<'a>> {
|
||||||
if sql.len() >= ::std::i32::MAX as usize {
|
if sql.len() >= ::std::i32::MAX as usize {
|
||||||
return Err(Error {
|
return Err(Error::from_sqlite_code(ffi::SQLITE_TOOBIG, None));
|
||||||
code: ffi::SQLITE_TOOBIG,
|
|
||||||
message: "statement too long".to_string(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
let mut c_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() };
|
let mut c_stmt: *mut ffi::sqlite3_stmt = unsafe { mem::uninitialized() };
|
||||||
let c_sql = try!(str_to_cstring(sql));
|
let c_sql = try!(str_to_cstring(sql));
|
||||||
@ -797,21 +858,12 @@ impl<'conn> Statement<'conn> {
|
|||||||
match r {
|
match r {
|
||||||
ffi::SQLITE_DONE => {
|
ffi::SQLITE_DONE => {
|
||||||
if self.column_count != 0 {
|
if self.column_count != 0 {
|
||||||
Err(Error {
|
Err(Error::ExecuteReturnedResults)
|
||||||
code: ffi::SQLITE_MISUSE,
|
|
||||||
message: "Unexpected column count - did you mean to call query?"
|
|
||||||
.to_string(),
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Ok(self.conn.changes())
|
Ok(self.conn.changes())
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
ffi::SQLITE_ROW => {
|
ffi::SQLITE_ROW => Err(Error::ExecuteReturnedResults),
|
||||||
Err(Error {
|
|
||||||
code: r,
|
|
||||||
message: "Unexpected row result - did you mean to call query?".to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => Err(self.conn.decode_result(r).unwrap_err()),
|
_ => Err(self.conn.decode_result(r).unwrap_err()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1052,12 +1104,7 @@ impl<'stmt> Rows<'stmt> {
|
|||||||
fn get_expected_row(&mut self) -> Result<Row<'stmt>> {
|
fn get_expected_row(&mut self) -> Result<Row<'stmt>> {
|
||||||
match self.next() {
|
match self.next() {
|
||||||
Some(row) => row,
|
Some(row) => row,
|
||||||
None => {
|
None => Err(Error::QueryReturnedNoRows),
|
||||||
Err(Error {
|
|
||||||
code: ffi::SQLITE_NOTICE,
|
|
||||||
message: "Query did not return a row".to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1142,26 +1189,17 @@ impl<'stmt> Row<'stmt> {
|
|||||||
/// for this row or if this row is stale.
|
/// for this row or if this row is stale.
|
||||||
pub fn get_checked<T: FromSql>(&self, idx: c_int) -> Result<T> {
|
pub fn get_checked<T: FromSql>(&self, idx: c_int) -> Result<T> {
|
||||||
if self.row_idx != self.current_row.get() {
|
if self.row_idx != self.current_row.get() {
|
||||||
return Err(Error {
|
return Err(Error::GetFromStaleRow);
|
||||||
code: ffi::SQLITE_MISUSE,
|
|
||||||
message: "Cannot get values from a row after advancing to next row".to_string(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
if idx < 0 || idx >= self.stmt.column_count {
|
if idx < 0 || idx >= self.stmt.column_count {
|
||||||
return Err(Error {
|
return Err(Error::InvalidColumnIndex(idx));
|
||||||
code: ffi::SQLITE_MISUSE,
|
|
||||||
message: "Invalid column index".to_string(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if T::column_has_valid_sqlite_type(self.stmt.stmt, idx) {
|
if T::column_has_valid_sqlite_type(self.stmt.stmt, idx) {
|
||||||
FromSql::column_result(self.stmt.stmt, idx)
|
FromSql::column_result(self.stmt.stmt, idx)
|
||||||
} else {
|
} else {
|
||||||
Err(Error {
|
Err(Error::InvalidColumnType)
|
||||||
code: ffi::SQLITE_MISMATCH,
|
|
||||||
message: "Invalid column type".to_string(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1264,8 +1302,10 @@ mod test {
|
|||||||
fn test_execute_select() {
|
fn test_execute_select() {
|
||||||
let db = checked_memory_handle();
|
let db = checked_memory_handle();
|
||||||
let err = db.execute("SELECT 1 WHERE 1 < ?", &[&1i32]).unwrap_err();
|
let err = db.execute("SELECT 1 WHERE 1 < ?", &[&1i32]).unwrap_err();
|
||||||
assert!(err.code == ffi::SQLITE_MISUSE);
|
match err {
|
||||||
assert!(err.message == "Unexpected column count - did you mean to call query?");
|
Error::ExecuteReturnedResults => (),
|
||||||
|
_ => panic!("Unexpected error: {}", err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1364,10 +1404,10 @@ mod test {
|
|||||||
.unwrap());
|
.unwrap());
|
||||||
|
|
||||||
let result = db.query_row("SELECT x FROM foo WHERE x > 5", &[], |r| r.get::<i64>(0));
|
let result = db.query_row("SELECT x FROM foo WHERE x > 5", &[], |r| r.get::<i64>(0));
|
||||||
let error = result.unwrap_err();
|
match result.unwrap_err() {
|
||||||
|
Error::QueryReturnedNoRows => (),
|
||||||
assert!(error.code == ffi::SQLITE_NOTICE);
|
err => panic!("Unexpected error {}", err),
|
||||||
assert!(error.message == "Query did not return a row");
|
}
|
||||||
|
|
||||||
let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", &[], |_| ());
|
let bad_query_result = db.query_row("NOT A PROPER QUERY; test123", &[], |_| ());
|
||||||
|
|
||||||
@ -1380,7 +1420,7 @@ mod test {
|
|||||||
db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap();
|
db.execute_batch("CREATE TABLE foo(x INTEGER);").unwrap();
|
||||||
|
|
||||||
let err = db.prepare("SELECT * FROM does_not_exist").unwrap_err();
|
let err = db.prepare("SELECT * FROM does_not_exist").unwrap_err();
|
||||||
assert!(err.message.contains("does_not_exist"));
|
assert!(format!("{}", err).contains("does_not_exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1397,8 +1437,10 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(2i32, second.get(0));
|
assert_eq!(2i32, second.get(0));
|
||||||
|
|
||||||
let result = first.get_checked::<i32>(0);
|
match first.get_checked::<i32>(0).unwrap_err() {
|
||||||
assert!(result.unwrap_err().message.contains("advancing to next row"));
|
Error::GetFromStaleRow => (),
|
||||||
|
err => panic!("Unexpected error {}", err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1433,15 +1475,17 @@ mod test {
|
|||||||
let result = db.execute("INSERT INTO foo (x) VALUES (NULL)", &[]);
|
let result = db.execute("INSERT INTO foo (x) VALUES (NULL)", &[]);
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
|
|
||||||
let err = result.err().unwrap();
|
match result.unwrap_err() {
|
||||||
assert_eq!(err.code, ffi::SQLITE_CONSTRAINT_NOTNULL);
|
Error::SqliteFailure(err, _) => assert_eq!(err.extended_code, ffi::SQLITE_CONSTRAINT_NOTNULL),
|
||||||
|
err => panic!("Unexpected error {}", err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod query_and_then_tests {
|
mod query_and_then_tests {
|
||||||
extern crate libsqlite3_sys as ffi;
|
extern crate libsqlite3_sys as ffi;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug)]
|
||||||
enum CustomError {
|
enum CustomError {
|
||||||
SomeError,
|
SomeError,
|
||||||
Sqlite(Error),
|
Sqlite(Error),
|
||||||
@ -1512,27 +1556,23 @@ mod test {
|
|||||||
db.execute_batch(sql).unwrap();
|
db.execute_batch(sql).unwrap();
|
||||||
|
|
||||||
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
|
let mut query = db.prepare("SELECT x, y FROM foo ORDER BY x DESC").unwrap();
|
||||||
let bad_type: Result<Vec<f64>> = query.query_and_then(&[],
|
let bad_type: Result<Vec<f64>> = query.query_and_then(&[], |row| row.get_checked(1))
|
||||||
|row| row.get_checked(1))
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
assert_eq!(bad_type,
|
match bad_type.unwrap_err() {
|
||||||
Err(Error {
|
Error::InvalidColumnType => (),
|
||||||
code: ffi::SQLITE_MISMATCH,
|
err => panic!("Unexpected error {}", err),
|
||||||
message: "Invalid column type".to_owned(),
|
}
|
||||||
}));
|
|
||||||
|
|
||||||
let bad_idx: Result<Vec<String>> = query.query_and_then(&[],
|
let bad_idx: Result<Vec<String>> = query.query_and_then(&[], |row| row.get_checked(3))
|
||||||
|row| row.get_checked(3))
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
assert_eq!(bad_idx,
|
match bad_idx.unwrap_err() {
|
||||||
Err(Error {
|
Error::InvalidColumnIndex(_) => (),
|
||||||
code: ffi::SQLITE_MISUSE,
|
err => panic!("Unexpected error {}", err),
|
||||||
message: "Invalid column index".to_owned(),
|
}
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1580,11 +1620,10 @@ mod test {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
assert_eq!(bad_type,
|
match bad_type.unwrap_err() {
|
||||||
Err(CustomError::Sqlite(Error {
|
CustomError::Sqlite(Error::InvalidColumnType) => (),
|
||||||
code: ffi::SQLITE_MISMATCH,
|
err => panic!("Unexpected error {}", err),
|
||||||
message: "Invalid column type".to_owned(),
|
}
|
||||||
})));
|
|
||||||
|
|
||||||
let bad_idx: CustomResult<Vec<String>> = query.query_and_then(&[], |row| {
|
let bad_idx: CustomResult<Vec<String>> = query.query_and_then(&[], |row| {
|
||||||
row.get_checked(3)
|
row.get_checked(3)
|
||||||
@ -1593,11 +1632,10 @@ mod test {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
assert_eq!(bad_idx,
|
match bad_idx.unwrap_err() {
|
||||||
Err(CustomError::Sqlite(Error {
|
CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
|
||||||
code: ffi::SQLITE_MISUSE,
|
err => panic!("Unexpected error {}", err),
|
||||||
message: "Invalid column index".to_owned(),
|
}
|
||||||
})));
|
|
||||||
|
|
||||||
let non_sqlite_err: CustomResult<Vec<String>> = query.query_and_then(&[], |_| {
|
let non_sqlite_err: CustomResult<Vec<String>> = query.query_and_then(&[], |_| {
|
||||||
Err(CustomError::SomeError)
|
Err(CustomError::SomeError)
|
||||||
@ -1605,7 +1643,10 @@ mod test {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
assert_eq!(non_sqlite_err, Err(CustomError::SomeError));
|
match non_sqlite_err.unwrap_err() {
|
||||||
|
CustomError::SomeError => (),
|
||||||
|
err => panic!("Unexpected error {}", err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1641,27 +1682,28 @@ mod test {
|
|||||||
row.get_checked(1).map_err(CustomError::Sqlite)
|
row.get_checked(1).map_err(CustomError::Sqlite)
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(bad_type,
|
match bad_type.unwrap_err() {
|
||||||
Err(CustomError::Sqlite(Error {
|
CustomError::Sqlite(Error::InvalidColumnType) => (),
|
||||||
code: ffi::SQLITE_MISMATCH,
|
err => panic!("Unexpected error {}", err),
|
||||||
message: "Invalid column type".to_owned(),
|
}
|
||||||
})));
|
|
||||||
|
|
||||||
let bad_idx: CustomResult<String> = db.query_row_and_then(query, &[], |row| {
|
let bad_idx: CustomResult<String> = db.query_row_and_then(query, &[], |row| {
|
||||||
row.get_checked(3).map_err(CustomError::Sqlite)
|
row.get_checked(3).map_err(CustomError::Sqlite)
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(bad_idx,
|
match bad_idx.unwrap_err() {
|
||||||
Err(CustomError::Sqlite(Error {
|
CustomError::Sqlite(Error::InvalidColumnIndex(_)) => (),
|
||||||
code: ffi::SQLITE_MISUSE,
|
err => panic!("Unexpected error {}", err),
|
||||||
message: "Invalid column index".to_owned(),
|
}
|
||||||
})));
|
|
||||||
|
|
||||||
let non_sqlite_err: CustomResult<String> = db.query_row_and_then(query, &[], |_| {
|
let non_sqlite_err: CustomResult<String> = db.query_row_and_then(query, &[], |_| {
|
||||||
Err(CustomError::SomeError)
|
Err(CustomError::SomeError)
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(non_sqlite_err, Err(CustomError::SomeError));
|
match non_sqlite_err.unwrap_err() {
|
||||||
|
CustomError::SomeError => (),
|
||||||
|
err => panic!("Unexpected error {}", err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -133,10 +133,7 @@ impl<'conn> Statement<'conn> {
|
|||||||
if let Some(i) = try!(self.parameter_index(name)) {
|
if let Some(i) = try!(self.parameter_index(name)) {
|
||||||
try!(self.conn.decode_result(unsafe { value.bind_parameter(self.stmt, i) }));
|
try!(self.conn.decode_result(unsafe { value.bind_parameter(self.stmt, i) }));
|
||||||
} else {
|
} else {
|
||||||
return Err(Error {
|
return Err(Error::InvalidParameterName(name.into()));
|
||||||
code: ffi::SQLITE_MISUSE,
|
|
||||||
message: format!("Invalid parameter name: {}", name),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
11
src/trace.rs
11
src/trace.rs
@ -42,14 +42,11 @@ pub unsafe fn config_log(callback: Option<fn(c_int, &str)>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if rc != ffi::SQLITE_OK {
|
if rc == ffi::SQLITE_OK {
|
||||||
return Err(Error {
|
Ok(())
|
||||||
code: rc,
|
} else {
|
||||||
message: "sqlite3_config(SQLITE_CONFIG_LOG, ...)".to_string(),
|
Err(Error::from_sqlite_code(rc, None))
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a message into the error log established by `config_log`.
|
/// Write a message into the error log established by `config_log`.
|
||||||
|
116
src/types.rs
116
src/types.rs
@ -232,14 +232,8 @@ impl FromSql for String {
|
|||||||
Ok("".to_string())
|
Ok("".to_string())
|
||||||
} else {
|
} else {
|
||||||
let c_slice = CStr::from_ptr(c_text as *const c_char).to_bytes();
|
let c_slice = CStr::from_ptr(c_text as *const c_char).to_bytes();
|
||||||
let utf8_str = str::from_utf8(c_slice);
|
let utf8_str = try!(str::from_utf8(c_slice));
|
||||||
utf8_str.map(|s| s.to_string())
|
Ok(utf8_str.into())
|
||||||
.map_err(|e| {
|
|
||||||
Error {
|
|
||||||
code: 0,
|
|
||||||
message: e.to_string(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,14 +266,10 @@ impl FromSql for time::Timespec {
|
|||||||
unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result<time::Timespec> {
|
unsafe fn column_result(stmt: *mut sqlite3_stmt, col: c_int) -> Result<time::Timespec> {
|
||||||
let col_str = FromSql::column_result(stmt, col);
|
let col_str = FromSql::column_result(stmt, col);
|
||||||
col_str.and_then(|txt: String| {
|
col_str.and_then(|txt: String| {
|
||||||
time::strptime(&txt, SQLITE_DATETIME_FMT)
|
match time::strptime(&txt, SQLITE_DATETIME_FMT) {
|
||||||
.map(|tm| tm.to_timespec())
|
Ok(tm) => Ok(tm.to_timespec()),
|
||||||
.map_err(|parse_error| {
|
Err(err) => Err(Error::FromSqlConversionFailure(Box::new(err))),
|
||||||
Error {
|
}
|
||||||
code: ffi::SQLITE_MISMATCH,
|
|
||||||
message: format!("{}", parse_error),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,8 +296,8 @@ impl<T: FromSql> FromSql for Option<T> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use Connection;
|
use Connection;
|
||||||
use ffi;
|
|
||||||
use super::time;
|
use super::time;
|
||||||
|
use Error;
|
||||||
use libc::{c_int, c_double};
|
use libc::{c_int, c_double};
|
||||||
|
|
||||||
fn checked_memory_handle() -> Connection {
|
fn checked_memory_handle() -> Connection {
|
||||||
@ -380,6 +370,13 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mismatched_types() {
|
fn test_mismatched_types() {
|
||||||
|
fn is_invalid_column_type(err: Error) -> bool {
|
||||||
|
match err {
|
||||||
|
Error::InvalidColumnType => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let db = checked_memory_handle();
|
let db = checked_memory_handle();
|
||||||
|
|
||||||
db.execute("INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
|
db.execute("INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
|
||||||
@ -403,69 +400,42 @@ mod test {
|
|||||||
// check some invalid types
|
// check some invalid types
|
||||||
|
|
||||||
// 0 is actually a blob (Vec<u8>)
|
// 0 is actually a blob (Vec<u8>)
|
||||||
assert_eq!(row.get_checked::<c_int>(0).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<c_int>(0).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<c_int>(0).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<i64>(0).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<i64>(0).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<c_double>(0).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<c_double>(0).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<String>(0).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<time::Timespec>(0).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<String>(0).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<Option<c_int>>(0).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
assert_eq!(row.get_checked::<time::Timespec>(0).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
assert_eq!(row.get_checked::<Option<c_int>>(0).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
|
|
||||||
// 1 is actually a text (String)
|
// 1 is actually a text (String)
|
||||||
assert_eq!(row.get_checked::<c_int>(1).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<c_int>(1).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<i64>(1).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<i64>(1).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<c_double>(1).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<Vec<u8>>(1).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<c_double>(1).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<Option<c_int>>(1).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
assert_eq!(row.get_checked::<Vec<u8>>(1).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
assert_eq!(row.get_checked::<Option<c_int>>(1).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
|
|
||||||
// 2 is actually an integer
|
// 2 is actually an integer
|
||||||
assert_eq!(row.get_checked::<c_double>(2).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<c_double>(2).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<String>(2).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<String>(2).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<Vec<u8>>(2).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<time::Timespec>(2).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<Vec<u8>>(2).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<Option<c_double>>(2).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
assert_eq!(row.get_checked::<time::Timespec>(2).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
assert_eq!(row.get_checked::<Option<c_double>>(2).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
|
|
||||||
// 3 is actually a float (c_double)
|
// 3 is actually a float (c_double)
|
||||||
assert_eq!(row.get_checked::<c_int>(3).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<c_int>(3).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<i64>(3).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<i64>(3).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<String>(3).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<Vec<u8>>(3).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<String>(3).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<time::Timespec>(3).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<Option<c_int>>(3).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<Vec<u8>>(3).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
assert_eq!(row.get_checked::<time::Timespec>(3).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
assert_eq!(row.get_checked::<Option<c_int>>(3).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
|
|
||||||
// 4 is actually NULL
|
// 4 is actually NULL
|
||||||
assert_eq!(row.get_checked::<c_int>(4).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<c_int>(4).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<i64>(4).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<i64>(4).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<c_double>(4).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<String>(4).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<c_double>(4).err().unwrap().code,
|
assert!(is_invalid_column_type(row.get_checked::<Vec<u8>>(4).err().unwrap()));
|
||||||
ffi::SQLITE_MISMATCH);
|
assert!(is_invalid_column_type(row.get_checked::<time::Timespec>(4).err().unwrap()));
|
||||||
assert_eq!(row.get_checked::<String>(4).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
assert_eq!(row.get_checked::<Vec<u8>>(4).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
assert_eq!(row.get_checked::<time::Timespec>(4).err().unwrap().code,
|
|
||||||
ffi::SQLITE_MISMATCH);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user