1use crate::sql::{SQL, Token};
12use crate::traits::SQLParam;
13use crate::types::Compatible;
14use crate::{MySQLDialect, PostgresDialect};
15
16use super::{AggOr, AggregateKind, Expr, NonNull, Null, Nullability, SQLExpr};
17
18pub trait NullOr<Rhs: Nullability>: Nullability {
36 type Output: Nullability;
38}
39
40impl NullOr<Self> for NonNull {
41 type Output = Self;
42}
43impl NullOr<Null> for NonNull {
44 type Output = Null;
45}
46impl NullOr<NonNull> for Null {
47 type Output = Self;
48}
49impl NullOr<Self> for Null {
50 type Output = Self;
51}
52
53pub trait NullAnd<Rhs: Nullability>: Nullability {
57 type Output: Nullability;
59}
60
61impl NullAnd<Self> for NonNull {
62 type Output = Self;
63}
64impl NullAnd<Null> for NonNull {
65 type Output = Self;
66}
67impl NullAnd<NonNull> for Null {
68 type Output = NonNull;
69}
70impl NullAnd<Self> for Null {
71 type Output = Self;
72}
73
74#[allow(clippy::type_complexity)]
97pub fn coalesce<'a, V, E, D, N>(
98 expr: E,
99 default: D,
100) -> SQLExpr<'a, V, E::SQLType, N, <E::Aggregate as AggOr<D::Aggregate>>::Output>
101where
102 V: SQLParam + 'a,
103 E: Expr<'a, V>,
104 D: Expr<'a, V>,
105 N: Nullability,
106 E::SQLType: Compatible<D::SQLType>,
107 E::Nullable: NullAnd<D::Nullable, Output = N>,
108 D::Nullable: Nullability,
109 E::Aggregate: AggOr<D::Aggregate>,
110 D::Aggregate: AggregateKind,
111{
112 SQLExpr::new(SQL::func(
113 "COALESCE",
114 expr.into_expr_sql()
115 .push(Token::COMMA)
116 .append(default.into_expr_sql()),
117 ))
118}
119
120#[allow(clippy::type_complexity)]
136pub fn coalesce_many<'a, V, E, I, N>(
137 first: E,
138 rest: I,
139) -> SQLExpr<
140 'a,
141 V,
142 E::SQLType,
143 N,
144 <E::Aggregate as AggOr<<I::Item as Expr<'a, V>>::Aggregate>>::Output,
145>
146where
147 V: SQLParam + 'a,
148 E: Expr<'a, V>,
149 N: Nullability,
150 I: IntoIterator,
151 I::Item: Expr<'a, V>,
152 E::SQLType: Compatible<<I::Item as Expr<'a, V>>::SQLType>,
153 E::Nullable: NullAnd<<I::Item as Expr<'a, V>>::Nullable, Output = N>,
154 <I::Item as Expr<'a, V>>::Nullable: Nullability,
155 E::Aggregate: AggOr<<I::Item as Expr<'a, V>>::Aggregate>,
156 <I::Item as Expr<'a, V>>::Aggregate: AggregateKind,
157{
158 let mut sql = first.into_expr_sql();
159 for value in rest {
160 sql = sql.push(Token::COMMA).append(value.into_expr_sql());
161 }
162 SQLExpr::new(SQL::func("COALESCE", sql))
163}
164
165#[allow(clippy::type_complexity)]
185pub fn nullif<'a, V, E1, E2>(
186 expr1: E1,
187 expr2: E2,
188) -> SQLExpr<'a, V, E1::SQLType, Null, <E1::Aggregate as AggOr<E2::Aggregate>>::Output>
189where
190 V: SQLParam + 'a,
191 E1: Expr<'a, V>,
192 E2: Expr<'a, V>,
193 E1::SQLType: Compatible<E2::SQLType>,
194 E1::Aggregate: AggOr<E2::Aggregate>,
195 E2::Aggregate: AggregateKind,
196{
197 SQLExpr::new(SQL::func(
198 "NULLIF",
199 expr1
200 .into_expr_sql()
201 .push(Token::COMMA)
202 .append(expr2.into_expr_sql()),
203 ))
204}
205
206#[allow(clippy::type_complexity)]
215pub fn ifnull<'a, V, E, D, N>(
216 expr: E,
217 default: D,
218) -> SQLExpr<'a, V, E::SQLType, N, <E::Aggregate as AggOr<D::Aggregate>>::Output>
219where
220 V: SQLParam + 'a,
221 E: Expr<'a, V>,
222 D: Expr<'a, V>,
223 N: Nullability,
224 E::SQLType: Compatible<D::SQLType>,
225 E::Nullable: NullAnd<D::Nullable, Output = N>,
226 D::Nullable: Nullability,
227 E::Aggregate: AggOr<D::Aggregate>,
228 D::Aggregate: AggregateKind,
229{
230 SQLExpr::new(SQL::func(
231 "IFNULL",
232 expr.into_expr_sql()
233 .push(Token::COMMA)
234 .append(default.into_expr_sql()),
235 ))
236}
237
238#[diagnostic::on_unimplemented(
247 message = "GREATEST/LEAST are not available for this dialect",
248 label = "use a dialect-specific extrema expression"
249)]
250pub trait GreatestLeastPolicy<L: Nullability, R: Nullability> {
251 type Nullable: Nullability;
252}
253
254impl<L, R> GreatestLeastPolicy<L, R> for PostgresDialect
255where
256 L: NullAnd<R>,
257 R: Nullability,
258{
259 type Nullable = <L as NullAnd<R>>::Output;
260}
261
262impl<L, R> GreatestLeastPolicy<L, R> for MySQLDialect
263where
264 L: NullOr<R>,
265 R: Nullability,
266{
267 type Nullable = <L as NullOr<R>>::Output;
268}
269
270#[allow(clippy::type_complexity)]
287pub fn greatest<'a, V, L, R>(
288 left: L,
289 right: R,
290) -> SQLExpr<
291 'a,
292 V,
293 L::SQLType,
294 <V::DialectMarker as GreatestLeastPolicy<L::Nullable, R::Nullable>>::Nullable,
295 <L::Aggregate as AggOr<R::Aggregate>>::Output,
296>
297where
298 V: SQLParam + 'a,
299 V::DialectMarker: GreatestLeastPolicy<L::Nullable, R::Nullable>,
300 L: Expr<'a, V>,
301 R: Expr<'a, V>,
302 L::SQLType: Compatible<R::SQLType>,
303 R::Nullable: Nullability,
304 L::Aggregate: AggOr<R::Aggregate>,
305 R::Aggregate: AggregateKind,
306{
307 SQLExpr::new(SQL::func(
308 "GREATEST",
309 left.into_expr_sql()
310 .push(Token::COMMA)
311 .append(right.into_expr_sql()),
312 ))
313}
314
315#[allow(clippy::type_complexity)]
332pub fn least<'a, V, L, R>(
333 left: L,
334 right: R,
335) -> SQLExpr<
336 'a,
337 V,
338 L::SQLType,
339 <V::DialectMarker as GreatestLeastPolicy<L::Nullable, R::Nullable>>::Nullable,
340 <L::Aggregate as AggOr<R::Aggregate>>::Output,
341>
342where
343 V: SQLParam + 'a,
344 V::DialectMarker: GreatestLeastPolicy<L::Nullable, R::Nullable>,
345 L: Expr<'a, V>,
346 R: Expr<'a, V>,
347 L::SQLType: Compatible<R::SQLType>,
348 R::Nullable: Nullability,
349 L::Aggregate: AggOr<R::Aggregate>,
350 R::Aggregate: AggregateKind,
351{
352 SQLExpr::new(SQL::func(
353 "LEAST",
354 left.into_expr_sql()
355 .push(Token::COMMA)
356 .append(right.into_expr_sql()),
357 ))
358}