1use alloc::sync::Arc;
20use alloc::vec::Vec;
21
22use crate::table::columnar::{
23 COL_SEQNO, COL_USER_KEY, COL_VALUE_TYPE, Column, ColumnBatch, Number, TypeTag,
24 bytes_column_row, frame_bytes_column,
25};
26use crate::{Error, Slice};
27
28#[derive(Clone, Debug, Eq, PartialEq)]
32pub enum Absent {
33 Null,
36 Default(Slice),
38 Error,
40}
41
42#[derive(Clone, Debug, Eq, PartialEq)]
45pub struct ProjectedField {
46 column_id: u16,
47 type_tag: Option<TypeTag>,
50 absent: Absent,
51}
52
53impl ProjectedField {
54 pub fn new(column_id: u16, type_tag: TypeTag, absent: Absent) -> crate::Result<Self> {
63 if intrinsic_type(column_id).is_some() {
64 return Err(Error::Projection(
65 "projection: an intrinsic column is projected by id, not declared",
66 ));
67 }
68 if type_tag.fixed_width() == Some(0) {
69 return Err(Error::Projection(
70 "projection: a fixed-width field has a width of zero",
71 ));
72 }
73 if let (Absent::Default(value), Some(width)) = (&absent, type_tag.fixed_width())
74 && value.len() != usize::from(width)
75 {
76 return Err(Error::Projection(
77 "projection: a default does not fit its field's width",
78 ));
79 }
80 Ok(Self {
81 column_id,
82 type_tag: Some(type_tag),
83 absent,
84 })
85 }
86
87 #[must_use]
92 pub fn by_id(column_id: u16) -> Self {
93 Self {
94 column_id,
95 type_tag: intrinsic_type(column_id),
96 absent: Absent::Null,
97 }
98 }
99
100 #[must_use]
102 pub fn column_id(&self) -> u16 {
103 self.column_id
104 }
105
106 #[must_use]
108 pub fn type_tag(&self) -> Option<TypeTag> {
109 self.type_tag
110 }
111
112 #[must_use]
114 pub fn absent(&self) -> &Absent {
115 &self.absent
116 }
117}
118
119fn intrinsic_type(column_id: u16) -> Option<TypeTag> {
121 match column_id {
122 COL_USER_KEY => Some(TypeTag::Bytes),
123 COL_SEQNO => Some(TypeTag::Number(Number::U64_LE)),
124 COL_VALUE_TYPE => Some(TypeTag::Fixed(1)),
125 _ => None,
126 }
127}
128
129pub trait ValueProjector: Send + Sync {
136 fn project(&self, key: &[u8], value: &[u8], row: &mut ProjectedRow<'_>) -> crate::Result<()>;
145}
146
147pub struct ProjectedRow<'a> {
149 fields: &'a [ProjectedField],
150 cells: &'a mut [Option<Vec<u8>>],
151}
152
153impl<'a> ProjectedRow<'a> {
154 pub(crate) fn new(fields: &'a [ProjectedField], cells: &'a mut [Option<Vec<u8>>]) -> Self {
156 Self { fields, cells }
157 }
158
159 #[must_use]
161 pub fn fields(&self) -> &[ProjectedField] {
162 self.fields
163 }
164
165 pub fn set(&mut self, index: usize, cell: &[u8]) -> crate::Result<()> {
173 let field = self.fields.get(index).ok_or(Error::Projection(
174 "projection: the projector set a field the projection does not have",
175 ))?;
176 let type_tag = field.type_tag.ok_or(Error::Projection(
177 "projection: a row value is projected into a field with no declared type",
178 ))?;
179 if let Some(width) = type_tag.fixed_width()
180 && cell.len() != usize::from(width)
181 {
182 return Err(Error::Projection(
183 "projection: the projector wrote a cell of the wrong width",
184 ));
185 }
186 let slot = self.cells.get_mut(index).ok_or(Error::Projection(
187 "projection: the projector set a field the projection does not have",
188 ))?;
189 let buf = slot.get_or_insert_with(Vec::new);
190 buf.clear();
191 buf.extend_from_slice(cell);
192 Ok(())
193 }
194}
195
196#[derive(Clone, Default)]
200pub struct Projection {
201 fields: Vec<ProjectedField>,
202 projector: Option<Arc<dyn ValueProjector>>,
203}
204
205impl core::fmt::Debug for Projection {
206 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
207 f.debug_struct("Projection")
208 .field("fields", &self.fields)
209 .field("projector", &self.projector.is_some())
210 .finish()
211 }
212}
213
214impl Projection {
215 #[must_use]
217 pub fn new() -> Self {
218 Self::default()
219 }
220
221 #[must_use]
223 pub fn column(mut self, column_id: u16) -> Self {
224 self.fields.push(ProjectedField::by_id(column_id));
225 self
226 }
227
228 #[must_use]
230 pub fn field(mut self, field: ProjectedField) -> Self {
231 self.fields.push(field);
232 self
233 }
234
235 #[must_use]
237 pub fn projector(mut self, projector: Arc<dyn ValueProjector>) -> Self {
238 self.projector = Some(projector);
239 self
240 }
241
242 #[must_use]
244 pub fn fields(&self) -> &[ProjectedField] {
245 &self.fields
246 }
247
248 #[must_use]
250 pub fn value_projector(&self) -> Option<&Arc<dyn ValueProjector>> {
251 self.projector.as_ref()
252 }
253
254 pub(crate) fn column_ids(&self) -> Vec<u16> {
256 self.fields.iter().map(|f| f.column_id).collect()
257 }
258}
259
260impl From<&[u16]> for Projection {
261 fn from(ids: &[u16]) -> Self {
262 Self {
263 fields: ids.iter().copied().map(ProjectedField::by_id).collect(),
264 projector: None,
265 }
266 }
267}
268
269impl<const N: usize> From<&[u16; N]> for Projection {
270 fn from(ids: &[u16; N]) -> Self {
271 Self::from(ids.as_slice())
272 }
273}
274
275impl From<&Vec<u16>> for Projection {
276 fn from(ids: &Vec<u16>) -> Self {
277 Self::from(ids.as_slice())
278 }
279}
280
281impl From<&Self> for Projection {
282 fn from(projection: &Self) -> Self {
283 projection.clone()
284 }
285}
286
287pub fn is_declared(field: &ProjectedField) -> bool {
290 field.type_tag.is_some() && intrinsic_type(field.column_id).is_none()
291}
292
293pub fn project_decided(
302 batch: ColumnBatch,
303 fields: &[ProjectedField],
304 projector: Option<&dyn ValueProjector>,
305 whole_id: u16,
306) -> crate::Result<ColumnBatch> {
307 let row_count = batch.row_count;
308 let rows = row_count as usize;
309 let find = |id: u16| batch.columns.iter().find(|c| c.column_id == id);
310 let (Some(values), Some(keys)) = (find(whole_id), find(COL_USER_KEY)) else {
311 return Err(MISSING_SCAN_COLUMN);
312 };
313 let types = find(COL_VALUE_TYPE);
314 let declared: Vec<ProjectedField> = fields.iter().filter(|f| is_declared(f)).cloned().collect();
315
316 let mut read: Vec<Option<Vec<Option<Vec<u8>>>>> = Vec::with_capacity(rows);
319 let mut cells: Vec<Option<Vec<u8>>> = alloc::vec![None; declared.len()];
320 for row in 0..row_count {
321 let is_value = match types {
322 Some(types) => {
323 let byte = *types.data.get(row as usize).ok_or(MISSING_SCAN_COLUMN)?;
324 returns_a_value(
325 crate::ValueType::try_from(byte)
326 .map_err(|()| Error::InvalidTag(("ValueType", byte)))?,
327 )
328 }
329 None => true,
330 };
331 if !(is_value && values.is_valid(row)) {
332 read.push(None);
333 continue;
334 }
335 let projector = projector.ok_or(UNREADABLE)?;
336 cells.fill(None);
337 let key = bytes_column_row(&keys.data, row_count, row)?;
338 let value = bytes_column_row(&values.data, row_count, row)?;
339 projector.project(key, value, &mut ProjectedRow::new(&declared, &mut cells))?;
340 read.push(Some(core::mem::take(&mut cells)));
341 cells = alloc::vec![None; declared.len()];
342 }
343 if read.iter().all(Option::is_none) {
344 return Ok(batch);
345 }
346
347 let ColumnBatch {
348 row_count,
349 mut columns,
350 } = batch;
351 for (index, field) in declared.iter().enumerate() {
352 let Some(at) = columns.iter().position(|c| c.column_id == field.column_id) else {
353 return Err(MISSING_SCAN_COLUMN);
354 };
355 let old = columns.get(at).ok_or(MISSING_SCAN_COLUMN)?;
356 let mut column: Vec<Option<Vec<u8>>> = Vec::with_capacity(rows);
357 for (row, projected) in (0..row_count).zip(&read) {
358 column.push(match projected {
359 Some(cells) => cells.get(index).cloned().flatten(),
360 None if old.is_valid(row) => Some(cell_of(old, row_count, row)?.to_vec()),
361 None => None,
362 });
363 }
364 let built = built_column(field, &column)?;
365 if let Some(slot) = columns.get_mut(at) {
366 *slot = built;
367 }
368 }
369 Ok(ColumnBatch { row_count, columns })
370}
371
372fn cell_of(column: &Column, rows: u32, row: u32) -> crate::Result<&[u8]> {
374 match column.type_tag.fixed_width() {
375 Some(width) => {
376 let width = usize::from(width);
377 let start = row as usize * width;
378 column
379 .data
380 .get(start..start + width)
381 .ok_or(MISSING_SCAN_COLUMN)
382 }
383 None => bytes_column_row(&column.data, rows, row),
384 }
385}
386
387pub(super) const UNREADABLE: Error = Error::Projection(
389 "projection: a row returned stores its value whole, and declared fields are read out \
390 of it through a projector, which is not set",
391);
392
393fn returns_a_value(value_type: crate::ValueType) -> bool {
397 matches!(
398 value_type,
399 crate::ValueType::Value | crate::ValueType::MergeOperand
400 )
401}
402
403fn built_column(field: &ProjectedField, cells: &[Option<Vec<u8>>]) -> crate::Result<Column> {
405 let type_tag = field.type_tag.ok_or(ABSENT_FIELD)?;
406 let count = cells.len();
407 let validity = cells.iter().any(Option::is_none).then(|| {
408 let mut bits = alloc::vec![0u8; count.div_ceil(8)];
409 for (row, cell) in cells.iter().enumerate() {
410 if cell.is_some()
411 && let Some(byte) = bits.get_mut(row / 8)
412 {
413 *byte |= 1 << (row % 8);
414 }
415 }
416 bits
417 });
418 let data = match type_tag.fixed_width() {
419 Some(width) => {
420 let width = usize::from(width);
421 let mut data = alloc::vec![0u8; count * width];
422 for (slot, cell) in data.chunks_exact_mut(width).zip(cells) {
423 if let Some(cell) = cell {
424 slot.copy_from_slice(cell);
425 }
426 }
427 Slice::from(data)
428 }
429 None => frame_bytes_column(count, || cells.iter().map(|c| c.as_deref().unwrap_or(&[])))?,
430 };
431 Ok(Column {
432 column_id: field.column_id,
433 type_tag,
434 validity,
435 data,
436 })
437}
438
439const MISSING_SCAN_COLUMN: Error =
442 Error::InvalidHeader("columnar_scan: a whole-value batch is missing its key or value column");
443
444pub fn absent_column(
448 field: &ProjectedField,
449 rows: u32,
450 is_value: &dyn Fn(u32) -> bool,
451) -> crate::Result<Column> {
452 let type_tag = field.type_tag.ok_or(ABSENT_FIELD)?;
453 let count = rows as usize;
454 let (validity, data) = match &field.absent {
455 Absent::Error if (0..rows).any(is_value) => return Err(ABSENT_FIELD),
456 Absent::Null | Absent::Error => {
457 let data = match type_tag.fixed_width() {
458 Some(width) => Slice::from(alloc::vec![0u8; count * usize::from(width)]),
459 None => frame_bytes_column(count, || core::iter::repeat_n(&[][..], count))?,
460 };
461 (Some(alloc::vec![0u8; count.div_ceil(8)]), data)
462 }
463 Absent::Default(value) => {
464 let data = match type_tag.fixed_width() {
465 Some(_) => Slice::from(value.repeat(count)),
466 None => frame_bytes_column(count, || core::iter::repeat_n(&**value, count))?,
467 };
468 (None, data)
469 }
470 };
471 Ok(Column {
472 column_id: field.column_id,
473 type_tag,
474 validity,
475 data,
476 })
477}
478
479const ABSENT_FIELD: Error = Error::Projection(
482 "projection: a projected field is absent from a row and its declaration does not allow it",
483);
484
485pub fn conform(batch: ColumnBatch, fields: &[ProjectedField]) -> crate::Result<ColumnBatch> {
496 conform_with(batch, fields, Conform::Strict).map(|(batch, _)| batch)
497}
498
499pub fn conform_lenient(
512 batch: ColumnBatch,
513 fields: &[ProjectedField],
514 judged: Option<u16>,
515) -> crate::Result<(ColumnBatch, Vec<u16>)> {
516 conform_with(batch, fields, Conform::Lenient { judged })
517}
518
519pub const MISTYPED: Error =
521 Error::Projection("projection: a segment stores a projected field under another type");
522
523#[derive(Clone, Copy)]
525enum Conform {
526 Strict,
528 Lenient { judged: Option<u16> },
530}
531
532fn conform_with(
537 batch: ColumnBatch,
538 fields: &[ProjectedField],
539 mode: Conform,
540) -> crate::Result<(ColumnBatch, Vec<u16>)> {
541 let strict = matches!(mode, Conform::Strict);
542 let defers = |field: &ProjectedField| match mode {
545 Conform::Strict => false,
546 Conform::Lenient { judged } => {
547 judged != Some(field.column_id) && matches!(field.absent, Absent::Default(_))
548 }
549 };
550 let ColumnBatch {
551 row_count,
552 mut columns,
553 } = batch;
554 let types = columns
555 .iter()
556 .find(|c| c.column_id == COL_VALUE_TYPE)
557 .map(|c| c.data.clone());
558 let is_value = |row: u32| {
559 strict
560 && types.as_ref().is_none_or(|types| {
561 types
562 .get(row as usize)
563 .and_then(|&byte| crate::ValueType::try_from(byte).ok())
564 .is_some_and(returns_a_value)
565 })
566 };
567 let mut mistyped = Vec::new();
568 let mut out = Vec::with_capacity(fields.len().max(columns.len()));
569 for field in fields {
570 let at = columns.iter().position(|c| c.column_id == field.column_id);
571 let column = match at {
572 Some(at) => {
573 let column = columns.remove(at);
574 if field.type_tag.is_some_and(|t| t != column.type_tag) {
575 if strict {
576 return Err(MISTYPED);
577 }
578 mistyped.push(field.column_id);
581 let null = ProjectedField {
582 absent: Absent::Null,
583 ..field.clone()
584 };
585 out.push(absent_column(&null, row_count, &|_| false)?);
586 continue;
587 }
588 if defers(field) {
589 column
590 } else {
591 fill_nulls(column, field, row_count, &is_value)?
592 }
593 }
594 None if !strict && field.type_tag.is_none() => continue,
597 None if defers(field) => {
598 let null = ProjectedField {
599 absent: Absent::Null,
600 ..field.clone()
601 };
602 absent_column(&null, row_count, &|_| false)?
603 }
604 None => absent_column(field, row_count, &is_value)?,
605 };
606 out.push(column);
607 }
608 out.extend(columns);
609 Ok((
610 ColumnBatch {
611 row_count,
612 columns: out,
613 },
614 mistyped,
615 ))
616}
617
618fn fill_nulls(
622 column: Column,
623 field: &ProjectedField,
624 rows: u32,
625 is_value: &dyn Fn(u32) -> bool,
626) -> crate::Result<Column> {
627 let Some(validity) = &column.validity else {
628 return Ok(column);
629 };
630 let is_null = |row: u32| {
631 validity
632 .get(row as usize / 8)
633 .is_none_or(|byte| byte >> (row % 8) & 1 == 0)
634 };
635 if !(0..rows).any(is_null) {
636 return Ok(column);
637 }
638 let value = match &field.absent {
639 Absent::Error if (0..rows).any(|row| is_null(row) && is_value(row)) => {
640 return Err(ABSENT_FIELD);
641 }
642 Absent::Null | Absent::Error => return Ok(column),
643 Absent::Default(value) => value,
644 };
645 let count = rows as usize;
646 let data = if let Some(width) = column.type_tag.fixed_width() {
647 let width = usize::from(width);
648 let mut data = column.data.to_vec();
649 for row in (0..rows).filter(|&row| is_null(row)) {
650 let at = row as usize * width;
651 data.get_mut(at..at + width)
652 .ok_or(Error::InvalidHeader("columnar: fixed column row truncated"))?
653 .copy_from_slice(value);
654 }
655 Slice::from(data)
656 } else {
657 let cells = (0..rows)
658 .map(|row| {
659 if is_null(row) {
660 Ok(&**value)
661 } else {
662 bytes_column_row(&column.data, rows, row)
663 }
664 })
665 .collect::<crate::Result<Vec<&[u8]>>>()?;
666 frame_bytes_column(count, || cells.iter().copied())?
667 };
668 Ok(Column {
669 column_id: column.column_id,
670 type_tag: column.type_tag,
671 validity: None,
672 data,
673 })
674}
675
676#[cfg(test)]
677#[expect(clippy::unwrap_used, reason = "test code")]
678mod tests;