Skip to content

feat(file-index): read range bitmap indexes - #849

Merged
JingsongLi merged 5 commits into
apache:mainfrom
jianguotian:codex/range-bitmap-reader
Sep 17, 2026
Merged

JingsongLi merged 5 commits into
apache:mainfrom
jianguotian:codex/range-bitmap-reader

Conversation

@jianguotian

Copy link
Copy Markdown
Contributor

Purpose

Paimon Java can write range-bitmap file indexes, but the Rust reader currently ignores them. As a result, selective predicates cannot turn those indexes into row selections, including for Mosaic row-group scheduling.

Changes

  • Decode the Java V1 range-bitmap header, chunk dictionaries, and BSI bitmaps.
  • Evaluate equality, set, range, between, and null predicates into FileIndexResult::Selection.
  • Support Java-compatible primitive, decimal, date/time, timestamp, and string encodings.
  • Preserve Rust predicate semantics for signed zero and fail open for NaN cases that cannot be proven safely.
  • Correctly select every row for all-null chunks and validate offsets, bitmap cardinality, and payload bounds.
  • Fail open on malformed or newer payloads rather than incorrectly pruning data.

This PR is query-side only: it reads Java-written range-bitmap indexes. A Rust writer can be added independently.

Verification

  • cargo test -p paimon --lib: 2,821 passed, 2 ignored
  • cargo clippy -p paimon --lib --tests -- -D warnings
  • Java-generated golden payloads for integer, string, and float columns
  • Regression coverage for all-null chunks, truncation, signed zero, NaN, and each supported predicate

Related Java work

Comment on lines +965 to +970
PredicateOperator::NotEq if literals.len() == 1 => {
let value = self.codec.value(&literals[0])?;
if value.is_nan() {
self.bsi.existing.clone()
} else {
self.not(&self.eq(&value))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eq() matches both signed zeros, but the Arrow row filter treats them as distinct. For f != +0.0, this drops -0.0 even though the row filter would keep it. I reproduced this with JAVA_FLOAT_V1 and evaluate_exact_leaf_predicate.

NOT IN and strict comparisons have the same issue (f < +0.0 loses -0.0). Could we keep these rows, or return Remain, and add a test against the row filter?

Comment on lines +941 to +942
PredicateOperator::Gt if literals.len() == 1 => {
self.gt(&self.codec.value(&literals[0])?)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NaN ordering still causes false negatives. With JAVA_FLOAT_V1, f > f32::from_bits(0xffc00000) matches all four non-null rows in evaluate_exact_leaf_predicate, but the index returns an empty selection because the literal is normalized to positive NaN.

Negative NaN values in the data can also be lost for f < 0.0. Could we keep pruning conservative for these cases and add regression coverage?

@QuakeWang QuakeWang left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@JingsongLi
JingsongLi merged commit 519d1c5 into apache:main Sep 17, 2026
14 checks passed
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.

3 participants