Skip to content

feat(storage): free-list + VACUUM to reclaim orphan pages (SQLR-6)#89

Merged
joaoh82 merged 1 commit into
mainfrom
sqlr-6-freelist-vacuum
May 4, 2026
Merged

feat(storage): free-list + VACUUM to reclaim orphan pages (SQLR-6)#89
joaoh82 merged 1 commit into
mainfrom
sqlr-6-freelist-vacuum

Conversation

@joaoh82
Copy link
Copy Markdown
Owner

@joaoh82 joaoh82 commented May 4, 2026

Summary

  • Adds a SQLite-style persisted free-page list at header.freelist_head (bytes [28..32], format version v6 on demand) plus a new FreelistTrunk page type. After DROP TABLE / DROP INDEX / ALTER TABLE DROP COLUMN, the dropped object's pages move onto the freelist instead of triggering a global page-number renumber.
  • Replaces save_database's linear next_free_page counter with a PageAllocator that draws from per-table preferred pools → global freelist → file extension. Unrelated tables keep their page numbers across drops, so the diff pager skips writing their byte-identical leaves.
  • New VACUUM; SQL statement (executor.rs, mod.rs dispatch) compacts the file by ignoring both pools and allocating linearly from page 1, then double-checkpoints so the on-disk shrink is visible immediately. Bare VACUUM; only — modifiers rejected.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --all-targets — no new errors
  • cargo test --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs — 441 tests pass (9 new SQLR-6 tests + 11 unit tests for allocator/freelist)
  • cargo doc --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs --no-deps builds clean
  • REPL smoke test: create 2 tables, populate, DROP TABLE accounts; VACUUM; → reports 2 pages reclaimed (8192 bytes), file shrinks on disk, reopen confirms remaining table's rows intact

Out of scope (follow-up tickets to be filed)

  • Auto-VACUUM after every drop (the task explicitly defers this — wait for usage data).
  • Cross-session freelist reconciliation beyond fs2 advisory locks.
  • Bounded on_disk cache (pre-existing pager limitation, unchanged by this PR).

🤖 Generated with Claude Code

DROP TABLE / DROP INDEX / ALTER TABLE DROP COLUMN now route the dropped
object's pages onto a persisted free-page list (header bytes [28..32],
chained `FreelistTrunk` pages, format version v6 on demand). Subsequent
saves draw from the freelist before extending the file, so unrelated
tables keep their page numbers across drops and the diff pager skips
their byte-identical re-stages. `VACUUM;` (new SQL statement) compacts
the file by ignoring the freelist and allocating contiguously from
page 1, then double-checkpoints so the on-disk shrink is visible
immediately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@joaoh82 joaoh82 merged commit 368dc6d into main May 4, 2026
16 checks passed
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