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