Skip to content

Add strict NotEqualTo/NotIn null and NaN tests#3547

Merged
geruh merged 1 commit into
apache:mainfrom
kevinjqliu:kevinjqliu/codex-strict-metrics-not-eq-not-in
Jun 22, 2026
Merged

Add strict NotEqualTo/NotIn null and NaN tests#3547
geruh merged 1 commit into
apache:mainfrom
kevinjqliu:kevinjqliu/codex-strict-metrics-not-eq-not-in

Conversation

@kevinjqliu

@kevinjqliu kevinjqliu commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Follow-up to #3521 for #3498.

This PR keeps the implementation from #3521 and tightens the strict metrics regression coverage:

  • Replace the singleton partial-null regression test with branch-focused mixed-null and all-null cases.
  • Add equivalent mixed-NaN and all-NaN coverage for float and double columns.
  • Use multi-value NotIn literals so the tests exercise visit_not_in instead of normalizing to NotEqualTo.

Why

#3521 fixed the strict metrics over-pruning bug by only short-circuiting negative predicates when a column contains only nulls or only NaNs. These tests lock in that boundary: mixed null/NaN counts must continue to bounds checks, while all-null/all-NaN counts can still prove ROWS_MUST_MATCH.

Java Parity

This follows Java StrictMetricsEvaluator, where negative predicates short-circuit only for all-null/all-NaN columns:

Validation

  • UV_CACHE_DIR=.cache/uv PYTHON_GIL=1 PYTHONPATH=. uv run pytest tests/expressions/test_evaluator.py -k "mixed_nulls_and_matching_bounds or all_nulls or mixed_nans_and_matching_bounds or all_nans or strict_integer_not_in"
  • UV_CACHE_DIR=.cache/uv PYTHON_GIL=1 PYTHONPATH=. uv run pytest tests/expressions/test_evaluator.py
  • UV_CACHE_DIR=.cache/uv PYTHON_GIL=1 PYTHONPATH=. uv run prek run -a
  • git diff --check

@rambleraptor

rambleraptor commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

This looks the same fix as #3521


should_read = _StrictMetricsEvaluator(strict_data_file_schema, NotIn("some_nulls", {"abc", "def"})).eval(strict_data_file_1)
assert should_read, "Should match: notIn on some nulls column, 'bbb' > 'abc' and 'bbb' < 'def'"
assert not should_read, "Should not match: mixed-null notIn cannot be proven when bounds are missing"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some_nulls has value_count = 50 and null_count = 10

So 40 values are non-null, but without lower/upper bounds the strict evaluator cannot rule out "abc" or "def" among them.

Before the fix, “column can contain nulls” incorrectly caused ROWS_MUST_MATCH; now only “column contains nulls only” can do that.

@kevinjqliu kevinjqliu changed the title [codex] Fix strict NotEqualTo and NotIn metrics with nulls and NaNs Fix strict NotEqualTo and NotIn metrics with nulls and NaNs Jun 22, 2026
@kevinjqliu kevinjqliu force-pushed the kevinjqliu/codex-strict-metrics-not-eq-not-in branch 2 times, most recently from fa29f84 to 43860b7 Compare June 22, 2026 01:45
)

should_read = _StrictMetricsEvaluator(schema, NotEqualTo("x", 5)).eval(data_file)
assert should_read == ROWS_MIGHT_NOT_MATCH, "Should not match: bounds prove the non-null value is 5"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value_count = 2 and null_count = 1, so there is 1 non-null value. Bounds [5..5] mean the non-null value is 5, so NotEqualTo("x", 5) / NotIn("x", {5, 6}) cannot match every row.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like something that would be great as a code comment :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is restating the logic in the test

nan_value_counts=None,
)

should_read = _StrictMetricsEvaluator(schema, NotEqualTo("x", 5)).eval(data_file)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value_count = 2 and null_count = 2, so all values are null. That means every row matches NotEqualTo("x", 5) / NotIn("x", {5, 6}).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code comment :)

upper_bounds={1: to_bytes(field_type, 5.0)},
)

should_read = _StrictMetricsEvaluator(schema, NotEqualTo("x", 5.0)).eval(data_file)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value_count = 2 and nan_count = 1, so there is 1 non-NaN value. Bounds [5.0..5.0] mean the non-NaN value is 5.0, so NotEqualTo("x", 5.0) / NotIn("x", {5.0, 6.0}) cannot match every row.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Weird, my comments didn't show up in the right place? Anyways, you wrote out some really useful explanations here and we should have these as code comments.

nan_value_counts={1: 2},
)

should_read = _StrictMetricsEvaluator(schema, NotEqualTo("x", 5.0)).eval(data_file)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value_count = 2 and nan_count = 2, so all values are NaN. That means every row matches NotEqualTo("x", 5.0) / NotIn("x", {5.0, 6.0}).

@kevinjqliu

Copy link
Copy Markdown
Contributor Author

This looks the same fix as #3521

thanks! i didnt see that one

@kevinjqliu kevinjqliu force-pushed the kevinjqliu/codex-strict-metrics-not-eq-not-in branch from 43860b7 to 104dfda Compare June 22, 2026 04:14
@kevinjqliu kevinjqliu changed the title Fix strict NotEqualTo and NotIn metrics with nulls and NaNs Add strict NotEqualTo/NotIn null and NaN tests Jun 22, 2026
assert not should_read, "Should not match: no_nulls field does not have bounds"


def test_strict_not_eq_partial_nulls_within_bounds() -> None:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed test_strict_not_eq_partial_nulls_within_bounds because the new branch-focused tests cover the same partial-null boundary more directly, and also add the missing all-null, mixed-NaN, and all-NaN cases. The removed test also used singleton NotIn("x", {5}), which normalizes to NotEqualTo("x", 5) and therefore did not exercise the strict visit_not_in branch.

@kevinjqliu kevinjqliu force-pushed the kevinjqliu/codex-strict-metrics-not-eq-not-in branch from 104dfda to a595909 Compare June 22, 2026 04:19
@kevinjqliu kevinjqliu marked this pull request as ready for review June 22, 2026 04:20

@rambleraptor rambleraptor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests look great!

)

should_read = _StrictMetricsEvaluator(schema, NotEqualTo("x", 5)).eval(data_file)
assert should_read == ROWS_MIGHT_NOT_MATCH, "Should not match: bounds prove the non-null value is 5"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like something that would be great as a code comment :)

nan_value_counts=None,
)

should_read = _StrictMetricsEvaluator(schema, NotEqualTo("x", 5)).eval(data_file)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code comment :)

@geruh geruh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@geruh geruh merged commit 8c7912f into apache:main Jun 22, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants