gluesql_core/query_builder/select/
having.rs1use {
2 super::{
3 BuildAggregationInputPlan, BuildAggregationPlan, BuildHavingPlan, BuildProjectInputPlan,
4 BuildSelect, DistinctNode,
5 },
6 crate::{
7 ast::Select,
8 plan::{AggregationPlan, HavingPlan, ProjectInputPlan},
9 query_builder::{
10 ExprNode, FilterNode, GroupByNode, 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 GroupBy(GroupByNode<'a>),
30}
31
32impl PrevNode<'_> {
33 fn build_aggregation_plan(self) -> Result<AggregationPlan> {
34 match self {
35 Self::Select(node) => node.build_aggregation_input_plan(),
36 Self::InnerNestedLoop(node) => node.build_aggregation_input_plan(),
37 Self::LeftOuterNestedLoop(node) => node.build_aggregation_input_plan(),
38 Self::InnerHash(node) => node.build_aggregation_input_plan(),
39 Self::LeftOuterHash(node) => node.build_aggregation_input_plan(),
40 Self::InnerCondition(node) => node.build_aggregation_input_plan(),
41 Self::LeftOuterCondition(node) => node.build_aggregation_input_plan(),
42 Self::Filter(node) => node.build_aggregation_input_plan(),
43 Self::GroupBy(node) => return node.build_aggregation_plan(),
44 }
45 .map(|input| AggregationPlan {
46 input,
47 group_by: Vec::new(),
48 aggregate_slots: Vec::new(),
49 })
50 }
51}
52
53impl BuildSelect for PrevNode<'_> {
54 fn build_select(self) -> Result<Select> {
55 match self {
56 Self::Select(node) => node.build_select(),
57 Self::InnerNestedLoop(node) => node.build_select(),
58 Self::LeftOuterNestedLoop(node) => node.build_select(),
59 Self::InnerHash(node) => node.build_select(),
60 Self::LeftOuterHash(node) => node.build_select(),
61 Self::InnerCondition(node) => node.build_select(),
62 Self::LeftOuterCondition(node) => node.build_select(),
63 Self::Filter(node) => node.build_select(),
64 Self::GroupBy(node) => node.build_select(),
65 }
66 }
67}
68
69impl<'a> From<SelectNode<'a>> for PrevNode<'a> {
70 fn from(node: SelectNode<'a>) -> Self {
71 Self::Select(node)
72 }
73}
74
75impl<'a> From<InnerNestedLoopJoinNode<'a>> for PrevNode<'a> {
76 fn from(node: InnerNestedLoopJoinNode<'a>) -> Self {
77 Self::InnerNestedLoop(Box::new(node))
78 }
79}
80
81impl<'a> From<LeftOuterNestedLoopJoinNode<'a>> for PrevNode<'a> {
82 fn from(node: LeftOuterNestedLoopJoinNode<'a>) -> Self {
83 Self::LeftOuterNestedLoop(Box::new(node))
84 }
85}
86
87impl<'a> From<InnerHashJoinNode<'a>> for PrevNode<'a> {
88 fn from(node: InnerHashJoinNode<'a>) -> Self {
89 Self::InnerHash(Box::new(node))
90 }
91}
92
93impl<'a> From<LeftOuterHashJoinNode<'a>> for PrevNode<'a> {
94 fn from(node: LeftOuterHashJoinNode<'a>) -> Self {
95 Self::LeftOuterHash(Box::new(node))
96 }
97}
98
99impl<'a> From<InnerJoinConditionNode<'a>> for PrevNode<'a> {
100 fn from(node: InnerJoinConditionNode<'a>) -> Self {
101 Self::InnerCondition(Box::new(node))
102 }
103}
104
105impl<'a> From<LeftOuterJoinConditionNode<'a>> for PrevNode<'a> {
106 fn from(node: LeftOuterJoinConditionNode<'a>) -> Self {
107 Self::LeftOuterCondition(Box::new(node))
108 }
109}
110
111impl<'a> From<FilterNode<'a>> for PrevNode<'a> {
112 fn from(node: FilterNode<'a>) -> Self {
113 Self::Filter(node)
114 }
115}
116
117impl<'a> From<GroupByNode<'a>> for PrevNode<'a> {
118 fn from(node: GroupByNode<'a>) -> Self {
119 Self::GroupBy(node)
120 }
121}
122
123#[derive(Clone, Debug)]
124pub struct HavingNode<'a> {
125 prev_node: PrevNode<'a>,
126 expr: ExprNode<'a>,
127}
128
129impl<'a> HavingNode<'a> {
130 pub(super) fn new<N: Into<PrevNode<'a>>, T: Into<ExprNode<'a>>>(prev_node: N, expr: T) -> Self {
131 Self {
132 prev_node: prev_node.into(),
133 expr: expr.into(),
134 }
135 }
136
137 pub fn offset<T: Into<ExprNode<'a>>>(self, expr: T) -> OffsetNode<'a> {
138 OffsetNode::new(self, expr)
139 }
140
141 pub fn limit<T: Into<ExprNode<'a>>>(self, expr: T) -> LimitNode<'a> {
142 LimitNode::new(self, expr)
143 }
144
145 pub fn project<T: Into<SelectItemList<'a>>>(self, select_items: T) -> ProjectNode<'a> {
146 ProjectNode::new(self, select_items)
147 }
148
149 pub fn order_by<T: Into<OrderByExprList<'a>>>(self, expr_list: T) -> SelectOrderByNode<'a> {
150 SelectOrderByNode::new(self, expr_list)
151 }
152
153 pub fn distinct(self) -> DistinctNode<'a> {
154 DistinctNode::new(self)
155 }
156
157 pub fn alias_as(self, table_alias: &'a str) -> SourceNode<'a> {
158 QueryNode::HavingNode(self).alias_as(table_alias)
159 }
160}
161
162impl BuildHavingPlan for HavingNode<'_> {
163 fn build_having_plan(self) -> Result<HavingPlan> {
164 Ok(HavingPlan {
165 input: self.prev_node.build_aggregation_plan()?,
166 expr: self.expr.build_expr_plan()?,
167 })
168 }
169}
170
171impl BuildProjectInputPlan for HavingNode<'_> {
172 fn build_project_input_plan(self) -> Result<ProjectInputPlan> {
173 self.build_having_plan().map(ProjectInputPlan::Having)
174 }
175}
176
177impl BuildSelect for HavingNode<'_> {
178 fn build_select(self) -> Result<Select> {
179 let mut select = self.prev_node.build_select()?;
180 select.having = Some(self.expr.build_expr()?);
181
182 Ok(select)
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use {
189 crate::{
190 data::Value,
191 plan::{
192 AggregationInputPlan, AggregationPlan, ExprPlan, HashJoinInputPlan, HashJoinPlan,
193 HavingPlan, InnerJoinInputPlan, InnerJoinPlan, ProjectInputPlan, ProjectPlan,
194 ProjectionPlan, QueryPlan, SelectItemPlan, SourcePlan, StatementPlan,
195 TableAccessPlan, TableSourcePlan,
196 },
197 query_builder::{
198 Build, QueryBuilderError, select::BuildQuery, table, test_query_builder,
199 },
200 result::Error,
201 },
202 pretty_assertions::assert_eq,
203 };
204
205 #[test]
206 fn prev_nodes() {
207 let actual = table("Foo").select().having("TRUE");
208 let expected = "SELECT * FROM Foo HAVING TRUE";
209 test_query_builder(actual, expected);
210
211 let actual = table("Foo").select().join("Bar").having("TRUE");
212 let expected = "SELECT * FROM Foo JOIN Bar HAVING TRUE";
213 test_query_builder(actual, expected);
214
215 let actual = table("Foo")
216 .select()
217 .join("Bar")
218 .on("Foo.id = Bar.id")
219 .having("TRUE");
220 let expected = "SELECT * FROM Foo JOIN Bar ON Foo.id = Bar.id HAVING TRUE";
221 test_query_builder(actual, expected);
222
223 let actual = table("Foo").select().filter("id > 1").having("TRUE");
224 let expected = "SELECT * FROM Foo WHERE id > 1 HAVING TRUE";
225 test_query_builder(actual, expected);
226 }
227
228 #[test]
229 fn plan_only_hash_join() {
230 let actual = table("Foo")
231 .select()
232 .join("Bar")
233 .hash_executor("Foo.id", "Bar.id")
234 .having("TRUE")
235 .build_query()
236 .map(|_| ());
237 let expected = Err(Error::QueryBuilder(
238 QueryBuilderError::HashJoinExecutorRequiresPlan,
239 ));
240 assert_eq!(actual, expected);
241
242 let actual = table("Foo")
243 .select()
244 .join("Bar")
245 .hash_executor("Foo.id", "Bar.id")
246 .having("TRUE")
247 .build();
248 let expected = Ok(StatementPlan::Query(QueryPlan::Project(ProjectPlan {
249 input: ProjectInputPlan::Having(HavingPlan {
250 input: AggregationPlan {
251 input: AggregationInputPlan::InnerJoin(Box::new(InnerJoinPlan {
252 input: InnerJoinInputPlan::Hash(HashJoinPlan {
253 input: HashJoinInputPlan::Source(SourcePlan::Table(TableSourcePlan {
254 name: "Foo".to_owned(),
255 alias: None,
256 access: TableAccessPlan::FullScan,
257 })),
258 right: SourcePlan::Table(TableSourcePlan {
259 name: "Bar".to_owned(),
260 alias: None,
261 access: TableAccessPlan::FullScan,
262 }),
263 input_key: ExprPlan::CompoundIdentifier {
264 alias: "Bar".to_owned(),
265 ident: "id".to_owned(),
266 },
267 right_key: ExprPlan::CompoundIdentifier {
268 alias: "Foo".to_owned(),
269 ident: "id".to_owned(),
270 },
271 right_filter: None,
272 }),
273 })),
274 group_by: Vec::new(),
275 aggregate_slots: Vec::new(),
276 },
277 expr: ExprPlan::Value(Value::Bool(true)),
278 }),
279 projection: ProjectionPlan::SelectItems(vec![SelectItemPlan::Wildcard]),
280 })));
281
282 assert_eq!(actual, expected);
283 }
284
285 #[test]
286 fn having() {
287 let actual = table("Bar")
289 .select()
290 .filter("id IS NULL")
291 .group_by("id, (a + name)")
292 .having("COUNT(id) > 10")
293 .offset(10);
294 let expected = "
295 SELECT * FROM Bar
296 WHERE id IS NULL
297 GROUP BY id, (a + name)
298 HAVING COUNT(id) > 10
299 OFFSET 10
300 ";
301 test_query_builder(actual, expected);
302
303 let actual = table("Bar")
305 .select()
306 .filter("id IS NULL")
307 .group_by("id, (a + name)")
308 .having("COUNT(id) > 10")
309 .limit(10);
310 let expected = "
311 SELECT * FROM Bar
312 WHERE id IS NULL
313 GROUP BY id, (a + name)
314 HAVING COUNT(id) > 10
315 LIMIT 10
316 ";
317 test_query_builder(actual, expected);
318
319 let actual = table("Bar")
321 .select()
322 .filter("id IS NULL")
323 .group_by("id, (a + name)")
324 .having("COUNT(id) > 10")
325 .project(vec!["id", "(a + name) AS b", "COUNT(id) AS c"]);
326 let expected = "
327 SELECT id, (a + name) AS b, COUNT(id) AS c
328 FROM Bar
329 WHERE id IS NULL
330 GROUP BY id, (a + name)
331 HAVING COUNT(id) > 10
332 ";
333 test_query_builder(actual, expected);
334
335 let actual = table("Bar")
337 .select()
338 .filter("id IS NULL")
339 .group_by("id, (a + name)")
340 .having("COUNT(id) > 10");
341 let expected = "
342 SELECT * FROM Bar
343 WHERE id IS NULL
344 GROUP BY id, (a + name)
345 HAVING COUNT(id) > 10
346 ";
347 test_query_builder(actual, expected);
348
349 let actual = table("Foo")
351 .select()
352 .group_by("a")
353 .having("a > 1")
354 .alias_as("Sub")
355 .select();
356 let expected = "SELECT * FROM (SELECT * FROM Foo GROUP BY a HAVING a > 1) Sub";
357 test_query_builder(actual, expected);
358 }
359}