andygrove opened a new issue, #5783:
URL: https://github.com/apache/datafusion-comet/issues/5783
### Describe the bug
When a Parquet file contains a struct with two or more byte-identical child
field names, Comet's native scan returns one output row per (input row ×
matching leaf) instead of one row per input row. The declared output schema
still has a single field, so the extra rows appear with no error and no warning.
Spark returns the correct row count, resolving the duplicate name to a
single child.
Three shapes, all under `spark.sql.caseSensitive=true`:
| Struct in the file | Spark | Comet |
| --- | --- | --- |
| `struct<dup, dup>` | 3 rows | **6 rows** |
| `struct<dup, dup, dup>` | 3 rows | **9 rows** |
| `struct<dup, dup, other>` | 3 rows | **error** (`StructArrayReader out of
sync`) |
### Steps to reproduce
Reproduced on `bc74cc79f` (current `main`), Spark 4.1, JDK 17, macOS aarch64.
```scala
withTempPath { path =>
spark
.range(3)
.selectExpr("named_struct('dup', id, 'dup', id + 100) as s")
.write
.mode("overwrite")
.parquet(path.toString)
// An explicit read schema is required. Spark blocks schema *inference* on
duplicate
// nested names with COLUMN_ALREADY_EXISTS, so this is only reachable when
the schema
// is declared (spark.read.schema(...), or a table with a declared schema).
spark.read.schema("s struct<dup: bigint>").parquet(path.toString).collect()
}
```
Observed, with `CometNativeScanExec` in the plan:
```
spark (comet disabled) -> count=3 rows=[[2]] [[1]] [[0]]
comet -> count=6 rows=[[2]] [[102]] [[1]] [[101]] [[0]]
[[100]]
```
The multiplier tracks the number of duplicate children, so three `dup`
children give nine rows:
```
named_struct('dup', id, 'dup', id + 100, 'dup', id + 200) as s // read as
s struct<dup: bigint>
spark -> count=3 rows=[[1]] [[2]] [[0]]
comet -> count=9 rows=[[1]] [[101]] [[201]] [[2]] [[102]] [[202]] [[0]]
[[100]] [[200]]
```
Adding a non-duplicate sibling turns it into a hard error rather than wrong
results:
```
named_struct('dup', id, 'dup', id + 100, 'other', id + 900) as s
// read as s struct<dup: bigint, other: bigint>
spark -> count=3 rows=[[2,902]] [[1,901]] [[0,900]]
comet -> SparkException: Arrow error: Parquet argument error: Parquet error:
StructArrayReader out of sync in read_records, expected 1 read, got 0
```
### Expected behavior
Match Spark: one row per input row, resolving the duplicate name to a single
child. Spark's `ParquetReadSupport` builds `caseSensitiveParquetFieldMap` with
`.toMap`, so the last child with a given name wins.
Failing with a clear error would also be acceptable, and is strictly better
than silently changing the row count.
### Additional context
- **Scope is duplicates inside a struct.** Top-level duplicate column names
are not reachable this way, because Spark's own writer rejects them with
`COLUMN_ALREADY_EXISTS`. `named_struct` permits duplicate field names and the
writer accepts them, so a plain Spark job can produce a triggering file.
- Comet's nested convert in `native/core/src/parquet/parquet_support.rs`
does have a duplicate-match check, but it is deliberately gated on
`!case_sensitive` (a case-sensitive collision means byte-identical names, where
an error saying "in case-insensitive mode" would be wrong). The case-sensitive
path falls through to `indices[0]`, i.e. first-wins, which is itself a smaller
divergence from Spark's last-wins. The row multiplication happens before that,
so the projection appears to match both leaves independently.
- Found while reviewing #5751 and #5707, which cover the *case-insensitive*
ambiguity in the same match arm. Not caused by that PR, whose diff is test-only.
- Related but a different code path: #5605 (native shuffle accepts structs
with duplicate field names, then fails importing the batch back to the JVM).
- I have applied `priority:critical` because the label covers "silent wrong
results", but the trigger is narrow (duplicate names inside a struct plus an
explicit read schema). Happy for triage to re-rank.
--
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]