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