Visitar URL original
Data: Add a Record stitcher for column files by anuragmantri · Pull Request #18388 · apache/iceberg · GitHub
Skip to content

Data: Add a Record stitcher for column files - #18388

Draft
anuragmantri wants to merge 12 commits into
apache:mainfrom
anuragmantri:data-generic-stitchers
Draft

anuragmantri wants to merge 12 commits into
apache:mainfrom
anuragmantri:data-generic-stitchers

Conversation

@anuragmantri

Copy link
Copy Markdown
Collaborator

Depends on #18324

This PR is only the top commit 192d90b

This PR adds org.apache.iceberg.data.GenericStitchers, modeled on SparkStitchers in #18324.

  • The stitched Record reads each field from the part that StitchLayout assigns it to.
  • set and setField write through to that part, as the Spark row does, and copy() returns a detached GenericRecord.
  • GenericReader now resolves column files to InputFiles, unencrypted only.
  • An encrypted column file fails with a clear error. inputFiles(DataFile) is the method the planned InputFilesDecryptor wiring will replace.
  • ColumnFileTestHelpers moves from Spark to core test sources so the data tests can use it.

Test plan:

  • Reading through GenericReader returns column-file values instead of stale base values, keeps a struct from the base intact, and still works for a projection of only base fields.
  • A deletion vector drops the right rows, because the delete filter reads _pos through the stitched record.
  • TestGenericStitchers validates the record's contract: fields resolve in projection order by position and name, a set value reads back, copy() stays intact when the parts change (readers reuse part records), and the parts list is not kept.

AI Disclosure

  • Model: Claude Opus 5.5
  • Platform/Tool: Claude Code
  • Human Oversight: Fully reviewed after code generation
  • Prompt Summary: Implement the Record stitcher that WiP column file reading #18324's StitcherRegistry names and wire it into GenericReader.

gaborkaszab and others added 12 commits October 5, 2026 14:12
This change introduces the interface for column files and also
integrates it to the schema for TrackedFile.
…lkDecrypt()

Changes this contains:
  - More generic interface with parameters:
      - Iterable<FileScanTask>
      - EncryptingFileIO
  - Contructs InputFiles lazily when first requested
  - EncryptionManager is wrapped into EncryptingFileIO input parameter
  - Use EncryptingFileIO.bulkDecrypt() instead of EncryptionManager.decrypt()
  - Throw if file location is not found
  - Removed usage of old contructor and getInputFile(FileScanTask)

With this design, this class is generic enough to be wired into other
readers as a common implementation for creating InputFiles with bulk
decryption taken into account. Later, GenericReader and Spark's readers can
swap to use this.

One hidden improvement: Previously this class created InputFiles using
location + key metadata without the size. While this works, when it comes
to reading, it requires an extra RPC to get the size of the file.
Now, with bulkDecrypt(), the location + size + key metadata are used
together saving an extra RPC per file.

Lazy initialization of the input files is guarded against concurrent
access, and the concurrency test uses a barrier with a timeout.
@anuragmantri anuragmantri changed the title Data generic stitchers Data: Add generic stitching readers Oct 5, 2026
@anuragmantri anuragmantri changed the title Data: Add generic stitching readers Data: Add a Record stitcher for column files Oct 5, 2026
@anuragmantri

Copy link
Copy Markdown
Collaborator Author

@gaborkaszab, this 192d90b commit adds a stitcher for generic reads, this is on top of #18324 so it's still in draft.

@gaborkaszab gaborkaszab 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.

Thanks for the PR, @anuragmantri !
I went through the non-test part as a first pass.

.build();
}

private Map<String, InputFile> inputFiles(DataFile file) {

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 for the record, as you wrote in the description, InputFilesDecryptor is meant.to be used in this class to provide the InputFiles. As a result this function will be gone.


@Override
public Record copy() {
return toGenericRecord().copy();

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.

Isn't this a double copy? toGenericRecord seems itself a full copy, and then we call copy() on it. Same for the other variation below.

}
}

private static class StitchedRecord implements Record {

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.

Many of the method implementation seems identical to StitchedInternalRow. Maybe have a common base class sharing the common functionality?

public Stitcher<Record> build(Schema projection, List<Schema> verticalSplits) {
StitchLayout layout = StitchLayout.of(projection, verticalSplits);
Types.StructType struct = projection.asStruct();
Map<String, Integer> positions = Maps.newHashMap();

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.

positions seems something that is specific for StitchedRecord. Is it needed because GenericRecord has some name-based operations?

I think it should be internal to StitchedRecord. Is it feasible not to pass as a param but construct internally?

This branch has not been deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants