mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-08 09:44:21 +01:00
Merge 'check freelist count in integrity check' from Jussi Saurio
Closes #3003
This commit is contained in:
@@ -5518,6 +5518,13 @@ pub enum IntegrityCheckError {
|
||||
references: Vec<u64>,
|
||||
page_category: PageCategory,
|
||||
},
|
||||
#[error(
|
||||
"Freelist count mismatch. actual_count={actual_count}, expected_count={expected_count}"
|
||||
)]
|
||||
FreelistCountMismatch {
|
||||
actual_count: usize,
|
||||
expected_count: usize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
@@ -5528,6 +5535,12 @@ pub(crate) enum PageCategory {
|
||||
FreePage,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CheckFreelist {
|
||||
pub expected_count: usize,
|
||||
pub actual_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct IntegrityCheckPageEntry {
|
||||
page_idx: usize,
|
||||
@@ -5540,6 +5553,7 @@ pub struct IntegrityCheckState {
|
||||
first_leaf_level: Option<usize>,
|
||||
page_reference: HashMap<u64, u64>,
|
||||
page: Option<PageRef>,
|
||||
pub freelist_count: CheckFreelist,
|
||||
}
|
||||
|
||||
impl IntegrityCheckState {
|
||||
@@ -5549,9 +5563,17 @@ impl IntegrityCheckState {
|
||||
page_reference: HashMap::new(),
|
||||
first_leaf_level: None,
|
||||
page: None,
|
||||
freelist_count: CheckFreelist {
|
||||
expected_count: 0,
|
||||
actual_count: 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_expected_freelist_count(&mut self, count: usize) {
|
||||
self.freelist_count.expected_count = count;
|
||||
}
|
||||
|
||||
pub fn start(
|
||||
&mut self,
|
||||
page_idx: usize,
|
||||
@@ -5585,10 +5607,7 @@ impl IntegrityCheckState {
|
||||
) {
|
||||
let page_id = entry.page_idx as u64;
|
||||
let Some(previous) = self.page_reference.insert(page_id, referenced_by) else {
|
||||
// do not traverse free pages as they have no meaingful structured content
|
||||
if entry.page_category != PageCategory::FreePage {
|
||||
self.page_stack.push(entry);
|
||||
}
|
||||
self.page_stack.push(entry);
|
||||
return;
|
||||
};
|
||||
errors.push(IntegrityCheckError::PageReferencedMultipleTimes {
|
||||
@@ -5647,6 +5666,7 @@ pub fn integrity_check(
|
||||
|
||||
let contents = page.get_contents();
|
||||
if page_category == PageCategory::FreeListTrunk {
|
||||
state.freelist_count.actual_count += 1;
|
||||
let next_freelist_trunk_page = contents.read_u32_no_offset(0);
|
||||
if next_freelist_trunk_page != 0 {
|
||||
state.push_page(
|
||||
@@ -5676,6 +5696,10 @@ pub fn integrity_check(
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if page_category == PageCategory::FreePage {
|
||||
state.freelist_count.actual_count += 1;
|
||||
continue;
|
||||
}
|
||||
if page_category == PageCategory::Overflow {
|
||||
let next_overflow_page = contents.read_u32_no_offset(0);
|
||||
if next_overflow_page != 0 {
|
||||
|
||||
@@ -7363,6 +7363,9 @@ pub fn op_integrity_check(
|
||||
let mut current_root_idx = 0;
|
||||
// check freelist pages first, if there are any for database
|
||||
if freelist_trunk_page > 0 {
|
||||
let expected_freelist_count =
|
||||
return_if_io!(pager.with_header(|header| header.freelist_pages.get()));
|
||||
integrity_check_state.set_expected_freelist_count(expected_freelist_count as usize);
|
||||
integrity_check_state.start(
|
||||
freelist_trunk_page as usize,
|
||||
PageCategory::FreeListTrunk,
|
||||
@@ -7389,6 +7392,14 @@ pub fn op_integrity_check(
|
||||
*current_root_idx += 1;
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
} else {
|
||||
if integrity_check_state.freelist_count.actual_count
|
||||
!= integrity_check_state.freelist_count.expected_count
|
||||
{
|
||||
errors.push(IntegrityCheckError::FreelistCountMismatch {
|
||||
actual_count: integrity_check_state.freelist_count.actual_count,
|
||||
expected_count: integrity_check_state.freelist_count.expected_count,
|
||||
});
|
||||
}
|
||||
let message = if errors.is_empty() {
|
||||
"ok".to_string()
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user