diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSink.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSink.java index 0c0f9e2e25..4c00db457f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSink.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSink.java @@ -32,6 +32,7 @@ import org.apache.doris.datasource.hive.HMSExternalTable; import org.apache.doris.datasource.iceberg.IcebergExternalDatabase; import org.apache.doris.datasource.iceberg.IcebergExternalTable; import org.apache.doris.nereids.CascadesContext; +import org.apache.doris.nereids.analyzer.Scope; import org.apache.doris.nereids.analyzer.UnboundHiveTableSink; import org.apache.doris.nereids.analyzer.UnboundIcebergTableSink; import org.apache.doris.nereids.analyzer.UnboundSlot; @@ -42,7 +43,6 @@ import org.apache.doris.nereids.pattern.MatchingContext; import org.apache.doris.nereids.rules.Rule; import org.apache.doris.nereids.rules.RuleType; import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext; -import org.apache.doris.nereids.rules.expression.rules.FunctionBinder; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.Cast; import org.apache.doris.nereids.trees.expressions.DefaultValueSlot; @@ -53,7 +53,6 @@ import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.expressions.functions.scalar.Substring; import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; -import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.commands.info.DMLCommandType; import org.apache.doris.nereids.trees.plans.logical.LogicalHiveTableSink; @@ -203,8 +202,8 @@ public class BindSink implements AnalysisRuleFactory { throw new AnalysisException(e.getMessage(), e.getCause()); } - Map columnToOutput = getColumnToOutput(ctx, table, isPartialUpdate, - boundSink, child); + Map columnToOutput = getColumnToOutput( + ctx, table, isPartialUpdate, boundSink, child); LogicalProject fullOutputProject = getOutputProjectByCoercion(table.getFullSchema(), child, columnToOutput); return boundSink.withChildAndUpdateOutput(fullOutputProject); } @@ -285,13 +284,11 @@ public class BindSink implements AnalysisRuleFactory { "mv column %s 's ref column cannot be null", column); Expression parsedExpression = expressionParser.parseExpression( column.getDefineExpr().toSqlWithoutTbl()); - Expression boundSlotExpression = SlotReplacer.INSTANCE - .replace(parsedExpression, columnToOutput); // the boundSlotExpression is an expression whose slots are bound but function // may not be bound, we have to bind it again. // for example: to_bitmap. - Expression boundExpression = FunctionBinder.INSTANCE.rewrite( - boundSlotExpression, new ExpressionRewriteContext(ctx.cascadesContext)); + Expression boundExpression = new CustomExpressionAnalyzer( + boundSink, ctx.cascadesContext, columnToOutput).analyze(parsedExpression); if (boundExpression instanceof Alias) { boundExpression = ((Alias) boundExpression).child(); } @@ -332,12 +329,15 @@ public class BindSink implements AnalysisRuleFactory { // update the value of the column to the current timestamp whenever there // is an update on the row if (column.hasOnUpdateDefaultValue()) { - Expression defualtValueExpression = FunctionBinder.INSTANCE.rewrite( - new NereidsParser().parseExpression( - column.getOnUpdateDefaultValueExpr().toSqlWithoutTbl()), - new ExpressionRewriteContext(ctx.cascadesContext)); + Expression unboundFunctionDefaultValue = new NereidsParser().parseExpression( + column.getOnUpdateDefaultValueExpr().toSqlWithoutTbl() + ); + Expression defualtValueExpression = ExpressionAnalyzer.analyzeFunction( + boundSink, ctx.cascadesContext, unboundFunctionDefaultValue + ); columnToOutput.put(column.getName(), - new Alias(defualtValueExpression, column.getName())); + new Alias(defualtValueExpression, column.getName()) + ); } else { continue; } @@ -365,10 +365,10 @@ public class BindSink implements AnalysisRuleFactory { .checkedCastTo(DataType.fromCatalogType(column.getType())), column.getName())); } else { - Expression defualtValueExpression = FunctionBinder.INSTANCE.rewrite( - new NereidsParser().parseExpression( - column.getDefaultValueExpr().toSqlWithoutTbl()), - new ExpressionRewriteContext(ctx.cascadesContext)); + Expression unboundDefaultValue = new NereidsParser().parseExpression( + column.getDefaultValueExpr().toSqlWithoutTbl()); + Expression defualtValueExpression = ExpressionAnalyzer.analyzeFunction( + boundSink, ctx.cascadesContext, unboundDefaultValue); if (defualtValueExpression instanceof Alias) { defualtValueExpression = ((Alias) defualtValueExpression).child(); } @@ -568,19 +568,21 @@ public class BindSink implements AnalysisRuleFactory { && !column.isMaterializedViewColumn(); } - private static class SlotReplacer extends DefaultExpressionRewriter> { - public static final SlotReplacer INSTANCE = new SlotReplacer(); + private static class CustomExpressionAnalyzer extends ExpressionAnalyzer { + private Map slotBinder; - public Expression replace(Expression e, Map replaceMap) { - return e.accept(this, replaceMap); + public CustomExpressionAnalyzer( + Plan currentPlan, CascadesContext cascadesContext, Map slotBinder) { + super(currentPlan, new Scope(ImmutableList.of()), cascadesContext, false, false); + this.slotBinder = slotBinder; } @Override - public Expression visitUnboundSlot(UnboundSlot unboundSlot, Map replaceMap) { - if (!replaceMap.containsKey(unboundSlot.getName())) { + public Expression visitUnboundSlot(UnboundSlot unboundSlot, ExpressionRewriteContext context) { + if (!slotBinder.containsKey(unboundSlot.getName())) { throw new AnalysisException("cannot find column from target table " + unboundSlot.getNameParts()); } - return replaceMap.get(unboundSlot.getName()); + return slotBinder.get(unboundSlot.getName()); } } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java index f6ccb87c8b..b9f34b91bf 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ExpressionAnalyzer.java @@ -35,6 +35,7 @@ import org.apache.doris.nereids.analyzer.UnboundStar; import org.apache.doris.nereids.analyzer.UnboundVariable; import org.apache.doris.nereids.analyzer.UnboundVariable.VariableType; import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.rules.expression.AbstractExpressionRewriteRule; import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext; import org.apache.doris.nereids.rules.expression.rules.FoldConstantRuleOnFE; import org.apache.doris.nereids.trees.expressions.Alias; @@ -78,6 +79,7 @@ import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes; import org.apache.doris.nereids.trees.plans.Plan; +import org.apache.doris.nereids.trees.plans.logical.LogicalPlan; import org.apache.doris.nereids.types.ArrayType; import org.apache.doris.nereids.types.BigIntType; import org.apache.doris.nereids.types.BooleanType; @@ -91,6 +93,7 @@ import org.apache.doris.qe.VariableMgr; import org.apache.doris.qe.VariableVarConverters; import org.apache.doris.qe.cache.CacheAnalyzer; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; @@ -102,9 +105,19 @@ import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.stream.Collectors; +import javax.annotation.Nullable; /** ExpressionAnalyzer */ public class ExpressionAnalyzer extends SubExprAnalyzer { + @VisibleForTesting + public static final AbstractExpressionRewriteRule FUNCTION_ANALYZER_RULE = new AbstractExpressionRewriteRule() { + @Override + public Expression rewrite(Expression expr, ExpressionRewriteContext ctx) { + return new ExpressionAnalyzer( + null, new Scope(ImmutableList.of()), null, false, false + ).analyze(expr, ctx); + } + }; private final Plan currentPlan; /* @@ -124,14 +137,30 @@ public class ExpressionAnalyzer extends SubExprAnalyzer qualifier = unboundStar.getQualifier(); @@ -356,15 +392,18 @@ public class ExpressionAnalyzer extends SubExprAnalyzer buildResult = builder.build(functionName, arguments); - StatementContext statementContext = context.cascadesContext.getStatementContext(); - if (buildResult.second instanceof Nondeterministic) { - hasNondeterministic = true; - } - Optional sqlCacheContext = statementContext.getSqlCacheContext(); - if (builder instanceof AliasUdfBuilder - || buildResult.second instanceof JavaUdf || buildResult.second instanceof JavaUdaf) { - if (sqlCacheContext.isPresent()) { - sqlCacheContext.get().setCannotProcessExpression(true); + Optional sqlCacheContext = Optional.empty(); + if (wantToParseSqlFromSqlCache) { + StatementContext statementContext = context.cascadesContext.getStatementContext(); + if (buildResult.second instanceof Nondeterministic) { + hasNondeterministic = true; + } + sqlCacheContext = statementContext.getSqlCacheContext(); + if (builder instanceof AliasUdfBuilder + || buildResult.second instanceof JavaUdf || buildResult.second instanceof JavaUdaf) { + if (sqlCacheContext.isPresent()) { + sqlCacheContext.get().setCannotProcessExpression(true); + } } } if (builder instanceof AliasUdfBuilder) { @@ -376,9 +415,9 @@ public class ExpressionAnalyzer extends SubExprAnalyzer argumentNames) { - lambdaFunction.foreachUp(e -> { - if (e instanceof UnboundSlot) { - UnboundSlot unboundSlot = (UnboundSlot) e; - throw new AnalysisException("Unknown lambda slot '" - + unboundSlot.getNameParts().get(unboundSlot.getNameParts().size() - 1) - + " in lambda arguments" + argumentNames); - } - }); - } - private UnboundFunction bindHighOrderFunction(UnboundFunction unboundFunction, ExpressionRewriteContext context) { int childrenSize = unboundFunction.children().size(); List subChildren = new ArrayList<>(); @@ -737,16 +768,20 @@ public class ExpressionAnalyzer extends SubExprAnalyzer arrayItemReferences = lambda.makeArguments(subChildren); - // 1.bindSlot List boundedSlots = arrayItemReferences.stream() .map(ArrayItemReference::toSlot) .collect(ImmutableList.toImmutableList()); - lambdaFunction = new SlotBinder(new Scope(boundedSlots), context.cascadesContext, - true, false).bind(lambdaFunction); - checkBoundLambda(lambdaFunction, lambda.getLambdaArgumentNames()); - // 2.bindFunction - lambdaFunction = lambdaFunction.accept(this, context); + ExpressionAnalyzer lambdaAnalyzer = new ExpressionAnalyzer(currentPlan, new Scope(boundedSlots), + context == null ? null : context.cascadesContext, true, false) { + @Override + protected void couldNotFoundColumn(UnboundSlot unboundSlot, String tableName) { + throw new AnalysisException("Unknown lambda slot '" + + unboundSlot.getNameParts().get(unboundSlot.getNameParts().size() - 1) + + " in lambda arguments" + lambda.getLambdaArgumentNames()); + } + }; + lambdaFunction = lambdaAnalyzer.analyze(lambdaFunction, context); Lambda lambdaClosure = lambda.withLambdaFunctionArguments(lambdaFunction, arrayItemReferences); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SlotBinder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SlotBinder.java deleted file mode 100644 index 3fa272568c..0000000000 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SlotBinder.java +++ /dev/null @@ -1,320 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.apache.doris.nereids.rules.analysis; - -import org.apache.doris.analysis.SetType; -import org.apache.doris.common.DdlException; -import org.apache.doris.common.util.Util; -import org.apache.doris.nereids.CascadesContext; -import org.apache.doris.nereids.analyzer.Scope; -import org.apache.doris.nereids.analyzer.UnboundAlias; -import org.apache.doris.nereids.analyzer.UnboundSlot; -import org.apache.doris.nereids.analyzer.UnboundStar; -import org.apache.doris.nereids.analyzer.UnboundVariable; -import org.apache.doris.nereids.analyzer.UnboundVariable.VariableType; -import org.apache.doris.nereids.exceptions.AnalysisException; -import org.apache.doris.nereids.trees.expressions.Alias; -import org.apache.doris.nereids.trees.expressions.BoundStar; -import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.NamedExpression; -import org.apache.doris.nereids.trees.expressions.Slot; -import org.apache.doris.nereids.trees.expressions.SlotReference; -import org.apache.doris.nereids.trees.expressions.Variable; -import org.apache.doris.nereids.trees.expressions.literal.IntegerLikeLiteral; -import org.apache.doris.nereids.trees.expressions.literal.Literal; -import org.apache.doris.nereids.trees.expressions.literal.StringLiteral; -import org.apache.doris.qe.ConnectContext; -import org.apache.doris.qe.GlobalVariable; -import org.apache.doris.qe.SessionVariable; -import org.apache.doris.qe.VariableMgr; -import org.apache.doris.qe.VariableVarConverters; - -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; -import org.apache.commons.lang3.StringUtils; - -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -/** - * SlotBinder is used to bind slot - */ -public class SlotBinder extends SubExprAnalyzer { - /* - bounded={table.a, a} - unbound=a - if enableExactMatch, 'a' is bound to bounded 'a', - if not enableExactMatch, 'a' is ambiguous - in order to be compatible to original planner, - exact match mode is not enabled for having clause - but enabled for order by clause - TODO after remove original planner, always enable exact match mode. - */ - private final boolean enableExactMatch; - private final boolean bindSlotInOuterScope; - - public SlotBinder(Scope scope, CascadesContext cascadesContext) { - this(scope, cascadesContext, true, true); - } - - public SlotBinder(Scope scope, CascadesContext cascadesContext, - boolean enableExactMatch, boolean bindSlotInOuterScope) { - super(scope, cascadesContext); - this.enableExactMatch = enableExactMatch; - this.bindSlotInOuterScope = bindSlotInOuterScope; - } - - public Expression bind(Expression expression) { - return expression.accept(this, null); - } - - @Override - public Expression visitUnboundVariable(UnboundVariable unboundVariable, CascadesContext context) { - String name = unboundVariable.getName(); - SessionVariable sessionVariable = ConnectContext.get().getSessionVariable(); - Literal literal = null; - if (unboundVariable.getType() == VariableType.DEFAULT) { - literal = VariableMgr.getLiteral(sessionVariable, name, SetType.DEFAULT); - } else if (unboundVariable.getType() == VariableType.SESSION) { - literal = VariableMgr.getLiteral(sessionVariable, name, SetType.SESSION); - } else if (unboundVariable.getType() == VariableType.GLOBAL) { - literal = VariableMgr.getLiteral(sessionVariable, name, SetType.GLOBAL); - } else if (unboundVariable.getType() == VariableType.USER) { - literal = ConnectContext.get().getLiteralForUserVar(name); - } - if (literal == null) { - throw new AnalysisException("Unsupported system variable: " + unboundVariable.getName()); - } - if (!Strings.isNullOrEmpty(name) && VariableVarConverters.hasConverter(name)) { - try { - Preconditions.checkArgument(literal instanceof IntegerLikeLiteral); - IntegerLikeLiteral integerLikeLiteral = (IntegerLikeLiteral) literal; - literal = new StringLiteral(VariableVarConverters.decode(name, integerLikeLiteral.getLongValue())); - } catch (DdlException e) { - throw new AnalysisException(e.getMessage()); - } - } - return new Variable(unboundVariable.getName(), unboundVariable.getType(), literal); - } - - @Override - public Expression visitUnboundAlias(UnboundAlias unboundAlias, CascadesContext context) { - Expression child = unboundAlias.child().accept(this, context); - if (unboundAlias.getAlias().isPresent()) { - return new Alias(child, unboundAlias.getAlias().get()); - } else if (child instanceof NamedExpression) { - return new Alias(child, ((NamedExpression) child).getName()); - } else { - return new Alias(child); - } - } - - @Override - public Slot visitUnboundSlot(UnboundSlot unboundSlot, CascadesContext context) { - Optional> boundedOpt = Optional.of(bindSlot(unboundSlot, getScope().getSlots())); - boolean foundInThisScope = !boundedOpt.get().isEmpty(); - // Currently only looking for symbols on the previous level. - if (bindSlotInOuterScope && !foundInThisScope && getScope().getOuterScope().isPresent()) { - boundedOpt = Optional.of(bindSlot(unboundSlot, - getScope() - .getOuterScope() - .get() - .getSlots())); - } - List bounded = boundedOpt.get(); - switch (bounded.size()) { - case 0: - // just return, give a chance to bind on another slot. - // if unbound finally, check will throw exception - return unboundSlot; - case 1: - if (!foundInThisScope - && !getScope().getOuterScope().get().getCorrelatedSlots().contains(bounded.get(0))) { - getScope().getOuterScope().get().getCorrelatedSlots().add(bounded.get(0)); - } - return bounded.get(0); - default: - if (enableExactMatch) { - // select t1.k k, t2.k - // from t1 join t2 order by k - // - // 't1.k k' is denoted by alias_k, its full name is 'k' - // 'order by k' is denoted as order_k, it full name is 'k' - // 't2.k' in select list, its full name is 't2.k' - // - // order_k can be bound on alias_k and t2.k - // alias_k is exactly matched, since its full name is exactly match full name of order_k - // t2.k is not exactly matched, since t2.k's full name is larger than order_k - List exactMatch = bounded.stream() - .filter(bound -> unboundSlot.getNameParts().size() == bound.getQualifier().size() + 1) - .collect(Collectors.toList()); - if (exactMatch.size() == 1) { - return exactMatch.get(0); - } - } - throw new AnalysisException(String.format("%s is ambiguous: %s.", - unboundSlot.toSql(), - bounded.stream() - .map(Slot::toString) - .collect(Collectors.joining(", ")))); - } - } - - @Override - public Expression visitUnboundStar(UnboundStar unboundStar, CascadesContext context) { - List qualifier = unboundStar.getQualifier(); - boolean showHidden = Util.showHiddenColumns(); - List slots = getScope().getSlots() - .stream() - .filter(slot -> !(slot instanceof SlotReference) - || (((SlotReference) slot).isVisible()) || showHidden) - .collect(Collectors.toList()); - switch (qualifier.size()) { - case 0: // select * - return new BoundStar(slots); - case 1: // select table.* - case 2: // select db.table.* - case 3: // select catalog.db.table.* - return bindQualifiedStar(qualifier, slots); - default: - throw new AnalysisException("Not supported qualifier: " - + StringUtils.join(qualifier, ".")); - } - } - - private BoundStar bindQualifiedStar(List qualifierStar, List boundSlots) { - // FIXME: compatible with previous behavior: - // https://github.com/apache/doris/pull/10415/files/3fe9cb0c3f805ab3a9678033b281b16ad93ec60a#r910239452 - List slots = boundSlots.stream().filter(boundSlot -> { - switch (qualifierStar.size()) { - // table.* - case 1: - List boundSlotQualifier = boundSlot.getQualifier(); - switch (boundSlotQualifier.size()) { - // bound slot is `column` and no qualified - case 0: - return false; - case 1: // bound slot is `table`.`column` - return qualifierStar.get(0).equalsIgnoreCase(boundSlotQualifier.get(0)); - case 2:// bound slot is `db`.`table`.`column` - return qualifierStar.get(0).equalsIgnoreCase(boundSlotQualifier.get(1)); - case 3:// bound slot is `catalog`.`db`.`table`.`column` - return qualifierStar.get(0).equalsIgnoreCase(boundSlotQualifier.get(2)); - default: - throw new AnalysisException("Not supported qualifier: " - + StringUtils.join(qualifierStar, ".")); - } - case 2: // db.table.* - boundSlotQualifier = boundSlot.getQualifier(); - switch (boundSlotQualifier.size()) { - // bound slot is `column` and no qualified - case 0: - case 1: // bound slot is `table`.`column` - return false; - case 2:// bound slot is `db`.`table`.`column` - return compareDbName(qualifierStar.get(0), boundSlotQualifier.get(0)) - && qualifierStar.get(1).equalsIgnoreCase(boundSlotQualifier.get(1)); - case 3:// bound slot is `catalog`.`db`.`table`.`column` - return compareDbName(qualifierStar.get(0), boundSlotQualifier.get(1)) - && qualifierStar.get(1).equalsIgnoreCase(boundSlotQualifier.get(2)); - default: - throw new AnalysisException("Not supported qualifier: " - + StringUtils.join(qualifierStar, ".") + ".*"); - } - case 3: // catalog.db.table.* - boundSlotQualifier = boundSlot.getQualifier(); - switch (boundSlotQualifier.size()) { - // bound slot is `column` and no qualified - case 0: - case 1: // bound slot is `table`.`column` - case 2: // bound slot is `db`.`table`.`column` - return false; - case 3:// bound slot is `catalog`.`db`.`table`.`column` - return qualifierStar.get(0).equalsIgnoreCase(boundSlotQualifier.get(0)) - && compareDbName(qualifierStar.get(1), boundSlotQualifier.get(1)) - && qualifierStar.get(2).equalsIgnoreCase(boundSlotQualifier.get(2)); - default: - throw new AnalysisException("Not supported qualifier: " - + StringUtils.join(qualifierStar, ".") + ".*"); - } - default: - throw new AnalysisException("Not supported name: " - + StringUtils.join(qualifierStar, ".") + ".*"); - } - }).collect(Collectors.toList()); - - if (slots.isEmpty()) { - throw new AnalysisException("unknown qualifier: " + StringUtils.join(qualifierStar, ".") + ".*"); - } - return new BoundStar(slots); - } - - private List bindSlot(UnboundSlot unboundSlot, List boundSlots) { - return boundSlots.stream().distinct().filter(boundSlot -> { - List nameParts = unboundSlot.getNameParts(); - int qualifierSize = boundSlot.getQualifier().size(); - int namePartsSize = nameParts.size(); - if (namePartsSize > qualifierSize + 1) { - return false; - } - if (namePartsSize == 1) { - return nameParts.get(0).equalsIgnoreCase(boundSlot.getName()); - } - if (namePartsSize == 2) { - String qualifierTableName = boundSlot.getQualifier().get(qualifierSize - 1); - return sameTableName(qualifierTableName, nameParts.get(0)) - && boundSlot.getName().equalsIgnoreCase(nameParts.get(1)); - } - if (nameParts.size() == 3) { - String qualifierTableName = boundSlot.getQualifier().get(qualifierSize - 1); - String qualifierDbName = boundSlot.getQualifier().get(qualifierSize - 2); - return compareDbName(nameParts.get(0), qualifierDbName) - && sameTableName(qualifierTableName, nameParts.get(1)) - && boundSlot.getName().equalsIgnoreCase(nameParts.get(2)); - } - // catalog.db.table.column - if (nameParts.size() == 4) { - String qualifierTableName = boundSlot.getQualifier().get(qualifierSize - 1); - String qualifierDbName = boundSlot.getQualifier().get(qualifierSize - 2); - String qualifierCatalogName = boundSlot.getQualifier().get(qualifierSize - 3); - return qualifierCatalogName.equalsIgnoreCase(nameParts.get(0)) - && compareDbName(nameParts.get(1), qualifierDbName) - && sameTableName(qualifierTableName, nameParts.get(2)) - && boundSlot.getName().equalsIgnoreCase(nameParts.get(3)); - } - //TODO: handle name parts more than three. - throw new AnalysisException("Not supported name: " - + StringUtils.join(nameParts, ".")); - }) - .map(s -> s.withName(unboundSlot.getNameParts().get(unboundSlot.getNameParts().size() - 1))) - .collect(Collectors.toList()); - } - - public static boolean compareDbName(String boundedDbName, String unBoundDbName) { - return unBoundDbName.equalsIgnoreCase(boundedDbName); - } - - public static boolean sameTableName(String boundSlot, String unboundSlot) { - if (GlobalVariable.lowerCaseTableNames != 1) { - return boundSlot.equals(unboundSlot); - } else { - return boundSlot.equalsIgnoreCase(unboundSlot); - } - } -} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubExprAnalyzer.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubExprAnalyzer.java index c3c17c7834..d52fc96ff9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubExprAnalyzer.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/SubExprAnalyzer.java @@ -173,6 +173,9 @@ class SubExprAnalyzer extends DefaultExpressionRewriter { } private AnalyzedResult analyzeSubquery(SubqueryExpr expr) { + if (cascadesContext == null) { + throw new IllegalStateException("Missing CascadesContext"); + } CascadesContext subqueryContext = CascadesContext.newContextWithCteContext( cascadesContext, expr.getQueryPlan(), cascadesContext.getCteContext()); Scope subqueryScope = genScopeWithSubquery(expr); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java deleted file mode 100644 index 1f170479e0..0000000000 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java +++ /dev/null @@ -1,365 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -package org.apache.doris.nereids.rules.expression.rules; - -import org.apache.doris.analysis.ArithmeticExpr.Operator; -import org.apache.doris.catalog.Env; -import org.apache.doris.catalog.FunctionRegistry; -import org.apache.doris.nereids.analyzer.Scope; -import org.apache.doris.nereids.analyzer.UnboundFunction; -import org.apache.doris.nereids.analyzer.UnboundSlot; -import org.apache.doris.nereids.exceptions.AnalysisException; -import org.apache.doris.nereids.rules.analysis.ArithmeticFunctionBinder; -import org.apache.doris.nereids.rules.analysis.SlotBinder; -import org.apache.doris.nereids.rules.expression.AbstractExpressionRewriteRule; -import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext; -import org.apache.doris.nereids.trees.expressions.ArrayItemReference; -import org.apache.doris.nereids.trees.expressions.BinaryArithmetic; -import org.apache.doris.nereids.trees.expressions.BitNot; -import org.apache.doris.nereids.trees.expressions.CaseWhen; -import org.apache.doris.nereids.trees.expressions.Cast; -import org.apache.doris.nereids.trees.expressions.ComparisonPredicate; -import org.apache.doris.nereids.trees.expressions.CompoundPredicate; -import org.apache.doris.nereids.trees.expressions.Divide; -import org.apache.doris.nereids.trees.expressions.EqualTo; -import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.InPredicate; -import org.apache.doris.nereids.trees.expressions.InSubquery; -import org.apache.doris.nereids.trees.expressions.IntegralDivide; -import org.apache.doris.nereids.trees.expressions.ListQuery; -import org.apache.doris.nereids.trees.expressions.Match; -import org.apache.doris.nereids.trees.expressions.Not; -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.FunctionBuilder; -import org.apache.doris.nereids.trees.expressions.functions.agg.Count; -import org.apache.doris.nereids.trees.expressions.functions.scalar.Lambda; -import org.apache.doris.nereids.trees.expressions.functions.scalar.Nvl; -import org.apache.doris.nereids.trees.expressions.functions.udf.AliasUdfBuilder; -import org.apache.doris.nereids.trees.expressions.literal.BigIntLiteral; -import org.apache.doris.nereids.trees.expressions.typecoercion.ImplicitCastInputTypes; -import org.apache.doris.nereids.types.ArrayType; -import org.apache.doris.nereids.types.BigIntType; -import org.apache.doris.nereids.types.BooleanType; -import org.apache.doris.nereids.types.DataType; -import org.apache.doris.nereids.util.TypeCoercionUtils; - -import com.google.common.collect.ImmutableList; -import org.apache.commons.lang3.StringUtils; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.stream.Collectors; - -/** - * function binder - */ -public class FunctionBinder extends AbstractExpressionRewriteRule { - - public static final FunctionBinder INSTANCE = new FunctionBinder(); - - @Override - public Expression visit(Expression expr, ExpressionRewriteContext context) { - expr = super.visit(expr, context); - expr.checkLegalityBeforeTypeCoercion(); - // this cannot be removed, because some function already construct in parser. - if (expr instanceof ImplicitCastInputTypes) { - List expectedInputTypes = ((ImplicitCastInputTypes) expr).expectedInputTypes(); - if (!expectedInputTypes.isEmpty()) { - return TypeCoercionUtils.implicitCastInputTypes(expr, expectedInputTypes); - } - } - return expr; - } - - /* ******************************************************************************************** - * bind function - * ******************************************************************************************** */ - private void checkBoundLambda(Expression lambdaFunction, List argumentNames) { - lambdaFunction.foreachUp(e -> { - if (e instanceof UnboundSlot) { - UnboundSlot unboundSlot = (UnboundSlot) e; - throw new AnalysisException("Unknown lambda slot '" - + unboundSlot.getNameParts().get(unboundSlot.getNameParts().size() - 1) - + " in lambda arguments" + argumentNames); - } - }); - } - - private UnboundFunction bindHighOrderFunction(UnboundFunction unboundFunction, ExpressionRewriteContext context) { - int childrenSize = unboundFunction.children().size(); - List subChildren = new ArrayList<>(); - for (int i = 1; i < childrenSize; i++) { - subChildren.add(unboundFunction.child(i).accept(this, context)); - } - - // bindLambdaFunction - Lambda lambda = (Lambda) unboundFunction.children().get(0); - Expression lambdaFunction = lambda.getLambdaFunction(); - List arrayItemReferences = lambda.makeArguments(subChildren); - - // 1.bindSlot - List boundedSlots = arrayItemReferences.stream() - .map(ArrayItemReference::toSlot) - .collect(ImmutableList.toImmutableList()); - lambdaFunction = new SlotBinder(new Scope(boundedSlots), context.cascadesContext, - true, false).bind(lambdaFunction); - checkBoundLambda(lambdaFunction, lambda.getLambdaArgumentNames()); - - // 2.bindFunction - lambdaFunction = lambdaFunction.accept(this, context); - - Lambda lambdaClosure = lambda.withLambdaFunctionArguments(lambdaFunction, arrayItemReferences); - - // We don't add the ArrayExpression in high order function at all - return unboundFunction.withChildren(ImmutableList.builder() - .add(lambdaClosure) - .build()); - } - - @Override - public Expression visitUnboundFunction(UnboundFunction unboundFunction, ExpressionRewriteContext context) { - if (unboundFunction.isHighOrder()) { - unboundFunction = bindHighOrderFunction(unboundFunction, context); - } else { - unboundFunction = unboundFunction.withChildren(unboundFunction.children().stream() - .map(e -> e.accept(this, context)).collect(Collectors.toList())); - } - - // bind function - FunctionRegistry functionRegistry = Env.getCurrentEnv().getFunctionRegistry(); - List arguments = unboundFunction.isDistinct() - ? ImmutableList.builder() - .add(unboundFunction.isDistinct()) - .addAll(unboundFunction.getArguments()) - .build() - : (List) unboundFunction.getArguments(); - - if (StringUtils.isEmpty(unboundFunction.getDbName())) { - // we will change arithmetic function like add(), subtract(), bitnot() - // to the corresponding objects rather than BoundFunction. - ArithmeticFunctionBinder functionBinder = new ArithmeticFunctionBinder(); - if (functionBinder.isBinaryArithmetic(unboundFunction.getName())) { - return functionBinder.bindBinaryArithmetic(unboundFunction.getName(), unboundFunction.children()) - .accept(this, context); - } - } - - String functionName = unboundFunction.getName(); - FunctionBuilder builder = functionRegistry.findFunctionBuilder( - unboundFunction.getDbName(), functionName, arguments); - if (builder instanceof AliasUdfBuilder) { - // we do type coercion in build function in alias function, so it's ok to return directly. - return builder.build(functionName, arguments).first; - } else { - Expression boundFunction = TypeCoercionUtils - .processBoundFunction((BoundFunction) builder.build(functionName, arguments).first); - if (boundFunction instanceof Count - && context.cascadesContext.getOuterScope().isPresent() - && !context.cascadesContext.getOuterScope().get().getCorrelatedSlots() - .isEmpty()) { - // consider sql: SELECT * FROM t1 WHERE t1.a <= (SELECT COUNT(t2.a) FROM t2 WHERE (t1.b = t2.b)); - // when unnest correlated subquery, we create a left join node. - // outer query is left table and subquery is right one - // if there is no match, the row from right table is filled with nulls - // but COUNT function is always not nullable. - // so wrap COUNT with Nvl to ensure it's result is 0 instead of null to get the correct result - boundFunction = new Nvl(boundFunction, new BigIntLiteral(0)); - } - return boundFunction; - } - } - - @Override - public Expression visitBoundFunction(BoundFunction boundFunction, ExpressionRewriteContext context) { - boundFunction = (BoundFunction) super.visitBoundFunction(boundFunction, context); - return TypeCoercionUtils.processBoundFunction(boundFunction); - } - - /** - * gets the method for calculating the time. - * e.g. YEARS_ADD、YEARS_SUB、DAYS_ADD 、DAYS_SUB - */ - @Override - public Expression visitTimestampArithmetic(TimestampArithmetic arithmetic, ExpressionRewriteContext context) { - Expression left = arithmetic.left().accept(this, context); - Expression right = arithmetic.right().accept(this, context); - - arithmetic = (TimestampArithmetic) arithmetic.withChildren(left, right); - // bind function - String funcOpName; - if (arithmetic.getFuncName() == null) { - // e.g. YEARS_ADD, MONTHS_SUB - funcOpName = String.format("%sS_%s", arithmetic.getTimeUnit(), - (arithmetic.getOp() == Operator.ADD) ? "ADD" : "SUB"); - } else { - funcOpName = arithmetic.getFuncName(); - } - arithmetic = (TimestampArithmetic) arithmetic.withFuncName(funcOpName.toLowerCase(Locale.ROOT)); - - // type coercion - return TypeCoercionUtils.processTimestampArithmetic(arithmetic); - } - - /* ******************************************************************************************** - * type coercion - * ******************************************************************************************** */ - - @Override - public Expression visitBitNot(BitNot bitNot, ExpressionRewriteContext context) { - Expression child = bitNot.child().accept(this, context); - // type coercion - if (!(child.getDataType().isIntegralType() || child.getDataType().isBooleanType())) { - child = new Cast(child, BigIntType.INSTANCE); - } - return bitNot.withChildren(child); - } - - @Override - public Expression visitDivide(Divide divide, ExpressionRewriteContext context) { - Expression left = divide.left().accept(this, context); - Expression right = divide.right().accept(this, context); - divide = (Divide) divide.withChildren(left, right); - // type coercion - return TypeCoercionUtils.processDivide(divide); - } - - @Override - public Expression visitIntegralDivide(IntegralDivide integralDivide, ExpressionRewriteContext context) { - Expression left = integralDivide.left().accept(this, context); - Expression right = integralDivide.right().accept(this, context); - integralDivide = (IntegralDivide) integralDivide.withChildren(left, right); - // type coercion - return TypeCoercionUtils.processIntegralDivide(integralDivide); - } - - @Override - public Expression visitBinaryArithmetic(BinaryArithmetic binaryArithmetic, ExpressionRewriteContext context) { - Expression left = binaryArithmetic.left().accept(this, context); - Expression right = binaryArithmetic.right().accept(this, context); - binaryArithmetic = (BinaryArithmetic) binaryArithmetic.withChildren(left, right); - return TypeCoercionUtils.processBinaryArithmetic(binaryArithmetic); - } - - @Override - public Expression visitCompoundPredicate(CompoundPredicate compoundPredicate, ExpressionRewriteContext context) { - Expression left = compoundPredicate.left().accept(this, context); - Expression right = compoundPredicate.right().accept(this, context); - CompoundPredicate ret = (CompoundPredicate) compoundPredicate.withChildren(left, right); - return TypeCoercionUtils.processCompoundPredicate(ret); - } - - @Override - public Expression visitNot(Not not, ExpressionRewriteContext context) { - Expression child = not.child().accept(this, context); - child = TypeCoercionUtils.castIfNotSameType(child, BooleanType.INSTANCE); - return not.withChildren(child); - } - - @Override - public Expression visitComparisonPredicate(ComparisonPredicate cp, ExpressionRewriteContext context) { - Expression left = cp.left().accept(this, context); - Expression right = cp.right().accept(this, context); - cp = (ComparisonPredicate) cp.withChildren(left, right); - return TypeCoercionUtils.processComparisonPredicate(cp); - } - - @Override - public Expression visitCaseWhen(CaseWhen caseWhen, ExpressionRewriteContext context) { - List rewrittenChildren = caseWhen.children().stream() - .map(e -> e.accept(this, context)).collect(Collectors.toList()); - CaseWhen newCaseWhen = caseWhen.withChildren(rewrittenChildren); - newCaseWhen.checkLegalityBeforeTypeCoercion(); - return TypeCoercionUtils.processCaseWhen(newCaseWhen); - } - - @Override - public Expression visitWhenClause(WhenClause whenClause, ExpressionRewriteContext context) { - return whenClause.withChildren(TypeCoercionUtils.castIfNotSameType( - whenClause.getOperand().accept(this, context), BooleanType.INSTANCE), - whenClause.getResult().accept(this, context)); - } - - @Override - public Expression visitInPredicate(InPredicate inPredicate, ExpressionRewriteContext context) { - List rewrittenChildren = inPredicate.children().stream() - .map(e -> e.accept(this, context)).collect(Collectors.toList()); - InPredicate newInPredicate = inPredicate.withChildren(rewrittenChildren); - return TypeCoercionUtils.processInPredicate(newInPredicate); - } - - @Override - public Expression visitInSubquery(InSubquery inSubquery, ExpressionRewriteContext context) { - Expression newCompareExpr = inSubquery.getCompareExpr().accept(this, context); - Expression newListQuery = inSubquery.getListQuery().accept(this, context); - ComparisonPredicate afterTypeCoercion = (ComparisonPredicate) TypeCoercionUtils.processComparisonPredicate( - new EqualTo(newCompareExpr, newListQuery)); - if (newListQuery.getDataType().isBitmapType()) { - if (!newCompareExpr.getDataType().isBigIntType()) { - newCompareExpr = new Cast(newCompareExpr, BigIntType.INSTANCE); - } - } else { - newCompareExpr = afterTypeCoercion.left(); - } - return new InSubquery(newCompareExpr, (ListQuery) afterTypeCoercion.right(), - inSubquery.getCorrelateSlots(), ((ListQuery) afterTypeCoercion.right()).getTypeCoercionExpr(), - inSubquery.isNot()); - } - - @Override - public Expression visitMatch(Match match, ExpressionRewriteContext context) { - Expression left = match.left().accept(this, context); - Expression right = match.right().accept(this, context); - // check child type - if (!left.getDataType().isStringLikeType() - && !(left.getDataType() instanceof ArrayType - && ((ArrayType) left.getDataType()).getItemType().isStringLikeType()) - && !left.getDataType().isVariantType()) { - throw new AnalysisException(String.format( - "left operand '%s' part of predicate " - + "'%s' should return type 'STRING', 'ARRAY or VARIANT' but " - + "returns type '%s'.", - left.toSql(), match.toSql(), left.getDataType())); - } - - if (!right.getDataType().isStringLikeType() && !right.getDataType().isNullType()) { - throw new AnalysisException(String.format( - "right operand '%s' part of predicate " + "'%s' should return type 'STRING' but " - + "returns type '%s'.", - right.toSql(), match.toSql(), right.getDataType())); - } - - if (left.getDataType().isVariantType()) { - left = new Cast(left, right.getDataType()); - } - return match.withChildren(left, right); - } - - @Override - public Expression visitCast(Cast cast, ExpressionRewriteContext context) { - cast = (Cast) super.visitCast(cast, context); - // NOTICE: just for compatibility with legacy planner. - if (cast.child().getDataType().isComplexType() || cast.getDataType().isComplexType()) { - TypeCoercionUtils.checkCanCastTo(cast.child().getDataType(), cast.getDataType()); - } - return cast; - } -} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdfBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdfBuilder.java index 9ddb8ea25e..2886b5cf4a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdfBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdfBuilder.java @@ -19,7 +19,9 @@ package org.apache.doris.nereids.trees.expressions.functions.udf; import org.apache.doris.common.Pair; import org.apache.doris.common.util.ReflectionUtils; -import org.apache.doris.nereids.rules.expression.rules.FunctionBinder; +import org.apache.doris.nereids.analyzer.Scope; +import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer; +import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.SlotReference; import org.apache.doris.nereids.trees.expressions.functions.BoundFunction; @@ -28,6 +30,7 @@ import org.apache.doris.nereids.types.DataType; import org.apache.doris.nereids.util.TypeCoercionUtils; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import java.util.List; @@ -80,22 +83,31 @@ public class AliasUdfBuilder extends UdfBuilder { Expression processedExpression = TypeCoercionUtils.processBoundFunction(boundAliasFunction); List inputs = processedExpression.getArguments(); - Expression boundFunction = FunctionBinder.INSTANCE.rewrite(aliasUdf.getUnboundFunction(), null); - // replace the placeholder slot to the input expressions. // adjust input, parameter and replaceMap to be corresponding. - Map slots = (boundFunction - .collect(SlotReference.class::isInstance)) - .stream().collect(Collectors.toMap(SlotReference::getName, k -> k, (v1, v2) -> v2)); + Map slots = Maps.newLinkedHashMap(); + aliasUdf.getUnboundFunction().foreachUp(child -> { + if (child instanceof SlotReference) { + slots.put(((SlotReference) child).getName(), (SlotReference) child); + } + }); - Map replaceMap = Maps.newHashMap(); + Map paramSlotToRealInput = Maps.newHashMap(); for (int i = 0; i < inputs.size(); ++i) { String parameter = aliasUdf.getParameters().get(i); Preconditions.checkArgument(slots.containsKey(parameter)); - replaceMap.put(slots.get(parameter), inputs.get(i)); + paramSlotToRealInput.put(slots.get(parameter), inputs.get(i)); } - return Pair.of(SlotReplacer.INSTANCE.replace(boundFunction, replaceMap), boundAliasFunction); + ExpressionAnalyzer udfAnalyzer = new ExpressionAnalyzer( + null, new Scope(ImmutableList.of()), null, false, false) { + @Override + public Expression visitSlotReference(SlotReference slotReference, ExpressionRewriteContext context) { + return paramSlotToRealInput.get(slotReference); + } + }; + + return Pair.of(udfAnalyzer.analyze(aliasUdf.getUnboundFunction()), boundAliasFunction); } private static class SlotReplacer extends DefaultExpressionRewriter> { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/UpdateCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/UpdateCommand.java index a62f021b9e..48766caa5c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/UpdateCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/UpdateCommand.java @@ -27,7 +27,7 @@ import org.apache.doris.nereids.analyzer.UnboundSlot; import org.apache.doris.nereids.analyzer.UnboundTableSinkCreator; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.parser.NereidsParser; -import org.apache.doris.nereids.rules.analysis.SlotBinder; +import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer; import org.apache.doris.nereids.trees.expressions.EqualTo; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.NamedExpression; @@ -212,8 +212,8 @@ public class UpdateCommand extends Command implements ForwardWithSync, Explainab throw new AnalysisException("column in assignment list is invalid, " + String.join(".", columnNameParts)); } List tableQualifier = RelationUtil.getQualifierName(ctx, nameParts); - if (!SlotBinder.sameTableName(tableAlias == null ? tableQualifier.get(2) : tableAlias, tableName) - || (dbName != null && SlotBinder.compareDbName(tableQualifier.get(1), dbName))) { + if (!ExpressionAnalyzer.sameTableName(tableAlias == null ? tableQualifier.get(2) : tableAlias, tableName) + || (dbName != null && ExpressionAnalyzer.compareDbName(tableQualifier.get(1), dbName))) { throw new AnalysisException("column in assignment list is invalid, " + String.join(".", columnNameParts)); } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java index 58ce64c09b..02f3caffa8 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java @@ -22,7 +22,6 @@ import org.apache.doris.nereids.datasets.tpch.AnalyzeCheckTestBase; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.properties.OrderKey; import org.apache.doris.nereids.rules.expression.ExpressionRewrite; -import org.apache.doris.nereids.rules.expression.rules.FunctionBinder; import org.apache.doris.nereids.trees.expressions.Add; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.Cast; @@ -95,7 +94,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Memo SlotReference value = new SlotReference(new ExprId(3), "value", TinyIntType.INSTANCE, true, ImmutableList.of()); PlanChecker.from(connectContext).analyze(sql) - .applyBottomUp(new ExpressionRewrite(FunctionBinder.INSTANCE)) + .applyBottomUp(new ExpressionRewrite(ExpressionAnalyzer.FUNCTION_ANALYZER_RULE)) .matches( logicalProject( logicalFilter( @@ -108,7 +107,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Memo sql = "SELECT a1 as value FROM t1 GROUP BY a1 HAVING value > 0"; PlanChecker.from(connectContext).analyze(sql) - .applyBottomUp(new ExpressionRewrite(FunctionBinder.INSTANCE)) + .applyBottomUp(new ExpressionRewrite(ExpressionAnalyzer.FUNCTION_ANALYZER_RULE)) .matches( logicalFilter( logicalProject( @@ -129,7 +128,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Memo ); Alias sumA2 = new Alias(new ExprId(3), new Sum(a2), "sum(a2)"); PlanChecker.from(connectContext).analyze(sql) - .applyBottomUp(new ExpressionRewrite(FunctionBinder.INSTANCE)) + .applyBottomUp(new ExpressionRewrite(ExpressionAnalyzer.FUNCTION_ANALYZER_RULE)) .matches( logicalProject( logicalFilter( diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java index b252b4650f..e245ee2f7e 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java @@ -21,7 +21,7 @@ import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.analyzer.UnboundRelation; import org.apache.doris.nereids.analyzer.UnboundSlot; import org.apache.doris.nereids.parser.NereidsParser; -import org.apache.doris.nereids.rules.expression.rules.FunctionBinder; +import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.expressions.SlotReference; @@ -106,7 +106,7 @@ public abstract class ExpressionRewriteTestHelper extends ExpressionRewrite { } protected Expression typeCoercion(Expression expression) { - return FunctionBinder.INSTANCE.rewrite(expression, null); + return ExpressionAnalyzer.FUNCTION_ANALYZER_RULE.rewrite(expression, null); } protected DataType getType(char t) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java index 6e8d7aea8e..cc86462aea 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/FoldConstantTest.java @@ -21,8 +21,8 @@ import org.apache.doris.analysis.ArithmeticExpr.Operator; import org.apache.doris.common.Config; import org.apache.doris.nereids.analyzer.UnboundRelation; import org.apache.doris.nereids.parser.NereidsParser; +import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer; import org.apache.doris.nereids.rules.expression.rules.FoldConstantRuleOnFE; -import org.apache.doris.nereids.rules.expression.rules.FunctionBinder; import org.apache.doris.nereids.trees.expressions.Cast; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.GreaterThan; @@ -715,7 +715,7 @@ class FoldConstantTest extends ExpressionRewriteTestHelper { MemoTestUtils.createCascadesContext(new UnboundRelation(new RelationId(1), ImmutableList.of("test_table")))); NereidsParser parser = new NereidsParser(); Expression e1 = parser.parseExpression(actualExpression); - e1 = new ExpressionNormalization().rewrite(FunctionBinder.INSTANCE.rewrite(e1, context), context); + e1 = new ExpressionNormalization().rewrite(ExpressionAnalyzer.FUNCTION_ANALYZER_RULE.rewrite(e1, context), context); Assertions.assertTrue(e1.getDataType() instanceof VarcharType); } @@ -725,7 +725,7 @@ class FoldConstantTest extends ExpressionRewriteTestHelper { NereidsParser parser = new NereidsParser(); Expression e1 = parser.parseExpression(actualExpression); - e1 = new ExpressionNormalization().rewrite(FunctionBinder.INSTANCE.rewrite(e1, context), context); + e1 = new ExpressionNormalization().rewrite(ExpressionAnalyzer.FUNCTION_ANALYZER_RULE.rewrite(e1, context), context); Assertions.assertEquals(expectedExpression, e1.toSql()); } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyArithmeticRuleTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyArithmeticRuleTest.java index e7423fe12d..55ce93da63 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyArithmeticRuleTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyArithmeticRuleTest.java @@ -17,8 +17,8 @@ package org.apache.doris.nereids.rules.expression; +import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer; import org.apache.doris.nereids.rules.expression.rules.FoldConstantRule; -import org.apache.doris.nereids.rules.expression.rules.FunctionBinder; import org.apache.doris.nereids.rules.expression.rules.SimplifyArithmeticComparisonRule; import org.apache.doris.nereids.rules.expression.rules.SimplifyArithmeticRule; @@ -30,7 +30,7 @@ class SimplifyArithmeticRuleTest extends ExpressionRewriteTestHelper { void testSimplifyArithmetic() { executor = new ExpressionRuleExecutor(ImmutableList.of( bottomUp(SimplifyArithmeticRule.INSTANCE), - FunctionBinder.INSTANCE, + ExpressionAnalyzer.FUNCTION_ANALYZER_RULE, bottomUp( FoldConstantRule.INSTANCE ) @@ -102,7 +102,7 @@ class SimplifyArithmeticRuleTest extends ExpressionRewriteTestHelper { SimplifyArithmeticComparisonRule.INSTANCE, SimplifyArithmeticRule.INSTANCE ), - FunctionBinder.INSTANCE, + ExpressionAnalyzer.FUNCTION_ANALYZER_RULE, bottomUp( FoldConstantRule.INSTANCE ) @@ -146,7 +146,7 @@ class SimplifyArithmeticRuleTest extends ExpressionRewriteTestHelper { SimplifyArithmeticComparisonRule.INSTANCE, SimplifyArithmeticRule.INSTANCE ), - FunctionBinder.INSTANCE, + ExpressionAnalyzer.FUNCTION_ANALYZER_RULE, bottomUp( FoldConstantRule.INSTANCE ) diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyRangeTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyRangeTest.java index f2c7425193..30155b3bf7 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyRangeTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/SimplifyRangeTest.java @@ -21,7 +21,7 @@ import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.analyzer.UnboundRelation; import org.apache.doris.nereids.analyzer.UnboundSlot; import org.apache.doris.nereids.parser.NereidsParser; -import org.apache.doris.nereids.rules.expression.rules.FunctionBinder; +import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer; import org.apache.doris.nereids.rules.expression.rules.SimplifyRange; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.Slot; @@ -364,7 +364,7 @@ public class SimplifyRangeTest extends ExpressionRewrite { } protected Expression typeCoercion(Expression expression) { - return FunctionBinder.INSTANCE.rewrite(expression, null); + return ExpressionAnalyzer.FUNCTION_ANALYZER_RULE.rewrite(expression, null); } private DataType getType(char t) {