sql_cli/data/
computed_view.rs1use crate::data::datatable::{DataTable, DataValue};
2use crate::sql::recursive_parser::SqlExpression;
3use std::sync::Arc;
4
5#[derive(Debug, Clone)]
7pub enum ViewColumn {
8 Original {
10 source_index: usize, name: String, },
13 Derived {
15 name: String,
16 expression: SqlExpression,
17 cached_values: Vec<DataValue>, },
19}
20
21#[derive(Debug, Clone)]
24pub struct ComputedDataView {
25 source_table: Arc<DataTable>,
27
28 columns: Vec<ViewColumn>,
30
31 visible_rows: Vec<usize>,
34}
35
36impl ComputedDataView {
37 #[must_use]
39 pub fn new(
40 source_table: Arc<DataTable>,
41 columns: Vec<ViewColumn>,
42 visible_rows: Vec<usize>,
43 ) -> Self {
44 Self {
45 source_table,
46 columns,
47 visible_rows,
48 }
49 }
50
51 #[must_use]
53 pub fn row_count(&self) -> usize {
54 self.visible_rows.len()
55 }
56
57 #[must_use]
59 pub fn column_count(&self) -> usize {
60 self.columns.len()
61 }
62
63 #[must_use]
65 pub fn column_names(&self) -> Vec<String> {
66 self.columns
67 .iter()
68 .map(|col| match col {
69 ViewColumn::Original { name, .. } => name.clone(),
70 ViewColumn::Derived { name, .. } => name.clone(),
71 })
72 .collect()
73 }
74
75 #[must_use]
77 pub fn get_value(&self, row_idx: usize, col_idx: usize) -> Option<DataValue> {
78 if row_idx >= self.visible_rows.len() || col_idx >= self.columns.len() {
80 return None;
81 }
82
83 match &self.columns[col_idx] {
84 ViewColumn::Original { source_index, .. } => {
85 let source_row_idx = self.visible_rows[row_idx];
87
88 self.source_table
90 .get_row(source_row_idx)
91 .and_then(|row| row.get(*source_index))
92 .cloned()
93 }
94 ViewColumn::Derived { cached_values, .. } => {
95 cached_values.get(row_idx).cloned()
97 }
98 }
99 }
100
101 #[must_use]
103 pub fn get_row_values(&self, row_idx: usize) -> Option<Vec<DataValue>> {
104 if row_idx >= self.visible_rows.len() {
105 return None;
106 }
107
108 let mut values = Vec::new();
109 for col_idx in 0..self.columns.len() {
110 values.push(self.get_value(row_idx, col_idx)?);
111 }
112 Some(values)
113 }
114
115 #[must_use]
117 pub fn source_table(&self) -> &Arc<DataTable> {
118 &self.source_table
119 }
120
121 #[must_use]
123 pub fn visible_rows(&self) -> &[usize] {
124 &self.visible_rows
125 }
126
127 #[must_use]
129 pub fn is_derived_column(&self, col_idx: usize) -> bool {
130 matches!(self.columns.get(col_idx), Some(ViewColumn::Derived { .. }))
131 }
132
133 #[must_use]
135 pub fn from_source_all_columns(source: Arc<DataTable>) -> Self {
136 let columns: Vec<ViewColumn> = source
137 .column_names()
138 .into_iter()
139 .enumerate()
140 .map(|(idx, name)| ViewColumn::Original {
141 source_index: idx,
142 name,
143 })
144 .collect();
145
146 let visible_rows: Vec<usize> = (0..source.row_count()).collect();
147
148 Self::new(source, columns, visible_rows)
149 }
150
151 #[must_use]
153 pub fn with_filtered_rows(mut self, row_indices: Vec<usize>) -> Self {
154 self.visible_rows = row_indices;
155
156 for col in &mut self.columns {
158 if let ViewColumn::Derived { cached_values, .. } = col {
159 let mut new_cache = Vec::new();
161 for &row_idx in &self.visible_rows {
162 if row_idx < cached_values.len() {
163 new_cache.push(cached_values[row_idx].clone());
164 }
165 }
166 *cached_values = new_cache;
167 }
168 }
169
170 self
171 }
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177 use crate::data::datatable::{DataColumn, DataRow};
178
179 fn create_test_table() -> Arc<DataTable> {
180 let mut table = DataTable::new("test");
181 table.add_column(DataColumn::new("a"));
182 table.add_column(DataColumn::new("b"));
183
184 table
185 .add_row(DataRow::new(vec![
186 DataValue::Integer(10),
187 DataValue::Float(2.5),
188 ]))
189 .unwrap();
190
191 table
192 .add_row(DataRow::new(vec![
193 DataValue::Integer(20),
194 DataValue::Float(3.5),
195 ]))
196 .unwrap();
197
198 Arc::new(table)
199 }
200
201 #[test]
202 fn test_original_columns_view() {
203 let table = create_test_table();
204 let view = ComputedDataView::from_source_all_columns(table);
205
206 assert_eq!(view.row_count(), 2);
207 assert_eq!(view.column_count(), 2);
208 assert_eq!(view.column_names(), vec!["a", "b"]);
209
210 assert_eq!(view.get_value(0, 0), Some(DataValue::Integer(10)));
212 assert_eq!(view.get_value(0, 1), Some(DataValue::Float(2.5)));
213 assert_eq!(view.get_value(1, 0), Some(DataValue::Integer(20)));
214 }
215
216 #[test]
217 fn test_mixed_columns() {
218 let table = create_test_table();
219
220 let columns = vec![
222 ViewColumn::Original {
223 source_index: 0,
224 name: "a".to_string(),
225 },
226 ViewColumn::Derived {
227 name: "doubled".to_string(),
228 expression: SqlExpression::Column("a".to_string()), cached_values: vec![
230 DataValue::Integer(20), DataValue::Integer(40), ],
233 },
234 ];
235
236 let view = ComputedDataView::new(table, columns, vec![0, 1]);
237
238 assert_eq!(view.column_count(), 2);
239 assert_eq!(view.column_names(), vec!["a", "doubled"]);
240
241 assert_eq!(view.get_value(0, 0), Some(DataValue::Integer(10)));
243
244 assert_eq!(view.get_value(0, 1), Some(DataValue::Integer(20)));
246 assert_eq!(view.get_value(1, 1), Some(DataValue::Integer(40)));
247 }
248
249 #[test]
250 fn test_filtered_rows() {
251 let table = create_test_table();
252 let view = ComputedDataView::from_source_all_columns(table).with_filtered_rows(vec![1]); assert_eq!(view.row_count(), 1);
255 assert_eq!(view.get_value(0, 0), Some(DataValue::Integer(20)));
256 }
257}