You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2015/06/01 19:56:33 UTC
[19/19] incubator-calcite git commit: [CALCITE-732] Implement
multiple distinct-COUNT using GROUPING SETS
[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 <jh...@apache.org>
Authored: Fri May 22 10:11:06 2015 -0700
Committer: Julian Hyde <jh...@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