Block-skip logic for integer predicates (lossless i64 comparison). When `BlockStats` carries `min_i64`/`max_i64` (written by minor v1+), the comparison is done entirely in i64 — no f64 rounding. When the exact fields are absent (segments written at minor v0), we fall through to the f64 path via `f64_to_exact_i64`: if the predicate value itself converts losslessly we can still do an exact comparis
(op: PredicateOp, value: i64, stats: &BlockStats)
| 275 | /// we can still do an exact comparison against the (possibly rounded) f64 |
| 276 | /// stats, which is conservative (may fail to skip) but never incorrect. |
| 277 | fn can_skip_integer(op: PredicateOp, value: i64, stats: &BlockStats) -> bool { |
| 278 | // Prefer lossless i64 stats when available. |
| 279 | if let (Some(smin), Some(smax)) = (stats.min_i64, stats.max_i64) { |
| 280 | return match op { |
| 281 | PredicateOp::Gt => smax <= value, |
| 282 | PredicateOp::Gte => smax < value, |
| 283 | PredicateOp::Lt => smin >= value, |
| 284 | PredicateOp::Lte => smin > value, |
| 285 | PredicateOp::Eq => value < smin || value > smax, |
| 286 | PredicateOp::Ne => smin == value && smax == value, |
| 287 | }; |
| 288 | } |
| 289 | |
| 290 | // Fallback: convert the predicate value to f64 if it is exactly |
| 291 | // representable, then use the f64 stats path. If the conversion is lossy |
| 292 | // we cannot safely skip — return false (conservative). |
| 293 | match f64_to_exact_i64(value as f64).and(Some(value as f64)) { |
| 294 | Some(fv) => can_skip_numeric(op, fv, stats), |
| 295 | None => false, |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | /// Convert an f64 to i64 only if the conversion is exact. |
| 300 | /// |
no test coverage detected