diesel/query_source/aliasing/macros.rs
1/// Declare a new alias for a table
2///
3/// This macro creates a value of the type [`Alias`](super::Alias)
4///
5/// Example usage
6/// -------------
7/// ```rust
8/// # include!("../../doctest_setup.rs");
9/// fn main() {
10/// use schema::users;
11/// let connection = &mut establish_connection();
12/// let (users1, users2) = diesel::alias!(schema::users as user1, schema::users as user2);
13/// # #[cfg(not(feature = "mysql"))] // self joins are not supported with mysql temporary tables
14/// let res = users1
15/// .inner_join(users2.on(users1.field(users::id).eq(users2.field(users::id))))
16/// .select((users1.fields((users::id, users::name)), users2.field(users::name)))
17/// .order_by(users2.field(users::id))
18/// .load::<((i32, String), String)>(connection);
19/// # #[cfg(not(feature = "mysql"))] // self joins are not supported with mysql temporary tables
20/// assert_eq!(
21/// res,
22/// Ok(vec![
23/// ((1, "Sean".to_owned()), "Sean".to_owned()),
24/// ((2, "Tess".to_owned()), "Tess".to_owned()),
25/// ]),
26/// );
27/// }
28/// ```
29///
30///
31/// Make type expressible
32/// ---------------------
33/// It may sometimes be useful to declare an alias at the module level, in such a way that the type
34/// of a query using it can be expressed (to not declare it anonymously).
35///
36/// This can be achieved in the following way
37/// ```rust
38/// # include!("../../doctest_setup.rs");
39/// use diesel::{query_source::Alias, dsl};
40///
41/// diesel::alias!(schema::users as users_alias: UsersAlias);
42/// // or
43/// diesel::alias!{
44/// pub const USERS_ALIAS_2: Alias<UsersAlias2> = schema::users as users_alias_2;
45/// }
46///
47/// fn some_function_that_returns_a_query_fragment(
48/// ) -> dsl::InnerJoin<schema::posts::table, Alias<UsersAlias>>
49/// {
50/// schema::posts::table.inner_join(users_alias)
51/// }
52/// # fn main() {
53/// # some_function_that_returns_a_query_fragment();
54/// # schema::posts::table.inner_join(USERS_ALIAS_2);
55/// # }
56/// ```
57///
58/// Note that you may also use this form within a function, in the following way:
59/// ```rust
60/// # include!("../../doctest_setup.rs");
61/// fn main() {
62/// diesel::alias!(schema::users as users_alias: UsersAlias);
63/// users_alias.inner_join(schema::posts::table);
64/// }
65/// ```
66///
67/// Troubleshooting and limitations
68/// -------------------------------
69/// If you encounter a **compilation error** where "the trait
70/// `AppearsInFromClause<Alias<your_alias>>` is not implemented", when trying to use two aliases to
71/// the same table within a single query note the following two limitations:
72/// - You will need to declare these in a single `alias!` call.
73/// - The path to the table module will have to be expressed in the exact same
74/// manner. (That is, you can do `alias!(schema::users as user1, schema::users as user2)`
75/// or `alias!(users as user1, users as user2)`, but not
76/// `alias!(schema::users as user1, users as user2)`)
77#[macro_export]
78macro_rules! alias {
79 ($($($table: ident)::+ as $alias: ident),* $(,)?) => {{
80 $crate::alias!(NoConst $($($table)::+ as $alias: $alias,)*);
81 ($($crate::query_source::Alias::<$alias>::default()),*)
82 }};
83 ($($($table: ident)::+ as $alias_name: ident: $alias_ty: ident),* $(,)?) => {
84 $crate::alias! {
85 $(
86 pub const $alias_name: Alias<$alias_ty> = $($table)::+ as $alias_name;
87 )*
88 }
89 };
90 ($($vis: vis const $const_name: ident: Alias<$alias_ty: ident> = $($table: ident)::+ as $alias_sql_name: ident);* $(;)?) => {
91 $crate::alias!(NoConst $($($table)::+ as $alias_sql_name: $vis $alias_ty,)*);
92 $(
93 #[allow(non_upper_case_globals)]
94 $vis const $const_name: $crate::query_source::Alias::<$alias_ty> =
95 $crate::query_source::Alias::new($alias_ty { table: $($table)::+::table });
96
97 #[allow(non_camel_case_types)]
98 $vis type $const_name = $crate::query_source::Alias::<$alias_ty>;
99 )*
100 };
101 (NoConst $($($table: ident)::+ as $alias_sql_name: ident: $vis: vis $alias_ty: ident),* $(,)?) => {
102 $(
103 #[allow(non_camel_case_types)]
104 #[derive(Debug, Clone, Copy)]
105 $vis struct $alias_ty {
106 table: $($table)::+::table,
107 }
108
109 impl $crate::query_source::AliasSource for $alias_ty {
110 const NAME: &'static str = stringify!($alias_sql_name);
111 type Target = $($table)::+::table;
112 fn target(&self) -> &Self::Target { &self.table }
113 }
114
115 impl ::std::default::Default for $alias_ty {
116 fn default() -> Self {
117 Self { table: $($table)::+::table }
118 }
119 }
120
121 // impl AppearsInFromClause<Alias<$alias>> for Alias<$alias>
122 impl $crate::internal::alias_macro::AliasAliasAppearsInFromClauseSameTable<$alias_ty, $($table)::+::table> for $alias_ty {
123 type Count = $crate::query_source::Once;
124 }
125 )*
126 $crate::__internal_alias_helper!($(table_ty = $($table)::+::table, table_tt = ($($table)::+), alias_ty = $alias_ty, alias_sql_name = $alias_sql_name;)*);
127 };
128}
129
130#[macro_export]
131#[doc(hidden)]
132/// This only exists to hide internals from the doc
133// The `($left_sql_name) != ($right_sql_name)` condition is not perfect because a user could still
134// cause runtime errors by declaring two aliases to different tables with the same name then use
135// them in the same query, but that would almost have to be voluntary, so this alone should prevent
136// most mistakes.
137macro_rules! __internal_alias_helper {
138 (
139 table_ty = $left_table_ty: ty, table_tt = $left_table_tt: tt, alias_ty = $left_alias: ident, alias_sql_name = $left_sql_name: ident;
140 $(table_ty = $right_table_ty: ty, table_tt = $right_table_tt: tt, alias_ty = $right_alias: ident, alias_sql_name = $right_sql_name: ident;)+
141 ) => {
142 $(
143 $crate::static_cond!{if ($left_table_tt) == ($right_table_tt) {
144 $crate::static_cond!{if ($left_sql_name) != ($right_sql_name) {
145 impl $crate::internal::alias_macro::AliasAliasAppearsInFromClauseSameTable<$left_alias, $left_table_ty>
146 for $right_alias
147 {
148 type Count = $crate::query_source::Never;
149 }
150 impl $crate::internal::alias_macro::AliasAliasAppearsInFromClauseSameTable<$right_alias, $left_table_ty>
151 for $left_alias
152 {
153 type Count = $crate::query_source::Never;
154 }
155 }}
156 }}
157 )*
158 $crate::__internal_alias_helper!($(table_ty = $right_table_ty, table_tt = $right_table_tt, alias_ty = $right_alias, alias_sql_name = $right_sql_name;)+);
159 };
160
161 (table_ty = $left_table_ty: ty, table_tt = $left_table_tt: tt, alias_ty = $left_alias: ident, alias_sql_name = $left_sql_name: ident;) => {};
162 () => {};
163}