sql_cli/execution/
mod.rs

1//! Unified execution module for SQL statement execution
2//!
3//! This module provides the foundation for unifying script mode and single query mode
4//! by offering a consistent execution path that:
5//!
6//! 1. Parses SQL exactly once
7//! 2. Applies preprocessing exactly once (if needed)
8//! 3. Executes AST directly (no re-parsing)
9//! 4. Manages execution context (temp tables, variables)
10//!
11//! # Architecture
12//!
13//! ```text
14//! ┌─────────────────────────────────────────┐
15//! │         StatementExecutor               │
16//! │  (Core execution engine)                │
17//! └──────────────┬──────────────────────────┘
18//!                │
19//!     ┌──────────┼──────────┐
20//!     ▼          ▼          ▼
21//! ┌────────┐ ┌────────┐ ┌─────────┐
22//! │Context │ │Config  │ │Pipeline │
23//! │(State) │ │(Flags) │ │(Preproc)│
24//! └────────┘ └────────┘ └─────────┘
25//! ```
26//!
27//! # Usage
28//!
29//! ```ignore
30//! use sql_cli::execution::{StatementExecutor, ExecutionContext, ExecutionConfig};
31//! use sql_cli::sql::recursive_parser::Parser;
32//! use std::sync::Arc;
33//!
34//! // Create execution context with source table
35//! let mut context = ExecutionContext::new(source_table);
36//!
37//! // Create executor with configuration
38//! let config = ExecutionConfig::new()
39//!     .with_case_insensitive(true)
40//!     .with_show_preprocessing(false);
41//! let executor = StatementExecutor::with_config(config);
42//!
43//! // Parse and execute
44//! let stmt = Parser::new("SELECT * FROM test").parse()?;
45//! let result = executor.execute(stmt, &mut context)?;
46//!
47//! println!("Rows: {}", result.dataview.row_count());
48//! println!("Execution time: {:.2}ms", result.stats.total_time_ms);
49//! ```
50//!
51//! # Phase 0 Status
52//!
53//! This module is part of Phase 0 of the execution mode unification plan.
54//! It provides the foundation without modifying existing code paths.
55//!
56//! **Completed:**
57//! - ExecutionContext - manages temp tables and state
58//! - ExecutionConfig - controls preprocessing and behavior
59//! - StatementExecutor - core execution engine
60//!
61//! **Next Steps (Future Phases):**
62//! - Phase 1: Refactor script mode to use StatementExecutor
63//! - Phase 2: Refactor single query mode to use StatementExecutor
64//! - Phase 3: Achieve feature parity
65//! - Phase 4: Integrate with TUI
66//!
67//! See `docs/EXECUTION_MODE_UNIFICATION_PLAN.md` for complete roadmap.
68
69pub mod config;
70pub mod context;
71pub mod statement_executor;
72
73// Re-export main types for convenience
74pub use config::ExecutionConfig;
75pub use context::ExecutionContext;
76pub use statement_executor::{ExecutionResult, ExecutionStats, StatementExecutor};
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use crate::data::datatable::{DataColumn, DataRow, DataTable, DataType, DataValue};
82    use crate::sql::recursive_parser::Parser;
83    use std::sync::Arc;
84
85    fn create_sample_table() -> DataTable {
86        let mut table = DataTable::new("sample");
87        table.add_column(DataColumn::new("id").with_type(DataType::Integer));
88        table.add_column(DataColumn::new("value").with_type(DataType::Integer));
89
90        for i in 1..=10 {
91            let _ = table.add_row(DataRow {
92                values: vec![DataValue::Integer(i), DataValue::Integer(i * 10)],
93            });
94        }
95
96        table
97    }
98
99    #[test]
100    fn test_module_integration() {
101        // Test that all components work together
102        let table = create_sample_table();
103        let mut context = ExecutionContext::new(Arc::new(table));
104
105        let config = ExecutionConfig::new()
106            .with_case_insensitive(false)
107            .with_auto_hide_empty(false);
108
109        let executor = StatementExecutor::with_config(config);
110
111        // Execute a simple query
112        let mut parser = Parser::new("SELECT id, value FROM sample WHERE id <= 5");
113        let stmt = parser.parse().unwrap();
114
115        let result = executor.execute(stmt, &mut context).unwrap();
116
117        assert_eq!(result.dataview.row_count(), 5);
118        assert_eq!(result.dataview.column_count(), 2);
119        assert!(result.stats.total_time_ms >= 0.0);
120    }
121
122    #[test]
123    fn test_temp_table_workflow() {
124        // Test the complete workflow with temp tables
125        let base_table = create_sample_table();
126        let mut context = ExecutionContext::new(Arc::new(base_table));
127        let executor = StatementExecutor::new();
128
129        // Create a temp table directly (for Phase 0, we're testing the infrastructure)
130        let mut temp_table = DataTable::new("#filtered");
131        temp_table.add_column(DataColumn::new("id").with_type(DataType::Integer));
132        temp_table.add_column(DataColumn::new("value").with_type(DataType::Integer));
133        for i in 1..=3 {
134            let _ = temp_table.add_row(DataRow {
135                values: vec![DataValue::Integer(i), DataValue::Integer(i * 10)],
136            });
137        }
138
139        // Store as temp table
140        context
141            .store_temp_table("#filtered".to_string(), Arc::new(temp_table))
142            .unwrap();
143
144        // Query the temp table
145        let mut parser = Parser::new("SELECT * FROM #filtered");
146        let stmt = parser.parse().unwrap();
147        let result = executor.execute(stmt, &mut context).unwrap();
148
149        assert_eq!(result.dataview.row_count(), 3);
150        assert!(context.has_temp_table("#filtered"));
151    }
152
153    #[test]
154    fn test_dual_table_usage() {
155        // Test queries without FROM clause (use DUAL)
156        let table = create_sample_table();
157        let mut context = ExecutionContext::new(Arc::new(table));
158        let executor = StatementExecutor::new();
159
160        let mut parser = Parser::new("SELECT 1+1 as result, 'hello' as greeting");
161        let stmt = parser.parse().unwrap();
162
163        let result = executor.execute(stmt, &mut context).unwrap();
164
165        assert_eq!(result.dataview.row_count(), 1);
166        assert_eq!(result.dataview.column_count(), 2);
167        assert!(!result.stats.preprocessing_applied); // No FROM, no preprocessing
168    }
169
170    #[test]
171    fn test_configuration_propagation() {
172        // Verify configuration affects execution
173        let table = create_sample_table();
174        let mut context = ExecutionContext::new(Arc::new(table));
175
176        // Test with preprocessing disabled
177        let config = ExecutionConfig::new().without_preprocessing();
178        let executor = StatementExecutor::with_config(config);
179
180        let mut parser = Parser::new("SELECT * FROM sample");
181        let stmt = parser.parse().unwrap();
182
183        let result = executor.execute(stmt, &mut context).unwrap();
184
185        // Should still work even with preprocessing disabled
186        assert_eq!(result.dataview.row_count(), 10);
187    }
188
189    #[test]
190    fn test_execution_statistics() {
191        // Verify statistics are collected correctly
192        let table = create_sample_table();
193        let mut context = ExecutionContext::new(Arc::new(table));
194        let executor = StatementExecutor::new();
195
196        let mut parser = Parser::new("SELECT * FROM sample WHERE value > 50");
197        let stmt = parser.parse().unwrap();
198
199        let result = executor.execute(stmt, &mut context).unwrap();
200
201        // Check statistics
202        assert_eq!(result.stats.row_count, 5); // values 60, 70, 80, 90, 100
203        assert_eq!(result.stats.column_count, 2);
204        assert!(result.stats.total_time_ms >= result.stats.preprocessing_time_ms);
205        assert!(result.stats.total_time_ms >= result.stats.execution_time_ms);
206    }
207}