You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by li...@apache.org on 2022/06/17 03:31:10 UTC

[incubator-doris] branch master updated: [Enhancement](Nereids) Automatic compute logical properties (#10176)

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

lingmiao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git


The following commit(s) were added to refs/heads/master by this push:
     new d51166dd2a [Enhancement](Nereids) Automatic compute logical properties (#10176)
d51166dd2a is described below

commit d51166dd2aa3ed75429b379a1ea36bc657c50b32
Author: 924060929 <92...@qq.com>
AuthorDate: Fri Jun 17 11:31:05 2022 +0800

    [Enhancement](Nereids) Automatic compute logical properties (#10176)
    
    Automatic compute logical properties
---
 .../org/apache/doris/nereids/OptimizerContext.java |   9 +-
 .../java/org/apache/doris/nereids/Planner.java     |   5 +-
 .../LogicalOperator.java => analyzer/Unbound.java} |  15 +--
 .../doris/nereids/analyzer/UnboundAlias.java       |   2 +-
 .../doris/nereids/analyzer/UnboundRelation.java    |  15 ++-
 .../apache/doris/nereids/analyzer/UnboundSlot.java |   2 +-
 .../apache/doris/nereids/analyzer/UnboundStar.java |   2 +-
 .../doris/nereids/jobs/cascades/ApplyRuleJob.java  |   2 +-
 .../nereids/jobs/rewrite/RewriteBottomUpJob.java   |  30 +++---
 .../nereids/jobs/rewrite/RewriteTopDownJob.java    |  26 +++---
 .../java/org/apache/doris/nereids/memo/Group.java  |  15 ++-
 .../java/org/apache/doris/nereids/memo/Memo.java   |  22 ++---
 .../doris/nereids/operators/OperatorType.java      |   1 +
 .../plans/logical/LogicalBinaryOperator.java       |  17 +++-
 .../operators/plans/logical/LogicalFilter.java     |   2 +-
 .../operators/plans/logical/LogicalJoin.java       |   2 +-
 .../plans/logical/LogicalLeafOperator.java         |  14 ++-
 .../operators/plans/logical/LogicalOperator.java   |   6 +-
 .../operators/plans/logical/LogicalProject.java    |   2 +-
 .../operators/plans/logical/LogicalRelation.java   |   2 +-
 .../plans/logical/LogicalUnaryOperator.java        |  15 ++-
 .../plans/physical/PhysicalBinaryOperator.java     |  20 ++--
 .../plans/physical/PhysicalLeafOperator.java       |  16 +---
 .../operators/plans/physical/PhysicalOperator.java |   8 --
 .../plans/physical/PhysicalUnaryOperator.java      |  18 +---
 .../doris/nereids/parser/LogicalPlanBuilder.java   |   3 +-
 .../nereids/pattern/GroupExpressionMatching.java   |  11 ++-
 .../doris/nereids/pattern/GroupMatching.java       |   7 +-
 .../org/apache/doris/nereids/pattern/Pattern.java  |   3 +-
 .../nereids/pattern/generator/JavaAstBuilder.java  |   4 +-
 .../nereids/properties/LogicalProperties.java      |   4 +
 ...operties.java => UnboundLogicalProperties.java} |  15 +--
 .../doris/nereids/trees/AbstractTreeNode.java      |   7 +-
 .../org/apache/doris/nereids/trees/TreeNode.java   |   2 +-
 .../doris/nereids/trees/expressions/EqualTo.java   |   2 +-
 .../nereids/trees/expressions/Expression.java      |   2 +-
 .../nereids/trees/expressions/GreaterThan.java     |   2 +-
 .../trees/expressions/GreaterThanEqual.java        |   2 +-
 .../doris/nereids/trees/expressions/LessThan.java  |   2 +-
 .../nereids/trees/expressions/LessThanEqual.java   |   2 +-
 .../doris/nereids/trees/expressions/Not.java       |   2 +-
 .../doris/nereids/trees/plans/AbstractPlan.java    |  14 ++-
 .../doris/nereids/trees/plans/PlaceHolderPlan.java |  60 ++++++------
 .../org/apache/doris/nereids/trees/plans/Plan.java |   1 +
 .../trees/plans/logical/AbstractLogicalPlan.java   |  18 ++--
 .../trees/plans/logical/LogicalBinaryPlan.java     |  25 +++--
 .../trees/plans/logical/LogicalLeafPlan.java       |  19 +++-
 .../nereids/trees/plans/logical/LogicalPlan.java   |   2 +-
 .../trees/plans/logical/LogicalUnaryPlan.java      |  16 +++-
 .../trees/plans/physical/AbstractPhysicalPlan.java |  11 +--
 .../trees/plans/physical/PhysicalBinaryPlan.java   |  18 +++-
 .../trees/plans/physical/PhysicalLeafPlan.java     |  15 ++-
 .../trees/plans/physical/PhysicalUnaryPlan.java    |  17 +++-
 .../doris/nereids/jobs/RewriteTopDownJobTest.java  |  35 +++++--
 .../pattern/GroupExpressionMatchingTest.java       |  16 ++--
 .../apache/doris/nereids/plan/TestPlanOutput.java  | 102 +++++++++++++++++++++
 56 files changed, 447 insertions(+), 260 deletions(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/OptimizerContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/OptimizerContext.java
index d5ac07f27f..0b1605f230 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/OptimizerContext.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/OptimizerContext.java
@@ -24,13 +24,12 @@ import org.apache.doris.nereids.jobs.scheduler.JobStack;
 import org.apache.doris.nereids.jobs.scheduler.SimpleJobScheduler;
 import org.apache.doris.nereids.memo.Memo;
 import org.apache.doris.nereids.rules.RuleSet;
-import org.apache.doris.nereids.trees.TreeNode;
 
 /**
  * Context used in memo.
  */
-public class OptimizerContext<NODE_TYPE extends TreeNode<NODE_TYPE>> {
-    private final Memo<NODE_TYPE> memo;
+public class OptimizerContext {
+    private final Memo memo;
     private RuleSet ruleSet;
     private JobPool jobPool;
     private final JobScheduler jobScheduler;
@@ -40,7 +39,7 @@ public class OptimizerContext<NODE_TYPE extends TreeNode<NODE_TYPE>> {
      *
      * @param memo {@link Memo} reference
      */
-    public OptimizerContext(Memo<NODE_TYPE> memo) {
+    public OptimizerContext(Memo memo) {
         this.memo = memo;
         this.ruleSet = new RuleSet();
         this.jobPool = new JobStack();
@@ -63,7 +62,7 @@ public class OptimizerContext<NODE_TYPE extends TreeNode<NODE_TYPE>> {
         this.ruleSet = ruleSet;
     }
 
-    public Memo<NODE_TYPE> getMemo() {
+    public Memo getMemo() {
         return memo;
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/Planner.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/Planner.java
index 31a87c9562..48ae896a3d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/Planner.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/Planner.java
@@ -23,7 +23,6 @@ import org.apache.doris.nereids.jobs.rewrite.RewriteBottomUpJob;
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.Memo;
 import org.apache.doris.nereids.properties.PhysicalProperties;
-import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
 import org.apache.doris.qe.ConnectContext;
@@ -48,10 +47,10 @@ public class Planner {
             LogicalPlan plan,
             PhysicalProperties outputProperties,
             ConnectContext connectContext) throws AnalysisException {
-        Memo<Plan> memo = new Memo<>();
+        Memo memo = new Memo();
         memo.initialize(plan);
 
-        OptimizerContext<Plan> optimizerContext = new OptimizerContext<>(memo);
+        OptimizerContext optimizerContext = new OptimizerContext(memo);
         plannerContext = new PlannerContext(optimizerContext, connectContext, outputProperties);
 
         plannerContext.getOptimizerContext().pushJob(
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/Unbound.java
similarity index 66%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOperator.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/Unbound.java
index 0421f8f159..a171d7ced5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/Unbound.java
@@ -15,17 +15,8 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.analyzer;
 
-import org.apache.doris.nereids.operators.plans.PlanOperator;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.Plan;
-
-import java.util.List;
-
-/**
- * interface for all concrete logical plan operator.
- */
-public interface LogicalOperator extends PlanOperator {
-    List<Slot> computeOutput(Plan... inputs);
+/** Use to marking unbound plan and unbound expression. */
+public interface Unbound {
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java
index d7140292f4..15b4e61f94 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java
@@ -31,7 +31,7 @@ import java.util.List;
  */
 public class UnboundAlias<CHILD_TYPE extends Expression>
         extends NamedExpression
-        implements UnaryExpression<CHILD_TYPE> {
+        implements UnaryExpression<CHILD_TYPE>, Unbound {
 
     public UnboundAlias(CHILD_TYPE child) {
         super(NodeType.UNBOUND_ALIAS, child);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
index 550894126d..a188ab01f5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
@@ -21,7 +21,10 @@ import org.apache.doris.nereids.analyzer.identifier.TableIdentifier;
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.operators.OperatorType;
 import org.apache.doris.nereids.operators.plans.logical.LogicalLeafOperator;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.properties.UnboundLogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.util.Utils;
 
 import com.clearspring.analytics.util.Lists;
@@ -32,7 +35,7 @@ import java.util.List;
 /**
  * Represent a relation plan node that has not been bound.
  */
-public class UnboundRelation extends LogicalLeafOperator {
+public class UnboundRelation extends LogicalLeafOperator implements Unbound {
     private final List<String> nameParts;
 
     public UnboundRelation(List<String> nameParts) {
@@ -64,9 +67,13 @@ public class UnboundRelation extends LogicalLeafOperator {
     }
 
     @Override
-    public List<Slot> doComputeOutput() {
-        // fixme: throw unchecked exception
-        throw new IllegalStateException(new UnboundException("output"));
+    public LogicalProperties computeLogicalProperties(Plan... inputs) {
+        return new UnboundLogicalProperties();
+    }
+
+    @Override
+    public List<Slot> computeOutput() {
+        throw new UnboundException("output");
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
index 5d1d4d7cbb..08ffcec866 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
@@ -28,7 +28,7 @@ import java.util.List;
 /**
  * Slot has not been bound.
  */
-public class UnboundSlot extends Slot {
+public class UnboundSlot extends Slot implements Unbound {
     private final List<String> nameParts;
 
     public UnboundSlot(List<String> nameParts) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java
index 11b8a54d3d..65b6cdbed1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java
@@ -29,7 +29,7 @@ import java.util.List;
 /**
  * Star expression.
  */
-public class UnboundStar extends NamedExpression implements LeafExpression {
+public class UnboundStar extends NamedExpression implements LeafExpression, Unbound {
     private final List<String> target;
 
     public UnboundStar(List<String> target) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/cascades/ApplyRuleJob.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/cascades/ApplyRuleJob.java
index 5cf36a91ef..a48a17d590 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/cascades/ApplyRuleJob.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/cascades/ApplyRuleJob.java
@@ -57,7 +57,7 @@ public class ApplyRuleJob extends Job<Plan> {
             return;
         }
 
-        GroupExpressionMatching<Plan> groupExpressionMatching
+        GroupExpressionMatching groupExpressionMatching
                 = new GroupExpressionMatching(rule.getPattern(), groupExpression);
         for (Plan plan : groupExpressionMatching) {
             List<Plan> newPlans = rule.transform(plan, context);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteBottomUpJob.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteBottomUpJob.java
index b82ca9f280..a6048dfa0c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteBottomUpJob.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteBottomUpJob.java
@@ -25,7 +25,7 @@ import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.pattern.GroupExpressionMatching;
 import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.trees.TreeNode;
+import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.base.Preconditions;
 
@@ -35,16 +35,16 @@ import java.util.Objects;
 /**
  * Bottom up job for rewrite, use pattern match.
  */
-public class RewriteBottomUpJob<NODE_TYPE extends TreeNode<NODE_TYPE>> extends Job<NODE_TYPE> {
+public class RewriteBottomUpJob extends Job<Plan> {
     private final Group group;
-    private final List<Rule<NODE_TYPE>> rules;
+    private final List<Rule<Plan>> rules;
     private final boolean childrenOptimized;
 
-    public RewriteBottomUpJob(Group group, List<Rule<NODE_TYPE>> rules, PlannerContext context) {
+    public RewriteBottomUpJob(Group group, List<Rule<Plan>> rules, PlannerContext context) {
         this(group, rules, context, false);
     }
 
-    private RewriteBottomUpJob(Group group, List<Rule<NODE_TYPE>> rules,
+    private RewriteBottomUpJob(Group group, List<Rule<Plan>> rules,
             PlannerContext context, boolean childrenOptimized) {
         super(JobType.BOTTOM_UP_REWRITE, context);
         this.group = Objects.requireNonNull(group, "group cannot be null");
@@ -57,23 +57,23 @@ public class RewriteBottomUpJob<NODE_TYPE extends TreeNode<NODE_TYPE>> extends J
         GroupExpression logicalExpression = group.getLogicalExpression();
         if (!childrenOptimized) {
             for (Group childGroup : logicalExpression.children()) {
-                pushTask(new RewriteBottomUpJob<>(childGroup, rules, context, false));
+                pushTask(new RewriteBottomUpJob(childGroup, rules, context, false));
             }
-            pushTask(new RewriteBottomUpJob<>(group, rules, context, true));
+            pushTask(new RewriteBottomUpJob(group, rules, context, true));
             return;
         }
 
-        List<Rule<NODE_TYPE>> validRules = getValidRules(logicalExpression, rules);
-        for (Rule<NODE_TYPE> rule : validRules) {
-            GroupExpressionMatching<NODE_TYPE> groupExpressionMatching
-                    = new GroupExpressionMatching<>(rule.getPattern(), logicalExpression);
-            for (NODE_TYPE before : groupExpressionMatching) {
-                List<NODE_TYPE> afters = rule.transform(before, context);
+        List<Rule<Plan>> validRules = getValidRules(logicalExpression, rules);
+        for (Rule<Plan> rule : validRules) {
+            GroupExpressionMatching groupExpressionMatching
+                    = new GroupExpressionMatching(rule.getPattern(), logicalExpression);
+            for (Plan before : groupExpressionMatching) {
+                List<Plan> afters = rule.transform(before, context);
                 Preconditions.checkArgument(afters.size() == 1);
-                NODE_TYPE after = afters.get(0);
+                Plan after = afters.get(0);
                 if (after != before) {
                     context.getOptimizerContext().getMemo().copyIn(after, group, rule.isRewrite());
-                    pushTask(new RewriteBottomUpJob<>(group, rules, context, false));
+                    pushTask(new RewriteBottomUpJob(group, rules, context, false));
                     return;
                 }
             }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteTopDownJob.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteTopDownJob.java
index 64b348510d..4aebb3e182 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteTopDownJob.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/rewrite/RewriteTopDownJob.java
@@ -24,7 +24,7 @@ import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.pattern.GroupExpressionMatching;
 import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.trees.TreeNode;
+import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.base.Preconditions;
 
@@ -34,9 +34,9 @@ import java.util.Objects;
 /**
  * Top down job for rewrite, use pattern match.
  */
-public class RewriteTopDownJob<NODE_TYPE extends TreeNode<NODE_TYPE>> extends Job<NODE_TYPE> {
+public class RewriteTopDownJob extends Job<Plan> {
     private final Group group;
-    private final List<Rule<NODE_TYPE>> rules;
+    private final List<Rule<Plan>> rules;
 
     /**
      * Constructor.
@@ -45,7 +45,7 @@ public class RewriteTopDownJob<NODE_TYPE extends TreeNode<NODE_TYPE>> extends Jo
      * @param rules rewrite rules
      * @param context planner context
      */
-    public RewriteTopDownJob(Group group, List<Rule<NODE_TYPE>> rules, PlannerContext context) {
+    public RewriteTopDownJob(Group group, List<Rule<Plan>> rules, PlannerContext context) {
         super(JobType.TOP_DOWN_REWRITE, context);
         this.group = Objects.requireNonNull(group, "group cannot be null");
         this.rules = Objects.requireNonNull(rules, "rules cannot be null");
@@ -55,17 +55,17 @@ public class RewriteTopDownJob<NODE_TYPE extends TreeNode<NODE_TYPE>> extends Jo
     public void execute() {
         GroupExpression logicalExpression = group.getLogicalExpression();
 
-        List<Rule<NODE_TYPE>> validRules = getValidRules(logicalExpression, rules);
-        for (Rule<NODE_TYPE> rule : validRules) {
-            GroupExpressionMatching<NODE_TYPE> groupExpressionMatching
-                    = new GroupExpressionMatching<>(rule.getPattern(), logicalExpression);
-            for (NODE_TYPE before : groupExpressionMatching) {
-                List<NODE_TYPE> afters = rule.transform(before, context);
+        List<Rule<Plan>> validRules = getValidRules(logicalExpression, rules);
+        for (Rule<Plan> rule : validRules) {
+            GroupExpressionMatching groupExpressionMatching
+                    = new GroupExpressionMatching(rule.getPattern(), logicalExpression);
+            for (Plan before : groupExpressionMatching) {
+                List<Plan> afters = rule.transform(before, context);
                 Preconditions.checkArgument(afters.size() == 1);
-                NODE_TYPE after = afters.get(0);
+                Plan after = afters.get(0);
                 if (after != before) {
                     context.getOptimizerContext().getMemo().copyIn(after, group, rule.isRewrite());
-                    pushTask(new RewriteTopDownJob<>(group, rules, context));
+                    pushTask(new RewriteTopDownJob(group, rules, context));
                     return;
                 }
             }
@@ -73,7 +73,7 @@ public class RewriteTopDownJob<NODE_TYPE extends TreeNode<NODE_TYPE>> extends Jo
         }
 
         for (Group childGroup : logicalExpression.children()) {
-            pushTask(new RewriteTopDownJob<>(childGroup, rules, context));
+            pushTask(new RewriteTopDownJob(childGroup, rules, context));
         }
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java
index ccbb6a072c..39fb09ae82 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java
@@ -50,12 +50,13 @@ public class Group {
      *
      * @param groupExpression first {@link GroupExpression} in this Group
      */
-    public Group(GroupExpression groupExpression) {
+    public Group(GroupExpression groupExpression, LogicalProperties logicalProperties) {
         if (groupExpression.getOperator() instanceof LogicalOperator) {
             this.logicalExpressions.add(groupExpression);
         } else {
             this.physicalExpressions.add(groupExpression);
         }
+        this.logicalProperties = logicalProperties;
         groupExpression.setParent(this);
     }
 
@@ -101,12 +102,12 @@ public class Group {
      * @param newExpression new logical group expression
      * @return old logical group expression
      */
-    public GroupExpression rewriteLogicalExpression(
-            GroupExpression newExpression) {
-        GroupExpression oldExpression = getLogicalExpression();
+    public GroupExpression rewriteLogicalExpression(GroupExpression newExpression,
+                                                    LogicalProperties logicalProperties) {
         logicalExpressions.clear();
         logicalExpressions.add(newExpression);
-        return oldExpression;
+        this.logicalProperties = logicalProperties;
+        return getLogicalExpression();
     }
 
     public double getCostLowerBound() {
@@ -143,6 +144,10 @@ public class Group {
         return logicalProperties;
     }
 
+    public void setLogicalProperties(LogicalProperties logicalProperties) {
+        this.logicalProperties = logicalProperties;
+    }
+
     public boolean isExplored() {
         return isExplored;
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
index 1158e6550e..b73951b21f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
@@ -17,7 +17,7 @@
 
 package org.apache.doris.nereids.memo;
 
-import org.apache.doris.nereids.trees.TreeNode;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.plans.Plan;
 
@@ -30,16 +30,14 @@ import java.util.Map;
 
 /**
  * Representation for memo in cascades optimizer.
- *
- * @param <NODE_TYPE> should be {@link Plan} or {@link Expression}
  */
-public class Memo<NODE_TYPE extends TreeNode<NODE_TYPE>> {
+public class Memo {
     private final List<Group> groups = Lists.newArrayList();
     // we could not use Set, because Set has no get method.
     private final Map<GroupExpression, GroupExpression> groupExpressions = Maps.newHashMap();
     private Group root;
 
-    public void initialize(NODE_TYPE node) {
+    public void initialize(Plan node) {
         root = copyIn(node, null, false).getParent();
     }
 
@@ -56,10 +54,10 @@ public class Memo<NODE_TYPE extends TreeNode<NODE_TYPE>> {
      * @param rewrite whether to rewrite the node to the target group
      * @return Reference of node in Memo
      */
-    public GroupExpression copyIn(NODE_TYPE node, Group target, boolean rewrite) {
+    public GroupExpression copyIn(Plan node, Group target, boolean rewrite) {
         Preconditions.checkArgument(!rewrite || target != null);
         List<Group> childrenGroups = Lists.newArrayList();
-        for (NODE_TYPE child : node.children()) {
+        for (Plan child : node.children()) {
             childrenGroups.add(copyIn(child, null, rewrite).getParent());
         }
         if (node.getGroupExpression().isPresent() && groupExpressions.containsKey(node.getGroupExpression().get())) {
@@ -67,7 +65,7 @@ public class Memo<NODE_TYPE extends TreeNode<NODE_TYPE>> {
         }
         GroupExpression newGroupExpression = new GroupExpression(node.getOperator());
         newGroupExpression.setChildren(childrenGroups);
-        return insertOrRewriteGroupExpression(newGroupExpression, target, rewrite);
+        return insertOrRewriteGroupExpression(newGroupExpression, target, rewrite, node.getLogicalProperties());
         // TODO: need to derive logical property if generate new group. currently we not copy logical plan into
     }
 
@@ -82,8 +80,8 @@ public class Memo<NODE_TYPE extends TreeNode<NODE_TYPE>> {
      * @param rewrite whether to rewrite the groupExpression to target group
      * @return existing groupExpression in memo or newly generated groupExpression
      */
-    private GroupExpression insertOrRewriteGroupExpression(
-            GroupExpression groupExpression, Group target, boolean rewrite) {
+    private GroupExpression insertOrRewriteGroupExpression(GroupExpression groupExpression, Group target,
+                                                           boolean rewrite, LogicalProperties logicalProperties) {
         GroupExpression existedGroupExpression = groupExpressions.get(groupExpression);
         if (existedGroupExpression != null) {
             if (target != null && !target.getGroupId().equals(existedGroupExpression.getParent().getGroupId())) {
@@ -93,13 +91,13 @@ public class Memo<NODE_TYPE extends TreeNode<NODE_TYPE>> {
         }
         if (target != null) {
             if (rewrite) {
-                GroupExpression oldExpression = target.rewriteLogicalExpression(groupExpression);
+                GroupExpression oldExpression = target.rewriteLogicalExpression(groupExpression, logicalProperties);
                 groupExpressions.remove(oldExpression);
             } else {
                 target.addGroupExpression(groupExpression);
             }
         } else {
-            Group group = new Group(groupExpression);
+            Group group = new Group(groupExpression, logicalProperties);
             Preconditions.checkArgument(!groups.contains(group), "new group with already exist output");
             groups.add(group);
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/OperatorType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/OperatorType.java
index f2ddc06716..10bb5c9347 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/OperatorType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/OperatorType.java
@@ -34,6 +34,7 @@ public enum OperatorType {
     LOGICAL_PROJECT,
     LOGICAL_FILTER,
     LOGICAL_JOIN,
+    PLACE_HOLDER,
 
     // physical plan
     PHYSICAL_OLAP_SCAN,
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalBinaryOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalBinaryOperator.java
index 228ee08358..fc7d3a9a6f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalBinaryOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalBinaryOperator.java
@@ -27,7 +27,10 @@ import org.apache.doris.nereids.trees.plans.PlaceHolderPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
 
+import com.google.common.base.Preconditions;
+
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all logical binary operator that have two inputs.
@@ -40,16 +43,20 @@ public abstract class LogicalBinaryOperator extends AbstractOperator
     }
 
     @Override
-    public final List<Slot> computeOutput(Plan... inputs) {
-        return doComputeOutput(inputs[0], inputs[1]);
+    public final LogicalProperties computeLogicalProperties(Plan... inputs) {
+        Preconditions.checkArgument(inputs.length == 2);
+        return new LogicalProperties(computeOutput(inputs[0], inputs[1]));
     }
 
-    public abstract List<Slot> doComputeOutput(Plan left, Plan right);
+    public abstract List<Slot> computeOutput(Plan left, Plan right);
 
     @Override
     public LogicalBinaryPlan toTreeNode(GroupExpression groupExpression) {
         LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
-        return new LogicalBinaryPlan(this, groupExpression, logicalProperties,
-                new PlaceHolderPlan(), new PlaceHolderPlan());
+        LogicalProperties leftChildProperties = groupExpression.child(0).getLogicalProperties();
+        LogicalProperties rightChildProperties = groupExpression.child(1).getLogicalProperties();
+        return new LogicalBinaryPlan(this, Optional.of(groupExpression), Optional.of(logicalProperties),
+                new PlaceHolderPlan(leftChildProperties), new PlaceHolderPlan(rightChildProperties)
+        );
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalFilter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalFilter.java
index c7d579898c..6b61dd36ea 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalFilter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalFilter.java
@@ -43,7 +43,7 @@ public class LogicalFilter extends LogicalUnaryOperator {
 
 
     @Override
-    public List<Slot> doComputeOutput(Plan input) {
+    public List<Slot> computeOutput(Plan input) {
         return input.getOutput();
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalJoin.java
index b1672627bd..8ed6c6e1b7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalJoin.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalJoin.java
@@ -71,7 +71,7 @@ public class LogicalJoin extends LogicalBinaryOperator {
     }
 
     @Override
-    public List<Slot> doComputeOutput(Plan leftInput, Plan rightInput) {
+    public List<Slot> computeOutput(Plan leftInput, Plan rightInput) {
         switch (joinType) {
             case LEFT_SEMI_JOIN:
                 return ImmutableList.copyOf(leftInput.getOutput());
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalLeafOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalLeafOperator.java
index 44c72f06e6..0e872c9f49 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalLeafOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalLeafOperator.java
@@ -21,11 +21,15 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.operators.AbstractOperator;
 import org.apache.doris.nereids.operators.OperatorType;
 import org.apache.doris.nereids.operators.plans.LeafPlanOperator;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
 
+import com.google.common.base.Preconditions;
+
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all logical operator that have no input.
@@ -38,14 +42,16 @@ public abstract class LogicalLeafOperator extends AbstractOperator
     }
 
     @Override
-    public final List<Slot> computeOutput(Plan... inputs) {
-        return doComputeOutput();
+    public LogicalProperties computeLogicalProperties(Plan... inputs) {
+        Preconditions.checkArgument(inputs.length == 0);
+        return new LogicalProperties(computeOutput());
     }
 
-    public abstract List<Slot> doComputeOutput();
+    public abstract List<Slot> computeOutput();
 
     @Override
     public LogicalLeafPlan toTreeNode(GroupExpression groupExpression) {
-        return new LogicalLeafPlan(this, groupExpression, groupExpression.getParent().getLogicalProperties());
+        LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
+        return new LogicalLeafPlan(this, Optional.of(groupExpression), Optional.of(logicalProperties));
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOperator.java
index 0421f8f159..0e38c86d0c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOperator.java
@@ -18,14 +18,12 @@
 package org.apache.doris.nereids.operators.plans.logical;
 
 import org.apache.doris.nereids.operators.plans.PlanOperator;
-import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.plans.Plan;
 
-import java.util.List;
-
 /**
  * interface for all concrete logical plan operator.
  */
 public interface LogicalOperator extends PlanOperator {
-    List<Slot> computeOutput(Plan... inputs);
+    LogicalProperties computeLogicalProperties(Plan... inputs);
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalProject.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalProject.java
index 5554542443..a9ba14d74a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalProject.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalProject.java
@@ -56,7 +56,7 @@ public class LogicalProject extends LogicalUnaryOperator {
     }
 
     @Override
-    public List<Slot> doComputeOutput(Plan input) {
+    public List<Slot> computeOutput(Plan input) {
         // fixme: not throw a checked exception
         return projects.stream()
                 .map(namedExpr -> {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalRelation.java
index 6f01817b82..baa1835f91 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalRelation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalRelation.java
@@ -62,7 +62,7 @@ public class LogicalRelation extends LogicalLeafOperator {
     }
 
     @Override
-    public List<Slot> doComputeOutput() {
+    public List<Slot> computeOutput() {
         return table.getBaseSchema()
                 .stream()
                 .map(col -> SlotReference.fromColumn(col, qualifier))
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalUnaryOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalUnaryOperator.java
index b04f2ae8f3..cb3ee3cf68 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalUnaryOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalUnaryOperator.java
@@ -27,7 +27,10 @@ import org.apache.doris.nereids.trees.plans.PlaceHolderPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
 
+import com.google.common.base.Preconditions;
+
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all logical operator that have one input.
@@ -40,15 +43,19 @@ public abstract class LogicalUnaryOperator extends AbstractOperator
     }
 
     @Override
-    public final List<Slot> computeOutput(Plan... inputs) {
-        return doComputeOutput(inputs[0]);
+    public LogicalProperties computeLogicalProperties(Plan... inputs) {
+        Preconditions.checkArgument(inputs.length == 1);
+        return new LogicalProperties(computeOutput(inputs[0]));
     }
 
-    public abstract List<Slot> doComputeOutput(Plan input);
+    public abstract List<Slot> computeOutput(Plan input);
 
     @Override
     public LogicalUnaryPlan toTreeNode(GroupExpression groupExpression) {
         LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
-        return new LogicalUnaryPlan(this, groupExpression, logicalProperties, new PlaceHolderPlan());
+        LogicalProperties childProperties = groupExpression.child(0).getLogicalProperties();
+        return new LogicalUnaryPlan(this, Optional.of(groupExpression),
+            Optional.of(logicalProperties), new PlaceHolderPlan(childProperties)
+        );
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalBinaryOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalBinaryOperator.java
index 813cb3d445..c34c6d25ec 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalBinaryOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalBinaryOperator.java
@@ -22,12 +22,10 @@ import org.apache.doris.nereids.operators.AbstractOperator;
 import org.apache.doris.nereids.operators.OperatorType;
 import org.apache.doris.nereids.operators.plans.BinaryPlanOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.PlaceHolderPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalBinaryPlan;
 
-import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all physical operator that have two inputs.
@@ -39,19 +37,13 @@ public abstract class PhysicalBinaryOperator extends AbstractOperator
         super(type);
     }
 
-    @Override
-    public final List<Slot> computeOutputs(LogicalProperties logicalProperties, Plan... inputs) {
-        return doComputeOutput(logicalProperties, inputs[0], inputs[1]);
-    }
-
-    public List<Slot> doComputeOutput(LogicalProperties logicalProperties, Plan left, Plan right) {
-        return logicalProperties.getOutput();
-    }
-
     @Override
     public PhysicalBinaryPlan toTreeNode(GroupExpression groupExpression) {
         LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
-        return new PhysicalBinaryPlan(this, groupExpression, logicalProperties,
-                new PlaceHolderPlan(), new PlaceHolderPlan());
+        LogicalProperties leftChildProperties = groupExpression.child(0).getLogicalProperties();
+        LogicalProperties rightChildProperties = groupExpression.child(1).getLogicalProperties();
+        return new PhysicalBinaryPlan(this, Optional.of(groupExpression), logicalProperties,
+                new PlaceHolderPlan(leftChildProperties), new PlaceHolderPlan(rightChildProperties)
+        );
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalLeafOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalLeafOperator.java
index 6cf64ede7c..fda9e7e600 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalLeafOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalLeafOperator.java
@@ -22,11 +22,9 @@ import org.apache.doris.nereids.operators.AbstractOperator;
 import org.apache.doris.nereids.operators.OperatorType;
 import org.apache.doris.nereids.operators.plans.LeafPlanOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalLeafPlan;
 
-import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all physical operator that have no input.
@@ -38,17 +36,9 @@ public abstract class PhysicalLeafOperator extends AbstractOperator
         super(type);
     }
 
-    @Override
-    public final List<Slot> computeOutputs(LogicalProperties logicalProperties, Plan... inputs) {
-        return doComputeOutput(logicalProperties);
-    }
-
-    public List<Slot> doComputeOutput(LogicalProperties logicalProperties) {
-        return logicalProperties.getOutput();
-    }
-
     @Override
     public PhysicalLeafPlan toTreeNode(GroupExpression groupExpression) {
-        return new PhysicalLeafPlan(this, groupExpression, groupExpression.getParent().getLogicalProperties());
+        LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
+        return new PhysicalLeafPlan(this, Optional.of(groupExpression), logicalProperties);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalOperator.java
index 8414f6356d..3f0777e6db 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalOperator.java
@@ -18,17 +18,9 @@
 package org.apache.doris.nereids.operators.plans.physical;
 
 import org.apache.doris.nereids.operators.plans.PlanOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.Plan;
-
-import java.util.List;
 
 /**
  * interface for all concrete physical operator.
  */
 public interface PhysicalOperator extends PlanOperator {
-
-    List<Slot> computeOutputs(LogicalProperties logicalProperties, Plan... inputs);
-
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalUnaryOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalUnaryOperator.java
index daecac8191..7c479bc8ed 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalUnaryOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalUnaryOperator.java
@@ -22,12 +22,10 @@ import org.apache.doris.nereids.operators.AbstractOperator;
 import org.apache.doris.nereids.operators.OperatorType;
 import org.apache.doris.nereids.operators.plans.UnaryPlanOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.PlaceHolderPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan;
 
-import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all physical operator that have one input.
@@ -39,18 +37,12 @@ public abstract class PhysicalUnaryOperator extends AbstractOperator
         super(type);
     }
 
-    @Override
-    public final List<Slot> computeOutputs(LogicalProperties logicalProperties, Plan... inputs) {
-        return doComputeOutput(logicalProperties, inputs[0]);
-    }
-
-    public List<Slot> doComputeOutput(LogicalProperties logicalProperties, Plan input) {
-        return logicalProperties.getOutput();
-    }
-
     @Override
     public PhysicalUnaryPlan toTreeNode(GroupExpression groupExpression) {
         LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
-        return new PhysicalUnaryPlan(this, groupExpression, logicalProperties, new PlaceHolderPlan());
+        LogicalProperties childProperties = groupExpression.child(0).getLogicalProperties();
+        return new PhysicalUnaryPlan(this, Optional.of(groupExpression),
+            logicalProperties, new PlaceHolderPlan(childProperties)
+        );
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index c70b8a4dc4..c90524f3ce 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -229,7 +229,8 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
             if (left == null) {
                 left = right;
             } else {
-                left = new LogicalBinaryPlan(new LogicalJoin(JoinType.INNER_JOIN, null), left, right);
+                left = new LogicalBinaryPlan(
+                        new LogicalJoin(JoinType.INNER_JOIN, Optional.empty()), left, right);
             }
             left = withJoinRelations(left, relation);
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupExpressionMatching.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupExpressionMatching.java
index 955665a18b..8da0ba8cde 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupExpressionMatching.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupExpressionMatching.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.pattern;
 
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.trees.TreeNode;
+import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.collect.Lists;
 
@@ -32,17 +33,17 @@ import java.util.Objects;
  * TODO: adapt ANY and MULTI
  * TODO: add ut
  */
-public class GroupExpressionMatching<NODE_TYPE extends TreeNode<NODE_TYPE>> implements Iterable<NODE_TYPE> {
-    private final Pattern<? extends NODE_TYPE, NODE_TYPE> pattern;
+public class GroupExpressionMatching implements Iterable<Plan> {
+    private final Pattern<? extends Plan, Plan> pattern;
     private final GroupExpression groupExpression;
 
-    public GroupExpressionMatching(Pattern<? extends NODE_TYPE, NODE_TYPE> pattern, GroupExpression groupExpression) {
+    public GroupExpressionMatching(Pattern<? extends Plan, Plan> pattern, GroupExpression groupExpression) {
         this.pattern = Objects.requireNonNull(pattern);
         this.groupExpression = Objects.requireNonNull(groupExpression);
     }
 
     @Override
-    public GroupExpressionIterator<NODE_TYPE> iterator() {
+    public GroupExpressionIterator<Plan> iterator() {
         return new GroupExpressionIterator<>(pattern, groupExpression);
     }
 
@@ -97,7 +98,7 @@ public class GroupExpressionMatching<NODE_TYPE extends TreeNode<NODE_TYPE>> impl
                     for (int i = 0; i < childrenResults.size(); i++) {
                         children.add(childrenResults.get(i).get(childrenResultsIndex[i]));
                     }
-                    NODE_TYPE result = root.newChildren(children);
+                    NODE_TYPE result = root.withChildren(children);
                     results.add(result);
                     offset = 0;
                     while (true) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupMatching.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupMatching.java
index 4771300ad9..ef22b4837d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupMatching.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupMatching.java
@@ -21,6 +21,7 @@ import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.operators.OperatorType;
 import org.apache.doris.nereids.trees.TreeNode;
+import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.collect.Lists;
 
@@ -59,18 +60,18 @@ public class GroupMatching<NODE_TYPE extends TreeNode> implements Iterable<NODE_
          * @param pattern pattern to match
          * @param group group to be matched
          */
-        public GroupIterator(Pattern<? extends NODE_TYPE, NODE_TYPE> pattern, Group group) {
+        public GroupIterator(Pattern<? extends Plan, Plan> pattern, Group group) {
             this.pattern = pattern;
             this.iterator = Lists.newArrayList();
             for (GroupExpression groupExpression : group.getLogicalExpressions()) {
-                GroupExpressionMatching.GroupExpressionIterator<NODE_TYPE> groupExpressionIterator =
+                GroupExpressionMatching.GroupExpressionIterator groupExpressionIterator =
                         new GroupExpressionMatching(pattern, groupExpression).iterator();
                 if (groupExpressionIterator.hasNext()) {
                     this.iterator.add(groupExpressionIterator);
                 }
             }
             for (GroupExpression groupExpression : group.getPhysicalExpressions()) {
-                GroupExpressionMatching.GroupExpressionIterator<NODE_TYPE> groupExpressionIterator =
+                GroupExpressionMatching.GroupExpressionIterator groupExpressionIterator =
                         new GroupExpressionMatching(pattern, groupExpression).iterator();
                 if (groupExpressionIterator.hasNext()) {
                     this.iterator.add(groupExpressionIterator);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Pattern.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Pattern.java
index 781e10aa97..009a92c934 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Pattern.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Pattern.java
@@ -161,7 +161,8 @@ public class Pattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE_TYP
     }
 
     @Override
-    public Pattern<? extends NODE_TYPE, NODE_TYPE> newChildren(List<Pattern<? extends NODE_TYPE, NODE_TYPE>> children) {
+    public Pattern<? extends NODE_TYPE, NODE_TYPE> withChildren(
+            List<Pattern<? extends NODE_TYPE, NODE_TYPE>> children) {
         throw new RuntimeException();
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/JavaAstBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/JavaAstBuilder.java
index 069b2dd4cb..6ccbdf5f6a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/JavaAstBuilder.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/JavaAstBuilder.java
@@ -206,9 +206,9 @@ public class JavaAstBuilder extends JavaParserBaseVisitor<JavaAstNode> {
                 } else if (memberCtx.methodDeclaration() != null) {
                     methodDeclarations.add(visitMethodDeclaration(memberCtx.methodDeclaration()));
                 }
+                // find inner class
+                memberCtx.accept(this);
             }
-            // find inner class
-            memberCtx.accept(this);
         }
 
         String className = getText(ctx.identifier());
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
index 80af9bcd59..078ece11d7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
@@ -36,4 +36,8 @@ public class LogicalProperties {
     public List<Slot> getOutput() {
         return output;
     }
+
+    public LogicalProperties withOutput(List<Slot> output) {
+        return new LogicalProperties(output);
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/UnboundLogicalProperties.java
similarity index 69%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/properties/UnboundLogicalProperties.java
index 80af9bcd59..3ea3234f1a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/UnboundLogicalProperties.java
@@ -17,6 +17,7 @@
 
 package org.apache.doris.nereids.properties;
 
+import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.expressions.Slot;
 
 import com.google.common.collect.ImmutableList;
@@ -24,16 +25,16 @@ import com.google.common.collect.ImmutableList;
 import java.util.List;
 
 /**
- * Logical properties used for analysis and optimize in Nereids.
+ * LogicalPlanOperator must compute and return non-null LogicalProperties without exception,
+ * so UnboundRelation.computeLogicalProperties() return a UnboundLogicalProperties temporary.
  */
-public class LogicalProperties {
-    protected List<Slot> output;
-
-    public LogicalProperties(List<Slot> output) {
-        this.output = ImmutableList.copyOf(output);
+public class UnboundLogicalProperties extends LogicalProperties {
+    public UnboundLogicalProperties() {
+        super(ImmutableList.of());
     }
 
+    @Override
     public List<Slot> getOutput() {
-        return output;
+        throw new UnboundException("output");
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/AbstractTreeNode.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/AbstractTreeNode.java
index 16cbaf9f44..d4f00a3406 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/AbstractTreeNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/AbstractTreeNode.java
@@ -24,6 +24,7 @@ import org.apache.doris.nereids.operators.Operator;
 import com.google.common.collect.ImmutableList;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 
 /**
@@ -43,7 +44,7 @@ public abstract class AbstractTreeNode<NODE_TYPE extends TreeNode<NODE_TYPE>>
 
 
     public AbstractTreeNode(NodeType type, NODE_TYPE... children) {
-        this(type, null, children);
+        this(type, Optional.empty(), children);
     }
 
     /**
@@ -53,10 +54,10 @@ public abstract class AbstractTreeNode<NODE_TYPE extends TreeNode<NODE_TYPE>>
      * @param groupExpression group expression related to the operator of this node
      * @param children children of this node
      */
-    public AbstractTreeNode(NodeType type, GroupExpression groupExpression, NODE_TYPE... children) {
+    public AbstractTreeNode(NodeType type, Optional<GroupExpression> groupExpression, NODE_TYPE... children) {
         this.type = type;
         this.children = ImmutableList.copyOf(children);
-        this.groupExpression = Optional.ofNullable(groupExpression);
+        this.groupExpression = Objects.requireNonNull(groupExpression, "groupExpression can not be null");
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java
index 12a8597782..6844607d6c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java
@@ -44,5 +44,5 @@ public interface TreeNode<NODE_TYPE extends TreeNode<NODE_TYPE>> {
 
     int arity();
 
-    NODE_TYPE newChildren(List<NODE_TYPE> children);
+    NODE_TYPE withChildren(List<NODE_TYPE> children);
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
index 4f561e525f..c60219e9ad 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
@@ -45,7 +45,7 @@ public class EqualTo<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE extend
     }
 
     @Override
-    public EqualTo<Expression, Expression> newChildren(List<Expression> children) {
+    public EqualTo<Expression, Expression> withChildren(List<Expression> children) {
         Preconditions.checkArgument(children.size() == 2);
         return new EqualTo<>(children.get(0), children.get(1));
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
index 7a275e4b2b..5b0d71e522 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
@@ -61,7 +61,7 @@ public abstract class Expression extends AbstractTreeNode<Expression> {
     }
 
     @Override
-    public Expression newChildren(List<Expression> children) {
+    public Expression withChildren(List<Expression> children) {
         throw new RuntimeException();
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java
index 7e0e291366..2b1ea60efd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java
@@ -50,7 +50,7 @@ public class GreaterThan<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE ex
     }
 
     @Override
-    public GreaterThan<Expression, Expression> newChildren(List<Expression> children) {
+    public GreaterThan<Expression, Expression> withChildren(List<Expression> children) {
         Preconditions.checkArgument(children.size() == 2);
         return new GreaterThan<>(children.get(0), children.get(1));
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java
index 2ca42bbb26..75e50feed0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java
@@ -50,7 +50,7 @@ public class GreaterThanEqual<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TY
     }
 
     @Override
-    public GreaterThanEqual<Expression, Expression> newChildren(List<Expression> children) {
+    public GreaterThanEqual<Expression, Expression> withChildren(List<Expression> children) {
         Preconditions.checkArgument(children.size() == 2);
         return new GreaterThanEqual<>(children.get(0), children.get(1));
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java
index 3cdaa4abaf..447fc9f499 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java
@@ -50,7 +50,7 @@ public class LessThan<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE exten
     }
 
     @Override
-    public LessThan<Expression, Expression> newChildren(List<Expression> children) {
+    public LessThan<Expression, Expression> withChildren(List<Expression> children) {
         Preconditions.checkArgument(children.size() == 2);
         return new LessThan<>(children.get(0), children.get(1));
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java
index f38dc8a8c1..a0fe0be7a5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java
@@ -50,7 +50,7 @@ public class LessThanEqual<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE
     }
 
     @Override
-    public LessThanEqual<Expression, Expression> newChildren(List<Expression> children) {
+    public LessThanEqual<Expression, Expression> withChildren(List<Expression> children) {
         Preconditions.checkArgument(children.size() == 2);
         return new LessThanEqual<>(children.get(0), children.get(1));
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
index a7a38e56ea..06ea928895 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
@@ -47,7 +47,7 @@ public class Not<CHILD_TYPE extends Expression> extends Expression
     }
 
     @Override
-    public Not<Expression> newChildren(List<Expression> children) {
+    public Not<Expression> withChildren(List<Expression> children) {
         Preconditions.checkArgument(children.size() == 1);
         return new Not<>(children.get(0));
     }
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 ff6cd109cc..b1f13694b9 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
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.trees.plans;
 
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.operators.plans.PlanOperator;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.AbstractTreeNode;
 import org.apache.doris.nereids.trees.NodeType;
 
@@ -27,6 +28,7 @@ import org.apache.commons.lang3.StringUtils;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
  * Abstract class for all concrete plan node.
@@ -36,14 +38,18 @@ public abstract class AbstractPlan<OP_TYPE extends PlanOperator>
 
     public final OP_TYPE operator;
 
-    public AbstractPlan(NodeType type, OP_TYPE operator, Plan... children) {
-        super(type, children);
-        this.operator = Objects.requireNonNull(operator, "operator can not be null");
+    protected final LogicalProperties logicalProperties;
+
+    public AbstractPlan(NodeType type, OP_TYPE operator, LogicalProperties logicalProperties, Plan... children) {
+        this(type, operator, Optional.empty(), logicalProperties, children);
     }
 
-    public AbstractPlan(NodeType type, OP_TYPE operator, GroupExpression groupExpression, Plan... children) {
+    /** all parameter constructor. */
+    public AbstractPlan(NodeType type, OP_TYPE operator, Optional<GroupExpression> groupExpression,
+                        LogicalProperties logicalProperties, Plan... children) {
         super(type, groupExpression, children);
         this.operator = Objects.requireNonNull(operator, "operator can not be null");
+        this.logicalProperties = Objects.requireNonNull(logicalProperties, "logicalProperties can not be null");
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlaceHolderPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlaceHolderPlan.java
index d1a325072f..24047896ed 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlaceHolderPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlaceHolderPlan.java
@@ -18,12 +18,17 @@
 package org.apache.doris.nereids.trees.plans;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.LeafPlanOperator;
+import org.apache.doris.nereids.operators.OperatorType;
+import org.apache.doris.nereids.operators.plans.logical.LogicalLeafOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
+
+import com.google.common.base.Preconditions;
 
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 
 /**
@@ -31,50 +36,49 @@ import java.util.Optional;
  * Used in {@link org.apache.doris.nereids.pattern.GroupExpressionMatching.GroupExpressionIterator},
  * as a place-holder when do match root.
  */
-public class PlaceHolderPlan implements LeafPlan {
-
-    @Override
-    public Optional<GroupExpression> getGroupExpression() {
-        return Optional.empty();
-    }
+public class PlaceHolderPlan extends LogicalLeafPlan<PlaceHolderPlan.PlaceHolderOperator> {
+    /** PlaceHolderOperator. */
+    public static class PlaceHolderOperator extends LogicalLeafOperator {
+        private final LogicalProperties logicalProperties;
 
-    @Override
-    public NodeType getType() {
-        return NodeType.FIXED;
-    }
+        public PlaceHolderOperator(LogicalProperties logicalProperties) {
+            super(OperatorType.PLACE_HOLDER);
+            this.logicalProperties = Objects.requireNonNull(logicalProperties, "logicalProperties can not be null");
+        }
 
-    @Override
-    public Plan newChildren(List children) {
-        throw new RuntimeException();
-    }
+        @Override
+        public List<Slot> computeOutput() {
+            throw new IllegalStateException("PlaceholderOperator can not compute output");
+        }
 
-    @Override
-    public LeafPlanOperator getOperator() {
-        throw new RuntimeException();
+        @Override
+        public LogicalProperties computeLogicalProperties(Plan... inputs) {
+            throw new IllegalStateException("PlaceholderOperator can not compute logical properties");
+        }
     }
 
-    @Override
-    public LogicalProperties getLogicalProperties() {
-        throw new RuntimeException();
+    public PlaceHolderPlan(LogicalProperties logicalProperties) {
+        super(new PlaceHolderOperator(logicalProperties), Optional.empty(), Optional.of(logicalProperties));
     }
 
     @Override
-    public List<Slot> getOutput() {
-        throw new RuntimeException();
+    public Optional<GroupExpression> getGroupExpression() {
+        return Optional.empty();
     }
 
     @Override
-    public String treeString() {
-        throw new RuntimeException();
+    public NodeType getType() {
+        return NodeType.FIXED;
     }
 
     @Override
-    public List<Plan> children() {
+    public PlaceHolderPlan withOutput(List<Slot> output) {
         throw new RuntimeException();
     }
 
     @Override
-    public Plan child(int index) {
-        throw new RuntimeException();
+    public PlaceHolderPlan withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 0);
+        return new PlaceHolderPlan(logicalProperties);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java
index ff7b26d617..cb899d7257 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java
@@ -37,4 +37,5 @@ public interface Plan extends TreeNode<Plan> {
 
     String treeString();
 
+    Plan withOutput(List<Slot> output);
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/AbstractLogicalPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/AbstractLogicalPlan.java
index 780dcbb59c..f790829bba 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/AbstractLogicalPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/AbstractLogicalPlan.java
@@ -25,8 +25,8 @@ import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.AbstractPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 
-import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all concrete logical plan.
@@ -34,17 +34,19 @@ import java.util.List;
 public abstract class AbstractLogicalPlan<OP_TYPE extends LogicalOperator>
         extends AbstractPlan<OP_TYPE> implements LogicalPlan {
 
-    protected final LogicalProperties logicalProperties;
-
     public AbstractLogicalPlan(NodeType type, OP_TYPE operator, Plan... children) {
-        super(type, operator, children);
-        this.logicalProperties = new LogicalProperties(Collections.emptyList());
+        super(type, operator, operator.computeLogicalProperties(children), children);
     }
 
     public AbstractLogicalPlan(NodeType type, OP_TYPE operator,
-            GroupExpression groupExpression, LogicalProperties logicalProperties, Plan... children) {
-        super(type, operator, groupExpression, children);
-        this.logicalProperties = logicalProperties;
+                               Optional<LogicalProperties> logicalProperties, Plan... children) {
+        super(type, operator, logicalProperties.orElseGet(() -> operator.computeLogicalProperties(children)), children);
+    }
+
+    public AbstractLogicalPlan(NodeType type, OP_TYPE operator, Optional<GroupExpression> groupExpression,
+                               Optional<LogicalProperties> logicalProperties, Plan... children) {
+        super(type, operator, groupExpression,
+                logicalProperties.orElseGet(() -> operator.computeLogicalProperties(children)), children);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalBinaryPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalBinaryPlan.java
index 8d860b9dd7..c7cfbd2bf3 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalBinaryPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalBinaryPlan.java
@@ -21,12 +21,14 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.operators.plans.logical.LogicalBinaryOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.BinaryPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.base.Preconditions;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all logical plan that have two children.
@@ -39,18 +41,29 @@ public class LogicalBinaryPlan<
         implements BinaryPlan<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
 
     public LogicalBinaryPlan(OP_TYPE operator, LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
-        super(NodeType.LOGICAL, operator, leftChild, rightChild);
+        super(NodeType.LOGICAL, operator, Optional.empty(), leftChild, rightChild);
     }
 
-    public LogicalBinaryPlan(OP_TYPE operator, GroupExpression groupExpression, LogicalProperties logicalProperties,
-            LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+    public LogicalBinaryPlan(OP_TYPE operator, Optional<LogicalProperties> logicalProperties,
+                             LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+        super(NodeType.LOGICAL, operator, logicalProperties, leftChild, rightChild);
+    }
+
+    public LogicalBinaryPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
+             Optional<LogicalProperties> logicalProperties, LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
         super(NodeType.LOGICAL, operator, groupExpression, logicalProperties, leftChild, rightChild);
     }
 
     @Override
-    public LogicalBinaryPlan<OP_TYPE, Plan, Plan> newChildren(List<Plan> children) {
+    public LogicalBinaryPlan<OP_TYPE, Plan, Plan> withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 2);
-        return new LogicalBinaryPlan(operator, groupExpression.orElse(null),
-                logicalProperties, children.get(0), children.get(1));
+        return new LogicalBinaryPlan(operator, groupExpression, Optional.empty(),
+            children.get(0), children.get(1));
+    }
+
+    @Override
+    public LogicalBinaryPlan<OP_TYPE, LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> withOutput(List<Slot> output) {
+        return new LogicalBinaryPlan<>(operator, groupExpression,
+            Optional.of(logicalProperties.withOutput(output)), left(), right());
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLeafPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLeafPlan.java
index 012424125e..9fe4e55bcd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLeafPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLeafPlan.java
@@ -21,12 +21,14 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.operators.plans.logical.LogicalLeafOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.LeafPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.base.Preconditions;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all logical plan that have no child.
@@ -39,13 +41,24 @@ public class LogicalLeafPlan<OP_TYPE extends LogicalLeafOperator>
         super(NodeType.LOGICAL, operator);
     }
 
-    public LogicalLeafPlan(OP_TYPE operator, GroupExpression groupExpression, LogicalProperties logicalProperties) {
+    public LogicalLeafPlan(OP_TYPE operator, Optional<LogicalProperties> logicalProperties) {
+        super(NodeType.LOGICAL, operator, logicalProperties);
+    }
+
+    public LogicalLeafPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
+                           Optional<LogicalProperties> logicalProperties) {
         super(NodeType.LOGICAL, operator, groupExpression, logicalProperties);
     }
 
     @Override
-    public LogicalLeafPlan<OP_TYPE> newChildren(List<Plan> children) {
+    public LogicalLeafPlan<OP_TYPE> withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 0);
-        return new LogicalLeafPlan(operator, groupExpression.orElse(null), logicalProperties);
+        return new LogicalLeafPlan(operator, groupExpression, Optional.empty());
+    }
+
+    @Override
+    public LogicalLeafPlan<OP_TYPE> withOutput(List<Slot> output) {
+        return new LogicalLeafPlan<>(operator, groupExpression,
+            Optional.of(logicalProperties.withOutput(output)));
     }
 }
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 3cb4341a6e..db19a062a6 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
@@ -32,7 +32,7 @@ public interface LogicalPlan extends Plan {
     LogicalOperator getOperator();
 
     @Override
-    LogicalPlan newChildren(List<Plan> children);
+    LogicalPlan withChildren(List<Plan> children);
 
     /**
      * Map a [[LogicalPlan]] to another [[LogicalPlan]] if the passed context exists using the
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnaryPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnaryPlan.java
index b44db32de2..a12c243ff6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnaryPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnaryPlan.java
@@ -21,12 +21,14 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.operators.plans.logical.LogicalUnaryOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.UnaryPlan;
 
 import com.google.common.base.Preconditions;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all logical plan that have one child.
@@ -39,14 +41,20 @@ public class LogicalUnaryPlan<OP_TYPE extends LogicalUnaryOperator, CHILD_TYPE e
         super(NodeType.LOGICAL, operator, child);
     }
 
-    public LogicalUnaryPlan(OP_TYPE operator, GroupExpression groupExpression, LogicalProperties logicalProperties,
-            CHILD_TYPE child) {
+    public LogicalUnaryPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
+                            Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
         super(NodeType.LOGICAL, operator, groupExpression, logicalProperties, child);
     }
 
     @Override
-    public LogicalUnaryPlan<OP_TYPE, Plan> newChildren(List<Plan> children) {
+    public LogicalUnaryPlan<OP_TYPE, Plan> withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 1);
-        return new LogicalUnaryPlan(operator, groupExpression.orElse(null), logicalProperties, children.get(0));
+        return new LogicalUnaryPlan(operator, groupExpression, Optional.empty(), children.get(0));
+    }
+
+    @Override
+    public LogicalUnaryPlan<OP_TYPE, CHILD_TYPE> withOutput(List<Slot> output) {
+        return new LogicalUnaryPlan<>(operator, groupExpression,
+            Optional.of(logicalProperties.withOutput(output)), child());
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java
index ddd0228250..736b1d2196 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java
@@ -27,7 +27,7 @@ import org.apache.doris.nereids.trees.plans.AbstractPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import java.util.List;
-import java.util.Objects;
+import java.util.Optional;
 
 /**
  * Abstract class for all concrete physical plan.
@@ -36,7 +36,6 @@ public abstract class AbstractPhysicalPlan<OP_TYPE extends PhysicalOperator>
         extends AbstractPlan<OP_TYPE>
         implements PhysicalPlan {
 
-    protected final LogicalProperties logicalProperties;
     protected final PhysicalProperties physicalProperties;
 
     /**
@@ -44,8 +43,7 @@ public abstract class AbstractPhysicalPlan<OP_TYPE extends PhysicalOperator>
      */
     public AbstractPhysicalPlan(NodeType type, OP_TYPE operator,
             LogicalProperties logicalProperties, Plan... children) {
-        super(type, operator, children);
-        this.logicalProperties = Objects.requireNonNull(logicalProperties, "logicalProperties can not be null");
+        super(type, operator, logicalProperties, children);
         // TODO: compute physical properties
         this.physicalProperties = new PhysicalProperties();
     }
@@ -59,10 +57,9 @@ public abstract class AbstractPhysicalPlan<OP_TYPE extends PhysicalOperator>
      * @param logicalProperties logical properties of this plan
      * @param children children of this plan
      */
-    public AbstractPhysicalPlan(NodeType type, OP_TYPE operator, GroupExpression groupExpression,
+    public AbstractPhysicalPlan(NodeType type, OP_TYPE operator, Optional<GroupExpression> groupExpression,
             LogicalProperties logicalProperties, Plan... children) {
-        super(type, operator, groupExpression, children);
-        this.logicalProperties = Objects.requireNonNull(logicalProperties, "logicalProperties can not be null");
+        super(type, operator, groupExpression, logicalProperties, children);
         // TODO: compute physical properties
         this.physicalProperties = new PhysicalProperties();
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinaryPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinaryPlan.java
index 9b95552216..aba0e86e7b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinaryPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinaryPlan.java
@@ -21,12 +21,14 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.operators.plans.physical.PhysicalBinaryOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.BinaryPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.base.Preconditions;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all physical plan that have two children.
@@ -43,15 +45,21 @@ public class PhysicalBinaryPlan<
         super(NodeType.PHYSICAL, operator, logicalProperties, leftChild, rightChild);
     }
 
-    public PhysicalBinaryPlan(OP_TYPE operator, GroupExpression groupExpression, LogicalProperties logicalProperties,
-            LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+    public PhysicalBinaryPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
+            LogicalProperties logicalProperties, LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
         super(NodeType.PHYSICAL, operator, groupExpression, logicalProperties, leftChild, rightChild);
     }
 
     @Override
-    public PhysicalBinaryPlan<OP_TYPE, Plan, Plan> newChildren(List<Plan> children) {
+    public PhysicalBinaryPlan<OP_TYPE, Plan, Plan> withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 2);
-        return new PhysicalBinaryPlan(operator, groupExpression.orElse(null), logicalProperties,
-                children.get(0), children.get(1));
+        return new PhysicalBinaryPlan(operator, groupExpression, logicalProperties,
+            children.get(0), children.get(1));
+    }
+
+    @Override
+    public PhysicalBinaryPlan<OP_TYPE, LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> withOutput(List<Slot> output) {
+        LogicalProperties logicalProperties = new LogicalProperties(output);
+        return new PhysicalBinaryPlan<>(operator, groupExpression, logicalProperties, left(), right());
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeafPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeafPlan.java
index 0408641b79..75bb1abea8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeafPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeafPlan.java
@@ -21,12 +21,14 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.operators.plans.physical.PhysicalLeafOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.LeafPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.base.Preconditions;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all physical plan that have no child.
@@ -39,13 +41,20 @@ public class PhysicalLeafPlan<OP_TYPE extends PhysicalLeafOperator>
         super(NodeType.PHYSICAL, operator, logicalProperties);
     }
 
-    public PhysicalLeafPlan(OP_TYPE operator, GroupExpression groupExpression, LogicalProperties logicalProperties) {
+    public PhysicalLeafPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
+                            LogicalProperties logicalProperties) {
         super(NodeType.PHYSICAL, operator, groupExpression, logicalProperties);
     }
 
     @Override
-    public PhysicalLeafPlan<OP_TYPE> newChildren(List<Plan> children) {
+    public PhysicalLeafPlan<OP_TYPE> withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 0);
-        return new PhysicalLeafPlan(operator, groupExpression.orElse(null), logicalProperties);
+        return new PhysicalLeafPlan(operator, groupExpression, logicalProperties);
+    }
+
+    @Override
+    public PhysicalLeafPlan<OP_TYPE> withOutput(List<Slot> output) {
+        LogicalProperties logicalProperties = new LogicalProperties(output);
+        return new PhysicalLeafPlan<>(operator, groupExpression, logicalProperties);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnaryPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnaryPlan.java
index caadb0c177..b113458b6c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnaryPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnaryPlan.java
@@ -21,12 +21,14 @@ import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.operators.plans.physical.PhysicalUnaryOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.UnaryPlan;
 
 import com.google.common.base.Preconditions;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Abstract class for all physical plan that have one child.
@@ -39,15 +41,20 @@ public class PhysicalUnaryPlan<OP_TYPE extends PhysicalUnaryOperator, CHILD_TYPE
         super(NodeType.PHYSICAL, operator, logicalProperties, child);
     }
 
-    public PhysicalUnaryPlan(OP_TYPE operator, GroupExpression groupExpression, LogicalProperties logicalProperties,
-            CHILD_TYPE child) {
+    public PhysicalUnaryPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
+                             LogicalProperties logicalProperties, CHILD_TYPE child) {
         super(NodeType.PHYSICAL, operator, groupExpression, logicalProperties, child);
     }
 
     @Override
-    public PhysicalUnaryPlan<OP_TYPE, Plan> newChildren(List<Plan> children) {
+    public PhysicalUnaryPlan<OP_TYPE, Plan> withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 1);
-        return new PhysicalUnaryPlan(operator, groupExpression.orElse(null),
-                logicalProperties, (Plan) children.get(0));
+        return new PhysicalUnaryPlan(operator, groupExpression, logicalProperties, children.get(0));
+    }
+
+    @Override
+    public PhysicalUnaryPlan<OP_TYPE, CHILD_TYPE> withOutput(List<Slot> output) {
+        LogicalProperties logicalProperties = new LogicalProperties(output);
+        return new PhysicalUnaryPlan<>(operator, groupExpression, logicalProperties, child());
     }
 }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/RewriteTopDownJobTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/RewriteTopDownJobTest.java
index b5e5fba18f..43d454c627 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/RewriteTopDownJobTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/RewriteTopDownJobTest.java
@@ -17,7 +17,9 @@
 
 package org.apache.doris.nereids.jobs;
 
-import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Table;
+import org.apache.doris.catalog.Type;
 import org.apache.doris.common.AnalysisException;
 import org.apache.doris.nereids.OptimizerContext;
 import org.apache.doris.nereids.PlannerContext;
@@ -33,9 +35,14 @@ import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+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.Plans;
+import org.apache.doris.nereids.types.IntegerType;
+import org.apache.doris.nereids.types.StringType;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -47,7 +54,10 @@ public class RewriteTopDownJobTest implements Plans {
         @Override
         public Rule<Plan> build() {
             return unboundRelation().then(unboundRelation -> plan(
-                new LogicalRelation(new OlapTable(), Lists.newArrayList("test"))
+                new LogicalRelation(new Table(0, "test", Table.TableType.OLAP, ImmutableList.of(
+                    new Column("id", Type.INT),
+                    new Column("name", Type.STRING)
+                )), Lists.newArrayList("test"))
             )).toRule(RuleType.BINDING_UNBOUND_RELATION_RULE);
         }
     }
@@ -56,24 +66,37 @@ public class RewriteTopDownJobTest implements Plans {
     public void testSimplestScene() throws AnalysisException {
         UnboundRelation unboundRelation = new UnboundRelation(Lists.newArrayList("test"));
         Plan leaf = plan(unboundRelation);
-        LogicalProject project = new LogicalProject(Lists.newArrayList());
+        LogicalProject project = new LogicalProject(ImmutableList.of(
+            new SlotReference("name", StringType.INSTANCE, true, ImmutableList.of("test")))
+        );
         Plan root = plan(project, leaf);
-        Memo<Plan> memo = new Memo();
+        Memo memo = new Memo();
         memo.initialize(root);
 
-        OptimizerContext<Plan> optimizerContext = new OptimizerContext<>(memo);
+        OptimizerContext optimizerContext = new OptimizerContext(memo);
         PlannerContext plannerContext = new PlannerContext(optimizerContext, null, new PhysicalProperties());
         List<Rule<Plan>> fakeRules = Lists.newArrayList(new FakeRule().build());
-        RewriteTopDownJob<Plan> rewriteTopDownJob = new RewriteTopDownJob<>(memo.getRoot(), fakeRules, plannerContext);
+        RewriteTopDownJob rewriteTopDownJob = new RewriteTopDownJob(memo.getRoot(), fakeRules, plannerContext);
         plannerContext.getOptimizerContext().pushJob(rewriteTopDownJob);
         plannerContext.getOptimizerContext().getJobScheduler().executeJobPool(plannerContext);
 
         Group rootGroup = memo.getRoot();
         Assertions.assertEquals(1, rootGroup.getLogicalExpressions().size());
         GroupExpression rootGroupExpression = rootGroup.getLogicalExpression();
+        List<Slot> output = rootGroup.getLogicalProperties().getOutput();
+        Assertions.assertEquals(output.size(), 1);
+        Assertions.assertEquals(output.get(0).getName(), "name");
+        Assertions.assertEquals(output.get(0).getDataType(), StringType.INSTANCE);
         Assertions.assertEquals(1, rootGroupExpression.children().size());
         Assertions.assertEquals(OperatorType.LOGICAL_PROJECT, rootGroupExpression.getOperator().getType());
+
         Group leafGroup = rootGroupExpression.child(0);
+        output = leafGroup.getLogicalProperties().getOutput();
+        Assertions.assertEquals(output.size(), 2);
+        Assertions.assertEquals(output.get(0).getName(), "id");
+        Assertions.assertEquals(output.get(0).getDataType(), IntegerType.INSTANCE);
+        Assertions.assertEquals(output.get(1).getName(), "name");
+        Assertions.assertEquals(output.get(1).getDataType(), StringType.INSTANCE);
         Assertions.assertEquals(1, leafGroup.getLogicalExpressions().size());
         GroupExpression leafGroupExpression = leafGroup.getLogicalExpression();
         Assertions.assertEquals(OperatorType.LOGICAL_BOUND_RELATION, leafGroupExpression.getOperator().getType());
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/pattern/GroupExpressionMatchingTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/pattern/GroupExpressionMatchingTest.java
index 4a3bbed0c4..1231a05a82 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/pattern/GroupExpressionMatchingTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/pattern/GroupExpressionMatchingTest.java
@@ -38,10 +38,10 @@ public class GroupExpressionMatchingTest implements Plans {
 
         UnboundRelation unboundRelation = new UnboundRelation(Lists.newArrayList("test"));
         Plan plan = plan(unboundRelation);
-        Memo<Plan> memo = new Memo<>();
+        Memo memo = new Memo();
         memo.initialize(plan);
 
-        GroupExpressionMatching<Plan> groupExpressionMatching
+        GroupExpressionMatching groupExpressionMatching
                 = new GroupExpressionMatching(pattern, memo.getRoot().getLogicalExpression());
         Iterator<Plan> iterator = groupExpressionMatching.iterator();
 
@@ -60,15 +60,15 @@ public class GroupExpressionMatchingTest implements Plans {
         Plan leaf = plan(unboundRelation);
         LogicalProject project = new LogicalProject(Lists.newArrayList());
         Plan root = plan(project, leaf);
-        Memo<Plan> memo = new Memo();
+        Memo memo = new Memo();
         memo.initialize(root);
 
         UnboundRelation anotherUnboundRelation = new UnboundRelation(Lists.newArrayList("test2"));
         Plan anotherLeaf = plan(anotherUnboundRelation);
         memo.copyIn(anotherLeaf, memo.getRoot().getLogicalExpression().child(0), false);
 
-        GroupExpressionMatching<Plan> groupExpressionMatching
-                = new GroupExpressionMatching<>(pattern, memo.getRoot().getLogicalExpression());
+        GroupExpressionMatching groupExpressionMatching
+                = new GroupExpressionMatching(pattern, memo.getRoot().getLogicalExpression());
         Iterator<Plan> iterator = groupExpressionMatching.iterator();
 
         Assertions.assertTrue(iterator.hasNext());
@@ -93,15 +93,15 @@ public class GroupExpressionMatchingTest implements Plans {
         Plan leaf = plan(unboundRelation);
         LogicalProject project = new LogicalProject(Lists.newArrayList());
         Plan root = plan(project, leaf);
-        Memo<Plan> memo = new Memo();
+        Memo memo = new Memo();
         memo.initialize(root);
 
         UnboundRelation anotherUnboundRelation = new UnboundRelation(Lists.newArrayList("test2"));
         Plan anotherLeaf = plan(anotherUnboundRelation);
         memo.copyIn(anotherLeaf, memo.getRoot().getLogicalExpression().child(0), false);
 
-        GroupExpressionMatching<Plan> groupExpressionMatching
-                = new GroupExpressionMatching<>(pattern, memo.getRoot().getLogicalExpression());
+        GroupExpressionMatching groupExpressionMatching
+                = new GroupExpressionMatching(pattern, memo.getRoot().getLogicalExpression());
         Iterator<Plan> iterator = groupExpressionMatching.iterator();
 
         Assertions.assertTrue(iterator.hasNext());
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/plan/TestPlanOutput.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/plan/TestPlanOutput.java
new file mode 100644
index 0000000000..e79d55e6da
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/plan/TestPlanOutput.java
@@ -0,0 +1,102 @@
+// 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.plan;
+
+import org.apache.doris.catalog.AggregateType;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Table;
+import org.apache.doris.catalog.Type;
+import org.apache.doris.nereids.analyzer.UnboundRelation;
+import org.apache.doris.nereids.exceptions.UnboundException;
+import org.apache.doris.nereids.operators.OperatorType;
+import org.apache.doris.nereids.operators.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.operators.plans.physical.PhysicalScan;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.Plans;
+import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
+import org.apache.doris.nereids.types.IntegerType;
+import org.apache.doris.nereids.types.StringType;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+
+import java.util.List;
+
+public class TestPlanOutput implements Plans {
+    @Test
+    public void testComputeOutput() {
+        Table table = new Table(0L, "a", Table.TableType.OLAP, ImmutableList.<Column>of(
+            new Column("id", Type.INT, true, AggregateType.NONE, "0", ""),
+            new Column("name", Type.STRING, true, AggregateType.NONE, "", "")
+        ));
+        LogicalLeafPlan<LogicalRelation> relationPlan = plan(
+            new LogicalRelation(table, ImmutableList.of("a"))
+        );
+        List<Slot> output = relationPlan.getOutput();
+        Assertions.assertTrue(output.size() == 2);
+        Assertions.assertEquals(output.get(0).getName(), "id");
+        Assertions.assertEquals(output.get(0).getQualifiedName(), "a.id");
+        Assertions.assertEquals(output.get(0).getDataType(), IntegerType.INSTANCE);
+
+        Assertions.assertEquals(output.get(1).getName(), "name");
+        Assertions.assertEquals(output.get(1).getQualifiedName(), "a.name");
+        Assertions.assertEquals(output.get(1).getDataType(), StringType.INSTANCE);
+    }
+
+    @Test
+    public void testLazyComputeOutput() {
+        // not throw exception when create new UnboundRelation
+        LogicalLeafPlan<UnboundRelation> relationPlan = plan(
+            new UnboundRelation(ImmutableList.of("a"))
+        );
+
+        try {
+            // throw exception when getOutput
+            relationPlan.getOutput();
+            throw new IllegalStateException("Expect an UnboundException but no exception");
+        } catch (UnboundException e) {
+            // correct exception
+        }
+    }
+
+    @Test
+    public void testWithOutput() {
+        Table table = new Table(0L, "a", Table.TableType.OLAP, ImmutableList.of(
+            new Column("id", Type.INT, true, AggregateType.NONE, "0", ""),
+            new Column("name", Type.STRING, true, AggregateType.NONE, "", "")
+        ));
+        LogicalLeafPlan<LogicalRelation> relationPlan = plan(
+            new LogicalRelation(table, ImmutableList.of("a"))
+        );
+
+        List<Slot> output = relationPlan.getOutput();
+        // column prune
+        LogicalLeafPlan<LogicalRelation> newPlan = relationPlan.withOutput(ImmutableList.of(output.get(0)));
+        output = newPlan.getOutput();
+        Assertions.assertTrue(output.size() == 1);
+        Assertions.assertEquals(output.get(0).getName(), "id");
+        Assertions.assertEquals(output.get(0).getQualifiedName(), "a.id");
+        Assertions.assertEquals(output.get(0).getDataType(), IntegerType.INSTANCE);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testPhysicalPlanMustHaveLogicalProperties() {
+        plan(new PhysicalScan(OperatorType.PHYSICAL_OLAP_SCAN, ImmutableList.of("tbl")) {}, null);
+    }
+}


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