content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to you under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.calcite.rel.core;
  
  import org.apache.calcite.linq4j.Ord;
  import org.apache.calcite.rel.RelCollation;
  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.parser.SqlParserPos;
  import org.apache.calcite.sql.type.SqlTypeUtil;
  import org.apache.calcite.util.ImmutableBitSet;
  import org.apache.calcite.util.Optionality;
  import org.apache.calcite.util.mapping.Mapping;
  import org.apache.calcite.util.mapping.Mappings;
  
  import com.google.common.collect.ImmutableList;
  
  import org.checkerframework.checker.nullness.qual.Nullable;
  
  import java.util.List;
  import java.util.Objects;
  
  import static com.google.common.base.Preconditions.checkArgument;
  
  import static java.util.Objects.requireNonNull;
  
  /**
   * Call to an aggregate function within an
   * {@link org.apache.calcite.rel.core.Aggregate}.
   */
  public class AggregateCall {
    //~ Instance fields --------------------------------------------------------
  
    /**
     * Some aggregate calls may produce runtime errors.  For these
     * we need to keep around the original source position information
     * so that the runtime can produce error messages pointing to
     * the offending source operation.  For "safe" aggregations
     * this field may be ZERO.
     */
    private final SqlParserPos pos;
    private final SqlAggFunction aggFunction;
  
    private final boolean distinct;
    private final boolean approximate;
    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.
    private final ImmutableList<Integer> argList;
    public final int filterArg;
    public final @Nullable ImmutableBitSet distinctKeys;
    public final RelCollation collation;
  
    //~ Constructors -----------------------------------------------------------
  
    /**
     * Creates an AggregateCall.
     *
     * @param aggFunction Aggregate function
     * @param distinct    Whether distinct
     * @param argList     List of ordinals of arguments
     * @param type        Result type
     * @param name        Name (may be null)
     */
    @Deprecated // to be removed before 2.0
    public AggregateCall(
        SqlAggFunction aggFunction,
        boolean distinct,
        List<Integer> argList,
        RelDataType type,
        String name) {
      this(SqlParserPos.ZERO, aggFunction, distinct, false, false,
          ImmutableList.of(), argList, -1, null,
          RelCollations.EMPTY, type, name);
    }
  
    /**
     * Creates an AggregateCall.
     *
     * @param pos         Source position for this aggregate.
     *                    Ideally it should only be ZERO when the aggregate
     *                    can never fail at runtime.
     * @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
     * @param distinctKeys Ordinals of fields to make values distinct on before
     *                    aggregating, or null
     * @param collation   How to sort values before aggregation (the
     *                    {@code WITHIN GROUP} clause in SQL)
     * @param type        Result type
     * @param name        Name (may be null)
     */
    private AggregateCall(SqlParserPos pos, SqlAggFunction aggFunction, boolean distinct,
        boolean approximate, boolean ignoreNulls,
        List<RexNode> rexList, List<Integer> argList,
        int filterArg, @Nullable ImmutableBitSet distinctKeys,
        RelCollation collation, RelDataType type, @Nullable String name) {
      this.pos = pos;
      this.type = requireNonNull(type, "type");
      this.name = name;
      this.aggFunction = requireNonNull(aggFunction, "aggFunction");
      this.argList = ImmutableList.copyOf(argList);
      this.rexList = ImmutableList.copyOf(rexList);
      this.distinctKeys = distinctKeys;
      this.filterArg = filterArg;
      this.collation = requireNonNull(collation, "collation");
      this.distinct = distinct;
      this.approximate = approximate;
      this.ignoreNulls = ignoreNulls;
      checkArgument(aggFunction.getDistinctOptionality() != Optionality.IGNORED
              || !distinct,
          "DISTINCT has no effect for this aggregate function, so must be false");
      checkArgument(filterArg < 0 || aggFunction.allowsFilter());
    }
  
    //~ Methods ----------------------------------------------------------------
  
    @Deprecated // to be removed before 2.0
    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,
          ImmutableList.of(), argList, -1,
          null, RelCollations.EMPTY, groupCount, input, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    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,
          ImmutableList.of(), argList, filterArg,
          null, RelCollations.EMPTY, groupCount, input, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    public static AggregateCall create(SqlAggFunction aggFunction,
        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,
          ImmutableList.of(), argList,
          filterArg, null, RelCollations.EMPTY, groupCount, input, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    public static AggregateCall create(SqlAggFunction aggFunction,
        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,
          ImmutableList.of(), argList, filterArg,
          null, collation, groupCount, input, type, name);
    }
  
    @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. */
    @Deprecated // to be removed before 2.0
    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) {
      return create(SqlParserPos.ZERO, aggFunction, distinct, approximate,
          ignoreNulls, rexList, argList, filterArg, distinctKeys, collation, groupCount,
          input, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    public static AggregateCall create(SqlParserPos pos, 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, preTypes,
                types, groupCount, filterArg >= 0);
        type = aggFunction.inferReturnType(callBinding);
      }
      return create(pos, aggFunction, distinct, approximate, ignoreNulls,
          rexList, argList, filterArg, distinctKeys, collation, 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,
        boolean hasEmptyGroup,
        RelNode input, @Nullable RelDataType type, @Nullable String name) {
      return create(SqlParserPos.ZERO, aggFunction, distinct, approximate,
          ignoreNulls, rexList, argList, filterArg, distinctKeys, collation, hasEmptyGroup,
          input, type, name);
    }
  
    public static AggregateCall create(SqlParserPos pos, SqlAggFunction aggFunction,
        boolean distinct, boolean approximate, boolean ignoreNulls,
        List<RexNode> rexList, List<Integer> argList, int filterArg,
        @Nullable ImmutableBitSet distinctKeys, RelCollation collation,
        boolean hasEmptyGroup,
        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;
        if (aggFunction.getKind() == SqlKind.PERCENTILE_DISC
            || aggFunction.getKind() == SqlKind.PERCENTILE_CONT) {
          callBinding =
              new Aggregate.PercentileDiscAggCallBinding(typeFactory, aggFunction,
                  SqlTypeUtil.projectTypes(input.getRowType(), argList),
                  SqlTypeUtil.projectTypes(input.getRowType(), collation.getKeys()).get(0),
                  hasEmptyGroup, filterArg >= 0);
        } else {
          callBinding =
              new Aggregate.AggCallBinding(typeFactory, aggFunction, preTypes, types,
                  hasEmptyGroup, filterArg >= 0);
        }
        type = aggFunction.inferReturnType(callBinding);
      }
      return create(pos, 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,
          ImmutableList.of(), argList, filterArg, null,
          RelCollations.EMPTY, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    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,
          ImmutableList.of(), argList, filterArg,
          null, RelCollations.EMPTY, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    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,
          ImmutableList.of(), argList, filterArg,
          null, collation, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    public static AggregateCall create(SqlAggFunction aggFunction,
        boolean distinct, boolean approximate, boolean ignoreNulls,
        List<Integer> argList, int filterArg, RelCollation collation,
        RelDataType type, @Nullable String name) {
      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<RexNode> rexList, List<Integer> argList, int filterArg,
        @Nullable ImmutableBitSet distinctKeys, RelCollation collation,
        RelDataType type, @Nullable String name) {
      return create(SqlParserPos.ZERO, aggFunction, distinct, approximate,
          ignoreNulls, rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    public static AggregateCall create(SqlParserPos pos, SqlAggFunction aggFunction,
        boolean distinct, boolean approximate, boolean ignoreNulls,
        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(pos, aggFunction, distinct2, approximate, ignoreNulls,
          rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    /**
     * Returns whether this AggregateCall is distinct, as in <code>
     * COUNT(DISTINCT empno)</code>.
     *
     * @return whether distinct
     */
    public final boolean isDistinct() {
      return distinct;
    }
  
    /** Withs {@link #isDistinct()}. */
    public AggregateCall withDistinct(boolean distinct) {
      return distinct == this.distinct ? this
          : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
              rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    /**
     * Returns whether this AggregateCall is approximate, as in <code>
     * APPROX_COUNT_DISTINCT(empno)</code>.
     *
     * @return whether approximate
     */
    public final boolean isApproximate() {
      return approximate;
    }
  
    /** Withs {@link #isApproximate()}. */
    public AggregateCall withApproximate(boolean approximate) {
      return approximate == this.approximate ? this
          : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
              rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    /**
     * Returns whether this AggregateCall ignores nulls.
     *
     * @return whether ignore nulls
     */
    public final boolean ignoreNulls() {
      return ignoreNulls;
    }
  
    /** Withs {@link #ignoreNulls()}. */
    public AggregateCall withIgnoreNulls(boolean ignoreNulls) {
      return ignoreNulls == this.ignoreNulls ? this
          : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
              rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    /**
     * Returns the aggregate function.
     *
     * @return aggregate function
     */
    public final SqlAggFunction getAggregation() {
      return aggFunction;
    }
  
    /**
     * Returns the aggregate ordering definition (the {@code WITHIN GROUP} clause
     * in SQL), or the empty list if not specified.
     *
     * @return ordering definition
     */
    public RelCollation getCollation() {
      return collation;
    }
  
    /** Withs {@link #getCollation()}. */
    public AggregateCall withCollation(RelCollation collation) {
      return collation.equals(this.collation) ? this
          : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
              rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    /**
     * Returns the ordinals of the arguments to this call.
     *
     * <p>The list is immutable.
     *
     * @return list of argument ordinals
     */
    public final List<Integer> getArgList() {
      return argList;
    }
  
    /** Withs {@link #getArgList()}. */
    public AggregateCall withArgList(List<Integer> argList) {
      return argList.equals(this.argList) ? this
          : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
              rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    /** Withs {@link #distinctKeys}. */
    public AggregateCall withDistinctKeys(
        @Nullable ImmutableBitSet distinctKeys) {
      return Objects.equals(distinctKeys, this.distinctKeys) ? this
          : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
              rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    /**
     * Returns the result type.
     *
     * @return result type
     */
    public final RelDataType getType() {
      return type;
    }
  
    /**
     * Returns the name.
     *
     * @return name
     */
    public @Nullable String getName() {
      return name;
    }
  
    /** Withs {@link #name}. */
    public AggregateCall withName(@Nullable String name) {
      return Objects.equals(name, this.name) ? this
          : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
              rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    public AggregateCall rename(@Nullable String name) {
      return withName(name);
    }
  
    @Override public String toString() {
      StringBuilder buf = new StringBuilder(aggFunction.toString());
      buf.append("(");
      if (approximate) {
        buf.append("APPROXIMATE ");
      }
      if (distinct) {
        buf.append(argList.isEmpty() ? "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(", ");
        }
        buf.append("$");
        buf.append(arg);
      }
      buf.append(")");
      if (distinctKeys != null) {
        buf.append(" WITHIN DISTINCT (");
        for (Ord<Integer> key : Ord.zip(distinctKeys)) {
          buf.append(key.i > 0 ? ", $" : "$");
          buf.append(key.e);
        }
        buf.append(")");
      }
      if (hasCollation()) {
        buf.append(" WITHIN GROUP (");
        buf.append(collation);
        buf.append(")");
      }
      if (hasFilter()) {
        buf.append(" FILTER $");
        buf.append(filterArg);
      }
      return buf.toString();
    }
  
    /** Returns whether this AggregateCall has a filter argument. */
    public boolean hasFilter() {
      return filterArg >= 0;
    }
  
    /** Returns true if this AggregateCall has a non-empty collation. Returns false otherwise. */
    public boolean hasCollation() {
      return !collation.equals(RelCollations.EMPTY);
    }
  
    /** Withs {@link #filterArg}. */
    public AggregateCall withFilter(int filterArg) {
      return filterArg == this.filterArg ? this
          : new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
              rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    public SqlParserPos getParserPosition() {
      return this.pos;
    }
  
    @Override public boolean equals(@Nullable Object o) {
      // Intentionally ignore the position
      return o == this
          || o instanceof AggregateCall
          && aggFunction.equals(((AggregateCall) o).aggFunction)
          && distinct == ((AggregateCall) o).distinct
          && approximate == ((AggregateCall) o).approximate
          && ignoreNulls == ((AggregateCall) o).ignoreNulls
          && argList.equals(((AggregateCall) o).argList)
          && filterArg == ((AggregateCall) o).filterArg
          && Objects.equals(distinctKeys, ((AggregateCall) o).distinctKeys)
          && collation.equals(((AggregateCall) o).collation);
    }
  
    @Override public int hashCode() {
      // Ignore the position!
      return Objects.hash(aggFunction, distinct, approximate, ignoreNulls,
          rexList, argList, filterArg, distinctKeys, collation);
    }
  
    /**
     * Creates a binding of this call in the context of an
     * {@link org.apache.calcite.rel.logical.LogicalAggregate},
     * which can then be used to infer the return type.
     */
    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(typeFactory,
            aggFunction, SqlTypeUtil.projectTypes(rowType, argList),
            SqlTypeUtil.projectTypes(rowType, collation.getKeys()).get(0),
            aggregateRelBase.hasEmptyGroup(), hasFilter());
      }
      return new Aggregate.AggCallBinding(typeFactory, aggFunction,
          RexUtil.types(rexList), SqlTypeUtil.projectTypes(rowType, argList),
          aggregateRelBase.hasEmptyGroup(), hasFilter());
    }
  
    /**
     * Creates an equivalent AggregateCall with new argument ordinals.
     *
     * @see #transform(Mappings.TargetMapping)
     *
     * @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> argList, int filterArg,
        @Nullable ImmutableBitSet distinctKeys, RelCollation collation) {
      return new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
          rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    public AggregateCall copy(List<Integer> argList, int filterArg,
        RelCollation collation) {
      // ignoring distinctKeys is error-prone
      return new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
          rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    public AggregateCall copy(List<Integer> argList, int filterArg) {
      // ignoring distinctKeys, collation is error-prone
      return new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
          rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    @Deprecated // to be removed before 2.0
    public AggregateCall copy(List<Integer> argList) {
      // ignoring filterArg, distinctKeys, collation is error-prone
      return new AggregateCall(pos, aggFunction, distinct, approximate, ignoreNulls,
          rexList, argList, filterArg, distinctKeys, collation, type, name);
    }
  
    /**
     * Creates an equivalent AggregateCall that is adapted to a new input types
     * and/or number of columns in GROUP BY.
     *
     * @param input            Relation that will be input of Aggregate
     * @param argList          Argument indices of the new call in the input
     * @param filterArg        Index of the filter, or -1
     * @param oldGroupKeyCount number of columns in GROUP BY of old aggregate
     * @param newGroupKeyCount number of columns in GROUP BY of new aggregate
     * @return AggregateCall that suits new inputs and GROUP BY columns
     */
    @Deprecated // to be removed before 2.0
    public AggregateCall adaptTo(RelNode input, List<Integer> argList,
        int filterArg, int oldGroupKeyCount, int newGroupKeyCount) {
      // The return type of aggregate call need to be recomputed.
      // Since it might depend on the number of columns in GROUP BY.
      final RelDataType newType =
          oldGroupKeyCount == newGroupKeyCount
              && argList.equals(this.argList)
              && filterArg == this.filterArg
              ? type
              : null;
      return create(pos, aggFunction, distinct, approximate, ignoreNulls,
          rexList, argList, filterArg, distinctKeys, collation,
          newGroupKeyCount, input, newType, getName());
    }
  
    /**
     * Creates an equivalent AggregateCall that is adapted to a new input types
     * and/or number of columns in GROUP BY.
     *
     * @param input            Relation that will be input of Aggregate
     * @param argList          Argument indices of the new call in the input
     * @param filterArg        Index of the filter, or -1
     * @param oldHasEmptyGroup Whether old aggregate contains empty group
     * @param newHasEmptyGroup Whether new aggregate contains empty group
     * @return AggregateCall that suits new inputs and GROUP BY columns
     */
    public AggregateCall adaptTo(RelNode input, List<Integer> argList,
        int filterArg, boolean oldHasEmptyGroup, boolean newHasEmptyGroup) {
      // The return type of aggregate call need to be recomputed.
      // Since it might depend on the number of columns in GROUP BY.
      final RelDataType newType =
          oldHasEmptyGroup == newHasEmptyGroup
              && argList.equals(this.argList)
              && filterArg == this.filterArg
              ? type
              : null;
      return create(pos, aggFunction, distinct, approximate, ignoreNulls,
          rexList, argList, filterArg, distinctKeys, collation,
          newHasEmptyGroup, input, newType, getName());
    }
  
    /** Creates a copy of this aggregate call, applying a mapping to its
     * arguments. */
    public AggregateCall transform(Mappings.TargetMapping mapping) {
      return new AggregateCall(pos, 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), type, name);
    }
  }
  