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}