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