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 = "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.
17pub trait ToSQL<'a, V: SQLParam> {
18    fn to_sql(&self) -> SQL<'a, V>;
19    fn alias(&self, alias: &'static str) -> SQL<'a, V> {
20        self.to_sql().alias(alias)
21    }
22}
23
24/// Wrapper for byte slices to avoid list semantics (`Vec<u8>` normally becomes a list).
25///
26/// Use this when you want a single BLOB/bytea parameter:
27/// ```ignore
28/// use drizzle_core::{SQLBytes, SQL};
29///
30/// let data = vec![1u8, 2, 3];
31/// let sql = SQL::bytes(&data); // or SQL::param(SQLBytes::new(&data))
32/// ```
33#[derive(Debug, Clone)]
34pub struct SQLBytes<'a>(pub Cow<'a, [u8]>);
35
36impl<'a> SQLBytes<'a> {
37    #[inline]
38    pub fn new(bytes: impl Into<Cow<'a, [u8]>>) -> Self {
39        Self(bytes.into())
40    }
41}
42
43impl<'a, T, V> From<&T> for SQL<'a, V>
44where
45    T: ToSQL<'a, V>,
46    V: SQLParam,
47{
48    fn from(value: &T) -> Self {
49        value.to_sql()
50    }
51}
52
53impl<'a, V: SQLParam, T> ToSQL<'a, V> for &T
54where
55    T: ToSQL<'a, V>,
56{
57    fn to_sql(&self) -> SQL<'a, V> {
58        (**self).to_sql()
59    }
60}
61
62impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for () {
63    fn to_sql(&self) -> SQL<'a, V> {
64        SQL::empty()
65    }
66}
67
68impl<'a, V, T> ToSQL<'a, V> for Vec<T>
69where
70    V: SQLParam + 'a,
71    T: ToSQL<'a, V>,
72{
73    fn to_sql(&self) -> SQL<'a, V> {
74        SQL::join(self.iter().map(ToSQL::to_sql), Token::COMMA)
75    }
76}
77
78impl<'a, V, T> ToSQL<'a, V> for &'a [T]
79where
80    V: SQLParam + 'a,
81    T: ToSQL<'a, V>,
82{
83    fn to_sql(&self) -> SQL<'a, V> {
84        SQL::join(self.iter().map(ToSQL::to_sql), Token::COMMA)
85    }
86}
87
88impl<'a, V, T, const N: usize> ToSQL<'a, V> for [T; N]
89where
90    V: SQLParam + 'a,
91    T: ToSQL<'a, V>,
92{
93    fn to_sql(&self) -> SQL<'a, V> {
94        SQL::join(self.iter().map(ToSQL::to_sql), Token::COMMA)
95    }
96}
97
98// Implement ToSQL for SQLTableInfo and SQLColumnInfo trait objects
99impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for &'static dyn SQLTableInfo {
100    fn to_sql(&self) -> SQL<'a, V> {
101        SQL::table(*self)
102    }
103}
104
105impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for &'static dyn SQLColumnInfo {
106    fn to_sql(&self) -> SQL<'a, V> {
107        SQL::column(*self)
108    }
109}
110
111impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for Box<[&'static dyn SQLColumnInfo]> {
112    fn to_sql(&self) -> SQL<'a, V> {
113        SQL::join(self.iter().map(|&v| SQL::column(v)), Token::COMMA)
114    }
115}
116
117impl<'a, V: SQLParam + 'a> ToSQL<'a, V> for Box<[&'static dyn SQLTableInfo]> {
118    fn to_sql(&self) -> SQL<'a, V> {
119        SQL::join(self.iter().map(|&v| SQL::table(v)), Token::COMMA)
120    }
121}
122
123// Implement ToSQL for primitive types
124impl<'a, V> ToSQL<'a, V> for &'a str
125where
126    V: SQLParam + 'a,
127    V: From<&'a str>,
128    V: Into<Cow<'a, V>>,
129{
130    fn to_sql(&self) -> SQL<'a, V> {
131        SQL::param(V::from(self))
132    }
133}
134
135impl<'a, V> ToSQL<'a, V> for String
136where
137    V: SQLParam + 'a,
138    V: From<String>,
139    V: Into<Cow<'a, V>>,
140{
141    fn to_sql(&self) -> SQL<'a, V> {
142        SQL::param(V::from(self.clone()))
143    }
144}
145
146impl<'a, V> ToSQL<'a, V> for Cow<'a, str>
147where
148    V: SQLParam + 'a,
149    V: From<&'a str>,
150    V: From<String>,
151    V: Into<Cow<'a, V>>,
152{
153    fn to_sql(&self) -> SQL<'a, V> {
154        match self {
155            Cow::Borrowed(value) => SQL::param(V::from(*value)),
156            Cow::Owned(value) => SQL::param(V::from(value.clone())),
157        }
158    }
159}
160
161impl<'a, V> ToSQL<'a, V> for Cow<'a, [u8]>
162where
163    V: SQLParam + 'a,
164    V: From<&'a [u8]>,
165    V: From<Vec<u8>>,
166    V: Into<Cow<'a, V>>,
167{
168    fn to_sql(&self) -> SQL<'a, V> {
169        match self {
170            Cow::Borrowed(value) => SQL::param(V::from(*value)),
171            Cow::Owned(value) => SQL::param(V::from(value.clone())),
172        }
173    }
174}
175
176impl<'a, V> ToSQL<'a, V> for SQLBytes<'a>
177where
178    V: SQLParam + 'a,
179    V: From<&'a [u8]>,
180    V: From<Vec<u8>>,
181    V: Into<Cow<'a, V>>,
182{
183    fn to_sql(&self) -> SQL<'a, V> {
184        match &self.0 {
185            Cow::Borrowed(value) => SQL::param(V::from(*value)),
186            Cow::Owned(value) => SQL::param(V::from(value.clone())),
187        }
188    }
189}
190
191macro_rules! impl_tosql_param_copy {
192    ($($ty:ty),+ $(,)?) => {
193        $(
194            impl<'a, V> ToSQL<'a, V> for $ty
195            where
196                V: SQLParam + 'a + From<$ty>,
197                V: Into<Cow<'a, V>>,
198            {
199                fn to_sql(&self) -> SQL<'a, V> {
200                    SQL::param(V::from(*self))
201                }
202            }
203        )+
204    };
205}
206
207impl_tosql_param_copy!(
208    i8, i16, i32, i64, f32, f64, bool, u8, u16, u32, u64, isize, usize
209);
210
211impl<'a, V, T> ToSQL<'a, V> for Option<T>
212where
213    V: SQLParam + 'a,
214    T: ToSQL<'a, V>,
215{
216    fn to_sql(&self) -> SQL<'a, V> {
217        match self {
218            Some(value) => value.to_sql(), // Let the inner type handle parameterization
219            None => SQL::raw("NULL"),      // NULL is a keyword, use raw
220        }
221    }
222}
223
224#[cfg(feature = "uuid")]
225impl<'a, V> ToSQL<'a, V> for Uuid
226where
227    V: SQLParam + 'a,
228    V: From<Uuid>,
229    V: Into<Cow<'a, V>>,
230{
231    fn to_sql(&self) -> SQL<'a, V> {
232        SQL::param(V::from(*self))
233    }
234}