sea_orm/query/
combine.rs

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