Skip to content

Spark 4.1: Fix row lineage in vectorized ORC reads - #17635

Draft
joyhaldar wants to merge 1 commit into
apache:mainfrom
joyhaldar:orc-vectorized-row-lineage
Draft

Spark 4.1: Fix row lineage in vectorized ORC reads#17635
joyhaldar wants to merge 1 commit into
apache:mainfrom
joyhaldar:orc-vectorized-row-lineage

Conversation

@joyhaldar

@joyhaldar joyhaldar commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

VectorizedSparkOrcReaders.StructConverter checked idToConstant before any metadata column, so _row_id matched the generic constant branch and every row received first_row_id. Stored per-row values were ignored and the position offset was never applied. _last_updated_sequence_number had the same problem.

_row_id for a file with first_row_id = 100. Rows 0 and 2 store explicit values (555, 557), rows 1 and 3 store null and should inherit 100 + position.

row expected before
0 555 100
1 101 100
2 557 100
3 103 100

The fix mirrors what the row-based reader already does in OrcValueReaders: check the metadata columns first, and fall back to first_row_id + position only when the file has no stored value. Adds RowIdColumnVector and LastUpdatedSeqColumnVector for that, and passes the ORC schema into StructConverter so the stored columns can be found by field id.

Row based ORC got lineage support in #15776 and #16534, but the vectorized reader was not added.

Found while adding vectorized coverage to the format model TCK (#17610).

Co-authored-by: Joy Haldar <joy.haldar@target.com>
@github-actions github-actions Bot added the spark label Aug 13, 2026
@joyhaldar

Copy link
Copy Markdown
Contributor Author

cc: @pvary, @Guosmilesmile, please take a look when you have some time.

@Guosmilesmile Guosmilesmile left a comment

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.

Thank you very much for the PR! Could we add an end-to-end test case as well?

};
}

private static Map<Integer, Integer> buildFieldIdToOrcIndex(TypeDescription orcType) {

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.

Seem the same in orc/src/main/java/org/apache/iceberg/orc/OrcValueReaders.java . Can we extract common part into a shared helper?


GenericRecord record = GenericRecord.create(writeSchema);
ImmutableList.Builder<Record> builder = ImmutableList.builder();
for (int i = 0; i < 4; i++) {

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.

We currently only test within a batch. Could we add a test case where batchOffsetInFile > 0 to cover the firstRowId + batchOffsetInFile + rowId logic?

orcType.getChildren().size());
}

private ColumnVector storedVector(

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.

Should these private methods be moved to the bottom, after the public methods?

}

@Test
public void testRowLineage() throws IOException {

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.

We should avoid using test prefixes for newly added tests.

}

@Test
public void testRowLineage() throws IOException {

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.

May be package private?

import org.apache.spark.sql.vectorized.ColumnarMap;
import org.apache.spark.unsafe.types.UTF8String;

public class LastUpdatedSeqColumnVector extends ColumnVector {

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.

There’s quite a bit of duplicated code between RowIdColumnVector and LastUpdatedSeqColumnVector. Could we extract the common logic into a base class?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants