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 1e6b285187..d7d46ecc15 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 @@ -36,6 +36,7 @@ import org.apache.doris.nereids.trees.plans.LeafPlan; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation; import org.apache.doris.nereids.trees.plans.algebra.SetOperation; +import org.apache.doris.nereids.trees.plans.logical.LogicalCatalogRelation; import org.apache.doris.nereids.trees.plans.logical.LogicalJoin; import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; import org.apache.doris.nereids.trees.plans.logical.LogicalProject; @@ -72,7 +73,6 @@ public class Memo { EventChannel.getDefaultChannel().addConsumers(new LogConsumer(GroupMergeEvent.class, EventChannel.LOG))); private static long stateId = 0; private final ConnectContext connectContext; - private final Set needRefreshTableIdSet = new HashSet<>(); private final AtomicLong refreshVersion = new AtomicLong(1); private final IdGenerator groupIdGenerator = GroupId.createGenerator(); private final Map groups = Maps.newLinkedHashMap(); @@ -416,7 +416,9 @@ public class Memo { throw new IllegalStateException("Insert a plan into targetGroup but differ in logicalproperties"); } // TODO Support sync materialized view in the future - if (plan instanceof LogicalPlan && plan instanceof CatalogRelation + if (connectContext != null + && connectContext.getSessionVariable().isEnableMaterializedViewNestRewrite() + && plan instanceof LogicalCatalogRelation && ((CatalogRelation) plan).getTable() instanceof MTMV && !plan.getGroupExpression().isPresent()) { refreshVersion.incrementAndGet(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/StructInfoMap.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/StructInfoMap.java index ae07c2043a..df41eb3eb4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/StructInfoMap.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/StructInfoMap.java @@ -48,12 +48,10 @@ public class StructInfoMap { * get struct info according to table map * * @param tableMap the original table map - * @param foldTableMap the fold table map * @param group the group that the mv matched * @return struct info or null if not found */ - public @Nullable StructInfo getStructInfo(Memo memo, BitSet tableMap, BitSet foldTableMap, - Group group, Plan originPlan) { + public @Nullable StructInfo getStructInfo(Memo memo, BitSet tableMap, Group group, Plan originPlan) { StructInfo structInfo = infoMap.get(tableMap); if (structInfo != null) { return structInfo; @@ -84,10 +82,6 @@ public class StructInfoMap { return groupExpressionMap.get(tableMap); } - public long getRefreshVersion() { - return refreshVersion; - } - public void setRefreshVersion(long refreshVersion) { this.refreshVersion = refreshVersion; } @@ -119,7 +113,8 @@ public class StructInfoMap { * */ public void refresh(Group group, long memoVersion) { - if (memoVersion == group.getstructInfoMap().refreshVersion) { + StructInfoMap structInfoMap = group.getstructInfoMap(); + if (!structInfoMap.getTableMaps().isEmpty() && memoVersion == structInfoMap.refreshVersion) { return; } Set refreshedGroup = new HashSet<>(); @@ -152,8 +147,7 @@ public class StructInfoMap { } // if cumulative child table map is different from current // or current group expression map is empty, should update the groupExpressionMap currently - Collection>> bitSetWithChildren = cartesianProduct(childrenTableMap, - new BitSet()); + Collection>> bitSetWithChildren = cartesianProduct(childrenTableMap); for (Pair> bitSetWithChild : bitSetWithChildren) { groupExpressionMap.putIfAbsent(bitSetWithChild.first, Pair.of(groupExpression, bitSetWithChild.second)); @@ -173,8 +167,7 @@ public class StructInfoMap { return tableMap; } - private Collection>> cartesianProduct(List> childrenTableMap, - BitSet targetBitSet) { + private Collection>> cartesianProduct(List> childrenTableMap) { Set> cartesianLists = Sets.cartesianProduct(childrenTableMap); List>> resultPairSet = new LinkedList<>(); for (List bitSetList : cartesianLists) { @@ -182,10 +175,6 @@ public class StructInfoMap { for (BitSet b : bitSetList) { bitSet.or(b); } - // filter the useless bitset which targetBitSet not contains, avoid exponential expansion - if (!targetBitSet.isEmpty() && !StructInfo.containsAll(targetBitSet, bitSet)) { - continue; - } resultPairSet.add(Pair.of(bitSet, bitSetList)); } return resultPairSet; 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 3405942c3a..6d0f602706 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 @@ -107,7 +107,6 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac if (checkIfRewritten(queryPlan, context)) { continue; } - context.tryReGenerateMvScanPlan(cascadesContext); // check mv plan is valid or not if (!checkPattern(context.getStructInfo())) { context.recordFailReason(context.getStructInfo(), @@ -321,6 +320,8 @@ public abstract class AbstractMaterializedViewRule implements ExplorationRuleFac continue; } recordIfRewritten(queryStructInfo.getOriginalPlan(), materializationContext); + // if rewrite successfully, try to regenerate mv scan because it maybe used again + materializationContext.tryReGenerateMvScanPlan(cascadesContext); rewriteResults.add(rewrittenPlan); } return rewriteResults; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java index 0fbcf6b7d6..6863a7e01b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/MaterializedViewUtils.java @@ -148,11 +148,10 @@ public class MaterializedViewUtils { if (plan.getGroupExpression().isPresent()) { Group ownerGroup = plan.getGroupExpression().get().getOwnerGroup(); StructInfoMap structInfoMap = ownerGroup.getstructInfoMap(); - if (cascadesContext.getMemo().getRefreshVersion() != structInfoMap.getRefreshVersion() - || structInfoMap.getTableMaps().isEmpty()) { - structInfoMap.refresh(ownerGroup, cascadesContext.getMemo().getRefreshVersion()); - structInfoMap.setRefreshVersion(cascadesContext.getMemo().getRefreshVersion()); - } + // Refresh struct info in current level plan from top to bottom + structInfoMap.refresh(ownerGroup, cascadesContext.getMemo().getRefreshVersion()); + structInfoMap.setRefreshVersion(cascadesContext.getMemo().getRefreshVersion()); + Set queryTableSets = structInfoMap.getTableMaps(); ImmutableList.Builder structInfosBuilder = ImmutableList.builder(); if (!queryTableSets.isEmpty()) { @@ -163,7 +162,7 @@ public class MaterializedViewUtils { continue; } StructInfo structInfo = structInfoMap.getStructInfo(cascadesContext.getMemo(), - queryTableSet, queryTableSet, ownerGroup, plan); + queryTableSet, ownerGroup, plan); if (structInfo != null) { structInfosBuilder.add(structInfo); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java index 480d153ba5..207da09a43 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java @@ -518,6 +518,9 @@ public class SessionVariable implements Serializable, Writable { public static final String ENABLE_MATERIALIZED_VIEW_UNION_REWRITE = "enable_materialized_view_union_rewrite"; + public static final String ENABLE_MATERIALIZED_VIEW_NEST_REWRITE + = "enable_materialized_view_nest_rewrite"; + public static final String CREATE_TABLE_PARTITION_MAX_NUM = "create_table_partition_max_num"; @@ -1630,6 +1633,11 @@ public class SessionVariable implements Serializable, Writable { + "respond to the query"}) public boolean enableMaterializedViewUnionRewrite = false; + @VariableMgr.VarAttr(name = ENABLE_MATERIALIZED_VIEW_NEST_REWRITE, needForward = true, + description = {"是否允许嵌套物化视图改写", + "Whether enable materialized view nest rewrite"}) + public boolean enableMaterializedViewNestRewrite = false; + @VariableMgr.VarAttr(name = CREATE_TABLE_PARTITION_MAX_NUM, needForward = true, description = {"建表时创建分区的最大数量", "The maximum number of partitions created during table creation"}) @@ -3656,6 +3664,10 @@ public class SessionVariable implements Serializable, Writable { return enableMaterializedViewUnionRewrite; } + public boolean isEnableMaterializedViewNestRewrite() { + return enableMaterializedViewNestRewrite; + } + public int getCreateTablePartitionMaxNum() { return createTablePartitionMaxNum; } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/StructInfoMapTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/StructInfoMapTest.java index 217cbf2f1a..d644ce2b89 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/StructInfoMapTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/StructInfoMapTest.java @@ -59,6 +59,7 @@ class StructInfoMapTest extends SqlTestBase { } }; connectContext.getSessionVariable().enableMaterializedViewRewrite = true; + connectContext.getSessionVariable().enableMaterializedViewNestRewrite = true; createMvByNereids("create materialized view mv1 BUILD IMMEDIATE REFRESH COMPLETE ON MANUAL\n" + " DISTRIBUTED BY RANDOM BUCKETS 1\n" + " PROPERTIES ('replication_num' = '1') \n" @@ -107,6 +108,7 @@ class StructInfoMapTest extends SqlTestBase { } }; connectContext.getSessionVariable().enableMaterializedViewRewrite = true; + connectContext.getSessionVariable().enableMaterializedViewNestRewrite = true; createMvByNereids("create materialized view mv1 BUILD IMMEDIATE REFRESH COMPLETE ON MANUAL\n" + " DISTRIBUTED BY RANDOM BUCKETS 1\n" + " PROPERTIES ('replication_num' = '1') \n" @@ -146,6 +148,7 @@ class StructInfoMapTest extends SqlTestBase { } }; connectContext.getSessionVariable().enableMaterializedViewRewrite = true; + connectContext.getSessionVariable().enableMaterializedViewNestRewrite = true; createMvByNereids("create materialized view mv1 BUILD IMMEDIATE REFRESH COMPLETE ON MANUAL\n" + " DISTRIBUTED BY RANDOM BUCKETS 1\n" + " PROPERTIES ('replication_num' = '1') \n" @@ -168,7 +171,7 @@ class StructInfoMapTest extends SqlTestBase { BitSet mvMap = structInfoMap.getTableMaps().stream() .filter(b -> b.cardinality() == 2) .collect(Collectors.toList()).get(0); - StructInfo structInfo = structInfoMap.getStructInfo(c1.getMemo(), mvMap, mvMap, root, null); + StructInfo structInfo = structInfoMap.getStructInfo(c1.getMemo(), mvMap, root, null); System.out.println(structInfo.getOriginalPlan().treeString()); BitSet bitSet = new BitSet(); structInfo.getRelations().forEach(r -> bitSet.set((int) r.getTable().getId())); diff --git a/regression-test/data/nereids_rules_p0/mv/nested/nested_materialized_view.out b/regression-test/data/nereids_rules_p0/mv/nested/nested_materialized_view.out index 09c11bcee6..2647a9f7d5 100644 --- a/regression-test/data/nereids_rules_p0/mv/nested/nested_materialized_view.out +++ b/regression-test/data/nereids_rules_p0/mv/nested/nested_materialized_view.out @@ -17,4 +17,25 @@ 6 6 6 -6 \ No newline at end of file +6 + +-- !query1_1_before -- +4 +4 +4 +4 +6 +6 +6 +6 + +-- !query1_1_after -- +4 +4 +4 +4 +6 +6 +6 +6 + diff --git a/regression-test/suites/nereids_rules_p0/mv/nested/nested_materialized_view.groovy b/regression-test/suites/nereids_rules_p0/mv/nested/nested_materialized_view.groovy index 6ad175e85c..5a5ddc5b3c 100644 --- a/regression-test/suites/nereids_rules_p0/mv/nested/nested_materialized_view.groovy +++ b/regression-test/suites/nereids_rules_p0/mv/nested/nested_materialized_view.groovy @@ -23,6 +23,7 @@ suite("nested_materialized_view") { sql "SET enable_fallback_to_original_planner=false" sql "SET enable_materialized_view_rewrite=true" sql "SET enable_nereids_timeout = false" + sql "SET enable_materialized_view_nest_rewrite = true" def create_mtmv = { db_name, mv_name, mv_sql -> sql """DROP MATERIALIZED VIEW IF EXISTS ${mv_name}""" @@ -179,4 +180,19 @@ suite("nested_materialized_view") { order_qt_query1_0_after "${query1_0}" sql """ DROP MATERIALIZED VIEW IF EXISTS mv1_0_inner_mv""" sql """ DROP MATERIALIZED VIEW IF EXISTS mv1_0""" + + + sql "SET enable_materialized_view_nest_rewrite = false" + + order_qt_query1_1_before "${query1_0}" + create_mtmv(db, "mv1_0_inner_mv", mv1_0_inner_mv) + check_mv_rewrite_fail(db, mv1_0, query1_0, "mv1_0") + + explain { + sql("${query1_0}") + contains("mv1_0_inner_mv(mv1_0_inner_mv)") + } + + order_qt_query1_1_after "${query1_0}" + } diff --git a/regression-test/suites/nereids_rules_p0/mv/nested_mtmv/nested_mtmv.groovy b/regression-test/suites/nereids_rules_p0/mv/nested_mtmv/nested_mtmv.groovy index 4a0f513f0f..988275c489 100644 --- a/regression-test/suites/nereids_rules_p0/mv/nested_mtmv/nested_mtmv.groovy +++ b/regression-test/suites/nereids_rules_p0/mv/nested_mtmv/nested_mtmv.groovy @@ -22,6 +22,7 @@ suite("nested_mtmv") { sql "SET enable_fallback_to_original_planner=false" sql "SET enable_materialized_view_rewrite=true" sql "SET enable_nereids_timeout = false" + sql "SET enable_materialized_view_nest_rewrite = true" sql """ drop table if exists orders_1