Skip to main content

kyu_executor/operators/
empty.rs

1//! Empty operator — produces a single row with no columns, then exhausts.
2
3use kyu_common::KyuResult;
4use crate::context::ExecutionContext;
5use crate::data_chunk::DataChunk;
6
7pub struct EmptyOp {
8    pub num_columns: usize,
9    done: bool,
10}
11
12impl EmptyOp {
13    pub fn new(num_columns: usize) -> Self {
14        Self {
15            num_columns,
16            done: false,
17        }
18    }
19
20    pub fn next(&mut self, _ctx: &ExecutionContext<'_>) -> KyuResult<Option<DataChunk>> {
21        if self.done {
22            return Ok(None);
23        }
24        self.done = true;
25        // Produce a single row. If num_columns == 0, it's still one row with no columns.
26        if self.num_columns == 0 {
27            Ok(Some(DataChunk::new_with_row_count(Vec::new(), 1)))
28        } else {
29            Ok(Some(DataChunk::single_empty_row(self.num_columns)))
30        }
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use super::*;
37    use kyu_types::TypedValue;
38
39    #[test]
40    fn empty_produces_one_row() {
41        let storage = crate::context::MockStorage::new();
42        let ctx = ExecutionContext::new(
43            kyu_catalog::CatalogContent::new(),
44            &storage,
45        );
46        let mut op = EmptyOp::new(2);
47        let chunk = op.next(&ctx).unwrap().unwrap();
48        assert_eq!(chunk.num_rows(), 1);
49        assert_eq!(chunk.get_value(0, 0), TypedValue::Null);
50        assert!(op.next(&ctx).unwrap().is_none());
51    }
52
53    #[test]
54    fn empty_zero_columns() {
55        let storage = crate::context::MockStorage::new();
56        let ctx = ExecutionContext::new(
57            kyu_catalog::CatalogContent::new(),
58            &storage,
59        );
60        let mut op = EmptyOp::new(0);
61        let chunk = op.next(&ctx).unwrap().unwrap();
62        assert_eq!(chunk.num_columns(), 0);
63        assert!(op.next(&ctx).unwrap().is_none());
64    }
65}