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


##########
pyiceberg/expressions/__init__.py:
##########
@@ -697,8 +697,13 @@ def __init__(
 
     def bind(self, schema: Schema, case_sensitive: bool = True) -> 
BoundSetPredicate:
         bound_term = self.term.bind(schema, case_sensitive)
-        literal_set = self.literals
-        return self.as_bound(bound_term, 
{lit.to(bound_term.ref().field.field_type) for lit in literal_set})  # type: 
ignore
+        field_type = bound_term.ref().field.field_type
+        # Literals outside the field's range can never match, so drop them 
rather
+        # than keep the clamped AboveMax/BelowMin sentinel in the bound set
+        bound_literals = {lit.to(field_type) for lit in self.literals}
+        return self.as_bound(  # type: ignore
+            bound_term, {lit for lit in bound_literals if not isinstance(lit, 
(AboveMax, BelowMin))}
+        )

Review Comment:
   `bound_literals = {lit.to(field_type) for lit in self.literals}` builds a 
set *before* filtering out `AboveMax`/`BelowMin`. Because `Literal.__eq__` 
compares only by `.value`, an out-of-range sentinel (value == max/min) can be 
considered equal to a user-provided boundary literal, and whichever is inserted 
first “wins” in the set. If the sentinel wins, the subsequent filter drops it 
and accidentally removes a legitimate literal (e.g., `In("id", 
[IntegerType.max, IntegerType.max + 1])` could lose `IntegerType.max`). Filter 
out sentinels *before* inserting into the set (or build the set via a loop) to 
avoid this collision.



##########
tests/expressions/test_evaluator.py:
##########
@@ -1907,3 +1910,30 @@ def test_strict_metrics_eval_bounds_after_promotion(
 
     evaluator = _StrictMetricsEvaluator(schema, op("col", lit))
     assert evaluator.eval(data_file) == expected
+
+
+def test_above_int_bounds_in() -> None:
+    schema = Schema(NestedField(1, "id", IntegerType(), required=False))
+    above_max = IntegerType.max + 1
+
+    assert In("id", [1, above_max]).bind(schema) == EqualTo("id", 
1).bind(schema)
+    assert NotIn("id", [1, above_max]).bind(schema) == NotEqualTo("id", 
1).bind(schema)
+    assert In("id", [above_max]).bind(schema) == AlwaysFalse()
+    assert NotIn("id", [above_max]).bind(schema) == AlwaysTrue()
+
+    # The clamped literal used to match the field's maximum
+    assert expression_evaluator(schema, In("id", [1, above_max]), 
True)(Record(IntegerType.max)) is False
+    assert expression_evaluator(schema, NotIn("id", [1, above_max]), 
True)(Record(IntegerType.max)) is True

Review Comment:
   These tests cover the pure out-of-range case, but they don’t cover the 
important edge case where the user includes the boundary value *and* an 
out-of-range value (e.g., `[IntegerType.max, IntegerType.max + 1]`). That 
scenario can regress if out-of-range sentinels are de-duplicated against the 
real boundary literal during binding; adding an assertion here would lock in 
the intended behavior.
   
   This issue also appears on line 1931 of the same file.



-- 
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