This is an automated email from the ASF dual-hosted git repository.

alamb pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new 3dc212c907 Implement `tree` explain for FilterExec (#15001)
3dc212c907 is described below

commit 3dc212c9078c92f57ab7f58e75e1258130c772d0
Author: Andrew Lamb <[email protected]>
AuthorDate: Wed Mar 5 08:55:06 2025 -0500

    Implement `tree` explain for FilterExec (#15001)
---
 datafusion/physical-plan/src/display.rs            | 35 +++++++++++++++++--
 datafusion/physical-plan/src/filter.rs             |  3 +-
 .../sqllogictest/test_files/explain_tree.slt       | 39 +++++++++++++++++++---
 3 files changed, 68 insertions(+), 9 deletions(-)

diff --git a/datafusion/physical-plan/src/display.rs 
b/datafusion/physical-plan/src/display.rs
index 26e0a822f5..096b515407 100644
--- a/datafusion/physical-plan/src/display.rs
+++ b/datafusion/physical-plan/src/display.rs
@@ -37,10 +37,41 @@ use super::{accept, ExecutionPlan, ExecutionPlanVisitor};
 #[derive(Debug, Clone, Copy)]
 pub enum DisplayFormatType {
     /// Default, compact format. Example: `FilterExec: c12 < 10.0`
+    ///
+    /// This format is designed to provide a detailed textual description
+    /// of all rele
     Default,
-    /// Verbose, showing all available details
+    /// Verbose, showing all available details.
+    ///
+    /// This form is even more detailed than [`Self::Default`]
     Verbose,
-    /// TreeRender, display plan like a tree.
+    /// TreeRender, displayed in the `tree` explain type.
+    ///
+    /// This format is inspired by DuckDB's explain plans. The information
+    /// presented should be "user friendly", and contain only the most relevant
+    /// information for understanding a plan. It should NOT contain the same 
level
+    /// of detail information as the  [`Self::Default`] format.
+    ///
+    /// In this mode, each line contains a key=value pair.
+    /// Everything before the first `=` is treated as the key, and everything 
after the
+    /// first `=` is treated as the value.
+    ///
+    /// For example, if the output of `TreeRender` is this:
+    /// ```text
+    /// partition_sizes=[1]
+    /// partitions=1
+    /// ```
+    ///
+    /// It is rendered in the center of a box in the following way:
+    ///
+    /// ```text
+    /// ┌───────────────────────────┐
+    /// │       DataSourceExec      │
+    /// │    --------------------   │
+    /// │    partition_sizes: [1]   │
+    /// │       partitions: 1       │
+    /// └───────────────────────────┘
+    ///  ```
     TreeRender,
 }
 
diff --git a/datafusion/physical-plan/src/filter.rs 
b/datafusion/physical-plan/src/filter.rs
index 524da586f5..ffcda1d888 100644
--- a/datafusion/physical-plan/src/filter.rs
+++ b/datafusion/physical-plan/src/filter.rs
@@ -330,8 +330,7 @@ impl DisplayAs for FilterExec {
                 write!(f, "FilterExec: {}{}", self.predicate, 
display_projections)
             }
             DisplayFormatType::TreeRender => {
-                // TODO: collect info
-                write!(f, "")
+                write!(f, "predicate={}", self.predicate)
             }
         }
     }
diff --git a/datafusion/sqllogictest/test_files/explain_tree.slt 
b/datafusion/sqllogictest/test_files/explain_tree.slt
index 5f07857820..18de2c6a61 100644
--- a/datafusion/sqllogictest/test_files/explain_tree.slt
+++ b/datafusion/sqllogictest/test_files/explain_tree.slt
@@ -74,13 +74,16 @@ physical_plan
 03)└─────────────┬─────────────┘
 04)┌─────────────┴─────────────┐
 05)│         FilterExec        │
-06)└─────────────┬─────────────┘
-07)┌─────────────┴─────────────┐
-08)│      RepartitionExec      │
+06)│    --------------------   │
+07)│         predicate:        │
+08)│    string_col@1 != foo    │
 09)└─────────────┬─────────────┘
 10)┌─────────────┴─────────────┐
-11)│       DataSourceExec      │
-12)└───────────────────────────┘
+11)│      RepartitionExec      │
+12)└─────────────┬─────────────┘
+13)┌─────────────┴─────────────┐
+14)│       DataSourceExec      │
+15)└───────────────────────────┘
 
 # Aggregate
 query TT
@@ -185,6 +188,32 @@ physical_plan
 26)│       DataSourceExec      ││       DataSourceExec      │
 27)└───────────────────────────┘└───────────────────────────┘
 
+# Long Filter (demonstrate what happens with wrapping)
+query TT
+explain SELECT int_col FROM table1
+WHERE string_col != 'foo' AND string_col != 'bar' AND string_col != 'a really 
long string constant'
+;
+----
+logical_plan
+01)Projection: table1.int_col
+02)--Filter: table1.string_col != Utf8("foo") AND table1.string_col != 
Utf8("bar") AND table1.string_col != Utf8("a really long string constant")
+03)----TableScan: table1 projection=[int_col, string_col], 
partial_filters=[table1.string_col != Utf8("foo"), table1.string_col != 
Utf8("bar"), table1.string_col != Utf8("a really long string constant")]
+physical_plan
+01)┌───────────────────────────┐
+02)│    CoalesceBatchesExec    │
+03)└─────────────┬─────────────┘
+04)┌─────────────┴─────────────┐
+05)│         FilterExec        │
+06)│    --------------------   │
+07)│         predicate:        │
+08)│string_col@1 != foo AND ...│
+09)└─────────────┬─────────────┘
+10)┌─────────────┴─────────────┐
+11)│      RepartitionExec      │
+12)└─────────────┬─────────────┘
+13)┌─────────────┴─────────────┐
+14)│       DataSourceExec      │
+15)└───────────────────────────┘
 
 
 # cleanup


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to