Skip to main content

drizzle_core/
bind.rs

1use crate::dialect::{MySQLDialect, PostgresDialect, SQLiteDialect};
2use crate::traits::SQLParam;
3use crate::types::{Assignable, DataType};
4
5#[cfg(any(feature = "alloc", feature = "std"))]
6use crate::prelude::{Arc, Box, Cow, Rc, String, Vec};
7
8/// The SQL type a Rust value has when bound in dialect `D`.
9///
10/// For example `i64` is `Integer` on SQLite and `Int8` on PostgreSQL.
11pub trait ValueTypeForDialect<D> {
12    /// The SQL type marker.
13    type SQLType: DataType;
14}
15
16/// A Rust value that can be bound where SQL type `Expected` is wanted.
17///
18/// Implemented for every `T` that converts into `V` and whose
19/// [`ValueTypeForDialect`] type can be stored in `Expected`. Used by
20/// [`TypedPlaceholder::bind`](crate::TypedPlaceholder::bind).
21#[diagnostic::on_unimplemented(
22    message = "`{Self}` cannot bind to a `{Expected}` placeholder",
23    label = "bind a value of the placeholder column's type",
24    note = "`None` binds only to the placeholder of a nullable column"
25)]
26pub trait BindValue<'a, V: SQLParam, Expected: DataType>: Sized {
27    /// Converts the value into the dialect's value type.
28    fn into_bind_value(self) -> V;
29}
30
31/// An `Option<T>` that can be bound where a nullable `Expected` is wanted.
32///
33/// Used by [`TypedPlaceholder::bind_opt`](crate::TypedPlaceholder::bind_opt).
34pub trait NullableBindValue<'a, V: SQLParam, Expected: DataType>: Sized {
35    /// Converts the value into the dialect's value type (`None` becomes
36    /// NULL).
37    fn into_nullable_bind_value(self) -> V;
38}
39
40impl<V, Expected, T> BindValue<'_, V, Expected> for T
41where
42    V: SQLParam + From<T>,
43    Expected: DataType + Assignable<<T as ValueTypeForDialect<V::DialectMarker>>::SQLType>,
44    T: ValueTypeForDialect<V::DialectMarker>,
45{
46    fn into_bind_value(self) -> V {
47        V::from(self)
48    }
49}
50
51impl<V, Expected, T> NullableBindValue<'_, V, Expected> for Option<T>
52where
53    V: SQLParam + From<Self>,
54    Expected: DataType + Assignable<<T as ValueTypeForDialect<V::DialectMarker>>::SQLType>,
55    T: ValueTypeForDialect<V::DialectMarker>,
56{
57    fn into_nullable_bind_value(self) -> V {
58        V::from(self)
59    }
60}
61
62// =============================================================================
63// ValueTypeForDialect impl generator
64// =============================================================================
65
66/// Declare `ValueTypeForDialect<$dialect>::SQLType = $sql` for one or more types.
67macro_rules! impl_value_type {
68    ($dialect:ty, $sql:ty => $($ty:ty),+ $(,)?) => {
69        $(
70            impl ValueTypeForDialect<$dialect> for $ty {
71                type SQLType = $sql;
72            }
73        )+
74    };
75}
76
77/// Same as `impl_value_type!`, but for types generic over `const N: usize`.
78///
79/// Used by optional containers and by fixed-size arrays supported by MySQL.
80macro_rules! impl_value_type_const_n {
81    ($dialect:ty, $sql:ty => $($ty:ty),+ $(,)?) => {
82        $(
83            impl<const N: usize> ValueTypeForDialect<$dialect> for $ty {
84                type SQLType = $sql;
85            }
86        )+
87    };
88}
89
90// =============================================================================
91// SQLite mappings
92// =============================================================================
93
94use drizzle_types::sqlite::types as sqlite_ty;
95
96impl_value_type!(SQLiteDialect, sqlite_ty::Integer =>
97    i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, bool);
98
99impl_value_type!(SQLiteDialect, sqlite_ty::Real => f32, f64);
100
101impl_value_type!(SQLiteDialect, sqlite_ty::Text => &str);
102
103#[cfg(any(feature = "alloc", feature = "std"))]
104impl_value_type!(SQLiteDialect, sqlite_ty::Text =>
105    Cow<'_, str>,
106    String,
107    Box<String>,
108    Rc<String>,
109    Arc<String>,
110    Box<str>,
111    Rc<str>,
112    Arc<str>,
113);
114
115impl_value_type!(SQLiteDialect, sqlite_ty::Text => compact_str::CompactString);
116
117#[cfg(feature = "arrayvec")]
118impl_value_type_const_n!(SQLiteDialect, sqlite_ty::Text => arrayvec::ArrayString<N>);
119
120impl_value_type!(SQLiteDialect, sqlite_ty::Blob => &[u8]);
121
122#[cfg(any(feature = "alloc", feature = "std"))]
123impl_value_type!(SQLiteDialect, sqlite_ty::Blob =>
124    Cow<'_, [u8]>,
125    Vec<u8>,
126    Box<Vec<u8>>,
127    Rc<Vec<u8>>,
128    Arc<Vec<u8>>,
129);
130
131#[cfg(feature = "arrayvec")]
132impl_value_type_const_n!(SQLiteDialect, sqlite_ty::Blob => arrayvec::ArrayVec<u8, N>);
133
134#[cfg(feature = "bytes")]
135impl_value_type!(SQLiteDialect, sqlite_ty::Blob => bytes::Bytes, bytes::BytesMut);
136
137#[cfg(feature = "smallvec-types")]
138impl_value_type_const_n!(SQLiteDialect, sqlite_ty::Blob => smallvec::SmallVec<[u8; N]>);
139
140#[cfg(feature = "uuid")]
141impl_value_type!(SQLiteDialect, sqlite_ty::Blob => uuid::Uuid, &uuid::Uuid);
142
143#[cfg(feature = "chrono")]
144impl_value_type!(SQLiteDialect, sqlite_ty::Text =>
145    chrono::NaiveDate,
146    chrono::NaiveTime,
147    chrono::NaiveDateTime,
148    chrono::DateTime<chrono::FixedOffset>,
149    chrono::DateTime<chrono::Utc>,
150    chrono::Duration,
151);
152
153#[cfg(feature = "time")]
154impl_value_type!(SQLiteDialect, sqlite_ty::Text =>
155    time::Date,
156    time::Time,
157    time::PrimitiveDateTime,
158    time::OffsetDateTime,
159    time::Duration,
160);
161
162#[cfg(feature = "jiff")]
163impl_value_type!(SQLiteDialect, sqlite_ty::Text =>
164    jiff::civil::Date,
165    jiff::civil::Time,
166    jiff::civil::DateTime,
167    jiff::Timestamp,
168);
169
170#[cfg(feature = "rust-decimal")]
171impl_value_type!(SQLiteDialect, sqlite_ty::Text =>
172    rust_decimal::Decimal,
173    &rust_decimal::Decimal,
174);
175
176#[cfg(feature = "serde")]
177impl_value_type!(SQLiteDialect, sqlite_ty::Text => serde_json::Value);
178
179// =============================================================================
180// Postgres mappings
181// =============================================================================
182
183use drizzle_types::postgres::types as pg_ty;
184
185impl_value_type!(PostgresDialect, pg_ty::Int2 => i8, i16, u8);
186impl_value_type!(PostgresDialect, pg_ty::Int4 => i32, u16);
187impl_value_type!(PostgresDialect, pg_ty::Int8 => i64, u32, u64, isize, usize);
188impl_value_type!(PostgresDialect, pg_ty::Float4 => f32);
189impl_value_type!(PostgresDialect, pg_ty::Float8 => f64);
190impl_value_type!(PostgresDialect, pg_ty::Boolean => bool);
191
192impl_value_type!(PostgresDialect, pg_ty::Text => &str);
193
194#[cfg(any(feature = "alloc", feature = "std"))]
195impl_value_type!(PostgresDialect, pg_ty::Text =>
196    Cow<'_, str>,
197    String,
198    Box<String>,
199    Rc<String>,
200    Arc<String>,
201    Box<str>,
202    Rc<str>,
203    Arc<str>,
204);
205
206#[cfg(feature = "compact-str")]
207impl_value_type!(PostgresDialect, pg_ty::Text => compact_str::CompactString);
208
209#[cfg(feature = "arrayvec")]
210impl_value_type_const_n!(PostgresDialect, pg_ty::Text => arrayvec::ArrayString<N>);
211
212impl_value_type!(PostgresDialect, pg_ty::Bytea => &[u8]);
213
214#[cfg(any(feature = "alloc", feature = "std"))]
215impl_value_type!(PostgresDialect, pg_ty::Bytea =>
216    Cow<'_, [u8]>,
217    Vec<u8>,
218    Box<Vec<u8>>,
219    Rc<Vec<u8>>,
220    Arc<Vec<u8>>,
221);
222
223#[cfg(feature = "arrayvec")]
224impl_value_type_const_n!(PostgresDialect, pg_ty::Bytea => arrayvec::ArrayVec<u8, N>);
225
226#[cfg(feature = "bytes")]
227impl_value_type!(PostgresDialect, pg_ty::Bytea => bytes::Bytes, bytes::BytesMut);
228
229#[cfg(feature = "smallvec-types")]
230impl_value_type_const_n!(PostgresDialect, pg_ty::Bytea => smallvec::SmallVec<[u8; N]>);
231
232#[cfg(feature = "uuid")]
233impl_value_type!(PostgresDialect, pg_ty::Uuid => uuid::Uuid, &uuid::Uuid);
234
235#[cfg(feature = "rust-decimal")]
236impl_value_type!(PostgresDialect, pg_ty::Numeric =>
237    rust_decimal::Decimal,
238    &rust_decimal::Decimal,
239);
240
241#[cfg(feature = "serde")]
242impl_value_type!(PostgresDialect, pg_ty::Json => serde_json::Value);
243
244#[cfg(feature = "chrono")]
245impl_value_type!(PostgresDialect, pg_ty::Date => chrono::NaiveDate);
246#[cfg(feature = "chrono")]
247impl_value_type!(PostgresDialect, pg_ty::Time => chrono::NaiveTime);
248#[cfg(feature = "chrono")]
249impl_value_type!(PostgresDialect, pg_ty::Timestamp => chrono::NaiveDateTime);
250#[cfg(feature = "chrono")]
251impl_value_type!(PostgresDialect, pg_ty::Timestamptz =>
252    chrono::DateTime<chrono::FixedOffset>,
253    chrono::DateTime<chrono::Utc>,
254);
255#[cfg(feature = "chrono")]
256impl_value_type!(PostgresDialect, pg_ty::Interval => chrono::Duration);
257
258#[cfg(feature = "time")]
259impl_value_type!(PostgresDialect, pg_ty::Date => time::Date);
260#[cfg(feature = "time")]
261impl_value_type!(PostgresDialect, pg_ty::Time => time::Time);
262#[cfg(feature = "time")]
263impl_value_type!(PostgresDialect, pg_ty::Timestamp => time::PrimitiveDateTime);
264#[cfg(feature = "time")]
265impl_value_type!(PostgresDialect, pg_ty::Timestamptz => time::OffsetDateTime);
266#[cfg(feature = "time")]
267impl_value_type!(PostgresDialect, pg_ty::Interval => time::Duration);
268
269#[cfg(feature = "jiff")]
270impl_value_type!(PostgresDialect, pg_ty::Date => jiff::civil::Date);
271#[cfg(feature = "jiff")]
272impl_value_type!(PostgresDialect, pg_ty::Time => jiff::civil::Time);
273#[cfg(feature = "jiff")]
274impl_value_type!(PostgresDialect, pg_ty::Timestamp => jiff::civil::DateTime);
275#[cfg(feature = "jiff")]
276impl_value_type!(PostgresDialect, pg_ty::Timestamptz => jiff::Timestamp);
277
278#[cfg(feature = "cidr")]
279impl_value_type!(PostgresDialect, pg_ty::Inet => cidr::IpInet);
280#[cfg(feature = "cidr")]
281impl_value_type!(PostgresDialect, pg_ty::Cidr => cidr::IpCidr);
282#[cfg(feature = "cidr")]
283impl_value_type!(PostgresDialect, pg_ty::MacAddr => [u8; 6]);
284#[cfg(feature = "cidr")]
285impl_value_type!(PostgresDialect, pg_ty::MacAddr8 => [u8; 8]);
286
287#[cfg(feature = "geo-types")]
288impl_value_type!(PostgresDialect, pg_ty::Point => geo_types::Point<f64>);
289#[cfg(feature = "geo-types")]
290impl_value_type!(PostgresDialect, pg_ty::LineString => geo_types::LineString<f64>);
291#[cfg(feature = "geo-types")]
292impl_value_type!(PostgresDialect, pg_ty::Rect => geo_types::Rect<f64>);
293
294#[cfg(feature = "bit-vec")]
295impl_value_type!(PostgresDialect, pg_ty::BitString => bit_vec::BitVec);
296
297// Postgres Vec<T> → Array<T>
298
299#[cfg(any(feature = "alloc", feature = "std"))]
300impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Text> =>
301    Vec<String>, Vec<&str>);
302
303#[cfg(any(feature = "alloc", feature = "std"))]
304impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Int2> => Vec<i16>);
305
306#[cfg(any(feature = "alloc", feature = "std"))]
307impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Int4> => Vec<i32>);
308
309#[cfg(any(feature = "alloc", feature = "std"))]
310impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Int8> => Vec<i64>);
311
312#[cfg(any(feature = "alloc", feature = "std"))]
313impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Float4> => Vec<f32>);
314
315#[cfg(any(feature = "alloc", feature = "std"))]
316impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Float8> => Vec<f64>);
317
318#[cfg(any(feature = "alloc", feature = "std"))]
319impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Boolean> => Vec<bool>);
320
321#[cfg(all(any(feature = "alloc", feature = "std"), feature = "uuid"))]
322impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Uuid> => Vec<uuid::Uuid>);
323
324#[cfg(all(any(feature = "alloc", feature = "std"), feature = "chrono"))]
325impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Date> => Vec<chrono::NaiveDate>);
326
327#[cfg(all(any(feature = "alloc", feature = "std"), feature = "chrono"))]
328impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Time> => Vec<chrono::NaiveTime>);
329
330#[cfg(all(any(feature = "alloc", feature = "std"), feature = "chrono"))]
331impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Timestamp> => Vec<chrono::NaiveDateTime>);
332
333#[cfg(all(any(feature = "alloc", feature = "std"), feature = "chrono"))]
334impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Timestamptz> => Vec<chrono::DateTime<chrono::Utc>>);
335
336#[cfg(all(any(feature = "alloc", feature = "std"), feature = "rust-decimal"))]
337impl_value_type!(PostgresDialect, drizzle_types::Array<pg_ty::Numeric> => Vec<rust_decimal::Decimal>);
338
339// =============================================================================
340// MySQL mappings
341// =============================================================================
342
343use drizzle_types::mysql::types as mysql_ty;
344
345impl_value_type!(MySQLDialect, mysql_ty::TinyInt => i8);
346impl_value_type!(MySQLDialect, mysql_ty::SmallInt => i16);
347impl_value_type!(MySQLDialect, mysql_ty::Int => i32);
348impl_value_type!(MySQLDialect, mysql_ty::BigInt => i64, isize);
349impl_value_type!(MySQLDialect, mysql_ty::TinyIntUnsigned => u8);
350impl_value_type!(MySQLDialect, mysql_ty::SmallIntUnsigned => u16);
351impl_value_type!(MySQLDialect, mysql_ty::IntUnsigned => u32);
352impl_value_type!(MySQLDialect, mysql_ty::BigIntUnsigned => u64, usize);
353impl_value_type!(MySQLDialect, mysql_ty::Float => f32);
354impl_value_type!(MySQLDialect, mysql_ty::Double => f64);
355impl_value_type!(MySQLDialect, mysql_ty::Boolean => bool);
356
357impl_value_type!(MySQLDialect, mysql_ty::Text => &str);
358
359#[cfg(any(feature = "alloc", feature = "std"))]
360impl_value_type!(MySQLDialect, mysql_ty::Text =>
361    Cow<'_, str>,
362    String,
363    Box<String>,
364    Rc<String>,
365    Arc<String>,
366    Box<str>,
367    Rc<str>,
368    Arc<str>,
369);
370
371#[cfg(feature = "compact-str")]
372impl_value_type!(MySQLDialect, mysql_ty::Text => compact_str::CompactString);
373
374#[cfg(feature = "arrayvec")]
375impl_value_type_const_n!(MySQLDialect, mysql_ty::Varchar => arrayvec::ArrayString<N>);
376
377impl_value_type!(MySQLDialect, mysql_ty::Blob => &[u8]);
378
379#[cfg(any(feature = "alloc", feature = "std"))]
380impl_value_type!(MySQLDialect, mysql_ty::Blob =>
381    Cow<'_, [u8]>,
382    Vec<u8>,
383    Box<Vec<u8>>,
384    Rc<Vec<u8>>,
385    Arc<Vec<u8>>,
386);
387
388#[cfg(feature = "arrayvec")]
389impl_value_type_const_n!(MySQLDialect, mysql_ty::Varbinary => arrayvec::ArrayVec<u8, N>);
390
391impl_value_type_const_n!(MySQLDialect, mysql_ty::Binary => [u8; N]);
392impl_value_type_const_n!(MySQLDialect, mysql_ty::Char => [char; N]);
393
394#[cfg(feature = "bytes")]
395impl_value_type!(MySQLDialect, mysql_ty::Blob => bytes::Bytes, bytes::BytesMut);
396
397#[cfg(feature = "smallvec-types")]
398impl_value_type_const_n!(MySQLDialect, mysql_ty::Blob => smallvec::SmallVec<[u8; N]>);
399
400#[cfg(feature = "uuid")]
401impl_value_type!(MySQLDialect, mysql_ty::Binary => uuid::Uuid, &uuid::Uuid);
402
403#[cfg(feature = "rust-decimal")]
404impl_value_type!(MySQLDialect, mysql_ty::Decimal =>
405    rust_decimal::Decimal,
406    &rust_decimal::Decimal,
407);
408
409#[cfg(feature = "serde")]
410impl_value_type!(MySQLDialect, mysql_ty::Json => serde_json::Value);
411
412#[cfg(feature = "chrono")]
413impl_value_type!(MySQLDialect, mysql_ty::Date => chrono::NaiveDate);
414#[cfg(feature = "chrono")]
415impl_value_type!(MySQLDialect, mysql_ty::Time => chrono::NaiveTime);
416#[cfg(feature = "chrono")]
417impl_value_type!(MySQLDialect, mysql_ty::DateTime => chrono::NaiveDateTime);
418#[cfg(feature = "chrono")]
419impl_value_type!(MySQLDialect, mysql_ty::Timestamp =>
420    chrono::DateTime<chrono::FixedOffset>,
421    chrono::DateTime<chrono::Utc>,
422);
423
424#[cfg(feature = "time")]
425impl_value_type!(MySQLDialect, mysql_ty::Date => time::Date);
426#[cfg(feature = "time")]
427impl_value_type!(MySQLDialect, mysql_ty::Time => time::Time);
428#[cfg(feature = "time")]
429impl_value_type!(MySQLDialect, mysql_ty::DateTime => time::PrimitiveDateTime);
430#[cfg(feature = "time")]
431impl_value_type!(MySQLDialect, mysql_ty::Timestamp => time::OffsetDateTime);
432
433#[cfg(feature = "jiff")]
434impl_value_type!(MySQLDialect, mysql_ty::Date => jiff::civil::Date);
435#[cfg(feature = "jiff")]
436impl_value_type!(MySQLDialect, mysql_ty::Time => jiff::civil::Time);
437#[cfg(feature = "jiff")]
438impl_value_type!(MySQLDialect, mysql_ty::DateTime => jiff::civil::DateTime);
439#[cfg(feature = "jiff")]
440impl_value_type!(MySQLDialect, mysql_ty::Timestamp => jiff::Timestamp);