[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
This commit is contained in:
shee
2022-06-14 16:20:48 +08:00
committed by GitHub
parent 14bc971159
commit 2fadaddda0
27 changed files with 717 additions and 21 deletions

View File

@ -141,7 +141,8 @@ expression
;
booleanExpression
: valueExpression #predicated
: NOT booleanExpression #not
| valueExpression #predicated
;
valueExpression

View File

@ -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());
}
}

View File

@ -0,0 +1,29 @@
// 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.exceptions;
/**
* sql parsing exception.
*/
public class ParsingException extends RuntimeException {
public ParsingException(String message) {
super(message);
}
}

View File

@ -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:

View File

@ -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);
// parser.addParseListener(PostProcessor)
// parser.removeErrorListeners()
// parser.addErrorListener(ParseErrorListener)
ParserRuleContext tree;
private TreeNode parse(String sql, Function<DorisParser, ParserRuleContext> parseFunction) {
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();
DorisLexer lexer = new DorisLexer(new CaseInsensitiveStream(CharStreams.fromString(sql)));
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
DorisParser parser = new DorisParser(tokenStream);
parser.getInterpreter().setPredictionMode(PredictionMode.LL);
tree = parser.singleStatement();
// 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 = 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 = 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);
}
}

View File

@ -0,0 +1,37 @@
// 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.trees.expressions.Expression;
/**
* Base class of expression rewrite rule.
*/
public abstract class AbstractExpressionRewriteRule extends ExpressionVisitor<Expression, ExpressionRewriteContext>
implements ExpressionRewriteRule {
@Override
public Expression rewrite(Expression expr, ExpressionRewriteContext ctx) {
return (Expression) expr.accept(this, ctx);
}
@Override
public Expression visitExpression(Expression expr, ExpressionRewriteContext ctx) {
return expr;
}
}

View File

@ -0,0 +1,24 @@
// 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;
/**
* expression rewrite context.
*/
public class ExpressionRewriteContext {
}

View File

@ -0,0 +1,27 @@
// 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.trees.expressions.Expression;
/**
* The interface of expression rewrite rule.
*/
public interface ExpressionRewriteRule {
Expression rewrite(Expression expr, ExpressionRewriteContext ctx);
}

View File

@ -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);
}
}

View File

@ -0,0 +1,43 @@
// 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.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;
/**
* Use the visitor pattern to iterate over all expressions for expression rewriting.
*/
public abstract class ExpressionVisitor<R, C> {
public abstract R visitExpression(Expression expr, C context);
public R visitNot(Not expr, C context) {
return visitExpression(expr, context);
}
public R visitComparisonPredicate(ComparisonPredicate expr, C context) {
return visitExpression(expr, context);
}
public R visitLiteral(Literal expr, C context) {
return visitExpression(expr, context);
}
}

View File

@ -0,0 +1,31 @@
// 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.trees.expressions.Expression;
/**
* Expression rewrite helper class.
*/
public class RewriteHelper {
public static boolean isConstant(Expression expr) {
return expr.isConstant();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -41,6 +41,7 @@ public enum NodeType {
NULL_SAFE_EQUAL,
NOT,
ALIAS,
COMPOUND,
// pattern
PATTERN,

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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));
}
}

View File

@ -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;
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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();

View File

@ -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.
*

View File

@ -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() + ")";

View File

@ -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);
}
}

View File

@ -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);
}
}