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

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

commit a237f7ec6e64ebc60def0c282fd6e3f685820a73
Author: 谢健 <jianx...@gmail.com>
AuthorDate: Thu Apr 25 16:31:38 2024 +0800

    [feature](Nereids): add equal set in functional dependencies (#33642)
---
 .../nereids/properties/FunctionalDependencies.java |  42 +++-
 .../trees/plans/BlockFuncDepsPropagation.java      |   5 +
 .../nereids/trees/plans/PropagateFuncDeps.java     |   5 +
 .../trees/plans/logical/LogicalAggregate.java      |   5 +
 .../trees/plans/logical/LogicalAssertNumRows.java  |   6 +
 .../plans/logical/LogicalCatalogRelation.java      |   5 +
 .../plans/logical/LogicalDeferMaterializeTopN.java |   5 +
 .../nereids/trees/plans/logical/LogicalExcept.java |  15 ++
 .../nereids/trees/plans/logical/LogicalFilter.java |  10 +
 .../trees/plans/logical/LogicalGenerate.java       |   6 +
 .../nereids/trees/plans/logical/LogicalHaving.java |  10 +
 .../trees/plans/logical/LogicalIntersect.java      |   9 +
 .../nereids/trees/plans/logical/LogicalJoin.java   |  16 ++
 .../nereids/trees/plans/logical/LogicalLimit.java  |   5 +
 .../trees/plans/logical/LogicalOneRowRelation.java |  16 ++
 .../nereids/trees/plans/logical/LogicalPlan.java   |   3 +
 .../trees/plans/logical/LogicalProject.java        |  21 ++
 .../nereids/trees/plans/logical/LogicalRepeat.java |   5 +
 .../trees/plans/logical/LogicalSqlCache.java       |  21 +-
 .../trees/plans/logical/LogicalSubQueryAlias.java  |  11 +
 .../nereids/trees/plans/logical/LogicalTopN.java   |   5 +
 .../nereids/trees/plans/logical/LogicalUnion.java  |  61 ++++++
 .../nereids/trees/plans/logical/LogicalView.java   |   6 +
 .../nereids/trees/plans/logical/LogicalWindow.java |   6 +
 .../apache/doris/nereids/util/ExpressionUtils.java |   8 +
 .../doris/nereids/util/ImmutableEqualSet.java      |  69 +++++--
 .../doris/nereids/properties/EqualSetTest.java     | 230 +++++++++++++++++++++
 .../suites/nereids_syntax_p0/join_order.groovy     |   8 +-
 28 files changed, 575 insertions(+), 39 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java
index 2b0c1f5c914..a516bf9ae1c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/FunctionalDependencies.java
@@ -18,11 +18,13 @@
 package org.apache.doris.nereids.properties;
 
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.util.ImmutableEqualSet;
 
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -36,15 +38,20 @@ public class FunctionalDependencies {
 
     public static final FunctionalDependencies EMPTY_FUNC_DEPS
             = new FunctionalDependencies(new NestedSet().toImmutable(),
-                    new NestedSet().toImmutable(), new 
ImmutableSet.Builder<FdItem>().build());
+                    new NestedSet().toImmutable(), new 
ImmutableSet.Builder<FdItem>().build(),
+                    ImmutableEqualSet.empty());
     private final NestedSet uniqueSet;
     private final NestedSet uniformSet;
     private final ImmutableSet<FdItem> fdItems;
 
-    private FunctionalDependencies(NestedSet uniqueSet, NestedSet uniformSet, 
ImmutableSet<FdItem> fdItems) {
+    private final ImmutableEqualSet<Slot> equalSet;
+
+    private FunctionalDependencies(
+            NestedSet uniqueSet, NestedSet uniformSet, ImmutableSet<FdItem> 
fdItems, ImmutableEqualSet<Slot> equalSet) {
         this.uniqueSet = uniqueSet;
         this.uniformSet = uniformSet;
         this.fdItems = fdItems;
+        this.equalSet = equalSet;
     }
 
     public boolean isEmpty() {
@@ -87,13 +94,26 @@ public class FunctionalDependencies {
         return slotSet.stream().noneMatch(Slot::nullable) && 
isUniform(slotSet);
     }
 
+    public boolean isNullSafeEqual(Slot l, Slot r) {
+        return equalSet.isEqual(l, r);
+    }
+
+    public boolean isEqualAndNotNotNull(Slot l, Slot r) {
+        return equalSet.isEqual(l, r) && !l.nullable() && !r.nullable();
+    }
+
+    public List<Set<Slot>> calAllEqualSet() {
+        return equalSet.calEqualSetList();
+    }
+
     public ImmutableSet<FdItem> getFdItems() {
         return fdItems;
     }
 
     @Override
     public String toString() {
-        return String.format("FuncDeps[uniform:%s, unique:%s, fdItems:%s]", 
uniformSet, uniqueSet, fdItems);
+        return String.format("FuncDeps[uniform:%s, unique:%s, fdItems:%s, 
equalSet:%s]",
+                uniformSet, uniqueSet, fdItems, equalSet);
     }
 
     /**
@@ -103,17 +123,21 @@ public class FunctionalDependencies {
         private final NestedSet uniqueSet;
         private final NestedSet uniformSet;
         private ImmutableSet<FdItem> fdItems;
+        private final ImmutableEqualSet.Builder<Slot> equalSetBuilder;
 
         public Builder() {
             uniqueSet = new NestedSet();
             uniformSet = new NestedSet();
             fdItems = new ImmutableSet.Builder<FdItem>().build();
+            equalSetBuilder = new ImmutableEqualSet.Builder<>();
         }
 
         public Builder(FunctionalDependencies other) {
             this.uniformSet = new NestedSet(other.uniformSet);
             this.uniqueSet = new NestedSet(other.uniqueSet);
             this.fdItems = ImmutableSet.copyOf(other.fdItems);
+            equalSetBuilder = new ImmutableEqualSet.Builder<>(other.equalSet);
+
         }
 
         public void addUniformSlot(Slot slot) {
@@ -147,11 +171,20 @@ public class FunctionalDependencies {
         public void addFunctionalDependencies(FunctionalDependencies fd) {
             uniformSet.add(fd.uniformSet);
             uniqueSet.add(fd.uniqueSet);
+            equalSetBuilder.addEqualSet(fd.equalSet);
+        }
+
+        public void addEqualPair(Slot l, Slot r) {
+            equalSetBuilder.addEqualPair(l, r);
+        }
+
+        public void addEqualSet(FunctionalDependencies functionalDependencies) 
{
+            equalSetBuilder.addEqualSet(functionalDependencies.equalSet);
         }
 
         public FunctionalDependencies build() {
             return new FunctionalDependencies(uniqueSet.toImmutable(), 
uniformSet.toImmutable(),
-                    ImmutableSet.copyOf(fdItems));
+                    ImmutableSet.copyOf(fdItems), equalSetBuilder.build());
         }
 
         public void pruneSlots(Set<Slot> outputSlots) {
@@ -162,6 +195,7 @@ public class FunctionalDependencies {
         public void replace(Map<Slot, Slot> replaceMap) {
             uniformSet.replace(replaceMap);
             uniqueSet.replace(replaceMap);
+            equalSetBuilder.replace(replaceMap);
         }
     }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
index a679ad0f7d2..68464364371 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BlockFuncDepsPropagation.java
@@ -46,4 +46,9 @@ public interface BlockFuncDepsPropagation extends LogicalPlan 
{
     default void computeUniform(FunctionalDependencies.Builder fdBuilder) {
         // don't generate
     }
+
+    @Override
+    default void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        // don't generate
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java
index f37059049ae..3d3d3cc8271 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PropagateFuncDeps.java
@@ -66,4 +66,9 @@ public interface PropagateFuncDeps extends LogicalPlan {
     default void computeUniform(FunctionalDependencies.Builder fdBuilder) {
         
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
     }
+
+    @Override
+    default void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
index 73ddde6cef5..031ea8a46b5 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
@@ -397,4 +397,9 @@ public class LogicalAggregate<CHILD_TYPE extends Plan>
 
         return builder.build();
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java
index cad8d6e14d6..a3f540d1847 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAssertNumRows.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.FdItem;
+import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement;
@@ -145,4 +146,9 @@ public class LogicalAssertNumRows<CHILD_TYPE extends Plan> 
extends LogicalUnary<
             getOutput().forEach(fdBuilder::addUniformSlot);
         }
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
index 433feb741ba..277695be079 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalCatalogRelation.java
@@ -215,4 +215,9 @@ public abstract class LogicalCatalogRelation extends 
LogicalRelation implements
         }
         return slotSet.build();
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        // don't generate any equal pair
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java
index 06c03f17f04..cf894fbce3a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalDeferMaterializeTopN.java
@@ -209,4 +209,9 @@ public class LogicalDeferMaterializeTopN<CHILD_TYPE extends 
Plan> extends Logica
                 "columnIdSlot", columnIdSlot
         );
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java
index d0223954411..5cfec0a4ed7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalExcept.java
@@ -21,6 +21,7 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.ExprFdItem;
 import org.apache.doris.nereids.properties.FdFactory;
 import org.apache.doris.nereids.properties.FdItem;
+import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
@@ -148,6 +149,20 @@ public class LogicalExcept extends LogicalSetOperation {
         fdBuilder.replace(replaceMap);
     }
 
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies());
+        Map<Slot, Slot> replaceMap = new HashMap<>();
+        List<Slot> output = getOutput();
+        List<? extends Slot> originalOutputs = regularChildrenOutputs.isEmpty()
+                ? child(0).getOutput()
+                : regularChildrenOutputs.get(0);
+        for (int i = 0; i < output.size(); i++) {
+            replaceMap.put(originalOutputs.get(i), output.get(i));
+        }
+        fdBuilder.replace(replaceMap);
+    }
+
     @Override
     public void computeUniform(Builder fdBuilder) {
         
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
index 69d378fd6a5..45b96bda451 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
@@ -17,6 +17,7 @@
 
 package org.apache.doris.nereids.trees.plans.logical;
 
+import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
@@ -166,4 +167,13 @@ public class LogicalFilter<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
         getConjuncts().forEach(e -> 
fdBuilder.addUniformSlot(ExpressionUtils.extractUniformSlot(e)));
         
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
     }
+
+    @Override
+    public void computeEqualSet(Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies());
+        for (Expression expression : getConjuncts()) {
+            Optional<Pair<Slot, Slot>> equalSlot = 
ExpressionUtils.extractEqualSlot(expression);
+            equalSlot.ifPresent(slotSlotPair -> 
fdBuilder.addEqualPair(slotSlotPair.first, slotSlotPair.second));
+        }
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
index a54a7514dbc..c0195e11fed 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalGenerate.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.FdItem;
+import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -169,4 +170,9 @@ public class LogicalGenerate<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD
     public void computeUniform(Builder fdBuilder) {
         
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
index da526c33af9..41ee1b14712 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
@@ -17,6 +17,7 @@
 
 package org.apache.doris.nereids.trees.plans.logical;
 
+import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
@@ -138,6 +139,15 @@ public class LogicalHaving<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
         return builder.build();
     }
 
+    @Override
+    public void computeEqualSet(Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies());
+        for (Expression expression : getConjuncts()) {
+            Optional<Pair<Slot, Slot>> equalSlot = 
ExpressionUtils.extractEqualSlot(expression);
+            equalSlot.ifPresent(slotSlotPair -> 
fdBuilder.addEqualPair(slotSlotPair.first, slotSlotPair.second));
+        }
+    }
+
     @Override
     public String toString() {
         return Utils.toSqlString("LogicalHaving", "predicates", 
getPredicate());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java
index e9e4889a8e5..3d3ccd9ad8e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalIntersect.java
@@ -139,6 +139,15 @@ public class LogicalIntersect extends LogicalSetOperation {
         }
     }
 
+    @Override
+    public void computeEqualSet(Builder fdBuilder) {
+        for (Plan child : children) {
+            fdBuilder.addEqualSet(
+                    child.getLogicalProperties().getFunctionalDependencies());
+            replaceSlotInFuncDeps(fdBuilder, child.getOutput(), getOutput());
+        }
+    }
+
     @Override
     public ImmutableSet<FdItem> computeFdItems() {
         Set<NamedExpression> output = ImmutableSet.copyOf(getOutput());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
index ea92aeca22d..9ad0b5ab23f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
@@ -696,4 +696,20 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
             
fdBuilder.addUniformSlot(left().getLogicalProperties().getFunctionalDependencies());
         }
     }
+
+    @Override
+    public void computeEqualSet(Builder fdBuilder) {
+        if (!joinType.isLeftSemiOrAntiJoin()) {
+            
fdBuilder.addEqualSet(right().getLogicalProperties().getFunctionalDependencies());
+        }
+        if (!joinType.isRightSemiOrAntiJoin()) {
+            
fdBuilder.addEqualSet(left().getLogicalProperties().getFunctionalDependencies());
+        }
+        if (joinType.isInnerJoin()) {
+            for (Expression expression : getHashJoinConjuncts()) {
+                Optional<Pair<Slot, Slot>> equalSlot = 
ExpressionUtils.extractEqualSlot(expression);
+                equalSlot.ifPresent(slotSlotPair -> 
fdBuilder.addEqualPair(slotSlotPair.first, slotSlotPair.second));
+            }
+        }
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java
index 02558fe2ed2..0fdf3212d88 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLimit.java
@@ -181,4 +181,9 @@ public class LogicalLimit<CHILD_TYPE extends Plan> extends 
LogicalUnary<CHILD_TY
         }
         return fdItems;
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
index 78a1e9a3a5e..b6e5af2c591 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
@@ -23,6 +23,7 @@ import org.apache.doris.nereids.properties.FdFactory;
 import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
@@ -37,7 +38,9 @@ import org.apache.doris.nereids.util.Utils;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
@@ -157,4 +160,17 @@ public class LogicalOneRowRelation extends LogicalRelation 
implements OneRowRela
 
         return builder.build();
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        Map<Expression, NamedExpression> aliasMap = new HashMap<>();
+        for (NamedExpression namedExpr : getOutputs()) {
+            if (namedExpr instanceof Alias) {
+                if (aliasMap.containsKey(namedExpr.child(0))) {
+                    fdBuilder.addEqualPair(namedExpr.toSlot(), 
aliasMap.get(namedExpr.child(0)).toSlot());
+                }
+                aliasMap.put(namedExpr.child(0), namedExpr);
+            }
+        }
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
index c0fb6ae7365..8c3be3b0fa4 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
@@ -63,6 +63,7 @@ public interface LogicalPlan extends Plan {
         FunctionalDependencies.Builder fdBuilder = new 
FunctionalDependencies.Builder();
         computeUniform(fdBuilder);
         computeUnique(fdBuilder);
+        computeEqualSet(fdBuilder);
         ImmutableSet<FdItem> fdItems = computeFdItems();
         fdBuilder.addFdItems(fdItems);
         return fdBuilder.build();
@@ -73,4 +74,6 @@ public interface LogicalPlan extends Plan {
     void computeUnique(FunctionalDependencies.Builder fdBuilder);
 
     void computeUniform(FunctionalDependencies.Builder fdBuilder);
+
+    void computeEqualSet(FunctionalDependencies.Builder fdBuilder);
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
index ebecee0d3ae..de23bc6f5b0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
@@ -23,6 +23,7 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.BoundStar;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
@@ -41,7 +42,9 @@ import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.ImmutableSet;
 import org.json.JSONObject;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 
@@ -282,4 +285,22 @@ public class LogicalProject<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_
         }
         fdBuilder.pruneSlots(getOutputSet());
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        Map<Expression, NamedExpression> aliasMap = new HashMap<>();
+        
fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies());
+        for (NamedExpression expr : getProjects()) {
+            if (expr instanceof Alias) {
+                if (aliasMap.containsKey(expr.child(0))) {
+                    fdBuilder.addEqualPair(expr.toSlot(), 
aliasMap.get(expr.child(0)).toSlot());
+                }
+                aliasMap.put(expr.child(0), expr);
+                if (expr.child(0).isSlot()) {
+                    fdBuilder.addEqualPair(expr.toSlot(), (Slot) 
expr.child(0));
+                }
+            }
+        }
+        fdBuilder.pruneSlots(getOutputSet());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java
index 95ae92b6866..9c24fab3352 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRepeat.java
@@ -204,4 +204,9 @@ public class LogicalRepeat<CHILD_TYPE extends Plan> extends 
LogicalUnary<CHILD_T
 
         return builder.build();
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child().getLogicalProperties().getFunctionalDependencies());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSqlCache.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSqlCache.java
index 663044d569f..26c3006d5e5 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSqlCache.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSqlCache.java
@@ -20,11 +20,10 @@ package org.apache.doris.nereids.trees.plans.logical;
 import org.apache.doris.analysis.Expr;
 import org.apache.doris.common.util.DebugUtil;
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.properties.FdItem;
-import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.BlockFuncDepsPropagation;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
 import org.apache.doris.nereids.trees.plans.TreeStringPlan;
@@ -36,14 +35,13 @@ import org.apache.doris.qe.ResultSet;
 import org.apache.doris.thrift.TUniqueId;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 
 /** LogicalSqlCache */
-public class LogicalSqlCache extends LogicalLeaf implements SqlCache, 
TreeStringPlan {
+public class LogicalSqlCache extends LogicalLeaf implements SqlCache, 
TreeStringPlan, BlockFuncDepsPropagation {
     private final TUniqueId queryId;
     private final List<String> columnLabels;
     private final List<Expr> resultExprs;
@@ -137,19 +135,4 @@ public class LogicalSqlCache extends LogicalLeaf 
implements SqlCache, TreeString
     public String getChildrenTreeString() {
         return planBody;
     }
-
-    @Override
-    public ImmutableSet<FdItem> computeFdItems() {
-        return ImmutableSet.of();
-    }
-
-    @Override
-    public void computeUnique(Builder fdBuilder) {
-
-    }
-
-    @Override
-    public void computeUniform(Builder fdBuilder) {
-
-    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java
index ea9fd143c94..5d23561b1a9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSubQueryAlias.java
@@ -191,6 +191,17 @@ public class LogicalSubQueryAlias<CHILD_TYPE extends Plan> 
extends LogicalUnary<
         return ImmutableSet.of();
     }
 
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies());
+        Map<Slot, Slot> replaceMap = new HashMap<>();
+        List<Slot> outputs = getOutput();
+        for (int i = 0; i < outputs.size(); i++) {
+            replaceMap.put(child(0).getOutput().get(i), outputs.get(i));
+        }
+        fdBuilder.replace(replaceMap);
+    }
+
     public void setRelationId(RelationId relationId) {
         this.relationId = relationId;
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java
index 791ded58cce..dd1c171ca2c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalTopN.java
@@ -187,6 +187,11 @@ public class LogicalTopN<CHILD_TYPE extends Plan> extends 
LogicalUnary<CHILD_TYP
         }
     }
 
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies());
+    }
+
     @Override
     public ImmutableSet<FdItem> computeFdItems() {
         ImmutableSet<FdItem> fdItems = 
child(0).getLogicalProperties().getFunctionalDependencies().getFdItems();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java
index 57d944bc2b9..8c5bbfcc6a6 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnion.java
@@ -25,6 +25,7 @@ import 
org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.LogicalProperties;
 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.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
@@ -36,7 +37,12 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
@@ -196,6 +202,61 @@ public class LogicalUnion extends LogicalSetOperation 
implements Union, OutputPr
         // don't propagate uniform slots
     }
 
+    private List<Set<Integer>> mapSlotToIndex(Plan plan, List<Set<Slot>> 
equalSlotsList) {
+        Map<Slot, Integer> slotToIndex = new HashMap<>();
+        for (int i = 0; i < plan.getOutput().size(); i++) {
+            slotToIndex.put(plan.getOutput().get(i), i);
+        }
+        List<Set<Integer>> equalSlotIndicesList = new ArrayList<>();
+        for (Set<Slot> equalSlots : equalSlotsList) {
+            Set<Integer> equalSlotIndices = new HashSet<>();
+            for (Slot slot : equalSlots) {
+                if (slotToIndex.containsKey(slot)) {
+                    equalSlotIndices.add(slotToIndex.get(slot));
+                }
+            }
+            if (equalSlotIndices.size() > 1) {
+                equalSlotIndicesList.add(equalSlotIndices);
+            }
+        }
+        return equalSlotIndicesList;
+    }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        List<Set<Integer>> unionEqualSlotIndicesList = new ArrayList<>();
+
+        for (Plan child : children) {
+            List<Set<Slot>> childEqualSlotsList =
+                    
child.getLogicalProperties().getFunctionalDependencies().calAllEqualSet();
+            List<Set<Integer>> childEqualSlotsIndicesList = 
mapSlotToIndex(child, childEqualSlotsList);
+            if (unionEqualSlotIndicesList.isEmpty()) {
+                unionEqualSlotIndicesList = childEqualSlotsIndicesList;
+            } else {
+                // Only all child of union has the equal pair, we keep the 
equal pair.
+                // It means we should calculate the intersection of all child
+                for (Set<Integer> childEqualSlotIndices : 
childEqualSlotsIndicesList) {
+                    for (Set<Integer> unionEqualSlotIndices : 
unionEqualSlotIndicesList) {
+                        if (Collections.disjoint(childEqualSlotIndices, 
unionEqualSlotIndices)) {
+                            
unionEqualSlotIndices.retainAll(childEqualSlotIndices);
+                        }
+                    }
+                }
+            }
+
+            List<Slot> ouputList = getOutput();
+            for (Set<Integer> equalSlotIndices : unionEqualSlotIndicesList) {
+                if (equalSlotIndices.size() <= 1) {
+                    continue;
+                }
+                int first = equalSlotIndices.iterator().next();
+                for (int idx : equalSlotIndices) {
+                    fdBuilder.addEqualPair(ouputList.get(first), 
ouputList.get(idx));
+                }
+            }
+        }
+    }
+
     @Override
     public ImmutableSet<FdItem> computeFdItems() {
         Set<NamedExpression> output = ImmutableSet.copyOf(getOutput());
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java
index 200ccc2ffcb..371d3cae43f 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java
@@ -21,6 +21,7 @@ import org.apache.doris.catalog.View;
 import org.apache.doris.nereids.exceptions.AnalysisException;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.FdItem;
+import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -146,4 +147,9 @@ public class LogicalView<BODY extends Plan> extends 
LogicalUnary<BODY> {
     public void computeUniform(Builder fdBuilder) {
         
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java
index b03a3365402..56a306b6005 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.FdItem;
+import org.apache.doris.nereids.properties.FunctionalDependencies;
 import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -324,4 +325,9 @@ public class LogicalWindow<CHILD_TYPE extends Plan> extends 
LogicalUnary<CHILD_T
             }
         }
     }
+
+    @Override
+    public void computeEqualSet(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addEqualSet(child(0).getLogicalProperties().getFunctionalDependencies());
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
index 51eef31de2f..a6a4d999a92 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
@@ -20,6 +20,7 @@ package org.apache.doris.nereids.util;
 import org.apache.doris.catalog.TableIf.TableType;
 import org.apache.doris.common.MaterializedViewException;
 import org.apache.doris.common.NereidsException;
+import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
 import org.apache.doris.nereids.rules.expression.rules.FoldConstantRule;
@@ -130,6 +131,13 @@ public class ExpressionUtils {
         }
     }
 
+    public static Optional<Pair<Slot, Slot>> extractEqualSlot(Expression expr) 
{
+        if (expr instanceof EqualTo && expr.child(0).isSlot() && 
expr.child(1).isSlot()) {
+            return Optional.of(Pair.of((Slot) expr.child(0), (Slot) 
expr.child(1)));
+        }
+        return Optional.empty();
+    }
+
     public static Optional<Expression> optionalAnd(List<Expression> 
expressions) {
         if (expressions.isEmpty()) {
             return Optional.empty();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ImmutableEqualSet.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ImmutableEqualSet.java
index f5f3dd75b51..35bc78ac117 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ImmutableEqualSet.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ImmutableEqualSet.java
@@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
 
 /**
@@ -44,34 +45,66 @@ public class ImmutableEqualSet<T> {
      * Builder for ImmutableEqualSet.
      */
     public static class Builder<T> {
-        private final Map<T, T> parent = new LinkedHashMap<>();
-        private final Map<T, Integer> size = new LinkedHashMap<>();
+        private Map<T, T> parent;
+
+        Builder(Map<T, T> parent) {
+            this.parent = parent;
+        }
+
+        public Builder() {
+            this(new LinkedHashMap<>());
+        }
+
+        public Builder(ImmutableEqualSet<T> equalSet) {
+            this(new LinkedHashMap<>(equalSet.root));
+        }
+
+        /**
+         * replace all key value according replace map
+         */
+        public void replace(Map<T, T> replaceMap) {
+            Map<T, T> newMap = new LinkedHashMap<>();
+            for (Entry<T, T> entry : parent.entrySet()) {
+                newMap.put(replaceMap.getOrDefault(entry.getKey(), 
entry.getKey()),
+                        replaceMap.getOrDefault(entry.getValue(), 
entry.getValue()));
+            }
+            parent = newMap;
+        }
 
         /**
          * Add a equal pair
          */
         public void addEqualPair(T a, T b) {
+            if (!parent.containsKey(a)) {
+                parent.put(a, a);
+            }
+            if (!parent.containsKey(b)) {
+                parent.put(b, b);
+            }
             T root1 = findRoot(a);
             T root2 = findRoot(b);
             if (root1 != root2) {
-                parent.put(b, root1);
-                findRoot(b);
+                parent.put(root1, root2);
             }
         }
 
-        private T findRoot(T a) {
-            parent.putIfAbsent(a, a); // Ensure that the element is added
-            size.putIfAbsent(a, 1); // Initialize size to 1
+        public void addEqualSet(ImmutableEqualSet<T> equalSet) {
+            this.parent.putAll(equalSet.root);
+        }
 
-            if (!parent.get(a).equals(a)) {
-                parent.put(a, findRoot(parent.get(a))); // Path compression
+        private T findRoot(T a) {
+            if (a.equals(parent.get(a))) {
+                return parent.get(a);
             }
-            return parent.get(a);
+            return findRoot(parent.get(a));
         }
 
         public ImmutableEqualSet<T> build() {
-            parent.keySet().forEach(this::findRoot);
-            return new ImmutableEqualSet<>(parent);
+            ImmutableMap.Builder<T, T> foldMapBuilder = new 
ImmutableMap.Builder<>();
+            for (T k : parent.keySet()) {
+                foldMapBuilder.put(k, findRoot(k));
+            }
+            return new ImmutableEqualSet<>(foldMapBuilder.build());
         }
     }
 
@@ -103,4 +136,16 @@ public class ImmutableEqualSet<T> {
     public Set<T> getAllItemSet() {
         return ImmutableSet.copyOf(root.keySet());
     }
+
+    public boolean isEqual(T l, T r) {
+        if (!root.containsKey(l) || !root.containsKey(r)) {
+            return false;
+        }
+        return root.get(l) == root.get(r);
+    }
+
+    @Override
+    public String toString() {
+        return root.toString();
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/EqualSetTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/EqualSetTest.java
new file mode 100644
index 00000000000..fec795ccbe2
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/EqualSetTest.java
@@ -0,0 +1,230 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.properties;
+
+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.types.IntegerType;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.utframe.TestWithFeService;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class EqualSetTest extends TestWithFeService {
+    Slot slot1 = new SlotReference("1", IntegerType.INSTANCE, false);
+    Slot slot2 = new SlotReference("2", IntegerType.INSTANCE, false);
+    Slot slot3 = new SlotReference("1", IntegerType.INSTANCE, false);
+    Slot slot4 = new SlotReference("1", IntegerType.INSTANCE, false);
+
+    @Override
+    protected void runBeforeAll() throws Exception {
+        createDatabase("test");
+        createTable("create table test.agg (\n"
+                + "id int not null,\n"
+                + "id2 int replace not null,\n"
+                + "name varchar(128) replace not null )\n"
+                + "AGGREGATE KEY(id)\n"
+                + "distributed by hash(id) buckets 10\n"
+                + "properties('replication_num' = '1');");
+        createTable("create table test.uni (\n"
+                + "id int not null,\n"
+                + "id2 int not null,\n"
+                + "name varchar(128) not null)\n"
+                + "UNIQUE KEY(id)\n"
+                + "distributed by hash(id) buckets 10\n"
+                + "properties('replication_num' = '1');");
+        connectContext.setDatabase("test");
+    }
+
+    @Test
+    void testAgg() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg where id2 = id group by id, 
id2")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+    }
+
+    @Test
+    void testTopNLimit() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg where id2 = id limit 1")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg where id2 = id limit 1 order 
by id")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+    }
+
+    @Test
+    void testSetOp() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg where id2 = id intersect 
select id, id2 from agg")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg where id2 = id except select 
id, id2 from agg")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg where id2 = id union all 
select id, id2 from agg")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isEmpty());
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg where id2 = id union all 
select id, id2 from agg where id2 = id")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg union all select id, id2 
from agg where id2 = id")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isEmpty());
+    }
+
+    @Test
+    void testFilterHaving() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg where id2 = id")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg  group by id, id2 having id 
= id2")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+    }
+
+    @Test
+    void testGenerate() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from agg lateral view 
explode([1,2,3]) tmp1 as e1 where id = id2")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+    }
+
+    @Test
+    void testJoin() {
+        // inner join
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select uni.id, agg.id from agg join uni "
+                        + "where agg.id = uni.id")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+
+        // foj
+        plan = PlanChecker.from(connectContext)
+                .analyze("select t1.id, t2.id, t3.id from agg as t1 join uni 
as t2 "
+                        + " on t1.id = t2.id  full outer join uni as t3 on 
t1.id2 = t2.id2")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        
Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies()
+                .isEqualAndNotNotNull(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        
Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(2)));
+
+        // loj
+        plan = PlanChecker.from(connectContext)
+                .analyze("select t1.id, t2.id, t3.id from agg as t1 join uni 
as t2 "
+                        + " on t1.id = t2.id  left outer join uni as t3 on 
t1.id2 = t2.id2")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isEqualAndNotNotNull(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        
Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(2)));
+
+        // roj
+        plan = PlanChecker.from(connectContext)
+                .analyze("select t1.id, t2.id, t3.id from agg as t1 join uni 
as t2 "
+                        + " on t1.id = t2.id  right outer join uni as t3 on 
t1.id2 = t2.id2")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        
Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies()
+                .isEqualAndNotNotNull(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        
Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies()
+                .isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(2)));
+    }
+
+    @Test
+    void testOneRowRelation() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select 1, 1")
+                .rewrite()
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+    }
+
+    @Test
+    void testProject() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id as o1, id as o2, id2 as o4, 1 as c1, 1 as 
c2 from uni where id = id2")
+                .rewrite()
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(2)));
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(1), 
plan.getOutput().get(2)));
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(3), 
plan.getOutput().get(4)));
+    }
+
+    @Test
+    void testSubQuery() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2 from (select id, id2 from agg where 
id = id2) t")
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+    }
+
+    @Test
+    void testWindow() {
+        // partition by uniform
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id, id2, row_number() over(partition by id) 
from agg where id = id2")
+                .rewrite()
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isNullSafeEqual(plan.getOutput().get(0), 
plan.getOutput().get(1)));
+    }
+
+}
diff --git a/regression-test/suites/nereids_syntax_p0/join_order.groovy 
b/regression-test/suites/nereids_syntax_p0/join_order.groovy
index 64056e0202b..461c2bb4485 100644
--- a/regression-test/suites/nereids_syntax_p0/join_order.groovy
+++ b/regression-test/suites/nereids_syntax_p0/join_order.groovy
@@ -43,7 +43,7 @@ suite("join_order") {
     """
     sql """ drop table if exists outerjoin_C_order;"""
     sql """
-        create table outerjoin_C_order ( c int not null )
+        create table outerjoin_C_order ( c int not null, c2 int not null )
         ENGINE=OLAP
         DISTRIBUTED BY HASH(c) BUCKETS 1
         PROPERTIES (
@@ -77,7 +77,7 @@ suite("join_order") {
 
     sql """insert into outerjoin_A_order values( 1,2 );"""
     sql """insert into outerjoin_B_order values( 1 );"""
-    sql """insert into outerjoin_C_order values( 1 );"""
+    sql """insert into outerjoin_C_order values( 1,2 );"""
     sql """insert into outerjoin_D_order values( 1,2,3 );"""
     sql """insert into outerjoin_E_order values( 1,2 );"""
 
@@ -96,13 +96,13 @@ suite("join_order") {
 
     sql 'set disable_join_reorder=true;'
     explain {
-        sql("select * from outerjoin_A_order, outerjoin_B_order, 
outerjoin_C_order where outerjoin_A_order.a1 = outerjoin_C_order.c and 
outerjoin_B_order.b = outerjoin_C_order.c;")
+        sql("select * from outerjoin_A_order, outerjoin_B_order, 
outerjoin_C_order where outerjoin_A_order.a1 = outerjoin_C_order.c and 
outerjoin_B_order.b = outerjoin_C_order.c2;")
         contains "CROSS JOIN"
     }
 
     sql 'set disable_join_reorder=false;'
     explain {
-        sql("select * from outerjoin_A_order, outerjoin_B_order, 
outerjoin_C_order where outerjoin_A_order.a1 = outerjoin_C_order.c and 
outerjoin_B_order.b = outerjoin_C_order.c;")
+        sql("select * from outerjoin_A_order, outerjoin_B_order, 
outerjoin_C_order where outerjoin_A_order.a1 = outerjoin_C_order.c and 
outerjoin_B_order.b = outerjoin_C_order.c2;")
         notContains "CROSS JOIN"
     }
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org
For additional commands, e-mail: commits-h...@doris.apache.org

Reply via email to