use crate::{
ast::Value,
catalog::objects::ColumnEntry,
common::interner::Interner,
storage::{
BufferPool, HeapFile, Storage, StorageError,
tuple::{deserialize_tuple, serialize_tuple},
},
};
const DEFAULT_POOL_CAPACITY: usize = 16;
pub struct TableHeap {
buffer_pool: BufferPool,
}
impl TableHeap {
pub fn open(
storage: &Storage,
db_name: &str,
schema_name: &str,
table_name: &str,
) -> Result<Self, StorageError> {
let path = storage.table_path(db_name, schema_name, table_name);
let heap_file = HeapFile::open(path)?;
let buffer_pool = BufferPool::new(heap_file, DEFAULT_POOL_CAPACITY);
Ok(Self { buffer_pool })
}
pub fn insert_tuple(
&mut self,
schema: &[ColumnEntry],
values: &[Value],
interner: &Interner,
) -> Result<(), StorageError> {
let bytes = serialize_tuple(schema, values, interner)?;
let (_page_id, frame_id) = if self.buffer_pool.num_pages() == 0 {
self.buffer_pool.new_page()?
} else {
let last_page_id = self.buffer_pool.num_pages() - 1;
let frame_id = self.buffer_pool.pin_page(last_page_id)?;
(last_page_id, frame_id)
};
let inserted = self.buffer_pool.get_page_mut(frame_id).insert_tuple(&bytes);
if inserted.is_some() {
self.buffer_pool.unpin_page(frame_id, true);
return Ok(());
}
self.buffer_pool.unpin_page(frame_id, false);
let (_new_page_id, new_frame_id) = self.buffer_pool.new_page()?;
let inserted = self
.buffer_pool
.get_page_mut(new_frame_id)
.insert_tuple(&bytes);
self.buffer_pool.unpin_page(new_frame_id, true);
match inserted {
Some(_) => Ok(()),
None => Err(StorageError::TupleError(
"tuple too large to fit in an empty page".to_string(),
)),
}
}
pub fn scan(
&mut self,
schema: &[ColumnEntry],
interner: &mut Interner,
) -> Result<Vec<Vec<Value>>, StorageError> {
let mut all_rows = Vec::new();
for page_id in 0..self.buffer_pool.num_pages() {
let frame_id = self.buffer_pool.pin_page(page_id)?;
let page = self.buffer_pool.get_page(frame_id);
for slot_id in 0..page.slot_count() {
if let Some(bytes) = page.get_tuple(slot_id) {
let values = deserialize_tuple(schema, bytes, interner)?;
all_rows.push(values);
}
}
self.buffer_pool.unpin_page(frame_id, false);
}
Ok(all_rows)
}
pub fn from_buffer_pool(bp: BufferPool) -> Self {
Self { buffer_pool: bp }
}
}