cynos_query/executor/
project.rs1use crate::executor::{Relation, RelationEntry, SharedTables};
4use alloc::rc::Rc;
5use alloc::vec;
6use alloc::vec::Vec;
7use cynos_core::{Row, Value};
8
9pub struct ProjectExecutor {
11 column_indices: Vec<usize>,
13}
14
15impl ProjectExecutor {
16 pub fn new(column_indices: Vec<usize>) -> Self {
18 Self { column_indices }
19 }
20
21 pub fn execute(&self, input: Relation) -> Relation {
23 let tables = input.tables().to_vec();
24 let shared_tables: SharedTables = tables.clone().into();
25 let entries: Vec<RelationEntry> = input
26 .into_iter()
27 .map(|entry| {
28 let values: Vec<Value> = self
29 .column_indices
30 .iter()
31 .map(|&idx| entry.get_field(idx).cloned().unwrap_or(Value::Null))
32 .collect();
33 RelationEntry::new_combined(Rc::new(Row::new(entry.id(), values)), shared_tables.clone())
34 })
35 .collect();
36
37 let table_column_counts = vec![self.column_indices.len()];
39 Relation { entries, tables, table_column_counts }
40 }
41}
42
43#[allow(dead_code)]
45pub fn project_relation<F>(input: Relation, transform: F) -> Relation
46where
47 F: Fn(&RelationEntry) -> Vec<Value>,
48{
49 let tables = input.tables().to_vec();
50 let shared_tables: SharedTables = tables.clone().into();
51 let entries: Vec<RelationEntry> = input
52 .into_iter()
53 .map(|entry| {
54 let values = transform(&entry);
55 RelationEntry::new_combined(Rc::new(Row::new(entry.id(), values)), shared_tables.clone())
56 })
57 .collect();
58
59 let table_column_counts = if entries.is_empty() {
61 vec![0]
62 } else {
63 vec![entries[0].row.len()]
64 };
65 Relation { entries, tables, table_column_counts }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use cynos_core::Row;
72 use alloc::vec;
73
74 #[test]
75 fn test_project_executor() {
76 let rows = vec![
77 Rc::new(Row::new(1, vec![Value::Int64(1), Value::String("Alice".into()), Value::Int64(25)])),
78 Rc::new(Row::new(2, vec![Value::Int64(2), Value::String("Bob".into()), Value::Int64(30)])),
79 ];
80 let input = Relation::from_rows(rows, vec!["users".into()]);
81
82 let executor = ProjectExecutor::new(vec![0, 2]);
84 let result = executor.execute(input);
85
86 assert_eq!(result.len(), 2);
87 let first = &result.entries[0];
88 assert_eq!(first.row.len(), 2);
89 assert_eq!(first.get_field(0), Some(&Value::Int64(1)));
90 assert_eq!(first.get_field(1), Some(&Value::Int64(25)));
91 }
92
93 #[test]
94 fn test_project_relation_transform() {
95 let rows = vec![Rc::new(Row::new(1, vec![Value::Int64(10), Value::Int64(20)]))];
96 let input = Relation::from_rows(rows, vec!["t".into()]);
97
98 let result = project_relation(input, |entry| {
99 let a = entry.get_field(0).and_then(|v| v.as_i64()).unwrap_or(0);
100 let b = entry.get_field(1).and_then(|v| v.as_i64()).unwrap_or(0);
101 vec![Value::Int64(a + b)]
102 });
103
104 assert_eq!(result.len(), 1);
105 assert_eq!(result.entries[0].get_field(0), Some(&Value::Int64(30)));
106 }
107}