Skip to main content

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;
6use std::convert::TryFrom;
7
8/// `ToSqlOutput` represents the possible output types for implementers of the
9/// [`ToSql`] trait.
10#[derive(Clone, Debug, PartialEq)]
11#[non_exhaustive]
12pub enum ToSqlOutput<'a> {
13    /// A borrowed SQLite-representable value.
14    Borrowed(ValueRef<'a>),
15
16    /// An owned SQLite-representable value.
17    Owned(Value),
18
19    /// `feature = "blob"` A BLOB of the given length that is filled with
20    /// zeroes.
21    #[cfg(feature = "blob")]
22    ZeroBlob(i32),
23
24    /// `feature = "array"`
25    #[cfg(feature = "array")]
26    Array(Array),
27}
28
29// Generically allow any type that can be converted into a ValueRef
30// to be converted into a ToSqlOutput as well.
31impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
32where
33    &'a T: Into<ValueRef<'a>>,
34{
35    #[inline]
36    fn from(t: &'a T) -> Self {
37        ToSqlOutput::Borrowed(t.into())
38    }
39}
40
41// We cannot also generically allow any type that can be converted
42// into a Value to be converted into a ToSqlOutput because of
43// coherence rules (https://github.com/rust-lang/rust/pull/46192),
44// so we'll manually implement it for all the types we know can
45// be converted into Values.
46macro_rules! from_value(
47    ($t:ty) => (
48        impl From<$t> for ToSqlOutput<'_> {
49            #[inline]
50            fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
51        }
52    )
53);
54from_value!(String);
55from_value!(Null);
56from_value!(bool);
57from_value!(i8);
58from_value!(i16);
59from_value!(i32);
60from_value!(i64);
61from_value!(isize);
62from_value!(u8);
63from_value!(u16);
64from_value!(u32);
65from_value!(f32);
66from_value!(f64);
67from_value!(Vec<u8>);
68
69// It would be nice if we could avoid the heap allocation (of the `Vec`) that
70// `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not
71// worth adding another case to Value.
72#[cfg(feature = "i128_blob")]
73from_value!(i128);
74
75#[cfg(feature = "uuid")]
76from_value!(uuid::Uuid);
77
78impl ToSql for ToSqlOutput<'_> {
79    #[inline]
80    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
81        Ok(match *self {
82            ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
83            ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
84
85            #[cfg(feature = "blob")]
86            ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
87            #[cfg(feature = "array")]
88            ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
89        })
90    }
91}
92
93/// A trait for types that can be converted into SQLite values. Returns
94/// [`Error::ToSqlConversionFailure`] if the conversion fails.
95pub trait ToSql {
96    /// Converts Rust value to SQLite value
97    fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
98}
99
100impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
101    #[inline]
102    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
103        self.as_ref().to_sql()
104    }
105}
106
107impl<T: ToSql + ?Sized> ToSql for Box<T> {
108    #[inline]
109    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
110        self.as_ref().to_sql()
111    }
112}
113
114impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
115    #[inline]
116    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
117        self.as_ref().to_sql()
118    }
119}
120
121impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
122    #[inline]
123    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
124        self.as_ref().to_sql()
125    }
126}
127
128// We should be able to use a generic impl like this:
129//
130// impl<T: Copy> ToSql for T where T: Into<Value> {
131//     fn to_sql(&self) -> Result<ToSqlOutput> {
132//         Ok(ToSqlOutput::from((*self).into()))
133//     }
134// }
135//
136// instead of the following macro, but this runs afoul of
137// https://github.com/rust-lang/rust/issues/30191 and reports conflicting
138// implementations even when there aren't any.
139
140macro_rules! to_sql_self(
141    ($t:ty) => (
142        impl ToSql for $t {
143            #[inline]
144            fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
145                Ok(ToSqlOutput::from(*self))
146            }
147        }
148    )
149);
150
151to_sql_self!(Null);
152to_sql_self!(bool);
153to_sql_self!(i8);
154to_sql_self!(i16);
155to_sql_self!(i32);
156to_sql_self!(i64);
157to_sql_self!(isize);
158to_sql_self!(u8);
159to_sql_self!(u16);
160to_sql_self!(u32);
161to_sql_self!(f32);
162to_sql_self!(f64);
163
164#[cfg(feature = "i128_blob")]
165to_sql_self!(i128);
166
167#[cfg(feature = "uuid")]
168to_sql_self!(uuid::Uuid);
169
170macro_rules! to_sql_self_fallible(
171    ($t:ty) => (
172        impl ToSql for $t {
173            #[inline]
174            fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
175                Ok(ToSqlOutput::Owned(Value::Integer(
176                    i64::try_from(*self).map_err(
177                        // TODO: Include the values in the error message.
178                        |err| Error::ToSqlConversionFailure(err.into())
179                    )?
180                )))
181            }
182        }
183    )
184);
185
186// Special implementations for usize and u64 because these conversions can fail.
187to_sql_self_fallible!(u64);
188to_sql_self_fallible!(usize);
189
190impl<T: ?Sized> ToSql for &'_ T
191where
192    T: ToSql,
193{
194    #[inline]
195    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
196        (*self).to_sql()
197    }
198}
199
200impl ToSql for String {
201    #[inline]
202    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
203        Ok(ToSqlOutput::from(self.as_str()))
204    }
205}
206
207impl ToSql for str {
208    #[inline]
209    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
210        Ok(ToSqlOutput::from(self))
211    }
212}
213
214impl ToSql for Vec<u8> {
215    #[inline]
216    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
217        Ok(ToSqlOutput::from(self.as_slice()))
218    }
219}
220
221impl ToSql for [u8] {
222    #[inline]
223    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
224        Ok(ToSqlOutput::from(self))
225    }
226}
227
228impl ToSql for Value {
229    #[inline]
230    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
231        Ok(ToSqlOutput::from(self))
232    }
233}
234
235impl<T: ToSql> ToSql for Option<T> {
236    #[inline]
237    fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
238        match *self {
239            None => Ok(ToSqlOutput::from(Null)),
240            Some(ref t) => t.to_sql(),
241        }
242    }
243}
244
245#[cfg(test)]
246mod test {
247    use super::ToSql;
248
249    fn is_to_sql<T: ToSql>() {}
250
251    #[test]
252    fn test_integral_types() {
253        is_to_sql::<i8>();
254        is_to_sql::<i16>();
255        is_to_sql::<i32>();
256        is_to_sql::<i64>();
257        is_to_sql::<u8>();
258        is_to_sql::<u16>();
259        is_to_sql::<u32>();
260    }
261
262    #[test]
263    fn test_cow_str() {
264        use std::borrow::Cow;
265        let s = "str";
266        let cow: Cow<str> = Cow::Borrowed(s);
267        let r = cow.to_sql();
268        assert!(r.is_ok());
269        let cow: Cow<str> = Cow::Owned::<str>(String::from(s));
270        let r = cow.to_sql();
271        assert!(r.is_ok());
272        // Ensure this compiles.
273        let _p: &[&dyn ToSql] = crate::params![cow];
274    }
275
276    #[test]
277    fn test_box_dyn() {
278        let s: Box<dyn ToSql> = Box::new("Hello world!");
279        let _s: &[&dyn ToSql] = crate::params![s];
280        let r = ToSql::to_sql(&s);
281
282        assert!(r.is_ok());
283    }
284
285    #[test]
286    fn test_box_deref() {
287        let s: Box<str> = "Hello world!".into();
288        let _s: &[&dyn ToSql] = crate::params![s];
289        let r = s.to_sql();
290
291        assert!(r.is_ok());
292    }
293
294    #[test]
295    fn test_box_direct() {
296        let s: Box<str> = "Hello world!".into();
297        let _s: &[&dyn ToSql] = crate::params![s];
298        let r = ToSql::to_sql(&s);
299
300        assert!(r.is_ok());
301    }
302
303    #[test]
304    fn test_cells() {
305        use std::{rc::Rc, sync::Arc};
306
307        let source_str: Box<str> = "Hello world!".into();
308
309        let s: Rc<Box<str>> = Rc::new(source_str.clone());
310        let _s: &[&dyn ToSql] = crate::params![s];
311        let r = s.to_sql();
312        assert!(r.is_ok());
313
314        let s: Arc<Box<str>> = Arc::new(source_str.clone());
315        let _s: &[&dyn ToSql] = crate::params![s];
316        let r = s.to_sql();
317        assert!(r.is_ok());
318
319        let s: Arc<str> = Arc::from(&*source_str);
320        let _s: &[&dyn ToSql] = crate::params![s];
321        let r = s.to_sql();
322        assert!(r.is_ok());
323
324        let s: Arc<dyn ToSql> = Arc::new(source_str.clone());
325        let _s: &[&dyn ToSql] = crate::params![s];
326        let r = s.to_sql();
327        assert!(r.is_ok());
328
329        let s: Rc<str> = Rc::from(&*source_str);
330        let _s: &[&dyn ToSql] = crate::params![s];
331        let r = s.to_sql();
332        assert!(r.is_ok());
333
334        let s: Rc<dyn ToSql> = Rc::new(source_str);
335        let _s: &[&dyn ToSql] = crate::params![s];
336        let r = s.to_sql();
337        assert!(r.is_ok());
338    }
339
340    #[cfg(feature = "i128_blob")]
341    #[test]
342    fn test_i128() -> crate::Result<()> {
343        use crate::Connection;
344        use std::i128;
345        let db = Connection::open_in_memory()?;
346        db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
347        db.execute(
348            "
349            INSERT INTO foo(i128, desc) VALUES
350                (?, 'zero'),
351                (?, 'neg one'), (?, 'neg two'),
352                (?, 'pos one'), (?, 'pos two'),
353                (?, 'min'), (?, 'max')",
354            [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
355        )?;
356
357        let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
358
359        let res = stmt
360            .query_map([], |row| {
361                Ok((row.get::<_, i128>(0)?, row.get::<_, String>(1)?))
362            })?
363            .collect::<Result<Vec<_>, _>>()?;
364
365        assert_eq!(
366            res,
367            &[
368                (i128::MIN, "min".to_owned()),
369                (-2, "neg two".to_owned()),
370                (-1, "neg one".to_owned()),
371                (0, "zero".to_owned()),
372                (1, "pos one".to_owned()),
373                (2, "pos two".to_owned()),
374                (i128::MAX, "max".to_owned()),
375            ]
376        );
377        Ok(())
378    }
379
380    #[cfg(feature = "uuid")]
381    #[test]
382    fn test_uuid() -> crate::Result<()> {
383        use crate::{params, Connection};
384        use uuid::Uuid;
385
386        let db = Connection::open_in_memory()?;
387        db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")?;
388
389        let id = Uuid::new_v4();
390
391        db.execute(
392            "INSERT INTO foo (id, label) VALUES (?, ?)",
393            params![id, "target"],
394        )?;
395
396        let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?")?;
397
398        let mut rows = stmt.query(params![id])?;
399        let row = rows.next()?.unwrap();
400
401        let found_id: Uuid = row.get_unwrap(0);
402        let found_label: String = row.get_unwrap(1);
403
404        assert_eq!(found_id, id);
405        assert_eq!(found_label, "target");
406        Ok(())
407    }
408}