Skip to content

Commit 6ad7a62

Browse files
committed
feat: pretty explain
1 parent 0f9773a commit 6ad7a62

39 files changed

+208
-6
lines changed

datafusion/common/src/config.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,10 @@ config_namespace! {
704704

705705
/// When set to true, the explain statement will print schema information
706706
pub show_schema: bool, default = false
707+
708+
/// Display format of explain. Default is "indent".
709+
/// When set to "pretty", it will print the plan in a tree-rendered format.
710+
pub format: String, default = "indent".to_string()
707711
}
708712
}
709713

datafusion/core/src/datasource/file_format/arrow.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ impl DisplayAs for ArrowFileSink {
301301
FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?;
302302
write!(f, ")")
303303
}
304+
DisplayFormatType::TreeRender => {
305+
write!(f, "") // TODO(renjj): add display info
306+
}
304307
}
305308
}
306309
}

datafusion/core/src/datasource/file_format/csv.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,9 @@ impl DisplayAs for CsvSink {
674674
FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?;
675675
write!(f, ")")
676676
}
677+
DisplayFormatType::TreeRender => {
678+
write!(f, "") // TODO(renjj): add display info
679+
}
677680
}
678681
}
679682
}

datafusion/core/src/datasource/file_format/json.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,9 @@ impl DisplayAs for JsonSink {
330330
FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?;
331331
write!(f, ")")
332332
}
333+
DisplayFormatType::TreeRender => {
334+
write!(f, "") // TODO(renjj): add display info
335+
}
333336
}
334337
}
335338
}

datafusion/core/src/datasource/file_format/parquet.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,9 @@ impl DisplayAs for ParquetSink {
702702
FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?;
703703
write!(f, ")")
704704
}
705+
DisplayFormatType::TreeRender => {
706+
write!(f, "") // TODO(renjj): add display info
707+
}
705708
}
706709
}
707710
}

datafusion/core/src/datasource/memory.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,9 @@ impl DisplayAs for MemSink {
332332
let partition_count = self.batches.len();
333333
write!(f, "MemoryTable (partitions={partition_count})")
334334
}
335+
DisplayFormatType::TreeRender => {
336+
write!(f, "") // TODO(renjj): add display info
337+
}
335338
}
336339
}
337340
}

datafusion/core/src/datasource/physical_plan/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ impl DisplayAs for FileGroupsDisplay<'_> {
233233
FileGroupDisplay(group).fmt_as(t, f)
234234
})?
235235
}
236+
DisplayFormatType::TreeRender => {
237+
write!(f, "")?; // TODO(renjj): add display info
238+
}
236239
}
237240
write!(f, "]}}")
238241
}
@@ -271,6 +274,9 @@ impl DisplayAs for FileGroupDisplay<'_> {
271274
Ok(())
272275
})?
273276
}
277+
DisplayFormatType::TreeRender => {
278+
write!(f, "")?; // TODO(renjj): add display info
279+
}
274280
}
275281
write!(f, "]")
276282
}

datafusion/core/src/datasource/physical_plan/parquet/source.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,9 @@ impl FileSource for ParquetSource {
584584

585585
write!(f, "{}{}", predicate_string, pruning_predicate_string)
586586
}
587+
DisplayFormatType::TreeRender => {
588+
write!(f, "") // TODO(renjj): add display info
589+
}
587590
}
588591
}
589592
fn supports_repartition(&self, _config: &FileScanConfig) -> bool {

datafusion/core/src/physical_planner.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
2020
use std::borrow::Cow;
2121
use std::collections::HashMap;
22+
use std::str::FromStr;
2223
use std::sync::Arc;
2324

2425
use crate::datasource::file_format::file_type_to_format;
@@ -86,6 +87,7 @@ use datafusion_physical_plan::execution_plan::InvariantLevel;
8687
use datafusion_physical_plan::memory::MemorySourceConfig;
8788
use datafusion_physical_plan::placeholder_row::PlaceholderRowExec;
8889
use datafusion_physical_plan::unnest::ListUnnest;
90+
use datafusion_physical_plan::DisplayFormatType;
8991

9092
use crate::schema_equivalence::schema_satisfied_by;
9193
use async_trait::async_trait;
@@ -1705,6 +1707,7 @@ impl DefaultPhysicalPlanner {
17051707
let mut stringified_plans = vec![];
17061708

17071709
let config = &session_state.config_options().explain;
1710+
let explain_format = DisplayFormatType::from_str(&config.format)?;
17081711

17091712
if !config.physical_plan_only {
17101713
stringified_plans.clone_from(&e.stringified_plans);
@@ -1724,7 +1727,11 @@ impl DefaultPhysicalPlanner {
17241727
displayable(input.as_ref())
17251728
.set_show_statistics(config.show_statistics)
17261729
.set_show_schema(config.show_schema)
1727-
.to_stringified(e.verbose, InitialPhysicalPlan),
1730+
.to_stringified(
1731+
e.verbose,
1732+
InitialPhysicalPlan,
1733+
explain_format,
1734+
),
17281735
);
17291736

17301737
// Show statistics + schema in verbose output even if not
@@ -1737,6 +1744,7 @@ impl DefaultPhysicalPlanner {
17371744
.to_stringified(
17381745
e.verbose,
17391746
InitialPhysicalPlanWithStats,
1747+
explain_format,
17401748
),
17411749
);
17421750
}
@@ -1747,6 +1755,7 @@ impl DefaultPhysicalPlanner {
17471755
.to_stringified(
17481756
e.verbose,
17491757
InitialPhysicalPlanWithSchema,
1758+
explain_format,
17501759
),
17511760
);
17521761
}
@@ -1762,7 +1771,11 @@ impl DefaultPhysicalPlanner {
17621771
displayable(plan)
17631772
.set_show_statistics(config.show_statistics)
17641773
.set_show_schema(config.show_schema)
1765-
.to_stringified(e.verbose, plan_type),
1774+
.to_stringified(
1775+
e.verbose,
1776+
plan_type,
1777+
explain_format,
1778+
),
17661779
);
17671780
},
17681781
);
@@ -1773,7 +1786,11 @@ impl DefaultPhysicalPlanner {
17731786
displayable(input.as_ref())
17741787
.set_show_statistics(config.show_statistics)
17751788
.set_show_schema(config.show_schema)
1776-
.to_stringified(e.verbose, FinalPhysicalPlan),
1789+
.to_stringified(
1790+
e.verbose,
1791+
FinalPhysicalPlan,
1792+
explain_format,
1793+
),
17771794
);
17781795

17791796
// Show statistics + schema in verbose output even if not
@@ -1786,6 +1803,7 @@ impl DefaultPhysicalPlanner {
17861803
.to_stringified(
17871804
e.verbose,
17881805
FinalPhysicalPlanWithStats,
1806+
explain_format,
17891807
),
17901808
);
17911809
}
@@ -1796,6 +1814,7 @@ impl DefaultPhysicalPlanner {
17961814
.to_stringified(
17971815
e.verbose,
17981816
FinalPhysicalPlanWithSchema,
1817+
explain_format,
17991818
),
18001819
);
18011820
}

datafusion/physical-plan/src/aggregates/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -789,6 +789,9 @@ impl DisplayAs for AggregateExec {
789789
write!(f, ", ordering_mode={:?}", self.input_order_mode)?;
790790
}
791791
}
792+
DisplayFormatType::TreeRender => {
793+
write!(f, "")?; // TODO(renjj): add display info
794+
}
792795
}
793796
Ok(())
794797
}

datafusion/physical-plan/src/analyze.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ impl DisplayAs for AnalyzeExec {
108108
DisplayFormatType::Default | DisplayFormatType::Verbose => {
109109
write!(f, "AnalyzeExec verbose={}", self.verbose)
110110
}
111+
DisplayFormatType::TreeRender => {
112+
write!(f, "") // TODO(renjj): add display info
113+
}
111114
}
112115
}
113116
}

datafusion/physical-plan/src/coalesce_batches.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ impl DisplayAs for CoalesceBatchesExec {
122122

123123
Ok(())
124124
}
125+
DisplayFormatType::TreeRender => {
126+
write!(f, "") // TODO(renjj): add display info
127+
}
125128
}
126129
}
127130
}

datafusion/physical-plan/src/coalesce_partitions.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ impl DisplayAs for CoalescePartitionsExec {
9292
}
9393
None => write!(f, "CoalescePartitionsExec"),
9494
},
95+
DisplayFormatType::TreeRender => {
96+
write!(f, "") // TODO(renjj): add display info
97+
}
9598
}
9699
}
97100
}

datafusion/physical-plan/src/display.rs

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
//! Implementation of physical plan display. See
1919
//! [`crate::displayable`] for examples of how to format
2020
21-
use std::fmt;
2221
use std::fmt::Formatter;
22+
use std::{fmt, str::FromStr};
2323

2424
use arrow_schema::SchemaRef;
2525

2626
use datafusion_common::display::{GraphvizBuilder, PlanType, StringifiedPlan};
27+
use datafusion_common::DataFusionError;
2728
use datafusion_expr::display_schema;
2829
use datafusion_physical_expr::LexOrdering;
2930

@@ -36,6 +37,23 @@ pub enum DisplayFormatType {
3637
Default,
3738
/// Verbose, showing all available details
3839
Verbose,
40+
/// TreeRender, display plan like a tree.
41+
TreeRender,
42+
}
43+
44+
impl FromStr for DisplayFormatType {
45+
type Err = DataFusionError;
46+
47+
fn from_str(s: &str) -> Result<Self, Self::Err> {
48+
match s.to_lowercase().as_str() {
49+
"indent" => Ok(Self::Default),
50+
"pretty" => Ok(Self::TreeRender),
51+
_ => Err(DataFusionError::Configuration(format!(
52+
"Invalid explain format: {}",
53+
s
54+
))),
55+
}
56+
}
3957
}
4058

4159
/// Wraps an `ExecutionPlan` with various methods for formatting
@@ -224,6 +242,37 @@ impl<'a> DisplayableExecutionPlan<'a> {
224242
}
225243
}
226244

245+
pub fn tree_render(&self) -> impl fmt::Display + 'a {
246+
let format_type = DisplayFormatType::TreeRender;
247+
struct Wrapper<'a> {
248+
format_type: DisplayFormatType,
249+
plan: &'a dyn ExecutionPlan,
250+
show_metrics: ShowMetrics,
251+
show_statistics: bool,
252+
show_schema: bool,
253+
}
254+
impl fmt::Display for Wrapper<'_> {
255+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
256+
let mut visitor = IndentVisitor {
257+
t: self.format_type,
258+
f,
259+
indent: 0,
260+
show_metrics: self.show_metrics,
261+
show_statistics: self.show_statistics,
262+
show_schema: self.show_schema,
263+
};
264+
accept(self.plan, &mut visitor)
265+
}
266+
}
267+
Wrapper {
268+
format_type,
269+
plan: self.inner,
270+
show_metrics: self.show_metrics,
271+
show_statistics: self.show_statistics,
272+
show_schema: self.show_schema,
273+
}
274+
}
275+
227276
/// Return a single-line summary of the root of the plan
228277
/// Example: `ProjectionExec: expr=[a@0 as a]`.
229278
pub fn one_line(&self) -> impl fmt::Display + 'a {
@@ -258,8 +307,20 @@ impl<'a> DisplayableExecutionPlan<'a> {
258307
}
259308

260309
/// format as a `StringifiedPlan`
261-
pub fn to_stringified(&self, verbose: bool, plan_type: PlanType) -> StringifiedPlan {
262-
StringifiedPlan::new(plan_type, self.indent(verbose).to_string())
310+
pub fn to_stringified(
311+
&self,
312+
verbose: bool,
313+
plan_type: PlanType,
314+
explain_format: DisplayFormatType,
315+
) -> StringifiedPlan {
316+
match explain_format {
317+
DisplayFormatType::Default | DisplayFormatType::Verbose => {
318+
StringifiedPlan::new(plan_type, self.indent(verbose).to_string())
319+
}
320+
DisplayFormatType::TreeRender => {
321+
StringifiedPlan::new(plan_type, self.tree_render().to_string())
322+
}
323+
}
263324
}
264325
}
265326

datafusion/physical-plan/src/empty.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ impl DisplayAs for EmptyExec {
9494
DisplayFormatType::Default | DisplayFormatType::Verbose => {
9595
write!(f, "EmptyExec")
9696
}
97+
DisplayFormatType::TreeRender => {
98+
write!(f, "") // TODO(renjj): add display info
99+
}
97100
}
98101
}
99102
}

datafusion/physical-plan/src/explain.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ impl DisplayAs for ExplainExec {
9494
DisplayFormatType::Default | DisplayFormatType::Verbose => {
9595
write!(f, "ExplainExec")
9696
}
97+
DisplayFormatType::TreeRender => {
98+
write!(f, "") // TODO(renjj): add display info
99+
}
97100
}
98101
}
99102
}

datafusion/physical-plan/src/filter.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,9 @@ impl DisplayAs for FilterExec {
329329
};
330330
write!(f, "FilterExec: {}{}", self.predicate, display_projections)
331331
}
332+
DisplayFormatType::TreeRender => {
333+
write!(f, "") // TODO(renjj): add display info
334+
}
332335
}
333336
}
334337
}

datafusion/physical-plan/src/insert.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ impl DisplayAs for DataSinkExec {
154154
write!(f, "DataSinkExec: sink=")?;
155155
self.sink.fmt_as(t, f)
156156
}
157+
DisplayFormatType::TreeRender => {
158+
write!(f, "") // TODO(renjj): add display info
159+
}
157160
}
158161
}
159162
}

datafusion/physical-plan/src/joins/cross_join.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ impl DisplayAs for CrossJoinExec {
240240
DisplayFormatType::Default | DisplayFormatType::Verbose => {
241241
write!(f, "CrossJoinExec")
242242
}
243+
DisplayFormatType::TreeRender => {
244+
write!(f, "") // TODO(renjj): add display info
245+
}
243246
}
244247
}
245248
}

datafusion/physical-plan/src/joins/hash_join.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,9 @@ impl DisplayAs for HashJoinExec {
668668
self.mode, self.join_type, on, display_filter, display_projections
669669
)
670670
}
671+
DisplayFormatType::TreeRender => {
672+
write!(f, "") // TODO(renjj): add display info
673+
}
671674
}
672675
}
673676
}

datafusion/physical-plan/src/joins/nested_loop_join.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,7 @@ impl DisplayAs for NestedLoopJoinExec {
424424
self.join_type, display_filter, display_projections
425425
)
426426
}
427+
DisplayFormatType::TreeRender => todo!(), // TODO(renjj): add display info
427428
}
428429
}
429430
}

0 commit comments

Comments
 (0)