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

   ## Which issue does this PR close?
   
   Closes #4680.
   
   ## Rationale for this change
   
   Spark builds `map_from_arrays` and `map_from_entries` through 
`ArrayBasedMapBuilder`, which checks each row in order: the key and value 
counts, then each key for NULL (`NULL_MAP_KEY`) or a repeat 
(`DUPLICATED_MAP_KEY` under the default 
`spark.sql.mapKeyDedupPolicy=EXCEPTION`). On `main`:
   
   - `map_from_arrays` runs DataFusion's `map`, which fails on NULL and 
duplicate keys with its own messages ("map key cannot be null", "map key must 
be unique ...") rather than Spark's exceptions, and compares key and value 
lengths per batch rather than per row.
   - `map_from_entries` runs `datafusion-spark`'s kernel, which never checks 
for a NULL key (Arrow then rejects the non-nullable key field), reports 
duplicates as an untyped DataFusion error, and misreads a sliced input. It 
builds its key mask from `keys_offsets[0]` but applies it from index 0 of the 
unsliced child, so an earlier row's keys can be paired with a later row's 
values.
   - Keys that are equal only under a non-default collation are not detected as 
duplicates, so on Spark 4 the `UTF8_LCASE` keys `a` and `A` build two entries 
where Spark raises `DUPLICATED_MAP_KEY`.
   
   Related open PRs touch the same code. #5854 also closes #4680, by making 
`LAST_WIN` native through `datafusion.spark.map_key_dedup_policy`. #5846 adds 
per-row length checks for `map_from_arrays`, and #5844 and #5867 change how 
`map_from_arrays` is routed. This PR keeps `LAST_WIN` on its existing path, see 
below.
   
   ## What changes are included in this PR?
   
   - `native/spark-expr/src/map_funcs/map_builder.rs` adds `SparkMapFromArrays` 
and `SparkMapFromEntries`, which share `build_map`.
     - Each non-NULL row is checked in Spark's order and fails with 
`SparkError::{MapKeyValueDiffSizes, NullMapKey, DuplicatedMapKey}`, which the 
existing shims turn into Spark's own exceptions.
     - Keys are compared by their Arrow row encoding in a per-row hash set.
     - A row holding a NULL entry makes the `map_from_entries` result NULL 
before its keys are checked, as in Spark.
     - The key and value children are reused zero-copy unless a NULL row spans 
some of their entries or their C Data export would carry an offset. Arrow 
Java's import ignores a nested child's offset, which a sliced boolean child 
keeps (#2051), so those children are copied with `take`.
   - `CometMapFromArrays` now calls `map_from_arrays` inside its existing 
`CaseWhen` null guard. The `datafusion-spark` `MapFromEntries` registration is 
replaced by the Comet UDF of the same name.
   - `MapBuilderSupport` in `maps.scala` is the shared gate.
     - `LAST_WIN` stays `Incompatible`, so `map_from_arrays` falls back to 
Spark and `map_from_entries` uses the codegen dispatcher, as before.
     - Keys with a non-default collation become `Incompatible`.
     - Floating-point keys become `Incompatible` under 
`spark.comet.exec.strictFloatingPoint=true`.
     - The compatibility note saying NULL keys were not detected is removed.
   - Docs: a "Map keys" section in the floating-point compatibility guide, 
notes in `expressions.md`, and corrected audit entries in `map_funcs.md`. Comet 
compares floating-point keys by their bits. Spark treats every NaN key as one 
key, and `-0.0` as `0.0` in nested keys and, from Spark 4.0, in top-level keys.
   
   Left out on purpose, possible follow-ups:
   
   - Native `LAST_WIN`. The `CaseWhen` guard evaluates its children twice 
(#5781), and `LAST_WIN` currently falls back correctly.
   - Spark-style normalization of floating-point keys. The existing 
`normalize_nested_floats` could cover nested keys.
   - `str_to_map` under `LAST_WIN`, which reads DataFusion's default policy 
(inferred from the code, not run).
   - Normalizing nested child offsets once at the JNI export boundary instead 
of in each kernel.
   
   ## How are these changes tested?
   
   - SQL fixtures:
     - `map_from_arrays.sql` and `map_from_entries.sql` cover distinct keys of 
many types (int boundaries, doubles with NaN and infinities, multibyte strings, 
structs with NULL fields, boolean, tinyint, smallint, bigint, float, 
decimal(38, 18), date, timestamp, timestamp_ntz, binary and array keys), NULL 
arrays and NULL entries, `NULL_MAP_KEY` and `DUPLICATED_MAP_KEY` in both check 
orders, per-row length mismatches whose totals add up across rows, and repeated 
struct keys.
     - `map_from_arrays_collation.sql` and `map_from_arrays_strict_fp.sql` 
cover the new gates.
     - `routing_maps_{enabled,disabled,opt_in}.sql` and 
`routing_collection_collation_{enabled,disabled}.sql` cover `map_from_entries` 
routing for floating-point keys under strict mode and for collated keys.
   - `CometMapExpressionSuite` adds `map constructors on a sliced batch read 
the visible rows`, which feeds both constructors a batch sliced by a native 
`OFFSET`, with boolean children. It is a Scala test because 
`CometSqlFileTestSuite` excludes `ConstantFolding`, which leaves `LIMIT ... 
OFFSET` unplannable.
   - A Rust unit test in `map_builder.rs` covers a NULL values row spanning 
keys on a sliced input with boolean values. Comet's serde never passes such a 
row, so SQL cannot reach it.
   
   Ran locally on the default Spark 4.1 profile: `CometSqlFileTestSuite 
expressions/map/` (30 passed), the new `CometMapExpressionSuite` test, and 
`CometSqlFileTestSuite routing_collection_collation` (2 passed). The other 
Spark profiles and the Rust unit test were not run locally.
   


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