use std::sync::{Arc, OnceLock};
use rudb_common::bounds::{Bound, Op};
use rudb_common::{LogicalType, Result, SessionTimeZone};
use rudb_graph::{KeyMap, Link, Pushed, Rids};
use rudb_metrics::Reduced;
use rudb_plan::{BuildSide, ColumnBinding, Expr, ExprRef, JoinKind, Node, NodeRef, Plan};
use rudb_storage::{Blocked, Range};
use rudb_vector::{Chunk, Vector};
use crate::expr::evaluate_all_in_time_zone;
use crate::lookup::has_nulls;
use crate::schema::Schema;
use crate::table::{Across, hash};
const BUDGET: usize = 32 << 20;
const SMALL: u64 = 1 << 23;
#[derive(Debug, Default)]
pub(crate) struct Sideways<'a> {
keyed: OnceLock<Keyed<'a>>,
binding: OnceLock<ColumnBinding>,
exact: OnceLock<Exact>,
found: OnceLock<Found>,
wanted: OnceLock<()>,
}
#[derive(Debug, Default)]
pub(crate) struct Found {
range: Option<(Bound, Bound)>,
filter: Option<Blocked>,
rows: Option<Rids>,
domain: Option<Domain>,
held: Option<Domain>,
reduced: Option<Reduced>,
keys: Option<Keys>,
}
const KEYS: usize = 1 << 12;
#[derive(Debug)]
pub(crate) struct Keys {
keys: Vec<i64>,
}
impl Keys {
pub(crate) fn misses(&self, range: &Range) -> bool {
let (Some(Bound::Int(low)), Some(Bound::Int(high))) = (&range.low, &range.high) else {
return false;
};
let at = self.keys.partition_point(|&key| i128::from(key) < *low);
self.keys.get(at).is_none_or(|&key| i128::from(key) > *high)
}
}
#[derive(Debug)]
pub(crate) struct Domain {
base: i128,
range: u64,
words: Vec<u64>,
}
impl Domain {
fn holds(&self, key: i64) -> bool {
let Ok(offset) = u64::try_from(i128::from(key) - self.base) else { return false };
offset < self.range && self.words[(offset / 64) as usize] >> (offset % 64) & 1 == 1
}
pub(crate) fn keep(&self, keys: &Vector, rows: usize, block: &mut Vec<i64>) -> Vec<u32> {
let mut kept = Vec::with_capacity(rows);
if keys.signed_block(block) && block.len() >= rows {
let none_null = keys.none_null();
if let (Ok(base), true, true) =
(i64::try_from(self.base), none_null, self.range < 1 << 62)
{
kept.resize(rows, 0);
let mut at = 0;
for (row, &key) in block[..rows].iter().enumerate() {
let offset = (key.wrapping_sub(base) as u64).min(self.range);
let hit = self.words[(offset / 64) as usize] >> (offset % 64) & 1 == 1;
kept[at] = row as u32;
at += usize::from(hit);
}
kept.truncate(at);
return kept;
}
for (row, &key) in block[..rows].iter().enumerate() {
if self.holds(key) && (none_null || !keys.is_null_at(row)) {
kept.push(row as u32);
}
}
return kept;
}
for row in 0..rows {
let key = keys.signed_at(row).and_then(|key| i64::try_from(key).ok());
if key.is_some_and(|key| self.holds(key)) {
kept.push(row as u32);
}
}
kept
}
}
#[derive(Debug)]
pub(crate) struct Exact {
parents: u64,
children: Option<u64>,
keys: OnceLock<Option<KeyMap>>,
link: OnceLock<Option<Link>>,
stored: Option<Stored>,
}
#[derive(Debug)]
pub(crate) struct Stored {
pub(crate) parent: rudb_native::Reader,
pub(crate) column: usize,
pub(crate) child: Option<(rudb_native::Reader, rudb_native::graph::Edge)>,
}
impl Exact {
#[cfg(test)]
pub(crate) fn new(keys: KeyMap, link: Option<Link>) -> Self {
Self {
parents: keys.len(),
children: link.as_ref().map(Link::children),
keys: OnceLock::from(Some(keys)),
link: OnceLock::from(link),
stored: None,
}
}
pub(crate) fn stored(stored: Stored) -> Self {
Self {
parents: stored.parent.table().rows() as u64,
children: stored.child.as_ref().map(|(child, _)| child.table().rows() as u64),
keys: OnceLock::new(),
link: OnceLock::new(),
stored: Some(stored),
}
}
fn might_skip(&self, held: u64) -> bool {
let Some(children) = self.children.filter(|&children| children > 0) else { return true };
if self.parents == 0 {
return false;
}
let per_part = self.parents as f64 * rudb_graph::PART_ROWS as f64 / children as f64;
let missed = (1.0 - held as f64 / self.parents as f64).powf(per_part.max(1.0));
missed >= 0.5
}
fn keys(&self) -> Option<&KeyMap> {
self.keys
.get_or_init(|| {
let stored = self.stored.as_ref()?;
rudb_native::graph::key_map(&stored.parent, stored.column)
})
.as_ref()
}
fn link(&self) -> Option<&Link> {
self.link
.get_or_init(|| {
let stored = self.stored.as_ref()?;
let (child, edge) = stored.child.as_ref()?;
rudb_native::graph::stored_link(child, &stored.parent, edge)
})
.as_ref()
}
}
#[derive(Debug)]
pub(crate) struct Keyed<'a> {
plan: &'a Plan,
expr: ExprRef,
schema: Schema,
time_zone: SessionTimeZone,
}
impl<'a> Keyed<'a> {
pub(crate) fn new(
plan: &'a Plan,
expr: ExprRef,
schema: Schema,
time_zone: SessionTimeZone,
) -> Self {
Self { plan, expr, schema, time_zone }
}
pub(crate) fn parts(&self) -> (&'a Plan, [ExprRef; 1], &Schema, SessionTimeZone) {
(self.plan, [self.expr], &self.schema, self.time_zone)
}
}
impl<'a> Sideways<'a> {
pub(crate) fn new() -> Arc<Self> {
Arc::new(Self::default())
}
pub(crate) fn keying(&self, keyed: Keyed<'a>) {
let _ = self.keyed.set(keyed);
}
pub(crate) fn about(&self, binding: ColumnBinding) {
let _ = self.binding.set(binding);
}
pub(crate) fn keyed(&self) -> Option<&Keyed<'a>> {
self.keyed.get()
}
pub(crate) fn exactly(&self, exact: Exact) {
let _ = self.exact.set(exact);
}
pub(crate) fn exact(&self) -> Option<&Exact> {
self.exact.get()
}
pub(crate) fn binding(&self) -> Option<ColumnBinding> {
self.binding.get().copied()
}
pub(crate) fn wanted(&self) {
let _ = self.wanted.set(());
}
pub(crate) fn is_wanted(&self) -> bool {
self.wanted.get().is_some()
}
pub(crate) fn kept(&self) -> Option<&Domain> {
let found = self.found.get()?;
found.domain.as_ref().or(found.held.as_ref())
}
pub(crate) fn found(&self, found: Found) {
let _ = self.found.set(found);
}
pub(crate) fn tests(&self, index: u32) -> Vec<(usize, Op, Bound)> {
let (Some(binding), Some(Some((low, high)))) =
(self.binding.get(), self.found.get().map(|found| &found.range))
else {
return Vec::new();
};
if binding.table != index {
return Vec::new();
}
let column = binding.column as usize;
vec![(column, Op::GreaterOrEqual, low.clone()), (column, Op::LessOrEqual, high.clone())]
}
pub(crate) fn sifting(&self, index: u32) -> Option<(usize, &Blocked)> {
let binding = self.binding.get()?;
if binding.table != index {
return None;
}
Some((binding.column as usize, self.found.get()?.filter.as_ref()?))
}
pub(crate) fn rows(&self, index: u32) -> Option<&Rids> {
if self.binding.get()?.table != index {
return None;
}
self.found.get()?.rows.as_ref()
}
pub(crate) fn reduction(&self, index: u32) -> Option<Reduced> {
if self.binding.get()?.table != index {
return None;
}
self.found.get()?.reduced
}
pub(crate) fn keys(&self, index: u32) -> Option<(usize, &Keys)> {
let binding = self.binding.get()?;
if binding.table != index {
return None;
}
Some((binding.column as usize, self.found.get()?.keys.as_ref()?))
}
pub(crate) fn domain(&self, index: u32) -> Option<(usize, &Domain)> {
let binding = self.binding.get()?;
if binding.table != index {
return None;
}
Some((binding.column as usize, self.found.get()?.domain.as_ref()?))
}
}
pub(crate) fn beneath(plan: &Plan, node: NodeRef, binding: ColumnBinding) -> Option<ColumnBinding> {
let mut at = node;
let mut binding = binding;
loop {
match *plan.node(at) {
Node::Get { index, .. } | Node::TableFunction { index, .. } => {
return (binding.table == index).then_some(binding);
}
Node::Filter { input, .. } => at = input,
Node::Project { input, index, exprs, .. } => {
if binding.table == index {
let exprs = plan.expr_list(exprs);
let at = exprs.get(binding.column as usize)?;
let Expr::Column(inner) = *plan.expr(*at) else { return None };
binding = inner;
}
at = input;
}
ref node @ Node::Join { .. } => at = through(node)?,
_ => return None,
}
}
}
pub(crate) fn through(node: &Node) -> Option<NodeRef> {
let Node::Join { left, right, kind, build, .. } = *node else { return None };
match (kind, build) {
(JoinKind::Inner, BuildSide::Left) => Some(right),
(JoinKind::Inner | JoinKind::Semi, BuildSide::Right) => Some(left),
_ => None,
}
}
pub(crate) fn found_for(
keyed: &Keyed<'_>,
exact: Option<&Exact>,
chunks: &[Chunk],
wanted: bool,
) -> Result<Found> {
let (plan, exprs, schema, time_zone) = keyed.parts();
let rows: usize = chunks.iter().map(Chunk::len).sum();
let exact = exact.filter(|exact| (rows as u64) < exact.parents);
let by_key = exact.map(|exact| domain_of(keyed, exact, chunks)).transpose()?.flatten();
let trying =
exact.filter(|exact| by_key.as_ref().is_none_or(|(_, held)| exact.might_skip(*held)));
let pushing = trying.map(|exact| reduce(keyed, exact, chunks)).transpose()?.flatten();
let pushed = match pushing {
Some(Pushing::Done(pushed)) => Some(pushed),
_ => None,
};
let mut reduced = pushed.as_ref().map(|pushed| Reduced {
kept: pushed.rids.len(),
rows: pushed.rids.rows(),
stopped: pushed.stopped,
by_key: false,
});
let mut domain = None;
if let (None, Some((bitmap, held))) = (&pushed, by_key) {
let parents = exact.and_then(Exact::keys).map_or(0, KeyMap::len);
reduced = Some(Reduced { kept: held, rows: parents, stopped: false, by_key: true });
domain = (held < parents).then_some(bitmap);
}
let stopped = pushed.as_ref().is_some_and(|pushed| pushed.stopped);
let exact = pushed.filter(|pushed| !pushed.stopped).map(|pushed| pushed.rids);
let mut extremes = Extremes::default();
let settled = exact.is_some() || stopped || reduced.is_some_and(|reduced| reduced.by_key);
let mut keyed = Vec::with_capacity(chunks.len());
for chunk in chunks {
let keys = evaluate_all_in_time_zone(plan, &exprs, schema, chunk, time_zone)?;
keyed.push((keys.into_iter().next(), chunk.len()));
}
if !settled && domain.is_none() {
domain = dense(&keyed, rows);
}
let held = if wanted && domain.is_none() { dense(&keyed, rows) } else { None };
let settled = settled || domain.is_some();
let mut filter = if settled { None } else { Blocked::sized(rows, BUDGET) };
let keys = if exact.is_none() && !stopped && rows <= KEYS { sorted(&keyed) } else { None };
let mut hashes = Vec::new();
for (keys, len) in &keyed {
let (Some(keys), len) = (keys, *len) else { continue };
extremes.widen(keys);
let Some(filter) = filter.as_mut() else { continue };
hash(std::slice::from_ref(keys), len, &mut hashes, Across::TwoInputs);
let nullable = has_nulls(keys, len);
for (row, &word) in hashes.iter().enumerate() {
if nullable && keys.is_null_at(row) {
continue;
}
filter.add(word);
}
}
Ok(Found { range: extremes.into_range(), filter, rows: exact, domain, held, reduced, keys })
}
fn dense(keyed: &[(Option<Vector>, usize)], rows: usize) -> Option<Domain> {
let mut block = Vec::new();
let mut low = i64::MAX;
let mut high = i64::MIN;
for (keys, len) in keyed {
let Some(keys) = keys else { continue };
if !integer(keys.logical_type()) || !keys.signed_block(&mut block) || block.len() < *len {
return None;
}
let nullable = has_nulls(keys, *len);
for (row, &key) in block[..*len].iter().enumerate() {
if nullable && keys.is_null_at(row) {
continue;
}
low = low.min(key);
high = high.max(key);
}
}
if low > high {
return None;
}
let range = u64::try_from(i128::from(high) - i128::from(low) + 1).ok()?;
let bytes = usize::try_from(range.div_ceil(8)).ok()?;
if (range > (rows as u64).saturating_mul(64) && range > SMALL) || bytes > BUDGET {
return None;
}
let mut words = vec![0_u64; words_for(range)?];
for (keys, len) in keyed {
let Some(keys) = keys else { continue };
if !keys.signed_block(&mut block) {
return None;
}
let nullable = has_nulls(keys, *len);
for (row, &key) in block[..*len].iter().enumerate() {
if nullable && keys.is_null_at(row) {
continue;
}
let offset = key.wrapping_sub(low) as u64;
words[(offset / 64) as usize] |= 1 << (offset % 64);
}
}
Some(Domain { base: i128::from(low), range, words })
}
fn words_for(range: u64) -> Option<usize> {
usize::try_from(range / 64 + 1).ok()
}
fn sorted(keyed: &[(Option<Vector>, usize)]) -> Option<Keys> {
let mut block = Vec::new();
let mut keys = Vec::new();
for (column, len) in keyed {
let Some(column) = column else { continue };
if !integer(column.logical_type()) || !column.signed_block(&mut block) || block.len() < *len
{
return None;
}
let nullable = has_nulls(column, *len);
for (row, &key) in block[..*len].iter().enumerate() {
if !(nullable && column.is_null_at(row)) {
keys.push(key);
}
}
}
keys.sort_unstable();
keys.dedup();
Some(Keys { keys })
}
fn integer(ty: &LogicalType) -> bool {
matches!(
ty,
LogicalType::TinyInt | LogicalType::SmallInt | LogicalType::Integer | LogicalType::BigInt
)
}
fn reduce(keyed: &Keyed<'_>, exact: &Exact, chunks: &[Chunk]) -> Result<Option<Pushing>> {
let Some(map) = exact.keys() else { return Ok(None) };
let Some(link) = exact.link() else { return Ok(None) };
let (plan, exprs, schema, time_zone) = keyed.parts();
let parents = link.parents();
let mut words = vec![0_u64; usize::try_from(parents.div_ceil(64)).unwrap_or(usize::MAX)];
for chunk in chunks {
let keys = evaluate_all_in_time_zone(plan, &exprs, schema, chunk, time_zone)?;
let Some(keys) = keys.first() else { continue };
let nullable = has_nulls(keys, chunk.len());
for row in 0..chunk.len() {
if nullable && keys.is_null_at(row) {
continue;
}
let Some(key) = keys.signed_at(row) else { return Ok(None) };
let Some(rid) = map.lookup(key)? else { return Ok(None) };
let Some(word) = usize::try_from(rid / 64).ok().and_then(|at| words.get_mut(at)) else {
return Ok(None);
};
*word |= 1 << (rid % 64);
}
}
let held = Rids::from_words(parents, words)?;
if map.span().is_some() {
let (reached, parts) = held.reach(link)?;
if reached.saturating_mul(2) >= parts {
return Ok(Some(Pushing::Declined));
}
}
Ok(Some(Pushing::Done(held.forward_or_stop(link)?)))
}
enum Pushing {
Done(Pushed),
Declined,
}
fn domain_of(keyed: &Keyed<'_>, exact: &Exact, chunks: &[Chunk]) -> Result<Option<(Domain, u64)>> {
let Some((base, range)) = exact.keys().and_then(KeyMap::span) else { return Ok(None) };
let Some(len) = words_for(range) else { return Ok(None) };
let (plan, exprs, schema, time_zone) = keyed.parts();
let mut words = vec![0_u64; len];
for chunk in chunks {
let keys = evaluate_all_in_time_zone(plan, &exprs, schema, chunk, time_zone)?;
let Some(keys) = keys.first() else { continue };
let nullable = has_nulls(keys, chunk.len());
for row in 0..chunk.len() {
if nullable && keys.is_null_at(row) {
continue;
}
let Some(key) = keys.signed_at(row) else { return Ok(None) };
let Some(offset) = key.checked_sub(base).and_then(|at| u64::try_from(at).ok()) else {
return Ok(None);
};
if offset >= range {
return Ok(None);
}
words[(offset / 64) as usize] |= 1 << (offset % 64);
}
}
let held = words.iter().map(|word| u64::from(word.count_ones())).sum();
Ok(Some((Domain { base, range, words }, held)))
}
#[derive(Debug, Default, Clone)]
pub(crate) struct Extremes {
low: Option<Bound>,
high: Option<Bound>,
}
impl Extremes {
pub(crate) fn widen(&mut self, keys: &Vector) {
let range = Range::of(keys);
if let Some(low) = range.low {
self.low = Some(match self.low.take() {
Some(held) => held.smaller(low),
None => low,
});
}
if let Some(high) = range.high {
self.high = Some(match self.high.take() {
Some(held) => held.larger(high),
None => high,
});
}
}
pub(crate) fn into_range(self) -> Option<(Bound, Bound)> {
Some((self.low?, self.high?))
}
}
impl Found {
#[cfg(test)]
pub(crate) fn of(range: Option<(Bound, Bound)>, filter: Option<Blocked>) -> Self {
Self { range, filter, rows: None, domain: None, held: None, reduced: None, keys: None }
}
#[cfg(test)]
pub(crate) fn listing(range: Option<(Bound, Bound)>, mut keys: Vec<i64>) -> Self {
keys.sort_unstable();
Self { keys: Some(Keys { keys }), ..Self::of(range, None) }
}
#[cfg(test)]
pub(crate) fn exactly(range: Option<(Bound, Bound)>, rows: Rids) -> Self {
Self {
range,
filter: None,
rows: Some(rows),
domain: None,
held: None,
reduced: None,
keys: None,
}
}
}
#[cfg(test)]
mod tests {
use rudb_common::bounds::{Bound, Op};
use rudb_common::{Field, LogicalType, SessionTimeZone, Value};
use rudb_plan::{ColumnBinding, Expr, ExprRef, Plan};
use rudb_storage::Blocked;
use rudb_vector::{Chunk, Vector};
use rudb_graph::{KeyMap, Link};
use super::{
Across, Exact, Extremes, Found, Keyed, SMALL, Schema, Sideways, beneath, found_for, hash,
};
fn found(
keyed: &Keyed<'_>,
exact: Option<&Exact>,
chunks: &[Chunk],
) -> rudb_common::Result<Found> {
found_for(keyed, exact, chunks, false)
}
fn column(values: &[Option<i32>]) -> Vector {
let values: Vec<Value> =
values.iter().map(|value| value.map_or(Value::Null, Value::Integer)).collect();
Vector::from_values(LogicalType::Integer, &values).expect("a column of integers")
}
#[test]
fn the_range_of_several_chunks_covers_every_one_of_them() {
let mut extremes = Extremes::default();
extremes.widen(&column(&[Some(5), Some(9)]));
extremes.widen(&column(&[Some(2), Some(7)]));
assert_eq!(extremes.into_range(), Some((Bound::Int(2), Bound::Int(9))));
}
#[test]
fn a_column_of_nulls_widens_nothing() {
let mut extremes = Extremes::default();
extremes.widen(&column(&[Some(4)]));
extremes.widen(&column(&[None, None]));
assert_eq!(extremes.into_range(), Some((Bound::Int(4), Bound::Int(4))));
}
#[test]
fn nothing_seen_is_no_range() {
assert_eq!(Extremes::default().into_range(), None);
}
#[test]
fn a_scan_is_told_only_about_its_own_column() {
let sideways = Sideways::new();
sideways.found(Found::of(Some((Bound::Int(1), Bound::Int(4))), None));
sideways.about(ColumnBinding::new(7, 2));
assert!(sideways.tests(8).is_empty(), "another table's scan");
assert_eq!(
sideways.tests(7),
vec![(2, Op::GreaterOrEqual, Bound::Int(1)), (2, Op::LessOrEqual, Bound::Int(4)),]
);
}
#[test]
fn an_unarmed_handoff_and_an_empty_build_side_both_say_nothing() {
let unarmed = Sideways::new();
assert!(unarmed.tests(1).is_empty());
assert!(unarmed.sifting(1).is_none());
let empty = Sideways::new();
empty.about(ColumnBinding::new(1, 0));
empty.found(Found::of(None, None));
assert!(empty.tests(1).is_empty());
assert!(empty.sifting(1).is_none());
}
fn chunk(values: &[Option<i32>]) -> Chunk {
Chunk::new(vec![column(values)]).expect("one column is one length")
}
fn chunks(values: &[Option<i32>]) -> Vec<Chunk> {
values.chunks(512).map(chunk).collect()
}
fn key(plan: &mut Plan) -> (ExprRef, Schema) {
let expr = plan.add_expr(Expr::Column(ColumnBinding::new(1, 0)), LogicalType::Integer);
let schema = Schema::numbered(vec![Field::new("k", LogicalType::Integer)], 1);
(expr, schema)
}
fn through(filter: &Blocked, values: &[Option<i32>]) -> Vec<bool> {
let probe = column(values);
let mut hashes = Vec::new();
hash(std::slice::from_ref(&probe), values.len(), &mut hashes, Across::TwoInputs);
hashes.iter().map(|&word| filter.holds(word)).collect()
}
#[test]
fn a_build_side_is_read_for_both_its_range_and_its_keys() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let found = found(&keyed, None, &[chunk(&[Some(5), Some(90_000_000)]), chunk(&[Some(2)])])
.expect("a column of integers");
assert_eq!(found.range, Some((Bound::Int(2), Bound::Int(90_000_000))));
let filter = found.filter.expect("a filter over three keys");
assert_eq!(through(&filter, &[Some(5), Some(90_000_000), Some(2)]), [true, true, true]);
}
#[test]
fn no_key_that_went_in_is_ever_turned_away() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let keys: Vec<Option<i32>> = (0..4_000).map(|value| Some(value * 10_000 + 11)).collect();
let found = found(&keyed, None, &chunks(&keys)).expect("a column of integers");
let filter = found.filter.expect("a filter over four thousand keys");
assert!(through(&filter, &keys).into_iter().all(|held| held), "a key it was given");
}
#[test]
fn a_key_the_build_side_never_held_is_nearly_always_turned_away() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let keys: Vec<Option<i32>> = (0..4_000).map(|value| Some(value * 10_000 + 11)).collect();
let absent: Vec<Option<i32>> =
(0..4_000).map(|value| Some(value * 7 + 100_000_000)).collect();
let found = found(&keyed, None, &chunks(&keys)).expect("a column of integers");
let filter = found.filter.expect("a filter over four thousand keys");
let through = through(&filter, &absent).into_iter().filter(|&held| held).count();
assert!(through < absent.len() / 10, "{through} of {} got through", absent.len());
}
#[test]
fn a_null_is_not_a_key_the_filter_holds() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let found =
found(&keyed, None, &[chunk(&[Some(3), None, Some(40_000_000)])]).expect("integers");
assert_eq!(found.range, Some((Bound::Int(3), Bound::Int(40_000_000))));
assert_eq!(through(&found.filter.expect("a filter"), &[None]), [false]);
}
#[test]
fn keys_close_together_are_kept_as_a_bitmap_instead_of_a_filter() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let found = found(&keyed, None, &[chunk(&[Some(-4), None, Some(60)]), chunk(&[Some(7)])])
.expect("integers");
assert!(found.filter.is_none(), "the bitmap is exact, so no filter is built beside it");
assert_eq!(found.range, Some((Bound::Int(-4), Bound::Int(60))));
let domain = found.domain.expect("a bitmap over sixty five values");
let driving = column(&[Some(7), Some(8), None, Some(-4), Some(-5), Some(61), Some(60)]);
let kept = domain.keep(&driving, driving.len(), &mut Vec::new());
assert_eq!(kept, [0, 3, 6]);
let whole = column(&[Some(60), Some(1), Some(7), Some(i32::MIN), Some(i32::MAX)]);
assert_eq!(domain.keep(&whole, whole.len(), &mut Vec::new()), [0, 2]);
}
#[test]
fn a_bitmap_whose_range_fills_its_words_drops_every_key_outside_it() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let found = found(&keyed, None, &[chunk(&[Some(0), Some(63), Some(64), Some(127)])])
.expect("integers");
let domain = found.domain.expect("a bitmap over a hundred and twenty eight values");
assert_eq!(domain.range, 128);
let driving = column(&[
Some(127),
Some(128),
Some(129),
Some(191),
Some(192),
Some(-1),
Some(i32::MAX),
Some(64),
]);
assert_eq!(domain.keep(&driving, driving.len(), &mut Vec::new()), [0, 7]);
}
#[test]
fn keys_spread_wide_still_get_a_filter() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let wide = i32::try_from(SMALL).expect("small");
let found = found(&keyed, None, &[chunk(&[Some(0), Some(wide)])]).expect("integers");
assert!(found.domain.is_none() && found.filter.is_some());
}
#[test]
fn a_few_keys_over_a_small_range_are_a_bitmap_however_far_apart() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let edge = i32::try_from(SMALL).expect("small") - 1;
let found = found(&keyed, None, &[chunk(&[Some(0), Some(edge)])]).expect("integers");
assert!(found.filter.is_none(), "the bitmap takes the filter's place");
let domain = found.domain.expect("two keys over the cache sized range");
let probe = column(&[Some(0), Some(1), Some(edge), Some(edge + 1)]);
assert_eq!(domain.keep(&probe, 4, &mut Vec::new()), [0, 2]);
}
#[test]
fn an_empty_build_side_turns_every_driving_row_away() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let found = found(&keyed, None, &[]).expect("nothing to read");
assert_eq!(found.range, None);
assert_eq!(through(&found.filter.expect("a filter of no keys"), &[Some(1)]), [false]);
}
#[test]
fn an_exact_side_keeps_the_children_of_the_parents_it_holds_and_no_others() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let parent_keys: Vec<Option<i128>> = (0..50).map(|rid| Some(100 + rid)).collect();
let parents_of: Vec<u64> = (0..50_000).map(|child| child / 1_000).collect();
let exact = Exact::new(
KeyMap::build(&parent_keys).expect("unique keys"),
Some(Link::build(&parents_of, 50).expect("every parent exists")),
);
let found = found(&keyed, Some(&exact), &[chunk(&[Some(103), None]), chunk(&[Some(140)])])
.expect("integers");
assert!(found.filter.is_none(), "the exact rows make the filter redundant");
let rows = found.rows.expect("an exact side");
let kept: Vec<u64> = rows.iter().collect();
let expected: Vec<u64> = (3_000..4_000).chain(40_000..41_000).collect();
assert_eq!(kept, expected);
assert_eq!(found.range, Some((Bound::Int(103), Bound::Int(140))));
}
#[test]
fn an_exact_side_asked_for_its_keys_holds_them_as_a_bitmap_as_well() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let parent_keys: Vec<Option<i128>> = (0..50).map(|rid| Some(100 + rid)).collect();
let parents_of: Vec<u64> = (0..50_000).map(|child| child / 1_000).collect();
let exact = Exact::new(
KeyMap::build(&parent_keys).expect("unique keys"),
Some(Link::build(&parents_of, 50).expect("every parent exists")),
);
let side = [chunk(&[Some(103), None]), chunk(&[Some(140)])];
let unasked = found(&keyed, Some(&exact), &side).expect("integers");
assert!(unasked.rows.is_some() && unasked.domain.is_none() && unasked.held.is_none());
let asked = found_for(&keyed, Some(&exact), &side, true).expect("integers");
assert!(asked.rows.is_some(), "the scan is still answered by the exact rows");
let held = asked.held.expect("a bitmap for the join above");
let kept: Vec<i64> = (90..160).filter(|&key| held.holds(key)).collect();
assert_eq!(kept, [103, 140]);
}
#[test]
fn a_side_whose_keys_are_a_dictionary_still_makes_its_bitmap() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let run = column(&[Some(10), Some(11), Some(12), Some(13), Some(14)]);
let codes = Vector::dictionary(vec![4, 1, 4], run).expect("codes inside the run");
let side = [Chunk::new(vec![codes]).expect("one column")];
let found = found(&keyed, None, &side).expect("integers");
let domain = found.domain.expect("a bitmap over the keys the codes name");
let kept: Vec<i64> = (0..20).filter(|&key| domain.holds(key)).collect();
assert_eq!(kept, [11, 14]);
}
#[test]
fn a_key_the_parent_does_not_hold_falls_back_to_the_filter() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let exact = Exact::new(
KeyMap::build(&[Some(1), Some(2), Some(3), Some(4)]).expect("unique keys"),
Some(Link::build(&[0, 1, 1, 2, 3], 4).expect("every parent exists")),
);
let found =
found(&keyed, Some(&exact), &[chunk(&[Some(1), Some(9_000_000)])]).expect("integers");
assert!(found.rows.is_none());
assert!(found.filter.is_some(), "the filter is what the join gets instead");
}
#[test]
fn a_side_with_no_link_keeps_the_keys_it_holds_as_a_bitmap() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let parent_keys: Vec<Option<i128>> = (0..200).map(|rid| Some(100 + rid)).collect();
let exact = Exact::new(KeyMap::build(&parent_keys).expect("unique keys"), None);
let found = found(&keyed, Some(&exact), &[chunk(&[Some(103), None]), chunk(&[Some(299)])])
.expect("integers");
assert!(found.filter.is_none() && found.rows.is_none(), "the bitmap is the whole answer");
let reduced = found.reduced.expect("a reduction to report");
assert_eq!((reduced.kept, reduced.rows, reduced.by_key), (2, 200, true));
let domain = found.domain.expect("a bitmap");
let driving = chunk(&[Some(103), Some(104), None, Some(299), Some(300), Some(99)]);
let mut block = Vec::new();
assert_eq!(domain.keep(&driving.columns()[0], driving.len(), &mut block), [0, 3]);
}
#[test]
fn a_side_as_long_as_the_parent_does_not_read_the_key_map() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let exact = Exact::new(KeyMap::build(&[Some(1), Some(2)]).expect("unique keys"), None);
let side = [chunk(&[Some(2), Some(1)])];
let armed = found(&keyed, Some(&exact), &side).expect("integers");
assert!(armed.reduced.is_none() && armed.rows.is_none(), "the key map is not asked");
let alone = found(&keyed, None, &side).expect("integers");
assert_eq!(armed.domain.is_some(), alone.domain.is_some());
assert!(exact.keys.get().is_some(), "handed over already read, so nothing was loaded");
}
#[test]
fn a_push_is_made_only_where_it_could_skip_parts() {
let mut plan = Plan::new();
let (expr, schema) = key(&mut plan);
let keyed = Keyed::new(&plan, expr, schema, SessionTimeZone::default());
let parent_keys: Vec<Option<i128>> = (0..1_000).map(|rid| Some(100 + rid)).collect();
let parents_of: Vec<u64> = (0..100_000).map(|child| child / 100).collect();
let exact = Exact::new(
KeyMap::build(&parent_keys).expect("unique keys"),
Some(Link::build(&parents_of, 1_000).expect("every parent exists")),
);
let spread: Vec<Option<i32>> = (0..1_000).step_by(5).map(|rid| Some(100 + rid)).collect();
let wide = found(&keyed, Some(&exact), &[chunk(&spread)]).expect("integers");
assert!(wide.rows.is_none(), "no push");
assert!(wide.domain.is_some() && wide.filter.is_none(), "the bitmap is the answer");
let reduced = wide.reduced.expect("a reduction to report");
assert_eq!((reduced.kept, reduced.rows, reduced.by_key), (200, 1_000, true));
let near =
found(&keyed, Some(&exact), &[chunk(&[Some(600), Some(601)])]).expect("integers");
let rows = near.rows.expect("a push");
assert_eq!(rows.iter().collect::<Vec<u64>>(), (50_000..50_200).collect::<Vec<u64>>());
assert!(near.domain.is_none());
let reduced = near.reduced.expect("a reduction to report");
assert!(!reduced.by_key);
}
fn driving(text: &str) -> Plan {
Plan::parse(text).expect("the plan text round trips")
}
#[test]
fn a_projection_between_the_join_and_the_scan_renames_the_column_the_filter_is_about() {
let plan = driving(
"Project #1 [#0.1::INTEGER AS k]\n \
TableFunction read_parquet args=['f'::VARCHAR] #0 [a::INTEGER, k::INTEGER]",
);
assert_eq!(
beneath(&plan, plan.root(), ColumnBinding::new(1, 0)),
Some(ColumnBinding::new(0, 1)),
"the scan's own name for the projection's column"
);
}
#[test]
fn a_filter_between_the_two_leaves_the_binding_alone() {
let plan = driving(
"Project #1 [#0.0::INTEGER AS k]\n \
Filter (#0.0::INTEGER > 3::INTEGER)::BOOLEAN\n \
TableFunction read_parquet args=['f'::VARCHAR] #0 [k::INTEGER]",
);
assert_eq!(
beneath(&plan, plan.root(), ColumnBinding::new(1, 0)),
Some(ColumnBinding::new(0, 0))
);
}
#[test]
fn a_computed_column_is_not_a_column_the_filter_can_be_about() {
let plan = driving(
"Project #1 [(#0.0::INTEGER > 3::INTEGER)::BOOLEAN AS k]\n \
TableFunction read_parquet args=['f'::VARCHAR] #0 [k::INTEGER]",
);
assert_eq!(beneath(&plan, plan.root(), ColumnBinding::new(1, 0)), None);
}
#[test]
fn a_walk_that_does_not_reach_the_scan_it_is_about_arms_nothing() {
let plan = driving(
"Limit 5 offset 0\n \
TableFunction read_parquet args=['f'::VARCHAR] #0 [k::INTEGER]",
);
assert_eq!(
beneath(&plan, plan.root(), ColumnBinding::new(0, 0)),
None,
"a node in the way"
);
let plan = driving("TableFunction read_parquet args=['f'::VARCHAR] #0 [k::INTEGER]");
assert_eq!(
beneath(&plan, plan.root(), ColumnBinding::new(3, 0)),
None,
"another table's column"
);
}
}