calcite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jh...@apache.org
Subject [19/19] incubator-calcite git commit: [CALCITE-732] Implement multiple distinct-COUNT using GROUPING SETS
Date Mon, 01 Jun 2015 17:56:33 GMT
[CALCITE-732] Implement multiple distinct-COUNT using GROUPING SETS


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

Branch: refs/heads/master
Commit: 3e50232b681e8dadb921580ee6f3e0376dd0f664
Parents: cd9c908
Author: Julian Hyde <jhyde@apache.org>
Authored: Fri May 22 10:11:06 2015 -0700
Committer: Julian Hyde <jhyde@apache.org>
Committed: Mon Jun 1 00:01:55 2015 -0700

----------------------------------------------------------------------
 .../org/apache/calcite/rel/core/Aggregate.java  |  27 ++-
 .../apache/calcite/rel/core/AggregateCall.java  |   9 +-
 .../AggregateExpandDistinctAggregatesRule.java  | 210 ++++++++++++++++-
 .../apache/calcite/sql/SqlOperatorBinding.java  |   7 +
 .../apache/calcite/sql/type/ReturnTypes.java    |   2 +-
 .../apache/calcite/util/ImmutableBitSet.java    |  16 +-
 .../apache/calcite/util/ImmutableIntList.java   |  10 +
 .../apache/calcite/test/RelOptRulesTest.java    |  69 ++++++
 .../calcite/util/ImmutableBitSetTest.java       |  17 +-
 .../org/apache/calcite/test/RelOptRulesTest.xml | 228 +++++++++++++++----
 core/src/test/resources/sql/agg.oq              |  34 +++
 11 files changed, 559 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java b/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java
index a483595..b95e44c 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/Aggregate.java
@@ -40,6 +40,7 @@ import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.sql.validate.SqlValidatorException;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.calcite.util.IntList;
+import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 
 import com.google.common.base.Preconditions;
@@ -197,6 +198,17 @@ public abstract class Aggregate extends SingleRel {
   }
 
   /**
+   * Returns a list of calls to aggregate functions together with their output
+   * field names.
+   *
+   * @return list of calls to aggregate functions and their output field names
+   */
+  public List<Pair<AggregateCall, String>> getNamedAggCalls() {
+    final int offset = getGroupCount() + getIndicatorCount();
+    return Pair.zip(aggCalls, Util.skip(getRowType().getFieldNames(), offset));
+  }
+
+  /**
    * Returns the number of grouping fields.
    * These grouping fields are the leading fields in both the input and output
    * records.
@@ -422,6 +434,7 @@ public abstract class Aggregate extends SingleRel {
   public static class AggCallBinding extends SqlOperatorBinding {
     private final List<RelDataType> operands;
     private final int groupCount;
+    private final boolean filter;
 
     /**
      * Creates an AggCallBinding
@@ -430,15 +443,15 @@ public abstract class Aggregate extends SingleRel {
      * @param aggFunction  Aggregate function
      * @param operands     Data types of operands
      * @param groupCount   Number of columns in the GROUP BY clause
+     * @param filter       Whether the aggregate function has a FILTER clause
      */
-    public AggCallBinding(
-        RelDataTypeFactory typeFactory,
-        SqlAggFunction aggFunction,
-        List<RelDataType> operands,
-        int groupCount) {
+    public AggCallBinding(RelDataTypeFactory typeFactory,
+        SqlAggFunction aggFunction, List<RelDataType> operands, int groupCount,
+        boolean filter) {
       super(typeFactory, aggFunction);
       this.operands = operands;
       this.groupCount = groupCount;
+      this.filter = filter;
       assert operands != null
           : "operands of aggregate call should not be null";
       assert groupCount >= 0
@@ -450,6 +463,10 @@ public abstract class Aggregate extends SingleRel {
       return groupCount;
     }
 
+    @Override public boolean hasFilter() {
+      return filter;
+    }
+
     public int getOperandCount() {
       return operands.size();
     }

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
index 0589e58..2b56522 100644
--- a/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
+++ b/core/src/main/java/org/apache/calcite/rel/core/AggregateCall.java
@@ -23,6 +23,7 @@ import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.type.SqlTypeUtil;
 import org.apache.calcite.util.mapping.Mappings;
 
+import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
@@ -114,7 +115,7 @@ public class AggregateCall {
           SqlTypeUtil.projectTypes(input.getRowType(), argList);
       final Aggregate.AggCallBinding callBinding =
           new Aggregate.AggCallBinding(typeFactory, aggFunction, types,
-              groupCount);
+              groupCount, filterArg >= 0);
       type = aggFunction.inferReturnType(callBinding);
     }
     return create(aggFunction, distinct, argList, filterArg, type, name);
@@ -182,7 +183,9 @@ public class AggregateCall {
    * @param name New name (may be null)
    */
   public AggregateCall rename(String name) {
-    // no need to copy argList - already immutable
+    if (Objects.equal(this.name, name)) {
+      return this;
+    }
     return new AggregateCall(aggFunction, distinct, argList, filterArg, type,
         name);
   }
@@ -236,7 +239,7 @@ public class AggregateCall {
     return new Aggregate.AggCallBinding(
         aggregateRelBase.getCluster().getTypeFactory(), aggFunction,
         SqlTypeUtil.projectTypes(rowType, argList),
-        filterArg >= 0 ? 0 : aggregateRelBase.getGroupCount());
+        aggregateRelBase.getGroupCount(), filterArg >= 0);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/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 96a031a..81733f0 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
@@ -16,6 +16,7 @@
  */
 package org.apache.calcite.rel.rules;
 
+import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.plan.RelOptRuleCall;
 import org.apache.calcite.plan.RelOptUtil;
@@ -25,13 +26,18 @@ import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.rel.core.JoinRelType;
 import org.apache.calcite.rel.core.RelFactories;
 import org.apache.calcite.rel.logical.LogicalAggregate;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeField;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeName;
 import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Pair;
 import org.apache.calcite.util.Util;
 
@@ -40,12 +46,14 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TreeSet;
 
 /**
  * Planner rule that expands distinct aggregates
@@ -68,20 +76,41 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule
{
 
   /** The default instance of the rule; operates only on logical expressions. */
   public static final AggregateExpandDistinctAggregatesRule INSTANCE =
-      new AggregateExpandDistinctAggregatesRule(LogicalAggregate.class,
+      new AggregateExpandDistinctAggregatesRule(LogicalAggregate.class, true,
           RelFactories.DEFAULT_JOIN_FACTORY);
 
-  private final RelFactories.JoinFactory joinFactory;
+  /** Instance of the rule that operates only on logical expressions and
+   * generates a join. */
+  public static final AggregateExpandDistinctAggregatesRule JOIN =
+      new AggregateExpandDistinctAggregatesRule(LogicalAggregate.class, false,
+          RelFactories.DEFAULT_JOIN_FACTORY);
+  public static final BigDecimal TWO = BigDecimal.valueOf(2L);
+
+  public final boolean useGroupingSets;
+  public final RelFactories.JoinFactory joinFactory;
+  public final RelFactories.AggregateFactory aggregateFactory =
+      RelFactories.DEFAULT_AGGREGATE_FACTORY;
+  public final RelFactories.ProjectFactory projectFactory =
+      RelFactories.DEFAULT_PROJECT_FACTORY;
 
   //~ Constructors -----------------------------------------------------------
 
   public AggregateExpandDistinctAggregatesRule(
       Class<? extends LogicalAggregate> clazz,
+      boolean useGroupingSets,
       RelFactories.JoinFactory joinFactory) {
     super(operand(clazz, any()));
+    this.useGroupingSets = useGroupingSets;
     this.joinFactory = joinFactory;
   }
 
+  @Deprecated // to be removed before 2.0
+  public AggregateExpandDistinctAggregatesRule(
+      Class<? extends LogicalAggregate> clazz,
+      RelFactories.JoinFactory joinFactory) {
+    this(clazz, false, joinFactory);
+  }
+
   //~ Methods ----------------------------------------------------------------
 
   public void onMatch(RelOptRuleCall call) {
@@ -113,6 +142,11 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule
{
       return;
     }
 
+    if (useGroupingSets) {
+      rewriteUsingGroupingSets(call, aggregate, argLists);
+      return;
+    }
+
     // Create a list of the expressions which will yield the final result.
     // Initially, the expressions point to the input field.
     final List<RelDataTypeField> aggFields =
@@ -149,9 +183,9 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule
{
     if (newAggCallList.isEmpty()) {
       rel = null;
     } else {
-      rel =
-          LogicalAggregate.create(aggregate.getInput(), aggregate.indicator,
-              groupSet, aggregate.getGroupSets(), newAggCallList);
+      rel = aggregateFactory.createAggregate(aggregate.getInput(),
+          aggregate.indicator,
+          groupSet, aggregate.getGroupSets(), newAggCallList);
     }
 
     // For each set of operands, find and rewrite all calls which have that
@@ -165,6 +199,172 @@ public final class AggregateExpandDistinctAggregatesRule extends RelOptRule
{
     call.transformTo(rel);
   }
 
+  private void rewriteUsingGroupingSets(RelOptRuleCall ruleCall,
+      Aggregate aggregate, Set<Pair<List<Integer>, Integer>> argLists)
{
+    final Set<ImmutableBitSet> groupSetTreeSet =
+        new TreeSet<>(ImmutableBitSet.ORDERING);
+    groupSetTreeSet.add(aggregate.getGroupSet());
+    for (Pair<List<Integer>, Integer> argList : argLists) {
+      groupSetTreeSet.add(
+          ImmutableBitSet.of(argList.left)
+              .setIf(argList.right, argList.right >= 0)
+              .union(aggregate.getGroupSet()));
+    }
+
+    final ImmutableList<ImmutableBitSet> groupSets =
+        ImmutableList.copyOf(groupSetTreeSet);
+    final ImmutableBitSet fullGroupSet = ImmutableBitSet.union(groupSets);
+
+    final List<AggregateCall> distinctAggCalls = new ArrayList<>();
+    for (Pair<AggregateCall, String> call : aggregate.getNamedAggCalls()) {
+      if (!call.left.isDistinct()) {
+        distinctAggCalls.add(call.left.rename(call.right));
+      }
+    }
+
+    final RelNode distinct =
+        aggregateFactory.createAggregate(aggregate.getInput(),
+            groupSets.size() > 1, fullGroupSet, groupSets, distinctAggCalls);
+    final int groupCount = fullGroupSet.cardinality();
+    final int indicatorCount = groupSets.size() > 1 ? groupCount : 0;
+
+    final RelOptCluster cluster = aggregate.getCluster();
+    final RexBuilder rexBuilder = cluster.getRexBuilder();
+    final RelDataTypeFactory typeFactory = cluster.getTypeFactory();
+    final RelDataType booleanType =
+        typeFactory.createTypeWithNullability(
+            typeFactory.createSqlType(SqlTypeName.BOOLEAN), false);
+    final List<Pair<RexNode, String>> predicates = new ArrayList<>();
+    final Map<ImmutableBitSet, Integer> filters = new HashMap<>();
+    /** Function to register an filter for a group set. */
+    class Registrar {
+      RexNode group = null;
+      int register(ImmutableBitSet groupSet) {
+        if (group == null) {
+          group = makeGroup(groupCount - 1);
+        }
+        final RexNode node =
+            rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, group,
+                rexBuilder.makeExactLiteral(
+                    toNumber(remap(fullGroupSet, groupSet))));
+        predicates.add(Pair.of(node, toString(groupSet)));
+        return groupCount + indicatorCount + distinctAggCalls.size()
+            + predicates.size() - 1;
+      }
+
+      private RexNode makeGroup(int i) {
+        final RexInputRef ref =
+            rexBuilder.makeInputRef(booleanType, groupCount + i);
+        final RexNode kase =
+            rexBuilder.makeCall(SqlStdOperatorTable.CASE, ref,
+                rexBuilder.makeExactLiteral(BigDecimal.ZERO),
+                rexBuilder.makeExactLiteral(TWO.pow(i)));
+        if (i == 0) {
+          return kase;
+        } else {
+          return rexBuilder.makeCall(SqlStdOperatorTable.PLUS,
+              makeGroup(i - 1), kase);
+        }
+      }
+
+      private BigDecimal toNumber(ImmutableBitSet bitSet) {
+        BigDecimal n = BigDecimal.ZERO;
+        for (int key : bitSet) {
+          n = n.add(TWO.pow(key));
+        }
+        return n;
+      }
+
+      private String toString(ImmutableBitSet bitSet) {
+        final StringBuilder buf = new StringBuilder("$i");
+        for (int key : bitSet) {
+          buf.append(key).append('_');
+        }
+        return buf.substring(0, buf.length() - 1);
+      }
+    }
+    final Registrar registrar = new Registrar();
+    for (ImmutableBitSet groupSet : groupSets) {
+      filters.put(groupSet, registrar.register(groupSet));
+    }
+
+    RelNode r = distinct;
+    if (!predicates.isEmpty()) {
+      List<Pair<RexNode, String>> nodes = new ArrayList<>();
+      for (RelDataTypeField f : r.getRowType().getFieldList()) {
+        final RexNode node = rexBuilder.makeInputRef(f.getType(), f.getIndex());
+        nodes.add(Pair.of(node, f.getName()));
+      }
+      nodes.addAll(predicates);
+      r = RelOptUtil.createProject(r, nodes, false);
+    }
+
+    int x = groupCount + indicatorCount;
+    final List<AggregateCall> newCalls = new ArrayList<>();
+    for (AggregateCall call : aggregate.getAggCallList()) {
+      final int newFilterArg;
+      final List<Integer> newArgList;
+      final SqlAggFunction aggregation;
+      if (!call.isDistinct()) {
+        aggregation = SqlStdOperatorTable.MIN;
+        newArgList = ImmutableIntList.of(x++);
+        newFilterArg = filters.get(aggregate.getGroupSet());
+      } else {
+        aggregation = call.getAggregation();
+        newArgList = remap(fullGroupSet, call.getArgList());
+        newFilterArg =
+            filters.get(
+                ImmutableBitSet.of(call.getArgList())
+                    .setIf(call.filterArg, call.filterArg >= 0)
+                    .union(aggregate.getGroupSet()));
+      }
+      final AggregateCall newCall =
+          AggregateCall.create(aggregation, false, newArgList, newFilterArg,
+              aggregate.getGroupCount(), distinct, null, call.name);
+      newCalls.add(newCall);
+    }
+
+    final RelNode newAggregate =
+        aggregateFactory.createAggregate(r, aggregate.indicator,
+            remap(fullGroupSet, aggregate.getGroupSet()),
+            remap(fullGroupSet, aggregate.getGroupSets()), newCalls);
+    ruleCall.transformTo(
+        RelOptUtil.createCastRel(newAggregate, aggregate.getRowType(), true,
+            projectFactory));
+  }
+
+  private static ImmutableBitSet remap(ImmutableBitSet groupSet,
+      ImmutableBitSet bitSet) {
+    final ImmutableBitSet.Builder builder = ImmutableBitSet.builder();
+    for (Integer bit : bitSet) {
+      builder.set(remap(groupSet, bit));
+    }
+    return builder.build();
+  }
+
+  private static ImmutableList<ImmutableBitSet> remap(ImmutableBitSet groupSet,
+      Iterable<ImmutableBitSet> bitSets) {
+    final ImmutableList.Builder<ImmutableBitSet> builder =
+        ImmutableList.builder();
+    for (ImmutableBitSet bitSet : bitSets) {
+      builder.add(remap(groupSet, bitSet));
+    }
+    return builder.build();
+  }
+
+  private static List<Integer> remap(ImmutableBitSet groupSet,
+      List<Integer> argList) {
+    ImmutableIntList list = ImmutableIntList.of();
+    for (int arg : argList) {
+      list = list.add(remap(groupSet, arg));
+    }
+    return list;
+  }
+
+  private static int remap(ImmutableBitSet groupSet, int arg) {
+    return arg < 0 ? -1 : groupSet.indexOf(arg);
+  }
+
   /**
    * Converts an aggregate relational expression that contains just one
    * distinct aggregate function (or perhaps several over the same arguments)

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java b/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java
index e767049..3e3b6f6 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlOperatorBinding.java
@@ -68,6 +68,13 @@ public abstract class SqlOperatorBinding {
   }
 
   /**
+   * Returns whether the operator is an aggregate function with a filter.
+   */
+  public boolean hasFilter() {
+    return false;
+  }
+
+  /**
    * @return bound operator
    */
   public SqlOperator getOperator() {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
index d9fcdcd..7095ac6 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/ReturnTypes.java
@@ -130,7 +130,7 @@ public abstract class ReturnTypes {
         @Override public RelDataType
         inferReturnType(SqlOperatorBinding opBinding) {
           final RelDataType type = super.inferReturnType(opBinding);
-          if (opBinding.getGroupCount() == 0) {
+          if (opBinding.getGroupCount() == 0 || opBinding.hasFilter()) {
             return opBinding.getTypeFactory()
                 .createTypeWithNullability(type, true);
           } else {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java b/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java
index 75d66de..d35a446 100644
--- a/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java
+++ b/core/src/main/java/org/apache/calcite/util/ImmutableBitSet.java
@@ -317,7 +317,9 @@ public class ImmutableBitSet
       b.append(i);
       for (i = nextSetBit(i + 1); i >= 0; i = nextSetBit(i + 1)) {
         int endOfRun = nextClearBit(i);
-        do { b.append(", ").append(i); }
+        do {
+          b.append(", ").append(i);
+        }
         while (++i < endOfRun);
       }
     }
@@ -742,11 +744,23 @@ public class ImmutableBitSet
     return union(ImmutableBitSet.of(i));
   }
 
+  /** Returns a bit set the same as this but with a given bit set if condition
+   * is true. */
+  public ImmutableBitSet setIf(int bit, boolean condition) {
+    return condition ? set(bit) : this;
+  }
+
   /** Returns a bit set the same as this but with a given bit cleared. */
   public ImmutableBitSet clear(int i) {
     return except(ImmutableBitSet.of(i));
   }
 
+  /** Returns a bit set the same as this but with a given bit cleared if
+   * condition is true. */
+  public ImmutableBitSet clearIf(int i, boolean condition) {
+    return condition ? except(ImmutableBitSet.of(i)) : this;
+  }
+
   /** Returns a {@link BitSet} that has the same contents as this
    * {@code ImmutableBitSet}. */
   public BitSet toBitSet() {

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/core/src/main/java/org/apache/calcite/util/ImmutableIntList.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/ImmutableIntList.java b/core/src/main/java/org/apache/calcite/util/ImmutableIntList.java
index 867c8f3..3dcfae6 100644
--- a/core/src/main/java/org/apache/calcite/util/ImmutableIntList.java
+++ b/core/src/main/java/org/apache/calcite/util/ImmutableIntList.java
@@ -221,6 +221,16 @@ public class ImmutableIntList extends FlatLists.AbstractFlatList<Integer>
{
     return -1;
   }
 
+  /** Returns a copy of this list with one element added. */
+  public ImmutableIntList add(int element) {
+    if (ints.length == 0) {
+      return of(element);
+    }
+    final int[] newInts = Arrays.copyOf(this.ints, ints.length + 1);
+    newInts[ints.length] = element;
+    return new ImmutableIntList(newInts);
+  }
+
   /** Returns a list that contains the values lower to upper - 1.
    *
    * <p>For example, {@code range(1, 3)} contains [1, 2]. */

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
index 41017c8..0c72361 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -427,6 +427,75 @@ public class RelOptRulesTest extends RelOptTestBase {
             + " from sales.emp group by deptno");
   }
 
+  /** Tests implementing multiple distinct count the old way, using a join. */
+  @Test public void testDistinctCountMultipleViaJoin() {
+    final HepProgram program = HepProgram.builder()
+        .addRuleInstance(AggregateExpandDistinctAggregatesRule.JOIN)
+        .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+        .build();
+    checkPlanning(program,
+        "select deptno, count(distinct ename), count(distinct job, ename),\n"
+            + "  count(distinct deptno, job), sum(sal)\n"
+            + " from sales.emp group by deptno");
+  }
+
+  /** Tests implementing multiple distinct count the new way, using GROUPING
+   *  SETS. */
+  @Test public void testDistinctCountMultiple() {
+    final HepProgram program = HepProgram.builder()
+        .addRuleInstance(AggregateExpandDistinctAggregatesRule.INSTANCE)
+        .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+        .build();
+    checkPlanning(program,
+        "select deptno, count(distinct ename), count(distinct job)\n"
+            + " from sales.emp group by deptno");
+  }
+
+  @Test public void testDistinctCountMultipleNoGroup() {
+    final HepProgram program = HepProgram.builder()
+        .addRuleInstance(AggregateExpandDistinctAggregatesRule.INSTANCE)
+        .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+        .build();
+    checkPlanning(program,
+        "select count(distinct ename), count(distinct job)\n"
+            + " from sales.emp");
+  }
+
+  @Test public void testDistinctCountMixedJoin() {
+    final HepProgram program = HepProgram.builder()
+        .addRuleInstance(AggregateExpandDistinctAggregatesRule.JOIN)
+        .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+        .build();
+    checkPlanning(program,
+        "select deptno, count(distinct ename), count(distinct job, ename),\n"
+            + "  count(distinct deptno, job), sum(sal)\n"
+            + " from sales.emp group by deptno");
+  }
+
+  @Test public void testDistinctCountMixed() {
+    final HepProgram program = HepProgram.builder()
+        .addRuleInstance(AggregateExpandDistinctAggregatesRule.INSTANCE)
+        .addRuleInstance(ProjectMergeRule.INSTANCE)
+        .build();
+    checkPlanning(program,
+        "select deptno, count(distinct deptno, job) as cddj, sum(sal) as s\n"
+            + " from sales.emp group by deptno");
+  }
+
+  @Test public void testDistinctCountMixed2() {
+    final HepProgram program = HepProgram.builder()
+        .addRuleInstance(AggregateExpandDistinctAggregatesRule.INSTANCE)
+        .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+        .addRuleInstance(ProjectMergeRule.INSTANCE)
+        .build();
+    checkPlanning(program,
+        "select deptno, count(distinct ename) as cde,\n"
+            + "  count(distinct job, ename) as cdje,\n"
+            + "  count(distinct deptno, job) as cddj,\n"
+            + "  sum(sal) as s\n"
+            + " from sales.emp group by deptno");
+  }
+
   @Test public void testDistinctCountGroupingSets1() {
     final HepProgram program = HepProgram.builder()
         .addRuleInstance(AggregateExpandDistinctAggregatesRule.INSTANCE)

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/core/src/test/java/org/apache/calcite/util/ImmutableBitSetTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/util/ImmutableBitSetTest.java b/core/src/test/java/org/apache/calcite/util/ImmutableBitSetTest.java
index 605817c..da34dc7 100644
--- a/core/src/test/java/org/apache/calcite/util/ImmutableBitSetTest.java
+++ b/core/src/test/java/org/apache/calcite/util/ImmutableBitSetTest.java
@@ -428,18 +428,23 @@ public class ImmutableBitSetTest {
 
   @Test public void testSet() {
     final ImmutableBitSet bitSet = ImmutableBitSet.of(29, 4, 1969);
-    assertThat(bitSet.set(30),
-        equalTo(ImmutableBitSet.of(29, 4, 1969, 30)));
-    assertThat(bitSet.set(29),
-        equalTo(bitSet));
+    final ImmutableBitSet bitSet2 = ImmutableBitSet.of(29, 4, 1969, 30);
+    assertThat(bitSet.set(30), equalTo(bitSet2));
+    assertThat(bitSet.set(30).set(30), equalTo(bitSet2));
+    assertThat(bitSet.set(29), equalTo(bitSet));
+    assertThat(bitSet.setIf(30, false), equalTo(bitSet));
+    assertThat(bitSet.setIf(30, true), equalTo(bitSet2));
   }
 
   @Test public void testClear() {
     final ImmutableBitSet bitSet = ImmutableBitSet.of(29, 4, 1969);
-    assertThat(bitSet.clear(29),
-        equalTo(ImmutableBitSet.of(4, 1969)));
+    final ImmutableBitSet bitSet2 = ImmutableBitSet.of(4, 1969);
+    assertThat(bitSet.clear(29), equalTo(bitSet2));
+    assertThat(bitSet.clear(29).clear(29), equalTo(bitSet2));
     assertThat(bitSet.clear(29).clear(4).clear(29).clear(1969),
         equalTo(ImmutableBitSet.of()));
+    assertThat(bitSet.clearIf(29, false), equalTo(bitSet));
+    assertThat(bitSet.clearIf(29, true), equalTo(bitSet2));
   }
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
index dea51a9..bec6c76 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -1576,11 +1576,7 @@ LogicalProject(DEPTNO=[CASE($2, null, $0)], JOB=[CASE($3, null, $1)],
EXPR$2=[$4
     </TestCase>
     <TestCase name="testPullFilterThroughAggregate">
         <Resource name="sql">
-            <![CDATA[select empno, sal, deptno from (
-  select empno, sal, deptno
-   from emp
-   where sal > 5000)
-  group by empno, sal, deptno]]>
+            <![CDATA[select empno, sal, deptno from (  select empno, sal, deptno  from
emp  where sal > 5000)group by empno, sal, deptno]]>
         </Resource>
         <Resource name="planBefore">
             <![CDATA[
@@ -1601,11 +1597,7 @@ LogicalFilter(condition=[>($1, 5000)])
     </TestCase>
     <TestCase name="testPullFilterThroughAggregateGroupingSets">
         <Resource name="sql">
-            <![CDATA[select empno, sal, deptno from (
-  select empno, sal, deptno
-   from emp
-   where sal > 5000)
-  group by rollup(empno, sal, deptno)]]>
+            <![CDATA[select empno, sal, deptno from (  select empno, sal, deptno  from
emp  where sal > 5000)group by rollup(empno, sal, deptno)]]>
         </Resource>
         <Resource name="planBefore">
             <![CDATA[
@@ -2972,13 +2964,7 @@ LogicalProject(X=[$0], EXPR$1=[$4], Y=[$1])
     </TestCase>
     <TestCase name="testPullAggregateThroughUnion">
         <Resource name="sql">
-            <![CDATA[select deptno, job from
- (select deptno, job from emp as e1
- group by deptno,job
-  union all
- select deptno, job from emp as e2
- group by deptno,job)
- group by deptno,job]]>
+            <![CDATA[select deptno, job from (select deptno, job from emp as e1 group
by deptno,job  union all select deptno, job from emp as e2 group by deptno,job) group by deptno,job]]>
         </Resource>
         <Resource name="planBefore">
             <![CDATA[
@@ -3084,13 +3070,12 @@ LogicalAggregate(group=[{0}], EXPR$1=[COUNT(DISTINCT $1)], EXPR$2=[SUM($2)])
         </Resource>
         <Resource name="planAfter">
             <![CDATA[
-LogicalProject(DEPTNO=[$0], EXPR$1=[$3], EXPR$2=[$1])
-  LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $2)], joinType=[inner])
-    LogicalAggregate(group=[{7}], EXPR$2=[SUM($5)])
-      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
-    LogicalAggregate(group=[{1}], EXPR$1=[COUNT($0)])
-      LogicalAggregate(group=[{1, 7}])
-        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+LogicalProject(DEPTNO=[$0], EXPR$1=[$1], EXPR$2=[CAST($2):INTEGER NOT NULL])
+  LogicalAggregate(group=[{0}], EXPR$1=[COUNT($1) FILTER $5], EXPR$2=[MIN($4) FILTER $6])
+    LogicalProject(DEPTNO=[$0], ENAME=[$1], i$DEPTNO=[$2], i$ENAME=[$3], EXPR$2=[$4], $i0_1=[=(+(CASE($2,
0, 1), CASE($3, 0, 2)), 3)], $i0=[=(+(CASE($2, 0, 1), CASE($3, 0, 2)), 1)])
+      LogicalProject(DEPTNO=[$1], ENAME=[$0], i$DEPTNO=[$3], i$ENAME=[$2], EXPR$2=[$4])
+        LogicalAggregate(group=[{1, 7}], groups=[[{1, 7}, {7}]], indicator=[true], EXPR$2=[SUM($5)])
+          LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
     </TestCase>
@@ -3133,14 +3118,11 @@ LogicalProject(DEPTNO=[$0], JOB=[$1], EXPR$2=[$4], EXPR$3=[$5])
         </Resource>
         <Resource name="planAfter">
             <![CDATA[
-LogicalProject(DEPTNO=[CASE($2, null, $0)], JOB=[CASE($3, null, $1)], EXPR$2=[$9], EXPR$3=[$4])
-  LogicalJoin(condition=[AND(IS NOT DISTINCT FROM($0, $5), IS NOT DISTINCT FROM($1, $6),
IS NOT DISTINCT FROM($2, $7), IS NOT DISTINCT FROM($3, $8))], joinType=[inner])
-    LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}, {}]], indicator=[true], EXPR$3=[SUM($3)])
-      LogicalProject(DEPTNO=[$7], JOB=[$2], ENAME=[$1], SAL=[$5])
-        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
-    LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}, {}]], indicator=[true], EXPR$2=[COUNT($2)])
-      LogicalAggregate(group=[{0, 1, 2}])
-        LogicalProject(DEPTNO=[$7], JOB=[$2], ENAME=[$1])
+LogicalProject(DEPTNO=[CASE($2, null, $0)], JOB=[CASE($3, null, $1)], EXPR$2=[$4], EXPR$3=[CAST($5):INTEGER
NOT NULL])
+  LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}, {}]], indicator=[true], EXPR$2=[COUNT($2)
FILTER $7], EXPR$3=[MIN($6) FILTER $8])
+    LogicalProject(DEPTNO=[$0], JOB=[$1], ENAME=[$2], i$DEPTNO=[$3], i$JOB=[$4], i$ENAME=[$5],
EXPR$3=[$6], $i0_1_2=[=(+(+(CASE($3, 0, 1), CASE($4, 0, 2)), CASE($5, 0, 4)), 7)], $i0_1=[=(+(+(CASE($3,
0, 1), CASE($4, 0, 2)), CASE($5, 0, 4)), 3)])
+      LogicalAggregate(group=[{0, 1, 2}], groups=[[{0, 1, 2}, {0, 1}]], indicator=[true],
EXPR$3=[SUM($3)])
+        LogicalProject(DEPTNO=[$7], JOB=[$2], ENAME=[$1], SAL=[$5])
           LogicalTableScan(table=[[CATALOG, SALES, EMP]])
 ]]>
         </Resource>
@@ -3361,10 +3343,8 @@ LogicalProject(EXPR$0=[$0], EXPR$1=[$1])
     </TestCase>
     <TestCase name="testPushAggregateThroughJoin1">
         <Resource name="sql">
-            <![CDATA[select e.empno,d.deptno
- from (select * from sales.emp where empno = 10) as e
- join sales.dept as d on e.empno = d.deptno
- group by e.empno,d.deptno]]>
+            <![CDATA[select e.empno,d.deptno 
+from (select * from sales.emp where empno = 10) as e join sales.dept as d on e.empno = d.deptno
group by e.empno,d.deptno]]>
         </Resource>
         <Resource name="planBefore">
             <![CDATA[
@@ -3390,11 +3370,8 @@ LogicalJoin(condition=[=($0, $1)], joinType=[inner])
     </TestCase>
     <TestCase name="testPushAggregateThroughJoin2">
         <Resource name="sql">
-            <![CDATA[select e.empno,d.deptno
- from (select * from sales.emp where empno = 10) as e
- join sales.dept as d on e.empno = d.deptno
- and e.deptno + e.empno = d.deptno + 5
- group by e.empno,d.deptno]]>
+            <![CDATA[select e.empno,d.deptno 
+from (select * from sales.emp where empno = 10) as e join sales.dept as d on e.empno = d.deptno
and e.deptno + e.empno = d.deptno + 5 group by e.empno,d.deptno]]>
         </Resource>
         <Resource name="planBefore">
             <![CDATA[
@@ -3423,10 +3400,8 @@ LogicalAggregate(group=[{0, 10}])
     </TestCase>
     <TestCase name="testPushAggregateThroughJoin3">
         <Resource name="sql">
-            <![CDATA[select e.empno,d.deptno
- from (select * from sales.emp where empno = 10) as e
- join sales.dept as d on e.empno < d.deptno
- group by e.empno,d.deptno]]>
+            <![CDATA[select e.empno,d.deptno 
+from (select * from sales.emp where empno = 10) as e join sales.dept as d on e.empno <
d.deptno group by e.empno,d.deptno]]>
         </Resource>
         <Resource name="planBefore">
             <![CDATA[
@@ -3451,10 +3426,8 @@ LogicalAggregate(group=[{0, 9}])
     </TestCase>
     <TestCase name="testPushAggregateThroughJoin4">
         <Resource name="sql">
-            <![CDATA[select e.empno,sum(sal)
- from (select * from sales.emp where empno = 10) as e
- join sales.dept as d on e.empno = d.deptno
- group by e.empno,d.deptno]]>
+            <![CDATA[select e.empno,sum(sal) 
+from (select * from sales.emp where empno = 10) as e join sales.dept as d on e.empno = d.deptno
group by e.empno,d.deptno]]>
         </Resource>
         <Resource name="planBefore">
             <![CDATA[
@@ -3501,4 +3474,161 @@ LogicalProject(EXPR$0=[1])
 ]]>
         </Resource>
     </TestCase>
+    <TestCase name="testDistinctCountMultipleViaJoin">
+        <Resource name="sql">
+            <![CDATA[select deptno, count(distinct ename), count(distinct job, ename),
+  count(distinct deptno, job), sum(sal)
+ from sales.emp group by deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[COUNT(DISTINCT $1)], EXPR$2=[COUNT(DISTINCT $2, $1)],
EXPR$3=[COUNT(DISTINCT $0, $2)], EXPR$4=[SUM($3)])
+  LogicalProject(DEPTNO=[$7], ENAME=[$1], JOB=[$2], SAL=[$5])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DEPTNO=[$0], EXPR$1=[$3], EXPR$2=[$5], EXPR$3=[$7], EXPR$4=[$1])
+  LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $6)], joinType=[inner])
+    LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $4)], joinType=[inner])
+      LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $2)], joinType=[inner])
+        LogicalAggregate(group=[{7}], EXPR$4=[SUM($5)])
+          LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+        LogicalAggregate(group=[{1}], EXPR$1=[COUNT($0)])
+          LogicalAggregate(group=[{1, 7}])
+            LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+      LogicalAggregate(group=[{2}], EXPR$2=[COUNT($1, $0)])
+        LogicalAggregate(group=[{1, 2, 7}])
+          LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalAggregate(group=[{1}], EXPR$3=[COUNT($1, $0)])
+      LogicalAggregate(group=[{2, 7}])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testDistinctCountMultiple">
+        <Resource name="sql">
+            <![CDATA[select deptno, count(distinct ename), count(distinct job)
+ from sales.emp group by deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[COUNT(DISTINCT $1)], EXPR$2=[COUNT(DISTINCT $2)])
+  LogicalProject(DEPTNO=[$7], ENAME=[$1], JOB=[$2])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[COUNT($1) FILTER $6], EXPR$2=[COUNT($2) FILTER $7])
+  LogicalProject(DEPTNO=[$0], ENAME=[$1], JOB=[$2], i$DEPTNO=[$3], i$ENAME=[$4], i$JOB=[$5],
$i0_1=[=(+(+(CASE($3, 0, 1), CASE($4, 0, 2)), CASE($5, 0, 4)), 3)], $i0_2=[=(+(+(CASE($3,
0, 1), CASE($4, 0, 2)), CASE($5, 0, 4)), 5)], $i0=[=(+(+(CASE($3, 0, 1), CASE($4, 0, 2)),
CASE($5, 0, 4)), 1)])
+    LogicalProject(DEPTNO=[$2], ENAME=[$0], JOB=[$1], i$DEPTNO=[$5], i$ENAME=[$3], i$JOB=[$4])
+      LogicalAggregate(group=[{1, 2, 7}], groups=[[{1, 7}, {2, 7}, {7}]], indicator=[true])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testDistinctCountMultipleNoGroup">
+        <Resource name="sql">
+            <![CDATA[select count(distinct ename), count(distinct job)
+ from sales.emp]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{}], EXPR$0=[COUNT(DISTINCT $0)], EXPR$1=[COUNT(DISTINCT $1)])
+  LogicalProject(ENAME=[$1], JOB=[$2])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalAggregate(group=[{}], EXPR$0=[COUNT($0) FILTER $4], EXPR$1=[COUNT($1) FILTER $5])
+  LogicalProject(ENAME=[$0], JOB=[$1], i$ENAME=[$2], i$JOB=[$3], $i0=[=(+(CASE($2, 0, 1),
CASE($3, 0, 2)), 1)], $i1=[=(+(CASE($2, 0, 1), CASE($3, 0, 2)), 2)], $=[=(+(CASE($2, 0, 1),
CASE($3, 0, 2)), 0)])
+    LogicalAggregate(group=[{1, 2}], groups=[[{1}, {2}, {}]], indicator=[true])
+      LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testDistinctCountMixed">
+        <Resource name="sql">
+            <![CDATA[select deptno, count(distinct deptno, job) as cddj, sum(sal) as s
+ from sales.emp group by deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{0}], CDDJ=[COUNT(DISTINCT $0, $1)], S=[SUM($2)])
+  LogicalProject(DEPTNO=[$7], JOB=[$2], SAL=[$5])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DEPTNO=[$0], CDDJ=[$1], S=[CAST($2):INTEGER NOT NULL])
+  LogicalAggregate(group=[{0}], CDDJ=[COUNT($0, $1) FILTER $5], S=[MIN($4) FILTER $6])
+    LogicalProject(DEPTNO=[$0], JOB=[$1], i$DEPTNO=[$2], i$JOB=[$3], S=[$4], $i0_1=[=(+(CASE($2,
0, 1), CASE($3, 0, 2)), 3)], $i0=[=(+(CASE($2, 0, 1), CASE($3, 0, 2)), 1)])
+      LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}]], indicator=[true], S=[SUM($2)])
+        LogicalProject(DEPTNO=[$7], JOB=[$2], SAL=[$5])
+          LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testDistinctCountMixedJoin">
+        <Resource name="sql">
+            <![CDATA[select deptno, count(distinct ename), count(distinct job, ename),
+  count(distinct deptno, job), sum(sal)
+ from sales.emp group by deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{0}], EXPR$1=[COUNT(DISTINCT $1)], EXPR$2=[COUNT(DISTINCT $2, $1)],
EXPR$3=[COUNT(DISTINCT $0, $2)], EXPR$4=[SUM($3)])
+  LogicalProject(DEPTNO=[$7], ENAME=[$1], JOB=[$2], SAL=[$5])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DEPTNO=[$0], EXPR$1=[$3], EXPR$2=[$5], EXPR$3=[$7], EXPR$4=[$1])
+  LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $6)], joinType=[inner])
+    LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $4)], joinType=[inner])
+      LogicalJoin(condition=[IS NOT DISTINCT FROM($0, $2)], joinType=[inner])
+        LogicalAggregate(group=[{7}], EXPR$4=[SUM($5)])
+          LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+        LogicalAggregate(group=[{1}], EXPR$1=[COUNT($0)])
+          LogicalAggregate(group=[{1, 7}])
+            LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+      LogicalAggregate(group=[{2}], EXPR$2=[COUNT($1, $0)])
+        LogicalAggregate(group=[{1, 2, 7}])
+          LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+    LogicalAggregate(group=[{1}], EXPR$3=[COUNT($1, $0)])
+      LogicalAggregate(group=[{2, 7}])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
+    <TestCase name="testDistinctCountMixed2">
+        <Resource name="sql">
+            <![CDATA[select deptno, count(distinct ename) as cde,
+  count(distinct job, ename) as cdje,
+  count(distinct deptno, job) as cddj,
+  sum(sal) as s
+ from sales.emp group by deptno]]>
+        </Resource>
+        <Resource name="planBefore">
+            <![CDATA[
+LogicalAggregate(group=[{0}], CDE=[COUNT(DISTINCT $1)], CDJE=[COUNT(DISTINCT $2, $1)], CDDJ=[COUNT(DISTINCT
$0, $2)], S=[SUM($3)])
+  LogicalProject(DEPTNO=[$7], ENAME=[$1], JOB=[$2], SAL=[$5])
+    LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+        <Resource name="planAfter">
+            <![CDATA[
+LogicalProject(DEPTNO=[$0], CDE=[$1], CDJE=[$2], CDDJ=[$3], S=[CAST($4):INTEGER NOT NULL])
+  LogicalAggregate(group=[{0}], CDE=[COUNT($1) FILTER $8], CDJE=[COUNT($2, $1) FILTER $7],
CDDJ=[COUNT($0, $2) FILTER $9], S=[MIN($6) FILTER $10])
+    LogicalProject(DEPTNO=[$2], ENAME=[$0], JOB=[$1], i$DEPTNO=[$5], i$ENAME=[$3], i$JOB=[$4],
S=[$6], $i0_1_2=[=(+(+(CASE($5, 0, 1), CASE($3, 0, 2)), CASE($4, 0, 4)), 7)], $i0_1=[=(+(+(CASE($5,
0, 1), CASE($3, 0, 2)), CASE($4, 0, 4)), 3)], $i0_2=[=(+(+(CASE($5, 0, 1), CASE($3, 0, 2)),
CASE($4, 0, 4)), 5)], $i0=[=(+(+(CASE($5, 0, 1), CASE($3, 0, 2)), CASE($4, 0, 4)), 1)])
+      LogicalAggregate(group=[{1, 2, 7}], groups=[[{1, 2, 7}, {1, 7}, {2, 7}, {7}]], indicator=[true],
S=[SUM($5)])
+        LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+        </Resource>
+    </TestCase>
 </Root>

http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/3e50232b/core/src/test/resources/sql/agg.oq
----------------------------------------------------------------------
diff --git a/core/src/test/resources/sql/agg.oq b/core/src/test/resources/sql/agg.oq
index 0936b0e..802f1dc 100644
--- a/core/src/test/resources/sql/agg.oq
+++ b/core/src/test/resources/sql/agg.oq
@@ -726,6 +726,40 @@ group by deptno;
 
 !ok
 
+# Multiple distinct count and non-distinct aggregates
+select deptno,
+ count(distinct job) as dj,
+ count(job) as j,
+ count(distinct mgr) as m,
+ sum(sal) as s
+from "scott".emp
+group by deptno;
++--------+----+---+---+----------+
+| DEPTNO | DJ | J | M | S        |
++--------+----+---+---+----------+
+|     10 |  3 | 3 | 2 |  8750.00 |
+|     20 |  3 | 5 | 4 | 10875.00 |
+|     30 |  3 | 6 | 2 |  9400.00 |
++--------+----+---+---+----------+
+(3 rows)
+
+!ok
+
+# Multiple distinct count and non-distinct aggregates, no GROUP BY
+select count(distinct job) as dj,
+ count(job) as j,
+ count(distinct mgr) as m,
+ sum(sal) as s
+from "scott".emp;
++----+----+---+----------+
+| DJ | J  | M | S        |
++----+----+---+----------+
+|  5 | 14 | 6 | 29025.00 |
++----+----+---+----------+
+(1 row)
+
+!ok
+
 # [CALCITE-729] IndexOutOfBoundsException in ROLLUP query on JDBC data source
 !use jdbc_scott
 select deptno, job, count(*) as c



Mime
View raw message