manuzhang opened a new pull request, #6246:
URL: https://github.com/apache/datafusion-comet/pull/6246

   ## Which issue does this PR close?
   
   Part of #4180 (the `spark.sql.parquet.binaryAsString` audit item) and #4764.
   
   Does not close #4121. Gap A is the same mechanism applied to annotated 
`STRING`
   columns; this PR enables the binary read schema only when
   `spark.sql.parquet.binaryAsString` is set, so `hll.sql` stays disabled.
   
   ## Rationale for this change
   
   When `spark.sql.parquet.binaryAsString` is enabled, Spark infers unannotated
   Parquet `BINARY` columns as `StringType`. Those columns can hold arbitrary 
bytes,
   and Spark keeps them verbatim in `UTF8String`.
   
   arrow-rs validates UTF-8 during Parquet decode whenever the target Arrow 
field is
   `Utf8`/`LargeUtf8` (`arrow/buffer/offset_buffer.rs::try_push`). So reading 
such a
   column natively failed with `Parquet error: encountered non UTF-8 data` where
   Spark succeeds.
   
   This follows the ingress policy the project already applies elsewhere: 
decode at
   the boundary rather than reinterpret or reject. `CAST(binary AS string)` 
(#4763)
   and the JVM-to-native FFI import boundary (#5310) both decode with
   `decode_utf8_spark_lossy`, which matches the JVM's `new String(bytes, 
UTF_8)`.
   This extends the same treatment to the native scan.
   
   The alternative, keeping affected data in Spark to preserve raw bytes, was
   considered and rejected. It contradicts the documented policy in
   `compatibility/index.md`, and it cannot coexist with the test added in #5234,
   which asserts `CometNativeScanExec` under `binaryAsString=true`.
   
   ## What changes are included in this PR?
   
   - `NativeScanCommon.binary_as_string` carries the configuration to the native
     scan; `CometNativeScan` sets it from `SQLConf.PARQUET_BINARY_AS_STRING`.
   - When it is set, `init_datasource_exec` builds the read schema with 
top-level
     `Utf8`/`LargeUtf8` fields replaced by `Binary`/`LargeBinary`, so arrow-rs 
runs
     no validation, and adds a scan-boundary projection that casts each such 
column
     back to the output type.
   - `parquet_convert_array` handles `(Binary, Utf8)` and `(LargeBinary, 
LargeUtf8)`
     with `spark_cast`, placed ahead of the `can_cast_types` arm so arrow's
     validating cast never sees malformed bytes. This covers nested values as 
well
     as the top-level projection.
   - Documents the configuration in the Spark configuration audit guide, 
including
     the divergences that follow from lossy decoding.
   
   ### Known limitation
   
   Parquet filter pushdown is skipped while the configuration is enabled, 
because
   filter expressions are built against the output `Utf8` schema and are not yet
   rewritten against the `Binary` read schema. Results stay correct, since 
Spark's
   `Filter` above the scan re-evaluates every data filter, but a scan that would
   otherwise prune row groups does not. Happy to split that out or address it 
here,
   whichever reviewers prefer.
   
   ### Divergence this does not remove
   
   Decoding is not byte-preserving, so the differences already documented under
   [Strings with non-UTF-8 
bytes](https://datafusion.apache.org/comet/user-guide/latest/compatibility/index.html)
   apply to malformed values this configuration surfaces: byte-level round 
trips,
   `octet_length`, hashing, and value identity, where two distinct malformed
   sequences both become `U+FFFD`. That is the same trade-off taken by #4763 and
   #5310.
   
   ## How are these changes tested?
   
   - `ParquetReadSuite`: a new `spark.sql.parquet.binaryAsString uses native 
scan`
     writes a raw Parquet file with no Spark schema metadata containing a valid
     value and the malformed byte `0xff`, and asserts the inferred schema under 
both
     configuration values, Spark answer parity, `CometNativeScanExec` in both 
cases,
     and that a projection reading only the non-string column stays native.
   - A Rust test covering a nested `List<Binary>` to `List<Utf8>` conversion, 
which
     exercises the new arm through `parquet_convert_array` rather than the scan
     projection.
   - The dictionary test added in #5234 passes unchanged, which is the case the
     rejected fallback design would have broken.
   
   Commands run locally:
   
   ```
   make core                                        # clean, no warnings
   cargo test -p datafusion-comet --lib parquet     # 197 passed, 0 failed, 5 
ignored
   ./mvnw test -Dtest=none \
     -Dsuites="org.apache.comet.parquet.ParquetReadV1Suite binary" -Pspark-4.1
                                                    # 4 passed, 0 failed
   ./mvnw spotless:apply -Pspark-4.1
   ```
   
   Only Spark 4.1 has been exercised locally so far, and the broader Spark SQL 
and
   Iceberg suites have not been run. This touches the serde, the planner and a
   native operator, so it needs `run-spark-sql-tests` before it goes near the 
merge
   queue.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


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