1use super::{Null, Value, ValueRef};
2#[cfg(feature = "array")]
3use crate::vtab::array::Array;
4use crate::{Error, Result};
5use std::borrow::Cow;
6use std::convert::TryFrom;
7
8#[derive(Clone, Debug, PartialEq)]
11#[non_exhaustive]
12pub enum ToSqlOutput<'a> {
13 Borrowed(ValueRef<'a>),
15
16 Owned(Value),
18
19 #[cfg(feature = "blob")]
22 ZeroBlob(i32),
23
24 #[cfg(feature = "array")]
26 Array(Array),
27}
28
29impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
32where
33 &'a T: Into<ValueRef<'a>>,
34{
35 #[inline]
36 fn from(t: &'a T) -> Self {
37 ToSqlOutput::Borrowed(t.into())
38 }
39}
40
41macro_rules! from_value(
47 ($t:ty) => (
48 impl From<$t> for ToSqlOutput<'_> {
49 #[inline]
50 fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
51 }
52 )
53);
54from_value!(String);
55from_value!(Null);
56from_value!(bool);
57from_value!(i8);
58from_value!(i16);
59from_value!(i32);
60from_value!(i64);
61from_value!(isize);
62from_value!(u8);
63from_value!(u16);
64from_value!(u32);
65from_value!(f32);
66from_value!(f64);
67from_value!(Vec<u8>);
68
69#[cfg(feature = "i128_blob")]
73from_value!(i128);
74
75#[cfg(feature = "uuid")]
76from_value!(uuid::Uuid);
77
78impl ToSql for ToSqlOutput<'_> {
79 #[inline]
80 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
81 Ok(match *self {
82 ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
83 ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
84
85 #[cfg(feature = "blob")]
86 ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i),
87 #[cfg(feature = "array")]
88 ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()),
89 })
90 }
91}
92
93pub trait ToSql {
96 fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
98}
99
100impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
101 #[inline]
102 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
103 self.as_ref().to_sql()
104 }
105}
106
107impl<T: ToSql + ?Sized> ToSql for Box<T> {
108 #[inline]
109 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
110 self.as_ref().to_sql()
111 }
112}
113
114impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
115 #[inline]
116 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
117 self.as_ref().to_sql()
118 }
119}
120
121impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
122 #[inline]
123 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
124 self.as_ref().to_sql()
125 }
126}
127
128macro_rules! to_sql_self(
141 ($t:ty) => (
142 impl ToSql for $t {
143 #[inline]
144 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
145 Ok(ToSqlOutput::from(*self))
146 }
147 }
148 )
149);
150
151to_sql_self!(Null);
152to_sql_self!(bool);
153to_sql_self!(i8);
154to_sql_self!(i16);
155to_sql_self!(i32);
156to_sql_self!(i64);
157to_sql_self!(isize);
158to_sql_self!(u8);
159to_sql_self!(u16);
160to_sql_self!(u32);
161to_sql_self!(f32);
162to_sql_self!(f64);
163
164#[cfg(feature = "i128_blob")]
165to_sql_self!(i128);
166
167#[cfg(feature = "uuid")]
168to_sql_self!(uuid::Uuid);
169
170macro_rules! to_sql_self_fallible(
171 ($t:ty) => (
172 impl ToSql for $t {
173 #[inline]
174 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
175 Ok(ToSqlOutput::Owned(Value::Integer(
176 i64::try_from(*self).map_err(
177 |err| Error::ToSqlConversionFailure(err.into())
179 )?
180 )))
181 }
182 }
183 )
184);
185
186to_sql_self_fallible!(u64);
188to_sql_self_fallible!(usize);
189
190impl<T: ?Sized> ToSql for &'_ T
191where
192 T: ToSql,
193{
194 #[inline]
195 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
196 (*self).to_sql()
197 }
198}
199
200impl ToSql for String {
201 #[inline]
202 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
203 Ok(ToSqlOutput::from(self.as_str()))
204 }
205}
206
207impl ToSql for str {
208 #[inline]
209 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
210 Ok(ToSqlOutput::from(self))
211 }
212}
213
214impl ToSql for Vec<u8> {
215 #[inline]
216 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
217 Ok(ToSqlOutput::from(self.as_slice()))
218 }
219}
220
221impl ToSql for [u8] {
222 #[inline]
223 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
224 Ok(ToSqlOutput::from(self))
225 }
226}
227
228impl ToSql for Value {
229 #[inline]
230 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
231 Ok(ToSqlOutput::from(self))
232 }
233}
234
235impl<T: ToSql> ToSql for Option<T> {
236 #[inline]
237 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
238 match *self {
239 None => Ok(ToSqlOutput::from(Null)),
240 Some(ref t) => t.to_sql(),
241 }
242 }
243}
244
245#[cfg(test)]
246mod test {
247 use super::ToSql;
248
249 fn is_to_sql<T: ToSql>() {}
250
251 #[test]
252 fn test_integral_types() {
253 is_to_sql::<i8>();
254 is_to_sql::<i16>();
255 is_to_sql::<i32>();
256 is_to_sql::<i64>();
257 is_to_sql::<u8>();
258 is_to_sql::<u16>();
259 is_to_sql::<u32>();
260 }
261
262 #[test]
263 fn test_cow_str() {
264 use std::borrow::Cow;
265 let s = "str";
266 let cow: Cow<str> = Cow::Borrowed(s);
267 let r = cow.to_sql();
268 assert!(r.is_ok());
269 let cow: Cow<str> = Cow::Owned::<str>(String::from(s));
270 let r = cow.to_sql();
271 assert!(r.is_ok());
272 let _p: &[&dyn ToSql] = crate::params![cow];
274 }
275
276 #[test]
277 fn test_box_dyn() {
278 let s: Box<dyn ToSql> = Box::new("Hello world!");
279 let _s: &[&dyn ToSql] = crate::params![s];
280 let r = ToSql::to_sql(&s);
281
282 assert!(r.is_ok());
283 }
284
285 #[test]
286 fn test_box_deref() {
287 let s: Box<str> = "Hello world!".into();
288 let _s: &[&dyn ToSql] = crate::params![s];
289 let r = s.to_sql();
290
291 assert!(r.is_ok());
292 }
293
294 #[test]
295 fn test_box_direct() {
296 let s: Box<str> = "Hello world!".into();
297 let _s: &[&dyn ToSql] = crate::params![s];
298 let r = ToSql::to_sql(&s);
299
300 assert!(r.is_ok());
301 }
302
303 #[test]
304 fn test_cells() {
305 use std::{rc::Rc, sync::Arc};
306
307 let source_str: Box<str> = "Hello world!".into();
308
309 let s: Rc<Box<str>> = Rc::new(source_str.clone());
310 let _s: &[&dyn ToSql] = crate::params![s];
311 let r = s.to_sql();
312 assert!(r.is_ok());
313
314 let s: Arc<Box<str>> = Arc::new(source_str.clone());
315 let _s: &[&dyn ToSql] = crate::params![s];
316 let r = s.to_sql();
317 assert!(r.is_ok());
318
319 let s: Arc<str> = Arc::from(&*source_str);
320 let _s: &[&dyn ToSql] = crate::params![s];
321 let r = s.to_sql();
322 assert!(r.is_ok());
323
324 let s: Arc<dyn ToSql> = Arc::new(source_str.clone());
325 let _s: &[&dyn ToSql] = crate::params![s];
326 let r = s.to_sql();
327 assert!(r.is_ok());
328
329 let s: Rc<str> = Rc::from(&*source_str);
330 let _s: &[&dyn ToSql] = crate::params![s];
331 let r = s.to_sql();
332 assert!(r.is_ok());
333
334 let s: Rc<dyn ToSql> = Rc::new(source_str);
335 let _s: &[&dyn ToSql] = crate::params![s];
336 let r = s.to_sql();
337 assert!(r.is_ok());
338 }
339
340 #[cfg(feature = "i128_blob")]
341 #[test]
342 fn test_i128() -> crate::Result<()> {
343 use crate::Connection;
344 use std::i128;
345 let db = Connection::open_in_memory()?;
346 db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
347 db.execute(
348 "
349 INSERT INTO foo(i128, desc) VALUES
350 (?, 'zero'),
351 (?, 'neg one'), (?, 'neg two'),
352 (?, 'pos one'), (?, 'pos two'),
353 (?, 'min'), (?, 'max')",
354 [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
355 )?;
356
357 let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
358
359 let res = stmt
360 .query_map([], |row| {
361 Ok((row.get::<_, i128>(0)?, row.get::<_, String>(1)?))
362 })?
363 .collect::<Result<Vec<_>, _>>()?;
364
365 assert_eq!(
366 res,
367 &[
368 (i128::MIN, "min".to_owned()),
369 (-2, "neg two".to_owned()),
370 (-1, "neg one".to_owned()),
371 (0, "zero".to_owned()),
372 (1, "pos one".to_owned()),
373 (2, "pos two".to_owned()),
374 (i128::MAX, "max".to_owned()),
375 ]
376 );
377 Ok(())
378 }
379
380 #[cfg(feature = "uuid")]
381 #[test]
382 fn test_uuid() -> crate::Result<()> {
383 use crate::{params, Connection};
384 use uuid::Uuid;
385
386 let db = Connection::open_in_memory()?;
387 db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")?;
388
389 let id = Uuid::new_v4();
390
391 db.execute(
392 "INSERT INTO foo (id, label) VALUES (?, ?)",
393 params![id, "target"],
394 )?;
395
396 let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?")?;
397
398 let mut rows = stmt.query(params![id])?;
399 let row = rows.next()?.unwrap();
400
401 let found_id: Uuid = row.get_unwrap(0);
402 let found_label: String = row.get_unwrap(1);
403
404 assert_eq!(found_id, id);
405 assert_eq!(found_label, "target");
406 Ok(())
407 }
408}