sea_orm/query/
combine.rs

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