gluesql_core/query_builder/select/
offset.rs1use {
2 super::{BuildProjectPlan, BuildQuery, BuildQueryPlan, DistinctNode, ValuesNode},
3 crate::{
4 ast::Query,
5 plan::{OffsetInputPlan, OffsetPlan, QueryPlan},
6 query_builder::{
7 ExprNode, FilterNode, GroupByNode, HavingNode, InnerHashJoinNode,
8 InnerJoinConditionNode, InnerNestedLoopJoinNode, LeftOuterHashJoinNode,
9 LeftOuterJoinConditionNode, LeftOuterNestedLoopJoinNode, OffsetLimitNode, ProjectNode,
10 QueryNode, SelectNode, SelectOrderByNode, SourceNode, ValuesOrderByNode,
11 },
12 result::Result,
13 },
14};
15
16#[derive(Clone, Debug)]
17pub(super) enum PrevNode<'a> {
18 Select(SelectNode<'a>),
19 Values(ValuesNode<'a>),
20 GroupBy(GroupByNode<'a>),
21 Having(HavingNode<'a>),
22 InnerNestedLoop(Box<InnerNestedLoopJoinNode<'a>>),
23 LeftOuterNestedLoop(Box<LeftOuterNestedLoopJoinNode<'a>>),
24 InnerHash(Box<InnerHashJoinNode<'a>>),
25 LeftOuterHash(Box<LeftOuterHashJoinNode<'a>>),
26 InnerCondition(Box<InnerJoinConditionNode<'a>>),
27 LeftOuterCondition(Box<LeftOuterJoinConditionNode<'a>>),
28 Filter(FilterNode<'a>),
29 SelectOrderBy(SelectOrderByNode<'a>),
30 ValuesOrderBy(ValuesOrderByNode<'a>),
31 Distinct(DistinctNode<'a>),
32 ProjectNode(Box<ProjectNode<'a>>),
33}
34
35impl PrevNode<'_> {
36 fn build_offset_input_plan(self) -> Result<OffsetInputPlan> {
37 match self {
38 Self::Select(node) => node.build_project_plan().map(OffsetInputPlan::Project),
39 Self::Values(node) => node.build_values_plan().map(OffsetInputPlan::Values),
40 Self::GroupBy(node) => node.build_project_plan().map(OffsetInputPlan::Project),
41 Self::Having(node) => node.build_project_plan().map(OffsetInputPlan::Project),
42 Self::InnerNestedLoop(node) => node.build_project_plan().map(OffsetInputPlan::Project),
43 Self::LeftOuterNestedLoop(node) => {
44 node.build_project_plan().map(OffsetInputPlan::Project)
45 }
46 Self::InnerHash(node) => node.build_project_plan().map(OffsetInputPlan::Project),
47 Self::LeftOuterHash(node) => node.build_project_plan().map(OffsetInputPlan::Project),
48 Self::InnerCondition(node) => node.build_project_plan().map(OffsetInputPlan::Project),
49 Self::LeftOuterCondition(node) => {
50 node.build_project_plan().map(OffsetInputPlan::Project)
51 }
52 Self::Filter(node) => node.build_project_plan().map(OffsetInputPlan::Project),
53 Self::SelectOrderBy(node) => node
54 .build_select_order_by_plan()
55 .map(OffsetInputPlan::SelectOrderBy),
56 Self::ValuesOrderBy(node) => node
57 .build_values_order_by_plan()
58 .map(OffsetInputPlan::ValuesOrderBy),
59 Self::Distinct(node) => node.build_distinct_plan().map(OffsetInputPlan::Distinct),
60 Self::ProjectNode(node) => node.build_project_plan().map(OffsetInputPlan::Project),
61 }
62 }
63}
64
65impl BuildQuery for PrevNode<'_> {
66 fn build_query(self) -> Result<Query> {
67 match self {
68 Self::Select(node) => node.build_query(),
69 Self::Values(node) => node.build_query(),
70 Self::GroupBy(node) => node.build_query(),
71 Self::Having(node) => node.build_query(),
72 Self::InnerNestedLoop(node) => node.build_query(),
73 Self::LeftOuterNestedLoop(node) => node.build_query(),
74 Self::InnerHash(node) => node.build_query(),
75 Self::LeftOuterHash(node) => node.build_query(),
76 Self::InnerCondition(node) => node.build_query(),
77 Self::LeftOuterCondition(node) => node.build_query(),
78 Self::Filter(node) => node.build_query(),
79 Self::SelectOrderBy(node) => node.build_query(),
80 Self::ValuesOrderBy(node) => node.build_query(),
81 Self::Distinct(node) => node.build_query(),
82 Self::ProjectNode(node) => node.build_query(),
83 }
84 }
85}
86
87impl<'a> From<SelectNode<'a>> for PrevNode<'a> {
88 fn from(node: SelectNode<'a>) -> Self {
89 PrevNode::Select(node)
90 }
91}
92
93impl<'a> From<ValuesNode<'a>> for PrevNode<'a> {
94 fn from(node: ValuesNode<'a>) -> Self {
95 PrevNode::Values(node)
96 }
97}
98
99impl<'a> From<GroupByNode<'a>> for PrevNode<'a> {
100 fn from(node: GroupByNode<'a>) -> Self {
101 PrevNode::GroupBy(node)
102 }
103}
104
105impl<'a> From<HavingNode<'a>> for PrevNode<'a> {
106 fn from(node: HavingNode<'a>) -> Self {
107 PrevNode::Having(node)
108 }
109}
110
111impl<'a> From<InnerNestedLoopJoinNode<'a>> for PrevNode<'a> {
112 fn from(node: InnerNestedLoopJoinNode<'a>) -> Self {
113 Self::InnerNestedLoop(Box::new(node))
114 }
115}
116
117impl<'a> From<LeftOuterNestedLoopJoinNode<'a>> for PrevNode<'a> {
118 fn from(node: LeftOuterNestedLoopJoinNode<'a>) -> Self {
119 Self::LeftOuterNestedLoop(Box::new(node))
120 }
121}
122
123impl<'a> From<InnerHashJoinNode<'a>> for PrevNode<'a> {
124 fn from(node: InnerHashJoinNode<'a>) -> Self {
125 Self::InnerHash(Box::new(node))
126 }
127}
128
129impl<'a> From<LeftOuterHashJoinNode<'a>> for PrevNode<'a> {
130 fn from(node: LeftOuterHashJoinNode<'a>) -> Self {
131 Self::LeftOuterHash(Box::new(node))
132 }
133}
134
135impl<'a> From<InnerJoinConditionNode<'a>> for PrevNode<'a> {
136 fn from(node: InnerJoinConditionNode<'a>) -> Self {
137 Self::InnerCondition(Box::new(node))
138 }
139}
140
141impl<'a> From<LeftOuterJoinConditionNode<'a>> for PrevNode<'a> {
142 fn from(node: LeftOuterJoinConditionNode<'a>) -> Self {
143 Self::LeftOuterCondition(Box::new(node))
144 }
145}
146
147impl<'a> From<FilterNode<'a>> for PrevNode<'a> {
148 fn from(node: FilterNode<'a>) -> Self {
149 PrevNode::Filter(node)
150 }
151}
152
153impl<'a> From<SelectOrderByNode<'a>> for PrevNode<'a> {
154 fn from(node: SelectOrderByNode<'a>) -> Self {
155 Self::SelectOrderBy(node)
156 }
157}
158
159impl<'a> From<ValuesOrderByNode<'a>> for PrevNode<'a> {
160 fn from(node: ValuesOrderByNode<'a>) -> Self {
161 Self::ValuesOrderBy(node)
162 }
163}
164
165impl<'a> From<DistinctNode<'a>> for PrevNode<'a> {
166 fn from(node: DistinctNode<'a>) -> Self {
167 Self::Distinct(node)
168 }
169}
170
171impl<'a> From<ProjectNode<'a>> for PrevNode<'a> {
172 fn from(node: ProjectNode<'a>) -> Self {
173 PrevNode::ProjectNode(Box::new(node))
174 }
175}
176
177#[derive(Clone, Debug)]
178pub struct OffsetNode<'a> {
179 prev_node: PrevNode<'a>,
180 expr: ExprNode<'a>,
181}
182
183impl<'a> OffsetNode<'a> {
184 pub(super) fn new<N: Into<PrevNode<'a>>, T: Into<ExprNode<'a>>>(prev_node: N, expr: T) -> Self {
185 Self {
186 prev_node: prev_node.into(),
187 expr: expr.into(),
188 }
189 }
190
191 pub fn limit<T: Into<ExprNode<'a>>>(self, expr: T) -> OffsetLimitNode<'a> {
192 OffsetLimitNode::new(self, expr)
193 }
194
195 pub fn alias_as(self, table_alias: &'a str) -> SourceNode<'a> {
196 QueryNode::OffsetNode(self).alias_as(table_alias)
197 }
198
199 pub(super) fn build_offset_plan(self) -> Result<OffsetPlan> {
200 let count = self.expr.build_expr_plan()?;
201 let input = self.prev_node.build_offset_input_plan()?;
202
203 Ok(OffsetPlan { input, count })
204 }
205}
206
207impl BuildQueryPlan for OffsetNode<'_> {
208 fn build_query_plan(self) -> Result<QueryPlan> {
209 self.build_offset_plan().map(QueryPlan::Offset)
210 }
211}
212
213impl BuildQuery for OffsetNode<'_> {
214 fn build_query(self) -> Result<Query> {
215 let mut node_data = self.prev_node.build_query()?;
216 node_data.offset = Some(self.expr.build_expr()?);
217
218 Ok(node_data)
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use {
225 crate::{
226 plan::{
227 HashJoinInputPlan, HashJoinPlan, InnerJoinInputPlan, InnerJoinPlan,
228 OffsetInputPlan, OffsetPlan, ProjectInputPlan, ProjectPlan, ProjectionPlan,
229 QueryPlan, SourcePlan, StatementPlan, TableAccessPlan, TableSourcePlan,
230 },
231 query_builder::{Build, SelectItemList, col, num, table, test_query_builder},
232 },
233 pretty_assertions::assert_eq,
234 };
235
236 #[test]
237 fn offset() {
238 let actual = table("Foo").select().offset(10);
240 let expected = "SELECT * FROM Foo OFFSET 10";
241 test_query_builder(actual, expected);
242
243 let actual = table("Foo").select().group_by("id").offset(10);
245 let expected = "SELECT * FROM Foo GROUP BY id OFFSET 10";
246 test_query_builder(actual, expected);
247
248 let actual = table("Foo")
250 .select()
251 .group_by("id")
252 .having("id > 10")
253 .offset(10);
254 let expected = "SELECT * FROM Foo GROUP BY id HAVING id > 10 OFFSET 10";
255 test_query_builder(actual, expected);
256
257 let actual = table("Foo").select().join("Bar").offset(10);
259 let expected = "SELECT * FROM Foo JOIN Bar OFFSET 10";
260 test_query_builder(actual, expected);
261
262 let actual = table("Foo").select().join_as("Bar", "B").offset(10);
264 let expected = "SELECT * FROM Foo JOIN Bar AS B OFFSET 10";
265 test_query_builder(actual, expected);
266
267 let actual = table("Foo")
269 .select()
270 .left_join("Bar")
271 .on("Foo.id = Bar.id")
272 .offset(10);
273 let expected = "SELECT * FROM Foo LEFT JOIN Bar ON Foo.id = Bar.id OFFSET 10";
274 test_query_builder(actual, expected);
275
276 let actual = table("Foo")
278 .select()
279 .left_join_as("Bar", "B")
280 .on("Foo.id = B.id")
281 .offset(10);
282 let expected = "SELECT * FROM Foo LEFT JOIN Bar AS B ON Foo.id = B.id OFFSET 10";
283 test_query_builder(actual, expected);
284
285 let actual = table("Foo")
287 .select()
288 .join("Bar")
289 .on("Foo.id = Bar.id")
290 .offset(10);
291 let expected = "SELECT * FROM Foo JOIN Bar ON Foo.id = Bar.id OFFSET 10";
292 test_query_builder(actual, expected);
293
294 let actual = table("Bar").select().filter("id > 2").offset(100);
296 let expected = "SELECT * FROM Bar WHERE id > 2 OFFSET 100";
297 test_query_builder(actual, expected);
298
299 let actual = table("Item").select().project("*").offset(10);
301 let expected = "SELECT * FROM Item OFFSET 10";
302 test_query_builder(actual, expected);
303
304 let actual = table("Player")
306 .select()
307 .join("PlayerItem")
308 .hash_executor("PlayerItem.user_id", "Player.id")
309 .offset(100)
310 .build();
311 let expected = {
312 let join = InnerJoinPlan {
313 input: InnerJoinInputPlan::Hash(HashJoinPlan {
314 input: HashJoinInputPlan::Source(SourcePlan::Table(TableSourcePlan {
315 name: "Player".to_owned(),
316 alias: None,
317 access: TableAccessPlan::FullScan,
318 })),
319 right: SourcePlan::Table(TableSourcePlan {
320 name: "PlayerItem".to_owned(),
321 alias: None,
322 access: TableAccessPlan::FullScan,
323 }),
324 input_key: col("Player.id").build_expr_plan().unwrap(),
325 right_key: col("PlayerItem.user_id").build_expr_plan().unwrap(),
326 right_filter: None,
327 }),
328 };
329 let project = ProjectPlan {
330 input: ProjectInputPlan::InnerJoin(Box::new(join)),
331 projection: ProjectionPlan::SelectItems(
332 SelectItemList::from("*").build_select_items_plan().unwrap(),
333 ),
334 };
335
336 let offset = OffsetPlan {
337 input: OffsetInputPlan::Project(project),
338 count: num(100).build_expr_plan().unwrap(),
339 };
340
341 Ok(StatementPlan::Query(QueryPlan::Offset(offset)))
342 };
343 assert_eq!(actual, expected);
344
345 let actual = table("Foo").select().offset(10).alias_as("Sub").select();
347 let expected = "SELECT * FROM (SELECT * FROM Foo OFFSET 10) Sub";
348 test_query_builder(actual, expected);
349 }
350}