use rudb_common::{Field, Result};
use rudb_pipeline::{Progress, Stream};
use rudb_plan::{ExprRef, Plan, Slice};
use rudb_vector::{Chunk, Selection};
use crate::prepared::{Prepared, Scratch};
use crate::schema::Schema;
#[derive(Debug)]
pub(crate) struct Filter {
predicate: Prepared,
}
impl Filter {
pub(crate) fn new(plan: &Plan, predicate: ExprRef, input: &Schema) -> Result<Self> {
Ok(Self { predicate: Prepared::one(plan, predicate, input)? })
}
}
impl Stream for Filter {
type Local = Scratch;
fn local(&self) -> Scratch {
self.predicate.scratch()
}
fn push(&self, chunk: &mut Chunk, scratch: &mut Scratch) -> Result<Progress> {
let kept = self.predicate.evaluate_filter(chunk, scratch)?;
if kept.len() != chunk.len() {
keep(chunk, &kept)?;
}
Ok(Progress::More)
}
}
#[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)]);
}
}