1use super::{Null, TimeUnit, Value, ValueRef, binding_unsupported_value, value_ref_from_value};
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!(u128);
51from_value!(isize);
52from_value!(u8);
53from_value!(u16);
54from_value!(u32);
55from_value!(u64);
56from_value!(usize);
57from_value!(f32);
58from_value!(f64);
59from_value!(Vec<u8>);
60
61#[cfg(feature = "uuid")]
62from_value!(uuid::Uuid);
63
64impl ToSql for ToSqlOutput<'_> {
65 #[inline]
66 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
67 Ok(match *self {
68 ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
69 ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(value_ref_from_value(v, binding_unsupported_value)?),
70 })
71 }
72}
73
74pub trait ToSql {
78 fn to_sql(&self) -> Result<ToSqlOutput<'_>>;
80}
81
82impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
83 #[inline]
84 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
85 self.as_ref().to_sql()
86 }
87}
88
89impl<T: ToSql + ?Sized> ToSql for Box<T> {
90 #[inline]
91 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
92 self.as_ref().to_sql()
93 }
94}
95
96impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
97 #[inline]
98 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
99 self.as_ref().to_sql()
100 }
101}
102
103impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
104 #[inline]
105 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
106 self.as_ref().to_sql()
107 }
108}
109
110macro_rules! to_sql_self(
123 ($t:ty) => (
124 impl ToSql for $t {
125 #[inline]
126 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
127 Ok(ToSqlOutput::from(*self))
128 }
129 }
130 )
131);
132
133to_sql_self!(Null);
134to_sql_self!(bool);
135to_sql_self!(i8);
136to_sql_self!(i16);
137to_sql_self!(i32);
138to_sql_self!(i64);
139to_sql_self!(i128);
140to_sql_self!(u128);
141to_sql_self!(isize);
142to_sql_self!(u8);
143to_sql_self!(u16);
144to_sql_self!(u32);
145to_sql_self!(f32);
146to_sql_self!(f64);
147to_sql_self!(u64);
148to_sql_self!(usize);
149
150#[cfg(feature = "uuid")]
151to_sql_self!(uuid::Uuid);
152
153impl<T: ?Sized> ToSql for &'_ T
154where
155 T: ToSql,
156{
157 #[inline]
158 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
159 (*self).to_sql()
160 }
161}
162
163impl ToSql for String {
164 #[inline]
165 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
166 Ok(ToSqlOutput::from(self.as_str()))
167 }
168}
169
170impl ToSql for str {
171 #[inline]
172 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
173 Ok(ToSqlOutput::from(self))
174 }
175}
176
177impl ToSql for Vec<u8> {
178 #[inline]
179 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
180 Ok(ToSqlOutput::from(self.as_slice()))
181 }
182}
183
184impl ToSql for [u8] {
185 #[inline]
186 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
187 Ok(ToSqlOutput::from(self))
188 }
189}
190
191impl ToSql for Value {
192 #[inline]
193 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
194 Ok(ToSqlOutput::Borrowed(value_ref_from_value(
195 self,
196 binding_unsupported_value,
197 )?))
198 }
199}
200
201impl<T: ToSql> ToSql for Option<T> {
202 #[inline]
203 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
204 match *self {
205 None => Ok(ToSqlOutput::from(Null)),
206 Some(ref t) => t.to_sql(),
207 }
208 }
209}
210
211impl ToSql for std::time::Duration {
212 fn to_sql(&self) -> crate::Result<ToSqlOutput<'_>> {
213 Ok(ToSqlOutput::Owned(Value::Timestamp(
214 TimeUnit::Microsecond,
215 self.as_micros() as i64,
216 )))
217 }
218}
219
220#[cfg(test)]
221mod test {
222 use super::{ToSql, ToSqlOutput};
223
224 fn is_to_sql<T: ToSql>() {}
225
226 #[test]
227 fn test_integral_types() {
228 is_to_sql::<i8>();
229 is_to_sql::<i16>();
230 is_to_sql::<i32>();
231 is_to_sql::<i64>();
232 is_to_sql::<i128>();
233 is_to_sql::<isize>();
234 is_to_sql::<u8>();
235 is_to_sql::<u16>();
236 is_to_sql::<u32>();
237 is_to_sql::<u64>();
238 is_to_sql::<u128>();
239 is_to_sql::<usize>();
240 }
241
242 #[test]
243 fn test_value_to_sql_rejects_unsupported_variants() {
244 use crate::{
245 Error,
246 types::{OrderedMap, Value},
247 };
248
249 let cases: &[(&str, Value)] = &[
250 ("List", Value::List(vec![Value::Int(1)])),
251 ("Array", Value::Array(vec![Value::Int(1)])),
252 (
253 "Struct",
254 Value::Struct(OrderedMap::from(vec![("k".to_string(), Value::Int(1))])),
255 ),
256 (
257 "Map",
258 Value::Map(OrderedMap::from(vec![(Value::Int(1), Value::Int(2))])),
259 ),
260 ("Union", Value::Union(Box::new(Value::Int(1)))),
261 ("Enum", Value::Enum("variant".to_string())),
262 ];
263
264 for (variant, value) in cases {
265 match value.to_sql() {
266 Err(Error::ToSqlConversionFailure(e)) => {
267 let msg = e.to_string();
268 assert!(
269 msg.contains(&format!("binding {variant} parameters is not yet supported")),
270 "{variant}: unexpected message {msg}"
271 );
272 }
273 Err(other) => panic!("{variant}: expected ToSqlConversionFailure, got {other:?}"),
274 Ok(_) => panic!("{variant}: expected error, got Ok"),
275 }
276 match ToSqlOutput::Owned(value.clone()).to_sql() {
277 Err(Error::ToSqlConversionFailure(e)) => {
278 let msg = e.to_string();
279 assert!(
280 msg.contains(&format!("binding {variant} parameters is not yet supported")),
281 "{variant}: unexpected message {msg}"
282 );
283 }
284 Err(other) => panic!("{variant}: expected ToSqlConversionFailure, got {other:?}"),
285 Ok(_) => panic!("{variant}: expected error, got Ok"),
286 }
287 }
288 }
289
290 #[test]
291 fn test_cow_str() {
292 use std::borrow::Cow;
293 let s = "str";
294 let cow: Cow<'_, str> = Cow::Borrowed(s);
295 let r = cow.to_sql();
296 assert!(r.is_ok());
297 let cow: Cow<'_, str> = Cow::Owned(String::from(s));
298 let r = cow.to_sql();
299 assert!(r.is_ok());
300 let _p: &[&dyn ToSql] = crate::params![cow];
302 }
303
304 #[test]
305 fn test_box_dyn() {
306 let s: Box<dyn ToSql> = Box::new("Hello world!");
307 let _s: &[&dyn ToSql] = crate::params![s];
308 let r = ToSql::to_sql(&s);
309
310 assert!(r.is_ok());
311 }
312
313 #[test]
314 fn test_box_deref() {
315 let s: Box<str> = "Hello world!".into();
316 let _s: &[&dyn ToSql] = crate::params![s];
317 let r = s.to_sql();
318
319 assert!(r.is_ok());
320 }
321
322 #[test]
323 fn test_box_direct() {
324 let s: Box<str> = "Hello world!".into();
325 let _s: &[&dyn ToSql] = crate::params![s];
326 let r = ToSql::to_sql(&s);
327
328 assert!(r.is_ok());
329 }
330
331 #[test]
332 fn test_cells() {
333 use std::{rc::Rc, sync::Arc};
334
335 let source_str: Box<str> = "Hello world!".into();
336
337 let s: Rc<Box<str>> = Rc::new(source_str.clone());
338 let _s: &[&dyn ToSql] = crate::params![s];
339 let r = s.to_sql();
340 assert!(r.is_ok());
341
342 let s: Arc<Box<str>> = Arc::new(source_str.clone());
343 let _s: &[&dyn ToSql] = crate::params![s];
344 let r = s.to_sql();
345 assert!(r.is_ok());
346
347 let s: Arc<str> = Arc::from(&*source_str);
348 let _s: &[&dyn ToSql] = crate::params![s];
349 let r = s.to_sql();
350 assert!(r.is_ok());
351
352 let s: Arc<dyn ToSql> = Arc::new(source_str.clone());
353 let _s: &[&dyn ToSql] = crate::params![s];
354 let r = s.to_sql();
355 assert!(r.is_ok());
356
357 let s: Rc<str> = Rc::from(&*source_str);
358 let _s: &[&dyn ToSql] = crate::params![s];
359 let r = s.to_sql();
360 assert!(r.is_ok());
361
362 let s: Rc<dyn ToSql> = Rc::new(source_str);
363 let _s: &[&dyn ToSql] = crate::params![s];
364 let r = s.to_sql();
365 assert!(r.is_ok());
366 }
367
368 #[test]
370 fn test_uuid_gen() -> crate::Result<()> {
371 use crate::Connection;
372
373 let db = Connection::open_in_memory()?;
374 db.execute_batch("CREATE TABLE foo (id uuid NOT NULL);")?;
375
376 db.execute("INSERT INTO foo (id) VALUES (gen_random_uuid())", [])?;
377
378 let found_id: String = db.prepare("SELECT id FROM foo")?.query_one([], |r| r.get(0))?;
379 assert_eq!(found_id.len(), 36);
380 Ok(())
381 }
382
383 #[cfg(feature = "uuid")]
384 #[test]
385 fn test_uuid_blob_type() -> crate::Result<()> {
386 use crate::{Connection, params};
387 use uuid::Uuid;
388
389 let db = Connection::open_in_memory()?;
390 db.execute_batch("CREATE TABLE foo (id BLOB CONSTRAINT uuidchk CHECK (octet_length(id) = 16), label TEXT);")?;
391
392 let id = Uuid::new_v4();
393 let id_vec = id.as_bytes().to_vec();
394 db.execute("INSERT INTO foo (id, label) VALUES (?, ?)", params![id_vec, "target"])?;
395
396 let (found_id, found_label): (Uuid, String) = db
397 .prepare("SELECT id, label FROM foo WHERE id = ?")?
398 .query_one(params![id_vec], |r| Ok((r.get_unwrap(0), r.get_unwrap(1))))?;
399 assert_eq!(found_id, id);
400 assert_eq!(found_label, "target");
401 Ok(())
402 }
403
404 #[cfg(feature = "uuid")]
405 #[test]
406 fn test_uuid_type() -> crate::Result<()> {
407 use crate::{Connection, params};
408 use uuid::Uuid;
409
410 let db = Connection::open_in_memory()?;
411 db.execute_batch("CREATE TABLE foo (id uuid, label TEXT);")?;
412
413 let id = Uuid::new_v4();
414 db.execute("INSERT INTO foo (id, label) VALUES (?, ?)", params![id, "target"])?;
415
416 let (found_id, found_label): (Uuid, String) = db
417 .prepare("SELECT id, label FROM foo WHERE id = ?")?
418 .query_one(params![id], |r| Ok((r.get_unwrap(0), r.get_unwrap(1))))?;
419 assert_eq!(found_id, id);
420 assert_eq!(found_label, "target");
421 Ok(())
422 }
423}