alopex_sql/executor/
result.rs

1//! Result types for the Executor module.
2//!
3//! This module defines the output types for SQL execution:
4//! - [`ExecutionResult`]: Top-level execution result
5//! - [`QueryResult`]: SELECT query results with column info
6//! - [`QueryRowIterator`]: Streaming query result with row iterator
7//! - [`Row`]: Internal row representation with row_id
8
9use crate::catalog::ColumnMetadata;
10use crate::executor::evaluator::EvalContext;
11use crate::executor::query::iterator::RowIterator;
12use crate::planner::ResolvedType;
13use crate::planner::typed_expr::Projection;
14use crate::storage::SqlValue;
15
16use super::ExecutorError;
17
18/// Result of executing a SQL statement.
19#[derive(Debug, Clone, PartialEq)]
20pub enum ExecutionResult {
21    /// DDL operation success (CREATE/DROP TABLE/INDEX).
22    Success,
23
24    /// DML operation success with affected row count.
25    RowsAffected(u64),
26
27    /// Query result with columns and rows.
28    Query(QueryResult),
29}
30
31/// Result of a SELECT query.
32#[derive(Debug, Clone, PartialEq)]
33pub struct QueryResult {
34    /// Column information for the result set.
35    pub columns: Vec<ColumnInfo>,
36
37    /// Result rows as vectors of SqlValue.
38    pub rows: Vec<Vec<SqlValue>>,
39}
40
41impl QueryResult {
42    /// Create a new query result with column info and rows.
43    pub fn new(columns: Vec<ColumnInfo>, rows: Vec<Vec<SqlValue>>) -> Self {
44        Self { columns, rows }
45    }
46
47    /// Create an empty query result with column info.
48    pub fn empty(columns: Vec<ColumnInfo>) -> Self {
49        Self {
50            columns,
51            rows: Vec::new(),
52        }
53    }
54
55    /// Returns the number of rows in the result.
56    pub fn row_count(&self) -> usize {
57        self.rows.len()
58    }
59
60    /// Returns the number of columns in the result.
61    pub fn column_count(&self) -> usize {
62        self.columns.len()
63    }
64
65    /// Returns true if the result is empty.
66    pub fn is_empty(&self) -> bool {
67        self.rows.is_empty()
68    }
69}
70
71/// Column information for query results.
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct ColumnInfo {
74    /// Column name (or alias if specified).
75    pub name: String,
76
77    /// Column data type.
78    pub data_type: ResolvedType,
79}
80
81impl ColumnInfo {
82    /// Create a new column info.
83    pub fn new(name: impl Into<String>, data_type: ResolvedType) -> Self {
84        Self {
85            name: name.into(),
86            data_type,
87        }
88    }
89}
90
91/// Internal row representation with row_id for DML operations.
92#[derive(Debug, Clone, PartialEq)]
93pub struct Row {
94    /// Row identifier (unique within table).
95    pub row_id: u64,
96
97    /// Column values.
98    pub values: Vec<SqlValue>,
99}
100
101impl Row {
102    /// Create a new row with row_id and values.
103    pub fn new(row_id: u64, values: Vec<SqlValue>) -> Self {
104        Self { row_id, values }
105    }
106
107    /// Get a column value by index.
108    pub fn get(&self, index: usize) -> Option<&SqlValue> {
109        self.values.get(index)
110    }
111
112    /// Returns the number of columns in the row.
113    pub fn len(&self) -> usize {
114        self.values.len()
115    }
116
117    /// Returns true if the row has no columns.
118    pub fn is_empty(&self) -> bool {
119        self.values.is_empty()
120    }
121}
122
123// ============================================================================
124// QueryRowIterator - Streaming query result
125// ============================================================================
126
127/// Streaming query result that yields rows one at a time.
128///
129/// This type enables true streaming output for SELECT queries by applying
130/// projection on-the-fly and yielding projected rows through an iterator.
131///
132/// # Example
133///
134/// ```ignore
135/// let mut iter = db.execute_sql_streaming("SELECT * FROM users")?;
136/// while let Some(row) = iter.next_row()? {
137///     println!("{:?}", row);
138/// }
139/// ```
140pub struct QueryRowIterator<'a> {
141    /// Underlying row iterator.
142    inner: Box<dyn RowIterator + 'a>,
143    /// Projection to apply to each row.
144    projection: Projection,
145    /// Schema of the input rows (kept for potential future use).
146    #[allow(dead_code)]
147    schema: Vec<ColumnMetadata>,
148    /// Column information for output rows.
149    columns: Vec<ColumnInfo>,
150}
151
152impl<'a> QueryRowIterator<'a> {
153    /// Create a new streaming query result.
154    pub fn new(
155        inner: Box<dyn RowIterator + 'a>,
156        projection: Projection,
157        schema: Vec<ColumnMetadata>,
158    ) -> Self {
159        // Build column info from projection
160        let columns = match &projection {
161            Projection::All(_) => schema
162                .iter()
163                .map(|col| ColumnInfo::new(&col.name, col.data_type.clone()))
164                .collect(),
165            Projection::Columns(cols) => cols
166                .iter()
167                .map(|col| {
168                    let name = col.alias.clone().unwrap_or_else(|| match &col.expr.kind {
169                        crate::planner::typed_expr::TypedExprKind::ColumnRef { column, .. } => {
170                            column.clone()
171                        }
172                        _ => "?column?".to_string(),
173                    });
174                    ColumnInfo::new(name, col.expr.resolved_type.clone())
175                })
176                .collect(),
177        };
178
179        Self {
180            inner,
181            projection,
182            schema,
183            columns,
184        }
185    }
186
187    /// Returns column information for the result set.
188    pub fn columns(&self) -> &[ColumnInfo] {
189        &self.columns
190    }
191
192    /// Advance and return the next projected row, or None if exhausted.
193    ///
194    /// # Errors
195    ///
196    /// Returns an error if reading or projection fails.
197    pub fn next_row(&mut self) -> Result<Option<Vec<SqlValue>>, ExecutorError> {
198        match self.inner.next_row() {
199            Some(Ok(row)) => {
200                let projected = self.project_row(&row)?;
201                Ok(Some(projected))
202            }
203            Some(Err(e)) => Err(e),
204            None => Ok(None),
205        }
206    }
207
208    /// Apply projection to a single row.
209    fn project_row(&self, row: &Row) -> Result<Vec<SqlValue>, ExecutorError> {
210        match &self.projection {
211            Projection::All(_) => Ok(row.values.clone()),
212            Projection::Columns(proj_cols) => {
213                let mut output = Vec::with_capacity(proj_cols.len());
214                let ctx = EvalContext::new(&row.values);
215                for col in proj_cols {
216                    let value = crate::executor::evaluator::evaluate(&col.expr, &ctx)?;
217                    output.push(value);
218                }
219                Ok(output)
220            }
221        }
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228
229    #[test]
230    fn test_execution_result_success() {
231        let result = ExecutionResult::Success;
232        assert!(matches!(result, ExecutionResult::Success));
233    }
234
235    #[test]
236    fn test_execution_result_rows_affected() {
237        let result = ExecutionResult::RowsAffected(5);
238        if let ExecutionResult::RowsAffected(count) = result {
239            assert_eq!(count, 5);
240        } else {
241            panic!("Expected RowsAffected variant");
242        }
243    }
244
245    #[test]
246    fn test_query_result_new() {
247        let columns = vec![
248            ColumnInfo::new("id", ResolvedType::Integer),
249            ColumnInfo::new("name", ResolvedType::Text),
250        ];
251        let rows = vec![
252            vec![SqlValue::Integer(1), SqlValue::Text("Alice".into())],
253            vec![SqlValue::Integer(2), SqlValue::Text("Bob".into())],
254        ];
255        let result = QueryResult::new(columns, rows);
256
257        assert_eq!(result.row_count(), 2);
258        assert_eq!(result.column_count(), 2);
259        assert!(!result.is_empty());
260    }
261
262    #[test]
263    fn test_query_result_empty() {
264        let columns = vec![ColumnInfo::new("id", ResolvedType::Integer)];
265        let result = QueryResult::empty(columns);
266
267        assert_eq!(result.row_count(), 0);
268        assert_eq!(result.column_count(), 1);
269        assert!(result.is_empty());
270    }
271
272    #[test]
273    fn test_row_new() {
274        let row = Row::new(
275            42,
276            vec![SqlValue::Integer(1), SqlValue::Text("test".into())],
277        );
278
279        assert_eq!(row.row_id, 42);
280        assert_eq!(row.len(), 2);
281        assert!(!row.is_empty());
282        assert_eq!(row.get(0), Some(&SqlValue::Integer(1)));
283        assert_eq!(row.get(1), Some(&SqlValue::Text("test".into())));
284        assert_eq!(row.get(2), None);
285    }
286
287    #[test]
288    fn test_column_info_new() {
289        let info = ColumnInfo::new("age", ResolvedType::Integer);
290        assert_eq!(info.name, "age");
291        assert_eq!(info.data_type, ResolvedType::Integer);
292    }
293}