1use byteorder::{BigEndian, ByteOrder};
10use serde::Serialize;
11
12use crate::innodb::constants::FIL_PAGE_DATA;
13
14const TRX_UNDO_PAGE_TYPE: usize = 0; const TRX_UNDO_PAGE_START: usize = 2; const TRX_UNDO_PAGE_FREE: usize = 4; #[allow(dead_code)]
21const TRX_UNDO_PAGE_NODE: usize = 6; const TRX_UNDO_PAGE_HDR_SIZE: usize = 18;
23
24const TRX_UNDO_STATE: usize = 0; const TRX_UNDO_LAST_LOG: usize = 2; #[allow(dead_code)]
28const TRX_UNDO_FSEG_HEADER: usize = 4; #[allow(dead_code)]
30const TRX_UNDO_PAGE_LIST: usize = 14; const TRX_UNDO_SEG_HDR_SIZE: usize = 30;
32
33const 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)]
46pub enum UndoPageType {
47 Insert,
49 Update,
51 Unknown(u16),
53}
54
55impl UndoPageType {
56 pub fn from_u16(value: u16) -> Self {
58 match value {
59 1 => UndoPageType::Insert,
60 2 => UndoPageType::Update,
61 v => UndoPageType::Unknown(v),
62 }
63 }
64
65 pub fn name(&self) -> &'static str {
67 match self {
68 UndoPageType::Insert => "INSERT",
69 UndoPageType::Update => "UPDATE",
70 UndoPageType::Unknown(_) => "UNKNOWN",
71 }
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
77pub enum UndoState {
78 Active,
80 Cached,
82 ToFree,
84 ToPurge,
86 Prepared,
88 Unknown(u16),
90}
91
92impl UndoState {
93 pub fn from_u16(value: u16) -> Self {
95 match value {
96 1 => UndoState::Active,
97 2 => UndoState::Cached,
98 3 => UndoState::ToFree,
99 4 => UndoState::ToPurge,
100 5 => UndoState::Prepared,
101 v => UndoState::Unknown(v),
102 }
103 }
104
105 pub fn name(&self) -> &'static str {
107 match self {
108 UndoState::Active => "ACTIVE",
109 UndoState::Cached => "CACHED",
110 UndoState::ToFree => "TO_FREE",
111 UndoState::ToPurge => "TO_PURGE",
112 UndoState::Prepared => "PREPARED",
113 UndoState::Unknown(_) => "UNKNOWN",
114 }
115 }
116}
117
118#[derive(Debug, Clone, Serialize)]
120pub struct UndoPageHeader {
121 pub page_type: UndoPageType,
123 pub start: u16,
125 pub free: u16,
127}
128
129#[derive(Debug, Clone, Serialize)]
131pub struct UndoSegmentHeader {
132 pub state: UndoState,
134 pub last_log: u16,
136}
137
138impl UndoPageHeader {
139 pub fn parse(page_data: &[u8]) -> Option<Self> {
143 let base = FIL_PAGE_DATA;
144 if page_data.len() < base + TRX_UNDO_PAGE_HDR_SIZE {
145 return None;
146 }
147
148 let d = &page_data[base..];
149 Some(UndoPageHeader {
150 page_type: UndoPageType::from_u16(BigEndian::read_u16(&d[TRX_UNDO_PAGE_TYPE..])),
151 start: BigEndian::read_u16(&d[TRX_UNDO_PAGE_START..]),
152 free: BigEndian::read_u16(&d[TRX_UNDO_PAGE_FREE..]),
153 })
154 }
155}
156
157impl UndoSegmentHeader {
158 pub fn parse(page_data: &[u8]) -> Option<Self> {
162 let base = FIL_PAGE_DATA + TRX_UNDO_PAGE_HDR_SIZE;
163 if page_data.len() < base + TRX_UNDO_SEG_HDR_SIZE {
164 return None;
165 }
166
167 let d = &page_data[base..];
168 Some(UndoSegmentHeader {
169 state: UndoState::from_u16(BigEndian::read_u16(&d[TRX_UNDO_STATE..])),
170 last_log: BigEndian::read_u16(&d[TRX_UNDO_LAST_LOG..]),
171 })
172 }
173}
174
175#[derive(Debug, Clone, Serialize)]
177pub struct UndoLogHeader {
178 pub trx_id: u64,
180 pub trx_no: u64,
182 pub del_marks: bool,
184 pub log_start: u16,
186 pub xid_exists: bool,
188 pub dict_trans: bool,
190 pub table_id: u64,
192 pub next_log: u16,
194 pub prev_log: u16,
196}
197
198impl UndoLogHeader {
199 pub fn parse(page_data: &[u8], log_offset: usize) -> Option<Self> {
204 if page_data.len() < log_offset + 34 {
205 return None;
206 }
207
208 let d = &page_data[log_offset..];
209 Some(UndoLogHeader {
210 trx_id: BigEndian::read_u64(&d[TRX_UNDO_TRX_ID..]),
211 trx_no: BigEndian::read_u64(&d[TRX_UNDO_TRX_NO..]),
212 del_marks: BigEndian::read_u16(&d[TRX_UNDO_DEL_MARKS..]) != 0,
213 log_start: BigEndian::read_u16(&d[TRX_UNDO_LOG_START..]),
214 xid_exists: d[TRX_UNDO_XID_EXISTS] != 0,
215 dict_trans: d[TRX_UNDO_DICT_TRANS] != 0,
216 table_id: BigEndian::read_u64(&d[TRX_UNDO_TABLE_ID..]),
217 next_log: BigEndian::read_u16(&d[TRX_UNDO_NEXT_LOG..]),
218 prev_log: BigEndian::read_u16(&d[TRX_UNDO_PREV_LOG..]),
219 })
220 }
221}
222
223#[derive(Debug, Clone, Serialize)]
228pub struct RsegArrayHeader {
229 pub size: u32,
231}
232
233impl RsegArrayHeader {
234 pub fn parse(page_data: &[u8]) -> Option<Self> {
238 let base = FIL_PAGE_DATA;
239 if page_data.len() < base + 4 {
240 return None;
241 }
242
243 Some(RsegArrayHeader {
244 size: BigEndian::read_u32(&page_data[base..]),
245 })
246 }
247
248 pub fn read_slots(page_data: &[u8], max_slots: usize) -> Vec<u32> {
252 let base = FIL_PAGE_DATA + 4; let mut slots = Vec::new();
254
255 for i in 0..max_slots {
256 let offset = base + i * 4;
257 if offset + 4 > page_data.len() {
258 break;
259 }
260 let page_no = BigEndian::read_u32(&page_data[offset..]);
261 if page_no != 0 && page_no != crate::innodb::constants::FIL_NULL {
262 slots.push(page_no);
263 }
264 }
265
266 slots
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 #[test]
275 fn test_undo_page_type() {
276 assert_eq!(UndoPageType::from_u16(1), UndoPageType::Insert);
277 assert_eq!(UndoPageType::from_u16(2), UndoPageType::Update);
278 assert_eq!(UndoPageType::from_u16(1).name(), "INSERT");
279 assert_eq!(UndoPageType::from_u16(2).name(), "UPDATE");
280 }
281
282 #[test]
283 fn test_undo_state() {
284 assert_eq!(UndoState::from_u16(1), UndoState::Active);
285 assert_eq!(UndoState::from_u16(2), UndoState::Cached);
286 assert_eq!(UndoState::from_u16(3), UndoState::ToFree);
287 assert_eq!(UndoState::from_u16(4), UndoState::ToPurge);
288 assert_eq!(UndoState::from_u16(5), UndoState::Prepared);
289 assert_eq!(UndoState::from_u16(1).name(), "ACTIVE");
290 }
291
292 #[test]
293 fn test_undo_page_header_parse() {
294 let mut page = vec![0u8; 256];
295 let base = FIL_PAGE_DATA;
296
297 BigEndian::write_u16(&mut page[base + TRX_UNDO_PAGE_TYPE..], 1);
299 BigEndian::write_u16(&mut page[base + TRX_UNDO_PAGE_START..], 100);
301 BigEndian::write_u16(&mut page[base + TRX_UNDO_PAGE_FREE..], 200);
303
304 let hdr = UndoPageHeader::parse(&page).unwrap();
305 assert_eq!(hdr.page_type, UndoPageType::Insert);
306 assert_eq!(hdr.start, 100);
307 assert_eq!(hdr.free, 200);
308 }
309
310 #[test]
311 fn test_undo_segment_header_parse() {
312 let mut page = vec![0u8; 256];
313 let base = FIL_PAGE_DATA + TRX_UNDO_PAGE_HDR_SIZE;
314
315 BigEndian::write_u16(&mut page[base + TRX_UNDO_STATE..], 1);
317 BigEndian::write_u16(&mut page[base + TRX_UNDO_LAST_LOG..], 150);
319
320 let hdr = UndoSegmentHeader::parse(&page).unwrap();
321 assert_eq!(hdr.state, UndoState::Active);
322 assert_eq!(hdr.last_log, 150);
323 }
324}