Skip to main content

sea_orm/query/
combine.rs

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