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