rusqlite/types/
to_sql.rs

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