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 1d7f367592e3f77b08152714241ed485f83e5fc2
Author: 谢健 <jianx...@gmail.com>
AuthorDate: Thu Apr 11 15:57:38 2024 +0800

    [refactor](Nereids): compute unique and uniform property respectively 
(#32908)
---
 .../nereids/properties/FunctionalDependencies.java |   4 +
 .../doris/nereids/trees/plans/AbstractPlan.java    |   2 +-
 .../trees/plans/BlockFuncDepsPropagation.java      |  18 +-
 .../nereids/trees/plans/PropagateFuncDeps.java     |  20 +-
 .../trees/plans/logical/LogicalAggregate.java      | 108 +++---
 .../trees/plans/logical/LogicalAssertNumRows.java  |  33 +-
 .../plans/logical/LogicalCatalogRelation.java      |  23 +-
 .../plans/logical/LogicalDeferMaterializeTopN.java |  29 +-
 .../nereids/trees/plans/logical/LogicalExcept.java |  59 ++--
 .../nereids/trees/plans/logical/LogicalFilter.java |  25 +-
 .../trees/plans/logical/LogicalGenerate.java       |  20 +-
 .../nereids/trees/plans/logical/LogicalHaving.java |  17 +-
 .../trees/plans/logical/LogicalIntersect.java      |  27 +-
 .../nereids/trees/plans/logical/LogicalJoin.java   | 130 ++++---
 .../nereids/trees/plans/logical/LogicalLimit.java  |  30 +-
 .../trees/plans/logical/LogicalOneRowRelation.java |  21 +-
 .../nereids/trees/plans/logical/LogicalPlan.java   |  17 +-
 .../trees/plans/logical/LogicalProject.java        |  65 ++--
 .../nereids/trees/plans/logical/LogicalRepeat.java |  19 +-
 .../trees/plans/logical/LogicalSubQueryAlias.java  |  26 +-
 .../nereids/trees/plans/logical/LogicalTopN.java   |  30 +-
 .../nereids/trees/plans/logical/LogicalUnion.java  |  22 +-
 .../nereids/trees/plans/logical/LogicalView.java   |  20 +-
 .../nereids/trees/plans/logical/LogicalWindow.java |  73 ++--
 .../apache/doris/nereids/util/ExpressionUtils.java |   4 +-
 .../properties/FunctionalDependenciesTest.java     |   4 +-
 .../doris/nereids/properties/UniformTest.java      | 207 ++++++++++++
 ...tionalDependenciesTest.java => UniqueTest.java} | 376 ++++++++++-----------
 28 files changed, 866 insertions(+), 563 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 c7e6030e137..2b0c1f5c914 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
@@ -136,6 +136,10 @@ public class FunctionalDependencies {
             uniqueSet.add(slotSet);
         }
 
+        public void addUniqueSlot(FunctionalDependencies 
functionalDependencies) {
+            uniqueSet.add(functionalDependencies.uniqueSet);
+        }
+
         public void addFdItems(ImmutableSet<FdItem> items) {
             fdItems = ImmutableSet.copyOf(items);
         }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AbstractPlan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AbstractPlan.java
index 286a92aab76..4747aa84898 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AbstractPlan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AbstractPlan.java
@@ -179,7 +179,7 @@ public abstract class AbstractPlan extends 
AbstractTreeNode<Plan> implements Pla
         } else {
             Supplier<List<Slot>> outputSupplier = 
Suppliers.memoize(this::computeOutput);
             Supplier<FunctionalDependencies> fdSupplier = () -> this 
instanceof LogicalPlan
-                    ? ((LogicalPlan) this).computeFuncDeps(outputSupplier)
+                    ? ((LogicalPlan) this).computeFuncDeps()
                     : FunctionalDependencies.EMPTY_FUNC_DEPS;
             return new LogicalProperties(outputSupplier, fdSupplier);
         }
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 07804587e3b..a679ad0f7d2 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
@@ -19,25 +19,31 @@ package org.apache.doris.nereids.trees.plans;
 
 import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
-import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 
 import com.google.common.collect.ImmutableSet;
 
-import java.util.List;
-import java.util.function.Supplier;
-
 /**
  * Block fd propagation, it always returns an empty fd
  */
 public interface BlockFuncDepsPropagation extends LogicalPlan {
     @Override
-    default FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
+    default FunctionalDependencies computeFuncDeps() {
         return FunctionalDependencies.EMPTY_FUNC_DEPS;
     }
 
     @Override
-    default ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    default ImmutableSet<FdItem> computeFdItems() {
         return ImmutableSet.of();
     }
+
+    @Override
+    default void computeUnique(FunctionalDependencies.Builder fdBuilder) {
+        // don't generate
+    }
+
+    @Override
+    default void computeUniform(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 c344f22ca21..f37059049ae 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
@@ -19,20 +19,16 @@ package org.apache.doris.nereids.trees.plans;
 
 import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
-import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 
 import com.google.common.collect.ImmutableSet;
 
-import java.util.List;
-import java.util.function.Supplier;
-
 /**
  * Propagate fd, keep children's fd
  */
 public interface PropagateFuncDeps extends LogicalPlan {
     @Override
-    default FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
+    default FunctionalDependencies computeFuncDeps() {
         if (children().size() == 1) {
             // Note when changing function dependencies, we always clone it.
             // So it's safe to return a reference
@@ -42,13 +38,13 @@ public interface PropagateFuncDeps extends LogicalPlan {
         children().stream()
                 .map(p -> p.getLogicalProperties().getFunctionalDependencies())
                 .forEach(builder::addFunctionalDependencies);
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
+        ImmutableSet<FdItem> fdItems = computeFdItems();
         builder.addFdItems(fdItems);
         return builder.build();
     }
 
     @Override
-    default ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    default ImmutableSet<FdItem> computeFdItems() {
         if (children().size() == 1) {
             // Note when changing function dependencies, we always clone it.
             // So it's safe to return a reference
@@ -60,4 +56,14 @@ public interface PropagateFuncDeps extends LogicalPlan {
                 .forEach(builder::addAll);
         return builder.build();
     }
+
+    @Override
+    default void computeUnique(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+    }
+
+    @Override
+    default void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addUniformSlot(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 fa4f891e7a2..1e5e5a45abe 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
@@ -21,17 +21,13 @@ import org.apache.doris.nereids.memo.GroupExpression;
 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.properties.TableFdItem;
-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;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
-import org.apache.doris.nereids.trees.expressions.VirtualSlotReference;
 import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait;
-import 
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
 import org.apache.doris.nereids.trees.expressions.functions.agg.Count;
 import org.apache.doris.nereids.trees.expressions.functions.agg.Ndv;
 import org.apache.doris.nereids.trees.plans.Plan;
@@ -45,12 +41,9 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
-import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.Set;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 /**
@@ -300,80 +293,85 @@ public class LogicalAggregate<CHILD_TYPE extends Plan>
                 hasPushed, sourceRepeat, Optional.empty(), Optional.empty(), 
normalizedChild);
     }
 
-    private void updateFuncDepsGroupByUnique(NamedExpression namedExpression, 
Builder fdBuilder) {
-        if (ExpressionUtils.isInjective(namedExpression)) {
-            
fdBuilder.addUniqueSlot(ImmutableSet.copyOf(namedExpression.getInputSlots()));
-            return;
+    private boolean isUniqueGroupByUnique(NamedExpression namedExpression) {
+        if (namedExpression.children().size() != 1) {
+            return false;
         }
+        Expression agg = namedExpression.child(0);
+        return ExpressionUtils.isInjectiveAgg(agg)
+                && 
child().getLogicalProperties().getFunctionalDependencies().isUniqueAndNotNull(agg.getInputSlots());
+    }
 
-        if (!(namedExpression instanceof Alias && namedExpression.child(0) 
instanceof AggregateFunction)) {
-            return;
+    private boolean isUniformGroupByUnique(NamedExpression namedExpression) {
+        if (namedExpression.children().size() != 1) {
+            return false;
         }
+        Expression agg = namedExpression.child(0);
+        return agg instanceof Count || agg instanceof Ndv;
+    }
 
-        AggregateFunction agg = (AggregateFunction) namedExpression.child(0);
-        if (agg instanceof Count || agg instanceof Ndv) {
-            fdBuilder.addUniformSlot(namedExpression.toSlot());
+    @Override
+    public void computeUnique(FunctionalDependencies.Builder fdBuilder) {
+        if (this.sourceRepeat.isPresent()) {
+            // roll up may generate new data
+            return;
+        }
+        FunctionalDependencies childFd = 
child(0).getLogicalProperties().getFunctionalDependencies();
+        ImmutableSet<Slot> groupByKeys = groupByExpressions.stream()
+                .map(s -> (Slot) s)
+                .collect(ImmutableSet.toImmutableSet());
+        // when group by all tuples, the result only have one row
+        if (groupByExpressions.isEmpty() || 
childFd.isUniformAndNotNull(groupByKeys)) {
+            getOutput().forEach(fdBuilder::addUniqueSlot);
             return;
         }
 
-        if (ExpressionUtils.isInjectiveAgg(agg)
-                && 
child().getLogicalProperties().getFunctionalDependencies().isUniqueAndNotNull(agg.getInputSlots()))
 {
-            fdBuilder.addUniqueSlot(namedExpression.toSlot());
+        // propagate all unique slots
+        fdBuilder.addUniqueSlot(childFd);
+
+        // group by keys is unique
+        fdBuilder.addUniqueSlot(groupByKeys);
+
+        // group by unique may has unique aggregate result
+        if (childFd.isUniqueAndNotNull(groupByKeys)) {
+            for (NamedExpression namedExpression : getOutputExpressions()) {
+                if (isUniqueGroupByUnique(namedExpression)) {
+                    fdBuilder.addUniqueSlot(namedExpression.toSlot());
+                }
+            }
         }
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
+    public void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        // always propagate uniform
         FunctionalDependencies childFd = 
child(0).getLogicalProperties().getFunctionalDependencies();
-        Set<Slot> outputSet = new HashSet<>(outputSupplier.get());
-        Builder fdBuilder = new Builder();
-        // when group by all tuples, the result only have one row
-        if (groupByExpressions.isEmpty()) {
-            outputSet.forEach(s -> {
-                fdBuilder.addUniformSlot(s);
-                fdBuilder.addUniqueSlot(s);
-            });
-            return fdBuilder.build();
-        }
+        fdBuilder.addUniformSlot(childFd);
 
-        // when group by complicate expression or virtual slot, just propagate 
uniform slots
-        if (groupByExpressions.stream()
-                .anyMatch(s -> !(s instanceof SlotReference) || s instanceof 
VirtualSlotReference)) {
-            fdBuilder.addUniformSlot(childFd);
-            fdBuilder.pruneSlots(outputSet);
-            return fdBuilder.build();
+        if (this.sourceRepeat.isPresent()) {
+            // roll up may generate new data
+            return;
         }
-
-        // when group by uniform slot, the result only have one row
         ImmutableSet<Slot> groupByKeys = groupByExpressions.stream()
                 .map(s -> (Slot) s)
                 .collect(ImmutableSet.toImmutableSet());
-        if (childFd.isUniformAndNotNull(groupByKeys)) {
-            outputSupplier.get().forEach(s -> {
-                fdBuilder.addUniformSlot(s);
-                fdBuilder.addUniqueSlot(s);
-            });
+        // when group by all tuples, the result only have one row
+        if (groupByExpressions.isEmpty() || 
childFd.isUniformAndNotNull(groupByKeys)) {
+            getOutput().forEach(fdBuilder::addUniformSlot);
+            return;
         }
 
-        // when group by unique slot, the result depends on agg func
         if (childFd.isUniqueAndNotNull(groupByKeys)) {
             for (NamedExpression namedExpression : getOutputExpressions()) {
-                updateFuncDepsGroupByUnique(namedExpression, fdBuilder);
+                if (isUniformGroupByUnique(namedExpression)) {
+                    fdBuilder.addUniformSlot(namedExpression.toSlot());
+                }
             }
         }
-
-        // group by keys is unique
-        fdBuilder.addUniqueSlot(groupByKeys);
-        fdBuilder.pruneSlots(outputSet);
-
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        fdBuilder.addFdItems(fdItems);
-
-        return fdBuilder.build();
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    public ImmutableSet<FdItem> computeFdItems() {
         ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
 
         ImmutableSet<SlotReference> groupByExprs = 
getGroupByExpressions().stream()
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 0279c9701ca..cad8d6e14d6 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
@@ -18,18 +18,21 @@
 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.Builder;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.AssertNumRowsElement;
+import 
org.apache.doris.nereids.trees.expressions.AssertNumRowsElement.Assertion;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
-import org.apache.doris.nereids.trees.plans.PropagateFuncDeps;
 import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Objects;
@@ -42,8 +45,7 @@ import java.util.stream.Collectors;
  * If the number of rows is more than the desired num of rows, the query will 
be cancelled.
  * The cancelled reason will be reported by Backend and displayed back to the 
user.
  */
-public class LogicalAssertNumRows<CHILD_TYPE extends Plan> extends 
LogicalUnary<CHILD_TYPE> implements
-        PropagateFuncDeps {
+public class LogicalAssertNumRows<CHILD_TYPE extends Plan> extends 
LogicalUnary<CHILD_TYPE> {
 
     private final AssertNumRowsElement assertNumRowsElement;
 
@@ -118,4 +120,29 @@ public class LogicalAssertNumRows<CHILD_TYPE extends Plan> 
extends LogicalUnary<
     public List<Slot> computeOutput() {
         return child().getOutput().stream().map(o -> 
o.withNullable(true)).collect(Collectors.toList());
     }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems() {
+        return ImmutableSet.of();
+    }
+
+    @Override
+    public void computeUnique(Builder fdBuilder) {
+        if (assertNumRowsElement.getDesiredNumOfRows() == 1
+                && (assertNumRowsElement.getAssertion() == Assertion.EQ
+                || assertNumRowsElement.getAssertion() == Assertion.LT
+                || assertNumRowsElement.getAssertion() == Assertion.LE)) {
+            getOutput().forEach(fdBuilder::addUniqueSlot);
+        }
+    }
+
+    @Override
+    public void computeUniform(Builder fdBuilder) {
+        if (assertNumRowsElement.getDesiredNumOfRows() == 1
+                && (assertNumRowsElement.getAssertion() == Assertion.EQ
+                || assertNumRowsElement.getAssertion() == Assertion.LT
+                || assertNumRowsElement.getAssertion() == Assertion.LE)) {
+            getOutput().forEach(fdBuilder::addUniformSlot);
+        }
+    }
 }
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 5f7982aae46..433feb741ba 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
@@ -49,7 +49,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 
 /**
  * abstract class catalog relation for logical relation
@@ -128,9 +127,16 @@ public abstract class LogicalCatalogRelation extends 
LogicalRelation implements
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
+    public FunctionalDependencies computeFuncDeps() {
         Builder fdBuilder = new Builder();
-        Set<Slot> outputSet = Utils.fastToImmutableSet(outputSupplier.get());
+        computeUnique(fdBuilder);
+        
fdBuilder.addFdItems(computeFdItems(Utils.fastToImmutableSet(getOutputSet())));
+        return fdBuilder.build();
+    }
+
+    @Override
+    public void computeUnique(FunctionalDependencies.Builder fdBuilder) {
+        Set<Slot> outputSet = Utils.fastToImmutableSet(getOutputSet());
         if (table instanceof OlapTable && ((OlapTable) 
table).getKeysType().isAggregationFamily()) {
             ImmutableSet.Builder<Slot> uniqSlots = 
ImmutableSet.builderWithExpectedSize(outputSet.size());
             for (Slot slot : outputSet) {
@@ -154,13 +160,16 @@ public abstract class LogicalCatalogRelation extends 
LogicalRelation implements
             Set<Column> columns = c.getUniqueKeys(table);
             fdBuilder.addUniqueSlot((ImmutableSet) 
findSlotsByColumn(outputSet, columns));
         }
-        fdBuilder.addFdItems(computeFdItems(outputSet));
-        return fdBuilder.build();
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
-        return computeFdItems(Utils.fastToImmutableSet(outputSupplier.get()));
+    public void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        // No uniform slot for catalog relation
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems() {
+        return computeFdItems(Utils.fastToImmutableSet(getOutputSet()));
     }
 
     private ImmutableSet<FdItem> computeFdItems(Set<Slot> outputSet) {
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 5bf0afe1fae..06c03f17f04 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
@@ -22,7 +22,6 @@ 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.properties.OrderKey;
 import org.apache.doris.nereids.trees.expressions.ExprId;
@@ -43,7 +42,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 
 /**
  * use for defer materialize top n
@@ -120,26 +118,29 @@ public class LogicalDeferMaterializeTopN<CHILD_TYPE 
extends Plan> extends Logica
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies fd = 
child(0).getLogicalProperties().getFunctionalDependencies();
+    public void computeUnique(FunctionalDependencies.Builder fdBuilder) {
         if (getLimit() == 1) {
-            Builder builder = new Builder();
-            List<Slot> output = outputSupplier.get();
-            output.forEach(builder::addUniformSlot);
-            output.forEach(builder::addUniqueSlot);
-            ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-            builder.addFdItems(fdItems);
-            fd = builder.build();
+            getOutput().forEach(fdBuilder::addUniqueSlot);
+        } else {
+            
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
         }
-        return fd;
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    public void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        if (getLimit() == 1) {
+            getOutput().forEach(fdBuilder::addUniformSlot);
+        } else {
+            
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+        }
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems() {
         ImmutableSet<FdItem> fdItems = 
child(0).getLogicalProperties().getFunctionalDependencies().getFdItems();
         if (getLimit() == 1) {
             ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
-            List<Slot> output = outputSupplier.get();
+            List<Slot> output = getOutput();
             ImmutableSet<SlotReference> slotSet = output.stream()
                     .filter(SlotReference.class::isInstance)
                     .map(SlotReference.class::cast)
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 707da68fe8f..d0223954411 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,7 +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;
 import org.apache.doris.nereids.trees.expressions.Slot;
@@ -39,7 +39,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 
 /**
  * Logical Except.
@@ -109,29 +108,8 @@ public class LogicalExcept extends LogicalSetOperation {
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies.Builder builder = new FunctionalDependencies
-                
.Builder(child(0).getLogicalProperties().getFunctionalDependencies());
-        Map<Slot, Slot> replaceMap = new HashMap<>();
-        List<Slot> output = outputSupplier.get();
-        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));
-        }
-        builder.replace(replaceMap);
-        if (qualifier == Qualifier.DISTINCT) {
-            builder.addUniqueSlot(ImmutableSet.copyOf(outputSupplier.get()));
-        }
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        builder.addFdItems(fdItems);
-        return builder.build();
-    }
-
-    @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
-        Set<NamedExpression> output = 
ImmutableSet.copyOf(outputSupplier.get());
+    public ImmutableSet<FdItem> computeFdItems() {
+        Set<NamedExpression> output = ImmutableSet.copyOf(getOutput());
         ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
 
         ImmutableSet<SlotReference> exprs = output.stream()
@@ -152,4 +130,35 @@ public class LogicalExcept extends LogicalSetOperation {
 
         return builder.build();
     }
+
+    @Override
+    public void computeUnique(Builder fdBuilder) {
+        
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+        if (qualifier == Qualifier.DISTINCT) {
+            fdBuilder.addUniqueSlot(ImmutableSet.copyOf(getOutput()));
+        }
+        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());
+        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);
+    }
 }
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 4375124a224..69d378fd6a5 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
@@ -19,7 +19,6 @@ 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;
@@ -41,7 +40,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -149,17 +147,7 @@ public class LogicalFilter<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        Builder fdBuilder = new Builder(
-                child().getLogicalProperties().getFunctionalDependencies());
-        getConjuncts().forEach(e -> 
fdBuilder.addUniformSlot(ExpressionUtils.extractUniformSlot(e)));
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        fdBuilder.addFdItems(fdItems);
-        return fdBuilder.build();
-    }
-
-    @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    public ImmutableSet<FdItem> computeFdItems() {
         ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
 
         ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
@@ -167,4 +155,15 @@ public class LogicalFilter<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
 
         return builder.build();
     }
+
+    @Override
+    public void computeUnique(Builder fdBuilder) {
+        
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+    }
+
+    @Override
+    public void computeUniform(Builder fdBuilder) {
+        getConjuncts().forEach(e -> 
fdBuilder.addUniformSlot(ExpressionUtils.extractUniformSlot(e)));
+        
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+    }
 }
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 0fb3964e511..a54a7514dbc 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,7 +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;
 import org.apache.doris.nereids.trees.expressions.Slot;
@@ -38,7 +38,6 @@ import com.google.common.collect.Lists;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.function.Supplier;
 
 /**
  * plan for table generator, the statement like: SELECT * FROM tbl LATERAL 
VIEW EXPLODE(c1) g as (gc1);
@@ -157,16 +156,17 @@ public class LogicalGenerate<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies.Builder builder = new 
FunctionalDependencies.Builder();
-        
builder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        builder.addFdItems(fdItems);
-        return builder.build();
+    public ImmutableSet<FdItem> computeFdItems() {
+        return ImmutableSet.of();
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
-        return ImmutableSet.of();
+    public void computeUnique(Builder fdBuilder) {
+        // don't generate and propagate unique
+    }
+
+    @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/LogicalHaving.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
index e4d01f12748..da526c33af9 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
@@ -19,7 +19,6 @@ 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;
@@ -39,7 +38,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 
 /**
  * Logical Having plan
@@ -120,17 +118,18 @@ public class LogicalHaving<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        Builder fdBuilder = new Builder(
-                child().getLogicalProperties().getFunctionalDependencies());
+    public void computeUnique(Builder fdBuilder) {
+        
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+    }
+
+    @Override
+    public void computeUniform(Builder fdBuilder) {
         getConjuncts().forEach(e -> 
fdBuilder.addUniformSlot(ExpressionUtils.extractUniformSlot(e)));
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        fdBuilder.addFdItems(fdItems);
-        return fdBuilder.build();
+        
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    public ImmutableSet<FdItem> computeFdItems() {
         ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
 
         ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
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 2fbdbd24561..e9e4889a8e5 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
@@ -22,6 +22,7 @@ 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;
 import org.apache.doris.nereids.trees.expressions.Slot;
@@ -39,7 +40,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 
 /**
  * Logical Intersect.
@@ -119,24 +119,29 @@ public class LogicalIntersect extends LogicalSetOperation 
{
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies.Builder builder = new 
FunctionalDependencies.Builder();
+    public void computeUnique(Builder fdBuilder) {
         for (Plan child : children) {
-            builder.addFunctionalDependencies(
+            fdBuilder.addUniqueSlot(
                     child.getLogicalProperties().getFunctionalDependencies());
-            replaceSlotInFuncDeps(builder, child.getOutput(), 
outputSupplier.get());
+            replaceSlotInFuncDeps(fdBuilder, child.getOutput(), getOutput());
         }
         if (qualifier == Qualifier.DISTINCT) {
-            builder.addUniqueSlot(ImmutableSet.copyOf(outputSupplier.get()));
+            fdBuilder.addUniqueSlot(ImmutableSet.copyOf(getOutput()));
+        }
+    }
+
+    @Override
+    public void computeUniform(Builder fdBuilder) {
+        for (Plan child : children) {
+            fdBuilder.addUniformSlot(
+                    child.getLogicalProperties().getFunctionalDependencies());
+            replaceSlotInFuncDeps(fdBuilder, child.getOutput(), getOutput());
         }
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        builder.addFdItems(fdItems);
-        return builder.build();
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
-        Set<NamedExpression> output = 
ImmutableSet.copyOf(outputSupplier.get());
+    public ImmutableSet<FdItem> computeFdItems() {
+        Set<NamedExpression> output = ImmutableSet.copyOf(getOutput());
         ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
 
         ImmutableSet<SlotReference> exprs = output.stream()
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 d18a454fe73..ea92aeca22d 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
@@ -24,7 +24,6 @@ import 
org.apache.doris.nereids.jobs.joinorder.hypergraph.bitmap.LongBitmap;
 import org.apache.doris.nereids.memo.GroupExpression;
 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.properties.TableFdItem;
@@ -58,7 +57,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import javax.annotation.Nullable;
@@ -460,76 +458,7 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        if (isMarkJoin()) {
-            // TODO disable function dependence calculation for mark join, but 
need re-think this in future.
-            return FunctionalDependencies.EMPTY_FUNC_DEPS;
-        }
-        //1. NALAJ and FOJ block functional dependencies
-        if (joinType.isNullAwareLeftAntiJoin() || joinType.isFullOuterJoin()) {
-            return FunctionalDependencies.EMPTY_FUNC_DEPS;
-        }
-
-        // left/right semi/anti join propagate left/right functional 
dependencies
-        if (joinType.isLeftAntiJoin() || joinType.isLefSemiJoin()) {
-            return left().getLogicalProperties().getFunctionalDependencies();
-        }
-        if (joinType.isRightSemiJoin() || joinType.isRightAntiJoin()) {
-            return right().getLogicalProperties().getFunctionalDependencies();
-        }
-
-        // if there is non-equal join conditions, block functional dependencies
-        if (!otherJoinConjuncts.isEmpty()) {
-            return FunctionalDependencies.EMPTY_FUNC_DEPS;
-        }
-
-        Pair<Set<Slot>, Set<Slot>> keys = extractNullRejectHashKeys();
-        if (keys == null) {
-            return FunctionalDependencies.EMPTY_FUNC_DEPS;
-        }
-
-        // Note here we only check whether the left is unique.
-        // So the hash condition can't be null-safe
-        // TODO: consider Null-safe hash condition when left and rigth is not 
nullable
-        boolean isLeftUnique = left().getLogicalProperties()
-                .getFunctionalDependencies().isUnique(keys.first);
-        boolean isRightUnique = right().getLogicalProperties()
-                .getFunctionalDependencies().isUnique(keys.second);
-        Builder fdBuilder = new Builder();
-        if (joinType.isInnerJoin()) {
-            // inner join propagate uniforms slots
-            // And if the hash keys is unique, inner join can propagate all 
functional dependencies
-            if (isLeftUnique && isRightUnique) {
-                
fdBuilder.addFunctionalDependencies(left().getLogicalProperties().getFunctionalDependencies());
-                
fdBuilder.addFunctionalDependencies(right().getLogicalProperties().getFunctionalDependencies());
-            } else {
-                
fdBuilder.addUniformSlot(left().getLogicalProperties().getFunctionalDependencies());
-                
fdBuilder.addUniformSlot(right().getLogicalProperties().getFunctionalDependencies());
-            }
-        }
-
-        // left/right outer join propagate left/right uniforms slots
-        // And if the right/left hash keys is unique,
-        // join can propagate left/right functional dependencies
-        if (joinType.isLeftOuterJoin()) {
-            if (isRightUnique) {
-                return 
left().getLogicalProperties().getFunctionalDependencies();
-            }
-            
fdBuilder.addUniformSlot(left().getLogicalProperties().getFunctionalDependencies());
-        }
-        if (joinType.isRightOuterJoin()) {
-            if (isLeftUnique) {
-                return 
left().getLogicalProperties().getFunctionalDependencies();
-            }
-            
fdBuilder.addUniformSlot(left().getLogicalProperties().getFunctionalDependencies());
-        }
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        fdBuilder.addFdItems(fdItems);
-        return fdBuilder.build();
-    }
-
-    @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    public ImmutableSet<FdItem> computeFdItems() {
         ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
         if (isMarkJoin() || joinType.isNullAwareLeftAntiJoin()
                 || joinType.isFullOuterJoin()
@@ -710,4 +639,61 @@ public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, 
RIGHT_CHILD_TYPE extends
         logicalJoin.put("Properties", properties);
         return logicalJoin;
     }
+
+    @Override
+    public void computeUnique(Builder fdBuilder) {
+        if (isMarkJoin()) {
+            // TODO disable function dependence calculation for mark join, but 
need re-think this in future.
+            return;
+        }
+        if (joinType.isLeftSemiOrAntiJoin()) {
+            
fdBuilder.addUniqueSlot(left().getLogicalProperties().getFunctionalDependencies());
+        } else if (joinType.isRightSemiOrAntiJoin()) {
+            
fdBuilder.addUniqueSlot(right().getLogicalProperties().getFunctionalDependencies());
+        }
+        // if there is non-equal join conditions, don't propagate unique
+        if (hashJoinConjuncts.isEmpty()) {
+            return;
+        }
+        Pair<Set<Slot>, Set<Slot>> keys = extractNullRejectHashKeys();
+        if (keys == null) {
+            return;
+        }
+
+        // Note here we only check whether the left is unique.
+        // So the hash condition can't be null-safe
+        // TODO: consider Null-safe hash condition when left and rigth is not 
nullable
+        boolean isLeftUnique = left().getLogicalProperties()
+                .getFunctionalDependencies().isUnique(keys.first);
+        boolean isRightUnique = right().getLogicalProperties()
+                .getFunctionalDependencies().isUnique(keys.second);
+
+        // left/right outer join propagate left/right uniforms slots
+        // And if the right/left hash keys is unique,
+        // join can propagate left/right functional dependencies
+        if (joinType.isLeftOuterJoin() && isRightUnique) {
+            
fdBuilder.addUniqueSlot(left().getLogicalProperties().getFunctionalDependencies());
+        } else if (joinType.isRightOuterJoin() && isLeftUnique) {
+            
fdBuilder.addUniqueSlot(right().getLogicalProperties().getFunctionalDependencies());
+        } else if (joinType.isInnerJoin() && isLeftUnique && isRightUnique) {
+            // inner join propagate uniforms slots
+            // And if the hash keys is unique, inner join can propagate all 
functional dependencies
+            
fdBuilder.addFunctionalDependencies(left().getLogicalProperties().getFunctionalDependencies());
+            
fdBuilder.addFunctionalDependencies(right().getLogicalProperties().getFunctionalDependencies());
+        }
+    }
+
+    @Override
+    public void computeUniform(Builder fdBuilder) {
+        if (isMarkJoin()) {
+            // TODO disable function dependence calculation for mark join, but 
need re-think this in future.
+            return;
+        }
+        if (!joinType.isLeftSemiOrAntiJoin()) {
+            
fdBuilder.addUniformSlot(right().getLogicalProperties().getFunctionalDependencies());
+        }
+        if (!joinType.isRightSemiOrAntiJoin()) {
+            
fdBuilder.addUniformSlot(left().getLogicalProperties().getFunctionalDependencies());
+        }
+    }
 }
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 482c7e26058..02558fe2ed2 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
@@ -22,7 +22,6 @@ 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.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
@@ -41,7 +40,6 @@ import com.google.common.collect.ImmutableSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.function.Supplier;
 
 /**
  * Logical limit plan
@@ -150,25 +148,29 @@ public class LogicalLimit<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_TY
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies fd = 
child(0).getLogicalProperties().getFunctionalDependencies();
-        if (getLimit() == 1 && !phase.isLocal()) {
-            Builder builder = new Builder();
-            outputSupplier.get().forEach(builder::addUniformSlot);
-            outputSupplier.get().forEach(builder::addUniqueSlot);
-            ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-            builder.addFdItems(fdItems);
-            fd = builder.build();
+    public void computeUnique(FunctionalDependencies.Builder fdBuilder) {
+        if (getLimit() == 1) {
+            getOutput().forEach(fdBuilder::addUniqueSlot);
+        } else {
+            
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+        }
+    }
+
+    @Override
+    public void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        if (getLimit() == 1) {
+            getOutput().forEach(fdBuilder::addUniformSlot);
+        } else {
+            
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
         }
-        return fd;
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    public ImmutableSet<FdItem> computeFdItems() {
         ImmutableSet<FdItem> fdItems = 
child(0).getLogicalProperties().getFunctionalDependencies().getFdItems();
         if (getLimit() == 1 && !phase.isLocal()) {
             ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
-            List<Slot> output = outputSupplier.get();
+            List<Slot> output = getOutput();
             ImmutableSet<SlotReference> slotSet = output.stream()
                     .filter(SlotReference.class::isInstance)
                     .map(SlotReference.class::cast)
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 d338be5cc88..78a1e9a3a5e 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
@@ -41,7 +41,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 
 /**
  * A relation that contains only one row consist of some constant expressions.
@@ -136,20 +135,18 @@ public class LogicalOneRowRelation extends 
LogicalRelation implements OneRowRela
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies.Builder builder = new 
FunctionalDependencies.Builder();
-        outputSupplier.get().forEach(s -> {
-            builder.addUniformSlot(s);
-            builder.addUniqueSlot(s);
-        });
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        builder.addFdItems(fdItems);
-        return builder.build();
+    public void computeUnique(FunctionalDependencies.Builder fdBuilder) {
+        getOutput().forEach(fdBuilder::addUniqueSlot);
+    }
+
+    @Override
+    public void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        getOutput().forEach(fdBuilder::addUniformSlot);
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
-        Set<NamedExpression> output = 
ImmutableSet.copyOf(outputSupplier.get());
+    public ImmutableSet<FdItem> computeFdItems() {
+        Set<NamedExpression> output = ImmutableSet.copyOf(getOutput());
         ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
         ImmutableSet<SlotReference> slotSet = output.stream()
                 .filter(SlotReference.class::isInstance)
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 860bd263acb..c0fb6ae7365 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
@@ -19,13 +19,11 @@ package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.properties.FdItem;
 import org.apache.doris.nereids.properties.FunctionalDependencies;
-import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 
-import java.util.List;
 import java.util.Optional;
 import java.util.function.BiFunction;
 import java.util.function.Supplier;
@@ -61,7 +59,18 @@ public interface LogicalPlan extends Plan {
      *   - BlockFDPropagation: clean the fd
      *   - PropagateFD: propagate the fd
      */
-    FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier);
+    default FunctionalDependencies computeFuncDeps() {
+        FunctionalDependencies.Builder fdBuilder = new 
FunctionalDependencies.Builder();
+        computeUniform(fdBuilder);
+        computeUnique(fdBuilder);
+        ImmutableSet<FdItem> fdItems = computeFdItems();
+        fdBuilder.addFdItems(fdItems);
+        return fdBuilder.build();
+    }
+
+    ImmutableSet<FdItem> computeFdItems();
+
+    void computeUnique(FunctionalDependencies.Builder fdBuilder);
 
-    ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> outputSupplier);
+    void computeUniform(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 89dd7d49677..ebecee0d3ae 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,7 +23,6 @@ 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;
@@ -42,11 +41,9 @@ import com.google.common.collect.ImmutableList.Builder;
 import com.google.common.collect.ImmutableSet;
 import org.json.JSONObject;
 
-import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.function.Supplier;
 
 /**
  * Logical project plan.
@@ -239,36 +236,50 @@ public class LogicalProject<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies childFuncDeps = 
child().getLogicalProperties().getFunctionalDependencies();
-        FunctionalDependencies.Builder builder = new 
FunctionalDependencies.Builder(childFuncDeps);
-        builder.pruneSlots(new HashSet<>(outputSupplier.get()));
-        projects.stream().filter(Alias.class::isInstance).forEach(proj -> {
-            if (proj.child(0).isConstant()) {
-                builder.addUniformSlot(proj.toSlot());
-            } else if (proj.child(0) instanceof Uuid) {
-                builder.addUniqueSlot(proj.toSlot());
+    public ImmutableSet<FdItem> computeFdItems() {
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+
+        ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
+        builder.addAll(childItems);
+
+        return builder.build();
+    }
+
+    @Override
+    public void computeUnique(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+        for (NamedExpression proj : getProjects()) {
+            if (proj.children().isEmpty()) {
+                continue;
+            }
+            if (proj.child(0) instanceof Uuid) {
+                fdBuilder.addUniqueSlot(proj.toSlot());
             } else if (ExpressionUtils.isInjective(proj.child(0))) {
                 ImmutableSet<Slot> inputs = 
ImmutableSet.copyOf(proj.getInputSlots());
-                if (childFuncDeps.isUnique(inputs)) {
-                    builder.addUniqueSlot(proj.toSlot());
-                } else if (childFuncDeps.isUniform(inputs)) {
-                    builder.addUniformSlot(proj.toSlot());
+                if 
(child(0).getLogicalProperties().getFunctionalDependencies().isUnique(inputs)) {
+                    fdBuilder.addUniqueSlot(proj.toSlot());
                 }
             }
-        });
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        builder.addFdItems(fdItems);
-        return builder.build();
+        }
+        fdBuilder.pruneSlots(getOutputSet());
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
-        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
-
-        ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
-        builder.addAll(childItems);
-
-        return builder.build();
+    public void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+        for (NamedExpression proj : getProjects()) {
+            if (proj.children().isEmpty()) {
+                continue;
+            }
+            if (proj.child(0).isConstant()) {
+                fdBuilder.addUniformSlot(proj.toSlot());
+            } else if (ExpressionUtils.isInjective(proj.child(0))) {
+                ImmutableSet<Slot> inputs = 
ImmutableSet.copyOf(proj.getInputSlots());
+                if 
(child(0).getLogicalProperties().getFunctionalDependencies().isUniform(inputs)) 
{
+                    fdBuilder.addUniformSlot(proj.toSlot());
+                }
+            }
+        }
+        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 5e0311afe1b..95ae92b6866 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
@@ -39,7 +39,6 @@ import com.google.common.collect.ImmutableSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.function.Supplier;
 
 /**
  * LogicalRepeat.
@@ -187,19 +186,17 @@ public class LogicalRepeat<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies.Builder builder = new 
FunctionalDependencies.Builder();
-        // Note uniform does not reject nullable slots
-        outputSupplier.get().stream()
-                
.filter(child(0).getLogicalProperties().getFunctionalDependencies()::isUniform)
-                .forEach(builder::addUniformSlot);
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        builder.addFdItems(fdItems);
-        return builder.build();
+    public void computeUnique(FunctionalDependencies.Builder fdBuilder) {
+        // don't generate unique slot
+    }
+
+    @Override
+    public void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    public ImmutableSet<FdItem> computeFdItems() {
         ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
 
         ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
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 68a451f1df1..ea9fd143c94 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
@@ -41,7 +41,6 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 
 /**
  * The node of logical plan for sub query and alias
@@ -165,22 +164,29 @@ public class LogicalSubQueryAlias<CHILD_TYPE extends 
Plan> extends LogicalUnary<
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies.Builder builder = new FunctionalDependencies
-                
.Builder(child(0).getLogicalProperties().getFunctionalDependencies());
+    public void computeUnique(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
         Map<Slot, Slot> replaceMap = new HashMap<>();
-        List<Slot> outputs = outputSupplier.get();
+        List<Slot> outputs = getOutput();
         for (int i = 0; i < outputs.size(); i++) {
             replaceMap.put(child(0).getOutput().get(i), outputs.get(i));
         }
-        builder.replace(replaceMap);
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        builder.addFdItems(fdItems);
-        return builder.build();
+        fdBuilder.replace(replaceMap);
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    public void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        
fdBuilder.addUniformSlot(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);
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems() {
         // TODO: inherit from child with replaceMap
         return ImmutableSet.of();
     }
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 1fb5dbaab72..791ded58cce 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
@@ -22,7 +22,6 @@ 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.properties.OrderKey;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -35,6 +34,7 @@ import 
org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
 import com.google.common.base.Suppliers;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -42,7 +42,6 @@ import com.google.common.collect.ImmutableSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.function.Supplier;
 
 /**
  * Logical top-N plan.
@@ -171,26 +170,29 @@ public class LogicalTopN<CHILD_TYPE extends Plan> extends 
LogicalUnary<CHILD_TYP
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies fd = 
child(0).getLogicalProperties().getFunctionalDependencies();
+    public void computeUnique(FunctionalDependencies.Builder fdBuilder) {
         if (getLimit() == 1) {
-            Builder builder = new Builder();
-            List<Slot> output = outputSupplier.get();
-            output.forEach(builder::addUniformSlot);
-            output.forEach(builder::addUniqueSlot);
-            ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-            builder.addFdItems(fdItems);
-            fd = builder.build();
+            getOutput().forEach(fdBuilder::addUniqueSlot);
+        } else {
+            
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
         }
-        return fd;
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
+    public void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        if (getLimit() == 1) {
+            getOutput().forEach(fdBuilder::addUniformSlot);
+        } else {
+            
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+        }
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems() {
         ImmutableSet<FdItem> fdItems = 
child(0).getLogicalProperties().getFunctionalDependencies().getFdItems();
         if (getLimit() == 1) {
             ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
-            List<Slot> output = outputSupplier.get();
+            List<Slot> output = getOutput();
             ImmutableSet<SlotReference> slotSet = output.stream()
                     .filter(SlotReference.class::isInstance)
                     .map(SlotReference.class::cast)
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 dac6996c0ca..57d944bc2b9 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,7 +25,6 @@ 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;
@@ -41,7 +40,6 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.function.Supplier;
 
 /**
  * Logical Union.
@@ -187,20 +185,20 @@ public class LogicalUnion extends LogicalSetOperation 
implements Union, OutputPr
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        if (qualifier != Qualifier.DISTINCT) {
-            return FunctionalDependencies.EMPTY_FUNC_DEPS;
+    public void computeUnique(FunctionalDependencies.Builder fdBuilder) {
+        if (qualifier == Qualifier.DISTINCT) {
+            fdBuilder.addUniqueSlot(ImmutableSet.copyOf(getOutput()));
         }
-        FunctionalDependencies.Builder builder = new 
FunctionalDependencies.Builder();
-        builder.addUniqueSlot(ImmutableSet.copyOf(outputSupplier.get()));
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        builder.addFdItems(fdItems);
-        return builder.build();
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
-        Set<NamedExpression> output = 
ImmutableSet.copyOf(outputSupplier.get());
+    public void computeUniform(FunctionalDependencies.Builder fdBuilder) {
+        // don't propagate uniform slots
+    }
+
+    @Override
+    public ImmutableSet<FdItem> computeFdItems() {
+        Set<NamedExpression> output = ImmutableSet.copyOf(getOutput());
         ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
 
         ImmutableSet<SlotReference> exprs = output.stream()
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 e8621b1754c..200ccc2ffcb 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,7 +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;
 import org.apache.doris.nereids.trees.expressions.Slot;
@@ -36,7 +36,6 @@ import com.google.common.collect.ImmutableSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.function.Supplier;
 
 /** LogicalView */
 public class LogicalView<BODY extends Plan> extends LogicalUnary<BODY> {
@@ -129,17 +128,22 @@ public class LogicalView<BODY extends Plan> extends 
LogicalUnary<BODY> {
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        return ((LogicalPlan) child()).computeFuncDeps(outputSupplier);
+    public ImmutableSet<FdItem> computeFdItems() {
+        return ((LogicalPlan) child()).computeFdItems();
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
-        return ((LogicalPlan) child()).computeFdItems(outputSupplier);
+    public Plan withChildren(List<Plan> children) {
+        return new LogicalView<>(view, (LogicalPlan) children.get(0));
     }
 
     @Override
-    public Plan withChildren(List<Plan> children) {
-        return new LogicalView<>(view, (LogicalPlan) children.get(0));
+    public void computeUnique(Builder fdBuilder) {
+        
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+    }
+
+    @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/LogicalWindow.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalWindow.java
index 457678787bd..b03a3365402 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,7 +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;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
@@ -43,7 +43,6 @@ import com.google.common.collect.ImmutableSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.function.Supplier;
 
 /**
  * logical node to deal with window functions;
@@ -228,35 +227,51 @@ public class LogicalWindow<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
         return Optional.ofNullable(window);
     }
 
-    private void updateFuncDepsByWindowExpr(NamedExpression namedExpression, 
FunctionalDependencies.Builder builder) {
+    private boolean isUnique(NamedExpression namedExpression) {
         if (namedExpression.children().size() != 1 || 
!(namedExpression.child(0) instanceof WindowExpression)) {
-            return;
+            return false;
         }
         WindowExpression windowExpr = (WindowExpression) 
namedExpression.child(0);
         List<Expression> partitionKeys = windowExpr.getPartitionKeys();
         // Now we only support slot type keys
         if (!partitionKeys.stream().allMatch(Slot.class::isInstance)) {
-            return;
+            return false;
         }
         ImmutableSet<Slot> slotSet = partitionKeys.stream()
                 .map(s -> (Slot) s)
                 .collect(ImmutableSet.toImmutableSet());
+        // if partition by keys are uniform or empty, output is unique
+        if 
(child(0).getLogicalProperties().getFunctionalDependencies().isUniformAndNotNull(slotSet)
+                || slotSet.isEmpty()) {
+            if (windowExpr.getFunction() instanceof RowNumber) {
+                return true;
+            }
+        }
+        return false;
+    }
 
+    private boolean isUniform(NamedExpression namedExpression) {
+        if (namedExpression.children().size() != 1 || 
!(namedExpression.child(0) instanceof WindowExpression)) {
+            return false;
+        }
+        WindowExpression windowExpr = (WindowExpression) 
namedExpression.child(0);
+        List<Expression> partitionKeys = windowExpr.getPartitionKeys();
+        // Now we only support slot type keys
+        if (!partitionKeys.stream().allMatch(Slot.class::isInstance)) {
+            return false;
+        }
+        ImmutableSet<Slot> slotSet = partitionKeys.stream()
+                .map(s -> (Slot) s)
+                .collect(ImmutableSet.toImmutableSet());
         // if partition by keys are unique, output is uniform
         if 
(child(0).getLogicalProperties().getFunctionalDependencies().isUniqueAndNotNull(slotSet))
 {
             if (windowExpr.getFunction() instanceof RowNumber
                     || windowExpr.getFunction() instanceof Rank
                     || windowExpr.getFunction() instanceof DenseRank) {
-                builder.addUniformSlot(namedExpression.toSlot());
-            }
-        }
-
-        // if partition by keys are uniform, output is unique
-        if 
(child(0).getLogicalProperties().getFunctionalDependencies().isUniformAndNotNull(slotSet))
 {
-            if (windowExpr.getFunction() instanceof RowNumber) {
-                builder.addUniqueSlot(namedExpression.toSlot());
+                return true;
             }
         }
+        return false;
     }
 
     private void updateFuncDepsByWindowExpr(NamedExpression namedExpression, 
ImmutableSet.Builder<FdItem> builder) {
@@ -278,27 +293,35 @@ public class LogicalWindow<CHILD_TYPE extends Plan> 
extends LogicalUnary<CHILD_T
     }
 
     @Override
-    public FunctionalDependencies computeFuncDeps(Supplier<List<Slot>> 
outputSupplier) {
-        FunctionalDependencies.Builder builder = new 
FunctionalDependencies.Builder(
-                child(0).getLogicalProperties().getFunctionalDependencies());
+    public ImmutableSet<FdItem> computeFdItems() {
+        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
+        ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
+        builder.addAll(childItems);
+
         for (NamedExpression namedExpression : windowExpressions) {
             updateFuncDepsByWindowExpr(namedExpression, builder);
         }
-        ImmutableSet<FdItem> fdItems = computeFdItems(outputSupplier);
-        builder.addFdItems(fdItems);
+
         return builder.build();
     }
 
     @Override
-    public ImmutableSet<FdItem> computeFdItems(Supplier<List<Slot>> 
outputSupplier) {
-        ImmutableSet.Builder<FdItem> builder = ImmutableSet.builder();
-        ImmutableSet<FdItem> childItems = 
child().getLogicalProperties().getFunctionalDependencies().getFdItems();
-        builder.addAll(childItems);
-
+    public void computeUnique(Builder fdBuilder) {
+        
fdBuilder.addUniqueSlot(child(0).getLogicalProperties().getFunctionalDependencies());
         for (NamedExpression namedExpression : windowExpressions) {
-            updateFuncDepsByWindowExpr(namedExpression, builder);
+            if (isUnique(namedExpression)) {
+                fdBuilder.addUniqueSlot(namedExpression.toSlot());
+            }
         }
+    }
 
-        return builder.build();
+    @Override
+    public void computeUniform(Builder fdBuilder) {
+        
fdBuilder.addUniformSlot(child(0).getLogicalProperties().getFunctionalDependencies());
+        for (NamedExpression namedExpression : windowExpressions) {
+            if (isUniform(namedExpression)) {
+                fdBuilder.addUniformSlot(namedExpression.toSlot());
+            }
+        }
     }
 }
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 64685bc55e3..401b8e0736b 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
@@ -41,7 +41,6 @@ import org.apache.doris.nereids.trees.expressions.Or;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.expressions.WindowExpression;
-import 
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
 import org.apache.doris.nereids.trees.expressions.functions.agg.Avg;
 import org.apache.doris.nereids.trees.expressions.functions.agg.Max;
 import org.apache.doris.nereids.trees.expressions.functions.agg.Min;
@@ -738,7 +737,8 @@ public class ExpressionUtils {
         return expression instanceof Slot;
     }
 
-    public static boolean isInjectiveAgg(AggregateFunction agg) {
+    // if the input is unique,  the output of agg is unique, too
+    public static boolean isInjectiveAgg(Expression agg) {
         return agg instanceof Sum || agg instanceof Avg || agg instanceof Max 
|| agg instanceof Min;
     }
 
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/FunctionalDependenciesTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/FunctionalDependenciesTest.java
index 5c4193d83a3..dce123cbff0 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/FunctionalDependenciesTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/FunctionalDependenciesTest.java
@@ -258,12 +258,12 @@ class FunctionalDependenciesTest extends 
TestWithFeService {
     @Test
     void testWindow() {
         Plan plan = PlanChecker.from(connectContext)
-                .analyze("select row_number() over(partition by id) from agg")
+                .analyze("select id, row_number() over(partition by id) from 
agg")
                 .rewrite()
                 .getPlan();
         
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
         Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(0)));
+                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(1)));
 
         plan = PlanChecker.from(connectContext)
                 .analyze("select row_number() over(partition by name) from agg 
where name = '1'")
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/UniformTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/UniformTest.java
new file mode 100644
index 00000000000..edd5d1e4f0f
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/UniformTest.java
@@ -0,0 +1,207 @@
+// 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 UniformTest 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"
+                + "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"
+                + "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() {
+        // group by unique
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select count(id) from agg group by id")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniform(plan.getOutput().get(0)));
+
+        // propagate uniform
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id from agg where id = 1 group by id ")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniform(plan.getOutput().get(0)));
+
+        // group by all/uniform
+        plan = PlanChecker.from(connectContext)
+                .analyze("select sum(id) from agg ")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniform(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select sum(id) from agg where id = 1 group by id")
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniform(plan.getOutput().get(0)));
+
+    }
+
+    @Test
+    void testTopNLimit() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select name from agg limit 1")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniformAndNotNull(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select name from agg order by name limit 1 ")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniformAndNotNull(plan.getOutput().get(0)));
+    }
+
+    @Test
+    void testSetOp() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select name from agg limit 1 except select name from 
agg")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniform(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id from agg intersect select name from agg 
limit 1")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniform(plan.getOutput().get(0)));
+    }
+
+    @Test
+    void testFilterHaving() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id from agg where id = 1")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniformAndNotNull(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select name from uni group by name having name = 
\"\"")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniformAndNotNull(plan.getOutput().get(0)));
+    }
+
+    @Test
+    void testGenerate() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id from agg lateral view explode([1,2,3]) 
tmp1 as e1")
+                .rewrite()
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                .getFunctionalDependencies().isEmpty());
+    }
+
+    @Test
+    void testJoin() {
+        // foj doesn't propagate any
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select uni.id from agg join uni "
+                        + "on agg.id = uni.id where uni.id = 1")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies().isUniform(plan.getOutput().get(0)));
+    }
+
+    @Test
+    void testOneRowRelation() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select 1")
+                .rewrite()
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isUniform(plan.getOutput().get(0)));
+    }
+
+    @Test
+    void testProject() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id as id2, 1 as id3 from uni where id = 1")
+                .rewrite()
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(0)));
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(1)));
+    }
+
+    @Test
+    void testSubQuery() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id from (select id from agg where id = 1) t")
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(0)));
+    }
+
+    @Test
+    void testWindow() {
+        // partition by uniform
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select row_number() over(partition by id) from agg")
+                .rewrite()
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isUniform(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select rank() over(partition by id) from agg")
+                .rewrite()
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isUniform(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select dense_rank() over(partition by id) from agg")
+                .rewrite()
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isUniform(plan.getOutput().get(0)));
+    }
+
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/FunctionalDependenciesTest.java
 b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/UniqueTest.java
similarity index 50%
copy from 
fe/fe-core/src/test/java/org/apache/doris/nereids/properties/FunctionalDependenciesTest.java
copy to 
fe/fe-core/src/test/java/org/apache/doris/nereids/properties/UniqueTest.java
index 5c4193d83a3..27d64ad186c 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/FunctionalDependenciesTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/properties/UniqueTest.java
@@ -17,21 +17,17 @@
 
 package org.apache.doris.nereids.properties;
 
-import org.apache.doris.nereids.properties.FunctionalDependencies.Builder;
 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.types.IntegerType;
 import org.apache.doris.nereids.util.PlanChecker;
 import org.apache.doris.utframe.TestWithFeService;
 
-import com.google.common.collect.ImmutableSet;
 import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
-class FunctionalDependenciesTest extends TestWithFeService {
+class UniqueTest 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);
@@ -56,83 +52,146 @@ class FunctionalDependenciesTest extends TestWithFeService 
{
     }
 
     @Test
-    void testUniform() {
-        Builder fdBuilder = new Builder();
-        fdBuilder.addUniformSlot(slot1);
-        FunctionalDependencies fd = fdBuilder.build();
-        Assertions.assertTrue(fd.isUniformAndNotNull(slot1));
-        Assertions.assertFalse(fd.isUniformAndNotNull(slot2));
-        fdBuilder.addUniformSlot(ImmutableSet.of(slot2));
-        fd = fdBuilder.build();
-        Assertions.assertTrue(fd.isUniformAndNotNull(slot2));
-        ImmutableSet<Slot> slotSet = ImmutableSet.of(slot1, slot2, slot3);
-        fdBuilder.addUniformSlot(slotSet);
-        fd = fdBuilder.build();
-        Assertions.assertTrue(fd.isUniformAndNotNull(slotSet));
-        Assertions.assertFalse(fd.isUniformAndNotNull(ImmutableSet.of(slot1, 
slot2, slot3, slot4)));
-        Assertions.assertTrue(fd.isUniformAndNotNull(ImmutableSet.of(slot1, 
slot2)));
-        Assertions.assertFalse(fd.isUniformAndNotNull(ImmutableSet.of(slot3, 
slot2)));
+    void testAgg() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select name from agg group by name")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniqueAndNotNull(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select sum(id) from agg")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUnique(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id, sum(id), avg(id), max(id), min(id) from 
agg group by id")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUnique(plan.getOutput().get(0)));
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUnique(plan.getOutput().get(1)));
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUnique(plan.getOutput().get(2)));
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUnique(plan.getOutput().get(3)));
+
     }
 
     @Test
-    void testUnique() {
-        Builder fdBuilder = new Builder();
-        fdBuilder.addUniqueSlot(slot1);
-        FunctionalDependencies fd = fdBuilder.build();
-        Assertions.assertTrue(fd.isUniqueAndNotNull(slot1));
-        Assertions.assertFalse(fd.isUniqueAndNotNull(slot2));
-        fdBuilder.addUniqueSlot(slot2);
-        fd = fdBuilder.build();
-        Assertions.assertTrue(fd.isUniqueAndNotNull(slot2));
-        ImmutableSet<Slot> slotSet = ImmutableSet.of(slot1, slot2, slot3);
-        fdBuilder.addUniqueSlot(slotSet);
-        fd = fdBuilder.build();
-        Assertions.assertTrue(fd.isUniqueAndNotNull(slotSet));
-        Assertions.assertTrue(fd.isUniqueAndNotNull(ImmutableSet.of(slot1, 
slot2, slot3, slot4)));
-        Assertions.assertFalse(fd.isUniqueAndNotNull(ImmutableSet.of(slot3, 
slot4)));
+    void testScan() throws Exception {
+        // test agg key
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id from agg")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniqueAndNotNull(plan.getOutput().get(0)));
+
+        // test unique key
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id from uni")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniqueAndNotNull(plan.getOutput().get(0)));
+
+        // test unique constraint
+        addConstraint("alter table agg add constraint uq unique(name)");
+        plan = PlanChecker.from(connectContext)
+                .analyze("select name from agg")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniqueAndNotNull(plan.getOutput().get(0)));
+        dropConstraint("alter table agg drop constraint uq");
+
+        // test primary constraint
+        addConstraint("alter table agg add constraint pk primary key(name)");
+        plan = PlanChecker.from(connectContext)
+                .analyze("select name from agg")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniqueAndNotNull(plan.getOutput().get(0)));
+        dropConstraint("alter table agg drop constraint pk");
     }
 
     @Test
-    void testMergeFD() {
-        Builder fdBuilder1 = new Builder();
-        fdBuilder1.addUniformSlot(slot1);
-        Builder fdBuilder2 = new Builder();
-        fdBuilder2.addUniformSlot(slot2);
-
-        fdBuilder1.addFunctionalDependencies(fdBuilder2.build());
-        FunctionalDependencies fd = fdBuilder1.build();
-        Assertions.assertTrue(fd.isUniformAndNotNull(slot1));
-        Assertions.assertTrue(fd.isUniformAndNotNull(slot2));
+    void testTopNLimit() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select name from agg limit 1")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniqueAndNotNull(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select name from agg order by name limit 1 ")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniqueAndNotNull(plan.getOutput().get(0)));
     }
 
     @Test
-    void testScan() {
+    void testSetOp() {
         Plan plan = PlanChecker.from(connectContext)
-                .analyze("select id from agg")
+                .analyze("select name from agg limit 1 except select name from 
agg")
                 .rewrite()
                 .getPlan();
         
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
                 .isUniqueAndNotNull(plan.getOutput().get(0)));
         plan = PlanChecker.from(connectContext)
-                .analyze("select id from uni")
+                .analyze("select id from agg intersect select name from agg")
                 .rewrite()
                 .getPlan();
         
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
                 .isUniqueAndNotNull(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select id from agg union all select name from agg")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isEmpty());
+        plan = PlanChecker.from(connectContext)
+                .analyze("select name, id from agg union select name, id from 
agg")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUnique(plan.getOutputSet()));
     }
 
     @Test
-    void testFilter() {
+    void testFilterHaving() {
         Plan plan = PlanChecker.from(connectContext)
-                .analyze("select name from agg where name = \"1\"")
+                .analyze("select id from agg where id = 1")
                 .rewrite()
                 .getPlan();
         
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
-                .isUniformAndNotNull(plan.getOutput().get(0)));
+                .isUniqueAndNotNull(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select name from uni group by name having name = 
\"\"")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isUniqueAndNotNull(plan.getOutput().get(0)));
+    }
+
+    @Test
+    void testGenerate() {
+        Plan plan = PlanChecker.from(connectContext)
+                .analyze("select id from agg lateral view explode([1,2,3]) 
tmp1 as e1")
+                .rewrite()
+                .getPlan();
+        Assertions.assertTrue(plan.getLogicalProperties()
+                .getFunctionalDependencies().isEmpty());
     }
 
     @Test
     void testJoin() {
+        // foj doesn't propagate any
         Plan plan = PlanChecker.from(connectContext)
                 .analyze("select * from agg full outer join uni "
                         + "on agg.id = uni.id")
@@ -140,248 +199,187 @@ class FunctionalDependenciesTest extends 
TestWithFeService {
                 .getPlan();
         
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies().isEmpty());
 
+        // loj propagate unique when right is unique
         plan = PlanChecker.from(connectContext)
-                .analyze("select agg.id, uni.id from agg full outer join uni "
-                        + "on agg.id = uni.id")
+                .analyze("select agg.id, uni.id from agg left outer join uni "
+                        + "on agg.id = uni.name")
                 .rewrite()
                 .getPlan();
         
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies().isEmpty());
-
         plan = PlanChecker.from(connectContext)
-                .analyze("select agg.id from agg left outer join uni "
+                .analyze("select agg.id, uni.id from agg left outer join uni "
                         + "on agg.id = uni.id")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies().isUnique(plan.getOutput().get(0)));
+        
Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies().isUnique(plan.getOutput().get(1)));
 
+        // roj propagate unique when left is unique
         plan = PlanChecker.from(connectContext)
-                .analyze("select agg.id from agg left semi join uni "
+                .analyze("select agg.id, uni.id from agg right outer join uni "
+                        + "on agg.id = uni.name")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies().isEmpty());
+        plan = PlanChecker.from(connectContext)
+                .analyze("select agg.id, uni.id from agg right outer join uni "
                         + "on agg.id = uni.id")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies().isUnique(plan.getOutput().get(1)));
+        
Assertions.assertFalse(plan.getLogicalProperties().getFunctionalDependencies().isUnique(plan.getOutput().get(0)));
 
+        // semi/anti join propagate all
+        plan = PlanChecker.from(connectContext)
+                .analyze("select agg.id from agg left semi join uni "
+                        + "on agg.id = uni.name")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies().isUnique(plan.getOutput().get(0)));
         plan = PlanChecker.from(connectContext)
                 .analyze("select agg.id from agg left anti join uni "
-                        + "on agg.id = uni.id")
+                        + "on agg.id = uni.name")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
-
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies().isUnique(plan.getOutput().get(0)));
         plan = PlanChecker.from(connectContext)
-                .analyze("select uni.id from agg right outer join uni "
-                        + "on agg.id = uni.id")
+                .analyze("select uni.id from agg right semi join uni "
+                        + "on agg.id = uni.name")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies().isUnique(plan.getOutput().get(0)));
+        plan = PlanChecker.from(connectContext)
+                .analyze("select uni.id from agg right anti join uni "
+                        + "on agg.id = uni.name")
+                .rewrite()
+                .getPlan();
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies().isUnique(plan.getOutput().get(0)));
 
+        // inner join propagate unique only when join key is unique
         plan = PlanChecker.from(connectContext)
                 .analyze("select uni.id, agg.id from agg inner join uni "
                         + "on agg.id = uni.id")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
         Assertions.assertTrue(plan.getLogicalProperties()
                 
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
         Assertions.assertTrue(plan.getLogicalProperties()
                 
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(1)));
-    }
-
-    @Test
-    void testAgg() {
-        Plan plan = PlanChecker.from(connectContext)
-                .analyze("select count(1), name from agg group by name")
-                .rewrite()
-                .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(1)));
 
         plan = PlanChecker.from(connectContext)
-                .analyze("select count(1), max(id), id from agg group by id")
+                .analyze("select uni.id, agg.id from agg inner join uni "
+                        + "on agg.id = uni.name")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(1)));
         Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(2)));
+                .getFunctionalDependencies().isEmpty());
         Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(0)));
-
+                .getFunctionalDependencies().isEmpty());
         plan = PlanChecker.from(connectContext)
-                .analyze("select count(1), id from agg where id = 1 group by 
id")
+                .analyze("select uni.id, agg.id from agg inner join uni "
+                        + "on agg.id < uni.id")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
         Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(1)));
+                .getFunctionalDependencies().isEmpty());
         Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(0)));
-
+                .getFunctionalDependencies().isEmpty());
         plan = PlanChecker.from(connectContext)
-                .analyze("select count(name) from agg")
+                .analyze("select uni.id, agg.id from agg inner join uni "
+                        + "on agg.id = uni.id or agg.name > uni.name")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
         Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(0)));
-
+                .getFunctionalDependencies().isEmpty());
+        Assertions.assertTrue(plan.getLogicalProperties()
+                .getFunctionalDependencies().isEmpty());
         plan = PlanChecker.from(connectContext)
-                .analyze("select id from agg where id = 1 group by cube(id, 
name)")
+                .analyze("select uni.id, agg.id from agg inner join uni "
+                        + "on agg.id = uni.id and agg.name > uni.name")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
         Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniform(plan.getOutput().get(0)));
-    }
-
-    @Test
-    void testGenerate() {
-        Plan plan = PlanChecker.from(connectContext)
-                .analyze("select k1 from (select 1 k1) as t lateral view 
explode([1,2,3]) tmp1 as e1")
-                .rewrite()
-                .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
+                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
         Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniform(plan.getOutput().get(0)));
+                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(1)));
     }
 
     @Test
-    void testWindow() {
+    void testOneRowRelation() {
         Plan plan = PlanChecker.from(connectContext)
-                .analyze("select row_number() over(partition by id) from agg")
-                .rewrite()
-                .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(0)));
-
-        plan = PlanChecker.from(connectContext)
-                .analyze("select row_number() over(partition by name) from agg 
where name = '1'")
+                .analyze("select 1")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
         Assertions.assertTrue(plan.getLogicalProperties()
                 
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
-
-        plan = PlanChecker.from(connectContext)
-                .analyze("select row_number() over(partition by name) from agg 
where name = '1' limit 1")
-                .rewrite()
-                .getPlan();
-        LogicalPartitionTopN<?> ptopn = (LogicalPartitionTopN<?>) 
plan.child(0).child(0).child(0).child(0).child(0);
-        
System.out.println(ptopn.getLogicalProperties().getFunctionalDependencies());
-        System.out.println(ptopn.getOutput());
-        Assertions.assertTrue(ptopn.getLogicalProperties()
-                
.getFunctionalDependencies().isUniformAndNotNull(ImmutableSet.copyOf(ptopn.getOutputSet())));
-
-        plan = PlanChecker.from(connectContext)
-                .analyze("select row_number() over(partition by name) from agg 
limit 1")
-                .rewrite()
-                .getPlan();
-        ptopn = (LogicalPartitionTopN<?>) 
plan.child(0).child(0).child(0).child(0).child(0);
-        Assertions.assertTrue(ptopn.getLogicalProperties()
-                .getFunctionalDependencies().isEmpty());
     }
 
     @Test
     void testProject() {
         Plan plan = PlanChecker.from(connectContext)
-                .analyze("select name from agg where id = 1")
+                .analyze("select id as id2 from uni")
                 .rewrite()
                 .getPlan();
         Assertions.assertTrue(plan.getLogicalProperties()
-                .getFunctionalDependencies().isEmpty());
+                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
     }
 
     @Test
-    void testLimitAndTopN() {
+    void testRepeat() {
         Plan plan = PlanChecker.from(connectContext)
-                .analyze("select name from agg limit 1")
+                .analyze("select id from agg  group by GROUPING SETS ((id, 
name), (id))")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(0)));
-
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isEmpty());
         plan = PlanChecker.from(connectContext)
-                .analyze("select name from agg order by id limit 1")
+                .analyze("select id from agg group by rollup (id, name)")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(0)));
+        
Assertions.assertTrue(plan.getLogicalProperties().getFunctionalDependencies()
+                .isEmpty());
     }
 
     @Test
     void testSubQuery() {
         Plan plan = PlanChecker.from(connectContext)
-                .analyze("select id from (select * from agg) t")
-                .rewrite()
+                .analyze("select id from (select id from agg) t")
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
         Assertions.assertTrue(plan.getLogicalProperties()
                 
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
     }
 
-    @Disabled
     @Test
-    void testCTE() {
+    void testAssertRows() {
         Plan plan = PlanChecker.from(connectContext)
-                .analyze("with t as (select * from agg) select id from t where 
id = 1")
+                .analyze("select id from agg where id = (select id from uni)")
+                .rewrite()
                 .getPlan();
-        System.out.println(plan.treeString());
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
         Assertions.assertTrue(plan.getLogicalProperties()
                 
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
     }
 
     @Test
-    void testSetOperation() {
+    void testWindow() {
+        // partition by uniform
         Plan plan = PlanChecker.from(connectContext)
-                .analyze("select * from agg union select * from uni")
-                .rewrite()
-                .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(ImmutableSet.copyOf(plan.getOutput())));
-
-        plan = PlanChecker.from(connectContext)
-                .analyze("select * from agg union all select * from uni")
-                .rewrite()
-                .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertFalse(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(ImmutableSet.copyOf(plan.getOutput())));
-
-        plan = PlanChecker.from(connectContext)
-                .analyze("select * from agg intersect select * from uni where 
name = \"1\"")
+                .analyze("select id, row_number() over(partition by id) from 
agg where id =1")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(ImmutableSet.copyOf(plan.getOutput())));
         Assertions.assertTrue(plan.getLogicalProperties()
                 
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
         Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniformAndNotNull(plan.getOutput().get(1)));
+                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(1)));
 
+        // partition by None
         plan = PlanChecker.from(connectContext)
-                .analyze("select * from agg except select * from uni")
+                .analyze("select id, row_number() over() from agg where id =1")
                 .rewrite()
                 .getPlan();
-        
System.out.println(plan.getLogicalProperties().getFunctionalDependencies());
-        Assertions.assertTrue(plan.getLogicalProperties()
-                
.getFunctionalDependencies().isUniqueAndNotNull(ImmutableSet.copyOf(plan.getOutput())));
         Assertions.assertTrue(plan.getLogicalProperties()
                 
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(0)));
+        Assertions.assertTrue(plan.getLogicalProperties()
+                
.getFunctionalDependencies().isUniqueAndNotNull(plan.getOutput().get(1)));
     }
+
 }


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

Reply via email to