From 6ad7a62ad1d96aab6818719c32ac81bfb9eada35 Mon Sep 17 00:00:00 2001 From: irenjj Date: Sat, 15 Feb 2025 11:52:53 +0800 Subject: [PATCH 01/17] feat: pretty explain --- datafusion/common/src/config.rs | 4 ++ .../core/src/datasource/file_format/arrow.rs | 3 + .../core/src/datasource/file_format/csv.rs | 3 + .../core/src/datasource/file_format/json.rs | 3 + .../src/datasource/file_format/parquet.rs | 3 + datafusion/core/src/datasource/memory.rs | 3 + .../core/src/datasource/physical_plan/mod.rs | 6 ++ .../physical_plan/parquet/source.rs | 3 + datafusion/core/src/physical_planner.rs | 25 ++++++- .../physical-plan/src/aggregates/mod.rs | 3 + datafusion/physical-plan/src/analyze.rs | 3 + .../physical-plan/src/coalesce_batches.rs | 3 + .../physical-plan/src/coalesce_partitions.rs | 3 + datafusion/physical-plan/src/display.rs | 67 ++++++++++++++++++- datafusion/physical-plan/src/empty.rs | 3 + datafusion/physical-plan/src/explain.rs | 3 + datafusion/physical-plan/src/filter.rs | 3 + datafusion/physical-plan/src/insert.rs | 3 + .../physical-plan/src/joins/cross_join.rs | 3 + .../physical-plan/src/joins/hash_join.rs | 3 + .../src/joins/nested_loop_join.rs | 1 + .../src/joins/sort_merge_join.rs | 3 + .../src/joins/symmetric_hash_join.rs | 3 + datafusion/physical-plan/src/limit.rs | 6 ++ datafusion/physical-plan/src/memory.rs | 6 ++ .../physical-plan/src/placeholder_row.rs | 3 + datafusion/physical-plan/src/projection.rs | 3 + .../physical-plan/src/recursive_query.rs | 3 + .../physical-plan/src/repartition/mod.rs | 3 + .../physical-plan/src/sorts/partial_sort.rs | 3 + datafusion/physical-plan/src/sorts/sort.rs | 3 + .../src/sorts/sort_preserving_merge.rs | 3 + datafusion/physical-plan/src/streaming.rs | 3 + datafusion/physical-plan/src/union.rs | 6 ++ datafusion/physical-plan/src/unnest.rs | 3 + datafusion/physical-plan/src/values.rs | 3 + .../src/windows/bounded_window_agg_exec.rs | 3 + .../src/windows/window_agg_exec.rs | 3 + datafusion/physical-plan/src/work_table.rs | 3 + 39 files changed, 208 insertions(+), 6 deletions(-) diff --git a/datafusion/common/src/config.rs b/datafusion/common/src/config.rs index c9900204b97f..a78890810ad4 100644 --- a/datafusion/common/src/config.rs +++ b/datafusion/common/src/config.rs @@ -704,6 +704,10 @@ config_namespace! { /// When set to true, the explain statement will print schema information pub show_schema: bool, default = false + + /// Display format of explain. Default is "indent". + /// When set to "pretty", it will print the plan in a tree-rendered format. + pub format: String, default = "indent".to_string() } } diff --git a/datafusion/core/src/datasource/file_format/arrow.rs b/datafusion/core/src/datasource/file_format/arrow.rs index 5a4bf103e7ce..5fe98a6fe219 100644 --- a/datafusion/core/src/datasource/file_format/arrow.rs +++ b/datafusion/core/src/datasource/file_format/arrow.rs @@ -301,6 +301,9 @@ impl DisplayAs for ArrowFileSink { FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/core/src/datasource/file_format/csv.rs b/datafusion/core/src/datasource/file_format/csv.rs index cd8a4dedc169..c44407c4c539 100644 --- a/datafusion/core/src/datasource/file_format/csv.rs +++ b/datafusion/core/src/datasource/file_format/csv.rs @@ -674,6 +674,9 @@ impl DisplayAs for CsvSink { FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/core/src/datasource/file_format/json.rs b/datafusion/core/src/datasource/file_format/json.rs index 3aae692151f6..d18abd379907 100644 --- a/datafusion/core/src/datasource/file_format/json.rs +++ b/datafusion/core/src/datasource/file_format/json.rs @@ -330,6 +330,9 @@ impl DisplayAs for JsonSink { FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/core/src/datasource/file_format/parquet.rs b/datafusion/core/src/datasource/file_format/parquet.rs index 0b23fd8da612..ee89ec031e67 100644 --- a/datafusion/core/src/datasource/file_format/parquet.rs +++ b/datafusion/core/src/datasource/file_format/parquet.rs @@ -702,6 +702,9 @@ impl DisplayAs for ParquetSink { FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/core/src/datasource/memory.rs b/datafusion/core/src/datasource/memory.rs index a996990105b3..73ba21043011 100644 --- a/datafusion/core/src/datasource/memory.rs +++ b/datafusion/core/src/datasource/memory.rs @@ -332,6 +332,9 @@ impl DisplayAs for MemSink { let partition_count = self.batches.len(); write!(f, "MemoryTable (partitions={partition_count})") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/core/src/datasource/physical_plan/mod.rs b/datafusion/core/src/datasource/physical_plan/mod.rs index 873df859702a..c3231dd636ca 100644 --- a/datafusion/core/src/datasource/physical_plan/mod.rs +++ b/datafusion/core/src/datasource/physical_plan/mod.rs @@ -233,6 +233,9 @@ impl DisplayAs for FileGroupsDisplay<'_> { FileGroupDisplay(group).fmt_as(t, f) })? } + DisplayFormatType::TreeRender => { + write!(f, "")?; // TODO(renjj): add display info + } } write!(f, "]}}") } @@ -271,6 +274,9 @@ impl DisplayAs for FileGroupDisplay<'_> { Ok(()) })? } + DisplayFormatType::TreeRender => { + write!(f, "")?; // TODO(renjj): add display info + } } write!(f, "]") } diff --git a/datafusion/core/src/datasource/physical_plan/parquet/source.rs b/datafusion/core/src/datasource/physical_plan/parquet/source.rs index 0705a398f4fb..8469db6c5da8 100644 --- a/datafusion/core/src/datasource/physical_plan/parquet/source.rs +++ b/datafusion/core/src/datasource/physical_plan/parquet/source.rs @@ -584,6 +584,9 @@ impl FileSource for ParquetSource { write!(f, "{}{}", predicate_string, pruning_predicate_string) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } fn supports_repartition(&self, _config: &FileScanConfig) -> bool { diff --git a/datafusion/core/src/physical_planner.rs b/datafusion/core/src/physical_planner.rs index 9fcb9562a485..9ccde53f207c 100644 --- a/datafusion/core/src/physical_planner.rs +++ b/datafusion/core/src/physical_planner.rs @@ -19,6 +19,7 @@ use std::borrow::Cow; use std::collections::HashMap; +use std::str::FromStr; use std::sync::Arc; use crate::datasource::file_format::file_type_to_format; @@ -86,6 +87,7 @@ use datafusion_physical_plan::execution_plan::InvariantLevel; use datafusion_physical_plan::memory::MemorySourceConfig; use datafusion_physical_plan::placeholder_row::PlaceholderRowExec; use datafusion_physical_plan::unnest::ListUnnest; +use datafusion_physical_plan::DisplayFormatType; use crate::schema_equivalence::schema_satisfied_by; use async_trait::async_trait; @@ -1705,6 +1707,7 @@ impl DefaultPhysicalPlanner { let mut stringified_plans = vec![]; let config = &session_state.config_options().explain; + let explain_format = DisplayFormatType::from_str(&config.format)?; if !config.physical_plan_only { stringified_plans.clone_from(&e.stringified_plans); @@ -1724,7 +1727,11 @@ impl DefaultPhysicalPlanner { displayable(input.as_ref()) .set_show_statistics(config.show_statistics) .set_show_schema(config.show_schema) - .to_stringified(e.verbose, InitialPhysicalPlan), + .to_stringified( + e.verbose, + InitialPhysicalPlan, + explain_format, + ), ); // Show statistics + schema in verbose output even if not @@ -1737,6 +1744,7 @@ impl DefaultPhysicalPlanner { .to_stringified( e.verbose, InitialPhysicalPlanWithStats, + explain_format, ), ); } @@ -1747,6 +1755,7 @@ impl DefaultPhysicalPlanner { .to_stringified( e.verbose, InitialPhysicalPlanWithSchema, + explain_format, ), ); } @@ -1762,7 +1771,11 @@ impl DefaultPhysicalPlanner { displayable(plan) .set_show_statistics(config.show_statistics) .set_show_schema(config.show_schema) - .to_stringified(e.verbose, plan_type), + .to_stringified( + e.verbose, + plan_type, + explain_format, + ), ); }, ); @@ -1773,7 +1786,11 @@ impl DefaultPhysicalPlanner { displayable(input.as_ref()) .set_show_statistics(config.show_statistics) .set_show_schema(config.show_schema) - .to_stringified(e.verbose, FinalPhysicalPlan), + .to_stringified( + e.verbose, + FinalPhysicalPlan, + explain_format, + ), ); // Show statistics + schema in verbose output even if not @@ -1786,6 +1803,7 @@ impl DefaultPhysicalPlanner { .to_stringified( e.verbose, FinalPhysicalPlanWithStats, + explain_format, ), ); } @@ -1796,6 +1814,7 @@ impl DefaultPhysicalPlanner { .to_stringified( e.verbose, FinalPhysicalPlanWithSchema, + explain_format, ), ); } diff --git a/datafusion/physical-plan/src/aggregates/mod.rs b/datafusion/physical-plan/src/aggregates/mod.rs index 85b41da85742..81e2af6a44f2 100644 --- a/datafusion/physical-plan/src/aggregates/mod.rs +++ b/datafusion/physical-plan/src/aggregates/mod.rs @@ -789,6 +789,9 @@ impl DisplayAs for AggregateExec { write!(f, ", ordering_mode={:?}", self.input_order_mode)?; } } + DisplayFormatType::TreeRender => { + write!(f, "")?; // TODO(renjj): add display info + } } Ok(()) } diff --git a/datafusion/physical-plan/src/analyze.rs b/datafusion/physical-plan/src/analyze.rs index 708f006b0d39..996cc0540f7e 100644 --- a/datafusion/physical-plan/src/analyze.rs +++ b/datafusion/physical-plan/src/analyze.rs @@ -108,6 +108,9 @@ impl DisplayAs for AnalyzeExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "AnalyzeExec verbose={}", self.verbose) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/coalesce_batches.rs b/datafusion/physical-plan/src/coalesce_batches.rs index fa8d125d62d1..ed6abde6f5d8 100644 --- a/datafusion/physical-plan/src/coalesce_batches.rs +++ b/datafusion/physical-plan/src/coalesce_batches.rs @@ -122,6 +122,9 @@ impl DisplayAs for CoalesceBatchesExec { Ok(()) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/coalesce_partitions.rs b/datafusion/physical-plan/src/coalesce_partitions.rs index 9a955155c01e..98ff1d6cbdc8 100644 --- a/datafusion/physical-plan/src/coalesce_partitions.rs +++ b/datafusion/physical-plan/src/coalesce_partitions.rs @@ -92,6 +92,9 @@ impl DisplayAs for CoalescePartitionsExec { } None => write!(f, "CoalescePartitionsExec"), }, + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/display.rs b/datafusion/physical-plan/src/display.rs index dbf82eee05eb..39a5f266a798 100644 --- a/datafusion/physical-plan/src/display.rs +++ b/datafusion/physical-plan/src/display.rs @@ -18,12 +18,13 @@ //! Implementation of physical plan display. See //! [`crate::displayable`] for examples of how to format -use std::fmt; use std::fmt::Formatter; +use std::{fmt, str::FromStr}; use arrow_schema::SchemaRef; use datafusion_common::display::{GraphvizBuilder, PlanType, StringifiedPlan}; +use datafusion_common::DataFusionError; use datafusion_expr::display_schema; use datafusion_physical_expr::LexOrdering; @@ -36,6 +37,23 @@ pub enum DisplayFormatType { Default, /// Verbose, showing all available details Verbose, + /// TreeRender, display plan like a tree. + TreeRender, +} + +impl FromStr for DisplayFormatType { + type Err = DataFusionError; + + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_str() { + "indent" => Ok(Self::Default), + "pretty" => Ok(Self::TreeRender), + _ => Err(DataFusionError::Configuration(format!( + "Invalid explain format: {}", + s + ))), + } + } } /// Wraps an `ExecutionPlan` with various methods for formatting @@ -224,6 +242,37 @@ impl<'a> DisplayableExecutionPlan<'a> { } } + pub fn tree_render(&self) -> impl fmt::Display + 'a { + let format_type = DisplayFormatType::TreeRender; + struct Wrapper<'a> { + format_type: DisplayFormatType, + plan: &'a dyn ExecutionPlan, + show_metrics: ShowMetrics, + show_statistics: bool, + show_schema: bool, + } + impl fmt::Display for Wrapper<'_> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let mut visitor = IndentVisitor { + t: self.format_type, + f, + indent: 0, + show_metrics: self.show_metrics, + show_statistics: self.show_statistics, + show_schema: self.show_schema, + }; + accept(self.plan, &mut visitor) + } + } + Wrapper { + format_type, + plan: self.inner, + show_metrics: self.show_metrics, + show_statistics: self.show_statistics, + show_schema: self.show_schema, + } + } + /// Return a single-line summary of the root of the plan /// Example: `ProjectionExec: expr=[a@0 as a]`. pub fn one_line(&self) -> impl fmt::Display + 'a { @@ -258,8 +307,20 @@ impl<'a> DisplayableExecutionPlan<'a> { } /// format as a `StringifiedPlan` - pub fn to_stringified(&self, verbose: bool, plan_type: PlanType) -> StringifiedPlan { - StringifiedPlan::new(plan_type, self.indent(verbose).to_string()) + pub fn to_stringified( + &self, + verbose: bool, + plan_type: PlanType, + explain_format: DisplayFormatType, + ) -> StringifiedPlan { + match explain_format { + DisplayFormatType::Default | DisplayFormatType::Verbose => { + StringifiedPlan::new(plan_type, self.indent(verbose).to_string()) + } + DisplayFormatType::TreeRender => { + StringifiedPlan::new(plan_type, self.tree_render().to_string()) + } + } } } diff --git a/datafusion/physical-plan/src/empty.rs b/datafusion/physical-plan/src/empty.rs index 5168c3cc101f..43dab20c557b 100644 --- a/datafusion/physical-plan/src/empty.rs +++ b/datafusion/physical-plan/src/empty.rs @@ -94,6 +94,9 @@ impl DisplayAs for EmptyExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "EmptyExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/explain.rs b/datafusion/physical-plan/src/explain.rs index cb00958cec4c..0e355632671a 100644 --- a/datafusion/physical-plan/src/explain.rs +++ b/datafusion/physical-plan/src/explain.rs @@ -94,6 +94,9 @@ impl DisplayAs for ExplainExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "ExplainExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/filter.rs b/datafusion/physical-plan/src/filter.rs index 91c44a4139d2..5df178c102bd 100644 --- a/datafusion/physical-plan/src/filter.rs +++ b/datafusion/physical-plan/src/filter.rs @@ -329,6 +329,9 @@ impl DisplayAs for FilterExec { }; write!(f, "FilterExec: {}{}", self.predicate, display_projections) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/insert.rs b/datafusion/physical-plan/src/insert.rs index 0b8c1eede36c..18981056e71a 100644 --- a/datafusion/physical-plan/src/insert.rs +++ b/datafusion/physical-plan/src/insert.rs @@ -154,6 +154,9 @@ impl DisplayAs for DataSinkExec { write!(f, "DataSinkExec: sink=")?; self.sink.fmt_as(t, f) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/joins/cross_join.rs b/datafusion/physical-plan/src/joins/cross_join.rs index ca4c26251de0..2cc7002850c9 100644 --- a/datafusion/physical-plan/src/joins/cross_join.rs +++ b/datafusion/physical-plan/src/joins/cross_join.rs @@ -240,6 +240,9 @@ impl DisplayAs for CrossJoinExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "CrossJoinExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/joins/hash_join.rs b/datafusion/physical-plan/src/joins/hash_join.rs index 6cdd91bb1721..96ad6fc503ca 100644 --- a/datafusion/physical-plan/src/joins/hash_join.rs +++ b/datafusion/physical-plan/src/joins/hash_join.rs @@ -668,6 +668,9 @@ impl DisplayAs for HashJoinExec { self.mode, self.join_type, on, display_filter, display_projections ) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/joins/nested_loop_join.rs b/datafusion/physical-plan/src/joins/nested_loop_join.rs index 07289d861bcf..7b07ce97b137 100644 --- a/datafusion/physical-plan/src/joins/nested_loop_join.rs +++ b/datafusion/physical-plan/src/joins/nested_loop_join.rs @@ -424,6 +424,7 @@ impl DisplayAs for NestedLoopJoinExec { self.join_type, display_filter, display_projections ) } + DisplayFormatType::TreeRender => todo!(), // TODO(renjj): add display info } } } diff --git a/datafusion/physical-plan/src/joins/sort_merge_join.rs b/datafusion/physical-plan/src/joins/sort_merge_join.rs index a3e835c64131..6de4b1989675 100644 --- a/datafusion/physical-plan/src/joins/sort_merge_join.rs +++ b/datafusion/physical-plan/src/joins/sort_merge_join.rs @@ -369,6 +369,9 @@ impl DisplayAs for SortMergeJoinExec { )) ) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/joins/symmetric_hash_join.rs b/datafusion/physical-plan/src/joins/symmetric_hash_join.rs index 47af4ab9a765..a0204455fcd1 100644 --- a/datafusion/physical-plan/src/joins/symmetric_hash_join.rs +++ b/datafusion/physical-plan/src/joins/symmetric_hash_join.rs @@ -380,6 +380,9 @@ impl DisplayAs for SymmetricHashJoinExec { self.mode, self.join_type, on, display_filter ) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/limit.rs b/datafusion/physical-plan/src/limit.rs index 15f19f6456a5..f22290029c55 100644 --- a/datafusion/physical-plan/src/limit.rs +++ b/datafusion/physical-plan/src/limit.rs @@ -108,6 +108,9 @@ impl DisplayAs for GlobalLimitExec { self.fetch.map_or("None".to_string(), |x| x.to_string()) ) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } @@ -261,6 +264,9 @@ impl DisplayAs for LocalLimitExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "LocalLimitExec: fetch={}", self.fetch) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/memory.rs b/datafusion/physical-plan/src/memory.rs index 3d161a047853..0c0049787e1c 100644 --- a/datafusion/physical-plan/src/memory.rs +++ b/datafusion/physical-plan/src/memory.rs @@ -430,6 +430,9 @@ impl DataSource for MemorySourceConfig { ) } } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } @@ -867,6 +870,9 @@ impl DisplayAs for LazyMemoryExec { .join(", ") ) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/placeholder_row.rs b/datafusion/physical-plan/src/placeholder_row.rs index 6a8f247ec0e6..757c9a402ef1 100644 --- a/datafusion/physical-plan/src/placeholder_row.rs +++ b/datafusion/physical-plan/src/placeholder_row.rs @@ -112,6 +112,9 @@ impl DisplayAs for PlaceholderRowExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "PlaceholderRowExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/projection.rs b/datafusion/physical-plan/src/projection.rs index 3ebfd8f8ca80..2d7fc2252373 100644 --- a/datafusion/physical-plan/src/projection.rs +++ b/datafusion/physical-plan/src/projection.rs @@ -167,6 +167,9 @@ impl DisplayAs for ProjectionExec { write!(f, "ProjectionExec: expr=[{}]", expr.join(", ")) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/recursive_query.rs b/datafusion/physical-plan/src/recursive_query.rs index bf7d2c7f275c..0d916916613f 100644 --- a/datafusion/physical-plan/src/recursive_query.rs +++ b/datafusion/physical-plan/src/recursive_query.rs @@ -223,6 +223,9 @@ impl DisplayAs for RecursiveQueryExec { self.name, self.is_distinct ) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/repartition/mod.rs b/datafusion/physical-plan/src/repartition/mod.rs index ffd1a5b520fa..8c2b32da4792 100644 --- a/datafusion/physical-plan/src/repartition/mod.rs +++ b/datafusion/physical-plan/src/repartition/mod.rs @@ -506,6 +506,9 @@ impl DisplayAs for RepartitionExec { } Ok(()) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/sorts/partial_sort.rs b/datafusion/physical-plan/src/sorts/partial_sort.rs index eeef73c45fc4..d33d5cb29ed5 100644 --- a/datafusion/physical-plan/src/sorts/partial_sort.rs +++ b/datafusion/physical-plan/src/sorts/partial_sort.rs @@ -226,6 +226,9 @@ impl DisplayAs for PartialSortExec { None => write!(f, "PartialSortExec: expr=[{}], common_prefix_length=[{common_prefix_length}]", self.expr), } } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/sorts/sort.rs b/datafusion/physical-plan/src/sorts/sort.rs index 6c538801d71a..c00a312636f5 100644 --- a/datafusion/physical-plan/src/sorts/sort.rs +++ b/datafusion/physical-plan/src/sorts/sort.rs @@ -880,6 +880,9 @@ impl DisplayAs for SortExec { None => write!(f, "SortExec: expr=[{}], preserve_partitioning=[{preserve_partitioning}]", self.expr), } } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs b/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs index c2cd9d7db3c6..85e692217d67 100644 --- a/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs +++ b/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs @@ -185,6 +185,9 @@ impl DisplayAs for SortPreservingMergeExec { Ok(()) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/streaming.rs b/datafusion/physical-plan/src/streaming.rs index 751af9921448..bc306de7fc1f 100644 --- a/datafusion/physical-plan/src/streaming.rs +++ b/datafusion/physical-plan/src/streaming.rs @@ -209,6 +209,9 @@ impl DisplayAs for StreamingTableExec { Ok(()) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/union.rs b/datafusion/physical-plan/src/union.rs index 7e0f88784644..21eb81f5bff9 100644 --- a/datafusion/physical-plan/src/union.rs +++ b/datafusion/physical-plan/src/union.rs @@ -157,6 +157,9 @@ impl DisplayAs for UnionExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "UnionExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } @@ -387,6 +390,9 @@ impl DisplayAs for InterleaveExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "InterleaveExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/unnest.rs b/datafusion/physical-plan/src/unnest.rs index 430391de5922..a624f801aa93 100644 --- a/datafusion/physical-plan/src/unnest.rs +++ b/datafusion/physical-plan/src/unnest.rs @@ -137,6 +137,9 @@ impl DisplayAs for UnnestExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "UnnestExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/values.rs b/datafusion/physical-plan/src/values.rs index ea1086c0a3d6..7c57263b755a 100644 --- a/datafusion/physical-plan/src/values.rs +++ b/datafusion/physical-plan/src/values.rs @@ -163,6 +163,9 @@ impl DisplayAs for ValuesExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "ValuesExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs b/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs index a734feae5533..779b2a3fbe35 100644 --- a/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs +++ b/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs @@ -252,6 +252,9 @@ impl DisplayAs for BoundedWindowAggExec { let mode = &self.input_order_mode; write!(f, "wdw=[{}], mode=[{:?}]", g.join(", "), mode)?; } + DisplayFormatType::TreeRender => { + write!(f, "")?; // TODO(renjj): add display info + } } Ok(()) } diff --git a/datafusion/physical-plan/src/windows/window_agg_exec.rs b/datafusion/physical-plan/src/windows/window_agg_exec.rs index d31fd66ca1f1..69fef85c8c82 100644 --- a/datafusion/physical-plan/src/windows/window_agg_exec.rs +++ b/datafusion/physical-plan/src/windows/window_agg_exec.rs @@ -181,6 +181,9 @@ impl DisplayAs for WindowAggExec { .collect(); write!(f, "wdw=[{}]", g.join(", "))?; } + DisplayFormatType::TreeRender => { + write!(f, "")?; // TODO(renjj): add display info + } } Ok(()) } diff --git a/datafusion/physical-plan/src/work_table.rs b/datafusion/physical-plan/src/work_table.rs index a6ced527cbb2..03b352e08c70 100644 --- a/datafusion/physical-plan/src/work_table.rs +++ b/datafusion/physical-plan/src/work_table.rs @@ -162,6 +162,9 @@ impl DisplayAs for WorkTableExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "WorkTableExec: name={}", self.name) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } From d53d876dcb469c99d67c0c826038edf47a3102c8 Mon Sep 17 00:00:00 2001 From: irenjj Date: Sat, 15 Feb 2025 13:31:38 +0800 Subject: [PATCH 02/17] fix compile errors --- datafusion-examples/examples/planner_api.rs | 20 ++++++++-- datafusion/core/src/physical_planner.rs | 3 ++ .../core/tests/custom_sources_cases/mod.rs | 3 ++ .../provider_filter_pushdown.rs | 3 ++ .../tests/custom_sources_cases/statistics.rs | 3 ++ .../physical_optimizer/join_selection.rs | 6 +++ .../tests/user_defined/user_defined_plan.rs | 3 ++ .../physical-plan/src/aggregates/mod.rs | 3 ++ datafusion/physical-plan/src/display.rs | 38 ++++++++++++++++++- datafusion/physical-plan/src/sorts/sort.rs | 3 ++ .../src/sorts/sort_preserving_merge.rs | 3 ++ datafusion/physical-plan/src/test/exec.rs | 18 +++++++++ 12 files changed, 101 insertions(+), 5 deletions(-) diff --git a/datafusion-examples/examples/planner_api.rs b/datafusion-examples/examples/planner_api.rs index e52f0d78682f..41110a3e0a9c 100644 --- a/datafusion-examples/examples/planner_api.rs +++ b/datafusion-examples/examples/planner_api.rs @@ -17,7 +17,7 @@ use datafusion::error::Result; use datafusion::logical_expr::{LogicalPlan, PlanType}; -use datafusion::physical_plan::displayable; +use datafusion::physical_plan::{displayable, DisplayFormatType}; use datafusion::physical_planner::DefaultPhysicalPlanner; use datafusion::prelude::*; @@ -78,7 +78,11 @@ async fn to_physical_plan_in_one_api_demo( println!( "Physical plan direct from logical plan:\n\n{}\n\n", displayable(physical_plan.as_ref()) - .to_stringified(false, PlanType::InitialPhysicalPlan) + .to_stringified( + false, + PlanType::InitialPhysicalPlan, + DisplayFormatType::Default + ) .plan ); @@ -120,7 +124,11 @@ async fn to_physical_plan_step_by_step_demo( println!( "Final physical plan:\n\n{}\n\n", displayable(physical_plan.as_ref()) - .to_stringified(false, PlanType::InitialPhysicalPlan) + .to_stringified( + false, + PlanType::InitialPhysicalPlan, + DisplayFormatType::Default + ) .plan ); @@ -135,7 +143,11 @@ async fn to_physical_plan_step_by_step_demo( println!( "Optimized physical plan:\n\n{}\n\n", displayable(physical_plan.as_ref()) - .to_stringified(false, PlanType::InitialPhysicalPlan) + .to_stringified( + false, + PlanType::InitialPhysicalPlan, + DisplayFormatType::Default + ) .plan ); diff --git a/datafusion/core/src/physical_planner.rs b/datafusion/core/src/physical_planner.rs index 9ccde53f207c..05b05f45367a 100644 --- a/datafusion/core/src/physical_planner.rs +++ b/datafusion/core/src/physical_planner.rs @@ -2721,6 +2721,9 @@ mod tests { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "NoOpExecutionPlan") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/core/tests/custom_sources_cases/mod.rs b/datafusion/core/tests/custom_sources_cases/mod.rs index aafefac04e32..de2bd71de9df 100644 --- a/datafusion/core/tests/custom_sources_cases/mod.rs +++ b/datafusion/core/tests/custom_sources_cases/mod.rs @@ -138,6 +138,9 @@ impl DisplayAs for CustomExecutionPlan { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "CustomExecutionPlan: projection={:#?}", self.projection) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs b/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs index af0506a50558..4cda92216b49 100644 --- a/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs +++ b/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs @@ -92,6 +92,9 @@ impl DisplayAs for CustomPlan { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "CustomPlan: batch_size={}", self.batches.len(),) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/core/tests/custom_sources_cases/statistics.rs b/datafusion/core/tests/custom_sources_cases/statistics.rs index 1fd6dfec79fb..2d258b358552 100644 --- a/datafusion/core/tests/custom_sources_cases/statistics.rs +++ b/datafusion/core/tests/custom_sources_cases/statistics.rs @@ -141,6 +141,9 @@ impl DisplayAs for StatisticsValidation { self.stats.num_rows, ) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/core/tests/physical_optimizer/join_selection.rs b/datafusion/core/tests/physical_optimizer/join_selection.rs index ae7adacadb19..82c1044d1763 100644 --- a/datafusion/core/tests/physical_optimizer/join_selection.rs +++ b/datafusion/core/tests/physical_optimizer/join_selection.rs @@ -925,6 +925,9 @@ impl DisplayAs for UnboundedExec { self.batch_produce.is_none(), ) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } @@ -1020,6 +1023,9 @@ impl DisplayAs for StatisticsExec { self.stats.num_rows, ) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/core/tests/user_defined/user_defined_plan.rs b/datafusion/core/tests/user_defined/user_defined_plan.rs index fae4b2cd82ab..3e52afbbae26 100644 --- a/datafusion/core/tests/user_defined/user_defined_plan.rs +++ b/datafusion/core/tests/user_defined/user_defined_plan.rs @@ -700,6 +700,9 @@ impl DisplayAs for TopKExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "TopKExec: k={}", self.k) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/aggregates/mod.rs b/datafusion/physical-plan/src/aggregates/mod.rs index 81e2af6a44f2..956046ecbb38 100644 --- a/datafusion/physical-plan/src/aggregates/mod.rs +++ b/datafusion/physical-plan/src/aggregates/mod.rs @@ -1800,6 +1800,9 @@ mod tests { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "TestYieldingExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } diff --git a/datafusion/physical-plan/src/display.rs b/datafusion/physical-plan/src/display.rs index 39a5f266a798..6f61c8623e97 100644 --- a/datafusion/physical-plan/src/display.rs +++ b/datafusion/physical-plan/src/display.rs @@ -253,13 +253,15 @@ impl<'a> DisplayableExecutionPlan<'a> { } impl fmt::Display for Wrapper<'_> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let mut visitor = IndentVisitor { + let mut visitor = TreeRenderVisitor { t: self.format_type, f, indent: 0, show_metrics: self.show_metrics, show_statistics: self.show_statistics, show_schema: self.show_schema, + height: 0, + weight: 0, }; accept(self.plan, &mut visitor) } @@ -509,6 +511,40 @@ impl ExecutionPlanVisitor for GraphvizVisitor<'_, '_> { } } +struct TreeRenderVisitor<'a, 'b> { + /// How to format each node + t: DisplayFormatType, + /// Write to this formatter + f: &'a mut Formatter<'b>, + /// Indent size + indent: usize, + /// How to show metrics + show_metrics: ShowMetrics, + /// If statistics should be displayed + show_statistics: bool, + /// If schema should be displayed + show_schema: bool, + height: usize, + weight: usize, +} + +impl ExecutionPlanVisitor for TreeRenderVisitor<'_, '_> { + type Error = fmt::Error; + + fn pre_visit(&mut self, plan: &dyn ExecutionPlan) -> Result { + + self.height += 1; + self.weight += 1; + todo!() + } + + fn post_visit(&mut self, _plan: &dyn ExecutionPlan) -> Result { + + self.weight -= 1; + Ok(true) + } +} + /// Trait for types which could have additional details when formatted in `Verbose` mode pub trait DisplayAs { /// Format according to `DisplayFormatType`, used when verbose representation looks diff --git a/datafusion/physical-plan/src/sorts/sort.rs b/datafusion/physical-plan/src/sorts/sort.rs index c00a312636f5..dfc8d58aacd1 100644 --- a/datafusion/physical-plan/src/sorts/sort.rs +++ b/datafusion/physical-plan/src/sorts/sort.rs @@ -1102,6 +1102,9 @@ mod tests { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "UnboundableExec",).unwrap() } + DisplayFormatType::TreeRender => { + write!(f, "").unwrap() // TODO(renjj): add display info + } } Ok(()) } diff --git a/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs b/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs index 85e692217d67..9b38372845ee 100644 --- a/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs +++ b/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs @@ -1389,6 +1389,9 @@ mod tests { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "CongestedExec",).unwrap() } + DisplayFormatType::TreeRender => { + write!(f, "").unwrap() // TODO(renjj): add display info + } } Ok(()) } diff --git a/datafusion/physical-plan/src/test/exec.rs b/datafusion/physical-plan/src/test/exec.rs index f0149faa8433..55d0a8dad26c 100644 --- a/datafusion/physical-plan/src/test/exec.rs +++ b/datafusion/physical-plan/src/test/exec.rs @@ -175,6 +175,9 @@ impl DisplayAs for MockExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "MockExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } @@ -337,6 +340,9 @@ impl DisplayAs for BarrierExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "BarrierExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } @@ -449,6 +455,9 @@ impl DisplayAs for ErrorExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "ErrorExec") } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } @@ -535,6 +544,9 @@ impl DisplayAs for StatisticsExec { self.stats.num_rows, ) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } @@ -630,6 +642,9 @@ impl DisplayAs for BlockingExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "BlockingExec",) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } @@ -772,6 +787,9 @@ impl DisplayAs for PanicExec { DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "PanicExec",) } + DisplayFormatType::TreeRender => { + write!(f, "") // TODO(renjj): add display info + } } } } From d143783200dde0fc2fbcebe9305f82613e8da797 Mon Sep 17 00:00:00 2001 From: irenjj Date: Sun, 23 Feb 2025 17:19:33 +0800 Subject: [PATCH 03/17] render_tree --- .../physical_plan/file_scan_config.rs | 5 + datafusion/physical-plan/src/display.rs | 416 +++++++++++++++--- datafusion/physical-plan/src/lib.rs | 1 + datafusion/physical-plan/src/memory.rs | 29 ++ datafusion/physical-plan/src/render_tree.rs | 141 ++++++ datafusion/physical-plan/src/source.rs | 6 + 6 files changed, 549 insertions(+), 49 deletions(-) create mode 100644 datafusion/physical-plan/src/render_tree.rs diff --git a/datafusion/core/src/datasource/physical_plan/file_scan_config.rs b/datafusion/core/src/datasource/physical_plan/file_scan_config.rs index dc9207da51cb..fc295b078c74 100644 --- a/datafusion/core/src/datasource/physical_plan/file_scan_config.rs +++ b/datafusion/core/src/datasource/physical_plan/file_scan_config.rs @@ -277,6 +277,11 @@ impl DataSource for FileScanConfig { file_scan.new_exec() as _ })) } + + fn collect_info(&self) -> HashMap { + // TODO: collect explain info + HashMap::new() + } } impl FileScanConfig { diff --git a/datafusion/physical-plan/src/display.rs b/datafusion/physical-plan/src/display.rs index 6f61c8623e97..f209b8529a7e 100644 --- a/datafusion/physical-plan/src/display.rs +++ b/datafusion/physical-plan/src/display.rs @@ -18,6 +18,7 @@ //! Implementation of physical plan display. See //! [`crate::displayable`] for examples of how to format +use std::collections::HashMap; use std::fmt::Formatter; use std::{fmt, str::FromStr}; @@ -28,6 +29,8 @@ use datafusion_common::DataFusionError; use datafusion_expr::display_schema; use datafusion_physical_expr::LexOrdering; +use crate::render_tree::RenderTree; + use super::{accept, ExecutionPlan, ExecutionPlanVisitor}; /// Options for controlling how each [`ExecutionPlan`] should format itself @@ -243,36 +246,16 @@ impl<'a> DisplayableExecutionPlan<'a> { } pub fn tree_render(&self) -> impl fmt::Display + 'a { - let format_type = DisplayFormatType::TreeRender; struct Wrapper<'a> { - format_type: DisplayFormatType, plan: &'a dyn ExecutionPlan, - show_metrics: ShowMetrics, - show_statistics: bool, - show_schema: bool, } impl fmt::Display for Wrapper<'_> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let mut visitor = TreeRenderVisitor { - t: self.format_type, - f, - indent: 0, - show_metrics: self.show_metrics, - show_statistics: self.show_statistics, - show_schema: self.show_schema, - height: 0, - weight: 0, - }; - accept(self.plan, &mut visitor) + let mut visitor = TreeRenderVisitor { f }; + visitor.visit(self.plan) } } - Wrapper { - format_type, - plan: self.inner, - show_metrics: self.show_metrics, - show_statistics: self.show_statistics, - show_schema: self.show_schema, - } + Wrapper { plan: self.inner } } /// Return a single-line summary of the root of the plan @@ -315,13 +298,11 @@ impl<'a> DisplayableExecutionPlan<'a> { plan_type: PlanType, explain_format: DisplayFormatType, ) -> StringifiedPlan { - match explain_format { - DisplayFormatType::Default | DisplayFormatType::Verbose => { - StringifiedPlan::new(plan_type, self.indent(verbose).to_string()) - } - DisplayFormatType::TreeRender => { + match (&explain_format, &plan_type) { + (DisplayFormatType::TreeRender, PlanType::FinalPhysicalPlan) => { StringifiedPlan::new(plan_type, self.tree_render().to_string()) } + _ => StringifiedPlan::new(plan_type, self.indent(verbose).to_string()), } } } @@ -512,36 +493,369 @@ impl ExecutionPlanVisitor for GraphvizVisitor<'_, '_> { } struct TreeRenderVisitor<'a, 'b> { - /// How to format each node - t: DisplayFormatType, /// Write to this formatter f: &'a mut Formatter<'b>, - /// Indent size - indent: usize, - /// How to show metrics - show_metrics: ShowMetrics, - /// If statistics should be displayed - show_statistics: bool, - /// If schema should be displayed - show_schema: bool, - height: usize, - weight: usize, } -impl ExecutionPlanVisitor for TreeRenderVisitor<'_, '_> { - type Error = fmt::Error; +impl TreeRenderVisitor<'_, '_> { + // TODO: Make these variables configurable. + const LTCORNER: &'static str = "┌"; + const RTCORNER: &'static str = "┐"; + const LDCORNER: &'static str = "└"; + const RDCORNER: &'static str = "┘"; + + const TMIDDLE: &'static str = "┬"; + const LMIDDLE: &'static str = "├"; + const DMIDDLE: &'static str = "┴"; + + const VERTICAL: &'static str = "│"; + const HORIZONTAL: &'static str = "─"; + + const MAXIMUM_RENDER_WIDTH: usize = 240; + const NODE_RENDER_WIDTH: usize = 29; + const MAX_EXTRA_LINES: usize = 30; + + pub fn visit(&mut self, plan: &dyn ExecutionPlan) -> Result<(), fmt::Error> { + let root = RenderTree::create_tree(plan); + + for y in 0..root.height { + // Start by rendering the top layer. + self.render_top_layer(&root, y)?; + // Now we render the content of the boxes + self.render_box_content(&root, y)?; + // Render the bottom layer of each of the boxes + self.render_bottom_layer(&root, y)?; + } - fn pre_visit(&mut self, plan: &dyn ExecutionPlan) -> Result { + Ok(()) + } - self.height += 1; - self.weight += 1; - todo!() + fn render_top_layer( + &mut self, + root: &RenderTree, + y: usize, + ) -> Result<(), fmt::Error> { + for x in 0..root.width { + if root.has_node(x, y) { + write!(self.f, "{}", Self::LTCORNER)?; + write!( + self.f, + "{}", + Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1) + )?; + if y == 0 { + // top level node: no node above this one + write!(self.f, "{}", Self::HORIZONTAL)?; + } else { + // render connection to node above this one + write!(self.f, "{}", Self::DMIDDLE)?; + } + write!( + self.f, + "{}", + Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1) + )?; + write!(self.f, "{}", Self::RTCORNER)?; + } else { + let mut has_adjacent_nodes = false; + for i in 0..(root.width - x) { + has_adjacent_nodes = has_adjacent_nodes || root.has_node(x + i, y); + } + if !has_adjacent_nodes { + // There are no nodes to the right side of this position + // no need to fill the empty space + continue; + } + // there are nodes next to this, fill the space + write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH))?; + } + } + writeln!(self.f)?; + + Ok(()) } - fn post_visit(&mut self, _plan: &dyn ExecutionPlan) -> Result { + fn render_box_content( + &mut self, + root: &RenderTree, + y: usize, + ) -> Result<(), fmt::Error> { + let mut extra_info: Vec> = vec![vec![]; root.width]; + let mut extra_height = 0; + + for (x, extra_info_item) in extra_info.iter_mut().enumerate().take(root.width) { + if let Some(node) = root.get_node(x, y) { + Self::split_up_extra_info( + &node.extra_text, + extra_info_item, + Self::MAX_EXTRA_LINES, + ); + if extra_info_item.len() > extra_height { + extra_height = extra_info_item.len(); + } + } + } - self.weight -= 1; - Ok(true) + let halfway_point = (extra_height + 1) / 2; + + // Render the actual node. + for render_y in 0..=extra_height { + for (x, node) in root.nodes.iter().enumerate().take(root.width) { + if x * Self::NODE_RENDER_WIDTH >= Self::MAXIMUM_RENDER_WIDTH { + break; + } + + let has_adjacent_nodes = + (0..root.width - x).any(|i| root.has_node(x + i, y)); + + if let Some(node) = node { + write!(self.f, "{}", Self::VERTICAL)?; + + // Rigure out what to render. + let mut render_text = String::new(); + if render_y == 0 { + render_text = node.name.clone(); + } else if render_y <= extra_info[x].len() { + render_text = extra_info[x][render_y - 1].clone(); + } + + render_text = Self::adjust_text_for_rendering( + &render_text, + Self::NODE_RENDER_WIDTH - 2, + ); + write!(self.f, "{}", render_text)?; + + if render_y == halfway_point && node.child_positions.len() > 1 { + write!(self.f, "{}", Self::LMIDDLE)?; + } else { + write!(self.f, "{}", Self::VERTICAL)?; + } + } else if render_y == halfway_point { + let has_child_to_the_right = + Self::should_render_whitespace(root, x, y); + if root.has_node(x, y + 1) { + // Node right below this one. + write!( + self.f, + "{}", + Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2) + )?; + if has_child_to_the_right { + write!(self.f, "{}", Self::TMIDDLE)?; + // Have another child to the right, Keep rendering the line. + write!( + self.f, + "{}", + Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2) + )?; + } else { + write!(self.f, "{}", Self::RTCORNER)?; + if has_adjacent_nodes { + // Only a child below this one: fill the reset with spaces. + write!( + self.f, + "{}", + " ".repeat(Self::NODE_RENDER_WIDTH / 2) + )?; + } + } + } else if has_child_to_the_right { + // Child to the right, but no child right below this one: render a full + // line. + write!( + self.f, + "{}", + Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH) + )?; + } else if has_adjacent_nodes { + // Empty spot: render spaces. + write!(self.f, "{}", " ".repeat(Self::NODE_RENDER_WIDTH))?; + } + } else if render_y >= halfway_point { + if root.has_node(x, y + 1) { + // Have a node below this empty spot: render a vertical line. + write!( + self.f, + "{}{}", + " ".repeat(Self::NODE_RENDER_WIDTH / 2), + Self::VERTICAL + )?; + if has_adjacent_nodes + || Self::should_render_whitespace(root, x, y) + { + write!( + self.f, + "{}", + " ".repeat(Self::NODE_RENDER_WIDTH / 2) + )?; + } + } else if has_adjacent_nodes + || Self::should_render_whitespace(root, x, y) + { + // Empty spot: render spaces. + write!(self.f, "{}", " ".repeat(Self::NODE_RENDER_WIDTH))?; + } + } else if has_adjacent_nodes { + // Empty spot: render spaces. + write!(self.f, "{}", " ".repeat(Self::NODE_RENDER_WIDTH))?; + } + } + writeln!(self.f)?; + } + + Ok(()) + } + + fn render_bottom_layer( + &mut self, + root: &RenderTree, + y: usize, + ) -> Result<(), fmt::Error> { + for x in 0..=root.width { + if x * Self::NODE_RENDER_WIDTH >= Self::MAXIMUM_RENDER_WIDTH { + break; + } + let mut has_adjacent_nodes = false; + for i in 0..(root.width - x) { + has_adjacent_nodes = has_adjacent_nodes || root.has_node(x + i, y); + } + if root.get_node(x, y).is_some() { + write!(self.f, "{}", Self::LDCORNER)?; + write!( + self.f, + "{}", + Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1) + )?; + if root.has_node(x, y + 1) { + // node below this one: connect to that one + write!(self.f, "{}", Self::TMIDDLE)?; + } else { + // no node below this one: end the box + write!(self.f, "{}", Self::HORIZONTAL)?; + } + write!( + self.f, + "{}", + Self::HORIZONTAL.repeat(Self::NODE_RENDER_WIDTH / 2 - 1) + )?; + write!(self.f, "{}", Self::RDCORNER)?; + } else if root.has_node(x, y + 1) { + write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH / 2))?; + write!(self.f, "{}", Self::VERTICAL)?; + if has_adjacent_nodes || Self::should_render_whitespace(root, x, y) { + write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH / 2))?; + } + } else if has_adjacent_nodes || Self::should_render_whitespace(root, x, y) { + write!(self.f, "{}", &" ".repeat(Self::NODE_RENDER_WIDTH))?; + } + } + writeln!(self.f)?; + + Ok(()) + } + + fn extra_info_separator() -> String { + "-".repeat(Self::NODE_RENDER_WIDTH - 9) + } + + fn remove_padding(s: &str) -> String { + s.trim().to_string() + } + + fn split_up_extra_info( + extra_info: &HashMap, + result: &mut Vec, + max_lines: usize, + ) { + if extra_info.is_empty() { + return; + } + + result.push(Self::extra_info_separator()); + + let mut requires_padding = false; + let mut was_inlined = false; + for (key, value) in extra_info { + let mut str = Self::remove_padding(value); + if str.is_empty() { + continue; + } + let mut is_inlined = false; + let available_width = Self::NODE_RENDER_WIDTH - 7; + let total_size = key.len() + str.len() + 2; + let is_multiline = str.contains('\n'); + if !is_multiline && total_size < available_width { + str = format!("{}: {}", key, str); + is_inlined = true; + } else { + str = format!("{}:\n{}", key, str); + } + if is_inlined && was_inlined { + requires_padding = false; + } + if requires_padding { + result.push(String::new()); + } + + let mut splits: Vec = str.split('\n').map(String::from).collect(); + if splits.len() > max_lines { + let mut truncated_splits = Vec::new(); + for split in splits.iter().take(max_lines / 2) { + truncated_splits.push(split.clone()); + } + truncated_splits.push("...".to_string()); + for split in splits.iter().skip(splits.len() - max_lines / 2) { + truncated_splits.push(split.clone()); + } + splits = truncated_splits; + } + for split in splits { + // TODO: check every line is less than MAX_LINE_RENDER_SIZE. + result.push(split); + } + requires_padding = true; + was_inlined = is_inlined; + } + } + + fn adjust_text_for_rendering(source: &str, max_render_width: usize) -> String { + let render_width = source.chars().count(); + if render_width > max_render_width { + let truncated = &source[..max_render_width - 3]; + format!("{}...", truncated) + } else { + let total_spaces = max_render_width - render_width; + let half_spaces = total_spaces / 2; + let extra_left_space = if total_spaces % 2 == 0 { 0 } else { 1 }; + format!( + "{}{}{}", + " ".repeat(half_spaces + extra_left_space), + source, + " ".repeat(half_spaces) + ) + } + } + + fn should_render_whitespace(root: &RenderTree, x: usize, y: usize) -> bool { + let mut found_children = 0; + + for i in (0..=x).rev() { + let node = root.get_node(i, y); + if root.has_node(i, y + 1) { + found_children += 1; + } + if let Some(node) = node { + if node.child_positions.len() > 1 + && found_children < node.child_positions.len() + { + return true; + } + + return false; + } + } + + false } } @@ -552,6 +866,10 @@ pub trait DisplayAs { /// /// Should not include a newline fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result; + + fn collect_info(&self) -> HashMap { + HashMap::new() + } } /// A new type wrapper to display `T` implementing`DisplayAs` using the `Default` mode diff --git a/datafusion/physical-plan/src/lib.rs b/datafusion/physical-plan/src/lib.rs index 9210e3b0273c..bdbde643c724 100644 --- a/datafusion/physical-plan/src/lib.rs +++ b/datafusion/physical-plan/src/lib.rs @@ -47,6 +47,7 @@ pub use crate::topk::TopK; pub use crate::visitor::{accept, visit_execution_plan, ExecutionPlanVisitor}; mod ordering; +mod render_tree; mod topk; mod visitor; diff --git a/datafusion/physical-plan/src/memory.rs b/datafusion/physical-plan/src/memory.rs index 0c0049787e1c..f1b5cf121738 100644 --- a/datafusion/physical-plan/src/memory.rs +++ b/datafusion/physical-plan/src/memory.rs @@ -18,6 +18,7 @@ //! Execution plan for reading in-memory batches of data use std::any::Any; +use std::collections::HashMap; use std::fmt; use std::sync::Arc; use std::task::{Context, Poll}; @@ -436,6 +437,34 @@ impl DataSource for MemorySourceConfig { } } + fn collect_info(&self) -> HashMap { + let mut result = HashMap::new(); + + let partition_sizes: Vec<_> = self.partitions.iter().map(|b| b.len()).collect(); + result.insert( + "partition_sizes".to_string(), + format!("{:?}", partition_sizes), + ); + + if let Some(output_ordering) = self.sort_information.first() { + result.insert("output_ordering".to_string(), output_ordering.to_string()); + } + + let eq_properties = self.eq_properties(); + let constraints = eq_properties.constraints(); + if !constraints.is_empty() { + result.insert("constraints".to_string(), constraints.to_string()); + } + + if let Some(limit) = self.fetch { + result.insert("fetch".to_string(), limit.to_string()); + } + + result.insert("partitions".to_string(), partition_sizes.len().to_string()); + + result + } + fn output_partitioning(&self) -> Partitioning { Partitioning::UnknownPartitioning(self.partitions.len()) } diff --git a/datafusion/physical-plan/src/render_tree.rs b/datafusion/physical-plan/src/render_tree.rs new file mode 100644 index 000000000000..95e28232bea9 --- /dev/null +++ b/datafusion/physical-plan/src/render_tree.rs @@ -0,0 +1,141 @@ +use std::cmp; +use std::collections::HashMap; +use std::sync::Arc; + +use crate::ExecutionPlan; + +#[allow(dead_code)] +pub struct Coordinate { + pub x: usize, + pub y: usize, +} + +impl Coordinate { + pub fn new(x: usize, y: usize) -> Self { + Coordinate { x, y } + } +} + +pub struct RenderTreeNode { + pub name: String, + pub extra_text: HashMap, + pub child_positions: Vec, +} + +impl RenderTreeNode { + pub fn new(name: String, extra_text: HashMap) -> Self { + RenderTreeNode { + name, + extra_text, + child_positions: vec![], + } + } + + fn add_child_position(&mut self, x: usize, y: usize) { + self.child_positions.push(Coordinate::new(x, y)); + } +} + +pub struct RenderTree { + pub nodes: Vec>>, + pub width: usize, + pub height: usize, +} + +impl RenderTree { + pub fn create_tree(plan: &dyn ExecutionPlan) -> Self { + let (width, height) = get_tree_width_height(plan); + + let mut result = Self::new(width, height); + + create_tree_recursive(&mut result, plan, 0, 0); + + result + } + + fn new(width: usize, height: usize) -> Self { + RenderTree { + nodes: vec![None; (width + 1) * (height + 1)], + width, + height, + } + } + + pub fn get_node(&self, x: usize, y: usize) -> Option> { + if x >= self.width || y >= self.height { + return None; + } + + let pos = self.get_position(x, y); + self.nodes.get(pos).and_then(|node| node.clone()) + } + + pub fn set_node(&mut self, x: usize, y: usize, node: Arc) { + let pos = self.get_position(x, y); + if let Some(slot) = self.nodes.get_mut(pos) { + *slot = Some(node); + } + } + + pub fn has_node(&self, x: usize, y: usize) -> bool { + if x >= self.width || y >= self.height { + return false; + } + + let pos = self.get_position(x, y); + self.nodes.get(pos).is_some_and(|node| node.is_some()) + } + + fn get_position(&self, x: usize, y: usize) -> usize { + y * self.width + x + } +} + +fn get_tree_width_height(plan: &dyn ExecutionPlan) -> (usize, usize) { + let children = plan.children(); + + if children.is_empty() { + return (1, 1); + } + + let mut width = 0; + let mut height = 0; + + for child in children { + let (child_width, child_height) = get_tree_width_height(child.as_ref()); + width += child_width; + height = cmp::max(height, child_height); + } + + height += 1; + + (width, height) +} + +fn create_tree_recursive( + result: &mut RenderTree, + plan: &dyn ExecutionPlan, + x: usize, + y: usize, +) -> usize { + let mut node = RenderTreeNode::new(plan.name().to_string(), plan.collect_info()); + + let children = plan.children(); + + if children.is_empty() { + result.set_node(x, y, Arc::new(node)); + return 1; + } + + let mut width = 0; + for child in children { + let child_x = x + width; + let child_y = y + 1; + node.add_child_position(child_x, child_y); + width += create_tree_recursive(result, child.as_ref(), child_x, child_y); + } + + result.set_node(x, y, Arc::new(node)); + + width +} diff --git a/datafusion/physical-plan/src/source.rs b/datafusion/physical-plan/src/source.rs index 0c1dfddd2678..fe45ce2760e4 100644 --- a/datafusion/physical-plan/src/source.rs +++ b/datafusion/physical-plan/src/source.rs @@ -16,6 +16,7 @@ // under the License. use std::any::Any; +use std::collections::HashMap; use std::fmt; use std::fmt::{Debug, Formatter}; use std::sync::Arc; @@ -41,6 +42,7 @@ pub trait DataSource: Send + Sync { ) -> datafusion_common::Result; fn as_any(&self) -> &dyn Any; fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result; + fn collect_info(&self) -> HashMap; fn repartitioned( &self, _target_partitions: usize, @@ -82,6 +84,10 @@ impl DisplayAs for DataSourceExec { write!(f, "DataSourceExec: ")?; self.source.fmt_as(t, f) } + + fn collect_info(&self) -> HashMap { + self.source.collect_info() + } } impl ExecutionPlan for DataSourceExec { From 8d2ecf52b01bbaca74f540e07aee97a47e4321df Mon Sep 17 00:00:00 2001 From: irenjj Date: Sun, 23 Feb 2025 20:22:18 +0800 Subject: [PATCH 04/17] fix compile error --- .../physical_plan/file_scan_config.rs | 1 + datafusion/datasource/src/memory.rs | 102 +++++++++++------- 2 files changed, 67 insertions(+), 36 deletions(-) diff --git a/datafusion/core/src/datasource/physical_plan/file_scan_config.rs b/datafusion/core/src/datasource/physical_plan/file_scan_config.rs index e62b3cb2dc06..eb4556a89231 100644 --- a/datafusion/core/src/datasource/physical_plan/file_scan_config.rs +++ b/datafusion/core/src/datasource/physical_plan/file_scan_config.rs @@ -26,6 +26,7 @@ use crate::datasource::file_format::file_compression_type::FileCompressionType; use crate::datasource::{listing::PartitionedFile, object_store::ObjectStoreUrl}; use crate::{error::Result, scalar::ScalarValue}; use std::any::Any; +use std::collections::HashMap; use std::fmt::Formatter; use std::{fmt, sync::Arc}; diff --git a/datafusion/datasource/src/memory.rs b/datafusion/datasource/src/memory.rs index efb178ad078e..46bffad5c9af 100644 --- a/datafusion/datasource/src/memory.rs +++ b/datafusion/datasource/src/memory.rs @@ -18,6 +18,7 @@ //! Execution plan for reading in-memory batches of data use std::any::Any; +use std::collections::HashMap; use std::fmt; use std::sync::Arc; @@ -388,42 +389,43 @@ impl DataSource for MemorySourceConfig { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { DisplayFormatType::Default | DisplayFormatType::Verbose => { - let partition_sizes: Vec<_> = - self.partitions.iter().map(|b| b.len()).collect(); - - let output_ordering = self - .sort_information - .first() - .map(|output_ordering| { - format!(", output_ordering={}", output_ordering) - }) - .unwrap_or_default(); - - let eq_properties = self.eq_properties(); - let constraints = eq_properties.constraints(); - let constraints = if constraints.is_empty() { - String::new() - } else { - format!(", {}", constraints) - }; - - let limit = self - .fetch - .map_or(String::new(), |limit| format!(", fetch={}", limit)); - if self.show_sizes { - write!( - f, - "partitions={}, partition_sizes={partition_sizes:?}{limit}{output_ordering}{constraints}", - partition_sizes.len(), - ) - } else { - write!( - f, - "partitions={}{limit}{output_ordering}{constraints}", - partition_sizes.len(), - ) - } - } + let partition_sizes: Vec<_> = + self.partitions.iter().map(|b| b.len()).collect(); + + let output_ordering = self + .sort_information + .first() + .map(|output_ordering| { + format!(", output_ordering={}", output_ordering) + }) + .unwrap_or_default(); + + let eq_properties = self.eq_properties(); + let constraints = eq_properties.constraints(); + let constraints = if constraints.is_empty() { + String::new() + } else { + format!(", {}", constraints) + }; + + let limit = self + .fetch + .map_or(String::new(), |limit| format!(", fetch={}", limit)); + if self.show_sizes { + write!( + f, + "partitions={}, partition_sizes={partition_sizes:?}{limit}{output_ordering}{constraints}", + partition_sizes.len(), + ) + } else { + write!( + f, + "partitions={}{limit}{output_ordering}{constraints}", + partition_sizes.len(), + ) + } + } + DisplayFormatType::TreeRender => todo!(), } } @@ -478,6 +480,34 @@ impl DataSource for MemorySourceConfig { }) .transpose() } + + fn collect_info(&self) -> HashMap { + let mut result = HashMap::new(); + + let partition_sizes: Vec<_> = self.partitions.iter().map(|b| b.len()).collect(); + result.insert( + "partition_sizes".to_string(), + format!("{:?}", partition_sizes), + ); + + if let Some(output_ordering) = self.sort_information.first() { + result.insert("output_ordering".to_string(), output_ordering.to_string()); + } + + let eq_properties = self.eq_properties(); + let constraints = eq_properties.constraints(); + if !constraints.is_empty() { + result.insert("constraints".to_string(), constraints.to_string()); + } + + if let Some(limit) = self.fetch { + result.insert("fetch".to_string(), limit.to_string()); + } + + result.insert("partitions".to_string(), partition_sizes.len().to_string()); + + result + } } impl MemorySourceConfig { From d905f46bbd61f643c64d52a52d2bae746b31bb7e Mon Sep 17 00:00:00 2001 From: irenjj Date: Sun, 23 Feb 2025 20:25:06 +0800 Subject: [PATCH 05/17] fmt --- datafusion/datasource/src/memory.rs | 64 +++++++++++++------------- datafusion/physical-plan/src/memory.rs | 2 +- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/datafusion/datasource/src/memory.rs b/datafusion/datasource/src/memory.rs index 46bffad5c9af..165489d5d1ca 100644 --- a/datafusion/datasource/src/memory.rs +++ b/datafusion/datasource/src/memory.rs @@ -389,42 +389,42 @@ impl DataSource for MemorySourceConfig { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { DisplayFormatType::Default | DisplayFormatType::Verbose => { - let partition_sizes: Vec<_> = - self.partitions.iter().map(|b| b.len()).collect(); - - let output_ordering = self - .sort_information - .first() - .map(|output_ordering| { - format!(", output_ordering={}", output_ordering) - }) - .unwrap_or_default(); - - let eq_properties = self.eq_properties(); - let constraints = eq_properties.constraints(); - let constraints = if constraints.is_empty() { - String::new() - } else { - format!(", {}", constraints) - }; - - let limit = self - .fetch - .map_or(String::new(), |limit| format!(", fetch={}", limit)); - if self.show_sizes { - write!( + let partition_sizes: Vec<_> = + self.partitions.iter().map(|b| b.len()).collect(); + + let output_ordering = self + .sort_information + .first() + .map(|output_ordering| { + format!(", output_ordering={}", output_ordering) + }) + .unwrap_or_default(); + + let eq_properties = self.eq_properties(); + let constraints = eq_properties.constraints(); + let constraints = if constraints.is_empty() { + String::new() + } else { + format!(", {}", constraints) + }; + + let limit = self + .fetch + .map_or(String::new(), |limit| format!(", fetch={}", limit)); + if self.show_sizes { + write!( f, "partitions={}, partition_sizes={partition_sizes:?}{limit}{output_ordering}{constraints}", partition_sizes.len(), ) - } else { - write!( - f, - "partitions={}{limit}{output_ordering}{constraints}", - partition_sizes.len(), - ) - } - } + } else { + write!( + f, + "partitions={}{limit}{output_ordering}{constraints}", + partition_sizes.len(), + ) + } + } DisplayFormatType::TreeRender => todo!(), } } diff --git a/datafusion/physical-plan/src/memory.rs b/datafusion/physical-plan/src/memory.rs index 86bbd12979fb..6eb380cbd4c7 100644 --- a/datafusion/physical-plan/src/memory.rs +++ b/datafusion/physical-plan/src/memory.rs @@ -192,7 +192,7 @@ impl DisplayAs for LazyMemoryExec { .join(", ") ) } - _ => todo!() + _ => todo!(), } } } From d2ec19343b31ea7c58985d6cd360dae5e0def36b Mon Sep 17 00:00:00 2001 From: irenjj Date: Sun, 23 Feb 2025 21:03:35 +0800 Subject: [PATCH 06/17] add License --- .../core/src/datasource/file_format/arrow.rs | 7 ++-- .../core/src/datasource/file_format/csv.rs | 7 ++-- .../core/src/datasource/file_format/json.rs | 7 ++-- .../src/datasource/file_format/parquet.rs | 7 ++-- datafusion/core/src/datasource/memory.rs | 7 ++-- .../core/src/datasource/physical_plan/mod.rs | 10 +---- .../physical_plan/parquet/source.rs | 7 ++-- datafusion/core/src/physical_planner.rs | 7 ++-- .../core/tests/custom_sources_cases/mod.rs | 7 ++-- .../provider_filter_pushdown.rs | 7 ++-- .../tests/custom_sources_cases/statistics.rs | 7 ++-- .../physical_optimizer/join_selection.rs | 14 +++---- .../tests/user_defined/user_defined_plan.rs | 7 ++-- datafusion/datasource/src/memory.rs | 5 ++- .../physical-plan/src/aggregates/mod.rs | 14 +++---- datafusion/physical-plan/src/analyze.rs | 7 ++-- .../physical-plan/src/coalesce_batches.rs | 7 ++-- .../physical-plan/src/coalesce_partitions.rs | 7 ++-- datafusion/physical-plan/src/display.rs | 10 +++-- datafusion/physical-plan/src/empty.rs | 7 ++-- datafusion/physical-plan/src/explain.rs | 7 ++-- datafusion/physical-plan/src/filter.rs | 7 ++-- datafusion/physical-plan/src/insert.rs | 7 ++-- .../physical-plan/src/joins/cross_join.rs | 7 ++-- .../physical-plan/src/joins/hash_join.rs | 7 ++-- .../src/joins/nested_loop_join.rs | 5 ++- .../src/joins/sort_merge_join.rs | 7 ++-- .../src/joins/symmetric_hash_join.rs | 7 ++-- datafusion/physical-plan/src/limit.rs | 14 +++---- .../physical-plan/src/placeholder_row.rs | 7 ++-- datafusion/physical-plan/src/projection.rs | 7 ++-- .../physical-plan/src/recursive_query.rs | 7 ++-- datafusion/physical-plan/src/render_tree.rs | 17 ++++++++ .../physical-plan/src/repartition/mod.rs | 7 ++-- .../physical-plan/src/sorts/partial_sort.rs | 7 ++-- datafusion/physical-plan/src/sorts/sort.rs | 16 +++---- .../src/sorts/sort_preserving_merge.rs | 16 +++---- datafusion/physical-plan/src/streaming.rs | 7 ++-- datafusion/physical-plan/src/test/exec.rs | 42 ++++++++----------- datafusion/physical-plan/src/union.rs | 14 +++---- datafusion/physical-plan/src/unnest.rs | 7 ++-- datafusion/physical-plan/src/values.rs | 7 ++-- .../src/windows/bounded_window_agg_exec.rs | 7 ++-- .../src/windows/window_agg_exec.rs | 7 ++-- datafusion/physical-plan/src/work_table.rs | 7 ++-- 45 files changed, 184 insertions(+), 224 deletions(-) diff --git a/datafusion/core/src/datasource/file_format/arrow.rs b/datafusion/core/src/datasource/file_format/arrow.rs index 0257a4e5576e..8a4d9ac3b992 100644 --- a/datafusion/core/src/datasource/file_format/arrow.rs +++ b/datafusion/core/src/datasource/file_format/arrow.rs @@ -296,14 +296,13 @@ impl Debug for ArrowFileSink { impl DisplayAs for ArrowFileSink { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "ArrowFileSink(file_groups=",)?; FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/src/datasource/file_format/csv.rs b/datafusion/core/src/datasource/file_format/csv.rs index f45422f05d55..d3bb4a290465 100644 --- a/datafusion/core/src/datasource/file_format/csv.rs +++ b/datafusion/core/src/datasource/file_format/csv.rs @@ -667,14 +667,13 @@ impl Debug for CsvSink { impl DisplayAs for CsvSink { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "CsvSink(file_groups=",)?; FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/src/datasource/file_format/json.rs b/datafusion/core/src/datasource/file_format/json.rs index 047e6a48fa34..347299522df5 100644 --- a/datafusion/core/src/datasource/file_format/json.rs +++ b/datafusion/core/src/datasource/file_format/json.rs @@ -322,14 +322,13 @@ impl Debug for JsonSink { impl DisplayAs for JsonSink { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "JsonSink(file_groups=",)?; FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/src/datasource/file_format/parquet.rs b/datafusion/core/src/datasource/file_format/parquet.rs index 22fd93095be5..e28553b7dc59 100644 --- a/datafusion/core/src/datasource/file_format/parquet.rs +++ b/datafusion/core/src/datasource/file_format/parquet.rs @@ -696,14 +696,13 @@ impl Debug for ParquetSink { impl DisplayAs for ParquetSink { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "ParquetSink(file_groups=",)?; FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/src/datasource/memory.rs b/datafusion/core/src/datasource/memory.rs index cbe73a11dc70..3b3039b1b60a 100644 --- a/datafusion/core/src/datasource/memory.rs +++ b/datafusion/core/src/datasource/memory.rs @@ -311,13 +311,12 @@ impl Debug for MemSink { impl DisplayAs for MemSink { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let partition_count = self.batches.len(); write!(f, "MemoryTable (partitions={partition_count})") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/src/datasource/physical_plan/mod.rs b/datafusion/core/src/datasource/physical_plan/mod.rs index aa39b06e5c0d..f6d6ad2f7ee4 100644 --- a/datafusion/core/src/datasource/physical_plan/mod.rs +++ b/datafusion/core/src/datasource/physical_plan/mod.rs @@ -125,7 +125,7 @@ impl DisplayAs for FileGroupsDisplay<'_> { let groups = if n_groups == 1 { "group" } else { "groups" }; write!(f, "{{{n_groups} {groups}: [")?; match t { - DisplayFormatType::Default => { + DisplayFormatType::Default | DisplayFormatType::TreeRender => { // To avoid showing too many partitions let max_groups = 5; fmt_up_to_n_elements(self.0, max_groups, f, |group, f| { @@ -137,9 +137,6 @@ impl DisplayAs for FileGroupsDisplay<'_> { FileGroupDisplay(group).fmt_as(t, f) })? } - DisplayFormatType::TreeRender => { - write!(f, "")?; // TODO(renjj): add display info - } } write!(f, "]}}") } @@ -158,7 +155,7 @@ impl DisplayAs for FileGroupDisplay<'_> { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> FmtResult { write!(f, "[")?; match t { - DisplayFormatType::Default => { + DisplayFormatType::Default | DisplayFormatType::TreeRender => { // To avoid showing too many files let max_files = 5; fmt_up_to_n_elements(self.0, max_files, f, |pf, f| { @@ -178,9 +175,6 @@ impl DisplayAs for FileGroupDisplay<'_> { Ok(()) })? } - DisplayFormatType::TreeRender => { - write!(f, "")?; // TODO(renjj): add display info - } } write!(f, "]") } diff --git a/datafusion/core/src/datasource/physical_plan/parquet/source.rs b/datafusion/core/src/datasource/physical_plan/parquet/source.rs index aa1a4bf1a324..2a94d3830161 100644 --- a/datafusion/core/src/datasource/physical_plan/parquet/source.rs +++ b/datafusion/core/src/datasource/physical_plan/parquet/source.rs @@ -559,7 +559,9 @@ impl FileSource for ParquetSource { fn fmt_extra(&self, t: DisplayFormatType, f: &mut Formatter) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let predicate_string = self .predicate() .map(|p| format!(", predicate={p}")) @@ -584,9 +586,6 @@ impl FileSource for ParquetSource { write!(f, "{}{}", predicate_string, pruning_predicate_string) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/src/physical_planner.rs b/datafusion/core/src/physical_planner.rs index e2fc663359d0..b812b7dc0a13 100644 --- a/datafusion/core/src/physical_planner.rs +++ b/datafusion/core/src/physical_planner.rs @@ -2736,12 +2736,11 @@ mod tests { impl DisplayAs for NoOpExecutionPlan { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "NoOpExecutionPlan") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/tests/custom_sources_cases/mod.rs b/datafusion/core/tests/custom_sources_cases/mod.rs index de2bd71de9df..e32f04d39d7b 100644 --- a/datafusion/core/tests/custom_sources_cases/mod.rs +++ b/datafusion/core/tests/custom_sources_cases/mod.rs @@ -135,12 +135,11 @@ impl DisplayAs for CustomExecutionPlan { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "CustomExecutionPlan: projection={:#?}", self.projection) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs b/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs index 4cda92216b49..60b2378d4f1f 100644 --- a/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs +++ b/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs @@ -89,12 +89,11 @@ impl DisplayAs for CustomPlan { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "CustomPlan: batch_size={}", self.batches.len(),) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/tests/custom_sources_cases/statistics.rs b/datafusion/core/tests/custom_sources_cases/statistics.rs index 2d258b358552..c545fde9501d 100644 --- a/datafusion/core/tests/custom_sources_cases/statistics.rs +++ b/datafusion/core/tests/custom_sources_cases/statistics.rs @@ -133,7 +133,9 @@ impl DisplayAs for StatisticsValidation { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!( f, "StatisticsValidation: col_count={}, row_count={:?}", @@ -141,9 +143,6 @@ impl DisplayAs for StatisticsValidation { self.stats.num_rows, ) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/tests/physical_optimizer/join_selection.rs b/datafusion/core/tests/physical_optimizer/join_selection.rs index e0a9a3b5316d..0cd0f47a0705 100644 --- a/datafusion/core/tests/physical_optimizer/join_selection.rs +++ b/datafusion/core/tests/physical_optimizer/join_selection.rs @@ -917,16 +917,15 @@ impl DisplayAs for UnboundedExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!( f, "UnboundedExec: unbounded={}", self.batch_produce.is_none(), ) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } @@ -1014,7 +1013,9 @@ impl DisplayAs for StatisticsExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!( f, "StatisticsExec: col_count={}, row_count={:?}", @@ -1022,9 +1023,6 @@ impl DisplayAs for StatisticsExec { self.stats.num_rows, ) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/core/tests/user_defined/user_defined_plan.rs b/datafusion/core/tests/user_defined/user_defined_plan.rs index 3e52afbbae26..9a9d8f349904 100644 --- a/datafusion/core/tests/user_defined/user_defined_plan.rs +++ b/datafusion/core/tests/user_defined/user_defined_plan.rs @@ -697,12 +697,11 @@ impl Debug for TopKExec { impl DisplayAs for TopKExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "TopKExec: k={}", self.k) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/datasource/src/memory.rs b/datafusion/datasource/src/memory.rs index 165489d5d1ca..f3b6bfdeb222 100644 --- a/datafusion/datasource/src/memory.rs +++ b/datafusion/datasource/src/memory.rs @@ -388,7 +388,9 @@ impl DataSource for MemorySourceConfig { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let partition_sizes: Vec<_> = self.partitions.iter().map(|b| b.len()).collect(); @@ -425,7 +427,6 @@ impl DataSource for MemorySourceConfig { ) } } - DisplayFormatType::TreeRender => todo!(), } } diff --git a/datafusion/physical-plan/src/aggregates/mod.rs b/datafusion/physical-plan/src/aggregates/mod.rs index 960888f42fc1..1226a39c489a 100644 --- a/datafusion/physical-plan/src/aggregates/mod.rs +++ b/datafusion/physical-plan/src/aggregates/mod.rs @@ -724,7 +724,9 @@ impl DisplayAs for AggregateExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "AggregateExec: mode={:?}", self.mode)?; let g: Vec = if self.group_by.is_single() { self.group_by @@ -789,9 +791,6 @@ impl DisplayAs for AggregateExec { write!(f, ", ordering_mode={:?}", self.input_order_mode)?; } } - DisplayFormatType::TreeRender => { - write!(f, "")?; // TODO(renjj): add display info - } } Ok(()) } @@ -1797,12 +1796,11 @@ mod tests { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "TestYieldingExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/analyze.rs b/datafusion/physical-plan/src/analyze.rs index 996cc0540f7e..f105e8cbd4fb 100644 --- a/datafusion/physical-plan/src/analyze.rs +++ b/datafusion/physical-plan/src/analyze.rs @@ -105,12 +105,11 @@ impl DisplayAs for AnalyzeExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "AnalyzeExec verbose={}", self.verbose) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/coalesce_batches.rs b/datafusion/physical-plan/src/coalesce_batches.rs index ed6abde6f5d8..28e1abdd14f8 100644 --- a/datafusion/physical-plan/src/coalesce_batches.rs +++ b/datafusion/physical-plan/src/coalesce_batches.rs @@ -110,7 +110,9 @@ impl DisplayAs for CoalesceBatchesExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!( f, "CoalesceBatchesExec: target_batch_size={}", @@ -122,9 +124,6 @@ impl DisplayAs for CoalesceBatchesExec { Ok(()) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/coalesce_partitions.rs b/datafusion/physical-plan/src/coalesce_partitions.rs index 98ff1d6cbdc8..14a42023efbd 100644 --- a/datafusion/physical-plan/src/coalesce_partitions.rs +++ b/datafusion/physical-plan/src/coalesce_partitions.rs @@ -86,15 +86,14 @@ impl DisplayAs for CoalescePartitionsExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => match self.fetch { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => match self.fetch { Some(fetch) => { write!(f, "CoalescePartitionsExec: fetch={fetch}") } None => write!(f, "CoalescePartitionsExec"), }, - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/display.rs b/datafusion/physical-plan/src/display.rs index ca24909d66c1..bbfd631db400 100644 --- a/datafusion/physical-plan/src/display.rs +++ b/datafusion/physical-plan/src/display.rs @@ -600,15 +600,17 @@ impl TreeRenderVisitor<'_, '_> { // Render the actual node. for render_y in 0..=extra_height { - for (x, node) in root.nodes.iter().enumerate().take(root.width) { + for (x, _) in root.nodes.iter().enumerate().take(root.width) { if x * Self::NODE_RENDER_WIDTH >= Self::MAXIMUM_RENDER_WIDTH { break; } - let has_adjacent_nodes = - (0..root.width - x).any(|i| root.has_node(x + i, y)); + let mut has_adjacent_nodes = false; + for i in 0..(root.width - x) { + has_adjacent_nodes = has_adjacent_nodes || root.has_node(x + i, y); + } - if let Some(node) = node { + if let Some(node) = root.get_node(x, y) { write!(self.f, "{}", Self::VERTICAL)?; // Rigure out what to render. diff --git a/datafusion/physical-plan/src/empty.rs b/datafusion/physical-plan/src/empty.rs index 97104dcf6ef4..3127c37e35ee 100644 --- a/datafusion/physical-plan/src/empty.rs +++ b/datafusion/physical-plan/src/empty.rs @@ -91,12 +91,11 @@ impl DisplayAs for EmptyExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "EmptyExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/explain.rs b/datafusion/physical-plan/src/explain.rs index 0e355632671a..87dc594fce06 100644 --- a/datafusion/physical-plan/src/explain.rs +++ b/datafusion/physical-plan/src/explain.rs @@ -91,12 +91,11 @@ impl DisplayAs for ExplainExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "ExplainExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/filter.rs b/datafusion/physical-plan/src/filter.rs index 0c37b68236d0..cca82108725b 100644 --- a/datafusion/physical-plan/src/filter.rs +++ b/datafusion/physical-plan/src/filter.rs @@ -308,7 +308,9 @@ impl DisplayAs for FilterExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let display_projections = if let Some(projection) = self.projection.as_ref() { @@ -329,9 +331,6 @@ impl DisplayAs for FilterExec { }; write!(f, "FilterExec: {}{}", self.predicate, display_projections) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/insert.rs b/datafusion/physical-plan/src/insert.rs index 1405f36b0c18..6bff24ea13d1 100644 --- a/datafusion/physical-plan/src/insert.rs +++ b/datafusion/physical-plan/src/insert.rs @@ -149,13 +149,12 @@ impl DataSinkExec { impl DisplayAs for DataSinkExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "DataSinkExec: sink=")?; self.sink.fmt_as(t, f) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/joins/cross_join.rs b/datafusion/physical-plan/src/joins/cross_join.rs index 2cc7002850c9..e0998862bdd8 100644 --- a/datafusion/physical-plan/src/joins/cross_join.rs +++ b/datafusion/physical-plan/src/joins/cross_join.rs @@ -237,12 +237,11 @@ impl DisplayAs for CrossJoinExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "CrossJoinExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/joins/hash_join.rs b/datafusion/physical-plan/src/joins/hash_join.rs index f8e4cbc75d5d..ed18be8f2b7d 100644 --- a/datafusion/physical-plan/src/joins/hash_join.rs +++ b/datafusion/physical-plan/src/joins/hash_join.rs @@ -633,7 +633,9 @@ impl HashJoinExec { impl DisplayAs for HashJoinExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let display_filter = self.filter.as_ref().map_or_else( || "".to_string(), |f| format!(", filter={}", f.expression()), @@ -668,9 +670,6 @@ impl DisplayAs for HashJoinExec { self.mode, self.join_type, on, display_filter, display_projections ) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/joins/nested_loop_join.rs b/datafusion/physical-plan/src/joins/nested_loop_join.rs index adfaff859916..e9d5aa81cd4d 100644 --- a/datafusion/physical-plan/src/joins/nested_loop_join.rs +++ b/datafusion/physical-plan/src/joins/nested_loop_join.rs @@ -395,7 +395,9 @@ impl NestedLoopJoinExec { impl DisplayAs for NestedLoopJoinExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let display_filter = self.filter.as_ref().map_or_else( || "".to_string(), |f| format!(", filter={}", f.expression()), @@ -424,7 +426,6 @@ impl DisplayAs for NestedLoopJoinExec { self.join_type, display_filter, display_projections ) } - DisplayFormatType::TreeRender => todo!(), // TODO(renjj): add display info } } } diff --git a/datafusion/physical-plan/src/joins/sort_merge_join.rs b/datafusion/physical-plan/src/joins/sort_merge_join.rs index 73f0086a65d0..aea96118a859 100644 --- a/datafusion/physical-plan/src/joins/sort_merge_join.rs +++ b/datafusion/physical-plan/src/joins/sort_merge_join.rs @@ -351,7 +351,9 @@ impl SortMergeJoinExec { impl DisplayAs for SortMergeJoinExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let on = self .on .iter() @@ -369,9 +371,6 @@ impl DisplayAs for SortMergeJoinExec { )) ) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/joins/symmetric_hash_join.rs b/datafusion/physical-plan/src/joins/symmetric_hash_join.rs index a0204455fcd1..44d0bbda212f 100644 --- a/datafusion/physical-plan/src/joins/symmetric_hash_join.rs +++ b/datafusion/physical-plan/src/joins/symmetric_hash_join.rs @@ -363,7 +363,9 @@ impl SymmetricHashJoinExec { impl DisplayAs for SymmetricHashJoinExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let display_filter = self.filter.as_ref().map_or_else( || "".to_string(), |f| format!(", filter={}", f.expression()), @@ -380,9 +382,6 @@ impl DisplayAs for SymmetricHashJoinExec { self.mode, self.join_type, on, display_filter ) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/limit.rs b/datafusion/physical-plan/src/limit.rs index 21d25dba6da1..6fd3e55ad1ea 100644 --- a/datafusion/physical-plan/src/limit.rs +++ b/datafusion/physical-plan/src/limit.rs @@ -100,7 +100,9 @@ impl DisplayAs for GlobalLimitExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!( f, "GlobalLimitExec: skip={}, fetch={}", @@ -108,9 +110,6 @@ impl DisplayAs for GlobalLimitExec { self.fetch.map_or("None".to_string(), |x| x.to_string()) ) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } @@ -261,12 +260,11 @@ impl DisplayAs for LocalLimitExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "LocalLimitExec: fetch={}", self.fetch) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/placeholder_row.rs b/datafusion/physical-plan/src/placeholder_row.rs index 878fdc264983..0fd4bd59f7b2 100644 --- a/datafusion/physical-plan/src/placeholder_row.rs +++ b/datafusion/physical-plan/src/placeholder_row.rs @@ -109,12 +109,11 @@ impl DisplayAs for PlaceholderRowExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "PlaceholderRowExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/projection.rs b/datafusion/physical-plan/src/projection.rs index b7649ddb0a26..3950cb06fcb1 100644 --- a/datafusion/physical-plan/src/projection.rs +++ b/datafusion/physical-plan/src/projection.rs @@ -151,7 +151,9 @@ impl DisplayAs for ProjectionExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let expr: Vec = self .expr .iter() @@ -167,9 +169,6 @@ impl DisplayAs for ProjectionExec { write!(f, "ProjectionExec: expr=[{}]", expr.join(", ")) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/recursive_query.rs b/datafusion/physical-plan/src/recursive_query.rs index 67997ebd8db9..0ed6043372ec 100644 --- a/datafusion/physical-plan/src/recursive_query.rs +++ b/datafusion/physical-plan/src/recursive_query.rs @@ -216,16 +216,15 @@ impl DisplayAs for RecursiveQueryExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!( f, "RecursiveQueryExec: name={}, is_distinct={}", self.name, self.is_distinct ) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/render_tree.rs b/datafusion/physical-plan/src/render_tree.rs index 95e28232bea9..22a18f2f1d31 100644 --- a/datafusion/physical-plan/src/render_tree.rs +++ b/datafusion/physical-plan/src/render_tree.rs @@ -1,3 +1,20 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + use std::cmp; use std::collections::HashMap; use std::sync::Arc; diff --git a/datafusion/physical-plan/src/repartition/mod.rs b/datafusion/physical-plan/src/repartition/mod.rs index 2ee2148370cd..80f82201c45d 100644 --- a/datafusion/physical-plan/src/repartition/mod.rs +++ b/datafusion/physical-plan/src/repartition/mod.rs @@ -488,7 +488,9 @@ impl DisplayAs for RepartitionExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!( f, "{}: partitioning={}, input_partitions={}", @@ -506,9 +508,6 @@ impl DisplayAs for RepartitionExec { } Ok(()) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/sorts/partial_sort.rs b/datafusion/physical-plan/src/sorts/partial_sort.rs index 0fff27536fb1..32acc8ea5d21 100644 --- a/datafusion/physical-plan/src/sorts/partial_sort.rs +++ b/datafusion/physical-plan/src/sorts/partial_sort.rs @@ -217,7 +217,9 @@ impl DisplayAs for PartialSortExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let common_prefix_length = self.common_prefix_length; match self.fetch { Some(fetch) => { @@ -226,9 +228,6 @@ impl DisplayAs for PartialSortExec { None => write!(f, "PartialSortExec: expr=[{}], common_prefix_length=[{common_prefix_length}]", self.expr), } } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/sorts/sort.rs b/datafusion/physical-plan/src/sorts/sort.rs index 67737360e368..092ac14846bf 100644 --- a/datafusion/physical-plan/src/sorts/sort.rs +++ b/datafusion/physical-plan/src/sorts/sort.rs @@ -945,7 +945,9 @@ impl SortExec { impl DisplayAs for SortExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let preserve_partitioning = self.preserve_partitioning; match self.fetch { Some(fetch) => { @@ -954,9 +956,6 @@ impl DisplayAs for SortExec { None => write!(f, "SortExec: expr=[{}], preserve_partitioning=[{preserve_partitioning}]", self.expr), } } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } @@ -1173,12 +1172,9 @@ mod tests { impl DisplayAs for SortedUnboundedExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { - write!(f, "UnboundableExec",).unwrap() - } - DisplayFormatType::TreeRender => { - write!(f, "").unwrap() // TODO(renjj): add display info - } + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => write!(f, "UnboundableExec",).unwrap(), } Ok(()) } diff --git a/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs b/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs index 97f228cdad60..65d66d2f3d1c 100644 --- a/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs +++ b/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs @@ -177,7 +177,9 @@ impl DisplayAs for SortPreservingMergeExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "SortPreservingMergeExec: [{}]", self.expr)?; if let Some(fetch) = self.fetch { write!(f, ", fetch={fetch}")?; @@ -185,9 +187,6 @@ impl DisplayAs for SortPreservingMergeExec { Ok(()) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } @@ -1383,12 +1382,9 @@ mod tests { impl DisplayAs for CongestedExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { - write!(f, "CongestedExec",).unwrap() - } - DisplayFormatType::TreeRender => { - write!(f, "").unwrap() // TODO(renjj): add display info - } + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => write!(f, "CongestedExec",).unwrap(), } Ok(()) } diff --git a/datafusion/physical-plan/src/streaming.rs b/datafusion/physical-plan/src/streaming.rs index 19301ee706d9..b3a14eeaa523 100644 --- a/datafusion/physical-plan/src/streaming.rs +++ b/datafusion/physical-plan/src/streaming.rs @@ -184,7 +184,9 @@ impl DisplayAs for StreamingTableExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!( f, "StreamingTableExec: partition_sizes={:?}", @@ -208,9 +210,6 @@ impl DisplayAs for StreamingTableExec { Ok(()) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/test/exec.rs b/datafusion/physical-plan/src/test/exec.rs index 55d0a8dad26c..0c32e40df167 100644 --- a/datafusion/physical-plan/src/test/exec.rs +++ b/datafusion/physical-plan/src/test/exec.rs @@ -172,12 +172,11 @@ impl DisplayAs for MockExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "MockExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } @@ -337,12 +336,11 @@ impl DisplayAs for BarrierExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "BarrierExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } @@ -452,12 +450,11 @@ impl DisplayAs for ErrorExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "ErrorExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } @@ -536,7 +533,9 @@ impl DisplayAs for StatisticsExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!( f, "StatisticsExec: col_count={}, row_count={:?}", @@ -544,9 +543,6 @@ impl DisplayAs for StatisticsExec { self.stats.num_rows, ) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } @@ -639,12 +635,11 @@ impl DisplayAs for BlockingExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "BlockingExec",) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } @@ -784,12 +779,11 @@ impl DisplayAs for PanicExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "PanicExec",) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/union.rs b/datafusion/physical-plan/src/union.rs index b0d5a22415f1..723b3d7fa269 100644 --- a/datafusion/physical-plan/src/union.rs +++ b/datafusion/physical-plan/src/union.rs @@ -154,12 +154,11 @@ impl DisplayAs for UnionExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "UnionExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } @@ -387,12 +386,11 @@ impl DisplayAs for InterleaveExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "InterleaveExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/unnest.rs b/datafusion/physical-plan/src/unnest.rs index a624f801aa93..cf1641abbd9f 100644 --- a/datafusion/physical-plan/src/unnest.rs +++ b/datafusion/physical-plan/src/unnest.rs @@ -134,12 +134,11 @@ impl DisplayAs for UnnestExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "UnnestExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/values.rs b/datafusion/physical-plan/src/values.rs index b193d1289500..a4235aef95e2 100644 --- a/datafusion/physical-plan/src/values.rs +++ b/datafusion/physical-plan/src/values.rs @@ -159,12 +159,11 @@ impl DisplayAs for ValuesExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "ValuesExec") } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } diff --git a/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs b/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs index 2cd9a26507e2..7207b5417922 100644 --- a/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs +++ b/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs @@ -235,7 +235,9 @@ impl DisplayAs for BoundedWindowAggExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "BoundedWindowAggExec: ")?; let g: Vec = self .window_expr @@ -252,9 +254,6 @@ impl DisplayAs for BoundedWindowAggExec { let mode = &self.input_order_mode; write!(f, "wdw=[{}], mode=[{:?}]", g.join(", "), mode)?; } - DisplayFormatType::TreeRender => { - write!(f, "")?; // TODO(renjj): add display info - } } Ok(()) } diff --git a/datafusion/physical-plan/src/windows/window_agg_exec.rs b/datafusion/physical-plan/src/windows/window_agg_exec.rs index 69fef85c8c82..75ab78418570 100644 --- a/datafusion/physical-plan/src/windows/window_agg_exec.rs +++ b/datafusion/physical-plan/src/windows/window_agg_exec.rs @@ -165,7 +165,9 @@ impl DisplayAs for WindowAggExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "WindowAggExec: ")?; let g: Vec = self .window_expr @@ -181,9 +183,6 @@ impl DisplayAs for WindowAggExec { .collect(); write!(f, "wdw=[{}]", g.join(", "))?; } - DisplayFormatType::TreeRender => { - write!(f, "")?; // TODO(renjj): add display info - } } Ok(()) } diff --git a/datafusion/physical-plan/src/work_table.rs b/datafusion/physical-plan/src/work_table.rs index 861060eac5a9..9706862321e0 100644 --- a/datafusion/physical-plan/src/work_table.rs +++ b/datafusion/physical-plan/src/work_table.rs @@ -159,12 +159,11 @@ impl DisplayAs for WorkTableExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!(f, "WorkTableExec: name={}", self.name) } - DisplayFormatType::TreeRender => { - write!(f, "") // TODO(renjj): add display info - } } } } From e0b27effdf7d925446ca4f6de95966b3a19667f5 Mon Sep 17 00:00:00 2001 From: irenjj Date: Sun, 23 Feb 2025 21:22:19 +0800 Subject: [PATCH 07/17] fix test --- datafusion/physical-plan/src/test.rs | 12 +++++++----- .../sqllogictest/test_files/information_schema.slt | 2 ++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/datafusion/physical-plan/src/test.rs b/datafusion/physical-plan/src/test.rs index 7d0e3778452f..a1bdda2f10b0 100644 --- a/datafusion/physical-plan/src/test.rs +++ b/datafusion/physical-plan/src/test.rs @@ -80,7 +80,9 @@ impl DisplayAs for TestMemoryExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result { write!(f, "DataSourceExec: ")?; match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { let partition_sizes: Vec<_> = self.partitions.iter().map(|b| b.len()).collect(); @@ -105,10 +107,10 @@ impl DisplayAs for TestMemoryExec { .map_or(String::new(), |limit| format!(", fetch={}", limit)); if self.show_sizes { write!( - f, - "partitions={}, partition_sizes={partition_sizes:?}{limit}{output_ordering}{constraints}", - partition_sizes.len(), - ) + f, + "partitions={}, partition_sizes={partition_sizes:?}{limit}{output_ordering}{constraints}", + partition_sizes.len(), + ) } else { write!( f, diff --git a/datafusion/sqllogictest/test_files/information_schema.slt b/datafusion/sqllogictest/test_files/information_schema.slt index 5a1caad46732..ce281cb3cea2 100644 --- a/datafusion/sqllogictest/test_files/information_schema.slt +++ b/datafusion/sqllogictest/test_files/information_schema.slt @@ -231,6 +231,7 @@ datafusion.execution.split_file_groups_by_statistics false datafusion.execution.target_partitions 7 datafusion.execution.time_zone +00:00 datafusion.execution.use_row_number_estimates_to_optimize_partitioning false +datafusion.explain.format indent datafusion.explain.logical_plan_only false datafusion.explain.physical_plan_only false datafusion.explain.show_schema false @@ -326,6 +327,7 @@ datafusion.execution.split_file_groups_by_statistics false Attempt to eliminate datafusion.execution.target_partitions 7 Number of partitions for query execution. Increasing partitions can increase concurrency. Defaults to the number of CPU cores on the system datafusion.execution.time_zone +00:00 The default time zone Some functions, e.g. `EXTRACT(HOUR from SOME_TIME)`, shift the underlying datetime according to this time zone, and then extract the hour datafusion.execution.use_row_number_estimates_to_optimize_partitioning false Should DataFusion use row number estimates at the input to decide whether increasing parallelism is beneficial or not. By default, only exact row numbers (not estimates) are used for this decision. Setting this flag to `true` will likely produce better plans. if the source of statistics is accurate. We plan to make this the default in the future. +datafusion.explain.format indent Display format of explain. Default is "indent". When set to "pretty", it will print the plan in a tree-rendered format. datafusion.explain.logical_plan_only false When set to true, the explain statement will only print logical plans datafusion.explain.physical_plan_only false When set to true, the explain statement will only print physical plans datafusion.explain.show_schema false When set to true, the explain statement will print schema information From 1aec472ef340035e7f08e222a1e6656c0b6acc8e Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Tue, 25 Feb 2025 14:09:15 -0500 Subject: [PATCH 08/17] Update config docs --- docs/source/user-guide/configs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/user-guide/configs.md b/docs/source/user-guide/configs.md index 999735f4c059..578211a74564 100644 --- a/docs/source/user-guide/configs.md +++ b/docs/source/user-guide/configs.md @@ -121,6 +121,7 @@ Environment variables are read during `SessionConfig` initialisation so they mus | datafusion.explain.show_statistics | false | When set to true, the explain statement will print operator statistics for physical plans | | datafusion.explain.show_sizes | true | When set to true, the explain statement will print the partition sizes | | datafusion.explain.show_schema | false | When set to true, the explain statement will print schema information | +| datafusion.explain.format | indent | Display format of explain. Default is "indent". When set to "pretty", it will print the plan in a tree-rendered format. | | datafusion.sql_parser.parse_float_as_decimal | false | When set to true, SQL parser will parse float as decimal type | | datafusion.sql_parser.enable_ident_normalization | true | When set to true, SQL parser will normalize ident (convert ident to lowercase when not quoted) | | datafusion.sql_parser.enable_options_value_normalization | false | When set to true, SQL parser will normalize options value (convert value to lowercase). Note that this option is ignored and will be removed in the future. All case-insensitive values are normalized automatically. | From 48fd985949f5213444366c4f749751e0dec9e5ea Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Tue, 25 Feb 2025 14:26:49 -0500 Subject: [PATCH 09/17] Add .slt test --- .../sqllogictest/test_files/explain_tree.slt | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 datafusion/sqllogictest/test_files/explain_tree.slt diff --git a/datafusion/sqllogictest/test_files/explain_tree.slt b/datafusion/sqllogictest/test_files/explain_tree.slt new file mode 100644 index 000000000000..8ee4cb2b70a8 --- /dev/null +++ b/datafusion/sqllogictest/test_files/explain_tree.slt @@ -0,0 +1,186 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Tests for tree explain + + + +statement ok +set datafusion.explain.format = "pretty"; + +######## Setup Data Files ####### + +# table1: CSV +query I +COPY (VALUES (1, 'foo', 1, '2023-01-01'), (2, 'bar', 2, '2023-01-02'), (3, 'baz', 3, '2023-01-03')) +TO 'test_files/scratch/explain_tree/table1.csv'; +---- +3 + +statement ok +CREATE EXTERNAL TABLE table1 ( + int_col INT, + string_col TEXT, + bigint_col BIGINT, + date_col DATE +) +STORED AS CSV +LOCATION 'test_files/scratch/explain_tree/table1.csv'; + +# table2: Parquet +query I +COPY (SELECT * from table1) +TO 'test_files/scratch/explain_tree/table2.parquet' +---- +3 + +statement ok +CREATE EXTERNAL TABLE table2 +STORED AS PARQUET +LOCATION 'test_files/scratch/explain_tree/table2.parquet'; + + +######## Begin Queries ######## + +# Filter +query TT +explain SELECT int_col FROM table1 WHERE string_col != 'foo'; +---- +logical_plan +01)Projection: table1.int_col +02)--Filter: table1.string_col != Utf8("foo") +03)----TableScan: table1 projection=[int_col, string_col], partial_filters=[table1.string_col != Utf8("foo")] +physical_plan +01)┌───────────────────────────┐ +02)│ CoalesceBatchesExec │ +03)└─────────────┬─────────────┘ +04)┌─────────────┴─────────────┐ +05)│ FilterExec │ +06)└─────────────┬─────────────┘ +07)┌─────────────┴─────────────┐ +08)│ RepartitionExec │ +09)└─────────────┬─────────────┘ +10)┌─────────────┴─────────────┐ +11)│ DataSourceExec │ +12)└───────────────────────────┘ + +# Aggregate +query TT +explain SELECT string_col, SUM(bigint_col) FROM table1 GROUP BY string_col; +---- +logical_plan +01)Aggregate: groupBy=[[table1.string_col]], aggr=[[sum(table1.bigint_col)]] +02)--TableScan: table1 projection=[string_col, bigint_col] +physical_plan +01)┌───────────────────────────┐ +02)│ AggregateExec │ +03)└─────────────┬─────────────┘ +04)┌─────────────┴─────────────┐ +05)│ CoalesceBatchesExec │ +06)└─────────────┬─────────────┘ +07)┌─────────────┴─────────────┐ +08)│ RepartitionExec │ +09)└─────────────┬─────────────┘ +10)┌─────────────┴─────────────┐ +11)│ AggregateExec │ +12)└─────────────┬─────────────┘ +13)┌─────────────┴─────────────┐ +14)│ RepartitionExec │ +15)└─────────────┬─────────────┘ +16)┌─────────────┴─────────────┐ +17)│ DataSourceExec │ +18)└───────────────────────────┘ + +# 2 Joins +query TT +explain SELECT table1.string_col, table2.date_col FROM table1 JOIN table2 ON table1.int_col = table2.int_col; +---- +logical_plan +01)Projection: table1.string_col, table2.date_col +02)--Inner Join: table1.int_col = table2.int_col +03)----TableScan: table1 projection=[int_col, string_col] +04)----TableScan: table2 projection=[int_col, date_col] +physical_plan +01)┌───────────────────────────┐ +02)│ CoalesceBatchesExec │ +03)└─────────────┬─────────────┘ +04)┌─────────────┴─────────────┐ +05)│ HashJoinExec ├──────────────┐ +06)└─────────────┬─────────────┘ │ +07)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +08)│ CoalesceBatchesExec ││ CoalesceBatchesExec │ +09)└─────────────┬─────────────┘└─────────────┬─────────────┘ +10)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +11)│ RepartitionExec ││ RepartitionExec │ +12)└─────────────┬─────────────┘└─────────────┬─────────────┘ +13)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +14)│ RepartitionExec ││ RepartitionExec │ +15)└─────────────┬─────────────┘└─────────────┬─────────────┘ +16)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +17)│ DataSourceExec ││ DataSourceExec │ +18)└───────────────────────────┘└───────────────────────────┘ + +# 3 Joins +query TT +explain SELECT + table1.string_col, + table2.date_col, + t1.date_col +FROM + table1 JOIN table2 ON table1.int_col = table2.int_col + JOIN table1 AS t1 ON table2.int_col = t1.int_col; +---- +logical_plan +01)Projection: table1.string_col, table2.date_col, t1.date_col +02)--Inner Join: table2.int_col = t1.int_col +03)----Projection: table1.string_col, table2.int_col, table2.date_col +04)------Inner Join: table1.int_col = table2.int_col +05)--------TableScan: table1 projection=[int_col, string_col] +06)--------TableScan: table2 projection=[int_col, date_col] +07)----SubqueryAlias: t1 +08)------TableScan: table1 projection=[int_col, date_col] +physical_plan +01)┌───────────────────────────┐ +02)│ CoalesceBatchesExec │ +03)└─────────────┬─────────────┘ +04)┌─────────────┴─────────────┐ +05)│ HashJoinExec ├───────────────────────────────────────────┐ +06)└─────────────┬─────────────┘ │ +07)┌─────────────┴─────────────┐ ┌─────────────┴─────────────┐ +08)│ CoalesceBatchesExec │ │ CoalesceBatchesExec │ +09)└─────────────┬─────────────┘ └─────────────┬─────────────┘ +10)┌─────────────┴─────────────┐ ┌─────────────┴─────────────┐ +11)│ HashJoinExec ├──────────────┐ │ RepartitionExec │ +12)└─────────────┬─────────────┘ │ └─────────────┬─────────────┘ +13)┌─────────────┴─────────────┐┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +14)│ CoalesceBatchesExec ││ CoalesceBatchesExec ││ RepartitionExec │ +15)└─────────────┬─────────────┘└─────────────┬─────────────┘└─────────────┬─────────────┘ +16)┌─────────────┴─────────────┐┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +17)│ RepartitionExec ││ RepartitionExec ││ DataSourceExec │ +18)└─────────────┬─────────────┘└─────────────┬─────────────┘└───────────────────────────┘ +19)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +20)│ RepartitionExec ││ RepartitionExec │ +21)└─────────────┬─────────────┘└─────────────┬─────────────┘ +22)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +23)│ DataSourceExec ││ DataSourceExec │ +24)└───────────────────────────┘└───────────────────────────┘ + + + +# cleanup +statement ok +drop table table1; From 5f2e4df9cc443132b311fbe842ad3ba9bb6e643a Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Tue, 25 Feb 2025 14:30:01 -0500 Subject: [PATCH 10/17] Add test for MemoryExec --- .../sqllogictest/test_files/explain_tree.slt | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/datafusion/sqllogictest/test_files/explain_tree.slt b/datafusion/sqllogictest/test_files/explain_tree.slt index 8ee4cb2b70a8..5dc612e773bb 100644 --- a/datafusion/sqllogictest/test_files/explain_tree.slt +++ b/datafusion/sqllogictest/test_files/explain_tree.slt @@ -54,6 +54,10 @@ STORED AS PARQUET LOCATION 'test_files/scratch/explain_tree/table2.parquet'; +# table3: Memoru +statement ok +CREATE TABLE table3 as select * from table1; + ######## Begin Queries ######## # Filter @@ -139,20 +143,19 @@ query TT explain SELECT table1.string_col, table2.date_col, - t1.date_col + table3.date_col FROM table1 JOIN table2 ON table1.int_col = table2.int_col - JOIN table1 AS t1 ON table2.int_col = t1.int_col; + JOIN table3 ON table2.int_col = table3.int_col; ---- logical_plan -01)Projection: table1.string_col, table2.date_col, t1.date_col -02)--Inner Join: table2.int_col = t1.int_col +01)Projection: table1.string_col, table2.date_col, table3.date_col +02)--Inner Join: table2.int_col = table3.int_col 03)----Projection: table1.string_col, table2.int_col, table2.date_col 04)------Inner Join: table1.int_col = table2.int_col 05)--------TableScan: table1 projection=[int_col, string_col] 06)--------TableScan: table2 projection=[int_col, date_col] -07)----SubqueryAlias: t1 -08)------TableScan: table1 projection=[int_col, date_col] +07)----TableScan: table3 projection=[int_col, date_col] physical_plan 01)┌───────────────────────────┐ 02)│ CoalesceBatchesExec │ @@ -167,20 +170,29 @@ physical_plan 11)│ HashJoinExec ├──────────────┐ │ RepartitionExec │ 12)└─────────────┬─────────────┘ │ └─────────────┬─────────────┘ 13)┌─────────────┴─────────────┐┌─────────────┴─────────────┐┌─────────────┴─────────────┐ -14)│ CoalesceBatchesExec ││ CoalesceBatchesExec ││ RepartitionExec │ -15)└─────────────┬─────────────┘└─────────────┬─────────────┘└─────────────┬─────────────┘ -16)┌─────────────┴─────────────┐┌─────────────┴─────────────┐┌─────────────┴─────────────┐ -17)│ RepartitionExec ││ RepartitionExec ││ DataSourceExec │ +14)│ CoalesceBatchesExec ││ CoalesceBatchesExec ││ DataSourceExec │ +15)│ ││ ││ -------------------- │ +16)│ ││ ││ partition_sizes: [1] │ +17)│ ││ ││ partitions: 1 │ 18)└─────────────┬─────────────┘└─────────────┬─────────────┘└───────────────────────────┘ 19)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ 20)│ RepartitionExec ││ RepartitionExec │ 21)└─────────────┬─────────────┘└─────────────┬─────────────┘ 22)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ -23)│ DataSourceExec ││ DataSourceExec │ -24)└───────────────────────────┘└───────────────────────────┘ +23)│ RepartitionExec ││ RepartitionExec │ +24)└─────────────┬─────────────┘└─────────────┬─────────────┘ +25)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +26)│ DataSourceExec ││ DataSourceExec │ +27)└───────────────────────────┘└───────────────────────────┘ # cleanup statement ok drop table table1; + +statement ok +drop table table2; + +statement ok +drop table table3; From b3518782069b1504298fa34f40f81e112e4c3cbc Mon Sep 17 00:00:00 2001 From: Andrew Lamb Date: Tue, 25 Feb 2025 14:58:39 -0500 Subject: [PATCH 11/17] Use BTreeMap to ensure consistent order --- datafusion/physical-plan/src/display.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/datafusion/physical-plan/src/display.rs b/datafusion/physical-plan/src/display.rs index bbfd631db400..6db143d58575 100644 --- a/datafusion/physical-plan/src/display.rs +++ b/datafusion/physical-plan/src/display.rs @@ -18,7 +18,7 @@ //! Implementation of physical plan display. See //! [`crate::displayable`] for examples of how to format -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::fmt::Formatter; use std::{fmt, str::FromStr}; @@ -777,7 +777,10 @@ impl TreeRenderVisitor<'_, '_> { let mut requires_padding = false; let mut was_inlined = false; - for (key, value) in extra_info { + + // use BTreeMap for repeatable key order + let sorted_extra_info: BTreeMap<_, _> = extra_info.iter().collect(); + for (key, value) in sorted_extra_info { let mut str = Self::remove_padding(value); if str.is_empty() { continue; From aba177d11c09ba6aa95f83c2c9596dfcf14bbca3 Mon Sep 17 00:00:00 2001 From: irenjj Date: Wed, 26 Feb 2025 20:10:38 +0800 Subject: [PATCH 12/17] credit & rename pretty to tree --- datafusion/common/src/config.rs | 2 +- datafusion/physical-plan/src/display.rs | 6 ++++-- datafusion/physical-plan/src/render_tree.rs | 3 +++ datafusion/sqllogictest/test_files/explain_tree.slt | 2 +- datafusion/sqllogictest/test_files/information_schema.slt | 2 +- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/datafusion/common/src/config.rs b/datafusion/common/src/config.rs index 0d78a936d8b5..90a56872d528 100644 --- a/datafusion/common/src/config.rs +++ b/datafusion/common/src/config.rs @@ -706,7 +706,7 @@ config_namespace! { pub show_schema: bool, default = false /// Display format of explain. Default is "indent". - /// When set to "pretty", it will print the plan in a tree-rendered format. + /// When set to "tree", it will print the plan in a tree-rendered format. pub format: String, default = "indent".to_string() } } diff --git a/datafusion/physical-plan/src/display.rs b/datafusion/physical-plan/src/display.rs index 6db143d58575..1edc4f2920fe 100644 --- a/datafusion/physical-plan/src/display.rs +++ b/datafusion/physical-plan/src/display.rs @@ -50,7 +50,7 @@ impl FromStr for DisplayFormatType { fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { "indent" => Ok(Self::Default), - "pretty" => Ok(Self::TreeRender), + "tree" => Ok(Self::TreeRender), _ => Err(DataFusionError::Configuration(format!( "Invalid explain format: {}", s @@ -492,13 +492,14 @@ impl ExecutionPlanVisitor for GraphvizVisitor<'_, '_> { } } +// This code is based on the DuckDB’s implementation: +// https://github.com/duckdb/duckdb/blob/main/src/include/duckdb/common/tree_renderer/text_tree_renderer.hpp struct TreeRenderVisitor<'a, 'b> { /// Write to this formatter f: &'a mut Formatter<'b>, } impl TreeRenderVisitor<'_, '_> { - // TODO: Make these variables configurable. const LTCORNER: &'static str = "┌"; const RTCORNER: &'static str = "┐"; const LDCORNER: &'static str = "└"; @@ -511,6 +512,7 @@ impl TreeRenderVisitor<'_, '_> { const VERTICAL: &'static str = "│"; const HORIZONTAL: &'static str = "─"; + // TODO: Make these variables configurable. const MAXIMUM_RENDER_WIDTH: usize = 240; const NODE_RENDER_WIDTH: usize = 29; const MAX_EXTRA_LINES: usize = 30; diff --git a/datafusion/physical-plan/src/render_tree.rs b/datafusion/physical-plan/src/render_tree.rs index 22a18f2f1d31..8aeac4ebfeb4 100644 --- a/datafusion/physical-plan/src/render_tree.rs +++ b/datafusion/physical-plan/src/render_tree.rs @@ -15,6 +15,9 @@ // specific language governing permissions and limitations // under the License. +// This code is based on the DuckDB’s implementation: +// https://github.com/duckdb/duckdb/blob/main/src/include/duckdb/common/render_tree.hpp + use std::cmp; use std::collections::HashMap; use std::sync::Arc; diff --git a/datafusion/sqllogictest/test_files/explain_tree.slt b/datafusion/sqllogictest/test_files/explain_tree.slt index 5dc612e773bb..5f0785782008 100644 --- a/datafusion/sqllogictest/test_files/explain_tree.slt +++ b/datafusion/sqllogictest/test_files/explain_tree.slt @@ -20,7 +20,7 @@ statement ok -set datafusion.explain.format = "pretty"; +set datafusion.explain.format = "tree"; ######## Setup Data Files ####### diff --git a/datafusion/sqllogictest/test_files/information_schema.slt b/datafusion/sqllogictest/test_files/information_schema.slt index ce281cb3cea2..751461522c5c 100644 --- a/datafusion/sqllogictest/test_files/information_schema.slt +++ b/datafusion/sqllogictest/test_files/information_schema.slt @@ -327,7 +327,7 @@ datafusion.execution.split_file_groups_by_statistics false Attempt to eliminate datafusion.execution.target_partitions 7 Number of partitions for query execution. Increasing partitions can increase concurrency. Defaults to the number of CPU cores on the system datafusion.execution.time_zone +00:00 The default time zone Some functions, e.g. `EXTRACT(HOUR from SOME_TIME)`, shift the underlying datetime according to this time zone, and then extract the hour datafusion.execution.use_row_number_estimates_to_optimize_partitioning false Should DataFusion use row number estimates at the input to decide whether increasing parallelism is beneficial or not. By default, only exact row numbers (not estimates) are used for this decision. Setting this flag to `true` will likely produce better plans. if the source of statistics is accurate. We plan to make this the default in the future. -datafusion.explain.format indent Display format of explain. Default is "indent". When set to "pretty", it will print the plan in a tree-rendered format. +datafusion.explain.format indent Display format of explain. Default is "indent". When set to "tree", it will print the plan in a tree-rendered format. datafusion.explain.logical_plan_only false When set to true, the explain statement will only print logical plans datafusion.explain.physical_plan_only false When set to true, the explain statement will only print physical plans datafusion.explain.show_schema false When set to true, the explain statement will print schema information From d58873c6a7e080878fb98315279ebd6504311980 Mon Sep 17 00:00:00 2001 From: irenjj Date: Wed, 26 Feb 2025 20:26:09 +0800 Subject: [PATCH 13/17] update config md --- docs/source/user-guide/configs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/user-guide/configs.md b/docs/source/user-guide/configs.md index 578211a74564..4b790f8d0442 100644 --- a/docs/source/user-guide/configs.md +++ b/docs/source/user-guide/configs.md @@ -121,7 +121,7 @@ Environment variables are read during `SessionConfig` initialisation so they mus | datafusion.explain.show_statistics | false | When set to true, the explain statement will print operator statistics for physical plans | | datafusion.explain.show_sizes | true | When set to true, the explain statement will print the partition sizes | | datafusion.explain.show_schema | false | When set to true, the explain statement will print schema information | -| datafusion.explain.format | indent | Display format of explain. Default is "indent". When set to "pretty", it will print the plan in a tree-rendered format. | +| datafusion.explain.format | indent | Display format of explain. Default is "indent". When set to "tree", it will print the plan in a tree-rendered format. | | datafusion.sql_parser.parse_float_as_decimal | false | When set to true, SQL parser will parse float as decimal type | | datafusion.sql_parser.enable_ident_normalization | true | When set to true, SQL parser will normalize ident (convert ident to lowercase when not quoted) | | datafusion.sql_parser.enable_options_value_normalization | false | When set to true, SQL parser will normalize options value (convert value to lowercase). Note that this option is ignored and will be removed in the future. All case-insensitive values are normalized automatically. | From daefaf2e93c3a2f2787fbf6ef257729294d3998a Mon Sep 17 00:00:00 2001 From: irenjj Date: Wed, 26 Feb 2025 22:28:30 +0800 Subject: [PATCH 14/17] remove collect_info interface --- datafusion/datasource/src/file_scan_config.rs | 5 -- datafusion/datasource/src/memory.rs | 54 ++++++++----------- datafusion/datasource/src/source.rs | 13 +++-- datafusion/physical-plan/src/display.rs | 6 +-- datafusion/physical-plan/src/memory.rs | 5 +- datafusion/physical-plan/src/render_tree.rs | 38 +++++++++++-- 6 files changed, 67 insertions(+), 54 deletions(-) diff --git a/datafusion/datasource/src/file_scan_config.rs b/datafusion/datasource/src/file_scan_config.rs index 41d4adb567ce..affea6e34afd 100644 --- a/datafusion/datasource/src/file_scan_config.rs +++ b/datafusion/datasource/src/file_scan_config.rs @@ -216,11 +216,6 @@ impl DataSource for FileScanConfig { self.fmt_file_source(t, f) } - fn collect_info(&self) -> HashMap { - // TODO: collect explain info - HashMap::new() - } - /// If supported by the underlying [`FileSource`], redistribute files across partitions according to their size. fn repartitioned( &self, diff --git a/datafusion/datasource/src/memory.rs b/datafusion/datasource/src/memory.rs index e3deac68850a..54cc4adf4afa 100644 --- a/datafusion/datasource/src/memory.rs +++ b/datafusion/datasource/src/memory.rs @@ -18,7 +18,6 @@ //! Execution plan for reading in-memory batches of data use std::any::Any; -use std::collections::HashMap; use std::fmt; use std::sync::Arc; @@ -388,9 +387,7 @@ impl DataSource for MemorySourceConfig { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let partition_sizes: Vec<_> = self.partitions.iter().map(|b| b.len()).collect(); @@ -427,6 +424,27 @@ impl DataSource for MemorySourceConfig { ) } } + DisplayFormatType::TreeRender => { + let partition_sizes: Vec<_> = + self.partitions.iter().map(|b| b.len()).collect(); + writeln!(f, "partition_sizes={:?}", partition_sizes)?; + + if let Some(output_ordering) = self.sort_information.first() { + writeln!(f, "output_ordering={}", output_ordering)?; + } + + let eq_properties = self.eq_properties(); + let constraints = eq_properties.constraints(); + if !constraints.is_empty() { + writeln!(f, "constraints={}", constraints)?; + } + + if let Some(limit) = self.fetch { + writeln!(f, "fetch={}", limit)?; + } + + write!(f, "partitions={}", partition_sizes.len()) + } } } @@ -481,34 +499,6 @@ impl DataSource for MemorySourceConfig { }) .transpose() } - - fn collect_info(&self) -> HashMap { - let mut result = HashMap::new(); - - let partition_sizes: Vec<_> = self.partitions.iter().map(|b| b.len()).collect(); - result.insert( - "partition_sizes".to_string(), - format!("{:?}", partition_sizes), - ); - - if let Some(output_ordering) = self.sort_information.first() { - result.insert("output_ordering".to_string(), output_ordering.to_string()); - } - - let eq_properties = self.eq_properties(); - let constraints = eq_properties.constraints(); - if !constraints.is_empty() { - result.insert("constraints".to_string(), constraints.to_string()); - } - - if let Some(limit) = self.fetch { - result.insert("fetch".to_string(), limit.to_string()); - } - - result.insert("partitions".to_string(), partition_sizes.len().to_string()); - - result - } } impl MemorySourceConfig { diff --git a/datafusion/datasource/src/source.rs b/datafusion/datasource/src/source.rs index 8b0043d9a9c1..1b2cdf8a3407 100644 --- a/datafusion/datasource/src/source.rs +++ b/datafusion/datasource/src/source.rs @@ -16,7 +16,6 @@ // under the License. use std::any::Any; -use std::collections::HashMap; use std::fmt; use std::fmt::{Debug, Formatter}; use std::sync::Arc; @@ -44,7 +43,6 @@ pub trait DataSource: Send + Sync { ) -> datafusion_common::Result; fn as_any(&self) -> &dyn Any; fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result; - fn collect_info(&self) -> HashMap; fn repartitioned( &self, _target_partitions: usize, @@ -83,13 +81,14 @@ pub struct DataSourceExec { impl DisplayAs for DataSourceExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result { - write!(f, "DataSourceExec: ")?; + match t { + DisplayFormatType::Default | DisplayFormatType::Verbose => { + write!(f, "DataSourceExec: ")? + } + DisplayFormatType::TreeRender => write!(f, "")?, + } self.source.fmt_as(t, f) } - - fn collect_info(&self) -> HashMap { - self.source.collect_info() - } } impl ExecutionPlan for DataSourceExec { diff --git a/datafusion/physical-plan/src/display.rs b/datafusion/physical-plan/src/display.rs index 1edc4f2920fe..9ca3924232a9 100644 --- a/datafusion/physical-plan/src/display.rs +++ b/datafusion/physical-plan/src/display.rs @@ -766,7 +766,7 @@ impl TreeRenderVisitor<'_, '_> { s.trim().to_string() } - fn split_up_extra_info( + pub fn split_up_extra_info( extra_info: &HashMap, result: &mut Vec, max_lines: usize, @@ -873,10 +873,6 @@ pub trait DisplayAs { /// /// Should not include a newline fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result; - - fn collect_info(&self) -> HashMap { - HashMap::new() - } } /// A new type wrapper to display `T` implementing`DisplayAs` using the `Default` mode diff --git a/datafusion/physical-plan/src/memory.rs b/datafusion/physical-plan/src/memory.rs index 6eb380cbd4c7..7078e764e901 100644 --- a/datafusion/physical-plan/src/memory.rs +++ b/datafusion/physical-plan/src/memory.rs @@ -180,7 +180,9 @@ impl fmt::Debug for LazyMemoryExec { impl DisplayAs for LazyMemoryExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default | DisplayFormatType::Verbose => { + DisplayFormatType::Default + | DisplayFormatType::Verbose + | DisplayFormatType::TreeRender => { write!( f, "LazyMemoryExec: partitions={}, batch_generators=[{}]", @@ -192,7 +194,6 @@ impl DisplayAs for LazyMemoryExec { .join(", ") ) } - _ => todo!(), } } } diff --git a/datafusion/physical-plan/src/render_tree.rs b/datafusion/physical-plan/src/render_tree.rs index 8aeac4ebfeb4..a1b5f2b46ba3 100644 --- a/datafusion/physical-plan/src/render_tree.rs +++ b/datafusion/physical-plan/src/render_tree.rs @@ -18,12 +18,14 @@ // This code is based on the DuckDB’s implementation: // https://github.com/duckdb/duckdb/blob/main/src/include/duckdb/common/render_tree.hpp -use std::cmp; use std::collections::HashMap; +use std::fmt::Formatter; use std::sync::Arc; +use std::{cmp, fmt}; -use crate::ExecutionPlan; +use crate::{DisplayFormatType, ExecutionPlan}; +// TODO: It's never used. #[allow(dead_code)] pub struct Coordinate { pub x: usize, @@ -36,9 +38,13 @@ impl Coordinate { } } +/// `RenderTreeNode` stores display info for `ExecutionPlan`. pub struct RenderTreeNode { + /// The name of physical `ExecutionPlan`. pub name: String, + /// Execution info collected from `ExecutionPlan`. pub extra_text: HashMap, + /// Current `ExecutionPlan`'s children position. pub child_positions: Vec, } @@ -63,6 +69,7 @@ pub struct RenderTree { } impl RenderTree { + /// Create a render tree based on the root of physical plan node. pub fn create_tree(plan: &dyn ExecutionPlan) -> Self { let (width, height) = get_tree_width_height(plan); @@ -132,13 +139,38 @@ fn get_tree_width_height(plan: &dyn ExecutionPlan) -> (usize, usize) { (width, height) } +fn fmt_display(plan: &dyn ExecutionPlan) -> impl fmt::Display + '_ { + struct Wrapper<'a> { + plan: &'a dyn ExecutionPlan, + } + + impl fmt::Display for Wrapper<'_> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.plan.fmt_as(DisplayFormatType::TreeRender, f)?; + Ok(()) + } + } + + Wrapper { plan } +} + fn create_tree_recursive( result: &mut RenderTree, plan: &dyn ExecutionPlan, x: usize, y: usize, ) -> usize { - let mut node = RenderTreeNode::new(plan.name().to_string(), plan.collect_info()); + let display_info = fmt_display(plan).to_string(); + let mut extra_info = HashMap::new(); + + // Parse the key-value pairs from the formatted string. + for line in display_info.lines() { + if let Some((key, value)) = line.split_once('=') { + extra_info.insert(key.to_string(), value.to_string()); + } + } + + let mut node = RenderTreeNode::new(plan.name().to_string(), extra_info); let children = plan.children(); From 3dde06fc3e0789e6801aff781ce6a8cb2a1cc371 Mon Sep 17 00:00:00 2001 From: irenjj Date: Wed, 26 Feb 2025 22:48:24 +0800 Subject: [PATCH 15/17] add comments --- datafusion/physical-plan/src/display.rs | 82 +++++++++++++++++---- datafusion/physical-plan/src/render_tree.rs | 40 +++++++++- 2 files changed, 104 insertions(+), 18 deletions(-) diff --git a/datafusion/physical-plan/src/display.rs b/datafusion/physical-plan/src/display.rs index 9ca3924232a9..f3c53c643993 100644 --- a/datafusion/physical-plan/src/display.rs +++ b/datafusion/physical-plan/src/display.rs @@ -492,31 +492,58 @@ impl ExecutionPlanVisitor for GraphvizVisitor<'_, '_> { } } -// This code is based on the DuckDB’s implementation: -// https://github.com/duckdb/duckdb/blob/main/src/include/duckdb/common/tree_renderer/text_tree_renderer.hpp +/// This module implements a tree-like art renderer for execution plans, +/// based on DuckDB's implementation: +/// https://github.com/duckdb/duckdb/blob/main/src/include/duckdb/common/tree_renderer/text_tree_renderer.hpp +/// +/// The rendered output looks like this: +/// ```text +/// ┌───────────────────────────┐ +/// │ CoalesceBatchesExec │ +/// └─────────────┬─────────────┘ +/// ┌─────────────┴─────────────┐ +/// │ HashJoinExec ├──────────────┐ +/// └─────────────┬─────────────┘ │ +/// ┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +/// │ DataSourceExec ││ DataSourceExec │ +/// └───────────────────────────┘└───────────────────────────┘ +/// ``` +/// +/// The renderer uses a three-layer approach for each node: +/// 1. Top layer: renders the top borders and connections +/// 2. Content layer: renders the node content and vertical connections +/// 3. Bottom layer: renders the bottom borders and connections +/// +/// Each node is rendered in a box of fixed width (NODE_RENDER_WIDTH). struct TreeRenderVisitor<'a, 'b> { /// Write to this formatter f: &'a mut Formatter<'b>, } impl TreeRenderVisitor<'_, '_> { - const LTCORNER: &'static str = "┌"; - const RTCORNER: &'static str = "┐"; - const LDCORNER: &'static str = "└"; - const RDCORNER: &'static str = "┘"; + // Unicode box-drawing characters for creating borders and connections. + const LTCORNER: &'static str = "┌"; // Left top corner + const RTCORNER: &'static str = "┐"; // Right top corner + const LDCORNER: &'static str = "└"; // Left bottom corner + const RDCORNER: &'static str = "┘"; // Right bottom corner - const TMIDDLE: &'static str = "┬"; - const LMIDDLE: &'static str = "├"; - const DMIDDLE: &'static str = "┴"; + const TMIDDLE: &'static str = "┬"; // Top T-junction (connects down) + const LMIDDLE: &'static str = "├"; // Left T-junction (connects right) + const DMIDDLE: &'static str = "┴"; // Bottom T-junction (connects up) - const VERTICAL: &'static str = "│"; - const HORIZONTAL: &'static str = "─"; + const VERTICAL: &'static str = "│"; // Vertical line + const HORIZONTAL: &'static str = "─"; // Horizontal line // TODO: Make these variables configurable. - const MAXIMUM_RENDER_WIDTH: usize = 240; - const NODE_RENDER_WIDTH: usize = 29; - const MAX_EXTRA_LINES: usize = 30; - + const MAXIMUM_RENDER_WIDTH: usize = 240; // Maximum total width of the rendered tree + const NODE_RENDER_WIDTH: usize = 29; // Width of each node's box + const MAX_EXTRA_LINES: usize = 30; // Maximum number of extra info lines per node + + /// Main entry point for rendering an execution plan as a tree. + /// The rendering process happens in three stages for each level of the tree: + /// 1. Render top borders and connections + /// 2. Render node content and vertical connections + /// 3. Render bottom borders and connections pub fn visit(&mut self, plan: &dyn ExecutionPlan) -> Result<(), fmt::Error> { let root = RenderTree::create_tree(plan); @@ -532,6 +559,11 @@ impl TreeRenderVisitor<'_, '_> { Ok(()) } + /// Renders the top layer of boxes at the given y-level of the tree. + /// This includes: + /// - Top corners (┌─┐) for nodes + /// - Horizontal connections between nodes + /// - Vertical connections to parent nodes fn render_top_layer( &mut self, root: &RenderTree, @@ -577,6 +609,12 @@ impl TreeRenderVisitor<'_, '_> { Ok(()) } + /// Renders the content layer of boxes at the given y-level of the tree. + /// This includes: + /// - Node names and extra information + /// - Vertical borders (│) for boxes + /// - Vertical connections between nodes + /// The content is center-aligned within each box. fn render_box_content( &mut self, root: &RenderTree, @@ -710,6 +748,11 @@ impl TreeRenderVisitor<'_, '_> { Ok(()) } + /// Renders the bottom layer of boxes at the given y-level of the tree. + /// This includes: + /// - Bottom corners (└─┘) for nodes + /// - Horizontal connections between nodes + /// - Vertical connections to child nodes fn render_bottom_layer( &mut self, root: &RenderTree, @@ -825,6 +868,10 @@ impl TreeRenderVisitor<'_, '_> { } } + /// Adjusts text to fit within the specified width by: + /// 1. Truncating with ellipsis if too long + /// 2. Center-aligning within the available space if shorter + /// This ensures consistent box widths in the rendered tree. fn adjust_text_for_rendering(source: &str, max_render_width: usize) -> String { let render_width = source.chars().count(); if render_width > max_render_width { @@ -843,6 +890,11 @@ impl TreeRenderVisitor<'_, '_> { } } + /// Determines if whitespace should be rendered at a given position. + /// This is important for: + /// 1. Maintaining proper spacing between sibling nodes + /// 2. Ensuring correct alignment of connections between parents and children + /// 3. Preserving the tree structure's visual clarity fn should_render_whitespace(root: &RenderTree, x: usize, y: usize) -> bool { let mut found_children = 0; diff --git a/datafusion/physical-plan/src/render_tree.rs b/datafusion/physical-plan/src/render_tree.rs index a1b5f2b46ba3..f970a51b1d6e 100644 --- a/datafusion/physical-plan/src/render_tree.rs +++ b/datafusion/physical-plan/src/render_tree.rs @@ -18,6 +18,9 @@ // This code is based on the DuckDB’s implementation: // https://github.com/duckdb/duckdb/blob/main/src/include/duckdb/common/render_tree.hpp +//! This module provides functionality for rendering an execution plan as a tree structure. +//! It helps in visualizing how different operations in a query are connected and organized. + use std::collections::HashMap; use std::fmt::Formatter; use std::sync::Arc; @@ -26,9 +29,13 @@ use std::{cmp, fmt}; use crate::{DisplayFormatType, ExecutionPlan}; // TODO: It's never used. +/// Represents a 2D coordinate in the rendered tree. +/// Used to track positions of nodes and their connections. #[allow(dead_code)] pub struct Coordinate { + /// Horizontal position in the tree pub x: usize, + /// Vertical position in the tree pub y: usize, } @@ -38,13 +45,14 @@ impl Coordinate { } } -/// `RenderTreeNode` stores display info for `ExecutionPlan`. +/// Represents a node in the render tree, containing information about an execution plan operator +/// and its relationships to other operators. pub struct RenderTreeNode { /// The name of physical `ExecutionPlan`. pub name: String, /// Execution info collected from `ExecutionPlan`. pub extra_text: HashMap, - /// Current `ExecutionPlan`'s children position. + /// Positions of child nodes in the rendered tree. pub child_positions: Vec, } @@ -62,14 +70,19 @@ impl RenderTreeNode { } } +/// Main structure for rendering an execution plan as a tree. +/// Manages a 2D grid of nodes and their layout information. pub struct RenderTree { + /// Storage for tree nodes in a flattened 2D grid pub nodes: Vec>>, + /// Total width of the rendered tree pub width: usize, + /// Total height of the rendered tree pub height: usize, } impl RenderTree { - /// Create a render tree based on the root of physical plan node. + /// Creates a new render tree from an execution plan. pub fn create_tree(plan: &dyn ExecutionPlan) -> Self { let (width, height) = get_tree_width_height(plan); @@ -118,9 +131,18 @@ impl RenderTree { } } +/// Calculates the required dimensions of the tree. +/// This ensures we allocate enough space for the entire tree structure. +/// +/// # Arguments +/// * `plan` - The execution plan to measure +/// +/// # Returns +/// * A tuple of (width, height) representing the dimensions needed for the tree fn get_tree_width_height(plan: &dyn ExecutionPlan) -> (usize, usize) { let children = plan.children(); + // Leaf nodes take up 1x1 space if children.is_empty() { return (1, 1); } @@ -154,6 +176,18 @@ fn fmt_display(plan: &dyn ExecutionPlan) -> impl fmt::Display + '_ { Wrapper { plan } } +/// Recursively builds the render tree structure. +/// Traverses the execution plan and creates corresponding render nodes while +/// maintaining proper positioning and parent-child relationships. +/// +/// # Arguments +/// * `result` - The render tree being constructed +/// * `plan` - Current execution plan node being processed +/// * `x` - Horizontal position in the tree +/// * `y` - Vertical position in the tree +/// +/// # Returns +/// * The width of the subtree rooted at the current node fn create_tree_recursive( result: &mut RenderTree, plan: &dyn ExecutionPlan, From 599e43fd2155fcde21a4938840bbafef1e4bc72f Mon Sep 17 00:00:00 2001 From: irenjj Date: Wed, 26 Feb 2025 23:44:28 +0800 Subject: [PATCH 16/17] fix test --- .../core/src/datasource/file_format/arrow.rs | 8 ++-- .../core/src/datasource/file_format/csv.rs | 8 ++-- .../core/src/datasource/file_format/json.rs | 8 ++-- .../src/datasource/file_format/parquet.rs | 8 ++-- datafusion/core/src/datasource/memory.rs | 8 ++-- datafusion/core/src/physical_planner.rs | 8 ++-- .../core/tests/custom_sources_cases/mod.rs | 9 ++-- .../provider_filter_pushdown.rs | 8 ++-- .../tests/custom_sources_cases/statistics.rs | 8 ++-- .../enforce_distribution.rs | 12 ++++- .../physical_optimizer/join_selection.rs | 17 ++++--- .../tests/physical_optimizer/test_utils.rs | 12 ++++- .../tests/user_defined/user_defined_plan.rs | 8 ++-- datafusion/datasource/src/file_scan_config.rs | 36 ++++++++------ datafusion/ffi/src/execution_plan.rs | 24 ++++++---- .../src/output_requirements.rs | 12 ++++- .../physical-plan/src/aggregates/mod.rs | 16 ++++--- datafusion/physical-plan/src/analyze.rs | 8 ++-- .../physical-plan/src/coalesce_batches.rs | 8 ++-- .../physical-plan/src/coalesce_partitions.rs | 8 ++-- datafusion/physical-plan/src/display.rs | 2 - datafusion/physical-plan/src/empty.rs | 8 ++-- datafusion/physical-plan/src/explain.rs | 8 ++-- datafusion/physical-plan/src/filter.rs | 8 ++-- datafusion/physical-plan/src/insert.rs | 8 ++-- .../physical-plan/src/joins/cross_join.rs | 8 ++-- .../physical-plan/src/joins/hash_join.rs | 8 ++-- .../src/joins/nested_loop_join.rs | 8 ++-- .../src/joins/sort_merge_join.rs | 8 ++-- .../src/joins/symmetric_hash_join.rs | 8 ++-- datafusion/physical-plan/src/limit.rs | 16 ++++--- datafusion/physical-plan/src/memory.rs | 8 ++-- .../physical-plan/src/placeholder_row.rs | 9 ++-- datafusion/physical-plan/src/projection.rs | 8 ++-- .../physical-plan/src/recursive_query.rs | 8 ++-- .../physical-plan/src/repartition/mod.rs | 8 ++-- .../physical-plan/src/sorts/partial_sort.rs | 8 ++-- datafusion/physical-plan/src/sorts/sort.rs | 18 ++++--- .../src/sorts/sort_preserving_merge.rs | 18 ++++--- datafusion/physical-plan/src/streaming.rs | 8 ++-- datafusion/physical-plan/src/test.rs | 8 ++-- datafusion/physical-plan/src/test/exec.rs | 48 ++++++++++++------- datafusion/physical-plan/src/union.rs | 16 ++++--- datafusion/physical-plan/src/unnest.rs | 7 +-- datafusion/physical-plan/src/values.rs | 8 ++-- .../src/windows/bounded_window_agg_exec.rs | 8 ++-- .../src/windows/window_agg_exec.rs | 8 ++-- datafusion/physical-plan/src/work_table.rs | 8 ++-- 48 files changed, 339 insertions(+), 189 deletions(-) diff --git a/datafusion/core/src/datasource/file_format/arrow.rs b/datafusion/core/src/datasource/file_format/arrow.rs index 98c80704fb18..6835d9a6da2a 100644 --- a/datafusion/core/src/datasource/file_format/arrow.rs +++ b/datafusion/core/src/datasource/file_format/arrow.rs @@ -296,13 +296,15 @@ impl Debug for ArrowFileSink { impl DisplayAs for ArrowFileSink { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "ArrowFileSink(file_groups=",)?; FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/core/src/datasource/file_format/csv.rs b/datafusion/core/src/datasource/file_format/csv.rs index 1c92fbbbe881..7a07c00d5fd4 100644 --- a/datafusion/core/src/datasource/file_format/csv.rs +++ b/datafusion/core/src/datasource/file_format/csv.rs @@ -667,13 +667,15 @@ impl Debug for CsvSink { impl DisplayAs for CsvSink { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "CsvSink(file_groups=",)?; FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/core/src/datasource/file_format/json.rs b/datafusion/core/src/datasource/file_format/json.rs index a40ff83dc8f1..133d69690f15 100644 --- a/datafusion/core/src/datasource/file_format/json.rs +++ b/datafusion/core/src/datasource/file_format/json.rs @@ -321,13 +321,15 @@ impl Debug for JsonSink { impl DisplayAs for JsonSink { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "JsonSink(file_groups=",)?; FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/core/src/datasource/file_format/parquet.rs b/datafusion/core/src/datasource/file_format/parquet.rs index faab74b5507e..593fc1b03252 100644 --- a/datafusion/core/src/datasource/file_format/parquet.rs +++ b/datafusion/core/src/datasource/file_format/parquet.rs @@ -698,13 +698,15 @@ impl Debug for ParquetSink { impl DisplayAs for ParquetSink { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "ParquetSink(file_groups=",)?; FileGroupDisplay(&self.config.file_groups).fmt_as(t, f)?; write!(f, ")") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/core/src/datasource/memory.rs b/datafusion/core/src/datasource/memory.rs index 3b3039b1b60a..d96944fa7a69 100644 --- a/datafusion/core/src/datasource/memory.rs +++ b/datafusion/core/src/datasource/memory.rs @@ -311,12 +311,14 @@ impl Debug for MemSink { impl DisplayAs for MemSink { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter<'_>) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let partition_count = self.batches.len(); write!(f, "MemoryTable (partitions={partition_count})") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/core/src/physical_planner.rs b/datafusion/core/src/physical_planner.rs index b812b7dc0a13..eef3d1b79694 100644 --- a/datafusion/core/src/physical_planner.rs +++ b/datafusion/core/src/physical_planner.rs @@ -2736,11 +2736,13 @@ mod tests { impl DisplayAs for NoOpExecutionPlan { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "NoOpExecutionPlan") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/core/tests/custom_sources_cases/mod.rs b/datafusion/core/tests/custom_sources_cases/mod.rs index e32f04d39d7b..eb930b9a60bc 100644 --- a/datafusion/core/tests/custom_sources_cases/mod.rs +++ b/datafusion/core/tests/custom_sources_cases/mod.rs @@ -135,11 +135,14 @@ impl DisplayAs for CustomExecutionPlan { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "CustomExecutionPlan: projection={:#?}", self.projection) } + + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs b/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs index 60b2378d4f1f..f68bcfaf1550 100644 --- a/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs +++ b/datafusion/core/tests/custom_sources_cases/provider_filter_pushdown.rs @@ -89,11 +89,13 @@ impl DisplayAs for CustomPlan { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "CustomPlan: batch_size={}", self.batches.len(),) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/core/tests/custom_sources_cases/statistics.rs b/datafusion/core/tests/custom_sources_cases/statistics.rs index c545fde9501d..66c886510e96 100644 --- a/datafusion/core/tests/custom_sources_cases/statistics.rs +++ b/datafusion/core/tests/custom_sources_cases/statistics.rs @@ -133,9 +133,7 @@ impl DisplayAs for StatisticsValidation { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!( f, "StatisticsValidation: col_count={}, row_count={:?}", @@ -143,6 +141,10 @@ impl DisplayAs for StatisticsValidation { self.stats.num_rows, ) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/core/tests/physical_optimizer/enforce_distribution.rs b/datafusion/core/tests/physical_optimizer/enforce_distribution.rs index 66d1380e09c3..fc2394d889d1 100644 --- a/datafusion/core/tests/physical_optimizer/enforce_distribution.rs +++ b/datafusion/core/tests/physical_optimizer/enforce_distribution.rs @@ -99,10 +99,18 @@ impl SortRequiredExec { impl DisplayAs for SortRequiredExec { fn fmt_as( &self, - _t: DisplayFormatType, + t: DisplayFormatType, f: &mut std::fmt::Formatter, ) -> std::fmt::Result { - write!(f, "SortRequiredExec: [{}]", self.expr) + match t { + DisplayFormatType::Default | DisplayFormatType::Verbose => { + write!(f, "SortRequiredExec: [{}]", self.expr) + } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } + } } } diff --git a/datafusion/core/tests/physical_optimizer/join_selection.rs b/datafusion/core/tests/physical_optimizer/join_selection.rs index 0cd0f47a0705..d3b6ec700bee 100644 --- a/datafusion/core/tests/physical_optimizer/join_selection.rs +++ b/datafusion/core/tests/physical_optimizer/join_selection.rs @@ -917,15 +917,17 @@ impl DisplayAs for UnboundedExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!( f, "UnboundedExec: unbounded={}", self.batch_produce.is_none(), ) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } @@ -1013,9 +1015,7 @@ impl DisplayAs for StatisticsExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!( f, "StatisticsExec: col_count={}, row_count={:?}", @@ -1023,6 +1023,11 @@ impl DisplayAs for StatisticsExec { self.stats.num_rows, ) } + + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/core/tests/physical_optimizer/test_utils.rs b/datafusion/core/tests/physical_optimizer/test_utils.rs index 418c46628daa..3e60c732bb3d 100644 --- a/datafusion/core/tests/physical_optimizer/test_utils.rs +++ b/datafusion/core/tests/physical_optimizer/test_utils.rs @@ -393,8 +393,16 @@ impl RequirementsTestExec { } impl DisplayAs for RequirementsTestExec { - fn fmt_as(&self, _t: DisplayFormatType, f: &mut Formatter) -> std::fmt::Result { - write!(f, "RequiredInputOrderingExec") + fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> std::fmt::Result { + match t { + DisplayFormatType::Default | DisplayFormatType::Verbose => { + write!(f, "RequiredInputOrderingExec") + } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } + } } } diff --git a/datafusion/core/tests/user_defined/user_defined_plan.rs b/datafusion/core/tests/user_defined/user_defined_plan.rs index 9a9d8f349904..915d61712074 100644 --- a/datafusion/core/tests/user_defined/user_defined_plan.rs +++ b/datafusion/core/tests/user_defined/user_defined_plan.rs @@ -697,11 +697,13 @@ impl Debug for TopKExec { impl DisplayAs for TopKExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "TopKExec: k={}", self.k) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/datasource/src/file_scan_config.rs b/datafusion/datasource/src/file_scan_config.rs index affea6e34afd..f6388ab421d0 100644 --- a/datafusion/datasource/src/file_scan_config.rs +++ b/datafusion/datasource/src/file_scan_config.rs @@ -194,26 +194,34 @@ impl DataSource for FileScanConfig { } fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> FmtResult { - let (schema, _, _, orderings) = self.project(); + match t { + DisplayFormatType::Default | DisplayFormatType::Verbose => { + let (schema, _, _, orderings) = self.project(); - write!(f, "file_groups=")?; - FileGroupsDisplay(&self.file_groups).fmt_as(t, f)?; + write!(f, "file_groups=")?; + FileGroupsDisplay(&self.file_groups).fmt_as(t, f)?; - if !schema.fields().is_empty() { - write!(f, ", projection={}", ProjectSchemaDisplay(&schema))?; - } + if !schema.fields().is_empty() { + write!(f, ", projection={}", ProjectSchemaDisplay(&schema))?; + } - if let Some(limit) = self.limit { - write!(f, ", limit={limit}")?; - } + if let Some(limit) = self.limit { + write!(f, ", limit={limit}")?; + } - display_orderings(f, &orderings)?; + display_orderings(f, &orderings)?; - if !self.constraints.is_empty() { - write!(f, ", {}", self.constraints)?; - } + if !self.constraints.is_empty() { + write!(f, ", {}", self.constraints)?; + } - self.fmt_file_source(t, f) + self.fmt_file_source(t, f) + } + DisplayFormatType::TreeRender => { + // TODO: collect info + Ok(()) + } + } } /// If supported by the underlying [`FileSource`], redistribute files across partitions according to their size. diff --git a/datafusion/ffi/src/execution_plan.rs b/datafusion/ffi/src/execution_plan.rs index 00602474d621..14a0908c4795 100644 --- a/datafusion/ffi/src/execution_plan.rs +++ b/datafusion/ffi/src/execution_plan.rs @@ -21,12 +21,12 @@ use abi_stable::{ std_types::{RResult, RString, RVec}, StableAbi, }; -use datafusion::error::Result; use datafusion::{ error::DataFusionError, execution::{SendableRecordBatchStream, TaskContext}, physical_plan::{DisplayAs, ExecutionPlan, PlanProperties}, }; +use datafusion::{error::Result, physical_plan::DisplayFormatType}; use tokio::runtime::Handle; use crate::{ @@ -198,14 +198,22 @@ unsafe impl Sync for ForeignExecutionPlan {} impl DisplayAs for ForeignExecutionPlan { fn fmt_as( &self, - _t: datafusion::physical_plan::DisplayFormatType, + t: DisplayFormatType, f: &mut std::fmt::Formatter, ) -> std::fmt::Result { - write!( - f, - "FFI_ExecutionPlan(number_of_children={})", - self.children.len(), - ) + match t { + DisplayFormatType::Default | DisplayFormatType::Verbose => { + write!( + f, + "FFI_ExecutionPlan(number_of_children={})", + self.children.len(), + ) + } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } + } } } @@ -315,7 +323,7 @@ mod tests { impl DisplayAs for EmptyExec { fn fmt_as( &self, - _t: datafusion::physical_plan::DisplayFormatType, + _t: DisplayFormatType, _f: &mut std::fmt::Formatter, ) -> std::fmt::Result { unimplemented!() diff --git a/datafusion/physical-optimizer/src/output_requirements.rs b/datafusion/physical-optimizer/src/output_requirements.rs index 90a570894a44..3ca0547aa11d 100644 --- a/datafusion/physical-optimizer/src/output_requirements.rs +++ b/datafusion/physical-optimizer/src/output_requirements.rs @@ -132,10 +132,18 @@ impl OutputRequirementExec { impl DisplayAs for OutputRequirementExec { fn fmt_as( &self, - _t: DisplayFormatType, + t: DisplayFormatType, f: &mut std::fmt::Formatter, ) -> std::fmt::Result { - write!(f, "OutputRequirementExec") + match t { + DisplayFormatType::Default | DisplayFormatType::Verbose => { + write!(f, "OutputRequirementExec") + } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } + } } } diff --git a/datafusion/physical-plan/src/aggregates/mod.rs b/datafusion/physical-plan/src/aggregates/mod.rs index 1226a39c489a..2fe5c17d8330 100644 --- a/datafusion/physical-plan/src/aggregates/mod.rs +++ b/datafusion/physical-plan/src/aggregates/mod.rs @@ -724,9 +724,7 @@ impl DisplayAs for AggregateExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "AggregateExec: mode={:?}", self.mode)?; let g: Vec = if self.group_by.is_single() { self.group_by @@ -791,6 +789,10 @@ impl DisplayAs for AggregateExec { write!(f, ", ordering_mode={:?}", self.input_order_mode)?; } } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "")?; + } } Ok(()) } @@ -1796,11 +1798,13 @@ mod tests { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "TestYieldingExec") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/analyze.rs b/datafusion/physical-plan/src/analyze.rs index f105e8cbd4fb..ea14ce676c1a 100644 --- a/datafusion/physical-plan/src/analyze.rs +++ b/datafusion/physical-plan/src/analyze.rs @@ -105,11 +105,13 @@ impl DisplayAs for AnalyzeExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "AnalyzeExec verbose={}", self.verbose) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/coalesce_batches.rs b/datafusion/physical-plan/src/coalesce_batches.rs index 28e1abdd14f8..0eb95bb66598 100644 --- a/datafusion/physical-plan/src/coalesce_batches.rs +++ b/datafusion/physical-plan/src/coalesce_batches.rs @@ -110,9 +110,7 @@ impl DisplayAs for CoalesceBatchesExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!( f, "CoalesceBatchesExec: target_batch_size={}", @@ -124,6 +122,10 @@ impl DisplayAs for CoalesceBatchesExec { Ok(()) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/coalesce_partitions.rs b/datafusion/physical-plan/src/coalesce_partitions.rs index 14a42023efbd..8fb40640dcc0 100644 --- a/datafusion/physical-plan/src/coalesce_partitions.rs +++ b/datafusion/physical-plan/src/coalesce_partitions.rs @@ -86,14 +86,16 @@ impl DisplayAs for CoalescePartitionsExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => match self.fetch { + DisplayFormatType::Default | DisplayFormatType::Verbose => match self.fetch { Some(fetch) => { write!(f, "CoalescePartitionsExec: fetch={fetch}") } None => write!(f, "CoalescePartitionsExec"), }, + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/display.rs b/datafusion/physical-plan/src/display.rs index f3c53c643993..e99973b4bebf 100644 --- a/datafusion/physical-plan/src/display.rs +++ b/datafusion/physical-plan/src/display.rs @@ -614,7 +614,6 @@ impl TreeRenderVisitor<'_, '_> { /// - Node names and extra information /// - Vertical borders (│) for boxes /// - Vertical connections between nodes - /// The content is center-aligned within each box. fn render_box_content( &mut self, root: &RenderTree, @@ -871,7 +870,6 @@ impl TreeRenderVisitor<'_, '_> { /// Adjusts text to fit within the specified width by: /// 1. Truncating with ellipsis if too long /// 2. Center-aligning within the available space if shorter - /// This ensures consistent box widths in the rendered tree. fn adjust_text_for_rendering(source: &str, max_render_width: usize) -> String { let render_width = source.chars().count(); if render_width > max_render_width { diff --git a/datafusion/physical-plan/src/empty.rs b/datafusion/physical-plan/src/empty.rs index 3127c37e35ee..3fdde39df6f1 100644 --- a/datafusion/physical-plan/src/empty.rs +++ b/datafusion/physical-plan/src/empty.rs @@ -91,11 +91,13 @@ impl DisplayAs for EmptyExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "EmptyExec") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/explain.rs b/datafusion/physical-plan/src/explain.rs index 87dc594fce06..bf488ccfae56 100644 --- a/datafusion/physical-plan/src/explain.rs +++ b/datafusion/physical-plan/src/explain.rs @@ -91,11 +91,13 @@ impl DisplayAs for ExplainExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "ExplainExec") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/filter.rs b/datafusion/physical-plan/src/filter.rs index cca82108725b..524da586f52e 100644 --- a/datafusion/physical-plan/src/filter.rs +++ b/datafusion/physical-plan/src/filter.rs @@ -308,9 +308,7 @@ impl DisplayAs for FilterExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let display_projections = if let Some(projection) = self.projection.as_ref() { @@ -331,6 +329,10 @@ impl DisplayAs for FilterExec { }; write!(f, "FilterExec: {}{}", self.predicate, display_projections) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/insert.rs b/datafusion/physical-plan/src/insert.rs index 6bff24ea13d1..ff65f6154b4a 100644 --- a/datafusion/physical-plan/src/insert.rs +++ b/datafusion/physical-plan/src/insert.rs @@ -149,12 +149,14 @@ impl DataSinkExec { impl DisplayAs for DataSinkExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "DataSinkExec: sink=")?; self.sink.fmt_as(t, f) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/joins/cross_join.rs b/datafusion/physical-plan/src/joins/cross_join.rs index e0998862bdd8..48dc7c9df302 100644 --- a/datafusion/physical-plan/src/joins/cross_join.rs +++ b/datafusion/physical-plan/src/joins/cross_join.rs @@ -237,11 +237,13 @@ impl DisplayAs for CrossJoinExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "CrossJoinExec") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/joins/hash_join.rs b/datafusion/physical-plan/src/joins/hash_join.rs index ed18be8f2b7d..fa712bb863a2 100644 --- a/datafusion/physical-plan/src/joins/hash_join.rs +++ b/datafusion/physical-plan/src/joins/hash_join.rs @@ -633,9 +633,7 @@ impl HashJoinExec { impl DisplayAs for HashJoinExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let display_filter = self.filter.as_ref().map_or_else( || "".to_string(), |f| format!(", filter={}", f.expression()), @@ -670,6 +668,10 @@ impl DisplayAs for HashJoinExec { self.mode, self.join_type, on, display_filter, display_projections ) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/joins/nested_loop_join.rs b/datafusion/physical-plan/src/joins/nested_loop_join.rs index e9d5aa81cd4d..f680de6738e5 100644 --- a/datafusion/physical-plan/src/joins/nested_loop_join.rs +++ b/datafusion/physical-plan/src/joins/nested_loop_join.rs @@ -395,9 +395,7 @@ impl NestedLoopJoinExec { impl DisplayAs for NestedLoopJoinExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let display_filter = self.filter.as_ref().map_or_else( || "".to_string(), |f| format!(", filter={}", f.expression()), @@ -426,6 +424,10 @@ impl DisplayAs for NestedLoopJoinExec { self.join_type, display_filter, display_projections ) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/joins/sort_merge_join.rs b/datafusion/physical-plan/src/joins/sort_merge_join.rs index aea96118a859..68d4e69d7059 100644 --- a/datafusion/physical-plan/src/joins/sort_merge_join.rs +++ b/datafusion/physical-plan/src/joins/sort_merge_join.rs @@ -351,9 +351,7 @@ impl SortMergeJoinExec { impl DisplayAs for SortMergeJoinExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let on = self .on .iter() @@ -371,6 +369,10 @@ impl DisplayAs for SortMergeJoinExec { )) ) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/joins/symmetric_hash_join.rs b/datafusion/physical-plan/src/joins/symmetric_hash_join.rs index 44d0bbda212f..03a68831438a 100644 --- a/datafusion/physical-plan/src/joins/symmetric_hash_join.rs +++ b/datafusion/physical-plan/src/joins/symmetric_hash_join.rs @@ -363,9 +363,7 @@ impl SymmetricHashJoinExec { impl DisplayAs for SymmetricHashJoinExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let display_filter = self.filter.as_ref().map_or_else( || "".to_string(), |f| format!(", filter={}", f.expression()), @@ -382,6 +380,10 @@ impl DisplayAs for SymmetricHashJoinExec { self.mode, self.join_type, on, display_filter ) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/limit.rs b/datafusion/physical-plan/src/limit.rs index 6fd3e55ad1ea..71a23d31ff66 100644 --- a/datafusion/physical-plan/src/limit.rs +++ b/datafusion/physical-plan/src/limit.rs @@ -100,9 +100,7 @@ impl DisplayAs for GlobalLimitExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!( f, "GlobalLimitExec: skip={}, fetch={}", @@ -110,6 +108,10 @@ impl DisplayAs for GlobalLimitExec { self.fetch.map_or("None".to_string(), |x| x.to_string()) ) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } @@ -260,11 +262,13 @@ impl DisplayAs for LocalLimitExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "LocalLimitExec: fetch={}", self.fetch) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/memory.rs b/datafusion/physical-plan/src/memory.rs index 7078e764e901..ae878fdab37b 100644 --- a/datafusion/physical-plan/src/memory.rs +++ b/datafusion/physical-plan/src/memory.rs @@ -180,9 +180,7 @@ impl fmt::Debug for LazyMemoryExec { impl DisplayAs for LazyMemoryExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!( f, "LazyMemoryExec: partitions={}, batch_generators=[{}]", @@ -194,6 +192,10 @@ impl DisplayAs for LazyMemoryExec { .join(", ") ) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/placeholder_row.rs b/datafusion/physical-plan/src/placeholder_row.rs index 0fd4bd59f7b2..72f2c13d2040 100644 --- a/datafusion/physical-plan/src/placeholder_row.rs +++ b/datafusion/physical-plan/src/placeholder_row.rs @@ -109,11 +109,14 @@ impl DisplayAs for PlaceholderRowExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "PlaceholderRowExec") } + + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/projection.rs b/datafusion/physical-plan/src/projection.rs index 3950cb06fcb1..8ff3824eff4c 100644 --- a/datafusion/physical-plan/src/projection.rs +++ b/datafusion/physical-plan/src/projection.rs @@ -151,9 +151,7 @@ impl DisplayAs for ProjectionExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let expr: Vec = self .expr .iter() @@ -169,6 +167,10 @@ impl DisplayAs for ProjectionExec { write!(f, "ProjectionExec: expr=[{}]", expr.join(", ")) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/recursive_query.rs b/datafusion/physical-plan/src/recursive_query.rs index 0ed6043372ec..7268735ea457 100644 --- a/datafusion/physical-plan/src/recursive_query.rs +++ b/datafusion/physical-plan/src/recursive_query.rs @@ -216,15 +216,17 @@ impl DisplayAs for RecursiveQueryExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!( f, "RecursiveQueryExec: name={}, is_distinct={}", self.name, self.is_distinct ) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/repartition/mod.rs b/datafusion/physical-plan/src/repartition/mod.rs index 80f82201c45d..e9a360c2ece3 100644 --- a/datafusion/physical-plan/src/repartition/mod.rs +++ b/datafusion/physical-plan/src/repartition/mod.rs @@ -488,9 +488,7 @@ impl DisplayAs for RepartitionExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!( f, "{}: partitioning={}, input_partitions={}", @@ -508,6 +506,10 @@ impl DisplayAs for RepartitionExec { } Ok(()) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/sorts/partial_sort.rs b/datafusion/physical-plan/src/sorts/partial_sort.rs index 32acc8ea5d21..bd0e6268de52 100644 --- a/datafusion/physical-plan/src/sorts/partial_sort.rs +++ b/datafusion/physical-plan/src/sorts/partial_sort.rs @@ -217,9 +217,7 @@ impl DisplayAs for PartialSortExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let common_prefix_length = self.common_prefix_length; match self.fetch { Some(fetch) => { @@ -228,6 +226,10 @@ impl DisplayAs for PartialSortExec { None => write!(f, "PartialSortExec: expr=[{}], common_prefix_length=[{common_prefix_length}]", self.expr), } } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/sorts/sort.rs b/datafusion/physical-plan/src/sorts/sort.rs index 092ac14846bf..105a90f0c77e 100644 --- a/datafusion/physical-plan/src/sorts/sort.rs +++ b/datafusion/physical-plan/src/sorts/sort.rs @@ -945,9 +945,7 @@ impl SortExec { impl DisplayAs for SortExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let preserve_partitioning = self.preserve_partitioning; match self.fetch { Some(fetch) => { @@ -956,6 +954,10 @@ impl DisplayAs for SortExec { None => write!(f, "SortExec: expr=[{}], preserve_partitioning=[{preserve_partitioning}]", self.expr), } } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } @@ -1172,9 +1174,13 @@ mod tests { impl DisplayAs for SortedUnboundedExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => write!(f, "UnboundableExec",).unwrap(), + DisplayFormatType::Default | DisplayFormatType::Verbose => { + write!(f, "UnboundableExec",).unwrap() + } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "").unwrap() + } } Ok(()) } diff --git a/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs b/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs index 65d66d2f3d1c..00fa78ce5229 100644 --- a/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs +++ b/datafusion/physical-plan/src/sorts/sort_preserving_merge.rs @@ -177,9 +177,7 @@ impl DisplayAs for SortPreservingMergeExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "SortPreservingMergeExec: [{}]", self.expr)?; if let Some(fetch) = self.fetch { write!(f, ", fetch={fetch}")?; @@ -187,6 +185,10 @@ impl DisplayAs for SortPreservingMergeExec { Ok(()) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } @@ -1382,9 +1384,13 @@ mod tests { impl DisplayAs for CongestedExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => write!(f, "CongestedExec",).unwrap(), + DisplayFormatType::Default | DisplayFormatType::Verbose => { + write!(f, "CongestedExec",).unwrap() + } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "").unwrap() + } } Ok(()) } diff --git a/datafusion/physical-plan/src/streaming.rs b/datafusion/physical-plan/src/streaming.rs index b3a14eeaa523..8104e8acf1f6 100644 --- a/datafusion/physical-plan/src/streaming.rs +++ b/datafusion/physical-plan/src/streaming.rs @@ -184,9 +184,7 @@ impl DisplayAs for StreamingTableExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!( f, "StreamingTableExec: partition_sizes={:?}", @@ -210,6 +208,10 @@ impl DisplayAs for StreamingTableExec { Ok(()) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/test.rs b/datafusion/physical-plan/src/test.rs index a1bdda2f10b0..a2dc1d778436 100644 --- a/datafusion/physical-plan/src/test.rs +++ b/datafusion/physical-plan/src/test.rs @@ -80,9 +80,7 @@ impl DisplayAs for TestMemoryExec { fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result { write!(f, "DataSourceExec: ")?; match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { let partition_sizes: Vec<_> = self.partitions.iter().map(|b| b.len()).collect(); @@ -119,6 +117,10 @@ impl DisplayAs for TestMemoryExec { ) } } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/test/exec.rs b/datafusion/physical-plan/src/test/exec.rs index 0c32e40df167..d0a0d25779cc 100644 --- a/datafusion/physical-plan/src/test/exec.rs +++ b/datafusion/physical-plan/src/test/exec.rs @@ -172,11 +172,13 @@ impl DisplayAs for MockExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "MockExec") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } @@ -336,11 +338,13 @@ impl DisplayAs for BarrierExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "BarrierExec") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } @@ -450,11 +454,13 @@ impl DisplayAs for ErrorExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "ErrorExec") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } @@ -533,9 +539,7 @@ impl DisplayAs for StatisticsExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!( f, "StatisticsExec: col_count={}, row_count={:?}", @@ -543,6 +547,10 @@ impl DisplayAs for StatisticsExec { self.stats.num_rows, ) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } @@ -635,11 +643,13 @@ impl DisplayAs for BlockingExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "BlockingExec",) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } @@ -779,11 +789,13 @@ impl DisplayAs for PanicExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "PanicExec",) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/union.rs b/datafusion/physical-plan/src/union.rs index 723b3d7fa269..791370917523 100644 --- a/datafusion/physical-plan/src/union.rs +++ b/datafusion/physical-plan/src/union.rs @@ -154,11 +154,13 @@ impl DisplayAs for UnionExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "UnionExec") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } @@ -386,11 +388,13 @@ impl DisplayAs for InterleaveExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "InterleaveExec") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/unnest.rs b/datafusion/physical-plan/src/unnest.rs index cf1641abbd9f..4e70d2dc4ee5 100644 --- a/datafusion/physical-plan/src/unnest.rs +++ b/datafusion/physical-plan/src/unnest.rs @@ -134,11 +134,12 @@ impl DisplayAs for UnnestExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "UnnestExec") } + DisplayFormatType::TreeRender => { + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/values.rs b/datafusion/physical-plan/src/values.rs index a4235aef95e2..6cb64bcb5d86 100644 --- a/datafusion/physical-plan/src/values.rs +++ b/datafusion/physical-plan/src/values.rs @@ -159,11 +159,13 @@ impl DisplayAs for ValuesExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "ValuesExec") } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } diff --git a/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs b/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs index 97d0a0f1722d..7e37156a133e 100644 --- a/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs +++ b/datafusion/physical-plan/src/windows/bounded_window_agg_exec.rs @@ -235,9 +235,7 @@ impl DisplayAs for BoundedWindowAggExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "BoundedWindowAggExec: ")?; let g: Vec = self .window_expr @@ -254,6 +252,10 @@ impl DisplayAs for BoundedWindowAggExec { let mode = &self.input_order_mode; write!(f, "wdw=[{}], mode=[{:?}]", g.join(", "), mode)?; } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "")?; + } } Ok(()) } diff --git a/datafusion/physical-plan/src/windows/window_agg_exec.rs b/datafusion/physical-plan/src/windows/window_agg_exec.rs index 75ab78418570..cd171115b610 100644 --- a/datafusion/physical-plan/src/windows/window_agg_exec.rs +++ b/datafusion/physical-plan/src/windows/window_agg_exec.rs @@ -165,9 +165,7 @@ impl DisplayAs for WindowAggExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "WindowAggExec: ")?; let g: Vec = self .window_expr @@ -183,6 +181,10 @@ impl DisplayAs for WindowAggExec { .collect(); write!(f, "wdw=[{}]", g.join(", "))?; } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "")?; + } } Ok(()) } diff --git a/datafusion/physical-plan/src/work_table.rs b/datafusion/physical-plan/src/work_table.rs index 9706862321e0..f082b05410dd 100644 --- a/datafusion/physical-plan/src/work_table.rs +++ b/datafusion/physical-plan/src/work_table.rs @@ -159,11 +159,13 @@ impl DisplayAs for WorkTableExec { f: &mut std::fmt::Formatter, ) -> std::fmt::Result { match t { - DisplayFormatType::Default - | DisplayFormatType::Verbose - | DisplayFormatType::TreeRender => { + DisplayFormatType::Default | DisplayFormatType::Verbose => { write!(f, "WorkTableExec: name={}", self.name) } + DisplayFormatType::TreeRender => { + // TODO: collect info + write!(f, "") + } } } } From 4907cf668be8fab375f76c43287d4004cc62de70 Mon Sep 17 00:00:00 2001 From: irenjj Date: Thu, 27 Feb 2025 08:30:27 +0800 Subject: [PATCH 17/17] fix doc --- datafusion/physical-plan/src/display.rs | 2 +- datafusion/physical-plan/src/render_tree.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/datafusion/physical-plan/src/display.rs b/datafusion/physical-plan/src/display.rs index e99973b4bebf..26e0a822f588 100644 --- a/datafusion/physical-plan/src/display.rs +++ b/datafusion/physical-plan/src/display.rs @@ -494,7 +494,7 @@ impl ExecutionPlanVisitor for GraphvizVisitor<'_, '_> { /// This module implements a tree-like art renderer for execution plans, /// based on DuckDB's implementation: -/// https://github.com/duckdb/duckdb/blob/main/src/include/duckdb/common/tree_renderer/text_tree_renderer.hpp +/// /// /// The rendered output looks like this: /// ```text diff --git a/datafusion/physical-plan/src/render_tree.rs b/datafusion/physical-plan/src/render_tree.rs index f970a51b1d6e..5f8da76a89b4 100644 --- a/datafusion/physical-plan/src/render_tree.rs +++ b/datafusion/physical-plan/src/render_tree.rs @@ -16,7 +16,7 @@ // under the License. // This code is based on the DuckDB’s implementation: -// https://github.com/duckdb/duckdb/blob/main/src/include/duckdb/common/render_tree.hpp +// //! This module provides functionality for rendering an execution plan as a tree structure. //! It helps in visualizing how different operations in a query are connected and organized.