Skip to main content

drizzle_core/traits/
to_sql.rs

1//! [`ToSQL`]: rendering values and schema items as SQL fragments.
2
3use crate::prelude::*;
4use crate::{
5    sql::{ColumnRef, SQL, TableRef, Token},
6    traits::SQLParam,
7};
8
9#[cfg(feature = "uuid")]
10use uuid::Uuid;
11
12/// Something that renders as a [`SQL`] fragment for the value type `V`.
13///
14/// Columns, tables, expressions, query builders and plain Rust values all
15/// implement it. Rust values (`i64`, `&str`, `Option<T>`, ...) render as
16/// bound parameters, `None` renders as `NULL`, and lists (`Vec<T>`, arrays,
17/// slices) render as comma-separated items. `'a` lets the fragment borrow
18/// its inputs instead of copying them.
19///
20/// # Examples
21///
22/// Rendering a custom type:
23///
24/// ```
25/// use drizzle_core::{SQL, ToSQL};
26/// # use drizzle_core::{Dialect, SQLParam, SQLiteDialect};
27/// # use std::borrow::Cow;
28/// # #[derive(Debug, Clone, PartialEq)]
29/// # struct Value(i64);
30/// # impl SQLParam for Value {
31/// #     const DIALECT: Dialect = Dialect::SQLite;
32/// #     type DialectMarker = SQLiteDialect;
33/// # }
34/// # impl From<Value> for Cow<'_, Value> {
35/// #     fn from(value: Value) -> Self { Cow::Owned(value) }
36/// # }
37///
38/// struct Now;
39///
40/// impl<'a> ToSQL<'a, Value> for Now {
41///     fn to_sql(&self) -> SQL<'a, Value> {
42///         SQL::raw("CURRENT_TIMESTAMP")
43///     }
44/// }
45///
46/// assert_eq!(Now.to_sql().sql(), "CURRENT_TIMESTAMP");
47/// ```
48#[diagnostic::on_unimplemented(
49    message = "`{Self}` cannot be converted to SQL",
50    label = "this type does not implement ToSQL for the current dialect",
51    note = "tuples larger than the enabled arity need a larger `colN` feature (col16, col32, col64, col128, col200) on drizzle-core"
52)]
53pub trait ToSQL<'a, V: SQLParam> {
54    /// Renders `self` as a SQL fragment.
55    fn to_sql(&self) -> SQL<'a, V>;
56
57    /// Renders `self` as a SQL fragment, consuming it.
58    ///
59    /// The default calls [`to_sql`](Self::to_sql). Types that already hold a
60    /// fragment (such as `SQL` and `SQLExpr`) override it to avoid a clone.
61    fn into_sql(self) -> SQL<'a, V>
62    where
63        Self: Sized,
64    {
65        self.to_sql()
66    }
67}
68
69/// Bytes bound as one binary parameter (BLOB / `bytea`).
70///
71/// A `Vec<u8>` passed to [`ToSQL`] renders as a list, one parameter per
72/// byte. Wrap it in `SQLBytes` (or use [`SQL::bytes`]) to bind it as a
73/// single value.
74///
75/// # Examples
76///
77/// ```
78/// use drizzle_core::{SQL, SQLBytes, ToSQL};
79/// # use drizzle_core::{Dialect, SQLParam, SQLiteDialect};
80/// # use std::borrow::Cow;
81/// # #[derive(Debug, Clone, PartialEq)]
82/// # struct Value(Vec<u8>);
83/// # impl SQLParam for Value {
84/// #     const DIALECT: Dialect = Dialect::SQLite;
85/// #     type DialectMarker = SQLiteDialect;
86/// # }
87/// # impl From<Value> for Cow<'_, Value> {
88/// #     fn from(value: Value) -> Self { Cow::Owned(value) }
89/// # }
90/// # impl From<&[u8]> for Value {
91/// #     fn from(bytes: &[u8]) -> Self { Value(bytes.to_vec()) }
92/// # }
93/// # impl From<Vec<u8>> for Value {
94/// #     fn from(bytes: Vec<u8>) -> Self { Value(bytes) }
95/// # }
96///
97/// let data = vec![1_u8, 2, 3];
98/// let sql: SQL<'_, Value> = SQLBytes::new(&data[..]).to_sql();
99/// assert_eq!(sql.sql(), "?");
100/// assert_eq!(sql.params().collect::<Vec<_>>(), [&Value(vec![1, 2, 3])]);
101/// ```
102#[derive(Debug, Clone)]
103pub struct SQLBytes<'a>(pub Cow<'a, [u8]>);
104
105/// Renders as the SQL literal `NULL`.
106#[derive(Debug, Clone, Copy, Default)]
107pub struct SQLNull;
108
109impl<'a> SQLBytes<'a> {
110    /// Wraps borrowed or owned bytes.
111    #[inline]
112    pub fn new(bytes: impl Into<Cow<'a, [u8]>>) -> Self {
113        Self(bytes.into())
114    }
115}
116
117impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for SQLNull {
118    fn to_sql(&self) -> SQL<'a, V> {
119        SQL::raw("NULL")
120    }
121}
122
123impl<'a, T, V> From<&T> for SQL<'a, V>
124where
125    T: ToSQL<'a, V>,
126    V: SQLParam,
127{
128    fn from(value: &T) -> Self {
129        value.to_sql()
130    }
131}
132
133impl<'a, V: SQLParam, T> ToSQL<'a, V> for &T
134where
135    T: ToSQL<'a, V>,
136{
137    fn to_sql(&self) -> SQL<'a, V> {
138        (**self).to_sql()
139    }
140}
141
142impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for () {
143    fn to_sql(&self) -> SQL<'a, V> {
144        SQL::empty()
145    }
146}
147
148impl<'a, V, T> ToSQL<'a, V> for Vec<T>
149where
150    V: SQLParam + 'a,
151    T: ToSQL<'a, V>,
152{
153    fn to_sql(&self) -> SQL<'a, V> {
154        SQL::join(self.iter().map(ToSQL::to_sql), Token::COMMA)
155    }
156}
157
158impl<'a, V, T> ToSQL<'a, V> for &'a [T]
159where
160    V: SQLParam + 'a,
161    T: ToSQL<'a, V>,
162{
163    fn to_sql(&self) -> SQL<'a, V> {
164        SQL::join(self.iter().map(ToSQL::to_sql), Token::COMMA)
165    }
166}
167
168impl<'a, V, T, const N: usize> ToSQL<'a, V> for [T; N]
169where
170    V: SQLParam + 'a,
171    T: ToSQL<'a, V>,
172{
173    fn to_sql(&self) -> SQL<'a, V> {
174        SQL::join(self.iter().map(ToSQL::to_sql), Token::COMMA)
175    }
176}
177
178impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for TableRef {
179    fn to_sql(&self) -> SQL<'a, V> {
180        SQL::table(*self)
181    }
182}
183
184impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for ColumnRef {
185    fn to_sql(&self) -> SQL<'a, V> {
186        SQL::column(*self)
187    }
188}
189
190// Implement ToSQL for primitive types
191impl<'a, V> ToSQL<'a, V> for &'a str
192where
193    V: SQLParam + 'a + From<&'a str> + Into<Cow<'a, V>>,
194{
195    fn to_sql(&self) -> SQL<'a, V> {
196        SQL::param(V::from(self))
197    }
198}
199
200impl<'a, V> ToSQL<'a, V> for Box<str>
201where
202    V: SQLParam + 'a + From<String> + Into<Cow<'a, V>>,
203{
204    fn to_sql(&self) -> SQL<'a, V> {
205        SQL::param(V::from(self.to_string()))
206    }
207
208    fn into_sql(self) -> SQL<'a, V> {
209        SQL::param(V::from(self.into_string()))
210    }
211}
212
213#[cfg(any(feature = "std", feature = "alloc"))]
214impl<'a, V> ToSQL<'a, V> for Rc<str>
215where
216    V: SQLParam + 'a + From<String> + Into<Cow<'a, V>>,
217{
218    fn to_sql(&self) -> SQL<'a, V> {
219        SQL::param(V::from(self.as_ref().to_string()))
220    }
221}
222
223#[cfg(any(feature = "std", feature = "alloc"))]
224impl<'a, V> ToSQL<'a, V> for Arc<str>
225where
226    V: SQLParam + 'a + From<String> + Into<Cow<'a, V>>,
227{
228    fn to_sql(&self) -> SQL<'a, V> {
229        SQL::param(V::from(self.as_ref().to_string()))
230    }
231}
232
233impl<'a, V, T> ToSQL<'a, V> for Box<T>
234where
235    V: SQLParam + 'a,
236    T: ToSQL<'a, V>,
237{
238    fn to_sql(&self) -> SQL<'a, V> {
239        (**self).to_sql()
240    }
241}
242
243#[cfg(any(feature = "std", feature = "alloc"))]
244impl<'a, V, T> ToSQL<'a, V> for Rc<T>
245where
246    V: SQLParam + 'a,
247    T: ToSQL<'a, V>,
248{
249    fn to_sql(&self) -> SQL<'a, V> {
250        (**self).to_sql()
251    }
252}
253
254#[cfg(any(feature = "std", feature = "alloc"))]
255impl<'a, V, T> ToSQL<'a, V> for Arc<T>
256where
257    V: SQLParam + 'a,
258    T: ToSQL<'a, V>,
259{
260    fn to_sql(&self) -> SQL<'a, V> {
261        (**self).to_sql()
262    }
263}
264
265impl<'a, V> ToSQL<'a, V> for String
266where
267    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
268{
269    fn to_sql(&self) -> SQL<'a, V> {
270        SQL::param(V::from(self.clone()))
271    }
272
273    fn into_sql(self) -> SQL<'a, V> {
274        SQL::param(V::from(self))
275    }
276}
277
278#[cfg(feature = "compact-str")]
279impl<'a, V> ToSQL<'a, V> for compact_str::CompactString
280where
281    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
282{
283    fn to_sql(&self) -> SQL<'a, V> {
284        SQL::param(V::from(self.clone()))
285    }
286
287    fn into_sql(self) -> SQL<'a, V> {
288        SQL::param(V::from(self))
289    }
290}
291
292#[cfg(feature = "bytes")]
293impl<'a, V> ToSQL<'a, V> for bytes::Bytes
294where
295    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
296{
297    fn to_sql(&self) -> SQL<'a, V> {
298        SQL::param(V::from(self.clone()))
299    }
300
301    fn into_sql(self) -> SQL<'a, V> {
302        SQL::param(V::from(self))
303    }
304}
305
306#[cfg(feature = "bytes")]
307impl<'a, V> ToSQL<'a, V> for bytes::BytesMut
308where
309    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
310{
311    fn to_sql(&self) -> SQL<'a, V> {
312        SQL::param(V::from(self.clone()))
313    }
314
315    fn into_sql(self) -> SQL<'a, V> {
316        SQL::param(V::from(self))
317    }
318}
319
320#[cfg(feature = "arrayvec")]
321impl<'a, V, const N: usize> ToSQL<'a, V> for arrayvec::ArrayString<N>
322where
323    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
324{
325    fn to_sql(&self) -> SQL<'a, V> {
326        SQL::param(V::from(*self))
327    }
328
329    fn into_sql(self) -> SQL<'a, V> {
330        SQL::param(V::from(self))
331    }
332}
333
334#[cfg(feature = "arrayvec")]
335impl<'a, V, const N: usize> ToSQL<'a, V> for arrayvec::ArrayVec<u8, N>
336where
337    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
338{
339    fn to_sql(&self) -> SQL<'a, V> {
340        SQL::param(V::from(self.clone()))
341    }
342
343    fn into_sql(self) -> SQL<'a, V> {
344        SQL::param(V::from(self))
345    }
346}
347
348#[cfg(feature = "smallvec-types")]
349impl<'a, V, const N: usize> ToSQL<'a, V> for smallvec::SmallVec<[u8; N]>
350where
351    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
352{
353    fn to_sql(&self) -> SQL<'a, V> {
354        SQL::param(V::from(self.clone()))
355    }
356
357    fn into_sql(self) -> SQL<'a, V> {
358        SQL::param(V::from(self))
359    }
360}
361
362impl<'a, V> ToSQL<'a, V> for Cow<'a, str>
363where
364    V: SQLParam + 'a + From<&'a str> + From<String> + Into<Cow<'a, V>>,
365{
366    fn to_sql(&self) -> SQL<'a, V> {
367        match self {
368            Cow::Borrowed(value) => SQL::param(V::from(*value)),
369            Cow::Owned(value) => SQL::param(V::from(value.clone())),
370        }
371    }
372
373    fn into_sql(self) -> SQL<'a, V> {
374        match self {
375            Cow::Borrowed(value) => SQL::param(V::from(value)),
376            Cow::Owned(value) => SQL::param(V::from(value)),
377        }
378    }
379}
380
381impl<'a, V> ToSQL<'a, V> for Cow<'a, [u8]>
382where
383    V: SQLParam + 'a + From<&'a [u8]> + From<Vec<u8>> + Into<Cow<'a, V>>,
384{
385    fn to_sql(&self) -> SQL<'a, V> {
386        match self {
387            Cow::Borrowed(value) => SQL::param(V::from(*value)),
388            Cow::Owned(value) => SQL::param(V::from(value.clone())),
389        }
390    }
391
392    fn into_sql(self) -> SQL<'a, V> {
393        match self {
394            Cow::Borrowed(value) => SQL::param(V::from(value)),
395            Cow::Owned(value) => SQL::param(V::from(value)),
396        }
397    }
398}
399
400impl<'a, V> ToSQL<'a, V> for SQLBytes<'a>
401where
402    V: SQLParam + 'a + From<&'a [u8]> + From<Vec<u8>> + Into<Cow<'a, V>>,
403{
404    fn to_sql(&self) -> SQL<'a, V> {
405        match &self.0 {
406            Cow::Borrowed(value) => SQL::param(V::from(*value)),
407            Cow::Owned(value) => SQL::param(V::from(value.clone())),
408        }
409    }
410
411    fn into_sql(self) -> SQL<'a, V> {
412        match self.0 {
413            Cow::Borrowed(value) => SQL::param(V::from(value)),
414            Cow::Owned(value) => SQL::param(V::from(value)),
415        }
416    }
417}
418
419macro_rules! impl_tosql_param_copy {
420    ($($ty:ty),+ $(,)?) => {
421        $(
422            impl<'a, V> ToSQL<'a, V> for $ty
423            where
424                V: SQLParam + 'a + From<$ty>,
425                V: Into<Cow<'a, V>>,
426            {
427                fn to_sql(&self) -> SQL<'a, V> {
428                    SQL::param(V::from(*self))
429                }
430            }
431        )+
432    };
433}
434
435impl_tosql_param_copy!(
436    i8, i16, i32, i64, f32, f64, bool, char, u8, u16, u32, u64, isize, usize
437);
438
439impl<'a, V, T> ToSQL<'a, V> for Option<T>
440where
441    V: SQLParam + 'a,
442    T: ToSQL<'a, V>,
443{
444    fn to_sql(&self) -> SQL<'a, V> {
445        self.as_ref()
446            .map_or_else(|| SQLNull.to_sql(), ToSQL::to_sql)
447    }
448}
449
450#[cfg(feature = "uuid")]
451impl<'a, V> ToSQL<'a, V> for Uuid
452where
453    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
454{
455    fn to_sql(&self) -> SQL<'a, V> {
456        SQL::param(V::from(*self))
457    }
458}
459
460// Date and time values bind as parameters in every dialect that stores them.
461#[cfg(feature = "chrono")]
462impl_tosql_param_copy!(
463    chrono::NaiveDate,
464    chrono::NaiveTime,
465    chrono::NaiveDateTime,
466    chrono::DateTime<chrono::Utc>,
467    chrono::DateTime<chrono::FixedOffset>,
468    chrono::Duration,
469);
470
471#[cfg(feature = "time")]
472impl_tosql_param_copy!(
473    time::Date,
474    time::Time,
475    time::PrimitiveDateTime,
476    time::OffsetDateTime,
477    time::Duration,
478);
479
480#[cfg(feature = "jiff")]
481impl_tosql_param_copy!(
482    jiff::civil::Date,
483    jiff::civil::Time,
484    jiff::civil::DateTime,
485    jiff::Timestamp,
486);
487
488#[cfg(feature = "rust-decimal")]
489impl<'a, V> ToSQL<'a, V> for rust_decimal::Decimal
490where
491    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
492{
493    fn to_sql(&self) -> SQL<'a, V> {
494        SQL::param(V::from(*self))
495    }
496}