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