1use super::{Null, Value, ValueRef};
2#[cfg(feature = "fallible_uint")]
3use crate::Error;
4use crate::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(u64),
22
23 #[cfg(feature = "functions")]
25 Arg(usize),
26
27 #[cfg(feature = "pointer")]
29 Pointer(
30 (
31 *const std::ffi::c_void,
32 &'static std::ffi::CStr,
33 crate::ffi::sqlite3_destructor_type,
34 ),
35 ),
36}
37
38#[cfg(feature = "pointer")]
39impl<'a> ToSqlOutput<'a> {
40 pub fn from_rc<T>(rc: std::rc::Rc<T>, ptr_type: &'static std::ffi::CStr) -> ToSqlOutput<'a> {
45 unsafe extern "C" fn free_rc<T>(p: *mut std::ffi::c_void) {
46 unsafe {
47 std::rc::Rc::decrement_strong_count(p.cast::<T>());
48 }
49 }
50 ToSqlOutput::Pointer((
51 std::rc::Rc::into_raw(rc).cast::<std::ffi::c_void>(),
52 ptr_type,
53 Some(free_rc::<T>),
54 ))
55 }
56
57 pub fn new_boxed<T>(v: T, ptr_type: &'static std::ffi::CStr) -> ToSqlOutput<'a> {
62 use crate::util::free_boxed_value;
63
64 ToSqlOutput::Pointer((
65 Box::into_raw(Box::new(v)).cast::<std::ffi::c_void>(),
66 ptr_type,
67 Some(free_boxed_value::<T>),
68 ))
69 }
70}
71
72impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
75where
76 &'a T: Into<ValueRef<'a>>,
77{
78 #[inline]
79 fn from(t: &'a T) -> Self {
80 ToSqlOutput::Borrowed(t.into())
81 }
82}
83
84macro_rules! from_value(
90 ($t:ty) => (
91 impl From<$t> for ToSqlOutput<'_> {
92 #[inline]
93 fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
94 }
95 );
96 (non_zero $t:ty) => (
97 impl From<$t> for ToSqlOutput<'_> {
98 #[inline]
99 fn from(t: $t) -> Self { ToSqlOutput::Owned(t.get().into())}
100 }
101 )
102);
103from_value!(String);
104from_value!(Null);
105from_value!(bool);
106from_value!(i8);
107from_value!(i16);
108from_value!(i32);
109from_value!(i64);
110from_value!(isize);
111from_value!(u8);
112from_value!(u16);
113from_value!(u32);
114from_value!(f32);
115from_value!(f64);
116from_value!(Vec<u8>);
117
118from_value!(non_zero std::num::NonZeroI8);
119from_value!(non_zero std::num::NonZeroI16);
120from_value!(non_zero std::num::NonZeroI32);
121from_value!(non_zero std::num::NonZeroI64);
122from_value!(non_zero std::num::NonZeroIsize);
123from_value!(non_zero std::num::NonZeroU8);
124from_value!(non_zero std::num::NonZeroU16);
125from_value!(non_zero std::num::NonZeroU32);
126
127#[cfg(feature = "i128_blob")]
131from_value!(i128);
132
133#[cfg(feature = "i128_blob")]
134from_value!(non_zero std::num::NonZeroI128);
135
136#[cfg(feature = "uuid")]
137from_value!(uuid::Uuid);
138
139impl ToSql for ToSqlOutput<'_> {
140 #[inline]
141 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
142 Ok(match *self {
143 ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
144 ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
145
146 #[cfg(feature = "blob")]
147 ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
148 #[cfg(feature = "functions")]
149 ToSqlOutput::Arg(i) => ToSqlOutput::Arg(i),
150 #[cfg(feature = "pointer")]
151 ToSqlOutput::Pointer(p) => ToSqlOutput::Pointer(p),
152 })
153 }
154}
155
156pub trait ToSql {
159 fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
161}
162
163impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
164 #[inline]
165 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
166 self.as_ref().to_sql()
167 }
168}
169
170impl<T: ToSql + ?Sized> ToSql for Box<T> {
171 #[inline]
172 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
173 self.as_ref().to_sql()
174 }
175}
176
177impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
178 #[inline]
179 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
180 self.as_ref().to_sql()
181 }
182}
183
184impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
185 #[inline]
186 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
187 self.as_ref().to_sql()
188 }
189}
190
191macro_rules! to_sql_self(
204 ($t:ty) => (
205 impl ToSql for $t {
206 #[inline]
207 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
208 Ok(ToSqlOutput::from(*self))
209 }
210 }
211 )
212);
213
214to_sql_self!(Null);
215to_sql_self!(bool);
216to_sql_self!(i8);
217to_sql_self!(i16);
218to_sql_self!(i32);
219to_sql_self!(i64);
220to_sql_self!(isize);
221to_sql_self!(u8);
222to_sql_self!(u16);
223to_sql_self!(u32);
224to_sql_self!(f32);
225to_sql_self!(f64);
226
227to_sql_self!(std::num::NonZeroI8);
228to_sql_self!(std::num::NonZeroI16);
229to_sql_self!(std::num::NonZeroI32);
230to_sql_self!(std::num::NonZeroI64);
231to_sql_self!(std::num::NonZeroIsize);
232to_sql_self!(std::num::NonZeroU8);
233to_sql_self!(std::num::NonZeroU16);
234to_sql_self!(std::num::NonZeroU32);
235
236#[cfg(feature = "i128_blob")]
237to_sql_self!(i128);
238
239#[cfg(feature = "i128_blob")]
240to_sql_self!(std::num::NonZeroI128);
241
242#[cfg(feature = "uuid")]
243to_sql_self!(uuid::Uuid);
244
245#[cfg(feature = "fallible_uint")]
246macro_rules! to_sql_self_fallible(
247 ($t:ty) => (
248 impl ToSql for $t {
249 #[inline]
250 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
251 Ok(ToSqlOutput::Owned(Value::Integer(
252 i64::try_from(*self).map_err(
253 |err| Error::ToSqlConversionFailure(err.into())
255 )?
256 )))
257 }
258 }
259 );
260 (non_zero $t:ty) => (
261 impl ToSql for $t {
262 #[inline]
263 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
264 Ok(ToSqlOutput::Owned(Value::Integer(
265 i64::try_from(self.get()).map_err(
266 |err| Error::ToSqlConversionFailure(err.into())
268 )?
269 )))
270 }
271 }
272 )
273);
274
275#[cfg(feature = "fallible_uint")]
277to_sql_self_fallible!(u64);
278#[cfg(feature = "fallible_uint")]
279to_sql_self_fallible!(usize);
280#[cfg(feature = "fallible_uint")]
281to_sql_self_fallible!(non_zero std::num::NonZeroU64);
282#[cfg(feature = "fallible_uint")]
283to_sql_self_fallible!(non_zero std::num::NonZeroUsize);
284
285impl<T: ?Sized> ToSql for &'_ T
286where
287 T: ToSql,
288{
289 #[inline]
290 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
291 (*self).to_sql()
292 }
293}
294
295impl ToSql for String {
296 #[inline]
297 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
298 Ok(ToSqlOutput::from(self.as_str()))
299 }
300}
301
302impl ToSql for str {
303 #[inline]
304 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
305 Ok(ToSqlOutput::from(self))
306 }
307}
308
309impl ToSql for Vec<u8> {
310 #[inline]
311 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
312 Ok(ToSqlOutput::from(self.as_slice()))
313 }
314}
315
316impl<const N: usize> ToSql for [u8; N] {
317 #[inline]
318 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
319 Ok(ToSqlOutput::from(&self[..]))
320 }
321}
322
323impl ToSql for [u8] {
324 #[inline]
325 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
326 Ok(ToSqlOutput::from(self))
327 }
328}
329
330impl ToSql for Value {
331 #[inline]
332 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
333 Ok(ToSqlOutput::from(self))
334 }
335}
336
337impl<T: ToSql> ToSql for Option<T> {
338 #[inline]
339 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
340 match *self {
341 None => Ok(ToSqlOutput::from(Null)),
342 Some(ref t) => t.to_sql(),
343 }
344 }
345}
346
347#[cfg(test)]
348mod test {
349 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
350 use wasm_bindgen_test::wasm_bindgen_test as test;
351
352 use super::{ToSql, ToSqlOutput};
353 use crate::{Result, types::Value, types::ValueRef};
354
355 fn is_to_sql<T: ToSql>() {}
356
357 #[test]
358 fn to_sql() -> Result<()> {
359 assert_eq!(
360 ToSqlOutput::Borrowed(ValueRef::Null).to_sql()?,
361 ToSqlOutput::Borrowed(ValueRef::Null)
362 );
363 assert_eq!(
364 ToSqlOutput::Owned(Value::Null).to_sql()?,
365 ToSqlOutput::Borrowed(ValueRef::Null)
366 );
367 Ok(())
368 }
369
370 #[test]
371 fn test_integral_types() {
372 is_to_sql::<i8>();
373 is_to_sql::<i16>();
374 is_to_sql::<i32>();
375 is_to_sql::<i64>();
376 is_to_sql::<isize>();
377 is_to_sql::<u8>();
378 is_to_sql::<u16>();
379 is_to_sql::<u32>();
380 #[cfg(feature = "fallible_uint")]
381 is_to_sql::<u64>();
382 #[cfg(feature = "fallible_uint")]
383 is_to_sql::<usize>();
384 }
385
386 #[test]
387 fn test_nonzero_types() {
388 is_to_sql::<std::num::NonZeroI8>();
389 is_to_sql::<std::num::NonZeroI16>();
390 is_to_sql::<std::num::NonZeroI32>();
391 is_to_sql::<std::num::NonZeroI64>();
392 is_to_sql::<std::num::NonZeroIsize>();
393 is_to_sql::<std::num::NonZeroU8>();
394 is_to_sql::<std::num::NonZeroU16>();
395 is_to_sql::<std::num::NonZeroU32>();
396 #[cfg(feature = "fallible_uint")]
397 is_to_sql::<std::num::NonZeroU64>();
398 #[cfg(feature = "fallible_uint")]
399 is_to_sql::<std::num::NonZeroUsize>();
400 }
401
402 #[test]
403 fn test_u8_array() {
404 let a: [u8; 99] = [0u8; 99];
405 let _a: &[&dyn ToSql] = crate::params![a];
406 let r = ToSql::to_sql(&a);
407
408 r.unwrap();
409 }
410
411 #[test]
412 fn test_cow_str() {
413 use std::borrow::Cow;
414 let s = "str";
415 let cow: Cow<str> = Cow::Borrowed(s);
416 let r = cow.to_sql();
417 r.unwrap();
418 let cow: Cow<str> = Cow::Owned::<str>(String::from(s));
419 let r = cow.to_sql();
420 r.unwrap();
421 let _p: &[&dyn ToSql] = crate::params![cow];
423 }
424
425 #[test]
426 fn test_box_dyn() {
427 let s: Box<dyn ToSql> = Box::new("Hello world!");
428 let _s: &[&dyn ToSql] = crate::params![s];
429 let r = ToSql::to_sql(&s);
430
431 r.unwrap();
432 }
433
434 #[test]
435 fn test_box_deref() {
436 let s: Box<str> = "Hello world!".into();
437 let _s: &[&dyn ToSql] = crate::params![s];
438 let r = s.to_sql();
439
440 r.unwrap();
441 }
442
443 #[test]
444 fn test_box_direct() {
445 let s: Box<str> = "Hello world!".into();
446 let _s: &[&dyn ToSql] = crate::params![s];
447 let r = ToSql::to_sql(&s);
448
449 r.unwrap();
450 }
451
452 #[test]
453 fn test_cells() {
454 use std::{rc::Rc, sync::Arc};
455
456 let source_str: Box<str> = "Hello world!".into();
457
458 let s: Rc<Box<str>> = Rc::new(source_str.clone());
459 let _s: &[&dyn ToSql] = crate::params![s];
460 let r = s.to_sql();
461 r.unwrap();
462
463 let s: Arc<Box<str>> = Arc::new(source_str.clone());
464 let _s: &[&dyn ToSql] = crate::params![s];
465 let r = s.to_sql();
466 r.unwrap();
467
468 let s: Arc<str> = Arc::from(&*source_str);
469 let _s: &[&dyn ToSql] = crate::params![s];
470 let r = s.to_sql();
471 r.unwrap();
472
473 let s: Arc<dyn ToSql> = Arc::new(source_str.clone());
474 let _s: &[&dyn ToSql] = crate::params![s];
475 let r = s.to_sql();
476 r.unwrap();
477
478 let s: Rc<str> = Rc::from(&*source_str);
479 let _s: &[&dyn ToSql] = crate::params![s];
480 let r = s.to_sql();
481 r.unwrap();
482
483 let s: Rc<dyn ToSql> = Rc::new(source_str);
484 let _s: &[&dyn ToSql] = crate::params![s];
485 let r = s.to_sql();
486 r.unwrap();
487 }
488
489 #[cfg(feature = "i128_blob")]
490 #[test]
491 #[cfg_attr(miri, ignore)]
492 fn test_i128() -> Result<()> {
493 use crate::Connection;
494 let db = Connection::open_in_memory()?;
495 db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
496 db.execute(
497 "
498 INSERT INTO foo(i128, desc) VALUES
499 (?1, 'zero'),
500 (?2, 'neg one'), (?3, 'neg two'),
501 (?4, 'pos one'), (?5, 'pos two'),
502 (?6, 'min'), (?7, 'max')",
503 [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
504 )?;
505
506 let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
507
508 let res = stmt
509 .query_map([], |row| {
510 Ok((row.get::<_, i128>(0)?, row.get::<_, String>(1)?))
511 })?
512 .collect::<Result<Vec<_>, _>>()?;
513
514 assert_eq!(
515 res,
516 &[
517 (i128::MIN, "min".to_owned()),
518 (-2, "neg two".to_owned()),
519 (-1, "neg one".to_owned()),
520 (0, "zero".to_owned()),
521 (1, "pos one".to_owned()),
522 (2, "pos two".to_owned()),
523 (i128::MAX, "max".to_owned()),
524 ]
525 );
526 Ok(())
527 }
528
529 #[cfg(feature = "i128_blob")]
530 #[test]
531 #[cfg_attr(miri, ignore)]
532 fn test_non_zero_i128() -> Result<()> {
533 use std::num::NonZeroI128;
534 macro_rules! nz {
535 ($x:expr) => {
536 NonZeroI128::new($x).unwrap()
537 };
538 }
539
540 let db = crate::Connection::open_in_memory()?;
541 db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
542 db.execute(
543 "INSERT INTO foo(i128, desc) VALUES
544 (?1, 'neg one'), (?2, 'neg two'),
545 (?3, 'pos one'), (?4, 'pos two'),
546 (?5, 'min'), (?6, 'max')",
547 [
548 nz!(-1),
549 nz!(-2),
550 nz!(1),
551 nz!(2),
552 nz!(i128::MIN),
553 nz!(i128::MAX),
554 ],
555 )?;
556 let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
557
558 let res = stmt
559 .query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?
560 .collect::<Result<Vec<(NonZeroI128, String)>, _>>()?;
561
562 assert_eq!(
563 res,
564 &[
565 (nz!(i128::MIN), "min".to_owned()),
566 (nz!(-2), "neg two".to_owned()),
567 (nz!(-1), "neg one".to_owned()),
568 (nz!(1), "pos one".to_owned()),
569 (nz!(2), "pos two".to_owned()),
570 (nz!(i128::MAX), "max".to_owned()),
571 ]
572 );
573 let err = db.query_row("SELECT ?1", [0i128], |row| row.get::<_, NonZeroI128>(0));
574 assert_eq!(err, Err(crate::Error::IntegralValueOutOfRange(0, 0)));
575 Ok(())
576 }
577
578 #[cfg(feature = "uuid")]
579 #[test]
580 #[cfg_attr(miri, ignore)]
581 fn test_uuid() -> Result<()> {
582 use crate::{Connection, params};
583 use uuid::Uuid;
584
585 let db = Connection::open_in_memory()?;
586 db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")?;
587
588 let id = Uuid::new_v4();
589
590 db.execute(
591 "INSERT INTO foo (id, label) VALUES (?1, ?2)",
592 params![id, "target"],
593 )?;
594
595 let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?1")?;
596
597 let mut rows = stmt.query(params![id])?;
598 let row = rows.next()?.unwrap();
599
600 let found_id: Uuid = row.get_unwrap(0);
601 let found_label: String = row.get_unwrap(1);
602
603 assert_eq!(found_id, id);
604 assert_eq!(found_label, "target");
605 Ok(())
606 }
607
608 #[cfg(feature = "pointer")]
609 #[test]
610 fn from_rc() {
611 let rc = std::rc::Rc::new("rc".to_owned());
612 if let ToSqlOutput::Pointer((ptr, _, Some(destructor))) = ToSqlOutput::from_rc(rc, c"rc") {
613 unsafe { destructor(ptr.cast_mut()) }
614 }
615 }
616
617 #[cfg(feature = "pointer")]
618 #[test]
619 fn new_boxed() {
620 let data = "box".to_owned();
621 if let ToSqlOutput::Pointer((ptr, _, Some(destructor))) =
622 ToSqlOutput::new_boxed(data, c"box")
623 {
624 unsafe { destructor(ptr.cast_mut()) }
625 }
626 }
627}