Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,22 @@ private Stream<TestSetSpec> 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove this

Suggested change
// 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))
Comment on lines +186 to +200

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up, the fix itself looks right. One coverage gap worth closing before merge.

All three new cases pass constant arguments, so ExpressionReducer folds the entire call during optimization and the generated runtime code is never reached. The plans:

-- ELT(CAST(2 AS TINYINT), 'scala', 'java')
== Optimized Execution Plan ==
Calc(select=[CAST('java' AS VARCHAR(5)) AS EXPR$0])     <- ELT folded away

-- ELT(b, 'scala', 'java')   where b TINYINT
== Optimized Execution Plan ==
Calc(select=[ELT(b, 'scala', 'java') AS EXPR$0])        <- ELT reaches the operator

To be clear, these cases do fail without the fix, because the reducer executes the function at plan time. But they only cover the constant-folding path, not the codegen'd operator path that a real job hits, so a future regression in the runtime path wouldn't be caught here.

The case just below at line 201 already uses the field-reference pattern, so extending the existing fields covers it:

.onFieldsWithData(null, null, null, new byte[] {1, 2, 3}, (byte) 2, (short) 2, 2L)
.andDataTypes(
        DataTypes.INT(), DataTypes.STRING(), DataTypes.BYTES(), DataTypes.BYTES(),
        DataTypes.TINYINT(), DataTypes.SMALLINT(), DataTypes.BIGINT())

and then $("f4").elt("scala", "java"), $("f5"), $("f6"), keeping one of the constant cases so the reducer path stays covered too.

I'm the reporter of FLINK-40338 and already have these written and verified locally (they fail with ClassCastException on java.lang.Byte/Short/Long without the fix). Happy to hand them over for you to include here, or to open them as a follow-up if you'd rather keep this PR as is, whichever you prefer.

Comment on lines +186 to +200

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just in case write a test with non-literals too please

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raminqaf agreed. Here's the patch I have locally, already verified, it fails with ClassCastException on java.lang.Byte, Short and Long before the fix and passes after:

.onFieldsWithData(null, null, null, new byte[] {1, 2, 3}, (byte) 2, (short) 2, 2L)
.andDataTypes(
        DataTypes.INT(), DataTypes.STRING(), DataTypes.BYTES(), DataTypes.BYTES(),
        DataTypes.TINYINT(), DataTypes.SMALLINT(), DataTypes.BIGINT())
.testResult(
        $("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(
        $("f6").elt("scala", "java"),
        "ELT(f6, 'scala', 'java')",
        "java",
        DataTypes.VARCHAR(5))

@hulincup feel free to take these directly.

.testResult(
lit(2).elt($("f2"), $("f3"), $("f3")),
"ELT(2, f2, f3, f3)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
}