diff --git a/src/error.rs b/src/error.rs index 700aea4..f9866ed 100644 --- a/src/error.rs +++ b/src/error.rs @@ -54,7 +54,7 @@ pub enum Error { /// 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. - InvalidColumnType, + InvalidColumnType(c_int, c_int), /// Error when a query that was expected to insert one row did not insert any or insert many. 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::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i), 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::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::InvalidColumnIndex(_) => "invalid column index", 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::StatementFailedToInsertRow => "statement failed to insert new row", @@ -176,7 +176,7 @@ impl error::Error for Error { Error::GetFromStaleRow | Error::InvalidColumnIndex(_) | Error::InvalidColumnName(_) | - Error::InvalidColumnType | + Error::InvalidColumnType(_, _) | Error::InvalidPath(_) => None, Error::StatementChangedRows(_) => None, Error::StatementFailedToInsertRow => None, diff --git a/src/lib.rs b/src/lib.rs index be87e6a..b6ad0ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1131,10 +1131,9 @@ impl<'stmt> Row<'stmt> { unsafe { let idx = try!(idx.idx(self.stmt)); - if T::column_has_valid_sqlite_type(self.stmt.stmt, idx) { - FromSql::column_result(self.stmt.stmt, idx) - } else { - Err(Error::InvalidColumnType) + match T::column_has_valid_sqlite_type(self.stmt.stmt, idx) { + Ok(()) => FromSql::column_result(self.stmt.stmt, idx), + Err(e) => Err(e), } } } @@ -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 { extern crate libsqlite3_sys as ffi; use super::*; @@ -1536,7 +1548,7 @@ mod test { .collect(); match bad_type.unwrap_err() { - Error::InvalidColumnType => (), + Error::InvalidColumnType(_, _) => (), err => panic!("Unexpected error {}", err), } @@ -1596,7 +1608,7 @@ mod test { .collect(); match bad_type.unwrap_err() { - CustomError::Sqlite(Error::InvalidColumnType) => (), + CustomError::Sqlite(Error::InvalidColumnType(_, _)) => (), err => panic!("Unexpected error {}", err), } @@ -1658,7 +1670,7 @@ mod test { }); match bad_type.unwrap_err() { - CustomError::Sqlite(Error::InvalidColumnType) => (), + CustomError::Sqlite(Error::InvalidColumnType(_, _)) => (), err => panic!("Unexpected error {}", err), } diff --git a/src/types/chrono.rs b/src/types/chrono.rs index a9e0bea..76201d7 100644 --- a/src/types/chrono.rs +++ b/src/types/chrono.rs @@ -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) } } @@ -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) } } @@ -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) } } @@ -118,7 +118,7 @@ impl FromSql for DateTime { } } - 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) } } @@ -130,7 +130,7 @@ impl FromSql for DateTime { 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::::column_has_valid_sqlite_type(stmt, col) } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 58b4dbd..edc9c1b 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -83,8 +83,17 @@ pub trait FromSql: Sized { /// 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 /// attempting to retrieve the value. - unsafe fn column_has_valid_sqlite_type(_: *mut sqlite3_stmt, _: c_int) -> bool { - true + unsafe fn column_has_valid_sqlite_type(_: *mut sqlite3_stmt, _: c_int) -> Result<()> { + 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)) } - unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { - sqlite3_column_type(stmt, col) == $c + unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> { + 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 { - sqlite3_column_type(stmt, col) == ffi::SQLITE_INTEGER + unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> { + 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 { - sqlite3_column_type(stmt, col) == ffi::SQLITE_TEXT + unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> { + column_has_expected_typed(stmt, col, ffi::SQLITE_TEXT) } } @@ -253,8 +262,8 @@ impl FromSql for 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 { - sqlite3_column_type(stmt, col) == ffi::SQLITE_BLOB + unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> { + column_has_expected_typed(stmt, col, ffi::SQLITE_BLOB) } } @@ -267,9 +276,12 @@ impl FromSql for Option { } } - unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> bool { - sqlite3_column_type(stmt, col) == ffi::SQLITE_NULL || - T::column_has_valid_sqlite_type(stmt, col) + unsafe fn column_has_valid_sqlite_type(stmt: *mut sqlite3_stmt, col: c_int) -> Result<()> { + if sqlite3_column_type(stmt, col) == ffi::SQLITE_NULL { + Ok(()) + } else { + T::column_has_valid_sqlite_type(stmt, col) + } } } @@ -297,7 +309,7 @@ impl FromSql for Value { ffi::SQLITE_FLOAT => Ok(Value::Real(ffi::sqlite3_column_double(stmt, col))), ffi::SQLITE_NULL => Ok(Value::Null), 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 is_invalid_column_type(err: Error) -> bool { match err { - Error::InvalidColumnType => true, + Error::InvalidColumnType(_, _) => true, _ => false, } } diff --git a/src/types/serde_json.rs b/src/types/serde_json.rs index dc1eeff..f4552af 100644 --- a/src/types/serde_json.rs +++ b/src/types/serde_json.rs @@ -31,7 +31,7 @@ impl FromSql for Value { let blob = try!(Vec::::column_result(stmt, col)); 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))) } diff --git a/src/types/time.rs b/src/types/time.rs index fe77956..bc3ba20 100644 --- a/src/types/time.rs +++ b/src/types/time.rs @@ -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) } } diff --git a/src/vtab/csvtab.rs b/src/vtab/csvtab.rs index 4b887aa..7de0059 100644 --- a/src/vtab/csvtab.rs +++ b/src/vtab/csvtab.rs @@ -267,7 +267,8 @@ mod test { WHERE v1.rowid < v2.rowid") .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::(0), 2); } db.execute_batch("DROP TABLE vtab").unwrap(); diff --git a/src/vtab/int_array.rs b/src/vtab/int_array.rs index 79c44f9..9929426 100644 --- a/src/vtab/int_array.rs +++ b/src/vtab/int_array.rs @@ -173,7 +173,6 @@ mod test { } } - s.reset_if_needed(); p1.borrow_mut().clear(); p2.borrow_mut().clear(); p3.borrow_mut().clear(); @@ -182,18 +181,19 @@ mod test { 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!(11, row.get(1)); assert_eq!(-5, row.get(2)); } - s.reset_if_needed(); p2.borrow_mut().clear(); p3.borrow_mut().clear(); p2.borrow_mut().append(&mut vec![3, 4, 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, "ex2").unwrap();