1use crate::delete_bitmap::DeleteBitmap;
16use crate::error::ColumnarError;
17use crate::format::{HEADER_SIZE, SegmentFooter, SegmentHeader};
18use crate::predicate::ScanPredicate;
19
20use super::block_decode::{
21 append_null_fill, decode_block, empty_decoded, result_valid_len, result_valid_slice_mut,
22};
23use super::types::DecodedColumn;
24
25fn checked_slice(data: &[u8], start: usize, len: usize) -> Result<&[u8], ColumnarError> {
26 let end = start
27 .checked_add(len)
28 .ok_or_else(|| ColumnarError::Corruption {
29 segment_id: None,
30 reason: "column block range overflow".into(),
31 offset: None,
32 })?;
33 data.get(start..end).ok_or(ColumnarError::TruncatedSegment {
34 expected: end,
35 got: data.len(),
36 })
37}
38
39pub struct SegmentReader<'a> {
44 pub(super) data: &'a [u8],
45 pub(super) footer: SegmentFooter,
46}
47
48#[derive(Debug)]
53pub struct OwnedSegmentReader {
54 plaintext: Vec<u8>,
56 footer: SegmentFooter,
57}
58
59impl OwnedSegmentReader {
60 fn from_plaintext(plaintext: Vec<u8>) -> Result<Self, ColumnarError> {
61 SegmentHeader::from_bytes(&plaintext)?;
62 let footer = SegmentFooter::from_segment_tail(&plaintext)?;
63 Ok(Self { plaintext, footer })
64 }
65
66 pub fn open_with_kek(
74 blob: &[u8],
75 kek: Option<&nodedb_wal::crypto::WalEncryptionKey>,
76 ) -> Result<Self, ColumnarError> {
77 let is_encrypted = blob.len() >= 4 && blob[0..4] == crate::encrypt::SEGC_MAGIC;
78 if is_encrypted {
79 let key = kek.ok_or(ColumnarError::MissingKek)?;
80 let plaintext = crate::encrypt::decrypt_segment(key, blob)?;
81 Self::from_plaintext(plaintext)
82 } else if kek.is_some() {
83 Err(ColumnarError::KekRequired)
84 } else {
85 Self::from_plaintext(blob.to_vec())
86 }
87 }
88
89 pub fn reader(&self) -> SegmentReader<'_> {
91 SegmentReader {
92 data: &self.plaintext,
93 footer: self.footer.clone(),
94 }
95 }
96
97 pub fn footer(&self) -> &SegmentFooter {
99 &self.footer
100 }
101
102 pub fn row_count(&self) -> u64 {
104 self.footer.row_count
105 }
106}
107
108impl<'a> SegmentReader<'a> {
109 pub fn open(data: &'a [u8]) -> Result<Self, ColumnarError> {
119 if data.len() >= 4 && data[0..4] == crate::encrypt::SEGC_MAGIC {
120 return Err(ColumnarError::MissingKek);
121 }
122 SegmentHeader::from_bytes(data)?;
123 let footer = SegmentFooter::from_segment_tail(data)?;
124 Ok(Self { data, footer })
125 }
126
127 pub fn footer(&self) -> &SegmentFooter {
129 &self.footer
130 }
131
132 pub fn row_count(&self) -> u64 {
134 self.footer.row_count
135 }
136
137 pub fn column_count(&self) -> usize {
139 self.footer.column_count as usize
140 }
141
142 pub fn read_column(&self, col_idx: usize) -> Result<DecodedColumn, ColumnarError> {
146 self.read_column_filtered(col_idx, &[])
147 }
148
149 pub fn read_column_filtered(
155 &self,
156 col_idx: usize,
157 predicates: &[ScanPredicate],
158 ) -> Result<DecodedColumn, ColumnarError> {
159 self.read_column_impl(col_idx, predicates, &DeleteBitmap::new())
160 }
161
162 pub fn read_columns(
167 &self,
168 col_indices: &[usize],
169 predicates: &[ScanPredicate],
170 ) -> Result<Vec<DecodedColumn>, ColumnarError> {
171 col_indices
172 .iter()
173 .map(|&idx| self.read_column_filtered(idx, predicates))
174 .collect()
175 }
176
177 pub fn read_column_with_deletes(
182 &self,
183 col_idx: usize,
184 predicates: &[ScanPredicate],
185 deletes: &DeleteBitmap,
186 ) -> Result<DecodedColumn, ColumnarError> {
187 self.read_column_impl(col_idx, predicates, deletes)
188 }
189
190 pub fn read_columns_with_deletes(
192 &self,
193 col_indices: &[usize],
194 predicates: &[ScanPredicate],
195 deletes: &DeleteBitmap,
196 ) -> Result<Vec<DecodedColumn>, ColumnarError> {
197 col_indices
198 .iter()
199 .map(|&idx| self.read_column_with_deletes(idx, predicates, deletes))
200 .collect()
201 }
202
203 fn read_column_impl(
206 &self,
207 col_idx: usize,
208 predicates: &[ScanPredicate],
209 deletes: &DeleteBitmap,
210 ) -> Result<DecodedColumn, ColumnarError> {
211 if col_idx >= self.footer.columns.len() {
212 return Err(ColumnarError::ColumnOutOfRange {
213 index: col_idx,
214 count: self.footer.columns.len(),
215 });
216 }
217
218 let col_meta = &self.footer.columns[col_idx];
219 let my_preds: Vec<&ScanPredicate> =
220 predicates.iter().filter(|p| p.col_idx == col_idx).collect();
221
222 let col_offset =
223 usize::try_from(col_meta.offset).map_err(|_| ColumnarError::Corruption {
224 segment_id: None,
225 reason: "column offset does not fit usize".into(),
226 offset: None,
227 })?;
228 let col_start =
229 HEADER_SIZE
230 .checked_add(col_offset)
231 .ok_or_else(|| ColumnarError::Corruption {
232 segment_id: None,
233 reason: "column offset range overflow".into(),
234 offset: None,
235 })?;
236 if col_start > self.data.len() {
237 return Err(ColumnarError::TruncatedSegment {
238 expected: col_start,
239 got: self.data.len(),
240 });
241 }
242 let mut cursor = col_start;
243 let mut result = empty_decoded(col_meta.layout);
244 let mut global_row: u32 = 0;
245
246 for block_stat in &col_meta.block_stats {
247 let block_row_count = block_stat.row_count;
248
249 let header_end = cursor
250 .checked_add(4)
251 .ok_or_else(|| ColumnarError::Corruption {
252 segment_id: None,
253 reason: "column block header range overflow".into(),
254 offset: None,
255 })?;
256 let header =
257 self.data
258 .get(cursor..header_end)
259 .ok_or(ColumnarError::TruncatedSegment {
260 expected: header_end,
261 got: self.data.len(),
262 })?;
263 let block_len = usize::try_from(u32::from_le_bytes([
264 header[0], header[1], header[2], header[3],
265 ]))
266 .map_err(|_| ColumnarError::Corruption {
267 segment_id: None,
268 reason: "column block length does not fit usize".into(),
269 offset: None,
270 })?;
271 let block_data = checked_slice(self.data, header_end, block_len)?;
272 cursor =
273 header_end
274 .checked_add(block_len)
275 .ok_or_else(|| ColumnarError::Corruption {
276 segment_id: None,
277 reason: "column block range overflow".into(),
278 offset: None,
279 })?;
280
281 let pred_skip = my_preds.iter().any(|p| p.can_skip_block(block_stat));
283
284 let delete_skip =
286 !deletes.is_empty() && deletes.is_block_fully_deleted(global_row, block_row_count);
287
288 if pred_skip || delete_skip {
289 append_null_fill(&mut result, block_row_count as usize);
290 global_row += block_row_count;
291 continue;
292 }
293
294 let pre_len = result_valid_len(&result);
296 decode_block(
297 &mut result,
298 block_data,
299 col_meta.layout,
300 col_meta.codec,
301 block_row_count as usize,
302 col_meta.dictionary.as_deref(),
303 )?;
304
305 if !deletes.is_empty() {
307 let valid_slice = result_valid_slice_mut(&mut result, pre_len);
308 deletes.apply_to_validity(valid_slice, global_row);
309 }
310
311 global_row += block_row_count;
312 }
313
314 Ok(result)
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::checked_slice;
321 use crate::error::ColumnarError;
322
323 #[test]
324 fn block_range_rejects_truncated_and_overflowed_lengths() {
325 assert!(matches!(
326 checked_slice(&[0; 4], 2, 4),
327 Err(ColumnarError::TruncatedSegment {
328 expected: 6,
329 got: 4
330 })
331 ));
332 assert!(matches!(
333 checked_slice(&[], usize::MAX, 1),
334 Err(ColumnarError::Corruption { .. })
335 ));
336 }
337}