Skip to content

Fix ArrayIndexOutOfBoundsException in CompositeBitmapIndex null handling#624

Open
hrstoyanov wants to merge 1 commit intoeclipse-store:mainfrom
hrstoyanov:fix/composite-index-null-handling
Open

Fix ArrayIndexOutOfBoundsException in CompositeBitmapIndex null handling#624
hrstoyanov wants to merge 1 commit intoeclipse-store:mainfrom
hrstoyanov:fix/composite-index-null-handling

Conversation

@hrstoyanov
Copy link
Copy Markdown

Summary

Fixes ArrayIndexOutOfBoundsException in AbstractCompositeBitmapIndex.internalHandleChanged() when updating an entity from null to non-null composite key value (or vice versa).

Issue Description

When an entity with a composite-indexed field (e.g., IndexerInstant for Instant fields) is:

  1. Added with a null value → composite key = NULL() = Object[1]
  2. Updated to a non-null value → composite key = Object[6] (for Instant's 6 components)

The method would throw:

java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1
    at org.eclipse.store.gigamap.types.SubBitmapIndexHashing.index()

Root Cause

The original internalHandleChanged() method called ensureSubIndices(newKeys) which grew the subIndices array, then iterated all sub-indices calling internalHandleChanged(oldKeys, ...). When oldKeys was shorter than the new array (e.g., length 1 vs 6), accessing oldKeys[position] threw ArrayIndexOutOfBoundsException.

Solution

Modified internalHandleChanged() to check isEmpty(oldKeys, i) and isEmpty(newKeys, i) per sub-index position:

  • Both empty: Skip (nothing to do)
  • Old empty, new non-empty: ADD via internalAddToEntry()
  • Old non-empty, new empty: REMOVE via internalRemove()
  • Both non-empty: HANDLE CHANGE via internalHandleChanged()

Files Changed

  1. AbstractCompositeBitmapIndex.java - Fixed internalHandleChanged() method (lines 529-568)
  2. CompositeIndexNullHandlingTest.java (NEW) - Comprehensive test coverage with 5 test methods

Testing

Test covers:

  • null→non-null updates
  • non-null→null updates
  • Persistence with EmbeddedStorage
  • Multiple entity updates
  • Query operations with mixed null/non-null values

Impact

  • Breaking Changes: None
  • Backward Compatible: Yes
  • Performance Impact: Minimal (adds 2 boolean checks per iteration)

Discovered by TourBiz project during Booking entity updates with nullable Instant fields.

- Add isEmpty() guards in internalHandleChanged() to properly handle
  composite key array length changes (e.g. NULL() Object[1] vs
  decomposed Instant Object[6])
- Dispatch to internalAddToEntry(), internalRemove(), or
  internalHandleChanged() based on old/new key presence per position
- Add comprehensive test coverage for null↔non-null updates
- Fixes issue reported by TourBiz project with Booking entity updates

Signed-off-by: Hristo Todorov
@hrstoyanov
Copy link
Copy Markdown
Author

This addresses #623

@hrstoyanov
Copy link
Copy Markdown
Author

hrstoyanov commented Mar 13, 2026

I have 100+ tests for Eclipse Store and with this fix it all pass now.

Please release 4.0.2

@zdenek-jonas
Copy link
Copy Markdown
Contributor

Thanks for the PR and the contribution. Due to our current review capacity, this will likely not be reviewed for at least a couple of weeks. Thank you for your patience.

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.

2 participants