1extern crate cast;
2
3use super::{TimeUnit, Value, ValueRef};
4use std::{error::Error, fmt};
5
6#[derive(Debug)]
8#[non_exhaustive]
9pub enum FromSqlError {
10 InvalidType,
13
14 OutOfRange(i128),
17
18 #[cfg(feature = "uuid")]
21 InvalidUuidSize(usize),
22
23 Other(Box<dyn Error + Send + Sync + 'static>),
25}
26
27impl PartialEq for FromSqlError {
28 fn eq(&self, other: &FromSqlError) -> bool {
29 match (self, other) {
30 (FromSqlError::InvalidType, FromSqlError::InvalidType) => true,
31 (FromSqlError::OutOfRange(n1), FromSqlError::OutOfRange(n2)) => n1 == n2,
32 #[cfg(feature = "uuid")]
33 (FromSqlError::InvalidUuidSize(s1), FromSqlError::InvalidUuidSize(s2)) => s1 == s2,
34 (..) => false,
35 }
36 }
37}
38
39impl fmt::Display for FromSqlError {
40 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41 match *self {
42 FromSqlError::InvalidType => write!(f, "Invalid type"),
43 FromSqlError::OutOfRange(i) => write!(f, "Value {i} out of range"),
44 #[cfg(feature = "uuid")]
45 FromSqlError::InvalidUuidSize(s) => {
46 write!(f, "Cannot read UUID value out of {s} byte blob")
47 }
48 FromSqlError::Other(ref err) => err.fmt(f),
49 }
50 }
51}
52
53impl Error for FromSqlError {
54 fn source(&self) -> Option<&(dyn Error + 'static)> {
55 if let FromSqlError::Other(ref err) = self {
56 Some(&**err)
57 } else {
58 None
59 }
60 }
61}
62
63pub type FromSqlResult<T> = Result<T, FromSqlError>;
65
66pub trait FromSql: Sized {
68 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self>;
70}
71
72macro_rules! from_sql_integral(
73 ($t:ident) => (
74 impl FromSql for $t {
75 #[inline]
76 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
77 match value {
78 ValueRef::TinyInt(i) => Ok(<$t as cast::From<i8>>::cast(i).unwrap()),
80 ValueRef::SmallInt(i) => Ok(<$t as cast::From<i16>>::cast(i).unwrap()),
81 ValueRef::Int(i) => Ok(<$t as cast::From<i32>>::cast(i).unwrap()),
82 ValueRef::BigInt(i) => Ok(<$t as cast::From<i64>>::cast(i).unwrap()),
83 ValueRef::HugeInt(i) => {
84 let v = <$t as cast::From<i128>>::cast(i);
85 if v.is_ok() {
86 Ok(v.unwrap())
87 } else {
88 Err(FromSqlError::OutOfRange(i))
89 }
90 },
91
92 ValueRef::UTinyInt(i) => Ok(<$t as cast::From<u8>>::cast(i).unwrap()),
93 ValueRef::USmallInt(i) => Ok(<$t as cast::From<u16>>::cast(i).unwrap()),
94 ValueRef::UInt(i) => Ok(<$t as cast::From<u32>>::cast(i).unwrap()),
95 ValueRef::UBigInt(i) => Ok(<$t as cast::From<u64>>::cast(i).unwrap()),
96
97 ValueRef::Float(i) => Ok(<$t as cast::From<f32>>::cast(i).unwrap()),
98 ValueRef::Double(i) => Ok(<$t as cast::From<f64>>::cast(i).unwrap()),
99
100 ValueRef::Decimal(i) => Ok(i.to_string().parse::<$t>().unwrap()),
102
103 ValueRef::Timestamp(_, i) => Ok(<$t as cast::From<i64>>::cast(i).unwrap()),
104 ValueRef::Date32(i) => Ok(<$t as cast::From<i32>>::cast(i).unwrap()),
105 ValueRef::Time64(TimeUnit::Microsecond, i) => Ok(<$t as cast::From<i64>>::cast(i).unwrap()),
106 ValueRef::Text(_) => {
107 let v = value.as_str()?.parse::<$t>();
108 match v {
109 Ok(i) => Ok(i),
110 Err(_) => {
111 let v = value.as_str()?.parse::<i128>();
112 match v {
113 Ok(i) => Err(FromSqlError::OutOfRange(i)),
114 _ => Err(FromSqlError::InvalidType),
115 }
116 },
117 }
118 }
119 _ => Err(FromSqlError::InvalidType),
120 }
121 }
122 }
123 )
124);
125
126trait Unwrap {
132 fn unwrap(self) -> Self;
133 fn is_ok(&self) -> bool;
134}
135
136macro_rules! unwrap_integral(
137 ($t:ident) => (
138 impl Unwrap for $t {
139 #[inline]
140 fn unwrap(self) -> Self {
141 self
142 }
143
144 #[inline]
145 fn is_ok(&self) -> bool {
146 true
147 }
148 }
149 )
150);
151
152unwrap_integral!(i8);
153unwrap_integral!(i16);
154unwrap_integral!(i32);
155unwrap_integral!(i64);
156unwrap_integral!(i128);
157unwrap_integral!(isize);
158unwrap_integral!(u8);
159unwrap_integral!(u16);
160unwrap_integral!(u32);
161unwrap_integral!(u64);
162unwrap_integral!(usize);
163unwrap_integral!(f32);
164unwrap_integral!(f64);
165
166from_sql_integral!(i8);
167from_sql_integral!(i16);
168from_sql_integral!(i32);
169from_sql_integral!(i64);
170from_sql_integral!(i128);
171from_sql_integral!(isize);
172from_sql_integral!(u8);
173from_sql_integral!(u16);
174from_sql_integral!(u32);
175from_sql_integral!(u64);
176from_sql_integral!(usize);
177from_sql_integral!(f32);
178from_sql_integral!(f64);
179
180impl FromSql for bool {
181 #[inline]
182 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
183 match value {
184 ValueRef::Boolean(b) => Ok(b),
185 _ => i8::column_result(value).map(|i| i != 0),
186 }
187 }
188}
189
190impl FromSql for String {
191 #[inline]
192 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
193 match value {
194 #[cfg(feature = "chrono")]
195 ValueRef::Date32(_) => Ok(chrono::NaiveDate::column_result(value)?.format("%F").to_string()),
196 #[cfg(feature = "chrono")]
197 ValueRef::Time64(..) => Ok(chrono::NaiveTime::column_result(value)?.format("%T%.f").to_string()),
198 #[cfg(feature = "chrono")]
199 ValueRef::Timestamp(..) => Ok(chrono::NaiveDateTime::column_result(value)?
200 .format("%F %T%.f")
201 .to_string()),
202 _ => value.as_str().map(ToString::to_string),
203 }
204 }
205}
206
207impl FromSql for Box<str> {
208 #[inline]
209 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
210 value.as_str().map(Into::into)
211 }
212}
213
214impl FromSql for std::rc::Rc<str> {
215 #[inline]
216 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
217 value.as_str().map(Into::into)
218 }
219}
220
221impl FromSql for std::sync::Arc<str> {
222 #[inline]
223 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
224 value.as_str().map(Into::into)
225 }
226}
227
228impl FromSql for Vec<u8> {
229 #[inline]
230 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
231 value.as_blob().map(|b| b.to_vec())
232 }
233}
234
235#[cfg(feature = "uuid")]
236impl FromSql for uuid::Uuid {
237 #[inline]
238 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
239 match value {
240 ValueRef::Text(..) => value
241 .as_str()
242 .and_then(|s| uuid::Uuid::parse_str(s).map_err(|_| FromSqlError::InvalidUuidSize(s.len()))),
243 ValueRef::Blob(..) => value
244 .as_blob()
245 .and_then(|bytes| {
246 uuid::Builder::from_slice(bytes).map_err(|_| FromSqlError::InvalidUuidSize(bytes.len()))
247 })
248 .map(|builder| builder.into_uuid()),
249 _ => Err(FromSqlError::InvalidType),
250 }
251 }
252}
253
254impl<T: FromSql> FromSql for Option<T> {
255 #[inline]
256 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
257 match value {
258 ValueRef::Null => Ok(None),
259 _ => FromSql::column_result(value).map(Some),
260 }
261 }
262}
263
264impl FromSql for Value {
265 #[inline]
266 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
267 Ok(value.into())
268 }
269}
270
271#[cfg(test)]
272mod test {
273 use super::FromSql;
274 use crate::{Connection, Error, Result};
275
276 #[test]
277 fn test_timestamp_raw() -> Result<()> {
278 let db = Connection::open_in_memory()?;
279 let sql = "BEGIN;
280 CREATE TABLE ts (sec TIMESTAMP_S, milli TIMESTAMP_MS, micro TIMESTAMP_US, nano TIMESTAMP_NS );
281 INSERT INTO ts VALUES (NULL,NULL,NULL,NULL );
282 INSERT INTO ts VALUES ('2008-01-01 00:00:01','2008-01-01 00:00:01.594','2008-01-01 00:00:01.88926','2008-01-01 00:00:01.889268000' );
283 -- INSERT INTO ts VALUES (NULL,NULL,NULL,1199145601889268321 );
284 END;";
285 db.execute_batch(sql)?;
286 let v = db.query_row(
287 "SELECT sec, milli, micro, nano FROM ts WHERE sec is not null",
288 [],
289 |row| <(i64, i64, i64, i64)>::try_from(row),
290 )?;
291 assert_eq!(v, (1199145601, 1199145601594, 1199145601889260, 1199145601889268000));
292 Ok(())
293 }
294
295 #[test]
296 fn test_time64_raw() -> Result<()> {
297 let db = Connection::open_in_memory()?;
298 let sql = "BEGIN;
299 CREATE TABLE time64 (t time);
300 INSERT INTO time64 VALUES ('20:08:10.998');
301 END;";
302 db.execute_batch(sql)?;
303 let v = db.query_row("SELECT * FROM time64", [], |row| <(i64,)>::try_from(row))?;
304 assert_eq!(v, (72490998000,));
305 Ok(())
306 }
307
308 #[test]
309 fn test_date32_raw() -> Result<()> {
310 let db = Connection::open_in_memory()?;
311 let sql = "BEGIN;
312 CREATE TABLE date32 (d date);
313 INSERT INTO date32 VALUES ('2008-01-01');
314 END;";
315 db.execute_batch(sql)?;
316 let v = db.query_row("SELECT * FROM date32", [], |row| <(i32,)>::try_from(row))?;
317 assert_eq!(v, (13879,));
318 Ok(())
319 }
320
321 #[test]
322 fn test_unsigned_integer() -> Result<()> {
323 let db = Connection::open_in_memory()?;
324 let sql = "BEGIN;
325 CREATE TABLE unsigned_int (u1 utinyint, u2 usmallint, u4 uinteger, u8 ubigint);
326 INSERT INTO unsigned_int VALUES (255, 65535, 4294967295, 18446744073709551615);
327 END;";
328 db.execute_batch(sql)?;
329 let v = db.query_row("SELECT * FROM unsigned_int", [], |row| {
330 <(u8, u16, u32, u64)>::try_from(row)
331 })?;
332 assert_eq!(v, (255, 65535, 4294967295, 18446744073709551615));
333 Ok(())
334 }
335
336 #[test]
338 fn test_hugeint_max_min() -> Result<()> {
339 let db = Connection::open_in_memory()?;
340 db.execute("CREATE TABLE huge_int (u1 hugeint, u2 hugeint);", [])?;
341 let i128max: i128 = i128::MAX;
343 let i128min: i128 = i128::MIN + 1;
344 db.execute("INSERT INTO huge_int VALUES (?, ?);", [&i128max, &i128min])?;
345 let v = db.query_row("SELECT * FROM huge_int", [], |row| <(i128, i128)>::try_from(row))?;
346 assert_eq!(v, (i128max, i128min));
347 Ok(())
348 }
349
350 #[test]
351 fn test_integral_ranges() -> Result<()> {
352 let db = Connection::open_in_memory()?;
353
354 fn check_ranges<T>(db: &Connection, out_of_range: &[i128], in_range: &[i128])
355 where
356 T: Into<i128> + FromSql + ::std::fmt::Debug,
357 {
358 for n in out_of_range {
359 let err = db.query_row("SELECT ?", [n], |r| r.get::<_, T>(0)).unwrap_err();
360 match err {
361 Error::IntegralValueOutOfRange(_, value) => assert_eq!(*n, value),
362 _ => panic!("unexpected error: {err}"),
363 }
364 }
365 for n in in_range {
366 assert_eq!(*n, db.query_row("SELECT ?", [n], |r| r.get::<_, T>(0)).unwrap().into());
367 }
368 }
369
370 check_ranges::<i8>(&db, &[-129, 128], &[-128, 0, 1, 127]);
371 check_ranges::<i16>(&db, &[-32769, 32768], &[-32768, -1, 0, 1, 32767]);
372 check_ranges::<i32>(
373 &db,
374 &[-2_147_483_649, 2_147_483_648],
375 &[-2_147_483_648, -1, 0, 1, 2_147_483_647],
376 );
377 check_ranges::<u8>(&db, &[-2, -1, 256], &[0, 1, 255]);
378 check_ranges::<u16>(&db, &[-2, -1, 65536], &[0, 1, 65535]);
379 check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
380 Ok(())
381 }
382
383 #[test]
385 fn test_uuid_string() -> Result<()> {
386 let db = Connection::open_in_memory()?;
387 let sql = "BEGIN;
388 CREATE TABLE uuid (u uuid);
389 INSERT INTO uuid VALUES ('10203040-5060-7080-0102-030405060708'),(NULL),('47183823-2574-4bfd-b411-99ed177d3e43');
390 END;";
391 db.execute_batch(sql)?;
392 let v = db.query_row("SELECT u FROM uuid order by u desc nulls last limit 1", [], |row| {
393 <(String,)>::try_from(row)
394 })?;
395 assert_eq!(v, ("47183823-2574-4bfd-b411-99ed177d3e43".to_string(),));
396 let v = db.query_row(
397 "SELECT u FROM uuid where u>?::UUID",
398 ["10203040-5060-7080-0102-030405060708"],
399 |row| <(String,)>::try_from(row),
400 )?;
401 assert_eq!(v, ("47183823-2574-4bfd-b411-99ed177d3e43".to_string(),));
402 Ok(())
403 }
404
405 #[cfg(feature = "uuid")]
406 #[test]
407 fn test_uuid_from_string() -> crate::Result<()> {
408 let db = Connection::open_in_memory()?;
409 let sql = "BEGIN;
410 CREATE TABLE uuid (u uuid);
411 INSERT INTO uuid VALUES ('10203040-5060-7080-0102-030405060708'),(NULL),('47183823-2574-4bfd-b411-99ed177d3e43');
412 END;";
413 db.execute_batch(sql)?;
414 let v = db.query_row("SELECT u FROM uuid order by u desc nulls last limit 1", [], |row| {
415 <(uuid::Uuid,)>::try_from(row)
416 })?;
417 assert_eq!(v.0.to_string(), "47183823-2574-4bfd-b411-99ed177d3e43");
418 Ok(())
419 }
420}