From 36c5dcf1e913d40de506b1e2e2a047086940f915 Mon Sep 17 00:00:00 2001 From: lichi Date: Wed, 15 Apr 2026 17:34:11 +0800 Subject: [PATCH 01/10] [fix](user_var)fix integer typing and prevent Variable->literal timing issue in expression analysis --- .../rules/analysis/ExpressionAnalyzer.java | 13 ++- .../org/apache/doris/qe/ConnectContext.java | 10 +- .../analysis/UserVariableAnalysisTest.java | 96 +++++++++++++++++++ .../data/query_p0/set/test_user_var.out | 3 + .../suites/query_p0/set/test_user_var.groovy | 28 ++++++ 5 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java 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 e2566c8427f464..dd6af316350dcd 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 @@ -219,7 +219,18 @@ public Expression visit(Expression expr, ExpressionRewriteContext context) { * ******************************************************************************************** */ @Override public Expression visitUnboundVariable(UnboundVariable unboundVariable, ExpressionRewriteContext context) { - return resolveUnboundVariable(unboundVariable); + Variable variable = resolveUnboundVariable(unboundVariable); + // Record used variable for SQL cache when context is available. + // Defensive null-check: some callers may invoke analysis without a full + // ExpressionRewriteContext (e.g. in isolated tests), so guard access. + if (context != null && context.cascadesContext != null) { + Optional sqlCacheContext = context.cascadesContext.getStatementContext() + .getSqlCacheContext(); + if (sqlCacheContext.isPresent()) { + sqlCacheContext.get().addUsedVariable(variable); + } + } + return variable.getRealExpression(); } /** resolveUnboundVariable */ diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java index 5fe0241bc3cd37..adcd11612f794e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java @@ -88,6 +88,7 @@ import org.xnio.StreamConnection; import java.io.IOException; +import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; @@ -590,7 +591,14 @@ public void setUserVar(String name, LiteralExpr value) { } else if (literalExpr instanceof IntLiteral) { // the value in the IntLiteral should be int, but now is long in old planner literalExpr // so type coercion to generate right new planner int Literal - return Literal.of((int) ((IntLiteral) literalExpr).getValue()); + switch (literalExpr.getType().getPrimitiveType()) { + case LARGEINT: + return Literal.of(new BigInteger(literalExpr.getStringValue())); + case BIGINT: + return Literal.of(((IntLiteral) literalExpr).getValue()); + default: + return Literal.of((int) ((IntLiteral) literalExpr).getValue()); + } } else if (literalExpr instanceof FloatLiteral) { return Literal.of(((FloatLiteral) literalExpr).getValue()); } else if (literalExpr instanceof DecimalLiteral) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java new file mode 100644 index 00000000000000..38e9faf7d8c233 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java @@ -0,0 +1,96 @@ +// 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.IntLiteral; +import org.apache.doris.nereids.CascadesContext; +import org.apache.doris.nereids.analyzer.UnboundVariable; +import org.apache.doris.nereids.analyzer.UnboundVariable.VariableType; +import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext; +import org.apache.doris.nereids.trees.expressions.ComparisonPredicate; +import org.apache.doris.nereids.trees.expressions.EqualTo; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.Variable; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.types.BigIntType; +import org.apache.doris.nereids.types.IntegerType; +import org.apache.doris.nereids.util.MemoTestUtils; +import org.apache.doris.qe.ConnectContext; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +/** Tests for user variable handling in expression analysis. */ +public class UserVariableAnalysisTest { + + @Test + public void testUserVarIntegerTypeSmall() { + ConnectContext ctx = MemoTestUtils.createConnectContext(); + // set user var @a = 1 (small int) + ctx.setUserVar("a", new IntLiteral(1L)); + + // get nereids literal via ConnectContext helper + Literal l = ConnectContext.get().getLiteralForUserVar("a"); + Assertions.assertNotNull(l); + Assertions.assertEquals(IntegerType.INSTANCE, l.getDataType()); + } + + @Test + public void testUserVarIntegerTypeBig() { + ConnectContext ctx = MemoTestUtils.createConnectContext(); + // set user var @b = Long.MAX_VALUE (bigint) + ctx.setUserVar("b", new IntLiteral(Long.MAX_VALUE)); + + Literal l = ConnectContext.get().getLiteralForUserVar("b"); + Assertions.assertNotNull(l); + Assertions.assertEquals(BigIntType.INSTANCE, l.getDataType()); + } + + @Test + public void testVariableRecordedAndTypeCoercion() { + // create context and cascades context (sql cache enabled by default in test utils) + ConnectContext ctx = MemoTestUtils.createConnectContext(); + ctx.setUserVar("uvar", new IntLiteral(42L)); + + // create a comparison using an unbound user variable: @uvar = 1 + UnboundVariable unboundVar = new UnboundVariable( + "uvar", VariableType.USER); + IntegerLiteral right = new IntegerLiteral(1); + ComparisonPredicate cmp = new EqualTo(unboundVar, right); + + // prepare analyzer with cascades context so sql cache context exists + CascadesContext cascadesContext = MemoTestUtils.createCascadesContext(ctx, "select 1"); + ExpressionAnalyzer analyzer = new ExpressionAnalyzer(null, + new org.apache.doris.nereids.analyzer.Scope(java.util.Collections.emptyList()), + cascadesContext, false, false); + ExpressionRewriteContext rewriteContext = new ExpressionRewriteContext(cascadesContext); + + // analyze should not throw and should handle type coercion + Expression analyzed = analyzer.analyze(cmp, rewriteContext); + Assertions.assertNotNull(analyzed); + + // sql cache context should have recorded the used variable + List used = cascadesContext.getStatementContext().getSqlCacheContext().get().getUsedVariables(); + Assertions.assertFalse(used.isEmpty()); + boolean found = used.stream().anyMatch(v -> "uvar".equals(v.getName())); + Assertions.assertTrue(found, "user variable should be recorded in sql cache context"); + } +} diff --git a/regression-test/data/query_p0/set/test_user_var.out b/regression-test/data/query_p0/set/test_user_var.out index 3a1c176056779c..c5f8658aac2d29 100644 --- a/regression-test/data/query_p0/set/test_user_var.out +++ b/regression-test/data/query_p0/set/test_user_var.out @@ -20,3 +20,6 @@ true false -- !select7 -- 1 +-- !select_user_var -- +2 + diff --git a/regression-test/suites/query_p0/set/test_user_var.groovy b/regression-test/suites/query_p0/set/test_user_var.groovy index e49564255195c9..a717441113a3a4 100644 --- a/regression-test/suites/query_p0/set/test_user_var.groovy +++ b/regression-test/suites/query_p0/set/test_user_var.groovy @@ -85,4 +85,32 @@ suite("test_user_var") { notContains "CAST" } + multi_sql """ + drop table if exists table_50_undef_partitions2_keys3_properties4_distributed_by54; + create table table_50_undef_partitions2_keys3_properties4_distributed_by54 ( + col_date_undef_signed date null , + col_int_undef_signed int null , + col_datetime_undef_signed datetime null , + pk int + ) engine=olap + DUPLICATE KEY(col_date_undef_signed) + PARTITION BY RANGE(col_date_undef_signed) ( + PARTITION p0 VALUES LESS THAN ('2023-12-11'), + PARTITION p1 VALUES LESS THAN ('2023-12-15'), + PARTITION p2 VALUES LESS THAN ('2023-12-16'), + PARTITION p3 VALUES LESS THAN ('2023-12-17'), + PARTITION p4 VALUES LESS THAN ('2024-01-18'), + PARTITION p5 VALUES LESS THAN ('2026-02-18'), + PARTITION p100 VALUES LESS THAN ('9999-12-31') + ) + + distributed by hash(pk) buckets 10 + properties("replication_num" = "1"); + insert into table_50_undef_partitions2_keys3_properties4_distributed_by54(pk,col_int_undef_signed,col_date_undef_signed,col_datetime_undef_signed) values (0,3,'2023-12-09','2004-12-17 12:35:40'),(1,null,'2023-12-15','2007-03-09 11:14:00'),(2,9,'2023-12-15','2005-01-10 12:14:20'),(3,1,null,'2012-08-13 07:51:37'),(4,5,'2023-12-12','2008-06-09 05:14:31'),(5,5,null,'2003-12-12 03:46:03'),(6,4,'2012-11-08','2013-05-17 19:32:15'),(7,5,'2023-12-15','2013-11-11 05:02:08'),(8,7,'2023-12-15','2001-07-01 20:59:59'),(9,9,'2023-12-14','2007-05-11 16:10:53'),(10,null,'2023-12-17','2014-12-22 08:07:07'),(11,9,null,'2019-12-26 12:33:12'),(12,null,'2023-12-12','2018-02-04 20:16:03'),(13,1,'2023-12-17','2012-12-23 09:29:23'),(14,null,'2023-12-14','2002-01-23 18:40:57'),(15,0,'2023-12-13','2005-03-20 23:57:50'),(16,7,'2023-12-15','2005-04-22 08:56:50'),(17,8,'2023-12-18','2019-06-27 11:46:05'),(18,0,'2023-12-17','2000-10-16 00:48:54'),(19,null,'2023-12-16','2010-02-18 12:33:42'),(20,9,'2023-12-16','2011-06-23 14:58:11'),(21,null,null,'2009-09-22 23:43:05'),(22,6,'2023-12-14','2004-01-12 10:08:48'),(23,3,null,'2008-08-23 17:12:45'),(24,2,'2023-12-16','2013-10-14 01:57:45'),(25,0,null,'2012-12-05 06:31:29'),(26,8,'2023-12-13','2014-04-03 17:49:28'),(27,null,'2023-12-11','2014-04-07 19:37:58'),(28,1,'2023-12-18','2014-09-18 14:09:04'),(29,8,'2023-12-14','2004-09-18 16:57:38'),(30,null,'2023-12-13','2014-10-10 01:44:04'),(31,9,'2023-12-15','2017-09-24 12:33:16'),(32,2,'2023-12-16','2013-11-15 17:12:36'),(33,4,'2005-04-24','2003-03-06 08:47:44'),(34,6,'2023-12-16','2001-05-21 10:40:16'),(35,2,'2023-12-17','2001-06-02 02:34:54'),(36,4,'2023-12-14','2010-03-06 08:46:49'),(37,4,null,'2000-04-08 07:56:25'),(38,8,'2023-12-10','2018-03-24 22:17:31'),(39,0,'2023-12-11','2009-12-13 17:40:47'),(40,9,'2023-12-16','2014-05-04 01:17:18'),(41,5,'2017-02-23','2005-04-10 15:34:29'),(42,2,'2023-12-15','2013-12-16 10:12:23'),(43,6,'2023-12-12','2000-07-28 14:30:17'),(44,7,'2023-12-11','2002-03-24 11:43:04'),(45,7,'2023-12-09','2012-07-25 19:06:56'),(46,null,'2005-12-05','2018-10-07 21:28:38'),(47,1,'2023-12-15','2019-06-15 23:13:02'),(48,4,null,'2012-10-23 02:55:35'),(49,5,'2023-12-17','2009-09-07 11:50:21'); + SET @v0 = 2502512601; + SET @v1 = 'default'; + SET @v2 = '2025-06-18'; + SET @v3 = '2023-12-17'; + """ + qt_select_user_var """SELECT WINDOW_FUNNEL(@v0, @v1, col_datetime_undef_signed, col_date_undef_signed != @v2, col_date_undef_signed > @v3) FROM table_50_undef_partitions2_keys3_properties4_distributed_by54 ;""" } \ No newline at end of file From d660c3fc1b10a598ebda87b84621827c03070424 Mon Sep 17 00:00:00 2001 From: lichi Date: Wed, 15 Apr 2026 18:49:46 +0800 Subject: [PATCH 02/10] fix comments --- .../java/org/apache/doris/qe/ConnectContext.java | 3 ++- .../rules/analysis/UserVariableAnalysisTest.java | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java index adcd11612f794e..4f75ec289d075c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java @@ -21,6 +21,7 @@ import org.apache.doris.analysis.DecimalLiteral; import org.apache.doris.analysis.FloatLiteral; import org.apache.doris.analysis.IntLiteral; +import org.apache.doris.analysis.LargeIntLiteral; import org.apache.doris.analysis.LiteralExpr; import org.apache.doris.analysis.NullLiteral; import org.apache.doris.analysis.RedirectStatus; @@ -588,7 +589,7 @@ public void setUserVar(String name, LiteralExpr value) { LiteralExpr literalExpr = userVars.get(varName); if (literalExpr instanceof BoolLiteral) { return Literal.of(((BoolLiteral) literalExpr).getValue()); - } else if (literalExpr instanceof IntLiteral) { + } else if (literalExpr instanceof IntLiteral || literalExpr instanceof LargeIntLiteral) { // the value in the IntLiteral should be int, but now is long in old planner literalExpr // so type coercion to generate right new planner int Literal switch (literalExpr.getType().getPrimitiveType()) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java index 38e9faf7d8c233..d4a9cb3c6a0c76 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java @@ -18,6 +18,7 @@ package org.apache.doris.nereids.rules.analysis; import org.apache.doris.analysis.IntLiteral; +import org.apache.doris.analysis.LargeIntLiteral; import org.apache.doris.nereids.CascadesContext; import org.apache.doris.nereids.analyzer.UnboundVariable; import org.apache.doris.nereids.analyzer.UnboundVariable.VariableType; @@ -30,6 +31,7 @@ import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.types.BigIntType; import org.apache.doris.nereids.types.IntegerType; +import org.apache.doris.nereids.types.LargeIntType; import org.apache.doris.nereids.util.MemoTestUtils; import org.apache.doris.qe.ConnectContext; @@ -64,6 +66,17 @@ public void testUserVarIntegerTypeBig() { Assertions.assertEquals(BigIntType.INSTANCE, l.getDataType()); } + @Test + public void testUserVarIntegerTypeLarge() { + ConnectContext ctx = MemoTestUtils.createConnectContext(); + // set user var @b = Long.MAX_VALUE (bigint) + ctx.setUserVar("c", new LargeIntLiteral(LargeIntLiteral.LARGE_INT_MAX)); + + Literal l = ConnectContext.get().getLiteralForUserVar("c"); + Assertions.assertNotNull(l); + Assertions.assertEquals(LargeIntType.INSTANCE, l.getDataType()); + } + @Test public void testVariableRecordedAndTypeCoercion() { // create context and cascades context (sql cache enabled by default in test utils) From 8a5f599fc7951de39d4b10a95e680e14e30ae8f4 Mon Sep 17 00:00:00 2001 From: lichi Date: Fri, 17 Apr 2026 11:28:53 +0800 Subject: [PATCH 03/10] fix comments --- .../rules/analysis/ExpressionAnalyzer.java | 13 +- .../functions/ExpressionTrait.java | 50 ++++++-- .../functions/ExpressionTraitTest.java | 114 ++++++++++++++++++ 3 files changed, 158 insertions(+), 19 deletions(-) create mode 100644 fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTraitTest.java 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 dd6af316350dcd..e2566c8427f464 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 @@ -219,18 +219,7 @@ public Expression visit(Expression expr, ExpressionRewriteContext context) { * ******************************************************************************************** */ @Override public Expression visitUnboundVariable(UnboundVariable unboundVariable, ExpressionRewriteContext context) { - Variable variable = resolveUnboundVariable(unboundVariable); - // Record used variable for SQL cache when context is available. - // Defensive null-check: some callers may invoke analysis without a full - // ExpressionRewriteContext (e.g. in isolated tests), so guard access. - if (context != null && context.cascadesContext != null) { - Optional sqlCacheContext = context.cascadesContext.getStatementContext() - .getSqlCacheContext(); - if (sqlCacheContext.isPresent()) { - sqlCacheContext.get().addUsedVariable(variable); - } - } - return variable.getRealExpression(); + return resolveUnboundVariable(unboundVariable); } /** resolveUnboundVariable */ diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java index 7ad16a5d77ce99..e2b53b5dbd5890 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java @@ -21,6 +21,7 @@ import org.apache.doris.nereids.exceptions.UnboundException; import org.apache.doris.nereids.trees.TreeNode; import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.Variable; import org.apache.doris.nereids.types.DataType; import com.google.common.collect.ImmutableList; @@ -46,23 +47,58 @@ default void checkLegalityBeforeTypeCoercion() {} @Developing default void checkLegalityAfterRewrite() {} + /** + * getArguments. + */ default List getArguments() { - return children(); + ImmutableList.Builder arguments = ImmutableList.builder(); + for (Expression arg : children()) { + if (arg instanceof Variable && ((Variable) arg).getRealExpression() != null) { + arguments.add(((Variable) arg).getRealExpression()); + } else { + arguments.add(arg); + } + } + return arguments.build(); } + /** + * getArgument. + */ default Expression getArgument(int index) { - return child(index); + Expression arg = child(index); + if (arg instanceof Variable && ((Variable) arg).getRealExpression() != null) { + return ((Variable) arg).getRealExpression(); + } else { + return arg; + } } + /** + * getArgumentsTypes. + */ default List getArgumentsTypes() { - return getArguments() - .stream() - .map(Expression::getDataType) - .collect(ImmutableList.toImmutableList()); + ImmutableList.Builder dataTypes = ImmutableList.builder(); + for (Expression arg : children()) { + if (arg instanceof Variable && ((Variable) arg).getRealExpression() != null) { + dataTypes.add(((Variable) arg).getRealExpression().getDataType()); + } else { + dataTypes.add(arg.getDataType()); + } + } + return dataTypes.build(); } + /** + * getArgumentType. + */ default DataType getArgumentType(int index) { - return child(index).getDataType(); + Expression arg = child(index); + if (arg instanceof Variable && ((Variable) arg).getRealExpression() != null) { + return ((Variable) arg).getRealExpression().getDataType(); + } else { + return arg.getDataType(); + } } default DataType getDataType() throws UnboundException { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTraitTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTraitTest.java new file mode 100644 index 00000000000000..0a9d246dbc5bf1 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTraitTest.java @@ -0,0 +1,114 @@ +// 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.analyzer.UnboundVariable.VariableType; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.Variable; +import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; +import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; +import org.apache.doris.nereids.types.DataType; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +/** + * Tests for ExpressionTrait behaviors when children are `Variable`. + */ +public class ExpressionTraitTest { + + static class DummyFunction extends Expression { + protected DummyFunction(List children) { + super(children); + } + + protected DummyFunction(Expression... children) { + super(children); + } + + @Override + public R accept(ExpressionVisitor visitor, C context) { + return null; + } + + @Override + public Expression withChildren(List children) { + return new DummyFunction(children); + } + + @Override + protected String computeToSql() { + return "dummy"; + } + + @Override + public boolean nullable() { + return false; + } + } + + @Test + public void testVariableWithRealExpression() { + IntegerLiteral lit = new IntegerLiteral(42); + Variable var = new Variable("v", VariableType.USER, lit); + + DummyFunction func = new DummyFunction(var); + + List args = func.getArguments(); + Assertions.assertEquals(1, args.size()); + Assertions.assertEquals(lit, args.get(0)); + + Assertions.assertEquals(lit, func.getArgument(0)); + + List types = func.getArgumentsTypes(); + Assertions.assertEquals(1, types.size()); + Assertions.assertEquals(lit.getDataType(), types.get(0)); + + Assertions.assertEquals(lit.getDataType(), func.getArgumentType(0)); + } + + @Test + public void testVariableWithoutRealExpression() { + IntegerLiteral lit = new IntegerLiteral(100); + // create a Variable but override getRealExpression to simulate a missing real expression + Variable var = new Variable("v", VariableType.USER, lit) { + @Override + public Expression getRealExpression() { + return null; + } + }; + + DummyFunction func = new DummyFunction(var); + + List args = func.getArguments(); + Assertions.assertEquals(1, args.size()); + // when Variable.getRealExpression() returns null, ExpressionTrait should return the Variable itself + Assertions.assertSame(var, args.get(0)); + + Assertions.assertSame(var, func.getArgument(0)); + + List types = func.getArgumentsTypes(); + Assertions.assertEquals(1, types.size()); + // fallback to variable.getDataType() + Assertions.assertEquals(var.getDataType(), types.get(0)); + + Assertions.assertEquals(var.getDataType(), func.getArgumentType(0)); + } +} From 5457c817fabe5a725d89034cb8505100e32ce3ff Mon Sep 17 00:00:00 2001 From: lichi Date: Fri, 17 Apr 2026 12:52:24 +0800 Subject: [PATCH 04/10] fix comments --- .../src/main/java/org/apache/doris/qe/ConnectContext.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java index 4f75ec289d075c..3cf9b29c16b096 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java @@ -597,6 +597,12 @@ public void setUserVar(String name, LiteralExpr value) { return Literal.of(new BigInteger(literalExpr.getStringValue())); case BIGINT: return Literal.of(((IntLiteral) literalExpr).getValue()); + case INT: + return Literal.of((int) ((IntLiteral) literalExpr).getValue()); + case SMALLINT: + return Literal.of((short) ((IntLiteral) literalExpr).getValue()); + case TINYINT: + return Literal.of((byte) ((IntLiteral) literalExpr).getValue()); default: return Literal.of((int) ((IntLiteral) literalExpr).getValue()); } From 1bc04fb9f3f3ee0eb0ad5184f4e2d22f8f494e03 Mon Sep 17 00:00:00 2001 From: lichi Date: Fri, 17 Apr 2026 14:54:16 +0800 Subject: [PATCH 05/10] fix comment --- .../analysis/UserVariableAnalysisTest.java | 85 ++++--------------- 1 file changed, 16 insertions(+), 69 deletions(-) diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java index d4a9cb3c6a0c76..e7ea8b491eafa8 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/UserVariableAnalysisTest.java @@ -19,91 +19,38 @@ import org.apache.doris.analysis.IntLiteral; import org.apache.doris.analysis.LargeIntLiteral; -import org.apache.doris.nereids.CascadesContext; -import org.apache.doris.nereids.analyzer.UnboundVariable; -import org.apache.doris.nereids.analyzer.UnboundVariable.VariableType; -import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext; -import org.apache.doris.nereids.trees.expressions.ComparisonPredicate; -import org.apache.doris.nereids.trees.expressions.EqualTo; -import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.expressions.Variable; -import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral; -import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.types.BigIntType; import org.apache.doris.nereids.types.IntegerType; import org.apache.doris.nereids.types.LargeIntType; +import org.apache.doris.nereids.types.SmallIntType; +import org.apache.doris.nereids.types.TinyIntType; import org.apache.doris.nereids.util.MemoTestUtils; import org.apache.doris.qe.ConnectContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.util.List; - /** Tests for user variable handling in expression analysis. */ public class UserVariableAnalysisTest { @Test - public void testUserVarIntegerTypeSmall() { - ConnectContext ctx = MemoTestUtils.createConnectContext(); - // set user var @a = 1 (small int) - ctx.setUserVar("a", new IntLiteral(1L)); - - // get nereids literal via ConnectContext helper - Literal l = ConnectContext.get().getLiteralForUserVar("a"); - Assertions.assertNotNull(l); - Assertions.assertEquals(IntegerType.INSTANCE, l.getDataType()); - } - - @Test - public void testUserVarIntegerTypeBig() { + public void testUserVarIntegerType() { ConnectContext ctx = MemoTestUtils.createConnectContext(); + // set user var @a = TINY_INT_MAX (tiny int) + ctx.setUserVar("a", new IntLiteral(Byte.MAX_VALUE)); + // set user var @a = SMALL_INT_MAX (small int) + ctx.setUserVar("b", new IntLiteral(Short.MAX_VALUE)); // set user var @b = Long.MAX_VALUE (bigint) - ctx.setUserVar("b", new IntLiteral(Long.MAX_VALUE)); - - Literal l = ConnectContext.get().getLiteralForUserVar("b"); - Assertions.assertNotNull(l); - Assertions.assertEquals(BigIntType.INSTANCE, l.getDataType()); - } - - @Test - public void testUserVarIntegerTypeLarge() { - ConnectContext ctx = MemoTestUtils.createConnectContext(); + ctx.setUserVar("c", new IntLiteral(Integer.MAX_VALUE)); // set user var @b = Long.MAX_VALUE (bigint) - ctx.setUserVar("c", new LargeIntLiteral(LargeIntLiteral.LARGE_INT_MAX)); - - Literal l = ConnectContext.get().getLiteralForUserVar("c"); - Assertions.assertNotNull(l); - Assertions.assertEquals(LargeIntType.INSTANCE, l.getDataType()); - } - - @Test - public void testVariableRecordedAndTypeCoercion() { - // create context and cascades context (sql cache enabled by default in test utils) - ConnectContext ctx = MemoTestUtils.createConnectContext(); - ctx.setUserVar("uvar", new IntLiteral(42L)); - - // create a comparison using an unbound user variable: @uvar = 1 - UnboundVariable unboundVar = new UnboundVariable( - "uvar", VariableType.USER); - IntegerLiteral right = new IntegerLiteral(1); - ComparisonPredicate cmp = new EqualTo(unboundVar, right); - - // prepare analyzer with cascades context so sql cache context exists - CascadesContext cascadesContext = MemoTestUtils.createCascadesContext(ctx, "select 1"); - ExpressionAnalyzer analyzer = new ExpressionAnalyzer(null, - new org.apache.doris.nereids.analyzer.Scope(java.util.Collections.emptyList()), - cascadesContext, false, false); - ExpressionRewriteContext rewriteContext = new ExpressionRewriteContext(cascadesContext); - - // analyze should not throw and should handle type coercion - Expression analyzed = analyzer.analyze(cmp, rewriteContext); - Assertions.assertNotNull(analyzed); + ctx.setUserVar("d", new IntLiteral(Long.MAX_VALUE)); + // set user var @b = Long.MAX_VALUE (bigint) + ctx.setUserVar("e", new LargeIntLiteral(LargeIntLiteral.LARGE_INT_MAX)); - // sql cache context should have recorded the used variable - List used = cascadesContext.getStatementContext().getSqlCacheContext().get().getUsedVariables(); - Assertions.assertFalse(used.isEmpty()); - boolean found = used.stream().anyMatch(v -> "uvar".equals(v.getName())); - Assertions.assertTrue(found, "user variable should be recorded in sql cache context"); + Assertions.assertEquals(TinyIntType.INSTANCE, ConnectContext.get().getLiteralForUserVar("a").getDataType()); + Assertions.assertEquals(SmallIntType.INSTANCE, ConnectContext.get().getLiteralForUserVar("b").getDataType()); + Assertions.assertEquals(IntegerType.INSTANCE, ConnectContext.get().getLiteralForUserVar("c").getDataType()); + Assertions.assertEquals(BigIntType.INSTANCE, ConnectContext.get().getLiteralForUserVar("d").getDataType()); + Assertions.assertEquals(LargeIntType.INSTANCE, ConnectContext.get().getLiteralForUserVar("e").getDataType()); } } From b29786f057f164c7605121a30cade0fb67ad865d Mon Sep 17 00:00:00 2001 From: lichi Date: Tue, 21 Apr 2026 11:58:33 +0800 Subject: [PATCH 06/10] fix comments --- .../nereids/trees/expressions/Default.java | 2 +- .../trees/expressions/InPredicate.java | 19 ++++++++++--------- .../doris/nereids/trees/expressions/Like.java | 5 +++-- .../expressions/functions/agg/BoolAnd.java | 2 +- .../expressions/functions/agg/BoolOr.java | 2 +- .../expressions/functions/agg/BoolXor.java | 2 +- .../expressions/functions/agg/Histogram.java | 2 +- .../functions/agg/LinearHistogram.java | 2 +- .../expressions/functions/agg/Median.java | 2 +- .../functions/agg/MultiDistinctSum.java | 2 +- .../functions/agg/MultiDistinctSum0.java | 2 +- .../functions/agg/PercentileReservoir.java | 7 ++++--- .../trees/expressions/functions/agg/Sum0.java | 2 +- .../trees/expressions/functions/ai/Embed.java | 2 +- .../functions/generator/ExplodeMap.java | 5 +++-- .../functions/generator/ExplodeMapOuter.java | 5 +++-- .../expressions/functions/scalar/Array.java | 2 +- .../functions/scalar/ArrayApply.java | 7 ++++--- .../functions/scalar/ArrayAvg.java | 7 ++++--- .../functions/scalar/ArrayCompact.java | 2 +- .../functions/scalar/ArrayCumSum.java | 7 ++++--- .../functions/scalar/ArrayDifference.java | 7 ++++--- .../functions/scalar/ArrayDistinct.java | 2 +- .../functions/scalar/ArrayEnumerateUniq.java | 2 +- .../functions/scalar/ArrayIntersect.java | 2 +- .../functions/scalar/ArrayMax.java | 2 +- .../functions/scalar/ArrayMin.java | 2 +- .../functions/scalar/ArrayPosition.java | 2 +- .../functions/scalar/ArrayProduct.java | 2 +- .../functions/scalar/ArrayRemove.java | 2 +- .../functions/scalar/ArrayReverseSort.java | 4 ++-- .../functions/scalar/ArraySort.java | 4 ++-- .../functions/scalar/ArraySortBy.java | 4 ++-- .../functions/scalar/ArraySum.java | 2 +- .../functions/scalar/ArrayUnion.java | 2 +- .../functions/scalar/ArraysOverlap.java | 2 +- .../expressions/functions/scalar/Char.java | 9 +++++---- .../functions/scalar/CountEqual.java | 2 +- .../expressions/functions/scalar/Random.java | 2 +- .../functions/scalar/RegexpReplace.java | 7 ++++--- .../functions/scalar/RegexpReplaceOne.java | 7 ++++--- .../functions/scalar/SplitByRegexp.java | 8 +++++--- .../functions/scalar/StructElement.java | 8 +++++--- .../functions/scalar/Tokenize.java | 6 +++--- .../expressions/functions/scalar/Uniform.java | 4 ++-- .../functions/scalar/WidthBucket.java | 2 +- 46 files changed, 100 insertions(+), 84 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Default.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Default.java index 918bf6710df45e..97a05d258c4faf 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Default.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Default.java @@ -58,7 +58,7 @@ public DataType getDataType() { @Override public void checkLegalityBeforeTypeCoercion() { - Expression arg = child(); + Expression arg = getArgument(0); if (!(arg instanceof SlotReference)) { throw new AnalysisException("DEFAULT requires a column reference"); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InPredicate.java index df972af18325a7..533f1aa45942eb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InPredicate.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/InPredicate.java @@ -135,29 +135,30 @@ public boolean nullable() throws UnboundException { @Override public void checkLegalityBeforeTypeCoercion() { - if (children().get(0).getDataType().isStructType()) { + List arguments = getArguments(); + if (arguments.get(0).getDataType().isStructType()) { // we should check in value list is all struct type - for (int i = 1; i < children().size(); i++) { - if (!children().get(i).getDataType().isStructType() && !children().get(i).getDataType().isNullType()) { + for (int i = 1; i < arguments.size(); i++) { + if (!arguments.get(i).getDataType().isStructType() && !arguments.get(i).getDataType().isNullType()) { throw new AnalysisException("in predicate struct should compare with struct type list, but got : " - + children().get(i).getDataType().toSql()); + + arguments.get(i).getDataType().toSql()); } } return; } - if (children().get(0).getDataType().isArrayType()) { + if (arguments.get(0).getDataType().isArrayType()) { // we should check in value list is all list type - for (int i = 1; i < children().size(); i++) { - if (!children().get(i).getDataType().isArrayType() && !children().get(i).getDataType().isNullType()) { + for (int i = 1; i < arguments.size(); i++) { + if (!arguments.get(i).getDataType().isArrayType() && !arguments.get(i).getDataType().isNullType()) { throw new AnalysisException("in predicate list should compare with struct type list, but got : " - + children().get(i).getDataType().toSql()); + + arguments.get(i).getDataType().toSql()); } } return; } - children().forEach(c -> { + arguments.forEach(c -> { if (c.getDataType().isObjectType()) { throw new AnalysisException("in predicate could not contains object type: " + this.toSql()); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Like.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Like.java index 10f25fb0ebca98..f979d719f85c86 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Like.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Like.java @@ -96,8 +96,9 @@ public Expression withInferred(boolean inferred) { @Override public void checkLegalityBeforeTypeCoercion() { if (arity() == 3) { - if (child(2) instanceof StringLikeLiteral) { - String escapeChar = ((StringLikeLiteral) child(2)).getStringValue(); + Expression escapeArgument = getArgument(2); + if (escapeArgument instanceof StringLikeLiteral) { + String escapeChar = ((StringLikeLiteral) escapeArgument).getStringValue(); if (escapeChar.getBytes().length != 1) { throw new AnalysisException( "like escape character must be a single ascii character: " + escapeChar); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolAnd.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolAnd.java index b686d16520371a..02f8dbec79a475 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolAnd.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolAnd.java @@ -67,7 +67,7 @@ public BoolAnd withDistinctAndChildren(boolean distinct, List childr @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child().getDataType(); + DataType argType = getArgument(0).getDataType(); if (!(argType.isBooleanType() || argType.isNumericType())) { throw new AnalysisException("bool_and requires a boolean or numeric argument"); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolOr.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolOr.java index 3e4f393b226a92..3eefb907d68e4c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolOr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolOr.java @@ -63,7 +63,7 @@ public BoolOr withDistinctAndChildren(boolean distinct, List childre @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child().getDataType(); + DataType argType = getArgument(0).getDataType(); if (!(argType.isBooleanType() || argType.isNumericType())) { throw new AnalysisException("bool_or requires a boolean or numeric argument"); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolXor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolXor.java index 4bdee9c22cbb56..6e84899ec9163c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolXor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/BoolXor.java @@ -63,7 +63,7 @@ public BoolXor withDistinctAndChildren(boolean distinct, List childr @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child().getDataType(); + DataType argType = getArgument(0).getDataType(); if (!(argType.isBooleanType() || argType.isNumericType())) { throw new AnalysisException("bool_xor requires a boolean or numeric argument"); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Histogram.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Histogram.java index 748cf69608bd28..26e029592433f3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Histogram.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Histogram.java @@ -82,7 +82,7 @@ private Histogram(AggregateFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - if (!(child(0).getDataType() instanceof PrimitiveType)) { + if (!(getArgument(0).getDataType() instanceof PrimitiveType)) { SearchSignature.throwCanNotFoundFunctionException(this.getName(), getArguments()); } if (arity() == 2 && !getArgument(1).isConstant()) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/LinearHistogram.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/LinearHistogram.java index e23620e38e720d..f5e2893e4a3279 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/LinearHistogram.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/LinearHistogram.java @@ -67,7 +67,7 @@ private LinearHistogram(AggregateFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - if (!(child(0).getDataType() instanceof PrimitiveType)) { + if (!(getArgument(0).getDataType() instanceof PrimitiveType)) { SearchSignature.throwCanNotFoundFunctionException(this.getName(), getArguments()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Median.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Median.java index 4604c94982f815..f91af0352deef4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Median.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Median.java @@ -79,7 +79,7 @@ private Median(NullableAggregateFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child().getDataType(); + DataType argType = getArgument(0).getDataType(); if (((!argType.isNumericType() && !argType.isNullType()) || argType.isOnlyMetricType())) { throw new AnalysisException("median requires a numeric parameter: " + toSql()); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/MultiDistinctSum.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/MultiDistinctSum.java index baf609cbb2810a..0e34ea50ba5f15 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/MultiDistinctSum.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/MultiDistinctSum.java @@ -53,7 +53,7 @@ private MultiDistinctSum(NullableAggregateFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child().getDataType(); + DataType argType = getArgument(0).getDataType(); if ((!argType.isNumericType() && !argType.isBooleanType() && !argType.isNullType()) || argType.isOnlyMetricType()) { throw new AnalysisException("sum requires a numeric or boolean parameter: " + this.toSql()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/MultiDistinctSum0.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/MultiDistinctSum0.java index fb0244e3d26d9d..f47adeac7d30c0 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/MultiDistinctSum0.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/MultiDistinctSum0.java @@ -56,7 +56,7 @@ public MultiDistinctSum0(AggregateFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child().getDataType(); + DataType argType = getArgument(0).getDataType(); if ((!argType.isNumericType() && !argType.isBooleanType() && !argType.isNullType()) || argType.isOnlyMetricType()) { throw new AnalysisException("sum0 requires a numeric or boolean parameter: " + this.toSql()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/PercentileReservoir.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/PercentileReservoir.java index 35826e93f3d3c4..7c6f7bb6652700 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/PercentileReservoir.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/PercentileReservoir.java @@ -67,12 +67,13 @@ private PercentileReservoir(NullableAggregateFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - if (!getArgument(1).isConstant()) { + Expression levelArgument = getArgument(1); + if (!levelArgument.isConstant()) { throw new AnalysisException( "percentile_reservoir requires second parameter must be a constant : " + this.toSql()); } - if (child(1) instanceof Literal) { - double value = ((Literal) child(1)).getDouble(); + if (levelArgument instanceof Literal) { + double value = ((Literal) levelArgument).getDouble(); if (value < 0 || value > 1) { throw new AnalysisException( "percentile_reservoir level must be in [0, 1], but got " + value + ": " + this.toSql()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Sum0.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Sum0.java index f9a47fcae4d60b..c2514426226bdd 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Sum0.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/agg/Sum0.java @@ -102,7 +102,7 @@ public MultiDistinctSum0 convertToMultiDistinct() { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child().getDataType(); + DataType argType = getArgument(0).getDataType(); if (!argType.isNumericType() && !argType.isBooleanType() && !argType.isNullType() && !argType.isStringLikeType()) { throw new AnalysisException("sum0 requires a numeric, boolean or string parameter: " + this.toSql()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ai/Embed.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ai/Embed.java index e3e8725856ff5d..0082609b7fdb0d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ai/Embed.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ai/Embed.java @@ -100,7 +100,7 @@ public void checkLegalityBeforeTypeCoercion() { return; } if (arity() == 2) { - String aiResourceName = requireStringLiteral(child(0), "resource name", + String aiResourceName = requireStringLiteral(getArgument(0), "resource name", "AI Function must accept literal for the resource name."); validateAIResource(aiResourceName); return; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/ExplodeMap.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/ExplodeMap.java index 68d9559e75ad92..674ba58274bd30 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/ExplodeMap.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/ExplodeMap.java @@ -63,9 +63,10 @@ public ExplodeMap withChildren(List children) { @Override public void checkLegalityBeforeTypeCoercion() { - if (!(child().getDataType() instanceof MapType)) { + Expression argument = getArgument(0); + if (!(argument.getDataType() instanceof MapType)) { throw new AnalysisException("only support map type for explode_map function but got " - + child().getDataType()); + + argument.getDataType()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/ExplodeMapOuter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/ExplodeMapOuter.java index e2564d080dd114..05512876da44a8 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/ExplodeMapOuter.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/generator/ExplodeMapOuter.java @@ -61,9 +61,10 @@ public ExplodeMapOuter withChildren(List children) { @Override public void checkLegalityBeforeTypeCoercion() { - if (!(child().getDataType() instanceof MapType)) { + Expression argument = getArgument(0); + if (!(argument.getDataType() instanceof MapType)) { throw new AnalysisException("only support map type for explode_map function but got " - + child().getDataType()); + + argument.getDataType()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java index 11257ef87defab..8da0b6f3b45778 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Array.java @@ -69,7 +69,7 @@ public void checkLegalityBeforeTypeCoercion() { if (children.isEmpty()) { return; } - DataType firstChildType = children.get(0).getDataType(); + DataType firstChildType = getArgument(0).getDataType(); if (firstChildType.isJsonType() || firstChildType.isVariantType()) { throw new AnalysisException("array does not support jsonb/variant type"); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayApply.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayApply.java index 8a052911ed6184..3f7ebf12332c5b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayApply.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayApply.java @@ -87,11 +87,12 @@ private void checkArguments(Expression arg0, Expression arg1, Expression arg2) { @Override public void checkLegalityBeforeTypeCoercion() { - if (!child(0).getDataType().isArrayType()) { + Expression argument = getArgument(0); + if (!argument.getDataType().isArrayType()) { throw new AnalysisException("array_apply does not support type " - + child(0).getDataType() + ", expression is " + toSql()); + + argument.getDataType() + ", expression is " + toSql()); } - DataType argType = ((ArrayType) child(0).getDataType()).getItemType(); + DataType argType = ((ArrayType) argument.getDataType()).getItemType(); if (!(argType.isIntegralType() || argType.isFloatLikeType() || argType.isDecimalLikeType() || argType.isDateLikeType() || argType.isBooleanType())) { throw new AnalysisException("array_apply does not support type " + argType + ", expression is " + toSql()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayAvg.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayAvg.java index 04d1568c043faa..d6c09b901fb6b0 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayAvg.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayAvg.java @@ -79,10 +79,11 @@ private ArrayAvg(ScalarFunctionParams functionParams) { */ @Override public void checkLegalityBeforeTypeCoercion() { - if (child(0).getDataType().isArrayType() - && !((ArrayType) child(0).getDataType()).getItemType().canBeCalculatedInArray()) { + Expression argument = getArgument(0); + if (argument.getDataType().isArrayType() + && !((ArrayType) argument.getDataType()).getItemType().canBeCalculatedInArray()) { throw new AnalysisException("array_avg does not support type " - + child(0).getDataType().toString() + ", expression is " + toSql()); + + argument.getDataType().toString() + ", expression is " + toSql()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayCompact.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayCompact.java index fa22977f71753b..2b8e0678073012 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayCompact.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayCompact.java @@ -61,7 +61,7 @@ private ArrayCompact(ScalarFunctionParams functionParams) { */ @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = ((ArrayType) child(0).getDataType()).getItemType(); + DataType argType = ((ArrayType) getArgument(0).getDataType()).getItemType(); if (argType.isMapType() || argType.isStructType()) { throw new AnalysisException("array_compact does not support type " + argType.toString() + ", expression is " + toSql()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayCumSum.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayCumSum.java index 83fcabda5e39b7..00a5046719b369 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayCumSum.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayCumSum.java @@ -80,10 +80,11 @@ private ArrayCumSum(ScalarFunctionParams functionParams) { */ @Override public void checkLegalityBeforeTypeCoercion() { - if (child(0).getDataType().isArrayType() - && !((ArrayType) child(0).getDataType()).getItemType().canBeCalculatedInArray()) { + Expression argument = getArgument(0); + if (argument.getDataType().isArrayType() + && !((ArrayType) argument.getDataType()).getItemType().canBeCalculatedInArray()) { throw new AnalysisException("array_cum_sum does not support type " - + child(0).getDataType().toString() + + argument.getDataType().toString() + ", expression is " + toSql()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayDifference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayDifference.java index 82103fa57487f3..9bd49e61d90e14 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayDifference.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayDifference.java @@ -76,10 +76,11 @@ private ArrayDifference(ScalarFunctionParams functionParams) { */ @Override public void checkLegalityBeforeTypeCoercion() { - if (child(0).getDataType().isArrayType() - && !((ArrayType) child(0).getDataType()).getItemType().canBeCalculatedInArray()) { + Expression argument = getArgument(0); + if (argument.getDataType().isArrayType() + && !((ArrayType) argument.getDataType()).getItemType().canBeCalculatedInArray()) { throw new AnalysisException("array_difference does not support type " - + child(0).getDataType().toString() + ", expression is " + toSql()); + + argument.getDataType().toString() + ", expression is " + toSql()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayDistinct.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayDistinct.java index d9297dcff6ad96..9d957b137a229d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayDistinct.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayDistinct.java @@ -61,7 +61,7 @@ private ArrayDistinct(ScalarFunctionParams functionParams) { */ @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child(0).getDataType(); + DataType argType = getArgument(0).getDataType(); if (argType.isArrayType()) { DataType itemType = ((ArrayType) argType).getItemType(); if (itemType.isMapType() || itemType.isStructType()) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayEnumerateUniq.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayEnumerateUniq.java index 2c327ae6198b01..beac5aaf0ab474 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayEnumerateUniq.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayEnumerateUniq.java @@ -65,7 +65,7 @@ private ArrayEnumerateUniq(ScalarFunctionParams functionParams) { */ @Override public void checkLegalityBeforeTypeCoercion() { - for (Expression arg : children()) { + for (Expression arg : getArguments()) { DataType argType = arg.getDataType(); if (argType.isArrayType()) { DataType itemType = ((ArrayType) argType).getItemType(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayIntersect.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayIntersect.java index 6f8eda85b797f1..c48b54305ed9a4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayIntersect.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayIntersect.java @@ -63,7 +63,7 @@ private ArrayIntersect(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { DataType itemType = NullType.INSTANCE; - for (Expression child : children()) { + for (Expression child : getArguments()) { DataType argType = child.getDataType(); // nullsafe if (argType == NullType.INSTANCE) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMax.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMax.java index 9dbae0cd847333..d471ad30a5e361 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMax.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMax.java @@ -59,7 +59,7 @@ private ArrayMax(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child().getDataType(); + DataType argType = getArgument(0).getDataType(); if (argType.isArrayType() && ((ArrayType) argType).getItemType().isComplexType()) { throw new AnalysisException("array_max does not support complex types: " + toSql()); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMin.java index 21de64ec97db83..5913fa317721b0 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMin.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayMin.java @@ -59,7 +59,7 @@ private ArrayMin(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child().getDataType(); + DataType argType = getArgument(0).getDataType(); if (argType.isArrayType() && ((ArrayType) argType).getItemType().isComplexType()) { throw new AnalysisException("array_min does not support complex types: " + toSql()); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayPosition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayPosition.java index 07387d1867c0d1..490b428b062238 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayPosition.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayPosition.java @@ -77,7 +77,7 @@ public ArrayPosition withChildren(List children) { */ @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child(0).getDataType(); + DataType argType = getArgument(0).getDataType(); if (argType.isArrayType() && ((ArrayType) argType).getItemType().isComplexType()) { throw new AnalysisException("array_position does not support complex types: " + toSql()); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayProduct.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayProduct.java index 842f200e08ab1b..fcd9b3852150bf 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayProduct.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayProduct.java @@ -69,7 +69,7 @@ private ArrayProduct(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child().getDataType(); + DataType argType = getArgument(0).getDataType(); if (argType instanceof ArrayType && ((ArrayType) argType).getItemType().isComplexType()) { throw new AnalysisException(toSql() + " does not support type: " + ((ArrayType) argType).getItemType().toString()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayRemove.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayRemove.java index 33b3a517edcd49..2610f84c8f7e9b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayRemove.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayRemove.java @@ -72,7 +72,7 @@ public ArrayRemove withChildren(List children) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child(0).getDataType(); + DataType argType = getArgument(0).getDataType(); if (argType.isArrayType() && (((ArrayType) argType).getItemType().isComplexType() || ((ArrayType) argType).getItemType().isVariantType() || ((ArrayType) argType).getItemType().isJsonType())) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayReverseSort.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayReverseSort.java index 9a3f28499d5ea9..4def17eda8f188 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayReverseSort.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayReverseSort.java @@ -57,8 +57,8 @@ private ArrayReverseSort(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - if (children.get(0).getDataType() instanceof ArrayType) { - DataType argType = child(0).getDataType(); + DataType argType = getArgument(0).getDataType(); + if (argType instanceof ArrayType) { // Find the innermost element type for nested arrays DataType itemType = ((ArrayType) argType).getItemType(); while (itemType.isArrayType()) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySort.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySort.java index 2e8b738237ab04..983d30381524e6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySort.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySort.java @@ -60,8 +60,8 @@ private ArraySort(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - if (children.get(0).getDataType() instanceof ArrayType) { - DataType argType = child(0).getDataType(); + DataType argType = getArgument(0).getDataType(); + if (argType instanceof ArrayType) { // Find the innermost element type for nested arrays DataType itemType = ((ArrayType) argType).getItemType(); while (itemType.isArrayType()) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySortBy.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySortBy.java index 43e1c25aa9799d..1d0fe2b15f3f59 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySortBy.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySortBy.java @@ -68,8 +68,8 @@ public ArraySortBy withChildren(List children) { @Override public void checkLegalityBeforeTypeCoercion() { - if (children.get(0).getDataType() instanceof ArrayType) { - DataType argType = child(0).getDataType(); + DataType argType = getArgument(0).getDataType(); + if (argType instanceof ArrayType) { // Find the innermost element type for nested arrays DataType itemType = ((ArrayType) argType).getItemType(); while (itemType.isArrayType()) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySum.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySum.java index 26ef6af72dad34..38766e320b9701 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySum.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraySum.java @@ -73,7 +73,7 @@ private ArraySum(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child(0).getDataType(); + DataType argType = getArgument(0).getDataType(); if (argType.isArrayType() && !((((ArrayType) argType).getItemType().isNumericType()) || ((ArrayType) argType).getItemType().isNullType())) { throw new AnalysisException("array_sum does not support types: " + argType.toSql()); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayUnion.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayUnion.java index bc9b6ef1da105d..fb4fc04e2cdeba 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayUnion.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArrayUnion.java @@ -68,7 +68,7 @@ public ArrayUnion withChildren(List children) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child(0).getDataType(); + DataType argType = getArgument(0).getDataType(); if (argType.isArrayType() && (((ArrayType) argType).getItemType().isComplexType() || ((ArrayType) argType).getItemType().isVariantType() || ((ArrayType) argType).getItemType().isJsonType())) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraysOverlap.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraysOverlap.java index c5c2e1e4ba0c69..2f9402f5444378 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraysOverlap.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/ArraysOverlap.java @@ -68,7 +68,7 @@ public ArraysOverlap withChildren(List children) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child(0).getDataType(); + DataType argType = getArgument(0).getDataType(); if (argType.isArrayType() && (((ArrayType) argType).getItemType().isComplexType() || ((ArrayType) argType).getItemType().isVariantType() || ((ArrayType) argType).getItemType().isJsonType())) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Char.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Char.java index 2171f279b22821..3a8b4fe82a11da 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Char.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Char.java @@ -54,13 +54,14 @@ private Char(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - if (!(child(0) instanceof StringLikeLiteral)) { - throw new AnalysisException("char charset name must be a constant: " + child(0).toSql()); + Expression charsetArgument = getArgument(0); + if (!(charsetArgument instanceof StringLikeLiteral)) { + throw new AnalysisException("char charset name must be a constant: " + charsetArgument.toSql()); } - StringLikeLiteral stringLiteral = (StringLikeLiteral) child(0); + StringLikeLiteral stringLiteral = (StringLikeLiteral) charsetArgument; if (!"utf8".equalsIgnoreCase(stringLiteral.getStringValue())) { throw new AnalysisException( - "char function currently only support charset name 'utf8': " + child(0).toSql()); + "char function currently only support charset name 'utf8': " + charsetArgument.toSql()); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CountEqual.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CountEqual.java index 51610f0a5738b2..20c6a9c208a596 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CountEqual.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/CountEqual.java @@ -64,7 +64,7 @@ private CountEqual(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - DataType argType = child(0).getDataType(); + DataType argType = getArgument(0).getDataType(); if (argType.isArrayType() && (((ArrayType) argType).getItemType().isComplexType() || ((ArrayType) argType).getItemType().isVariantType() || ((ArrayType) argType).getItemType().isJsonType())) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java index 625a06ce52b39f..f07f097d23cc16 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java @@ -97,7 +97,7 @@ private Random(UniqueFunctionParams functionParams) { public void checkLegalityBeforeTypeCoercion() { // align with original planner behavior, refer to: // org/apache/doris/analysis/Expr.getBuiltinFunction() - for (Expression child : children()) { + for (Expression child : getArguments()) { if (!child.isLiteral()) { throw new AnalysisException("The param of rand function must be literal "); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RegexpReplace.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RegexpReplace.java index 4e7f1299d710d3..0bf01b44c2e816 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RegexpReplace.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RegexpReplace.java @@ -84,11 +84,12 @@ public RegexpReplace withChildren(List children) { @Override public void checkLegalityBeforeTypeCoercion() { - if (children().size() == 3) { + List arguments = getArguments(); + if (arguments.size() == 3) { return; } - if (children().size() == 4) { - Expression value = child(3); + if (arguments.size() == 4) { + Expression value = getArgument(3); DataType type = value.getDataType(); if (!type.isStringLikeType()) { throw new AnalysisException( diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RegexpReplaceOne.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RegexpReplaceOne.java index a86d17e6123d09..a3a0b8045a35f4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RegexpReplaceOne.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/RegexpReplaceOne.java @@ -84,11 +84,12 @@ public RegexpReplaceOne withChildren(List children) { @Override public void checkLegalityBeforeTypeCoercion() { - if (children().size() == 3) { + List arguments = getArguments(); + if (arguments.size() == 3) { return; } - if (children().size() == 4) { - Expression value = child(3); + if (arguments.size() == 4) { + Expression value = getArgument(3); DataType type = value.getDataType(); if (!type.isStringLikeType()) { throw new AnalysisException( diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SplitByRegexp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SplitByRegexp.java index f7afb7d262f296..c689ca644c41fa 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SplitByRegexp.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/SplitByRegexp.java @@ -77,9 +77,11 @@ public SplitByRegexp withChildren(List children) { @Override public void checkLegalityBeforeTypeCoercion() { - if (children().size() == 3) { - if (!child(2).isConstant() || !(child(2) instanceof IntegerLikeLiteral) - || (((IntegerLikeLiteral) child(2)).getIntValue() < 0)) { + List arguments = getArguments(); + if (arguments.size() == 3) { + Expression thirdArgument = getArgument(2); + if (!thirdArgument.isConstant() || !(thirdArgument instanceof IntegerLikeLiteral) + || (((IntegerLikeLiteral) thirdArgument).getIntValue() < 0)) { throw new AnalysisException("the third parameter of " + getName() + " function must be a positive constant: " + toSql()); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java index ed29818b688ddb..e350b66dbc3b9a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java @@ -58,11 +58,13 @@ private StructElement(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - if (!(child(0).getDataType() instanceof StructType - || child(0).getDataType() instanceof NullType)) { + Expression structArgument = getArgument(0); + Expression fieldArgument = getArgument(1); + if (!(structArgument.getDataType() instanceof StructType + || structArgument.getDataType() instanceof NullType)) { SearchSignature.throwCanNotFoundFunctionException(this.getName(), this.getArguments()); } - if (!(child(1) instanceof StringLikeLiteral || child(1) instanceof IntegerLikeLiteral)) { + if (!(fieldArgument instanceof StringLikeLiteral || fieldArgument instanceof IntegerLikeLiteral)) { throw new AnalysisException("struct_element only allows" + " constant int or string second parameter: " + this.toSql()); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Tokenize.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Tokenize.java index 19c8cc2453d86e..634e8606710a9a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Tokenize.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Tokenize.java @@ -59,7 +59,7 @@ private Tokenize(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - Expression rightChild = child(1); + Expression rightChild = getArgument(1); // tokenize(k7, null) could return NULL if (rightChild instanceof NullLiteral) { return; @@ -67,12 +67,12 @@ public void checkLegalityBeforeTypeCoercion() { if (!(rightChild instanceof StringLikeLiteral)) { throw new AnalysisException("tokenize second argument must be string literal"); } - String properties = ((StringLikeLiteral) child(1)).value; + String properties = ((StringLikeLiteral) rightChild).value; if (properties == null || properties.isEmpty()) { return; } try { - new NereidsParser().parseProperties(((StringLikeLiteral) child(1)).value); + new NereidsParser().parseProperties(properties); } catch (Throwable e) { throw new AnalysisException("tokenize second argument must be properties format"); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Uniform.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Uniform.java index fbae666c55fc0b..ec18424bab8d65 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Uniform.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Uniform.java @@ -60,10 +60,10 @@ private Uniform(ScalarFunctionParams functionParams) { @Override public void checkLegalityBeforeTypeCoercion() { - if (!child(0).isLiteral()) { + if (!getArgument(0).isLiteral()) { throw new AnalysisException("The first parameter (min) of uniform function must be literal"); } - if (!child(1).isLiteral()) { + if (!getArgument(1).isLiteral()) { throw new AnalysisException("The second parameter (max) of uniform function must be literal"); } // if do folding on BE, will before checkLegalityAfterRewrite, so we need it here too. diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WidthBucket.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WidthBucket.java index 090a850a8b8fb0..f789955b72a744 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WidthBucket.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/WidthBucket.java @@ -77,7 +77,7 @@ public WidthBucket withChildren(List children) { @Override public void checkLegalityBeforeTypeCoercion() { - if (!child(3).isLiteral()) { + if (!getArgument(3).isLiteral()) { throw new AnalysisException("The fourth argument of WidthBucket must be a constant."); } } From 5ebfe86bea702f1193a89ee1c94dd86cf8474d39 Mon Sep 17 00:00:00 2001 From: lichi Date: Tue, 21 Apr 2026 14:39:09 +0800 Subject: [PATCH 07/10] fix comments --- .../trees/expressions/functions/scalar/Random.java | 4 ---- .../expressions/functions/scalar/StructElement.java | 12 ++++++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java index f07f097d23cc16..d35b1dd9ef8f94 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/Random.java @@ -23,12 +23,10 @@ import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.expressions.StatementScopeIdGenerator; import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; -import org.apache.doris.nereids.trees.expressions.literal.Literal; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.BigIntType; import org.apache.doris.nereids.types.DoubleType; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import java.util.List; @@ -76,8 +74,6 @@ public Random(ExprId uniqueId, boolean ignoreUniqueId) { public Random(ExprId uniqueId, boolean ignoreUniqueId, Expression arg) { super("random", uniqueId, ignoreUniqueId, arg); - // align with original planner behavior, refer to: org/apache/doris/analysis/Expr.getBuiltinFunction() - Preconditions.checkState(arg instanceof Literal, "The param of rand function must be literal"); } public Random(ExprId uniqueId, boolean ignoreUniqueId, Expression lchild, Expression rchild) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java index e350b66dbc3b9a..0b4b9a8b1bf0c3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java @@ -86,22 +86,22 @@ public R accept(ExpressionVisitor visitor, C context) { @Override public List getSignatures() { - if (child(0).getDataType() instanceof NullType) { + if (getArgument(0).getDataType() instanceof NullType) { return ImmutableList.of( FunctionSignature.ret(NullType.INSTANCE).args(NullType.INSTANCE, child(1).getDataType()) ); } - StructType structArgType = (StructType) child(0).getDataType(); + StructType structArgType = (StructType) getArgument(0).getDataType(); DataType retType; - if (child(1) instanceof IntegerLikeLiteral) { - int offset = ((IntegerLikeLiteral) child(1)).getIntValue(); + if (getArgument(1) instanceof IntegerLikeLiteral) { + int offset = ((IntegerLikeLiteral) getArgument(1)).getIntValue(); if (offset <= 0 || offset > structArgType.getFields().size()) { throw new AnalysisException("the specified field index out of bound: " + this.toSql()); } else { retType = structArgType.getFields().get(offset - 1).getDataType(); } - } else if (child(1) instanceof StringLikeLiteral) { - String name = ((StringLikeLiteral) child(1)).getStringValue(); + } else if (getArgument(1) instanceof StringLikeLiteral) { + String name = ((StringLikeLiteral) getArgument(1)).getStringValue(); StructField field = structArgType.getField(name); if (field == null) { throw new AnalysisException("the specified field name " + name + " was not found: " + this.toSql()); From 3f8dc174c81aeb7edf6340ebcaaf3d5961ab7d8f Mon Sep 17 00:00:00 2001 From: lichi Date: Tue, 21 Apr 2026 18:12:20 +0800 Subject: [PATCH 08/10] add review agents --- .../org/apache/doris/nereids/trees/expressions/AGENTS.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/AGENTS.md diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/AGENTS.md b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/AGENTS.md new file mode 100644 index 00000000000000..8fcf8cbce1eab1 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/AGENTS.md @@ -0,0 +1,5 @@ +# Nereids expressions — Review Guide + +## checkLegalityBeforeTypeCoercion + +- [ ] For all overloaded checkLegalityBeforeTypeCoercion methods, ensure child nodes are obtained via getArguments() or getArgument() instead of children() or child(). From d56c0110060c99f2b77a2a3fb462d15c7311acfe Mon Sep 17 00:00:00 2001 From: lichi Date: Fri, 24 Apr 2026 11:04:38 +0800 Subject: [PATCH 09/10] remove useless changes --- .../functions/ExpressionTrait.java | 34 ++++++------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java index e2b53b5dbd5890..f80a156e7ab6e9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTrait.java @@ -51,15 +51,17 @@ default void checkLegalityAfterRewrite() {} * getArguments. */ default List getArguments() { + boolean hasVariableArg = false; ImmutableList.Builder arguments = ImmutableList.builder(); for (Expression arg : children()) { - if (arg instanceof Variable && ((Variable) arg).getRealExpression() != null) { + if (arg instanceof Variable) { arguments.add(((Variable) arg).getRealExpression()); + hasVariableArg = true; } else { arguments.add(arg); } } - return arguments.build(); + return hasVariableArg ? arguments.build() : children(); } /** @@ -67,38 +69,22 @@ default List getArguments() { */ default Expression getArgument(int index) { Expression arg = child(index); - if (arg instanceof Variable && ((Variable) arg).getRealExpression() != null) { + if (arg instanceof Variable) { return ((Variable) arg).getRealExpression(); } else { return arg; } } - /** - * getArgumentsTypes. - */ default List getArgumentsTypes() { - ImmutableList.Builder dataTypes = ImmutableList.builder(); - for (Expression arg : children()) { - if (arg instanceof Variable && ((Variable) arg).getRealExpression() != null) { - dataTypes.add(((Variable) arg).getRealExpression().getDataType()); - } else { - dataTypes.add(arg.getDataType()); - } - } - return dataTypes.build(); + return getArguments() + .stream() + .map(Expression::getDataType) + .collect(ImmutableList.toImmutableList()); } - /** - * getArgumentType. - */ default DataType getArgumentType(int index) { - Expression arg = child(index); - if (arg instanceof Variable && ((Variable) arg).getRealExpression() != null) { - return ((Variable) arg).getRealExpression().getDataType(); - } else { - return arg.getDataType(); - } + return child(index).getDataType(); } default DataType getDataType() throws UnboundException { From f954061295cbd6041ef175511b6acb5bef31f2e9 Mon Sep 17 00:00:00 2001 From: lichi Date: Fri, 24 Apr 2026 13:14:10 +0800 Subject: [PATCH 10/10] fix fe ut --- .../functions/ExpressionTraitTest.java | 30 +------------------ 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTraitTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTraitTest.java index 0a9d246dbc5bf1..03a36c2c927754 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTraitTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/ExpressionTraitTest.java @@ -65,7 +65,7 @@ public boolean nullable() { } @Test - public void testVariableWithRealExpression() { + public void testVariable() { IntegerLiteral lit = new IntegerLiteral(42); Variable var = new Variable("v", VariableType.USER, lit); @@ -83,32 +83,4 @@ public void testVariableWithRealExpression() { Assertions.assertEquals(lit.getDataType(), func.getArgumentType(0)); } - - @Test - public void testVariableWithoutRealExpression() { - IntegerLiteral lit = new IntegerLiteral(100); - // create a Variable but override getRealExpression to simulate a missing real expression - Variable var = new Variable("v", VariableType.USER, lit) { - @Override - public Expression getRealExpression() { - return null; - } - }; - - DummyFunction func = new DummyFunction(var); - - List args = func.getArguments(); - Assertions.assertEquals(1, args.size()); - // when Variable.getRealExpression() returns null, ExpressionTrait should return the Variable itself - Assertions.assertSame(var, args.get(0)); - - Assertions.assertSame(var, func.getArgument(0)); - - List types = func.getArgumentsTypes(); - Assertions.assertEquals(1, types.size()); - // fallback to variable.getDataType() - Assertions.assertEquals(var.getDataType(), types.get(0)); - - Assertions.assertEquals(var.getDataType(), func.getArgumentType(0)); - } }