gluesql_core/query_builder/select/
root.rs1use {
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 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 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 let actual = select().project("1 + 1");
245 let expected = "SELECT 1 + 1";
246 test_query_builder(actual, expected);
247
248 let actual = table("User").select().distinct();
250 let expected = "SELECT DISTINCT * FROM User";
251 test_query_builder(actual, expected);
252
253 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}