test: rewrite scalar expression filtering tests with eval ground truth and bug regression (#48450)

## Summary
- Rewrite `test_milvus_client_scalar_filtering.py` (2156→1567 lines)
with eval-based ground truth, deterministic boundary values, and
comprehensive operator/type coverage
- Add `NullValue` sentinel class in `common_func.py` for SQL NULL 3VL
semantics (reusable across test files)
- 4 focused test classes replacing 1 monolithic class: Correctness,
IndexConsistency, CornerCase (bug regression), JSON

## Why
The original test had fundamental gaps discovered while analyzing real
bugs:
- **No boundary values** (INT64_MAX/MIN, 2^53) → missed #48440
(overflow)
- **No ground truth** (only self-consistency) → couldn't detect wrong
results from all code paths
- **Hand-written validators** with incorrect NULL handling → false
passes
- **No NOT+AND+OR compound coverage** → missed #48441 (3VL bitmap)
- **No bool literal / mixed-type IN tests** → missed #48443, #48442

## What's covered now

**Type coverage**: INT8/16/32/64, FLOAT, DOUBLE, BOOL, VARCHAR, JSON,
ARRAY(INT8/16/32/64/FLOAT/DOUBLE/BOOL/VARCHAR)

**Operator coverage**: `==, !=, >, <, >=, <=, IN, NOT IN, LIKE (12
patterns), IS NULL, IS NOT NULL, +, -, *, /, %, **, array_contains,
array_contains_all, array_contains_any, array_length, json path,
json_contains`

**Compound expressions**: AND, OR, NOT, &&, ||, NOT+AND, NOT+OR, triple
nesting, DeMorgan, double NOT, 3-way AND/OR, cross-field, range patterns

**Index consistency**: INVERTED, BITMAP, STL_SORT, TRIE — verified with
both cross-index comparison AND ground truth

**Bug regression (L0)**: #48440 overflow (+,-,*), #48441 3VL
NOT+nullable (incl. all-NULL segment), #48442 JSON mixed-type IN
precision, #48443 bool literal parser

## Test results (master-20260318-3ec434d)
```
37 tests: 32 passed, 5 failed (all real Milvus bugs)
L0+L1 (19 tests, -n 4): 37 seconds
```

| Failed test | Issue |
|---|---|
| test_int64_overflow_addition | #48440 |
| test_int64_overflow_subtraction | #48440 |
| test_3vl_not_all_null_segment | #48441 |
| test_bool_literal_in_logical_expr | #48443 |
| test_json_mixed_type_in_precision | #48442 |

## Tag distribution
- **L0 (7)**: Bug regression — every PR must run
- **L1 (12)**: Core type correctness + compound + index consistency
- **L2 (18)**: Extended types + all arrays + JSON path/contains

issue: #48048

## Test plan
- [x] L0+L1 pass (minus known Milvus bugs)
- [x] L2 pass
- [x] Eval engine verified: SQL NULL 3VL, NOT propagation, NOT IN, IS
NULL/IS NOT NULL
- [x] No false passes: parser errors → pytest.fail, skip rate tracking

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Signed-off-by: yanliang567 <82361606+yanliang567@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
yanliang567
2026-04-07 15:17:38 +08:00
committed by GitHub
co-authored by Claude Opus 4.6
parent b1d7b7427a
commit 3af5fe71e3
2 changed files with 1538 additions and 2059 deletions
+39
View File
@@ -49,6 +49,45 @@ except ValueError as e:
RNG = None
class NullValue:
"""Sentinel for SQL NULL semantics in expression evaluation.
All comparisons return False (NULL compared to anything is unknown/falsy).
Arithmetic propagates NULL. Python's OR short-circuit handles cases like
``int64 == 0 or float == 100`` correctly when float is NULL.
"""
def __eq__(self, other):
if isinstance(other, NullValue):
return False
return False
def __ne__(self, other): return False
def __lt__(self, other): return False
def __le__(self, other): return False
def __gt__(self, other): return False
def __ge__(self, other): return False
def __add__(self, other): return self
def __radd__(self, other): return self
def __sub__(self, other): return self
def __rsub__(self, other): return self
def __mul__(self, other): return self
def __rmul__(self, other): return self
def __truediv__(self, other): return self
def __rtruediv__(self, other): return self
def __mod__(self, other): return self
def __rmod__(self, other): return self
def __pow__(self, other): return self
def __rpow__(self, other): return self
def __neg__(self): return self
def __pos__(self): return self
def __abs__(self): return self
def __hash__(self): return id(self)
def __bool__(self): return False
def __repr__(self): return "NULL"
SQL_NULL = NullValue()
@singledispatch
def to_serializable(val):
"""Used by default."""
File diff suppressed because it is too large Load Diff