Skip to main content

gluesql_core/query_builder/select/
root.rs

1use {
2    super::{
3        BuildAggregationInputPlan, BuildFilterInputPlan, BuildProjectInputPlan, BuildSelect,
4        BuildSourcePlan, DistinctNode,
5    },
6    crate::{
7        ast::{
8            Expr, Literal, Projection, Select, SelectItem, TableAlias, TableFactor, TableWithJoins,
9        },
10        plan::{
11            AggregationInputPlan, DerivedSourcePlan, DictionarySourcePlan, FilterInputPlan,
12            ProjectInputPlan, SeriesSourcePlan, SourcePlan, TableAliasPlan, TableSourcePlan,
13        },
14        query_builder::{
15            ExprList, ExprNode, FilterNode, GroupByNode, HavingNode, InnerNestedLoopJoinNode,
16            LeftOuterNestedLoopJoinNode, LimitNode, OffsetNode, OrderByExprList, ProjectNode,
17            QueryBuilderError, QueryNode, SelectItemList, SelectOrderByNode, SourceNode,
18            TableAccessNode,
19        },
20        result::Result,
21        translate::alias_or_name,
22    },
23};
24
25#[derive(Clone, Debug)]
26pub struct SelectNode<'a> {
27    source_node: SourceNode<'a>,
28}
29
30impl<'a> SelectNode<'a> {
31    pub(in crate::query_builder) fn new(source_node: SourceNode<'a>) -> Self {
32        Self { source_node }
33    }
34
35    pub fn distinct(self) -> DistinctNode<'a> {
36        DistinctNode::new(self)
37    }
38
39    pub fn filter<T: Into<ExprNode<'a>>>(self, expr: T) -> FilterNode<'a> {
40        FilterNode::new(self, expr)
41    }
42
43    pub fn group_by<T: Into<ExprList<'a>>>(self, expr_list: T) -> GroupByNode<'a> {
44        GroupByNode::new(self, expr_list)
45    }
46
47    pub fn having<T: Into<ExprNode<'a>>>(self, expr: T) -> HavingNode<'a> {
48        HavingNode::new(self, expr)
49    }
50
51    pub fn offset<T: Into<ExprNode<'a>>>(self, expr: T) -> OffsetNode<'a> {
52        OffsetNode::new(self, expr)
53    }
54
55    pub fn limit<T: Into<ExprNode<'a>>>(self, expr: T) -> LimitNode<'a> {
56        LimitNode::new(self, expr)
57    }
58
59    pub fn project<T: Into<SelectItemList<'a>>>(self, select_items: T) -> ProjectNode<'a> {
60        ProjectNode::new(self, select_items)
61    }
62
63    pub fn order_by<T: Into<OrderByExprList<'a>>>(
64        self,
65        order_by_exprs: T,
66    ) -> SelectOrderByNode<'a> {
67        SelectOrderByNode::new(self, order_by_exprs)
68    }
69
70    pub fn join(self, table_name: &str) -> InnerNestedLoopJoinNode<'a> {
71        InnerNestedLoopJoinNode::from_select(self, table_name.to_owned(), None)
72    }
73
74    pub fn join_as(self, table_name: &str, alias: &str) -> InnerNestedLoopJoinNode<'a> {
75        InnerNestedLoopJoinNode::from_select(self, table_name.to_owned(), Some(alias.to_owned()))
76    }
77
78    pub fn left_join(self, table_name: &str) -> LeftOuterNestedLoopJoinNode<'a> {
79        LeftOuterNestedLoopJoinNode::from_select(self, table_name.to_owned(), None)
80    }
81
82    pub fn left_join_as(self, table_name: &str, alias: &str) -> LeftOuterNestedLoopJoinNode<'a> {
83        LeftOuterNestedLoopJoinNode::from_select(
84            self,
85            table_name.to_owned(),
86            Some(alias.to_owned()),
87        )
88    }
89
90    pub fn alias_as(self, table_alias: &'a str) -> SourceNode<'a> {
91        QueryNode::SelectNode(self).alias_as(table_alias)
92    }
93}
94
95impl BuildSourcePlan for SelectNode<'_> {
96    fn build_source_plan(self) -> Result<SourcePlan> {
97        match self.source_node {
98            SourceNode::Table {
99                name,
100                alias,
101                access,
102            } => Ok(SourcePlan::Table(TableSourcePlan {
103                name,
104                alias: alias.map(|name| TableAliasPlan {
105                    name,
106                    columns: Vec::new(),
107                }),
108                access: access.build_table_access_plan()?,
109            })),
110            SourceNode::Dictionary { dictionary, alias } => {
111                Ok(SourcePlan::Dictionary(DictionarySourcePlan {
112                    dictionary,
113                    alias: TableAliasPlan {
114                        name: alias,
115                        columns: Vec::new(),
116                    },
117                }))
118            }
119            SourceNode::Series { size, alias } => Ok(SourcePlan::Series(SeriesSourcePlan {
120                alias: TableAliasPlan {
121                    name: alias,
122                    columns: Vec::new(),
123                },
124                size: size.build_expr_plan()?,
125            })),
126            SourceNode::Derived { query, alias } => Ok(SourcePlan::Derived(DerivedSourcePlan {
127                query: Box::new(query.build_query_plan()?),
128                alias: TableAliasPlan {
129                    name: alias,
130                    columns: Vec::new(),
131                },
132            })),
133        }
134    }
135}
136
137impl BuildFilterInputPlan for SelectNode<'_> {
138    fn build_filter_input_plan(self) -> Result<FilterInputPlan> {
139        self.build_source_plan().map(FilterInputPlan::Source)
140    }
141}
142
143impl BuildAggregationInputPlan for SelectNode<'_> {
144    fn build_aggregation_input_plan(self) -> Result<AggregationInputPlan> {
145        self.build_source_plan().map(AggregationInputPlan::Source)
146    }
147}
148
149impl BuildProjectInputPlan for SelectNode<'_> {
150    fn build_project_input_plan(self) -> Result<ProjectInputPlan> {
151        self.build_source_plan().map(ProjectInputPlan::Source)
152    }
153}
154
155impl BuildSelect for SelectNode<'_> {
156    fn build_select(self) -> Result<Select> {
157        let relation = match self.source_node {
158            SourceNode::Table {
159                name,
160                alias,
161                access: TableAccessNode::FullScan,
162            } => TableFactor::Table {
163                name,
164                alias: alias.map(|name| TableAlias {
165                    name,
166                    columns: Vec::new(),
167                }),
168            },
169            SourceNode::Table { .. } => {
170                return Err(QueryBuilderError::IndexByRequiresPlan.into());
171            }
172            SourceNode::Dictionary { dictionary, alias } => TableFactor::Dictionary {
173                dict: dictionary,
174                alias: alias_or_name(None, alias),
175            },
176            SourceNode::Series { size, alias } => TableFactor::Series {
177                alias: alias_or_name(None, alias),
178                size: size.build_expr()?,
179            },
180            SourceNode::Derived { query, alias } => TableFactor::Derived {
181                subquery: query.build_query()?,
182                alias: TableAlias {
183                    name: alias,
184                    columns: Vec::new(),
185                },
186            },
187        };
188
189        let from = TableWithJoins {
190            relation,
191            joins: Vec::new(),
192        };
193
194        Ok(Select {
195            distinct: false,
196            projection: Projection::SelectItems(vec![SelectItem::Wildcard]),
197            from,
198            selection: None,
199            group_by: Vec::new(),
200            having: None,
201        })
202    }
203}
204
205pub fn select<'a>() -> SelectNode<'a> {
206    SelectNode {
207        source_node: SourceNode::Series {
208            size: Expr::Literal(Literal::Number(1.into())).into(),
209            alias: "Series".to_owned(),
210        },
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use {
217        crate::{
218            query_builder::{
219                QueryBuilderError, primary_key, select, select::BuildSelect, table,
220                test_query_builder,
221            },
222            result::Error,
223        },
224        pretty_assertions::assert_eq,
225    };
226
227    #[test]
228    fn select_root() {
229        // select node -> build
230        let actual = table("App").select();
231        let expected = "SELECT * FROM App";
232        test_query_builder(actual, expected);
233
234        let actual = table("Item").alias_as("i").select();
235        let expected = "SELECT * FROM Item i";
236        test_query_builder(actual, expected);
237
238        // select -> derived subquery
239        let actual = table("App").select().alias_as("Sub").select();
240        let expected = "SELECT * FROM (SELECT * FROM App) Sub";
241        test_query_builder(actual, expected);
242
243        // select without table
244        let actual = select().project("1 + 1");
245        let expected = "SELECT 1 + 1";
246        test_query_builder(actual, expected);
247
248        // select distinct
249        let actual = table("User").select().distinct();
250        let expected = "SELECT DISTINCT * FROM User";
251        test_query_builder(actual, expected);
252
253        // select distinct with project
254        let actual = table("Item").select().project("name").distinct();
255        let expected = "SELECT DISTINCT name FROM Item";
256        test_query_builder(actual, expected);
257    }
258
259    #[test]
260    fn index_by_ast_build_requires_plan() {
261        let actual = table("Player")
262            .index_by(primary_key().eq("1"))
263            .select()
264            .build_select();
265
266        assert_eq!(
267            actual,
268            Err(Error::QueryBuilder(QueryBuilderError::IndexByRequiresPlan))
269        );
270    }
271}