Skip to content

Fix null list of structs being read and written as an empty list - #3834

Open
slachiewicz wants to merge 2 commits into
apache:mainfrom
slachiewicz:fix-null-list-of-struct
Open

Fix null list of structs being read and written as an empty list#3834
slachiewicz wants to merge 2 commits into
apache:mainfrom
slachiewicz:fix-null-list-of-struct

Conversation

@slachiewicz

Copy link
Copy Markdown
Member

A null list<struct<...>> was rebuilt as an empty list, losing the distinction
between the two. test_null_list_and_map has documented this since #252, asserting
the corrupted value with the correct assertion commented out pending
apache/arrow#38809.

That dependency turns out not to hold for this symptom: from_arrays takes a mask
argument, so carrying the validity bitmap over fixes the null loss on its own. The
rebuild itself is still needed for #38809 and is unchanged.

The write path is affected too, and there the loss is permanent — the Parquet file
pyiceberg produces contains an empty list, so no reader can tell it from a null. The
new unit test covers that direction; the integration assertion covers the read side.

Closes #3833, which has the full analysis and a standalone reproduction.

This change was created with AI assistance.

from_arrays() takes the offsets buffer alone, which cannot express a null
list, so rebuilding the array without a mask collapsed every null
list<struct<...>> into an empty one. On write that is unrecoverable: the
Parquet file itself then holds the empty list.

The rebuild still works around apache/arrow#38809 and stays; only the mask
is added. That restores the assertion test_null_list_and_map has carried
commented out since apache#252.

Closes apache#3833
@slachiewicz
slachiewicz marked this pull request as ready for review August 23, 2026 09:49
The comment claimed the branch could go once apache/arrow#38809 was
fixed. That is fixed in pyarrow 19, but the branch must stay: Iceberg
renames by field-id while cast matches element fields by name, so
dropping it nulls out any field renamed inside a list element.

Verified: the new test passes, and fails with the rebuild removed.
@slachiewicz

Copy link
Copy Markdown
Member Author

Pushed a second commit that removes the # This can be removed once this has been fixed: https://github.com/apache/arrow/issues/38809 comment above the rebuild, and adds a test for what the rebuild is actually load-bearing for.

The comment is wrong twice over. apache/arrow#38809 is fixed as of pyarrow 19.0.0 (apache/arrow#44587 taught the struct cast to fill absent output fields with null), and a cast really does now handle a field superset, reordering, widening and a null list element. But the rebuild still cannot go, because Iceberg renames by field-id while cast matches struct fields by name. Dropping the rebuild and letting the trailing list_array.cast(arrow_field) do the work silently nulls out any field renamed inside a list element:

with rebuild:    [[{'after': 1}], [], None, [{'after': 3}]]
rebuild removed: [[{'after': None}], [], None, [{'after': None}]]

That is test__to_requested_schema_renamed_field_in_list_of_structs, which fails without the rebuild. The null-list test alone does not catch it, since on pyarrow 25 the cast preserves the null on its own.

This comment was created with AI assistance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Null list<struct<...>> is written and read as an empty list

1 participant