This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.0 by this push:
     new 3b73f996a14 branch-4.0: [opt](mtmv) Optimize pre rbo query rewrite by 
materialized view performance to avoid fe oom and slow #59154 (#61231)
3b73f996a14 is described below

commit 3b73f996a14e3ec78debb513c0f21bdd19138244
Author: seawinde <[email protected]>
AuthorDate: Thu Mar 12 09:55:49 2026 +0800

    branch-4.0: [opt](mtmv) Optimize pre rbo query rewrite by materialized view 
performance to avoid fe oom and slow #59154 (#61231)
    
    Cherry-pick of commit b43c16c9f35c02701c915daee56de662976389a1 from
    master to branch-4.0.
    
    Original PR: https://github.com/apache/doris/pull/59154
---
 .../main/java/org/apache/doris/mtmv/MTMVCache.java |   3 +-
 .../org/apache/doris/nereids/StatementContext.java |  11 +-
 .../java/org/apache/doris/nereids/memo/Memo.java   |  37 ++-
 .../apache/doris/nereids/memo/StructInfoMap.java   | 294 ++++++++++++++++-----
 .../mv/AbstractMaterializedViewAggregateRule.java  |  24 +-
 .../mv/AbstractMaterializedViewJoinRule.java       |   1 -
 .../mv/AbstractMaterializedViewRule.java           |  53 ++--
 .../mv/AbstractMaterializedViewScanRule.java       |   1 -
 .../mv/AbstractMaterializedViewWindowRule.java     |   1 -
 .../exploration/mv/MaterializationContext.java     |  29 +-
 .../exploration/mv/MaterializedViewUtils.java      |  30 ++-
 .../mv/PartitionIncrementMaintainer.java           |  11 +-
 .../mv/PreMaterializedViewRewriter.java            |   4 +-
 .../nereids/rules/exploration/mv/Predicates.java   |   3 +-
 .../nereids/rules/exploration/mv/StructInfo.java   |  34 ++-
 .../rules/rewrite/QueryPartitionCollector.java     |   7 +-
 .../apache/doris/nereids/util/ExpressionUtils.java |   8 +-
 .../doris/nereids/memo/StructInfoMapTest.java      |  75 ++++--
 .../mv/PointQueryShouldNotMvRewriteTest.java       |   5 +-
 .../mv/PreMaterializedViewRewriterTest.java        |   4 +-
 .../apache/doris/nereids/mv/PredicatesTest.java    |  14 +-
 .../rules/exploration/mv/HyperGraphAggTest.java    |   7 +-
 .../apache/doris/nereids/sqltest/SqlTestBase.java  |   9 +-
 .../doris/nereids/util/ExpressionUtilsTest.java    |   3 +-
 .../mv/nested_mtmv/nested_mtmv.groovy              |  12 +-
 25 files changed, 426 insertions(+), 254 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 3a2cb45f51a..391a9546500 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
@@ -48,7 +48,6 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.List;
 import java.util.Optional;
 
@@ -182,7 +181,7 @@ public class MTMVCache {
         }, mvPlan, plan, false);
         // Construct structInfo once for use later
         Optional<StructInfo> structInfoOptional = 
MaterializationContext.constructStructInfo(mvPlan, plan,
-                cascadesContext, new BitSet());
+                cascadesContext);
         return Pair.of(mvPlan, structInfoOptional.orElse(null));
     }
 }
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 92fcd8a1592..6f4b97b9403 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
@@ -265,9 +265,10 @@ public class StatementContext implements Closeable {
     private long materializedViewRewriteDuration = 0L;
 
     // Record used table and it's used partitions
-    private final Multimap<List<String>, Pair<RelationId, Set<String>>> 
tableUsedPartitionNameMap = HashMultimap
-            .create();
-    private final Map<Integer, Integer> relationIdToCommonTableIdMap = new 
HashMap<>();
+    private final Multimap<List<String>, Pair<RelationId, Set<String>>> 
tableUsedPartitionNameMap =
+            HashMultimap.create();
+    // Record query common table id to relation id mapping, this is used for 
mv rewrite
+    private final Multimap<Integer, Integer> commonTableIdToRelationIdToMap = 
HashMultimap.create();
 
     // Record mtmv and valid partitions map because this is time-consuming 
behavior
     private final Map<BaseTableInfo, Collection<Partition>> 
mvCanRewritePartitionsMap = new HashMap<>();
@@ -1094,8 +1095,8 @@ public class StatementContext implements Closeable {
         return tableUsedPartitionNameMap;
     }
 
-    public Map<Integer, Integer> getRelationIdToCommonTableIdMap() {
-        return relationIdToCommonTableIdMap;
+    public Multimap<Integer, Integer> getCommonTableIdToRelationIdMap() {
+        return commonTableIdToRelationIdToMap;
     }
 
     public Map<BaseTableInfo, Collection<Partition>> 
getMvCanRewritePartitionsMap() {
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 de1a2cba892..4eb48d8311f 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
@@ -34,6 +34,7 @@ import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.LeafPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.TableId;
 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;
@@ -51,6 +52,7 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -59,7 +61,7 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.PriorityQueue;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javax.annotation.Nullable;
@@ -74,7 +76,9 @@ public class Memo {
             EventChannel.getDefaultChannel().addConsumers(new 
LogConsumer(GroupMergeEvent.class, EventChannel.LOG)));
     private static long stateId = 0;
     private final ConnectContext connectContext;
-    private final AtomicLong refreshVersion = new AtomicLong(1);
+    // The key is the query tableId, the value is the refresh version when 
last refresh, this is needed
+    // because struct info refresh base on target tableId.
+    private final Map<Integer, AtomicInteger> refreshVersion = new HashMap<>();
     private final Map<Class<? extends AbstractMaterializedViewRule>, 
Set<Long>> materializationCheckSuccessMap =
             new LinkedHashMap<>();
     private final Map<Class<? extends AbstractMaterializedViewRule>, 
Set<Long>> materializationCheckFailMap =
@@ -128,12 +132,28 @@ public class Memo {
         return groupExpressions.size();
     }
 
-    public long getRefreshVersion() {
-        return refreshVersion.get();
+    /** get the refresh version map*/
+    public Map<Integer, AtomicInteger> getRefreshVersion() {
+        return refreshVersion;
     }
 
-    public long incrementAndGetRefreshVersion() {
-        return refreshVersion.incrementAndGet();
+    /** return the incremented refresh version for the given commonTableId*/
+    public long incrementAndGetRefreshVersion(int commonTableId) {
+        return refreshVersion.compute(commonTableId, (k, v) -> {
+            if (v == null) {
+                return new AtomicInteger(1);
+            }
+            v.incrementAndGet();
+            return v;
+        }).get();
+    }
+
+    /** return the incremented refresh version for the given relationId set*/
+    public void incrementAndGetRefreshVersion(BitSet commonTableIdSet) {
+        for (int i = commonTableIdSet.nextSetBit(0); i >= 0;
+                i = commonTableIdSet.nextSetBit(i + 1)) {
+            incrementAndGetRefreshVersion(i);
+        }
     }
 
     /**
@@ -461,13 +481,14 @@ public class Memo {
                     plan.getLogicalProperties(), 
targetGroup.getLogicalProperties());
             throw new IllegalStateException("Insert a plan into targetGroup 
but differ in logicalproperties");
         }
-        // TODO Support sync materialized view in the future
         if (connectContext != null
                 && 
connectContext.getSessionVariable().isEnableMaterializedViewNestRewrite()
                 && plan instanceof LogicalCatalogRelation
                 && ((CatalogRelation) plan).getTable() instanceof MTMV
                 && !plan.getGroupExpression().isPresent()) {
-            incrementAndGetRefreshVersion();
+            TableId mvCommonTableId
+                    = 
this.connectContext.getStatementContext().getTableId(((CatalogRelation) 
plan).getTable());
+            incrementAndGetRefreshVersion(mvCommonTableId.asInt());
         }
         Optional<GroupExpression> groupExpr = plan.getGroupExpression();
         if (groupExpr.isPresent()) {
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 2f9e0ca5243..ac567c4bd34 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
@@ -43,6 +43,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 import javax.annotation.Nullable;
 
 /**
@@ -51,80 +52,221 @@ import javax.annotation.Nullable;
 public class StructInfoMap {
 
     public static final Logger LOG = LogManager.getLogger(StructInfoMap.class);
+    // 2166136261
+    private static final int FNV32_OFFSET_BASIS = 0x811C9DC5;
+    // 16777619
+    private static final int FNV32_PRIME = 0x01000193;
+    /**
+     * Strategy for table ID mode
+     */
+    private static final IdModeStrategy TABLE_ID_STRATEGY = new 
IdModeStrategy() {
+        @Override
+        public Map<BitSet, Pair<GroupExpression, List<BitSet>>> 
getGroupExpressionMap(StructInfoMap structInfoMap) {
+            return structInfoMap.groupExpressionMapByTableId;
+        }
+
+        @Override
+        public Map<BitSet, StructInfo> getInfoMap(StructInfoMap structInfoMap) 
{
+            return structInfoMap.infoMapByTableId;
+        }
+
+        @Override
+        public BitSet constructLeaf(GroupExpression groupExpression, 
CascadesContext cascadesContext,
+                                    boolean forceRefresh) {
+            Plan plan = groupExpression.getPlan();
+            BitSet tableMap = new BitSet();
+            if (plan instanceof LogicalCatalogRelation) {
+                LogicalCatalogRelation relation = (LogicalCatalogRelation) 
plan;
+                TableIf table = relation.getTable();
+                if (!forceRefresh && cascadesContext.getStatementContext()
+                        
.getMaterializationRewrittenSuccessSet().contains(table.getFullQualifiers())) {
+                    return tableMap;
+                }
+                
tableMap.set(cascadesContext.getStatementContext().getTableId(table).asInt());
+            }
+            return tableMap;
+        }
+
+        @Override
+        public int computeMemoVersion(BitSet targetIdMap, CascadesContext 
cascadesContext) {
+            return getMemoVersion(targetIdMap, 
cascadesContext.getMemo().getRefreshVersion());
+        }
+    };
+
+    /**
+     * Strategy for relation ID mode
+     */
+    private static final IdModeStrategy RELATION_ID_STRATEGY = new 
IdModeStrategy() {
+        @Override
+        public Map<BitSet, Pair<GroupExpression, List<BitSet>>> 
getGroupExpressionMap(StructInfoMap structInfoMap) {
+            return structInfoMap.groupExpressionMapByRelationId;
+        }
+
+        @Override
+        public Map<BitSet, StructInfo> getInfoMap(StructInfoMap structInfoMap) 
{
+            return structInfoMap.infoMapByRelationId;
+        }
+
+        @Override
+        public BitSet constructLeaf(GroupExpression groupExpression, 
CascadesContext cascadesContext,
+                                    boolean forceRefresh) {
+            Plan plan = groupExpression.getPlan();
+            BitSet tableMap = new BitSet();
+            if (plan instanceof LogicalCatalogRelation) {
+                LogicalCatalogRelation relation = (LogicalCatalogRelation) 
plan;
+                TableIf table = relation.getTable();
+                if (!forceRefresh && cascadesContext.getStatementContext()
+                        
.getMaterializationRewrittenSuccessSet().contains(table.getFullQualifiers())) {
+                    return tableMap;
+                }
+                tableMap.set(relation.getRelationId().asInt());
+            }
+            if (plan instanceof LogicalCTEConsumer || plan instanceof 
LogicalEmptyRelation
+                    || plan instanceof LogicalOneRowRelation) {
+                tableMap.set(((LogicalRelation) plan).getRelationId().asInt());
+            }
+            return tableMap;
+        }
+
+        @Override
+        public int computeMemoVersion(BitSet targetIdMap, CascadesContext 
cascadesContext) {
+            return getMemoVersion(targetIdMap, 
cascadesContext.getMemo().getRefreshVersion());
+        }
+    };
     /**
      * The map key is the relation id bit set to get corresponding plan 
accurately
      */
-    private final Map<BitSet, Pair<GroupExpression, List<BitSet>>> 
groupExpressionMap = new HashMap<>();
+    private final Map<BitSet, Pair<GroupExpression, List<BitSet>>> 
groupExpressionMapByRelationId = new HashMap<>();
     /**
      * The map key is the relation id bit set to get corresponding plan 
accurately
      */
-    private final Map<BitSet, StructInfo> infoMap = new HashMap<>();
-    private long refreshVersion = 0;
+    private final Map<BitSet, StructInfo> infoMapByRelationId = new 
HashMap<>();
+
+    /**
+     * The map key is the common table id bit set to get corresponding plan 
accurately
+     */
+    private final Map<BitSet, Pair<GroupExpression, List<BitSet>>> 
groupExpressionMapByTableId = new HashMap<>();
+    /**
+     * The map key is the common table id bit set to get corresponding plan 
accurately
+     */
+    private final Map<BitSet, StructInfo> infoMapByTableId = new HashMap<>();
+
+    // The key is the tableIds query used, the value is the refresh version 
when last refresh
+    private final Map<BitSet, Integer> refreshVersion = new HashMap<>();
 
     /**
      * get struct info according to table map
      *
-     * @param tableMap the original table map
+     * @param targetIdMap the original table map
      * @param group the group that the mv matched
      * @return struct info or null if not found
      */
-    public @Nullable StructInfo getStructInfo(CascadesContext cascadesContext, 
BitSet tableMap, Group group,
-            Plan originPlan, boolean forceRefresh) {
-        StructInfo structInfo = infoMap.get(tableMap);
+    public @Nullable StructInfo getStructInfo(CascadesContext cascadesContext, 
BitSet targetIdMap, Group group,
+            Plan originPlan, boolean forceRefresh, boolean tableIdMode) {
+        IdModeStrategy strategy = getStrategy(tableIdMode);
+        Map<BitSet, StructInfo> infoMap = strategy.getInfoMap(this);
+        Map<BitSet, Pair<GroupExpression, List<BitSet>>> groupExprMap = 
strategy.getGroupExpressionMap(this);
+
+        StructInfo structInfo = infoMap.get(targetIdMap);
         if (structInfo != null) {
             return structInfo;
         }
-        if (groupExpressionMap.isEmpty() || 
!groupExpressionMap.containsKey(tableMap)) {
-            refresh(group, cascadesContext, tableMap, new HashSet<>(),
-                    forceRefresh);
-            
group.getStructInfoMap().setRefreshVersion(cascadesContext.getMemo().getRefreshVersion());
+        if (groupExprMap.isEmpty() || !groupExprMap.containsKey(targetIdMap)) {
+            int memoVersion = strategy.computeMemoVersion(targetIdMap, 
cascadesContext);
+            refresh(group, cascadesContext, targetIdMap, new HashSet<>(), 
forceRefresh, memoVersion, tableIdMode);
+            group.getStructInfoMap().setRefreshVersion(targetIdMap, 
cascadesContext.getMemo().getRefreshVersion());
         }
-        if (groupExpressionMap.containsKey(tableMap)) {
-            Pair<GroupExpression, List<BitSet>> groupExpressionBitSetPair = 
getGroupExpressionWithChildren(tableMap);
+        if (groupExprMap.containsKey(targetIdMap)) {
+            Pair<GroupExpression, List<BitSet>> groupExpressionBitSetPair =
+                    getGroupExpressionWithChildren(targetIdMap, tableIdMode);
             // NOTICE: During the transition from physicalAggregate to logical 
aggregation,
-            // the original function signature needs to remain unchanged 
because the constructor of LogicalAggregation
-            // will recalculate the signature of the aggregation function.
+            // the original function signature needs to remain unchanged 
because the constructor
+            // of LogicalAggregation will recalculate the signature of the 
aggregation function.
             // When the calculated signature is inconsistent with the original 
signature
             // (e.g. due to the influence of the session variable 
enable_decimal256),
             // a problem will arise where the output type of the rewritten 
plan is inconsistent with
             // the output type of the upper-level operator.
             structInfo = MoreFieldsThread.keepFunctionSignature(() ->
                     constructStructInfo(groupExpressionBitSetPair.first, 
groupExpressionBitSetPair.second,
-                    originPlan, cascadesContext));
-            infoMap.put(tableMap, structInfo);
+                            originPlan, cascadesContext, tableIdMode));
+            infoMap.put(targetIdMap, structInfo);
         }
         return structInfo;
     }
 
-    public Set<BitSet> getTableMaps() {
-        return groupExpressionMap.keySet();
+    public Set<BitSet> getTableMaps(boolean tableIdMode) {
+        return getStrategy(tableIdMode).getGroupExpressionMap(this).keySet();
+    }
+
+    public Pair<GroupExpression, List<BitSet>> 
getGroupExpressionWithChildren(BitSet tableMap, boolean tableIdMode) {
+        return 
getStrategy(tableIdMode).getGroupExpressionMap(this).get(tableMap);
+    }
+
+    // Set the refresh version for the given targetIdSet
+    public void setRefreshVersion(BitSet targetIdSet, Map<Integer, 
AtomicInteger> memoRefreshVersionMap) {
+        this.refreshVersion.put(targetIdSet, getMemoVersion(targetIdSet, 
memoRefreshVersionMap));
     }
 
-    public Pair<GroupExpression, List<BitSet>> 
getGroupExpressionWithChildren(BitSet tableMap) {
-        return groupExpressionMap.get(tableMap);
+    // Set the refresh version for the given targetIdSet
+    public void setRefreshVersion(BitSet targetIdSet, int memoRefreshVersion) {
+        this.refreshVersion.put(targetIdSet, memoRefreshVersion);
     }
 
-    public void setRefreshVersion(long refreshVersion) {
-        this.refreshVersion = refreshVersion;
+    // Get the refresh version for the given targetIdSet, if not exist, return 0
+    public long getRefreshVersion(BitSet targetIdSet) {
+        return refreshVersion.computeIfAbsent(targetIdSet, k -> 0);
+    }
+
+    /**
+     * Compute a compact "version fingerprint" for the given relation id set.
+     * Algorithm:
+     * - Uses a 32-bit FNV-1a-style hash. Start from FNV32_OFFSET_BASIS and 
multiply by FNV32_PRIME.
+     * - Iterate each set bit (target id) in the BitSet:
+     *   - Fetch its current refresh version from memoRefreshVersionMap 
(default 0 if absent).
+     *   - Mix the version into the hash by XOR, then diffuse by multiplying 
the FNV prime.
+     * - Returns the final hash as the memo version for this set of relations.
+     * Benefits:
+     * - Stable fingerprint: any change in any relation's version produces a 
different hash, enabling
+     *   fast cache invalidation checks without scanning all versions every 
time.
+     * - Order-independent: relies on set iteration; the same set yields the 
same hash regardless of order.
+     * - Low memory and CPU overhead: compresses multiple integers into a 
single 32-bit value efficiently.
+     * - Incremental-friendly: new relations/versions can be incorporated by 
re-running on the changed set.
+     * - Good diffusion: XOR + prime multiplication reduces collisions 
compared to simple sums.
+     * Notes:
+     * - The Integer.MAX_VALUE guard prevents potential overflow edge cases in 
BitSet iteration.
+     */
+    public static int getMemoVersion(BitSet targetIdSet, Map<Integer, 
AtomicInteger> memoRefreshVersionMap) {
+        int hash = FNV32_OFFSET_BASIS;
+        for (int id = targetIdSet.nextSetBit(0);
+                id >= 0; id = targetIdSet.nextSetBit(id + 1)) {
+            AtomicInteger ver = memoRefreshVersionMap.get(id);
+            int tmpVer = ver == null ? 0 : ver.get();
+            hash ^= tmpVer;
+            hash *= FNV32_PRIME;
+            if (id == Integer.MAX_VALUE) {
+                break;
+            }
+        }
+        return hash;
     }
 
     private StructInfo constructStructInfo(GroupExpression groupExpression, 
List<BitSet> children,
-            Plan originPlan, CascadesContext cascadesContext) {
+            Plan originPlan, CascadesContext cascadesContext, boolean 
tableIdMode) {
         // this plan is not origin plan, should record origin plan in struct 
info
-        Plan plan = constructPlan(groupExpression, children);
+        Plan plan = constructPlan(groupExpression, children, tableIdMode);
         return originPlan == null ? StructInfo.of(plan, cascadesContext)
                 : StructInfo.of(plan, originPlan, cascadesContext);
     }
 
-    private Plan constructPlan(GroupExpression groupExpression, List<BitSet> 
children) {
+    private Plan constructPlan(GroupExpression groupExpression, List<BitSet> 
children, boolean tableIdMode) {
         List<Plan> childrenPlan = new ArrayList<>();
         for (int i = 0; i < children.size(); i++) {
             StructInfoMap structInfoMap = 
groupExpression.child(i).getStructInfoMap();
             BitSet childMap = children.get(i);
             Pair<GroupExpression, List<BitSet>> groupExpressionBitSetPair
-                    = structInfoMap.getGroupExpressionWithChildren(childMap);
+                    = structInfoMap.getGroupExpressionWithChildren(childMap, 
tableIdMode);
             childrenPlan.add(
-                    constructPlan(groupExpressionBitSetPair.first, 
groupExpressionBitSetPair.second));
+                    constructPlan(groupExpressionBitSetPair.first, 
groupExpressionBitSetPair.second, tableIdMode));
         }
         // need to clear current group expression info by using 
withGroupExpression
         // this plan would copy into memo, if with group expression, would 
cause err
@@ -136,46 +278,53 @@ public class StructInfoMap {
      *
      * @param group the root group
      * @param targetBitSet refreshed group expression table bitset must 
intersect with the targetBitSet
-     *
      */
     public void refresh(Group group, CascadesContext cascadesContext,
-            BitSet targetBitSet,
-            Set<Integer> refreshedGroup,
-            boolean forceRefresh) {
+            BitSet targetBitSet, Set<Integer> refreshedGroup,
+            boolean forceRefresh, int memoVersion, boolean tableIdMode) {
+        IdModeStrategy strategy = getStrategy(tableIdMode);
+        Map<BitSet, Pair<GroupExpression, List<BitSet>>> groupExprMap = 
strategy.getGroupExpressionMap(this);
         StructInfoMap structInfoMap = group.getStructInfoMap();
         refreshedGroup.add(group.getGroupId().asInt());
-        long memoVersion = cascadesContext.getMemo().getRefreshVersion();
-        if (!structInfoMap.getTableMaps().isEmpty() && memoVersion == 
structInfoMap.refreshVersion) {
+        if (!structInfoMap.getTableMaps(tableIdMode).isEmpty()
+                && memoVersion == 
structInfoMap.getRefreshVersion(targetBitSet)) {
             return;
         }
         for (GroupExpression groupExpression : group.getLogicalExpressions()) {
             List<Set<BitSet>> childrenTableMap = new LinkedList<>();
             if (groupExpression.children().isEmpty()) {
-                BitSet leaf = constructLeaf(groupExpression, cascadesContext, 
forceRefresh);
+                BitSet leaf = strategy.constructLeaf(groupExpression, 
cascadesContext, forceRefresh);
                 if (leaf.isEmpty()) {
                     break;
                 }
-                groupExpressionMap.put(leaf, Pair.of(groupExpression, new 
LinkedList<>()));
+                groupExprMap.put(leaf, Pair.of(groupExpression, new 
LinkedList<>()));
                 continue;
             }
+            // this is used for filter group expression whose children's table 
map all not in targetBitSet
+            BitSet filteredTableMaps = new BitSet();
+            // groupExpression self could be pruned
             for (Group child : groupExpression.children()) {
+                // group in expression should all be reserved
                 StructInfoMap childStructInfoMap = child.getStructInfoMap();
                 if (!refreshedGroup.contains(child.getGroupId().asInt())) {
-                    childStructInfoMap.refresh(child, cascadesContext, 
targetBitSet, refreshedGroup, forceRefresh);
-                    childStructInfoMap.setRefreshVersion(memoVersion);
+                    childStructInfoMap.refresh(child, cascadesContext, 
targetBitSet,
+                            refreshedGroup, forceRefresh, memoVersion, 
tableIdMode);
+                    childStructInfoMap.setRefreshVersion(targetBitSet, 
memoVersion);
                 }
-                Set<BitSet> filteredTableMaps = new HashSet<>();
-                for (BitSet tableMaps : 
child.getStructInfoMap().getTableMaps()) {
-                    // filter the tableSet that used intersects with 
targetBitSet
-                    if (!targetBitSet.isEmpty() && 
!tableMaps.intersects(targetBitSet)) {
-                        continue;
-                    }
-                    filteredTableMaps.add(tableMaps);
+                Set<BitSet> groupTableSet = new HashSet<>();
+                for (BitSet tableMaps : 
child.getStructInfoMap().getTableMaps(tableIdMode)) {
+                    groupTableSet.add(tableMaps);
+                    filteredTableMaps.or(tableMaps);
                 }
                 if (!filteredTableMaps.isEmpty()) {
-                    childrenTableMap.add(filteredTableMaps);
+                    childrenTableMap.add(groupTableSet);
                 }
             }
+            // filter the tableSet that used intersects with targetBitSet, 
make sure the at least constructed
+            if (!structInfoMap.getTableMaps(tableIdMode).isEmpty() && 
!targetBitSet.isEmpty()
+                    && !filteredTableMaps.isEmpty() && 
!filteredTableMaps.intersects(targetBitSet)) {
+                continue;
+            }
             if (childrenTableMap.isEmpty()) {
                 continue;
             }
@@ -186,7 +335,7 @@ public class StructInfoMap {
                     eachGroupExpressionTableSet.or(bitSet);
                 }
             }
-            if (groupExpressionMap.containsKey(eachGroupExpressionTableSet)) {
+            if (groupExprMap.containsKey(eachGroupExpressionTableSet)) {
                 // for the group expressions of group, only need to refresh 
any of the group expression
                 // when they have the same group expression table set
                 continue;
@@ -195,36 +344,12 @@ public class StructInfoMap {
             // or current group expression map is empty, should update the 
groupExpressionMap currently
             Collection<Pair<BitSet, List<BitSet>>> bitSetWithChildren = 
cartesianProduct(childrenTableMap);
             for (Pair<BitSet, List<BitSet>> bitSetWithChild : 
bitSetWithChildren) {
-                groupExpressionMap.putIfAbsent(bitSetWithChild.first,
+                groupExprMap.putIfAbsent(bitSetWithChild.first,
                         Pair.of(groupExpression, bitSetWithChild.second));
             }
-
         }
     }
 
-    private BitSet constructLeaf(GroupExpression groupExpression, 
CascadesContext cascadesContext,
-            boolean forceRefresh) {
-        Plan plan = groupExpression.getPlan();
-        BitSet tableMap = new BitSet();
-        if (plan instanceof LogicalCatalogRelation) {
-            LogicalCatalogRelation logicalCatalogRelation = 
(LogicalCatalogRelation) plan;
-            TableIf table = logicalCatalogRelation.getTable();
-            // If disable materialized view nest rewrite, and mv already 
rewritten successfully once, doesn't construct
-            // table id map for nest mv rewrite
-            if (!forceRefresh && cascadesContext.getStatementContext()
-                    
.getMaterializationRewrittenSuccessSet().contains(table.getFullQualifiers())) {
-                return tableMap;
-            }
-            tableMap.set(logicalCatalogRelation.getRelationId().asInt());
-        }
-        // one row relation / CTE consumer
-        if (plan instanceof LogicalCTEConsumer || plan instanceof 
LogicalEmptyRelation
-                || plan instanceof LogicalOneRowRelation) {
-            tableMap.set(((LogicalRelation) plan).getRelationId().asInt());
-        }
-        return tableMap;
-    }
-
     private Collection<Pair<BitSet, List<BitSet>>> 
cartesianProduct(List<Set<BitSet>> childrenTableMap) {
         Set<List<BitSet>> cartesianLists = 
Sets.cartesianProduct(childrenTableMap);
         List<Pair<BitSet, List<BitSet>>> resultPairSet = new LinkedList<>();
@@ -238,8 +363,31 @@ public class StructInfoMap {
         return resultPairSet;
     }
 
+    /**
+     * Strategy interface to handle different ID modes (tableId vs relationId)
+     */
+    private interface IdModeStrategy {
+        Map<BitSet, Pair<GroupExpression, List<BitSet>>> 
getGroupExpressionMap(StructInfoMap structInfoMap);
+
+        Map<BitSet, StructInfo> getInfoMap(StructInfoMap structInfoMap);
+
+        BitSet constructLeaf(GroupExpression groupExpression, CascadesContext 
cascadesContext, boolean forceRefresh);
+
+        int computeMemoVersion(BitSet targetIdMap, CascadesContext 
cascadesContext);
+    }
+
+    private static IdModeStrategy getStrategy(boolean tableIdMode) {
+        return tableIdMode ? TABLE_ID_STRATEGY : RELATION_ID_STRATEGY;
+    }
+
     @Override
     public String toString() {
-        return "StructInfoMap{ groupExpressionMap = " + groupExpressionMap + 
", infoMap = " + infoMap + '}';
+        return "StructInfoMap{"
+                + " groupExpressionMapByRelationId=" + 
groupExpressionMapByRelationId.keySet()
+                + ", infoMapByRelationId=" + infoMapByRelationId.keySet()
+                + ", groupExpressionMapByTableId=" + 
groupExpressionMapByTableId.keySet()
+                + ", infoMapByTableId=" + infoMapByTableId.keySet()
+                + ", refreshVersion=" + refreshVersion
+                + '}';
     }
 }
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 87ddc7a0ca5..a7e1e171ee2 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
@@ -59,7 +59,6 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Sets;
 
 import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -124,7 +123,6 @@ public abstract class AbstractMaterializedViewAggregateRule 
extends AbstractMate
                     queryTopPlan,
                     materializationContext.getShuttledExprToScanExprMapping(),
                     viewToQuerySlotMapping,
-                    queryStructInfo.getTableBitSet(),
                     ImmutableMap.of(), cascadesContext);
             boolean isRewrittenQueryExpressionValid = true;
             if (!rewrittenQueryExpressions.isEmpty()) {
@@ -314,11 +312,10 @@ public abstract class 
AbstractMaterializedViewAggregateRule extends AbstractMate
             MaterializationContext materializationContext, String 
summaryIfFail, Supplier<String> detailIfFail) {
         Expression queryFunctionShuttled = 
ExpressionUtils.shuttleExpressionWithLineage(
                 queryExpression,
-                queryStructInfo.getTopPlan(),
-                queryStructInfo.getTableBitSet());
+                queryStructInfo.getTopPlan());
         AggregateExpressionRewriteContext expressionRewriteContext = new 
AggregateExpressionRewriteContext(
                 rewriteMode, mvShuttledExprToMvScanExprQueryBased, 
queryStructInfo.getTopPlan(),
-                queryStructInfo.getTableBitSet(), 
queryStructInfo.getGroupingId());
+                queryStructInfo.getGroupingId());
         Expression rewrittenExpression = 
queryFunctionShuttled.accept(AGGREGATE_EXPRESSION_REWRITER,
                 expressionRewriteContext);
         if (!expressionRewriteContext.isValid()) {
@@ -358,7 +355,7 @@ public abstract class AbstractMaterializedViewAggregateRule 
extends AbstractMate
         boolean canUnionRewrite = false;
         // Check the query plan group by expression contains partition col or 
not
         List<? extends Expression> groupByShuttledExpressions =
-                
ExpressionUtils.shuttleExpressionWithLineage(groupByExpressions, queryPlan, new 
BitSet());
+                
ExpressionUtils.shuttleExpressionWithLineage(groupByExpressions, queryPlan);
         for (Expression expression : groupByShuttledExpressions) {
             canUnionRewrite = !expression.collectToSet(expr -> {
                 if (!(expr instanceof SlotReference) || !((SlotReference) 
expr).isColumnFromTable()) {
@@ -431,13 +428,13 @@ public abstract class 
AbstractMaterializedViewAggregateRule extends AbstractMate
         LogicalAggregate<Plan> viewAggregate = viewTopPlanAndAggPair.value();
 
         Set<Expression> queryGroupByShuttledExpression = new 
HashSet<>(ExpressionUtils.shuttleExpressionWithLineage(
-                queryAggregate.getGroupByExpressions(), queryTopPlan, 
queryStructInfo.getTableBitSet()));
+                queryAggregate.getGroupByExpressions(), queryTopPlan));
 
         // try to eliminate group by dimension by function dependency if group 
by expression is not in query
         Map<Expression, Expression> 
viewShuttledExpressionQueryBasedToGroupByExpressionMap = new HashMap<>();
         List<Expression> viewGroupByExpressions = 
viewAggregate.getGroupByExpressions();
         List<? extends Expression> viewGroupByShuttledExpressions = 
ExpressionUtils.shuttleExpressionWithLineage(
-                viewGroupByExpressions, viewTopPlan, 
viewStructInfo.getTableBitSet());
+                viewGroupByExpressions, viewTopPlan);
 
         for (int index = 0; index < viewGroupByExpressions.size(); index++) {
             Expression viewExpression = viewGroupByExpressions.get(index);
@@ -712,8 +709,7 @@ public abstract class AbstractMaterializedViewAggregateRule 
extends AbstractMate
             if 
(ExpressionRewriteMode.EXPRESSION_ROLL_UP.equals(rewriteContext.getExpressionRewriteMode()))
 {
                 Expression queryFunctionShuttled = 
ExpressionUtils.shuttleExpressionWithLineage(
                         aggregateFunction,
-                        rewriteContext.getQueryTopPlan(),
-                        rewriteContext.getQueryTableBitSet());
+                        rewriteContext.getQueryTopPlan());
                 rewrittenFunction = rollup(aggregateFunction, 
queryFunctionShuttled,
                         
rewriteContext.getMvExprToMvScanExprQueryBasedMapping());
                 if (rewrittenFunction == null) {
@@ -788,16 +784,14 @@ public abstract class 
AbstractMaterializedViewAggregateRule extends AbstractMate
         private final ExpressionRewriteMode expressionRewriteMode;
         private final Map<Expression, Expression> 
mvExprToMvScanExprQueryBasedMapping;
         private final Plan queryTopPlan;
-        private final BitSet queryTableBitSet;
         private final Optional<SlotReference> groupingId;
 
         public AggregateExpressionRewriteContext(ExpressionRewriteMode 
expressionRewriteMode,
                 Map<Expression, Expression> 
mvExprToMvScanExprQueryBasedMapping, Plan queryTopPlan,
-                BitSet queryTableBitSet, Optional<SlotReference> groupingId) {
+                                                 Optional<SlotReference> 
groupingId) {
             this.expressionRewriteMode = expressionRewriteMode;
             this.mvExprToMvScanExprQueryBasedMapping = 
mvExprToMvScanExprQueryBasedMapping;
             this.queryTopPlan = queryTopPlan;
-            this.queryTableBitSet = queryTableBitSet;
             this.groupingId = groupingId;
         }
 
@@ -821,10 +815,6 @@ public abstract class 
AbstractMaterializedViewAggregateRule extends AbstractMate
             return queryTopPlan;
         }
 
-        public BitSet getQueryTableBitSet() {
-            return queryTableBitSet;
-        }
-
         public Optional<SlotReference> getGroupingId() {
             return groupingId;
         }
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 511d7d83b68..f56cb00c5d6 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
@@ -51,7 +51,6 @@ public abstract class AbstractMaterializedViewJoinRule 
extends AbstractMateriali
                 queryStructInfo.getTopPlan(),
                 materializationContext.getShuttledExprToScanExprMapping(),
                 targetToSourceMapping,
-                queryStructInfo.getTableBitSet(),
                 ImmutableMap.of(), cascadesContext
         );
         // Can not rewrite, bail out
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 036ffcd2251..c4b18a96b84 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
@@ -79,7 +79,6 @@ import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -128,18 +127,18 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
                     statementContext.getMaterializedViewRewriteDuration());
             return rewrittenPlans;
         }
-        for (MaterializationContext context : 
cascadesContext.getMaterializationContexts()) {
+        for (MaterializationContext materializationContext : 
cascadesContext.getMaterializationContexts()) {
             statementContext.getMaterializedViewStopwatch().reset().start();
-            if (checkIfRewritten(queryPlan, context)) {
+            if (checkIfRewritten(queryPlan, materializationContext)) {
                 continue;
             }
             // check mv plan is valid or not
-            if (!isMaterializationValid(queryPlan, cascadesContext, context)) {
+            if (!isMaterializationValid(queryPlan, cascadesContext, 
materializationContext)) {
                 continue;
             }
             // get query struct infos according to the view strut info, if 
valid query struct infos is empty, bail out
             List<StructInfo> queryStructInfos = 
getValidQueryStructInfos(queryPlan, cascadesContext,
-                    context.getCommonTableIdSet(statementContext));
+                    materializationContext);
             if (queryStructInfos.isEmpty()) {
                 continue;
             }
@@ -163,11 +162,11 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
                 }
                 try {
                     if (rewrittenPlans.size() < 
sessionVariable.getMaterializedViewRewriteSuccessCandidateNum()) {
-                        rewrittenPlans.addAll(doRewrite(queryStructInfo, 
cascadesContext, context));
+                        rewrittenPlans.addAll(doRewrite(queryStructInfo, 
cascadesContext, materializationContext));
                     }
                 } catch (Exception exception) {
                     LOG.warn("Materialized view rule exec fail", exception);
-                    context.recordFailReason(queryStructInfo,
+                    materializationContext.recordFailReason(queryStructInfo,
                             "Materialized view rule exec fail", 
exception::toString);
                 } finally {
                     elapsed = 
statementContext.getMaterializedViewStopwatch().elapsed(TimeUnit.MILLISECONDS);
@@ -185,18 +184,18 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
      * Get valid query struct infos, if invalid record the invalid reason
      */
     protected List<StructInfo> getValidQueryStructInfos(Plan queryPlan, 
CascadesContext cascadesContext,
-            BitSet materializedViewTableSet) {
+            MaterializationContext materializationContext) {
         List<StructInfo> validStructInfos = new ArrayList<>();
         // For every materialized view we should trigger refreshing struct 
info map
-        List<StructInfo> uncheckedStructInfos = 
MaterializedViewUtils.extractStructInfo(queryPlan, queryPlan,
-                cascadesContext, materializedViewTableSet);
-        uncheckedStructInfos.forEach(queryStructInfo -> {
+        List<StructInfo> uncheckedQueryStructInfos = 
MaterializedViewUtils.extractStructInfoFuzzy(queryPlan, queryPlan,
+                cascadesContext, 
materializationContext.getCommonTableIdSet(cascadesContext.getStatementContext()));
+        uncheckedQueryStructInfos.forEach(queryStructInfo -> {
             boolean valid = checkQueryPattern(queryStructInfo, 
cascadesContext) && queryStructInfo.isValid();
             if (!valid) {
                 cascadesContext.getMaterializationContexts().forEach(ctx ->
                         ctx.recordFailReason(queryStructInfo, "Query struct 
info is invalid",
                                 () -> String.format("query table bitmap is %s, 
plan is %s",
-                                        queryStructInfo.getTableBitSet(), 
queryPlan.treeString())
+                                        queryStructInfo.getRelations(), 
queryPlan.treeString())
                         ));
             } else {
                 validStructInfos.add(queryStructInfo);
@@ -277,9 +276,7 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
                 // Try to rewrite compensate predicates by using mv scan
                 List<Expression> rewriteCompensatePredicates = 
rewriteExpression(compensatePredicates.toList(),
                         queryPlan, 
materializationContext.getShuttledExprToScanExprMapping(),
-                        viewToQuerySlotMapping, 
queryStructInfo.getTableBitSet(),
-                        compensatePredicates.getRangePredicateMap(),
-                        cascadesContext);
+                        viewToQuerySlotMapping, 
compensatePredicates.getRangePredicateMap(), cascadesContext);
                 if (rewriteCompensatePredicates.isEmpty()) {
                     materializationContext.recordFailReason(queryStructInfo,
                             "Rewrite compensate predicate by view fail",
@@ -320,7 +317,7 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
                     && sessionVariable.isEnableMaterializedViewUnionRewrite()) 
{
                 MTMV mtmv = ((AsyncMaterializationContext) 
materializationContext).getMtmv();
                 Map<List<String>, Set<String>> queryUsedPartitions = 
PartitionCompensator.getQueryUsedPartitions(
-                        cascadesContext.getStatementContext(), 
queryStructInfo.getTableBitSet());
+                        cascadesContext.getStatementContext(), 
queryStructInfo.getRelationBitSet());
                 Set<MTMVRelatedTableIf> pctTables = 
mtmv.getMvPartitionInfo().getPctTables();
                 boolean relateTableUsedPartitionsAnyNull = false;
                 boolean relateTableUsedPartitionsAllEmpty = true;
@@ -465,12 +462,18 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
                     materializationContext);
             rewriteResults.add(rewrittenPlan);
             recordIfRewritten(queryStructInfo.getOriginalPlan(), 
materializationContext, cascadesContext);
-            // If rewrite successfully, try to clear mv scan currently because 
it maybe used again
-            materializationContext.clearScanPlan(cascadesContext);
+            resetMaterializationContext(materializationContext, 
cascadesContext);
         }
         return rewriteResults;
     }
 
+    // reset some materialization context state after one materialized view 
written successfully
+    private void resetMaterializationContext(MaterializationContext 
currentContext,
+                                             CascadesContext cascadesContext) {
+        // If rewrite successfully, try to clear mv scan currently because it 
maybe used again
+        currentContext.clearScanPlan(cascadesContext);
+    }
+
     // Set materialization context statistics to statementContext for cost 
estimate later
     // this should be called before MaterializationContext.clearScanPlan 
because clearScanPlan change the
     // mv scan plan relation id
@@ -563,14 +566,14 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
      *         then use the corresponding value of mapping to replace it
      */
     protected List<Expression> rewriteExpression(List<? extends Expression> 
sourceExpressionsToWrite, Plan sourcePlan,
-            ExpressionMapping targetExpressionMapping, SlotMapping 
targetToSourceMapping, BitSet sourcePlanBitSet,
+            ExpressionMapping targetExpressionMapping, SlotMapping 
targetToSourceMapping,
             Map<Expression, ExpressionInfo> queryExprToInfoMap, 
CascadesContext cascadesContext) {
         // Firstly, rewrite the target expression using source with inverse 
mapping
         // then try to use the target expression to represent the query. if 
any of source expressions
         // could not be represented by target expressions, return null.
         // generate target to target replacement expression mapping, and 
change target expression to source based
         List<? extends Expression> sourceShuttledExpressions = 
ExpressionUtils.shuttleExpressionWithLineage(
-                sourceExpressionsToWrite, sourcePlan, sourcePlanBitSet);
+                sourceExpressionsToWrite, sourcePlan);
         ExpressionMapping expressionMappingKeySourceBased = 
targetExpressionMapping.keyPermute(targetToSourceMapping);
         // target to target replacement expression mapping, because mv is 1:1 
so get the first element
         List<Map<Expression, Expression>> flattenExpressionMap = 
expressionMappingKeySourceBased.flattenMap();
@@ -892,7 +895,7 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
         }
         // query slot need shuttle to use table slot, avoid alias influence
         Set<Expression> queryUsedNeedRejectNullSlotsViewBased = 
ExpressionUtils.shuttleExpressionWithLineage(
-                        new ArrayList<>(queryNullRejectSlotSet), 
queryStructInfo.getTopPlan(), new BitSet()).stream()
+                        new ArrayList<>(queryNullRejectSlotSet), 
queryStructInfo.getTopPlan()).stream()
                 .map(expr -> ExpressionUtils.replace(expr, 
queryToViewMapping.toSlotReferenceMap()))
                 .collect(Collectors.toSet());
         // view slot need shuttle to use table slot, avoid alias influence
@@ -900,7 +903,7 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
         for (Set<Slot> requireNullableSlots : requireNoNullableViewSlot) {
             shuttledRequireNoNullableViewSlot.add(
                     ExpressionUtils.shuttleExpressionWithLineage(new 
ArrayList<>(requireNullableSlots),
-                                    viewStructInfo.getTopPlan(), new 
BitSet()).stream().map(Slot.class::cast)
+                                    
viewStructInfo.getTopPlan()).stream().map(Slot.class::cast)
                             .collect(Collectors.toSet()));
         }
         // query pulledUp predicates should have null reject predicates and 
contains any require noNullable slot
@@ -1076,7 +1079,7 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
         List<Expression> queryOrderKeysExpressions = queryOrderKeys.stream()
                 .map(OrderKey::getExpr).collect(Collectors.toList());
         List<? extends Expression> queryOrderByExpressionsShuttled = 
ExpressionUtils.shuttleExpressionWithLineage(
-                queryOrderKeysExpressions, queryStructInfo.getTopPlan(), 
queryStructInfo.getTableBitSet());
+                queryOrderKeysExpressions, queryStructInfo.getTopPlan());
 
         List<OrderKey> queryShuttledOrderKeys = new ArrayList<>();
         for (int i = 0; i < queryOrderKeys.size(); i++) {
@@ -1087,7 +1090,7 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
         List<OrderKey> viewShuttledOrderKeys = new ArrayList<>();
         List<? extends Expression> viewOrderByExpressionsShuttled = 
ExpressionUtils.shuttleExpressionWithLineage(
                 
viewOrderKeys.stream().map(OrderKey::getExpr).collect(Collectors.toList()),
-                viewStructInfo.getTopPlan(), new BitSet());
+                viewStructInfo.getTopPlan());
         List<Expression> viewOrderByExpressionsQueryBasedSet = 
ExpressionUtils.replace(
                 
viewOrderByExpressionsShuttled.stream().map(Expression.class::cast).collect(Collectors.toList()),
                 viewToQuerySlotMapping.toSlotReferenceMap());
@@ -1107,7 +1110,7 @@ public abstract class AbstractMaterializedViewRule 
implements ExplorationRuleFac
         // try to rewrite the order by expressions using the mv scan slot
         List<Expression> rewrittenExpressions = 
rewriteExpression(queryOrderKeysExpressions,
                 queryStructInfo.getTopPlan(), 
materializationContext.shuttledExprToScanExprMapping,
-                viewToQuerySlotMapping, queryStructInfo.getTableBitSet(), 
ImmutableMap.of(), cascadesContext);
+                viewToQuerySlotMapping, ImmutableMap.of(), cascadesContext);
         if (rewrittenExpressions.isEmpty()) {
             materializationContext.recordFailReason(queryStructInfo,
                     "query topN order keys rewrite fail, query topN order keys 
is not consistent "
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewScanRule.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewScanRule.java
index 24c63361f69..e542194f508 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewScanRule.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewScanRule.java
@@ -51,7 +51,6 @@ public abstract class AbstractMaterializedViewScanRule 
extends AbstractMateriali
                 queryStructInfo.getTopPlan(),
                 materializationContext.getShuttledExprToScanExprMapping(),
                 targetToSourceMapping,
-                queryStructInfo.getTableBitSet(),
                 ImmutableMap.of(), cascadesContext
         );
         // Can not rewrite, bail out
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewWindowRule.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewWindowRule.java
index 9a2913aca43..5be68758ef9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewWindowRule.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewWindowRule.java
@@ -102,7 +102,6 @@ public abstract class AbstractMaterializedViewWindowRule 
extends AbstractMateria
                 queryStructInfo.getTopPlan(),
                 materializationContext.getShuttledExprToScanExprMapping(),
                 viewToQuerySlotMapping,
-                queryStructInfo.getTableBitSet(),
                 ImmutableMap.of(), cascadesContext
         );
         // Can not rewrite, bail out
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 3386e4cd603..2ab84c2108b 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
@@ -108,6 +108,8 @@ public abstract class MaterializationContext {
     // for one materialization query may be multi when nested materialized 
view.
     protected final Multimap<ObjectId, Pair<String, String>> failReason = 
HashMultimap.create();
     protected List<String> identifier;
+    // The common table id set which is used in materialization, added for 
performance consideration
+    private BitSet commonTableIdSet;
 
     /**
      * MaterializationContext, this contains necessary info for query 
rewriting by materialization
@@ -121,7 +123,7 @@ public abstract class MaterializationContext {
                 && ExplainLevel.MEMO_PLAN == 
parsedStatement.getExplainOptions().getExplainLevel();
         // Construct materialization struct info, catch exception which may 
cause planner roll back
         this.structInfo = structInfo == null
-                ? constructStructInfo(plan, originalPlan, cascadesContext, new 
BitSet()).orElseGet(() -> null)
+                ? constructStructInfo(plan, originalPlan, 
cascadesContext).orElseGet(() -> null)
                 : structInfo;
         this.available = this.structInfo != null;
         if (available) {
@@ -135,22 +137,14 @@ public abstract class MaterializationContext {
      * @param originalPlan original plan, the output is right
      */
     public static Optional<StructInfo> constructStructInfo(Plan plan, Plan 
originalPlan,
-            CascadesContext cascadesContext, BitSet expectedTableBitSet) {
-        List<StructInfo> viewStructInfos;
+                                                           CascadesContext 
cascadesContext) {
         try {
-            viewStructInfos = MaterializedViewUtils.extractStructInfo(plan, 
originalPlan,
-                    cascadesContext, expectedTableBitSet);
-            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, 
materialization plan is %s",
-                        plan.treeString()));
-            }
+            return Optional.of(StructInfo.of(plan, originalPlan, 
cascadesContext));
         } catch (Exception exception) {
             LOG.warn(String.format("construct materialization struct info 
fail, materialization plan is %s",
                     plan.treeString()), exception);
             return Optional.empty();
         }
-        return Optional.of(viewStructInfos.get(0));
     }
 
     public boolean alreadyRewrite(GroupId groupId) {
@@ -382,16 +376,19 @@ public abstract class MaterializationContext {
     }
 
     /**
-     * get materialization context common table id by current statementContext
+     * get materialization context common table id by current 
currentQueryStatementContext
      */
-    public BitSet getCommonTableIdSet(StatementContext statementContext) {
-        BitSet commonTableId = new BitSet();
+    public BitSet getCommonTableIdSet(StatementContext 
currentQueryStatementContext) {
+        if (commonTableIdSet != null) {
+            return commonTableIdSet;
+        }
+        commonTableIdSet = new BitSet();
         for (StructInfoNode node : 
structInfo.getRelationIdStructInfoNodeMap().values()) {
             for (CatalogRelation catalogRelation : node.getCatalogRelation()) {
-                
commonTableId.set(statementContext.getTableId(catalogRelation.getTable()).asInt());
+                
commonTableIdSet.set(currentQueryStatementContext.getTableId(catalogRelation.getTable()).asInt());
             }
         }
-        return commonTableId;
+        return commonTableIdSet;
     }
 
     @Override
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 319703cfa07..4c3194703f5 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
@@ -271,31 +271,33 @@ public class MaterializedViewUtils {
      * Extract struct info from plan, support to get struct info from logical 
plan or plan in group.
      * @param plan maybe remove unnecessary plan node, and the logical output 
maybe wrong
      * @param originalPlan original plan, the output is right
+     * @param cascadesContext the cascadesContext when extractStructInfo
+     * @param targetTableIdSet the target relation id set which used to filter 
struct info,
+     *                            empty means no struct info match
      */
-    public static List<StructInfo> extractStructInfo(Plan plan, Plan 
originalPlan, CascadesContext cascadesContext,
-            BitSet materializedViewTableSet) {
+    public static List<StructInfo> extractStructInfoFuzzy(Plan plan, Plan 
originalPlan,
+                                                          CascadesContext 
cascadesContext, BitSet targetTableIdSet) {
         // If plan belong to some group, construct it with group struct info
         if (plan.getGroupExpression().isPresent()) {
             Group ownerGroup = plan.getGroupExpression().get().getOwnerGroup();
             StructInfoMap structInfoMap = ownerGroup.getStructInfoMap();
             // Refresh struct info in current level plan from top to bottom
             SessionVariable sessionVariable = 
cascadesContext.getConnectContext().getSessionVariable();
-            structInfoMap.refresh(ownerGroup, cascadesContext, new BitSet(), 
new HashSet<>(),
-                    sessionVariable.isEnableMaterializedViewNestRewrite());
-            
structInfoMap.setRefreshVersion(cascadesContext.getMemo().getRefreshVersion());
-            Set<BitSet> queryTableSets = structInfoMap.getTableMaps();
+            int memoVersion = StructInfoMap.getMemoVersion(targetTableIdSet,
+                    cascadesContext.getMemo().getRefreshVersion());
+            structInfoMap.refresh(ownerGroup, cascadesContext, 
targetTableIdSet, new HashSet<>(),
+                    sessionVariable.isEnableMaterializedViewNestRewrite(), 
memoVersion, true);
+            structInfoMap.setRefreshVersion(targetTableIdSet, 
cascadesContext.getMemo().getRefreshVersion());
+            Set<BitSet> queryTableIdSets = structInfoMap.getTableMaps(true);
             ImmutableList.Builder<StructInfo> structInfosBuilder = 
ImmutableList.builder();
-            if (!queryTableSets.isEmpty()) {
-                for (BitSet queryTableSet : queryTableSets) {
-                    BitSet queryCommonTableSet = 
MaterializedViewUtils.transformToCommonTableId(queryTableSet,
-                            
cascadesContext.getStatementContext().getRelationIdToCommonTableIdMap());
+            if (!queryTableIdSets.isEmpty()) {
+                for (BitSet queryTableIdSet : queryTableIdSets) {
                     // compare relation id corresponding table id
-                    if (!materializedViewTableSet.isEmpty()
-                            && !containsAll(materializedViewTableSet, 
queryCommonTableSet)) {
+                    if (!containsAll(targetTableIdSet, queryTableIdSet)) {
                         continue;
                     }
-                    StructInfo structInfo = 
structInfoMap.getStructInfo(cascadesContext, queryTableSet, ownerGroup,
-                            originalPlan, 
sessionVariable.isEnableMaterializedViewNestRewrite());
+                    StructInfo structInfo = 
structInfoMap.getStructInfo(cascadesContext, queryTableIdSet, ownerGroup,
+                            originalPlan, 
sessionVariable.isEnableMaterializedViewNestRewrite(), true);
                     if (structInfo != null) {
                         structInfosBuilder.add(structInfo);
                     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PartitionIncrementMaintainer.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PartitionIncrementMaintainer.java
index 46045ce50f3..4b29f95aeef 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PartitionIncrementMaintainer.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PartitionIncrementMaintainer.java
@@ -66,7 +66,6 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
 import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -200,7 +199,7 @@ public class PartitionIncrementMaintainer {
                 for (Set<Slot> equalSlotSet : shuttledEqualSlotSet) {
                     if (equalSlotSet.contains(consumerSlot)) {
                         Expression shuttledSlot = 
ExpressionUtils.shuttleExpressionWithLineage(
-                                producerSlot, producerPlan, new BitSet());
+                                producerSlot, producerPlan);
                         if (shuttledSlot instanceof Slot) {
                             equalSlotSet.add((Slot) shuttledSlot);
                         }
@@ -537,9 +536,9 @@ public class PartitionIncrementMaintainer {
                 Optional<Expression> partitionExpressionOpt = 
partitionTableColumnInfo.getPartitionExpression();
                 Expression partitionExpressionActual = partitionExpressionOpt
                         .map(expr -> 
ExpressionUtils.shuttleExpressionWithLineage(expr,
-                                context.getOriginalPlan(), new BitSet()))
+                                context.getOriginalPlan()))
                         .orElseGet(() -> 
ExpressionUtils.shuttleExpressionWithLineage(partitionNamedExpression,
-                                context.getOriginalPlan(), new BitSet()));
+                                context.getOriginalPlan()));
                 // merge date_trunc
                 partitionExpressionActual = new 
ExpressionNormalization().rewrite(partitionExpressionActual,
                         new 
ExpressionRewriteContext(context.getCascadesContext()));
@@ -547,7 +546,7 @@ public class PartitionIncrementMaintainer {
                 for (Expression projectSlotToCheck : expressionsToCheck) {
                     Expression expressionShuttledToCheck =
                             
ExpressionUtils.shuttleExpressionWithLineage(projectSlotToCheck,
-                                    context.getOriginalPlan(), new BitSet());
+                                    context.getOriginalPlan());
                     // merge date_trunc
                     expressionShuttledToCheck = new 
ExpressionNormalization().rewrite(expressionShuttledToCheck,
                             new 
ExpressionRewriteContext(context.getCascadesContext()));
@@ -831,7 +830,7 @@ public class PartitionIncrementMaintainer {
         List<Expression> extendedPartitionEqualSlotSet = new 
ArrayList<>(partitionEqualSlotSet);
         extendedPartitionEqualSlotSet.add(slot);
         List<? extends Expression> shuttledEqualExpressions = 
ExpressionUtils.shuttleExpressionWithLineage(
-                extendedPartitionEqualSlotSet, join, new BitSet());
+                extendedPartitionEqualSlotSet, join);
         for (Expression shuttledEqualExpression : shuttledEqualExpressions) {
             Set<Slot> objects = shuttledEqualExpression.collectToSet(expr -> 
expr instanceof SlotReference);
             if (objects.size() != 1 || !(shuttledEqualExpression instanceof 
SlotReference)) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PreMaterializedViewRewriter.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PreMaterializedViewRewriter.java
index 5eb4f82f0e3..d098c00e40b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PreMaterializedViewRewriter.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PreMaterializedViewRewriter.java
@@ -88,11 +88,9 @@ public class PreMaterializedViewRewriter {
         Pair<Map<List<String>, MaterializationContext>, BitSet> 
chosenMaterializationAndUsedTable
                 = 
MaterializedViewUtils.getChosenMaterializationAndUsedTable(physicalPlan,
                 cascadesContext.getAllMaterializationContexts());
-        // Calc the table id set which is used by physical plan
-        cascadesContext.getMemo().incrementAndGetRefreshVersion();
         // Extract logical plan by table id set by the corresponding best 
physical plan
         StructInfo structInfo = 
root.getStructInfoMap().getStructInfo(cascadesContext,
-                chosenMaterializationAndUsedTable.value(), root, null, true);
+                chosenMaterializationAndUsedTable.value(), root, null, true, 
false);
         if (structInfo == null) {
             LOG.error("preMaterializedViewRewriter rewrite structInfo is null, 
query id is {}",
                     cascadesContext.getConnectContext().getQueryIdentifier());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
index 5ccf624c510..bbd9f6181da 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
@@ -41,7 +41,6 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
 import java.util.ArrayList;
-import java.util.BitSet;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -117,7 +116,7 @@ public class Predicates {
 
         List<? extends Expression> viewPredicatesShuttled = 
ExpressionUtils.shuttleExpressionWithLineage(
                 
Lists.newArrayList(viewStructInfoPredicates.getCouldNotPulledUpPredicates()),
-                viewStructInfo.getTopPlan(), new BitSet());
+                viewStructInfo.getTopPlan());
         List<Expression> viewPredicatesQueryBased = 
ExpressionUtils.replace((List<Expression>) viewPredicatesShuttled,
                 viewToQuerySlotMapping.toSlotReferenceMap());
         // could not be pulled up predicates in query and view should be same
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
index 79c6d2ee119..a45863aa32c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
@@ -107,8 +107,8 @@ public class StructInfo {
     private final List<CatalogRelation> relations;
     // This is generated by cascadesContext, this may be different in 
different cascadesContext
     // So if the cascadesContext currently is different form the 
cascadesContext which generated it.
-    // Should regenerate the tableBitSet by current cascadesContext and call 
withTableBitSet method
-    private final BitSet tableBitSet;
+    // Should regenerate the relationBitSet by current cascadesContext and 
call withTableBitSet method
+    private final BitSet relationBitSet;
     // this is for LogicalCompatibilityContext later
     private final Map<RelationId, StructInfoNode> relationIdStructInfoNodeMap;
     // this recorde the predicates which can pull up, not shuttled
@@ -148,7 +148,7 @@ public class StructInfo {
             Map<ExpressionPosition, Multimap<Expression, Pair<Expression, 
HyperElement>>>
                     shuttledExpressionsToExpressionsMap,
             Map<ExpressionPosition, Map<Expression, Expression>> 
expressionToShuttledExpressionToMap,
-            BitSet tableIdSet,
+            BitSet relationIdSet,
             SplitPredicate splitPredicate,
             EquivalenceClass equivalenceClass,
             List<? extends Expression> planOutputShuttledExpressions) {
@@ -159,7 +159,7 @@ public class StructInfo {
         this.topPlan = topPlan;
         this.bottomPlan = bottomPlan;
         this.relations = relations;
-        this.tableBitSet = tableIdSet;
+        this.relationBitSet = relationIdSet;
         this.relationIdStructInfoNodeMap = relationIdStructInfoNodeMap;
         this.predicates = predicates;
         this.groupingId = groupingId;
@@ -177,7 +177,7 @@ public class StructInfo {
         return new StructInfo(this.originalPlan, this.originalPlanId, 
this.hyperGraph, this.valid, this.topPlan,
                 this.bottomPlan, this.relations, 
this.relationIdStructInfoNodeMap, predicates, this.groupingId,
                 this.shuttledExpressionsToExpressionsMap, 
this.expressionToShuttledExpressionToMap,
-                this.tableBitSet, null, null, 
this.planOutputShuttledExpressions);
+                this.relationBitSet, null, null, 
this.planOutputShuttledExpressions);
     }
 
     private static boolean collectStructInfoFromGraph(HyperGraph hyperGraph,
@@ -202,8 +202,7 @@ public class StructInfo {
             List<Expression> nodeExpressions = 
structInfoNode.getCouldMoveExpressions();
             if (nodeExpressions != null) {
                 List<? extends Expression> shuttledExpressions = 
ExpressionUtils.shuttleExpressionWithLineage(
-                        nodeExpressions, structInfoNode.getPlan(),
-                        new BitSet());
+                        nodeExpressions, structInfoNode.getPlan());
                 for (int index = 0; index < nodeExpressions.size(); index++) {
                     
putShuttledExpressionToExpressionsMap(shuttledExpressionsToExpressionsMap,
                             expressionToShuttledExpressionToMap, 
ExpressionPosition.NODE_COULD_MOVE,
@@ -213,8 +212,7 @@ public class StructInfo {
             nodeExpressions = structInfoNode.getCouldNotMoveExpressions();
             if (nodeExpressions != null) {
                 List<? extends Expression> shuttledExpressions = 
ExpressionUtils.shuttleExpressionWithLineage(
-                        nodeExpressions, structInfoNode.getPlan(),
-                        new BitSet());
+                        nodeExpressions, structInfoNode.getPlan());
                 for (int index = 0; index < nodeExpressions.size(); index++) {
                     
putShuttledExpressionToExpressionsMap(shuttledExpressionsToExpressionsMap,
                             expressionToShuttledExpressionToMap, 
ExpressionPosition.NODE_COULD_NOT_MOVE,
@@ -233,7 +231,7 @@ public class StructInfo {
             // Record the exprId to expr map in the processing to strut info
             // Replace expressions by expression map
             List<? extends Expression> shuttledExpressions = 
ExpressionUtils.shuttleExpressionWithLineage(
-                    joinConjunctExpressions, topPlan, new BitSet());
+                    joinConjunctExpressions, topPlan);
             for (int i = 0; i < shuttledExpressions.size(); i++) {
                 
putShuttledExpressionToExpressionsMap(shuttledExpressionsToExpressionsMap,
                         expressionToShuttledExpressionToMap,
@@ -245,7 +243,7 @@ public class StructInfo {
         hyperGraph.getFilterEdges().forEach(filterEdge -> {
             List<? extends Expression> filterExpressions = 
filterEdge.getExpressions();
             List<? extends Expression> shuttledExpressions = 
ExpressionUtils.shuttleExpressionWithLineage(
-                    filterExpressions, topPlan, new BitSet());
+                    filterExpressions, topPlan);
             for (int i = 0; i < shuttledExpressions.size(); i++) {
                 
putShuttledExpressionToExpressionsMap(shuttledExpressionsToExpressionsMap,
                         expressionToShuttledExpressionToMap,
@@ -260,7 +258,7 @@ public class StructInfo {
     private static Pair<SplitPredicate, EquivalenceClass> 
predicatesDerive(Predicates predicates, Plan originalPlan) {
         // construct equivalenceClass according to equals predicates
         List<Expression> shuttledExpression = 
ExpressionUtils.shuttleExpressionWithLineage(
-                        new ArrayList<>(predicates.getPulledUpPredicates()), 
originalPlan, new BitSet()).stream()
+                        new ArrayList<>(predicates.getPulledUpPredicates()), 
originalPlan).stream()
                 .map(Expression.class::cast)
                 .collect(Collectors.toList());
         SplitPredicate splitPredicate = 
Predicates.splitPredicates(ExpressionUtils.and(shuttledExpression));
@@ -311,13 +309,13 @@ public class StructInfo {
         Map<RelationId, StructInfoNode> relationIdStructInfoNodeMap = new 
LinkedHashMap<>();
         Map<ExpressionPosition, Multimap<Expression, Pair<Expression, 
HyperElement>>>
                 shuttledHashConjunctsToConjunctsMap = new LinkedHashMap<>();
-        BitSet tableBitSet = new BitSet();
+        BitSet relationBitSet = new BitSet();
         Map<ExpressionPosition, Map<Expression, Expression>> 
expressionToShuttledExpressionToMap = new HashMap<>();
         boolean valid = collectStructInfoFromGraph(hyperGraph, topPlan, 
shuttledHashConjunctsToConjunctsMap,
                 expressionToShuttledExpressionToMap,
                 relationList,
                 relationIdStructInfoNodeMap,
-                tableBitSet,
+                relationBitSet,
                 cascadesContext);
         valid = valid
                 && hyperGraph.getNodes().stream().allMatch(n -> 
((StructInfoNode) n).getExpressions() != null);
@@ -333,11 +331,11 @@ public class StructInfo {
                 predicateCollectorContext.getCouldNotPullUpPredicates());
         // this should use the output of originalPlan to make sure the output 
right order
         List<? extends Expression> planOutputShuttledExpressions =
-                
ExpressionUtils.shuttleExpressionWithLineage(originalPlan.getOutput(), 
originalPlan, new BitSet());
+                
ExpressionUtils.shuttleExpressionWithLineage(originalPlan.getOutput(), 
originalPlan);
         return new StructInfo(originalPlan, originalPlanId, hyperGraph, valid, 
topPlan, bottomPlan,
                 relationList, relationIdStructInfoNodeMap, predicates, 
planSplitContext.getGroupingId(),
                 shuttledHashConjunctsToConjunctsMap, 
expressionToShuttledExpressionToMap,
-                tableBitSet, null, null, planOutputShuttledExpressions);
+                relationBitSet, null, null, planOutputShuttledExpressions);
     }
 
     public List<CatalogRelation> getRelations() {
@@ -441,8 +439,8 @@ public class StructInfo {
         return originalPlanId;
     }
 
-    public BitSet getTableBitSet() {
-        return tableBitSet;
+    public BitSet getRelationBitSet() {
+        return relationBitSet;
     }
 
     public List<? extends Expression> getPlanOutputShuttledExpressions() {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/QueryPartitionCollector.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/QueryPartitionCollector.java
index 9907f5270e7..e0c833f95b1 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/QueryPartitionCollector.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/QueryPartitionCollector.java
@@ -36,7 +36,6 @@ import org.apache.logging.log4j.Logger;
 
 import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -56,9 +55,9 @@ public class QueryPartitionCollector extends 
DefaultPlanVisitor<Void, CascadesCo
         }
         StatementContext statementContext = context.getStatementContext();
         // Collect relationId to tableId mapping
-        Map<Integer, Integer> relationIdToTableId = 
statementContext.getRelationIdToCommonTableIdMap();
-        relationIdToTableId.put(catalogRelation.getRelationId().asInt(),
-                
statementContext.getTableId(catalogRelation.getTable()).asInt());
+        Multimap<Integer, Integer> relationIdToTableId = 
statementContext.getCommonTableIdToRelationIdMap();
+        
relationIdToTableId.put(statementContext.getTableId(catalogRelation.getTable()).asInt(),
+                catalogRelation.getRelationId().asInt());
         // Collect table used partition mapping
         Multimap<List<String>, Pair<RelationId, Set<String>>> 
tableUsedPartitionNameMap = statementContext
                 .getTableUsedPartitionNameMap();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
index 8f1ca1658de..78299b1d7d7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
@@ -301,15 +301,10 @@ public class ExpressionUtils {
         }
     }
 
-    public static Expression shuttleExpressionWithLineage(Expression 
expression, Plan plan, BitSet tableBitSet) {
+    public static Expression shuttleExpressionWithLineage(Expression 
expression, Plan plan) {
         return shuttleExpressionWithLineage(Lists.newArrayList(expression), 
plan).get(0);
     }
 
-    public static List<? extends Expression> 
shuttleExpressionWithLineage(List<? extends Expression> expressions,
-            Plan plan, BitSet tableBitSet) {
-        return shuttleExpressionWithLineage(expressions, plan);
-    }
-
     /**
      * Replace the slot in expressions with the lineage identifier from 
specifiedbaseTable sets or target table types
      * example as following:
@@ -317,7 +312,6 @@ public class ExpressionUtils {
      * select b - 5 as a, d from table
      * );
      * op expression before is: a + 10 as a1, d. after is: b - 5 + 10, d
-     * todo to get from plan struct info
      */
     public static List<? extends Expression> 
shuttleExpressionWithLineage(List<? extends Expression> expressions,
             Plan plan) {
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 d1ba92c7d1c..6be5527341b 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
@@ -27,6 +27,7 @@ import org.apache.doris.nereids.util.PlanChecker;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.SessionVariable;
 
+import com.google.common.collect.Multimap;
 import mockit.Mock;
 import mockit.MockUp;
 import org.junit.jupiter.api.Assertions;
@@ -61,10 +62,19 @@ class StructInfoMapTest extends SqlTestBase {
                 .rewrite()
                 .optimize();
         Group root = c1.getMemo().getRoot();
-        Set<BitSet> tableMaps = root.getStructInfoMap().getTableMaps();
+        Set<BitSet> tableMaps = root.getStructInfoMap().getTableMaps(true);
         Assertions.assertTrue(tableMaps.isEmpty());
-        root.getStructInfoMap().refresh(root, c1, new BitSet(), new 
HashSet<>(),
-                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite);
+
+        Multimap<Integer, Integer> commonTableIdToRelationIdMap
+                = c1.getStatementContext().getCommonTableIdToRelationIdMap();
+        BitSet targetBitSet = new BitSet();
+        for (Integer tableId : commonTableIdToRelationIdMap.keys()) {
+            targetBitSet.set(tableId);
+        }
+        c1.getMemo().incrementAndGetRefreshVersion(targetBitSet);
+        int memoVersion = StructInfoMap.getMemoVersion(targetBitSet, 
c1.getMemo().getRefreshVersion());
+        root.getStructInfoMap().refresh(root, c1, targetBitSet, new 
HashSet<>(),
+                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite, 
memoVersion, true);
         Assertions.assertEquals(1, tableMaps.size());
         new MockUp<MTMVRelationManager>() {
             @Mock
@@ -81,6 +91,7 @@ class StructInfoMapTest extends SqlTestBase {
         };
         connectContext.getSessionVariable().enableMaterializedViewRewrite = 
true;
         connectContext.getSessionVariable().enableMaterializedViewNestRewrite 
= true;
+        
connectContext.getSessionVariable().materializedViewRewriteDurationThresholdMs 
= 1000000;
 
         dropMvByNereids("drop materialized view if exists mv1");
         createMvByNereids("create materialized view mv1 BUILD IMMEDIATE 
REFRESH COMPLETE ON MANUAL\n"
@@ -102,9 +113,19 @@ class StructInfoMapTest extends SqlTestBase {
                 .optimize()
                 .printlnBestPlanTree();
         root = c1.getMemo().getRoot();
-        root.getStructInfoMap().refresh(root, c1, new BitSet(), new 
HashSet<>(),
-                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite);
-        tableMaps = root.getStructInfoMap().getTableMaps();
+        // because refresh struct info by targetBitSet when 
getValidQueryStructInfos, this would cause
+        // query struct info version increase twice. so need increase the memo 
version manually.
+        commonTableIdToRelationIdMap
+                = c1.getStatementContext().getCommonTableIdToRelationIdMap();
+        targetBitSet = new BitSet();
+        for (Integer tableId : commonTableIdToRelationIdMap.keys()) {
+            targetBitSet.set(tableId);
+        }
+        c1.getMemo().incrementAndGetRefreshVersion(targetBitSet);
+        memoVersion = StructInfoMap.getMemoVersion(targetBitSet, 
c1.getMemo().getRefreshVersion());
+        root.getStructInfoMap().refresh(root, c1, targetBitSet, new 
HashSet<>(),
+                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite, 
memoVersion, true);
+        tableMaps = root.getStructInfoMap().getTableMaps(true);
         Assertions.assertEquals(2, tableMaps.size());
         dropMvByNereids("drop materialized view mv1");
     }
@@ -130,12 +151,12 @@ class StructInfoMapTest extends SqlTestBase {
                 .rewrite()
                 .optimize();
         Group root = c1.getMemo().getRoot();
-        Set<BitSet> tableMaps = root.getStructInfoMap().getTableMaps();
+        Set<BitSet> tableMaps = root.getStructInfoMap().getTableMaps(true);
         Assertions.assertTrue(tableMaps.isEmpty());
         root.getStructInfoMap().refresh(root, c1, new BitSet(), new 
HashSet<>(),
-                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite);
+                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite, 0, true);
         root.getStructInfoMap().refresh(root, c1, new BitSet(), new 
HashSet<>(),
-                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite);
+                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite, 0, true);
         Assertions.assertEquals(1, tableMaps.size());
         new MockUp<MTMVRelationManager>() {
             @Mock
@@ -172,9 +193,19 @@ class StructInfoMapTest extends SqlTestBase {
                 .optimize()
                 .printlnBestPlanTree();
         root = c1.getMemo().getRoot();
-        root.getStructInfoMap().refresh(root, c1, new BitSet(), new 
HashSet<>(),
-                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite);
-        tableMaps = root.getStructInfoMap().getTableMaps();
+        // because refresh struct info by targetBitSet when 
getValidQueryStructInfos, this would cause
+        // query struct info version increase twice. so need increase the memo 
version manually.
+        Multimap<Integer, Integer> commonTableIdToRelationIdMap
+                = c1.getStatementContext().getCommonTableIdToRelationIdMap();
+        BitSet targetBitSet = new BitSet();
+        for (Integer relationId : commonTableIdToRelationIdMap.values()) {
+            targetBitSet.set(relationId);
+        }
+        c1.getMemo().incrementAndGetRefreshVersion(targetBitSet);
+        int memoVersion = StructInfoMap.getMemoVersion(targetBitSet, 
c1.getMemo().getRefreshVersion());
+        root.getStructInfoMap().refresh(root, c1, targetBitSet, new 
HashSet<>(),
+                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite, 
memoVersion, true);
+        tableMaps = root.getStructInfoMap().getTableMaps(true);
         Assertions.assertEquals(2, tableMaps.size());
         dropMvByNereids("drop materialized view mv1");
     }
@@ -229,15 +260,25 @@ class StructInfoMapTest extends SqlTestBase {
                 .preMvRewrite()
                 .optimize();
         Group root = c1.getMemo().getRoot();
-        root.getStructInfoMap().refresh(root, c1, new BitSet(), new 
HashSet<>(),
-                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite);
+        // because refresh struct info by targetBitSet when 
getValidQueryStructInfos, this would cause
+        // query struct info version increase twice. so need increase the memo 
version manually.
+        Multimap<Integer, Integer> commonTableIdToRelationIdMap
+                = c1.getStatementContext().getCommonTableIdToRelationIdMap();
+        BitSet targetBitSet = new BitSet();
+        for (Integer relationId : commonTableIdToRelationIdMap.values()) {
+            targetBitSet.set(relationId);
+        }
+        c1.getMemo().incrementAndGetRefreshVersion(targetBitSet);
+        int memoVersion = StructInfoMap.getMemoVersion(targetBitSet, 
c1.getMemo().getRefreshVersion());
+        root.getStructInfoMap().refresh(root, c1, targetBitSet, new 
HashSet<>(),
+                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite, 
memoVersion, true);
         StructInfoMap structInfoMap = root.getStructInfoMap();
-        Assertions.assertEquals(2, structInfoMap.getTableMaps().size());
-        BitSet mvMap = structInfoMap.getTableMaps().stream()
+        Assertions.assertEquals(2, structInfoMap.getTableMaps(true).size());
+        BitSet mvMap = structInfoMap.getTableMaps(true).stream()
                 .filter(b -> b.cardinality() == 2)
                 .collect(Collectors.toList()).get(0);
         StructInfo structInfo = structInfoMap.getStructInfo(c1, mvMap, root, 
null,
-                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite);
+                
connectContext.getSessionVariable().enableMaterializedViewNestRewrite, true);
         System.out.println(structInfo.getOriginalPlan().treeString());
         BitSet bitSet = new BitSet();
         for (CatalogRelation relation : structInfo.getRelations()) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PointQueryShouldNotMvRewriteTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PointQueryShouldNotMvRewriteTest.java
index 423c996a26b..0b8881cc63d 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PointQueryShouldNotMvRewriteTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PointQueryShouldNotMvRewriteTest.java
@@ -85,8 +85,9 @@ public class PointQueryShouldNotMvRewriteTest extends 
SqlTestBase {
                 .optimize()
                 .printlnBestPlanTree();
         Group root = c1.getMemo().getRoot();
-        root.getStructInfoMap().refresh(root, c1, new BitSet(), new 
HashSet<>(), false);
-        Set<BitSet> tableMaps = root.getStructInfoMap().getTableMaps();
+        root.getStructInfoMap().refresh(root, c1, new BitSet(), new 
HashSet<>(), false, 0,
+                false);
+        Set<BitSet> tableMaps = root.getStructInfoMap().getTableMaps(false);
         Assertions.assertEquals(1, tableMaps.size());
         dropMvByNereids("drop materialized view mv1");
     }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PreMaterializedViewRewriterTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PreMaterializedViewRewriterTest.java
index 11fc0a27de8..95bd0d293a0 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PreMaterializedViewRewriterTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PreMaterializedViewRewriterTest.java
@@ -2967,9 +2967,9 @@ public class PreMaterializedViewRewriterTest extends 
SqlTestBase {
         // extract plan from memo and check is equals or not
         Memo memo = cascadesContext.getMemo();
         for (Map.Entry<BitSet, LogicalPlan> planEntry : 
bitSetLogicalPlanMap.entrySet()) {
-            memo.incrementAndGetRefreshVersion();
+            memo.incrementAndGetRefreshVersion(planEntry.getKey());
             StructInfo structInfo = 
memo.getRoot().getStructInfoMap().getStructInfo(cascadesContext,
-                    planEntry.getKey(), memo.getRoot(), null, true);
+                    planEntry.getKey(), memo.getRoot(), null, true, false);
             Assertions.assertNotNull(structInfo);
             
Assertions.assertTrue(structInfo.getOriginalPlan().deepEquals(planEntry.getValue()));
         }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PredicatesTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PredicatesTest.java
index 6b5e0596205..473ebb4908f 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PredicatesTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/PredicatesTest.java
@@ -20,7 +20,6 @@ package org.apache.doris.nereids.mv;
 import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.rules.exploration.mv.ComparisonResult;
 import org.apache.doris.nereids.rules.exploration.mv.HyperGraphComparator;
-import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewUtils;
 import org.apache.doris.nereids.rules.exploration.mv.Predicates;
 import org.apache.doris.nereids.rules.exploration.mv.Predicates.ExpressionInfo;
 import org.apache.doris.nereids.rules.exploration.mv.StructInfo;
@@ -34,7 +33,6 @@ import org.apache.doris.nereids.util.PlanChecker;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
-import java.util.BitSet;
 import java.util.Map;
 
 /** Test the method in Predicates*/
@@ -107,10 +105,8 @@ public class PredicatesTest extends SqlTestBase {
                 .rewrite()
                 .getAllPlan().get(0).child(0);
 
-        StructInfo mvStructInfo = 
MaterializedViewUtils.extractStructInfo(mvPlan, mvPlan,
-                mvContext, new BitSet()).get(0);
-        StructInfo queryStructInfo = 
MaterializedViewUtils.extractStructInfo(queryPlan, queryPlan,
-                queryContext, new BitSet()).get(0);
+        StructInfo mvStructInfo = StructInfo.of(mvPlan, mvPlan, mvContext);
+        StructInfo queryStructInfo = StructInfo.of(queryPlan, queryPlan, 
queryContext);
         RelationMapping relationMapping = 
RelationMapping.generate(mvStructInfo.getRelations(),
                         queryStructInfo.getRelations(), 16).get(0);
 
@@ -163,10 +159,8 @@ public class PredicatesTest extends SqlTestBase {
                 .rewrite()
                 .getAllPlan().get(0).child(0);
 
-        StructInfo mvStructInfo = 
MaterializedViewUtils.extractStructInfo(mvPlan, mvPlan,
-                mvContext, new BitSet()).get(0);
-        StructInfo queryStructInfo = 
MaterializedViewUtils.extractStructInfo(queryPlan, queryPlan,
-                queryContext, new BitSet()).get(0);
+        StructInfo mvStructInfo = StructInfo.of(mvPlan, mvPlan, mvContext);
+        StructInfo queryStructInfo = StructInfo.of(queryPlan, queryPlan, 
queryContext);
         RelationMapping relationMapping = 
RelationMapping.generate(mvStructInfo.getRelations(),
                 queryStructInfo.getRelations(), 16).get(0);
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphAggTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphAggTest.java
index b62c41db00d..af36afe7151 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphAggTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphAggTest.java
@@ -31,7 +31,6 @@ import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
-import java.util.BitSet;
 import java.util.Objects;
 
 class HyperGraphAggTest extends SqlTestBase {
@@ -89,10 +88,8 @@ class HyperGraphAggTest extends SqlTestBase {
     }
 
     LogicalCompatibilityContext constructContext(Plan p1, Plan p2) {
-        StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1, p1,
-                null, new BitSet()).get(0);
-        StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2, p2,
-                null, new BitSet()).get(0);
+        StructInfo st1 = StructInfo.of(p1, p1, null);
+        StructInfo st2 = StructInfo.of(p2, p2, null);
         RelationMapping rm = RelationMapping.generate(st1.getRelations(), 
st2.getRelations(), 8)
                 .get(0);
         SlotMapping sm = SlotMapping.generate(rm);
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlTestBase.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlTestBase.java
index a34ec7efb15..1be34d85bef 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlTestBase.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/sqltest/SqlTestBase.java
@@ -21,7 +21,6 @@ import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.MetaIdGenerator.IdGeneratorBuffer;
 import org.apache.doris.nereids.CascadesContext;
 import 
org.apache.doris.nereids.rules.exploration.mv.LogicalCompatibilityContext;
-import org.apache.doris.nereids.rules.exploration.mv.MaterializedViewUtils;
 import org.apache.doris.nereids.rules.exploration.mv.StructInfo;
 import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping;
 import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
@@ -30,8 +29,6 @@ import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.util.MemoPatternMatchSupported;
 import org.apache.doris.utframe.TestWithFeService;
 
-import java.util.BitSet;
-
 public abstract class SqlTestBase extends TestWithFeService implements 
MemoPatternMatchSupported {
     @Override
     protected void runBeforeAll() throws Exception {
@@ -840,10 +837,8 @@ public abstract class SqlTestBase extends 
TestWithFeService implements MemoPatte
     }
 
     protected LogicalCompatibilityContext constructContext(Plan p1, Plan p2, 
CascadesContext context) {
-        StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1, p1,
-                context, new BitSet()).get(0);
-        StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2, p2,
-                context, new BitSet()).get(0);
+        StructInfo st1 = StructInfo.of(p1, p1, context);
+        StructInfo st2 = StructInfo.of(p2, p2, context);
         RelationMapping rm = RelationMapping.generate(st1.getRelations(), 
st2.getRelations(), 8)
                 .get(0);
         SlotMapping sm = SlotMapping.generate(rm);
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java
index 1e952255891..c390f1b2351 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java
@@ -41,7 +41,6 @@ import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import java.util.Arrays;
-import java.util.BitSet;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -226,7 +225,7 @@ public class ExpressionUtilsTest extends TestWithFeService {
         LogicalProject<LogicalOdbcScan> project = new 
LogicalProject<>(ImmutableList.of(a, bAlias),
                 new LogicalOdbcScan(new RelationId(0), olapTable, 
ImmutableList.of("test")));
         List<? extends Expression> expressions = 
ExpressionUtils.shuttleExpressionWithLineage(project.getOutput(),
-                project, new BitSet());
+                project);
         // should not loop, should break out loop
         Assertions.assertEquals(expressions, ImmutableList.of(a, 
bAlias.toSlot()));
     }
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 30ea11ec956..89e927c3f93 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
@@ -214,19 +214,19 @@ suite("nested_mtmv") {
         group by l_orderkey, l_linenumber, l_partkey, o_orderkey, o_custkey, 
ps_partkey
         """
     def mv_level1_name = "mv_level1_name"
-    def mv_stmt_5 = """
+    def mv_level2_stmt = """
         select l_orderkey, l_linenumber, l_partkey, o_orderkey, o_custkey, 
ps_partkey, col1
         from ${mv_level1_name}
         """
     def mv_level2_name = "mv_level2_name"
-    def mv_stmt_6 = """
+    def mv_level3_stmt = """
         select t1.l_orderkey, t2.l_linenumber, t1.l_partkey, t2.o_orderkey, 
t1.o_custkey, t2.ps_partkey, t1.col1
         from ${mv_level1_name} as t1
         left join ${mv_level1_name} as t2 
         on t1.l_orderkey = t2.l_orderkey
         """
     def mv_level3_name = "mv_level3_name"
-    def mv_stmt_7 = """
+    def mv_level4_stmt = """
         select t1.l_orderkey, t2.l_linenumber, t1.l_partkey, t2.o_orderkey, 
t1.o_custkey, t2.ps_partkey, t1.col1
         from ${mv_level2_name} as t1
         left join ${mv_level2_name} as t2 
@@ -236,11 +236,11 @@ suite("nested_mtmv") {
 
     create_async_mv(db, mv_level1_name, mv_stmt_4)
 
-    create_async_mv(db, mv_level2_name, mv_stmt_5)
+    create_async_mv(db, mv_level2_name, mv_level2_stmt)
 
-    create_async_mv(db, mv_level3_name, mv_stmt_6)
+    create_async_mv(db, mv_level3_name, mv_level3_stmt)
 
-    create_async_mv(db, mv_level4_name, mv_stmt_7)
+    create_async_mv(db, mv_level4_name, mv_level4_stmt)
 
     def query_stmt_2 = """
         select t1.l_orderkey, t2.l_linenumber, t1.l_partkey, t2.o_orderkey, 
t1.o_custkey, t2.ps_partkey, t1.col1


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to