sea_orm/query/
join.rs

1use crate::{
2    find_linked_recursive, join_tbl_on_condition, unpack_table_ref, ColumnTrait, EntityTrait,
3    IdenStatic, Iterable, Linked, QuerySelect, Related, Select, SelectA, SelectB, SelectThree,
4    SelectTwo, SelectTwoMany,
5};
6pub use sea_query::JoinType;
7use sea_query::{Alias, Condition, Expr, IntoIden, SeaRc, SelectExpr};
8
9impl<E> Select<E>
10where
11    E: EntityTrait,
12{
13    /// Left Join with a Related Entity.
14    pub fn left_join<R>(self, _: R) -> Self
15    where
16        R: EntityTrait,
17        E: Related<R>,
18    {
19        self.join_join(JoinType::LeftJoin, E::to(), E::via())
20    }
21
22    /// Right Join with a Related Entity.
23    pub fn right_join<R>(self, _: R) -> Self
24    where
25        R: EntityTrait,
26        E: Related<R>,
27    {
28        self.join_join(JoinType::RightJoin, E::to(), E::via())
29    }
30
31    /// Inner Join with a Related Entity.
32    pub fn inner_join<R>(self, _: R) -> Self
33    where
34        R: EntityTrait,
35        E: Related<R>,
36    {
37        self.join_join(JoinType::InnerJoin, E::to(), E::via())
38    }
39
40    /// Join with an Entity Related to me.
41    pub fn reverse_join<R>(self, _: R) -> Self
42    where
43        R: EntityTrait + Related<E>,
44    {
45        self.join_rev(JoinType::InnerJoin, R::to())
46    }
47
48    /// Left Join with a Related Entity and select both Entity.
49    pub fn find_also_related<R>(self, r: R) -> SelectTwo<E, R>
50    where
51        R: EntityTrait,
52        E: Related<R>,
53    {
54        self.left_join(r).select_also(r)
55    }
56
57    /// Left Join with a Related Entity and select the related Entity as a `Vec`
58    pub fn find_with_related<R>(self, r: R) -> SelectTwoMany<E, R>
59    where
60        R: EntityTrait,
61        E: Related<R>,
62    {
63        self.left_join(r).select_with(r)
64    }
65
66    /// Left Join with a Linked Entity and select both Entity.
67    pub fn find_also_linked<L, T>(self, l: L) -> SelectTwo<E, T>
68    where
69        L: Linked<FromEntity = E, ToEntity = T>,
70        T: EntityTrait,
71    {
72        let mut slf = self;
73        for (i, mut rel) in l.link().into_iter().enumerate() {
74            let to_tbl = Alias::new(format!("r{i}")).into_iden();
75            let from_tbl = if i > 0 {
76                Alias::new(format!("r{}", i - 1)).into_iden()
77            } else {
78                unpack_table_ref(&rel.from_tbl)
79            };
80            let table_ref = rel.to_tbl;
81
82            let mut condition = Condition::all().add(join_tbl_on_condition(
83                SeaRc::clone(&from_tbl),
84                SeaRc::clone(&to_tbl),
85                rel.from_col,
86                rel.to_col,
87            ));
88            if let Some(f) = rel.on_condition.take() {
89                condition = condition.add(f(SeaRc::clone(&from_tbl), SeaRc::clone(&to_tbl)));
90            }
91
92            slf.query()
93                .join_as(JoinType::LeftJoin, table_ref, to_tbl, condition);
94        }
95        slf = slf.apply_alias(SelectA.as_str());
96        let mut select_two = SelectTwo::new_without_prepare(slf.query);
97        for col in <T::Column as Iterable>::iter() {
98            let alias = format!("{}{}", SelectB.as_str(), col.as_str());
99            let expr = Expr::col((
100                Alias::new(format!("r{}", l.link().len() - 1)).into_iden(),
101                col.into_iden(),
102            ));
103            select_two.query().expr(SelectExpr {
104                expr: col.select_as(expr),
105                alias: Some(SeaRc::new(Alias::new(alias))),
106                window: None,
107            });
108        }
109        select_two
110    }
111
112    /// Left Join with a Linked Entity and select Entity as a `Vec`.
113    pub fn find_with_linked<L, T>(self, l: L) -> SelectTwoMany<E, T>
114    where
115        L: Linked<FromEntity = E, ToEntity = T>,
116        T: EntityTrait,
117    {
118        let mut slf = self;
119        for (i, mut rel) in l.link().into_iter().enumerate() {
120            let to_tbl = Alias::new(format!("r{i}")).into_iden();
121            let from_tbl = if i > 0 {
122                Alias::new(format!("r{}", i - 1)).into_iden()
123            } else {
124                unpack_table_ref(&rel.from_tbl)
125            };
126            let table_ref = rel.to_tbl;
127
128            let mut condition = Condition::all().add(join_tbl_on_condition(
129                SeaRc::clone(&from_tbl),
130                SeaRc::clone(&to_tbl),
131                rel.from_col,
132                rel.to_col,
133            ));
134            if let Some(f) = rel.on_condition.take() {
135                condition = condition.add(f(SeaRc::clone(&from_tbl), SeaRc::clone(&to_tbl)));
136            }
137
138            slf.query()
139                .join_as(JoinType::LeftJoin, table_ref, to_tbl, condition);
140        }
141        slf = slf.apply_alias(SelectA.as_str());
142        let mut select_two_many = SelectTwoMany::new_without_prepare(slf.query);
143        for col in <T::Column as Iterable>::iter() {
144            let alias = format!("{}{}", SelectB.as_str(), col.as_str());
145            let expr = Expr::col((
146                Alias::new(format!("r{}", l.link().len() - 1)).into_iden(),
147                col.into_iden(),
148            ));
149            select_two_many.query().expr(SelectExpr {
150                expr: col.select_as(expr),
151                alias: Some(SeaRc::new(Alias::new(alias))),
152                window: None,
153            });
154        }
155        select_two_many
156    }
157
158    #[doc(hidden)]
159    /// Recursive self-join with CTE
160    pub fn find_with_linked_recursive<L>(self, l: L) -> Select<E>
161    where
162        L: Linked<FromEntity = E, ToEntity = E>,
163    {
164        find_linked_recursive(self, l.link())
165    }
166}
167
168impl<E, F> SelectTwo<E, F>
169where
170    E: EntityTrait,
171    F: EntityTrait,
172{
173    /// Left Join with an Entity Related to the first Entity
174    pub fn find_also_related<R>(self, r: R) -> SelectThree<E, F, R>
175    where
176        R: EntityTrait,
177        E: Related<R>,
178    {
179        self.join_join(JoinType::LeftJoin, E::to(), E::via())
180            .select_also(r)
181    }
182
183    /// Left Join with an Entity Related to the second Entity
184    pub fn and_also_related<R>(self, r: R) -> SelectThree<E, F, R>
185    where
186        R: EntityTrait,
187        F: Related<R>,
188    {
189        self.join_join(JoinType::LeftJoin, F::to(), F::via())
190            .select_also(r)
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use crate::tests_cfg::{cake, cake_filling, cake_filling_price, entity_linked, filling, fruit};
197    use crate::{
198        ColumnTrait, DbBackend, EntityTrait, ModelTrait, QueryFilter, QuerySelect, QueryTrait,
199        RelationTrait,
200    };
201    use pretty_assertions::assert_eq;
202    use sea_query::{Alias, ConditionType, Expr, IntoCondition, JoinType};
203
204    #[test]
205    fn join_1() {
206        assert_eq!(
207            cake::Entity::find()
208                .left_join(fruit::Entity)
209                .build(DbBackend::MySql)
210                .to_string(),
211            [
212                "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
213                "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
214            ]
215            .join(" ")
216        );
217    }
218
219    #[test]
220    fn join_2() {
221        assert_eq!(
222            cake::Entity::find()
223                .inner_join(fruit::Entity)
224                .filter(fruit::Column::Name.contains("cherry"))
225                .build(DbBackend::MySql)
226                .to_string(),
227            [
228                "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
229                "INNER JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
230                "WHERE `fruit`.`name` LIKE \'%cherry%\'"
231            ]
232            .join(" ")
233        );
234    }
235
236    #[test]
237    fn join_3() {
238        assert_eq!(
239            fruit::Entity::find()
240                .reverse_join(cake::Entity)
241                .build(DbBackend::MySql)
242                .to_string(),
243            [
244                "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
245                "INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
246            ]
247            .join(" ")
248        );
249    }
250
251    #[test]
252    fn join_4() {
253        use crate::{Related, Select};
254
255        let find_fruit: Select<fruit::Entity> = cake::Entity::find_related();
256        assert_eq!(
257            find_fruit
258                .filter(cake::Column::Id.eq(11))
259                .build(DbBackend::MySql)
260                .to_string(),
261            [
262                "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
263                "INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
264                "WHERE `cake`.`id` = 11",
265            ]
266            .join(" ")
267        );
268    }
269
270    #[test]
271    fn join_5() {
272        let cake_model = cake::Model {
273            id: 12,
274            name: "".to_owned(),
275        };
276
277        assert_eq!(
278            cake_model
279                .find_related(fruit::Entity)
280                .build(DbBackend::MySql)
281                .to_string(),
282            [
283                "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
284                "INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
285                "WHERE `cake`.`id` = 12",
286            ]
287            .join(" ")
288        );
289    }
290
291    #[test]
292    fn join_6() {
293        assert_eq!(
294            cake::Entity::find()
295                .left_join(filling::Entity)
296                .build(DbBackend::MySql)
297                .to_string(),
298            [
299                "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
300                "LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id`",
301                "LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id`",
302            ]
303            .join(" ")
304        );
305    }
306
307    #[test]
308    fn join_7() {
309        use crate::{Related, Select};
310
311        let find_filling: Select<filling::Entity> = cake::Entity::find_related();
312        assert_eq!(
313            find_filling.build(DbBackend::MySql).to_string(),
314            [
315                "SELECT `filling`.`id`, `filling`.`name`, `filling`.`vendor_id` FROM `filling`",
316                "INNER JOIN `cake_filling` ON `cake_filling`.`filling_id` = `filling`.`id`",
317                "INNER JOIN `cake` ON `cake`.`id` = `cake_filling`.`cake_id`",
318            ]
319            .join(" ")
320        );
321    }
322
323    #[test]
324    fn join_8() {
325        use crate::{Related, Select};
326
327        let find_cake_filling_price: Select<cake_filling_price::Entity> =
328            cake_filling::Entity::find_related();
329        assert_eq!(
330            find_cake_filling_price.build(DbBackend::Postgres).to_string(),
331            [
332                r#"SELECT "cake_filling_price"."cake_id", "cake_filling_price"."filling_id", "cake_filling_price"."price""#,
333                r#"FROM "public"."cake_filling_price""#,
334                r#"INNER JOIN "cake_filling" ON"#,
335                r#""cake_filling"."cake_id" = "cake_filling_price"."cake_id" AND"#,
336                r#""cake_filling"."filling_id" = "cake_filling_price"."filling_id""#,
337            ]
338            .join(" ")
339        );
340    }
341
342    #[test]
343    fn join_9() {
344        use crate::{Related, Select};
345
346        let find_cake_filling: Select<cake_filling::Entity> =
347            cake_filling_price::Entity::find_related();
348        assert_eq!(
349            find_cake_filling.build(DbBackend::Postgres).to_string(),
350            [
351                r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id""#,
352                r#"FROM "cake_filling""#,
353                r#"INNER JOIN "public"."cake_filling_price" ON"#,
354                r#""cake_filling_price"."cake_id" = "cake_filling"."cake_id" AND"#,
355                r#""cake_filling_price"."filling_id" = "cake_filling"."filling_id""#,
356            ]
357            .join(" ")
358        );
359    }
360
361    #[test]
362    fn join_10() {
363        let cake_model = cake::Model {
364            id: 12,
365            name: "".to_owned(),
366        };
367
368        assert_eq!(
369            cake_model
370                .find_linked(entity_linked::CakeToFilling)
371                .build(DbBackend::MySql)
372                .to_string(),
373            [
374                r#"SELECT `filling`.`id`, `filling`.`name`, `filling`.`vendor_id`"#,
375                r#"FROM `filling`"#,
376                r#"INNER JOIN `cake_filling` AS `r0` ON `r0`.`filling_id` = `filling`.`id`"#,
377                r#"INNER JOIN `cake` AS `r1` ON `r1`.`id` = `r0`.`cake_id`"#,
378                r#"WHERE `r1`.`id` = 12"#,
379            ]
380            .join(" ")
381        );
382    }
383
384    #[test]
385    fn join_11() {
386        let cake_model = cake::Model {
387            id: 18,
388            name: "".to_owned(),
389        };
390
391        assert_eq!(
392            cake_model
393                .find_linked(entity_linked::CakeToFillingVendor)
394                .build(DbBackend::MySql)
395                .to_string(),
396            [
397                r#"SELECT `vendor`.`id`, `vendor`.`name`"#,
398                r#"FROM `vendor`"#,
399                r#"INNER JOIN `filling` AS `r0` ON `r0`.`vendor_id` = `vendor`.`id`"#,
400                r#"INNER JOIN `cake_filling` AS `r1` ON `r1`.`filling_id` = `r0`.`id`"#,
401                r#"INNER JOIN `cake` AS `r2` ON `r2`.`id` = `r1`.`cake_id`"#,
402                r#"WHERE `r2`.`id` = 18"#,
403            ]
404            .join(" ")
405        );
406    }
407
408    #[test]
409    fn join_12() {
410        assert_eq!(
411            cake::Entity::find()
412                .find_also_linked(entity_linked::CakeToFilling)
413                .build(DbBackend::MySql)
414                .to_string(),
415            [
416                r#"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,"#,
417                r#"`r1`.`id` AS `B_id`, `r1`.`name` AS `B_name`, `r1`.`vendor_id` AS `B_vendor_id`"#,
418                r#"FROM `cake`"#,
419                r#"LEFT JOIN `cake_filling` AS `r0` ON `cake`.`id` = `r0`.`cake_id`"#,
420                r#"LEFT JOIN `filling` AS `r1` ON `r0`.`filling_id` = `r1`.`id`"#,
421            ]
422            .join(" ")
423        );
424    }
425
426    #[test]
427    fn join_13() {
428        assert_eq!(
429            cake::Entity::find()
430                .find_also_linked(entity_linked::CakeToFillingVendor)
431                .build(DbBackend::MySql)
432                .to_string(),
433            [
434                r#"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,"#,
435                r#"`r2`.`id` AS `B_id`, `r2`.`name` AS `B_name`"#,
436                r#"FROM `cake`"#,
437                r#"LEFT JOIN `cake_filling` AS `r0` ON `cake`.`id` = `r0`.`cake_id`"#,
438                r#"LEFT JOIN `filling` AS `r1` ON `r0`.`filling_id` = `r1`.`id`"#,
439                r#"LEFT JOIN `vendor` AS `r2` ON `r1`.`vendor_id` = `r2`.`id`"#,
440            ]
441            .join(" ")
442        );
443    }
444
445    #[test]
446    fn join_14() {
447        assert_eq!(
448            cake::Entity::find()
449                .join(JoinType::LeftJoin, cake::Relation::TropicalFruit.def())
450                .build(DbBackend::MySql)
451                .to_string(),
452            [
453                "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
454                "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'",
455            ]
456            .join(" ")
457        );
458    }
459
460    #[test]
461    fn join_15() {
462        let cake_model = cake::Model {
463            id: 18,
464            name: "".to_owned(),
465        };
466
467        assert_eq!(
468            cake_model
469                .find_linked(entity_linked::CheeseCakeToFillingVendor)
470                .build(DbBackend::MySql)
471                .to_string(),
472            [
473                r#"SELECT `vendor`.`id`, `vendor`.`name`"#,
474                r#"FROM `vendor`"#,
475                r#"INNER JOIN `filling` AS `r0` ON `r0`.`vendor_id` = `vendor`.`id`"#,
476                r#"INNER JOIN `cake_filling` AS `r1` ON `r1`.`filling_id` = `r0`.`id`"#,
477                r#"INNER JOIN `cake` AS `r2` ON `r2`.`id` = `r1`.`cake_id` AND `r2`.`name` LIKE '%cheese%'"#,
478                r#"WHERE `r2`.`id` = 18"#,
479            ]
480            .join(" ")
481        );
482    }
483
484    #[test]
485    fn join_16() {
486        // removed wrong test case
487    }
488
489    #[test]
490    fn join_17() {
491        assert_eq!(
492            cake::Entity::find()
493                .find_also_linked(entity_linked::CheeseCakeToFillingVendor)
494                .build(DbBackend::MySql)
495                .to_string(),
496            [
497                r#"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,"#,
498                r#"`r2`.`id` AS `B_id`, `r2`.`name` AS `B_name`"#,
499                r#"FROM `cake`"#,
500                r#"LEFT JOIN `cake_filling` AS `r0` ON `cake`.`id` = `r0`.`cake_id` AND `cake`.`name` LIKE '%cheese%'"#,
501                r#"LEFT JOIN `filling` AS `r1` ON `r0`.`filling_id` = `r1`.`id`"#,
502                r#"LEFT JOIN `vendor` AS `r2` ON `r1`.`vendor_id` = `r2`.`id`"#,
503            ]
504            .join(" ")
505        );
506    }
507
508    #[test]
509    fn join_18() {
510        // removed wrong test case
511    }
512
513    #[test]
514    fn join_19() {
515        assert_eq!(
516            cake::Entity::find()
517                .join(JoinType::LeftJoin, cake::Relation::TropicalFruit.def())
518                .join(
519                    JoinType::LeftJoin,
520                    cake_filling::Relation::Cake
521                        .def()
522                        .rev()
523                        .on_condition(|_left, right| {
524                            Expr::col((right, cake_filling::Column::CakeId))
525                                .gt(10)
526                                .into_condition()
527                        })
528                )
529                .join(
530                    JoinType::LeftJoin,
531                    cake_filling::Relation::Filling
532                        .def()
533                        .on_condition(|_left, right| {
534                            Expr::col((right, filling::Column::Name))
535                                .like("%lemon%")
536                                .into_condition()
537                        })
538                )
539                .join(JoinType::LeftJoin, filling::Relation::Vendor.def())
540                .build(DbBackend::MySql)
541                .to_string(),
542            [
543                "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
544                "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'",
545                "LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id` AND `cake_filling`.`cake_id` > 10",
546                "LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id` AND `filling`.`name` LIKE '%lemon%'",
547                "LEFT JOIN `vendor` ON `filling`.`vendor_id` = `vendor`.`id`",
548            ]
549            .join(" ")
550        );
551    }
552
553    #[test]
554    fn join_20() {
555        assert_eq!(
556            cake::Entity::find()
557                .column_as(
558                    Expr::col((Alias::new("fruit_alias"), fruit::Column::Name)),
559                    "fruit_name"
560                )
561                .join_as(
562                    JoinType::LeftJoin,
563                    cake::Relation::Fruit
564                        .def()
565                        .on_condition(|_left, right| {
566                            Expr::col((right, fruit::Column::Name))
567                                .like("%tropical%")
568                                .into_condition()
569                        }),
570                    Alias::new("fruit_alias")
571                )
572                .build(DbBackend::MySql)
573                .to_string(),
574            [
575                "SELECT `cake`.`id`, `cake`.`name`, `fruit_alias`.`name` AS `fruit_name` FROM `cake`",
576                "LEFT JOIN `fruit` AS `fruit_alias` ON `cake`.`id` = `fruit_alias`.`cake_id` AND `fruit_alias`.`name` LIKE '%tropical%'",
577            ]
578            .join(" ")
579        );
580    }
581
582    #[test]
583    fn join_21() {
584        assert_eq!(
585            cake::Entity::find()
586                .column_as(
587                    Expr::col((Alias::new("cake_filling_alias"), cake_filling::Column::CakeId)),
588                    "cake_filling_cake_id"
589                )
590                .join(JoinType::LeftJoin, cake::Relation::TropicalFruit.def())
591                .join_as_rev(
592                    JoinType::LeftJoin,
593                    cake_filling::Relation::Cake
594                        .def()
595                        .on_condition(|left, _right| {
596                            Expr::col((left, cake_filling::Column::CakeId))
597                                .gt(10)
598                                .into_condition()
599                        }),
600                    Alias::new("cake_filling_alias")
601                )
602                .build(DbBackend::MySql)
603                .to_string(),
604            [
605                "SELECT `cake`.`id`, `cake`.`name`, `cake_filling_alias`.`cake_id` AS `cake_filling_cake_id` FROM `cake`",
606                "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'",
607                "LEFT JOIN `cake_filling` AS `cake_filling_alias` ON `cake_filling_alias`.`cake_id` = `cake`.`id` AND `cake_filling_alias`.`cake_id` > 10",
608            ]
609            .join(" ")
610        );
611    }
612
613    #[test]
614    fn join_22() {
615        assert_eq!(
616            cake::Entity::find()
617                .column_as(
618                    Expr::col((Alias::new("cake_filling_alias"), cake_filling::Column::CakeId)),
619                    "cake_filling_cake_id"
620                )
621                .join(JoinType::LeftJoin, cake::Relation::OrTropicalFruit.def())
622                .join_as_rev(
623                    JoinType::LeftJoin,
624                    cake_filling::Relation::Cake
625                        .def()
626                        .condition_type(ConditionType::Any)
627                        .on_condition(|left, _right| {
628                            Expr::col((left, cake_filling::Column::CakeId))
629                                .gt(10)
630                                .into_condition()
631                        }),
632                    Alias::new("cake_filling_alias")
633                )
634                .build(DbBackend::MySql)
635                .to_string(),
636            [
637                "SELECT `cake`.`id`, `cake`.`name`, `cake_filling_alias`.`cake_id` AS `cake_filling_cake_id` FROM `cake`",
638                "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` OR `fruit`.`name` LIKE '%tropical%'",
639                "LEFT JOIN `cake_filling` AS `cake_filling_alias` ON `cake_filling_alias`.`cake_id` = `cake`.`id` OR `cake_filling_alias`.`cake_id` > 10",
640            ]
641            .join(" ")
642        );
643    }
644
645    #[test]
646    fn join_23() {
647        let cake_model = cake::Model {
648            id: 18,
649            name: "".to_owned(),
650        };
651
652        assert_eq!(
653            cake_model
654                .find_linked(entity_linked::CakeToCakeViaFilling)
655                .build(DbBackend::MySql)
656                .to_string(),
657            [
658                "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
659                "INNER JOIN `cake_filling` AS `r0` ON `r0`.`cake_id` = `cake`.`id`",
660                "INNER JOIN `filling` AS `r1` ON `r1`.`id` = `r0`.`filling_id`",
661                "INNER JOIN `cake_filling` AS `r2` ON `r2`.`filling_id` = `r1`.`id`",
662                "INNER JOIN `cake` AS `r3` ON `r3`.`id` = `r2`.`cake_id`",
663                "WHERE `r3`.`id` = 18",
664            ]
665            .join(" ")
666        );
667    }
668
669    #[test]
670    fn join_24() {
671        let cake_model = cake::Model {
672            id: 12,
673            name: "".to_owned(),
674        };
675
676        assert_eq!(
677            cake_model
678                .find_linked_recursive(entity_linked::CakeToCakeViaFilling)
679                .build(DbBackend::MySql)
680                .to_string(),
681            [
682                "WITH `cte` AS (SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
683                "INNER JOIN `cake_filling` AS `r0` ON `r0`.`cake_id` = `cake`.`id`",
684                "INNER JOIN `filling` AS `r1` ON `r1`.`id` = `r0`.`filling_id`",
685                "INNER JOIN `cake_filling` AS `r2` ON `r2`.`filling_id` = `r1`.`id`",
686                "INNER JOIN `cake` AS `r3` ON `r3`.`id` = `r2`.`cake_id`",
687                "WHERE `r3`.`id` = 12",
688                "UNION ALL (SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
689                "INNER JOIN `cake_filling` AS `r0` ON `r0`.`cake_id` = `cake`.`id`",
690                "INNER JOIN `filling` AS `r1` ON `r1`.`id` = `r0`.`filling_id`",
691                "INNER JOIN `cake_filling` AS `r2` ON `r2`.`filling_id` = `r1`.`id`",
692                "INNER JOIN `cte` AS `r3` ON `r3`.`id` = `r2`.`cake_id`))",
693                "SELECT `cake`.`id`, `cake`.`name` FROM `cte` AS `cake`",
694            ]
695            .join(" ")
696        );
697    }
698
699    #[test]
700    fn join_25() {
701        assert_eq!(
702            cake::Entity::find()
703                .find_with_linked_recursive(entity_linked::CakeToCakeViaFilling)
704                .build(DbBackend::MySql)
705                .to_string(),
706            [
707                "WITH `cte` AS (SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
708                "UNION ALL (SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
709                "INNER JOIN `cake_filling` AS `r0` ON `r0`.`cake_id` = `cake`.`id`",
710                "INNER JOIN `filling` AS `r1` ON `r1`.`id` = `r0`.`filling_id`",
711                "INNER JOIN `cake_filling` AS `r2` ON `r2`.`filling_id` = `r1`.`id`",
712                "INNER JOIN `cte` AS `r3` ON `r3`.`id` = `r2`.`cake_id`))",
713                "SELECT `cake`.`id`, `cake`.`name` FROM `cte` AS `cake`",
714            ]
715            .join(" ")
716        );
717    }
718}