http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/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
index 3c0b527..8215dca 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlFilterOperator.java
@@ -69,13 +69,18 @@ public class SqlFilterOperator extends SqlBinaryOperator {
       SqlValidatorScope operandScope) {
     assert call.getOperator() == this;
     assert call.operandCount() == 2;
-    SqlCall aggCall = call.operand(0);
+    SqlCall aggCall = getAggCall(call);
     if (!aggCall.getOperator().isAggregator()) {
       throw validator.newValidationError(aggCall,
           RESOURCE.filterNonAggregate());
     }
     final SqlNode condition = call.operand(1);
-    validator.validateAggregateParams(aggCall, condition, scope);
+    SqlNodeList orderList = null;
+    if (hasWithinGroupCall(call)) {
+      SqlCall withinGroupCall = getWithinGroupCall(call);
+      orderList = withinGroupCall.operand(1);
+    }
+    validator.validateAggregateParams(aggCall, condition, orderList, scope);
 
     final RelDataType type = validator.deriveType(scope, condition);
     if (!SqlTypeUtil.inBooleanFamily(type)) {
@@ -92,14 +97,7 @@ public class SqlFilterOperator extends SqlBinaryOperator {
     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;
+    final SqlCall aggCall = getAggCall(call);
 
     // 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
@@ -114,9 +112,36 @@ public class SqlFilterOperator extends SqlBinaryOperator {
 
     // Copied from validateOperands
     ((SqlValidatorImpl) validator).setValidatedNodeType(call, ret);
-    ((SqlValidatorImpl) validator).setValidatedNodeType(agg, ret);
+    ((SqlValidatorImpl) validator).setValidatedNodeType(aggCall, ret);
+    if (hasWithinGroupCall(call)) {
+      ((SqlValidatorImpl) 
validator).setValidatedNodeType(getWithinGroupCall(call), ret);
+    }
     return ret;
   }
+
+  private static SqlCall getAggCall(SqlCall call) {
+    assert call.getOperator().getKind() == SqlKind.FILTER;
+    call = call.operand(0);
+    if (call.getOperator().getKind() == SqlKind.WITHIN_GROUP) {
+      call = call.operand(0);
+    }
+    return call;
+  }
+
+  private static SqlCall getWithinGroupCall(SqlCall call) {
+    assert call.getOperator().getKind() == SqlKind.FILTER;
+    call = call.operand(0);
+    if (call.getOperator().getKind() == SqlKind.WITHIN_GROUP) {
+      return call;
+    }
+    throw new AssertionError();
+  }
+
+  private static boolean hasWithinGroupCall(SqlCall call) {
+    assert call.getOperator().getKind() == SqlKind.FILTER;
+    call = call.operand(0);
+    return call.getOperator().getKind() == SqlKind.WITHIN_GROUP;
+  }
 }
 
 // End SqlFilterOperator.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/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 e77b4be..225d3b4 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -213,6 +213,11 @@ public enum SqlKind {
   FILTER,
 
   /**
+   * WITHIN_GROUP operator
+   */
+  WITHIN_GROUP,
+
+  /**
    * Window specification
    */
   WINDOW,

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/SqlRankFunction.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlRankFunction.java 
b/core/src/main/java/org/apache/calcite/sql/SqlRankFunction.java
index f330463..0b01d91 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlRankFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlRankFunction.java
@@ -19,6 +19,7 @@ package org.apache.calcite.sql;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlReturnTypeInference;
+import org.apache.calcite.util.Optionality;
 
 /**
  * Operator which aggregates sets of values into a result.
@@ -35,7 +36,7 @@ public class SqlRankFunction extends SqlAggFunction {
       boolean requiresOrder) {
     super(kind.name(), null, kind, returnTypes, null,
         OperandTypes.NILADIC, SqlFunctionCategory.NUMERIC, requiresOrder,
-        true);
+        true, Optionality.FORBIDDEN);
   }
 
   //~ Methods ----------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
index 83dad3a..652886f 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlSplittableAggFunction.java
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.sql;
 
+import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
@@ -118,7 +119,7 @@ public interface SqlSplittableAggFunction {
 
     public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall 
e) {
       return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
-          ImmutableIntList.of(), -1,
+          ImmutableIntList.of(), -1, RelCollations.EMPTY,
           typeFactory.createSqlType(SqlTypeName.BIGINT), null);
     }
 
@@ -147,8 +148,8 @@ public interface SqlSplittableAggFunction {
       }
       int ordinal = extra.register(node);
       return AggregateCall.create(SqlStdOperatorTable.SUM0, false, false,
-          ImmutableList.of(ordinal), -1, aggregateCall.type,
-          aggregateCall.name);
+          ImmutableList.of(ordinal), -1, aggregateCall.collation,
+          aggregateCall.type, aggregateCall.name);
     }
 
     /**
@@ -207,8 +208,10 @@ public interface SqlSplittableAggFunction {
         Registry<RexNode> extra, int offset, RelDataType inputRowType,
         AggregateCall aggregateCall, int leftSubTotal, int rightSubTotal) {
       assert (leftSubTotal >= 0) != (rightSubTotal >= 0);
+      assert aggregateCall.collation.getFieldCollations().isEmpty();
       final int arg = leftSubTotal >= 0 ? leftSubTotal : rightSubTotal;
-      return aggregateCall.copy(ImmutableIntList.of(arg), -1);
+      return aggregateCall.copy(ImmutableIntList.of(arg), -1,
+          RelCollations.EMPTY);
     }
   }
 
@@ -230,6 +233,7 @@ public interface SqlSplittableAggFunction {
     public AggregateCall other(RelDataTypeFactory typeFactory, AggregateCall 
e) {
       return AggregateCall.create(SqlStdOperatorTable.COUNT, false, false,
           ImmutableIntList.of(), -1,
+          RelCollations.EMPTY,
           typeFactory.createSqlType(SqlTypeName.BIGINT), null);
     }
 
@@ -260,8 +264,8 @@ public interface SqlSplittableAggFunction {
       }
       int ordinal = extra.register(node);
       return AggregateCall.create(getMergeAggFunctionOfTopSplit(), false, 
false,
-          ImmutableList.of(ordinal), -1, aggregateCall.type,
-          aggregateCall.name);
+          ImmutableList.of(ordinal), -1, aggregateCall.collation,
+          aggregateCall.type, aggregateCall.name);
     }
 
     protected abstract SqlAggFunction getMergeAggFunctionOfTopSplit();

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java 
b/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java
new file mode 100644
index 0000000..3ec0ee8
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/sql/SqlWithinGroupOperator.java
@@ -0,0 +1,85 @@
+/*
+ * 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.validate.SqlValidator;
+import org.apache.calcite.sql.validate.SqlValidatorScope;
+
+import static org.apache.calcite.util.Static.RESOURCE;
+
+/**
+ * An operator that applies a sort operation 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: order operation list
+ * </ul>
+ */
+public class SqlWithinGroupOperator extends SqlBinaryOperator {
+
+  public SqlWithinGroupOperator() {
+    super("WITHIN GROUP", SqlKind.WITHIN_GROUP, 100, true, ReturnTypes.ARG0,
+        null, OperandTypes.ANY_ANY);
+  }
+
+  @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec, 
int rightPrec) {
+    assert call.operandCount() == 2;
+    call.operand(0).unparse(writer, 0, 0);
+    writer.keyword("WITHIN GROUP");
+    final SqlWriter.Frame orderFrame =
+        writer.startList(SqlWriter.FrameTypeEnum.ORDER_BY_LIST, "(", ")");
+    writer.keyword("ORDER BY");
+    ((SqlNodeList) call.operand(1)).commaList(writer);
+    writer.endList(orderFrame);
+  }
+
+  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(call,
+          RESOURCE.withinGroupNotAllowed(aggCall.getOperator().getName()));
+    }
+    final SqlNodeList orderList = call.operand(1);
+    for (SqlNode order : orderList) {
+      RelDataType nodeType =
+          validator.deriveType(scope, order);
+      assert nodeType != null;
+    }
+    validator.validateAggregateParams(aggCall, null, orderList, scope);
+  }
+
+  public RelDataType deriveType(
+      SqlValidator validator,
+      SqlValidatorScope scope,
+      SqlCall call) {
+    // Validate type of the inner aggregate call
+    return validateOperands(validator, scope, call);
+  }
+}
+
+// End SqlWithinGroupOperator.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java
index d1001dd..132c8cd 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlAbstractGroupFunction.java
@@ -31,6 +31,7 @@ import org.apache.calcite.sql.validate.SelectScope;
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql.validate.SqlValidatorScope;
 import org.apache.calcite.sql.validate.SqlValidatorUtil;
+import org.apache.calcite.util.Optionality;
 import org.apache.calcite.util.Static;
 
 /**
@@ -55,7 +56,7 @@ public class SqlAbstractGroupFunction extends SqlAggFunction {
       SqlOperandTypeChecker operandTypeChecker,
       SqlFunctionCategory category) {
     super(name, null, kind, returnTypeInference, operandTypeInference,
-        operandTypeChecker, category, false, false);
+        operandTypeChecker, category, false, false, Optionality.FORBIDDEN);
   }
 
   @Override public void validateCall(SqlCall call, SqlValidator validator,

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlAnyValueAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlAnyValueAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlAnyValueAggFunction.java
index 49208b9..32e9792 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlAnyValueAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlAnyValueAggFunction.java
@@ -21,6 +21,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.base.Preconditions;
 
@@ -44,7 +45,8 @@ public class SqlAnyValueAggFunction extends SqlAggFunction {
         OperandTypes.ANY,
         SqlFunctionCategory.SYSTEM,
         false,
-        false);
+        false,
+        Optionality.FORBIDDEN);
     Preconditions.checkArgument(kind == SqlKind.ANY_VALUE);
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlAvgAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlAvgAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlAvgAggFunction.java
index 6be1ce9..f1ffcf9 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlAvgAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlAvgAggFunction.java
@@ -22,6 +22,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.base.Preconditions;
 
@@ -51,8 +52,10 @@ public class SqlAvgAggFunction extends SqlAggFunction {
         OperandTypes.NUMERIC,
         SqlFunctionCategory.NUMERIC,
         false,
-        false);
-    Preconditions.checkArgument(SqlKind.AVG_AGG_FUNCTIONS.contains(kind), 
"unsupported sql kind");
+        false,
+        Optionality.FORBIDDEN);
+    Preconditions.checkArgument(SqlKind.AVG_AGG_FUNCTIONS.contains(kind),
+        "unsupported sql kind");
   }
 
   @Deprecated // to be removed before 2.0

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
index db54102..1678727 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCountAggFunction.java
@@ -30,6 +30,7 @@ import org.apache.calcite.sql.type.SqlOperandTypeChecker;
 import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql.validate.SqlValidatorScope;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.collect.ImmutableList;
 
@@ -49,9 +50,11 @@ public class SqlCountAggFunction extends SqlAggFunction {
     this(name, SqlValidator.STRICT ? OperandTypes.ANY : 
OperandTypes.ONE_OR_MORE);
   }
 
-  public SqlCountAggFunction(String name, SqlOperandTypeChecker 
sqlOperandTypeChecker) {
+  public SqlCountAggFunction(String name,
+      SqlOperandTypeChecker sqlOperandTypeChecker) {
     super(name, null, SqlKind.COUNT, ReturnTypes.BIGINT, null,
-        sqlOperandTypeChecker, SqlFunctionCategory.NUMERIC, false, false);
+        sqlOperandTypeChecker, SqlFunctionCategory.NUMERIC, false, false,
+        Optionality.FORBIDDEN);
   }
 
   //~ Methods ----------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlCovarAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlCovarAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlCovarAggFunction.java
index 8591959..764339d 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlCovarAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlCovarAggFunction.java
@@ -22,6 +22,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.base.Preconditions;
 
@@ -48,7 +49,8 @@ public class SqlCovarAggFunction extends SqlAggFunction {
         OperandTypes.NUMERIC_NUMERIC,
         SqlFunctionCategory.NUMERIC,
         false,
-        false);
+        false,
+        Optionality.FORBIDDEN);
     Preconditions.checkArgument(SqlKind.COVAR_AVG_AGG_FUNCTIONS.contains(kind),
         "unsupported sql kind: " + kind);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlFirstLastValueAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlFirstLastValueAggFunction.java
 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlFirstLastValueAggFunction.java
index 72dccd1..397acf8 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlFirstLastValueAggFunction.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlFirstLastValueAggFunction.java
@@ -24,6 +24,7 @@ import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -48,7 +49,8 @@ public class SqlFirstLastValueAggFunction extends 
SqlAggFunction {
         OperandTypes.ANY,
         SqlFunctionCategory.NUMERIC,
         false,
-        true);
+        true,
+        Optionality.FORBIDDEN);
     Preconditions.checkArgument(kind == SqlKind.FIRST_VALUE
         || kind == SqlKind.LAST_VALUE);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlHistogramAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlHistogramAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlHistogramAggFunction.java
index a3fb5f2..864f8b4 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlHistogramAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlHistogramAggFunction.java
@@ -23,6 +23,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.collect.ImmutableList;
 
@@ -53,7 +54,8 @@ public class SqlHistogramAggFunction extends SqlAggFunction {
         OperandTypes.NUMERIC_OR_STRING,
         SqlFunctionCategory.NUMERIC,
         false,
-        false);
+        false,
+        Optionality.FORBIDDEN);
     this.type = type;
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
index d52c82b..55dc0cf 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
@@ -28,6 +28,7 @@ import 
org.apache.calcite.sql.type.SqlSingleOperandTypeChecker;
 import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeTransform;
 import org.apache.calcite.sql.type.SqlTypeTransforms;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -78,7 +79,8 @@ public class SqlLeadLagAggFunction extends SqlAggFunction {
         OPERAND_TYPES,
         SqlFunctionCategory.NUMERIC,
         false,
-        true);
+        true,
+        Optionality.FORBIDDEN);
     Preconditions.checkArgument(kind == SqlKind.LEAD
         || kind == SqlKind.LAG);
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java
index 438c7f1..edbc4b0 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlMinMaxAggFunction.java
@@ -24,6 +24,7 @@ import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlSplittableAggFunction;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -75,7 +76,8 @@ public class SqlMinMaxAggFunction extends SqlAggFunction {
         OperandTypes.COMPARABLE_ORDERED,
         SqlFunctionCategory.SYSTEM,
         false,
-        false);
+        false,
+        Optionality.FORBIDDEN);
     this.argTypes = ImmutableList.of();
     this.minMaxKind = MINMAX_COMPARABLE;
     Preconditions.checkArgument(kind == SqlKind.MIN

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlNthValueAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlNthValueAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlNthValueAggFunction.java
index b1a2a86..6e66653 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlNthValueAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlNthValueAggFunction.java
@@ -21,6 +21,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
 
 /**
  * <code>NTH_VALUE</code> windowed aggregate function
@@ -31,7 +32,7 @@ public class SqlNthValueAggFunction extends SqlAggFunction {
   public SqlNthValueAggFunction(SqlKind kind) {
     super(kind.name(), null, kind, ReturnTypes.ARG0_NULLABLE_IF_EMPTY,
         null, OperandTypes.ANY_NUMERIC, SqlFunctionCategory.NUMERIC, false,
-        true);
+        true, Optionality.FORBIDDEN);
   }
 }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlNtileAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlNtileAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlNtileAggFunction.java
index 18677f5..d451c0b 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlNtileAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlNtileAggFunction.java
@@ -21,6 +21,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
 
 /**
  * <code>NTILE</code> aggregate function
@@ -37,7 +38,8 @@ public class SqlNtileAggFunction extends SqlAggFunction {
         OperandTypes.POSITIVE_INTEGER_LITERAL,
         SqlFunctionCategory.NUMERIC,
         false,
-        true);
+        true,
+        Optionality.FORBIDDEN);
   }
 
 }

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlSingleValueAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlSingleValueAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlSingleValueAggFunction.java
index 6e745df..6f4518e 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlSingleValueAggFunction.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlSingleValueAggFunction.java
@@ -23,6 +23,7 @@ import org.apache.calcite.sql.SqlFunctionCategory;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.collect.ImmutableList;
 
@@ -51,7 +52,8 @@ public class SqlSingleValueAggFunction extends SqlAggFunction 
{
         OperandTypes.ANY,
         SqlFunctionCategory.SYSTEM,
         false,
-        false);
+        false,
+        Optionality.FORBIDDEN);
     this.type = type;
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/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 827a516..ac09932 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
@@ -46,6 +46,7 @@ import org.apache.calcite.sql.SqlUnnestOperator;
 import org.apache.calcite.sql.SqlUtil;
 import org.apache.calcite.sql.SqlValuesOperator;
 import org.apache.calcite.sql.SqlWindow;
+import org.apache.calcite.sql.SqlWithinGroupOperator;
 import org.apache.calcite.sql.SqlWriter;
 import org.apache.calcite.sql.type.InferTypes;
 import org.apache.calcite.sql.type.OperandTypes;
@@ -57,6 +58,7 @@ import org.apache.calcite.sql.validate.SqlConformance;
 import org.apache.calcite.sql.validate.SqlModality;
 import org.apache.calcite.sql2rel.AuxiliaryConverter;
 import org.apache.calcite.util.Litmus;
+import org.apache.calcite.util.Optionality;
 import org.apache.calcite.util.Pair;
 
 import com.google.common.collect.ImmutableList;
@@ -177,6 +179,9 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
    *  aggregate function. */
   public static final SqlFilterOperator FILTER = new SqlFilterOperator();
 
+  /** <code>WITHIN_GROUP</code> operator performs aggregations on ordered data 
input. */
+  public static final SqlWithinGroupOperator WITHIN_GROUP = new 
SqlWithinGroupOperator();
+
   /** {@code CUBE} operator, occurs within {@code GROUP BY} clause
    * or nested within a {@code GROUPING SETS}. */
   public static final SqlInternalOperator CUBE =
@@ -1980,7 +1985,8 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
           ReturnTypes.TO_MULTISET,
           null,
           OperandTypes.ANY,
-          SqlFunctionCategory.SYSTEM, false, false) {
+          SqlFunctionCategory.SYSTEM, false, false,
+          Optionality.OPTIONAL) {
       };
 
   /**
@@ -1992,7 +1998,8 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
           ReturnTypes.ARG0,
           null,
           OperandTypes.MULTISET,
-          SqlFunctionCategory.SYSTEM, false, false) {
+          SqlFunctionCategory.SYSTEM, false, false,
+          Optionality.FORBIDDEN) {
       };
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java
index eac5731..941a4e7 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumAggFunction.java
@@ -24,6 +24,7 @@ import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlSplittableAggFunction;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.collect.ImmutableList;
 
@@ -54,7 +55,8 @@ public class SqlSumAggFunction extends SqlAggFunction {
         OperandTypes.NUMERIC,
         SqlFunctionCategory.NUMERIC,
         false,
-        false);
+        false,
+        Optionality.FORBIDDEN);
     this.type = type;
   }
 

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java
 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java
index 1fd9bf1..b74e9f9 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlSumEmptyIsZeroAggFunction.java
@@ -25,6 +25,7 @@ import org.apache.calcite.sql.SqlSplittableAggFunction;
 import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
 
 import com.google.common.collect.ImmutableList;
 
@@ -48,7 +49,8 @@ public class SqlSumEmptyIsZeroAggFunction extends 
SqlAggFunction {
         OperandTypes.NUMERIC,
         SqlFunctionCategory.NUMERIC,
         false,
-        false);
+        false,
+        Optionality.FORBIDDEN);
   }
 
   //~ Methods ----------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/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 a00a523..2962459 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
@@ -156,6 +156,10 @@ class AggChecker extends SqlBasicVisitor<Void> {
       call.operand(0).accept(this);
       return null;
     }
+    if (call.getKind() == SqlKind.WITHIN_GROUP) {
+      call.operand(0).accept(this);
+      return null;
+    }
     // Visit the operand in window function
     if (call.getKind() == SqlKind.OVER) {
       for (SqlNode operand : call.<SqlCall>operand(0).getOperandList()) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
index 0cf8c0f..186daf5 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/SqlUserDefinedAggFunction.java
@@ -31,6 +31,7 @@ import org.apache.calcite.sql.type.SqlOperandTypeChecker;
 import org.apache.calcite.sql.type.SqlOperandTypeInference;
 import org.apache.calcite.sql.type.SqlReturnTypeInference;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.util.Optionality;
 import org.apache.calcite.util.Util;
 
 import com.google.common.collect.Lists;
@@ -57,10 +58,12 @@ public class SqlUserDefinedAggFunction extends 
SqlAggFunction {
       SqlReturnTypeInference returnTypeInference,
       SqlOperandTypeInference operandTypeInference,
       SqlOperandTypeChecker operandTypeChecker, AggregateFunction function,
-      boolean requiresOrder, boolean requiresOver, RelDataTypeFactory 
typeFactory) {
+      boolean requiresOrder, boolean requiresOver,
+      Optionality requiresGroupOrder, RelDataTypeFactory typeFactory) {
     super(Util.last(opName.names), opName, SqlKind.OTHER_FUNCTION,
         returnTypeInference, operandTypeInference, operandTypeChecker,
-        SqlFunctionCategory.USER_DEFINED_FUNCTION, requiresOrder, 
requiresOver);
+        SqlFunctionCategory.USER_DEFINED_FUNCTION, requiresOrder, requiresOver,
+        requiresGroupOrder);
     this.function = function;
     this.typeFactory = typeFactory;
   }

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/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 a4b4297..2045eac 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
@@ -294,12 +294,14 @@ public interface SqlValidator {
   /**
    * Validates parameters for aggregate function.
    *
-   * @param aggCall     Function containing COLUMN_LIST parameter
-   * @param filter      Filter, or null
+   * @param aggCall     Call to aggregate function
+   * @param filter      Filter ({@code FILTER (WHERE)} clause), or null
+   * @param orderList   Ordering specification ({@code WITHING GROUP} clause),
+   *                    or null
    * @param scope       Syntactic scope
    */
   void validateAggregateParams(SqlCall aggCall, SqlNode filter,
-      SqlValidatorScope scope);
+      SqlNodeList orderList, SqlValidatorScope scope);
 
   /**
    * Validates a COLUMN_LIST parameter

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/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 f7ce4d6..626399f 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
@@ -45,6 +45,7 @@ import org.apache.calcite.sql.JoinConditionType;
 import org.apache.calcite.sql.JoinType;
 import org.apache.calcite.sql.SqlAccessEnum;
 import org.apache.calcite.sql.SqlAccessType;
+import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlBasicCall;
 import org.apache.calcite.sql.SqlCall;
 import org.apache.calcite.sql.SqlCallBinding;
@@ -4868,7 +4869,7 @@ public class SqlValidatorImpl implements 
SqlValidatorWithHints {
     targetWindow.setWindowCall(null);
     call.validate(this, scope);
 
-    validateAggregateParams(call, null, scope);
+    validateAggregateParams(call, null, null, scope);
 
     // Disable nested aggregates post validation
     inWindow = false;
@@ -5141,7 +5142,7 @@ public class SqlValidatorImpl implements 
SqlValidatorWithHints {
   }
 
   public void validateAggregateParams(SqlCall aggCall, SqlNode filter,
-      SqlValidatorScope scope) {
+      SqlNodeList orderList, 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).
@@ -5175,6 +5176,40 @@ public class SqlValidatorImpl implements 
SqlValidatorWithHints {
         throw newValidationError(filter, RESOURCE.aggregateInFilterIllegal());
       }
     }
+    if (orderList != null) {
+      for (SqlNode param : orderList) {
+        if (a.findAgg(param) != null) {
+          throw newValidationError(aggCall,
+              RESOURCE.aggregateInWithinGroupIllegal());
+        }
+      }
+    }
+
+    final SqlAggFunction op = (SqlAggFunction) aggCall.getOperator();
+    switch (op.requiresGroupOrder()) {
+    case MANDATORY:
+      if (orderList == null || orderList.size() == 0) {
+        throw newValidationError(aggCall,
+            RESOURCE.aggregateMissingWithinGroupClause(op.getName()));
+      }
+      break;
+    case OPTIONAL:
+      break;
+    case IGNORED:
+      // rewrite the order list to empty
+      if (orderList != null) {
+        orderList.getList().clear();
+      }
+      break;
+    case FORBIDDEN:
+      if (orderList != null && orderList.size() != 0) {
+        throw newValidationError(aggCall,
+            RESOURCE.withinGroupClauseIllegalInAggregate(op.getName()));
+      }
+      break;
+    default:
+      throw new AssertionError(op);
+    }
   }
 
   public void validateCall(

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/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 e9031ba..5d6fc16 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -20,6 +20,7 @@ import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptUtil;
 import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Aggregate;
@@ -794,12 +795,11 @@ public class RelFieldTrimmer implements ReflectiveVisitor 
{
         aggregate.getGroupSet().rebuild();
     // 2. agg functions
     for (AggregateCall aggCall : aggregate.getAggCallList()) {
-      for (int i : aggCall.getArgList()) {
-        inputFieldsUsed.set(i);
-      }
+      inputFieldsUsed.addAll(aggCall.getArgList());
       if (aggCall.filterArg >= 0) {
         inputFieldsUsed.set(aggCall.filterArg);
       }
+      inputFieldsUsed.addAll(RelCollations.ordinals(aggCall.collation));
     }
 
     // Create input with trimmed columns.
@@ -871,6 +871,7 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
                 .distinct(aggCall.isDistinct())
                 .filter(filterArg)
                 .approximate(aggCall.isApproximate())
+                .sort(relBuilder.fields(aggCall.collation))
                 .as(aggCall.name);
         mapping.set(j, groupCount + indicatorCount + newAggCallList.size());
         newAggCallList.add(newAggCall);

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java 
b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
index 9750664..9512312 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/SqlToRelConverter.java
@@ -199,6 +199,7 @@ import java.util.Objects;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
 import javax.annotation.Nonnull;
 
 import static org.apache.calcite.sql.SqlUtil.stripAs;
@@ -1124,9 +1125,10 @@ public class SqlToRelConverter {
             LogicalAggregate.create(seek, ImmutableBitSet.of(), null,
                 ImmutableList.of(
                     AggregateCall.create(SqlStdOperatorTable.COUNT, false,
-                        false, ImmutableList.of(), -1, longType, null),
+                        false, ImmutableList.of(), -1, RelCollations.EMPTY,
+                        longType, null),
                     AggregateCall.create(SqlStdOperatorTable.COUNT, false,
-                        false, args, -1, longType, null)));
+                        false, args, -1, RelCollations.EMPTY, longType, 
null)));
         LogicalJoin join =
             LogicalJoin.create(bb.root, aggregate, 
rexBuilder.makeLiteral(true),
                 ImmutableSet.of(), JoinRelType.INNER);
@@ -2727,6 +2729,10 @@ public class SqlToRelConverter {
     replaceSubQueries(bb, aggregateFinder.filterList,
         RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
 
+    // also replace sub-queries inside ordering spec in the aggregates
+    replaceSubQueries(bb, aggregateFinder.orderList,
+        RelOptUtil.Logic.TRUE_FALSE_UNKNOWN);
+
     // If group-by clause is missing, pretend that it has zero elements.
     if (groupList == null) {
       groupList = SqlNodeList.EMPTY;
@@ -4667,7 +4673,9 @@ public class SqlToRelConverter {
       if (agg != null) {
         final SqlOperator op = call.getOperator();
         if (window == null
-            && (op.isAggregator() || op.getKind() == SqlKind.FILTER)) {
+            && (op.isAggregator()
+            || op.getKind() == SqlKind.FILTER
+            || op.getKind() == SqlKind.WITHIN_GROUP)) {
           return agg.lookupAggregates(call);
         }
       }
@@ -4918,7 +4926,8 @@ public class SqlToRelConverter {
     public Void visit(SqlCall call) {
       switch (call.getKind()) {
       case FILTER:
-        translateAgg((SqlCall) call.operand(0), call.operand(1), call);
+      case WITHIN_GROUP:
+        translateAgg(call);
         return null;
       case SELECT:
         // rchen 2006-10-17:
@@ -4952,7 +4961,7 @@ public class SqlToRelConverter {
           inOver = false;
         } else {
           // We're beyond the one ignored level
-          translateAgg(call, null, call);
+          translateAgg(call);
           return null;
         }
       }
@@ -4967,8 +4976,24 @@ public class SqlToRelConverter {
       return null;
     }
 
-    private void translateAgg(SqlCall call, SqlNode filter, SqlCall outerCall) 
{
+    private void translateAgg(SqlCall call) {
+      translateAgg(call, null, null, call);
+    }
+
+    private void translateAgg(SqlCall call, SqlNode filter,
+        SqlNodeList orderList, SqlCall outerCall) {
       assert bb.agg == this;
+      assert outerCall != null;
+      switch (call.getKind()) {
+      case FILTER:
+        assert filter == null;
+        translateAgg(call.operand(0), call.operand(1), orderList, outerCall);
+        return;
+      case WITHIN_GROUP:
+        assert orderList == null;
+        translateAgg(call.operand(0), filter, call.operand(1), outerCall);
+        return;
+      }
       final List<Integer> args = new ArrayList<>();
       int filterArg = -1;
       final List<RelDataType> argTypes =
@@ -5026,6 +5051,24 @@ public class SqlToRelConverter {
         distinct = true;
         approximate = true;
       }
+      final RelCollation collation;
+      if (orderList == null || orderList.size() == 0) {
+        collation = RelCollations.EMPTY;
+      } else {
+        collation = RelCollations.of(
+            orderList.getList()
+                .stream()
+                .map(order ->
+                    bb.convertSortExpression(order,
+                        RelFieldCollation.Direction.ASCENDING,
+                        RelFieldCollation.NullDirection.UNSPECIFIED))
+                .map(fieldCollation ->
+                    new RelFieldCollation(
+                        lookupOrCreateGroupExpr(fieldCollation.left),
+                        fieldCollation.getDirection(),
+                        fieldCollation.getNullDirection()))
+                .collect(Collectors.toList()));
+      }
       final AggregateCall aggCall =
           AggregateCall.create(
               aggFunction,
@@ -5033,6 +5076,7 @@ public class SqlToRelConverter {
               approximate,
               args,
               filterArg,
+              collation,
               type,
               nameMap.get(outerCall.toString()));
       final AggregatingSelectScope.Resolved r =
@@ -5358,6 +5402,7 @@ public class SqlToRelConverter {
   private static class AggregateFinder extends SqlBasicVisitor<Void> {
     final SqlNodeList list = new SqlNodeList(SqlParserPos.ZERO);
     final SqlNodeList filterList = new SqlNodeList(SqlParserPos.ZERO);
+    final SqlNodeList orderList = new SqlNodeList(SqlParserPos.ZERO);
 
     @Override public Void visit(SqlCall call) {
       // ignore window aggregates and ranking functions (associated with OVER 
operator)
@@ -5375,6 +5420,16 @@ public class SqlToRelConverter {
         return null;
       }
 
+      if (call.getOperator().getKind() == SqlKind.WITHIN_GROUP) {
+        // the WHERE in a WITHIN_GROUP must be tracked too so we can call 
replaceSubQueries on it.
+        // see https://issues.apache.org/jira/browse/CALCITE-1910
+        final SqlNode aggCall = call.getOperandList().get(0);
+        final SqlNodeList orderList = (SqlNodeList) 
call.getOperandList().get(1);
+        list.add(aggCall);
+        orderList.getList().forEach(this.orderList::add);
+        return null;
+      }
+
       if (call.getOperator().isAggregator()) {
         list.add(call);
         return null;

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java 
b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index 323f65a..3b0be9c 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -94,6 +94,7 @@ import java.math.BigDecimal;
 import java.util.AbstractList;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Deque;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -785,17 +786,18 @@ public class RelBuilder {
   public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
       boolean approximate, RexNode filter, String alias,
       Iterable<? extends RexNode> operands) {
-    return aggregateCall(aggFunction, distinct, filter, alias, operands)
-        .approximate(approximate);
+    return aggregateCall(aggFunction, distinct, approximate, filter,
+        ImmutableList.of(), alias, ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to an aggregate function.
    *
    * <p>To add other operands, apply
-   * {@link AggCall#filter(RexNode...)},
    * {@link AggCall#distinct()},
-   * {@link AggCall#as},
-   * {@link AggCall#sort} to the result. */
+   * {@link AggCall#approximate(boolean)},
+   * {@link AggCall#filter(RexNode...)},
+   * {@link AggCall#sort},
+   * {@link AggCall#as} to the result. */
   public AggCall aggregateCall(SqlAggFunction aggFunction,
       Iterable<? extends RexNode> operands) {
     return aggregateCall(aggFunction, false, false, null, ImmutableList.of(),
@@ -805,10 +807,11 @@ public class RelBuilder {
   /** Creates a call to an aggregate function.
    *
    * <p>To add other operands, apply
-   * {@link AggCall#filter(RexNode...)},
    * {@link AggCall#distinct()},
-   * {@link AggCall#as},
-   * {@link AggCall#sort} to the result. */
+   * {@link AggCall#approximate(boolean)},
+   * {@link AggCall#filter(RexNode...)},
+   * {@link AggCall#sort},
+   * {@link AggCall#as} to the result. */
   public AggCall aggregateCall(SqlAggFunction aggFunction,
       RexNode... operands) {
     return aggregateCall(aggFunction, false, false, null, ImmutableList.of(),
@@ -816,7 +819,7 @@ public class RelBuilder {
   }
 
   /** Creates a call to an aggregate function with all applicable operands. */
-  AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
+  protected AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
       boolean approximate, RexNode filter, ImmutableList<RexNode> orderKeys,
       String alias, ImmutableList<RexNode> operands) {
     return new AggCallImpl(aggFunction, distinct, approximate, filter, alias,
@@ -828,11 +831,24 @@ public class RelBuilder {
     return count(false, null, operands);
   }
 
+  /** Creates a call to the {@code COUNT} aggregate function. */
+  public AggCall count(Iterable<? extends RexNode> operands) {
+    return count(false, null, operands);
+  }
+
   /** Creates a call to the {@code COUNT} aggregate function,
    * optionally distinct and with an alias. */
   public AggCall count(boolean distinct, String alias, RexNode... operands) {
-    return aggregateCall(SqlStdOperatorTable.COUNT, distinct, null, alias,
-        ImmutableList.copyOf(operands));
+    return aggregateCall(SqlStdOperatorTable.COUNT, distinct, false, null,
+        ImmutableList.of(), alias, ImmutableList.copyOf(operands));
+  }
+
+  /** Creates a call to the {@code COUNT} aggregate function,
+   * optionally distinct and with an alias. */
+  public AggCall count(boolean distinct, String alias,
+      Iterable<? extends RexNode> operands) {
+    return aggregateCall(SqlStdOperatorTable.COUNT, distinct, false, null,
+        ImmutableList.of(), alias, ImmutableList.copyOf(operands));
   }
 
   /** Creates a call to the {@code COUNT(*)} aggregate function. */
@@ -848,8 +864,8 @@ public class RelBuilder {
   /** Creates a call to the {@code SUM} aggregate function,
    * optionally distinct and with an alias. */
   public AggCall sum(boolean distinct, String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.SUM, distinct, null, alias,
-        ImmutableList.of(operand));
+    return aggregateCall(SqlStdOperatorTable.SUM, distinct, false, null,
+        ImmutableList.of(), alias, ImmutableList.of(operand));
   }
 
   /** Creates a call to the {@code AVG} aggregate function. */
@@ -860,8 +876,8 @@ public class RelBuilder {
   /** Creates a call to the {@code AVG} aggregate function,
    * optionally distinct and with an alias. */
   public AggCall avg(boolean distinct, String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.AVG, distinct, null, alias,
-        ImmutableList.of(operand));
+    return aggregateCall(SqlStdOperatorTable.AVG, distinct, false, null,
+        ImmutableList.of(), alias, ImmutableList.of(operand));
   }
 
   /** Creates a call to the {@code MIN} aggregate function. */
@@ -872,8 +888,8 @@ public class RelBuilder {
   /** Creates a call to the {@code MIN} aggregate function,
    * optionally with an alias. */
   public AggCall min(String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.MIN, false, null, alias,
-        ImmutableList.of(operand));
+    return aggregateCall(SqlStdOperatorTable.MIN, false, false, null,
+        ImmutableList.of(), alias, ImmutableList.of(operand));
   }
 
   /** Creates a call to the {@code MAX} aggregate function,
@@ -884,8 +900,8 @@ public class RelBuilder {
 
   /** Creates a call to the {@code MAX} aggregate function. */
   public AggCall max(String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.MAX, false, null, alias,
-        ImmutableList.of(operand));
+    return aggregateCall(SqlStdOperatorTable.MAX, false, false, null,
+        ImmutableList.of(), alias, ImmutableList.of(operand));
   }
 
   // Methods for patterns
@@ -1447,10 +1463,17 @@ public class RelBuilder {
         if (aggCall1.filter != null && !aggCall1.aggFunction.allowsFilter()) {
           throw new IllegalArgumentException("FILTER not allowed");
         }
+        RelCollation collation =
+            RelCollations.of(aggCall1.orderKeys
+                .stream()
+                .map(orderKey ->
+                    collation(orderKey, RelFieldCollation.Direction.ASCENDING,
+                        null, Collections.emptyList()))
+                .collect(Collectors.toList()));
         aggregateCall =
             AggregateCall.create(aggCall1.aggFunction, aggCall1.distinct,
-                aggCall1.approximate, args,
-                filterArg, groupSet.cardinality(), r, null, aggCall1.alias);
+                aggCall1.approximate, args, filterArg, collation,
+                groupSet.cardinality(), r, null, aggCall1.alias);
       } else {
         aggregateCall = ((AggCallImpl2) aggCall).aggregateCall;
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java 
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index c7a3d22..8462d95 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -17,6 +17,10 @@
 package org.apache.calcite.util;
 
 import org.apache.calcite.DataContext;
+import org.apache.calcite.adapter.enumerable.AggregateLambdaFactory;
+import org.apache.calcite.adapter.enumerable.OrderedAggregateLambdaFactory;
+import 
org.apache.calcite.adapter.enumerable.SequencedAdderAggregateLambdaFactory;
+import org.apache.calcite.adapter.enumerable.SourceSorter;
 import org.apache.calcite.adapter.java.ReflectiveSchema;
 import org.apache.calcite.adapter.jdbc.JdbcSchema;
 import org.apache.calcite.avatica.util.DateTimeUtils;
@@ -433,7 +437,21 @@ public enum BuiltInMethod {
   CONTEXT_ROOT(Context.class, "root", true),
   DATA_CONTEXT_GET_QUERY_PROVIDER(DataContext.class, "getQueryProvider"),
   METADATA_REL(Metadata.class, "rel"),
-  STRUCT_ACCESS(SqlFunctions.class, "structAccess", Object.class, int.class, 
String.class);
+  STRUCT_ACCESS(SqlFunctions.class, "structAccess", Object.class, int.class,
+      String.class),
+  SOURCE_SORTER(SourceSorter.class, Function2.class, Function1.class,
+      Comparator.class),
+  ORDERED_AGGREGATE_LAMBDA_FACTORY(OrderedAggregateLambdaFactory.class,
+      Function0.class, List.class),
+  
SEQUENCED_ADDER_AGGREGATE_LAMBDA_FACTORY(SequencedAdderAggregateLambdaFactory.class,
+      Function0.class, List.class),
+  AGG_LAMBDA_FACTORY_ACC_INITIALIZER(AggregateLambdaFactory.class,
+      "accumulatorInitializer"),
+  AGG_LAMBDA_FACTORY_ACC_ADDER(AggregateLambdaFactory.class, 
"accumulatorAdder"),
+  AGG_LAMBDA_FACTORY_ACC_RESULT_SELECTOR(AggregateLambdaFactory.class,
+      "resultSelector", Function2.class),
+  
AGG_LAMBDA_FACTORY_ACC_SINGLE_GROUP_RESULT_SELECTOR(AggregateLambdaFactory.class,
+      "singleGroupResultSelector", Function1.class);
 
   public final Method method;
   public final Constructor constructor;

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/java/org/apache/calcite/util/Optionality.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/Optionality.java 
b/core/src/main/java/org/apache/calcite/util/Optionality.java
new file mode 100644
index 0000000..43da19d
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/util/Optionality.java
@@ -0,0 +1,41 @@
+/*
+ * 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.util;
+
+/**
+ * Four states that describe whether a particular behavior or
+ * property is allowed and/or not allowed.
+ */
+public enum Optionality {
+  /** A property is <em>mandatory</em> if an instance must possess it;
+   * it is an error if it does not. */
+  MANDATORY,
+
+  /** A property is <em>optional</em> if an instance may or may not possess it;
+   * neither state is an error. */
+  OPTIONAL,
+
+  /** A property is <em>ignored</em> if an instance may or may not possess it;
+   * if it possesses the property, the effect is as if it does not. */
+  IGNORED,
+
+  /** A property is <em>forbidden</em> if an instance must not possess it;
+   * it is an error if the instance has the property. */
+  FORBIDDEN
+}
+
+// End Optionality.java

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
----------------------------------------------------------------------
diff --git 
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties 
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index faefd68..edb93b9 100644
--- 
a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ 
b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -109,6 +109,9 @@ AggregateIllegalInClause=Aggregate expression is illegal in 
{0} clause
 WindowedAggregateIllegalInClause=Windowed aggregate expression is illegal in 
{0} clause
 NestedAggIllegal=Aggregate expressions cannot be nested
 AggregateInFilterIllegal=FILTER must not contain aggregate expression
+AggregateInWithinGroupIllegal=WITHIN GROUP must not contain aggregate 
expression
+AggregateMissingWithinGroupClause=Aggregate expression ''{0}'' must contain a 
within group clause
+WithinGroupClauseIllegalInAggregate=Aggregate expression ''{0}'' must not 
contain a within group clause
 AggregateIllegalInOrderBy=Aggregate expression is illegal in ORDER BY clause 
of non-aggregating SELECT
 CondMustBeBoolean={0} clause must be a condition
 HavingMustBeBoolean=HAVING clause must be a condition
@@ -146,6 +149,7 @@ OrderByOverlap=ORDER BY not allowed in both base and 
referenced windows
 RefWindowWithFrame=Referenced window cannot have framing declarations
 TypeNotSupported=Type ''{0}'' is not supported
 FunctionQuantifierNotAllowed=DISTINCT/ALL not allowed with {0} function
+WithinGroupNotAllowed=WITHIN GROUP not allowed with {0} function
 SomeButNotAllArgumentsAreNamed=Some but not all arguments are named
 DuplicateArgumentName=Duplicate argument name ''{0}''
 DefaultForOptionalParameter=DEFAULT is only allowed for optional parameters

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java 
b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
index c411888..7b63497 100644
--- a/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
+++ b/core/src/test/java/org/apache/calcite/plan/RelWriterTest.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.plan;
 
 import org.apache.calcite.adapter.java.ReflectiveSchema;
+import org.apache.calcite.rel.RelCollations;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.externalize.RelJsonReader;
@@ -140,10 +141,10 @@ public class RelWriterTest {
                   ImmutableList.of(
                       AggregateCall.create(SqlStdOperatorTable.COUNT,
                           true, false, ImmutableList.of(1), -1,
-                          bigIntType, "c"),
+                          RelCollations.EMPTY, bigIntType, "c"),
                       AggregateCall.create(SqlStdOperatorTable.COUNT,
                           false, false, ImmutableList.of(), -1,
-                          bigIntType, "d")));
+                          RelCollations.EMPTY, bigIntType, "d")));
           aggregate.explain(writer);
           return writer.asString();
         });

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java 
b/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
index 2822c1c..e901868 100644
--- 
a/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
+++ 
b/core/src/test/java/org/apache/calcite/plan/volcano/TraitPropagationTest.java
@@ -165,7 +165,8 @@ public class TraitPropagationTest {
 
       // aggregate on s, count
       AggregateCall aggCall = AggregateCall.create(SqlStdOperatorTable.COUNT,
-          false, false, Collections.singletonList(1), -1, sqlBigInt, "cnt");
+          false, false, Collections.singletonList(1), -1, RelCollations.EMPTY,
+          sqlBigInt, "cnt");
       RelNode agg = new LogicalAggregate(cluster,
           cluster.traitSetOf(Convention.NONE), project, false,
           ImmutableBitSet.of(0), null, Collections.singletonList(aggCall));

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index 94b98b7..7a5181e 100644
--- 
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++ 
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -274,8 +274,8 @@ public class RelToSqlConverterTest {
     final RelNode root = builder
         .scan("EMP")
         .aggregate(builder.groupKey(),
-            builder.aggregateCall(SqlStdOperatorTable.SUM0, false, null, "s",
-                builder.field(3)))
+            builder.aggregateCall(SqlStdOperatorTable.SUM0, builder.field(3))
+                .as("s"))
         .build();
     final String expectedMysql = "SELECT COALESCE(SUM(`MGR`), 0) AS `s`\n"
         + "FROM `scott`.`EMP`";
@@ -2763,6 +2763,53 @@ public class RelToSqlConverterTest {
         callsUnparseCallOnSqlSelect[0], is(true));
   }
 
+  @Test public void testWithinGroup1() {
+    final String query = "select \"product_class_id\", collect(\"net_weight\") 
"
+        + "within group (order by \"net_weight\" desc) "
+        + "from \"product\" group by \"product_class_id\"";
+    final String expected = "SELECT \"product_class_id\", 
COLLECT(\"net_weight\") "
+        + "WITHIN GROUP (ORDER BY \"net_weight\" DESC)\n"
+        + "FROM \"foodmart\".\"product\"\n"
+        + "GROUP BY \"product_class_id\"";
+    sql(query).ok(expected);
+  }
+
+  @Test public void testWithinGroup2() {
+    final String query = "select \"product_class_id\", collect(\"net_weight\") 
"
+        + "within group (order by \"low_fat\", \"net_weight\" desc nulls last) 
"
+        + "from \"product\" group by \"product_class_id\"";
+    final String expected = "SELECT \"product_class_id\", 
COLLECT(\"net_weight\") "
+        + "WITHIN GROUP (ORDER BY \"low_fat\", \"net_weight\" DESC NULLS 
LAST)\n"
+        + "FROM \"foodmart\".\"product\"\n"
+        + "GROUP BY \"product_class_id\"";
+    sql(query).ok(expected);
+  }
+
+  @Test public void testWithinGroup3() {
+    final String query = "select \"product_class_id\", collect(\"net_weight\") 
"
+        + "within group (order by \"net_weight\" desc), "
+        + "min(\"low_fat\")"
+        + "from \"product\" group by \"product_class_id\"";
+    final String expected = "SELECT \"product_class_id\", 
COLLECT(\"net_weight\") "
+        + "WITHIN GROUP (ORDER BY \"net_weight\" DESC), MIN(\"low_fat\")\n"
+        + "FROM \"foodmart\".\"product\"\n"
+        + "GROUP BY \"product_class_id\"";
+    sql(query).ok(expected);
+  }
+
+  @Test public void testWithinGroup4() {
+    // filter in AggregateCall is not unparsed
+    final String query = "select \"product_class_id\", collect(\"net_weight\") 
"
+        + "within group (order by \"net_weight\" desc) filter (where 
\"net_weight\" > 0)"
+        + "from \"product\" group by \"product_class_id\"";
+    final String expected = "SELECT \"product_class_id\", 
COLLECT(\"net_weight\") "
+        + "WITHIN GROUP (ORDER BY \"net_weight\" DESC)\n"
+        + "FROM \"foodmart\".\"product\"\n"
+        + "GROUP BY \"product_class_id\"";
+    sql(query).ok(expected);
+  }
+
+
   /** Fluid interface to run tests. */
   static class Sql {
     private final SchemaPlus schema;

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java 
b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index 6ad2769..d8dddc3 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -8194,6 +8194,64 @@ public class SqlParserTest {
     sql(sql).ok(expected);
   }
 
+  @Test public void testWithinGroupClause1() {
+    final String sql = "select col1,\n"
+        + " collect(col2) within group (order by col3)\n"
+        + "from t\n"
+        + "order by col1 limit 10";
+    final String expected = "SELECT `COL1`,"
+        + " (COLLECT(`COL2`) WITHIN GROUP (ORDER BY `COL3`))\n"
+        + "FROM `T`\n"
+        + "ORDER BY `COL1`\n"
+        + "FETCH NEXT 10 ROWS ONLY";
+    sql(sql).ok(expected);
+  }
+
+  @Test public void testWithinGroupClause2() {
+    final String sql = "select collect(col2) within group (order by col3)\n"
+        + "from t\n"
+        + "order by col1 limit 10";
+    final String expected = "SELECT"
+        + " (COLLECT(`COL2`) WITHIN GROUP (ORDER BY `COL3`))\n"
+        + "FROM `T`\n"
+        + "ORDER BY `COL1`\n"
+        + "FETCH NEXT 10 ROWS ONLY";
+    sql(sql).ok(expected);
+  }
+
+  @Test public void testWithinGroupClause3() {
+    final String sql = "select collect(col2) within group (^)^ "
+        + "from t order by col1 limit 10";
+    sql(sql).fails("(?s).*Encountered \"\\)\" at line 1, column 36\\..*");
+  }
+
+  @Test public void testWithinGroupClause4() {
+    final String sql = "select col1,\n"
+        + " collect(col2) within group (order by col3, col4)\n"
+        + "from t\n"
+        + "order by col1 limit 10";
+    final String expected = "SELECT `COL1`,"
+        + " (COLLECT(`COL2`) WITHIN GROUP (ORDER BY `COL3`, `COL4`))\n"
+        + "FROM `T`\n"
+        + "ORDER BY `COL1`\n"
+        + "FETCH NEXT 10 ROWS ONLY";
+    sql(sql).ok(expected);
+  }
+
+  @Test public void testWithinGroupClause5() {
+    final String sql = "select col1,\n"
+        + " collect(col2) within group (\n"
+        + "  order by col3 desc nulls first, col4 asc nulls last)\n"
+        + "from t\n"
+        + "order by col1 limit 10";
+    final String expected = "SELECT `COL1`, (COLLECT(`COL2`) "
+        + "WITHIN GROUP (ORDER BY `COL3` DESC NULLS FIRST, `COL4` NULLS 
LAST))\n"
+        + "FROM `T`\n"
+        + "ORDER BY `COL1`\n"
+        + "FETCH NEXT 10 ROWS ONLY";
+    sql(sql).ok(expected);
+  }
+
   //~ Inner Interfaces -------------------------------------------------------
 
   /**

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java 
b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
index 4484762..da7d099 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
@@ -5622,6 +5622,8 @@ public abstract class SqlOperatorBaseTest {
     final String[] values = {"0", "CAST(null AS INTEGER)", "2", "2"};
     tester.checkAgg("collect(x)", values,
         Collections.singletonList("[0, 2, 2]"), (double) 0);
+    tester.checkAgg("collect(x) within group(order by x desc)", values,
+        Collections.singletonList("[2, 2, 0]"), (double) 0);
     Object result1 = -3;
     if (!enable) {
       return;

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/test/JdbcTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index cd87e5f..888ed6c 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -6657,6 +6657,81 @@ public class JdbcTest {
     connection.close();
   }
 
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-2224";>[CALCITE-2224]
+   * WITHIN GROUP clause for aggregate functions</a>. */
+  @Test public void testWithinGroupClause1() {
+    final String sql = "select X,\n"
+        + " collect(Y) within group (order by Y desc) as \"SET\"\n"
+        + "from (values (1, 'a'), (1, 'b'),\n"
+        + "             (3, 'c'), (3, 'd')) AS t(X, Y)\n"
+        + "group by X\n"
+        + "limit 10";
+    CalciteAssert.that().query(sql)
+        .returnsUnordered("X=1; SET=[b, a]",
+            "X=3; SET=[d, c]");
+  }
+
+  @Test public void testWithinGroupClause2() {
+    final String sql = "select X,\n"
+        + " collect(Y) within group (order by Y desc) as SET_1,\n"
+        + " collect(Y) within group (order by Y asc) as SET_2\n"
+        + "from (values (1, 'a'), (1, 'b'), (3, 'c'), (3, 'd')) AS t(X, Y)\n"
+        + "group by X\n"
+        + "limit 10";
+    CalciteAssert
+        .that()
+        .query(sql)
+        .returnsUnordered("X=1; SET_1=[b, a]; SET_2=[a, b]",
+            "X=3; SET_1=[d, c]; SET_2=[c, d]");
+  }
+
+  @Test public void testWithinGroupClause3() {
+    final String sql = "select"
+        + " collect(Y) within group (order by Y desc) as SET_1,\n"
+        + " collect(Y) within group (order by Y asc) as SET_2\n"
+        + "from (values (1, 'a'), (1, 'b'), (3, 'c'), (3, 'd')) AS t(X, Y)\n"
+        + "limit 10";
+    CalciteAssert.that().query(sql)
+        .returns("SET_1=[d, c, b, a]; SET_2=[a, b, c, d]\n");
+  }
+
+  @Test public void testWithinGroupClause4() {
+    final String sql = "select"
+        + " collect(Y) within group (order by Y desc) as SET_1,\n"
+        + " collect(Y) within group (order by Y asc) as SET_2\n"
+        + "from (values (1, 'a'), (1, 'b'), (3, 'c'), (3, 'd')) AS t(X, Y)\n"
+        + "group by X\n"
+        + "limit 10";
+    CalciteAssert.that().query(sql)
+        .returnsUnordered("SET_1=[b, a]; SET_2=[a, b]",
+            "SET_1=[d, c]; SET_2=[c, d]");
+  }
+
+  @Test public void testWithinGroupClause5() {
+    CalciteAssert
+        .that()
+        .query("select collect(array[X, Y])\n"
+            + " within group (order by Y desc) as \"SET\"\n"
+            + "from (values ('b', 'a'), ('a', 'b'), ('a', 'c'),\n"
+            + "             ('a', 'd')) AS t(X, Y)\n"
+            + "limit 10")
+        .returns("SET=[[a, d], [a, c], [a, b], [b, a]]\n");
+  }
+
+  @Test public void testWithinGroupClause6() {
+    final String sql = "select collect(\"commission\")"
+        + " within group (order by \"commission\")\n"
+        + "from \"hr\".\"emps\"";
+    CalciteAssert.that()
+        .with(CalciteAssert.Config.REGULAR)
+        .query(sql)
+        .explainContains("EnumerableAggregate(group=[{}], "
+            + "EXPR$0=[COLLECT($4) WITHIN GROUP ([4])])")
+        .returns("EXPR$0=[250, 500, 1000]\n");
+  }
+
   private static String sums(int n, boolean c) {
     final StringBuilder b = new StringBuilder();
     for (int i = 0; i < n; i++) {

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java 
b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
index ec94d94..0c216b3 100644
--- a/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelMetadataTest.java
@@ -1350,7 +1350,7 @@ public class RelMetadataTest extends SqlToRelTestBase {
             ImmutableList.of(
                 AggregateCall.create(SqlStdOperatorTable.COUNT,
                     false, false, ImmutableIntList.of(),
-                    -1, 2, join, null, null)));
+                    -1, RelCollations.EMPTY, 2, join, null, null)));
     rowSize = mq.getAverageRowSize(aggregate);
     columnSizes = mq.getAverageColumnSizes(aggregate);
     assertThat(columnSizes.size(), equalTo(3));

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index 92f98a8..94707fe 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -2862,6 +2862,34 @@ public class SqlToRelConverterTest extends 
SqlToRelTestBase {
     sql(sql).with(tester).ok();
   }
 
+  @Test public void testWithinGroup1() {
+    final String sql = "select deptno,\n"
+        + " collect(empno) within group (order by deptno, hiredate desc)\n"
+        + "from emp\n"
+        + "group by deptno";
+    sql(sql).ok();
+  }
+
+  @Test public void testWithinGroup2() {
+    final String sql = "select dept.deptno,\n"
+        + " collect(sal) within group (order by sal desc) as s,\n"
+        + " collect(sal) within group (order by 1)as s1,\n"
+        + " collect(sal) within group (order by sal)\n"
+        + "  filter (where sal > 2000) as s2\n"
+        + "from emp\n"
+        + "join dept using (deptno)\n"
+        + "group by dept.deptno";
+    sql(sql).ok();
+  }
+
+  @Test public void testWithinGroup3() {
+    final String sql = "select deptno,\n"
+        + " collect(empno) within group (order by empno not in (1, 2)), 
count(*)\n"
+        + "from emp\n"
+        + "group by deptno";
+    sql(sql).ok();
+  }
+
   /**
    * Visitor that checks that every {@link RelNode} in a tree is valid.
    *

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java 
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index abfdc57..a1d4233 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -7116,6 +7116,40 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         .fails("FILTER must not contain aggregate expression");
   }
 
+  @Test public void testWithinGroup() {
+    sql("select deptno,\n"
+        + " collect(empno) within group(order by 1)\n"
+        + "from emp\n"
+        + "group by deptno").ok();
+    sql("select collect(empno) within group(order by 1)\n"
+        + "from emp\n"
+        + "group by ()").ok();
+    sql("select deptno,\n"
+        + " collect(empno) within group(order by deptno)\n"
+        + "from emp\n"
+        + "group by deptno").ok();
+    sql("select deptno,\n"
+        + " collect(empno) within group(order by deptno, hiredate desc)\n"
+        + "from emp\n"
+        + "group by deptno").ok();
+    sql("select deptno,\n"
+        + " collect(empno) within group(\n"
+        + "  order by cast(deptno as varchar), hiredate desc)\n"
+        + "from emp\n"
+        + "group by deptno").ok();
+    sql("select collect(empno) within group(order by 1)\n"
+        + "from emp\n"
+        + "group by deptno").ok();
+    sql("select collect(empno) within group(order by 1)\n"
+        + "from emp").ok();
+    sql("select ^power(deptno, 1) within group(order by 1)^ from emp")
+        .fails("(?s).*WITHIN GROUP not allowed with POWER function.*");
+    sql("select ^collect(empno)^ within group(order by count(*))\n"
+        + "from emp\n"
+        + "group by deptno")
+        .fails("WITHIN GROUP must not contain aggregate expression");
+  }
+
   @Test public void testCorrelatingVariables() {
     // reference to unqualified correlating column
     check("select * from emp where exists (\n"
@@ -8645,6 +8679,7 @@ public class SqlValidatorTest extends 
SqlValidatorTestCase {
         + "NEXT_VALUE -\n"
         + "PATTERN_EXCLUDE -\n"
         + "PATTERN_PERMUTE -\n"
+        + "WITHIN GROUP left\n"
         + "\n"
         + "PATTERN_QUANTIFIER -\n"
         + "\n"

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java 
b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
index e0ed1c1..3b68d16 100644
--- a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
+++ b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
@@ -72,6 +72,7 @@ import org.apache.calcite.sql.util.ListSqlOperatorTable;
 import org.apache.calcite.sql.validate.SqlValidator;
 import org.apache.calcite.sql.validate.SqlValidatorScope;
 import org.apache.calcite.test.CalciteAssert;
+import org.apache.calcite.util.Optionality;
 import org.apache.calcite.util.Util;
 
 import com.google.common.base.Throwables;
@@ -1060,7 +1061,8 @@ public class PlannerTest {
   public static class MyCountAggFunction extends SqlAggFunction {
     public MyCountAggFunction() {
       super("MY_COUNT", null, SqlKind.OTHER_FUNCTION, ReturnTypes.BIGINT, null,
-          OperandTypes.ANY, SqlFunctionCategory.NUMERIC, false, false);
+          OperandTypes.ANY, SqlFunctionCategory.NUMERIC, false, false,
+          Optionality.FORBIDDEN);
     }
 
     @SuppressWarnings("deprecation")

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
----------------------------------------------------------------------
diff --git 
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml 
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index 35d69e5..db6d2a9 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -5296,4 +5296,42 @@ LogicalProject(ANYEMPNO=[$1])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testWithinGroup1">
+        <Resource name="sql">
+            <![CDATA[select deptno, collect(empno) within group (order by 
deptno, hiredate desc) from emp group by deptno]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[COLLECT($1) WITHIN GROUP ([1, 2 DESC])])
+  LogicalProject(DEPTNO=[$7], EMPNO=[$0], HIREDATE=[$4])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testWithinGroup2">
+        <Resource name="sql">
+            <![CDATA[select dept.deptno, collect(sal) within group (order by 
sal desc) as s, collect(sal) within group (order by 1)as s1, collect(sal) 
within group (order by sal) filter (where sal > 2000) as s2 from emp join dept 
using (deptno) group by dept.deptn]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalAggregate(group=[{0}], S=[COLLECT($1) WITHIN GROUP ([1 DESC])], 
S1=[COLLECT($1) WITHIN GROUP ([2])], S2=[COLLECT($1) WITHIN GROUP ([1]) FILTER 
$3])
+  LogicalProject(DEPTNO=[$9], SAL=[$5], $f2=[1], $f3=[>($5, 2000)])
+    LogicalJoin(condition=[=($7, $9)], joinType=[inner])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+      LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testWithinGroup3">
+        <Resource name="sql">
+            <![CDATA[select deptno, collect(empno) filter (where empno not in 
(1, 2)), count(*) from emp group by deptno]]>
+        </Resource>
+        <Resource name="plan">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[COLLECT($1) WITHIN GROUP ([2])], 
EXPR$2=[COUNT()])
+  LogicalProject(DEPTNO=[$7], EMPNO=[$0], $f2=[AND(<>($0, 1), <>($0, 2))])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
 </Root>

http://git-wip-us.apache.org/repos/asf/calcite/blob/7bc9f140/core/src/test/resources/sql/agg.iq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/agg.iq 
b/core/src/test/resources/sql/agg.iq
index 24b75b3..d093a8a 100755
--- a/core/src/test/resources/sql/agg.iq
+++ b/core/src/test/resources/sql/agg.iq
@@ -2381,4 +2381,127 @@ group by MONTH(HIREDATE);
 
 !ok
 
+# [CALCITE-2224] WITHIN GROUP clause for aggregate functions
+select deptno, collect(empno) within group (order by empno asc) as empnos
+from "scott".emp
+group by deptno;
+
++--------+--------------------------------------+
+| DEPTNO | EMPNOS                               |
++--------+--------------------------------------+
+|     10 | [7782, 7839, 7934]                   |
+|     20 | [7369, 7566, 7788, 7876, 7902]       |
+|     30 | [7499, 7521, 7654, 7698, 7844, 7900] |
++--------+--------------------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{7}], EMPNOS=[COLLECT($0) WITHIN GROUP ([0])])
+  EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
+select deptno, collect(empno) within group (order by empno desc) as empnos
+from "scott".emp
+group by deptno;
+
++--------+--------------------------------------+
+| DEPTNO | EMPNOS                               |
++--------+--------------------------------------+
+|     10 | [7934, 7839, 7782]                   |
+|     20 | [7902, 7876, 7788, 7566, 7369]       |
+|     30 | [7900, 7844, 7698, 7654, 7521, 7499] |
++--------+--------------------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{7}], EMPNOS=[COLLECT($0) WITHIN GROUP ([0 DESC])])
+  EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
+select deptno, collect(empno) within group (order by empno desc)
+filter (where empno > 7500) as empnos
+from "scott".emp
+group by deptno;
+
++--------+--------------------------------+
+| DEPTNO | EMPNOS                         |
++--------+--------------------------------+
+|     10 | [7934, 7839, 7782]             |
+|     20 | [7902, 7876, 7788, 7566]       |
+|     30 | [7900, 7844, 7698, 7654, 7521] |
++--------+--------------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{0}], EMPNOS=[COLLECT($1) WITHIN GROUP ([1 DESC]) 
FILTER $2])
+  EnumerableCalc(expr#0..7=[{inputs}], expr#8=[7500], expr#9=[>($t0, $t8)], 
DEPTNO=[$t7], EMPNO=[$t0], $f2=[$t9])
+    EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
+select deptno, collect(empno) within group (order by empno desc) as empnos1,
+collect(empno) within group (order by empno asc) as empnos2
+from "scott".emp
+group by deptno;
+
++--------+--------------------------------------+--------------------------------------+
+| DEPTNO | EMPNOS1                              | EMPNOS2                      
        |
++--------+--------------------------------------+--------------------------------------+
+|     10 | [7934, 7839, 7782]                   | [7782, 7839, 7934]           
        |
+|     20 | [7902, 7876, 7788, 7566, 7369]       | [7369, 7566, 7788, 7876, 
7902]       |
+|     30 | [7900, 7844, 7698, 7654, 7521, 7499] | [7499, 7521, 7654, 7698, 
7844, 7900] |
++--------+--------------------------------------+--------------------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{7}], EMPNOS1=[COLLECT($0) WITHIN GROUP ([0 
DESC])], EMPNOS2=[COLLECT($0) WITHIN GROUP ([0])])
+  EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
+# Aggregate WITHIN GROUP with JOIN
+select dept.deptno,
+  collect(sal) within group (order by sal desc) as s,
+  collect(sal) within group (order by 1)as s1,
+  collect(sal) within group (order by sal) filter (where sal > 2000) as s2
+from "scott".emp
+join "scott".dept using (deptno)
+group by dept.deptno;
+
++--------+-------------------------------------------------------+-------------------------------------------------------+-----------------------------+
+| DEPTNO | S                                                     | S1          
                                          | S2                          |
++--------+-------------------------------------------------------+-------------------------------------------------------+-----------------------------+
+|     10 | [5000.00, 2450.00, 1300.00]                           | [2450.00, 
5000.00, 1300.00]                           | [2450.00, 5000.00]          |
+|     20 | [3000.00, 3000.00, 2975.00, 1100.00, 800.00]          | [800.00, 
2975.00, 3000.00, 1100.00, 3000.00]          | [2975.00, 3000.00, 3000.00] |
+|     30 | [2850.00, 1600.00, 1500.00, 1250.00, 1250.00, 950.00] | [1600.00, 
1250.00, 1250.00, 2850.00, 1500.00, 950.00] | [2850.00]                   |
++--------+-------------------------------------------------------+-------------------------------------------------------+-----------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{0}], S=[COLLECT($1) WITHIN GROUP ([1 DESC])], 
S1=[COLLECT($1) WITHIN GROUP ([2])], S2=[COLLECT($1) WITHIN GROUP ([1]) FILTER 
$3])
+  EnumerableCalc(expr#0..3=[{inputs}], expr#4=[1], expr#5=[2000], 
expr#6=[>($t2, $t5)], expr#7=[IS TRUE($t6)], DEPTNO=[$t0], SAL=[$t2], 
$f2=[$t4], $f3=[$t7])
+    EnumerableJoin(condition=[=($0, $3)], joinType=[inner])
+      EnumerableCalc(expr#0..2=[{inputs}], DEPTNO=[$t0])
+        EnumerableTableScan(table=[[scott, DEPT]])
+      EnumerableCalc(expr#0..7=[{inputs}], EMPNO=[$t0], SAL=[$t5], 
DEPTNO=[$t7])
+        EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
+select deptno, collect(empno + 1) within group (order by 1) as empnos
+from "scott".emp
+group by deptno;
+
++--------+--------------------------------------+
+| DEPTNO | EMPNOS                               |
++--------+--------------------------------------+
+|     10 | [7783, 7840, 7935]                   |
+|     20 | [7370, 7567, 7789, 7877, 7903]       |
+|     30 | [7500, 7522, 7655, 7699, 7845, 7901] |
++--------+--------------------------------------+
+(3 rows)
+
+!ok
+EnumerableAggregate(group=[{0}], EMPNOS=[COLLECT($1) WITHIN GROUP ([2])])
+  EnumerableCalc(expr#0..7=[{inputs}], expr#8=[1], expr#9=[+($t0, $t8)], 
DEPTNO=[$t7], $f1=[$t9], $f2=[$t8])
+    EnumerableTableScan(table=[[scott, EMP]])
+!plan
+
 # End agg.iq

Reply via email to