drizzle_core/expr/util.rs
1//! Helpers: aliases (`AS`), `CAST`, `TYPEOF`, raw typed SQL and `EXCLUDED`.
2
3use crate::dialect::{DialectSupports, feature};
4use crate::dialect::{MySQLDialect, PostgresDialect, SQLiteDialect};
5use crate::sql::{SQL, Token};
6use crate::traits::{SQLColumnInfo, SQLParam, ToSQL};
7use crate::types::{Compatible, DataType, Textual};
8
9use super::{AggregateKind, Expr, NonNull, Null, Nullability, SQLExpr, Scalar};
10use crate::scope::ScopeOnly;
11
12// =============================================================================
13// ALIAS
14// =============================================================================
15
16/// An expression renamed with `AS "name"`; created by [`alias`] or
17/// [`AliasExt::alias`].
18///
19/// Keeps the inner expression's SQL type, nullability and decoded Rust type,
20/// so an aliased column in a SELECT list still decodes to the right type.
21#[derive(Clone, Copy, Debug)]
22pub struct AliasedExpr<E> {
23 pub(crate) expr: E,
24 pub(crate) name: &'static str,
25}
26
27impl<'a, V, E> ToSQL<'a, V> for AliasedExpr<E>
28where
29 V: SQLParam + 'a,
30 E: ToSQL<'a, V>,
31{
32 fn to_sql(&self) -> SQL<'a, V> {
33 self.expr.to_sql().alias(self.name)
34 }
35
36 fn into_sql(self) -> SQL<'a, V> {
37 self.expr.into_sql().alias(self.name)
38 }
39}
40
41impl<'a, V, E> Expr<'a, V> for AliasedExpr<E>
42where
43 V: SQLParam + 'a,
44 E: Expr<'a, V>,
45{
46 type SQLType = E::SQLType;
47 type Nullable = E::Nullable;
48 type Aggregate = E::Aggregate;
49
50 fn to_expr_sql(&self) -> SQL<'a, V> {
51 self.expr.to_expr_sql().alias(self.name)
52 }
53
54 fn into_expr_sql(self) -> SQL<'a, V> {
55 self.expr.into_expr_sql().alias(self.name)
56 }
57}
58
59impl<E: super::ExprSources> super::ExprSources for AliasedExpr<E> {
60 type Sources = E::Sources;
61}
62
63impl<E: super::HasAggStatus> super::HasAggStatus for AliasedExpr<E> {
64 type Status = E::Status;
65}
66
67impl<E: crate::row::ExprValueType> crate::row::ExprValueType for AliasedExpr<E> {
68 type ValueType = E::ValueType;
69}
70
71impl<E> crate::row::IntoSelectTarget for AliasedExpr<E>
72where
73 E: crate::row::ExprValueType,
74{
75 type Marker = crate::row::SelectCols<(Self,)>;
76}
77
78/// Method syntax for naming a selected expression.
79///
80/// - `.alias("name")` sets a name at run time ([`AliasedExpr`]).
81/// - `.named::<Tag>()` sets a name in the type ([`NamedExpr`]), so a derived
82/// table built from the query can refer to the column.
83///
84/// Implemented for every expression with a known Rust type. On raw [`SQL`],
85/// the inherent [`SQL::alias`] method is called instead and returns `SQL`.
86///
87/// # Examples
88///
89/// ```rust
90/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
91/// # use drizzle_core::{ColumnRef, SQL, SQLParam, ToSQL, expr::*};
92/// # #[derive(Clone, Debug)] struct Value(String);
93/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
94/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
95/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
96/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
97/// # fn col<X: drizzle_core::types::DataType, N: Nullability>(c: &'static str) -> C<X, N> { Box::leak(Box::new(SQLExpr::new(SQL::column(ColumnRef::sql("users", c))))) }
98/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
99/// # struct Users { id: C<Int>, age: C<Int>, name: C<Text>, email: C<Text, Null>, score: C<Real, Null>, active: C<<D as DialectTypes>::Bool>, created_at: C<<D as DialectTypes>::Timestamp> }
100/// # let users = Users { id: col("id"), age: col("age"), name: col("name"), email: col("email"), score: col("score"), active: col("active"), created_at: col("created_at") };
101/// let n = count(users.id).alias("user_count");
102/// assert_eq!(n.to_sql().sql(), r#"COUNT ("users"."id") AS "user_count""#);
103/// ```
104pub trait AliasExt: Sized {
105 /// Renames this expression: `expr AS "name"`.
106 fn alias(self, name: &'static str) -> AliasedExpr<Self> {
107 AliasedExpr { expr: self, name }
108 }
109
110 /// Names this expression with a type-level [`Tag`](crate::Tag), rendered
111 /// as `expr AS "<Tag::NAME>"`.
112 fn named<Name: crate::Tag>(self) -> NamedExpr<Self, Name> {
113 NamedExpr {
114 expr: self,
115 name: core::marker::PhantomData,
116 }
117 }
118}
119
120impl<T: crate::row::ExprValueType> AliasExt for T {}
121
122/// Renames an expression: `expr AS "name"`.
123///
124/// The result keeps the expression's SQL type, nullability and decoded Rust
125/// type. Same as [`AliasExt::alias`].
126///
127/// # Examples
128///
129/// ```rust
130/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
131/// # use drizzle_core::{ColumnRef, SQL, SQLParam, ToSQL, expr::*};
132/// # #[derive(Clone, Debug)] struct Value(String);
133/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
134/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
135/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
136/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
137/// # fn col<X: drizzle_core::types::DataType, N: Nullability>(c: &'static str) -> C<X, N> { Box::leak(Box::new(SQLExpr::new(SQL::column(ColumnRef::sql("users", c))))) }
138/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
139/// # struct Users { id: C<Int>, age: C<Int>, name: C<Text>, email: C<Text, Null>, score: C<Real, Null>, active: C<<D as DialectTypes>::Bool>, created_at: C<<D as DialectTypes>::Timestamp> }
140/// # let users = Users { id: col("id"), age: col("age"), name: col("name"), email: col("email"), score: col("score"), active: col("active"), created_at: col("created_at") };
141/// let label = alias(upper(users.name), "label");
142/// assert_eq!(label.to_sql().sql(), r#"UPPER ("users"."name") AS "label""#);
143/// ```
144pub const fn alias<E>(expr: E, name: &'static str) -> AliasedExpr<E> {
145 AliasedExpr { expr, name }
146}
147
148/// An expression whose output name is a type-level [`Tag`](crate::Tag);
149/// created by [`AliasExt::named`].
150///
151/// Unlike [`AliasedExpr`], the name is part of the type, so a derived table
152/// built from the query can select this column by name.
153#[derive(Clone, Copy, Debug)]
154pub struct NamedExpr<E, Name> {
155 pub(crate) expr: E,
156 pub(crate) name: core::marker::PhantomData<Name>,
157}
158
159impl<E, Name> NamedExpr<E, Name> {
160 /// Returns the wrapped expression.
161 pub const fn expression(&self) -> &E {
162 &self.expr
163 }
164
165 /// Returns the wrapped expression by value.
166 pub fn into_expression(self) -> E {
167 self.expr
168 }
169}
170
171impl<'a, V, E, Name> ToSQL<'a, V> for NamedExpr<E, Name>
172where
173 V: SQLParam + 'a,
174 E: ToSQL<'a, V>,
175 Name: crate::Tag,
176{
177 fn to_sql(&self) -> SQL<'a, V> {
178 self.expr.to_sql().alias(Name::NAME)
179 }
180
181 fn into_sql(self) -> SQL<'a, V> {
182 self.expr.into_sql().alias(Name::NAME)
183 }
184}
185
186impl<E: super::ExprSources, Name> super::ExprSources for NamedExpr<E, Name> {
187 type Sources = E::Sources;
188}
189
190impl<'a, V, E, Name> Expr<'a, V> for NamedExpr<E, Name>
191where
192 V: SQLParam + 'a,
193 E: Expr<'a, V>,
194 Name: crate::Tag,
195{
196 type SQLType = E::SQLType;
197 type Nullable = E::Nullable;
198 type Aggregate = E::Aggregate;
199
200 fn to_expr_sql(&self) -> SQL<'a, V> {
201 self.expr.to_expr_sql().alias(Name::NAME)
202 }
203
204 fn into_expr_sql(self) -> SQL<'a, V> {
205 self.expr.into_expr_sql().alias(Name::NAME)
206 }
207}
208
209impl<E, Name> super::HasAggStatus for NamedExpr<E, Name>
210where
211 E: super::HasAggStatus,
212{
213 type Status = E::Status;
214}
215
216impl<E, Name> crate::row::ExprValueType for NamedExpr<E, Name>
217where
218 E: crate::row::ExprValueType,
219{
220 type ValueType = E::ValueType;
221}
222
223impl<E, Name> crate::row::IntoSelectTarget for NamedExpr<E, Name>
224where
225 E: crate::row::ExprValueType,
226{
227 type Marker = crate::row::SelectCols<(Self,)>;
228}
229
230impl<E, Name> crate::row::GroupByIdentity for NamedExpr<E, Name>
231where
232 E: crate::row::GroupByIdentity,
233{
234 type Identity = E::Identity;
235}
236
237// =============================================================================
238// TYPEOF
239// =============================================================================
240
241impl DialectSupports<feature::Typeof> for SQLiteDialect {}
242
243/// The storage class of a value as text (`TYPEOF`), on SQLite.
244///
245/// Returns `'null'`, `'integer'`, `'real'`, `'text'` or `'blob'`. Accepts any
246/// expression. The result is text and never NULL.
247///
248/// # Examples
249///
250/// ```rust
251/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
252/// # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
253/// # #[derive(Clone, Debug)] struct Value(String);
254/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
255/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
256/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
257/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
258/// # fn col<X: drizzle_core::types::DataType, N: Nullability>(c: &'static str) -> C<X, N> { Box::leak(Box::new(SQLExpr::new(SQL::column(ColumnRef::sql("users", c))))) }
259/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
260/// # struct Users { id: C<Int>, age: C<Int>, name: C<Text>, email: C<Text, Null>, score: C<Real, Null>, active: C<<D as DialectTypes>::Bool>, created_at: C<<D as DialectTypes>::Timestamp> }
261/// # let users = Users { id: col("id"), age: col("age"), name: col("name"), email: col("email"), score: col("score"), active: col("active"), created_at: col("created_at") };
262/// assert_eq!(typeof_(users.age).sql(), r#"TYPEOF ("users"."age")"#);
263/// ```
264#[allow(clippy::type_complexity)]
265pub fn typeof_<'a, V, E>(
266 expr: E,
267) -> SQLExpr<
268 'a,
269 V,
270 <V::DialectMarker as crate::dialect::DialectTypes>::Text,
271 NonNull,
272 E::Aggregate,
273 ScopeOnly<E::Sources>,
274>
275where
276 V: SQLParam + 'a,
277 V::DialectMarker: DialectSupports<feature::Typeof>,
278 E: Expr<'a, V>,
279{
280 SQLExpr::new(SQL::func("TYPEOF", expr.into_expr_sql()))
281}
282
283/// Same as [`typeof_`], spelled with a raw identifier.
284#[allow(clippy::type_complexity)]
285pub fn r#typeof<'a, V, E>(
286 expr: E,
287) -> SQLExpr<
288 'a,
289 V,
290 <V::DialectMarker as crate::dialect::DialectTypes>::Text,
291 NonNull,
292 E::Aggregate,
293 ScopeOnly<E::Sources>,
294>
295where
296 V: SQLParam + 'a,
297 V::DialectMarker: DialectSupports<feature::Typeof>,
298 E: Expr<'a, V>,
299{
300 typeof_(expr)
301}
302
303// =============================================================================
304// CAST
305// =============================================================================
306
307/// The SQL type name [`cast`] writes when given a type marker, such as
308/// `"INTEGER"` for SQLite's `Integer`.
309pub trait DefaultCastTypeName: DataType {
310 /// The type name used in `CAST(expr AS <name>)`.
311 const CAST_TYPE_NAME: &'static str;
312}
313
314impl DefaultCastTypeName for drizzle_types::sqlite::types::Integer {
315 const CAST_TYPE_NAME: &'static str = "INTEGER";
316}
317impl DefaultCastTypeName for drizzle_types::sqlite::types::Text {
318 const CAST_TYPE_NAME: &'static str = "TEXT";
319}
320impl DefaultCastTypeName for drizzle_types::sqlite::types::Real {
321 const CAST_TYPE_NAME: &'static str = "REAL";
322}
323impl DefaultCastTypeName for drizzle_types::sqlite::types::Blob {
324 const CAST_TYPE_NAME: &'static str = "BLOB";
325}
326impl DefaultCastTypeName for drizzle_types::sqlite::types::Numeric {
327 const CAST_TYPE_NAME: &'static str = "NUMERIC";
328}
329impl DefaultCastTypeName for drizzle_types::sqlite::types::Any {
330 const CAST_TYPE_NAME: &'static str = "ANY";
331}
332
333impl DefaultCastTypeName for drizzle_types::postgres::types::Int2 {
334 const CAST_TYPE_NAME: &'static str = "SMALLINT";
335}
336impl DefaultCastTypeName for drizzle_types::postgres::types::Int4 {
337 const CAST_TYPE_NAME: &'static str = "INTEGER";
338}
339impl DefaultCastTypeName for drizzle_types::postgres::types::Int8 {
340 const CAST_TYPE_NAME: &'static str = "BIGINT";
341}
342impl DefaultCastTypeName for drizzle_types::postgres::types::Float4 {
343 const CAST_TYPE_NAME: &'static str = "REAL";
344}
345impl DefaultCastTypeName for drizzle_types::postgres::types::Float8 {
346 const CAST_TYPE_NAME: &'static str = "DOUBLE PRECISION";
347}
348impl DefaultCastTypeName for drizzle_types::postgres::types::Varchar {
349 const CAST_TYPE_NAME: &'static str = "VARCHAR";
350}
351impl DefaultCastTypeName for drizzle_types::postgres::types::Text {
352 const CAST_TYPE_NAME: &'static str = "TEXT";
353}
354impl DefaultCastTypeName for drizzle_types::postgres::types::Char {
355 const CAST_TYPE_NAME: &'static str = "CHAR";
356}
357impl DefaultCastTypeName for drizzle_types::postgres::types::Bytea {
358 const CAST_TYPE_NAME: &'static str = "BYTEA";
359}
360impl DefaultCastTypeName for drizzle_types::postgres::types::Boolean {
361 const CAST_TYPE_NAME: &'static str = "BOOLEAN";
362}
363impl DefaultCastTypeName for drizzle_types::postgres::types::Timestamptz {
364 const CAST_TYPE_NAME: &'static str = "TIMESTAMPTZ";
365}
366impl DefaultCastTypeName for drizzle_types::postgres::types::Timestamp {
367 const CAST_TYPE_NAME: &'static str = "TIMESTAMP";
368}
369impl DefaultCastTypeName for drizzle_types::postgres::types::Date {
370 const CAST_TYPE_NAME: &'static str = "DATE";
371}
372impl DefaultCastTypeName for drizzle_types::postgres::types::Time {
373 const CAST_TYPE_NAME: &'static str = "TIME";
374}
375impl DefaultCastTypeName for drizzle_types::postgres::types::Timetz {
376 const CAST_TYPE_NAME: &'static str = "TIMETZ";
377}
378impl DefaultCastTypeName for drizzle_types::postgres::types::Numeric {
379 const CAST_TYPE_NAME: &'static str = "NUMERIC";
380}
381impl DefaultCastTypeName for drizzle_types::postgres::types::Uuid {
382 const CAST_TYPE_NAME: &'static str = "UUID";
383}
384impl DefaultCastTypeName for drizzle_types::postgres::types::Json {
385 const CAST_TYPE_NAME: &'static str = "JSON";
386}
387impl DefaultCastTypeName for drizzle_types::postgres::types::Jsonb {
388 const CAST_TYPE_NAME: &'static str = "JSONB";
389}
390impl DefaultCastTypeName for drizzle_types::postgres::types::Any {
391 const CAST_TYPE_NAME: &'static str = "ANY";
392}
393impl DefaultCastTypeName for drizzle_types::postgres::types::Interval {
394 const CAST_TYPE_NAME: &'static str = "INTERVAL";
395}
396impl DefaultCastTypeName for drizzle_types::postgres::types::Inet {
397 const CAST_TYPE_NAME: &'static str = "INET";
398}
399impl DefaultCastTypeName for drizzle_types::postgres::types::Cidr {
400 const CAST_TYPE_NAME: &'static str = "CIDR";
401}
402impl DefaultCastTypeName for drizzle_types::postgres::types::MacAddr {
403 const CAST_TYPE_NAME: &'static str = "MACADDR";
404}
405impl DefaultCastTypeName for drizzle_types::postgres::types::MacAddr8 {
406 const CAST_TYPE_NAME: &'static str = "MACADDR8";
407}
408impl DefaultCastTypeName for drizzle_types::postgres::types::Point {
409 const CAST_TYPE_NAME: &'static str = "POINT";
410}
411impl DefaultCastTypeName for drizzle_types::postgres::types::LineString {
412 const CAST_TYPE_NAME: &'static str = "PATH";
413}
414impl DefaultCastTypeName for drizzle_types::postgres::types::Rect {
415 const CAST_TYPE_NAME: &'static str = "BOX";
416}
417impl DefaultCastTypeName for drizzle_types::postgres::types::BitString {
418 const CAST_TYPE_NAME: &'static str = "BIT VARYING";
419}
420impl DefaultCastTypeName for drizzle_types::postgres::types::Line {
421 const CAST_TYPE_NAME: &'static str = "LINE";
422}
423impl DefaultCastTypeName for drizzle_types::postgres::types::LineSegment {
424 const CAST_TYPE_NAME: &'static str = "LSEG";
425}
426impl DefaultCastTypeName for drizzle_types::postgres::types::Polygon {
427 const CAST_TYPE_NAME: &'static str = "POLYGON";
428}
429impl DefaultCastTypeName for drizzle_types::postgres::types::Circle {
430 const CAST_TYPE_NAME: &'static str = "CIRCLE";
431}
432impl DefaultCastTypeName for drizzle_types::postgres::types::Enum {
433 const CAST_TYPE_NAME: &'static str = "TEXT";
434}
435
436impl DefaultCastTypeName for drizzle_types::mysql::types::BigInt {
437 const CAST_TYPE_NAME: &'static str = "SIGNED";
438}
439impl DefaultCastTypeName for drizzle_types::mysql::types::BigIntUnsigned {
440 const CAST_TYPE_NAME: &'static str = "UNSIGNED";
441}
442impl DefaultCastTypeName for drizzle_types::mysql::types::Float {
443 const CAST_TYPE_NAME: &'static str = "FLOAT";
444}
445impl DefaultCastTypeName for drizzle_types::mysql::types::Double {
446 const CAST_TYPE_NAME: &'static str = "DOUBLE";
447}
448impl DefaultCastTypeName for drizzle_types::mysql::types::Decimal {
449 const CAST_TYPE_NAME: &'static str = "DECIMAL";
450}
451impl DefaultCastTypeName for drizzle_types::mysql::types::Varchar {
452 const CAST_TYPE_NAME: &'static str = "CHAR";
453}
454impl DefaultCastTypeName for drizzle_types::mysql::types::Varbinary {
455 const CAST_TYPE_NAME: &'static str = "BINARY";
456}
457impl DefaultCastTypeName for drizzle_types::mysql::types::Json {
458 const CAST_TYPE_NAME: &'static str = "JSON";
459}
460impl DefaultCastTypeName for drizzle_types::mysql::types::Date {
461 const CAST_TYPE_NAME: &'static str = "DATE";
462}
463impl DefaultCastTypeName for drizzle_types::mysql::types::Time {
464 const CAST_TYPE_NAME: &'static str = "TIME";
465}
466impl DefaultCastTypeName for drizzle_types::mysql::types::DateTime {
467 const CAST_TYPE_NAME: &'static str = "DATETIME";
468}
469impl DefaultCastTypeName for drizzle_types::mysql::types::Year {
470 const CAST_TYPE_NAME: &'static str = "YEAR";
471}
472
473/// The target argument of [`cast`]: a SQL type name such as `"VARCHAR(255)"`,
474/// or a type marker value whose [`DefaultCastTypeName`] is used.
475#[diagnostic::on_unimplemented(
476 message = "`{Self}` is not a cast target for `{T}` here",
477 label = "a type marker target must be the cast's result type, from this dialect",
478 note = "or pass the SQL type name as a string, such as `\"VARCHAR(255)\"`"
479)]
480pub trait CastTarget<'a, T: DataType, D> {
481 /// The SQL type name to cast to.
482 fn cast_type_name(self) -> &'a str;
483}
484
485/// Casts from `Source` to `Target` that dialect `D` allows.
486///
487/// On SQLite and PostgreSQL the two types must be compatible. On MySQL,
488/// `CAST` accepts a fixed set of targets (`SIGNED`, `DOUBLE`, `CHAR`,
489/// `DATE`, ...).
490#[diagnostic::on_unimplemented(
491 message = "cannot cast `{Source}` to `{Target}` for this dialect",
492 label = "cast target is incompatible with source type",
493 note = "use a supported target marker, or raw SQL when the conversion is intentionally dialect-specific"
494)]
495pub trait CastTypePolicy<D, Source: DataType, Target: DataType> {}
496
497/// Nullability of a cast to `Self` on dialect `D`. MySQL temporal casts give
498/// NULL for invalid input, so they are nullable.
499#[doc(hidden)]
500pub trait CastNullabilityPolicy<D, Input: Nullability>: DataType {
501 type Output: Nullability;
502}
503
504macro_rules! mysql_cast_policy {
505 (
506 preserving: [$($preserving:ty),+ $(,)?],
507 nullable: [$($nullable:ty),+ $(,)?],
508 ) => {
509 $(
510 impl<Source: DataType> CastTypePolicy<MySQLDialect, Source, $preserving> for () {}
511
512 impl<Input: Nullability> CastNullabilityPolicy<MySQLDialect, Input> for $preserving {
513 type Output = Input;
514 }
515 )+
516 $(
517 impl<Source: DataType> CastTypePolicy<MySQLDialect, Source, $nullable> for () {}
518
519 impl<Input: Nullability> CastNullabilityPolicy<MySQLDialect, Input> for $nullable {
520 type Output = Null;
521 }
522 )+
523 };
524}
525
526mysql_cast_policy! {
527 preserving: [
528 drizzle_types::mysql::types::BigInt,
529 drizzle_types::mysql::types::BigIntUnsigned,
530 drizzle_types::mysql::types::Float,
531 drizzle_types::mysql::types::Double,
532 drizzle_types::mysql::types::Decimal,
533 drizzle_types::mysql::types::Varchar,
534 drizzle_types::mysql::types::Varbinary,
535 drizzle_types::mysql::types::Json,
536 ],
537 nullable: [
538 drizzle_types::mysql::types::Date,
539 drizzle_types::mysql::types::Time,
540 drizzle_types::mysql::types::DateTime,
541 drizzle_types::mysql::types::Year,
542 ],
543}
544
545impl<Source: DataType + Compatible<Target>, Target: DataType>
546 CastTypePolicy<PostgresDialect, Source, Target> for ()
547{
548}
549
550impl<Input: Nullability, Target: DataType> CastNullabilityPolicy<PostgresDialect, Input>
551 for Target
552{
553 type Output = Input;
554}
555
556impl<Source: DataType + Compatible<Target>, Target: DataType>
557 CastTypePolicy<SQLiteDialect, Source, Target> for ()
558{
559}
560
561impl<Input: Nullability, Target: DataType> CastNullabilityPolicy<SQLiteDialect, Input> for Target {
562 type Output = Input;
563}
564
565impl<'a, T: DataType, D> CastTarget<'a, T, D> for &'a str {
566 fn cast_type_name(self) -> &'a str {
567 self
568 }
569}
570
571impl<'a, T, D> CastTarget<'a, T, D> for T
572where
573 T: DataType + DefaultCastTypeName,
574{
575 fn cast_type_name(self) -> &'a str {
576 T::CAST_TYPE_NAME
577 }
578}
579
580/// Converts an expression to another SQL type (`CAST(expr AS type)`).
581///
582/// The target is either a type marker value, such as
583/// `drizzle::sqlite::types::Text`, whose SQL name is used, or a SQL type
584/// name such as `"VARCHAR(255)"`. With a name, give the result type
585/// explicitly: `cast::<_, _, Text>(expr, "VARCHAR(255)")`.
586///
587/// On SQLite and PostgreSQL, source and target types must be compatible (see
588/// [`CastTypePolicy`]). The result has the target type and keeps the
589/// expression's aggregate kind. It keeps the expression's nullability,
590/// except for MySQL casts to temporal types, which can give NULL for invalid
591/// input and so are nullable.
592///
593/// # Examples
594///
595/// ```rust
596/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
597/// # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
598/// # #[derive(Clone, Debug)] struct Value(String);
599/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
600/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
601/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
602/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
603/// # fn col<X: drizzle_core::types::DataType, N: Nullability>(c: &'static str) -> C<X, N> { Box::leak(Box::new(SQLExpr::new(SQL::column(ColumnRef::sql("users", c))))) }
604/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
605/// # struct Users { id: C<Int>, age: C<Int>, name: C<Text>, email: C<Text, Null>, score: C<Real, Null>, active: C<<D as DialectTypes>::Bool>, created_at: C<<D as DialectTypes>::Timestamp> }
606/// # let users = Users { id: col("id"), age: col("age"), name: col("name"), email: col("email"), score: col("score"), active: col("active"), created_at: col("created_at") };
607/// // Integer to SQLite REAL, using the type marker.
608/// let real = cast(users.age, Real::default());
609/// assert_eq!(real.sql(), r#"CAST ("users"."age" AS REAL)"#);
610///
611/// // The same with an explicit type name.
612/// let real = cast::<_, _, Real>(users.age, "DOUBLE");
613/// assert_eq!(real.sql(), r#"CAST ("users"."age" AS DOUBLE)"#);
614/// ```
615#[allow(clippy::type_complexity)]
616pub fn cast<'a, V, E, Target>(
617 expr: E,
618 target_type: impl CastTarget<'a, Target, V::DialectMarker>,
619) -> SQLExpr<
620 'a,
621 V,
622 Target,
623 <Target as CastNullabilityPolicy<V::DialectMarker, E::Nullable>>::Output,
624 E::Aggregate,
625 E::Sources,
626>
627where
628 V: SQLParam + 'a,
629 E: Expr<'a, V>,
630 Target: DataType + CastNullabilityPolicy<V::DialectMarker, E::Nullable>,
631 (): CastTypePolicy<V::DialectMarker, E::SQLType, Target>,
632{
633 SQLExpr::new(SQL::func(
634 "CAST",
635 expr.into_expr_sql()
636 .push(Token::AS)
637 .append(SQL::raw(target_type.cast_type_name())),
638 ))
639}
640
641// =============================================================================
642// STRING CONCATENATION
643// =============================================================================
644
645/// Joins two text values; same as [`concat`](super::concat).
646///
647/// Renders `left || right` on SQLite and PostgreSQL and `CONCAT(left, right)`
648/// on MySQL. Both arguments must be text. The result is text, nullable if
649/// either argument is.
650///
651/// # Examples
652///
653/// ```rust
654/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
655/// # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
656/// # #[derive(Clone, Debug)] struct Value(String);
657/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
658/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
659/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
660/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
661/// # fn col<X: drizzle_core::types::DataType, N: Nullability>(c: &'static str) -> C<X, N> { Box::leak(Box::new(SQLExpr::new(SQL::column(ColumnRef::sql("users", c))))) }
662/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
663/// # struct Users { id: C<Int>, age: C<Int>, name: C<Text>, email: C<Text, Null>, score: C<Real, Null>, active: C<<D as DialectTypes>::Bool>, created_at: C<<D as DialectTypes>::Timestamp> }
664/// # let users = Users { id: col("id"), age: col("age"), name: col("name"), email: col("email"), score: col("score"), active: col("active"), created_at: col("created_at") };
665/// let label = string_concat(users.name, "!");
666/// assert_eq!(label.sql(), r#""users"."name" || ?"#);
667/// ```
668#[allow(clippy::type_complexity)]
669pub fn string_concat<'a, V, L, R>(
670 left: L,
671 right: R,
672) -> SQLExpr<
673 'a,
674 V,
675 <V::DialectMarker as crate::dialect::DialectTypes>::Text,
676 <L::Nullable as Nullability>::Or<R::Nullable>,
677 <L::Aggregate as AggregateKind>::Or<R::Aggregate>,
678 (L::Sources, R::Sources),
679>
680where
681 V: SQLParam + 'a,
682 L: Expr<'a, V>,
683 R: Expr<'a, V>,
684 L::SQLType: Textual,
685 R::SQLType: Textual,
686 R::Nullable: Nullability,
687 R::Aggregate: AggregateKind,
688{
689 super::concat(left, right)
690}
691
692// =============================================================================
693// RAW SQL Expression
694// =============================================================================
695
696/// A raw SQL fragment with a declared SQL type, typed as nullable.
697///
698/// The text is inserted into the query as is, so never build it from user
699/// input. The type system trusts the declared type `T` and cannot check the
700/// SQL. The result is nullable; use [`raw_non_null`] when the SQL can never be
701/// NULL.
702///
703/// # Examples
704///
705/// ```rust
706/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
707/// # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
708/// # #[derive(Clone, Debug)] struct Value(String);
709/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
710/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
711/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
712/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
713/// # fn col<X: drizzle_core::types::DataType, N: Nullability>(c: &'static str) -> C<X, N> { Box::leak(Box::new(SQLExpr::new(SQL::column(ColumnRef::sql("users", c))))) }
714/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
715/// # struct Users { id: C<Int>, age: C<Int>, name: C<Text>, email: C<Text, Null>, score: C<Real, Null>, active: C<<D as DialectTypes>::Bool>, created_at: C<<D as DialectTypes>::Timestamp> }
716/// # let users = Users { id: col("id"), age: col("age"), name: col("name"), email: col("email"), score: col("score"), active: col("active"), created_at: col("created_at") };
717/// let one = raw::<Value, Int>("1");
718/// assert_eq!(eq(users.id, one).sql(), r#""users"."id" = 1"#);
719/// ```
720#[must_use]
721pub fn raw<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, Null, Scalar, ()>
722where
723 V: SQLParam + 'a,
724 T: DataType,
725{
726 SQLExpr::new(SQL::raw(sql))
727}
728
729/// A raw SQL fragment with a declared SQL type, typed as nullable; same as
730/// [`raw`].
731#[must_use]
732pub fn raw_nullable<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, Null, Scalar, ()>
733where
734 V: SQLParam + 'a,
735 T: DataType,
736{
737 SQLExpr::new(SQL::raw(sql))
738}
739
740/// A raw SQL fragment with a declared SQL type, typed as non-null.
741///
742/// Like [`raw`], but the result is non-null. The type system trusts both the
743/// declared type `T` and the claim that the SQL is never NULL.
744///
745/// # Examples
746///
747/// ```rust
748/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
749/// # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
750/// # #[derive(Clone, Debug)] struct Value(String);
751/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
752/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
753/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
754/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
755/// # fn col<X: drizzle_core::types::DataType, N: Nullability>(c: &'static str) -> C<X, N> { Box::leak(Box::new(SQLExpr::new(SQL::column(ColumnRef::sql("users", c))))) }
756/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
757/// # struct Users { id: C<Int>, age: C<Int>, name: C<Text>, email: C<Text, Null>, score: C<Real, Null>, active: C<<D as DialectTypes>::Bool>, created_at: C<<D as DialectTypes>::Timestamp> }
758/// # let users = Users { id: col("id"), age: col("age"), name: col("name"), email: col("email"), score: col("score"), active: col("active"), created_at: col("created_at") };
759/// let now = raw_non_null::<Value, Text>("CURRENT_TIMESTAMP");
760/// assert_eq!(now.sql(), "CURRENT_TIMESTAMP");
761/// ```
762#[must_use]
763pub fn raw_non_null<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, NonNull, Scalar, ()>
764where
765 V: SQLParam + 'a,
766 T: DataType,
767{
768 SQLExpr::new(SQL::raw(sql))
769}
770
771// =============================================================================
772// EXCLUDED (for ON CONFLICT DO UPDATE)
773// =============================================================================
774
775/// A column of the row that failed to insert (`EXCLUDED."column"`); created
776/// by [`excluded`].
777#[derive(Clone, Copy, Debug)]
778pub struct Excluded<C> {
779 column: C,
780}
781
782impl DialectSupports<feature::Excluded> for SQLiteDialect {}
783impl DialectSupports<feature::Excluded> for PostgresDialect {}
784
785/// Refers to the value a conflicting insert tried to write (`EXCLUDED."column"`).
786///
787/// Use it in `ON CONFLICT ... DO UPDATE SET` to copy the new value into the
788/// existing row. The argument must be a table column. The result has the
789/// column's SQL type and nullability. Available on SQLite and PostgreSQL.
790///
791/// # Examples
792///
793/// ```rust
794/// # use drizzle_core::dialect::{Dialect, SQLiteDialect};
795/// # use drizzle_core::{SQL, SQLColumnInfo, SQLParam, SQLTableInfo, ToSQL, expr::excluded};
796/// # #[derive(Clone, Debug)] struct Value;
797/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = SQLiteDialect; }
798/// # struct NameColumn;
799/// # impl SQLColumnInfo for NameColumn {
800/// # fn name(&self) -> &'static str { "name" }
801/// # fn is_not_null(&self) -> bool { true }
802/// # fn is_primary_key(&self) -> bool { false }
803/// # fn is_unique(&self) -> bool { false }
804/// # fn r#type(&self) -> &'static str { "TEXT" }
805/// # fn has_default(&self) -> bool { false }
806/// # fn table(&self) -> &'static dyn SQLTableInfo { unimplemented!() }
807/// # }
808/// // `NameColumn` stands in for a generated column such as `users.name`.
809/// let new_name: SQL<'_, Value> = excluded(NameColumn).to_sql();
810/// assert_eq!(new_name.sql(), r#"EXCLUDED."name""#);
811/// ```
812///
813/// In an upsert built with a dialect crate:
814///
815/// ```text
816/// // INSERT INTO "simple" ("id", "name") VALUES (?, ?)
817/// // ON CONFLICT ("id") DO UPDATE SET "name" = EXCLUDED."name"
818/// db.insert(simple)
819/// .values([InsertSimple::new("test").with_id(1)])
820/// .on_conflict(simple.id)
821/// .do_update(UpdateSimple::default().with_name(excluded(simple.name)));
822/// ```
823pub const fn excluded<C>(column: C) -> Excluded<C> {
824 Excluded { column }
825}
826
827/// `EXCLUDED` is the proposed insert row, not a FROM source.
828impl<C> super::ExprSources for Excluded<C> {
829 type Sources = ();
830}
831
832impl<'a, V, C> Expr<'a, V> for Excluded<C>
833where
834 V: SQLParam + 'a,
835 V::DialectMarker: DialectSupports<feature::Excluded>,
836 C: Expr<'a, V> + SQLColumnInfo,
837{
838 type SQLType = C::SQLType;
839 type Nullable = C::Nullable;
840 type Aggregate = C::Aggregate;
841}
842
843impl<'a, V, C> ToSQL<'a, V> for Excluded<C>
844where
845 V: SQLParam + 'a,
846 V::DialectMarker: DialectSupports<feature::Excluded>,
847 C: SQLColumnInfo,
848{
849 fn to_sql(&self) -> SQL<'a, V> {
850 SQL::empty()
851 .push(Token::EXCLUDED)
852 .push(Token::DOT)
853 .append(SQL::ident(self.column.name()))
854 }
855}