morrySnow commented on code in PR #12151: URL: https://github.com/apache/doris/pull/12151#discussion_r977225255
########## fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/TypeCoercion.java: ########## @@ -82,6 +85,23 @@ public Expression visitBinaryOperator(BinaryOperator binaryOperator, ExpressionR .orElse(binaryOperator.withChildren(left, right)); } + @Override + public Expression visitDivide(Divide divide, ExpressionRewriteContext context) { Review Comment: pls remove TODO at L48 ########## fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java: ########## @@ -945,6 +946,9 @@ private Expression withPredicate(Expression valueExpression, PredicateContext ct ); } break; + case DorisParser.NULL: Review Comment: pls remove TODO at L388 in function `visitPredicated` ########## fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java: ########## @@ -257,4 +259,20 @@ public static List<Expression> mergeArguments(Object... arguments) { } return builder.build(); } + + public static boolean isAllLiteral(Expression... children) { + return Arrays.stream(children).allMatch(c -> c instanceof Literal); + } + + public static boolean isAllLiteral(List<Expression> children) { + return children.stream().allMatch(c -> c instanceof Literal); + } + + public static boolean hasNullLiteral(List<Expression> children) { + return children.stream().anyMatch(c -> c instanceof NullLiteral); + } + + public static boolean allNullLiteral(List<Expression> children) { Review Comment: ```suggestion public static boolean isAllNullLiteral(List<Expression> children) { ``` ########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionEvaluator.java: ########## @@ -0,0 +1,210 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions; + +import org.apache.doris.catalog.Env; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.nereids.trees.expressions.functions.ExecutableFunctions; +import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.rewrite.FEFunction; +import org.apache.doris.rewrite.FEFunctionList; + +import com.google.common.collect.ImmutableMultimap; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * An expression evaluator that evaluates the value of an expression. + */ +public enum ExpressionEvaluator { + INSTANCE; + private ImmutableMultimap<String, FunctionInvoker> functions; + + ExpressionEvaluator() { + registerFunctions(); + } + + /** + * Evaluate the value of the expression. + */ + public Expression eval(Expression expression) { + + if (!expression.isConstant() || expression instanceof AggregateFunction) { + return expression; + } + + String fnName = null; + DataType[] args = null; + if (expression instanceof BinaryArithmetic) { + BinaryArithmetic arithmetic = (BinaryArithmetic) expression; + fnName = arithmetic.getLegacyOperator().getName(); + args = new DataType[]{arithmetic.left().getDataType(), arithmetic.right().getDataType()}; + } else if (expression instanceof TimestampArithmetic) { + TimestampArithmetic arithmetic = (TimestampArithmetic) expression; + fnName = arithmetic.getFuncName(); + args = new DataType[]{arithmetic.left().getDataType(), arithmetic.right().getDataType()}; + } + + if ((Env.getCurrentEnv().isNullResultWithOneNullParamFunction(fnName))) { + for (Expression e : expression.children()) { + if (e instanceof NullLiteral) { + return Literal.of(null); + } + } + } + + return invoke(expression, fnName, args); + } + + private Expression invoke(Expression expression, String fnName, DataType[] args) { + FunctionSignature signature = new FunctionSignature(fnName, args, null); + FunctionInvoker invoker = getFunction(signature); + if (invoker != null) { + try { + return invoker.invoke(expression.children()); + } catch (AnalysisException e) { + return expression; + } + } + return expression; + } + + private FunctionInvoker getFunction(FunctionSignature signature) { + Collection<FunctionInvoker> functionInvokers = functions.get(signature.getName()); + if (functionInvokers == null) { + return null; + } + for (FunctionInvoker invoker : functionInvokers) { + DataType[] argTypes1 = invoker.getSignature().getArgTypes(); + DataType[] argTypes2 = signature.getArgTypes(); Review Comment: ```suggestion DataType[] candidateTypes = invoker.getSignature().getArgTypes(); DataType[] expectedTypes = signature.getArgTypes(); ``` ########## fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/FoldConstantRuleOnBE.java: ########## @@ -0,0 +1,196 @@ +// 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.analysis.Expr; +import org.apache.doris.analysis.ExprId; +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.PrimitiveType; +import org.apache.doris.catalog.Type; +import org.apache.doris.common.IdGenerator; +import org.apache.doris.common.LoadException; +import org.apache.doris.common.util.TimeUtils; +import org.apache.doris.common.util.VectorizedUtil; +import org.apache.doris.nereids.glue.translator.ExpressionTranslator; +import org.apache.doris.nereids.rules.expression.rewrite.AbstractExpressionRewriteRule; +import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewriteContext; +import org.apache.doris.nereids.trees.expressions.Between; +import org.apache.doris.nereids.trees.expressions.Cast; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.proto.InternalService; +import org.apache.doris.proto.InternalService.PConstantExprResult; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.rpc.BackendServiceProxy; +import org.apache.doris.system.Backend; +import org.apache.doris.thrift.TExpr; +import org.apache.doris.thrift.TFoldConstantParams; +import org.apache.doris.thrift.TNetworkAddress; +import org.apache.doris.thrift.TPrimitiveType; +import org.apache.doris.thrift.TQueryGlobals; + +import com.google.common.collect.Maps; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * Constant evaluation of an expression. + */ +public class FoldConstantRuleOnBE extends AbstractExpressionRewriteRule { + public static final FoldConstantRuleOnBE INSTANCE = new FoldConstantRuleOnBE(); + private static final Logger LOG = LogManager.getLogger(FoldConstantRuleOnBE.class); + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private final IdGenerator<ExprId> idGenerator = ExprId.createGenerator(); + + @Override + public Expression rewrite(Expression expr, ExpressionRewriteContext ctx) { + Expression expression = FoldConstantRuleOnFE.INSTANCE.rewrite(expr, ctx); + return foldByBe(expression, ctx); + } + + private Expression foldByBe(Expression root, ExpressionRewriteContext context) { + Map<String, Expression> constMap = Maps.newHashMap(); + Map<String, TExpr> staleConstTExprMap = Maps.newHashMap(); + collectConst(root, constMap, staleConstTExprMap); + if (constMap.isEmpty()) { + return root; + } + Map<String, Map<String, TExpr>> paramMap = new HashMap<>(); + paramMap.put("0", staleConstTExprMap); + Map<String, Expression> resultMap = evalOnBe(paramMap, constMap, context.connectContext); + if (!resultMap.isEmpty()) { + return replace(root, constMap, resultMap); + } + return root; + } + + private Expression replace(Expression root, Map<String, Expression> constMap, Map<String, Expression> resultMap) { + for (Entry<String, Expression> entry : constMap.entrySet()) { + if (entry.getValue().equals(root)) { + return resultMap.get(entry.getKey()); + } + } + List<Expression> newChildren = new ArrayList<>(); + boolean hasNewChildren = false; + for (Expression child : root.children()) { + Expression newChild = replace(child, constMap, resultMap); + if (newChild != child) { + hasNewChildren = true; + } + newChildren.add(newChild); + } + return hasNewChildren ? root.withChildren(newChildren) : root; + } + + private void collectConst(Expression expr, Map<String, Expression> constMap, Map<String, TExpr> tExprMap) { + if (expr.isConstant()) { + if (expr instanceof Cast) { + if (((Cast) expr).child().isNullLiteral()) { + return; + } + } + if (expr.isLiteral()) { + return; + } + if (expr instanceof Between) { + return; + } + String id = idGenerator.getNextId().toString(); + constMap.put(id, expr); + Expr staleExpr = ExpressionTranslator.translate(expr, null); + tExprMap.put(id, staleExpr.treeToThrift()); + } else { + for (int i = 0; i < expr.children().size(); i++) { + final Expression child = expr.children().get(i); + collectConst(child, constMap, tExprMap); + } + } + } + + private Map<String, Expression> evalOnBe(Map<String, Map<String, TExpr>> paramMap, + Map<String, Expression> constMap, ConnectContext context) { + + Map<String, Expression> resultMap = new HashMap<>(); + try { + List<Long> backendIds = Env.getCurrentSystemInfo().getBackendIds(true); + if (backendIds.isEmpty()) { + throw new LoadException("No alive backends"); Review Comment: why LoadException ########## fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/FoldConstantRuleOnFE.java: ########## @@ -0,0 +1,310 @@ +// 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.expressions.And; +import org.apache.doris.nereids.trees.expressions.BinaryArithmetic; +import org.apache.doris.nereids.trees.expressions.CaseWhen; +import org.apache.doris.nereids.trees.expressions.Cast; +import org.apache.doris.nereids.trees.expressions.EqualTo; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.ExpressionEvaluator; +import org.apache.doris.nereids.trees.expressions.GreaterThan; +import org.apache.doris.nereids.trees.expressions.GreaterThanEqual; +import org.apache.doris.nereids.trees.expressions.InPredicate; +import org.apache.doris.nereids.trees.expressions.IsNull; +import org.apache.doris.nereids.trees.expressions.LessThan; +import org.apache.doris.nereids.trees.expressions.LessThanEqual; +import org.apache.doris.nereids.trees.expressions.Like; +import org.apache.doris.nereids.trees.expressions.Not; +import org.apache.doris.nereids.trees.expressions.NullSafeEqual; +import org.apache.doris.nereids.trees.expressions.Or; +import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.expressions.TimestampArithmetic; +import org.apache.doris.nereids.trees.expressions.WhenClause; +import org.apache.doris.nereids.trees.expressions.functions.BoundFunction; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.util.ExpressionUtils; + +import com.google.common.collect.Lists; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * evaluate an expression on fe. + */ +public class FoldConstantRuleOnFE extends AbstractExpressionRewriteRule { + public static final FoldConstantRuleOnFE INSTANCE = new FoldConstantRuleOnFE(); + + @Override + public Expression rewrite(Expression expr, ExpressionRewriteContext ctx) { + return process(expr, ctx); + } + + @Override + public Expression visit(Expression expr, ExpressionRewriteContext context) { + return expr; + } + + /** + * process constant expression. + */ + public Expression process(Expression expr, ExpressionRewriteContext ctx) { + if (expr instanceof PropagateNullable) { Review Comment: do we need to fold children first? in other words, do a postorder traversal ########## fe/fe-core/src/main/java/org/apache/doris/nereids/util/TypeCoercionUtils.java: ########## @@ -202,6 +207,39 @@ public static Optional<DataType> findTightestCommonType(DataType left, DataType return Optional.ofNullable(tightestCommonType); } + /** + * The type used for arithmetic operations. + */ + public static DataType getNumResultType(DataType type) { + if (type.isTinyIntType() || type.isSmallIntType() || type.isIntType() || type.isBigIntType()) { + return BigIntType.INSTANCE; + } else if (type.isLargeIntType()) { + return LargeIntType.INSTANCE; + } else if (type.isFloatType() || type.isDoubleType() || type.isStringType()) { + return DoubleType.INSTANCE; + } else if (type.isDecimalType()) { + return DecimalType.SYSTEM_DEFAULT; + } else if (type.isNullType()) { + return NullType.INSTANCE; + } + throw new AnalysisException("no found appropriate data type."); + } + + /** + * The common type used by arithmetic operations. + */ + public static DataType findCommonType(DataType t1, DataType t2) { Review Comment: findCommonNumericsType ########## fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewriteTest.java: ########## @@ -65,7 +65,7 @@ public void testNotRewrite() { @Test public void testNormalizeExpressionRewrite() { - executor = new ExpressionRuleExecutor(NormalizeBinaryPredicatesRule.INSTANCE); + executor = new ExpressionRuleExecutor(ImmutableList.of(NormalizeBinaryPredicatesRule.INSTANCE), null); Review Comment: nit: add another constructor with one parameter ########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExecutableFunctions.java: ########## @@ -0,0 +1,243 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions.functions; + +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DecimalLiteral; +import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.literal.SmallIntLiteral; +import org.apache.doris.nereids.trees.expressions.literal.TinyIntLiteral; +import org.apache.doris.rewrite.FEFunction; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.math.BigDecimal; + +/** + * functions that can be executed in FE. + */ +public class ExecutableFunctions { + public static final ExecutableFunctions INSTANCE = new ExecutableFunctions(); + + private static final Logger LOG = LogManager.getLogger(ExecutableFunctions.class); + + /** + * Executable arithmetic functions + */ + + @FEFunction(name = "add", argTypes = {"TINYINT", "TINYINT"}, returnType = "TINYINT") Review Comment: The annotion 'FEFunction' is the one the legacy plan used? does it affect the legacy plan const fold? ########## fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/FoldConstantRuleOnBE.java: ########## @@ -0,0 +1,196 @@ +// 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.analysis.Expr; +import org.apache.doris.analysis.ExprId; +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.PrimitiveType; +import org.apache.doris.catalog.Type; +import org.apache.doris.common.IdGenerator; +import org.apache.doris.common.LoadException; +import org.apache.doris.common.util.TimeUtils; +import org.apache.doris.common.util.VectorizedUtil; +import org.apache.doris.nereids.glue.translator.ExpressionTranslator; +import org.apache.doris.nereids.rules.expression.rewrite.AbstractExpressionRewriteRule; +import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewriteContext; +import org.apache.doris.nereids.trees.expressions.Between; +import org.apache.doris.nereids.trees.expressions.Cast; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.proto.InternalService; +import org.apache.doris.proto.InternalService.PConstantExprResult; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.rpc.BackendServiceProxy; +import org.apache.doris.system.Backend; +import org.apache.doris.thrift.TExpr; +import org.apache.doris.thrift.TFoldConstantParams; +import org.apache.doris.thrift.TNetworkAddress; +import org.apache.doris.thrift.TPrimitiveType; +import org.apache.doris.thrift.TQueryGlobals; + +import com.google.common.collect.Maps; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * Constant evaluation of an expression. + */ +public class FoldConstantRuleOnBE extends AbstractExpressionRewriteRule { + public static final FoldConstantRuleOnBE INSTANCE = new FoldConstantRuleOnBE(); + private static final Logger LOG = LogManager.getLogger(FoldConstantRuleOnBE.class); + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private final IdGenerator<ExprId> idGenerator = ExprId.createGenerator(); + + @Override + public Expression rewrite(Expression expr, ExpressionRewriteContext ctx) { + Expression expression = FoldConstantRuleOnFE.INSTANCE.rewrite(expr, ctx); + return foldByBe(expression, ctx); + } + + private Expression foldByBe(Expression root, ExpressionRewriteContext context) { Review Comment: ```suggestion private Expression foldByBE(Expression root, ExpressionRewriteContext context) { ``` ########## fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/FoldConstantRuleOnFE.java: ########## @@ -0,0 +1,310 @@ +// 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.expressions.And; +import org.apache.doris.nereids.trees.expressions.BinaryArithmetic; +import org.apache.doris.nereids.trees.expressions.CaseWhen; +import org.apache.doris.nereids.trees.expressions.Cast; +import org.apache.doris.nereids.trees.expressions.EqualTo; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.ExpressionEvaluator; +import org.apache.doris.nereids.trees.expressions.GreaterThan; +import org.apache.doris.nereids.trees.expressions.GreaterThanEqual; +import org.apache.doris.nereids.trees.expressions.InPredicate; +import org.apache.doris.nereids.trees.expressions.IsNull; +import org.apache.doris.nereids.trees.expressions.LessThan; +import org.apache.doris.nereids.trees.expressions.LessThanEqual; +import org.apache.doris.nereids.trees.expressions.Like; +import org.apache.doris.nereids.trees.expressions.Not; +import org.apache.doris.nereids.trees.expressions.NullSafeEqual; +import org.apache.doris.nereids.trees.expressions.Or; +import org.apache.doris.nereids.trees.expressions.Slot; +import org.apache.doris.nereids.trees.expressions.TimestampArithmetic; +import org.apache.doris.nereids.trees.expressions.WhenClause; +import org.apache.doris.nereids.trees.expressions.functions.BoundFunction; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable; +import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.util.ExpressionUtils; + +import com.google.common.collect.Lists; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * evaluate an expression on fe. + */ +public class FoldConstantRuleOnFE extends AbstractExpressionRewriteRule { + public static final FoldConstantRuleOnFE INSTANCE = new FoldConstantRuleOnFE(); + + @Override + public Expression rewrite(Expression expr, ExpressionRewriteContext ctx) { + return process(expr, ctx); + } + + @Override + public Expression visit(Expression expr, ExpressionRewriteContext context) { + return expr; + } + + /** + * process constant expression. + */ + public Expression process(Expression expr, ExpressionRewriteContext ctx) { + if (expr instanceof PropagateNullable) { + List<Expression> children = expr.children() + .stream() + .map(child -> process(child, ctx)) + .collect(Collectors.toList()); + + if (ExpressionUtils.hasNullLiteral(children)) { + return Literal.of(null); + } + + if (!ExpressionUtils.isAllLiteral(children)) { + return expr.withChildren(children); + } + return expr.withChildren(children).accept(this, ctx); + } else { + return expr.accept(this, ctx); + } + } + + @Override + public Expression visitEqualTo(EqualTo equalTo, ExpressionRewriteContext context) { + return BooleanLiteral.of(((Literal) equalTo.left()).compareTo((Literal) equalTo.right()) == 0); Review Comment: where to ensure all children are literal? ########## fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionEvaluator.java: ########## @@ -0,0 +1,210 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.trees.expressions; + +import org.apache.doris.catalog.Env; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.nereids.trees.expressions.functions.ExecutableFunctions; +import org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.rewrite.FEFunction; +import org.apache.doris.rewrite.FEFunctionList; + +import com.google.common.collect.ImmutableMultimap; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * An expression evaluator that evaluates the value of an expression. + */ +public enum ExpressionEvaluator { + INSTANCE; + private ImmutableMultimap<String, FunctionInvoker> functions; + + ExpressionEvaluator() { + registerFunctions(); + } + + /** + * Evaluate the value of the expression. + */ + public Expression eval(Expression expression) { + + if (!expression.isConstant() || expression instanceof AggregateFunction) { + return expression; + } + + String fnName = null; + DataType[] args = null; + if (expression instanceof BinaryArithmetic) { + BinaryArithmetic arithmetic = (BinaryArithmetic) expression; + fnName = arithmetic.getLegacyOperator().getName(); + args = new DataType[]{arithmetic.left().getDataType(), arithmetic.right().getDataType()}; + } else if (expression instanceof TimestampArithmetic) { + TimestampArithmetic arithmetic = (TimestampArithmetic) expression; + fnName = arithmetic.getFuncName(); + args = new DataType[]{arithmetic.left().getDataType(), arithmetic.right().getDataType()}; + } + + if ((Env.getCurrentEnv().isNullResultWithOneNullParamFunction(fnName))) { + for (Expression e : expression.children()) { + if (e instanceof NullLiteral) { + return Literal.of(null); + } + } + } + + return invoke(expression, fnName, args); + } + + private Expression invoke(Expression expression, String fnName, DataType[] args) { + FunctionSignature signature = new FunctionSignature(fnName, args, null); + FunctionInvoker invoker = getFunction(signature); + if (invoker != null) { + try { + return invoker.invoke(expression.children()); + } catch (AnalysisException e) { + return expression; + } + } + return expression; + } + + private FunctionInvoker getFunction(FunctionSignature signature) { + Collection<FunctionInvoker> functionInvokers = functions.get(signature.getName()); + if (functionInvokers == null) { + return null; + } + for (FunctionInvoker invoker : functionInvokers) { Review Comment: ```suggestion for (FunctionInvoker candidate : functionInvokers) { ``` ########## fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/FoldConstantRuleOnBE.java: ########## @@ -0,0 +1,196 @@ +// 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.analysis.Expr; +import org.apache.doris.analysis.ExprId; +import org.apache.doris.analysis.LiteralExpr; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.PrimitiveType; +import org.apache.doris.catalog.Type; +import org.apache.doris.common.IdGenerator; +import org.apache.doris.common.LoadException; +import org.apache.doris.common.util.TimeUtils; +import org.apache.doris.common.util.VectorizedUtil; +import org.apache.doris.nereids.glue.translator.ExpressionTranslator; +import org.apache.doris.nereids.rules.expression.rewrite.AbstractExpressionRewriteRule; +import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewriteContext; +import org.apache.doris.nereids.trees.expressions.Between; +import org.apache.doris.nereids.trees.expressions.Cast; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.types.DataType; +import org.apache.doris.proto.InternalService; +import org.apache.doris.proto.InternalService.PConstantExprResult; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.rpc.BackendServiceProxy; +import org.apache.doris.system.Backend; +import org.apache.doris.thrift.TExpr; +import org.apache.doris.thrift.TFoldConstantParams; +import org.apache.doris.thrift.TNetworkAddress; +import org.apache.doris.thrift.TPrimitiveType; +import org.apache.doris.thrift.TQueryGlobals; + +import com.google.common.collect.Maps; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + * Constant evaluation of an expression. + */ +public class FoldConstantRuleOnBE extends AbstractExpressionRewriteRule { + public static final FoldConstantRuleOnBE INSTANCE = new FoldConstantRuleOnBE(); + private static final Logger LOG = LogManager.getLogger(FoldConstantRuleOnBE.class); + private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private final IdGenerator<ExprId> idGenerator = ExprId.createGenerator(); + + @Override + public Expression rewrite(Expression expr, ExpressionRewriteContext ctx) { + Expression expression = FoldConstantRuleOnFE.INSTANCE.rewrite(expr, ctx); + return foldByBe(expression, ctx); + } + + private Expression foldByBe(Expression root, ExpressionRewriteContext context) { + Map<String, Expression> constMap = Maps.newHashMap(); + Map<String, TExpr> staleConstTExprMap = Maps.newHashMap(); + collectConst(root, constMap, staleConstTExprMap); + if (constMap.isEmpty()) { + return root; + } + Map<String, Map<String, TExpr>> paramMap = new HashMap<>(); + paramMap.put("0", staleConstTExprMap); + Map<String, Expression> resultMap = evalOnBe(paramMap, constMap, context.connectContext); + if (!resultMap.isEmpty()) { + return replace(root, constMap, resultMap); + } + return root; + } + + private Expression replace(Expression root, Map<String, Expression> constMap, Map<String, Expression> resultMap) { + for (Entry<String, Expression> entry : constMap.entrySet()) { + if (entry.getValue().equals(root)) { + return resultMap.get(entry.getKey()); + } + } + List<Expression> newChildren = new ArrayList<>(); + boolean hasNewChildren = false; + for (Expression child : root.children()) { + Expression newChild = replace(child, constMap, resultMap); + if (newChild != child) { + hasNewChildren = true; + } + newChildren.add(newChild); + } + return hasNewChildren ? root.withChildren(newChildren) : root; + } + + private void collectConst(Expression expr, Map<String, Expression> constMap, Map<String, TExpr> tExprMap) { + if (expr.isConstant()) { + if (expr instanceof Cast) { Review Comment: nit: add some comment to describe why need to process these expr specially -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org