This is an automated email from the ASF dual-hosted git repository.
morrySnow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 69aa8764dfe [fix](fe) Fix alias function with cast outermost expr and
reject illegal expressions (#63254)
69aa8764dfe is described below
commit 69aa8764dfe4750934483ce8157457284d309052
Author: morrySnow <[email protected]>
AuthorDate: Mon May 18 15:13:34 2026 +0800
[fix](fe) Fix alias function with cast outermost expr and reject illegal
expressions (#63254)
### What problem does this PR solve?
Problem Summary:
1. When an alias function body has a cast as the outermost expression
(e.g., `cast(n as varchar(20))`), AliasUdf.translateToNereidsFunction
attempted to cast the parsed expression to UnboundFunction, causing
ClassCastException. Fixed by changing the unboundFunction field type
from UnboundFunction to Expression so any expression can be stored.
2. Alias functions whose body contains aggregate functions, window
expressions, subqueries, or other non-scalar expressions could be
created successfully, which is incorrect. Added a containsType check in
translateToLegacyExpr that throws AnalysisException("Alias function only
supports scalar functions.") before attempting the legacy translation.
### Release note
Fix alias function creation failure when the outermost expression is a
cast, and reject alias functions containing aggregate/window/subquery
expressions.
Co-authored-by: Copilot <[email protected]>
---
.../trees/expressions/functions/udf/AliasUdf.java | 17 +++---------
.../plans/commands/CreateFunctionCommand.java | 14 +++++++++-
.../doris/nereids/trees/expressions/UdfTest.java | 31 ++++++++++++++++++++++
3 files changed, 48 insertions(+), 14 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdf.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdf.java
index 041df6694d4..60833ea70cc 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdf.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/udf/AliasUdf.java
@@ -22,7 +22,6 @@ import org.apache.doris.analysis.ToSqlParams;
import org.apache.doris.catalog.AliasFunction;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.FunctionSignature;
-import org.apache.doris.nereids.analyzer.UnboundFunction;
import org.apache.doris.nereids.parser.NereidsParser;
import org.apache.doris.nereids.trees.expressions.Expression;
import
org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature;
@@ -44,23 +43,15 @@ import java.util.stream.Collectors;
* alias function
*/
public class AliasUdf extends ScalarFunction implements
ExplicitlyCastableSignature {
- private final UnboundFunction unboundFunction;
+ private final Expression unboundFunction;
private final List<String> parameters;
private final List<DataType> argTypes;
private final Map<String, String> sessionVariables;
- /**
- * constructor
- */
- public AliasUdf(String name, List<DataType> argTypes, UnboundFunction
unboundFunction,
- List<String> parameters, Expression... arguments) {
- this(name, argTypes, unboundFunction, parameters, null, arguments);
- }
-
/**
* constructor with session variables
*/
- public AliasUdf(String name, List<DataType> argTypes, UnboundFunction
unboundFunction,
+ public AliasUdf(String name, List<DataType> argTypes, Expression
unboundFunction,
List<String> parameters, Map<String, String> sessionVariables,
Expression... arguments) {
super(name, arguments);
this.argTypes = argTypes;
@@ -78,7 +69,7 @@ public class AliasUdf extends ScalarFunction implements
ExplicitlyCastableSignat
return parameters;
}
- public UnboundFunction getUnboundFunction() {
+ public Expression getUnboundFunction() {
return unboundFunction;
}
@@ -110,7 +101,7 @@ public class AliasUdf extends ScalarFunction implements
ExplicitlyCastableSignat
AliasUdf aliasUdf = new AliasUdf(
function.functionName(),
Arrays.stream(function.getArgs()).map(DataType::fromCatalogType).collect(Collectors.toList()),
- ((UnboundFunction) parsedFunction),
+ parsedFunction,
function.getParameters(),
sessionVariables);
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateFunctionCommand.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateFunctionCommand.java
index eaacae8aaa6..d2d14b02012 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateFunctionCommand.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateFunctionCommand.java
@@ -67,10 +67,15 @@ import
org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.IntegralDivide;
import org.apache.doris.nereids.trees.expressions.Mod;
import org.apache.doris.nereids.trees.expressions.Multiply;
+import org.apache.doris.nereids.trees.expressions.Placeholder;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
import org.apache.doris.nereids.trees.expressions.Subtract;
+import org.apache.doris.nereids.trees.expressions.WindowExpression;
import org.apache.doris.nereids.trees.expressions.functions.BoundFunction;
+import
org.apache.doris.nereids.trees.expressions.functions.scalar.GroupingScalarFunction;
+import
org.apache.doris.nereids.trees.expressions.functions.table.TableValuedFunction;
import org.apache.doris.nereids.trees.plans.PlanType;
import org.apache.doris.nereids.trees.plans.commands.info.FunctionArgTypesInfo;
import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
@@ -1137,7 +1142,7 @@ public class CreateFunctionCommand extends Command
implements ForwardWithSync {
ConnectContext.get().getStatementContext().getNextRelationId(), new
ArrayList<>());
CascadesContext cascadesContext =
CascadesContext.initContext(ctx.getStatementContext(), plan,
PhysicalProperties.ANY);
- Map<String, DataType> argTypeMap = new CaseInsensitiveMap();
+ Map<String, DataType> argTypeMap = new CaseInsensitiveMap<>();
List<DataType> argTypes = argsDef.getArgTypeDefs();
if (!parameters.isEmpty()) {
if (parameters.size() != argTypes.size()) {
@@ -1151,6 +1156,13 @@ public class CreateFunctionCommand extends Command
implements ForwardWithSync {
ExpressionAnalyzer analyzer = new
CustomExpressionAnalyzer(cascadesContext, argTypeMap);
expression = analyzer.analyze(expression);
+ if (expression.containsType(
+
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction.class,
+ GroupingScalarFunction.class, WindowExpression.class,
Placeholder.class,
+ TableValuedFunction.class, SubqueryExpr.class)) {
+ throw new AnalysisException("Alias function only supports scalar
functions.");
+ }
+
PlanTranslatorContext translatorContext = new
PlanTranslatorContext(cascadesContext);
ExpressionToExpr translator = new ExpressionToExpr();
return expression.accept(translator, translatorContext);
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/UdfTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/UdfTest.java
index d2e004d0a54..73a44299f73 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/UdfTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/UdfTest.java
@@ -188,6 +188,37 @@ public class UdfTest extends TestWithFeService implements
PlanPatternMatchSuppor
);
}
+ @Test
+ public void testAliasFunctionWithCastOutermostExpression() throws
Exception {
+ // Bug fix: when the outermost expression of an alias function body is
a Cast,
+ // AliasUdf.translateToNereidsFunction previously cast parsedFunction
to UnboundFunction,
+ // causing ClassCastException. After the fix, parsedFunction is kept
as Expression.
+ createFunction(
+ "create alias function f_cast_varchar(int) with parameter(n)
as cast(n as varchar(20))");
+
+ Assertions.assertEquals(1, Env.getCurrentEnv().getFunctionRegistry()
+ .findUdfBuilder(connectContext.getDatabase(),
"f_cast_varchar").size());
+
+ // Verify the function can be used in a query without error
+ PlanChecker.from(connectContext)
+ .analyze("select f_cast_varchar(42)")
+ .matches(
+ logicalOneRowRelation()
+ .when(oneRow -> oneRow.getProjects().size() ==
1)
+ );
+ }
+
+ @Test
+ public void testAliasFunctionWithIllegalExpressionsRejected() throws
Exception {
+ // Bug fix: before the fix, alias functions containing aggregate
functions in their body
+ // could be created successfully, which is incorrect behavior.
+ // After the fix, they are rejected with a clear error message.
+ Exception e = Assertions.assertThrows(Exception.class, () ->
+ createFunction(
+ "create alias function f_agg_rejected(int) with
parameter(n) as sum(n)"));
+ Assertions.assertTrue(e.getMessage().contains("Alias function only
supports scalar functions."));
+ }
+
@Test
public void testReadFromStream() throws Exception {
createFunction("create global alias function f8(int) with parameter(n)
as hours_add(now(3), n)");
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]