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}