Block-skip logic for string predicates.
(op: PredicateOp, value: &str, stats: &BlockStats)
| 320 | |
| 321 | /// Block-skip logic for string predicates. |
| 322 | fn can_skip_string(op: PredicateOp, value: &str, stats: &BlockStats) -> bool { |
| 323 | let (Some(smin), Some(smax)) = (&stats.str_min, &stats.str_max) else { |
| 324 | // No string zone-map information → cannot skip. |
| 325 | return false; |
| 326 | }; |
| 327 | |
| 328 | let skip_by_range = match op { |
| 329 | // column > value → skip if block.max <= value (no string is > value) |
| 330 | PredicateOp::Gt => smax.as_str() <= value, |
| 331 | // column >= value → skip if block.max < value |
| 332 | PredicateOp::Gte => smax.as_str() < value, |
| 333 | // column < value → skip if block.min >= value |
| 334 | PredicateOp::Lt => smin.as_str() >= value, |
| 335 | // column <= value → skip if block.min > value |
| 336 | PredicateOp::Lte => smin.as_str() > value, |
| 337 | // column = value → skip if value outside [min, max] |
| 338 | PredicateOp::Eq => value < smin.as_str() || value > smax.as_str(), |
| 339 | // column != value → skip only if the entire block contains that exact value |
| 340 | PredicateOp::Ne => smin.as_str() == value && smax.as_str() == value, |
| 341 | }; |
| 342 | |
| 343 | if skip_by_range { |
| 344 | return true; |
| 345 | } |
| 346 | |
| 347 | // For Eq predicates, apply bloom filter as an additional fast-reject. |
| 348 | if op == PredicateOp::Eq |
| 349 | && let Some(ref bloom) = stats.bloom |
| 350 | && !bloom_may_contain(bloom, value) |
| 351 | { |
| 352 | return true; // Bloom says "definitely not present" → skip. |
| 353 | } |
| 354 | |
| 355 | false |
| 356 | } |
| 357 | |
| 358 | // ── Bloom filter ──────────────────────────────────────────────────────────── |
| 359 |
no test coverage detected