Skip to main content

rusqlite/types/
from_sql.rs

1use super::{Value, ValueRef};
2use std::error::Error;
3use std::fmt;
4
5/// Enum listing possible errors from [`FromSql`] trait.
6#[derive(Debug)]
7#[non_exhaustive]
8pub enum FromSqlError {
9    /// Error when an SQLite value is requested, but the type of the result
10    /// cannot be converted to the requested Rust type.
11    InvalidType,
12
13    /// Error when the i64 value returned by SQLite cannot be stored into the
14    /// requested type.
15    OutOfRange(i64),
16
17    /// Error when the blob result returned by SQLite cannot be stored into the
18    /// requested type due to a size mismatch.
19    InvalidBlobSize {
20        /// The expected size of the blob.
21        expected_size: usize,
22        /// The actual size of the blob that was returned.
23        blob_size: usize,
24    },
25
26    /// An error case available for implementors of the [`FromSql`] trait.
27    Other(Box<dyn Error + Send + Sync + 'static>),
28}
29
30impl PartialEq for FromSqlError {
31    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            (
36                FromSqlError::InvalidBlobSize {
37                    expected_size: es1,
38                    blob_size: bs1,
39                },
40                FromSqlError::InvalidBlobSize {
41                    expected_size: es2,
42                    blob_size: bs2,
43                },
44            ) => es1 == es2 && bs1 == bs2,
45            (..) => false,
46        }
47    }
48}
49
50impl fmt::Display for FromSqlError {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        match *self {
53            FromSqlError::InvalidType => write!(f, "Invalid type"),
54            FromSqlError::OutOfRange(i) => write!(f, "Value {i} out of range"),
55            FromSqlError::InvalidBlobSize {
56                expected_size,
57                blob_size,
58            } => {
59                write!(
60                    f,
61                    "Cannot read {expected_size} byte value out of {blob_size} byte blob"
62                )
63            }
64            FromSqlError::Other(ref err) => err.fmt(f),
65        }
66    }
67}
68
69impl Error for FromSqlError {
70    fn source(&self) -> Option<&(dyn Error + 'static)> {
71        if let FromSqlError::Other(ref err) = self {
72            Some(&**err)
73        } else {
74            None
75        }
76    }
77}
78
79/// Result type for implementors of the [`FromSql`] trait.
80pub type FromSqlResult<T> = Result<T, FromSqlError>;
81
82/// A trait for types that can be created from a SQLite value.
83pub trait FromSql: Sized {
84    /// Converts SQLite value into Rust value.
85    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self>;
86}
87
88macro_rules! from_sql_integral(
89    ($t:ident) => (
90        impl FromSql for $t {
91            #[inline]
92            fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
93                let i = i64::column_result(value)?;
94                i.try_into().map_err(|_| FromSqlError::OutOfRange(i))
95            }
96        }
97    );
98    (non_zero $nz:ty, $z:ty) => (
99        impl FromSql for $nz {
100            #[inline]
101            fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
102                let i = <$z>::column_result(value)?;
103                <$nz>::new(i).ok_or(FromSqlError::OutOfRange(0))
104            }
105        }
106    )
107);
108
109from_sql_integral!(i8);
110from_sql_integral!(i16);
111from_sql_integral!(i32);
112// from_sql_integral!(i64); // Not needed because the native type is i64.
113from_sql_integral!(isize);
114from_sql_integral!(u8);
115from_sql_integral!(u16);
116from_sql_integral!(u32);
117from_sql_integral!(u64);
118from_sql_integral!(usize);
119
120from_sql_integral!(non_zero std::num::NonZeroIsize, isize);
121from_sql_integral!(non_zero std::num::NonZeroI8, i8);
122from_sql_integral!(non_zero std::num::NonZeroI16, i16);
123from_sql_integral!(non_zero std::num::NonZeroI32, i32);
124from_sql_integral!(non_zero std::num::NonZeroI64, i64);
125#[cfg(feature = "i128_blob")]
126#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
127from_sql_integral!(non_zero std::num::NonZeroI128, i128);
128
129from_sql_integral!(non_zero std::num::NonZeroUsize, usize);
130from_sql_integral!(non_zero std::num::NonZeroU8, u8);
131from_sql_integral!(non_zero std::num::NonZeroU16, u16);
132from_sql_integral!(non_zero std::num::NonZeroU32, u32);
133from_sql_integral!(non_zero std::num::NonZeroU64, u64);
134// std::num::NonZeroU128 is not supported since u128 isn't either
135
136impl FromSql for i64 {
137    #[inline]
138    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
139        value.as_i64()
140    }
141}
142
143impl FromSql for f32 {
144    #[inline]
145    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
146        match value {
147            ValueRef::Integer(i) => Ok(i as f32),
148            ValueRef::Real(f) => Ok(f as f32),
149            _ => Err(FromSqlError::InvalidType),
150        }
151    }
152}
153
154impl FromSql for f64 {
155    #[inline]
156    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
157        match value {
158            ValueRef::Integer(i) => Ok(i as f64),
159            ValueRef::Real(f) => Ok(f),
160            _ => Err(FromSqlError::InvalidType),
161        }
162    }
163}
164
165impl FromSql for bool {
166    #[inline]
167    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
168        i64::column_result(value).map(|i| i != 0)
169    }
170}
171
172impl FromSql for String {
173    #[inline]
174    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
175        value.as_str().map(ToString::to_string)
176    }
177}
178
179impl FromSql for Box<str> {
180    #[inline]
181    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
182        value.as_str().map(Into::into)
183    }
184}
185
186impl FromSql for std::rc::Rc<str> {
187    #[inline]
188    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
189        value.as_str().map(Into::into)
190    }
191}
192
193impl FromSql for std::sync::Arc<str> {
194    #[inline]
195    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
196        value.as_str().map(Into::into)
197    }
198}
199
200impl FromSql for Vec<u8> {
201    #[inline]
202    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
203        value.as_blob().map(<[u8]>::to_vec)
204    }
205}
206
207impl<const N: usize> FromSql for [u8; N] {
208    #[inline]
209    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
210        let slice = value.as_blob()?;
211        slice.try_into().map_err(|_| FromSqlError::InvalidBlobSize {
212            expected_size: N,
213            blob_size: slice.len(),
214        })
215    }
216}
217
218#[cfg(feature = "i128_blob")]
219#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
220impl FromSql for i128 {
221    #[inline]
222    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
223        let bytes = <[u8; 16]>::column_result(value)?;
224        Ok(i128::from_be_bytes(bytes) ^ (1_i128 << 127))
225    }
226}
227
228#[cfg(feature = "uuid")]
229#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
230impl FromSql for uuid::Uuid {
231    #[inline]
232    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
233        let bytes = <[u8; 16]>::column_result(value)?;
234        Ok(uuid::Uuid::from_u128(u128::from_be_bytes(bytes)))
235    }
236}
237
238impl<T: FromSql> FromSql for Option<T> {
239    #[inline]
240    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
241        match value {
242            ValueRef::Null => Ok(None),
243            _ => FromSql::column_result(value).map(Some),
244        }
245    }
246}
247
248impl FromSql for Value {
249    #[inline]
250    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
251        Ok(value.into())
252    }
253}
254
255#[cfg(test)]
256mod test {
257    use super::FromSql;
258    use crate::{Connection, Error, Result};
259
260    #[test]
261    fn test_integral_ranges() -> Result<()> {
262        let db = Connection::open_in_memory()?;
263
264        fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64])
265        where
266            T: Into<i64> + FromSql + std::fmt::Debug,
267        {
268            for n in out_of_range {
269                let err = db
270                    .query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
271                    .unwrap_err();
272                match err {
273                    Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
274                    _ => panic!("unexpected error: {err}"),
275                }
276            }
277            for n in in_range {
278                assert_eq!(
279                    *n,
280                    db.query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
281                        .unwrap()
282                        .into()
283                );
284            }
285        }
286
287        check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]);
288        check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
289        check_ranges::<i32>(
290            &db,
291            &[-2_147_483_649, 2_147_483_648],
292            &[-2_147_483_648, -1, 0, 1, 2_147_483_647],
293        );
294        check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]);
295        check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]);
296        check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
297        Ok(())
298    }
299
300    #[test]
301    fn test_nonzero_ranges() -> Result<()> {
302        let db = Connection::open_in_memory()?;
303
304        macro_rules! check_ranges {
305            ($nz:ty, $out_of_range:expr, $in_range:expr) => {
306                for &n in $out_of_range {
307                    assert_eq!(
308                        db.query_row("SELECT ?1", [n], |r| r.get::<_, $nz>(0)),
309                        Err(Error::IntegralValueOutOfRange(0, n)),
310                        "{}",
311                        std::any::type_name::<$nz>()
312                    );
313                }
314                for &n in $in_range {
315                    let non_zero = <$nz>::new(n).unwrap();
316                    assert_eq!(
317                        Ok(non_zero),
318                        db.query_row("SELECT ?1", [non_zero], |r| r.get::<_, $nz>(0))
319                    );
320                }
321            };
322        }
323
324        check_ranges!(std::num::NonZeroI8, &[0, -129, 128], &[-128, 1, 127]);
325        check_ranges!(
326            std::num::NonZeroI16,
327            &[0, -32769, 32768],
328            &[-32768, -1, 1, 32767]
329        );
330        check_ranges!(
331            std::num::NonZeroI32,
332            &[0, -2_147_483_649, 2_147_483_648],
333            &[-2_147_483_648, -1, 1, 2_147_483_647]
334        );
335        check_ranges!(
336            std::num::NonZeroI64,
337            &[0],
338            &[-2_147_483_648, -1, 1, 2_147_483_647, i64::MAX, i64::MIN]
339        );
340        check_ranges!(
341            std::num::NonZeroIsize,
342            &[0],
343            &[-2_147_483_648, -1, 1, 2_147_483_647]
344        );
345        check_ranges!(std::num::NonZeroU8, &[0, -2, -1, 256], &[1, 255]);
346        check_ranges!(std::num::NonZeroU16, &[0, -2, -1, 65536], &[1, 65535]);
347        check_ranges!(
348            std::num::NonZeroU32,
349            &[0, -2, -1, 4_294_967_296],
350            &[1, 4_294_967_295]
351        );
352        check_ranges!(
353            std::num::NonZeroU64,
354            &[0, -2, -1, -4_294_967_296],
355            &[1, 4_294_967_295, i64::MAX as u64]
356        );
357        check_ranges!(
358            std::num::NonZeroUsize,
359            &[0, -2, -1, -4_294_967_296],
360            &[1, 4_294_967_295]
361        );
362
363        Ok(())
364    }
365}