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