1use crate::{
2 ColumnTrait, EntityTrait, IdenStatic, Iterable, Linked, QueryFilter, QuerySelect, QueryTrait,
3 Related, Select, SelectA, SelectB, SelectThree, SelectTwo, SelectTwoMany, SelectTwoRequired,
4 TopologyChain, TopologyStar, find_linked_recursive, join_tbl_on_condition,
5};
6pub use sea_query::JoinType;
7use sea_query::{Condition, ConditionType, Expr, IntoCondition, IntoIden, SelectExpr};
8
9impl<E> Select<E>
10where
11 E: EntityTrait,
12{
13 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 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 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 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 pub fn find_also<R>(self, _: E, 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 pub fn find_also_related<R>(self, r: R) -> SelectTwo<E, R>
59 where
60 R: EntityTrait,
61 E: Related<R>,
62 {
63 self.left_join(r).select_also(r)
64 }
65
66 pub fn find_both_related<R>(self, r: R) -> SelectTwoRequired<E, R>
68 where
69 R: EntityTrait,
70 E: Related<R>,
71 {
72 self.inner_join(r).select_two_required(r)
73 }
74
75 pub fn find_with_related<R>(self, r: R) -> SelectTwoMany<E, R>
77 where
78 R: EntityTrait,
79 E: Related<R>,
80 {
81 self.left_join(r).select_with(r)
82 }
83
84 pub fn find_also_linked<L, T>(self, l: L) -> SelectTwo<E, T>
86 where
87 L: Linked<FromEntity = E, ToEntity = T>,
88 T: EntityTrait,
89 {
90 SelectTwo::new_without_prepare(self.left_join_linked(l).into_query())
91 }
92
93 pub fn find_with_linked<L, T>(self, l: L) -> SelectTwoMany<E, T>
95 where
96 L: Linked<FromEntity = E, ToEntity = T>,
97 T: EntityTrait,
98 {
99 SelectTwoMany::new_without_prepare(self.left_join_linked(l).into_query())
100 }
101
102 pub fn left_join_linked<L, T>(mut self, l: L) -> Self
104 where
105 L: Linked<FromEntity = E, ToEntity = T>,
106 T: EntityTrait,
107 {
108 for (i, mut rel) in l.link().into_iter().enumerate() {
109 let r = self.linked_index;
110 self.linked_index += 1;
111 let to_tbl = format!("r{r}").into_iden();
112 let from_tbl = if i > 0 {
113 format!("r{}", r - 1).into_iden()
114 } else {
115 rel.from_tbl.sea_orm_table().clone()
116 };
117 let table_ref = rel.to_tbl;
118
119 let mut condition = match rel.condition_type {
120 ConditionType::All => Condition::all(),
121 ConditionType::Any => Condition::any(),
122 };
123 condition = condition.add(join_tbl_on_condition(
124 from_tbl.clone(),
125 to_tbl.clone(),
126 rel.from_col,
127 rel.to_col,
128 ));
129 if let Some(f) = rel.on_condition.take() {
130 condition = condition.add(f(from_tbl.clone(), to_tbl.clone()));
131 }
132
133 self.query
134 .join_as(JoinType::LeftJoin, table_ref, to_tbl, condition);
135 }
136 self = self.apply_alias(SelectA.as_str());
137 for col in <T::Column as Iterable>::iter() {
138 let alias = format!("{}{}", SelectB.as_str(), col.as_str());
139 let expr = Expr::col((
140 format!("r{}", self.linked_index - 1).into_iden(),
141 col.into_iden(),
142 ));
143 self.query.expr(SelectExpr {
144 expr: col.select_as(expr),
145 alias: Some(alias.into_iden()),
146 window: None,
147 });
148 }
149 self
150 }
151
152 pub fn has_related<R, C>(mut self, _: R, condition: C) -> Self
185 where
186 R: EntityTrait,
187 E: Related<R>,
188 C: IntoCondition,
189 {
190 let mut to = None;
191 let mut condition = Condition::all().add(condition.into_condition());
192 condition = condition.add(if let Some(via) = E::via() {
193 to = Some(E::to());
194 via
195 } else {
196 E::to()
197 });
198 let mut subquery = R::find()
199 .select_only()
200 .expr(Expr::cust("1"))
201 .filter(condition)
202 .into_query();
203 if let Some(to) = to {
204 subquery.inner_join(to.from_tbl.clone(), to);
206 }
207 self.query.cond_where(Expr::exists(subquery));
208 self
209 }
210
211 #[doc(hidden)]
212 pub fn find_with_linked_recursive<L>(self, l: L) -> Select<E>
214 where
215 L: Linked<FromEntity = E, ToEntity = E>,
216 {
217 find_linked_recursive(self, l.link())
218 }
219}
220
221impl<E, F> SelectTwo<E, F>
222where
223 E: EntityTrait,
224 F: EntityTrait,
225{
226 #[doc(hidden)]
228 pub fn select_also_fake<R>(self, _: R) -> SelectThree<E, F, R, TopologyStar>
229 where
230 R: EntityTrait,
231 {
232 SelectThree::new_without_prepare(self.into_query())
234 }
235
236 pub fn find_also<G, R>(self, _: G, _: R) -> SelectThree<E, F, R, TopologyStar>
238 where
239 R: EntityTrait,
240 G: EntityTrait + Related<R>,
241 {
242 SelectThree::new(
243 self.join_join(JoinType::LeftJoin, G::to(), G::via())
244 .into_query(),
245 )
246 }
247
248 pub fn find_also_related<R>(self, _: R) -> SelectThree<E, F, R, TopologyStar>
250 where
251 R: EntityTrait,
252 E: Related<R>,
253 {
254 SelectThree::new(
255 self.join_join(JoinType::LeftJoin, E::to(), E::via())
256 .into_query(),
257 )
258 }
259
260 pub fn and_also_related<R>(self, _: R) -> SelectThree<E, F, R, TopologyChain>
262 where
263 R: EntityTrait,
264 F: Related<R>,
265 {
266 SelectThree::new(
267 self.join_join(JoinType::LeftJoin, F::to(), F::via())
268 .into_query(),
269 )
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use crate::tests_cfg::{
276 cake, cake_compact, cake_filling, cake_filling_price, entity_linked, filling, fruit,
277 };
278 use crate::{
279 ColumnTrait, DbBackend, EntityTrait, ModelTrait, QueryFilter, QuerySelect, QueryTrait,
280 RelationTrait,
281 };
282 use pretty_assertions::assert_eq;
283 use sea_query::{ConditionType, Expr, ExprTrait, IntoCondition, JoinType};
284
285 #[test]
286 fn join_1() {
287 assert_eq!(
288 cake::Entity::find()
289 .left_join(fruit::Entity)
290 .build(DbBackend::MySql)
291 .to_string(),
292 [
293 "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
294 "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
295 ]
296 .join(" ")
297 );
298 }
299
300 #[test]
301 fn join_2() {
302 assert_eq!(
303 cake::Entity::find()
304 .inner_join(fruit::Entity)
305 .filter(fruit::Column::Name.contains("cherry"))
306 .build(DbBackend::MySql)
307 .to_string(),
308 [
309 "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
310 "INNER JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
311 "WHERE `fruit`.`name` LIKE \'%cherry%\'"
312 ]
313 .join(" ")
314 );
315 }
316
317 #[test]
318 fn join_3() {
319 assert_eq!(
320 fruit::Entity::find()
321 .reverse_join(cake::Entity)
322 .build(DbBackend::MySql)
323 .to_string(),
324 [
325 "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
326 "INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
327 ]
328 .join(" ")
329 );
330 }
331
332 #[test]
333 fn join_4() {
334 use crate::{Related, Select};
335
336 let find_fruit: Select<fruit::Entity> = cake::Entity::find_related();
337 assert_eq!(
338 find_fruit
339 .filter(cake::Column::Id.eq(11))
340 .build(DbBackend::MySql)
341 .to_string(),
342 [
343 "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
344 "INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
345 "WHERE `cake`.`id` = 11",
346 ]
347 .join(" ")
348 );
349
350 let find_fruit: Select<fruit::Entity> = cake::Entity::find_related_rev();
351 assert_eq!(
352 find_fruit
353 .filter(cake::Column::Id.eq(11))
354 .build(DbBackend::MySql)
355 .to_string(),
356 [
357 "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
358 "INNER JOIN `cake` ON `fruit`.`cake_id` = `cake`.`id`",
359 "WHERE `cake`.`id` = 11",
360 ]
361 .join(" ")
362 );
363 }
364
365 #[test]
366 fn join_5() {
367 let cake_model = cake::Model {
368 id: 12,
369 name: "".to_owned(),
370 };
371
372 assert_eq!(
373 cake_model
374 .find_related(fruit::Entity)
375 .build(DbBackend::MySql)
376 .to_string(),
377 [
378 "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
379 "INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`",
380 "WHERE `cake`.`id` = 12",
381 ]
382 .join(" ")
383 );
384 }
385
386 #[test]
387 fn join_6() {
388 assert_eq!(
389 cake::Entity::find()
390 .left_join(filling::Entity)
391 .build(DbBackend::MySql)
392 .to_string(),
393 [
394 "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
395 "LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id`",
396 "LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id`",
397 ]
398 .join(" ")
399 );
400 }
401
402 #[test]
403 fn join_7() {
404 use crate::{Related, Select};
405
406 let find_filling: Select<filling::Entity> = cake::Entity::find_related();
407 assert_eq!(
408 find_filling.build(DbBackend::MySql).to_string(),
409 [
410 "SELECT `filling`.`id`, `filling`.`name`, `filling`.`vendor_id` FROM `filling`",
411 "INNER JOIN `cake_filling` ON `cake_filling`.`filling_id` = `filling`.`id`",
412 "INNER JOIN `cake` ON `cake`.`id` = `cake_filling`.`cake_id`",
413 ]
414 .join(" ")
415 );
416
417 let find_filling: Select<filling::Entity> = cake::Entity::find_related_rev();
418 assert_eq!(
419 find_filling.build(DbBackend::MySql).to_string(),
420 [
421 "SELECT `filling`.`id`, `filling`.`name`, `filling`.`vendor_id` FROM `filling`",
422 "INNER JOIN `cake_filling` ON `filling`.`id` = `cake_filling`.`filling_id`",
423 "INNER JOIN `cake` ON `cake_filling`.`cake_id` = `cake`.`id`",
424 ]
425 .join(" ")
426 );
427 }
428
429 #[test]
430 fn join_8() {
431 use crate::{Related, Select};
432
433 let find_cake_filling_price: Select<cake_filling_price::Entity> =
434 cake_filling::Entity::find_related();
435 assert_eq!(
436 find_cake_filling_price.build(DbBackend::Postgres).to_string(),
437 [
438 r#"SELECT "cake_filling_price"."cake_id", "cake_filling_price"."filling_id", "cake_filling_price"."price""#,
439 r#"FROM "public"."cake_filling_price""#,
440 r#"INNER JOIN "cake_filling" ON"#,
441 r#""cake_filling"."cake_id" = "cake_filling_price"."cake_id" AND"#,
442 r#""cake_filling"."filling_id" = "cake_filling_price"."filling_id""#,
443 ]
444 .join(" ")
445 );
446 }
447
448 #[test]
449 fn join_9() {
450 use crate::{Related, Select};
451
452 let find_cake_filling: Select<cake_filling::Entity> =
453 cake_filling_price::Entity::find_related();
454 assert_eq!(
455 find_cake_filling.build(DbBackend::Postgres).to_string(),
456 [
457 r#"SELECT "cake_filling"."cake_id", "cake_filling"."filling_id""#,
458 r#"FROM "cake_filling""#,
459 r#"INNER JOIN "public"."cake_filling_price" ON"#,
460 r#""cake_filling_price"."cake_id" = "cake_filling"."cake_id" AND"#,
461 r#""cake_filling_price"."filling_id" = "cake_filling"."filling_id""#,
462 ]
463 .join(" ")
464 );
465 }
466
467 #[test]
468 fn join_10() {
469 let cake_model = cake::Model {
470 id: 12,
471 name: "".to_owned(),
472 };
473
474 assert_eq!(
475 cake_model
476 .find_linked(entity_linked::CakeToFilling)
477 .build(DbBackend::MySql)
478 .to_string(),
479 [
480 r"SELECT `filling`.`id`, `filling`.`name`, `filling`.`vendor_id`",
481 r"FROM `filling`",
482 r"INNER JOIN `cake_filling` AS `r0` ON `r0`.`filling_id` = `filling`.`id`",
483 r"INNER JOIN `cake` AS `r1` ON `r1`.`id` = `r0`.`cake_id`",
484 r"WHERE `r1`.`id` = 12",
485 ]
486 .join(" ")
487 );
488 }
489
490 #[test]
491 fn join_11() {
492 let cake_model = cake::Model {
493 id: 18,
494 name: "".to_owned(),
495 };
496
497 assert_eq!(
498 cake_model
499 .find_linked(entity_linked::CakeToFillingVendor)
500 .build(DbBackend::MySql)
501 .to_string(),
502 [
503 r"SELECT `vendor`.`id`, `vendor`.`name`",
504 r"FROM `vendor`",
505 r"INNER JOIN `filling` AS `r0` ON `r0`.`vendor_id` = `vendor`.`id`",
506 r"INNER JOIN `cake_filling` AS `r1` ON `r1`.`filling_id` = `r0`.`id`",
507 r"INNER JOIN `cake` AS `r2` ON `r2`.`id` = `r1`.`cake_id`",
508 r"WHERE `r2`.`id` = 18",
509 ]
510 .join(" ")
511 );
512 }
513
514 #[test]
515 fn join_12() {
516 assert_eq!(
517 cake::Entity::find()
518 .find_also_linked(entity_linked::CakeToFilling)
519 .build(DbBackend::MySql)
520 .to_string(),
521 [
522 r"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
523 r"`r1`.`id` AS `B_id`, `r1`.`name` AS `B_name`, `r1`.`vendor_id` AS `B_vendor_id`",
524 r"FROM `cake`",
525 r"LEFT JOIN `cake_filling` AS `r0` ON `cake`.`id` = `r0`.`cake_id`",
526 r"LEFT JOIN `filling` AS `r1` ON `r0`.`filling_id` = `r1`.`id`",
527 ]
528 .join(" ")
529 );
530 }
531
532 #[test]
533 fn join_13() {
534 assert_eq!(
535 cake::Entity::find()
536 .find_also_linked(entity_linked::CakeToFillingVendor)
537 .build(DbBackend::MySql)
538 .to_string(),
539 [
540 r"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
541 r"`r2`.`id` AS `B_id`, `r2`.`name` AS `B_name`",
542 r"FROM `cake`",
543 r"LEFT JOIN `cake_filling` AS `r0` ON `cake`.`id` = `r0`.`cake_id`",
544 r"LEFT JOIN `filling` AS `r1` ON `r0`.`filling_id` = `r1`.`id`",
545 r"LEFT JOIN `vendor` AS `r2` ON `r1`.`vendor_id` = `r2`.`id`",
546 ]
547 .join(" ")
548 );
549 }
550
551 #[test]
552 fn join_14() {
553 assert_eq!(
554 cake_compact::Entity::find()
555 .join(JoinType::LeftJoin, cake_compact::Relation::TropicalFruit.def())
556 .build(DbBackend::MySql)
557 .to_string(),
558 [
559 "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
560 "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'",
561 ]
562 .join(" ")
563 );
564 }
565
566 #[test]
567 fn join_15() {
568 let cake_model = cake::Model {
569 id: 18,
570 name: "".to_owned(),
571 };
572
573 assert_eq!(
574 cake_model
575 .find_linked(entity_linked::CheeseCakeToFillingVendor)
576 .build(DbBackend::MySql)
577 .to_string(),
578 [
579 r"SELECT `vendor`.`id`, `vendor`.`name`",
580 r"FROM `vendor`",
581 r"INNER JOIN `filling` AS `r0` ON `r0`.`vendor_id` = `vendor`.`id`",
582 r"INNER JOIN `cake_filling` AS `r1` ON `r1`.`filling_id` = `r0`.`id`",
583 r"INNER JOIN `cake` AS `r2` ON `r2`.`id` = `r1`.`cake_id` AND `r2`.`name` LIKE '%cheese%'",
584 r"WHERE `r2`.`id` = 18",
585 ]
586 .join(" ")
587 );
588 }
589
590 #[test]
591 fn join_16() {
592 }
594
595 #[test]
596 fn join_17() {
597 assert_eq!(
598 cake::Entity::find()
599 .find_also_linked(entity_linked::CheeseCakeToFillingVendor)
600 .build(DbBackend::MySql)
601 .to_string(),
602 [
603 r"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
604 r"`r2`.`id` AS `B_id`, `r2`.`name` AS `B_name`",
605 r"FROM `cake`",
606 r"LEFT JOIN `cake_filling` AS `r0` ON `cake`.`id` = `r0`.`cake_id` AND `cake`.`name` LIKE '%cheese%'",
607 r"LEFT JOIN `filling` AS `r1` ON `r0`.`filling_id` = `r1`.`id`",
608 r"LEFT JOIN `vendor` AS `r2` ON `r1`.`vendor_id` = `r2`.`id`",
609 ]
610 .join(" ")
611 );
612 }
613
614 #[test]
615 fn join_18() {
616 }
618
619 #[test]
620 fn join_19() {
621 assert_eq!(
622 cake_compact::Entity::find()
623 .join(JoinType::LeftJoin, cake_compact::Relation::TropicalFruit.def())
624 .join(
625 JoinType::LeftJoin,
626 cake_filling::Relation::Cake
627 .def()
628 .rev()
629 .on_condition(|_left, right| {
630 Expr::col((right, cake_filling::Column::CakeId))
631 .gt(10)
632 .into_condition()
633 })
634 )
635 .join(
636 JoinType::LeftJoin,
637 cake_filling::Relation::Filling
638 .def()
639 .on_condition(|_left, right| {
640 Expr::col((right, filling::Column::Name))
641 .like("%lemon%")
642 .into_condition()
643 })
644 )
645 .join(JoinType::LeftJoin, filling::Relation::Vendor.def())
646 .build(DbBackend::MySql)
647 .to_string(),
648 [
649 "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
650 "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'",
651 "LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id` AND `cake_filling`.`cake_id` > 10",
652 "LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id` AND `filling`.`name` LIKE '%lemon%'",
653 "LEFT JOIN `vendor` ON `filling`.`vendor_id` = `vendor`.`id`",
654 ]
655 .join(" ")
656 );
657 }
658
659 #[test]
660 fn join_20() {
661 assert_eq!(
662 cake::Entity::find()
663 .column_as(
664 Expr::col(("fruit_alias", fruit::Column::Name)),
665 "fruit_name"
666 )
667 .join_as(
668 JoinType::LeftJoin,
669 cake::Relation::Fruit
670 .def()
671 .on_condition(|_left, right| {
672 Expr::col((right, fruit::Column::Name))
673 .like("%tropical%")
674 .into_condition()
675 }),
676 "fruit_alias"
677 )
678 .build(DbBackend::MySql)
679 .to_string(),
680 [
681 "SELECT `cake`.`id`, `cake`.`name`, `fruit_alias`.`name` AS `fruit_name` FROM `cake`",
682 "LEFT JOIN `fruit` AS `fruit_alias` ON `cake`.`id` = `fruit_alias`.`cake_id` AND `fruit_alias`.`name` LIKE '%tropical%'",
683 ]
684 .join(" ")
685 );
686 }
687
688 #[test]
689 fn join_21() {
690 assert_eq!(
691 cake_compact::Entity::find()
692 .column_as(
693 Expr::col(("cake_filling_alias", cake_filling::Column::CakeId)),
694 "cake_filling_cake_id"
695 )
696 .join(JoinType::LeftJoin, cake_compact::Relation::TropicalFruit.def())
697 .join_as_rev(
698 JoinType::LeftJoin,
699 cake_filling::Relation::Cake
700 .def()
701 .on_condition(|left, _right| {
702 Expr::col((left, cake_filling::Column::CakeId))
703 .gt(10)
704 .into_condition()
705 }),
706 "cake_filling_alias"
707 )
708 .build(DbBackend::MySql)
709 .to_string(),
710 [
711 "SELECT `cake`.`id`, `cake`.`name`, `cake_filling_alias`.`cake_id` AS `cake_filling_cake_id` FROM `cake`",
712 "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` AND `fruit`.`name` LIKE '%tropical%'",
713 "LEFT JOIN `cake_filling` AS `cake_filling_alias` ON `cake_filling_alias`.`cake_id` = `cake`.`id` AND `cake_filling_alias`.`cake_id` > 10",
714 ]
715 .join(" ")
716 );
717 }
718
719 #[test]
720 fn join_22() {
721 assert_eq!(
722 cake_compact::Entity::find()
723 .column_as(
724 Expr::col(("cake_filling_alias", cake_filling::Column::CakeId)),
725 "cake_filling_cake_id"
726 )
727 .join(JoinType::LeftJoin, cake_compact::Relation::OrTropicalFruit.def())
728 .join_as_rev(
729 JoinType::LeftJoin,
730 cake_filling::Relation::Cake
731 .def()
732 .condition_type(ConditionType::Any)
733 .on_condition(|left, _right| {
734 Expr::col((left, cake_filling::Column::CakeId))
735 .gt(10)
736 .into_condition()
737 }),
738 "cake_filling_alias"
739 )
740 .build(DbBackend::MySql)
741 .to_string(),
742 [
743 "SELECT `cake`.`id`, `cake`.`name`, `cake_filling_alias`.`cake_id` AS `cake_filling_cake_id` FROM `cake`",
744 "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id` OR `fruit`.`name` LIKE '%tropical%'",
745 "LEFT JOIN `cake_filling` AS `cake_filling_alias` ON `cake_filling_alias`.`cake_id` = `cake`.`id` OR `cake_filling_alias`.`cake_id` > 10",
746 ]
747 .join(" ")
748 );
749 }
750
751 #[test]
752 fn join_23() {
753 let cake_model = cake::Model {
754 id: 18,
755 name: "".to_owned(),
756 };
757
758 assert_eq!(
759 cake_model
760 .find_linked(entity_linked::CakeToCakeViaFilling)
761 .build(DbBackend::MySql)
762 .to_string(),
763 [
764 "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
765 "INNER JOIN `cake_filling` AS `r0` ON `r0`.`cake_id` = `cake`.`id`",
766 "INNER JOIN `filling` AS `r1` ON `r1`.`id` = `r0`.`filling_id`",
767 "INNER JOIN `cake_filling` AS `r2` ON `r2`.`filling_id` = `r1`.`id`",
768 "INNER JOIN `cake` AS `r3` ON `r3`.`id` = `r2`.`cake_id`",
769 "WHERE `r3`.`id` = 18",
770 ]
771 .join(" ")
772 );
773 }
774
775 #[test]
776 fn join_24() {
777 let cake_model = cake::Model {
778 id: 12,
779 name: "".to_owned(),
780 };
781
782 assert_eq!(
783 cake_model
784 .find_linked_recursive(entity_linked::CakeToCakeViaFilling)
785 .build(DbBackend::MySql)
786 .to_string(),
787 [
788 "WITH `cte` AS (SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
789 "INNER JOIN `cake_filling` AS `r0` ON `r0`.`cake_id` = `cake`.`id`",
790 "INNER JOIN `filling` AS `r1` ON `r1`.`id` = `r0`.`filling_id`",
791 "INNER JOIN `cake_filling` AS `r2` ON `r2`.`filling_id` = `r1`.`id`",
792 "INNER JOIN `cake` AS `r3` ON `r3`.`id` = `r2`.`cake_id`",
793 "WHERE `r3`.`id` = 12",
794 "UNION ALL (SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
795 "INNER JOIN `cake_filling` AS `r0` ON `r0`.`cake_id` = `cake`.`id`",
796 "INNER JOIN `filling` AS `r1` ON `r1`.`id` = `r0`.`filling_id`",
797 "INNER JOIN `cake_filling` AS `r2` ON `r2`.`filling_id` = `r1`.`id`",
798 "INNER JOIN `cte` AS `r3` ON `r3`.`id` = `r2`.`cake_id`))",
799 "SELECT `cake`.`id`, `cake`.`name` FROM `cte` AS `cake`",
800 ]
801 .join(" ")
802 );
803 }
804
805 #[test]
806 fn join_25() {
807 assert_eq!(
808 cake::Entity::find()
809 .find_with_linked_recursive(entity_linked::CakeToCakeViaFilling)
810 .build(DbBackend::MySql)
811 .to_string(),
812 [
813 "WITH `cte` AS (SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
814 "UNION ALL (SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
815 "INNER JOIN `cake_filling` AS `r0` ON `r0`.`cake_id` = `cake`.`id`",
816 "INNER JOIN `filling` AS `r1` ON `r1`.`id` = `r0`.`filling_id`",
817 "INNER JOIN `cake_filling` AS `r2` ON `r2`.`filling_id` = `r1`.`id`",
818 "INNER JOIN `cte` AS `r3` ON `r3`.`id` = `r2`.`cake_id`))",
819 "SELECT `cake`.`id`, `cake`.`name` FROM `cte` AS `cake`",
820 ]
821 .join(" ")
822 );
823 }
824
825 #[test]
826 fn chained_left_join_linked_uses_previous_hop_alias() {
827 assert_eq!(
828 cake::Entity::find()
829 .left_join_linked(entity_linked::CakeToFilling)
830 .left_join_linked(entity_linked::CakeToFilling)
831 .select_only()
832 .column(cake::Column::Id)
833 .build(DbBackend::MySql)
834 .to_string(),
835 [
836 r"SELECT `cake`.`id` FROM `cake`",
837 r"LEFT JOIN `cake_filling` AS `r0` ON `cake`.`id` = `r0`.`cake_id`",
838 r"LEFT JOIN `filling` AS `r1` ON `r0`.`filling_id` = `r1`.`id`",
839 r"LEFT JOIN `cake_filling` AS `r2` ON `cake`.`id` = `r2`.`cake_id`",
840 r"LEFT JOIN `filling` AS `r3` ON `r2`.`filling_id` = `r3`.`id`",
841 ]
842 .join(" ")
843 );
844 }
845
846 #[derive(Debug)]
847 struct CakeToOrTropicalFruit;
848
849 impl crate::Linked for CakeToOrTropicalFruit {
850 type FromEntity = cake_compact::Entity;
851
852 type ToEntity = fruit::Entity;
853
854 fn link(&self) -> Vec<crate::LinkDef> {
855 vec![
856 cake_compact::Relation::Fruit
857 .def()
858 .condition_type(ConditionType::Any)
859 .on_condition(|_left, right| {
860 Expr::col((right, fruit::Column::Name))
861 .like("%tropical%")
862 .into_condition()
863 }),
864 ]
865 }
866 }
867
868 #[test]
869 fn left_join_linked_respects_condition_type() {
870 assert_eq!(
871 cake_compact::Entity::find()
872 .left_join_linked(CakeToOrTropicalFruit)
873 .select_only()
874 .column(cake_compact::Column::Id)
875 .build(DbBackend::MySql)
876 .to_string(),
877 [
878 r"SELECT `cake`.`id` FROM `cake`",
879 r"LEFT JOIN `fruit` AS `r0` ON `cake`.`id` = `r0`.`cake_id` OR `r0`.`name` LIKE '%tropical%'",
880 ]
881 .join(" ")
882 );
883 }
884
885 #[test]
886 fn find_linked_respects_condition_type() {
887 let cake_model = cake_compact::Model {
888 id: 18,
889 name: "".to_owned(),
890 };
891
892 assert_eq!(
893 cake_model
894 .find_linked(CakeToOrTropicalFruit)
895 .build(DbBackend::MySql)
896 .to_string(),
897 [
898 r"SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`",
899 r"INNER JOIN `cake` AS `r0` ON `r0`.`id` = `fruit`.`cake_id` OR `fruit`.`name` LIKE '%tropical%'",
900 r"WHERE `r0`.`id` = 18",
901 ]
902 .join(" ")
903 );
904 }
905}