use crate::Dataset;
use lance_core::utils::address::RowAddress;
use lance_core::{Error, Result};
use lance_select::{RowAddrSelection, RowAddrTreeMap};
use roaring::RoaringBitmap;
use std::collections::BTreeMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FragmentSlice {
pub fragment_id: u32,
pub row_offset: u64,
pub row_count: u64,
}
pub(super) async fn physical_rows_from_slices(
dataset: &Dataset,
slices: &[FragmentSlice],
) -> Result<RowAddrTreeMap> {
let mut grouped = BTreeMap::<u32, Vec<&FragmentSlice>>::new();
for slice in slices {
grouped.entry(slice.fragment_id).or_default().push(slice);
}
let mut rows = RowAddrTreeMap::new();
for (fragment_id, slices) in grouped {
let fragment = dataset.get_fragment(fragment_id as usize).ok_or_else(|| Error::invalid_input(format!(
"fragment slice references fragment_id={fragment_id}, which is not present in dataset version={}", dataset.version().version
)))?;
let physical_row_count = fragment.physical_rows().await? as u64;
let mut offsets = RoaringBitmap::new();
for slice in slices {
let FragmentSlice {
row_offset,
row_count,
..
} = *slice;
let end = row_offset.checked_add(row_count).ok_or_else(|| Error::invalid_input(format!(
"fragment slice end overflow: fragment_id={fragment_id}, row_offset={row_offset}, row_count={row_count}, physical_row_count={physical_row_count}, dataset_version={}", dataset.version().version
)))?;
if end > physical_row_count || end > RowAddress::FRAGMENT_SIZE {
return Err(Error::invalid_input(format!(
"fragment slice is outside fragment bounds: fragment_id={fragment_id}, row_offset={row_offset}, row_count={row_count}, physical_row_count={physical_row_count}, dataset_version={}",
dataset.version().version
)));
}
if row_count != 0 {
offsets.insert_range(row_offset as u32..=(end - 1) as u32);
}
}
if !offsets.is_empty() {
rows.insert_bitmap(fragment_id, offsets);
}
}
Ok(rows)
}
pub async fn validate_physical_rows(dataset: &Dataset, rows: &RowAddrTreeMap) -> Result<()> {
for (fragment_id, selection) in rows.iter() {
let fragment = dataset
.get_fragment(*fragment_id as usize)
.ok_or_else(|| {
Error::invalid_input(format!(
"physical row selection references fragment_id={fragment_id}, which is not present in dataset version={}",
dataset.version().version
))
})?;
if let RowAddrSelection::Partial(offsets) = selection
&& let Some(max_offset) = offsets.max()
{
let physical_row_count = fragment.physical_rows().await? as u64;
if u64::from(max_offset) >= physical_row_count {
return Err(Error::invalid_input(format!(
"physical row selection for fragment_id={fragment_id} contains row_offset={max_offset}, but the fragment has physical_row_count={physical_row_count} in dataset version={}",
dataset.version().version
)));
}
}
}
Ok(())
}