Skip to content

Commit

Permalink
Merge pull request #3288 from weiznich/speedup_compile_times_for_joins
Browse files Browse the repository at this point in the history
Speedup compile times for joins
  • Loading branch information
weiznich committed Aug 25, 2022
2 parents 596cdd2 + d3dc7e5 commit 91c16b4
Show file tree
Hide file tree
Showing 11 changed files with 700 additions and 84 deletions.
110 changes: 100 additions & 10 deletions diesel/src/query_source/joins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,41 +106,59 @@ impl<Left, Right> QuerySource for Join<Left, Right, Inner>
where
Left: QuerySource + AppendSelection<Right::DefaultSelection>,
Right: QuerySource,
Left::Output: SelectableExpression<Self>,
Left::Output: AppearsOnTable<Self>,
Self: Clone,
{
type FromClause = Self;
type DefaultSelection = Left::Output;
// combining two valid selectable expressions for both tables will always yield a
// valid selectable expressions for the whole join, so no need to check that here
// again. These checked turned out to be quite expensive in terms of compile time
// so we use a wrapper type to just skip the check and forward other more relevant
// trait implementations to the inner type
//
// See https://github.com/diesel-rs/diesel/issues/3223 for details
type DefaultSelection = self::private::SkipSelectableExpressionBoundCheckWrapper<Left::Output>;

fn from_clause(&self) -> Self::FromClause {
self.clone()
}

fn default_selection(&self) -> Self::DefaultSelection {
self.left
.source
.append_selection(self.right.source.default_selection())
self::private::SkipSelectableExpressionBoundCheckWrapper(
self.left
.source
.append_selection(self.right.source.default_selection()),
)
}
}

impl<Left, Right> QuerySource for Join<Left, Right, LeftOuter>
where
Left: QuerySource + AppendSelection<Nullable<Right::DefaultSelection>>,
Right: QuerySource,
Left::Output: SelectableExpression<Self>,
Left::Output: AppearsOnTable<Self>,
Self: Clone,
{
type FromClause = Self;
type DefaultSelection = Left::Output;
// combining two valid selectable expressions for both tables will always yield a
// valid selectable expressions for the whole join, so no need to check that here
// again. These checked turned out to be quite expensive in terms of compile time
// so we use a wrapper type to just skip the check and forward other more relevant
// trait implementations to the inner type
//
// See https://github.com/diesel-rs/diesel/issues/3223 for details
type DefaultSelection = self::private::SkipSelectableExpressionBoundCheckWrapper<Left::Output>;

fn from_clause(&self) -> Self::FromClause {
self.clone()
}

fn default_selection(&self) -> Self::DefaultSelection {
self.left
.source
.append_selection(self.right.source.default_selection().nullable())
self::private::SkipSelectableExpressionBoundCheckWrapper(
self.left
.source
.append_selection(self.right.source.default_selection().nullable()),
)
}
}

Expand Down Expand Up @@ -422,3 +440,75 @@ where
impl<T: Table> ToInnerJoin for T {
type InnerJoin = T;
}

mod private {
use crate::backend::Backend;
use crate::expression::{Expression, ValidGrouping};
use crate::query_builder::{AstPass, QueryFragment, SelectClauseExpression};
use crate::{AppearsOnTable, QueryResult, SelectableExpression};

#[derive(Debug, crate::query_builder::QueryId, Copy, Clone)]
pub struct SkipSelectableExpressionBoundCheckWrapper<T>(pub(super) T);

impl<DB, T> QueryFragment<DB> for SkipSelectableExpressionBoundCheckWrapper<T>
where
T: QueryFragment<DB>,
DB: Backend,
{
fn walk_ast<'b>(&'b self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> {
self.0.walk_ast(pass)
}
}

// The default select clause is only valid for no group by clause
// anyway so we can just skip the recursive check here
impl<T> ValidGrouping<()> for SkipSelectableExpressionBoundCheckWrapper<T> {
type IsAggregate = crate::expression::is_aggregate::No;
}

// This needs to use the expression impl
impl<QS, T> SelectClauseExpression<QS> for SkipSelectableExpressionBoundCheckWrapper<T>
where
T: SelectClauseExpression<QS>,
{
type Selection = T::Selection;

type SelectClauseSqlType = T::SelectClauseSqlType;
}

// The default select clause for joins is always valid assuming that
// the default select clause of all involved query sources is
// valid too. We can skip the recursive check here.
// This is the main optimization.
impl<QS, T> SelectableExpression<QS> for SkipSelectableExpressionBoundCheckWrapper<T> where
Self: AppearsOnTable<QS>
{
}

impl<QS, T> AppearsOnTable<QS> for SkipSelectableExpressionBoundCheckWrapper<T> where
Self: Expression
{
}

// Expression must recurse the whole expression
// as this is required for the return type of the query
impl<T> Expression for SkipSelectableExpressionBoundCheckWrapper<T>
where
T: Expression,
{
type SqlType = T::SqlType;
}

impl<T, Selection> crate::util::TupleAppend<Selection>
for SkipSelectableExpressionBoundCheckWrapper<T>
where
T: crate::util::TupleAppend<Selection>,
{
// We're re-wrapping after anyway
type Output = T::Output;

fn tuple_append(self, right: Selection) -> Self::Output {
self.0.tuple_append(right)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ error[E0277]: Cannot select `f64` from `NoFromClause`
<(T0, T1, T2, T3, T4, T5) as SelectableExpression<QS>>
<(T0, T1, T2, T3, T4, T5, T6) as SelectableExpression<QS>>
<(T0, T1, T2, T3, T4, T5, T6, T7) as SelectableExpression<QS>>
and 154 others
and 155 others
= note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `(f64, f64)`
= note: 1 redundant requirement hidden
= note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>`
Expand Down Expand Up @@ -42,7 +42,7 @@ error[E0277]: the trait bound `f64: ValidGrouping<()>` is not satisfied
<(T0, T1, T2, T3, T4, T5) as ValidGrouping<__GroupByClause>>
<(T0, T1, T2, T3, T4, T5, T6) as ValidGrouping<__GroupByClause>>
<(T0, T1, T2, T3, T4, T5, T6, T7) as ValidGrouping<__GroupByClause>>
and 139 others
and 140 others
= note: required because of the requirements on the impl of `ValidGrouping<()>` for `(f64, f64)`
= note: 1 redundant requirement hidden
= note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>`
Expand Down Expand Up @@ -70,7 +70,7 @@ error[E0277]: Cannot select `f64` from `NoFromClause`
<(T0, T1, T2, T3, T4, T5) as SelectableExpression<QS>>
<(T0, T1, T2, T3, T4, T5, T6) as SelectableExpression<QS>>
<(T0, T1, T2, T3, T4, T5, T6, T7) as SelectableExpression<QS>>
and 154 others
and 155 others
= note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `(f64, f64)`
= note: 1 redundant requirement hidden
= note: required because of the requirements on the impl of `SelectableExpression<NoFromClause>` for `diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>`
Expand Down Expand Up @@ -98,7 +98,7 @@ error[E0277]: the trait bound `f64: ValidGrouping<()>` is not satisfied
<(T0, T1, T2, T3, T4, T5) as ValidGrouping<__GroupByClause>>
<(T0, T1, T2, T3, T4, T5, T6) as ValidGrouping<__GroupByClause>>
<(T0, T1, T2, T3, T4, T5, T6, T7) as ValidGrouping<__GroupByClause>>
and 139 others
and 140 others
= note: required because of the requirements on the impl of `ValidGrouping<()>` for `(f64, f64)`
= note: 1 redundant requirement hidden
= note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>`
Expand All @@ -125,7 +125,7 @@ error[E0277]: the trait bound `f64: QueryId` is not satisfied
(T0, T1, T2, T3, T4)
(T0, T1, T2, T3, T4, T5)
(T0, T1, T2, T3, T4, T5, T6)
and 228 others
and 229 others
= note: required because of the requirements on the impl of `QueryId` for `(f64, f64)`
= note: 3 redundant requirements hidden
= note: required because of the requirements on the impl of `QueryId` for `SelectStatement<NoFromClause, diesel::query_builder::select_clause::SelectClause<diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>>>`
Expand All @@ -151,7 +151,7 @@ error[E0277]: the trait bound `f64: QueryFragment<Pg>` is not satisfied
<(T0, T1, T2, T3, T4) as QueryFragment<__DB>>
<(T0, T1, T2, T3, T4, T5) as QueryFragment<__DB>>
<(T0, T1, T2, T3, T4, T5, T6) as QueryFragment<__DB>>
and 267 others
and 268 others
= note: required because of the requirements on the impl of `QueryFragment<Pg>` for `(f64, f64)`
= note: 4 redundant requirements hidden
= note: required because of the requirements on the impl of `QueryFragment<Pg>` for `SelectStatement<NoFromClause, diesel::query_builder::select_clause::SelectClause<diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>>>`
Expand All @@ -177,7 +177,7 @@ error[E0277]: the trait bound `f64: diesel::Expression` is not satisfied
(T0, T1, T2, T3, T4, T5)
(T0, T1, T2, T3, T4, T5, T6)
(T0, T1, T2, T3, T4, T5, T6, T7)
and 119 others
and 120 others
= note: required because of the requirements on the impl of `AsExpression<diesel::sql_types::Integer>` for `f64`
= note: required because of the requirements on the impl of `AsExpressionList<diesel::sql_types::Integer>` for `(f64, f64)`
note: required by a bound in `diesel::dsl::array`
Expand All @@ -201,5 +201,5 @@ error[E0277]: the trait bound `f64: diesel::Expression` is not satisfied
(T0, T1, T2, T3, T4, T5)
(T0, T1, T2, T3, T4, T5, T6)
(T0, T1, T2, T3, T4, T5, T6, T7)
and 119 others
and 120 others
= note: required because of the requirements on the impl of `AsExpression<diesel::sql_types::Integer>` for `f64`
Loading

0 comments on commit 91c16b4

Please sign in to comment.