sql_cli/data/
computed_view.rs

1use crate::data::datatable::{DataTable, DataValue};
2use crate::sql::recursive_parser::SqlExpression;
3use std::sync::Arc;
4
5/// Represents a column in a computed view - either original or derived
6#[derive(Debug, Clone)]
7pub enum ViewColumn {
8    /// Direct reference to a column in the original table
9    Original {
10        source_index: usize, // Index in the original DataTable
11        name: String,        // May be aliased from original
12    },
13    /// Computed/derived column with cached results
14    Derived {
15        name: String,
16        expression: SqlExpression,
17        cached_values: Vec<DataValue>, // Pre-computed for all visible rows
18    },
19}
20
21/// A view over a `DataTable` that can contain both original and computed columns
22/// This is query-scoped - exists only for the duration of one query result
23#[derive(Debug, Clone)]
24pub struct ComputedDataView {
25    /// Reference to the original source table (never modified)
26    source_table: Arc<DataTable>,
27
28    /// Column definitions (mix of original and derived)
29    columns: Vec<ViewColumn>,
30
31    /// Which rows from the source table are visible (after WHERE clause filtering)
32    /// Indices refer to rows in `source_table`
33    visible_rows: Vec<usize>,
34}
35
36impl ComputedDataView {
37    /// Create a new computed view with specified columns and visible rows
38    #[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    /// Get the number of visible rows
52    #[must_use]
53    pub fn row_count(&self) -> usize {
54        self.visible_rows.len()
55    }
56
57    /// Get the number of columns (original + derived)
58    #[must_use]
59    pub fn column_count(&self) -> usize {
60        self.columns.len()
61    }
62
63    /// Get column names
64    #[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    /// Get a value at a specific row and column
76    #[must_use]
77    pub fn get_value(&self, row_idx: usize, col_idx: usize) -> Option<DataValue> {
78        // Check bounds
79        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                // Get the actual row index in the source table
86                let source_row_idx = self.visible_rows[row_idx];
87
88                // Get value from original table
89                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                // Return pre-computed value
96                cached_values.get(row_idx).cloned()
97            }
98        }
99    }
100
101    /// Get all values for a row
102    #[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    /// Get the underlying source table (for reference, not modification)
116    #[must_use]
117    pub fn source_table(&self) -> &Arc<DataTable> {
118        &self.source_table
119    }
120
121    /// Get the visible row indices (useful for debugging)
122    #[must_use]
123    pub fn visible_rows(&self) -> &[usize] {
124        &self.visible_rows
125    }
126
127    /// Check if a column is derived
128    #[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    /// Create a simple view showing all columns from source (no computations)
134    #[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    /// Create a view with filtered rows (WHERE clause applied)
152    #[must_use]
153    pub fn with_filtered_rows(mut self, row_indices: Vec<usize>) -> Self {
154        self.visible_rows = row_indices;
155
156        // Update cached values for derived columns
157        for col in &mut self.columns {
158            if let ViewColumn::Derived { cached_values, .. } = col {
159                // Filter cached values to match new row set
160                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        // Check values
211        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        // Create a view with original column 'a' and derived column 'doubled'
221        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()), // Placeholder
229                cached_values: vec![
230                    DataValue::Integer(20), // 10 * 2
231                    DataValue::Integer(40), // 20 * 2
232                ],
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        // Original column
242        assert_eq!(view.get_value(0, 0), Some(DataValue::Integer(10)));
243
244        // Derived column
245        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]); // Only second row visible
253
254        assert_eq!(view.row_count(), 1);
255        assert_eq!(view.get_value(0, 0), Some(DataValue::Integer(20)));
256    }
257}