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

github-merge-queue[bot] 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 945c8db4c fix(table): reuse partition field IDs on v3 tables (#4110)
945c8db4c is described below

commit 945c8db4c82b4fb1c2decc29109bf9630515b0d2
Author: Bishop Kammeraad <[email protected]>
AuthorDate: Sat Oct 24 21:42:14 2026 +0000

    fix(table): reuse partition field IDs on v3 tables (#4110)
    
    * fix(table): reuse partition field IDs on v3 tables
    
    UpdateSpec only looked up historical partition fields when the format
    version was exactly 2, so v3 tables got a fresh field ID when a removed
    partition field was added back. Java does this for format version >= 2.
    
    * test: cover removing and re-adding a partition field in one update
    
    Co-authored-by: Copilot App <[email protected]>
    
    ---------
    
    Co-authored-by: Kevin Liu <[email protected]>
    Co-authored-by: Copilot App <[email protected]>
---
 pyiceberg/table/update/spec.py |  2 +-
 tests/table/test_init.py       | 24 ++++++++++++++++++++++++
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/pyiceberg/table/update/spec.py b/pyiceberg/table/update/spec.py
index e060d6c26..5b55d96b1 100644
--- a/pyiceberg/table/update/spec.py
+++ b/pyiceberg/table/update/spec.py
@@ -268,7 +268,7 @@ class UpdateSpec(UpdateTableMetadata["UpdateSpec"]):
         return PartitionSpec(*partition_fields, spec_id=new_spec_id)
 
     def _partition_field(self, transform_key: tuple[int, Transform[Any, Any]], 
name: str | None) -> PartitionField:
-        if self._transaction.table_metadata.format_version == 2:
+        if self._transaction.table_metadata.format_version >= 2:
             source_id, transform = transform_key
             historical_fields = []
             for spec in self._transaction.table_metadata.specs().values():
diff --git a/tests/table/test_init.py b/tests/table/test_init.py
index 5ee5e4566..20acc870a 100644
--- a/tests/table/test_init.py
+++ b/tests/table/test_init.py
@@ -1479,6 +1479,30 @@ def test_last_assigned_partition_id(table_v2: Table) -> 
None:
         
AssertLastAssignedPartitionId(last_assigned_partition_id=1).validate(base_metadata)
 
 
[email protected]("table", [lf("table_v2"), lf("table_v3")])
+def test_update_spec_reuses_removed_partition_field_id(table: Table) -> None:
+    txn = table.transaction()
+    txn.update_spec().remove_field("x").add_field("y", BucketTransform(16), 
"y_bucket").commit()
+    txn.update_spec().add_identity("x").commit()
+
+    assert txn.table_metadata.spec().fields == (
+        PartitionField(source_id=2, field_id=1001, 
transform=BucketTransform(16), name="y_bucket"),
+        PartitionField(source_id=1, field_id=1000, 
transform=IdentityTransform(), name="x"),
+    )
+    assert txn.table_metadata.last_partition_id == 1001
+
+
[email protected]("table", [lf("table_v2"), lf("table_v3")])
+def test_update_spec_remove_and_add_same_field_reuses_id(table: Table) -> None:
+    txn = table.transaction()
+    txn.update_spec().remove_field("x").add_identity("x").commit()
+
+    assert txn.table_metadata.spec().fields == (
+        PartitionField(source_id=1, field_id=1000, 
transform=IdentityTransform(), name="x"),
+    )
+    assert txn.table_metadata.last_partition_id == 1000
+
+
 def test_assert_default_spec_id(table_v2: Table) -> None:
     base_metadata = table_v2.metadata
     
AssertDefaultSpecId(default_spec_id=base_metadata.default_spec_id).validate(base_metadata)

Reply via email to