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 left_sql = operand_sql(expr);
156 let mut values_iter = values.into_iter();
157
158 match values_iter.next() {
159 None => {
160 if negated {
161 left_sql.append(SQL::raw("NOT IN (SELECT NULL WHERE 1=0)"))
162 } else {
163 left_sql.append(SQL::raw("IN (SELECT NULL WHERE 1=0)"))
164 }
165 }
166 Some(first_value) => {
167 let mut result = left_sql;
168 if negated {
169 result = result.push(Token::NOT);
170 }
171
172 result = result
173 .push(Token::IN)
174 .push(Token::LPAREN)
175 .append(ComparisonOperand::into_comparison_sql(first_value));
176
177 for value in values_iter {
178 result = result
179 .push(Token::COMMA)
180 .append(ComparisonOperand::into_comparison_sql(value));
181 }
182 result.push(Token::RPAREN)
183 }
184 }
185}
186
187pub fn in_subquery<'a, V, L, S, M>(
199 lhs: L,
200 subquery: S,
201) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, L::Aggregate>
202where
203 V: SQLParam + 'a,
204 L: InSubqueryLhs<'a, V, M>,
205 S: Expr<'a, V>,
206 L::SQLType: Compatible<S::SQLType>,
207{
208 SQLExpr::new(
209 lhs.into_lhs_sql()
210 .push(Token::IN)
211 .append(subquery.into_sql().parens()),
212 )
213}
214
215pub fn not_in_subquery<'a, V, L, S, M>(
226 lhs: L,
227 subquery: S,
228) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, L::Aggregate>
229where
230 V: SQLParam + 'a,
231 L: InSubqueryLhs<'a, V, M>,
232 S: Expr<'a, V>,
233 L::SQLType: Compatible<S::SQLType>,
234{
235 SQLExpr::new(
236 lhs.into_lhs_sql()
237 .push(Token::NOT)
238 .push(Token::IN)
239 .append(subquery.into_sql().parens()),
240 )
241}
242
243pub fn exists<'a, V, S>(
251 subquery: S,
252) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, Scalar>
253where
254 V: SQLParam + 'a,
255 S: ToSQL<'a, V>,
256{
257 SQLExpr::new(
258 SQL::from_iter([Token::EXISTS, Token::LPAREN])
259 .append(subquery.into_sql())
260 .push(Token::RPAREN),
261 )
262}
263
264pub fn not_exists<'a, V, S>(
268 subquery: S,
269) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, Scalar>
270where
271 V: SQLParam + 'a,
272 S: ToSQL<'a, V>,
273{
274 SQLExpr::new(
275 SQL::from_iter([Token::NOT, Token::EXISTS, Token::LPAREN])
276 .append(subquery.into_sql())
277 .push(Token::RPAREN),
278 )
279}