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#[doc(hidden)]
132#[macro_export]
133macro_rules! __diesel_impl_allow_in_same_group_by_clause {
134    (
135        left = [$($left_path:tt)::+],
136    ) => {};
137    (
138        left = [$($left_path:tt)::+],
139        $($right_path:tt)::+
140    ) => {
141        $crate::__diesel_impl_allow_in_same_group_by_clause! {
142            left = [$($left_path)+],
143            right = [$($right_path)+],
144            left_tbl = [],
145            left_path = [],
146        }
147    };
148    (
149        left = [$($left_path:tt)::+],
150        $($right_path:tt)::+,
151        $($other:tt)*
152    ) => {
153        $crate::__diesel_impl_allow_in_same_group_by_clause! {
154            left = [$($left_path)+],
155            right = [$($right_path)+],
156            left_tbl = [],
157            left_path = [],
158        }
159        $crate::__diesel_impl_allow_in_same_group_by_clause! {
160            left = [$($left_path)::+],
161            $($other)*
162        }
163    };
164    (
165        left = [$left_path_p1: tt  $($left_path: tt)+],
166        right = [$($right_path: tt)*],
167        left_tbl = [$($left_tbl:tt)?],
168        left_path = [$($left_out_path:tt)*],
169    ) => {
170        $crate::__diesel_impl_allow_in_same_group_by_clause! {
171            left = [$($left_path)+],
172            right = [$($right_path)*],
173            left_tbl = [$left_path_p1],
174            left_path = [$($left_out_path)* $($left_tbl)?],
175        }
176    };
177    (
178        left = [$left_col: tt],
179        right = [$($right_path: tt)*],
180        left_tbl = [$($left_tbl:tt)?],
181        left_path = [$($left_out_path:tt)*],
182    ) => {
183        $crate::__diesel_impl_allow_in_same_group_by_clause! {
184            left = [$left_col],
185            right = [$($right_path)*],
186            left_tbl = [$($left_tbl)?],
187            left_path = [$($left_out_path)*],
188            right_tbl = [],
189            right_path = [],
190        }
191    };
192    (
193        left = [$left_col: tt ],
194        right = [$right_path_p1: tt  $($right_path: tt)+],
195        left_tbl = [$($left_tbl:tt)?],
196        left_path = [$($left_out_path:tt)*],
197        right_tbl = [$($right_tbl:tt)?],
198        right_path = [$($right_out_path:tt)*],
199    ) => {
200        $crate::__diesel_impl_allow_in_same_group_by_clause! {
201            left = [$left_col],
202            right = [$($right_path)+],
203            left_tbl = [$($left_tbl)?],
204            left_path = [$($left_out_path)*],
205            right_tbl = [$right_path_p1],
206            right_path = [$($right_out_path)* $($right_tbl)?],
207        }
208    };
209    (
210        left = [$left_col: tt],
211        right = [$right_col: tt],
212        left_tbl = [$left_tbl:tt],
213        left_path = [$($left_begin:tt)*],
214        right_tbl = [$right_tbl:tt],
215        right_path = [$($right_begin:tt)*],
216    ) => {
217        $crate::static_cond! {
218            if $left_tbl != $right_tbl {
219                impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $left_tbl :: $left_col> for $($right_begin ::)* $right_tbl :: $right_col {
220                    type Output = $crate::expression::is_contained_in_group_by::No;
221                }
222
223                impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $right_tbl :: $right_col> for $($left_begin ::)* $left_tbl :: $left_col {
224                    type Output = $crate::expression::is_contained_in_group_by::No;
225                }
226            }
227        }
228    };
229    (
230        left = [$left_col: tt],
231        right = [$right_col: tt],
232        left_tbl = [$($left_tbl:tt)?],
233        left_path = [$($left_begin:tt)*],
234        right_tbl = [$($right_tbl:tt)?],
235        right_path = [$($right_begin:tt)*],
236    ) => {
237        impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $($left_tbl ::)? $left_col> for $($right_begin ::)* $($right_tbl ::)? $right_col {
238            type Output = $crate::expression::is_contained_in_group_by::No;
239        }
240
241        impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $($right_tbl ::)? $right_col> for $($left_begin ::)* $($left_tbl ::)? $left_col {
242            type Output = $crate::expression::is_contained_in_group_by::No;
243        }
244    };
245
246}
247
248/// Allow two or more columns which are otherwise unrelated to be used together
249/// in a group by clause.
250///
251/// This macro must be invoked any time two columns need to appear in the same
252/// group by clause. When this macro is invoked with more than 2 columns, every
253/// combination of those columns will be allowed to appear together.
254///
255/// # Example
256///
257/// ```
258/// # include!("../doctest_setup.rs");
259/// # use crate::schema::{users, posts};
260/// // This would be required
261///
262/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id, posts::title);
263/// # fn main() {
264/// // to do implement the following join
265/// users::table
266///     .inner_join(posts::table)
267///     .group_by((users::name, posts::id, posts::title))
268/// # ;
269/// # }
270/// ```
271///
272/// When more than two columns are passed, the relevant code is generated for
273/// every combination of those columns. This code would be equivalent to the
274/// previous example.
275///
276/// ```
277/// # include!("../doctest_setup.rs");
278/// # use crate::schema::{users, posts};
279/// #
280/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::title);
281/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id);
282/// allow_columns_to_appear_in_same_group_by_clause!(posts::title, posts::id);
283/// # fn main() {}
284/// ```
285#[macro_export]
286macro_rules! allow_columns_to_appear_in_same_group_by_clause {
287    ($($left_path:tt)::+, $($right_path:tt)::+ $(,)?) => {
288        $crate::__diesel_impl_allow_in_same_group_by_clause! {
289            left = [$($left_path)::+],
290            $($right_path)::+,
291        }
292    };
293    ($($left_path:tt)::+, $($right_path:tt)::+, $($other: tt)*) => {
294        $crate::__diesel_impl_allow_in_same_group_by_clause! {
295            left = [$($left_path)::+],
296            $($right_path)::+,
297            $($other)*
298        }
299        $crate::allow_columns_to_appear_in_same_group_by_clause! {
300            $($right_path)::+,
301            $($other)*
302        }
303    };
304    ($last_col:ty,) => {};
305    () => {};
306}
307
308#[macro_export]
309#[doc(hidden)]
310macro_rules! __diesel_with_dollar_sign {
311    ($($body:tt)*) => {
312        macro_rules! __with_dollar_sign { $($body)* }
313        __with_dollar_sign!($);
314    }
315}
316
317// The order of these modules is important (at least for those which have tests).
318// Utility macros which don't call any others need to come first.
319#[macro_use]
320mod internal;
321#[macro_use]
322mod static_cond;
323#[macro_use]
324mod ops;
325
326#[cfg(test)]
327mod tests {
328    use crate::prelude::*;
329
330    table! {
331        foo.bars {
332            id -> Integer,
333            baz -> Text,
334        }
335    }
336
337    mod my_types {
338        #[derive(Debug, Clone, Copy, crate::sql_types::SqlType)]
339        pub struct MyCustomType;
340    }
341
342    table! {
343        use crate::sql_types::*;
344        use crate::macros::tests::my_types::*;
345
346        table_with_custom_types {
347            id -> Integer,
348            my_type -> MyCustomType,
349        }
350    }
351
352    table! {
353        use crate::sql_types::*;
354        use crate::macros::tests::my_types::*;
355
356        /// Table documentation
357        ///
358        /// some in detail documentation
359        table_with_custom_type_and_id (a) {
360            /// Column documentation
361            ///
362            /// some more details
363            a -> Integer,
364            my_type -> MyCustomType,
365        }
366    }
367
368    #[diesel_test_helper::test]
369    #[cfg(feature = "postgres")]
370    fn table_with_custom_schema() {
371        use crate::pg::Pg;
372        let expected_sql = r#"SELECT "foo"."bars"."baz" FROM "foo"."bars" -- binds: []"#;
373        assert_eq!(
374            expected_sql,
375            &crate::debug_query::<Pg, _>(&bars::table.select(bars::baz)).to_string()
376        );
377    }
378
379    table! {
380        use crate::sql_types;
381        use crate::sql_types::*;
382
383        table_with_arbitrarily_complex_types {
384            id -> sql_types::Integer,
385            qualified_nullable -> sql_types::Nullable<sql_types::Integer>,
386            deeply_nested_type -> Nullable<Nullable<Integer>>,
387            // This actually should work, but there appears to be a rustc bug
388            // on the `AsExpression` bound for `EqAll` when the ty param is a projection
389            // projected_type -> <Nullable<Integer> as sql_types::IntoNullable>::Nullable,
390            //random_tuple -> (Integer, Integer),
391        }
392    }
393
394    table!(
395        foo {
396            /// Column doc
397            id -> Integer,
398
399            #[sql_name = "type"]
400            /// Also important to document this column
401            mytype -> Integer,
402
403            /// And this one
404            #[sql_name = "bleh"]
405            hey -> Integer,
406        }
407    );
408
409    #[diesel_test_helper::test]
410    #[cfg(feature = "postgres")]
411    fn table_with_column_renaming_postgres() {
412        use crate::pg::Pg;
413        let expected_sql = r#"SELECT "foo"."id", "foo"."type", "foo"."bleh" FROM "foo" WHERE ("foo"."type" = $1) -- binds: [1]"#;
414        assert_eq!(
415            expected_sql,
416            crate::debug_query::<Pg, _>(&foo::table.filter(foo::mytype.eq(1))).to_string()
417        );
418    }
419
420    #[diesel_test_helper::test]
421    #[cfg(feature = "mysql")]
422    fn table_with_column_renaming_mysql() {
423        use crate::mysql::Mysql;
424        let expected_sql = r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#;
425        assert_eq!(
426            expected_sql,
427            crate::debug_query::<Mysql, _>(&foo::table.filter(foo::mytype.eq(1))).to_string()
428        );
429    }
430
431    #[diesel_test_helper::test]
432    #[cfg(feature = "sqlite")]
433    fn table_with_column_renaming_sqlite() {
434        use crate::sqlite::Sqlite;
435        let expected_sql = r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#;
436        assert_eq!(
437            expected_sql,
438            crate::debug_query::<Sqlite, _>(&foo::table.filter(foo::mytype.eq(1))).to_string()
439        );
440    }
441
442    table!(
443        use crate::sql_types::*;
444
445        /// Some documentation
446        #[sql_name="mod"]
447        /// Some more documentation
448        bar {
449            id -> Integer,
450        }
451    );
452
453    #[diesel_test_helper::test]
454    #[cfg(feature = "postgres")]
455    fn table_renaming_postgres() {
456        use crate::pg::Pg;
457        let expected_sql = r#"SELECT "mod"."id" FROM "mod" -- binds: []"#;
458        assert_eq!(
459            expected_sql,
460            crate::debug_query::<Pg, _>(&bar::table.select(bar::id)).to_string()
461        );
462    }
463
464    #[diesel_test_helper::test]
465    #[cfg(feature = "mysql")]
466    fn table_renaming_mysql() {
467        use crate::mysql::Mysql;
468        let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#;
469        assert_eq!(
470            expected_sql,
471            crate::debug_query::<Mysql, _>(&bar::table.select(bar::id)).to_string()
472        );
473    }
474
475    #[diesel_test_helper::test]
476    #[cfg(feature = "sqlite")]
477    fn table_renaming_sqlite() {
478        use crate::sqlite::Sqlite;
479        let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#;
480        assert_eq!(
481            expected_sql,
482            crate::debug_query::<Sqlite, _>(&bar::table.select(bar::id)).to_string()
483        );
484    }
485
486    mod tests_for_allow_combined_group_by_syntax {
487        use crate::table;
488
489        table! {
490            a(b) {
491                b -> Text,
492                c -> Text,
493                d -> Text,
494                e -> Text,
495            }
496        }
497
498        table! {
499            b(a) {
500                a -> Text,
501                c -> Text,
502                d -> Text,
503            }
504        }
505
506        table! {
507            c(a) {
508                a -> Text,
509                b -> Text,
510                d -> Text,
511            }
512        }
513
514        // allow using table::column
515        allow_columns_to_appear_in_same_group_by_clause!(a::b, b::a, a::d,);
516
517        // allow using full paths
518        allow_columns_to_appear_in_same_group_by_clause!(self::a::c, self::b::c, self::b::d,);
519
520        use self::a::d as a_d;
521        use self::b::d as b_d;
522        use self::c::d as c_d;
523
524        // allow using plain identifiers
525        allow_columns_to_appear_in_same_group_by_clause!(a_d, b_d, c_d);
526
527        // allow mixing all variants
528        allow_columns_to_appear_in_same_group_by_clause!(c_d, self::b::a, a::e,);
529    }
530}