Skip to content

feat(format): read and write sparse structural pages#7889

Merged
Xuanwo merged 9 commits into
mainfrom
xuanwo/sparse-stack-3-sparse-reader
Jul 21, 2026
Merged

feat(format): read and write sparse structural pages#7889
Xuanwo merged 9 commits into
mainfrom
xuanwo/sparse-stack-3-sparse-reader

Conversation

@Xuanwo

@Xuanwo Xuanwo commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

This PR replays #7754 unchanged against main.

#7754 was accidentally merged into xuanwo/sparse-stack-2-empty-inline-bitpacked instead of main. This PR only corrects that target mistake and introduces no changes beyond the original PR.

All design discussion, review history, approvals, and validation are recorded in #7754.

@Xuanwo Xuanwo added enhancement New feature or request A-deps Dependency updates A-encoding Encoding, IO, file reader/writer A-format On-disk format: protos and format spec docs labels Jul 21, 2026
@Xuanwo
Xuanwo requested a review from wjones127 July 21, 2026 17:35
@github-actions

Copy link
Copy Markdown
Contributor

Important

This PR touches the Lance format specification.

Substantive changes to the format specification — the .proto definitions
and the spec docs under docs/src/format/ — require a PMC vote before merge.
Minor edits such as typo fixes, wording, or formatting are excluded; use your
judgment.

If this is a meaningful format change:

  • Start a vote following the Lance community voting process.
    Format specification modifications need 3 binding +1 votes (excluding the
    proposer), held on GitHub Discussions, with a minimum voting period of 1 week.
  • Once the vote passes, link the completed vote in this PR. It should not be
    merged until the vote is linked.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Sparse structural encoding

Layer / File(s) Summary
Sparse format contract
docs/src/format/file/*, protos/encodings_v2_1.proto, protos/file2.proto, rust/lance-encoding/src/constants.rs
Documents and defines the Lance 2.3 sparse page layout, protobuf structures, metadata option, and unstable-version guidance.
Sparse planning and page writing
rust/lance-encoding/src/repdef.rs, rust/lance-encoding/src/encodings/logical/primitive*, rust/lance-encoding/src/constants.rs
Normalizes structural plans, selects sparse encoding, serializes sparse layers and value chunks, and preserves dense-page handling.
Sparse scheduling and decoding
rust/lance-encoding/src/encodings/logical/primitive/sparse.rs, rust/lance-encoding/src/repdef.rs, rust/lance-encoding/src/encodings/logical/primitive.rs
Validates sparse metadata, schedules selected value chunks, projects structural plans, and reconstructs Arrow values and offsets.
Structural validation and error propagation
rust/lance-encoding/src/decoder.rs, rust/lance-encoding/src/encodings/logical/{fixed_size_list,list,map,struct}.rs, rust/lance-encoding/src/repdef.rs
Adds runtime validation for malformed structures, sibling metadata mismatches, collection construction, and decoder error preservation.
File reader integration and verification
rust/lance-file/src/reader.rs, rust/lance-encoding/src/testing.rs, rust/lance/benches/s3_file_reader_diagnostics.rs
Adds sparse row-count inference, fallible metadata decoding, sparse encoding discovery, diagnostics, and range/take roundtrip coverage.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

  • lance-format/lance#7756 — Shares the Lance 2.3 SparseLayout plumbing and sparse structural encode/decode integration.

Suggested reviewers: westonpace, wjones127

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.27% 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
Title check ✅ Passed The title clearly summarizes the main change: sparse structural page read/write support in format code.
Description check ✅ Passed The description is directly related, explaining that this PR replays the same sparse-stack changes against main.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch xuanwo/sparse-stack-3-sparse-reader

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

@Xuanwo Xuanwo changed the title feat(format): read and write sparse structural pages feat(format): replay sparse structural pages against main Jul 21, 2026
@Xuanwo Xuanwo removed enhancement New feature or request A-deps Dependency updates A-encoding Encoding, IO, file reader/writer A-format On-disk format: protos and format spec docs labels Jul 21, 2026
@Xuanwo
Xuanwo removed the request for review from wjones127 July 21, 2026 17:36
@github-actions github-actions Bot added the enhancement New feature or request label Jul 21, 2026
@Xuanwo Xuanwo removed the enhancement New feature or request label Jul 21, 2026
@Xuanwo Xuanwo changed the title feat(format): replay sparse structural pages against main feat(format): read and write sparse structural pages Jul 21, 2026

@westonpace westonpace 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.

Replaying approval from #7754

@github-actions github-actions Bot added the enhancement New feature or request label Jul 21, 2026
@github-actions github-actions Bot added A-encoding Encoding, IO, file reader/writer A-format On-disk format: protos and format spec docs labels Jul 21, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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-encoding/src/encodings/logical/primitive/sparse/writer.rs`:
- Around line 1788-1810: Update test_explicit_sparse_rejects_lance_2_2 to assert
that both create_encoder failures return the expected Error variant, while
retaining the existing message-content checks. Apply the same variant assertion
to the regular nullable input and structural_only case, using matches! or the
project’s established error-pattern style.
🪄 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: 668aab78-0b61-4fbd-8335-3ecba15aa5fa

📥 Commits

Reviewing files that changed from the base of the PR and between f91da0a and c07ea94.

📒 Files selected for processing (19)
  • docs/src/format/file/encoding.md
  • docs/src/format/file/versioning.md
  • protos/encodings_v2_1.proto
  • protos/file2.proto
  • rust/lance-encoding/src/constants.rs
  • rust/lance-encoding/src/decoder.rs
  • rust/lance-encoding/src/encodings/logical/fixed_size_list.rs
  • rust/lance-encoding/src/encodings/logical/list.rs
  • rust/lance-encoding/src/encodings/logical/map.rs
  • rust/lance-encoding/src/encodings/logical/primitive.rs
  • rust/lance-encoding/src/encodings/logical/primitive/miniblock.rs
  • rust/lance-encoding/src/encodings/logical/primitive/sparse.rs
  • rust/lance-encoding/src/encodings/logical/primitive/sparse/writer.rs
  • rust/lance-encoding/src/encodings/logical/struct.rs
  • rust/lance-encoding/src/encodings/physical/bitpacking.rs
  • rust/lance-encoding/src/repdef.rs
  • rust/lance-encoding/src/testing.rs
  • rust/lance-file/src/reader.rs
  • rust/lance/benches/s3_file_reader_diagnostics.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (4)
rust/lance-encoding/src/repdef.rs (2)

1681-1688: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Document sparse validation failures on this public API.

Line 1686 now returns Result; add # Errors describing malformed sparse structural metadata, plus a synchronized usage example/link to the sparse unraveller contract.

As per coding guidelines, public APIs require semantic documentation, examples, and links to relevant methods or structs.

🤖 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-encoding/src/repdef.rs` around lines 1681 - 1688, Update the
public RepDef::max_lists documentation to include a # Errors section describing
failures from malformed sparse structural metadata, and add a synchronized usage
example that links to the sparse unraveller contract and relevant method or
struct. Keep the existing validity caveat and implementation behavior unchanged.

Source: Coding guidelines


983-992: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove the guarded unwrap() from the serializer planner.

Line 992 relies on a separate predicate for safety. Bind max_visible_level with let Some(...) = ... else { ... } and return the existing WithinBudget fast path instead.

Proposed fix
-        let max_visible_level = SerializedRepDefs::max_visible_level(def_meaning);
-        let should_plan = !self.has_fsl && max_schema_rep > 0 && max_visible_level.is_some();
-
-        if !should_plan {
+        if self.has_fsl || max_schema_rep == 0 {
             self.normalize_specials();
             return Ok(MiniBlockRepDefBudget::WithinBudget);
         }
-
-        let max_visible_level = max_visible_level.unwrap();
+        let Some(max_visible_level) = SerializedRepDefs::max_visible_level(def_meaning) else {
+            self.normalize_specials();
+            return Ok(MiniBlockRepDefBudget::WithinBudget);
+        };

As per coding guidelines, never use bare .unwrap() in library code for fallible operations.

🤖 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-encoding/src/repdef.rs` around lines 983 - 992, Replace the
separate max_visible_level predicate and later unwrap in the serializer planning
flow with a let-else binding around SerializedRepDefs::max_visible_level. In the
else branch, call self.normalize_specials() and return
MiniBlockRepDefBudget::WithinBudget; retain the existing planning conditions and
subsequent logic for Some values.

Source: Coding guidelines

rust/lance-file/src/reader.rs (2)

946-956: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Classify malformed on-disk metadata as corrupt-file errors.

These checks reject persisted file-format corruption, not invalid caller parameters. Preserve the detailed context/source but use Error::corrupt_file so callers can distinguish damaged files from API-input errors.

  • rust/lance-file/src/reader.rs#L946-L956: classify invalid global-buffer alignment as corrupt file metadata.
  • rust/lance-file/src/reader.rs#L1162-L1312: classify invalid encoding envelopes, missing metadata, mismatched vectors, and unaligned page offsets as corrupt file metadata.

As per coding guidelines, “use Error::corrupt_file for format or integrity issues.”

🤖 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-file/src/reader.rs` around lines 946 - 956, Update the metadata
validation errors in reader.rs to use Error::corrupt_file instead of
Error::invalid_input_source. Apply this to the global-buffer alignment check
near lines 946-956 and all invalid encoding envelopes, missing metadata,
mismatched vectors, and unaligned page-offset checks within lines 1162-1312,
preserving each error’s existing context and source details.

Source: Coding guidelines


2751-2879: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Force sparse pages to cross a page boundary.

This test uses one six-row batch with default page limits and never asserts multiple pages. Force multiple V2.3 sparse pages and verify a range/take crossing a boundary, otherwise the new per-page num_slots aggregation remains untested.

As per coding guidelines, “Every bugfix and feature must have corresponding tests.”

🤖 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-file/src/reader.rs` around lines 2751 - 2879, Update
sparse_file_writer_reader_scan_range_and_take_roundtrip to configure
sufficiently small page limits when writing the V2_3 file, ensuring the six-row
batch produces multiple sparse pages and asserting that multiple pages were
created. Keep the existing full scan, range, and take checks, with the range or
take selection crossing a page boundary so per-page num_slots aggregation is
exercised.

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.

Outside diff comments:
In `@rust/lance-encoding/src/repdef.rs`:
- Around line 1681-1688: Update the public RepDef::max_lists documentation to
include a # Errors section describing failures from malformed sparse structural
metadata, and add a synchronized usage example that links to the sparse
unraveller contract and relevant method or struct. Keep the existing validity
caveat and implementation behavior unchanged.
- Around line 983-992: Replace the separate max_visible_level predicate and
later unwrap in the serializer planning flow with a let-else binding around
SerializedRepDefs::max_visible_level. In the else branch, call
self.normalize_specials() and return MiniBlockRepDefBudget::WithinBudget; retain
the existing planning conditions and subsequent logic for Some values.

In `@rust/lance-file/src/reader.rs`:
- Around line 946-956: Update the metadata validation errors in reader.rs to use
Error::corrupt_file instead of Error::invalid_input_source. Apply this to the
global-buffer alignment check near lines 946-956 and all invalid encoding
envelopes, missing metadata, mismatched vectors, and unaligned page-offset
checks within lines 1162-1312, preserving each error’s existing context and
source details.
- Around line 2751-2879: Update
sparse_file_writer_reader_scan_range_and_take_roundtrip to configure
sufficiently small page limits when writing the V2_3 file, ensuring the six-row
batch produces multiple sparse pages and asserting that multiple pages were
created. Keep the existing full scan, range, and take checks, with the range or
take selection crossing a page boundary so per-page num_slots aggregation is
exercised.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: QUIET

Plan: Pro Plus

Run ID: 5e68fb18-23a2-42e7-b005-9d5841b66a57

📥 Commits

Reviewing files that changed from the base of the PR and between c07ea94 and 49524ac.

📒 Files selected for processing (3)
  • rust/lance-encoding/src/encodings/logical/primitive.rs
  • rust/lance-encoding/src/repdef.rs
  • rust/lance-file/src/reader.rs

@Xuanwo
Xuanwo merged commit df7c8cc into main Jul 21, 2026
38 checks passed
@Xuanwo
Xuanwo deleted the xuanwo/sparse-stack-3-sparse-reader branch July 21, 2026 18:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-encoding Encoding, IO, file reader/writer A-format On-disk format: protos and format spec docs enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants