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

huajianlan 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 74ffdbeebc [feature](Nereids) Support OneRowRelation and EmptyRelation 
(#12416)
74ffdbeebc is described below

commit 74ffdbeebc8089c52791c02d3b2daabe7487b8a0
Author: 924060929 <924060...@qq.com>
AuthorDate: Thu Sep 8 12:21:13 2022 +0800

    [feature](Nereids) Support OneRowRelation and EmptyRelation (#12416)
    
    Support OneRowRelation and EmptyRelation.
    
    OneRowRelation: `select 100, 'abc', substring('abc', 1, 2)`
    EmptyRelation: `select * from tbl limit 0`
    
    Note:
    PhysicalOneRowRelation will translate to UnionNode(constExpr) for BE 
execution
---
 .../main/java/org/apache/doris/common/Pair.java    |   8 ++
 .../org/apache/doris/nereids/NereidsPlanner.java   |   2 +-
 .../algebra/Scan.java => analyzer/Relation.java}   |  20 +---
 ...undRelation.java => UnboundOneRowRelation.java} |  85 ++++++--------
 .../doris/nereids/analyzer/UnboundRelation.java    |   8 +-
 .../glue/translator/PhysicalPlanTranslator.java    |  55 ++++++++++
 .../glue/translator/PlanTranslatorContext.java     |   7 +-
 .../doris/nereids/jobs/batch/RewriteJob.java       |   2 +
 .../apache/doris/nereids/memo/GroupExpression.java |  11 +-
 .../java/org/apache/doris/nereids/memo/Memo.java   |  20 ++--
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  23 +++-
 .../org/apache/doris/nereids/rules/RuleSet.java    |   4 +
 .../org/apache/doris/nereids/rules/RuleType.java   |   6 +
 .../doris/nereids/rules/analysis/BindFunction.java |  17 +++
 .../nereids/rules/analysis/BindSlotReference.java  |  14 +++
 .../rules/analysis/ProjectToGlobalAggregate.java   |  16 ++-
 .../expression/rewrite/ExpressionRewrite.java      |  29 +++++
 .../expression/rewrite/rules/TypeCoercion.java     |  22 ++--
 ...gicalEmptyRelationToPhysicalEmptyRelation.java} |  26 ++---
 ...calOneRowRelationToPhysicalOneRowRelation.java} |  26 ++---
 .../LogicalLimitZeroToLogicalEmptyRelation.java}   |  27 ++---
 .../doris/nereids/stats/StatsCalculator.java       |  57 ++++++++++
 .../org/apache/doris/nereids/trees/TreeNode.java   |  16 +++
 .../nereids/trees/expressions/SlotReference.java   |  24 +++-
 .../trees/expressions/functions/Substring.java     |  19 +++-
 .../nereids/trees/expressions/literal/Literal.java |   9 ++
 .../trees/expressions/literal/VarcharLiteral.java  |   5 +
 .../apache/doris/nereids/trees/plans/PlanType.java |   5 +
 .../algebra/{Scan.java => EmptyRelation.java}      |  20 ++--
 .../algebra/{Scan.java => OneRowRelation.java}     |  18 +--
 .../doris/nereids/trees/plans/algebra/Scan.java    |   3 +-
 .../trees/plans/logical/LogicalEmptyRelation.java  | 113 +++++++++++++++++++
 .../trees/plans/logical/LogicalOneRowRelation.java | 115 +++++++++++++++++++
 .../trees/plans/physical/AbstractPhysicalPlan.java |   5 +-
 .../trees/plans/physical/PhysicalBinary.java       |   5 +-
 .../plans/physical/PhysicalEmptyRelation.java      | 122 +++++++++++++++++++++
 .../nereids/trees/plans/physical/PhysicalLeaf.java |   3 +-
 .../plans/physical/PhysicalOneRowRelation.java     | 113 +++++++++++++++++++
 .../trees/plans/physical/PhysicalUnary.java        |   3 +-
 .../nereids/trees/plans/visitor/PlanVisitor.java   |  25 +++++
 .../org/apache/doris/nereids/types/DataType.java   |  28 ++++-
 .../org/apache/doris/planner/SetOperationNode.java |  14 +++
 .../java/org/apache/doris/planner/UnionNode.java   |   4 +-
 .../apache/doris/nereids/memo/MemoRewriteTest.java |  33 ++----
 .../doris/nereids/parser/HavingClauseTest.java     | 100 +++++++++--------
 .../expression/rewrite/ExpressionRewriteTest.java  |  10 +-
 .../apache/doris/nereids/util/FieldChecker.java    |   8 +-
 .../apache/doris/nereids/util/MemoTestUtils.java   |  44 --------
 .../suites/nereids_syntax_p0/empty_relation.groovy |  28 ++---
 .../nereids_syntax_p0/one_row_relation.groovy      |  25 ++---
 50 files changed, 1051 insertions(+), 351 deletions(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/Pair.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/Pair.java
index 581e896033..c3ae810582 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/Pair.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/Pair.java
@@ -47,6 +47,14 @@ public class Pair<F, S> {
         return new Pair<>(first, second);
     }
 
+    public F key() {
+        return first;
+    }
+
+    public S value() {
+        return second;
+    }
+
     /**
      * A pair is equal if both parts are equal().
      */
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
index e4e872c71f..7bcea8a1e5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
@@ -111,7 +111,7 @@ public class NereidsPlanner extends Planner {
         // TODO: What is the appropriate time to set physical properties? 
Maybe before enter.
         // cascades style optimize phase.
 
-        // cost-based optimize and explode plan space
+        // cost-based optimize and explore plan space
         optimize();
 
         PhysicalPlan physicalPlan = chooseBestPlan(getRoot(), 
PhysicalProperties.ANY);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
 b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/Relation.java
similarity index 62%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/Relation.java
index f0c536cf83..48b30ec450 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/Relation.java
@@ -15,24 +15,10 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.plans.algebra;
-
-import org.apache.doris.catalog.Table;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
-
-import java.util.Collections;
-import java.util.List;
+package org.apache.doris.nereids.analyzer;
 
 /**
- * Common interface for logical/physical scan.
+ * Relation base interface
  */
-public interface Scan {
-    List<Expression> getExpressions();
-
-    Table getTable();
-
-    default List<Slot> getOutput() {
-        return Collections.emptyList();
-    }
+public interface Relation {
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundOneRowRelation.java
similarity index 56%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundOneRowRelation.java
index f654bcb089..6ba9b72674 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundOneRowRelation.java
@@ -17,83 +17,69 @@
 
 package org.apache.doris.nereids.analyzer;
 
-import org.apache.doris.nereids.analyzer.identifier.TableIdentifier;
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.properties.UnboundLogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.algebra.OneRowRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalLeaf;
 import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.nereids.util.Utils;
 
-import com.google.common.collect.Lists;
-import org.apache.commons.lang3.StringUtils;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 
 /**
- * Represent a relation plan node that has not been bound.
+ * A relation that contains only one row consist of some constant expressions.
+ * e.g. select 100, 'value'
  */
-public class UnboundRelation extends LogicalLeaf implements Unbound {
-    private final List<String> nameParts;
+public class UnboundOneRowRelation extends LogicalLeaf implements Unbound, 
OneRowRelation {
+    private final List<NamedExpression> projects;
 
-    public UnboundRelation(List<String> nameParts) {
-        this(nameParts, Optional.empty(), Optional.empty());
+    public UnboundOneRowRelation(List<NamedExpression> projects) {
+        this(projects, Optional.empty(), Optional.empty());
     }
 
-    public UnboundRelation(List<String> nameParts, Optional<GroupExpression> 
groupExpression,
+    private UnboundOneRowRelation(List<NamedExpression> projects, 
Optional<GroupExpression> groupExpression,
             Optional<LogicalProperties> logicalProperties) {
-        super(PlanType.LOGICAL_UNBOUND_RELATION, groupExpression, 
logicalProperties);
-        this.nameParts = nameParts;
+        super(PlanType.LOGICAL_UNBOUND_ONE_ROW_RELATION, groupExpression, 
logicalProperties);
+        Preconditions.checkArgument(projects.stream().noneMatch(p -> 
p.containsType(Slot.class)),
+                "OneRowRelation can not contains any slot");
+        this.projects = ImmutableList.copyOf(projects);
     }
 
-    public UnboundRelation(TableIdentifier identifier) {
-        this(identifier, Optional.empty(), Optional.empty());
-    }
-
-    /**
-     * Constructor for UnboundRelation.
-     *
-     * @param identifier relation identifier
-     */
-    public UnboundRelation(TableIdentifier identifier, 
Optional<GroupExpression> groupExpression,
-            Optional<LogicalProperties> logicalProperties) {
-        super(PlanType.LOGICAL_UNBOUND_RELATION, groupExpression, 
logicalProperties);
-        this.nameParts = Lists.newArrayList();
-        if (identifier.getDatabaseName().isPresent()) {
-            nameParts.add(identifier.getDatabaseName().get());
-        }
-        nameParts.add(identifier.getTableName());
-    }
-
-    public List<String> getNameParts() {
-        return nameParts;
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitUnboundOneRowRelation(this, context);
     }
 
-    public String getTableName() {
-        return nameParts.stream().map(Utils::quoteIfNeeded)
-                .reduce((left, right) -> left + "." + right).orElse("");
+    @Override
+    public List<NamedExpression> getProjects() {
+        return projects;
     }
 
     @Override
-    public LogicalProperties computeLogicalProperties() {
-        return UnboundLogicalProperties.INSTANCE;
+    public List<Expression> getExpressions() {
+        throw new 
UnsupportedOperationException(this.getClass().getSimpleName() + " don't support 
getExpression()");
     }
 
     @Override
     public Plan withGroupExpression(Optional<GroupExpression> groupExpression) 
{
-        return new UnboundRelation(nameParts, groupExpression, 
Optional.of(getLogicalProperties()));
+        return new UnboundOneRowRelation(projects, groupExpression, 
Optional.of(logicalPropertiesSupplier.get()));
     }
 
     @Override
     public Plan withLogicalProperties(Optional<LogicalProperties> 
logicalProperties) {
-        return new UnboundRelation(nameParts, Optional.empty(), 
logicalProperties);
+        return new UnboundOneRowRelation(projects, Optional.empty(), 
logicalProperties);
     }
 
     @Override
@@ -102,18 +88,15 @@ public class UnboundRelation extends LogicalLeaf 
implements Unbound {
     }
 
     @Override
-    public String toString() {
-        return "UnboundRelation" + "(" + StringUtils.join(nameParts, ".") + 
")";
-    }
-
-    @Override
-    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
-        return visitor.visitUnboundRelation(this, context);
+    public LogicalProperties computeLogicalProperties() {
+        return UnboundLogicalProperties.INSTANCE;
     }
 
     @Override
-    public List<Expression> getExpressions() {
-        throw new 
UnsupportedOperationException(this.getClass().getSimpleName() + " don't support 
getExpression()");
+    public String toString() {
+        return Utils.toSqlString("UnboundOneRowRelation",
+                "projects", projects
+        );
     }
 
     @Override
@@ -127,12 +110,12 @@ public class UnboundRelation extends LogicalLeaf 
implements Unbound {
         if (!super.equals(o)) {
             return false;
         }
-        UnboundRelation that = (UnboundRelation) o;
-        return Objects.equals(nameParts, that.nameParts);
+        UnboundOneRowRelation that = (UnboundOneRowRelation) o;
+        return Objects.equals(projects, that.projects);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(super.hashCode(), nameParts);
+        return Objects.hash(projects);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
index f654bcb089..4fce8df6e4 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
@@ -40,7 +40,7 @@ import java.util.Optional;
 /**
  * Represent a relation plan node that has not been bound.
  */
-public class UnboundRelation extends LogicalLeaf implements Unbound {
+public class UnboundRelation extends LogicalLeaf implements Relation, Unbound {
     private final List<String> nameParts;
 
     public UnboundRelation(List<String> nameParts) {
@@ -103,7 +103,9 @@ public class UnboundRelation extends LogicalLeaf implements 
Unbound {
 
     @Override
     public String toString() {
-        return "UnboundRelation" + "(" + StringUtils.join(nameParts, ".") + 
")";
+        return Utils.toSqlString("UnboundRelation",
+                "nameParts", StringUtils.join(nameParts, ".")
+        );
     }
 
     @Override
@@ -133,6 +135,6 @@ public class UnboundRelation extends LogicalLeaf implements 
Unbound {
 
     @Override
     public int hashCode() {
-        return Objects.hash(super.hashCode(), nameParts);
+        return Objects.hash(nameParts);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
index d35bc6dd87..fea1194fba 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
@@ -29,6 +29,7 @@ import org.apache.doris.analysis.SortInfo;
 import org.apache.doris.analysis.TableName;
 import org.apache.doris.analysis.TableRef;
 import org.apache.doris.analysis.TupleDescriptor;
+import org.apache.doris.analysis.TupleId;
 import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.Table;
 import org.apache.doris.common.Pair;
@@ -52,11 +53,13 @@ import 
org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalSort;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribute;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalEmptyRelation;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalLimit;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalNestedLoopJoin;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalOneRowRelation;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalQuickSort;
@@ -69,6 +72,7 @@ import org.apache.doris.planner.AggregationNode;
 import org.apache.doris.planner.AssertNumRowsNode;
 import org.apache.doris.planner.CrossJoinNode;
 import org.apache.doris.planner.DataPartition;
+import org.apache.doris.planner.EmptySetNode;
 import org.apache.doris.planner.ExchangeNode;
 import org.apache.doris.planner.HashJoinNode;
 import org.apache.doris.planner.HashJoinNode.DistributionMode;
@@ -76,6 +80,7 @@ import org.apache.doris.planner.OlapScanNode;
 import org.apache.doris.planner.PlanFragment;
 import org.apache.doris.planner.PlanNode;
 import org.apache.doris.planner.SortNode;
+import org.apache.doris.planner.UnionNode;
 import org.apache.doris.thrift.TPartitionType;
 
 import com.google.common.base.Preconditions;
@@ -227,6 +232,56 @@ public class PhysicalPlanTranslator extends 
DefaultPlanVisitor<PlanFragment, Pla
         return currentFragment;
     }
 
+    @Override
+    public PlanFragment visitPhysicalEmptyRelation(PhysicalEmptyRelation 
emptyRelation, PlanTranslatorContext context) {
+        List<Slot> output = emptyRelation.getOutput();
+        TupleDescriptor tupleDescriptor = generateTupleDesc(output, null, 
context);
+        for (int i = 0; i < output.size(); i++) {
+            SlotDescriptor slotDescriptor = tupleDescriptor.getSlots().get(i);
+            slotDescriptor.setIsNullable(true); // we should set to nullable, 
or else BE would core
+
+            Slot slot = output.get(i);
+            SlotRef slotRef = context.findSlotRef(slot.getExprId());
+            slotRef.setLabel(slot.getName());
+        }
+
+        ArrayList<TupleId> tupleIds = new ArrayList();
+        tupleIds.add(tupleDescriptor.getId());
+        EmptySetNode emptySetNode = new EmptySetNode(context.nextPlanNodeId(), 
tupleIds);
+
+        PlanFragment planFragment = new PlanFragment(context.nextFragmentId(), 
emptySetNode,
+                DataPartition.UNPARTITIONED);
+        context.addPlanFragment(planFragment);
+        return planFragment;
+    }
+
+    @Override
+    public PlanFragment visitPhysicalOneRowRelation(PhysicalOneRowRelation 
oneRowRelation,
+            PlanTranslatorContext context) {
+        List<Slot> slots = oneRowRelation.getLogicalProperties().getOutput();
+        TupleDescriptor oneRowTuple = generateTupleDesc(slots, null, context);
+
+        List<Expr> legacyExprs = oneRowRelation.getProjects()
+                .stream()
+                .map(expr -> ExpressionTranslator.translate(expr, context))
+                .collect(Collectors.toList());
+
+        for (int i = 0; i < legacyExprs.size(); i++) {
+            SlotDescriptor slotDescriptor = oneRowTuple.getSlots().get(i);
+            Expr expr = legacyExprs.get(i);
+            slotDescriptor.setSourceExpr(expr);
+            slotDescriptor.setIsNullable(true); // we should set to nullable, 
or else BE would core
+        }
+
+        UnionNode unionNode = new UnionNode(context.nextPlanNodeId(), 
oneRowTuple.getId());
+        unionNode.addConstExprList(legacyExprs);
+        unionNode.finalizeForNereids(oneRowTuple, oneRowTuple.getSlots());
+
+        PlanFragment planFragment = new PlanFragment(context.nextFragmentId(), 
unionNode, DataPartition.UNPARTITIONED);
+        context.addPlanFragment(planFragment);
+        return planFragment;
+    }
+
     @Override
     public PlanFragment visitPhysicalOlapScan(PhysicalOlapScan olapScan, 
PlanTranslatorContext context) {
         // Create OlapScanNode
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PlanTranslatorContext.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PlanTranslatorContext.java
index 7f9c3e2712..24b21d5f0d 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PlanTranslatorContext.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PlanTranslatorContext.java
@@ -38,6 +38,7 @@ import com.google.common.collect.Maps;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 /**
  * Context of physical plan.
@@ -117,10 +118,10 @@ public class PlanTranslatorContext {
      */
     public SlotDescriptor createSlotDesc(TupleDescriptor tupleDesc, 
SlotReference slotReference) {
         SlotDescriptor slotDescriptor = this.addSlotDesc(tupleDesc);
-        Column column = slotReference.getColumn();
+        Optional<Column> column = slotReference.getColumn();
         // Only the SlotDesc that in the tuple generated for scan node would 
have corresponding column.
-        if (column != null) {
-            slotDescriptor.setColumn(column);
+        if (column.isPresent()) {
+            slotDescriptor.setColumn(column.get());
         }
         
slotDescriptor.setType(slotReference.getDataType().toCatalogDataType());
         slotDescriptor.setIsMaterialized(true);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/RewriteJob.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/RewriteJob.java
index a8d59aeb02..1fe9e3f8b8 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/RewriteJob.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/batch/RewriteJob.java
@@ -23,6 +23,7 @@ import 
org.apache.doris.nereids.rules.expression.rewrite.ExpressionNormalization
 import org.apache.doris.nereids.rules.rewrite.AggregateDisassemble;
 import org.apache.doris.nereids.rules.rewrite.logical.ColumnPruning;
 import org.apache.doris.nereids.rules.rewrite.logical.FindHashConditionForJoin;
+import 
org.apache.doris.nereids.rules.rewrite.logical.LogicalLimitZeroToLogicalEmptyRelation;
 import org.apache.doris.nereids.rules.rewrite.logical.MergeConsecutiveFilters;
 import org.apache.doris.nereids.rules.rewrite.logical.MergeConsecutiveLimits;
 import org.apache.doris.nereids.rules.rewrite.logical.MergeConsecutiveProjects;
@@ -67,6 +68,7 @@ public class RewriteJob extends BatchRulesJob {
                 .add(bottomUpBatch(ImmutableList.of(new 
MergeConsecutiveProjects())))
                 .add(topDownBatch(ImmutableList.of(new 
MergeConsecutiveFilters())))
                 .add(bottomUpBatch(ImmutableList.of(new 
MergeConsecutiveLimits())))
+                .add(bottomUpBatch(ImmutableList.of(new 
LogicalLimitZeroToLogicalEmptyRelation())))
                 .add(topDownBatch(ImmutableList.of(new 
PruneOlapScanPartition())))
                 .build();
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/GroupExpression.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/GroupExpression.java
index 22b5723e57..6137647887 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/GroupExpression.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/GroupExpression.java
@@ -18,13 +18,11 @@
 package org.apache.doris.nereids.memo;
 
 import org.apache.doris.common.Pair;
-import org.apache.doris.nereids.analyzer.UnboundRelation;
+import org.apache.doris.nereids.analyzer.Relation;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalRelation;
 import org.apache.doris.statistics.StatsDeriveResult;
 
 import com.google.common.base.Preconditions;
@@ -208,10 +206,9 @@ public class GroupExpression {
             return false;
         }
         GroupExpression that = (GroupExpression) o;
-        // if the plan is UnboundRelation or LogicalRelation or 
PhysicalRelation, this == that should be true,
-        // when if one relation appear in plan more than once,
-        // we cannot distinguish them throw equals function, since equals 
function cannot use output info.
-        if (plan instanceof UnboundRelation || plan instanceof LogicalRelation 
|| plan instanceof PhysicalRelation) {
+        // FIXME: Doris not support temporary materialization, so we should 
not merge same
+        //        scan relation plan. We should add id for XxxRelation and 
compare by id.
+        if (plan instanceof Relation) {
             return false;
         }
         return children.equals(that.children) && plan.equals(that.plan)
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
index adb9b31692..f0b6f64691 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
@@ -158,19 +158,21 @@ public class Memo {
      * 
+---------------------------------------+-----------------------------------+--------------------------------+
      * | case 2:                               |                               
    |                                |
      * | if targetGroup is null                |              true             
    |      new group expression      |
-     * | and same group expression exist       |                               
    |                                |
+     * | and same group expression not exist   |                               
    |                                |
      * 
+---------------------------------------+-----------------------------------+--------------------------------+
      * | case 3:                               |                               
    |                                |
-     * | if targetGroup is null                |              true             
    |      new group expression      |
+     * | if targetGroup is not null            |              true             
    |      new group expression      |
      * | and same group expression not exits   |                               
    |                                |
      * 
+---------------------------------------+-----------------------------------+--------------------------------+
      * | case 4:                               |                               
    |                                |
-     * | if targetGroup equal to the exists    |              true             
    |      new group expression      |
-     * | group expression's owner group        |                               
    |                                |
+     * | if targetGroup is not null and not    |              true             
    |      new group expression      |
+     * | equal to the existed group            |                               
    |                                |
+     * | expression's owner group              |                               
    |                                |
      * 
+---------------------------------------+-----------------------------------+--------------------------------+
      * | case 5:                               |                               
    |                                |
-     * | if targetGroup not equal to the       |              false            
    |    existed group expression    |
-     * | exists group expression's owner group |                               
    |                                |
+     * | if targetGroup is null or equal to    |              false            
    |    existed group expression    |
+     * | the existed group expression's owner  |                               
    |                                |
+     * | group                                 |                               
    |                                |
      * 
+---------------------------------------+-----------------------------------+--------------------------------+
      * </pre>
      *
@@ -190,15 +192,19 @@ public class Memo {
             return rewriteByExistedPlan(targetGroup, plan);
         }
 
-        // try to create a new group expression
         List<Group> childrenGroups = rewriteChildrenPlansToGroups(plan, 
targetGroup);
+        plan = replaceChildrenToGroupPlan(plan, childrenGroups);
+
+        // try to create a new group expression
         GroupExpression newGroupExpression = new GroupExpression(plan, 
childrenGroups);
 
         // slow check the groupExpression/plan whether exists in the memo
         GroupExpression existedExpression = 
groupExpressions.get(newGroupExpression);
         if (existedExpression == null) {
+            // case 2 or case 3
             return rewriteByNewGroupExpression(targetGroup, plan, 
newGroupExpression);
         } else {
+            // case 4 or case 5
             return rewriteByExistedGroupExpression(targetGroup, plan, 
existedExpression, newGroupExpression);
         }
     }
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 5aa750e34c..3f534c1c27 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
@@ -72,6 +72,7 @@ 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.UnboundOneRowRelation;
 import org.apache.doris.nereids.analyzer.UnboundRelation;
 import org.apache.doris.nereids.analyzer.UnboundSlot;
 import org.apache.doris.nereids.analyzer.UnboundStar;
@@ -117,7 +118,9 @@ import 
org.apache.doris.nereids.trees.expressions.literal.IntervalLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.LargeIntLiteral;
 import org.apache.doris.nereids.trees.expressions.literal.Literal;
 import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
-import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.VarcharLiteral;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.commands.Command;
 import org.apache.doris.nereids.trees.plans.commands.ExplainCommand;
@@ -216,9 +219,8 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
     @Override
     public LogicalPlan 
visitRegularQuerySpecification(RegularQuerySpecificationContext ctx) {
         return ParserUtils.withOrigin(ctx, () -> {
-            // TODO: support one row relation
             if (ctx.fromClause() == null) {
-                throw new ParseException("Unsupported one row relation", ctx);
+                return withOneRowRelation(ctx.selectClause());
             }
 
             LogicalPlan relation = visitFromClause(ctx.fromClause());
@@ -588,7 +590,11 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
     @Override
     public Literal visitIntegerLiteral(IntegerLiteralContext ctx) {
         BigInteger bigInt = new BigInteger(ctx.getText());
-        if (BigInteger.valueOf(bigInt.intValue()).equals(bigInt)) {
+        if (BigInteger.valueOf(bigInt.byteValue()).equals(bigInt)) {
+            return new TinyIntLiteral(bigInt.byteValue());
+        } else if (BigInteger.valueOf(bigInt.shortValue()).equals(bigInt)) {
+            return new SmallIntLiteral(bigInt.shortValue());
+        } else if (BigInteger.valueOf(bigInt.intValue()).equals(bigInt)) {
             return new IntegerLiteral(bigInt.intValue());
         } else if (BigInteger.valueOf(bigInt.longValue()).equals(bigInt)) {
             return new BigIntLiteral(bigInt.longValueExact());
@@ -605,7 +611,7 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
                 .map(str -> str.substring(1, str.length() - 1))
                 .reduce((s1, s2) -> s1 + s2)
                 .orElse("");
-        return new StringLiteral(s);
+        return new VarcharLiteral(s);
     }
 
     @Override
@@ -749,6 +755,13 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
         });
     }
 
+    private UnboundOneRowRelation withOneRowRelation(SelectClauseContext 
selectCtx) {
+        return ParserUtils.withOrigin(selectCtx, () -> {
+            List<NamedExpression> projects = 
getNamedExpressions(selectCtx.namedExpressionSeq());
+            return new UnboundOneRowRelation(projects);
+        });
+    }
+
     /**
      * 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),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
index fb4ec44c40..fb2696788c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleSet.java
@@ -21,11 +21,13 @@ import 
org.apache.doris.nereids.rules.exploration.join.JoinCommute;
 import org.apache.doris.nereids.rules.exploration.join.JoinCommuteProject;
 import 
org.apache.doris.nereids.rules.implementation.LogicalAggToPhysicalHashAgg;
 import 
org.apache.doris.nereids.rules.implementation.LogicalAssertNumRowsToPhysicalAssertNumRows;
+import 
org.apache.doris.nereids.rules.implementation.LogicalEmptyRelationToPhysicalEmptyRelation;
 import 
org.apache.doris.nereids.rules.implementation.LogicalFilterToPhysicalFilter;
 import org.apache.doris.nereids.rules.implementation.LogicalJoinToHashJoin;
 import 
org.apache.doris.nereids.rules.implementation.LogicalJoinToNestedLoopJoin;
 import 
org.apache.doris.nereids.rules.implementation.LogicalLimitToPhysicalLimit;
 import 
org.apache.doris.nereids.rules.implementation.LogicalOlapScanToPhysicalOlapScan;
+import 
org.apache.doris.nereids.rules.implementation.LogicalOneRowRelationToPhysicalOneRowRelation;
 import 
org.apache.doris.nereids.rules.implementation.LogicalProjectToPhysicalProject;
 import 
org.apache.doris.nereids.rules.implementation.LogicalSortToPhysicalQuickSort;
 import org.apache.doris.nereids.rules.implementation.LogicalTopNToPhysicalTopN;
@@ -60,6 +62,8 @@ public class RuleSet {
             .add(new LogicalSortToPhysicalQuickSort())
             .add(new LogicalTopNToPhysicalTopN())
             .add(new LogicalAssertNumRowsToPhysicalAssertNumRows())
+            .add(new LogicalOneRowRelationToPhysicalOneRowRelation())
+            .add(new LogicalEmptyRelationToPhysicalEmptyRelation())
             .build();
 
     public List<Rule> getExplorationRules() {
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 20c3a9fb79..efcf3adae3 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
@@ -30,6 +30,7 @@ public enum RuleType {
 
     // **** make sure BINDING_UNBOUND_LOGICAL_PLAN is the lowest priority in 
the rewrite rules. ****
     BINDING_NON_LEAF_LOGICAL_PLAN(RuleTypeClass.REWRITE),
+    BINDING_ONE_ROW_RELATION_SLOT(RuleTypeClass.REWRITE),
     BINDING_RELATION(RuleTypeClass.REWRITE),
     BINDING_PROJECT_SLOT(RuleTypeClass.REWRITE),
     BINDING_FILTER_SLOT(RuleTypeClass.REWRITE),
@@ -37,6 +38,7 @@ public enum RuleType {
     BINDING_AGGREGATE_SLOT(RuleTypeClass.REWRITE),
     BINDING_SORT_SLOT(RuleTypeClass.REWRITE),
     BINDING_LIMIT_SLOT(RuleTypeClass.REWRITE),
+    BINDING_ONE_ROW_RELATION_FUNCTION(RuleTypeClass.REWRITE),
     BINDING_PROJECT_FUNCTION(RuleTypeClass.REWRITE),
     BINDING_AGGREGATE_FUNCTION(RuleTypeClass.REWRITE),
     BINDING_SUBQUERY_ALIAS_SLOT(RuleTypeClass.REWRITE),
@@ -84,6 +86,7 @@ public enum RuleType {
     COLUMN_PRUNE_SORT_CHILD(RuleTypeClass.REWRITE),
     COLUMN_PRUNE_JOIN_CHILD(RuleTypeClass.REWRITE),
     // expression of plan rewrite
+    REWRITE_ONE_ROW_RELATION_EXPRESSION(RuleTypeClass.REWRITE),
     REWRITE_PROJECT_EXPRESSION(RuleTypeClass.REWRITE),
     REWRITE_AGG_EXPRESSION(RuleTypeClass.REWRITE),
     REWRITE_FILTER_EXPRESSION(RuleTypeClass.REWRITE),
@@ -96,6 +99,7 @@ public enum RuleType {
     REWRITE_SENTINEL(RuleTypeClass.REWRITE),
     OLAP_SCAN_PARTITION_PRUNE(RuleTypeClass.REWRITE),
     SWAP_FILTER_AND_PROJECT(RuleTypeClass.REWRITE),
+    LOGICAL_LIMIT_TO_LOGICAL_EMPTY_RELATION_RULE(RuleTypeClass.REWRITE),
 
     // exploration rules
     TEST_EXPLORATION(RuleTypeClass.EXPLORATION),
@@ -105,6 +109,7 @@ public enum RuleType {
     LOGICAL_JOIN_EXCHANGE(RuleTypeClass.EXPLORATION),
 
     // implementation rules
+    
LOGICAL_ONE_ROW_RELATION_TO_PHYSICAL_ONE_ROW_RELATION(RuleTypeClass.IMPLEMENTATION),
     LOGICAL_AGG_TO_PHYSICAL_HASH_AGG_RULE(RuleTypeClass.IMPLEMENTATION),
     LOGICAL_JOIN_TO_HASH_JOIN_RULE(RuleTypeClass.IMPLEMENTATION),
     LOGICAL_JOIN_TO_NESTED_LOOP_JOIN_RULE(RuleTypeClass.IMPLEMENTATION),
@@ -112,6 +117,7 @@ public enum RuleType {
     LOGICAL_FILTER_TO_PHYSICAL_FILTER_RULE(RuleTypeClass.IMPLEMENTATION),
     LOGICAL_SORT_TO_PHYSICAL_QUICK_SORT_RULE(RuleTypeClass.IMPLEMENTATION),
     LOGICAL_TOP_N_TO_PHYSICAL_TOP_N_RULE(RuleTypeClass.IMPLEMENTATION),
+    
LOGICAL_EMPTY_RELATION_TO_PHYSICAL_EMPTY_RELATION_RULE(RuleTypeClass.IMPLEMENTATION),
     LOGICAL_LIMIT_TO_PHYSICAL_LIMIT_RULE(RuleTypeClass.IMPLEMENTATION),
     LOGICAL_OLAP_SCAN_TO_PHYSICAL_OLAP_SCAN_RULE(RuleTypeClass.IMPLEMENTATION),
     
LOGICAL_ASSERT_NUM_ROWS_TO_PHYSICAL_ASSERT_NUM_ROWS(RuleTypeClass.IMPLEMENTATION),
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
index d1a4a4cd2c..7f309d768a 100644
--- 
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
@@ -35,6 +35,7 @@ import 
org.apache.doris.nereids.trees.expressions.functions.Year;
 import 
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalHaving;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.types.DateTimeType;
 import org.apache.doris.nereids.types.DateType;
@@ -52,6 +53,22 @@ public class BindFunction implements AnalysisRuleFactory {
     @Override
     public List<Rule> buildRules() {
         return ImmutableList.of(
+            RuleType.BINDING_ONE_ROW_RELATION_FUNCTION.build(
+                logicalOneRowRelation().then(oneRowRelation -> {
+                    List<NamedExpression> projects = 
oneRowRelation.getProjects();
+                    List<NamedExpression> boundProjects = bind(projects);
+                    // TODO:
+                    // trick logic: currently XxxRelation in GroupExpression 
always difference to each other,
+                    // so this rule must check the expression whether is 
changed to prevent dead loop because
+                    // new LogicalOneRowRelation can hit this rule too. we 
would remove code until the pr
+                    // (@wangshuo128) mark the id in XxxRelation, then we can 
compare XxxRelation in
+                    // GroupExpression by id
+                    if (projects.equals(boundProjects)) {
+                        return oneRowRelation;
+                    }
+                    return new LogicalOneRowRelation(boundProjects);
+                })
+            ),
             RuleType.BINDING_PROJECT_FUNCTION.build(
                 logicalProject().then(project -> {
                     List<NamedExpression> boundExpr = 
bind(project.getProjects());
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 282e119fe5..63380462c6 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
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.rules.analysis;
 
 import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.analyzer.UnboundAlias;
+import org.apache.doris.nereids.analyzer.UnboundOneRowRelation;
 import org.apache.doris.nereids.analyzer.UnboundSlot;
 import org.apache.doris.nereids.analyzer.UnboundStar;
 import org.apache.doris.nereids.exceptions.AnalysisException;
@@ -44,6 +45,7 @@ import 
org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalHaving;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
@@ -153,6 +155,18 @@ public class BindSlotReference implements 
AnalysisRuleFactory {
                     return new LogicalHaving<>(boundPredicates, 
having.child());
                 })
             ),
+            RuleType.BINDING_ONE_ROW_RELATION_SLOT.build(
+                    // we should bind UnboundAlias in the UnboundOneRowRelation
+                    unboundOneRowRelation().thenApply(ctx -> {
+                        UnboundOneRowRelation oneRowRelation = ctx.root;
+                        List<NamedExpression> projects = 
oneRowRelation.getProjects()
+                                .stream()
+                                .map(project -> bind(project, 
ImmutableList.of(), oneRowRelation, ctx.cascadesContext))
+                                .collect(Collectors.toList());
+                        return new LogicalOneRowRelation(projects);
+                    })
+            ),
+
             RuleType.BINDING_NON_LEAF_LOGICAL_PLAN.build(
                 logicalPlan()
                         .when(plan -> plan.canBind() && !(plan instanceof 
LeafPlan))
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
index e999e79761..eb02c719f5 100644
--- 
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
@@ -25,7 +25,21 @@ import 
org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
 
 import com.google.common.collect.ImmutableList;
 
-/** ProjectToGlobalAggregate. */
+/**
+ * ProjectToGlobalAggregate.
+ *
+ * example sql:
+ * <pre>
+ * select sum(value)
+ * from tbl
+ * </pre>
+ *
+ * origin plan:                                                 transformed 
plan:
+ *
+ * LogicalProject(projects=[sum(value)])                        
LogicalAggregate(groupBy=[], output=[sum(value)])
+ *            |                                      =>                        
      |
+ *  LogicalOlapScan(table=tbl)                                                 
 LogicalOlapScan(table=tbl)
+ */
 public class ProjectToGlobalAggregate extends OneAnalysisRuleFactory {
     @Override
     public Rule build() {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
index 75dbd5d415..d808183c24 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
@@ -26,6 +26,7 @@ import 
org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
 import com.google.common.collect.ImmutableList;
@@ -42,6 +43,10 @@ import java.util.stream.Collectors;
 public class ExpressionRewrite implements RewriteRuleFactory {
     private final ExpressionRuleExecutor rewriter;
 
+    public ExpressionRewrite(ExpressionRewriteRule... rules) {
+        this.rewriter = new 
ExpressionRuleExecutor(ImmutableList.copyOf(rules));
+    }
+
     public ExpressionRewrite(ExpressionRuleExecutor rewriter) {
         this.rewriter = Objects.requireNonNull(rewriter, "rewriter is null");
     }
@@ -49,12 +54,36 @@ public class ExpressionRewrite implements 
RewriteRuleFactory {
     @Override
     public List<Rule> buildRules() {
         return ImmutableList.of(
+                new OneRowRelationExpressionRewrite().build(),
                 new ProjectExpressionRewrite().build(),
                 new AggExpressionRewrite().build(),
                 new FilterExpressionRewrite().build(),
                 new JoinExpressionRewrite().build());
     }
 
+    private class OneRowRelationExpressionRewrite extends 
OneRewriteRuleFactory {
+        @Override
+        public Rule build() {
+            return logicalOneRowRelation().then(oneRowRelation -> {
+                List<NamedExpression> projects = oneRowRelation.getProjects();
+                List<NamedExpression> newProjects = projects
+                        .stream()
+                        .map(expr -> (NamedExpression) rewriter.rewrite(expr))
+                        .collect(Collectors.toList());
+                // TODO:
+                // trick logic: currently XxxRelation in GroupExpression 
always difference to each other,
+                // so this rule must check the expression whether is changed 
to prevent dead loop because
+                // new LogicalOneRowRelation can hit this rule too. we would 
remove code until the pr
+                // (@wangshuo128) mark the id in XxxRelation, then we can 
compare XxxRelation in
+                // GroupExpression by id
+                if (projects.equals(newProjects)) {
+                    return oneRowRelation;
+                }
+                return new LogicalOneRowRelation(newProjects);
+            }).toRule(RuleType.REWRITE_ONE_ROW_RELATION_EXPRESSION);
+        }
+    }
+
     private class ProjectExpressionRewrite extends OneRewriteRuleFactory {
         @Override
         public Rule build() {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/TypeCoercion.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/TypeCoercion.java
index cc745e4b73..f13d20ec87 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/TypeCoercion.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/TypeCoercion.java
@@ -34,7 +34,6 @@ import com.google.common.collect.Lists;
 
 import java.util.List;
 import java.util.Optional;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 /**
@@ -141,21 +140,16 @@ public class TypeCoercion extends 
AbstractExpressionRewriteRule {
     private Expression visitImplicitCastInputTypes(Expression expr, 
ExpressionRewriteContext ctx) {
         ImplicitCastInputTypes implicitCastInputTypes = 
(ImplicitCastInputTypes) expr;
         List<Expression> newChildren = 
Lists.newArrayListWithCapacity(expr.arity());
-        AtomicInteger changed = new AtomicInteger(0);
+        boolean changed = false;
         for (int i = 0; i < 
implicitCastInputTypes.expectedInputTypes().size(); i++) {
-            newChildren.add(implicitCast(expr.child(i), 
implicitCastInputTypes.expectedInputTypes().get(i), ctx)
-                    .map(e -> {
-                        changed.incrementAndGet();
-                        return e;
-                    })
-                    .orElse(expr.child(0))
-            );
-        }
-        if (changed.get() != 0) {
-            return expr.withChildren(newChildren);
-        } else {
-            return expr;
+            AbstractDataType expectedType = 
implicitCastInputTypes.expectedInputTypes().get(i);
+            Optional<Expression> castResult = implicitCast(expr.child(i), 
expectedType, ctx);
+            if (castResult.isPresent()) {
+                changed = true;
+            }
+            newChildren.add(castResult.orElse(expr.child(i)));
         }
+        return changed ? expr.withChildren(newChildren) : expr;
     }
 
     /**
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalEmptyRelationToPhysicalEmptyRelation.java
similarity index 54%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalEmptyRelationToPhysicalEmptyRelation.java
index f0c536cf83..b48d07a4a7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalEmptyRelationToPhysicalEmptyRelation.java
@@ -15,24 +15,20 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.plans.algebra;
+package org.apache.doris.nereids.rules.implementation;
 
-import org.apache.doris.catalog.Table;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
-
-import java.util.Collections;
-import java.util.List;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalEmptyRelation;
 
 /**
- * Common interface for logical/physical scan.
+ * Implementation rule that convert logical empty relation to physical empty 
relation.
  */
-public interface Scan {
-    List<Expression> getExpressions();
-
-    Table getTable();
-
-    default List<Slot> getOutput() {
-        return Collections.emptyList();
+public class LogicalEmptyRelationToPhysicalEmptyRelation extends 
OneImplementationRuleFactory {
+    @Override
+    public Rule build() {
+        return logicalEmptyRelation()
+                .then(relation -> new 
PhysicalEmptyRelation(relation.getProjects(), relation.getLogicalProperties()))
+                
.toRule(RuleType.LOGICAL_EMPTY_RELATION_TO_PHYSICAL_EMPTY_RELATION_RULE);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalOneRowRelationToPhysicalOneRowRelation.java
similarity index 54%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalOneRowRelationToPhysicalOneRowRelation.java
index f0c536cf83..d81a8797dd 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalOneRowRelationToPhysicalOneRowRelation.java
@@ -15,24 +15,20 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.plans.algebra;
+package org.apache.doris.nereids.rules.implementation;
 
-import org.apache.doris.catalog.Table;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
-
-import java.util.Collections;
-import java.util.List;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalOneRowRelation;
 
 /**
- * Common interface for logical/physical scan.
+ * Implementation rule that convert logical aggregation to physical hash 
aggregation.
  */
-public interface Scan {
-    List<Expression> getExpressions();
-
-    Table getTable();
-
-    default List<Slot> getOutput() {
-        return Collections.emptyList();
+public class LogicalOneRowRelationToPhysicalOneRowRelation extends 
OneImplementationRuleFactory {
+    @Override
+    public Rule build() {
+        return logicalOneRowRelation()
+                .then(relation -> new 
PhysicalOneRowRelation(relation.getProjects(), relation.getLogicalProperties()))
+                
.toRule(RuleType.LOGICAL_ONE_ROW_RELATION_TO_PHYSICAL_ONE_ROW_RELATION);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/LogicalLimitZeroToLogicalEmptyRelation.java
similarity index 53%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/LogicalLimitZeroToLogicalEmptyRelation.java
index f0c536cf83..e43808cfed 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/LogicalLimitZeroToLogicalEmptyRelation.java
@@ -15,24 +15,25 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.plans.algebra;
+package org.apache.doris.nereids.rules.rewrite.logical;
 
-import org.apache.doris.catalog.Table;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.rules.Rule;
+import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
 
-import java.util.Collections;
 import java.util.List;
 
 /**
- * Common interface for logical/physical scan.
+ * e.g.
+ * LogicalLimit(limit=0)   => LogicalEmptyRelation(projects=[limit.output()])
  */
-public interface Scan {
-    List<Expression> getExpressions();
-
-    Table getTable();
-
-    default List<Slot> getOutput() {
-        return Collections.emptyList();
+public class LogicalLimitZeroToLogicalEmptyRelation extends 
OneRewriteRuleFactory {
+    @Override
+    public Rule build() {
+        return logicalLimit()
+                .when(limit -> limit.getLimit() == 0)
+                .then(limit -> new LogicalEmptyRelation((List) 
limit.getOutput()))
+                .toRule(RuleType.LOGICAL_LIMIT_TO_LOGICAL_EMPTY_RELATION_RULE);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
index 8b1fcd29b3..6f16d06f04 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
@@ -21,6 +21,7 @@ import org.apache.doris.catalog.MaterializedIndex;
 import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.Partition;
 import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
@@ -28,29 +29,35 @@ import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.algebra.Aggregate;
+import org.apache.doris.nereids.trees.plans.algebra.EmptyRelation;
 import org.apache.doris.nereids.trees.plans.algebra.Filter;
 import org.apache.doris.nereids.trees.plans.algebra.Limit;
+import org.apache.doris.nereids.trees.plans.algebra.OneRowRelation;
 import org.apache.doris.nereids.trees.plans.algebra.Project;
 import org.apache.doris.nereids.trees.plans.algebra.Scan;
 import org.apache.doris.nereids.trees.plans.algebra.TopN;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAssertNumRows;
+import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.logical.LogicalLimit;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
 import org.apache.doris.nereids.trees.plans.logical.LogicalTopN;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribute;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalEmptyRelation;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalLimit;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalLocalQuickSort;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalNestedLoopJoin;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalOneRowRelation;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalQuickSort;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalTopN;
@@ -96,6 +103,11 @@ public class StatsCalculator extends 
DefaultPlanVisitor<StatsDeriveResult, Void>
         groupExpression.setStatDerived(true);
     }
 
+    @Override
+    public StatsDeriveResult visitLogicalEmptyRelation(LogicalEmptyRelation 
emptyRelation, Void context) {
+        return computeEmptyRelation(emptyRelation);
+    }
+
     @Override
     public StatsDeriveResult visitLogicalLimit(LogicalLimit<? extends Plan> 
limit, Void context) {
         return computeLimit(limit);
@@ -106,6 +118,11 @@ public class StatsCalculator extends 
DefaultPlanVisitor<StatsDeriveResult, Void>
         return computeLimit(limit);
     }
 
+    @Override
+    public StatsDeriveResult visitLogicalOneRowRelation(LogicalOneRowRelation 
oneRowRelation, Void context) {
+        return computeOneRowRelation(oneRowRelation);
+    }
+
     @Override
     public StatsDeriveResult visitLogicalAggregate(LogicalAggregate<? extends 
Plan> aggregate, Void context) {
         return computeAggregate(aggregate);
@@ -149,11 +166,21 @@ public class StatsCalculator extends 
DefaultPlanVisitor<StatsDeriveResult, Void>
         return groupExpression.getCopyOfChildStats(0);
     }
 
+    @Override
+    public StatsDeriveResult visitPhysicalEmptyRelation(PhysicalEmptyRelation 
emptyRelation, Void context) {
+        return computeEmptyRelation(emptyRelation);
+    }
+
     @Override
     public StatsDeriveResult visitPhysicalAggregate(PhysicalAggregate<? 
extends Plan> agg, Void context) {
         return computeAggregate(agg);
     }
 
+    @Override
+    public StatsDeriveResult 
visitPhysicalOneRowRelation(PhysicalOneRowRelation oneRowRelation, Void 
context) {
+        return computeOneRowRelation(oneRowRelation);
+    }
+
     @Override
     public StatsDeriveResult visitPhysicalOlapScan(PhysicalOlapScan olapScan, 
Void context) {
         return computeScan(olapScan);
@@ -320,4 +347,34 @@ public class StatsCalculator extends 
DefaultPlanVisitor<StatsDeriveResult, Void>
         statsDeriveResult.setSlotToColumnStats(columnsStats);
         return statsDeriveResult;
     }
+
+    private StatsDeriveResult computeOneRowRelation(OneRowRelation 
oneRowRelation) {
+        Map<Slot, ColumnStats> columnStatsMap = oneRowRelation.getProjects()
+                .stream()
+                .map(project -> {
+                    ColumnStats columnStats = new ColumnStats();
+                    columnStats.setNdv(1);
+                    // TODO: compute the literal size
+                    return Pair.of(project.toSlot(), columnStats);
+                })
+                .collect(Collectors.toMap(Pair::key, Pair::value));
+        int rowCount = 1;
+        return new StatsDeriveResult(rowCount, columnStatsMap);
+    }
+
+    private StatsDeriveResult computeEmptyRelation(EmptyRelation 
emptyRelation) {
+        Map<Slot, ColumnStats> columnStatsMap = emptyRelation.getProjects()
+                .stream()
+                .map(project -> {
+                    ColumnStats columnStats = new ColumnStats();
+                    columnStats.setNdv(0);
+                    columnStats.setMaxSize(0);
+                    columnStats.setNumNulls(0);
+                    columnStats.setAvgSize(0);
+                    return Pair.of(project.toSlot(), columnStats);
+                })
+                .collect(Collectors.toMap(Pair::key, Pair::value));
+        int rowCount = 0;
+        return new StatsDeriveResult(rowCount, columnStatsMap);
+    }
 }
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 ea8158ae22..04381846ab 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
@@ -84,4 +84,20 @@ public interface TreeNode<NODE_TYPE extends 
TreeNode<NODE_TYPE>> {
         });
         return (T) result.build();
     }
+
+    /**
+     * iterate top down and test predicate if contains any instance of the 
classes
+     * @param types classes array
+     * @return true if it has any instance of the types
+     */
+    default boolean containsType(Class...types) {
+        return anyMatch(node -> {
+            for (Class type : types) {
+                if (type.isInstance(node)) {
+                    return true;
+                }
+            }
+            return false;
+        });
+    }
 }
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 ec62dbe25e..6634933318 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
@@ -26,17 +26,23 @@ import com.google.common.collect.ImmutableList;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
+import javax.annotation.Nullable;
 
 /**
  * Reference to slot in expression.
  */
 public class SlotReference extends Slot {
     private final ExprId exprId;
+    // TODO: we should distinguish the name is alias or column name, and the 
column name should contains
+    //       `cluster:db`.`table`.`column`
     private final String name;
     private final List<String> qualifier;
     private final DataType dataType;
     private final boolean nullable;
 
+    private final Column column;
+
     public SlotReference(String name, DataType dataType) {
         this(NamedExpressionUtil.newExprId(), name, dataType, true, 
ImmutableList.of());
     }
@@ -49,6 +55,10 @@ public class SlotReference extends Slot {
         this(NamedExpressionUtil.newExprId(), name, dataType, nullable, 
qualifier);
     }
 
+    public SlotReference(ExprId exprId, String name, DataType dataType, 
boolean nullable, List<String> qualifier) {
+        this(exprId, name, dataType, nullable, qualifier, null);
+    }
+
     /**
      * Constructor for SlotReference.
      *
@@ -57,18 +67,22 @@ public class SlotReference extends Slot {
      * @param dataType slot reference logical data type
      * @param nullable true if nullable
      * @param qualifier slot reference qualifier
+     * @param column the column which this slot come from
      */
-    public SlotReference(ExprId exprId, String name, DataType dataType, 
boolean nullable, List<String> qualifier) {
+    public SlotReference(ExprId exprId, String name, DataType dataType, 
boolean nullable,
+            List<String> qualifier, @Nullable Column column) {
         this.exprId = exprId;
         this.name = name;
         this.dataType = dataType;
         this.qualifier = qualifier;
         this.nullable = nullable;
+        this.column = column;
     }
 
     public static SlotReference fromColumn(Column column, List<String> 
qualifier) {
         DataType dataType = 
DataType.convertFromCatalogDataType(column.getType());
-        return new SlotReference(column.getName(), dataType, 
column.isAllowNull(), qualifier);
+        return new SlotReference(NamedExpressionUtil.newExprId(), 
column.getName(), dataType,
+                column.isAllowNull(), qualifier, column);
     }
 
     @Override
@@ -134,8 +148,8 @@ public class SlotReference extends Slot {
         return Objects.hash(exprId);
     }
 
-    public Column getColumn() {
-        return new Column(name, dataType.toCatalogDataType());
+    public Optional<Column> getColumn() {
+        return Optional.ofNullable(column);
     }
 
     @Override
@@ -158,7 +172,7 @@ public class SlotReference extends Slot {
 
     @Override
     public Slot withQualifier(List<String> qualifiers) {
-        return new SlotReference(exprId, name, dataType, nullable, qualifiers);
+        return new SlotReference(exprId, name, dataType, nullable, qualifiers, 
column);
     }
 
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Substring.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Substring.java
index d33a66e6a9..ac90843349 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Substring.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/Substring.java
@@ -23,7 +23,7 @@ import 
org.apache.doris.nereids.trees.expressions.shape.TernaryExpression;
 import 
org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes;
 import org.apache.doris.nereids.types.DataType;
 import org.apache.doris.nereids.types.IntegerType;
-import org.apache.doris.nereids.types.StringType;
+import org.apache.doris.nereids.types.VarcharType;
 import org.apache.doris.nereids.types.coercion.AbstractDataType;
 import org.apache.doris.nereids.types.coercion.TypeCollection;
 
@@ -52,9 +52,24 @@ public class Substring extends BoundFunction implements 
TernaryExpression, Impli
         super("substring", str, pos, new IntegerLiteral(Integer.MAX_VALUE));
     }
 
+    public Expression getTarget() {
+        return child(0);
+    }
+
+    public Expression getPosition() {
+        return child(1);
+    }
+
+    public Expression getLength() {
+        return child(2);
+    }
+
     @Override
     public DataType getDataType() {
-        return StringType.INSTANCE;
+        if (getLength() instanceof IntegerLiteral) {
+            return VarcharType.createVarcharType(((IntegerLiteral) 
getLength()).getValue());
+        }
+        return VarcharType.SYSTEM_DEFAULT;
     }
 
     @Override
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java
index c80d2a30db..4ba8265c9a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/Literal.java
@@ -24,6 +24,7 @@ import 
org.apache.doris.nereids.trees.expressions.shape.LeafExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.DataType;
 
+import java.math.BigInteger;
 import java.util.Objects;
 
 /**
@@ -49,8 +50,16 @@ public abstract class Literal extends Expression implements 
LeafExpression {
     public static Literal of(Object value) {
         if (value == null) {
             return new NullLiteral();
+        } else if (value instanceof Byte) {
+            return new TinyIntLiteral(((Byte) value).byteValue());
+        } else if (value instanceof Short) {
+            return new SmallIntLiteral(((Short) value).shortValue());
         } else if (value instanceof Integer) {
             return new IntegerLiteral((Integer) value);
+        } else if (value instanceof Long) {
+            return new BigIntLiteral(((Long) value).longValue());
+        } else if (value instanceof BigInteger) {
+            return new LargeIntLiteral((BigInteger) value);
         } else if (value instanceof Boolean) {
             return new BooleanLiteral((Boolean) value);
         } else if (value instanceof String) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarcharLiteral.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarcharLiteral.java
index 7d43a3cd4d..99e40811d7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarcharLiteral.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/literal/VarcharLiteral.java
@@ -58,4 +58,9 @@ public class VarcharLiteral extends Literal {
     public LiteralExpr toLegacyLiteral() {
         return new StringLiteral(value);
     }
+
+    @Override
+    public String toString() {
+        return "'" + value + "'";
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
index 68aae991db..9ef8a7eae9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
@@ -25,6 +25,9 @@ public enum PlanType {
 
     // logical plan
     LOGICAL_SUBQUERY_ALIAS,
+    LOGICAL_UNBOUND_ONE_ROW_RELATION,
+    LOGICAL_EMPTY_RELATION,
+    LOGICAL_ONE_ROW_RELATION,
     LOGICAL_UNBOUND_RELATION,
     LOGICAL_BOUND_RELATION,
     LOGICAL_PROJECT,
@@ -42,6 +45,8 @@ public enum PlanType {
     GROUP_PLAN,
 
     // physical plan
+    PHYSICAL_EMPTY_RELATION,
+    PHYSICAL_ONE_ROW_RELATION,
     PHYSICAL_OLAP_SCAN,
     PHYSICAL_PROJECT,
     PHYSICAL_FILTER,
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/EmptyRelation.java
similarity index 68%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/EmptyRelation.java
index f0c536cf83..ed6a6907ad 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/EmptyRelation.java
@@ -17,22 +17,16 @@
 
 package org.apache.doris.nereids.trees.plans.algebra;
 
-import org.apache.doris.catalog.Table;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
 
-import java.util.Collections;
 import java.util.List;
 
 /**
- * Common interface for logical/physical scan.
+ * Common interface for logical/physical empty relation.
+ *
+ * e.g.
+ * select * from tbl limit 0
  */
-public interface Scan {
-    List<Expression> getExpressions();
-
-    Table getTable();
-
-    default List<Slot> getOutput() {
-        return Collections.emptyList();
-    }
+public interface EmptyRelation {
+    List<NamedExpression> getProjects();
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/OneRowRelation.java
similarity index 68%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
copy to 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/OneRowRelation.java
index f0c536cf83..2c63d4e856 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/OneRowRelation.java
@@ -17,22 +17,14 @@
 
 package org.apache.doris.nereids.trees.plans.algebra;
 
-import org.apache.doris.catalog.Table;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.analyzer.Relation;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
 
-import java.util.Collections;
 import java.util.List;
 
 /**
- * Common interface for logical/physical scan.
+ * Common interface for logical/physical OneRowRelation.
  */
-public interface Scan {
-    List<Expression> getExpressions();
-
-    Table getTable();
-
-    default List<Slot> getOutput() {
-        return Collections.emptyList();
-    }
+public interface OneRowRelation extends Relation {
+    List<NamedExpression> getProjects();
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
index f0c536cf83..37e0b75bd7 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.plans.algebra;
 
 import org.apache.doris.catalog.Table;
+import org.apache.doris.nereids.analyzer.Relation;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 
@@ -27,7 +28,7 @@ import java.util.List;
 /**
  * Common interface for logical/physical scan.
  */
-public interface Scan {
+public interface Scan extends Relation {
     List<Expression> getExpressions();
 
     Table getTable();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalEmptyRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalEmptyRelation.java
new file mode 100644
index 0000000000..0db6d661b3
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalEmptyRelation.java
@@ -0,0 +1,113 @@
+// 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.plans.logical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.algebra.EmptyRelation;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.nereids.util.Utils;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * A logical relation that contains empty row.
+ * e.g.
+ * select * from tbl limit 0
+ */
+public class LogicalEmptyRelation extends LogicalLeaf implements EmptyRelation 
{
+    private final List<NamedExpression> projects;
+
+    public LogicalEmptyRelation(List<NamedExpression> projects) {
+        this(projects, Optional.empty(), Optional.empty());
+    }
+
+    public LogicalEmptyRelation(List<NamedExpression> projects, 
Optional<GroupExpression> groupExpression,
+            Optional<LogicalProperties> logicalProperties) {
+        super(PlanType.LOGICAL_ONE_ROW_RELATION, groupExpression, 
logicalProperties);
+        this.projects = ImmutableList.copyOf(Objects.requireNonNull(projects, 
"projects can not be null"));
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitLogicalEmptyRelation(this, context);
+    }
+
+    @Override
+    public List<NamedExpression> getProjects() {
+        return projects;
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) 
{
+        return new LogicalEmptyRelation(projects, groupExpression, 
Optional.of(logicalPropertiesSupplier.get()));
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> 
logicalProperties) {
+        return new LogicalEmptyRelation(projects, Optional.empty(), 
logicalProperties);
+    }
+
+    @Override
+    public List<Slot> computeOutput() {
+        return projects.stream()
+                .map(NamedExpression::toSlot)
+                .collect(ImmutableList.toImmutableList());
+    }
+
+    @Override
+    public String toString() {
+        return Utils.toSqlString("LogicalEmptyRelation",
+                "projects", projects
+        );
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+        LogicalEmptyRelation that = (LogicalEmptyRelation) o;
+        return Objects.equals(projects, that.projects);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(projects);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
new file mode 100644
index 0000000000..3486fe7200
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOneRowRelation.java
@@ -0,0 +1,115 @@
+// 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.plans.logical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.algebra.OneRowRelation;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.nereids.util.Utils;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * A relation that contains only one row consist of some constant expressions.
+ * e.g. select 100, 'value'
+ */
+public class LogicalOneRowRelation extends LogicalLeaf implements 
OneRowRelation {
+    private final List<NamedExpression> projects;
+
+    public LogicalOneRowRelation(List<NamedExpression> projects) {
+        this(projects, Optional.empty(), Optional.empty());
+    }
+
+    private LogicalOneRowRelation(List<NamedExpression> projects, 
Optional<GroupExpression> groupExpression,
+            Optional<LogicalProperties> logicalProperties) {
+        super(PlanType.LOGICAL_ONE_ROW_RELATION, groupExpression, 
logicalProperties);
+        Preconditions.checkArgument(projects.stream().noneMatch(p -> 
p.containsType(Slot.class)),
+                "OneRowRelation can not contains any slot");
+        this.projects = ImmutableList.copyOf(Objects.requireNonNull(projects, 
"projects can not be null"));
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitLogicalOneRowRelation(this, context);
+    }
+
+    @Override
+    public List<NamedExpression> getProjects() {
+        return projects;
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return (List) projects;
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) 
{
+        return new LogicalOneRowRelation(projects, groupExpression, 
Optional.of(logicalPropertiesSupplier.get()));
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> 
logicalProperties) {
+        return new LogicalOneRowRelation(projects, Optional.empty(), 
logicalProperties);
+    }
+
+    @Override
+    public List<Slot> computeOutput() {
+        return projects.stream()
+                .map(NamedExpression::toSlot)
+                .collect(ImmutableList.toImmutableList());
+    }
+
+    @Override
+    public String toString() {
+        return Utils.toSqlString("LogicalOneRowRelation",
+                "projects", projects
+        );
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+        LogicalOneRowRelation that = (LogicalOneRowRelation) o;
+        return Objects.equals(projects, that.projects);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(projects);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java
index e28d0ecdef..ffb493013d 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java
@@ -25,6 +25,7 @@ import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
 
 import java.util.Optional;
+import javax.annotation.Nullable;
 
 /**
  * Abstract class for all concrete physical plan.
@@ -54,9 +55,9 @@ public abstract class AbstractPhysicalPlan extends 
AbstractPlan implements Physi
     }
 
     public AbstractPhysicalPlan(PlanType type, Optional<GroupExpression> 
groupExpression,
-            LogicalProperties logicalProperties, PhysicalProperties 
physicalProperties, Plan... children) {
+            LogicalProperties logicalProperties, @Nullable PhysicalProperties 
physicalProperties, Plan... children) {
         super(type, groupExpression, Optional.of(logicalProperties), children);
-        this.physicalProperties = physicalProperties;
+        this.physicalProperties = physicalProperties == null ? 
PhysicalProperties.ANY : physicalProperties;
     }
 
     public PhysicalProperties getPhysicalProperties() {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinary.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinary.java
index 5b5a8602c3..ab62f137c1 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinary.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinary.java
@@ -25,6 +25,7 @@ import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.PlanType;
 
 import java.util.Optional;
+import javax.annotation.Nullable;
 
 /**
  * Abstract class for all physical plan that have two children.
@@ -47,8 +48,8 @@ public abstract class PhysicalBinary<
     }
 
     public PhysicalBinary(PlanType type, Optional<GroupExpression> 
groupExpression,
-            LogicalProperties logicalProperties, PhysicalProperties 
physicalProperties, LEFT_CHILD_TYPE leftChild,
-            RIGHT_CHILD_TYPE rightChild) {
+            LogicalProperties logicalProperties, @Nullable PhysicalProperties 
physicalProperties,
+            LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
         super(type, groupExpression, logicalProperties, physicalProperties, 
leftChild, rightChild);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalEmptyRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalEmptyRelation.java
new file mode 100644
index 0000000000..20c4fc687e
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalEmptyRelation.java
@@ -0,0 +1,122 @@
+// 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.plans.physical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.properties.PhysicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.algebra.EmptyRelation;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.nereids.util.Utils;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * A physical relation that contains empty row.
+ * e.g.
+ * select * from tbl limit 0
+ */
+public class PhysicalEmptyRelation extends PhysicalLeaf implements 
EmptyRelation {
+    private final List<NamedExpression> projects;
+
+    public PhysicalEmptyRelation(List<NamedExpression> projects, 
LogicalProperties logicalProperties) {
+        this(projects, Optional.empty(), logicalProperties, null);
+    }
+
+    public PhysicalEmptyRelation(List<NamedExpression> projects, 
Optional<GroupExpression> groupExpression,
+            LogicalProperties logicalProperties, PhysicalProperties 
physicalProperties) {
+        super(PlanType.PHYSICAL_EMPTY_RELATION, groupExpression, 
logicalProperties, physicalProperties);
+        this.projects = ImmutableList.copyOf(Objects.requireNonNull(projects, 
"projects can not be null"));
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitPhysicalEmptyRelation(this, context);
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) 
{
+        return new PhysicalEmptyRelation(projects, groupExpression,
+                logicalPropertiesSupplier.get(), physicalProperties);
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> 
logicalProperties) {
+        return new PhysicalEmptyRelation(projects, Optional.empty(),
+                logicalProperties.get(), physicalProperties);
+    }
+
+    @Override
+    public List<Slot> computeOutput() {
+        return projects.stream()
+                .map(NamedExpression::toSlot)
+                .collect(ImmutableList.toImmutableList());
+    }
+
+    @Override
+    public String toString() {
+        return Utils.toSqlString("PhysicalEmptyRelation",
+                "projects", projects
+        );
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+        PhysicalEmptyRelation that = (PhysicalEmptyRelation) o;
+        return Objects.equals(projects, that.projects);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(projects);
+    }
+
+    @Override
+    public List<NamedExpression> getProjects() {
+        return projects;
+    }
+
+    @Override
+    public PhysicalPlan withPhysicalProperties(PhysicalProperties 
physicalProperties) {
+        return new PhysicalEmptyRelation(projects, Optional.empty(),
+                logicalPropertiesSupplier.get(), physicalProperties);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeaf.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeaf.java
index ee174a970c..8ca608994e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeaf.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeaf.java
@@ -24,6 +24,7 @@ import org.apache.doris.nereids.trees.plans.LeafPlan;
 import org.apache.doris.nereids.trees.plans.PlanType;
 
 import java.util.Optional;
+import javax.annotation.Nullable;
 
 /**
  * Abstract class for all physical plan that have no child.
@@ -40,7 +41,7 @@ public abstract class PhysicalLeaf extends 
AbstractPhysicalPlan implements LeafP
     }
 
     public PhysicalLeaf(PlanType type, Optional<GroupExpression> 
groupExpression,
-            LogicalProperties logicalProperties, PhysicalProperties 
physicalProperties) {
+            LogicalProperties logicalProperties, @Nullable PhysicalProperties 
physicalProperties) {
         super(type, groupExpression, logicalProperties, physicalProperties);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalOneRowRelation.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalOneRowRelation.java
new file mode 100644
index 0000000000..fc44e8da29
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalOneRowRelation.java
@@ -0,0 +1,113 @@
+// 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.plans.physical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.properties.PhysicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.algebra.OneRowRelation;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.nereids.util.Utils;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * A physical relation that contains only one row consist of some constant 
expressions.
+ * e.g. select 100, 'value'
+ */
+public class PhysicalOneRowRelation extends PhysicalLeaf implements 
OneRowRelation {
+    private final List<NamedExpression> projects;
+
+    public PhysicalOneRowRelation(List<NamedExpression> projects, 
LogicalProperties logicalProperties) {
+        this(projects, Optional.empty(), logicalProperties, null);
+    }
+
+    private PhysicalOneRowRelation(List<NamedExpression> projects, 
Optional<GroupExpression> groupExpression,
+            LogicalProperties logicalProperties, PhysicalProperties 
physicalProperties) {
+        super(PlanType.PHYSICAL_ONE_ROW_RELATION, groupExpression, 
logicalProperties, physicalProperties);
+        
Preconditions.checkArgument(projects.stream().allMatch(Expression::isConstant),
+                "OneRowRelation must consist of some constant expression");
+        this.projects = ImmutableList.copyOf(Objects.requireNonNull(projects, 
"projects can not be null"));
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitPhysicalOneRowRelation(this, context);
+    }
+
+    @Override
+    public List<NamedExpression> getProjects() {
+        return projects;
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return (List) projects;
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) 
{
+        return new PhysicalOneRowRelation(projects, groupExpression,
+                logicalPropertiesSupplier.get(), physicalProperties);
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> 
logicalProperties) {
+        return new PhysicalOneRowRelation(projects, Optional.empty(),
+                logicalProperties.get(), physicalProperties);
+    }
+
+    @Override
+    public String toString() {
+        return Utils.toSqlString("PhysicalOneRowRelation",
+                "expressions", projects
+        );
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        PhysicalOneRowRelation that = (PhysicalOneRowRelation) o;
+        return Objects.equals(projects, that.projects);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(projects);
+    }
+
+    @Override
+    public PhysicalOneRowRelation withPhysicalProperties(PhysicalProperties 
physicalProperties) {
+        return new PhysicalOneRowRelation(projects, Optional.empty(),
+                logicalPropertiesSupplier.get(), physicalProperties);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnary.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnary.java
index a387b7e5a3..5337938df0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnary.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnary.java
@@ -25,6 +25,7 @@ import org.apache.doris.nereids.trees.plans.PlanType;
 import org.apache.doris.nereids.trees.plans.UnaryPlan;
 
 import java.util.Optional;
+import javax.annotation.Nullable;
 
 /**
  * Abstract class for all physical plan that have one child.
@@ -43,7 +44,7 @@ public abstract class PhysicalUnary<CHILD_TYPE extends Plan>
     }
 
     public PhysicalUnary(PlanType type, Optional<GroupExpression> 
groupExpression,
-            LogicalProperties logicalProperties, PhysicalProperties 
physicalProperties, CHILD_TYPE child) {
+            LogicalProperties logicalProperties, @Nullable PhysicalProperties 
physicalProperties, CHILD_TYPE child) {
         super(type, groupExpression, logicalProperties, physicalProperties, 
child);
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java
index e50c81d608..e96cdeb564 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java
@@ -17,6 +17,7 @@
 
 package org.apache.doris.nereids.trees.plans.visitor;
 
+import org.apache.doris.nereids.analyzer.UnboundOneRowRelation;
 import org.apache.doris.nereids.analyzer.UnboundRelation;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
@@ -25,11 +26,13 @@ import 
org.apache.doris.nereids.trees.plans.commands.ExplainCommand;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
 import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAssertNumRows;
+import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalHaving;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.logical.LogicalLimit;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalSelectHint;
@@ -40,12 +43,14 @@ import 
org.apache.doris.nereids.trees.plans.physical.AbstractPhysicalSort;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalAssertNumRows;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribute;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalEmptyRelation;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalLimit;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalLocalQuickSort;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalNestedLoopJoin;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalOneRowRelation;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalQuickSort;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalRelation;
@@ -81,6 +86,18 @@ public abstract class PlanVisitor<R, C> {
         return visit(alias, context);
     }
 
+    public R visitUnboundOneRowRelation(UnboundOneRowRelation oneRowRelation, 
C context) {
+        return visit(oneRowRelation, context);
+    }
+
+    public R visitLogicalEmptyRelation(LogicalEmptyRelation emptyRelation, C 
context) {
+        return visit(emptyRelation, context);
+    }
+
+    public R visitLogicalOneRowRelation(LogicalOneRowRelation oneRowRelation, 
C context) {
+        return visit(oneRowRelation, context);
+    }
+
     public R visitUnboundRelation(UnboundRelation relation, C context) {
         return visit(relation, context);
     }
@@ -153,6 +170,14 @@ public abstract class PlanVisitor<R, C> {
         return visit(scan, context);
     }
 
+    public R visitPhysicalEmptyRelation(PhysicalEmptyRelation emptyRelation, C 
context) {
+        return visit(emptyRelation, context);
+    }
+
+    public R visitPhysicalOneRowRelation(PhysicalOneRowRelation 
oneRowRelation, C context) {
+        return visit(oneRowRelation, context);
+    }
+
     public R visitPhysicalOlapScan(PhysicalOlapScan olapScan, C context) {
         return visitPhysicalScan(olapScan, context);
     }
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 1b1060f0af..b786cefd58 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
@@ -33,12 +33,16 @@ import com.google.common.collect.ImmutableMap;
 
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
 import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Abstract class for all data type in Nereids.
  */
 public abstract class DataType implements AbstractDataType {
+    private static final Pattern VARCHAR_PATTERN = 
Pattern.compile("varchar(\\(\\d+\\))?");
 
     // use class and supplier here to avoid class load deadlock.
     private static final Map<Class<? extends NumericType>, Supplier<DataType>> 
PROMOTION_MAP
@@ -116,14 +120,21 @@ public abstract class DataType implements 
AbstractDataType {
     public static DataType convertFromString(String type) {
         // TODO: use a better way to resolve types
         // TODO: support varchar, char, decimal
-        switch (type.toLowerCase()) {
+        type = type.toLowerCase();
+        switch (type) {
             case "bool":
             case "boolean":
                 return BooleanType.INSTANCE;
+            case "tinyint":
+                return TinyIntType.INSTANCE;
+            case "smallint":
+                return SmallIntType.INSTANCE;
             case "int":
                 return IntegerType.INSTANCE;
             case "bigint":
                 return BigIntType.INSTANCE;
+            case "largeint":
+                return LargeIntType.INSTANCE;
             case "double":
                 return DoubleType.INSTANCE;
             case "string":
@@ -133,6 +144,10 @@ public abstract class DataType implements AbstractDataType 
{
             case "datetime":
                 return DateTimeType.INSTANCE;
             default:
+                Optional<VarcharType> varcharType = matchVarchar(type);
+                if (varcharType.isPresent()) {
+                    return varcharType.get();
+                }
                 throw new AnalysisException("Nereids do not support type: " + 
type);
         }
     }
@@ -229,4 +244,15 @@ public abstract class DataType implements AbstractDataType 
{
     }
 
     public abstract int width();
+
+    private static Optional<VarcharType> matchVarchar(String type) {
+        Matcher matcher = VARCHAR_PATTERN.matcher(type);
+        if (matcher.find()) {
+            VarcharType varcharType = matcher.groupCount() > 1
+                    ? 
VarcharType.createVarcharType(Integer.valueOf(matcher.group(1)))
+                    : VarcharType.SYSTEM_DEFAULT;
+            return Optional.of(varcharType);
+        }
+        return Optional.empty();
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/planner/SetOperationNode.java 
b/fe/fe-core/src/main/java/org/apache/doris/planner/SetOperationNode.java
index bd130a8957..ad6e4ee8fb 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/planner/SetOperationNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/planner/SetOperationNode.java
@@ -445,4 +445,18 @@ public abstract class SetOperationNode extends PlanNode {
         numInstances = Math.max(1, numInstances);
         return numInstances;
     }
+
+    public void finalizeForNereids(TupleDescriptor tupleDescriptor, 
List<SlotDescriptor> constExprSlots) {
+        materializedConstExprLists.clear();
+        for (List<Expr> exprList : constExprLists) {
+            Preconditions.checkState(exprList.size() == constExprSlots.size());
+            List<Expr> newExprList = Lists.newArrayList();
+            for (int i = 0; i < exprList.size(); ++i) {
+                if (constExprSlots.get(i).isMaterialized()) {
+                    newExprList.add(exprList.get(i));
+                }
+            }
+            materializedConstExprLists.add(newExprList);
+        }
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/UnionNode.java 
b/fe/fe-core/src/main/java/org/apache/doris/planner/UnionNode.java
index ef42258d71..40982d07e7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/planner/UnionNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/planner/UnionNode.java
@@ -29,11 +29,11 @@ import org.apache.doris.thrift.TPlanNodeType;
 import java.util.List;
 
 public class UnionNode extends SetOperationNode {
-    protected UnionNode(PlanNodeId id, TupleId tupleId) {
+    public UnionNode(PlanNodeId id, TupleId tupleId) {
         super(id, tupleId, "UNION", StatisticalType.UNION_NODE);
     }
 
-    protected UnionNode(PlanNodeId id, TupleId tupleId,
+    public UnionNode(PlanNodeId id, TupleId tupleId,
                         List<Expr> setOpResultExprs, boolean isInSubplan) {
         super(id, tupleId, "UNION", setOpResultExprs, isInSubplan, 
StatisticalType.UNION_NODE);
     }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoRewriteTest.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoRewriteTest.java
index 45fb703921..f4ecd3cf45 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoRewriteTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoRewriteTest.java
@@ -229,9 +229,9 @@ public class MemoRewriteTest implements 
PatternMatchSupported {
      * A -> A(B): will run into dead loop, we can not detect it in the group 
tree, because B usually not equals
      *            to other object (e.g. UnboundRelation), but can detect the 
rule's invoke times.
      *
-     *  limit(student)                        limit(1)                         
 limit(1)
-     *       |                      ->           |                   ->        
     |                 ->    ...
-     *     any                                  any                            
    any
+     *      limit(1)                             limit(1)                      
    limit(1)
+     *         |                      ->           |                   ->      
       |                 ->    ...
+     * UnboundRelation(student)            UnboundRelation(student)         
UnboundRelation(student)
      *
      * you should split A into some states:
      * 1. A(not rewrite)
@@ -310,8 +310,7 @@ public class MemoRewriteTest implements 
PatternMatchSupported {
                         logicalLimit().when(limit10::equals).then(limit -> 
limit)
                 )
                 .checkGroupNum(2)
-                .checkFirstRootLogicalPlan(limit10)
-                .matches(
+                .matchesFromRoot(
                         logicalLimit(
                                 logicalOlapScan().when(student::equals)
                         ).when(limit10::equals)
@@ -335,8 +334,7 @@ public class MemoRewriteTest implements 
PatternMatchSupported {
                         logicalLimit().when(limit10::equals).then(limit -> 
limit.withChildren(limit.child()))
                 )
                 .checkGroupNum(2)
-                .checkFirstRootLogicalPlan(limit10)
-                .matches(
+                .matchesFromRoot(
                         logicalLimit(
                                 logicalOlapScan().when(student::equals)
                         ).when(limit10::equals)
@@ -360,8 +358,7 @@ public class MemoRewriteTest implements 
PatternMatchSupported {
                         logicalLimit().when(limit10::equals).then(limit -> 
limit.child())
                 )
                 .checkGroupNum(1)
-                .checkFirstRootLogicalPlan(student)
-                .matches(
+                .matchesFromRoot(
                     logicalOlapScan().when(student::equals)
                 );
     }
@@ -383,8 +380,7 @@ public class MemoRewriteTest implements 
PatternMatchSupported {
                         
logicalLimit(logicalOlapScan()).when(limit10::equals).then(limit -> 
limit.child())
                 )
                 .checkGroupNum(1)
-                .checkFirstRootLogicalPlan(student)
-                .matches(
+                .matchesFromRoot(
                         logicalOlapScan().when(student::equals)
                 );
     }
@@ -407,8 +403,7 @@ public class MemoRewriteTest implements 
PatternMatchSupported {
                         logicalLimit(unboundRelation()).then(limit -> student)
                 )
                 .checkGroupNum(1)
-                .checkFirstRootLogicalPlan(student)
-                .matches(
+                .matchesFromRoot(
                         logicalOlapScan().when(student::equals)
                 );
     }
@@ -433,8 +428,7 @@ public class MemoRewriteTest implements 
PatternMatchSupported {
                         logicalLimit(unboundRelation()).then(limit -> limit5)
                 )
                 .checkGroupNum(2)
-                .checkFirstRootLogicalPlan(limit5)
-                .matches(
+                .matchesFromRoot(
                         logicalLimit(
                                 logicalOlapScan().when(student::equals)
                         ).when(limit5::equals)
@@ -460,8 +454,7 @@ public class MemoRewriteTest implements 
PatternMatchSupported {
                         logicalLimit().when(limit10::equals).then(limit -> 
limit5)
                 )
                 .checkGroupNum(2)
-                .checkFirstRootLogicalPlan(limit5)
-                .matches(
+                .matchesFromRoot(
                         logicalLimit(
                                 logicalOlapScan().when(student::equals)
                         ).when(limit5::equals)
@@ -547,8 +540,7 @@ public class MemoRewriteTest implements 
PatternMatchSupported {
                         logicalLimit(unboundRelation()).then(l -> limit10)
                 )
                 .checkGroupNum(3)
-                .checkFirstRootLogicalPlan(limit10)
-                .matches(
+                .matchesFromRoot(
                         logicalLimit(
                                 logicalLimit(
                                         logicalOlapScan().when(scan::equals)
@@ -587,8 +579,7 @@ public class MemoRewriteTest implements 
PatternMatchSupported {
                         )
                 )
                 .checkGroupNum(3)
-                .checkFirstRootLogicalPlan(limit5)
-                .matches(
+                .matchesFromRoot(
                         logicalLimit(
                                 logicalLimit(
                                         unboundRelation().when(student::equals)
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/HavingClauseTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/HavingClauseTest.java
index c2120ca019..655c347aa0 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/HavingClauseTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/HavingClauseTest.java
@@ -20,6 +20,8 @@ package org.apache.doris.nereids.parser;
 import org.apache.doris.common.ExceptionChecker;
 import org.apache.doris.nereids.datasets.tpch.AnalyzeCheckTestBase;
 import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewrite;
+import org.apache.doris.nereids.rules.expression.rewrite.rules.TypeCoercion;
 import org.apache.doris.nereids.trees.expressions.Add;
 import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.And;
@@ -30,8 +32,9 @@ import 
org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.expressions.functions.Count;
 import org.apache.doris.nereids.trees.expressions.functions.Min;
 import org.apache.doris.nereids.trees.expressions.functions.Sum;
-import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
-import org.apache.doris.nereids.types.IntegerType;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral;
+import org.apache.doris.nereids.types.TinyIntType;
 import org.apache.doris.nereids.util.FieldChecker;
 import org.apache.doris.nereids.util.PatternMatchSupported;
 import org.apache.doris.nereids.util.PlanChecker;
@@ -50,9 +53,9 @@ public class HavingClauseTest extends AnalyzeCheckTestBase 
implements PatternMat
         connectContext.setDatabase("default_cluster:test_having");
         createTables(
                 "CREATE TABLE t1 (\n"
-                        + "    pk INT,\n"
-                        + "    a1 INT,\n"
-                        + "    a2 INT\n"
+                        + "    pk TINYINT,\n"
+                        + "    a1 TINYINT,\n"
+                        + "    a2 TINYINT\n"
                         + ")\n"
                         + "DUPLICATE KEY (pk)\n"
                         + "DISTRIBUTED BY HASH (pk)\n"
@@ -60,9 +63,9 @@ public class HavingClauseTest extends AnalyzeCheckTestBase 
implements PatternMat
                         + "    'replication_num' = '1'\n"
                         + ");",
                 "CREATE TABLE t2 (\n"
-                        + "    pk INT,\n"
-                        + "    b1 INT,\n"
-                        + "    b2 INT\n"
+                        + "    pk TINYINT,\n"
+                        + "    b1 TINYINT,\n"
+                        + "    b2 TINYINT\n"
                         + ")\n"
                         + "DUPLICATE KEY (pk)\n"
                         + "DISTRIBUTED BY HASH (pk)\n"
@@ -81,7 +84,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase 
implements PatternMat
     public void testHavingGroupBySlot() throws Exception {
         String sql = "SELECT a1 FROM t1 GROUP BY a1 HAVING a1 > 0";
         SlotReference a1 = new SlotReference(
-                new ExprId(1), "a1", IntegerType.INSTANCE, true,
+                new ExprId(1), "a1", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         PlanChecker.from(connectContext).analyze(sql)
@@ -90,52 +93,55 @@ public class HavingClauseTest extends AnalyzeCheckTestBase 
implements PatternMat
                         logicalAggregate(
                             logicalOlapScan()
                         ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(a1)))
-                    ).when(FieldChecker.check("predicates", new 
GreaterThan(a1, new IntegerLiteral(0)))));
+                    ).when(FieldChecker.check("predicates", new 
GreaterThan(a1, new TinyIntLiteral((byte) 0)))));
         NamedExpressionUtil.clear();
 
         sql = "SELECT a1 as value FROM t1 GROUP BY a1 HAVING a1 > 0";
         a1 = new SlotReference(
-                new ExprId(2), "a1", IntegerType.INSTANCE, true,
+                new ExprId(2), "a1", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         Alias value = new Alias(new ExprId(0), a1, "value");
         PlanChecker.from(connectContext).analyze(sql)
+                .applyBottomUp(new ExpressionRewrite(TypeCoercion.INSTANCE))
                 .matchesFromRoot(
                     logicalFilter(
                         logicalAggregate(
                             logicalOlapScan()
                         ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(value)))
-                    ).when(FieldChecker.check("predicates", new 
GreaterThan(value.toSlot(), new IntegerLiteral(0)))));
+                    ).when(FieldChecker.check("predicates", new 
GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0)))));
         NamedExpressionUtil.clear();
 
         sql = "SELECT a1 as value FROM t1 GROUP BY a1 HAVING value > 0";
         PlanChecker.from(connectContext).analyze(sql)
+                .applyBottomUp(new ExpressionRewrite(TypeCoercion.INSTANCE))
                 .matchesFromRoot(
                     logicalFilter(
                         logicalAggregate(
                             logicalOlapScan()
                         ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(value)))
-                    ).when(FieldChecker.check("predicates", new 
GreaterThan(value.toSlot(), new IntegerLiteral(0)))));
+                    ).when(FieldChecker.check("predicates", new 
GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0)))));
         NamedExpressionUtil.clear();
 
         sql = "SELECT SUM(a2) FROM t1 GROUP BY a1 HAVING a1 > 0";
         a1 = new SlotReference(
-                new ExprId(1), "a1", IntegerType.INSTANCE, true,
+                new ExprId(1), "a1", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         SlotReference a2 = new SlotReference(
-                new ExprId(2), "a2", IntegerType.INSTANCE, true,
+                new ExprId(2), "a2", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         Alias sumA2 = new Alias(new ExprId(3), new Sum(a2), "SUM(a2)");
         PlanChecker.from(connectContext).analyze(sql)
+                .applyBottomUp(new ExpressionRewrite(TypeCoercion.INSTANCE))
                 .matchesFromRoot(
                     logicalProject(
                         logicalFilter(
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(sumA2, a1)))
-                        ).when(FieldChecker.check("predicates", new 
GreaterThan(a1, new IntegerLiteral(0))))
+                        ).when(FieldChecker.check("predicates", new 
GreaterThan(a1, new TinyIntLiteral((byte) 0))))
                     ).when(FieldChecker.check("projects", 
Lists.newArrayList(sumA2.toSlot()))));
         NamedExpressionUtil.clear();
     }
@@ -144,11 +150,11 @@ public class HavingClauseTest extends 
AnalyzeCheckTestBase implements PatternMat
     public void testHavingAggregateFunction() throws Exception {
         String sql = "SELECT a1 FROM t1 GROUP BY a1 HAVING SUM(a2) > 0";
         SlotReference a1 = new SlotReference(
-                new ExprId(1), "a1", IntegerType.INSTANCE, true,
+                new ExprId(1), "a1", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         SlotReference a2 = new SlotReference(
-                new ExprId(2), "a2", IntegerType.INSTANCE, true,
+                new ExprId(2), "a2", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         Alias sumA2 = new Alias(new ExprId(3), new Sum(a2), "sum(a2)");
@@ -159,7 +165,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase 
implements PatternMat
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(a1, sumA2)))
-                        ).when(FieldChecker.check("predicates", new 
GreaterThan(sumA2.toSlot(), new IntegerLiteral(0))))
+                        ).when(FieldChecker.check("predicates", new 
GreaterThan(sumA2.toSlot(), new TinyIntLiteral((byte) 0))))
                     ).when(FieldChecker.check("projects", 
Lists.newArrayList(a1.toSlot()))));
         NamedExpressionUtil.clear();
 
@@ -171,16 +177,16 @@ public class HavingClauseTest extends 
AnalyzeCheckTestBase implements PatternMat
                         logicalAggregate(
                             logicalOlapScan()
                         ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(a1, sumA2)))
-                    ).when(FieldChecker.check("predicates", new 
GreaterThan(sumA2.toSlot(), new IntegerLiteral(0)))));
+                    ).when(FieldChecker.check("predicates", new 
GreaterThan(sumA2.toSlot(), new TinyIntLiteral((byte) 0)))));
         NamedExpressionUtil.clear();
 
         sql = "SELECT a1, SUM(a2) as value FROM t1 GROUP BY a1 HAVING SUM(a2) 
> 0";
         a1 = new SlotReference(
-                new ExprId(2), "a1", IntegerType.INSTANCE, true,
+                new ExprId(2), "a1", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         a2 = new SlotReference(
-                new ExprId(3), "a2", IntegerType.INSTANCE, true,
+                new ExprId(3), "a2", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         Alias value = new Alias(new ExprId(0), new Sum(a2), "value");
@@ -190,7 +196,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase 
implements PatternMat
                         logicalAggregate(
                             logicalOlapScan()
                         ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(a1, value)))
-                    ).when(FieldChecker.check("predicates", new 
GreaterThan(value.toSlot(), new IntegerLiteral(0)))));
+                    ).when(FieldChecker.check("predicates", new 
GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0)))));
         NamedExpressionUtil.clear();
 
         sql = "SELECT a1, SUM(a2) as value FROM t1 GROUP BY a1 HAVING value > 
0";
@@ -200,21 +206,21 @@ public class HavingClauseTest extends 
AnalyzeCheckTestBase implements PatternMat
                         logicalAggregate(
                             logicalOlapScan()
                         ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(a1, value)))
-                    ).when(FieldChecker.check("predicates", new 
GreaterThan(value.toSlot(), new IntegerLiteral(0)))));
+                    ).when(FieldChecker.check("predicates", new 
GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0)))));
         NamedExpressionUtil.clear();
 
         sql = "SELECT a1, SUM(a2) FROM t1 GROUP BY a1 HAVING MIN(pk) > 0";
         a1 = new SlotReference(
-                new ExprId(1), "a1", IntegerType.INSTANCE, true,
+                new ExprId(1), "a1", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         a2 = new SlotReference(
-                new ExprId(2), "a2", IntegerType.INSTANCE, true,
+                new ExprId(2), "a2", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         sumA2 = new Alias(new ExprId(3), new Sum(a2), "SUM(a2)");
         SlotReference pk = new SlotReference(
-                new ExprId(0), "pk", IntegerType.INSTANCE, true,
+                new ExprId(0), "pk", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         Alias minPK = new Alias(new ExprId(4), new Min(pk), "min(pk)");
@@ -225,7 +231,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase 
implements PatternMat
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(a1, sumA2, minPK)))
-                        ).when(FieldChecker.check("predicates", new 
GreaterThan(minPK.toSlot(), new IntegerLiteral(0))))
+                        ).when(FieldChecker.check("predicates", new 
GreaterThan(minPK.toSlot(), new TinyIntLiteral((byte) 0))))
                     ).when(FieldChecker.check("projects", 
Lists.newArrayList(a1.toSlot(), sumA2.toSlot()))));
         NamedExpressionUtil.clear();
 
@@ -237,11 +243,11 @@ public class HavingClauseTest extends 
AnalyzeCheckTestBase implements PatternMat
                         logicalAggregate(
                             logicalOlapScan()
                         ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(a1, sumA1A2)))
-                    ).when(FieldChecker.check("predicates", new 
GreaterThan(sumA1A2.toSlot(), new IntegerLiteral(0)))));
+                    ).when(FieldChecker.check("predicates", new 
GreaterThan(sumA1A2.toSlot(), new TinyIntLiteral((byte) 0)))));
         NamedExpressionUtil.clear();
 
         sql = "SELECT a1, SUM(a1 + a2) FROM t1 GROUP BY a1 HAVING SUM(a1 + a2 
+ 3) > 0";
-        Alias sumA1A23 = new Alias(new ExprId(4), new Sum(new Add(new Add(a1, 
a2), new IntegerLiteral(3))),
+        Alias sumA1A23 = new Alias(new ExprId(4), new Sum(new Add(new Add(a1, 
a2), new TinyIntLiteral((byte) 3))),
                 "sum(((a1 + a2) + 3))");
         PlanChecker.from(connectContext).analyze(sql)
                 .matchesFromRoot(
@@ -250,7 +256,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase 
implements PatternMat
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(a1, sumA1A2, sumA1A23)))
-                        ).when(FieldChecker.check("predicates", new 
GreaterThan(sumA1A23.toSlot(), new IntegerLiteral(0))))
+                        ).when(FieldChecker.check("predicates", new 
GreaterThan(sumA1A23.toSlot(), new TinyIntLiteral((byte) 0))))
                     ).when(FieldChecker.check("projects", 
Lists.newArrayList(a1.toSlot(), sumA1A2.toSlot()))));
         NamedExpressionUtil.clear();
 
@@ -263,7 +269,7 @@ public class HavingClauseTest extends AnalyzeCheckTestBase 
implements PatternMat
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", 
Lists.newArrayList(a1, countStar)))
-                        ).when(FieldChecker.check("predicates", new 
GreaterThan(countStar.toSlot(), new IntegerLiteral(0))))
+                        ).when(FieldChecker.check("predicates", new 
GreaterThan(countStar.toSlot(), new TinyIntLiteral((byte) 0))))
                     ).when(FieldChecker.check("projects", 
Lists.newArrayList(a1.toSlot()))));
         NamedExpressionUtil.clear();
     }
@@ -272,15 +278,15 @@ public class HavingClauseTest extends 
AnalyzeCheckTestBase implements PatternMat
     void testJoin() throws Exception {
         String sql = "SELECT a1, sum(a2) FROM t1, t2 WHERE t1.pk = t2.pk GROUP 
BY a1 HAVING a1 > SUM(b1)";
         SlotReference a1 = new SlotReference(
-                new ExprId(1), "a1", IntegerType.INSTANCE, true,
+                new ExprId(1), "a1", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         SlotReference a2 = new SlotReference(
-                new ExprId(2), "a2", IntegerType.INSTANCE, true,
+                new ExprId(2), "a2", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         SlotReference b1 = new SlotReference(
-                new ExprId(4), "b1", IntegerType.INSTANCE, true,
+                new ExprId(4), "b1", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t2")
         );
         Alias sumA2 = new Alias(new ExprId(6), new Sum(a2), "sum(a2)");
@@ -332,22 +338,22 @@ public class HavingClauseTest extends 
AnalyzeCheckTestBase implements PatternMat
                 + "FROM t1, t2 WHERE t1.pk = t2.pk GROUP BY t1.pk, t1.pk + 1\n"
                 + "HAVING t1.pk > 0 AND COUNT(a1) + 1 > 0 AND SUM(a1 + a2) + 1 
> 0 AND v1 + 1 > 0 AND v1 > 0";
         SlotReference pk = new SlotReference(
-                new ExprId(1), "pk", IntegerType.INSTANCE, true,
+                new ExprId(1), "pk", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         SlotReference a1 = new SlotReference(
-                new ExprId(2), "a1", IntegerType.INSTANCE, true,
+                new ExprId(2), "a1", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
         SlotReference a2 = new SlotReference(
-                new ExprId(3), "a1", IntegerType.INSTANCE, true,
+                new ExprId(3), "a1", TinyIntType.INSTANCE, true,
                 ImmutableList.of("default_cluster:test_having", "t1")
         );
-        Alias pk1 = new Alias(new ExprId(7), new Add(pk, 
IntegerLiteral.of(1)), "(pk + 1)");
-        Alias pk11 = new Alias(new ExprId(8), new Add(new Add(pk, 
IntegerLiteral.of(1)), IntegerLiteral.of(1)), "((pk + 1) + 1)");
-        Alias pk2 = new Alias(new ExprId(9), new Add(pk, 
IntegerLiteral.of(2)), "(pk + 2)");
+        Alias pk1 = new Alias(new ExprId(7), new Add(pk, Literal.of((byte) 
1)), "(pk + 1)");
+        Alias pk11 = new Alias(new ExprId(8), new Add(new Add(pk, 
Literal.of((byte) 1)), Literal.of((byte) 1)), "((pk + 1) + 1)");
+        Alias pk2 = new Alias(new ExprId(9), new Add(pk, Literal.of((byte) 
2)), "(pk + 2)");
         Alias sumA1 = new Alias(new ExprId(10), new Sum(a1), "SUM(a1)");
-        Alias countA11 = new Alias(new ExprId(11), new Add(new Count(a1), 
IntegerLiteral.of(1)), "(COUNT(a1) + 1)");
+        Alias countA11 = new Alias(new ExprId(11), new Add(new Count(a1), 
Literal.of((byte) 1)), "(COUNT(a1) + 1)");
         Alias sumA1A2 = new Alias(new ExprId(12), new Sum(new Add(a1, a2)), 
"SUM((a1 + a2))");
         Alias v1 = new Alias(new ExprId(0), new Count(a2), "v1");
         PlanChecker.from(connectContext).analyze(sql)
@@ -368,12 +374,12 @@ public class HavingClauseTest extends 
AnalyzeCheckTestBase implements PatternMat
                                         new And(
                                                 new And(
                                                         new And(
-                                                                new 
GreaterThan(pk.toSlot(), IntegerLiteral.of(0)),
-                                                                new 
GreaterThan(countA11.toSlot(), IntegerLiteral.of(0))),
-                                                        new GreaterThan(new 
Add(sumA1A2.toSlot(), IntegerLiteral.of(1)), IntegerLiteral.of(0))),
-                                                new GreaterThan(new 
Add(v1.toSlot(), IntegerLiteral.of(1)), IntegerLiteral.of(0))
+                                                                new 
GreaterThan(pk.toSlot(), Literal.of((byte) 0)),
+                                                                new 
GreaterThan(countA11.toSlot(), Literal.of((byte) 0))),
+                                                        new GreaterThan(new 
Add(sumA1A2.toSlot(), Literal.of((byte) 1)), Literal.of((byte) 0))),
+                                                new GreaterThan(new 
Add(v1.toSlot(), Literal.of((byte) 1)), Literal.of((byte) 0))
                                         ),
-                                        new GreaterThan(v1.toSlot(), 
IntegerLiteral.of(0))
+                                        new GreaterThan(v1.toSlot(), 
Literal.of((byte) 0))
                                 )
                         ))
                     ).when(FieldChecker.check(
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteTest.java
index 80b70f56ca..2798e05a5b 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteTest.java
@@ -176,13 +176,13 @@ public class ExpressionRewriteTest {
         executor = new 
ExpressionRuleExecutor(ImmutableList.of(SimplifyCastRule.INSTANCE));
 
         // deduplicate
-        assertRewrite("CAST(1 AS int)", "1");
-        assertRewrite("CAST('str' AS string)", "'str'");
-        assertRewrite("CAST(CAST(1 AS int) AS int)", "1");
+        assertRewrite("CAST(1 AS tinyint)", "1");
+        assertRewrite("CAST('str' AS varchar)", "'str'");
+        assertRewrite("CAST(CAST(1 AS tinyint) AS tinyint)", "1");
 
         // deduplicate inside
-        assertRewrite("CAST(CAST('str' AS string) AS double)", "CAST('str' AS 
double)");
-        assertRewrite("CAST(CAST(1 AS int) AS double)", "CAST(1 AS double)");
+        assertRewrite("CAST(CAST('str' AS varchar) AS double)", "CAST('str' AS 
double)");
+        assertRewrite("CAST(CAST(1 AS tinyint) AS double)", "CAST(1 AS 
double)");
     }
 
     private void assertRewrite(String expression, String expected) {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/FieldChecker.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/FieldChecker.java
index 01fcc38bd9..f9adf43ed2 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/FieldChecker.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/FieldChecker.java
@@ -28,14 +28,14 @@ public class FieldChecker {
             Field field;
             try {
                 field = o.getClass().getDeclaredField(fieldName);
-            } catch (NoSuchFieldException e) {
-                throw new RuntimeException(e);
+            } catch (Throwable e) {
+                throw new RuntimeException("Check " + fieldName + " failed", 
e);
             }
             field.setAccessible(true);
             try {
                 Assertions.assertEquals(value, field.get(o));
-            } catch (IllegalAccessException e) {
-                throw new RuntimeException(e);
+            } catch (Throwable e) {
+                throw new RuntimeException("Check " + fieldName + " failed", 
e);
             }
             return true;
         };
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/MemoTestUtils.java 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/MemoTestUtils.java
index 4a2542ba17..faa9bdc10d 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/MemoTestUtils.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/MemoTestUtils.java
@@ -21,9 +21,6 @@ import org.apache.doris.analysis.UserIdentity;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.StatementContext;
-import org.apache.doris.nereids.memo.Group;
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.memo.Memo;
 import org.apache.doris.nereids.parser.NereidsParser;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
@@ -114,47 +111,6 @@ public class MemoTestUtils {
         }
     }
 
-    public static String printGroupTree(Memo memo) {
-        Group root = memo.getRoot();
-        StringBuilder builder = new StringBuilder();
-        printGroup(root, 0, true, builder);
-        return builder.toString();
-    }
-
-    private static void printGroup(Group group, int depth, boolean 
isLastGroup, StringBuilder builder) {
-        if (!group.getLogicalExpressions().isEmpty()) {
-            builder.append(getIdentStr(depth + 1))
-                    .append(group.getPhysicalExpressions().isEmpty() ? "+--" : 
"|--")
-                    .append("logicalExpressions:\n");
-            for (int i = 0; i < group.getLogicalExpressions().size(); i++) {
-                GroupExpression logicalExpression = 
group.getLogicalExpressions().get(i);
-                boolean isLastExpression = i + 1 == 
group.getLogicalExpressions().size();
-                printGroupExpression(logicalExpression, depth + 2, 
isLastExpression, builder);
-            }
-        }
-
-        if (!group.getPhysicalExpressions().isEmpty()) {
-            builder.append(getIdentStr(depth + 1)).append("+-- 
physicalExpressions:\n");
-            for (int i = 0; i < group.getPhysicalExpressions().size(); i++) {
-                GroupExpression logicalExpression = 
group.getPhysicalExpressions().get(i);
-                boolean isLastExpression = i + 1 == 
group.getPhysicalExpressions().size();
-                printGroupExpression(logicalExpression, depth + 2, 
isLastExpression, builder);
-            }
-        }
-    }
-
-    private static void printGroupExpression(GroupExpression groupExpression, 
int indent,
-            boolean isLastExpression, StringBuilder builder) {
-        builder.append(getIdentStr(indent))
-                .append(isLastExpression ? "+--" : "|--")
-                .append(groupExpression.getPlan().toString()).append("\n");
-        for (int i = 0; i < groupExpression.children().size(); i++) {
-            Group childGroup = groupExpression.children().get(i);
-            boolean isLastGroup = i + 1 == groupExpression.children().size();
-            printGroup(childGroup, indent + 1, isLastGroup, builder);
-        }
-    }
-
     private static String getIdentStr(int indent) {
         return StringUtils.repeat("   ", indent);
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
 b/regression-test/suites/nereids_syntax_p0/empty_relation.groovy
similarity index 62%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
copy to regression-test/suites/nereids_syntax_p0/empty_relation.groovy
index f0c536cf83..05e1af14ee 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
+++ b/regression-test/suites/nereids_syntax_p0/empty_relation.groovy
@@ -15,24 +15,18 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.plans.algebra;
+suite("empty_relation") {
+    // enable nereids and vectorized engine
+    sql "SET enable_vectorized_engine=true"
+    sql "SET enable_nereids_planner=true"
 
-import org.apache.doris.catalog.Table;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Common interface for logical/physical scan.
- */
-public interface Scan {
-    List<Expression> getExpressions();
-
-    Table getTable();
+    test {
+        sql "select *, substring(s_name, 1, 2) from supplier limit 0"
+        result([])
+    }
 
-    default List<Slot> getOutput() {
-        return Collections.emptyList();
+    explain {
+        sql "select *, substring(s_name, 1, 2) from supplier limit 0"
+        contains ":VEMPTYSET"
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
 b/regression-test/suites/nereids_syntax_p0/one_row_relation.groovy
similarity index 62%
copy from 
fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
copy to regression-test/suites/nereids_syntax_p0/one_row_relation.groovy
index f0c536cf83..9a73edf1bb 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Scan.java
+++ b/regression-test/suites/nereids_syntax_p0/one_row_relation.groovy
@@ -15,24 +15,13 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.plans.algebra;
+suite("one_row_relation") {
+    // enable nereids and vectorized engine
+    sql "SET enable_vectorized_engine=true"
+    sql "SET enable_nereids_planner=true"
 
-import org.apache.doris.catalog.Table;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Common interface for logical/physical scan.
- */
-public interface Scan {
-    List<Expression> getExpressions();
-
-    Table getTable();
-
-    default List<Slot> getOutput() {
-        return Collections.emptyList();
+    test {
+        sql "select 100, 'abc', substring('abc', 1, 2), 
substring(substring('abcdefg', 4, 3), 1, 2)"
+        result([[100, "abc", "ab", "de"]])
     }
 }


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

Reply via email to