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