1use crate::config::config::BehaviorConfig;
2use crate::data::data_view::DataView;
3use crate::data::query_engine::QueryEngine;
4use crate::data::temp_table_registry::TempTableRegistry;
5use crate::debug_trace::{DebugContext, DebugLevel, QueryTrace, ScopedTimer};
6use crate::execution_plan::ExecutionPlan;
7use anyhow::Result;
8use std::sync::Arc;
9use std::time::Duration;
10use tracing::{debug, info};
11
12pub struct QueryExecutionResult {
14 pub dataview: DataView,
16
17 pub stats: QueryStats,
19
20 pub hidden_columns: Vec<String>,
22
23 pub query: String,
25
26 pub execution_plan: Option<ExecutionPlan>,
28
29 pub debug_trace: Option<String>,
31}
32
33pub struct QueryStats {
35 pub row_count: usize,
36 pub column_count: usize,
37 pub execution_time: Duration,
38 pub query_engine_time: Duration,
39}
40
41pub struct QueryExecutionService {
43 case_insensitive: bool,
44 auto_hide_empty: bool,
45 date_notation: String,
46 behavior_config: Option<BehaviorConfig>,
47}
48
49impl QueryExecutionService {
50 #[must_use]
51 pub fn new(case_insensitive: bool, auto_hide_empty: bool) -> Self {
52 Self {
53 case_insensitive,
54 auto_hide_empty,
55 date_notation: "us".to_string(),
56 behavior_config: None,
57 }
58 }
59
60 #[must_use]
61 pub fn with_behavior_config(behavior_config: BehaviorConfig) -> Self {
62 let case_insensitive = behavior_config.case_insensitive_default;
63 let auto_hide_empty = behavior_config.hide_empty_columns;
64 let date_notation = behavior_config.default_date_notation.clone();
65 Self {
66 case_insensitive,
67 auto_hide_empty,
68 date_notation,
69 behavior_config: Some(behavior_config),
70 }
71 }
72
73 #[must_use]
74 pub fn with_date_notation(
75 case_insensitive: bool,
76 auto_hide_empty: bool,
77 date_notation: String,
78 ) -> Self {
79 Self {
80 case_insensitive,
81 auto_hide_empty,
82 date_notation,
83 behavior_config: None,
84 }
85 }
86
87 pub fn execute(
90 &self,
91 query: &str,
92 current_dataview: Option<&DataView>,
93 original_source: Option<&crate::data::datatable::DataTable>,
94 ) -> Result<QueryExecutionResult> {
95 self.execute_with_debug(query, current_dataview, original_source, None)
96 }
97
98 pub fn execute_with_temp_tables(
100 &self,
101 query: &str,
102 current_dataview: Option<&DataView>,
103 original_source: Option<&crate::data::datatable::DataTable>,
104 temp_tables: Option<&TempTableRegistry>,
105 ) -> Result<QueryExecutionResult> {
106 self.execute_with_temp_tables_and_debug(
107 query,
108 current_dataview,
109 original_source,
110 temp_tables,
111 None,
112 )
113 }
114
115 pub fn execute_with_debug(
117 &self,
118 query: &str,
119 current_dataview: Option<&DataView>,
120 original_source: Option<&crate::data::datatable::DataTable>,
121 debug_context: Option<DebugContext>,
122 ) -> Result<QueryExecutionResult> {
123 self.execute_with_temp_tables_and_debug(
124 query,
125 current_dataview,
126 original_source,
127 None,
128 debug_context,
129 )
130 }
131
132 pub fn execute_with_temp_tables_and_debug(
134 &self,
135 query: &str,
136 current_dataview: Option<&DataView>,
137 original_source: Option<&crate::data::datatable::DataTable>,
138 temp_tables: Option<&TempTableRegistry>,
139 debug_context: Option<DebugContext>,
140 ) -> Result<QueryExecutionResult> {
141 let debug_enabled = debug_context.is_some();
143
144 let mut trace = if let Some(ctx) = debug_context {
145 QueryTrace::new(ctx)
146 } else {
147 QueryTrace::new(DebugContext::new(DebugLevel::Off))
148 };
149 use crate::query_plan::{CTEHoister, ExpressionLifter};
151 use crate::sql::recursive_parser::Parser;
152
153 let parse_timer = ScopedTimer::new();
154 trace.log("PARSER", "START", format!("Parsing query: {}", query));
155
156 let mut parser = if debug_enabled {
158 Parser::new(query).with_debug_trace(true)
159 } else {
160 Parser::new(query)
161 };
162 let mut statement = parser
163 .parse()
164 .map_err(|e| anyhow::anyhow!("Parse error: {}", e))?;
165
166 trace.log_timed(
167 "PARSER",
168 "COMPLETE",
169 format!(
170 "Parsed {} CTEs, FROM: {:?}",
171 statement.ctes.len(),
172 statement.from_table
173 ),
174 parse_timer.elapsed_us(),
175 );
176
177 let lift_timer = ScopedTimer::new();
179 trace.log("OPTIMIZER", "LIFT_START", "Starting expression lifting");
180
181 let mut expr_lifter = ExpressionLifter::new();
182 let lifted = expr_lifter.lift_expressions(&mut statement);
183 if !lifted.is_empty() {
184 info!(
185 "Applied expression lifting - {} CTEs generated",
186 lifted.len()
187 );
188 trace.log_timed(
189 "OPTIMIZER",
190 "LIFT_COMPLETE",
191 format!("Generated {} lifted CTEs", lifted.len()),
192 lift_timer.elapsed_us(),
193 );
194 } else {
195 trace.log_timed(
196 "OPTIMIZER",
197 "LIFT_COMPLETE",
198 "No expressions needed lifting",
199 lift_timer.elapsed_us(),
200 );
201 }
202
203 let hoist_timer = ScopedTimer::new();
205 trace.log("OPTIMIZER", "HOIST_START", "Starting CTE hoisting");
206
207 let original_cte_count = statement.ctes.len();
208 statement = CTEHoister::hoist_ctes(statement);
209
210 trace.log_timed(
211 "OPTIMIZER",
212 "HOIST_COMPLETE",
213 format!(
214 "CTEs after hoisting: {} (was {})",
215 statement.ctes.len(),
216 original_cte_count
217 ),
218 hoist_timer.elapsed_us(),
219 );
220
221 let uses_dual = statement
222 .from_table
223 .as_ref()
224 .is_some_and(|t| t.to_uppercase() == "DUAL");
225
226 let no_from_clause = statement.from_table.is_none();
227
228 let source_table = if uses_dual || no_from_clause {
230 info!("QueryExecutionService: Using DUAL table for expression evaluation");
231 crate::data::datatable::DataTable::dual()
232 } else if let Some(original) = original_source {
233 info!(
235 "QueryExecutionService: Using original source with {} columns: {:?}",
236 original.column_count(),
237 original.column_names()
238 );
239 debug!(
240 "QueryExecutionService: DEBUG - Using original source with {} columns for query",
241 original.column_count()
242 );
243 original.clone()
244 } else if let Some(view) = current_dataview {
245 info!(
247 "QueryExecutionService: WARNING - No original source, using current view's source with {} columns: {:?}",
248 view.source().column_count(),
249 view.source().column_names()
250 );
251 debug!(
252 "QueryExecutionService: DEBUG WARNING - No original source, using view source with {} columns",
253 view.source().column_count()
254 );
255 view.source().clone()
256 } else {
257 return Err(anyhow::anyhow!("No data loaded"));
258 };
259
260 let table_arc = Arc::new(source_table);
262
263 let query_start = std::time::Instant::now();
265 trace.log(
266 "EXECUTOR",
267 "START",
268 format!(
269 "Executing with {} rows, {} columns",
270 table_arc.row_count(),
271 table_arc.column_count()
272 ),
273 );
274 let engine = if let Some(ref config) = self.behavior_config {
275 QueryEngine::with_behavior_config(config.clone())
276 } else {
277 QueryEngine::with_case_insensitive_and_date_notation(
278 self.case_insensitive,
279 self.date_notation.clone(),
280 )
281 };
282 let mut new_dataview = if temp_tables.is_some() {
283 engine.execute_with_temp_tables(table_arc, query, temp_tables)?
284 } else {
285 engine.execute(table_arc, query)?
286 };
287 let query_engine_time = query_start.elapsed();
288
289 trace.log_timed(
290 "EXECUTOR",
291 "COMPLETE",
292 format!("Query returned {} rows", new_dataview.row_count()),
293 query_engine_time.as_micros() as u64,
294 );
295
296 let mut hidden_columns = Vec::new();
298 if self.auto_hide_empty {
299 let hidden = new_dataview.hide_empty_columns();
300 if hidden > 0 {
301 info!("Auto-hidden {} empty columns after query execution", hidden);
302 hidden_columns = vec![format!("{} columns", hidden)];
305 }
306 }
307
308 let stats = QueryStats {
310 row_count: new_dataview.row_count(),
311 column_count: new_dataview.column_count(),
312 execution_time: query_start.elapsed(),
313 query_engine_time,
314 };
315
316 let debug_output = if trace.is_enabled() {
317 Some(trace.format_output())
318 } else {
319 None
320 };
321
322 Ok(QueryExecutionResult {
323 dataview: new_dataview,
324 stats,
325 hidden_columns,
326 query: query.to_string(),
327 execution_plan: None,
328 debug_trace: debug_output,
329 })
330 }
331
332 pub fn set_case_insensitive(&mut self, case_insensitive: bool) {
334 self.case_insensitive = case_insensitive;
335 }
336
337 pub fn set_auto_hide_empty(&mut self, auto_hide: bool) {
338 self.auto_hide_empty = auto_hide;
339 }
340
341 pub fn set_date_notation(&mut self, date_notation: String) {
342 self.date_notation = date_notation;
343 }
344}
345
346impl QueryExecutionResult {
347 #[must_use]
349 pub fn status_message(&self) -> String {
350 let hidden_msg = if self.hidden_columns.is_empty() {
351 String::new()
352 } else {
353 format!(" ({} auto-hidden)", self.hidden_columns.len())
354 };
355
356 format!(
357 "Query executed: {} rows, {} columns{} ({} ms)",
358 self.stats.row_count,
359 self.stats.column_count,
360 hidden_msg,
361 self.stats.execution_time.as_millis()
362 )
363 }
364
365 #[must_use]
367 pub fn column_names(&self) -> Vec<String> {
368 self.dataview.column_names()
369 }
370
371 #[must_use]
373 pub fn table_name(&self) -> String {
374 self.dataview.source().name.clone()
375 }
376}