1use itertools::Itertools;
22use std::fmt;
23
24use mz_expr::explain::{HumanizedExplain, HumanizerMode, fmt_text_constant_rows};
25use mz_expr::virtual_syntax::{AlgExcept, Except};
26use mz_expr::{Id, WindowFrame};
27use mz_ore::str::{IndentLike, separated};
28use mz_repr::Diff;
29use mz_repr::explain::text::DisplayText;
30use mz_repr::explain::{CompactScalarSeq, Indices, PlanRenderingContext};
31
32use crate::plan::{AggregateExpr, Hir, HirRelationExpr, HirScalarExpr, JoinKind, WindowExprType};
33
34impl DisplayText<PlanRenderingContext<'_, HirRelationExpr>> for HirRelationExpr {
35 fn fmt_text(
36 &self,
37 f: &mut fmt::Formatter<'_>,
38 ctx: &mut PlanRenderingContext<'_, HirRelationExpr>,
39 ) -> fmt::Result {
40 if ctx.config.raw_syntax {
41 self.fmt_raw_syntax(f, ctx)
42 } else {
43 self.fmt_virtual_syntax(f, ctx)
44 }
45 }
46}
47
48impl HirRelationExpr {
49 fn fmt_virtual_syntax(
50 &self,
51 f: &mut fmt::Formatter<'_>,
52 ctx: &mut PlanRenderingContext<'_, HirRelationExpr>,
53 ) -> fmt::Result {
54 if let Some(Except { all, lhs, rhs }) = Hir::un_except(self) {
55 if all {
56 writeln!(f, "{}ExceptAll", ctx.indent)?;
57 } else {
58 writeln!(f, "{}Except", ctx.indent)?;
59 }
60 ctx.indented(|ctx| {
61 lhs.fmt_text(f, ctx)?;
62 rhs.fmt_text(f, ctx)?;
63 Ok(())
64 })?;
65 } else {
66 self.fmt_raw_syntax(f, ctx)?;
68 }
69
70 Ok(())
71 }
72
73 fn fmt_raw_syntax(
74 &self,
75 f: &mut fmt::Formatter<'_>,
76 ctx: &mut PlanRenderingContext<'_, HirRelationExpr>,
77 ) -> fmt::Result {
78 use HirRelationExpr::*;
79
80 let mode = HumanizedExplain::new(ctx.config.redacted);
81
82 match &self {
83 Constant { rows, .. } => {
84 if !rows.is_empty() {
85 writeln!(f, "{}Constant", ctx.indent)?;
86 ctx.indented(|ctx| {
87 fmt_text_constant_rows(
88 f,
89 rows.iter().map(|row| (row, &Diff::ONE)),
90 &mut ctx.indent,
91 ctx.config.redacted,
92 )
93 })?;
94 } else {
95 writeln!(f, "{}Constant <empty>", ctx.indent)?;
96 }
97 }
98 Let {
99 name,
100 id,
101 value,
102 body,
103 } => {
104 let mut bindings = vec![(id, name, value.as_ref())];
105 let mut head = body.as_ref();
106
107 while let Let {
110 name,
111 id,
112 value,
113 body,
114 } = head
115 {
116 bindings.push((id, name, value.as_ref()));
117 head = body.as_ref();
118 }
119
120 writeln!(f, "{}With", ctx.indent)?;
121 ctx.indented(|ctx| {
122 for (id, name, value) in bindings.iter() {
123 writeln!(f, "{}cte [{} as {}] =", ctx.indent, *id, *name)?;
125 ctx.indented(|ctx| value.fmt_text(f, ctx))?;
126 }
127 Ok(())
128 })?;
129 writeln!(f, "{}Return", ctx.indent)?;
130 ctx.indented(|ctx| head.fmt_text(f, ctx))?;
131 }
132 LetRec {
133 limit,
134 bindings,
135 body,
136 } => {
137 write!(f, "{}With Mutually Recursive", ctx.indent)?;
138 if let Some(limit) = limit {
139 write!(f, " {}", limit)?;
140 }
141 writeln!(f)?;
142 ctx.indented(|ctx| {
143 for (name, id, value, _type) in bindings.iter() {
144 writeln!(f, "{}cte [{} as {}] =", ctx.indent, *id, *name)?;
146 ctx.indented(|ctx| value.fmt_text(f, ctx))?;
147 }
148 Ok(())
149 })?;
150 writeln!(f, "{}Return", ctx.indent)?;
151 ctx.indented(|ctx| body.fmt_text(f, ctx))?;
152 }
153 Get { id, .. } => match id {
154 Id::Local(id) => {
155 writeln!(f, "{}Get {}", ctx.indent, id)?;
157 }
158 Id::Global(id) => {
159 let humanized_id = ctx
160 .humanizer
161 .humanize_id(*id)
162 .unwrap_or_else(|| id.to_string());
163 writeln!(f, "{}Get {}", ctx.indent, humanized_id)?;
164 }
165 },
166 Project { outputs, input } => {
167 let outputs = Indices(outputs);
168 writeln!(f, "{}Project ({})", ctx.indent, outputs)?;
169 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
170 }
171 Map { scalars, input } => {
172 let scalars = CompactScalarSeq(scalars);
173 writeln!(f, "{}Map ({})", ctx.indent, scalars)?;
174 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
175 }
176 CallTable { func, exprs } => {
177 let exprs = CompactScalarSeq(exprs);
178 writeln!(f, "{}CallTable {}({})", ctx.indent, func, exprs)?;
179 }
180 Filter { predicates, input } => {
181 let predicates = separated(" AND ", predicates);
182 writeln!(f, "{}Filter {}", ctx.indent, predicates)?;
183 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
184 }
185 Join {
186 left,
187 right,
188 on,
189 kind,
190 } => {
191 if on.is_literal_true() && kind == &JoinKind::Inner {
192 write!(f, "{}CrossJoin", ctx.indent)?;
193 } else {
194 write!(f, "{}{}Join {}", ctx.indent, kind, on)?;
195 }
196 writeln!(f)?;
197 ctx.indented(|ctx| {
198 left.fmt_text(f, ctx)?;
199 right.fmt_text(f, ctx)?;
200 Ok(())
201 })?;
202 }
203 Reduce {
204 group_key,
205 aggregates,
206 expected_group_size,
207 input,
208 } => {
209 write!(f, "{}Reduce", ctx.indent)?;
210 if group_key.len() > 0 {
211 let group_key = Indices(group_key);
212 write!(f, " group_by=[{}]", group_key)?;
213 }
214 if aggregates.len() > 0 {
215 let aggregates = separated(", ", aggregates);
216 write!(f, " aggregates=[{}]", aggregates)?;
217 }
218 if let Some(expected_group_size) = expected_group_size {
219 write!(f, " exp_group_size={}", expected_group_size)?;
220 }
221 writeln!(f)?;
222 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
223 }
224 Distinct { input } => {
225 writeln!(f, "{}Distinct", ctx.indent)?;
226 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
227 }
228 TopK {
229 group_key,
230 order_key,
231 limit,
232 offset,
233 input,
234 expected_group_size,
235 } => {
236 write!(f, "{}TopK", ctx.indent)?;
237 if group_key.len() > 0 {
238 let group_by = Indices(group_key);
239 write!(f, " group_by=[{}]", group_by)?;
240 }
241 if order_key.len() > 0 {
242 let order_by = mode.seq(order_key, None);
243 let order_by = separated(", ", order_by);
244 write!(f, " order_by=[{}]", order_by)?;
245 }
246 if let Some(limit) = limit {
247 write!(f, " limit={}", limit)?;
248 }
249 if offset > &0 {
250 write!(f, " offset={}", offset)?
251 }
252 if let Some(expected_group_size) = expected_group_size {
253 write!(f, " exp_group_size={}", expected_group_size)?;
254 }
255 writeln!(f)?;
256 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
257 }
258 Negate { input } => {
259 writeln!(f, "{}Negate", ctx.indent)?;
260 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
261 }
262 Threshold { input } => {
263 writeln!(f, "{}Threshold", ctx.indent)?;
264 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
265 }
266 Union { base, inputs } => {
267 writeln!(f, "{}Union", ctx.indent)?;
268 ctx.indented(|ctx| {
269 base.fmt_text(f, ctx)?;
270 for input in inputs.iter() {
271 input.fmt_text(f, ctx)?;
272 }
273 Ok(())
274 })?;
275 }
276 }
277
278 Ok(())
279 }
280}
281
282impl fmt::Display for HirScalarExpr {
283 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284 use HirRelationExpr::Get;
285 use HirScalarExpr::*;
286 match self {
287 Column(i) => write!(
288 f,
289 "#{}{}",
290 (0..i.level).map(|_| '^').collect::<String>(),
291 i.column
292 ),
293 Parameter(i) => write!(f, "${}", i),
294 Literal(row, _) => write!(f, "{}", row.unpack_first()),
295 CallUnmaterializable(func) => write!(f, "{}()", func),
296 CallUnary { func, expr } => {
297 if let mz_expr::UnaryFunc::Not(_) = *func {
298 if let CallUnary { func, expr } = expr.as_ref() {
299 if let Some(is) = func.is() {
300 return write!(f, "({}) IS NOT {}", expr, is);
301 }
302 }
303 }
304 if let Some(is) = func.is() {
305 write!(f, "({}) IS {}", expr, is)
306 } else {
307 write!(f, "{}({})", func, expr)
308 }
309 }
310 CallBinary { func, expr1, expr2 } => {
311 if func.is_infix_op() {
312 write!(f, "({} {} {})", expr1, func, expr2)
313 } else {
314 write!(f, "{}({}, {})", func, expr1, expr2)
315 }
316 }
317 CallVariadic { func, exprs } => {
318 use mz_expr::VariadicFunc::*;
319 match func {
320 ArrayCreate { .. } => {
321 let exprs = separated(", ", exprs);
322 write!(f, "array[{}]", exprs)
323 }
324 ListCreate { .. } => {
325 let exprs = separated(", ", exprs);
326 write!(f, "list[{}]", exprs)
327 }
328 RecordCreate { .. } => {
329 let exprs = separated(", ", exprs);
330 write!(f, "row({})", exprs)
331 }
332 func if func.is_infix_op() && exprs.len() > 1 => {
333 let func = format!(" {} ", func);
334 let exprs = separated(&func, exprs);
335 write!(f, "({})", exprs)
336 }
337 func => {
338 let exprs = separated(", ", exprs);
339 write!(f, "{}({})", func, exprs)
340 }
341 }
342 }
343 If { cond, then, els } => {
344 write!(f, "case when {} then {} else {} end", cond, then, els)
345 }
346 Windowing(expr) => {
347 let (column_orders, ignore_nulls, window_frame) = match &expr.func {
352 WindowExprType::Scalar(scalar_window_expr) => {
353 write!(f, "{}()", scalar_window_expr.func)?;
354 (&scalar_window_expr.order_by, false, None)
355 }
356 WindowExprType::Value(value_window_expr) => {
357 write!(f, "{}({})", value_window_expr.func, value_window_expr.args)?;
358 (
359 &value_window_expr.order_by,
360 value_window_expr.ignore_nulls,
361 Some(&value_window_expr.window_frame),
362 )
363 }
364 WindowExprType::Aggregate(aggregate_window_expr) => {
365 write!(f, "{}", aggregate_window_expr.aggregate_expr)?;
366 (
367 &aggregate_window_expr.order_by,
368 false,
369 Some(&aggregate_window_expr.window_frame),
370 )
371 }
372 };
373
374 assert!(
379 column_orders
380 .iter()
381 .enumerate()
382 .all(|(i, column_order)| i == column_order.column)
383 );
384 let order_by = column_orders
385 .iter()
386 .zip_eq(expr.order_by.iter())
387 .map(|(column_order, expr)| {
388 ColumnOrderWithExpr {
389 expr: expr.clone(),
390 desc: column_order.desc,
391 nulls_last: column_order.nulls_last,
392 }
393 })
395 .collect_vec();
396
397 if ignore_nulls {
399 write!(f, " ignore nulls")?;
400 }
401
402 write!(f, " over (")?;
406 if !expr.partition_by.is_empty() {
407 write!(
408 f,
409 "partition by [{}] ",
410 separated(", ", expr.partition_by.iter())
411 )?;
412 }
413 write!(f, "order by [{}]", separated(", ", order_by.iter()))?;
414 if let Some(window_frame) = window_frame {
415 if *window_frame != WindowFrame::default() {
416 write!(f, " {}", window_frame)?;
417 }
418 }
419 write!(f, ")")?;
420
421 Ok(())
422 }
423 Exists(expr) => match expr.as_ref() {
424 Get { id, .. } => write!(f, "exists(Get {})", id), _ => write!(f, "exists(???)"),
426 },
427 Select(expr) => match expr.as_ref() {
428 Get { id, .. } => write!(f, "select(Get {})", id), _ => write!(f, "select(???)"),
430 },
431 }
432 }
433}
434
435struct ColumnOrderWithExpr {
438 pub expr: HirScalarExpr,
440 pub desc: bool,
442 pub nulls_last: bool,
444}
445
446impl fmt::Display for ColumnOrderWithExpr {
447 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
448 write!(
450 f,
451 "{} {} {}",
452 self.expr,
453 if self.desc { "desc" } else { "asc" },
454 if self.nulls_last {
455 "nulls_last"
456 } else {
457 "nulls_first"
458 },
459 )
460 }
461}
462
463impl fmt::Display for AggregateExpr {
464 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
465 if self.is_count_asterisk() {
466 return write!(f, "count(*)");
467 }
468
469 let mode = HumanizedExplain::new(false);
471 let func = self.func.clone().into_expr();
472 let func = mode.expr(&func, None);
473 let distinct = if self.distinct { "distinct " } else { "" };
474
475 write!(f, "{}({}", func, distinct)?;
476 self.expr.fmt(f)?;
477 write!(f, ")")
478 }
479}