1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
use query_builder::AsQuery;
use query_source::{joins, Table, JoinTo};

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

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

impl<T, Rhs, Kind, On> InternalJoinDsl<Rhs, Kind, On> for T where
    T: Table + AsQuery,
    T::Query: InternalJoinDsl<Rhs, Kind, On>,
{
    type Output = <T::Query as InternalJoinDsl<Rhs, Kind, On>>::Output;

    fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output {
        self.as_query().join(rhs, kind, on)
    }
}

#[doc(hidden)]
/// `JoinDsl` support trait to emulate associated type constructors and grab
/// the known on clause from the associations API
pub trait JoinWithImplicitOnClause<Rhs, Kind> {
    type Output: AsQuery;

    fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output;
}

impl<Lhs, Rhs, Kind> JoinWithImplicitOnClause<Rhs, Kind> for Lhs where
    Lhs: JoinTo<Rhs>,
    Lhs: InternalJoinDsl<Rhs, Kind, <Lhs as JoinTo<Rhs>>::JoinOnClause>,
{
    type Output = <Lhs as InternalJoinDsl<Rhs, Kind, Lhs::JoinOnClause>>::Output;

    fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output {
        self.join(rhs, kind, Lhs::join_on_clause())
    }
}

/// Methods allowing various joins between two or more tables.
///
/// Joining between two tables requires a [`#[belongs_to]`
/// association][associations] that defines the relationship.
///
/// You can join to as many tables as you'd like in a query, with the
/// restriction that no table can appear in the query more than once. The reason
/// for this restriction is that one of the appearances would require aliasing,
/// and we do not currently have a fleshed out story for dealing with table
/// aliases.
///
/// You may also need to call [`enable_multi_table_joins!`][] (particularly if
/// you see an unexpected error about `AppearsInFromClause`). See the
/// documentation for [`enable_multi_table_joins!`][] for details.
///
/// Diesel expects multi-table joins to be semantically grouped based on the
/// relationships. For example, `users.inner_join(posts.inner_join(comments))`
/// is not the same as `users.inner_join(posts).inner_join(comments)`. The first
/// would deserialize into `(User, (Post, Comment))` and generate the following
/// SQL:
///
/// ```sql
/// SELECT * FROM users
///     INNER JOIN posts ON posts.user_id = users.id
///     INNER JOIN comments ON comments.post_id = posts.id
/// ```
///
/// While the second query would deserialize into `(User, Post, Comment)` and
/// generate the following SQL:
///
/// ```sql
/// SELECT * FROM users
///     INNER JOIN posts ON posts.user_id = users.id
///     INNER JOIN comments ON comments.user_id = users.id
/// ```
///
/// [associations]: ../associations/index.html
/// [`enable_multi_table_joins!`]: ../macro.enable_multi_table_joins.html
pub trait JoinDsl: Sized {
    /// Join two tables using a SQL `INNER JOIN`. The `ON` clause is defined
    /// via the [associations API](../associations/index.html).
    fn inner_join<Rhs>(self, rhs: Rhs) -> Self::Output where
        Self: JoinWithImplicitOnClause<Rhs, joins::Inner>,
    {
        self.join_with_implicit_on_clause(rhs, joins::Inner)
    }

    /// Join two tables using a SQL `LEFT OUTER JOIN`. The `ON` clause is defined
    /// via the [associations API](../associations/index.html).
    fn left_outer_join<Rhs>(self, rhs: Rhs) -> Self::Output where
        Self: JoinWithImplicitOnClause<Rhs, joins::LeftOuter>,
    {
        self.join_with_implicit_on_clause(rhs, joins::LeftOuter)
    }

    /// Alias for `left_outer_join`
    fn left_join<Rhs>(self, rhs: Rhs) -> Self::Output where
        Self: JoinWithImplicitOnClause<Rhs, joins::LeftOuter>,
    {
        self.left_outer_join(rhs)
    }

}

impl<T: AsQuery> JoinDsl for T {
}