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 = "functions")]
27 #[cfg_attr(docsrs, doc(cfg(feature = "functions")))]
28 Arg(usize),
29
30 #[cfg(feature = "array")]
32 #[cfg_attr(docsrs, doc(cfg(feature = "array")))]
33 Array(Array),
34}
35
36impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
39where
40 &'a T: Into<ValueRef<'a>>,
41{
42 #[inline]
43 fn from(t: &'a T) -> Self {
44 ToSqlOutput::Borrowed(t.into())
45 }
46}
47
48macro_rules! from_value(
54 ($t:ty) => (
55 impl From<$t> for ToSqlOutput<'_> {
56 #[inline]
57 fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
58 }
59 );
60 (non_zero $t:ty) => (
61 impl From<$t> for ToSqlOutput<'_> {
62 #[inline]
63 fn from(t: $t) -> Self { ToSqlOutput::Owned(t.get().into())}
64 }
65 )
66);
67from_value!(String);
68from_value!(Null);
69from_value!(bool);
70from_value!(i8);
71from_value!(i16);
72from_value!(i32);
73from_value!(i64);
74from_value!(isize);
75from_value!(u8);
76from_value!(u16);
77from_value!(u32);
78from_value!(f32);
79from_value!(f64);
80from_value!(Vec<u8>);
81
82from_value!(non_zero std::num::NonZeroI8);
83from_value!(non_zero std::num::NonZeroI16);
84from_value!(non_zero std::num::NonZeroI32);
85from_value!(non_zero std::num::NonZeroI64);
86from_value!(non_zero std::num::NonZeroIsize);
87from_value!(non_zero std::num::NonZeroU8);
88from_value!(non_zero std::num::NonZeroU16);
89from_value!(non_zero std::num::NonZeroU32);
90
91#[cfg(feature = "i128_blob")]
95#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
96from_value!(i128);
97
98#[cfg(feature = "i128_blob")]
99#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
100from_value!(non_zero std::num::NonZeroI128);
101
102#[cfg(feature = "uuid")]
103#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
104from_value!(uuid::Uuid);
105
106impl ToSql for ToSqlOutput<'_> {
107 #[inline]
108 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
109 Ok(match *self {
110 ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
111 ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
112
113 #[cfg(feature = "blob")]
114 ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
115 #[cfg(feature = "functions")]
116 ToSqlOutput::Arg(i) => ToSqlOutput::Arg(i),
117 #[cfg(feature = "array")]
118 ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
119 })
120 }
121}
122
123pub trait ToSql {
126 fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
128}
129
130impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
131 #[inline]
132 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
133 self.as_ref().to_sql()
134 }
135}
136
137impl<T: ToSql + ?Sized> ToSql for Box<T> {
138 #[inline]
139 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
140 self.as_ref().to_sql()
141 }
142}
143
144impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
145 #[inline]
146 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
147 self.as_ref().to_sql()
148 }
149}
150
151impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
152 #[inline]
153 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
154 self.as_ref().to_sql()
155 }
156}
157
158macro_rules! to_sql_self(
171 ($t:ty) => (
172 impl ToSql for $t {
173 #[inline]
174 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
175 Ok(ToSqlOutput::from(*self))
176 }
177 }
178 )
179);
180
181to_sql_self!(Null);
182to_sql_self!(bool);
183to_sql_self!(i8);
184to_sql_self!(i16);
185to_sql_self!(i32);
186to_sql_self!(i64);
187to_sql_self!(isize);
188to_sql_self!(u8);
189to_sql_self!(u16);
190to_sql_self!(u32);
191to_sql_self!(f32);
192to_sql_self!(f64);
193
194to_sql_self!(std::num::NonZeroI8);
195to_sql_self!(std::num::NonZeroI16);
196to_sql_self!(std::num::NonZeroI32);
197to_sql_self!(std::num::NonZeroI64);
198to_sql_self!(std::num::NonZeroIsize);
199to_sql_self!(std::num::NonZeroU8);
200to_sql_self!(std::num::NonZeroU16);
201to_sql_self!(std::num::NonZeroU32);
202
203#[cfg(feature = "i128_blob")]
204#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
205to_sql_self!(i128);
206
207#[cfg(feature = "i128_blob")]
208#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
209to_sql_self!(std::num::NonZeroI128);
210
211#[cfg(feature = "uuid")]
212#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
213to_sql_self!(uuid::Uuid);
214
215macro_rules! to_sql_self_fallible(
216 ($t:ty) => (
217 impl ToSql for $t {
218 #[inline]
219 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
220 Ok(ToSqlOutput::Owned(Value::Integer(
221 i64::try_from(*self).map_err(
222 |err| Error::ToSqlConversionFailure(err.into())
224 )?
225 )))
226 }
227 }
228 );
229 (non_zero $t:ty) => (
230 impl ToSql for $t {
231 #[inline]
232 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
233 Ok(ToSqlOutput::Owned(Value::Integer(
234 i64::try_from(self.get()).map_err(
235 |err| Error::ToSqlConversionFailure(err.into())
237 )?
238 )))
239 }
240 }
241 )
242);
243
244to_sql_self_fallible!(u64);
246to_sql_self_fallible!(usize);
247to_sql_self_fallible!(non_zero std::num::NonZeroU64);
248to_sql_self_fallible!(non_zero std::num::NonZeroUsize);
249
250impl<T: ?Sized> ToSql for &'_ T
251where
252 T: ToSql,
253{
254 #[inline]
255 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
256 (*self).to_sql()
257 }
258}
259
260impl ToSql for String {
261 #[inline]
262 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
263 Ok(ToSqlOutput::from(self.as_str()))
264 }
265}
266
267impl ToSql for str {
268 #[inline]
269 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
270 Ok(ToSqlOutput::from(self))
271 }
272}
273
274impl ToSql for Vec<u8> {
275 #[inline]
276 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
277 Ok(ToSqlOutput::from(self.as_slice()))
278 }
279}
280
281impl<const N: usize> ToSql for [u8; N] {
282 #[inline]
283 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
284 Ok(ToSqlOutput::from(&self[..]))
285 }
286}
287
288impl ToSql for [u8] {
289 #[inline]
290 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
291 Ok(ToSqlOutput::from(self))
292 }
293}
294
295impl ToSql for Value {
296 #[inline]
297 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
298 Ok(ToSqlOutput::from(self))
299 }
300}
301
302impl<T: ToSql> ToSql for Option<T> {
303 #[inline]
304 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
305 match *self {
306 None => Ok(ToSqlOutput::from(Null)),
307 Some(ref t) => t.to_sql(),
308 }
309 }
310}
311
312#[cfg(test)]
313mod test {
314 use super::ToSql;
315
316 fn is_to_sql<T: ToSql>() {}
317
318 #[test]
319 fn test_integral_types() {
320 is_to_sql::<i8>();
321 is_to_sql::<i16>();
322 is_to_sql::<i32>();
323 is_to_sql::<i64>();
324 is_to_sql::<isize>();
325 is_to_sql::<u8>();
326 is_to_sql::<u16>();
327 is_to_sql::<u32>();
328 is_to_sql::<u64>();
329 is_to_sql::<usize>();
330 }
331
332 #[test]
333 fn test_nonzero_types() {
334 is_to_sql::<std::num::NonZeroI8>();
335 is_to_sql::<std::num::NonZeroI16>();
336 is_to_sql::<std::num::NonZeroI32>();
337 is_to_sql::<std::num::NonZeroI64>();
338 is_to_sql::<std::num::NonZeroIsize>();
339 is_to_sql::<std::num::NonZeroU8>();
340 is_to_sql::<std::num::NonZeroU16>();
341 is_to_sql::<std::num::NonZeroU32>();
342 is_to_sql::<std::num::NonZeroU64>();
343 is_to_sql::<std::num::NonZeroUsize>();
344 }
345
346 #[test]
347 fn test_u8_array() {
348 let a: [u8; 99] = [0u8; 99];
349 let _a: &[&dyn ToSql] = crate::params![a];
350 let r = ToSql::to_sql(&a);
351
352 r.unwrap();
353 }
354
355 #[test]
356 fn test_cow_str() {
357 use std::borrow::Cow;
358 let s = "str";
359 let cow: Cow<str> = Cow::Borrowed(s);
360 let r = cow.to_sql();
361 r.unwrap();
362 let cow: Cow<str> = Cow::Owned::<str>(String::from(s));
363 let r = cow.to_sql();
364 r.unwrap();
365 let _p: &[&dyn ToSql] = crate::params![cow];
367 }
368
369 #[test]
370 fn test_box_dyn() {
371 let s: Box<dyn ToSql> = Box::new("Hello world!");
372 let _s: &[&dyn ToSql] = crate::params![s];
373 let r = ToSql::to_sql(&s);
374
375 r.unwrap();
376 }
377
378 #[test]
379 fn test_box_deref() {
380 let s: Box<str> = "Hello world!".into();
381 let _s: &[&dyn ToSql] = crate::params![s];
382 let r = s.to_sql();
383
384 r.unwrap();
385 }
386
387 #[test]
388 fn test_box_direct() {
389 let s: Box<str> = "Hello world!".into();
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_cells() {
398 use std::{rc::Rc, sync::Arc};
399
400 let source_str: Box<str> = "Hello world!".into();
401
402 let s: Rc<Box<str>> = Rc::new(source_str.clone());
403 let _s: &[&dyn ToSql] = crate::params![s];
404 let r = s.to_sql();
405 r.unwrap();
406
407 let s: Arc<Box<str>> = Arc::new(source_str.clone());
408 let _s: &[&dyn ToSql] = crate::params![s];
409 let r = s.to_sql();
410 r.unwrap();
411
412 let s: Arc<str> = Arc::from(&*source_str);
413 let _s: &[&dyn ToSql] = crate::params![s];
414 let r = s.to_sql();
415 r.unwrap();
416
417 let s: Arc<dyn ToSql> = Arc::new(source_str.clone());
418 let _s: &[&dyn ToSql] = crate::params![s];
419 let r = s.to_sql();
420 r.unwrap();
421
422 let s: Rc<str> = Rc::from(&*source_str);
423 let _s: &[&dyn ToSql] = crate::params![s];
424 let r = s.to_sql();
425 r.unwrap();
426
427 let s: Rc<dyn ToSql> = Rc::new(source_str);
428 let _s: &[&dyn ToSql] = crate::params![s];
429 let r = s.to_sql();
430 r.unwrap();
431 }
432
433 #[cfg(feature = "i128_blob")]
434 #[test]
435 fn test_i128() -> crate::Result<()> {
436 use crate::Connection;
437 let db = Connection::open_in_memory()?;
438 db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
439 db.execute(
440 "
441 INSERT INTO foo(i128, desc) VALUES
442 (?1, 'zero'),
443 (?2, 'neg one'), (?3, 'neg two'),
444 (?4, 'pos one'), (?5, 'pos two'),
445 (?6, 'min'), (?7, 'max')",
446 [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
447 )?;
448
449 let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
450
451 let res = stmt
452 .query_map([], |row| {
453 Ok((row.get::<_, i128>(0)?, row.get::<_, String>(1)?))
454 })?
455 .collect::<Result<Vec<_>, _>>()?;
456
457 assert_eq!(
458 res,
459 &[
460 (i128::MIN, "min".to_owned()),
461 (-2, "neg two".to_owned()),
462 (-1, "neg one".to_owned()),
463 (0, "zero".to_owned()),
464 (1, "pos one".to_owned()),
465 (2, "pos two".to_owned()),
466 (i128::MAX, "max".to_owned()),
467 ]
468 );
469 Ok(())
470 }
471
472 #[cfg(feature = "i128_blob")]
473 #[test]
474 fn test_non_zero_i128() -> crate::Result<()> {
475 use std::num::NonZeroI128;
476 macro_rules! nz {
477 ($x:expr) => {
478 NonZeroI128::new($x).unwrap()
479 };
480 }
481
482 let db = crate::Connection::open_in_memory()?;
483 db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
484 db.execute(
485 "INSERT INTO foo(i128, desc) VALUES
486 (?1, 'neg one'), (?2, 'neg two'),
487 (?3, 'pos one'), (?4, 'pos two'),
488 (?5, 'min'), (?6, 'max')",
489 [
490 nz!(-1),
491 nz!(-2),
492 nz!(1),
493 nz!(2),
494 nz!(i128::MIN),
495 nz!(i128::MAX),
496 ],
497 )?;
498 let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
499
500 let res = stmt
501 .query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?
502 .collect::<Result<Vec<(NonZeroI128, String)>, _>>()?;
503
504 assert_eq!(
505 res,
506 &[
507 (nz!(i128::MIN), "min".to_owned()),
508 (nz!(-2), "neg two".to_owned()),
509 (nz!(-1), "neg one".to_owned()),
510 (nz!(1), "pos one".to_owned()),
511 (nz!(2), "pos two".to_owned()),
512 (nz!(i128::MAX), "max".to_owned()),
513 ]
514 );
515 let err = db.query_row("SELECT ?1", [0i128], |row| row.get::<_, NonZeroI128>(0));
516 assert_eq!(err, Err(crate::Error::IntegralValueOutOfRange(0, 0)));
517 Ok(())
518 }
519
520 #[cfg(feature = "uuid")]
521 #[test]
522 fn test_uuid() -> crate::Result<()> {
523 use crate::{params, Connection};
524 use uuid::Uuid;
525
526 let db = Connection::open_in_memory()?;
527 db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")?;
528
529 let id = Uuid::new_v4();
530
531 db.execute(
532 "INSERT INTO foo (id, label) VALUES (?1, ?2)",
533 params![id, "target"],
534 )?;
535
536 let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?1")?;
537
538 let mut rows = stmt.query(params![id])?;
539 let row = rows.next()?.unwrap();
540
541 let found_id: Uuid = row.get_unwrap(0);
542 let found_label: String = row.get_unwrap(1);
543
544 assert_eq!(found_id, id);
545 assert_eq!(found_label, "target");
546 Ok(())
547 }
548}