///|
/// Aggregate shape of one traversed SQLite B-tree.
pub(all) struct BtreeStatistics {
root_page : UInt64
total_pages : Int
interior_pages : Int
leaf_pages : Int
table_pages : Int
index_pages : Int
total_cells : Int
maximum_depth : Int
maximum_children : Int
} derive(Eq, Debug)
///|
pub fn summarize_btree(traversal : BtreeTraversal) -> BtreeStatistics {
let mut interior_pages = 0
let mut leaf_pages = 0
let mut table_pages = 0
let mut index_pages = 0
let mut total_cells = 0
let mut maximum_depth = 0
let mut maximum_children = 0
for visit in traversal.visits {
if visit.page_type.is_interior() {
interior_pages = interior_pages + 1
} else {
leaf_pages = leaf_pages + 1
}
if visit.page_type.is_table() {
table_pages = table_pages + 1
} else {
index_pages = index_pages + 1
}
total_cells = total_cells + visit.cell_count
if visit.depth > maximum_depth {
maximum_depth = visit.depth
}
if visit.child_pages.length() > maximum_children {
maximum_children = visit.child_pages.length()
}
}
{
root_page: traversal.root_page,
total_pages: traversal.visits.length(),
interior_pages,
leaf_pages,
table_pages,
index_pages,
total_cells,
maximum_depth,
maximum_children,
}
}
///|
pub fn BtreeStatistics::is_single_page(self : BtreeStatistics) -> Bool {
self.total_pages == 1 && self.maximum_depth == 0
}
///|
pub fn BtreeStatistics::average_cells_per_page(
self : BtreeStatistics,
) -> Double {
if self.total_pages == 0 {
0.0
} else {
self.total_cells.to_double() / self.total_pages.to_double()
}
}
///|
/// Every occurrence of one database page in a WAL, including frames that do
/// not become visible in the newest committed snapshot.
pub(all) struct WalPageHistory {
page_number : UInt64
frame_indexes : Array[Int]
valid_frame_indexes : Array[Int]
mut committed_frame : Int?
uncommitted_frame_indexes : Array[Int]
} derive(Eq, Debug)
///|
pub fn WalPageHistory::occurrence_count(self : WalPageHistory) -> Int {
self.frame_indexes.length()
}
///|
pub fn WalPageHistory::valid_occurrence_count(self : WalPageHistory) -> Int {
self.valid_frame_indexes.length()
}
///|
pub fn WalPageHistory::has_uncommitted_change(self : WalPageHistory) -> Bool {
self.uncommitted_frame_indexes.length() > 0
}
///|
pub fn WalPageHistory::latest_valid_frame(self : WalPageHistory) -> Int? {
if self.valid_frame_indexes.length() == 0 {
None
} else {
Some(self.valid_frame_indexes[self.valid_frame_indexes.length() - 1])
}
}
///|
fn find_page_history(
histories : Array[WalPageHistory],
page_number : UInt64,
) -> Int? {
for index = 0; index < histories.length(); index = index + 1 {
if histories[index].page_number == page_number {
return Some(index)
}
}
None
}
///|
pub fn WalFile::page_histories(self : WalFile) -> Array[WalPageHistory] {
let histories : Array[WalPageHistory] = []
let commit_limit = match self.last_commit_frame {
Some(value) => value
None => 0
}
for frame in self.frames {
let history_index = match find_page_history(histories, frame.page_number) {
Some(value) => value
None => {
histories.push({
page_number: frame.page_number,
frame_indexes: [],
valid_frame_indexes: [],
committed_frame: None,
uncommitted_frame_indexes: [],
})
histories.length() - 1
}
}
let history = histories[history_index]
history.frame_indexes.push(frame.index)
if frame.is_valid() {
history.valid_frame_indexes.push(frame.index)
if frame.index <= commit_limit {
history.committed_frame = Some(frame.index)
} else {
history.uncommitted_frame_indexes.push(frame.index)
}
}
}
histories
}
///|
pub fn WalFile::committed_transaction_count(self : WalFile) -> Int {
let mut count = 0
for transaction in self.transactions {
if transaction.committed {
count = count + 1
}
}
count
}
///|
pub fn WalFile::uncommitted_frame_count(self : WalFile) -> Int {
let mut count = 0
for history in self.page_histories() {
count = count + history.uncommitted_frame_indexes.length()
}
count
}
///|
pub(all) struct SnapshotStatistics {
commit_frame : Int
logical_page_count : UInt64
changed_pages : Int
changed_bytes : Int
changed_ranges : Int
pages_added_by_wal : Int
largest_page_change : Int
} derive(Eq, Debug)
///|
pub fn summarize_snapshot(
snapshot : SnapshotView,
) -> SnapshotStatistics raise ParseError {
let differences = snapshot.differences()
let mut changed_bytes = 0
let mut changed_ranges = 0
let mut pages_added_by_wal = 0
let mut largest_page_change = 0
for difference in differences {
changed_bytes = changed_bytes + difference.changed_bytes
changed_ranges = changed_ranges + difference.ranges.length()
if difference.page_number > snapshot.database.page_count {
pages_added_by_wal = pages_added_by_wal + 1
}
if difference.changed_bytes > largest_page_change {
largest_page_change = difference.changed_bytes
}
}
{
commit_frame: snapshot.max_frame,
logical_page_count: snapshot.logical_page_count,
changed_pages: differences.length(),
changed_bytes,
changed_ranges,
pages_added_by_wal,
largest_page_change,
}
}
///|
pub fn SnapshotStatistics::is_unchanged(self : SnapshotStatistics) -> Bool {
self.changed_pages == 0 && self.changed_bytes == 0
}
///|
pub fn SnapshotStatistics::average_changed_bytes_per_page(
self : SnapshotStatistics,
) -> Double {
if self.changed_pages == 0 {
0.0
} else {
self.changed_bytes.to_double() / self.changed_pages.to_double()
}
}