1use crate::{
2 error::*, ConnectionTrait, DbBackend, EntityTrait, FromQueryResult, IdenStatic, Iterable,
3 ModelTrait, PartialModelTrait, PrimaryKeyArity, PrimaryKeyToColumn, PrimaryKeyTrait,
4 QueryResult, QuerySelect, Select, SelectA, SelectB, SelectTwo, SelectTwoMany, Statement,
5 StreamTrait, TryGetableMany,
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: S,
23}
24
25#[derive(Clone, Debug)]
27pub struct SelectorRaw<S>
28where
29 S: SelectorTrait,
30{
31 pub(crate) stmt: Statement,
32 #[allow(dead_code)]
33 selector: S,
34}
35
36pub trait SelectorTrait {
38 #[allow(missing_docs)]
39 type Item: Sized;
40
41 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr>;
43}
44
45#[derive(Debug)]
47pub struct SelectGetableValue<T, C>
48where
49 T: TryGetableMany,
50 C: strum::IntoEnumIterator + sea_query::Iden,
51{
52 columns: PhantomData<C>,
53 model: PhantomData<T>,
54}
55
56#[derive(Debug)]
58pub struct SelectGetableTuple<T>
59where
60 T: TryGetableMany,
61{
62 model: PhantomData<T>,
63}
64
65#[derive(Debug)]
67pub struct SelectModel<M>
68where
69 M: FromQueryResult,
70{
71 model: PhantomData<M>,
72}
73
74#[derive(Clone, Debug)]
76pub struct SelectTwoModel<M, N>
77where
78 M: FromQueryResult,
79 N: FromQueryResult,
80{
81 model: PhantomData<(M, N)>,
82}
83
84impl<T, C> SelectorTrait for SelectGetableValue<T, C>
85where
86 T: TryGetableMany,
87 C: strum::IntoEnumIterator + sea_query::Iden,
88{
89 type Item = T;
90
91 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
92 let cols: Vec<String> = C::iter().map(|col| col.to_string()).collect();
93 T::try_get_many(&res, "", &cols).map_err(Into::into)
94 }
95}
96
97impl<T> SelectorTrait for SelectGetableTuple<T>
98where
99 T: TryGetableMany,
100{
101 type Item = T;
102
103 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
104 T::try_get_many_by_index(&res).map_err(Into::into)
105 }
106}
107
108impl<M> SelectorTrait for SelectModel<M>
109where
110 M: FromQueryResult + Sized,
111{
112 type Item = M;
113
114 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
115 M::from_query_result(&res, "")
116 }
117}
118
119impl<M, N> SelectorTrait for SelectTwoModel<M, N>
120where
121 M: FromQueryResult + Sized,
122 N: FromQueryResult + Sized,
123{
124 type Item = (M, Option<N>);
125
126 fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr> {
127 Ok((
128 M::from_query_result(&res, SelectA.as_str())?,
129 N::from_query_result_optional(&res, SelectB.as_str())?,
130 ))
131 }
132}
133
134impl<E> Select<E>
135where
136 E: EntityTrait,
137{
138 #[allow(clippy::wrong_self_convention)]
140 pub fn from_raw_sql(self, stmt: Statement) -> SelectorRaw<SelectModel<E::Model>> {
141 SelectorRaw {
142 stmt,
143 selector: SelectModel { model: PhantomData },
144 }
145 }
146
147 pub fn into_model<M>(self) -> Selector<SelectModel<M>>
149 where
150 M: FromQueryResult,
151 {
152 Selector {
153 query: self.query,
154 selector: SelectModel { model: PhantomData },
155 }
156 }
157
158 pub fn into_partial_model<M>(self) -> Selector<SelectModel<M>>
191 where
192 M: PartialModelTrait,
193 {
194 M::select_cols(QuerySelect::select_only(self)).into_model::<M>()
195 }
196
197 #[cfg(feature = "with-json")]
199 pub fn into_json(self) -> Selector<SelectModel<JsonValue>> {
200 Selector {
201 query: self.query,
202 selector: SelectModel { model: PhantomData },
203 }
204 }
205
206 pub fn into_values<T, C>(self) -> Selector<SelectGetableValue<T, C>>
309 where
310 T: TryGetableMany,
311 C: strum::IntoEnumIterator + sea_query::Iden,
312 {
313 Selector::<SelectGetableValue<T, C>>::with_columns(self.query)
314 }
315
316 pub fn into_tuple<T>(self) -> Selector<SelectGetableTuple<T>>
408 where
409 T: TryGetableMany,
410 {
411 Selector::<SelectGetableTuple<T>>::into_tuple(self.query)
412 }
413
414 pub async fn one<C>(self, db: &C) -> Result<Option<E::Model>, DbErr>
416 where
417 C: ConnectionTrait,
418 {
419 self.into_model().one(db).await
420 }
421
422 pub async fn all<C>(self, db: &C) -> Result<Vec<E::Model>, DbErr>
424 where
425 C: ConnectionTrait,
426 {
427 self.into_model().all(db).await
428 }
429
430 pub async fn stream<'a: 'b, 'b, C>(
432 self,
433 db: &'a C,
434 ) -> Result<impl Stream<Item = Result<E::Model, DbErr>> + 'b + Send, DbErr>
435 where
436 C: ConnectionTrait + StreamTrait + Send,
437 {
438 self.into_model().stream(db).await
439 }
440
441 pub async fn stream_partial_model<'a: 'b, 'b, C, M>(
443 self,
444 db: &'a C,
445 ) -> Result<impl Stream<Item = Result<M, DbErr>> + 'b + Send, DbErr>
446 where
447 C: ConnectionTrait + StreamTrait + Send,
448 M: PartialModelTrait + Send + 'b,
449 {
450 self.into_partial_model().stream(db).await
451 }
452}
453
454impl<E, F> SelectTwo<E, F>
455where
456 E: EntityTrait,
457 F: EntityTrait,
458{
459 pub fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
461 where
462 M: FromQueryResult,
463 N: FromQueryResult,
464 {
465 Selector {
466 query: self.query,
467 selector: SelectTwoModel { model: PhantomData },
468 }
469 }
470
471 pub fn into_partial_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
473 where
474 M: PartialModelTrait,
475 N: PartialModelTrait,
476 {
477 let select = QuerySelect::select_only(self);
478 let select = M::select_cols(select);
479 let select = N::select_cols(select);
480 select.into_model::<M, N>()
481 }
482
483 #[cfg(feature = "with-json")]
485 pub fn into_json(self) -> Selector<SelectTwoModel<JsonValue, JsonValue>> {
486 Selector {
487 query: self.query,
488 selector: SelectTwoModel { model: PhantomData },
489 }
490 }
491
492 pub async fn one<C>(self, db: &C) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr>
494 where
495 C: ConnectionTrait,
496 {
497 self.into_model().one(db).await
498 }
499
500 pub async fn all<C>(self, db: &C) -> Result<Vec<(E::Model, Option<F::Model>)>, DbErr>
502 where
503 C: ConnectionTrait,
504 {
505 self.into_model().all(db).await
506 }
507
508 pub async fn stream<'a: 'b, 'b, C>(
510 self,
511 db: &'a C,
512 ) -> Result<impl Stream<Item = Result<(E::Model, Option<F::Model>), DbErr>> + 'b, DbErr>
513 where
514 C: ConnectionTrait + StreamTrait + Send,
515 {
516 self.into_model().stream(db).await
517 }
518
519 pub async fn stream_partial_model<'a: 'b, 'b, C, M, N>(
521 self,
522 db: &'a C,
523 ) -> Result<impl Stream<Item = Result<(M, Option<N>), DbErr>> + 'b + Send, DbErr>
524 where
525 C: ConnectionTrait + StreamTrait + Send,
526 M: PartialModelTrait + Send + 'b,
527 N: PartialModelTrait + Send + 'b,
528 {
529 self.into_partial_model().stream(db).await
530 }
531}
532
533impl<E, F> SelectTwoMany<E, F>
534where
535 E: EntityTrait,
536 F: EntityTrait,
537{
538 fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
540 where
541 M: FromQueryResult,
542 N: FromQueryResult,
543 {
544 Selector {
545 query: self.query,
546 selector: SelectTwoModel { model: PhantomData },
547 }
548 }
549
550 fn into_partial_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
552 where
553 M: PartialModelTrait,
554 N: PartialModelTrait,
555 {
556 let select = self.select_only();
557 let select = M::select_cols(select);
558 let select = N::select_cols(select);
559 select.into_model()
560 }
561
562 #[cfg(feature = "with-json")]
564 pub fn into_json(self) -> Selector<SelectTwoModel<JsonValue, JsonValue>> {
565 Selector {
566 query: self.query,
567 selector: SelectTwoModel { model: PhantomData },
568 }
569 }
570
571 pub async fn stream<'a: 'b, 'b, C>(
573 self,
574 db: &'a C,
575 ) -> Result<impl Stream<Item = Result<(E::Model, Option<F::Model>), DbErr>> + 'b + Send, DbErr>
576 where
577 C: ConnectionTrait + StreamTrait + Send,
578 {
579 self.into_model().stream(db).await
580 }
581
582 pub async fn stream_partial_model<'a: 'b, 'b, C, M, N>(
584 self,
585 db: &'a C,
586 ) -> Result<impl Stream<Item = Result<(M, Option<N>), DbErr>> + 'b + Send, DbErr>
587 where
588 C: ConnectionTrait + StreamTrait + Send,
589 M: PartialModelTrait + Send + 'b,
590 N: PartialModelTrait + Send + 'b,
591 {
592 self.into_partial_model().stream(db).await
593 }
594
595 pub async fn all<C>(self, db: &C) -> Result<Vec<(E::Model, Vec<F::Model>)>, DbErr>
604 where
605 C: ConnectionTrait,
606 {
607 let rows = self.into_model().all(db).await?;
608 Ok(consolidate_query_result::<E, F>(rows))
609 }
610
611 }
620
621impl<S> Selector<S>
622where
623 S: SelectorTrait,
624{
625 pub fn with_columns<T, C>(query: SelectStatement) -> Selector<SelectGetableValue<T, C>>
628 where
629 T: TryGetableMany,
630 C: strum::IntoEnumIterator + sea_query::Iden,
631 {
632 Selector {
633 query,
634 selector: SelectGetableValue {
635 columns: PhantomData,
636 model: PhantomData,
637 },
638 }
639 }
640
641 pub fn into_tuple<T>(query: SelectStatement) -> Selector<SelectGetableTuple<T>>
643 where
644 T: TryGetableMany,
645 {
646 Selector {
647 query,
648 selector: SelectGetableTuple { model: PhantomData },
649 }
650 }
651
652 fn into_selector_raw<C>(self, db: &C) -> SelectorRaw<S>
653 where
654 C: ConnectionTrait,
655 {
656 let builder = db.get_database_backend();
657 let stmt = builder.build(&self.query);
658 SelectorRaw {
659 stmt,
660 selector: self.selector,
661 }
662 }
663
664 pub fn into_statement(self, builder: DbBackend) -> Statement {
666 builder.build(&self.query)
667 }
668
669 pub async fn one<C>(mut self, db: &C) -> Result<Option<S::Item>, DbErr>
671 where
672 C: ConnectionTrait,
673 {
674 self.query.limit(1);
675 self.into_selector_raw(db).one(db).await
676 }
677
678 pub async fn all<C>(self, db: &C) -> Result<Vec<S::Item>, DbErr>
680 where
681 C: ConnectionTrait,
682 {
683 self.into_selector_raw(db).all(db).await
684 }
685
686 pub async fn stream<'a: 'b, 'b, C>(
688 self,
689 db: &'a C,
690 ) -> Result<Pin<Box<dyn Stream<Item = Result<S::Item, DbErr>> + 'b + Send>>, DbErr>
691 where
692 C: ConnectionTrait + StreamTrait + Send,
693 S: 'b,
694 S::Item: Send,
695 {
696 self.into_selector_raw(db).stream(db).await
697 }
698}
699
700impl<S> SelectorRaw<S>
701where
702 S: SelectorTrait,
703{
704 pub fn from_statement<M>(stmt: Statement) -> SelectorRaw<SelectModel<M>>
706 where
707 M: FromQueryResult,
708 {
709 SelectorRaw {
710 stmt,
711 selector: SelectModel { model: PhantomData },
712 }
713 }
714
715 pub fn with_columns<T, C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<T, C>>
718 where
719 T: TryGetableMany,
720 C: strum::IntoEnumIterator + sea_query::Iden,
721 {
722 SelectorRaw {
723 stmt,
724 selector: SelectGetableValue {
725 columns: PhantomData,
726 model: PhantomData,
727 },
728 }
729 }
730
731 pub fn into_model<M>(self) -> SelectorRaw<SelectModel<M>>
796 where
797 M: FromQueryResult,
798 {
799 SelectorRaw {
800 stmt: self.stmt,
801 selector: SelectModel { model: PhantomData },
802 }
803 }
804
805 #[cfg(feature = "with-json")]
862 pub fn into_json(self) -> SelectorRaw<SelectModel<JsonValue>> {
863 SelectorRaw {
864 stmt: self.stmt,
865 selector: SelectModel { model: PhantomData },
866 }
867 }
868
869 pub fn into_statement(self) -> Statement {
871 self.stmt
872 }
873
874 pub async fn one<C>(self, db: &C) -> Result<Option<S::Item>, DbErr>
915 where
916 C: ConnectionTrait,
917 {
918 let row = db.query_one(self.stmt).await?;
919 match row {
920 Some(row) => Ok(Some(S::from_raw_query_result(row)?)),
921 None => Ok(None),
922 }
923 }
924
925 pub async fn all<C>(self, db: &C) -> Result<Vec<S::Item>, DbErr>
966 where
967 C: ConnectionTrait,
968 {
969 let rows = db.query_all(self.stmt).await?;
970 let mut models = Vec::new();
971 for row in rows.into_iter() {
972 models.push(S::from_raw_query_result(row)?);
973 }
974 Ok(models)
975 }
976
977 pub async fn stream<'a: 'b, 'b, C>(
979 self,
980 db: &'a C,
981 ) -> Result<Pin<Box<dyn Stream<Item = Result<S::Item, DbErr>> + 'b + Send>>, DbErr>
982 where
983 C: ConnectionTrait + StreamTrait + Send,
984 S: 'b,
985 S::Item: Send,
986 {
987 let stream = db.stream(self.stmt).await?;
988 Ok(Box::pin(stream.and_then(|row| {
989 futures_util::future::ready(S::from_raw_query_result(row))
990 })))
991 }
992}
993
994#[allow(clippy::unwrap_used)]
995fn consolidate_query_result<L, R>(
996 rows: Vec<(L::Model, Option<R::Model>)>,
997) -> Vec<(L::Model, Vec<R::Model>)>
998where
999 L: EntityTrait,
1000 R: EntityTrait,
1001{
1002 match <<L::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY {
1003 1 => {
1004 let col = <L::PrimaryKey as Iterable>::iter()
1005 .next()
1006 .unwrap()
1007 .into_column();
1008 consolidate_query_result_of::<L, R, UnitPk<L>>(rows, UnitPk(col))
1009 }
1010 2 => {
1011 let mut iter = <L::PrimaryKey as Iterable>::iter();
1012 let col1 = iter.next().unwrap().into_column();
1013 let col2 = iter.next().unwrap().into_column();
1014 consolidate_query_result_of::<L, R, PairPk<L>>(rows, PairPk(col1, col2))
1015 }
1016 _ => {
1017 let cols: Vec<_> = <L::PrimaryKey as Iterable>::iter()
1018 .map(|pk| pk.into_column())
1019 .collect();
1020 consolidate_query_result_of::<L, R, TuplePk<L>>(rows, TuplePk(cols))
1021 }
1022 }
1023}
1024
1025trait ModelKey<E: EntityTrait> {
1026 type Type: Hash + PartialEq + Eq;
1027 fn get(&self, model: &E::Model) -> Self::Type;
1028}
1029
1030struct UnitPk<E: EntityTrait>(E::Column);
1032struct PairPk<E: EntityTrait>(E::Column, E::Column);
1033struct TuplePk<E: EntityTrait>(Vec<E::Column>);
1034
1035impl<E: EntityTrait> ModelKey<E> for UnitPk<E> {
1036 type Type = Value;
1037 fn get(&self, model: &E::Model) -> Self::Type {
1038 model.get(self.0)
1039 }
1040}
1041
1042impl<E: EntityTrait> ModelKey<E> for PairPk<E> {
1043 type Type = (Value, Value);
1044 fn get(&self, model: &E::Model) -> Self::Type {
1045 (model.get(self.0), model.get(self.1))
1046 }
1047}
1048
1049impl<E: EntityTrait> ModelKey<E> for TuplePk<E> {
1050 type Type = Vec<Value>;
1051 fn get(&self, model: &E::Model) -> Self::Type {
1052 let mut key = Vec::new();
1053 for col in self.0.iter() {
1054 key.push(model.get(*col));
1055 }
1056 key
1057 }
1058}
1059
1060fn consolidate_query_result_of<L, R, KEY: ModelKey<L>>(
1061 mut rows: Vec<(L::Model, Option<R::Model>)>,
1062 model_key: KEY,
1063) -> Vec<(L::Model, Vec<R::Model>)>
1064where
1065 L: EntityTrait,
1066 R: EntityTrait,
1067{
1068 let mut hashmap: HashMap<KEY::Type, Vec<R::Model>> =
1069 rows.iter_mut().fold(HashMap::new(), |mut acc, row| {
1070 let key = model_key.get(&row.0);
1071 if let Some(value) = row.1.take() {
1072 let vec: Option<&mut Vec<R::Model>> = acc.get_mut(&key);
1073 if let Some(vec) = vec {
1074 vec.push(value)
1075 } else {
1076 acc.insert(key, vec![value]);
1077 }
1078 } else {
1079 acc.entry(key).or_default();
1080 }
1081
1082 acc
1083 });
1084
1085 rows.into_iter()
1086 .filter_map(|(l_model, _)| {
1087 let l_pk = model_key.get(&l_model);
1088 let r_models = hashmap.remove(&l_pk);
1089 r_models.map(|r_models| (l_model, r_models))
1090 })
1091 .collect()
1092}
1093
1094#[allow(dead_code)]
1096fn consolidate_query_result_of_ordered_rows<L, R>(
1097 rows: Vec<(L::Model, Option<R::Model>)>,
1098) -> Vec<(L::Model, Vec<R::Model>)>
1099where
1100 L: EntityTrait,
1101 R: EntityTrait,
1102{
1103 let mut acc: Vec<(L::Model, Vec<R::Model>)> = Vec::new();
1104 for (l, r) in rows {
1105 if let Some((last_l, last_r)) = acc.last_mut() {
1106 let mut same_l = true;
1107 for pk_col in <L::PrimaryKey as Iterable>::iter() {
1108 let col = pk_col.into_column();
1109 let val = l.get(col);
1110 let last_val = last_l.get(col);
1111 if !val.eq(&last_val) {
1112 same_l = false;
1113 break;
1114 }
1115 }
1116 if same_l {
1117 if let Some(r) = r {
1118 last_r.push(r);
1119 continue;
1120 }
1121 }
1122 }
1123 let rows = match r {
1124 Some(r) => vec![r],
1125 None => vec![],
1126 };
1127 acc.push((l, rows));
1128 }
1129 acc
1130}
1131
1132#[cfg(test)]
1133mod tests {
1134 use pretty_assertions::assert_eq;
1135
1136 fn cake_fruit_model(
1137 cake_id: i32,
1138 fruit_id: i32,
1139 ) -> (
1140 sea_orm::tests_cfg::cake::Model,
1141 sea_orm::tests_cfg::fruit::Model,
1142 ) {
1143 (cake_model(cake_id), fruit_model(fruit_id, Some(cake_id)))
1144 }
1145
1146 fn cake_model(id: i32) -> sea_orm::tests_cfg::cake::Model {
1147 let name = match id {
1148 1 => "apple cake",
1149 2 => "orange cake",
1150 3 => "fruit cake",
1151 4 => "chocolate cake",
1152 _ => "",
1153 }
1154 .to_string();
1155 sea_orm::tests_cfg::cake::Model { id, name }
1156 }
1157
1158 fn filling_model(id: i32) -> sea_orm::tests_cfg::filling::Model {
1159 let name = match id {
1160 1 => "apple juice",
1161 2 => "orange jam",
1162 3 => "fruit",
1163 4 => "chocolate crust",
1164 _ => "",
1165 }
1166 .to_string();
1167 sea_orm::tests_cfg::filling::Model {
1168 id,
1169 name,
1170 vendor_id: Some(1),
1171 ignored_attr: 0,
1172 }
1173 }
1174
1175 fn cake_filling_models(
1176 cake_id: i32,
1177 filling_id: i32,
1178 ) -> (
1179 sea_orm::tests_cfg::cake::Model,
1180 sea_orm::tests_cfg::filling::Model,
1181 ) {
1182 (cake_model(cake_id), filling_model(filling_id))
1183 }
1184
1185 fn fruit_model(id: i32, cake_id: Option<i32>) -> sea_orm::tests_cfg::fruit::Model {
1186 let name = match id {
1187 1 => "apple",
1188 2 => "orange",
1189 3 => "grape",
1190 4 => "strawberry",
1191 _ => "",
1192 }
1193 .to_string();
1194 sea_orm::tests_cfg::fruit::Model { id, name, cake_id }
1195 }
1196
1197 fn cake_vendor_link(
1198 cake_id: i32,
1199 vendor_id: i32,
1200 ) -> (
1201 sea_orm::tests_cfg::cake::Model,
1202 sea_orm::tests_cfg::vendor::Model,
1203 ) {
1204 (cake_model(cake_id), vendor_model(vendor_id))
1205 }
1206
1207 fn vendor_model(id: i32) -> sea_orm::tests_cfg::vendor::Model {
1208 let name = match id {
1209 1 => "Apollo",
1210 2 => "Benny",
1211 3 => "Christine",
1212 4 => "David",
1213 _ => "",
1214 }
1215 .to_string();
1216 sea_orm::tests_cfg::vendor::Model { id, name }
1217 }
1218
1219 #[smol_potat::test]
1220 pub async fn also_related() -> Result<(), sea_orm::DbErr> {
1221 use sea_orm::tests_cfg::*;
1222 use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
1223
1224 let db = MockDatabase::new(DbBackend::Postgres)
1225 .append_query_results([[cake_fruit_model(1, 1)]])
1226 .into_connection();
1227
1228 assert_eq!(
1229 Cake::find().find_also_related(Fruit).all(&db).await?,
1230 [(cake_model(1), Some(fruit_model(1, Some(1))))]
1231 );
1232
1233 assert_eq!(
1234 db.into_transaction_log(),
1235 [Transaction::many([Statement::from_sql_and_values(
1236 DbBackend::Postgres,
1237 [
1238 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1239 r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
1240 r#"FROM "cake""#,
1241 r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
1242 ]
1243 .join(" ")
1244 .as_str(),
1245 []
1246 ),])]
1247 );
1248
1249 Ok(())
1250 }
1251
1252 #[smol_potat::test]
1253 pub async fn also_related_2() -> Result<(), sea_orm::DbErr> {
1254 use sea_orm::tests_cfg::*;
1255 use sea_orm::{DbBackend, EntityTrait, MockDatabase};
1256
1257 let db = MockDatabase::new(DbBackend::Postgres)
1258 .append_query_results([[cake_fruit_model(1, 1), cake_fruit_model(1, 2)]])
1259 .into_connection();
1260
1261 assert_eq!(
1262 Cake::find().find_also_related(Fruit).all(&db).await?,
1263 [
1264 (cake_model(1), Some(fruit_model(1, Some(1)))),
1265 (cake_model(1), Some(fruit_model(2, Some(1))))
1266 ]
1267 );
1268
1269 Ok(())
1270 }
1271
1272 #[smol_potat::test]
1273 pub async fn also_related_3() -> Result<(), sea_orm::DbErr> {
1274 use sea_orm::tests_cfg::*;
1275 use sea_orm::{DbBackend, EntityTrait, MockDatabase};
1276
1277 let db = MockDatabase::new(DbBackend::Postgres)
1278 .append_query_results([[
1279 cake_fruit_model(1, 1),
1280 cake_fruit_model(1, 2),
1281 cake_fruit_model(2, 3),
1282 ]])
1283 .into_connection();
1284
1285 assert_eq!(
1286 Cake::find().find_also_related(Fruit).all(&db).await?,
1287 [
1288 (cake_model(1), Some(fruit_model(1, Some(1)))),
1289 (cake_model(1), Some(fruit_model(2, Some(1)))),
1290 (cake_model(2), Some(fruit_model(3, Some(2))))
1291 ]
1292 );
1293
1294 Ok(())
1295 }
1296
1297 #[smol_potat::test]
1298 pub async fn also_related_4() -> Result<(), sea_orm::DbErr> {
1299 use sea_orm::tests_cfg::*;
1300 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1301
1302 let db = MockDatabase::new(DbBackend::Postgres)
1303 .append_query_results([[
1304 cake_fruit_model(1, 1).into_mock_row(),
1305 cake_fruit_model(1, 2).into_mock_row(),
1306 cake_fruit_model(2, 3).into_mock_row(),
1307 (cake_model(3), None::<fruit::Model>).into_mock_row(),
1308 ]])
1309 .into_connection();
1310
1311 assert_eq!(
1312 Cake::find().find_also_related(Fruit).all(&db).await?,
1313 [
1314 (cake_model(1), Some(fruit_model(1, Some(1)))),
1315 (cake_model(1), Some(fruit_model(2, Some(1)))),
1316 (cake_model(2), Some(fruit_model(3, Some(2)))),
1317 (cake_model(3), None)
1318 ]
1319 );
1320
1321 Ok(())
1322 }
1323
1324 #[smol_potat::test]
1325 pub async fn also_related_many_to_many() -> Result<(), sea_orm::DbErr> {
1326 use sea_orm::tests_cfg::*;
1327 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1328
1329 let db = MockDatabase::new(DbBackend::Postgres)
1330 .append_query_results([[
1331 cake_filling_models(1, 1).into_mock_row(),
1332 cake_filling_models(1, 2).into_mock_row(),
1333 cake_filling_models(2, 2).into_mock_row(),
1334 ]])
1335 .into_connection();
1336
1337 assert_eq!(
1338 Cake::find().find_also_related(Filling).all(&db).await?,
1339 [
1340 (cake_model(1), Some(filling_model(1))),
1341 (cake_model(1), Some(filling_model(2))),
1342 (cake_model(2), Some(filling_model(2))),
1343 ]
1344 );
1345
1346 Ok(())
1347 }
1348
1349 #[smol_potat::test]
1350 pub async fn also_related_many_to_many_2() -> Result<(), sea_orm::DbErr> {
1351 use sea_orm::tests_cfg::*;
1352 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1353
1354 let db = MockDatabase::new(DbBackend::Postgres)
1355 .append_query_results([[
1356 cake_filling_models(1, 1).into_mock_row(),
1357 cake_filling_models(1, 2).into_mock_row(),
1358 cake_filling_models(2, 2).into_mock_row(),
1359 (cake_model(3), None::<filling::Model>).into_mock_row(),
1360 ]])
1361 .into_connection();
1362
1363 assert_eq!(
1364 Cake::find().find_also_related(Filling).all(&db).await?,
1365 [
1366 (cake_model(1), Some(filling_model(1))),
1367 (cake_model(1), Some(filling_model(2))),
1368 (cake_model(2), Some(filling_model(2))),
1369 (cake_model(3), None)
1370 ]
1371 );
1372
1373 Ok(())
1374 }
1375
1376 #[smol_potat::test]
1377 pub async fn with_related() -> Result<(), sea_orm::DbErr> {
1378 use sea_orm::tests_cfg::*;
1379 use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
1380
1381 let db = MockDatabase::new(DbBackend::Postgres)
1382 .append_query_results([[
1383 cake_fruit_model(1, 1),
1384 cake_fruit_model(2, 2),
1385 cake_fruit_model(2, 3),
1386 ]])
1387 .into_connection();
1388
1389 assert_eq!(
1390 Cake::find().find_with_related(Fruit).all(&db).await?,
1391 [
1392 (cake_model(1), vec![fruit_model(1, Some(1))]),
1393 (
1394 cake_model(2),
1395 vec![fruit_model(2, Some(2)), fruit_model(3, Some(2))]
1396 )
1397 ]
1398 );
1399
1400 assert_eq!(
1401 db.into_transaction_log(),
1402 [Transaction::many([Statement::from_sql_and_values(
1403 DbBackend::Postgres,
1404 [
1405 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1406 r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
1407 r#"FROM "cake""#,
1408 r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
1409 r#"ORDER BY "cake"."id" ASC"#
1410 ]
1411 .join(" ")
1412 .as_str(),
1413 []
1414 ),])]
1415 );
1416
1417 Ok(())
1418 }
1419
1420 #[smol_potat::test]
1421 pub async fn with_related_2() -> Result<(), sea_orm::DbErr> {
1422 use sea_orm::tests_cfg::*;
1423 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1424
1425 let db = MockDatabase::new(DbBackend::Postgres)
1426 .append_query_results([[
1427 cake_fruit_model(1, 1).into_mock_row(),
1428 cake_fruit_model(2, 2).into_mock_row(),
1429 cake_fruit_model(2, 3).into_mock_row(),
1430 cake_fruit_model(2, 4).into_mock_row(),
1431 ]])
1432 .into_connection();
1433
1434 assert_eq!(
1435 Cake::find().find_with_related(Fruit).all(&db).await?,
1436 [
1437 (cake_model(1), vec![fruit_model(1, Some(1)),]),
1438 (
1439 cake_model(2),
1440 vec![
1441 fruit_model(2, Some(2)),
1442 fruit_model(3, Some(2)),
1443 fruit_model(4, Some(2)),
1444 ]
1445 ),
1446 ]
1447 );
1448
1449 Ok(())
1450 }
1451
1452 #[smol_potat::test]
1453 pub async fn with_related_empty() -> Result<(), sea_orm::DbErr> {
1454 use sea_orm::tests_cfg::*;
1455 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1456
1457 let db = MockDatabase::new(DbBackend::Postgres)
1458 .append_query_results([[
1459 cake_fruit_model(1, 1).into_mock_row(),
1460 cake_fruit_model(2, 2).into_mock_row(),
1461 cake_fruit_model(2, 3).into_mock_row(),
1462 cake_fruit_model(2, 4).into_mock_row(),
1463 (cake_model(3), None::<fruit::Model>).into_mock_row(),
1464 ]])
1465 .into_connection();
1466
1467 assert_eq!(
1468 Cake::find().find_with_related(Fruit).all(&db).await?,
1469 [
1470 (cake_model(1), vec![fruit_model(1, Some(1)),]),
1471 (
1472 cake_model(2),
1473 vec![
1474 fruit_model(2, Some(2)),
1475 fruit_model(3, Some(2)),
1476 fruit_model(4, Some(2)),
1477 ]
1478 ),
1479 (cake_model(3), vec![])
1480 ]
1481 );
1482
1483 Ok(())
1484 }
1485
1486 #[smol_potat::test]
1487 pub async fn with_related_many_to_many() -> Result<(), sea_orm::DbErr> {
1488 use sea_orm::tests_cfg::*;
1489 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1490
1491 let db = MockDatabase::new(DbBackend::Postgres)
1492 .append_query_results([[
1493 cake_filling_models(1, 1).into_mock_row(),
1494 cake_filling_models(1, 2).into_mock_row(),
1495 cake_filling_models(2, 2).into_mock_row(),
1496 ]])
1497 .into_connection();
1498
1499 assert_eq!(
1500 Cake::find().find_with_related(Filling).all(&db).await?,
1501 [
1502 (cake_model(1), vec![filling_model(1), filling_model(2)]),
1503 (cake_model(2), vec![filling_model(2)]),
1504 ]
1505 );
1506
1507 Ok(())
1508 }
1509
1510 #[smol_potat::test]
1511 pub async fn with_related_many_to_many_2() -> Result<(), sea_orm::DbErr> {
1512 use sea_orm::tests_cfg::*;
1513 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1514
1515 let db = MockDatabase::new(DbBackend::Postgres)
1516 .append_query_results([[
1517 cake_filling_models(1, 1).into_mock_row(),
1518 cake_filling_models(1, 2).into_mock_row(),
1519 cake_filling_models(2, 2).into_mock_row(),
1520 (cake_model(3), None::<filling::Model>).into_mock_row(),
1521 ]])
1522 .into_connection();
1523
1524 assert_eq!(
1525 Cake::find().find_with_related(Filling).all(&db).await?,
1526 [
1527 (cake_model(1), vec![filling_model(1), filling_model(2)]),
1528 (cake_model(2), vec![filling_model(2)]),
1529 (cake_model(3), vec![])
1530 ]
1531 );
1532
1533 Ok(())
1534 }
1535
1536 #[smol_potat::test]
1537 pub async fn also_linked_base() -> Result<(), sea_orm::DbErr> {
1538 use sea_orm::tests_cfg::*;
1539 use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
1540
1541 let db = MockDatabase::new(DbBackend::Postgres)
1542 .append_query_results([[cake_vendor_link(1, 1)]])
1543 .into_connection();
1544
1545 assert_eq!(
1546 Cake::find()
1547 .find_also_linked(entity_linked::CakeToFillingVendor)
1548 .all(&db)
1549 .await?,
1550 [(cake_model(1), Some(vendor_model(1)))]
1551 );
1552
1553 assert_eq!(
1554 db.into_transaction_log(),
1555 [Transaction::many([Statement::from_sql_and_values(
1556 DbBackend::Postgres,
1557 [
1558 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1559 r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
1560 r#"FROM "cake""#,
1561 r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
1562 r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
1563 r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
1564 ]
1565 .join(" ")
1566 .as_str(),
1567 []
1568 ),])]
1569 );
1570
1571 Ok(())
1572 }
1573
1574 #[smol_potat::test]
1575 pub async fn also_linked_same_cake() -> Result<(), sea_orm::DbErr> {
1576 use sea_orm::tests_cfg::*;
1577 use sea_orm::{DbBackend, EntityTrait, MockDatabase};
1578
1579 let db = MockDatabase::new(DbBackend::Postgres)
1580 .append_query_results([[
1581 cake_vendor_link(1, 1),
1582 cake_vendor_link(1, 2),
1583 cake_vendor_link(2, 3),
1584 ]])
1585 .into_connection();
1586
1587 assert_eq!(
1588 Cake::find()
1589 .find_also_linked(entity_linked::CakeToFillingVendor)
1590 .all(&db)
1591 .await?,
1592 [
1593 (cake_model(1), Some(vendor_model(1))),
1594 (cake_model(1), Some(vendor_model(2))),
1595 (cake_model(2), Some(vendor_model(3)))
1596 ]
1597 );
1598
1599 Ok(())
1600 }
1601
1602 #[smol_potat::test]
1603 pub async fn also_linked_same_vendor() -> Result<(), sea_orm::DbErr> {
1604 use sea_orm::tests_cfg::*;
1605 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1606
1607 let db = MockDatabase::new(DbBackend::Postgres)
1608 .append_query_results([[
1609 cake_vendor_link(1, 1).into_mock_row(),
1610 cake_vendor_link(2, 1).into_mock_row(),
1611 cake_vendor_link(3, 2).into_mock_row(),
1612 ]])
1613 .into_connection();
1614
1615 assert_eq!(
1616 Cake::find()
1617 .find_also_linked(entity_linked::CakeToFillingVendor)
1618 .all(&db)
1619 .await?,
1620 [
1621 (cake_model(1), Some(vendor_model(1))),
1622 (cake_model(2), Some(vendor_model(1))),
1623 (cake_model(3), Some(vendor_model(2))),
1624 ]
1625 );
1626
1627 Ok(())
1628 }
1629
1630 #[smol_potat::test]
1631 pub async fn also_linked_many_to_many() -> Result<(), sea_orm::DbErr> {
1632 use sea_orm::tests_cfg::*;
1633 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1634
1635 let db = MockDatabase::new(DbBackend::Postgres)
1636 .append_query_results([[
1637 cake_vendor_link(1, 1).into_mock_row(),
1638 cake_vendor_link(1, 2).into_mock_row(),
1639 cake_vendor_link(1, 3).into_mock_row(),
1640 cake_vendor_link(2, 1).into_mock_row(),
1641 cake_vendor_link(2, 2).into_mock_row(),
1642 ]])
1643 .into_connection();
1644
1645 assert_eq!(
1646 Cake::find()
1647 .find_also_linked(entity_linked::CakeToFillingVendor)
1648 .all(&db)
1649 .await?,
1650 [
1651 (cake_model(1), Some(vendor_model(1))),
1652 (cake_model(1), Some(vendor_model(2))),
1653 (cake_model(1), Some(vendor_model(3))),
1654 (cake_model(2), Some(vendor_model(1))),
1655 (cake_model(2), Some(vendor_model(2))),
1656 ]
1657 );
1658
1659 Ok(())
1660 }
1661
1662 #[smol_potat::test]
1663 pub async fn also_linked_empty() -> Result<(), sea_orm::DbErr> {
1664 use sea_orm::tests_cfg::*;
1665 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1666
1667 let db = MockDatabase::new(DbBackend::Postgres)
1668 .append_query_results([[
1669 cake_vendor_link(1, 1).into_mock_row(),
1670 cake_vendor_link(2, 2).into_mock_row(),
1671 cake_vendor_link(3, 3).into_mock_row(),
1672 (cake_model(4), None::<vendor::Model>).into_mock_row(),
1673 ]])
1674 .into_connection();
1675
1676 assert_eq!(
1677 Cake::find()
1678 .find_also_linked(entity_linked::CakeToFillingVendor)
1679 .all(&db)
1680 .await?,
1681 [
1682 (cake_model(1), Some(vendor_model(1))),
1683 (cake_model(2), Some(vendor_model(2))),
1684 (cake_model(3), Some(vendor_model(3))),
1685 (cake_model(4), None)
1686 ]
1687 );
1688
1689 Ok(())
1690 }
1691
1692 #[smol_potat::test]
1693 pub async fn with_linked_base() -> Result<(), sea_orm::DbErr> {
1694 use sea_orm::tests_cfg::*;
1695 use sea_orm::{DbBackend, EntityTrait, MockDatabase, Statement, Transaction};
1696
1697 let db = MockDatabase::new(DbBackend::Postgres)
1698 .append_query_results([[
1699 cake_vendor_link(1, 1),
1700 cake_vendor_link(2, 2),
1701 cake_vendor_link(2, 3),
1702 ]])
1703 .into_connection();
1704
1705 assert_eq!(
1706 Cake::find()
1707 .find_with_linked(entity_linked::CakeToFillingVendor)
1708 .all(&db)
1709 .await?,
1710 [
1711 (cake_model(1), vec![vendor_model(1)]),
1712 (cake_model(2), vec![vendor_model(2), vendor_model(3)])
1713 ]
1714 );
1715
1716 assert_eq!(
1717 db.into_transaction_log(),
1718 [Transaction::many([Statement::from_sql_and_values(
1719 DbBackend::Postgres,
1720 [
1721 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1722 r#""r2"."id" AS "B_id", "r2"."name" AS "B_name" FROM "cake""#,
1723 r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
1724 r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
1725 r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
1726 ]
1727 .join(" ")
1728 .as_str(),
1729 []
1730 ),])]
1731 );
1732
1733 Ok(())
1734 }
1735
1736 #[smol_potat::test]
1737 pub async fn with_linked_same_vendor() -> Result<(), sea_orm::DbErr> {
1738 use sea_orm::tests_cfg::*;
1739 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1740
1741 let db = MockDatabase::new(DbBackend::Postgres)
1742 .append_query_results([[
1743 cake_vendor_link(1, 1).into_mock_row(),
1744 cake_vendor_link(2, 2).into_mock_row(),
1745 cake_vendor_link(3, 2).into_mock_row(),
1746 ]])
1747 .into_connection();
1748
1749 assert_eq!(
1750 Cake::find()
1751 .find_with_linked(entity_linked::CakeToFillingVendor)
1752 .all(&db)
1753 .await?,
1754 [
1755 (cake_model(1), vec![vendor_model(1)]),
1756 (cake_model(2), vec![vendor_model(2)]),
1757 (cake_model(3), vec![vendor_model(2)])
1758 ]
1759 );
1760
1761 Ok(())
1762 }
1763
1764 #[smol_potat::test]
1765 pub async fn with_linked_empty() -> Result<(), sea_orm::DbErr> {
1766 use sea_orm::tests_cfg::*;
1767 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1768
1769 let db = MockDatabase::new(DbBackend::Postgres)
1770 .append_query_results([[
1771 cake_vendor_link(1, 1).into_mock_row(),
1772 cake_vendor_link(2, 1).into_mock_row(),
1773 cake_vendor_link(2, 2).into_mock_row(),
1774 (cake_model(3), None::<vendor::Model>).into_mock_row(),
1775 ]])
1776 .into_connection();
1777
1778 assert_eq!(
1779 Cake::find()
1780 .find_with_linked(entity_linked::CakeToFillingVendor)
1781 .all(&db)
1782 .await?,
1783 [
1784 (cake_model(1), vec![vendor_model(1)]),
1785 (cake_model(2), vec![vendor_model(1), vendor_model(2)]),
1786 (cake_model(3), vec![])
1787 ]
1788 );
1789
1790 Ok(())
1791 }
1792
1793 #[smol_potat::test]
1795 pub async fn with_linked_repeated() -> Result<(), sea_orm::DbErr> {
1796 use sea_orm::tests_cfg::*;
1797 use sea_orm::{DbBackend, EntityTrait, IntoMockRow, MockDatabase};
1798
1799 let db = MockDatabase::new(DbBackend::Postgres)
1800 .append_query_results([[
1801 cake_vendor_link(1, 1).into_mock_row(),
1802 cake_vendor_link(1, 1).into_mock_row(),
1803 cake_vendor_link(2, 1).into_mock_row(),
1804 cake_vendor_link(2, 2).into_mock_row(),
1805 ]])
1806 .into_connection();
1807
1808 assert_eq!(
1809 Cake::find()
1810 .find_with_linked(entity_linked::CakeToFillingVendor)
1811 .all(&db)
1812 .await?,
1813 [
1814 (cake_model(1), vec![vendor_model(1), vendor_model(1)]),
1815 (cake_model(2), vec![vendor_model(1), vendor_model(2)]),
1816 ]
1817 );
1818
1819 Ok(())
1820 }
1821}