fix: preserve map field metadata and honor target sorted flag in cast_map_to_map - #5227
Smallfu666 wants to merge 1 commit into
Conversation
|
Review assisted by an LLM (Claude Code). I checked the branch out, ran the tests, and verified the claims below myself. Thanks for taking this on. The two core fixes are right. Using What I ran locally on the branch:
A few things I would like to resolve before merge. The sorted rejectionIssue #5097 asked for the target The code comment points to the PR description for planner reachability, but I could not find that discussion there. When I traced it, That suggests no Comet plan ever asks for a Cast options in the fast pathThe fast path passes the static Test coverage
Along the same lines, a test that casts the same input through both paths and compares results would guard against the two drifting. One thing I checked that is fineCasting Separately, Comet's CIThere are no CI results on this yet and the branch is 35 commits behind main. Could you rebase so a full run can be triggered? My local runs only covered the Rust tests and the three map cast suites. |
23ccf79 to
89d30fb
Compare
|
Thanks for the review, and for checking out the branch and running it yourself. Your read is right, so I dropped the rejection branch and kept the I confirmed your four sites and then looked for a producer you had not covered. I could not find One correction on the count. 2 of the 16 tests exercised the rejection, not 5, and those are the 2 I Cast options are now built by an Both coverage tests are in, and I verified with a temporary probe which branch each test actually Separately, all of this was pinned only by Rust assertions, which proves agreement with my own Nothing under Rebased onto current main. CI has not started, since fork runs still need a maintainer to approve If you still think the |
89d30fb to
f2986a2
Compare
This is careful work. The comments citing specific arrow-array and arrow-data line numbers for why a guard is or is not reachable are unusually good, and the "intentionally not Three questions. Target field nullability can now make a
Under Is a differing Spark's If it is, the sort-flag half of the fix is defensive rather than a bug users can hit, and saying so would help set expectations. If it is not, an example of where the flags diverge would be valuable in the description, since "cast to a map type with a different The delegated path and eval mode The rename-only path calls |
b3eca55 to
7eb306f
Compare
7eb306f to
7ca9d33
Compare
…_map_to_map cast_map_to_map rebuilt the entries, key and value fields with Field::new, which dropped field metadata and target nullability, so data_type() did not equal the requested target. serde.rs runs map key and value fields through with_parquet_field_id, so a target really can carry PARQUET:field_id. It also used the source sorted flag for the result rather than the target's. Delegate the rename-only case to arrow's cast, which clones the target entries field rather than rebuilding it. Build the hand-built case with the target sorted flag and target fields, and use try_new instead of new. Guard the entries field count before indexing, since that indexing happens before try_new and would otherwise panic on a 0 or 1 field target. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
7ca9d33 to
39df950
Compare
|
Thanks again for the review, and sorry for the churn on this branch. I went back through your The unreachable sorted rejection is gone. The target sorted flag is simply preserved, and the I also kept the field-count guard because the indexing happens before While rechecking the surrounding behavior I found that the entries null buffer mentioned in #5097 I also found a separate reachable The branch is now rebased on current main and the Rust tests, clippy, and fmt are clean. CI is |
Which issue does this PR close?
Related to #5097. Intentionally not
Closes, see the scope note below.Rationale for this change
cast_map_to_maprebuilt the entries, key and value fields from scratch withField::new, whichdropped field metadata and target nullability, so
data_type()did not equal the requestedtarget. Comet's
serde.rsruns map key and value fields throughwith_parquet_field_id, so atarget really can carry
PARQUET:field_id, and that is the loss with a real producer.It also used the source
sortedflag for the result rather than the target's.What changes are included in this PR?
is the common Parquet
key_valueto Sparkentriesrelabel, delegate to arrow's cast. Arrowclones the target entries field rather than rebuilding it, so metadata survives.
cast_arrayand build the resultwith the target sorted flag and the target fields.
try_newreplacesnew, so a malformedtarget returns
Errinstead of panicking.[0]and[1], so a target with 0 or 1 fieldsreturns
Errrather than panicking. Without it thattry_newpromise is not actually true,because the indexing happens first.
Scope notes
Two things found while working on this are deliberately not here.
The
sortedflag half of #5097 is defensive rather than a bug users can reach.serde.rsbuildsevery map type with
sorted = false, the planner propagates it unchanged, andmap_sortreusesthe input flag, so the flags always agree in a Comet plan and the hand-built branch is only
reached when a child type changes. The target flag is now simply copied, and the unreachable
rejection that was here before is gone.
The entries null buffer half of #5097 does not appear to be a live bug.
MapArray::try_newrejects entries carrying any null,
StructArray::try_newdiscards an all-valid null buffer, andthe
ArrayDataroute normalizes one toNoneinArrayDataBuilder::build, soentries().nulls()isNonehowever the array was built. The hand-built path now passesNonewith that reasoning recorded, rather than reading a buffer back that cannot exist. This is why
the PR says
Related torather thanCloses.A reachable
TRY_CASTdivergence for narrowing integral map keys is deliberately out of scopehere and is filed as #5995. It is a
TRY_CASTsemantics question rather than a metadatapreservation one.
How are these changes tested?
9 map-cast unit tests in
cast.rs: the rename-only fast path with values, offsets and nullspreserved, the hand-built path with metadata and child type casts, a key and value cast, the
target sorted flag, a cross-path comparison over sliced input carrying a null row, a malformed
target, and the 0, 1 and 3 entry-field cases.
All nine fail on unpatched main.
cargo test -p datafusion-comet-spark-exprpasses 857 tests.cargo clippy -p datafusion-comet-spark-expr --all-targets -- -D warningsandcargo fmt --all -- --checkare clean.No SQL fixture was added.
CometNativeCastSuite's "cast MapType to MapType" already comparesmap to map casts against Spark, and a SQL answer comparison cannot observe field metadata or a
sort flag, so a fixture would not be a regression test for this change.
🤖 Generated with Claude Code