akar_storage/
local_wal.rs1use crate::wal::WALRecord;
11use std::io::Write;
12
13#[derive(Debug, Default)]
19pub struct LocalWAL {
20 buffer: Vec<u8>,
22 count: usize,
24 size: usize,
26}
27
28impl LocalWAL {
29 pub fn new() -> Self {
31 Self::default()
32 }
33
34 pub fn log_record(&mut self, record: &WALRecord) {
36 self.write_record(record);
37 }
38
39 fn write_record(&mut self, record: &WALRecord) {
41 use akar_common::serialization::Serialize;
42 match record {
43 WALRecord::Insert { table_id, data } => {
44 self.buffer.write_all(b"I").unwrap();
45 table_id.serialize(&mut self.buffer).unwrap();
46 (data.len() as u32).serialize(&mut self.buffer).unwrap();
47 self.buffer.write_all(data).unwrap();
48 self.size += 1 + 8 + 4 + data.len();
49 }
50 WALRecord::Delete { table_id, row_id } => {
51 self.buffer.write_all(b"D").unwrap();
52 table_id.serialize(&mut self.buffer).unwrap();
53 row_id.serialize(&mut self.buffer).unwrap();
54 self.size += 1 + 8 + 8;
55 }
56 WALRecord::Update {
57 table_id,
58 row_id,
59 column,
60 data,
61 } => {
62 self.buffer.write_all(b"U").unwrap();
63 table_id.serialize(&mut self.buffer).unwrap();
64 row_id.serialize(&mut self.buffer).unwrap();
65 column.serialize(&mut self.buffer).unwrap();
66 (data.len() as u32).serialize(&mut self.buffer).unwrap();
67 self.buffer.write_all(data).unwrap();
68 self.size += 1 + 8 + 8 + 4 + 4 + data.len();
69 }
70 WALRecord::UpdateFsm { page_idx, is_free } => {
71 self.buffer.write_all(b"F").unwrap();
72 page_idx.serialize(&mut self.buffer).unwrap();
73 let is_free_u8: u8 = if *is_free { 1 } else { 0 };
74 is_free_u8.serialize(&mut self.buffer).unwrap();
75 self.size += 1 + 8 + 1;
76 }
77 WALRecord::ColumnWrite {
78 table_id,
79 col_id,
80 page_id,
81 data,
82 } => {
83 self.buffer.write_all(b"W").unwrap();
84 table_id.serialize(&mut self.buffer).unwrap();
85 col_id.serialize(&mut self.buffer).unwrap();
86 page_id.serialize(&mut self.buffer).unwrap();
87 (data.len() as u32).serialize(&mut self.buffer).unwrap();
88 self.buffer.write_all(data).unwrap();
89 self.size += 1 + 8 + 4 + 8 + 4 + data.len();
90 }
91 WALRecord::Commit { transaction_id } => {
92 self.buffer.write_all(b"C").unwrap();
93 transaction_id.serialize(&mut self.buffer).unwrap();
94 self.size += 1 + 8;
95 }
96 WALRecord::Rollback { transaction_id } => {
97 self.buffer.write_all(b"R").unwrap();
98 transaction_id.serialize(&mut self.buffer).unwrap();
99 self.size += 1 + 8;
100 }
101 WALRecord::Checkpoint => {
102 self.buffer.write_all(b"K").unwrap();
103 self.size += 1;
104 }
105 WALRecord::LocalWALData { data } => {
109 self.buffer.write_all(b"L").unwrap();
110 (data.len() as u32).serialize(&mut self.buffer).unwrap();
111 self.buffer.write_all(data).unwrap();
112 self.size += 1 + 4 + data.len();
113 }
114 WALRecord::CreateTable { table_id }
116 | WALRecord::DropTable { table_id }
117 | WALRecord::AlterTable { table_id }
118 | WALRecord::CreateIndex { table_id }
119 | WALRecord::DropIndex { table_id }
120 | WALRecord::CreateSequence { table_id } => {
121 let tag: u8 = match record {
122 WALRecord::CreateTable { .. } => b'T',
123 WALRecord::DropTable { .. } => b'A',
124 WALRecord::AlterTable { .. } => b'M',
125 WALRecord::CreateIndex { .. } => b'N',
126 WALRecord::DropIndex { .. } => b'X',
127 WALRecord::CreateSequence { .. } => b'Q',
128 _ => unreachable!(),
129 };
130 self.buffer.write_all(&[tag]).unwrap();
131 table_id.serialize(&mut self.buffer).unwrap();
132 self.size += 1 + 8;
133 }
134 }
135 self.count += 1;
136 }
137
138 pub fn log_insert(&mut self, table_id: u64, data: Vec<u8>) {
140 self.write_record(&WALRecord::Insert { table_id, data });
141 }
142
143 pub fn log_delete(&mut self, table_id: u64, row_id: u64) {
145 self.write_record(&WALRecord::Delete { table_id, row_id });
146 }
147
148 pub fn log_update(&mut self, table_id: u64, row_id: u64, column: u32, data: Vec<u8>) {
150 self.write_record(&WALRecord::Update {
151 table_id,
152 row_id,
153 column,
154 data,
155 });
156 }
157
158 pub fn log_column_write(&mut self, table_id: u64, col_id: u32, page_id: u64, data: Vec<u8>) {
160 self.write_record(&WALRecord::ColumnWrite {
161 table_id,
162 col_id,
163 page_id,
164 data,
165 });
166 }
167
168 pub fn log_begin_transaction(&mut self) {
170 }
173
174 pub fn log_commit(&mut self, transaction_id: u64) {
176 self.write_record(&WALRecord::Commit { transaction_id });
177 }
178
179 pub fn log_rollback(&mut self, transaction_id: u64) {
181 self.write_record(&WALRecord::Rollback { transaction_id });
182 }
183
184 pub fn log_checkpoint(&mut self) {
186 self.write_record(&WALRecord::Checkpoint);
187 }
188
189 pub fn buffer(&self) -> &[u8] {
191 &self.buffer
192 }
193
194 pub fn into_buffer(mut self) -> Vec<u8> {
196 std::mem::take(&mut self.buffer)
197 }
198
199 pub fn count(&self) -> usize {
201 self.count
202 }
203
204 pub fn size(&self) -> usize {
206 self.size
207 }
208
209 pub fn is_empty(&self) -> bool {
211 self.buffer.is_empty()
212 }
213
214 pub fn clear(&mut self) {
216 self.buffer.clear();
217 self.count = 0;
218 self.size = 0;
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 #[test]
227 fn test_local_wal_empty() {
228 let lwal = LocalWAL::new();
229 assert!(lwal.is_empty());
230 assert_eq!(lwal.count(), 0);
231 assert_eq!(lwal.size(), 0);
232 }
233
234 #[test]
235 fn test_local_wal_insert_record() {
236 let mut lwal = LocalWAL::new();
237 lwal.log_insert(1, vec![0x01, 0x02, 0x03]);
238 assert!(!lwal.is_empty());
239 assert_eq!(lwal.count(), 1);
240 assert!(lwal.size() > 0);
241 }
242
243 #[test]
244 fn test_local_wal_multiple_records() {
245 let mut lwal = LocalWAL::new();
246 lwal.log_insert(1, vec![0x01]);
247 lwal.log_delete(1, 42);
248 lwal.log_update(1, 42, 0, vec![0x05]);
249 lwal.log_commit(100);
250 assert_eq!(lwal.count(), 4);
251 assert!(lwal.size() > 0);
252 }
253
254 #[test]
255 fn test_local_wal_clear() {
256 let mut lwal = LocalWAL::new();
257 lwal.log_insert(1, vec![0x01]);
258 lwal.clear();
259 assert!(lwal.is_empty());
260 assert_eq!(lwal.count(), 0);
261 }
262
263 #[test]
264 fn test_local_wal_into_buffer() {
265 let mut lwal = LocalWAL::new();
266 lwal.log_insert(1, vec![0x01, 0x02]);
267 let buf = lwal.into_buffer();
268 assert!(!buf.is_empty());
269 }
270
271 #[test]
272 fn test_local_wal_buffer_content() {
273 let mut lwal = LocalWAL::new();
274 lwal.log_insert(42, vec![0xAB, 0xCD]);
275 let buf = lwal.buffer();
276 assert_eq!(buf[0], b'I', "First byte should be 'I' for Insert");
278 }
279}