1use std::fmt;
24use std::ops::Deref;
25
26use itertools::{Itertools, izip};
27use mz_expr::explain::{HumanizedExplain, HumanizerMode, fmt_text_constant_rows};
28use mz_expr::{Id, MirScalarExpr};
29use mz_ore::str::{IndentLike, StrExt, separated};
30use mz_repr::explain::text::DisplayText;
31use mz_repr::explain::{
32 CompactScalarSeq, CompactScalars, ExplainConfig, Indices, PlanRenderingContext,
33};
34
35use crate::plan::join::delta_join::{DeltaPathPlan, DeltaStagePlan};
36use crate::plan::join::linear_join::LinearStagePlan;
37use crate::plan::join::{DeltaJoinPlan, JoinClosure, LinearJoinPlan};
38use crate::plan::reduce::{
39 AccumulablePlan, BasicPlan, CollationPlan, HierarchicalPlan, SingleBasicPlan,
40};
41use crate::plan::{AvailableCollections, LirId, Plan, PlanNode};
42
43impl DisplayText<PlanRenderingContext<'_, Plan>> for Plan {
44 fn fmt_text(
45 &self,
46 f: &mut fmt::Formatter<'_>,
47 ctx: &mut PlanRenderingContext<'_, Plan>,
48 ) -> fmt::Result {
49 use PlanNode::*;
50
51 let mode = HumanizedExplain::new(ctx.config.redacted);
52 let annotations = PlanAnnotations::new(ctx.config.clone(), self);
53
54 match &self.node {
55 Constant { rows } => match rows {
56 Ok(rows) => {
57 if !rows.is_empty() {
58 writeln!(f, "{}Constant{}", ctx.indent, annotations)?;
59 ctx.indented(|ctx| {
60 fmt_text_constant_rows(
61 f,
62 rows.iter().map(|(data, _, diff)| (data, diff)),
63 &mut ctx.indent,
64 ctx.config.redacted,
65 )
66 })?;
67 } else {
68 writeln!(f, "{}Constant <empty>{}", ctx.indent, annotations)?;
69 }
70 }
71 Err(err) => {
72 if mode.redacted() {
73 writeln!(f, "{}Error █{}", ctx.indent, annotations)?;
74 } else {
75 writeln!(
76 f,
77 "{}Error {}{}",
78 ctx.indent,
79 err.to_string().quoted(),
80 annotations
81 )?;
82 }
83 }
84 },
85 Get { id, keys, plan } => {
86 ctx.indent.set(); let id = match id {
90 Id::Local(id) => id.to_string(),
91 Id::Global(id) => ctx
92 .humanizer
93 .humanize_id(*id)
94 .unwrap_or_else(|| id.to_string()),
95 };
96 use crate::plan::GetPlan;
98 match plan {
99 GetPlan::PassArrangements => {
100 writeln!(
101 f,
102 "{}Get::PassArrangements {}{}",
103 ctx.indent, id, annotations
104 )?;
105 ctx.indent += 1;
106 }
107 GetPlan::Arrangement(key, val, mfp) => {
108 writeln!(f, "{}Get::Arrangement {}{}", ctx.indent, id, annotations)?;
109 ctx.indent += 1;
110 mode.expr(mfp, None).fmt_text(f, ctx)?;
111 {
112 let key = mode.seq(key, None);
113 let key = CompactScalars(key);
114 writeln!(f, "{}key={}", ctx.indent, key)?;
115 }
116 if let Some(val) = val {
117 let val = mode.expr(val, None);
118 writeln!(f, "{}val={}", ctx.indent, val)?;
119 }
120 }
121 GetPlan::Collection(mfp) => {
122 writeln!(f, "{}Get::Collection {}{}", ctx.indent, id, annotations)?;
123 ctx.indent += 1;
124 mode.expr(mfp, None).fmt_text(f, ctx)?;
125 }
126 }
127
128 keys.fmt_text(f, ctx)?;
130
131 ctx.indent.reset(); }
133 Let { id, value, body } => {
134 let mut bindings = vec![(id, value.as_ref())];
135 let mut head = body.as_ref();
136
137 while let Let { id, value, body } = &head.node {
140 bindings.push((id, value.as_ref()));
141 head = body.as_ref();
142 }
143
144 writeln!(f, "{}With", ctx.indent)?;
145 ctx.indented(|ctx| {
146 for (id, value) in bindings.iter() {
147 writeln!(f, "{}cte {} =", ctx.indent, *id)?;
148 ctx.indented(|ctx| value.fmt_text(f, ctx))?;
149 }
150 Ok(())
151 })?;
152 writeln!(f, "{}Return{}", ctx.indent, annotations)?;
153 ctx.indented(|ctx| head.fmt_text(f, ctx))?;
154 }
155 LetRec {
156 ids,
157 values,
158 limits,
159 body,
160 } => {
161 let bindings = izip!(ids.iter(), values, limits).collect_vec();
162 let head = body.as_ref();
163
164 writeln!(f, "{}With Mutually Recursive", ctx.indent)?;
165 ctx.indented(|ctx| {
166 for (id, value, limit) in bindings.iter() {
167 if let Some(limit) = limit {
168 writeln!(f, "{}cte {} {} =", ctx.indent, limit, *id)?;
169 } else {
170 writeln!(f, "{}cte {} =", ctx.indent, *id)?;
171 }
172 ctx.indented(|ctx| value.fmt_text(f, ctx))?;
173 }
174 Ok(())
175 })?;
176 writeln!(f, "{}Return{}", ctx.indent, annotations)?;
177 ctx.indented(|ctx| head.fmt_text(f, ctx))?;
178 }
179 Mfp {
180 input,
181 mfp,
182 input_key_val,
183 } => {
184 writeln!(f, "{}Mfp{}", ctx.indent, annotations)?;
185 ctx.indented(|ctx| {
186 mode.expr(mfp, None).fmt_text(f, ctx)?;
187 if let Some((key, val)) = input_key_val {
188 {
189 let key = mode.seq(key, None);
190 let key = CompactScalars(key);
191 writeln!(f, "{}input_key={}", ctx.indent, key)?;
192 }
193 if let Some(val) = val {
194 let val = mode.expr(val, None);
195 writeln!(f, "{}input_val={}", ctx.indent, val)?;
196 }
197 }
198 input.fmt_text(f, ctx)
199 })?;
200 }
201 FlatMap {
202 input,
203 func,
204 exprs,
205 mfp_after,
206 input_key,
207 } => {
208 let exprs = mode.seq(exprs, None);
209 let exprs = CompactScalars(exprs);
210 writeln!(
211 f,
212 "{}FlatMap {}({}){}",
213 ctx.indent, func, exprs, annotations
214 )?;
215 ctx.indented(|ctx| {
216 if !mfp_after.is_identity() {
217 writeln!(f, "{}mfp_after", ctx.indent)?;
218 ctx.indented(|ctx| mode.expr(mfp_after, None).fmt_text(f, ctx))?;
219 }
220 if let Some(key) = input_key {
221 let key = mode.seq(key, None);
222 let key = CompactScalars(key);
223 writeln!(f, "{}input_key={}", ctx.indent, key)?;
224 }
225 input.fmt_text(f, ctx)
226 })?;
227 }
228 Join { inputs, plan } => {
229 use crate::plan::join::JoinPlan;
230 match plan {
231 JoinPlan::Linear(plan) => {
232 writeln!(f, "{}Join::Linear{}", ctx.indent, annotations)?;
233 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
234 }
235 JoinPlan::Delta(plan) => {
236 writeln!(f, "{}Join::Delta{}", ctx.indent, annotations)?;
237 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
238 }
239 }
240 ctx.indented(|ctx| {
241 for input in inputs {
242 input.fmt_text(f, ctx)?;
243 }
244 Ok(())
245 })?;
246 }
247 Reduce {
248 input,
249 key_val_plan,
250 plan,
251 input_key,
252 mfp_after,
253 } => {
254 use crate::plan::reduce::ReducePlan;
255 match plan {
256 ReducePlan::Distinct => {
257 writeln!(f, "{}Reduce::Distinct{}", ctx.indent, annotations)?;
258 }
259 ReducePlan::Accumulable(plan) => {
260 writeln!(f, "{}Reduce::Accumulable{}", ctx.indent, annotations)?;
261 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
262 }
263 ReducePlan::Hierarchical(plan) => {
264 writeln!(f, "{}Reduce::Hierarchical{}", ctx.indent, annotations)?;
265 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
266 }
267 ReducePlan::Basic(plan) => {
268 writeln!(f, "{}Reduce::Basic{}", ctx.indent, annotations)?;
269 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
270 }
271 ReducePlan::Collation(plan) => {
272 writeln!(f, "{}Reduce::Collation{}", ctx.indent, annotations)?;
273 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
274 }
275 }
276 ctx.indented(|ctx| {
277 if key_val_plan.val_plan.deref().is_identity() {
278 writeln!(f, "{}val_plan=id", ctx.indent)?;
279 } else {
280 writeln!(f, "{}val_plan", ctx.indent)?;
281 ctx.indented(|ctx| {
282 let val_plan = mode.expr(key_val_plan.val_plan.deref(), None);
283 val_plan.fmt_text(f, ctx)
284 })?;
285 }
286 if key_val_plan.key_plan.deref().is_identity() {
287 writeln!(f, "{}key_plan=id", ctx.indent)?;
288 } else {
289 writeln!(f, "{}key_plan", ctx.indent)?;
290 ctx.indented(|ctx| {
291 let key_plan = mode.expr(key_val_plan.key_plan.deref(), None);
292 key_plan.fmt_text(f, ctx)
293 })?;
294 }
295 if let Some(key) = input_key {
296 let key = mode.seq(key, None);
297 let key = CompactScalars(key);
298 writeln!(f, "{}input_key={}", ctx.indent, key)?;
299 }
300 if !mfp_after.is_identity() {
301 writeln!(f, "{}mfp_after", ctx.indent)?;
302 ctx.indented(|ctx| mode.expr(mfp_after, None).fmt_text(f, ctx))?;
303 }
304
305 input.fmt_text(f, ctx)
306 })?;
307 }
308 TopK { input, top_k_plan } => {
309 use crate::plan::top_k::TopKPlan;
310 match top_k_plan {
311 TopKPlan::MonotonicTop1(plan) => {
312 write!(f, "{}TopK::MonotonicTop1", ctx.indent)?;
313 if plan.group_key.len() > 0 {
314 let group_by = mode.seq(&plan.group_key, None);
315 let group_by = CompactScalars(group_by);
316 write!(f, " group_by=[{}]", group_by)?;
317 }
318 if plan.order_key.len() > 0 {
319 let order_by = mode.seq(&plan.order_key, None);
320 let order_by = separated(", ", order_by);
321 write!(f, " order_by=[{}]", order_by)?;
322 }
323 if plan.must_consolidate {
324 write!(f, " must_consolidate")?;
325 }
326 }
327 TopKPlan::MonotonicTopK(plan) => {
328 write!(f, "{}TopK::MonotonicTopK", ctx.indent)?;
329 if plan.group_key.len() > 0 {
330 let group_by = mode.seq(&plan.group_key, None);
331 let group_by = CompactScalars(group_by);
332 write!(f, " group_by=[{}]", group_by)?;
333 }
334 if plan.order_key.len() > 0 {
335 let order_by = mode.seq(&plan.order_key, None);
336 let order_by = separated(", ", order_by);
337 write!(f, " order_by=[{}]", order_by)?;
338 }
339 if let Some(limit) = &plan.limit {
340 let limit = mode.expr(limit, None);
341 write!(f, " limit={}", limit)?;
342 }
343 if plan.must_consolidate {
344 write!(f, " must_consolidate")?;
345 }
346 }
347 TopKPlan::Basic(plan) => {
348 write!(f, "{}TopK::Basic", ctx.indent)?;
349 if plan.group_key.len() > 0 {
350 let group_by = mode.seq(&plan.group_key, None);
351 let group_by = CompactScalars(group_by);
352 write!(f, " group_by=[{}]", group_by)?;
353 }
354 if plan.order_key.len() > 0 {
355 let order_by = mode.seq(&plan.order_key, None);
356 let order_by = separated(", ", order_by);
357 write!(f, " order_by=[{}]", order_by)?;
358 }
359 if let Some(limit) = &plan.limit {
360 let limit = mode.expr(limit, None);
361 write!(f, " limit={}", limit)?;
362 }
363 if &plan.offset > &0 {
364 write!(f, " offset={}", plan.offset)?;
365 }
366 }
367 }
368 writeln!(f, "{}", annotations)?;
369 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
370 }
371 Negate { input } => {
372 writeln!(f, "{}Negate{}", ctx.indent, annotations)?;
373 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
374 }
375 Threshold {
376 input,
377 threshold_plan,
378 } => {
379 use crate::plan::threshold::ThresholdPlan;
380 match threshold_plan {
381 ThresholdPlan::Basic(plan) => {
382 let ensure_arrangement = Arrangement::from(&plan.ensure_arrangement);
383 write!(f, "{}Threshold::Basic", ctx.indent)?;
384 write!(f, " ensure_arrangement=")?;
385 ensure_arrangement.fmt_text(f, ctx)?;
386 writeln!(f, "{}", annotations)?;
387 }
388 };
389 ctx.indented(|ctx| input.fmt_text(f, ctx))?;
390 }
391 Union {
392 inputs,
393 consolidate_output,
394 } => {
395 if *consolidate_output {
396 writeln!(
397 f,
398 "{}Union consolidate_output={}{}",
399 ctx.indent, consolidate_output, annotations
400 )?;
401 } else {
402 writeln!(f, "{}Union{}", ctx.indent, annotations)?;
403 }
404 ctx.indented(|ctx| {
405 for input in inputs.iter() {
406 input.fmt_text(f, ctx)?;
407 }
408 Ok(())
409 })?;
410 }
411 ArrangeBy {
412 input,
413 forms,
414 input_key,
415 input_mfp,
416 } => {
417 writeln!(f, "{}ArrangeBy{}", ctx.indent, annotations)?;
418 ctx.indented(|ctx| {
419 if let Some(key) = input_key {
420 let key = mode.seq(key, None);
421 let key = CompactScalars(key);
422 writeln!(f, "{}input_key=[{}]", ctx.indent, key)?;
423 }
424 mode.expr(input_mfp, None).fmt_text(f, ctx)?;
425 forms.fmt_text(f, ctx)?;
426 input.fmt_text(f, ctx)
428 })?;
429 }
430 }
431
432 Ok(())
433 }
434}
435
436impl DisplayText<PlanRenderingContext<'_, Plan>> for AvailableCollections {
437 fn fmt_text(
438 &self,
439 f: &mut fmt::Formatter<'_>,
440 ctx: &mut PlanRenderingContext<'_, Plan>,
441 ) -> fmt::Result {
442 let raw = &self.raw;
444 writeln!(f, "{}raw={}", ctx.indent, raw)?;
445 for (i, arrangement) in self.arranged.iter().enumerate() {
447 let arrangement = Arrangement::from(arrangement);
448 write!(f, "{}arrangements[{}]=", ctx.indent, i)?;
449 arrangement.fmt_text(f, ctx)?;
450 writeln!(f, "")?;
451 }
452 if let Some(types) = self.types.as_ref() {
454 if types.len() > 0 {
455 write!(f, "{}types=[", ctx.indent)?;
456 write!(
457 f,
458 "{}",
459 separated(
460 ", ",
461 types
462 .iter()
463 .map(|c| ctx.humanizer.humanize_column_type(c, false))
464 )
465 )?;
466 writeln!(f, "]")?;
467 }
468 }
469 Ok(())
470 }
471}
472
473impl DisplayText<PlanRenderingContext<'_, Plan>> for LinearJoinPlan {
474 fn fmt_text(
475 &self,
476 f: &mut fmt::Formatter<'_>,
477 ctx: &mut PlanRenderingContext<'_, Plan>,
478 ) -> fmt::Result {
479 let mode = HumanizedExplain::new(ctx.config.redacted);
480 let plan = self;
481 if let Some(closure) = plan.final_closure.as_ref() {
482 if !closure.is_identity() {
483 writeln!(f, "{}final_closure", ctx.indent)?;
484 ctx.indented(|ctx| closure.fmt_text(f, ctx))?;
485 }
486 }
487 for (i, plan) in plan.stage_plans.iter().enumerate().rev() {
488 writeln!(f, "{}linear_stage[{}]", ctx.indent, i)?;
489 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
490 }
491 if let Some(closure) = plan.initial_closure.as_ref() {
492 if !closure.is_identity() {
493 writeln!(f, "{}initial_closure", ctx.indent)?;
494 ctx.indented(|ctx| closure.fmt_text(f, ctx))?;
495 }
496 }
497 match &plan.source_key {
498 Some(source_key) => {
499 let source_key = mode.seq(source_key, None);
500 let source_key = CompactScalars(source_key);
501 writeln!(
502 f,
503 "{}source={{ relation={}, key=[{}] }}",
504 ctx.indent, &plan.source_relation, source_key
505 )?
506 }
507 None => writeln!(
508 f,
509 "{}source={{ relation={}, key=[] }}",
510 ctx.indent, &plan.source_relation
511 )?,
512 };
513 Ok(())
514 }
515}
516
517impl DisplayText<PlanRenderingContext<'_, Plan>> for LinearStagePlan {
518 fn fmt_text(
519 &self,
520 f: &mut fmt::Formatter<'_>,
521 ctx: &mut PlanRenderingContext<'_, Plan>,
522 ) -> fmt::Result {
523 let mode = HumanizedExplain::new(ctx.config.redacted);
524
525 let plan = self;
526 if !plan.closure.is_identity() {
527 writeln!(f, "{}closure", ctx.indent)?;
528 ctx.indented(|ctx| plan.closure.fmt_text(f, ctx))?;
529 }
530 {
531 let lookup_relation = &plan.lookup_relation;
532 let lookup_key = CompactScalarSeq(&plan.lookup_key);
533 writeln!(
534 f,
535 "{}lookup={{ relation={}, key=[{}] }}",
536 ctx.indent, lookup_relation, lookup_key
537 )?;
538 }
539 {
540 let stream_key = mode.seq(&plan.stream_key, None);
541 let stream_key = CompactScalars(stream_key);
542 let stream_thinning = Indices(&plan.stream_thinning);
543 writeln!(
544 f,
545 "{}stream={{ key=[{}], thinning=({}) }}",
546 ctx.indent, stream_key, stream_thinning
547 )?;
548 }
549 Ok(())
550 }
551}
552
553impl DisplayText<PlanRenderingContext<'_, Plan>> for DeltaJoinPlan {
554 fn fmt_text(
555 &self,
556 f: &mut fmt::Formatter<'_>,
557 ctx: &mut PlanRenderingContext<'_, Plan>,
558 ) -> fmt::Result {
559 for (i, plan) in self.path_plans.iter().enumerate() {
560 writeln!(f, "{}plan_path[{}]", ctx.indent, i)?;
561 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
562 }
563 Ok(())
564 }
565}
566
567impl DisplayText<PlanRenderingContext<'_, Plan>> for DeltaPathPlan {
568 fn fmt_text(
569 &self,
570 f: &mut fmt::Formatter<'_>,
571 ctx: &mut PlanRenderingContext<'_, Plan>,
572 ) -> fmt::Result {
573 let mode = HumanizedExplain::new(ctx.config.redacted);
574 let plan = self;
575 if let Some(closure) = plan.final_closure.as_ref() {
576 if !closure.is_identity() {
577 writeln!(f, "{}final_closure", ctx.indent)?;
578 ctx.indented(|ctx| closure.fmt_text(f, ctx))?;
579 }
580 }
581 for (i, plan) in plan.stage_plans.iter().enumerate().rev() {
582 writeln!(f, "{}delta_stage[{}]", ctx.indent, i)?;
583 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
584 }
585 if !plan.initial_closure.is_identity() {
586 writeln!(f, "{}initial_closure", ctx.indent)?;
587 ctx.indented(|ctx| plan.initial_closure.fmt_text(f, ctx))?;
588 }
589 {
590 let source_relation = &plan.source_relation;
591 let source_key = mode.seq(&plan.source_key, None);
592 let source_key = CompactScalars(source_key);
593 writeln!(
594 f,
595 "{}source={{ relation={}, key=[{}] }}",
596 ctx.indent, source_relation, source_key
597 )?;
598 }
599 Ok(())
600 }
601}
602
603impl DisplayText<PlanRenderingContext<'_, Plan>> for DeltaStagePlan {
604 fn fmt_text(
605 &self,
606 f: &mut fmt::Formatter<'_>,
607 ctx: &mut PlanRenderingContext<'_, Plan>,
608 ) -> fmt::Result {
609 let mode = HumanizedExplain::new(ctx.config.redacted);
610 let plan = self;
611 if !plan.closure.is_identity() {
612 writeln!(f, "{}closure", ctx.indent)?;
613 ctx.indented(|ctx| plan.closure.fmt_text(f, ctx))?;
614 }
615 {
616 let lookup_relation = &plan.lookup_relation;
617 let lookup_key = mode.seq(&plan.lookup_key, None);
618 let lookup_key = CompactScalars(lookup_key);
619 writeln!(
620 f,
621 "{}lookup={{ relation={}, key=[{}] }}",
622 ctx.indent, lookup_relation, lookup_key
623 )?;
624 }
625 {
626 let stream_key = mode.seq(&plan.stream_key, None);
627 let stream_key = CompactScalars(stream_key);
628 let stream_thinning = mode.seq(&plan.stream_thinning, None);
629 let stream_thinning = CompactScalars(stream_thinning);
630 writeln!(
631 f,
632 "{}stream={{ key=[{}], thinning=({}) }}",
633 ctx.indent, stream_key, stream_thinning
634 )?;
635 }
636 Ok(())
637 }
638}
639
640impl DisplayText<PlanRenderingContext<'_, Plan>> for JoinClosure {
641 fn fmt_text(
642 &self,
643 f: &mut fmt::Formatter<'_>,
644 ctx: &mut PlanRenderingContext<'_, Plan>,
645 ) -> fmt::Result {
646 let mode = HumanizedExplain::new(ctx.config.redacted);
647 mode.expr(self.before.deref(), None).fmt_text(f, ctx)?;
648 if !self.ready_equivalences.is_empty() {
649 let equivalences = separated(
650 " AND ",
651 self.ready_equivalences
652 .iter()
653 .map(|equivalence| separated(" = ", mode.seq(equivalence, None))),
654 );
655 writeln!(f, "{}ready_equivalences={}", ctx.indent, equivalences)?;
656 }
657 Ok(())
658 }
659}
660
661impl DisplayText<PlanRenderingContext<'_, Plan>> for AccumulablePlan {
662 fn fmt_text(
663 &self,
664 f: &mut fmt::Formatter<'_>,
665 ctx: &mut PlanRenderingContext<'_, Plan>,
666 ) -> fmt::Result {
667 let mode = HumanizedExplain::new(ctx.config.redacted);
668 for (i, (i_aggs, i_datum, agg)) in self.simple_aggrs.iter().enumerate() {
676 let agg = mode.expr(agg, None);
677 write!(f, "{}simple_aggrs[{}]=", ctx.indent, i)?;
678 writeln!(f, "({}, {}, {})", i_aggs, i_datum, agg)?;
679 }
680 for (i, (i_aggs, i_datum, agg)) in self.distinct_aggrs.iter().enumerate() {
682 let agg = mode.expr(agg, None);
683 write!(f, "{}distinct_aggrs[{}]=", ctx.indent, i)?;
684 writeln!(f, "({}, {}, {})", i_aggs, i_datum, agg)?;
685 }
686 Ok(())
687 }
688}
689
690impl DisplayText<PlanRenderingContext<'_, Plan>> for HierarchicalPlan {
691 fn fmt_text(
692 &self,
693 f: &mut fmt::Formatter<'_>,
694 ctx: &mut PlanRenderingContext<'_, Plan>,
695 ) -> fmt::Result {
696 let mode = HumanizedExplain::new(ctx.config.redacted);
697 match self {
698 HierarchicalPlan::Monotonic(plan) => {
699 let aggr_funcs = mode.seq(&plan.aggr_funcs, None);
700 let aggr_funcs = separated(", ", aggr_funcs);
701 writeln!(f, "{}aggr_funcs=[{}]", ctx.indent, aggr_funcs)?;
702 let skips = separated(", ", &plan.skips);
703 writeln!(f, "{}skips=[{}]", ctx.indent, skips)?;
704 writeln!(f, "{}monotonic", ctx.indent)?;
705 if plan.must_consolidate {
706 writeln!(f, "{}must_consolidate", ctx.indent)?;
707 }
708 }
709 HierarchicalPlan::Bucketed(plan) => {
710 let aggr_funcs = mode.seq(&plan.aggr_funcs, None);
711 let aggr_funcs = separated(", ", aggr_funcs);
712 writeln!(f, "{}aggr_funcs=[{}]", ctx.indent, aggr_funcs)?;
713 let skips = separated(", ", &plan.skips);
714 writeln!(f, "{}skips=[{}]", ctx.indent, skips)?;
715 let buckets = separated(", ", &plan.buckets);
716 writeln!(f, "{}buckets=[{}]", ctx.indent, buckets)?;
717 }
718 }
719 Ok(())
720 }
721}
722
723impl DisplayText<PlanRenderingContext<'_, Plan>> for BasicPlan {
724 fn fmt_text(
725 &self,
726 f: &mut fmt::Formatter<'_>,
727 ctx: &mut PlanRenderingContext<'_, Plan>,
728 ) -> fmt::Result {
729 let mode = HumanizedExplain::new(ctx.config.redacted);
730 match self {
731 BasicPlan::Single(SingleBasicPlan {
732 index,
733 expr,
734 fused_unnest_list,
735 }) => {
736 let agg = mode.expr(expr, None);
737 let fused_unnest_list = if *fused_unnest_list {
738 ", fused_unnest_list=true"
739 } else {
740 ""
741 };
742 writeln!(
743 f,
744 "{}aggr=({}, {}{})",
745 ctx.indent, index, agg, fused_unnest_list
746 )?;
747 }
748 BasicPlan::Multiple(aggs) => {
749 for (i, (i_datum, agg)) in aggs.iter().enumerate() {
750 let agg = mode.expr(agg, None);
751 writeln!(f, "{}aggrs[{}]=({}, {})", ctx.indent, i, i_datum, agg)?;
752 }
753 }
754 }
755 Ok(())
756 }
757}
758
759impl DisplayText<PlanRenderingContext<'_, Plan>> for CollationPlan {
760 fn fmt_text(
761 &self,
762 f: &mut fmt::Formatter<'_>,
763 ctx: &mut PlanRenderingContext<'_, Plan>,
764 ) -> fmt::Result {
765 {
766 use crate::plan::reduce::ReductionType;
767 let aggregate_types = &self
768 .aggregate_types
769 .iter()
770 .map(|reduction_type| match reduction_type {
771 ReductionType::Accumulable => "a".to_string(),
772 ReductionType::Hierarchical => "h".to_string(),
773 ReductionType::Basic => "b".to_string(),
774 })
775 .collect::<Vec<_>>();
776 let aggregate_types = separated(", ", aggregate_types);
777 writeln!(f, "{}aggregate_types=[{}]", ctx.indent, aggregate_types)?;
778 }
779 if let Some(plan) = &self.accumulable {
780 writeln!(f, "{}accumulable", ctx.indent)?;
781 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
782 }
783 if let Some(plan) = &self.hierarchical {
784 writeln!(f, "{}hierarchical", ctx.indent)?;
785 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
786 }
787 if let Some(plan) = &self.basic {
788 writeln!(f, "{}basic", ctx.indent)?;
789 ctx.indented(|ctx| plan.fmt_text(f, ctx))?;
790 }
791 Ok(())
792 }
793}
794
795struct Arrangement<'a> {
797 key: &'a Vec<MirScalarExpr>,
798 permutation: Permutation<'a>,
799 thinning: &'a Vec<usize>,
800}
801
802impl<'a> From<&'a (Vec<MirScalarExpr>, Vec<usize>, Vec<usize>)> for Arrangement<'a> {
803 fn from(
804 (key, permutation, thinning): &'a (Vec<MirScalarExpr>, Vec<usize>, Vec<usize>),
805 ) -> Self {
806 Arrangement {
807 key,
808 permutation: Permutation(permutation),
809 thinning,
810 }
811 }
812}
813
814impl<'a> DisplayText<PlanRenderingContext<'_, Plan>> for Arrangement<'a> {
815 fn fmt_text(
816 &self,
817 f: &mut fmt::Formatter<'_>,
818 ctx: &mut PlanRenderingContext<'_, Plan>,
819 ) -> fmt::Result {
820 let mode = HumanizedExplain::new(ctx.config.redacted);
821 let key = mode.seq(self.key, None);
823 let key = CompactScalars(key);
824 let permutation = &self.permutation;
826 let thinning = Indices(self.thinning);
828 write!(
830 f,
831 "{{ key=[{}], permutation={}, thinning=({}) }}",
832 key, permutation, thinning
833 )
834 }
835}
836
837struct Permutation<'a>(&'a Vec<usize>);
839
840impl<'a> fmt::Display for Permutation<'a> {
841 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
842 let mut pairs = vec![];
843 for (x, y) in self.0.iter().enumerate().filter(|(x, y)| x != *y) {
844 pairs.push(format!("#{}: #{}", x, y));
845 }
846
847 if pairs.len() > 0 {
848 write!(f, "{{{}}}", separated(", ", pairs))
849 } else {
850 write!(f, "id")
851 }
852 }
853}
854
855struct PlanAnnotations {
857 config: ExplainConfig,
858 node_id: LirId,
859}
860
861impl PlanAnnotations {
867 fn new(config: ExplainConfig, plan: &Plan) -> Self {
868 let node_id = plan.lir_id;
869 Self { config, node_id }
870 }
871}
872
873impl fmt::Display for PlanAnnotations {
874 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
875 if self.config.node_ids {
876 f.debug_struct(" //")
877 .field("node_id", &self.node_id)
878 .finish()
879 } else {
880 Ok(())
882 }
883 }
884}