Conversation
There was a problem hiding this comment.
Code Review
This pull request improves PyArrow schema unification and struct field alignment in Ray Data. Key changes include stripping null types first during field reconciliation, ensuring struct fields are only reconciled when all arms are structs (preventing incorrect unification of struct and primitive mixes), and properly handling all-null nested fields by filling them with nulls of the target struct type. Comprehensive unit tests are also added. The reviewer suggested a performance optimization in _reconcile_field to immediately return the single type when only one non-null type is present, avoiding unnecessary overhead.
e225e52 to
e888075
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
Reviewed by Cursor Bugbot for commit e888075. Configure here.
12a6e99 to
3249dac
Compare
iamjustinhsu
left a comment
There was a problem hiding this comment.
Wow nice catch! tyty
|
@iamjustinhsu don't forget to click go/enable auto-merge if ready to merge |
…ct alignment Fix three bugs in the struct alignment / schema reconciliation path: A) Nested all-null field crashes _backfill_missing_fields with TypeError because the recursive call doesn't guard against pa.null() columns. B) Top-level all-null struct column is silently skipped by _align_struct_fields (pre-existing), causing pa.concat_tables to fail in default promote mode. C) _reconcile_field step 4 (null-list reconciliation) iterates the unfiltered type list, so a pa.null() arm is returned instead of the concrete list type (pre-existing). Root cause of (C): the parameter named non_null_types actually received pa.null() entries from the caller. Fix by filtering pa.null() once at function entry, renaming the parameter to field_types, and simplifying the struct branch to use all(is_struct(...)). Signed-off-by: dragongu <andrewgu@vip.qq.com>
16f77c1 to
baae2d0
Compare
Address review feedback on the null-handling fix: * ``_reconcile_diverging_fields`` used to reconcile a field on the fly and then skip every later schema for that field. Combined with the early return in ``_reconcile_field`` (a single non-null type is already the complete result), a field could be locked before a later schema was inspected: ``[struct, null, int64]`` silently unified to ``struct`` instead of raising, and a null arm could shadow a later concrete type. Collect all field types first, reconcile once afterwards. * ``_align_struct_fields`` no longer duplicates the null/struct handling that ``_backfill_missing_fields`` already implements; it now delegates every mismatched column, which also means all-null columns are aligned explicitly instead of relying on PyArrow's promote mode. Tests: * ``test_unify_schemas_rejects_late_struct_primitive_mix`` pins that a later incompatible type is not hidden by an earlier null arm. * ``test_align_struct_fields_top_level_non_struct_field`` pins the guard for a non-struct column aligned against a struct schema. * ``test_unify_schemas_null_list_reconciled_when_pyarrow_cannot_unify`` covers null-list reconciliation when another field forces the reconciliation path (PyArrow >= 17 unifies the single-field case on its own, so it never reached ``_reconcile_field`` before). Signed-off-by: dragongu <andrewgu@vip.qq.com>
Review nit: comments were dropped from code whose behaviour did not change. Restore them and clarify the one branch whose rationale is not obvious: * ``# Find first non-null list type`` in ``_reconcile_field`` step 4. * ``# Check if the column type matches a struct type`` / ``# Align struct fields`` / ``# Replace the column with the aligned version`` in ``_align_struct_fields``. * Spell out why ``_backfill_missing_fields`` handles all-null columns explicitly rather than relying on PyArrow's promote mode. Signed-off-by: dragongu <andrewgu@vip.qq.com>
baae2d0 to
07f79f8
Compare

Description
Fix null-type handling in Arrow schema reconciliation and struct alignment.
A nested field that is all-null in one block and a struct in another crashes
_backfill_missing_fieldswithTypeError: 'pyarrow.lib.DataType' object is not iterable. This PR fixes the nested case and one pre-existing bug.Bug A — Nested all-null field crashes alignment (the original fix)
_align_struct_fieldsguards top-level columns withisinstance(column.type, pa.StructType), but its recursive call only inspects the target field type, so a nestedpa.null()column slips through.Bug B —
_reconcile_fieldstep 4 returnspa.null()instead of list type (pre-existing)The
non_null_typesparameter actually receivedpa.null()entries from the caller. Step 3 (struct reconciliation) had its own per-step null filter, but step 4 (null-list reconciliation) iterated the unfiltered list. When the input was[pa.null(), pa.list_(pa.null()), pa.list_(pa.int64())], step 4 returnedpa.null()instead ofpa.list_(pa.int64()).Fixed by filtering
pa.null()once at the top of_reconcile_fieldinstead of per-step, and renaming the parameter fromnon_null_typestofield_typesto reflect its actual contract.Reproduction script
Save as
repro.pyand run onmainbefore this PR to see both bugs. After applying this PR both printOK.