lance_core/utils/
fragment_reuse.rs1use std::sync::Arc;
7
8use async_trait::async_trait;
9
10use super::row_addr_remap::RowAddrRemap;
11use crate::deepsize::{Context, DeepSizeOf};
12use crate::{Error, Result};
13
14#[async_trait]
21pub trait MappingReader: Send + Sync + std::fmt::Debug + DeepSizeOf {
22 async fn remap_row_id(&self, row_id: u64) -> Result<Option<u64>>;
24
25 async fn remap_row_ids(&self, row_ids: &[u64]) -> Result<Vec<Option<u64>>> {
27 let mut mapped = Vec::with_capacity(row_ids.len());
28 for &row_id in row_ids {
29 mapped.push(self.remap_row_id(row_id).await?);
30 }
31 Ok(mapped)
32 }
33}
34
35#[derive(Debug)]
38pub struct OrderedCompactionMapping {
39 remap: Arc<RowAddrRemap>,
40}
41
42impl OrderedCompactionMapping {
43 pub fn new(remap: Arc<RowAddrRemap>) -> Self {
45 Self { remap }
46 }
47
48 fn map_row_id(&self, row_id: u64) -> Result<Option<u64>> {
49 self.remap.get(row_id).ok_or_else(|| {
50 Error::invalid_input(format!("row ID {row_id} is outside compaction mapping"))
51 })
52 }
53}
54
55impl DeepSizeOf for OrderedCompactionMapping {
56 fn deep_size_of_children(&self, context: &mut Context) -> usize {
57 self.remap.deep_size_of_children(context)
58 }
59}
60
61#[async_trait]
62impl MappingReader for OrderedCompactionMapping {
63 async fn remap_row_id(&self, row_id: u64) -> Result<Option<u64>> {
64 self.map_row_id(row_id)
65 }
66
67 async fn remap_row_ids(&self, row_ids: &[u64]) -> Result<Vec<Option<u64>>> {
68 row_ids
69 .iter()
70 .map(|&row_id| self.map_row_id(row_id))
71 .collect()
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::utils::address::RowAddress;
79 use crate::utils::row_addr_remap::GroupInputWithLayout;
80 use roaring::RoaringTreemap;
81
82 #[derive(Debug, DeepSizeOf)]
83 struct SingleRowReader;
84
85 #[async_trait]
86 impl MappingReader for SingleRowReader {
87 async fn remap_row_id(&self, row_id: u64) -> Result<Option<u64>> {
88 if row_id == u64::MAX {
89 return Err(Error::invalid_input("invalid test row ID"));
90 }
91 Ok((row_id != 0).then_some(row_id))
92 }
93 }
94
95 #[tokio::test]
96 async fn default_batch_preserves_positions_and_propagates_errors() {
97 let reader = SingleRowReader;
98 assert_eq!(
99 reader.remap_row_ids(&[2, 0, 1, 2]).await.unwrap(),
100 vec![Some(2), None, Some(1), Some(2)],
101 );
102 assert!(reader.remap_row_ids(&[]).await.unwrap().is_empty());
103 let error = reader.remap_row_ids(&[1, u64::MAX]).await.unwrap_err();
104 assert!(matches!(error, Error::InvalidInput { .. }));
105 assert!(error.to_string().contains("invalid test row ID"));
106 }
107
108 #[tokio::test]
109 async fn compaction_reader_preserves_legacy_mapping_semantics() {
110 let address = |fragment, offset| u64::from(RowAddress::new_from_parts(fragment, offset));
111 let remap = Arc::new(
112 RowAddrRemap::compact_with_layout([GroupInputWithLayout {
113 rewritten_old_row_addrs: RoaringTreemap::from_iter([
114 address(1, 0),
115 address(1, 2),
116 address(2, 0),
117 ]),
118 old_frags: vec![(1, 3), (2, 1)],
119 new_frags: vec![(3, 2), (4, 1)],
120 }])
121 .unwrap(),
122 );
123 let reader = OrderedCompactionMapping::new(remap.clone());
124 let input = [address(2, 0), address(1, 1), address(1, 0), address(1, 0)];
125 let expected: Vec<_> = input.iter().map(|&a| remap.get(a).unwrap()).collect();
126 assert_eq!(reader.remap_row_ids(&input).await.unwrap(), expected);
127 for (&row_id, &mapped) in input.iter().zip(&expected) {
128 assert_eq!(reader.remap_row_id(row_id).await.unwrap(), mapped);
129 }
130 assert!(reader.remap_row_ids(&[]).await.unwrap().is_empty());
131 for invalid in [address(99, 0), address(1, 3)] {
132 for error in [
133 reader.remap_row_id(invalid).await.unwrap_err(),
134 reader
135 .remap_row_ids(&[input[0], invalid])
136 .await
137 .unwrap_err(),
138 ] {
139 assert!(matches!(error, Error::InvalidInput { .. }));
140 assert!(error.to_string().contains("outside compaction mapping"));
141 }
142 }
143 }
144}