gluesql_core/query_builder/select/
group_by.rs1use {
2 super::{
3 BuildAggregationInputPlan, BuildAggregationPlan, BuildProjectInputPlan, BuildSelect,
4 DistinctNode,
5 },
6 crate::{
7 ast::Select,
8 plan::{AggregationInputPlan, AggregationPlan, ProjectInputPlan},
9 query_builder::{
10 ExprList, ExprNode, FilterNode, HavingNode, InnerHashJoinNode, InnerJoinConditionNode,
11 InnerNestedLoopJoinNode, LeftOuterHashJoinNode, LeftOuterJoinConditionNode,
12 LeftOuterNestedLoopJoinNode, LimitNode, OffsetNode, OrderByExprList, ProjectNode,
13 QueryNode, SelectItemList, SelectNode, SelectOrderByNode, SourceNode,
14 },
15 result::Result,
16 },
17};
18
19#[derive(Clone, Debug)]
20pub(super) enum PrevNode<'a> {
21 Select(SelectNode<'a>),
22 InnerNestedLoop(Box<InnerNestedLoopJoinNode<'a>>),
23 LeftOuterNestedLoop(Box<LeftOuterNestedLoopJoinNode<'a>>),
24 InnerHash(Box<InnerHashJoinNode<'a>>),
25 LeftOuterHash(Box<LeftOuterHashJoinNode<'a>>),
26 InnerCondition(Box<InnerJoinConditionNode<'a>>),
27 LeftOuterCondition(Box<LeftOuterJoinConditionNode<'a>>),
28 Filter(FilterNode<'a>),
29}
30
31impl BuildAggregationInputPlan for PrevNode<'_> {
32 fn build_aggregation_input_plan(self) -> Result<AggregationInputPlan> {
33 match self {
34 Self::Select(node) => node.build_aggregation_input_plan(),
35 Self::InnerNestedLoop(node) => node.build_aggregation_input_plan(),
36 Self::LeftOuterNestedLoop(node) => node.build_aggregation_input_plan(),
37 Self::InnerHash(node) => node.build_aggregation_input_plan(),
38 Self::LeftOuterHash(node) => node.build_aggregation_input_plan(),
39 Self::InnerCondition(node) => node.build_aggregation_input_plan(),
40 Self::LeftOuterCondition(node) => node.build_aggregation_input_plan(),
41 Self::Filter(node) => node.build_aggregation_input_plan(),
42 }
43 }
44}
45
46impl BuildSelect for PrevNode<'_> {
47 fn build_select(self) -> Result<Select> {
48 match self {
49 Self::Select(node) => node.build_select(),
50 Self::InnerNestedLoop(node) => node.build_select(),
51 Self::LeftOuterNestedLoop(node) => node.build_select(),
52 Self::InnerHash(node) => node.build_select(),
53 Self::LeftOuterHash(node) => node.build_select(),
54 Self::InnerCondition(node) => node.build_select(),
55 Self::LeftOuterCondition(node) => node.build_select(),
56 Self::Filter(node) => node.build_select(),
57 }
58 }
59}
60
61impl<'a> From<SelectNode<'a>> for PrevNode<'a> {
62 fn from(node: SelectNode<'a>) -> Self {
63 PrevNode::Select(node)
64 }
65}
66
67impl<'a> From<InnerNestedLoopJoinNode<'a>> for PrevNode<'a> {
68 fn from(node: InnerNestedLoopJoinNode<'a>) -> Self {
69 Self::InnerNestedLoop(Box::new(node))
70 }
71}
72
73impl<'a> From<LeftOuterNestedLoopJoinNode<'a>> for PrevNode<'a> {
74 fn from(node: LeftOuterNestedLoopJoinNode<'a>) -> Self {
75 Self::LeftOuterNestedLoop(Box::new(node))
76 }
77}
78
79impl<'a> From<InnerHashJoinNode<'a>> for PrevNode<'a> {
80 fn from(node: InnerHashJoinNode<'a>) -> Self {
81 Self::InnerHash(Box::new(node))
82 }
83}
84
85impl<'a> From<LeftOuterHashJoinNode<'a>> for PrevNode<'a> {
86 fn from(node: LeftOuterHashJoinNode<'a>) -> Self {
87 Self::LeftOuterHash(Box::new(node))
88 }
89}
90
91impl<'a> From<InnerJoinConditionNode<'a>> for PrevNode<'a> {
92 fn from(node: InnerJoinConditionNode<'a>) -> Self {
93 Self::InnerCondition(Box::new(node))
94 }
95}
96
97impl<'a> From<LeftOuterJoinConditionNode<'a>> for PrevNode<'a> {
98 fn from(node: LeftOuterJoinConditionNode<'a>) -> Self {
99 Self::LeftOuterCondition(Box::new(node))
100 }
101}
102
103impl<'a> From<FilterNode<'a>> for PrevNode<'a> {
104 fn from(node: FilterNode<'a>) -> Self {
105 PrevNode::Filter(node)
106 }
107}
108
109#[derive(Clone, Debug)]
110pub struct GroupByNode<'a> {
111 prev_node: PrevNode<'a>,
112 expr_list: ExprList<'a>,
113}
114
115impl<'a> GroupByNode<'a> {
116 pub(super) fn new<N: Into<PrevNode<'a>>, T: Into<ExprList<'a>>>(
117 prev_node: N,
118 expr_list: T,
119 ) -> Self {
120 Self {
121 prev_node: prev_node.into(),
122 expr_list: expr_list.into(),
123 }
124 }
125
126 pub fn having<T: Into<ExprNode<'a>>>(self, expr: T) -> HavingNode<'a> {
127 HavingNode::new(self, expr)
128 }
129
130 pub fn offset<T: Into<ExprNode<'a>>>(self, expr: T) -> OffsetNode<'a> {
131 OffsetNode::new(self, expr)
132 }
133
134 pub fn limit<T: Into<ExprNode<'a>>>(self, expr: T) -> LimitNode<'a> {
135 LimitNode::new(self, expr)
136 }
137
138 pub fn project<T: Into<SelectItemList<'a>>>(self, select_items: T) -> ProjectNode<'a> {
139 ProjectNode::new(self, select_items)
140 }
141
142 pub fn order_by<T: Into<OrderByExprList<'a>>>(self, expr_list: T) -> SelectOrderByNode<'a> {
143 SelectOrderByNode::new(self, expr_list)
144 }
145
146 pub fn distinct(self) -> DistinctNode<'a> {
147 DistinctNode::new(self)
148 }
149
150 pub fn alias_as(self, table_alias: &'a str) -> SourceNode<'a> {
151 QueryNode::GroupByNode(self).alias_as(table_alias)
152 }
153}
154
155impl BuildAggregationPlan for GroupByNode<'_> {
156 fn build_aggregation_plan(self) -> Result<AggregationPlan> {
157 Ok(AggregationPlan {
158 input: self.prev_node.build_aggregation_input_plan()?,
159 group_by: self.expr_list.build_exprs_plan()?,
160 aggregate_slots: Vec::new(),
161 })
162 }
163}
164
165impl BuildProjectInputPlan for GroupByNode<'_> {
166 fn build_project_input_plan(self) -> Result<ProjectInputPlan> {
167 self.build_aggregation_plan()
168 .map(ProjectInputPlan::Aggregation)
169 }
170}
171
172impl BuildSelect for GroupByNode<'_> {
173 fn build_select(self) -> Result<Select> {
174 let mut select = self.prev_node.build_select()?;
175 select.group_by = self.expr_list.build_exprs()?;
176
177 Ok(select)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use {
184 crate::{
185 plan::{
186 AggregationInputPlan, AggregationPlan, HashJoinInputPlan, HashJoinPlan,
187 InnerJoinInputPlan, InnerJoinPlan, ProjectInputPlan, ProjectPlan, ProjectionPlan,
188 QueryPlan, SourcePlan, StatementPlan, TableAccessPlan, TableSourcePlan,
189 },
190 query_builder::{Build, SelectItemList, col, table, test_query_builder},
191 },
192 pretty_assertions::assert_eq,
193 };
194
195 #[test]
196 fn group_by() {
197 let actual = table("Foo").select().group_by("a");
199 let expected = "SELECT * FROM Foo GROUP BY a";
200 test_query_builder(actual, expected);
201
202 let actual = table("Foo").select().join("Bar").group_by("b");
204 let expected = "SELECT * FROM Foo JOIN Bar GROUP BY b";
205 test_query_builder(actual, expected);
206
207 let actual = table("Foo").select().join_as("Bar", "B").group_by("b");
209 let expected = "SELECT * FROM Foo JOIN Bar AS B GROUP BY b";
210 test_query_builder(actual, expected);
211
212 let actual = table("Foo").select().left_join("Bar").group_by("b");
214 let expected = "SELECT * FROM Foo LEFT JOIN Bar GROUP BY b";
215 test_query_builder(actual, expected);
216
217 let actual = table("Foo").select().left_join_as("Bar", "B").group_by("b");
219 let expected = "SELECT * FROM Foo LEFT JOIN Bar AS B GROUP BY b";
220 test_query_builder(actual, expected);
221
222 let actual = table("Foo")
224 .select()
225 .join("Bar")
226 .on("Foo.id = Bar.id")
227 .group_by("b");
228 let expected = "SELECT * FROM Foo JOIN Bar ON Foo.id = Bar.id GROUP BY b";
229 test_query_builder(actual, expected);
230
231 let actual = table("Bar")
233 .select()
234 .filter(col("id").is_null())
235 .group_by("id, (a + name)");
236 let expected = "
237 SELECT * FROM Bar
238 WHERE id IS NULL
239 GROUP BY id, (a + name)
240 ";
241 test_query_builder(actual, expected);
242
243 let actual = table("Player")
245 .select()
246 .join("PlayerItem")
247 .hash_executor("PlayerItem.user_id", "Player.id")
248 .group_by("PlayerItem.category")
249 .build();
250 let expected = {
251 let join = InnerJoinPlan {
252 input: InnerJoinInputPlan::Hash(HashJoinPlan {
253 input: HashJoinInputPlan::Source(SourcePlan::Table(TableSourcePlan {
254 name: "Player".to_owned(),
255 alias: None,
256 access: TableAccessPlan::FullScan,
257 })),
258 right: SourcePlan::Table(TableSourcePlan {
259 name: "PlayerItem".to_owned(),
260 alias: None,
261 access: TableAccessPlan::FullScan,
262 }),
263 input_key: col("Player.id").build_expr_plan().unwrap(),
264 right_key: col("PlayerItem.user_id").build_expr_plan().unwrap(),
265 right_filter: None,
266 }),
267 };
268 let project = ProjectPlan {
269 input: ProjectInputPlan::Aggregation(AggregationPlan {
270 input: AggregationInputPlan::InnerJoin(Box::new(join)),
271 group_by: vec![col("PlayerItem.category").build_expr_plan().unwrap()],
272 aggregate_slots: Vec::new(),
273 }),
274 projection: ProjectionPlan::SelectItems(
275 SelectItemList::from("*").build_select_items_plan().unwrap(),
276 ),
277 };
278
279 Ok(StatementPlan::Query(QueryPlan::Project(project)))
280 };
281 assert_eq!(actual, expected);
282
283 let actual = table("Foo").select().group_by("a").alias_as("Sub").select();
285 let expected = "SELECT * FROM (SELECT * FROM Foo GROUP BY a) Sub";
286 test_query_builder(actual, expected);
287 }
288}