Skip to main content

rusqlite/types/
to_sql.rs

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