qzyu999 opened a new issue, #23472:
URL: https://github.com/apache/datafusion/issues/23472

   ### Is your feature request related to a problem or challenge?
   
   Lakehouse table formats (Apache Iceberg, Delta Lake, Apache Hudi) require 
detailed per-file Parquet metadata after writing data files. Specifically, they 
need the Parquet FileMetaData thrift structure which contains:
   
   - Per-column compressed/uncompressed sizes
   - Per-column value counts and null counts  
   - Per-column min/max statistics (lower_bounds / upper_bounds)
   - Row group split offsets
   - Total file size and record count
   
   This metadata is used to construct manifest entries (Iceberg's DataFile, 
Delta's AddFile) that enable scan planning optimizations like partition pruning 
and column statistics-based row group skipping.
   
   **Currently**, when using DataFusion's DataFrame.write_parquet() or COPY 
INTO ... FORMAT PARQUET, the ParquetSink writes the file correctly but only 
returns a count of rows written to the caller. The detailed FileMetaData is 
computed internally (it's in the Parquet footer) but is discarded  not exposed 
to the Python/Rust caller.
   
   This forces lakehouse integrations (iceberg-rust, pyiceberg, delta-rs) to 
implement their own Parquet writers (wrapping arrow-rs ArrowWriter directly) to 
capture this metadata, bypassing DataFusion's write path entirely. This means 
they cannot benefit from DataFusion's bounded-memory write capabilities 
(spill-to-disk during large writes, partitioned output, etc.).
   
   ### Describe the solution you'd like
   
   Expose parquet::file::metadata::FileMetaData (or a subset of it) from 
ParquetSink after write completion. Concretely:
   
   **Option A (preferred): Return metadata in the output RecordBatch**
   
   The DataSink::write_all() currently returns a RecordBatch with a single 
count column. Extend this to optionally include per-file metadata columns:
   
   `
   | count | path                    | file_size | column_sizes      | 
null_counts       | lower_bounds      | upper_bounds      |
   
|-------|-------------------------|-----------|-------------------|-------------------|-------------------|-------------------|
   | 50000 | s3://bucket/part-0.pq   | 4194304   | {0: 2097152, ...} | {0: 0, 
1: 42}    | {0: b'\\x01', ...} | {0: b'\\xff', ...} |
   `
   
   This could be opt-in via a session config flag (e.g., 
datafusion.execution.parquet.return_file_metadata = true) to avoid breaking 
existing behavior.
   
   **Option B: Callback/hook on ParquetSink**
   
   Allow users to provide a callback that receives FileMetaData for each 
written file:
   
   ` ust
   let sink = ParquetSink::new(...)
       .with_metadata_callback(|path, metadata| {
           // Caller captures per-file metadata here
       });
   `
   
   **Option C: Separate metadata query after write**
   
   After write_parquet(), allow querying the written file's metadata without 
re-reading the full file:
   
   `python
   df.write_parquet('output/')
   metadata = ctx.parquet_metadata('output/part-0.parquet')
   `
   
   ### Describe alternatives you've considered
   
   1. **Bypass DataFusion's write path entirely** (current workaround): Use 
rrow-rs ArrowWriter directly with a custom metadata collector. This works but 
loses DataFusion's partitioned writes, memory management, and parallelism.
   
   2. **Re-read the Parquet footer after writing**: Open the written file, seek 
to the footer, parse FileMetaData. This works but adds an extra I/O round-trip 
per file and is inefficient for cloud storage (requires a separate request to 
read the last N bytes).
   
   3. **Compute statistics from the Arrow data before writing**: Traverse the 
Arrow arrays to compute min/max/null counts before calling write. This 
duplicates work that the Parquet writer already does internally and doesn't 
capture compressed sizes or split offsets (which are only known after encoding).
   
   ### Additional context
   
   **Downstream consumers who would benefit:**
   - [apache/iceberg-rust](https://github.com/apache/iceberg-rust)  needs 
DataFile metadata for Iceberg commits
   - [apache/iceberg-python 
(pyiceberg)](https://github.com/apache/iceberg-python)  same, via 
datafusion-python
   - [delta-io/delta-rs](https://github.com/delta-io/delta-rs)  needs AddFile 
metadata for Delta commits
   - Any DataFusion-based lakehouse integration
   
   **The specific fields needed** (from Parquet's FileMetaData thrift):
   - 
   um_rows (already returned)
   -  ow_groups[*].columns[*].meta_data.total_compressed_size  column_sizes
   -  ow_groups[*].columns[*].meta_data.num_values  value_counts
   -  ow_groups[*].columns[*].meta_data.statistics.null_count  null_value_counts
   -  ow_groups[*].columns[*].meta_data.statistics.min_value  lower_bounds
   -  ow_groups[*].columns[*].meta_data.statistics.max_value  upper_bounds
   -  ow_groups[*].columns[0].meta_data.data_page_offset  split_offsets
   - Total file size in bytes
   
   **Related upstream issue:** 
https://github.com/apache/datafusion-python/issues/1624 (per-session object 
store config  a prerequisite for efficient streaming writes to cloud storage)


-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to