use crate::columnar::{decode_page_native, NativeColumn};
use crate::error::Result;
use crate::row_id_set::RowIdSet;
use crate::schema::TypeId;
use crate::sorted_run::{RunReader, SYS_ROW_ID};
pub trait Cursor: Send {
fn next_batch(&mut self) -> Result<Option<Vec<NativeColumn>>>;
fn remaining_rows(&self) -> usize;
fn projection_types(&self) -> Vec<TypeId>;
}
pub fn drain_cursor_to_columns(
cursor: &mut dyn Cursor,
projection: &[(u16, TypeId)],
) -> Result<Vec<(u16, NativeColumn)>> {
let ncols = projection.len();
let mut acc: Vec<Vec<NativeColumn>> = (0..ncols).map(|_| Vec::new()).collect();
while let Some(batch) = cursor.next_batch()? {
for (j, col) in batch.into_iter().enumerate() {
if j < ncols {
acc[j].push(col);
}
}
}
Ok(acc
.into_iter()
.enumerate()
.map(|(j, pieces)| {
let col = if pieces.is_empty() {
crate::columnar::null_native(projection[j].1, 0)
} else {
NativeColumn::concat(&pieces)
};
(projection[j].0, col)
})
.collect())
}
#[derive(Clone)]
pub(crate) struct PagePlan {
pub(crate) seq: usize,
pub(crate) positions: Vec<usize>,
}
pub struct NativePageCursor {
reader: RunReader,
projection: Vec<(u16, TypeId)>,
plans: Vec<PagePlan>,
next: usize,
overlay: Option<Vec<NativeColumn>>,
overlay_rows: usize,
}
impl NativePageCursor {
pub(crate) fn new_with_overlay(
reader: RunReader,
projection: Vec<(u16, TypeId)>,
plans: Vec<PagePlan>,
overlay: Option<Vec<NativeColumn>>,
) -> Self {
let overlay_rows = overlay
.as_ref()
.map(|cols| cols.first().map(|c| c.len()).unwrap_or(0))
.unwrap_or(0);
Self {
reader,
projection,
plans,
next: 0,
overlay,
overlay_rows,
}
}
pub fn projection_types(&self) -> Vec<TypeId> {
self.projection.iter().map(|(_, t)| *t).collect()
}
pub fn remaining_rows(&self) -> usize {
let pages: usize = self.plans[self.next..]
.iter()
.map(|p| p.positions.len())
.sum();
pages + self.overlay_rows
}
pub fn next_batch(&mut self) -> Result<Option<Vec<NativeColumn>>> {
while self.next < self.plans.len() {
let plan = self.plans[self.next].clone();
self.next += 1;
if plan.positions.is_empty() {
continue;
}
let nrows = self
.reader
.page_row_counts(SYS_ROW_ID)?
.get(plan.seq)
.copied()
.unwrap_or(0);
let mut cols = Vec::with_capacity(self.projection.len());
for (cid, ty) in &self.projection {
let col = if self.reader.has_column(*cid) {
let page = self.reader.read_page(*cid, plan.seq)?;
let decoded = decode_page_native(*ty, &page, nrows)?;
decoded.gather(&plan.positions)
} else {
crate::columnar::null_native(*ty, plan.positions.len())
};
cols.push(col);
}
return Ok(Some(cols));
}
if self.overlay_rows > 0 {
self.overlay_rows = 0;
if let Some(cols) = self.overlay.take() {
return Ok(Some(cols));
}
return Ok(Some(Vec::new()));
}
Ok(None)
}
}
impl Cursor for NativePageCursor {
fn next_batch(&mut self) -> Result<Option<Vec<NativeColumn>>> {
NativePageCursor::next_batch(self)
}
fn remaining_rows(&self) -> usize {
NativePageCursor::remaining_rows(self)
}
fn projection_types(&self) -> Vec<TypeId> {
NativePageCursor::projection_types(self)
}
}
const MERGE_BATCH_ROWS: usize = 65_536;
pub(crate) struct RunStream {
reader: RunReader,
survivors: Vec<(u64, usize, usize)>,
head: usize,
page_row_counts: Vec<usize>,
cur_page: Option<usize>,
cur_cols: Vec<NativeColumn>,
}
impl RunStream {
pub(crate) fn new(
reader: RunReader,
survivors: Vec<(u64, usize, usize)>,
page_row_counts: Vec<usize>,
) -> Self {
Self {
reader,
survivors,
head: 0,
page_row_counts,
cur_page: None,
cur_cols: Vec::new(),
}
}
}
pub struct MultiRunCursor {
streams: Vec<RunStream>,
projection: Vec<(u16, TypeId)>,
heap: std::collections::BinaryHeap<std::cmp::Reverse<(u64, usize)>>,
remaining: usize,
overlay: Option<Vec<NativeColumn>>,
overlay_rows: usize,
overlay_done: bool,
}
impl MultiRunCursor {
pub(crate) fn new(
streams: Vec<RunStream>,
projection: Vec<(u16, TypeId)>,
heap: std::collections::BinaryHeap<std::cmp::Reverse<(u64, usize)>>,
remaining: usize,
overlay: Option<Vec<NativeColumn>>,
) -> Self {
let overlay_rows = overlay
.as_ref()
.map(|cols| cols.first().map(|c| c.len()).unwrap_or(0))
.unwrap_or(0);
Self {
streams,
projection,
heap,
remaining,
overlay,
overlay_rows,
overlay_done: false,
}
}
fn decode_page(&mut self, sidx: usize, page_seq: usize) -> Result<()> {
let ncols = self.projection.len();
let stream = &mut self.streams[sidx];
let nrows = stream.page_row_counts.get(page_seq).copied().unwrap_or(0);
let mut cols = Vec::with_capacity(ncols);
for (cid, ty) in &self.projection {
let col = if stream.reader.has_column(*cid) {
let page = stream.reader.read_page(*cid, page_seq)?;
decode_page_native(*ty, &page, nrows)?
} else {
crate::columnar::null_native(*ty, nrows)
};
cols.push(col);
}
stream.cur_page = Some(page_seq);
stream.cur_cols = cols;
Ok(())
}
}
impl Cursor for MultiRunCursor {
fn projection_types(&self) -> Vec<TypeId> {
self.projection.iter().map(|(_, t)| *t).collect()
}
fn remaining_rows(&self) -> usize {
self.remaining + self.overlay_rows
}
fn next_batch(&mut self) -> Result<Option<Vec<NativeColumn>>> {
if !self.heap.is_empty() {
let mut segments: Vec<(usize, usize, Vec<usize>)> = Vec::new();
let mut count = 0usize;
while count < MERGE_BATCH_ROWS {
let Some(std::cmp::Reverse((_, sidx))) = self.heap.pop() else {
break;
};
let stream = &mut self.streams[sidx];
if stream.head >= stream.survivors.len() {
continue;
}
let (_rid, page_seq, pos) = stream.survivors[stream.head];
stream.head += 1;
if let Some(last) = segments.last_mut() {
if last.0 == sidx && last.1 == page_seq {
last.2.push(pos);
} else {
segments.push((sidx, page_seq, vec![pos]));
}
} else {
segments.push((sidx, page_seq, vec![pos]));
}
count += 1;
self.remaining -= 1;
if stream.head < stream.survivors.len() {
let next_rid = stream.survivors[stream.head].0;
self.heap.push(std::cmp::Reverse((next_rid, sidx)));
}
}
let ncols = self.projection.len();
if ncols == 0 {
return Ok(Some(Vec::new()));
}
let mut pieces: Vec<Vec<NativeColumn>> = vec![Vec::new(); ncols];
for (sidx, page_seq, positions) in &segments {
if self.streams[*sidx].cur_page != Some(*page_seq) {
self.decode_page(*sidx, *page_seq)?;
}
let cur_cols = &self.streams[*sidx].cur_cols;
for j in 0..ncols {
pieces[j].push(cur_cols[j].gather(positions));
}
}
let out: Vec<NativeColumn> = (0..ncols)
.map(|j| NativeColumn::concat(&pieces[j]))
.collect();
return Ok(Some(out));
}
if !self.overlay_done && self.overlay_rows > 0 {
self.overlay_done = true;
self.overlay_rows = 0;
if let Some(cols) = self.overlay.take() {
return Ok(Some(cols));
}
return Ok(Some(Vec::new()));
}
Ok(None)
}
}
pub(crate) fn build_page_plans(
visible_positions: &[usize],
rids: &[i64],
page_row_counts: &[usize],
survivors: Option<&RowIdSet>,
) -> Vec<PagePlan> {
debug_assert_eq!(visible_positions.len(), rids.len());
let mut starts = Vec::with_capacity(page_row_counts.len());
let mut acc = 0usize;
for &r in page_row_counts {
starts.push(acc);
acc += r;
}
let mut by_page: std::collections::BTreeMap<usize, Vec<usize>> =
std::collections::BTreeMap::new();
let n = visible_positions.len();
let selective = match survivors {
Some(set) if n > 0 => (set.len() as u64).saturating_mul(32) < n as u64,
_ => false,
};
if selective {
let set = survivors.unwrap();
for s in set.to_sorted_vec() {
let Ok(i) = rids.binary_search(&(s as i64)) else {
continue; };
let global = visible_positions[i];
let page_seq = match starts.partition_point(|&st| st <= global) {
0 => continue,
p => p - 1,
};
by_page
.entry(page_seq)
.or_default()
.push(global - starts[page_seq]);
}
} else {
for (i, &global) in visible_positions.iter().enumerate() {
if let Some(set) = survivors {
if !set.contains(rids[i] as u64) {
continue;
}
}
let page_seq = match starts.partition_point(|&s| s <= global) {
0 => continue,
p => p - 1,
};
let within = global - starts[page_seq];
by_page.entry(page_seq).or_default().push(within);
}
}
by_page
.into_iter()
.map(|(seq, mut positions)| {
positions.sort_unstable();
PagePlan { seq, positions }
})
.collect()
}