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]