diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java index d7d46ecc15..09a0ca6984 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java @@ -30,6 +30,7 @@ import org.apache.doris.nereids.properties.LogicalProperties; import org.apache.doris.nereids.properties.PhysicalProperties; import org.apache.doris.nereids.properties.RequestPropertyDeriver; import org.apache.doris.nereids.properties.RequirePropertiesSupplier; +import org.apache.doris.nereids.rules.exploration.mv.AbstractMaterializedViewRule; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.plans.GroupPlan; import org.apache.doris.nereids.trees.plans.LeafPlan; @@ -53,6 +54,7 @@ import org.apache.logging.log4j.Logger; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -74,6 +76,10 @@ public class Memo { private static long stateId = 0; private final ConnectContext connectContext; private final AtomicLong refreshVersion = new AtomicLong(1); + private final Map, Set> materializationCheckSuccessMap = + new LinkedHashMap<>(); + private final Map, Set> materializationCheckFailMap = + new LinkedHashMap<>(); private final IdGenerator groupIdGenerator = GroupId.createGenerator(); private final Map groups = Maps.newLinkedHashMap(); // we could not use Set, because Set does not have get method. @@ -127,6 +133,46 @@ public class Memo { return refreshVersion.get(); } + /** + * Record materialization check result for performance + */ + public void recordMaterializationCheckResult(Class target, + Long checkedMaterializationId, boolean isSuccess) { + if (isSuccess) { + Set checkedSet = materializationCheckSuccessMap.get(target); + if (checkedSet == null) { + checkedSet = new HashSet<>(); + materializationCheckSuccessMap.put(target, checkedSet); + } + checkedSet.add(checkedMaterializationId); + } else { + Set checkResultSet = materializationCheckFailMap.get(target); + if (checkResultSet == null) { + checkResultSet = new HashSet<>(); + materializationCheckFailMap.put(target, checkResultSet); + } + checkResultSet.add(checkedMaterializationId); + } + } + + /** + * Get the info for materialization context is checked + * + * @return if true, check successfully, if false check fail, if null not checked + */ + public Boolean materializationHasChecked(Class target, + long materializationId) { + Set checkSuccessSet = materializationCheckSuccessMap.get(target); + if (checkSuccessSet != null && checkSuccessSet.contains(materializationId)) { + return true; + } + Set checkFailSet = materializationCheckFailMap.get(target); + if (checkFailSet != null && checkFailSet.contains(materializationId)) { + return false; + } + return null; + } + private Plan skipProject(Plan plan, Group targetGroup) { // Some top project can't be eliminated if (plan instanceof LogicalProject && ((LogicalProject) plan).canEliminate()) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java index dcff9db69d..41157a6c86 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java @@ -108,11 +108,7 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac continue; } // check mv plan is valid or not - boolean valid = checkPattern(context.getStructInfo()) && context.getStructInfo().isValid(); - if (!valid) { - context.recordFailReason(context.getStructInfo(), - "View struct info is invalid", () -> String.format(", view plan is %s", - context.getStructInfo().getOriginalPlan().treeString())); + if (!isMaterializationValid(cascadesContext, context)) { continue; } // get query struct infos according to the view strut info, if valid query struct infos is empty, bail out @@ -668,6 +664,40 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac && context.alreadyRewrite(plan.getGroupExpression().get().getOwnerGroup().getGroupId()); } + // check mv plan is valid or not, this can use cache for performance + private boolean isMaterializationValid(CascadesContext cascadesContext, MaterializationContext context) { + long materializationId = context.getMaterializationQualifier().hashCode(); + Boolean cachedCheckResult = cascadesContext.getMemo().materializationHasChecked(this.getClass(), + materializationId); + if (cachedCheckResult == null) { + // need check in real time + boolean checkResult = checkPattern(context.getStructInfo()); + if (!checkResult) { + context.recordFailReason(context.getStructInfo(), + "View struct info is invalid", () -> String.format("view plan is %s", + context.getStructInfo().getOriginalPlan().treeString())); + cascadesContext.getMemo().recordMaterializationCheckResult(this.getClass(), materializationId, + false); + return false; + } else { + cascadesContext.getMemo().recordMaterializationCheckResult(this.getClass(), + materializationId, true); + } + } else if (!cachedCheckResult) { + context.recordFailReason(context.getStructInfo(), + "View struct info is invalid", () -> String.format("view plan is %s", + context.getStructInfo().getOriginalPlan().treeString())); + return false; + } + if (!context.getStructInfo().isValid()) { + context.recordFailReason(context.getStructInfo(), + "View struct info is invalid", () -> String.format("view plan is %s", + context.getStructInfo().getOriginalPlan().treeString())); + return false; + } + return true; + } + /** * Query and mv match node */