Datasource: Add file_metadata() to DataSink for per-file write metadata#23656
Open
qzyu999 wants to merge 1 commit into
Open
Datasource: Add file_metadata() to DataSink for per-file write metadata#23656qzyu999 wants to merge 1 commit into
qzyu999 wants to merge 1 commit into
Conversation
qzyu999
force-pushed
the
datasink-write-metadata
branch
from
July 17, 2026 02:24
ed75a23 to
a4d415e
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #23656 +/- ##
==========================================
+ Coverage 80.66% 80.68% +0.01%
==========================================
Files 1086 1086
Lines 366694 367027 +333
Branches 366694 367027 +333
==========================================
+ Hits 295806 296121 +315
- Misses 53265 53277 +12
- Partials 17623 17629 +6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Add FileWriteMetadata struct to datafusion-datasource along with a new default method on the DataSink trait: - file_metadata(): Post-hoc accessor returning per-file path, row count, byte size, and optional format-specific metadata bytes after write_all completes. The default implementation returns an empty Vec, so existing DataSink implementations are unaffected. ParquetSink overrides file_metadata() to expose path, row_count, and byte_size for each written file using the existing self.written HashMap that already collects ParquetMetaData during writes. The typed ParquetMetaData remains accessible via ParquetSink::written() for Rust consumers who need column-level statistics directly. DataSinkExec gains a file_metadata() convenience accessor that delegates to the underlying sink. This enables external table formats (Iceberg, Delta Lake, Hudi) to obtain per-file metadata after a DataFusion write without re-reading file footers or bypassing the write pipeline entirely. Closes apache#23472
qzyu999
force-pushed
the
datasink-write-metadata
branch
from
July 17, 2026 09:43
a4d415e to
f9c560a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
Closes #23472.
Rationale for this change
External table formats (Apache Iceberg, Delta Lake, Apache Hudi) require per-file column statistics (column_sizes, null_counts, lower/upper bounds, split_offsets) when committing written files to their catalogs. Currently,
ParquetSinkcomputes and stores this metadata internally (self.written) but theDataSinktrait only returns a row count forcing external consumers to either:DataSinkExec.sink()toParquetSinkand callwritten()(fragile, undocumented)This PR adds a standard, non-breaking mechanism to expose per-file metadata through the
DataSinktrait.What changes are included in this PR?
New type in
datafusion-datasource:FileWriteMetadata: path + row_count + byte_size + optional format-specific metadata bytesTrait extension (non-breaking, default implementation):
DataSink::file_metadata()Vec<FileWriteMetadata>(default: empty)ParquetSink implementation:
file_metadata()using the existingself.writtenHashMap that already collectsParquetMetaDataduring writes zero additional I/ODataSinkExec convenience:
file_metadata()method that delegates to the inner sink, avoiding the downcast danceThe typed
ParquetMetaDataremains accessible via the existingParquetSink::written()method for Rust consumers who need full column-level statistics directly. Theformat_metadata: Option<Bytes>field onFileWriteMetadatais reserved for future Thrift serialization when FFI consumers (e.g. Python via PyO3) need it.Are these changes tested?
Yes 14 new tests across both crates:
datafusion-datasource(7 tests):FileWriteMetadataequality and clone behaviorfile_metadata()returns empty afterwrite_allfile_metadata()returns correct entriesDataSinkExec::file_metadata()delegates correctlydatafusion-datasource-parquet(7 tests):write_allcount == sum of per-file row_counts)ParquetSink::written()accessorDataSinkExecwrapper withArc<dyn DataSink>dispatchAll existing
parquet_sink_write*tests continue to pass unchanged.Are there any user-facing changes?
New public API surface (additive only, no breaking changes):
DataSink::file_metadata()default methodFileWriteMetadatastructDataSinkExec::file_metadata()convenience method