sea_orm/query/
combine.rs

1use crate::{
2    ColumnTrait, EntityTrait, IdenStatic, Iterable, QueryTrait, Select, SelectTwo, SelectTwoMany,
3};
4use core::marker::PhantomData;
5use sea_query::{Iden, IntoIden, Order, SelectExpr, SelectStatement, SimpleExpr};
6use std::borrow::Cow;
7
8macro_rules! select_def {
9    ( $ident: ident, $str: expr ) => {
10        /// Implements the traits [Iden] for select alias
11        #[derive(Debug, Clone, Copy)]
12        pub struct $ident;
13
14        impl Iden for $ident {
15            fn quoted(&self) -> Cow<'static, str> {
16                Cow::Borrowed(IdenStatic::as_str(self))
17            }
18
19            fn unquoted(&self) -> &str {
20                IdenStatic::as_str(self)
21            }
22        }
23
24        impl IdenStatic for $ident {
25            fn as_str(&self) -> &'static str {
26                $str
27            }
28        }
29    };
30}
31
32select_def!(SelectA, "A_");
33select_def!(SelectB, "B_");
34select_def!(SelectC, "C_");
35select_def!(SelectD, "D_");
36select_def!(SelectE, "E_");
37select_def!(SelectF, "F_");
38
39impl<E> Select<E>
40where
41    E: EntityTrait,
42{
43    pub(crate) fn apply_alias(mut self, pre: &str) -> Self {
44        self.query().exprs_mut_for_each(|sel| {
45            match &sel.alias {
46                Some(alias) => {
47                    let alias = format!("{}{}", pre, alias.to_string().as_str());
48                    sel.alias = Some(alias.into_iden());
49                }
50                None => {
51                    let col = match &sel.expr {
52                        SimpleExpr::Column(col_ref) => match col_ref.column() {
53                            Some(col) => col,
54                            None => {
55                                panic!("cannot apply alias for Column with asterisk");
56                            }
57                        },
58                        SimpleExpr::AsEnum(_, simple_expr) => match simple_expr.as_ref() {
59                            SimpleExpr::Column(col_ref) => match col_ref.column() {
60                                Some(col) => col,
61                                None => {
62                                    panic!("cannot apply alias for AsEnum with asterisk")
63                                }
64                            },
65                            _ => {
66                                panic!("cannot apply alias for AsEnum with expr other than Column")
67                            }
68                        },
69                        _ => panic!("cannot apply alias for expr other than Column or AsEnum"),
70                    };
71                    let alias = format!("{}{}", pre, col.to_string().as_str());
72                    sel.alias = Some(alias.into_iden());
73                }
74            };
75        });
76        self
77    }
78
79    /// Selects extra Entity and returns it together with the Entity from `Self`
80    pub fn select_also<F>(mut self, _: F) -> SelectTwo<E, F>
81    where
82        F: EntityTrait,
83    {
84        self = self.apply_alias(SelectA.as_str());
85        SelectTwo::new(self.into_query())
86    }
87
88    /// Only used by Entity loader
89    #[doc(hidden)]
90    pub fn select_also_fake<F>(mut self, _: F) -> SelectTwo<E, F>
91    where
92        F: EntityTrait,
93    {
94        self = self.apply_alias(SelectA.as_str());
95        SelectTwo::new_without_prepare(self.into_query())
96    }
97
98    /// Makes a SELECT operation in conjunction to another relation
99    pub fn select_with<F>(mut self, _: F) -> SelectTwoMany<E, F>
100    where
101        F: EntityTrait,
102    {
103        self = self.apply_alias(SelectA.as_str());
104        SelectTwoMany::new(self.into_query())
105    }
106}
107
108impl<E, F> SelectTwo<E, F>
109where
110    E: EntityTrait,
111    F: EntityTrait,
112{
113    pub(crate) fn new(query: SelectStatement) -> Self {
114        Self::new_without_prepare(query).prepare_select()
115    }
116
117    pub(crate) fn new_without_prepare(query: SelectStatement) -> Self {
118        Self {
119            query,
120            entity: PhantomData,
121        }
122    }
123
124    fn prepare_select(mut self) -> Self {
125        prepare_select_col::<F, _, _>(&mut self, SelectB);
126        self
127    }
128}
129
130impl<E, F> SelectTwoMany<E, F>
131where
132    E: EntityTrait,
133    F: EntityTrait,
134{
135    pub(crate) fn new(query: SelectStatement) -> Self {
136        Self::new_without_prepare(query)
137            .prepare_select()
138            .prepare_order_by()
139    }
140
141    pub(crate) fn new_without_prepare(query: SelectStatement) -> Self {
142        Self {
143            query,
144            entity: PhantomData,
145        }
146    }
147
148    fn prepare_select(mut self) -> Self {
149        prepare_select_col::<F, _, _>(&mut self, SelectB);
150        self
151    }
152
153    fn prepare_order_by(mut self) -> Self {
154        for col in <E::PrimaryKey as Iterable>::iter() {
155            self.query.order_by((E::default(), col), Order::Asc);
156        }
157        self
158    }
159}
160
161pub(crate) fn prepare_select_col<F, S, A>(selector: &mut S, alias: A)
162where
163    F: EntityTrait,
164    S: QueryTrait<QueryStatement = SelectStatement>,
165    A: IdenStatic,
166{
167    for col in <F::Column as Iterable>::iter() {
168        let alias = format!("{}{}", alias.as_str(), col.as_str());
169        selector.query().expr(SelectExpr {
170            expr: col.select_as(col.into_expr()),
171            alias: Some(alias.into_iden()),
172            window: None,
173        });
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use crate::tests_cfg::{cake, fruit};
180    use crate::{ColumnTrait, DbBackend, EntityTrait, QueryFilter, QuerySelect, QueryTrait};
181
182    #[test]
183    fn alias_1() {
184        assert_eq!(
185            cake::Entity::find()
186                .column_as(cake::Column::Id, "B")
187                .apply_alias("A_")
188                .build(DbBackend::MySql)
189                .to_string(),
190            "SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`, `cake`.`id` AS `A_B` FROM `cake`",
191        );
192    }
193
194    #[test]
195    fn select_also_1() {
196        assert_eq!(
197            cake::Entity::find()
198                .left_join(fruit::Entity)
199                .select_also(fruit::Entity)
200                .build(DbBackend::MySql)
201                .to_string(),
202            [
203                "SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
204                "`fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id`",
205                "FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
206            ].join(" ")
207        );
208    }
209
210    #[test]
211    fn select_with_1() {
212        assert_eq!(
213            cake::Entity::find()
214                .left_join(fruit::Entity)
215                .select_with(fruit::Entity)
216                .build(DbBackend::MySql)
217                .to_string(),
218            [
219                "SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
220                "`fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id`",
221                "FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
222                "ORDER BY `cake`.`id` ASC",
223            ].join(" ")
224        );
225    }
226
227    #[test]
228    fn select_also_2() {
229        assert_eq!(
230            cake::Entity::find()
231                .left_join(fruit::Entity)
232                .select_also(fruit::Entity)
233                .filter(cake::Column::Id.eq(1))
234                .filter(fruit::Column::Id.eq(2))
235                .build(DbBackend::MySql)
236                .to_string(),
237            [
238                "SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
239                "`fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id`",
240                "FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
241                "WHERE `cake`.`id` = 1 AND `fruit`.`id` = 2",
242            ].join(" ")
243        );
244    }
245
246    #[test]
247    fn select_with_2() {
248        assert_eq!(
249            cake::Entity::find()
250                .left_join(fruit::Entity)
251                .select_with(fruit::Entity)
252                .filter(cake::Column::Id.eq(1))
253                .filter(fruit::Column::Id.eq(2))
254                .build(DbBackend::MySql)
255                .to_string(),
256            [
257                "SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,",
258                "`fruit`.`id` AS `B_id`, `fruit`.`name` AS `B_name`, `fruit`.`cake_id` AS `B_cake_id`",
259                "FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`",
260                "WHERE `cake`.`id` = 1 AND `fruit`.`id` = 2",
261                "ORDER BY `cake`.`id` ASC",
262            ].join(" ")
263        );
264    }
265}