gluesql_core/ast_builder/
query.rs

1use {
2    super::{
3        select::{Prebuild, ValuesNode},
4        table_factor::TableType,
5        ExprList, FilterNode, GroupByNode, HashJoinNode, HavingNode, JoinConstraintNode, JoinNode,
6        LimitNode, OffsetLimitNode, OffsetNode, OrderByNode, ProjectNode, SelectNode,
7        TableFactorNode,
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                col, glue_indexes, glue_objects, glue_table_columns, glue_tables, series, table,
133                test_query, SelectItemList,
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                projection: SelectItemList::from("*").try_into().unwrap(),
178                from: TableWithJoins {
179                    relation: TableFactor::Table {
180                        name: "Player".to_owned(),
181                        alias: None,
182                        index: None,
183                    },
184                    joins: vec![join],
185                },
186                selection: None,
187                group_by: Vec::new(),
188                having: None,
189            };
190
191            Query {
192                body: SetExpr::Select(Box::new(select)),
193                order_by: Vec::new(),
194                limit: None,
195                offset: None,
196            }
197        };
198        assert_eq!(Query::try_from(actual).unwrap(), expected);
199
200        let actual = table("FOO").select().group_by("id").into();
201        let expected = "SELECT * FROM FOO GROUP BY id";
202        test_query(actual, expected);
203
204        let actual = table("FOO")
205            .select()
206            .group_by("id")
207            .having("COUNT(id) > 10")
208            .into();
209        let expected = "SELECT * FROM FOO GROUP BY id HAVING COUNT(id) > 10";
210        test_query(actual, expected);
211
212        let actual = table("FOO")
213            .select()
214            .group_by("city")
215            .having("COUNT(name) < 100")
216            .limit(3)
217            .into();
218        let expected = "SELECT * FROM FOO GROUP BY city HAVING COUNT(name) < 100 LIMIT 3";
219        test_query(actual, expected);
220
221        let actual = table("FOO").select().offset(10).into();
222        let expected = "SELECT * FROM FOO OFFSET 10";
223        test_query(actual, expected);
224
225        let actual = table("FOO")
226            .select()
227            .group_by("city")
228            .having("COUNT(name) < 100")
229            .offset(1)
230            .limit(3)
231            .into();
232        let expected = "SELECT * FROM FOO GROUP BY city HAVING COUNT(name) < 100 OFFSET 1 LIMIT 3";
233        test_query(actual, expected);
234
235        let actual = table("FOO").select().project("id, name").limit(10).into();
236        let expected = r#"SELECT id, name FROM FOO LIMIT 10"#;
237        test_query(actual, expected);
238
239        let actual = table("Foo").select().order_by("score DESC").into();
240        let expected = "SELECT * FROM Foo ORDER BY score DESC";
241        test_query(actual, expected);
242
243        let actual = glue_objects().select().into();
244        let expected = "SELECT * FROM GLUE_OBJECTS";
245        test_query(actual, expected);
246
247        let actual = glue_tables().select().into();
248        let expected = "SELECT * FROM GLUE_TABLES";
249        test_query(actual, expected);
250
251        let actual = glue_indexes().select().into();
252        let expected = "SELECT * FROM GLUE_INDEXES";
253        test_query(actual, expected);
254
255        let actual = glue_table_columns().select().into();
256        let expected = "SELECT * FROM GLUE_TABLE_COLUMNS";
257        test_query(actual, expected);
258
259        let actual = series("1 + 2").select().into();
260        let expected = "SELECT * FROM SERIES(1 + 2)";
261        test_query(actual, expected);
262
263        let actual = table("Items").select().alias_as("Sub").select().into();
264        let expected = "SELECT * FROM (SELECT * FROM Items) AS Sub";
265        test_query(actual, expected);
266    }
267}