Skip to content

Add nested struct schema evolution support for Map types#23914

Open
kosiew wants to merge 10 commits into
apache:mainfrom
kosiew:map-evolution-03-20835
Open

Add nested struct schema evolution support for Map types#23914
kosiew wants to merge 10 commits into
apache:mainfrom
kosiew:map-evolution-03-20835

Conversation

@kosiew

@kosiew kosiew commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Closes #20835
This is the last of a series of PR to close #20835

Rationale for this change

Map types were not handled by DataFusion's recursive nested schema adaptation logic, leaving map key/value struct evolution undefined and unsupported. This change adds centralized validation and casting semantics for Map columns so planner-time validation and runtime casting behave consistently for supported schema evolution scenarios while rejecting unsafe key evolution.

What changes are included in this PR?

  • Add recursive Map support to the nested schema adaptation path used by planning and execution.

  • Introduce Map compatibility validation covering:

    • positional matching of map entry key/value children,
    • name-based evolution of nested Struct fields,
    • matching sorted flags,
    • validation of map entry invariants,
    • conservative rules for map key evolution to preserve key identity.
  • Implement Map casting that:

    • recursively adapts key and value children,
    • preserves map offsets, nulls, and sortedness,
    • compacts unreachable or null-hidden entries before recursive casting to avoid invalid child data affecting visible rows.
  • Extend nested-cast detection so compatible Map types are routed through the specialized nested adaptation path.

  • Document supported map evolution semantics alongside the validation logic.

  • Add comprehensive unit tests covering:

    • map key and value struct evolution,
    • positional entry matching with renamed entry fields,
    • planner/runtime validation parity,
    • sorted vs. unsorted map behavior,
    • key evolution rejection cases,
    • null and sliced map handling,
    • all-null map behavior, and
    • map entry invariant validation.
  • Add end-to-end Parquet tests covering successful map value struct evolution and rejection of incompatible evolution.

Are these changes tested?

Yes.

New test coverage includes unit tests in nested_struct.rs for:

  • Map key/value struct schema evolution
  • Nested map-entry struct adaptation
  • Planner/runtime validation parity
  • Sorted map restrictions
  • Invalid key evolution rejection
  • Null parent and sliced map handling
  • All-null map behavior
  • Map entry invariant validation

It also adds end-to-end Parquet tests:

  • test_map_value_struct_schema_evolution_end_to_end
  • test_map_value_struct_incompatible_schema_evolution_rejected

Are there any user-facing changes?

Yes.

DataFusion now supports recursive schema adaptation for compatible Map types whose keys and/or values recursively contain Structs. Compatible schema evolution is applied during planning and execution using consistent validation rules, while unsupported or unsafe map key evolution, sorted-flag changes, and incompatible non-nullable schema changes are rejected. This extends schema evolution behavior but does not introduce a public API change.

LLM-generated code disclosure

This PR includes LLM-generated code and comments. All LLM-generated content has been manually reviewed.

kosiew added 8 commits July 26, 2026 20:34
- Adapt recursive Map key/value structures.
- Implement shared planner/runtime validation.
- Preserve offsets and nulls; require matching sorted flags.
- Enforce Arrow Map entry/key invariants.
- Handle nullable additions, extras, null/all-null maps, and incompatible/non-nullable rejections.
- Add Parquet end-to-end regression tests.
…etadata internally

- Updated cast_map_column to accept &MapArray and reduce redundant arguments
- Extracted a shared error assertion helper for planner/runtime components
- Map key/value children now matched positionally while preserving target technical names.
- Implemented safe key evolution with no source key-field removal, injective primitive widening, nullable additions, and unchanged key types for sorted Maps.
- Compacted hidden null-parents and sliced unreachable entries before casting.
- Updated public rustdoc to document Map evolution semantics.
- Expanded Parquet test coverage for differing physical/logical entry names.
- Added planner/runtime regressions tests for uniqueness, sortedness, nulls, slices, and unsafe casts.
- Allow sorted targets to be set to false.
- Reject transitioning from sorted=false to sorted=true.
- Maintain requirement for unchanged key types in sorted targets.
- Allow downgraded unsorted targets to utilize existing safe key evolution.
- Update public Rust documentation.
- Add planner/runtime parity and value/key assertions.
- Reused validated target Map children and eliminated duplicate validation and map_entry_fields.
- Implemented common planner/runtime error assertion helper.
- Removed redundant sortedness assertions that are already covered by exact type equality.
- Public APIs remain unchanged.
…structs at planner boundary

- Added regression test for empty key struct to ensure nullable-only target key field is rejected in both planner and runtime.
…chema helpers

- Added `test_map_value_struct_incompatible_schema_evolution_rejected` for end-to-end negative testing of Parquet.
- Refactored shared map fixture and schema helper functions for improved clarity and reusability.
- Removed unused `let _ =` in nested_struct.rs
- Added local name lookup/set for map key struct validation in nested_struct.rs
- Extracted shared map E2E setup helper in expr_adapter.rs
- Added nullable/non-nullable schema wrapper helpers, avoiding bool at call sites in expr_adapter.rs
@github-actions github-actions Bot added core Core DataFusion crate common Related to common crate labels Jul 27, 2026
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.48322% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 80.69%. Comparing base (f1ab86d) to head (46560db).
⚠️ Report is 15 commits behind head on main.

Files with missing lines Patch % Lines
datafusion/common/src/nested_struct.rs 97.48% 12 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #23914      +/-   ##
==========================================
+ Coverage   80.65%   80.69%   +0.03%     
==========================================
  Files        1091     1094       +3     
  Lines      371031   372366    +1335     
  Branches   371031   372366    +1335     
==========================================
+ Hits       299256   300478    +1222     
- Misses      53935    53981      +46     
- Partials    17840    17907      +67     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

kosiew added 2 commits July 27, 2026 12:16
- Updated the sorted to unsorted test to enforce rejection.
- Clarified documentation for requires_nested_struct_cast Map behavior.
- Extracted compact_map_entries() with explanation for identity take.
- Extracted is_injective_map_key_cast() and added documentation for conservative policy.
… cast_map_column and rewording to “specialized Map casting path”
@kosiew
kosiew marked this pull request as ready for review July 27, 2026 05:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

common Related to common crate core Core DataFusion crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support additive schema evolution for List<Struct> / nested container types in Parquet scans

2 participants