Expand description
Persisted free-page list (SQLR-6).
After DROP TABLE / DROP INDEX / ALTER TABLE DROP COLUMN, the next
save_database no longer references the dropped object’s pages. Without
a freelist those pages would either be re-numbered (every later table
shifts down → high write amplification) or stranded as orphans on disk.
The freelist solves both: pages no longer referenced from sqlrite_master
go on a persisted stack rooted at header.freelist_head, so subsequent
saves can pull from there before extending the file. The unrelated tables
that didn’t change keep their page numbers and their pages re-stage byte-
identical → the diff pager skips writing them.
§On-disk layout
Each freelist trunk page carries the standard 7-byte page header with
page_type = PAGE_TYPE_FREELIST_TRUNK (5) and next_page pointing at the
next trunk in the chain (0 = end). The 4089-byte payload area holds:
0..2 count: u16 LE — number of free leaf-page IDs that follow
2..2+4*N page_ids[N]: u32 LE — free leaf-page numbers, ascendingA trunk holds up to (PAYLOAD_PER_PAGE - 2) / 4 = 1021 free page IDs.
Larger freelists chain across multiple trunks. The trunk pages themselves
are part of the live page set (they hold metadata) and are not on the
freelist they encode.
Constants§
- FREELIST_
IDS_ PER_ TRUNK - Maximum number of free page IDs a single trunk page can hold.
PAYLOAD_PER_PAGEis 4089; we reserve 2 bytes for the count and 4 bytes per ID, giving(4089 - 2) / 4 = 1021. - MIN_
PAGES_ FOR_ AUTO_ VACUUM - Auto-VACUUM (SQLR-10) does not fire on databases below this many
pages. The 4 KiB page size makes 16 pages = 64 KiB — small enough
that the cost of a full-file rewrite is negligible if the user
genuinely wants it (manual
VACUUM;still works), but large enough that single-table churn doesn’t blow past the threshold and trigger a noisy compact every few statements. - PAGE_
TYPE_ FREELIST_ TRUNK - Page-type tag for a freelist trunk page. Distinct from existing page tags
(
2 = TableLeaf,3 = Overflow,4 = InteriorNode);1was retired.
Functions§
- encode_
trunk - Encodes a single freelist trunk page into the given buffer.
- freelist_
to_ deque - Helper: collect a freelist into a
VecDeque<u32>sorted ascending — the ordering thePageAllocatoruses to draw next free pages from. - read_
freelist - Walks the freelist chain rooted at
headand returns every free leaf-page ID. The trunk pages themselves are not included — they’re live metadata. Returns the trunk page numbers separately so the caller can release them (the next save re-encodes the freelist from scratch and frees the old trunks for reuse). - should_
auto_ vacuum - Returns
trueif the on-disk freelist (counting both leaf and trunk pages — they’re all reclaimable bytes) exceedsthresholdofheader.page_count, i.e. the file is bloated enough to be worth compacting. Returnsfalsefor tiny databases (underMIN_PAGES_FOR_AUTO_VACUUM) and for empty freelists, both as fast paths to keep the auto-VACUUM hook cheap on the common case. - stage_
freelist - Stages the freelist chain into the pager.
free_pagesis the full set of pages that need persisted-on-the-freelist treatment — both the trunk pages (metadata) and the leaf entries (the actually-free leaves).