| pub(crate) mod prelude { |
| #[cfg_attr( |
| any(feature = "huge-tables", feature = "large-tables"), |
| allow(deprecated) |
| )] |
| // This is a false positive, we reexport it later |
| #[allow(unreachable_pub, unused_imports)] |
| #[doc(inline)] |
| pub use crate::{ |
| allow_columns_to_appear_in_same_group_by_clause, allow_tables_to_appear_in_same_query, |
| joinable, table, |
| }; |
| } |
| |
| #[doc(inline)] |
| pub use diesel_derives::table_proc as table; |
| |
| /// Allow two tables to be referenced in a join query without providing an |
| /// explicit `ON` clause. |
| /// |
| /// The generated `ON` clause will always join to the primary key of the parent |
| /// table. This macro removes the need to call [`.on`] explicitly, you will |
| /// still need to invoke |
| /// [`allow_tables_to_appear_in_same_query!`](crate::allow_tables_to_appear_in_same_query) |
| /// for these two tables to be able to use the resulting query, unless you are |
| /// using `diesel print-schema` which will generate it for you. |
| /// |
| /// If you are using `diesel print-schema`, an invocation of this macro |
| /// will be generated for every foreign key in your database unless |
| /// one of the following is true: |
| /// |
| /// - The foreign key references something other than the primary key |
| /// - The foreign key is composite |
| /// - There is more than one foreign key connecting two tables |
| /// - The foreign key is self-referential |
| /// |
| /// # Example |
| /// |
| /// ```rust |
| /// # include!("../doctest_setup.rs"); |
| /// use schema::*; |
| /// |
| /// # /* |
| /// joinable!(posts -> users (user_id)); |
| /// allow_tables_to_appear_in_same_query!(posts, users); |
| /// # */ |
| /// |
| /// # fn main() { |
| /// let implicit_on_clause = users::table.inner_join(posts::table); |
| /// let implicit_on_clause_sql = diesel::debug_query::<DB, _>(&implicit_on_clause).to_string(); |
| /// |
| /// let explicit_on_clause = users::table |
| /// .inner_join(posts::table.on(posts::user_id.eq(users::id))); |
| /// let explicit_on_clause_sql = diesel::debug_query::<DB, _>(&explicit_on_clause).to_string(); |
| /// |
| /// assert_eq!(implicit_on_clause_sql, explicit_on_clause_sql); |
| /// # } |
| /// |
| /// ``` |
| /// |
| /// In the example above, the line `joinable!(posts -> users (user_id));` |
| /// |
| /// specifies the relation of the tables and the ON clause in the following way: |
| /// |
| /// `child_table -> parent_table (foreign_key)` |
| /// |
| /// * `parent_table` is the Table with the Primary key. |
| /// |
| /// * `child_table` is the Table with the Foreign key. |
| /// |
| /// So given the Table declaration from [Associations docs](crate::associations) |
| /// |
| /// * The parent table would be `User` |
| /// * The child table would be `Post` |
| /// * and the Foreign key would be `Post.user_id` |
| /// |
| /// For joins that do not explicitly use on clauses via [`JoinOnDsl`](crate::prelude::JoinOnDsl) |
| /// the following on clause is generated implicitly: |
| /// ```sql |
| /// post JOIN users ON posts.user_id = users.id |
| /// ``` |
| #[macro_export] |
| macro_rules! joinable { |
| ($($child:ident)::* -> $($parent:ident)::* ($source:ident)) => { |
| $crate::joinable_inner!($($child)::* ::table => $($parent)::* ::table : ($($child)::* ::$source = $($parent)::* ::table)); |
| $crate::joinable_inner!($($parent)::* ::table => $($child)::* ::table : ($($child)::* ::$source = $($parent)::* ::table)); |
| } |
| } |
| |
| #[macro_export] |
| #[doc(hidden)] |
| macro_rules! joinable_inner { |
| ($left_table:path => $right_table:path : ($foreign_key:path = $parent_table:path)) => { |
| $crate::joinable_inner!( |
| left_table_ty = $left_table, |
| right_table_ty = $right_table, |
| right_table_expr = $right_table, |
| foreign_key = $foreign_key, |
| primary_key_ty = <$parent_table as $crate::query_source::Table>::PrimaryKey, |
| primary_key_expr = |
| <$parent_table as $crate::query_source::Table>::primary_key(&$parent_table), |
| ); |
| }; |
| |
| ( |
| left_table_ty = $left_table_ty:ty, |
| right_table_ty = $right_table_ty:ty, |
| right_table_expr = $right_table_expr:expr, |
| foreign_key = $foreign_key:path, |
| primary_key_ty = $primary_key_ty:ty, |
| primary_key_expr = $primary_key_expr:expr, |
| ) => { |
| impl $crate::JoinTo<$right_table_ty> for $left_table_ty { |
| type FromClause = $right_table_ty; |
| type OnClause = $crate::dsl::Eq< |
| $crate::internal::table_macro::NullableExpression<$foreign_key>, |
| $crate::internal::table_macro::NullableExpression<$primary_key_ty>, |
| >; |
| |
| fn join_target(rhs: $right_table_ty) -> (Self::FromClause, Self::OnClause) { |
| use $crate::{ExpressionMethods, NullableExpressionMethods}; |
| |
| ( |
| rhs, |
| $foreign_key.nullable().eq($primary_key_expr.nullable()), |
| ) |
| } |
| } |
| }; |
| } |
| |
| /// Allow two or more tables which are otherwise unrelated to be used together |
| /// in a query. |
| /// |
| /// This macro must be invoked any time two tables need to appear in the same |
| /// query either because they are being joined together, or because one appears |
| /// in a subselect. When this macro is invoked with more than 2 tables, every |
| /// combination of those tables will be allowed to appear together. |
| /// |
| /// If you are using `diesel print-schema`, an invocation of |
| /// this macro will be generated for you for all tables in your schema. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// # use diesel::{allow_tables_to_appear_in_same_query, table}; |
| /// # |
| /// // This would be required to do `users.inner_join(posts.inner_join(comments))` |
| /// allow_tables_to_appear_in_same_query!(comments, posts, users); |
| /// |
| /// table! { |
| /// comments { |
| /// id -> Integer, |
| /// post_id -> Integer, |
| /// body -> VarChar, |
| /// } |
| /// } |
| /// |
| /// table! { |
| /// posts { |
| /// id -> Integer, |
| /// user_id -> Integer, |
| /// title -> VarChar, |
| /// } |
| /// } |
| /// |
| /// table! { |
| /// users { |
| /// id -> Integer, |
| /// name -> VarChar, |
| /// } |
| /// } |
| /// ``` |
| /// |
| /// When more than two tables are passed, the relevant code is generated for |
| /// every combination of those tables. This code would be equivalent to the |
| /// previous example. |
| /// |
| /// ``` |
| /// # use diesel::{allow_tables_to_appear_in_same_query, table}; |
| /// # table! { |
| /// # comments { |
| /// # id -> Integer, |
| /// # post_id -> Integer, |
| /// # body -> VarChar, |
| /// # } |
| /// # } |
| /// # |
| /// # table! { |
| /// # posts { |
| /// # id -> Integer, |
| /// # user_id -> Integer, |
| /// # title -> VarChar, |
| /// # } |
| /// # } |
| /// # |
| /// # table! { |
| /// # users { |
| /// # id -> Integer, |
| /// # name -> VarChar, |
| /// # } |
| /// # } |
| /// # |
| /// allow_tables_to_appear_in_same_query!(comments, posts); |
| /// allow_tables_to_appear_in_same_query!(comments, users); |
| /// allow_tables_to_appear_in_same_query!(posts, users); |
| /// # |
| /// # fn main() {} |
| /// ``` |
| #[macro_export] |
| macro_rules! allow_tables_to_appear_in_same_query { |
| ($left_mod:ident, $($right_mod:ident),+ $(,)*) => { |
| $( |
| impl $crate::query_source::TableNotEqual<$left_mod::table> for $right_mod::table {} |
| impl $crate::query_source::TableNotEqual<$right_mod::table> for $left_mod::table {} |
| $crate::__diesel_internal_backend_specific_allow_tables_to_appear_in_same_query!($left_mod, $right_mod); |
| )+ |
| $crate::allow_tables_to_appear_in_same_query!($($right_mod,)+); |
| }; |
| |
| ($last_table:ident,) => {}; |
| |
| () => {}; |
| } |
| #[doc(hidden)] |
| #[macro_export] |
| #[cfg(feature = "postgres_backend")] |
| macro_rules! __diesel_internal_backend_specific_allow_tables_to_appear_in_same_query { |
| ($left:ident, $right:ident) => { |
| impl $crate::query_source::TableNotEqual<$left::table> |
| for $crate::query_builder::Only<$right::table> |
| { |
| } |
| impl $crate::query_source::TableNotEqual<$right::table> |
| for $crate::query_builder::Only<$left::table> |
| { |
| } |
| impl $crate::query_source::TableNotEqual<$crate::query_builder::Only<$left::table>> |
| for $right::table |
| { |
| } |
| impl $crate::query_source::TableNotEqual<$crate::query_builder::Only<$right::table>> |
| for $left::table |
| { |
| } |
| }; |
| } |
| #[doc(hidden)] |
| #[macro_export] |
| #[cfg(not(feature = "postgres_backend"))] |
| macro_rules! __diesel_internal_backend_specific_allow_tables_to_appear_in_same_query { |
| ($left:ident, $right:ident) => {}; |
| } |
| |
| #[doc(hidden)] |
| #[macro_export] |
| macro_rules! __diesel_impl_allow_in_same_group_by_clause { |
| ( |
| left = [$($left_path:tt)::+], |
| ) => {}; |
| ( |
| left = [$($left_path:tt)::+], |
| $($right_path:tt)::+ |
| ) => { |
| $crate::__diesel_impl_allow_in_same_group_by_clause! { |
| left = [$($left_path)+], |
| right = [$($right_path)+], |
| left_tbl = [], |
| left_path = [], |
| } |
| }; |
| ( |
| left = [$($left_path:tt)::+], |
| $($right_path:tt)::+, |
| $($other:tt)* |
| ) => { |
| $crate::__diesel_impl_allow_in_same_group_by_clause! { |
| left = [$($left_path)+], |
| right = [$($right_path)+], |
| left_tbl = [], |
| left_path = [], |
| } |
| $crate::__diesel_impl_allow_in_same_group_by_clause! { |
| left = [$($left_path)::+], |
| $($other)* |
| } |
| }; |
| ( |
| left = [$left_path_p1: tt $($left_path: tt)+], |
| right = [$($right_path: tt)*], |
| left_tbl = [$($left_tbl:tt)?], |
| left_path = [$($left_out_path:tt)*], |
| ) => { |
| $crate::__diesel_impl_allow_in_same_group_by_clause! { |
| left = [$($left_path)+], |
| right = [$($right_path)*], |
| left_tbl = [$left_path_p1], |
| left_path = [$($left_out_path)* $($left_tbl)?], |
| } |
| }; |
| ( |
| left = [$left_col: tt], |
| right = [$($right_path: tt)*], |
| left_tbl = [$($left_tbl:tt)?], |
| left_path = [$($left_out_path:tt)*], |
| ) => { |
| $crate::__diesel_impl_allow_in_same_group_by_clause! { |
| left = [$left_col], |
| right = [$($right_path)*], |
| left_tbl = [$($left_tbl)?], |
| left_path = [$($left_out_path)*], |
| right_tbl = [], |
| right_path = [], |
| } |
| }; |
| ( |
| left = [$left_col: tt ], |
| right = [$right_path_p1: tt $($right_path: tt)+], |
| left_tbl = [$($left_tbl:tt)?], |
| left_path = [$($left_out_path:tt)*], |
| right_tbl = [$($right_tbl:tt)?], |
| right_path = [$($right_out_path:tt)*], |
| ) => { |
| $crate::__diesel_impl_allow_in_same_group_by_clause! { |
| left = [$left_col], |
| right = [$($right_path)+], |
| left_tbl = [$($left_tbl)?], |
| left_path = [$($left_out_path)*], |
| right_tbl = [$right_path_p1], |
| right_path = [$($right_out_path)* $($right_tbl)?], |
| } |
| }; |
| ( |
| left = [$left_col: tt], |
| right = [$right_col: tt], |
| left_tbl = [$left_tbl:tt], |
| left_path = [$($left_begin:tt)*], |
| right_tbl = [$right_tbl:tt], |
| right_path = [$($right_begin:tt)*], |
| ) => { |
| $crate::static_cond! { |
| if $left_tbl != $right_tbl { |
| impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $left_tbl :: $left_col> for $($right_begin ::)* $right_tbl :: $right_col { |
| type Output = $crate::expression::is_contained_in_group_by::No; |
| } |
| |
| impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $right_tbl :: $right_col> for $($left_begin ::)* $left_tbl :: $left_col { |
| type Output = $crate::expression::is_contained_in_group_by::No; |
| } |
| } |
| } |
| }; |
| ( |
| left = [$left_col: tt], |
| right = [$right_col: tt], |
| left_tbl = [$($left_tbl:tt)?], |
| left_path = [$($left_begin:tt)*], |
| right_tbl = [$($right_tbl:tt)?], |
| right_path = [$($right_begin:tt)*], |
| ) => { |
| impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $($left_tbl ::)? $left_col> for $($right_begin ::)* $($right_tbl ::)? $right_col { |
| type Output = $crate::expression::is_contained_in_group_by::No; |
| } |
| |
| impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $($right_tbl ::)? $right_col> for $($left_begin ::)* $($left_tbl ::)? $left_col { |
| type Output = $crate::expression::is_contained_in_group_by::No; |
| } |
| }; |
| |
| } |
| |
| /// Allow two or more columns which are otherwise unrelated to be used together |
| /// in a group by clause. |
| /// |
| /// This macro must be invoked any time two columns need to appear in the same |
| /// group by clause. When this macro is invoked with more than 2 columns, every |
| /// combination of those columns will be allowed to appear together. |
| /// |
| /// # Example |
| /// |
| /// ``` |
| /// # include!("../doctest_setup.rs"); |
| /// # use crate::schema::{users, posts}; |
| /// // This would be required |
| /// |
| /// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id, posts::title); |
| /// # fn main() { |
| /// // to do implement the following join |
| /// users::table.inner_join(posts::table).group_by((users::name, posts::id, posts::title)) |
| /// # ; |
| /// # } |
| /// ``` |
| /// |
| /// When more than two columns are passed, the relevant code is generated for |
| /// every combination of those columns. This code would be equivalent to the |
| /// previous example. |
| /// |
| /// ``` |
| /// # include!("../doctest_setup.rs"); |
| /// # use crate::schema::{users, posts}; |
| /// # |
| /// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::title); |
| /// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id); |
| /// allow_columns_to_appear_in_same_group_by_clause!(posts::title, posts::id); |
| /// # fn main() {} |
| /// ``` |
| #[macro_export] |
| macro_rules! allow_columns_to_appear_in_same_group_by_clause { |
| ($($left_path:tt)::+, $($right_path:tt)::+ $(,)?) => { |
| $crate::__diesel_impl_allow_in_same_group_by_clause! { |
| left = [$($left_path)::+], |
| $($right_path)::+, |
| } |
| }; |
| ($($left_path:tt)::+, $($right_path:tt)::+, $($other: tt)*) => { |
| $crate::__diesel_impl_allow_in_same_group_by_clause! { |
| left = [$($left_path)::+], |
| $($right_path)::+, |
| $($other)* |
| } |
| $crate::allow_columns_to_appear_in_same_group_by_clause! { |
| $($right_path)::+, |
| $($other)* |
| } |
| }; |
| ($last_col:ty,) => {}; |
| () => {}; |
| } |
| |
| #[macro_export] |
| #[doc(hidden)] |
| macro_rules! __diesel_with_dollar_sign { |
| ($($body:tt)*) => { |
| macro_rules! __with_dollar_sign { $($body)* } |
| __with_dollar_sign!($); |
| } |
| } |
| |
| // The order of these modules is important (at least for those which have tests). |
| // Utility macros which don't call any others need to come first. |
| #[macro_use] |
| mod internal; |
| #[macro_use] |
| mod static_cond; |
| #[macro_use] |
| mod ops; |
| |
| #[cfg(test)] |
| mod tests { |
| use crate::prelude::*; |
| |
| table! { |
| foo.bars { |
| id -> Integer, |
| baz -> Text, |
| } |
| } |
| |
| mod my_types { |
| #[derive(Debug, Clone, Copy, crate::sql_types::SqlType)] |
| pub struct MyCustomType; |
| } |
| |
| table! { |
| use crate::sql_types::*; |
| use crate::macros::tests::my_types::*; |
| |
| table_with_custom_types { |
| id -> Integer, |
| my_type -> MyCustomType, |
| } |
| } |
| |
| table! { |
| use crate::sql_types::*; |
| use crate::macros::tests::my_types::*; |
| |
| /// Table documentation |
| /// |
| /// some in detail documentation |
| table_with_custom_type_and_id (a) { |
| /// Column documentation |
| /// |
| /// some more details |
| a -> Integer, |
| my_type -> MyCustomType, |
| } |
| } |
| |
| #[test] |
| #[cfg(feature = "postgres")] |
| fn table_with_custom_schema() { |
| use crate::pg::Pg; |
| let expected_sql = r#"SELECT "foo"."bars"."baz" FROM "foo"."bars" -- binds: []"#; |
| assert_eq!( |
| expected_sql, |
| &crate::debug_query::<Pg, _>(&bars::table.select(bars::baz)).to_string() |
| ); |
| } |
| |
| table! { |
| use crate::sql_types; |
| use crate::sql_types::*; |
| |
| table_with_arbitrarily_complex_types { |
| id -> sql_types::Integer, |
| qualified_nullable -> sql_types::Nullable<sql_types::Integer>, |
| deeply_nested_type -> Nullable<Nullable<Integer>>, |
| // This actually should work, but there appears to be a rustc bug |
| // on the `AsExpression` bound for `EqAll` when the ty param is a projection |
| // projected_type -> <Nullable<Integer> as sql_types::IntoNullable>::Nullable, |
| //random_tuple -> (Integer, Integer), |
| } |
| } |
| |
| table!( |
| foo { |
| /// Column doc |
| id -> Integer, |
| |
| #[sql_name = "type"] |
| /// Also important to document this column |
| mytype -> Integer, |
| |
| /// And this one |
| #[sql_name = "bleh"] |
| hey -> Integer, |
| } |
| ); |
| |
| #[test] |
| #[cfg(feature = "postgres")] |
| fn table_with_column_renaming_postgres() { |
| use crate::pg::Pg; |
| let expected_sql = r#"SELECT "foo"."id", "foo"."type", "foo"."bleh" FROM "foo" WHERE ("foo"."type" = $1) -- binds: [1]"#; |
| assert_eq!( |
| expected_sql, |
| crate::debug_query::<Pg, _>(&foo::table.filter(foo::mytype.eq(1))).to_string() |
| ); |
| } |
| |
| #[test] |
| #[cfg(feature = "mysql")] |
| fn table_with_column_renaming_mysql() { |
| use crate::mysql::Mysql; |
| let expected_sql = r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#; |
| assert_eq!( |
| expected_sql, |
| crate::debug_query::<Mysql, _>(&foo::table.filter(foo::mytype.eq(1))).to_string() |
| ); |
| } |
| |
| #[test] |
| #[cfg(feature = "sqlite")] |
| fn table_with_column_renaming_sqlite() { |
| use crate::sqlite::Sqlite; |
| let expected_sql = r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#; |
| assert_eq!( |
| expected_sql, |
| crate::debug_query::<Sqlite, _>(&foo::table.filter(foo::mytype.eq(1))).to_string() |
| ); |
| } |
| |
| table!( |
| use crate::sql_types::*; |
| |
| /// Some documentation |
| #[sql_name="mod"] |
| /// Some more documentation |
| bar { |
| id -> Integer, |
| } |
| ); |
| |
| #[test] |
| #[cfg(feature = "postgres")] |
| fn table_renaming_postgres() { |
| use crate::pg::Pg; |
| let expected_sql = r#"SELECT "mod"."id" FROM "mod" -- binds: []"#; |
| assert_eq!( |
| expected_sql, |
| crate::debug_query::<Pg, _>(&bar::table.select(bar::id)).to_string() |
| ); |
| } |
| |
| #[test] |
| #[cfg(feature = "mysql")] |
| fn table_renaming_mysql() { |
| use crate::mysql::Mysql; |
| let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#; |
| assert_eq!( |
| expected_sql, |
| crate::debug_query::<Mysql, _>(&bar::table.select(bar::id)).to_string() |
| ); |
| } |
| |
| #[test] |
| #[cfg(feature = "sqlite")] |
| fn table_renaming_sqlite() { |
| use crate::sqlite::Sqlite; |
| let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#; |
| assert_eq!( |
| expected_sql, |
| crate::debug_query::<Sqlite, _>(&bar::table.select(bar::id)).to_string() |
| ); |
| } |
| |
| mod tests_for_allow_combined_group_by_syntax { |
| use crate::table; |
| |
| table! { |
| a(b) { |
| b -> Text, |
| c -> Text, |
| d -> Text, |
| e -> Text, |
| } |
| } |
| |
| table! { |
| b(a) { |
| a -> Text, |
| c -> Text, |
| d -> Text, |
| } |
| } |
| |
| table! { |
| c(a) { |
| a -> Text, |
| b -> Text, |
| d -> Text, |
| } |
| } |
| |
| // allow using table::column |
| allow_columns_to_appear_in_same_group_by_clause!(a::b, b::a, a::d,); |
| |
| // allow using full paths |
| allow_columns_to_appear_in_same_group_by_clause!(self::a::c, self::b::c, self::b::d,); |
| |
| use self::a::d as a_d; |
| use self::b::d as b_d; |
| use self::c::d as c_d; |
| |
| // allow using plain identifiers |
| allow_columns_to_appear_in_same_group_by_clause!(a_d, b_d, c_d); |
| |
| // allow mixing all variants |
| allow_columns_to_appear_in_same_group_by_clause!(c_d, self::b::a, a::e,); |
| } |
| } |