Skip to content

fix(arrow): preserve predicates across numeric schema promotion - #3123

Open
mattfaltyn wants to merge 2 commits into
apache:mainfrom
mattfaltyn:fix/arrow-numeric-predicate-promotion
Open

fix(arrow): preserve predicates across numeric schema promotion#3123
mattfaltyn wants to merge 2 commits into
apache:mainfrom
mattfaltyn:fix/arrow-numeric-predicate-promotion

Conversation

@mattfaltyn

@mattfaltyn mattfaltyn commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

What changes are included in this PR?

After numeric schema evolution, Arrow row filters compare an old physical column with a literal bound to the new table schema. Casting the literal down can turn out-of-range integers into nulls or round floating-point values, silently changing which rows match.

This change promotes Int32 columns to Int64, Float32 to Float64, and Decimal128 to a wider precision at the same scale before comparison. The six binary comparisons and IN/NOT IN share this path; each membership predicate promotes the column once per batch. Existing scalar casts between equivalent Arrow string/binary representations remain unchanged.

Optional page-index pruning now also promotes integer/float min/max values to long/double, instead of attaching the new type to an old physical literal variant. Otherwise, pruning can discard matching pages before the corrected row filter executes. Other index types keep their existing behavior.

No public API, dependency, metadata format, or persisted-data changes.

Scope: this covers Arrow row filtering and integer/float page-bound promotion. General decimal page-index decoding is still unsupported upstream even without evolution; adding FIXED_LEN_BYTE_ARRAY support is outside this fix (see closed, unmerged #1950). Decimal row filtering and row-group pruning are covered.

Are these changes tested?

  • End-to-end Parquet regression: 472 scan configurations covering int→long, float→double, and decimal precision widening at scales 0 and 2. It checks out-of-range and in-range literals, all six comparisons, IN/NOT IN, nulls, and double literals just above/below a stored float that round to it when narrowed.
  • The fixture spans multiple pages and row groups; reads use single-row batches. Both projected and filter-only columns are covered, with row-group and page pruning toggled independently for int/float. Decimal runs omit only unsupported page-index decoding.
  • The original regression fails on upstream main. The expanded regression also fails against the initial row-filter-only PR head: with page pruning enabled, x < 2147483648 returns no rows instead of [2, 3, 4]. All 472 configurations pass with both fixes.
  • cargo test -p iceberg --lib arrow::reader --locked: 103 passed.
  • cargo test -p iceberg --lib expr::visitors --locked: 201 passed.
  • Strict workspace Clippy (--all-targets --all-features --workspace --locked -- -D warnings) passed.
  • Rust formatting, tracked TOML formatting, and dependency-use checks passed. The tracked-file TOML check excludes a generated trybuild manifest under target/.
  • Full local workspace suite (cargo nextest run --all-targets --all-features --workspace --locked --no-fail-fast --test-threads 1): 2,157 passed, none failed or skipped.
  • Workspace doctests (cargo test --no-fail-fast --doc --all-features --workspace --locked): 93 passed, 23 ignored.
  • All-target/all-feature workspace build and the standalone iceberg build with no default features passed.
  • Python bindings built with Maturin; uv run --no-sync pytest: 17 passed, 2 skipped (credential-gated Hugging Face tests).

All 21 upstream checks pass on c598504a3, including the integration suite and Linux/macOS/Windows builds and Python tests. All local results above were rerun on this updated commit.

Local Rust validation uses the pinned nightly with CARGO_PROFILE_DEV_DEBUG=0. On this ARM Mac, integration fixtures run under Colima with native Java 8 for Hive and ICEBERG_TEST_HMS_ENDPOINT=127.0.0.1:9083. The full local suite uses one worker to avoid SQLite fixture write-lock contention; no repository configuration or product code is changed for these local environment adaptations.

AI Disclosure

Codex assisted with investigation, implementation, regression tests, and this description. The regression was executed before and after the fix, and the changed comparison paths were reviewed. Expanded validation reproduced the integer/float page-pruning gap before it was fixed. The separate decimal page-index feature remains outside this PR's scope.

@mattfaltyn
mattfaltyn marked this pull request as ready for review August 31, 2026 17:00
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.

bug(arrow): filters lose rows after numeric schema promotion

1 participant