Skip to content

Commit ab3eea7

Browse files
committed
Set up the groundwork for multi-table joins
This solves the fundamental problem of needing `SelectableExpression<Join<Left, Right>> for column` to work when we don't know where the table concretely appears, just that it appears somewhere. With this commit, the logic is now that a column can be selected from any join where its table appears within the join exactly once. This disallows `users.inner_join(posts.inner_join(users))`, which we don't want to allow as it would require the second instance of `users` to be aliased. This does require that there is an `impl ContainsTable<left> for right` for every two tables that would appear. Right now I'm just brute-forcing these when the association between the two tables is defined. For multi-table joins to work when two of the tables in the join do not have an association, this will require a manual impl. We'll probably provide something like `enable_multi_table_joins!(users, comments)` or similar. This *may* go away eventually with specialization, if projections on default associated types are eventually allowed in the fully monomorphic case. It should be noted that this commit does not enable multi-table joins on itself. `Join` still does not implement `JoinDsl`, and tables do not implement `JoinTo<Join<Mid, Right>>`. The code required to enable that is pretty minimal, but I don't want to flip the switch just yet, as we're actually generating invalid SQL at the moment and that needs to be resolved. However, with this change, all of the limitations that prevent multi-table joins at the type level are eliminated.
1 parent 201c8cd commit ab3eea7

3 files changed

Lines changed: 62 additions & 35 deletions

File tree

diesel/src/macros/mod.rs

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,47 +26,31 @@ macro_rules! __diesel_column {
2626
impl SelectableExpression<$($table)::*> for $column_name {
2727
}
2828

29-
impl AppearsOnTable<$($table)::*> for $column_name {
30-
}
31-
32-
impl<Right, Kind> SelectableExpression<
33-
Join<$($table)::*, Right, Kind>,
34-
> for $column_name where
35-
$column_name: AppearsOnTable<Join<$($table)::*, Right, Kind>>,
36-
{
37-
}
38-
39-
impl<Left> SelectableExpression<
40-
Join<Left, $($table)::*, Inner>,
41-
> for $column_name where
42-
Left: $crate::JoinTo<$($table)::*>
29+
impl<QS> AppearsOnTable<QS> for $column_name where
30+
QS: ContainsTable<$($table)::*, Count=Once>,
4331
{
4432
}
4533

46-
impl<Right, Kind> AppearsOnTable<
47-
Join<$($table)::*, Right, Kind>,
34+
impl<Left, Right> SelectableExpression<
35+
Join<Left, Right, LeftOuter>,
4836
> for $column_name where
49-
Right: QuerySource,
50-
$($table)::*: $crate::JoinTo<Right>
37+
$column_name: AppearsOnTable<Join<Left, Right, LeftOuter>>,
38+
Left: ContainsTable<$($table)::*, Count=Once>,
39+
Right: ContainsTable<$($table)::*, Count=Never>,
5140
{
5241
}
5342

54-
impl<Left, Kind> AppearsOnTable<
55-
Join<Left, $($table)::*, Kind>,
43+
impl<Left, Right> SelectableExpression<
44+
Join<Left, Right, Inner>,
5645
> for $column_name where
57-
Left: $crate::JoinTo<$($table)::*>
58-
{
59-
}
60-
61-
// FIXME: Remove this when overlapping marker traits are stable
62-
impl<Join, On> AppearsOnTable<JoinOn<Join, On>> for $column_name where
63-
$column_name: AppearsOnTable<Join>,
46+
$column_name: AppearsOnTable<Join<Left, Right, Inner>>,
47+
Join<Left, Right, Inner>: ContainsTable<$($table)::*, Count=Once>,
6448
{
6549
}
6650

6751
// FIXME: Remove this when overlapping marker traits are stable
6852
impl<Join, On> SelectableExpression<JoinOn<Join, On>> for $column_name where
69-
$column_name: SelectableExpression<Join>,
53+
$column_name: SelectableExpression<Join> + AppearsOnTable<JoinOn<Join, On>>,
7054
{
7155
}
7256

@@ -339,6 +323,7 @@ macro_rules! table_body {
339323
use $crate::associations::HasTable;
340324
use $crate::query_builder::*;
341325
use $crate::query_builder::nodes::Identifier;
326+
use $crate::query_source::{ContainsTable, Once};
342327
$(use $($import)::+;)+
343328
pub use self::columns::*;
344329

@@ -418,6 +403,10 @@ macro_rules! table_body {
418403
}
419404
}
420405

406+
impl ContainsTable<table> for table {
407+
type Count = Once;
408+
}
409+
421410
impl_query_id!(table);
422411

423412
/// Contains all of the columns of this table
@@ -426,7 +415,8 @@ macro_rules! table_body {
426415
use $crate::{Expression, SelectableExpression, AppearsOnTable, QuerySource};
427416
use $crate::backend::Backend;
428417
use $crate::query_builder::{QueryFragment, AstPass};
429-
use $crate::query_source::joins::{Join, JoinOn, Inner};
418+
use $crate::query_source::joins::{Join, JoinOn, Inner, LeftOuter};
419+
use $crate::query_source::{ContainsTable, Once, Never};
430420
use $crate::result::QueryResult;
431421
$(use $($import)::+;)+
432422

@@ -534,6 +524,9 @@ macro_rules! joinable_inner {
534524
primary_key_ty = $primary_key_ty:ty,
535525
primary_key_expr = $primary_key_expr:expr,
536526
) => {
527+
impl $crate::query_source::ContainsTable<$right_table_ty> for $left_table_ty {
528+
type Count = $crate::query_source::Never;
529+
}
537530
impl $crate::JoinTo<$right_table_ty> for $left_table_ty {
538531
type JoinOnClause = $crate::expression::helper_types::Eq<
539532
$crate::expression::nullable::Nullable<$foreign_key>,

diesel/src/query_source/joins.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,6 @@ impl<Left, Right, On, T> SelectableExpression<JoinOn<Join<Left, Right, LeftOuter
133133
{
134134
}
135135

136-
// FIXME: We want these blanket impls when overlapping marker traits are stable
137-
// impl<T, Join, On> AppearsOnTable<JoinOn<Join, On>> for T where
138-
// T: AppearsOnTable<Join>,
139-
// {
140-
// }
141-
142136
// FIXME: We want these blanket impls when overlapping marker traits are stable
143137
// impl<T, Join, On> SelectableExpression<JoinOn<Join, On>> for T where
144138
// T: SelectableExpression<Join> + AppearsOnTable<JoinOn<Join, On>>,
@@ -180,3 +174,33 @@ impl<DB: Backend> QueryFragment<DB> for LeftOuter {
180174
Ok(())
181175
}
182176
}
177+
178+
use super::{Succ, Never, ContainsTable};
179+
180+
impl<T, Left, Right, Kind> ContainsTable<T> for Join<Left, Right, Kind> where
181+
Left: ContainsTable<T>,
182+
Right: ContainsTable<T>,
183+
Left::Count: Plus<Right::Count>,
184+
{
185+
type Count = <Left::Count as Plus<Right::Count>>::Output;
186+
}
187+
188+
impl<T, Join, On> ContainsTable<T> for JoinOn<Join, On> where
189+
Join: ContainsTable<T>,
190+
{
191+
type Count = Join::Count;
192+
}
193+
194+
pub trait Plus<T> {
195+
type Output;
196+
}
197+
198+
impl<T, U> Plus<T> for Succ<U> where
199+
U: Plus<T>,
200+
{
201+
type Output = Succ<U::Output>;
202+
}
203+
204+
impl<T> Plus<T> for Never {
205+
type Output = T;
206+
}

diesel/src/query_source/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,13 @@ pub trait Table: QuerySource + AsQuery + Sized {
4848
fn primary_key(&self) -> Self::PrimaryKey;
4949
fn all_columns() -> Self::AllColumns;
5050
}
51+
52+
pub trait ContainsTable<QS> {
53+
type Count;
54+
}
55+
56+
#[allow(missing_debug_implementations, missing_copy_implementations)]
57+
pub struct Never;
58+
#[allow(missing_debug_implementations, missing_copy_implementations)]
59+
pub struct Succ<T>(T);
60+
pub type Once = Succ<Never>;

0 commit comments

Comments
 (0)