Skip to main content

hematite/catalog/
row_id.rs

1//! Rowid table cell formats for relational tables.
2
3use crate::error::{HematiteError, Result};
4
5use super::record::StoredRow;
6use super::serialization::RowCodec;
7
8pub const INVALID_PAGE_ID: u32 = u32::MAX;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct RowidLeafCell {
12    pub rowid: u64,
13    pub payload: Vec<u8>,
14}
15
16pub const ROWID_LEAF_FIXED_HEADER_SIZE: usize = 20;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct RowidLeafCellLayout {
20    pub rowid: u64,
21    pub total_payload_len: u32,
22    pub local_payload: Vec<u8>,
23    pub overflow_first_page: u32,
24}
25
26impl RowidLeafCellLayout {
27    pub fn local_payload_len_for(total_payload_len: usize, max_local_payload: usize) -> usize {
28        total_payload_len.min(max_local_payload)
29    }
30
31    pub fn encode(&self) -> Result<Vec<u8>> {
32        if self.local_payload.len() > self.total_payload_len as usize {
33            return Err(HematiteError::StorageError(
34                "Local payload cannot exceed total payload length".to_string(),
35            ));
36        }
37
38        let local_len = self.local_payload.len() as u32;
39        let mut out = Vec::with_capacity(ROWID_LEAF_FIXED_HEADER_SIZE + self.local_payload.len());
40        out.extend_from_slice(&self.rowid.to_le_bytes());
41        out.extend_from_slice(&self.total_payload_len.to_le_bytes());
42        out.extend_from_slice(&local_len.to_le_bytes());
43        out.extend_from_slice(&self.overflow_first_page.to_le_bytes());
44        out.extend_from_slice(&self.local_payload);
45        Ok(out)
46    }
47
48    pub fn decode(data: &[u8]) -> Result<Self> {
49        if data.len() < ROWID_LEAF_FIXED_HEADER_SIZE {
50            return Err(HematiteError::CorruptedData(
51                "Rowid fixed leaf cell header is truncated".to_string(),
52            ));
53        }
54
55        let rowid = u64::from_le_bytes([
56            data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
57        ]);
58        let total_payload_len = u32::from_le_bytes([data[8], data[9], data[10], data[11]]);
59        let local_len = u32::from_le_bytes([data[12], data[13], data[14], data[15]]) as usize;
60        let overflow_first_page = u32::from_le_bytes([data[16], data[17], data[18], data[19]]);
61
62        if ROWID_LEAF_FIXED_HEADER_SIZE + local_len != data.len() {
63            return Err(HematiteError::CorruptedData(
64                "Rowid fixed leaf local payload length mismatch".to_string(),
65            ));
66        }
67        if local_len > total_payload_len as usize {
68            return Err(HematiteError::CorruptedData(
69                "Rowid fixed leaf local payload exceeds total payload length".to_string(),
70            ));
71        }
72
73        Ok(Self {
74            rowid,
75            total_payload_len,
76            local_payload: data[ROWID_LEAF_FIXED_HEADER_SIZE..].to_vec(),
77            overflow_first_page,
78        })
79    }
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
83pub struct EncodedRowidRecord {
84    pub cell: RowidLeafCellLayout,
85    pub overflow_payload: Vec<u8>,
86}
87
88pub fn encode_stored_row_record(
89    row: &StoredRow,
90    max_local_payload: usize,
91) -> Result<EncodedRowidRecord> {
92    let mut payload = RowCodec::encode_stored_row(&StoredRow {
93        row_id: 0,
94        values: row.values.clone(),
95    })?;
96    if payload.len() < 4 {
97        return Err(HematiteError::CorruptedData(
98            "Stored row payload is truncated".to_string(),
99        ));
100    }
101    payload.drain(0..4);
102    let local_len = RowidLeafCellLayout::local_payload_len_for(payload.len(), max_local_payload);
103    let local_payload = payload[0..local_len].to_vec();
104    let overflow_payload = payload[local_len..].to_vec();
105
106    Ok(EncodedRowidRecord {
107        cell: RowidLeafCellLayout {
108            rowid: row.row_id,
109            total_payload_len: payload.len() as u32,
110            local_payload,
111            overflow_first_page: INVALID_PAGE_ID,
112        },
113        overflow_payload,
114    })
115}
116
117pub fn decode_stored_row_record(rowid: u64, payload: &[u8]) -> Result<StoredRow> {
118    let mut serialized = Vec::with_capacity(payload.len() + 4);
119    serialized.extend_from_slice(&(payload.len() as u32).to_le_bytes());
120    serialized.extend_from_slice(payload);
121    let mut row = RowCodec::decode_stored_row(&serialized)?;
122    row.row_id = rowid;
123    Ok(row)
124}
125
126pub fn materialize_row_record_cell<F>(
127    row: &StoredRow,
128    max_local_payload: usize,
129    mut write_overflow_chain: F,
130) -> Result<Vec<u8>>
131where
132    F: FnMut(&[u8]) -> Result<Option<u32>>,
133{
134    let mut encoded = encode_stored_row_record(row, max_local_payload)?;
135    let overflow_first = write_overflow_chain(&encoded.overflow_payload)?;
136    encoded.cell.overflow_first_page = overflow_first.unwrap_or(INVALID_PAGE_ID);
137    encoded.cell.encode()
138}
139
140pub fn hydrate_row_record_cell<F>(
141    cell_bytes: &[u8],
142    mut read_overflow_chain: F,
143) -> Result<StoredRow>
144where
145    F: FnMut(Option<u32>, usize) -> Result<Vec<u8>>,
146{
147    let cell = RowidLeafCellLayout::decode(cell_bytes)?;
148    let local_len = cell.local_payload.len();
149    let total_len = cell.total_payload_len as usize;
150    if local_len > total_len {
151        return Err(HematiteError::CorruptedData(
152            "Cell local payload exceeds total payload length".to_string(),
153        ));
154    }
155
156    let overflow_len = total_len - local_len;
157    let overflow_first = if cell.overflow_first_page == INVALID_PAGE_ID {
158        None
159    } else {
160        Some(cell.overflow_first_page)
161    };
162    let overflow_payload = read_overflow_chain(overflow_first, overflow_len)?;
163
164    let mut payload = cell.local_payload;
165    payload.extend_from_slice(&overflow_payload);
166    decode_stored_row_record(cell.rowid, &payload)
167}
168
169pub fn free_row_record_overflow<F>(cell_bytes: &[u8], mut free_overflow_chain: F) -> Result<()>
170where
171    F: FnMut(Option<u32>) -> Result<()>,
172{
173    let cell = RowidLeafCellLayout::decode(cell_bytes)?;
174    let overflow_first = if cell.overflow_first_page == INVALID_PAGE_ID {
175        None
176    } else {
177        Some(cell.overflow_first_page)
178    };
179    free_overflow_chain(overflow_first)
180}
181
182impl RowidLeafCell {
183    pub const HEADER_SIZE: usize = 12;
184
185    pub fn encode(&self) -> Vec<u8> {
186        let mut out = Vec::with_capacity(Self::HEADER_SIZE + self.payload.len());
187        out.extend_from_slice(&self.rowid.to_le_bytes());
188        out.extend_from_slice(&(self.payload.len() as u32).to_le_bytes());
189        out.extend_from_slice(&self.payload);
190        out
191    }
192
193    pub fn decode(data: &[u8]) -> Result<Self> {
194        if data.len() < Self::HEADER_SIZE {
195            return Err(HematiteError::CorruptedData(
196                "Rowid leaf cell header is truncated".to_string(),
197            ));
198        }
199
200        let rowid = u64::from_le_bytes([
201            data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
202        ]);
203        let payload_len = u32::from_le_bytes([data[8], data[9], data[10], data[11]]) as usize;
204        if Self::HEADER_SIZE + payload_len != data.len() {
205            return Err(HematiteError::CorruptedData(
206                "Rowid leaf cell payload length mismatch".to_string(),
207            ));
208        }
209
210        Ok(Self {
211            rowid,
212            payload: data[Self::HEADER_SIZE..].to_vec(),
213        })
214    }
215}
216
217#[derive(Debug, Clone, PartialEq, Eq)]
218pub struct RowidInternalCell {
219    pub separator_rowid: u64,
220    pub child_page_id: u32,
221}
222
223impl RowidInternalCell {
224    pub const SIZE: usize = 12;
225
226    pub fn encode(&self) -> [u8; Self::SIZE] {
227        let mut out = [0u8; Self::SIZE];
228        out[0..8].copy_from_slice(&self.separator_rowid.to_le_bytes());
229        out[8..12].copy_from_slice(&self.child_page_id.to_le_bytes());
230        out
231    }
232
233    pub fn decode(data: &[u8]) -> Result<Self> {
234        if data.len() != Self::SIZE {
235            return Err(HematiteError::CorruptedData(
236                "Rowid internal cell size mismatch".to_string(),
237            ));
238        }
239
240        let separator_rowid = u64::from_le_bytes([
241            data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
242        ]);
243        let child_page_id = u32::from_le_bytes([data[8], data[9], data[10], data[11]]);
244
245        Ok(Self {
246            separator_rowid,
247            child_page_id,
248        })
249    }
250}