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