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

   ### Describe the bug
   
   The native Iceberg scan fails any query that reads a struct column, or a 
list of structs, when a data file omits one of its optional nested fields, even 
though the table schema never changed. Spark reads the missing field as NULL. 
Iceberg accepts such files: its write compatibility check allows a missing 
optional field at any depth, so a Spark write with 
`write.spark.accept-any-schema=true` produces one, and so can other engines.
   
   The scan fails with `Incorrect number of arrays for StructArray fields, 
expected 2 got 1`, the error #6504 reported for files written before a nested 
field was added. #6543 added a `CometScanRule` fallback for that case, but 
`IcebergReflection.nestedFieldsAddedOrRenamed` compares the projected fields 
against the table's schema history. Here no schema in the history lacks the 
field, so the scan stays native. Reading the iceberg-rust code at the current 
pin, `RecordBatchTransformer` passes the file's narrower struct through, and 
arrow's struct cast then matches children by position and fails.
   
   ### Steps to reproduce
   
   In a `CometTestBase` suite with a Hadoop Iceberg catalog `cat` and 
`spark.comet.scan.icebergNative.enabled=true`:
   
   ```scala
   spark.sql("""CREATE TABLE cat.db.t (id INT, s STRUCT<a: INT, b: INT>, l 
ARRAY<STRUCT<x: INT, y: INT>>)
     USING iceberg TBLPROPERTIES ('format-version'='2', 
'write.spark.accept-any-schema'='true')""")
   val full = StructType(Seq(
     StructField("id", IntegerType),
     StructField("s", StructType(Seq(StructField("a", IntegerType), 
StructField("b", IntegerType)))),
     StructField("l", ArrayType(StructType(Seq(StructField("x", IntegerType), 
StructField("y", IntegerType)))))))
   val narrow = StructType(Seq(
     StructField("id", IntegerType),
     StructField("s", StructType(Seq(StructField("a", IntegerType)))),
     StructField("l", ArrayType(StructType(Seq(StructField("x", 
IntegerType)))))))
   withSQLConf(CometConf.COMET_ENABLED.key -> "false") {
     spark.createDataFrame(spark.sparkContext.parallelize(Seq(Row(1, Row(1, 2), 
Seq(Row(1, 2)))), 1), full)
       .writeTo("cat.db.t").append()
     // Iceberg accepts a file that omits the optional fields s.b and 
l.element.y.
     spark.createDataFrame(spark.sparkContext.parallelize(Seq(Row(2, Row(20), 
Seq(Row(20)))), 1), narrow)
       .writeTo("cat.db.t").append()
   }
   spark.sql("SELECT id, s FROM cat.db.t").collect()
   ```
   
   On `main` at 66c63bd307 (Spark 4.1.3, Iceberg 1.11):
   
   | Query | Spark | Comet native scan |
   | --- | --- | --- |
   | `SELECT id, s` | `[1,[1,2]] [2,[20,null]]` | `Iceberg scan error: 
Unexpected => Arrow Schema Error, source: Invalid argument error: Incorrect 
number of arrays for StructArray fields, expected 2 got 1` |
   | `SELECT id, s.b` | `[1,2] [2,null]` | the same error |
   | `SELECT id, l` | `[1,[[1,2]]] [2,[[20,null]]]` | the same error |
   | `SELECT *` | the rows above | the same error |
   
   ### Expected behavior
   
   The native scan reads a missing optional nested field as NULL, as Spark does.
   
   ### Additional context
   
   Found in a nested-type audit of the native scans. Reading the code, the 
iceberg-rust bump in #6934 should fix this, because at `b154603` 
`RecordBatchTransformer` reconciles struct children by field id and fills 
missing ones. That was not verified. Until then, the failure is loud rather 
than a wrong result.
   


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