1use crate::sql::parser::ast::{SqlExpression, WhereClause};
5use std::fmt;
6use std::time::{Duration, Instant};
7
8#[derive(Debug, Clone)]
10pub struct ExecutionStep {
11 pub step_type: StepType,
12 pub description: String,
13 pub details: Vec<String>,
14 pub rows_in: Option<usize>,
15 pub rows_out: Option<usize>,
16 pub duration: Option<Duration>,
17 pub children: Vec<ExecutionStep>,
18}
19
20#[derive(Debug, Clone)]
22pub enum StepType {
23 Parse,
24 LoadData,
25 TableScan,
26 Filter,
27 Sort,
28 GroupBy,
29 Having,
30 Select,
31 Limit,
32 Output,
33 Join,
34 WindowFunction,
35 Expression,
36 CTE,
37 Subquery,
38 Aggregate,
39 Distinct,
40}
41
42impl fmt::Display for StepType {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 match self {
45 StepType::Parse => write!(f, "PARSE"),
46 StepType::LoadData => write!(f, "LOAD_DATA"),
47 StepType::TableScan => write!(f, "TABLE_SCAN"),
48 StepType::Filter => write!(f, "FILTER"),
49 StepType::Sort => write!(f, "SORT"),
50 StepType::GroupBy => write!(f, "GROUP_BY"),
51 StepType::Having => write!(f, "HAVING"),
52 StepType::Select => write!(f, "SELECT"),
53 StepType::Limit => write!(f, "LIMIT"),
54 StepType::Output => write!(f, "OUTPUT"),
55 StepType::Join => write!(f, "JOIN"),
56 StepType::WindowFunction => write!(f, "WINDOW"),
57 StepType::Expression => write!(f, "EXPR"),
58 StepType::CTE => write!(f, "CTE"),
59 StepType::Subquery => write!(f, "SUBQUERY"),
60 StepType::Aggregate => write!(f, "AGGREGATE"),
61 StepType::Distinct => write!(f, "DISTINCT"),
62 }
63 }
64}
65
66pub struct ExecutionPlanBuilder {
68 steps: Vec<ExecutionStep>,
69 current_step: Option<ExecutionStep>,
70 start_time: Option<Instant>,
71}
72
73impl ExecutionPlanBuilder {
74 pub fn new() -> Self {
75 Self {
76 steps: Vec::new(),
77 current_step: None,
78 start_time: None,
79 }
80 }
81
82 pub fn begin_step(&mut self, step_type: StepType, description: String) {
84 if let Some(current) = self.current_step.take() {
85 self.steps.push(current);
86 }
87
88 self.current_step = Some(ExecutionStep {
89 step_type,
90 description,
91 details: Vec::new(),
92 rows_in: None,
93 rows_out: None,
94 duration: None,
95 children: Vec::new(),
96 });
97 self.start_time = Some(Instant::now());
98 }
99
100 pub fn add_detail(&mut self, detail: String) {
102 if let Some(ref mut step) = self.current_step {
103 step.details.push(detail);
104 }
105 }
106
107 pub fn set_rows_in(&mut self, count: usize) {
109 if let Some(ref mut step) = self.current_step {
110 step.rows_in = Some(count);
111 }
112 }
113
114 pub fn set_rows_out(&mut self, count: usize) {
116 if let Some(ref mut step) = self.current_step {
117 step.rows_out = Some(count);
118 }
119 }
120
121 pub fn end_step(&mut self) {
123 if let Some(ref mut step) = self.current_step {
124 if let Some(start) = self.start_time {
125 step.duration = Some(start.elapsed());
126 }
127 }
128
129 if let Some(step) = self.current_step.take() {
130 self.steps.push(step);
131 }
132 self.start_time = None;
133 }
134
135 pub fn add_child_step(&mut self, child: ExecutionStep) {
137 if let Some(ref mut step) = self.current_step {
138 step.children.push(child);
139 }
140 }
141
142 pub fn build(mut self) -> ExecutionPlan {
144 if let Some(current) = self.current_step.take() {
145 self.steps.push(current);
146 }
147
148 let total_duration = self.steps.iter().filter_map(|s| s.duration).sum();
149
150 ExecutionPlan {
151 steps: self.steps,
152 total_duration,
153 }
154 }
155}
156
157pub struct ExecutionPlan {
159 pub steps: Vec<ExecutionStep>,
160 pub total_duration: Duration,
161}
162
163impl ExecutionPlan {
164 pub fn format_tree(&self) -> String {
166 let mut output = String::new();
167 output.push_str("\n");
168 output
169 .push_str("╔══════════════════════════════════════════════════════════════════════╗\n");
170 output
171 .push_str("║ EXECUTION PLAN ║\n");
172 output.push_str(
173 "╚══════════════════════════════════════════════════════════════════════╝\n\n",
174 );
175
176 for (i, step) in self.steps.iter().enumerate() {
177 self.format_step(&mut output, step, 0, i == self.steps.len() - 1);
178 }
179
180 output.push_str("\n");
181 output.push_str(&format!(
182 "Total Execution Time: {:.3}ms\n",
183 self.total_duration.as_secs_f64() * 1000.0
184 ));
185
186 output
187 }
188
189 fn format_step(&self, output: &mut String, step: &ExecutionStep, indent: usize, is_last: bool) {
190 let prefix = if indent == 0 {
191 if is_last {
192 "└─".to_string()
193 } else {
194 "├─".to_string()
195 }
196 } else {
197 format!(
198 "{}{}",
199 " ".repeat(indent),
200 if is_last { "└─" } else { "├─" }
201 )
202 };
203
204 let time_str = step
206 .duration
207 .map(|d| format!(" [{:.3}ms]", d.as_secs_f64() * 1000.0))
208 .unwrap_or_default();
209
210 let rows_str = match (step.rows_in, step.rows_out) {
211 (Some(i), Some(o)) if i != o => format!(" (rows: {} → {})", i, o),
212 (_, Some(o)) => format!(" (rows: {})", o),
213 _ => String::new(),
214 };
215
216 output.push_str(&format!(
217 "{} {} {}{}{}\n",
218 prefix, step.step_type, step.description, rows_str, time_str
219 ));
220
221 let detail_indent = if indent == 0 {
223 " ".to_string()
224 } else {
225 " ".repeat(indent + 1)
226 };
227 for detail in &step.details {
228 output.push_str(&format!("{} • {}\n", detail_indent, detail));
229 }
230
231 for (i, child) in step.children.iter().enumerate() {
233 self.format_step(output, child, indent + 1, i == step.children.len() - 1);
234 }
235 }
236
237 pub fn format_table(&self) -> String {
239 let mut output = String::new();
240 output.push_str("\n");
241 output.push_str(
242 "┌────────────────┬──────────────────────────────┬──────────┬──────────┬──────────┐\n",
243 );
244 output.push_str(
245 "│ Step │ Description │ Rows In │ Rows Out │ Time(ms) │\n",
246 );
247 output.push_str(
248 "├────────────────┼──────────────────────────────┼──────────┼──────────┼──────────┤\n",
249 );
250
251 for step in &self.steps {
252 self.format_step_table(&mut output, step, 0);
253 }
254
255 output.push_str(
256 "└────────────────┴──────────────────────────────┴──────────┴──────────┴──────────┘\n",
257 );
258 output.push_str(&format!(
259 "\nTotal Time: {:.3}ms\n",
260 self.total_duration.as_secs_f64() * 1000.0
261 ));
262
263 output
264 }
265
266 fn format_step_table(&self, output: &mut String, step: &ExecutionStep, indent: usize) {
267 let step_name = format!("{}{}", " ".repeat(indent), step.step_type);
268 let desc = if step.description.len() > 28 {
269 format!("{}...", &step.description[..25])
270 } else {
271 step.description.clone()
272 };
273
274 let rows_in = step
275 .rows_in
276 .map(|r| r.to_string())
277 .unwrap_or_else(|| "-".to_string());
278
279 let rows_out = step
280 .rows_out
281 .map(|r| r.to_string())
282 .unwrap_or_else(|| "-".to_string());
283
284 let time = step
285 .duration
286 .map(|d| format!("{:.3}", d.as_secs_f64() * 1000.0))
287 .unwrap_or_else(|| "-".to_string());
288
289 output.push_str(&format!(
290 "│ {:<14} │ {:<28} │ {:>8} │ {:>8} │ {:>8} │\n",
291 step_name, desc, rows_in, rows_out, time
292 ));
293
294 for child in &step.children {
295 self.format_step_table(output, child, indent + 1);
296 }
297 }
298
299 pub fn get_insights(&self) -> Vec<String> {
301 let mut insights = Vec::new();
302
303 let mut steps_with_time: Vec<_> = self
305 .steps
306 .iter()
307 .filter_map(|s| s.duration.map(|d| (s, d)))
308 .collect();
309 steps_with_time.sort_by_key(|(_, d)| std::cmp::Reverse(*d));
310
311 if let Some((slowest, duration)) = steps_with_time.first() {
312 let percentage = (duration.as_secs_f64() / self.total_duration.as_secs_f64()) * 100.0;
313 if percentage > 50.0 {
314 insights.push(format!(
315 "⚠️ {} step took {:.1}% of total execution time ({:.3}ms)",
316 slowest.step_type,
317 percentage,
318 duration.as_secs_f64() * 1000.0
319 ));
320 }
321 }
322
323 for step in &self.steps {
325 if matches!(step.step_type, StepType::Filter) {
326 if let (Some(rows_in), Some(rows_out)) = (step.rows_in, step.rows_out) {
327 if rows_in > 0 {
328 let selectivity = (rows_out as f64 / rows_in as f64) * 100.0;
329 if selectivity < 10.0 {
330 insights.push(format!(
331 "✓ Highly selective filter: {:.1}% of rows passed ({}→{})",
332 selectivity, rows_in, rows_out
333 ));
334 } else if selectivity > 90.0 {
335 insights.push(format!(
336 "⚠️ Low selectivity filter: {:.1}% of rows passed ({}→{})",
337 selectivity, rows_in, rows_out
338 ));
339 }
340 }
341 }
342 }
343 }
344
345 for step in &self.steps {
347 if matches!(step.step_type, StepType::Sort) {
348 if let Some(rows) = step.rows_in {
349 if rows > 10000 {
350 insights.push(format!("⚠️ Sorting large dataset: {} rows", rows));
351 }
352 }
353 }
354 }
355
356 insights
357 }
358}
359
360pub fn analyze_where_clause(where_clause: &WhereClause) -> Vec<String> {
362 let mut details = Vec::new();
363
364 let condition_count = where_clause.conditions.len();
366 details.push(format!("Conditions: {}", condition_count));
367
368 for (i, condition) in where_clause.conditions.iter().enumerate() {
370 let expr_detail = analyze_expression(&condition.expr);
371 details.push(format!(" Condition {}: {}", i + 1, expr_detail));
372
373 if let Some(connector) = &condition.connector {
374 details.push(format!(" Connector: {:?}", connector));
375 }
376 }
377
378 details
379}
380
381pub fn analyze_expression(expr: &SqlExpression) -> String {
383 match expr {
384 SqlExpression::Column(name) => format!("Column({})", name),
385 SqlExpression::BinaryOp { left, op, right } => {
386 format!(
387 "BinaryOp({} {} {})",
388 analyze_expression(left),
389 op,
390 analyze_expression(right)
391 )
392 }
393 SqlExpression::FunctionCall { name, args, .. } => {
394 format!("Function({}, {} args)", name, args.len())
395 }
396 SqlExpression::Between { expr, .. } => {
397 format!("Between({})", analyze_expression(expr))
398 }
399 SqlExpression::InList { expr, values } => {
400 format!(
401 "InList({}, {} values)",
402 analyze_expression(expr),
403 values.len()
404 )
405 }
406 SqlExpression::NotInList { expr, values } => {
407 format!(
408 "NotInList({}, {} values)",
409 analyze_expression(expr),
410 values.len()
411 )
412 }
413 _ => format!("{:?}", expr),
414 }
415}