andygrove opened a new issue, #6659:
URL: https://github.com/apache/datafusion-comet/issues/6659

   ### Describe the bug
   
   For a `float` or `double` field inside a struct, the native Iceberg writer's 
`value_counts` and `null_value_counts` differ from iceberg-java's on Iceberg 
1.9 and later whenever the struct is NULL in some rows. iceberg-java counts 
only the rows where the struct is present. The native path counts every row, 
and counts the NULL-struct rows as nulls.
   
   With the reproduction below on Spark 4.1 / Iceberg 1.11 (100 rows, `s` NULL 
on the 50 odd ids), the table's data files add up to a `value_count` of 100 and 
a `null_value_count` of 50 for `s.x` natively, against 50 and 0 through 
iceberg-java. #6562 saw the same numbers.
   
   The native path takes these counts from the Parquet footer. 
`IcebergReflection.buildFloatFieldMetrics` builds one `FieldMetrics` per 
float/double leaf for `ParquetUtil.footerMetrics`, with the value and null 
counts copied from the native `DataFile`, which carries the footer's counts. 
Its doc assumes they are "footer-derived on both sides". That held through 
Iceberg 1.8, whose `footerMetrics` takes value and null counts from the footer 
and uses `FieldMetrics` only for NaN counts and bounds. From 1.9, 
`footerMetrics` delegates to `ParquetMetrics`, whose `metricsFromFieldMetrics` 
takes the value, null and NaN counts from the `FieldMetrics` whenever one 
exists for the field. The JVM writer's `FieldMetrics` come from its value 
writers, and `ParquetValueWriters.OptionWriter` writes a NULL struct's columns 
with `column.writeNull` directly, so the field's own writer never sees those 
rows.
   
   Fields under a list or map are not affected, because `ParquetMetrics` keeps 
no metrics for them.
   
   The native counts never prune more than iceberg-java's. They report at least 
as many nulls, so `IS NULL` prunes no more files, and "only nulls", "only NaNs" 
and "nulls or NaNs" come out the same or less often. So this is a metadata 
parity difference, visible in the `files` and `data_files` metadata tables, 
rather than a wrong query result.
   
   ### Steps to reproduce
   
   ```sql
   CREATE TABLE t (id INT, s STRUCT<x: DOUBLE>) USING iceberg;
   
   INSERT INTO t
   SELECT id, IF(id % 2 = 0, named_struct('x', CAST(id AS DOUBLE)), NULL)
   FROM range(100);
   
   SELECT value_counts, null_value_counts FROM t.data_files;
   ```
   
   Run the insert once with 
`spark.comet.write.iceberg.splitOperator.enabled=true` and 
`spark.comet.write.iceberg.enabled=true`, where the native writer engages 
(`CometIcebergWriteExec` in the plan), and once with only the split operator 
on. The test "native acceleration: NaN counts skip values under NULL structs, 
lists and maps" in #6658 builds the same shape and compares NaN counts only.
   
   ### Expected behavior
   
   The native path reports the counts iceberg-java reports for the linked 
Iceberg version: the footer's counts through 1.8, and from 1.9 only the rows 
where every enclosing struct is present.
   
   ### Additional context
   
   Split out of #6562, which covers the NaN counts. Part of #5649.
   


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