sqlrite/sql/pager/page.rs
1//! Page layout primitives.
2//!
3//! Every database file is a sequence of fixed-size pages. Page 0 holds the
4//! `DbHeader`; every other page is a typed, chained payload container.
5//!
6//! Layout of a non-header page (`PAGE_SIZE` bytes total):
7//! ```text
8//! 0..1 PageType tag (u8)
9//! 1..5 next-page (u32 LE; 0 = end of chain)
10//! 5..7 payload length (u16 LE; bytes used in the payload area)
11//! 7..end payload bytes
12//! ```
13
14use crate::error::{Result, SQLRiteError};
15
16/// Size of every page in bytes. SQLite's default too — small enough to fit
17/// in one disk sector group, large enough to carry meaningful payload.
18pub const PAGE_SIZE: usize = 4096;
19
20/// Bytes consumed by the per-page header (type + next-ptr + payload-len).
21pub const PAGE_HEADER_SIZE: usize = 7;
22
23/// Usable payload bytes per page after subtracting the header.
24pub const PAYLOAD_PER_PAGE: usize = PAGE_SIZE - PAGE_HEADER_SIZE;
25
26/// Identifies what kind of content a page holds.
27///
28/// Phase 3c retired the `SchemaRoot` tag (tag value `1`) because the
29/// schema catalog is now stored as a regular table (`sqlrite_master`)
30/// with leaf pages. Tag `1` remains reserved so future variants don't
31/// alias it.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33#[repr(u8)]
34pub enum PageType {
35 /// Leaf page of a table — holds a slot directory and cells.
36 TableLeaf = 2,
37 /// Continuation page carrying the spilled body of an oversized cell.
38 Overflow = 3,
39 /// Interior B-Tree node — holds a slot directory of dividers pointing
40 /// at child pages plus a rightmost-child pointer in the payload header.
41 InteriorNode = 4,
42}
43
44impl PageType {
45 // Used by integrity-check paths. Direct `page_buf[0] == TableLeaf as u8`
46 // compares are how current call sites check page types.
47 #[allow(dead_code)]
48 pub fn from_u8(v: u8) -> Result<PageType> {
49 match v {
50 2 => Ok(PageType::TableLeaf),
51 3 => Ok(PageType::Overflow),
52 4 => Ok(PageType::InteriorNode),
53 other => Err(SQLRiteError::Internal(format!(
54 "unknown page type tag {other}"
55 ))),
56 }
57 }
58}
59
60// The actual encoding/decoding of a page into/out of a `PAGE_SIZE`-byte
61// buffer lives in `pager/mod.rs`; those helpers used to live here but were
62// inlined once the `Pager` took over raw byte I/O.