diesel/macros/
mod.rs

1pub(crate) mod prelude {
2    #[cfg_attr(
3        any(feature = "huge-tables", feature = "large-tables"),
4        allow(deprecated)
5    )]
6    // This is a false positive, we reexport it later
7    #[allow(unreachable_pub, unused_imports)]
8    #[doc(inline)]
9    pub use crate::{
10        allow_columns_to_appear_in_same_group_by_clause, allow_tables_to_appear_in_same_query,
11        joinable, table,
12    };
13}
14
15#[doc(inline)]
16pub use diesel_derives::table_proc as table;
17
18/// Allow two tables to be referenced in a join query without providing an
19/// explicit `ON` clause.
20///
21/// The generated `ON` clause will always join to the primary key of the parent
22/// table. This macro removes the need to call [`.on`](crate::query_dsl::JoinOnDsl::on) explicitly, you will
23/// still need to invoke
24/// [`allow_tables_to_appear_in_same_query!`](crate::allow_tables_to_appear_in_same_query)
25/// for these two tables to be able to use the resulting query, unless you are
26/// using `diesel print-schema` which will generate it for you.
27///
28/// If you are using `diesel print-schema`, an invocation of this macro
29/// will be generated for every foreign key in your database unless
30/// one of the following is true:
31///
32/// - The foreign key references something other than the primary key
33/// - The foreign key is composite
34/// - There is more than one foreign key connecting two tables
35/// - The foreign key is self-referential
36///
37/// # Example
38///
39/// ```rust
40/// # include!("../doctest_setup.rs");
41/// use schema::*;
42///
43/// # /*
44/// joinable!(posts -> users (user_id));
45/// allow_tables_to_appear_in_same_query!(posts, users);
46/// # */
47///
48/// # fn main() {
49/// let implicit_on_clause = users::table.inner_join(posts::table);
50/// let implicit_on_clause_sql = diesel::debug_query::<DB, _>(&implicit_on_clause).to_string();
51///
52/// let explicit_on_clause = users::table
53///     .inner_join(posts::table.on(posts::user_id.eq(users::id)));
54/// let explicit_on_clause_sql = diesel::debug_query::<DB, _>(&explicit_on_clause).to_string();
55///
56/// assert_eq!(implicit_on_clause_sql, explicit_on_clause_sql);
57/// # }
58/// ```
59///
60/// In the example above, the line `joinable!(posts -> users (user_id));`
61///
62/// specifies the relation of the tables and the ON clause in the following way:
63///
64/// `child_table -> parent_table (foreign_key)`
65///
66/// * `parent_table` is the Table with the Primary key.
67///
68/// * `child_table` is the Table with the Foreign key.
69///
70/// So given the Table declaration from [Associations docs](crate::associations)
71///
72/// * The parent table would be `User`
73/// * The child table would be `Post`
74/// * and the Foreign key would be `Post.user_id`
75///
76/// For joins that do not explicitly use on clauses via [`JoinOnDsl`](crate::prelude::JoinOnDsl)
77/// the following on clause is generated implicitly:
78/// ```sql
79/// post JOIN users ON posts.user_id = users.id
80/// ```
81#[macro_export]
82macro_rules! joinable {
83    ($($child:ident)::* -> $($parent:ident)::* ($source:ident)) => {
84        $crate::joinable_inner!($($child)::* ::table => $($parent)::* ::table : ($($child)::* ::$source = $($parent)::* ::table));
85        $crate::joinable_inner!($($parent)::* ::table => $($child)::* ::table : ($($child)::* ::$source = $($parent)::* ::table));
86    }
87}
88
89#[macro_export]
90#[doc(hidden)]
91macro_rules! joinable_inner {
92    ($left_table:path => $right_table:path : ($foreign_key:path = $parent_table:path)) => {
93        $crate::joinable_inner!(
94            left_table_ty = $left_table,
95            right_table_ty = $right_table,
96            right_table_expr = $right_table,
97            foreign_key = $foreign_key,
98            primary_key_ty = <$parent_table as $crate::query_source::Table>::PrimaryKey,
99            primary_key_expr =
100                <$parent_table as $crate::query_source::Table>::primary_key(&$parent_table),
101        );
102    };
103
104    (
105        left_table_ty = $left_table_ty:ty,
106        right_table_ty = $right_table_ty:ty,
107        right_table_expr = $right_table_expr:expr,
108        foreign_key = $foreign_key:path,
109        primary_key_ty = $primary_key_ty:ty,
110        primary_key_expr = $primary_key_expr:expr,
111    ) => {
112        impl $crate::JoinTo<$right_table_ty> for $left_table_ty {
113            type FromClause = $right_table_ty;
114            type OnClause = $crate::dsl::Eq<
115                $crate::internal::table_macro::NullableExpression<$foreign_key>,
116                $crate::internal::table_macro::NullableExpression<$primary_key_ty>,
117            >;
118
119            fn join_target(rhs: $right_table_ty) -> (Self::FromClause, Self::OnClause) {
120                use $crate::{ExpressionMethods, NullableExpressionMethods};
121
122                (
123                    rhs,
124                    $foreign_key.nullable().eq($primary_key_expr.nullable()),
125                )
126            }
127        }
128    };
129}
130
131/// Allow two or more tables which are otherwise unrelated to be used together
132/// in a query.
133///
134/// This macro must be invoked any time two tables need to appear in the same
135/// query either because they are being joined together, or because one appears
136/// in a subselect. When this macro is invoked with more than 2 tables, every
137/// combination of those tables will be allowed to appear together.
138///
139/// If you are using `diesel print-schema`, an invocation of
140/// this macro will be generated for you for all tables in your schema.
141///
142/// # Example
143///
144/// ```
145/// # use diesel::{allow_tables_to_appear_in_same_query, table};
146/// #
147/// // This would be required to do `users.inner_join(posts.inner_join(comments))`
148/// allow_tables_to_appear_in_same_query!(comments, posts, users);
149///
150/// table! {
151///     comments {
152///         id -> Integer,
153///         post_id -> Integer,
154///         body -> VarChar,
155///     }
156/// }
157///
158/// table! {
159///    posts {
160///        id -> Integer,
161///        user_id -> Integer,
162///        title -> VarChar,
163///    }
164/// }
165///
166/// table! {
167///     users {
168///        id -> Integer,
169///        name -> VarChar,
170///     }
171/// }
172/// ```
173///
174/// When more than two tables are passed, the relevant code is generated for
175/// every combination of those tables. This code would be equivalent to the
176/// previous example.
177///
178/// ```
179/// # use diesel::{allow_tables_to_appear_in_same_query, table};
180/// # table! {
181/// #    comments {
182/// #        id -> Integer,
183/// #        post_id -> Integer,
184/// #        body -> VarChar,
185/// #    }
186/// # }
187/// #
188/// # table! {
189/// #    posts {
190/// #        id -> Integer,
191/// #        user_id -> Integer,
192/// #        title -> VarChar,
193/// #    }
194/// # }
195/// #
196/// # table! {
197/// #     users {
198/// #        id -> Integer,
199/// #        name -> VarChar,
200/// #     }
201/// # }
202/// #
203/// allow_tables_to_appear_in_same_query!(comments, posts);
204/// allow_tables_to_appear_in_same_query!(comments, users);
205/// allow_tables_to_appear_in_same_query!(posts, users);
206/// #
207/// # fn main() {}
208/// ```
209#[macro_export]
210macro_rules! allow_tables_to_appear_in_same_query {
211    ($left_mod:ident, $($right_mod:ident),+ $(,)*) => {
212        $(
213            impl $crate::query_source::TableNotEqual<$left_mod::table> for $right_mod::table {}
214            impl $crate::query_source::TableNotEqual<$right_mod::table> for $left_mod::table {}
215        )+
216        $crate::allow_tables_to_appear_in_same_query!($($right_mod,)+);
217    };
218
219    ($last_table:ident,) => {};
220
221    () => {};
222}
223
224#[doc(hidden)]
225#[macro_export]
226macro_rules! __diesel_impl_allow_in_same_group_by_clause {
227    (
228        left = [$($left_path:tt)::+],
229    ) => {};
230    (
231        left = [$($left_path:tt)::+],
232        $($right_path:tt)::+
233    ) => {
234        $crate::__diesel_impl_allow_in_same_group_by_clause! {
235            left = [$($left_path)+],
236            right = [$($right_path)+],
237            left_tbl = [],
238            left_path = [],
239        }
240    };
241    (
242        left = [$($left_path:tt)::+],
243        $($right_path:tt)::+,
244        $($other:tt)*
245    ) => {
246        $crate::__diesel_impl_allow_in_same_group_by_clause! {
247            left = [$($left_path)+],
248            right = [$($right_path)+],
249            left_tbl = [],
250            left_path = [],
251        }
252        $crate::__diesel_impl_allow_in_same_group_by_clause! {
253            left = [$($left_path)::+],
254            $($other)*
255        }
256    };
257    (
258        left = [$left_path_p1: tt  $($left_path: tt)+],
259        right = [$($right_path: tt)*],
260        left_tbl = [$($left_tbl:tt)?],
261        left_path = [$($left_out_path:tt)*],
262    ) => {
263        $crate::__diesel_impl_allow_in_same_group_by_clause! {
264            left = [$($left_path)+],
265            right = [$($right_path)*],
266            left_tbl = [$left_path_p1],
267            left_path = [$($left_out_path)* $($left_tbl)?],
268        }
269    };
270    (
271        left = [$left_col: tt],
272        right = [$($right_path: tt)*],
273        left_tbl = [$($left_tbl:tt)?],
274        left_path = [$($left_out_path:tt)*],
275    ) => {
276        $crate::__diesel_impl_allow_in_same_group_by_clause! {
277            left = [$left_col],
278            right = [$($right_path)*],
279            left_tbl = [$($left_tbl)?],
280            left_path = [$($left_out_path)*],
281            right_tbl = [],
282            right_path = [],
283        }
284    };
285    (
286        left = [$left_col: tt ],
287        right = [$right_path_p1: tt  $($right_path: tt)+],
288        left_tbl = [$($left_tbl:tt)?],
289        left_path = [$($left_out_path:tt)*],
290        right_tbl = [$($right_tbl:tt)?],
291        right_path = [$($right_out_path:tt)*],
292    ) => {
293        $crate::__diesel_impl_allow_in_same_group_by_clause! {
294            left = [$left_col],
295            right = [$($right_path)+],
296            left_tbl = [$($left_tbl)?],
297            left_path = [$($left_out_path)*],
298            right_tbl = [$right_path_p1],
299            right_path = [$($right_out_path)* $($right_tbl)?],
300        }
301    };
302    (
303        left = [$left_col: tt],
304        right = [$right_col: tt],
305        left_tbl = [$left_tbl:tt],
306        left_path = [$($left_begin:tt)*],
307        right_tbl = [$right_tbl:tt],
308        right_path = [$($right_begin:tt)*],
309    ) => {
310        $crate::static_cond! {
311            if $left_tbl != $right_tbl {
312                impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $left_tbl :: $left_col> for $($right_begin ::)* $right_tbl :: $right_col {
313                    type Output = $crate::expression::is_contained_in_group_by::No;
314                }
315
316                impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $right_tbl :: $right_col> for $($left_begin ::)* $left_tbl :: $left_col {
317                    type Output = $crate::expression::is_contained_in_group_by::No;
318                }
319            }
320        }
321    };
322    (
323        left = [$left_col: tt],
324        right = [$right_col: tt],
325        left_tbl = [$($left_tbl:tt)?],
326        left_path = [$($left_begin:tt)*],
327        right_tbl = [$($right_tbl:tt)?],
328        right_path = [$($right_begin:tt)*],
329    ) => {
330        impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $($left_tbl ::)? $left_col> for $($right_begin ::)* $($right_tbl ::)? $right_col {
331            type Output = $crate::expression::is_contained_in_group_by::No;
332        }
333
334        impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $($right_tbl ::)? $right_col> for $($left_begin ::)* $($left_tbl ::)? $left_col {
335            type Output = $crate::expression::is_contained_in_group_by::No;
336        }
337    };
338
339}
340
341/// Allow two or more columns which are otherwise unrelated to be used together
342/// in a group by clause.
343///
344/// This macro must be invoked any time two columns need to appear in the same
345/// group by clause. When this macro is invoked with more than 2 columns, every
346/// combination of those columns will be allowed to appear together.
347///
348/// # Example
349///
350/// ```
351/// # include!("../doctest_setup.rs");
352/// # use crate::schema::{users, posts};
353/// // This would be required
354///
355/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id, posts::title);
356/// # fn main() {
357/// // to do implement the following join
358/// users::table
359///     .inner_join(posts::table)
360///     .group_by((users::name, posts::id, posts::title))
361/// # ;
362/// # }
363/// ```
364///
365/// When more than two columns are passed, the relevant code is generated for
366/// every combination of those columns. This code would be equivalent to the
367/// previous example.
368///
369/// ```
370/// # include!("../doctest_setup.rs");
371/// # use crate::schema::{users, posts};
372/// #
373/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::title);
374/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id);
375/// allow_columns_to_appear_in_same_group_by_clause!(posts::title, posts::id);
376/// # fn main() {}
377/// ```
378#[macro_export]
379macro_rules! allow_columns_to_appear_in_same_group_by_clause {
380    ($($left_path:tt)::+, $($right_path:tt)::+ $(,)?) => {
381        $crate::__diesel_impl_allow_in_same_group_by_clause! {
382            left = [$($left_path)::+],
383            $($right_path)::+,
384        }
385    };
386    ($($left_path:tt)::+, $($right_path:tt)::+, $($other: tt)*) => {
387        $crate::__diesel_impl_allow_in_same_group_by_clause! {
388            left = [$($left_path)::+],
389            $($right_path)::+,
390            $($other)*
391        }
392        $crate::allow_columns_to_appear_in_same_group_by_clause! {
393            $($right_path)::+,
394            $($other)*
395        }
396    };
397    ($last_col:ty,) => {};
398    () => {};
399}
400
401#[macro_export]
402#[doc(hidden)]
403macro_rules! __diesel_with_dollar_sign {
404    ($($body:tt)*) => {
405        macro_rules! __with_dollar_sign { $($body)* }
406        __with_dollar_sign!($);
407    }
408}
409
410// The order of these modules is important (at least for those which have tests).
411// Utility macros which don't call any others need to come first.
412#[macro_use]
413mod internal;
414#[macro_use]
415mod static_cond;
416#[macro_use]
417mod ops;
418
419#[cfg(test)]
420mod tests {
421    use crate::prelude::*;
422
423    table! {
424        foo.bars {
425            id -> Integer,
426            baz -> Text,
427        }
428    }
429
430    mod my_types {
431        #[derive(Debug, Clone, Copy, crate::sql_types::SqlType)]
432        pub struct MyCustomType;
433    }
434
435    table! {
436        use crate::sql_types::*;
437        use crate::macros::tests::my_types::*;
438
439        table_with_custom_types {
440            id -> Integer,
441            my_type -> MyCustomType,
442        }
443    }
444
445    table! {
446        use crate::sql_types::*;
447        use crate::macros::tests::my_types::*;
448
449        /// Table documentation
450        ///
451        /// some in detail documentation
452        table_with_custom_type_and_id (a) {
453            /// Column documentation
454            ///
455            /// some more details
456            a -> Integer,
457            my_type -> MyCustomType,
458        }
459    }
460
461    #[diesel_test_helper::test]
462    #[cfg(feature = "postgres")]
463    fn table_with_custom_schema() {
464        use crate::pg::Pg;
465        let expected_sql = r#"SELECT "foo"."bars"."baz" FROM "foo"."bars" -- binds: []"#;
466        assert_eq!(
467            expected_sql,
468            &crate::debug_query::<Pg, _>(&bars::table.select(bars::baz)).to_string()
469        );
470    }
471
472    table! {
473        use crate::sql_types;
474        use crate::sql_types::*;
475
476        table_with_arbitrarily_complex_types {
477            id -> sql_types::Integer,
478            qualified_nullable -> sql_types::Nullable<sql_types::Integer>,
479            deeply_nested_type -> Nullable<Nullable<Integer>>,
480            // This actually should work, but there appears to be a rustc bug
481            // on the `AsExpression` bound for `EqAll` when the ty param is a projection
482            // projected_type -> <Nullable<Integer> as sql_types::IntoNullable>::Nullable,
483            //random_tuple -> (Integer, Integer),
484        }
485    }
486
487    table!(
488        foo {
489            /// Column doc
490            id -> Integer,
491
492            #[sql_name = "type"]
493            /// Also important to document this column
494            mytype -> Integer,
495
496            /// And this one
497            #[sql_name = "bleh"]
498            hey -> Integer,
499        }
500    );
501
502    #[diesel_test_helper::test]
503    #[cfg(feature = "postgres")]
504    fn table_with_column_renaming_postgres() {
505        use crate::pg::Pg;
506        let expected_sql = r#"SELECT "foo"."id", "foo"."type", "foo"."bleh" FROM "foo" WHERE ("foo"."type" = $1) -- binds: [1]"#;
507        assert_eq!(
508            expected_sql,
509            crate::debug_query::<Pg, _>(&foo::table.filter(foo::mytype.eq(1))).to_string()
510        );
511    }
512
513    #[diesel_test_helper::test]
514    #[cfg(feature = "mysql")]
515    fn table_with_column_renaming_mysql() {
516        use crate::mysql::Mysql;
517        let expected_sql = r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#;
518        assert_eq!(
519            expected_sql,
520            crate::debug_query::<Mysql, _>(&foo::table.filter(foo::mytype.eq(1))).to_string()
521        );
522    }
523
524    #[diesel_test_helper::test]
525    #[cfg(feature = "sqlite")]
526    fn table_with_column_renaming_sqlite() {
527        use crate::sqlite::Sqlite;
528        let expected_sql = r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#;
529        assert_eq!(
530            expected_sql,
531            crate::debug_query::<Sqlite, _>(&foo::table.filter(foo::mytype.eq(1))).to_string()
532        );
533    }
534
535    table!(
536        use crate::sql_types::*;
537
538        /// Some documentation
539        #[sql_name="mod"]
540        /// Some more documentation
541        bar {
542            id -> Integer,
543        }
544    );
545
546    #[diesel_test_helper::test]
547    #[cfg(feature = "postgres")]
548    fn table_renaming_postgres() {
549        use crate::pg::Pg;
550        let expected_sql = r#"SELECT "mod"."id" FROM "mod" -- binds: []"#;
551        assert_eq!(
552            expected_sql,
553            crate::debug_query::<Pg, _>(&bar::table.select(bar::id)).to_string()
554        );
555    }
556
557    #[diesel_test_helper::test]
558    #[cfg(feature = "mysql")]
559    fn table_renaming_mysql() {
560        use crate::mysql::Mysql;
561        let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#;
562        assert_eq!(
563            expected_sql,
564            crate::debug_query::<Mysql, _>(&bar::table.select(bar::id)).to_string()
565        );
566    }
567
568    #[diesel_test_helper::test]
569    #[cfg(feature = "sqlite")]
570    fn table_renaming_sqlite() {
571        use crate::sqlite::Sqlite;
572        let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#;
573        assert_eq!(
574            expected_sql,
575            crate::debug_query::<Sqlite, _>(&bar::table.select(bar::id)).to_string()
576        );
577    }
578
579    mod tests_for_allow_combined_group_by_syntax {
580        use crate::table;
581
582        table! {
583            a(b) {
584                b -> Text,
585                c -> Text,
586                d -> Text,
587                e -> Text,
588            }
589        }
590
591        table! {
592            b(a) {
593                a -> Text,
594                c -> Text,
595                d -> Text,
596            }
597        }
598
599        table! {
600            c(a) {
601                a -> Text,
602                b -> Text,
603                d -> Text,
604            }
605        }
606
607        // allow using table::column
608        allow_columns_to_appear_in_same_group_by_clause!(a::b, b::a, a::d,);
609
610        // allow using full paths
611        allow_columns_to_appear_in_same_group_by_clause!(self::a::c, self::b::c, self::b::d,);
612
613        use self::a::d as a_d;
614        use self::b::d as b_d;
615        use self::c::d as c_d;
616
617        // allow using plain identifiers
618        allow_columns_to_appear_in_same_group_by_clause!(a_d, b_d, c_d);
619
620        // allow mixing all variants
621        allow_columns_to_appear_in_same_group_by_clause!(c_d, self::b::a, a::e,);
622    }
623}