You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/08/01 00:42:55 UTC

[18/50] [abbrv] incubator-calcite git commit: [CALCITE-704] FILTER clause for aggregate functions

[CALCITE-704] FILTER clause for aggregate functions

Implemented in enumerable convention for all aggregate functions, including user-defined.

Implemented "agg(DISTINCT arg [, ...]) FILTER (WHERE condition)" for all strict aggregate functions (i.e. aggregate functions that do not wish to see nulls).

Not implemented in JDBC, MongoDB and other adapters.

Not implemented in window functions.

Deprecate calling AggregateCall constructor; use create method instead.


Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/f5434a49
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/f5434a49
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/f5434a49

Branch: refs/heads/branch-release
Commit: f5434a495271a6887f7964423630405cf3d8c877
Parents: 91f0fca
Author: Julian Hyde <jh...@apache.org>
Authored: Wed Apr 29 13:47:26 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Wed Apr 29 14:40:21 2015 -0700

----------------------------------------------------------------------
 core/src/main/codegen/templates/Parser.jj       |  31 ++-
 .../adapter/enumerable/AggAddContext.java       |   6 +
 .../adapter/enumerable/EnumerableAggregate.java |  37 +--
 .../adapter/enumerable/EnumerableWindow.java    |   4 +
 .../enumerable/StrictAggImplementor.java        |  18 +-
 .../apache/calcite/adapter/jdbc/JdbcRules.java  |  18 ++
 .../calcite/interpreter/AggregateNode.java      |  15 +-
 .../org/apache/calcite/plan/RelOptUtil.java     |  15 +-
 .../calcite/plan/SubstitutionVisitor.java       |   7 +-
 .../org/apache/calcite/rel/core/Aggregate.java  |   3 +
 .../apache/calcite/rel/core/AggregateCall.java  | 101 ++++++--
 .../org/apache/calcite/rel/core/Window.java     |   9 +-
 .../calcite/rel/externalize/RelJsonReader.java  |   4 +-
 .../AggregateExpandDistinctAggregatesRule.java  | 113 +++++----
 .../rel/rules/AggregateFilterTransposeRule.java |   5 +-
 .../rel/rules/AggregateProjectMergeRule.java    |  12 +-
 .../AggregateProjectPullUpConstantsRule.java    |   9 +-
 .../rel/rules/AggregateReduceFunctionsRule.java |  21 +-
 .../java/org/apache/calcite/rex/RexBuilder.java |   2 +-
 .../apache/calcite/runtime/CalciteResource.java |   6 +
 .../org/apache/calcite/sql/SqlAggFunction.java  |   2 +-
 .../apache/calcite/sql/SqlFilterOperator.java   | 121 +++++++++
 .../java/org/apache/calcite/sql/SqlKind.java    |   5 +
 .../org/apache/calcite/sql/SqlOverOperator.java |   2 +-
 .../calcite/sql/fun/SqlStdOperatorTable.java    |   5 +
 .../apache/calcite/sql/validate/AggChecker.java |   4 +
 .../calcite/sql/validate/SqlValidator.java      |   6 +-
 .../calcite/sql/validate/SqlValidatorImpl.java  |  13 +-
 .../apache/calcite/sql2rel/RelDecorrelator.java |  19 +-
 .../apache/calcite/sql2rel/RelFieldTrimmer.java |  17 +-
 .../calcite/sql2rel/SqlToRelConverter.java      | 253 ++++++++++---------
 .../main/java/org/apache/calcite/util/Bug.java  |   8 +-
 .../apache/calcite/util/mapping/Mappings.java   |  10 +-
 .../calcite/runtime/CalciteResource.properties  |   2 +
 .../org/apache/calcite/plan/RelWriterTest.java  |  11 +-
 .../plan/volcano/TraitPropagationTest.java      |   5 +-
 .../calcite/sql/parser/SqlParserTest.java       |  11 +
 .../java/org/apache/calcite/test/JdbcTest.java  |  71 ++++++
 .../calcite/test/SqlToRelConverterTest.java     |   6 +
 .../apache/calcite/test/SqlValidatorTest.java   |  22 ++
 .../calcite/test/SqlToRelConverterTest.xml      |  12 +
 core/src/test/resources/sql/agg.oq              | 122 +++++++++
 doc/REFERENCE.md                                |  14 +
 43 files changed, 861 insertions(+), 316 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/codegen/templates/Parser.jj
----------------------------------------------------------------------
diff --git a/core/src/main/codegen/templates/Parser.jj b/core/src/main/codegen/templates/Parser.jj
index 6b62ce3..36f9e05 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -4101,8 +4101,9 @@ SqlNode NamedFunctionCall() :
     List<SqlNode> args;
     SqlParserPos pos;
     SqlParserPos starPos;
+    SqlParserPos filterPos = null;
+    SqlNode filter = null;
     SqlParserPos overPos = null;
-    boolean over = false;
     SqlCall function = null;
     SqlFunctionCategory funcType = SqlFunctionCategory.USER_DEFINED_FUNCTION;
     SqlNode e = null;
@@ -4138,10 +4139,17 @@ SqlNode NamedFunctionCall() :
                 args.remove(0);
             }
         )
-        [ <OVER>
+        [
+            <FILTER> { filterPos = getPos(); }
+            <LPAREN>
+            <WHERE>
+            filter = Expression(ExprContext.ACCEPT_SUBQUERY)
+            <RPAREN>  { filterPos = filterPos.plus(getPos()); }
+        ]
+        [
+            <OVER>
             {
                 overPos = getPos();
-                over = true;
                 pos = pos.plus(overPos);
             }
             (
@@ -4154,17 +4162,20 @@ SqlNode NamedFunctionCall() :
                 qualifiedName, pos, funcType, quantifier,
                 SqlParserUtil.toNodeArray(args));
 
-            if (over) {
+            if (filter != null) {
+                function = SqlStdOperatorTable.FILTER.createCall(filterPos,
+                    function, filter);
+            }
+            if (overPos != null) {
                 if (id != null) {
-                    return SqlStdOperatorTable.OVER.createCall(
-                        overPos, new SqlNode[] {function, id});
+                    function = SqlStdOperatorTable.OVER.createCall(overPos,
+                        function, id);
                 } else {
-                    return SqlStdOperatorTable.OVER.createCall(
-                        overPos, new SqlNode[] { function, e });
+                    function = SqlStdOperatorTable.OVER.createCall(overPos,
+                        function, e);
                 }
-            } else {
-                return function;
             }
+            return function;
         }
     )
 }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/adapter/enumerable/AggAddContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggAddContext.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggAddContext.java
index 9f8f595..4da0447 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/AggAddContext.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/AggAddContext.java
@@ -38,6 +38,12 @@ public interface AggAddContext extends AggResultContext {
   List<RexNode> rexArguments();
 
   /**
+   * Returns {@link org.apache.calcite.rex.RexNode} representation of the
+   * filter, or null.
+   */
+  RexNode rexFilterArgument();
+
+  /**
    * Returns Linq4j form of arguments.
    * The resulting value is equivalent to
    * {@code rowTranslator().translateList(rexArguments())}.

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java
index 38f3ee9..60f9b11 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableAggregate.java
@@ -19,6 +19,7 @@ package org.apache.calcite.adapter.enumerable;
 import org.apache.calcite.adapter.enumerable.impl.AggAddContextImpl;
 import org.apache.calcite.adapter.enumerable.impl.AggResultContextImpl;
 import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.linq4j.function.Function0;
 import org.apache.calcite.linq4j.function.Function1;
 import org.apache.calcite.linq4j.function.Function2;
@@ -177,12 +178,9 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
     final int groupCount = getGroupCount();
     final int indicatorCount = getIndicatorCount();
 
-    final List<AggImpState> aggs =
-        new ArrayList<AggImpState>(aggCalls.size());
-
-    for (int i = 0; i < aggCalls.size(); i++) {
-      AggregateCall call = aggCalls.get(i);
-      aggs.add(new AggImpState(i, call, false));
+    final List<AggImpState> aggs = new ArrayList<>(aggCalls.size());
+    for (Ord<AggregateCall> call : Ord.zip(aggCalls)) {
+      aggs.add(new AggImpState(call.i, call.e, false));
     }
 
     // Function0<Object[]> accumulatorInitializer =
@@ -191,11 +189,10 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
     //             return new Object[] {0, 0};
     //         }
     //     };
-    final List<Expression> initExpressions =
-        new ArrayList<Expression>();
+    final List<Expression> initExpressions = new ArrayList<>();
     final BlockBuilder initBlock = new BlockBuilder();
 
-    final List<Type> aggStateTypes = new ArrayList<Type>();
+    final List<Type> aggStateTypes = new ArrayList<>();
     for (final AggImpState agg : aggs) {
       agg.context =
           new AggContext() {
@@ -230,8 +227,7 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
 
       aggStateTypes.addAll(state);
 
-      final List<Expression> decls =
-          new ArrayList<Expression>(state.size());
+      final List<Expression> decls = new ArrayList<>(state.size());
       for (int i = 0; i < state.size(); i++) {
         String aggName = "a" + agg.aggIdx;
         if (CalcitePrepareImpl.DEBUG) {
@@ -281,9 +277,8 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
     for (int i = 0, stateOffset = 0; i < aggs.size(); i++) {
       final AggImpState agg = aggs.get(i);
 
-      int stateSize = agg.state.size();
-      List<Expression> accumulator =
-          new ArrayList<Expression>(stateSize);
+      final int stateSize = agg.state.size();
+      final List<Expression> accumulator = new ArrayList<>(stateSize);
       for (int j = 0; j < stateSize; j++) {
         accumulator.add(accPhysType.fieldReference(acc_, j + stateOffset));
       }
@@ -296,14 +291,20 @@ public class EnumerableAggregate extends Aggregate implements EnumerableRel {
             public List<RexNode> rexArguments() {
               List<RelDataTypeField> inputTypes =
                   inputPhysType.getRowType().getFieldList();
-              List<RexNode> args = new ArrayList<RexNode>();
-              for (Integer index : agg.call.getArgList()) {
-                args.add(
-                    new RexInputRef(index, inputTypes.get(index).getType()));
+              List<RexNode> args = new ArrayList<>();
+              for (int index : agg.call.getArgList()) {
+                args.add(RexInputRef.of(index, inputTypes));
               }
               return args;
             }
 
+            public RexNode rexFilterArgument() {
+              return agg.call.filterArg < 0
+                  ? null
+                  : RexInputRef.of(agg.call.filterArg,
+                      inputPhysType.getRowType());
+            }
+
             public RexToLixTranslator rowTranslator() {
               return RexToLixTranslator.forAggregation(typeFactory,
                   currentBlock(),

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java
index 7650c62..15f77f5 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java
@@ -828,6 +828,10 @@ public class EnumerableWindow extends Window implements EnumerableRel {
             public List<RexNode> rexArguments() {
               return rexArguments.apply(agg);
             }
+
+            public RexNode rexFilterArgument() {
+              return null; // REVIEW
+            }
           };
       agg.implementor.implementAdd(agg.context, addContext);
     }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java
index 783dd43..8b463e8 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/StrictAggImplementor.java
@@ -74,7 +74,7 @@ public abstract class StrictAggImplementor implements AggImplementor {
     }
     trackNullsPerRow = !(info instanceof WinAggContext) || hasNullableArgs;
 
-    List<Type> res = new ArrayList<Type>(subState.size() + 1);
+    List<Type> res = new ArrayList<>(subState.size() + 1);
     res.addAll(subState);
     res.add(boolean.class); // has not nulls
     return res;
@@ -111,10 +111,16 @@ public abstract class StrictAggImplementor implements AggImplementor {
   }
 
   public final void implementAdd(AggContext info, final AggAddContext add) {
-    List<RexNode> args = add.rexArguments();
-    RexToLixTranslator translator = add.rowTranslator();
-    List<Expression> conditions =
-      translator.translateList(args, RexImpTable.NullAs.IS_NOT_NULL);
+    final List<RexNode> args = add.rexArguments();
+    final RexToLixTranslator translator = add.rowTranslator();
+    final List<Expression> conditions = new ArrayList<>();
+    conditions.addAll(
+        translator.translateList(args, RexImpTable.NullAs.IS_NOT_NULL));
+    if (add.rexFilterArgument() != null) {
+      conditions.add(
+          translator.translate(add.rexFilterArgument(),
+              RexImpTable.NullAs.FALSE));
+    }
     Expression condition = Expressions.foldAnd(conditions);
     if (Expressions.constant(false).equals(condition)) {
       return;
@@ -137,7 +143,7 @@ public abstract class StrictAggImplementor implements AggImplementor {
       return;
     }
 
-    final Map<RexNode, Boolean> nullables = new HashMap<RexNode, Boolean>();
+    final Map<RexNode, Boolean> nullables = new HashMap<>();
     for (RexNode arg : args) {
       if (translator.isNullable(arg)) {
         nullables.put(arg, false);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
index d519029..307434f 100644
--- a/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
+++ b/core/src/main/java/org/apache/calcite/adapter/jdbc/JdbcRules.java
@@ -70,6 +70,7 @@ import org.apache.calcite.rex.RexProgram;
 import org.apache.calcite.schema.ModifiableTable;
 import org.apache.calcite.sql.JoinConditionType;
 import org.apache.calcite.sql.JoinType;
+import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlDialect;
 import org.apache.calcite.sql.SqlFunction;
@@ -97,6 +98,7 @@ import org.apache.calcite.util.trace.CalciteTrace;
 import com.google.common.collect.ImmutableList;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -678,6 +680,22 @@ public class JdbcRules {
         throws InvalidRelException {
       super(cluster, traitSet, input, indicator, groupSet, groupSets, aggCalls);
       assert getConvention() instanceof JdbcConvention;
+      for (AggregateCall aggCall : aggCalls) {
+        if (!canImplement(aggCall.getAggregation())) {
+          throw new InvalidRelException("cannot implement aggregate function "
+              + aggCall.getAggregation());
+        }
+      }
+    }
+
+    /** Returns whether this JDBC data source can implement a given aggregate
+     * function. */
+    private boolean canImplement(SqlAggFunction aggregation) {
+      return Arrays.asList(SqlStdOperatorTable.COUNT,
+          SqlStdOperatorTable.SUM,
+          SqlStdOperatorTable.SUM0,
+          SqlStdOperatorTable.MIN,
+          SqlStdOperatorTable.MAX).contains(aggregation);
     }
 
     @Override public JdbcAggregate copy(RelTraitSet traitSet, RelNode input,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java b/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java
index 1dff33a..f17df8f 100644
--- a/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java
+++ b/core/src/main/java/org/apache/calcite/interpreter/AggregateNode.java
@@ -33,7 +33,6 @@ import org.apache.calcite.linq4j.tree.ParameterExpression;
 import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.schema.impl.AggregateFunctionImpl;
@@ -145,16 +144,20 @@ public class AggregateNode extends AbstractSingleNode<Aggregate> {
       AggAddContext addContext =
           new AggAddContextImpl(builder2, accumulator) {
             public List<RexNode> rexArguments() {
-              List<RelDataTypeField> inputTypes =
-                  inputPhysType.getRowType().getFieldList();
               List<RexNode> args = new ArrayList<RexNode>();
-              for (Integer index : agg.call.getArgList()) {
-                args.add(
-                    new RexInputRef(index, inputTypes.get(index).getType()));
+              for (int index : agg.call.getArgList()) {
+                args.add(RexInputRef.of(index, inputPhysType.getRowType()));
               }
               return args;
             }
 
+            public RexNode rexFilterArgument() {
+              return agg.call.filterArg < 0
+                  ? null
+                  : RexInputRef.of(agg.call.filterArg,
+                      inputPhysType.getRowType());
+            }
+
             public RexToLixTranslator rowTranslator() {
               return RexToLixTranslator.forAggregation(typeFactory,
                   currentBlock(),

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
----------------------------------------------------------------------
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 cf68db9..3deb4e4 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -694,19 +694,10 @@ public abstract class RelOptUtil {
     final List<AggregateCall> aggCalls = new ArrayList<>();
 
     for (int i = 0; i < aggCallCnt; i++) {
-      RelDataType returnType =
-          SqlStdOperatorTable.SINGLE_VALUE.inferReturnType(
-              cluster.getRexBuilder().getTypeFactory(),
-              ImmutableList.of(
-                  rel.getRowType().getFieldList().get(i).getType()));
-
       aggCalls.add(
-          new AggregateCall(
-              SqlStdOperatorTable.SINGLE_VALUE,
-              false,
-              ImmutableList.of(i),
-              returnType,
-              null));
+          AggregateCall.create(
+              SqlStdOperatorTable.SINGLE_VALUE, false, ImmutableList.of(i), -1,
+              0, rel, null, null));
     }
 
     return LogicalAggregate.create(rel, false,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
----------------------------------------------------------------------
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 e0ad056..410339f 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -1166,7 +1166,8 @@ public class SubstitutionVisitor {
     return Lists.transform(aggCallList,
         new Function<AggregateCall, AggregateCall>() {
           public AggregateCall apply(AggregateCall call) {
-            return call.copy(Mappings.apply2(mapping, call.getArgList()));
+            return call.copy(Mappings.apply2(mapping, call.getArgList()),
+                Mappings.apply(mapping, call.filterArg));
           }
         });
   }
@@ -1214,9 +1215,9 @@ public class SubstitutionVisitor {
           return null;
         }
         aggregateCalls.add(
-            new AggregateCall(getRollup(aggregateCall.getAggregation()),
+            AggregateCall.create(getRollup(aggregateCall.getAggregation()),
                 aggregateCall.isDistinct(),
-                ImmutableList.of(target.groupSet.cardinality() + i),
+                ImmutableList.of(target.groupSet.cardinality() + i), -1,
                 aggregateCall.type, aggregateCall.name));
       }
       result = MutableAggregate.of(target, false, groupSet.build(), null,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java
----------------------------------------------------------------------
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 d84ef8f..a483595 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
@@ -137,6 +137,9 @@ public abstract class Aggregate extends SingleRel {
     assert groupSet.length() <= child.getRowType().getFieldCount();
     for (AggregateCall aggCall : aggCalls) {
       assert typeMatchesInferred(aggCall, true);
+      assert aggCall.filterArg < 0
+          || child.getRowType().getFieldList().get(aggCall.filterArg).getType()
+              .getSqlTypeName() == SqlTypeName.BOOLEAN;
     }
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
----------------------------------------------------------------------
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 fa1620c..0589e58 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
@@ -23,6 +23,7 @@ import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.util.mapping.Mappings;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
 import java.util.List;
@@ -43,6 +44,7 @@ public class AggregateCall {
   // 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;
 
   //~ Constructors -----------------------------------------------------------
 
@@ -55,29 +57,56 @@ public class AggregateCall {
    * @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.type = type;
-    this.name = name;
-    assert aggFunction != null;
-    assert argList != null;
-    assert type != null;
-    this.aggFunction = aggFunction;
+    this(aggFunction, distinct, argList, -1, type, name);
+  }
 
+  /**
+   * Creates an AggregateCall.
+   *
+   * @param aggFunction Aggregate function
+   * @param distinct    Whether distinct
+   * @param argList     List of ordinals of arguments
+   * @param filterArg   Ordinal of filter argument, or -1
+   * @param type        Result type
+   * @param name        Name (may be null)
+   */
+  private AggregateCall(
+      SqlAggFunction aggFunction,
+      boolean distinct,
+      List<Integer> argList,
+      int filterArg,
+      RelDataType type,
+      String name) {
+    this.type = Preconditions.checkNotNull(type);
+    this.name = name;
+    this.aggFunction = Preconditions.checkNotNull(aggFunction);
     this.argList = ImmutableList.copyOf(argList);
+    this.filterArg = filterArg;
     this.distinct = distinct;
   }
 
   //~ Methods ----------------------------------------------------------------
 
   /** 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, List<Integer> argList, int groupCount, RelNode input,
       RelDataType type, String name) {
+    return create(aggFunction, distinct, argList, -1, groupCount, input, type,
+        name);
+  }
+
+  /** Creates an AggregateCall, inferring its type if {@code type} is null. */
+  public static AggregateCall create(SqlAggFunction aggFunction,
+      boolean distinct, List<Integer> argList, int filterArg, int groupCount,
+      RelNode input, RelDataType type, String name) {
     if (type == null) {
       final RelDataTypeFactory typeFactory =
           input.getCluster().getTypeFactory();
@@ -88,7 +117,15 @@ public class AggregateCall {
               groupCount);
       type = aggFunction.inferReturnType(callBinding);
     }
-    return new AggregateCall(aggFunction, distinct, argList, type, name);
+    return create(aggFunction, distinct, argList, filterArg, type, name);
+  }
+
+  /** Creates an AggregateCall. */
+  public static AggregateCall create(SqlAggFunction aggFunction,
+      boolean distinct, List<Integer> argList, int filterArg, RelDataType type,
+      String name) {
+    return new AggregateCall(aggFunction, distinct, argList, filterArg, type,
+        name);
   }
 
   /**
@@ -146,7 +183,8 @@ public class AggregateCall {
    */
   public AggregateCall rename(String name) {
     // no need to copy argList - already immutable
-    return new AggregateCall(aggFunction, distinct, argList, type, name);
+    return new AggregateCall(aggFunction, distinct, argList, filterArg, type,
+        name);
   }
 
   public String toString() {
@@ -164,22 +202,25 @@ public class AggregateCall {
       buf.append(arg);
     }
     buf.append(")");
+    if (filterArg >= 0) {
+      buf.append(" FILTER $");
+      buf.append(filterArg);
+    }
     return buf.toString();
   }
 
-  // override Object
-  public boolean equals(Object o) {
+  @Override public boolean equals(Object o) {
     if (!(o instanceof AggregateCall)) {
       return false;
     }
     AggregateCall other = (AggregateCall) o;
     return aggFunction.equals(other.aggFunction)
         && (distinct == other.distinct)
-        && argList.equals(other.argList);
+        && argList.equals(other.argList)
+        && filterArg == other.filterArg;
   }
 
-  // override Object
-  public int hashCode() {
+  @Override public int hashCode() {
     return aggFunction.hashCode() + argList.hashCode();
   }
 
@@ -193,10 +234,9 @@ public class AggregateCall {
     final RelDataType rowType = aggregateRelBase.getInput().getRowType();
 
     return new Aggregate.AggCallBinding(
-        aggregateRelBase.getCluster().getTypeFactory(),
-        (SqlAggFunction) aggFunction,
+        aggregateRelBase.getCluster().getTypeFactory(), aggFunction,
         SqlTypeUtil.projectTypes(rowType, argList),
-        aggregateRelBase.getGroupCount());
+        filterArg >= 0 ? 0 : aggregateRelBase.getGroupCount());
   }
 
   /**
@@ -205,8 +245,14 @@ public class AggregateCall {
    * @param args Arguments
    * @return AggregateCall that suits new inputs and GROUP BY columns
    */
+  public AggregateCall copy(List<Integer> args, int filterArg) {
+    return new AggregateCall(aggFunction, distinct, args, filterArg, type,
+        name);
+  }
+
+  @Deprecated // to be removed before 2.0
   public AggregateCall copy(List<Integer> args) {
-    return new AggregateCall(aggFunction, distinct, args, type, name);
+    return copy(args, filterArg);
   }
 
   /**
@@ -214,26 +260,31 @@ public class AggregateCall {
    * and/or number of columns in GROUP BY.
    *
    * @param input relation that will be used as a child of aggregate
-   * @param aggArgs argument indices of the new call in the input
+   * @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
    */
-  public AggregateCall adaptTo(RelNode input, List<Integer> aggArgs,
-      int oldGroupKeyCount, int newGroupKeyCount) {
-    final SqlAggFunction sqlAgg = (SqlAggFunction) aggFunction;
+  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 ? type : null;
-    return create(sqlAgg, distinct, aggArgs, newGroupKeyCount, input, newType,
-        getName());
+        oldGroupKeyCount == newGroupKeyCount
+            && argList.equals(this.argList)
+            && filterArg == this.filterArg
+            ? type
+            : null;
+    return create(aggFunction, distinct, argList, filterArg, 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.permute(argList, mapping));
+    return copy(Mappings.permute(argList, mapping),
+        Mappings.apply(mapping, filterArg));
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/rel/core/Window.java
----------------------------------------------------------------------
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 df7510d..103b24f 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
@@ -313,12 +313,9 @@ public abstract class Window extends SingleRel {
 
         public AggregateCall get(int index) {
           final RexWinAggCall aggCall = aggCalls.get(index);
-          return new AggregateCall(
-              (SqlAggFunction) aggCall.getOperator(),
-              false,
-              getProjectOrdinals(aggCall.getOperands()),
-              aggCall.getType(),
-              fieldNames.get(aggCall.ordinal));
+          return AggregateCall.create((SqlAggFunction) aggCall.getOperator(),
+              false, getProjectOrdinals(aggCall.getOperands()), -1,
+              aggCall.getType(), fieldNames.get(aggCall.ordinal));
         }
       };
     }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/rel/externalize/RelJsonReader.java
----------------------------------------------------------------------
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 e8740d7..12bf70c 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
@@ -267,9 +267,11 @@ public class RelJsonReader {
         relJson.toAggregation(aggName, jsonAggCall);
     final Boolean distinct = (Boolean) jsonAggCall.get("distinct");
     final List<Integer> operands = (List<Integer>) jsonAggCall.get("operands");
+    final Integer filterOperand = (Integer) jsonAggCall.get("filter");
     final RelDataType type =
         relJson.toType(cluster.getTypeFactory(), jsonAggCall.get("type"));
-    return new AggregateCall(aggregation, distinct, operands, type, null);
+    return AggregateCall.create(aggregation, distinct, operands,
+        filterOperand == null ? -1 : filterOperand, type, null);
   }
 
   private RelNode lookupInput(String jsonInput) {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/rel/rules/AggregateExpandDistinctAggregatesRule.java
----------------------------------------------------------------------
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 3afe7cc..e05c781 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
@@ -37,6 +37,7 @@ import org.apache.calcite.util.Util;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
@@ -92,27 +93,22 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
     // Find all of the agg expressions. We use a LinkedHashSet to ensure
     // determinism.
     int nonDistinctCount = 0;
-    Set<List<Integer>> argListSets = new LinkedHashSet<List<Integer>>();
+    final Set<Pair<List<Integer>, Integer>> argLists = new LinkedHashSet<>();
     for (AggregateCall aggCall : aggregate.getAggCallList()) {
       if (!aggCall.isDistinct()) {
         ++nonDistinctCount;
         continue;
       }
-      ArrayList<Integer> argList = new ArrayList<Integer>();
-      for (Integer arg : aggCall.getArgList()) {
-        argList.add(arg);
-      }
-      argListSets.add(argList);
+      argLists.add(Pair.of(aggCall.getArgList(), aggCall.filterArg));
     }
-    Util.permAssert(argListSets.size() > 0, "containsDistinctCall lied");
+    Util.permAssert(argLists.size() > 0, "containsDistinctCall lied");
 
     // If all of the agg expressions are distinct and have the same
     // arguments then we can use a more efficient form.
-    if ((nonDistinctCount == 0) && (argListSets.size() == 1)) {
-      RelNode converted =
-          convertMonopole(
-              aggregate,
-              argListSets.iterator().next());
+    if (nonDistinctCount == 0 && argLists.size() == 1) {
+      final Pair<List<Integer>, Integer> pair =
+          Iterables.getOnlyElement(argLists);
+      RelNode converted = convertMonopole(aggregate, pair.left, pair.right);
       call.transformTo(converted);
       return;
     }
@@ -121,7 +117,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
     // Initially, the expressions point to the input field.
     final List<RelDataTypeField> aggFields =
         aggregate.getRowType().getFieldList();
-    final List<RexInputRef> refs = new ArrayList<RexInputRef>();
+    final List<RexInputRef> refs = new ArrayList<>();
     final List<String> fieldNames = aggregate.getRowType().getFieldNames();
     final ImmutableBitSet groupSet = aggregate.getGroupSet();
     final int groupAndIndicatorCount =
@@ -131,7 +127,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
     }
 
     // Aggregate the original relation, including any non-distinct aggregates.
-    List<AggregateCall> newAggCallList = new ArrayList<AggregateCall>();
+    final List<AggregateCall> newAggCallList = new ArrayList<>();
     int i = -1;
     for (AggregateCall aggCall : aggregate.getAggCallList()) {
       ++i;
@@ -160,8 +156,8 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
 
     // For each set of operands, find and rewrite all calls which have that
     // set of operands.
-    for (List<Integer> argList : argListSets) {
-      rel = doRewrite(aggregate, rel, argList, refs);
+    for (Pair<List<Integer>, Integer> argList : argLists) {
+      rel = doRewrite(aggregate, rel, argList.left, argList.right, refs);
     }
 
     rel = RelOptUtil.createProject(rel, refs, fieldNames);
@@ -174,9 +170,8 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
    * distinct aggregate function (or perhaps several over the same arguments)
    * and no non-distinct aggregate functions.
    */
-  private RelNode convertMonopole(
-      Aggregate aggregate,
-      List<Integer> argList) {
+  private RelNode convertMonopole(Aggregate aggregate, List<Integer> argList,
+      int filterArg) {
     // For example,
     //    SELECT deptno, COUNT(DISTINCT sal), SUM(DISTINCT sal)
     //    FROM emp
@@ -192,9 +187,9 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
 
     // Project the columns of the GROUP BY plus the arguments
     // to the agg function.
-    Map<Integer, Integer> sourceOf = new HashMap<Integer, Integer>();
+    final Map<Integer, Integer> sourceOf = new HashMap<>();
     final Aggregate distinct =
-        createSelectDistinct(aggregate, argList, sourceOf);
+        createSelectDistinct(aggregate, argList, filterArg, sourceOf);
 
     // Create an aggregate on top, with the new aggregate list.
     final List<AggregateCall> newAggCalls =
@@ -220,16 +215,13 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
    *                  first distinct aggregate in a query with no non-distinct
    *                  aggregates)
    * @param argList   Arguments to the distinct aggregate function
+   * @param filterArg Argument that filters input to aggregate function, or -1
    * @param refs      Array of expressions which will be the projected by the
    *                  result of this rule. Those relating to this arg list will
-   *                  be modified
-   * @return Relational expression
+   *                  be modified  @return Relational expression
    */
-  private RelNode doRewrite(
-      Aggregate aggregate,
-      RelNode left,
-      List<Integer> argList,
-      List<RexInputRef> refs) {
+  private RelNode doRewrite(Aggregate aggregate, RelNode left,
+      List<Integer> argList, int filterArg, List<RexInputRef> refs) {
     final RexBuilder rexBuilder = aggregate.getCluster().getRexBuilder();
     final List<RelDataTypeField> leftFields;
     if (left == null) {
@@ -281,9 +273,9 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
 
     // Project the columns of the GROUP BY plus the arguments
     // to the agg function.
-    Map<Integer, Integer> sourceOf = new HashMap<Integer, Integer>();
+    final Map<Integer, Integer> sourceOf = new HashMap<>();
     final Aggregate distinct =
-        createSelectDistinct(aggregate, argList, sourceOf);
+        createSelectDistinct(aggregate, argList, filterArg, sourceOf);
 
     // Now compute the aggregate functions on top of the distinct dataset.
     // Each distinct agg becomes a non-distinct call to the corresponding
@@ -291,7 +283,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
     //   "COUNT(DISTINCT e.sal)"
     // becomes
     //   "COUNT(distinct_e.sal)".
-    List<AggregateCall> aggCallList = new ArrayList<AggregateCall>();
+    final List<AggregateCall> aggCallList = new ArrayList<>();
     final List<AggregateCall> aggCalls = aggregate.getAggCallList();
 
     final int groupAndIndicatorCount =
@@ -313,18 +305,16 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
 
       // Re-map arguments.
       final int argCount = aggCall.getArgList().size();
-      final List<Integer> newArgs = new ArrayList<Integer>(argCount);
+      final List<Integer> newArgs = new ArrayList<>(argCount);
       for (int j = 0; j < argCount; j++) {
         final Integer arg = aggCall.getArgList().get(j);
         newArgs.add(sourceOf.get(arg));
       }
+      final int newFilterArg =
+          aggCall.filterArg >= 0 ? sourceOf.get(aggCall.filterArg) : -1;
       final AggregateCall newAggCall =
-          new AggregateCall(
-              aggCall.getAggregation(),
-              false,
-              newArgs,
-              aggCall.getType(),
-              aggCall.getName());
+          AggregateCall.create(aggCall.getAggregation(), false, newArgs,
+              newFilterArg, aggCall.getType(), aggCall.getName());
       assert refs.get(i) == null;
       if (left == null) {
         refs.set(i,
@@ -385,7 +375,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
       final AggregateCall aggCall = newAggCalls.get(i);
 
       // Ignore agg calls which are not distinct or have the wrong set
-      // arguments. If we're rewriting aggs whose args are {sal}, we will
+      // arguments. If we're rewriting aggregates whose args are {sal}, we will
       // rewrite COUNT(DISTINCT sal) and SUM(DISTINCT sal) but ignore
       // COUNT(DISTINCT gender) or SUM(sal).
       if (!aggCall.isDistinct()) {
@@ -397,18 +387,14 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
 
       // Re-map arguments.
       final int argCount = aggCall.getArgList().size();
-      final List<Integer> newArgs = new ArrayList<Integer>(argCount);
+      final List<Integer> newArgs = new ArrayList<>(argCount);
       for (int j = 0; j < argCount; j++) {
         final Integer arg = aggCall.getArgList().get(j);
         newArgs.add(sourceOf.get(arg));
       }
       final AggregateCall newAggCall =
-          new AggregateCall(
-              aggCall.getAggregation(),
-              false,
-              newArgs,
-              aggCall.getType(),
-              aggCall.getName());
+          AggregateCall.create(aggCall.getAggregation(), false, newArgs, -1,
+              aggCall.getType(), aggCall.getName());
       newAggCalls.set(i, newAggCall);
     }
   }
@@ -427,7 +413,7 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
    * from t group by f0</pre>
    * </blockquote>
    *
-   * and the arglist
+   * and the argument list
    *
    * <blockquote>{2}</blockquote>
    *
@@ -444,17 +430,15 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
    *
    * @param aggregate Aggregate relational expression
    * @param argList   Ordinals of columns to make distinct
+   * @param filterArg Ordinal of column to filter on, or -1
    * @param sourceOf  Out parameter, is populated with a map of where each
    *                  output field came from
    * @return Aggregate relational expression which projects the required
    * columns
    */
-  private static Aggregate createSelectDistinct(
-      Aggregate aggregate,
-      List<Integer> argList,
-      Map<Integer, Integer> sourceOf) {
-    final List<Pair<RexNode, String>> projects =
-        new ArrayList<Pair<RexNode, String>>();
+  private static Aggregate createSelectDistinct(Aggregate aggregate,
+      List<Integer> argList, int filterArg, Map<Integer, Integer> sourceOf) {
+    final List<Pair<RexNode, String>> projects = new ArrayList<>();
     final RelNode child = aggregate.getInput();
     final List<RelDataTypeField> childFields =
         child.getRowType().getFieldList();
@@ -463,6 +447,29 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule {
       projects.add(RexInputRef.of2(i, childFields));
     }
     for (Integer arg : argList) {
+      if (filterArg >= 0) {
+        // Implement
+        //   agg(DISTINCT arg) FILTER $f
+        // by generating
+        //   SELECT DISTINCT ... CASE WHEN $f THEN arg ELSE NULL END AS arg
+        // and then applying
+        //   agg(arg)
+        // as usual.
+        //
+        // It works except for (rare) agg functions that need to see null
+        // values.
+        final RexBuilder rexBuilder = aggregate.getCluster().getRexBuilder();
+        final RexInputRef filterRef = RexInputRef.of(filterArg, childFields);
+        final Pair<RexNode, String> argRef = RexInputRef.of2(arg, childFields);
+        RexNode condition =
+            rexBuilder.makeCall(SqlStdOperatorTable.CASE, filterRef,
+                argRef.left,
+                rexBuilder.ensureType(argRef.left.getType(),
+                    rexBuilder.constantNull(), true));
+        sourceOf.put(arg, projects.size());
+        projects.add(Pair.of(condition, "i$" + argRef.right));
+        continue;
+      }
       if (sourceOf.get(arg) != null) {
         continue;
       }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
----------------------------------------------------------------------
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 dfa325d..302a02e 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
@@ -137,8 +137,9 @@ public class AggregateFilterTransposeRule extends RelOptRule {
           return;
         }
         topAggCallList.add(
-            new AggregateCall(rollup, aggregateCall.isDistinct(),
-                ImmutableList.of(i++), aggregateCall.type, aggregateCall.name));
+            AggregateCall.create(rollup, aggregateCall.isDistinct(),
+                ImmutableList.of(i++), -1, aggregateCall.type,
+                aggregateCall.name));
       }
       final Aggregate topAggregate =
           aggregate.copy(aggregate.getTraitSet(), newFilter,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
index 91e33f2..e581e1a 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectMergeRule.java
@@ -103,7 +103,17 @@ public class AggregateProjectMergeRule extends RelOptRule {
           return null;
         }
       }
-      aggCalls.add(aggregateCall.copy(newArgs.build()));
+      final int newFilterArg;
+      if (aggregateCall.filterArg >= 0) {
+        final RexNode rex = project.getProjects().get(aggregateCall.filterArg);
+        if (!(rex instanceof RexInputRef)) {
+          return null;
+        }
+        newFilterArg = ((RexInputRef) rex).getIndex();
+      } else {
+        newFilterArg = -1;
+      }
+      aggCalls.add(aggregateCall.copy(newArgs.build(), newFilterArg));
     }
 
     final Aggregate newAggregate =

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
index c7667a6..75bb99e 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateProjectPullUpConstantsRule.java
@@ -136,8 +136,8 @@ public class AggregateProjectPullUpConstantsRule extends RelOptRule {
           new ArrayList<AggregateCall>();
       for (AggregateCall aggCall : aggregate.getAggCallList()) {
         newAggCalls.add(
-            aggCall.adaptTo(input, aggCall.getArgList(), groupCount,
-                newGroupCount));
+            aggCall.adaptTo(input, aggCall.getArgList(), aggCall.filterArg,
+                groupCount, newGroupCount));
       }
       newAggregate =
           LogicalAggregate.create(input, false,
@@ -176,8 +176,11 @@ public class AggregateProjectPullUpConstantsRule extends RelOptRule {
           final Integer arg = aggCall.getArgList().get(j);
           args.add(mapping.getTarget(arg));
         }
+        final int filterArg = aggCall.filterArg < 0 ? aggCall.filterArg
+            : mapping.getTarget(aggCall.filterArg);
         newAggCalls.add(
-            aggCall.adaptTo(project, args, groupCount, newGroupCount));
+            aggCall.adaptTo(project, args, filterArg, groupCount,
+                newGroupCount));
       }
 
       // Aggregate on projection.

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/rel/rules/AggregateReduceFunctionsRule.java
----------------------------------------------------------------------
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 95ca9c8..2a79f14 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
@@ -281,10 +281,10 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             avgInputType.isNullable() || nGroups == 0);
     SqlAggFunction sumAgg = new SqlSumAggFunction(sumType);
     AggregateCall sumCall =
-        new AggregateCall(
-            sumAgg,
+        AggregateCall.create(sumAgg,
             oldCall.isDistinct(),
             oldCall.getArgList(),
+            oldCall.filterArg,
             sumType,
             null);
     AggregateCall countCall =
@@ -292,6 +292,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             SqlStdOperatorTable.COUNT,
             oldCall.isDistinct(),
             oldCall.getArgList(),
+            oldCall.filterArg,
             oldAggRel.getGroupCount(),
             oldAggRel.getInput(),
             null,
@@ -338,17 +339,14 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
         typeFactory.createTypeWithNullability(
             argType, argType.isNullable());
     final AggregateCall sumZeroCall =
-        new AggregateCall(
-            SqlStdOperatorTable.SUM0,
-            oldCall.isDistinct(),
-            oldCall.getArgList(),
-            sumType,
-            null);
+        AggregateCall.create(SqlStdOperatorTable.SUM0, oldCall.isDistinct(),
+            oldCall.getArgList(), oldCall.filterArg, sumType, null);
     final AggregateCall countCall =
         AggregateCall.create(
             SqlStdOperatorTable.COUNT,
             oldCall.isDistinct(),
             oldCall.getArgList(),
+            oldCall.filterArg,
             oldAggRel.getGroupCount(),
             oldAggRel,
             null,
@@ -425,10 +423,11 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             argType,
             true);
     final AggregateCall sumArgSquaredAggCall =
-        new AggregateCall(
+        AggregateCall.create(
             new SqlSumAggFunction(sumType),
             oldCall.isDistinct(),
             ImmutableIntList.of(argSquaredOrdinal),
+            oldCall.filterArg,
             sumType,
             null);
     final RexNode sumArgSquared =
@@ -440,10 +439,11 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             ImmutableList.of(argType));
 
     final AggregateCall sumArgAggCall =
-        new AggregateCall(
+        AggregateCall.create(
             new SqlSumAggFunction(sumType),
             oldCall.isDistinct(),
             ImmutableIntList.of(argOrdinal),
+            oldCall.filterArg,
             sumType,
             null);
     final RexNode sumArg =
@@ -463,6 +463,7 @@ public class AggregateReduceFunctionsRule extends RelOptRule {
             SqlStdOperatorTable.COUNT,
             oldCall.isDistinct(),
             oldCall.getArgList(),
+            oldCall.filterArg,
             oldAggRel.getGroupCount(),
             oldAggRel.getInput(),
             null,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
index 32b16dc..335293a 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBuilder.java
@@ -290,7 +290,7 @@ public class RexBuilder {
       final List<Integer> args = aggCall.getArgList();
       final List<Integer> nullableArgs = nullableArgs(args, aggArgTypes);
       if (!nullableArgs.equals(args)) {
-        aggCall = aggCall.copy(nullableArgs);
+        aggCall = aggCall.copy(nullableArgs, aggCall.filterArg);
       }
     }
     RexNode rex = aggCallMapping.get(aggCall);

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index 245d00a..1e8badf 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -273,6 +273,9 @@ public interface CalciteResource {
   @BaseMessage("Aggregate expressions cannot be nested")
   ExInst<SqlValidatorException> nestedAggIllegal();
 
+  @BaseMessage("FILTER must not contain aggregate expression")
+  ExInst<SqlValidatorException> aggregateInFilterIllegal();
+
   @BaseMessage("Aggregate expression is illegal in ORDER BY clause of non-aggregating SELECT")
   ExInst<SqlValidatorException> aggregateIllegalInOrderBy();
 
@@ -285,6 +288,9 @@ public interface CalciteResource {
   @BaseMessage("OVER must be applied to aggregate function")
   ExInst<SqlValidatorException> overNonAggregate();
 
+  @BaseMessage("FILTER must be applied to aggregate function")
+  ExInst<SqlValidatorException> filterNonAggregate();
+
   @BaseMessage("Cannot override window attribute")
   ExInst<SqlValidatorException> cannotOverrideWindowAttribute();
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java b/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java
index 6949d94..4f51be2 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlAggFunction.java
@@ -72,7 +72,7 @@ public abstract class SqlAggFunction extends SqlFunction {
       SqlValidatorScope scope,
       SqlValidatorScope operandScope) {
     super.validateCall(call, validator, scope, operandScope);
-    validator.validateAggregateParams(call, scope);
+    validator.validateAggregateParams(call, null, scope);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java
new file mode 100644
index 0000000..5661da3
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java
@@ -0,0 +1,121 @@
+/*
+ * 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.sql;
+
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.sql.validate.SqlValidatorScope;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * An operator that applies a filter before rows are included in an aggregate
+ * function.
+ *
+ * <p>Operands are as follows:</p>
+ *
+ * <ul>
+ * <li>0: a call to an aggregate function ({@link SqlCall})
+ * <li>1: predicate
+ * </ul>
+ */
+public class SqlFilterOperator extends SqlBinaryOperator {
+  //~ Constructors -----------------------------------------------------------
+
+  public SqlFilterOperator() {
+    super("FILTER", SqlKind.FILTER, 2, true, ReturnTypes.ARG0_FORCE_NULLABLE,
+        null, OperandTypes.ANY_ANY);
+  }
+
+  //~ Methods ----------------------------------------------------------------
+
+
+  @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec,
+      int rightPrec) {
+    assert call.operandCount() == 2;
+    final SqlWriter.Frame frame =
+        writer.startList(SqlWriter.FrameTypeEnum.SIMPLE);
+    call.operand(0).unparse(writer, leftPrec, getLeftPrec());
+    writer.sep(getName());
+    writer.sep("(");
+    writer.sep("WHERE");
+    call.operand(1).unparse(writer, getRightPrec(), rightPrec);
+    writer.sep(")");
+    writer.endList(frame);
+  }
+
+  public void validateCall(
+      SqlCall call,
+      SqlValidator validator,
+      SqlValidatorScope scope,
+      SqlValidatorScope operandScope) {
+    assert call.getOperator() == this;
+    assert call.operandCount() == 2;
+    SqlCall aggCall = call.operand(0);
+    if (!aggCall.getOperator().isAggregator()) {
+      throw validator.newValidationError(aggCall,
+          RESOURCE.filterNonAggregate());
+    }
+    final SqlNode condition = call.operand(1);
+    validator.validateAggregateParams(aggCall, condition, scope);
+
+    final RelDataType type = validator.deriveType(scope, condition);
+    if (!SqlTypeUtil.inBooleanFamily(type)) {
+      throw validator.newValidationError(condition,
+          RESOURCE.condMustBeBoolean("FILTER"));
+    }
+  }
+
+  public RelDataType deriveType(
+      SqlValidator validator,
+      SqlValidatorScope scope,
+      SqlCall call) {
+    // Validate type of the inner aggregate call
+    validateOperands(validator, scope, call);
+
+    // Assume the first operand is an aggregate call and derive its type.
+    SqlNode agg = call.operand(0);
+
+    if (!(agg instanceof SqlCall)) {
+      throw new IllegalStateException("Argument to SqlOverOperator"
+          + " should be SqlCall, got " + agg.getClass() + ": " + agg);
+    }
+
+    final SqlCall aggCall = (SqlCall) agg;
+
+    // Pretend that group-count is 0. This tells the aggregate function that it
+    // might be invoked with 0 rows in a group. Most aggregate functions will
+    // return NULL in this case.
+    SqlCallBinding opBinding = new SqlCallBinding(validator, scope, aggCall) {
+      @Override public int getGroupCount() {
+        return 0;
+      }
+    };
+
+    RelDataType ret = aggCall.getOperator().inferReturnType(opBinding);
+
+    // Copied from validateOperands
+    validator.setValidatedNodeType(call, ret);
+    validator.setValidatedNodeType(agg, ret);
+    return ret;
+  }
+}
+
+// End SqlFilterOperator.java

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/sql/SqlKind.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
index 3843231..244a241 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -187,6 +187,11 @@ public enum SqlKind {
   OVER,
 
   /**
+   * FILTER operator
+   */
+  FILTER,
+
+  /**
    * Window specification
    */
   WINDOW,

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/sql/SqlOverOperator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOverOperator.java b/core/src/main/java/org/apache/calcite/sql/SqlOverOperator.java
index 6255aaa..f4f5b12 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlOverOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlOverOperator.java
@@ -68,7 +68,7 @@ public class SqlOverOperator extends SqlBinaryOperator {
       throw validator.newValidationError(aggCall, RESOURCE.overNonAggregate());
     }
     validator.validateWindow(call.operand(1), scope, aggCall);
-    validator.validateAggregateParams(aggCall, scope);
+    validator.validateAggregateParams(aggCall, null, scope);
   }
 
   public RelDataType deriveType(

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 0a86799..0f7ca67 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -21,6 +21,7 @@ import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlAsOperator;
 import org.apache.calcite.sql.SqlBinaryOperator;
 import org.apache.calcite.sql.SqlCall;
+import org.apache.calcite.sql.SqlFilterOperator;
 import org.apache.calcite.sql.SqlFunction;
 import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlFunctionalOperator;
@@ -149,6 +150,10 @@ public class SqlStdOperatorTable extends ReflectiveSqlOperatorTable {
    */
   public static final SqlAsOperator AS = new SqlAsOperator();
 
+  /** <code>FILTER</code> operator filters which rows are included in an
+   *  aggregate function. */
+  public static final SqlFilterOperator FILTER = new SqlFilterOperator();
+
   /** {@code CUBE} operator, occurs within {@code GROUP BY} clause
    * or nested within a {@code GROUPING SETS}. */
   public static final SqlInternalOperator CUBE =

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java b/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
index 1846618..f8c2776 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/AggChecker.java
@@ -144,6 +144,10 @@ class AggChecker extends SqlBasicVisitor<Void> {
       // BY deptno'
       return null;
     }
+    if (call.getKind() == SqlKind.FILTER) {
+      call.operand(0).accept(this);
+      return null;
+    }
     if (isGroupExpr(call)) {
       // This call matches an expression in the GROUP BY clause.
       return null;

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
index a02b43f..bd6d0b6 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidator.java
@@ -282,10 +282,12 @@ public interface SqlValidator {
   /**
    * Validates parameters for aggregate function.
    *
-   * @param aggFunction function containing COLUMN_LIST parameter
+   * @param aggCall     Function containing COLUMN_LIST parameter
+   * @param filter      Filter, or null
    * @param scope       Syntactic scope
    */
-  void validateAggregateParams(SqlCall aggFunction, SqlValidatorScope scope);
+  void validateAggregateParams(SqlCall aggCall, SqlNode filter,
+      SqlValidatorScope scope);
 
   /**
    * Validates a COLUMN_LIST parameter

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index a0e7fdd..4a47c78 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -3947,18 +3947,21 @@ public class SqlValidatorImpl implements SqlValidatorWithHints {
     call.validate(this, scope);
   }
 
-  public void validateAggregateParams(
-      SqlCall aggFunction,
+  public void validateAggregateParams(SqlCall aggCall, SqlNode filter,
       SqlValidatorScope scope) {
     // For agg(expr), expr cannot itself contain aggregate function
     // invocations.  For example, SUM(2*MAX(x)) is illegal; when
     // we see it, we'll report the error for the SUM (not the MAX).
     // For more than one level of nesting, the error which results
     // depends on the traversal order for validation.
-    for (SqlNode param : aggFunction.getOperandList()) {
-      final SqlNode agg = aggOrOverFinder.findAgg(param);
+    for (SqlNode param : aggCall.getOperandList()) {
       if (aggOrOverFinder.findAgg(param) != null) {
-        throw newValidationError(aggFunction, RESOURCE.nestedAggIllegal());
+        throw newValidationError(aggCall, RESOURCE.nestedAggIllegal());
+      }
+    }
+    if (filter != null) {
+      if (aggOrOverFinder.findAgg(filter) != null) {
+        throw newValidationError(filter, RESOURCE.aggregateInFilterIllegal());
       }
     }
   }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
index 34e2f27..627c5b0 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelDecorrelator.java
@@ -562,9 +562,11 @@ public class RelDecorrelator implements ReflectiveVisitor {
       for (int oldPos : oldAggArgs) {
         aggArgs.add(combinedMap.get(oldPos));
       }
+      final int filterArg = oldAggCall.filterArg < 0 ? oldAggCall.filterArg
+          : combinedMap.get(oldAggCall.filterArg);
 
       newAggCalls.add(
-          oldAggCall.adaptTo(newProjectRel, aggArgs,
+          oldAggCall.adaptTo(newProjectRel, aggArgs, filterArg,
               oldGroupKeyCount, newGroupKeyCount));
 
       // The old to new output position mapping will be the same as that
@@ -2297,23 +2299,24 @@ public class RelDecorrelator implements ReflectiveVisitor {
       k = -1;
       for (AggregateCall aggCall : aggCalls) {
         ++k;
-        final List<Integer> aggArgs = aggCall.getArgList();
-        final List<Integer> newAggArgs;
+        final List<Integer> argList;
 
         if (isCountStar.contains(k)) {
           // this is a count(*), transform it to count(nullIndicator)
           // the null indicator is located at the end
-          newAggArgs = Collections.singletonList(nullIndicatorPos);
+          argList = Collections.singletonList(nullIndicatorPos);
         } else {
-          newAggArgs = Lists.newArrayList();
+          argList = Lists.newArrayList();
 
-          for (Integer aggArg : aggArgs) {
-            newAggArgs.add(aggArg + groupCount);
+          for (Integer aggArg : aggCall.getArgList()) {
+            argList.add(aggArg + groupCount);
           }
         }
 
+        int filterArg = aggCall.filterArg < 0 ? aggCall.filterArg
+            : aggCall.filterArg + groupCount;
         newAggCalls.add(
-            aggCall.adaptTo(joinOutputProjRel, newAggArgs,
+            aggCall.adaptTo(joinOutputProjRel, argList, filterArg,
                 aggRel.getGroupCount(), groupCount));
       }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/f5434a49/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
index b8701e5..99975b3 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -464,8 +464,8 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     RexNode newConditionExpr =
         conditionExpr.accept(shuttle);
 
-    final RelNode newFilter = filterFactory.createFilter(newInput,
-        newConditionExpr);
+    final RelNode newFilter = filterFactory.createFilter(
+        newInput, newConditionExpr);
 
     // The result has the same mapping as the input gave us. Sometimes we
     // return fields that the consumer didn't ask for, because the filter
@@ -514,8 +514,8 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     final RelCollation newCollation =
         sort.getTraitSet().canonize(RexUtil.apply(inputMapping, collation));
     final RelTraitSet newTraitSet = sort.getTraitSet().replace(newCollation);
-    final RelNode newSort = sortFactory.createSort(newTraitSet, newInput,
-        newCollation, sort.offset, sort.fetch);
+    final RelNode newSort = sortFactory.createSort(
+        newTraitSet, newInput, newCollation, sort.offset, sort.fetch);
 
     // The result has the same mapping as the input gave us. Sometimes we
     // return fields that the consumer didn't ask for, because the filter
@@ -764,6 +764,9 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
       for (int i : aggCall.getArgList()) {
         inputFieldsUsed.set(i);
       }
+      if (aggCall.filterArg >= 0) {
+        inputFieldsUsed.set(aggCall.filterArg);
+      }
     }
 
     // Create input with trimmed columns.
@@ -830,13 +833,13 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
     }
 
     // Now create new agg calls, and populate mapping for them.
-    final List<AggregateCall> newAggCallList =
-        new ArrayList<AggregateCall>();
+    final List<AggregateCall> newAggCallList = new ArrayList<>();
     j = groupCount + indicatorCount;
     for (AggregateCall aggCall : aggregate.getAggCallList()) {
       if (fieldsUsed.get(j)) {
         AggregateCall newAggCall =
-            aggCall.copy(Mappings.apply2(inputMapping, aggCall.getArgList()));
+            aggCall.copy(Mappings.apply2(inputMapping, aggCall.getArgList()),
+                Mappings.apply(inputMapping, aggCall.filterArg));
         if (newAggCall.equals(aggCall)) {
           newAggCall = aggCall; // immutable -> canonize to save space
         }