diff --git a/datafusion/core/src/physical_planner.rs b/datafusion/core/src/physical_planner.rs index 135b32a0a8d7a..bc3c1d0ac99b2 100644 --- a/datafusion/core/src/physical_planner.rs +++ b/datafusion/core/src/physical_planner.rs @@ -1588,6 +1588,7 @@ type AggregateExprWithOptionalArgs = ( pub fn create_aggregate_expr_with_name_and_maybe_filter( e: &Expr, name: Option, + human_displan: String, logical_input_schema: &DFSchema, physical_input_schema: &Schema, execution_props: &ExecutionProps, @@ -1642,6 +1643,7 @@ pub fn create_aggregate_expr_with_name_and_maybe_filter( .order_by(ordering_reqs) .schema(Arc::new(physical_input_schema.to_owned())) .alias(name) + .human_display(human_displan) .with_ignore_nulls(ignore_nulls) .with_distinct(*distinct) .build() @@ -1664,15 +1666,22 @@ pub fn create_aggregate_expr_and_maybe_filter( execution_props: &ExecutionProps, ) -> Result { // unpack (nested) aliased logical expressions, e.g. "sum(col) as total" - let (name, e) = match e { - Expr::Alias(Alias { expr, name, .. }) => (Some(name.clone()), expr.as_ref()), - Expr::AggregateFunction(_) => (Some(e.schema_name().to_string()), e), - _ => (None, e), + let (name, human_display, e) = match e { + Expr::Alias(Alias { expr, name, .. }) => { + (Some(name.clone()), String::default(), expr.as_ref()) + } + Expr::AggregateFunction(_) => ( + Some(e.schema_name().to_string()), + e.human_display().to_string(), + e, + ), + _ => (None, String::default(), e), }; create_aggregate_expr_with_name_and_maybe_filter( e, name, + human_display, logical_input_schema, physical_input_schema, execution_props, diff --git a/datafusion/expr/src/expr.rs b/datafusion/expr/src/expr.rs index 6f110895a40ab..9f6855b698243 100644 --- a/datafusion/expr/src/expr.rs +++ b/datafusion/expr/src/expr.rs @@ -64,6 +64,15 @@ use sqlparser::ast::{ /// /// [`ExprFunctionExt`]: crate::expr_fn::ExprFunctionExt /// +/// # Printing Expressions +/// +/// You can print `Expr`s using the the `Debug` trait, `Display` trait, or +/// [`Self::human_display`]. See the [examples](#examples-displaying-exprs) below. +/// +/// If you need SQL to pass to other systems, consider using [`Unparser`]. +/// +/// [`Unparser`]: https://docs.rs/datafusion/latest/datafusion/sql/unparser/struct.Unparser.html +/// /// # Schema Access /// /// See [`ExprSchemable::get_type`] to access the [`DataType`] and nullability @@ -76,9 +85,9 @@ use sqlparser::ast::{ /// `Expr` and [`TreeNode::transform`] can be used to rewrite an expression. See /// the examples below and [`TreeNode`] for more information. /// -/// # Examples +/// # Examples: Creating and Using `Expr`s /// -/// ## Column references and literals +/// ## Column References and Literals /// /// [`Expr::Column`] refer to the values of columns and are often created with /// the [`col`] function. For example to create an expression `c1` referring to @@ -104,6 +113,7 @@ use sqlparser::ast::{ /// // All literals are strongly typed in DataFusion. To make an `i64` 42: /// let expr = lit(42i64); /// assert_eq!(expr, Expr::Literal(ScalarValue::Int64(Some(42)))); +/// assert_eq!(expr, Expr::Literal(ScalarValue::Int64(Some(42)))); /// // To make a (typed) NULL: /// let expr = Expr::Literal(ScalarValue::Int64(None)); /// // to make an (untyped) NULL (the optimizer will coerce this to the correct type): @@ -171,7 +181,51 @@ use sqlparser::ast::{ /// ]); /// ``` /// -/// # Visiting and Rewriting `Expr`s +/// # Examples: Displaying `Exprs` +/// +/// There are three ways to print an `Expr` depending on the usecase. +/// +/// ## Use `Debug` trait +/// +/// Following Rust conventions, the `Debug` implementation prints out the +/// internal structure of the expression, which is useful for debugging. +/// +/// ``` +/// # use datafusion_expr::{lit, col}; +/// let expr = col("c1") + lit(42); +/// assert_eq!(format!("{expr:?}"), "BinaryExpr(BinaryExpr { left: Column(Column { relation: None, name: \"c1\" }), op: Plus, right: Literal(Int32(42)) })"); +/// ``` +/// +/// ## Use the `Display` trait (detailed expression) +/// +/// The `Display` implementation prints out the expression in a SQL-like form, +/// but has additional details such as the data type of literals. This is useful +/// for understanding the expression in more detail and is used for the low level +/// [`ExplainFormat::Indent`] explain plan format. +/// +/// [`ExplainFormat::Indent`]: crate::logical_plan::ExplainFormat::Indent +/// +/// ``` +/// # use datafusion_expr::{lit, col}; +/// let expr = col("c1") + lit(42); +/// assert_eq!(format!("{expr}"), "c1 + Int32(42)"); +/// ``` +/// +/// ## Use [`Self::human_display`] (human readable) +/// +/// [`Self::human_display`] prints out the expression in a SQL-like form, optimized +/// for human consumption by end users. It is used for the +/// [`ExplainFormat::Tree`] explain plan format. +/// +/// [`ExplainFormat::Tree`]: crate::logical_plan::ExplainFormat::Tree +/// +///``` +/// # use datafusion_expr::{lit, col}; +/// let expr = col("c1") + lit(42); +/// assert_eq!(format!("{}", expr.human_display()), "c1 + 42"); +/// ``` +/// +/// # Examples: Visiting and Rewriting `Expr`s /// /// Here is an example that finds all literals in an `Expr` tree: /// ``` @@ -1147,6 +1201,31 @@ impl Expr { SchemaDisplay(self) } + /// Human readable display formatting for this expression. + /// + /// This function is primarily used in printing the explain tree output, + /// (e.g. `EXPLAIN FORMAT TREE `), providing a readable format to + /// show how expressions are used in physical and logical plans. See the + /// [`Expr`] for other ways to format expressions + /// + /// Note this format is intended for human consumption rather than SQL for + /// other systems. If you need SQL to pass to other systems, consider using + /// [`Unparser`]. + /// + /// [`Unparser`]: https://docs.rs/datafusion/latest/datafusion/sql/unparser/struct.Unparser.html + /// + /// # Example + /// ``` + /// # use datafusion_expr::{col, lit}; + /// let expr = col("foo") + lit(42); + /// // For EXPLAIN output: + /// // "foo + 42" + /// println!("{}", expr.human_display()); + /// ``` + pub fn human_display(&self) -> impl Display + '_ { + SqlDisplay(self) + } + /// Returns the qualifier and the schema name of this expression. /// /// Used when the expression forms the output field of a certain plan. @@ -2596,6 +2675,187 @@ impl Display for SchemaDisplay<'_> { } } +/// A helper struct for displaying an `Expr` as an SQL-like string. +struct SqlDisplay<'a>(&'a Expr); + +impl Display for SqlDisplay<'_> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self.0 { + Expr::Literal(scalar) => scalar.fmt(f), + Expr::Alias(Alias { name, .. }) => write!(f, "{name}"), + Expr::Between(Between { + expr, + negated, + low, + high, + }) => { + if *negated { + write!( + f, + "{} NOT BETWEEN {} AND {}", + SqlDisplay(expr), + SqlDisplay(low), + SqlDisplay(high), + ) + } else { + write!( + f, + "{} BETWEEN {} AND {}", + SqlDisplay(expr), + SqlDisplay(low), + SqlDisplay(high), + ) + } + } + Expr::BinaryExpr(BinaryExpr { left, op, right }) => { + write!(f, "{} {op} {}", SqlDisplay(left), SqlDisplay(right),) + } + Expr::Case(Case { + expr, + when_then_expr, + else_expr, + }) => { + write!(f, "CASE ")?; + + if let Some(e) = expr { + write!(f, "{} ", SqlDisplay(e))?; + } + + for (when, then) in when_then_expr { + write!(f, "WHEN {} THEN {} ", SqlDisplay(when), SqlDisplay(then),)?; + } + + if let Some(e) = else_expr { + write!(f, "ELSE {} ", SqlDisplay(e))?; + } + + write!(f, "END") + } + Expr::Cast(Cast { expr, .. }) | Expr::TryCast(TryCast { expr, .. }) => { + write!(f, "{}", SqlDisplay(expr)) + } + Expr::InList(InList { + expr, + list, + negated, + }) => { + write!( + f, + "{}{} IN {}", + SqlDisplay(expr), + if *negated { " NOT" } else { "" }, + ExprListDisplay::comma_separated(list.as_slice()) + ) + } + Expr::GroupingSet(GroupingSet::Cube(exprs)) => { + write!( + f, + "ROLLUP ({})", + ExprListDisplay::comma_separated(exprs.as_slice()) + ) + } + Expr::GroupingSet(GroupingSet::GroupingSets(lists_of_exprs)) => { + write!(f, "GROUPING SETS (")?; + for exprs in lists_of_exprs.iter() { + write!( + f, + "({})", + ExprListDisplay::comma_separated(exprs.as_slice()) + )?; + } + write!(f, ")") + } + Expr::GroupingSet(GroupingSet::Rollup(exprs)) => { + write!( + f, + "ROLLUP ({})", + ExprListDisplay::comma_separated(exprs.as_slice()) + ) + } + Expr::IsNull(expr) => write!(f, "{} IS NULL", SqlDisplay(expr)), + Expr::IsNotNull(expr) => { + write!(f, "{} IS NOT NULL", SqlDisplay(expr)) + } + Expr::IsUnknown(expr) => { + write!(f, "{} IS UNKNOWN", SqlDisplay(expr)) + } + Expr::IsNotUnknown(expr) => { + write!(f, "{} IS NOT UNKNOWN", SqlDisplay(expr)) + } + Expr::IsTrue(expr) => write!(f, "{} IS TRUE", SqlDisplay(expr)), + Expr::IsFalse(expr) => write!(f, "{} IS FALSE", SqlDisplay(expr)), + Expr::IsNotTrue(expr) => { + write!(f, "{} IS NOT TRUE", SqlDisplay(expr)) + } + Expr::IsNotFalse(expr) => { + write!(f, "{} IS NOT FALSE", SqlDisplay(expr)) + } + Expr::Like(Like { + negated, + expr, + pattern, + escape_char, + case_insensitive, + }) => { + write!( + f, + "{} {}{} {}", + SqlDisplay(expr), + if *negated { "NOT " } else { "" }, + if *case_insensitive { "ILIKE" } else { "LIKE" }, + SqlDisplay(pattern), + )?; + + if let Some(char) = escape_char { + write!(f, " CHAR '{char}'")?; + } + + Ok(()) + } + Expr::Negative(expr) => write!(f, "(- {})", SqlDisplay(expr)), + Expr::Not(expr) => write!(f, "NOT {}", SqlDisplay(expr)), + Expr::Unnest(Unnest { expr }) => { + write!(f, "UNNEST({})", SqlDisplay(expr)) + } + Expr::SimilarTo(Like { + negated, + expr, + pattern, + escape_char, + .. + }) => { + write!( + f, + "{} {} {}", + SqlDisplay(expr), + if *negated { + "NOT SIMILAR TO" + } else { + "SIMILAR TO" + }, + SqlDisplay(pattern), + )?; + if let Some(char) = escape_char { + write!(f, " CHAR '{char}'")?; + } + + Ok(()) + } + Expr::AggregateFunction(AggregateFunction { func, params }) => { + match func.human_display(params) { + Ok(name) => { + write!(f, "{name}") + } + Err(e) => { + write!(f, "got error from schema_name {}", e) + } + } + } + _ => write!(f, "{}", self.0), + } + } +} + /// Get schema_name for Vector of expressions /// /// Internal usage. Please call `schema_name_from_exprs` instead @@ -2607,6 +2867,38 @@ pub(crate) fn schema_name_from_exprs_comma_separated_without_space( schema_name_from_exprs_inner(exprs, ",") } +/// Formats a list of `&Expr` with a custom separator using SQL display format +pub struct ExprListDisplay<'a> { + exprs: &'a [Expr], + sep: &'a str, +} + +impl<'a> ExprListDisplay<'a> { + /// Create a new display struct with the given expressions and separator + pub fn new(exprs: &'a [Expr], sep: &'a str) -> Self { + Self { exprs, sep } + } + + /// Create a new display struct with comma-space separator + pub fn comma_separated(exprs: &'a [Expr]) -> Self { + Self::new(exprs, ", ") + } +} + +impl Display for ExprListDisplay<'_> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let mut first = true; + for expr in self.exprs { + if !first { + write!(f, "{}", self.sep)?; + } + write!(f, "{}", SqlDisplay(expr))?; + first = false; + } + Ok(()) + } +} + /// Get schema_name for Vector of expressions pub fn schema_name_from_exprs(exprs: &[Expr]) -> Result { schema_name_from_exprs_inner(exprs, ", ") diff --git a/datafusion/expr/src/udaf.rs b/datafusion/expr/src/udaf.rs index f9039cea2edc7..b75e8fd3cd3c4 100644 --- a/datafusion/expr/src/udaf.rs +++ b/datafusion/expr/src/udaf.rs @@ -31,7 +31,7 @@ use datafusion_physical_expr_common::physical_expr::PhysicalExpr; use crate::expr::{ schema_name_from_exprs, schema_name_from_exprs_comma_separated_without_space, - schema_name_from_sorts, AggregateFunction, AggregateFunctionParams, + schema_name_from_sorts, AggregateFunction, AggregateFunctionParams, ExprListDisplay, WindowFunctionParams, }; use crate::function::{ @@ -175,6 +175,13 @@ impl AggregateUDF { self.inner.schema_name(params) } + /// Returns a human readable expression. + /// + /// See [`Expr::human_display`] for details. + pub fn human_display(&self, params: &AggregateFunctionParams) -> Result { + self.inner.human_display(params) + } + pub fn window_function_schema_name( &self, params: &WindowFunctionParams, @@ -452,6 +459,45 @@ pub trait AggregateUDFImpl: Debug + Send + Sync { Ok(schema_name) } + /// Returns a human readable expression. + /// + /// See [`Expr::human_display`] for details. + fn human_display(&self, params: &AggregateFunctionParams) -> Result { + let AggregateFunctionParams { + args, + distinct, + filter, + order_by, + null_treatment, + } = params; + + let mut schema_name = String::new(); + + schema_name.write_fmt(format_args!( + "{}({}{})", + self.name(), + if *distinct { "DISTINCT " } else { "" }, + ExprListDisplay::comma_separated(args.as_slice()) + ))?; + + if let Some(null_treatment) = null_treatment { + schema_name.write_fmt(format_args!(" {}", null_treatment))?; + } + + if let Some(filter) = filter { + schema_name.write_fmt(format_args!(" FILTER (WHERE {filter})"))?; + }; + + if let Some(order_by) = order_by { + schema_name.write_fmt(format_args!( + " ORDER BY [{}]", + schema_name_from_sorts(order_by)? + ))?; + }; + + Ok(schema_name) + } + /// Returns the name of the column this expression would create /// /// See [`Expr::schema_name`] for details diff --git a/datafusion/physical-expr/src/aggregate.rs b/datafusion/physical-expr/src/aggregate.rs index 34c4e52d517e7..ae3d9050fa628 100644 --- a/datafusion/physical-expr/src/aggregate.rs +++ b/datafusion/physical-expr/src/aggregate.rs @@ -65,6 +65,8 @@ pub struct AggregateExprBuilder { /// Physical expressions of the aggregate function args: Vec>, alias: Option, + /// A human readable name + human_display: String, /// Arrow Schema for the aggregate function schema: SchemaRef, /// The physical order by expressions @@ -83,6 +85,7 @@ impl AggregateExprBuilder { fun, args, alias: None, + human_display: String::default(), schema: Arc::new(Schema::empty()), ordering_req: LexOrdering::default(), ignore_nulls: false, @@ -99,6 +102,7 @@ impl AggregateExprBuilder { fun, args, alias, + human_display, schema, ordering_req, ignore_nulls, @@ -148,6 +152,7 @@ impl AggregateExprBuilder { args, data_type, name, + human_display, schema: Arc::unwrap_or_clone(schema), ordering_req, ignore_nulls, @@ -164,6 +169,11 @@ impl AggregateExprBuilder { self } + pub fn human_display(mut self, name: String) -> Self { + self.human_display = name; + self + } + pub fn schema(mut self, schema: SchemaRef) -> Self { self.schema = schema; self @@ -214,7 +224,10 @@ pub struct AggregateFunctionExpr { args: Vec>, /// Output / return type of this aggregate data_type: DataType, + /// Output column name that this expression creates name: String, + /// Simplified name for `tree` explain. + human_display: String, schema: Schema, // The physical order by expressions ordering_req: LexOrdering, @@ -245,6 +258,11 @@ impl AggregateFunctionExpr { &self.name } + /// Simplified name for `tree` explain. + pub fn human_display(&self) -> &str { + &self.human_display + } + /// Return if the aggregation is distinct pub fn is_distinct(&self) -> bool { self.is_distinct diff --git a/datafusion/physical-plan/src/aggregates/mod.rs b/datafusion/physical-plan/src/aggregates/mod.rs index 4230eeeed0c9a..84ba0fe3b630b 100644 --- a/datafusion/physical-plan/src/aggregates/mod.rs +++ b/datafusion/physical-plan/src/aggregates/mod.rs @@ -48,6 +48,7 @@ use datafusion_physical_expr::{ PhysicalSortRequirement, }; +use datafusion_physical_expr_common::physical_expr::fmt_sql; use itertools::Itertools; pub(crate) mod group_values; @@ -742,17 +743,18 @@ impl DisplayAs for AggregateExec { t: DisplayFormatType, f: &mut std::fmt::Formatter, ) -> std::fmt::Result { - let format_expr_with_alias = - |(e, alias): &(Arc, String)| -> String { - let e = e.to_string(); - if &e != alias { - format!("{e} as {alias}") - } else { - e - } - }; match t { DisplayFormatType::Default | DisplayFormatType::Verbose => { + let format_expr_with_alias = + |(e, alias): &(Arc, String)| -> String { + let e = e.to_string(); + if &e != alias { + format!("{e} as {alias}") + } else { + e + } + }; + write!(f, "AggregateExec: mode={:?}", self.mode)?; let g: Vec = if self.group_by.is_single() { self.group_by @@ -801,6 +803,16 @@ impl DisplayAs for AggregateExec { } } DisplayFormatType::TreeRender => { + let format_expr_with_alias = + |(e, alias): &(Arc, String)| -> String { + let expr_sql = fmt_sql(e.as_ref()).to_string(); + if &expr_sql != alias { + format!("{expr_sql} as {alias}") + } else { + expr_sql + } + }; + let g: Vec = if self.group_by.is_single() { self.group_by .expr @@ -830,17 +842,18 @@ impl DisplayAs for AggregateExec { }) .collect() }; - // TODO: Implement `fmt_sql` for `AggregateFunctionExpr`. let a: Vec = self .aggr_expr .iter() - .map(|agg| agg.name().to_string()) + .map(|agg| agg.human_display().to_string()) .collect(); writeln!(f, "mode={:?}", self.mode)?; if !g.is_empty() { writeln!(f, "group_by={}", g.join(", "))?; } - writeln!(f, "aggr={}", a.join(", "))?; + if !a.is_empty() { + writeln!(f, "aggr={}", a.join(", "))?; + } } } Ok(()) diff --git a/datafusion/sqllogictest/test_files/explain_tree.slt b/datafusion/sqllogictest/test_files/explain_tree.slt index 22b1b46019752..aaea05be76b54 100644 --- a/datafusion/sqllogictest/test_files/explain_tree.slt +++ b/datafusion/sqllogictest/test_files/explain_tree.slt @@ -204,53 +204,50 @@ physical_plan 04)│ aggr: │ 05)│ sum(table1.bigint_col) │ 06)│ │ -07)│ group_by: │ -08)│ string_col@0 as string_col│ -09)│ │ -10)│ mode: │ -11)│ FinalPartitioned │ -12)└─────────────┬─────────────┘ -13)┌─────────────┴─────────────┐ -14)│ CoalesceBatchesExec │ -15)│ -------------------- │ -16)│ target_batch_size: │ -17)│ 8192 │ -18)└─────────────┬─────────────┘ -19)┌─────────────┴─────────────┐ -20)│ RepartitionExec │ -21)│ -------------------- │ -22)│ output_partition_count: │ -23)│ 4 │ -24)│ │ -25)│ partitioning_scheme: │ -26)│ Hash([string_col@0], 4) │ -27)└─────────────┬─────────────┘ -28)┌─────────────┴─────────────┐ -29)│ AggregateExec │ -30)│ -------------------- │ -31)│ aggr: │ -32)│ sum(table1.bigint_col) │ -33)│ │ -34)│ group_by: │ -35)│ string_col@0 as string_col│ -36)│ │ -37)│ mode: Partial │ -38)└─────────────┬─────────────┘ -39)┌─────────────┴─────────────┐ -40)│ RepartitionExec │ -41)│ -------------------- │ -42)│ output_partition_count: │ -43)│ 1 │ -44)│ │ -45)│ partitioning_scheme: │ -46)│ RoundRobinBatch(4) │ -47)└─────────────┬─────────────┘ -48)┌─────────────┴─────────────┐ -49)│ DataSourceExec │ -50)│ -------------------- │ -51)│ files: 1 │ -52)│ format: csv │ -53)└───────────────────────────┘ +07)│ group_by: string_col │ +08)│ │ +09)│ mode: │ +10)│ FinalPartitioned │ +11)└─────────────┬─────────────┘ +12)┌─────────────┴─────────────┐ +13)│ CoalesceBatchesExec │ +14)│ -------------------- │ +15)│ target_batch_size: │ +16)│ 8192 │ +17)└─────────────┬─────────────┘ +18)┌─────────────┴─────────────┐ +19)│ RepartitionExec │ +20)│ -------------------- │ +21)│ output_partition_count: │ +22)│ 4 │ +23)│ │ +24)│ partitioning_scheme: │ +25)│ Hash([string_col@0], 4) │ +26)└─────────────┬─────────────┘ +27)┌─────────────┴─────────────┐ +28)│ AggregateExec │ +29)│ -------------------- │ +30)│ aggr: │ +31)│ sum(table1.bigint_col) │ +32)│ │ +33)│ group_by: string_col │ +34)│ mode: Partial │ +35)└─────────────┬─────────────┘ +36)┌─────────────┴─────────────┐ +37)│ RepartitionExec │ +38)│ -------------------- │ +39)│ output_partition_count: │ +40)│ 1 │ +41)│ │ +42)│ partitioning_scheme: │ +43)│ RoundRobinBatch(4) │ +44)└─────────────┬─────────────┘ +45)┌─────────────┴─────────────┐ +46)│ DataSourceExec │ +47)│ -------------------- │ +48)│ files: 1 │ +49)│ format: csv │ +50)└───────────────────────────┘ # Limit @@ -1339,7 +1336,7 @@ physical_plan 12)-----------------------------┌─────────────┴─────────────┐ 13)-----------------------------│ AggregateExec │ 14)-----------------------------│ -------------------- │ -15)-----------------------------│ aggr: count(Int64(1)) │ +15)-----------------------------│ aggr: count(1) │ 16)-----------------------------│ mode: Final │ 17)-----------------------------└─────────────┬─────────────┘ 18)-----------------------------┌─────────────┴─────────────┐ @@ -1348,7 +1345,7 @@ physical_plan 21)-----------------------------┌─────────────┴─────────────┐ 22)-----------------------------│ AggregateExec │ 23)-----------------------------│ -------------------- │ -24)-----------------------------│ aggr: count(Int64(1)) │ +24)-----------------------------│ aggr: count(1) │ 25)-----------------------------│ mode: Partial │ 26)-----------------------------└─────────────┬─────────────┘ 27)-----------------------------┌─────────────┴─────────────┐ @@ -1477,69 +1474,60 @@ physical_plan 07)┌─────────────┴─────────────┐ 08)│ AggregateExec │ 09)│ -------------------- │ -10)│ aggr: count(Int64(1)) │ -11)│ │ -12)│ group_by: │ -13)│ name@0 as name │ -14)│ │ -15)│ mode: │ -16)│ SinglePartitioned │ -17)└─────────────┬─────────────┘ -18)┌─────────────┴─────────────┐ -19)│ InterleaveExec ├──────────────┐ -20)└─────────────┬─────────────┘ │ -21)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ -22)│ AggregateExec ││ AggregateExec │ -23)│ -------------------- ││ -------------------- │ -24)│ aggr ││ aggr │ -25)│ ││ │ -26)│ group_by: ││ group_by: │ -27)│ name@0 as name ││ name@0 as name │ -28)│ ││ │ -29)│ mode: ││ mode: │ -30)│ FinalPartitioned ││ FinalPartitioned │ -31)└─────────────┬─────────────┘└─────────────┬─────────────┘ -32)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ -33)│ CoalesceBatchesExec ││ CoalesceBatchesExec │ -34)│ -------------------- ││ -------------------- │ -35)│ target_batch_size: ││ target_batch_size: │ -36)│ 8192 ││ 8192 │ -37)└─────────────┬─────────────┘└─────────────┬─────────────┘ -38)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ -39)│ RepartitionExec ││ RepartitionExec │ -40)│ -------------------- ││ -------------------- │ -41)│ output_partition_count: ││ output_partition_count: │ -42)│ 4 ││ 4 │ -43)│ ││ │ -44)│ partitioning_scheme: ││ partitioning_scheme: │ -45)│ Hash([name@0], 4) ││ Hash([name@0], 4) │ -46)└─────────────┬─────────────┘└─────────────┬─────────────┘ -47)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ -48)│ RepartitionExec ││ RepartitionExec │ -49)│ -------------------- ││ -------------------- │ -50)│ output_partition_count: ││ output_partition_count: │ -51)│ 1 ││ 1 │ -52)│ ││ │ -53)│ partitioning_scheme: ││ partitioning_scheme: │ -54)│ RoundRobinBatch(4) ││ RoundRobinBatch(4) │ -55)└─────────────┬─────────────┘└─────────────┬─────────────┘ -56)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ -57)│ AggregateExec ││ AggregateExec │ -58)│ -------------------- ││ -------------------- │ -59)│ aggr ││ aggr │ -60)│ ││ │ -61)│ group_by: ││ group_by: │ -62)│ name@0 as name ││ name@0 as name │ -63)│ ││ │ -64)│ mode: Partial ││ mode: Partial │ -65)└─────────────┬─────────────┘└─────────────┬─────────────┘ -66)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ -67)│ DataSourceExec ││ DataSourceExec │ -68)│ -------------------- ││ -------------------- │ -69)│ bytes: 1320 ││ bytes: 1312 │ -70)│ format: memory ││ format: memory │ -71)│ rows: 1 ││ rows: 1 │ -72)└───────────────────────────┘└───────────────────────────┘ +10)│ aggr: count(1) │ +11)│ group_by: name │ +12)│ │ +13)│ mode: │ +14)│ SinglePartitioned │ +15)└─────────────┬─────────────┘ +16)┌─────────────┴─────────────┐ +17)│ InterleaveExec ├──────────────┐ +18)└─────────────┬─────────────┘ │ +19)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +20)│ AggregateExec ││ AggregateExec │ +21)│ -------------------- ││ -------------------- │ +22)│ group_by: name ││ group_by: name │ +23)│ ││ │ +24)│ mode: ││ mode: │ +25)│ FinalPartitioned ││ FinalPartitioned │ +26)└─────────────┬─────────────┘└─────────────┬─────────────┘ +27)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +28)│ CoalesceBatchesExec ││ CoalesceBatchesExec │ +29)│ -------------------- ││ -------------------- │ +30)│ target_batch_size: ││ target_batch_size: │ +31)│ 8192 ││ 8192 │ +32)└─────────────┬─────────────┘└─────────────┬─────────────┘ +33)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +34)│ RepartitionExec ││ RepartitionExec │ +35)│ -------------------- ││ -------------------- │ +36)│ output_partition_count: ││ output_partition_count: │ +37)│ 4 ││ 4 │ +38)│ ││ │ +39)│ partitioning_scheme: ││ partitioning_scheme: │ +40)│ Hash([name@0], 4) ││ Hash([name@0], 4) │ +41)└─────────────┬─────────────┘└─────────────┬─────────────┘ +42)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +43)│ RepartitionExec ││ RepartitionExec │ +44)│ -------------------- ││ -------------------- │ +45)│ output_partition_count: ││ output_partition_count: │ +46)│ 1 ││ 1 │ +47)│ ││ │ +48)│ partitioning_scheme: ││ partitioning_scheme: │ +49)│ RoundRobinBatch(4) ││ RoundRobinBatch(4) │ +50)└─────────────┬─────────────┘└─────────────┬─────────────┘ +51)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +52)│ AggregateExec ││ AggregateExec │ +53)│ -------------------- ││ -------------------- │ +54)│ group_by: name ││ group_by: name │ +55)│ mode: Partial ││ mode: Partial │ +56)└─────────────┬─────────────┘└─────────────┬─────────────┘ +57)┌─────────────┴─────────────┐┌─────────────┴─────────────┐ +58)│ DataSourceExec ││ DataSourceExec │ +59)│ -------------------- ││ -------------------- │ +60)│ bytes: 1320 ││ bytes: 1312 │ +61)│ format: memory ││ format: memory │ +62)│ rows: 1 ││ rows: 1 │ +63)└───────────────────────────┘└───────────────────────────┘ # Test explain tree for UnionExec query TT @@ -1975,7 +1963,7 @@ physical_plan 07)┌─────────────┴─────────────┐ 08)│ AggregateExec │ 09)│ -------------------- │ -10)│ aggr: count(Int64(1)) │ +10)│ aggr: count(1) │ 11)│ mode: Final │ 12)└─────────────┬─────────────┘ 13)┌─────────────┴─────────────┐ @@ -1984,7 +1972,7 @@ physical_plan 16)┌─────────────┴─────────────┐ 17)│ AggregateExec │ 18)│ -------------------- │ -19)│ aggr: count(Int64(1)) │ +19)│ aggr: count(1) │ 20)│ mode: Partial │ 21)└─────────────┬─────────────┘ 22)┌─────────────┴─────────────┐