From d51f8f7de4ef95e69eb545f46479d96769162359 Mon Sep 17 00:00:00 2001 From: Codetector Date: Sat, 3 Aug 2024 22:54:31 -0700 Subject: [PATCH] parsing more pages. --- src/innodb/page/lob/data_page.rs | 69 ++++++++++++++++++++++++++++++++ src/innodb/page/lob/mod.rs | 2 + src/innodb/table/row.rs | 20 +++++---- 3 files changed, 84 insertions(+), 7 deletions(-) create mode 100644 src/innodb/page/lob/data_page.rs diff --git a/src/innodb/page/lob/data_page.rs b/src/innodb/page/lob/data_page.rs new file mode 100644 index 0000000..d5b104d --- /dev/null +++ b/src/innodb/page/lob/data_page.rs @@ -0,0 +1,69 @@ +use anyhow::{anyhow, Result}; + +use crate::innodb::{page::{Page, PageType}, InnoDBError}; + +#[derive(Debug, Clone)] +pub struct LobDataHeader { + pub version: u8, + pub data_len: u32, + pub trx_id: u64, // 6 bytes +} + +impl LobDataHeader { + pub fn size() -> usize { + 1 + 4 + 6 + } + + pub fn try_from_bytes(buf: &[u8]) -> Result { + if buf.len() < 11 { + return Err(anyhow!("Buffer too short for LobDataHeader")); + } + + let version = buf[0]; + let data_len = u32::from_be_bytes(buf[1..5].try_into()?); + + // trx_id is 6 bytes, so we need to pad it with two zero bytes for u64 + let trx_id = u64::from_be_bytes([0, 0, buf[5], buf[6], buf[7], buf[8], buf[9], buf[10]]); + + Ok(LobDataHeader { + version, + data_len, + trx_id, + }) + } +} + +#[derive(Debug)] +pub struct LobData<'a> { + pub page: &'a Page<'a>, + pub header: LobDataHeader, +} + +impl <'a> LobData<'a> { + pub fn try_from_page(p: &'a Page<'a>) -> Result { + match p.header.page_type { + PageType::LobData => Ok(LobData{ + header: LobDataHeader::try_from_bytes(p.body())?, + page: p, + }), + _ => Err(anyhow!(InnoDBError::InvalidPageType { + expected: PageType::LobData, + has: p.header.page_type + })), + } + } + + pub fn read(&self, offset: usize, buf: &mut [u8]) -> usize { + let data_len = self.header.data_len as usize; + let data = &self.body()[..data_len]; + assert!(offset < data.len(), "offset too large"); + let data = &data[offset..]; + let bytes_to_copy = std::cmp::min(buf.len(), data.len()); + buf[..bytes_to_copy].copy_from_slice(&data[..bytes_to_copy]); + bytes_to_copy + } + + pub fn body(&self) -> &[u8] { + &self.page.body()[LobDataHeader::size()..] + } +} diff --git a/src/innodb/page/lob/mod.rs b/src/innodb/page/lob/mod.rs index 65b09b6..6e41d4b 100644 --- a/src/innodb/page/lob/mod.rs +++ b/src/innodb/page/lob/mod.rs @@ -6,6 +6,8 @@ use anyhow::{anyhow, Ok, Result}; use super::{Page, PageType}; +pub mod data_page; + /* * General Flow for reading extern records * diff --git a/src/innodb/table/row.rs b/src/innodb/table/row.rs index f55ba1f..c537fe6 100644 --- a/src/innodb/table/row.rs +++ b/src/innodb/table/row.rs @@ -6,11 +6,11 @@ use std::{ }; use crate::innodb::{ - buffer_manager::BufferManager, + buffer_manager::{self, BufferManager}, file_list::FileListInnerNode, page::{ index::record::{Record, RECORD_HEADER_FIXED_LENGTH}, - lob::{LobFirst, LobIndexEntry}, + lob::{data_page::LobData, LobFirst, LobIndexEntry}, }, table::blob_header::ExternReference, InnoDBError, @@ -139,8 +139,9 @@ impl<'a> Row<'a> { extern_header: &ExternReference, buffer_mgr: &mut dyn BufferManager, ) -> Result> { + let space_id = extern_header.space_id; let first_page_number = extern_header.page_number; - let lob_first_page = buffer_mgr.open_page(extern_header.space_id, first_page_number)?; + let lob_first_page = buffer_mgr.open_page(space_id, first_page_number)?; if lob_first_page.header.offset != extern_header.page_number { return Err(anyhow!(InnoDBError::InvalidPage)); } @@ -165,18 +166,23 @@ impl<'a> Row<'a> { let node = LobIndexEntry::try_from_bytes(buf)?; trace!("Index Node: {:#?}", node); + let mut bytes_read = 0usize; if node.page_number == first_page_number { - let bytes_read = lob_first.read(page_offset, &mut output_buffer[filled..]); - filled += bytes_read; - page_offset = page_offset.saturating_sub(bytes_read); + bytes_read = lob_first.read(page_offset, &mut output_buffer[filled..]); trace!( "Read {} bytes from first page, in total expecting {} bytes", bytes_read, output_buffer.len() ); } else { - // TODO: implemtn LobData + let page_guard = buffer_mgr.open_page(space_id, node.page_number)?; + let data_page = LobData::try_from_page(&page_guard)?; + trace!("Data page: {:#?}", data_page); + bytes_read = data_page.read(page_offset, &mut output_buffer[filled..]); + trace!("Read {} bytes from data page", bytes_read); } + filled += bytes_read; + page_offset = page_offset.saturating_sub(bytes_read); node_location = node.file_list_node.next; }