use super::super::objects::Table;
use super::super::transactions::TransactionId;
use super::block_layer::file_manager2::{FileManager2, FileManager2Error};
use super::block_layer::free_space_manager::{FreeSpaceManager, FreeSpaceManagerError, FreeStat};
use super::format_traits::Serializable;
use super::page_formats::{PageData, PageDataError, PageId, PageOffset, PageType, UInt12};
use super::row_formats::{ItemPointer, RowData, RowDataError};
use super::EncodedSize;
use crate::engine::objects::SqlTuple;
use async_stream::try_stream;
use futures::stream::Stream;
use std::sync::Arc;
use thiserror::Error;
#[derive(Clone)]
pub struct RowManager {
file_manager: Arc<FileManager2>,
free_space_manager: FreeSpaceManager,
}
impl RowManager {
pub fn new(
file_manager: Arc<FileManager2>,
free_space_manager: FreeSpaceManager,
) -> RowManager {
RowManager {
file_manager,
free_space_manager,
}
}
pub async fn insert_row(
&self,
current_tran_id: TransactionId,
table: &Arc<Table>,
user_data: SqlTuple,
) -> Result<ItemPointer, RowManagerError> {
self.insert_row_internal(current_tran_id, table, user_data)
.await
}
pub async fn delete_row(
&self,
current_tran_id: TransactionId,
table: &Arc<Table>,
row_pointer: ItemPointer,
) -> Result<(), RowManagerError> {
let page_id = PageId {
resource_key: table.id,
page_type: PageType::Data,
};
let (page, page_guard) = self
.file_manager
.get_page_for_update(&page_id, &row_pointer.page)
.await?;
let mut page = PageData::parse(table, row_pointer.page, &page)?;
let mut row = page
.get_row(row_pointer.count)
.ok_or(RowManagerError::NonExistentRow(
row_pointer.count,
row_pointer.page,
))?
.clone();
if row.max.is_some() {
return Err(RowManagerError::AlreadyDeleted(
row_pointer.count,
row.max.unwrap(),
));
}
row.max = Some(current_tran_id);
page.update(row, row_pointer.count)?;
let new_page = page.serialize_and_pad();
self.file_manager.update_page(page_guard, new_page).await?;
Ok(())
}
pub async fn update_row(
&mut self,
current_tran_id: TransactionId,
table: &Arc<Table>,
row_pointer: ItemPointer,
new_user_data: SqlTuple,
) -> Result<ItemPointer, RowManagerError> {
let page_id = PageId {
resource_key: table.id,
page_type: PageType::Data,
};
let (old_page_buffer, old_guard) = self
.file_manager
.get_page_for_update(&page_id, &row_pointer.page)
.await?;
let mut old_page = PageData::parse(table, row_pointer.page, &old_page_buffer)?;
let mut old_row = old_page
.get_row(row_pointer.count)
.ok_or(RowManagerError::NonExistentRow(
row_pointer.count,
row_pointer.page,
))?
.clone();
if old_row.max.is_some() {
return Err(RowManagerError::AlreadyDeleted(
row_pointer.count,
old_row.max.unwrap(),
));
}
let new_row_len = RowData::encoded_size(&new_user_data);
let new_row_pointer;
if old_page.can_fit(new_row_len) {
new_row_pointer = old_page.insert(current_tran_id, table, new_user_data)?;
} else {
self.free_space_manager
.mark_page(page_id, row_pointer.page, FreeStat::Full)
.await?;
new_row_pointer = self
.insert_row_internal(current_tran_id, table, new_user_data)
.await?;
}
old_row.max = Some(current_tran_id);
old_row.item_pointer = new_row_pointer;
old_page.update(old_row, row_pointer.count)?;
let old_page_buffer = old_page.serialize_and_pad();
self.file_manager
.update_page(old_guard, old_page_buffer)
.await?;
Ok(new_row_pointer)
}
pub async fn get(
&self,
table: &Arc<Table>,
row_pointer: ItemPointer,
) -> Result<RowData, RowManagerError> {
let page_id = PageId {
resource_key: table.id,
page_type: PageType::Data,
};
let (page_buffer, _page_guard) = self
.file_manager
.get_page(&page_id, &row_pointer.page)
.await?;
let page = PageData::parse(table, row_pointer.page, &page_buffer)?;
let row = page
.get_row(row_pointer.count)
.ok_or(RowManagerError::NonExistentRow(
row_pointer.count,
row_pointer.page,
))?
.clone();
Ok(row)
}
pub fn get_stream(
&self,
table: &Arc<Table>,
) -> impl Stream<Item = Result<RowData, RowManagerError>> {
let page_id = PageId {
resource_key: table.id,
page_type: PageType::Data,
};
let file_manager = self.file_manager.clone();
let table = table.clone();
try_stream! {
let mut page_num = PageOffset(0);
loop {
match file_manager.get_page(&page_id, &page_num).await {
Ok((buffer, _guard)) => {
let page = PageData::parse(&table, page_num, &buffer)?;
for await row in page.get_stream() {
yield row;
}
},
Err(_) => {
return ();
}
}
page_num += PageOffset(1);
}
}
}
async fn insert_row_internal(
&self,
current_tran_id: TransactionId,
table: &Arc<Table>,
user_data: SqlTuple,
) -> Result<ItemPointer, RowManagerError> {
let page_id = PageId {
resource_key: table.id,
page_type: PageType::Data,
};
let user_data_size = RowData::encoded_size(&user_data);
loop {
let next_free_page = self.free_space_manager.get_next_free_page(page_id).await?;
match self
.file_manager
.get_page_for_update(&page_id, &next_free_page)
.await
{
Ok((buffer, page_guard)) => {
let mut page = PageData::parse(table, next_free_page, &buffer)?;
if page.can_fit(user_data_size) {
let new_row_pointer = page.insert(current_tran_id, table, user_data)?;
let buffer = page.serialize_and_pad();
self.file_manager.update_page(page_guard, buffer).await?;
return Ok(new_row_pointer);
} else {
self.free_space_manager
.mark_page(page_id, next_free_page, FreeStat::Full)
.await?;
continue;
}
}
Err(_) => {
let (new_page_offset, new_page_guard) =
self.file_manager.get_next_offset(&page_id).await?;
let mut new_page = PageData::new(new_page_offset);
let new_row_pointer = new_page.insert(current_tran_id, table, user_data)?;
let new_page_buffer = new_page.serialize_and_pad();
self.file_manager
.add_page(new_page_guard, new_page_buffer)
.await?;
return Ok(new_row_pointer);
}
}
}
}
}
#[derive(Error, Debug)]
pub enum RowManagerError {
#[error(transparent)]
PageDataError(#[from] PageDataError),
#[error(transparent)]
FileManager2Error(#[from] FileManager2Error),
#[error(transparent)]
FreeSpaceManagerError(#[from] FreeSpaceManagerError),
#[error(transparent)]
RowDataError(#[from] RowDataError),
#[error("Page {0} does not exist")]
NonExistentPage(PageOffset),
#[error("Row {0} in Page {1} does not exist")]
NonExistentRow(UInt12, PageOffset),
#[error("Row {0} already deleted in {1}")]
AlreadyDeleted(UInt12, TransactionId),
#[error("Row {0} is not visible")]
NotVisibleRow(RowData),
}
#[cfg(test)]
mod tests {
use super::*;
use crate::engine::get_row;
use crate::engine::get_table;
use futures::pin_mut;
use tempfile::TempDir;
use tokio_stream::StreamExt;
#[tokio::test]
async fn test_row_manager_mass_insert() -> Result<(), Box<dyn std::error::Error>> {
let tmp = TempDir::new()?;
let tmp_dir = tmp.path().as_os_str().to_os_string();
let table = get_table();
let fm = Arc::new(FileManager2::new(tmp_dir.clone())?);
let fsm = FreeSpaceManager::new(fm.clone());
let rm = RowManager::new(fm, fsm);
let tran_id = TransactionId::new(1);
for i in 0..50 {
rm.clone()
.insert_row(tran_id, &table, get_row(i.to_string()))
.await?;
}
drop(rm);
let fm = Arc::new(FileManager2::new(tmp_dir.clone())?);
let fsm = FreeSpaceManager::new(fm.clone());
let rm = RowManager::new(fm, fsm);
pin_mut!(rm);
let result_rows: Vec<RowData> = rm
.clone()
.get_stream(&table)
.map(Result::unwrap)
.collect()
.await;
assert_eq!(result_rows.len(), 50);
result_rows
.iter()
.enumerate()
.take(50)
.map(|(i, row)| {
let sample_row = get_row(i.to_string());
assert_eq!(row.user_data, sample_row);
})
.for_each(drop);
Ok(())
}
#[tokio::test]
async fn test_row_manager_crud() -> Result<(), Box<dyn std::error::Error>> {
let tmp = TempDir::new()?;
let tmp_dir = tmp.path().as_os_str().to_os_string();
let table = get_table();
let fm = Arc::new(FileManager2::new(tmp_dir.clone())?);
let fsm = FreeSpaceManager::new(fm.clone());
let mut rm = RowManager::new(fm, fsm);
let tran_id = TransactionId::new(1);
let insert_pointer = rm
.insert_row(tran_id, &table, get_row("test".to_string()))
.await?;
let tran_id_2 = TransactionId::new(3);
let update_pointer = rm
.update_row(
tran_id_2,
&table,
insert_pointer,
get_row("test2".to_string()),
)
.await?;
pin_mut!(rm);
let result_rows: Vec<RowData> = rm.get_stream(&table).map(Result::unwrap).collect().await;
assert_eq!(result_rows.len(), 2);
let tran_id_3 = TransactionId::new(3);
rm.delete_row(tran_id_3, &table, update_pointer).await?;
Ok(())
}
}