Skip to main content

gluesql_core/query_builder/select/
offset_limit.rs

1use {
2    super::{BuildQuery, BuildQueryPlan},
3    crate::{
4        ast::Query,
5        plan::{LimitInputPlan, LimitPlan, OffsetPlan, QueryPlan},
6        query_builder::{ExprNode, OffsetNode, QueryNode, SourceNode},
7        result::Result,
8    },
9};
10
11#[derive(Clone, Debug)]
12pub(super) enum PrevNode<'a> {
13    Offset(OffsetNode<'a>),
14}
15
16impl PrevNode<'_> {
17    fn build_offset_plan(self) -> Result<OffsetPlan> {
18        match self {
19            Self::Offset(node) => node.build_offset_plan(),
20        }
21    }
22}
23
24impl BuildQuery for PrevNode<'_> {
25    fn build_query(self) -> Result<Query> {
26        match self {
27            Self::Offset(node) => node.build_query(),
28        }
29    }
30}
31
32impl<'a> From<OffsetNode<'a>> for PrevNode<'a> {
33    fn from(node: OffsetNode<'a>) -> Self {
34        PrevNode::Offset(node)
35    }
36}
37
38#[derive(Clone, Debug)]
39pub struct OffsetLimitNode<'a> {
40    prev_node: PrevNode<'a>,
41    expr: ExprNode<'a>,
42}
43
44impl<'a> OffsetLimitNode<'a> {
45    pub(super) fn new<N: Into<PrevNode<'a>>, T: Into<ExprNode<'a>>>(prev_node: N, expr: T) -> Self {
46        Self {
47            prev_node: prev_node.into(),
48            expr: expr.into(),
49        }
50    }
51
52    pub fn alias_as(self, table_alias: &'a str) -> SourceNode<'a> {
53        QueryNode::OffsetLimitNode(self).alias_as(table_alias)
54    }
55}
56
57impl BuildQueryPlan for OffsetLimitNode<'_> {
58    fn build_query_plan(self) -> Result<QueryPlan> {
59        let count = self.expr.build_expr_plan()?;
60        self.prev_node.build_offset_plan().map(|offset| {
61            QueryPlan::Limit(LimitPlan {
62                input: LimitInputPlan::Offset(offset),
63                count,
64            })
65        })
66    }
67}
68
69impl BuildQuery for OffsetLimitNode<'_> {
70    fn build_query(self) -> Result<Query> {
71        let mut node_data = self.prev_node.build_query()?;
72        node_data.limit = Some(self.expr.build_expr()?);
73
74        Ok(node_data)
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use crate::query_builder::{table, test_query_builder};
81
82    #[test]
83    fn offset_limit() {
84        // offset node -> limit node -> build node
85        let actual = table("Bar")
86            .select()
87            .group_by("city")
88            .having("COUNT(name) < 100")
89            .offset(1)
90            .limit(3);
91        let expected = "
92            SELECT * FROM Bar
93            GROUP BY city
94            HAVING COUNT(name) < 100
95            OFFSET 1
96            LIMIT 3;
97        ";
98        test_query_builder(actual, expected);
99
100        // project node -> offset node -> limit node
101        let actual = table("Bar")
102            .select()
103            .group_by("city")
104            .having("COUNT(name) < 100")
105            .project("city")
106            .offset(1)
107            .limit(3);
108        let expected = "
109            SELECT city FROM Bar
110            GROUP BY city
111            HAVING COUNT(name) < 100
112            OFFSET 1
113            LIMIT 3;
114        ";
115        test_query_builder(actual, expected);
116
117        // select -> offset -> limit -> derived subquery
118        let actual = table("Bar")
119            .select()
120            .group_by("city")
121            .having("COUNT(name) < 100")
122            .offset(1)
123            .limit(3)
124            .alias_as("Sub")
125            .select();
126        let expected = "
127            SELECT * FROM (
128                SELECT * FROM Bar
129                GROUP BY city
130                HAVING COUNT(name) < 100
131                OFFSET 1
132                LIMIT 3
133            ) Sub
134        ";
135        test_query_builder(actual, expected);
136    }
137}