@@ -72,12 +72,20 @@ protected LogicalPlan rule(Aggregate aggregate) {
7272 Map <GroupingFunction , Attribute > groupingAttributes = new HashMap <>();
7373 List <Expression > newGroupings = new ArrayList <>(aggregate .groupings ());
7474 List <NamedExpression > newProjections = new ArrayList <>();
75-
75+ Map < NamedExpression , Attribute > referenceAttributes = new HashMap <>();
7676 boolean groupingChanged = false ;
7777
7878 List <Alias > newEvals = new ArrayList <>();
7979 int [] counter = new int [] { 0 };
8080
81+ // Count DateFormat occurrences to avoid incorrect grouping when replacing multiple DATE_FORMAT with DATE_TRUNC
82+ int [] dateFormatCount = new int [] { 0 };
83+ for (Expression g : newGroupings ) {
84+ if (g instanceof Alias as && as .child () instanceof DateFormat ) {
85+ dateFormatCount [0 ]++;
86+ }
87+ }
88+
8189 // start with the groupings since the aggs might reuse/reference them
8290 for (int i = 0 , s = newGroupings .size (); i < s ; i ++) {
8391 Expression g = newGroupings .get (i );
@@ -95,7 +103,7 @@ protected LogicalPlan rule(Aggregate aggregate) {
95103 // Move the alias into an eval and replace it with its attribute.
96104 groupingChanged = true ;
97105 var attr = as .toAttribute ();
98- if (asChild instanceof DateFormat df ) {
106+ if (asChild instanceof DateFormat df && dateFormatCount [ 0 ] == 1 ) {
99107 // Extract the format pattern and field from DateFormat
100108 Literal format = (Literal ) df .children ().getFirst ();
101109 Expression field = df .children ().get (1 );
@@ -115,7 +123,7 @@ protected LogicalPlan rule(Aggregate aggregate) {
115123 // Create a new eval alias for the optimized expression
116124 Alias newEval = as .replaceChild (expression );
117125 newEvals .add (newEval );
118- newProjections . add ( newEval .toAttribute ());
126+ referenceAttributes . put ( attr , newEval .toAttribute ());
119127 evalNames .put (as .name (), attr );
120128 as = alias ;
121129 }
@@ -178,11 +186,12 @@ protected LogicalPlan rule(Aggregate aggregate) {
178186 if (ref != null ) {
179187 aggsChanged .set (true );
180188 newAggs .add (ref );
189+ newProjections .add (referenceAttributes .getOrDefault (ref , ref .toAttribute ()));
190+ continue ;
181191 }
182- } else {
183- newAggs .add (a );
184- newProjections .add (a .toAttribute ());
185192 }
193+ newAggs .add (a );
194+ newProjections .add (a .toAttribute ());
186195 }
187196
188197 if (evals .size () > 0 ) {
@@ -285,18 +294,21 @@ private static Literal formatToMinimalInterval(String format, Source source) {
285294 return new Literal (source , ChronoUnit .HOURS .getDuration (), DataType .TIME_DURATION );
286295 } else if (formatterAsString .contains (ChronoField .AMPM_OF_DAY .toString ())) {
287296 return new Literal (source , ChronoUnit .HALF_DAYS , DataType .TIME_DURATION );
288- } else if (formatterAsString .contains (ChronoField .DAY_OF_WEEK .toString ())) {
289- return new Literal (source , Period .ofDays (1 ), DataType .DATE_PERIOD );
290- } else if (formatterAsString .contains (ChronoField .ALIGNED_WEEK_OF_MONTH .toString ())) {
291- return new Literal (source , Period .ofDays (7 ), DataType .DATE_PERIOD );
292- } else if (formatterAsString .contains (ChronoField .MONTH_OF_YEAR .toString ())) {
293- return new Literal (source , Period .ofMonths (1 ), DataType .DATE_PERIOD );
294- } else if (formatterAsString .contains (IsoFields .QUARTER_OF_YEAR .toString ())) {
295- return new Literal (source , Period .ofMonths (3 ), DataType .DATE_PERIOD );
296- } else if (formatterAsString .contains (ChronoField .YEAR_OF_ERA .toString ())
297- || formatterAsString .contains (ChronoField .YEAR .toString ())) {
298- return new Literal (source , Period .ofYears (1 ), DataType .DATE_PERIOD );
299- }
297+ } else if (formatterAsString .contains (ChronoField .DAY_OF_WEEK .toString ())
298+ || formatterAsString .contains (ChronoField .DAY_OF_MONTH .toString ())
299+ || formatterAsString .contains (ChronoField .DAY_OF_YEAR .toString ())) {
300+ return new Literal (source , Period .ofDays (1 ), DataType .DATE_PERIOD );
301+ } else if (formatterAsString .contains (ChronoField .ALIGNED_WEEK_OF_MONTH .toString ())
302+ || formatterAsString .contains (ChronoField .ALIGNED_WEEK_OF_YEAR .toString ())) {
303+ return new Literal (source , Period .ofDays (7 ), DataType .DATE_PERIOD );
304+ } else if (formatterAsString .contains (ChronoField .MONTH_OF_YEAR .toString ())) {
305+ return new Literal (source , Period .ofMonths (1 ), DataType .DATE_PERIOD );
306+ } else if (formatterAsString .contains (IsoFields .QUARTER_OF_YEAR .toString ())) {
307+ return new Literal (source , Period .ofMonths (3 ), DataType .DATE_PERIOD );
308+ } else if (formatterAsString .contains (ChronoField .YEAR_OF_ERA .toString ())
309+ || formatterAsString .contains (ChronoField .YEAR .toString ())) {
310+ return new Literal (source , Period .ofYears (1 ), DataType .DATE_PERIOD );
311+ }
300312 } catch (IllegalArgumentException ignored ) {}
301313 return null ;
302314 }
0 commit comments