@@ -67,6 +67,8 @@ pub struct AggregateExprBuilder {
6767 alias : Option < String > ,
6868 /// A human readable name
6969 human_display : Option < String > ,
70+ /// Whether `human_display` includes an explicit `... as <alias>` suffix.
71+ human_display_is_aliased : bool ,
7072 /// Arrow Schema for the aggregate function
7173 schema : SchemaRef ,
7274 /// The physical order by expressions
@@ -86,6 +88,7 @@ impl AggregateExprBuilder {
8688 args,
8789 alias : None ,
8890 human_display : None ,
91+ human_display_is_aliased : false ,
8992 schema : Arc :: new ( Schema :: empty ( ) ) ,
9093 order_bys : vec ! [ ] ,
9194 ignore_nulls : false ,
@@ -190,6 +193,7 @@ impl AggregateExprBuilder {
190193 args,
191194 alias,
192195 human_display,
196+ human_display_is_aliased,
193197 schema,
194198 order_bys,
195199 ignore_nulls,
@@ -239,6 +243,7 @@ impl AggregateExprBuilder {
239243 return_field,
240244 name,
241245 human_display,
246+ human_display_is_aliased,
242247 schema : Arc :: unwrap_or_clone ( schema) ,
243248 order_bys,
244249 ignore_nulls,
@@ -261,6 +266,12 @@ impl AggregateExprBuilder {
261266 self
262267 }
263268
269+ #[ doc( hidden) ]
270+ pub fn with_aliased_human_display ( mut self , human_display_is_aliased : bool ) -> Self {
271+ self . human_display_is_aliased = human_display_is_aliased;
272+ self
273+ }
274+
264275 pub fn schema ( mut self , schema : SchemaRef ) -> Self {
265276 self . schema = schema;
266277 self
@@ -317,6 +328,8 @@ pub struct AggregateFunctionExpr {
317328 name : String ,
318329 /// Simplified name for `tree` explain.
319330 human_display : Option < String > ,
331+ /// Whether `human_display` includes an explicit `... as <alias>` suffix.
332+ human_display_is_aliased : bool ,
320333 schema : Schema ,
321334 // The physical order by expressions
322335 order_bys : Vec < PhysicalSortExpr > ,
@@ -352,6 +365,11 @@ impl AggregateFunctionExpr {
352365 self . human_display . as_deref ( )
353366 }
354367
368+ #[ doc( hidden) ]
369+ pub fn has_aliased_human_display ( & self ) -> bool {
370+ self . human_display_is_aliased
371+ }
372+
355373 /// Return if the aggregation is distinct
356374 pub fn is_distinct ( & self ) -> bool {
357375 self . is_distinct
@@ -462,6 +480,7 @@ impl AggregateFunctionExpr {
462480 . order_by ( self . order_bys . clone ( ) )
463481 . schema ( Arc :: new ( self . schema . clone ( ) ) )
464482 . alias ( self . name ( ) . to_string ( ) )
483+ . with_aliased_human_display ( self . human_display_is_aliased )
465484 . with_ignore_nulls ( self . ignore_nulls )
466485 . with_distinct ( self . is_distinct )
467486 . with_reversed ( self . is_reversed ) ;
@@ -586,15 +605,15 @@ impl AggregateFunctionExpr {
586605 ReversedUDAF :: NotSupported => None ,
587606 ReversedUDAF :: Identical => Some ( self . clone ( ) ) ,
588607 ReversedUDAF :: Reversed ( reverse_udf) => {
589- let aliased_human_display = self
590- . human_display ( )
591- . and_then ( |human_display| {
592- strip_alias_suffix ( human_display, self . name ( ) )
593- } )
594- . map ( str:: to_string) ;
595- let was_aliased = aliased_human_display. is_some ( ) ;
608+ let was_aliased = self . has_aliased_human_display ( ) ;
596609 let mut name = self . name ( ) . to_string ( ) ;
597610 let mut human_display = self . human_display ( ) . map ( str:: to_string) ;
611+ // Reversing display follows two paths:
612+ // - aliased display keeps the output `name` unchanged and rewrites only
613+ // the lowered expression in `human_display`, then re-attaches
614+ // `as <alias>`.
615+ // - non-aliased display rewrites the canonical `name`, and rewrites
616+ // `human_display` only when present.
598617 // If the function is changed, we need to reverse order_by clause as well
599618 // i.e. First(a order by b asc null first) -> Last(a order by b desc null last)
600619 if !was_aliased && self . fun ( ) . name ( ) != reverse_udf. name ( ) {
@@ -609,8 +628,19 @@ impl AggregateFunctionExpr {
609628 }
610629
611630 if let Some ( human_display) = human_display. as_mut ( ) {
612- if let Some ( expr_display) = aliased_human_display {
613- * human_display = expr_display;
631+ if was_aliased {
632+ let stripped = match strip_alias_suffix (
633+ human_display,
634+ self . name ( ) ,
635+ ) {
636+ Some ( stripped) => stripped. to_string ( ) ,
637+ None => panic ! (
638+ "invariant violated: aliased aggregate human_display must end with ` as {}`: {}" ,
639+ self . name( ) ,
640+ human_display
641+ ) ,
642+ } ;
643+ * human_display = stripped;
614644 }
615645
616646 if self . fun ( ) . name ( ) != reverse_udf. name ( ) {
@@ -632,6 +662,7 @@ impl AggregateFunctionExpr {
632662 . order_by ( self . order_bys . iter ( ) . map ( |e| e. reverse ( ) ) . collect ( ) )
633663 . schema ( Arc :: new ( self . schema . clone ( ) ) )
634664 . alias ( name)
665+ . with_aliased_human_display ( was_aliased)
635666 . with_ignore_nulls ( self . ignore_nulls )
636667 . with_distinct ( self . is_distinct )
637668 . with_reversed ( !self . is_reversed ) ;
@@ -693,6 +724,7 @@ impl AggregateFunctionExpr {
693724 name : self . name . clone ( ) ,
694725 // TODO: Human name should be updated after re-write to not mislead
695726 human_display : self . human_display . clone ( ) ,
727+ human_display_is_aliased : self . human_display_is_aliased ,
696728 schema : self . schema . clone ( ) ,
697729 order_bys : new_order_bys,
698730 ignore_nulls : self . ignore_nulls ,
0 commit comments