1use crate::dialect::DialectTypes;
4use crate::sql::{SQL, Token};
5use crate::traits::{SQLParam, ToSQL};
6use crate::types::{Compatible, DataType};
7
8use super::{AggregateKind, 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: ToSQL<'a, V>,
15{
16 value.into_sql().parens_if_subquery()
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
43where
44 V: SQLParam + 'a,
45 E: Expr<'a, V>,
46{
47 type SQLType = E::SQLType;
48 type Aggregate = E::Aggregate;
49 fn into_lhs_sql(self) -> SQL<'a, V> {
50 self.into_sql().parens_if_subquery()
51 }
52}
53
54macro_rules! impl_in_subquery_lhs_tuple {
56 ($($E:ident),+; $($idx:tt),+) => {
57 impl<'a, V, $($E),+> InSubqueryLhs<'a, V, Multi> for ($($E,)+)
58 where
59 V: SQLParam + 'a,
60 $($E: Expr<'a, V>,)+
61 {
62 type SQLType = ($($E::SQLType,)+);
63 type Aggregate = Scalar;
64 fn into_lhs_sql(self) -> SQL<'a, V> {
65 ToSQL::into_sql(self).parens()
66 }
67 }
68 };
69}
70
71with_col_sizes_8!(impl_in_subquery_lhs_tuple);
72
73#[cfg(any(
74 feature = "col16",
75 feature = "col32",
76 feature = "col64",
77 feature = "col128",
78 feature = "col200"
79))]
80with_col_sizes_16!(impl_in_subquery_lhs_tuple);
81
82#[cfg(any(
83 feature = "col32",
84 feature = "col64",
85 feature = "col128",
86 feature = "col200"
87))]
88with_col_sizes_32!(impl_in_subquery_lhs_tuple);
89
90#[cfg(any(feature = "col64", feature = "col128", feature = "col200"))]
91with_col_sizes_64!(impl_in_subquery_lhs_tuple);
92
93#[cfg(any(feature = "col128", feature = "col200"))]
94with_col_sizes_128!(impl_in_subquery_lhs_tuple);
95
96#[cfg(feature = "col200")]
97with_col_sizes_200!(impl_in_subquery_lhs_tuple);
98
99pub fn in_array<'a, V, E, I, R>(
108 expr: E,
109 values: I,
110) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, E::Aggregate>
111where
112 V: SQLParam + 'a,
113 E: Expr<'a, V>,
114 I: IntoIterator<Item = R>,
115 R: Expr<'a, V>,
116 E::SQLType: Compatible<R::SQLType>,
117{
118 SQLExpr::new(in_array_impl(expr, values, false))
119}
120
121pub fn not_in_array<'a, V, E, I, R>(
126 expr: E,
127 values: I,
128) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, E::Aggregate>
129where
130 V: SQLParam + 'a,
131 E: Expr<'a, V>,
132 I: IntoIterator<Item = R>,
133 R: Expr<'a, V>,
134 E::SQLType: Compatible<R::SQLType>,
135{
136 SQLExpr::new(in_array_impl(expr, values, true))
137}
138
139fn in_array_impl<'a, V, E, I, R>(expr: E, values: I, negated: bool) -> SQL<'a, V>
140where
141 V: SQLParam + 'a,
142 E: Expr<'a, V>,
143 I: IntoIterator<Item = R>,
144 R: Expr<'a, V>,
145 E::SQLType: Compatible<R::SQLType>,
146{
147 let left_sql = operand_sql(expr);
148 let mut values_iter = values.into_iter();
149
150 match values_iter.next() {
151 None => {
152 if negated {
153 left_sql.append(SQL::raw("NOT IN (SELECT NULL WHERE 1=0)"))
154 } else {
155 left_sql.append(SQL::raw("IN (SELECT NULL WHERE 1=0)"))
156 }
157 }
158 Some(first_value) => {
159 let mut result = left_sql;
160 if negated {
161 result = result.push(Token::NOT);
162 }
163
164 result = result
165 .push(Token::IN)
166 .push(Token::LPAREN)
167 .append(operand_sql(first_value));
168
169 for value in values_iter {
170 result = result.push(Token::COMMA).append(operand_sql(value));
171 }
172 result.push(Token::RPAREN)
173 }
174 }
175}
176
177pub fn in_subquery<'a, V, L, S, M>(
189 lhs: L,
190 subquery: S,
191) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, L::Aggregate>
192where
193 V: SQLParam + 'a,
194 L: InSubqueryLhs<'a, V, M>,
195 S: Expr<'a, V>,
196 L::SQLType: Compatible<S::SQLType>,
197{
198 SQLExpr::new(
199 lhs.into_lhs_sql()
200 .push(Token::IN)
201 .append(subquery.into_sql().parens()),
202 )
203}
204
205pub fn not_in_subquery<'a, V, L, S, M>(
216 lhs: L,
217 subquery: S,
218) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, L::Aggregate>
219where
220 V: SQLParam + 'a,
221 L: InSubqueryLhs<'a, V, M>,
222 S: Expr<'a, V>,
223 L::SQLType: Compatible<S::SQLType>,
224{
225 SQLExpr::new(
226 lhs.into_lhs_sql()
227 .push(Token::NOT)
228 .push(Token::IN)
229 .append(subquery.into_sql().parens()),
230 )
231}
232
233pub fn exists<'a, V, S>(
241 subquery: S,
242) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, Scalar>
243where
244 V: SQLParam + 'a,
245 S: ToSQL<'a, V>,
246{
247 SQLExpr::new(
248 SQL::from_iter([Token::EXISTS, Token::LPAREN])
249 .append(subquery.into_sql())
250 .push(Token::RPAREN),
251 )
252}
253
254pub fn not_exists<'a, V, S>(
258 subquery: S,
259) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Bool, NonNull, Scalar>
260where
261 V: SQLParam + 'a,
262 S: ToSQL<'a, V>,
263{
264 SQLExpr::new(
265 SQL::from_iter([Token::NOT, Token::EXISTS, Token::LPAREN])
266 .append(subquery.into_sql())
267 .push(Token::RPAREN),
268 )
269}