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

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

commit ddb4200f8f52b02afb1b866abb0785218e2c5994
Author: Julian Hyde <[email protected]>
AuthorDate: Fri Apr 7 12:11:02 2023 -0400

    Refactor: Add fields AggregateCall.rexList and 
RelBuilder.AggCall.preOperands
    
    These changes are in preparation for [CALCITE-4334] LITERAL_AGG.
    
    Also improve the fix for [CALCITE-5506].
---
 .../java/org/apache/calcite/plan/RelOptUtil.java   |   4 +-
 .../apache/calcite/plan/SubstitutionVisitor.java   |   3 +-
 .../org/apache/calcite/rel/core/Aggregate.java     |  48 +++++---
 .../org/apache/calcite/rel/core/AggregateCall.java | 136 +++++++++++++--------
 .../java/org/apache/calcite/rel/core/Window.java   |   3 +-
 .../calcite/rel/externalize/RelJsonReader.java     |   3 +-
 .../apache/calcite/rel/rel2sql/SqlImplementor.java |  30 +++--
 .../rel/rules/AggregateCaseToFilterRule.java       |  34 +++---
 .../AggregateExpandDistinctAggregatesRule.java     |  24 ++--
 .../rel/rules/AggregateFilterTransposeRule.java    |   2 +-
 .../rel/rules/AggregateReduceFunctionsRule.java    |  14 ++-
 .../calcite/rel/rules/AggregateStarTableRule.java  |  26 ++--
 .../rel/rules/AggregateUnionTransposeRule.java     |   2 +-
 .../rel/rules/ProjectAggregateMergeRule.java       |   4 +-
 .../org/apache/calcite/sql/SqlOperatorBinding.java |   9 +-
 .../calcite/sql/SqlSplittableAggFunction.java      |  23 ++--
 .../calcite/sql/fun/SqlBasicAggFunction.java       |  11 +-
 .../apache/calcite/sql2rel/SqlToRelConverter.java  |   9 +-
 .../java/org/apache/calcite/tools/RelBuilder.java  | 123 ++++++++++++++-----
 .../materialize/NormalizationTrimFieldTest.java    |   2 +-
 .../org/apache/calcite/plan/RelWriterTest.java     |   6 +-
 .../calcite/plan/volcano/TraitPropagationTest.java |   3 +-
 .../calcite/rel/rel2sql/RelToSqlConverterTest.java |   3 +-
 .../org/apache/calcite/test/RelMetadataTest.java   |   4 +-
 24 files changed, 345 insertions(+), 181 deletions(-)

diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java 
b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
index b3e9552dac..1e1df16a29 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -959,8 +959,8 @@ public abstract class RelOptUtil {
     for (int i = 0; i < aggCallCnt; i++) {
       aggCalls.add(
           AggregateCall.create(SqlStdOperatorTable.SINGLE_VALUE, false, false,
-              false, ImmutableList.of(i), -1, null, RelCollations.EMPTY, 0, 
rel,
-              null, null));
+              false, ImmutableList.of(), ImmutableList.of(i), -1,
+              null, RelCollations.EMPTY, 0, rel, null, null));
     }
 
     return LogicalAggregate.create(rel, ImmutableList.of(), 
ImmutableBitSet.of(),
diff --git 
a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java 
b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
index 2315c5d672..f631cce3f1 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -1979,6 +1979,7 @@ public class SubstitutionVisitor {
               newAggCall =
                   AggregateCall.create(aggFunction, aggregateCall.isDistinct(),
                       aggregateCall.isApproximate(), 
aggregateCall.ignoreNulls(),
+                      aggregateCall.rexList,
                       ImmutableList.of(target.groupSet.cardinality() + i), -1,
                       aggregateCall.distinctKeys, aggregateCall.collation,
                       aggregateCall.type, aggregateCall.name);
@@ -2048,7 +2049,7 @@ public class SubstitutionVisitor {
     }
     return AggregateCall.create(aggregation,
         queryAggCall.isDistinct(), queryAggCall.isApproximate(),
-        queryAggCall.ignoreNulls(),
+        queryAggCall.ignoreNulls(), queryAggCall.rexList,
         newArgList, -1, queryAggCall.distinctKeys,
         queryAggCall.collation, queryAggCall.type,
         queryAggCall.name);
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java 
b/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java
index 568a1b45d4..0736bfa054 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java
@@ -46,7 +46,6 @@ import org.apache.calcite.util.Litmus;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 
-import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.math.IntMath;
 
@@ -56,9 +55,12 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Objects;
 import java.util.Set;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
+import static java.util.Objects.requireNonNull;
+
 /**
  * Relational operator that eliminates
  * duplicates and computes totals.
@@ -102,7 +104,7 @@ public abstract class Aggregate extends SingleRel 
implements Hintable {
    * before 2.0. */
   @Experimental
   public static void checkIndicator(boolean indicator) {
-    Preconditions.checkArgument(!indicator,
+    checkArgument(!indicator,
         "indicator is no longer supported; use GROUPING function instead");
   }
 
@@ -160,7 +162,7 @@ public abstract class Aggregate extends SingleRel 
implements Hintable {
     super(cluster, traitSet, input);
     this.hints = ImmutableList.copyOf(hints);
     this.aggCalls = ImmutableList.copyOf(aggCalls);
-    this.groupSet = Objects.requireNonNull(groupSet, "groupSet");
+    this.groupSet = requireNonNull(groupSet, "groupSet");
     if (groupSets == null) {
       this.groupSets = ImmutableList.of(groupSet);
     } else {
@@ -173,7 +175,7 @@ public abstract class Aggregate extends SingleRel 
implements Hintable {
     assert groupSet.length() <= input.getRowType().getFieldCount();
     for (AggregateCall aggCall : aggCalls) {
       assert typeMatchesInferred(aggCall, Litmus.THROW);
-      Preconditions.checkArgument(aggCall.filterArg < 0
+      checkArgument(aggCall.filterArg < 0
           || isPredicate(input, aggCall.filterArg),
           "filter must be BOOLEAN NOT NULL");
     }
@@ -574,6 +576,7 @@ public abstract class Aggregate extends SingleRel 
implements Hintable {
    * context of a {@link org.apache.calcite.rel.logical.LogicalAggregate}.
    */
   public static class AggCallBinding extends SqlOperatorBinding {
+    private final List<RelDataType> preOperands;
     private final List<RelDataType> operands;
     private final int groupCount;
     private final boolean filter;
@@ -583,22 +586,33 @@ public abstract class Aggregate extends SingleRel 
implements Hintable {
      *
      * @param typeFactory  Type factory
      * @param aggFunction  Aggregate function
+     * @param preOperands  Data types of pre-operands
      * @param operands     Data types of operands
      * @param groupCount   Number of columns in the GROUP BY clause
      * @param filter       Whether the aggregate function has a FILTER clause
      */
     public AggCallBinding(RelDataTypeFactory typeFactory,
-        SqlAggFunction aggFunction, List<RelDataType> operands, int groupCount,
+        SqlAggFunction aggFunction, List<RelDataType> preOperands,
+        List<RelDataType> operands, int groupCount,
         boolean filter) {
       super(typeFactory, aggFunction);
-      this.operands = operands;
+      this.preOperands = requireNonNull(preOperands, "preOperands");
+      this.operands =
+          requireNonNull(operands,
+              "operands of aggregate call should not be null");
       this.groupCount = groupCount;
       this.filter = filter;
-      assert operands != null
-          : "operands of aggregate call should not be null";
-      assert groupCount >= 0
-          : "number of group by columns should be greater than zero in "
-          + "aggregate call. Got " + groupCount;
+      checkArgument(groupCount >= 0,
+          "number of group by columns should be greater than zero in "
+              + "aggregate call. Got %s", groupCount);
+    }
+
+    @Deprecated // to be removed before 2.0
+    public AggCallBinding(RelDataTypeFactory typeFactory,
+        SqlAggFunction aggFunction, List<RelDataType> operands, int groupCount,
+        boolean filter) {
+      this(typeFactory, aggFunction, ImmutableList.of(), operands, groupCount,
+          filter);
     }
 
     @Override public int getGroupCount() {
@@ -609,12 +623,18 @@ public abstract class Aggregate extends SingleRel 
implements Hintable {
       return filter;
     }
 
+    @Override public int getPreOperandCount() {
+      return preOperands.size();
+    }
+
     @Override public int getOperandCount() {
-      return operands.size();
+      return preOperands.size() + operands.size();
     }
 
     @Override public RelDataType getOperandType(int ordinal) {
-      return operands.get(ordinal);
+      return ordinal < preOperands.size()
+          ? preOperands.get(ordinal)
+          : operands.get(ordinal - preOperands.size());
     }
 
     @Override public CalciteException newError(
diff --git a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java 
b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
index b1cdcbc57f..fa0d978962 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
@@ -22,6 +22,8 @@ import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUtil;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.SqlTypeUtil;
@@ -38,6 +40,8 @@ import org.checkerframework.checker.nullness.qual.Nullable;
 import java.util.List;
 import java.util.Objects;
 
+import static java.util.Objects.requireNonNull;
+
 /**
  * Call to an aggregate function within an
  * {@link org.apache.calcite.rel.core.Aggregate}.
@@ -52,6 +56,7 @@ public class AggregateCall {
   private final boolean ignoreNulls;
   public final RelDataType type;
   public final @Nullable String name;
+  public final List<RexNode> rexList;
 
   // We considered using ImmutableIntList but we would not save much memory:
   // since all values are small, ImmutableList uses cached Integer values.
@@ -79,7 +84,8 @@ public class AggregateCall {
       RelDataType type,
       String name) {
     this(aggFunction, distinct, false, false,
-        argList, -1, null, RelCollations.EMPTY, type, name);
+        ImmutableList.of(), argList, -1, null,
+        RelCollations.EMPTY, type, name);
   }
 
   /**
@@ -88,6 +94,7 @@ public class AggregateCall {
    * @param aggFunction Aggregate function
    * @param distinct    Whether distinct
    * @param approximate Whether approximate
+   * @param rexList     List of pre-arguments
    * @param argList     List of ordinals of arguments
    * @param filterArg   Ordinal of filter argument (the
    *                    {@code FILTER (WHERE ...)} clause in SQL), or -1
@@ -99,16 +106,18 @@ public class AggregateCall {
    * @param name        Name (may be null)
    */
   private AggregateCall(SqlAggFunction aggFunction, boolean distinct,
-      boolean approximate, boolean ignoreNulls, List<Integer> argList,
+      boolean approximate, boolean ignoreNulls,
+      List<RexNode> rexList, List<Integer> argList,
       int filterArg, @Nullable ImmutableBitSet distinctKeys,
       RelCollation collation, RelDataType type, @Nullable String name) {
-    this.type = Objects.requireNonNull(type, "type");
+    this.type = requireNonNull(type, "type");
     this.name = name;
-    this.aggFunction = Objects.requireNonNull(aggFunction, "aggFunction");
+    this.aggFunction = requireNonNull(aggFunction, "aggFunction");
     this.argList = ImmutableList.copyOf(argList);
+    this.rexList = ImmutableList.copyOf(rexList);
     this.distinctKeys = distinctKeys;
     this.filterArg = filterArg;
-    this.collation = Objects.requireNonNull(collation, "collation");
+    this.collation = requireNonNull(collation, "collation");
     this.distinct = distinct;
     this.approximate = approximate;
     this.ignoreNulls = ignoreNulls;
@@ -124,7 +133,8 @@ public class AggregateCall {
   public static AggregateCall create(SqlAggFunction aggFunction,
       boolean distinct, List<Integer> argList, int groupCount, RelNode input,
       @Nullable RelDataType type, @Nullable String name) {
-    return create(aggFunction, distinct, false, false, argList, -1,
+    return create(aggFunction, distinct, false, false,
+        ImmutableList.of(), argList, -1,
         null, RelCollations.EMPTY, groupCount, input, type, name);
   }
 
@@ -132,7 +142,8 @@ public class AggregateCall {
   public static AggregateCall create(SqlAggFunction aggFunction,
       boolean distinct, List<Integer> argList, int filterArg, int groupCount,
       RelNode input, @Nullable RelDataType type, @Nullable String name) {
-    return create(aggFunction, distinct, false, false, argList, filterArg,
+    return create(aggFunction, distinct, false, false,
+        ImmutableList.of(), argList, filterArg,
         null, RelCollations.EMPTY, groupCount, input, type, name);
   }
 
@@ -141,7 +152,8 @@ public class AggregateCall {
       boolean distinct, boolean approximate, List<Integer> argList,
       int filterArg, int groupCount,
       RelNode input, @Nullable RelDataType type, @Nullable String name) {
-    return create(aggFunction, distinct, approximate, false, argList,
+    return create(aggFunction, distinct, approximate, false,
+        ImmutableList.of(), argList,
         filterArg, null, RelCollations.EMPTY, groupCount, input, type, name);
   }
 
@@ -150,36 +162,51 @@ public class AggregateCall {
       boolean distinct, boolean approximate, List<Integer> argList,
       int filterArg, RelCollation collation, int groupCount,
       RelNode input, @Nullable RelDataType type, @Nullable String name) {
-    return create(aggFunction, distinct, approximate, false, argList, 
filterArg,
+    return create(aggFunction, distinct, approximate, false,
+        ImmutableList.of(), argList, filterArg,
         null, collation, groupCount, input, type, name);
   }
 
-  /** Creates an AggregateCall, inferring its type if {@code type} is null. */
+  @Deprecated // to be removed before 2.0
   public static AggregateCall create(SqlAggFunction aggFunction,
       boolean distinct, boolean approximate, boolean ignoreNulls,
       List<Integer> argList, int filterArg,
       @Nullable ImmutableBitSet distinctKeys, RelCollation collation,
       int groupCount,
       RelNode input, @Nullable RelDataType type, @Nullable String name) {
+    return create(aggFunction, distinct, approximate, ignoreNulls,
+        ImmutableList.of(), argList, filterArg,
+        distinctKeys, collation, groupCount, input, type, name);
+  }
+
+    /** Creates an AggregateCall, inferring its type if {@code type} is null. 
*/
+  public static AggregateCall create(SqlAggFunction aggFunction,
+      boolean distinct, boolean approximate, boolean ignoreNulls,
+      List<RexNode> rexList, List<Integer> argList, int filterArg,
+      @Nullable ImmutableBitSet distinctKeys, RelCollation collation,
+      int groupCount,
+      RelNode input, @Nullable RelDataType type, @Nullable String name) {
     if (type == null) {
       final RelDataTypeFactory typeFactory =
           input.getCluster().getTypeFactory();
+      final List<RelDataType> preTypes = RexUtil.types(rexList);
       final List<RelDataType> types =
           SqlTypeUtil.projectTypes(input.getRowType(), argList);
       final Aggregate.AggCallBinding callBinding =
-          new Aggregate.AggCallBinding(typeFactory, aggFunction, types,
-              groupCount, filterArg >= 0);
+          new Aggregate.AggCallBinding(typeFactory, aggFunction, preTypes,
+              types, groupCount, filterArg >= 0);
       type = aggFunction.inferReturnType(callBinding);
     }
-    return create(aggFunction, distinct, approximate, ignoreNulls, argList,
-        filterArg, distinctKeys, collation, type, name);
+    return create(aggFunction, distinct, approximate, ignoreNulls,
+        rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   @Deprecated // to be removed before 2.0
   public static AggregateCall create(SqlAggFunction aggFunction,
       boolean distinct, List<Integer> argList, int filterArg, RelDataType type,
       @Nullable String name) {
-    return create(aggFunction, distinct, false, false, argList, filterArg, 
null,
+    return create(aggFunction, distinct, false, false,
+        ImmutableList.of(), argList, filterArg, null,
         RelCollations.EMPTY, type, name);
   }
 
@@ -187,7 +214,8 @@ public class AggregateCall {
   public static AggregateCall create(SqlAggFunction aggFunction,
       boolean distinct, boolean approximate, List<Integer> argList,
       int filterArg, RelDataType type, @Nullable String name) {
-    return create(aggFunction, distinct, approximate, false, argList, 
filterArg,
+    return create(aggFunction, distinct, approximate, false,
+        ImmutableList.of(), argList, filterArg,
         null, RelCollations.EMPTY, type, name);
   }
 
@@ -195,7 +223,8 @@ public class AggregateCall {
   public static AggregateCall create(SqlAggFunction aggFunction,
       boolean distinct, boolean approximate, List<Integer> argList,
       int filterArg, RelCollation collation, RelDataType type, @Nullable 
String name) {
-    return create(aggFunction, distinct, approximate, false, argList, 
filterArg,
+    return create(aggFunction, distinct, approximate, false,
+        ImmutableList.of(), argList, filterArg,
         null, collation, type, name);
   }
 
@@ -204,20 +233,21 @@ public class AggregateCall {
       boolean distinct, boolean approximate, boolean ignoreNulls,
       List<Integer> argList, int filterArg, RelCollation collation,
       RelDataType type, @Nullable String name) {
-    return create(aggFunction, distinct, approximate, ignoreNulls, argList,
+    return create(aggFunction, distinct, approximate, ignoreNulls,
+        ImmutableList.of(), argList,
         filterArg, null, collation, type, name);
   }
 
   /** Creates an AggregateCall. */
   public static AggregateCall create(SqlAggFunction aggFunction,
       boolean distinct, boolean approximate, boolean ignoreNulls,
-      List<Integer> argList, int filterArg,
+      List<RexNode> rexList, List<Integer> argList, int filterArg,
       @Nullable ImmutableBitSet distinctKeys, RelCollation collation,
       RelDataType type, @Nullable String name) {
     final boolean distinct2 = distinct
         && (aggFunction.getDistinctOptionality() != Optionality.IGNORED);
     return new AggregateCall(aggFunction, distinct2, approximate, ignoreNulls,
-        argList, filterArg, distinctKeys, collation, type, name);
+        rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   /**
@@ -234,7 +264,7 @@ public class AggregateCall {
   public AggregateCall withDistinct(boolean distinct) {
     return distinct == this.distinct ? this
         : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
-            argList, filterArg, distinctKeys, collation, type, name);
+            rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   /**
@@ -251,7 +281,7 @@ public class AggregateCall {
   public AggregateCall withApproximate(boolean approximate) {
     return approximate == this.approximate ? this
         : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
-            argList, filterArg, distinctKeys, collation, type, name);
+            rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   /**
@@ -267,7 +297,7 @@ public class AggregateCall {
   public AggregateCall withIgnoreNulls(boolean ignoreNulls) {
     return ignoreNulls == this.ignoreNulls ? this
         : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
-            argList, filterArg, distinctKeys, collation, type, name);
+            rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   /**
@@ -293,7 +323,7 @@ public class AggregateCall {
   public AggregateCall withCollation(RelCollation collation) {
     return collation.equals(this.collation) ? this
         : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
-            argList, filterArg, distinctKeys, collation, type, name);
+            rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   /**
@@ -311,7 +341,7 @@ public class AggregateCall {
   public AggregateCall withArgList(List<Integer> argList) {
     return argList.equals(this.argList) ? this
         : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
-            argList, filterArg, distinctKeys, collation, type, name);
+            rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   /** Withs {@link #distinctKeys}. */
@@ -319,7 +349,7 @@ public class AggregateCall {
       @Nullable ImmutableBitSet distinctKeys) {
     return Objects.equals(distinctKeys, this.distinctKeys) ? this
         : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
-            argList, filterArg, distinctKeys, collation, type, name);
+            rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   /**
@@ -344,7 +374,7 @@ public class AggregateCall {
   public AggregateCall withName(@Nullable String name) {
     return Objects.equals(name, this.name) ? this
         : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
-            argList, filterArg, distinctKeys, collation, type, name);
+            rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   @Deprecated // to be removed before 2.0
@@ -362,6 +392,12 @@ public class AggregateCall {
       buf.append((argList.size() == 0) ? "DISTINCT" : "DISTINCT ");
     }
     int i = -1;
+    for (RexNode rexNode : rexList) {
+      if (++i > 0) {
+        buf.append(", ");
+      }
+      buf.append(rexNode);
+    }
     for (Integer arg : argList) {
       if (++i > 0) {
         buf.append(", ");
@@ -399,7 +435,7 @@ public class AggregateCall {
   public AggregateCall withFilter(int filterArg) {
     return filterArg == this.filterArg ? this
         : new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
-            argList, filterArg, distinctKeys, collation, type, name);
+            rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   @Override public boolean equals(@Nullable Object o) {
@@ -417,7 +453,7 @@ public class AggregateCall {
 
   @Override public int hashCode() {
     return Objects.hash(aggFunction, distinct, approximate, ignoreNulls,
-        argList, filterArg, distinctKeys, collation);
+        rexList, argList, filterArg, distinctKeys, collation);
   }
 
   /**
@@ -428,19 +464,19 @@ public class AggregateCall {
   public Aggregate.AggCallBinding createBinding(
       Aggregate aggregateRelBase) {
     final RelDataType rowType = aggregateRelBase.getInput().getRowType();
+    final RelDataTypeFactory typeFactory =
+        aggregateRelBase.getCluster().getTypeFactory();
 
     if (aggFunction.getKind() == SqlKind.PERCENTILE_DISC
         || aggFunction.getKind() == SqlKind.PERCENTILE_CONT) {
       assert collation.getKeys().size() == 1;
-      return new Aggregate.PercentileDiscAggCallBinding(
-          aggregateRelBase.getCluster().getTypeFactory(), aggFunction,
-          SqlTypeUtil.projectTypes(rowType, argList),
+      return new Aggregate.PercentileDiscAggCallBinding(typeFactory,
+          aggFunction, SqlTypeUtil.projectTypes(rowType, argList),
           SqlTypeUtil.projectTypes(rowType, collation.getKeys()).get(0),
           aggregateRelBase.getGroupCount(), hasFilter());
     }
-    return new Aggregate.AggCallBinding(
-        aggregateRelBase.getCluster().getTypeFactory(), aggFunction,
-        SqlTypeUtil.projectTypes(rowType, argList),
+    return new Aggregate.AggCallBinding(typeFactory, aggFunction,
+        RexUtil.types(rexList), SqlTypeUtil.projectTypes(rowType, argList),
         aggregateRelBase.getGroupCount(), hasFilter());
   }
 
@@ -449,33 +485,36 @@ public class AggregateCall {
    *
    * @see #transform(Mappings.TargetMapping)
    *
-   * @param args Arguments
+   * @param argList Arguments
    * @return AggregateCall that suits new inputs and GROUP BY columns
    */
   @Deprecated // to be removed before 2.0
-  public AggregateCall copy(List<Integer> args, int filterArg,
+  public AggregateCall copy(List<Integer> argList, int filterArg,
       @Nullable ImmutableBitSet distinctKeys, RelCollation collation) {
     return new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
-        args, filterArg, distinctKeys, collation, type, name);
+        rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   @Deprecated // to be removed before 2.0
-  public AggregateCall copy(List<Integer> args, int filterArg,
+  public AggregateCall copy(List<Integer> argList, int filterArg,
       RelCollation collation) {
     // ignoring distinctKeys is error-prone
-    return copy(args, filterArg, distinctKeys, collation);
+    return new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   @Deprecated // to be removed before 2.0
-  public AggregateCall copy(List<Integer> args, int filterArg) {
+  public AggregateCall copy(List<Integer> argList, int filterArg) {
     // ignoring distinctKeys, collation is error-prone
-    return copy(args, filterArg, distinctKeys, collation);
+    return new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   @Deprecated // to be removed before 2.0
-  public AggregateCall copy(List<Integer> args) {
+  public AggregateCall copy(List<Integer> argList) {
     // ignoring filterArg, distinctKeys, collation is error-prone
-    return copy(args, filterArg, distinctKeys, collation);
+    return new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        rexList, argList, filterArg, distinctKeys, collation, type, name);
   }
 
   /**
@@ -499,17 +538,18 @@ public class AggregateCall {
             && filterArg == this.filterArg
             ? type
             : null;
-    return create(aggFunction, distinct, approximate, ignoreNulls, argList,
-        filterArg, distinctKeys, collation,
+    return create(aggFunction, distinct, approximate, ignoreNulls,
+        rexList, argList, filterArg, distinctKeys, collation,
         newGroupKeyCount, input, newType, getName());
   }
 
   /** Creates a copy of this aggregate call, applying a mapping to its
    * arguments. */
   public AggregateCall transform(Mappings.TargetMapping mapping) {
-    return copy(Mappings.apply2((Mapping) mapping, argList),
+    return new AggregateCall(aggFunction, distinct, approximate, ignoreNulls,
+        rexList, Mappings.apply2((Mapping) mapping, argList),
         hasFilter() ? Mappings.apply(mapping, filterArg) : -1,
         distinctKeys == null ? null : distinctKeys.permute(mapping),
-        RelCollations.permute(collation, mapping));
+        RelCollations.permute(collation, mapping), type, name);
   }
 }
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Window.java 
b/core/src/main/java/org/apache/calcite/rel/core/Window.java
index 8b52f999e8..437944f0c5 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Window.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Window.java
@@ -368,7 +368,8 @@ public abstract class Window extends SingleRel implements 
Hintable {
           final RexWinAggCall aggCall = aggCalls.get(index);
           final SqlAggFunction op = (SqlAggFunction) aggCall.getOperator();
           return AggregateCall.create(op, aggCall.distinct, false,
-              aggCall.ignoreNulls, getProjectOrdinals(aggCall.getOperands()),
+              aggCall.ignoreNulls, ImmutableList.of(),
+              getProjectOrdinals(aggCall.getOperands()),
               -1, null, RelCollations.EMPTY,
               aggCall.getType(), fieldNames.get(aggCall.ordinal));
         }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java 
b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
index 8b6d1e0f03..80624bb122 100644
--- a/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
+++ b/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
@@ -338,7 +338,8 @@ public class RelJsonReader {
     final RelDataType type =
         relJson.toType(cluster.getTypeFactory(), jsonAggType);
     final String name = (String) jsonAggCall.get("name");
-    return AggregateCall.create(aggregation, distinct, false, false, operands,
+    return AggregateCall.create(aggregation, distinct, false, false,
+        ImmutableList.of(), operands,
         filterOperand == null ? -1 : filterOperand,
         null, RelCollations.EMPTY, type, name);
   }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java 
b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
index 6b77384110..1b2151b44b 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -88,6 +88,7 @@ import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.validate.SqlValidatorUtil;
 import org.apache.calcite.util.DateString;
+import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.RangeSets;
 import org.apache.calcite.util.Sarg;
@@ -1834,15 +1835,26 @@ public abstract class SqlImplementor {
       if (rel instanceof Project) {
         Project project = (Project) rel;
         RelNode input = project.getInput();
-        // Cannot merge because "select 1 from t"
-        // is different from "select 1 from (select count(1) from t)"
-        final boolean hasInputRef = project.getProjects()
-            .stream()
-            .anyMatch(rex -> RexUtil.containsInputRef(rex));
-        final boolean hasAggregate =
-            input instanceof Aggregate && input.getRowType().getFieldCount() > 
0;
-        if (!hasInputRef && hasAggregate) {
-          return true;
+        if (input instanceof Aggregate) {
+          // Cannot merge because "select 1 from t"
+          // is different from "select 1 from (select count(1) from t)"
+          //
+          // Some databases don't allow "GROUP BY ()". On those databases,
+          //   SELECT MIN(1) FROM t
+          // is valid and
+          //   SELECT 1 FROM t GROUP BY ()
+          // is not. So, if an aggregate has no group keys we can only remove
+          // the subquery if there is at least one aggregate function.
+          // See RelToSqlConverter.buildAggregate.
+          final Aggregate aggregate = (Aggregate) input;
+          final ImmutableBitSet fieldsUsed =
+              RelOptUtil.InputFinder.bits(project.getProjects(), null);
+          final boolean hasAggregate =
+              !aggregate.getGroupSet().isEmpty()
+                  || !aggregate.getAggCallList().isEmpty();
+          if (hasAggregate && fieldsUsed.isEmpty()) {
+            return true;
+          }
         }
       }
 
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java
index 1ebe303a5c..f4642855ac 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateCaseToFilterRule.java
@@ -134,9 +134,9 @@ public class AggregateCaseToFilterRule
     call.getPlanner().prune(aggregate);
   }
 
-  private static @Nullable AggregateCall transform(AggregateCall aggregateCall,
+  private static @Nullable AggregateCall transform(AggregateCall call,
       Project project, List<RexNode> newProjects) {
-    final int singleArg = soleArgument(aggregateCall);
+    final int singleArg = soleArgument(call);
     if (singleArg < 0) {
       return null;
     }
@@ -166,17 +166,17 @@ public class AggregateCaseToFilterRule
     // Combine the CASE filter with an honest-to-goodness SQL FILTER, if the
     // latter is present.
     final RexNode filter;
-    if (aggregateCall.filterArg >= 0) {
+    if (call.filterArg >= 0) {
       filter =
           rexBuilder.makeCall(SqlStdOperatorTable.AND,
-              project.getProjects().get(aggregateCall.filterArg),
+              project.getProjects().get(call.filterArg),
               filterFromCase);
     } else {
       filter = filterFromCase;
     }
 
-    final SqlKind kind = aggregateCall.getAggregation().getKind();
-    if (aggregateCall.isDistinct()) {
+    final SqlKind kind = call.getAggregation().getKind();
+    if (call.isDistinct()) {
       // Just one style supported:
       //   COUNT(DISTINCT CASE WHEN x = 'foo' THEN y END)
       // =>
@@ -187,9 +187,9 @@ public class AggregateCaseToFilterRule
         newProjects.add(arg1);
         newProjects.add(filter);
         return AggregateCall.create(SqlStdOperatorTable.COUNT, true, false,
-            false, ImmutableList.of(newProjects.size() - 2),
+            false, call.rexList, ImmutableList.of(newProjects.size() - 2),
             newProjects.size() - 1, null, RelCollations.EMPTY,
-            aggregateCall.getType(), aggregateCall.getName());
+            call.getType(), call.getName());
       }
       return null;
     }
@@ -211,9 +211,9 @@ public class AggregateCaseToFilterRule
         && RexLiteral.isNullLiteral(arg2)) {
       newProjects.add(filter);
       return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
-          false, ImmutableList.of(), newProjects.size() - 1, null,
-          RelCollations.EMPTY, aggregateCall.getType(),
-          aggregateCall.getName());
+          false, call.rexList, ImmutableList.of(), newProjects.size() - 1, 
null,
+          RelCollations.EMPTY, call.getType(),
+          call.getName());
     } else if (kind == SqlKind.SUM // Case B
         && isIntLiteral(arg1, BigDecimal.ONE)
         && isIntLiteral(arg2, BigDecimal.ZERO)) {
@@ -224,18 +224,18 @@ public class AggregateCaseToFilterRule
           typeFactory.createTypeWithNullability(
               typeFactory.createSqlType(SqlTypeName.BIGINT), false);
       return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
-          false, ImmutableList.of(), newProjects.size() - 1, null,
-          RelCollations.EMPTY, dataType, aggregateCall.getName());
+          false, call.rexList, ImmutableList.of(), newProjects.size() - 1, 
null,
+          RelCollations.EMPTY, dataType, call.getName());
     } else if ((RexLiteral.isNullLiteral(arg2) // Case A1
-            && aggregateCall.getAggregation().allowsFilter())
+            && call.getAggregation().allowsFilter())
         || (kind == SqlKind.SUM // Case A2
             && isIntLiteral(arg2, BigDecimal.ZERO))) {
       newProjects.add(arg1);
       newProjects.add(filter);
-      return AggregateCall.create(aggregateCall.getAggregation(), false,
-          false, false, ImmutableList.of(newProjects.size() - 2),
+      return AggregateCall.create(call.getAggregation(), false,
+          false, false, call.rexList, ImmutableList.of(newProjects.size() - 2),
           newProjects.size() - 1, null, RelCollations.EMPTY,
-          aggregateCall.getType(), aggregateCall.getName());
+          call.getType(), call.getName());
     } else {
       return null;
     }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
index 51e1132ba6..41d8313e59 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
@@ -323,7 +323,7 @@ public final class AggregateExpandDistinctAggregatesRule
       if (!aggCall.isDistinct()) {
         final AggregateCall newCall =
             AggregateCall.create(aggCall.getAggregation(), false,
-                aggCall.isApproximate(), aggCall.ignoreNulls(),
+                aggCall.isApproximate(), aggCall.ignoreNulls(), 
aggCall.rexList,
                 aggCall.getArgList(), -1, aggCall.distinctKeys,
                 aggCall.collation, bottomGroupSet.cardinality(),
                 relBuilder.peek(), null, aggCall.name);
@@ -352,6 +352,7 @@ public final class AggregateExpandDistinctAggregatesRule
                 false,
                 aggCall.isApproximate(),
                 aggCall.ignoreNulls(),
+                aggCall.rexList,
                 newArgList,
                 -1,
                 aggCall.distinctKeys,
@@ -369,16 +370,15 @@ public final class AggregateExpandDistinctAggregatesRule
           newCall =
               AggregateCall.create(new SqlSumEmptyIsZeroAggFunction(), false,
                   aggCall.isApproximate(), aggCall.ignoreNulls(),
-                  newArgs, -1, aggCall.distinctKeys, aggCall.collation,
-                  originalGroupSet.cardinality(), relBuilder.peek(),
-                  null,
-                  aggCall.getName());
+                  aggCall.rexList, newArgs, -1, aggCall.distinctKeys,
+                  aggCall.collation, originalGroupSet.cardinality(),
+                  relBuilder.peek(), null, aggCall.getName());
         } else {
           newCall =
               AggregateCall.create(aggCall.getAggregation(), false,
                   aggCall.isApproximate(), aggCall.ignoreNulls(),
-                  newArgs, -1, aggCall.distinctKeys, aggCall.collation,
-                  originalGroupSet.cardinality(),
+                  aggCall.rexList, newArgs, -1, aggCall.distinctKeys,
+                  aggCall.collation, originalGroupSet.cardinality(),
                   relBuilder.peek(), null, aggCall.name);
         }
         nonDistinctAggCallProcessedSoFar++;
@@ -472,7 +472,7 @@ public final class AggregateExpandDistinctAggregatesRule
 
     distinctAggCalls.add(
         AggregateCall.create(SqlStdOperatorTable.GROUPING, false, false, false,
-            ImmutableIntList.copyOf(fullGroupSet), -1,
+            ImmutableList.of(), ImmutableIntList.copyOf(fullGroupSet), -1,
             null, RelCollations.EMPTY,
             groupSets.size(), relBuilder.peek(), null, "$g"));
 
@@ -530,7 +530,8 @@ public final class AggregateExpandDistinctAggregatesRule
       final AggregateCall newCall =
           AggregateCall.create(aggregation, false,
               aggCall.isApproximate(), aggCall.ignoreNulls(),
-              newArgList, newFilterArg, aggCall.distinctKeys, 
aggCall.collation,
+              aggCall.rexList, newArgList, newFilterArg,
+              aggCall.distinctKeys, aggCall.collation,
               aggregate.getGroupCount(), relBuilder.peek(), null, 
aggCall.name);
       newCalls.add(newCall);
     }
@@ -744,7 +745,7 @@ public final class AggregateExpandDistinctAggregatesRule
                   () -> "sourceOf.get(" + aggCall.filterArg + ")");
       final AggregateCall newAggCall =
           AggregateCall.create(aggCall.getAggregation(), false,
-              aggCall.isApproximate(), aggCall.ignoreNulls(),
+              aggCall.isApproximate(), aggCall.ignoreNulls(), aggCall.rexList,
               newArgs, newFilterArg, aggCall.distinctKeys, aggCall.collation,
               aggCall.getType(), aggCall.getName());
       assert refs.get(i) == null;
@@ -832,7 +833,8 @@ public final class AggregateExpandDistinctAggregatesRule
       }
       final AggregateCall newAggCall =
           AggregateCall.create(aggCall.getAggregation(), false,
-              aggCall.isApproximate(), aggCall.ignoreNulls(), newArgs, -1,
+              aggCall.isApproximate(), aggCall.ignoreNulls(),
+              aggCall.rexList, newArgs, -1,
               aggCall.distinctKeys, aggCall.collation,
               aggCall.getType(), aggCall.getName());
       newAggCalls.set(i, newAggCall);
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
index 9fe4bcc194..43524a2900 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
@@ -147,7 +147,7 @@ public class AggregateFilterTransposeRule
         topAggCallList.add(
             AggregateCall.create(rollup, aggregateCall.isDistinct(),
                 aggregateCall.isApproximate(), aggregateCall.ignoreNulls(),
-                ImmutableList.of(i++), -1,
+                aggregateCall.rexList, ImmutableList.of(i++), -1,
                 aggregateCall.distinctKeys, aggregateCall.collation,
                 aggregateCall.type, aggregateCall.name));
       }
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
index f92b2a74de..ec09c77c47 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
@@ -371,12 +371,13 @@ public class AggregateReduceFunctionsRule
       int filter) {
     final Aggregate.AggCallBinding binding =
         new Aggregate.AggCallBinding(typeFactory, aggFunction,
-            ImmutableList.of(operandType), oldAggRel.getGroupCount(),
-            filter >= 0);
+            ImmutableList.of(), ImmutableList.of(operandType),
+            oldAggRel.getGroupCount(), filter >= 0);
     return AggregateCall.create(aggFunction,
         oldCall.isDistinct(),
         oldCall.isApproximate(),
         oldCall.ignoreNulls(),
+        oldCall.rexList,
         ImmutableIntList.of(argOrdinal),
         filter,
         oldCall.distinctKeys,
@@ -398,6 +399,7 @@ public class AggregateReduceFunctionsRule
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
+            oldCall.rexList,
             oldCall.getArgList(),
             oldCall.filterArg,
             oldCall.distinctKeys,
@@ -411,6 +413,7 @@ public class AggregateReduceFunctionsRule
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
+            oldCall.rexList,
             oldCall.getArgList(),
             oldCall.filterArg,
             oldCall.distinctKeys,
@@ -455,7 +458,7 @@ public class AggregateReduceFunctionsRule
 
     final AggregateCall sumZeroCall =
         AggregateCall.create(SqlStdOperatorTable.SUM0, oldCall.isDistinct(),
-            oldCall.isApproximate(), oldCall.ignoreNulls(),
+            oldCall.isApproximate(), oldCall.ignoreNulls(), oldCall.rexList,
             oldCall.getArgList(), oldCall.filterArg, oldCall.distinctKeys,
             oldCall.collation, oldAggRel.getGroupCount(), oldAggRel.getInput(),
             null, oldCall.name);
@@ -464,6 +467,7 @@ public class AggregateReduceFunctionsRule
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
+            oldCall.rexList,
             oldCall.getArgList(),
             oldCall.filterArg,
             oldCall.distinctKeys,
@@ -554,6 +558,7 @@ public class AggregateReduceFunctionsRule
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
+            oldCall.rexList,
             ImmutableIntList.of(argOrdinal),
             oldCall.filterArg,
             oldCall.distinctKeys,
@@ -579,6 +584,7 @@ public class AggregateReduceFunctionsRule
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
+            oldCall.rexList,
             oldCall.getArgList(),
             oldCall.filterArg,
             oldCall.distinctKeys,
@@ -643,6 +649,7 @@ public class AggregateReduceFunctionsRule
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
+            oldCall.rexList,
             ImmutableIntList.of(argOrdinal),
             filterArg,
             oldCall.distinctKeys,
@@ -688,6 +695,7 @@ public class AggregateReduceFunctionsRule
             oldCall.isDistinct(),
             oldCall.isApproximate(),
             oldCall.ignoreNulls(),
+            oldCall.rexList,
             argOrdinals,
             filterArg,
             oldCall.distinctKeys,
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
index 3b432da412..7b2e9a1696 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateStarTableRule.java
@@ -194,14 +194,14 @@ public class AggregateStarTableRule
     call.transformTo(relBuilder.build());
   }
 
-  private static @Nullable AggregateCall rollUp(int groupCount, RelBuilder 
relBuilder,
-      AggregateCall aggregateCall, TileKey tileKey) {
-    if (aggregateCall.isDistinct()) {
+  private static @Nullable AggregateCall rollUp(int groupCount,
+      RelBuilder relBuilder, AggregateCall call, TileKey tileKey) {
+    if (call.isDistinct()) {
       return null;
     }
-    final SqlAggFunction aggregation = aggregateCall.getAggregation();
+    final SqlAggFunction aggregation = call.getAggregation();
     final Pair<SqlAggFunction, List<Integer>> seek =
-        Pair.of(aggregation, aggregateCall.getArgList());
+        Pair.of(aggregation, call.getArgList());
     final int offset = tileKey.dimensions.cardinality();
     final ImmutableList<Lattice.Measure> measures = tileKey.measures;
 
@@ -214,17 +214,17 @@ public class AggregateStarTableRule
       if (roll == null) {
         break tryRoll;
       }
-      return AggregateCall.create(roll, false, aggregateCall.isApproximate(),
-          aggregateCall.ignoreNulls(), ImmutableList.of(offset + i), -1,
-          aggregateCall.distinctKeys, aggregateCall.collation,
-          groupCount, relBuilder.peek(), null, aggregateCall.name);
+      return AggregateCall.create(roll, false, call.isApproximate(),
+          call.ignoreNulls(), call.rexList, ImmutableList.of(offset + i), -1,
+          call.distinctKeys, call.collation,
+          groupCount, relBuilder.peek(), null, call.name);
     }
 
     // Second, try to satisfy the aggregation based on group set columns.
   tryGroup:
     {
       List<Integer> newArgs = new ArrayList<>();
-      for (Integer arg : aggregateCall.getArgList()) {
+      for (Integer arg : call.getArgList()) {
         int z = tileKey.dimensions.indexOf(arg);
         if (z < 0) {
           break tryGroup;
@@ -232,9 +232,9 @@ public class AggregateStarTableRule
         newArgs.add(z);
       }
       return AggregateCall.create(aggregation, false,
-          aggregateCall.isApproximate(), aggregateCall.ignoreNulls(),
-          newArgs, -1, aggregateCall.distinctKeys, aggregateCall.collation,
-          groupCount, relBuilder.peek(), null, aggregateCall.name);
+          call.isApproximate(), call.ignoreNulls(), call.rexList,
+          newArgs, -1, call.distinctKeys, call.collation,
+          groupCount, relBuilder.peek(), null, call.name);
     }
 
     // No roll up possible.
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
index fda5254c20..32df27a641 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
@@ -215,7 +215,7 @@ public class AggregateUnionTransposeRule
       AggregateCall newCall =
           AggregateCall.create(aggFun, origCall.isDistinct(),
               origCall.isApproximate(), origCall.ignoreNulls(),
-              ImmutableList.of(groupCount + ord.i), -1,
+              origCall.rexList, ImmutableList.of(groupCount + ord.i), -1,
               origCall.distinctKeys, origCall.collation,
               groupCount, input, aggType, origCall.getName());
       newCalls.add(newCall);
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java
index 11af131f4c..3c2f5e404c 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/ProjectAggregateMergeRule.java
@@ -165,8 +165,8 @@ public class ProjectAggregateMergeRule
       List<AggregateCall> aggCallList) {
     final AggregateCall sum0 =
         AggregateCall.create(SqlStdOperatorTable.SUM0, sum.isDistinct(),
-            sum.isApproximate(), sum.ignoreNulls(), sum.getArgList(),
-            sum.filterArg, sum.distinctKeys, sum.collation,
+            sum.isApproximate(), sum.ignoreNulls(), sum.rexList,
+            sum.getArgList(), sum.filterArg, sum.distinctKeys, sum.collation,
             typeFactory.createTypeWithNullability(sum.type, false), null);
     final int i = aggCallList.indexOf(sum0);
     if (i >= 0) {
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java 
b/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java
index 04e64cfeca..578c1ee505 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java
@@ -208,9 +208,16 @@ public abstract class SqlOperatorBinding {
             getOperandType(ordinal).getSqlTypeName());
   }
 
-  /** Returns the number of bound operands. */
+  /** Returns the number of bound operands.
+   * Includes pre-operands and regular operands. */
   public abstract int getOperandCount();
 
+  /** Returns the number of pre-operands.
+   * Zero except for a few aggregate functions. */
+  public int getPreOperandCount() {
+    return 0;
+  }
+
   /**
    * Gets the type of a bound operand.
    *
diff --git 
a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
index 7bd55d7b22..452ff6ef9a 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
@@ -135,9 +135,10 @@ public interface SqlSplittableAggFunction {
 
     @Override public @Nullable AggregateCall other(RelDataTypeFactory 
typeFactory,
         AggregateCall e) {
+      final RelDataType type = typeFactory.createSqlType(SqlTypeName.BIGINT);
       return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
-          false, ImmutableIntList.of(), -1, null, RelCollations.EMPTY,
-          typeFactory.createSqlType(SqlTypeName.BIGINT), null);
+          false, ImmutableList.of(), ImmutableIntList.of(), -1, null,
+          RelCollations.EMPTY, type, null);
     }
 
     @Override public AggregateCall topSplit(RexBuilder rexBuilder,
@@ -165,8 +166,9 @@ public interface SqlSplittableAggFunction {
       }
       int ordinal = extra.register(node);
       return AggregateCall.create(SqlStdOperatorTable.SUM0, false, false,
-          false, ImmutableList.of(ordinal), -1, aggregateCall.distinctKeys,
-          aggregateCall.collation, aggregateCall.type, aggregateCall.name);
+          false, aggregateCall.rexList, ImmutableList.of(ordinal), -1,
+          aggregateCall.distinctKeys, aggregateCall.collation,
+          aggregateCall.type, aggregateCall.name);
     }
 
     /**
@@ -205,7 +207,7 @@ public interface SqlSplittableAggFunction {
               || top.getAggregation().getKind() == SqlKind.SUM0)) {
         return AggregateCall.create(bottom.getAggregation(),
             bottom.isDistinct(), bottom.isApproximate(), false,
-            bottom.getArgList(), bottom.filterArg,
+            bottom.rexList, bottom.getArgList(), bottom.filterArg,
             bottom.distinctKeys, bottom.getCollation(),
             bottom.getType(), top.getName());
       } else {
@@ -250,7 +252,7 @@ public interface SqlSplittableAggFunction {
       if (top.getAggregation().getKind() == bottom.getAggregation().getKind()) 
{
         return AggregateCall.create(bottom.getAggregation(),
             bottom.isDistinct(), bottom.isApproximate(), false,
-            bottom.getArgList(), bottom.filterArg,
+            bottom.rexList, bottom.getArgList(), bottom.filterArg,
             bottom.distinctKeys, bottom.getCollation(),
             bottom.getType(), top.getName());
       } else {
@@ -277,9 +279,10 @@ public interface SqlSplittableAggFunction {
 
     @Override public @Nullable AggregateCall other(RelDataTypeFactory 
typeFactory,
         AggregateCall e) {
+      final RelDataType type = typeFactory.createSqlType(SqlTypeName.BIGINT);
       return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
-          false, ImmutableIntList.of(), -1, null, RelCollations.EMPTY,
-          typeFactory.createSqlType(SqlTypeName.BIGINT), null);
+          false, ImmutableList.of(), ImmutableIntList.of(), -1, null,
+          RelCollations.EMPTY, type, null);
     }
 
     @Override public AggregateCall topSplit(RexBuilder rexBuilder,
@@ -309,7 +312,7 @@ public interface SqlSplittableAggFunction {
       }
       int ordinal = extra.register(node);
       return AggregateCall.create(getMergeAggFunctionOfTopSplit(), false, 
false,
-          false, ImmutableList.of(ordinal), -1,
+          false, aggregateCall.rexList, ImmutableList.of(ordinal), -1,
           aggregateCall.distinctKeys, aggregateCall.collation,
           aggregateCall.type, aggregateCall.name);
     }
@@ -321,7 +324,7 @@ public interface SqlSplittableAggFunction {
               || topKind == SqlKind.SUM0)) {
         return AggregateCall.create(bottom.getAggregation(),
             bottom.isDistinct(), bottom.isApproximate(), false,
-            bottom.getArgList(), bottom.filterArg,
+            bottom.rexList, bottom.getArgList(), bottom.filterArg,
             bottom.distinctKeys, bottom.getCollation(),
             bottom.getType(), top.getName());
       } else {
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlBasicAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlBasicAggFunction.java
index 95623dfc56..33f9d888f3 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlBasicAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlBasicAggFunction.java
@@ -58,7 +58,8 @@ public final class SqlBasicAggFunction extends SqlAggFunction 
{
   private SqlBasicAggFunction(String name, @Nullable SqlIdentifier 
sqlIdentifier,
       SqlKind kind, SqlReturnTypeInference returnTypeInference,
       @Nullable SqlOperandTypeInference operandTypeInference,
-      SqlOperandTypeChecker operandTypeChecker, SqlFunctionCategory funcType,
+      SqlOperandTypeChecker operandTypeChecker,
+      SqlFunctionCategory funcType,
       boolean requiresOrder, boolean requiresOver,
       Optionality requiresGroupOrder, Optionality distinctOptionality,
       SqlSyntax syntax, boolean allowsNullTreatment, boolean allowsSeparator,
@@ -68,7 +69,8 @@ public final class SqlBasicAggFunction extends SqlAggFunction 
{
         requireNonNull(operandTypeChecker, "operandTypeChecker"),
         requireNonNull(funcType, "funcType"), requiresOrder, requiresOver,
         requiresGroupOrder);
-    this.distinctOptionality = requireNonNull(distinctOptionality, 
"distinctOptionality");
+    this.distinctOptionality =
+        requireNonNull(distinctOptionality, "distinctOptionality");
     this.syntax = requireNonNull(syntax, "syntax");
     this.allowsNullTreatment = allowsNullTreatment;
     this.allowsSeparator = allowsSeparator;
@@ -94,6 +96,10 @@ public final class SqlBasicAggFunction extends 
SqlAggFunction {
 
   //~ Methods ----------------------------------------------------------------
 
+  @Override public <T> @Nullable T unwrap(Class<T> clazz) {
+    return super.unwrap(clazz);
+  }
+
   @Override public RelDataType deriveType(SqlValidator validator,
       SqlValidatorScope scope, SqlCall call) {
     SqlCall strippedCall = call;
@@ -217,4 +223,5 @@ public final class SqlBasicAggFunction extends 
SqlAggFunction {
         requiresOver(), groupOrder, distinctOptionality, syntax,
         allowsNullTreatment, allowsSeparator, percentile);
   }
+
 }
diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java 
b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index a0659bfc90..63b78f91ce 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -1282,11 +1282,11 @@ public class SqlToRelConverter {
                 null,
                 ImmutableList.of(
                     AggregateCall.create(SqlStdOperatorTable.COUNT, false,
-                        false, false, ImmutableList.of(), -1, null,
-                        RelCollations.EMPTY, longType, null),
+                        false, false, ImmutableList.of(), ImmutableList.of(),
+                        -1, null, RelCollations.EMPTY, longType, null),
                     AggregateCall.create(SqlStdOperatorTable.COUNT, false,
-                        false, false, args, -1, null,
-                        RelCollations.EMPTY, longType, null)));
+                        false, false, ImmutableList.of(), args,
+                        -1, null, RelCollations.EMPTY, longType, null)));
         LogicalJoin join =
             LogicalJoin.create(bb.root(), aggregate, ImmutableList.of(),
                 rexBuilder.makeLiteral(true), ImmutableSet.of(), 
JoinRelType.INNER);
@@ -6216,6 +6216,7 @@ public class SqlToRelConverter {
               distinct,
               approximate,
               ignoreNulls,
+              ImmutableList.of(),
               args,
               filterArg,
               distinctKeys,
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java 
b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index 5e3662df81..47a35b8f1c 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -1352,21 +1352,26 @@ public class RelBuilder {
   public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
       RexNode filter, @Nullable String alias, RexNode... operands) {
     return aggregateCall(aggFunction, distinct, false, false, filter, null,
-        ImmutableList.of(), alias, ImmutableList.copyOf(operands));
+        ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.copyOf(operands));
   }
 
   @Deprecated // to be removed before 2.0
   public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
-      boolean approximate, RexNode filter, @Nullable String alias, RexNode... 
operands) {
+      boolean approximate, RexNode filter, @Nullable String alias,
+      RexNode... operands) {
     return aggregateCall(aggFunction, distinct, approximate, false, filter,
-        null, ImmutableList.of(), alias, ImmutableList.copyOf(operands));
+        null, ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.copyOf(operands));
   }
 
   @Deprecated // to be removed before 2.0
   public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
-      RexNode filter, @Nullable String alias, Iterable<? extends RexNode> 
operands) {
+      RexNode filter, @Nullable String alias,
+      Iterable<? extends RexNode> operands) {
     return aggregateCall(aggFunction, distinct, false, false, filter, null,
-        ImmutableList.of(), alias, ImmutableList.copyOf(operands));
+        ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.copyOf(operands));
   }
 
   @Deprecated // to be removed before 2.0
@@ -1374,7 +1379,8 @@ public class RelBuilder {
       boolean approximate, RexNode filter, @Nullable String alias,
       Iterable<? extends RexNode> operands) {
     return aggregateCall(aggFunction, distinct, approximate, false, filter,
-        null, ImmutableList.of(), alias, ImmutableList.copyOf(operands));
+        null, ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to an aggregate function.
@@ -1388,7 +1394,8 @@ public class RelBuilder {
   public AggCall aggregateCall(SqlAggFunction aggFunction,
       Iterable<? extends RexNode> operands) {
     return aggregateCall(aggFunction, false, false, false, null, null,
-        ImmutableList.of(), null, ImmutableList.copyOf(operands));
+        ImmutableList.of(), null, ImmutableList.of(),
+        ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to an aggregate function.
@@ -1402,7 +1409,8 @@ public class RelBuilder {
   public AggCall aggregateCall(SqlAggFunction aggFunction,
       RexNode... operands) {
     return aggregateCall(aggFunction, false, false, false, null, null,
-        ImmutableList.of(), null, ImmutableList.copyOf(operands));
+        ImmutableList.of(), null, ImmutableList.of(),
+        ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to an aggregate function as a copy of an
@@ -1411,7 +1419,8 @@ public class RelBuilder {
     return aggregateCall(a.getAggregation(), a.isDistinct(), a.isApproximate(),
         a.ignoreNulls(), a.filterArg < 0 ? null : field(a.filterArg),
         a.distinctKeys == null ? null : fields(a.distinctKeys),
-        fields(a.collation), a.name, fields(a.getArgList()));
+        fields(a.collation), a.name, ImmutableList.copyOf(a.rexList),
+        fields(a.getArgList()));
   }
 
   /** Creates a call to an aggregate function as a copy of an
@@ -1423,6 +1432,7 @@ public class RelBuilder {
         a.distinctKeys == null ? null
             : fields(Mappings.apply(mapping, a.distinctKeys)),
         fields(RexUtil.apply(mapping, a.collation)), a.name,
+        ImmutableList.copyOf(a.rexList),
         fields(Mappings.apply2(mapping, a.getArgList())));
   }
 
@@ -1430,10 +1440,10 @@ public class RelBuilder {
   protected AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
       boolean approximate, boolean ignoreNulls, @Nullable RexNode filter,
       @Nullable ImmutableList<RexNode> distinctKeys,
-      ImmutableList<RexNode> orderKeys,
-      @Nullable String alias, ImmutableList<RexNode> operands) {
+      ImmutableList<RexNode> orderKeys, @Nullable String alias,
+      ImmutableList<RexNode> preOperands, ImmutableList<RexNode> operands) {
     return new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
-        filter, alias, operands, distinctKeys, orderKeys);
+        filter, alias, preOperands, operands, distinctKeys, orderKeys);
   }
 
   /** Creates a call to the {@code COUNT} aggregate function. */
@@ -1448,9 +1458,11 @@ public class RelBuilder {
 
   /** Creates a call to the {@code COUNT} aggregate function,
    * optionally distinct and with an alias. */
-  public AggCall count(boolean distinct, @Nullable String alias, RexNode... 
operands) {
+  public AggCall count(boolean distinct, @Nullable String alias,
+      RexNode... operands) {
     return aggregateCall(SqlStdOperatorTable.COUNT, distinct, false, false, 
null,
-        null, ImmutableList.of(), alias, ImmutableList.copyOf(operands));
+        null, ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to the {@code COUNT} aggregate function,
@@ -1458,7 +1470,8 @@ public class RelBuilder {
   public AggCall count(boolean distinct, @Nullable String alias,
       Iterable<? extends RexNode> operands) {
     return aggregateCall(SqlStdOperatorTable.COUNT, distinct, false, false, 
null,
-        null, ImmutableList.of(), alias, ImmutableList.copyOf(operands));
+        null, ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to the {@code COUNT(*)} aggregate function. */
@@ -1473,9 +1486,11 @@ public class RelBuilder {
 
   /** Creates a call to the {@code SUM} aggregate function,
    * optionally distinct and with an alias. */
-  public AggCall sum(boolean distinct, @Nullable String alias, RexNode 
operand) {
+  public AggCall sum(boolean distinct, @Nullable String alias,
+      RexNode operand) {
     return aggregateCall(SqlStdOperatorTable.SUM, distinct, false, false, null,
-        null, ImmutableList.of(), alias, ImmutableList.of(operand));
+        null, ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.of(operand));
   }
 
   /** Creates a call to the {@code AVG} aggregate function. */
@@ -1485,9 +1500,11 @@ public class RelBuilder {
 
   /** Creates a call to the {@code AVG} aggregate function,
    * optionally distinct and with an alias. */
-  public AggCall avg(boolean distinct, @Nullable String alias, RexNode 
operand) {
+  public AggCall avg(boolean distinct, @Nullable String alias,
+      RexNode operand) {
     return aggregateCall(SqlStdOperatorTable.AVG, distinct, false, false, null,
-        null, ImmutableList.of(), alias, ImmutableList.of(operand));
+        null, ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.of(operand));
   }
 
   /** Creates a call to the {@code MIN} aggregate function. */
@@ -1499,7 +1516,8 @@ public class RelBuilder {
    * optionally with an alias. */
   public AggCall min(@Nullable String alias, RexNode operand) {
     return aggregateCall(SqlStdOperatorTable.MIN, false, false, false, null,
-        null, ImmutableList.of(), alias, ImmutableList.of(operand));
+        null, ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.of(operand));
   }
 
   /** Creates a call to the {@code MAX} aggregate function,
@@ -1511,7 +1529,8 @@ public class RelBuilder {
   /** Creates a call to the {@code MAX} aggregate function. */
   public AggCall max(@Nullable String alias, RexNode operand) {
     return aggregateCall(SqlStdOperatorTable.MAX, false, false, false, null,
-        null, ImmutableList.of(), alias, ImmutableList.of(operand));
+        null, ImmutableList.of(), alias, ImmutableList.of(),
+        ImmutableList.of(operand));
   }
 
   // Methods for patterns
@@ -3794,6 +3813,19 @@ public class RelBuilder {
       return sort(ImmutableList.copyOf(orderKeys));
     }
 
+    /** Returns a copy of this AggCall with the given pre-operands.
+     *
+     * <p>Pre-operands apply at the start of aggregation and are constant for
+     * the whole query. They do not reference input columns and are typically
+     * {@link RexLiteral}. An example is
+     * {@link org.apache.calcite.sql.fun.SqlInternalOperators#LITERAL_AGG};
+     * most aggregate functions do not take pre-operands. */
+    AggCall preOperands(Iterable<? extends RexNode> preOperands);
+
+    default AggCall preOperands(RexNode... preOperands) {
+      return preOperands(ImmutableList.copyOf(preOperands));
+    }
+
     /** Returns a copy of this AggCall that makes its input values unique by
      * {@code distinctKeys} before aggregating, as in SQL's
      * {@code WITHIN DISTINCT} clause. */
@@ -3937,13 +3969,15 @@ public class RelBuilder {
     private final boolean ignoreNulls;
     private final @Nullable RexNode filter;
     private final @Nullable String alias;
+    private final ImmutableList<RexNode> preOperands; // may be empty
     private final ImmutableList<RexNode> operands; // may be empty
     private final @Nullable ImmutableList<RexNode> distinctKeys; // may be 
empty or null
     private final ImmutableList<RexNode> orderKeys; // may be empty
 
     AggCallImpl(SqlAggFunction aggFunction, boolean distinct,
         boolean approximate, boolean ignoreNulls, @Nullable RexNode filter,
-        @Nullable String alias, ImmutableList<RexNode> operands,
+        @Nullable String alias, ImmutableList<RexNode> preOperands,
+        ImmutableList<RexNode> operands,
         @Nullable ImmutableList<RexNode> distinctKeys,
         ImmutableList<RexNode> orderKeys) {
       this.aggFunction = requireNonNull(aggFunction, "aggFunction");
@@ -3954,6 +3988,7 @@ public class RelBuilder {
       this.approximate = approximate;
       this.ignoreNulls = ignoreNulls;
       this.alias = alias;
+      this.preOperands = requireNonNull(preOperands, "preOperands");
       this.operands = requireNonNull(operands, "operands");
       this.distinctKeys = distinctKeys;
       this.orderKeys = requireNonNull(orderKeys, "orderKeys");
@@ -3975,6 +4010,14 @@ public class RelBuilder {
       if (distinct) {
         b.append("DISTINCT ");
       }
+      if (preOperands.size() > 0) {
+        b.append(preOperands.get(0));
+        for (int i = 1; i < preOperands.size(); i++) {
+          b.append(", ");
+          b.append(preOperands.get(i));
+        }
+        b.append(operands.size() > 0 ? "; " : ";");
+      }
       if (operands.size() > 0) {
         b.append(operands.get(0));
         for (int i = 1; i < operands.size(); i++) {
@@ -4008,7 +4051,8 @@ public class RelBuilder {
       final RelDataType type =
           getTypeFactory().createSqlType(SqlTypeName.BOOLEAN);
       return AggregateCall.create(aggFunction, distinct, approximate,
-          ignoreNulls, ImmutableList.of(), -1, null, collation, type, alias);
+          ignoreNulls, ImmutableList.of(), ImmutableList.of(), -1,
+          null, collation, type, alias);
     }
 
     @Override public AggregateCall aggregateCall(Registrar registrar,
@@ -4040,9 +4084,10 @@ public class RelBuilder {
             .filter(r::fieldIsNullable)
             .collect(Util.toImmutableList());
       }
+
       return AggregateCall.create(aggFunction, distinct, approximate,
-          ignoreNulls, args, filterArg, distinctKeys, collation,
-          groupSet.cardinality(), r, null, alias);
+          ignoreNulls, preOperands, args, filterArg, distinctKeys,
+          collation, groupSet.cardinality(), r, null, alias);
     }
 
     @Override public void register(Registrar registrar) {
@@ -4056,6 +4101,16 @@ public class RelBuilder {
       registrar.registerExpressions(orderKeys);
     }
 
+    @Override public AggCall preOperands(
+        Iterable<? extends RexNode> preOperands) {
+      final ImmutableList<RexNode> preOperandList =
+          ImmutableList.copyOf(preOperands);
+      return preOperandList.equals(this.preOperands)
+          ? this
+          : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
+              filter, alias, preOperandList, operands, distinctKeys, 
orderKeys);
+    }
+
     @Override public OverCall over() {
       return new OverCallImpl(aggFunction, distinct, operands, ignoreNulls,
           alias);
@@ -4067,7 +4122,7 @@ public class RelBuilder {
       return orderKeyList.equals(this.orderKeys)
           ? this
           : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
-              filter, alias, operands, distinctKeys, orderKeyList);
+              filter, alias, preOperands, operands, distinctKeys, 
orderKeyList);
     }
 
     @Override public AggCall sort(RexNode... orderKeys) {
@@ -4080,42 +4135,42 @@ public class RelBuilder {
       return Objects.equals(distinctKeyList, this.distinctKeys)
           ? this
           : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
-              filter, alias, operands, distinctKeyList, orderKeys);
+              filter, alias, preOperands, operands, distinctKeyList, 
orderKeys);
     }
 
     @Override public AggCall approximate(boolean approximate) {
       return approximate == this.approximate
           ? this
           : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
-              filter, alias, operands, distinctKeys, orderKeys);
+              filter, alias, preOperands, operands, distinctKeys, orderKeys);
     }
 
     @Override public AggCall filter(@Nullable RexNode condition) {
       return Objects.equals(condition, this.filter)
           ? this
           : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
-              condition, alias, operands, distinctKeys, orderKeys);
+              condition, alias, preOperands, operands, distinctKeys, 
orderKeys);
     }
 
     @Override public AggCall as(@Nullable String alias) {
       return Objects.equals(alias, this.alias)
           ? this
           : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
-              filter, alias, operands, distinctKeys, orderKeys);
+              filter, alias, preOperands, operands, distinctKeys, orderKeys);
     }
 
     @Override public AggCall distinct(boolean distinct) {
       return distinct == this.distinct
           ? this
           : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
-              filter, alias, operands, distinctKeys, orderKeys);
+              filter, alias, preOperands, operands, distinctKeys, orderKeys);
     }
 
     @Override public AggCall ignoreNulls(boolean ignoreNulls) {
       return ignoreNulls == this.ignoreNulls
           ? this
           : new AggCallImpl(aggFunction, distinct, approximate, ignoreNulls,
-              filter, alias, operands, distinctKeys, orderKeys);
+              filter, alias, preOperands, operands, distinctKeys, orderKeys);
     }
   }
 
@@ -4161,6 +4216,10 @@ public class RelBuilder {
       // nothing to do
     }
 
+    @Override public AggCall preOperands(Iterable<? extends RexNode> 
preOperands) {
+      throw new UnsupportedOperationException();
+    }
+
     @Override public AggCall sort(Iterable<RexNode> orderKeys) {
       throw new UnsupportedOperationException();
     }
diff --git 
a/core/src/test/java/org/apache/calcite/materialize/NormalizationTrimFieldTest.java
 
b/core/src/test/java/org/apache/calcite/materialize/NormalizationTrimFieldTest.java
index 9f8fb3db5a..c53b822b21 100644
--- 
a/core/src/test/java/org/apache/calcite/materialize/NormalizationTrimFieldTest.java
+++ 
b/core/src/test/java/org/apache/calcite/materialize/NormalizationTrimFieldTest.java
@@ -86,7 +86,7 @@ public class NormalizationTrimFieldTest extends 
SqlToRelTestBase {
     final AggregateCall call =
         AggregateCall.create(count.getAggregation(),
             count.isDistinct(), count.isApproximate(),
-            count.ignoreNulls(), ImmutableList.of(3),
+            count.ignoreNulls(), count.rexList, ImmutableList.of(3),
             count.filterArg, null, count.collation,
             count.getType(), count.getName());
     final RelNode query =
diff --git a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java 
b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
index a2166e2100..65912a6be0 100644
--- a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
+++ b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
@@ -544,10 +544,12 @@ class RelWriterTest {
                   null,
                   ImmutableList.of(
                       AggregateCall.create(SqlStdOperatorTable.COUNT,
-                          true, false, false, ImmutableList.of(1), -1, null,
+                          true, false, false, ImmutableList.of(),
+                          ImmutableList.of(1), -1, null,
                           RelCollations.EMPTY, bigIntType, "c"),
                       AggregateCall.create(SqlStdOperatorTable.COUNT,
-                          false, false, false, ImmutableList.of(), -1, null,
+                          false, false, false, ImmutableList.of(),
+                          ImmutableList.of(), -1, null,
                           RelCollations.EMPTY, bigIntType, "d")));
           aggregate.explain(writer);
           return writer.asString();
diff --git 
a/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java 
b/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
index 3b19d0421f..0caa7ec3e5 100644
--- 
a/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
+++ 
b/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
@@ -171,7 +171,8 @@ class TraitPropagationTest {
       // aggregate on s, count
       AggregateCall aggCall =
           AggregateCall.create(SqlStdOperatorTable.COUNT,
-              false, false, false, Collections.singletonList(1), -1,
+              false, false, false, ImmutableList.of(),
+              Collections.singletonList(1), -1,
               null, RelCollations.EMPTY, sqlBigInt, "cnt");
       RelNode agg =
           new LogicalAggregate(cluster,
diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index f9eaf8fe9a..d0a55e025a 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -684,8 +684,7 @@ class RelToSqlConverterTest {
         + "INNER JOIN "
         + "(SELECT 1 AS \"K\"\n"
         + "FROM (SELECT COUNT(\"ENAME\") AS \"DUMMY\"\n"
-        + "FROM \"scott\".\"EMP\") AS \"t0\") AS \"t1\" "
-        + "ON \"t\".\"K\" = \"t1\".\"K\"");
+        + "FROM \"scott\".\"EMP\") AS \"t0\") AS \"t1\" ON \"t\".\"K\" = 
\"t1\".\"K\"");
   }
 
   /** Tests GROUP BY ROLLUP of two columns. The SQL for MySQL has
diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java 
b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index 10288f440a..eb2ef3b55c 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -1898,8 +1898,8 @@ public class RelMetadataTest {
             bitSetOf(2, 0),
             ImmutableList.of(),
             ImmutableList.of(
-                AggregateCall.create(SqlStdOperatorTable.COUNT,
-                    false, false, false, ImmutableIntList.of(),
+                AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
+                    false, ImmutableList.of(), ImmutableIntList.of(),
                     -1, null, RelCollations.EMPTY, 2, join, null, null)));
     rowSize = mq.getAverageRowSize(aggregate);
     columnSizes = mq.getAverageColumnSizes(aggregate);

Reply via email to