Skip to content

bug: FM index returns row addresses but does not declare the address domain #8848

Description

@wjones127

FmIndex is trained over the _rowaddr column — TrainingCriteria::new(TrainingOrdering::None).with_row_addr() at rust/lance-index/src/scalar/fmindex.rs:1666 and :2264, and the training batches are read from ROW_ADDR at :1783. Its search results are therefore physical row addresses.

However, FmIndex does not override ScalarIndex::results_are_row_addresses(), so it inherits the default false from rust/lance-index-core/src/scalar.rs:568. Only bloomfilter.rs:508, zonemap.rs:720, and scalar_logical.rs:166 declare the address domain today.

On a dataset without stable row ids this is harmless, because _rowid == _rowaddr. On a dataset with stable row ids the two domains diverge, and the dataset layer skips the address-to-row-id translation it would otherwise apply. The addresses are then treated as row ids, so the query returns the wrong rows — or no rows — with no error.

The same missing declaration also affects index maintenance: rust/lance-table/src/transaction/index_maintenance.rs:42 and rust/lance-table/src/transaction/manifest_build.rs:819 branch on results_are_row_addrs() to decide how fragment coverage is maintained, and take the row-id-domain branch for FM indices.

Reachable from Python via create_scalar_index(..., "FM") on a dataset created with enable_stable_row_ids=True.

The fix is to override results_are_row_addresses() to return true, plus a regression test that builds an FM index on a stable-row-id dataset with more than one fragment and asserts the returned rows match an unindexed scan.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-indexVector index, linalg, tokenizerbugSomething isn't workingperformance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions