blob: 166a4eec8d44fc237e854b79769bea2552991f76 [file]
// Copyright 2022 The Blaze Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub mod acc;
pub mod agg_context;
pub mod agg_table;
pub mod avg;
pub mod brickhouse;
pub mod collect_list;
pub mod collect_set;
pub mod count;
pub mod first;
pub mod first_ignores_null;
pub mod maxmin;
pub mod sum;
use std::{
any::Any,
fmt::Debug,
sync::{
atomic::{AtomicUsize, Ordering::SeqCst},
Arc,
},
};
use arrow::{array::*, datatypes::*};
use datafusion::{
common::{Result, ScalarValue},
logical_expr::aggregate_function,
physical_expr::PhysicalExpr,
};
use datafusion_ext_commons::df_execution_err;
use datafusion_ext_exprs::cast::TryCastExpr;
use slimmer_box::SlimmerBox;
use crate::agg::acc::{
AccumInitialValue, AccumStateRow, AccumStateValAddr, AggDynBinary, AggDynScalar, AggDynStr,
RefAccumStateRow,
};
pub const AGG_BUF_COLUMN_NAME: &str = "#9223372036854775807";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AggExecMode {
HashAgg,
SortAgg,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AggMode {
Partial,
PartialMerge,
Final,
}
impl AggMode {
pub fn is_partial(&self) -> bool {
matches!(self, AggMode::Partial)
}
pub fn is_partial_merge(&self) -> bool {
matches!(self, AggMode::PartialMerge)
}
pub fn is_final(&self) -> bool {
matches!(self, AggMode::Final)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AggFunction {
Count,
Sum,
Avg,
Max,
Min,
First,
FirstIgnoresNull,
CollectList,
CollectSet,
BrickhouseCollect,
BrickhouseCombineUnique,
}
#[derive(Debug, Clone)]
pub struct GroupingExpr {
pub field_name: String,
pub expr: Arc<dyn PhysicalExpr>,
}
#[derive(Debug, Clone)]
pub struct AggExpr {
pub field_name: String,
pub mode: AggMode,
pub agg: Arc<dyn Agg>,
}
pub trait WithAggBufAddrs {
fn set_accum_state_val_addrs(&mut self, accum_state_val_addrs: &[AccumStateValAddr]);
}
pub trait WithMemTracking {
fn mem_used_tracker(&self) -> &AtomicUsize;
fn mem_used(&self) -> usize {
self.mem_used_tracker().load(SeqCst)
}
fn add_mem_used(&self, mem_used: usize) {
self.mem_used_tracker().fetch_add(mem_used, SeqCst);
}
fn sub_mem_used(&self, mem_used: usize) {
let _ = self
.mem_used_tracker()
.fetch_update(SeqCst, SeqCst, |v| Some(v.saturating_sub(mem_used)));
}
fn reset_mem_used(&self) {
self.mem_used_tracker().store(0, SeqCst);
}
}
pub trait Agg: WithAggBufAddrs + WithMemTracking + Send + Sync + Debug {
fn as_any(&self) -> &dyn Any;
fn exprs(&self) -> Vec<Arc<dyn PhysicalExpr>>;
fn data_type(&self) -> &DataType;
fn nullable(&self) -> bool;
fn accums_initial(&self) -> &[AccumInitialValue];
fn with_new_exprs(&self, exprs: Vec<Arc<dyn PhysicalExpr>>) -> Result<Arc<dyn Agg>>;
fn prepare_partial_args(&self, partial_inputs: &[ArrayRef]) -> Result<Vec<ArrayRef>> {
// default implementation: directly return the inputs
Ok(partial_inputs.iter().map(Clone::clone).collect())
}
fn increase_acc_mem_used(&self, acc: &mut RefAccumStateRow);
fn partial_update(
&self,
acc: &mut RefAccumStateRow,
values: &[ArrayRef],
row_idx: usize,
) -> Result<()>;
fn partial_batch_update(
&self,
accs: &mut [RefAccumStateRow],
values: &[ArrayRef],
) -> Result<()> {
for row_idx in 0..accs.len() {
let acc = &mut accs[row_idx];
self.partial_update(acc, values, row_idx)?;
}
Ok(())
}
fn partial_update_all(&self, acc: &mut RefAccumStateRow, values: &[ArrayRef]) -> Result<()>;
fn partial_merge(
&self,
acc: &mut RefAccumStateRow,
merging_acc: &mut RefAccumStateRow,
) -> Result<()>;
fn partial_batch_merge(
&self,
accs: &mut [RefAccumStateRow],
merging_accs: &mut [RefAccumStateRow],
) -> Result<()> {
for row_idx in 0..accs.len() {
let acc = &mut accs[row_idx];
let merging_acc = &mut merging_accs[row_idx];
self.partial_merge(acc, merging_acc)?;
}
Ok(())
}
fn final_merge(&self, acc: &mut RefAccumStateRow) -> Result<ScalarValue>;
fn final_batch_merge(&self, accs: &mut [RefAccumStateRow]) -> Result<ArrayRef>;
}
pub fn create_agg(
agg_function: AggFunction,
children: &[Arc<dyn PhysicalExpr>],
input_schema: &SchemaRef,
) -> Result<Arc<dyn Agg>> {
Ok(match agg_function {
AggFunction::Count => {
let return_type = DataType::Int64;
Arc::new(count::AggCount::try_new(children[0].clone(), return_type)?)
}
AggFunction::Sum => {
let arg_type = children[0].data_type(input_schema)?;
let return_type = aggregate_function::AggregateFunction::return_type(
&aggregate_function::AggregateFunction::Sum,
&[arg_type],
)?;
Arc::new(sum::AggSum::try_new(
Arc::new(TryCastExpr::new(children[0].clone(), return_type.clone())),
return_type,
)?)
}
AggFunction::Avg => {
let arg_type = children[0].data_type(input_schema)?;
let return_type = aggregate_function::AggregateFunction::return_type(
&aggregate_function::AggregateFunction::Avg,
&[arg_type],
)?;
Arc::new(avg::AggAvg::try_new(
Arc::new(TryCastExpr::new(children[0].clone(), return_type.clone())),
return_type,
)?)
}
AggFunction::Max => {
let dt = children[0].data_type(input_schema)?;
Arc::new(maxmin::AggMax::try_new(children[0].clone(), dt)?)
}
AggFunction::Min => {
let dt = children[0].data_type(input_schema)?;
Arc::new(maxmin::AggMin::try_new(children[0].clone(), dt)?)
}
AggFunction::First => {
let dt = children[0].data_type(input_schema)?;
Arc::new(first::AggFirst::try_new(children[0].clone(), dt)?)
}
AggFunction::FirstIgnoresNull => {
let dt = children[0].data_type(input_schema)?;
Arc::new(first_ignores_null::AggFirstIgnoresNull::try_new(
children[0].clone(),
dt,
)?)
}
AggFunction::CollectList => {
let arg_type = children[0].data_type(input_schema)?;
let return_type = DataType::new_list(arg_type.clone(), true);
Arc::new(collect_list::AggCollectList::try_new(
children[0].clone(),
return_type,
arg_type,
)?)
}
AggFunction::CollectSet => {
let arg_type = children[0].data_type(input_schema)?;
let return_type = DataType::new_list(arg_type.clone(), true);
Arc::new(collect_set::AggCollectSet::try_new(
children[0].clone(),
return_type,
arg_type,
)?)
}
AggFunction::BrickhouseCollect => {
let arg_type = children[0].data_type(input_schema)?;
let arg_list_inner_type = match arg_type {
DataType::List(field) => field.data_type().clone(),
_ => return df_execution_err!("brickhouse.collect expect list type"),
};
Arc::new(brickhouse::collect::AggCollect::try_new(
children[0].clone(),
arg_list_inner_type,
)?)
}
AggFunction::BrickhouseCombineUnique => {
let arg_type = children[0].data_type(input_schema)?;
let arg_list_inner_type = match arg_type {
DataType::List(field) => field.data_type().clone(),
_ => return df_execution_err!("brickhouse.combine_unique expect list type"),
};
Arc::new(brickhouse::collect::AggCollect::try_new(
children[0].clone(),
arg_list_inner_type,
)?)
}
})
}
fn default_final_merge_with_addr(
agg: &impl Agg,
acc: &mut RefAccumStateRow,
addr: AccumStateValAddr,
) -> Result<ScalarValue> {
// default implementation:
// extract the only one values from acc and convert to ScalarValue
// this works for sum/min/max/first
macro_rules! handle_fixed {
($ty:ident) => {{
if acc.is_fixed_valid(addr) {
ScalarValue::$ty(Some(acc.fixed_value(addr)))
} else {
ScalarValue::$ty(None)
}
}};
}
macro_rules! handle_timestamp {
($ty:ident, $tz:expr) => {{
let v = if acc.is_fixed_valid(addr) {
Some(acc.fixed_value(addr))
} else {
None
};
ScalarValue::$ty(v, $tz.clone())
}};
}
Ok(match agg.data_type() {
DataType::Null => ScalarValue::Null,
DataType::Boolean => handle_fixed!(Boolean),
DataType::Float32 => handle_fixed!(Float32),
DataType::Float64 => handle_fixed!(Float64),
DataType::Int8 => handle_fixed!(Int8),
DataType::Int16 => handle_fixed!(Int16),
DataType::Int32 => handle_fixed!(Int32),
DataType::Int64 => handle_fixed!(Int64),
DataType::UInt8 => handle_fixed!(UInt8),
DataType::UInt16 => handle_fixed!(UInt16),
DataType::UInt32 => handle_fixed!(UInt32),
DataType::UInt64 => handle_fixed!(UInt64),
DataType::Decimal128(prec, scale) => {
let v = if acc.is_fixed_valid(addr) {
Some(acc.fixed_value(addr))
} else {
None
};
ScalarValue::Decimal128(v, *prec, *scale)
}
DataType::Date32 => handle_fixed!(Date32),
DataType::Date64 => handle_fixed!(Date64),
DataType::Timestamp(TimeUnit::Second, tz) => handle_timestamp!(TimestampSecond, tz),
DataType::Timestamp(TimeUnit::Millisecond, tz) => {
handle_timestamp!(TimestampMillisecond, tz)
}
DataType::Timestamp(TimeUnit::Microsecond, tz) => {
handle_timestamp!(TimestampMicrosecond, tz)
}
DataType::Timestamp(TimeUnit::Nanosecond, tz) => {
handle_timestamp!(TimestampNanosecond, tz)
}
DataType::Utf8 => ScalarValue::Utf8(match std::mem::take(acc.dyn_value_mut(addr)) {
Some(v) => Some({
agg.sub_mem_used(v.mem_size());
let boxed = SlimmerBox::into_box(
v.as_any_boxed()
.downcast::<AggDynStr>()
.or_else(|_| df_execution_err!("error downcasting to AggDynStr"))?
.into_value(),
);
boxed.into_string()
}),
None => None,
}),
DataType::Binary => ScalarValue::Binary(match std::mem::take(acc.dyn_value_mut(addr)) {
Some(v) => Some({
agg.sub_mem_used(v.mem_size());
v.as_any_boxed()
.downcast::<AggDynBinary>()
.or_else(|_| df_execution_err!("error downcasting to AggDynStr"))?
.into_value()
.into_vec()
}),
None => None,
}),
other => match std::mem::take(acc.dyn_value_mut(addr)) {
Some(v) => {
agg.sub_mem_used(v.mem_size());
v.as_any_boxed()
.downcast::<AggDynScalar>()
.or_else(|_| df_execution_err!("error downcasting to AggDynScalar"))?
.into_value()
}
None => ScalarValue::try_from(other)?,
},
})
}
fn default_final_batch_merge_with_addr(
agg: &impl Agg,
accs: &mut [RefAccumStateRow],
addr: AccumStateValAddr,
) -> Result<ArrayRef> {
// default implementation:
// extract the only one values from acc and convert to ScalarValue
// this works for sum/min/max/first
macro_rules! handle_fixed {
($ty:ident) => {{
type B = paste::paste! {[< $ty Builder >]};
let mut builder = B::with_capacity(accs.len());
for acc in accs {
if acc.is_fixed_valid(addr) {
builder.append_value(acc.fixed_value(addr));
} else {
builder.append_null();
};
}
builder.finish()
}};
}
macro_rules! mkarray {
($a:expr) => {{
let array: Arc<dyn Array + 'static> = Arc::new($a);
array
}};
}
Ok(match agg.data_type() {
DataType::Null => mkarray!(NullArray::new(accs.len())),
DataType::Boolean => mkarray!(handle_fixed!(Boolean)),
DataType::Float32 => mkarray!(handle_fixed!(Float32)),
DataType::Float64 => mkarray!(handle_fixed!(Float64)),
DataType::Int8 => mkarray!(handle_fixed!(Int8)),
DataType::Int16 => mkarray!(handle_fixed!(Int16)),
DataType::Int32 => mkarray!(handle_fixed!(Int32)),
DataType::Int64 => mkarray!(handle_fixed!(Int64)),
DataType::UInt8 => mkarray!(handle_fixed!(UInt8)),
DataType::UInt16 => mkarray!(handle_fixed!(UInt16)),
DataType::UInt32 => mkarray!(handle_fixed!(UInt32)),
DataType::UInt64 => mkarray!(handle_fixed!(UInt64)),
DataType::Decimal128(prec, scale) => {
mkarray!(handle_fixed!(Decimal128).with_precision_and_scale(*prec, *scale)?)
}
DataType::Date32 => mkarray!(handle_fixed!(Date32)),
DataType::Date64 => mkarray!(handle_fixed!(Date64)),
DataType::Timestamp(TimeUnit::Second, tz) => {
mkarray!(handle_fixed!(TimestampSecond).with_timezone_opt(tz.clone()))
}
DataType::Timestamp(TimeUnit::Millisecond, tz) => {
mkarray!(handle_fixed!(TimestampMillisecond).with_timezone_opt(tz.clone()))
}
DataType::Timestamp(TimeUnit::Microsecond, tz) => {
mkarray!(handle_fixed!(TimestampMicrosecond).with_timezone_opt(tz.clone()))
}
DataType::Timestamp(TimeUnit::Nanosecond, tz) => {
mkarray!(handle_fixed!(TimestampNanosecond).with_timezone_opt(tz.clone()))
}
DataType::Utf8 => {
mkarray!(accs
.iter_mut()
.map(|acc| {
let dyn_str = std::mem::take(acc.dyn_value_mut(addr));
match dyn_str {
Some(s) => {
agg.sub_mem_used(s.mem_size());
let boxed = SlimmerBox::into_box(
s.as_any_boxed()
.downcast::<AggDynStr>()
.unwrap()
.into_value(),
);
Some(boxed.into_string())
}
None => None,
}
})
.collect::<StringArray>())
}
DataType::Binary => {
mkarray!(accs
.iter_mut()
.map(|acc| {
let dyn_binary = std::mem::take(acc.dyn_value_mut(addr));
match dyn_binary {
Some(s) => Some({
agg.sub_mem_used(s.mem_size());
s.as_any_boxed()
.downcast::<AggDynBinary>()
.unwrap()
.into_value()
.into_vec()
}),
None => None,
}
})
.collect::<BinaryArray>())
}
other => {
let scalars = accs
.iter_mut()
.map(|acc| {
let dyn_scalar = std::mem::take(acc.dyn_value_mut(addr));
match dyn_scalar {
Some(s) => {
agg.sub_mem_used(s.mem_size());
s.as_any_boxed()
.downcast::<AggDynScalar>()
.unwrap()
.into_value()
}
None => ScalarValue::try_from(other).unwrap(),
}
})
.collect::<Vec<_>>();
ScalarValue::iter_to_array(scalars)?
}
})
}