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