Repository: calcite
Updated Branches:
  refs/heads/master 425fa7b7f -> ebc43d928


[CALCITE-2654] In RelBuilder, add a fluent API for building complex aggregate 
calls

To interface AggCall in RelBuilder, add methods distinct(boolean),
filter(RexNode), approximate(boolean), alias(String). And simplify the
RelBuilder.aggregateCall method to just two arguments:
aggregateCall(op, operands). Thus you only specify the arguments that
are of interest. Similar changes to count, countStar, min, max, sum,
avg.


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

Branch: refs/heads/master
Commit: 4cc46130f71d21e4f5b76f6e645380cd83d2c921
Parents: 425fa7b
Author: Julian Hyde <[email protected]>
Authored: Sat Nov 3 19:59:11 2018 -0700
Committer: Julian Hyde <[email protected]>
Committed: Wed Nov 7 16:33:06 2018 -0800

----------------------------------------------------------------------
 .../rel/rules/AbstractMaterializedViewRule.java |  21 +-
 .../AggregateExpandDistinctAggregatesRule.java  |  21 +-
 .../rel/rules/AggregateExtractProjectRule.java  |   9 +-
 .../calcite/rel/rules/SubQueryRemoveRule.java   |   8 +-
 .../apache/calcite/sql2rel/RelFieldTrimmer.java |   8 +-
 .../org/apache/calcite/tools/PigRelBuilder.java |   3 +-
 .../org/apache/calcite/tools/RelBuilder.java    | 249 ++++++++++++++++---
 .../rel/rel2sql/RelToSqlConverterTest.java      |   4 +-
 .../org/apache/calcite/test/RelBuilderTest.java |  66 +++--
 site/_docs/algebra.md                           |  22 +-
 10 files changed, 298 insertions(+), 113 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
index 3a82f95..68a9382 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java
@@ -1194,13 +1194,14 @@ public abstract class AbstractMaterializedViewRule 
extends RelOptRule {
           // Cannot rollup this aggregate, bail out
           return null;
         }
+        final RexInputRef operand =
+            rexBuilder.makeInputRef(relBuilder.peek(),
+                aggregate.getGroupCount() + i);
         aggregateCalls.add(
-            relBuilder.aggregateCall(
-                rollupAgg,
-                aggCall.isDistinct(), aggCall.isApproximate(), null,
-                aggCall.name,
-                rexBuilder.makeInputRef(relBuilder.peek(),
-                    aggregate.getGroupCount() + i)));
+            relBuilder.aggregateCall(rollupAgg, operand)
+                .distinct(aggCall.isDistinct())
+                .approximate(aggCall.isApproximate())
+                .as(aggCall.name));
       }
       RelNode prevNode = relBuilder.peek();
       RelNode result = relBuilder
@@ -1466,10 +1467,12 @@ public abstract class AbstractMaterializedViewRule 
extends RelOptRule {
                 // Cannot rollup this aggregate, bail out
                 return null;
               }
+              final RexInputRef operand = rexBuilder.makeInputRef(input, k);
               aggregateCalls.add(
-                  relBuilder.aggregateCall(
-                      rollupAgg, queryAggCall.isDistinct(), 
queryAggCall.isApproximate(),
-                      null, queryAggCall.name, rexBuilder.makeInputRef(input, 
k)));
+                  relBuilder.aggregateCall(rollupAgg, operand)
+                      .approximate(queryAggCall.isApproximate())
+                      .distinct(queryAggCall.isDistinct())
+                      .as(queryAggCall.name));
               rewritingMapping.set(k, sourceIdx);
               added = true;
             }

http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/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 d150989..6d974d8 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
@@ -274,14 +274,15 @@ public final class AggregateExpandDistinctAggregatesRule 
extends RelOptRule {
 
     // Add the distinct aggregate column(s) to the group-by columns,
     // if not already a part of the group-by
-    final SortedSet<Integer> bottomGroupSet = new TreeSet<>();
-    bottomGroupSet.addAll(aggregate.getGroupSet().asList());
+    final SortedSet<Integer> bottomGroups = new TreeSet<>();
+    bottomGroups.addAll(aggregate.getGroupSet().asList());
     for (AggregateCall aggCall : originalAggCalls) {
       if (aggCall.isDistinct()) {
-        bottomGroupSet.addAll(aggCall.getArgList());
+        bottomGroups.addAll(aggCall.getArgList());
         break;  // since we only have single distinct call
       }
     }
+    final ImmutableBitSet bottomGroupSet = ImmutableBitSet.of(bottomGroups);
 
     // Generate the intermediate aggregate B, the one on the bottom that 
converts
     // a distinct call to group by call.
@@ -295,16 +296,15 @@ public final class AggregateExpandDistinctAggregatesRule 
extends RelOptRule {
         final AggregateCall newCall =
             AggregateCall.create(aggCall.getAggregation(), false,
                 aggCall.isApproximate(), aggCall.getArgList(), -1,
-                ImmutableBitSet.of(bottomGroupSet).cardinality(),
+                bottomGroupSet.cardinality(),
                 relBuilder.peek(), null, aggCall.name);
         bottomAggregateCalls.add(newCall);
       }
     }
     // Generate the aggregate B (see the reference example above)
     relBuilder.push(
-        aggregate.copy(
-            aggregate.getTraitSet(), relBuilder.build(),
-            false, ImmutableBitSet.of(bottomGroupSet), null, 
bottomAggregateCalls));
+        aggregate.copy(aggregate.getTraitSet(), relBuilder.build(),
+            false, bottomGroupSet, null, bottomAggregateCalls));
 
     // Add aggregate A (see the reference example above), the top aggregate
     // to handle the rest of the aggregation that the bottom aggregate hasn't 
handled
@@ -316,7 +316,7 @@ public final class AggregateExpandDistinctAggregatesRule 
extends RelOptRule {
       if (aggCall.isDistinct()) {
         List<Integer> newArgList = new ArrayList<>();
         for (int arg : aggCall.getArgList()) {
-          newArgList.add(bottomGroupSet.headSet(arg).size());
+          newArgList.add(bottomGroups.headSet(arg).size());
         }
         newCall =
             AggregateCall.create(aggCall.getAggregation(),
@@ -332,7 +332,8 @@ public final class AggregateExpandDistinctAggregatesRule 
extends RelOptRule {
         // If aggregate B had a COUNT aggregate call the corresponding 
aggregate at
         // aggregate A must be SUM. For other aggregates, it remains the same.
         final List<Integer> newArgs =
-            Lists.newArrayList(bottomGroupSet.size() + 
nonDistinctAggCallProcessedSoFar);
+            Lists.newArrayList(bottomGroups.size()
+                + nonDistinctAggCallProcessedSoFar);
         if (aggCall.getAggregation().getKind() == SqlKind.COUNT) {
           newCall =
               AggregateCall.create(new SqlSumEmptyIsZeroAggFunction(), false,
@@ -357,7 +358,7 @@ public final class AggregateExpandDistinctAggregatesRule 
extends RelOptRule {
     // output), minus the distinct aggCall's input.
     final Set<Integer> topGroupSet = new HashSet<>();
     int groupSetToAdd = 0;
-    for (int bottomGroup : bottomGroupSet) {
+    for (int bottomGroup : bottomGroups) {
       if (originalGroupSet.get(bottomGroup)) {
         topGroupSet.add(groupSetToAdd);
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
index 5559c97..08b12dd 100644
--- 
a/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
+++ 
b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
@@ -116,9 +116,12 @@ public class AggregateExtractProjectRule extends 
RelOptRule {
       final RexNode filterArg = aggCall.filterArg < 0 ? null
           : relBuilder.field(Mappings.apply(mapping, aggCall.filterArg));
       newAggCallList.add(
-          relBuilder.aggregateCall(aggCall.getAggregation(),
-              aggCall.isDistinct(), aggCall.isApproximate(),
-              filterArg, aggCall.name, args));
+          relBuilder.aggregateCall(aggCall.getAggregation(), args)
+              .distinct(aggCall.isDistinct())
+              .filter(filterArg)
+              .approximate(aggCall.isApproximate())
+              .approximate(aggCall.isApproximate())
+              .as(aggCall.name));
     }
 
     final RelBuilder.GroupKey groupKey =

http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java 
b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
index 974c57d..39bccbf 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/SubQueryRemoveRule.java
@@ -124,8 +124,8 @@ public abstract class SubQueryRemoveRule extends RelOptRule 
{
         ImmutableBitSet.of());
     if (unique == null || !unique) {
       builder.aggregate(builder.groupKey(),
-          builder.aggregateCall(SqlStdOperatorTable.SINGLE_VALUE, false,
-              false, null, null, builder.field(0)));
+          builder.aggregateCall(SqlStdOperatorTable.SINGLE_VALUE,
+              builder.field(0)));
     }
     builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);
     return field(builder, inputCount, offset);
@@ -385,8 +385,8 @@ public abstract class SubQueryRemoveRule extends RelOptRule 
{
         // Builds the cross join
         builder.aggregate(builder.groupKey(),
             builder.count(false, "c"),
-            builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false, 
null,
-                "ck", builder.fields()));
+            builder.aggregateCall(SqlStdOperatorTable.COUNT, builder.fields())
+                .as("ck"));
         builder.as("ct");
         if (!variablesSet.isEmpty()) {
           builder.join(JoinRelType.LEFT, builder.literal(true), variablesSet);

http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/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 733ee02..e9031ba 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java
@@ -867,9 +867,11 @@ public class RelFieldTrimmer implements ReflectiveVisitor {
         final RexNode filterArg = aggCall.filterArg < 0 ? null
             : relBuilder.field(Mappings.apply(inputMapping, 
aggCall.filterArg));
         RelBuilder.AggCall newAggCall =
-            relBuilder.aggregateCall(aggCall.getAggregation(),
-                aggCall.isDistinct(), aggCall.isApproximate(),
-                filterArg, aggCall.name, args);
+            relBuilder.aggregateCall(aggCall.getAggregation(), args)
+                .distinct(aggCall.isDistinct())
+                .filter(filterArg)
+                .approximate(aggCall.isApproximate())
+                .as(aggCall.name);
         mapping.set(j, groupCount + indicatorCount + newAggCallList.size());
         newAggCallList.add(newAggCall);
       }

http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java 
b/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
index dcac18b..50738cd 100644
--- a/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/PigRelBuilder.java
@@ -146,8 +146,7 @@ public class PigRelBuilder extends RelBuilder {
           cluster.getRexBuilder().makeCall(peek(1, 0).getRowType(),
               SqlStdOperatorTable.ROW, fields());
       aggregate(groupKey.e,
-          aggregateCall(SqlStdOperatorTable.COLLECT, false, false, null,
-              getAlias(), row));
+          aggregateCall(SqlStdOperatorTable.COLLECT, row).as(getAlias()));
       if (groupKey.i < n - 1) {
         push(r);
         List<RexNode> predicates = new ArrayList<>();

http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/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 d2b5d5d..323f65a 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -763,72 +763,129 @@ public class RelBuilder {
   @Deprecated // to be removed before 2.0
   public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
       RexNode filter, String alias, RexNode... operands) {
-    return aggregateCall(aggFunction, distinct, false, filter, alias,
-        ImmutableList.copyOf(operands));
+    return aggregateCall(aggFunction, distinct, false, filter,
+        ImmutableList.of(), alias, ImmutableList.copyOf(operands));
   }
 
-  /** Creates a call to an aggregate function. */
+  @Deprecated // to be removed before 2.0
   public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
       boolean approximate, RexNode filter, String alias, RexNode... operands) {
-    return aggregateCall(aggFunction, distinct, approximate, filter, alias,
-        ImmutableList.copyOf(operands));
+    return aggregateCall(aggFunction, distinct, approximate, filter,
+        ImmutableList.of(), alias, ImmutableList.copyOf(operands));
   }
 
   @Deprecated // to be removed before 2.0
   public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
       RexNode filter, String alias, Iterable<? extends RexNode> operands) {
-    return aggregateCall(aggFunction, distinct, false, filter, alias, 
operands);
+    return aggregateCall(aggFunction, distinct, false, filter,
+        ImmutableList.of(), alias, ImmutableList.copyOf(operands));
   }
 
-  /** Creates a call to an aggregate function. */
+  @Deprecated // to be removed before 2.0
   public AggCall aggregateCall(SqlAggFunction aggFunction, boolean distinct,
       boolean approximate, RexNode filter, String alias,
       Iterable<? extends RexNode> operands) {
-    if (filter != null) {
-      if (filter.getType().getSqlTypeName() != SqlTypeName.BOOLEAN) {
-        throw RESOURCE.filterMustBeBoolean().ex();
-      }
-      if (filter.getType().isNullable()) {
-        filter = call(SqlStdOperatorTable.IS_TRUE, filter);
-      }
-    }
+    return aggregateCall(aggFunction, distinct, filter, alias, operands)
+        .approximate(approximate);
+  }
+
+  /** 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. */
+  public AggCall aggregateCall(SqlAggFunction aggFunction,
+      Iterable<? extends RexNode> operands) {
+    return aggregateCall(aggFunction, false, false, null, ImmutableList.of(),
+        null, 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. */
+  public AggCall aggregateCall(SqlAggFunction aggFunction,
+      RexNode... operands) {
+    return aggregateCall(aggFunction, false, false, null, ImmutableList.of(),
+        null, ImmutableList.copyOf(operands));
+  }
+
+  /** Creates a call to an aggregate function with all applicable operands. */
+  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,
-        ImmutableList.copyOf(operands));
+        operands, orderKeys);
+  }
+
+  /** Creates a call to the {@code COUNT} aggregate function. */
+  public AggCall count(RexNode... operands) {
+    return count(false, null, operands);
   }
 
-  /** Creates a call to the COUNT aggregate function. */
+  /** 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, false, null,
-        alias, operands);
+    return aggregateCall(SqlStdOperatorTable.COUNT, distinct, null, alias,
+        ImmutableList.copyOf(operands));
   }
 
-  /** Creates a call to the COUNT(*) aggregate function. */
+  /** Creates a call to the {@code COUNT(*)} aggregate function. */
   public AggCall countStar(String alias) {
-    return aggregateCall(SqlStdOperatorTable.COUNT, false, false, null, alias);
+    return count(false, alias);
   }
 
-  /** Creates a call to the SUM aggregate function. */
+  /** Creates a call to the {@code SUM} aggregate function. */
+  public AggCall sum(RexNode operand) {
+    return sum(false, null, operand);
+  }
+
+  /** 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, false, null, alias,
-        operand);
+    return aggregateCall(SqlStdOperatorTable.SUM, distinct, null, alias,
+        ImmutableList.of(operand));
   }
 
-  /** Creates a call to the AVG aggregate function. */
+  /** Creates a call to the {@code AVG} aggregate function. */
+  public AggCall avg(RexNode operand) {
+    return avg(false, null, operand);
+  }
+
+  /** 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, false, null, alias,
-        operand);
+    return aggregateCall(SqlStdOperatorTable.AVG, distinct, null, alias,
+        ImmutableList.of(operand));
   }
 
-  /** Creates a call to the MIN aggregate function. */
+  /** Creates a call to the {@code MIN} aggregate function. */
+  public AggCall min(RexNode operand) {
+    return min(null, operand);
+  }
+
+  /** 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, false, null, alias,
-        operand);
+    return aggregateCall(SqlStdOperatorTable.MIN, false, null, alias,
+        ImmutableList.of(operand));
+  }
+
+  /** Creates a call to the {@code MAX} aggregate function,
+   * optionally with an alias. */
+  public AggCall max(RexNode operand) {
+    return max(null, operand);
   }
 
-  /** Creates a call to the MAX aggregate function. */
+  /** Creates a call to the {@code MAX} aggregate function. */
   public AggCall max(String alias, RexNode operand) {
-    return aggregateCall(SqlStdOperatorTable.MAX, false, false, null, alias,
-        operand);
+    return aggregateCall(SqlStdOperatorTable.MAX, false, null, alias,
+        ImmutableList.of(operand));
   }
 
   // Methods for patterns
@@ -2041,6 +2098,32 @@ public class RelBuilder {
    *
    * @see RelBuilder#aggregateCall */
   public interface AggCall {
+    /** Returns a copy of this AggCall that applies a filter before aggregating
+     * values. */
+    AggCall filter(RexNode condition);
+
+    /** Returns a copy of this AggCall that sorts its input values by
+     * {@code orderKeys} before aggregating, as in SQL's {@code WITHIN GROUP}
+     * clause. */
+    AggCall sort(Iterable<RexNode> orderKeys);
+
+    /** Returns a copy of this AggCall that sorts its input values by
+     * {@code orderKeys} before aggregating, as in SQL's {@code WITHIN GROUP}
+     * clause. */
+    AggCall sort(RexNode... orderKeys);
+
+    /** Returns a copy of this AggCall that may return approximate results
+     * if {@code approximate} is true. */
+    AggCall approximate(boolean approximate);
+
+    /** Returns a copy of this AggCall with a given alias. */
+    AggCall as(String alias);
+
+    /** Returns a copy of this AggCall that is optionally distinct. */
+    AggCall distinct(boolean distinct);
+
+    /** Returns a copy of this AggCall that is distinct. */
+    AggCall distinct();
   }
 
   /** Information necessary to create the GROUP BY clause of an Aggregate.
@@ -2081,23 +2164,79 @@ public class RelBuilder {
   }
 
   /** Implementation of {@link AggCall}. */
-  private static class AggCallImpl implements AggCall {
+  private class AggCallImpl implements AggCall {
     private final SqlAggFunction aggFunction;
     private final boolean distinct;
     private final boolean approximate;
-    private final RexNode filter;
-    private final String alias;
-    private final ImmutableList<RexNode> operands;
+    private final RexNode filter; // may be null
+    private final String alias; // may be null
+    private final ImmutableList<RexNode> operands; // may be empty, never null
+    private final ImmutableList<RexNode> orderKeys; // may be empty, never null
 
     AggCallImpl(SqlAggFunction aggFunction, boolean distinct,
         boolean approximate, RexNode filter,
-        String alias, ImmutableList<RexNode> operands) {
-      this.aggFunction = aggFunction;
+        String alias, ImmutableList<RexNode> operands,
+        ImmutableList<RexNode> orderKeys) {
+      this.aggFunction = Objects.requireNonNull(aggFunction);
       this.distinct = distinct;
       this.approximate = approximate;
-      this.filter = filter;
       this.alias = alias;
-      this.operands = operands;
+      this.operands = Objects.requireNonNull(operands);
+      this.orderKeys = Objects.requireNonNull(orderKeys);
+      if (filter != null) {
+        if (filter.getType().getSqlTypeName() != SqlTypeName.BOOLEAN) {
+          throw RESOURCE.filterMustBeBoolean().ex();
+        }
+        if (filter.getType().isNullable()) {
+          filter = call(SqlStdOperatorTable.IS_TRUE, filter);
+        }
+      }
+      this.filter = filter;
+    }
+
+    public AggCall sort(Iterable<RexNode> orderKeys) {
+      final ImmutableList<RexNode> orderKeyList =
+          ImmutableList.copyOf(orderKeys);
+      return orderKeyList.equals(this.orderKeys)
+          ? this
+          : new AggCallImpl(aggFunction, distinct, approximate, filter,
+              alias, operands, orderKeyList);
+    }
+
+    public AggCall sort(RexNode... orderKeys) {
+      return sort(ImmutableList.copyOf(orderKeys));
+    }
+
+    public AggCall approximate(boolean approximate) {
+      return approximate == this.approximate
+          ? this
+          : new AggCallImpl(aggFunction, distinct, approximate, filter,
+              alias, operands, orderKeys);
+    }
+
+    public AggCall filter(RexNode condition) {
+      return Objects.equals(condition, this.filter)
+          ? this
+          : new AggCallImpl(aggFunction, distinct, approximate, condition,
+              alias, operands, orderKeys);
+    }
+
+    public AggCall as(String alias) {
+      return Objects.equals(alias, this.alias)
+          ? this
+          : new AggCallImpl(aggFunction, distinct, approximate, filter,
+              alias, operands, orderKeys);
+    }
+
+    public AggCall distinct(boolean distinct) {
+      return distinct == this.distinct
+          ? this
+          : new AggCallImpl(aggFunction, distinct, approximate, filter,
+              alias, operands, orderKeys);
+    }
+
+    public AggCall distinct() {
+      return distinct(true);
     }
   }
 
@@ -2109,6 +2248,34 @@ public class RelBuilder {
     AggCallImpl2(AggregateCall aggregateCall) {
       this.aggregateCall = Objects.requireNonNull(aggregateCall);
     }
+
+    public AggCall sort(Iterable<RexNode> orderKeys) {
+      throw new UnsupportedOperationException();
+    }
+
+    public AggCall sort(RexNode... orderKeys) {
+      throw new UnsupportedOperationException();
+    }
+
+    public AggCall approximate(boolean approximate) {
+      throw new UnsupportedOperationException();
+    }
+
+    public AggCall filter(RexNode condition) {
+      throw new UnsupportedOperationException();
+    }
+
+    public AggCall as(String alias) {
+      throw new UnsupportedOperationException();
+    }
+
+    public AggCall distinct(boolean distinct) {
+      throw new UnsupportedOperationException();
+    }
+
+    public AggCall distinct() {
+      throw new UnsupportedOperationException();
+    }
   }
 
   /** Collects the extra expressions needed for {@link #aggregate}.

http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/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 70bf317..94b98b7 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, false, null,
-                "s", builder.field(3)))
+            builder.aggregateCall(SqlStdOperatorTable.SUM0, false, null, "s",
+                builder.field(3)))
         .build();
     final String expectedMysql = "SELECT COALESCE(SUM(`MGR`), 0) AS `s`\n"
         + "FROM `scott`.`EMP`";

http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java 
b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index 3624204..e3d5b95 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -505,7 +505,6 @@ public class RelBuilderTest {
                     builder.literal(20)))
             .aggregate(builder.groupKey(0, 1, 2),
                 builder.aggregateCall(SqlStdOperatorTable.SUM,
-                    false, false, null, null,
                     builder.field(0)))
             .project(builder.field("c"),
                 builder.field("a"))
@@ -661,8 +660,7 @@ public class RelBuilderTest {
     RelNode root =
         builder.scan("EMP")
             .aggregate(builder.groupKey(),
-                builder.aggregateCall(SqlStdOperatorTable.COUNT, true, false,
-                    null, "C", builder.field("DEPTNO")))
+                builder.count(true, "C", builder.field("DEPTNO")))
             .build();
     final String expected = ""
         + "LogicalAggregate(group=[{}], C=[COUNT(DISTINCT $7)])\n"
@@ -684,12 +682,10 @@ public class RelBuilderTest {
                         builder.field(4),
                         builder.field(3)),
                     builder.field(1)),
-                builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false,
-                    null, "C"),
-                builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
-                    null, "S",
+                builder.countStar("C"),
+                builder.sum(
                     builder.call(SqlStdOperatorTable.PLUS, builder.field(3),
-                        builder.literal(1))))
+                        builder.literal(1))).as("S"))
             .build();
     final String expected = ""
         + "LogicalAggregate(group=[{1, 8}], C=[COUNT()], S=[SUM($9)])\n"
@@ -711,12 +707,9 @@ public class RelBuilderTest {
     final RelBuilder builder = RelBuilder.create(config().build());
     RelNode root =
         builder.scan("EMP")
-            .aggregate(
-                builder.groupKey(builder.field(1)),
-                builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false,
-                    null, "C"))
-            .aggregate(
-                builder.groupKey(builder.field(0)))
+            .aggregate(builder.groupKey(builder.field(1)),
+                builder.count().as("C"))
+            .aggregate(builder.groupKey(builder.field(0)))
             .build();
     final String expected = ""
         + "LogicalProject(ENAME=[$0])\n"
@@ -738,8 +731,7 @@ public class RelBuilderTest {
         builder.scan("EMP")
             .aggregate(
                 builder.groupKey(builder.field(1)),
-                builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false,
-                    null, "C"))
+                builder.count().as("C"))
             .filter(
                 builder.call(SqlStdOperatorTable.GREATER_THAN, 
builder.field(1),
                     builder.literal(3)))
@@ -766,9 +758,11 @@ public class RelBuilderTest {
                 builder.groupKey(ImmutableBitSet.of(7),
                     ImmutableList.of(ImmutableBitSet.of(7),
                         ImmutableBitSet.of())),
-                builder.aggregateCall(SqlStdOperatorTable.COUNT, false, false,
-                    builder.call(SqlStdOperatorTable.GREATER_THAN,
-                        builder.field("EMPNO"), builder.literal(100)), "C"))
+                builder.count()
+                    .filter(
+                        builder.call(SqlStdOperatorTable.GREATER_THAN,
+                            builder.field("EMPNO"), builder.literal(100)))
+                    .as("C"))
             .build();
     final String expected = ""
         + "LogicalAggregate(group=[{7}], groups=[[{7}, {}]], C=[COUNT() FILTER 
$8])\n"
@@ -788,8 +782,9 @@ public class RelBuilderTest {
           builder.scan("EMP")
               .aggregate(
                   builder.groupKey(builder.field("DEPTNO")),
-                  builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
-                      builder.field("COMM"), "C", builder.field("SAL")))
+                  builder.sum(builder.field("SAL"))
+                      .filter(builder.field("COMM"))
+                      .as("C"))
               .build();
       fail("expected error, got " + root);
     } catch (CalciteException e) {
@@ -808,10 +803,11 @@ public class RelBuilderTest {
         builder.scan("EMP")
             .aggregate(
                 builder.groupKey(builder.field("DEPTNO")),
-                builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
-                    builder.call(SqlStdOperatorTable.LESS_THAN,
-                        builder.field("COMM"), builder.literal(100)), "C",
-                    builder.field("SAL")))
+                builder.sum(builder.field("SAL"))
+                    .filter(
+                        builder.call(SqlStdOperatorTable.LESS_THAN,
+                            builder.field("COMM"), builder.literal(100)))
+                    .as("C"))
             .build();
     final String expected = ""
         + "LogicalAggregate(group=[{7}], C=[SUM($5) FILTER $8])\n"
@@ -912,8 +908,8 @@ public class RelBuilderTest {
     RelNode root =
         builder.scan("EMP")
             .aggregate(builder.groupKey(6, 7),
-                builder.aggregateCall(SqlStdOperatorTable.GROUPING, false,
-                    false, null, "g", builder.field("DEPTNO")))
+                builder.aggregateCall(SqlStdOperatorTable.GROUPING,
+                    builder.field("DEPTNO")).as("g"))
             .build();
     final String expected = ""
         + "LogicalAggregate(group=[{6, 7}], g=[GROUPING($7)])\n"
@@ -927,8 +923,10 @@ public class RelBuilderTest {
       RelNode root =
           builder.scan("EMP")
               .aggregate(builder.groupKey(6, 7),
-                  builder.aggregateCall(SqlStdOperatorTable.GROUPING, true,
-                      false, null, "g", builder.field("DEPTNO")))
+                  builder.aggregateCall(SqlStdOperatorTable.GROUPING,
+                      builder.field("DEPTNO"))
+                      .distinct(true)
+                      .as("g"))
               .build();
       fail("expected error, got " + root);
     } catch (IllegalArgumentException e) {
@@ -942,9 +940,10 @@ public class RelBuilderTest {
       RelNode root =
           builder.scan("EMP")
               .aggregate(builder.groupKey(6, 7),
-                  builder.aggregateCall(SqlStdOperatorTable.GROUPING, false,
-                      false, builder.literal(true), "g",
-                      builder.field("DEPTNO")))
+                  builder.aggregateCall(SqlStdOperatorTable.GROUPING,
+                      builder.field("DEPTNO"))
+                      .filter(builder.literal(true))
+                      .as("g"))
               .build();
       fail("expected error, got " + root);
     } catch (IllegalArgumentException e) {
@@ -1482,8 +1481,7 @@ public class RelBuilderTest {
             .project(builder.field("DEPTNO"),
                 builder.literal(20))
             .aggregate(builder.groupKey(builder.field("EMP_alias", "DEPTNO")),
-                builder.aggregateCall(SqlStdOperatorTable.SUM, false, false,
-                    null, null, builder.field(1)))
+                builder.sum(builder.field(1)))
             .project(builder.alias(builder.field(1), "sum"),
                 builder.field("EMP_alias", "DEPTNO"))
             .build();

http://git-wip-us.apache.org/repos/asf/calcite/blob/4cc46130/site/_docs/algebra.md
----------------------------------------------------------------------
diff --git a/site/_docs/algebra.md b/site/_docs/algebra.md
index 2b5c66a..e5b505b 100644
--- a/site/_docs/algebra.md
+++ b/site/_docs/algebra.md
@@ -390,9 +390,21 @@ The following methods return an
 
 | Method              | Description
 |:------------------- |:-----------
-| `aggregateCall(op, distinct, approximate, filter, alias, 
expr...)`<br/>`aggregateCall(op, distinct, approximate, filter, alias, 
exprList)` | Creates a call to a given aggregate function, with an optional 
filter expression
-| `count(distinct, alias, expr...)` | Creates a call to the COUNT aggregate 
function
+| `aggregateCall(op, distinct, approximate, filter, orderKeys, alias, 
expr...)`<br/>`aggregateCall(op, distinct, approximate, filter, orderKeys, 
alias, exprList)` | Creates a call to a given aggregate function, with an 
optional filter expression and a list of optional ordering keys (for sorting 
input values before aggregation)
+| `count( [ distinct, alias, ] expr...)` | Creates a call to the COUNT 
aggregate function
 | `countStar(alias)` | Creates a call to the COUNT(*) aggregate function
-| `sum(distinct, alias, expr)` | Creates a call to the SUM aggregate function
-| `min(alias, expr)` | Creates a call to the MIN aggregate function
-| `max(alias, expr)` | Creates a call to the MAX aggregate function
+| `sum( [ distinct, alias, ] expr)` | Creates a call to the SUM aggregate 
function
+| `min( [ alias, ] expr)` | Creates a call to the MIN aggregate function
+| `max( [ alias, ] expr)` | Creates a call to the MAX aggregate function
+
+To further modify the `AggCall`, call its methods:
+
+| Method               | Description
+|:-------------------- |:-----------
+| `distinct()`         | Eliminates duplicate values before aggregating (see 
SQL `DISTINCT`)
+| `distinct(distinct)` | Eliminates duplicate values before aggregating if 
`distinct`
+| `as(alias)`          | Assigns a column alias to this expression (see SQL 
`AS`)
+| `filter(expr)`       | Filters rows before aggregating (see SQL `FILTER 
(WHERE ...)`)
+| `sort(expr, ...)`<br/>`sort(exprList)` | Sorts rows before aggregating (see 
SQL `WITHIN GROUP`)
+| `approximate(approximate)` | Allows approximate value for the aggregate of 
`approximate`
+

Reply via email to