gluesql_core/ast_builder/select/
root.rs

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