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