Skip to main content

drizzle_core/traits/
to_sql.rs

1//! `ToSQL` trait for converting types to 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/// Trait for types that can be converted to SQL fragments.
13///
14/// The `'a` lifetime ties any borrowed parameter values to the resulting SQL
15/// fragment, allowing zero-copy SQL construction when inputs are already
16/// borrowed.
17#[diagnostic::on_unimplemented(
18    message = "`{Self}` cannot be converted to SQL",
19    label = "this type does not implement ToSQL for the current dialect"
20)]
21pub trait ToSQL<'a, V: SQLParam> {
22    fn to_sql(&self) -> SQL<'a, V>;
23
24    /// Consume self and return SQL without cloning.
25    /// Default delegates to `to_sql()` (which clones). Types that own their SQL
26    /// (like `SQL` and `SQLExpr`) override this to avoid the clone.
27    fn into_sql(self) -> SQL<'a, V>
28    where
29        Self: Sized,
30    {
31        self.to_sql()
32    }
33}
34
35/// Wrapper for byte slices to avoid list semantics (`Vec<u8>` normally becomes a list).
36///
37/// Use this when you want a single BLOB/bytea parameter:
38/// ```rust
39/// # let _ = r####"
40/// use drizzle_core::{SQLBytes, SQL};
41///
42/// let data = vec![1u8, 2, 3];
43/// let sql = SQL::bytes(&data); // or SQL::param(SQLBytes::new(&data))
44/// # "####;
45/// ```
46#[derive(Debug, Clone)]
47pub struct SQLBytes<'a>(pub Cow<'a, [u8]>);
48
49/// Explicit SQL NULL marker.
50#[derive(Debug, Clone, Copy, Default)]
51pub struct SQLNull;
52
53impl<'a> SQLBytes<'a> {
54    #[inline]
55    pub fn new(bytes: impl Into<Cow<'a, [u8]>>) -> Self {
56        Self(bytes.into())
57    }
58}
59
60impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for SQLNull {
61    fn to_sql(&self) -> SQL<'a, V> {
62        SQL::raw("NULL")
63    }
64}
65
66impl<'a, T, V> From<&T> for SQL<'a, V>
67where
68    T: ToSQL<'a, V>,
69    V: SQLParam,
70{
71    fn from(value: &T) -> Self {
72        value.to_sql()
73    }
74}
75
76impl<'a, V: SQLParam, T> ToSQL<'a, V> for &T
77where
78    T: ToSQL<'a, V>,
79{
80    fn to_sql(&self) -> SQL<'a, V> {
81        (**self).to_sql()
82    }
83}
84
85impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for () {
86    fn to_sql(&self) -> SQL<'a, V> {
87        SQL::empty()
88    }
89}
90
91impl<'a, V, T> ToSQL<'a, V> for Vec<T>
92where
93    V: SQLParam + 'a,
94    T: ToSQL<'a, V>,
95{
96    fn to_sql(&self) -> SQL<'a, V> {
97        SQL::join(self.iter().map(ToSQL::to_sql), Token::COMMA)
98    }
99}
100
101impl<'a, V, T> ToSQL<'a, V> for &'a [T]
102where
103    V: SQLParam + 'a,
104    T: ToSQL<'a, V>,
105{
106    fn to_sql(&self) -> SQL<'a, V> {
107        SQL::join(self.iter().map(ToSQL::to_sql), Token::COMMA)
108    }
109}
110
111impl<'a, V, T, const N: usize> ToSQL<'a, V> for [T; N]
112where
113    V: SQLParam + 'a,
114    T: ToSQL<'a, V>,
115{
116    fn to_sql(&self) -> SQL<'a, V> {
117        SQL::join(self.iter().map(ToSQL::to_sql), Token::COMMA)
118    }
119}
120
121impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for TableRef {
122    fn to_sql(&self) -> SQL<'a, V> {
123        SQL::table(*self)
124    }
125}
126
127impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for ColumnRef {
128    fn to_sql(&self) -> SQL<'a, V> {
129        SQL::column(*self)
130    }
131}
132
133// Implement ToSQL for primitive types
134impl<'a, V> ToSQL<'a, V> for &'a str
135where
136    V: SQLParam + 'a + From<&'a str> + Into<Cow<'a, V>>,
137{
138    fn to_sql(&self) -> SQL<'a, V> {
139        SQL::param(V::from(self))
140    }
141}
142
143impl<'a, V> ToSQL<'a, V> for Box<str>
144where
145    V: SQLParam + 'a + From<String> + Into<Cow<'a, V>>,
146{
147    fn to_sql(&self) -> SQL<'a, V> {
148        SQL::param(V::from(self.to_string()))
149    }
150
151    fn into_sql(self) -> SQL<'a, V> {
152        SQL::param(V::from(self.into_string()))
153    }
154}
155
156#[cfg(any(feature = "std", feature = "alloc"))]
157impl<'a, V> ToSQL<'a, V> for Rc<str>
158where
159    V: SQLParam + 'a + From<String> + Into<Cow<'a, V>>,
160{
161    fn to_sql(&self) -> SQL<'a, V> {
162        SQL::param(V::from(self.as_ref().to_string()))
163    }
164}
165
166#[cfg(any(feature = "std", feature = "alloc"))]
167impl<'a, V> ToSQL<'a, V> for Arc<str>
168where
169    V: SQLParam + 'a + From<String> + Into<Cow<'a, V>>,
170{
171    fn to_sql(&self) -> SQL<'a, V> {
172        SQL::param(V::from(self.as_ref().to_string()))
173    }
174}
175
176impl<'a, V, T> ToSQL<'a, V> for Box<T>
177where
178    V: SQLParam + 'a,
179    T: ToSQL<'a, V>,
180{
181    fn to_sql(&self) -> SQL<'a, V> {
182        (**self).to_sql()
183    }
184}
185
186#[cfg(any(feature = "std", feature = "alloc"))]
187impl<'a, V, T> ToSQL<'a, V> for Rc<T>
188where
189    V: SQLParam + 'a,
190    T: ToSQL<'a, V>,
191{
192    fn to_sql(&self) -> SQL<'a, V> {
193        (**self).to_sql()
194    }
195}
196
197#[cfg(any(feature = "std", feature = "alloc"))]
198impl<'a, V, T> ToSQL<'a, V> for Arc<T>
199where
200    V: SQLParam + 'a,
201    T: ToSQL<'a, V>,
202{
203    fn to_sql(&self) -> SQL<'a, V> {
204        (**self).to_sql()
205    }
206}
207
208impl<'a, V> ToSQL<'a, V> for String
209where
210    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
211{
212    fn to_sql(&self) -> SQL<'a, V> {
213        SQL::param(V::from(self.clone()))
214    }
215
216    fn into_sql(self) -> SQL<'a, V> {
217        SQL::param(V::from(self))
218    }
219}
220
221impl<'a, V> ToSQL<'a, V> for Cow<'a, str>
222where
223    V: SQLParam + 'a + From<&'a str> + From<String> + Into<Cow<'a, V>>,
224{
225    fn to_sql(&self) -> SQL<'a, V> {
226        match self {
227            Cow::Borrowed(value) => SQL::param(V::from(*value)),
228            Cow::Owned(value) => SQL::param(V::from(value.clone())),
229        }
230    }
231
232    fn into_sql(self) -> SQL<'a, V> {
233        match self {
234            Cow::Borrowed(value) => SQL::param(V::from(value)),
235            Cow::Owned(value) => SQL::param(V::from(value)),
236        }
237    }
238}
239
240impl<'a, V> ToSQL<'a, V> for Cow<'a, [u8]>
241where
242    V: SQLParam + 'a + From<&'a [u8]> + From<Vec<u8>> + Into<Cow<'a, V>>,
243{
244    fn to_sql(&self) -> SQL<'a, V> {
245        match self {
246            Cow::Borrowed(value) => SQL::param(V::from(*value)),
247            Cow::Owned(value) => SQL::param(V::from(value.clone())),
248        }
249    }
250
251    fn into_sql(self) -> SQL<'a, V> {
252        match self {
253            Cow::Borrowed(value) => SQL::param(V::from(value)),
254            Cow::Owned(value) => SQL::param(V::from(value)),
255        }
256    }
257}
258
259impl<'a, V> ToSQL<'a, V> for SQLBytes<'a>
260where
261    V: SQLParam + 'a + From<&'a [u8]> + From<Vec<u8>> + Into<Cow<'a, V>>,
262{
263    fn to_sql(&self) -> SQL<'a, V> {
264        match &self.0 {
265            Cow::Borrowed(value) => SQL::param(V::from(*value)),
266            Cow::Owned(value) => SQL::param(V::from(value.clone())),
267        }
268    }
269
270    fn into_sql(self) -> SQL<'a, V> {
271        match self.0 {
272            Cow::Borrowed(value) => SQL::param(V::from(value)),
273            Cow::Owned(value) => SQL::param(V::from(value)),
274        }
275    }
276}
277
278macro_rules! impl_tosql_param_copy {
279    ($($ty:ty),+ $(,)?) => {
280        $(
281            impl<'a, V> ToSQL<'a, V> for $ty
282            where
283                V: SQLParam + 'a + From<$ty>,
284                V: Into<Cow<'a, V>>,
285            {
286                fn to_sql(&self) -> SQL<'a, V> {
287                    SQL::param(V::from(*self))
288                }
289            }
290        )+
291    };
292}
293
294impl_tosql_param_copy!(
295    i8, i16, i32, i64, f32, f64, bool, u8, u16, u32, u64, isize, usize
296);
297
298impl<'a, V, T> ToSQL<'a, V> for Option<T>
299where
300    V: SQLParam + 'a,
301    T: ToSQL<'a, V>,
302{
303    fn to_sql(&self) -> SQL<'a, V> {
304        self.as_ref()
305            .map_or_else(|| SQLNull.to_sql(), ToSQL::to_sql)
306    }
307}
308
309#[cfg(feature = "uuid")]
310impl<'a, V> ToSQL<'a, V> for Uuid
311where
312    V: SQLParam + 'a + From<Self> + Into<Cow<'a, V>>,
313{
314    fn to_sql(&self) -> SQL<'a, V> {
315        SQL::param(V::from(*self))
316    }
317}