Skip to main content

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