gluesql_core/ast_builder/select/
offset.rs1use {
2 super::{Prebuild, ValuesNode},
3 crate::{
4 ast::Query,
5 ast_builder::{
6 ExprNode, FilterNode, GroupByNode, HashJoinNode, HavingNode, JoinConstraintNode,
7 JoinNode, OffsetLimitNode, OrderByNode, ProjectNode, QueryNode, SelectNode,
8 TableFactorNode,
9 },
10 result::Result,
11 },
12};
13
14#[derive(Clone, Debug)]
15pub enum PrevNode<'a> {
16 Select(SelectNode<'a>),
17 Values(ValuesNode<'a>),
18 GroupBy(GroupByNode<'a>),
19 Having(HavingNode<'a>),
20 Join(Box<JoinNode<'a>>),
21 JoinConstraint(Box<JoinConstraintNode<'a>>),
22 HashJoin(HashJoinNode<'a>),
23 Filter(FilterNode<'a>),
24 OrderBy(OrderByNode<'a>),
25 ProjectNode(Box<ProjectNode<'a>>),
26}
27
28impl<'a> Prebuild<Query> for PrevNode<'a> {
29 fn prebuild(self) -> Result<Query> {
30 match self {
31 Self::Select(node) => node.prebuild(),
32 Self::Values(node) => node.prebuild(),
33 Self::GroupBy(node) => node.prebuild(),
34 Self::Having(node) => node.prebuild(),
35 Self::Join(node) => node.prebuild(),
36 Self::JoinConstraint(node) => node.prebuild(),
37 Self::HashJoin(node) => node.prebuild(),
38 Self::Filter(node) => node.prebuild(),
39 Self::OrderBy(node) => node.prebuild(),
40 Self::ProjectNode(node) => node.prebuild(),
41 }
42 }
43}
44
45impl<'a> From<SelectNode<'a>> for PrevNode<'a> {
46 fn from(node: SelectNode<'a>) -> Self {
47 PrevNode::Select(node)
48 }
49}
50
51impl<'a> From<ValuesNode<'a>> for PrevNode<'a> {
52 fn from(node: ValuesNode<'a>) -> Self {
53 PrevNode::Values(node)
54 }
55}
56
57impl<'a> From<GroupByNode<'a>> for PrevNode<'a> {
58 fn from(node: GroupByNode<'a>) -> Self {
59 PrevNode::GroupBy(node)
60 }
61}
62
63impl<'a> From<HavingNode<'a>> for PrevNode<'a> {
64 fn from(node: HavingNode<'a>) -> Self {
65 PrevNode::Having(node)
66 }
67}
68
69impl<'a> From<JoinConstraintNode<'a>> for PrevNode<'a> {
70 fn from(node: JoinConstraintNode<'a>) -> Self {
71 PrevNode::JoinConstraint(Box::new(node))
72 }
73}
74
75impl<'a> From<JoinNode<'a>> for PrevNode<'a> {
76 fn from(node: JoinNode<'a>) -> Self {
77 PrevNode::Join(Box::new(node))
78 }
79}
80
81impl<'a> From<HashJoinNode<'a>> for PrevNode<'a> {
82 fn from(node: HashJoinNode<'a>) -> Self {
83 PrevNode::HashJoin(node)
84 }
85}
86
87impl<'a> From<FilterNode<'a>> for PrevNode<'a> {
88 fn from(node: FilterNode<'a>) -> Self {
89 PrevNode::Filter(node)
90 }
91}
92
93impl<'a> From<OrderByNode<'a>> for PrevNode<'a> {
94 fn from(node: OrderByNode<'a>) -> Self {
95 PrevNode::OrderBy(node)
96 }
97}
98
99impl<'a> From<ProjectNode<'a>> for PrevNode<'a> {
100 fn from(node: ProjectNode<'a>) -> Self {
101 PrevNode::ProjectNode(Box::new(node))
102 }
103}
104
105#[derive(Clone, Debug)]
106pub struct OffsetNode<'a> {
107 prev_node: PrevNode<'a>,
108 expr: ExprNode<'a>,
109}
110
111impl<'a> OffsetNode<'a> {
112 pub fn new<N: Into<PrevNode<'a>>, T: Into<ExprNode<'a>>>(prev_node: N, expr: T) -> Self {
113 Self {
114 prev_node: prev_node.into(),
115 expr: expr.into(),
116 }
117 }
118
119 pub fn limit<T: Into<ExprNode<'a>>>(self, expr: T) -> OffsetLimitNode<'a> {
120 OffsetLimitNode::new(self, expr)
121 }
122
123 pub fn alias_as(self, table_alias: &'a str) -> TableFactorNode<'a> {
124 QueryNode::OffsetNode(self).alias_as(table_alias)
125 }
126}
127
128impl<'a> Prebuild<Query> for OffsetNode<'a> {
129 fn prebuild(self) -> Result<Query> {
130 let mut node_data = self.prev_node.prebuild()?;
131 node_data.offset = Some(self.expr.try_into()?);
132
133 Ok(node_data)
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use {
140 crate::{
141 ast::{
142 Join, JoinConstraint, JoinExecutor, JoinOperator, Query, Select, SetExpr,
143 Statement, TableFactor, TableWithJoins,
144 },
145 ast_builder::{Build, SelectItemList, col, num, table, test},
146 },
147 pretty_assertions::assert_eq,
148 };
149
150 #[test]
151 fn offset() {
152 let actual = table("Foo").select().offset(10).build();
154 let expected = "SELECT * FROM Foo OFFSET 10";
155 test(actual, expected);
156
157 let actual = table("Foo").select().group_by("id").offset(10).build();
159 let expected = "SELECT * FROM Foo GROUP BY id OFFSET 10";
160 test(actual, expected);
161
162 let actual = table("Foo")
164 .select()
165 .group_by("id")
166 .having("id > 10")
167 .offset(10)
168 .build();
169 let expected = "SELECT * FROM Foo GROUP BY id HAVING id > 10 OFFSET 10";
170 test(actual, expected);
171
172 let actual = table("Foo").select().join("Bar").offset(10).build();
174 let expected = "SELECT * FROM Foo JOIN Bar OFFSET 10";
175 test(actual, expected);
176
177 let actual = table("Foo").select().join_as("Bar", "B").offset(10).build();
179 let expected = "SELECT * FROM Foo JOIN Bar AS B OFFSET 10";
180 test(actual, expected);
181
182 let actual = table("Foo")
184 .select()
185 .left_join("Bar")
186 .on("Foo.id = Bar.id")
187 .offset(10)
188 .build();
189 let expected = "SELECT * FROM Foo LEFT JOIN Bar ON Foo.id = Bar.id OFFSET 10";
190 test(actual, expected);
191
192 let actual = table("Foo")
194 .select()
195 .left_join_as("Bar", "B")
196 .on("Foo.id = B.id")
197 .offset(10)
198 .build();
199 let expected = "SELECT * FROM Foo LEFT JOIN Bar AS B ON Foo.id = B.id OFFSET 10";
200 test(actual, expected);
201
202 let actual = table("Foo")
204 .select()
205 .join("Bar")
206 .on("Foo.id = Bar.id")
207 .offset(10)
208 .build();
209 let expected = "SELECT * FROM Foo JOIN Bar ON Foo.id = Bar.id OFFSET 10";
210 test(actual, expected);
211
212 let actual = table("Bar").select().filter("id > 2").offset(100).build();
214 let expected = "SELECT * FROM Bar WHERE id > 2 OFFSET 100";
215 test(actual, expected);
216
217 let actual = table("Item").select().project("*").offset(10).build();
219 let expected = "SELECT * FROM Item OFFSET 10";
220 test(actual, expected);
221
222 let actual = table("Player")
224 .select()
225 .join("PlayerItem")
226 .hash_executor("PlayerItem.user_id", "Player.id")
227 .offset(100)
228 .build();
229 let expected = {
230 let join = Join {
231 relation: TableFactor::Table {
232 name: "PlayerItem".to_owned(),
233 alias: None,
234 index: None,
235 },
236 join_operator: JoinOperator::Inner(JoinConstraint::None),
237 join_executor: JoinExecutor::Hash {
238 key_expr: col("PlayerItem.user_id").try_into().unwrap(),
239 value_expr: col("Player.id").try_into().unwrap(),
240 where_clause: None,
241 },
242 };
243 let select = Select {
244 distinct: false,
245 projection: SelectItemList::from("*").try_into().unwrap(),
246 from: TableWithJoins {
247 relation: TableFactor::Table {
248 name: "Player".to_owned(),
249 alias: None,
250 index: None,
251 },
252 joins: vec![join],
253 },
254 selection: None,
255 group_by: Vec::new(),
256 having: None,
257 };
258
259 Ok(Statement::Query(Query {
260 body: SetExpr::Select(Box::new(select)),
261 order_by: Vec::new(),
262 limit: None,
263 offset: Some(num(100).try_into().unwrap()),
264 }))
265 };
266 assert_eq!(actual, expected);
267
268 let actual = table("Foo")
270 .select()
271 .offset(10)
272 .alias_as("Sub")
273 .select()
274 .build();
275 let expected = "SELECT * FROM (SELECT * FROM Foo OFFSET 10) Sub";
276 test(actual, expected);
277 }
278}