1use super::{Value, ValueRef};
2use std::borrow::Cow;
3use std::error::Error;
4use std::fmt;
5
6#[derive(Debug)]
8#[non_exhaustive]
9pub enum FromSqlError {
10 InvalidType,
13
14 OutOfRange(i64),
17
18 InvalidBlobSize {
21 expected_size: usize,
23 blob_size: usize,
25 },
26
27 Other(Box<dyn Error + Send + Sync + 'static>),
29}
30
31impl PartialEq for FromSqlError {
32 fn eq(&self, other: &Self) -> bool {
33 match (self, other) {
34 (Self::InvalidType, Self::InvalidType) => true,
35 (Self::OutOfRange(n1), Self::OutOfRange(n2)) => n1 == n2,
36 (
37 Self::InvalidBlobSize {
38 expected_size: es1,
39 blob_size: bs1,
40 },
41 Self::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 Self::InvalidType => write!(f, "Invalid type"),
55 Self::OutOfRange(i) => write!(f, "Value {i} out of range"),
56 Self::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 Self::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 Self::Other(ref err) = self {
73 Some(&**err)
74 } else {
75 None
76 }
77 }
78}
79
80pub type FromSqlResult<T> = Result<T, FromSqlError>;
82
83pub trait FromSql: Sized {
85 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);
113from_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);
135impl 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 Self),
149 ValueRef::Real(f) => Ok(f as Self),
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 Self),
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 FromSql for Box<[u8]> {
209 #[inline]
210 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
211 value.as_blob().map(Box::<[u8]>::from)
212 }
213}
214
215impl FromSql for std::rc::Rc<[u8]> {
216 #[inline]
217 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
218 value.as_blob().map(std::rc::Rc::<[u8]>::from)
219 }
220}
221
222impl FromSql for std::sync::Arc<[u8]> {
223 #[inline]
224 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
225 value.as_blob().map(std::sync::Arc::<[u8]>::from)
226 }
227}
228
229impl<const N: usize> FromSql for [u8; N] {
230 #[inline]
231 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
232 let slice = value.as_blob()?;
233 slice.try_into().map_err(|_| FromSqlError::InvalidBlobSize {
234 expected_size: N,
235 blob_size: slice.len(),
236 })
237 }
238}
239
240#[cfg(feature = "i128_blob")]
241#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
242impl FromSql for i128 {
243 #[inline]
244 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
245 let bytes = <[u8; 16]>::column_result(value)?;
246 Ok(Self::from_be_bytes(bytes) ^ (1_i128 << 127))
247 }
248}
249
250#[cfg(feature = "uuid")]
251#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
252impl FromSql for uuid::Uuid {
253 #[inline]
254 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
255 let bytes = <[u8; 16]>::column_result(value)?;
256 Ok(Self::from_u128(u128::from_be_bytes(bytes)))
257 }
258}
259
260impl<T: FromSql> FromSql for Option<T> {
261 #[inline]
262 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
263 match value {
264 ValueRef::Null => Ok(None),
265 _ => FromSql::column_result(value).map(Some),
266 }
267 }
268}
269
270impl<T: ?Sized> FromSql for Cow<'_, T>
271where
272 T: ToOwned,
273 T::Owned: FromSql,
274{
275 #[inline]
276 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
277 <T::Owned>::column_result(value).map(Cow::Owned)
278 }
279}
280
281impl FromSql for Value {
282 #[inline]
283 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
284 Ok(value.into())
285 }
286}
287
288#[cfg(test)]
289mod test {
290 use super::FromSql;
291 use crate::{Connection, Error, Result};
292 use std::borrow::Cow;
293 use std::rc::Rc;
294 use std::sync::Arc;
295
296 #[test]
297 fn test_integral_ranges() -> Result<()> {
298 let db = Connection::open_in_memory()?;
299
300 fn check_ranges<T>(db: &Connection, out_of_range: &[i64], in_range: &[i64])
301 where
302 T: Into<i64> + FromSql + std::fmt::Debug,
303 {
304 for n in out_of_range {
305 let err = db
306 .query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
307 .unwrap_err();
308 match err {
309 Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
310 _ => panic!("unexpected error: {err}"),
311 }
312 }
313 for n in in_range {
314 assert_eq!(
315 *n,
316 db.query_row("SELECT ?1", [n], |r| r.get::<_, T>(0))
317 .unwrap()
318 .into()
319 );
320 }
321 }
322
323 check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]);
324 check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
325 check_ranges::<i32>(
326 &db,
327 &[-2_147_483_649, 2_147_483_648],
328 &[-2_147_483_648, -1, 0, 1, 2_147_483_647],
329 );
330 check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]);
331 check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]);
332 check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
333 Ok(())
334 }
335
336 #[test]
337 fn test_nonzero_ranges() -> Result<()> {
338 let db = Connection::open_in_memory()?;
339
340 macro_rules! check_ranges {
341 ($nz:ty, $out_of_range:expr, $in_range:expr) => {
342 for &n in $out_of_range {
343 assert_eq!(
344 db.query_row("SELECT ?1", [n], |r| r.get::<_, $nz>(0)),
345 Err(Error::IntegralValueOutOfRange(0, n)),
346 "{}",
347 std::any::type_name::<$nz>()
348 );
349 }
350 for &n in $in_range {
351 let non_zero = <$nz>::new(n).unwrap();
352 assert_eq!(
353 Ok(non_zero),
354 db.query_row("SELECT ?1", [non_zero], |r| r.get::<_, $nz>(0))
355 );
356 }
357 };
358 }
359
360 check_ranges!(std::num::NonZeroI8, &[0, -129, 128], &[-128, 1, 127]);
361 check_ranges!(
362 std::num::NonZeroI16,
363 &[0, -32769, 32768],
364 &[-32768, -1, 1, 32767]
365 );
366 check_ranges!(
367 std::num::NonZeroI32,
368 &[0, -2_147_483_649, 2_147_483_648],
369 &[-2_147_483_648, -1, 1, 2_147_483_647]
370 );
371 check_ranges!(
372 std::num::NonZeroI64,
373 &[0],
374 &[-2_147_483_648, -1, 1, 2_147_483_647, i64::MAX, i64::MIN]
375 );
376 check_ranges!(
377 std::num::NonZeroIsize,
378 &[0],
379 &[-2_147_483_648, -1, 1, 2_147_483_647]
380 );
381 check_ranges!(std::num::NonZeroU8, &[0, -2, -1, 256], &[1, 255]);
382 check_ranges!(std::num::NonZeroU16, &[0, -2, -1, 65536], &[1, 65535]);
383 check_ranges!(
384 std::num::NonZeroU32,
385 &[0, -2, -1, 4_294_967_296],
386 &[1, 4_294_967_295]
387 );
388 check_ranges!(
389 std::num::NonZeroU64,
390 &[0, -2, -1, -4_294_967_296],
391 &[1, 4_294_967_295, i64::MAX as u64]
392 );
393 check_ranges!(
394 std::num::NonZeroUsize,
395 &[0, -2, -1, -4_294_967_296],
396 &[1, 4_294_967_295]
397 );
398
399 Ok(())
400 }
401
402 #[test]
403 fn test_cow() -> Result<()> {
404 let db = Connection::open_in_memory()?;
405
406 assert_eq!(
407 db.query_row("SELECT 'this is a string'", [], |r| r
408 .get::<_, Cow<'_, str>>(0)),
409 Ok(Cow::Borrowed("this is a string")),
410 );
411 assert_eq!(
412 db.query_row("SELECT x'09ab20fdee87'", [], |r| r
413 .get::<_, Cow<'_, [u8]>>(0)),
414 Ok(Cow::Owned(vec![0x09, 0xab, 0x20, 0xfd, 0xee, 0x87])),
415 );
416 assert_eq!(
417 db.query_row("SELECT 24.5", [], |r| r.get::<_, Cow<'_, f32>>(0),),
418 Ok(Cow::Borrowed(&24.5)),
419 );
420
421 Ok(())
422 }
423
424 #[test]
425 fn test_heap_slice() -> Result<()> {
426 let db = Connection::open_in_memory()?;
427
428 assert_eq!(
429 db.query_row("SELECT 'Some string slice!'", [], |r| r
430 .get::<_, Rc<str>>(0)),
431 Ok(Rc::from("Some string slice!")),
432 );
433 assert_eq!(
434 db.query_row("SELECT x'012366779988fedc'", [], |r| r
435 .get::<_, Rc<[u8]>>(0)),
436 Ok(Rc::from(b"\x01\x23\x66\x77\x99\x88\xfe\xdc".as_slice())),
437 );
438
439 assert_eq!(
440 db.query_row(
441 "SELECT x'6120737472696e672043414e206265206120626c6f62'",
442 [],
443 |r| r.get::<_, Box<[u8]>>(0)
444 ),
445 Ok(b"a string CAN be a blob".to_vec().into_boxed_slice()),
446 );
447 assert_eq!(
448 db.query_row("SELECT 'This is inside an Arc.'", [], |r| r
449 .get::<_, Arc<str>>(0)),
450 Ok(Arc::from("This is inside an Arc.")),
451 );
452 assert_eq!(
453 db.query_row("SELECT x'afd374'", [], |r| r.get::<_, Arc<[u8]>>(0),),
454 Ok(Arc::from(b"\xaf\xd3\x74".as_slice())),
455 );
456
457 Ok(())
458 }
459}