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

xuyangzhong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/master by this push:
     new 7f707f3ece7 [FLINK-40095][table-planner] Fix JSON aggregation 
functions failing in group window aggregate (#28682)
7f707f3ece7 is described below

commit 7f707f3ece7c0fa5bf8bb44f9db499dc73d932ec
Author: Xuyang <[email protected]>
AuthorDate: Wed Jul 15 11:21:57 2026 +0800

    [FLINK-40095][table-planner] Fix JSON aggregation functions failing in 
group window aggregate (#28682)
---
 .../planner/calcite/RelTimeIndicatorConverter.java |   2 +
 .../table/planner/hint/FlinkHintStrategies.java    |   4 +-
 .../logical/WrapJsonAggFunctionArgumentsRule.java  |  32 +-
 .../nodes/calcite/LogicalWindowAggregate.scala     |  28 +-
 .../plan/nodes/calcite/WindowAggregate.scala       |   3 +-
 .../logical/FlinkLogicalWindowAggregate.scala      |  25 +-
 .../planner/plan/rules/FlinkBatchRuleSets.scala    |   3 +-
 .../planner/plan/rules/FlinkStreamRuleSets.scala   |   3 +-
 .../BuiltInAggregateFunctionTestBase.java          |  29 +-
 .../functions/JsonAggregationFunctionsITCase.java  | 100 +++-
 .../WrapJsonAggFunctionArgumentsRuleTest.java      |  81 ++-
 .../WrapJsonAggFunctionArgumentsRuleTest.xml       | 547 ++++++++++++++++++++-
 .../plan/metadata/FlinkRelMdHandlerTestBase.scala  |   6 +
 .../plan/metadata/FlinkRelMdRowCountTest.scala     |   3 +
 14 files changed, 800 insertions(+), 66 deletions(-)

diff --git 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/calcite/RelTimeIndicatorConverter.java
 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/calcite/RelTimeIndicatorConverter.java
index 8f6ce596c49..86954858c16 100644
--- 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/calcite/RelTimeIndicatorConverter.java
+++ 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/calcite/RelTimeIndicatorConverter.java
@@ -584,6 +584,7 @@ public final class RelTimeIndicatorConverter extends 
RelHomogeneousShuttle {
         return new FlinkLogicalWindowAggregate(
                 agg.getCluster(),
                 agg.getTraitSet(),
+                agg.getHints(),
                 newInput,
                 agg.getGroupSet(),
                 updatedAggCalls,
@@ -596,6 +597,7 @@ public final class RelTimeIndicatorConverter extends 
RelHomogeneousShuttle {
                 new FlinkLogicalWindowAggregate(
                         tableAgg.getCluster(),
                         tableAgg.getTraitSet(),
+                        List.of(),
                         tableAgg.getInput(),
                         tableAgg.getGroupSet(),
                         tableAgg.getAggCallList(),
diff --git 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/hint/FlinkHintStrategies.java
 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/hint/FlinkHintStrategies.java
index 33e6d4346dd..5978f98017d 100644
--- 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/hint/FlinkHintStrategies.java
+++ 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/hint/FlinkHintStrategies.java
@@ -82,7 +82,9 @@ public abstract class FlinkHintStrategies {
                 .hintStrategy(
                         FlinkHints.HINT_NAME_JSON_AGGREGATE_WRAPPED,
                         HintStrategy.builder(HintPredicates.AGGREGATE)
-                                
.excludedRules(WrapJsonAggFunctionArgumentsRule.INSTANCE)
+                                .excludedRules(
+                                        
WrapJsonAggFunctionArgumentsRule.AGGREGATE_INSTANCE,
+                                        
WrapJsonAggFunctionArgumentsRule.WINDOW_AGGREGATE_INSTANCE)
                                 .build())
                 // internal query hint used for alias
                 .hintStrategy(
diff --git 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRule.java
 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRule.java
index 813034adf29..089e542f4c9 100644
--- 
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRule.java
+++ 
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRule.java
@@ -22,12 +22,14 @@ import org.apache.flink.annotation.Internal;
 import org.apache.flink.table.functions.BuiltInFunctionDefinitions;
 import org.apache.flink.table.planner.functions.bridging.BridgingSqlFunction;
 import org.apache.flink.table.planner.hint.FlinkHints;
+import 
org.apache.flink.table.planner.plan.nodes.calcite.LogicalWindowAggregate;
 
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelRule;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.hint.RelHint;
 import org.apache.calcite.rel.logical.LogicalAggregate;
@@ -63,13 +65,19 @@ import static 
org.apache.flink.table.functions.BuiltInFunctionDefinitions.JSON_S
  * their correct representation, and the actual aggregation function's 
implementation can simply
  * insert the values as raw nodes instead. This avoids having to re-implement 
the logic for all
  * supported types in the aggregation function again.
+ *
+ * <p>This rule supports both {@link LogicalAggregate} and {@link 
LogicalWindowAggregate}.
  */
 @Internal
 @Value.Enclosing
 public class WrapJsonAggFunctionArgumentsRule
         extends RelRule<WrapJsonAggFunctionArgumentsRule.Config> {
 
-    public static final RelOptRule INSTANCE = new 
WrapJsonAggFunctionArgumentsRule(Config.DEFAULT);
+    public static final RelOptRule AGGREGATE_INSTANCE =
+            new WrapJsonAggFunctionArgumentsRule(Config.AGGREGATE_DEFAULT);
+
+    public static final RelOptRule WINDOW_AGGREGATE_INSTANCE =
+            new 
WrapJsonAggFunctionArgumentsRule(Config.WINDOW_AGGREGATE_DEFAULT);
 
     /** Marker hint that a call has already been transformed. */
     private static final RelHint MARKER_HINT =
@@ -81,15 +89,15 @@ public class WrapJsonAggFunctionArgumentsRule
 
     @Override
     public void onMatch(RelOptRuleCall call) {
-        final LogicalAggregate aggregate = call.rel(0);
+        final Aggregate aggregate = call.rel(0);
         final RelNode aggInput = aggregate.getInput();
         final RelBuilder relBuilder = call.builder().push(aggInput);
 
-        final LogicalAggregate wrappedAggregate = wrapJsonAggregate(aggregate, 
relBuilder);
+        final Aggregate wrappedAggregate = wrapJsonAggregate(aggregate, 
relBuilder);
         
call.transformTo(wrappedAggregate.withHints(Collections.singletonList(MARKER_HINT)));
     }
 
-    private LogicalAggregate wrapJsonAggregate(LogicalAggregate aggregate, 
RelBuilder relBuilder) {
+    private Aggregate wrapJsonAggregate(Aggregate aggregate, RelBuilder 
relBuilder) {
         final int inputCount = 
aggregate.getInput().getRowType().getFieldCount();
         List<AggregateCall> aggCallList = new 
ArrayList<>(aggregate.getAggCallList());
         // This map is a mapping relationship between jsonObjectAggCall and 
the argument index
@@ -175,19 +183,25 @@ public class WrapJsonAggFunctionArgumentsRule
     /** Configuration for {@link WrapJsonAggFunctionArgumentsRule}. */
     @Value.Immutable(singleton = false)
     public interface Config extends RelRule.Config {
-        Config DEFAULT =
+        Config AGGREGATE_DEFAULT =
+                ImmutableWrapJsonAggFunctionArgumentsRule.Config.builder()
+                        .build()
+                        .as(Config.class)
+                        .onAggregateClass(LogicalAggregate.class);
+
+        Config WINDOW_AGGREGATE_DEFAULT =
                 ImmutableWrapJsonAggFunctionArgumentsRule.Config.builder()
                         .build()
                         .as(Config.class)
-                        .onJsonAggregateFunctions();
+                        .onAggregateClass(LogicalWindowAggregate.class);
 
         @Override
         default RelOptRule toRule() {
             return new WrapJsonAggFunctionArgumentsRule(this);
         }
 
-        default Config onJsonAggregateFunctions() {
-            final Predicate<LogicalAggregate> jsonAggPredicate =
+        default <T extends Aggregate> Config onAggregateClass(Class<T> 
aggregateClass) {
+            final Predicate<T> jsonAggPredicate =
                     aggregate ->
                             aggregate.getAggCallList().stream()
                                     
.anyMatch(WrapJsonAggFunctionArgumentsRule::isJsonAggregation);
@@ -195,7 +209,7 @@ public class WrapJsonAggFunctionArgumentsRule
             final RelRule.OperandTransform aggTransform =
                     operandBuilder ->
                             operandBuilder
-                                    .operand(LogicalAggregate.class)
+                                    .operand(aggregateClass)
                                     .predicate(jsonAggPredicate)
                                     .anyInputs();
 
diff --git 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/calcite/LogicalWindowAggregate.scala
 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/calcite/LogicalWindowAggregate.scala
index 0d9e223b268..16eb483a901 100644
--- 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/calcite/LogicalWindowAggregate.scala
+++ 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/calcite/LogicalWindowAggregate.scala
@@ -24,6 +24,7 @@ import org.apache.calcite.plan.{Convention, RelOptCluster, 
RelTraitSet}
 import org.apache.calcite.rel.RelNode
 import org.apache.calcite.rel.core.{Aggregate, AggregateCall}
 import org.apache.calcite.rel.core.Aggregate.Group
+import org.apache.calcite.rel.hint.RelHint
 import org.apache.calcite.util.ImmutableBitSet
 
 import java.util
@@ -31,12 +32,33 @@ import java.util
 final class LogicalWindowAggregate(
     cluster: RelOptCluster,
     traitSet: RelTraitSet,
+    hints: util.List[RelHint],
     child: RelNode,
     groupSet: ImmutableBitSet,
     aggCalls: util.List[AggregateCall],
     window: LogicalWindow,
     namedProperties: util.List[NamedWindowProperty])
-  extends WindowAggregate(cluster, traitSet, child, groupSet, aggCalls, 
window, namedProperties) {
+  extends WindowAggregate(
+    cluster,
+    traitSet,
+    hints,
+    child,
+    groupSet,
+    aggCalls,
+    window,
+    namedProperties) {
+
+  override def withHints(hintList: util.List[RelHint]): RelNode = {
+    new LogicalWindowAggregate(
+      cluster,
+      traitSet,
+      hintList,
+      input,
+      getGroupSet,
+      aggCalls,
+      window,
+      namedProperties)
+  }
 
   override def copy(
       traitSet: RelTraitSet,
@@ -47,6 +69,7 @@ final class LogicalWindowAggregate(
     new LogicalWindowAggregate(
       cluster,
       traitSet,
+      getHints,
       input,
       groupSet,
       aggCalls,
@@ -58,6 +81,7 @@ final class LogicalWindowAggregate(
     new LogicalWindowAggregate(
       cluster,
       traitSet,
+      getHints,
       input,
       getGroupSet,
       aggCalls,
@@ -76,6 +100,7 @@ final class LogicalWindowAggregate(
     new LogicalWindowAggregate(
       cluster,
       traitSet,
+      getHints,
       input,
       groupSet,
       aggCalls,
@@ -97,6 +122,7 @@ object LogicalWindowAggregate {
     new LogicalWindowAggregate(
       cluster,
       traitSet,
+      agg.getHints,
       agg.getInput,
       agg.getGroupSet,
       agg.getAggCallList,
diff --git 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/calcite/WindowAggregate.scala
 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/calcite/WindowAggregate.scala
index 333d80961d0..fd50f547871 100644
--- 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/calcite/WindowAggregate.scala
+++ 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/calcite/WindowAggregate.scala
@@ -41,6 +41,7 @@ import scala.collection.JavaConverters._
 abstract class WindowAggregate(
     cluster: RelOptCluster,
     traitSet: RelTraitSet,
+    hints: util.List[RelHint],
     child: RelNode,
     groupSet: ImmutableBitSet,
     aggCalls: util.List[AggregateCall],
@@ -49,7 +50,7 @@ abstract class WindowAggregate(
   extends Aggregate(
     cluster,
     traitSet,
-    new util.ArrayList[RelHint],
+    hints,
     child,
     groupSet,
     ImmutableList.of(groupSet),
diff --git 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/logical/FlinkLogicalWindowAggregate.scala
 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/logical/FlinkLogicalWindowAggregate.scala
index c483d660f0a..1f2ddab0806 100644
--- 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/logical/FlinkLogicalWindowAggregate.scala
+++ 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/nodes/logical/FlinkLogicalWindowAggregate.scala
@@ -28,6 +28,7 @@ import org.apache.calcite.rel.convert.ConverterRule
 import org.apache.calcite.rel.convert.ConverterRule.Config
 import org.apache.calcite.rel.core.{Aggregate, AggregateCall}
 import org.apache.calcite.rel.core.Aggregate.Group
+import org.apache.calcite.rel.hint.RelHint
 import org.apache.calcite.rel.metadata.RelMetadataQuery
 import org.apache.calcite.sql.SqlKind
 import org.apache.calcite.util.ImmutableBitSet
@@ -39,12 +40,21 @@ import scala.collection.JavaConverters._
 class FlinkLogicalWindowAggregate(
     cluster: RelOptCluster,
     traitSet: RelTraitSet,
+    hints: util.List[RelHint],
     child: RelNode,
     groupSet: ImmutableBitSet,
     aggCalls: util.List[AggregateCall],
     window: LogicalWindow,
     namedProperties: util.List[NamedWindowProperty])
-  extends WindowAggregate(cluster, traitSet, child, groupSet, aggCalls, 
window, namedProperties)
+  extends WindowAggregate(
+    cluster,
+    traitSet,
+    hints,
+    child,
+    groupSet,
+    aggCalls,
+    window,
+    namedProperties)
   with FlinkLogicalRel {
 
   override def copy(
@@ -56,6 +66,7 @@ class FlinkLogicalWindowAggregate(
     new FlinkLogicalWindowAggregate(
       cluster,
       traitSet,
+      getHints,
       input,
       groupSet,
       aggCalls,
@@ -73,6 +84,17 @@ class FlinkLogicalWindowAggregate(
     planner.getCostFactory.makeCost(rowCnt, cpuCost, rowCnt * rowSize)
   }
 
+  override def withHints(hintList: util.List[RelHint]): RelNode = {
+    new FlinkLogicalWindowAggregate(
+      cluster,
+      traitSet,
+      hintList,
+      input,
+      getGroupSet,
+      aggCalls,
+      window,
+      namedProperties)
+  }
 }
 
 class FlinkLogicalWindowAggregateConverter(config: Config) extends 
ConverterRule(config) {
@@ -100,6 +122,7 @@ class FlinkLogicalWindowAggregateConverter(config: Config) 
extends ConverterRule
     new FlinkLogicalWindowAggregate(
       rel.getCluster,
       traitSet,
+      agg.getHints,
       newInput,
       agg.getGroupSet,
       agg.getAggCallList,
diff --git 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/rules/FlinkBatchRuleSets.scala
 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/rules/FlinkBatchRuleSets.scala
index e840808b5e1..3fb057bc08d 100644
--- 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/rules/FlinkBatchRuleSets.scala
+++ 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/rules/FlinkBatchRuleSets.scala
@@ -139,7 +139,8 @@ object FlinkBatchRuleSets {
         // vector search rule.
         ConstantVectorSearchCallToCorrelateRule.INSTANCE,
         // Wrap arguments for JSON aggregate functions
-        WrapJsonAggFunctionArgumentsRule.INSTANCE,
+        WrapJsonAggFunctionArgumentsRule.AGGREGATE_INSTANCE,
+        WrapJsonAggFunctionArgumentsRule.WINDOW_AGGREGATE_INSTANCE,
         // prune COUNT(*) input to project a constant before aggregation
         PruneCountStarInputRule.INSTANCE
       )).asJava)
diff --git 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/rules/FlinkStreamRuleSets.scala
 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/rules/FlinkStreamRuleSets.scala
index 65e6adbf772..0617400e4ae 100644
--- 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/rules/FlinkStreamRuleSets.scala
+++ 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/plan/rules/FlinkStreamRuleSets.scala
@@ -139,7 +139,8 @@ object FlinkStreamRuleSets {
           // rewrite constant table function scan to correlate
           JoinTableFunctionScanToCorrelateRule.INSTANCE,
           // Wrap arguments for JSON aggregate functions
-          WrapJsonAggFunctionArgumentsRule.INSTANCE,
+          WrapJsonAggFunctionArgumentsRule.AGGREGATE_INSTANCE,
+          WrapJsonAggFunctionArgumentsRule.WINDOW_AGGREGATE_INSTANCE,
           // prune COUNT(*) input to project a constant before aggregation
           PruneCountStarInputRule.INSTANCE
         )
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/BuiltInAggregateFunctionTestBase.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/BuiltInAggregateFunctionTestBase.java
index 4c5f85a057c..f6b748948f4 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/BuiltInAggregateFunctionTestBase.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/BuiltInAggregateFunctionTestBase.java
@@ -106,10 +106,19 @@ abstract class BuiltInAggregateFunctionTestBase {
         testCase.execute(new MiniClusterClient(miniCluster.getConfiguration(), 
miniCluster));
     }
 
-    protected static Table asTable(TableEnvironment tEnv, DataType 
sourceRowType, List<Row> rows) {
+    protected static Table asTable(
+            TableEnvironment tEnv,
+            DataType sourceRowType,
+            List<Row> rows,
+            @Nullable String rowtimeColumn,
+            @Nullable String watermarkExpression) {
+        final Schema.Builder schemaBuilder = 
Schema.newBuilder().fromRowDataType(sourceRowType);
+        if (rowtimeColumn != null) {
+            schemaBuilder.watermark(rowtimeColumn, watermarkExpression);
+        }
         final TableDescriptor descriptor =
                 TableFactoryHarness.newBuilder()
-                        
.schema(Schema.newBuilder().fromRowDataType(sourceRowType).build())
+                        .schema(schemaBuilder.build())
                         .source(asSource(rows, sourceRowType))
                         .build();
 
@@ -204,6 +213,8 @@ abstract class BuiltInAggregateFunctionTestBase {
         private final Configuration configuration = new Configuration();
         private DataType sourceRowType;
         private List<Row> sourceRows;
+        private @Nullable String rowtimeColumn;
+        private @Nullable String watermarkExpression;
 
         private TestSpec(BuiltInFunctionDefinition definition) {
             this.definition = definition;
@@ -238,6 +249,12 @@ abstract class BuiltInAggregateFunctionTestBase {
             return this;
         }
 
+        TestSpec withWatermark(String rowtimeColumn, String 
watermarkExpression) {
+            this.rowtimeColumn = rowtimeColumn;
+            this.watermarkExpression = watermarkExpression;
+            return this;
+        }
+
         TestSpec testSqlResult(
                 Function<Table, String> sqlSpec, DataType expectedRowType, 
List<Row> expectedRows) {
             this.testItems.add(new SqlTestItem(sqlSpec, expectedRowType, 
expectedRows));
@@ -343,7 +360,13 @@ abstract class BuiltInAggregateFunctionTestBase {
                                         .inStreamingMode()
                                         .withConfiguration(configuration)
                                         .build());
-                final Table sourceTable = asTable(tEnv, sourceRowType, 
sourceRows);
+                final Table sourceTable =
+                        asTable(
+                                tEnv,
+                                sourceRowType,
+                                sourceRows,
+                                rowtimeColumn,
+                                watermarkExpression);
 
                 testItem.execute(tEnv, sourceTable, clusterClient);
             };
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/JsonAggregationFunctionsITCase.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/JsonAggregationFunctionsITCase.java
index 34b00813d75..32827b79d5a 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/JsonAggregationFunctionsITCase.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/JsonAggregationFunctionsITCase.java
@@ -22,6 +22,7 @@ import org.apache.flink.table.api.JsonOnNull;
 import org.apache.flink.table.functions.BuiltInFunctionDefinitions;
 import org.apache.flink.types.Row;
 
+import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.stream.Stream;
@@ -29,6 +30,7 @@ import java.util.stream.Stream;
 import static org.apache.flink.table.api.DataTypes.INT;
 import static org.apache.flink.table.api.DataTypes.ROW;
 import static org.apache.flink.table.api.DataTypes.STRING;
+import static org.apache.flink.table.api.DataTypes.TIMESTAMP;
 import static org.apache.flink.table.api.DataTypes.VARCHAR;
 import static org.apache.flink.table.api.Expressions.$;
 import static org.apache.flink.table.api.Expressions.jsonArrayAgg;
@@ -240,6 +242,102 @@ class JsonAggregationFunctionsITCase extends 
BuiltInAggregateFunctionTestBase {
                                 ROW(INT(), STRING(), STRING().notNull()),
                                 Arrays.asList(
                                         Row.of(1, "A", "[\"A\"]"),
-                                        Row.of(2, "D", "[\"C\",\"D\"]"))));
+                                        Row.of(2, "D", "[\"C\",\"D\"]"))),
+
+                // JSON_OBJECTAGG - Window (group window) Aggregation
+                
TestSpec.forFunction(BuiltInFunctionDefinitions.JSON_OBJECTAGG_NULL_ON_NULL)
+                        .withDescription("Window Aggregation")
+                        .withSource(
+                                ROW(STRING(), INT(), TIMESTAMP(3)),
+                                Arrays.asList(
+                                        Row.ofKind(
+                                                INSERT,
+                                                "A",
+                                                1,
+                                                
LocalDateTime.parse("2020-01-01T00:00:01")),
+                                        Row.ofKind(
+                                                INSERT,
+                                                "B",
+                                                2,
+                                                
LocalDateTime.parse("2020-01-01T00:00:02")),
+                                        Row.ofKind(
+                                                INSERT,
+                                                "C",
+                                                3,
+                                                
LocalDateTime.parse("2020-01-01T00:00:06"))))
+                        .withWatermark("f2", "f2 - INTERVAL '1' SECOND")
+                        .testSqlResult(
+                                source ->
+                                        "SELECT JSON_OBJECTAGG(f0 VALUE f1) 
FROM "
+                                                + source
+                                                + " GROUP BY TUMBLE(f2, 
INTERVAL '5' SECOND)",
+                                ROW(VARCHAR(2000).notNull()),
+                                Arrays.asList(Row.of("{\"A\":1,\"B\":2}"), 
Row.of("{\"C\":3}"))),
+                
TestSpec.forFunction(BuiltInFunctionDefinitions.JSON_OBJECTAGG_NULL_ON_NULL)
+                        .withDescription("Window Group Aggregation With Other 
Aggs")
+                        .withSource(
+                                ROW(INT(), STRING(), INT(), TIMESTAMP(3)),
+                                Arrays.asList(
+                                        Row.ofKind(
+                                                INSERT,
+                                                1,
+                                                "A",
+                                                1,
+                                                
LocalDateTime.parse("2020-01-01T00:00:01")),
+                                        Row.ofKind(
+                                                INSERT,
+                                                1,
+                                                "B",
+                                                3,
+                                                
LocalDateTime.parse("2020-01-01T00:00:02")),
+                                        Row.ofKind(
+                                                INSERT,
+                                                2,
+                                                "A",
+                                                2,
+                                                
LocalDateTime.parse("2020-01-01T00:00:06")),
+                                        Row.ofKind(
+                                                INSERT,
+                                                2,
+                                                "C",
+                                                5,
+                                                
LocalDateTime.parse("2020-01-01T00:00:07"))))
+                        .withWatermark("f3", "f3 - INTERVAL '1' SECOND")
+                        .testSqlResult(
+                                source ->
+                                        "SELECT f0, JSON_OBJECTAGG(f1 VALUE 
f2), max(f2) FROM "
+                                                + source
+                                                + " GROUP BY f0, TUMBLE(f3, 
INTERVAL '5' SECOND)",
+                                ROW(INT(), VARCHAR(2000).notNull(), INT()),
+                                Arrays.asList(
+                                        Row.of(1, "{\"A\":1,\"B\":3}", 3),
+                                        Row.of(2, "{\"A\":2,\"C\":5}", 5))),
+
+                // JSON_ARRAYAGG - Window (group window) Aggregation
+                
TestSpec.forFunction(BuiltInFunctionDefinitions.JSON_ARRAYAGG_ABSENT_ON_NULL)
+                        .withDescription("Window Aggregation")
+                        .withSource(
+                                ROW(STRING(), TIMESTAMP(3)),
+                                Arrays.asList(
+                                        Row.ofKind(
+                                                INSERT,
+                                                "A",
+                                                
LocalDateTime.parse("2020-01-01T00:00:01")),
+                                        Row.ofKind(
+                                                INSERT,
+                                                "B",
+                                                
LocalDateTime.parse("2020-01-01T00:00:02")),
+                                        Row.ofKind(
+                                                INSERT,
+                                                "C",
+                                                
LocalDateTime.parse("2020-01-01T00:00:06"))))
+                        .withWatermark("f1", "f1 - INTERVAL '1' SECOND")
+                        .testSqlResult(
+                                source ->
+                                        "SELECT JSON_ARRAYAGG(f0) FROM "
+                                                + source
+                                                + " GROUP BY TUMBLE(f1, 
INTERVAL '5' SECOND)",
+                                ROW(VARCHAR(2000).notNull()),
+                                Arrays.asList(Row.of("[\"A\",\"B\"]"), 
Row.of("[\"C\"]"))));
     }
 }
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.java
index e512d3b26d5..e424cd2aec1 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.java
@@ -24,27 +24,35 @@ import org.apache.flink.table.planner.utils.TableTestUtil;
 import 
org.apache.flink.testutils.junit.extensions.parameterized.ParameterizedTestExtension;
 import org.apache.flink.testutils.junit.extensions.parameterized.Parameters;
 
+import org.junit.jupiter.api.Assumptions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.TestTemplate;
 import org.junit.jupiter.api.extension.ExtendWith;
 
+import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
+import java.util.List;
 
 /** Tests for {@link WrapJsonAggFunctionArgumentsRule}. */
 @ExtendWith(ParameterizedTestExtension.class)
 class WrapJsonAggFunctionArgumentsRuleTest extends TableTestBase {
 
     private final boolean batchMode;
+    private final boolean isGroupWindowAgg;
     private TableTestUtil util;
 
-    @Parameters(name = "batchMode = {0}")
-    private static Collection<Boolean> data() {
-        return Arrays.asList(true, false);
+    @Parameters(name = "batchMode = {0}, isGroupWindowAgg = {1}")
+    private static List<Boolean[]> data() {
+        return Arrays.asList(
+                new Boolean[] {true, false},
+                new Boolean[] {true, true},
+                new Boolean[] {false, false},
+                new Boolean[] {false, true});
     }
 
-    WrapJsonAggFunctionArgumentsRuleTest(boolean batchMode) {
+    WrapJsonAggFunctionArgumentsRuleTest(boolean batchMode, boolean 
isGroupWindowAgg) {
         this.batchMode = batchMode;
+        this.isGroupWindowAgg = isGroupWindowAgg;
     }
 
     @BeforeEach
@@ -67,70 +75,89 @@ class WrapJsonAggFunctionArgumentsRuleTest extends 
TableTestBase {
                                 + batchMode
                                 + "'\n)");
 
-        util.tableEnv()
-                .executeSql(
-                        "CREATE TABLE T1(\n"
-                                + " f0 INTEGER,\n"
-                                + " f1 VARCHAR,\n"
-                                + " f2 BIGINT\n"
-                                + ") WITH (\n"
-                                + " 'connector' = 'values'\n"
-                                + " ,'bounded' = '"
-                                + batchMode
-                                + "'\n)");
+        if (isGroupWindowAgg) {
+            util.tableEnv()
+                    .executeSql(
+                            "ALTER TABLE T add (rt TIMESTAMP(3), WATERMARK FOR 
rt as rt - interval '1' second)");
+        }
     }
 
     @TestTemplate
     void testJsonObjectAgg() {
-        util.verifyRelPlan("SELECT JSON_OBJECTAGG(f1 VALUE f1) FROM T");
+        util.verifyRelPlan(renderAggregateSQL("JSON_OBJECTAGG(f1 VALUE f1)"));
     }
 
     @TestTemplate
     void testJsonObjectAggInGroupWindow() {
-        util.verifyRelPlan("SELECT f0, JSON_OBJECTAGG(f1 VALUE f0) FROM T 
GROUP BY f0");
+        util.verifyRelPlan(renderAggregateSQL("f0, JSON_OBJECTAGG(f1 VALUE 
f0)", "f0"));
     }
 
     @TestTemplate
     void testJsonArrayAgg() {
-        util.verifyRelPlan("SELECT JSON_ARRAYAGG(f0) FROM T");
+        util.verifyRelPlan(renderAggregateSQL("JSON_ARRAYAGG(f0)"));
     }
 
     @TestTemplate
     void testJsonArrayAggInGroupWindow() {
-        util.verifyRelPlan("SELECT f0, JSON_ARRAYAGG(f0) FROM T GROUP BY f0");
+        util.verifyRelPlan(renderAggregateSQL("f0, JSON_ARRAYAGG(f0)", "f0"));
     }
 
     @TestTemplate
     void testJsonObjectAggWithOtherAggs() {
-        util.verifyRelPlan("SELECT COUNT(*), JSON_OBJECTAGG(f1 VALUE f1) FROM 
T");
+        util.verifyRelPlan(renderAggregateSQL("COUNT(*), JSON_OBJECTAGG(f1 
VALUE f1)"));
     }
 
     @TestTemplate
     void testGroupJsonObjectAggWithOtherAggs() {
         util.verifyRelPlan(
-                "SELECT f0, COUNT(*), JSON_OBJECTAGG(f1 VALUE f0), SUM(f2) 
FROM T GROUP BY f0");
+                renderAggregateSQL("f0, COUNT(*), JSON_OBJECTAGG(f1 VALUE f0), 
SUM(f2)", "f0"));
     }
 
     @TestTemplate
     void testJsonArrayAggWithOtherAggs() {
-        util.verifyRelPlan("SELECT COUNT(*), JSON_ARRAYAGG(f0) FROM T");
+        util.verifyRelPlan(renderAggregateSQL("COUNT(*), JSON_ARRAYAGG(f0)"));
     }
 
     @TestTemplate
     void testGroupJsonArrayAggInWithOtherAggs() {
-        util.verifyRelPlan("SELECT f0, COUNT(*), JSON_ARRAYAGG(f0), SUM(f2) 
FROM T GROUP BY f0");
+        util.verifyRelPlan(renderAggregateSQL("f0, COUNT(*), 
JSON_ARRAYAGG(f0), SUM(f2)", "f0"));
     }
 
     @TestTemplate
     void testJsonArrayAggAndJsonObjectAggWithOtherAggs() {
         util.verifyRelPlan(
-                "SELECT MAX(f0), JSON_OBJECTAGG(f1 VALUE f0), 
JSON_ARRAYAGG(f1), JSON_ARRAYAGG(f0) FROM T");
+                renderAggregateSQL(
+                        "MAX(f0), JSON_OBJECTAGG(f1 VALUE f0), 
JSON_ARRAYAGG(f1), JSON_ARRAYAGG(f0)"));
     }
 
     @TestTemplate
     void testGroupJsonArrayAggAndJsonObjectAggWithOtherAggs() {
         util.verifyRelPlan(
-                "SELECT f0, JSON_OBJECTAGG(f1 VALUE f2), JSON_ARRAYAGG(f1), 
JSON_ARRAYAGG(f2),"
-                        + " SUM(f2) FROM T GROUP BY f0");
+                renderAggregateSQL(
+                        "f0, JSON_OBJECTAGG(f1 VALUE f2), JSON_ARRAYAGG(f1), 
JSON_ARRAYAGG(f2),"
+                                + " SUM(f2)",
+                        "f0"));
+    }
+
+    @TestTemplate
+    void testJsonObjectAggWithWindowTVF() {
+        Assumptions.assumeTrue(isGroupWindowAgg);
+        util.verifyRelPlan(
+                "SELECT JSON_OBJECTAGG(f1 VALUE f1) "
+                        + "FROM TABLE(TUMBLE(TABLE T, DESCRIPTOR(rt), INTERVAL 
'5' SECOND))");
+    }
+
+    private String renderAggregateSQL(String projects, String... groupKeys) {
+        List<String> newGroupKeys = new ArrayList<>(Arrays.asList(groupKeys));
+        if (isGroupWindowAgg) {
+            newGroupKeys.add("TUMBLE(rt, INTERVAL '5' SECOND)");
+        }
+        String groupKeyStr;
+        if (newGroupKeys.isEmpty()) {
+            groupKeyStr = "";
+        } else {
+            groupKeyStr = " GROUP BY " + String.join(",", newGroupKeys);
+        }
+        return String.format("SELECT %s FROM T%s", projects, groupKeyStr);
     }
 }
diff --git 
a/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.xml
 
b/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.xml
index de4f8bb7341..e5480a1e27b 100644
--- 
a/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.xml
+++ 
b/flink-table/flink-table-planner/src/test/resources/org/apache/flink/table/planner/plan/rules/logical/WrapJsonAggFunctionArgumentsRuleTest.xml
@@ -16,7 +16,7 @@ See the License for the specific language governing 
permissions and
 limitations under the License.
 -->
 <Root>
-  <TestCase name="testGroupJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode 
= false]">
+  <TestCase name="testGroupJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode 
= false, isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT f0, JSON_OBJECTAGG(f1 VALUE f2), JSON_ARRAYAGG(f1), 
JSON_ARRAYAGG(f2), SUM(f2) FROM T GROUP BY f0]]>
     </Resource>
@@ -35,7 +35,30 @@ GroupAggregate(groupBy=[f0], select=[f0, 
JSON_OBJECTAGG_NULL_ON_NULL(f1, $f4) AS
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testGroupJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode 
= true]">
+  <TestCase name="testGroupJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode 
= false, isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT f0, JSON_OBJECTAGG(f1 VALUE f2), JSON_ARRAYAGG(f1), 
JSON_ARRAYAGG(f2), SUM(f2) FROM T GROUP BY f0,TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(f0=[$0], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4], EXPR$4=[$5])
++- LogicalAggregate(group=[{0, 1}], EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($2, 
$3)], EXPR$2=[JSON_ARRAYAGG_ABSENT_ON_NULL($2)], 
EXPR$3=[JSON_ARRAYAGG_ABSENT_ON_NULL($3)], EXPR$4=[SUM($3)])
+   +- LogicalProject(f0=[$0], $f1=[$TUMBLE($3, 5000:INTERVAL SECOND)], 
f1=[$1], f2=[$2])
+      +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 1000:INTERVAL 
SECOND)])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupWindowAggregate(groupBy=[f0], window=[TumblingGroupWindow('w$, rt, 
5000)], select=[f0, JSON_OBJECTAGG_NULL_ON_NULL(f1, $f5) AS EXPR$1, 
JSON_ARRAYAGG_ABSENT_ON_NULL($f4) AS EXPR$2, JSON_ARRAYAGG_ABSENT_ON_NULL($f5) 
AS EXPR$3, SUM(f2) AS EXPR$4])
++- Exchange(distribution=[hash[f0]])
+   +- Calc(select=[f0, rt, f1, f2, JSON_STRING(f1) AS $f4, JSON_STRING(f2) AS 
$f5])
+      +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+         +- TableSourceScan(table=[[default_catalog, default_database, T]], 
fields=[f0, f1, f2, rt])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testGroupJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode 
= true, isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT f0, JSON_OBJECTAGG(f1 VALUE f2), JSON_ARRAYAGG(f1), 
JSON_ARRAYAGG(f2), SUM(f2) FROM T GROUP BY f0]]>
     </Resource>
@@ -55,7 +78,29 @@ SortAggregate(isMerge=[false], groupBy=[f0], select=[f0, 
JSON_OBJECTAGG_NULL_ON_
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testGroupJsonArrayAggInWithOtherAggs[batchMode = false]">
+  <TestCase name="testGroupJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode 
= true, isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT f0, JSON_OBJECTAGG(f1 VALUE f2), JSON_ARRAYAGG(f1), 
JSON_ARRAYAGG(f2), SUM(f2) FROM T GROUP BY f0,TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(f0=[$0], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4], EXPR$4=[$5])
++- LogicalAggregate(group=[{0, 1}], EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($2, 
$3)], EXPR$2=[JSON_ARRAYAGG_ABSENT_ON_NULL($2)], 
EXPR$3=[JSON_ARRAYAGG_ABSENT_ON_NULL($3)], EXPR$4=[SUM($3)])
+   +- LogicalProject(f0=[$0], $f1=[$TUMBLE($3, 5000:INTERVAL SECOND)], 
f1=[$1], f2=[$2])
+      +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortWindowAggregate(groupBy=[f0], window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[f0, JSON_OBJECTAGG_NULL_ON_NULL(f1, $f5) AS EXPR$1, 
JSON_ARRAYAGG_ABSENT_ON_NULL($f4) AS EXPR$2, JSON_ARRAYAGG_ABSENT_ON_NULL($f5) 
AS EXPR$3, SUM(f2) AS EXPR$4])
++- Calc(select=[f0, rt, f1, f2, JSON_STRING(f1) AS $f4, JSON_STRING(f2) AS 
$f5])
+   +- Sort(orderBy=[f0 ASC, rt ASC])
+      +- Exchange(distribution=[hash[f0]])
+         +- TableSourceScan(table=[[default_catalog, default_database, T]], 
fields=[f0, f1, f2, rt])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testGroupJsonArrayAggInWithOtherAggs[batchMode = false, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT f0, COUNT(*), JSON_ARRAYAGG(f0), SUM(f2) FROM T GROUP BY 
f0]]>
     </Resource>
@@ -75,7 +120,30 @@ GroupAggregate(groupBy=[f0], select=[f0, COUNT(*) AS 
EXPR$1, JSON_ARRAYAGG_ABSEN
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testGroupJsonArrayAggInWithOtherAggs[batchMode = true]">
+  <TestCase name="testGroupJsonArrayAggInWithOtherAggs[batchMode = false, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT f0, COUNT(*), JSON_ARRAYAGG(f0), SUM(f2) FROM T GROUP BY 
f0,TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(f0=[$0], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4])
++- LogicalAggregate(group=[{0, 1}], EXPR$1=[COUNT()], 
EXPR$2=[JSON_ARRAYAGG_ABSENT_ON_NULL($0)], EXPR$3=[SUM($2)])
+   +- LogicalProject(f0=[$0], $f1=[$TUMBLE($3, 5000:INTERVAL SECOND)], f2=[$2])
+      +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 1000:INTERVAL 
SECOND)])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupWindowAggregate(groupBy=[f0], window=[TumblingGroupWindow('w$, rt, 
5000)], select=[f0, COUNT(*) AS EXPR$1, JSON_ARRAYAGG_ABSENT_ON_NULL($f3) AS 
EXPR$2, SUM(f2) AS EXPR$3])
++- Exchange(distribution=[hash[f0]])
+   +- Calc(select=[f0, rt, f2, JSON_STRING(f0) AS $f3])
+      +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[f0, rt, f2], metadata=[]]], fields=[f0, rt, f2])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testGroupJsonArrayAggInWithOtherAggs[batchMode = true, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT f0, COUNT(*), JSON_ARRAYAGG(f0), SUM(f2) FROM T GROUP BY 
f0]]>
     </Resource>
@@ -96,7 +164,29 @@ SortAggregate(isMerge=[false], groupBy=[f0], select=[f0, 
COUNT(*) AS EXPR$1, JSO
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testGroupJsonObjectAggWithOtherAggs[batchMode = false]">
+  <TestCase name="testGroupJsonArrayAggInWithOtherAggs[batchMode = true, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT f0, COUNT(*), JSON_ARRAYAGG(f0), SUM(f2) FROM T GROUP BY 
f0,TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(f0=[$0], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4])
++- LogicalAggregate(group=[{0, 1}], EXPR$1=[COUNT()], 
EXPR$2=[JSON_ARRAYAGG_ABSENT_ON_NULL($0)], EXPR$3=[SUM($2)])
+   +- LogicalProject(f0=[$0], $f1=[$TUMBLE($3, 5000:INTERVAL SECOND)], f2=[$2])
+      +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortWindowAggregate(groupBy=[f0], window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[f0, COUNT(*) AS EXPR$1, JSON_ARRAYAGG_ABSENT_ON_NULL($f3) AS EXPR$2, 
SUM(f2) AS EXPR$3])
++- Calc(select=[f0, rt, f2, JSON_STRING(f0) AS $f3])
+   +- Sort(orderBy=[f0 ASC, rt ASC])
+      +- Exchange(distribution=[hash[f0]])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[f0, rt, f2], metadata=[]]], fields=[f0, rt, f2])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testGroupJsonObjectAggWithOtherAggs[batchMode = false, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT f0, COUNT(*), JSON_OBJECTAGG(f1 VALUE f0), SUM(f2) FROM 
T GROUP BY f0]]>
     </Resource>
@@ -115,7 +205,30 @@ GroupAggregate(groupBy=[f0], select=[f0, COUNT(*) AS 
EXPR$1, JSON_OBJECTAGG_NULL
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testGroupJsonObjectAggWithOtherAggs[batchMode = true]">
+  <TestCase name="testGroupJsonObjectAggWithOtherAggs[batchMode = false, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT f0, COUNT(*), JSON_OBJECTAGG(f1 VALUE f0), SUM(f2) FROM 
T GROUP BY f0,TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(f0=[$0], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4])
++- LogicalAggregate(group=[{0, 1}], EXPR$1=[COUNT()], 
EXPR$2=[JSON_OBJECTAGG_NULL_ON_NULL($2, $0)], EXPR$3=[SUM($3)])
+   +- LogicalProject(f0=[$0], $f1=[$TUMBLE($3, 5000:INTERVAL SECOND)], 
f1=[$1], f2=[$2])
+      +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 1000:INTERVAL 
SECOND)])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupWindowAggregate(groupBy=[f0], window=[TumblingGroupWindow('w$, rt, 
5000)], select=[f0, COUNT(*) AS EXPR$1, JSON_OBJECTAGG_NULL_ON_NULL(f1, $f4) AS 
EXPR$2, SUM(f2) AS EXPR$3])
++- Exchange(distribution=[hash[f0]])
+   +- Calc(select=[f0, rt, f1, f2, JSON_STRING(f0) AS $f4])
+      +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+         +- TableSourceScan(table=[[default_catalog, default_database, T]], 
fields=[f0, f1, f2, rt])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testGroupJsonObjectAggWithOtherAggs[batchMode = true, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT f0, COUNT(*), JSON_OBJECTAGG(f1 VALUE f0), SUM(f2) FROM 
T GROUP BY f0]]>
     </Resource>
@@ -137,7 +250,31 @@ SortAggregate(isMerge=[true], groupBy=[f0], select=[f0, 
Final_COUNT(count1$0) AS
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode = 
false]">
+  <TestCase name="testGroupJsonObjectAggWithOtherAggs[batchMode = true, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT f0, COUNT(*), JSON_OBJECTAGG(f1 VALUE f0), SUM(f2) FROM 
T GROUP BY f0,TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(f0=[$0], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4])
++- LogicalAggregate(group=[{0, 1}], EXPR$1=[COUNT()], 
EXPR$2=[JSON_OBJECTAGG_NULL_ON_NULL($2, $0)], EXPR$3=[SUM($3)])
+   +- LogicalProject(f0=[$0], $f1=[$TUMBLE($3, 5000:INTERVAL SECOND)], 
f1=[$1], f2=[$2])
+      +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortWindowAggregate(groupBy=[f0], window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[f0, Final_COUNT(count1$0) AS EXPR$1, 
Final_JSON_OBJECTAGG_NULL_ON_NULL(EXPR$2) AS EXPR$2, Final_SUM(sum$1) AS 
EXPR$3])
++- Sort(orderBy=[f0 ASC, assignedWindow$ ASC])
+   +- Exchange(distribution=[hash[f0]])
+      +- LocalSortWindowAggregate(groupBy=[f0], 
window=[TumblingGroupWindow('w$, rt, 5000)], select=[f0, Partial_COUNT(*) AS 
count1$0, Partial_JSON_OBJECTAGG_NULL_ON_NULL(f1, $f4) AS EXPR$2, 
Partial_SUM(f2) AS sum$1])
+         +- Calc(select=[f0, rt, f1, f2, JSON_STRING(f0) AS $f4])
+            +- Sort(orderBy=[f0 ASC, rt ASC])
+               +- TableSourceScan(table=[[default_catalog, default_database, 
T]], fields=[f0, f1, f2, rt])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode = 
false, isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT MAX(f0), JSON_OBJECTAGG(f1 VALUE f0), JSON_ARRAYAGG(f1), 
JSON_ARRAYAGG(f0) FROM T]]>
     </Resource>
@@ -157,7 +294,30 @@ GroupAggregate(select=[MAX(f0) AS EXPR$0, 
JSON_OBJECTAGG_NULL_ON_NULL(f1, $f2) A
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode = 
true]">
+  <TestCase name="testJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode = 
false, isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT MAX(f0), JSON_OBJECTAGG(f1 VALUE f0), JSON_ARRAYAGG(f1), 
JSON_ARRAYAGG(f0) FROM T GROUP BY TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4])
++- LogicalAggregate(group=[{0}], EXPR$0=[MAX($1)], 
EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($2, $1)], 
EXPR$2=[JSON_ARRAYAGG_ABSENT_ON_NULL($2)], 
EXPR$3=[JSON_ARRAYAGG_ABSENT_ON_NULL($1)])
+   +- LogicalProject($f0=[$TUMBLE($3, 5000:INTERVAL SECOND)], f0=[$0], f1=[$1])
+      +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 1000:INTERVAL 
SECOND)])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[MAX(f0) AS EXPR$0, JSON_OBJECTAGG_NULL_ON_NULL(f1, $f3) AS EXPR$1, 
JSON_ARRAYAGG_ABSENT_ON_NULL($f4) AS EXPR$2, JSON_ARRAYAGG_ABSENT_ON_NULL($f3) 
AS EXPR$3])
++- Exchange(distribution=[single])
+   +- Calc(select=[rt, f0, f1, JSON_STRING(f0) AS $f3, JSON_STRING(f1) AS $f4])
+      +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[rt, f0, f1], metadata=[]]], fields=[rt, f0, f1])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode = 
true, isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT MAX(f0), JSON_OBJECTAGG(f1 VALUE f0), JSON_ARRAYAGG(f1), 
JSON_ARRAYAGG(f0) FROM T]]>
     </Resource>
@@ -177,7 +337,29 @@ SortAggregate(isMerge=[false], select=[MAX(f0) AS EXPR$0, 
JSON_OBJECTAGG_NULL_ON
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonArrayAggInGroupWindow[batchMode = false]">
+  <TestCase name="testJsonArrayAggAndJsonObjectAggWithOtherAggs[batchMode = 
true, isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT MAX(f0), JSON_OBJECTAGG(f1 VALUE f0), JSON_ARRAYAGG(f1), 
JSON_ARRAYAGG(f0) FROM T GROUP BY TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1], EXPR$1=[$2], EXPR$2=[$3], EXPR$3=[$4])
++- LogicalAggregate(group=[{0}], EXPR$0=[MAX($1)], 
EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($2, $1)], 
EXPR$2=[JSON_ARRAYAGG_ABSENT_ON_NULL($2)], 
EXPR$3=[JSON_ARRAYAGG_ABSENT_ON_NULL($1)])
+   +- LogicalProject($f0=[$TUMBLE($3, 5000:INTERVAL SECOND)], f0=[$0], f1=[$1])
+      +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[MAX(f0) AS EXPR$0, JSON_OBJECTAGG_NULL_ON_NULL(f1, $f3) AS EXPR$1, 
JSON_ARRAYAGG_ABSENT_ON_NULL($f4) AS EXPR$2, JSON_ARRAYAGG_ABSENT_ON_NULL($f3) 
AS EXPR$3])
++- Calc(select=[rt, f0, f1, JSON_STRING(f0) AS $f3, JSON_STRING(f1) AS $f4])
+   +- Sort(orderBy=[rt ASC])
+      +- Exchange(distribution=[single])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[rt, f0, f1], metadata=[]]], fields=[rt, f0, f1])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonArrayAggInGroupWindow[batchMode = false, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT f0, JSON_ARRAYAGG(f0) FROM T GROUP BY f0]]>
     </Resource>
@@ -197,7 +379,30 @@ GroupAggregate(groupBy=[f0], select=[f0, 
JSON_ARRAYAGG_ABSENT_ON_NULL($f1) AS EX
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonArrayAggInGroupWindow[batchMode = true]">
+  <TestCase name="testJsonArrayAggInGroupWindow[batchMode = false, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT f0, JSON_ARRAYAGG(f0) FROM T GROUP BY f0,TUMBLE(rt, 
INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(f0=[$0], EXPR$1=[$2])
++- LogicalAggregate(group=[{0, 1}], EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($0)])
+   +- LogicalProject(f0=[$0], $f1=[$TUMBLE($3, 5000:INTERVAL SECOND)])
+      +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 1000:INTERVAL 
SECOND)])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupWindowAggregate(groupBy=[f0], window=[TumblingGroupWindow('w$, rt, 
5000)], select=[f0, JSON_ARRAYAGG_ABSENT_ON_NULL($f2) AS EXPR$1])
++- Exchange(distribution=[hash[f0]])
+   +- Calc(select=[f0, rt, JSON_STRING(f0) AS $f2])
+      +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[f0, rt], metadata=[]]], fields=[f0, rt])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonArrayAggInGroupWindow[batchMode = true, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT f0, JSON_ARRAYAGG(f0) FROM T GROUP BY f0]]>
     </Resource>
@@ -218,7 +423,29 @@ SortAggregate(isMerge=[false], groupBy=[f0], select=[f0, 
JSON_ARRAYAGG_ABSENT_ON
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonArrayAggWithOtherAggs[batchMode = false]">
+  <TestCase name="testJsonArrayAggInGroupWindow[batchMode = true, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT f0, JSON_ARRAYAGG(f0) FROM T GROUP BY f0,TUMBLE(rt, 
INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(f0=[$0], EXPR$1=[$2])
++- LogicalAggregate(group=[{0, 1}], EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($0)])
+   +- LogicalProject(f0=[$0], $f1=[$TUMBLE($3, 5000:INTERVAL SECOND)])
+      +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortWindowAggregate(groupBy=[f0], window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[f0, JSON_ARRAYAGG_ABSENT_ON_NULL($f2) AS EXPR$1])
++- Calc(select=[f0, rt, JSON_STRING(f0) AS $f2])
+   +- Sort(orderBy=[f0 ASC, rt ASC])
+      +- Exchange(distribution=[hash[f0]])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[f0, rt], metadata=[]]], fields=[f0, rt])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonArrayAggWithOtherAggs[batchMode = false, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT COUNT(*), JSON_ARRAYAGG(f0) FROM T]]>
     </Resource>
@@ -238,7 +465,30 @@ GroupAggregate(select=[COUNT(*) AS EXPR$0, 
JSON_ARRAYAGG_ABSENT_ON_NULL($f1) AS
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonArrayAggWithOtherAggs[batchMode = true]">
+  <TestCase name="testJsonArrayAggWithOtherAggs[batchMode = false, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT COUNT(*), JSON_ARRAYAGG(f0) FROM T GROUP BY TUMBLE(rt, 
INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1], EXPR$1=[$2])
++- LogicalAggregate(group=[{0}], EXPR$0=[COUNT()], 
EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($1)])
+   +- LogicalProject($f0=[$TUMBLE($3, 5000:INTERVAL SECOND)], f0=[$0])
+      +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 1000:INTERVAL 
SECOND)])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[COUNT(*) AS EXPR$0, JSON_ARRAYAGG_ABSENT_ON_NULL($f2) AS EXPR$1])
++- Exchange(distribution=[single])
+   +- Calc(select=[rt, f0, JSON_STRING(f0) AS $f2])
+      +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[rt, f0], metadata=[]]], fields=[rt, f0])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonArrayAggWithOtherAggs[batchMode = true, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT COUNT(*), JSON_ARRAYAGG(f0) FROM T]]>
     </Resource>
@@ -258,7 +508,29 @@ SortAggregate(isMerge=[false], select=[COUNT(*) AS EXPR$0, 
JSON_ARRAYAGG_ABSENT_
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonArrayAgg[batchMode = false]">
+  <TestCase name="testJsonArrayAggWithOtherAggs[batchMode = true, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT COUNT(*), JSON_ARRAYAGG(f0) FROM T GROUP BY TUMBLE(rt, 
INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1], EXPR$1=[$2])
++- LogicalAggregate(group=[{0}], EXPR$0=[COUNT()], 
EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($1)])
+   +- LogicalProject($f0=[$TUMBLE($3, 5000:INTERVAL SECOND)], f0=[$0])
+      +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[COUNT(*) AS EXPR$0, JSON_ARRAYAGG_ABSENT_ON_NULL($f2) AS EXPR$1])
++- Calc(select=[rt, f0, JSON_STRING(f0) AS $f2])
+   +- Sort(orderBy=[rt ASC])
+      +- Exchange(distribution=[single])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[rt, f0], metadata=[]]], fields=[rt, f0])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonArrayAgg[batchMode = false, isGroupWindowAgg = 
false]">
     <Resource name="sql">
       <![CDATA[SELECT JSON_ARRAYAGG(f0) FROM T]]>
     </Resource>
@@ -278,7 +550,30 @@ GroupAggregate(select=[JSON_ARRAYAGG_ABSENT_ON_NULL($f1) 
AS EXPR$0])
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonArrayAgg[batchMode = true]">
+  <TestCase name="testJsonArrayAgg[batchMode = false, isGroupWindowAgg = 
true]">
+    <Resource name="sql">
+      <![CDATA[SELECT JSON_ARRAYAGG(f0) FROM T GROUP BY TUMBLE(rt, INTERVAL 
'5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1])
++- LogicalAggregate(group=[{0}], EXPR$0=[JSON_ARRAYAGG_ABSENT_ON_NULL($1)])
+   +- LogicalProject($f0=[$TUMBLE($3, 5000:INTERVAL SECOND)], f0=[$0])
+      +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 1000:INTERVAL 
SECOND)])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[JSON_ARRAYAGG_ABSENT_ON_NULL($f2) AS EXPR$0])
++- Exchange(distribution=[single])
+   +- Calc(select=[rt, f0, JSON_STRING(f0) AS $f2])
+      +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[rt, f0], metadata=[]]], fields=[rt, f0])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonArrayAgg[batchMode = true, isGroupWindowAgg = 
false]">
     <Resource name="sql">
       <![CDATA[SELECT JSON_ARRAYAGG(f0) FROM T]]>
     </Resource>
@@ -298,7 +593,29 @@ SortAggregate(isMerge=[false], 
select=[JSON_ARRAYAGG_ABSENT_ON_NULL($f1) AS EXPR
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonObjectAggInGroupWindow[batchMode = false]">
+  <TestCase name="testJsonArrayAgg[batchMode = true, isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT JSON_ARRAYAGG(f0) FROM T GROUP BY TUMBLE(rt, INTERVAL 
'5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1])
++- LogicalAggregate(group=[{0}], EXPR$0=[JSON_ARRAYAGG_ABSENT_ON_NULL($1)])
+   +- LogicalProject($f0=[$TUMBLE($3, 5000:INTERVAL SECOND)], f0=[$0])
+      +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[JSON_ARRAYAGG_ABSENT_ON_NULL($f2) AS EXPR$0])
++- Calc(select=[rt, f0, JSON_STRING(f0) AS $f2])
+   +- Sort(orderBy=[rt ASC])
+      +- Exchange(distribution=[single])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[rt, f0], metadata=[]]], fields=[rt, f0])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonObjectAggInGroupWindow[batchMode = false, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT f0, JSON_OBJECTAGG(f1 VALUE f0) FROM T GROUP BY f0]]>
     </Resource>
@@ -318,7 +635,30 @@ GroupAggregate(groupBy=[f0], select=[f0, 
JSON_OBJECTAGG_NULL_ON_NULL(f1, $f2) AS
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonObjectAggInGroupWindow[batchMode = true]">
+  <TestCase name="testJsonObjectAggInGroupWindow[batchMode = false, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT f0, JSON_OBJECTAGG(f1 VALUE f0) FROM T GROUP BY 
f0,TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(f0=[$0], EXPR$1=[$2])
++- LogicalAggregate(group=[{0, 1}], EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($2, 
$0)])
+   +- LogicalProject(f0=[$0], $f1=[$TUMBLE($3, 5000:INTERVAL SECOND)], f1=[$1])
+      +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 1000:INTERVAL 
SECOND)])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupWindowAggregate(groupBy=[f0], window=[TumblingGroupWindow('w$, rt, 
5000)], select=[f0, JSON_OBJECTAGG_NULL_ON_NULL(f1, $f3) AS EXPR$1])
++- Exchange(distribution=[hash[f0]])
+   +- Calc(select=[f0, rt, f1, JSON_STRING(f0) AS $f3])
+      +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[f0, rt, f1], metadata=[]]], fields=[f0, rt, f1])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonObjectAggInGroupWindow[batchMode = true, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT f0, JSON_OBJECTAGG(f1 VALUE f0) FROM T GROUP BY f0]]>
     </Resource>
@@ -341,7 +681,31 @@ SortAggregate(isMerge=[true], groupBy=[f0], select=[f0, 
Final_JSON_OBJECTAGG_NUL
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonObjectAggWithOtherAggs[batchMode = false]">
+  <TestCase name="testJsonObjectAggInGroupWindow[batchMode = true, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT f0, JSON_OBJECTAGG(f1 VALUE f0) FROM T GROUP BY 
f0,TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(f0=[$0], EXPR$1=[$2])
++- LogicalAggregate(group=[{0, 1}], EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($2, 
$0)])
+   +- LogicalProject(f0=[$0], $f1=[$TUMBLE($3, 5000:INTERVAL SECOND)], f1=[$1])
+      +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortWindowAggregate(groupBy=[f0], window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[f0, Final_JSON_OBJECTAGG_NULL_ON_NULL(EXPR$1) AS EXPR$1])
++- Sort(orderBy=[f0 ASC, assignedWindow$ ASC])
+   +- Exchange(distribution=[hash[f0]])
+      +- LocalSortWindowAggregate(groupBy=[f0], 
window=[TumblingGroupWindow('w$, rt, 5000)], select=[f0, 
Partial_JSON_OBJECTAGG_NULL_ON_NULL(f1, $f3) AS EXPR$1])
+         +- Calc(select=[f0, rt, f1, JSON_STRING(f0) AS $f3])
+            +- Sort(orderBy=[f0 ASC, rt ASC])
+               +- TableSourceScan(table=[[default_catalog, default_database, 
T, project=[f0, rt, f1], metadata=[]]], fields=[f0, rt, f1])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonObjectAggWithOtherAggs[batchMode = false, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT COUNT(*), JSON_OBJECTAGG(f1 VALUE f1) FROM T]]>
     </Resource>
@@ -361,7 +725,30 @@ GroupAggregate(select=[COUNT(*) AS EXPR$0, 
JSON_OBJECTAGG_NULL_ON_NULL($f1, $f1)
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonObjectAggWithOtherAggs[batchMode = true]">
+  <TestCase name="testJsonObjectAggWithOtherAggs[batchMode = false, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT COUNT(*), JSON_OBJECTAGG(f1 VALUE f1) FROM T GROUP BY 
TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1], EXPR$1=[$2])
++- LogicalAggregate(group=[{0}], EXPR$0=[COUNT()], 
EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($1, $1)])
+   +- LogicalProject($f0=[$TUMBLE($3, 5000:INTERVAL SECOND)], f1=[$1])
+      +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 1000:INTERVAL 
SECOND)])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[COUNT(*) AS EXPR$0, JSON_OBJECTAGG_NULL_ON_NULL($f2, $f2) AS EXPR$1])
++- Exchange(distribution=[single])
+   +- Calc(select=[rt, f1, JSON_STRING(f1) AS $f2])
+      +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[rt, f1], metadata=[]]], fields=[rt, f1])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonObjectAggWithOtherAggs[batchMode = true, 
isGroupWindowAgg = false]">
     <Resource name="sql">
       <![CDATA[SELECT COUNT(*), JSON_OBJECTAGG(f1 VALUE f1) FROM T]]>
     </Resource>
@@ -382,7 +769,80 @@ SortAggregate(isMerge=[true], 
select=[Final_COUNT(count1$0) AS EXPR$0, Final_JSO
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonObjectAgg[batchMode = false]">
+  <TestCase name="testJsonObjectAggWithOtherAggs[batchMode = true, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT COUNT(*), JSON_OBJECTAGG(f1 VALUE f1) FROM T GROUP BY 
TUMBLE(rt, INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1], EXPR$1=[$2])
++- LogicalAggregate(group=[{0}], EXPR$0=[COUNT()], 
EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($1, $1)])
+   +- LogicalProject($f0=[$TUMBLE($3, 5000:INTERVAL SECOND)], f1=[$1])
+      +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[Final_COUNT(count1$0) AS EXPR$0, 
Final_JSON_OBJECTAGG_NULL_ON_NULL(EXPR$1) AS EXPR$1])
++- Sort(orderBy=[assignedWindow$ ASC])
+   +- Exchange(distribution=[single])
+      +- LocalSortWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[Partial_COUNT(*) AS count1$0, Partial_JSON_OBJECTAGG_NULL_ON_NULL($f2, 
$f2) AS EXPR$1])
+         +- Calc(select=[rt, f1, JSON_STRING(f1) AS $f2])
+            +- Sort(orderBy=[rt ASC])
+               +- TableSourceScan(table=[[default_catalog, default_database, 
T, project=[rt, f1], metadata=[]]], fields=[rt, f1])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonObjectAggWithWindowTVF[batchMode = false, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT JSON_OBJECTAGG(f1 VALUE f1) FROM TABLE(TUMBLE(TABLE T, 
DESCRIPTOR(rt), INTERVAL '5' SECOND))]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalAggregate(group=[{}], EXPR$0=[JSON_OBJECTAGG_NULL_ON_NULL($0, $0)])
++- LogicalProject(f1=[$1])
+   +- LogicalTableFunctionScan(invocation=[TUMBLE(TABLE(#0), 
DESCRIPTOR(_UTF-16LE'rt'), 5000:INTERVAL SECOND)], rowType=[RecordType(INTEGER 
f0, VARCHAR(2147483647) f1, BIGINT f2, TIMESTAMP(3) *ROWTIME* rt, TIMESTAMP(3) 
window_start, TIMESTAMP(3) window_end, TIMESTAMP(3) *ROWTIME* window_time)])
+      +- LogicalProject(f0=[$0], f1=[$1], f2=[$2], rt=[$3])
+         +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 
1000:INTERVAL SECOND)])
+            +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupAggregate(select=[JSON_OBJECTAGG_NULL_ON_NULL($f1, $f1) AS EXPR$0])
++- Exchange(distribution=[single])
+   +- Calc(select=[f1, JSON_STRING(f1) AS $f1])
+      +- WindowTableFunction(window=[TUMBLE(time_col=[rt], size=[5 s])])
+         +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+            +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[f1, rt], metadata=[]]], fields=[f1, rt])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonObjectAggWithWindowTVF[batchMode = true, 
isGroupWindowAgg = true]">
+    <Resource name="sql">
+      <![CDATA[SELECT JSON_OBJECTAGG(f1 VALUE f1) FROM TABLE(TUMBLE(TABLE T, 
DESCRIPTOR(rt), INTERVAL '5' SECOND))]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalAggregate(group=[{}], EXPR$0=[JSON_OBJECTAGG_NULL_ON_NULL($0, $0)])
++- LogicalProject(f1=[$1])
+   +- LogicalTableFunctionScan(invocation=[TUMBLE(TABLE(#0), 
DESCRIPTOR(_UTF-16LE'rt'), 5000:INTERVAL SECOND)], rowType=[RecordType(INTEGER 
f0, VARCHAR(2147483647) f1, BIGINT f2, TIMESTAMP(3) rt, TIMESTAMP(3) 
window_start, TIMESTAMP(3) window_end, TIMESTAMP(3) window_time)])
+      +- LogicalProject(f0=[$0], f1=[$1], f2=[$2], rt=[$3])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortAggregate(isMerge=[true], 
select=[Final_JSON_OBJECTAGG_NULL_ON_NULL(EXPR$0) AS EXPR$0])
++- Exchange(distribution=[single])
+   +- LocalSortAggregate(select=[Partial_JSON_OBJECTAGG_NULL_ON_NULL($f1, $f1) 
AS EXPR$0])
+      +- Calc(select=[f1, JSON_STRING(f1) AS $f1])
+         +- WindowTableFunction(window=[TUMBLE(time_col=[rt], size=[5 s])])
+            +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[f1, rt], metadata=[]]], fields=[f1, rt])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonObjectAgg[batchMode = false, isGroupWindowAgg = 
false]">
     <Resource name="sql">
       <![CDATA[SELECT JSON_OBJECTAGG(f1 VALUE f1) FROM T]]>
     </Resource>
@@ -402,7 +862,30 @@ GroupAggregate(select=[JSON_OBJECTAGG_NULL_ON_NULL($f1, 
$f1) AS EXPR$0])
 ]]>
     </Resource>
   </TestCase>
-  <TestCase name="testJsonObjectAgg[batchMode = true]">
+  <TestCase name="testJsonObjectAgg[batchMode = false, isGroupWindowAgg = 
true]">
+    <Resource name="sql">
+      <![CDATA[SELECT JSON_OBJECTAGG(f1 VALUE f1) FROM T GROUP BY TUMBLE(rt, 
INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1])
++- LogicalAggregate(group=[{0}], EXPR$0=[JSON_OBJECTAGG_NULL_ON_NULL($1, $1)])
+   +- LogicalProject($f0=[$TUMBLE($3, 5000:INTERVAL SECOND)], f1=[$1])
+      +- LogicalWatermarkAssigner(rowtime=[rt], watermark=[-($3, 1000:INTERVAL 
SECOND)])
+         +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+GroupWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[JSON_OBJECTAGG_NULL_ON_NULL($f2, $f2) AS EXPR$0])
++- Exchange(distribution=[single])
+   +- Calc(select=[rt, f1, JSON_STRING(f1) AS $f2])
+      +- WatermarkAssigner(rowtime=[rt], watermark=[-(rt, 1000:INTERVAL 
SECOND)])
+         +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[rt, f1], metadata=[]]], fields=[rt, f1])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonObjectAgg[batchMode = true, isGroupWindowAgg = 
false]">
     <Resource name="sql">
       <![CDATA[SELECT JSON_OBJECTAGG(f1 VALUE f1) FROM T]]>
     </Resource>
@@ -420,6 +903,30 @@ SortAggregate(isMerge=[true], 
select=[Final_JSON_OBJECTAGG_NULL_ON_NULL(EXPR$0)
    +- LocalSortAggregate(select=[Partial_JSON_OBJECTAGG_NULL_ON_NULL($f1, $f1) 
AS EXPR$0])
       +- Calc(select=[f1, JSON_STRING(f1) AS $f1])
          +- TableSourceScan(table=[[default_catalog, default_database, T, 
project=[f1], metadata=[]]], fields=[f1])
+]]>
+    </Resource>
+  </TestCase>
+  <TestCase name="testJsonObjectAgg[batchMode = true, isGroupWindowAgg = 
true]">
+    <Resource name="sql">
+      <![CDATA[SELECT JSON_OBJECTAGG(f1 VALUE f1) FROM T GROUP BY TUMBLE(rt, 
INTERVAL '5' SECOND)]]>
+    </Resource>
+    <Resource name="ast">
+      <![CDATA[
+LogicalProject(EXPR$0=[$1])
++- LogicalAggregate(group=[{0}], EXPR$0=[JSON_OBJECTAGG_NULL_ON_NULL($1, $1)])
+   +- LogicalProject($f0=[$TUMBLE($3, 5000:INTERVAL SECOND)], f1=[$1])
+      +- LogicalTableScan(table=[[default_catalog, default_database, T]])
+]]>
+    </Resource>
+    <Resource name="optimized rel plan">
+      <![CDATA[
+SortWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[Final_JSON_OBJECTAGG_NULL_ON_NULL(EXPR$0) AS EXPR$0])
++- Sort(orderBy=[assignedWindow$ ASC])
+   +- Exchange(distribution=[single])
+      +- LocalSortWindowAggregate(window=[TumblingGroupWindow('w$, rt, 5000)], 
select=[Partial_JSON_OBJECTAGG_NULL_ON_NULL($f2, $f2) AS EXPR$0])
+         +- Calc(select=[rt, f1, JSON_STRING(f1) AS $f2])
+            +- Sort(orderBy=[rt ASC])
+               +- TableSourceScan(table=[[default_catalog, default_database, 
T, project=[rt, f1], metadata=[]]], fields=[rt, f1])
 ]]>
     </Resource>
   </TestCase>
diff --git 
a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/metadata/FlinkRelMdHandlerTestBase.scala
 
b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/metadata/FlinkRelMdHandlerTestBase.scala
index d210e06b43f..0837f65b3e2 100644
--- 
a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/metadata/FlinkRelMdHandlerTestBase.scala
+++ 
b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/metadata/FlinkRelMdHandlerTestBase.scala
@@ -1834,6 +1834,7 @@ class FlinkRelMdHandlerTestBase {
     val logicalWindowAgg = new LogicalWindowAggregate(
       ts.getCluster,
       ts.getTraitSet,
+      util.List.of(),
       project,
       ImmutableBitSet.of(0, 1),
       aggCallOfWindowAgg,
@@ -1845,6 +1846,7 @@ class FlinkRelMdHandlerTestBase {
     val flinkLogicalWindowAgg = new FlinkLogicalWindowAggregate(
       ts.getCluster,
       logicalTraits,
+      util.List.of(),
       new FlinkLogicalCalc(ts.getCluster, flinkLogicalTraits, flinkLogicalTs, 
program),
       ImmutableBitSet.of(0, 1),
       aggCallOfWindowAgg,
@@ -2004,6 +2006,7 @@ class FlinkRelMdHandlerTestBase {
     val logicalWindowAgg = new LogicalWindowAggregate(
       ts.getCluster,
       ts.getTraitSet,
+      util.List.of(),
       project,
       ImmutableBitSet.of(1),
       aggCallOfWindowAgg,
@@ -2015,6 +2018,7 @@ class FlinkRelMdHandlerTestBase {
     val flinkLogicalWindowAgg = new FlinkLogicalWindowAggregate(
       ts.getCluster,
       logicalTraits,
+      util.List.of(),
       new FlinkLogicalCalc(ts.getCluster, flinkLogicalTraits, flinkLogicalTs, 
program),
       ImmutableBitSet.of(1),
       aggCallOfWindowAgg,
@@ -2187,6 +2191,7 @@ class FlinkRelMdHandlerTestBase {
     val logicalWindowAggWithAuxGroup = new LogicalWindowAggregate(
       ts.getCluster,
       ts.getTraitSet,
+      util.List.of(),
       project,
       ImmutableBitSet.of(0),
       aggCallOfWindowAgg,
@@ -2198,6 +2203,7 @@ class FlinkRelMdHandlerTestBase {
     val flinkLogicalWindowAggWithAuxGroup = new FlinkLogicalWindowAggregate(
       ts.getCluster,
       logicalTraits,
+      util.List.of(),
       new FlinkLogicalCalc(ts.getCluster, flinkLogicalTraits, flinkLogicalTs, 
program),
       ImmutableBitSet.of(0),
       aggCallOfWindowAgg,
diff --git 
a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/metadata/FlinkRelMdRowCountTest.scala
 
b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/metadata/FlinkRelMdRowCountTest.scala
index ad44ac902db..8985ae6e69c 100644
--- 
a/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/metadata/FlinkRelMdRowCountTest.scala
+++ 
b/flink-table/flink-table-planner/src/test/scala/org/apache/flink/table/planner/plan/metadata/FlinkRelMdRowCountTest.scala
@@ -28,6 +28,8 @@ import org.apache.calcite.util.ImmutableBitSet
 import org.junit.jupiter.api.Assertions._
 import org.junit.jupiter.api.Test
 
+import java.util
+
 class FlinkRelMdRowCountTest extends FlinkRelMdHandlerTestBase {
 
   @Test
@@ -183,6 +185,7 @@ class FlinkRelMdRowCountTest extends 
FlinkRelMdHandlerTestBase {
     val windowAgg = new LogicalWindowAggregate(
       ts.getCluster,
       ts.getTraitSet,
+      util.List.of(),
       ts,
       ImmutableBitSet.of(0, 1),
       aggCallOfWindowAgg,

Reply via email to