1 use super::{Value, ValueRef}; 2 use std::error::Error; 3 use std::fmt; 4 5 /// Enum listing possible errors from `FromSql` trait. 6 #[derive(Debug)] 7 pub enum FromSqlError { 8 /// Error when an SQLite value is requested, but the type of the result 9 /// cannot be converted to the requested Rust type. 10 InvalidType, 11 12 /// Error when the i64 value returned by SQLite cannot be stored into the 13 /// requested type. 14 OutOfRange(i64), 15 16 /// Error returned when reading an `i128` from a blob with a size 17 /// other than 16. Only available when the `i128_blob` feature is enabled. 18 #[cfg(feature = "i128_blob")] 19 InvalidI128Size(usize), 20 21 /// Error returned when reading a `uuid` from a blob with a size 22 /// other than 16. Only available when the `uuid` feature is enabled. 23 #[cfg(feature = "uuid")] 24 InvalidUuidSize(usize), 25 26 /// An error case available for implementors of the `FromSql` trait. 27 Other(Box<dyn Error + Send + Sync>), 28 } 29 30 impl PartialEq for FromSqlError { eq(&self, other: &FromSqlError) -> bool31 fn eq(&self, other: &FromSqlError) -> bool { 32 match (self, other) { 33 (FromSqlError::InvalidType, FromSqlError::InvalidType) => true, 34 (FromSqlError::OutOfRange(n1), FromSqlError::OutOfRange(n2)) => n1 == n2, 35 #[cfg(feature = "i128_blob")] 36 (FromSqlError::InvalidI128Size(s1), FromSqlError::InvalidI128Size(s2)) => s1 == s2, 37 #[cfg(feature = "uuid")] 38 (FromSqlError::InvalidUuidSize(s1), FromSqlError::InvalidUuidSize(s2)) => s1 == s2, 39 (_, _) => false, 40 } 41 } 42 } 43 44 impl fmt::Display for FromSqlError { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 46 match *self { 47 FromSqlError::InvalidType => write!(f, "Invalid type"), 48 FromSqlError::OutOfRange(i) => write!(f, "Value {} out of range", i), 49 #[cfg(feature = "i128_blob")] 50 FromSqlError::InvalidI128Size(s) => { 51 write!(f, "Cannot read 128bit value out of {} byte blob", s) 52 } 53 #[cfg(feature = "uuid")] 54 FromSqlError::InvalidUuidSize(s) => { 55 write!(f, "Cannot read UUID value out of {} byte blob", s) 56 } 57 FromSqlError::Other(ref err) => err.fmt(f), 58 } 59 } 60 } 61 62 impl Error for FromSqlError { description(&self) -> &str63 fn description(&self) -> &str { 64 match *self { 65 FromSqlError::InvalidType => "invalid type", 66 FromSqlError::OutOfRange(_) => "value out of range", 67 #[cfg(feature = "i128_blob")] 68 FromSqlError::InvalidI128Size(_) => "unexpected blob size for 128bit value", 69 #[cfg(feature = "uuid")] 70 FromSqlError::InvalidUuidSize(_) => "unexpected blob size for UUID value", 71 FromSqlError::Other(ref err) => err.description(), 72 } 73 } 74 75 #[allow(clippy::match_same_arms)] 76 #[allow(deprecated)] cause(&self) -> Option<&dyn Error>77 fn cause(&self) -> Option<&dyn Error> { 78 match *self { 79 FromSqlError::Other(ref err) => err.cause(), 80 _ => None, 81 } 82 } 83 } 84 85 /// Result type for implementors of the `FromSql` trait. 86 pub type FromSqlResult<T> = Result<T, FromSqlError>; 87 88 /// A trait for types that can be created from a SQLite value. 89 /// 90 /// Note that `FromSql` and `ToSql` are defined for most integral types, but 91 /// not `u64` or `usize`. This is intentional; SQLite returns integers as 92 /// signed 64-bit values, which cannot fully represent the range of these 93 /// types. Rusqlite would have to 94 /// decide how to handle negative values: return an error or reinterpret as a 95 /// very large postive numbers, neither of which 96 /// is guaranteed to be correct for everyone. Callers can work around this by 97 /// fetching values as i64 and then doing the interpretation themselves or by 98 /// defining a newtype and implementing `FromSql`/`ToSql` for it. 99 pub trait FromSql: Sized { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>100 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self>; 101 } 102 103 impl FromSql for isize { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>104 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 105 i64::column_result(value).and_then(|i| { 106 if i < isize::min_value() as i64 || i > isize::max_value() as i64 { 107 Err(FromSqlError::OutOfRange(i)) 108 } else { 109 Ok(i as isize) 110 } 111 }) 112 } 113 } 114 115 macro_rules! from_sql_integral( 116 ($t:ident) => ( 117 impl FromSql for $t { 118 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 119 i64::column_result(value).and_then(|i| { 120 if i < i64::from($t::min_value()) || i > i64::from($t::max_value()) { 121 Err(FromSqlError::OutOfRange(i)) 122 } else { 123 Ok(i as $t) 124 } 125 }) 126 } 127 } 128 ) 129 ); 130 131 from_sql_integral!(i8); 132 from_sql_integral!(i16); 133 from_sql_integral!(i32); 134 from_sql_integral!(u8); 135 from_sql_integral!(u16); 136 from_sql_integral!(u32); 137 138 impl FromSql for i64 { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>139 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 140 value.as_i64() 141 } 142 } 143 144 impl FromSql for f64 { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>145 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 146 match value { 147 ValueRef::Integer(i) => Ok(i as f64), 148 ValueRef::Real(f) => Ok(f), 149 _ => Err(FromSqlError::InvalidType), 150 } 151 } 152 } 153 154 impl FromSql for bool { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>155 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 156 i64::column_result(value).map(|i| match i { 157 0 => false, 158 _ => true, 159 }) 160 } 161 } 162 163 impl FromSql for String { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>164 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 165 value.as_str().map(ToString::to_string) 166 } 167 } 168 169 impl FromSql for Vec<u8> { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>170 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 171 value.as_blob().map(|b| b.to_vec()) 172 } 173 } 174 175 #[cfg(feature = "i128_blob")] 176 impl FromSql for i128 { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>177 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 178 use byteorder::{BigEndian, ByteOrder}; 179 180 value.as_blob().and_then(|bytes| { 181 if bytes.len() == 16 { 182 Ok(BigEndian::read_i128(bytes) ^ (1i128 << 127)) 183 } else { 184 Err(FromSqlError::InvalidI128Size(bytes.len())) 185 } 186 }) 187 } 188 } 189 190 #[cfg(feature = "uuid")] 191 impl FromSql for uuid::Uuid { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>192 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 193 value 194 .as_blob() 195 .and_then(|bytes| { 196 uuid::Builder::from_slice(bytes) 197 .map_err(|_| FromSqlError::InvalidUuidSize(bytes.len())) 198 }) 199 .map(|mut builder| builder.build()) 200 } 201 } 202 203 impl<T: FromSql> FromSql for Option<T> { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>204 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 205 match value { 206 ValueRef::Null => Ok(None), 207 _ => FromSql::column_result(value).map(Some), 208 } 209 } 210 } 211 212 impl FromSql for Value { column_result(value: ValueRef<'_>) -> FromSqlResult<Self>213 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> { 214 Ok(value.into()) 215 } 216 } 217 218 #[cfg(test)] 219 mod test { 220 use super::FromSql; 221 use crate::{Connection, Error}; 222 checked_memory_handle() -> Connection223 fn checked_memory_handle() -> Connection { 224 Connection::open_in_memory().unwrap() 225 } 226 227 #[test] test_integral_ranges()228 fn test_integral_ranges() { 229 let db = checked_memory_handle(); 230 231 fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64]) 232 where 233 T: Into<i64> + FromSql + ::std::fmt::Debug, 234 { 235 for n in out_of_range { 236 let err = db 237 .query_row("SELECT ?", &[n], |r| r.get::<_, T>(0)) 238 .unwrap_err(); 239 match err { 240 Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value), 241 _ => panic!("unexpected error: {}", err), 242 } 243 } 244 for n in in_range { 245 assert_eq!( 246 *n, 247 db.query_row("SELECT ?", &[n], |r| r.get::<_, T>(0)) 248 .unwrap() 249 .into() 250 ); 251 } 252 } 253 254 check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]); 255 check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]); 256 check_ranges::<i32>( 257 &db, 258 &[-2_147_483_649, 2_147_483_648], 259 &[-2_147_483_648, -1, 0, 1, 2_147_483_647], 260 ); 261 check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]); 262 check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]); 263 check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]); 264 } 265 } 266