Skip to main content

gluesql_core/query_builder/
query.rs

1use {
2    super::{
3        DistinctNode, ExprList, FilterNode, GroupByNode, HavingNode, InnerHashJoinNode,
4        InnerJoinConditionNode, InnerNestedLoopJoinNode, LeftOuterHashJoinNode,
5        LeftOuterJoinConditionNode, LeftOuterNestedLoopJoinNode, LimitNode, OffsetLimitNode,
6        OffsetNode, ProjectNode, SelectNode, SelectOrderByNode, SourceNode, ValuesOrderByNode,
7        select::{BuildQuery, BuildQueryPlan, ValuesNode},
8    },
9    crate::{
10        ast::{Query, SetExpr, Values},
11        parse_sql::parse_query,
12        plan::{QueryPlan, ValuesPlan},
13        result::Result,
14        translate::{NO_PARAMS, translate_query},
15    },
16};
17
18#[derive(Clone, Debug)]
19pub enum QueryNode<'a> {
20    Text(String),
21    Values(Vec<ExprList<'a>>),
22    SelectNode(SelectNode<'a>),
23    ValuesNode(ValuesNode<'a>),
24    InnerNestedLoopJoinNode(InnerNestedLoopJoinNode<'a>),
25    LeftOuterNestedLoopJoinNode(LeftOuterNestedLoopJoinNode<'a>),
26    InnerHashJoinNode(InnerHashJoinNode<'a>),
27    LeftOuterHashJoinNode(LeftOuterHashJoinNode<'a>),
28    InnerJoinConditionNode(InnerJoinConditionNode<'a>),
29    LeftOuterJoinConditionNode(LeftOuterJoinConditionNode<'a>),
30    GroupByNode(GroupByNode<'a>),
31    HavingNode(HavingNode<'a>),
32    LimitNode(LimitNode<'a>),
33    OffsetNode(OffsetNode<'a>),
34    OffsetLimitNode(OffsetLimitNode<'a>),
35    FilterNode(FilterNode<'a>),
36    ProjectNode(ProjectNode<'a>),
37    SelectOrderByNode(SelectOrderByNode<'a>),
38    ValuesOrderByNode(ValuesOrderByNode<'a>),
39    DistinctNode(DistinctNode<'a>),
40}
41
42impl<'a> QueryNode<'a> {
43    pub fn alias_as(self, table_alias: &'a str) -> SourceNode<'a> {
44        SourceNode::Derived {
45            query: Box::new(self),
46            alias: table_alias.to_owned(),
47        }
48    }
49
50    pub(super) fn build_query(self) -> Result<Query> {
51        match self {
52            QueryNode::Text(query_node) => {
53                parse_query(query_node).and_then(|item| translate_query(&item, NO_PARAMS))
54            }
55            QueryNode::Values(values) => {
56                let values = values
57                    .into_iter()
58                    .map(ExprList::build_exprs)
59                    .collect::<Result<Vec<_>>>()?;
60
61                Ok(Query {
62                    body: SetExpr::Values(Values(values)),
63                    order_by: Vec::new(),
64                    limit: None,
65                    offset: None,
66                })
67            }
68            QueryNode::SelectNode(node) => node.build_query(),
69            QueryNode::ValuesNode(node) => node.build_query(),
70            QueryNode::InnerNestedLoopJoinNode(node) => node.build_query(),
71            QueryNode::LeftOuterNestedLoopJoinNode(node) => node.build_query(),
72            QueryNode::InnerHashJoinNode(node) => node.build_query(),
73            QueryNode::LeftOuterHashJoinNode(node) => node.build_query(),
74            QueryNode::InnerJoinConditionNode(node) => node.build_query(),
75            QueryNode::LeftOuterJoinConditionNode(node) => node.build_query(),
76            QueryNode::GroupByNode(node) => node.build_query(),
77            QueryNode::HavingNode(node) => node.build_query(),
78            QueryNode::FilterNode(node) => node.build_query(),
79            QueryNode::LimitNode(node) => node.build_query(),
80            QueryNode::OffsetNode(node) => node.build_query(),
81            QueryNode::OffsetLimitNode(node) => node.build_query(),
82            QueryNode::ProjectNode(node) => node.build_query(),
83            QueryNode::SelectOrderByNode(node) => node.build_query(),
84            QueryNode::ValuesOrderByNode(node) => node.build_query(),
85            QueryNode::DistinctNode(node) => node.build_query(),
86        }
87    }
88
89    pub(super) fn build_query_plan(self) -> Result<QueryPlan> {
90        match self {
91            QueryNode::Text(query_node) => parse_query(query_node)
92                .and_then(|item| translate_query(&item, NO_PARAMS).map(Into::into)),
93            QueryNode::Values(values) => {
94                let values = values
95                    .into_iter()
96                    .map(ExprList::build_exprs_plan)
97                    .collect::<Result<Vec<_>>>()?;
98
99                Ok(QueryPlan::Values(ValuesPlan(values)))
100            }
101            QueryNode::SelectNode(node) => node.build_query_plan(),
102            QueryNode::ValuesNode(node) => node.build_query_plan(),
103            QueryNode::InnerNestedLoopJoinNode(node) => node.build_query_plan(),
104            QueryNode::LeftOuterNestedLoopJoinNode(node) => node.build_query_plan(),
105            QueryNode::InnerHashJoinNode(node) => node.build_query_plan(),
106            QueryNode::LeftOuterHashJoinNode(node) => node.build_query_plan(),
107            QueryNode::InnerJoinConditionNode(node) => node.build_query_plan(),
108            QueryNode::LeftOuterJoinConditionNode(node) => node.build_query_plan(),
109            QueryNode::GroupByNode(node) => node.build_query_plan(),
110            QueryNode::HavingNode(node) => node.build_query_plan(),
111            QueryNode::FilterNode(node) => node.build_query_plan(),
112            QueryNode::LimitNode(node) => node.build_query_plan(),
113            QueryNode::OffsetNode(node) => node.build_query_plan(),
114            QueryNode::OffsetLimitNode(node) => node.build_query_plan(),
115            QueryNode::ProjectNode(node) => node.build_query_plan(),
116            QueryNode::SelectOrderByNode(node) => node.build_query_plan(),
117            QueryNode::ValuesOrderByNode(node) => node.build_query_plan(),
118            QueryNode::DistinctNode(node) => node.build_query_plan(),
119        }
120    }
121}
122
123impl From<&str> for QueryNode<'_> {
124    fn from(query: &str) -> Self {
125        Self::Text(query.to_owned())
126    }
127}
128
129impl<'a> From<SelectNode<'a>> for QueryNode<'a> {
130    fn from(node: SelectNode<'a>) -> Self {
131        QueryNode::SelectNode(node)
132    }
133}
134
135macro_rules! impl_from_select_nodes {
136    ($type: ident) => {
137        impl<'a> From<$type<'a>> for QueryNode<'a> {
138            fn from(node: $type<'a>) -> Self {
139                QueryNode::$type(node)
140            }
141        }
142    };
143}
144
145impl_from_select_nodes!(InnerNestedLoopJoinNode);
146impl_from_select_nodes!(LeftOuterNestedLoopJoinNode);
147impl_from_select_nodes!(InnerHashJoinNode);
148impl_from_select_nodes!(LeftOuterHashJoinNode);
149impl_from_select_nodes!(InnerJoinConditionNode);
150impl_from_select_nodes!(LeftOuterJoinConditionNode);
151impl_from_select_nodes!(GroupByNode);
152impl_from_select_nodes!(HavingNode);
153impl_from_select_nodes!(FilterNode);
154impl_from_select_nodes!(LimitNode);
155impl_from_select_nodes!(OffsetNode);
156impl_from_select_nodes!(OffsetLimitNode);
157impl_from_select_nodes!(ProjectNode);
158impl_from_select_nodes!(SelectOrderByNode);
159impl_from_select_nodes!(ValuesOrderByNode);
160impl_from_select_nodes!(DistinctNode);
161
162#[cfg(test)]
163mod test {
164    use {
165        super::QueryNode,
166        crate::{
167            plan::{
168                HashJoinInputPlan, HashJoinPlan, InnerJoinInputPlan, InnerJoinPlan,
169                ProjectInputPlan, ProjectPlan, ProjectionPlan, QueryPlan, SourcePlan,
170                TableAccessPlan, TableSourcePlan,
171            },
172            query_builder::{
173                SelectItemList, col, glue_indexes, glue_objects, glue_table_columns, glue_tables,
174                series, table, test_query, test_query_builder, values,
175            },
176        },
177        pretty_assertions::assert_eq,
178    };
179
180    #[test]
181    fn query() {
182        let actual = QueryNode::Values(vec!["1, 'a'".into(), "2, 'b'".into()]);
183        let expected = "VALUES(1, 'a'), (2, 'b')";
184        test_query(actual, expected);
185
186        let actual = table("FOO").select().into();
187        let expected = "SELECT * FROM FOO";
188        test_query(actual, expected);
189
190        let actual = table("Bar").select().join("Foo").into();
191        let expected = "SELECT * FROM Bar JOIN Foo";
192        test_query(actual, expected);
193
194        let actual = table("Bar")
195            .select()
196            .join("Foo")
197            .on("Foo.id = Bar.foo_id")
198            .into();
199        let expected = "SELECT * FROM Bar JOIN Foo ON Foo.id = Bar.foo_id";
200        test_query(actual, expected);
201
202        let actual = QueryNode::from(
203            table("Player")
204                .select()
205                .join("PlayerItem")
206                .hash_executor("PlayerItem.user_id", "Player.id"),
207        )
208        .build_query_plan()
209        .unwrap();
210        let expected = {
211            let join = InnerJoinPlan {
212                input: InnerJoinInputPlan::Hash(HashJoinPlan {
213                    input: HashJoinInputPlan::Source(SourcePlan::Table(TableSourcePlan {
214                        name: "Player".to_owned(),
215                        alias: None,
216                        access: TableAccessPlan::FullScan,
217                    })),
218                    right: SourcePlan::Table(TableSourcePlan {
219                        name: "PlayerItem".to_owned(),
220                        alias: None,
221                        access: TableAccessPlan::FullScan,
222                    }),
223                    input_key: col("Player.id").build_expr_plan().unwrap(),
224                    right_key: col("PlayerItem.user_id").build_expr_plan().unwrap(),
225                    right_filter: None,
226                }),
227            };
228            let project = ProjectPlan {
229                input: ProjectInputPlan::InnerJoin(Box::new(join)),
230                projection: ProjectionPlan::SelectItems(
231                    SelectItemList::from("*").build_select_items_plan().unwrap(),
232                ),
233            };
234
235            QueryPlan::Project(project)
236        };
237        assert_eq!(actual, expected);
238
239        let actual = table("FOO").select().group_by("id").into();
240        let expected = "SELECT * FROM FOO GROUP BY id";
241        test_query(actual, expected);
242
243        let actual = table("FOO")
244            .select()
245            .group_by("id")
246            .having("COUNT(id) > 10")
247            .into();
248        let expected = "SELECT * FROM FOO GROUP BY id HAVING COUNT(id) > 10";
249        test_query(actual, expected);
250
251        let actual = table("FOO")
252            .select()
253            .group_by("city")
254            .having("COUNT(name) < 100")
255            .limit(3)
256            .into();
257        let expected = "SELECT * FROM FOO GROUP BY city HAVING COUNT(name) < 100 LIMIT 3";
258        test_query(actual, expected);
259
260        let actual = table("FOO").select().offset(10).into();
261        let expected = "SELECT * FROM FOO OFFSET 10";
262        test_query(actual, expected);
263
264        let actual = table("FOO")
265            .select()
266            .group_by("city")
267            .having("COUNT(name) < 100")
268            .offset(1)
269            .limit(3)
270            .into();
271        let expected = "SELECT * FROM FOO GROUP BY city HAVING COUNT(name) < 100 OFFSET 1 LIMIT 3";
272        test_query(actual, expected);
273
274        let actual = table("FOO").select().project("id, name").limit(10).into();
275        let expected = r"SELECT id, name FROM FOO LIMIT 10";
276        test_query(actual, expected);
277
278        let actual = table("Foo").select().order_by("score DESC").into();
279        let expected = "SELECT * FROM Foo ORDER BY score DESC";
280        test_query(actual, expected);
281
282        let actual = table("Foo")
283            .select()
284            .project("id")
285            .order_by("id")
286            .distinct()
287            .into();
288        let expected = "SELECT DISTINCT id FROM Foo ORDER BY id";
289        test_query(actual, expected);
290
291        let actual = glue_objects().select().into();
292        let expected = "SELECT * FROM GLUE_OBJECTS";
293        test_query(actual, expected);
294
295        let actual = glue_tables().select().into();
296        let expected = "SELECT * FROM GLUE_TABLES";
297        test_query(actual, expected);
298
299        let actual = glue_indexes().select().into();
300        let expected = "SELECT * FROM GLUE_INDEXES";
301        test_query(actual, expected);
302
303        let actual = glue_table_columns().select().into();
304        let expected = "SELECT * FROM GLUE_TABLE_COLUMNS";
305        test_query(actual, expected);
306
307        let actual = series("1 + 2").select().into();
308        let expected = "SELECT * FROM SERIES(1 + 2)";
309        test_query(actual, expected);
310
311        let actual = table("Items").select().alias_as("Sub").select().into();
312        let expected = "SELECT * FROM (SELECT * FROM Items) AS Sub";
313        test_query(actual, expected);
314    }
315
316    #[test]
317    fn select_distinct_builds_after_order_by() {
318        let actual = table("Item").select().distinct();
319        test_query_builder(actual, "SELECT DISTINCT * FROM Item");
320
321        let actual = table("Item").select().order_by("id").distinct();
322        test_query_builder(actual, "SELECT DISTINCT * FROM Item ORDER BY id");
323
324        let actual = table("Item").select().distinct().offset(2);
325        test_query_builder(actual, "SELECT DISTINCT * FROM Item OFFSET 2");
326
327        let actual = table("Item").select().order_by("id").distinct().offset(2);
328        test_query_builder(actual, "SELECT DISTINCT * FROM Item ORDER BY id OFFSET 2");
329
330        let actual = table("Item").select().distinct().limit(3);
331        test_query_builder(actual, "SELECT DISTINCT * FROM Item LIMIT 3");
332
333        let actual = table("Item").select().order_by("id").distinct().limit(3);
334        test_query_builder(actual, "SELECT DISTINCT * FROM Item ORDER BY id LIMIT 3");
335
336        let actual = table("Item").select().distinct().offset(2).limit(3);
337        test_query_builder(actual, "SELECT DISTINCT * FROM Item OFFSET 2 LIMIT 3");
338
339        let actual = table("Item")
340            .select()
341            .order_by("id")
342            .distinct()
343            .offset(2)
344            .limit(3);
345        test_query_builder(
346            actual,
347            "SELECT DISTINCT * FROM Item ORDER BY id OFFSET 2 LIMIT 3",
348        );
349    }
350
351    #[test]
352    fn query_builder_builds_only_valid_terminal_stage_relations() {
353        let actual = table("Foo").select();
354        test_query_builder(actual, "SELECT * FROM Foo");
355
356        let actual = table("Foo").select().order_by("id");
357        test_query_builder(actual, "SELECT * FROM Foo ORDER BY id");
358
359        let actual = table("Foo").select().offset(2);
360        test_query_builder(actual, "SELECT * FROM Foo OFFSET 2");
361
362        let actual = table("Foo").select().order_by("id").offset(2);
363        test_query_builder(actual, "SELECT * FROM Foo ORDER BY id OFFSET 2");
364
365        let actual = table("Foo").select().limit(3);
366        test_query_builder(actual, "SELECT * FROM Foo LIMIT 3");
367
368        let actual = table("Foo").select().order_by("id").limit(3);
369        test_query_builder(actual, "SELECT * FROM Foo ORDER BY id LIMIT 3");
370
371        let actual = table("Foo").select().offset(2).limit(3);
372        test_query_builder(actual, "SELECT * FROM Foo OFFSET 2 LIMIT 3");
373
374        let actual = table("Foo").select().order_by("id").offset(2).limit(3);
375        test_query_builder(actual, "SELECT * FROM Foo ORDER BY id OFFSET 2 LIMIT 3");
376    }
377
378    #[test]
379    fn query_builder_preserves_values_terminal_stage_relations() {
380        let actual = values(vec!["1"]);
381        test_query_builder(actual, "VALUES (1)");
382
383        let actual = values(vec!["1"]).order_by("column1");
384        test_query_builder(actual, "VALUES (1) ORDER BY column1");
385
386        let actual = values(vec!["1"]).offset(2);
387        test_query_builder(actual, "VALUES (1) OFFSET 2");
388
389        let actual = values(vec!["1"]).order_by("column1").offset(2);
390        test_query_builder(actual, "VALUES (1) ORDER BY column1 OFFSET 2");
391
392        let actual = values(vec!["1"]).limit(3);
393        test_query_builder(actual, "VALUES (1) LIMIT 3");
394
395        let actual = values(vec!["1"]).order_by("column1").limit(3);
396        test_query_builder(actual, "VALUES (1) ORDER BY column1 LIMIT 3");
397
398        let actual = values(vec!["1"]).offset(2).limit(3);
399        test_query_builder(actual, "VALUES (1) OFFSET 2 LIMIT 3");
400
401        let actual = values(vec!["1"]).order_by("column1").offset(2).limit(3);
402        test_query_builder(actual, "VALUES (1) ORDER BY column1 OFFSET 2 LIMIT 3");
403    }
404}