This is an automated email from the ASF dual-hosted git repository.
morrySnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 8c01e2ac90c [improvement](eager-agg) Push down distinct aggregation
using statistics (#65099)
8c01e2ac90c is described below
commit 8c01e2ac90c8a8e557f5ad10911cc2b870074cda
Author: feiniaofeiafei <[email protected]>
AuthorDate: Wed Aug 26 17:26:46 2026 +0800
[improvement](eager-agg) Push down distinct aggregation using statistics
(#65099)
### What problem does this PR solve?
Problem Summary: The distinct aggregation pushdown used a separate
cost-based rewrite and supported only one distinct aggregate. Move pure
distinct aggregation into eager aggregation so statistics decide whether
to deduplicate below joins. Support multiple SUM/COUNT distinct
aggregates with identical keys and multi-column COUNT DISTINCT while
keeping the upper distinct aggregate unchanged. Queries containing
non-distinct aggregates or different distinct keys are not rewritten.
### Release note
Use statistics-driven eager aggregation for pure same-key distinct
aggregates across joins.
---
.../apache/doris/nereids/hint/UseCboRuleHint.java | 47 ----
.../jobs/executor/AbstractBatchJobExecutor.java | 19 +-
.../doris/nereids/jobs/executor/Rewriter.java | 18 --
.../nereids/jobs/rewrite/CostBasedRewriteJob.java | 210 -----------------
.../doris/nereids/parser/LogicalPlanBuilder.java | 21 --
.../nereids/properties/SelectHintUseCboRule.java | 53 -----
.../org/apache/doris/nereids/rules/RuleType.java | 4 -
.../rules/analysis/EliminateLogicalSelectHint.java | 13 --
.../rules/rewrite/PushDownAggThroughJoin.java | 242 -------------------
.../PushDownAggWithDistinctThroughJoinOneSide.java | 176 --------------
.../rewrite/eageraggregation/EagerAggRewriter.java | 2 +-
.../eageraggregation/PushDownAggregation.java | 133 +++++++----
.../analysis/EliminateLogicalSelectHintTest.java | 56 -----
.../rewrite/PushDownCountThroughJoinTest.java | 216 -----------------
.../rules/rewrite/PushDownSumThroughJoinTest.java | 134 -----------
.../eageraggregation/EagerAggRewriterTest.java | 85 ++++++-
.../eager_agg/distinct_agg_func_push_down.out | 237 +++++++++++++++++++
.../eager_agg/distinct_agg_func_push_down.groovy | 256 +++++++++++++++++++++
18 files changed, 661 insertions(+), 1261 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/hint/UseCboRuleHint.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/hint/UseCboRuleHint.java
deleted file mode 100644
index 34e18ab8568..00000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/hint/UseCboRuleHint.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// 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.doris.nereids.hint;
-
-/**
- * rule hint.
- */
-public class UseCboRuleHint extends Hint {
-
- private final boolean isNotUseCboRule;
-
- public UseCboRuleHint(String hintName, boolean isNotUseCboRule) {
- super(hintName);
- this.isNotUseCboRule = isNotUseCboRule;
- }
-
- public boolean isNotUseCboRule() {
- return isNotUseCboRule;
- }
-
- @Override
- public String getExplainString() {
- StringBuilder out = new StringBuilder();
- if (isNotUseCboRule) {
- out.append("no_use_");
- } else {
- out.append("use_");
- }
- out.append(getHintName());
- return out.toString();
- }
-}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/AbstractBatchJobExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/AbstractBatchJobExecutor.java
index f9c82df2416..8cd266e3b73 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/AbstractBatchJobExecutor.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/AbstractBatchJobExecutor.java
@@ -22,7 +22,6 @@ import org.apache.doris.nereids.jobs.JobContext;
import org.apache.doris.nereids.jobs.rewrite.AdaptiveBottomUpRewriteJob;
import org.apache.doris.nereids.jobs.rewrite.AdaptiveTopDownRewriteJob;
import org.apache.doris.nereids.jobs.rewrite.BottomUpVisitorRewriteJob;
-import org.apache.doris.nereids.jobs.rewrite.CostBasedRewriteJob;
import org.apache.doris.nereids.jobs.rewrite.CustomRewriteJob;
import org.apache.doris.nereids.jobs.rewrite.PlanTreeRewriteBottomUpJob;
import org.apache.doris.nereids.jobs.rewrite.PlanTreeRewriteTopDownJob;
@@ -98,10 +97,6 @@ public abstract class AbstractBatchJobExecutor {
return new TopicRewriteJob(topicName, Arrays.asList(jobs), condition);
}
- public static RewriteJob costBased(RewriteJob... jobs) {
- return new CostBasedRewriteJob(Arrays.asList(jobs));
- }
-
public static RewriteJob bottomUp(RuleFactory... ruleFactories) {
return bottomUp(Arrays.asList(ruleFactories));
}
@@ -161,21 +156,15 @@ public abstract class AbstractBatchJobExecutor {
continue;
}
- if (shouldRun(currentJob, jobContext, jobs, i)) {
- do {
- jobContext.setRewritten(false);
- currentJob.execute(jobContext);
- } while (!currentJob.isOnce() && jobContext.isRewritten());
- }
+ do {
+ jobContext.setRewritten(false);
+ currentJob.execute(jobContext);
+ } while (!currentJob.isOnce() && jobContext.isRewritten());
}
}
public abstract List<RewriteJob> getJobs();
- protected boolean shouldRun(RewriteJob rewriteJob, JobContext jobContext,
List<RewriteJob> jobs, int jobIndex) {
- return true;
- }
-
private static Predicate<Plan> getTraversePredicate() {
Set<Class<Plan>> notTraverseChildren = NOT_TRAVERSE_CHILDREN.get();
return notTraverseChildren == null
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
index 162289c3355..7a3d6495204 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
@@ -18,8 +18,6 @@
package org.apache.doris.nereids.jobs.executor;
import org.apache.doris.nereids.CascadesContext;
-import org.apache.doris.nereids.jobs.JobContext;
-import org.apache.doris.nereids.jobs.rewrite.CostBasedRewriteJob;
import org.apache.doris.nereids.jobs.rewrite.RewriteJob;
import org.apache.doris.nereids.rules.RuleSet;
import org.apache.doris.nereids.rules.RuleType;
@@ -131,7 +129,6 @@ import
org.apache.doris.nereids.rules.rewrite.PullUpProjectUnderLimit;
import org.apache.doris.nereids.rules.rewrite.PullUpProjectUnderTopN;
import org.apache.doris.nereids.rules.rewrite.PushCountIntoUnionAll;
import org.apache.doris.nereids.rules.rewrite.PushDownAggThroughJoinOnPkFk;
-import
org.apache.doris.nereids.rules.rewrite.PushDownAggWithDistinctThroughJoinOneSide;
import org.apache.doris.nereids.rules.rewrite.PushDownEncodeSlot;
import org.apache.doris.nereids.rules.rewrite.PushDownFilterIntoSchemaScan;
import org.apache.doris.nereids.rules.rewrite.PushDownFilterThroughProject;
@@ -686,7 +683,6 @@ public class Rewriter extends AbstractBatchJobExecutor {
cascadesContext ->
cascadesContext.rewritePlanContainsTypes(
LogicalAggregate.class, LogicalJoin.class
),
- costBased(topDown(new
PushDownAggWithDistinctThroughJoinOneSide())),
custom(RuleType.PUSH_DOWN_AGG_THROUGH_JOIN,
PushDownAggregation::new),
topDown(new PushCountIntoUnionAll())
),
@@ -977,20 +973,6 @@ public class Rewriter extends AbstractBatchJobExecutor {
return rewriteJobs;
}
- @Override
- protected boolean shouldRun(RewriteJob rewriteJob, JobContext jobContext,
List<RewriteJob> jobs, int jobIndex) {
- if (rewriteJob instanceof CostBasedRewriteJob) {
- if (runCboRules) {
- jobContext.setRemainJobs(jobs.subList(jobIndex + 1,
jobs.size()));
- return true;
- } else {
- return false;
- }
- } else {
- return true;
- }
- }
-
@Override
public void execute() {
MoreFieldsThread.keepFunctionSignature(() -> {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/CostBasedRewriteJob.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/CostBasedRewriteJob.java
deleted file mode 100644
index ac0df804da2..00000000000
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/CostBasedRewriteJob.java
+++ /dev/null
@@ -1,210 +0,0 @@
-// 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.doris.nereids.jobs.rewrite;
-
-import org.apache.doris.common.Pair;
-import org.apache.doris.nereids.CascadesContext;
-import org.apache.doris.nereids.StatementContext;
-import org.apache.doris.nereids.cost.Cost;
-import org.apache.doris.nereids.hint.Hint;
-import org.apache.doris.nereids.hint.UseCboRuleHint;
-import org.apache.doris.nereids.jobs.JobContext;
-import org.apache.doris.nereids.jobs.executor.Optimizer;
-import org.apache.doris.nereids.jobs.executor.Rewriter;
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.rules.RuleType;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalCTEAnchor;
-import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
-import org.apache.doris.qe.ConnectContext;
-
-import com.google.common.collect.ImmutableList;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Cost based rewrite job.
- * This job do
- */
-public class CostBasedRewriteJob implements RewriteJob {
-
- private static final Logger LOG =
LogManager.getLogger(CostBasedRewriteJob.class);
-
- private final List<RewriteJob> rewriteJobs;
-
- public CostBasedRewriteJob(List<RewriteJob> rewriteJobs) {
- this.rewriteJobs = rewriteJobs;
- // need to generate real rewrite job list
- }
-
- @Override
- public void execute(JobContext jobContext) {
- // checkHint.first means whether it use hint and checkHint.second
means what
- // kind of hint it used
- Pair<Boolean, Hint> checkHint = checkRuleHint();
- // this means it no_use_cbo_rule(xxx) hint
- if (checkHint.first && checkHint.second == null) {
- return;
- }
- CascadesContext currentCtx = jobContext.getCascadesContext();
- CascadesContext skipCboRuleCtx =
CascadesContext.newCurrentTreeContext(currentCtx);
- CascadesContext applyCboRuleCtx =
CascadesContext.newCurrentTreeContext(currentCtx);
- // execute cbo rule on one candidate
- Rewriter.getCteChildrenRewriter(applyCboRuleCtx,
rewriteJobs).execute();
- Plan applyCboPlan = applyCboRuleCtx.getRewritePlan();
- if
(skipCboRuleCtx.getRewritePlan().deepEquals(applyCboRuleCtx.getRewritePlan())) {
- // this means rewrite do not do anything
- return;
- }
-
- StatementContext.CteEnvironmentSnapshot cteEnvSnapshot =
currentCtx.getStatementContext().cacheCteEnvironment();
- // compare two candidates
- Optional<Pair<Cost, GroupExpression>> skipCboRuleCost =
getCost(currentCtx, skipCboRuleCtx, jobContext);
- currentCtx.getStatementContext().restoreCteEnvironment(cteEnvSnapshot);
- Optional<Pair<Cost, GroupExpression>> appliedCboRuleCost =
getCost(currentCtx, applyCboRuleCtx, jobContext);
- currentCtx.getStatementContext().restoreCteEnvironment(cteEnvSnapshot);
- // If one of them optimize failed, just return
- if (!skipCboRuleCost.isPresent() || !appliedCboRuleCost.isPresent()) {
- LOG.warn("Cbo rewrite execute failed on sql: {}, jobs are {}, plan
is {}.",
-
currentCtx.getStatementContext().getOriginStatement().originStmt,
- rewriteJobs, currentCtx.getRewritePlan());
- return;
- }
- if (checkHint.first) {
- checkHint.second.setStatus(Hint.HintStatus.SUCCESS);
- if (!((UseCboRuleHint) checkHint.second).isNotUseCboRule()) {
-
currentCtx.addPlanProcesses(applyCboRuleCtx.getPlanProcesses());
- currentCtx.setRewritePlan(applyCboRuleCtx.getRewritePlan());
- }
- return;
- }
- // If the candidate applied cbo rule is better, replace the original
plan with
- // it.
- if (appliedCboRuleCost.get().first.getValue() <
skipCboRuleCost.get().first.getValue()) {
- currentCtx.setRewritePlan(applyCboPlan);
- }
- }
-
- /**
- * check if we have use rule hint or no use rule hint
- * return an optional object which checkHint.first means whether it use
hint
- * and checkHint.second means what kind of hint it used
- * example, when we use *+ no_use_cbo_rule(xxx) * the optional would be
(true,
- * false)
- * which means it use hint and the hint forbid this kind of rule
- */
- private Pair<Boolean, Hint> checkRuleHint() {
- Pair<Boolean, Hint> checkResult = Pair.of(false, null);
- RewriteJob rewriteJob = rewriteJobs.get(0);
- List<Rule> rules = ImmutableList.of();
- if (rewriteJob instanceof AdaptiveTopDownRewriteJob) {
- rules = ((AdaptiveTopDownRewriteJob) rewriteJob).getRules();
- } else if (rewriteJob instanceof AdaptiveBottomUpRewriteJob) {
- rules = ((AdaptiveBottomUpRewriteJob) rewriteJob).getRules();
- } else if (rewriteJob instanceof RootPlanTreeRewriteJob) {
- rules = ((RootPlanTreeRewriteJob) rewriteJob).getRules();
- } else if (rewriteJob instanceof TopDownVisitorRewriteJob) {
- rules = ((TopDownVisitorRewriteJob)
rewriteJob).getRules().getAllRules();
- } else if (rewriteJob instanceof BottomUpVisitorRewriteJob) {
- rules = ((BottomUpVisitorRewriteJob)
rewriteJob).getRules().getAllRules();
- }
- for (Rule rule : rules) {
- checkResult = checkRuleHintWithHintName(rule.getRuleType());
- if (checkResult.first) {
- return checkResult;
- }
- }
- if (rewriteJob instanceof CustomRewriteJob) {
- checkResult = checkRuleHintWithHintName(((CustomRewriteJob)
rewriteJob).getRuleType());
- }
- return checkResult;
- }
-
- /**
- * for these rules we need use_cbo_rule hint to enable it, otherwise it
would be
- * close by default
- */
- private static boolean checkBlackList(RuleType ruleType) {
- List<RuleType> ruleWhiteList = new ArrayList<>(Arrays.asList(
- RuleType.PUSH_DOWN_AGG_THROUGH_JOIN,
- RuleType.PUSH_DOWN_AGG_THROUGH_JOIN_ONE_SIDE,
- RuleType.PUSH_DOWN_DISTINCT_THROUGH_JOIN,
- RuleType.PUSH_DOWN_AGG_WITH_DISTINCT_THROUGH_JOIN_ONE_SIDE));
- if (!ruleWhiteList.isEmpty() && ruleWhiteList.contains(ruleType)) {
- return true;
- }
- return false;
- }
-
- /**
- * main mechanism of checkRuleHint
- * return an optional object which checkHint.first means whether it use
hint
- * and checkHint.second means what kind of hint it used
- */
- private Pair<Boolean, Hint> checkRuleHintWithHintName(RuleType ruleType) {
- for (Hint hint :
ConnectContext.get().getStatementContext().getHints()) {
- if (hint.getHintName().equalsIgnoreCase(ruleType.name())) {
- return Pair.of(true, hint);
- }
- }
- if (checkBlackList(ruleType)) {
- return Pair.of(true, null);
- }
- return Pair.of(false, null);
- }
-
- @Override
- public boolean isOnce() {
- // TODO: currently, we do not support execute it more than once.
- return true;
- }
-
- private Optional<Pair<Cost, GroupExpression>> getCost(CascadesContext
currentCtx,
- CascadesContext cboCtx, JobContext jobContext) {
- // Do subtree rewriter
- Rewriter.getCteChildrenRewriter(cboCtx,
jobContext.getRemainJobs()).execute();
- CascadesContext rootCtx = currentCtx.getRoot();
- if (rootCtx.getRewritePlan() instanceof LogicalCTEAnchor) {
- // set subtree rewrite cache
- currentCtx.getStatementContext().getRewrittenCteProducer()
- .put(currentCtx.getCurrentTree().orElse(null),
(LogicalPlan) cboCtx.getRewritePlan());
- // Do post tree rewrite
- CascadesContext rootCtxCopy =
CascadesContext.newCurrentTreeContext(rootCtx);
- rootCtxCopy.withPlanProcess(currentCtx.showPlanProcess(), () -> {
-
Rewriter.getWholeTreeRewriterWithoutCostBasedJobs(rootCtxCopy).execute();
- });
- // Do optimize
- new Optimizer(rootCtxCopy).execute();
- return rootCtxCopy.getMemo().getRoot().getLowestCostPlan(
-
rootCtxCopy.getCurrentJobContext().getRequiredProperties());
- } else {
- // Do post tree rewrite
- CascadesContext cboCtxCopy =
CascadesContext.newCurrentTreeContext(cboCtx);
- // Do optimize
- new Optimizer(cboCtxCopy).execute();
- return cboCtxCopy.getMemo().getRoot().getLowestCostPlan(
- cboCtxCopy.getCurrentJobContext().getRequiredProperties());
- }
- }
-}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 69d2a45d4f5..1fc86341310 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -522,7 +522,6 @@ import org.apache.doris.nereids.properties.SelectHint;
import org.apache.doris.nereids.properties.SelectHintLeading;
import org.apache.doris.nereids.properties.SelectHintOrdered;
import org.apache.doris.nereids.properties.SelectHintSetVar;
-import org.apache.doris.nereids.properties.SelectHintUseCboRule;
import org.apache.doris.nereids.properties.SelectHintUseMv;
import org.apache.doris.nereids.trees.TableSample;
import org.apache.doris.nereids.trees.expressions.Add;
@@ -4858,26 +4857,6 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
case "ordered":
hints.add(new SelectHintOrdered(hintName));
break;
- case "use_cbo_rule":
- List<String> useRuleParameters = new ArrayList<>();
- for (HintAssignmentContext kv :
hintStatement.parameters) {
- if (kv.key != null) {
- String parameterName =
visitIdentifierOrText(kv.key);
- useRuleParameters.add(parameterName);
- }
- }
- hints.add(new SelectHintUseCboRule(hintName,
useRuleParameters, false));
- break;
- case "no_use_cbo_rule":
- List<String> noUseRuleParameters = new
ArrayList<>();
- for (HintAssignmentContext kv :
hintStatement.parameters) {
- String parameterName =
visitIdentifierOrText(kv.key);
- if (kv.key != null) {
- noUseRuleParameters.add(parameterName);
- }
- }
- hints.add(new SelectHintUseCboRule(hintName,
noUseRuleParameters, true));
- break;
default:
break;
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/SelectHintUseCboRule.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/SelectHintUseCboRule.java
deleted file mode 100644
index 47c1ef9dcd7..00000000000
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/SelectHintUseCboRule.java
+++ /dev/null
@@ -1,53 +0,0 @@
-// 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.doris.nereids.properties;
-
-import java.util.List;
-
-/**
- * select hint CostBasedRule.
- */
-public class SelectHintUseCboRule extends SelectHint {
- private final List<String> parameters;
-
- private final boolean isNotUseCboRule;
-
- public SelectHintUseCboRule(String hintName, List<String> parameters,
boolean isNotUseCboRule) {
- super(hintName);
- this.parameters = parameters;
- this.isNotUseCboRule = isNotUseCboRule;
- }
-
- public List<String> getParameters() {
- return parameters;
- }
-
- public boolean isNotUseCboRule() {
- return isNotUseCboRule;
- }
-
- @Override
- public String getHintName() {
- return super.getHintName();
- }
-
- @Override
- public String toString() {
- return super.getHintName();
- }
-}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
index 4a57e891695..a981dc49681 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
@@ -227,8 +227,6 @@ public enum RuleType {
PUSH_DOWN_FILTER_THROUGH_CTE_ANCHOR(RuleTypeClass.REWRITE),
PUSH_DOWN_MAX_MIN_FILTER(RuleTypeClass.REWRITE),
- PUSH_DOWN_DISTINCT_THROUGH_JOIN(RuleTypeClass.REWRITE),
-
ADD_PROJECT_FOR_JOIN(RuleTypeClass.REWRITE),
ADD_PROJECT_FOR_VOLATILE_EXPRESSION(RuleTypeClass.REWRITE),
VARIANT_SUB_PATH_PRUNING(RuleTypeClass.REWRITE),
@@ -238,8 +236,6 @@ public enum RuleType {
COLUMN_PRUNING(RuleTypeClass.REWRITE),
ELIMINATE_SORT(RuleTypeClass.REWRITE),
- PUSH_DOWN_AGG_THROUGH_JOIN_ONE_SIDE(RuleTypeClass.REWRITE),
- PUSH_DOWN_AGG_WITH_DISTINCT_THROUGH_JOIN_ONE_SIDE(RuleTypeClass.REWRITE),
PUSH_DOWN_AGG_THROUGH_JOIN(RuleTypeClass.REWRITE),
PUSH_DOWN_AGG_THROUGH_JOIN_ON_PKFK(RuleTypeClass.REWRITE),
TRANSPOSE_LOGICAL_SEMI_JOIN_LOGICAL_JOIN(RuleTypeClass.REWRITE),
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHint.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHint.java
index 49db25ca097..b732aecc423 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHint.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHint.java
@@ -22,12 +22,10 @@ import org.apache.doris.nereids.StatementContext;
import org.apache.doris.nereids.hint.Hint;
import org.apache.doris.nereids.hint.LeadingHint;
import org.apache.doris.nereids.hint.OrderedHint;
-import org.apache.doris.nereids.hint.UseCboRuleHint;
import org.apache.doris.nereids.hint.UseMvHint;
import org.apache.doris.nereids.properties.SelectHint;
import org.apache.doris.nereids.properties.SelectHintLeading;
import org.apache.doris.nereids.properties.SelectHintSetVar;
-import org.apache.doris.nereids.properties.SelectHintUseCboRule;
import org.apache.doris.nereids.properties.SelectHintUseMv;
import org.apache.doris.nereids.rules.Rule;
import org.apache.doris.nereids.rules.RuleType;
@@ -66,9 +64,6 @@ public class EliminateLogicalSelectHint extends
OneRewriteRuleFactory {
} else if (hintName.equalsIgnoreCase("LEADING")) {
extractLeading((SelectHintLeading) hint,
ctx.cascadesContext,
ctx.statementContext, selectHintPlan);
- } else if (hintName.equalsIgnoreCase("USE_CBO_RULE")
- || hintName.equalsIgnoreCase("NO_USE_CBO_RULE")) {
- extractRule((SelectHintUseCboRule) hint,
ctx.statementContext);
} else if (hintName.equalsIgnoreCase("USE_MV")) {
extractMv((SelectHintUseMv) hint,
ConnectContext.get().getStatementContext());
} else if (hintName.equalsIgnoreCase("NO_USE_MV")) {
@@ -116,14 +111,6 @@ public class EliminateLogicalSelectHint extends
OneRewriteRuleFactory {
}
}
- private void extractRule(SelectHintUseCboRule selectHint, StatementContext
statementContext) {
- // rule hint need added to statementContext only cause it's set in all
scopes
- for (String parameter : selectHint.getParameters()) {
- UseCboRuleHint hint = new UseCboRuleHint(parameter,
selectHint.isNotUseCboRule());
- statementContext.addHint(hint);
- }
- }
-
private void extractMv(SelectHintUseMv selectHint, StatementContext
statementContext) {
boolean isAllMv = selectHint.getTables().isEmpty();
UseMvHint useMvHint = new UseMvHint(selectHint.getHintName(),
selectHint.getTables(),
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownAggThroughJoin.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownAggThroughJoin.java
deleted file mode 100644
index d92168c90f0..00000000000
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownAggThroughJoin.java
+++ /dev/null
@@ -1,242 +0,0 @@
-// 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.doris.nereids.rules.rewrite;
-
-import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.rules.RuleType;
-import org.apache.doris.nereids.trees.expressions.Alias;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Multiply;
-import org.apache.doris.nereids.trees.expressions.NamedExpression;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
-import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
-import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
-import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
-import org.apache.doris.nereids.util.TypeCoercionUtils;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * TODO: distinct | just push one level
- * Support Pushdown Count(*)/Count(col).
- * Count(col) -> Sum( cnt * cntStar )
- * Count(*) -> Sum( leftCntStar * rightCntStar )
- * <p>
- * Related paper "Eager aggregation and lazy aggregation".
- * <pre>
- * aggregate: count(x)
- * |
- * join
- * | \
- * | *
- * (x)
- * ->
- * aggregate: Sum( cnt * cntStar )
- * |
- * join
- * | \
- * | aggregate: count(*) as cntStar
- * aggregate: count(x) as cnt
- * </pre>
- * Notice: rule can't optimize condition that groupby is empty when Count(*)
exists.
- */
-public class PushDownAggThroughJoin implements RewriteRuleFactory {
- @Override
- public List<Rule> buildRules() {
- return ImmutableList.of(
- logicalAggregate(
- innerLogicalJoin()
- .when(join -> join.getOtherJoinConjuncts().isEmpty())
- .whenNot(join -> join.children().stream().anyMatch(p
-> p instanceof LogicalAggregate))
- )
- .when(agg ->
agg.getGroupByExpressions().stream().allMatch(e -> e instanceof Slot))
- .when(agg -> {
- Set<AggregateFunction> funcs =
agg.getAggregateFunctions();
- return !funcs.isEmpty() && funcs.stream()
- .allMatch(f -> !f.isDistinct()
- && (f instanceof Count &&
(((Count) f).isCountStar() || f.child(
- 0) instanceof Slot)
- || (f instanceof Sum && f.child(0)
instanceof Slot))
- );
- })
- .thenApply(ctx -> {
- LogicalAggregate<LogicalJoin<Plan, Plan>> agg =
ctx.root;
- return pushAgg(agg, agg.child(),
ImmutableList.of());
- })
- .toRule(RuleType.PUSH_DOWN_AGG_THROUGH_JOIN),
- logicalAggregate(
- logicalProject(
- innerLogicalJoin()
- .when(join ->
join.getOtherJoinConjuncts().isEmpty())
- .whenNot(join ->
join.children().stream()
- .anyMatch(p -> p instanceof
LogicalAggregate))
- ).when(LogicalProject::isAllSlots)
- )
- .when(agg -> agg.getGroupByExpressions().stream()
- .allMatch(e -> e instanceof Slot))
- .when(agg -> {
- Set<AggregateFunction> funcs =
agg.getAggregateFunctions();
- return !funcs.isEmpty() && funcs.stream()
- .allMatch(f -> !f.isDistinct()
- && (f instanceof Count &&
(((Count) f).isCountStar() || f.child(
- 0) instanceof Slot)
- || (f instanceof Sum && f.child(0)
instanceof Slot))
- );
- })
- .thenApply(ctx -> {
- LogicalAggregate<LogicalProject<LogicalJoin<Plan,
Plan>>> agg = ctx.root;
- return pushAgg(agg, agg.child().child(),
agg.child().getProjects());
- })
- .toRule(RuleType.PUSH_DOWN_AGG_THROUGH_JOIN)
- );
- }
-
- private static LogicalAggregate<Plan> pushAgg(LogicalAggregate<? extends
Plan> agg,
- LogicalJoin<Plan, Plan> join, List<NamedExpression> projects) {
- List<Slot> leftOutput = join.left().getOutput();
- List<Slot> rightOutput = join.right().getOutput();
-
- List<AggregateFunction> leftAggs = new ArrayList<>();
- List<AggregateFunction> rightAggs = new ArrayList<>();
- List<Count> countStars = new ArrayList<>();
- for (AggregateFunction f : agg.getAggregateFunctions()) {
- if (f instanceof Count && ((Count) f).isCountStar()) {
- countStars.add((Count) f);
- } else {
- Slot slot = (Slot) f.child(0);
- if (leftOutput.contains(slot)) {
- leftAggs.add(f);
- } else if (rightOutput.contains(slot)) {
- rightAggs.add(f);
- } else {
- throw new IllegalStateException("Slot " + slot + " not
found in join output");
- }
- }
- }
-
- Set<Slot> leftGroupBy = new HashSet<>();
- Set<Slot> rightGroupBy = new HashSet<>();
- for (Expression e : agg.getGroupByExpressions()) {
- Slot slot = (Slot) e;
- if (leftOutput.contains(slot)) {
- leftGroupBy.add(slot);
- } else if (rightOutput.contains(slot)) {
- rightGroupBy.add(slot);
- } else {
- return null;
- }
- }
-
- if (!countStars.isEmpty() && leftGroupBy.isEmpty() &&
rightGroupBy.isEmpty()) {
- return null;
- }
-
- join.getHashJoinConjuncts().forEach(e ->
e.getInputSlots().forEach(slot -> {
- if (leftOutput.contains(slot)) {
- leftGroupBy.add(slot);
- } else if (rightOutput.contains(slot)) {
- rightGroupBy.add(slot);
- } else {
- throw new IllegalStateException("Slot " + slot + " not found
in join output");
- }
- }));
-
- Alias leftCnt = null;
- Alias rightCnt = null;
- // left agg
- Map<Slot, NamedExpression> leftSlotToOutput = new HashMap<>();
- Builder<NamedExpression> leftAggOutputBuilder =
ImmutableList.<NamedExpression>builder().addAll(leftGroupBy);
- leftAggs.forEach(func -> {
- Alias alias = func.alias(func.getName());
- leftSlotToOutput.put((Slot) func.child(0), alias);
- leftAggOutputBuilder.add(alias);
- });
- if (!rightAggs.isEmpty() || !countStars.isEmpty()) {
- leftCnt = new Count().alias("leftCntStar");
- leftAggOutputBuilder.add(leftCnt);
- }
- LogicalAggregate<Plan> leftAgg = new LogicalAggregate<>(
- ImmutableList.copyOf(leftGroupBy),
leftAggOutputBuilder.build(), join.left());
- // right agg
- Map<Slot, NamedExpression> rightSlotToOutput = new HashMap<>();
- Builder<NamedExpression> rightAggOutputBuilder =
ImmutableList.<NamedExpression>builder().addAll(rightGroupBy);
- rightAggs.forEach(func -> {
- Alias alias = func.alias(func.getName());
- rightSlotToOutput.put((Slot) func.child(0), alias);
- rightAggOutputBuilder.add(alias);
- });
- if (!leftAggs.isEmpty() || !countStars.isEmpty()) {
- rightCnt = new Count().alias("rightCntStar");
- rightAggOutputBuilder.add(rightCnt);
- }
- LogicalAggregate<Plan> rightAgg = new LogicalAggregate<>(
- ImmutableList.copyOf(rightGroupBy),
rightAggOutputBuilder.build(), join.right());
-
- Plan newJoin = join.withChildren(leftAgg, rightAgg);
-
- // top Sum agg
- // count(slot) -> sum( count(slot) * cntStar )
- // count(*) -> sum( leftCntStar * leftCntStar )
- List<NamedExpression> newOutputExprs = new ArrayList<>();
- for (NamedExpression ne : agg.getOutputExpressions()) {
- if (ne instanceof Alias && ((Alias) ne).child() instanceof
AggregateFunction) {
- AggregateFunction func = (AggregateFunction) ((Alias)
ne).child();
- if (func instanceof Count && ((Count) func).isCountStar()) {
- Preconditions.checkState(rightCnt != null && leftCnt !=
null);
- Expression multiply =
TypeCoercionUtils.processBinaryArithmetic(
- new Multiply(leftCnt.toSlot(), rightCnt.toSlot()));
- Expression expr = new Sum(multiply);
- newOutputExprs.add((NamedExpression)
ne.withChildren(expr));
- } else {
- Slot slot = (Slot) func.child(0);
- if (leftSlotToOutput.containsKey(slot)) {
- Preconditions.checkState(rightCnt != null);
- Expression multiply =
TypeCoercionUtils.processBinaryArithmetic(
- new
Multiply(leftSlotToOutput.get(slot).toSlot(), rightCnt.toSlot()));
- Expression expr = new Sum(multiply);
- newOutputExprs.add((NamedExpression)
ne.withChildren(expr));
- } else if (rightSlotToOutput.containsKey(slot)) {
- Preconditions.checkState(leftCnt != null);
- Expression multiply =
TypeCoercionUtils.processBinaryArithmetic(
- new
Multiply(rightSlotToOutput.get(slot).toSlot(), leftCnt.toSlot()));
- Expression expr = new Sum(multiply);
- newOutputExprs.add((NamedExpression)
ne.withChildren(expr));
- } else {
- throw new IllegalStateException("Slot " + slot + " not
found in join output");
- }
- }
- } else {
- newOutputExprs.add(ne);
- }
- }
- return agg.withAggOutputChild(newOutputExprs, newJoin);
- }
-}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownAggWithDistinctThroughJoinOneSide.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownAggWithDistinctThroughJoinOneSide.java
deleted file mode 100644
index dd09b2b52a3..00000000000
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownAggWithDistinctThroughJoinOneSide.java
+++ /dev/null
@@ -1,176 +0,0 @@
-// 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.doris.nereids.rules.rewrite;
-
-import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.rules.RuleType;
-import org.apache.doris.nereids.trees.expressions.Alias;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.NamedExpression;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
-import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
-import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
-import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
-import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
-import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
-import com.google.common.collect.Sets;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Push down agg function with distinct through join on only one side.
- */
-public class PushDownAggWithDistinctThroughJoinOneSide implements
RewriteRuleFactory {
- @Override
- public List<Rule> buildRules() {
- return ImmutableList.of(
- logicalAggregate(logicalProject(innerLogicalJoin()))
- .when(agg -> agg.child().isAllSlots())
- .when(agg ->
agg.child().child().getOtherJoinConjuncts().isEmpty())
- .when(agg -> !agg.isGenerated())
- .whenNot(agg -> agg.getAggregateFunctions().isEmpty())
- .whenNot(agg -> agg.child()
- .child(0).children().stream().anyMatch(p -> p
instanceof LogicalAggregate))
- .when(agg -> {
- Set<AggregateFunction> funcs =
agg.getAggregateFunctions();
- if (funcs.size() > 1) {
- return false;
- } else {
- return funcs.stream()
- .allMatch(f -> (f instanceof Min || f
instanceof Max || f instanceof Sum
- || f instanceof Count) &&
f.isDistinct() && f.arity() == 1
- && f.child(0) instanceof Slot);
- }
- })
- .thenApply(ctx -> {
- LogicalAggregate<LogicalProject<LogicalJoin<Plan,
Plan>>> agg = ctx.root;
- return pushDownAggWithDistinct(agg,
agg.child().child(), agg.child().getProjects());
- })
-
.toRule(RuleType.PUSH_DOWN_AGG_WITH_DISTINCT_THROUGH_JOIN_ONE_SIDE)
- );
- }
-
- private static LogicalAggregate<Plan>
pushDownAggWithDistinct(LogicalAggregate<? extends Plan> agg,
- LogicalJoin<Plan, Plan> join, List<NamedExpression> projects) {
- Plan leftJoin = join.left();
- Plan rightJoin = join.right();
- List<Slot> leftJoinOutput = leftJoin.getOutput();
- List<Slot> rightJoinOutput = rightJoin.getOutput();
-
- List<AggregateFunction> leftFuncs = new ArrayList<>();
- List<AggregateFunction> rightFuncs = new ArrayList<>();
- Set<Slot> leftFuncSlotSet = new HashSet<>();
- Set<Slot> rightFuncSlotSet = new HashSet<>();
- Set<Slot> newAggOverJoinGroupByKeys = new HashSet<>();
- for (AggregateFunction func : agg.getAggregateFunctions()) {
- Slot slot = (Slot) func.child(0);
- newAggOverJoinGroupByKeys.add(slot);
- if (leftJoinOutput.contains(slot)) {
- leftFuncs.add(func);
- leftFuncSlotSet.add(slot);
- } else if (rightJoinOutput.contains(slot)) {
- rightFuncs.add(func);
- rightFuncSlotSet.add(slot);
- } else {
- throw new IllegalStateException("Slot " + slot + " not found
in join output");
- }
- }
- boolean isLeftSideAggDistinct = !leftFuncs.isEmpty() &&
rightFuncs.isEmpty();
- boolean isRightSideAggDistinct = leftFuncs.isEmpty() &&
!rightFuncs.isEmpty();
- if (!isLeftSideAggDistinct && !isRightSideAggDistinct) {
- return null;
- }
-
- Set<Slot> leftPushDownGroupBy = new HashSet<>();
- Set<Slot> rightPushDownGroupBy = new HashSet<>();
- for (Expression e : agg.getGroupByExpressions()) {
- Slot slot = (Slot) e;
- newAggOverJoinGroupByKeys.add(slot);
- if (leftJoinOutput.contains(slot)) {
- leftPushDownGroupBy.add(slot);
- } else if (rightJoinOutput.contains(slot)) {
- rightPushDownGroupBy.add(slot);
- } else {
- return null;
- }
- }
- join.getHashJoinConjuncts().forEach(e ->
e.getInputSlots().forEach(slot -> {
- if (leftJoinOutput.contains(slot)) {
- leftPushDownGroupBy.add(slot);
- } else if (rightJoinOutput.contains(slot)) {
- rightPushDownGroupBy.add(slot);
- } else {
- throw new IllegalStateException("Slot " + slot + " not found
in join output");
- }
- }));
-
- if (isLeftSideAggDistinct) {
- leftPushDownGroupBy.add((Slot) leftFuncs.get(0).child(0));
- Builder<NamedExpression> leftAggOutputBuilder =
ImmutableList.<NamedExpression>builder()
- .addAll(leftPushDownGroupBy);
- leftJoin = new
LogicalAggregate<>(ImmutableList.copyOf(leftPushDownGroupBy),
- leftAggOutputBuilder.build(), join.left());
- } else {
- rightPushDownGroupBy.add((Slot) rightFuncs.get(0).child(0));
- Builder<NamedExpression> rightAggOutputBuilder =
ImmutableList.<NamedExpression>builder()
- .addAll(rightPushDownGroupBy);
- rightJoin = new
LogicalAggregate<>(ImmutableList.copyOf(rightPushDownGroupBy),
- rightAggOutputBuilder.build(), join.right());
- }
-
- Preconditions.checkState(leftJoin != join.left() || rightJoin !=
join.right(),
- "not pushing down aggr with distinct through join on single
side successfully");
- Plan newJoin = join.withChildren(leftJoin, rightJoin);
- LogicalAggregate<? extends Plan> newAggOverJoin =
agg.withChildGroupByAndOutput(
- ImmutableList.copyOf(newAggOverJoinGroupByKeys), projects,
newJoin);
-
- List<NamedExpression> newOutputExprs = new ArrayList<>();
- for (NamedExpression ne : agg.getOutputExpressions()) {
- if (ne instanceof Alias && ((Alias) ne).child() instanceof
AggregateFunction) {
- AggregateFunction func = (AggregateFunction) ((Alias)
ne).child();
- Slot slot = (Slot) func.child(0);
- if (leftFuncSlotSet.contains(slot) ||
rightFuncSlotSet.contains(slot)) {
- Expression newFunc = discardDistinct(func);
- newOutputExprs.add((NamedExpression)
ne.withChildren(newFunc));
- } else {
- throw new IllegalStateException("Slot " + slot + " not
found in join output");
- }
- } else {
- newOutputExprs.add(ne);
- }
- }
- return agg.withAggOutputChild(newOutputExprs, newAggOverJoin);
- }
-
- private static Expression discardDistinct(AggregateFunction func) {
- Preconditions.checkState(func.isDistinct(), "current aggregation
function is not distinct");
- Set<Expression> aggChild = Sets.newLinkedHashSet(func.children());
- return func.withDistinctAndChildren(false,
ImmutableList.copyOf(aggChild));
- }
-}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java
index e0219de031a..4fed1f45cb5 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriter.java
@@ -675,7 +675,7 @@ public class EagerAggRewriter extends
DefaultPlanRewriter<PushDownAggContext> {
if (context.aggFuncAndGroupKeyAllEmpty() ||
context.hasVolatileFunctions()) {
return project;
}
- if (containsVolatileGroupKeyAfterProject(project, context)) {
+ if (containsVolatileGroupKeyAfterProject(project, context) ||
project.containsNoneMovableFunction()) {
return genAggregate(project, context);
}
if (project.child() instanceof LogicalCatalogRelation
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java
index 2229058b701..4dcd720ccfa 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/PushDownAggregation.java
@@ -47,6 +47,7 @@ import
org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.qe.SessionVariable;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -57,6 +58,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -132,15 +134,25 @@ public class PushDownAggregation extends
DefaultPlanRewriter<JobContext> impleme
boolean hasDecomposedAggIf = false;
boolean containsNullToNonNull = false;
Map<NamedExpression, List<AggregateFunction>>
aggFunctionsForOutputExpressions = Maps.newHashMap();
- for (NamedExpression aggOutput : agg.getOutputExpressions()) {
- List<AggregateFunction> funcs = Lists.newArrayList();
- aggFunctionsForOutputExpressions.put(aggOutput, funcs);
- for (Object obj :
aggOutput.collect(AggregateFunction.class::isInstance)) {
- AggregateFunction aggFunction = (AggregateFunction) obj;
- if (aggFunction.isDistinct()) {
- return agg;
- }
- if (pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ Set<AggregateFunction> allAggFunctions = agg.getAggregateFunctions();
+ boolean hasDistinctAgg =
allAggFunctions.stream().anyMatch(AggregateFunction::isDistinct);
+ if (hasDistinctAgg) {
+ // Keep the distinct aggregate functions unchanged at the upper
aggregate. The common distinct keys
+ // become extra group keys for duplicate elimination below joins.
+ Optional<Set<SlotReference>> distinctKeys =
getCommonDistinctKeys(allAggFunctions);
+ if (!distinctKeys.isPresent()) {
+ return agg;
+ }
+ groupKeys.addAll(distinctKeys.get());
+ } else {
+ for (NamedExpression aggOutput : agg.getOutputExpressions()) {
+ List<AggregateFunction> funcs = Lists.newArrayList();
+ aggFunctionsForOutputExpressions.put(aggOutput, funcs);
+ for (Object obj :
aggOutput.collect(AggregateFunction.class::isInstance)) {
+ AggregateFunction aggFunction = (AggregateFunction) obj;
+ if
(!pushDownAggFunctionSet.contains(aggFunction.getClass())) {
+ return agg;
+ }
if (aggFunction.containsVolatileExpression()) {
return agg;
}
@@ -187,9 +199,6 @@ public class PushDownAggregation extends
DefaultPlanRewriter<JobContext> impleme
aggFunctions.add(aggFunction);
funcs.add(aggFunction);
}
-
- } else {
- return agg;
}
}
}
@@ -230,42 +239,47 @@ public class PushDownAggregation extends
DefaultPlanRewriter<JobContext> impleme
// ->scan(T1[A...])
// ->scan(T2)
List<NamedExpression> newOutputExpressions = new ArrayList<>();
- for (NamedExpression ne : agg.getOutputExpressions()) {
- if (ne instanceof SlotReference) {
- newOutputExpressions.add(ne);
- } else {
- // every expression has its own replaceMap
- // aggregation(output=[min(A), sum(A)])
- // --> join
- // -> T1 [A ...]
- // -> T2 [...]
- // =>
- // aggregation(output=[min(minA), sum(sumA)])
- // --> join
- // -> agg(output=[min(A) as minA, sum(A) as
sumA])
- // -> T1 [A ...]
- // -> T2 [...]
- // for min(A), replaceMap: A->minA
- // for sum(A), replaceMap: A->sumA
- // for count(A), replaceMap: count(A)->sum(countA),
because count needs rollup to sum
- Map<Expression, Expression> replaceMap = new
HashMap<>();
- List<AggregateFunction> relatedAggFunc =
aggFunctionsForOutputExpressions.get(ne);
- for (AggregateFunction func : relatedAggFunc) {
- Alias pushedAlias =
pushDownContext.getAliasMap().get(func);
- ExprId pushId = pushedAlias.getExprId();
- if (!state.hasAggFuncOutput(pushId)) {
- continue;
- }
- Expression value =
state.getPushedAggFuncSlot(pushId);
- if (func instanceof Count) {
- replaceMap.put(func, new Sum0(value));
- } else if (func.arity() > 0) {
- replaceMap.put(func.child(0), value);
+ if (hasDistinctAgg) {
+ newOutputExpressions.addAll(agg.getOutputExpressions());
+ } else {
+ for (NamedExpression ne : agg.getOutputExpressions()) {
+ if (ne instanceof SlotReference) {
+ newOutputExpressions.add(ne);
+ } else {
+ // every expression has its own replaceMap
+ // aggregation(output=[min(A), sum(A)])
+ // --> join
+ // -> T1 [A ...]
+ // -> T2 [...]
+ // =>
+ // aggregation(output=[min(minA), sum(sumA)])
+ // --> join
+ // -> agg(output=[min(A) as minA,
sum(A) as sumA])
+ // -> T1 [A ...]
+ // -> T2 [...]
+ // for min(A), replaceMap: A->minA
+ // for sum(A), replaceMap: A->sumA
+ // for count(A), replaceMap:
count(A)->sum(countA), because count needs rollup to sum
+ Map<Expression, Expression> replaceMap = new
HashMap<>();
+ List<AggregateFunction> relatedAggFunc =
aggFunctionsForOutputExpressions.get(ne);
+ for (AggregateFunction func : relatedAggFunc) {
+ Alias pushedAlias =
pushDownContext.getAliasMap().get(func);
+ ExprId pushId = pushedAlias.getExprId();
+ if (!state.hasAggFuncOutput(pushId)) {
+ continue;
+ }
+ Expression value =
state.getPushedAggFuncSlot(pushId);
+ if (func instanceof Count) {
+ replaceMap.put(func, new Sum0(value));
+ } else if (func.arity() > 0) {
+ replaceMap.put(func.child(0), value);
+ }
}
+ NamedExpression replaceAliasExpr =
+ (NamedExpression)
ExpressionUtils.replace(ne, replaceMap);
+ replaceAliasExpr = (NamedExpression)
ExpressionUtils.rebuildSignature(replaceAliasExpr);
+ newOutputExpressions.add(replaceAliasExpr);
}
- NamedExpression replaceAliasExpr = (NamedExpression)
ExpressionUtils.replace(ne, replaceMap);
- replaceAliasExpr = (NamedExpression)
ExpressionUtils.rebuildSignature(replaceAliasExpr);
- newOutputExpressions.add(replaceAliasExpr);
}
}
LogicalAggregate<Plan> eagerAgg =
@@ -280,6 +294,33 @@ public class PushDownAggregation extends
DefaultPlanRewriter<JobContext> impleme
return agg;
}
+ private Optional<Set<SlotReference>>
getCommonDistinctKeys(Set<AggregateFunction> aggFunctions) {
+ Set<SlotReference> commonDistinctKeys = null;
+ for (AggregateFunction aggFunction : aggFunctions) {
+ if (!aggFunction.isDistinct() ||
!pushDownAggFunctionSet.contains(aggFunction.getClass())
+ || aggFunction.containsVolatileExpression()) {
+ return Optional.empty();
+ }
+ ImmutableSet.Builder<SlotReference> distinctKeysBuilder =
ImmutableSet.builder();
+ for (Expression child : aggFunction.children()) {
+ if (!(child instanceof SlotReference)) {
+ return Optional.empty();
+ }
+ distinctKeysBuilder.add((SlotReference) child);
+ }
+ Set<SlotReference> distinctKeys = distinctKeysBuilder.build();
+ if (distinctKeys.isEmpty()) {
+ return Optional.empty();
+ }
+ if (commonDistinctKeys == null) {
+ commonDistinctKeys = distinctKeys;
+ } else if (!commonDistinctKeys.equals(distinctKeys)) {
+ return Optional.empty();
+ }
+ }
+ return Optional.ofNullable(commonDistinctKeys);
+ }
+
private boolean checkSubTreePattern(Plan root) {
return containsPushDownJoin(root)
&& checkPlanNodeType(root);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHintTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHintTest.java
deleted file mode 100644
index b9e3f0142d1..00000000000
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHintTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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.doris.nereids.rules.analysis;
-
-import org.apache.doris.nereids.hint.Hint;
-import org.apache.doris.nereids.hint.UseCboRuleHint;
-import org.apache.doris.nereids.sqltest.SqlTestBase;
-import org.apache.doris.nereids.util.PlanChecker;
-
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Tests for EliminateLogicalSelectHint, verifying that no_use_cbo_rule hints
are correctly
- * propagated into the statement context after analysis.
- */
-public class EliminateLogicalSelectHintTest extends SqlTestBase {
-
- @Test
- public void testNoUseCboRuleHintIsRecognized() {
- String sql = "SELECT /*+ no_use_cbo_rule(INFER_SET_OPERATOR_DISTINCT)
*/ * FROM T1";
- PlanChecker checker = PlanChecker.from(connectContext).analyze(sql);
-
- List<Hint> hints =
checker.getCascadesContext().getStatementContext().getHints();
- List<UseCboRuleHint> cboHints = hints.stream()
- .filter(h -> h instanceof UseCboRuleHint)
- .map(h -> (UseCboRuleHint) h)
- .collect(Collectors.toList());
-
- Assertions.assertFalse(cboHints.isEmpty(),
- "no_use_cbo_rule hint should be added to statementContext
hints");
-
Assertions.assertTrue(cboHints.stream().anyMatch(UseCboRuleHint::isNotUseCboRule),
- "hint should have isNotUseCboRule=true");
- Assertions.assertTrue(
- cboHints.stream().anyMatch(h ->
h.getHintName().equalsIgnoreCase("INFER_SET_OPERATOR_DISTINCT")),
- "hint should carry the rule name INFER_SET_OPERATOR_DISTINCT");
- }
-}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownCountThroughJoinTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownCountThroughJoinTest.java
deleted file mode 100644
index 6f86c63aeff..00000000000
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownCountThroughJoinTest.java
+++ /dev/null
@@ -1,216 +0,0 @@
-// 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.doris.nereids.rules.rewrite;
-
-import org.apache.doris.common.Pair;
-import org.apache.doris.nereids.trees.expressions.Alias;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Multiply;
-import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
-import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
-import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
-import org.apache.doris.nereids.trees.plans.JoinType;
-import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
-import org.apache.doris.nereids.types.DecimalV3Type;
-import org.apache.doris.nereids.util.LogicalPlanBuilder;
-import org.apache.doris.nereids.util.MemoPatternMatchSupported;
-import org.apache.doris.nereids.util.MemoTestUtils;
-import org.apache.doris.nereids.util.PlanChecker;
-import org.apache.doris.nereids.util.PlanConstructor;
-
-import com.google.common.collect.ImmutableList;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
-class PushDownCountThroughJoinTest implements MemoPatternMatchSupported {
- private static final LogicalOlapScan scan1 =
PlanConstructor.newLogicalOlapScan(0, "t1", 0);
- private static final LogicalOlapScan scan2 =
PlanConstructor.newLogicalOlapScan(1, "t2", 0);
-
- @Test
- void testSingleCount() {
- Alias count = new Count(scan1.getOutput().get(0)).alias("count");
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(0),
ImmutableList.of(scan1.getOutput().get(0), count))
- .build();
-
- PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .printlnTree()
- .matches(
- logicalAggregate(
- logicalJoin(
- logicalAggregate(),
- logicalAggregate()
- )
- )
- );
- }
-
- @Test
- void testMultiCount() {
- Alias leftCnt1 = new Count(scan1.getOutput().get(0)).alias("leftCnt1");
- Alias leftCnt2 = new Count(scan1.getOutput().get(1)).alias("leftCnt2");
- Alias rightCnt1 = new
Count(scan2.getOutput().get(1)).alias("rightCnt1");
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(0),
- ImmutableList.of(scan1.getOutput().get(0), leftCnt1,
leftCnt2, rightCnt1))
- .build();
-
- PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .matches(
- logicalAggregate(
- logicalJoin(
- logicalAggregate(),
- logicalAggregate()
- )
- )
- );
- }
-
- /**
- * verify that after applying PushDownAggThroughJoin rule, agg func has
correct dataType
- */
- @Test
- void testSumAndDataType() {
- LogicalOlapScan salary1 = new
LogicalOlapScan(PlanConstructor.getNextRelationId(),
- PlanConstructor.salary, ImmutableList.of(""));
- Alias sum = new Sum(salary1.getOutput().get(2)).alias("sum");
- LogicalPlan plan = new LogicalPlanBuilder(salary1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(0),
- ImmutableList.of(salary1.getOutput().get(0), sum))
- .build();
- PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .matches(logicalAggregate(
- logicalJoin(
- logicalAggregate(),
- logicalAggregate()
- )
- ).when(agg -> {
- Multiply multiply = null;
- for (Expression expr : agg.getOutputExpressions()) {
- if (expr instanceof Alias
- && expr.child(0) instanceof Sum
- && expr.child(0).child(0) instanceof Multiply)
{
- multiply = (Multiply) expr.child(0).child(0);
- break;
- }
- }
- if (multiply == null) {
- return false;
- }
- Assertions.assertInstanceOf(DecimalV3Type.class,
multiply.child(0).getDataType());
- Assertions.assertInstanceOf(DecimalV3Type.class,
multiply.child(1).getDataType());
- return true;
- }));
- }
-
- @Test
- void testSingleCountStar() {
- Alias count = new Count().alias("countStar");
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(0),
ImmutableList.of(scan1.getOutput().get(0), count))
- .build();
-
- PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .matches(
- logicalAggregate(
- logicalJoin(
- logicalAggregate(),
- logicalAggregate()
- )
- )
- );
- }
-
- @Test
- void testSingleCountStarEmptyGroupBy() {
- Alias count = new Count().alias("countStar");
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(),
ImmutableList.of(count))
- .build();
-
- // shouldn't rewrite.
- PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .matches(
- logicalAggregate(
- logicalJoin(
- logicalOlapScan(),
- logicalOlapScan()
- )
- )
- );
- }
-
- @Test
- void testCountNullNotPushedDown() {
- // count(null) should NOT be treated as count(*) and should NOT be
pushed down.
- // NullLiteral is neither isCountStar() nor instanceof Slot,
- // so the rule's predicate rejects the aggregate.
- Alias countNull = new Count(NullLiteral.INSTANCE).alias("countNull");
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(0),
- ImmutableList.of(scan1.getOutput().get(0), countNull))
- .build();
-
- // Should NOT rewrite — aggregate stays above the original join.
- PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .matches(
- logicalAggregate(
- logicalJoin(
- logicalOlapScan(),
- logicalOlapScan()
- )
- )
- );
- }
-
- @Test
- void testBothSideCountAndCountStar() {
- Alias leftCnt = new Count(scan1.getOutput().get(0)).alias("leftCnt");
- Alias rightCnt = new Count(scan2.getOutput().get(0)).alias("rightCnt");
- Alias countStar = new Count().alias("countStar");
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(0),
- ImmutableList.of(scan1.getOutput().get(0), leftCnt,
rightCnt, countStar))
- .build();
-
- PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .matches(
- logicalAggregate(
- logicalJoin(
- logicalAggregate(),
- logicalAggregate()
- )
- )
- );
- }
-}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownSumThroughJoinTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownSumThroughJoinTest.java
deleted file mode 100644
index b3901c7f7b1..00000000000
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownSumThroughJoinTest.java
+++ /dev/null
@@ -1,134 +0,0 @@
-// 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.doris.nereids.rules.rewrite;
-
-import org.apache.doris.common.Pair;
-import org.apache.doris.nereids.rules.RuleType;
-import org.apache.doris.nereids.trees.expressions.Alias;
-import org.apache.doris.nereids.trees.expressions.functions.agg.Sum;
-import org.apache.doris.nereids.trees.plans.JoinType;
-import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
-import org.apache.doris.nereids.util.LogicalPlanBuilder;
-import org.apache.doris.nereids.util.MemoPatternMatchSupported;
-import org.apache.doris.nereids.util.MemoTestUtils;
-import org.apache.doris.nereids.util.PlanChecker;
-import org.apache.doris.nereids.util.PlanConstructor;
-import org.apache.doris.qe.ConnectContext;
-import org.apache.doris.qe.SessionVariable;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
-
-class PushDownSumThroughJoinTest implements MemoPatternMatchSupported {
- private static final LogicalOlapScan scan1 =
PlanConstructor.newLogicalOlapScan(0, "t1", 0);
- private static final LogicalOlapScan scan2 =
PlanConstructor.newLogicalOlapScan(1, "t2", 0);
-
- private ConnectContext createMockedContext() {
- ConnectContext ctx = MemoTestUtils.createConnectContext();
- SessionVariable sv = Mockito.spy(ctx.getSessionVariable());
-
Mockito.doReturn(ImmutableSet.of(RuleType.PUSH_DOWN_AGG_THROUGH_JOIN.type()))
- .when(sv).getEnableNereidsRules();
- ctx.setSessionVariable(sv);
- return ctx;
- }
-
- @Test
- void testSingleJoinLeftSum() {
- Alias sum = new Sum(scan1.getOutput().get(1)).alias("sum");
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(0),
ImmutableList.of(scan1.getOutput().get(0), sum))
- .build();
-
- PlanChecker.from(createMockedContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .matches(
- logicalAggregate(
- logicalJoin(
- logicalAggregate(),
- logicalAggregate()
- )
- )
- );
- }
-
- @Test
- void testSingleJoinRightSum() {
- Alias sum = new Sum(scan2.getOutput().get(1)).alias("sum");
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(0),
ImmutableList.of(scan1.getOutput().get(0), sum))
- .build();
-
- PlanChecker.from(createMockedContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .matches(
- logicalAggregate(
- logicalJoin(
- logicalAggregate(),
- logicalAggregate()
- )
- )
- );
- }
-
- @Test
- void testSingleJoinBothSum() {
- Alias leftSum = new Sum(scan1.getOutput().get(1)).alias("leftSum");
- Alias rightSum = new Sum(scan2.getOutput().get(1)).alias("rightSum");
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(0),
ImmutableList.of(scan1.getOutput().get(0), leftSum, rightSum))
- .build();
-
- PlanChecker.from(createMockedContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .matches(
- logicalAggregate(
- logicalJoin(
- logicalAggregate(),
- logicalAggregate()
- )
- )
- );
- }
-
- @Test
- void testAggNotOutputGroupBy() {
- // agg don't output group by
- Alias sum = new Sum(scan1.getOutput().get(1)).alias("sum");
- LogicalPlan plan = new LogicalPlanBuilder(scan1)
- .join(scan2, JoinType.INNER_JOIN, Pair.of(0, 0))
- .aggGroupUsingIndex(ImmutableList.of(0), ImmutableList.of(sum))
- .build();
-
- PlanChecker.from(createMockedContext(), plan)
- .applyTopDown(new PushDownAggThroughJoin())
- .matches(
- logicalAggregate(
- logicalJoin(
- logicalAggregate(),
- logicalAggregate()
- )
- )
- );
- }
-}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriterTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriterTest.java
index d5eb42fa0ea..c44e76c9616 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriterTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/eageraggregation/EagerAggRewriterTest.java
@@ -19,6 +19,7 @@ package
org.apache.doris.nereids.rules.rewrite.eageraggregation;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.analyzer.UnboundRelation;
+import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.rules.exploration.join.JoinReorderContext;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.Expression;
@@ -138,19 +139,57 @@ class EagerAggRewriterTest extends TestWithFeService
implements MemoPatternMatch
}
@Test
- void testNotPushDownDistinctAgg() {
- // Distinct aggregation should not be pushed down.
+ void testPushDownDistinctAggWithSameKey() {
connectContext.getSessionVariable().setEagerAggregationMode(1);
connectContext.getSessionVariable().setDisableJoinReorder(true);
+ disableDistinctAggregateRewrite();
try {
- String sql = "select count(distinct t1.name), t2.id2 from t1 join
t2"
+ String sql = "select count(distinct t1.id1), sum(distinct t1.id1),
t2.id2 from t1 join t2"
+ " on t1.id1 = t2.id2 group by t2.id2";
- PlanChecker.from(connectContext)
- .analyze(sql)
- .rewrite()
- .nonMatch(logicalJoin(logicalAggregate(), any()))
- .nonMatch(logicalJoin(any(), logicalAggregate()))
- .printlnTree();
+ assertDistinctAggPushDown(sql, 1);
+ } finally {
+ connectContext.getSessionVariable().setEagerAggregationMode(0);
+ connectContext.getSessionVariable().setDisableJoinReorder(false);
+ resetDisabledRules();
+ }
+ }
+
+ @Test
+ void testPushDownMultiColumnCountDistinct() {
+ connectContext.getSessionVariable().setEagerAggregationMode(1);
+ connectContext.getSessionVariable().setDisableJoinReorder(true);
+ disableDistinctAggregateRewrite();
+ try {
+ String sql = "select count(distinct t1.id1, t1.name), t2.id2 from
t1 join t2"
+ + " on t1.id1 = t2.id2 group by t2.id2";
+ assertDistinctAggPushDown(sql, 2);
+ } finally {
+ connectContext.getSessionVariable().setEagerAggregationMode(0);
+ connectContext.getSessionVariable().setDisableJoinReorder(false);
+ resetDisabledRules();
+ }
+ }
+
+ @Test
+ void testNotPushDownDistinctAggWithDifferentKeys() {
+ connectContext.getSessionVariable().setEagerAggregationMode(1);
+ connectContext.getSessionVariable().setDisableJoinReorder(true);
+ try {
+ assertNoAggregateUnderJoin("select count(distinct t1.id1),
count(distinct t1.name), t2.id2"
+ + " from t1 join t2 on t1.id1 = t2.id2 group by t2.id2");
+ } finally {
+ connectContext.getSessionVariable().setEagerAggregationMode(0);
+ connectContext.getSessionVariable().setDisableJoinReorder(false);
+ }
+ }
+
+ @Test
+ void testNotPushDownDistinctAggWithNonDistinctAgg() {
+ connectContext.getSessionVariable().setEagerAggregationMode(1);
+ connectContext.getSessionVariable().setDisableJoinReorder(true);
+ try {
+ assertNoAggregateUnderJoin("select count(distinct t1.name),
max(t1.id1), t2.id2"
+ + " from t1 join t2 on t1.id1 = t2.id2 group by t2.id2");
} finally {
connectContext.getSessionVariable().setEagerAggregationMode(0);
connectContext.getSessionVariable().setDisableJoinReorder(false);
@@ -850,6 +889,34 @@ class EagerAggRewriterTest extends TestWithFeService
implements MemoPatternMatch
Assertions.assertFalse(containsAggregateUnderJoin(plan),
plan.treeString());
}
+ private void assertDistinctAggPushDown(String sql, int
expectedLeftGroupKeyCount) {
+ Plan plan = PlanChecker.from(connectContext)
+ .analyze(sql)
+ .rewrite()
+ .getPlan();
+ LogicalAggregate<?> topAggregate = findFirstPlan(plan,
LogicalAggregate.class);
+ Assertions.assertNotNull(topAggregate, plan.treeString());
+ Assertions.assertTrue(topAggregate.getAggregateFunctions().stream()
+ .allMatch(aggFunction -> aggFunction.isDistinct()),
plan.treeString());
+ LogicalJoin<?, ?> join = findFirstPlan(topAggregate.child(),
LogicalJoin.class);
+ Assertions.assertNotNull(join, plan.treeString());
+ LogicalAggregate<?> leftAggregate = findFirstPlan(join.left(),
LogicalAggregate.class);
+ Assertions.assertNotNull(leftAggregate, plan.treeString());
+ Assertions.assertEquals(expectedLeftGroupKeyCount,
+ leftAggregate.getGroupByExpressions().size(),
plan.treeString());
+ }
+
+ private void disableDistinctAggregateRewrite() {
+
connectContext.getSessionVariable().setDisableNereidsRules(String.join(",",
+ RuleType.PRUNE_EMPTY_PARTITION.name(),
+ RuleType.DISTINCT_AGGREGATE_SPLIT.name(),
+ RuleType.PROCESS_SCALAR_AGG_MUST_USE_MULTI_DISTINCT.name()));
+ }
+
+ private void resetDisabledRules() {
+
connectContext.getSessionVariable().setDisableNereidsRules(RuleType.PRUNE_EMPTY_PARTITION.name());
+ }
+
private boolean containsAggregateUnderJoin(Plan plan) {
if (plan instanceof LogicalJoin) {
LogicalJoin<?, ?> join = (LogicalJoin<?, ?>) plan;
diff --git
a/regression-test/data/query_p0/eager_agg/distinct_agg_func_push_down.out
b/regression-test/data/query_p0/eager_agg/distinct_agg_func_push_down.out
new file mode 100644
index 00000000000..69b42b6eedc
--- /dev/null
+++ b/regression-test/data/query_p0/eager_agg/distinct_agg_func_push_down.out
@@ -0,0 +1,237 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !groupby_pushdown_basic --
+1
+1
+1
+3
+
+-- !groupby_pushdown_left_join --
+1
+1
+1
+3
+
+-- !groupby_pushdown_right_join --
+1
+1
+1
+3
+
+-- !groupby_pushdown_full_join --
+1
+1
+1
+3
+
+-- !groupby_pushdown_left_semi_join --
+1
+1
+1
+3
+
+-- !groupby_pushdown_left_anti_join --
+
+-- !groupby_pushdown_complex_conditions --
+
+-- !groupby_pushdown_with_aggregate --
+1 1
+1 2
+1 3
+3 2
+
+-- !groupby_pushdown_subquery --
+
+-- !groupby_pushdown_outer_join --
+1
+1
+1
+3
+
+-- !groupby_pushdown_deep_subquery --
+
+-- !groupby_pushdown_having --
+
+-- !groupby_pushdown_mixed_aggregates --
+1 1
+1 2
+1 3
+3 6
+
+-- !groupby_pushdown_multi_table_join --
+1
+1
+1
+
+-- !groupby_pushdown_with_order_by --
+1
+1
+1
+3
+
+-- !groupby_pushdown_multiple_equal_conditions --
+1
+1
+1
+
+-- !groupby_pushdown_equal_conditions_with_aggregate --
+1 1
+2 1
+3 1
+
+-- !groupby_pushdown_equal_conditions_non_aggregate --
+a 1
+b 1
+c 1
+
+-- !groupby_pushdown_equal_conditions_non_aggregate_with_aggregate --
+a 1 1
+b 1 1
+c 1 1
+
+-- !groupby_pushdown_with_where_clause --
+
+-- !groupby_pushdown_varied_aggregates --
+1 1.5 1
+1 4.5 1
+1 7.5 1
+3 7 0
+
+-- !groupby_pushdown_with_order_by_limit --
+1
+1
+1
+3
+
+-- !groupby_pushdown_alias_multiple_equal_conditions --
+1
+1
+1
+
+-- !groupby_pushdown_complex_join_condition --
+
+-- !groupby_pushdown_function_processed_columns --
+0
+1
+1
+1
+
+-- !groupby_pushdown_nested_queries --
+
+-- !with_hint_groupby_pushdown_basic --
+1
+1
+1
+3
+
+-- !with_hint_groupby_pushdown_left_join --
+1
+1
+1
+3
+
+-- !with_hint_groupby_pushdown_right_join --
+1
+1
+1
+3
+
+-- !with_hint_groupby_pushdown_full_join --
+1
+1
+1
+3
+
+-- !with_hint_groupby_pushdown_left_semi_join --
+1
+1
+1
+3
+
+-- !with_hint_groupby_pushdown_left_anti_join --
+
+-- !with_hint_groupby_pushdown_complex_conditions --
+
+-- !with_hint_groupby_pushdown_with_aggregate --
+1 1
+1 2
+1 3
+3 2
+
+-- !with_hint_groupby_pushdown_subquery --
+
+-- !with_hint_groupby_pushdown_outer_join --
+1
+1
+1
+3
+
+-- !with_hint_groupby_pushdown_deep_subquery --
+
+-- !with_hint_groupby_pushdown_having --
+
+-- !with_hint_groupby_pushdown_mixed_aggregates --
+1 1
+1 2
+1 3
+3 6
+
+-- !with_hint_groupby_pushdown_multi_table_join --
+1
+1
+1
+
+-- !with_hint_groupby_pushdown_with_order_by --
+1
+1
+1
+3
+
+-- !with_hint_groupby_pushdown_multiple_equal_conditions --
+1
+1
+1
+
+-- !with_hint_groupby_pushdown_equal_conditions_with_aggregate --
+1 1
+2 1
+3 1
+
+-- !with_hint_groupby_pushdown_equal_conditions_non_aggregate --
+a 1
+b 1
+c 1
+
+-- !with_hint_groupby_pushdown_equal_conditions_non_aggregate_with_aggregate --
+a 1 1
+b 1 1
+c 1 1
+
+-- !with_hint_groupby_pushdown_with_where_clause --
+
+-- !with_hint_groupby_pushdown_varied_aggregates --
+1 1.5 1
+1 4.5 1
+1 7.5 1
+3 7 0
+
+-- !with_hint_groupby_pushdown_with_order_by_limit --
+1
+1
+1
+3
+
+-- !with_hint_groupby_pushdown_alias_multiple_equal_conditions --
+1
+1
+1
+
+-- !with_hint_groupby_pushdown_complex_join_condition --
+
+-- !with_hint_groupby_pushdown_function_processed_columns --
+0
+1
+1
+1
+
+-- !with_hint_groupby_pushdown_nested_queries --
+
diff --git
a/regression-test/suites/query_p0/eager_agg/distinct_agg_func_push_down.groovy
b/regression-test/suites/query_p0/eager_agg/distinct_agg_func_push_down.groovy
new file mode 100644
index 00000000000..69bb4302068
--- /dev/null
+++
b/regression-test/suites/query_p0/eager_agg/distinct_agg_func_push_down.groovy
@@ -0,0 +1,256 @@
+// 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.
+
+suite("push_down_count_distinct_through_join_one_side") {
+ sql "set runtime_filter_mode=OFF"
+ sql "set eager_aggregation_mode=1;"
+ sql """
+ DROP TABLE IF EXISTS count_with_distinct_t;
+ """
+
+ sql """
+ CREATE TABLE IF NOT EXISTS count_with_distinct_t(
+ `id` int(32),
+ `score` int(64) NULL,
+ `name` varchar(64) NULL
+ ) ENGINE = OLAP
+ DISTRIBUTED BY HASH(id) BUCKETS 4
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default: 1"
+ );
+ """
+
+ sql "insert into count_with_distinct_t values (1, 1, 'a')"
+ sql "insert into count_with_distinct_t values (2, null, 'a')"
+ sql "insert into count_with_distinct_t values (3, 1, null)"
+ sql "insert into count_with_distinct_t values (4, 2, 'b')"
+ sql "insert into count_with_distinct_t values (5, null, 'b')"
+ sql "insert into count_with_distinct_t values (6, 2, null)"
+ sql "insert into count_with_distinct_t values (7, 3, 'c')"
+ sql "insert into count_with_distinct_t values (8, null, 'c')"
+ sql "insert into count_with_distinct_t values (9, 3, null)"
+ sql "insert into count_with_distinct_t values (10, null, null)"
+ sql "analyze table count_with_distinct_t with full with sync;"
+
+ order_qt_groupby_pushdown_basic """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_left_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 left
join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_right_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 right
join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_full_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 full
join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_left_semi_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 inner
join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_left_anti_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 left
anti join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_complex_conditions """
+ select count(distinct t1.score) from count_with_distinct_t t1 join
count_with_distinct_t t2 on t1.id = t2.id and t1.name < t2.name group by
t1.name;
+ """
+
+ order_qt_groupby_pushdown_with_aggregate """
+ select count(distinct t1.score), avg(t1.score) from
count_with_distinct_t t1 join count_with_distinct_t t2 on t1.id = t2.id group
by t1.name;
+ """
+
+ order_qt_groupby_pushdown_subquery """
+ select count(distinct t1.score) from (select * from
count_with_distinct_t where score > 10) t1 join count_with_distinct_t t2 on
t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_outer_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 left
join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_deep_subquery """
+ select count(distinct t1.score) from (select * from (select * from
count_with_distinct_t) count_with_distinct_t where score > 10) t1 join
count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_having """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id group by t1.name having
count(distinct t1.score) > 100;
+ """
+
+ order_qt_groupby_pushdown_mixed_aggregates """
+ select count(distinct t1.score), sum(distinct t1.score) from
count_with_distinct_t t1 join count_with_distinct_t t2 on t1.id = t2.id group
by t1.name;
+ """
+
+ order_qt_groupby_pushdown_multi_table_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 join
count_with_distinct_t t2 on t1.id = t2.id join count_with_distinct_t t3 on
t1.name = t3.name group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_with_order_by """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id group by t1.name order by t1.name;
+ """
+
+ order_qt_groupby_pushdown_multiple_equal_conditions """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id and t1.name = t2.name group by
t1.name;
+ """
+
+ order_qt_groupby_pushdown_equal_conditions_with_aggregate """
+ select sum(distinct t1.score), count(distinct t2.score) from
count_with_distinct_t t1 join count_with_distinct_t t2 on t1.id = t2.id and
t1.name = t2.name group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_equal_conditions_non_aggregate """
+ select t1.name, count(distinct t1.score) from count_with_distinct_t
t1, count_with_distinct_t t2 where t1.id = t2.id and t1.name = t2.name group by
t1.name;
+ """
+
+ order_qt_groupby_pushdown_equal_conditions_non_aggregate_with_aggregate """
+ select t1.name, count(distinct t1.score), count(distinct t2.score)
from count_with_distinct_t t1, count_with_distinct_t t2 where t1.id = t2.id and
t1.name = t2.name group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_with_where_clause """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id and t1.score > 50 group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_varied_aggregates """
+ select count(distinct t1.score), avg(t1.id), count(distinct t2.name)
from count_with_distinct_t t1 join count_with_distinct_t t2 on t1.id = t2.id
group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_with_order_by_limit """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id group by t1.name order by
count(distinct t1.score) limit 10;
+ """
+
+ order_qt_groupby_pushdown_alias_multiple_equal_conditions """
+ select count(distinct t1_alias.score) from count_with_distinct_t
t1_alias join count_with_distinct_t t2_alias on t1_alias.id = t2_alias.id and
t1_alias.name = t2_alias.name group by t1_alias.name;
+ """
+
+ order_qt_groupby_pushdown_complex_join_condition """
+ select count(distinct t1.score) from count_with_distinct_t t1 join
count_with_distinct_t t2 on t1.id = t2.id and t1.score = t2.score and t1.name
<> t2.name group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_function_processed_columns """
+ select count(distinct LENGTH(t1.name)) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_groupby_pushdown_nested_queries """
+ select count(distinct t1.score) from (select * from
count_with_distinct_t where score > 20) t1 join (select * from
count_with_distinct_t where id < 100) t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_basic """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_left_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 left
join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_right_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 right
join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_full_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 full
join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_left_semi_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 inner
join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_left_anti_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 left
anti join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_complex_conditions """
+ select count(distinct t1.score) from count_with_distinct_t t1 join
count_with_distinct_t t2 on t1.id = t2.id and t1.name < t2.name group by
t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_with_aggregate """
+ select count(distinct t1.score), avg(t1.score) from
count_with_distinct_t t1 join count_with_distinct_t t2 on t1.id = t2.id group
by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_subquery """
+ select count(distinct t1.score) from (select * from
count_with_distinct_t where score > 10) t1 join count_with_distinct_t t2 on
t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_outer_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 left
join count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_deep_subquery """
+ select count(distinct t1.score) from (select * from (select * from
count_with_distinct_t) count_with_distinct_t where score > 10) t1 join
count_with_distinct_t t2 on t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_having """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id group by t1.name having
count(distinct t1.score) > 100;
+ """
+
+ order_qt_with_hint_groupby_pushdown_mixed_aggregates """
+ select count(distinct t1.score), sum(distinct t1.score) from
count_with_distinct_t t1 join count_with_distinct_t t2 on t1.id = t2.id group
by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_multi_table_join """
+ select count(distinct t1.score) from count_with_distinct_t t1 join
count_with_distinct_t t2 on t1.id = t2.id join count_with_distinct_t t3 on
t1.name = t3.name group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_with_order_by """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id group by t1.name order by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_multiple_equal_conditions """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id and t1.name = t2.name group by
t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_equal_conditions_with_aggregate """
+ select sum(distinct t1.score), count(distinct t2.score) from
count_with_distinct_t t1 join count_with_distinct_t t2 on t1.id = t2.id and
t1.name = t2.name group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_equal_conditions_non_aggregate """
+ select t1.name, count(distinct t1.score) from count_with_distinct_t
t1, count_with_distinct_t t2 where t1.id = t2.id and t1.name = t2.name group by
t1.name;
+ """
+
+
order_qt_with_hint_groupby_pushdown_equal_conditions_non_aggregate_with_aggregate
"""
+ select t1.name, count(distinct t1.score), count(distinct t2.score)
from count_with_distinct_t t1, count_with_distinct_t t2 where t1.id = t2.id and
t1.name = t2.name group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_with_where_clause """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id and t1.score > 50 group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_varied_aggregates """
+ select count(distinct t1.score), avg(t1.id), count(distinct t2.name)
from count_with_distinct_t t1 join count_with_distinct_t t2 on t1.id = t2.id
group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_with_order_by_limit """
+ select count(distinct t1.score) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id group by t1.name order by
count(distinct t1.score) limit 10;
+ """
+
+ order_qt_with_hint_groupby_pushdown_alias_multiple_equal_conditions """
+ select count(distinct t1_alias.score) from count_with_distinct_t
t1_alias join count_with_distinct_t t2_alias on t1_alias.id = t2_alias.id and
t1_alias.name = t2_alias.name group by t1_alias.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_complex_join_condition """
+ select count(distinct t1.score) from count_with_distinct_t t1 join
count_with_distinct_t t2 on t1.id = t2.id and t1.score = t2.score and t1.name
<> t2.name group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_function_processed_columns """
+ select count(distinct LENGTH(t1.name)) from count_with_distinct_t t1,
count_with_distinct_t t2 where t1.id = t2.id group by t1.name;
+ """
+
+ order_qt_with_hint_groupby_pushdown_nested_queries """
+ select count(distinct t1.score) from (select * from
count_with_distinct_t where score > 20) t1 join (select * from
count_with_distinct_t where id < 100) t2 on t1.id = t2.id group by t1.name;
+ """
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]