Skip to main content

rusqlite/types/
from_sql.rs

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