Skip to main content

sochdb_query/executor/
limit.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3//! Limit + Offset operator.
4
5use super::node::PlanNode;
6use super::types::{Row, Schema};
7use sochdb_core::Result;
8
9/// Limit operator: skips `offset` rows, then returns at most `limit` rows.
10///
11/// ```text
12/// Limit(limit=10, offset=5)
13///   └── input
14/// ```
15pub struct LimitNode {
16    input: Box<dyn PlanNode>,
17    limit: Option<usize>,
18    offset: usize,
19    /// Rows emitted so far (after offset).
20    emitted: usize,
21    /// Rows skipped so far (for offset).
22    skipped: usize,
23}
24
25impl LimitNode {
26    pub fn new(input: Box<dyn PlanNode>, limit: Option<usize>, offset: usize) -> Self {
27        Self {
28            input,
29            limit,
30            offset,
31            emitted: 0,
32            skipped: 0,
33        }
34    }
35}
36
37impl PlanNode for LimitNode {
38    fn schema(&self) -> &Schema {
39        self.input.schema()
40    }
41
42    fn next(&mut self) -> Result<Option<Row>> {
43        // Skip offset rows
44        while self.skipped < self.offset {
45            match self.input.next()? {
46                Some(_) => self.skipped += 1,
47                None => return Ok(None),
48            }
49        }
50
51        // Check limit
52        if let Some(limit) = self.limit {
53            if self.emitted >= limit {
54                return Ok(None);
55            }
56        }
57
58        match self.input.next()? {
59            Some(row) => {
60                self.emitted += 1;
61                Ok(Some(row))
62            }
63            None => Ok(None),
64        }
65    }
66
67    fn reset(&mut self) -> Result<()> {
68        self.emitted = 0;
69        self.skipped = 0;
70        self.input.reset()
71    }
72}