mirror of
https://github.com/isar/rusqlite.git
synced 2024-11-23 00:39:20 +08:00
Add range checks for i32's FromSql impl.
This commit is contained in:
parent
8562aa7f1f
commit
1974ee573c
12
src/error.rs
12
src/error.rs
@ -25,6 +25,11 @@ pub enum Error {
|
||||
/// the requested Rust type.
|
||||
FromSqlConversionFailure(usize, Type, Box<error::Error + Send + Sync>),
|
||||
|
||||
/// Error when SQLite gives us an integral value outside the range of the requested type (e.g.,
|
||||
/// trying to get the value 1000 into a `u8`). The associated `c_int` is the column index, and
|
||||
/// the associated `i64` is the value returned by SQLite.
|
||||
IntegralValueOutOfRange(c_int, i64),
|
||||
|
||||
/// Error converting a string to UTF-8.
|
||||
Utf8Error(str::Utf8Error),
|
||||
|
||||
@ -99,6 +104,9 @@ impl fmt::Display for Error {
|
||||
i,
|
||||
err)
|
||||
}
|
||||
Error::IntegralValueOutOfRange(col, val) => {
|
||||
write!(f, "Integer {} out of range at index {}", val, col)
|
||||
}
|
||||
Error::Utf8Error(ref err) => err.fmt(f),
|
||||
Error::NulError(ref err) => err.fmt(f),
|
||||
Error::InvalidParameterName(ref name) => write!(f, "Invalid parameter name: {}", name),
|
||||
@ -133,6 +141,9 @@ impl error::Error for Error {
|
||||
"SQLite was compiled or configured for single-threaded use only"
|
||||
}
|
||||
Error::FromSqlConversionFailure(_, _, ref err) => err.description(),
|
||||
Error::IntegralValueOutOfRange(_, _) => {
|
||||
"integral value out of range of requested type"
|
||||
}
|
||||
Error::Utf8Error(ref err) => err.description(),
|
||||
Error::InvalidParameterName(_) => "invalid parameter name",
|
||||
Error::NulError(ref err) => err.description(),
|
||||
@ -160,6 +171,7 @@ impl error::Error for Error {
|
||||
Error::Utf8Error(ref err) => Some(err),
|
||||
Error::NulError(ref err) => Some(err),
|
||||
|
||||
Error::IntegralValueOutOfRange(_, _) |
|
||||
Error::SqliteSingleThreadedMode |
|
||||
Error::InvalidParameterName(_) |
|
||||
Error::ExecuteReturnedResults |
|
||||
|
@ -1129,6 +1129,7 @@ impl<'a, 'stmt> Row<'a, 'stmt> {
|
||||
let value = unsafe { ValueRef::new(&self.stmt.stmt, idx) };
|
||||
FromSql::column_result(value).map_err(|err| match err {
|
||||
FromSqlError::InvalidType => Error::InvalidColumnType(idx, value.data_type()),
|
||||
FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
|
||||
FromSqlError::Other(err) => {
|
||||
Error::FromSqlConversionFailure(idx as usize, value.data_type(), err)
|
||||
}
|
||||
|
@ -5,9 +5,13 @@ use std::fmt;
|
||||
/// Enum listing possible errors from `FromSql` trait.
|
||||
#[derive(Debug)]
|
||||
pub enum FromSqlError {
|
||||
/// Error when an SQLite value is requested, but the type of the result cannot be converted to the
|
||||
/// requested Rust type.
|
||||
/// Error when an SQLite value is requested, but the type of the result cannot be converted to
|
||||
/// the requested Rust type.
|
||||
InvalidType,
|
||||
|
||||
/// Error when the i64 value returned by SQLite cannot be stored into the requested type.
|
||||
OutOfRange(i64),
|
||||
|
||||
/// An error case available for implementors of the `FromSql` trait.
|
||||
Other(Box<Error + Send + Sync>),
|
||||
}
|
||||
@ -16,6 +20,7 @@ impl fmt::Display for FromSqlError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
FromSqlError::InvalidType => write!(f, "Invalid type"),
|
||||
FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i),
|
||||
FromSqlError::Other(ref err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
@ -25,6 +30,7 @@ impl Error for FromSqlError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
FromSqlError::InvalidType => "invalid type",
|
||||
FromSqlError::OutOfRange(_) => "value out of range",
|
||||
FromSqlError::Other(ref err) => err.description(),
|
||||
}
|
||||
}
|
||||
@ -33,6 +39,7 @@ impl Error for FromSqlError {
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
match *self {
|
||||
FromSqlError::InvalidType => None,
|
||||
FromSqlError::OutOfRange(_) => None,
|
||||
FromSqlError::Other(ref err) => err.cause(),
|
||||
}
|
||||
}
|
||||
@ -48,7 +55,13 @@ pub trait FromSql: Sized {
|
||||
|
||||
impl FromSql for i32 {
|
||||
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
|
||||
i64::column_result(value).map(|i| i as i32)
|
||||
i64::column_result(value).and_then(|i| {
|
||||
if i < i32::min_value() as i64 || i > i32::max_value() as i64 {
|
||||
Err(FromSqlError::OutOfRange(i))
|
||||
} else {
|
||||
Ok(i as i32)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,3 +116,36 @@ impl FromSql for Value {
|
||||
Ok(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use {Connection, Error};
|
||||
|
||||
fn checked_memory_handle() -> Connection {
|
||||
Connection::open_in_memory().unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_integral_ranges() {
|
||||
let db = checked_memory_handle();
|
||||
|
||||
fn assert_out_of_range_error(err: Error, value: i64) {
|
||||
match err {
|
||||
Error::IntegralValueOutOfRange(_, bad) => assert_eq!(bad, value),
|
||||
_ => panic!("unexpected error {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
// i32
|
||||
for bad in &[-2147483649, 2147483648] {
|
||||
let err = db.query_row("SELECT ?", &[bad], |r| r.get_checked::<_, i32>(0))
|
||||
.unwrap()
|
||||
.unwrap_err();
|
||||
assert_out_of_range_error(err, *bad);
|
||||
}
|
||||
for good in &[-2147483648, 2147483647] {
|
||||
assert_eq!(*good,
|
||||
db.query_row("SELECT ?", &[good], |r| r.get::<_, i32>(0)).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user