///|
/// auto-vacuum Ptrmap 的五种页面反向引用类型。
pub(all) enum PtrmapType {
RootPage
FreePage
FirstOverflow
NextOverflow
NonRootBtree
} derive(Debug, Eq)
///|
pub extend PtrmapType with @debug.Debug::{to_repr}
///|
pub extend PtrmapType with Eq::{equal, not_equal}
///|
/// 已校验的反向指针;page_number 和 map_page 从 1 开始,parent_page 为格式存储的父页值,根页/空闲页使用 0。
pub(all) struct PtrmapEntry {
page_number : Int
map_page : Int
kind : PtrmapType
parent_page : Int
} derive(Debug, Eq)
///|
pub extend PtrmapEntry with @debug.Debug::{to_repr}
///|
pub extend PtrmapEntry with Eq::{equal, not_equal}
///|
fn ptrmap_page_number(number : Int, usable : Int, page_size : Int) -> Int {
let step = usable / 5 + 1
let map_page = (number - 2) / step * step + 2
let lock_page = 1073741824 / page_size + 1
if map_page == lock_page {
map_page + 1
} else {
map_page
}
}
///|
fn Database::visit_ptrmap(
self : Database,
visit : (PtrmapEntry) -> Bool raise SqliteError,
trace? : InspectionTrace = InspectionTrace::new(),
) -> Bool raise SqliteError {
let cursor = self.ptrmap_cursor(trace)
while true {
match cursor.next() {
MapPage(number) => {
cursor.cached = self.read_page(number)
cursor.cached_number = number
}
MapEntry(entry) => if !visit(entry) { return false }
MapFinished => return true
}
} nobreak {
false
}
}
///|
/// 读取 auto-vacuum 反向指针;与真实页归属对照请使用 inspect_database。
/// 读取并校验 auto-vacuum 的 Ptrmap 条目;非 auto-vacuum 返回空数组。真实归属交叉检查由 inspect_database 完成。
pub fn Database::ptrmap_entries(
self : Database,
) -> Array[PtrmapEntry] raise SqliteError {
let entries : Array[PtrmapEntry] = []
ignore(
self.visit_ptrmap(entry => {
entries.push(entry)
true
}),
)
entries
}
///|
fn OwnershipState::check_root_positions(
self : OwnershipState,
db : Database,
) -> Unit {
let largest = db.header.largest_root_page.reinterpret_as_int()
let mut actual_largest = 1
for number, owner in self.owners {
if owner.kind == BtreeRoot {
if number > actual_largest {
actual_largest = number
}
} else if owner.kind != PointerMap &&
owner.kind != LockByte &&
number <= largest {
self.trace.at(PtrmapCheck, Some(number))
self.failed = true
self.issue(
ScanError(
None,
Invalid(
"auto-vacuum 的非根页出现在 largest root 之前:\{number}",
),
),
)
}
}
if actual_largest != largest {
self.trace.at(PtrmapCheck, Some(1), byte_offset=Some(52))
self.failed = true
self.issue(
ScanError(None, Invalid("largest root 与 schema 根页集合不一致")),
)
}
}
///|
fn OwnershipState::check_ptrmap_entry(
self : OwnershipState,
entry : PtrmapEntry,
) -> Bool {
if self.owners.get(entry.page_number) is Some(owner) {
let expected = match owner.kind {
BtreeRoot => RootPage
BtreeChild => NonRootBtree
OverflowFirst => FirstOverflow
OverflowContinuation => NextOverflow
FreelistTrunk | FreelistLeaf => FreePage
PointerMap | LockByte => return false
}
let parent = if expected == RootPage || expected == FreePage {
0
} else {
owner.parent_page.unwrap()
}
if entry.kind != expected || entry.parent_page != parent {
self.failed = true
self.issue(PtrmapMismatch(entry, owner))
}
} else {
// 尚无归属的页由最终覆盖检查报告,不根据 Ptrmap 推断真实归属。
return false
}
true
}