1use byteorder::{BigEndian, ByteOrder};
2use serde::Serialize;
3
4use crate::innodb::constants::FIL_PAGE_DATA;
5
6const TRX_UNDO_PAGE_TYPE: usize = 0; const TRX_UNDO_PAGE_START: usize = 2; const TRX_UNDO_PAGE_FREE: usize = 4; #[allow(dead_code)]
13const TRX_UNDO_PAGE_NODE: usize = 6; const TRX_UNDO_PAGE_HDR_SIZE: usize = 18;
15
16const TRX_UNDO_STATE: usize = 0; const TRX_UNDO_LAST_LOG: usize = 2; #[allow(dead_code)]
20const TRX_UNDO_FSEG_HEADER: usize = 4; #[allow(dead_code)]
22const TRX_UNDO_PAGE_LIST: usize = 14; const TRX_UNDO_SEG_HDR_SIZE: usize = 30;
24
25const TRX_UNDO_TRX_ID: usize = 0; const TRX_UNDO_TRX_NO: usize = 8; const TRX_UNDO_DEL_MARKS: usize = 16; const TRX_UNDO_LOG_START: usize = 18; const TRX_UNDO_XID_EXISTS: usize = 20; const TRX_UNDO_DICT_TRANS: usize = 21; const TRX_UNDO_TABLE_ID: usize = 22; const TRX_UNDO_NEXT_LOG: usize = 30; const TRX_UNDO_PREV_LOG: usize = 32; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
38pub enum UndoPageType {
39 Insert,
41 Update,
43 Unknown(u16),
45}
46
47impl UndoPageType {
48 pub fn from_u16(value: u16) -> Self {
49 match value {
50 1 => UndoPageType::Insert,
51 2 => UndoPageType::Update,
52 v => UndoPageType::Unknown(v),
53 }
54 }
55
56 pub fn name(&self) -> &'static str {
57 match self {
58 UndoPageType::Insert => "INSERT",
59 UndoPageType::Update => "UPDATE",
60 UndoPageType::Unknown(_) => "UNKNOWN",
61 }
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
67pub enum UndoState {
68 Active,
70 Cached,
72 ToFree,
74 ToPurge,
76 Prepared,
78 Unknown(u16),
80}
81
82impl UndoState {
83 pub fn from_u16(value: u16) -> Self {
84 match value {
85 1 => UndoState::Active,
86 2 => UndoState::Cached,
87 3 => UndoState::ToFree,
88 4 => UndoState::ToPurge,
89 5 => UndoState::Prepared,
90 v => UndoState::Unknown(v),
91 }
92 }
93
94 pub fn name(&self) -> &'static str {
95 match self {
96 UndoState::Active => "ACTIVE",
97 UndoState::Cached => "CACHED",
98 UndoState::ToFree => "TO_FREE",
99 UndoState::ToPurge => "TO_PURGE",
100 UndoState::Prepared => "PREPARED",
101 UndoState::Unknown(_) => "UNKNOWN",
102 }
103 }
104}
105
106#[derive(Debug, Clone, Serialize)]
108pub struct UndoPageHeader {
109 pub page_type: UndoPageType,
111 pub start: u16,
113 pub free: u16,
115}
116
117#[derive(Debug, Clone, Serialize)]
119pub struct UndoSegmentHeader {
120 pub state: UndoState,
122 pub last_log: u16,
124}
125
126impl UndoPageHeader {
127 pub fn parse(page_data: &[u8]) -> Option<Self> {
131 let base = FIL_PAGE_DATA;
132 if page_data.len() < base + TRX_UNDO_PAGE_HDR_SIZE {
133 return None;
134 }
135
136 let d = &page_data[base..];
137 Some(UndoPageHeader {
138 page_type: UndoPageType::from_u16(BigEndian::read_u16(&d[TRX_UNDO_PAGE_TYPE..])),
139 start: BigEndian::read_u16(&d[TRX_UNDO_PAGE_START..]),
140 free: BigEndian::read_u16(&d[TRX_UNDO_PAGE_FREE..]),
141 })
142 }
143}
144
145impl UndoSegmentHeader {
146 pub fn parse(page_data: &[u8]) -> Option<Self> {
150 let base = FIL_PAGE_DATA + TRX_UNDO_PAGE_HDR_SIZE;
151 if page_data.len() < base + TRX_UNDO_SEG_HDR_SIZE {
152 return None;
153 }
154
155 let d = &page_data[base..];
156 Some(UndoSegmentHeader {
157 state: UndoState::from_u16(BigEndian::read_u16(&d[TRX_UNDO_STATE..])),
158 last_log: BigEndian::read_u16(&d[TRX_UNDO_LAST_LOG..]),
159 })
160 }
161}
162
163#[derive(Debug, Clone, Serialize)]
165pub struct UndoLogHeader {
166 pub trx_id: u64,
168 pub trx_no: u64,
170 pub del_marks: bool,
172 pub log_start: u16,
174 pub xid_exists: bool,
176 pub dict_trans: bool,
178 pub table_id: u64,
180 pub next_log: u16,
182 pub prev_log: u16,
184}
185
186impl UndoLogHeader {
187 pub fn parse(page_data: &[u8], log_offset: usize) -> Option<Self> {
192 if page_data.len() < log_offset + 34 {
193 return None;
194 }
195
196 let d = &page_data[log_offset..];
197 Some(UndoLogHeader {
198 trx_id: BigEndian::read_u64(&d[TRX_UNDO_TRX_ID..]),
199 trx_no: BigEndian::read_u64(&d[TRX_UNDO_TRX_NO..]),
200 del_marks: BigEndian::read_u16(&d[TRX_UNDO_DEL_MARKS..]) != 0,
201 log_start: BigEndian::read_u16(&d[TRX_UNDO_LOG_START..]),
202 xid_exists: d[TRX_UNDO_XID_EXISTS] != 0,
203 dict_trans: d[TRX_UNDO_DICT_TRANS] != 0,
204 table_id: BigEndian::read_u64(&d[TRX_UNDO_TABLE_ID..]),
205 next_log: BigEndian::read_u16(&d[TRX_UNDO_NEXT_LOG..]),
206 prev_log: BigEndian::read_u16(&d[TRX_UNDO_PREV_LOG..]),
207 })
208 }
209}
210
211#[derive(Debug, Clone, Serialize)]
216pub struct RsegArrayHeader {
217 pub size: u32,
219}
220
221impl RsegArrayHeader {
222 pub fn parse(page_data: &[u8]) -> Option<Self> {
226 let base = FIL_PAGE_DATA;
227 if page_data.len() < base + 4 {
228 return None;
229 }
230
231 Some(RsegArrayHeader {
232 size: BigEndian::read_u32(&page_data[base..]),
233 })
234 }
235
236 pub fn read_slots(page_data: &[u8], max_slots: usize) -> Vec<u32> {
240 let base = FIL_PAGE_DATA + 4; let mut slots = Vec::new();
242
243 for i in 0..max_slots {
244 let offset = base + i * 4;
245 if offset + 4 > page_data.len() {
246 break;
247 }
248 let page_no = BigEndian::read_u32(&page_data[offset..]);
249 if page_no != 0 && page_no != crate::innodb::constants::FIL_NULL {
250 slots.push(page_no);
251 }
252 }
253
254 slots
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_undo_page_type() {
264 assert_eq!(UndoPageType::from_u16(1), UndoPageType::Insert);
265 assert_eq!(UndoPageType::from_u16(2), UndoPageType::Update);
266 assert_eq!(UndoPageType::from_u16(1).name(), "INSERT");
267 assert_eq!(UndoPageType::from_u16(2).name(), "UPDATE");
268 }
269
270 #[test]
271 fn test_undo_state() {
272 assert_eq!(UndoState::from_u16(1), UndoState::Active);
273 assert_eq!(UndoState::from_u16(2), UndoState::Cached);
274 assert_eq!(UndoState::from_u16(3), UndoState::ToFree);
275 assert_eq!(UndoState::from_u16(4), UndoState::ToPurge);
276 assert_eq!(UndoState::from_u16(5), UndoState::Prepared);
277 assert_eq!(UndoState::from_u16(1).name(), "ACTIVE");
278 }
279
280 #[test]
281 fn test_undo_page_header_parse() {
282 let mut page = vec![0u8; 256];
283 let base = FIL_PAGE_DATA;
284
285 BigEndian::write_u16(&mut page[base + TRX_UNDO_PAGE_TYPE..], 1);
287 BigEndian::write_u16(&mut page[base + TRX_UNDO_PAGE_START..], 100);
289 BigEndian::write_u16(&mut page[base + TRX_UNDO_PAGE_FREE..], 200);
291
292 let hdr = UndoPageHeader::parse(&page).unwrap();
293 assert_eq!(hdr.page_type, UndoPageType::Insert);
294 assert_eq!(hdr.start, 100);
295 assert_eq!(hdr.free, 200);
296 }
297
298 #[test]
299 fn test_undo_segment_header_parse() {
300 let mut page = vec![0u8; 256];
301 let base = FIL_PAGE_DATA + TRX_UNDO_PAGE_HDR_SIZE;
302
303 BigEndian::write_u16(&mut page[base + TRX_UNDO_STATE..], 1);
305 BigEndian::write_u16(&mut page[base + TRX_UNDO_LAST_LOG..], 150);
307
308 let hdr = UndoSegmentHeader::parse(&page).unwrap();
309 assert_eq!(hdr.state, UndoState::Active);
310 assert_eq!(hdr.last_log, 150);
311 }
312}