From a1d7732ae7efbf16fa72e1a05e156c2868a8fd1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E7=90=B3=28HuLin=29?= Date: Thu, 6 Aug 2026 09:44:43 +0800 Subject: [PATCH 1/2] [FLINK-40338][table-runtime] ELT() throws ClassCastException when index is not INT EltFunction.eval declares index as java.lang.Number to support TINYINT/SMALLINT/INT/BIGINT, but indexes the varargs array with exprs[(int) index - 1]. Per JLS 5.5, casting a Number reference to int compiles to a checkcast to Integer followed by unboxing, so a Byte, Short, or Long value throws ClassCastException on the success path (1 <= index <= exprs.length). The out-of-range guard above uses index.longValue(), so out-of-range indices of any type still return NULL correctly; the exception only fires in the valid range. Narrow the already-unboxed long idx (computed above for the range check) via primitive narrowing (JLS 5.1.3) instead, which emits no checkcast and works for every INTEGER_NUMERIC type. Adds TINYINT/SMALLINT/BIGINT index regression cases to StringFunctionsITCase. --- .../planner/functions/StringFunctionsITCase.java | 16 ++++++++++++++++ .../runtime/functions/scalar/EltFunction.java | 5 ++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/StringFunctionsITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/StringFunctionsITCase.java index 0fd1c53dac979..e1e6223c29dbb 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/StringFunctionsITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/StringFunctionsITCase.java @@ -182,6 +182,22 @@ private Stream eltTestCases() { DataTypes.VARCHAR(5)) .testResult( lit(2).elt("a", "b"), "ELT(2, 'a', 'b')", "b", DataTypes.CHAR(1)) + // FLINK-40338: non-INT INTEGER_NUMERIC index must not throw ClassCastException + .testResult( + lit(2).cast(DataTypes.TINYINT()).elt("scala", "java"), + "ELT(CAST(2 AS TINYINT), 'scala', 'java')", + "java", + DataTypes.VARCHAR(5)) + .testResult( + lit(2).cast(DataTypes.SMALLINT()).elt("scala", "java"), + "ELT(CAST(2 AS SMALLINT), 'scala', 'java')", + "java", + DataTypes.VARCHAR(5)) + .testResult( + lit(2).cast(DataTypes.BIGINT()).elt("scala", "java"), + "ELT(CAST(2 AS BIGINT), 'scala', 'java')", + "java", + DataTypes.VARCHAR(5)) .testResult( lit(2).elt($("f2"), $("f3"), $("f3")), "ELT(2, f2, f3, f3)", diff --git a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/EltFunction.java b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/EltFunction.java index 9af2cf20367d8..9ad4ac7116dff 100644 --- a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/EltFunction.java +++ b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/scalar/EltFunction.java @@ -40,6 +40,9 @@ public EltFunction(SpecializedContext context) { if (idx < 1 || idx > exprs.length) { return null; } - return exprs[(int) index - 1]; + // Narrow the already-unboxed long instead of casting the Number reference. + // Casting `index` (java.lang.Number) to int compiles to a checkcast to Integer + // followed by unboxing, which throws ClassCastException for Byte/Short/Long. + return exprs[(int) idx - 1]; } } From 8ee7dd53ccc4d84b8bb9ba12ae6b80a9ac912e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E7=90=B3=28HuLin=29?= Date: Fri, 7 Aug 2026 08:40:08 +0800 Subject: [PATCH 2/2] [FLINK-40338][table-planner] Cover codegen path with field-reference ELT tests Constant test cases are folded by ExpressionReducer during optimization, so they only cover the constant-folding path, not the codegen'd operator path that a real job hits. Extend onFieldsWithData with TINYINT/SMALLINT/ BIGINT fields and add field-reference test cases (f4/f5/f6) to cover the runtime path; keep one constant case so the reducer path stays covered. Test plan suggested by @SEPURI-SAI-KRISHNA and @raminqaf during review. --- .../functions/StringFunctionsITCase.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/StringFunctionsITCase.java b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/StringFunctionsITCase.java index e1e6223c29dbb..076eeb5ba5a33 100644 --- a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/StringFunctionsITCase.java +++ b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/StringFunctionsITCase.java @@ -149,12 +149,15 @@ private Stream bTrimTestCases() { private Stream eltTestCases() { return Stream.of( TestSetSpec.forFunction(BuiltInFunctionDefinitions.ELT) - .onFieldsWithData(null, null, null, new byte[] {1, 2, 3}) + .onFieldsWithData(null, null, null, new byte[] {1, 2, 3}, (byte) 2, (short) 2, 2L) .andDataTypes( DataTypes.INT(), DataTypes.STRING(), DataTypes.BYTES(), - DataTypes.BYTES()) + DataTypes.BYTES(), + DataTypes.TINYINT(), + DataTypes.SMALLINT(), + DataTypes.BIGINT()) // null input .testResult( $("f0").elt("a", "b"), "ELT(f0, 'a', 'b')", null, DataTypes.CHAR(1)) @@ -182,20 +185,27 @@ private Stream eltTestCases() { DataTypes.VARCHAR(5)) .testResult( lit(2).elt("a", "b"), "ELT(2, 'a', 'b')", "b", DataTypes.CHAR(1)) - // FLINK-40338: non-INT INTEGER_NUMERIC index must not throw ClassCastException + // FLINK-40338: non-INT INTEGER_NUMERIC index must not throw ClassCastException. + // Constant case covers the ExpressionReducer (constant-folding) path; + // field-reference cases below cover the codegen'd operator path. .testResult( lit(2).cast(DataTypes.TINYINT()).elt("scala", "java"), "ELT(CAST(2 AS TINYINT), 'scala', 'java')", "java", DataTypes.VARCHAR(5)) .testResult( - lit(2).cast(DataTypes.SMALLINT()).elt("scala", "java"), - "ELT(CAST(2 AS SMALLINT), 'scala', 'java')", + $("f4").elt("scala", "java"), + "ELT(f4, 'scala', 'java')", + "java", + DataTypes.VARCHAR(5)) + .testResult( + $("f5").elt("scala", "java"), + "ELT(f5, 'scala', 'java')", "java", DataTypes.VARCHAR(5)) .testResult( - lit(2).cast(DataTypes.BIGINT()).elt("scala", "java"), - "ELT(CAST(2 AS BIGINT), 'scala', 'java')", + $("f6").elt("scala", "java"), + "ELT(f6, 'scala', 'java')", "java", DataTypes.VARCHAR(5)) .testResult(