use crate::wal::WALRecord;
use std::io::Write;
#[derive(Debug, Default)]
pub struct LocalWAL {
buffer: Vec<u8>,
count: usize,
size: usize,
}
impl LocalWAL {
pub fn new() -> Self {
Self::default()
}
fn write_record(&mut self, record: &WALRecord) {
use akar_common::serialization::Serialize;
match record {
WALRecord::Insert { table_id, data } => {
self.buffer.write_all(b"I").unwrap();
table_id.serialize(&mut self.buffer).unwrap();
(data.len() as u32).serialize(&mut self.buffer).unwrap();
self.buffer.write_all(data).unwrap();
self.size += 1 + 8 + 4 + data.len();
}
WALRecord::Delete { table_id, row_id } => {
self.buffer.write_all(b"D").unwrap();
table_id.serialize(&mut self.buffer).unwrap();
row_id.serialize(&mut self.buffer).unwrap();
self.size += 1 + 8 + 8;
}
WALRecord::Update {
table_id,
row_id,
column,
data,
} => {
self.buffer.write_all(b"U").unwrap();
table_id.serialize(&mut self.buffer).unwrap();
row_id.serialize(&mut self.buffer).unwrap();
column.serialize(&mut self.buffer).unwrap();
(data.len() as u32).serialize(&mut self.buffer).unwrap();
self.buffer.write_all(data).unwrap();
self.size += 1 + 8 + 8 + 4 + 4 + data.len();
}
WALRecord::UpdateFsm { page_idx, is_free } => {
self.buffer.write_all(b"F").unwrap();
page_idx.serialize(&mut self.buffer).unwrap();
let is_free_u8: u8 = if *is_free { 1 } else { 0 };
is_free_u8.serialize(&mut self.buffer).unwrap();
self.size += 1 + 8 + 1;
}
WALRecord::ColumnWrite {
table_id,
col_id,
page_id,
data,
} => {
self.buffer.write_all(b"W").unwrap();
table_id.serialize(&mut self.buffer).unwrap();
col_id.serialize(&mut self.buffer).unwrap();
page_id.serialize(&mut self.buffer).unwrap();
(data.len() as u32).serialize(&mut self.buffer).unwrap();
self.buffer.write_all(data).unwrap();
self.size += 1 + 8 + 4 + 8 + 4 + data.len();
}
WALRecord::Commit { transaction_id } => {
self.buffer.write_all(b"C").unwrap();
transaction_id.serialize(&mut self.buffer).unwrap();
self.size += 1 + 8;
}
WALRecord::Rollback { transaction_id } => {
self.buffer.write_all(b"R").unwrap();
transaction_id.serialize(&mut self.buffer).unwrap();
self.size += 1 + 8;
}
WALRecord::Checkpoint => {
self.buffer.write_all(b"K").unwrap();
self.size += 1;
}
WALRecord::LocalWALData { data } => {
self.buffer.write_all(b"L").unwrap();
(data.len() as u32).serialize(&mut self.buffer).unwrap();
self.buffer.write_all(data).unwrap();
self.size += 1 + 4 + data.len();
}
WALRecord::CreateTable { table_id }
| WALRecord::DropTable { table_id }
| WALRecord::AlterTable { table_id }
| WALRecord::CreateIndex { table_id }
| WALRecord::DropIndex { table_id }
| WALRecord::CreateSequence { table_id } => {
let tag: u8 = match record {
WALRecord::CreateTable { .. } => b'T',
WALRecord::DropTable { .. } => b'A',
WALRecord::AlterTable { .. } => b'M',
WALRecord::CreateIndex { .. } => b'N',
WALRecord::DropIndex { .. } => b'X',
WALRecord::CreateSequence { .. } => b'Q',
_ => unreachable!(),
};
self.buffer.write_all(&[tag]).unwrap();
table_id.serialize(&mut self.buffer).unwrap();
self.size += 1 + 8;
}
}
self.count += 1;
}
pub fn log_insert(&mut self, table_id: u64, data: Vec<u8>) {
self.write_record(&WALRecord::Insert { table_id, data });
}
pub fn log_delete(&mut self, table_id: u64, row_id: u64) {
self.write_record(&WALRecord::Delete { table_id, row_id });
}
pub fn log_update(&mut self, table_id: u64, row_id: u64, column: u32, data: Vec<u8>) {
self.write_record(&WALRecord::Update {
table_id,
row_id,
column,
data,
});
}
pub fn log_column_write(&mut self, table_id: u64, col_id: u32, page_id: u64, data: Vec<u8>) {
self.write_record(&WALRecord::ColumnWrite {
table_id,
col_id,
page_id,
data,
});
}
pub fn log_begin_transaction(&mut self) {
}
pub fn log_commit(&mut self, transaction_id: u64) {
self.write_record(&WALRecord::Commit { transaction_id });
}
pub fn log_rollback(&mut self, transaction_id: u64) {
self.write_record(&WALRecord::Rollback { transaction_id });
}
pub fn log_checkpoint(&mut self) {
self.write_record(&WALRecord::Checkpoint);
}
pub fn buffer(&self) -> &[u8] {
&self.buffer
}
pub fn into_buffer(mut self) -> Vec<u8> {
std::mem::take(&mut self.buffer)
}
pub fn count(&self) -> usize {
self.count
}
pub fn size(&self) -> usize {
self.size
}
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
pub fn clear(&mut self) {
self.buffer.clear();
self.count = 0;
self.size = 0;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_local_wal_empty() {
let lwal = LocalWAL::new();
assert!(lwal.is_empty());
assert_eq!(lwal.count(), 0);
assert_eq!(lwal.size(), 0);
}
#[test]
fn test_local_wal_insert_record() {
let mut lwal = LocalWAL::new();
lwal.log_insert(1, vec![0x01, 0x02, 0x03]);
assert!(!lwal.is_empty());
assert_eq!(lwal.count(), 1);
assert!(lwal.size() > 0);
}
#[test]
fn test_local_wal_multiple_records() {
let mut lwal = LocalWAL::new();
lwal.log_insert(1, vec![0x01]);
lwal.log_delete(1, 42);
lwal.log_update(1, 42, 0, vec![0x05]);
lwal.log_commit(100);
assert_eq!(lwal.count(), 4);
assert!(lwal.size() > 0);
}
#[test]
fn test_local_wal_clear() {
let mut lwal = LocalWAL::new();
lwal.log_insert(1, vec![0x01]);
lwal.clear();
assert!(lwal.is_empty());
assert_eq!(lwal.count(), 0);
}
#[test]
fn test_local_wal_into_buffer() {
let mut lwal = LocalWAL::new();
lwal.log_insert(1, vec![0x01, 0x02]);
let buf = lwal.into_buffer();
assert!(!buf.is_empty());
}
#[test]
fn test_local_wal_buffer_content() {
let mut lwal = LocalWAL::new();
lwal.log_insert(42, vec![0xAB, 0xCD]);
let buf = lwal.buffer();
assert_eq!(buf[0], b'I', "First byte should be 'I' for Insert");
}
}