gluesql_core/ast_builder/select/
offset.rs

1use {
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        // select node -> offset node -> build
153        let actual = table("Foo").select().offset(10).build();
154        let expected = "SELECT * FROM Foo OFFSET 10";
155        test(actual, expected);
156
157        // group by node -> offset node -> build
158        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        // having node -> offset node -> build
163        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        // join node -> offset node -> build
173        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        // join node -> offset node -> build
178        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        // join node -> offset node -> build
183        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        // join node -> offset node -> build
193        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        // join constraint node -> offset node -> build
203        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        // filter node -> offset node -> build
213        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        // project node -> offset node -> build
218        let actual = table("Item").select().project("*").offset(10).build();
219        let expected = "SELECT * FROM Item OFFSET 10";
220        test(actual, expected);
221
222        // hash join node -> offset node -> build
223        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        // select -> offset -> derived subquery
269        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}