rusqlite/types/
to_sql.rs

1use super::{Null, Value, ValueRef};
2#[cfg(feature = "array")]
3use crate::vtab::array::Array;
4use crate::{Error, Result};
5use std::borrow::Cow;
6use 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]
12pub 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.
33impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
34where
35    &'a T: Into<ValueRef<'a>>,
36{
37    #[inline]
38    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.
48macro_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);
56from_value!(String);
57from_value!(Null);
58from_value!(bool);
59from_value!(i8);
60from_value!(i16);
61from_value!(i32);
62from_value!(i64);
63from_value!(isize);
64from_value!(u8);
65from_value!(u16);
66from_value!(u32);
67from_value!(f32);
68from_value!(f64);
69from_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")))]
76from_value!(i128);
77
78#[cfg(feature = "uuid")]
79#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
80from_value!(uuid::Uuid);
81
82impl ToSql for ToSqlOutput<'_> {
83    #[inline]
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.
99pub trait ToSql {
100    /// Converts Rust value to SQLite value
101    fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
102}
103
104impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
105    #[inline]
106    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
107        self.as_ref().to_sql()
108    }
109}
110
111impl<T: ToSql + ?Sized> ToSql for Box<T> {
112    #[inline]
113    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
114        self.as_ref().to_sql()
115    }
116}
117
118impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
119    #[inline]
120    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
121        self.as_ref().to_sql()
122    }
123}
124
125impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
126    #[inline]
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
144macro_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
155to_sql_self!(Null);
156to_sql_self!(bool);
157to_sql_self!(i8);
158to_sql_self!(i16);
159to_sql_self!(i32);
160to_sql_self!(i64);
161to_sql_self!(isize);
162to_sql_self!(u8);
163to_sql_self!(u16);
164to_sql_self!(u32);
165to_sql_self!(f32);
166to_sql_self!(f64);
167
168#[cfg(feature = "i128_blob")]
169#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
170to_sql_self!(i128);
171
172#[cfg(feature = "uuid")]
173#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
174to_sql_self!(uuid::Uuid);
175
176macro_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.
193to_sql_self_fallible!(u64);
194to_sql_self_fallible!(usize);
195
196impl<T: ?Sized> ToSql for &'_ T
197where
198    T: ToSql,
199{
200    #[inline]
201    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
202        (*self).to_sql()
203    }
204}
205
206impl ToSql for String {
207    #[inline]
208    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
209        Ok(ToSqlOutput::from(self.as_str()))
210    }
211}
212
213impl ToSql for str {
214    #[inline]
215    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
216        Ok(ToSqlOutput::from(self))
217    }
218}
219
220impl ToSql for Vec<u8> {
221    #[inline]
222    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
223        Ok(ToSqlOutput::from(self.as_slice()))
224    }
225}
226
227impl<const N: usize> ToSql for [u8; N] {
228    #[inline]
229    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
230        Ok(ToSqlOutput::from(&self[..]))
231    }
232}
233
234impl ToSql for [u8] {
235    #[inline]
236    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
237        Ok(ToSqlOutput::from(self))
238    }
239}
240
241impl ToSql for Value {
242    #[inline]
243    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
244        Ok(ToSqlOutput::from(self))
245    }
246}
247
248impl<T: ToSql> ToSql for Option<T> {
249    #[inline]
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)]
259mod test {
260    use super::ToSql;
261
262    fn is_to_sql<T: ToSql>() {}
263
264    #[test]
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]
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]
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]
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]
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]
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]
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]
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]
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}