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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
use query_builder::AsQuery; use query_source::{Table, JoinTo, QuerySource}; use query_source::joins::{self, OnClauseWrapper}; #[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<<Lhs as JoinTo<Rhs>>::FromClause, Kind, <Lhs as JoinTo<Rhs>>::OnClause>, { type Output = <Lhs as InternalJoinDsl<Lhs::FromClause, Kind, Lhs::OnClause>>::Output; fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output { let (from, on) = Lhs::join_target(rhs); self.join(from, kind, on) } } /// 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 { } pub trait JoinOnDsl: Sized { /// Specify the `ON` clause for a join statement. This will override /// any implicit `ON` clause that would come from `#[belongs_to]` /// /// # Example /// /// ```rust /// # #[macro_use] extern crate diesel; /// # include!("src/doctest_setup.rs"); /// # /// # table! { /// # users { /// # id -> Integer, /// # name -> VarChar, /// # } /// # } /// # /// # table! { /// # posts { /// # id -> Integer, /// # user_id -> Integer, /// # title -> Text, /// # } /// # } /// # /// # enable_multi_table_joins!(users, posts); /// # /// # fn main() { /// # let connection = establish_connection(); /// let data = users::table /// .left_join(posts::table.on( /// users::id.eq(posts::user_id).and( /// posts::title.eq("My first post")) /// )) /// .select((users::name, posts::title.nullable())) /// .load(&connection); /// let expected = vec![ /// ("Sean".to_string(), Some("My first post".to_string())), /// ("Tess".to_string(), None), /// ]; /// assert_eq!(Ok(expected), data); /// # } fn on<On>(self, on: On) -> OnClauseWrapper<Self, On> { OnClauseWrapper::new(self, on) } } impl<T: QuerySource> JoinOnDsl for T {}