You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2026/05/18 07:13:42 UTC
(doris) branch master updated: [fix](fe) Fix alias function with cast outermost expr and reject illegal expressions (#63254)
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 <zh...@selectdb.com>
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 <22...@users.noreply.github.com>
---
.../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: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org