This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 99ad5a83482 [Fix](nereids) Fix cte rewrite by mv failure and
predicates compensation by mistake (#29820)
99ad5a83482 is described below
commit 99ad5a8348274e41fa136ff80ce1c332dd74c99f
Author: seawinde <[email protected]>
AuthorDate: Fri Jan 12 16:30:35 2024 +0800
[Fix](nereids) Fix cte rewrite by mv failure and predicates compensation by
mistake (#29820)
Fix cte rewrite by mv wrongly when query has scalar aggregate but view no
For example as following, it should not be rewritten by materialized view
successfully
// materialzied view define
def mv20_1 = """
select
l_shipmode,
l_shipinstruct,
sum(l_extendedprice),
count()
from lineitem
left join
orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY
group by
l_shipmode,
l_shipinstruct;
"""
// query sql
def query20_1 =
"""
select
sum(l_extendedprice),
count()
from lineitem
left join
orders
on lineitem.L_ORDERKEY = orders.O_ORDERKEY
"""
Fix predicates compensation by mistake
For example as following, it can return right result, but it's wrong
earlier.
// materialzied view define
def mv7_1 = """
select l_shipdate, o_orderdate, l_partkey, l_suppkey
from lineitem
left join orders
on lineitem.l_orderkey = orders.o_orderkey
where l_shipdate = '2023-12-08' and o_orderdate = '2023-12-08';
"""
// query sql
def query7_1 = """
select l_shipdate, o_orderdate, l_partkey, l_suppkey
from (select * from lineitem where l_shipdate = '2023-10-17' ) t1
left join orders
on t1.l_orderkey = orders.o_orderkey;
"""
and optimize some code usage and add more comment for method
---
.../query-async-materialized-view.md | 1 -
.../query-async-materialized-view.md | 1 -
.../mv/AbstractMaterializedViewAggregateRule.java | 28 ++-
.../mv/AbstractMaterializedViewRule.java | 215 ++++++---------------
.../mv/LogicalCompatibilityContext.java | 8 +-
.../exploration/mv/MaterializedViewUtils.java | 18 ++
.../nereids/rules/exploration/mv/Predicates.java | 178 +++++++++++++++--
.../nereids/rules/exploration/mv/StructInfo.java | 32 ++-
.../plans/visitor/ExpressionLineageReplacer.java | 25 ++-
.../joinorder/hypergraph/CompareOuterJoinTest.java | 6 +-
.../jobs/joinorder/hypergraph/InferJoinTest.java | 6 +-
.../joinorder/hypergraph/InferPredicateTest.java | 6 +-
.../joinorder/hypergraph/PullupExpressionTest.java | 6 +-
.../exploration/mv/HyperGraphComparatorTest.java | 4 +-
.../mv/agg_with_roll_up/aggregate_with_roll_up.out | 36 ++++
.../aggregate_without_roll_up.out | 28 +++
.../nereids_rules_p0/mv/join/inner/inner_join.out | 16 ++
.../mv/join/left_outer/outer_join.out | 44 +++++
.../agg_with_roll_up/aggregate_with_roll_up.groovy | 90 +++++++--
.../aggregate_without_roll_up.groovy | 107 +++++++++-
.../mv/join/inner/inner_join.groovy | 35 ++--
.../mv/join/left_outer/outer_join.groovy | 82 ++++++--
22 files changed, 716 insertions(+), 256 deletions(-)
diff --git
a/docs/en/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
b/docs/en/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
index 4c6418e7606..8cd13b4e43a 100644
---
a/docs/en/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
+++
b/docs/en/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
@@ -398,7 +398,6 @@ If you want to know the detailed information about
materialized view candidates,
| SET enable_nereids_planner = true; |
Asynchronous materialized views are only supported under the new optimizer, so
the new optimizer needs to be enabled. |
| SET enable_materialized_view_rewrite = true; |
Enable or disable query transparent rewriting, default is disabled
|
| SET materialized_view_rewrite_enable_contain_external_table = true; |
Whether materialized views participating in transparent rewriting are allowed
to contain external tables, default is not allowed |
-| SET disable_nereids_rules = 'ELIMINATE_OUTER_JOIN'; |
Currently, outer join elimination has an impact on transparent |
## Limitations
diff --git
a/docs/zh-CN/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
b/docs/zh-CN/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
index d35a5383dc8..1921498f85c 100644
---
a/docs/zh-CN/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
+++
b/docs/zh-CN/docs/query-acceleration/async-materialized-view/query-async-materialized-view.md
@@ -380,7 +380,6 @@ WHERE o_orderkey > 5 AND o_orderkey <= 10;
| SET enable_nereids_planner = true; |
异步物化视图只有在新优化器下才支持,所以需要开启新优化器 |
| SET enable_materialized_view_rewrite = true; |
开启或者关闭查询透明改写,默认关闭 |
| SET materialized_view_rewrite_enable_contain_external_table = true; |
参与透明改写的物化视图是否允许包含外表,默认不允许 |
-| SET disable_nereids_rules = 'ELIMINATE_OUTER_JOIN'; | 目前
outer join 消除会对透明改写有影响,暂时需要关闭,后面会优化 |
## 限制
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 2804a0814f6..e47e15dd56b 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
@@ -48,8 +48,6 @@ import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.Collection;
@@ -68,8 +66,6 @@ public abstract class AbstractMaterializedViewAggregateRule
extends AbstractMate
protected static final Multimap<Expression, Expression>
AGGREGATE_ROLL_UP_EQUIVALENT_FUNCTION_MAP =
ArrayListMultimap.create();
- protected final String currentClassName = this.getClass().getSimpleName();
- private final Logger logger = LogManager.getLogger(this.getClass());
static {
// support count distinct roll up
@@ -142,6 +138,18 @@ public abstract class
AbstractMaterializedViewAggregateRule extends AbstractMate
materializationContext.getMvExprToMvScanExprMapping(),
queryToViewSlotMapping)));
}
+ // if view is scalar aggregate but query is not. Or if query is scalar
aggregate but view is not
+ // Should not rewrite
+ if (queryTopPlanAndAggPair.value().getGroupByExpressions().isEmpty()
+ ||
viewTopPlanAndAggPair.value().getGroupByExpressions().isEmpty()) {
+
materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
+ Pair.of("only one the of query or view is scalar aggregate
and "
+ + "can not rewrite expression meanwhile",
+ String.format("query aggregate = %s,\n view
aggregate = %s,\n",
+
queryTopPlanAndAggPair.value().treeString(),
+
viewTopPlanAndAggPair.value().treeString())));
+ return null;
+ }
// try to roll up.
// split the query top plan expressions to group expressions and
functions, if can not, bail out.
Pair<Set<? extends Expression>, Set<? extends Expression>>
queryGroupAndFunctionPair
@@ -268,15 +276,15 @@ public abstract class
AbstractMaterializedViewAggregateRule extends AbstractMate
Plan viewTopPlan = viewTopPlanAndAggPair.key();
LogicalAggregate<Plan> queryAggregate = queryTopPlanAndAggPair.value();
LogicalAggregate<Plan> viewAggregate = viewTopPlanAndAggPair.value();
- List<? extends Expression> queryGroupShuttledExpression =
ExpressionUtils.shuttleExpressionWithLineage(
- queryAggregate.getGroupByExpressions(), queryTopPlan);
- List<? extends Expression> viewGroupShuttledExpression =
ExpressionUtils.shuttleExpressionWithLineage(
+ Set<? extends Expression> queryGroupShuttledExpression = new HashSet<>(
+ ExpressionUtils.shuttleExpressionWithLineage(
+ queryAggregate.getGroupByExpressions(), queryTopPlan));
+ Set<? extends Expression> viewGroupShuttledExpressionQueryBased =
ExpressionUtils.shuttleExpressionWithLineage(
viewAggregate.getGroupByExpressions(), viewTopPlan)
.stream()
.map(expr -> ExpressionUtils.replace(expr,
viewToQurySlotMapping.toSlotReferenceMap()))
- .collect(Collectors.toList());
- return queryAggregate.getGroupByExpressions().size() ==
viewAggregate.getGroupByExpressions().size()
- &&
queryGroupShuttledExpression.equals(viewGroupShuttledExpression);
+ .collect(Collectors.toSet());
+ return
queryGroupShuttledExpression.equals(viewGroupShuttledExpressionQueryBased);
}
/**
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 7e87fd4c15a..352bccc019b 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
@@ -31,16 +31,13 @@ import org.apache.doris.nereids.jobs.executor.Rewriter;
import org.apache.doris.nereids.memo.GroupExpression;
import org.apache.doris.nereids.rules.exploration.ExplorationRuleFactory;
import org.apache.doris.nereids.rules.exploration.mv.Predicates.SplitPredicate;
-import
org.apache.doris.nereids.rules.exploration.mv.mapping.EquivalenceClassSetMapping;
import org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionMapping;
import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping;
import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
import org.apache.doris.nereids.trees.expressions.Alias;
-import org.apache.doris.nereids.trees.expressions.EqualTo;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.expressions.functions.scalar.NonNullable;
import org.apache.doris.nereids.trees.expressions.functions.scalar.Nullable;
import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
@@ -61,7 +58,6 @@ import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -85,7 +81,7 @@ public abstract class AbstractMaterializedViewRule implements
ExplorationRuleFac
if (materializationContexts.isEmpty()) {
return rewriteResults;
}
- List<StructInfo> queryStructInfos = extractStructInfo(queryPlan,
cascadesContext);
+ List<StructInfo> queryStructInfos =
MaterializedViewUtils.extractStructInfo(queryPlan, cascadesContext);
// TODO Just Check query queryPlan firstly, support multi later.
StructInfo queryStructInfo = queryStructInfos.get(0);
if (!checkPattern(queryStructInfo)) {
@@ -99,7 +95,8 @@ public abstract class AbstractMaterializedViewRule implements
ExplorationRuleFac
if (checkIfRewritten(queryPlan, materializationContext)) {
continue;
}
- List<StructInfo> viewStructInfos =
extractStructInfo(materializationContext.getMvPlan(), cascadesContext);
+ List<StructInfo> viewStructInfos =
MaterializedViewUtils.extractStructInfo(
+ materializationContext.getMvPlan(), cascadesContext);
if (viewStructInfos.size() > 1) {
// view struct info should only have one
materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
@@ -145,16 +142,10 @@ public abstract class AbstractMaterializedViewRule
implements ExplorationRuleFac
comparisonResult.getErrorMessage()));
continue;
}
- // TODO: Use set of list? And consider view expr
- List<Expression> pulledUpExpressions =
ImmutableList.copyOf(comparisonResult.getQueryExpressions());
- // set pulled up expression to queryStructInfo predicates and
update related predicates
- if (!pulledUpExpressions.isEmpty()) {
- queryStructInfo.addPredicates(pulledUpExpressions);
- }
SplitPredicate compensatePredicates =
predicatesCompensate(queryStructInfo, viewStructInfo,
queryToViewSlotMapping, comparisonResult,
cascadesContext);
// Can not compensate, bail out
- if (compensatePredicates.isEmpty()) {
+ if (compensatePredicates.isInvalid()) {
materializationContext.recordFailReason(queryStructInfo.getOriginalPlanId(),
Pair.of("Predicate compensate fail",
String.format("query predicates = %s,\n
query equivalenceClass = %s, \n"
@@ -222,6 +213,9 @@ public abstract class AbstractMaterializedViewRule
implements ExplorationRuleFac
return rewriteResults;
}
+ /**
+ * Check the logical properties of rewritten plan by mv is the same with
source plan
+ */
protected boolean checkOutput(Plan sourcePlan, Plan rewrittenPlan,
MaterializationContext materializationContext) {
if (sourcePlan.getGroupExpression().isPresent() &&
!rewrittenPlan.getLogicalProperties()
.equals(sourcePlan.getGroupExpression().get().getOwnerGroup().getLogicalProperties()))
{
@@ -238,7 +232,7 @@ public abstract class AbstractMaterializedViewRule
implements ExplorationRuleFac
}
/**
- * Partition will be pruned in query then add the record the partitions to
select partitions on
+ * Partition will be pruned in query then add the pruned partitions to
select partitions field of
* catalog relation.
* Maybe only just some partitions is valid in materialized view, so we
should check if the mv can
* offer the partitions which query used or not.
@@ -285,10 +279,23 @@ public abstract class AbstractMaterializedViewRule
implements ExplorationRuleFac
* Use target expression to represent the source expression. Visit the
source expression,
* try to replace the source expression with target expression in
targetExpressionMapping, if found then
* replace the source expression by target expression mapping value.
- * Note: make the target expression map key to source based according to
targetExpressionNeedSourceBased,
- * if targetExpressionNeedSourceBased is true, we should make it source
based.
- * the key expression in targetExpressionMapping should be shuttled. with
the method
- * ExpressionUtils.shuttleExpressionWithLineage.
+ *
+ * @param sourceExpressionsToWrite the source expression to write by
target expression
+ * @param sourcePlan the source plan witch the source expression belong to
+ * @param targetExpressionMapping target expression mapping, if finding
the expression in key set of the mapping
+ * then use the corresponding value of mapping to replace it
+ * @param targetExpressionNeedSourceBased if
targetExpressionNeedSourceBased is true,
+ * we should make the target expression map key to source based,
+ * Note: the key expression in targetExpressionMapping should be
shuttled. with the method
+ * ExpressionUtils.shuttleExpressionWithLineage.
+ * example as following:
+ * source target
+ * project(slot 1, 2) project(slot 3, 2, 1)
+ * scan(table) scan(table)
+ * then
+ * transform source to:
+ * project(slot 2, 1)
+ * target
*/
protected List<Expression> rewriteExpression(List<? extends Expression>
sourceExpressionsToWrite, Plan sourcePlan,
ExpressionMapping targetExpressionMapping, SlotMapping
sourceToTargetMapping,
@@ -296,15 +303,6 @@ public abstract class AbstractMaterializedViewRule
implements ExplorationRuleFac
// 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
// can not be represented by target expressions, return null.
- //
- // example as following:
- // source target
- // project(slot 1, 2) project(slot 3, 2, 1)
- // scan(table) scan(table)
- //
- // transform source to:
- // project(slot 2, 1)
- // target
// generate target to target replacement expression mapping, and
change target expression to source based
List<? extends Expression> sourceShuttledExpressions =
ExpressionUtils.shuttleExpressionWithLineage(
sourceExpressionsToWrite, sourcePlan);
@@ -348,6 +346,9 @@ public abstract class AbstractMaterializedViewRule
implements ExplorationRuleFac
return rewrittenExpressions;
}
+ /**
+ * Rewrite single expression, the logic is the same with above
+ */
protected Expression rewriteExpression(Expression
sourceExpressionsToWrite, Plan sourcePlan,
ExpressionMapping targetExpressionMapping, SlotMapping
sourceToTargetMapping,
boolean targetExpressionNeedSourceBased) {
@@ -374,27 +375,43 @@ public abstract class AbstractMaterializedViewRule
implements ExplorationRuleFac
ComparisonResult comparisonResult,
CascadesContext cascadesContext
) {
+ // TODO: Use set of list? And consider view expr
+ List<Expression> queryPulledUpExpressions =
ImmutableList.copyOf(comparisonResult.getQueryExpressions());
+ // set pulled up expression to queryStructInfo predicates and update
related predicates
+ if (!queryPulledUpExpressions.isEmpty()) {
+ queryStructInfo.addPredicates(queryPulledUpExpressions);
+ }
+ List<Expression> viewPulledUpExpressions =
ImmutableList.copyOf(comparisonResult.getViewExpressions());
+ // set pulled up expression to viewStructInfo predicates and update
related predicates
+ if (!viewPulledUpExpressions.isEmpty()) {
+ viewStructInfo.addPredicates(viewPulledUpExpressions);
+ }
// viewEquivalenceClass to query based
SlotMapping viewToQuerySlotMapping = queryToViewSlotMapping.inverse();
- final Set<Expression> equalCompensateConjunctions =
compensateEquivalence(
+ // equal predicate compensate
+ final Set<Expression> equalCompensateConjunctions =
Predicates.compensateEquivalence(
queryStructInfo,
viewStructInfo,
viewToQuerySlotMapping,
comparisonResult);
// range compensate
- final Set<Expression> rangeCompensatePredicates =
compensateRangePredicate(
+ final Set<Expression> rangeCompensatePredicates =
Predicates.compensateRangePredicate(
queryStructInfo,
viewStructInfo,
viewToQuerySlotMapping,
comparisonResult);
// residual compensate
- final Set<Expression> residualCompensatePredicates =
compensateResidualPredicate(
+ final Set<Expression> residualCompensatePredicates =
Predicates.compensateResidualPredicate(
queryStructInfo,
viewStructInfo,
viewToQuerySlotMapping,
comparisonResult);
- // if the join type in query and mv plan is different, we should check
and add filter on mv to make
- // the mv join type is accord with query
+ if (equalCompensateConjunctions == null || rangeCompensatePredicates
== null
+ || residualCompensatePredicates == null) {
+ return SplitPredicate.INVALID_INSTANCE;
+ }
+ // if the join type in query and mv plan is different, we should check
query is have the
+ // filters which rejects null
Set<Set<Slot>> requireNoNullableViewSlot =
comparisonResult.getViewNoNullableSlot();
// check query is use the null reject slot which view comparison need
if (!requireNoNullableViewSlot.isEmpty()) {
@@ -403,7 +420,7 @@ public abstract class AbstractMaterializedViewRule
implements ExplorationRuleFac
cascadesContext);
if (nullRejectPredicates.isEmpty() ||
queryPulledUpPredicates.containsAll(nullRejectPredicates)) {
// query has not null reject predicates, so return
- return SplitPredicate.invalid();
+ return SplitPredicate.INVALID_INSTANCE;
}
Set<Expression> queryUsedNeedRejectNullSlotsViewBased =
nullRejectPredicates.stream()
.map(expression ->
TypeUtils.isNotNull(expression).orElse(null))
@@ -413,122 +430,17 @@ public abstract class AbstractMaterializedViewRule
implements ExplorationRuleFac
.collect(Collectors.toSet());
if (requireNoNullableViewSlot.stream().anyMatch(
set -> Sets.intersection(set,
queryUsedNeedRejectNullSlotsViewBased).isEmpty())) {
- return SplitPredicate.invalid();
+ return SplitPredicate.INVALID_INSTANCE;
}
}
- return
SplitPredicate.of(ExpressionUtils.and(equalCompensateConjunctions),
- rangeCompensatePredicates.isEmpty() ? BooleanLiteral.of(true)
+ return SplitPredicate.of(equalCompensateConjunctions.isEmpty() ?
BooleanLiteral.TRUE
+ : ExpressionUtils.and(equalCompensateConjunctions),
+ rangeCompensatePredicates.isEmpty() ? BooleanLiteral.TRUE
: ExpressionUtils.and(rangeCompensatePredicates),
- residualCompensatePredicates.isEmpty() ?
BooleanLiteral.of(true)
+ residualCompensatePredicates.isEmpty() ? BooleanLiteral.TRUE
: ExpressionUtils.and(residualCompensatePredicates));
}
- protected Set<Expression> compensateEquivalence(StructInfo queryStructInfo,
- StructInfo viewStructInfo,
- SlotMapping viewToQuerySlotMapping,
- ComparisonResult comparisonResult) {
- EquivalenceClass queryEquivalenceClass =
queryStructInfo.getEquivalenceClass();
- EquivalenceClass viewEquivalenceClass =
viewStructInfo.getEquivalenceClass();
- Map<SlotReference, SlotReference> viewToQuerySlotMap =
viewToQuerySlotMapping.toSlotReferenceMap();
- EquivalenceClass viewEquivalenceClassQueryBased =
viewEquivalenceClass.permute(viewToQuerySlotMap);
- if (viewEquivalenceClassQueryBased == null) {
- return ImmutableSet.of();
- }
- final Set<Expression> equalCompensateConjunctions = new HashSet<>();
- if (queryEquivalenceClass.isEmpty() && viewEquivalenceClass.isEmpty())
{
- equalCompensateConjunctions.add(BooleanLiteral.of(true));
- }
- if (queryEquivalenceClass.isEmpty()
- && !viewEquivalenceClass.isEmpty()) {
- return ImmutableSet.of();
- }
- EquivalenceClassSetMapping queryToViewEquivalenceMapping =
- EquivalenceClassSetMapping.generate(queryEquivalenceClass,
viewEquivalenceClassQueryBased);
- // can not map all target equivalence class, can not compensate
- if (queryToViewEquivalenceMapping.getEquivalenceClassSetMap().size()
- < viewEquivalenceClass.getEquivalenceSetList().size()) {
- return ImmutableSet.of();
- }
- // do equal compensate
- Set<Set<SlotReference>> mappedQueryEquivalenceSet =
-
queryToViewEquivalenceMapping.getEquivalenceClassSetMap().keySet();
- queryEquivalenceClass.getEquivalenceSetList().forEach(
- queryEquivalenceSet -> {
- // compensate the equivalence in query but not in view
- if
(!mappedQueryEquivalenceSet.contains(queryEquivalenceSet)) {
- Iterator<SlotReference> iterator =
queryEquivalenceSet.iterator();
- SlotReference first = iterator.next();
- while (iterator.hasNext()) {
- Expression equals = new EqualTo(first,
iterator.next());
- equalCompensateConjunctions.add(equals);
- }
- } else {
- // compensate the equivalence both in query and view,
but query has more equivalence
- Set<SlotReference> viewEquivalenceSet =
-
queryToViewEquivalenceMapping.getEquivalenceClassSetMap().get(queryEquivalenceSet);
- Set<SlotReference> copiedQueryEquivalenceSet = new
HashSet<>(queryEquivalenceSet);
-
copiedQueryEquivalenceSet.removeAll(viewEquivalenceSet);
- SlotReference first =
viewEquivalenceSet.iterator().next();
- for (SlotReference slotReference :
copiedQueryEquivalenceSet) {
- Expression equals = new EqualTo(first,
slotReference);
- equalCompensateConjunctions.add(equals);
- }
- }
- }
- );
- return equalCompensateConjunctions;
- }
-
- protected Set<Expression> compensateResidualPredicate(StructInfo
queryStructInfo,
- StructInfo viewStructInfo,
- SlotMapping viewToQuerySlotMapping,
- ComparisonResult comparisonResult) {
- SplitPredicate querySplitPredicate =
queryStructInfo.getSplitPredicate();
- SplitPredicate viewSplitPredicate = viewStructInfo.getSplitPredicate();
- Expression queryResidualPredicate =
querySplitPredicate.getResidualPredicate();
- Expression viewResidualPredicate =
viewSplitPredicate.getResidualPredicate();
- Expression viewResidualPredicateQueryBased =
- ExpressionUtils.replace(viewResidualPredicate,
viewToQuerySlotMapping.toSlotReferenceMap());
- Set<Expression> queryResidualSet =
-
Sets.newHashSet(ExpressionUtils.extractConjunction(queryResidualPredicate));
- Set<Expression> viewResidualQueryBasedSet =
-
Sets.newHashSet(ExpressionUtils.extractConjunction(viewResidualPredicateQueryBased));
- // query residual predicate can not contain all view residual
predicate when view have residual predicate,
- // bail out
- if (!viewResidualPredicateQueryBased.equals(BooleanLiteral.TRUE)
- && !queryResidualSet.containsAll(viewResidualQueryBasedSet)) {
- return ImmutableSet.of();
- }
- queryResidualSet.removeAll(viewResidualQueryBasedSet);
- return queryResidualSet;
- }
-
- protected Set<Expression> compensateRangePredicate(StructInfo
queryStructInfo,
- StructInfo viewStructInfo,
- SlotMapping viewToQuerySlotMapping,
- ComparisonResult comparisonResult) {
- // TODO range predicates and residual predicates compensate, Simplify
implementation.
- SplitPredicate querySplitPredicate =
queryStructInfo.getSplitPredicate();
- SplitPredicate viewSplitPredicate = viewStructInfo.getSplitPredicate();
-
- Expression queryRangePredicate =
querySplitPredicate.getRangePredicate();
- Expression viewRangePredicate = viewSplitPredicate.getRangePredicate();
- Expression viewRangePredicateQueryBased =
- ExpressionUtils.replace(viewRangePredicate,
viewToQuerySlotMapping.toSlotReferenceMap());
-
- Set<Expression> queryRangeSet =
-
Sets.newHashSet(ExpressionUtils.extractConjunction(queryRangePredicate));
- Set<Expression> viewRangeQueryBasedSet =
-
Sets.newHashSet(ExpressionUtils.extractConjunction(viewRangePredicateQueryBased));
- // query range predicate can not contain all view range predicate when
view have range predicate, bail out
- if (!viewRangePredicateQueryBased.equals(BooleanLiteral.TRUE)
- && !queryRangeSet.containsAll(viewRangeQueryBasedSet)) {
- return ImmutableSet.of();
- }
- queryRangeSet.removeAll(viewRangeQueryBasedSet);
- return queryRangeSet;
- }
-
/**
* Decide the match mode
*
@@ -554,23 +466,6 @@ public abstract class AbstractMaterializedViewRule
implements ExplorationRuleFac
return MatchMode.NOT_MATCH;
}
- /**
- * Extract struct info from plan, support to get struct info from logical
plan or plan in group.
- */
- public static List<StructInfo> extractStructInfo(Plan plan,
CascadesContext cascadesContext) {
- if (plan.getGroupExpression().isPresent() &&
!plan.getGroupExpression().get().getOwnerGroup().getStructInfos()
- .isEmpty()) {
- return
plan.getGroupExpression().get().getOwnerGroup().getStructInfos();
- } else {
- // build struct info and add them to current group
- List<StructInfo> structInfos = StructInfo.of(plan);
- if (plan.getGroupExpression().isPresent()) {
-
plan.getGroupExpression().get().getOwnerGroup().addStructInfo(structInfos);
- }
- return structInfos;
- }
- }
-
/**
* Check the pattern of query or materializedView is supported or not.
*/
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/LogicalCompatibilityContext.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/LogicalCompatibilityContext.java
index 9c494ae80b2..3cd3000a670 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/LogicalCompatibilityContext.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/LogicalCompatibilityContext.java
@@ -78,10 +78,11 @@ public class LogicalCompatibilityContext {
}
/**
- * generate logical compatibility context
+ * Generate logical compatibility context,
+ * this make expression mapping between query and view by relation and the
slot in relation mapping
*/
public static LogicalCompatibilityContext from(RelationMapping
relationMapping,
- SlotMapping slotMapping,
+ SlotMapping queryToViewSlotMapping,
StructInfo queryStructInfo,
StructInfo viewStructInfo) {
// init node mapping
@@ -101,7 +102,8 @@ public class LogicalCompatibilityContext {
}
}
// init expression mapping
- Map<SlotReference, SlotReference> viewToQuerySlotMapping =
slotMapping.inverse().toSlotReferenceMap();
+ Map<SlotReference, SlotReference> viewToQuerySlotMapping =
queryToViewSlotMapping.inverse()
+ .toSlotReferenceMap();
Map<Expression, Expression> queryShuttledExprToExprMap =
queryStructInfo.getShuttledHashConjunctsToConjunctsMap();
Map<Expression, Expression> viewShuttledExprToExprMap =
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 3a8da2c752b..976eb9a5cfc 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
@@ -23,6 +23,7 @@ import org.apache.doris.catalog.PartitionInfo;
import org.apache.doris.catalog.PartitionType;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.mtmv.BaseTableInfo;
+import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.expressions.Slot;
@@ -124,6 +125,23 @@ public class MaterializedViewUtils {
return analyzedPlan.accept(TableQueryOperatorChecker.INSTANCE, null);
}
+ /**
+ * Extract struct info from plan, support to get struct info from logical
plan or plan in group.
+ */
+ public static List<StructInfo> extractStructInfo(Plan plan,
CascadesContext cascadesContext) {
+ if (plan.getGroupExpression().isPresent() &&
!plan.getGroupExpression().get().getOwnerGroup().getStructInfos()
+ .isEmpty()) {
+ return
plan.getGroupExpression().get().getOwnerGroup().getStructInfos();
+ } else {
+ // build struct info and add them to current group
+ List<StructInfo> structInfos = StructInfo.of(plan);
+ if (plan.getGroupExpression().isPresent()) {
+
plan.getGroupExpression().get().getOwnerGroup().addStructInfo(structInfos);
+ }
+ return structInfos;
+ }
+ }
+
private static final class TableQueryOperatorChecker extends
DefaultPlanVisitor<Boolean, Void> {
public static final TableQueryOperatorChecker INSTANCE = new
TableQueryOperatorChecker();
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 39a89c80c29..93bd1d314b1 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
@@ -17,15 +17,23 @@
package org.apache.doris.nereids.rules.exploration.mv;
+import
org.apache.doris.nereids.rules.exploration.mv.mapping.EquivalenceClassSetMapping;
+import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.nereids.util.Utils;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -72,6 +80,127 @@ public class Predicates {
return predicatesSplit.getSplitPredicate();
}
+ /**
+ * compensate equivalence predicates
+ */
+ public static Set<Expression> compensateEquivalence(StructInfo
queryStructInfo,
+ StructInfo viewStructInfo,
+ SlotMapping viewToQuerySlotMapping,
+ ComparisonResult comparisonResult) {
+ EquivalenceClass queryEquivalenceClass =
queryStructInfo.getEquivalenceClass();
+ EquivalenceClass viewEquivalenceClass =
viewStructInfo.getEquivalenceClass();
+ Map<SlotReference, SlotReference> viewToQuerySlotMap =
viewToQuerySlotMapping.toSlotReferenceMap();
+ EquivalenceClass viewEquivalenceClassQueryBased =
viewEquivalenceClass.permute(viewToQuerySlotMap);
+ if (viewEquivalenceClassQueryBased == null) {
+ return null;
+ }
+ final Set<Expression> equalCompensateConjunctions = new HashSet<>();
+ if (queryEquivalenceClass.isEmpty() && viewEquivalenceClass.isEmpty())
{
+ equalCompensateConjunctions.add(BooleanLiteral.TRUE);
+ }
+ if (queryEquivalenceClass.isEmpty()
+ && !viewEquivalenceClass.isEmpty()) {
+ return null;
+ }
+ EquivalenceClassSetMapping queryToViewEquivalenceMapping =
+ EquivalenceClassSetMapping.generate(queryEquivalenceClass,
viewEquivalenceClassQueryBased);
+ // can not map all target equivalence class, can not compensate
+ if (queryToViewEquivalenceMapping.getEquivalenceClassSetMap().size()
+ < viewEquivalenceClass.getEquivalenceSetList().size()) {
+ return null;
+ }
+ // do equal compensate
+ Set<Set<SlotReference>> mappedQueryEquivalenceSet =
+
queryToViewEquivalenceMapping.getEquivalenceClassSetMap().keySet();
+ queryEquivalenceClass.getEquivalenceSetList().forEach(
+ queryEquivalenceSet -> {
+ // compensate the equivalence in query but not in view
+ if
(!mappedQueryEquivalenceSet.contains(queryEquivalenceSet)) {
+ Iterator<SlotReference> iterator =
queryEquivalenceSet.iterator();
+ SlotReference first = iterator.next();
+ while (iterator.hasNext()) {
+ Expression equals = new EqualTo(first,
iterator.next());
+ equalCompensateConjunctions.add(equals);
+ }
+ } else {
+ // compensate the equivalence both in query and view,
but query has more equivalence
+ Set<SlotReference> viewEquivalenceSet =
+
queryToViewEquivalenceMapping.getEquivalenceClassSetMap().get(queryEquivalenceSet);
+ Set<SlotReference> copiedQueryEquivalenceSet = new
HashSet<>(queryEquivalenceSet);
+
copiedQueryEquivalenceSet.removeAll(viewEquivalenceSet);
+ SlotReference first =
viewEquivalenceSet.iterator().next();
+ for (SlotReference slotReference :
copiedQueryEquivalenceSet) {
+ Expression equals = new EqualTo(first,
slotReference);
+ equalCompensateConjunctions.add(equals);
+ }
+ }
+ }
+ );
+ return equalCompensateConjunctions;
+ }
+
+ /**
+ * compensate range predicates
+ */
+ public static Set<Expression> compensateRangePredicate(StructInfo
queryStructInfo,
+ StructInfo viewStructInfo,
+ SlotMapping viewToQuerySlotMapping,
+ ComparisonResult comparisonResult) {
+ // TODO Range predicates compensate, simplify implementation currently.
+ SplitPredicate querySplitPredicate =
queryStructInfo.getSplitPredicate();
+ SplitPredicate viewSplitPredicate = viewStructInfo.getSplitPredicate();
+
+ Expression queryRangePredicate =
querySplitPredicate.getRangePredicate();
+ Expression viewRangePredicate = viewSplitPredicate.getRangePredicate();
+ Expression viewRangePredicateQueryBased =
+ ExpressionUtils.replace(viewRangePredicate,
viewToQuerySlotMapping.toSlotReferenceMap());
+
+ Set<Expression> queryRangeSet =
+
Sets.newHashSet(ExpressionUtils.extractConjunction(queryRangePredicate));
+ Set<Expression> viewRangeQueryBasedSet =
+
Sets.newHashSet(ExpressionUtils.extractConjunction(viewRangePredicateQueryBased));
+ // remove unnecessary literal BooleanLiteral.TRUE
+ queryRangeSet.remove(BooleanLiteral.TRUE);
+ viewRangeQueryBasedSet.remove(BooleanLiteral.TRUE);
+ // query residual predicate can not contain all view residual
predicate when view have residual predicate,
+ // bail out
+ if (!queryRangeSet.containsAll(viewRangeQueryBasedSet)) {
+ return null;
+ }
+ queryRangeSet.removeAll(viewRangeQueryBasedSet);
+ return queryRangeSet;
+ }
+
+ /**
+ * compensate residual predicates
+ */
+ public static Set<Expression> compensateResidualPredicate(StructInfo
queryStructInfo,
+ StructInfo viewStructInfo,
+ SlotMapping viewToQuerySlotMapping,
+ ComparisonResult comparisonResult) {
+ // TODO Residual predicates compensate, simplify implementation
currently.
+ SplitPredicate querySplitPredicate =
queryStructInfo.getSplitPredicate();
+ SplitPredicate viewSplitPredicate = viewStructInfo.getSplitPredicate();
+ Expression queryResidualPredicate =
querySplitPredicate.getResidualPredicate();
+ Expression viewResidualPredicate =
viewSplitPredicate.getResidualPredicate();
+ Expression viewResidualPredicateQueryBased =
+ ExpressionUtils.replace(viewResidualPredicate,
viewToQuerySlotMapping.toSlotReferenceMap());
+ Set<Expression> queryResidualSet =
+
Sets.newHashSet(ExpressionUtils.extractConjunction(queryResidualPredicate));
+ Set<Expression> viewResidualQueryBasedSet =
+
Sets.newHashSet(ExpressionUtils.extractConjunction(viewResidualPredicateQueryBased));
+ // remove unnecessary literal BooleanLiteral.TRUE
+ queryResidualSet.remove(BooleanLiteral.TRUE);
+ viewResidualQueryBasedSet.remove(BooleanLiteral.TRUE);
+ // query residual predicate can not contain all view residual
predicate when view have residual predicate,
+ // bail out
+ if (!queryResidualSet.containsAll(viewResidualQueryBasedSet)) {
+ return null;
+ }
+ queryResidualSet.removeAll(viewResidualQueryBasedSet);
+ return queryResidualSet;
+ }
+
@Override
public String toString() {
return Utils.toSqlString("Predicates", "pulledUpPredicates",
pulledUpPredicates);
@@ -81,9 +210,11 @@ public class Predicates {
* The split different representation for predicate expression, such as
equal, range and residual predicate.
*/
public static final class SplitPredicate {
- private Optional<Expression> equalPredicate;
- private Optional<Expression> rangePredicate;
- private Optional<Expression> residualPredicate;
+ public static final SplitPredicate INVALID_INSTANCE =
+ SplitPredicate.of(null, null, null);
+ private final Optional<Expression> equalPredicate;
+ private final Optional<Expression> rangePredicate;
+ private final Optional<Expression> residualPredicate;
public SplitPredicate(Expression equalPredicate, Expression
rangePredicate, Expression residualPredicate) {
this.equalPredicate = Optional.ofNullable(equalPredicate);
@@ -103,10 +234,6 @@ public class Predicates {
return residualPredicate.orElse(BooleanLiteral.TRUE);
}
- public static SplitPredicate invalid() {
- return new SplitPredicate(null, null, null);
- }
-
/**
* SplitPredicate construct
*/
@@ -117,27 +244,23 @@ public class Predicates {
}
/**
- * isEmpty
+ * Check the predicates are invalid or not. If any of the predicates
is null, it is invalid.
*/
- public boolean isEmpty() {
- return !equalPredicate.isPresent()
- && !rangePredicate.isPresent()
- && !residualPredicate.isPresent();
+ public boolean isInvalid() {
+ return Objects.equals(this, INVALID_INSTANCE);
}
public List<Expression> toList() {
- return ImmutableList.of(equalPredicate.orElse(BooleanLiteral.TRUE),
- rangePredicate.orElse(BooleanLiteral.TRUE),
- residualPredicate.orElse(BooleanLiteral.TRUE));
+ return ImmutableList.of(getEqualPredicate(), getRangePredicate(),
getResidualPredicate());
}
/**
* Check the predicates in SplitPredicate is whether all true or not
*/
public boolean isAlwaysTrue() {
- Expression equalExpr = equalPredicate.orElse(BooleanLiteral.TRUE);
- Expression rangeExpr = rangePredicate.orElse(BooleanLiteral.TRUE);
- Expression residualExpr =
residualPredicate.orElse(BooleanLiteral.TRUE);
+ Expression equalExpr = getEqualPredicate();
+ Expression rangeExpr = getRangePredicate();
+ Expression residualExpr = getResidualPredicate();
return equalExpr instanceof BooleanLiteral
&& rangeExpr instanceof BooleanLiteral
&& residualExpr instanceof BooleanLiteral
@@ -146,6 +269,25 @@ public class Predicates {
&& ((BooleanLiteral) residualExpr).getValue();
}
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SplitPredicate that = (SplitPredicate) o;
+ return Objects.equals(equalPredicate, that.equalPredicate)
+ && Objects.equals(rangePredicate, that.rangePredicate)
+ && Objects.equals(residualPredicate,
that.residualPredicate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(equalPredicate, rangePredicate,
residualPredicate);
+ }
+
@Override
public String toString() {
return Utils.toSqlString("SplitPredicate",
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 0ac3085faeb..d79153bdc8e 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
@@ -23,6 +23,7 @@ import org.apache.doris.nereids.memo.Group;
import org.apache.doris.nereids.memo.GroupExpression;
import org.apache.doris.nereids.rules.exploration.mv.Predicates.SplitPredicate;
import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.ExprId;
import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.expressions.literal.Literal;
@@ -40,8 +41,10 @@ import
org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import org.apache.doris.nereids.trees.plans.logical.LogicalRepeat;
import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
+import org.apache.doris.nereids.trees.plans.visitor.ExpressionLineageReplacer;
import org.apache.doris.nereids.util.ExpressionUtils;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -83,8 +86,11 @@ public class StructInfo {
// split predicates is shuttled
private SplitPredicate splitPredicate;
private EquivalenceClass equivalenceClass;
- // this is for LogicalCompatibilityContext later
+ // Key is the expression shuttled and the value is the origin expression
+ // this is for building LogicalCompatibilityContext later.
private final Map<Expression, Expression>
shuttledHashConjunctsToConjunctsMap = new HashMap<>();
+ // Record the exprId and the corresponding expr map, this is used by
expression shuttled
+ private final Map<ExprId, Expression> namedExprIdAndExprMapping = new
HashMap<>();
private StructInfo(Plan originalPlan, @Nullable Plan topPlan, @Nullable
Plan bottomPlan, HyperGraph hyperGraph) {
this.originalPlan = originalPlan;
@@ -117,12 +123,22 @@ public class StructInfo {
// Collect expression from join condition in hyper graph
this.hyperGraph.getJoinEdges().forEach(edge -> {
List<Expression> hashJoinConjuncts = edge.getHashJoinConjuncts();
+ // shuttle expression in edge for the build of
LogicalCompatibilityContext later.
+ // Record the exprId to expr map in the processing to strut info
+ // TODO get exprId to expr map when complex project is ready in
join dege
hashJoinConjuncts.forEach(conjunctExpr -> {
- // shuttle expression in edge for LogicalCompatibilityContext
later
- shuttledHashConjunctsToConjunctsMap.put(
- ExpressionUtils.shuttleExpressionWithLineage(
- Lists.newArrayList(conjunctExpr),
edge.getJoin()).get(0),
- conjunctExpr);
+ ExpressionLineageReplacer.ExpressionReplaceContext
replaceContext =
+ new ExpressionLineageReplacer.ExpressionReplaceContext(
+ Lists.newArrayList(conjunctExpr),
+ ImmutableSet.of(),
+ ImmutableSet.of());
+ this.topPlan.accept(ExpressionLineageReplacer.INSTANCE,
replaceContext);
+ // Replace expressions by expression map
+ List<Expression> replacedExpressions =
replaceContext.getReplacedExpressions();
+
shuttledHashConjunctsToConjunctsMap.put(replacedExpressions.get(0),
conjunctExpr);
+ // Record this, will be used in top level expression shuttle
later, see the method
+ // ExpressionLineageReplacer#visitGroupPlan
+
this.namedExprIdAndExprMapping.putAll(replaceContext.getExprIdExpressionMap());
});
List<Expression> otherJoinConjuncts = edge.getOtherJoinConjuncts();
if (!otherJoinConjuncts.isEmpty()) {
@@ -267,6 +283,10 @@ public class StructInfo {
return originalPlanId;
}
+ public Map<ExprId, Expression> getNamedExprIdAndExprMapping() {
+ return namedExprIdAndExprMapping;
+ }
+
/**
* Judge the source graph logical is whether the same as target
* For inner join should judge only the join tables,
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/ExpressionLineageReplacer.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/ExpressionLineageReplacer.java
index 2a4b81a5828..b7cc0dff80b 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/ExpressionLineageReplacer.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/ExpressionLineageReplacer.java
@@ -18,6 +18,8 @@
package org.apache.doris.nereids.trees.plans.visitor;
import org.apache.doris.catalog.TableIf.TableType;
+import org.apache.doris.nereids.memo.Group;
+import org.apache.doris.nereids.rules.exploration.mv.StructInfo;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.ExprId;
import org.apache.doris.nereids.trees.expressions.Expression;
@@ -25,6 +27,7 @@ import
org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.expressions.Slot;
import
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
import
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
import org.apache.doris.nereids.trees.plans.Plan;
import
org.apache.doris.nereids.trees.plans.visitor.ExpressionLineageReplacer.ExpressionReplaceContext;
@@ -56,6 +59,22 @@ public class ExpressionLineageReplacer extends
DefaultPlanVisitor<Expression, Ex
return super.visit(plan, context);
}
+ @Override
+ public Expression visitGroupPlan(GroupPlan groupPlan,
ExpressionReplaceContext context) {
+ Group group = groupPlan.getGroup();
+ if (group == null) {
+ return visit(groupPlan, context);
+ }
+ List<StructInfo> structInfos = group.getStructInfos();
+ if (structInfos.isEmpty()) {
+ return visit(groupPlan, context);
+ }
+ // TODO only support group has one struct info, will support more
struct info later
+ StructInfo structInfo = structInfos.get(0);
+
context.getExprIdExpressionMap().putAll(structInfo.getNamedExprIdAndExprMapping());
+ return visit(groupPlan, context);
+ }
+
/**
* Replace the expression with lineage according the exprIdExpressionMap
*/
@@ -93,7 +112,7 @@ public class ExpressionLineageReplacer extends
DefaultPlanVisitor<Expression, Ex
}
/**
- * The Collector for target named expressions in the whole plan, and will
be used to
+ * The Collector for named expressions in the whole plan, and will be used
to
* replace the target expression later
* TODO Collect named expression by targetTypes, tableIdentifiers
*/
@@ -128,7 +147,9 @@ public class ExpressionLineageReplacer extends
DefaultPlanVisitor<Expression, Ex
private Map<ExprId, Expression> exprIdExpressionMap;
private List<Expression> replacedExpressions;
- /**ExpressionReplaceContext*/
+ /**
+ * ExpressionReplaceContext
+ */
public ExpressionReplaceContext(List<Expression> targetExpressions,
Set<TableType> targetTypes,
Set<String> tableIdentifiers) {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java
index 8914f4ac80b..f73d8a2923e 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/CompareOuterJoinTest.java
@@ -19,10 +19,10 @@ package org.apache.doris.nereids.jobs.joinorder.hypergraph;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.rules.RuleSet;
-import
org.apache.doris.nereids.rules.exploration.mv.AbstractMaterializedViewRule;
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.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;
@@ -194,9 +194,9 @@ class CompareOuterJoinTest extends SqlTestBase {
}
LogicalCompatibilityContext constructContext(Plan p1, Plan p2) {
- StructInfo st1 = AbstractMaterializedViewRule.extractStructInfo(p1,
+ StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1,
null).get(0);
- StructInfo st2 = AbstractMaterializedViewRule.extractStructInfo(p2,
+ StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2,
null).get(0);
RelationMapping rm = RelationMapping.generate(st1.getRelations(),
st2.getRelations()).get(0);
SlotMapping sm = SlotMapping.generate(rm);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferJoinTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferJoinTest.java
index 33100c1180b..3f1ce3fc345 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferJoinTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferJoinTest.java
@@ -19,10 +19,10 @@ package org.apache.doris.nereids.jobs.joinorder.hypergraph;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.rules.RuleSet;
-import
org.apache.doris.nereids.rules.exploration.mv.AbstractMaterializedViewRule;
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.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;
@@ -162,9 +162,9 @@ class InferJoinTest extends SqlTestBase {
}
LogicalCompatibilityContext constructContext(Plan p1, Plan p2) {
- StructInfo st1 = AbstractMaterializedViewRule.extractStructInfo(p1,
+ StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1,
null).get(0);
- StructInfo st2 = AbstractMaterializedViewRule.extractStructInfo(p2,
+ StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2,
null).get(0);
RelationMapping rm = RelationMapping.generate(st1.getRelations(),
st2.getRelations()).get(0);
SlotMapping sm = SlotMapping.generate(rm);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferPredicateTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferPredicateTest.java
index bd18f31a7c0..5ea49a51f5c 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferPredicateTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/InferPredicateTest.java
@@ -19,10 +19,10 @@ package org.apache.doris.nereids.jobs.joinorder.hypergraph;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.rules.RuleSet;
-import
org.apache.doris.nereids.rules.exploration.mv.AbstractMaterializedViewRule;
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.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;
@@ -61,9 +61,9 @@ class InferPredicateTest extends SqlTestBase {
}
LogicalCompatibilityContext constructContext(Plan p1, Plan p2) {
- StructInfo st1 = AbstractMaterializedViewRule.extractStructInfo(p1,
+ StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1,
null).get(0);
- StructInfo st2 = AbstractMaterializedViewRule.extractStructInfo(p2,
+ StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2,
null).get(0);
RelationMapping rm = RelationMapping.generate(st1.getRelations(),
st2.getRelations()).get(0);
SlotMapping sm = SlotMapping.generate(rm);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java
index 5489f06379c..44b60753947 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/hypergraph/PullupExpressionTest.java
@@ -19,10 +19,10 @@ package org.apache.doris.nereids.jobs.joinorder.hypergraph;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.rules.RuleSet;
-import
org.apache.doris.nereids.rules.exploration.mv.AbstractMaterializedViewRule;
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.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;
@@ -140,9 +140,9 @@ class PullupExpressionTest extends SqlTestBase {
}
LogicalCompatibilityContext constructContext(Plan p1, Plan p2) {
- StructInfo st1 = AbstractMaterializedViewRule.extractStructInfo(p1,
+ StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1,
null).get(0);
- StructInfo st2 = AbstractMaterializedViewRule.extractStructInfo(p2,
+ StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2,
null).get(0);
RelationMapping rm = RelationMapping.generate(st1.getRelations(),
st2.getRelations()).get(0);
SlotMapping sm = SlotMapping.generate(rm);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphComparatorTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphComparatorTest.java
index 77b7fd67294..b89934d4577 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphComparatorTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/HyperGraphComparatorTest.java
@@ -161,9 +161,9 @@ class HyperGraphComparatorTest extends SqlTestBase {
}
LogicalCompatibilityContext constructContext(Plan p1, Plan p2) {
- StructInfo st1 = AbstractMaterializedViewRule.extractStructInfo(p1,
+ StructInfo st1 = MaterializedViewUtils.extractStructInfo(p1,
null).get(0);
- StructInfo st2 = AbstractMaterializedViewRule.extractStructInfo(p2,
+ StructInfo st2 = MaterializedViewUtils.extractStructInfo(p2,
null).get(0);
RelationMapping rm = RelationMapping.generate(st1.getRelations(),
st2.getRelations()).get(0);
SlotMapping sm = SlotMapping.generate(rm);
diff --git
a/regression-test/data/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.out
b/regression-test/data/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.out
index f20fc747473..10c593bd5f0 100644
---
a/regression-test/data/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.out
+++
b/regression-test/data/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.out
@@ -65,6 +65,14 @@
2 3 2023-12-12 57.40
2 4 2023-12-10 46.00
+-- !query19_1_before --
+2 3 2023-12-08 20.00 10.50 9.50 0 2
+2 3 2023-12-12 57.40 56.20 1.20 0 2
+
+-- !query19_1_after --
+2 3 2023-12-08 20.00 10.50 9.50 0 2
+2 3 2023-12-12 57.40 56.20 1.20 0 2
+
-- !query20_0_before --
2023-12-08 3 2023-12-08 20.00 10.50 9.50 2 0
2023-12-09 3 2023-12-09 11.50 11.50 11.50 1 0
@@ -117,6 +125,14 @@
3 3 2023-12-11 43.20 43.20 43.20 1 0
4 3 2023-12-09 11.50 11.50 11.50 1 0
+-- !query22_1_before --
+2 3 2023-12-08 20.00 10.50 9.50 2 0
+2 3 2023-12-12 57.40 56.20 1.20 2 0
+
+-- !query22_1_after --
+2 3 2023-12-08 20.00 10.50 9.50 2 0
+2 3 2023-12-12 57.40 56.20 1.20 2 0
+
-- !query23_0_before --
2 3 2023-12-08 20.00 10.50 9.50 2 0
@@ -219,3 +235,23 @@
2023-12-11 2 43.20 43.20 43.20 1 0 0
2023-12-12 2 57.40 56.20 1.20 2 0 0
+-- !query28_0_before --
+2023-12-08 20.00
+2023-12-09 11.50
+2023-12-10 46.00
+2023-12-11 43.20
+2023-12-12 57.40
+
+-- !query28_0_after --
+2023-12-08 20.00
+2023-12-09 11.50
+2023-12-10 46.00
+2023-12-11 43.20
+2023-12-12 57.40
+
+-- !query29_0_before --
+8
+
+-- !query29_0_after --
+8
+
diff --git
a/regression-test/data/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.out
b/regression-test/data/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.out
index 8bf8d7d4f9f..cc6e4730fdf 100644
---
a/regression-test/data/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.out
+++
b/regression-test/data/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.out
@@ -163,9 +163,37 @@
4 2 43.20
6 2 57.40
+-- !query19_2_before --
+2023-12-08 2 3 3 20.00 10.50 9.50 2
+2023-12-09 4 3 3 11.50 11.50 11.50 1
+2023-12-10 2 4 4 46.00 33.50 12.50 2
+2023-12-11 3 3 3 43.20 43.20 43.20 1
+2023-12-12 2 3 3 57.40 56.20 1.20 2
+
+-- !query19_2_after --
+2023-12-08 2 3 3 20.00 10.50 9.50 2
+2023-12-09 4 3 3 11.50 11.50 11.50 1
+2023-12-10 2 4 4 46.00 33.50 12.50 2
+2023-12-11 3 3 3 43.20 43.20 43.20 1
+2023-12-12 2 3 3 57.40 56.20 1.20 2
+
-- !query20_0_before --
0 0 0 0 0 0 0 0 0 0
0 0
-- !query20_0_after --
0 0 0 0 0 0 0 0 0 0
0 0
+-- !query20_1_before --
+56.00 8
+
+-- !query20_1_after --
+56.00 8
+
+-- !query20_2_before --
+b a 39.00 6
+d c 17.00 2
+
+-- !query20_2_after --
+b a 39.00 6
+d c 17.00 2
+
diff --git a/regression-test/data/nereids_rules_p0/mv/join/inner/inner_join.out
b/regression-test/data/nereids_rules_p0/mv/join/inner/inner_join.out
index 62d3523c670..1cf464023ee 100644
--- a/regression-test/data/nereids_rules_p0/mv/join/inner/inner_join.out
+++ b/regression-test/data/nereids_rules_p0/mv/join/inner/inner_join.out
@@ -147,6 +147,22 @@
6
6
+-- !query2_2_before --
+4
+4
+4
+4
+6
+6
+
+-- !query2_2_after --
+4
+4
+4
+4
+6
+6
+
-- !query2_3_before --
4
4
diff --git
a/regression-test/data/nereids_rules_p0/mv/join/left_outer/outer_join.out
b/regression-test/data/nereids_rules_p0/mv/join/left_outer/outer_join.out
index 4d0e131c5ff..845ef3933dc 100644
--- a/regression-test/data/nereids_rules_p0/mv/join/left_outer/outer_join.out
+++ b/regression-test/data/nereids_rules_p0/mv/join/left_outer/outer_join.out
@@ -127,6 +127,38 @@
6
6
+-- !query2_2_before --
+4
+4
+4
+4
+6
+6
+
+-- !query2_2_after --
+4
+4
+4
+4
+6
+6
+
+-- !query2_3_before --
+4
+4
+4
+4
+6
+6
+
+-- !query2_3_after --
+4
+4
+4
+4
+6
+6
+
-- !query3_0_before --
4
4
@@ -215,6 +247,14 @@
6
6
+-- !query5_1_before --
+2023-12-08 2023-12-08 2 3
+2023-12-08 2023-12-08 2 3
+
+-- !query5_1_after --
+2023-12-08 2023-12-08 2 3
+2023-12-08 2023-12-08 2 3
+
-- !query6_0_before --
2 3 2023-12-08
2 3 2023-12-08
@@ -237,6 +277,10 @@
-- !query7_0_after --
3 3 2023-12-11
+-- !query7_1_before --
+
+-- !query7_1_after --
+
-- !query8_0_before --
1 0 8 0 10.0000 10.50 9.50
2 0 2 0 11.5000 11.50 11.50
diff --git
a/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy
b/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy
index 9cdca736fe5..cffb031cb17 100644
---
a/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy
+++
b/regression-test/suites/nereids_rules_p0/mv/agg_with_roll_up/aggregate_with_roll_up.groovy
@@ -533,11 +533,10 @@ suite("aggregate_with_roll_up") {
"o_orderdate, " +
"l_partkey, " +
"l_suppkey"
-// // Should pass but not, tmp
-// order_qt_query19_1_before "${query19_1}"
-// check_rewrite(mv19_1, query19_1, "mv19_1")
-// order_qt_query19_1_after "${query19_1}"
-// sql """ DROP MATERIALIZED VIEW IF EXISTS mv19_1"""
+ order_qt_query19_1_before "${query19_1}"
+ check_rewrite(mv19_1, query19_1, "mv19_1")
+ order_qt_query19_1_after "${query19_1}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv19_1"""
// filter inside + right + use not roll up dimension
@@ -708,11 +707,10 @@ suite("aggregate_with_roll_up") {
"o_orderdate, " +
"l_partkey, " +
"l_suppkey"
- // Should pass but not, tmp
-// order_qt_query22_1_before "${query22_1}"
-// check_rewrite(mv22_1, query22_1, "mv22_1")
-// order_qt_query22_1_after "${query22_1}"
-// sql """ DROP MATERIALIZED VIEW IF EXISTS mv22_0"""
+ order_qt_query22_1_before "${query22_1}"
+ check_rewrite(mv22_1, query22_1, "mv22_1")
+ order_qt_query22_1_after "${query22_1}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv22_1"""
// filter outside + right + use not roll up dimension
@@ -1017,9 +1015,79 @@ suite("aggregate_with_roll_up") {
group by
o_orderdate,
o_shippriority;
- """
+ """
order_qt_query27_0_before "${query27_0}"
check_not_match(mv27_0, query27_0, "mv27_0")
order_qt_query27_0_after "${query27_0}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv27_0"""
+
+ // with cte
+ // left join + aggregate
+ def mv28_0 = """
+ select l_shipdate, o_orderdate, l_partkey, l_suppkey,
+ sum(ifnull(o_totalprice, 0)) as sum_total,
+ max(o_totalprice) as max_total,
+ min(o_totalprice) as min_total,
+ count(*) as count_all,
+ bitmap_union(to_bitmap(case when o_shippriority > 1 and o_orderkey
IN (1, 2, 3) then o_custkey else null end)) as cnt_1,
+ bitmap_union(to_bitmap(case when o_shippriority > 2 and o_orderkey
IN (3, 4, 5) then o_custkey else null end)) as cnt_2
+ from lineitem
+ left join orders on l_orderkey = o_orderkey and l_shipdate =
o_orderdate
+ group by
+ l_shipdate,
+ o_orderdate,
+ l_partkey,
+ l_suppkey;
+ """
+ def query28_0 = """
+ with cte_view_1 as (
+ select l_shipdate, o_orderdate, l_partkey, l_suppkey,
+ ifnull(o_totalprice, 0) as price_with_no_null
+ from lineitem
+ left join orders on l_orderkey = o_orderkey and l_shipdate =
o_orderdate
+ )
+ select
+ l_shipdate,
+ sum(price_with_no_null)
+ from
+ cte_view_1 cte_view
+ group by
+ l_shipdate
+ order by
+ l_shipdate
+ limit 10;
+ """
+ order_qt_query28_0_before "${query28_0}"
+ check_rewrite(mv28_0, query28_0, "mv28_0")
+ order_qt_query28_0_after "${query28_0}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv28_0"""
+
+
+ // scalar aggregate
+ def mv29_0 = """
+ select count(*)
+ from lineitem
+ left join orders on l_orderkey = o_orderkey and l_shipdate =
o_orderdate;
+ """
+ def query29_0 = """
+ select *
+ from
+ (
+ with cte_view_1 as (
+ select l_shipdate, o_orderdate, l_partkey, l_suppkey,
+ ifnull(o_totalprice, 0) as price_with_no_null
+ from lineitem
+ left join orders on l_orderkey = o_orderkey and l_shipdate =
o_orderdate
+ )
+ select
+ count(1) count_all
+ from
+ cte_view_1 cte_view
+ limit 1
+ ) as t;
+ """
+ order_qt_query29_0_before "${query29_0}"
+ check_rewrite(mv29_0, query29_0, "mv29_0")
+ order_qt_query29_0_after "${query29_0}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv29_0"""
}
diff --git
a/regression-test/suites/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.groovy
b/regression-test/suites/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.groovy
index be56ebe05f6..b254fe6ac30 100644
---
a/regression-test/suites/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.groovy
+++
b/regression-test/suites/nereids_rules_p0/mv/agg_without_roll_up/aggregate_without_roll_up.groovy
@@ -172,8 +172,8 @@ suite("aggregate_without_roll_up") {
}
}
-// // single table
-// // with filter
+ // single table
+ // with filter
def mv1_0 = "select o_shippriority, o_comment, " +
"sum(o_totalprice) as sum_total, " +
"max(o_totalprice) as max_total, " +
@@ -726,6 +726,52 @@ suite("aggregate_without_roll_up") {
order_qt_query19_1_after "${query19_1}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv19_1"""
+ // with duplicated group by column
+ def mv19_2 = """
+ select
+ o_orderdate as o_orderdate_1,
+ l_partkey as l_partkey_1,
+ l_suppkey as l_suppkey_1,
+ l_suppkey as l_suppkey_2,
+ l_partkey as l_partkey_2,
+ o_orderdate as o_orderdate_2,
+ sum(o_totalprice),
+ max(o_totalprice) as max_total,
+ min(o_totalprice) as min_total,
+ count(*) as count_all
+ from lineitem
+ left join orders on l_orderkey = o_orderkey and l_shipdate =
o_orderdate
+ group by
+ o_orderdate,
+ l_partkey,
+ l_suppkey,
+ l_suppkey,
+ l_partkey,
+ o_orderdate;
+ """
+ def query19_2 = """
+ select
+ o_orderdate as o_orderdate_1,
+ l_partkey as l_partkey_1,
+ l_suppkey as l_suppkey_1,
+ l_suppkey as l_suppkey_2,
+ sum(o_totalprice),
+ max(o_totalprice),
+ min(o_totalprice),
+ count(*)
+ from lineitem
+ left join orders on l_orderkey = o_orderkey and l_shipdate =
o_orderdate
+ group by
+ o_orderdate,
+ l_suppkey,
+ l_partkey,
+ l_suppkey;
+ """
+ order_qt_query19_2_before "${query19_2}"
+ check_rewrite(mv19_2, query19_2, "mv19_2")
+ order_qt_query19_2_after "${query19_2}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv19_2"""
+
// without group, scalar aggregate
def mv20_0 = "select count(distinct case when O_SHIPPRIORITY > 1 and
O_ORDERKEY IN (1, 3) then O_ORDERSTATUS else null end) as filter_cnt_1, " +
"count(distinct case when O_SHIPPRIORITY > 2 and O_ORDERKEY IN (2)
then O_ORDERSTATUS else null end) as filter_cnt_2, " +
@@ -773,4 +819,61 @@ suite("aggregate_without_roll_up") {
check_rewrite(mv20_0, query20_0, "mv20_0")
order_qt_query20_0_after "${query20_0}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv20_0"""
+
+ // mv is not scalar aggregate bug query is, should not rewrite
+ def mv20_1 = """
+ select
+ l_shipmode,
+ l_shipinstruct,
+ sum(l_extendedprice),
+ count(*)
+ from lineitem
+ left join
+ orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY
+ group by
+ l_shipmode,
+ l_shipinstruct;
+ """
+ def query20_1 =
+ """
+ select
+ sum(l_extendedprice),
+ count(*)
+ from lineitem
+ left join
+ orders
+ on lineitem.L_ORDERKEY = orders.O_ORDERKEY
+ """
+ order_qt_query20_1_before "${query20_1}"
+ check_not_match(mv20_1, query20_1, "mv20_1")
+ order_qt_query20_1_after "${query20_1}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv20_1"""
+
+ // mv is scalar aggregate bug query not, should not rewrite
+ def mv20_2 = """
+ select
+ sum(l_extendedprice),
+ count(*)
+ from lineitem
+ left join
+ orders
+ on lineitem.L_ORDERKEY = orders.O_ORDERKEY
+ """
+ def query20_2 = """
+ select
+ l_shipmode,
+ l_shipinstruct,
+ sum(l_extendedprice),
+ count(*)
+ from lineitem
+ left join
+ orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY
+ group by
+ l_shipmode,
+ l_shipinstruct;
+ """
+ order_qt_query20_2_before "${query20_2}"
+ check_not_match(mv20_2, query20_2, "mv20_2")
+ order_qt_query20_2_after "${query20_2}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv20_2"""
}
diff --git
a/regression-test/suites/nereids_rules_p0/mv/join/inner/inner_join.groovy
b/regression-test/suites/nereids_rules_p0/mv/join/inner/inner_join.groovy
index 401dbd279a1..ed50e19c605 100644
--- a/regression-test/suites/nereids_rules_p0/mv/join/inner/inner_join.groovy
+++ b/regression-test/suites/nereids_rules_p0/mv/join/inner/inner_join.groovy
@@ -215,16 +215,20 @@ suite("inner_join") {
order_qt_query1_3_after "${query1_3}"
sql """ DROP MATERIALIZED VIEW IF EXISTS mv1_3"""
- def mv1_4 = "select lineitem.L_LINENUMBER, orders.O_CUSTKEY,
partsupp.PS_AVAILQTY " +
- "from lineitem " +
- "inner join orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY " +
- "inner join partsupp on lineitem.L_PARTKEY = partsupp.PS_PARTKEY "
+
- "and lineitem.L_SUPPKEY = partsupp.PS_SUPPKEY"
- def query1_4 = "select lineitem.L_LINENUMBER " +
- "from lineitem " +
- "inner join orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY " +
- "inner join partsupp on lineitem.L_PARTKEY = partsupp.PS_PARTKEY "
+
- "and lineitem.L_SUPPKEY = partsupp.PS_SUPPKEY"
+ def mv1_4 = """
+ select lineitem.L_LINENUMBER, orders.O_CUSTKEY, partsupp.PS_AVAILQTY
+ from lineitem
+ inner join orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY
+ inner join partsupp on lineitem.L_PARTKEY = partsupp.PS_PARTKEY
+ and lineitem.L_SUPPKEY = partsupp.PS_SUPPKEY;
+ """
+ def query1_4 = """
+ select lineitem.L_LINENUMBER
+ from lineitem
+ inner join orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY
+ inner join partsupp on lineitem.L_PARTKEY = partsupp.PS_PARTKEY
+ and lineitem.L_SUPPKEY = partsupp.PS_SUPPKEY;
+ """
order_qt_query1_4_before "${query1_4}"
check_rewrite(mv1_4, query1_4, "mv1_4")
order_qt_query1_4_after "${query1_4}"
@@ -273,18 +277,17 @@ suite("inner_join") {
sql """ DROP MATERIALIZED VIEW IF EXISTS mv2_1"""
- def mv2_2 = "select t1.L_LINENUMBER, orders.O_CUSTKEY " +
+ def mv2_2 = "select t1.L_LINENUMBER, orders.O_CUSTKEY, l_suppkey " +
"from (select * from lineitem where L_LINENUMBER > 1) t1 " +
"inner join orders on t1.L_ORDERKEY = orders.O_ORDERKEY "
def query2_2 = "select lineitem.L_LINENUMBER " +
"from lineitem " +
"inner join orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY " +
"where lineitem.L_LINENUMBER > 1 and l_suppkey = 3"
- // Should success but not, because mv contains the part filter of mv, tmp
-// order_qt_query2_2_before "${query2_2}"
-// check_rewrite(mv2_2, query2_2, "mv2_2")
-// order_qt_query2_2_after "${query2_2}"
-// sql """ DROP MATERIALIZED VIEW IF EXISTS mv2_2"""
+ order_qt_query2_2_before "${query2_2}"
+ check_rewrite(mv2_2, query2_2, "mv2_2")
+ order_qt_query2_2_after "${query2_2}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv2_2"""
def mv2_3 = "select lineitem.L_LINENUMBER, orders.O_CUSTKEY,
partsupp.PS_AVAILQTY, l_suppkey " +
diff --git
a/regression-test/suites/nereids_rules_p0/mv/join/left_outer/outer_join.groovy
b/regression-test/suites/nereids_rules_p0/mv/join/left_outer/outer_join.groovy
index a407b4cd472..83b2ca726b7 100644
---
a/regression-test/suites/nereids_rules_p0/mv/join/left_outer/outer_join.groovy
+++
b/regression-test/suites/nereids_rules_p0/mv/join/left_outer/outer_join.groovy
@@ -244,18 +244,38 @@ suite("outer_join") {
sql """ DROP MATERIALIZED VIEW IF EXISTS mv2_1"""
- def mv2_2 = "select t1.L_LINENUMBER, orders.O_CUSTKEY " +
- "from (select * from lineitem where L_LINENUMBER > 1) t1 " +
- "left join orders on t1.L_ORDERKEY = orders.O_ORDERKEY "
- def query2_2 = "select lineitem.L_LINENUMBER " +
- "from lineitem " +
- "left join orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY " +
- "where lineitem.L_LINENUMBER > 1 and l_suppkey = 3"
- // Should success but not, tmp
-// order_qt_query2_2_before "${query2_2}"
-// check_rewrite(mv2_2, query2_2, "mv2_2")
-// order_qt_query2_2_after "${query2_2}"
-// sql """ DROP MATERIALIZED VIEW IF EXISTS mv2_2"""
+ def mv2_2 ="""
+ select t1.L_LINENUMBER, orders.O_CUSTKEY
+ from (select * from lineitem where L_LINENUMBER > 1) t1
+ left join orders on t1.L_ORDERKEY = orders.O_ORDERKEY;
+ """
+ def query2_2 = """
+ select lineitem.L_LINENUMBER
+ from lineitem
+ left join orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY
+ where lineitem.L_LINENUMBER > 1 and l_suppkey = 3;
+ """
+ order_qt_query2_2_before "${query2_2}"
+ check_not_match(mv2_2, query2_2, "mv2_2")
+ order_qt_query2_2_after "${query2_2}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv2_2"""
+
+
+ def mv2_3 ="""
+ select t1.L_LINENUMBER, orders.O_CUSTKEY, l_suppkey
+ from (select * from lineitem where L_LINENUMBER > 1) t1
+ left join orders on t1.L_ORDERKEY = orders.O_ORDERKEY;
+ """
+ def query2_3 = """
+ select lineitem.L_LINENUMBER
+ from lineitem
+ left join orders on lineitem.L_ORDERKEY = orders.O_ORDERKEY
+ where lineitem.L_LINENUMBER > 1 and l_suppkey = 3;
+ """
+ order_qt_query2_3_before "${query2_3}"
+ check_rewrite(mv2_3, query2_3, "mv2_3")
+ order_qt_query2_3_after "${query2_3}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv2_3"""
// filter outside + right
@@ -338,6 +358,25 @@ suite("outer_join") {
sql """ DROP MATERIALIZED VIEW IF EXISTS mv5_0"""
+ def mv5_1 = """
+ select l_shipdate, o_orderdate, l_partkey, l_suppkey
+ from (select * from lineitem where l_shipdate = '2023-12-08' ) t1
+ left join orders
+ on t1.l_orderkey = orders.o_orderkey
+ """
+ def query5_1 = """
+ select l_shipdate, o_orderdate, l_partkey, l_suppkey
+ from lineitem
+ left join orders
+ on lineitem.l_orderkey = orders.o_orderkey
+ where o_orderdate = '2023-12-08'
+ """
+ order_qt_query5_1_before "${query5_1}"
+ check_not_match(mv5_1, query5_1, "mv5_1")
+ order_qt_query5_1_after "${query5_1}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv5_1"""
+
+
// filter inside + right
def mv6_0 = "select l_shipdate, o_orderdate, l_partkey, l_suppkey " +
"from lineitem " +
@@ -370,6 +409,25 @@ suite("outer_join") {
sql """ DROP MATERIALIZED VIEW IF EXISTS mv7_0"""
+ def mv7_1 = """
+ select l_shipdate, o_orderdate, l_partkey, l_suppkey
+ from lineitem
+ left join orders
+ on lineitem.l_orderkey = orders.o_orderkey
+ where l_shipdate = '2023-12-08' and o_orderdate = '2023-12-08';
+ """
+ def query7_1 = """
+ select l_shipdate, o_orderdate, l_partkey, l_suppkey
+ from (select * from lineitem where l_shipdate = '2023-10-17' ) t1
+ left join orders
+ on t1.l_orderkey = orders.o_orderkey;
+ """
+ order_qt_query7_1_before "${query7_1}"
+ check_not_match(mv7_1, query7_1, "mv7_1")
+ order_qt_query7_1_after "${query7_1}"
+ sql """ DROP MATERIALIZED VIEW IF EXISTS mv7_1"""
+
+
// self join test
def mv8_0 = """
select
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]