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