Skip to content

PyArrow: Support view types when writing to a partitioned table - #4121

Open
DG47 wants to merge 2 commits into
apache:mainfrom
DG47:fix/partitioned-write-view-types
Open

DG47 wants to merge 2 commits into
apache:mainfrom
DG47:fix/partitioned-write-view-types

Conversation

@DG47

@DG47 DG47 commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Closes #4120

Rationale for this change

string_view and binary_view columns are accepted on write, and appending them to an unpartitioned table works. Appending them to a partitioned table fails in _determine_partitions, because PyArrow has no group by, filter or take kernels for view types, and the pyiceberg-core bucket and truncate transforms do not accept them either. pa.table(polars_df) produces exactly this shape, since Polars exports strings and binaries as view types over the PyCapsule interface.

Depending on the spec, the append fails with Keys of type string_view, Function 'array_filter' has no kernel matching input types (string_view, bool), or Unsupported data type for bucket transform: Utf8View.

This change casts view types, including ones nested in structs, lists and maps, to large_string / large_binary before the table is split into partitions. Tables without view types are not cast. The data files are written the same as for large_string / large_binary input.

One remaining gap is outside this repo: truncate on a binary column already fails for large_binary input in pyiceberg-core (apache/iceberg-rust#3119), and now binary_view input hits that same error.

Are these changes tested?

Yes:

  • test_determine_partitions_with_view_types: identity, bucket and truncate partitions on a string_view column, with binary_view and nested view columns alongside.
  • test_append_view_types_to_partitioned_table: end-to-end append to a partitioned in-memory table, then read back.

Both fail on main and pass with this change, on pyarrow 25.0.1 and on the minimum supported pyarrow 18.1.0.

Are there any user-facing changes?

Yes. Appending, overwriting, and dynamically overwriting partitioned tables with Arrow view type columns now works where it used to raise.

Appending an Arrow table with string_view or binary_view columns, which
pa.table() produces from a Polars DataFrame, worked for unpartitioned
tables but failed for partitioned ones: PyArrow has no group by, filter
or take kernels for view types, and pyiceberg-core's bucket and truncate
transforms reject them. Cast view types, including nested ones, to their
large counterparts before splitting the table into partitions.
Comment thread pyiceberg/io/pyarrow.py Outdated
return pc.field(partition_field_name) == value


def _replace_view_types(arrow_type: pa.DataType) -> pa.DataType:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @DG47 this makes sense to me. Instead of building this recursive function ourself, we can also implement this using the PyArrowSchemaVisitor:

class PyArrowSchemaVisitor(Generic[T], ABC):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, done in 75c2fe0. The recursion is gone; _determine_partitions now calls visit_pyarrow(arrow_table.schema, _ConvertViewTypes()), and the visitor lives next to _ConvertToLargeTypes. Unlike that one it keeps the list flavour, nested field names, nullability and metadata, so the cast is a pure view-to-large rewrite. One wrinkle: visit_pyarrow unwraps dictionary types before calling primitive, so the visitor re-applies the dictionary encoding in field, list and map to leave dictionary columns untouched.

This branch has not been deployed

No deployments
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.

Appending string_view / binary_view data to a partitioned table fails

2 participants