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

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


The following commit(s) were added to refs/heads/branch-3.1 by this push:
     new ffcf01f4687 branch-3.1: [fix](Nereids) not generate duplicate exprid 
after convert outer to anti rule #52798 (#53773)
ffcf01f4687 is described below

commit ffcf01f46876af4335fa73a1c6fc72e1acfc1bc9
Author: morrySnow <[email protected]>
AuthorDate: Fri Jul 25 17:45:10 2025 +0800

    branch-3.1: [fix](Nereids) not generate duplicate exprid after convert 
outer to anti rule #52798 (#53773)
    
    cherry picked from #52798
---
 .../doris/nereids/jobs/executor/Rewriter.java      |   7 +-
 .../org/apache/doris/nereids/rules/RuleSet.java    |   2 -
 .../rules/expression/ExpressionRewrite.java        | 186 ++++++++++++++++++++-
 .../rules/rewrite/ConvertOuterJoinToAntiJoin.java  |  86 +++++++---
 .../nereids/rules/rewrite/ExprIdRewriter.java      | 184 --------------------
 .../expressions/StatementScopeIdGenerator.java     |   2 +-
 .../trees/plans/logical/LogicalCTEConsumer.java    |  17 ++
 .../rewrite/ConvertOuterJoinToAntiJoinTest.java    |  22 ++-
 .../rules/rewrite/EliminateOuterJoinTest.java      |   5 +-
 .../rules/rewrite/SplitMultiDistinctTest.java      |  76 +++++----
 .../org/apache/doris/nereids/util/PlanChecker.java |   9 +
 .../distinct_split/disitinct_split.out             | Bin 9558 -> 9553 bytes
 .../transform_outer_join_to_anti.groovy            |   8 +
 13 files changed, 342 insertions(+), 262 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
index ec3a0ed2ec7..555f9dbca90 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java
@@ -49,6 +49,7 @@ import 
org.apache.doris.nereids.rules.rewrite.CollectCteConsumerOutput;
 import org.apache.doris.nereids.rules.rewrite.CollectFilterAboveConsumer;
 import org.apache.doris.nereids.rules.rewrite.ColumnPruning;
 import org.apache.doris.nereids.rules.rewrite.ConvertInnerOrCrossJoin;
+import org.apache.doris.nereids.rules.rewrite.ConvertOuterJoinToAntiJoin;
 import org.apache.doris.nereids.rules.rewrite.CountDistinctRewrite;
 import org.apache.doris.nereids.rules.rewrite.CountLiteralRewrite;
 import org.apache.doris.nereids.rules.rewrite.CreatePartitionTopNFromWindow;
@@ -448,8 +449,7 @@ public class Rewriter extends AbstractBatchJobExecutor {
                                 new CollectCteConsumerOutput()
                         )
                 ),
-                topic("Collect used column", custom(RuleType.COLLECT_COLUMNS, 
QueryColumnCollector::new)
-            )
+                topic("Collect used column", custom(RuleType.COLLECT_COLUMNS, 
QueryColumnCollector::new))
         )
     );
 
@@ -457,6 +457,7 @@ public class Rewriter extends AbstractBatchJobExecutor {
             ImmutableSet.of(LogicalCTEAnchor.class),
             () -> jobs(
                 // after variant sub path pruning, we need do column pruning 
again
+                bottomUp(RuleSet.PUSH_DOWN_FILTERS),
                 custom(RuleType.COLUMN_PRUNING, ColumnPruning::new),
                 bottomUp(ImmutableList.of(
                         new PushDownFilterThroughProject(),
@@ -550,6 +551,8 @@ public class Rewriter extends AbstractBatchJobExecutor {
                         topic("rewrite cte sub-tree before sub path push down",
                                 custom(RuleType.REWRITE_CTE_CHILDREN, () -> 
new RewriteCteChildren(beforePushDownJobs))
                         )));
+                rewriteJobs.addAll(jobs(topic("convert outer join to anti",
+                        custom(RuleType.CONVERT_OUTER_JOIN_TO_ANTI, 
ConvertOuterJoinToAntiJoin::new))));
                 if (needOrExpansion) {
                     rewriteJobs.addAll(jobs(topic("or expansion",
                             custom(RuleType.OR_EXPANSION, () -> 
OrExpansion.INSTANCE))));
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
index bcd12ac17d2..15943a25a90 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
@@ -86,7 +86,6 @@ import 
org.apache.doris.nereids.rules.implementation.LogicalTVFRelationToPhysica
 import org.apache.doris.nereids.rules.implementation.LogicalTopNToPhysicalTopN;
 import 
org.apache.doris.nereids.rules.implementation.LogicalUnionToPhysicalUnion;
 import 
org.apache.doris.nereids.rules.implementation.LogicalWindowToPhysicalWindow;
-import org.apache.doris.nereids.rules.rewrite.ConvertOuterJoinToAntiJoin;
 import org.apache.doris.nereids.rules.rewrite.CreatePartitionTopNFromWindow;
 import org.apache.doris.nereids.rules.rewrite.EliminateFilter;
 import org.apache.doris.nereids.rules.rewrite.EliminateOuterJoin;
@@ -148,7 +147,6 @@ public class RuleSet {
             new PushDownFilterThroughGenerate(),
             new PushDownProjectThroughLimit(),
             new EliminateOuterJoin(),
-            new ConvertOuterJoinToAntiJoin(),
             new MergeProjects(),
             new MergeFilters(),
             new MergeGenerates(),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionRewrite.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionRewrite.java
index 376c22c7079..d8b9145727d 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionRewrite.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/ExpressionRewrite.java
@@ -20,6 +20,7 @@ package org.apache.doris.nereids.rules.expression;
 import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.pattern.ExpressionPatternRules;
 import org.apache.doris.nereids.pattern.ExpressionPatternTraverseListeners;
+import org.apache.doris.nereids.pattern.MatchingContext;
 import org.apache.doris.nereids.properties.OrderKey;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
@@ -28,26 +29,39 @@ import 
org.apache.doris.nereids.rules.rewrite.RewriteRuleFactory;
 import org.apache.doris.nereids.trees.expressions.EqualPredicate;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.OrderExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.expressions.functions.Function;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalCTEConsumer;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalGenerate;
 import org.apache.doris.nereids.trees.plans.logical.LogicalHaving;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPartitionTopN;
 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.logical.LogicalSetOperation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalSink;
 import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
+import org.apache.doris.nereids.trees.plans.logical.LogicalTopN;
+import org.apache.doris.nereids.trees.plans.logical.LogicalWindow;
 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.ImmutableList.Builder;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.ImmutableSet;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -80,7 +94,19 @@ public class ExpressionRewrite implements RewriteRuleFactory 
{
                 new JoinExpressionRewrite().build(),
                 new SortExpressionRewrite().build(),
                 new LogicalRepeatRewrite().build(),
-                new HavingExpressionRewrite().build());
+                new HavingExpressionRewrite().build(),
+                new LogicalPartitionTopNExpressionRewrite().build(),
+                new LogicalTopNExpressionRewrite().build(),
+                new LogicalSetOperationRewrite().build(),
+                new LogicalWindowRewrite().build(),
+                new LogicalCteConsumerRewrite().build(),
+                new LogicalResultSinkRewrite().build(),
+                new LogicalFileSinkRewrite().build(),
+                new LogicalHiveTableSinkRewrite().build(),
+                new LogicalIcebergTableSinkRewrite().build(),
+                new LogicalJdbcTableSinkRewrite().build(),
+                new LogicalOlapTableSinkRewrite().build(),
+                new LogicalDeferMaterializeResultSinkRewrite().build());
     }
 
     /** GenerateExpressionRewrite */
@@ -278,6 +304,164 @@ public class ExpressionRewrite implements 
RewriteRuleFactory {
         }
     }
 
+    private class LogicalWindowRewrite extends OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return logicalWindow().thenApply(ctx -> {
+                LogicalWindow<Plan> window = ctx.root;
+                List<NamedExpression> windowExpressions = 
window.getWindowExpressions();
+                ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
+                List<NamedExpression> result = rewriteAll(windowExpressions, 
rewriter, context);
+                return window.withExpressionsAndChild(result, window.child());
+            })
+            .toRule(RuleType.REWRITE_WINDOW_EXPRESSION);
+        }
+    }
+
+    private class LogicalSetOperationRewrite extends OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return logicalSetOperation().thenApply(ctx -> {
+                LogicalSetOperation setOperation = ctx.root;
+                List<List<SlotReference>> slotsList = 
setOperation.getRegularChildrenOutputs();
+                List<List<SlotReference>> newSlotsList = new ArrayList<>();
+                ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
+                for (List<SlotReference> slots : slotsList) {
+                    List<SlotReference> result = rewriteAll(slots, rewriter, 
context);
+                    newSlotsList.add(result);
+                }
+                return 
setOperation.withChildrenAndTheirOutputs(setOperation.children(), newSlotsList);
+            })
+            .toRule(RuleType.REWRITE_SET_OPERATION_EXPRESSION);
+        }
+    }
+
+    private class LogicalTopNExpressionRewrite extends OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return logicalTopN().thenApply(ctx -> {
+                LogicalTopN<Plan> topN = ctx.root;
+                List<OrderKey> orderKeys = topN.getOrderKeys();
+                ImmutableList.Builder<OrderKey> rewrittenOrderKeys
+                        = 
ImmutableList.builderWithExpectedSize(orderKeys.size());
+                ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
+                boolean changed = false;
+                for (OrderKey k : orderKeys) {
+                    Expression expression = rewriter.rewrite(k.getExpr(), 
context);
+                    changed |= expression != k.getExpr();
+                    rewrittenOrderKeys.add(new OrderKey(expression, k.isAsc(), 
k.isNullFirst()));
+                }
+                return changed ? 
topN.withOrderKeys(rewrittenOrderKeys.build()) : topN;
+            }).toRule(RuleType.REWRITE_TOPN_EXPRESSION);
+        }
+    }
+
+    private class LogicalPartitionTopNExpressionRewrite extends 
OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return logicalPartitionTopN().thenApply(ctx -> {
+                LogicalPartitionTopN<Plan> partitionTopN = ctx.root;
+                ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
+                List<OrderExpression> newOrderExpressions = new ArrayList<>();
+                for (OrderExpression orderExpression : 
partitionTopN.getOrderKeys()) {
+                    OrderKey orderKey = orderExpression.getOrderKey();
+                    Expression expr = rewriter.rewrite(orderKey.getExpr(), 
context);
+                    OrderKey newOrderKey = new OrderKey(expr, 
orderKey.isAsc(), orderKey.isNullFirst());
+                    newOrderExpressions.add(new OrderExpression(newOrderKey));
+                }
+                List<Expression> result = 
rewriteAll(partitionTopN.getPartitionKeys(), rewriter, context);
+                return partitionTopN.withPartitionKeysAndOrderKeys(result, 
newOrderExpressions);
+            }).toRule(RuleType.REWRITE_PARTITION_TOPN_EXPRESSION);
+        }
+    }
+
+    private class LogicalCteConsumerRewrite extends OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return logicalCTEConsumer().thenApply(ctx -> {
+                LogicalCTEConsumer consumer = ctx.root;
+                boolean changed = false;
+                ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
+                ImmutableMap.Builder<Slot, Slot> cToPBuilder = 
ImmutableMap.builder();
+                ImmutableMultimap.Builder<Slot, Slot> pToCBuilder = 
ImmutableMultimap.builder();
+                for (Map.Entry<Slot, Slot> entry : 
consumer.getConsumerToProducerOutputMap().entrySet()) {
+                    Slot key = (Slot) rewriter.rewrite(entry.getKey(), 
context);
+                    Slot value = (Slot) rewriter.rewrite(entry.getValue(), 
context);
+                    cToPBuilder.put(key, value);
+                    pToCBuilder.put(value, key);
+                    if (!key.equals(entry.getKey()) || 
!value.equals(entry.getValue())) {
+                        changed = true;
+                    }
+                }
+                return changed ? consumer.withTwoMaps(cToPBuilder.build(), 
pToCBuilder.build()) : consumer;
+            }).toRule(RuleType.REWRITE_TOPN_EXPRESSION);
+        }
+    }
+
+    private class LogicalResultSinkRewrite extends OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return 
logicalResultSink().thenApply(ExpressionRewrite.this::applyRewriteToSink)
+                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
+        }
+    }
+
+    private class LogicalFileSinkRewrite extends OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return 
logicalFileSink().thenApply(ExpressionRewrite.this::applyRewriteToSink)
+                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
+        }
+    }
+
+    private class LogicalHiveTableSinkRewrite extends OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return 
logicalHiveTableSink().thenApply(ExpressionRewrite.this::applyRewriteToSink)
+                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
+        }
+    }
+
+    private class LogicalIcebergTableSinkRewrite extends OneRewriteRuleFactory 
{
+        @Override
+        public Rule build() {
+            return 
logicalIcebergTableSink().thenApply(ExpressionRewrite.this::applyRewriteToSink)
+                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
+        }
+    }
+
+    private class LogicalJdbcTableSinkRewrite extends OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return 
logicalJdbcTableSink().thenApply(ExpressionRewrite.this::applyRewriteToSink)
+                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
+        }
+    }
+
+    private class LogicalOlapTableSinkRewrite extends OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return 
logicalOlapTableSink().thenApply(ExpressionRewrite.this::applyRewriteToSink)
+                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
+        }
+    }
+
+    private class LogicalDeferMaterializeResultSinkRewrite extends 
OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return 
logicalDeferMaterializeResultSink().thenApply(ExpressionRewrite.this::applyRewriteToSink)
+                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
+        }
+    }
+
+    private LogicalSink<Plan> applyRewriteToSink(MatchingContext<? extends 
LogicalSink<Plan>> ctx) {
+        LogicalSink<Plan> sink = ctx.root;
+        ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
+        List<NamedExpression> outputExprs = sink.getOutputExprs();
+        List<NamedExpression> result = rewriteAll(outputExprs, rewriter, 
context);
+        return sink.withOutputExprs(result);
+    }
+
     /** LogicalRepeatRewrite */
     public class LogicalRepeatRewrite extends OneRewriteRuleFactory {
         @Override
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ConvertOuterJoinToAntiJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ConvertOuterJoinToAntiJoin.java
index c9185fd1a3c..46445573055 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ConvertOuterJoinToAntiJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ConvertOuterJoinToAntiJoin.java
@@ -17,9 +17,9 @@
 
 package org.apache.doris.nereids.rules.rewrite;
 
-import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.jobs.JobContext;
 import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
@@ -28,9 +28,14 @@ import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 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.visitor.CustomRewriter;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
 import org.apache.doris.nereids.util.TypeUtils;
 
-import java.util.List;
+import com.google.common.collect.ImmutableList;
+
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -42,18 +47,41 @@ import java.util.stream.Collectors;
  * project(A.*)
  *    - LeftAntiJoin(A, B)
  */
-public class ConvertOuterJoinToAntiJoin extends OneRewriteRuleFactory {
+public class ConvertOuterJoinToAntiJoin extends 
DefaultPlanRewriter<Map<ExprId, ExprId>> implements CustomRewriter {
+    private ExprIdRewriter exprIdReplacer;
+
+    @Override
+    public Plan rewriteRoot(Plan plan, JobContext jobContext) {
+        if (!plan.containsType(LogicalJoin.class)) {
+            return plan;
+        }
+        Map<ExprId, ExprId> replaceMap = new HashMap<>();
+        ExprIdRewriter.ReplaceRule replaceRule = new 
ExprIdRewriter.ReplaceRule(replaceMap);
+        exprIdReplacer = new ExprIdRewriter(replaceRule, jobContext);
+        return plan.accept(this, replaceMap);
+    }
 
     @Override
-    public Rule build() {
-        return logicalFilter(logicalJoin()
-                .when(join -> join.getJoinType().isOuterJoin()))
-                .then(this::toAntiJoin)
-        .toRule(RuleType.CONVERT_OUTER_JOIN_TO_ANTI);
+    public Plan visit(Plan plan, Map<ExprId, ExprId> replaceMap) {
+        plan = visitChildren(this, plan, replaceMap);
+        plan = exprIdReplacer.rewriteExpr(plan, replaceMap);
+        return plan;
     }
 
-    private Plan toAntiJoin(LogicalFilter<LogicalJoin<Plan, Plan>> filter) {
+    @Override
+    public Plan visitLogicalFilter(LogicalFilter<? extends Plan> filter, 
Map<ExprId, ExprId> replaceMap) {
+        filter = (LogicalFilter<? extends Plan>) visit(filter, replaceMap);
+        if (!(filter.child() instanceof LogicalJoin)) {
+            return filter;
+        }
+        return toAntiJoin((LogicalFilter<LogicalJoin<Plan, Plan>>) filter, 
replaceMap);
+    }
+
+    private Plan toAntiJoin(LogicalFilter<LogicalJoin<Plan, Plan>> filter, 
Map<ExprId, ExprId> replaceMap) {
         LogicalJoin<Plan, Plan> join = filter.child();
+        if (!join.getJoinType().isLeftOuterJoin() && 
!join.getJoinType().isRightOuterJoin()) {
+            return filter;
+        }
 
         Set<Slot> alwaysNullSlots = filter.getConjuncts().stream()
                 .filter(p -> TypeUtils.isNull(p).isPresent())
@@ -66,33 +94,37 @@ public class ConvertOuterJoinToAntiJoin extends 
OneRewriteRuleFactory {
                 .filter(s -> alwaysNullSlots.contains(s) && !s.nullable())
                 .collect(Collectors.toSet());
 
-        Plan newJoin = null;
+        Plan newChild = null;
         if (join.getJoinType().isLeftOuterJoin() && 
!rightAlwaysNullSlots.isEmpty()) {
-            newJoin = join.withJoinTypeAndContext(JoinType.LEFT_ANTI_JOIN, 
join.getJoinReorderContext());
+            newChild = join.withJoinTypeAndContext(JoinType.LEFT_ANTI_JOIN, 
join.getJoinReorderContext());
         }
         if (join.getJoinType().isRightOuterJoin() && 
!leftAlwaysNullSlots.isEmpty()) {
-            newJoin = join.withJoinTypeAndContext(JoinType.RIGHT_ANTI_JOIN, 
join.getJoinReorderContext());
+            newChild = join.withJoinTypeAndContext(JoinType.RIGHT_ANTI_JOIN, 
join.getJoinReorderContext());
         }
-        if (newJoin == null) {
-            return null;
+        if (newChild == null) {
+            return filter;
         }
 
-        if (!newJoin.getOutputSet().containsAll(filter.getInputSlots())) {
+        if (!newChild.getOutputSet().containsAll(filter.getInputSlots())) {
             // if there are slots that don't belong to join output, we use 
null alias to replace them
             // such as:
             //   project(A.id, null as B.id)
             //       -  (A left anti join B)
-            Set<Slot> joinOutput = newJoin.getOutputSet();
-            List<NamedExpression> projects = filter.getOutput().stream()
-                    .map(s -> {
-                        if (joinOutput.contains(s)) {
-                            return s;
-                        } else {
-                            return new Alias(s.getExprId(), new 
NullLiteral(s.getDataType()), s.getName());
-                        }
-                    }).collect(Collectors.toList());
-            newJoin = new LogicalProject<>(projects, newJoin);
+            Set<Slot> joinOutputs = newChild.getOutputSet();
+            ImmutableList.Builder<NamedExpression> projectsBuilder = 
ImmutableList.builder();
+            for (NamedExpression e : filter.getOutput()) {
+                if (joinOutputs.contains(e)) {
+                    projectsBuilder.add(e);
+                } else {
+                    Alias newAlias = new Alias(new 
NullLiteral(e.getDataType()), e.getName(), e.getQualifier());
+                    replaceMap.put(e.getExprId(), newAlias.getExprId());
+                    projectsBuilder.add(newAlias);
+                }
+            }
+            newChild = new LogicalProject<>(projectsBuilder.build(), newChild);
+            return exprIdReplacer.rewriteExpr(filter.withChildren(newChild), 
replaceMap);
+        } else {
+            return filter.withChildren(newChild);
         }
-        return filter.withChildren(newJoin);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ExprIdRewriter.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ExprIdRewriter.java
index 60c9da4bc6e..5e065fa3724 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ExprIdRewriter.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/ExprIdRewriter.java
@@ -18,32 +18,20 @@
 package org.apache.doris.nereids.rules.rewrite;
 
 import org.apache.doris.nereids.jobs.JobContext;
-import org.apache.doris.nereids.pattern.MatchingContext;
 import org.apache.doris.nereids.pattern.Pattern;
-import org.apache.doris.nereids.properties.OrderKey;
 import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.expression.ExpressionPatternMatcher;
 import org.apache.doris.nereids.rules.expression.ExpressionPatternRuleFactory;
 import org.apache.doris.nereids.rules.expression.ExpressionRewrite;
-import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
 import org.apache.doris.nereids.rules.expression.ExpressionRuleExecutor;
 import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.NamedExpression;
-import org.apache.doris.nereids.trees.expressions.OrderExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalPartitionTopN;
-import org.apache.doris.nereids.trees.plans.logical.LogicalSetOperation;
-import org.apache.doris.nereids.trees.plans.logical.LogicalSink;
-import org.apache.doris.nereids.trees.plans.logical.LogicalTopN;
-import org.apache.doris.nereids.trees.plans.logical.LogicalWindow;
 
 import com.google.common.collect.ImmutableList;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -58,26 +46,6 @@ public class ExprIdRewriter extends ExpressionRewrite {
         this.jobContext = jobContext;
     }
 
-    @Override
-    public List<Rule> buildRules() {
-        ImmutableList.Builder<Rule> builder = ImmutableList.builder();
-        builder.addAll(super.buildRules());
-        builder.addAll(ImmutableList.of(
-                new LogicalPartitionTopNExpressionRewrite().build(),
-                new LogicalTopNExpressionRewrite().build(),
-                new LogicalSetOperationRewrite().build(),
-                new LogicalWindowRewrite().build(),
-                new LogicalResultSinkRewrite().build(),
-                new LogicalFileSinkRewrite().build(),
-                new LogicalHiveTableSinkRewrite().build(),
-                new LogicalIcebergTableSinkRewrite().build(),
-                new LogicalJdbcTableSinkRewrite().build(),
-                new LogicalOlapTableSinkRewrite().build(),
-                new LogicalDeferMaterializeResultSinkRewrite().build()
-                ));
-        return builder.build();
-    }
-
     /**rewriteExpr*/
     public Plan rewriteExpr(Plan plan, Map<ExprId, ExprId> replaceMap) {
         if (replaceMap.isEmpty()) {
@@ -129,156 +97,4 @@ public class ExprIdRewriter extends ExpressionRewrite {
             );
         }
     }
-
-    private class LogicalResultSinkRewrite extends OneRewriteRuleFactory {
-        @Override
-        public Rule build() {
-            return 
logicalResultSink().thenApply(ExprIdRewriter.this::applyRewrite)
-                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
-        }
-    }
-
-    private class LogicalFileSinkRewrite extends OneRewriteRuleFactory {
-        @Override
-        public Rule build() {
-            return 
logicalFileSink().thenApply(ExprIdRewriter.this::applyRewrite)
-                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
-        }
-    }
-
-    private class LogicalHiveTableSinkRewrite extends OneRewriteRuleFactory {
-        @Override
-        public Rule build() {
-            return 
logicalHiveTableSink().thenApply(ExprIdRewriter.this::applyRewrite)
-                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
-        }
-    }
-
-    private class LogicalIcebergTableSinkRewrite extends OneRewriteRuleFactory 
{
-        @Override
-        public Rule build() {
-            return 
logicalIcebergTableSink().thenApply(ExprIdRewriter.this::applyRewrite)
-                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
-        }
-    }
-
-    private class LogicalJdbcTableSinkRewrite extends OneRewriteRuleFactory {
-        @Override
-        public Rule build() {
-            return 
logicalJdbcTableSink().thenApply(ExprIdRewriter.this::applyRewrite)
-                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
-        }
-    }
-
-    private class LogicalOlapTableSinkRewrite extends OneRewriteRuleFactory {
-        @Override
-        public Rule build() {
-            return 
logicalOlapTableSink().thenApply(ExprIdRewriter.this::applyRewrite)
-                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
-        }
-    }
-
-    private class LogicalDeferMaterializeResultSinkRewrite extends 
OneRewriteRuleFactory {
-        @Override
-        public Rule build() {
-            return 
logicalDeferMaterializeResultSink().thenApply(ExprIdRewriter.this::applyRewrite)
-                    .toRule(RuleType.REWRITE_SINK_EXPRESSION);
-        }
-    }
-
-    private class LogicalSetOperationRewrite extends OneRewriteRuleFactory {
-        @Override
-        public Rule build() {
-            return logicalSetOperation().thenApply(ctx -> {
-                LogicalSetOperation setOperation = ctx.root;
-                List<List<SlotReference>> slotsList = 
setOperation.getRegularChildrenOutputs();
-                List<List<SlotReference>> newSlotsList = new ArrayList<>();
-                ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
-                for (List<SlotReference> slots : slotsList) {
-                    List<SlotReference> newSlots = rewriteAll(slots, rewriter, 
context);
-                    newSlotsList.add(newSlots);
-                }
-                if (newSlotsList.equals(slotsList)) {
-                    return setOperation;
-                }
-                return 
setOperation.withChildrenAndTheirOutputs(setOperation.children(), newSlotsList);
-            })
-            .toRule(RuleType.REWRITE_SET_OPERATION_EXPRESSION);
-        }
-    }
-
-    private class LogicalWindowRewrite extends OneRewriteRuleFactory {
-        @Override
-        public Rule build() {
-            return logicalWindow().thenApply(ctx -> {
-                LogicalWindow<Plan> window = ctx.root;
-                List<NamedExpression> windowExpressions = 
window.getWindowExpressions();
-                ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
-                List<NamedExpression> newWindowExpressions = 
rewriteAll(windowExpressions, rewriter, context);
-                if (newWindowExpressions.equals(windowExpressions)) {
-                    return window;
-                }
-                return window.withExpressionsAndChild(newWindowExpressions, 
window.child());
-            })
-            .toRule(RuleType.REWRITE_WINDOW_EXPRESSION);
-        }
-    }
-
-    private class LogicalTopNExpressionRewrite extends OneRewriteRuleFactory {
-        @Override
-        public Rule build() {
-            return logicalTopN().thenApply(ctx -> {
-                LogicalTopN<Plan> topN = ctx.root;
-                List<OrderKey> orderKeys = topN.getOrderKeys();
-                ImmutableList.Builder<OrderKey> rewrittenOrderKeys
-                        = 
ImmutableList.builderWithExpectedSize(orderKeys.size());
-                ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
-                boolean changed = false;
-                for (OrderKey k : orderKeys) {
-                    Expression expression = rewriter.rewrite(k.getExpr(), 
context);
-                    changed |= expression != k.getExpr();
-                    rewrittenOrderKeys.add(new OrderKey(expression, k.isAsc(), 
k.isNullFirst()));
-                }
-                return changed ? 
topN.withOrderKeys(rewrittenOrderKeys.build()) : topN;
-            }).toRule(RuleType.REWRITE_TOPN_EXPRESSION);
-        }
-    }
-
-    private class LogicalPartitionTopNExpressionRewrite extends 
OneRewriteRuleFactory {
-        @Override
-        public Rule build() {
-            return logicalPartitionTopN().thenApply(ctx -> {
-                LogicalPartitionTopN<Plan> partitionTopN = ctx.root;
-                ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
-                List<OrderExpression> newOrderExpressions = new ArrayList<>();
-                boolean changed = false;
-                for (OrderExpression orderExpression : 
partitionTopN.getOrderKeys()) {
-                    OrderKey orderKey = orderExpression.getOrderKey();
-                    Expression expr = rewriter.rewrite(orderKey.getExpr(), 
context);
-                    changed |= expr != orderKey.getExpr();
-                    OrderKey newOrderKey = new OrderKey(expr, 
orderKey.isAsc(), orderKey.isNullFirst());
-                    newOrderExpressions.add(new OrderExpression(newOrderKey));
-                }
-                List<Expression> newPartitionKeys = 
rewriteAll(partitionTopN.getPartitionKeys(), rewriter, context);
-                if 
(!newPartitionKeys.equals(partitionTopN.getPartitionKeys())) {
-                    changed = true;
-                }
-                if (!changed) {
-                    return partitionTopN;
-                }
-                return 
partitionTopN.withPartitionKeysAndOrderKeys(newPartitionKeys, 
newOrderExpressions);
-            }).toRule(RuleType.REWRITE_PARTITION_TOPN_EXPRESSION);
-        }
-    }
-
-    private LogicalSink<Plan> applyRewrite(MatchingContext<? extends 
LogicalSink<Plan>> ctx) {
-        LogicalSink<Plan> sink = ctx.root;
-        ExpressionRewriteContext context = new 
ExpressionRewriteContext(ctx.cascadesContext);
-        List<NamedExpression> outputExprs = sink.getOutputExprs();
-        List<NamedExpression> newOutputExprs = rewriteAll(outputExprs, 
rewriter, context);
-        if (outputExprs.equals(newOutputExprs)) {
-            return sink;
-        }
-        return sink.withOutputExprs(newOutputExprs);
-    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StatementScopeIdGenerator.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StatementScopeIdGenerator.java
index df7ef2ab69a..cf0ecc3cb9b 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StatementScopeIdGenerator.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StatementScopeIdGenerator.java
@@ -81,6 +81,6 @@ public class StatementScopeIdGenerator {
         if (ConnectContext.get() != null) {
             ConnectContext.get().setStatementContext(new StatementContext());
         }
-        statementContext = new StatementContext();
+        statementContext = new StatementContext(10000);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCTEConsumer.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCTEConsumer.java
index 2941392ea71..7c1079935a1 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCTEConsumer.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCTEConsumer.java
@@ -199,4 +199,21 @@ public class LogicalCTEConsumer extends LogicalRelation 
implements BlockFuncDeps
                 "relationId", relationId,
                 "name", name);
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+        LogicalCTEConsumer that = (LogicalCTEConsumer) o;
+        return Objects.equals(consumerToProducerOutputMap, 
that.consumerToProducerOutputMap);
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode();
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ConvertOuterJoinToAntiJoinTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ConvertOuterJoinToAntiJoinTest.java
index 1159fc2a7ce..b3166c22240 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ConvertOuterJoinToAntiJoinTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/ConvertOuterJoinToAntiJoinTest.java
@@ -32,17 +32,21 @@ import 
org.apache.doris.nereids.util.MemoPatternMatchSupported;
 import org.apache.doris.nereids.util.MemoTestUtils;
 import org.apache.doris.nereids.util.PlanChecker;
 import org.apache.doris.nereids.util.PlanConstructor;
+import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Sets;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 class ConvertOuterJoinToAntiJoinTest implements MemoPatternMatchSupported {
-    private final LogicalOlapScan scan1;
-    private final LogicalOlapScan scan2;
+    private LogicalOlapScan scan1;
+    private LogicalOlapScan scan2;
 
-    public ConvertOuterJoinToAntiJoinTest() throws Exception {
+    @BeforeEach
+    void setUp() throws Exception {
         // clear id so that slot id keep consistent every running
+        ConnectContext.remove();
         StatementScopeIdGenerator.clear();
         scan1 = PlanConstructor.newLogicalOlapScan(0, "t1", 0);
         scan2 = PlanConstructor.newLogicalOlapScan(1, "t2", 0);
@@ -58,7 +62,7 @@ class ConvertOuterJoinToAntiJoinTest implements 
MemoPatternMatchSupported {
 
         PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
                 .applyTopDown(new InferFilterNotNull())
-                .applyTopDown(new ConvertOuterJoinToAntiJoin())
+                .applyCustom(new ConvertOuterJoinToAntiJoin())
                 .printlnTree()
                 .matches(logicalJoin().when(join -> 
join.getJoinType().isLeftAntiJoin()));
     }
@@ -73,7 +77,7 @@ class ConvertOuterJoinToAntiJoinTest implements 
MemoPatternMatchSupported {
 
         PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
                 .applyTopDown(new InferFilterNotNull())
-                .applyTopDown(new ConvertOuterJoinToAntiJoin())
+                .applyCustom(new ConvertOuterJoinToAntiJoin())
                 .printlnTree()
                 .matches(logicalJoin().when(join -> 
join.getJoinType().isRightAntiJoin()));
     }
@@ -91,7 +95,7 @@ class ConvertOuterJoinToAntiJoinTest implements 
MemoPatternMatchSupported {
 
         PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
                 .applyTopDown(new InferFilterNotNull())
-                .applyTopDown(new ConvertOuterJoinToAntiJoin())
+                .applyCustom(new ConvertOuterJoinToAntiJoin())
                 .printlnTree()
                 .matches(logicalJoin().when(join -> 
join.getJoinType().isLeftAntiJoin()));
     }
@@ -109,7 +113,7 @@ class ConvertOuterJoinToAntiJoinTest implements 
MemoPatternMatchSupported {
 
         PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
                 .applyTopDown(new InferFilterNotNull())
-                .applyTopDown(new ConvertOuterJoinToAntiJoin())
+                .applyCustom(new ConvertOuterJoinToAntiJoin())
                 .printlnTree()
                 .matches(logicalJoin().when(join -> 
join.getJoinType().isLeftAntiJoin()));
     }
@@ -127,7 +131,7 @@ class ConvertOuterJoinToAntiJoinTest implements 
MemoPatternMatchSupported {
 
         PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
                 .applyTopDown(new InferFilterNotNull())
-                .applyTopDown(new ConvertOuterJoinToAntiJoin())
+                .applyCustom(new ConvertOuterJoinToAntiJoin())
                 .printlnTree()
                 .matches(logicalJoin().when(join -> 
join.getJoinType().isLeftOuterJoin()));
     }
@@ -146,7 +150,7 @@ class ConvertOuterJoinToAntiJoinTest implements 
MemoPatternMatchSupported {
 
         PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
                 .applyTopDown(new InferFilterNotNull())
-                .applyTopDown(new ConvertOuterJoinToAntiJoin())
+                .applyCustom(new ConvertOuterJoinToAntiJoin())
                 .printlnTree()
                 .matches(logicalJoin().when(join -> 
join.getJoinType().isLeftOuterJoin()));
     }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/EliminateOuterJoinTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/EliminateOuterJoinTest.java
index 255f1e82e00..f0034410163 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/EliminateOuterJoinTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/EliminateOuterJoinTest.java
@@ -21,6 +21,7 @@ import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.trees.expressions.And;
 import org.apache.doris.nereids.trees.expressions.GreaterThan;
 import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.Literal;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
@@ -69,7 +70,7 @@ class EliminateOuterJoinTest implements 
MemoPatternMatchSupported {
     void testEliminateRight() {
         LogicalPlan plan = new LogicalPlanBuilder(scan1)
                 .join(scan2, JoinType.RIGHT_OUTER_JOIN, Pair.of(0, 0))  // 
t1.id = t2.id
-                .filter(new GreaterThan(scan1.getOutput().get(0), 
Literal.of(1)))
+                .filter(new GreaterThan(scan1.getOutput().get(0), new 
IntegerLiteral(1)))
                 .build();
 
         PlanChecker.from(MemoTestUtils.createConnectContext(), plan)
@@ -81,7 +82,7 @@ class EliminateOuterJoinTest implements 
MemoPatternMatchSupported {
                         logicalFilter(
                                 logicalJoin().when(join -> 
join.getJoinType().isInnerJoin())
                         ).when(filter -> filter.getConjuncts().size() == 1)
-                                .when(filter -> 
Objects.equals(filter.getConjuncts().toString(), "[(id#0 > 1)]"))
+                                .when(filter -> 
Objects.equals(filter.getConjuncts().iterator().next(), new 
GreaterThan(scan1.getOutput().get(0), new IntegerLiteral(1))))
                 );
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/SplitMultiDistinctTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/SplitMultiDistinctTest.java
index ebc6331506a..acadb01e759 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/SplitMultiDistinctTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/SplitMultiDistinctTest.java
@@ -46,23 +46,25 @@ public class SplitMultiDistinctTest extends 
TestWithFeService implements MemoPat
                     physicalCTEAnchor(
                             physicalCTEProducer(any()),
                             physicalResultSink(
-                                    physicalProject(
+
                                             physicalNestedLoopJoin(
+                                                    physicalProject(
                                                     physicalHashAggregate(
                                                             physicalDistribute(
                                                                     
physicalHashAggregate(
                                                                             
physicalHashAggregate(
                                                                                
     physicalDistribute(
-                                                                               
             physicalHashAggregate(any())))))),
+                                                                               
             physicalHashAggregate(any()))))))),
                                                     physicalDistribute(
+                                                            physicalProject(
                                                             
physicalHashAggregate(
                                                                     
physicalDistribute(
                                                                             
physicalHashAggregate(
                                                                                
     physicalHashAggregate(
                                                                                
             physicalDistribute(
-                                                                               
                     physicalHashAggregate(any())))))))
+                                                                               
                     physicalHashAggregate(any()))))))))
                                             )
-                                    )
+
                             )
                     )
             );
@@ -78,23 +80,25 @@ public class SplitMultiDistinctTest extends 
TestWithFeService implements MemoPat
                     physicalCTEAnchor(
                             physicalCTEProducer(any()),
                             physicalResultSink(
-                                    physicalProject(
+
                                             physicalNestedLoopJoin(
+                                                    physicalProject(
                                                     physicalHashAggregate(
                                                             physicalDistribute(
                                                                     
physicalHashAggregate(
                                                                             
physicalHashAggregate(
                                                                                
     physicalDistribute(
-                                                                               
             physicalHashAggregate(any())))))),
+                                                                               
             physicalHashAggregate(any()))))))),
                                                     physicalDistribute(
+                                                            physicalProject(
                                                             
physicalHashAggregate(
                                                                     
physicalDistribute(
                                                                             
physicalHashAggregate(
                                                                                
     physicalHashAggregate(
                                                                                
             physicalDistribute(
-                                                                               
                     physicalHashAggregate(any())))))))
+                                                                               
                     physicalHashAggregate(any()))))))))
                                             )
-                                    )
+
                             )
                     )
             );
@@ -108,26 +112,26 @@ public class SplitMultiDistinctTest extends 
TestWithFeService implements MemoPat
             Plan plan = planner.getOptimizedPlan();
             MatchingUtils.assertMatches(plan,
                     physicalCTEAnchor(
-                            physicalCTEProducer(any()),
-                            physicalResultSink(
-                                    physicalProject(
-                                            physicalNestedLoopJoin(
-                                                    physicalHashAggregate(
-                                                            physicalDistribute(
-                                                                    
physicalHashAggregate(
-                                                                            
physicalHashAggregate(
-                                                                               
     physicalDistribute(
-                                                                               
             physicalHashAggregate(any())))))),
-                                                    physicalDistribute(
-                                                            
physicalHashAggregate(
-                                                                    
physicalDistribute(
-                                                                            
physicalHashAggregate(
-                                                                               
     physicalHashAggregate(
-                                                                               
             physicalDistribute(
-                                                                               
                     physicalHashAggregate(any())))))))
-                                            )
-                                    )
-                            )
+                        physicalCTEProducer(any()),
+                        physicalResultSink(
+                             physicalNestedLoopJoin(
+                                 physicalProject(
+                                     physicalHashAggregate(
+                                         physicalDistribute(
+                                             physicalHashAggregate(
+                                                 physicalHashAggregate(
+                                                     physicalDistribute(
+                                                         
physicalHashAggregate(any()))))))),
+                                     physicalDistribute(
+                                         physicalProject(
+                                             physicalHashAggregate(
+                                                 physicalDistribute(
+                                                     physicalHashAggregate(
+                                                         physicalHashAggregate(
+                                                             
physicalDistribute(
+                                                                
physicalHashAggregate(any()))))))))
+                                )
+                        )
                     )
             );
         });
@@ -142,19 +146,21 @@ public class SplitMultiDistinctTest extends 
TestWithFeService implements MemoPat
                     physicalCTEAnchor(
                             physicalCTEProducer(any()),
                             physicalResultSink(
-                                    physicalProject(
+
                                             physicalNestedLoopJoin(
+                                                    physicalProject(
                                                     physicalHashAggregate(
                                                             
physicalHashAggregate(
                                                                     
physicalDistribute(
-                                                                            
physicalHashAggregate(any())))),
+                                                                            
physicalHashAggregate(any()))))),
                                                     physicalDistribute(
+                                                            physicalProject(
                                                             
physicalHashAggregate(
                                                                     
physicalHashAggregate(
                                                                             
physicalDistribute(
-                                                                               
     physicalHashAggregate(any())))))
+                                                                               
     physicalHashAggregate(any()))))))
                                             )
-                                    )
+
                             )
                     )
             );
@@ -174,14 +180,16 @@ public class SplitMultiDistinctTest extends 
TestWithFeService implements MemoPat
                                     physicalDistribute(
                                             physicalProject(
                                                     physicalHashJoin(
+                                                            physicalProject(
                                                             
physicalHashAggregate(
                                                                     
physicalHashAggregate(
                                                                             
physicalDistribute(
-                                                                               
     physicalHashAggregate(any())))),
+                                                                               
     physicalHashAggregate(any()))))),
+                                                            physicalProject(
                                                             
physicalHashAggregate(
                                                                     
physicalHashAggregate(
                                                                             
physicalDistribute(
-                                                                               
     physicalHashAggregate(any()))))
+                                                                               
     physicalHashAggregate(any())))))
                                                     ).when(join ->
                                                         join.getJoinType() == 
JoinType.INNER_JOIN && join.getHashJoinConjuncts().get(0) instanceof 
NullSafeEqual
                                                     )
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
index 71d0f0101b0..6962572d07a 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
@@ -32,6 +32,7 @@ import org.apache.doris.nereids.jobs.cascades.DeriveStatsJob;
 import org.apache.doris.nereids.jobs.executor.Optimizer;
 import org.apache.doris.nereids.jobs.executor.Rewriter;
 import org.apache.doris.nereids.jobs.joinorder.JoinOrderJob;
+import org.apache.doris.nereids.jobs.rewrite.CustomRewriteJob;
 import org.apache.doris.nereids.jobs.rewrite.PlanTreeRewriteBottomUpJob;
 import org.apache.doris.nereids.jobs.rewrite.PlanTreeRewriteTopDownJob;
 import org.apache.doris.nereids.jobs.rewrite.RootPlanTreeRewriteJob;
@@ -202,6 +203,14 @@ public class PlanChecker {
         return this;
     }
 
+    public PlanChecker applyCustom(CustomRewriter customRewriter) {
+        CustomRewriteJob customRewriteJob = new CustomRewriteJob(() -> 
customRewriter, RuleType.TEST_REWRITE);
+        customRewriteJob.execute(cascadesContext.getCurrentJobContext());
+        cascadesContext.toMemo();
+        MemoValidator.validate(cascadesContext.getMemo());
+        return this;
+    }
+
     /**
      * apply a top down rewrite rule if you not care the ruleId
      *
diff --git 
a/regression-test/data/nereids_rules_p0/distinct_split/disitinct_split.out 
b/regression-test/data/nereids_rules_p0/distinct_split/disitinct_split.out
index a0aff0a9a19..ede0fb5259c 100644
Binary files 
a/regression-test/data/nereids_rules_p0/distinct_split/disitinct_split.out and 
b/regression-test/data/nereids_rules_p0/distinct_split/disitinct_split.out 
differ
diff --git 
a/regression-test/suites/nereids_syntax_p0/transform_outer_join_to_anti.groovy 
b/regression-test/suites/nereids_syntax_p0/transform_outer_join_to_anti.groovy
index ccbb8fd64a8..f806f4ce5c7 100644
--- 
a/regression-test/suites/nereids_syntax_p0/transform_outer_join_to_anti.groovy
+++ 
b/regression-test/suites/nereids_syntax_p0/transform_outer_join_to_anti.groovy
@@ -84,4 +84,12 @@ suite("transform_outer_join_to_anti") {
         sql("select * from eliminate_outer_join_A right outer join 
eliminate_outer_join_B on eliminate_outer_join_B.b = eliminate_outer_join_A.a 
where eliminate_outer_join_A.a is null and eliminate_outer_join_B.null_b is 
null and eliminate_outer_join_A.null_a is null")
         contains "ANTI JOIN"
     }
+
+    explain {
+        sql """with temp as (
+                   select * from eliminate_outer_join_A left outer join 
eliminate_outer_join_B on eliminate_outer_join_B.b = eliminate_outer_join_A.a 
where eliminate_outer_join_B.b is null
+               )
+               select * from temp t1 join temp t2"""
+        contains "ANTI JOIN"
+    }
 }


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

Reply via email to