Skip to content

fix: preserve RTree row id semantics#7666

Closed
ddupg wants to merge 1 commit into
lance-format:mainfrom
ddupg:codex/ddu-272-rtree-row-id
Closed

fix: preserve RTree row id semantics#7666
ddupg wants to merge 1 commit into
lance-format:mainfrom
ddupg:codex/ddu-272-rtree-row-id

Conversation

@ddupg

@ddupg ddupg commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • keep RTree scalar index payloads interpreted as logical row ids
  • reject fragment coverage recalculation from RTree row-id payloads instead of deriving fragments from physical row addresses
  • cover stable-row-id multi-fragment RTree SQL scans where _rowid differs from _rowaddr

Testing

  • cargo fmt --all
  • cargo test -p lance test_geo_rtree_index
  • cargo test -p lance-index --features geo scalar::rtree::tests
  • git diff --check

Summary by CodeRabbit

  • Bug Fixes
    • Improved spatial (R-tree) null handling so bbox intersections and search_null consistently use global row IDs, including when row-id remapping is enabled.
    • Tightened R-tree index schema validation: the second field must be named exactly _rowid and be UInt64 (with clearer expected vs actual details).
  • Tests
    • Expanded geo R-tree coverage to run with both stable and unstable row ID modes.
    • Updated assertions to compare indexed vs unindexed results using sorted (row_id, row_addr) pairs, including additional stable-row-id checks.

@github-actions github-actions Bot added A-index Vector index, linalg, tokenizer bug Something isn't working labels Jul 7, 2026
@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.29630% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
rust/lance-index/src/scalar/rtree.rs 96.29% 0 Missing and 2 partials ⚠️

📢 Thoughts on this report? Let us know!

@ddupg
ddupg force-pushed the codex/ddu-272-rtree-row-id branch 2 times, most recently from 27bfc21 to f4972ab Compare July 7, 2026 11:58
@ddupg
ddupg marked this pull request as ready for review July 7, 2026 13:03
@ddupg

ddupg commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@Xuanwo Could you help review this? thanks.

@ddupg
ddupg force-pushed the codex/ddu-272-rtree-row-id branch from f4972ab to 8821a30 Compare July 13, 2026 03:01
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The R-tree scalar index now tracks nulls and query results by absolute row ID, tightens row-ID schema validation, propagates the representation through updates and training, and expands geo-index tests for stable and unstable row IDs.

Changes

R-tree row ID tracking

Layer / File(s) Summary
Row ID query and construction flow
rust/lance-index/src/scalar/rtree.rs
Null statistics, bbox stream processing, search results, and schema validation now use absolute row IDs; logical row-id fragment inclusion returns a not-supported error.
Update and training propagation
rust/lance-index/src/scalar/rtree.rs
Index updates union null_row_ids, and training writes null values from the row-ID keyed map.
Row ID behavior tests
rust/lance-index/src/scalar/rtree.rs, rust/lance/src/dataset/tests/dataset_geo.rs
Tests validate row-ID-based null and bbox results, stable and unstable row IDs, row addresses, and indexed versus unindexed query equivalence.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately captures the main change: preserving RTree row ID semantics.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rust/lance-index/src/scalar/rtree.rs (1)

641-653: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Include the actual field name/type found in the schema-validation error.

The error message states the required shape but omits what was actually found, making failures harder to diagnose.

♻️ Proposed fix
         let row_id_field = schema.field(1);
         if row_id_field.name() != ROW_ID || *row_id_field.data_type() != DataType::UInt64 {
             return Err(Error::invalid_input_source(
-                "Second field in RTree index schema must be _rowid with type UInt64".into(),
+                format!(
+                    "Second field in RTree index schema must be {} with type UInt64, found field '{}' with type {:?}",
+                    ROW_ID, row_id_field.name(), row_id_field.data_type()
+                ),
             ));
         }

As per coding guidelines: "Include full context in error messages such as variable names, values, sizes, types, and indices; avoid generic messages like "Invalid chunk size"."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-index/src/scalar/rtree.rs` around lines 641 - 653, Update
validate_schema’s invalid-input error for the second field to include the actual
row_id_field.name() and data_type() values alongside the expected _rowid/UInt64
requirements; preserve the existing schema validation and error type.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/lance-index/src/scalar/rtree.rs`:
- Around line 480-482: Replace the panic in RTreeIndex::calculate_included_frags
with the existing Error::not_supported(...) error and preserve its Result return
type. Add a test covering this method that asserts it returns Result::Err
without panicking, including the expected unsupported-error behavior.

---

Outside diff comments:
In `@rust/lance-index/src/scalar/rtree.rs`:
- Around line 641-653: Update validate_schema’s invalid-input error for the
second field to include the actual row_id_field.name() and data_type() values
alongside the expected _rowid/UInt64 requirements; preserve the existing schema
validation and error type.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ddff190f-b0b7-4777-98b3-1b41774f6ae8

📥 Commits

Reviewing files that changed from the base of the PR and between f698426 and 8821a30.

📒 Files selected for processing (2)
  • rust/lance-index/src/scalar/rtree.rs
  • rust/lance/src/dataset/tests/dataset_geo.rs

Comment thread rust/lance-index/src/scalar/rtree.rs
@ddupg
ddupg force-pushed the codex/ddu-272-rtree-row-id branch from 8821a30 to 2c42824 Compare July 13, 2026 11:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rust/lance-index/src/scalar/rtree.rs (1)

717-717: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Preallocate the per-batch selection buffer.

non_null_indexes can contain up to num_rows entries and is rebuilt for every batch.

Proposed fix
-            let mut non_null_indexes = vec![];
+            let mut non_null_indexes = Vec::with_capacity(num_rows);

As per coding guidelines, use Vec::with_capacity() when size is known or estimable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-index/src/scalar/rtree.rs` at line 717, Update the per-batch
non_null_indexes allocation in the R-tree batch processing logic to use
Vec::with_capacity(num_rows), since the buffer can hold up to num_rows entries
and is rebuilt for each batch.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/lance/src/dataset/tests/dataset_geo.rs`:
- Line 211: Update the test’s rows vector initialization near the exact-two-row
assertion to use Vec::with_capacity with capacity 2 instead of Vec::new(),
preserving the existing row collection and assertions.

---

Outside diff comments:
In `@rust/lance-index/src/scalar/rtree.rs`:
- Line 717: Update the per-batch non_null_indexes allocation in the R-tree batch
processing logic to use Vec::with_capacity(num_rows), since the buffer can hold
up to num_rows entries and is rebuilt for each batch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b271a3bf-4065-4bda-9c11-9613d7f65ebe

📥 Commits

Reviewing files that changed from the base of the PR and between 8821a30 and 2c42824.

📒 Files selected for processing (2)
  • rust/lance-index/src/scalar/rtree.rs
  • rust/lance/src/dataset/tests/dataset_geo.rs

Comment thread rust/lance/src/dataset/tests/dataset_geo.rs Outdated
@ddupg
ddupg force-pushed the codex/ddu-272-rtree-row-id branch 3 times, most recently from 386c76e to 5b37c05 Compare July 13, 2026 12:05

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
rust/lance-index/src/scalar/rtree.rs (1)

480-482: ⚠️ Potential issue | 🔴 Critical

Panic on manifest migration path still unresolved.

calculate_included_frags still panics via unimplemented!(). Per the PR objective this method should now reject fragment coverage recalculation from row-ID payloads, not abort. commit.rs calls this method to rebuild stale fragment bitmaps, so this can crash manifest migration for any pre-existing RTree index. This was already flagged in a prior review round and remains unfixed; no Result::Err test was added either.

🛡️ Proposed fix
     async fn calculate_included_frags(&self) -> Result<RoaringBitmap> {
-        unimplemented!()
+        // RTree indexes store logical row IDs, not physical row addresses, so
+        // fragment coverage cannot be derived from the payload alone.
+        Err(Error::not_supported(
+            "RTree index does not support fragment bitmap recalculation from row-ID payloads"
+                .into(),
+        ))
     }

Add a #[tokio::test] asserting this returns Result::Err with the expected message, per the "test with Result::Err assertions instead of #[should_panic]" guideline.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/lance-index/src/scalar/rtree.rs` around lines 480 - 482, Replace the
unimplemented!() body in calculate_included_frags with a Result::Err carrying
the expected rejection message, so manifest migration returns an error instead
of panicking. Add a #[tokio::test] covering this method and assert it returns
Err with the expected message, without using #[should_panic].

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/lance/src/dataset/tests/dataset_geo.rs`:
- Around line 156-158: Update the rstest configuration for test_geo_rtree_index
to replace #[values(true, false)] with named #[case::stable_row_ids(true)] and
#[case::unstable_row_ids(false)] entries, and change the parameter annotation to
#[case] while preserving the existing test behavior.

---

Duplicate comments:
In `@rust/lance-index/src/scalar/rtree.rs`:
- Around line 480-482: Replace the unimplemented!() body in
calculate_included_frags with a Result::Err carrying the expected rejection
message, so manifest migration returns an error instead of panicking. Add a
#[tokio::test] covering this method and assert it returns Err with the expected
message, without using #[should_panic].
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 37c0cadf-30a1-4bf5-acf7-4e6cfa264ee5

📥 Commits

Reviewing files that changed from the base of the PR and between 2c42824 and 1ee0605.

📒 Files selected for processing (2)
  • rust/lance-index/src/scalar/rtree.rs
  • rust/lance/src/dataset/tests/dataset_geo.rs

Comment thread rust/lance/src/dataset/tests/dataset_geo.rs Outdated
@ddupg
ddupg force-pushed the codex/ddu-272-rtree-row-id branch from 5b37c05 to 63ee5dc Compare July 13, 2026 12:15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/lance/src/dataset/tests/dataset_geo.rs`:
- Around line 229-234: Update the row validation loop in the dataset geo test so
the use_stable_row_ids false branch asserts row_id equals row_addr, while
preserving the existing assert_ne! check for stable IDs and the fragment ID
assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c8f24940-438c-4637-8ef1-c67e671461cc

📥 Commits

Reviewing files that changed from the base of the PR and between 5b37c05 and 63ee5dc.

📒 Files selected for processing (2)
  • rust/lance-index/src/scalar/rtree.rs
  • rust/lance/src/dataset/tests/dataset_geo.rs

Comment thread rust/lance/src/dataset/tests/dataset_geo.rs
@ddupg
ddupg force-pushed the codex/ddu-272-rtree-row-id branch 2 times, most recently from e828af1 to d130677 Compare July 15, 2026 06:55
@ddupg
ddupg force-pushed the codex/ddu-272-rtree-row-id branch from d130677 to 020d4bc Compare July 20, 2026 06:38
@ddupg

ddupg commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Duplicate of #7932

@ddupg ddupg closed this Jul 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-index Vector index, linalg, tokenizer bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant