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 pub fn new(case_insensitive: bool, auto_hide_empty: bool) -> Self {
42 Self {
43 case_insensitive,
44 auto_hide_empty,
45 date_notation: "us".to_string(),
46 behavior_config: None,
47 }
48 }
49
50 pub fn with_behavior_config(behavior_config: BehaviorConfig) -> Self {
51 let case_insensitive = behavior_config.case_insensitive_default;
52 let auto_hide_empty = behavior_config.hide_empty_columns;
53 let date_notation = behavior_config.default_date_notation.clone();
54 Self {
55 case_insensitive,
56 auto_hide_empty,
57 date_notation,
58 behavior_config: Some(behavior_config),
59 }
60 }
61
62 pub fn with_date_notation(
63 case_insensitive: bool,
64 auto_hide_empty: bool,
65 date_notation: String,
66 ) -> Self {
67 Self {
68 case_insensitive,
69 auto_hide_empty,
70 date_notation,
71 behavior_config: None,
72 }
73 }
74
75 pub fn execute(
78 &self,
79 query: &str,
80 current_dataview: Option<&DataView>,
81 original_source: Option<&crate::data::datatable::DataTable>,
82 ) -> Result<QueryExecutionResult> {
83 use crate::sql::recursive_parser::Parser;
85 let mut parser = Parser::new(query);
86 let statement = parser
87 .parse()
88 .map_err(|e| anyhow::anyhow!("Parse error: {}", e))?;
89
90 let uses_dual = statement
91 .from_table
92 .as_ref()
93 .map(|t| t.to_uppercase() == "DUAL")
94 .unwrap_or(false);
95
96 let no_from_clause = statement.from_table.is_none();
97
98 let source_table = if uses_dual || no_from_clause {
100 info!("QueryExecutionService: Using DUAL table for expression evaluation");
101 crate::data::datatable::DataTable::dual()
102 } else if let Some(original) = original_source {
103 info!(
105 "QueryExecutionService: Using original source with {} columns: {:?}",
106 original.column_count(),
107 original.column_names()
108 );
109 debug!(
110 "QueryExecutionService: DEBUG - Using original source with {} columns for query",
111 original.column_count()
112 );
113 original.clone()
114 } else if let Some(view) = current_dataview {
115 info!(
117 "QueryExecutionService: WARNING - No original source, using current view's source with {} columns: {:?}",
118 view.source().column_count(),
119 view.source().column_names()
120 );
121 debug!(
122 "QueryExecutionService: DEBUG WARNING - No original source, using view source with {} columns",
123 view.source().column_count()
124 );
125 view.source().clone()
126 } else {
127 return Err(anyhow::anyhow!("No data loaded"));
128 };
129
130 let table_arc = Arc::new(source_table);
132
133 let query_start = std::time::Instant::now();
135 let engine = if let Some(ref config) = self.behavior_config {
136 QueryEngine::with_behavior_config(config.clone())
137 } else {
138 QueryEngine::with_case_insensitive_and_date_notation(
139 self.case_insensitive,
140 self.date_notation.clone(),
141 )
142 };
143 let mut new_dataview = engine.execute(table_arc, query)?;
144 let query_engine_time = query_start.elapsed();
145
146 let mut hidden_columns = Vec::new();
148 if self.auto_hide_empty {
149 let hidden = new_dataview.hide_empty_columns();
150 if hidden > 0 {
151 info!("Auto-hidden {} empty columns after query execution", hidden);
152 hidden_columns = vec![format!("{} columns", hidden)];
155 }
156 }
157
158 let stats = QueryStats {
160 row_count: new_dataview.row_count(),
161 column_count: new_dataview.column_count(),
162 execution_time: query_start.elapsed(),
163 query_engine_time,
164 };
165
166 Ok(QueryExecutionResult {
167 dataview: new_dataview,
168 stats,
169 hidden_columns,
170 query: query.to_string(),
171 })
172 }
173
174 pub fn set_case_insensitive(&mut self, case_insensitive: bool) {
176 self.case_insensitive = case_insensitive;
177 }
178
179 pub fn set_auto_hide_empty(&mut self, auto_hide: bool) {
180 self.auto_hide_empty = auto_hide;
181 }
182
183 pub fn set_date_notation(&mut self, date_notation: String) {
184 self.date_notation = date_notation;
185 }
186}
187
188impl QueryExecutionResult {
189 pub fn status_message(&self) -> String {
191 let hidden_msg = if !self.hidden_columns.is_empty() {
192 format!(" ({} auto-hidden)", self.hidden_columns.len())
193 } else {
194 String::new()
195 };
196
197 format!(
198 "Query executed: {} rows, {} columns{} ({} ms)",
199 self.stats.row_count,
200 self.stats.column_count,
201 hidden_msg,
202 self.stats.execution_time.as_millis()
203 )
204 }
205
206 pub fn column_names(&self) -> Vec<String> {
208 self.dataview.column_names()
209 }
210
211 pub fn table_name(&self) -> String {
213 self.dataview.source().name.clone()
214 }
215}