Copilot commented on code in PR #3863:
URL: https://github.com/apache/iceberg-python/pull/3863#discussion_r3861774680


##########
tests/table/test_init.py:
##########
@@ -2036,3 +2040,71 @@ def _spy(*args: Any, **kwargs: Any) -> FileIO:
 
     assert seen_locations, "expected at least one load_file_io call"
     assert all(loc is not None for loc in seen_locations), f"load_file_io 
called without a location: {seen_locations}"
+
+
+def test_build_partition_predicate_with_evolved_fields(table_v2: Table) -> 
None:
+    tx = table_v2.transaction()
+    records = {Record("A", "us")}
+    fields = ["category", "region"]
+
+    # Without evolved fields
+    pred = tx._build_partition_predicate(records, fields)
+    assert pred == And(EqualTo(Reference("category"), "A"), 
EqualTo(Reference("region"), "us"))
+
+    # With evolved fields
+    pred_evolved = tx._build_partition_predicate(records, fields, 
evolved_fields={"region"})
+    assert pred_evolved == And(
+        EqualTo(Reference("category"), "A"),
+        Or(EqualTo(Reference("region"), "us"), IsNull(Reference("region"))),
+    )
+
+
+def test_dynamic_partition_overwrite_with_partition_spec_evolution(warehouse: 
Path) -> None:
+    import pyarrow as pa
+
+    from pyiceberg.catalog.sql import SqlCatalog
+
+    catalog = SqlCatalog(name="test", 
uri=f"sqlite:///{warehouse.as_posix()}/test_dpo_evolve.db", 
warehouse=f"file://{warehouse}")

Review Comment:
   `warehouse=f"file://{warehouse}"` produces an invalid/ambiguous file URI for 
absolute paths (it becomes `file:////tmp/...` because `str(Path)` starts with 
`/`). Use `warehouse.as_uri()` (or equivalent) to generate a correct 
`file:///...` URI so the catalog consistently resolves the warehouse location 
across platforms.



##########
pyiceberg/table/__init__.py:
##########
@@ -390,31 +395,61 @@ def _set_ref_snapshot(
 
         return updates, requirements
 
-    def _build_partition_predicate(self, partition_records: set[Record], 
partition_fields: list[str]) -> BooleanExpression:
+    def _build_partition_predicate(
+        self,
+        partition_records: set[Record],
+        partition_fields: list[str],
+        evolved_fields: set[str] | None = None,
+    ) -> BooleanExpression:
         """Build a filter predicate matching any of the input partition 
records.
 
         Args:
             partition_records: A set of partition records to match
             partition_fields: The field names to reference for each position 
in a partition record
+            evolved_fields: Optional set of field names added during partition 
spec evolution
 
         Returns:
             A predicate matching any of the input partition records.
         """
         if not partition_records or not partition_fields:
             return AlwaysFalse()
 
+        evolved = evolved_fields or set()
         per_record_exprs: list[BooleanExpression] = []
         for partition_record in partition_records:
-            predicates: list[BooleanExpression] = [
-                EqualTo(Reference(partition_field), partition_record[pos])
-                if partition_record[pos] is not None
-                else IsNull(Reference(partition_field))
-                for pos, partition_field in enumerate(partition_fields)
-            ]
+            predicates: list[BooleanExpression] = []
+            for pos, field in enumerate(partition_fields):
+                ref = Reference(field)
+                val = partition_record[pos]
+                if val is None:
+                    predicates.append(IsNull(ref))
+                elif field in evolved:
+                    predicates.append(Or(EqualTo(ref, val), IsNull(ref)))
+                else:
+                    predicates.append(EqualTo(ref, val))
+
             per_record_exprs.append(And(*predicates) if len(predicates) > 1 
else predicates[0])
 
         return Or(*per_record_exprs) if len(per_record_exprs) > 1 else 
per_record_exprs[0]
 
+    def _get_evolved_partition_fields(self, current_spec: PartitionSpec) -> 
set[str]:
+        """Find partition fields in the current spec that were absent in any 
historical partitioned spec."""

Review Comment:
   The docstring is a bit ambiguous: “absent in any historical” can be read as 
“absent in at least one historical spec” or “absent in all historical specs.” 
Since the implementation uses an intersection across historical specs (i.e., 
treats fields as evolved if they’re missing from at least one historical spec), 
consider clarifying the wording (e.g., “absent in at least one historical 
partitioned spec” / “not present in all historical partitioned specs”) to match 
the actual behavior.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to