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 @@ -149,12 +149,16 @@ private Stream<TestSetSpec> bTrimTestCases() {
private Stream<TestSetSpec> 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))
Expand Down Expand Up @@ -182,6 +186,26 @@ private Stream<TestSetSpec> eltTestCases() {
DataTypes.VARCHAR(5))
.testResult(
lit(2).elt("a", "b"), "ELT(2, 'a', 'b')", "b", DataTypes.CHAR(1))
.testResult(
lit(2).cast(DataTypes.TINYINT()).elt("scala", "java"),
"ELT(CAST(2 AS TINYINT), 'scala', 'java')",
"java",
DataTypes.VARCHAR(5))
.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))
Comment on lines +189 to +208

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 +189 to +208

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.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done — removed the redundant constant cases and added field-reference tests (f4/f5/f6) per your suggestion, so the codegen'd operator path is now covered alongside the reducer path. Thanks @SEPURI-SAI-KRISHNA for the verified patch and @raminqaf for catching the gap.

.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.
Comment on lines +43 to +45

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.

why do we need it if we have tests?

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.

other wise with such approach we should comment on every line the same which I don't think is the right way

return exprs[(int) idx - 1];
}
}