• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use super::{Null, Value, ValueRef};
2 #[cfg(feature = "array")]
3 use crate::vtab::array::Array;
4 use crate::{Error, Result};
5 use std::borrow::Cow;
6 
7 /// `ToSqlOutput` represents the possible output types for implementers of the
8 /// [`ToSql`] trait.
9 #[derive(Clone, Debug, PartialEq)]
10 #[non_exhaustive]
11 pub enum ToSqlOutput<'a> {
12     /// A borrowed SQLite-representable value.
13     Borrowed(ValueRef<'a>),
14 
15     /// An owned SQLite-representable value.
16     Owned(Value),
17 
18     /// A BLOB of the given length that is filled with
19     /// zeroes.
20     #[cfg(feature = "blob")]
21     #[cfg_attr(docsrs, doc(cfg(feature = "blob")))]
22     ZeroBlob(i32),
23 
24     /// n-th arg of an SQL scalar function
25     #[cfg(feature = "functions")]
26     #[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
27     Arg(usize),
28 
29     /// `feature = "array"`
30     #[cfg(feature = "array")]
31     #[cfg_attr(docsrs, doc(cfg(feature = "array")))]
32     Array(Array),
33 }
34 
35 // Generically allow any type that can be converted into a ValueRef
36 // to be converted into a ToSqlOutput as well.
37 impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
38 where
39     &'a T: Into<ValueRef<'a>>,
40 {
41     #[inline]
from(t: &'a T) -> Self42     fn from(t: &'a T) -> Self {
43         ToSqlOutput::Borrowed(t.into())
44     }
45 }
46 
47 // We cannot also generically allow any type that can be converted
48 // into a Value to be converted into a ToSqlOutput because of
49 // coherence rules (https://github.com/rust-lang/rust/pull/46192),
50 // so we'll manually implement it for all the types we know can
51 // be converted into Values.
52 macro_rules! from_value(
53     ($t:ty) => (
54         impl From<$t> for ToSqlOutput<'_> {
55             #[inline]
56             fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
57         }
58     );
59     (non_zero $t:ty) => (
60         impl From<$t> for ToSqlOutput<'_> {
61             #[inline]
62             fn from(t: $t) -> Self { ToSqlOutput::Owned(t.get().into())}
63         }
64     )
65 );
66 from_value!(String);
67 from_value!(Null);
68 from_value!(bool);
69 from_value!(i8);
70 from_value!(i16);
71 from_value!(i32);
72 from_value!(i64);
73 from_value!(isize);
74 from_value!(u8);
75 from_value!(u16);
76 from_value!(u32);
77 from_value!(f32);
78 from_value!(f64);
79 from_value!(Vec<u8>);
80 
81 from_value!(non_zero std::num::NonZeroI8);
82 from_value!(non_zero std::num::NonZeroI16);
83 from_value!(non_zero std::num::NonZeroI32);
84 from_value!(non_zero std::num::NonZeroI64);
85 from_value!(non_zero std::num::NonZeroIsize);
86 from_value!(non_zero std::num::NonZeroU8);
87 from_value!(non_zero std::num::NonZeroU16);
88 from_value!(non_zero std::num::NonZeroU32);
89 
90 // It would be nice if we could avoid the heap allocation (of the `Vec`) that
91 // `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not
92 // worth adding another case to Value.
93 #[cfg(feature = "i128_blob")]
94 #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
95 from_value!(i128);
96 
97 #[cfg(feature = "i128_blob")]
98 #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
99 from_value!(non_zero std::num::NonZeroI128);
100 
101 #[cfg(feature = "uuid")]
102 #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
103 from_value!(uuid::Uuid);
104 
105 impl ToSql for ToSqlOutput<'_> {
106     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>107     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
108         Ok(match *self {
109             ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
110             ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
111 
112             #[cfg(feature = "blob")]
113             ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
114             #[cfg(feature = "functions")]
115             ToSqlOutput::Arg(i) => ToSqlOutput::Arg(i),
116             #[cfg(feature = "array")]
117             ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
118         })
119     }
120 }
121 
122 /// A trait for types that can be converted into SQLite values. Returns
123 /// [`Error::ToSqlConversionFailure`] if the conversion fails.
124 pub trait ToSql {
125     /// Converts Rust value to SQLite value
to_sql(&self) -> Result<ToSqlOutput<'_>>126     fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
127 }
128 
129 impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
130     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>131     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
132         self.as_ref().to_sql()
133     }
134 }
135 
136 impl<T: ToSql + ?Sized> ToSql for Box<T> {
137     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>138     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
139         self.as_ref().to_sql()
140     }
141 }
142 
143 impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
144     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>145     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
146         self.as_ref().to_sql()
147     }
148 }
149 
150 impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
151     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>152     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
153         self.as_ref().to_sql()
154     }
155 }
156 
157 // We should be able to use a generic impl like this:
158 //
159 // impl<T: Copy> ToSql for T where T: Into<Value> {
160 //     fn to_sql(&self) -> Result<ToSqlOutput> {
161 //         Ok(ToSqlOutput::from((*self).into()))
162 //     }
163 // }
164 //
165 // instead of the following macro, but this runs afoul of
166 // https://github.com/rust-lang/rust/issues/30191 and reports conflicting
167 // implementations even when there aren't any.
168 
169 macro_rules! to_sql_self(
170     ($t:ty) => (
171         impl ToSql for $t {
172             #[inline]
173             fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
174                 Ok(ToSqlOutput::from(*self))
175             }
176         }
177     )
178 );
179 
180 to_sql_self!(Null);
181 to_sql_self!(bool);
182 to_sql_self!(i8);
183 to_sql_self!(i16);
184 to_sql_self!(i32);
185 to_sql_self!(i64);
186 to_sql_self!(isize);
187 to_sql_self!(u8);
188 to_sql_self!(u16);
189 to_sql_self!(u32);
190 to_sql_self!(f32);
191 to_sql_self!(f64);
192 
193 to_sql_self!(std::num::NonZeroI8);
194 to_sql_self!(std::num::NonZeroI16);
195 to_sql_self!(std::num::NonZeroI32);
196 to_sql_self!(std::num::NonZeroI64);
197 to_sql_self!(std::num::NonZeroIsize);
198 to_sql_self!(std::num::NonZeroU8);
199 to_sql_self!(std::num::NonZeroU16);
200 to_sql_self!(std::num::NonZeroU32);
201 
202 #[cfg(feature = "i128_blob")]
203 #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
204 to_sql_self!(i128);
205 
206 #[cfg(feature = "i128_blob")]
207 #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
208 to_sql_self!(std::num::NonZeroI128);
209 
210 #[cfg(feature = "uuid")]
211 #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
212 to_sql_self!(uuid::Uuid);
213 
214 macro_rules! to_sql_self_fallible(
215     ($t:ty) => (
216         impl ToSql for $t {
217             #[inline]
218             fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
219                 Ok(ToSqlOutput::Owned(Value::Integer(
220                     i64::try_from(*self).map_err(
221                         // TODO: Include the values in the error message.
222                         |err| Error::ToSqlConversionFailure(err.into())
223                     )?
224                 )))
225             }
226         }
227     );
228     (non_zero $t:ty) => (
229         impl ToSql for $t {
230             #[inline]
231             fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
232                 Ok(ToSqlOutput::Owned(Value::Integer(
233                     i64::try_from(self.get()).map_err(
234                         // TODO: Include the values in the error message.
235                         |err| Error::ToSqlConversionFailure(err.into())
236                     )?
237                 )))
238             }
239         }
240     )
241 );
242 
243 // Special implementations for usize and u64 because these conversions can fail.
244 to_sql_self_fallible!(u64);
245 to_sql_self_fallible!(usize);
246 to_sql_self_fallible!(non_zero std::num::NonZeroU64);
247 to_sql_self_fallible!(non_zero std::num::NonZeroUsize);
248 
249 impl<T: ?Sized> ToSql for &'_ T
250 where
251     T: ToSql,
252 {
253     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>254     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
255         (*self).to_sql()
256     }
257 }
258 
259 impl ToSql for String {
260     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>261     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
262         Ok(ToSqlOutput::from(self.as_str()))
263     }
264 }
265 
266 impl ToSql for str {
267     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>268     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
269         Ok(ToSqlOutput::from(self))
270     }
271 }
272 
273 impl ToSql for Vec<u8> {
274     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>275     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
276         Ok(ToSqlOutput::from(self.as_slice()))
277     }
278 }
279 
280 impl<const N: usize> ToSql for [u8; N] {
281     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>282     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
283         Ok(ToSqlOutput::from(&self[..]))
284     }
285 }
286 
287 impl ToSql for [u8] {
288     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>289     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
290         Ok(ToSqlOutput::from(self))
291     }
292 }
293 
294 impl ToSql for Value {
295     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>296     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
297         Ok(ToSqlOutput::from(self))
298     }
299 }
300 
301 impl<T: ToSql> ToSql for Option<T> {
302     #[inline]
to_sql(&self) -> Result<ToSqlOutput<'_>>303     fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
304         match *self {
305             None => Ok(ToSqlOutput::from(Null)),
306             Some(ref t) => t.to_sql(),
307         }
308     }
309 }
310 
311 #[cfg(test)]
312 mod test {
313     use super::{ToSql, ToSqlOutput};
314     use crate::{types::Value, types::ValueRef, Result};
315 
is_to_sql<T: ToSql>()316     fn is_to_sql<T: ToSql>() {}
317 
318     #[test]
to_sql() -> Result<()>319     fn to_sql() -> Result<()> {
320         assert_eq!(
321             ToSqlOutput::Borrowed(ValueRef::Null).to_sql()?,
322             ToSqlOutput::Borrowed(ValueRef::Null)
323         );
324         assert_eq!(
325             ToSqlOutput::Owned(Value::Null).to_sql()?,
326             ToSqlOutput::Borrowed(ValueRef::Null)
327         );
328         Ok(())
329     }
330 
331     #[test]
test_integral_types()332     fn test_integral_types() {
333         is_to_sql::<i8>();
334         is_to_sql::<i16>();
335         is_to_sql::<i32>();
336         is_to_sql::<i64>();
337         is_to_sql::<isize>();
338         is_to_sql::<u8>();
339         is_to_sql::<u16>();
340         is_to_sql::<u32>();
341         is_to_sql::<u64>();
342         is_to_sql::<usize>();
343     }
344 
345     #[test]
test_nonzero_types()346     fn test_nonzero_types() {
347         is_to_sql::<std::num::NonZeroI8>();
348         is_to_sql::<std::num::NonZeroI16>();
349         is_to_sql::<std::num::NonZeroI32>();
350         is_to_sql::<std::num::NonZeroI64>();
351         is_to_sql::<std::num::NonZeroIsize>();
352         is_to_sql::<std::num::NonZeroU8>();
353         is_to_sql::<std::num::NonZeroU16>();
354         is_to_sql::<std::num::NonZeroU32>();
355         is_to_sql::<std::num::NonZeroU64>();
356         is_to_sql::<std::num::NonZeroUsize>();
357     }
358 
359     #[test]
test_u8_array()360     fn test_u8_array() {
361         let a: [u8; 99] = [0u8; 99];
362         let _a: &[&dyn ToSql] = crate::params![a];
363         let r = ToSql::to_sql(&a);
364 
365         r.unwrap();
366     }
367 
368     #[test]
test_cow_str()369     fn test_cow_str() {
370         use std::borrow::Cow;
371         let s = "str";
372         let cow: Cow<str> = Cow::Borrowed(s);
373         let r = cow.to_sql();
374         r.unwrap();
375         let cow: Cow<str> = Cow::Owned::<str>(String::from(s));
376         let r = cow.to_sql();
377         r.unwrap();
378         // Ensure this compiles.
379         let _p: &[&dyn ToSql] = crate::params![cow];
380     }
381 
382     #[test]
test_box_dyn()383     fn test_box_dyn() {
384         let s: Box<dyn ToSql> = Box::new("Hello world!");
385         let _s: &[&dyn ToSql] = crate::params![s];
386         let r = ToSql::to_sql(&s);
387 
388         r.unwrap();
389     }
390 
391     #[test]
test_box_deref()392     fn test_box_deref() {
393         let s: Box<str> = "Hello world!".into();
394         let _s: &[&dyn ToSql] = crate::params![s];
395         let r = s.to_sql();
396 
397         r.unwrap();
398     }
399 
400     #[test]
test_box_direct()401     fn test_box_direct() {
402         let s: Box<str> = "Hello world!".into();
403         let _s: &[&dyn ToSql] = crate::params![s];
404         let r = ToSql::to_sql(&s);
405 
406         r.unwrap();
407     }
408 
409     #[test]
test_cells()410     fn test_cells() {
411         use std::{rc::Rc, sync::Arc};
412 
413         let source_str: Box<str> = "Hello world!".into();
414 
415         let s: Rc<Box<str>> = Rc::new(source_str.clone());
416         let _s: &[&dyn ToSql] = crate::params![s];
417         let r = s.to_sql();
418         r.unwrap();
419 
420         let s: Arc<Box<str>> = Arc::new(source_str.clone());
421         let _s: &[&dyn ToSql] = crate::params![s];
422         let r = s.to_sql();
423         r.unwrap();
424 
425         let s: Arc<str> = Arc::from(&*source_str);
426         let _s: &[&dyn ToSql] = crate::params![s];
427         let r = s.to_sql();
428         r.unwrap();
429 
430         let s: Arc<dyn ToSql> = Arc::new(source_str.clone());
431         let _s: &[&dyn ToSql] = crate::params![s];
432         let r = s.to_sql();
433         r.unwrap();
434 
435         let s: Rc<str> = Rc::from(&*source_str);
436         let _s: &[&dyn ToSql] = crate::params![s];
437         let r = s.to_sql();
438         r.unwrap();
439 
440         let s: Rc<dyn ToSql> = Rc::new(source_str);
441         let _s: &[&dyn ToSql] = crate::params![s];
442         let r = s.to_sql();
443         r.unwrap();
444     }
445 
446     #[cfg(feature = "i128_blob")]
447     #[test]
test_i128() -> crate::Result<()>448     fn test_i128() -> crate::Result<()> {
449         use crate::Connection;
450         let db = Connection::open_in_memory()?;
451         db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
452         db.execute(
453             "
454             INSERT INTO foo(i128, desc) VALUES
455                 (?1, 'zero'),
456                 (?2, 'neg one'), (?3, 'neg two'),
457                 (?4, 'pos one'), (?5, 'pos two'),
458                 (?6, 'min'), (?7, 'max')",
459             [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
460         )?;
461 
462         let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
463 
464         let res = stmt
465             .query_map([], |row| {
466                 Ok((row.get::<_, i128>(0)?, row.get::<_, String>(1)?))
467             })?
468             .collect::<Result<Vec<_>, _>>()?;
469 
470         assert_eq!(
471             res,
472             &[
473                 (i128::MIN, "min".to_owned()),
474                 (-2, "neg two".to_owned()),
475                 (-1, "neg one".to_owned()),
476                 (0, "zero".to_owned()),
477                 (1, "pos one".to_owned()),
478                 (2, "pos two".to_owned()),
479                 (i128::MAX, "max".to_owned()),
480             ]
481         );
482         Ok(())
483     }
484 
485     #[cfg(feature = "i128_blob")]
486     #[test]
test_non_zero_i128() -> crate::Result<()>487     fn test_non_zero_i128() -> crate::Result<()> {
488         use std::num::NonZeroI128;
489         macro_rules! nz {
490             ($x:expr) => {
491                 NonZeroI128::new($x).unwrap()
492             };
493         }
494 
495         let db = crate::Connection::open_in_memory()?;
496         db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
497         db.execute(
498             "INSERT INTO foo(i128, desc) VALUES
499                 (?1, 'neg one'), (?2, 'neg two'),
500                 (?3, 'pos one'), (?4, 'pos two'),
501                 (?5, 'min'), (?6, 'max')",
502             [
503                 nz!(-1),
504                 nz!(-2),
505                 nz!(1),
506                 nz!(2),
507                 nz!(i128::MIN),
508                 nz!(i128::MAX),
509             ],
510         )?;
511         let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
512 
513         let res = stmt
514             .query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?
515             .collect::<Result<Vec<(NonZeroI128, String)>, _>>()?;
516 
517         assert_eq!(
518             res,
519             &[
520                 (nz!(i128::MIN), "min".to_owned()),
521                 (nz!(-2), "neg two".to_owned()),
522                 (nz!(-1), "neg one".to_owned()),
523                 (nz!(1), "pos one".to_owned()),
524                 (nz!(2), "pos two".to_owned()),
525                 (nz!(i128::MAX), "max".to_owned()),
526             ]
527         );
528         let err = db.query_row("SELECT ?1", [0i128], |row| row.get::<_, NonZeroI128>(0));
529         assert_eq!(err, Err(crate::Error::IntegralValueOutOfRange(0, 0)));
530         Ok(())
531     }
532 
533     #[cfg(feature = "uuid")]
534     #[test]
test_uuid() -> crate::Result<()>535     fn test_uuid() -> crate::Result<()> {
536         use crate::{params, Connection};
537         use uuid::Uuid;
538 
539         let db = Connection::open_in_memory()?;
540         db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")?;
541 
542         let id = Uuid::new_v4();
543 
544         db.execute(
545             "INSERT INTO foo (id, label) VALUES (?1, ?2)",
546             params![id, "target"],
547         )?;
548 
549         let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?1")?;
550 
551         let mut rows = stmt.query(params![id])?;
552         let row = rows.next()?.unwrap();
553 
554         let found_id: Uuid = row.get_unwrap(0);
555         let found_label: String = row.get_unwrap(1);
556 
557         assert_eq!(found_id, id);
558         assert_eq!(found_label, "target");
559         Ok(())
560     }
561 }
562