diesel/query_source/
joins.rs

1use super::{AppearsInFromClause, Plus, QueryRelation};
2use crate::backend::Backend;
3use crate::backend::DieselReserveSpecialization;
4use crate::expression::grouped::Grouped;
5use crate::expression::nullable::Nullable;
6use crate::prelude::*;
7use crate::query_builder::*;
8use crate::query_dsl::InternalJoinDsl;
9use crate::sql_types::BoolOrNullableBool;
10use crate::util::TupleAppend;
11
12/// A query source representing the join between two tables
13pub struct Join<Left: QuerySource, Right: QuerySource, Kind> {
14    left: FromClause<Left>,
15    right: FromClause<Right>,
16    kind: Kind,
17}
18
19impl<Left, Right, Kind> Clone for Join<Left, Right, Kind>
20where
21    Left: QuerySource,
22    FromClause<Left>: Clone,
23    Right: QuerySource,
24    FromClause<Right>: Clone,
25    Kind: Clone,
26{
27    fn clone(&self) -> Self {
28        Self {
29            left: self.left.clone(),
30            right: self.right.clone(),
31            kind: self.kind.clone(),
32        }
33    }
34}
35
36impl<Left, Right, Kind> Copy for Join<Left, Right, Kind>
37where
38    Left: QuerySource,
39    FromClause<Left>: Copy,
40    Right: QuerySource,
41    FromClause<Right>: Copy,
42    Kind: Copy,
43{
44}
45
46impl<Left, Right, Kind> std::fmt::Debug for Join<Left, Right, Kind>
47where
48    Left: QuerySource,
49    FromClause<Left>: std::fmt::Debug,
50    Right: QuerySource,
51    FromClause<Right>: std::fmt::Debug,
52    Kind: std::fmt::Debug,
53{
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        f.debug_struct("Join")
56            .field("left", &self.left)
57            .field("right", &self.right)
58            .field("kind", &self.kind)
59            .finish()
60    }
61}
62
63impl<Left, Right, Kind> QueryId for Join<Left, Right, Kind>
64where
65    Left: QueryId + QuerySource + 'static,
66    Right: QueryId + QuerySource + 'static,
67    Kind: QueryId,
68{
69    type QueryId = Join<Left, Right, Kind::QueryId>;
70
71    const HAS_STATIC_QUERY_ID: bool =
72        Left::HAS_STATIC_QUERY_ID && Right::HAS_STATIC_QUERY_ID && Kind::HAS_STATIC_QUERY_ID;
73}
74
75#[derive(#[automatically_derived]
impl<Join: ::core::fmt::Debug, On: ::core::fmt::Debug> ::core::fmt::Debug for
    JoinOn<Join, On> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "JoinOn",
            "join", &self.join, "on", &&self.on)
    }
}Debug, #[automatically_derived]
impl<Join: ::core::clone::Clone, On: ::core::clone::Clone>
    ::core::clone::Clone for JoinOn<Join, On> {
    #[inline]
    fn clone(&self) -> JoinOn<Join, On> {
        JoinOn {
            join: ::core::clone::Clone::clone(&self.join),
            on: ::core::clone::Clone::clone(&self.on),
        }
    }
}Clone, #[automatically_derived]
impl<Join: ::core::marker::Copy, On: ::core::marker::Copy>
    ::core::marker::Copy for JoinOn<Join, On> {
}Copy, const _: () =
    {
        use diesel;
        #[allow(non_camel_case_types)]
        impl<Join: diesel::query_builder::QueryId,
            On: diesel::query_builder::QueryId> diesel::query_builder::QueryId
            for JoinOn<Join, On> {
            type QueryId =
                JoinOn<<Join as diesel::query_builder::QueryId>::QueryId,
                <On as diesel::query_builder::QueryId>::QueryId>;
            const HAS_STATIC_QUERY_ID: bool =
                <Join as diesel::query_builder::QueryId>::HAS_STATIC_QUERY_ID
                        &&
                        <On as diesel::query_builder::QueryId>::HAS_STATIC_QUERY_ID
                    && true;
            const IS_WINDOW_FUNCTION: bool =
                <Join as diesel::query_builder::QueryId>::IS_WINDOW_FUNCTION
                        ||
                        <On as diesel::query_builder::QueryId>::IS_WINDOW_FUNCTION
                    || false;
        }
    };QueryId)]
76#[doc(hidden)]
77/// A query source representing the join between two tables with an explicit
78/// `ON` given. `Join` should usually be referenced instead, as all "type
79/// safety" traits are implemented in terms of `Join` implementing them.
80pub struct JoinOn<Join, On> {
81    join: Join,
82    on: On,
83}
84
85impl<Left, Right, Kind> Join<Left, Right, Kind>
86where
87    Left: QuerySource,
88    Right: QuerySource,
89{
90    pub(crate) fn new(left: Left, right: Right, kind: Kind) -> Self {
91        Join {
92            left: FromClause::new(left),
93            right: FromClause::new(right),
94            kind,
95        }
96    }
97
98    pub(crate) fn on<On>(self, on: On) -> JoinOn<Self, On> {
99        JoinOn { join: self, on: on }
100    }
101}
102
103impl<Left, Right> QuerySource for Join<Left, Right, Inner>
104where
105    Left: QuerySource + AppendSelection<Right::DefaultSelection>,
106    Right: QuerySource,
107    Left::Output: AppearsOnTable<Self>,
108    Self: Clone,
109{
110    type FromClause = Self;
111    // combining two valid selectable expressions for both tables will always yield a
112    // valid selectable expressions for the whole join, so no need to check that here
113    // again. These checked turned out to be quite expensive in terms of compile time
114    // so we use a wrapper type to just skip the check and forward other more relevant
115    // trait implementations to the inner type
116    //
117    // See https://github.com/diesel-rs/diesel/issues/3223 for details
118    type DefaultSelection = self::private::SkipSelectableExpressionBoundCheckWrapper<Left::Output>;
119
120    fn from_clause(&self) -> Self::FromClause {
121        self.clone()
122    }
123
124    fn default_selection(&self) -> Self::DefaultSelection {
125        self::private::SkipSelectableExpressionBoundCheckWrapper(
126            self.left
127                .source
128                .append_selection(self.right.source.default_selection()),
129        )
130    }
131}
132
133impl<Left, Right> QuerySource for Join<Left, Right, LeftOuter>
134where
135    Left: QuerySource + AppendSelection<Nullable<Right::DefaultSelection>>,
136    Right: QuerySource,
137    Left::Output: AppearsOnTable<Self>,
138    Self: Clone,
139{
140    type FromClause = Self;
141    // combining two valid selectable expressions for both tables will always yield a
142    // valid selectable expressions for the whole join, so no need to check that here
143    // again. These checked turned out to be quite expensive in terms of compile time
144    // so we use a wrapper type to just skip the check and forward other more relevant
145    // trait implementations to the inner type
146    //
147    // See https://github.com/diesel-rs/diesel/issues/3223 for details
148    type DefaultSelection = self::private::SkipSelectableExpressionBoundCheckWrapper<Left::Output>;
149
150    fn from_clause(&self) -> Self::FromClause {
151        self.clone()
152    }
153
154    fn default_selection(&self) -> Self::DefaultSelection {
155        self::private::SkipSelectableExpressionBoundCheckWrapper(
156            self.left
157                .source
158                .append_selection(self.right.source.default_selection().nullable()),
159        )
160    }
161}
162
163#[derive(#[automatically_derived]
impl ::core::fmt::Debug for OnKeyword {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "OnKeyword")
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for OnKeyword {
    #[inline]
    fn clone(&self) -> OnKeyword { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for OnKeyword { }Copy)]
164pub struct OnKeyword;
165
166impl<DB: Backend> nodes::MiddleFragment<DB> for OnKeyword {
167    fn push_sql(&self, mut pass: AstPass<'_, '_, DB>) {
168        pass.push_sql(" ON ");
169    }
170}
171
172impl<Join, On> QuerySource for JoinOn<Join, On>
173where
174    Join: QuerySource,
175    On: AppearsOnTable<Join::FromClause> + Clone,
176    On::SqlType: BoolOrNullableBool,
177    Join::DefaultSelection: SelectableExpression<Self>,
178{
179    type FromClause = Grouped<nodes::InfixNode<Join::FromClause, On, OnKeyword>>;
180    type DefaultSelection = Join::DefaultSelection;
181
182    fn from_clause(&self) -> Self::FromClause {
183        Grouped(nodes::InfixNode::new(
184            self.join.from_clause(),
185            self.on.clone(),
186            OnKeyword,
187        ))
188    }
189
190    fn default_selection(&self) -> Self::DefaultSelection {
191        self.join.default_selection()
192    }
193}
194
195impl<Left, Right, Kind, DB> QueryFragment<DB> for Join<Left, Right, Kind>
196where
197    DB: Backend + DieselReserveSpecialization,
198    Left: QuerySource,
199    Left::FromClause: QueryFragment<DB>,
200    Right: QuerySource,
201    Right::FromClause: QueryFragment<DB>,
202    Kind: QueryFragment<DB>,
203{
204    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
205        self.left.from_clause.walk_ast(out.reborrow())?;
206        self.kind.walk_ast(out.reborrow())?;
207        out.push_sql(" JOIN ");
208        self.right.from_clause.walk_ast(out.reborrow())?;
209        Ok(())
210    }
211}
212
213/// Indicates that two tables can be joined without an explicit `ON` clause.
214///
215/// Implementations of this trait are generated by invoking [`joinable!`].
216/// Implementing this trait means that you can call
217/// `left_table.inner_join(right_table)`, without supplying the `ON` clause
218/// explicitly. To join two tables which do not implement this trait, you will
219/// need to call [`.on`].
220///
221/// See [`joinable!`] and [`inner_join`] for usage examples.
222///
223/// [`joinable!`]: crate::joinable!
224/// [`.on`]: crate::query_dsl::JoinOnDsl::on()
225/// [`inner_join`]: crate::query_dsl::QueryDsl::inner_join()
226#[diagnostic::on_unimplemented(
227    message = "cannot join `{T}` to `{Self}` due to missing relation",
228    note = "joining tables directly either requires a `diesel::joinable!` definition \
229            or calling `JoinOnDsl::on` to manually specify the `ON` clause of the join`"
230)]
231pub trait JoinTo<T> {
232    #[doc(hidden)]
233    type FromClause;
234    #[doc(hidden)]
235    type OnClause;
236    #[doc(hidden)]
237    fn join_target(rhs: T) -> (Self::FromClause, Self::OnClause);
238}
239
240#[doc(hidden)]
241/// Used to ensure the sql type of `left.join(mid).join(right)` is
242/// `(Left, Mid, Right)` and not `((Left, Mid), Right)`. This needs
243/// to be separate from `TupleAppend` because we still want to keep
244/// the column lists (which are tuples) separate.
245pub trait AppendSelection<Selection> {
246    type Output;
247
248    fn append_selection(&self, selection: Selection) -> Self::Output;
249}
250
251impl<T, Selection> AppendSelection<Selection> for T
252where
253    T: QueryRelation,
254{
255    type Output = (T::AllColumns, Selection);
256
257    fn append_selection(&self, selection: Selection) -> Self::Output {
258        (T::all_columns(), selection)
259    }
260}
261
262impl<Left, Mid, Selection, Kind> AppendSelection<Selection> for Join<Left, Mid, Kind>
263where
264    Left: QuerySource,
265    Mid: QuerySource,
266    Self: QuerySource,
267    <Self as QuerySource>::DefaultSelection: TupleAppend<Selection>,
268{
269    type Output = <<Self as QuerySource>::DefaultSelection as TupleAppend<Selection>>::Output;
270
271    fn append_selection(&self, selection: Selection) -> Self::Output {
272        self.default_selection().tuple_append(selection)
273    }
274}
275
276impl<Join, On, Selection> AppendSelection<Selection> for JoinOn<Join, On>
277where
278    Join: AppendSelection<Selection>,
279{
280    type Output = Join::Output;
281
282    fn append_selection(&self, selection: Selection) -> Self::Output {
283        self.join.append_selection(selection)
284    }
285}
286
287#[doc(hidden)]
288#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Inner {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "Inner")
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for Inner {
    #[inline]
    fn clone(&self) -> Inner { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for Inner { }Copy, #[automatically_derived]
impl ::core::default::Default for Inner {
    #[inline]
    fn default() -> Inner { Inner {} }
}Default, const _: () =
    {
        use diesel;
        #[allow(non_camel_case_types)]
        impl diesel::query_builder::QueryId for Inner {
            type QueryId = Inner<>;
            const HAS_STATIC_QUERY_ID: bool = true;
            const IS_WINDOW_FUNCTION: bool = false;
        }
    };QueryId)]
289pub struct Inner;
290
291impl<DB> QueryFragment<DB> for Inner
292where
293    DB: Backend + DieselReserveSpecialization,
294{
295    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
296        out.push_sql(" INNER");
297        Ok(())
298    }
299}
300
301#[doc(hidden)]
302#[derive(#[automatically_derived]
impl ::core::fmt::Debug for LeftOuter {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "LeftOuter")
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for LeftOuter {
    #[inline]
    fn clone(&self) -> LeftOuter { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for LeftOuter { }Copy, #[automatically_derived]
impl ::core::default::Default for LeftOuter {
    #[inline]
    fn default() -> LeftOuter { LeftOuter {} }
}Default, const _: () =
    {
        use diesel;
        #[allow(non_camel_case_types)]
        impl diesel::query_builder::QueryId for LeftOuter {
            type QueryId = LeftOuter<>;
            const HAS_STATIC_QUERY_ID: bool = true;
            const IS_WINDOW_FUNCTION: bool = false;
        }
    };QueryId)]
303pub struct LeftOuter;
304
305impl<DB> QueryFragment<DB> for LeftOuter
306where
307    DB: Backend + DieselReserveSpecialization,
308{
309    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
310        out.push_sql(" LEFT OUTER");
311        Ok(())
312    }
313}
314
315impl<Left, Mid, Right, Kind> JoinTo<Right> for Join<Left, Mid, Kind>
316where
317    Left: JoinTo<Right> + QuerySource,
318    Mid: QuerySource,
319{
320    type FromClause = <Left as JoinTo<Right>>::FromClause;
321    type OnClause = Left::OnClause;
322
323    fn join_target(rhs: Right) -> (Self::FromClause, Self::OnClause) {
324        Left::join_target(rhs)
325    }
326}
327
328impl<Join, On, Right> JoinTo<Right> for JoinOn<Join, On>
329where
330    Join: JoinTo<Right>,
331{
332    type FromClause = Join::FromClause;
333    type OnClause = Join::OnClause;
334
335    fn join_target(rhs: Right) -> (Self::FromClause, Self::OnClause) {
336        Join::join_target(rhs)
337    }
338}
339
340impl<T, Left, Right, Kind> AppearsInFromClause<T> for Join<Left, Right, Kind>
341where
342    Left: AppearsInFromClause<T> + QuerySource,
343    Right: AppearsInFromClause<T> + QuerySource,
344    Left::Count: Plus<Right::Count>,
345{
346    type Count = <Left::Count as Plus<Right::Count>>::Output;
347}
348
349impl<T, Join, On> AppearsInFromClause<T> for JoinOn<Join, On>
350where
351    Join: AppearsInFromClause<T>,
352{
353    type Count = Join::Count;
354}
355
356#[doc(hidden)]
357#[derive(#[automatically_derived]
impl<Source: ::core::fmt::Debug, On: ::core::fmt::Debug> ::core::fmt::Debug
    for OnClauseWrapper<Source, On> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "OnClauseWrapper", "source", &self.source, "on", &&self.on)
    }
}Debug, #[automatically_derived]
impl<Source: ::core::clone::Clone, On: ::core::clone::Clone>
    ::core::clone::Clone for OnClauseWrapper<Source, On> {
    #[inline]
    fn clone(&self) -> OnClauseWrapper<Source, On> {
        OnClauseWrapper {
            source: ::core::clone::Clone::clone(&self.source),
            on: ::core::clone::Clone::clone(&self.on),
        }
    }
}Clone, #[automatically_derived]
impl<Source: ::core::marker::Copy, On: ::core::marker::Copy>
    ::core::marker::Copy for OnClauseWrapper<Source, On> {
}Copy)]
358pub struct OnClauseWrapper<Source, On> {
359    pub(crate) source: Source,
360    pub(crate) on: On,
361}
362
363impl<Source, On> OnClauseWrapper<Source, On> {
364    pub fn new(source: Source, on: On) -> Self {
365        OnClauseWrapper { source, on }
366    }
367}
368
369impl<Lhs, Rhs, On> JoinTo<OnClauseWrapper<Rhs, On>> for Lhs
370where
371    Lhs: QueryRelation,
372{
373    type FromClause = Rhs;
374    type OnClause = On;
375
376    fn join_target(rhs: OnClauseWrapper<Rhs, On>) -> (Self::FromClause, Self::OnClause) {
377        (rhs.source, rhs.on)
378    }
379}
380
381impl<Lhs, Rhs, On> JoinTo<Rhs> for OnClauseWrapper<Lhs, On>
382where
383    Lhs: JoinTo<Rhs>,
384{
385    type FromClause = <Lhs as JoinTo<Rhs>>::FromClause;
386    type OnClause = <Lhs as JoinTo<Rhs>>::OnClause;
387
388    fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) {
389        <Lhs as JoinTo<Rhs>>::join_target(rhs)
390    }
391}
392
393impl<Rhs, Kind, On1, On2, Lhs> InternalJoinDsl<Rhs, Kind, On1> for OnClauseWrapper<Lhs, On2>
394where
395    Lhs: InternalJoinDsl<Rhs, Kind, On1>,
396{
397    type Output = OnClauseWrapper<<Lhs as InternalJoinDsl<Rhs, Kind, On1>>::Output, On2>;
398
399    fn join(self, rhs: Rhs, kind: Kind, on: On1) -> Self::Output {
400        OnClauseWrapper {
401            source: self.source.join(rhs, kind, on),
402            on: self.on,
403        }
404    }
405}
406
407impl<Qs, On> QueryDsl for OnClauseWrapper<Qs, On> {}
408
409#[doc(hidden)]
410/// Convert any joins in a `FROM` clause into an inner join.
411///
412/// This trait is used to determine whether
413/// `Nullable<T>: SelectableExpression<SomeJoin>`. We consider it to be
414/// selectable if `T: SelectableExpression<InnerJoin>`. Since `SomeJoin`
415/// may be deeply nested, we need to recursively change any appearances of
416/// `LeftOuter` to `Inner` in order to perform this check.
417pub trait ToInnerJoin {
418    type InnerJoin;
419}
420
421impl<Left, Right, Kind> ToInnerJoin for Join<Left, Right, Kind>
422where
423    Left: ToInnerJoin + QuerySource,
424    Left::InnerJoin: QuerySource,
425    Right: ToInnerJoin + QuerySource,
426    Right::InnerJoin: QuerySource,
427{
428    type InnerJoin = Join<Left::InnerJoin, Right::InnerJoin, Inner>;
429}
430
431impl<Join, On> ToInnerJoin for JoinOn<Join, On>
432where
433    Join: ToInnerJoin,
434{
435    type InnerJoin = JoinOn<Join::InnerJoin, On>;
436}
437
438impl<From> ToInnerJoin for SelectStatement<FromClause<From>>
439where
440    From: ToInnerJoin + QuerySource,
441    From::InnerJoin: QuerySource,
442{
443    type InnerJoin = SelectStatement<FromClause<From::InnerJoin>>;
444}
445
446impl<T: Table> ToInnerJoin for T {
447    type InnerJoin = T;
448}
449
450mod private {
451    use crate::backend::Backend;
452    use crate::expression::{Expression, ValidGrouping};
453    use crate::query_builder::{AstPass, QueryFragment, SelectClauseExpression};
454    use crate::{AppearsOnTable, QueryResult, SelectableExpression};
455
456    #[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug> ::core::fmt::Debug for
    SkipSelectableExpressionBoundCheckWrapper<T> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f,
            "SkipSelectableExpressionBoundCheckWrapper", &&self.0)
    }
}Debug, const _: () =
    {
        use diesel;
        #[allow(non_camel_case_types)]
        impl<T: diesel::query_builder::QueryId> diesel::query_builder::QueryId
            for SkipSelectableExpressionBoundCheckWrapper<T> {
            type QueryId =
                SkipSelectableExpressionBoundCheckWrapper<<T as
                diesel::query_builder::QueryId>::QueryId>;
            const HAS_STATIC_QUERY_ID: bool =
                <T as diesel::query_builder::QueryId>::HAS_STATIC_QUERY_ID &&
                    true;
            const IS_WINDOW_FUNCTION: bool =
                <T as diesel::query_builder::QueryId>::IS_WINDOW_FUNCTION ||
                    false;
        }
    };crate::query_builder::QueryId, #[automatically_derived]
impl<T: ::core::marker::Copy> ::core::marker::Copy for
    SkipSelectableExpressionBoundCheckWrapper<T> {
}Copy, #[automatically_derived]
impl<T: ::core::clone::Clone> ::core::clone::Clone for
    SkipSelectableExpressionBoundCheckWrapper<T> {
    #[inline]
    fn clone(&self) -> SkipSelectableExpressionBoundCheckWrapper<T> {
        SkipSelectableExpressionBoundCheckWrapper(::core::clone::Clone::clone(&self.0))
    }
}Clone)]
457    pub struct SkipSelectableExpressionBoundCheckWrapper<T>(pub(super) T);
458
459    impl<DB, T> QueryFragment<DB> for SkipSelectableExpressionBoundCheckWrapper<T>
460    where
461        T: QueryFragment<DB>,
462        DB: Backend,
463    {
464        fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
465            self.0.walk_ast(pass)
466        }
467    }
468
469    // The default select clause is only valid for no group by clause
470    // anyway so we can just skip the recursive check here
471    impl<T> ValidGrouping<()> for SkipSelectableExpressionBoundCheckWrapper<T> {
472        type IsAggregate = crate::expression::is_aggregate::No;
473    }
474
475    // This needs to use the expression impl
476    impl<QS, T> SelectClauseExpression<QS> for SkipSelectableExpressionBoundCheckWrapper<T>
477    where
478        T: SelectClauseExpression<QS>,
479    {
480        type Selection = T::Selection;
481
482        type SelectClauseSqlType = T::SelectClauseSqlType;
483    }
484
485    // The default select clause for joins is always valid assuming that
486    // the default select clause of all involved query sources is
487    // valid too. We can skip the recursive check here.
488    // This is the main optimization.
489    impl<QS, T> SelectableExpression<QS> for SkipSelectableExpressionBoundCheckWrapper<T> where
490        Self: AppearsOnTable<QS>
491    {
492    }
493
494    impl<QS, T> AppearsOnTable<QS> for SkipSelectableExpressionBoundCheckWrapper<T> where
495        Self: Expression
496    {
497    }
498
499    // Expression must recurse the whole expression
500    // as this is required for the return type of the query
501    impl<T> Expression for SkipSelectableExpressionBoundCheckWrapper<T>
502    where
503        T: Expression,
504    {
505        type SqlType = T::SqlType;
506    }
507
508    impl<T, Selection> crate::util::TupleAppend<Selection>
509        for SkipSelectableExpressionBoundCheckWrapper<T>
510    where
511        T: crate::util::TupleAppend<Selection>,
512    {
513        // We're re-wrapping after anyway
514        type Output = T::Output;
515
516        fn tuple_append(self, right: Selection) -> Self::Output {
517            self.0.tuple_append(right)
518        }
519    }
520}