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`] 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            $crate::__diesel_internal_backend_specific_allow_tables_to_appear_in_same_query!($left_mod, $right_mod);
216        )+
217        $crate::allow_tables_to_appear_in_same_query!($($right_mod,)+);
218    };
219
220    ($last_table:ident,) => {};
221
222    () => {};
223}
224#[doc(hidden)]
225#[macro_export]
226#[cfg(feature = "postgres_backend")]
227macro_rules! __diesel_internal_backend_specific_allow_tables_to_appear_in_same_query {
228    ($left:ident, $right:ident) => {
229        impl $crate::query_source::TableNotEqual<$left::table>
230            for $crate::query_builder::Only<$right::table>
231        {
232        }
233        impl $crate::query_source::TableNotEqual<$right::table>
234            for $crate::query_builder::Only<$left::table>
235        {
236        }
237        impl $crate::query_source::TableNotEqual<$crate::query_builder::Only<$left::table>>
238            for $right::table
239        {
240        }
241        impl $crate::query_source::TableNotEqual<$crate::query_builder::Only<$right::table>>
242            for $left::table
243        {
244        }
245        impl<TSM> $crate::query_source::TableNotEqual<$left::table>
246            for $crate::query_builder::Tablesample<$right::table, TSM>
247        where
248            TSM: $crate::internal::table_macro::TablesampleMethod,
249        {
250        }
251        impl<TSM> $crate::query_source::TableNotEqual<$right::table>
252            for $crate::query_builder::Tablesample<$left::table, TSM>
253        where
254            TSM: $crate::internal::table_macro::TablesampleMethod,
255        {
256        }
257        impl<TSM>
258            $crate::query_source::TableNotEqual<
259                $crate::query_builder::Tablesample<$left::table, TSM>,
260            > for $right::table
261        where
262            TSM: $crate::internal::table_macro::TablesampleMethod,
263        {
264        }
265        impl<TSM>
266            $crate::query_source::TableNotEqual<
267                $crate::query_builder::Tablesample<$right::table, TSM>,
268            > for $left::table
269        where
270            TSM: $crate::internal::table_macro::TablesampleMethod,
271        {
272        }
273    };
274}
275#[doc(hidden)]
276#[macro_export]
277#[cfg(not(feature = "postgres_backend"))]
278macro_rules! __diesel_internal_backend_specific_allow_tables_to_appear_in_same_query {
279    ($left:ident, $right:ident) => {};
280}
281
282#[doc(hidden)]
283#[macro_export]
284macro_rules! __diesel_impl_allow_in_same_group_by_clause {
285    (
286        left = [$($left_path:tt)::+],
287    ) => {};
288    (
289        left = [$($left_path:tt)::+],
290        $($right_path:tt)::+
291    ) => {
292        $crate::__diesel_impl_allow_in_same_group_by_clause! {
293            left = [$($left_path)+],
294            right = [$($right_path)+],
295            left_tbl = [],
296            left_path = [],
297        }
298    };
299    (
300        left = [$($left_path:tt)::+],
301        $($right_path:tt)::+,
302        $($other:tt)*
303    ) => {
304        $crate::__diesel_impl_allow_in_same_group_by_clause! {
305            left = [$($left_path)+],
306            right = [$($right_path)+],
307            left_tbl = [],
308            left_path = [],
309        }
310        $crate::__diesel_impl_allow_in_same_group_by_clause! {
311            left = [$($left_path)::+],
312            $($other)*
313        }
314    };
315    (
316        left = [$left_path_p1: tt  $($left_path: tt)+],
317        right = [$($right_path: tt)*],
318        left_tbl = [$($left_tbl:tt)?],
319        left_path = [$($left_out_path:tt)*],
320    ) => {
321        $crate::__diesel_impl_allow_in_same_group_by_clause! {
322            left = [$($left_path)+],
323            right = [$($right_path)*],
324            left_tbl = [$left_path_p1],
325            left_path = [$($left_out_path)* $($left_tbl)?],
326        }
327    };
328    (
329        left = [$left_col: tt],
330        right = [$($right_path: tt)*],
331        left_tbl = [$($left_tbl:tt)?],
332        left_path = [$($left_out_path:tt)*],
333    ) => {
334        $crate::__diesel_impl_allow_in_same_group_by_clause! {
335            left = [$left_col],
336            right = [$($right_path)*],
337            left_tbl = [$($left_tbl)?],
338            left_path = [$($left_out_path)*],
339            right_tbl = [],
340            right_path = [],
341        }
342    };
343    (
344        left = [$left_col: tt ],
345        right = [$right_path_p1: tt  $($right_path: tt)+],
346        left_tbl = [$($left_tbl:tt)?],
347        left_path = [$($left_out_path:tt)*],
348        right_tbl = [$($right_tbl:tt)?],
349        right_path = [$($right_out_path:tt)*],
350    ) => {
351        $crate::__diesel_impl_allow_in_same_group_by_clause! {
352            left = [$left_col],
353            right = [$($right_path)+],
354            left_tbl = [$($left_tbl)?],
355            left_path = [$($left_out_path)*],
356            right_tbl = [$right_path_p1],
357            right_path = [$($right_out_path)* $($right_tbl)?],
358        }
359    };
360    (
361        left = [$left_col: tt],
362        right = [$right_col: tt],
363        left_tbl = [$left_tbl:tt],
364        left_path = [$($left_begin:tt)*],
365        right_tbl = [$right_tbl:tt],
366        right_path = [$($right_begin:tt)*],
367    ) => {
368        $crate::static_cond! {
369            if $left_tbl != $right_tbl {
370                impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $left_tbl :: $left_col> for $($right_begin ::)* $right_tbl :: $right_col {
371                    type Output = $crate::expression::is_contained_in_group_by::No;
372                }
373
374                impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $right_tbl :: $right_col> for $($left_begin ::)* $left_tbl :: $left_col {
375                    type Output = $crate::expression::is_contained_in_group_by::No;
376                }
377            }
378        }
379    };
380    (
381        left = [$left_col: tt],
382        right = [$right_col: tt],
383        left_tbl = [$($left_tbl:tt)?],
384        left_path = [$($left_begin:tt)*],
385        right_tbl = [$($right_tbl:tt)?],
386        right_path = [$($right_begin:tt)*],
387    ) => {
388        impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $($left_tbl ::)? $left_col> for $($right_begin ::)* $($right_tbl ::)? $right_col {
389            type Output = $crate::expression::is_contained_in_group_by::No;
390        }
391
392        impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $($right_tbl ::)? $right_col> for $($left_begin ::)* $($left_tbl ::)? $left_col {
393            type Output = $crate::expression::is_contained_in_group_by::No;
394        }
395    };
396
397}
398
399/// Allow two or more columns which are otherwise unrelated to be used together
400/// in a group by clause.
401///
402/// This macro must be invoked any time two columns need to appear in the same
403/// group by clause. When this macro is invoked with more than 2 columns, every
404/// combination of those columns will be allowed to appear together.
405///
406/// # Example
407///
408/// ```
409/// # include!("../doctest_setup.rs");
410/// # use crate::schema::{users, posts};
411/// // This would be required
412///
413/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id, posts::title);
414/// # fn main() {
415/// // to do implement the following join
416/// users::table
417///     .inner_join(posts::table)
418///     .group_by((users::name, posts::id, posts::title))
419/// # ;
420/// # }
421/// ```
422///
423/// When more than two columns are passed, the relevant code is generated for
424/// every combination of those columns. This code would be equivalent to the
425/// previous example.
426///
427/// ```
428/// # include!("../doctest_setup.rs");
429/// # use crate::schema::{users, posts};
430/// #
431/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::title);
432/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id);
433/// allow_columns_to_appear_in_same_group_by_clause!(posts::title, posts::id);
434/// # fn main() {}
435/// ```
436#[macro_export]
437macro_rules! allow_columns_to_appear_in_same_group_by_clause {
438    ($($left_path:tt)::+, $($right_path:tt)::+ $(,)?) => {
439        $crate::__diesel_impl_allow_in_same_group_by_clause! {
440            left = [$($left_path)::+],
441            $($right_path)::+,
442        }
443    };
444    ($($left_path:tt)::+, $($right_path:tt)::+, $($other: tt)*) => {
445        $crate::__diesel_impl_allow_in_same_group_by_clause! {
446            left = [$($left_path)::+],
447            $($right_path)::+,
448            $($other)*
449        }
450        $crate::allow_columns_to_appear_in_same_group_by_clause! {
451            $($right_path)::+,
452            $($other)*
453        }
454    };
455    ($last_col:ty,) => {};
456    () => {};
457}
458
459#[macro_export]
460#[doc(hidden)]
461macro_rules! __diesel_with_dollar_sign {
462    ($($body:tt)*) => {
463        macro_rules! __with_dollar_sign { $($body)* }
464        __with_dollar_sign!($);
465    }
466}
467
468// The order of these modules is important (at least for those which have tests).
469// Utility macros which don't call any others need to come first.
470#[macro_use]
471mod internal;
472#[macro_use]
473mod static_cond;
474#[macro_use]
475mod ops;
476
477#[cfg(test)]
478mod tests {
479    use crate::prelude::*;
480
481    table! {
482        foo.bars {
483            id -> Integer,
484            baz -> Text,
485        }
486    }
487
488    mod my_types {
489        #[derive(Debug, Clone, Copy, crate::sql_types::SqlType)]
490        pub struct MyCustomType;
491    }
492
493    table! {
494        use crate::sql_types::*;
495        use crate::macros::tests::my_types::*;
496
497        table_with_custom_types {
498            id -> Integer,
499            my_type -> MyCustomType,
500        }
501    }
502
503    table! {
504        use crate::sql_types::*;
505        use crate::macros::tests::my_types::*;
506
507        /// Table documentation
508        ///
509        /// some in detail documentation
510        table_with_custom_type_and_id (a) {
511            /// Column documentation
512            ///
513            /// some more details
514            a -> Integer,
515            my_type -> MyCustomType,
516        }
517    }
518
519    #[diesel_test_helper::test]
520    #[cfg(feature = "postgres")]
521    fn table_with_custom_schema() {
522        use crate::pg::Pg;
523        let expected_sql = r#"SELECT "foo"."bars"."baz" FROM "foo"."bars" -- binds: []"#;
524        assert_eq!(
525            expected_sql,
526            &crate::debug_query::<Pg, _>(&bars::table.select(bars::baz)).to_string()
527        );
528    }
529
530    table! {
531        use crate::sql_types;
532        use crate::sql_types::*;
533
534        table_with_arbitrarily_complex_types {
535            id -> sql_types::Integer,
536            qualified_nullable -> sql_types::Nullable<sql_types::Integer>,
537            deeply_nested_type -> Nullable<Nullable<Integer>>,
538            // This actually should work, but there appears to be a rustc bug
539            // on the `AsExpression` bound for `EqAll` when the ty param is a projection
540            // projected_type -> <Nullable<Integer> as sql_types::IntoNullable>::Nullable,
541            //random_tuple -> (Integer, Integer),
542        }
543    }
544
545    table!(
546        foo {
547            /// Column doc
548            id -> Integer,
549
550            #[sql_name = "type"]
551            /// Also important to document this column
552            mytype -> Integer,
553
554            /// And this one
555            #[sql_name = "bleh"]
556            hey -> Integer,
557        }
558    );
559
560    #[diesel_test_helper::test]
561    #[cfg(feature = "postgres")]
562    fn table_with_column_renaming_postgres() {
563        use crate::pg::Pg;
564        let expected_sql = r#"SELECT "foo"."id", "foo"."type", "foo"."bleh" FROM "foo" WHERE ("foo"."type" = $1) -- binds: [1]"#;
565        assert_eq!(
566            expected_sql,
567            crate::debug_query::<Pg, _>(&foo::table.filter(foo::mytype.eq(1))).to_string()
568        );
569    }
570
571    #[diesel_test_helper::test]
572    #[cfg(feature = "mysql")]
573    fn table_with_column_renaming_mysql() {
574        use crate::mysql::Mysql;
575        let expected_sql = r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#;
576        assert_eq!(
577            expected_sql,
578            crate::debug_query::<Mysql, _>(&foo::table.filter(foo::mytype.eq(1))).to_string()
579        );
580    }
581
582    #[diesel_test_helper::test]
583    #[cfg(feature = "sqlite")]
584    fn table_with_column_renaming_sqlite() {
585        use crate::sqlite::Sqlite;
586        let expected_sql = r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#;
587        assert_eq!(
588            expected_sql,
589            crate::debug_query::<Sqlite, _>(&foo::table.filter(foo::mytype.eq(1))).to_string()
590        );
591    }
592
593    table!(
594        use crate::sql_types::*;
595
596        /// Some documentation
597        #[sql_name="mod"]
598        /// Some more documentation
599        bar {
600            id -> Integer,
601        }
602    );
603
604    #[diesel_test_helper::test]
605    #[cfg(feature = "postgres")]
606    fn table_renaming_postgres() {
607        use crate::pg::Pg;
608        let expected_sql = r#"SELECT "mod"."id" FROM "mod" -- binds: []"#;
609        assert_eq!(
610            expected_sql,
611            crate::debug_query::<Pg, _>(&bar::table.select(bar::id)).to_string()
612        );
613    }
614
615    #[diesel_test_helper::test]
616    #[cfg(feature = "mysql")]
617    fn table_renaming_mysql() {
618        use crate::mysql::Mysql;
619        let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#;
620        assert_eq!(
621            expected_sql,
622            crate::debug_query::<Mysql, _>(&bar::table.select(bar::id)).to_string()
623        );
624    }
625
626    #[diesel_test_helper::test]
627    #[cfg(feature = "sqlite")]
628    fn table_renaming_sqlite() {
629        use crate::sqlite::Sqlite;
630        let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#;
631        assert_eq!(
632            expected_sql,
633            crate::debug_query::<Sqlite, _>(&bar::table.select(bar::id)).to_string()
634        );
635    }
636
637    mod tests_for_allow_combined_group_by_syntax {
638        use crate::table;
639
640        table! {
641            a(b) {
642                b -> Text,
643                c -> Text,
644                d -> Text,
645                e -> Text,
646            }
647        }
648
649        table! {
650            b(a) {
651                a -> Text,
652                c -> Text,
653                d -> Text,
654            }
655        }
656
657        table! {
658            c(a) {
659                a -> Text,
660                b -> Text,
661                d -> Text,
662            }
663        }
664
665        // allow using table::column
666        allow_columns_to_appear_in_same_group_by_clause!(a::b, b::a, a::d,);
667
668        // allow using full paths
669        allow_columns_to_appear_in_same_group_by_clause!(self::a::c, self::b::c, self::b::d,);
670
671        use self::a::d as a_d;
672        use self::b::d as b_d;
673        use self::c::d as c_d;
674
675        // allow using plain identifiers
676        allow_columns_to_appear_in_same_group_by_clause!(a_d, b_d, c_d);
677
678        // allow mixing all variants
679        allow_columns_to_appear_in_same_group_by_clause!(c_d, self::b::a, a::e,);
680    }
681}