Skip to main content

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);
130#[cfg(feature = "fallible_uint")]
131from_sql_integral!(u64);
132#[cfg(feature = "fallible_uint")]
133from_sql_integral!(usize);
134
135from_sql_integral!(non_zero std::num::NonZeroIsize, isize);
136from_sql_integral!(non_zero std::num::NonZeroI8, i8);
137from_sql_integral!(non_zero std::num::NonZeroI16, i16);
138from_sql_integral!(non_zero std::num::NonZeroI32, i32);
139from_sql_integral!(non_zero std::num::NonZeroI64, i64);
140#[cfg(feature = "i128_blob")]
141from_sql_integral!(non_zero std::num::NonZeroI128, i128);
142
143#[cfg(feature = "fallible_uint")]
144from_sql_integral!(non_zero std::num::NonZeroUsize, usize);
145from_sql_integral!(non_zero std::num::NonZeroU8, u8);
146from_sql_integral!(non_zero std::num::NonZeroU16, u16);
147from_sql_integral!(non_zero std::num::NonZeroU32, u32);
148#[cfg(feature = "fallible_uint")]
149from_sql_integral!(non_zero std::num::NonZeroU64, u64);
150// std::num::NonZeroU128 is not supported since u128 isn't either
151
152impl FromSql for i64 {
153    #[inline]
154    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
155        value.as_i64()
156    }
157}
158
159impl FromSql for f32 {
160    #[inline]
161    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
162        match value {
163            ValueRef::Integer(i) => Ok(i as Self),
164            ValueRef::Real(f) => Ok(f as Self),
165            _ => Err(FromSqlError::InvalidType),
166        }
167    }
168}
169
170impl FromSql for f64 {
171    #[inline]
172    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
173        match value {
174            ValueRef::Integer(i) => Ok(i as Self),
175            ValueRef::Real(f) => Ok(f),
176            _ => Err(FromSqlError::InvalidType),
177        }
178    }
179}
180
181impl FromSql for bool {
182    #[inline]
183    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
184        i64::column_result(value).map(|i| i != 0)
185    }
186}
187
188impl FromSql for String {
189    #[inline]
190    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
191        value.as_str().map(ToString::to_string)
192    }
193}
194
195impl FromSql for Box<str> {
196    #[inline]
197    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
198        value.as_str().map(Into::into)
199    }
200}
201
202impl FromSql for std::rc::Rc<str> {
203    #[inline]
204    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
205        value.as_str().map(Into::into)
206    }
207}
208
209impl FromSql for std::sync::Arc<str> {
210    #[inline]
211    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
212        value.as_str().map(Into::into)
213    }
214}
215
216impl FromSql for Vec<u8> {
217    #[inline]
218    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
219        value.as_blob().map(<[u8]>::to_vec)
220    }
221}
222
223impl FromSql for Box<[u8]> {
224    #[inline]
225    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
226        value.as_blob().map(Box::<[u8]>::from)
227    }
228}
229
230impl FromSql for std::rc::Rc<[u8]> {
231    #[inline]
232    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
233        value.as_blob().map(std::rc::Rc::<[u8]>::from)
234    }
235}
236
237impl FromSql for std::sync::Arc<[u8]> {
238    #[inline]
239    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
240        value.as_blob().map(std::sync::Arc::<[u8]>::from)
241    }
242}
243
244impl<const N: usize> FromSql for [u8; N] {
245    #[inline]
246    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
247        let slice = value.as_blob()?;
248        slice.try_into().map_err(|_| FromSqlError::InvalidBlobSize {
249            expected_size: N,
250            blob_size: slice.len(),
251        })
252    }
253}
254
255#[cfg(feature = "i128_blob")]
256impl FromSql for i128 {
257    #[inline]
258    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
259        let bytes = <[u8; 16]>::column_result(value)?;
260        Ok(Self::from_be_bytes(bytes) ^ (1_i128 << 127))
261    }
262}
263
264#[cfg(feature = "uuid")]
265impl FromSql for uuid::Uuid {
266    #[inline]
267    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
268        let bytes = <[u8; 16]>::column_result(value)?;
269        Ok(Self::from_u128(u128::from_be_bytes(bytes)))
270    }
271}
272
273impl<T: FromSql> FromSql for Option<T> {
274    #[inline]
275    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
276        match value {
277            ValueRef::Null => Ok(None),
278            _ => FromSql::column_result(value).map(Some),
279        }
280    }
281}
282
283impl<T: ?Sized> FromSql for Cow<'_, T>
284where
285    T: ToOwned,
286    T::Owned: FromSql,
287{
288    #[inline]
289    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
290        <T::Owned>::column_result(value).map(Cow::Owned)
291    }
292}
293
294impl FromSql for Value {
295    #[inline]
296    fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
297        Ok(value.into())
298    }
299}
300
301#[cfg(test)]
302mod test {
303    #[cfg(all(target_family = "wasm", target_os = "unknown"))]
304    use wasm_bindgen_test::wasm_bindgen_test as test;
305
306    use super::{FromSql, FromSqlError};
307    use crate::{Connection, Error, Result};
308    use std::borrow::Cow;
309    use std::rc::Rc;
310    use std::sync::Arc;
311
312    #[test]
313    fn test_integral_ranges() -> Result<()> {
314        let db = Connection::open_in_memory()?;
315
316        fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64])
317        where
318            T: Into<i64> + FromSql + std::fmt::Debug,
319        {
320            for n in out_of_range {
321                let err = db
322                    .query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
323                    .unwrap_err();
324                match err {
325                    Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
326                    _ => panic!("unexpected error: {err}"),
327                }
328            }
329            for n in in_range {
330                assert_eq!(
331                    *n,
332                    db.query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
333                        .unwrap()
334                        .into()
335                );
336            }
337        }
338
339        check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]);
340        check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
341        check_ranges::<i32>(
342            &db,
343            &[-2_147_483_649, 2_147_483_648],
344            &[-2_147_483_648, -1, 0, 1, 2_147_483_647],
345        );
346        check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]);
347        check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]);
348        check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
349        Ok(())
350    }
351
352    #[test]
353    fn test_nonzero_ranges() -> Result<()> {
354        let db = Connection::open_in_memory()?;
355
356        macro_rules! check_ranges {
357            ($nz:ty, $out_of_range:expr, $in_range:expr) => {
358                for &n in $out_of_range {
359                    assert_eq!(
360                        db.query_row("SELECT ?1", [n], |r| r.get::<_, $nz>(0)),
361                        Err(Error::IntegralValueOutOfRange(0, n)),
362                        "{}",
363                        std::any::type_name::<$nz>()
364                    );
365                }
366                for &n in $in_range {
367                    let non_zero = <$nz>::new(n).unwrap();
368                    assert_eq!(
369                        Ok(non_zero),
370                        db.query_row("SELECT ?1", [non_zero], |r| r.get::<_, $nz>(0))
371                    );
372                }
373            };
374        }
375
376        check_ranges!(std::num::NonZeroI8, &[0, -129, 128], &[-128, 1, 127]);
377        check_ranges!(
378            std::num::NonZeroI16,
379            &[0, -32769, 32768],
380            &[-32768, -1, 1, 32767]
381        );
382        check_ranges!(
383            std::num::NonZeroI32,
384            &[0, -2_147_483_649, 2_147_483_648],
385            &[-2_147_483_648, -1, 1, 2_147_483_647]
386        );
387        check_ranges!(
388            std::num::NonZeroI64,
389            &[0],
390            &[-2_147_483_648, -1, 1, 2_147_483_647, i64::MAX, i64::MIN]
391        );
392        check_ranges!(
393            std::num::NonZeroIsize,
394            &[0],
395            &[-2_147_483_648, -1, 1, 2_147_483_647]
396        );
397        check_ranges!(std::num::NonZeroU8, &[0, -2, -1, 256], &[1, 255]);
398        check_ranges!(std::num::NonZeroU16, &[0, -2, -1, 65536], &[1, 65535]);
399        check_ranges!(
400            std::num::NonZeroU32,
401            &[0, -2, -1, 4_294_967_296],
402            &[1, 4_294_967_295]
403        );
404        #[cfg(feature = "fallible_uint")]
405        check_ranges!(
406            std::num::NonZeroU64,
407            &[0, -2, -1, -4_294_967_296],
408            &[1, 4_294_967_295, i64::MAX as u64]
409        );
410        #[cfg(feature = "fallible_uint")]
411        check_ranges!(
412            std::num::NonZeroUsize,
413            &[0, -2, -1, -4_294_967_296],
414            &[1, 4_294_967_295]
415        );
416
417        Ok(())
418    }
419
420    #[test]
421    fn test_cow() -> Result<()> {
422        let db = Connection::open_in_memory()?;
423
424        assert_eq!(
425            db.query_row("SELECT 'this is a string'", [], |r| r
426                .get::<_, Cow<'_, str>>(0)),
427            Ok(Cow::Borrowed("this is a string")),
428        );
429        assert_eq!(
430            db.query_row("SELECT x'09ab20fdee87'", [], |r| r
431                .get::<_, Cow<'_, [u8]>>(0)),
432            Ok(Cow::Owned(vec![0x09, 0xab, 0x20, 0xfd, 0xee, 0x87])),
433        );
434        assert_eq!(
435            db.query_row("SELECT 24.5", [], |r| r.get::<_, Cow<'_, f32>>(0),),
436            Ok(Cow::Borrowed(&24.5)),
437        );
438
439        Ok(())
440    }
441
442    #[test]
443    fn test_heap_slice() -> Result<()> {
444        let db = Connection::open_in_memory()?;
445
446        assert_eq!(
447            db.query_row("SELECT 'text'", [], |r| r.get::<_, Box<str>>(0)),
448            Ok(Box::from("text")),
449        );
450        assert_eq!(
451            db.query_row("SELECT 'Some string slice!'", [], |r| r
452                .get::<_, Rc<str>>(0)),
453            Ok(Rc::from("Some string slice!")),
454        );
455        assert_eq!(
456            db.query_row("SELECT x'012366779988fedc'", [], |r| r
457                .get::<_, Rc<[u8]>>(0)),
458            Ok(Rc::from(b"\x01\x23\x66\x77\x99\x88\xfe\xdc".as_slice())),
459        );
460
461        assert_eq!(
462            db.query_row(
463                "SELECT x'6120737472696e672043414e206265206120626c6f62'",
464                [],
465                |r| r.get::<_, Box<[u8]>>(0)
466            ),
467            Ok(b"a string CAN be a blob".to_vec().into_boxed_slice()),
468        );
469        assert_eq!(
470            db.query_row("SELECT 'This is inside an Arc.'", [], |r| r
471                .get::<_, Arc<str>>(0)),
472            Ok(Arc::from("This is inside an Arc.")),
473        );
474        assert_eq!(
475            db.query_row("SELECT x'afd374'", [], |r| r.get::<_, Arc<[u8]>>(0),),
476            Ok(Arc::from(b"\xaf\xd3\x74".as_slice())),
477        );
478
479        Ok(())
480    }
481
482    #[test]
483    fn from_sql_error() {
484        use std::error::Error as _;
485        assert_ne!(FromSqlError::InvalidType, FromSqlError::OutOfRange(0));
486        assert_ne!(FromSqlError::OutOfRange(0), FromSqlError::OutOfRange(1));
487        assert_ne!(
488            FromSqlError::InvalidBlobSize {
489                expected_size: 0,
490                blob_size: 0
491            },
492            FromSqlError::InvalidBlobSize {
493                expected_size: 0,
494                blob_size: 1
495            }
496        );
497        assert!(FromSqlError::InvalidType.source().is_none());
498        let err = std::io::Error::from(std::io::ErrorKind::UnexpectedEof);
499        assert!(FromSqlError::Other(Box::new(err)).source().is_some());
500    }
501}