diesel/query_source/aliasing/macros.rs
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 159 160 161 162 163
/// Declare a new alias for a table
///
/// This macro creates a value of the type [`Alias`](super::Alias)
///
/// Example usage
/// -------------
/// ```rust
/// # include!("../../doctest_setup.rs");
/// fn main() {
/// use schema::users;
/// let connection = &mut establish_connection();
/// let (users1, users2) = diesel::alias!(schema::users as user1, schema::users as user2);
/// # #[cfg(not(feature = "mysql"))] // self joins are not supported with mysql temporary tables
/// let res = users1
/// .inner_join(users2.on(users1.field(users::id).eq(users2.field(users::id))))
/// .select((users1.fields((users::id, users::name)), users2.field(users::name)))
/// .order_by(users2.field(users::id))
/// .load::<((i32, String), String)>(connection);
/// # #[cfg(not(feature = "mysql"))] // self joins are not supported with mysql temporary tables
/// assert_eq!(
/// res,
/// Ok(vec![
/// ((1, "Sean".to_owned()), "Sean".to_owned()),
/// ((2, "Tess".to_owned()), "Tess".to_owned()),
/// ]),
/// );
/// }
/// ```
///
///
/// Make type expressible
/// ---------------------
/// It may sometimes be useful to declare an alias at the module level, in such a way that the type
/// of a query using it can be expressed (to not declare it anonymously).
///
/// This can be achieved in the following way
/// ```rust
/// # include!("../../doctest_setup.rs");
/// use diesel::{query_source::Alias, dsl};
///
/// diesel::alias!(schema::users as users_alias: UsersAlias);
/// // or
/// diesel::alias!{
/// pub const USERS_ALIAS_2: Alias<UsersAlias2> = schema::users as users_alias_2;
/// }
///
/// fn some_function_that_returns_a_query_fragment(
/// ) -> dsl::InnerJoin<schema::posts::table, Alias<UsersAlias>>
/// {
/// schema::posts::table.inner_join(users_alias)
/// }
/// # fn main() {
/// # some_function_that_returns_a_query_fragment();
/// # schema::posts::table.inner_join(USERS_ALIAS_2);
/// # }
/// ```
///
/// Note that you may also use this form within a function, in the following way:
/// ```rust
/// # include!("../../doctest_setup.rs");
/// fn main() {
/// diesel::alias!(schema::users as users_alias: UsersAlias);
/// users_alias.inner_join(schema::posts::table);
/// }
/// ```
///
/// Troubleshooting and limitations
/// -------------------------------
/// If you encounter a **compilation error** where "the trait
/// `AppearsInFromClause<Alias<your_alias>>` is not implemented", when trying to use two aliases to
/// the same table within a single query note the following two limitations:
/// - You will need to declare these in a single `alias!` call.
/// - The path to the table module will have to be expressed in the exact same
/// manner. (That is, you can do `alias!(schema::users as user1, schema::users as user2)`
/// or `alias!(users as user1, users as user2)`, but not
/// `alias!(schema::users as user1, users as user2)`)
#[macro_export]
macro_rules! alias {
($($($table: ident)::+ as $alias: ident),* $(,)?) => {{
$crate::alias!(NoConst $($($table)::+ as $alias: $alias,)*);
($($crate::query_source::Alias::<$alias>::default()),*)
}};
($($($table: ident)::+ as $alias_name: ident: $alias_ty: ident),* $(,)?) => {
$crate::alias! {
$(
pub const $alias_name: Alias<$alias_ty> = $($table)::+ as $alias_name;
)*
}
};
($($vis: vis const $const_name: ident: Alias<$alias_ty: ident> = $($table: ident)::+ as $alias_sql_name: ident);* $(;)?) => {
$crate::alias!(NoConst $($($table)::+ as $alias_sql_name: $vis $alias_ty,)*);
$(
#[allow(non_upper_case_globals)]
$vis const $const_name: $crate::query_source::Alias::<$alias_ty> =
$crate::query_source::Alias::new($alias_ty { table: $($table)::+::table });
#[allow(non_camel_case_types)]
$vis type $const_name = $crate::query_source::Alias::<$alias_ty>;
)*
};
(NoConst $($($table: ident)::+ as $alias_sql_name: ident: $vis: vis $alias_ty: ident),* $(,)?) => {
$(
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy)]
$vis struct $alias_ty {
table: $($table)::+::table,
}
impl $crate::query_source::AliasSource for $alias_ty {
const NAME: &'static str = stringify!($alias_sql_name);
type Target = $($table)::+::table;
fn target(&self) -> &Self::Target { &self.table }
}
impl ::std::default::Default for $alias_ty {
fn default() -> Self {
Self { table: $($table)::+::table }
}
}
// impl AppearsInFromClause<Alias<$alias>> for Alias<$alias>
impl $crate::internal::alias_macro::AliasAliasAppearsInFromClauseSameTable<$alias_ty, $($table)::+::table> for $alias_ty {
type Count = $crate::query_source::Once;
}
)*
$crate::__internal_alias_helper!($(table_ty = $($table)::+::table, table_tt = ($($table)::+), alias_ty = $alias_ty, alias_sql_name = $alias_sql_name;)*);
};
}
#[macro_export]
#[doc(hidden)]
/// This only exists to hide internals from the doc
// The `($left_sql_name) != ($right_sql_name)` condition is not perfect because a user could still
// cause runtime errors by declaring two aliases to different tables with the same name then use
// them in the same query, but that would almost have to be voluntary, so this alone should prevent
// most mistakes.
macro_rules! __internal_alias_helper {
(
table_ty = $left_table_ty: ty, table_tt = $left_table_tt: tt, alias_ty = $left_alias: ident, alias_sql_name = $left_sql_name: ident;
$(table_ty = $right_table_ty: ty, table_tt = $right_table_tt: tt, alias_ty = $right_alias: ident, alias_sql_name = $right_sql_name: ident;)+
) => {
$(
$crate::static_cond!{if ($left_table_tt) == ($right_table_tt) {
$crate::static_cond!{if ($left_sql_name) != ($right_sql_name) {
impl $crate::internal::alias_macro::AliasAliasAppearsInFromClauseSameTable<$left_alias, $left_table_ty>
for $right_alias
{
type Count = $crate::query_source::Never;
}
impl $crate::internal::alias_macro::AliasAliasAppearsInFromClauseSameTable<$right_alias, $left_table_ty>
for $left_alias
{
type Count = $crate::query_source::Never;
}
}}
}}
)*
$crate::__internal_alias_helper!($(table_ty = $right_table_ty, table_tt = $right_table_tt, alias_ty = $right_alias, alias_sql_name = $right_sql_name;)+);
};
(table_ty = $left_table_ty: ty, table_tt = $left_table_tt: tt, alias_ty = $left_alias: ident, alias_sql_name = $left_sql_name: ident;) => {};
() => {};
}