gluesql_core/ast_builder/select/
root.rs

1use {
2    super::{Prebuild, join::JoinOperatorType},
3    crate::{
4        ast::{Expr, Literal, Query, Select, SelectItem, TableAlias, TableFactor, TableWithJoins},
5        ast_builder::{
6            ExprList, ExprNode, FilterNode, GroupByNode, JoinNode, LimitNode, OffsetNode,
7            OrderByExprList, OrderByNode, ProjectNode, QueryNode, SelectItemList, TableFactorNode,
8            table_factor::TableType,
9        },
10        result::Result,
11        translate::alias_or_name,
12    },
13};
14
15#[derive(Clone, Debug)]
16pub struct SelectNode<'a> {
17    table_node: TableFactorNode<'a>,
18    distinct: bool,
19}
20
21impl<'a> SelectNode<'a> {
22    pub fn new(table_node: TableFactorNode<'a>) -> Self {
23        Self {
24            table_node,
25            distinct: false,
26        }
27    }
28
29    #[must_use]
30    pub fn distinct(mut self) -> Self {
31        self.distinct = true;
32        self
33    }
34
35    pub fn filter<T: Into<ExprNode<'a>>>(self, expr: T) -> FilterNode<'a> {
36        FilterNode::new(self, expr)
37    }
38
39    pub fn group_by<T: Into<ExprList<'a>>>(self, expr_list: T) -> GroupByNode<'a> {
40        GroupByNode::new(self, expr_list)
41    }
42
43    pub fn offset<T: Into<ExprNode<'a>>>(self, expr: T) -> OffsetNode<'a> {
44        OffsetNode::new(self, expr)
45    }
46
47    pub fn limit<T: Into<ExprNode<'a>>>(self, expr: T) -> LimitNode<'a> {
48        LimitNode::new(self, expr)
49    }
50
51    pub fn project<T: Into<SelectItemList<'a>>>(self, select_items: T) -> ProjectNode<'a> {
52        ProjectNode::new(self, select_items)
53    }
54
55    pub fn order_by<T: Into<OrderByExprList<'a>>>(self, order_by_exprs: T) -> OrderByNode<'a> {
56        OrderByNode::new(self, order_by_exprs)
57    }
58
59    pub fn join(self, table_name: &str) -> JoinNode<'a> {
60        JoinNode::new(self, table_name.to_owned(), None, JoinOperatorType::Inner)
61    }
62
63    pub fn join_as(self, table_name: &str, alias: &str) -> JoinNode<'a> {
64        JoinNode::new(
65            self,
66            table_name.to_owned(),
67            Some(alias.to_owned()),
68            JoinOperatorType::Inner,
69        )
70    }
71
72    pub fn left_join(self, table_name: &str) -> JoinNode<'a> {
73        JoinNode::new(self, table_name.to_owned(), None, JoinOperatorType::Left)
74    }
75
76    pub fn left_join_as(self, table_name: &str, alias: &str) -> JoinNode<'a> {
77        JoinNode::new(
78            self,
79            table_name.to_owned(),
80            Some(alias.to_owned()),
81            JoinOperatorType::Left,
82        )
83    }
84
85    pub fn alias_as(self, table_alias: &'a str) -> TableFactorNode<'a> {
86        QueryNode::SelectNode(self).alias_as(table_alias)
87    }
88}
89
90impl Prebuild<Select> for SelectNode<'_> {
91    fn prebuild(self) -> Result<Select> {
92        let alias = self.table_node.table_alias.map(|name| TableAlias {
93            name,
94            columns: Vec::new(),
95        });
96
97        let index = match self.table_node.index {
98            Some(index) => Some(index.prebuild()?),
99            None => None,
100        };
101
102        let relation = match self.table_node.table_type {
103            TableType::Table => TableFactor::Table {
104                name: self.table_node.table_name,
105                alias,
106                index,
107            },
108            TableType::Dictionary(dict) => TableFactor::Dictionary {
109                dict,
110                alias: alias_or_name(alias, self.table_node.table_name),
111            },
112            TableType::Series(args) => TableFactor::Series {
113                alias: alias_or_name(alias, self.table_node.table_name),
114                size: args.try_into()?,
115            },
116            TableType::Derived { subquery, alias } => TableFactor::Derived {
117                subquery: Query::try_from(*subquery)?,
118                alias: TableAlias {
119                    name: alias,
120                    columns: Vec::new(),
121                },
122            },
123        };
124
125        let from = TableWithJoins {
126            relation,
127            joins: Vec::new(),
128        };
129
130        Ok(Select {
131            distinct: self.distinct,
132            projection: vec![SelectItem::Wildcard],
133            from,
134            selection: None,
135            group_by: Vec::new(),
136            having: None,
137        })
138    }
139}
140
141pub fn select<'a>() -> SelectNode<'a> {
142    SelectNode {
143        distinct: false,
144        table_node: TableFactorNode {
145            table_name: "Series".to_owned(),
146            table_type: TableType::Series(Expr::Literal(Literal::Number(1.into())).into()),
147            table_alias: None,
148            index: None,
149        },
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use crate::ast_builder::{Build, select, table, test};
156
157    #[test]
158    fn select_root() {
159        // select node -> build
160        let actual = table("App").select().build();
161        let expected = "SELECT * FROM App";
162        test(&actual, expected);
163
164        let actual = table("Item").alias_as("i").select().build();
165        let expected = "SELECT * FROM Item i";
166        test(&actual, expected);
167
168        // select -> derived subquery
169        let actual = table("App").select().alias_as("Sub").select().build();
170        let expected = "SELECT * FROM (SELECT * FROM App) Sub";
171        test(&actual, expected);
172
173        // select without table
174        let actual = select().project("1 + 1").build();
175        let expected = "SELECT 1 + 1";
176        test(&actual, expected);
177
178        // select distinct
179        let actual = table("User").select().distinct().build();
180        let expected = "SELECT DISTINCT * FROM User";
181        test(&actual, expected);
182
183        // select distinct with project
184        let actual = table("Item").select().distinct().project("name").build();
185        let expected = "SELECT DISTINCT name FROM Item";
186        test(&actual, expected);
187    }
188}