1use crate::expression::{AppearsOnTable, Expression, SelectableExpression, ValidGrouping};
4use crate::query_source::aliasing::{AliasSource, FieldAliasMapper};
5use crate::result::QueryResult;
6use crate::{query_builder, query_source, sql_types};
7
8use std::marker::PhantomData;
9
10pub(crate) mod private {
11 use super::*;
12
13 #[derive(Debug, Clone, Copy, diesel::query_builder::QueryId, sql_types::DieselNumericOps)]
14 pub struct Cast<E, ST> {
15 pub(super) expr: E,
16 pub(super) sql_type: PhantomData<ST>,
17 }
18}
19pub(crate) use private::Cast;
20
21impl<E, ST> Cast<E, ST> {
22 pub(crate) fn new(expr: E) -> Self {
23 Self {
24 expr,
25 sql_type: PhantomData,
26 }
27 }
28}
29
30impl<E, ST, GroupByClause> ValidGrouping<GroupByClause> for Cast<E, ST>
31where
32 E: ValidGrouping<GroupByClause>,
33{
34 type IsAggregate = E::IsAggregate;
35}
36
37impl<E, ST, QS> SelectableExpression<QS> for Cast<E, ST>
38where
39 Cast<E, ST>: AppearsOnTable<QS>,
40 E: SelectableExpression<QS>,
41{
42}
43
44impl<E, ST, QS> AppearsOnTable<QS> for Cast<E, ST>
45where
46 Cast<E, ST>: Expression,
47 E: AppearsOnTable<QS>,
48{
49}
50
51impl<E, ST> Expression for Cast<E, ST>
52where
53 E: Expression,
54 ST: sql_types::SingleValue,
55{
56 type SqlType = ST;
57}
58
59impl<E, ST, DB> query_builder::QueryFragment<DB> for Cast<E, ST>
60where
61 E: query_builder::QueryFragment<DB>,
62 DB: diesel::backend::Backend,
63 ST: KnownCastSqlTypeName<DB>,
64{
65 fn walk_ast<'b>(&'b self, mut out: query_builder::AstPass<'_, 'b, DB>) -> QueryResult<()> {
66 out.push_sql("CAST(");
67 self.expr.walk_ast(out.reborrow())?;
68 out.push_sql(" AS ");
69 out.push_sql(ST::SQL_TYPE_NAME);
70 out.push_sql(")");
71 Ok(())
72 }
73}
74
75#[diagnostic::on_unimplemented(
80 note = "In order to use `CAST`, it is necessary that Diesel knows how to express the name \
81 of this type in the given backend.",
82 note = "If you run into this error message and believe that this cast should be supported \
83 open a PR that adds that trait implementation here: https://github.com/diesel-rs/diesel/blob/2fafe60a8f4ca3407dca5fe010a6092fa8a1858a/diesel/src/expression/cast.rs#L113."
84)]
85pub trait KnownCastSqlTypeName<DB> {
86 const SQL_TYPE_NAME: &'static str;
89}
90
91impl<ST, DB> KnownCastSqlTypeName<DB> for sql_types::Nullable<ST>
92where
93 ST: KnownCastSqlTypeName<DB>,
94{
95 const SQL_TYPE_NAME: &'static str = <ST as KnownCastSqlTypeName<DB>>::SQL_TYPE_NAME;
96}
97
98macro_rules! type_name {
99 ($($backend: ty: $backend_feature: literal { $($type: ident => $val: literal,)+ })*) => {
100 $(
101 $(
102 #[cfg(feature = $backend_feature)]
103 impl KnownCastSqlTypeName<$backend> for sql_types::$type {
104 const SQL_TYPE_NAME: &'static str = $val;
105 }
106 )*
107 )*
108 };
109}
110
111type_name! {
112 diesel::pg::Pg: "postgres_backend" {
113 Bool => "bool",
114 Int2 => "int2",
115 Int4 => "int4",
116 Int8 => "int8",
117 Float => "float4",
118 Double => "float8",
119 Numeric => "numeric",
120 Text => "text",
121 Date => "date",
122 Interval => "interval",
123 Time => "time",
124 Timestamp => "timestamp",
125 Uuid => "uuid",
126 Json => "json",
127 Jsonb => "jsonb",
128 }
129 diesel::mysql::Mysql: "mysql_backend" {
130 Int8 => "signed",
131 Text => "char",
132 Date => "date",
133 Datetime => "datetime",
134 Decimal => "decimal",
135 Time => "time",
136 }
137 diesel::sqlite::Sqlite: "sqlite" {
138 Int4 => "integer",
139 Int8 => "bigint",
140 Text => "text",
141 Json => "json",
142 Jsonb => "jsonb",
143 }
144}
145
146impl<S, E, ST> FieldAliasMapper<S> for Cast<E, ST>
147where
148 S: AliasSource,
149 E: FieldAliasMapper<S>,
150{
151 type Out = Cast<<E as FieldAliasMapper<S>>::Out, ST>;
152
153 fn map(self, alias: &query_source::Alias<S>) -> Self::Out {
154 Cast {
155 expr: self.expr.map(alias),
156 sql_type: self.sql_type,
157 }
158 }
159}
160
161pub trait FallibleCastsTo<ST> {}
163
164impl<ST1, ST2> FallibleCastsTo<sql_types::Nullable<ST2>> for sql_types::Nullable<ST1> where
165 ST1: CastsTo<ST2>
166{
167}
168
169pub trait CastsTo<ST>: FallibleCastsTo<ST> {}
172
173impl<ST1, ST2> CastsTo<sql_types::Nullable<ST2>> for sql_types::Nullable<ST1> where ST1: CastsTo<ST2>
174{}
175
176macro_rules! casts_impl {
177 (
178 $(
179 $($feature: literal : )? ($to: tt <- $from: tt),
180 )+
181 ) => {
182 $(
183 $(#[cfg(feature = $feature)])?
184 impl FallibleCastsTo<sql_types::$to> for sql_types::$from {}
185 $(#[cfg(feature = $feature)])?
186 impl CastsTo<sql_types::$to> for sql_types::$from {}
187 )+
188 };
189}
190
191casts_impl!(
192 (Bool <- Int4),
193 (Float4 <- Int4),
194 (Float4 <- Int8),
195 (Float8 <- Float4),
196 (Float8 <- Int4),
197 (Float8 <- Int8),
198 (Int8 <- Int4),
199 (Int8 <- Float4),
200 (Int8 <- Float8),
201 (Int4 <- Bool),
202 (Int4 <- Float4),
203 (Text <- Bool),
204 (Text <- Float4),
205 (Text <- Float8),
206 (Text <- Int4),
207 (Text <- Int8),
208 (Text <- Date),
209 (Text <- Json),
210 (Text <- Jsonb),
211 (Text <- Time),
212 (Json <- Jsonb),
213 (Jsonb <- Json),
214 "mysql_backend": (Text <- Datetime),
215 "postgres_backend": (Text <- Uuid),
216);
217
218macro_rules! fallible_casts_impl {
219 (
220 $(
221 $($feature: literal : )? ($to: tt <- $from: tt),
222 )+
223 ) => {
224 $(
225 $(#[cfg(feature = $feature)])?
226 impl FallibleCastsTo<sql_types::$to> for sql_types::$from {}
227 )+
228 };
229}
230
231fallible_casts_impl!(
232 (Int4 <- Int8),
233 (Int4 <- Float8),
234 (Int4 <- Text),
235 (Int8 <- Text),
236 (Float4 <- Float8),
237 (Float4 <- Text),
238 (Float8 <- Text),
239 (Json <- Text),
240 (Jsonb <- Text),
241 (Bool <- Text),
242 (Date <- Text),
243 (Time <- Text),
244 "mysql_backend": (Datetime <- Text),
245 "postgres_backend": (Uuid <- Text),
246);