1use crate::{
2 ConnectionTrait, DbBackend, EntityTrait, FromQueryResult, IdenStatic, Iterable, ModelTrait,
3 PartialModelTrait, PrimaryKeyArity, PrimaryKeyToColumn, PrimaryKeyTrait, QueryResult,
4 QuerySelect, Select, SelectA, SelectB, SelectC, SelectThree, SelectTwo, SelectTwoMany,
5 Statement, StreamTrait, TryGetableMany, error::*,
6};
7use futures_util::{Stream, TryStreamExt};
8use sea_query::{SelectStatement, Value};
9use std::collections::HashMap;
10use std::{hash::Hash, marker::PhantomData, pin::Pin};
11
12#[cfg(feature = "with-json")]
13use crate::JsonValue;
14
15#[derive(Clone, Debug)]
17pub struct Selector<S>
18where
19 S: SelectorTrait,
20{
21 pub(crate) query: SelectStatement,
22 selector: PhantomData<S>,
23}
24
25#[derive(Clone, Debug)]
27pub struct SelectorRaw<S>
28where
29 S: SelectorTrait,
30{
31 pub(crate) stmt: Statement,
32 pub(super) selector: PhantomData<S>,
33}
34
35pub trait SelectorTrait {
37 #[allow(missing_docs)]
38 type Item: Sized;
39
40 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr>;
42}
43
44#[derive(Debug)]
46pub struct SelectGetableValue<T, C>
47where
48 T: TryGetableMany,
49 C: strum::IntoEnumIterator + sea_query::Iden,
50{
51 columns: PhantomData<C>,
52 model: PhantomData<T>,
53}
54
55#[derive(Debug)]
57pub struct SelectGetableTuple<T>
58where
59 T: TryGetableMany,
60{
61 model: PhantomData<T>,
62}
63
64#[derive(Debug)]
66pub struct SelectModel<M>
67where
68 M: FromQueryResult,
69{
70 model: PhantomData<M>,
71}
72
73#[derive(Clone, Debug)]
75pub struct SelectTwoModel<M, N>
76where
77 M: FromQueryResult,
78 N: FromQueryResult,
79{
80 model: PhantomData<(M, N)>,
81}
82
83#[derive(Clone, Debug)]
85pub struct SelectThreeModel<M, N, O>
86where
87 M: FromQueryResult,
88 N: FromQueryResult,
89 O: FromQueryResult,
90{
91 model: PhantomData<(M, N, O)>,
92}
93
94impl<T, C> Default for SelectGetableValue<T, C>
95where
96 T: TryGetableMany,
97 C: strum::IntoEnumIterator + sea_query::Iden,
98{
99 fn default() -> Self {
100 Self {
101 columns: PhantomData,
102 model: PhantomData,
103 }
104 }
105}
106
107impl<T, C> SelectorTrait for SelectGetableValue<T, C>
108where
109 T: TryGetableMany,
110 C: strum::IntoEnumIterator + sea_query::Iden,
111{
112 type Item = T;
113
114 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
115 let cols: Vec<String> = C::iter().map(|col| col.to_string()).collect();
116 T::try_get_many(&res, "", &cols).map_err(Into::into)
117 }
118}
119
120impl<T> SelectorTrait for SelectGetableTuple<T>
121where
122 T: TryGetableMany,
123{
124 type Item = T;
125
126 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
127 T::try_get_many_by_index(&res).map_err(Into::into)
128 }
129}
130
131impl<M> SelectorTrait for SelectModel<M>
132where
133 M: FromQueryResult + Sized,
134{
135 type Item = M;
136
137 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
138 M::from_query_result(&res, "")
139 }
140}
141
142impl<M, N> SelectorTrait for SelectTwoModel<M, N>
143where
144 M: FromQueryResult + Sized,
145 N: FromQueryResult + Sized,
146{
147 type Item = (M, Option<N>);
148
149 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
150 Ok((
151 M::from_query_result(&res, SelectA.as_str())?,
152 N::from_query_result_optional(&res, SelectB.as_str())?,
153 ))
154 }
155}
156
157impl<M, N, O> SelectorTrait for SelectThreeModel<M, N, O>
158where
159 M: FromQueryResult + Sized,
160 N: FromQueryResult + Sized,
161 O: FromQueryResult + Sized,
162{
163 type Item = (M, Option<N>, Option<O>);
164
165 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
166 Ok((
167 M::from_query_result(&res, SelectA.as_str())?,
168 N::from_query_result_optional(&res, SelectB.as_str())?,
169 O::from_query_result_optional(&res, SelectC.as_str())?,
170 ))
171 }
172}
173
174impl<E> Select<E>
175where
176 E: EntityTrait,
177{
178 #[allow(clippy::wrong_self_convention)]
180 pub fn from_raw_sql(self, stmt: Statement) -> SelectorRaw<SelectModel<E::Model>> {
181 SelectorRaw {
182 stmt,
183 selector: PhantomData,
184 }
185 }
186
187 pub fn into_model<M>(self) -> Selector<SelectModel<M>>
189 where
190 M: FromQueryResult,
191 {
192 Selector {
193 query: self.query,
194 selector: PhantomData,
195 }
196 }
197
198 pub fn into_partial_model<M>(self) -> Selector<SelectModel<M>>
231 where
232 M: PartialModelTrait,
233 {
234 M::select_cols(QuerySelect::select_only(self)).into_model::<M>()
235 }
236
237 #[cfg(feature = "with-json")]
239 pub fn into_json(self) -> Selector<SelectModel<JsonValue>> {
240 Selector {
241 query: self.query,
242 selector: PhantomData,
243 }
244 }
245
246 pub fn into_values<T, C>(self) -> Selector<SelectGetableValue<T, C>>
349 where
350 T: TryGetableMany,
351 C: strum::IntoEnumIterator + sea_query::Iden,
352 {
353 Selector {
354 query: self.query,
355 selector: PhantomData,
356 }
357 }
358
359 pub fn into_tuple<T>(self) -> Selector<SelectGetableTuple<T>>
451 where
452 T: TryGetableMany,
453 {
454 Selector {
455 query: self.query,
456 selector: PhantomData,
457 }
458 }
459
460 pub async fn one<C>(self, db: &C) -> Result<Option<E::Model>, DbErr>
462 where
463 C: ConnectionTrait,
464 {
465 self.into_model().one(db).await
466 }
467
468 pub async fn all<C>(self, db: &C) -> Result<Vec<E::Model>, DbErr>
470 where
471 C: ConnectionTrait,
472 {
473 self.into_model().all(db).await
474 }
475
476 pub async fn stream<'a: 'b, 'b, C>(
478 self,
479 db: &'a C,
480 ) -> Result<impl Stream<Item = Result<E::Model, DbErr>> + 'b + Send, DbErr>
481 where
482 C: ConnectionTrait + StreamTrait + Send,
483 {
484 self.into_model().stream(db).await
485 }
486
487 pub async fn stream_partial_model<'a: 'b, 'b, C, M>(
489 self,
490 db: &'a C,
491 ) -> Result<impl Stream<Item = Result<M, DbErr>> + 'b + Send, DbErr>
492 where
493 C: ConnectionTrait + StreamTrait + Send,
494 M: PartialModelTrait + Send + 'b,
495 {
496 self.into_partial_model().stream(db).await
497 }
498}
499
500impl<E, F> SelectTwo<E, F>
501where
502 E: EntityTrait,
503 F: EntityTrait,
504{
505 pub fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
507 where
508 M: FromQueryResult,
509 N: FromQueryResult,
510 {
511 Selector {
512 query: self.query,
513 selector: PhantomData,
514 }
515 }
516
517 pub fn into_partial_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
519 where
520 M: PartialModelTrait,
521 N: PartialModelTrait,
522 {
523 let select = QuerySelect::select_only(self);
524 let select = M::select_cols(select);
525 let select = N::select_cols(select);
526 select.into_model::<M, N>()
527 }
528
529 #[cfg(feature = "with-json")]
531 pub fn into_json(self) -> Selector<SelectTwoModel<JsonValue, JsonValue>> {
532 Selector {
533 query: self.query,
534 selector: PhantomData,
535 }
536 }
537
538 pub async fn one<C>(self, db: &C) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr>
540 where
541 C: ConnectionTrait,
542 {
543 self.into_model().one(db).await
544 }
545
546 pub async fn all<C>(self, db: &C) -> Result<Vec<(E::Model, Option<F::Model>)>, DbErr>
548 where
549 C: ConnectionTrait,
550 {
551 self.into_model().all(db).await
552 }
553
554 pub async fn stream<'a: 'b, 'b, C>(
556 self,
557 db: &'a C,
558 ) -> Result<impl Stream<Item = Result<(E::Model, Option<F::Model>), DbErr>> + 'b, DbErr>
559 where
560 C: ConnectionTrait + StreamTrait + Send,
561 {
562 self.into_model().stream(db).await
563 }
564
565 pub async fn stream_partial_model<'a: 'b, 'b, C, M, N>(
567 self,
568 db: &'a C,
569 ) -> Result<impl Stream<Item = Result<(M, Option<N>), DbErr>> + 'b + Send, DbErr>
570 where
571 C: ConnectionTrait + StreamTrait + Send,
572 M: PartialModelTrait + Send + 'b,
573 N: PartialModelTrait + Send + 'b,
574 {
575 self.into_partial_model().stream(db).await
576 }
577}
578
579impl<E, F> SelectTwoMany<E, F>
580where
581 E: EntityTrait,
582 F: EntityTrait,
583{
584 fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
586 where
587 M: FromQueryResult,
588 N: FromQueryResult,
589 {
590 Selector {
591 query: self.query,
592 selector: PhantomData,
593 }
594 }
595
596 fn into_partial_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
598 where
599 M: PartialModelTrait,
600 N: PartialModelTrait,
601 {
602 let select = self.select_only();
603 let select = M::select_cols(select);
604 let select = N::select_cols(select);
605 select.into_model()
606 }
607
608 #[cfg(feature = "with-json")]
610 pub fn into_json(self) -> Selector<SelectTwoModel<JsonValue, JsonValue>> {
611 Selector {
612 query: self.query,
613 selector: PhantomData,
614 }
615 }
616
617 pub async fn stream<'a: 'b, 'b, C>(
619 self,
620 db: &'a C,
621 ) -> Result<impl Stream<Item = Result<(E::Model, Option<F::Model>), DbErr>> + 'b + Send, DbErr>
622 where
623 C: ConnectionTrait + StreamTrait + Send,
624 {
625 self.into_model().stream(db).await
626 }
627
628 pub async fn stream_partial_model<'a: 'b, 'b, C, M, N>(
630 self,
631 db: &'a C,
632 ) -> Result<impl Stream<Item = Result<(M, Option<N>), DbErr>> + 'b + Send, DbErr>
633 where
634 C: ConnectionTrait + StreamTrait + Send,
635 M: PartialModelTrait + Send + 'b,
636 N: PartialModelTrait + Send + 'b,
637 {
638 self.into_partial_model().stream(db).await
639 }
640
641 pub async fn all<C>(self, db: &C) -> Result<Vec<(E::Model, Vec<F::Model>)>, DbErr>
650 where
651 C: ConnectionTrait,
652 {
653 let rows = self.into_model().all(db).await?;
654 Ok(consolidate_query_result::<E, F>(rows))
655 }
656
657 }
666
667impl<E, F, G> SelectThree<E, F, G>
668where
669 E: EntityTrait,
670 F: EntityTrait,
671 G: EntityTrait,
672{
673 pub fn into_model<M, N, O>(self) -> Selector<SelectThreeModel<M, N, O>>
675 where
676 M: FromQueryResult,
677 N: FromQueryResult,
678 O: FromQueryResult,
679 {
680 Selector {
681 query: self.query,
682 selector: PhantomData,
683 }
684 }
685
686 pub fn into_partial_model<M, N, O>(self) -> Selector<SelectThreeModel<M, N, O>>
688 where
689 M: PartialModelTrait,
690 N: PartialModelTrait,
691 O: PartialModelTrait,
692 {
693 let select = QuerySelect::select_only(self);
694 let select = M::select_cols(select);
695 let select = N::select_cols(select);
696 select.into_model::<M, N, O>()
697 }
698
699 #[cfg(feature = "with-json")]
701 pub fn into_json(self) -> Selector<SelectThreeModel<JsonValue, JsonValue, JsonValue>> {
702 Selector {
703 query: self.query,
704 selector: PhantomData,
705 }
706 }
707
708 pub async fn one<C>(
710 self,
711 db: &C,
712 ) -> Result<Option<(E::Model, Option<F::Model>, Option<G::Model>)>, DbErr>
713 where
714 C: ConnectionTrait,
715 {
716 self.into_model().one(db).await
717 }
718
719 pub async fn all<C>(
721 self,
722 db: &C,
723 ) -> Result<Vec<(E::Model, Option<F::Model>, Option<G::Model>)>, DbErr>
724 where
725 C: ConnectionTrait,
726 {
727 self.into_model().all(db).await
728 }
729
730 pub async fn stream<'a: 'b, 'b, C>(
732 self,
733 db: &'a C,
734 ) -> Result<
735 impl Stream<Item = Result<(E::Model, Option<F::Model>, Option<G::Model>), DbErr>> + 'b,
736 DbErr,
737 >
738 where
739 C: ConnectionTrait + StreamTrait + Send,
740 {
741 self.into_model().stream(db).await
742 }
743
744 pub async fn stream_partial_model<'a: 'b, 'b, C, M, N, O>(
746 self,
747 db: &'a C,
748 ) -> Result<impl Stream<Item = Result<(M, Option<N>, Option<O>), DbErr>> + 'b + Send, DbErr>
749 where
750 C: ConnectionTrait + StreamTrait + Send,
751 M: PartialModelTrait + Send + 'b,
752 N: PartialModelTrait + Send + 'b,
753 O: PartialModelTrait + Send + 'b,
754 {
755 self.into_partial_model().stream(db).await
756 }
757}
758
759impl<S> Selector<S>
760where
761 S: SelectorTrait,
762{
763 pub fn into_statement(self, builder: DbBackend) -> Statement {
765 builder.build(&self.query)
766 }
767
768 pub async fn one<C>(mut self, db: &C) -> Result<Option<S::Item>, DbErr>
770 where
771 C: ConnectionTrait,
772 {
773 self.query.limit(1);
774 let row = db.query_one(&self.query).await?;
775 match row {
776 Some(row) => Ok(Some(S::from_raw_query_result(row)?)),
777 None => Ok(None),
778 }
779 }
780
781 pub async fn all<C>(self, db: &C) -> Result<Vec<S::Item>, DbErr>
783 where
784 C: ConnectionTrait,
785 {
786 let rows = db.query_all(&self.query).await?;
787 let mut models = Vec::new();
788 for row in rows.into_iter() {
789 models.push(S::from_raw_query_result(row)?);
790 }
791 Ok(models)
792 }
793
794 pub async fn stream<'a: 'b, 'b, C>(
796 self,
797 db: &'a C,
798 ) -> Result<Pin<Box<dyn Stream<Item = Result<S::Item, DbErr>> + 'b + Send>>, DbErr>
799 where
800 C: ConnectionTrait + StreamTrait + Send,
801 S: 'b,
802 S::Item: Send,
803 {
804 let stream = db.stream(&self.query).await?;
805 Ok(Box::pin(stream.and_then(|row| {
806 futures_util::future::ready(S::from_raw_query_result(row))
807 })))
808 }
809}
810
811impl<S> SelectorRaw<S>
812where
813 S: SelectorTrait,
814{
815 pub fn from_statement<M>(stmt: Statement) -> SelectorRaw<SelectModel<M>>
817 where
818 M: FromQueryResult,
819 {
820 SelectorRaw {
821 stmt,
822 selector: PhantomData,
823 }
824 }
825
826 pub fn into_model<M>(self) -> SelectorRaw<SelectModel<M>>
891 where
892 M: FromQueryResult,
893 {
894 SelectorRaw {
895 stmt: self.stmt,
896 selector: PhantomData,
897 }
898 }
899
900 #[cfg(feature = "with-json")]
957 pub fn into_json(self) -> SelectorRaw<SelectModel<JsonValue>> {
958 SelectorRaw {
959 stmt: self.stmt,
960 selector: PhantomData,
961 }
962 }
963
964 pub fn into_statement(self) -> Statement {
966 self.stmt
967 }
968
969 pub async fn one<C>(self, db: &C) -> Result<Option<S::Item>, DbErr>
1010 where
1011 C: ConnectionTrait,
1012 {
1013 let row = db.query_one_raw(self.stmt).await?;
1014 match row {
1015 Some(row) => Ok(Some(S::from_raw_query_result(row)?)),
1016 None => Ok(None),
1017 }
1018 }
1019
1020 pub async fn all<C>(self, db: &C) -> Result<Vec<S::Item>, DbErr>
1061 where
1062 C: ConnectionTrait,
1063 {
1064 let rows = db.query_all_raw(self.stmt).await?;
1065 let mut models = Vec::new();
1066 for row in rows.into_iter() {
1067 models.push(S::from_raw_query_result(row)?);
1068 }
1069 Ok(models)
1070 }
1071
1072 pub async fn stream<'a: 'b, 'b, C>(
1074 self,
1075 db: &'a C,
1076 ) -> Result<Pin<Box<dyn Stream<Item = Result<S::Item, DbErr>> + 'b + Send>>, DbErr>
1077 where
1078 C: ConnectionTrait + StreamTrait + Send,
1079 S: 'b,
1080 S::Item: Send,
1081 {
1082 let stream = db.stream_raw(self.stmt).await?;
1083 Ok(Box::pin(stream.and_then(|row| {
1084 futures_util::future::ready(S::from_raw_query_result(row))
1085 })))
1086 }
1087}
1088
1089#[allow(clippy::unwrap_used)]
1090fn consolidate_query_result<L, R>(
1091 rows: Vec<(L::Model, Option<R::Model>)>,
1092) -> Vec<(L::Model, Vec<R::Model>)>
1093where
1094 L: EntityTrait,
1095 R: EntityTrait,
1096{
1097 match <<L::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY {
1098 1 => {
1099 let col = <L::PrimaryKey as Iterable>::iter()
1100 .next()
1101 .unwrap()
1102 .into_column();
1103 consolidate_query_result_of::<L, R, UnitPk<L>>(rows, UnitPk(col))
1104 }
1105 2 => {
1106 let mut iter = <L::PrimaryKey as Iterable>::iter();
1107 let col1 = iter.next().unwrap().into_column();
1108 let col2 = iter.next().unwrap().into_column();
1109 consolidate_query_result_of::<L, R, PairPk<L>>(rows, PairPk(col1, col2))
1110 }
1111 _ => {
1112 let cols: Vec<_> = <L::PrimaryKey as Iterable>::iter()
1113 .map(|pk| pk.into_column())
1114 .collect();
1115 consolidate_query_result_of::<L, R, TuplePk<L>>(rows, TuplePk(cols))
1116 }
1117 }
1118}
1119
1120trait ModelKey<E: EntityTrait> {
1121 type Type: Hash + PartialEq + Eq;
1122 fn get(&self, model: &E::Model) -> Self::Type;
1123}
1124
1125struct UnitPk<E: EntityTrait>(E::Column);
1127struct PairPk<E: EntityTrait>(E::Column, E::Column);
1128struct TuplePk<E: EntityTrait>(Vec<E::Column>);
1129
1130impl<E: EntityTrait> ModelKey<E> for UnitPk<E> {
1131 type Type = Value;
1132 fn get(&self, model: &E::Model) -> Self::Type {
1133 model.get(self.0)
1134 }
1135}
1136
1137impl<E: EntityTrait> ModelKey<E> for PairPk<E> {
1138 type Type = (Value, Value);
1139 fn get(&self, model: &E::Model) -> Self::Type {
1140 (model.get(self.0), model.get(self.1))
1141 }
1142}
1143
1144impl<E: EntityTrait> ModelKey<E> for TuplePk<E> {
1145 type Type = Vec<Value>;
1146 fn get(&self, model: &E::Model) -> Self::Type {
1147 let mut key = Vec::new();
1148 for col in self.0.iter() {
1149 key.push(model.get(*col));
1150 }
1151 key
1152 }
1153}
1154
1155fn consolidate_query_result_of<L, R, KEY: ModelKey<L>>(
1156 mut rows: Vec<(L::Model, Option<R::Model>)>,
1157 model_key: KEY,
1158) -> Vec<(L::Model, Vec<R::Model>)>
1159where
1160 L: EntityTrait,
1161 R: EntityTrait,
1162{
1163 let mut hashmap: HashMap<KEY::Type, Vec<R::Model>> =
1164 rows.iter_mut().fold(HashMap::new(), |mut acc, row| {
1165 let key = model_key.get(&row.0);
1166 if let Some(value) = row.1.take() {
1167 let vec: Option<&mut Vec<R::Model>> = acc.get_mut(&key);
1168 if let Some(vec) = vec {
1169 vec.push(value)
1170 } else {
1171 acc.insert(key, vec![value]);
1172 }
1173 } else {
1174 acc.entry(key).or_default();
1175 }
1176
1177 acc
1178 });
1179
1180 rows.into_iter()
1181 .filter_map(|(l_model, _)| {
1182 let l_pk = model_key.get(&l_model);
1183 let r_models = hashmap.remove(&l_pk);
1184 r_models.map(|r_models| (l_model, r_models))
1185 })
1186 .collect()
1187}
1188
1189#[allow(dead_code)]
1191fn consolidate_query_result_of_ordered_rows<L, R>(
1192 rows: Vec<(L::Model, Option<R::Model>)>,
1193) -> Vec<(L::Model, Vec<R::Model>)>
1194where
1195 L: EntityTrait,
1196 R: EntityTrait,
1197{
1198 let mut acc: Vec<(L::Model, Vec<R::Model>)> = Vec::new();
1199 for (l, r) in rows {
1200 if let Some((last_l, last_r)) = acc.last_mut() {
1201 let mut same_l = true;
1202 for pk_col in <L::PrimaryKey as Iterable>::iter() {
1203 let col = pk_col.into_column();
1204 let val = l.get(col);
1205 let last_val = last_l.get(col);
1206 if !val.eq(&last_val) {
1207 same_l = false;
1208 break;
1209 }
1210 }
1211 if same_l {
1212 if let Some(r) = r {
1213 last_r.push(r);
1214 continue;
1215 }
1216 }
1217 }
1218 let rows = match r {
1219 Some(r) => vec![r],
1220 None => vec![],
1221 };
1222 acc.push((l, rows));
1223 }
1224 acc
1225}
1226
1227#[cfg(test)]
1228mod tests {
1229 use pretty_assertions::assert_eq;
1230
1231 fn cake_fruit_model(
1232 cake_id: i32,
1233 fruit_id: i32,
1234 ) -> (
1235 sea_orm::tests_cfg::cake::Model,
1236 sea_orm::tests_cfg::fruit::Model,
1237 ) {
1238 (cake_model(cake_id), fruit_model(fruit_id, Some(cake_id)))
1239 }
1240
1241 fn cake_model(id: i32) -> sea_orm::tests_cfg::cake::Model {
1242 let name = match id {
1243 1 => "apple cake",
1244 2 => "orange cake",
1245 3 => "fruit cake",
1246 4 => "chocolate cake",
1247 _ => "",
1248 }
1249 .to_string();
1250 sea_orm::tests_cfg::cake::Model { id, name }
1251 }
1252
1253 fn filling_model(id: i32) -> sea_orm::tests_cfg::filling::Model {
1254 let name = match id {
1255 1 => "apple juice",
1256 2 => "orange jam",
1257 3 => "fruit",
1258 4 => "chocolate crust",
1259 _ => "",
1260 }
1261 .to_string();
1262 sea_orm::tests_cfg::filling::Model {
1263 id,
1264 name,
1265 vendor_id: Some(1),
1266 ignored_attr: 0,
1267 }
1268 }
1269
1270 fn cake_filling_models(
1271 cake_id: i32,
1272 filling_id: i32,
1273 ) -> (
1274 sea_orm::tests_cfg::cake::Model,
1275 sea_orm::tests_cfg::filling::Model,
1276 ) {
1277 (cake_model(cake_id), filling_model(filling_id))
1278 }
1279
1280 fn fruit_model(id: i32, cake_id: Option<i32>) -> sea_orm::tests_cfg::fruit::Model {
1281 let name = match id {
1282 1 => "apple",
1283 2 => "orange",
1284 3 => "grape",
1285 4 => "strawberry",
1286 _ => "",
1287 }
1288 .to_string();
1289 sea_orm::tests_cfg::fruit::Model { id, name, cake_id }
1290 }
1291
1292 fn cake_vendor_link(
1293 cake_id: i32,
1294 vendor_id: i32,
1295 ) -> (
1296 sea_orm::tests_cfg::cake::Model,
1297 sea_orm::tests_cfg::vendor::Model,
1298 ) {
1299 (cake_model(cake_id), vendor_model(vendor_id))
1300 }
1301
1302 fn vendor_model(id: i32) -> sea_orm::tests_cfg::vendor::Model {
1303 let name = match id {
1304 1 => "Apollo",
1305 2 => "Benny",
1306 3 => "Christine",
1307 4 => "David",
1308 _ => "",
1309 }
1310 .to_string();
1311 sea_orm::tests_cfg::vendor::Model { id, name }
1312 }
1313
1314 #[smol_potat::test]
1315 pub async fn also_related() -> Result<(), sea_orm::DbErr> {
1316 use sea_orm::tests_cfg::*;
1317 use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
1318
1319 let db = MockDatabase::new(DbBackend::Postgres)
1320 .append_query_results([[cake_fruit_model(1, 1)]])
1321 .into_connection();
1322
1323 assert_eq!(
1324 Cake::find().find_also_related(Fruit).all(&db).await?,
1325 [(cake_model(1), Some(fruit_model(1, Some(1))))]
1326 );
1327
1328 assert_eq!(
1329 db.into_transaction_log(),
1330 [Transaction::many([Statement::from_sql_and_values(
1331 DbBackend::Postgres,
1332 [
1333 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1334 r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
1335 r#"FROM "cake""#,
1336 r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
1337 ]
1338 .join(" ")
1339 .as_str(),
1340 []
1341 ),])]
1342 );
1343
1344 Ok(())
1345 }
1346
1347 #[smol_potat::test]
1348 pub async fn also_related_2() -> Result<(), sea_orm::DbErr> {
1349 use sea_orm::tests_cfg::*;
1350 use sea_orm::{DbBackend, EntityTrait, MockDatabase};
1351
1352 let db = MockDatabase::new(DbBackend::Postgres)
1353 .append_query_results([[cake_fruit_model(1, 1), cake_fruit_model(1, 2)]])
1354 .into_connection();
1355
1356 assert_eq!(
1357 Cake::find().find_also_related(Fruit).all(&db).await?,
1358 [
1359 (cake_model(1), Some(fruit_model(1, Some(1)))),
1360 (cake_model(1), Some(fruit_model(2, Some(1))))
1361 ]
1362 );
1363
1364 Ok(())
1365 }
1366
1367 #[smol_potat::test]
1368 pub async fn also_related_3() -> Result<(), sea_orm::DbErr> {
1369 use sea_orm::tests_cfg::*;
1370 use sea_orm::{DbBackend, EntityTrait, MockDatabase};
1371
1372 let db = MockDatabase::new(DbBackend::Postgres)
1373 .append_query_results([[
1374 cake_fruit_model(1, 1),
1375 cake_fruit_model(1, 2),
1376 cake_fruit_model(2, 3),
1377 ]])
1378 .into_connection();
1379
1380 assert_eq!(
1381 Cake::find().find_also_related(Fruit).all(&db).await?,
1382 [
1383 (cake_model(1), Some(fruit_model(1, Some(1)))),
1384 (cake_model(1), Some(fruit_model(2, Some(1)))),
1385 (cake_model(2), Some(fruit_model(3, Some(2))))
1386 ]
1387 );
1388
1389 Ok(())
1390 }
1391
1392 #[smol_potat::test]
1393 pub async fn also_related_4() -> Result<(), sea_orm::DbErr> {
1394 use sea_orm::tests_cfg::*;
1395 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1396
1397 let db = MockDatabase::new(DbBackend::Postgres)
1398 .append_query_results([[
1399 cake_fruit_model(1, 1).into_mock_row(),
1400 cake_fruit_model(1, 2).into_mock_row(),
1401 cake_fruit_model(2, 3).into_mock_row(),
1402 (cake_model(3), None::<fruit::Model>).into_mock_row(),
1403 ]])
1404 .into_connection();
1405
1406 assert_eq!(
1407 Cake::find().find_also_related(Fruit).all(&db).await?,
1408 [
1409 (cake_model(1), Some(fruit_model(1, Some(1)))),
1410 (cake_model(1), Some(fruit_model(2, Some(1)))),
1411 (cake_model(2), Some(fruit_model(3, Some(2)))),
1412 (cake_model(3), None)
1413 ]
1414 );
1415
1416 Ok(())
1417 }
1418
1419 #[smol_potat::test]
1420 pub async fn also_related_many_to_many() -> Result<(), sea_orm::DbErr> {
1421 use sea_orm::tests_cfg::*;
1422 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1423
1424 let db = MockDatabase::new(DbBackend::Postgres)
1425 .append_query_results([[
1426 cake_filling_models(1, 1).into_mock_row(),
1427 cake_filling_models(1, 2).into_mock_row(),
1428 cake_filling_models(2, 2).into_mock_row(),
1429 ]])
1430 .into_connection();
1431
1432 assert_eq!(
1433 Cake::find().find_also_related(Filling).all(&db).await?,
1434 [
1435 (cake_model(1), Some(filling_model(1))),
1436 (cake_model(1), Some(filling_model(2))),
1437 (cake_model(2), Some(filling_model(2))),
1438 ]
1439 );
1440
1441 Ok(())
1442 }
1443
1444 #[smol_potat::test]
1445 pub async fn also_related_many_to_many_2() -> Result<(), sea_orm::DbErr> {
1446 use sea_orm::tests_cfg::*;
1447 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1448
1449 let db = MockDatabase::new(DbBackend::Postgres)
1450 .append_query_results([[
1451 cake_filling_models(1, 1).into_mock_row(),
1452 cake_filling_models(1, 2).into_mock_row(),
1453 cake_filling_models(2, 2).into_mock_row(),
1454 (cake_model(3), None::<filling::Model>).into_mock_row(),
1455 ]])
1456 .into_connection();
1457
1458 assert_eq!(
1459 Cake::find().find_also_related(Filling).all(&db).await?,
1460 [
1461 (cake_model(1), Some(filling_model(1))),
1462 (cake_model(1), Some(filling_model(2))),
1463 (cake_model(2), Some(filling_model(2))),
1464 (cake_model(3), None)
1465 ]
1466 );
1467
1468 Ok(())
1469 }
1470
1471 #[smol_potat::test]
1472 pub async fn with_related() -> Result<(), sea_orm::DbErr> {
1473 use sea_orm::tests_cfg::*;
1474 use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
1475
1476 let db = MockDatabase::new(DbBackend::Postgres)
1477 .append_query_results([[
1478 cake_fruit_model(1, 1),
1479 cake_fruit_model(2, 2),
1480 cake_fruit_model(2, 3),
1481 ]])
1482 .into_connection();
1483
1484 assert_eq!(
1485 Cake::find().find_with_related(Fruit).all(&db).await?,
1486 [
1487 (cake_model(1), vec![fruit_model(1, Some(1))]),
1488 (
1489 cake_model(2),
1490 vec![fruit_model(2, Some(2)), fruit_model(3, Some(2))]
1491 )
1492 ]
1493 );
1494
1495 assert_eq!(
1496 db.into_transaction_log(),
1497 [Transaction::many([Statement::from_sql_and_values(
1498 DbBackend::Postgres,
1499 [
1500 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1501 r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
1502 r#"FROM "cake""#,
1503 r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
1504 r#"ORDER BY "cake"."id" ASC"#
1505 ]
1506 .join(" ")
1507 .as_str(),
1508 []
1509 ),])]
1510 );
1511
1512 Ok(())
1513 }
1514
1515 #[smol_potat::test]
1516 pub async fn with_related_2() -> Result<(), sea_orm::DbErr> {
1517 use sea_orm::tests_cfg::*;
1518 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1519
1520 let db = MockDatabase::new(DbBackend::Postgres)
1521 .append_query_results([[
1522 cake_fruit_model(1, 1).into_mock_row(),
1523 cake_fruit_model(2, 2).into_mock_row(),
1524 cake_fruit_model(2, 3).into_mock_row(),
1525 cake_fruit_model(2, 4).into_mock_row(),
1526 ]])
1527 .into_connection();
1528
1529 assert_eq!(
1530 Cake::find().find_with_related(Fruit).all(&db).await?,
1531 [
1532 (cake_model(1), vec![fruit_model(1, Some(1)),]),
1533 (
1534 cake_model(2),
1535 vec![
1536 fruit_model(2, Some(2)),
1537 fruit_model(3, Some(2)),
1538 fruit_model(4, Some(2)),
1539 ]
1540 ),
1541 ]
1542 );
1543
1544 Ok(())
1545 }
1546
1547 #[smol_potat::test]
1548 pub async fn with_related_empty() -> Result<(), sea_orm::DbErr> {
1549 use sea_orm::tests_cfg::*;
1550 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1551
1552 let db = MockDatabase::new(DbBackend::Postgres)
1553 .append_query_results([[
1554 cake_fruit_model(1, 1).into_mock_row(),
1555 cake_fruit_model(2, 2).into_mock_row(),
1556 cake_fruit_model(2, 3).into_mock_row(),
1557 cake_fruit_model(2, 4).into_mock_row(),
1558 (cake_model(3), None::<fruit::Model>).into_mock_row(),
1559 ]])
1560 .into_connection();
1561
1562 assert_eq!(
1563 Cake::find().find_with_related(Fruit).all(&db).await?,
1564 [
1565 (cake_model(1), vec![fruit_model(1, Some(1)),]),
1566 (
1567 cake_model(2),
1568 vec![
1569 fruit_model(2, Some(2)),
1570 fruit_model(3, Some(2)),
1571 fruit_model(4, Some(2)),
1572 ]
1573 ),
1574 (cake_model(3), vec![])
1575 ]
1576 );
1577
1578 Ok(())
1579 }
1580
1581 #[smol_potat::test]
1582 pub async fn with_related_many_to_many() -> Result<(), sea_orm::DbErr> {
1583 use sea_orm::tests_cfg::*;
1584 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1585
1586 let db = MockDatabase::new(DbBackend::Postgres)
1587 .append_query_results([[
1588 cake_filling_models(1, 1).into_mock_row(),
1589 cake_filling_models(1, 2).into_mock_row(),
1590 cake_filling_models(2, 2).into_mock_row(),
1591 ]])
1592 .into_connection();
1593
1594 assert_eq!(
1595 Cake::find().find_with_related(Filling).all(&db).await?,
1596 [
1597 (cake_model(1), vec![filling_model(1), filling_model(2)]),
1598 (cake_model(2), vec![filling_model(2)]),
1599 ]
1600 );
1601
1602 Ok(())
1603 }
1604
1605 #[smol_potat::test]
1606 pub async fn with_related_many_to_many_2() -> Result<(), sea_orm::DbErr> {
1607 use sea_orm::tests_cfg::*;
1608 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1609
1610 let db = MockDatabase::new(DbBackend::Postgres)
1611 .append_query_results([[
1612 cake_filling_models(1, 1).into_mock_row(),
1613 cake_filling_models(1, 2).into_mock_row(),
1614 cake_filling_models(2, 2).into_mock_row(),
1615 (cake_model(3), None::<filling::Model>).into_mock_row(),
1616 ]])
1617 .into_connection();
1618
1619 assert_eq!(
1620 Cake::find().find_with_related(Filling).all(&db).await?,
1621 [
1622 (cake_model(1), vec![filling_model(1), filling_model(2)]),
1623 (cake_model(2), vec![filling_model(2)]),
1624 (cake_model(3), vec![])
1625 ]
1626 );
1627
1628 Ok(())
1629 }
1630
1631 #[smol_potat::test]
1632 pub async fn also_linked_base() -> Result<(), sea_orm::DbErr> {
1633 use sea_orm::tests_cfg::*;
1634 use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
1635
1636 let db = MockDatabase::new(DbBackend::Postgres)
1637 .append_query_results([[cake_vendor_link(1, 1)]])
1638 .into_connection();
1639
1640 assert_eq!(
1641 Cake::find()
1642 .find_also_linked(entity_linked::CakeToFillingVendor)
1643 .all(&db)
1644 .await?,
1645 [(cake_model(1), Some(vendor_model(1)))]
1646 );
1647
1648 assert_eq!(
1649 db.into_transaction_log(),
1650 [Transaction::many([Statement::from_sql_and_values(
1651 DbBackend::Postgres,
1652 [
1653 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1654 r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
1655 r#"FROM "cake""#,
1656 r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
1657 r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
1658 r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
1659 ]
1660 .join(" ")
1661 .as_str(),
1662 []
1663 ),])]
1664 );
1665
1666 Ok(())
1667 }
1668
1669 #[smol_potat::test]
1670 pub async fn also_linked_same_cake() -> Result<(), sea_orm::DbErr> {
1671 use sea_orm::tests_cfg::*;
1672 use sea_orm::{DbBackend, EntityTrait, MockDatabase};
1673
1674 let db = MockDatabase::new(DbBackend::Postgres)
1675 .append_query_results([[
1676 cake_vendor_link(1, 1),
1677 cake_vendor_link(1, 2),
1678 cake_vendor_link(2, 3),
1679 ]])
1680 .into_connection();
1681
1682 assert_eq!(
1683 Cake::find()
1684 .find_also_linked(entity_linked::CakeToFillingVendor)
1685 .all(&db)
1686 .await?,
1687 [
1688 (cake_model(1), Some(vendor_model(1))),
1689 (cake_model(1), Some(vendor_model(2))),
1690 (cake_model(2), Some(vendor_model(3)))
1691 ]
1692 );
1693
1694 Ok(())
1695 }
1696
1697 #[smol_potat::test]
1698 pub async fn also_linked_same_vendor() -> Result<(), sea_orm::DbErr> {
1699 use sea_orm::tests_cfg::*;
1700 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1701
1702 let db = MockDatabase::new(DbBackend::Postgres)
1703 .append_query_results([[
1704 cake_vendor_link(1, 1).into_mock_row(),
1705 cake_vendor_link(2, 1).into_mock_row(),
1706 cake_vendor_link(3, 2).into_mock_row(),
1707 ]])
1708 .into_connection();
1709
1710 assert_eq!(
1711 Cake::find()
1712 .find_also_linked(entity_linked::CakeToFillingVendor)
1713 .all(&db)
1714 .await?,
1715 [
1716 (cake_model(1), Some(vendor_model(1))),
1717 (cake_model(2), Some(vendor_model(1))),
1718 (cake_model(3), Some(vendor_model(2))),
1719 ]
1720 );
1721
1722 Ok(())
1723 }
1724
1725 #[smol_potat::test]
1726 pub async fn also_linked_many_to_many() -> Result<(), sea_orm::DbErr> {
1727 use sea_orm::tests_cfg::*;
1728 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1729
1730 let db = MockDatabase::new(DbBackend::Postgres)
1731 .append_query_results([[
1732 cake_vendor_link(1, 1).into_mock_row(),
1733 cake_vendor_link(1, 2).into_mock_row(),
1734 cake_vendor_link(1, 3).into_mock_row(),
1735 cake_vendor_link(2, 1).into_mock_row(),
1736 cake_vendor_link(2, 2).into_mock_row(),
1737 ]])
1738 .into_connection();
1739
1740 assert_eq!(
1741 Cake::find()
1742 .find_also_linked(entity_linked::CakeToFillingVendor)
1743 .all(&db)
1744 .await?,
1745 [
1746 (cake_model(1), Some(vendor_model(1))),
1747 (cake_model(1), Some(vendor_model(2))),
1748 (cake_model(1), Some(vendor_model(3))),
1749 (cake_model(2), Some(vendor_model(1))),
1750 (cake_model(2), Some(vendor_model(2))),
1751 ]
1752 );
1753
1754 Ok(())
1755 }
1756
1757 #[smol_potat::test]
1758 pub async fn also_linked_empty() -> Result<(), sea_orm::DbErr> {
1759 use sea_orm::tests_cfg::*;
1760 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1761
1762 let db = MockDatabase::new(DbBackend::Postgres)
1763 .append_query_results([[
1764 cake_vendor_link(1, 1).into_mock_row(),
1765 cake_vendor_link(2, 2).into_mock_row(),
1766 cake_vendor_link(3, 3).into_mock_row(),
1767 (cake_model(4), None::<vendor::Model>).into_mock_row(),
1768 ]])
1769 .into_connection();
1770
1771 assert_eq!(
1772 Cake::find()
1773 .find_also_linked(entity_linked::CakeToFillingVendor)
1774 .all(&db)
1775 .await?,
1776 [
1777 (cake_model(1), Some(vendor_model(1))),
1778 (cake_model(2), Some(vendor_model(2))),
1779 (cake_model(3), Some(vendor_model(3))),
1780 (cake_model(4), None)
1781 ]
1782 );
1783
1784 Ok(())
1785 }
1786
1787 #[smol_potat::test]
1788 pub async fn with_linked_base() -> Result<(), sea_orm::DbErr> {
1789 use sea_orm::tests_cfg::*;
1790 use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
1791
1792 let db = MockDatabase::new(DbBackend::Postgres)
1793 .append_query_results([[
1794 cake_vendor_link(1, 1),
1795 cake_vendor_link(2, 2),
1796 cake_vendor_link(2, 3),
1797 ]])
1798 .into_connection();
1799
1800 assert_eq!(
1801 Cake::find()
1802 .find_with_linked(entity_linked::CakeToFillingVendor)
1803 .all(&db)
1804 .await?,
1805 [
1806 (cake_model(1), vec![vendor_model(1)]),
1807 (cake_model(2), vec![vendor_model(2), vendor_model(3)])
1808 ]
1809 );
1810
1811 assert_eq!(
1812 db.into_transaction_log(),
1813 [Transaction::many([Statement::from_sql_and_values(
1814 DbBackend::Postgres,
1815 [
1816 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1817 r#""r2"."id" AS "B_id", "r2"."name" AS "B_name" FROM "cake""#,
1818 r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
1819 r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
1820 r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
1821 ]
1822 .join(" ")
1823 .as_str(),
1824 []
1825 ),])]
1826 );
1827
1828 Ok(())
1829 }
1830
1831 #[smol_potat::test]
1832 pub async fn with_linked_same_vendor() -> Result<(), sea_orm::DbErr> {
1833 use sea_orm::tests_cfg::*;
1834 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1835
1836 let db = MockDatabase::new(DbBackend::Postgres)
1837 .append_query_results([[
1838 cake_vendor_link(1, 1).into_mock_row(),
1839 cake_vendor_link(2, 2).into_mock_row(),
1840 cake_vendor_link(3, 2).into_mock_row(),
1841 ]])
1842 .into_connection();
1843
1844 assert_eq!(
1845 Cake::find()
1846 .find_with_linked(entity_linked::CakeToFillingVendor)
1847 .all(&db)
1848 .await?,
1849 [
1850 (cake_model(1), vec![vendor_model(1)]),
1851 (cake_model(2), vec![vendor_model(2)]),
1852 (cake_model(3), vec![vendor_model(2)])
1853 ]
1854 );
1855
1856 Ok(())
1857 }
1858
1859 #[smol_potat::test]
1860 pub async fn with_linked_empty() -> Result<(), sea_orm::DbErr> {
1861 use sea_orm::tests_cfg::*;
1862 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1863
1864 let db = MockDatabase::new(DbBackend::Postgres)
1865 .append_query_results([[
1866 cake_vendor_link(1, 1).into_mock_row(),
1867 cake_vendor_link(2, 1).into_mock_row(),
1868 cake_vendor_link(2, 2).into_mock_row(),
1869 (cake_model(3), None::<vendor::Model>).into_mock_row(),
1870 ]])
1871 .into_connection();
1872
1873 assert_eq!(
1874 Cake::find()
1875 .find_with_linked(entity_linked::CakeToFillingVendor)
1876 .all(&db)
1877 .await?,
1878 [
1879 (cake_model(1), vec![vendor_model(1)]),
1880 (cake_model(2), vec![vendor_model(1), vendor_model(2)]),
1881 (cake_model(3), vec![])
1882 ]
1883 );
1884
1885 Ok(())
1886 }
1887
1888 #[smol_potat::test]
1890 pub async fn with_linked_repeated() -> Result<(), sea_orm::DbErr> {
1891 use sea_orm::tests_cfg::*;
1892 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1893
1894 let db = MockDatabase::new(DbBackend::Postgres)
1895 .append_query_results([[
1896 cake_vendor_link(1, 1).into_mock_row(),
1897 cake_vendor_link(1, 1).into_mock_row(),
1898 cake_vendor_link(2, 1).into_mock_row(),
1899 cake_vendor_link(2, 2).into_mock_row(),
1900 ]])
1901 .into_connection();
1902
1903 assert_eq!(
1904 Cake::find()
1905 .find_with_linked(entity_linked::CakeToFillingVendor)
1906 .all(&db)
1907 .await?,
1908 [
1909 (cake_model(1), vec![vendor_model(1), vendor_model(1)]),
1910 (cake_model(2), vec![vendor_model(1), vendor_model(2)]),
1911 ]
1912 );
1913
1914 Ok(())
1915 }
1916}