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 82d7f28bffd [opt](mtmv) ensure rewritten plan output order correct even project been eliminated (#31870) 82d7f28bffd is described below commit 82d7f28bffdde22635704c404c5c59898d7e29fc Author: morrySnow <101034200+morrys...@users.noreply.github.com> AuthorDate: Fri Mar 8 10:54:57 2024 +0800 [opt](mtmv) ensure rewritten plan output order correct even project been eliminated (#31870) --- .../main/java/org/apache/doris/mtmv/MTMVCache.java | 27 ++++------- .../mv/AbstractMaterializedViewRule.java | 52 +++++++++++----------- .../mv/InitMaterializationContextHook.java | 2 +- .../exploration/mv/MaterializationContext.java | 19 +++----- 4 files changed, 43 insertions(+), 57 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVCache.java b/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVCache.java index ec64f8f5f2b..94076563ee1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVCache.java +++ b/fe/fe-core/src/main/java/org/apache/doris/mtmv/MTMVCache.java @@ -22,7 +22,6 @@ import org.apache.doris.nereids.NereidsPlanner; import org.apache.doris.nereids.StatementContext; import org.apache.doris.nereids.parser.NereidsParser; import org.apache.doris.nereids.properties.PhysicalProperties; -import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel; import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; @@ -30,9 +29,6 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalResultSink; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.OriginStatement; -import java.util.List; -import java.util.stream.Collectors; - /** * The cache for materialized view cache */ @@ -40,20 +36,20 @@ public class MTMVCache { // the materialized view plan which should be optimized by the same rules to query private final Plan logicalPlan; - // this should be shuttle expression with lineage - private final List<NamedExpression> mvOutputExpressions; + // for stable output order, we should use original plan + private final Plan originalPlan; - public MTMVCache(Plan logicalPlan, List<NamedExpression> mvOutputExpressions) { + public MTMVCache(Plan logicalPlan, Plan originalPlan) { this.logicalPlan = logicalPlan; - this.mvOutputExpressions = mvOutputExpressions; + this.originalPlan = originalPlan; } public Plan getLogicalPlan() { return logicalPlan; } - public List<NamedExpression> getMvOutputExpressions() { - return mvOutputExpressions; + public Plan getOriginalPlan() { + return originalPlan; } public static MTMVCache from(MTMV mtmv, ConnectContext connectContext) { @@ -66,15 +62,10 @@ public class MTMVCache { if (mvSqlStatementContext.getConnectContext().getStatementContext() == null) { mvSqlStatementContext.getConnectContext().setStatementContext(mvSqlStatementContext); } - Plan mvRewrittenPlan = - planner.plan(unboundMvPlan, PhysicalProperties.ANY, ExplainLevel.REWRITTEN_PLAN); + Plan mvRewrittenPlan = planner.plan(unboundMvPlan, PhysicalProperties.ANY, ExplainLevel.REWRITTEN_PLAN); + // TODO: should use visitor or a new rule to remove result sink node Plan mvPlan = mvRewrittenPlan instanceof LogicalResultSink ? (Plan) ((LogicalResultSink) mvRewrittenPlan).child() : mvRewrittenPlan; - // use rewritten plan output expression currently, if expression rewrite fail, - // consider to use the analyzed plan for output expressions only - List<NamedExpression> mvOutputExpressions = mvPlan.getOutput().stream() - .map(NamedExpression.class::cast) - .collect(Collectors.toList()); - return new MTMVCache(mvPlan, mvOutputExpressions); + return new MTMVCache(mvPlan, mvRewrittenPlan); } } 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 a11b9101045..e001359574e 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 @@ -35,6 +35,7 @@ import org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionMapping; import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping; 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.ExprId; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.Not; @@ -55,6 +56,7 @@ import org.apache.doris.nereids.util.TypeUtils; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import java.util.ArrayList; @@ -259,27 +261,32 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac * Rewrite by rules and try to make output is the same after optimize by rules */ protected Plan rewriteByRules(CascadesContext cascadesContext, Plan rewrittenPlan, Plan originPlan) { + List<Slot> originOutputs = originPlan.getOutput(); + if (originOutputs.size() != rewrittenPlan.getOutput().size()) { + return null; + } + Map<Slot, ExprId> originSlotToRewrittenExprId = Maps.newHashMap(); + for (int i = 0; i < originOutputs.size(); i++) { + originSlotToRewrittenExprId.put(originOutputs.get(i), rewrittenPlan.getOutput().get(i).getExprId()); + } // run rbo job on mv rewritten plan CascadesContext rewrittenPlanContext = CascadesContext.initContext( cascadesContext.getStatementContext(), rewrittenPlan, cascadesContext.getCurrentJobContext().getRequiredProperties()); Rewriter.getWholeTreeRewriter(rewrittenPlanContext).execute(); rewrittenPlan = rewrittenPlanContext.getRewritePlan(); - List<Slot> originPlanOutput = originPlan.getOutput(); - List<Slot> rewrittenPlanOutput = rewrittenPlan.getOutput(); - if (originPlanOutput.size() != rewrittenPlanOutput.size()) { - return null; - } - List<NamedExpression> expressions = new ArrayList<>(); - // should add project above rewritten plan if top plan is not project, if aggregate above will nu - if (!isOutputValid(originPlan, rewrittenPlan)) { - for (int i = 0; i < originPlanOutput.size(); i++) { - expressions.add(((NamedExpression) normalizeExpression(originPlanOutput.get(i), - rewrittenPlanOutput.get(i)))); - } - return new LogicalProject<>(expressions, rewrittenPlan, false); + + // for get right nullable after rewritten, we need this map + Map<ExprId, Slot> exprIdToNewRewrittenSlot = Maps.newHashMap(); + for (Slot slot : rewrittenPlan.getOutput()) { + exprIdToNewRewrittenSlot.put(slot.getExprId(), slot); } - return rewrittenPlan; + + // normalize nullable + ImmutableList<NamedExpression> convertNullable = originOutputs.stream() + .map(s -> normalizeExpression(s, exprIdToNewRewrittenSlot.get(originSlotToRewrittenExprId.get(s)))) + .collect(ImmutableList.toImmutableList()); + return new LogicalProject<>(convertNullable, rewrittenPlan); } /** @@ -398,22 +405,17 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac * Normalize expression with query, keep the consistency of exprId and nullable props with * query */ - protected Expression normalizeExpression(Expression sourceExpression, Expression replacedExpression) { - if (sourceExpression instanceof NamedExpression - && replacedExpression.nullable() != sourceExpression.nullable()) { + private NamedExpression normalizeExpression( + NamedExpression sourceExpression, NamedExpression replacedExpression) { + Expression innerExpression = replacedExpression; + if (replacedExpression.nullable() != sourceExpression.nullable()) { // if enable join eliminate, query maybe inner join and mv maybe outer join. // If the slot is at null generate side, the nullable maybe different between query and view // So need to force to consistent. - replacedExpression = sourceExpression.nullable() + innerExpression = sourceExpression.nullable() ? new Nullable(replacedExpression) : new NonNullable(replacedExpression); } - if (sourceExpression instanceof NamedExpression - && !sourceExpression.equals(replacedExpression)) { - NamedExpression sourceNamedExpression = (NamedExpression) sourceExpression; - replacedExpression = new Alias(sourceNamedExpression.getExprId(), replacedExpression, - sourceNamedExpression.getName()); - } - return replacedExpression; + return new Alias(sourceExpression.getExprId(), innerExpression, sourceExpression.getName()); } /** diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/InitMaterializationContextHook.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/InitMaterializationContextHook.java index 8046639d1ba..ab8f4d69d45 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/InitMaterializationContextHook.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/InitMaterializationContextHook.java @@ -95,7 +95,7 @@ public class InitMaterializationContextHook implements PlannerHook { // todo should force keep consistency to mv sql plan output Plan projectScan = new LogicalProject<Plan>(mvProjects, mvScan); cascadesContext.addMaterializationContext( - MaterializationContext.fromMaterializedView(materializedView, projectScan, cascadesContext)); + MaterializationContext.fromMaterializedView(materializedView, projectScan)); }); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializationContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializationContext.java index 0414f7007f3..e64e1290954 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializationContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializationContext.java @@ -22,7 +22,6 @@ import org.apache.doris.catalog.Table; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.Pair; import org.apache.doris.mtmv.MTMVCache; -import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.memo.GroupId; import org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionMapping; import org.apache.doris.nereids.trees.plans.ObjectId; @@ -47,7 +46,8 @@ import java.util.stream.Collectors; public class MaterializationContext { private static final Logger LOG = LogManager.getLogger(MaterializationContext.class); - private MTMV mtmv; + + private final MTMV mtmv; // Should use stmt id generator in query context private final Plan mvScanPlan; private final List<Table> baseTables; @@ -68,11 +68,7 @@ public class MaterializationContext { /** * MaterializationContext, this contains necessary info for query rewriting by mv */ - public MaterializationContext(MTMV mtmv, - Plan mvScanPlan, - CascadesContext cascadesContext, - List<Table> baseTables, - List<Table> baseViews) { + public MaterializationContext(MTMV mtmv, Plan mvScanPlan, List<Table> baseTables, List<Table> baseViews) { this.mtmv = mtmv; this.mvScanPlan = mvScanPlan; this.baseTables = baseTables; @@ -91,8 +87,8 @@ public class MaterializationContext { // mv output expression shuttle, this will be used to expression rewrite this.mvExprToMvScanExprMapping = ExpressionMapping.generate( ExpressionUtils.shuttleExpressionWithLineage( - mtmvCache.getMvOutputExpressions(), - mtmvCache.getLogicalPlan()), + mtmvCache.getOriginalPlan().getOutput(), + mtmvCache.getOriginalPlan()), mvScanPlan.getExpressions()); // copy the plan from cache, which the plan in cache may change this.mvPlan = mtmvCache.getLogicalPlan(); @@ -229,13 +225,10 @@ public class MaterializationContext { /** * MaterializationContext fromMaterializedView */ - public static MaterializationContext fromMaterializedView(MTMV materializedView, - Plan mvScanPlan, - CascadesContext cascadesContext) { + public static MaterializationContext fromMaterializedView(MTMV materializedView, Plan mvScanPlan) { return new MaterializationContext( materializedView, mvScanPlan, - cascadesContext, ImmutableList.of(), ImmutableList.of()); } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org