This is an automated email from the ASF dual-hosted git repository.

kevinjqliu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-python.git


The following commit(s) were added to refs/heads/main by this push:
     new af17de8d fix: Allow update_column to change required for list elements 
and map values (#2798)
af17de8d is described below

commit af17de8de335f1b2ac65e90b4ef690e6be493c18
Author: Somasundaram Sekar <[email protected]>
AuthorDate: Sun Jan 25 19:58:39 2026 +0100

    fix: Allow update_column to change required for list elements and map 
values (#2798)
    
    ## Summary
    - Fixes #2786: `update_schema().update_column()` was not updating the
    `required` property for list elements or map values
    - Modified `_ApplyChanges.list()` to check `self._updates` for element's
    required property
    - Modified `_ApplyChanges.map()` to check `self._updates` for value's
    required property
    - Added 2 unit tests for list element and map value required updates
    
    ## Test plan
    - [x] All 3032 unit tests pass
    - [x] Lint passes
    - [x] New tests cover: updating list element required, updating map
    value required
    
    Closes #2786
    
    Co-authored-by: Somasundaram Sekar <[email protected]>
---
 pyiceberg/table/update/schema.py | 12 ++++++++--
 tests/table/test_init.py         | 48 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+), 2 deletions(-)

diff --git a/pyiceberg/table/update/schema.py b/pyiceberg/table/update/schema.py
index c2d99f69..828f1e87 100644
--- a/pyiceberg/table/update/schema.py
+++ b/pyiceberg/table/update/schema.py
@@ -805,7 +805,11 @@ class _ApplyChanges(SchemaVisitor[IcebergType | None]):
         if element_type is None:
             raise ValueError(f"Cannot delete element type from list: 
{element_result}")
 
-        return ListType(element_id=list_type.element_id, element=element_type, 
element_required=list_type.element_required)
+        element_required = list_type.element_required
+        if update := self._updates.get(list_type.element_id):
+            element_required = update.required
+
+        return ListType(element_id=list_type.element_id, element=element_type, 
element_required=element_required)
 
     def map(self, map_type: MapType, key_result: IcebergType | None, 
value_result: IcebergType | None) -> IcebergType | None:
         key_id: int = map_type.key_field.field_id
@@ -827,12 +831,16 @@ class _ApplyChanges(SchemaVisitor[IcebergType | None]):
         if value_type is None:
             raise ValueError(f"Cannot delete value type from map: 
{value_field}")
 
+        value_required = map_type.value_required
+        if update := self._updates.get(map_type.value_id):
+            value_required = update.required
+
         return MapType(
             key_id=map_type.key_id,
             key_type=map_type.key_type,
             value_id=map_type.value_id,
             value_type=value_type,
-            value_required=map_type.value_required,
+            value_required=value_required,
         )
 
     def primitive(self, primitive: PrimitiveType) -> IcebergType | None:
diff --git a/tests/table/test_init.py b/tests/table/test_init.py
index d41badc5..5487008c 100644
--- a/tests/table/test_init.py
+++ b/tests/table/test_init.py
@@ -509,6 +509,54 @@ def test_add_nested_list_type_column(table_v2: Table) -> 
None:
     assert new_schema.highest_field_id == 7
 
 
+def test_update_list_element_required(table_v2: Table) -> None:
+    """Test that update_column can change list element's required property."""
+    # Add a list column with optional elements
+    update = UpdateSchema(transaction=table_v2.transaction())
+    list_type = ListType(element_id=1, element_type=StringType(), 
element_required=False)
+    update.add_column(path="tags", field_type=list_type)
+    schema_with_list = update._apply()
+
+    # Verify initial state
+    field = schema_with_list.find_field("tags")
+    assert isinstance(field.field_type, ListType)
+    assert field.field_type.element_required is False
+
+    # Update element to required
+    update2 = UpdateSchema(transaction=table_v2.transaction(), 
schema=schema_with_list)
+    update2._allow_incompatible_changes = True  # Allow optional -> required
+    new_schema = update2.update_column(("tags", "element"), 
required=True)._apply()
+
+    # Verify the update
+    field = new_schema.find_field("tags")
+    assert isinstance(field.field_type, ListType)
+    assert field.field_type.element_required is True
+
+
+def test_update_map_value_required(table_v2: Table) -> None:
+    """Test that update_column can change map value's required property."""
+    # Add a map column with optional values
+    update = UpdateSchema(transaction=table_v2.transaction())
+    map_type = MapType(key_id=1, key_type=StringType(), value_id=2, 
value_type=IntegerType(), value_required=False)
+    update.add_column(path="metadata", field_type=map_type)
+    schema_with_map = update._apply()
+
+    # Verify initial state
+    field = schema_with_map.find_field("metadata")
+    assert isinstance(field.field_type, MapType)
+    assert field.field_type.value_required is False
+
+    # Update value to required
+    update2 = UpdateSchema(transaction=table_v2.transaction(), 
schema=schema_with_map)
+    update2._allow_incompatible_changes = True  # Allow optional -> required
+    new_schema = update2.update_column(("metadata", "value"), 
required=True)._apply()
+
+    # Verify the update
+    field = new_schema.find_field("metadata")
+    assert isinstance(field.field_type, MapType)
+    assert field.field_type.value_required is True
+
+
 def test_apply_set_properties_update(table_v2: Table) -> None:
     base_metadata = table_v2.metadata
 

Reply via email to