fix: preserve Spark semantics for dictionary-encoded Parquet inputs and reject dictionary targets - #5234
Conversation
|
Thanks for picking this up. Inverting the order so the Spark cast owns the value semantics and Arrow owns the dictionary construction is the right shape, and deleting the parquet branch is a nice cleanup. I spent some time verifying the two preconditions the issue asked about, and both hold up:
Dropping the I also liked the test data choice in A few things I would like to work through before this merges. The new path supports fewer dictionary value types than the old one
Under the old code, What makes this awkward to guard against is that Could you add a test for The value cast is threaded with cast options but the packing call is notWould you mind using The surviving from-dictionary arm still hand-rolls, and it ignores the requested key typeThis one predates the PR, but it is the same function and the same class of bug you just fixed on the parquet side, so I would rather not leave it floating. The Arrow's Test coverageThe two new tests are well targeted at what changed. Two gaps I would like closed:
One question on scopeIt would help if the description said whether any real query plan can request a |
|
@andygrove Thanks for the detailed review, especially for verifying that the generic Parquet fallback fully covers the deleted branch. I pushed a follow-up in After tracing the Spark and Comet paths, I ended up separating dictionary sources from dictionary targets. Dictionary targetsI confirmed that a real Spark plan cannot request a I therefore removed target packing instead of adding Boolean, non- With Dictionary targets unsupported, there is no remaining requested-key mismatch to track separately. Dictionary sourcesThe source-Dictionary path is reachable during native Parquet schema adaptation. Spark ignores I rewrote that handler to use SQL coverageI added an end-to-end native Parquet test. It writes an unannotated Parquet As a negative control, removing the source-Dictionary handler makes the test fail: Spark returns The hand-written Parquet dictionary branch remains deleted, and the synthetic Parquet Dictionary-target test was removed because its target cannot originate from a Spark schema. |
Which issue does this PR close?
Closes #5096.
Rationale for this change
Comet can receive Arrow dictionary arrays as physical Parquet input before schema adaptation. Spark, however, cannot express
Dictionaryas a SQL cast target. Spark infers a Parquet schema from its own metadata or the Parquet physical schema, while arrow-rs also honorsARROW:schema; an unannotated ParquetBINARYcolumn can therefore reach Comet asDictionary(Int32, Binary)while Spark expectsStringTypewhenbinaryAsStringis enabled.That reachable source path must cast dictionary values with Spark semantics before Arrow unpacks the dictionary. The previous implementation conflated reachable dictionary sources with unreachable dictionary targets, hard-coded
Int32dictionary keys, and duplicated Arrow's dictionary handling in the Parquet conversion path.What changes are included in this PR?
Dictionarycast targets with a clear internal error because Spark cannot produce them.cast_with_optionsto unpack the result.can_cast_types/cast_with_optionsfallback.Dictionary(Int32, Binary)metadata and invalid UTF-8, then verifies Comet matches Spark's replacement-character behavior.How are these changes tested?
cargo test --manifest-path native/Cargo.toml -p datafusion-comet-spark-expr --lib(595 passed)cargo test --manifest-path native/Cargo.toml -p datafusion-comet --lib(135 passed, 4 HDFS tests ignored)cargo clippy --manifest-path native/Cargo.toml -p datafusion-comet-spark-expr -p datafusion-comet --lib --tests -- -D warningscargo fmt --manifest-path native/Cargo.toml --all -- --check-Pspark-3.5./mvnw spotless:check -DskipTests -Dskip-ratgit diff --checkAs a negative control, removing the source-dictionary handler makes the SQL regression fail: Spark returns
f�o, while Comet returnsnull.