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