gluesql_core/ast_builder/
query.rs1use {
2 super::{
3 ExprList, FilterNode, GroupByNode, HashJoinNode, HavingNode, JoinConstraintNode, JoinNode,
4 LimitNode, OffsetLimitNode, OffsetNode, OrderByNode, ProjectNode, SelectNode,
5 TableFactorNode,
6 select::{Prebuild, ValuesNode},
7 table_factor::TableType,
8 },
9 crate::{
10 ast::{Expr, Query, SetExpr, Values},
11 parse_sql::parse_query,
12 result::{Error, Result},
13 translate::translate_query,
14 },
15};
16
17#[derive(Clone, Debug)]
18pub enum QueryNode<'a> {
19 Text(String),
20 Values(Vec<ExprList<'a>>),
21 SelectNode(SelectNode<'a>),
22 ValuesNode(ValuesNode<'a>),
23 JoinNode(JoinNode<'a>),
24 JoinConstraintNode(JoinConstraintNode<'a>),
25 HashJoinNode(HashJoinNode<'a>),
26 GroupByNode(GroupByNode<'a>),
27 HavingNode(HavingNode<'a>),
28 LimitNode(LimitNode<'a>),
29 OffsetNode(OffsetNode<'a>),
30 OffsetLimitNode(OffsetLimitNode<'a>),
31 FilterNode(FilterNode<'a>),
32 ProjectNode(ProjectNode<'a>),
33 OrderByNode(OrderByNode<'a>),
34}
35
36impl<'a> QueryNode<'a> {
37 pub fn alias_as(self, table_alias: &'a str) -> TableFactorNode<'a> {
38 TableFactorNode {
39 table_name: table_alias.to_owned(),
40 table_type: TableType::Derived {
41 subquery: Box::new(self),
42 alias: table_alias.to_owned(),
43 },
44 table_alias: None,
45 index: None,
46 }
47 }
48}
49
50impl<'a> From<&str> for QueryNode<'a> {
51 fn from(query: &str) -> Self {
52 Self::Text(query.to_owned())
53 }
54}
55
56impl<'a> From<SelectNode<'a>> for QueryNode<'a> {
57 fn from(node: SelectNode<'a>) -> Self {
58 QueryNode::SelectNode(node)
59 }
60}
61
62macro_rules! impl_from_select_nodes {
63 ($type: ident) => {
64 impl<'a> From<$type<'a>> for QueryNode<'a> {
65 fn from(node: $type<'a>) -> Self {
66 QueryNode::$type(node)
67 }
68 }
69 };
70}
71
72impl_from_select_nodes!(JoinNode);
73impl_from_select_nodes!(JoinConstraintNode);
74impl_from_select_nodes!(HashJoinNode);
75impl_from_select_nodes!(GroupByNode);
76impl_from_select_nodes!(HavingNode);
77impl_from_select_nodes!(FilterNode);
78impl_from_select_nodes!(LimitNode);
79impl_from_select_nodes!(OffsetNode);
80impl_from_select_nodes!(OffsetLimitNode);
81impl_from_select_nodes!(ProjectNode);
82impl_from_select_nodes!(OrderByNode);
83
84impl<'a> TryFrom<QueryNode<'a>> for Query {
85 type Error = Error;
86
87 fn try_from(query_node: QueryNode<'a>) -> Result<Self> {
88 match query_node {
89 QueryNode::Text(query_node) => {
90 parse_query(query_node).and_then(|item| translate_query(&item))
91 }
92 QueryNode::Values(values) => {
93 let values: Vec<Vec<Expr>> = values
94 .into_iter()
95 .map(TryInto::try_into)
96 .collect::<Result<Vec<_>>>()?;
97
98 Ok(Query {
99 body: SetExpr::Values(Values(values)),
100 order_by: Vec::new(),
101 limit: None,
102 offset: None,
103 })
104 }
105 QueryNode::SelectNode(node) => node.prebuild(),
106 QueryNode::ValuesNode(node) => node.prebuild(),
107 QueryNode::JoinNode(node) => node.prebuild(),
108 QueryNode::JoinConstraintNode(node) => node.prebuild(),
109 QueryNode::HashJoinNode(node) => node.prebuild(),
110 QueryNode::GroupByNode(node) => node.prebuild(),
111 QueryNode::HavingNode(node) => node.prebuild(),
112 QueryNode::FilterNode(node) => node.prebuild(),
113 QueryNode::LimitNode(node) => node.prebuild(),
114 QueryNode::OffsetNode(node) => node.prebuild(),
115 QueryNode::OffsetLimitNode(node) => node.prebuild(),
116 QueryNode::ProjectNode(node) => node.prebuild(),
117 QueryNode::OrderByNode(node) => node.prebuild(),
118 }
119 }
120}
121
122#[cfg(test)]
123mod test {
124 use {
125 super::QueryNode,
126 crate::{
127 ast::{
128 Join, JoinConstraint, JoinExecutor, JoinOperator, Query, Select, SetExpr,
129 TableFactor, TableWithJoins,
130 },
131 ast_builder::{
132 SelectItemList, col, glue_indexes, glue_objects, glue_table_columns, glue_tables,
133 series, table, test_query,
134 },
135 },
136 pretty_assertions::assert_eq,
137 };
138
139 #[test]
140 fn query() {
141 let actual = table("FOO").select().into();
142 let expected = "SELECT * FROM FOO";
143 test_query(actual, expected);
144
145 let actual = table("Bar").select().join("Foo").into();
146 let expected = "SELECT * FROM Bar JOIN Foo";
147 test_query(actual, expected);
148
149 let actual = table("Bar")
150 .select()
151 .join("Foo")
152 .on("Foo.id = Bar.foo_id")
153 .into();
154 let expected = "SELECT * FROM Bar JOIN Foo ON Foo.id = Bar.foo_id";
155 test_query(actual, expected);
156
157 let actual: QueryNode = table("Player")
158 .select()
159 .join("PlayerItem")
160 .hash_executor("PlayerItem.user_id", "Player.id")
161 .into();
162 let expected = {
163 let join = Join {
164 relation: TableFactor::Table {
165 name: "PlayerItem".to_owned(),
166 alias: None,
167 index: None,
168 },
169 join_operator: JoinOperator::Inner(JoinConstraint::None),
170 join_executor: JoinExecutor::Hash {
171 key_expr: col("PlayerItem.user_id").try_into().unwrap(),
172 value_expr: col("Player.id").try_into().unwrap(),
173 where_clause: None,
174 },
175 };
176 let select = Select {
177 distinct: false,
178 projection: SelectItemList::from("*").try_into().unwrap(),
179 from: TableWithJoins {
180 relation: TableFactor::Table {
181 name: "Player".to_owned(),
182 alias: None,
183 index: None,
184 },
185 joins: vec![join],
186 },
187 selection: None,
188 group_by: Vec::new(),
189 having: None,
190 };
191
192 Query {
193 body: SetExpr::Select(Box::new(select)),
194 order_by: Vec::new(),
195 limit: None,
196 offset: None,
197 }
198 };
199 assert_eq!(Query::try_from(actual).unwrap(), expected);
200
201 let actual = table("FOO").select().group_by("id").into();
202 let expected = "SELECT * FROM FOO GROUP BY id";
203 test_query(actual, expected);
204
205 let actual = table("FOO")
206 .select()
207 .group_by("id")
208 .having("COUNT(id) > 10")
209 .into();
210 let expected = "SELECT * FROM FOO GROUP BY id HAVING COUNT(id) > 10";
211 test_query(actual, expected);
212
213 let actual = table("FOO")
214 .select()
215 .group_by("city")
216 .having("COUNT(name) < 100")
217 .limit(3)
218 .into();
219 let expected = "SELECT * FROM FOO GROUP BY city HAVING COUNT(name) < 100 LIMIT 3";
220 test_query(actual, expected);
221
222 let actual = table("FOO").select().offset(10).into();
223 let expected = "SELECT * FROM FOO OFFSET 10";
224 test_query(actual, expected);
225
226 let actual = table("FOO")
227 .select()
228 .group_by("city")
229 .having("COUNT(name) < 100")
230 .offset(1)
231 .limit(3)
232 .into();
233 let expected = "SELECT * FROM FOO GROUP BY city HAVING COUNT(name) < 100 OFFSET 1 LIMIT 3";
234 test_query(actual, expected);
235
236 let actual = table("FOO").select().project("id, name").limit(10).into();
237 let expected = r#"SELECT id, name FROM FOO LIMIT 10"#;
238 test_query(actual, expected);
239
240 let actual = table("Foo").select().order_by("score DESC").into();
241 let expected = "SELECT * FROM Foo ORDER BY score DESC";
242 test_query(actual, expected);
243
244 let actual = glue_objects().select().into();
245 let expected = "SELECT * FROM GLUE_OBJECTS";
246 test_query(actual, expected);
247
248 let actual = glue_tables().select().into();
249 let expected = "SELECT * FROM GLUE_TABLES";
250 test_query(actual, expected);
251
252 let actual = glue_indexes().select().into();
253 let expected = "SELECT * FROM GLUE_INDEXES";
254 test_query(actual, expected);
255
256 let actual = glue_table_columns().select().into();
257 let expected = "SELECT * FROM GLUE_TABLE_COLUMNS";
258 test_query(actual, expected);
259
260 let actual = series("1 + 2").select().into();
261 let expected = "SELECT * FROM SERIES(1 + 2)";
262 test_query(actual, expected);
263
264 let actual = table("Items").select().alias_as("Sub").select().into();
265 let expected = "SELECT * FROM (SELECT * FROM Items) AS Sub";
266 test_query(actual, expected);
267 }
268}