1use crate::expressions::{Expression, JoinKind};
8use serde::{Deserialize, Serialize};
9use std::collections::{HashMap, HashSet};
10
11#[derive(Debug)]
13pub struct Plan {
14 pub root: Step,
16 dag: Option<HashMap<usize, HashSet<usize>>>,
18}
19
20impl Plan {
21 pub fn from_expression(expression: &Expression) -> Option<Self> {
23 let root = Step::from_expression(expression, &HashMap::new())?;
24 Some(Self { root, dag: None })
25 }
26
27 pub fn dag(&mut self) -> &HashMap<usize, HashSet<usize>> {
29 if self.dag.is_none() {
30 let mut dag = HashMap::new();
31 let mut next_id = 0;
32 Self::build_dag(&self.root, &mut dag, &mut next_id);
33 self.dag = Some(dag);
34 }
35 self.dag.as_ref().unwrap()
36 }
37
38 fn build_dag(
39 step: &Step,
40 dag: &mut HashMap<usize, HashSet<usize>>,
41 next_id: &mut usize,
42 ) -> usize {
43 let id = *next_id;
44 *next_id += 1;
45
46 let dependencies = step
47 .dependencies
48 .iter()
49 .map(|dependency| Self::build_dag(dependency, dag, next_id))
50 .collect();
51 dag.insert(id, dependencies);
52 id
53 }
54
55 pub fn leaves(&self) -> Vec<&Step> {
57 let mut leaves = Vec::new();
58 self.collect_leaves(&self.root, &mut leaves);
59 leaves
60 }
61
62 fn collect_leaves<'a>(&'a self, step: &'a Step, leaves: &mut Vec<&'a Step>) {
63 if step.dependencies.is_empty() {
64 leaves.push(step);
65 } else {
66 for dep in &step.dependencies {
67 self.collect_leaves(dep, leaves);
68 }
69 }
70 }
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct Step {
76 pub name: String,
78 pub kind: StepKind,
80 pub projections: Vec<Expression>,
82 pub dependencies: Vec<Step>,
84 pub aggregations: Vec<Expression>,
86 pub group_by: Vec<Expression>,
88 pub condition: Option<Expression>,
90 pub order_by: Vec<Expression>,
92 pub limit: Option<Expression>,
94}
95
96#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
98#[serde(rename_all = "snake_case")]
99pub enum StepKind {
100 Scan,
102 Join(JoinType),
104 Aggregate,
106 Sort,
108 SetOperation(SetOperationType),
110}
111
112#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
114#[serde(rename_all = "snake_case")]
115pub enum JoinType {
116 Inner,
117 Left,
118 Right,
119 Full,
120 Cross,
121}
122
123#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
125#[serde(rename_all = "snake_case")]
126pub enum SetOperationType {
127 Union,
128 UnionAll,
129 Intersect,
130 Except,
131}
132
133impl Step {
134 pub fn new(name: impl Into<String>, kind: StepKind) -> Self {
136 Self {
137 name: name.into(),
138 kind,
139 projections: Vec::new(),
140 dependencies: Vec::new(),
141 aggregations: Vec::new(),
142 group_by: Vec::new(),
143 condition: None,
144 order_by: Vec::new(),
145 limit: None,
146 }
147 }
148
149 pub fn from_expression(expression: &Expression, ctes: &HashMap<String, Step>) -> Option<Self> {
151 match expression {
152 Expression::Select(select) => {
153 let mut step = Self::from_select(select, ctes)?;
154
155 if let Some(ref order_by) = select.order_by {
157 let sort_step = Step {
158 name: step.name.clone(),
159 kind: StepKind::Sort,
160 projections: Vec::new(),
161 dependencies: vec![step],
162 aggregations: Vec::new(),
163 group_by: Vec::new(),
164 condition: None,
165 order_by: order_by
166 .expressions
167 .iter()
168 .map(|o| o.this.clone())
169 .collect(),
170 limit: None,
171 };
172 step = sort_step;
173 }
174
175 if let Some(ref limit) = select.limit {
177 step.limit = Some(limit.this.clone());
178 }
179
180 Some(step)
181 }
182 Expression::Union(union) => {
183 let left = Self::from_expression(&union.left, ctes)?;
184 let right = Self::from_expression(&union.right, ctes)?;
185
186 let op_type = if union.all {
187 SetOperationType::UnionAll
188 } else {
189 SetOperationType::Union
190 };
191
192 Some(Step {
193 name: "UNION".to_string(),
194 kind: StepKind::SetOperation(op_type),
195 projections: Vec::new(),
196 dependencies: vec![left, right],
197 aggregations: Vec::new(),
198 group_by: Vec::new(),
199 condition: None,
200 order_by: Vec::new(),
201 limit: None,
202 })
203 }
204 Expression::Intersect(intersect) => {
205 let left = Self::from_expression(&intersect.left, ctes)?;
206 let right = Self::from_expression(&intersect.right, ctes)?;
207
208 Some(Step {
209 name: "INTERSECT".to_string(),
210 kind: StepKind::SetOperation(SetOperationType::Intersect),
211 projections: Vec::new(),
212 dependencies: vec![left, right],
213 aggregations: Vec::new(),
214 group_by: Vec::new(),
215 condition: None,
216 order_by: Vec::new(),
217 limit: None,
218 })
219 }
220 Expression::Except(except) => {
221 let left = Self::from_expression(&except.left, ctes)?;
222 let right = Self::from_expression(&except.right, ctes)?;
223
224 Some(Step {
225 name: "EXCEPT".to_string(),
226 kind: StepKind::SetOperation(SetOperationType::Except),
227 projections: Vec::new(),
228 dependencies: vec![left, right],
229 aggregations: Vec::new(),
230 group_by: Vec::new(),
231 condition: None,
232 order_by: Vec::new(),
233 limit: None,
234 })
235 }
236 _ => None,
237 }
238 }
239
240 fn from_select(
241 select: &crate::expressions::Select,
242 ctes: &HashMap<String, Step>,
243 ) -> Option<Self> {
244 let mut ctes = ctes.clone();
246 if let Some(ref with) = select.with {
247 for cte in &with.ctes {
248 if let Some(step) = Self::from_expression(&cte.this, &ctes) {
249 ctes.insert(cte.alias.name.clone(), step);
250 }
251 }
252 }
253
254 let mut step = if let Some(ref from) = select.from {
256 if let Some(table_expr) = from.expressions.first() {
257 Self::from_table_expression(table_expr, &ctes)?
258 } else {
259 return None;
260 }
261 } else {
262 Step::new("", StepKind::Scan)
264 };
265
266 for join in &select.joins {
268 let right = Self::from_table_expression(&join.this, &ctes)?;
269
270 let join_type = match join.kind {
271 JoinKind::Inner => JoinType::Inner,
272 JoinKind::Left | JoinKind::NaturalLeft => JoinType::Left,
273 JoinKind::Right | JoinKind::NaturalRight => JoinType::Right,
274 JoinKind::Full | JoinKind::NaturalFull => JoinType::Full,
275 JoinKind::Cross | JoinKind::Natural => JoinType::Cross,
276 _ => JoinType::Inner,
277 };
278
279 let join_step = Step {
280 name: step.name.clone(),
281 kind: StepKind::Join(join_type),
282 projections: Vec::new(),
283 dependencies: vec![step, right],
284 aggregations: Vec::new(),
285 group_by: Vec::new(),
286 condition: join.on.clone(),
287 order_by: Vec::new(),
288 limit: None,
289 };
290 step = join_step;
291 }
292
293 let has_aggregations = select.expressions.iter().any(|e| contains_aggregate(e));
295 let has_group_by = select.group_by.is_some();
296
297 if has_aggregations || has_group_by {
298 let agg_step = Step {
300 name: step.name.clone(),
301 kind: StepKind::Aggregate,
302 projections: select.expressions.clone(),
303 dependencies: vec![step],
304 aggregations: extract_aggregations(&select.expressions),
305 group_by: select
306 .group_by
307 .as_ref()
308 .map(|g| g.expressions.clone())
309 .unwrap_or_default(),
310 condition: None,
311 order_by: Vec::new(),
312 limit: None,
313 };
314 step = agg_step;
315 } else {
316 step.projections = select.expressions.clone();
317 }
318
319 Some(step)
320 }
321
322 fn from_table_expression(expr: &Expression, ctes: &HashMap<String, Step>) -> Option<Self> {
323 match expr {
324 Expression::Table(table) => {
325 if let Some(cte_step) = ctes.get(&table.name.name) {
327 return Some(cte_step.clone());
328 }
329
330 Some(Step::new(&table.name.name, StepKind::Scan))
332 }
333 Expression::Alias(alias) => {
334 let mut step = Self::from_table_expression(&alias.this, ctes)?;
335 step.name = alias.alias.name.clone();
336 Some(step)
337 }
338 Expression::Subquery(sq) => {
339 let step = Self::from_expression(&sq.this, ctes)?;
340 Some(step)
341 }
342 _ => None,
343 }
344 }
345
346 pub fn add_dependency(&mut self, dep: Step) {
348 self.dependencies.push(dep);
349 }
350}
351
352fn contains_aggregate(expr: &Expression) -> bool {
354 if crate::traversal::is_aggregate(expr) {
355 return true;
356 }
357
358 match expr {
359 Expression::Alias(alias) => contains_aggregate(&alias.this),
360 Expression::Add(op) | Expression::Sub(op) | Expression::Mul(op) | Expression::Div(op) => {
361 contains_aggregate(&op.left) || contains_aggregate(&op.right)
362 }
363 Expression::Function(func) => func.args.iter().any(contains_aggregate),
364 _ => false,
365 }
366}
367
368fn extract_aggregations(expressions: &[Expression]) -> Vec<Expression> {
370 let mut aggs = Vec::new();
371 for expr in expressions {
372 collect_aggregations(expr, &mut aggs);
373 }
374 aggs
375}
376
377fn collect_aggregations(expr: &Expression, aggs: &mut Vec<Expression>) {
378 if crate::traversal::is_aggregate(expr) {
379 aggs.push(expr.clone());
380 return;
381 }
382
383 match expr {
384 Expression::Alias(alias) => {
385 collect_aggregations(&alias.this, aggs);
386 }
387 Expression::Add(op) | Expression::Sub(op) | Expression::Mul(op) | Expression::Div(op) => {
388 collect_aggregations(&op.left, aggs);
389 collect_aggregations(&op.right, aggs);
390 }
391 Expression::Function(func) => {
392 for arg in &func.args {
393 collect_aggregations(arg, aggs);
394 }
395 }
396 _ => {}
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403 use crate::dialects::{Dialect, DialectType};
404
405 fn parse(sql: &str) -> Expression {
406 let dialect = Dialect::get(DialectType::Generic);
407 let ast = dialect.parse(sql).unwrap();
408 ast.into_iter().next().unwrap()
409 }
410
411 #[test]
412 fn test_simple_scan() {
413 let sql = "SELECT a, b FROM t";
414 let expr = parse(sql);
415 let plan = Plan::from_expression(&expr);
416
417 assert!(plan.is_some());
418 let plan = plan.unwrap();
419 assert_eq!(plan.root.kind, StepKind::Scan);
420 assert_eq!(plan.root.name, "t");
421 }
422
423 #[test]
424 fn test_join() {
425 let sql = "SELECT t1.a, t2.b FROM t1 JOIN t2 ON t1.id = t2.id";
426 let expr = parse(sql);
427 let plan = Plan::from_expression(&expr);
428
429 assert!(plan.is_some());
430 let plan = plan.unwrap();
431 assert!(matches!(plan.root.kind, StepKind::Join(_)));
432 assert_eq!(plan.root.dependencies.len(), 2);
433 }
434
435 #[test]
436 fn test_nested_join_dag_has_unique_preorder_ids() {
437 let expr = parse(
438 "SELECT t1.a FROM t1 \
439 JOIN t2 ON t1.id = t2.id \
440 JOIN t3 ON t2.id = t3.id",
441 );
442 let mut plan = Plan::from_expression(&expr).unwrap();
443 let dag = plan.dag();
444
445 assert_eq!(dag.len(), 5);
446 assert_eq!(dag.get(&0), Some(&HashSet::from([1, 4])));
447 assert_eq!(dag.get(&1), Some(&HashSet::from([2, 3])));
448 assert_eq!(dag.get(&2), Some(&HashSet::new()));
449 assert_eq!(dag.get(&3), Some(&HashSet::new()));
450 assert_eq!(dag.get(&4), Some(&HashSet::new()));
451
452 assert!(dag
453 .values()
454 .flat_map(|dependencies| dependencies.iter())
455 .all(|dependency| dag.contains_key(dependency)));
456 }
457
458 #[test]
459 fn test_aggregate() {
460 let sql = "SELECT x, SUM(y) FROM t GROUP BY x";
461 let expr = parse(sql);
462 let plan = Plan::from_expression(&expr);
463
464 assert!(plan.is_some());
465 let plan = plan.unwrap();
466 assert_eq!(plan.root.kind, StepKind::Aggregate);
467 }
468
469 #[test]
470 fn test_union() {
471 let sql = "SELECT a FROM t1 UNION SELECT b FROM t2";
472 let expr = parse(sql);
473 let plan = Plan::from_expression(&expr);
474
475 assert!(plan.is_some());
476 let plan = plan.unwrap();
477 assert!(matches!(
478 plan.root.kind,
479 StepKind::SetOperation(SetOperationType::Union)
480 ));
481 }
482
483 #[test]
484 fn test_contains_aggregate() {
485 let select_with_agg = parse("SELECT SUM(x) FROM t");
487 if let Expression::Select(ref sel) = select_with_agg {
488 assert!(!sel.expressions.is_empty());
489 assert!(
490 contains_aggregate(&sel.expressions[0]),
491 "Expected SUM to be detected as aggregate function"
492 );
493 } else {
494 panic!("Expected SELECT expression");
495 }
496
497 let select_without_agg = parse("SELECT x + 1 FROM t");
499 if let Expression::Select(ref sel) = select_without_agg {
500 assert!(!sel.expressions.is_empty());
501 assert!(
502 !contains_aggregate(&sel.expressions[0]),
503 "Expected x + 1 to not be an aggregate function"
504 );
505 } else {
506 panic!("Expected SELECT expression");
507 }
508 }
509}