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

   ### Describe the bug
   
   When none of the requested fields under a nested column exist in a Parquet 
file, Spark's legacy behavior returns the column as NULL, but the native 
Parquet scan returns a non-null array or struct that holds NULLs. Legacy is the 
only behavior on Spark 3.4, 3.5 and 4.0, and it is what Spark 4.1+ does with 
`spark.sql.legacy.parquet.returnNullStructIfAllFieldsMissing=true`. The shapes 
are an array whose element struct has no field in the file, a struct whose only 
requested child is such a struct, and a struct whose requested children all 
lead to missing fields. Schema evolution that replaced a nested struct's fields 
produces them. The results are wrong without an error: `count(col)` and `col IS 
NULL` both disagree with Spark.
   
   Spark's `ParquetColumnVector` marks any struct, array or map vector all-null 
when every child vector is missing, and it applies this bottom-up (Spark 3.5.9 
`ParquetColumnVector.java` lines 111-134). An array whose element struct has no 
field in the file is therefore missing itself, and so is a struct whose 
requested children are all missing at some depth. Comet's 
`parquet_convert_struct_to_struct` 
(`native/core/src/parquet/parquet_support.rs` lines 565-576) only nulls a 
struct when none of its direct requested fields matches a file field, and the 
list conversion keeps the list's own validity. So the list stays non-null with 
NULL elements, and a struct whose child exists by name but has no matching 
leaves stays non-null.
   
   ### Steps to reproduce
   
   In a `CometTestBase` suite:
   
   ```scala
   spark.sql("""SELECT 1 AS id,
     |  array(named_struct('x', 1, 'y', 'a')) AS arrs,
     |  named_struct('a', 1, 'c', named_struct('d', 2)) AS s""".stripMargin)
     .coalesce(1).write.parquet(path)
   val df = spark.read
     .schema("id int, arrs array<struct<w:int>>, s struct<c:struct<n:int>>")
     .parquet(path)
   df.collect()
   df.selectExpr("count(arrs)", "count(s)").collect()
   ```
   
   On `main` at 66c63bd307:
   
   | Spark version and mode | Query | Spark | Comet native scan |
   | --- | --- | --- | --- |
   | 3.4.3, 3.5.9 (default) | `SELECT *` | `[1,null,null]` | 
`[1,[null],[null]]` |
   | 3.4.3, 3.5.9 (default) | `count(arrs), count(s)` | `[0,0]` | `[1,1]` |
   | 4.1.3, `returnNullStructIfAllFieldsMissing=true` | `SELECT *` | 
`[1,null,null]` | `[1,[null],[null]]` |
   | 4.1.3, default | `SELECT *` | `[1,[[null]],[[null]]]` | 
`[1,[[null]],[[null]]]` |
   
   Over a wider generated dataset the same holds for `sm struct<sa: 
array<struct<z9: int>>>` (Spark NULL, Comet `{[null, ...]}`) and for `sm 
struct<mm: map<string,int>, sa: array<struct<z9: int>>>`, where Spark keeps 
`sm` but returns `sa` as NULL. A map whose value struct has no field in the 
file matches Spark, because the keys are present.
   
   ### Expected behavior
   
   With the legacy behavior in effect, a requested nested column, array element 
or struct field whose subtree has no field in the file reads as NULL at the 
highest level whose whole subtree is missing, as Spark's reader does.
   
   ### Additional context
   
   Found in a nested-type audit of the native scans. Spark 4.1's default 
behavior (SPARK-53535) matches already. A related detail: on Spark before 4.1, 
Comet honors 
`spark.sql.legacy.parquet.returnNullStructIfAllFieldsMissing=false` when a 
session sets it (`CometNativeScan.populateScanConfFlags`), although Spark 3.x 
ignores that 4.1-only key, so setting it there makes even the plain all-missing 
struct diverge.
   


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