1use crate::dialect::DialectTypes;
4use crate::sql::{SQL, Token};
5use crate::traits::{SQLParam, ToSQL};
6use crate::types::{Compatible, DataType};
7
8use super::{AggregateKind, ComparisonOperand, Expr, NonNull, SQLExpr, Scalar};
9
10#[inline]
11fn operand_sql<'a, V, T>(value: T) -> SQL<'a, V>
12where
13 V: SQLParam + 'a,
14 T: Expr<'a, V>,
15{
16 value.into_expr_sql()
17}
18
19#[doc(hidden)]
25pub enum Single {}
26
27#[doc(hidden)]
29pub enum Multi {}
30
31pub trait InSubqueryLhs<'a, V: SQLParam, M>: Sized {
36 type SQLType: DataType;
37 type Aggregate: AggregateKind;
38 fn into_lhs_sql(self) -> SQL<'a, V>;
39}
40
41impl<'a, V, E> InSubqueryLhs<'a, V, Single> for E
50where
51 V: SQLParam + 'a,
52 E: Expr<'a, V>,
53 E::SQLType: Compatible<E::SQLType>,
54{
55 type SQLType = E::SQLType;
56 type Aggregate = E::Aggregate;
57 fn into_lhs_sql(self) -> SQL<'a, V> {
58 self.into_expr_sql()
59 }
60}
61
62macro_rules! impl_in_subquery_lhs_tuple {
64 ($($E:ident),+; $($idx:tt),+) => {
65 impl<'a, V, $($E),+> InSubqueryLhs<'a, V, Multi> for ($($E,)+)
66 where
67 V: SQLParam + 'a,
68 $($E: Expr<'a, V>,)+
69 {
70 type SQLType = ($($E::SQLType,)+);
71 type Aggregate = Scalar;
72 fn into_lhs_sql(self) -> SQL<'a, V> {
73 ToSQL::into_sql(self).parens()
74 }
75 }
76 };
77}
78
79with_col_sizes_8!(impl_in_subquery_lhs_tuple);
80
81#[cfg(any(
82 feature = "col16",
83 feature = "col32",
84 feature = "col64",
85 feature = "col128",
86 feature = "col200"
87))]
88with_col_sizes_16!(impl_in_subquery_lhs_tuple);
89
90#[cfg(any(
91 feature = "col32",
92 feature = "col64",
93 feature = "col128",
94 feature = "col200"
95))]
96with_col_sizes_32!(impl_in_subquery_lhs_tuple);
97
98#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
99with_col_sizes_64!(impl_in_subquery_lhs_tuple);
100
101#[cfg(any(feature = "col128", feature = "col200"))]
102with_col_sizes_128!(impl_in_subquery_lhs_tuple);
103
104#[cfg(feature = "col200")]
105with_col_sizes_200!(impl_in_subquery_lhs_tuple);
106
107pub fn in_array<'a, V, E, I, R>(
116 expr: E,
117 values: I,
118) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, E::Aggregate>
119where
120 V: SQLParam + 'a,
121 E: Expr<'a, V>,
122 I: IntoIterator<Item = R>,
123 R: ComparisonOperand<'a, V, E>,
124 E::SQLType: Compatible<<R as ComparisonOperand<'a, V, E>>::SQLType>,
125{
126 SQLExpr::new(in_array_impl(expr, values, false))
127}
128
129pub fn not_in_array<'a, V, E, I, R>(
134 expr: E,
135 values: I,
136) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, E::Aggregate>
137where
138 V: SQLParam + 'a,
139 E: Expr<'a, V>,
140 I: IntoIterator<Item = R>,
141 R: ComparisonOperand<'a, V, E>,
142 E::SQLType: Compatible<<R as ComparisonOperand<'a, V, E>>::SQLType>,
143{
144 SQLExpr::new(in_array_impl(expr, values, true))
145}
146
147fn in_array_impl<'a, V, E, I, R>(expr: E, values: I, negated: bool) -> SQL<'a, V>
148where
149 V: SQLParam + 'a,
150 E: Expr<'a, V>,
151 I: IntoIterator<Item = R>,
152 R: ComparisonOperand<'a, V, E>,
153 E::SQLType: Compatible<<R as ComparisonOperand<'a, V, E>>::SQLType>,
154{
155 let mut values_iter = values.into_iter();
156
157 match values_iter.next() {
158 None => SQL::raw(if negated { "TRUE" } else { "FALSE" }),
159 Some(first_value) => {
160 let mut result = operand_sql(expr);
161 if negated {
162 result = result.push(Token::NOT);
163 }
164
165 result = result
166 .push(Token::IN)
167 .push(Token::LPAREN)
168 .append(ComparisonOperand::into_comparison_sql(first_value));
169
170 for value in values_iter {
171 result = result
172 .push(Token::COMMA)
173 .append(ComparisonOperand::into_comparison_sql(value));
174 }
175 result.push(Token::RPAREN)
176 }
177 }
178}
179
180pub fn in_subquery<'a, V, L, S, M>(
192 lhs: L,
193 subquery: S,
194) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, L::Aggregate>
195where
196 V: SQLParam + 'a,
197 L: InSubqueryLhs<'a, V, M>,
198 S: Expr<'a, V>,
199 L::SQLType: Compatible<S::SQLType>,
200{
201 SQLExpr::new(
202 lhs.into_lhs_sql()
203 .push(Token::IN)
204 .append(subquery.into_sql().parens()),
205 )
206}
207
208pub fn not_in_subquery<'a, V, L, S, M>(
219 lhs: L,
220 subquery: S,
221) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, L::Aggregate>
222where
223 V: SQLParam + 'a,
224 L: InSubqueryLhs<'a, V, M>,
225 S: Expr<'a, V>,
226 L::SQLType: Compatible<S::SQLType>,
227{
228 SQLExpr::new(
229 lhs.into_lhs_sql()
230 .push(Token::NOT)
231 .push(Token::IN)
232 .append(subquery.into_sql().parens()),
233 )
234}
235
236pub fn exists<'a, V, S>(
244 subquery: S,
245) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, Scalar>
246where
247 V: SQLParam + 'a,
248 S: ToSQL<'a, V>,
249{
250 SQLExpr::new(
251 SQL::from_iter([Token::EXISTS, Token::LPAREN])
252 .append(subquery.into_sql())
253 .push(Token::RPAREN),
254 )
255}
256
257pub fn not_exists<'a, V, S>(
261 subquery: S,
262) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, Scalar>
263where
264 V: SQLParam + 'a,
265 S: ToSQL<'a, V>,
266{
267 SQLExpr::new(
268 SQL::from_iter([Token::NOT, Token::EXISTS, Token::LPAREN])
269 .append(subquery.into_sql())
270 .push(Token::RPAREN),
271 )
272}