1use std::panic::AssertUnwindSafe;
13
14use mz_expr::explain::{ExplainContext, ExplainSinglePlan};
15use mz_expr::visit::{Visit, VisitChildren};
16use mz_expr::{Id, LocalId};
17use mz_repr::SqlRelationType;
18use mz_repr::explain::{AnnotatedPlan, Explain, ExplainError, ScalarOps, UnsupportedFormat};
19
20use crate::plan::{HirRelationExpr, HirScalarExpr};
21
22mod text;
23
24impl<'a> Explain<'a> for HirRelationExpr {
25 type Context = ExplainContext<'a>;
26
27 type Text = ExplainSinglePlan<'a, HirRelationExpr>;
28
29 type Json = ExplainSinglePlan<'a, HirRelationExpr>;
30
31 type Dot = UnsupportedFormat;
32
33 fn explain_text(&'a mut self, context: &'a Self::Context) -> Result<Self::Text, ExplainError> {
34 self.as_explain_single_plan(context)
35 }
36
37 fn explain_json(&'a mut self, context: &'a Self::Context) -> Result<Self::Json, ExplainError> {
38 self.as_explain_single_plan(context)
39 }
40}
41
42impl<'a> HirRelationExpr {
43 fn as_explain_single_plan(
44 &'a mut self,
45 context: &'a ExplainContext<'a>,
46 ) -> Result<ExplainSinglePlan<'a, HirRelationExpr>, ExplainError> {
47 if !context.config.raw_plans {
51 mz_ore::panic::catch_unwind_str(AssertUnwindSafe(|| {
52 normalize_subqueries(self);
53 Ok(())
54 }))
55 .unwrap_or_else(|panic| {
56 tracing::error!("caught a panic during `normalize_subqueries`: {panic}");
59
60 let msg = format!("unexpected panic during `normalize_subqueries`: {panic}");
61 Err(ExplainError::UnknownError(msg))
62 })?
63 }
64
65 let plan = AnnotatedPlan {
68 plan: self,
69 annotations: Default::default(),
70 };
71 Ok(ExplainSinglePlan { context, plan })
72 }
73}
74
75pub fn normalize_subqueries<'a>(expr: &'a mut HirRelationExpr) {
84 struct Binding {
88 local_id: LocalId,
89 subquery: HirRelationExpr,
90 }
91
92 let mut bindings = Vec::<Binding>::new();
95 let mut id_gen = id_gen(expr).peekable();
97
98 let mut collect_subqueries = |expr: &mut HirRelationExpr, bindings: &mut Vec<Binding>| {
103 expr.visit_mut_children(|expr: &mut HirScalarExpr| {
104 use HirRelationExpr::Get;
105 use HirScalarExpr::{Exists, Select};
106 expr.visit_mut_post(&mut |expr: &mut HirScalarExpr| match expr {
107 Exists(expr, _) | Select(expr, _) => match expr.as_mut() {
108 Get { .. } => (),
109 expr => {
110 let local_id = id_gen.next().unwrap();
112 let mut subquery = Get {
114 id: Id::Local(local_id.clone()),
115 typ: SqlRelationType::empty(), };
117 std::mem::swap(expr, &mut subquery);
119 bindings.push(Binding { local_id, subquery });
121 }
122 },
123 _ => (),
124 });
125 });
126 };
127
128 let insert_let_bindings = |expr: &mut HirRelationExpr, bindings: &mut Vec<Binding>| {
131 for binding in bindings.drain(..) {
132 let name = format!("subquery-{}", Into::<u64>::into(&binding.local_id));
133 let id = binding.local_id;
134 let value = Box::new(binding.subquery);
135 let body = Box::new(expr.take());
136 *expr = HirRelationExpr::Let {
137 name,
138 id,
139 value,
140 body,
141 }
142 }
143 };
144
145 expr.visit_mut_post(&mut |expr: &mut HirRelationExpr| {
146 collect_subqueries(expr, &mut bindings);
148 insert_let_bindings(expr, &mut bindings);
150 })
151}
152
153fn id_gen(expr: &HirRelationExpr) -> impl Iterator<Item = LocalId> + use<> {
156 let mut max_id = 0_u64;
157
158 expr.visit_pre(&mut |expr| {
159 match expr {
160 HirRelationExpr::Let { id, .. } => max_id = std::cmp::max(max_id, id.into()),
161 _ => (),
162 };
163 });
164
165 (max_id + 1..).map(LocalId::new)
166}
167
168impl ScalarOps for HirScalarExpr {
169 fn match_col_ref(&self) -> Option<usize> {
170 match self {
171 HirScalarExpr::Column(c, _name) if c.level == 0 => Some(c.column),
172 _ => None,
173 }
174 }
175
176 fn references(&self, column: usize) -> bool {
177 match self {
178 HirScalarExpr::Column(c, _name) => c.column == column && c.level == 0,
179 _ => false,
180 }
181 }
182}