diesel/expression/
array_comparison.rs

1//! This module contains the query dsl node definitions
2//! for array comparison operations like `IN` and `NOT IN`
3
4use crate::backend::{sql_dialect, Backend, SqlDialect};
5use crate::expression::subselect::Subselect;
6use crate::expression::{
7    AppearsOnTable, AsExpression, Expression, SelectableExpression, TypedExpressionType,
8    ValidGrouping,
9};
10use crate::query_builder::combination_clause::CombinationClause;
11use crate::query_builder::{
12    AstPass, BoxedSelectStatement, QueryFragment, QueryId, SelectQuery, SelectStatement,
13};
14use crate::result::QueryResult;
15use crate::serialize::ToSql;
16use crate::sql_types::{self, HasSqlType, SingleValue, SqlType};
17use std::marker::PhantomData;
18
19/// Query dsl node that represents a `left IN (values)`
20/// expression
21///
22/// Third party backend can customize the [`QueryFragment`]
23/// implementation of this query dsl node via
24/// [`SqlDialect::ArrayComparison`]. A customized implementation
25/// is expected to provide the same semantics as an ANSI SQL
26/// `IN` expression.
27///
28/// The postgres backend provided a specialized implementation
29/// by using `left = ANY(values)` as optimized variant instead.
30#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)]
31#[non_exhaustive]
32pub struct In<T, U> {
33    /// The expression on the left side of the `IN` keyword
34    pub left: T,
35    /// The values clause of the `IN` expression
36    pub values: U,
37}
38
39/// Query dsl node that represents a `left NOT IN (values)`
40/// expression
41///
42/// Third party backend can customize the [`QueryFragment`]
43/// implementation of this query dsl node via
44/// [`SqlDialect::ArrayComparison`]. A customized implementation
45/// is expected to provide the same semantics as an ANSI SQL
46/// `NOT IN` expression.0
47///
48/// The postgres backend provided a specialized implementation
49/// by using `left = ALL(values)` as optimized variant instead.
50#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)]
51#[non_exhaustive]
52pub struct NotIn<T, U> {
53    /// The expression on the left side of the `NOT IN` keyword
54    pub left: T,
55    /// The values clause of the `NOT IN` expression
56    pub values: U,
57}
58
59impl<T, U> In<T, U> {
60    pub(crate) fn new(left: T, values: U) -> Self {
61        In { left, values }
62    }
63}
64
65impl<T, U> NotIn<T, U> {
66    pub(crate) fn new(left: T, values: U) -> Self {
67        NotIn { left, values }
68    }
69}
70
71impl<T, U> Expression for In<T, U>
72where
73    T: Expression,
74    U: Expression<SqlType = T::SqlType>,
75    T::SqlType: SqlType,
76    sql_types::is_nullable::IsSqlTypeNullable<T::SqlType>:
77        sql_types::MaybeNullableType<sql_types::Bool>,
78{
79    type SqlType = sql_types::is_nullable::MaybeNullable<
80        sql_types::is_nullable::IsSqlTypeNullable<T::SqlType>,
81        sql_types::Bool,
82    >;
83}
84
85impl<T, U> Expression for NotIn<T, U>
86where
87    T: Expression,
88    U: Expression<SqlType = T::SqlType>,
89    T::SqlType: SqlType,
90    sql_types::is_nullable::IsSqlTypeNullable<T::SqlType>:
91        sql_types::MaybeNullableType<sql_types::Bool>,
92{
93    type SqlType = sql_types::is_nullable::MaybeNullable<
94        sql_types::is_nullable::IsSqlTypeNullable<T::SqlType>,
95        sql_types::Bool,
96    >;
97}
98
99impl<T, U, DB> QueryFragment<DB> for In<T, U>
100where
101    DB: Backend,
102    Self: QueryFragment<DB, DB::ArrayComparison>,
103{
104    fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
105        <Self as QueryFragment<DB, DB::ArrayComparison>>::walk_ast(self, pass)
106    }
107}
108
109impl<T, U, DB> QueryFragment<DB, sql_dialect::array_comparison::AnsiSqlArrayComparison> for In<T, U>
110where
111    DB: Backend
112        + SqlDialect<ArrayComparison = sql_dialect::array_comparison::AnsiSqlArrayComparison>,
113    T: QueryFragment<DB>,
114    U: QueryFragment<DB> + MaybeEmpty,
115{
116    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
117        if self.values.is_empty() {
118            out.push_sql("1=0");
119        } else {
120            self.left.walk_ast(out.reborrow())?;
121            out.push_sql(" IN (");
122            self.values.walk_ast(out.reborrow())?;
123            out.push_sql(")");
124        }
125        Ok(())
126    }
127}
128
129impl<T, U, DB> QueryFragment<DB> for NotIn<T, U>
130where
131    DB: Backend,
132    Self: QueryFragment<DB, DB::ArrayComparison>,
133{
134    fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
135        <Self as QueryFragment<DB, DB::ArrayComparison>>::walk_ast(self, pass)
136    }
137}
138
139impl<T, U, DB> QueryFragment<DB, sql_dialect::array_comparison::AnsiSqlArrayComparison>
140    for NotIn<T, U>
141where
142    DB: Backend
143        + SqlDialect<ArrayComparison = sql_dialect::array_comparison::AnsiSqlArrayComparison>,
144    T: QueryFragment<DB>,
145    U: QueryFragment<DB> + MaybeEmpty,
146{
147    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
148        if self.values.is_empty() {
149            out.push_sql("1=1");
150        } else {
151            self.left.walk_ast(out.reborrow())?;
152            out.push_sql(" NOT IN (");
153            self.values.walk_ast(out.reborrow())?;
154            out.push_sql(")");
155        }
156        Ok(())
157    }
158}
159
160impl_selectable_expression!(In<T, U>);
161impl_selectable_expression!(NotIn<T, U>);
162
163/// This trait describes how a type is transformed to the
164/// `IN (values)` value expression
165///
166/// Diesel provided several implementations here:
167///
168///  - An implementation for any [`Iterator`] over values
169///    that implement [`AsExpression<ST>`] for the corresponding
170///    sql type ST. The corresponding values clause will contain
171///    bind statements for each individual value.
172///  - An implementation for select statements, that returns
173///    a single field. The corresponding values clause will contain
174///    the sub query.
175///
176///  This trait is exposed for custom third party backends so
177///  that they can restrict the [`QueryFragment`] implementations
178///  for [`In`] and [`NotIn`].
179pub trait AsInExpression<T: SqlType + TypedExpressionType> {
180    /// Type of the expression returned by [AsInExpression::as_in_expression]
181    type InExpression: MaybeEmpty + Expression<SqlType = T>;
182
183    /// Construct the diesel query dsl representation of
184    /// the `IN (values)` clause for the given type
185    #[allow(clippy::wrong_self_convention)]
186    // That's a public api, we cannot just change it to
187    // appease clippy
188    fn as_in_expression(self) -> Self::InExpression;
189}
190
191impl<I, T, ST> AsInExpression<ST> for I
192where
193    I: IntoIterator<Item = T>,
194    T: AsExpression<ST>,
195    ST: SqlType + TypedExpressionType,
196{
197    type InExpression = Many<ST, T>;
198
199    fn as_in_expression(self) -> Self::InExpression {
200        Many {
201            values: self.into_iter().collect(),
202            p: PhantomData,
203        }
204    }
205}
206
207/// A helper trait to check if the values clause of
208/// an [`In`] or [`NotIn`] query dsl node is empty or not
209pub trait MaybeEmpty {
210    /// Returns `true` if self represents an empty collection
211    /// Otherwise `false` is returned.
212    fn is_empty(&self) -> bool;
213}
214
215impl<ST, F, S, D, W, O, LOf, G, H, LC> AsInExpression<ST>
216    for SelectStatement<F, S, D, W, O, LOf, G, H, LC>
217where
218    ST: SqlType + TypedExpressionType,
219    Subselect<Self, ST>: Expression<SqlType = ST>,
220    Self: SelectQuery<SqlType = ST>,
221{
222    type InExpression = Subselect<Self, ST>;
223
224    fn as_in_expression(self) -> Self::InExpression {
225        Subselect::new(self)
226    }
227}
228
229impl<'a, ST, QS, DB, GB> AsInExpression<ST> for BoxedSelectStatement<'a, ST, QS, DB, GB>
230where
231    ST: SqlType + TypedExpressionType,
232    Subselect<BoxedSelectStatement<'a, ST, QS, DB, GB>, ST>: Expression<SqlType = ST>,
233{
234    type InExpression = Subselect<Self, ST>;
235
236    fn as_in_expression(self) -> Self::InExpression {
237        Subselect::new(self)
238    }
239}
240
241impl<ST, Combinator, Rule, Source, Rhs> AsInExpression<ST>
242    for CombinationClause<Combinator, Rule, Source, Rhs>
243where
244    ST: SqlType + TypedExpressionType,
245    Self: SelectQuery<SqlType = ST>,
246    Subselect<Self, ST>: Expression<SqlType = ST>,
247{
248    type InExpression = Subselect<Self, ST>;
249
250    fn as_in_expression(self) -> Self::InExpression {
251        Subselect::new(self)
252    }
253}
254
255/// Query dsl node for an `IN (values)` clause containing
256/// a variable number of bind values.
257///
258/// Third party backend can customize the [`QueryFragment`]
259/// implementation of this query dsl node via
260/// [`SqlDialect::ArrayComparison`]. The default
261/// implementation does generate one bind per value
262/// in the `values` field.
263///
264/// Diesel provides an optimized implementation for Postgresql
265/// like database systems that bind all values with one
266/// bind value of the type `Array<ST>` instead.
267#[derive(Debug, Clone)]
268pub struct Many<ST, I> {
269    /// The values contained in the `IN (values)` clause
270    pub values: Vec<I>,
271    p: PhantomData<ST>,
272}
273
274impl<ST, I, GB> ValidGrouping<GB> for Many<ST, I>
275where
276    ST: SingleValue,
277    I: AsExpression<ST>,
278    I::Expression: ValidGrouping<GB>,
279{
280    type IsAggregate = <I::Expression as ValidGrouping<GB>>::IsAggregate;
281}
282
283impl<ST, I> Expression for Many<ST, I>
284where
285    ST: TypedExpressionType,
286{
287    type SqlType = ST;
288}
289
290impl<ST, I> MaybeEmpty for Many<ST, I> {
291    fn is_empty(&self) -> bool {
292        self.values.is_empty()
293    }
294}
295
296impl<ST, I, QS> SelectableExpression<QS> for Many<ST, I>
297where
298    Many<ST, I>: AppearsOnTable<QS>,
299    ST: SingleValue,
300    I: AsExpression<ST>,
301    <I as AsExpression<ST>>::Expression: SelectableExpression<QS>,
302{
303}
304
305impl<ST, I, QS> AppearsOnTable<QS> for Many<ST, I>
306where
307    Many<ST, I>: Expression,
308    I: AsExpression<ST>,
309    ST: SingleValue,
310    <I as AsExpression<ST>>::Expression: SelectableExpression<QS>,
311{
312}
313
314impl<ST, I, DB> QueryFragment<DB> for Many<ST, I>
315where
316    Self: QueryFragment<DB, DB::ArrayComparison>,
317    DB: Backend,
318{
319    fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
320        <Self as QueryFragment<DB, DB::ArrayComparison>>::walk_ast(self, pass)
321    }
322}
323
324impl<ST, I, DB> QueryFragment<DB, sql_dialect::array_comparison::AnsiSqlArrayComparison>
325    for Many<ST, I>
326where
327    DB: Backend
328        + HasSqlType<ST>
329        + SqlDialect<ArrayComparison = sql_dialect::array_comparison::AnsiSqlArrayComparison>,
330    ST: SingleValue,
331    I: ToSql<ST, DB>,
332{
333    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
334        out.unsafe_to_cache_prepared();
335        let mut first = true;
336        for value in &self.values {
337            if first {
338                first = false;
339            } else {
340                out.push_sql(", ");
341            }
342            out.push_bind_param(value)?;
343        }
344        Ok(())
345    }
346}
347
348impl<ST, I> QueryId for Many<ST, I> {
349    type QueryId = ();
350
351    const HAS_STATIC_QUERY_ID: bool = false;
352}