rusqlite/types/
from_sql.rs

1use super::{Value, ValueRef};
2use std::borrow::Cow;
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    /// Error when the blob result returned by SQLite cannot be stored into the
19    /// requested type due to a size mismatch.
20    InvalidBlobSize {
21        /// The expected size of the blob.
22        expected_size: usize,
23        /// The actual size of the blob that was returned.
24        blob_size: usize,
25    },
26
27    /// An error case available for implementors of the [`FromSql`] trait.
28    Other(Box<dyn Error + Send + Sync + 'static>),
29}
30
31impl FromSqlError {
32    /// Converts an arbitrary error type to [`FromSqlError`].
33    ///
34    /// This is a convenience function that boxes and unsizes the error type. It's main purpose is
35    /// to be usable in the `map_err` method. So instead of
36    /// `result.map_err(|error| FromSqlError::Other(Box::new(error))` you can write
37    /// `result.map_err(FromSqlError::other)`.
38    pub fn other<E: Error + Send + Sync + 'static>(error: E) -> Self {
39        Self::Other(Box::new(error))
40    }
41}
42
43impl PartialEq for FromSqlError {
44    fn eq(&self, other: &Self) -> bool {
45        match (self, other) {
46            (Self::InvalidType, Self::InvalidType) => true,
47            (Self::OutOfRange(n1), Self::OutOfRange(n2)) => n1 == n2,
48            (
49                Self::InvalidBlobSize {
50                    expected_size: es1,
51                    blob_size: bs1,
52                },
53                Self::InvalidBlobSize {
54                    expected_size: es2,
55                    blob_size: bs2,
56                },
57            ) => es1 == es2 && bs1 == bs2,
58            (..) => false,
59        }
60    }
61}
62
63impl fmt::Display for FromSqlError {
64    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65        match *self {
66            Self::InvalidType => write!(f, "Invalid type"),
67            Self::OutOfRange(i) => write!(f, "Value {i} out of range"),
68            Self::InvalidBlobSize {
69                expected_size,
70                blob_size,
71            } => {
72                write!(
73                    f,
74                    "Cannot read {expected_size} byte value out of {blob_size} byte blob"
75                )
76            }
77            Self::Other(ref err) => err.fmt(f),
78        }
79    }
80}
81
82impl Error for FromSqlError {
83    fn source(&self) -> Option<&(dyn Error + 'static)> {
84        if let Self::Other(ref err) = self {
85            Some(&**err)
86        } else {
87            None
88        }
89    }
90}
91
92/// Result type for implementors of the [`FromSql`] trait.
93pub type FromSqlResult<T> = Result<T, FromSqlError>;
94
95/// A trait for types that can be created from a SQLite value.
96pub trait FromSql: Sized {
97    /// Converts SQLite value into Rust value.
98    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self>;
99}
100
101macro_rules! from_sql_integral(
102    ($t:ident) => (
103        impl FromSql for $t {
104            #[inline]
105            fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
106                let i = i64::column_result(value)?;
107                i.try_into().map_err(|_| FromSqlError::OutOfRange(i))
108            }
109        }
110    );
111    (non_zero $nz:ty, $z:ty) => (
112        impl FromSql for $nz {
113            #[inline]
114            fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
115                let i = <$z>::column_result(value)?;
116                <$nz>::new(i).ok_or(FromSqlError::OutOfRange(0))
117            }
118        }
119    )
120);
121
122from_sql_integral!(i8);
123from_sql_integral!(i16);
124from_sql_integral!(i32);
125// from_sql_integral!(i64); // Not needed because the native type is i64.
126from_sql_integral!(isize);
127from_sql_integral!(u8);
128from_sql_integral!(u16);
129from_sql_integral!(u32);
130from_sql_integral!(u64);
131from_sql_integral!(usize);
132
133from_sql_integral!(non_zero std::num::NonZeroIsize, isize);
134from_sql_integral!(non_zero std::num::NonZeroI8, i8);
135from_sql_integral!(non_zero std::num::NonZeroI16, i16);
136from_sql_integral!(non_zero std::num::NonZeroI32, i32);
137from_sql_integral!(non_zero std::num::NonZeroI64, i64);
138#[cfg(feature = "i128_blob")]
139from_sql_integral!(non_zero std::num::NonZeroI128, i128);
140
141from_sql_integral!(non_zero std::num::NonZeroUsize, usize);
142from_sql_integral!(non_zero std::num::NonZeroU8, u8);
143from_sql_integral!(non_zero std::num::NonZeroU16, u16);
144from_sql_integral!(non_zero std::num::NonZeroU32, u32);
145from_sql_integral!(non_zero std::num::NonZeroU64, u64);
146// std::num::NonZeroU128 is not supported since u128 isn't either
147
148impl FromSql for i64 {
149    #[inline]
150    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
151        value.as_i64()
152    }
153}
154
155impl FromSql for f32 {
156    #[inline]
157    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
158        match value {
159            ValueRef::Integer(i) => Ok(i as Self),
160            ValueRef::Real(f) => Ok(f as Self),
161            _ => Err(FromSqlError::InvalidType),
162        }
163    }
164}
165
166impl FromSql for f64 {
167    #[inline]
168    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
169        match value {
170            ValueRef::Integer(i) => Ok(i as Self),
171            ValueRef::Real(f) => Ok(f),
172            _ => Err(FromSqlError::InvalidType),
173        }
174    }
175}
176
177impl FromSql for bool {
178    #[inline]
179    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
180        i64::column_result(value).map(|i| i != 0)
181    }
182}
183
184impl FromSql for String {
185    #[inline]
186    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
187        value.as_str().map(ToString::to_string)
188    }
189}
190
191impl FromSql for Box<str> {
192    #[inline]
193    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
194        value.as_str().map(Into::into)
195    }
196}
197
198impl FromSql for std::rc::Rc<str> {
199    #[inline]
200    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
201        value.as_str().map(Into::into)
202    }
203}
204
205impl FromSql for std::sync::Arc<str> {
206    #[inline]
207    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
208        value.as_str().map(Into::into)
209    }
210}
211
212impl FromSql for Vec<u8> {
213    #[inline]
214    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
215        value.as_blob().map(<[u8]>::to_vec)
216    }
217}
218
219impl FromSql for Box<[u8]> {
220    #[inline]
221    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
222        value.as_blob().map(Box::<[u8]>::from)
223    }
224}
225
226impl FromSql for std::rc::Rc<[u8]> {
227    #[inline]
228    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
229        value.as_blob().map(std::rc::Rc::<[u8]>::from)
230    }
231}
232
233impl FromSql for std::sync::Arc<[u8]> {
234    #[inline]
235    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
236        value.as_blob().map(std::sync::Arc::<[u8]>::from)
237    }
238}
239
240impl<const N: usize> FromSql for [u8; N] {
241    #[inline]
242    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
243        let slice = value.as_blob()?;
244        slice.try_into().map_err(|_| FromSqlError::InvalidBlobSize {
245            expected_size: N,
246            blob_size: slice.len(),
247        })
248    }
249}
250
251#[cfg(feature = "i128_blob")]
252impl FromSql for i128 {
253    #[inline]
254    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
255        let bytes = <[u8; 16]>::column_result(value)?;
256        Ok(Self::from_be_bytes(bytes) ^ (1_i128 << 127))
257    }
258}
259
260#[cfg(feature = "uuid")]
261impl FromSql for uuid::Uuid {
262    #[inline]
263    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
264        let bytes = <[u8; 16]>::column_result(value)?;
265        Ok(Self::from_u128(u128::from_be_bytes(bytes)))
266    }
267}
268
269impl<T: FromSql> FromSql for Option<T> {
270    #[inline]
271    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
272        match value {
273            ValueRef::Null => Ok(None),
274            _ => FromSql::column_result(value).map(Some),
275        }
276    }
277}
278
279impl<T: ?Sized> FromSql for Cow<'_, T>
280where
281    T: ToOwned,
282    T::Owned: FromSql,
283{
284    #[inline]
285    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
286        <T::Owned>::column_result(value).map(Cow::Owned)
287    }
288}
289
290impl FromSql for Value {
291    #[inline]
292    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
293        Ok(value.into())
294    }
295}
296
297#[cfg(test)]
298mod test {
299    use super::{FromSql, FromSqlError};
300    use crate::{Connection, Error, Result};
301    use std::borrow::Cow;
302    use std::rc::Rc;
303    use std::sync::Arc;
304
305    #[test]
306    fn test_integral_ranges() -> Result<()> {
307        let db = Connection::open_in_memory()?;
308
309        fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64])
310        where
311            T: Into<i64> + FromSql + std::fmt::Debug,
312        {
313            for n in out_of_range {
314                let err = db
315                    .query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
316                    .unwrap_err();
317                match err {
318                    Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
319                    _ => panic!("unexpected error: {err}"),
320                }
321            }
322            for n in in_range {
323                assert_eq!(
324                    *n,
325                    db.query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
326                        .unwrap()
327                        .into()
328                );
329            }
330        }
331
332        check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]);
333        check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
334        check_ranges::<i32>(
335            &db,
336            &[-2_147_483_649, 2_147_483_648],
337            &[-2_147_483_648, -1, 0, 1, 2_147_483_647],
338        );
339        check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]);
340        check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]);
341        check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
342        Ok(())
343    }
344
345    #[test]
346    fn test_nonzero_ranges() -> Result<()> {
347        let db = Connection::open_in_memory()?;
348
349        macro_rules! check_ranges {
350            ($nz:ty, $out_of_range:expr, $in_range:expr) => {
351                for &n in $out_of_range {
352                    assert_eq!(
353                        db.query_row("SELECT ?1", [n], |r| r.get::<_, $nz>(0)),
354                        Err(Error::IntegralValueOutOfRange(0, n)),
355                        "{}",
356                        std::any::type_name::<$nz>()
357                    );
358                }
359                for &n in $in_range {
360                    let non_zero = <$nz>::new(n).unwrap();
361                    assert_eq!(
362                        Ok(non_zero),
363                        db.query_row("SELECT ?1", [non_zero], |r| r.get::<_, $nz>(0))
364                    );
365                }
366            };
367        }
368
369        check_ranges!(std::num::NonZeroI8, &[0, -129, 128], &[-128, 1, 127]);
370        check_ranges!(
371            std::num::NonZeroI16,
372            &[0, -32769, 32768],
373            &[-32768, -1, 1, 32767]
374        );
375        check_ranges!(
376            std::num::NonZeroI32,
377            &[0, -2_147_483_649, 2_147_483_648],
378            &[-2_147_483_648, -1, 1, 2_147_483_647]
379        );
380        check_ranges!(
381            std::num::NonZeroI64,
382            &[0],
383            &[-2_147_483_648, -1, 1, 2_147_483_647, i64::MAX, i64::MIN]
384        );
385        check_ranges!(
386            std::num::NonZeroIsize,
387            &[0],
388            &[-2_147_483_648, -1, 1, 2_147_483_647]
389        );
390        check_ranges!(std::num::NonZeroU8, &[0, -2, -1, 256], &[1, 255]);
391        check_ranges!(std::num::NonZeroU16, &[0, -2, -1, 65536], &[1, 65535]);
392        check_ranges!(
393            std::num::NonZeroU32,
394            &[0, -2, -1, 4_294_967_296],
395            &[1, 4_294_967_295]
396        );
397        check_ranges!(
398            std::num::NonZeroU64,
399            &[0, -2, -1, -4_294_967_296],
400            &[1, 4_294_967_295, i64::MAX as u64]
401        );
402        check_ranges!(
403            std::num::NonZeroUsize,
404            &[0, -2, -1, -4_294_967_296],
405            &[1, 4_294_967_295]
406        );
407
408        Ok(())
409    }
410
411    #[test]
412    fn test_cow() -> Result<()> {
413        let db = Connection::open_in_memory()?;
414
415        assert_eq!(
416            db.query_row("SELECT 'this is a string'", [], |r| r
417                .get::<_, Cow<'_, str>>(0)),
418            Ok(Cow::Borrowed("this is a string")),
419        );
420        assert_eq!(
421            db.query_row("SELECT x'09ab20fdee87'", [], |r| r
422                .get::<_, Cow<'_, [u8]>>(0)),
423            Ok(Cow::Owned(vec![0x09, 0xab, 0x20, 0xfd, 0xee, 0x87])),
424        );
425        assert_eq!(
426            db.query_row("SELECT 24.5", [], |r| r.get::<_, Cow<'_, f32>>(0),),
427            Ok(Cow::Borrowed(&24.5)),
428        );
429
430        Ok(())
431    }
432
433    #[test]
434    fn test_heap_slice() -> Result<()> {
435        let db = Connection::open_in_memory()?;
436
437        assert_eq!(
438            db.query_row("SELECT 'text'", [], |r| r.get::<_, Box<str>>(0)),
439            Ok(Box::from("text")),
440        );
441        assert_eq!(
442            db.query_row("SELECT 'Some string slice!'", [], |r| r
443                .get::<_, Rc<str>>(0)),
444            Ok(Rc::from("Some string slice!")),
445        );
446        assert_eq!(
447            db.query_row("SELECT x'012366779988fedc'", [], |r| r
448                .get::<_, Rc<[u8]>>(0)),
449            Ok(Rc::from(b"\x01\x23\x66\x77\x99\x88\xfe\xdc".as_slice())),
450        );
451
452        assert_eq!(
453            db.query_row(
454                "SELECT x'6120737472696e672043414e206265206120626c6f62'",
455                [],
456                |r| r.get::<_, Box<[u8]>>(0)
457            ),
458            Ok(b"a string CAN be a blob".to_vec().into_boxed_slice()),
459        );
460        assert_eq!(
461            db.query_row("SELECT 'This is inside an Arc.'", [], |r| r
462                .get::<_, Arc<str>>(0)),
463            Ok(Arc::from("This is inside an Arc.")),
464        );
465        assert_eq!(
466            db.query_row("SELECT x'afd374'", [], |r| r.get::<_, Arc<[u8]>>(0),),
467            Ok(Arc::from(b"\xaf\xd3\x74".as_slice())),
468        );
469
470        Ok(())
471    }
472
473    #[test]
474    fn from_sql_error() {
475        use std::error::Error as _;
476        assert_ne!(FromSqlError::InvalidType, FromSqlError::OutOfRange(0));
477        assert_ne!(FromSqlError::OutOfRange(0), FromSqlError::OutOfRange(1));
478        assert_ne!(
479            FromSqlError::InvalidBlobSize {
480                expected_size: 0,
481                blob_size: 0
482            },
483            FromSqlError::InvalidBlobSize {
484                expected_size: 0,
485                blob_size: 1
486            }
487        );
488        assert!(FromSqlError::InvalidType.source().is_none());
489        let err = std::io::Error::from(std::io::ErrorKind::UnexpectedEof);
490        assert!(FromSqlError::Other(Box::new(err)).source().is_some());
491    }
492}