use std::sync::Arc;
use vibesql_types::DataType;
use super::{
super::{
super::{
calculate_degree, estimate_max_key_size,
serialize::{write_internal_node_no_sync, write_leaf_node_no_sync},
},
structure::{InternalNode, Key, LeafNode},
},
BTreeIndex,
};
use crate::{
page::{PageId, PageManager, PAGE_SIZE},
StorageError,
};
impl BTreeIndex {
pub fn bulk_load(
sorted_entries: Vec<(Key, super::super::structure::RowId)>,
key_schema: Vec<DataType>,
page_manager: Arc<PageManager>,
) -> Result<Self, StorageError> {
let degree = calculate_degree(&key_schema);
let metadata_page_id = 0;
if sorted_entries.is_empty() {
return Self::new(page_manager, key_schema);
}
let key_size = estimate_max_key_size(&key_schema);
let fixed_overhead = 3 + key_size + 5 + 16; let max_row_ids_per_entry = if fixed_overhead >= PAGE_SIZE {
1 } else {
(PAGE_SIZE - fixed_overhead) / 8
};
let max_row_ids_per_entry = (max_row_ids_per_entry * 4 / 5).max(1);
let mut grouped_entries: Vec<(Key, Vec<super::super::structure::RowId>)> = Vec::new();
for (key, row_id) in sorted_entries {
if let Some((last_key, row_ids)) = grouped_entries.last_mut() {
if last_key == &key {
row_ids.push(row_id);
continue;
}
}
grouped_entries.push((key, vec![row_id]));
}
let mut chunked_entries: Vec<(Key, Vec<super::super::structure::RowId>)> = Vec::new();
for (key, row_ids) in grouped_entries {
if row_ids.len() <= max_row_ids_per_entry {
chunked_entries.push((key, row_ids));
} else {
for chunk in row_ids.chunks(max_row_ids_per_entry) {
chunked_entries.push((key.clone(), chunk.to_vec()));
}
}
}
let grouped_entries = chunked_entries;
let typical_entry_size = key_size + 1 + 8;
let available_for_entries = PAGE_SIZE.saturating_sub(19); let leaf_capacity = (available_for_entries / typical_entry_size).max(1);
let leaf_capacity = ((leaf_capacity * 3) / 4).max(1);
let mut leaf_page_ids = Vec::new();
let mut prev_leaf_page_id: Option<PageId> = None;
let mut entries_iter = grouped_entries.into_iter().peekable();
while entries_iter.peek().is_some() {
let page_id = page_manager.allocate_page()?;
let mut leaf = LeafNode::new(page_id);
let mut current_size = 0;
let max_size = available_for_entries;
while let Some((_key, row_ids)) = entries_iter.peek() {
let varint_size = if row_ids.len() < 128 {
1
} else if row_ids.len() < 16384 {
2
} else {
3
};
let entry_size = key_size + varint_size + row_ids.len() * 8;
if current_size > 0 && current_size + entry_size > max_size {
break; }
current_size += entry_size;
let (key, row_ids) = entries_iter.next().unwrap();
leaf.entries.push((key, row_ids));
if leaf.entries.len() >= leaf_capacity {
break;
}
}
if let Some(prev_id) = prev_leaf_page_id {
leaf.prev_leaf = prev_id;
let mut prev_leaf =
super::super::super::serialize::read_leaf_node(&page_manager, prev_id)?;
prev_leaf.next_leaf = page_id;
write_leaf_node_no_sync(&page_manager, &prev_leaf, degree)?;
}
write_leaf_node_no_sync(&page_manager, &leaf, degree)?;
leaf_page_ids.push(page_id);
prev_leaf_page_id = Some(page_id);
}
let mut current_level = leaf_page_ids;
let mut height = 1;
while current_level.len() > 1 {
height += 1;
let mut next_level = Vec::new();
let internal_capacity = (degree * 3) / 4;
let internal_capacity = internal_capacity.max(2);
let mut level_iter = current_level.into_iter().peekable();
while level_iter.peek().is_some() {
let page_id = page_manager.allocate_page()?;
let mut internal = InternalNode::new(page_id);
if let Some(child_page_id) = level_iter.next() {
internal.children.push(child_page_id);
}
for _ in 1..internal_capacity {
if let Some(child_page_id) = level_iter.next() {
let first_key = read_first_key_from_page(&page_manager, child_page_id)?;
internal.keys.push(first_key);
internal.children.push(child_page_id);
} else {
break;
}
}
write_internal_node_no_sync(&page_manager, &internal, degree)?;
next_level.push(page_id);
}
current_level = next_level;
}
let root_page_id = current_level[0];
let index = BTreeIndex {
root_page_id,
key_schema,
degree,
height,
page_manager,
metadata_page_id,
undo_log: None,
};
index.save_metadata_no_sync()?;
index.page_manager.sync()?;
Ok(index)
}
}
pub(super) fn read_first_key_from_page(
page_manager: &Arc<PageManager>,
page_id: PageId,
) -> Result<Key, StorageError> {
let page = page_manager.read_page(page_id)?;
let page_type = page.data[0];
if page_type == super::super::super::PAGE_TYPE_LEAF {
let leaf = super::super::super::serialize::read_leaf_node(page_manager, page_id)?;
if leaf.entries.is_empty() {
return Err(StorageError::IoError("Leaf node has no entries".to_string()));
}
Ok(leaf.entries[0].0.clone())
} else if page_type == super::super::super::PAGE_TYPE_INTERNAL {
let internal = super::super::super::serialize::read_internal_node(page_manager, page_id)?;
if internal.keys.is_empty() {
if internal.children.is_empty() {
return Err(StorageError::IoError(
"Internal node has no keys or children".to_string(),
));
}
return read_first_key_from_page(page_manager, internal.children[0]);
}
Ok(internal.keys[0].clone())
} else {
Err(StorageError::IoError(format!("Invalid page type: {}", page_type)))
}
}