1use std::ops::RangeInclusive;
5use std::sync::Arc;
6
7use super::{RowIdSequence, U64Segment};
8use lance_core::deepsize::DeepSizeOf;
9use lance_core::utils::address::RowAddress;
10use lance_core::utils::deletion::DeletionVector;
11use lance_core::{Error, Result};
12use rangemap::RangeInclusiveMap;
13
14#[derive(Debug)]
27pub struct RowIdIndex(RangeInclusiveMap<u64, (U64Segment, U64Segment)>);
28
29pub struct FragmentRowIdIndex {
30 pub fragment_id: u32,
31 pub row_id_sequence: Arc<RowIdSequence>,
32 pub deletion_vector: Arc<DeletionVector>,
33}
34
35impl RowIdIndex {
36 pub fn new(fragment_indices: &[FragmentRowIdIndex]) -> Result<Self> {
38 let chunks = fragment_indices
39 .iter()
40 .flat_map(decompose_sequence)
41 .collect::<Vec<_>>();
42
43 let mut final_chunks = Vec::new();
44 for processed_chunk in prep_index_chunks(chunks) {
45 match processed_chunk {
46 RawIndexChunk::NonOverlapping(chunk) => {
47 final_chunks.push(chunk);
48 }
49 RawIndexChunk::Overlapping(_range, overlapping_chunks) => {
50 let merged_chunk = merge_overlapping_chunks(overlapping_chunks)?;
54 final_chunks.push(merged_chunk);
55 }
56 }
57 }
58
59 Ok(Self(RangeInclusiveMap::from_iter(final_chunks)))
60 }
61
62 pub fn get(&self, row_id: u64) -> Option<RowAddress> {
66 let (row_id_segment, address_segment) = self.0.get(&row_id)?;
67 let pos = row_id_segment.position(row_id)?;
68 let address = address_segment.get(pos)?;
69 Some(RowAddress::from(address))
70 }
71
72 pub fn get_many(&self, row_ids: &[u64]) -> Vec<Option<RowAddress>> {
79 let n = row_ids.len();
80 let mut out = vec![None; n];
81 if n == 0 {
82 return out;
83 }
84
85 let mut sorted: Vec<(u64, usize)> = row_ids.iter().copied().zip(0..n).collect();
86 sorted.sort_unstable_by_key(|&(id, _)| id);
87
88 let mut chunks = self.0.iter().peekable();
89 for (id, orig_idx) in sorted {
90 while let Some((range, _)) = chunks.peek() {
92 if *range.end() < id {
93 chunks.next();
94 } else {
95 break;
96 }
97 }
98 let Some((range, (row_id_seg, addr_seg))) = chunks.peek() else {
99 break;
100 };
101 if id < *range.start() {
102 continue; }
104 if let Some(pos) = row_id_seg.position(id)
105 && let Some(addr) = addr_seg.get(pos)
106 {
107 out[orig_idx] = Some(RowAddress::from(addr));
108 }
109 }
110 out
111 }
112}
113
114impl DeepSizeOf for RowIdIndex {
115 fn deep_size_of_children(&self, context: &mut lance_core::deepsize::Context) -> usize {
116 self.0
117 .iter()
118 .map(|(_, (row_id_segment, address_segment))| {
119 (2 * std::mem::size_of::<u64>())
120 + std::mem::size_of::<(U64Segment, U64Segment)>()
121 + row_id_segment.deep_size_of_children(context)
122 + address_segment.deep_size_of_children(context)
123 })
124 .sum()
125 }
126}
127
128fn decompose_sequence(
129 frag_index: &FragmentRowIdIndex,
130) -> Vec<(RangeInclusive<u64>, (U64Segment, U64Segment))> {
131 let mut start_address: u64 = RowAddress::first_row(frag_index.fragment_id).into();
132 let mut current_offset = 0u32;
133 let no_deletions = frag_index.deletion_vector.is_empty();
134
135 frag_index
136 .row_id_sequence
137 .0
138 .iter()
139 .filter_map(|segment| {
140 let segment_len = segment.len();
141
142 let result = if no_deletions {
143 decompose_segment_no_deletions(segment, start_address)
144 } else {
145 decompose_segment_with_deletions(
146 segment,
147 start_address,
148 current_offset,
149 &frag_index.deletion_vector,
150 )
151 };
152
153 current_offset += segment_len as u32;
154 start_address += segment_len as u64;
155
156 result
157 })
158 .collect()
159}
160
161fn build_chunk_from_pairs(pairs: Vec<(u64, u64)>) -> Option<IndexChunk> {
163 if pairs.is_empty() {
164 return None;
165 }
166 let (row_ids, addresses): (Vec<u64>, Vec<u64>) = pairs.into_iter().unzip();
167 let row_id_segment = U64Segment::from_iter(row_ids);
168 let address_segment = U64Segment::from_iter(addresses);
169 let coverage = row_id_segment.range()?;
170 Some((coverage, (row_id_segment, address_segment)))
171}
172
173fn decompose_segment_no_deletions(segment: &U64Segment, start_address: u64) -> Option<IndexChunk> {
175 match segment {
176 U64Segment::Range(range) if !range.is_empty() => {
177 let len = range.end - range.start;
178 let row_id_segment = U64Segment::Range(range.clone());
179 let address_segment = U64Segment::Range(start_address..start_address + len);
180 let coverage = range.start..=range.end - 1;
181 Some((coverage, (row_id_segment, address_segment)))
182 }
183 _ if segment.is_empty() => None,
184 _ => {
185 let pairs: Vec<(u64, u64)> = segment
187 .iter()
188 .enumerate()
189 .map(|(i, row_id)| (row_id, start_address + i as u64))
190 .collect();
191 build_chunk_from_pairs(pairs)
192 }
193 }
194}
195
196fn decompose_segment_with_deletions(
198 segment: &U64Segment,
199 start_address: u64,
200 current_offset: u32,
201 deletion_vector: &DeletionVector,
202) -> Option<IndexChunk> {
203 let pairs: Vec<(u64, u64)> = segment
204 .iter()
205 .enumerate()
206 .filter_map(|(i, row_id)| {
207 let row_offset = current_offset + i as u32;
208 if !deletion_vector.contains(row_offset) {
209 Some((row_id, start_address + i as u64))
210 } else {
211 None
212 }
213 })
214 .collect();
215 build_chunk_from_pairs(pairs)
216}
217
218type IndexChunk = (RangeInclusive<u64>, (U64Segment, U64Segment));
219
220#[derive(Debug)]
221enum RawIndexChunk {
222 NonOverlapping(IndexChunk),
223 Overlapping(RangeInclusive<u64>, Vec<IndexChunk>),
224}
225
226impl RawIndexChunk {
227 fn range_end(&self) -> u64 {
228 match self {
229 Self::NonOverlapping((range, _)) => *range.end(),
230 Self::Overlapping(range, _) => *range.end(),
231 }
232 }
233}
234
235fn prep_index_chunks(mut chunks: Vec<IndexChunk>) -> impl Iterator<Item = RawIndexChunk> {
240 chunks.sort_by_key(|(range, _)| u64::MAX - *range.start());
241
242 let mut output = Vec::new();
243
244 if let Some(first_chunk) = chunks.pop() {
246 output.push(RawIndexChunk::NonOverlapping(first_chunk));
247 } else {
248 return output.into_iter();
250 }
251
252 let mut current_range = 0..=0;
253 let mut current_overlap = Vec::new();
254 while let Some(chunk) = chunks.pop() {
255 debug_assert_eq!(
256 current_overlap
257 .iter()
258 .map(|(range, _): &IndexChunk| *range.start())
259 .min()
260 .unwrap_or_default(),
261 *current_range.start(),
262 );
263 debug_assert_eq!(
264 current_overlap
265 .iter()
266 .map(|(range, _): &IndexChunk| *range.end())
267 .max()
268 .unwrap_or_default(),
269 *current_range.end(),
270 );
271
272 if current_overlap.is_empty() {
273 let last_chunk_end = output.last().unwrap().range_end();
275 if *chunk.0.start() <= last_chunk_end {
276 match output.pop().unwrap() {
278 RawIndexChunk::NonOverlapping(chunk) => {
279 current_overlap.push(chunk);
280 }
281 _ => unreachable!(),
282 }
283 current_overlap.push(chunk);
284
285 let range_start = *current_overlap.first().unwrap().0.start();
286 let range_end = *current_overlap
287 .last()
288 .unwrap()
289 .0
290 .end()
291 .max(current_overlap.first().unwrap().0.end());
292 current_range = range_start..=range_end;
293 } else {
294 output.push(RawIndexChunk::NonOverlapping(chunk));
296 }
297 } else {
298 if chunk.0.start() <= current_range.end() {
300 let range_end = *chunk.0.end().max(current_range.end());
302 current_range = *current_range.start()..=range_end;
303
304 current_overlap.push(chunk);
305 } else {
306 output.push(RawIndexChunk::Overlapping(
308 std::mem::replace(&mut current_range, 0..=0),
309 std::mem::take(&mut current_overlap),
310 ));
311 output.push(RawIndexChunk::NonOverlapping(chunk));
312 }
313 }
314 }
315 debug_assert_eq!(
316 current_overlap
317 .iter()
318 .map(|(range, _): &IndexChunk| *range.start())
319 .min()
320 .unwrap_or_default(),
321 *current_range.start(),
322 );
323 debug_assert_eq!(
324 current_overlap
325 .iter()
326 .map(|(range, _): &IndexChunk| *range.end())
327 .max()
328 .unwrap_or_default(),
329 *current_range.end(),
330 );
331
332 if !current_overlap.is_empty() {
333 output.push(RawIndexChunk::Overlapping(
334 current_range.clone(),
335 current_overlap,
336 ));
337 }
338
339 output.into_iter()
340}
341
342fn merge_overlapping_chunks(overlapping_chunks: Vec<IndexChunk>) -> Result<IndexChunk> {
343 let total_capacity = overlapping_chunks
344 .iter()
345 .map(|(_, (row_ids, _))| row_ids.len())
346 .sum();
347 let mut values = Vec::with_capacity(total_capacity);
348 for (_, (row_ids, row_addrs)) in overlapping_chunks.iter() {
349 values.extend(row_ids.iter().zip(row_addrs.iter()));
350 }
351 values.sort_by_key(|(row_id, _)| *row_id);
352 if let Some(w) = values.windows(2).find(|w| w[0].0 == w[1].0) {
355 return Err(Error::internal(format!(
356 "row id index corrupt: stable row id {} is live in multiple fragments",
357 w[0].0
358 )));
359 }
360 let row_id_segment = U64Segment::from_iter(values.iter().map(|(row_id, _)| *row_id));
361 let address_segment = U64Segment::from_iter(values.iter().map(|(_, row_addr)| *row_addr));
362
363 let range = row_id_segment.range().unwrap();
364
365 Ok((range, (row_id_segment, address_segment)))
366}
367
368#[cfg(test)]
369mod tests {
370 use super::*;
371 use proptest::{prelude::Strategy, prop_assert_eq};
372
373 #[test]
374 fn test_new_index() {
375 let fragment_indices = vec![
376 FragmentRowIdIndex {
377 fragment_id: 10,
378 row_id_sequence: Arc::new(RowIdSequence(vec![
379 U64Segment::Range(0..10),
380 U64Segment::RangeWithHoles {
381 range: 10..17,
382 holes: vec![12, 15].into(),
383 },
384 U64Segment::SortedArray(vec![20, 25, 30].into()),
385 ])),
386 deletion_vector: Arc::new(DeletionVector::default()),
387 },
388 FragmentRowIdIndex {
389 fragment_id: 20,
390 row_id_sequence: Arc::new(RowIdSequence(vec![
391 U64Segment::RangeWithBitmap {
392 range: 17..20,
393 bitmap: [true, false, true].as_slice().into(),
394 },
395 U64Segment::Array(vec![40, 50, 60].into()),
396 ])),
397 deletion_vector: Arc::new(DeletionVector::default()),
398 },
399 ];
400
401 let index = RowIdIndex::new(&fragment_indices).unwrap();
402
403 assert_eq!(index.get(0), Some(RowAddress::new_from_parts(10, 0)));
405 assert_eq!(index.get(15), None);
406 assert_eq!(index.get(16), Some(RowAddress::new_from_parts(10, 14)));
407 assert_eq!(index.get(17), Some(RowAddress::new_from_parts(20, 0)));
408 assert_eq!(index.get(25), Some(RowAddress::new_from_parts(10, 16)));
409 assert_eq!(index.get(40), Some(RowAddress::new_from_parts(20, 2)));
410 assert_eq!(index.get(60), Some(RowAddress::new_from_parts(20, 4)));
411 assert_eq!(index.get(61), None);
412 }
413
414 #[test]
415 fn test_new_index_overlap() {
416 let fragment_indices = vec![
417 FragmentRowIdIndex {
418 fragment_id: 23,
419 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::SortedArray(
420 vec![3, 6, 9].into(),
421 )])),
422 deletion_vector: Arc::new(DeletionVector::default()),
423 },
424 FragmentRowIdIndex {
425 fragment_id: 42,
426 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::SortedArray(
427 vec![2, 5, 8].into(),
428 )])),
429 deletion_vector: Arc::new(DeletionVector::default()),
430 },
431 FragmentRowIdIndex {
432 fragment_id: 10,
433 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::SortedArray(
434 vec![1, 4, 7].into(),
435 )])),
436 deletion_vector: Arc::new(DeletionVector::default()),
437 },
438 ];
439
440 let index = RowIdIndex::new(&fragment_indices).unwrap();
441
442 assert_eq!(index.get(1), Some(RowAddress::new_from_parts(10, 0)));
444 assert_eq!(index.get(2), Some(RowAddress::new_from_parts(42, 0)));
445 assert_eq!(index.get(3), Some(RowAddress::new_from_parts(23, 0)));
446 assert_eq!(index.get(4), Some(RowAddress::new_from_parts(10, 1)));
447 assert_eq!(index.get(5), Some(RowAddress::new_from_parts(42, 1)));
448 assert_eq!(index.get(6), Some(RowAddress::new_from_parts(23, 1)));
449 assert_eq!(index.get(7), Some(RowAddress::new_from_parts(10, 2)));
450 assert_eq!(index.get(8), Some(RowAddress::new_from_parts(42, 2)));
451 assert_eq!(index.get(9), Some(RowAddress::new_from_parts(23, 2)));
452 }
453
454 #[test]
455 fn test_new_index_unsorted_row_ids() {
456 let fragment_indices = vec![
458 FragmentRowIdIndex {
459 fragment_id: 10,
460 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Array(
461 vec![9, 3, 6].into(), )])),
463 deletion_vector: Arc::new(DeletionVector::default()),
464 },
465 FragmentRowIdIndex {
466 fragment_id: 20,
467 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Array(
468 vec![8, 2, 5].into(), )])),
470 deletion_vector: Arc::new(DeletionVector::default()),
471 },
472 FragmentRowIdIndex {
473 fragment_id: 30,
474 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Array(
475 vec![7, 1, 4].into(), )])),
477 deletion_vector: Arc::new(DeletionVector::default()),
478 },
479 ];
480
481 let index = RowIdIndex::new(&fragment_indices).unwrap();
482
483 assert_eq!(index.get(1), Some(RowAddress::new_from_parts(30, 1)));
485 assert_eq!(index.get(2), Some(RowAddress::new_from_parts(20, 1)));
486 assert_eq!(index.get(3), Some(RowAddress::new_from_parts(10, 1)));
487 assert_eq!(index.get(4), Some(RowAddress::new_from_parts(30, 2)));
488 assert_eq!(index.get(5), Some(RowAddress::new_from_parts(20, 2)));
489 assert_eq!(index.get(6), Some(RowAddress::new_from_parts(10, 2)));
490 assert_eq!(index.get(7), Some(RowAddress::new_from_parts(30, 0)));
491 assert_eq!(index.get(8), Some(RowAddress::new_from_parts(20, 0)));
492 assert_eq!(index.get(9), Some(RowAddress::new_from_parts(10, 0)));
493
494 assert_eq!(index.get(0), None);
496 assert_eq!(index.get(10), None);
497 }
498
499 #[test]
500 fn test_new_index_partial_overlap() {
501 let fragment_indices = vec![
502 FragmentRowIdIndex {
503 fragment_id: 0,
504 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::RangeWithHoles {
505 range: 0..100,
506 holes: vec![50].into(),
507 }])),
508 deletion_vector: Arc::new(DeletionVector::default()),
509 },
510 FragmentRowIdIndex {
511 fragment_id: 1,
512 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Range(50..51)])),
513 deletion_vector: Arc::new(DeletionVector::default()),
514 },
515 ];
516
517 let index = RowIdIndex::new(&fragment_indices).unwrap();
518
519 assert_eq!(index.get(0), Some(RowAddress::new_from_parts(0, 0)));
521 assert_eq!(index.get(49), Some(RowAddress::new_from_parts(0, 49)));
522 assert_eq!(index.get(50), Some(RowAddress::new_from_parts(1, 0)));
523 assert_eq!(index.get(51), Some(RowAddress::new_from_parts(0, 50)));
524 assert_eq!(index.get(99), Some(RowAddress::new_from_parts(0, 98)));
525 }
526
527 #[test]
528 fn test_overlapping_chunks_sparse_with_deletions() {
529 let fragment_indices = vec![
532 FragmentRowIdIndex {
533 fragment_id: 10,
534 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::SortedArray(
535 vec![1, 3, 5, 7, 9].into(),
536 )])),
537 deletion_vector: Arc::new(DeletionVector::default()),
538 },
539 FragmentRowIdIndex {
540 fragment_id: 20,
541 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::SortedArray(
542 vec![0, 2, 4, 6, 8].into(),
543 )])),
544 deletion_vector: Arc::new(DeletionVector::from_iter(vec![2])),
546 },
547 ];
548
549 let index = RowIdIndex::new(&fragment_indices).unwrap();
550
551 assert_eq!(index.get(0), Some(RowAddress::new_from_parts(20, 0)));
552 assert_eq!(index.get(1), Some(RowAddress::new_from_parts(10, 0)));
553 assert_eq!(index.get(2), Some(RowAddress::new_from_parts(20, 1)));
554 assert_eq!(index.get(3), Some(RowAddress::new_from_parts(10, 1)));
555 assert_eq!(index.get(4), None);
556 assert_eq!(index.get(6), Some(RowAddress::new_from_parts(20, 3)));
558 assert_eq!(index.get(8), Some(RowAddress::new_from_parts(20, 4)));
559 assert_eq!(index.get(9), Some(RowAddress::new_from_parts(10, 4)));
560 }
561
562 #[test]
563 fn test_index_with_deletion_vector() {
564 let deletion_vector = DeletionVector::from_iter(vec![2, 3]);
565
566 let fragment_indices = vec![FragmentRowIdIndex {
567 fragment_id: 10,
568 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Range(0..6)])),
569 deletion_vector: Arc::new(deletion_vector),
570 }];
571
572 let index = RowIdIndex::new(&fragment_indices).unwrap();
573
574 assert_eq!(index.get(0), Some(RowAddress::new_from_parts(10, 0)));
575 assert_eq!(index.get(1), Some(RowAddress::new_from_parts(10, 1)));
576 assert_eq!(index.get(4), Some(RowAddress::new_from_parts(10, 4)));
577 assert_eq!(index.get(5), Some(RowAddress::new_from_parts(10, 5)));
578
579 assert_eq!(index.get(2), None);
580 assert_eq!(index.get(3), None);
581 }
582
583 #[test]
584 fn test_empty_fragment_sequences() {
585 let fragment_indices = vec![
586 FragmentRowIdIndex {
587 fragment_id: 10,
588 row_id_sequence: Arc::new(RowIdSequence(vec![])),
589 deletion_vector: Arc::new(DeletionVector::default()),
590 },
591 FragmentRowIdIndex {
592 fragment_id: 20,
593 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Range(5..8)])),
594 deletion_vector: Arc::new(DeletionVector::default()),
595 },
596 ];
597
598 let index = RowIdIndex::new(&fragment_indices).unwrap();
599
600 assert_eq!(index.get(5), Some(RowAddress::new_from_parts(20, 0)));
601 assert_eq!(index.get(7), Some(RowAddress::new_from_parts(20, 2)));
602 assert_eq!(index.get(4), None);
603 }
604
605 #[test]
606 fn test_completely_empty_index() {
607 let fragment_indices = vec![];
608 let index = RowIdIndex::new(&fragment_indices).unwrap();
609
610 assert_eq!(index.get(0), None);
611 assert_eq!(index.get(100), None);
612 }
613
614 #[test]
615 fn test_non_overlapping_ranges() {
616 let fragment_indices = vec![
617 FragmentRowIdIndex {
618 fragment_id: 10,
619 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Range(0..5)])),
620 deletion_vector: Arc::new(DeletionVector::default()),
621 },
622 FragmentRowIdIndex {
623 fragment_id: 20,
624 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Range(5..10)])),
625 deletion_vector: Arc::new(DeletionVector::default()),
626 },
627 FragmentRowIdIndex {
628 fragment_id: 30,
629 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Range(10..15)])),
630 deletion_vector: Arc::new(DeletionVector::default()),
631 },
632 ];
633
634 let index = RowIdIndex::new(&fragment_indices).unwrap();
635
636 assert_eq!(index.get(0), Some(RowAddress::new_from_parts(10, 0)));
637 assert_eq!(index.get(4), Some(RowAddress::new_from_parts(10, 4)));
638 assert_eq!(index.get(5), Some(RowAddress::new_from_parts(20, 0)));
639 assert_eq!(index.get(9), Some(RowAddress::new_from_parts(20, 4)));
640 assert_eq!(index.get(10), Some(RowAddress::new_from_parts(30, 0)));
641 assert_eq!(index.get(14), Some(RowAddress::new_from_parts(30, 4)));
642 }
643
644 fn arbitrary_row_ids(
645 num_fragments_range: std::ops::Range<usize>,
646 frag_size_range: std::ops::Range<usize>,
647 ) -> impl Strategy<Value = Vec<(u32, Arc<RowIdSequence>)>> {
648 let fragment_sizes = proptest::collection::vec(frag_size_range, num_fragments_range);
649 fragment_sizes.prop_flat_map(|fragment_sizes| {
650 let num_rows = fragment_sizes.iter().sum::<usize>() as u64;
651 let row_ids = 0..num_rows;
652 let row_ids = row_ids.collect::<Vec<_>>();
653 let row_ids_shuffled = proptest::strategy::Just(row_ids).prop_shuffle();
654 row_ids_shuffled.prop_map(move |row_ids| {
655 let mut sequences = Vec::with_capacity(fragment_sizes.len());
656 let mut i = 0;
657 for size in &fragment_sizes {
658 let end = i + size;
659 let sequence =
660 RowIdSequence(vec![U64Segment::from_slice(row_ids[i..end].into())]);
661 sequences.push((i as u32, Arc::new(sequence)));
662 i = end;
663 }
664 sequences
665 })
666 })
667 }
668
669 #[test]
670 fn test_large_range_segments_no_deletions() {
671 let rows_per_fragment = 250_000u64;
675 let num_fragments = 100u32;
676 let mut offset = 0u64;
677
678 let fragment_indices: Vec<FragmentRowIdIndex> = (0..num_fragments)
679 .map(|frag_id| {
680 let start = offset;
681 offset += rows_per_fragment;
682 FragmentRowIdIndex {
683 fragment_id: frag_id,
684 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Range(
685 start..start + rows_per_fragment,
686 )])),
687 deletion_vector: Arc::new(DeletionVector::default()),
688 }
689 })
690 .collect();
691
692 let start = std::time::Instant::now();
693 let index = RowIdIndex::new(&fragment_indices).unwrap();
694 let elapsed = start.elapsed();
695
696 assert_eq!(index.get(0), Some(RowAddress::new_from_parts(0, 0)));
698 assert_eq!(
699 index.get(rows_per_fragment - 1),
700 Some(RowAddress::new_from_parts(0, rows_per_fragment as u32 - 1))
701 );
702 assert_eq!(
703 index.get(rows_per_fragment),
704 Some(RowAddress::new_from_parts(1, 0))
705 );
706 let last_row = num_fragments as u64 * rows_per_fragment - 1;
707 assert_eq!(
708 index.get(last_row),
709 Some(RowAddress::new_from_parts(
710 num_fragments - 1,
711 rows_per_fragment as u32 - 1
712 ))
713 );
714 assert_eq!(index.get(last_row + 1), None);
715
716 assert!(
719 elapsed.as_secs() < 1,
720 "Index build took {:?} for {} fragments x {} rows = {} total rows. \
721 This suggests the O(rows) -> O(fragments) optimization is not working.",
722 elapsed,
723 num_fragments,
724 rows_per_fragment,
725 num_fragments as u64 * rows_per_fragment,
726 );
727 }
728
729 #[test]
730 fn test_large_range_segments_with_deletions() {
731 let rows_per_fragment = 1_000u64;
732 let num_fragments = 10u32;
733 let mut offset = 0u64;
734
735 let fragment_indices: Vec<FragmentRowIdIndex> = (0..num_fragments)
736 .map(|frag_id| {
737 let start = offset;
738 offset += rows_per_fragment;
739
740 let mut deleted = roaring::RoaringBitmap::new();
742 for i in (0..rows_per_fragment as u32).step_by(3) {
743 deleted.insert(i);
744 }
745
746 FragmentRowIdIndex {
747 fragment_id: frag_id,
748 row_id_sequence: Arc::new(RowIdSequence(vec![U64Segment::Range(
749 start..start + rows_per_fragment,
750 )])),
751 deletion_vector: Arc::new(DeletionVector::Bitmap(deleted)),
752 }
753 })
754 .collect();
755
756 let index = RowIdIndex::new(&fragment_indices).unwrap();
757
758 assert_eq!(index.get(0), None);
761 assert_eq!(index.get(3), None);
763
764 assert_eq!(index.get(1), Some(RowAddress::new_from_parts(0, 1)));
767 assert_eq!(index.get(2), Some(RowAddress::new_from_parts(0, 2)));
769 assert_eq!(index.get(4), Some(RowAddress::new_from_parts(0, 4)));
771
772 assert_eq!(index.get(rows_per_fragment), None);
775 assert_eq!(
777 index.get(rows_per_fragment + 1),
778 Some(RowAddress::new_from_parts(1, 1))
779 );
780
781 let last_row = num_fragments as u64 * rows_per_fragment - 1;
784 assert_eq!(index.get(last_row), None);
785 assert_eq!(
787 index.get(last_row - 1),
788 Some(RowAddress::new_from_parts(num_fragments - 1, 998))
789 );
790
791 assert_eq!(index.get(last_row + 1), None);
793 }
794
795 proptest::proptest! {
796 #[test]
797 fn test_new_index_robustness(row_ids in arbitrary_row_ids(0..5, 0..32)) {
798 let fragment_indices: Vec<FragmentRowIdIndex> = row_ids
799 .iter()
800 .map(|(frag_id, sequence)| FragmentRowIdIndex {
801 fragment_id: *frag_id,
802 row_id_sequence: sequence.clone(),
803 deletion_vector: Arc::new(DeletionVector::default()),
804 })
805 .collect();
806
807 let index = RowIdIndex::new(&fragment_indices).unwrap();
808 for (frag_id, sequence) in row_ids.iter() {
809 for (local_offset, row_id) in sequence.iter().enumerate() {
810 prop_assert_eq!(
811 index.get(row_id),
812 Some(RowAddress::new_from_parts(*frag_id, local_offset as u32)),
813 "Row id {} in sequence {:?} not found in index {:?}",
814 row_id,
815 sequence,
816 index
817 );
818 }
819 }
820 }
821 }
822}