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