use rudb_common::{Error, Field, LogicalType, Memory, Reservation, Result, Value};
use rudb_kernels::{Accumulator, is_true};
use rudb_plan::{Expr, ExprRef, Plan, Slice};
use rudb_vector::{Chunk, Vector};
use crate::expr::{evaluate, evaluate_all};
use crate::key::{Key, RowMap, RowSet};
use crate::operator::Operator;
use crate::rows;
use crate::schema::Schema;
#[derive(Debug, Clone)]
struct Call {
name: String,
args: Vec<ExprRef>,
distinct: bool,
filter: Option<ExprRef>,
returns: LogicalType,
}
#[derive(Debug)]
pub(crate) struct Aggregate<'a> {
input: Box<dyn Operator + 'a>,
plan: &'a Plan,
input_schema: Schema,
groups: Vec<ExprRef>,
calls: Vec<Call>,
schema: Schema,
built: bool,
chunks: Vec<Chunk>,
at: usize,
memory: Memory,
held: Reservation,
}
impl<'a> Aggregate<'a> {
pub(crate) fn new(
plan: &'a Plan,
input: Box<dyn Operator + 'a>,
index: u32,
groups: Slice,
aggregates: Slice,
memory: &Memory,
) -> Result<Self> {
let input_schema = input.schema().clone();
let groups: Vec<ExprRef> = plan.expr_list(groups).to_vec();
let mut calls = Vec::new();
for &reference in plan.expr_list(aggregates) {
let Expr::Aggregate { name, args, distinct, filter } = *plan.expr(reference) else {
return Err(Error::internal(format!(
"expression {reference} is in the aggregate list of an Aggregate and is not an aggregate"
)));
};
calls.push(Call {
name: plan.string(name).to_string(),
args: plan.expr_list(args).to_vec(),
distinct,
filter,
returns: plan.expr_type(reference).clone(),
});
}
let mut fields = Vec::with_capacity(groups.len() + calls.len());
for (at, &group) in groups.iter().enumerate() {
fields.push(Field::new(
group_name(plan, group, &input_schema, at),
plan.expr_type(group).clone(),
));
}
for call in &calls {
fields.push(Field::new(call.name.clone(), call.returns.clone()));
}
let schema = Schema::numbered(fields, index);
Ok(Self {
input,
plan,
input_schema,
groups,
calls,
schema,
built: false,
chunks: Vec::new(),
at: 0,
memory: memory.clone(),
held: memory.reservation(),
})
}
fn build(&mut self) -> Result<()> {
let mut scratch = self.memory.reservation();
let mut charged = 0;
let mut slots: RowMap<usize> = RowMap::default();
let mut states: Vec<Accumulator> = Vec::new();
let mut seen: Vec<RowSet> = Vec::new();
let calls = self.calls.len();
let sets = self.calls.iter().any(|call| call.distinct);
let alone = self.groups.is_empty();
let mut groups = 0;
if alone {
groups = 1;
self.fresh(&mut states)?;
if sets {
seen.resize_with(calls, RowSet::default);
}
}
let by_vector: Vec<bool> = self
.calls
.iter()
.map(|call| alone && !call.distinct && call.filter.is_none())
.collect();
let every = by_vector.iter().all(|&yes| yes);
let mut key = Key(Vec::new());
let mut given: Vec<Key> = vec![Key(Vec::new()); calls];
while let Some(chunk) = self.input.next()? {
let keys = evaluate_all(self.plan, &self.groups, &self.input_schema, &chunk)?;
let mut taken = 0;
let mut arguments = Vec::with_capacity(self.calls.len());
let mut filters = Vec::with_capacity(self.calls.len());
for call in &self.calls {
arguments.push(evaluate_all(self.plan, &call.args, &self.input_schema, &chunk)?);
filters.push(match call.filter {
Some(filter) => Some(evaluate(self.plan, filter, &self.input_schema, &chunk)?),
None => None,
});
}
for at in 0..calls {
if by_vector[at] {
states[at].update_run(&arguments[at], chunk.len())?;
}
}
if alone && every {
continue;
}
for row in 0..chunk.len() {
let slot = if alone {
0
} else {
fill(&mut key, &keys, row);
match slots.get(&key) {
Some(&slot) => slot,
None => {
let slot = groups;
groups += 1;
taken += rows::heap(&key.0);
slots.insert(key.clone(), slot);
self.fresh(&mut states)?;
if sets {
seen.resize_with(seen.len() + calls, RowSet::default);
}
slot
}
}
};
for (at, call) in self.calls.iter().enumerate() {
if by_vector[at] {
continue;
}
if let Some(flags) = &filters[at] {
if !is_true(&flags.value_at(row)) {
continue;
}
}
let args = &mut given[at];
fill(args, &arguments[at], row);
if call.distinct {
let set = &mut seen[slot * calls + at];
if set.contains(args) {
continue;
}
taken += rows::footprint(&args.0);
set.insert(args.clone());
}
states[slot * calls + at].update(&args.0)?;
}
}
scratch.grow(taken)?;
let now = tables(&slots, &states, &seen);
rows::capacity(now, &mut charged, &mut scratch)?;
}
let mut out: Vec<Vec<Value>> = vec![Vec::new(); groups];
for (key, slot) in slots {
out[slot] = key.0;
}
for (slot, row) in out.iter_mut().enumerate() {
for accumulator in &states[slot * calls..slot * calls + calls] {
row.push(accumulator.finish()?);
}
}
self.chunks = rows::chunks(&self.schema.types(), &out, &mut self.held)?;
Ok(())
}
fn fresh(&self, states: &mut Vec<Accumulator>) -> Result<()> {
for call in &self.calls {
states.push(Accumulator::new(&call.name, &call.returns)?);
}
Ok(())
}
}
fn fill(key: &mut Key, columns: &[Vector], row: usize) {
key.0.truncate(columns.len());
for (at, column) in columns.iter().enumerate() {
match key.0.get_mut(at) {
Some(slot) => set(slot, column, row),
None => key.0.push(column.value_at(row)),
}
}
}
fn set(slot: &mut Value, column: &Vector, row: usize) {
if let (Value::Varchar(buffer), Some(text)) = (&mut *slot, column.text_at(row)) {
buffer.clear();
buffer.push_str(text);
return;
}
*slot = column.value_at(row);
}
impl Operator for Aggregate<'_> {
fn schema(&self) -> &Schema {
&self.schema
}
fn next(&mut self) -> Result<Option<Chunk>> {
if !self.built {
self.build()?;
self.built = true;
}
if self.at >= self.chunks.len() {
return Ok(None);
}
let chunk = self.chunks[self.at].clone();
self.at += 1;
Ok(Some(chunk))
}
}
fn tables(slots: &RowMap<usize>, states: &Vec<Accumulator>, seen: &Vec<RowSet>) -> u64 {
let width = |count: usize, size: usize| {
u64::try_from(count).unwrap_or(u64::MAX).saturating_mul(width_of(size))
};
rows::buckets(slots.capacity()) * (width_of(size_of::<(Key, usize)>()) + 1)
+ width(states.capacity(), size_of::<Accumulator>())
+ width(seen.capacity(), size_of::<RowSet>())
}
fn width_of(size: usize) -> u64 {
u64::try_from(size).unwrap_or(u64::MAX)
}
fn group_name(plan: &Plan, group: ExprRef, input: &Schema, at: usize) -> String {
if let Expr::Column(binding) = *plan.expr(group) {
if let Some(position) = input.position_of(binding) {
return input.fields()[position].name.clone();
}
}
format!("group{at}")
}
#[derive(Debug)]
pub(crate) struct Distinct<'a> {
input: Box<dyn Operator + 'a>,
plan: &'a Plan,
on: Vec<ExprRef>,
schema: Schema,
built: bool,
chunks: Vec<Chunk>,
at: usize,
memory: Memory,
held: Reservation,
}
impl<'a> Distinct<'a> {
pub(crate) fn new(
plan: &'a Plan,
input: Box<dyn Operator + 'a>,
on: Slice,
memory: &Memory,
) -> Self {
let schema = input.schema().clone();
Self {
input,
plan,
on: plan.expr_list(on).to_vec(),
schema,
built: false,
chunks: Vec::new(),
at: 0,
memory: memory.clone(),
held: memory.reservation(),
}
}
fn build(&mut self) -> Result<()> {
let mut scratch = self.memory.reservation();
let mut charged = 0;
let mut seen: RowSet = RowSet::default();
let mut kept: Vec<Vec<Value>> = Vec::new();
let mut key = Key(Vec::new());
while let Some(chunk) = self.input.next()? {
let keys = if self.on.is_empty() {
Vec::new()
} else {
evaluate_all(self.plan, &self.on, &self.schema, &chunk)?
};
let mut taken = 0;
for row in 0..chunk.len() {
if self.on.is_empty() {
key.0.clear();
key.0.extend(chunk.row(row));
} else {
fill(&mut key, &keys, row);
}
if seen.contains(&key) {
continue;
}
let values: Vec<Value> =
if self.on.is_empty() { key.0.clone() } else { chunk.row(row).collect() };
taken += rows::heap(&key.0) + rows::heap(&values);
seen.insert(key.clone());
kept.push(values);
}
scratch.grow(taken)?;
let now = rows::buckets(seen.capacity()) * (width_of(size_of::<Key>()) + 1)
+ width_of(kept.capacity() * size_of::<Vec<Value>>());
rows::capacity(now, &mut charged, &mut scratch)?;
}
self.chunks = rows::chunks(&self.schema.types(), &kept, &mut self.held)?;
Ok(())
}
}
impl Operator for Distinct<'_> {
fn schema(&self) -> &Schema {
&self.schema
}
fn next(&mut self) -> Result<Option<Chunk>> {
if !self.built {
self.build()?;
self.built = true;
}
if self.at >= self.chunks.len() {
return Ok(None);
}
let chunk = self.chunks[self.at].clone();
self.at += 1;
Ok(Some(chunk))
}
}