sql_cli/data/
computed_view.rs

1use crate::data::datatable::{DataTable, DataValue};
2use crate::sql::parser::ast::ColumnRef;
3use crate::sql::recursive_parser::SqlExpression;
4use std::sync::Arc;
5
6/// Represents a column in a computed view - either original or derived
7#[derive(Debug, Clone)]
8pub enum ViewColumn {
9    /// Direct reference to a column in the original table
10    Original {
11        source_index: usize, // Index in the original DataTable
12        name: String,        // May be aliased from original
13    },
14    /// Computed/derived column with cached results
15    Derived {
16        name: String,
17        expression: SqlExpression,
18        cached_values: Vec<DataValue>, // Pre-computed for all visible rows
19    },
20}
21
22/// A view over a `DataTable` that can contain both original and computed columns
23/// This is query-scoped - exists only for the duration of one query result
24#[derive(Debug, Clone)]
25pub struct ComputedDataView {
26    /// Reference to the original source table (never modified)
27    source_table: Arc<DataTable>,
28
29    /// Column definitions (mix of original and derived)
30    columns: Vec<ViewColumn>,
31
32    /// Which rows from the source table are visible (after WHERE clause filtering)
33    /// Indices refer to rows in `source_table`
34    visible_rows: Vec<usize>,
35}
36
37impl ComputedDataView {
38    /// Create a new computed view with specified columns and visible rows
39    #[must_use]
40    pub fn new(
41        source_table: Arc<DataTable>,
42        columns: Vec<ViewColumn>,
43        visible_rows: Vec<usize>,
44    ) -> Self {
45        Self {
46            source_table,
47            columns,
48            visible_rows,
49        }
50    }
51
52    /// Get the number of visible rows
53    #[must_use]
54    pub fn row_count(&self) -> usize {
55        self.visible_rows.len()
56    }
57
58    /// Get the number of columns (original + derived)
59    #[must_use]
60    pub fn column_count(&self) -> usize {
61        self.columns.len()
62    }
63
64    /// Get column names
65    #[must_use]
66    pub fn column_names(&self) -> Vec<String> {
67        self.columns
68            .iter()
69            .map(|col| match col {
70                ViewColumn::Original { name, .. } => name.clone(),
71                ViewColumn::Derived { name, .. } => name.clone(),
72            })
73            .collect()
74    }
75
76    /// Get a value at a specific row and column
77    #[must_use]
78    pub fn get_value(&self, row_idx: usize, col_idx: usize) -> Option<DataValue> {
79        // Check bounds
80        if row_idx >= self.visible_rows.len() || col_idx >= self.columns.len() {
81            return None;
82        }
83
84        match &self.columns[col_idx] {
85            ViewColumn::Original { source_index, .. } => {
86                // Get the actual row index in the source table
87                let source_row_idx = self.visible_rows[row_idx];
88
89                // Get value from original table
90                self.source_table
91                    .get_row(source_row_idx)
92                    .and_then(|row| row.get(*source_index))
93                    .cloned()
94            }
95            ViewColumn::Derived { cached_values, .. } => {
96                // Return pre-computed value
97                cached_values.get(row_idx).cloned()
98            }
99        }
100    }
101
102    /// Get all values for a row
103    #[must_use]
104    pub fn get_row_values(&self, row_idx: usize) -> Option<Vec<DataValue>> {
105        if row_idx >= self.visible_rows.len() {
106            return None;
107        }
108
109        let mut values = Vec::new();
110        for col_idx in 0..self.columns.len() {
111            values.push(self.get_value(row_idx, col_idx)?);
112        }
113        Some(values)
114    }
115
116    /// Get the underlying source table (for reference, not modification)
117    #[must_use]
118    pub fn source_table(&self) -> &Arc<DataTable> {
119        &self.source_table
120    }
121
122    /// Get the visible row indices (useful for debugging)
123    #[must_use]
124    pub fn visible_rows(&self) -> &[usize] {
125        &self.visible_rows
126    }
127
128    /// Check if a column is derived
129    #[must_use]
130    pub fn is_derived_column(&self, col_idx: usize) -> bool {
131        matches!(self.columns.get(col_idx), Some(ViewColumn::Derived { .. }))
132    }
133
134    /// Create a simple view showing all columns from source (no computations)
135    #[must_use]
136    pub fn from_source_all_columns(source: Arc<DataTable>) -> Self {
137        let columns: Vec<ViewColumn> = source
138            .column_names()
139            .into_iter()
140            .enumerate()
141            .map(|(idx, name)| ViewColumn::Original {
142                source_index: idx,
143                name,
144            })
145            .collect();
146
147        let visible_rows: Vec<usize> = (0..source.row_count()).collect();
148
149        Self::new(source, columns, visible_rows)
150    }
151
152    /// Create a view with filtered rows (WHERE clause applied)
153    #[must_use]
154    pub fn with_filtered_rows(mut self, row_indices: Vec<usize>) -> Self {
155        self.visible_rows = row_indices;
156
157        // Update cached values for derived columns
158        for col in &mut self.columns {
159            if let ViewColumn::Derived { cached_values, .. } = col {
160                // Filter cached values to match new row set
161                let mut new_cache = Vec::new();
162                for &row_idx in &self.visible_rows {
163                    if row_idx < cached_values.len() {
164                        new_cache.push(cached_values[row_idx].clone());
165                    }
166                }
167                *cached_values = new_cache;
168            }
169        }
170
171        self
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    use crate::data::datatable::{DataColumn, DataRow};
179
180    fn create_test_table() -> Arc<DataTable> {
181        let mut table = DataTable::new("test");
182        table.add_column(DataColumn::new("a"));
183        table.add_column(DataColumn::new("b"));
184
185        table
186            .add_row(DataRow::new(vec![
187                DataValue::Integer(10),
188                DataValue::Float(2.5),
189            ]))
190            .unwrap();
191
192        table
193            .add_row(DataRow::new(vec![
194                DataValue::Integer(20),
195                DataValue::Float(3.5),
196            ]))
197            .unwrap();
198
199        Arc::new(table)
200    }
201
202    #[test]
203    fn test_original_columns_view() {
204        let table = create_test_table();
205        let view = ComputedDataView::from_source_all_columns(table);
206
207        assert_eq!(view.row_count(), 2);
208        assert_eq!(view.column_count(), 2);
209        assert_eq!(view.column_names(), vec!["a", "b"]);
210
211        // Check values
212        assert_eq!(view.get_value(0, 0), Some(DataValue::Integer(10)));
213        assert_eq!(view.get_value(0, 1), Some(DataValue::Float(2.5)));
214        assert_eq!(view.get_value(1, 0), Some(DataValue::Integer(20)));
215    }
216
217    #[test]
218    fn test_mixed_columns() {
219        let table = create_test_table();
220
221        // Create a view with original column 'a' and derived column 'doubled'
222        let columns = vec![
223            ViewColumn::Original {
224                source_index: 0,
225                name: "a".to_string(),
226            },
227            ViewColumn::Derived {
228                name: "doubled".to_string(),
229                expression: SqlExpression::Column(ColumnRef::unquoted("a".to_string())), // Placeholder
230                cached_values: vec![
231                    DataValue::Integer(20), // 10 * 2
232                    DataValue::Integer(40), // 20 * 2
233                ],
234            },
235        ];
236
237        let view = ComputedDataView::new(table, columns, vec![0, 1]);
238
239        assert_eq!(view.column_count(), 2);
240        assert_eq!(view.column_names(), vec!["a", "doubled"]);
241
242        // Original column
243        assert_eq!(view.get_value(0, 0), Some(DataValue::Integer(10)));
244
245        // Derived column
246        assert_eq!(view.get_value(0, 1), Some(DataValue::Integer(20)));
247        assert_eq!(view.get_value(1, 1), Some(DataValue::Integer(40)));
248    }
249
250    #[test]
251    fn test_filtered_rows() {
252        let table = create_test_table();
253        let view = ComputedDataView::from_source_all_columns(table).with_filtered_rows(vec![1]); // Only second row visible
254
255        assert_eq!(view.row_count(), 1);
256        assert_eq!(view.get_value(0, 0), Some(DataValue::Integer(20)));
257    }
258}