gluesql_core/ast_builder/select/
having.rs1use {
2 super::Prebuild,
3 crate::{
4 ast::Select,
5 ast_builder::{
6 ExprNode, GroupByNode, LimitNode, OffsetNode, OrderByExprList, OrderByNode,
7 ProjectNode, QueryNode, SelectItemList, TableFactorNode,
8 },
9 result::Result,
10 },
11};
12
13#[derive(Clone, Debug)]
14pub enum PrevNode<'a> {
15 GroupBy(GroupByNode<'a>),
16}
17
18impl<'a> Prebuild<Select> for PrevNode<'a> {
19 fn prebuild(self) -> Result<Select> {
20 match self {
21 Self::GroupBy(node) => node.prebuild(),
22 }
23 }
24}
25
26impl<'a> From<GroupByNode<'a>> for PrevNode<'a> {
27 fn from(node: GroupByNode<'a>) -> Self {
28 PrevNode::GroupBy(node)
29 }
30}
31
32#[derive(Clone, Debug)]
33pub struct HavingNode<'a> {
34 prev_node: PrevNode<'a>,
35 expr: ExprNode<'a>,
36}
37
38impl<'a> HavingNode<'a> {
39 pub fn new<N: Into<PrevNode<'a>>, T: Into<ExprNode<'a>>>(prev_node: N, expr: T) -> Self {
40 Self {
41 prev_node: prev_node.into(),
42 expr: expr.into(),
43 }
44 }
45
46 pub fn offset<T: Into<ExprNode<'a>>>(self, expr: T) -> OffsetNode<'a> {
47 OffsetNode::new(self, expr)
48 }
49
50 pub fn limit<T: Into<ExprNode<'a>>>(self, expr: T) -> LimitNode<'a> {
51 LimitNode::new(self, expr)
52 }
53
54 pub fn project<T: Into<SelectItemList<'a>>>(self, select_items: T) -> ProjectNode<'a> {
55 ProjectNode::new(self, select_items)
56 }
57
58 pub fn order_by<T: Into<OrderByExprList<'a>>>(self, expr_list: T) -> OrderByNode<'a> {
59 OrderByNode::new(self, expr_list)
60 }
61
62 pub fn alias_as(self, table_alias: &'a str) -> TableFactorNode<'a> {
63 QueryNode::HavingNode(self).alias_as(table_alias)
64 }
65}
66
67impl<'a> Prebuild<Select> for HavingNode<'a> {
68 fn prebuild(self) -> Result<Select> {
69 let mut select: Select = self.prev_node.prebuild()?;
70 select.having = Some(self.expr.try_into()?);
71
72 Ok(select)
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use crate::ast_builder::{Build, table, test};
79
80 #[test]
81 fn having() {
82 let actual = table("Bar")
84 .select()
85 .filter("id IS NULL")
86 .group_by("id, (a + name)")
87 .having("COUNT(id) > 10")
88 .offset(10)
89 .build();
90 let expected = "
91 SELECT * FROM Bar
92 WHERE id IS NULL
93 GROUP BY id, (a + name)
94 HAVING COUNT(id) > 10
95 OFFSET 10
96 ";
97 test(actual, expected);
98
99 let actual = table("Bar")
101 .select()
102 .filter("id IS NULL")
103 .group_by("id, (a + name)")
104 .having("COUNT(id) > 10")
105 .limit(10)
106 .build();
107 let expected = "
108 SELECT * FROM Bar
109 WHERE id IS NULL
110 GROUP BY id, (a + name)
111 HAVING COUNT(id) > 10
112 LIMIT 10
113 ";
114 test(actual, expected);
115
116 let actual = table("Bar")
118 .select()
119 .filter("id IS NULL")
120 .group_by("id, (a + name)")
121 .having("COUNT(id) > 10")
122 .project(vec!["id", "(a + name) AS b", "COUNT(id) AS c"])
123 .build();
124 let expected = "
125 SELECT id, (a + name) AS b, COUNT(id) AS c
126 FROM Bar
127 WHERE id IS NULL
128 GROUP BY id, (a + name)
129 HAVING COUNT(id) > 10
130 ";
131 test(actual, expected);
132
133 let actual = table("Bar")
135 .select()
136 .filter("id IS NULL")
137 .group_by("id, (a + name)")
138 .having("COUNT(id) > 10")
139 .build();
140 let expected = "
141 SELECT * FROM Bar
142 WHERE id IS NULL
143 GROUP BY id, (a + name)
144 HAVING COUNT(id) > 10
145 ";
146 test(actual, expected);
147
148 let actual = table("Foo")
150 .select()
151 .group_by("a")
152 .having("a > 1")
153 .alias_as("Sub")
154 .select()
155 .build();
156 let expected = "SELECT * FROM (SELECT * FROM Foo GROUP BY a HAVING a > 1) Sub";
157 test(actual, expected);
158 }
159}