use std::sync::Arc;
use vibesql_types::DataType;
use super::{
super::calculate_degree,
datatype_serialization::{deserialize_datatype, serialize_datatype},
structure::{InternalNode, Key, LeafNode, RowId},
};
use crate::{
page::{PageId, PageManager},
StorageError,
};
#[derive(Debug, Clone)]
pub enum DiskUndoOp {
DeleteSpecific { key: Key, row_id: RowId },
Reinsert { key: Key, row_ids: Vec<RowId> },
}
mod bulk_load;
mod delete;
mod insert;
mod query;
mod rebalance;
#[derive(Debug)]
pub struct BTreeIndex {
pub(super) root_page_id: PageId,
pub(super) key_schema: Vec<DataType>,
pub(super) degree: usize,
pub(super) height: usize,
pub(super) page_manager: Arc<PageManager>,
#[allow(dead_code)]
pub(super) metadata_page_id: PageId,
pub(super) undo_log: Option<Vec<DiskUndoOp>>,
}
impl BTreeIndex {
pub fn key_schema(&self) -> &[DataType] {
&self.key_schema
}
pub fn begin_undo_logging(&mut self) {
if self.undo_log.is_none() {
self.undo_log = Some(Vec::new());
}
}
pub fn is_undo_logging(&self) -> bool {
self.undo_log.is_some()
}
pub(super) fn record_undo_reinsert(&mut self, key: Key, row_ids: Vec<RowId>) {
if row_ids.is_empty() {
return;
}
if let Some(log) = self.undo_log.as_mut() {
log.push(DiskUndoOp::Reinsert { key, row_ids });
}
}
pub fn clear_undo_log(&mut self) {
self.undo_log = None;
}
pub fn rollback_undo_log(&mut self) -> Result<(), StorageError> {
let log = match self.undo_log.take() {
Some(log) => log,
None => return Ok(()),
};
self.replay_inverses(log)
}
pub fn undo_log_marker(&self) -> Option<usize> {
self.undo_log.as_ref().map(|log| log.len())
}
pub fn rollback_undo_log_to(&mut self, marker: usize) -> Result<(), StorageError> {
let suffix = match self.undo_log.as_mut() {
Some(log) if log.len() > marker => log.split_off(marker),
_ => return Ok(()),
};
let prefix = self.undo_log.take();
let result = self.replay_inverses(suffix);
self.undo_log = prefix.or(Some(Vec::new()));
result
}
fn replay_inverses(&mut self, log: Vec<DiskUndoOp>) -> Result<(), StorageError> {
debug_assert!(
self.undo_log.is_none(),
"replay_inverses must run with the undo-log detached so inverses are \
not re-recorded",
);
for op in log.into_iter().rev() {
match op {
DiskUndoOp::DeleteSpecific { key, row_id } => {
self.delete_specific(&key, row_id)?;
}
DiskUndoOp::Reinsert { key, row_ids } => {
for row_id in row_ids {
self.insert(key.clone(), row_id)?;
}
}
}
}
Ok(())
}
pub(crate) fn collect_all_entries(&self) -> Result<Vec<(Key, Vec<RowId>)>, StorageError> {
let mut entries: Vec<(Key, Vec<RowId>)> = Vec::new();
if self.height == 0 {
return Ok(entries);
}
let mut current_leaf = self.find_leftmost_leaf()?;
loop {
for (key, row_ids) in ¤t_leaf.entries {
entries.push((key.clone(), row_ids.clone()));
}
if current_leaf.next_leaf == super::super::NULL_PAGE_ID {
break;
}
current_leaf = self.read_leaf_node(current_leaf.next_leaf)?;
}
Ok(entries)
}
pub(crate) fn rebuild_in_place_logged(
&mut self,
new_entries: Vec<(Key, RowId)>,
) -> Result<(), StorageError> {
debug_assert!(
self.is_undo_logging(),
"rebuild_in_place_logged must only be used while undo-logging is armed; \
the non-transactional path should use bulk_load + swap",
);
let existing = self.collect_all_entries()?;
for (key, _row_ids) in existing {
self.delete(&key)?;
}
for (key, row_id) in new_entries {
self.insert(key, row_id)?;
}
Ok(())
}
pub fn new(
page_manager: Arc<PageManager>,
key_schema: Vec<DataType>,
) -> Result<Self, StorageError> {
let degree = calculate_degree(&key_schema);
let metadata_page_id = 0;
let root_page_id = page_manager.allocate_page()?;
let root_leaf = LeafNode::new(root_page_id);
let index = BTreeIndex {
root_page_id,
key_schema,
degree,
height: 1,
page_manager,
metadata_page_id,
undo_log: None,
};
index.write_leaf_node(&root_leaf)?;
index.save_metadata()?;
Ok(index)
}
pub fn load(page_manager: Arc<PageManager>) -> Result<Self, StorageError> {
let metadata_page = page_manager.read_page(0)?;
let mut offset = 0;
let root_page_id_bytes: [u8; 8] =
metadata_page.data[offset..offset + 8].try_into().unwrap();
let root_page_id = u64::from_le_bytes(root_page_id_bytes);
offset += 8;
let degree_bytes: [u8; 2] = metadata_page.data[offset..offset + 2].try_into().unwrap();
let degree = u16::from_le_bytes(degree_bytes) as usize;
offset += 2;
let height_bytes: [u8; 2] = metadata_page.data[offset..offset + 2].try_into().unwrap();
let height = u16::from_le_bytes(height_bytes) as usize;
offset += 2;
let schema_len_bytes: [u8; 2] = metadata_page.data[offset..offset + 2].try_into().unwrap();
let schema_len = u16::from_le_bytes(schema_len_bytes) as usize;
offset += 2;
let mut key_schema = Vec::new();
for _ in 0..schema_len {
let (data_type, bytes_read) = deserialize_datatype(&metadata_page.data[offset..])?;
key_schema.push(data_type);
offset += bytes_read;
}
Ok(BTreeIndex {
root_page_id,
key_schema,
degree,
height,
page_manager,
metadata_page_id: 0,
undo_log: None,
})
}
fn serialize_metadata(&self) -> Result<crate::page::Page, StorageError> {
let mut metadata_page = self.page_manager.read_page(0)?;
let mut offset = 0;
metadata_page.data[offset..offset + 8].copy_from_slice(&self.root_page_id.to_le_bytes());
offset += 8;
metadata_page.data[offset..offset + 2].copy_from_slice(&(self.degree as u16).to_le_bytes());
offset += 2;
metadata_page.data[offset..offset + 2].copy_from_slice(&(self.height as u16).to_le_bytes());
offset += 2;
metadata_page.data[offset..offset + 2]
.copy_from_slice(&(self.key_schema.len() as u16).to_le_bytes());
offset += 2;
for data_type in &self.key_schema {
let bytes_written = serialize_datatype(data_type, &mut metadata_page.data[offset..])?;
offset += bytes_written;
}
metadata_page.mark_dirty();
Ok(metadata_page)
}
pub(super) fn save_metadata(&self) -> Result<(), StorageError> {
let mut page = self.serialize_metadata()?;
self.page_manager.write_page(&mut page)?;
Ok(())
}
pub(super) fn save_metadata_no_sync(&self) -> Result<(), StorageError> {
let mut page = self.serialize_metadata()?;
self.page_manager.write_page_no_sync(&mut page)?;
Ok(())
}
pub fn degree(&self) -> usize {
self.degree
}
pub fn height(&self) -> usize {
self.height
}
pub fn root_page_id(&self) -> PageId {
self.root_page_id
}
#[allow(dead_code)]
pub(crate) fn write_internal_node(&self, node: &InternalNode) -> Result<(), StorageError> {
super::super::serialize::write_internal_node(&self.page_manager, node, self.degree)
}
pub(crate) fn write_leaf_node(&self, node: &LeafNode) -> Result<(), StorageError> {
super::super::serialize::write_leaf_node(&self.page_manager, node, self.degree)
}
#[allow(dead_code)]
pub(crate) fn read_internal_node(&self, page_id: PageId) -> Result<InternalNode, StorageError> {
super::super::serialize::read_internal_node(&self.page_manager, page_id)
}
#[allow(dead_code)]
pub(crate) fn read_leaf_node(&self, page_id: PageId) -> Result<LeafNode, StorageError> {
super::super::serialize::read_leaf_node(&self.page_manager, page_id)
}
}