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