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;
6
7/// `ToSqlOutput` represents the possible output types for implementers of the
8/// [`ToSql`] trait.
9#[derive(Clone, Debug, PartialEq)]
10#[non_exhaustive]
11pub 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.
37impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
38where
39    &'a T: Into<ValueRef<'a>>,
40{
41    #[inline]
42    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.
52macro_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);
66from_value!(String);
67from_value!(Null);
68from_value!(bool);
69from_value!(i8);
70from_value!(i16);
71from_value!(i32);
72from_value!(i64);
73from_value!(isize);
74from_value!(u8);
75from_value!(u16);
76from_value!(u32);
77from_value!(f32);
78from_value!(f64);
79from_value!(Vec<u8>);
80
81from_value!(non_zero std::num::NonZeroI8);
82from_value!(non_zero std::num::NonZeroI16);
83from_value!(non_zero std::num::NonZeroI32);
84from_value!(non_zero std::num::NonZeroI64);
85from_value!(non_zero std::num::NonZeroIsize);
86from_value!(non_zero std::num::NonZeroU8);
87from_value!(non_zero std::num::NonZeroU16);
88from_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")))]
95from_value!(i128);
96
97#[cfg(feature = "i128_blob")]
98#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
99from_value!(non_zero std::num::NonZeroI128);
100
101#[cfg(feature = "uuid")]
102#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
103from_value!(uuid::Uuid);
104
105impl ToSql for ToSqlOutput<'_> {
106    #[inline]
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.
124pub trait ToSql {
125    /// Converts Rust value to SQLite value
126    fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
127}
128
129impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
130    #[inline]
131    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
132        self.as_ref().to_sql()
133    }
134}
135
136impl<T: ToSql + ?Sized> ToSql for Box<T> {
137    #[inline]
138    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
139        self.as_ref().to_sql()
140    }
141}
142
143impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
144    #[inline]
145    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
146        self.as_ref().to_sql()
147    }
148}
149
150impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
151    #[inline]
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
169macro_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
180to_sql_self!(Null);
181to_sql_self!(bool);
182to_sql_self!(i8);
183to_sql_self!(i16);
184to_sql_self!(i32);
185to_sql_self!(i64);
186to_sql_self!(isize);
187to_sql_self!(u8);
188to_sql_self!(u16);
189to_sql_self!(u32);
190to_sql_self!(f32);
191to_sql_self!(f64);
192
193to_sql_self!(std::num::NonZeroI8);
194to_sql_self!(std::num::NonZeroI16);
195to_sql_self!(std::num::NonZeroI32);
196to_sql_self!(std::num::NonZeroI64);
197to_sql_self!(std::num::NonZeroIsize);
198to_sql_self!(std::num::NonZeroU8);
199to_sql_self!(std::num::NonZeroU16);
200to_sql_self!(std::num::NonZeroU32);
201
202#[cfg(feature = "i128_blob")]
203#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
204to_sql_self!(i128);
205
206#[cfg(feature = "i128_blob")]
207#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
208to_sql_self!(std::num::NonZeroI128);
209
210#[cfg(feature = "uuid")]
211#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
212to_sql_self!(uuid::Uuid);
213
214macro_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.
244to_sql_self_fallible!(u64);
245to_sql_self_fallible!(usize);
246to_sql_self_fallible!(non_zero std::num::NonZeroU64);
247to_sql_self_fallible!(non_zero std::num::NonZeroUsize);
248
249impl<T: ?Sized> ToSql for &'_ T
250where
251    T: ToSql,
252{
253    #[inline]
254    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
255        (*self).to_sql()
256    }
257}
258
259impl ToSql for String {
260    #[inline]
261    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
262        Ok(ToSqlOutput::from(self.as_str()))
263    }
264}
265
266impl ToSql for str {
267    #[inline]
268    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
269        Ok(ToSqlOutput::from(self))
270    }
271}
272
273impl ToSql for Vec<u8> {
274    #[inline]
275    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
276        Ok(ToSqlOutput::from(self.as_slice()))
277    }
278}
279
280impl<const N: usize> ToSql for [u8; N] {
281    #[inline]
282    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
283        Ok(ToSqlOutput::from(&self[..]))
284    }
285}
286
287impl ToSql for [u8] {
288    #[inline]
289    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
290        Ok(ToSqlOutput::from(self))
291    }
292}
293
294impl ToSql for Value {
295    #[inline]
296    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
297        Ok(ToSqlOutput::from(self))
298    }
299}
300
301impl<T: ToSql> ToSql for Option<T> {
302    #[inline]
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)]
312mod test {
313    use super::{ToSql, ToSqlOutput};
314    use crate::{types::Value, types::ValueRef, Result};
315
316    fn is_to_sql<T: ToSql>() {}
317
318    #[test]
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]
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]
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]
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]
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]
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]
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]
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]
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]
448    fn test_i128() -> 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]
487    fn test_non_zero_i128() -> 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]
535    fn test_uuid() -> 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}