use rudb_common::Result;
use rudb_common::rules::Rule;
use rudb_plan::{Expr, Node, Plan};
use crate::estimate::{self, DISTINCT, Facts};
use crate::extremes;
use crate::pass::{Context, Pass, top_down};
const WIDEST: u64 = 1 << 20;
const SPARSEST: u64 = 8;
#[derive(Debug)]
pub struct AggregateDense;
impl Pass for AggregateDense {
fn name(&self) -> &'static str {
"aggregate_dense"
}
fn run(&self, plan: &mut Plan, context: &Context) -> Result<()> {
if context.allows(Rule::DirectAddressing) {
densify(plan, context.facts());
}
Ok(())
}
}
fn densify(plan: &mut Plan, stats: &Facts) {
let mut found = Vec::new();
for node in top_down(plan) {
let Node::Aggregate { index, groups, .. } = *plan.node(node) else {
continue;
};
let &[key] = plan.expr_list(groups) else { continue };
let &Expr::Column(binding) = plan.expr(key) else { continue };
let Some(values) = range(plan, binding, stats) else { continue };
found.push((index, values));
}
for (index, (low, values)) in found {
plan.densify(index, low, values);
}
}
fn range(plan: &Plan, binding: rudb_plan::ColumnBinding, stats: &Facts) -> Option<(i128, u64)> {
let (low, high) = extremes::span(plan, binding, 16)?;
let values = u64::try_from(high.checked_sub(low)?.checked_add(1)?).ok()?;
if values > WIDEST {
return None;
}
if let Some(&distinct) = estimate::stated(plan, binding, stats).read(DISTINCT) {
if distinct > 0 && values > distinct.saturating_mul(SPARSEST) {
return None;
}
}
Some((low, values))
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use rudb_common::Stat;
use rudb_common::bounds::{Bound, End, Spread, Test, Zones};
use rudb_common::stat::Provenance;
use rudb_plan::Plan;
use super::{AggregateDense, Rule, WIDEST};
use crate::estimate::Facts;
use crate::pass::{Context, Pass};
const SCAN: &str = "Get memory.main.t AS t #0 [a::INTEGER]";
#[derive(Debug)]
struct Stub {
low: Stat<Bound>,
high: Stat<Bound>,
}
impl Stub {
fn exact(low: i128, high: i128) -> Arc<Self> {
Arc::new(Self {
low: Stat::exact(Bound::Int(low), Provenance::ZoneMap),
high: Stat::exact(Bound::Int(high), Provenance::ZoneMap),
})
}
fn silent() -> Arc<Self> {
Arc::new(Self { low: Stat::Unknown, high: Stat::Unknown })
}
}
impl Zones for Stub {
fn column(&self, name: &str) -> Option<usize> {
(name == "a").then_some(0)
}
fn surviving(&self, _tests: &[Test]) -> Option<u64> {
None
}
fn spread(&self, _tests: &[Test]) -> Option<Spread> {
None
}
fn extreme(&self, _column: usize, end: End) -> Stat<Bound> {
match end {
End::Low => self.low.clone(),
End::High => self.high.clone(),
}
}
fn nulls(&self, _column: usize) -> Stat<u64> {
Stat::Unknown
}
}
fn grouped(zones: Arc<Stub>) -> Plan {
let text = format!("Aggregate #1 groups=[#0.0::INTEGER] aggregates=[]\n {SCAN}\n");
let mut plan = Plan::parse(&text).expect("a plan that parses");
plan.set_zones(0, zones as Arc<dyn Zones>);
plan
}
fn counted(distinct: Option<u64>) -> Context {
let mut facts = Facts::new();
facts.record("memory", "main", "t", 1_000_000);
if let Some(distinct) = distinct {
facts.record_distinct("memory", "main", "t", "a", distinct, Provenance::Dictionary);
}
let mut context = Context::new();
context.measure(Arc::new(facts));
context
}
fn run(plan: &mut Plan, context: &Context) {
AggregateDense.run(plan, context).expect("a pass that cannot fail");
}
fn without(rule: Rule) -> Context {
let mut context = counted(Some(100));
let mut rules = rudb_common::rules::Rules::default();
rules.set(rule, false);
context.govern(rules);
context
}
#[test]
fn the_rule_s_own_setting_turns_it_off() {
let mut plan = grouped(Stub::exact(100, 199));
run(&mut plan, &without(Rule::DirectAddressing));
assert_eq!(plan.dense_count(), 0, "stats_direct_addressing = off left the hash table");
}
#[test]
fn the_master_setting_turns_it_off_too() {
let mut plan = grouped(Stub::exact(100, 199));
run(&mut plan, &without(Rule::StatsAll));
assert_eq!(plan.dense_count(), 0, "statistics = off left the hash table");
}
#[test]
fn a_bounded_key_column_becomes_a_range() {
let mut plan = grouped(Stub::exact(100, 199));
run(&mut plan, &counted(Some(100)));
assert_eq!(plan.dense(1), Some((100, 100)));
}
#[test]
fn a_negative_low_end_is_kept_as_it_is() {
let mut plan = grouped(Stub::exact(-40, 59));
run(&mut plan, &counted(Some(100)));
assert_eq!(plan.dense(1), Some((-40, 100)));
}
#[test]
fn one_value_is_one_cell() {
let mut plan = grouped(Stub::exact(7, 7));
run(&mut plan, &counted(Some(1)));
assert_eq!(plan.dense(1), Some((7, 1)));
}
#[test]
fn a_column_nobody_bounded_is_left_alone() {
let mut plan = grouped(Stub::silent());
run(&mut plan, &counted(Some(100)));
assert_eq!(plan.dense_count(), 0);
}
#[test]
fn a_range_past_the_widest_is_left_alone() {
let wide = i128::from(WIDEST) + 1;
let mut plan = grouped(Stub::exact(0, wide));
run(&mut plan, &counted(None));
assert_eq!(plan.dense_count(), 0);
}
#[test]
fn a_range_that_is_mostly_holes_is_left_alone() {
let mut plan = grouped(Stub::exact(0, 999));
run(&mut plan, &counted(Some(10)));
assert_eq!(plan.dense_count(), 0);
}
#[test]
fn an_uncounted_column_is_decided_by_the_size_alone() {
let mut plan = grouped(Stub::exact(0, 999));
run(&mut plan, &counted(None));
assert_eq!(plan.dense(1), Some((0, 1000)));
}
#[test]
fn an_ungrouped_aggregate_has_no_key_to_address() {
let text = format!("Aggregate #1 groups=[] aggregates=[count_star()::BIGINT]\n {SCAN}\n");
let mut plan = Plan::parse(&text).expect("a plan that parses");
plan.set_zones(0, Stub::exact(0, 99) as Arc<dyn Zones>);
run(&mut plan, &counted(None));
assert_eq!(plan.dense_count(), 0);
}
#[test]
fn a_second_run_writes_what_the_first_one_wrote() {
let mut plan = grouped(Stub::exact(100, 199));
let context = counted(Some(100));
run(&mut plan, &context);
let once = plan.dense(1);
run(&mut plan, &context);
assert_eq!(plan.dense(1), once);
assert_eq!(plan.dense_count(), 1);
}
#[test]
fn the_pass_is_off_when_it_is_named() {
let context = Context::without("aggregate_dense").expect("a name that is a pass");
assert!(context.is_disabled("aggregate_dense"));
}
}