Fix tests and improve InvalidColumnType error message.

This commit is contained in:
gwenn 2016-05-18 21:25:13 +02:00
parent 29373e7d0d
commit 42d95f042f
8 changed files with 63 additions and 38 deletions

View File

@ -54,7 +54,7 @@ pub enum Error {
/// Error when the value of a particular column is requested, but the type of the result in /// Error when the value of a particular column is requested, but the type of the result in
/// that column cannot be converted to the requested Rust type. /// that column cannot be converted to the requested Rust type.
InvalidColumnType, InvalidColumnType(c_int, c_int),
/// Error when a query that was expected to insert one row did not insert any or insert many. /// Error when a query that was expected to insert one row did not insert any or insert many.
StatementChangedRows(c_int), StatementChangedRows(c_int),
@ -114,7 +114,7 @@ impl fmt::Display for Error {
Error::GetFromStaleRow => write!(f, "Attempted to get a value from a stale row"), Error::GetFromStaleRow => write!(f, "Attempted to get a value from a stale row"),
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i), Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i),
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name), Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name),
Error::InvalidColumnType => write!(f, "Invalid column type"), Error::InvalidColumnType(i, t) => write!(f, "Invalid column type {} at index: {}", t, i),
Error::StatementChangedRows(i) => write!(f, "Query changed {} rows", i), Error::StatementChangedRows(i) => write!(f, "Query changed {} rows", i),
Error::StatementFailedToInsertRow => write!(f, "Statement failed to insert new row"), Error::StatementFailedToInsertRow => write!(f, "Statement failed to insert new row"),
@ -148,7 +148,7 @@ impl error::Error for Error {
Error::GetFromStaleRow => "attempted to get a value from a stale row", Error::GetFromStaleRow => "attempted to get a value from a stale row",
Error::InvalidColumnIndex(_) => "invalid column index", Error::InvalidColumnIndex(_) => "invalid column index",
Error::InvalidColumnName(_) => "invalid column name", Error::InvalidColumnName(_) => "invalid column name",
Error::InvalidColumnType => "invalid column type", Error::InvalidColumnType(_, _) => "invalid column type",
Error::StatementChangedRows(_) => "query inserted zero or more than one row", Error::StatementChangedRows(_) => "query inserted zero or more than one row",
Error::StatementFailedToInsertRow => "statement failed to insert new row", Error::StatementFailedToInsertRow => "statement failed to insert new row",
@ -176,7 +176,7 @@ impl error::Error for Error {
Error::GetFromStaleRow | Error::GetFromStaleRow |
Error::InvalidColumnIndex(_) | Error::InvalidColumnIndex(_) |
Error::InvalidColumnName(_) | Error::InvalidColumnName(_) |
Error::InvalidColumnType | Error::InvalidColumnType(_, _) |
Error::InvalidPath(_) => None, Error::InvalidPath(_) => None,
Error::StatementChangedRows(_) => None, Error::StatementChangedRows(_) => None,
Error::StatementFailedToInsertRow => None, Error::StatementFailedToInsertRow => None,

View File

@ -1131,10 +1131,9 @@ impl<'stmt> Row<'stmt> {
unsafe { unsafe {
let idx = try!(idx.idx(self.stmt)); let idx = try!(idx.idx(self.stmt));
if T::column_has_valid_sqlite_type(self.stmt.stmt, idx) { match T::column_has_valid_sqlite_type(self.stmt.stmt, idx) {
FromSql::column_result(self.stmt.stmt, idx) Ok(()) => FromSql::column_result(self.stmt.stmt, idx),
} else { Err(e) => Err(e),
Err(Error::InvalidColumnType)
} }
} }
} }
@ -1456,6 +1455,19 @@ mod test {
} }
} }
#[test]
#[should_panic]
fn test_rows_dropped() {
let db = checked_memory_handle();
db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();
db.execute_batch("INSERT INTO foo(x) VALUES(1)").unwrap();
let mut stmt = db.prepare("SELECT x FROM foo").unwrap();
let row = stmt.query(&[]).unwrap().next().unwrap().unwrap();
assert_eq!(1i32, row.get(0));
}
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::*;
@ -1536,7 +1548,7 @@ mod test {
.collect(); .collect();
match bad_type.unwrap_err() { match bad_type.unwrap_err() {
Error::InvalidColumnType => (), Error::InvalidColumnType(_, _) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }
@ -1596,7 +1608,7 @@ mod test {
.collect(); .collect();
match bad_type.unwrap_err() { match bad_type.unwrap_err() {
CustomError::Sqlite(Error::InvalidColumnType) => (), CustomError::Sqlite(Error::InvalidColumnType(_, _)) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }
@ -1658,7 +1670,7 @@ mod test {
}); });
match bad_type.unwrap_err() { match bad_type.unwrap_err() {
CustomError::Sqlite(Error::InvalidColumnType) => (), CustomError::Sqlite(Error::InvalidColumnType(_, _)) => (),
err => panic!("Unexpected error {}", err), err => panic!("Unexpected error {}", err),
} }

View File

@ -27,7 +27,7 @@ impl FromSql for NaiveDate {
} }
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
String::column_has_valid_sqlite_type(stmt, col) String::column_has_valid_sqlite_type(stmt, col)
} }
} }
@ -55,7 +55,7 @@ impl FromSql for NaiveTime {
} }
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
String::column_has_valid_sqlite_type(stmt, col) String::column_has_valid_sqlite_type(stmt, col)
} }
} }
@ -86,7 +86,7 @@ impl FromSql for NaiveDateTime {
} }
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
String::column_has_valid_sqlite_type(stmt, col) String::column_has_valid_sqlite_type(stmt, col)
} }
} }
@ -118,7 +118,7 @@ impl FromSql for DateTime<UTC> {
} }
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
String::column_has_valid_sqlite_type(stmt, col) String::column_has_valid_sqlite_type(stmt, col)
} }
} }
@ -130,7 +130,7 @@ impl FromSql for DateTime<Local> {
Ok(utc_dt.with_timezone(&Local)) Ok(utc_dt.with_timezone(&Local))
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
DateTime::<UTC>::column_has_valid_sqlite_type(stmt, col) DateTime::<UTC>::column_has_valid_sqlite_type(stmt, col)
} }
} }

View File

@ -83,8 +83,17 @@ pub trait FromSql: Sized {
/// the type reported by SQLite matches a type suitable for Self. This method is used /// the type reported by SQLite matches a type suitable for Self. This method is used
/// by `Row::get_checked` to confirm that the column contains a valid type before /// by `Row::get_checked` to confirm that the column contains a valid type before
/// attempting to retrieve the value. /// attempting to retrieve the value.
unsafe fn column_has_valid_sqlite_type(_: *mut sqlite3_stmt, _: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(_: *mut sqlite3_stmt, _: c_int) -> Result<()> {
true Ok(())
}
}
unsafe fn column_has_expected_typed(stmt: *mut sqlite3_stmt, col: c_int, expected_type: c_int) -> Result<()> {
let actual_type = sqlite3_column_type(stmt, col);
if actual_type == expected_type {
Ok(())
} else {
Err(Error::InvalidColumnType(col, actual_type))
} }
} }
@ -197,8 +206,8 @@ macro_rules! raw_from_impl(
Ok(ffi::$f(stmt, col)) Ok(ffi::$f(stmt, col))
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
sqlite3_column_type(stmt, col) == $c column_has_expected_typed(stmt, col, $c)
} }
} }
) )
@ -216,8 +225,8 @@ impl FromSql for bool {
} }
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
sqlite3_column_type(stmt, col) == ffi::SQLITE_INTEGER column_has_expected_typed(stmt, col, ffi::SQLITE_INTEGER)
} }
} }
@ -233,8 +242,8 @@ impl FromSql for String {
} }
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
sqlite3_column_type(stmt, col) == ffi::SQLITE_TEXT column_has_expected_typed(stmt, col, ffi::SQLITE_TEXT)
} }
} }
@ -253,8 +262,8 @@ impl FromSql for Vec<u8> {
Ok(from_raw_parts(mem::transmute(c_blob), len).to_vec()) Ok(from_raw_parts(mem::transmute(c_blob), len).to_vec())
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
sqlite3_column_type(stmt, col) == ffi::SQLITE_BLOB column_has_expected_typed(stmt, col, ffi::SQLITE_BLOB)
} }
} }
@ -267,10 +276,13 @@ impl<T: FromSql> FromSql for Option<T> {
} }
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
sqlite3_column_type(stmt, col) == ffi::SQLITE_NULL || if sqlite3_column_type(stmt, col) == ffi::SQLITE_NULL {
Ok(())
} else {
T::column_has_valid_sqlite_type(stmt, col) T::column_has_valid_sqlite_type(stmt, col)
} }
}
} }
/// Dynamic type value (http://sqlite.org/datatype3.html) /// Dynamic type value (http://sqlite.org/datatype3.html)
@ -297,7 +309,7 @@ impl FromSql for Value {
ffi::SQLITE_FLOAT => Ok(Value::Real(ffi::sqlite3_column_double(stmt, col))), ffi::SQLITE_FLOAT => Ok(Value::Real(ffi::sqlite3_column_double(stmt, col))),
ffi::SQLITE_NULL => Ok(Value::Null), ffi::SQLITE_NULL => Ok(Value::Null),
ffi::SQLITE_BLOB => FromSql::column_result(stmt, col).map(Value::Blob), ffi::SQLITE_BLOB => FromSql::column_result(stmt, col).map(Value::Blob),
_ => Err(Error::InvalidColumnType), ct => Err(Error::InvalidColumnType(col, ct)),
} }
} }
} }
@ -371,7 +383,7 @@ mod test {
fn test_mismatched_types() { fn test_mismatched_types() {
fn is_invalid_column_type(err: Error) -> bool { fn is_invalid_column_type(err: Error) -> bool {
match err { match err {
Error::InvalidColumnType => true, Error::InvalidColumnType(_, _) => true,
_ => false, _ => false,
} }
} }

View File

@ -31,7 +31,7 @@ impl FromSql for Value {
let blob = try!(Vec::<u8>::column_result(stmt, col)); let blob = try!(Vec::<u8>::column_result(stmt, col));
serde_json::from_slice(&blob) serde_json::from_slice(&blob)
} }
_ => return Err(Error::InvalidColumnType), ct => return Err(Error::InvalidColumnType(col, ct)),
}; };
value_result.map_err(|err| Error::FromSqlConversionFailure(Box::new(err))) value_result.map_err(|err| Error::FromSqlConversionFailure(Box::new(err)))
} }

View File

@ -24,7 +24,7 @@ impl FromSql for time::Timespec {
} }
} }
unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> {
String::column_has_valid_sqlite_type(stmt, col) String::column_has_valid_sqlite_type(stmt, col)
} }
} }

View File

@ -267,7 +267,8 @@ mod test {
WHERE v1.rowid < v2.rowid") WHERE v1.rowid < v2.rowid")
.unwrap(); .unwrap();
let row = s.query(&[]).unwrap().next().unwrap().unwrap(); let mut rows = s.query(&[]).unwrap();
let row = rows.next().unwrap().unwrap();
assert_eq!(row.get::<i32, i32>(0), 2); assert_eq!(row.get::<i32, i32>(0), 2);
} }
db.execute_batch("DROP TABLE vtab").unwrap(); db.execute_batch("DROP TABLE vtab").unwrap();

View File

@ -173,7 +173,6 @@ mod test {
} }
} }
s.reset_if_needed();
p1.borrow_mut().clear(); p1.borrow_mut().clear();
p2.borrow_mut().clear(); p2.borrow_mut().clear();
p3.borrow_mut().clear(); p3.borrow_mut().clear();
@ -182,18 +181,19 @@ mod test {
p3.borrow_mut().append(&mut vec![-5, -10]); p3.borrow_mut().append(&mut vec![-5, -10]);
{ {
let row = s.query(&[]).unwrap().next().unwrap().unwrap(); let mut rows = s.query(&[]).unwrap();
let row = rows.next().unwrap().unwrap();
assert_eq!(1, row.get(0)); assert_eq!(1, row.get(0));
assert_eq!(11, row.get(1)); assert_eq!(11, row.get(1));
assert_eq!(-5, row.get(2)); assert_eq!(-5, row.get(2));
} }
s.reset_if_needed();
p2.borrow_mut().clear(); p2.borrow_mut().clear();
p3.borrow_mut().clear(); p3.borrow_mut().clear();
p2.borrow_mut().append(&mut vec![3, 4, 5]); p2.borrow_mut().append(&mut vec![3, 4, 5]);
p3.borrow_mut().append(&mut vec![0, -5]); p3.borrow_mut().append(&mut vec![0, -5]);
assert!(s.query(&[]).unwrap().next().is_none()); let mut rows = s.query(&[]).unwrap();
assert!(rows.next().is_none());
int_array::drop_int_array(&db, "ex1").unwrap(); int_array::drop_int_array(&db, "ex1").unwrap();
int_array::drop_int_array(&db, "ex2").unwrap(); int_array::drop_int_array(&db, "ex2").unwrap();