ptimizeroracle opened a new pull request, #3921:
URL: https://github.com/apache/iceberg-python/pull/3921

   # Rationale for this change
   
   `_StrictMetricsEvaluationVisitor.visit_not_in` returns a false 
`ROWS_MUST_MATCH` when the
   upper bound of a float column is NaN.
   
   `NaN >= val` is `False` for every value, so the `upper >= val` filter drops 
every literal
   from the set and the empty set is read as proof that no row can match. But a 
NaN upper
   bound only means NaN sorts greatest in the writer's min/max: the file can 
still contain
   non-NaN values that are in the literal set.
   
   Reproducer (offline, no catalog):
   
   ```python
   schema = Schema(NestedField(1, "x", DoubleType(), required=False))
   data_file = DataFile.from_args(
       file_path="file.parquet",
       file_format=FileFormat.PARQUET,
       partition={},
       record_count=2,
       file_size_in_bytes=1,
       value_counts={1: 2},
       null_value_counts={1: 0},
       nan_value_counts={1: 1},
       lower_bounds={1: to_bytes(DoubleType(), 1.0)},
       upper_bounds={1: to_bytes(DoubleType(), float("nan"))},
   )
   
   _StrictMetricsEvaluator(schema, NotIn("x", {1.0, 2.0})).eval(data_file)
   # main: True (ROWS_MUST_MATCH)
   # fixed: False (ROWS_MIGHT_NOT_MATCH)
   ```
   
   The file describes a column `{1.0, NaN}`. Evaluating the predicate on the 
actual rows
   with `expression_evaluator` gives `[False, True]`: the row `1.0` does not 
match
   `NotIn("x", {1.0, 2.0})`, so `ROWS_MUST_MATCH` is impossible.
   
   This is not just a wrong pruning verdict. `_DeleteFiles._compute_deletes`
   (pyiceberg/table/update/snapshot.py:612) drops a whole data file when the 
strict
   evaluator returns `ROWS_MUST_MATCH`, so a `Table.delete(...)` with a `NotIn` 
filter can
   delete rows that should be kept.
   
   Java parity: `StrictEvalVisitor.notIn`
   
(api/src/main/java/org/apache/iceberg/expressions/StrictEvalVisitor.java:358-383)
   guards the lower bound explicitly with `NaNUtil.isNaN(lower)` and its 
comparator orders
   NaN greatest, so a NaN upper bound never excludes literals there. The Python 
port
   already mirrors the lower-bound guard (visitors.py:1774); this adds the 
missing
   upper-bound guard with the same comment, matching the Java docs note that 
NaN bounds
   are unreliable when the column can contain non-NaN data.
   
   Same genre as #3891 (NaN/null-bound handling in the metrics evaluators).
   
   ## Are these changes tested?
   
   Yes. `test_strict_not_in_with_nan_upper_bound` in 
tests/expressions/test_evaluator.py,
   parametrized over FloatType/DoubleType, modeled on the neighboring
   `test_strict_not_equal_and_not_in_with_mixed_nans_and_matching_bounds`. It 
fails on
   main and passes with the fix. Full unit suite: 4019 passed, 3 skipped.
   
   ## Are there any user-facing changes?
   
   Yes, bug fix: `Table.delete` with a `NotIn` row filter no longer drops data 
files whose
   float column has a NaN upper bound and partially-matching rows. No API 
changes.
   


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