perf(r2rml): push numeric (double/decimal) FILTER predicates to Iceberg pruning (PR-7, H4) - #1494
Closed
aaj3f wants to merge 2 commits into
Closed
perf(r2rml): push numeric (double/decimal) FILTER predicates to Iceberg pruning (PR-7, H4)#1494aaj3f wants to merge 2 commits into
aaj3f wants to merge 2 commits into
Conversation
…rg pruning (PR-7, H4) Widen file/row-group pruning to double and decimal so numeric FILTERs skip data files, not just int/date/string. The blind spot was two-layer: the query side never produced a numeric ScanValue (so no numeric predicate reached the reader -- the real cause of q019 files_pruned=0), and the iceberg side would have declined it anyway (no Double/FLBA-decimal stat_bounds arm; no Decimal arm in TypedValue::lt/le). Both are closed here, gated by FLUREE_ICEBERG_NUMERIC_STATS. Query side: ScanValue gains Double + Decimal; to_scan_value pushes them (gated) with a scale-normalizing BigDecimal decompose. build_iceberg_filter bridges Double->Float64 (double cols) and Decimal->Decimal (decimal cols). An INTEGER literal against a decimal column (q019's `?deb > 1000000` on the xsd:decimal money column) coerces to an EXACT scale-0 decimal via int_pushdown_literal so it is comparable to the column's decimal bounds (decimal_cmp normalizes the scale gap) -- gated by an api-side OnceLock mirroring the query switch, so switch-off keeps the pre-PR-7 Int64 (no-prune) form and the revert guarantee holds. Iceberg side: stat_bounds gains Double/Float and FLBA-decimal arms (decoding the column's scale, not the literal's); prunable_stats admits FLBA-encoded decimals (the encoding the Iceberg spec mandates) and declines INT32/INT64-backed decimals with a debug breadcrumb; TypedValue::lt/le gain a Decimal arm and NaN-safe Float arms. Correctness: the pushdown stays a strict SUPERSET (over-keep, never over-prune; the in-engine FILTER is the sole authority). NaN over-prune (F15) -- latent and unreachable pre-PR-7, armed by the Double push -- is fixed in-PR: a NaN operand in Float lt/le returns None -> keep. Two conservative residuals are declined observably (double literal vs decimal column, decimal literal vs int column; inexact / no exact cross-type compare -> keep + debug breadcrumb). H4 live gate (DW_SF01, ~7670 files per fact table): query files_pruned/selected wall (hot) note q019 7670/0 800ms integer > money threshold; cold 38.8s -> 4.0s (~10x) q020 7579/91 635ms date range -- representative partial prune q021 7670/0 636ms int account-code band q019 switch-off 0/7670 916ms byte-identical -> perfect revert Native 54/54 parity: 0 hash mismatch. Snowflake decimals ARE FLBA in practice (the prune worked end to end). Note on the total prunes: q019/q021 select zero rows on this dataset, so their full prune is the predicate-selects-nothing case (the strongest but easiest prune); a partially-selective money filter prunes proportional to data layout, with q020's 91-file partial the representative middle. This is a different case from the 05-diagnosis "int pushes but nothing prunes" observation, which measured an in-range predicate over the column's value spread -- q021 here is an out-of-range band -- so it is not a regression of that finding.
aaj3f
force-pushed
the
perf/r2rml-pr4c-optional-star
branch
from
July 15, 2026 16:48
b1cb988 to
edcd07b
Compare
…case scan_value_from_bigdecimal on a round literal produces a NEGATIVE scale via normalized() (unscaled 1, scale -6 = 1_000_000). decimal_cmp handles it by construction (max-scale rescale keeps exponents >= 0) but no test pinned it. Add a negative-scale-vs-positive-scale round trip to decimal_lt_le_cross_scale (lt/le/ ge, exact-equality across the sign boundary) — the last untested arm (#1494).
aaj3f
force-pushed
the
perf/r2rml-pr7-numeric-stats
branch
from
July 15, 2026 17:04
2a07bbb to
b0bba4f
Compare
Contributor
Author
|
Consolidating at maintainer request. #1494 (PR-7 — numeric double/decimal FILTER pushdown to Iceberg pruning) is carried forward in #1507 (the R2RML/Iceberg virtual-dataset performance program), which brings the #1475–#1507 lineage forward as a single reviewable unit. Its verification of record is the program's corpus benchmark, published as C2-bench-wave1.md (native == virtual, 0 hash mismatches). The pre-consolidation branch tip is preserved at tag |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
PR-7 (H4): push numeric (double / decimal) FILTER predicates down to Iceberg file / row-group pruning, so a numeric range/equality filter skips data files instead of decoding all of them. Previously only int / date / string predicates pruned; a decimal or double FILTER decoded every file.
The two-layer blind spot
ScanValuecarried no numeric type, soto_scan_valuenever produced a numeric predicate,build_iceberg_filternever emitted one, and the reader never saw it — the real reason q019 showedfiles_pruned = 0.stat_boundshad no Double / FLBA-decimal arm,prunable_statsdeclined all decimals, andTypedValue::lt/lehad no Decimal arm (a pushed decimal would silently never prune).Both are closed here, gated by
FLUREE_ICEBERG_NUMERIC_STATS(default on).Change surface
Query:
ScanValue+Double/Decimal;to_scan_valuegated arms + scale-normalizing BigDecimal decompose;build_iceberg_filterDouble→Float64 (double cols), Decimal→Decimal (decimal cols). An integer literal against a decimal column (q019?deb > 1000000on thexsd:decimalmoney column) coerces to an EXACT scale-0 decimal viaint_pushdown_literal—decimal_cmpnormalizes the scale gap. Gated api-side (FLUREE_ICEBERG_NUMERIC_STATSOnceLock, disk-catalog-cache precedent); switch-off keeps the pre-PR-7Int64no-prune form (full revert).Iceberg:
stat_bounds+Double/Float/FLBA-decimal (decodes the column's scale, not the literal's);prunable_statsadmits FLBA-encoded decimals (the encoding the Iceberg spec mandates), declines INT32/INT64-backed decimals with a debug breadcrumb;TypedValue::lt/le+Decimal arm + NaN-safe Float.Correctness
The pushdown is a strict superset — it may over-keep, never over-prune; the in-engine FILTER is the sole authority. A latent NaN over-prune (F15; unreachable pre-PR-7, armed by the Double push) is fixed in-PR: a NaN operand in Float
lt/lereturnsNone→ keep. Two conservative residuals decline observably (double-vs-decimal, decimal-vs-int; inexact / no exact cross-type compare → keep + debug breadcrumb).H4 live gate (DW_SF01, ~7670 files per fact table)
Native 54/54 parity: 0 hash mismatch. Snowflake decimals ARE FLBA in practice (the prune worked end to end).
Caveat: q019/q021 select zero rows on this dataset, so their total prune is the predicate-selects-nothing case (the strongest but easiest prune); a partially-selective filter prunes proportional to data layout (q020's 91-file partial is the representative middle). This is a different case from the 05-diagnosis "int pushes but nothing prunes" note, which measured an in-range predicate over the column's value spread — q021 here is an out-of-range band — so it is not a regression of that finding.
Tests
11 new hermetic unit tests over real Parquet fixtures: double prune/keep, FLBA-decimal cross-scale (9.99↔9.990) prune/keep/boundary, int→scale-0-decimal coercion (positive prune / in-bounds keep / rescale-overflow keep / switch-off revert), INT-decimal decline, NaN-keep, and the BigDecimal decompose. iceberg lib 172, query r2rml 59, api r2rml 14 — all green; clippy + fmt clean.
Stacked on
perf/r2rml-pr4c-optional-star. PR-5 (scan-side top-k), whose pruning leg these numeric stats unblock, is the next and final roadmap item.