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.
475pub trait CastTarget<'a, T: DataType, D> {
476 /// The SQL type name to cast to.
477 fn cast_type_name(self) -> &'a str;
478}
479
480/// Casts from `Source` to `Target` that dialect `D` allows.
481///
482/// On SQLite and PostgreSQL the two types must be compatible. On MySQL,
483/// `CAST` accepts a fixed set of targets (`SIGNED`, `DOUBLE`, `CHAR`,
484/// `DATE`, ...).
485#[diagnostic::on_unimplemented(
486 message = "cannot cast `{Source}` to `{Target}` for this dialect",
487 label = "cast target is incompatible with source type",
488 note = "use a supported target marker, or raw SQL when the conversion is intentionally dialect-specific"
489)]
490pub trait CastTypePolicy<D, Source: DataType, Target: DataType> {}
491
492/// Nullability of a cast to `Self` on dialect `D`. MySQL temporal casts give
493/// NULL for invalid input, so they are nullable.
494#[doc(hidden)]
495pub trait CastNullabilityPolicy<D, Input: Nullability>: DataType {
496 type Output: Nullability;
497}
498
499macro_rules! mysql_cast_policy {
500 (
501 preserving: [$($preserving:ty),+ $(,)?],
502 nullable: [$($nullable:ty),+ $(,)?],
503 ) => {
504 $(
505 impl<Source: DataType> CastTypePolicy<MySQLDialect, Source, $preserving> for () {}
506
507 impl<Input: Nullability> CastNullabilityPolicy<MySQLDialect, Input> for $preserving {
508 type Output = Input;
509 }
510 )+
511 $(
512 impl<Source: DataType> CastTypePolicy<MySQLDialect, Source, $nullable> for () {}
513
514 impl<Input: Nullability> CastNullabilityPolicy<MySQLDialect, Input> for $nullable {
515 type Output = Null;
516 }
517 )+
518 };
519}
520
521mysql_cast_policy! {
522 preserving: [
523 drizzle_types::mysql::types::BigInt,
524 drizzle_types::mysql::types::BigIntUnsigned,
525 drizzle_types::mysql::types::Float,
526 drizzle_types::mysql::types::Double,
527 drizzle_types::mysql::types::Decimal,
528 drizzle_types::mysql::types::Varchar,
529 drizzle_types::mysql::types::Varbinary,
530 drizzle_types::mysql::types::Json,
531 ],
532 nullable: [
533 drizzle_types::mysql::types::Date,
534 drizzle_types::mysql::types::Time,
535 drizzle_types::mysql::types::DateTime,
536 drizzle_types::mysql::types::Year,
537 ],
538}
539
540impl<Source: DataType + Compatible<Target>, Target: DataType>
541 CastTypePolicy<PostgresDialect, Source, Target> for ()
542{
543}
544
545impl<Input: Nullability, Target: DataType> CastNullabilityPolicy<PostgresDialect, Input>
546 for Target
547{
548 type Output = Input;
549}
550
551impl<Source: DataType + Compatible<Target>, Target: DataType>
552 CastTypePolicy<SQLiteDialect, Source, Target> for ()
553{
554}
555
556impl<Input: Nullability, Target: DataType> CastNullabilityPolicy<SQLiteDialect, Input> for Target {
557 type Output = Input;
558}
559
560impl<'a, T: DataType, D> CastTarget<'a, T, D> for &'a str {
561 fn cast_type_name(self) -> &'a str {
562 self
563 }
564}
565
566impl<'a, T, D> CastTarget<'a, T, D> for T
567where
568 T: DataType + DefaultCastTypeName,
569{
570 fn cast_type_name(self) -> &'a str {
571 T::CAST_TYPE_NAME
572 }
573}
574
575/// Converts an expression to another SQL type (`CAST(expr AS type)`).
576///
577/// The target is either a type marker value, such as
578/// `drizzle::sqlite::types::Text`, whose SQL name is used, or a SQL type
579/// name such as `"VARCHAR(255)"`. With a name, give the result type
580/// explicitly: `cast::<_, _, Text>(expr, "VARCHAR(255)")`.
581///
582/// On SQLite and PostgreSQL, source and target types must be compatible (see
583/// [`CastTypePolicy`]). The result has the target type and keeps the
584/// expression's aggregate kind. It keeps the expression's nullability,
585/// except for MySQL casts to temporal types, which can give NULL for invalid
586/// input and so are nullable.
587///
588/// # Examples
589///
590/// ```rust
591/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
592/// # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
593/// # #[derive(Clone, Debug)] struct Value(String);
594/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
595/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
596/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
597/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
598/// # 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))))) }
599/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
600/// # 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> }
601/// # 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") };
602/// // Integer to SQLite REAL, using the type marker.
603/// let real = cast(users.age, Real::default());
604/// assert_eq!(real.sql(), r#"CAST ("users"."age" AS REAL)"#);
605///
606/// // The same with an explicit type name.
607/// let real = cast::<_, _, Real>(users.age, "DOUBLE");
608/// assert_eq!(real.sql(), r#"CAST ("users"."age" AS DOUBLE)"#);
609/// ```
610#[allow(clippy::type_complexity)]
611pub fn cast<'a, V, E, Target>(
612 expr: E,
613 target_type: impl CastTarget<'a, Target, V::DialectMarker>,
614) -> SQLExpr<
615 'a,
616 V,
617 Target,
618 <Target as CastNullabilityPolicy<V::DialectMarker, E::Nullable>>::Output,
619 E::Aggregate,
620 E::Sources,
621>
622where
623 V: SQLParam + 'a,
624 E: Expr<'a, V>,
625 Target: DataType + CastNullabilityPolicy<V::DialectMarker, E::Nullable>,
626 (): CastTypePolicy<V::DialectMarker, E::SQLType, Target>,
627{
628 SQLExpr::new(SQL::func(
629 "CAST",
630 expr.into_expr_sql()
631 .push(Token::AS)
632 .append(SQL::raw(target_type.cast_type_name())),
633 ))
634}
635
636// =============================================================================
637// STRING CONCATENATION
638// =============================================================================
639
640/// Joins two text values; same as [`concat`](super::concat).
641///
642/// Renders `left || right` on SQLite and PostgreSQL and `CONCAT(left, right)`
643/// on MySQL. Both arguments must be text. The result is text, nullable if
644/// either argument is.
645///
646/// # Examples
647///
648/// ```rust
649/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
650/// # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
651/// # #[derive(Clone, Debug)] struct Value(String);
652/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
653/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
654/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
655/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
656/// # 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))))) }
657/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
658/// # 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> }
659/// # 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") };
660/// let label = string_concat(users.name, "!");
661/// assert_eq!(label.sql(), r#""users"."name" || ?"#);
662/// ```
663#[allow(clippy::type_complexity)]
664pub fn string_concat<'a, V, L, R>(
665 left: L,
666 right: R,
667) -> SQLExpr<
668 'a,
669 V,
670 <V::DialectMarker as crate::dialect::DialectTypes>::Text,
671 <L::Nullable as Nullability>::Or<R::Nullable>,
672 <L::Aggregate as AggregateKind>::Or<R::Aggregate>,
673 (L::Sources, R::Sources),
674>
675where
676 V: SQLParam + 'a,
677 L: Expr<'a, V>,
678 R: Expr<'a, V>,
679 L::SQLType: Textual,
680 R::SQLType: Textual,
681 R::Nullable: Nullability,
682 R::Aggregate: AggregateKind,
683{
684 super::concat(left, right)
685}
686
687// =============================================================================
688// RAW SQL Expression
689// =============================================================================
690
691/// A raw SQL fragment with a declared SQL type, typed as nullable.
692///
693/// The text is inserted into the query as is, so never build it from user
694/// input. The type system trusts the declared type `T` and cannot check the
695/// SQL. The result is nullable; use [`raw_non_null`] when the SQL can never be
696/// NULL.
697///
698/// # Examples
699///
700/// ```rust
701/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
702/// # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
703/// # #[derive(Clone, Debug)] struct Value(String);
704/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
705/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
706/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
707/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
708/// # 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))))) }
709/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
710/// # 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> }
711/// # 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") };
712/// let one = raw::<Value, Int>("1");
713/// assert_eq!(eq(users.id, one).sql(), r#""users"."id" = 1"#);
714/// ```
715#[must_use]
716pub fn raw<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, Null, Scalar, ()>
717where
718 V: SQLParam + 'a,
719 T: DataType,
720{
721 SQLExpr::new(SQL::raw(sql))
722}
723
724/// A raw SQL fragment with a declared SQL type, typed as nullable; same as
725/// [`raw`].
726#[must_use]
727pub fn raw_nullable<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, Null, Scalar, ()>
728where
729 V: SQLParam + 'a,
730 T: DataType,
731{
732 SQLExpr::new(SQL::raw(sql))
733}
734
735/// A raw SQL fragment with a declared SQL type, typed as non-null.
736///
737/// Like [`raw`], but the result is non-null. The type system trusts both the
738/// declared type `T` and the claim that the SQL is never NULL.
739///
740/// # Examples
741///
742/// ```rust
743/// # use drizzle_core::dialect::{Dialect, DialectTypes, SQLiteDialect as D};
744/// # use drizzle_core::{ColumnRef, SQL, SQLParam, expr::*};
745/// # #[derive(Clone, Debug)] struct Value(String);
746/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = D; }
747/// # impl<X: ToString> From<X> for Value { fn from(v: X) -> Self { Value(v.to_string()) } }
748/// # impl From<Value> for std::borrow::Cow<'_, Value> { fn from(v: Value) -> Self { Self::Owned(v) } }
749/// # type C<X, N = NonNull> = &'static SQLExpr<'static, Value, X, N>;
750/// # 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))))) }
751/// # type Int = <D as DialectTypes>::Int; type Text = <D as DialectTypes>::Text; type Real = <D as DialectTypes>::Double;
752/// # 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> }
753/// # 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") };
754/// let now = raw_non_null::<Value, Text>("CURRENT_TIMESTAMP");
755/// assert_eq!(now.sql(), "CURRENT_TIMESTAMP");
756/// ```
757#[must_use]
758pub fn raw_non_null<'a, V, T>(sql: &'a str) -> SQLExpr<'a, V, T, NonNull, Scalar, ()>
759where
760 V: SQLParam + 'a,
761 T: DataType,
762{
763 SQLExpr::new(SQL::raw(sql))
764}
765
766// =============================================================================
767// EXCLUDED (for ON CONFLICT DO UPDATE)
768// =============================================================================
769
770/// A column of the row that failed to insert (`EXCLUDED."column"`); created
771/// by [`excluded`].
772#[derive(Clone, Copy, Debug)]
773pub struct Excluded<C> {
774 column: C,
775}
776
777impl DialectSupports<feature::Excluded> for SQLiteDialect {}
778impl DialectSupports<feature::Excluded> for PostgresDialect {}
779
780/// Refers to the value a conflicting insert tried to write (`EXCLUDED."column"`).
781///
782/// Use it in `ON CONFLICT ... DO UPDATE SET` to copy the new value into the
783/// existing row. The argument must be a table column. The result has the
784/// column's SQL type and nullability. Available on SQLite and PostgreSQL.
785///
786/// # Examples
787///
788/// ```rust
789/// # use drizzle_core::dialect::{Dialect, SQLiteDialect};
790/// # use drizzle_core::{SQL, SQLColumnInfo, SQLParam, SQLTableInfo, ToSQL, expr::excluded};
791/// # #[derive(Clone, Debug)] struct Value;
792/// # impl SQLParam for Value { const DIALECT: Dialect = Dialect::SQLite; type DialectMarker = SQLiteDialect; }
793/// # struct NameColumn;
794/// # impl SQLColumnInfo for NameColumn {
795/// # fn name(&self) -> &'static str { "name" }
796/// # fn is_not_null(&self) -> bool { true }
797/// # fn is_primary_key(&self) -> bool { false }
798/// # fn is_unique(&self) -> bool { false }
799/// # fn r#type(&self) -> &'static str { "TEXT" }
800/// # fn has_default(&self) -> bool { false }
801/// # fn table(&self) -> &'static dyn SQLTableInfo { unimplemented!() }
802/// # }
803/// // `NameColumn` stands in for a generated column such as `users.name`.
804/// let new_name: SQL<'_, Value> = excluded(NameColumn).to_sql();
805/// assert_eq!(new_name.sql(), r#"EXCLUDED."name""#);
806/// ```
807///
808/// In an upsert built with a dialect crate:
809///
810/// ```text
811/// // INSERT INTO "simple" ("id", "name") VALUES (?, ?)
812/// // ON CONFLICT ("id") DO UPDATE SET "name" = EXCLUDED."name"
813/// db.insert(simple)
814/// .values([InsertSimple::new("test").with_id(1)])
815/// .on_conflict(simple.id)
816/// .do_update(UpdateSimple::default().with_name(excluded(simple.name)));
817/// ```
818pub const fn excluded<C>(column: C) -> Excluded<C> {
819 Excluded { column }
820}
821
822/// `EXCLUDED` is the proposed insert row, not a FROM source.
823impl<C> super::ExprSources for Excluded<C> {
824 type Sources = ();
825}
826
827impl<'a, V, C> Expr<'a, V> for Excluded<C>
828where
829 V: SQLParam + 'a,
830 V::DialectMarker: DialectSupports<feature::Excluded>,
831 C: Expr<'a, V> + SQLColumnInfo,
832{
833 type SQLType = C::SQLType;
834 type Nullable = C::Nullable;
835 type Aggregate = C::Aggregate;
836}
837
838impl<'a, V, C> ToSQL<'a, V> for Excluded<C>
839where
840 V: SQLParam + 'a,
841 V::DialectMarker: DialectSupports<feature::Excluded>,
842 C: SQLColumnInfo,
843{
844 fn to_sql(&self) -> SQL<'a, V> {
845 SQL::empty()
846 .push(Token::EXCLUDED)
847 .push(Token::DOT)
848 .append(SQL::ident(self.column.name()))
849 }
850}