[fix](nereids) Fix use aggregate mv wrongly when rewrite query which only contains join (#30858)

the materialized view def is as following:
>            select 
>               o_orderdate, 
>               o_shippriority, 
>               o_comment, 
>               l_orderkey, 
>               o_orderkey, 
>               count(*) 
>             from 
>               orders 
>               left join lineitem on l_orderkey = o_orderkey
>               group by o_orderdate, 
>               o_shippriority, 
>               o_comment, 
>               l_orderkey;

the query should rewrite success by using above materialized view
>             select 
>               o_orderdate, 
>               o_shippriority, 
>               o_comment, 
>               l_orderkey, 
>               ps_partkey, 
>               count(*) 
>             from 
>               orders left 
>               join lineitem on l_orderkey = o_orderkey
>               left join partsupp on ps_partkey = l_orderkey
>               group by
>              o_orderdate, 
>              o_shippriority, 
>              o_comment, 
>              l_orderkey, 
>              ps_partkey;
This commit is contained in:
seawinde
2024-02-06 11:49:56 +08:00
committed by yiguolei
parent 2e4daa7006
commit 5c2a4a80dd
6 changed files with 138 additions and 20 deletions

View File

@ -18,6 +18,7 @@
package org.apache.doris.nereids.rules.exploration.mv;
import org.apache.doris.common.Pair;
import org.apache.doris.nereids.rules.exploration.mv.StructInfo.PlanCheckContext;
import org.apache.doris.nereids.rules.exploration.mv.StructInfo.PlanSplitContext;
import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
import org.apache.doris.nereids.trees.expressions.Alias;
@ -363,12 +364,16 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate
}
}
// Check Aggregate is simple or not and check join is whether valid or not.
// Support project, filter, join, logical relation node and join condition should only contain
// slot reference equals currently.
/**
* Check Aggregate is simple or not and check join is whether valid or not.
* Support project, filter, join, logical relation node and join condition should only contain
* slot reference equals currently.
*/
@Override
protected boolean checkPattern(StructInfo structInfo) {
return structInfo.getTopPlan().accept(StructInfo.PLAN_PATTERN_CHECKER, SUPPORTED_JOIN_TYPE_SET);
PlanCheckContext checkContext = PlanCheckContext.of(SUPPORTED_JOIN_TYPE_SET);
return structInfo.getTopPlan().accept(StructInfo.PLAN_PATTERN_CHECKER, checkContext)
&& checkContext.isContainsTopAggregate() && checkContext.getTopAggregateNum() <= 1;
}
/**

View File

@ -18,6 +18,7 @@
package org.apache.doris.nereids.rules.exploration.mv;
import org.apache.doris.common.Pair;
import org.apache.doris.nereids.rules.exploration.mv.StructInfo.PlanCheckContext;
import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.Expression;
@ -76,10 +77,13 @@ public abstract class AbstractMaterializedViewJoinRule extends AbstractMateriali
/**
* Check join is whether valid or not. Support join's input only support project, filter, join,
* logical relation node and join condition should be slot reference equals currently
* logical relation, simple aggregate node. Con not have aggregate above on join.
* Join condition should be slot reference equals currently.
*/
@Override
protected boolean checkPattern(StructInfo structInfo) {
return structInfo.getTopPlan().accept(StructInfo.PLAN_PATTERN_CHECKER, SUPPORTED_JOIN_TYPE_SET);
PlanCheckContext checkContext = PlanCheckContext.of(SUPPORTED_JOIN_TYPE_SET);
return structInfo.getTopPlan().accept(StructInfo.PLAN_PATTERN_CHECKER, checkContext)
&& !checkContext.isContainsTopAggregate();
}
}

View File

@ -446,39 +446,91 @@ public class StructInfo {
}
}
/**
* The context for plan check context, make sure that the plan in query and mv is valid or not
*/
public static class PlanCheckContext {
// the aggregate above join
private boolean containsTopAggregate = false;
private int topAggregateNum = 0;
private boolean alreadyMeetJoin = false;
private final Set<JoinType> supportJoinTypes;
public PlanCheckContext(Set<JoinType> supportJoinTypes) {
this.supportJoinTypes = supportJoinTypes;
}
public boolean isContainsTopAggregate() {
return containsTopAggregate;
}
public void setContainsTopAggregate(boolean containsTopAggregate) {
this.containsTopAggregate = containsTopAggregate;
}
public boolean isAlreadyMeetJoin() {
return alreadyMeetJoin;
}
public void setAlreadyMeetJoin(boolean alreadyMeetJoin) {
this.alreadyMeetJoin = alreadyMeetJoin;
}
public Set<JoinType> getSupportJoinTypes() {
return supportJoinTypes;
}
public int getTopAggregateNum() {
return topAggregateNum;
}
public void plusTopAggregateNum() {
this.topAggregateNum += 1;
}
public static PlanCheckContext of(Set<JoinType> supportJoinTypes) {
return new PlanCheckContext(supportJoinTypes);
}
}
/**
* PlanPatternChecker, this is used to check the plan pattern is valid or not
*/
public static class PlanPatternChecker extends DefaultPlanVisitor<Boolean, Set<JoinType>> {
public static class PlanPatternChecker extends DefaultPlanVisitor<Boolean, PlanCheckContext> {
@Override
public Boolean visitLogicalJoin(LogicalJoin<? extends Plan, ? extends Plan> join,
Set<JoinType> supportJoinTypes) {
if (!supportJoinTypes.contains(join.getJoinType())) {
PlanCheckContext checkContext) {
checkContext.setAlreadyMeetJoin(true);
if (!checkContext.getSupportJoinTypes().contains(join.getJoinType())) {
return false;
}
if (!join.getOtherJoinConjuncts().isEmpty()) {
return false;
}
return visit(join, supportJoinTypes);
return visit(join, checkContext);
}
@Override
public Boolean visitLogicalAggregate(LogicalAggregate<? extends Plan> aggregate,
Set<JoinType> supportJoinTypes) {
PlanCheckContext checkContext) {
if (!checkContext.isAlreadyMeetJoin()) {
checkContext.setContainsTopAggregate(true);
checkContext.plusTopAggregateNum();
}
if (aggregate.getSourceRepeat().isPresent()) {
return false;
}
return visit(aggregate, supportJoinTypes);
return visit(aggregate, checkContext);
}
@Override
public Boolean visitGroupPlan(GroupPlan groupPlan, Set<JoinType> supportJoinTypes) {
public Boolean visitGroupPlan(GroupPlan groupPlan, PlanCheckContext checkContext) {
return groupPlan.getGroup().getLogicalExpressions().stream()
.anyMatch(logicalExpression -> logicalExpression.getPlan().accept(this, supportJoinTypes));
.anyMatch(logicalExpression -> logicalExpression.getPlan().accept(this, checkContext));
}
@Override
public Boolean visit(Plan plan, Set<JoinType> supportJoinTypes) {
public Boolean visit(Plan plan, PlanCheckContext checkContext) {
if (plan instanceof Filter
|| plan instanceof Project
|| plan instanceof CatalogRelation
@ -486,14 +538,14 @@ public class StructInfo {
|| plan instanceof LogicalSort
|| plan instanceof LogicalAggregate
|| plan instanceof GroupPlan) {
return doVisit(plan, supportJoinTypes);
return doVisit(plan, checkContext);
}
return false;
}
private Boolean doVisit(Plan plan, Set<JoinType> supportJoinTypes) {
private Boolean doVisit(Plan plan, PlanCheckContext checkContext) {
for (Plan child : plan.children()) {
boolean valid = child.accept(this, supportJoinTypes);
boolean valid = child.accept(this, checkContext);
if (!valid) {
return false;
}