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 mut trace = if let Some(ctx) = debug_context {
106 QueryTrace::new(ctx)
107 } else {
108 QueryTrace::new(DebugContext::new(DebugLevel::Off))
109 };
110 use crate::query_plan::{CTEHoister, ExpressionLifter};
112 use crate::sql::recursive_parser::Parser;
113
114 let parse_timer = ScopedTimer::new();
115 trace.log("PARSER", "START", format!("Parsing query: {}", query));
116
117 let mut parser = Parser::new(query);
118 let mut statement = parser
119 .parse()
120 .map_err(|e| anyhow::anyhow!("Parse error: {}", e))?;
121
122 trace.log_timed(
123 "PARSER",
124 "COMPLETE",
125 format!(
126 "Parsed {} CTEs, FROM: {:?}",
127 statement.ctes.len(),
128 statement.from_table
129 ),
130 parse_timer.elapsed_us(),
131 );
132
133 let lift_timer = ScopedTimer::new();
135 trace.log("OPTIMIZER", "LIFT_START", "Starting expression lifting");
136
137 let mut expr_lifter = ExpressionLifter::new();
138 let lifted = expr_lifter.lift_expressions(&mut statement);
139 if !lifted.is_empty() {
140 info!(
141 "Applied expression lifting - {} CTEs generated",
142 lifted.len()
143 );
144 trace.log_timed(
145 "OPTIMIZER",
146 "LIFT_COMPLETE",
147 format!("Generated {} lifted CTEs", lifted.len()),
148 lift_timer.elapsed_us(),
149 );
150 } else {
151 trace.log_timed(
152 "OPTIMIZER",
153 "LIFT_COMPLETE",
154 "No expressions needed lifting",
155 lift_timer.elapsed_us(),
156 );
157 }
158
159 let hoist_timer = ScopedTimer::new();
161 trace.log("OPTIMIZER", "HOIST_START", "Starting CTE hoisting");
162
163 let original_cte_count = statement.ctes.len();
164 statement = CTEHoister::hoist_ctes(statement);
165
166 trace.log_timed(
167 "OPTIMIZER",
168 "HOIST_COMPLETE",
169 format!(
170 "CTEs after hoisting: {} (was {})",
171 statement.ctes.len(),
172 original_cte_count
173 ),
174 hoist_timer.elapsed_us(),
175 );
176
177 let uses_dual = statement
178 .from_table
179 .as_ref()
180 .is_some_and(|t| t.to_uppercase() == "DUAL");
181
182 let no_from_clause = statement.from_table.is_none();
183
184 let source_table = if uses_dual || no_from_clause {
186 info!("QueryExecutionService: Using DUAL table for expression evaluation");
187 crate::data::datatable::DataTable::dual()
188 } else if let Some(original) = original_source {
189 info!(
191 "QueryExecutionService: Using original source with {} columns: {:?}",
192 original.column_count(),
193 original.column_names()
194 );
195 debug!(
196 "QueryExecutionService: DEBUG - Using original source with {} columns for query",
197 original.column_count()
198 );
199 original.clone()
200 } else if let Some(view) = current_dataview {
201 info!(
203 "QueryExecutionService: WARNING - No original source, using current view's source with {} columns: {:?}",
204 view.source().column_count(),
205 view.source().column_names()
206 );
207 debug!(
208 "QueryExecutionService: DEBUG WARNING - No original source, using view source with {} columns",
209 view.source().column_count()
210 );
211 view.source().clone()
212 } else {
213 return Err(anyhow::anyhow!("No data loaded"));
214 };
215
216 let table_arc = Arc::new(source_table);
218
219 let query_start = std::time::Instant::now();
221 trace.log(
222 "EXECUTOR",
223 "START",
224 format!(
225 "Executing with {} rows, {} columns",
226 table_arc.row_count(),
227 table_arc.column_count()
228 ),
229 );
230 let engine = if let Some(ref config) = self.behavior_config {
231 QueryEngine::with_behavior_config(config.clone())
232 } else {
233 QueryEngine::with_case_insensitive_and_date_notation(
234 self.case_insensitive,
235 self.date_notation.clone(),
236 )
237 };
238 let mut new_dataview = engine.execute(table_arc, query)?;
239 let query_engine_time = query_start.elapsed();
240
241 trace.log_timed(
242 "EXECUTOR",
243 "COMPLETE",
244 format!("Query returned {} rows", new_dataview.row_count()),
245 query_engine_time.as_micros() as u64,
246 );
247
248 let mut hidden_columns = Vec::new();
250 if self.auto_hide_empty {
251 let hidden = new_dataview.hide_empty_columns();
252 if hidden > 0 {
253 info!("Auto-hidden {} empty columns after query execution", hidden);
254 hidden_columns = vec![format!("{} columns", hidden)];
257 }
258 }
259
260 let stats = QueryStats {
262 row_count: new_dataview.row_count(),
263 column_count: new_dataview.column_count(),
264 execution_time: query_start.elapsed(),
265 query_engine_time,
266 };
267
268 let debug_output = if trace.is_enabled() {
269 Some(trace.format_output())
270 } else {
271 None
272 };
273
274 Ok(QueryExecutionResult {
275 dataview: new_dataview,
276 stats,
277 hidden_columns,
278 query: query.to_string(),
279 execution_plan: None,
280 debug_trace: debug_output,
281 })
282 }
283
284 pub fn set_case_insensitive(&mut self, case_insensitive: bool) {
286 self.case_insensitive = case_insensitive;
287 }
288
289 pub fn set_auto_hide_empty(&mut self, auto_hide: bool) {
290 self.auto_hide_empty = auto_hide;
291 }
292
293 pub fn set_date_notation(&mut self, date_notation: String) {
294 self.date_notation = date_notation;
295 }
296}
297
298impl QueryExecutionResult {
299 #[must_use]
301 pub fn status_message(&self) -> String {
302 let hidden_msg = if self.hidden_columns.is_empty() {
303 String::new()
304 } else {
305 format!(" ({} auto-hidden)", self.hidden_columns.len())
306 };
307
308 format!(
309 "Query executed: {} rows, {} columns{} ({} ms)",
310 self.stats.row_count,
311 self.stats.column_count,
312 hidden_msg,
313 self.stats.execution_time.as_millis()
314 )
315 }
316
317 #[must_use]
319 pub fn column_names(&self) -> Vec<String> {
320 self.dataview.column_names()
321 }
322
323 #[must_use]
325 pub fn table_name(&self) -> String {
326 self.dataview.source().name.clone()
327 }
328}