diesel/query_builder/select_statement/
boxed.rs

1use std::marker::PhantomData;
2
3use crate::backend::{sql_dialect, DieselReserveSpecialization};
4use crate::dsl::AsExprOf;
5use crate::expression::subselect::ValidSubselect;
6use crate::expression::*;
7use crate::insertable::Insertable;
8use crate::query_builder::combination_clause::*;
9use crate::query_builder::distinct_clause::DistinctClause;
10use crate::query_builder::group_by_clause::ValidGroupByClause;
11use crate::query_builder::having_clause::HavingClause;
12use crate::query_builder::insert_statement::InsertFromSelect;
13use crate::query_builder::limit_clause::LimitClause;
14use crate::query_builder::limit_offset_clause::BoxedLimitOffsetClause;
15use crate::query_builder::offset_clause::OffsetClause;
16use crate::query_builder::order_clause::OrderClause;
17use crate::query_builder::where_clause::{BoxedWhereClause, WhereAnd, WhereOr};
18use crate::query_builder::*;
19use crate::query_dsl::methods::*;
20use crate::query_dsl::*;
21use crate::query_source::joins::*;
22use crate::query_source::{QuerySource, Table};
23use crate::sql_types::{BigInt, BoolOrNullableBool, IntoNullable};
24
25// This is used by the table macro internally
26/// This type represents a boxed select query
27///
28/// Using this type directly is only meaningful for custom backends
29/// that need to provide a custom [`QueryFragment`] implementation
30#[allow(missing_debug_implementations)]
31#[diesel_derives::__diesel_public_if(
32    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
33    public_fields(
34        select,
35        from,
36        distinct,
37        where_clause,
38        order,
39        limit_offset,
40        group_by,
41        having
42    )
43)]
44pub struct BoxedSelectStatement<'a, ST, QS, DB, GB = ()> {
45    /// The select clause of the query
46    select: Box<dyn QueryFragment<DB> + Send + 'a>,
47    /// The from clause of the query
48    from: QS,
49    /// The distinct clause of the query
50    distinct: Box<dyn QueryFragment<DB> + Send + 'a>,
51    /// The where clause of the query
52    where_clause: BoxedWhereClause<'a, DB>,
53    /// The order clause of the query
54    order: Option<Box<dyn QueryFragment<DB> + Send + 'a>>,
55    /// The combined limit/offset clause of the query
56    limit_offset: BoxedLimitOffsetClause<'a, DB>,
57    /// The group by clause of the query
58    group_by: Box<dyn QueryFragment<DB> + Send + 'a>,
59    /// The having clause of the query
60    having: Box<dyn QueryFragment<DB> + Send + 'a>,
61    _marker: PhantomData<(ST, GB)>,
62}
63
64impl<'a, ST, QS: QuerySource, DB, GB> BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB> {
65    #[allow(clippy::too_many_arguments)]
66    pub(crate) fn new<S, G>(
67        select: S,
68        from: FromClause<QS>,
69        distinct: Box<dyn QueryFragment<DB> + Send + 'a>,
70        where_clause: BoxedWhereClause<'a, DB>,
71        order: Option<Box<dyn QueryFragment<DB> + Send + 'a>>,
72        limit_offset: BoxedLimitOffsetClause<'a, DB>,
73        group_by: G,
74        having: Box<dyn QueryFragment<DB> + Send + 'a>,
75    ) -> Self
76    where
77        DB: Backend,
78        G: ValidGroupByClause<Expressions = GB> + QueryFragment<DB> + Send + 'a,
79        S: SelectClauseExpression<FromClause<QS>, SelectClauseSqlType = ST>
80            + QueryFragment<DB>
81            + Send
82            + 'a,
83        S::Selection: ValidGrouping<GB>,
84    {
85        BoxedSelectStatement {
86            select: Box::new(select),
87            from,
88            distinct,
89            where_clause,
90            order,
91            limit_offset,
92            group_by: Box::new(group_by),
93            having,
94            _marker: PhantomData,
95        }
96    }
97}
98
99impl<'a, ST, DB, GB> BoxedSelectStatement<'a, ST, NoFromClause, DB, GB> {
100    #[allow(clippy::too_many_arguments)]
101    pub(crate) fn new_no_from_clause<S, G>(
102        select: S,
103        from: NoFromClause,
104        distinct: Box<dyn QueryFragment<DB> + Send + 'a>,
105        where_clause: BoxedWhereClause<'a, DB>,
106        order: Option<Box<dyn QueryFragment<DB> + Send + 'a>>,
107        limit_offset: BoxedLimitOffsetClause<'a, DB>,
108        group_by: G,
109        having: Box<dyn QueryFragment<DB> + Send + 'a>,
110    ) -> Self
111    where
112        DB: Backend,
113        G: ValidGroupByClause<Expressions = GB> + QueryFragment<DB> + Send + 'a,
114        S: SelectClauseExpression<NoFromClause, SelectClauseSqlType = ST>
115            + QueryFragment<DB>
116            + Send
117            + 'a,
118        S::Selection: ValidGrouping<GB>,
119    {
120        BoxedSelectStatement {
121            select: Box::new(select),
122            from,
123            distinct,
124            where_clause,
125            order,
126            limit_offset,
127            group_by: Box::new(group_by),
128            having,
129            _marker: PhantomData,
130        }
131    }
132}
133
134// that's a trait to control who can access these methods
135#[doc(hidden)] // exported via internal::derives::multiconnection
136pub trait BoxedQueryHelper<'a, QS, DB> {
137    fn build_query<'b, 'c>(
138        &'b self,
139        out: AstPass<'_, 'c, DB>,
140        where_clause_handler: impl Fn(
141            &'b BoxedWhereClause<'a, DB>,
142            AstPass<'_, 'c, DB>,
143        ) -> QueryResult<()>,
144    ) -> QueryResult<()>
145    where
146        DB: Backend,
147        QS: QueryFragment<DB>,
148        BoxedLimitOffsetClause<'a, DB>: QueryFragment<DB>,
149        'b: 'c;
150}
151
152impl<'a, ST, QS, DB, GB> BoxedQueryHelper<'a, QS, DB> for BoxedSelectStatement<'a, ST, QS, DB, GB> {
153    fn build_query<'b, 'c>(
154        &'b self,
155        mut out: AstPass<'_, 'c, DB>,
156        where_clause_handler: impl Fn(
157            &'b BoxedWhereClause<'a, DB>,
158            AstPass<'_, 'c, DB>,
159        ) -> QueryResult<()>,
160    ) -> QueryResult<()>
161    where
162        DB: Backend,
163        QS: QueryFragment<DB>,
164        BoxedLimitOffsetClause<'a, DB>: QueryFragment<DB>,
165        'b: 'c,
166    {
167        out.push_sql("SELECT ");
168        self.distinct.walk_ast(out.reborrow())?;
169        self.select.walk_ast(out.reborrow())?;
170        self.from.walk_ast(out.reborrow())?;
171        where_clause_handler(&self.where_clause, out.reborrow())?;
172        self.group_by.walk_ast(out.reborrow())?;
173        self.having.walk_ast(out.reborrow())?;
174
175        if let Some(ref order) = self.order {
176            out.push_sql(" ORDER BY ");
177            order.walk_ast(out.reborrow())?;
178        }
179        self.limit_offset.walk_ast(out.reborrow())?;
180        Ok(())
181    }
182}
183
184impl<ST, QS, DB, GB> Query for BoxedSelectStatement<'_, ST, QS, DB, GB>
185where
186    DB: Backend,
187{
188    type SqlType = ST;
189}
190
191impl<ST, QS, DB, GB> SelectQuery for BoxedSelectStatement<'_, ST, QS, DB, GB>
192where
193    DB: Backend,
194{
195    type SqlType = ST;
196}
197
198impl<ST, QS, QS2, DB, GB> ValidSubselect<QS2> for BoxedSelectStatement<'_, ST, QS, DB, GB> where
199    Self: Query<SqlType = ST>
200{
201}
202
203impl<ST, QS, DB, GB> QueryFragment<DB> for BoxedSelectStatement<'_, ST, QS, DB, GB>
204where
205    DB: Backend,
206    Self: QueryFragment<DB, DB::SelectStatementSyntax>,
207{
208    fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
209        <Self as QueryFragment<DB, DB::SelectStatementSyntax>>::walk_ast(self, pass)
210    }
211}
212
213impl<'a, ST, QS, DB, GB>
214    QueryFragment<DB, sql_dialect::select_statement_syntax::AnsiSqlSelectStatement>
215    for BoxedSelectStatement<'a, ST, QS, DB, GB>
216where
217    DB: Backend<
218            SelectStatementSyntax = sql_dialect::select_statement_syntax::AnsiSqlSelectStatement,
219        > + DieselReserveSpecialization,
220    QS: QueryFragment<DB>,
221    BoxedLimitOffsetClause<'a, DB>: QueryFragment<DB>,
222{
223    fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
224        self.build_query(out, |where_clause, out| where_clause.walk_ast(out))
225    }
226}
227
228impl<ST, QS, DB, GB> QueryId for BoxedSelectStatement<'_, ST, QS, DB, GB> {
229    type QueryId = ();
230
231    const HAS_STATIC_QUERY_ID: bool = false;
232}
233
234impl<'a, ST, QS, DB, Rhs, Kind, On, GB> InternalJoinDsl<Rhs, Kind, On>
235    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
236where
237    QS: QuerySource,
238    Rhs: QuerySource,
239    JoinOn<Join<QS, Rhs, Kind>, On>: QuerySource,
240    BoxedSelectStatement<'a, ST, FromClause<JoinOn<Join<QS, Rhs, Kind>, On>>, DB, GB>: AsQuery,
241{
242    type Output = BoxedSelectStatement<'a, ST, FromClause<JoinOn<Join<QS, Rhs, Kind>, On>>, DB, GB>;
243
244    fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output {
245        BoxedSelectStatement {
246            select: self.select,
247            from: FromClause::new(Join::new(self.from.source, rhs, kind).on(on)),
248            distinct: self.distinct,
249            where_clause: self.where_clause,
250            order: self.order,
251            limit_offset: self.limit_offset,
252            group_by: self.group_by,
253            having: self.having,
254            _marker: PhantomData,
255        }
256    }
257}
258
259impl<ST, QS, DB, GB> DistinctDsl for BoxedSelectStatement<'_, ST, QS, DB, GB>
260where
261    DB: Backend,
262    DistinctClause: QueryFragment<DB>,
263{
264    type Output = Self;
265
266    fn distinct(mut self) -> Self::Output {
267        self.distinct = Box::new(DistinctClause);
268        self
269    }
270}
271
272impl<'a, ST, QS, DB, Selection, GB> SelectDsl<Selection>
273    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
274where
275    DB: Backend,
276    QS: QuerySource,
277    Selection: SelectableExpression<QS> + QueryFragment<DB> + ValidGrouping<GB> + Send + 'a,
278{
279    type Output = BoxedSelectStatement<'a, Selection::SqlType, FromClause<QS>, DB, GB>;
280
281    fn select(self, selection: Selection) -> Self::Output {
282        BoxedSelectStatement {
283            select: Box::new(selection),
284            from: self.from,
285            distinct: self.distinct,
286            where_clause: self.where_clause,
287            order: self.order,
288            limit_offset: self.limit_offset,
289            group_by: self.group_by,
290            having: self.having,
291            _marker: PhantomData,
292        }
293    }
294}
295
296impl<'a, ST, DB, Selection, GB> SelectDsl<Selection>
297    for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB>
298where
299    DB: Backend,
300    Selection:
301        SelectableExpression<NoFromClause> + QueryFragment<DB> + ValidGrouping<GB> + Send + 'a,
302{
303    type Output = BoxedSelectStatement<'a, Selection::SqlType, NoFromClause, DB, GB>;
304
305    fn select(self, selection: Selection) -> Self::Output {
306        BoxedSelectStatement {
307            select: Box::new(selection),
308            from: self.from,
309            distinct: self.distinct,
310            where_clause: self.where_clause,
311            order: self.order,
312            limit_offset: self.limit_offset,
313            group_by: self.group_by,
314            having: self.having,
315            _marker: PhantomData,
316        }
317    }
318}
319
320impl<'a, ST, QS, DB, Predicate, GB> FilterDsl<Predicate>
321    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
322where
323    QS: QuerySource,
324    BoxedWhereClause<'a, DB>: WhereAnd<Predicate, Output = BoxedWhereClause<'a, DB>>,
325    Predicate: AppearsOnTable<QS> + NonAggregate,
326    Predicate::SqlType: BoolOrNullableBool,
327{
328    type Output = Self;
329
330    fn filter(mut self, predicate: Predicate) -> Self::Output {
331        self.where_clause = self.where_clause.and(predicate);
332        self
333    }
334}
335
336impl<'a, ST, DB, Predicate, GB> FilterDsl<Predicate>
337    for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB>
338where
339    BoxedWhereClause<'a, DB>: WhereAnd<Predicate, Output = BoxedWhereClause<'a, DB>>,
340    Predicate: AppearsOnTable<NoFromClause> + NonAggregate,
341    Predicate::SqlType: BoolOrNullableBool,
342{
343    type Output = Self;
344
345    fn filter(mut self, predicate: Predicate) -> Self::Output {
346        self.where_clause = self.where_clause.and(predicate);
347        self
348    }
349}
350
351impl<'a, ST, QS, DB, Predicate, GB> OrFilterDsl<Predicate>
352    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
353where
354    QS: QuerySource,
355    BoxedWhereClause<'a, DB>: WhereOr<Predicate, Output = BoxedWhereClause<'a, DB>>,
356    Predicate: AppearsOnTable<QS> + NonAggregate,
357    Predicate::SqlType: BoolOrNullableBool,
358{
359    type Output = Self;
360
361    fn or_filter(mut self, predicate: Predicate) -> Self::Output {
362        self.where_clause = self.where_clause.or(predicate);
363        self
364    }
365}
366
367impl<'a, ST, DB, Predicate, GB> OrFilterDsl<Predicate>
368    for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB>
369where
370    BoxedWhereClause<'a, DB>: WhereOr<Predicate, Output = BoxedWhereClause<'a, DB>>,
371    Predicate: AppearsOnTable<NoFromClause> + NonAggregate,
372    Predicate::SqlType: BoolOrNullableBool,
373{
374    type Output = Self;
375
376    fn or_filter(mut self, predicate: Predicate) -> Self::Output {
377        self.where_clause = self.where_clause.or(predicate);
378        self
379    }
380}
381
382impl<ST, QS, DB, GB> LimitDsl for BoxedSelectStatement<'_, ST, QS, DB, GB>
383where
384    DB: Backend,
385    LimitClause<AsExprOf<i64, BigInt>>: QueryFragment<DB>,
386{
387    type Output = Self;
388
389    fn limit(mut self, limit: i64) -> Self::Output {
390        self.limit_offset.limit = Some(Box::new(LimitClause(limit.into_sql::<BigInt>())));
391        self
392    }
393}
394
395impl<ST, QS, DB, GB> OffsetDsl for BoxedSelectStatement<'_, ST, QS, DB, GB>
396where
397    DB: Backend,
398    OffsetClause<AsExprOf<i64, BigInt>>: QueryFragment<DB>,
399{
400    type Output = Self;
401
402    fn offset(mut self, offset: i64) -> Self::Output {
403        self.limit_offset.offset = Some(Box::new(OffsetClause(offset.into_sql::<BigInt>())));
404        self
405    }
406}
407
408// no impls for `NoFromClause` here because order is not really supported there yet
409impl<'a, ST, QS, DB, Order, GB> OrderDsl<Order>
410    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
411where
412    DB: Backend,
413    QS: QuerySource,
414    Order: QueryFragment<DB> + AppearsOnTable<QS> + Send + 'a,
415{
416    type Output = Self;
417
418    fn order(mut self, order: Order) -> Self::Output {
419        self.order = OrderClause(order).into();
420        self
421    }
422}
423
424impl<'a, ST, QS, DB, Order, GB> ThenOrderDsl<Order>
425    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
426where
427    DB: Backend + 'a,
428    QS: QuerySource,
429    Order: QueryFragment<DB> + AppearsOnTable<QS> + Send + 'a,
430{
431    type Output = Self;
432
433    fn then_order_by(mut self, order: Order) -> Self::Output {
434        self.order = match self.order {
435            Some(old) => Some(Box::new((old, order))),
436            None => Some(Box::new(order)),
437        };
438        self
439    }
440}
441
442impl<ST, QS, DB, Rhs> JoinTo<Rhs> for BoxedSelectStatement<'_, ST, FromClause<QS>, DB, ()>
443where
444    QS: JoinTo<Rhs> + QuerySource,
445{
446    type FromClause = <QS as JoinTo<Rhs>>::FromClause;
447    type OnClause = QS::OnClause;
448
449    fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) {
450        QS::join_target(rhs)
451    }
452}
453
454impl<ST, QS, DB, GB> QueryDsl for BoxedSelectStatement<'_, ST, QS, DB, GB> {}
455
456impl<ST, QS, DB, Conn, GB> RunQueryDsl<Conn> for BoxedSelectStatement<'_, ST, QS, DB, GB> {}
457
458impl<ST, QS, DB, T, GB> Insertable<T> for BoxedSelectStatement<'_, ST, QS, DB, GB>
459where
460    T: Table,
461    Self: Query,
462    <T::AllColumns as ValidGrouping<()>>::IsAggregate:
463        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
464{
465    type Values = InsertFromSelect<Self, T::AllColumns>;
466
467    fn values(self) -> Self::Values {
468        InsertFromSelect::new(self)
469    }
470}
471
472impl<ST, QS, DB, T, GB> Insertable<T> for &BoxedSelectStatement<'_, ST, QS, DB, GB>
473where
474    T: Table,
475    Self: Query,
476    <T::AllColumns as ValidGrouping<()>>::IsAggregate:
477        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
478{
479    type Values = InsertFromSelect<Self, T::AllColumns>;
480
481    fn values(self) -> Self::Values {
482        InsertFromSelect::new(self)
483    }
484}
485
486impl<'a, ST, QS, DB, GB> SelectNullableDsl for BoxedSelectStatement<'a, ST, QS, DB, GB>
487where
488    ST: IntoNullable,
489{
490    type Output = BoxedSelectStatement<'a, ST::Nullable, QS, DB>;
491
492    fn nullable(self) -> Self::Output {
493        BoxedSelectStatement {
494            select: self.select,
495            from: self.from,
496            distinct: self.distinct,
497            where_clause: self.where_clause,
498            order: self.order,
499            limit_offset: self.limit_offset,
500            group_by: self.group_by,
501            having: self.having,
502            _marker: PhantomData,
503        }
504    }
505}
506
507impl<'a, ST, QS, DB, GB, Predicate> HavingDsl<Predicate>
508    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
509where
510    QS: QuerySource,
511    DB: Backend,
512    GB: Expression,
513    HavingClause<Predicate>: QueryFragment<DB> + Send + 'a,
514    Predicate: AppearsOnTable<QS>,
515    Predicate::SqlType: BoolOrNullableBool,
516{
517    type Output = Self;
518
519    fn having(mut self, predicate: Predicate) -> Self::Output {
520        self.having = Box::new(HavingClause(predicate));
521        self
522    }
523}
524
525impl<ST, QS, DB, GB> CombineDsl for BoxedSelectStatement<'_, ST, QS, DB, GB>
526where
527    Self: Query,
528{
529    type Query = Self;
530
531    fn union<Rhs>(self, rhs: Rhs) -> crate::dsl::Union<Self, Rhs>
532    where
533        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
534    {
535        CombinationClause::new(Union, Distinct, self, rhs.as_query())
536    }
537
538    fn union_all<Rhs>(self, rhs: Rhs) -> crate::dsl::UnionAll<Self, Rhs>
539    where
540        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
541    {
542        CombinationClause::new(Union, All, self, rhs.as_query())
543    }
544
545    fn intersect<Rhs>(self, rhs: Rhs) -> crate::dsl::Intersect<Self, Rhs>
546    where
547        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
548    {
549        CombinationClause::new(Intersect, Distinct, self, rhs.as_query())
550    }
551
552    fn intersect_all<Rhs>(self, rhs: Rhs) -> crate::dsl::IntersectAll<Self, Rhs>
553    where
554        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
555    {
556        CombinationClause::new(Intersect, All, self, rhs.as_query())
557    }
558
559    fn except<Rhs>(self, rhs: Rhs) -> crate::dsl::Except<Self, Rhs>
560    where
561        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
562    {
563        CombinationClause::new(Except, Distinct, self, rhs.as_query())
564    }
565
566    fn except_all<Rhs>(self, rhs: Rhs) -> crate::dsl::ExceptAll<Self, Rhs>
567    where
568        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
569    {
570        CombinationClause::new(Except, All, self, rhs.as_query())
571    }
572}
573
574#[cfg(test)]
575mod tests {
576    use crate::prelude::*;
577
578    table! {
579        users {
580            id -> Integer,
581        }
582    }
583
584    fn assert_send<T>(_: T)
585    where
586        T: Send,
587    {
588    }
589
590    macro_rules! assert_boxed_query_send {
591        ($backend:ty) => {{
592            assert_send(users::table.into_boxed::<$backend>());
593            assert_send(
594                users::table
595                    .filter(users::id.eq(10))
596                    .into_boxed::<$backend>(),
597            );
598        };};
599    }
600
601    #[test]
602    fn boxed_is_send() {
603        #[cfg(feature = "postgres")]
604        assert_boxed_query_send!(crate::pg::Pg);
605
606        #[cfg(feature = "sqlite")]
607        assert_boxed_query_send!(crate::sqlite::Sqlite);
608
609        #[cfg(feature = "mysql")]
610        assert_boxed_query_send!(crate::mysql::Mysql);
611    }
612}