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 2018/01/03 02:35:03 UTC
[3/3] calcite git commit: [CALCITE-2113] Prune the columns coming
from the input of an Aggregate by pushing a Project,
in both core and Druid adapter (Nishant Bangarwa)
[CALCITE-2113] Prune the columns coming from the input of an Aggregate by pushing a Project, in both core and Druid adapter (Nishant Bangarwa)
Introduce a rule, DruidAggregateExtractProjectRule that
transforms Aggregate(DruidQuery) to Aggregate(Project(DruidQuery)).
The Project would fix column pruning to Druid in case
HepPlanner is used to plan queries.
Also a general-purpose rule AggregateExtractProjectRule.
Close apache/calcite#594
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/d3b35a4b
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/d3b35a4b
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/d3b35a4b
Branch: refs/heads/master
Commit: d3b35a4b5c9270f905175fefa312ab5d8207c1b0
Parents: 1e9fd38
Author: Nishant <ni...@gmail.com>
Authored: Fri Dec 29 23:49:25 2017 +0530
Committer: Julian Hyde <jh...@apache.org>
Committed: Tue Jan 2 17:52:42 2018 -0800
----------------------------------------------------------------------
.../rel/rules/AggregateExtractProjectRule.java | 145 +++++++++++++++++++
.../apache/calcite/test/RelOptRulesTest.java | 79 +++++++++-
.../org/apache/calcite/test/RelOptRulesTest.xml | 82 ++++++++++-
.../calcite/adapter/druid/DruidRules.java | 28 ++++
.../org/apache/calcite/test/DruidAdapterIT.java | 4 +-
5 files changed, 328 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/d3b35a4b/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
new file mode 100644
index 0000000..5bb88ec
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateExtractProjectRule.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.rules;
+
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptRuleCall;
+import org.apache.calcite.plan.RelOptRuleOperand;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.AggregateCall;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.tools.RelBuilderFactory;
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.mapping.Mapping;
+import org.apache.calcite.util.mapping.MappingType;
+import org.apache.calcite.util.mapping.Mappings;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+/**
+ * Rule to extract a {@link org.apache.calcite.rel.core.Project}
+ * from an {@link org.apache.calcite.rel.core.Aggregate}
+ * and push it down towards the input.
+ *
+ * <p>What projections can be safely pushed down depends upon which fields the
+ * Aggregate uses.
+ *
+ * <p>To prevent cycles, this rule will not extract a {@code Project} if the
+ * {@code Aggregate}s input is already a {@code Project}.
+ */
+public class AggregateExtractProjectRule extends RelOptRule {
+
+ /** Predicate that prevents matching against an {@code Aggregate} whose input
+ * is already a {@code Project}. This will prevent this rule firing
+ * repeatedly. */
+ private static final Predicate<RelNode> PREDICATE = new Predicate<RelNode>() {
+ @Override public boolean apply(@Nullable RelNode relNode) {
+ return !(relNode instanceof Project);
+ }
+ };
+
+ /**
+ * Creates an AggregateExtractProjectRule.
+ *
+ * @param relBuilderFactory Builder for relational expressions
+ */
+ public AggregateExtractProjectRule(
+ Class<? extends Aggregate> aggregateClass,
+ Class<? extends RelNode> inputClass,
+ RelBuilderFactory relBuilderFactory) {
+ this(operand(aggregateClass, operand(inputClass, null, PREDICATE, any())),
+ relBuilderFactory);
+ }
+
+ public AggregateExtractProjectRule(RelOptRuleOperand operand,
+ RelBuilderFactory builderFactory) {
+ super(operand, builderFactory, null);
+ }
+
+ public void onMatch(RelOptRuleCall call) {
+ final Aggregate aggregate = call.rel(0);
+ final RelNode input = call.rel(1);
+ // Compute which input fields are used.
+ // 1. group fields are always used
+ final ImmutableBitSet.Builder inputFieldsUsed =
+ aggregate.getGroupSet().rebuild();
+ // 2. agg functions
+ for (AggregateCall aggCall : aggregate.getAggCallList()) {
+ for (int i : aggCall.getArgList()) {
+ inputFieldsUsed.set(i);
+ }
+ if (aggCall.filterArg >= 0) {
+ inputFieldsUsed.set(aggCall.filterArg);
+ }
+ }
+ final RelBuilder relBuilder = call.builder().push(input);
+ final List<RexNode> projects = new ArrayList<>();
+ final Mapping mapping =
+ Mappings.create(MappingType.INVERSE_SURJECTION,
+ aggregate.getInput().getRowType().getFieldCount(),
+ inputFieldsUsed.cardinality());
+ int j = 0;
+ for (int i : inputFieldsUsed.build()) {
+ projects.add(relBuilder.field(i));
+ mapping.set(i, j++);
+ }
+
+ relBuilder.project(projects);
+
+ final ImmutableBitSet newGroupSet =
+ Mappings.apply(mapping, aggregate.getGroupSet());
+
+ final ImmutableList<ImmutableBitSet> newGroupSets =
+ ImmutableList.copyOf(
+ Iterables.transform(aggregate.getGroupSets(),
+ new Function<ImmutableBitSet, ImmutableBitSet>() {
+ public ImmutableBitSet apply(ImmutableBitSet input) {
+ return Mappings.apply(mapping, input);
+ }
+ }));
+ final List<RelBuilder.AggCall> newAggCallList = new ArrayList<>();
+ for (AggregateCall aggCall : aggregate.getAggCallList()) {
+ final ImmutableList<RexNode> args =
+ relBuilder.fields(
+ Mappings.apply2(mapping, aggCall.getArgList()));
+ 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));
+ }
+
+ final RelBuilder.GroupKey groupKey =
+ relBuilder.groupKey(newGroupSet, newGroupSets);
+ relBuilder.aggregate(groupKey, newAggCallList);
+ call.transformTo(relBuilder.build());
+ }
+}
+
+// End AggregateExtractProjectRule.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/d3b35a4b/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 057954b..d76babe 100644
--- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
@@ -29,19 +29,23 @@ import org.apache.calcite.prepare.Prepare;
import org.apache.calcite.rel.RelCollationTraitDef;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.RelRoot;
+import org.apache.calcite.rel.core.Aggregate;
import org.apache.calcite.rel.core.Intersect;
import org.apache.calcite.rel.core.Join;
import org.apache.calcite.rel.core.JoinRelType;
import org.apache.calcite.rel.core.Minus;
+import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.core.RelFactories;
import org.apache.calcite.rel.core.Union;
import org.apache.calcite.rel.logical.LogicalProject;
import org.apache.calcite.rel.logical.LogicalTableModify;
+import org.apache.calcite.rel.logical.LogicalTableScan;
import org.apache.calcite.rel.metadata.CachingRelMetadataProvider;
import org.apache.calcite.rel.metadata.ChainedRelMetadataProvider;
import org.apache.calcite.rel.metadata.DefaultRelMetadataProvider;
import org.apache.calcite.rel.metadata.RelMetadataProvider;
import org.apache.calcite.rel.rules.AggregateExpandDistinctAggregatesRule;
+import org.apache.calcite.rel.rules.AggregateExtractProjectRule;
import org.apache.calcite.rel.rules.AggregateFilterTransposeRule;
import org.apache.calcite.rel.rules.AggregateJoinTransposeRule;
import org.apache.calcite.rel.rules.AggregateProjectMergeRule;
@@ -96,12 +100,16 @@ import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.runtime.Hook;
+import org.apache.calcite.runtime.PredicateImpl;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql2rel.SqlToRelConverter;
import org.apache.calcite.tools.RelBuilder;
+import static org.apache.calcite.plan.RelOptRule.none;
+import static org.apache.calcite.plan.RelOptRule.operand;
+
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
@@ -112,6 +120,8 @@ import org.junit.Test;
import java.util.Arrays;
import java.util.List;
+import javax.annotation.Nullable;
+
import static org.junit.Assert.assertTrue;
/**
@@ -2492,7 +2502,7 @@ public class RelOptRulesTest extends RelOptTestBase {
.check();
}
- @Test public void testAggregateProjectMerge() throws Exception {
+ @Test public void testAggregateProjectMerge() {
HepProgram program = new HepProgramBuilder()
.addRuleInstance(AggregateProjectMergeRule.INSTANCE)
.build();
@@ -2503,7 +2513,7 @@ public class RelOptRulesTest extends RelOptTestBase {
+ "group by x, y");
}
- @Test public void testAggregateGroupingSetsProjectMerge() throws Exception {
+ @Test public void testAggregateGroupingSetsProjectMerge() {
HepProgram program = new HepProgramBuilder()
.addRuleInstance(AggregateProjectMergeRule.INSTANCE)
.build();
@@ -2514,6 +2524,71 @@ public class RelOptRulesTest extends RelOptTestBase {
+ "group by rollup(x, y)");
}
+ @Test public void testAggregateExtractProjectRule() {
+ final String sql = "select sum(sal)\n"
+ + "from emp";
+ HepProgram pre = new HepProgramBuilder()
+ .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+ .build();
+ final AggregateExtractProjectRule rule =
+ new AggregateExtractProjectRule(Aggregate.class, LogicalTableScan.class,
+ RelFactories.LOGICAL_BUILDER);
+ sql(sql).withPre(pre).withRule(rule).check();
+ }
+
+ @Test public void testAggregateExtractProjectRuleWithGroupingSets() {
+ final String sql = "select empno, deptno, sum(sal)\n"
+ + "from emp\n"
+ + "group by grouping sets ((empno, deptno),(deptno),(empno))";
+ HepProgram pre = new HepProgramBuilder()
+ .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+ .build();
+ final AggregateExtractProjectRule rule =
+ new AggregateExtractProjectRule(Aggregate.class, LogicalTableScan.class,
+ RelFactories.LOGICAL_BUILDER);
+ sql(sql).withPre(pre).withRule(rule).check();
+ }
+
+
+ /** Test with column used in both grouping set and argument to aggregate
+ * function. */
+ @Test public void testAggregateExtractProjectRuleWithGroupingSets2() {
+ final String sql = "select empno, deptno, sum(empno)\n"
+ + "from emp\n"
+ + "group by grouping sets ((empno, deptno),(deptno),(empno))";
+ HepProgram pre = new HepProgramBuilder()
+ .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+ .build();
+ final AggregateExtractProjectRule rule =
+ new AggregateExtractProjectRule(Aggregate.class, LogicalTableScan.class,
+ RelFactories.LOGICAL_BUILDER);
+ sql(sql).withPre(pre).withRule(rule).check();
+ }
+
+ @Test public void testAggregateExtractProjectRuleWithFilter() {
+ final String sql = "select sum(sal) filter (where empno = 40)\n"
+ + "from emp";
+ HepProgram pre = new HepProgramBuilder()
+ .addRuleInstance(AggregateProjectMergeRule.INSTANCE)
+ .build();
+ // AggregateProjectMergeRule does not merges Project with Filter.
+ // Force match Aggregate on top of Project once explicitly in unit test.
+ final AggregateExtractProjectRule rule =
+ new AggregateExtractProjectRule(
+ operand(Aggregate.class,
+ operand(Project.class, null,
+ new PredicateImpl<Project>() {
+ int matchCount = 0;
+
+ public boolean test(@Nullable Project project) {
+ return matchCount++ == 0;
+ }
+ },
+ none())),
+ RelFactories.LOGICAL_BUILDER);
+ sql(sql).withPre(pre).withRule(rule).checkUnchanged();
+ }
+
@Test public void testPullAggregateThroughUnion() {
HepProgram program = new HepProgramBuilder()
.addRuleInstance(AggregateUnionAggregateRule.INSTANCE)
http://git-wip-us.apache.org/repos/asf/calcite/blob/d3b35a4b/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 bde75fe..17041ef 100644
--- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml
@@ -16,6 +16,78 @@ See the License for the specific language governing permissions and
limitations under the License.
-->
<Root>
+ <TestCase name="testAggregateExtractProjectRule">
+ <Resource name="sql">
+ <![CDATA[select sum(sal)
+from emp]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalAggregate(group=[{}], EXPR$0=[SUM($5)])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalAggregate(group=[{}], EXPR$0=[SUM($0)])
+ LogicalProject(SAL=[$5])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testAggregateExtractProjectRuleWithFilter">
+ <Resource name="sql">
+ <![CDATA[select sum(sal) filter (where empno = 40)
+from emp]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalAggregate(group=[{}], EXPR$0=[SUM($0) FILTER $1])
+ LogicalProject(SAL=[$5], $f1=[=($0, 40)])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testAggregateExtractProjectRuleWithGroupingSets">
+ <Resource name="sql">
+ <![CDATA[select empno, deptno, sum(sal)
+from emp
+group by grouping sets ((empno, deptno),(deptno),(empno))]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalAggregate(group=[{0, 7}], groups=[[{0, 7}, {0}, {7}]], EXPR$2=[SUM($5)])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalAggregate(group=[{0, 2}], groups=[[{0, 2}, {0}, {2}]], EXPR$2=[SUM($1)])
+ LogicalProject(EMPNO=[$0], SAL=[$5], DEPTNO=[$7])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testAggregateExtractProjectRuleWithGroupingSets2">
+ <Resource name="sql">
+ <![CDATA[select empno, deptno, sum(empno)
+from emp
+group by grouping sets ((empno, deptno),(deptno),(empno))]]>
+ </Resource>
+ <Resource name="planBefore">
+ <![CDATA[
+LogicalAggregate(group=[{0, 7}], groups=[[{0, 7}, {0}, {7}]], EXPR$2=[SUM($0)])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ <Resource name="planAfter">
+ <![CDATA[
+LogicalAggregate(group=[{0, 1}], groups=[[{0, 1}, {0}, {1}]], EXPR$2=[SUM($0)])
+ LogicalProject(EMPNO=[$0], DEPTNO=[$7])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
<TestCase name="testReduceNot">
<Resource name="sql">
<![CDATA[select *
@@ -5778,10 +5850,9 @@ LogicalProject(JOB=[$0], EXPR$1=[$2])
</TestCase>
<TestCase name="testPushAggregateSumWithoutGroupKeyThroughJoin">
<Resource name="sql">
- <![CDATA[select e.job,sum(sal)
+ <![CDATA[select sum(sal)
from (select * from sales.emp where empno = 10) as e
-join sales.dept as d on e.job = d.name
-group by e.job,d.name]]>
+join sales.dept as d on e.job = d.name]]>
</Resource>
<Resource name="planBefore">
<![CDATA[
@@ -5809,10 +5880,9 @@ LogicalAggregate(group=[{}], EXPR$0=[SUM($4)])
</TestCase>
<TestCase name="testPushAggregateSumThroughJoinAfterAggregateReduce">
<Resource name="sql">
- <![CDATA[select e.job,sum(sal)
+ <![CDATA[select sum(sal)
from (select * from sales.emp where empno = 10) as e
-join sales.dept as d on e.job = d.name
-group by e.job,d.name]]>
+join sales.dept as d on e.job = d.name]]>
</Resource>
<Resource name="planBefore">
<![CDATA[
http://git-wip-us.apache.org/repos/asf/calcite/blob/d3b35a4b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
----------------------------------------------------------------------
diff --git a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
index f2e931d..2743820 100644
--- a/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
+++ b/druid/src/main/java/org/apache/calcite/adapter/druid/DruidRules.java
@@ -31,6 +31,7 @@ import org.apache.calcite.rel.core.Project;
import org.apache.calcite.rel.core.RelFactories;
import org.apache.calcite.rel.core.Sort;
import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.rules.AggregateExtractProjectRule;
import org.apache.calcite.rel.rules.AggregateFilterTransposeRule;
import org.apache.calcite.rel.rules.FilterAggregateTransposeRule;
import org.apache.calcite.rel.rules.FilterProjectTransposeRule;
@@ -110,12 +111,15 @@ public class DruidRules {
new DruidFilterAggregateTransposeRule(RelFactories.LOGICAL_BUILDER);
public static final DruidPostAggregationProjectRule POST_AGGREGATION_PROJECT =
new DruidPostAggregationProjectRule(RelFactories.LOGICAL_BUILDER);
+ public static final DruidAggregateExtractProjectRule PROJECT_EXTRACT_RULE =
+ new DruidAggregateExtractProjectRule(RelFactories.LOGICAL_BUILDER);
public static final List<RelOptRule> RULES =
ImmutableList.of(FILTER,
PROJECT_FILTER_TRANSPOSE,
AGGREGATE_FILTER_TRANSPOSE,
AGGREGATE_PROJECT,
+ PROJECT_EXTRACT_RULE,
PROJECT,
POST_AGGREGATION_PROJECT,
AGGREGATE,
@@ -1270,6 +1274,30 @@ public class DruidRules {
relBuilderFactory);
}
}
+
+ /**
+ * Rule to extract a {@link org.apache.calcite.rel.core.Project} from
+ * {@link org.apache.calcite.rel.core.Aggregate} on top of
+ * {@link org.apache.calcite.adapter.druid.DruidQuery} based on the fields
+ * used in the aggregate.
+ */
+ public static class DruidAggregateExtractProjectRule
+ extends AggregateExtractProjectRule {
+
+ /**
+ * Creates a DruidAggregateExtractProjectRule.
+ *
+ * @param relBuilderFactory Builder for relational expressions
+ */
+ public DruidAggregateExtractProjectRule(
+ RelBuilderFactory relBuilderFactory) {
+ super(
+ operand(Aggregate.class,
+ operand(DruidQuery.class, none())),
+ relBuilderFactory);
+ }
+ }
+
}
// End DruidRules.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/d3b35a4b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
----------------------------------------------------------------------
diff --git a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
index fff466f..6108813 100644
--- a/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
+++ b/druid/src/test/java/org/apache/calcite/test/DruidAdapterIT.java
@@ -3089,8 +3089,8 @@ public class DruidAdapterIT {
+ "group by \"B\"";
String expectedSubExplain = "PLAN=EnumerableInterpreter\n"
+ " DruidQuery(table=[[foodmart, foodmart]], "
- + "intervals=[[1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z]], projects=[[$63, "
- + "$89]], groups=[{0}], aggs=[[COUNT($1)]]";
+ + "intervals=[[1900-01-09T00:00:00.000Z/2992-01-10T00:00:00.000Z]], projects=[[$89, "
+ + "$63]], groups=[{1}], aggs=[[COUNT($0)]]";
testCountWithApproxDistinct(true, sql, expectedSubExplain);
testCountWithApproxDistinct(false, sql, expectedSubExplain);