gluesql_core/ast_builder/
query.rs

1use {
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}