From 5e9bda678c5acce1dfda534aca2f350b439ab63b Mon Sep 17 00:00:00 2001 From: seawinde Date: Sat, 23 Nov 2024 03:28:38 +0800 Subject: [PATCH] Pick some pr to branch 21 #42279 #44164 (#44369) --- .../nereids/rules/analysis/BindRelation.java | 46 ++++++++++------- .../mv/AbstractMaterializedViewRule.java | 27 ++++++---- .../mv/AsyncMaterializationContext.java | 19 +++++-- .../mv/MaterializationContext.java | 48 +++++++++--------- .../exploration/mv/MaterializedViewUtils.java | 22 ++++++--- .../doris/nereids/mv/IdStatisticsMapTest.java | 3 +- .../mv/join/left_outer/outer_join.out | 46 +++++++++++++++++ .../doris/regression/suite/Suite.groovy | 8 +-- .../mv/join/left_outer/outer_join.groovy | 49 +++++++++++++++++++ 9 files changed, 200 insertions(+), 68 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java index 5e049a9d9b393f..d06647ee5153b1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java @@ -244,25 +244,7 @@ private LogicalPlan makeOlapScan(TableIf table, UnboundRelation unboundRelation, } else { // it's a duplicate, unique or hash distribution agg table // add delete sign filter on olap scan if needed - if (!Util.showHiddenColumns() && scan.getTable().hasDeleteSign() - && !ConnectContext.get().getSessionVariable().skipDeleteSign()) { - // table qualifier is catalog.db.table, we make db.table.column - Slot deleteSlot = null; - for (Slot slot : scan.getOutput()) { - if (slot.getName().equals(Column.DELETE_SIGN)) { - deleteSlot = slot; - break; - } - } - Preconditions.checkArgument(deleteSlot != null); - Expression conjunct = new EqualTo(new TinyIntLiteral((byte) 0), deleteSlot); - if (!((OlapTable) table).getEnableUniqueKeyMergeOnWrite()) { - scan = scan.withPreAggStatus( - PreAggStatus.off(Column.DELETE_SIGN + " is used as conjuncts.")); - } - return new LogicalFilter<>(Sets.newHashSet(conjunct), scan); - } - return scan; + return checkAndAddDeleteSignFilter(scan, ConnectContext.get(), (OlapTable) table); } } @@ -370,6 +352,32 @@ private Optional handleMetaTable(TableIf table, UnboundRelation unb return Optional.empty(); } + /** + * Add delete sign filter on olap scan if need. + */ + public static LogicalPlan checkAndAddDeleteSignFilter(LogicalOlapScan scan, ConnectContext connectContext, + OlapTable olapTable) { + if (!Util.showHiddenColumns() && scan.getTable().hasDeleteSign() + && !connectContext.getSessionVariable().skipDeleteSign()) { + // table qualifier is catalog.db.table, we make db.table.column + Slot deleteSlot = null; + for (Slot slot : scan.getOutput()) { + if (slot.getName().equals(Column.DELETE_SIGN)) { + deleteSlot = slot; + break; + } + } + Preconditions.checkArgument(deleteSlot != null); + Expression conjunct = new EqualTo(new TinyIntLiteral((byte) 0), deleteSlot); + if (!olapTable.getEnableUniqueKeyMergeOnWrite()) { + scan = scan.withPreAggStatus(PreAggStatus.off( + Column.DELETE_SIGN + " is used as conjuncts.")); + } + return new LogicalFilter<>(Sets.newHashSet(conjunct), scan); + } + return scan; + } + private LogicalPlan getLogicalPlan(TableIf table, UnboundRelation unboundRelation, List qualifiedTableName, CascadesContext cascadesContext) { // for create view stmt replace tableName to ctl.db.tableName 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 884ac0980ad562..8bff4ca0f0e899 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 @@ -234,7 +234,7 @@ protected List doRewrite(StructInfo queryStructInfo, CascadesContext casca continue; } Plan rewrittenPlan; - Plan mvScan = materializationContext.getScanPlan(); + Plan mvScan = materializationContext.getScanPlan(queryStructInfo, cascadesContext); Plan queryPlan = queryStructInfo.getTopPlan(); if (compensatePredicates.isAlwaysTrue()) { rewrittenPlan = mvScan; @@ -254,15 +254,14 @@ protected List doRewrite(StructInfo queryStructInfo, CascadesContext casca } rewrittenPlan = new LogicalFilter<>(Sets.newLinkedHashSet(rewriteCompensatePredicates), mvScan); } + boolean checkResult = rewriteQueryByViewPreCheck(matchMode, queryStructInfo, + viewStructInfo, viewToQuerySlotMapping, rewrittenPlan, materializationContext); + if (!checkResult) { + continue; + } // Rewrite query by view rewrittenPlan = rewriteQueryByView(matchMode, queryStructInfo, viewStructInfo, viewToQuerySlotMapping, rewrittenPlan, materializationContext, cascadesContext); - // If rewrite successfully, try to get mv read lock to avoid data inconsistent, - // try to get lock which should added before RBO - if (materializationContext instanceof AsyncMaterializationContext && !materializationContext.isSuccess()) { - cascadesContext.getStatementContext() - .addTableReadLock(((AsyncMaterializationContext) materializationContext).getMtmv()); - } rewrittenPlan = MaterializedViewUtils.rewriteByRules(cascadesContext, childContext -> { Rewriter.getWholeTreeRewriter(childContext).execute(); @@ -374,9 +373,9 @@ protected List doRewrite(StructInfo queryStructInfo, CascadesContext casca } trySetStatistics(materializationContext, cascadesContext); rewriteResults.add(rewrittenPlan); - // if rewrite successfully, try to regenerate mv scan because it maybe used again - materializationContext.tryReGenerateScanPlan(cascadesContext); recordIfRewritten(queryStructInfo.getOriginalPlan(), materializationContext, cascadesContext); + // If rewrite successfully, try to clear mv scan currently because it maybe used again + materializationContext.clearScanPlan(cascadesContext); } return rewriteResults; } @@ -527,6 +526,16 @@ protected Pair>, Map>> return Pair.of(mvPartitionNeedRemoveNameMap, baseTablePartitionNeedUnionNameMap); } + /** + * Query rewrite result may output origin plan , this will cause loop. + * if return origin plan, need add check hear. + */ + protected boolean rewriteQueryByViewPreCheck(MatchMode matchMode, StructInfo queryStructInfo, + StructInfo viewStructInfo, SlotMapping viewToQuerySlotMapping, Plan tempRewritedPlan, + MaterializationContext materializationContext) { + return true; + } + /** * Rewrite query by view, for aggregate or join rewriting should be different inherit class implementation */ diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java index b555f71e04d271..96d37ad546a7b4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AsyncMaterializationContext.java @@ -27,6 +27,7 @@ import org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionMapping; import org.apache.doris.nereids.trees.plans.ObjectId; import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.PreAggStatus; import org.apache.doris.nereids.trees.plans.RelationId; import org.apache.doris.nereids.trees.plans.algebra.Relation; import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan; @@ -56,8 +57,7 @@ public class AsyncMaterializationContext extends MaterializationContext { */ public AsyncMaterializationContext(MTMV mtmv, Plan mvPlan, Plan mvOriginalPlan, List baseTables, List
baseViews, CascadesContext cascadesContext, StructInfo structInfo) { - super(mvPlan, mvOriginalPlan, MaterializedViewUtils.generateMvScanPlan(mtmv, cascadesContext), - cascadesContext, structInfo); + super(mvPlan, mvOriginalPlan, cascadesContext, structInfo); this.mtmv = mtmv; } @@ -67,7 +67,8 @@ public MTMV getMtmv() { @Override Plan doGenerateScanPlan(CascadesContext cascadesContext) { - return MaterializedViewUtils.generateMvScanPlan(this.mtmv, cascadesContext); + return MaterializedViewUtils.generateMvScanPlan(this.mtmv, this.mtmv.getBaseIndexId(), + this.mtmv.getPartitionIds(), PreAggStatus.on(), cascadesContext); } @Override @@ -107,7 +108,8 @@ public Optional> getPlanStatistics(CascadesContext cascades return Optional.empty(); } RelationId relationId = null; - Optional logicalOlapScan = this.getScanPlan().collectFirst(LogicalOlapScan.class::isInstance); + Optional logicalOlapScan = this.getScanPlan(null, cascadesContext) + .collectFirst(LogicalOlapScan.class::isInstance); if (logicalOlapScan.isPresent()) { relationId = logicalOlapScan.get().getRelationId(); } @@ -127,7 +129,14 @@ boolean isFinalChosen(Relation relation) { ); } - public Plan getScanPlan() { + @Override + public Plan getScanPlan(StructInfo queryInfo, CascadesContext cascadesContext) { + // If try to get scan plan or rewrite successfully, try to get mv read lock to avoid meta data inconsistent, + // try to get lock which should added before RBO + if (!this.isSuccess()) { + cascadesContext.getStatementContext().addTableReadLock(this.getMtmv()); + } + super.getScanPlan(queryInfo, cascadesContext); return scanPlan; } 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 38d0eb886e8ced..38eba2ac3406ff 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 @@ -105,22 +105,13 @@ public abstract class MaterializationContext { /** * MaterializationContext, this contains necessary info for query rewriting by materialization */ - public MaterializationContext(Plan plan, Plan originalPlan, Plan scanPlan, + public MaterializationContext(Plan plan, Plan originalPlan, CascadesContext cascadesContext, StructInfo structInfo) { this.plan = plan; this.originalPlan = originalPlan; - this.scanPlan = scanPlan; - StatementBase parsedStatement = cascadesContext.getStatementContext().getParsedStatement(); this.enableRecordFailureDetail = parsedStatement != null && parsedStatement.isExplain() && ExplainLevel.MEMO_PLAN == parsedStatement.getExplainOptions().getExplainLevel(); - List originalPlanOutput = originalPlan.getOutput(); - List scanPlanOutput = this.scanPlan.getOutput(); - if (originalPlanOutput.size() == scanPlanOutput.size()) { - for (int slotIndex = 0; slotIndex < originalPlanOutput.size(); slotIndex++) { - this.exprToScanExprMapping.put(originalPlanOutput.get(slotIndex), scanPlanOutput.get(slotIndex)); - } - } // Construct materialization struct info, catch exception which may cause planner roll back this.structInfo = structInfo == null ? constructStructInfo(plan, originalPlan, cascadesContext, new BitSet()).orElseGet(() -> null) @@ -128,10 +119,6 @@ public MaterializationContext(Plan plan, Plan originalPlan, Plan scanPlan, this.available = this.structInfo != null; if (available) { this.planOutputShuttledExpressions = this.structInfo.getPlanOutputShuttledExpressions(); - // materialization output expression shuttle, this will be used to expression rewrite - this.shuttledExprToScanExprMapping = ExpressionMapping.generate( - this.planOutputShuttledExpressions, - scanPlanOutput); } } @@ -176,17 +163,19 @@ public void addMatchedGroup(GroupId groupId, boolean rewriteSuccess) { * if MaterializationContext is already rewritten successfully, then should generate new scan plan in later * query rewrite, because one plan may hit the materialized view repeatedly and the materialization scan output * should be different. - * This method should be called when query rewrite successfully */ - public void tryReGenerateScanPlan(CascadesContext cascadesContext) { + public void tryGenerateScanPlan(CascadesContext cascadesContext) { + if (!this.isAvailable()) { + return; + } this.scanPlan = doGenerateScanPlan(cascadesContext); - // materialization output expression shuttle, this will be used to expression rewrite - this.shuttledExprToScanExprMapping = ExpressionMapping.generate( - this.planOutputShuttledExpressions, - this.scanPlan.getOutput()); + // Materialization output expression shuttle, this will be used to expression rewrite + List scanPlanOutput = this.scanPlan.getOutput(); + this.shuttledExprToScanExprMapping = ExpressionMapping.generate(this.planOutputShuttledExpressions, + scanPlanOutput); + // This is used by normalize statistics column expression Map regeneratedMapping = new HashMap<>(); List originalPlanOutput = originalPlan.getOutput(); - List scanPlanOutput = this.scanPlan.getOutput(); if (originalPlanOutput.size() == scanPlanOutput.size()) { for (int slotIndex = 0; slotIndex < originalPlanOutput.size(); slotIndex++) { regeneratedMapping.put(originalPlanOutput.get(slotIndex), scanPlanOutput.get(slotIndex)); @@ -195,6 +184,17 @@ public void tryReGenerateScanPlan(CascadesContext cascadesContext) { this.exprToScanExprMapping = regeneratedMapping; } + /** + * Should clear scan plan after materializationContext is already rewritten successfully, + * Because one plan may hit the materialized view repeatedly and the materialization scan output + * should be different. + */ + public void clearScanPlan(CascadesContext cascadesContext) { + this.scanPlan = null; + this.shuttledExprToScanExprMapping = null; + this.exprToScanExprMapping = null; + } + public void addSlotMappingToCache(RelationMapping relationMapping, SlotMapping slotMapping) { queryToMaterializationSlotMappingCache.put(relationMapping, slotMapping); } @@ -275,7 +275,11 @@ public Plan getOriginalPlan() { return originalPlan; } - public Plan getScanPlan() { + public Plan getScanPlan(StructInfo queryStructInfo, CascadesContext cascadesContext) { + if (this.scanPlan == null || this.shuttledExprToScanExprMapping == null + || this.exprToScanExprMapping == null) { + tryGenerateScanPlan(cascadesContext); + } return scanPlan; } 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 04e27d0e83b1ba..52d9afa48ea57f 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 @@ -20,7 +20,7 @@ import org.apache.doris.analysis.Expr; import org.apache.doris.analysis.SlotRef; import org.apache.doris.catalog.Column; -import org.apache.doris.catalog.MTMV; +import org.apache.doris.catalog.OlapTable; import org.apache.doris.catalog.PartitionType; import org.apache.doris.catalog.TableIf; import org.apache.doris.catalog.constraint.TableIdentifier; @@ -30,6 +30,7 @@ import org.apache.doris.nereids.memo.Group; import org.apache.doris.nereids.memo.StructInfoMap; import org.apache.doris.nereids.rules.RuleType; +import org.apache.doris.nereids.rules.analysis.BindRelation; import org.apache.doris.nereids.rules.expression.ExpressionNormalization; import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext; import org.apache.doris.nereids.trees.expressions.Alias; @@ -212,19 +213,24 @@ public static List extractStructInfo(Plan plan, Plan originalPlan, C * when query rewrite, because one plan may hit the materialized view repeatedly and the mv scan output * should be different */ - public static Plan generateMvScanPlan(MTMV materializedView, CascadesContext cascadesContext) { - return new LogicalOlapScan( + public static Plan generateMvScanPlan(OlapTable table, long indexId, + List partitionIds, + PreAggStatus preAggStatus, + CascadesContext cascadesContext) { + LogicalOlapScan olapScan = new LogicalOlapScan( cascadesContext.getStatementContext().getNextRelationId(), - materializedView, - materializedView.getFullQualifiers(), + table, + ImmutableList.of(table.getQualifiedDbName()), ImmutableList.of(), - materializedView.getPartitionIds(), - materializedView.getBaseIndexId(), - PreAggStatus.on(), + partitionIds, + indexId, + preAggStatus, ImmutableList.of(), // this must be empty, or it will be used to sample ImmutableList.of(), Optional.empty()); + return BindRelation.checkAndAddDeleteSignFilter(olapScan, cascadesContext.getConnectContext(), + olapScan.getTable()); } /** diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/IdStatisticsMapTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/IdStatisticsMapTest.java index 4b0ca1849552c2..0090982db00898 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/IdStatisticsMapTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/IdStatisticsMapTest.java @@ -75,7 +75,8 @@ public boolean isMVPartitionValid(MTMV mtmv, ConnectContext ctx, boolean isMVPar .analyze() .rewrite(); // scan plan output will be refreshed after mv rewrite successfully, so need tmp store - Set materializationScanOutput = c1.getMaterializationContexts().get(0).getScanPlan().getOutputSet(); + Set materializationScanOutput = c1.getMaterializationContexts().get(0) + .getScanPlan(null, c1).getOutputSet(); tmpPlanChecker .optimize() .printlnBestPlanTree(); 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 1a1b846054bbce..b8e78048d8e9ff 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 @@ -373,3 +373,49 @@ 2023-12-12 2 mi 108 2 2023-12-12 2 mi 108 2 +-- !query12_0_before -- +2023-12-09 1 yy 95 4 +2023-12-09 1 yy 95 4 +2023-12-09 1 yy 96 4 +2023-12-09 1 yy 96 4 +2023-12-09 1 yy 97 4 +2023-12-09 1 yy 97 4 +2023-12-10 1 yy 100 2 +2023-12-10 1 yy 101 2 +2023-12-10 1 yy 98 2 +2023-12-10 1 yy 99 2 +2023-12-11 2 mm 102 3 +2023-12-11 2 mm 103 3 +2023-12-11 2 mm 104 3 +2023-12-12 2 mi 105 2 +2023-12-12 2 mi 105 2 +2023-12-12 2 mi 106 2 +2023-12-12 2 mi 106 2 +2023-12-12 2 mi 107 2 +2023-12-12 2 mi 107 2 +2023-12-12 2 mi 108 2 +2023-12-12 2 mi 108 2 + +-- !query12_0_after -- +2023-12-09 1 yy 95 4 +2023-12-09 1 yy 95 4 +2023-12-09 1 yy 96 4 +2023-12-09 1 yy 96 4 +2023-12-09 1 yy 97 4 +2023-12-09 1 yy 97 4 +2023-12-10 1 yy 100 2 +2023-12-10 1 yy 101 2 +2023-12-10 1 yy 98 2 +2023-12-10 1 yy 99 2 +2023-12-11 2 mm 102 3 +2023-12-11 2 mm 103 3 +2023-12-11 2 mm 104 3 +2023-12-12 2 mi 105 2 +2023-12-12 2 mi 105 2 +2023-12-12 2 mi 106 2 +2023-12-12 2 mi 106 2 +2023-12-12 2 mi 107 2 +2023-12-12 2 mi 107 2 +2023-12-12 2 mi 108 2 +2023-12-12 2 mi 108 2 + diff --git a/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy b/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy index 4e7fa2d71df2e6..fa43c3852778f2 100644 --- a/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy +++ b/regression-test/framework/src/main/groovy/org/apache/doris/regression/suite/Suite.groovy @@ -1549,7 +1549,7 @@ class Suite implements GroovyInterceptable { // is_partition_statistics_ready is the bool value which identifying if partition row count is valid or not // if true, check if chosen by cbo or doesn't check void mv_rewrite_success(query_sql, mv_name, sync_cbo_rewrite = enable_sync_mv_cost_based_rewrite(), - is_partition_statistics_ready = true) { + is_partition_statistics_ready = true) { logger.info("query_sql = " + query_sql + ", mv_name = " + mv_name + ", sync_cbo_rewrite = " +sync_cbo_rewrite + ", is_partition_statistics_ready = " + is_partition_statistics_ready) if (!is_partition_statistics_ready) { @@ -1575,7 +1575,7 @@ class Suite implements GroovyInterceptable { // is_partition_statistics_ready is the bool value which identifying if partition row count is valid or not // if true, check if chosen by cbo or doesn't check void mv_rewrite_all_success( query_sql, mv_names, sync_cbo_rewrite = enable_sync_mv_cost_based_rewrite(), - is_partition_statistics_ready = true) { + is_partition_statistics_ready = true) { logger.info("query_sql = " + query_sql + ", mv_names = " + mv_names + ", sync_cbo_rewrite = " +sync_cbo_rewrite + ", is_partition_statistics_ready = " + is_partition_statistics_ready) if (!is_partition_statistics_ready) { @@ -1613,7 +1613,7 @@ class Suite implements GroovyInterceptable { // is_partition_statistics_ready is the bool value which identifying if partition row count is valid or not // if true, check if chosen by cbo or doesn't check void mv_rewrite_any_success(query_sql, mv_names, sync_cbo_rewrite = enable_sync_mv_cost_based_rewrite(), - is_partition_statistics_ready = true) { + is_partition_statistics_ready = true) { logger.info("query_sql = " + query_sql + ", mv_names = " + mv_names + ", sync_cbo_rewrite = " +sync_cbo_rewrite + ", is_partition_statistics_ready = " + is_partition_statistics_ready) if (!is_partition_statistics_ready) { @@ -1649,7 +1649,7 @@ class Suite implements GroovyInterceptable { // multi mv part in rewrite process, all rewrte success without check if chosen by cbo // sync_cbo_rewrite is the bool value which control sync mv is use cbo based mv rewrite void mv_rewrite_all_success_without_check_chosen(query_sql, mv_names, - sync_cbo_rewrite = enable_sync_mv_cost_based_rewrite()){ + sync_cbo_rewrite = enable_sync_mv_cost_based_rewrite()){ logger.info("query_sql = " + query_sql + ", mv_names = " + mv_names) if (!sync_cbo_rewrite) { explain { 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 4065e58c0b557d..daebc78f23457c 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 @@ -759,4 +759,53 @@ suite("outer_join") { async_mv_rewrite_success(db, mv11_0, query11_0, "mv11_0") order_qt_query11_0_after "${query11_0}" sql """ DROP MATERIALIZED VIEW IF EXISTS mv11_0""" + + + def mv12_0 = """ + select + o_orderdate, + o_shippriority, + o_comment, + o.o_code as o_o_code, + l_orderkey, + l_partkey, + l.o_code as l_o_code + from + orders_same_col o left + join lineitem_same_col l on l_orderkey = o_orderkey + left join partsupp on ps_partkey = l_partkey and l_suppkey = ps_suppkey; + """ + + def query12_0 = """ + select + o_orderdate, + o_shippriority, + o_comment, + o.o_code + l_orderkey, + l_partkey + from + orders_same_col o left + join lineitem_same_col l on l_orderkey = o_orderkey + left join partsupp on ps_partkey = l_partkey and l_suppkey = ps_suppkey + where l.o_code <> '91' + union all + select + o_orderdate, + o_shippriority, + o_comment, + o.o_code + l_orderkey, + l_partkey + from + orders_same_col o left + join lineitem_same_col l on l_orderkey = o_orderkey + left join partsupp on ps_partkey = l_partkey and l_suppkey = ps_suppkey + where l.o_code = '92'; + """ + + order_qt_query12_0_before "${query12_0}" + async_mv_rewrite_success(db, mv12_0, query12_0, "mv12_0") + order_qt_query12_0_after "${query12_0}" + sql """ DROP MATERIALIZED VIEW IF EXISTS mv12_0""" }