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 FromSqlError {
32 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
92pub type FromSqlResult<T> = Result<T, FromSqlError>;
94
95pub trait FromSql: Sized {
97 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);
125from_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);
150impl 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}