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

[incubator-doris] branch master updated: [Enhancement] (Nereids) scalar expression rewrite framework (#9942)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 2fadaddda0 [Enhancement] (Nereids) scalar expression rewrite framework (#9942)
2fadaddda0 is described below

commit 2fadaddda0feba5608282e5a3cf58207a560de17
Author: shee <13...@users.noreply.github.com>
AuthorDate: Tue Jun 14 16:20:48 2022 +0800

    [Enhancement] (Nereids) scalar expression rewrite framework (#9942)
    
    Issue Number: close #9633
    
    The scalar expression is rewritten using the visitor pattern as a traversal.
    
    In the abstract class ExpressionVisitor, which contains all predicate to rewrite.
    
    We have provided a rewrite rules interface ExpressionRewriteRule, AbstractExpressionRewriteRule class implements the interface and expanded the ExpressionVisitor, if we want to realize an expression rewriting rules, Direct implementation AbstractExpressionRewriteRule provided in the method of traversing the predicate.
    
    There are two rules to refer: NormalizeExpressionRule and SimplifyNotExprRule
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |  3 +-
 .../apache/doris/nereids/analyzer/UnboundSlot.java | 12 ++++
 .../ParsingException.java}                         | 13 ++--
 .../doris/nereids/parser/LogicalPlanBuilder.java   | 21 ++++++
 .../org/apache/doris/nereids/parser/SqlParser.java | 55 ++++++++++-----
 .../rewrite/AbstractExpressionRewriteRule.java}    | 22 +++---
 .../rewrite/ExpressionRewriteContext.java}         | 10 +--
 .../expression/rewrite/ExpressionRewriteRule.java} | 11 ++-
 .../expression/rewrite/ExpressionRewriter.java     | 66 ++++++++++++++++++
 .../expression/rewrite/ExpressionVisitor.java}     | 29 ++++----
 .../expression/rewrite/RewriteHelper.java}         | 13 ++--
 .../rewrite/rules/NormalizeExpressionRule.java     | 63 +++++++++++++++++
 .../rewrite/rules/SimplifyNotExprRule.java         | 80 ++++++++++++++++++++++
 .../org/apache/doris/nereids/trees/NodeType.java   |  1 +
 .../trees/expressions/BinaryExpression.java        | 10 +++
 .../trees/expressions/ComparisonPredicate.java     | 25 +++++++
 .../doris/nereids/trees/expressions/EqualTo.java   | 11 +++
 .../nereids/trees/expressions/Expression.java      | 17 +++++
 .../nereids/trees/expressions/GreaterThan.java     | 11 +++
 .../trees/expressions/GreaterThanEqual.java        | 11 +++
 .../doris/nereids/trees/expressions/LessThan.java  | 11 +++
 .../nereids/trees/expressions/LessThanEqual.java   | 11 +++
 .../doris/nereids/trees/expressions/Literal.java   | 33 +++++++++
 .../nereids/trees/expressions/NamedExpression.java |  5 ++
 .../doris/nereids/trees/expressions/Not.java       | 30 ++++++++
 .../nereids/trees/expressions/UnaryExpression.java |  4 ++
 .../expression/rewrite/ExpressionRewriteTest.java  | 65 ++++++++++++++++++
 27 files changed, 572 insertions(+), 71 deletions(-)

diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index d4c580dfc7..edf28c1d7e 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -141,7 +141,8 @@ expression
     ;
 
 booleanExpression
-    : valueExpression                                              #predicated
+    : NOT booleanExpression                                                                  #not
+    | valueExpression                                                                        #predicated
     ;
 
 valueExpression
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
index 230d015d3a..c0a20a529f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
@@ -64,4 +64,16 @@ public class UnboundSlot extends Slot<UnboundSlot> {
     public String toString() {
         return "'" + getName();
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        UnboundSlot other = (UnboundSlot) o;
+        return nameParts.containsAll(other.getNameParts());
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/exceptions/ParsingException.java
similarity index 71%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/exceptions/ParsingException.java
index 991edda91c..fb72872fdf 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/exceptions/ParsingException.java
@@ -15,14 +15,15 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.expressions;
-
-import org.apache.doris.nereids.trees.UnaryNode;
+package org.apache.doris.nereids.exceptions;
 
 /**
- * Abstract class for all expression that have one child.
+ * sql parsing exception.
  */
-public interface UnaryExpression<EXPR_TYPE extends UnaryExpression<EXPR_TYPE, CHILD_TYPE>,
-        CHILD_TYPE extends Expression> extends UnaryNode<EXPR_TYPE, CHILD_TYPE> {
+public class ParsingException extends RuntimeException {
+
+    public ParsingException(String message) {
+        super(message);
+    }
 
 }
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 5b20cfcb3d..77c827433e 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
@@ -23,6 +23,7 @@ import org.apache.doris.nereids.DorisParser.BooleanLiteralContext;
 import org.apache.doris.nereids.DorisParser.ColumnReferenceContext;
 import org.apache.doris.nereids.DorisParser.ComparisonContext;
 import org.apache.doris.nereids.DorisParser.DereferenceContext;
+import org.apache.doris.nereids.DorisParser.ExpressionContext;
 import org.apache.doris.nereids.DorisParser.FromClauseContext;
 import org.apache.doris.nereids.DorisParser.IdentifierListContext;
 import org.apache.doris.nereids.DorisParser.IdentifierSeqContext;
@@ -32,6 +33,7 @@ import org.apache.doris.nereids.DorisParser.JoinRelationContext;
 import org.apache.doris.nereids.DorisParser.MultipartIdentifierContext;
 import org.apache.doris.nereids.DorisParser.NamedExpressionContext;
 import org.apache.doris.nereids.DorisParser.NamedExpressionSeqContext;
+import org.apache.doris.nereids.DorisParser.NotContext;
 import org.apache.doris.nereids.DorisParser.NullLiteralContext;
 import org.apache.doris.nereids.DorisParser.PredicatedContext;
 import org.apache.doris.nereids.DorisParser.QualifiedNameContext;
@@ -149,6 +151,12 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
         return ParserUtils.withOrigin(ctx, f);
     }
 
+    @Override
+    public Expression visitExpression(ExpressionContext ctx) {
+        Supplier<Expression> f = () -> (Expression) visit(ctx.booleanExpression());
+        return ParserUtils.withOrigin(ctx, f);
+    }
+
     @Override
     public List<Expression> visitNamedExpressionSeq(NamedExpressionSeqContext ctx) {
         List<Expression> expressions = Lists.newArrayList();
@@ -387,6 +395,19 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
         }
     }
 
+    /**
+     * Create a not expression.
+     * format: NOT Expression
+     * for example:
+     * not 1
+     * not 1=1
+     */
+    @Override
+    public Expression visitNot(NotContext ctx) {
+        Expression child = expression(ctx.booleanExpression());
+        return new Not(child);
+    }
+
     /**
      * Create a predicated expression. A predicated expression is a normal expression with a
      * predicate attached to it, for example:
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/SqlParser.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/SqlParser.java
index 9c734e1edc..b4179b7d1f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/SqlParser.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/SqlParser.java
@@ -19,6 +19,9 @@ package org.apache.doris.nereids.parser;
 
 import org.apache.doris.nereids.DorisLexer;
 import org.apache.doris.nereids.DorisParser;
+import org.apache.doris.nereids.exceptions.ParsingException;
+import org.apache.doris.nereids.trees.TreeNode;
+import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 
 import org.antlr.v4.runtime.CharStreams;
@@ -27,6 +30,8 @@ import org.antlr.v4.runtime.ParserRuleContext;
 import org.antlr.v4.runtime.atn.PredictionMode;
 import org.antlr.v4.runtime.misc.ParseCancellationException;
 
+import java.util.function.Function;
+
 /**
  * Sql parser, convert sql DSL to logical plan.
  */
@@ -40,30 +45,42 @@ public class SqlParser {
      * @throws Exception throw exception when failed in parse stage
      */
     public LogicalPlan parse(String sql) throws Exception {
-        DorisLexer lexer = new DorisLexer(new CaseInsensitiveStream(CharStreams.fromString(sql)));
+        return (LogicalPlan) parse(sql, DorisParser::singleStatement);
+    }
 
-        CommonTokenStream tokenStream = new CommonTokenStream(lexer);
-        DorisParser parser = new DorisParser(tokenStream);
+    private TreeNode parse(String sql, Function<DorisParser, ParserRuleContext> parseFunction) {
+        try {
+            DorisLexer lexer = new DorisLexer(new CaseInsensitiveStream(CharStreams.fromString(sql)));
+            CommonTokenStream tokenStream = new CommonTokenStream(lexer);
+            DorisParser parser = new DorisParser(tokenStream);
 
-        // parser.addParseListener(PostProcessor)
-        // parser.removeErrorListeners()
-        // parser.addErrorListener(ParseErrorListener)
+            // parser.addParseListener(PostProcessor)
+            // parser.removeErrorListeners()
+            // parser.addErrorListener(ParseErrorListener)
 
-        ParserRuleContext tree;
-        try {
-            // first, try parsing with potentially faster SLL mode
-            parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
-            tree = parser.singleStatement();
-        } catch (ParseCancellationException ex) {
-            // if we fail, parse with LL mode
-            tokenStream.seek(0); // rewind input stream
-            parser.reset();
+            ParserRuleContext tree;
+            try {
+                // first, try parsing with potentially faster SLL mode
+                parser.getInterpreter().setPredictionMode(PredictionMode.SLL);
+                tree = parseFunction.apply(parser);
+            } catch (ParseCancellationException ex) {
+                // if we fail, parse with LL mode
+                tokenStream.seek(0); // rewind input stream
+                parser.reset();
 
-            parser.getInterpreter().setPredictionMode(PredictionMode.LL);
-            tree = parser.singleStatement();
+                parser.getInterpreter().setPredictionMode(PredictionMode.LL);
+                tree = parseFunction.apply(parser);
+            }
+
+            LogicalPlanBuilder logicalPlanBuilder = new LogicalPlanBuilder();
+            return (TreeNode) logicalPlanBuilder.visit(tree);
+
+        } catch (StackOverflowError e) {
+            throw new ParsingException(e.getMessage());
         }
+    }
 
-        LogicalPlanBuilder logicalPlanBuilder = new LogicalPlanBuilder();
-        return (LogicalPlan) logicalPlanBuilder.visit(tree);
+    public Expression createExpression(String expression) {
+        return (Expression) parse(expression, DorisParser::expression);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/AbstractExpressionRewriteRule.java
similarity index 59%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/AbstractExpressionRewriteRule.java
index 15ddcf54b5..741fa057bc 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/AbstractExpressionRewriteRule.java
@@ -15,27 +15,23 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.expressions;
+package org.apache.doris.nereids.rules.expression.rewrite;
 
-import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.Expression;
 
 /**
- * Not expression: not a.
+ * Base class of expression rewrite rule.
  */
-public class Not<CHILD_TYPE extends Expression> extends Expression<Not<CHILD_TYPE>>
-        implements UnaryExpression<Not<CHILD_TYPE>, CHILD_TYPE> {
-    public Not(CHILD_TYPE child) {
-        super(NodeType.NOT, child);
-    }
+public abstract class AbstractExpressionRewriteRule extends ExpressionVisitor<Expression, ExpressionRewriteContext>
+        implements ExpressionRewriteRule {
 
     @Override
-    public boolean nullable() throws UnboundException {
-        return child().nullable();
+    public Expression rewrite(Expression expr, ExpressionRewriteContext ctx) {
+        return (Expression) expr.accept(this, ctx);
     }
 
     @Override
-    public String toString() {
-        return "( not " + child() + ")";
+    public Expression visitExpression(Expression expr, ExpressionRewriteContext ctx) {
+        return expr;
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteContext.java
similarity index 70%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteContext.java
index 991edda91c..13e2267f2b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteContext.java
@@ -15,14 +15,10 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.expressions;
-
-import org.apache.doris.nereids.trees.UnaryNode;
+package org.apache.doris.nereids.rules.expression.rewrite;
 
 /**
- * Abstract class for all expression that have one child.
+ * expression rewrite context.
  */
-public interface UnaryExpression<EXPR_TYPE extends UnaryExpression<EXPR_TYPE, CHILD_TYPE>,
-        CHILD_TYPE extends Expression> extends UnaryNode<EXPR_TYPE, CHILD_TYPE> {
-
+public class ExpressionRewriteContext {
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteRule.java
similarity index 70%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteRule.java
index 991edda91c..c9ec2f92f9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteRule.java
@@ -15,14 +15,13 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.expressions;
+package org.apache.doris.nereids.rules.expression.rewrite;
 
-import org.apache.doris.nereids.trees.UnaryNode;
+import org.apache.doris.nereids.trees.expressions.Expression;
 
 /**
- * Abstract class for all expression that have one child.
+ * The interface of expression rewrite rule.
  */
-public interface UnaryExpression<EXPR_TYPE extends UnaryExpression<EXPR_TYPE, CHILD_TYPE>,
-        CHILD_TYPE extends Expression> extends UnaryNode<EXPR_TYPE, CHILD_TYPE> {
-
+public interface ExpressionRewriteRule {
+    Expression rewrite(Expression expr, ExpressionRewriteContext ctx);
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriter.java
new file mode 100644
index 0000000000..6e2a060ddd
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriter.java
@@ -0,0 +1,66 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.expression.rewrite;
+
+import org.apache.doris.nereids.rules.expression.rewrite.rules.NormalizeExpressionRule;
+import org.apache.doris.nereids.rules.expression.rewrite.rules.SimplifyNotExprRule;
+import org.apache.doris.nereids.trees.expressions.Expression;
+
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
+/**
+ * Expression rewrite entry, which contains all rewrite rules.
+ */
+public class ExpressionRewriter {
+
+    public static final List<ExpressionRewriteRule> REWRITE_RULES = Lists.newArrayList(
+        new SimplifyNotExprRule(),
+        new NormalizeExpressionRule()
+    );
+
+    private final ExpressionRewriteContext ctx;
+    private final List<ExpressionRewriteRule> rules;
+
+    public ExpressionRewriter(List<ExpressionRewriteRule> rules) {
+        this.rules = rules;
+        this.ctx = new ExpressionRewriteContext();
+    }
+
+    public ExpressionRewriter(ExpressionRewriteRule rule) {
+        this.rules = Lists.newArrayList(rule);
+        this.ctx = new ExpressionRewriteContext();
+    }
+
+    /**
+     * Given an expression, returns a rewritten expression.
+     */
+    public Expression rewrite(Expression root) {
+        Expression result = root;
+        for (ExpressionRewriteRule rule : rules) {
+            result = applyRule(result, rule);
+        }
+        return result;
+    }
+
+    private Expression applyRule(Expression expr, ExpressionRewriteRule rule) {
+        return rule.rewrite(expr, ctx);
+    }
+
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionVisitor.java
similarity index 50%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionVisitor.java
index 20517d7523..e3fb595ce4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionVisitor.java
@@ -15,28 +15,29 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.expressions;
+package org.apache.doris.nereids.rules.expression.rewrite;
 
-import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Literal;
+import org.apache.doris.nereids.trees.expressions.Not;
 
 /**
- * Equal to expression: a = b.
+ * Use the visitor pattern to iterate over all expressions for expression rewriting.
  */
-public class EqualTo<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE extends Expression>
-        extends ComparisonPredicate<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
+public abstract class ExpressionVisitor<R, C> {
 
-    public EqualTo(LEFT_CHILD_TYPE left, RIGHT_CHILD_TYPE right) {
-        super(NodeType.EQUAL_TO, left, right);
+    public abstract R visitExpression(Expression expr, C context);
+
+    public R visitNot(Not expr, C context) {
+        return visitExpression(expr, context);
     }
 
-    @Override
-    public boolean nullable() throws UnboundException {
-        return left().nullable() || right().nullable();
+    public R visitComparisonPredicate(ComparisonPredicate expr, C context) {
+        return visitExpression(expr, context);
     }
 
-    @Override
-    public String toString() {
-        return "(" + left() + " = " + right() + ")";
+    public R visitLiteral(Literal expr, C context) {
+        return visitExpression(expr, context);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/RewriteHelper.java
similarity index 71%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/RewriteHelper.java
index 991edda91c..6f6f1758b6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/RewriteHelper.java
@@ -15,14 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees.expressions;
+package org.apache.doris.nereids.rules.expression.rewrite;
 
-import org.apache.doris.nereids.trees.UnaryNode;
+import org.apache.doris.nereids.trees.expressions.Expression;
 
 /**
- * Abstract class for all expression that have one child.
+ * Expression rewrite helper class.
  */
-public interface UnaryExpression<EXPR_TYPE extends UnaryExpression<EXPR_TYPE, CHILD_TYPE>,
-        CHILD_TYPE extends Expression> extends UnaryNode<EXPR_TYPE, CHILD_TYPE> {
+public class RewriteHelper {
+
+    public static boolean isConstant(Expression expr) {
+        return expr.isConstant();
+    }
 
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/NormalizeExpressionRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/NormalizeExpressionRule.java
new file mode 100644
index 0000000000..8da9c1e5f9
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/NormalizeExpressionRule.java
@@ -0,0 +1,63 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.expression.rewrite.rules;
+
+import org.apache.doris.nereids.rules.expression.rewrite.AbstractExpressionRewriteRule;
+import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewriteContext;
+import org.apache.doris.nereids.rules.expression.rewrite.RewriteHelper;
+import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.GreaterThan;
+import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
+import org.apache.doris.nereids.trees.expressions.LessThan;
+import org.apache.doris.nereids.trees.expressions.LessThanEqual;
+
+/**
+ * Normalizes binary predicates of the form 'expr' op 'slot' so that the slot is on the left-hand side.
+ * For example:
+ * 5 > id -> id < 5
+ */
+public class NormalizeExpressionRule extends AbstractExpressionRewriteRule {
+
+    public static NormalizeExpressionRule INSTANCE = new NormalizeExpressionRule();
+
+    @Override
+    public Expression visitComparisonPredicate(ComparisonPredicate expr, ExpressionRewriteContext context) {
+
+        if (RewriteHelper.isConstant(expr.left()) && !RewriteHelper.isConstant(expr.right())) {
+            NodeType exprType = expr.getType();
+            switch (exprType) {
+                case EQUAL_TO:
+                    return new EqualTo(expr.right(), expr.left());
+                case GREATER_THAN:
+                    return new LessThan(expr.right(), expr.left());
+                case GREATER_THAN_EQUAL:
+                    return new LessThanEqual(expr.right(), expr.left());
+                case LESS_THAN:
+                    return new GreaterThan(expr.right(), expr.left());
+                case LESS_THAN_EQUAL:
+                    return new GreaterThanEqual(expr.right(), expr.left());
+                default:
+                    return expr;
+            }
+        }
+        return expr;
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java
new file mode 100644
index 0000000000..f8025e8f39
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java
@@ -0,0 +1,80 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.expression.rewrite.rules;
+
+import org.apache.doris.nereids.rules.expression.rewrite.AbstractExpressionRewriteRule;
+import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewriteContext;
+import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.GreaterThan;
+import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
+import org.apache.doris.nereids.trees.expressions.LessThan;
+import org.apache.doris.nereids.trees.expressions.LessThanEqual;
+import org.apache.doris.nereids.trees.expressions.Not;
+
+/**
+ * Rewrite rule of NOT expression.
+ * For example:
+ * not a -> not a.
+ * not not a -> a.
+ * not not not a -> not a.
+ * not a > b -> a <= b.
+ * not a < b -> a >= b.
+ * not a >= b -> a < b.
+ * not a <= b -> a > b.
+ * not a=b -> not a=b.
+ */
+public class SimplifyNotExprRule extends AbstractExpressionRewriteRule {
+
+    public static SimplifyNotExprRule INSTANCE = new SimplifyNotExprRule();
+
+
+    @Override
+    public Expression visitNot(Not expr, ExpressionRewriteContext context) {
+
+        Expression child = expr.child();
+
+        if (child instanceof ComparisonPredicate) {
+            ComparisonPredicate cp = (ComparisonPredicate) expr.child();
+            Expression left =  rewrite(cp.left(), context);
+            Expression right = rewrite(cp.right(), context);
+            NodeType type = cp.getType();
+            switch (type) {
+                case GREATER_THAN:
+                    return new LessThanEqual(left, right);
+                case GREATER_THAN_EQUAL:
+                    return new LessThan(left, right);
+                case LESS_THAN:
+                    return new GreaterThanEqual(left, right);
+                case LESS_THAN_EQUAL:
+                    return new GreaterThan(left, right);
+                default:
+                    return expr;
+            }
+        }
+
+        if (child instanceof Not) {
+            Not son = (Not) child;
+            return rewrite(son.child(), context);
+        }
+
+        return expr;
+    }
+
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java
index 19644e7930..1f22f3020f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java
@@ -41,6 +41,7 @@ public enum NodeType {
     NULL_SAFE_EQUAL,
     NOT,
     ALIAS,
+    COMPOUND,
 
     // pattern
     PATTERN,
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryExpression.java
index 9cd3d9abc4..b031de9d0f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryExpression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/BinaryExpression.java
@@ -28,4 +28,14 @@ public interface BinaryExpression<
             RIGHT_CHILD_TYPE extends Expression>
         extends BinaryNode<EXPR_TYPE, LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
 
+    @Override
+    default LEFT_CHILD_TYPE left() {
+        return child(0);
+    }
+
+    @Override
+    default RIGHT_CHILD_TYPE right() {
+        return child(1);
+    }
+
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java
index 4f3307d9ab..88fc03c2f9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java
@@ -18,10 +18,13 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
+import org.apache.doris.nereids.rules.expression.rewrite.ExpressionVisitor;
 import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DataType;
 
+import java.util.Objects;
+
 /**
  * Comparison predicate expression.
  * Such as: "=", "<", "<=", ">", ">=", "<=>"
@@ -50,4 +53,26 @@ public class ComparisonPredicate<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD
     public String sql() {
         return toString();
     }
+
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitComparisonPredicate(this, context);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type, left(), right());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        ComparisonPredicate other = (ComparisonPredicate) o;
+        return (type == other.getType()) && Objects.equals(left(), other.left())
+                && Objects.equals(right(), other.right());
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
index 20517d7523..3f66da9106 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
@@ -19,6 +19,11 @@ package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.TreeNode;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
 
 /**
  * Equal to expression: a = b.
@@ -39,4 +44,10 @@ public class EqualTo<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE extend
     public String toString() {
         return "(" + left() + " = " + right() + ")";
     }
+
+    @Override
+    public EqualTo newChildren(List<TreeNode> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new EqualTo((Expression) children.get(0), (Expression) children.get(1));
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
index af3b59687c..b52c58b023 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
@@ -18,6 +18,7 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
+import org.apache.doris.nereids.rules.expression.rewrite.ExpressionVisitor;
 import org.apache.doris.nereids.trees.AbstractTreeNode;
 import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.TreeNode;
@@ -47,6 +48,10 @@ public abstract class Expression<EXPR_TYPE extends Expression<EXPR_TYPE>>
         throw new UnboundException("nullable");
     }
 
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitExpression(this, context);
+    }
+
     @Override
     public List<Expression> children() {
         return (List) children;
@@ -61,4 +66,16 @@ public abstract class Expression<EXPR_TYPE extends Expression<EXPR_TYPE>>
     public EXPR_TYPE newChildren(List<TreeNode> children) {
         throw new RuntimeException();
     }
+
+    /**
+     * Whether the expression is a constant.
+     */
+    public boolean isConstant() {
+        for (Expression child : children()) {
+            if (child.isConstant()) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java
index 99ce4cb7b9..12497ff94e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java
@@ -19,6 +19,11 @@ package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.TreeNode;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
 
 /**
  * Greater than expression: a > b.
@@ -44,4 +49,10 @@ public class GreaterThan<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE ex
     public String toString() {
         return "(" + left() + " > " + right() + ")";
     }
+
+    @Override
+    public GreaterThan newChildren(List<TreeNode> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new GreaterThan((Expression) children.get(0), (Expression) children.get(1));
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java
index 4e7a2f92dc..e20499f6c2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java
@@ -19,6 +19,11 @@ package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.TreeNode;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
 
 /**
  * Greater than and equal expression: a >= b.
@@ -44,4 +49,10 @@ public class GreaterThanEqual<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TY
     public String toString() {
         return "(" + left() + " >= " + right() + ")";
     }
+
+    @Override
+    public GreaterThanEqual newChildren(List<TreeNode> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new GreaterThanEqual((Expression) children.get(0), (Expression) children.get(1));
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java
index cfea4676de..3e97d42102 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java
@@ -19,6 +19,11 @@ package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.TreeNode;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
 
 /**
  * Less than expression: a < b.
@@ -44,4 +49,10 @@ public class LessThan<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE exten
     public String toString() {
         return "(" + left() + " < " + right() + ")";
     }
+
+    @Override
+    public LessThan newChildren(List<TreeNode> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new LessThan((Expression) children.get(0), (Expression) children.get(1));
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java
index 5ba2c1a5bb..2a759a4973 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java
@@ -19,6 +19,11 @@ package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.TreeNode;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
 
 /**
  * Less than and equal expression: a <= b.
@@ -44,4 +49,10 @@ public class LessThanEqual<LEFT_CHILD_TYPE extends Expression, RIGHT_CHILD_TYPE
     public String toString() {
         return "(" + left() + " <= " + right() + ")";
     }
+
+    @Override
+    public LessThanEqual newChildren(List<TreeNode> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new LessThanEqual((Expression) children.get(0), (Expression) children.get(1));
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java
index 79e830d91d..244d523fb6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java
@@ -20,6 +20,7 @@ package org.apache.doris.nereids.trees.expressions;
 import org.apache.doris.analysis.Expr;
 import org.apache.doris.analysis.IntLiteral;
 import org.apache.doris.nereids.exceptions.UnboundException;
+import org.apache.doris.nereids.rules.expression.rewrite.ExpressionVisitor;
 import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DataType;
@@ -27,6 +28,8 @@ import org.apache.doris.nereids.types.IntegerType;
 import org.apache.doris.nereids.types.NullType;
 import org.apache.doris.nereids.types.StringType;
 
+import java.util.Objects;
+
 /**
  * All data type literal expression in Nereids.
  */
@@ -67,6 +70,9 @@ public class Literal extends Expression<Literal> implements LeafExpression<Liter
         }
     }
 
+    public Object getValue() {
+        return value;
+    }
 
     /**
      * Convert to legacy literal expression in Doris.
@@ -91,11 +97,38 @@ public class Literal extends Expression<Literal> implements LeafExpression<Liter
         return value == null;
     }
 
+    @Override
+    public boolean isConstant() {
+        return true;
+    }
+
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitLiteral(this, context);
+    }
+
     @Override
     public String sql() {
         return null;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        Literal other = (Literal) o;
+        return Objects.equals(value, other.getValue());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(value);
+    }
+
     @Override
     public String toString() {
         return value.toString();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java
index ae7f4d27f1..c3f796a066 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java
@@ -49,6 +49,11 @@ public abstract class NamedExpression<EXPR_TYPE extends NamedExpression<EXPR_TYP
         throw new UnboundException("qualifier");
     }
 
+    @Override
+    public boolean isConstant() {
+        return false;
+    }
+
     /**
      * Get qualified name of NamedExpression.
      *
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
index 15ddcf54b5..ac512a9242 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
@@ -18,7 +18,14 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
+import org.apache.doris.nereids.rules.expression.rewrite.ExpressionVisitor;
 import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.TreeNode;
+
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+import java.util.Objects;
 
 /**
  * Not expression: not a.
@@ -34,6 +41,29 @@ public class Not<CHILD_TYPE extends Expression> extends Expression<Not<CHILD_TYP
         return child().nullable();
     }
 
+    @Override
+    public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) {
+        return visitor.visitNot(this, context);
+    }
+
+    @Override
+    public Not newChildren(List<TreeNode> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new Not((Expression) children.get(0));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        Not other = (Not) o;
+        return Objects.equals(child(), other.child());
+    }
+
     @Override
     public String toString() {
         return "( not " + child() + ")";
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
index 991edda91c..ace6c53ba3 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/UnaryExpression.java
@@ -25,4 +25,8 @@ import org.apache.doris.nereids.trees.UnaryNode;
 public interface UnaryExpression<EXPR_TYPE extends UnaryExpression<EXPR_TYPE, CHILD_TYPE>,
         CHILD_TYPE extends Expression> extends UnaryNode<EXPR_TYPE, CHILD_TYPE> {
 
+    @Override
+    default CHILD_TYPE child() {
+        return child(0);
+    }
 }
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
new file mode 100644
index 0000000000..5f06b4f68a
--- /dev/null
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteTest.java
@@ -0,0 +1,65 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the notICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.rules.expression.rewrite;
+
+import org.apache.doris.nereids.parser.SqlParser;
+import org.apache.doris.nereids.rules.expression.rewrite.rules.NormalizeExpressionRule;
+import org.apache.doris.nereids.rules.expression.rewrite.rules.SimplifyNotExprRule;
+import org.apache.doris.nereids.trees.expressions.Expression;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * all expr rewrite rule test case.
+ */
+public class ExpressionRewriteTest {
+    private static final SqlParser PARSER = new SqlParser();
+    private ExpressionRewriter rewriter;
+
+    @Test
+    public void testNotRewrite() {
+        rewriter = new ExpressionRewriter(SimplifyNotExprRule.INSTANCE);
+
+        assertRewrite("not x > y", "x <= y");
+        assertRewrite("not x < y", "x >= y");
+        assertRewrite("not x >= y", "x < y");
+        assertRewrite("not x <= y", "x > y");
+        assertRewrite("not x = y", "not x = y");
+        assertRewrite("not not x > y", "x > y");
+        assertRewrite("not not not x > y", "x <= y");
+    }
+
+    @Test
+    public void testNormalizeExpressionRewrite() {
+        rewriter = new ExpressionRewriter(NormalizeExpressionRule.INSTANCE);
+
+        assertRewrite("2 > x", "x < 2");
+        assertRewrite("2 >= x", "x <= 2");
+        assertRewrite("2 < x", "x > 2");
+        assertRewrite("2 <= x", "x >= 2");
+        assertRewrite("2 = x", "x = 2");
+    }
+
+    private void assertRewrite(String expression, String expected) {
+        Expression needRewriteExpression = PARSER.createExpression(expression);
+        Expression expectedExpression = PARSER.createExpression(expected);
+        Expression rewrittenExpression = rewriter.rewrite(needRewriteExpression);
+        Assert.assertEquals(expectedExpression, rewrittenExpression);
+    }
+}


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