Skip to main content

diesel/query_builder/select_statement/
boxed.rs

1use core::marker::PhantomData;
2
3use crate::backend::{DieselReserveSpecialization, sql_dialect};
4use crate::dsl::AsExprOf;
5use crate::expression::subselect::{ValidSubselect, ValidSubselectGrouping};
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#[doc = " This type represents a boxed select query"]
#[doc = ""]
#[doc = " Using this type directly is only meaningful for custom backends"]
#[doc = " that need to provide a custom [`QueryFragment`] implementation"]
#[allow(missing_debug_implementations)]
#[non_exhaustive]
pub struct BoxedSelectStatement<'a, ST, QS, DB, GB = ()> {
    #[doc = " The select clause of the query"]
    pub select: Box<dyn QueryFragment<DB> + Send + 'a>,
    #[doc = " The from clause of the query"]
    pub from: QS,
    #[doc = " The distinct clause of the query"]
    pub distinct: Box<dyn QueryFragment<DB> + Send + 'a>,
    #[doc = " The where clause of the query"]
    pub where_clause: BoxedWhereClause<'a, DB>,
    #[doc = " The order clause of the query"]
    pub order: Option<Box<dyn QueryFragment<DB> + Send + 'a>>,
    #[doc = " The combined limit/offset clause of the query"]
    pub limit_offset: BoxedLimitOffsetClause<'a, DB>,
    #[doc = " The group by clause of the query"]
    pub group_by: Box<dyn QueryFragment<DB> + Send + 'a>,
    #[doc = " The having clause of the query"]
    pub having: Box<dyn QueryFragment<DB> + Send + 'a>,
    _marker: PhantomData<(ST, GB)>,
}#[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 + 'b,
147        QS: QueryFragment<DB>,
148        BoxedLimitOffsetClause<'a, DB>: QueryFragment<DB>,
149        'a: 'b,
150        'b: 'c;
151}
152
153impl<'a, ST, QS, DB, GB> BoxedQueryHelper<'a, QS, DB> for BoxedSelectStatement<'a, ST, QS, DB, GB> {
154    fn build_query<'b, 'c>(
155        &'b self,
156        mut out: AstPass<'_, 'c, DB>,
157        where_clause_handler: impl Fn(
158            &'b BoxedWhereClause<'a, DB>,
159            AstPass<'_, 'c, DB>,
160        ) -> QueryResult<()>,
161    ) -> QueryResult<()>
162    where
163        DB: Backend,
164        QS: QueryFragment<DB>,
165        BoxedLimitOffsetClause<'a, DB>: QueryFragment<DB>,
166        'b: 'c,
167    {
168        out.push_sql("SELECT ");
169        self.distinct.walk_ast(out.reborrow())?;
170        self.select.walk_ast(out.reborrow())?;
171        self.from.walk_ast(out.reborrow())?;
172        where_clause_handler(&self.where_clause, out.reborrow())?;
173        self.group_by.walk_ast(out.reborrow())?;
174        self.having.walk_ast(out.reborrow())?;
175
176        if let Some(ref order) = self.order {
177            out.push_sql(" ORDER BY ");
178            order.walk_ast(out.reborrow())?;
179        }
180        self.limit_offset.walk_ast(out.reborrow())?;
181        Ok(())
182    }
183}
184
185impl<ST, QS, DB, GB> Query for BoxedSelectStatement<'_, ST, QS, DB, GB>
186where
187    DB: Backend,
188{
189    type SqlType = ST;
190}
191
192impl<ST, QS, DB, GB> SelectQuery for BoxedSelectStatement<'_, ST, QS, DB, GB>
193where
194    DB: Backend,
195{
196    type SqlType = ST;
197}
198
199impl<ST, QS, QS2, DB, GB> ValidSubselect<QS2> for BoxedSelectStatement<'_, ST, QS, DB, GB> where
200    Self: Query<SqlType = ST>
201{
202}
203
204// Boxing and every boxed clause method only accept expressions of `QS`, so a boxed
205// statement never references the outer query
206impl<ST, QS, DB, GB, OuterGB> ValidSubselectGrouping<OuterGB>
207    for BoxedSelectStatement<'_, ST, QS, DB, GB>
208{
209    type IsAggregate = is_aggregate::Never;
210}
211
212impl<ST, QS, DB, GB> QueryFragment<DB> for BoxedSelectStatement<'_, ST, QS, DB, GB>
213where
214    DB: Backend,
215    Self: QueryFragment<DB, DB::SelectStatementSyntax>,
216{
217    fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
218        <Self as QueryFragment<DB, DB::SelectStatementSyntax>>::walk_ast(self, pass)
219    }
220}
221
222impl<'a, ST, QS, DB, GB>
223    QueryFragment<DB, sql_dialect::select_statement_syntax::AnsiSqlSelectStatement>
224    for BoxedSelectStatement<'a, ST, QS, DB, GB>
225where
226    DB: Backend<
227            SelectStatementSyntax = sql_dialect::select_statement_syntax::AnsiSqlSelectStatement,
228        > + DieselReserveSpecialization,
229    QS: QueryFragment<DB>,
230    BoxedLimitOffsetClause<'a, DB>: QueryFragment<DB>,
231{
232    fn walk_ast<'b>(&'b self, out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
233        self.build_query(out, |where_clause, out| where_clause.walk_ast(out))
234    }
235}
236
237impl<ST, QS, DB, GB> QueryId for BoxedSelectStatement<'_, ST, QS, DB, GB> {
238    type QueryId = ();
239
240    const HAS_STATIC_QUERY_ID: bool = false;
241}
242
243impl<'a, ST, QS, DB, Rhs, Kind, On, GB> InternalJoinDsl<Rhs, Kind, On>
244    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
245where
246    QS: QuerySource,
247    Rhs: QuerySource,
248    JoinOn<Join<QS, Rhs, Kind>, On>: QuerySource,
249    BoxedSelectStatement<'a, ST, FromClause<JoinOn<Join<QS, Rhs, Kind>, On>>, DB, GB>: AsQuery,
250{
251    type Output = BoxedSelectStatement<'a, ST, FromClause<JoinOn<Join<QS, Rhs, Kind>, On>>, DB, GB>;
252
253    fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output {
254        BoxedSelectStatement {
255            select: self.select,
256            from: FromClause::new(Join::new(self.from.source, rhs, kind).on(on)),
257            distinct: self.distinct,
258            where_clause: self.where_clause,
259            order: self.order,
260            limit_offset: self.limit_offset,
261            group_by: self.group_by,
262            having: self.having,
263            _marker: PhantomData,
264        }
265    }
266}
267
268impl<ST, QS, DB, GB> DistinctDsl for BoxedSelectStatement<'_, ST, QS, DB, GB>
269where
270    DB: Backend,
271    DistinctClause: QueryFragment<DB>,
272{
273    type Output = Self;
274
275    fn distinct(mut self) -> Self::Output {
276        self.distinct = Box::new(DistinctClause);
277        self
278    }
279}
280
281impl<'a, ST, QS, DB, GB> BoxedSelectStatement<'a, ST, QS, DB, GB> {
282    /// The statement with `select` as its select clause and every other clause kept.
283    fn with_select<NewST>(
284        self,
285        select: Box<dyn QueryFragment<DB> + Send + 'a>,
286    ) -> BoxedSelectStatement<'a, NewST, QS, DB, GB> {
287        BoxedSelectStatement {
288            select,
289            from: self.from,
290            distinct: self.distinct,
291            where_clause: self.where_clause,
292            order: self.order,
293            limit_offset: self.limit_offset,
294            group_by: self.group_by,
295            having: self.having,
296            _marker: PhantomData,
297        }
298    }
299}
300
301impl<'a, ST, QS, DB, Selection, GB> SelectDsl<Selection>
302    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
303where
304    DB: Backend,
305    QS: QuerySource,
306    Selection: SelectableExpression<QS> + QueryFragment<DB> + ValidGrouping<GB> + Send + 'a,
307{
308    type Output = BoxedSelectStatement<'a, Selection::SqlType, FromClause<QS>, DB, GB>;
309
310    fn select(self, selection: Selection) -> Self::Output {
311        self.with_select(Box::new(selection))
312    }
313}
314
315impl<'a, ST, DB, Selection, GB> SelectDsl<Selection>
316    for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB>
317where
318    DB: Backend,
319    Selection:
320        SelectableExpression<NoFromClause> + QueryFragment<DB> + ValidGrouping<GB> + Send + 'a,
321{
322    type Output = BoxedSelectStatement<'a, Selection::SqlType, NoFromClause, DB, GB>;
323
324    fn select(self, selection: Selection) -> Self::Output {
325        self.with_select(Box::new(selection))
326    }
327}
328
329impl<'a, ST, QS, DB, Predicate, GB> FilterDsl<Predicate>
330    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
331where
332    QS: QuerySource,
333    BoxedWhereClause<'a, DB>: WhereAnd<Predicate, Output = BoxedWhereClause<'a, DB>>,
334    Predicate: AppearsOnTable<QS> + NonAggregate,
335    Predicate::SqlType: BoolOrNullableBool,
336{
337    type Output = Self;
338
339    fn filter(mut self, predicate: Predicate) -> Self::Output {
340        self.where_clause = self.where_clause.and(predicate);
341        self
342    }
343}
344
345impl<'a, ST, DB, Predicate, GB> FilterDsl<Predicate>
346    for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB>
347where
348    BoxedWhereClause<'a, DB>: WhereAnd<Predicate, Output = BoxedWhereClause<'a, DB>>,
349    Predicate: AppearsOnTable<NoFromClause> + NonAggregate,
350    Predicate::SqlType: BoolOrNullableBool,
351{
352    type Output = Self;
353
354    fn filter(mut self, predicate: Predicate) -> Self::Output {
355        self.where_clause = self.where_clause.and(predicate);
356        self
357    }
358}
359
360impl<'a, ST, QS, DB, Predicate, GB> OrFilterDsl<Predicate>
361    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
362where
363    QS: QuerySource,
364    BoxedWhereClause<'a, DB>: WhereOr<Predicate, Output = BoxedWhereClause<'a, DB>>,
365    Predicate: AppearsOnTable<QS> + NonAggregate,
366    Predicate::SqlType: BoolOrNullableBool,
367{
368    type Output = Self;
369
370    fn or_filter(mut self, predicate: Predicate) -> Self::Output {
371        self.where_clause = self.where_clause.or(predicate);
372        self
373    }
374}
375
376impl<'a, ST, DB, Predicate, GB> OrFilterDsl<Predicate>
377    for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB>
378where
379    BoxedWhereClause<'a, DB>: WhereOr<Predicate, Output = BoxedWhereClause<'a, DB>>,
380    Predicate: AppearsOnTable<NoFromClause> + NonAggregate,
381    Predicate::SqlType: BoolOrNullableBool,
382{
383    type Output = Self;
384
385    fn or_filter(mut self, predicate: Predicate) -> Self::Output {
386        self.where_clause = self.where_clause.or(predicate);
387        self
388    }
389}
390
391impl<ST, QS, DB, GB> LimitDsl for BoxedSelectStatement<'_, ST, QS, DB, GB>
392where
393    DB: Backend,
394    LimitClause<AsExprOf<i64, BigInt>>: QueryFragment<DB>,
395{
396    type Output = Self;
397
398    fn limit(mut self, limit: i64) -> Self::Output {
399        self.limit_offset.limit = Some(Box::new(LimitClause(limit.into_sql::<BigInt>())));
400        self
401    }
402}
403
404impl<ST, QS, DB, GB> OffsetDsl for BoxedSelectStatement<'_, ST, QS, DB, GB>
405where
406    DB: Backend,
407    OffsetClause<AsExprOf<i64, BigInt>>: QueryFragment<DB>,
408{
409    type Output = Self;
410
411    fn offset(mut self, offset: i64) -> Self::Output {
412        self.limit_offset.offset = Some(Box::new(OffsetClause(offset.into_sql::<BigInt>())));
413        self
414    }
415}
416
417// no impls for `NoFromClause` here because order is not really supported there yet
418impl<'a, ST, QS, DB, Order, GB> OrderDsl<Order>
419    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
420where
421    DB: Backend,
422    QS: QuerySource,
423    Order: QueryFragment<DB> + AppearsOnTable<QS> + Send + 'a,
424{
425    type Output = Self;
426
427    fn order(mut self, order: Order) -> Self::Output {
428        self.order = OrderClause(order).into();
429        self
430    }
431}
432
433impl<'a, ST, QS, DB, Order, GB> ThenOrderDsl<Order>
434    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
435where
436    DB: Backend + 'a,
437    QS: QuerySource,
438    Order: QueryFragment<DB> + AppearsOnTable<QS> + Send + 'a,
439{
440    type Output = Self;
441
442    fn then_order_by(mut self, order: Order) -> Self::Output {
443        self.order = match self.order {
444            Some(old) => Some(Box::new((old, order))),
445            None => Some(Box::new(order)),
446        };
447        self
448    }
449}
450
451impl<ST, QS, DB, Rhs> JoinTo<Rhs> for BoxedSelectStatement<'_, ST, FromClause<QS>, DB, ()>
452where
453    QS: JoinTo<Rhs> + QuerySource,
454{
455    type FromClause = <QS as JoinTo<Rhs>>::FromClause;
456    type OnClause = QS::OnClause;
457
458    fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) {
459        QS::join_target(rhs)
460    }
461}
462
463impl<ST, QS, DB, GB> QueryDsl for BoxedSelectStatement<'_, ST, QS, DB, GB> {}
464
465impl<ST, QS, DB, GB> RunQueryDslSupport for BoxedSelectStatement<'_, ST, QS, DB, GB> {}
466
467impl<ST, QS, DB, T, GB> Insertable<T> for BoxedSelectStatement<'_, ST, QS, DB, GB>
468where
469    T: Table,
470    Self: Query,
471    <T::AllColumns as ValidGrouping<()>>::IsAggregate:
472        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
473{
474    type Values = InsertFromSelect<Self, T::AllColumns>;
475
476    fn values(self) -> Self::Values {
477        InsertFromSelect::new(self)
478    }
479}
480
481impl<ST, QS, DB, T, GB> Insertable<T> for &BoxedSelectStatement<'_, ST, QS, DB, GB>
482where
483    T: Table,
484    Self: Query,
485    <T::AllColumns as ValidGrouping<()>>::IsAggregate:
486        MixedAggregates<is_aggregate::No, Output = is_aggregate::No>,
487{
488    type Values = InsertFromSelect<Self, T::AllColumns>;
489
490    fn values(self) -> Self::Values {
491        InsertFromSelect::new(self)
492    }
493}
494
495impl<'a, ST, QS, DB, GB> SelectNullableDsl for BoxedSelectStatement<'a, ST, QS, DB, GB>
496where
497    ST: IntoNullable,
498{
499    type Output = BoxedSelectStatement<'a, ST::Nullable, QS, DB>;
500
501    fn nullable(self) -> Self::Output {
502        BoxedSelectStatement {
503            select: self.select,
504            from: self.from,
505            distinct: self.distinct,
506            where_clause: self.where_clause,
507            order: self.order,
508            limit_offset: self.limit_offset,
509            group_by: self.group_by,
510            having: self.having,
511            _marker: PhantomData,
512        }
513    }
514}
515
516impl<'a, ST, QS, DB, GB, Predicate> HavingDsl<Predicate>
517    for BoxedSelectStatement<'a, ST, FromClause<QS>, DB, GB>
518where
519    QS: QuerySource,
520    DB: Backend,
521    GB: Expression,
522    HavingClause<Predicate>: QueryFragment<DB> + Send + 'a,
523    Predicate: AppearsOnTable<QS>,
524    Predicate::SqlType: BoolOrNullableBool,
525{
526    type Output = Self;
527
528    fn having(mut self, predicate: Predicate) -> Self::Output {
529        self.having = Box::new(HavingClause(predicate));
530        self
531    }
532}
533
534impl<ST, QS, DB, GB> CombineDsl for BoxedSelectStatement<'_, ST, QS, DB, GB>
535where
536    Self: Query,
537{
538    type Query = Self;
539
540    fn union<Rhs>(self, rhs: Rhs) -> crate::dsl::Union<Self, Rhs>
541    where
542        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
543    {
544        CombinationClause::new(Union, Distinct, self, rhs.as_query())
545    }
546
547    fn union_all<Rhs>(self, rhs: Rhs) -> crate::dsl::UnionAll<Self, Rhs>
548    where
549        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
550    {
551        CombinationClause::new(Union, All, self, rhs.as_query())
552    }
553
554    fn intersect<Rhs>(self, rhs: Rhs) -> crate::dsl::Intersect<Self, Rhs>
555    where
556        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
557    {
558        CombinationClause::new(Intersect, Distinct, self, rhs.as_query())
559    }
560
561    fn intersect_all<Rhs>(self, rhs: Rhs) -> crate::dsl::IntersectAll<Self, Rhs>
562    where
563        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
564    {
565        CombinationClause::new(Intersect, All, self, rhs.as_query())
566    }
567
568    fn except<Rhs>(self, rhs: Rhs) -> crate::dsl::Except<Self, Rhs>
569    where
570        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
571    {
572        CombinationClause::new(Except, Distinct, self, rhs.as_query())
573    }
574
575    fn except_all<Rhs>(self, rhs: Rhs) -> crate::dsl::ExceptAll<Self, Rhs>
576    where
577        Rhs: AsQuery<SqlType = <Self::Query as Query>::SqlType>,
578    {
579        CombinationClause::new(Except, All, self, rhs.as_query())
580    }
581}
582
583#[cfg(test)]
584mod tests {
585    use crate::prelude::*;
586
587    table! {
588        users {
589            id -> Integer,
590        }
591    }
592
593    fn assert_send<T>(_: T)
594    where
595        T: Send,
596    {
597    }
598
599    macro_rules! assert_boxed_query_send {
600        ($backend:ty) => {{
601            assert_send(users::table.into_boxed::<$backend>());
602            assert_send(
603                users::table
604                    .filter(users::id.eq(10))
605                    .into_boxed::<$backend>(),
606            );
607        };};
608    }
609
610    #[diesel_test_helper::test]
611    fn boxed_is_send() {
612        #[cfg(feature = "postgres")]
613        assert_boxed_query_send!(crate::pg::Pg);
614
615        #[cfg(feature = "__sqlite-shared")]
616        assert_boxed_query_send!(crate::sqlite::Sqlite);
617
618        #[cfg(feature = "mysql")]
619        assert_boxed_query_send!(crate::mysql::Mysql);
620
621        #[cfg(feature = "mariadb")]
622        assert_boxed_query_send!(crate::mariadb::Mariadb);
623    }
624}