mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 00:39:20 +08:00
Include the name of the column in InvalidColumnType errors
This commit is contained in:
parent
454899f612
commit
4356f5a176
@ -27,8 +27,7 @@ impl Statement<'_> {
|
||||
let n = self.column_count();
|
||||
let mut cols = Vec::with_capacity(n as usize);
|
||||
for i in 0..n {
|
||||
let slice = self.stmt.column_name(i);
|
||||
let s = str::from_utf8(slice.to_bytes()).unwrap();
|
||||
let s = self.column_name(i);
|
||||
cols.push(s);
|
||||
}
|
||||
cols
|
||||
@ -40,6 +39,13 @@ impl Statement<'_> {
|
||||
self.stmt.column_count()
|
||||
}
|
||||
|
||||
pub(crate) fn column_name(&self, col: usize) -> &str {
|
||||
// Just panic if the bounds are wrong for now, we never call this
|
||||
// without checking first.
|
||||
let slice = self.stmt.column_name(col).expect("Column out of bounds");
|
||||
str::from_utf8(slice.to_bytes()).unwrap()
|
||||
}
|
||||
|
||||
/// Returns the column index in the result set for a given column name.
|
||||
///
|
||||
/// If there is no AS clause then the name of the column is unspecified and
|
||||
@ -53,7 +59,9 @@ impl Statement<'_> {
|
||||
let bytes = name.as_bytes();
|
||||
let n = self.column_count();
|
||||
for i in 0..n {
|
||||
if bytes.eq_ignore_ascii_case(self.stmt.column_name(i).to_bytes()) {
|
||||
// Note: `column_name` is only fallible if `i` is out of bounds,
|
||||
// which we've already checked.
|
||||
if bytes.eq_ignore_ascii_case(self.stmt.column_name(i).unwrap().to_bytes()) {
|
||||
return Ok(i);
|
||||
}
|
||||
}
|
||||
@ -65,8 +73,7 @@ impl Statement<'_> {
|
||||
let n = self.column_count();
|
||||
let mut cols = Vec::with_capacity(n as usize);
|
||||
for i in 0..n {
|
||||
let slice = self.stmt.column_name(i);
|
||||
let name = str::from_utf8(slice.to_bytes()).unwrap();
|
||||
let name = self.column_name(i);
|
||||
let slice = self.stmt.column_decltype(i);
|
||||
let decl_type = slice.map(|s| str::from_utf8(s.to_bytes()).unwrap());
|
||||
cols.push(Column { name, decl_type });
|
||||
@ -125,4 +132,38 @@ mod test {
|
||||
&[Some("text"), Some("text"), Some("text"),]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_column_name_in_error() {
|
||||
use crate::{types::Type, Error};
|
||||
let db = Connection::open_in_memory().unwrap();
|
||||
db.execute_batch(
|
||||
"BEGIN;
|
||||
CREATE TABLE foo(x INTEGER, y TEXT);
|
||||
INSERT INTO foo VALUES(4, NULL);
|
||||
END;").unwrap();
|
||||
let mut stmt = db.prepare("SELECT x as renamed, y FROM foo").unwrap();
|
||||
let mut rows = stmt.query(crate::NO_PARAMS).unwrap();
|
||||
let row = rows.next().unwrap().unwrap();
|
||||
match row.get::<_, String>(0).unwrap_err() {
|
||||
Error::InvalidColumnType(idx, name, ty) => {
|
||||
assert_eq!(idx, 0);
|
||||
assert_eq!(name, "renamed");
|
||||
assert_eq!(ty, Type::Integer);
|
||||
}
|
||||
e => {
|
||||
panic!("Unexpected error type: {:?}", e);
|
||||
}
|
||||
}
|
||||
match row.get::<_, String>("y").unwrap_err() {
|
||||
Error::InvalidColumnType(idx, name, ty) => {
|
||||
assert_eq!(idx, 1);
|
||||
assert_eq!(name, "y");
|
||||
assert_eq!(ty, Type::Null);
|
||||
}
|
||||
e => {
|
||||
panic!("Unexpected error type: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
src/error.rs
14
src/error.rs
@ -59,7 +59,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(usize, Type),
|
||||
InvalidColumnType(usize, String, Type),
|
||||
|
||||
/// Error when a query that was expected to insert one row did not insert
|
||||
/// any or insert many.
|
||||
@ -117,8 +117,8 @@ impl PartialEq for Error {
|
||||
(Error::QueryReturnedNoRows, Error::QueryReturnedNoRows) => true,
|
||||
(Error::InvalidColumnIndex(i1), Error::InvalidColumnIndex(i2)) => i1 == i2,
|
||||
(Error::InvalidColumnName(n1), Error::InvalidColumnName(n2)) => n1 == n2,
|
||||
(Error::InvalidColumnType(i1, t1), Error::InvalidColumnType(i2, t2)) => {
|
||||
i1 == i2 && t1 == t2
|
||||
(Error::InvalidColumnType(i1, n1, t1), Error::InvalidColumnType(i2, n2, t2)) => {
|
||||
i1 == i2 && t1 == t2 && n1 == n2
|
||||
}
|
||||
(Error::StatementChangedRows(n1), Error::StatementChangedRows(n2)) => n1 == n2,
|
||||
#[cfg(feature = "functions")]
|
||||
@ -182,8 +182,8 @@ impl fmt::Display for Error {
|
||||
Error::QueryReturnedNoRows => write!(f, "Query returned no rows"),
|
||||
Error::InvalidColumnIndex(i) => write!(f, "Invalid column index: {}", i),
|
||||
Error::InvalidColumnName(ref name) => write!(f, "Invalid column name: {}", name),
|
||||
Error::InvalidColumnType(i, ref t) => {
|
||||
write!(f, "Invalid column type {} at index: {}", t, i)
|
||||
Error::InvalidColumnType(i, ref name, ref t) => {
|
||||
write!(f, "Invalid column type {} at index: {}, name: {}", t, i, name)
|
||||
}
|
||||
Error::StatementChangedRows(i) => write!(f, "Query changed {} rows", i),
|
||||
|
||||
@ -229,7 +229,7 @@ impl error::Error for Error {
|
||||
Error::QueryReturnedNoRows => "query returned no rows",
|
||||
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",
|
||||
|
||||
#[cfg(feature = "functions")]
|
||||
@ -262,7 +262,7 @@ impl error::Error for Error {
|
||||
| Error::QueryReturnedNoRows
|
||||
| Error::InvalidColumnIndex(_)
|
||||
| Error::InvalidColumnName(_)
|
||||
| Error::InvalidColumnType(_, _)
|
||||
| Error::InvalidColumnType(_, _, _)
|
||||
| Error::InvalidPath(_)
|
||||
| Error::StatementChangedRows(_)
|
||||
| Error::InvalidQuery => None,
|
||||
|
@ -1476,7 +1476,7 @@ mod test {
|
||||
.collect();
|
||||
|
||||
match bad_type.unwrap_err() {
|
||||
Error::InvalidColumnType(_, _) => (),
|
||||
Error::InvalidColumnType(_, _, _) => (),
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
|
||||
@ -1531,7 +1531,7 @@ mod test {
|
||||
.collect();
|
||||
|
||||
match bad_type.unwrap_err() {
|
||||
CustomError::Sqlite(Error::InvalidColumnType(_, _)) => (),
|
||||
CustomError::Sqlite(Error::InvalidColumnType(_, _, _)) => (),
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
|
||||
@ -1588,7 +1588,7 @@ mod test {
|
||||
});
|
||||
|
||||
match bad_type.unwrap_err() {
|
||||
CustomError::Sqlite(Error::InvalidColumnType(_, _)) => (),
|
||||
CustomError::Sqlite(Error::InvalidColumnType(_, _, _)) => (),
|
||||
err => panic!("Unexpected error {}", err),
|
||||
}
|
||||
|
||||
|
@ -37,8 +37,18 @@ impl RawStatement {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn column_name(&self, idx: usize) -> &CStr {
|
||||
unsafe { CStr::from_ptr(ffi::sqlite3_column_name(self.0, idx as c_int)) }
|
||||
pub fn column_name(&self, idx: usize) -> Option<&CStr> {
|
||||
let idx = idx as c_int;
|
||||
if idx < 0 || idx >= self.column_count() as c_int {
|
||||
return None;
|
||||
}
|
||||
unsafe {
|
||||
let ptr = ffi::sqlite3_column_name(self.0, idx);
|
||||
// If ptr is null here, it's an OOM, so there's probably nothing
|
||||
// meaningful we can do. Just assert instead of returning None.
|
||||
assert!(!ptr.is_null(), "Null pointer from sqlite3_column_name: Out of memory?");
|
||||
Some(CStr::from_ptr(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&self) -> c_int {
|
||||
|
@ -223,15 +223,15 @@ impl<'stmt> Row<'stmt> {
|
||||
let idx = idx.idx(self.stmt)?;
|
||||
let value = self.stmt.value_ref(idx);
|
||||
FromSql::column_result(value).map_err(|err| match err {
|
||||
FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()),
|
||||
FromSqlError::InvalidType => Error::InvalidColumnType(idx, self.stmt.column_name(idx).into(), value.data_type()),
|
||||
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
|
||||
FromSqlError::Other(err) => {
|
||||
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
|
||||
}
|
||||
#[cfg(feature = "i128_blob")]
|
||||
FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(idx, value.data_type()),
|
||||
FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(idx, self.stmt.column_name(idx).into(), value.data_type()),
|
||||
#[cfg(feature = "uuid")]
|
||||
FromSqlError::InvalidUuidSize(_) => Error::InvalidColumnType(idx, value.data_type()),
|
||||
FromSqlError::InvalidUuidSize(_) => Error::InvalidColumnType(idx, self.stmt.column_name(idx).into(), value.data_type()),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,7 @@ mod test {
|
||||
fn test_mismatched_types() {
|
||||
fn is_invalid_column_type(err: Error) -> bool {
|
||||
match err {
|
||||
Error::InvalidColumnType(_, _) => true,
|
||||
Error::InvalidColumnType(_, _, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -472,7 +472,7 @@ impl Values<'_> {
|
||||
}
|
||||
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
|
||||
#[cfg(feature = "i128_blob")]
|
||||
FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(idx, value.data_type()),
|
||||
FromSqlError::InvalidI128Size(_) => Error::InvalidColumnType(idx, idx.to_string(), value.data_type()),
|
||||
#[cfg(feature = "uuid")]
|
||||
FromSqlError::InvalidUuidSize(_) => {
|
||||
Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err))
|
||||
|
Loading…
Reference in New Issue
Block a user