Skip to content

Commit b17bb9c

Browse files
committed
Refactor join construction to be much more composeable
This moves most of the knowledge of join construction out of `JoinTo` and into the join methods themselves. Aside from exposing less of our internals, this is useful to us because it's much easier to implement `JoinTo` on joins themselves than it is to implement the specific join methods. The downside to this approach is that it gives us less specific control over certain types, and means that `users.inner_join(posts).inner_join(settings)` will likely have the type `(User, (Post, Setting))` instead of `(User, Post, Setting)` which will make it unfortunately difficult to differentiate `user.inner_join(posts).inner_join(comments)` from `user.inner_join(posts.inner_join(comments))`. Still, I think this is the path that will get us to *some* form of multi-table joins, and we can always refine later. This structure should make it fairly simple to add all of: - Joining to subselects - Cross joins - Join with explicit on-clauses - Self-referential joins (though not through the associations API) Additionally, the only real blocker remaining for multi-table joins is implementing `SelectableExpression` which should hopefully be unblocked by Rust soon.
1 parent b85e476 commit b17bb9c

6 files changed

Lines changed: 182 additions & 103 deletions

File tree

diesel/src/macros/mod.rs

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -43,37 +43,49 @@ macro_rules! __diesel_column {
4343
impl<Left> SelectableExpression<
4444
Join<Left, $($table)::*, Inner>,
4545
> for $column_name where
46-
Left: $crate::JoinTo<$($table)::*, Inner>
46+
Left: $crate::JoinTo<$($table)::*>
4747
{
4848
}
4949

5050
impl<Right> AppearsOnTable<
5151
Join<$($table)::*, Right, Inner>,
5252
> for $column_name where
5353
Right: Table,
54-
$($table)::*: $crate::JoinTo<Right, Inner>
54+
$($table)::*: $crate::JoinTo<Right>
5555
{
5656
}
5757

5858
impl<Left> AppearsOnTable<
5959
Join<Left, $($table)::*, Inner>,
6060
> for $column_name where
61-
Left: $crate::JoinTo<$($table)::*, Inner>
61+
Left: $crate::JoinTo<$($table)::*>
6262
{
6363
}
6464

6565
impl<Right> AppearsOnTable<
6666
Join<$($table)::*, Right, LeftOuter>,
6767
> for $column_name where
6868
Right: Table,
69-
$($table)::*: $crate::JoinTo<Right, LeftOuter>
69+
$($table)::*: $crate::JoinTo<Right>
7070
{
7171
}
7272

7373
impl<Left> AppearsOnTable<
7474
Join<Left, $($table)::*, LeftOuter>,
7575
> for $column_name where
76-
Left: $crate::JoinTo<$($table)::*, LeftOuter>
76+
Left: $crate::JoinTo<$($table)::*>
77+
{
78+
}
79+
80+
// FIXME: Remove this when overlapping marker traits are stable
81+
impl<Join, On> AppearsOnTable<JoinOn<Join, On>> for $column_name where
82+
$column_name: AppearsOnTable<Join>,
83+
{
84+
}
85+
86+
// FIXME: Remove this when overlapping marker traits are stable
87+
impl<Join, On> SelectableExpression<JoinOn<Join, On>> for $column_name where
88+
$column_name: SelectableExpression<Join>,
7789
{
7890
}
7991

@@ -433,7 +445,7 @@ macro_rules! table_body {
433445
use $crate::{Table, Expression, SelectableExpression, AppearsOnTable, QuerySource};
434446
use $crate::backend::Backend;
435447
use $crate::query_builder::{QueryBuilder, BuildQueryResult, QueryFragment, AstPass};
436-
use $crate::query_source::joins::{Join, Inner, LeftOuter};
448+
use $crate::query_source::joins::{Join, JoinOn, Inner, LeftOuter};
437449
use $crate::result::QueryResult;
438450
$(use $($import)::+;)+
439451

@@ -545,26 +557,16 @@ macro_rules! joinable_inner {
545557
primary_key_ty = $primary_key_ty:ty,
546558
primary_key_expr = $primary_key_expr:expr,
547559
) => {
548-
impl<JoinType> $crate::JoinTo<$right_table_ty, JoinType> for $left_table_ty {
549-
type JoinClause = $crate::query_builder::nodes::Join<
550-
<$left_table_ty as $crate::QuerySource>::FromClause,
551-
<$right_table_ty as $crate::QuerySource>::FromClause,
552-
$crate::expression::helper_types::Eq<
553-
$crate::expression::nullable::Nullable<$foreign_key>,
554-
$crate::expression::nullable::Nullable<$primary_key_ty>,
555-
>,
556-
JoinType,
560+
impl $crate::JoinTo<$right_table_ty> for $left_table_ty {
561+
type JoinOnClause = $crate::expression::helper_types::Eq<
562+
$crate::expression::nullable::Nullable<$foreign_key>,
563+
$crate::expression::nullable::Nullable<$primary_key_ty>,
557564
>;
558565

559-
fn join_clause(&self, join_type: JoinType) -> Self::JoinClause {
560-
use $crate::{QuerySource, ExpressionMethods};
566+
fn join_on_clause() -> Self::JoinOnClause {
567+
use $crate::ExpressionMethods;
561568

562-
$crate::query_builder::nodes::Join::new(
563-
self.from_clause(),
564-
$right_table_expr.from_clause(),
565-
$foreign_key.nullable().eq($primary_key_expr.nullable()),
566-
join_type,
567-
)
569+
$foreign_key.nullable().eq($primary_key_expr.nullable())
568570
}
569571
}
570572
}

diesel/src/query_builder/nodes/mod.rs

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,51 +15,6 @@ impl<'a, DB: Backend> QueryFragment<DB> for Identifier<'a> {
1515
}
1616
}
1717

18-
#[derive(Debug, Copy, Clone)]
19-
pub struct Join<T, U, V, W> {
20-
lhs: T,
21-
rhs: U,
22-
predicate: V,
23-
join_type: W,
24-
}
25-
26-
impl<T, U, V, W> Join<T, U, V, W> {
27-
pub fn new(lhs: T, rhs: U, predicate: V, join_type: W) -> Self {
28-
Join {
29-
lhs: lhs,
30-
rhs: rhs,
31-
predicate: predicate,
32-
join_type: join_type,
33-
}
34-
}
35-
}
36-
37-
impl<T, U, V, W, DB> QueryFragment<DB> for Join<T, U, V, W> where
38-
DB: Backend,
39-
T: QueryFragment<DB>,
40-
U: QueryFragment<DB>,
41-
V: QueryFragment<DB>,
42-
W: QueryFragment<DB>,
43-
{
44-
fn to_sql(&self, out: &mut DB::QueryBuilder) -> BuildQueryResult {
45-
try!(self.lhs.to_sql(out));
46-
try!(self.join_type.to_sql(out));
47-
out.push_sql(" JOIN ");
48-
try!(self.rhs.to_sql(out));
49-
out.push_sql(" ON ");
50-
try!(self.predicate.to_sql(out));
51-
Ok(())
52-
}
53-
54-
fn walk_ast(&self, pass: &mut AstPass<DB>) -> QueryResult<()> {
55-
self.lhs.walk_ast(pass)?;
56-
self.join_type.walk_ast(pass)?;
57-
self.rhs.walk_ast(pass)?;
58-
self.predicate.walk_ast(pass)?;
59-
Ok(())
60-
}
61-
}
62-
6318
#[derive(Debug, Copy, Clone)]
6419
pub struct InfixNode<'a, T, U> {
6520
lhs: T,

diesel/src/query_builder/select_statement/dsl_impls.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,20 @@ use query_builder::{AsQuery, Query, QueryFragment, SelectStatement};
1313
use query_dsl::*;
1414
use query_dsl::boxed_dsl::InternalBoxedDsl;
1515
use query_source::QuerySource;
16-
use query_source::joins::Join;
16+
use query_source::joins::{Join, JoinOn, JoinTo};
1717
use super::BoxedSelectStatement;
1818
use types::{self, Bool};
1919

20-
impl<F, S, D, W, O, L, Of, G, Rhs, Kind> InternalJoinDsl<Rhs, Kind>
20+
impl<F, S, D, W, O, L, Of, G, Rhs, Kind, On> InternalJoinDsl<Rhs, Kind, On>
2121
for SelectStatement<F, S, D, W, O, L, Of, G> where
22-
SelectStatement<Join<F, Rhs, Kind>, S, D, W, O, L, Of, G>: AsQuery,
22+
SelectStatement<JoinOn<Join<F, Rhs, Kind>, On>, S, D, W, O, L, Of, G>: AsQuery,
2323
{
24-
type Output = SelectStatement<Join<F, Rhs, Kind>, S, D, W, O, L, Of, G>;
24+
type Output = SelectStatement<JoinOn<Join<F, Rhs, Kind>, On>, S, D, W, O, L, Of, G>;
2525

26-
fn join(self, rhs: Rhs, kind: Kind) -> Self::Output {
26+
fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output {
2727
SelectStatement::new(
2828
self.select,
29-
Join::new(self.from, rhs, kind),
29+
Join::new(self.from, rhs, kind).on(on),
3030
self.distinct,
3131
self.where_clause,
3232
self.order,
@@ -276,3 +276,17 @@ impl<F, W> IntoUpdateTarget
276276
}
277277
}
278278
}
279+
280+
// FIXME: Should we disable joining when `.group_by` has been called? Are there
281+
// any other query methods where a join no longer has the same semantics as
282+
// joining on just the table?
283+
impl<F, S, D, W, O, L, Of, G, Rhs> JoinTo<Rhs>
284+
for SelectStatement<F, S, D, W, O, L, Of, G> where
285+
F: JoinTo<Rhs>,
286+
{
287+
type JoinOnClause = F::JoinOnClause;
288+
289+
fn join_on_clause() -> Self::JoinOnClause {
290+
F::join_on_clause()
291+
}
292+
}

diesel/src/query_dsl/join_dsl.rs

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,56 @@
11
use query_builder::AsQuery;
2-
use query_source::{joins, QuerySource};
2+
use query_source::{joins, QuerySource, JoinTo};
33

44
#[doc(hidden)]
55
/// `JoinDsl` support trait to emulate associated type constructors
6-
pub trait InternalJoinDsl<Rhs, Kind> {
6+
pub trait InternalJoinDsl<Rhs, Kind, On> {
77
type Output: AsQuery;
88

9-
fn join(self, rhs: Rhs, kind: Kind) -> Self::Output;
9+
fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output;
1010
}
1111

12-
impl<T, Rhs, Kind> InternalJoinDsl<Rhs, Kind> for T where
12+
impl<T, Rhs, Kind, On> InternalJoinDsl<Rhs, Kind, On> for T where
1313
T: QuerySource + AsQuery,
14-
T::Query: InternalJoinDsl<Rhs, Kind>,
14+
T::Query: InternalJoinDsl<Rhs, Kind, On>,
1515
{
16-
type Output = <T::Query as InternalJoinDsl<Rhs, Kind>>::Output;
16+
type Output = <T::Query as InternalJoinDsl<Rhs, Kind, On>>::Output;
1717

18-
fn join(self, rhs: Rhs, kind: Kind) -> Self::Output {
19-
self.as_query().join(rhs, kind)
18+
fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output {
19+
self.as_query().join(rhs, kind, on)
20+
}
21+
}
22+
23+
#[doc(hidden)]
24+
/// `JoinDsl` support trait to emulate associated type constructors and grab
25+
/// the known on clause from the associations API
26+
pub trait JoinWithImplicitOnClause<Rhs, Kind> {
27+
type Output: AsQuery;
28+
29+
fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output;
30+
}
31+
32+
impl<Lhs, Rhs, Kind> JoinWithImplicitOnClause<Rhs, Kind> for Lhs where
33+
Lhs: JoinTo<Rhs>,
34+
Lhs: InternalJoinDsl<Rhs, Kind, <Lhs as JoinTo<Rhs>>::JoinOnClause>,
35+
{
36+
type Output = <Lhs as InternalJoinDsl<Rhs, Kind, Lhs::JoinOnClause>>::Output;
37+
38+
fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output {
39+
self.join(rhs, kind, Lhs::join_on_clause())
2040
}
2141
}
2242

2343
pub trait JoinDsl: Sized {
2444
fn inner_join<Rhs>(self, rhs: Rhs) -> Self::Output where
25-
Self: InternalJoinDsl<Rhs, joins::Inner>,
45+
Self: JoinWithImplicitOnClause<Rhs, joins::Inner>,
2646
{
27-
self.join(rhs, joins::Inner)
47+
self.join_with_implicit_on_clause(rhs, joins::Inner)
2848
}
2949

3050
fn left_outer_join<Rhs>(self, rhs: Rhs) -> Self::Output where
31-
Self: InternalJoinDsl<Rhs, joins::LeftOuter>,
51+
Self: JoinWithImplicitOnClause<Rhs, joins::LeftOuter>,
3252
{
33-
self.join(rhs, joins::LeftOuter)
53+
self.join_with_implicit_on_clause(rhs, joins::LeftOuter)
3454
}
3555
}
3656

0 commit comments

Comments
 (0)