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