diesel/query_builder/
combination_clause.rs

1//! Combine queries using a combinator like `UNION`, `INTERSECT` or `EXPECT`
2//! with or without `ALL` rule for duplicates
3//!
4//! Within this module, types commonly use the following abbreviations:
5//!
6//! O: Order By Clause
7//! L: Limit Clause
8//! Of: Offset Clause
9//! LOf: Limit Offset Clause
10
11use crate::backend::{Backend, DieselReserveSpecialization};
12use crate::dsl::AsExprOf;
13use crate::expression::subselect::ValidSubselect;
14use crate::expression::IntoSql;
15use crate::expression::NonAggregate;
16use crate::query_builder::insert_statement::InsertFromSelect;
17use crate::query_builder::limit_clause::{LimitClause, NoLimitClause};
18use crate::query_builder::limit_offset_clause::LimitOffsetClause;
19use crate::query_builder::offset_clause::{NoOffsetClause, OffsetClause};
20use crate::query_builder::order_clause::{NoOrderClause, OrderClause};
21use crate::query_builder::{AsQuery, AstPass, Query, QueryFragment, QueryId, SelectQuery};
22use crate::query_dsl::methods::*;
23use crate::query_dsl::positional_order_dsl::{IntoPositionalOrderExpr, PositionalOrderDsl};
24use crate::sql_types::BigInt;
25use crate::{CombineDsl, Insertable, QueryDsl, QueryResult, RunQueryDsl, Table};
26
27#[derive(Debug, Copy, Clone, QueryId)]
28#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."]
29/// Combine queries using a combinator like `UNION`, `INTERSECT` or `EXPECT`
30/// with or without `ALL` rule for duplicates
31pub struct CombinationClause<
32    Combinator,
33    Rule,
34    Source,
35    Rhs,
36    Order = NoOrderClause,
37    LimitOffset = LimitOffsetClause<NoLimitClause, NoOffsetClause>,
38> {
39    combinator: Combinator,
40    duplicate_rule: Rule,
41    source: ParenthesisWrapper<Source>,
42    rhs: ParenthesisWrapper<Rhs>,
43    /// The order clause of the query
44    order: Order,
45    /// The combined limit/offset clause of the query
46    limit_offset: LimitOffset,
47}
48
49impl<Combinator, Rule, Source, Rhs> CombinationClause<Combinator, Rule, Source, Rhs> {
50    /// Create a new combination
51    pub(crate) fn new(
52        combinator: Combinator,
53        duplicate_rule: Rule,
54        source: Source,
55        rhs: Rhs,
56    ) -> Self {
57        CombinationClause {
58            combinator,
59            duplicate_rule,
60            source: ParenthesisWrapper { inner: source },
61            rhs: ParenthesisWrapper { inner: rhs },
62            order: NoOrderClause,
63            limit_offset: LimitOffsetClause {
64                limit_clause: NoLimitClause,
65                offset_clause: NoOffsetClause,
66            },
67        }
68    }
69}
70
71impl<Combinator, Rule, Source, Rhs, O, LOf> QueryDsl
72    for CombinationClause<Combinator, Rule, Source, Rhs, O, LOf>
73{
74}
75
76impl<Combinator, Rule, Source, Rhs, O, LOf> Query
77    for CombinationClause<Combinator, Rule, Source, Rhs, O, LOf>
78where
79    Source: Query,
80    Rhs: Query<SqlType = Source::SqlType>,
81{
82    type SqlType = Source::SqlType;
83}
84
85impl<Combinator, Rule, Source, Rhs, O, LOf> SelectQuery
86    for CombinationClause<Combinator, Rule, Source, Rhs, O, LOf>
87where
88    Source: SelectQuery,
89    Rhs: SelectQuery<SqlType = Source::SqlType>,
90{
91    type SqlType = Source::SqlType;
92}
93
94impl<Combinator, Rule, Source, Rhs, O, LOf, QS> ValidSubselect<QS>
95    for CombinationClause<Combinator, Rule, Source, Rhs, O, LOf>
96where
97    Source: ValidSubselect<QS>,
98    Rhs: ValidSubselect<QS>,
99{
100}
101
102impl<Combinator, Rule, Source, Rhs, O, LOf, Conn> RunQueryDsl<Conn>
103    for CombinationClause<Combinator, Rule, Source, Rhs, O, LOf>
104{
105}
106
107impl<Combinator, Rule, Source, Rhs, O, LOf, T> Insertable<T>
108    for CombinationClause<Combinator, Rule, Source, Rhs, O, LOf>
109where
110    T: Table,
111    T::AllColumns: NonAggregate,
112    Self: Query,
113{
114    type Values = InsertFromSelect<Self, T::AllColumns>;
115
116    fn values(self) -> Self::Values {
117        InsertFromSelect::new(self)
118    }
119}
120
121impl<Combinator, Rule, Source, OriginRhs, O, LOf> CombineDsl
122    for CombinationClause<Combinator, Rule, Source, OriginRhs, O, LOf>
123where
124    Self: Query,
125{
126    type Query = Self;
127
128    fn union<Rhs>(self, rhs: Rhs) -> crate::dsl::Union<Self, Rhs>
129    where
130        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
131    {
132        CombinationClause::new(Union, Distinct, self, rhs.as_query())
133    }
134
135    fn union_all<Rhs>(self, rhs: Rhs) -> crate::dsl::UnionAll<Self, Rhs>
136    where
137        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
138    {
139        CombinationClause::new(Union, All, self, rhs.as_query())
140    }
141
142    fn intersect<Rhs>(self, rhs: Rhs) -> crate::dsl::Intersect<Self, Rhs>
143    where
144        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
145    {
146        CombinationClause::new(Intersect, Distinct, self, rhs.as_query())
147    }
148
149    fn intersect_all<Rhs>(self, rhs: Rhs) -> crate::dsl::IntersectAll<Self, Rhs>
150    where
151        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
152    {
153        CombinationClause::new(Intersect, All, self, rhs.as_query())
154    }
155
156    fn except<Rhs>(self, rhs: Rhs) -> crate::dsl::Except<Self, Rhs>
157    where
158        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
159    {
160        CombinationClause::new(Except, Distinct, self, rhs.as_query())
161    }
162
163    fn except_all<Rhs>(self, rhs: Rhs) -> crate::dsl::ExceptAll<Self, Rhs>
164    where
165        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
166    {
167        CombinationClause::new(Except, All, self, rhs.as_query())
168    }
169}
170
171impl<Combinator, Rule, Source, Rhs, O, LOf, DB: Backend> QueryFragment<DB>
172    for CombinationClause<Combinator, Rule, Source, Rhs, O, LOf>
173where
174    Combinator: QueryFragment<DB>,
175    Rule: QueryFragment<DB>,
176    ParenthesisWrapper<Source>: QueryFragment<DB>,
177    ParenthesisWrapper<Rhs>: QueryFragment<DB>,
178    O: QueryFragment<DB>,
179    LOf: QueryFragment<DB>,
180    DB: Backend + SupportsCombinationClause<Combinator, Rule> + DieselReserveSpecialization,
181{
182    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
183        self.source.walk_ast(out.reborrow())?;
184        self.combinator.walk_ast(out.reborrow())?;
185        self.duplicate_rule.walk_ast(out.reborrow())?;
186        self.rhs.walk_ast(out.reborrow())?;
187        self.order.walk_ast(out.reborrow())?;
188        self.limit_offset.walk_ast(out)
189    }
190}
191
192impl<ST, Combinator, Rule, Source, Rhs, O, LOf, RawExpr, Expr> PositionalOrderDsl<RawExpr>
193    for CombinationClause<Combinator, Rule, Source, Rhs, O, LOf>
194where
195    Self: SelectQuery<SqlType = ST>,
196    CombinationClause<Combinator, Rule, Source, Rhs, OrderClause<Expr>, LOf>:
197        SelectQuery<SqlType = ST>,
198    RawExpr: IntoPositionalOrderExpr<Output = Expr>,
199{
200    type Output = CombinationClause<Combinator, Rule, Source, Rhs, OrderClause<Expr>, LOf>;
201
202    fn positional_order_by(self, expr: RawExpr) -> Self::Output {
203        let order = OrderClause(expr.into_positional_expr());
204
205        CombinationClause {
206            combinator: self.combinator,
207            duplicate_rule: self.duplicate_rule,
208            source: self.source,
209            rhs: self.rhs,
210            order,
211            limit_offset: self.limit_offset,
212        }
213    }
214}
215
216#[doc(hidden)]
217type Limit = AsExprOf<i64, BigInt>;
218
219impl<ST, Combinator, Rule, Source, Rhs, O, L, Of> LimitDsl
220    for CombinationClause<Combinator, Rule, Source, Rhs, O, LimitOffsetClause<L, Of>>
221where
222    Self: SelectQuery<SqlType = ST>,
223    CombinationClause<Combinator, Rule, Source, Rhs, O, LimitOffsetClause<LimitClause<Limit>, Of>>:
224        SelectQuery<SqlType = ST>,
225{
226    type Output = CombinationClause<
227        Combinator,
228        Rule,
229        Source,
230        Rhs,
231        O,
232        LimitOffsetClause<LimitClause<Limit>, Of>,
233    >;
234
235    fn limit(self, limit: i64) -> Self::Output {
236        let limit_clause = LimitClause(limit.into_sql::<BigInt>());
237        CombinationClause {
238            combinator: self.combinator,
239            duplicate_rule: self.duplicate_rule,
240            source: self.source,
241            rhs: self.rhs,
242            order: self.order,
243            limit_offset: LimitOffsetClause {
244                limit_clause,
245                offset_clause: self.limit_offset.offset_clause,
246            },
247        }
248    }
249}
250
251#[doc(hidden)]
252type Offset = Limit;
253
254impl<ST, Combinator, Rule, Source, Rhs, O, L, Of> OffsetDsl
255    for CombinationClause<Combinator, Rule, Source, Rhs, O, LimitOffsetClause<L, Of>>
256where
257    Self: SelectQuery<SqlType = ST>,
258    CombinationClause<Combinator, Rule, Source, Rhs, O, LimitOffsetClause<L, OffsetClause<Offset>>>:
259        SelectQuery<SqlType = ST>,
260{
261    type Output = CombinationClause<
262        Combinator,
263        Rule,
264        Source,
265        Rhs,
266        O,
267        LimitOffsetClause<L, OffsetClause<Offset>>,
268    >;
269
270    fn offset(self, offset: i64) -> Self::Output {
271        let offset_clause = OffsetClause(offset.into_sql::<BigInt>());
272        CombinationClause {
273            combinator: self.combinator,
274            duplicate_rule: self.duplicate_rule,
275            source: self.source,
276            rhs: self.rhs,
277            order: self.order,
278            limit_offset: LimitOffsetClause {
279                limit_clause: self.limit_offset.limit_clause,
280                offset_clause,
281            },
282        }
283    }
284}
285
286#[derive(Debug, Copy, Clone, QueryId)]
287/// Computes the set union of the rows returned by the involved `SELECT` statements using SQL `UNION`
288pub struct Union;
289
290impl<DB> QueryFragment<DB> for Union
291where
292    DB: Backend + DieselReserveSpecialization,
293{
294    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
295        out.push_sql(" UNION ");
296        Ok(())
297    }
298}
299
300#[derive(Debug, Copy, Clone, QueryId)]
301/// Computes the set intersection of the rows returned by the involved `SELECT` statements using SQL `INTERSECT`
302pub struct Intersect;
303
304impl<DB> QueryFragment<DB> for Intersect
305where
306    DB: Backend + DieselReserveSpecialization,
307{
308    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
309        out.push_sql(" INTERSECT ");
310        Ok(())
311    }
312}
313
314#[derive(Debug, Copy, Clone, QueryId)]
315/// Computes the set difference of the rows returned by the involved `SELECT` statements using SQL `EXCEPT`
316pub struct Except;
317
318impl<DB> QueryFragment<DB> for Except
319where
320    DB: Backend + DieselReserveSpecialization,
321{
322    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
323        out.push_sql(" EXCEPT ");
324        Ok(())
325    }
326}
327
328#[derive(Debug, Copy, Clone, QueryId)]
329/// Remove duplicate rows in the result, this is the default behavior of `UNION`, `INTERSECT` and `EXCEPT`
330pub struct Distinct;
331
332impl<DB> QueryFragment<DB> for Distinct
333where
334    DB: Backend + DieselReserveSpecialization,
335{
336    fn walk_ast<'b>(&'b self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> {
337        Ok(())
338    }
339}
340
341#[derive(Debug, Copy, Clone, QueryId)]
342/// Keep duplicate rows in the result
343pub struct All;
344
345impl<DB> QueryFragment<DB> for All
346where
347    DB: Backend + DieselReserveSpecialization,
348{
349    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
350        out.push_sql("ALL ");
351        Ok(())
352    }
353}
354
355/// Marker trait used to indicate whenever a backend supports given combination
356pub trait SupportsCombinationClause<Combinator, Rule> {}
357
358#[derive(Debug, Copy, Clone, QueryId)]
359/// Wrapper used to wrap rhs sql in parenthesis when supported by backend
360#[diesel_derives::__diesel_public_if(
361    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
362    public_fields(inner)
363)]
364pub struct ParenthesisWrapper<T> {
365    /// the inner parenthesis definition
366    #[allow(dead_code)]
367    inner: T,
368}
369
370#[cfg(feature = "postgres_backend")]
371mod postgres {
372    use super::*;
373    use crate::pg::Pg;
374
375    impl<T: QueryFragment<Pg>> QueryFragment<Pg> for ParenthesisWrapper<T> {
376        fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
377            out.push_sql("(");
378            self.inner.walk_ast(out.reborrow())?;
379            out.push_sql(")");
380            Ok(())
381        }
382    }
383
384    impl SupportsCombinationClause<Union, Distinct> for Pg {}
385    impl SupportsCombinationClause<Union, All> for Pg {}
386    impl SupportsCombinationClause<Intersect, Distinct> for Pg {}
387    impl SupportsCombinationClause<Intersect, All> for Pg {}
388    impl SupportsCombinationClause<Except, Distinct> for Pg {}
389    impl SupportsCombinationClause<Except, All> for Pg {}
390}
391
392#[cfg(feature = "mysql_backend")]
393mod mysql {
394    use super::*;
395    use crate::mysql::Mysql;
396
397    impl<T: QueryFragment<Mysql>> QueryFragment<Mysql> for ParenthesisWrapper<T> {
398        fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> {
399            out.push_sql("(");
400            self.inner.walk_ast(out.reborrow())?;
401            out.push_sql(")");
402            Ok(())
403        }
404    }
405
406    impl SupportsCombinationClause<Union, Distinct> for Mysql {}
407    impl SupportsCombinationClause<Union, All> for Mysql {}
408}
409
410#[cfg(feature = "sqlite")]
411mod sqlite {
412    use super::*;
413    use crate::sqlite::Sqlite;
414
415    impl<T: QueryFragment<Sqlite>> QueryFragment<Sqlite> for ParenthesisWrapper<T> {
416        fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> {
417            // SQLite does not support parenthesis around this clause
418            // we can emulate this by construct a fake outer
419            // SELECT * FROM (inner_query) statement
420            out.push_sql("SELECT * FROM (");
421            self.inner.walk_ast(out.reborrow())?;
422            out.push_sql(")");
423            Ok(())
424        }
425    }
426
427    impl SupportsCombinationClause<Union, Distinct> for Sqlite {}
428    impl SupportsCombinationClause<Union, All> for Sqlite {}
429    impl SupportsCombinationClause<Intersect, Distinct> for Sqlite {}
430    impl SupportsCombinationClause<Except, Distinct> for Sqlite {}
431}