use rudb_common::{Field, Result};
use rudb_pipeline::{Compaction, Gauge, Progress, Stream, narrow};
use rudb_plan::{ExprRef, Node, NodeRef, Plan, Slice};
use rudb_seam::{Context, SeamId, Settings};
use rudb_vector::{Chunk, Selection};
use crate::prepared::{Prepared, Scratch};
use crate::register::compaction;
use crate::schema::Schema;
#[derive(Debug)]
pub(crate) struct Filter {
predicate: Prepared,
compaction: &'static dyn Compaction,
passes: u32,
}
#[derive(Debug)]
pub(crate) struct Filtering {
scratch: Scratch,
gauge: Gauge,
}
impl Filter {
pub(crate) fn new(
plan: &Plan,
node: NodeRef,
predicate: ExprRef,
input: &Schema,
seams: &Settings,
) -> Result<Self> {
let types = input.types();
let context = Context::new(SeamId::ChunkCompaction, seams).with_types(&types);
let compaction = compaction().choose(&context)?.strategy();
Ok(Self {
predicate: Prepared::one(plan, predicate, input)?,
compaction,
passes: later_passes(plan, node),
})
}
}
impl Stream for Filter {
type Local = Filtering;
fn local(&self) -> Filtering {
Filtering { scratch: self.predicate.scratch(), gauge: Gauge::new(self.passes) }
}
fn push(&self, chunk: &mut Chunk, local: &mut Filtering) -> Result<Progress> {
let kept = self.predicate.evaluate_filter(chunk, &mut local.scratch)?;
if kept.len() != chunk.len() {
narrow(self.compaction, chunk, &kept, &mut local.gauge)?;
}
Ok(Progress::More)
}
}
fn later_passes(plan: &Plan, filter: NodeRef) -> u32 {
passes_between(plan, plan.root(), filter).unwrap_or(1)
}
fn passes_between(plan: &Plan, node: NodeRef, filter: NodeRef) -> Option<u32> {
if node == filter {
return Some(0);
}
let here = reads(plan.node(node));
for child in plan.node(node).children().into_iter().flatten() {
if let Some(below) = passes_between(plan, child, filter) {
return Some(below + here);
}
}
None
}
fn reads(node: &Node) -> u32 {
match node {
Node::Limit { .. }
| Node::Get { .. }
| Node::Dummy
| Node::Values { .. }
| Node::TableFunction { .. } => 0,
Node::Filter { .. } | Node::Project { .. } => 1,
Node::Aggregate { .. } | Node::Distinct { .. } | Node::SetOp { .. } => 2,
Node::TopN { .. } | Node::CrossProduct { .. } => 2,
Node::Join { .. } => 3,
Node::Sort { .. } => 3,
}
}
#[derive(Debug)]
pub(crate) struct Project {
exprs: Prepared,
schema: Schema,
}
impl Project {
pub(crate) fn new(
plan: &Plan,
input: &Schema,
index: u32,
exprs: Slice,
names: Slice,
) -> Result<Self> {
let exprs: Vec<ExprRef> = plan.expr_list(exprs).to_vec();
let names = plan.name_list(names);
if names.len() != exprs.len() {
return Err(rudb_common::Error::internal(format!(
"a projection of {} expressions under {} names",
exprs.len(),
names.len()
)));
}
let fields = exprs
.iter()
.zip(names)
.map(|(&expr, &name)| Field::new(plan.string(name), plan.expr_type(expr).clone()))
.collect();
let exprs = Prepared::new(plan, &exprs, input)?;
Ok(Self { exprs, schema: Schema::numbered(fields, index) })
}
pub(crate) fn schema(&self) -> &Schema {
&self.schema
}
}
impl Stream for Project {
type Local = Scratch;
fn local(&self) -> Scratch {
self.exprs.scratch()
}
fn push(&self, chunk: &mut Chunk, scratch: &mut Scratch) -> Result<Progress> {
let mut columns = Vec::with_capacity(self.exprs.len());
self.exprs.evaluate(chunk, scratch, &mut columns)?;
*chunk = Chunk::with_rows(columns, chunk.len())?;
Ok(Progress::More)
}
}
#[derive(Debug)]
pub(crate) struct Limit {
count: Option<u64>,
offset: u64,
}
#[derive(Debug, Default)]
pub(crate) struct Taken {
skipped: u64,
emitted: u64,
}
impl Limit {
pub(crate) fn new(count: Option<u64>, offset: u64) -> Self {
Self { count, offset }
}
fn room(&self, taken: &Taken) -> Option<u64> {
self.count.map(|count| count.saturating_sub(taken.emitted))
}
}
impl Stream for Limit {
type Local = Taken;
fn local(&self) -> Taken {
Taken::default()
}
fn push(&self, chunk: &mut Chunk, taken: &mut Taken) -> Result<Progress> {
let rows = chunk.len() as u64;
let skipping = (self.offset - taken.skipped).min(rows);
taken.skipped += skipping;
let available = rows - skipping;
let taking = match self.room(taken) {
Some(room) => room.min(available),
None => available,
};
taken.emitted += taking;
if skipping != 0 || taking != rows {
let mut kept = Selection::with_capacity(taking as usize);
for row in skipping..skipping + taking {
kept.push(row as usize);
}
keep(chunk, &kept)?;
}
match self.room(taken) {
Some(0) => Ok(Progress::Done),
_ => Ok(Progress::More),
}
}
}
fn keep(chunk: &mut Chunk, kept: &Selection) -> Result<()> {
let whole = std::mem::replace(chunk, Chunk::empty(&[]));
*chunk = whole.select(kept)?;
Ok(())
}
#[cfg(test)]
mod tests {
use rudb_common::{LogicalType, Value};
use rudb_vector::{Data, Vector};
use super::{Limit, Progress, Stream};
use rudb_vector::Chunk;
fn chunk(values: &[i32]) -> Chunk {
let column = Vector::flat(LogicalType::Integer, Data::Int32(values.to_vec().into()))
.expect("integers are an i32 layout");
Chunk::new(vec![column]).expect("one column is one length")
}
fn rows(chunk: &Chunk) -> Vec<Value> {
(0..chunk.len()).map(|row| chunk.value_at(row, 0)).collect()
}
#[test]
fn the_chunk_that_fills_the_count_is_the_one_that_says_done() {
let limit = Limit::new(Some(3), 0);
let mut taken = limit.local();
let mut first = chunk(&[1, 2]);
assert_eq!(limit.push(&mut first, &mut taken).expect("two rows fit"), Progress::More);
assert_eq!(rows(&first), vec![Value::Integer(1), Value::Integer(2)]);
let mut second = chunk(&[3, 4]);
assert_eq!(limit.push(&mut second, &mut taken).expect("one row fits"), Progress::Done);
assert_eq!(rows(&second), vec![Value::Integer(3)]);
}
#[test]
fn an_offset_that_falls_inside_a_chunk_is_counted_in_rows() {
let limit = Limit::new(None, 3);
let mut taken = limit.local();
let mut first = chunk(&[1, 2]);
assert_eq!(limit.push(&mut first, &mut taken).expect("all skipped"), Progress::More);
assert!(first.is_empty());
let mut second = chunk(&[3, 4, 5]);
assert_eq!(limit.push(&mut second, &mut taken).expect("one more skipped"), Progress::More);
assert_eq!(rows(&second), vec![Value::Integer(4), Value::Integer(5)]);
}
#[test]
fn a_limit_of_nothing_is_done_on_the_first_chunk() {
let limit = Limit::new(Some(0), 0);
let mut taken = limit.local();
let mut first = chunk(&[1, 2]);
assert_eq!(limit.push(&mut first, &mut taken).expect("nothing wanted"), Progress::Done);
assert!(first.is_empty());
}
#[test]
fn two_instances_of_one_limit_do_not_share_a_count() {
let limit = Limit::new(Some(3), 0);
let mut one = limit.local();
let mut two = limit.local();
let mut first = chunk(&[1, 2, 3]);
assert_eq!(limit.push(&mut first, &mut one).expect("three rows"), Progress::Done);
let mut second = chunk(&[4, 5, 6]);
assert_eq!(limit.push(&mut second, &mut two).expect("three rows"), Progress::Done);
assert_eq!(rows(&second), vec![Value::Integer(4), Value::Integer(5), Value::Integer(6)]);
}
}