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#[derive(Clone, Debug, PartialEq)]
11#[non_exhaustive]
12pub enum ToSqlOutput<'a> {
13 Borrowed(ValueRef<'a>),
15
16 Owned(Value),
18
19 #[cfg(feature = "blob")]
22 #[cfg_attr(docsrs, doc(cfg(feature = "blob")))]
23 ZeroBlob(i32),
24
25 #[cfg(feature = "array")]
27 #[cfg_attr(docsrs, doc(cfg(feature = "array")))]
28 Array(Array),
29}
30
31impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
34where
35 &'a T: Into<ValueRef<'a>>,
36{
37 #[inline]
38 fn from(t: &'a T) -> Self {
39 ToSqlOutput::Borrowed(t.into())
40 }
41}
42
43macro_rules! from_value(
49 ($t:ty) => (
50 impl From<$t> for ToSqlOutput<'_> {
51 #[inline]
52 fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
53 }
54 )
55);
56from_value!(String);
57from_value!(Null);
58from_value!(bool);
59from_value!(i8);
60from_value!(i16);
61from_value!(i32);
62from_value!(i64);
63from_value!(isize);
64from_value!(u8);
65from_value!(u16);
66from_value!(u32);
67from_value!(f32);
68from_value!(f64);
69from_value!(Vec<u8>);
70
71#[cfg(feature = "i128_blob")]
75#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
76from_value!(i128);
77
78#[cfg(feature = "uuid")]
79#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
80from_value!(uuid::Uuid);
81
82impl ToSql for ToSqlOutput<'_> {
83 #[inline]
84 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
85 Ok(match *self {
86 ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
87 ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
88
89 #[cfg(feature = "blob")]
90 ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
91 #[cfg(feature = "array")]
92 ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
93 })
94 }
95}
96
97pub trait ToSql {
100 fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
102}
103
104impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
105 #[inline]
106 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
107 self.as_ref().to_sql()
108 }
109}
110
111impl<T: ToSql + ?Sized> ToSql for Box<T> {
112 #[inline]
113 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
114 self.as_ref().to_sql()
115 }
116}
117
118impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
119 #[inline]
120 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
121 self.as_ref().to_sql()
122 }
123}
124
125impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
126 #[inline]
127 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
128 self.as_ref().to_sql()
129 }
130}
131
132macro_rules! to_sql_self(
145 ($t:ty) => (
146 impl ToSql for $t {
147 #[inline]
148 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
149 Ok(ToSqlOutput::from(*self))
150 }
151 }
152 )
153);
154
155to_sql_self!(Null);
156to_sql_self!(bool);
157to_sql_self!(i8);
158to_sql_self!(i16);
159to_sql_self!(i32);
160to_sql_self!(i64);
161to_sql_self!(isize);
162to_sql_self!(u8);
163to_sql_self!(u16);
164to_sql_self!(u32);
165to_sql_self!(f32);
166to_sql_self!(f64);
167
168#[cfg(feature = "i128_blob")]
169#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
170to_sql_self!(i128);
171
172#[cfg(feature = "uuid")]
173#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
174to_sql_self!(uuid::Uuid);
175
176macro_rules! to_sql_self_fallible(
177 ($t:ty) => (
178 impl ToSql for $t {
179 #[inline]
180 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
181 Ok(ToSqlOutput::Owned(Value::Integer(
182 i64::try_from(*self).map_err(
183 |err| Error::ToSqlConversionFailure(err.into())
185 )?
186 )))
187 }
188 }
189 )
190);
191
192to_sql_self_fallible!(u64);
194to_sql_self_fallible!(usize);
195
196impl<T: ?Sized> ToSql for &'_ T
197where
198 T: ToSql,
199{
200 #[inline]
201 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
202 (*self).to_sql()
203 }
204}
205
206impl ToSql for String {
207 #[inline]
208 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
209 Ok(ToSqlOutput::from(self.as_str()))
210 }
211}
212
213impl ToSql for str {
214 #[inline]
215 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
216 Ok(ToSqlOutput::from(self))
217 }
218}
219
220impl ToSql for Vec<u8> {
221 #[inline]
222 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
223 Ok(ToSqlOutput::from(self.as_slice()))
224 }
225}
226
227impl<const N: usize> ToSql for [u8; N] {
228 #[inline]
229 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
230 Ok(ToSqlOutput::from(&self[..]))
231 }
232}
233
234impl ToSql for [u8] {
235 #[inline]
236 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
237 Ok(ToSqlOutput::from(self))
238 }
239}
240
241impl ToSql for Value {
242 #[inline]
243 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
244 Ok(ToSqlOutput::from(self))
245 }
246}
247
248impl<T: ToSql> ToSql for Option<T> {
249 #[inline]
250 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
251 match *self {
252 None => Ok(ToSqlOutput::from(Null)),
253 Some(ref t) => t.to_sql(),
254 }
255 }
256}
257
258#[cfg(test)]
259mod test {
260 use super::ToSql;
261
262 fn is_to_sql<T: ToSql>() {}
263
264 #[test]
265 fn test_integral_types() {
266 is_to_sql::<i8>();
267 is_to_sql::<i16>();
268 is_to_sql::<i32>();
269 is_to_sql::<i64>();
270 is_to_sql::<u8>();
271 is_to_sql::<u16>();
272 is_to_sql::<u32>();
273 }
274
275 #[test]
276 fn test_u8_array() {
277 let a: [u8; 99] = [0u8; 99];
278 let _a: &[&dyn ToSql] = crate::params![a];
279 let r = ToSql::to_sql(&a);
280
281 assert!(r.is_ok());
282 }
283
284 #[test]
285 fn test_cow_str() {
286 use std::borrow::Cow;
287 let s = "str";
288 let cow: Cow<str> = Cow::Borrowed(s);
289 let r = cow.to_sql();
290 assert!(r.is_ok());
291 let cow: Cow<str> = Cow::Owned::<str>(String::from(s));
292 let r = cow.to_sql();
293 assert!(r.is_ok());
294 let _p: &[&dyn ToSql] = crate::params![cow];
296 }
297
298 #[test]
299 fn test_box_dyn() {
300 let s: Box<dyn ToSql> = Box::new("Hello world!");
301 let _s: &[&dyn ToSql] = crate::params![s];
302 let r = ToSql::to_sql(&s);
303
304 assert!(r.is_ok());
305 }
306
307 #[test]
308 fn test_box_deref() {
309 let s: Box<str> = "Hello world!".into();
310 let _s: &[&dyn ToSql] = crate::params![s];
311 let r = s.to_sql();
312
313 assert!(r.is_ok());
314 }
315
316 #[test]
317 fn test_box_direct() {
318 let s: Box<str> = "Hello world!".into();
319 let _s: &[&dyn ToSql] = crate::params![s];
320 let r = ToSql::to_sql(&s);
321
322 assert!(r.is_ok());
323 }
324
325 #[test]
326 fn test_cells() {
327 use std::{rc::Rc, sync::Arc};
328
329 let source_str: Box<str> = "Hello world!".into();
330
331 let s: Rc<Box<str>> = Rc::new(source_str.clone());
332 let _s: &[&dyn ToSql] = crate::params![s];
333 let r = s.to_sql();
334 assert!(r.is_ok());
335
336 let s: Arc<Box<str>> = Arc::new(source_str.clone());
337 let _s: &[&dyn ToSql] = crate::params![s];
338 let r = s.to_sql();
339 assert!(r.is_ok());
340
341 let s: Arc<str> = Arc::from(&*source_str);
342 let _s: &[&dyn ToSql] = crate::params![s];
343 let r = s.to_sql();
344 assert!(r.is_ok());
345
346 let s: Arc<dyn ToSql> = Arc::new(source_str.clone());
347 let _s: &[&dyn ToSql] = crate::params![s];
348 let r = s.to_sql();
349 assert!(r.is_ok());
350
351 let s: Rc<str> = Rc::from(&*source_str);
352 let _s: &[&dyn ToSql] = crate::params![s];
353 let r = s.to_sql();
354 assert!(r.is_ok());
355
356 let s: Rc<dyn ToSql> = Rc::new(source_str);
357 let _s: &[&dyn ToSql] = crate::params![s];
358 let r = s.to_sql();
359 assert!(r.is_ok());
360 }
361
362 #[cfg(feature = "i128_blob")]
363 #[test]
364 fn test_i128() -> crate::Result<()> {
365 use crate::Connection;
366 let db = Connection::open_in_memory()?;
367 db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
368 db.execute(
369 "
370 INSERT INTO foo(i128, desc) VALUES
371 (?, 'zero'),
372 (?, 'neg one'), (?, 'neg two'),
373 (?, 'pos one'), (?, 'pos two'),
374 (?, 'min'), (?, 'max')",
375 [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
376 )?;
377
378 let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
379
380 let res = stmt
381 .query_map([], |row| {
382 Ok((row.get::<_, i128>(0)?, row.get::<_, String>(1)?))
383 })?
384 .collect::<Result<Vec<_>, _>>()?;
385
386 assert_eq!(
387 res,
388 &[
389 (i128::MIN, "min".to_owned()),
390 (-2, "neg two".to_owned()),
391 (-1, "neg one".to_owned()),
392 (0, "zero".to_owned()),
393 (1, "pos one".to_owned()),
394 (2, "pos two".to_owned()),
395 (i128::MAX, "max".to_owned()),
396 ]
397 );
398 Ok(())
399 }
400
401 #[cfg(feature = "uuid")]
402 #[test]
403 fn test_uuid() -> crate::Result<()> {
404 use crate::{params, Connection};
405 use uuid::Uuid;
406
407 let db = Connection::open_in_memory()?;
408 db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")?;
409
410 let id = Uuid::new_v4();
411
412 db.execute(
413 "INSERT INTO foo (id, label) VALUES (?, ?)",
414 params![id, "target"],
415 )?;
416
417 let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?")?;
418
419 let mut rows = stmt.query(params![id])?;
420 let row = rows.next()?.unwrap();
421
422 let found_id: Uuid = row.get_unwrap(0);
423 let found_label: String = row.get_unwrap(1);
424
425 assert_eq!(found_id, id);
426 assert_eq!(found_label, "target");
427 Ok(())
428 }
429}