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