1use super::{Null, TimeUnit, Value, ValueRef};
2use crate::Result;
3use std::borrow::Cow;
4
5#[derive(Clone, Debug, PartialEq)]
8#[non_exhaustive]
9pub enum ToSqlOutput<'a> {
10 Borrowed(ValueRef<'a>),
12
13 Owned(Value),
15}
16
17impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
20where
21 &'a T: Into<ValueRef<'a>>,
22{
23 #[inline]
24 fn from(t: &'a T) -> Self {
25 ToSqlOutput::Borrowed(t.into())
26 }
27}
28
29macro_rules! from_value(
35 ($t:ty) => (
36 impl From<$t> for ToSqlOutput<'_> {
37 #[inline]
38 fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
39 }
40 )
41);
42from_value!(String);
43from_value!(Null);
44from_value!(bool);
45from_value!(i8);
46from_value!(i16);
47from_value!(i32);
48from_value!(i64);
49from_value!(i128);
50from_value!(isize);
51from_value!(u8);
52from_value!(u16);
53from_value!(u32);
54from_value!(u64);
55from_value!(usize);
56from_value!(f32);
57from_value!(f64);
58from_value!(Vec<u8>);
59
60#[cfg(feature = "uuid")]
61from_value!(uuid::Uuid);
62
63impl ToSql for ToSqlOutput<'_> {
64 #[inline]
65 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
66 Ok(match *self {
67 ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
68 ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)),
69 })
70 }
71}
72
73pub trait ToSql {
76 fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
78}
79
80impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
81 #[inline]
82 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
83 self.as_ref().to_sql()
84 }
85}
86
87impl<T: ToSql + ?Sized> ToSql for Box<T> {
88 #[inline]
89 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
90 self.as_ref().to_sql()
91 }
92}
93
94impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
95 #[inline]
96 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
97 self.as_ref().to_sql()
98 }
99}
100
101impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
102 #[inline]
103 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
104 self.as_ref().to_sql()
105 }
106}
107
108macro_rules! to_sql_self(
121 ($t:ty) => (
122 impl ToSql for $t {
123 #[inline]
124 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
125 Ok(ToSqlOutput::from(*self))
126 }
127 }
128 )
129);
130
131to_sql_self!(Null);
132to_sql_self!(bool);
133to_sql_self!(i8);
134to_sql_self!(i16);
135to_sql_self!(i32);
136to_sql_self!(i64);
137to_sql_self!(i128);
138to_sql_self!(isize);
139to_sql_self!(u8);
140to_sql_self!(u16);
141to_sql_self!(u32);
142to_sql_self!(f32);
143to_sql_self!(f64);
144to_sql_self!(u64);
145to_sql_self!(usize);
146
147#[cfg(feature = "uuid")]
148to_sql_self!(uuid::Uuid);
149
150impl<T: ?Sized> ToSql for &'_ T
151where
152 T: ToSql,
153{
154 #[inline]
155 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
156 (*self).to_sql()
157 }
158}
159
160impl ToSql for String {
161 #[inline]
162 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
163 Ok(ToSqlOutput::from(self.as_str()))
164 }
165}
166
167impl ToSql for str {
168 #[inline]
169 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
170 Ok(ToSqlOutput::from(self))
171 }
172}
173
174impl ToSql for Vec<u8> {
175 #[inline]
176 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
177 Ok(ToSqlOutput::from(self.as_slice()))
178 }
179}
180
181impl ToSql for [u8] {
182 #[inline]
183 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
184 Ok(ToSqlOutput::from(self))
185 }
186}
187
188impl ToSql for Value {
189 #[inline]
190 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
191 Ok(ToSqlOutput::from(self))
192 }
193}
194
195impl<T: ToSql> ToSql for Option<T> {
196 #[inline]
197 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
198 match *self {
199 None => Ok(ToSqlOutput::from(Null)),
200 Some(ref t) => t.to_sql(),
201 }
202 }
203}
204
205impl ToSql for std::time::Duration {
206 fn to_sql(&self) -> crate::Result<ToSqlOutput<'_>> {
207 Ok(ToSqlOutput::Owned(Value::Timestamp(
208 TimeUnit::Microsecond,
209 self.as_micros() as i64,
210 )))
211 }
212}
213
214#[cfg(test)]
215mod test {
216 use super::ToSql;
217
218 fn is_to_sql<T: ToSql>() {}
219
220 #[test]
221 fn test_integral_types() {
222 is_to_sql::<i8>();
223 is_to_sql::<i16>();
224 is_to_sql::<i32>();
225 is_to_sql::<i64>();
226 is_to_sql::<u8>();
227 is_to_sql::<u16>();
228 is_to_sql::<u32>();
229 }
230
231 #[test]
232 fn test_cow_str() {
233 use std::borrow::Cow;
234 let s = "str";
235 let cow: Cow<'_, str> = Cow::Borrowed(s);
236 let r = cow.to_sql();
237 assert!(r.is_ok());
238 let cow: Cow<'_, str> = Cow::Owned(String::from(s));
239 let r = cow.to_sql();
240 assert!(r.is_ok());
241 let _p: &[&dyn ToSql] = crate::params![cow];
243 }
244
245 #[test]
246 fn test_box_dyn() {
247 let s: Box<dyn ToSql> = Box::new("Hello world!");
248 let _s: &[&dyn ToSql] = crate::params![s];
249 let r = ToSql::to_sql(&s);
250
251 assert!(r.is_ok());
252 }
253
254 #[test]
255 fn test_box_deref() {
256 let s: Box<str> = "Hello world!".into();
257 let _s: &[&dyn ToSql] = crate::params![s];
258 let r = s.to_sql();
259
260 assert!(r.is_ok());
261 }
262
263 #[test]
264 fn test_box_direct() {
265 let s: Box<str> = "Hello world!".into();
266 let _s: &[&dyn ToSql] = crate::params![s];
267 let r = ToSql::to_sql(&s);
268
269 assert!(r.is_ok());
270 }
271
272 #[test]
273 fn test_cells() {
274 use std::{rc::Rc, sync::Arc};
275
276 let source_str: Box<str> = "Hello world!".into();
277
278 let s: Rc<Box<str>> = Rc::new(source_str.clone());
279 let _s: &[&dyn ToSql] = crate::params![s];
280 let r = s.to_sql();
281 assert!(r.is_ok());
282
283 let s: Arc<Box<str>> = Arc::new(source_str.clone());
284 let _s: &[&dyn ToSql] = crate::params![s];
285 let r = s.to_sql();
286 assert!(r.is_ok());
287
288 let s: Arc<str> = Arc::from(&*source_str);
289 let _s: &[&dyn ToSql] = crate::params![s];
290 let r = s.to_sql();
291 assert!(r.is_ok());
292
293 let s: Arc<dyn ToSql> = Arc::new(source_str.clone());
294 let _s: &[&dyn ToSql] = crate::params![s];
295 let r = s.to_sql();
296 assert!(r.is_ok());
297
298 let s: Rc<str> = Rc::from(&*source_str);
299 let _s: &[&dyn ToSql] = crate::params![s];
300 let r = s.to_sql();
301 assert!(r.is_ok());
302
303 let s: Rc<dyn ToSql> = Rc::new(source_str);
304 let _s: &[&dyn ToSql] = crate::params![s];
305 let r = s.to_sql();
306 assert!(r.is_ok());
307 }
308
309 #[test]
311 fn test_uuid_gen() -> crate::Result<()> {
312 use crate::Connection;
313
314 let db = Connection::open_in_memory()?;
315 db.execute_batch("CREATE TABLE foo (id uuid NOT NULL);")?;
316
317 db.execute("INSERT INTO foo (id) VALUES (gen_random_uuid())", [])?;
318
319 let found_id: String = db.prepare("SELECT id FROM foo")?.query_one([], |r| r.get(0))?;
320 assert_eq!(found_id.len(), 36);
321 Ok(())
322 }
323
324 #[cfg(feature = "uuid")]
325 #[test]
326 fn test_uuid_blob_type() -> crate::Result<()> {
327 use crate::{params, Connection};
328 use uuid::Uuid;
329
330 let db = Connection::open_in_memory()?;
331 db.execute_batch("CREATE TABLE foo (id BLOB CONSTRAINT uuidchk CHECK (octet_length(id) = 16), label TEXT);")?;
332
333 let id = Uuid::new_v4();
334 let id_vec = id.as_bytes().to_vec();
335 db.execute("INSERT INTO foo (id, label) VALUES (?, ?)", params![id_vec, "target"])?;
336
337 let (found_id, found_label): (Uuid, String) = db
338 .prepare("SELECT id, label FROM foo WHERE id = ?")?
339 .query_one(params![id_vec], |r| Ok((r.get_unwrap(0), r.get_unwrap(1))))?;
340 assert_eq!(found_id, id);
341 assert_eq!(found_label, "target");
342 Ok(())
343 }
344
345 #[cfg(feature = "uuid")]
346 #[test]
347 fn test_uuid_type() -> crate::Result<()> {
348 use crate::{params, Connection};
349 use uuid::Uuid;
350
351 let db = Connection::open_in_memory()?;
352 db.execute_batch("CREATE TABLE foo (id uuid, label TEXT);")?;
353
354 let id = Uuid::new_v4();
355 db.execute("INSERT INTO foo (id, label) VALUES (?, ?)", params![id, "target"])?;
356
357 let (found_id, found_label): (Uuid, String) = db
358 .prepare("SELECT id, label FROM foo WHERE id = ?")?
359 .query_one(params![id], |r| Ok((r.get_unwrap(0), r.get_unwrap(1))))?;
360 assert_eq!(found_id, id);
361 assert_eq!(found_label, "target");
362 Ok(())
363 }
364}