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