[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:
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user