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