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