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