Skip to content

Commit a3ed51d

Browse files
fix: raise when binding starts-with with an out-of-range literal
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 <126076773+Shahzeb-DataEngineer@users.noreply.github.com>
1 parent e3fbd94 commit a3ed51d

2 files changed

Lines changed: 18 additions & 0 deletions

File tree

‎pyiceberg/expressions/__init__.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,11 +915,15 @@ def bind(self, schema: Schema, case_sensitive: bool = True) -> BoundLiteralPredi
915915
return AlwaysTrue()
916916
elif isinstance(self, (GreaterThan, GreaterThanOrEqual, EqualTo)):
917917
return AlwaysFalse()
918+
else:
919+
raise ValueError(f"Cannot bind {type(self).__name__} with AboveMax literal")
918920
elif isinstance(lit, BelowMin):
919921
if isinstance(self, (GreaterThan, GreaterThanOrEqual, NotEqualTo)):
920922
return AlwaysTrue()
921923
elif isinstance(self, (LessThan, LessThanOrEqual, EqualTo)):
922924
return AlwaysFalse()
925+
else:
926+
raise ValueError(f"Cannot bind {type(self).__name__} with BelowMin literal")
923927

924928
return self.as_bound(bound_term, lit) # type: ignore
925929

‎tests/expressions/test_expressions.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,20 @@ def test_empty_bind_not_in(table_schema_simple: Schema) -> None:
295295
assert bound == AlwaysTrue()
296296

297297

298+
@pytest.mark.parametrize(
299+
"expression, match",
300+
[
301+
(StartsWith("bar", "2147483648"), "AboveMax"),
302+
(NotStartsWith("bar", "2147483648"), "AboveMax"),
303+
(StartsWith("bar", "-2147483649"), "BelowMin"),
304+
(NotStartsWith("bar", "-2147483649"), "BelowMin"),
305+
],
306+
)
307+
def test_bind_starts_with_out_of_range_literal(table_schema_simple: Schema, expression: BooleanExpression, match: str) -> None:
308+
with pytest.raises(ValueError, match=match):
309+
expression.bind(table_schema_simple)
310+
311+
298312
def test_bind_not_in_equal_term(table_schema_simple: Schema) -> None:
299313
bound = BoundNotIn(
300314
BoundReference(table_schema_simple.find_field(1), table_schema_simple.accessor_for_field(1)), {literal("hello")}

0 commit comments

Comments
 (0)