LuciferYang opened a new pull request, #9561: URL: https://github.com/apache/paimon/pull/9561
### Purpose close #9560 `RowFileFormat.createReaderFactory` built one `NestedProjectedRow` and `RowFormatReaderFactory` handed that same instance to every reader it created. The iterator mutates the wrapper in place per row, and `NestedProjectedRow.replaceRow` returns `this`, so two readers from one factory hand out the same row object: reading from one overwrites the row whose consumer is still holding it. That breaks `RecordReader.readBatch`, which documents that the returned iterator and the objects in it may be held onto for some time and should not be reused. This creates the projection per reader instead. Any consumer that holds a row from one reader while advancing another reader of the same factory is affected. In this codebase that is merge-on-read: `KeyValueFileReaderFactory` caches one `FormatReaderMapping` per `(schemaId, formatIdentifier)`, `MergeTreeReaders` opens one reader per sorted run through it, and `SortMergeReaderWithMinHeap` and the loser tree hold each run's current `KeyValue` while advancing the others. That path is traced from the code and not reproduced here: it needs a primary-key table with `file.format = 'row'`, two overlapping sorted runs, and a column projection. The change also drops the `@Nullable` on `projectedRowType`. It was correct on the `NestedProjectedRow` field it replaced, since `create` returns null when the two schemas are equal, but not on a `RowType` that `create` dereferences; `FileFormat.createReaderFactory` declares only `filters` as nullable and all call sites pass a real `RowType`. ### Tests `RowFormatReadWriteTest.testInterleavedReadersDoNotShareProjectedRow` writes two single-row files, opens a reader for each from one factory, reads a row from A, then reads from B and checks that A's row is neither the same object as B's nor overwritten. The projection has to drop a column: with an identical schema `NestedProjectedRow.create` returns null, no wrapper is involved, and the test would pass against the old code too. The comment in the test says so. Against the pre-fix code it fails on `assertThat(rowA).isNotSameAs(rowB)` with `Expected not same: org.apache.paimon.utils.NestedProjectedRow@...`, which names the mechanism rather than just a wrong value. The final assertion is not redundant with it: two distinct wrappers over one shared underlying row would satisfy `isNotSameAs` and still fail the value check. `mvn -pl paimon-format test` on JDK 8: 597 tests, 0 failures. `spotless:check` and `checkstyle:check` are clean. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
