You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by dr...@apache.org on 2026/04/22 20:03:00 UTC

(iceberg-python) branch main updated: perf: build partition filter with balanced tree to avoid RecursionError (#3264)

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

geruh 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 df258f59c perf: build partition filter with balanced tree to avoid RecursionError (#3264)
df258f59c is described below

commit df258f59c1820958a40f3de3ffb83e57c139d558
Author: Yingjian Wu <15...@users.noreply.github.com>
AuthorDate: Wed Apr 22 13:02:53 2026 -0700

    perf: build partition filter with balanced tree to avoid RecursionError (#3264)
---
 pyiceberg/table/__init__.py | 24 ++++++++++++------------
 tests/table/test_init.py    | 13 +++++++++++++
 2 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/pyiceberg/table/__init__.py b/pyiceberg/table/__init__.py
index bb8765b65..7f1524642 100644
--- a/pyiceberg/table/__init__.py
+++ b/pyiceberg/table/__init__.py
@@ -367,20 +367,20 @@ class Transaction:
             A predicate matching any of the input partition records.
         """
         partition_fields = [schema.find_field(field.source_id).name for field in spec.fields]
+        if not partition_records or not partition_fields:
+            return AlwaysFalse()
 
-        expr: BooleanExpression = AlwaysFalse()
+        per_record_exprs: list[BooleanExpression] = []
         for partition_record in partition_records:
-            match_partition_expression: BooleanExpression = AlwaysTrue()
-
-            for pos, partition_field in enumerate(partition_fields):
-                predicate = (
-                    EqualTo(Reference(partition_field), partition_record[pos])
-                    if partition_record[pos] is not None
-                    else IsNull(Reference(partition_field))
-                )
-                match_partition_expression = And(match_partition_expression, predicate)
-            expr = Or(expr, match_partition_expression)
-        return expr
+            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)
+            ]
+            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 _append_snapshot_producer(
         self, snapshot_properties: dict[str, str], branch: str | None = MAIN_BRANCH
diff --git a/tests/table/test_init.py b/tests/table/test_init.py
index 30c4a3a45..0c4ea258f 100644
--- a/tests/table/test_init.py
+++ b/tests/table/test_init.py
@@ -32,6 +32,7 @@ from pyiceberg.expressions import (
     EqualTo,
     In,
 )
+from pyiceberg.expressions.visitors import bind
 from pyiceberg.io import PY_IO_IMPL, load_file_io
 from pyiceberg.partitioning import PartitionField, PartitionSpec
 from pyiceberg.schema import Schema
@@ -90,6 +91,7 @@ from pyiceberg.transforms import (
     BucketTransform,
     IdentityTransform,
 )
+from pyiceberg.typedef import Record
 from pyiceberg.types import (
     BinaryType,
     BooleanType,
@@ -1753,3 +1755,14 @@ def test_check_uuid_passes_when_match(table_v2: Table, example_table_metadata_v2
     new_metadata = TableMetadataV2(**example_table_metadata_v2)
     # Should not raise with same uuid
     Table._check_uuid(table_v2.metadata, new_metadata)
+
+
+def test_build_large_partition_predicate(table_v2: Table) -> None:
+    with table_v2.transaction() as tx:
+        expr = tx._build_partition_predicate(
+            partition_records={Record(i) for i in range(5000)},
+            spec=table_v2.metadata.spec(),
+            schema=table_v2.metadata.schema(),
+        )
+
+    bind(table_v2.metadata.schema(), expr, case_sensitive=True)