sql_cli/services/
query_execution_service.rs1use crate::config::config::BehaviorConfig;
2use crate::data::data_view::DataView;
3use crate::data::query_engine::QueryEngine;
4use crate::execution_plan::ExecutionPlan;
5use anyhow::Result;
6use std::sync::Arc;
7use std::time::Duration;
8use tracing::{debug, info};
9
10pub struct QueryExecutionResult {
12 pub dataview: DataView,
14
15 pub stats: QueryStats,
17
18 pub hidden_columns: Vec<String>,
20
21 pub query: String,
23
24 pub execution_plan: Option<ExecutionPlan>,
26}
27
28pub struct QueryStats {
30 pub row_count: usize,
31 pub column_count: usize,
32 pub execution_time: Duration,
33 pub query_engine_time: Duration,
34}
35
36pub struct QueryExecutionService {
38 case_insensitive: bool,
39 auto_hide_empty: bool,
40 date_notation: String,
41 behavior_config: Option<BehaviorConfig>,
42}
43
44impl QueryExecutionService {
45 #[must_use]
46 pub fn new(case_insensitive: bool, auto_hide_empty: bool) -> Self {
47 Self {
48 case_insensitive,
49 auto_hide_empty,
50 date_notation: "us".to_string(),
51 behavior_config: None,
52 }
53 }
54
55 #[must_use]
56 pub fn with_behavior_config(behavior_config: BehaviorConfig) -> Self {
57 let case_insensitive = behavior_config.case_insensitive_default;
58 let auto_hide_empty = behavior_config.hide_empty_columns;
59 let date_notation = behavior_config.default_date_notation.clone();
60 Self {
61 case_insensitive,
62 auto_hide_empty,
63 date_notation,
64 behavior_config: Some(behavior_config),
65 }
66 }
67
68 #[must_use]
69 pub fn with_date_notation(
70 case_insensitive: bool,
71 auto_hide_empty: bool,
72 date_notation: String,
73 ) -> Self {
74 Self {
75 case_insensitive,
76 auto_hide_empty,
77 date_notation,
78 behavior_config: None,
79 }
80 }
81
82 pub fn execute(
85 &self,
86 query: &str,
87 current_dataview: Option<&DataView>,
88 original_source: Option<&crate::data::datatable::DataTable>,
89 ) -> Result<QueryExecutionResult> {
90 use crate::sql::recursive_parser::Parser;
92 let mut parser = Parser::new(query);
93 let statement = parser
94 .parse()
95 .map_err(|e| anyhow::anyhow!("Parse error: {}", e))?;
96
97 let uses_dual = statement
98 .from_table
99 .as_ref()
100 .is_some_and(|t| t.to_uppercase() == "DUAL");
101
102 let no_from_clause = statement.from_table.is_none();
103
104 let source_table = if uses_dual || no_from_clause {
106 info!("QueryExecutionService: Using DUAL table for expression evaluation");
107 crate::data::datatable::DataTable::dual()
108 } else if let Some(original) = original_source {
109 info!(
111 "QueryExecutionService: Using original source with {} columns: {:?}",
112 original.column_count(),
113 original.column_names()
114 );
115 debug!(
116 "QueryExecutionService: DEBUG - Using original source with {} columns for query",
117 original.column_count()
118 );
119 original.clone()
120 } else if let Some(view) = current_dataview {
121 info!(
123 "QueryExecutionService: WARNING - No original source, using current view's source with {} columns: {:?}",
124 view.source().column_count(),
125 view.source().column_names()
126 );
127 debug!(
128 "QueryExecutionService: DEBUG WARNING - No original source, using view source with {} columns",
129 view.source().column_count()
130 );
131 view.source().clone()
132 } else {
133 return Err(anyhow::anyhow!("No data loaded"));
134 };
135
136 let table_arc = Arc::new(source_table);
138
139 let query_start = std::time::Instant::now();
141 let engine = if let Some(ref config) = self.behavior_config {
142 QueryEngine::with_behavior_config(config.clone())
143 } else {
144 QueryEngine::with_case_insensitive_and_date_notation(
145 self.case_insensitive,
146 self.date_notation.clone(),
147 )
148 };
149 let mut new_dataview = engine.execute(table_arc, query)?;
150 let query_engine_time = query_start.elapsed();
151
152 let mut hidden_columns = Vec::new();
154 if self.auto_hide_empty {
155 let hidden = new_dataview.hide_empty_columns();
156 if hidden > 0 {
157 info!("Auto-hidden {} empty columns after query execution", hidden);
158 hidden_columns = vec![format!("{} columns", hidden)];
161 }
162 }
163
164 let stats = QueryStats {
166 row_count: new_dataview.row_count(),
167 column_count: new_dataview.column_count(),
168 execution_time: query_start.elapsed(),
169 query_engine_time,
170 };
171
172 Ok(QueryExecutionResult {
173 dataview: new_dataview,
174 stats,
175 hidden_columns,
176 query: query.to_string(),
177 execution_plan: None,
178 })
179 }
180
181 pub fn set_case_insensitive(&mut self, case_insensitive: bool) {
183 self.case_insensitive = case_insensitive;
184 }
185
186 pub fn set_auto_hide_empty(&mut self, auto_hide: bool) {
187 self.auto_hide_empty = auto_hide;
188 }
189
190 pub fn set_date_notation(&mut self, date_notation: String) {
191 self.date_notation = date_notation;
192 }
193}
194
195impl QueryExecutionResult {
196 #[must_use]
198 pub fn status_message(&self) -> String {
199 let hidden_msg = if self.hidden_columns.is_empty() {
200 String::new()
201 } else {
202 format!(" ({} auto-hidden)", self.hidden_columns.len())
203 };
204
205 format!(
206 "Query executed: {} rows, {} columns{} ({} ms)",
207 self.stats.row_count,
208 self.stats.column_count,
209 hidden_msg,
210 self.stats.execution_time.as_millis()
211 )
212 }
213
214 #[must_use]
216 pub fn column_names(&self) -> Vec<String> {
217 self.dataview.column_names()
218 }
219
220 #[must_use]
222 pub fn table_name(&self) -> String {
223 self.dataview.source().name.clone()
224 }
225}