Skip to main content

cynos_query/executor/
limit.rs

1//! Limit executor.
2
3use crate::executor::Relation;
4
5/// Limit executor - applies LIMIT and OFFSET to a relation.
6pub struct LimitExecutor {
7    limit: usize,
8    offset: usize,
9}
10
11impl LimitExecutor {
12    /// Creates a new limit executor.
13    pub fn new(limit: usize, offset: usize) -> Self {
14        Self { limit, offset }
15    }
16
17    /// Creates a limit executor with only a limit (no offset).
18    pub fn limit_only(limit: usize) -> Self {
19        Self { limit, offset: 0 }
20    }
21
22    /// Executes the limit on the input relation.
23    ///
24    /// Note: This operation is O(offset + limit) for the actual work,
25    /// but dropping the unused entries is O(n - offset - limit).
26    /// In a real query engine, limit would be pushed down to avoid
27    /// materializing unnecessary rows.
28    pub fn execute(&self, mut input: Relation) -> Relation {
29        let tables = input.tables().to_vec();
30        let table_column_counts = input.table_column_counts().to_vec();
31        let len = input.entries.len();
32        let start = self.offset.min(len);
33        let end = (self.offset + self.limit).min(len);
34
35        // Truncate tail first (drops elements after end)
36        input.entries.truncate(end);
37        // Remove head elements (drops elements before start)
38        if start > 0 {
39            input.entries.drain(..start);
40        }
41
42        Relation {
43            entries: input.entries,
44            tables,
45            table_column_counts,
46        }
47    }
48}
49
50/// Applies limit and offset to a relation.
51#[allow(dead_code)]
52pub fn limit_relation(input: Relation, limit: usize, offset: usize) -> Relation {
53    let tables = input.tables().to_vec();
54    let table_column_counts = input.table_column_counts().to_vec();
55    let entries = input
56        .entries
57        .into_iter()
58        .skip(offset)
59        .take(limit)
60        .collect();
61
62    Relation { entries, tables, table_column_counts }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use alloc::vec;
69    use alloc::vec::Vec;
70    use cynos_core::{Row, Value};
71
72    #[test]
73    fn test_limit_executor() {
74        let rows: Vec<Row> = (0..10)
75            .map(|i| Row::new(i, vec![Value::Int64(i as i64)]))
76            .collect();
77        let input = Relation::from_rows_owned(rows, vec!["t".into()]);
78
79        let executor = LimitExecutor::new(3, 2);
80        let result = executor.execute(input);
81
82        assert_eq!(result.len(), 3);
83        assert_eq!(result.entries[0].get_field(0), Some(&Value::Int64(2)));
84        assert_eq!(result.entries[1].get_field(0), Some(&Value::Int64(3)));
85        assert_eq!(result.entries[2].get_field(0), Some(&Value::Int64(4)));
86    }
87
88    #[test]
89    fn test_limit_only() {
90        let rows: Vec<Row> = (0..10)
91            .map(|i| Row::new(i, vec![Value::Int64(i as i64)]))
92            .collect();
93        let input = Relation::from_rows_owned(rows, vec!["t".into()]);
94
95        let executor = LimitExecutor::limit_only(5);
96        let result = executor.execute(input);
97
98        assert_eq!(result.len(), 5);
99        assert_eq!(result.entries[0].get_field(0), Some(&Value::Int64(0)));
100    }
101
102    #[test]
103    fn test_limit_exceeds_size() {
104        let rows = vec![
105            Row::new(0, vec![Value::Int64(0)]),
106            Row::new(1, vec![Value::Int64(1)]),
107        ];
108        let input = Relation::from_rows_owned(rows, vec!["t".into()]);
109
110        let executor = LimitExecutor::new(100, 0);
111        let result = executor.execute(input);
112
113        assert_eq!(result.len(), 2);
114    }
115
116    #[test]
117    fn test_offset_exceeds_size() {
118        let rows = vec![
119            Row::new(0, vec![Value::Int64(0)]),
120            Row::new(1, vec![Value::Int64(1)]),
121        ];
122        let input = Relation::from_rows_owned(rows, vec!["t".into()]);
123
124        let executor = LimitExecutor::new(10, 100);
125        let result = executor.execute(input);
126
127        assert_eq!(result.len(), 0);
128    }
129}