[FLINK-40232][table-runtime] Handle null rowtime in OutputConversionOperator - #29027
[FLINK-40232][table-runtime] Handle null rowtime in OutputConversionOperator#29027imankhadiv wants to merge 1 commit into
Conversation
1cd6354 to
e9c706b
Compare
|
@nateab thanks for assigning me FLINK-40232 — Azure is green on this one, so it's ready for review. Both rowtime lookups in Test coverage in Could you take a look, or point me to the right committer for table-runtime? Happy to rebase or adjust anything. |
| // timestamp might be TIMESTAMP or TIMESTAMP_LTZ | ||
| final long rowtime = rowData.getTimestamp(rowtimeIndex, 3).getMillisecond(); | ||
| outRecord.setTimestamp(rowtime); | ||
| updateRowtime(rowData, rowtimeIndex); |
There was a problem hiding this comment.
nit: you could move updateRowtime call outside the if else and set the index in the if else.
There was a problem hiding this comment.
Applied in a0142a2 — the position is now computed in the if/else and updateRowtime is called once, with the two type comments kept on their respective branches.
There was a problem hiding this comment.
Azure is green on a0142a2. Since the nit is addressed and the change is confined to OutputConversionOperator, would you know which committer is the right one to tag for a table-runtime review?
…perator OutputConversionOperator.processElement dereferenced the result of RowData#getTimestamp without a null check on both the rowtime metadata path and the rowtime column path, so a null rowtime attribute failed the job with a NullPointerException. Skipping setTimestamp() alone is not enough: outRecord is a single StreamRecord allocated in open() and reused for every element, and StreamRecord#replace(Object) leaves the timestamp untouched. The record carrying the null rowtime would therefore inherit the timestamp of the preceding record, turning the NPE into a silently wrong timestamp. The null case now erases the timestamp explicitly. Generated-by: Claude Code (Claude Opus 5)
e9c706b to
a0142a2
Compare
What is the purpose of the change
OutputConversionOperator.processElementdereferences the result ofRowData#getTimestamp(...)without a null check, so converting a table with anullrowtime attribute into aDataStreamfails the job with aNullPointerException. Both rowtime paths are affected: the rowtime metadata path and the rowtime column path.Adding null checks alone is not enough.
outRecordis a singleStreamRecordallocated once inopen()and reused for every element, andStreamRecord#replace(Object)leaves the timestamp untouched. Merely skippingsetTimestamp()would leave the previous record's timestamp on the record carrying the null rowtime, i.e. it would trade theNullPointerExceptionfor a silently wrong timestamp. The null case therefore erases the timestamp explicitly.Brief change log
OutputConversionOperatorgo through a new privateupdateRowtime(RowData, int)helperStreamRecord#eraseTimestamp()when it is null, so a record with a null rowtime cannot inherit the preceding record's timestampVerifying this change
This change added tests and can be verified as follows:
OutputConversionOperatorTestwith four cases — a null rowtime is emitted without a timestamp, and a null rowtime does not inherit the timestamp of the preceding record — each covering the rowtime metadata path and the rowtime column pathNullPointerException, and the two "does not inherit" cases again against a null-check-only variant of the fix, which is what motivated the expliciteraseTimestamp()flink-table-runtimemodule test suite passes (1824 tests)DataStreamJavaITCasepasses (38 tests); it covers the non-null rowtime path end to end viatoDataStreamwith event-time windows and a downstream assertion onctx.timestamp()Does this pull request potentially affect one of the following parts:
@Public(Evolving): noisNullAtcheck per record, in an operator that already read the rowtime fieldDocumentation
Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Claude Opus 5)