Skip to content

fix(storage): pre-collect preferred-pool pages before staging (SQLR-1)#93

Merged
joaoh82 merged 1 commit into
mainfrom
fix/sqlr-1-create-index-multi-page
May 5, 2026
Merged

fix(storage): pre-collect preferred-pool pages before staging (SQLR-1)#93
joaoh82 merged 1 commit into
mainfrom
fix/sqlr-1-create-index-multi-page

Conversation

@joaoh82

@joaoh82 joaoh82 commented May 5, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fixes SQLR-1: CREATE INDEX on a wide table panicked during the post-DDL auto-save with Internal("unknown paged-entry kind tag 0x4 …").
  • Root cause: save_database_with_mode walked each B-Tree's previous root via collect_pages_for_btree during the staging loop. Pager::read_page shadows on-disk bytes with the in-flight staged buffer, so a new index extending past the old high-water would overwrite the pages of any later-staged object — sqlrite_master (always staged last) being the canonical victim. The master walk then read the new index's freshly-staged leaf and tripped on KIND_INDEX = 0x04 going through PagedEntry::decode, which only knows KIND_LOCAL/KIND_OVERFLOW.
  • Fix: snapshot every prior B-Tree's page set up front (right after read_old_rootpages, before clear_staged()), then look those snapshots up in the staging loop instead of re-walking. Master gets its own old_master_pages snapshot for the same reason. All five in-loop collect_pages_for_btree calls removed.
  • Tightens PagedEntry::decode's error to name the offending kind and the B-Tree the caller meant to walk, and documents the per-B-Tree decoder dispatch contract in docs/file-format.md so the bug-class doesn't recur.

Test plan

  • New regression test secondary_index_with_interior_level_round_trips — 5000-row CREATE INDEX, reopen, lookup spot-checks, asserts root is InteriorNode (the regression's necessary condition).
  • New regression test drop_then_recreate_wide_index_does_not_panic — exercises page recycling between two large versions of the same index name.
  • New unit tests paged_entry_decode_rejects_index_kind_with_clear_error and paged_entry_decode_rejects_hnsw_and_fts_kinds — wrong-decoder error message coverage.
  • cargo build --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --all-targets
  • cargo test --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs (384 engine + 19 MCP + 12 FFI + all doctests pass)
  • cargo fmt --all -- --check
  • cargo clippy --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --all-targets (no new warnings)
  • cargo doc --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --no-deps

🤖 Generated with Claude Code

CREATE INDEX on a wide table panicked during the post-DDL auto-save
with `unknown paged-entry kind tag 0x4` because save_database walked
each B-Tree's previous root *during* the staging loop. Pager::read_page
shadows on-disk bytes with the in-flight `staged` buffer, so a new
index extending past the old high-water would overwrite the pages of
any later-staged object whose old root sat in that range. master is
staged last, so its old root was the canonical victim — the master
walk read the new index's freshly-staged leaf and tripped on
KIND_INDEX (0x04). Snapshot every prior B-Tree's page set up front
(right after read_old_rootpages, before clear_staged) and look the
snapshots up in the staging loop instead of re-walking. master gets
its own old_master_pages snapshot for the same reason.

Also tightens PagedEntry::decode's error to name the offending kind
(KIND_INDEX/KIND_INTERIOR/KIND_HNSW/KIND_FTS_POSTING) and the B-Tree
the caller meant to walk, and documents the per-B-Tree decoder
dispatch contract in docs/file-format.md so the bug-class doesn't
recur when a future cell kind lands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@joaoh82 joaoh82 merged commit 1c291c1 into main May 5, 2026
15 checks passed
@joaoh82 joaoh82 mentioned this pull request May 5, 2026
3 tasks
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.

1 participant