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/30 08:26:45 UTC

[doris] branch master updated: [feature](nereids) Support analyze for test SSB (#10415)

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/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 77b1565b96 [feature](nereids) Support analyze for test SSB (#10415)
77b1565b96 is described below

commit 77b1565b96e916cbb27ffe62c17701c2e11adaf2
Author: 924060929 <92...@qq.com>
AuthorDate: Thu Jun 30 16:26:39 2022 +0800

    [feature](nereids) Support analyze for test SSB (#10415)
    
    Follow-up #10241, this PR go through parse and analyze the SSB and add this functions:
    
    1. support parse parenthesizedExpression
    2. support analyze LogicalAggregate and LogicalSort
    3. replace the functionCall to UnboundFunction and BoundFunction
    4. support sum aggregate funciton
    5. fix the dead loop in the ExpressionRewriter
    6. refine some code
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |  16 +-
 .../java/org/apache/doris/nereids/PlanContext.java |   2 +-
 .../doris/nereids/analyzer/UnboundAlias.java       |  31 +-
 .../{UnboundAlias.java => UnboundFunction.java}    |  50 +-
 .../nereids/jobs/rewrite/RewriteBottomUpJob.java   |  12 +-
 .../nereids/jobs/rewrite/RewriteTopDownJob.java    |   8 +
 ...gicalAggregation.java => LogicalAggregate.java} |  45 +-
 .../operators/plans/logical/LogicalFilter.java     |   2 +-
 .../operators/plans/logical/LogicalJoin.java       |   2 +-
 .../operators/plans/logical/LogicalProject.java    |  19 +-
 .../operators/plans/logical/LogicalRelation.java   |   4 +-
 .../operators/plans/logical/LogicalSort.java       |   6 +-
 .../doris/nereids/parser/LogicalPlanBuilder.java   | 789 +++++++++++----------
 .../nereids/pattern/GroupExpressionMatching.java   |  14 +-
 .../org/apache/doris/nereids/rules/RuleType.java   |   9 +-
 .../doris/nereids/rules/analysis/BindFunction.java |  89 +++
 .../nereids/rules/analysis/BindSlotReference.java  | 155 +++-
 .../rules/analysis/ProjectToGlobalAggregate.java   |  53 ++
 .../rewrite/AbstractExpressionRewriteRule.java     |   5 -
 .../rewrite/rules/SimplifyNotExprRule.java         |   1 -
 .../LogicalAggToPhysicalHashAgg.java               |   2 +-
 .../org/apache/doris/nereids/trees/NodeType.java   |   5 +-
 .../{expressions/Slot.java => TernaryNode.java}    |  33 +-
 .../org/apache/doris/nereids/trees/TreeNode.java   |  29 +
 .../nereids/trees/analysis/FunctionParams.java     |  83 ---
 .../doris/nereids/trees/expressions/Add.java       |  19 +-
 .../doris/nereids/trees/expressions/Alias.java     |  24 +-
 .../doris/nereids/trees/expressions/And.java       |  10 +
 .../nereids/trees/expressions/Arithmetic.java      |  42 +-
 .../{BetweenPredicate.java => Between.java}        |  50 +-
 .../trees/expressions/ComparisonPredicate.java     |  26 -
 .../trees/expressions/CompoundPredicate.java       |   9 +-
 .../expressions/DefaultExpressionRewriter.java     | 112 +--
 .../expressions/DefaultExpressionVisitor.java}     |  17 +-
 .../doris/nereids/trees/expressions/Divide.java    |  17 +-
 .../nereids/trees/expressions/Expression.java      |  17 +-
 .../trees/expressions/ExpressionConverter.java     |  18 +-
 .../trees/expressions/ExpressionVisitor.java       |  77 +-
 .../nereids/trees/expressions/FunctionCall.java    |  58 --
 .../doris/nereids/trees/expressions/Mod.java       |  17 +-
 .../doris/nereids/trees/expressions/Multiply.java  |  17 +-
 .../doris/nereids/trees/expressions/Not.java       |  12 +-
 .../nereids/trees/expressions/NullSafeEqual.java   |  10 +
 .../apache/doris/nereids/trees/expressions/Or.java |  10 +
 .../doris/nereids/trees/expressions/Slot.java      |  10 -
 .../nereids/trees/expressions/SlotReference.java   |   9 +-
 .../doris/nereids/trees/expressions/Subtract.java  |  17 +-
 .../{Slot.java => TernaryExpression.java}          |  28 +-
 .../expressions/functions/AggregateFunction.java}  |  23 +-
 .../{Not.java => functions/BoundFunction.java}     |  59 +-
 .../nereids/trees/expressions/functions/Sum.java   |  67 ++
 .../doris/nereids/trees/plans/AbstractPlan.java    |  20 +
 .../trees/plans/PhysicalPlanTranslator.java        |   2 +-
 .../org/apache/doris/nereids/trees/plans/Plan.java |   2 +
 .../trees/plans/logical/LogicalBinaryPlan.java     |   5 +
 .../trees/plans/logical/LogicalLeafPlan.java       |   5 +
 .../nereids/trees/plans/logical/LogicalPlan.java   |  10 +
 .../trees/plans/logical/LogicalUnaryPlan.java      |   5 +
 .../trees/plans/physical/PhysicalBinaryPlan.java   |   5 +
 .../trees/plans/physical/PhysicalLeafPlan.java     |   4 +
 .../trees/plans/physical/PhysicalUnaryPlan.java    |   5 +
 .../types/{DoubleType.java => BigIntType.java}     |   9 +-
 .../org/apache/doris/nereids/types/DataType.java   |   2 +
 .../org/apache/doris/nereids/types/DoubleType.java |   2 +-
 .../{AnalyzeTest.java => AnalyzeSSBTest.java}      | 128 +++-
 .../pattern/GroupExpressionMatchingTest.java       |  20 +
 .../org/apache/doris/nereids/ssb/SSBUtils.java     | 351 +++++++++
 .../apache/doris/utframe/TestWithFeService.java    |  18 +-
 68 files changed, 1858 insertions(+), 974 deletions(-)

diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index b175d9748d..4d47e85dfc 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -183,7 +183,7 @@ predicate
 valueExpression
     : primaryExpression                                                                      #valueExpressionDefault
     | operator=(MINUS | PLUS) valueExpression                                                #arithmeticUnary
-    | left=valueExpression operator=(ASTERISK | SLASH | PERCENT) right=valueExpression #arithmeticBinary
+    | left=valueExpression operator=(ASTERISK | SLASH | PERCENT) right=valueExpression       #arithmeticBinary
     | left=valueExpression operator=(PLUS | MINUS) right=valueExpression                     #arithmeticBinary
     | left=valueExpression comparisonOperator right=valueExpression                          #comparison
     ;
@@ -192,10 +192,11 @@ primaryExpression
     : constant                                                                                 #constantDefault
     | ASTERISK                                                                                 #star
     | qualifiedName DOT ASTERISK                                                               #star
-    | functionExpression                                                                       #functioncall
+    | identifier '(' DISTINCT? arguments+=expression* ')'                                      #functionCall
     | LEFT_PAREN query RIGHT_PAREN                                                             #subqueryExpression
     | identifier                                                                               #columnReference
     | base=primaryExpression DOT fieldName=identifier                                          #dereference
+    | LEFT_PAREN expression RIGHT_PAREN                                                        #parenthesizedExpression
     ;
 
 qualifiedName
@@ -209,10 +210,6 @@ constant
     | STRING+                                                                                  #stringLiteral
     ;
 
-functionExpression
-    : aggFunction                                                                              #aggFunctions
-    ;
-
 comparisonOperator
     : EQ | NEQ | LT | LTE | GT | GTE | NSEQ
     ;
@@ -221,13 +218,6 @@ booleanValue
     : TRUE | FALSE
     ;
 
-//TODO: In the future, instead of specifying the function name,
-//      the function information is obtained by parsing the catalog. This method is more scalable.
-aggFunction
-    : AVG '(' DISTINCT? expression ')'
-    | SUM '(' DISTINCT? expression ')'
-    ;
-
 
 // this rule is used for explicitly capturing wrong identifiers such as test-table, which should actually be `test-table`
 // replace identifier with errorCapturingIdentifier where the immediate follow symbol is not an expression, otherwise
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/PlanContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/PlanContext.java
index 50ead51f0e..9f37171747 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/PlanContext.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/PlanContext.java
@@ -90,7 +90,7 @@ public class PlanContext {
     public List<Id> getChildOutputIds(int index) {
         List<Id> ids = Lists.newArrayList();
         childLogicalPropertyAt(index).getOutput().forEach(slot -> {
-            ids.add(slot.getId());
+            ids.add(slot.getExprId());
         });
         return ids;
     }
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 ea13c02e92..e17e5ed645 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
@@ -19,11 +19,13 @@ package org.apache.doris.nereids.analyzer;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.NodeType;
-import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.ExpressionVisitor;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.UnaryExpression;
+import org.apache.doris.nereids.types.DataType;
+
+import com.google.common.base.Preconditions;
 
 import java.util.List;
 
@@ -39,32 +41,23 @@ public class UnboundAlias<CHILD_TYPE extends Expression>
     }
 
     @Override
-    public String sql() {
-        return null;
-    }
-
-    @Override
-    public String getName() throws UnboundException {
-        return null;
-    }
-
-    @Override
-    public ExprId getExprId() throws UnboundException {
-        return null;
-    }
-
-    @Override
-    public List<String> getQualifier() throws UnboundException {
-        return null;
+    public DataType getDataType() throws UnboundException {
+        return child().getDataType();
     }
 
     @Override
     public String toString() {
-        return "UnboundAlias(" + child() + ", None)";
+        return "UnboundAlias(" + child() + ")";
     }
 
     @Override
     public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
         return visitor.visitUnboundAlias(this, context);
     }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new UnboundAlias<>(children.get(0));
+    }
 }
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/UnboundFunction.java
similarity index 54%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundFunction.java
index ea13c02e92..f82b13259a 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/UnboundFunction.java
@@ -17,54 +17,54 @@
 
 package org.apache.doris.nereids.analyzer;
 
-import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.NodeType;
-import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.ExpressionVisitor;
-import org.apache.doris.nereids.trees.expressions.NamedExpression;
-import org.apache.doris.nereids.trees.expressions.UnaryExpression;
+
+import com.google.common.base.Joiner;
 
 import java.util.List;
+import java.util.Objects;
 
 /**
- * Expression for unbound alias.
+ * Expression for unbound function.
  */
-public class UnboundAlias<CHILD_TYPE extends Expression>
-        extends NamedExpression
-        implements UnaryExpression<CHILD_TYPE>, Unbound {
+public class UnboundFunction extends Expression implements Unbound {
 
-    public UnboundAlias(CHILD_TYPE child) {
-        super(NodeType.UNBOUND_ALIAS, child);
-    }
+    private final String name;
+    private final boolean isDistinct;
 
-    @Override
-    public String sql() {
-        return null;
+    public UnboundFunction(String name, boolean isDistinct, List<Expression> arguments) {
+        super(NodeType.UNBOUND_FUNCTION, arguments.toArray(new Expression[0]));
+        this.name = Objects.requireNonNull(name, "name can not be null");
+        this.isDistinct = isDistinct;
     }
 
-    @Override
-    public String getName() throws UnboundException {
-        return null;
+    public String getName() {
+        return name;
     }
 
-    @Override
-    public ExprId getExprId() throws UnboundException {
-        return null;
+    public boolean isDistinct() {
+        return isDistinct;
     }
 
-    @Override
-    public List<String> getQualifier() throws UnboundException {
-        return null;
+    public List<Expression> getArguments() {
+        return children();
     }
 
     @Override
     public String toString() {
-        return "UnboundAlias(" + child() + ", None)";
+        String params = Joiner.on(", ").join(children);
+        return "'" + name + "(" + (isDistinct ? "DISTINCT " : "")  + params + ")";
     }
 
     @Override
     public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
-        return visitor.visitUnboundAlias(this, context);
+        return visitor.visitUnboundFunction(this, context);
+    }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        return new UnboundFunction(name, isDistinct, children);
     }
 }
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 6cbf8b8e6b..81a6719b13 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,12 +25,14 @@ 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.rules.RuleFactory;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.base.Preconditions;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * Bottom up job for rewrite, use pattern match.
@@ -40,6 +42,12 @@ public class RewriteBottomUpJob extends Job<Plan> {
     private final List<Rule<Plan>> rules;
     private final boolean childrenOptimized;
 
+    public RewriteBottomUpJob(Group group, PlannerContext context, List<RuleFactory<Plan>> factories) {
+        this(group, factories.stream()
+                .flatMap(factory -> factory.buildRules().stream())
+                .collect(Collectors.toList()), context, false);
+    }
+
     public RewriteBottomUpJob(Group group, List<Rule<Plan>> rules, PlannerContext context) {
         this(group, rules, context, false);
     }
@@ -72,10 +80,10 @@ public class RewriteBottomUpJob extends Job<Plan> {
                 Preconditions.checkArgument(afters.size() == 1);
                 Plan after = afters.get(0);
                 if (after != before) {
-                    GroupExpression gexpr = context.getOptimizerContext()
+                    GroupExpression groupExpr = context.getOptimizerContext()
                             .getMemo()
                             .copyIn(after, group, rule.isRewrite());
-                    gexpr.setApplied(rule);
+                    groupExpr.setApplied(rule);
                     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 4aebb3e182..89c7f70d6b 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,12 +24,14 @@ 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.rules.RuleFactory;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.base.Preconditions;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 /**
  * Top down job for rewrite, use pattern match.
@@ -38,6 +40,12 @@ public class RewriteTopDownJob extends Job<Plan> {
     private final Group group;
     private final List<Rule<Plan>> rules;
 
+    public RewriteTopDownJob(Group group, PlannerContext context, List<RuleFactory<Plan>> factories) {
+        this(group, factories.stream()
+                .flatMap(factory -> factory.buildRules().stream())
+                .collect(Collectors.toList()), context);
+    }
+
     /**
      * Constructor.
      *
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalAggregation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalAggregate.java
similarity index 67%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalAggregation.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalAggregate.java
index 00088bf1f1..77fe7fc694 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalAggregation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalAggregate.java
@@ -17,7 +17,6 @@
 
 package org.apache.doris.nereids.operators.plans.logical;
 
-import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.operators.OperatorType;
 import org.apache.doris.nereids.operators.plans.AggPhase;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -29,19 +28,20 @@ import com.google.common.collect.ImmutableList;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Logical Aggregation plan operator.
  * <p>
  * eg:select a, sum(b), c from table group by a, c;
- * groupByExpressions: Column field after group by. eg: a, c;
- * outputExpressions: Column field after select. eg: a, sum(b), c;
+ * groupByExprList: Column field after group by. eg: a, c;
+ * outputExpressionList: Column field after select. eg: a, sum(b), c;
  * partitionExprList: Column field after partition by.
  * <p>
  * Each agg node only contains the select statement field of the same layer,
  * and other agg nodes in the subquery contain.
  */
-public class LogicalAggregation extends LogicalUnaryOperator {
+public class LogicalAggregate extends LogicalUnaryOperator {
 
     private final List<Expression> groupByExprList;
     private final List<NamedExpression> outputExpressionList;
@@ -52,7 +52,7 @@ public class LogicalAggregation extends LogicalUnaryOperator {
     /**
      * Desc: Constructor for LogicalAggregation.
      */
-    public LogicalAggregation(List<Expression> groupByExprList, List<NamedExpression> outputExpressionList) {
+    public LogicalAggregate(List<Expression> groupByExprList, List<NamedExpression> outputExpressionList) {
         super(OperatorType.LOGICAL_AGGREGATION);
         this.groupByExprList = groupByExprList;
         this.outputExpressionList = outputExpressionList;
@@ -80,23 +80,40 @@ public class LogicalAggregation extends LogicalUnaryOperator {
 
     @Override
     public String toString() {
-        return "Aggregation (" + "outputExpressionList: " + StringUtils.join(outputExpressionList, ", ")
-                + ", groupByExprList: " + StringUtils.join(groupByExprList, ", ") + ")";
+        return "LogicalAggregate (" + "outputExpressionList: ["
+                + StringUtils.join(outputExpressionList, ", ")
+                + "], groupByExprList: [" + StringUtils.join(groupByExprList, ", ") + "])";
     }
 
     @Override
     public List<Slot> computeOutput(Plan input) {
-        return outputExpressionList.stream().map(namedExpr -> {
-            try {
-                return namedExpr.toSlot();
-            } catch (UnboundException e) {
-                throw new IllegalStateException(e);
-            }
-        }).collect(ImmutableList.toImmutableList());
+        return outputExpressionList.stream()
+                .map(NamedExpression::toSlot)
+                .collect(ImmutableList.toImmutableList());
     }
 
     @Override
     public List<Expression> getExpressions() {
         return new ImmutableList.Builder<Expression>().addAll(groupByExprList).addAll(outputExpressionList).build();
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        LogicalAggregate that = (LogicalAggregate) o;
+        return Objects.equals(groupByExprList, that.groupByExprList)
+                && Objects.equals(outputExpressionList, that.outputExpressionList)
+                && Objects.equals(partitionExprList, that.partitionExprList)
+                && aggPhase == that.aggPhase;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(groupByExprList, outputExpressionList, partitionExprList, aggPhase);
+    }
 }
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 9f424906eb..960739f102 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
@@ -49,7 +49,7 @@ public class LogicalFilter extends LogicalUnaryOperator {
 
     @Override
     public String toString() {
-        return "Filter (" + predicates + ")";
+        return "LogicalFilter (" + predicates + ")";
     }
 
     @Override
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 facedc2836..d3a93bbf96 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
@@ -87,7 +87,7 @@ public class LogicalJoin extends LogicalBinaryOperator {
 
     @Override
     public String toString() {
-        StringBuilder sb = new StringBuilder("Join (").append(joinType);
+        StringBuilder sb = new StringBuilder("LogicalJoin (").append(joinType);
         condition.ifPresent(expression -> sb.append(", ").append(expression));
         return sb.append(")").toString();
     }
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 989477b7ec..a13d2e2785 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
@@ -64,11 +64,28 @@ public class LogicalProject extends LogicalUnaryOperator {
 
     @Override
     public String toString() {
-        return "Project (" + StringUtils.join(projects, ", ") + ")";
+        return "LogicalProject (" + StringUtils.join(projects, ", ") + ")";
     }
 
     @Override
     public List<Expression> getExpressions() {
         return new ImmutableList.Builder<Expression>().addAll(projects).build();
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        LogicalProject that = (LogicalProject) o;
+        return projects.equals(that.projects);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(projects);
+    }
 }
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 6f7ce83e14..ce874e74a5 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
@@ -46,7 +46,7 @@ public abstract class LogicalRelation extends LogicalLeafOperator {
     public LogicalRelation(Table table, List<String> qualifier) {
         super(OperatorType.LOGICAL_BOUND_RELATION);
         this.table = Objects.requireNonNull(table, "table can not be null");
-        this.qualifier = Objects.requireNonNull(qualifier, "qualifier can not be null");
+        this.qualifier = ImmutableList.copyOf(Objects.requireNonNull(qualifier, "qualifier can not be null"));
     }
 
     public Table getTable() {
@@ -59,7 +59,7 @@ public abstract class LogicalRelation extends LogicalLeafOperator {
 
     @Override
     public String toString() {
-        return "Relation(" + StringUtils.join(qualifier, ".") + "." + table.getName() + ")";
+        return "LogicalRelation (" + StringUtils.join(qualifier, ".") + ")";
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalSort.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalSort.java
index 6c63f8a09e..16a8e396f4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalSort.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalSort.java
@@ -28,7 +28,6 @@ import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
 import java.util.Objects;
-import java.util.stream.Collectors;
 
 /**
  * Logical Sort plan operator.
@@ -76,7 +75,8 @@ public class LogicalSort extends LogicalUnaryOperator {
 
     @Override
     public List<Expression> getExpressions() {
-        return new ImmutableList.Builder<Expression>().addAll(
-                orderKeys.stream().map(OrderKey::getExpr).collect(Collectors.toList())).build();
+        return orderKeys.stream()
+                .map(OrderKey::getExpr)
+                .collect(ImmutableList.toImmutableList());
     }
 }
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 8cac723572..f19c8fb9a7 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
@@ -20,16 +20,13 @@ package org.apache.doris.nereids.parser;
 
 import org.apache.doris.nereids.DorisParser;
 import org.apache.doris.nereids.DorisParser.AggClauseContext;
-import org.apache.doris.nereids.DorisParser.AggFunctionsContext;
 import org.apache.doris.nereids.DorisParser.ArithmeticBinaryContext;
 import org.apache.doris.nereids.DorisParser.ArithmeticUnaryContext;
 import org.apache.doris.nereids.DorisParser.BooleanLiteralContext;
 import org.apache.doris.nereids.DorisParser.ColumnReferenceContext;
 import org.apache.doris.nereids.DorisParser.ComparisonContext;
 import org.apache.doris.nereids.DorisParser.DereferenceContext;
-import org.apache.doris.nereids.DorisParser.ExpressionContext;
 import org.apache.doris.nereids.DorisParser.FromClauseContext;
-import org.apache.doris.nereids.DorisParser.GroupByItemContext;
 import org.apache.doris.nereids.DorisParser.IdentifierListContext;
 import org.apache.doris.nereids.DorisParser.IdentifierSeqContext;
 import org.apache.doris.nereids.DorisParser.IntegerLiteralContext;
@@ -53,32 +50,29 @@ import org.apache.doris.nereids.DorisParser.SelectClauseContext;
 import org.apache.doris.nereids.DorisParser.SingleStatementContext;
 import org.apache.doris.nereids.DorisParser.SortItemContext;
 import org.apache.doris.nereids.DorisParser.StarContext;
-import org.apache.doris.nereids.DorisParser.StatementContext;
 import org.apache.doris.nereids.DorisParser.StringLiteralContext;
 import org.apache.doris.nereids.DorisParser.TableNameContext;
 import org.apache.doris.nereids.DorisParser.WhereClauseContext;
 import org.apache.doris.nereids.DorisParserBaseVisitor;
 import org.apache.doris.nereids.analyzer.UnboundAlias;
+import org.apache.doris.nereids.analyzer.UnboundFunction;
 import org.apache.doris.nereids.analyzer.UnboundRelation;
 import org.apache.doris.nereids.analyzer.UnboundSlot;
 import org.apache.doris.nereids.analyzer.UnboundStar;
 import org.apache.doris.nereids.operators.plans.JoinType;
-import org.apache.doris.nereids.operators.plans.logical.LogicalAggregation;
+import org.apache.doris.nereids.operators.plans.logical.LogicalAggregate;
 import org.apache.doris.nereids.operators.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
 import org.apache.doris.nereids.operators.plans.logical.LogicalSort;
 import org.apache.doris.nereids.properties.OrderKey;
-import org.apache.doris.nereids.trees.analysis.FunctionParams;
 import org.apache.doris.nereids.trees.expressions.Add;
 import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.And;
-import org.apache.doris.nereids.trees.expressions.Arithmetic;
-import org.apache.doris.nereids.trees.expressions.BetweenPredicate;
+import org.apache.doris.nereids.trees.expressions.Between;
 import org.apache.doris.nereids.trees.expressions.Divide;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.FunctionCall;
 import org.apache.doris.nereids.trees.expressions.GreaterThan;
 import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
 import org.apache.doris.nereids.trees.expressions.LessThan;
@@ -96,34 +90,22 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.antlr.v4.runtime.ParserRuleContext;
 import org.antlr.v4.runtime.RuleContext;
-import org.antlr.v4.runtime.Token;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.RuleNode;
 import org.antlr.v4.runtime.tree.TerminalNode;
-import org.apache.commons.collections.CollectionUtils;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
-import java.util.function.BiFunction;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
 
 /**
  * Build an logical plan tree with unbounded nodes.
  */
 public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
 
-    /**
-     * Create a logical plan using a where clause.
-     */
-    private final BiFunction<WhereClauseContext, LogicalPlan, LogicalPlan> withWhereClause
-            = (WhereClauseContext ctx, LogicalPlan plan) -> new LogicalUnaryPlan(
-                    new LogicalFilter(expression((ctx.booleanExpression()))), plan);
-
     protected <T> T typedVisit(ParseTree ctx) {
         return (T) ctx.accept(this);
     }
@@ -144,225 +126,42 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
 
     @Override
     public LogicalPlan visitSingleStatement(SingleStatementContext ctx) {
-        Supplier<LogicalPlan> f = () -> (LogicalPlan) visit(ctx.statement());
-        return ParserUtils.withOrigin(ctx, f);
+        return ParserUtils.withOrigin(ctx, () -> (LogicalPlan) visit(ctx.statement()));
     }
 
     /**
      * Visit multi-statements.
      */
-    public Object visitMultiStatements(MultiStatementsContext ctx) {
-        List<LogicalPlan> logicalPlanList = new ArrayList<>();
-        for (StatementContext stmtCtx : ctx.statement()) {
-            LogicalPlan logicalPlan = (LogicalPlan) visit(stmtCtx);
-            logicalPlanList.add(logicalPlan);
-        }
-        return logicalPlanList;
+    @Override
+    public List<LogicalPlan> visitMultiStatements(MultiStatementsContext ctx) {
+        return visit(ctx.statement(), LogicalPlan.class);
     }
 
     /* ********************************************************************************************
      * Plan parsing
      * ******************************************************************************************** */
-    private LogicalPlan plan(ParserRuleContext tree) {
-        return (LogicalPlan) tree.accept(this);
-    }
-
     @Override
     public LogicalPlan visitQuery(QueryContext ctx) {
-        Supplier<LogicalPlan> f = () -> {
+        return ParserUtils.withOrigin(ctx, () -> {
             // TODO: need to add withQueryResultClauses and withCTE
             LogicalPlan query = plan(ctx.queryTerm());
-            LogicalPlan queryOrganization = withQueryOrganization(ctx.queryOrganization(), query);
+            LogicalPlan queryOrganization = withQueryOrganization(query, ctx.queryOrganization());
             return queryOrganization;
-        };
-        return ParserUtils.withOrigin(ctx, f);
-    }
-
-    private LogicalPlan withQueryOrganization(QueryOrganizationContext ctx, LogicalPlan children) {
-        List<OrderKey> orderKeys = visitQueryOrganization(ctx);
-        return orderKeys == null ? children : new LogicalUnaryPlan(new LogicalSort(orderKeys), children);
+        });
     }
 
     @Override
     public LogicalPlan visitRegularQuerySpecification(RegularQuerySpecificationContext ctx) {
-        Supplier<LogicalPlan> f = () -> {
+        return ParserUtils.withOrigin(ctx, () -> {
             // TODO: support on row relation
-            LogicalPlan from = visitFromClause(ctx.fromClause());
-            return withSelectQuerySpecification(ctx, ctx.selectClause(), ctx.whereClause(), from, ctx.aggClause());
-        };
-        return ParserUtils.withOrigin(ctx, f);
-    }
-
-    @Override
-    public Expression visitExpression(ExpressionContext ctx) {
-        Supplier<Expression> f = () -> (Expression) visit(ctx.booleanExpression());
-        return ParserUtils.withOrigin(ctx, f);
-    }
-
-    @Override
-    public List<Expression> visitNamedExpressionSeq(NamedExpressionSeqContext ctx) {
-        List<Expression> expressions = Lists.newArrayList();
-        if (ctx != null) {
-            for (NamedExpressionContext namedExpressionContext : ctx.namedExpression()) {
-                Expression expression = typedVisit(namedExpressionContext);
-                expressions.add(expression);
-            }
-        }
-        return expressions;
-    }
-
-    /**
-     * Add a regular (SELECT) query specification to a logical plan. The query specification
-     * is the core of the logical plan, this is where sourcing (FROM clause), projection (SELECT),
-     * aggregation (GROUP BY ... HAVING ...) and filtering (WHERE) takes place.
-     *
-     * <p>Note that query hints are ignored (both by the parser and the builder).
-     */
-    private LogicalPlan withSelectQuerySpecification(
-            ParserRuleContext ctx,
-            SelectClauseContext selectClause,
-            WhereClauseContext whereClause,
-            LogicalPlan relation,
-            AggClauseContext aggClause) {
-        Supplier<LogicalPlan> f = () -> {
-            //        Filter(expression(ctx.booleanExpression), plan);
-            LogicalPlan plan = visitCommonSelectQueryClausePlan(relation,
-                    visitNamedExpressionSeq(selectClause.namedExpressionSeq()), whereClause, aggClause);
-            // TODO: process hint
-            return plan;
-        };
-        return ParserUtils.withOrigin(ctx, f);
-    }
-
-    private LogicalPlan visitCommonSelectQueryClausePlan(
-            LogicalPlan relation,
-            List<Expression> expressions,
-            WhereClauseContext whereClause,
-            AggClauseContext aggClause) {
-        // TODO: add lateral views
-        // val withLateralView = lateralView.asScala.foldLeft(relation)(withGenerate)
-
-        // add where
-        LogicalPlan withFilter = relation.optionalMap(whereClause, withWhereClause);
-
-        List<NamedExpression> namedExpressions = expressions.stream().map(expression -> {
-            if (expression instanceof NamedExpression) {
-                return (NamedExpression) expression;
-            } else {
-                return new UnboundAlias(expression);
-            }
-        }).collect(Collectors.toList());
-
-        LogicalPlan withProject;
-        if (CollectionUtils.isNotEmpty(namedExpressions)) {
-            withProject = new LogicalUnaryPlan(new LogicalProject(namedExpressions), withFilter);
-        } else {
-            withProject = withFilter;
-        }
-
-        LogicalPlan withAgg;
-        if (aggClause != null) {
-            withAgg = withAggClause(namedExpressions, aggClause.groupByItem(), withFilter);
-        } else {
-            withAgg = withProject;
-        }
-
-        return withAgg;
-    }
-
-    @Override
-    public LogicalPlan visitFromClause(FromClauseContext ctx) {
-        LogicalPlan left = null;
-        for (RelationContext relation : ctx.relation()) {
-            LogicalPlan right = plan(relation.relationPrimary());
-            if (left == null) {
-                left = right;
-            } else {
-                left = new LogicalBinaryPlan(new LogicalJoin(JoinType.INNER_JOIN, Optional.empty()), left, right);
-            }
-            left = withJoinRelations(left, relation);
-        }
-        // TODO: pivot and lateral view
-        return left;
-    }
-
-    /**
-     * Join one more [[LogicalPlan]]s to the current logical plan.
-     */
-    private LogicalPlan withJoinRelations(LogicalPlan base, RelationContext ctx) {
-        LogicalPlan last = base;
-        for (JoinRelationContext join : ctx.joinRelation()) {
-            JoinType joinType;
-            if (join.joinType().LEFT() != null) {
-                joinType = JoinType.LEFT_OUTER_JOIN;
-            } else if (join.joinType().RIGHT() != null) {
-                joinType = JoinType.RIGHT_OUTER_JOIN;
-            } else if (join.joinType().FULL() != null) {
-                joinType = JoinType.FULL_OUTER_JOIN;
-            } else if (join.joinType().SEMI() != null) {
-                joinType = JoinType.LEFT_SEMI_JOIN;
-            } else if (join.joinType().ANTI() != null) {
-                joinType = JoinType.LEFT_ANTI_JOIN;
-            } else if (join.joinType().CROSS() != null) {
-                joinType = JoinType.CROSS_JOIN;
-            } else {
-                joinType = JoinType.INNER_JOIN;
-            }
-
-            // TODO: natural join, lateral join, using join
-            JoinCriteriaContext joinCriteria = join.joinCriteria();
-            Expression condition;
-            if (joinCriteria == null) {
-                condition = null;
-            } else {
-                condition = expression(joinCriteria.booleanExpression());
-            }
-
-            last = new LogicalBinaryPlan(new LogicalJoin(joinType, Optional.ofNullable(condition)), last,
-                    plan(join.relationPrimary()));
-        }
-        return last;
-    }
-
-    private LogicalPlan withAggClause(List<NamedExpression> aggExpressions, GroupByItemContext ctx,
-            LogicalPlan aggClause) {
-        List<Expression> tmpExpressions = new ArrayList<>();
-        for (ExpressionContext expressionCtx : ctx.expression()) {
-            tmpExpressions.add(typedVisit(expressionCtx));
-        }
-        return new LogicalUnaryPlan(new LogicalAggregation(tmpExpressions, aggExpressions), aggClause);
-    }
-
-    /**
-     * Generate OrderKey.
-     *
-     * @param ctx SortItemContext
-     * @return OrderKey
-     */
-    public OrderKey genOrderKeys(SortItemContext ctx) {
-        boolean isAsc = ctx.DESC() == null;
-        // TODO(wj): isNullFirst
-        boolean isNullFirst = true;
-        Expression expression = typedVisit(ctx.expression());
-        return new OrderKey(expression, isAsc, isNullFirst);
-    }
-
-    /**
-     * Create OrderKey list.
-     *
-     * @param ctx QueryOrganizationContext
-     * @return List of OrderKey
-     */
-    public List<OrderKey> visitQueryOrganization(QueryOrganizationContext ctx) {
-        List<OrderKey> orderKeys = new ArrayList<>();
-        if (ctx.sortClause().ORDER() != null) {
-            for (SortItemContext sortItemContext : ctx.sortClause().sortItem()) {
-                orderKeys.add(genOrderKeys(sortItemContext));
-            }
-            return new ArrayList<>(orderKeys);
-        } else {
-            return null;
-        }
+            LogicalPlan relation = withRelation(Optional.ofNullable(ctx.fromClause()));
+            return withSelectQuerySpecification(
+                ctx, relation,
+                ctx.selectClause(),
+                Optional.ofNullable(ctx.whereClause()),
+                Optional.ofNullable(ctx.aggClause())
+            );
+        });
     }
 
     /**
@@ -376,61 +175,25 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
         return new LogicalLeafPlan(relation);
     }
 
-    /**
-     * Create a Sequence of Strings for a parenthesis enclosed alias list.
-     */
-    @Override
-    public List<String> visitIdentifierList(IdentifierListContext ctx) {
-        return visitIdentifierSeq(ctx.identifierSeq());
-    }
-
-    /**
-     * Create a Sequence of Strings for an identifier list.
-     */
-    @Override
-    public List<String> visitIdentifierSeq(IdentifierSeqContext ctx) {
-        return ctx.ident.stream().map(RuleContext::getText).collect(Collectors.toList());
-    }
-
-    /* ********************************************************************************************
-     * Table Identifier parsing
-     * ******************************************************************************************** */
-
-    @Override
-    public List<String> visitMultipartIdentifier(MultipartIdentifierContext ctx) {
-        return ctx.parts.stream().map(RuleContext::getText).collect(Collectors.toList());
-    }
-
-    /* ********************************************************************************************
-     * Expression parsing
-     * ******************************************************************************************** */
-
-    /**
-     * Create an expression from the given context. This method just passes the context on to the
-     * visitor and only takes care of typing (We assume that the visitor returns an Expression here).
-     */
-    private Expression expression(ParserRuleContext ctx) {
-        return typedVisit(ctx);
-    }
-
     /**
      * Create a star (i.e. all) expression; this selects all elements (in the specified object).
      * Both un-targeted (global) and targeted aliases are supported.
      */
     @Override
     public Expression visitStar(StarContext ctx) {
-        Supplier<Expression> f = () -> {
+        return ParserUtils.withOrigin(ctx, () -> {
             final QualifiedNameContext qualifiedNameContext = ctx.qualifiedName();
             List<String> target;
             if (qualifiedNameContext != null) {
-                target = qualifiedNameContext.identifier().stream().map(RuleContext::getText)
-                        .collect(Collectors.toList());
+                target = qualifiedNameContext.identifier()
+                        .stream()
+                        .map(RuleContext::getText)
+                        .collect(ImmutableList.toImmutableList());
             } else {
                 target = Lists.newArrayList();
             }
             return new UnboundStar(target);
-        };
-        return ParserUtils.withOrigin(ctx, f);
+        });
     }
 
     /**
@@ -439,12 +202,14 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
      */
     @Override
     public Expression visitNamedExpression(NamedExpressionContext ctx) {
-        final Expression expression = expression(ctx.expression());
-        if (ctx.name != null) {
-            return new Alias(expression, ctx.name.getText());
-        } else {
-            return expression;
-        }
+        return ParserUtils.withOrigin(ctx, () -> {
+            Expression expression = getExpression(ctx.expression());
+            if (ctx.name != null) {
+                return new Alias(expression, ctx.name.getText());
+            } else {
+                return expression;
+            }
+        });
     }
 
     /**
@@ -460,27 +225,30 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
      */
     @Override
     public Expression visitComparison(ComparisonContext ctx) {
-        Expression left = expression(ctx.left);
-        Expression right = expression(ctx.right);
-        TerminalNode operator = (TerminalNode) ctx.comparisonOperator().getChild(0);
-        switch (operator.getSymbol().getType()) {
-            case DorisParser.EQ:
-                return new EqualTo(left, right);
-            case DorisParser.NEQ:
-                return new Not(new EqualTo(left, right));
-            case DorisParser.LT:
-                return new LessThan(left, right);
-            case DorisParser.GT:
-                return new GreaterThan(left, right);
-            case DorisParser.LTE:
-                return new LessThanEqual(left, right);
-            case DorisParser.GTE:
-                return new GreaterThanEqual(left, right);
-            case DorisParser.NSEQ:
-                return new NullSafeEqual(left, right);
-            default:
-                return null;
-        }
+        return ParserUtils.withOrigin(ctx, () -> {
+            Expression left = getExpression(ctx.left);
+            Expression right = getExpression(ctx.right);
+            TerminalNode operator = (TerminalNode) ctx.comparisonOperator().getChild(0);
+            switch (operator.getSymbol().getType()) {
+                case DorisParser.EQ:
+                    return new EqualTo(left, right);
+                case DorisParser.NEQ:
+                    return new Not(new EqualTo(left, right));
+                case DorisParser.LT:
+                    return new LessThan(left, right);
+                case DorisParser.GT:
+                    return new GreaterThan(left, right);
+                case DorisParser.LTE:
+                    return new LessThanEqual(left, right);
+                case DorisParser.GTE:
+                    return new GreaterThanEqual(left, right);
+                case DorisParser.NSEQ:
+                    return new NullSafeEqual(left, right);
+                default:
+                    throw new IllegalStateException("Unsupported comparison expression: "
+                        + operator.getSymbol().getText());
+            }
+        });
     }
 
     /**
@@ -492,23 +260,24 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
      */
     @Override
     public Expression visitLogicalNot(LogicalNotContext ctx) {
-        Expression child = expression(ctx.booleanExpression());
-        return new Not(child);
+        return ParserUtils.withOrigin(ctx, () -> new Not(getExpression(ctx.booleanExpression())));
     }
 
     @Override
     public Expression visitLogicalBinary(LogicalBinaryContext ctx) {
-        Expression left = expression(ctx.left);
-        Expression right = expression(ctx.right);
-
-        switch (ctx.operator.getType()) {
-            case DorisParser.AND:
-                return new And(left, right);
-            case DorisParser.OR:
-                return new Or(left, right);
-            default:
-                return null;
-        }
+        return ParserUtils.withOrigin(ctx, () -> {
+            Expression left = getExpression(ctx.left);
+            Expression right = getExpression(ctx.right);
+
+            switch (ctx.operator.getType()) {
+                case DorisParser.AND:
+                    return new And(left, right);
+                case DorisParser.OR:
+                    return new Or(left, right);
+                default:
+                    throw new IllegalStateException("Unsupported logical binary type: " + ctx.operator.getText());
+            }
+        });
     }
 
     /**
@@ -520,108 +289,80 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
      */
     @Override
     public Expression visitPredicated(PredicatedContext ctx) {
-        Expression e = expression(ctx.valueExpression());
-        // TODO: add predicate(is not null ...)
-        if (ctx.predicate() != null) {
-            return withPredicate(ctx.predicate(), e);
-        }
-        return e;
-    }
-
-    /**
-     * match predicate type and generate different predicates.
-     *
-     * @param ctx PredicateContext
-     * @param e Expression
-     * @return Expression
-     */
-    public Expression withPredicate(PredicateContext ctx, Expression e) {
-        switch (ctx.kind.getType()) {
-            case DorisParser.BETWEEN:
-                return withBetween(ctx, e);
-            default:
-                return null;
-        }
-    }
-
-    /**
-     * Generate between predicate.
-     *
-     * @param ctx PredicateContext
-     * @param e Expression
-     * @return Expression
-     */
-    public Expression withBetween(PredicateContext ctx, Expression e) {
-        boolean isNotBetween = ctx.NOT() != null ? true : false;
-        BetweenPredicate betweenPredicate = new BetweenPredicate(e, expression(ctx.lower), expression(ctx.upper));
-        return isNotBetween ? new Not(betweenPredicate) : betweenPredicate;
+        return ParserUtils.withOrigin(ctx, () -> {
+            Expression e = getExpression(ctx.valueExpression());
+            // TODO: add predicate(is not null ...)
+            return ctx.predicate() == null ? e : withPredicate(e, ctx.predicate());
+        });
     }
 
     @Override
     public Expression visitArithmeticUnary(ArithmeticUnaryContext ctx) {
-        Expression e = expression(ctx);
-        switch (ctx.operator.getType()) {
-            case DorisParser.PLUS:
-                return e;
-            case DorisParser.MINUS:
-                //TODO: Add single operator subtraction
-            default:
-                return null;
-        }
+        return ParserUtils.withOrigin(ctx, () -> {
+            Expression e = getExpression(ctx);
+            switch (ctx.operator.getType()) {
+                case DorisParser.PLUS:
+                    return e;
+                case DorisParser.MINUS:
+                    //TODO: Add single operator subtraction
+                default:
+                    throw new IllegalStateException("Unsupported arithmetic unary type: " + ctx.operator.getText());
+            }
+        });
     }
 
     @Override
     public Expression visitArithmeticBinary(ArithmeticBinaryContext ctx) {
-        Expression left = expression(ctx.left);
-        Expression right = expression(ctx.right);
-
-        return genArithmetic(ctx.operator, left, right);
-    }
-
-    private Arithmetic genArithmetic(Token token, Expression left, Expression right) {
-        switch (token.getType()) {
-            case DorisParser.ASTERISK:
-                return new Multiply(left, right);
-            case DorisParser.SLASH:
-                return new Divide(left, right);
-            case DorisParser.PERCENT:
-                return new Mod(left, right);
-            case DorisParser.PLUS:
-                return new Add(left, right);
-            case DorisParser.MINUS:
-                return new Subtract(left, right);
-            default:
-                return null;
-        }
+        return ParserUtils.withOrigin(ctx, () -> {
+            Expression left = getExpression(ctx.left);
+            Expression right = getExpression(ctx.right);
+
+            return ParserUtils.withOrigin(ctx, () -> {
+                switch (ctx.operator.getType()) {
+                    case DorisParser.ASTERISK:
+                        return new Multiply(left, right);
+                    case DorisParser.SLASH:
+                        return new Divide(left, right);
+                    case DorisParser.PERCENT:
+                        return new Mod(left, right);
+                    case DorisParser.PLUS:
+                        return new Add(left, right);
+                    case DorisParser.MINUS:
+                        return new Subtract(left, right);
+                    default:
+                        throw new IllegalStateException("Unsupported arithmetic binary type: "
+                            + ctx.operator.getText());
+                }
+            });
+        });
     }
 
     @Override
-    public Expression visitAggFunctions(AggFunctionsContext ctx) {
-        // TODO:In the future, instead of specifying the function name,
-        //      the function information is obtained by parsing the catalog. This method is more scalable.
-        String functionName = "";
-        if (ctx.aggFunction().SUM() != null) {
-            functionName = "sum";
-        } else if (ctx.aggFunction().AVG() != null) {
-            functionName = "avg";
-        }
-
-        return new FunctionCall(functionName,
-                new FunctionParams(ctx.aggFunction().DISTINCT() != null, expression(ctx.aggFunction().expression())));
+    public UnboundFunction visitFunctionCall(DorisParser.FunctionCallContext ctx) {
+        return ParserUtils.withOrigin(ctx, () -> {
+            // TODO:In the future, instead of specifying the function name,
+            //      the function information is obtained by parsing the catalog. This method is more scalable.
+            String functionName = ctx.identifier().getText();
+            boolean isDistinct = ctx.DISTINCT() != null;
+            List<Expression> params = visit(ctx.expression(), Expression.class);
+            return new UnboundFunction(functionName, isDistinct, params);
+        });
     }
 
     @Override
     public Expression visitDereference(DereferenceContext ctx) {
-        Expression e = expression(ctx.base);
-        if (e instanceof UnboundSlot) {
-            UnboundSlot unboundAttribute = (UnboundSlot) e;
-            List<String> nameParts = Lists.newArrayList(unboundAttribute.getNameParts());
-            nameParts.add(ctx.fieldName.getText());
-            return new UnboundSlot(nameParts);
-        } else {
-            // todo: base is an expression, may be not a table name.
-            return null;
-        }
+        return ParserUtils.withOrigin(ctx, () -> {
+            Expression e = getExpression(ctx.base);
+            if (e instanceof UnboundSlot) {
+                UnboundSlot unboundAttribute = (UnboundSlot) e;
+                List<String> nameParts = Lists.newArrayList(unboundAttribute.getNameParts());
+                nameParts.add(ctx.fieldName.getText());
+                return new UnboundSlot(nameParts);
+            } else {
+                // todo: base is an expression, may be not a table name.
+                throw new IllegalStateException("Unsupported dereference expression: " + ctx.getText());
+            }
+        });
     }
 
     @Override
@@ -653,7 +394,273 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
 
     @Override
     public Literal visitStringLiteral(StringLiteralContext ctx) {
-        String s = ctx.STRING().stream().map(ParseTree::getText).reduce((s1, s2) -> s1 + s2).orElse("");
+        String s = ctx.STRING().stream()
+                .map(ParseTree::getText)
+                .reduce((s1, s2) -> s1 + s2)
+                .orElse("");
         return new Literal(s);
     }
+
+    @Override
+    public Expression visitParenthesizedExpression(DorisParser.ParenthesizedExpressionContext ctx) {
+        return getExpression(ctx.expression());
+    }
+
+    @Override
+    public List<Expression> visitNamedExpressionSeq(NamedExpressionSeqContext namedCtx) {
+        return visit(namedCtx.namedExpression(), Expression.class);
+    }
+
+    /**
+     * Create OrderKey list.
+     *
+     * @param ctx QueryOrganizationContext
+     * @return List of OrderKey
+     */
+    @Override
+    public List<OrderKey> visitQueryOrganization(QueryOrganizationContext ctx) {
+        return ParserUtils.withOrigin(ctx, () -> {
+            if (ctx.sortClause().ORDER() != null) {
+                return visit(ctx.sortClause().sortItem(), OrderKey.class);
+            } else {
+                return ImmutableList.of();
+            }
+        });
+    }
+
+    @Override
+    public LogicalPlan visitFromClause(FromClauseContext ctx) {
+        return ParserUtils.withOrigin(ctx, () -> {
+            LogicalPlan left = null;
+            // build left deep join tree
+            for (RelationContext relation : ctx.relation()) {
+                LogicalPlan right = plan(relation.relationPrimary());
+                if (left == null) {
+                    left = right;
+                } else {
+                    LogicalJoin logicalJoin = new LogicalJoin(JoinType.INNER_JOIN, Optional.empty());
+                    left = new LogicalBinaryPlan(logicalJoin, left, right);
+                }
+                left = withJoinRelations(left, relation);
+            }
+            // TODO: pivot and lateral view
+            return left;
+        });
+    }
+
+    /* ********************************************************************************************
+     * Table Identifier parsing
+     * ******************************************************************************************** */
+
+    @Override
+    public List<String> visitMultipartIdentifier(MultipartIdentifierContext ctx) {
+        return ctx.parts.stream()
+            .map(RuleContext::getText)
+            .collect(ImmutableList.toImmutableList());
+    }
+
+    /**
+     * Create a Sequence of Strings for a parenthesis enclosed alias list.
+     */
+    @Override
+    public List<String> visitIdentifierList(IdentifierListContext ctx) {
+        return visitIdentifierSeq(ctx.identifierSeq());
+    }
+
+    /**
+     * Create a Sequence of Strings for an identifier list.
+     */
+    @Override
+    public List<String> visitIdentifierSeq(IdentifierSeqContext ctx) {
+        return ctx.ident.stream()
+            .map(RuleContext::getText)
+            .collect(ImmutableList.toImmutableList());
+    }
+
+    /**
+     * get OrderKey.
+     *
+     * @param ctx SortItemContext
+     * @return SortItems
+     */
+    @Override
+    public OrderKey visitSortItem(SortItemContext ctx) {
+        return ParserUtils.withOrigin(ctx, () -> {
+            boolean isAsc = ctx.DESC() == null;
+            // TODO(wj): isNullFirst
+            boolean isNullFirst = true;
+            Expression expression = typedVisit(ctx.expression());
+            return new OrderKey(expression, isAsc, isNullFirst);
+        });
+    }
+
+    private <T> List<T> visit(List<? extends ParserRuleContext> contexts, Class<T> clazz) {
+        return contexts.stream()
+            .map(this::visit)
+            .map(clazz::cast)
+            .collect(ImmutableList.toImmutableList());
+    }
+
+    private LogicalPlan plan(ParserRuleContext tree) {
+        return (LogicalPlan) tree.accept(this);
+    }
+
+    /* ********************************************************************************************
+     * Expression parsing
+     * ******************************************************************************************** */
+
+    /**
+     * Create an expression from the given context. This method just passes the context on to the
+     * visitor and only takes care of typing (We assume that the visitor returns an Expression here).
+     */
+    private Expression getExpression(ParserRuleContext ctx) {
+        return typedVisit(ctx);
+    }
+
+    private LogicalPlan withQueryOrganization(LogicalPlan children, QueryOrganizationContext ctx) {
+        List<OrderKey> orderKeys = visitQueryOrganization(ctx);
+        return orderKeys.isEmpty() ? children : new LogicalUnaryPlan(new LogicalSort(orderKeys), children);
+    }
+
+    /**
+     * Add a regular (SELECT) query specification to a logical plan. The query specification
+     * is the core of the logical plan, this is where sourcing (FROM clause), projection (SELECT),
+     * aggregation (GROUP BY ... HAVING ...) and filtering (WHERE) takes place.
+     *
+     * <p>Note that query hints are ignored (both by the parser and the builder).
+     */
+    private LogicalPlan withSelectQuerySpecification(
+            ParserRuleContext ctx,
+            LogicalPlan inputRelation,
+            SelectClauseContext selectClause,
+            Optional<WhereClauseContext> whereClause,
+            Optional<AggClauseContext> aggClause) {
+        return ParserUtils.withOrigin(ctx, () -> {
+            // TODO: process hint
+            // TODO: add lateral views
+
+            // from -> where -> group by -> having -> select
+
+            LogicalPlan filter = withFilter(inputRelation, whereClause);
+            LogicalPlan aggregate = withAggregate(filter, selectClause, aggClause);
+            // TODO: replace and process having at this position
+            LogicalPlan having = aggregate; // LogicalPlan having = withFilter(aggregate, havingClause);
+            LogicalPlan projection = withProjection(having, selectClause, aggClause);
+            return projection;
+        });
+    }
+
+    private LogicalPlan withRelation(Optional<FromClauseContext> ctx) {
+        if (ctx.isPresent()) {
+            return visitFromClause(ctx.get());
+        } else {
+            throw new IllegalStateException("Unsupported one row relation");
+        }
+    }
+
+    /**
+     * Join one more [[LogicalPlan]]s to the current logical plan.
+     */
+    private LogicalPlan withJoinRelations(LogicalPlan input, RelationContext ctx) {
+        LogicalPlan last = input;
+        for (JoinRelationContext join : ctx.joinRelation()) {
+            JoinType joinType;
+            if (join.joinType().LEFT() != null) {
+                joinType = JoinType.LEFT_OUTER_JOIN;
+            } else if (join.joinType().RIGHT() != null) {
+                joinType = JoinType.RIGHT_OUTER_JOIN;
+            } else if (join.joinType().FULL() != null) {
+                joinType = JoinType.FULL_OUTER_JOIN;
+            } else if (join.joinType().SEMI() != null) {
+                joinType = JoinType.LEFT_SEMI_JOIN;
+            } else if (join.joinType().ANTI() != null) {
+                joinType = JoinType.LEFT_ANTI_JOIN;
+            } else if (join.joinType().CROSS() != null) {
+                joinType = JoinType.CROSS_JOIN;
+            } else {
+                joinType = JoinType.INNER_JOIN;
+            }
+
+            // TODO: natural join, lateral join, using join
+            JoinCriteriaContext joinCriteria = join.joinCriteria();
+            Expression condition;
+            if (joinCriteria == null) {
+                condition = null;
+            } else {
+                condition = getExpression(joinCriteria.booleanExpression());
+            }
+
+            last = new LogicalBinaryPlan(
+                    new LogicalJoin(joinType, Optional.ofNullable(condition)),
+                    last, plan(join.relationPrimary())
+            );
+        }
+        return last;
+    }
+
+    private LogicalPlan withProjection(LogicalPlan input, SelectClauseContext selectCtx,
+                                       Optional<AggClauseContext> aggCtx) {
+        return ParserUtils.withOrigin(selectCtx, () -> {
+            // TODO: skip if havingClause exists
+            if (aggCtx.isPresent()) {
+                return input;
+            } else {
+                List<NamedExpression> projects = getNamedExpressions(selectCtx.namedExpressionSeq());
+                return new LogicalUnaryPlan(new LogicalProject(projects), input);
+            }
+        });
+    }
+
+    private LogicalPlan withFilter(LogicalPlan input, Optional<WhereClauseContext> whereCtx) {
+        return input.optionalMap(whereCtx, () ->
+            new LogicalUnaryPlan(new LogicalFilter(getExpression((whereCtx.get().booleanExpression()))), input)
+        );
+    }
+
+    private LogicalPlan withAggregate(LogicalPlan input, SelectClauseContext selectCtx,
+                                      Optional<AggClauseContext> aggCtx) {
+        return input.optionalMap(aggCtx, () -> {
+            List<Expression> groupByExpressions = visit(aggCtx.get().groupByItem().expression(), Expression.class);
+            List<NamedExpression> namedExpressions = getNamedExpressions(selectCtx.namedExpressionSeq());
+            return new LogicalUnaryPlan(new LogicalAggregate(groupByExpressions, namedExpressions), input);
+        });
+    }
+
+    /**
+     * match predicate type and generate different predicates.
+     *
+     * @param ctx PredicateContext
+     * @param valueExpression valueExpression
+     * @return Expression
+     */
+    private Expression withPredicate(Expression valueExpression, PredicateContext ctx) {
+        return ParserUtils.withOrigin(ctx, () -> {
+            switch (ctx.kind.getType()) {
+                case DorisParser.BETWEEN:
+                    boolean isNotBetween = ctx.NOT() != null ? true : false;
+                    Between between = new Between(
+                            valueExpression,
+                            getExpression(ctx.lower),
+                            getExpression(ctx.upper)
+                    );
+                    return isNotBetween ? new Not(between) : between;
+                default:
+                    throw new IllegalStateException("Unsupported predicate type: " + ctx.kind.getText());
+            }
+        });
+    }
+
+    private List<NamedExpression> getNamedExpressions(NamedExpressionSeqContext namedCtx) {
+        return ParserUtils.withOrigin(namedCtx, () -> {
+            List<Expression> expressions = visit(namedCtx.namedExpression(), Expression.class);
+            List<NamedExpression> namedExpressions = expressions.stream().map(expression -> {
+                if (expression instanceof NamedExpression) {
+                    return (NamedExpression) expression;
+                } else {
+                    return new UnboundAlias(expression);
+                }
+            }).collect(ImmutableList.toImmutableList());
+            return namedExpressions;
+        });
+    }
 }
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 bf8be9b626..33857257cc 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.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.collect.ImmutableList;
@@ -28,6 +29,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
  * Get all pattern matching subtree in query plan from a group expression.
@@ -102,9 +104,13 @@ public class GroupExpressionMatching implements Iterable<Plan> {
                     Group childGroup = groupExpression.child(i);
                     List<Plan> childrenPlan = matchingChildGroup(pattern, childGroup, i);
                     childrenPlans.add(childrenPlan);
+                    if (childrenPlan.isEmpty()) {
+                        // current pattern is match but children patterns not match
+                        return;
+                    }
                 }
 
-                assembleAllCombinationPlanTree(root, pattern, childrenPlans);
+                assembleAllCombinationPlanTree(root, pattern, groupExpression, childrenPlans);
             }
         }
 
@@ -129,6 +135,7 @@ public class GroupExpressionMatching implements Iterable<Plan> {
         }
 
         private void assembleAllCombinationPlanTree(Plan root, Pattern<Plan, Plan> rootPattern,
+                                                    GroupExpression groupExpression,
                                                     List<List<Plan>> childrenPlans) {
             int[] childrenPlanIndex = new int[childrenPlans.size()];
             int offset = 0;
@@ -139,11 +146,14 @@ public class GroupExpressionMatching implements Iterable<Plan> {
                 for (int i = 0; i < childrenPlans.size(); i++) {
                     children.add(childrenPlans.get(i).get(childrenPlanIndex[i]));
                 }
+
+                LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
                 // assemble children: replace GroupPlan to real plan,
                 // withChildren will erase groupExpression, so we must
                 // withGroupExpression too.
                 Plan rootWithChildren = root.withChildren(children)
-                        .withGroupExpression(root.getGroupExpression());
+                        .withGroupExpression(root.getGroupExpression())
+                        .withLogicalProperties(Optional.of(logicalProperties));
                 if (rootPattern.matchPredicates(rootWithChildren)) {
                     results.add(rootWithChildren);
                 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
index a2caa236c2..2e961f27ee 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java
@@ -29,11 +29,16 @@ public enum RuleType {
     BINDING_PROJECT_SLOT(RuleTypeClass.REWRITE),
     BINDING_FILTER_SLOT(RuleTypeClass.REWRITE),
     BINDING_JOIN_SLOT(RuleTypeClass.REWRITE),
-    BINDING_SENTINEL(RuleTypeClass.REWRITE),
+    BINDING_AGGREGATE_SLOT(RuleTypeClass.REWRITE),
+    BINDING_SORT_SLOT(RuleTypeClass.REWRITE),
+    BINDING_PROJECT_FUNCTION(RuleTypeClass.REWRITE),
+    BINDING_AGGREGATE_FUNCTION(RuleTypeClass.REWRITE),
+    RESOLVE_PROJECT_ALIAS(RuleTypeClass.REWRITE),
+    RESOLVE_AGGREGATE_ALIAS(RuleTypeClass.REWRITE),
+    PROJECT_TO_GLOBAL_AGGREGATE(RuleTypeClass.REWRITE),
 
     // rewrite rules
     COLUMN_PRUNE_PROJECTION(RuleTypeClass.REWRITE),
-    REWRITE_SENTINEL(RuleTypeClass.REWRITE),
 
     // exploration rules
     LOGICAL_JOIN_COMMUTATIVE(RuleTypeClass.EXPLORATION),
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java
new file mode 100644
index 0000000000..6d51d70a67
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java
@@ -0,0 +1,89 @@
+// 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.rules.analysis;
+
+import org.apache.doris.nereids.analyzer.UnboundFunction;
+import org.apache.doris.nereids.operators.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.expressions.DefaultExpressionRewriter;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.functions.Sum;
+import org.apache.doris.nereids.trees.plans.Plan;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * BindFunction.
+ */
+public class BindFunction implements AnalysisRuleFactory {
+    @Override
+    public List<Rule<Plan>> buildRules() {
+        return ImmutableList.of(
+            RuleType.BINDING_PROJECT_FUNCTION.build(
+                logicalProject().then(project -> {
+                    List<NamedExpression> boundExpr = bind(project.operator.getProjects());
+                    LogicalProject op = new LogicalProject(boundExpr);
+                    return plan(op, project.child());
+                })
+            ),
+            RuleType.BINDING_AGGREGATE_FUNCTION.build(
+                logicalAggregate().then(agg -> {
+                    List<Expression> groupBy = bind(agg.operator.getGroupByExprList());
+                    List<NamedExpression> output = bind(agg.operator.getOutputExpressionList());
+                    LogicalAggregate op = new LogicalAggregate(groupBy, output);
+                    return plan(op, agg.child());
+                })
+            )
+        );
+    }
+
+    private <E extends Expression> List<E> bind(List<E> exprList) {
+        return exprList.stream()
+            .map(expr -> FunctionBinder.INSTANCE.bind(expr))
+            .collect(Collectors.toList());
+    }
+
+    private static class FunctionBinder extends DefaultExpressionRewriter<Void> {
+        public static final FunctionBinder INSTANCE = new FunctionBinder();
+
+        public <E extends Expression> E bind(E expression) {
+            return (E) expression.accept(this, null);
+        }
+
+        @Override
+        public Expression visitUnboundFunction(UnboundFunction unboundFunction, Void context) {
+            String name = unboundFunction.getName();
+            // TODO: lookup function in the function registry
+            if (!name.equalsIgnoreCase("sum")) {
+                return unboundFunction;
+            }
+
+            List<Expression> arguments = unboundFunction.getArguments();
+            if (arguments.size() != 1) {
+                return unboundFunction;
+            }
+            return new Sum(unboundFunction.getArguments().get(0));
+        }
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
index d67b091061..a6cc5e9afb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
@@ -21,11 +21,15 @@ import org.apache.doris.nereids.analyzer.UnboundAlias;
 import org.apache.doris.nereids.analyzer.UnboundSlot;
 import org.apache.doris.nereids.analyzer.UnboundStar;
 import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.operators.plans.logical.LogicalAggregate;
 import org.apache.doris.nereids.operators.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
+import org.apache.doris.nereids.operators.plans.logical.LogicalSort;
+import org.apache.doris.nereids.properties.OrderKey;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.DefaultExpressionRewriter;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -33,13 +37,14 @@ import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
 
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * BindSlotReference.
@@ -50,50 +55,99 @@ public class BindSlotReference implements AnalysisRuleFactory {
         return ImmutableList.of(
             RuleType.BINDING_PROJECT_SLOT.build(
                 logicalProject().then(project -> {
-                    List<NamedExpression> boundSlots = bind(project.operator.getProjects(), project.children());
-                    return plan(new LogicalProject(boundSlots), project.child());
+                    List<NamedExpression> boundSlots =
+                            bind(project.operator.getProjects(), project.children(), project);
+                    return plan(new LogicalProject(flatBoundStar(boundSlots)), project.child());
                 })
             ),
             RuleType.BINDING_FILTER_SLOT.build(
                 logicalFilter().then(filter -> {
-                    Expression boundPredicates = bind(filter.operator.getPredicates(), filter.children());
+                    Expression boundPredicates = bind(
+                            filter.operator.getPredicates(), filter.children(), filter);
                     return plan(new LogicalFilter(boundPredicates), filter.child());
                 })
             ),
             RuleType.BINDING_JOIN_SLOT.build(
                 logicalJoin().then(join -> {
-                    Optional<Expression> cond = join.operator.getCondition().map(expr -> bind(expr, join.children()));
+                    Optional<Expression> cond = join.operator.getCondition()
+                            .map(expr -> bind(expr, join.children(), join));
                     LogicalJoin op = new LogicalJoin(join.operator.getJoinType(), cond);
                     return plan(op, join.left(), join.right());
                 })
+            ),
+            RuleType.BINDING_AGGREGATE_SLOT.build(
+                logicalAggregate().then(agg -> {
+                    List<Expression> groupBy = bind(
+                            agg.operator.getGroupByExprList(), agg.children(), agg);
+                    List<NamedExpression> output = bind(
+                            agg.operator.getOutputExpressionList(), agg.children(), agg);
+                    LogicalAggregate op = new LogicalAggregate(groupBy, output);
+                    return plan(op, agg.child());
+                })
+            ),
+            RuleType.BINDING_SORT_SLOT.build(
+                logicalSort().then(sort -> {
+                    List<OrderKey> sortItemList = sort.operator.getOrderKeys()
+                            .stream()
+                            .map(orderKey -> {
+                                Expression item = bind(orderKey.getExpr(), sort.children(), sort);
+                                return new OrderKey(item, orderKey.isAsc(), orderKey.isNullFirst());
+                            }).collect(Collectors.toList());
+
+                    LogicalSort op = new LogicalSort(sortItemList);
+                    return plan(op, sort.child());
+                })
             )
         );
     }
 
-    private <E extends Expression> List<E> bind(List<E> exprList, List<Plan> inputs) {
+    private List<NamedExpression> flatBoundStar(List<NamedExpression> boundSlots) {
+        return boundSlots
+            .stream()
+            .flatMap(slot -> {
+                if (slot instanceof BoundStar) {
+                    return ((BoundStar) slot).getSlots().stream();
+                } else {
+                    return Stream.of(slot);
+                }
+            }).collect(Collectors.toList());
+    }
+
+    private <E extends Expression> List<E> bind(List<E> exprList, List<Plan> inputs, Plan plan) {
         return exprList.stream()
-            .map(expr -> bind(expr, inputs))
+            .map(expr -> bind(expr, inputs, plan))
             .collect(Collectors.toList());
     }
 
-    private <E extends Expression> E bind(E expr, List<Plan> inputs) {
+    private <E extends Expression> E bind(E expr, List<Plan> inputs, Plan plan) {
         List<Slot> boundedSlots = inputs.stream()
                 .flatMap(input -> input.getOutput().stream())
                 .collect(Collectors.toList());
-        return (E) new SlotBinder(boundedSlots).visit(expr, null);
+        return (E) new SlotBinder(boundedSlots, plan).bind(expr);
     }
 
     private class SlotBinder extends DefaultExpressionRewriter<Void> {
         private List<Slot> boundSlots;
+        private Plan plan;
 
-        public SlotBinder(List<Slot> boundSlots) {
+        public SlotBinder(List<Slot> boundSlots, Plan plan) {
             this.boundSlots = boundSlots;
+            this.plan = plan;
+        }
+
+        public Expression bind(Expression expression) {
+            return expression.accept(this, null);
         }
 
         @Override
         public Expression visitUnboundAlias(UnboundAlias unboundAlias, Void context) {
-            Expression child = visit(unboundAlias.child(), context);
-            return new Alias(child, unboundAlias.getName());
+            Expression child = unboundAlias.child().accept(this, context);
+            if (child instanceof NamedExpression) {
+                return new Alias(child, ((NamedExpression) child).getName());
+            } else {
+                // TODO: resolve aliases
+                return new Alias(child, child.sql());
+            }
         }
 
         @Override
@@ -114,35 +168,62 @@ public class BindSlotReference implements AnalysisRuleFactory {
 
         @Override
         public Expression visitUnboundStar(UnboundStar unboundStar, Void context) {
+            if (!(plan instanceof LogicalProject)) {
+                throw new AnalysisException("UnboundStar must exists in Projection");
+            }
             List<String> qualifier = unboundStar.getQualifier();
-            List<Slot> boundSlots = Lists.newArrayList();
             switch (qualifier.size()) {
                 case 0: // select *
-                    boundSlots.addAll(boundSlots);
-                    break;
+                    return new BoundStar(boundSlots);
                 case 1: // select table.*
-                    analyzeBoundSlots(qualifier, context);
-                    break;
                 case 2: // select db.table.*
-                    analyzeBoundSlots(qualifier, context);
-                    break;
+                    return bindQualifiedStar(qualifier, context);
                 default:
                     throw new AnalysisException("Not supported qualifier: "
                         + StringUtils.join(qualifier, "."));
             }
-            return null;
         }
 
-        private void analyzeBoundSlots(List<String> qualifier, Void context) {
-            this.boundSlots.stream()
-                    .forEach(slot ->
-                        boundSlots.add(visitUnboundSlot(new UnboundSlot(
-                            ImmutableList.<String>builder()
-                                .addAll(qualifier)
-                                .add(slot.getName())
-                                .build()
-                        ), context))
-                    );
+        private BoundStar bindQualifiedStar(List<String> qualifierStar, Void context) {
+            // FIXME: compatible with previous behavior:
+            // https://github.com/apache/doris/pull/10415/files/3fe9cb0c3f805ab3a9678033b281b16ad93ec60a#r910239452
+            List<Slot> slots = boundSlots.stream().filter(boundSlot -> {
+                switch (qualifierStar.size()) {
+                    // table.*
+                    case 1:
+                        List<String> boundSlotQualifier = boundSlot.getQualifier();
+                        switch (boundSlotQualifier.size()) {
+                            // bound slot is `column` and no qualified
+                            case 0: return false;
+                            case 1: // bound slot is `table`.`column`
+                                return qualifierStar.get(0).equalsIgnoreCase(boundSlotQualifier.get(0));
+                            case 2:// bound slot is `db`.`table`.`column`
+                                return qualifierStar.get(0).equalsIgnoreCase(boundSlotQualifier.get(1));
+                            default:
+                                throw new AnalysisException("Not supported qualifier: "
+                                    + StringUtils.join(qualifierStar, "."));
+                        }
+                    case 2: // db.table.*
+                        boundSlotQualifier = boundSlot.getQualifier();
+                        switch (boundSlotQualifier.size()) {
+                            // bound slot is `column` and no qualified
+                            case 0:
+                            case 1: // bound slot is `table`.`column`
+                                return false;
+                            case 2:// bound slot is `db`.`table`.`column`
+                                return qualifierStar.get(0).equalsIgnoreCase(boundSlotQualifier.get(0))
+                                        && qualifierStar.get(1).equalsIgnoreCase(boundSlotQualifier.get(1));
+                            default:
+                                throw new AnalysisException("Not supported qualifier: "
+                                    + StringUtils.join(qualifierStar, ".") + ".*");
+                        }
+                    default:
+                        throw new AnalysisException("Not supported name: "
+                            + StringUtils.join(qualifierStar, ".") + ".*");
+                }
+            }).collect(Collectors.toList());
+
+            return new BoundStar(slots);
         }
 
         private List<Slot> bindSlot(UnboundSlot unboundSlot, List<Slot> boundSlots) {
@@ -175,4 +256,18 @@ public class BindSlotReference implements AnalysisRuleFactory {
             }).collect(Collectors.toList());
         }
     }
+
+    /** BoundStar is used to wrap list of slots for temporary. */
+    private class BoundStar extends NamedExpression {
+        public BoundStar(List<Slot> children) {
+            super(NodeType.BOUND_STAR, children.toArray(new Slot[0]));
+            Preconditions.checkArgument(children.stream().allMatch(slot -> !(slot instanceof UnboundSlot)),
+                    "BoundStar can not wrap UnboundSlot"
+            );
+        }
+
+        public List<Slot> getSlots() {
+            return (List) children();
+        }
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ProjectToGlobalAggregate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ProjectToGlobalAggregate.java
new file mode 100644
index 0000000000..6d02101588
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ProjectToGlobalAggregate.java
@@ -0,0 +1,53 @@
+// 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.rules.analysis;
+
+import org.apache.doris.nereids.operators.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.functions.AggregateFunction;
+import org.apache.doris.nereids.trees.plans.Plan;
+
+import com.google.common.collect.ImmutableList;
+
+/** ProjectToGlobalAggregate. */
+public class ProjectToGlobalAggregate extends OneAnalysisRuleFactory {
+    @Override
+    public Rule<Plan> build() {
+        return RuleType.PROJECT_TO_GLOBAL_AGGREGATE.build(
+           logicalProject().then(project -> {
+               boolean needGlobalAggregate = project.operator.getProjects()
+                       .stream()
+                       .anyMatch(this::hasNonWindowedAggregateFunction);
+
+               if (needGlobalAggregate) {
+                   LogicalAggregate op = new LogicalAggregate(ImmutableList.of(), project.operator.getProjects());
+                   return plan(op, project.child());
+               } else {
+                   return project;
+               }
+           })
+        );
+    }
+
+    private boolean hasNonWindowedAggregateFunction(Expression expression) {
+        // TODO: exclude windowed aggregate function
+        return expression.anyMatch(AggregateFunction.class::isInstance);
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/AbstractExpressionRewriteRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/AbstractExpressionRewriteRule.java
index c7833d3bc5..f17fb1430c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/AbstractExpressionRewriteRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/AbstractExpressionRewriteRule.java
@@ -30,9 +30,4 @@ public abstract class AbstractExpressionRewriteRule extends DefaultExpressionRew
     public Expression rewrite(Expression expr, ExpressionRewriteContext ctx) {
         return expr.accept(this, ctx);
     }
-
-    @Override
-    public Expression visit(Expression expr, ExpressionRewriteContext ctx) {
-        return expr;
-    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java
index f8025e8f39..4b7bb6fd1e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java
@@ -44,7 +44,6 @@ public class SimplifyNotExprRule extends AbstractExpressionRewriteRule {
 
     public static SimplifyNotExprRule INSTANCE = new SimplifyNotExprRule();
 
-
     @Override
     public Expression visitNot(Not expr, ExpressionRewriteContext context) {
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAggToPhysicalHashAgg.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAggToPhysicalHashAgg.java
index 5ea7138473..a0b1a0d902 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAggToPhysicalHashAgg.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAggToPhysicalHashAgg.java
@@ -28,7 +28,7 @@ import org.apache.doris.nereids.trees.plans.Plan;
 public class LogicalAggToPhysicalHashAgg extends OneImplementationRuleFactory {
     @Override
     public Rule<Plan> build() {
-        return logicalAggregation().then(agg -> plan(
+        return logicalAggregate().then(agg -> plan(
             new PhysicalAggregation(
                 // TODO: for use a function to judge whether use stream
                 agg.getOperator().getGroupByExprList(),
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java
index 50a1c1858b..453d6f6bfd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java
@@ -29,9 +29,12 @@ public enum NodeType {
 
     // expressions
     EXPRESSION,
+    UNBOUND_FUNCTION,
     UNBOUND_ALIAS,
     UNBOUND_SLOT,
     UNBOUND_STAR,
+    BOUND_STAR,
+    BOUND_FUNCTION,
     LITERAL,
     SLOT_REFERENCE,
     COMPARISON_PREDICATE,
@@ -58,7 +61,7 @@ public enum NodeType {
     BITXOR,
     BITNOT,
     FACTORIAL,
-    FUNCTIONCALL,
+    FUNCTION_CALL,
 
     // pattern
     PATTERN
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TernaryNode.java
similarity index 56%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TernaryNode.java
index 78f1ba3491..d598d604cd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TernaryNode.java
@@ -15,31 +15,32 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.expressions;
-
-import org.apache.doris.nereids.trees.NodeType;
+package org.apache.doris.nereids.trees;
 
 /**
- * Abstract class for all slot in expression.
+ * interface for all tree node that have two children.
  */
-public abstract class Slot extends NamedExpression implements LeafExpression {
-    private ExprId id;
+public interface TernaryNode<
+            NODE_TYPE extends TreeNode<NODE_TYPE>,
+            FIRST_CHILD_TYPE extends TreeNode,
+            SECOND_CHILD_TYPE extends TreeNode,
+            THIRD_CHILD_TYPE extends TreeNode>
+        extends TreeNode<NODE_TYPE> {
 
-    public Slot(NodeType type, ExprId id, Expression... children) {
-        super(type, children);
-        this.id = id;
+    default FIRST_CHILD_TYPE first() {
+        return (FIRST_CHILD_TYPE) child(0);
     }
 
-    public Slot(NodeType type) {
-        super(type);
+    default SECOND_CHILD_TYPE second() {
+        return (SECOND_CHILD_TYPE) child(1);
     }
 
-    @Override
-    public Slot toSlot() {
-        return this;
+    default THIRD_CHILD_TYPE third() {
+        return (THIRD_CHILD_TYPE) child(2);
     }
 
-    public ExprId getId() {
-        return id;
+    @Override
+    default int arity() {
+        return 3;
     }
 }
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 1f1a3363c1..e5de2d26b9 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
@@ -22,6 +22,8 @@ import org.apache.doris.nereids.operators.Operator;
 
 import java.util.List;
 import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /**
  * interface for all node in Nereids, include plan node and expression.
@@ -46,4 +48,31 @@ public interface TreeNode<NODE_TYPE extends TreeNode<NODE_TYPE>> {
 
     NODE_TYPE withChildren(List<NODE_TYPE> children);
 
+    /**
+     * Foreach treeNode. Top-down traverse implicitly.
+     * @param func foreach function
+     */
+    default void foreach(Consumer<TreeNode<NODE_TYPE>> func) {
+        func.accept(this);
+        for (NODE_TYPE child : children()) {
+            child.foreach(func);
+        }
+    }
+
+    /**
+     * iterate top down and test predicate if any matched. Top-down traverse implicitly.
+     * @param predicate predicate
+     * @return true if any predicate return true
+     */
+    default boolean anyMatch(Predicate<TreeNode<NODE_TYPE>> predicate) {
+        if (predicate.test(this)) {
+            return true;
+        }
+        for (NODE_TYPE child : children()) {
+            if (child.anyMatch(predicate)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/analysis/FunctionParams.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/analysis/FunctionParams.java
deleted file mode 100644
index 1830568f02..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/analysis/FunctionParams.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// 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.trees.analysis;
-
-import org.apache.doris.nereids.trees.expressions.Expression;
-
-import com.google.common.collect.Lists;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Function's Params.
- */
-public class FunctionParams {
-    private boolean isStar;
-    private List<Expression> expression;
-    private boolean isDistinct;
-
-    /**
-     * Constructor for FunctionParams.
-     */
-    public FunctionParams(boolean isDistinct, List<Expression> exprs) {
-        isStar = false;
-        this.isDistinct = isDistinct;
-        this.expression = exprs;
-    }
-
-    public FunctionParams(boolean isDistinct, Expression expression) {
-        this(isDistinct, Lists.newArrayList(expression));
-    }
-
-
-    // c'tor for non-star, non-distinct params
-    public FunctionParams(Expression exprs) {
-        this(false, exprs);
-    }
-
-    // c'tor for <agg>(*)
-    private FunctionParams() {
-        expression = null;
-        isStar = true;
-        isDistinct = false;
-    }
-
-    public List<Expression> getExpressionList() {
-        return expression;
-    }
-
-    public boolean isStar() {
-        return isStar;
-    }
-
-    public boolean isDistinct() {
-        return isDistinct;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 31 * Boolean.hashCode(isStar) + Boolean.hashCode(isDistinct);
-        if (expression != null) {
-            for (Expression expr : expression) {
-                result = 31 * result + Objects.hashCode(expr);
-            }
-        }
-        return result;
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java
index db20391bd4..27bf07a430 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Add.java
@@ -17,6 +17,10 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
 /**
  * Add Expression.
  */
@@ -28,7 +32,20 @@ public class Add<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE extends Ex
 
     @Override
     public String sql() {
-        return left().sql() + ' ' + getArithOperator().toString()
+        return left().sql() + ' ' + getArithmeticOperator().toString()
                 + ' ' + right().sql();
     }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new Add<>(children.get(0), children.get(1));
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitAdd(this, context);
+    }
+
+
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java
index 425c3b0bc9..dacf7146e0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java
@@ -19,8 +19,10 @@ package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.types.DataType;
 
-import com.google.common.collect.Lists;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 
 import java.util.List;
 
@@ -42,9 +44,9 @@ public class Alias<CHILD_TYPE extends Expression> extends NamedExpression
      */
     public Alias(CHILD_TYPE child, String name) {
         super(NodeType.ALIAS, child);
-        exprId = NamedExpressionUtil.newExprId();
+        this.exprId = NamedExpressionUtil.newExprId();
         this.name = name;
-        qualifier = Lists.newArrayList();
+        this.qualifier = ImmutableList.of();
     }
 
     @Override
@@ -67,13 +69,29 @@ public class Alias<CHILD_TYPE extends Expression> extends NamedExpression
         return new SlotReference(exprId, name, child().getDataType(), child().nullable(), qualifier);
     }
 
+    @Override
+    public DataType getDataType() throws UnboundException {
+        return child().getDataType();
+    }
+
     @Override
     public String sql() {
         return null;
     }
 
+    @Override
+    public boolean nullable() throws UnboundException {
+        return child().nullable();
+    }
+
     @Override
     public String toString() {
         return child().toString() + " AS " + name;
     }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new Alias<>(children.get(0), name);
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java
index 3da0626bad..9fcafe49b4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java
@@ -19,6 +19,10 @@ package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.trees.NodeType;
 
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
 /**
  * And predicate expression.
  */
@@ -33,4 +37,10 @@ public class And<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE extends Ex
     public And(LEFT_CHILD_TYPE left, RIGHT_CHILD_TYPE right) {
         super(NodeType.AND, left, right);
     }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new And<>(children.get(0), children.get(1));
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java
index ae0724663c..b4f55c7cd5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java
@@ -21,11 +21,14 @@ package org.apache.doris.nereids.trees.expressions;
 import org.apache.doris.analysis.ArithmeticExpr;
 import org.apache.doris.analysis.ArithmeticExpr.Operator;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.types.DataType;
+
+import java.util.Objects;
 
 /**
  * All arithmetic operator.
  */
-public class Arithmetic extends Expression {
+public abstract class Arithmetic extends Expression {
 
     enum OperatorPosition {
         BINARY_INFIX,
@@ -107,7 +110,7 @@ public class Arithmetic extends Expression {
         this.op = op;
     }
 
-    public ArithmeticOperator getArithOperator() {
+    public ArithmeticOperator getArithmeticOperator() {
         return op;
     }
 
@@ -141,7 +144,38 @@ public class Arithmetic extends Expression {
     }
 
     @Override
-    public String sql() {
-        return null;
+    public DataType getDataType() {
+        // TODO: split Unary and Binary arithmetic
+        int arity = arity();
+        if (arity == 1) {
+            return child(0).getDataType();
+        } else if (arity == 2) {
+            // TODO: binary arithmetic
+            return child(0).getDataType();
+        } else {
+            return super.getDataType();
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        Arithmetic that = (Arithmetic) o;
+        return op == that.op;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(op);
+    }
+
+    @Override
+    public String toString() {
+        return sql();
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BetweenPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java
similarity index 57%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BetweenPredicate.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java
index e4c875b0ef..882622387c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BetweenPredicate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java
@@ -22,10 +22,19 @@ import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DataType;
 
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+import java.util.Objects;
+
 /**
  * Between predicate expression.
  */
-public class BetweenPredicate extends Expression {
+public class Between<
+            FIRST_CHILD_TYPE extends Expression,
+            SECOND_CHILD_TYPE extends Expression,
+            THIRD_CHILD_TYPE extends Expression>
+        extends Expression implements TernaryExpression<FIRST_CHILD_TYPE, SECOND_CHILD_TYPE, THIRD_CHILD_TYPE> {
 
     private Expression compareExpr;
     private Expression lowerBound;
@@ -38,8 +47,8 @@ public class BetweenPredicate extends Expression {
      * @param upperBound     right child of between predicate
      */
 
-    public BetweenPredicate(Expression compareExpr, Expression lowerBound,
-            Expression upperBound) {
+    public Between(Expression compareExpr, Expression lowerBound,
+                   Expression upperBound) {
         super(NodeType.BETWEEN, compareExpr, lowerBound, upperBound);
         this.compareExpr = compareExpr;
         this.lowerBound = lowerBound;
@@ -53,13 +62,16 @@ public class BetweenPredicate extends Expression {
 
     @Override
     public String sql() {
-        return compareExpr.sql()
-                + " BETWEEN "
-                + lowerBound.sql() + " AND " + upperBound.sql();
+        return compareExpr.sql() + " BETWEEN " + lowerBound.sql() + " AND " + upperBound.sql();
+    }
+
+    @Override
+    public String toString() {
+        return compareExpr + " BETWEEN " + lowerBound + " AND " + upperBound;
     }
 
     public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
-        return visitor.visitBetweenPredicate(this, context);
+        return visitor.visitBetween(this, context);
     }
 
     public Expression getCompareExpr() {
@@ -74,4 +86,28 @@ public class BetweenPredicate extends Expression {
         return upperBound;
     }
 
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 3);
+        return new Between<>(children.get(0), children.get(1), children.get(2));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        Between<?, ?, ?> between = (Between<?, ?, ?>) o;
+        return Objects.equals(compareExpr, between.compareExpr)
+                && Objects.equals(lowerBound, between.lowerBound)
+                && Objects.equals(upperBound, between.upperBound);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(compareExpr, lowerBound, upperBound);
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java
index a0b495baf8..f00c59039f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java
@@ -73,30 +73,4 @@ public abstract class ComparisonPredicate<LEFT_CHILD_TYPE extends Expression, RI
         return (type == other.getType()) && Objects.equals(left(), other.left())
                 && Objects.equals(right(), other.right());
     }
-
-    /**
-     * create new ComparisonPredicate with new children.
-     *
-     * @param left left child
-     * @param right right child
-     * @return Corresponding comparisonPredicate child class.
-     */
-    public ComparisonPredicate withChildren(Expression left, Expression right) {
-        switch (type) {
-            case EQUAL_TO:
-                return new EqualTo(left, right);
-            case GREATER_THAN:
-                return new GreaterThan(left, right);
-            case GREATER_THAN_EQUAL:
-                return new GreaterThanEqual(left, right);
-            case LESS_THAN:
-                return new LessThan(left, right);
-            case LESS_THAN_EQUAL:
-                return new LessThanEqual(left, right);
-            case NULL_SAFE_EQUAL:
-                return new NullSafeEqual(left, right);
-            default:
-                throw new IllegalStateException("Invalid type for ComparisonPredicate: " + type);
-        }
-    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java
index fbb3faa50e..f09e1ae4f6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java
@@ -40,9 +40,16 @@ public class CompoundPredicate<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_T
     @Override
     public String sql() {
         String nodeType = getType().toString();
-        return left().sql() + ' ' + nodeType + ' ' + right().sql();
+        return "(" + left().sql() + " " + nodeType + " " + right().sql() + ")";
     }
 
+    @Override
+    public String toString() {
+        String nodeType = getType().toString();
+        return nodeType + "(" + left() + ", " + right() + ")";
+    }
+
+    @Override
     public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
         return visitor.visitCompoundPredicate(this, context);
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DefaultExpressionRewriter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DefaultExpressionRewriter.java
index 7e868cb10f..b46a1d21a8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DefaultExpressionRewriter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DefaultExpressionRewriter.java
@@ -17,11 +17,8 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
-import org.apache.doris.nereids.analyzer.UnboundAlias;
-import org.apache.doris.nereids.analyzer.UnboundSlot;
-import org.apache.doris.nereids.analyzer.UnboundStar;
-
-import javax.ws.rs.NotSupportedException;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Default implementation for expression rewriting, delegating to child expressions and rewrite current root
@@ -31,102 +28,15 @@ public abstract class DefaultExpressionRewriter<C> extends ExpressionVisitor<Exp
 
     @Override
     public Expression visit(Expression expr, C context) {
-        return expr.accept(this, context);
-    }
-
-    @Override
-    public Expression visitAlias(Alias alias, C context) {
-        Expression child = visit(alias.child(), context);
-        if (child != alias.child()) {
-            return new Alias(child, alias.getName());
-        } else {
-            return alias;
+        List<Expression> newChildren = new ArrayList<>();
+        boolean hasNewChildren = false;
+        for (Expression child : expr.children()) {
+            Expression newChild = child.accept(this, context);
+            if (newChild != child) {
+                hasNewChildren = true;
+            }
+            newChildren.add(newChild);
         }
-    }
-
-    @Override
-    public Expression visitComparisonPredicate(ComparisonPredicate cp, C context) {
-        Expression left = visit(cp.left(), context);
-        Expression right = visit(cp.right(), context);
-        if (left != cp.left() || right != cp.right()) {
-            return cp.withChildren(left, right);
-        } else {
-            return cp;
-        }
-    }
-
-    @Override
-    public Expression visitEqualTo(EqualTo equalTo, C context) {
-        return visitComparisonPredicate(equalTo, context);
-    }
-
-    @Override
-    public Expression visitGreaterThan(GreaterThan greaterThan, C context) {
-        return visitComparisonPredicate(greaterThan, context);
-    }
-
-    @Override
-    public Expression visitGreaterThanEqual(GreaterThanEqual greaterThanEqual, C context) {
-        return visitComparisonPredicate(greaterThanEqual, context);
-    }
-
-    @Override
-    public Expression visitLessThan(LessThan lessThan, C context) {
-        return visitComparisonPredicate(lessThan, context);
-    }
-
-    @Override
-    public Expression visitLessThanEqual(LessThanEqual lessThanEqual, C context) {
-        return visitComparisonPredicate(lessThanEqual, context);
-    }
-
-    @Override
-    public Expression visitNullSafeEqual(NullSafeEqual nullSafeEqual, C context) {
-        return visitComparisonPredicate(nullSafeEqual, context);
-    }
-
-    @Override
-    public Expression visitNamedExpression(NamedExpression namedExpression, C context) {
-        throw new NotSupportedException("Not supported for rewrite abstract class NamedExpression.");
-    }
-
-    @Override
-    public Expression visitNot(Not not, C context) {
-        Expression child = visit(not.child(), context);
-        if (child != not.child()) {
-            return new Not(child);
-        } else {
-            return not;
-        }
-    }
-
-    @Override
-    public Expression visitSlotReference(SlotReference slotReference, C context) {
-        return slotReference;
-    }
-
-    @Override
-    public Expression visitLiteral(Literal literal, C context) {
-        return literal;
-    }
-
-    @Override
-    public Expression visitUnboundAlias(UnboundAlias unboundAlias, C context) {
-        Expression child = visit(unboundAlias.child(), context);
-        if (child != unboundAlias.child()) {
-            return new UnboundAlias(child);
-        } else {
-            return unboundAlias;
-        }
-    }
-
-    @Override
-    public Expression visitUnboundSlot(UnboundSlot unboundSlot, C context) {
-        return unboundSlot;
-    }
-
-    @Override
-    public Expression visitUnboundStar(UnboundStar unboundStar, C context) {
-        return unboundStar;
+        return hasNewChildren ? expr.withChildren(newChildren) : expr;
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DefaultExpressionVisitor.java
similarity index 69%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DefaultExpressionVisitor.java
index fc69063abe..c94ead46e8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/DefaultExpressionVisitor.java
@@ -15,18 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.types;
-
-import org.apache.doris.catalog.Type;
+package org.apache.doris.nereids.trees.expressions;
 
 /**
- * Double data type in Nereids.
+ * Use the visitor to iterate over all expressions for expression.
  */
-public class DoubleType extends FractionalType {
-    public static IntegerType INSTANCE = new IntegerType();
-
+public class DefaultExpressionVisitor<R, C> extends ExpressionVisitor<R, C> {
     @Override
-    public Type toCatalogDataType() {
-        return Type.DOUBLE;
+    public R visit(Expression expr, C context) {
+        for (Expression child : expr.children()) {
+            child.accept(this, context);
+        }
+        return null;
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Divide.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Divide.java
index e921557d09..9486843f8e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Divide.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Divide.java
@@ -17,6 +17,10 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
 /**
  * Divide Expression.
  */
@@ -28,7 +32,18 @@ public class Divide<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE extends
 
     @Override
     public String sql() {
-        return left().sql() + ' ' + getArithOperator().toString()
+        return left().sql() + ' ' + getArithmeticOperator().toString()
                 + ' ' + right().sql();
     }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new Divide<>(children.get(0), children.get(1));
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitDivide(this, context);
+    }
 }
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 53b6aea8ee..a16638326e 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
@@ -26,6 +26,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Abstract class for all Expression in Nereids.
@@ -51,18 +52,17 @@ public abstract class Expression extends AbstractTreeNode<Expression> {
     }
 
     public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
-        logger.warn("accept() is not implemented by " + this.getClass());
         return visitor.visit(this, context);
     }
 
     @Override
     public List<Expression> children() {
-        return (List) children;
+        return children;
     }
 
     @Override
     public Expression child(int index) {
-        return (Expression) children.get(index);
+        return children.get(index);
     }
 
     @Override
@@ -82,4 +82,15 @@ public abstract class Expression extends AbstractTreeNode<Expression> {
         return false;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        Expression that = (Expression) o;
+        return Objects.equals(children(), that.children());
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionConverter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionConverter.java
index f2a7d771ae..e07eb9344f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionConverter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionConverter.java
@@ -29,6 +29,7 @@ import org.apache.doris.analysis.NullLiteral;
 import org.apache.doris.analysis.StringLiteral;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.functions.BoundFunction;
 import org.apache.doris.nereids.trees.plans.PlanTranslatorContext;
 import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DataType;
@@ -44,7 +45,7 @@ import java.util.List;
  * Used to convert expression of new optimizer to stale expr.
  */
 @SuppressWarnings("rawtypes")
-public class ExpressionConverter extends ExpressionVisitor<Expr, PlanTranslatorContext> {
+public class ExpressionConverter extends DefaultExpressionVisitor<Expr, PlanTranslatorContext> {
 
     public static ExpressionConverter converter = new ExpressionConverter();
 
@@ -52,11 +53,6 @@ public class ExpressionConverter extends ExpressionVisitor<Expr, PlanTranslatorC
         return converter.visit(expression, planContext);
     }
 
-    @Override
-    public Expr visit(Expression expr, PlanTranslatorContext context) {
-        return expr.accept(this, context);
-    }
-
     @Override
     public Expr visitSlotReference(SlotReference slotReference, PlanTranslatorContext context) {
         return context.findExpr(slotReference);
@@ -134,16 +130,16 @@ public class ExpressionConverter extends ExpressionVisitor<Expr, PlanTranslatorC
 
     // TODO: Supports for `distinct`
     @Override
-    public Expr visitFunctionCall(FunctionCall function, PlanTranslatorContext context) {
+    public Expr visitBoundFunction(BoundFunction function, PlanTranslatorContext context) {
         List<Expr> paramList = new ArrayList<>();
-        for (Expression expr : function.getFnParams().getExpressionList()) {
+        for (Expression expr : function.getArguments()) {
             paramList.add(visit(expr, context));
         }
-        return new FunctionCallExpr(function.getFnName().toString(), paramList);
+        return new FunctionCallExpr(function.getName(), paramList);
     }
 
     @Override
-    public Expr visitBetweenPredicate(BetweenPredicate betweenPredicate, PlanTranslatorContext context) {
+    public Expr visitBetween(Between between, PlanTranslatorContext context) {
         throw new RuntimeException("Unexpected invocation");
     }
 
@@ -171,7 +167,7 @@ public class ExpressionConverter extends ExpressionVisitor<Expr, PlanTranslatorC
 
     @Override
     public Expr visitArithmetic(Arithmetic arithmetic, PlanTranslatorContext context) {
-        Arithmetic.ArithmeticOperator arithmeticOperator = arithmetic.getArithOperator();
+        Arithmetic.ArithmeticOperator arithmeticOperator = arithmetic.getArithmeticOperator();
         return new ArithmeticExpr(arithmeticOperator.getStaleOp(),
                 visit(arithmetic.child(0), context),
                 arithmeticOperator.isBinary() ? visit(arithmetic.child(1), context) : null);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionVisitor.java
index 943762537f..5a6c6517a9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionVisitor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionVisitor.java
@@ -18,19 +18,21 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.analyzer.UnboundAlias;
+import org.apache.doris.nereids.analyzer.UnboundFunction;
 import org.apache.doris.nereids.analyzer.UnboundSlot;
 import org.apache.doris.nereids.analyzer.UnboundStar;
+import org.apache.doris.nereids.trees.expressions.functions.AggregateFunction;
+import org.apache.doris.nereids.trees.expressions.functions.BoundFunction;
 
 /**
- * Use the visitor pattern to iterate over all expressions for expression rewriting.
+ * Use the visitor to visit expression and forward to unified method(visitExpression).
  */
 public abstract class ExpressionVisitor<R, C> {
 
     public abstract R visit(Expression expr, C context);
 
-
     public R visitAlias(Alias alias, C context) {
-        return visit(alias, context);
+        return visitNamedExpression(alias, context);
     }
 
     public R visitComparisonPredicate(ComparisonPredicate cp, C context) {
@@ -38,75 +40,106 @@ public abstract class ExpressionVisitor<R, C> {
     }
 
     public R visitEqualTo(EqualTo equalTo, C context) {
-        return visit(equalTo, context);
+        return visitComparisonPredicate(equalTo, context);
     }
 
     public R visitGreaterThan(GreaterThan greaterThan, C context) {
-        return visit(greaterThan, context);
+        return visitComparisonPredicate(greaterThan, context);
     }
 
     public R visitGreaterThanEqual(GreaterThanEqual greaterThanEqual, C context) {
-        return visit(greaterThanEqual, context);
+        return visitComparisonPredicate(greaterThanEqual, context);
     }
 
     public R visitLessThan(LessThan lessThan, C context) {
-        return visit(lessThan, context);
+        return visitComparisonPredicate(lessThan, context);
     }
 
     public R visitLessThanEqual(LessThanEqual lessThanEqual, C context) {
-        return visit(lessThanEqual, context);
+        return visitComparisonPredicate(lessThanEqual, context);
     }
 
-    public R visitNamedExpression(NamedExpression namedExpression, C context) {
-        return visit(namedExpression, context);
+    public R visitNullSafeEqual(NullSafeEqual nullSafeEqual, C context) {
+        return visitComparisonPredicate(nullSafeEqual, context);
     }
 
     public R visitNot(Not not, C context) {
         return visit(not, context);
     }
 
-    public R visitNullSafeEqual(NullSafeEqual nullSafeEqual, C context) {
-        return visit(nullSafeEqual, context);
+    public R visitSlot(Slot slot, C context) {
+        return visitNamedExpression(slot, context);
+    }
+
+    public R visitNamedExpression(NamedExpression namedExpression, C context) {
+        return visit(namedExpression, context);
     }
 
     public R visitSlotReference(SlotReference slotReference, C context) {
-        return visit(slotReference, context);
+        return visitSlot(slotReference, context);
     }
 
     public R visitLiteral(Literal literal, C context) {
         return visit(literal, context);
     }
 
-    public R visitFunctionCall(FunctionCall function, C context) {
-        return visit(function, context);
-    }
-
-    public R visitBetweenPredicate(BetweenPredicate betweenPredicate, C context) {
-        return visit(betweenPredicate, context);
+    public R visitBetween(Between between, C context) {
+        return visit(between, context);
     }
 
     public R visitCompoundPredicate(CompoundPredicate compoundPredicate, C context) {
         return visit(compoundPredicate, context);
     }
 
+    public R visitBoundFunction(BoundFunction boundFunction, C context) {
+        return visit(boundFunction, context);
+    }
+
+    public R visitAggregateFunction(AggregateFunction aggregateFunction, C context) {
+        return visitBoundFunction(aggregateFunction, context);
+    }
+
     public R visitArithmetic(Arithmetic arithmetic, C context) {
         return visit(arithmetic, context);
     }
 
+    public R visitAdd(Add add, C context) {
+        return visitArithmetic(add, context);
+    }
+
+    public R visitSubtract(Subtract subtract, C context) {
+        return visitArithmetic(subtract, context);
+    }
+
+    public R visitMultiply(Multiply multiply, C context) {
+        return visitArithmetic(multiply, context);
+    }
+
+    public R visitDivide(Divide divide, C context) {
+        return visitArithmetic(divide, context);
+    }
+
+    public R visitMod(Mod mod, C context) {
+        return visitArithmetic(mod, context);
+    }
 
     /* ********************************************************************************************
      * Unbound expressions
      * ********************************************************************************************/
 
+    public R visitUnboundFunction(UnboundFunction unboundFunction, C context) {
+        return visit(unboundFunction, context);
+    }
+
     public R visitUnboundAlias(UnboundAlias unboundAlias, C context) {
-        return visit(unboundAlias, context);
+        return visitNamedExpression(unboundAlias, context);
     }
 
     public R visitUnboundSlot(UnboundSlot unboundSlot, C context) {
-        return visit(unboundSlot, context);
+        return visitSlot(unboundSlot, context);
     }
 
     public R visitUnboundStar(UnboundStar unboundStar, C context) {
-        return visit(unboundStar, context);
+        return visitNamedExpression(unboundStar, context);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/FunctionCall.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/FunctionCall.java
deleted file mode 100644
index a987ce17c4..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/FunctionCall.java
+++ /dev/null
@@ -1,58 +0,0 @@
-// 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.trees.expressions;
-
-import org.apache.doris.analysis.FunctionName;
-import org.apache.doris.nereids.trees.NodeType;
-import org.apache.doris.nereids.trees.analysis.FunctionParams;
-
-/**
- * Logical FunctionCall Expression.
- */
-public class FunctionCall extends Expression {
-
-    private FunctionName fnName;
-
-    private FunctionParams fnParams;
-
-    private FunctionCall(FunctionName functionName, FunctionParams functionParams) {
-        super(NodeType.FUNCTIONCALL, functionParams.getExpressionList().toArray(new Expression[0]));
-        this.fnName = functionName;
-        this.fnParams = functionParams;
-    }
-
-    public FunctionCall(String functionName, Expression params) {
-        this(new FunctionName(functionName), new FunctionParams(false, params));
-    }
-
-    public FunctionCall(String functionName, FunctionParams functionParams) {
-        this(new FunctionName(functionName), functionParams);
-    }
-
-    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
-        return visitor.visitFunctionCall(this, context);
-    }
-
-    public FunctionName getFnName() {
-        return fnName;
-    }
-
-    public FunctionParams getFnParams() {
-        return fnParams;
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Mod.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Mod.java
index f5210f89fd..f26e2ffa81 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Mod.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Mod.java
@@ -17,6 +17,10 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
 /**
  * Mod Expression.
  */
@@ -28,7 +32,18 @@ public class Mod<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE extends Ex
 
     @Override
     public String sql() {
-        return left().sql() + ' ' + getArithOperator().toString()
+        return left().sql() + ' ' + getArithmeticOperator().toString()
                 + ' ' + right().sql();
     }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new Mod<>(children.get(0), children.get(1));
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitMod(this, context);
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Multiply.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Multiply.java
index 7852053dc3..47036cfb39 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Multiply.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Multiply.java
@@ -17,6 +17,10 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
 /**
  * Multiply Expression.
  */
@@ -29,7 +33,18 @@ public class Multiply<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE exten
 
     @Override
     public String sql() {
-        return left().sql() + ' ' + getArithOperator().toString()
+        return left().sql() + ' ' + getArithmeticOperator().toString()
                 + ' ' + right().sql();
     }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitMultiply(this, context);
+    }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new Multiply<>(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 5865c89485..88968711c0 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
@@ -45,12 +45,6 @@ public class Not<CHILD_TYPE extends Expression> extends Expression
         return visitor.visitNot(this, context);
     }
 
-    @Override
-    public Not<Expression> withChildren(List<Expression> children) {
-        Preconditions.checkArgument(children.size() == 1);
-        return new Not<>(children.get(0));
-    }
-
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -67,4 +61,10 @@ public class Not<CHILD_TYPE extends Expression> extends Expression
     public String toString() {
         return "( not " + child() + ")";
     }
+
+    @Override
+    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/expressions/NullSafeEqual.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java
index afc4b20d74..637f33c119 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java
@@ -20,6 +20,10 @@ package org.apache.doris.nereids.trees.expressions;
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.NodeType;
 
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
 /**
  * Null safe equal expression: a <=> b.
  * Unlike normal equal to expression, null <=> null is true.
@@ -50,4 +54,10 @@ public class NullSafeEqual<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE
     public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
         return visitor.visitNullSafeEqual(this, context);
     }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new NullSafeEqual<>(children.get(0), children.get(1));
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java
index eddc88126d..6d522765dc 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java
@@ -19,6 +19,10 @@ package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.trees.NodeType;
 
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
 /**
  * Or predicate expression.
  */
@@ -33,4 +37,10 @@ public class Or<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE extends Exp
     public Or(LEFT_CHILD_TYPE left, RIGHT_CHILD_TYPE right) {
         super(NodeType.OR, left, right);
     }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new Or<>(children.get(0), children.get(1));
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java
index 78f1ba3491..3a215d1c97 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java
@@ -23,12 +23,6 @@ import org.apache.doris.nereids.trees.NodeType;
  * Abstract class for all slot in expression.
  */
 public abstract class Slot extends NamedExpression implements LeafExpression {
-    private ExprId id;
-
-    public Slot(NodeType type, ExprId id, Expression... children) {
-        super(type, children);
-        this.id = id;
-    }
 
     public Slot(NodeType type) {
         super(type);
@@ -38,8 +32,4 @@ public abstract class Slot extends NamedExpression implements LeafExpression {
     public Slot toSlot() {
         return this;
     }
-
-    public ExprId getId() {
-        return id;
-    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
index ca6769e048..ba4a97eb0d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
@@ -21,6 +21,7 @@ import org.apache.doris.catalog.Column;
 import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.types.DataType;
 
+import com.google.common.base.Preconditions;
 import org.apache.commons.lang.StringUtils;
 
 import java.util.List;
@@ -90,7 +91,7 @@ public class SlotReference extends Slot {
 
     @Override
     public String sql() {
-        return null;
+        return name;
     }
 
     @Override
@@ -132,4 +133,10 @@ public class SlotReference extends Slot {
     public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
         return visitor.visitSlotReference(this, context);
     }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 0);
+        return this;
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Subtract.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Subtract.java
index c4944709c0..1f4e5c08cd 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Subtract.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Subtract.java
@@ -17,6 +17,10 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
 /**
  * Subtract Expression. BinaryExpression.
  */
@@ -28,7 +32,18 @@ public class Subtract<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE exten
 
     @Override
     public String sql() {
-        return left().sql() + ' ' + getArithOperator().toString()
+        return left().sql() + ' ' + getArithmeticOperator().toString()
                 + ' ' + right().sql();
     }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new Subtract<>(children.get(0), children.get(1));
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitSubtract(this, context);
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/TernaryExpression.java
similarity index 58%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/TernaryExpression.java
index 78f1ba3491..afece2d4b1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/TernaryExpression.java
@@ -17,29 +17,27 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
-import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.TernaryNode;
 
 /**
- * Abstract class for all slot in expression.
+ * Interface for all expression that have three children.
  */
-public abstract class Slot extends NamedExpression implements LeafExpression {
-    private ExprId id;
+public interface TernaryExpression<
+            FIRST_CHILD_TYPE extends Expression,
+            SECOND_CHILD_TYPE extends Expression,
+            THIRD_CHILD_TYPE extends Expression>
+        extends TernaryNode<Expression, FIRST_CHILD_TYPE, SECOND_CHILD_TYPE, THIRD_CHILD_TYPE> {
 
-    public Slot(NodeType type, ExprId id, Expression... children) {
-        super(type, children);
-        this.id = id;
-    }
 
-    public Slot(NodeType type) {
-        super(type);
+    default FIRST_CHILD_TYPE first() {
+        return (FIRST_CHILD_TYPE) child(0);
     }
 
-    @Override
-    public Slot toSlot() {
-        return this;
+    default SECOND_CHILD_TYPE second() {
+        return (SECOND_CHILD_TYPE) child(1);
     }
 
-    public ExprId getId() {
-        return id;
+    default THIRD_CHILD_TYPE third() {
+        return (THIRD_CHILD_TYPE) child(2);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/AbstractExpressionRewriteRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggregateFunction.java
similarity index 61%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/AbstractExpressionRewriteRule.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggregateFunction.java
index c7833d3bc5..6f80ffd519 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/AbstractExpressionRewriteRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/AggregateFunction.java
@@ -15,24 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.rules.expression.rewrite;
+package org.apache.doris.nereids.trees.expressions.functions;
 
-import org.apache.doris.nereids.trees.expressions.DefaultExpressionRewriter;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionVisitor;
+import org.apache.doris.nereids.types.DataType;
 
-/**
- * Base class of expression rewrite rule.
- */
-public abstract class AbstractExpressionRewriteRule extends DefaultExpressionRewriter<ExpressionRewriteContext>
-        implements ExpressionRewriteRule {
+/** AggregateFunction. */
+public abstract class AggregateFunction extends BoundFunction {
 
-    @Override
-    public Expression rewrite(Expression expr, ExpressionRewriteContext ctx) {
-        return expr.accept(this, ctx);
+    public AggregateFunction(String name, Expression... arguments) {
+        super(name, arguments);
     }
 
+    public abstract DataType getIntermediateType();
+
     @Override
-    public Expression visit(Expression expr, ExpressionRewriteContext ctx) {
-        return expr;
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitAggregateFunction(this, context);
     }
 }
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/functions/BoundFunction.java
similarity index 50%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BoundFunction.java
index 5865c89485..465f5833a8 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/functions/BoundFunction.java
@@ -15,40 +15,37 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.expressions;
+package org.apache.doris.nereids.trees.expressions.functions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.NodeType;
-
-import com.google.common.base.Preconditions;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionVisitor;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
-/**
- * Not expression: not a.
- */
-public class Not<CHILD_TYPE extends Expression> extends Expression
-        implements UnaryExpression<CHILD_TYPE> {
+/** BoundFunction. */
+public class BoundFunction extends Expression {
+    private String name;
 
-    public Not(CHILD_TYPE child) {
-        super(NodeType.NOT, child);
+    public BoundFunction(String name, Expression... arguments) {
+        super(NodeType.BOUND_FUNCTION, arguments);
+        this.name = Objects.requireNonNull(name, "name can not be null");
     }
 
-    @Override
-    public boolean nullable() throws UnboundException {
-        return child().nullable();
+    public String getName() {
+        return name;
     }
 
-    @Override
-    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
-        return visitor.visitNot(this, context);
+    public List<Expression> getArguments() {
+        return children;
     }
 
     @Override
-    public Not<Expression> withChildren(List<Expression> children) {
-        Preconditions.checkArgument(children.size() == 1);
-        return new Not<>(children.get(0));
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitBoundFunction(this, context);
     }
 
     @Override
@@ -59,12 +56,30 @@ public class Not<CHILD_TYPE extends Expression> extends Expression
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
-        Not<Expression> other = (Not) o;
-        return Objects.equals(child(), other.child());
+        BoundFunction that = (BoundFunction) o;
+        return Objects.equals(name, that.name) && Objects.equals(children, that.children);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), name);
+    }
+
+    @Override
+    public String sql() throws UnboundException {
+        String args = children()
+                .stream()
+                .map(Expression::sql)
+                .collect(Collectors.joining(", "));
+        return name + "(" + args + ")";
     }
 
     @Override
     public String toString() {
-        return "( not " + child() + ")";
+        String args = children()
+                .stream()
+                .map(Expression::toString)
+                .collect(Collectors.joining(", "));
+        return name + "(" + args + ")";
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Sum.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Sum.java
new file mode 100644
index 0000000000..097017d37e
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Sum.java
@@ -0,0 +1,67 @@
+// 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.trees.expressions.functions;
+
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.UnaryExpression;
+import org.apache.doris.nereids.types.BigIntType;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.nereids.types.DoubleType;
+import org.apache.doris.nereids.types.FractionalType;
+import org.apache.doris.nereids.types.IntegralType;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
+/** sum agg function. */
+public class Sum extends AggregateFunction implements UnaryExpression<Expression> {
+
+    public Sum(Expression child) {
+        super("sum", child);
+    }
+
+    @Override
+    public DataType getDataType() {
+        DataType dataType = child().getDataType();
+        if (dataType instanceof IntegralType) {
+            return BigIntType.INSTANCE;
+        } else if (dataType instanceof FractionalType) {
+            // TODO: precision + 10
+            return DoubleType.INSTANCE;
+        } else {
+            throw new IllegalStateException("Unsupported sum type: " + dataType);
+        }
+    }
+
+    @Override
+    public boolean nullable() {
+        return false;
+    }
+
+    @Override
+    public Expression withChildren(List<Expression> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new Sum(children.get(0));
+    }
+
+    @Override
+    public DataType getIntermediateType() {
+        return getDataType();
+    }
+}
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 751e34d17b..2bb4b9b2b9 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
@@ -133,4 +133,24 @@ public abstract class AbstractPlan<OP_TYPE extends PlanOperator>
     public String toString() {
         return operator.toString();
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        AbstractPlan<?> that = (AbstractPlan<?>) o;
+        return limit == that.limit
+                && Objects.equals(operator, that.operator)
+                && Objects.equals(statsDeriveResult, that.statsDeriveResult)
+                && Objects.equals(logicalProperties, that.logicalProperties);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(operator, statsDeriveResult, limit, logicalProperties);
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PhysicalPlanTranslator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PhysicalPlanTranslator.java
index 23859d43b5..162399d01a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PhysicalPlanTranslator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PhysicalPlanTranslator.java
@@ -285,7 +285,7 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
         tupleDescriptor.setTable(table);
         for (Slot slot : slotList) {
             SlotReference slotReference = (SlotReference) slot;
-            SlotDescriptor slotDescriptor = context.addSlotDesc(tupleDescriptor, slot.getId().asInt());
+            SlotDescriptor slotDescriptor = context.addSlotDesc(tupleDescriptor, slot.getExprId().asInt());
             slotDescriptor.setColumn(slotReference.getColumn());
             slotDescriptor.setType(slotReference.getDataType().toCatalogDataType());
             slotDescriptor.setIsMaterialized(true);
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 d0dc9e596e..6ad14306d5 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
@@ -43,4 +43,6 @@ public interface Plan extends TreeNode<Plan>, PlanStats {
     Plan withOutput(List<Slot> output);
 
     Plan withGroupExpression(Optional<GroupExpression> groupExpression);
+
+    Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties);
 }
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 8da08fc878..8f845df0c5 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
@@ -68,4 +68,9 @@ public class LogicalBinaryPlan<
     public LogicalBinaryPlan withGroupExpression(Optional<GroupExpression> groupExpression) {
         return new LogicalBinaryPlan<>(operator, groupExpression, Optional.of(logicalProperties), left(), right());
     }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new LogicalBinaryPlan<>(operator, groupExpression, logicalProperties, 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 859d1edab4..8b04f1fc37 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
@@ -65,4 +65,9 @@ public class LogicalLeafPlan<OP_TYPE extends LogicalLeafOperator>
     public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
         return new LogicalLeafPlan<>(operator, groupExpression, Optional.of(logicalProperties));
     }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new LogicalLeafPlan<>(operator, groupExpression, logicalProperties);
+    }
 }
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 db19a062a6..5c65cd694f 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
@@ -21,7 +21,9 @@ import org.apache.doris.nereids.operators.plans.logical.LogicalOperator;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.function.BiFunction;
+import java.util.function.Supplier;
 
 /**
  * Abstract class for all logical plan in Nereids.
@@ -45,4 +47,12 @@ public interface LogicalPlan extends Plan {
             return this;
         }
     }
+
+    default <C> LogicalPlan optionalMap(Optional<C> ctx, Supplier<LogicalPlan> f) {
+        if (ctx.isPresent()) {
+            return f.get();
+        } else {
+            return this;
+        }
+    }
 }
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 d7041e7d83..df86bdbad9 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
@@ -65,4 +65,9 @@ public class LogicalUnaryPlan<OP_TYPE extends LogicalUnaryOperator, CHILD_TYPE e
     public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
         return new LogicalUnaryPlan<>(operator, groupExpression, Optional.of(logicalProperties), child());
     }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new LogicalUnaryPlan<>(operator, groupExpression, logicalProperties, child());
+    }
 }
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 80b4e0eaa4..c17ab725ba 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
@@ -66,4 +66,9 @@ public class PhysicalBinaryPlan<
     public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
         return new PhysicalBinaryPlan<>(operator, groupExpression, logicalProperties, left(), right());
     }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new PhysicalBinaryPlan<>(operator, groupExpression, logicalProperties.get(), 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 2fdfd5a165..dcc7d99c98 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
@@ -62,4 +62,8 @@ public class PhysicalLeafPlan<OP_TYPE extends PhysicalLeafOperator>
     public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
         return new PhysicalLeafPlan<>(operator, groupExpression, logicalProperties);
     }
+
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new PhysicalLeafPlan<>(operator, groupExpression, logicalProperties.get());
+    }
 }
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 0134fbe976..1b6b5f09a5 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
@@ -62,4 +62,9 @@ public class PhysicalUnaryPlan<OP_TYPE extends PhysicalUnaryOperator, CHILD_TYPE
     public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
         return new PhysicalUnaryPlan<>(operator, groupExpression, logicalProperties, child());
     }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new PhysicalUnaryPlan<>(operator, groupExpression, logicalProperties.get(), child());
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/BigIntType.java
similarity index 84%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/types/BigIntType.java
index fc69063abe..4c4a79efb9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/BigIntType.java
@@ -20,13 +20,14 @@ package org.apache.doris.nereids.types;
 import org.apache.doris.catalog.Type;
 
 /**
- * Double data type in Nereids.
+ * BigInt data type in Nereids.
  */
-public class DoubleType extends FractionalType {
-    public static IntegerType INSTANCE = new IntegerType();
+public class BigIntType extends IntegralType {
+    public static BigIntType INSTANCE = new BigIntType();
 
     @Override
     public Type toCatalogDataType() {
-        return Type.DOUBLE;
+        return Type.BIGINT;
     }
 }
+
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
index 2e7d8fb7b5..24ae434f5f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DataType.java
@@ -40,6 +40,8 @@ public abstract class DataType {
             switch (scalarType.getPrimitiveType()) {
                 case INT:
                     return IntegerType.INSTANCE;
+                case BIGINT:
+                    return BigIntType.INSTANCE;
                 case DOUBLE:
                     return DoubleType.INSTANCE;
                 case STRING:
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java
index fc69063abe..b953e8dd58 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/DoubleType.java
@@ -23,7 +23,7 @@ import org.apache.doris.catalog.Type;
  * Double data type in Nereids.
  */
 public class DoubleType extends FractionalType {
-    public static IntegerType INSTANCE = new IntegerType();
+    public static DoubleType INSTANCE = new DoubleType();
 
     @Override
     public Type toCatalogDataType() {
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/AnalyzeTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/AnalyzeSSBTest.java
similarity index 54%
rename from fe/fe-core/src/test/java/org/apache/doris/nereids/AnalyzeTest.java
rename to fe/fe-core/src/test/java/org/apache/doris/nereids/AnalyzeSSBTest.java
index fd8104476b..49f16a959e 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/AnalyzeTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/AnalyzeSSBTest.java
@@ -19,57 +19,121 @@ package org.apache.doris.nereids;
 
 import org.apache.doris.nereids.analyzer.Unbound;
 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.parser.NereidsParser;
 import org.apache.doris.nereids.properties.PhysicalProperties;
+import org.apache.doris.nereids.rules.RuleFactory;
+import org.apache.doris.nereids.rules.analysis.BindFunction;
 import org.apache.doris.nereids.rules.analysis.BindRelation;
 import org.apache.doris.nereids.rules.analysis.BindSlotReference;
+import org.apache.doris.nereids.rules.analysis.ProjectToGlobalAggregate;
+import org.apache.doris.nereids.ssb.SSBUtils;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.utframe.TestWithFeService;
 
+import com.google.common.collect.ImmutableList;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import java.util.List;
 
-public class AnalyzeTest extends TestWithFeService {
+public class AnalyzeSSBTest extends TestWithFeService {
 
     private final NereidsParser parser = new NereidsParser();
 
     @Override
     protected void runBeforeAll() throws Exception {
         createDatabase("test");
-        createTable("create table test.t1 (\n"
-                + "a int not null,\n"
-                + "b varchar(128),\n"
-                + "c int)\n"
-                + "distributed by hash(b) buckets 10\n"
-                + "properties('replication_num' = '1');");
-        createTable("create table test.t2 (\n"
-                + "d int not null, \n"
-                + "e varchar(128), \n"
-                + "f int)\n"
-                + "distributed by hash(e) buckets 10\n"
-                + "properties('replication_num' = '1');");
+        connectContext.setDatabase("default_cluster:test");
+
+        SSBUtils.createTables(this);
     }
 
     /**
      * TODO: check bound plan and expression details.
      */
     @Test
-    public void test() throws Exception {
-        connectContext.setDatabase("default_cluster:test");
-        String sql = "select a, b, e from t1 join t2 on t1.a=t2.d";
+    public void q1_1() {
+        checkAnalyze(SSBUtils.Q1_1);
+    }
+
+    @Test
+    public void q1_2() {
+        checkAnalyze(SSBUtils.Q1_2);
+    }
+
+    @Test
+    public void q1_3() {
+        checkAnalyze(SSBUtils.Q1_3);
+    }
+
+    @Test
+    public void q2_1() {
+        checkAnalyze(SSBUtils.Q2_1);
+    }
+
+    @Test
+    public void q2_2() {
+        checkAnalyze(SSBUtils.Q2_2);
+    }
+
+    @Test
+    public void q2_3() {
+        checkAnalyze(SSBUtils.Q2_3);
+    }
+
+    @Test
+    public void q3_1() {
+        checkAnalyze(SSBUtils.Q3_1);
+    }
+
+    @Test
+    public void q3_2() {
+        checkAnalyze(SSBUtils.Q3_2);
+    }
+
+    @Test
+    public void q3_3() {
+        checkAnalyze(SSBUtils.Q3_3);
+    }
+
+    @Test
+    public void q3_4() {
+        checkAnalyze(SSBUtils.Q3_4);
+    }
+
+    @Test
+    public void q4_1() {
+        checkAnalyze(SSBUtils.Q4_1);
+    }
+
+    @Test
+    public void q4_2() {
+        checkAnalyze(SSBUtils.Q4_2);
+    }
+
+    @Test
+    public void q4_3() {
+        checkAnalyze(SSBUtils.Q4_3);
+    }
+
+    private void checkAnalyze(String sql) {
         LogicalPlan analyzed = analyze(sql);
+        System.out.println(analyzed.treeString());
         Assertions.assertTrue(checkBound(analyzed));
     }
 
-    private LogicalPlan analyze(String sql) throws Exception {
-        LogicalPlan parsed = parser.parseSingle(sql);
-        return analyze(parsed, connectContext);
+    private LogicalPlan analyze(String sql) {
+        try {
+            LogicalPlan parsed = parser.parseSingle(sql);
+            return analyze(parsed, connectContext);
+        } catch (Throwable t) {
+            throw new IllegalStateException("Analyze failed", t);
+        }
     }
 
     private LogicalPlan analyze(LogicalPlan inputPlan, ConnectContext connectContext) {
@@ -77,24 +141,30 @@ public class AnalyzeTest extends TestWithFeService {
         memo.initialize(inputPlan);
         OptimizerContext optimizerContext = new OptimizerContext(memo);
         PlannerContext plannerContext = new PlannerContext(optimizerContext, connectContext, new PhysicalProperties());
-        optimizerContext.pushJob(
-            new RewriteBottomUpJob(memo.getRoot(), new BindSlotReference().buildRules(), plannerContext)
-        );
-        optimizerContext.pushJob(
-            new RewriteBottomUpJob(memo.getRoot(), new BindRelation().buildRules(), plannerContext)
-        );
-        plannerContext.getOptimizerContext().getJobScheduler().executeJobPool(plannerContext);
+
+        executeRewriteBottomUpJob(plannerContext, new BindFunction());
+        executeRewriteBottomUpJob(plannerContext, new BindRelation());
+        executeRewriteBottomUpJob(plannerContext, new BindSlotReference());
+        executeRewriteBottomUpJob(plannerContext, new ProjectToGlobalAggregate());
         return (LogicalPlan) memo.copyOut();
     }
 
+    private void executeRewriteBottomUpJob(PlannerContext plannerContext, RuleFactory<Plan> ruleFactory) {
+        OptimizerContext optimizerContext = plannerContext.getOptimizerContext();
+        Group rootGroup = optimizerContext.getMemo().getRoot();
+        RewriteBottomUpJob job = new RewriteBottomUpJob(rootGroup, plannerContext, ImmutableList.of(ruleFactory));
+        optimizerContext.pushJob(job);
+        optimizerContext.getJobScheduler().executeJobPool(plannerContext);
+    }
+
     private boolean checkBound(LogicalPlan root) {
-        if (!checkPlanNodeBound(root))  {
+        if (!checkPlanBound(root))  {
             return false;
         }
 
         List<Plan> children = root.children();
         for (Plan child : children) {
-            if (!checkBound((LogicalPlan) child)) {
+            if (!checkPlanBound((LogicalPlan) child)) {
                 return false;
             }
         }
@@ -104,7 +174,7 @@ public class AnalyzeTest extends TestWithFeService {
     /**
      * PlanNode and its expressions are all bound.
      */
-    private boolean checkPlanNodeBound(LogicalPlan plan) {
+    private boolean checkPlanBound(LogicalPlan plan) {
         if (plan instanceof Unbound) {
             return false;
         }
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 f45daafd83..7e877e3b4e 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
@@ -200,6 +200,26 @@ public class GroupExpressionMatchingTest implements Plans {
         Assertions.assertFalse(iterator.hasNext());
     }
 
+    @Test
+    public void testTopMatchButChildrenNotMatch() {
+        Plan root = plan(new LogicalJoin(JoinType.LEFT_OUTER_JOIN),
+                plan(new UnboundRelation(ImmutableList.of("a"))),
+                plan(new UnboundRelation(ImmutableList.of("b")))
+        );
+
+
+        Memo memo = new Memo();
+        memo.initialize(root);
+
+        Pattern pattern = patterns()
+                .innerLogicalJoin(patterns().logicalFilter(), patterns().any()).pattern;
+        GroupExpressionMatching groupExpressionMatching
+                = new GroupExpressionMatching(pattern, memo.getRoot().getLogicalExpression());
+        Iterator<Plan> iterator = groupExpressionMatching.iterator();
+
+        Assertions.assertFalse(iterator.hasNext());
+    }
+
     private org.apache.doris.nereids.pattern.GeneratedPatterns patterns() {
         return () -> RulePromise.REWRITE;
     }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/ssb/SSBUtils.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/ssb/SSBUtils.java
new file mode 100644
index 0000000000..cf43c1246b
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/ssb/SSBUtils.java
@@ -0,0 +1,351 @@
+// 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.ssb;
+
+import org.apache.doris.utframe.TestWithFeService;
+
+public class SSBUtils {
+    public static final String Q1_1 = "SELECT SUM(lo_extendedprice * lo_discount) AS REVENUE\n"
+            + "FROM lineorder, dates\n"
+            + "WHERE\n"
+            + "    lo_orderdate = d_datekey\n"
+            + "    AND d_year = 1993\n"
+            + "    AND lo_discount BETWEEN 1 AND 3\n"
+            + "    AND lo_quantity < 25";
+
+    public static final String Q1_2 = "SELECT SUM(lo_extendedprice * lo_discount) AS REVENUE\n"
+            + "FROM lineorder, dates\n"
+            + "WHERE\n"
+            + "    lo_orderdate = d_datekey\n"
+            + "    AND d_yearmonth = 'Jan1994'\n"
+            + "    AND lo_discount BETWEEN 4 AND 6\n"
+            + "    AND lo_quantity BETWEEN 26 AND 35";
+
+    public static final String Q1_3 = "SELECT\n"
+            + "    SUM(lo_extendedprice * lo_discount) AS REVENUE\n"
+            + "FROM lineorder, dates\n"
+            + "WHERE\n"
+            + "    lo_orderdate = d_datekey\n"
+            + "    AND d_weeknuminyear = 6\n"
+            + "    AND d_year = 1994\n"
+            + "    AND lo_discount BETWEEN 5 AND 7\n"
+            + "    AND lo_quantity BETWEEN 26 AND 35";
+
+    public static final String Q2_1 = "SELECT SUM(lo_revenue), d_year, p_brand\n"
+            + "FROM lineorder, dates, part, supplier\n"
+            + "WHERE\n"
+            + "    lo_orderdate = d_datekey\n"
+            + "    AND lo_partkey = p_partkey\n"
+            + "    AND lo_suppkey = s_suppkey\n"
+            + "    AND p_category = 'MFGR#12'\n"
+            + "    AND s_region = 'AMERICA'\n"
+            + "GROUP BY d_year, p_brand\n"
+            + "ORDER BY d_year, p_brand";
+
+    public static final String Q2_2 = "SELECT SUM(lo_revenue), d_year, p_brand\n"
+            + "FROM lineorder, dates, part, supplier\n"
+            + "WHERE\n"
+            + "    lo_orderdate = d_datekey\n"
+            + "    AND lo_partkey = p_partkey\n"
+            + "    AND lo_suppkey = s_suppkey\n"
+            + "    AND p_brand BETWEEN 'MFGR#2221' AND 'MFGR#2228'\n"
+            + "    AND s_region = 'ASIA'\n"
+            + "GROUP BY d_year, p_brand\n"
+            + "ORDER BY d_year, p_brand";
+
+    public static final String Q2_3 = "SELECT SUM(lo_revenue), d_year, p_brand\n"
+            + "FROM lineorder, dates, part, supplier\n"
+            + "WHERE\n"
+            + "    lo_orderdate = d_datekey\n"
+            + "    AND lo_partkey = p_partkey\n"
+            + "    AND lo_suppkey = s_suppkey\n"
+            + "    AND p_brand = 'MFGR#2239'\n"
+            + "    AND s_region = 'EUROPE'\n"
+            + "GROUP BY d_year, p_brand\n"
+            + "ORDER BY d_year, p_brand";
+
+    public static final String Q3_1 = "SELECT\n"
+            + "    c_nation,\n"
+            + "    s_nation,\n"
+            + "    d_year,\n"
+            + "    SUM(lo_revenue) AS REVENUE\n"
+            + "FROM customer, lineorder, supplier, dates\n"
+            + "WHERE\n"
+            + "    lo_custkey = c_custkey\n"
+            + "    AND lo_suppkey = s_suppkey\n"
+            + "    AND lo_orderdate = d_datekey\n"
+            + "    AND c_region = 'ASIA'\n"
+            + "    AND s_region = 'ASIA'\n"
+            + "    AND d_year >= 1992\n"
+            + "    AND d_year <= 1997\n"
+            + "GROUP BY c_nation, s_nation, d_year\n"
+            + "ORDER BY d_year ASC, REVENUE DESC";
+
+    public static final String Q3_2 = "SELECT\n"
+            + "    c_city,\n"
+            + "    s_city,\n"
+            + "    d_year,\n"
+            + "    SUM(lo_revenue) AS REVENUE\n"
+            + "FROM customer, lineorder, supplier, dates\n"
+            + "WHERE\n"
+            + "    lo_custkey = c_custkey\n"
+            + "    AND lo_suppkey = s_suppkey\n"
+            + "    AND lo_orderdate = d_datekey\n"
+            + "    AND c_nation = 'UNITED STATES'\n"
+            + "    AND s_nation = 'UNITED STATES'\n"
+            + "    AND d_year >= 1992\n"
+            + "    AND d_year <= 1997\n"
+            + "GROUP BY c_city, s_city, d_year\n"
+            + "ORDER BY d_year ASC, REVENUE DESC";
+
+    public static final String Q3_3 = "SELECT\n"
+            + "    c_city,\n"
+            + "    s_city,\n"
+            + "    d_year,\n"
+            + "    SUM(lo_revenue) AS REVENUE\n"
+            + "FROM customer, lineorder, supplier, dates\n"
+            + "WHERE\n"
+            + "    lo_custkey = c_custkey\n"
+            + "    AND lo_suppkey = s_suppkey\n"
+            + "    AND lo_orderdate = d_datekey\n"
+            + "    AND (\n"
+            + "        c_city = 'UNITED KI1'\n"
+            + "        OR c_city = 'UNITED KI5'\n"
+            + "    )\n"
+            + "    AND (\n"
+            + "        s_city = 'UNITED KI1'\n"
+            + "        OR s_city = 'UNITED KI5'\n"
+            + "    )\n"
+            + "    AND d_year >= 1992\n"
+            + "    AND d_year <= 1997\n"
+            + "GROUP BY c_city, s_city, d_year\n"
+            + "ORDER BY d_year ASC, REVENUE DESC";
+
+    public static final String Q3_4 = "SELECT\n"
+            + "    c_city,\n"
+            + "    s_city,\n"
+            + "    d_year,\n"
+            + "    SUM(lo_revenue) AS REVENUE\n"
+            + "FROM customer, lineorder, supplier, dates\n"
+            + "WHERE\n"
+            + "    lo_custkey = c_custkey\n"
+            + "    AND lo_suppkey = s_suppkey\n"
+            + "    AND lo_orderdate = d_datekey\n"
+            + "    AND (\n"
+            + "        c_city = 'UNITED KI1'\n"
+            + "        OR c_city = 'UNITED KI5'\n"
+            + "    )\n"
+            + "    AND (\n"
+            + "        s_city = 'UNITED KI1'\n"
+            + "        OR s_city = 'UNITED KI5'\n"
+            + "    )\n"
+            + "    AND d_yearmonth = 'Dec1997'\n"
+            + "GROUP BY c_city, s_city, d_year\n"
+            + "ORDER BY d_year ASC, REVENUE DESC";
+
+    public static final String Q4_1 = "SELECT\n"
+            + "    d_year,\n"
+            + "    c_nation,\n"
+            + "    SUM(lo_revenue - lo_supplycost) AS PROFIT\n"
+            + "FROM dates, customer, supplier, part, lineorder\n"
+            + "WHERE\n"
+            + "    lo_custkey = c_custkey\n"
+            + "    AND lo_suppkey = s_suppkey\n"
+            + "    AND lo_partkey = p_partkey\n"
+            + "    AND lo_orderdate = d_datekey\n"
+            + "    AND c_region = 'AMERICA'\n"
+            + "    AND s_region = 'AMERICA'\n"
+            + "    AND (\n"
+            + "        p_mfgr = 'MFGR#1'\n"
+            + "        OR p_mfgr = 'MFGR#2'\n"
+            + "    )\n"
+            + "GROUP BY d_year, c_nation\n"
+            + "ORDER BY d_year, c_nation";
+
+    public static final String Q4_2 = "SELECT\n"
+            + "    d_year,\n"
+            + "    s_nation,\n"
+            + "    p_category,\n"
+            + "    SUM(lo_revenue - lo_supplycost) AS PROFIT\n"
+            + "FROM dates, customer, supplier, part, lineorder\n"
+            + "WHERE\n"
+            + "    lo_custkey = c_custkey\n"
+            + "    AND lo_suppkey = s_suppkey\n"
+            + "    AND lo_partkey = p_partkey\n"
+            + "    AND lo_orderdate = d_datekey\n"
+            + "    AND c_region = 'AMERICA'\n"
+            + "    AND s_region = 'AMERICA'\n"
+            + "    AND (\n"
+            + "        d_year = 1997\n"
+            + "        OR d_year = 1998\n"
+            + "    )\n"
+            + "    AND (\n"
+            + "        p_mfgr = 'MFGR#1'\n"
+            + "        OR p_mfgr = 'MFGR#2'\n"
+            + "    )\n"
+            + "GROUP BY d_year, s_nation, p_category\n"
+            + "ORDER BY d_year, s_nation, p_category";
+
+    public static final String Q4_3 = "SELECT\n"
+            + "    d_year,\n"
+            + "    s_city,\n"
+            + "    p_brand,\n"
+            + "    SUM(lo_revenue - lo_supplycost) AS PROFIT\n"
+            + "FROM dates, customer, supplier, part, lineorder\n"
+            + "WHERE\n"
+            + "    lo_custkey = c_custkey\n"
+            + "    AND lo_suppkey = s_suppkey\n"
+            + "    AND lo_partkey = p_partkey\n"
+            + "    AND lo_orderdate = d_datekey\n"
+            + "    AND s_nation = 'UNITED STATES'\n"
+            + "    AND (\n"
+            + "        d_year = 1997\n"
+            + "        OR d_year = 1998\n"
+            + "    )\n"
+            + "    AND p_category = 'MFGR#14'\n"
+            + "GROUP BY d_year, s_city, p_brand\n"
+            + "ORDER BY d_year, s_city, p_brand";
+
+    public static void createTables(TestWithFeService service) throws Exception {
+        service.createTable("CREATE TABLE IF NOT EXISTS `lineorder` (\n"
+                + "  `lo_orderkey` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_linenumber` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_custkey` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_partkey` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_suppkey` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_orderdate` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_orderpriority` varchar(16) NOT NULL COMMENT \"\",\n"
+                + "  `lo_shippriority` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_quantity` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_extendedprice` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_ordtotalprice` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_discount` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_revenue` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_supplycost` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_tax` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_commitdate` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `lo_shipmode` varchar(11) NOT NULL COMMENT \"\"\n"
+                + ") ENGINE=OLAP\n"
+                + "DUPLICATE KEY(`lo_orderkey`)\n"
+                + "COMMENT \"OLAP\"\n"
+                + "PARTITION BY RANGE(`lo_orderdate`)\n"
+                + "(PARTITION p1 VALUES [(\"-2147483648\"), (\"19930101\")),\n"
+                + "PARTITION p2 VALUES [(\"19930101\"), (\"19940101\")),\n"
+                + "PARTITION p3 VALUES [(\"19940101\"), (\"19950101\")),\n"
+                + "PARTITION p4 VALUES [(\"19950101\"), (\"19960101\")),\n"
+                + "PARTITION p5 VALUES [(\"19960101\"), (\"19970101\")),\n"
+                + "PARTITION p6 VALUES [(\"19970101\"), (\"19980101\")),\n"
+                + "PARTITION p7 VALUES [(\"19980101\"), (\"19990101\")))\n"
+                + "DISTRIBUTED BY HASH(`lo_orderkey`) BUCKETS 48\n"
+                + "PROPERTIES (\n"
+                + "\"replication_num\" = \"1\",\n"
+                + "\"colocate_with\" = \"groupa1\",\n"
+                + "\"in_memory\" = \"false\",\n"
+                + "\"storage_format\" = \"DEFAULT\"\n"
+                + ")");
+
+        service.createTable("CREATE TABLE IF NOT EXISTS `customer` (\n"
+                + "  `c_custkey` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `c_name` varchar(26) NOT NULL COMMENT \"\",\n"
+                + "  `c_address` varchar(41) NOT NULL COMMENT \"\",\n"
+                + "  `c_city` varchar(11) NOT NULL COMMENT \"\",\n"
+                + "  `c_nation` varchar(16) NOT NULL COMMENT \"\",\n"
+                + "  `c_region` varchar(13) NOT NULL COMMENT \"\",\n"
+                + "  `c_phone` varchar(16) NOT NULL COMMENT \"\",\n"
+                + "  `c_mktsegment` varchar(11) NOT NULL COMMENT \"\"\n"
+                + ") ENGINE=OLAP\n"
+                + "DUPLICATE KEY(`c_custkey`)\n"
+                + "COMMENT \"OLAP\"\n"
+                + "DISTRIBUTED BY HASH(`c_custkey`) BUCKETS 12\n"
+                + "PROPERTIES (\n"
+                + "\"replication_num\" = \"1\",\n"
+                + "\"colocate_with\" = \"groupa2\",\n"
+                + "\"in_memory\" = \"false\",\n"
+                + "\"storage_format\" = \"DEFAULT\"\n"
+                + ")");
+
+        service.createTable("CREATE TABLE IF NOT EXISTS `dates` (\n"
+                + "  `d_datekey` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_date` varchar(20) NOT NULL COMMENT \"\",\n"
+                + "  `d_dayofweek` varchar(10) NOT NULL COMMENT \"\",\n"
+                + "  `d_month` varchar(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_year` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_yearmonthnum` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_yearmonth` varchar(9) NOT NULL COMMENT \"\",\n"
+                + "  `d_daynuminweek` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_daynuminmonth` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_daynuminyear` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_monthnuminyear` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_weeknuminyear` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_sellingseason` varchar(14) NOT NULL COMMENT \"\",\n"
+                + "  `d_lastdayinweekfl` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_lastdayinmonthfl` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_holidayfl` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `d_weekdayfl` int(11) NOT NULL COMMENT \"\"\n"
+                + ") ENGINE=OLAP\n"
+                + "DUPLICATE KEY(`d_datekey`)\n"
+                + "COMMENT \"OLAP\"\n"
+                + "DISTRIBUTED BY HASH(`d_datekey`) BUCKETS 1\n"
+                + "PROPERTIES (\n"
+                + "\"replication_num\" = \"1\",\n"
+                + "\"in_memory\" = \"false\",\n"
+                + "\"colocate_with\" = \"groupa3\",\n"
+                + "\"storage_format\" = \"DEFAULT\"\n"
+                + ")");
+
+        service.createTable("CREATE TABLE IF NOT EXISTS `supplier` (\n"
+                + "  `s_suppkey` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `s_name` varchar(26) NOT NULL COMMENT \"\",\n"
+                + "  `s_address` varchar(26) NOT NULL COMMENT \"\",\n"
+                + "  `s_city` varchar(11) NOT NULL COMMENT \"\",\n"
+                + "  `s_nation` varchar(16) NOT NULL COMMENT \"\",\n"
+                + "  `s_region` varchar(13) NOT NULL COMMENT \"\",\n"
+                + "  `s_phone` varchar(16) NOT NULL COMMENT \"\"\n"
+                + ") ENGINE=OLAP\n"
+                + "DUPLICATE KEY(`s_suppkey`)\n"
+                + "COMMENT \"OLAP\"\n"
+                + "DISTRIBUTED BY HASH(`s_suppkey`) BUCKETS 12\n"
+                + "PROPERTIES (\n"
+                + "\"replication_num\" = \"1\",\n"
+                + "\"colocate_with\" = \"groupa4\",\n"
+                + "\"in_memory\" = \"false\",\n"
+                + "\"storage_format\" = \"DEFAULT\"\n"
+                + ")");
+
+        service.createTable("CREATE TABLE IF NOT EXISTS `part` (\n"
+                + "  `p_partkey` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `p_name` varchar(23) NOT NULL COMMENT \"\",\n"
+                + "  `p_mfgr` varchar(7) NOT NULL COMMENT \"\",\n"
+                + "  `p_category` varchar(8) NOT NULL COMMENT \"\",\n"
+                + "  `p_brand` varchar(10) NOT NULL COMMENT \"\",\n"
+                + "  `p_color` varchar(12) NOT NULL COMMENT \"\",\n"
+                + "  `p_type` varchar(26) NOT NULL COMMENT \"\",\n"
+                + "  `p_size` int(11) NOT NULL COMMENT \"\",\n"
+                + "  `p_container` varchar(11) NOT NULL COMMENT \"\"\n"
+                + ") ENGINE=OLAP\n"
+                + "DUPLICATE KEY(`p_partkey`)\n"
+                + "COMMENT \"OLAP\"\n"
+                + "DISTRIBUTED BY HASH(`p_partkey`) BUCKETS 12\n"
+                + "PROPERTIES (\n"
+                + "\"replication_num\" = \"1\",\n"
+                + "\"colocate_with\" = \"groupa5\",\n"
+                + "\"in_memory\" = \"false\",\n"
+                + "\"storage_format\" = \"DEFAULT\"\n"
+                + ")");
+    }
+}
diff --git a/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java b/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java
index feed9b0a58..85db937129 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/utframe/TestWithFeService.java
@@ -307,11 +307,11 @@ public abstract class TestWithFeService {
         return port;
     }
 
-    protected String getSQLPlanOrErrorMsg(String sql) throws Exception {
+    public String getSQLPlanOrErrorMsg(String sql) throws Exception {
         return getSQLPlanOrErrorMsg(sql, false);
     }
 
-    protected String getSQLPlanOrErrorMsg(String sql, boolean isVerbose) throws Exception {
+    public String getSQLPlanOrErrorMsg(String sql, boolean isVerbose) throws Exception {
         connectContext.getState().reset();
         StmtExecutor stmtExecutor = new StmtExecutor(connectContext, sql);
         connectContext.setExecutor(stmtExecutor);
@@ -325,7 +325,7 @@ public abstract class TestWithFeService {
         }
     }
 
-    protected Planner getSQLPlanner(String queryStr) throws Exception {
+    public Planner getSQLPlanner(String queryStr) throws Exception {
         connectContext.getState().reset();
         StmtExecutor stmtExecutor = new StmtExecutor(connectContext, queryStr);
         stmtExecutor.execute();
@@ -336,7 +336,7 @@ public abstract class TestWithFeService {
         }
     }
 
-    protected StmtExecutor getSqlStmtExecutor(String queryStr) throws Exception {
+    public StmtExecutor getSqlStmtExecutor(String queryStr) throws Exception {
         connectContext.getState().reset();
         StmtExecutor stmtExecutor = new StmtExecutor(connectContext, queryStr);
         stmtExecutor.execute();
@@ -347,28 +347,28 @@ public abstract class TestWithFeService {
         }
     }
 
-    protected void createDatabase(String db) throws Exception {
+    public void createDatabase(String db) throws Exception {
         String createDbStmtStr = "CREATE DATABASE " + db;
         CreateDbStmt createDbStmt = (CreateDbStmt) parseAndAnalyzeStmt(createDbStmtStr);
         Catalog.getCurrentCatalog().createDb(createDbStmt);
     }
 
-    protected void useDatabase(String dbName) {
+    public void useDatabase(String dbName) {
         connectContext.setDatabase(ClusterNamespace.getFullName(SystemInfoService.DEFAULT_CLUSTER, dbName));
     }
 
-    protected void createTable(String sql) throws Exception {
+    public void createTable(String sql) throws Exception {
         createTables(sql);
     }
 
-    protected void createTables(String... sqls) throws Exception {
+    public void createTables(String... sqls) throws Exception {
         for (String sql : sqls) {
             CreateTableStmt stmt = (CreateTableStmt) parseAndAnalyzeStmt(sql);
             Catalog.getCurrentCatalog().createTable(stmt);
         }
     }
 
-    protected void createView(String sql) throws Exception {
+    public void createView(String sql) throws Exception {
         CreateViewStmt createViewStmt = (CreateViewStmt) parseAndAnalyzeStmt(sql);
         Catalog.getCurrentCatalog().createView(createViewStmt);
     }


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