This is an automated email from the ASF dual-hosted git repository. yiguolei pushed a commit to branch branch-2.1 in repository https://gitbox.apache.org/repos/asf/doris.git
commit 8599e8ee6491221713c4445e72f9a27dbf8cd8bf Author: seawinde <149132972+seawi...@users.noreply.github.com> AuthorDate: Tue May 28 11:11:56 2024 +0800 [improvement](mtmv) Add id to statistics map in statement context for cost estimation later (#35436) Add id to statistics map in statement context for cost estimation later this helps to improve the probability to use materialized view when query a single table with aggregate and many filter --- .../main/java/org/apache/doris/mtmv/MTMVCache.java | 14 ++- .../org/apache/doris/nereids/StatementContext.java | 25 +++++ .../mv/AbstractMaterializedViewAggregateRule.java | 6 +- .../mv/AbstractMaterializedViewJoinRule.java | 4 +- .../mv/AbstractMaterializedViewRule.java | 17 ++- .../mv/AsyncMaterializationContext.java | 39 +++++-- .../exploration/mv/MaterializationContext.java | 117 ++++++++++++--------- .../exploration/mv/MaterializedViewScanRule.java | 4 +- .../IdStatisticsMapTest.java} | 22 ++-- .../nereids/{memo => mv}/MvTableIdIsLongTest.java | 2 +- 10 files changed, 171 insertions(+), 79 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 8bd87e2e149..c65125de2f2 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 @@ -34,6 +34,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalResultSink; import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.OriginStatement; +import org.apache.doris.statistics.Statistics; import com.google.common.collect.ImmutableList; @@ -47,10 +48,12 @@ public class MTMVCache { private final Plan logicalPlan; // The original plan of mv def sql private final Plan originalPlan; + private final Statistics statistics; - public MTMVCache(Plan logicalPlan, Plan originalPlan) { + public MTMVCache(Plan logicalPlan, Plan originalPlan, Statistics statistics) { this.logicalPlan = logicalPlan; this.originalPlan = originalPlan; + this.statistics = statistics; } public Plan getLogicalPlan() { @@ -61,6 +64,10 @@ public class MTMVCache { return originalPlan; } + public Statistics getStatistics() { + return statistics; + } + public static MTMVCache from(MTMV mtmv, ConnectContext connectContext) { LogicalPlan unboundMvPlan = new NereidsParser().parseSingle(mtmv.getQuerySql()); StatementContext mvSqlStatementContext = new StatementContext(connectContext, @@ -71,7 +78,8 @@ public class MTMVCache { } // Can not convert to table sink, because use the same column from different table when self join // the out slot is wrong - Plan originPlan = planner.plan(unboundMvPlan, PhysicalProperties.ANY, ExplainLevel.REWRITTEN_PLAN); + planner.plan(unboundMvPlan, PhysicalProperties.ANY, ExplainLevel.ALL_PLAN); + Plan originPlan = planner.getCascadesContext().getRewritePlan(); // Eliminate result sink because sink operator is useless in query rewrite by materialized view // and the top sort can also be removed Plan mvPlan = originPlan.accept(new DefaultPlanRewriter<Object>() { @@ -88,6 +96,6 @@ public class MTMVCache { ImmutableList.of(Rewriter.custom(RuleType.ELIMINATE_SORT, EliminateSort::new))).execute(); return childContext.getRewritePlan(); }, mvPlan, originPlan); - return new MTMVCache(mvPlan, originPlan); + return new MTMVCache(mvPlan, originPlan, planner.getCascadesContext().getMemo().getRoot().getStatistics()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java index 6e4b3c23fc0..f4a32d723a6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java @@ -20,6 +20,7 @@ package org.apache.doris.nereids; import org.apache.doris.analysis.StatementBase; import org.apache.doris.catalog.TableIf; import org.apache.doris.catalog.constraint.TableIdentifier; +import org.apache.doris.common.Id; import org.apache.doris.common.IdGenerator; import org.apache.doris.common.Pair; import org.apache.doris.nereids.hint.Hint; @@ -41,7 +42,9 @@ import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.OriginStatement; import org.apache.doris.qe.SessionVariable; import org.apache.doris.qe.cache.CacheAnalyzer; +import org.apache.doris.statistics.Statistics; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; import com.google.common.base.Supplier; import com.google.common.base.Suppliers; @@ -146,6 +149,10 @@ public class StatementContext implements Closeable { // Record table id mapping, the key is the hash code of union catalogId, databaseId, tableId // the value is the auto-increment id in the cascades context private final Map<TableIdentifier, TableId> tableIdMapping = new LinkedHashMap<>(); + // Record the materialization statistics by id which is used for cost estimation. + // Maybe return null, which means the id according statistics should calc normally rather than getting + // form this map + private final Map<RelationId, Statistics> relationIdToStatisticsMap = new LinkedHashMap<>(); public StatementContext() { this(ConnectContext.get(), null, 0); @@ -412,6 +419,24 @@ public class StatementContext implements Closeable { indexInSqlToString.put(pair, replacement); } + public void addStatistics(Id id, Statistics statistics) { + if (id instanceof RelationId) { + this.relationIdToStatisticsMap.put((RelationId) id, statistics); + } + } + + public Optional<Statistics> getStatistics(Id id) { + if (id instanceof RelationId) { + return Optional.ofNullable(this.relationIdToStatisticsMap.get((RelationId) id)); + } + return Optional.empty(); + } + + @VisibleForTesting + public Map<RelationId, Statistics> getRelationIdToStatisticsMap() { + return relationIdToStatisticsMap; + } + /** addTableReadLock */ public synchronized void addTableReadLock(TableIf tableIf) { if (!tableIf.needReadLockWhenPlan()) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewAggregateRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewAggregateRule.java index b37b04d8022..1e013498a12 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewAggregateRule.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewAggregateRule.java @@ -97,7 +97,7 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate viewStructInfo)) { List<Expression> rewrittenQueryExpressions = rewriteExpression(queryTopPlan.getOutput(), queryTopPlan, - materializationContext.getMvExprToMvScanExprMapping(), + materializationContext.getExprToScanExprMapping(), viewToQuerySlotMapping, true, queryStructInfo.getTableBitSet()); @@ -121,7 +121,7 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate () -> String.format("expressionToWrite = %s,\n mvExprToMvScanExprMapping = %s,\n" + "viewToQuerySlotMapping = %s", queryTopPlan.getOutput(), - materializationContext.getMvExprToMvScanExprMapping(), + materializationContext.getExprToScanExprMapping(), viewToQuerySlotMapping)); } // if view is scalar aggregate but query is not. Or if query is scalar aggregate but view is not @@ -150,7 +150,7 @@ public abstract class AbstractMaterializedViewAggregateRule extends AbstractMate List<? extends Expression> queryExpressions = queryTopPlan.getOutput(); // permute the mv expr mapping to query based Map<Expression, Expression> mvExprToMvScanExprQueryBased = - materializationContext.getMvExprToMvScanExprMapping().keyPermute(viewToQuerySlotMapping) + materializationContext.getExprToScanExprMapping().keyPermute(viewToQuerySlotMapping) .flattenMap().get(0); for (Expression topExpression : queryExpressions) { // if agg function, try to roll up and rewrite diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java index 3b20cefbba8..4f95c248eca 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java @@ -46,7 +46,7 @@ public abstract class AbstractMaterializedViewJoinRule extends AbstractMateriali List<Expression> expressionsRewritten = rewriteExpression( queryStructInfo.getExpressions(), queryStructInfo.getTopPlan(), - materializationContext.getMvExprToMvScanExprMapping(), + materializationContext.getExprToScanExprMapping(), targetToSourceMapping, true, queryStructInfo.getTableBitSet() @@ -57,7 +57,7 @@ public abstract class AbstractMaterializedViewJoinRule extends AbstractMateriali "Rewrite expressions by view in join fail", () -> String.format("expressionToRewritten is %s,\n mvExprToMvScanExprMapping is %s,\n" + "targetToSourceMapping = %s", queryStructInfo.getExpressions(), - materializationContext.getMvExprToMvScanExprMapping(), + materializationContext.getExprToScanExprMapping(), targetToSourceMapping)); return null; } 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 8442d2485c7..ea057e8bfcc 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 @@ -24,6 +24,7 @@ import org.apache.doris.catalog.PartitionInfo; import org.apache.doris.catalog.PartitionItem; import org.apache.doris.catalog.PartitionType; import org.apache.doris.catalog.TableIf; +import org.apache.doris.common.Id; import org.apache.doris.common.Pair; import org.apache.doris.mtmv.BaseTableInfo; import org.apache.doris.mtmv.MTMVPartitionInfo; @@ -59,6 +60,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalProject; import org.apache.doris.nereids.trees.plans.logical.LogicalUnion; import org.apache.doris.nereids.util.ExpressionUtils; import org.apache.doris.nereids.util.TypeUtils; +import org.apache.doris.statistics.Statistics; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMultimap; @@ -77,6 +79,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -226,21 +229,21 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac continue; } Plan rewrittenPlan; - Plan mvScan = materializationContext.getMvScanPlan(); + Plan mvScan = materializationContext.getScanPlan(); Plan queryPlan = queryStructInfo.getTopPlan(); if (compensatePredicates.isAlwaysTrue()) { rewrittenPlan = mvScan; } else { // Try to rewrite compensate predicates by using mv scan List<Expression> rewriteCompensatePredicates = rewriteExpression(compensatePredicates.toList(), - queryPlan, materializationContext.getMvExprToMvScanExprMapping(), + queryPlan, materializationContext.getExprToScanExprMapping(), viewToQuerySlotMapping, true, queryStructInfo.getTableBitSet()); if (rewriteCompensatePredicates.isEmpty()) { materializationContext.recordFailReason(queryStructInfo, "Rewrite compensate predicate by view fail", () -> String.format("compensatePredicates = %s,\n mvExprToMvScanExprMapping = %s,\n" + "viewToQuerySlotMapping = %s", - compensatePredicates, materializationContext.getMvExprToMvScanExprMapping(), + compensatePredicates, materializationContext.getExprToScanExprMapping(), viewToQuerySlotMapping)); continue; } @@ -334,9 +337,15 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac continue; } recordIfRewritten(queryStructInfo.getOriginalPlan(), materializationContext); + Optional<Pair<Id, Statistics>> materializationPlanStatistics = + materializationContext.getPlanStatistics(cascadesContext); + if (materializationPlanStatistics.isPresent() && materializationPlanStatistics.get().key() != null) { + cascadesContext.getStatementContext().addStatistics( + materializationPlanStatistics.get().key(), materializationPlanStatistics.get().value()); + } rewriteResults.add(rewrittenPlan); // if rewrite successfully, try to regenerate mv scan because it maybe used again - materializationContext.tryReGenerateMvScanPlan(cascadesContext); + materializationContext.tryReGenerateScanPlan(cascadesContext); } return rewriteResults; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java index 1c0d854dd90..f1c4372f104 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java @@ -19,14 +19,20 @@ package org.apache.doris.nereids.rules.exploration.mv; import org.apache.doris.catalog.MTMV; import org.apache.doris.catalog.Table; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Id; import org.apache.doris.common.Pair; +import org.apache.doris.mtmv.MTMVCache; import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionMapping; import org.apache.doris.nereids.trees.plans.ObjectId; import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.RelationId; import org.apache.doris.nereids.trees.plans.algebra.Relation; +import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan; import org.apache.doris.nereids.trees.plans.physical.PhysicalCatalogRelation; import org.apache.doris.nereids.util.Utils; +import org.apache.doris.statistics.Statistics; import com.google.common.collect.Multimap; import org.apache.logging.log4j.LogManager; @@ -35,6 +41,7 @@ import org.apache.logging.log4j.Logger; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Optional; /** * Async context for query rewrite by materialized view @@ -58,7 +65,7 @@ public class AsyncMaterializationContext extends MaterializationContext { } @Override - Plan doGenerateMvPlan(CascadesContext cascadesContext) { + Plan doGenerateScanPlan(CascadesContext cascadesContext) { return MaterializedViewUtils.generateMvScanPlan(this.mtmv, cascadesContext); } @@ -85,6 +92,24 @@ public class AsyncMaterializationContext extends MaterializationContext { "failReason", failReasonBuilder.toString()); } + @Override + public Optional<Pair<Id, Statistics>> getPlanStatistics(CascadesContext cascadesContext) { + MTMVCache mtmvCache; + try { + mtmvCache = mtmv.getOrGenerateCache(cascadesContext.getConnectContext()); + } catch (AnalysisException e) { + LOG.warn(String.format("get mv plan statistics fail, materialization qualifier is %s", + getMaterializationQualifier()), e); + return Optional.empty(); + } + RelationId relationId = null; + List<Object> scanObjs = this.getPlan().collectFirst(plan -> plan instanceof LogicalOlapScan); + if (scanObjs != null && !scanObjs.isEmpty()) { + relationId = ((LogicalOlapScan) scanObjs.get(0)).getRelationId(); + } + return Optional.of(Pair.of(relationId, mtmvCache.getStatistics())); + } + @Override boolean isFinalChosen(Relation relation) { if (!(relation instanceof PhysicalCatalogRelation)) { @@ -93,8 +118,8 @@ public class AsyncMaterializationContext extends MaterializationContext { return ((PhysicalCatalogRelation) relation).getTable() instanceof MTMV; } - public Plan getMvScanPlan() { - return mvScanPlan; + public Plan getScanPlan() { + return scanPlan; } public List<Table> getBaseTables() { @@ -105,16 +130,16 @@ public class AsyncMaterializationContext extends MaterializationContext { return baseViews; } - public ExpressionMapping getMvExprToMvScanExprMapping() { - return mvExprToMvScanExprMapping; + public ExpressionMapping getExprToScanExprMapping() { + return exprToScanExprMapping; } public boolean isAvailable() { return available; } - public Plan getMvPlan() { - return mvPlan; + public Plan getPlan() { + return plan; } public Multimap<ObjectId, Pair<String, String>> getFailReason() { 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 13e8c4456c0..d67390476eb 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 @@ -19,6 +19,7 @@ package org.apache.doris.nereids.rules.exploration.mv; import org.apache.doris.analysis.StatementBase; import org.apache.doris.catalog.Table; +import org.apache.doris.common.Id; import org.apache.doris.common.Pair; import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.memo.GroupId; @@ -34,6 +35,7 @@ import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan; import org.apache.doris.nereids.trees.plans.physical.PhysicalRelation; import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor; import org.apache.doris.nereids.util.ExpressionUtils; +import org.apache.doris.statistics.Statistics; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableSet; @@ -47,6 +49,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -56,69 +59,75 @@ import java.util.stream.Collectors; */ public abstract class MaterializationContext { private static final Logger LOG = LogManager.getLogger(MaterializationContext.class); - public final Map<RelationMapping, SlotMapping> queryToMvSlotMappingCache = new HashMap<>(); + public final Map<RelationMapping, SlotMapping> queryToMaterializationSlotMappingCache = new HashMap<>(); protected List<Table> baseTables; protected List<Table> baseViews; - // The plan of mv def sql - protected Plan mvPlan; - // The original plan of mv def sql - protected Plan originalMvPlan; + // The plan of materialization def sql + protected Plan plan; + // The original plan of materialization sql + protected Plan originalPlan; // Should regenerate when materialization is already rewritten successfully because one query may hit repeatedly // make sure output is different in multi using - protected Plan mvScanPlan; - // The mvPlan output shuttled expression, this is used by generate field mvExprToMvScanExprMapping - protected List<? extends Expression> mvPlanOutputShuttledExpressions; - // Generated mapping from mv plan out shuttled expr to mv scan plan out slot mapping, this is used for later used - protected ExpressionMapping mvExprToMvScanExprMapping; + protected Plan scanPlan; + // The materialization plan output shuttled expression, this is used by generate field + // exprToScanExprMapping + protected List<? extends Expression> planOutputShuttledExpressions; + // Generated mapping from materialization plan out shuttled expr to materialization scan plan out slot mapping, + // this is used for later used + protected ExpressionMapping exprToScanExprMapping; // This mark the materialization context is available or not, // will not be used in query transparent rewritten if false protected boolean available = true; - // Mark the mv plan in the context is already rewritten successfully or not + // Mark the materialization plan in the context is already rewritten successfully or not protected boolean success = false; // Mark enable record failure detail info or not, because record failure detail info is performance-depleting protected final boolean enableRecordFailureDetail; - // The mv plan struct info + // The materialization plan struct info protected final StructInfo structInfo; - // Group id set that are rewritten unsuccessfully by this mv for reducing rewrite times + // Group id set that are rewritten unsuccessfully by this materialization for reducing rewrite times protected final Set<GroupId> matchedFailGroups = new HashSet<>(); - // Group id set that are rewritten successfully by this mv for reducing rewrite times + // Group id set that are rewritten successfully by this materialization for reducing rewrite times protected final Set<GroupId> matchedSuccessGroups = new HashSet<>(); - // Record the reason, if rewrite by mv fail. The failReason should be empty if success. + // Record the reason, if rewrite by materialization fail. The failReason should be empty if success. // The key is the query belonged group expression objectId, the value is the fail reasons because // for one materialization query may be multi when nested materialized view. protected final Multimap<ObjectId, Pair<String, String>> failReason = HashMultimap.create(); /** - * MaterializationContext, this contains necessary info for query rewriting by mv + * MaterializationContext, this contains necessary info for query rewriting by materialization */ - public MaterializationContext(Plan mvPlan, Plan originalMvPlan, Plan mvScanPlan, CascadesContext cascadesContext) { - this.mvPlan = mvPlan; - this.originalMvPlan = originalMvPlan; - this.mvScanPlan = mvScanPlan; + public MaterializationContext(Plan plan, Plan originalPlan, + Plan scanPlan, CascadesContext cascadesContext) { + this.plan = plan; + this.originalPlan = originalPlan; + this.scanPlan = scanPlan; StatementBase parsedStatement = cascadesContext.getStatementContext().getParsedStatement(); this.enableRecordFailureDetail = parsedStatement != null && parsedStatement.isExplain() && ExplainLevel.MEMO_PLAN == parsedStatement.getExplainOptions().getExplainLevel(); - this.mvPlanOutputShuttledExpressions = ExpressionUtils.shuttleExpressionWithLineage( - originalMvPlan.getOutput(), - originalMvPlan, + this.planOutputShuttledExpressions = ExpressionUtils.shuttleExpressionWithLineage( + originalPlan.getOutput(), + originalPlan, new BitSet()); - // mv output expression shuttle, this will be used to expression rewrite - this.mvExprToMvScanExprMapping = ExpressionMapping.generate(this.mvPlanOutputShuttledExpressions, - this.mvScanPlan.getOutput()); - // Construct mv struct info, catch exception which may cause planner roll back + // materialization output expression shuttle, this will be used to expression rewrite + this.exprToScanExprMapping = ExpressionMapping.generate( + this.planOutputShuttledExpressions, + this.scanPlan.getOutput()); + // Construct materialization struct info, catch exception which may cause planner roll back List<StructInfo> viewStructInfos; try { - viewStructInfos = MaterializedViewUtils.extractStructInfo(mvPlan, cascadesContext, new BitSet()); + viewStructInfos = MaterializedViewUtils.extractStructInfo(plan, cascadesContext, new BitSet()); if (viewStructInfos.size() > 1) { // view struct info should only have one, log error and use the first struct info - LOG.warn(String.format("view strut info is more than one, mv scan plan is %s, mv plan is %s", - mvScanPlan.treeString(), mvPlan.treeString())); + LOG.warn(String.format("view strut info is more than one, materialization scan plan is %s, " + + "materialization plan is %s", + scanPlan.treeString(), plan.treeString())); } } catch (Exception exception) { - LOG.warn(String.format("construct mv struct info fail, mv scan plan is %s, mv plan is %s", - mvScanPlan.treeString(), mvPlan.treeString()), exception); + LOG.warn(String.format("construct materialization struct info fail, materialization scan plan is %s, " + + "materialization plan is %s", + scanPlan.treeString(), plan.treeString()), exception); this.available = false; this.structInfo = null; return; @@ -141,32 +150,33 @@ public abstract class MaterializationContext { /** * Try to generate scan plan for materialization * if MaterializationContext is already rewritten successfully, then should generate new scan plan in later - * query rewrite, because one plan may hit the materialized view repeatedly and the mv scan output + * query rewrite, because one plan may hit the materialized view repeatedly and the materialization scan output * should be different. * This method should be called when query rewrite successfully */ - public void tryReGenerateMvScanPlan(CascadesContext cascadesContext) { - this.mvScanPlan = doGenerateMvPlan(cascadesContext); - // mv output expression shuttle, this will be used to expression rewrite - this.mvExprToMvScanExprMapping = ExpressionMapping.generate(this.mvPlanOutputShuttledExpressions, - this.mvScanPlan.getOutput()); + public void tryReGenerateScanPlan(CascadesContext cascadesContext) { + this.scanPlan = doGenerateScanPlan(cascadesContext); + // materialization output expression shuttle, this will be used to expression rewrite + this.exprToScanExprMapping = ExpressionMapping.generate( + this.planOutputShuttledExpressions, + this.scanPlan.getOutput()); } public void addSlotMappingToCache(RelationMapping relationMapping, SlotMapping slotMapping) { - queryToMvSlotMappingCache.put(relationMapping, slotMapping); + queryToMaterializationSlotMappingCache.put(relationMapping, slotMapping); } public SlotMapping getSlotMappingFromCache(RelationMapping relationMapping) { - return queryToMvSlotMappingCache.get(relationMapping); + return queryToMaterializationSlotMappingCache.get(relationMapping); } /** * Try to generate scan plan for materialization * if MaterializationContext is already rewritten successfully, then should generate new scan plan in later - * query rewrite, because one plan may hit the materialized view repeatedly and the mv scan output + * query rewrite, because one plan may hit the materialized view repeatedly and the materialization scan output * should be different */ - abstract Plan doGenerateMvPlan(CascadesContext cascadesContext); + abstract Plan doGenerateScanPlan(CascadesContext cascadesContext); /** * Get materialization unique qualifier which identify it @@ -178,21 +188,28 @@ public abstract class MaterializationContext { */ abstract String getStringInfo(); + /** + * Get materialization plan statistics, the key is the identifier of statistics + * the value is Statistics. + * the statistics is used by cost estimation when the materialization is used + */ + abstract Optional<Pair<Id, Statistics>> getPlanStatistics(CascadesContext cascadesContext); + /** * Calc the relation is chosen finally or not */ abstract boolean isFinalChosen(Relation relation); - public Plan getMvPlan() { - return mvPlan; + public Plan getPlan() { + return plan; } - public Plan getOriginalMvPlan() { - return originalMvPlan; + public Plan getOriginalPlan() { + return originalPlan; } - public Plan getMvScanPlan() { - return mvScanPlan; + public Plan getScanPlan() { + return scanPlan; } public List<Table> getBaseTables() { @@ -203,8 +220,8 @@ public abstract class MaterializationContext { return baseViews; } - public ExpressionMapping getMvExprToMvScanExprMapping() { - return mvExprToMvScanExprMapping; + public ExpressionMapping getExprToScanExprMapping() { + return exprToScanExprMapping; } public boolean isAvailable() { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewScanRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewScanRule.java index d6d7817d35f..82e7944a81e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewScanRule.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewScanRule.java @@ -47,7 +47,7 @@ public abstract class MaterializedViewScanRule extends AbstractMaterializedViewR List<Expression> expressionsRewritten = rewriteExpression( queryStructInfo.getExpressions(), queryStructInfo.getTopPlan(), - materializationContext.getMvExprToMvScanExprMapping(), + materializationContext.getExprToScanExprMapping(), targetToSourceMapping, true, queryStructInfo.getTableBitSet() @@ -58,7 +58,7 @@ public abstract class MaterializedViewScanRule extends AbstractMaterializedViewR "Rewrite expressions by view in scan fail", () -> String.format("expressionToRewritten is %s,\n mvExprToMvScanExprMapping is %s,\n" + "targetToSourceMapping = %s", queryStructInfo.getExpressions(), - materializationContext.getMvExprToMvScanExprMapping(), + materializationContext.getExprToScanExprMapping(), targetToSourceMapping)); return null; } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MvTableIdIsLongTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/IdStatisticsMapTest.java similarity index 74% copy from fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MvTableIdIsLongTest.java copy to fe/fe-core/src/test/java/org/apache/doris/nereids/mv/IdStatisticsMapTest.java index 75aa7718c00..6660457b884 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MvTableIdIsLongTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/IdStatisticsMapTest.java @@ -15,15 +15,17 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.memo; +package org.apache.doris.nereids.mv; import org.apache.doris.catalog.MTMV; import org.apache.doris.mtmv.MTMVRelationManager; import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.sqltest.SqlTestBase; +import org.apache.doris.nereids.trees.plans.RelationId; import org.apache.doris.nereids.util.PlanChecker; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.SessionVariable; +import org.apache.doris.statistics.Statistics; import mockit.Mock; import mockit.MockUp; @@ -31,14 +33,16 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.BitSet; +import java.util.Map; +import java.util.Optional; /** - * Test mv rewrite when base table id is lager then integer + * Test idStatisticsMap in StatementContext is valid */ -public class MvTableIdIsLongTest extends SqlTestBase { +public class IdStatisticsMapTest extends SqlTestBase { @Test - void testMvRewriteWhenBaseTableIdIsLong() throws Exception { + void testIdStatisticsIsExistWhenRewriteByMv() throws Exception { connectContext.getSessionVariable().setDisableNereidsRules("PRUNE_EMPTY_PARTITION"); BitSet disableNereidsRules = connectContext.getSessionVariable().getDisableNereidsRules(); new MockUp<SessionVariable>() { @@ -55,7 +59,7 @@ public class MvTableIdIsLongTest extends SqlTestBase { }; connectContext.getSessionVariable().enableMaterializedViewRewrite = true; connectContext.getSessionVariable().enableMaterializedViewNestRewrite = true; - createMvByNereids("create materialized view mv1 BUILD IMMEDIATE REFRESH COMPLETE ON MANUAL\n" + createMvByNereids("create materialized view mv100 BUILD IMMEDIATE REFRESH COMPLETE ON MANUAL\n" + " DISTRIBUTED BY RANDOM BUCKETS 1\n" + " PROPERTIES ('replication_num' = '1') \n" + " as select T1.id from T1 inner join T2 " @@ -71,7 +75,11 @@ public class MvTableIdIsLongTest extends SqlTestBase { .rewrite() .optimize() .printlnBestPlanTree(); - Assertions.assertTrue(c1.getMemo().toString().contains("mv1")); - dropMvByNereids("drop materialized view mv1"); + Map<RelationId, Statistics> idStatisticsMap = c1.getStatementContext().getRelationIdToStatisticsMap(); + Assertions.assertFalse(idStatisticsMap.isEmpty()); + RelationId relationId = idStatisticsMap.keySet().iterator().next(); + Optional<Statistics> statistics = c1.getStatementContext().getStatistics(relationId); + Assertions.assertTrue(statistics.isPresent()); + dropMvByNereids("drop materialized view mv100"); } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MvTableIdIsLongTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/MvTableIdIsLongTest.java similarity index 98% rename from fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MvTableIdIsLongTest.java rename to fe/fe-core/src/test/java/org/apache/doris/nereids/mv/MvTableIdIsLongTest.java index 75aa7718c00..fd3887d3cfd 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MvTableIdIsLongTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/MvTableIdIsLongTest.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.memo; +package org.apache.doris.nereids.mv; import org.apache.doris.catalog.MTMV; import org.apache.doris.mtmv.MTMVRelationManager; --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org