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

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-4144-9345369e8e7411e9351313bda2501df33b99c311
in repository https://gitbox.apache.org/repos/asf/iceberg-python.git

commit 0a2e7caa65502be3d5090bc053292439c2253cf6
Author: Shahzeb Ali <[email protected]>
AuthorDate: Tue Oct 27 23:04:53 2026 +0000

    fix: raise when binding starts-with with an out-of-range literal (#4144)
    
    LiteralPredicate.bind folded AboveMax/BelowMin literals to AlwaysTrue or
    AlwaysFalse for the comparison operations, but starts-with and
    not-starts-with fell through and built a bound predicate wrapping the
    sentinel itself, which then crashed evaluators. Raise a ValueError at
    bind time instead, mirroring the Java implementation, which throws for
    operations that have no defined fold.
    
    Signed-off-by: Shahzeb Ali 
<[email protected]>
---
 pyiceberg/expressions/__init__.py     |  4 ++++
 tests/expressions/test_expressions.py | 14 ++++++++++++++
 2 files changed, 18 insertions(+)

diff --git a/pyiceberg/expressions/__init__.py 
b/pyiceberg/expressions/__init__.py
index 1fb9810ff..ad916171c 100644
--- a/pyiceberg/expressions/__init__.py
+++ b/pyiceberg/expressions/__init__.py
@@ -916,11 +916,15 @@ class LiteralPredicate(UnboundPredicate, ABC):
                 return AlwaysTrue()
             elif isinstance(self, (GreaterThan, GreaterThanOrEqual, EqualTo)):
                 return AlwaysFalse()
+            else:
+                raise ValueError(f"Cannot bind {type(self).__name__} with 
AboveMax literal")
         elif isinstance(lit, BelowMin):
             if isinstance(self, (GreaterThan, GreaterThanOrEqual, NotEqualTo)):
                 return AlwaysTrue()
             elif isinstance(self, (LessThan, LessThanOrEqual, EqualTo)):
                 return AlwaysFalse()
+            else:
+                raise ValueError(f"Cannot bind {type(self).__name__} with 
BelowMin literal")
 
         return self.as_bound(bound_term, lit)  # type: ignore
 
diff --git a/tests/expressions/test_expressions.py 
b/tests/expressions/test_expressions.py
index 8ce48a689..5a33db254 100644
--- a/tests/expressions/test_expressions.py
+++ b/tests/expressions/test_expressions.py
@@ -295,6 +295,20 @@ def test_empty_bind_not_in(table_schema_simple: Schema) -> 
None:
     assert bound == AlwaysTrue()
 
 
[email protected](
+    "expression, match",
+    [
+        (StartsWith("bar", "2147483648"), "AboveMax"),
+        (NotStartsWith("bar", "2147483648"), "AboveMax"),
+        (StartsWith("bar", "-2147483649"), "BelowMin"),
+        (NotStartsWith("bar", "-2147483649"), "BelowMin"),
+    ],
+)
+def test_bind_starts_with_out_of_range_literal(table_schema_simple: Schema, 
expression: BooleanExpression, match: str) -> None:
+    with pytest.raises(ValueError, match=match):
+        expression.bind(table_schema_simple)
+
+
 def test_bind_not_in_equal_term(table_schema_simple: Schema) -> None:
     bound = BoundNotIn(
         BoundReference(table_schema_simple.find_field(1), 
table_schema_simple.accessor_for_field(1)), {literal("hello")}

Reply via email to