blob: 2a8467eb88327ec8cbfe757d6dbef945c1cbc685 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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(crate) mod groups_accumulator {
#[allow(unused_imports)]
pub(crate) mod accumulate {
pub use datafusion_functions_aggregate_common::aggregate::groups_accumulator::accumulate::NullState;
}
pub use datafusion_functions_aggregate_common::aggregate::groups_accumulator::{
accumulate::NullState, GroupsAccumulatorAdapter,
};
}
pub(crate) mod stats {
pub use datafusion_functions_aggregate_common::stats::StatsType;
}
pub mod utils {
pub use datafusion_functions_aggregate_common::utils::{
get_accum_scalar_values_as_arrays, get_sort_options, ordering_fields,
DecimalAverager, Hashable,
};
}
use std::fmt::Debug;
use std::sync::Arc;
use crate::expressions::Column;
use arrow::compute::SortOptions;
use arrow::datatypes::{DataType, FieldRef, Schema, SchemaRef};
use datafusion_common::{internal_err, not_impl_err, Result, ScalarValue};
use datafusion_expr::{AggregateUDF, ReversedUDAF, SetMonotonicity};
use datafusion_expr_common::accumulator::Accumulator;
use datafusion_expr_common::groups_accumulator::GroupsAccumulator;
use datafusion_expr_common::type_coercion::aggregates::check_arg_count;
use datafusion_functions_aggregate_common::accumulator::{
AccumulatorArgs, StateFieldsArgs,
};
use datafusion_functions_aggregate_common::order::AggregateOrderSensitivity;
use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
use datafusion_physical_expr_common::sort_expr::PhysicalSortExpr;
/// Builder for physical [`AggregateFunctionExpr`]
///
/// `AggregateFunctionExpr` contains the information necessary to call
/// an aggregate expression.
#[derive(Debug, Clone)]
pub struct AggregateExprBuilder {
fun: Arc<AggregateUDF>,
/// Physical expressions of the aggregate function
args: Vec<Arc<dyn PhysicalExpr>>,
alias: Option<String>,
/// A human readable name
human_display: String,
/// Arrow Schema for the aggregate function
schema: SchemaRef,
/// The physical order by expressions
order_bys: Vec<PhysicalSortExpr>,
/// Whether to ignore null values
ignore_nulls: bool,
/// Whether is distinct aggregate function
is_distinct: bool,
/// Whether the expression is reversed
is_reversed: bool,
}
impl AggregateExprBuilder {
pub fn new(fun: Arc<AggregateUDF>, args: Vec<Arc<dyn PhysicalExpr>>) -> Self {
Self {
fun,
args,
alias: None,
human_display: String::default(),
schema: Arc::new(Schema::empty()),
order_bys: vec![],
ignore_nulls: false,
is_distinct: false,
is_reversed: false,
}
}
/// Constructs an `AggregateFunctionExpr` from the builder
///
/// Note that an [`Self::alias`] must be provided before calling this method.
///
/// # Example: Create an [`AggregateUDF`]
///
/// In the following example, [`AggregateFunctionExpr`] will be built using [`AggregateExprBuilder`]
/// which provides a build function. Full example could be accessed from the source file.
///
/// ```
/// # use std::any::Any;
/// # use std::sync::Arc;
/// # use arrow::datatypes::{DataType, FieldRef};
/// # use datafusion_common::{Result, ScalarValue};
/// # use datafusion_expr::{col, ColumnarValue, Documentation, Signature, Volatility, Expr};
/// # use datafusion_expr::{AggregateUDFImpl, AggregateUDF, Accumulator, function::{AccumulatorArgs, StateFieldsArgs}};
/// # use arrow::datatypes::Field;
/// #
/// # #[derive(Debug, Clone, PartialEq, Eq, Hash)]
/// # struct FirstValueUdf {
/// # signature: Signature,
/// # }
/// #
/// # impl FirstValueUdf {
/// # fn new() -> Self {
/// # Self {
/// # signature: Signature::any(1, Volatility::Immutable),
/// # }
/// # }
/// # }
/// #
/// # impl AggregateUDFImpl for FirstValueUdf {
/// # fn as_any(&self) -> &dyn Any {
/// # unimplemented!()
/// # }
/// #
/// # fn name(&self) -> &str {
/// # unimplemented!()
/// # }
/// #
/// # fn signature(&self) -> &Signature {
/// # unimplemented!()
/// # }
/// #
/// # fn return_type(&self, args: &[DataType]) -> Result<DataType> {
/// # unimplemented!()
/// # }
/// #
/// # fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn Accumulator>> {
/// # unimplemented!()
/// # }
/// #
/// # fn state_fields(&self, args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
/// # unimplemented!()
/// # }
/// #
/// # fn documentation(&self) -> Option<&Documentation> {
/// # unimplemented!()
/// # }
/// # }
/// #
/// # let first_value = AggregateUDF::from(FirstValueUdf::new());
/// # let expr = first_value.call(vec![col("a")]);
/// #
/// # use datafusion_physical_expr::expressions::Column;
/// # use datafusion_physical_expr_common::physical_expr::PhysicalExpr;
/// # use datafusion_physical_expr::aggregate::AggregateExprBuilder;
/// # use datafusion_physical_expr::expressions::PhysicalSortExpr;
/// # use datafusion_physical_expr::PhysicalSortRequirement;
/// #
/// fn build_aggregate_expr() -> Result<()> {
/// let args = vec![Arc::new(Column::new("a", 0)) as Arc<dyn PhysicalExpr>];
/// let order_by = vec![PhysicalSortExpr {
/// expr: Arc::new(Column::new("x", 1)) as Arc<dyn PhysicalExpr>,
/// options: Default::default(),
/// }];
///
/// let first_value = AggregateUDF::from(FirstValueUdf::new());
///
/// let aggregate_expr = AggregateExprBuilder::new(
/// Arc::new(first_value),
/// args
/// )
/// .order_by(order_by)
/// .alias("first_a_by_x")
/// .ignore_nulls()
/// .build()?;
///
/// Ok(())
/// }
/// ```
///
/// This creates a physical expression equivalent to SQL:
/// `first_value(a ORDER BY x) IGNORE NULLS AS first_a_by_x`
pub fn build(self) -> Result<AggregateFunctionExpr> {
let Self {
fun,
args,
alias,
human_display,
schema,
order_bys,
ignore_nulls,
is_distinct,
is_reversed,
} = self;
if args.is_empty() {
return internal_err!("args should not be empty");
}
let ordering_types = order_bys
.iter()
.map(|e| e.expr.data_type(&schema))
.collect::<Result<Vec<_>>>()?;
let ordering_fields = utils::ordering_fields(&order_bys, &ordering_types);
let input_exprs_fields = args
.iter()
.map(|arg| arg.return_field(&schema))
.collect::<Result<Vec<_>>>()?;
check_arg_count(
fun.name(),
&input_exprs_fields,
&fun.signature().type_signature,
)?;
let return_field = fun.return_field(&input_exprs_fields)?;
let is_nullable = fun.is_nullable();
let name = match alias {
None => {
return internal_err!(
"AggregateExprBuilder::alias must be provided prior to calling build"
)
}
Some(alias) => alias,
};
let arg_fields = args
.iter()
.map(|e| e.return_field(schema.as_ref()))
.collect::<Result<Vec<_>>>()?;
Ok(AggregateFunctionExpr {
fun: Arc::unwrap_or_clone(fun),
args,
arg_fields,
return_field,
name,
human_display,
schema: Arc::unwrap_or_clone(schema),
order_bys,
ignore_nulls,
ordering_fields,
is_distinct,
input_fields: input_exprs_fields,
is_reversed,
is_nullable,
})
}
pub fn alias(mut self, alias: impl Into<String>) -> Self {
self.alias = Some(alias.into());
self
}
pub fn human_display(mut self, name: String) -> Self {
self.human_display = name;
self
}
pub fn schema(mut self, schema: SchemaRef) -> Self {
self.schema = schema;
self
}
pub fn order_by(mut self, order_bys: Vec<PhysicalSortExpr>) -> Self {
self.order_bys = order_bys;
self
}
pub fn reversed(mut self) -> Self {
self.is_reversed = true;
self
}
pub fn with_reversed(mut self, is_reversed: bool) -> Self {
self.is_reversed = is_reversed;
self
}
pub fn distinct(mut self) -> Self {
self.is_distinct = true;
self
}
pub fn with_distinct(mut self, is_distinct: bool) -> Self {
self.is_distinct = is_distinct;
self
}
pub fn ignore_nulls(mut self) -> Self {
self.ignore_nulls = true;
self
}
pub fn with_ignore_nulls(mut self, ignore_nulls: bool) -> Self {
self.ignore_nulls = ignore_nulls;
self
}
}
/// Physical aggregate expression of a UDAF.
///
/// Instances are constructed via [`AggregateExprBuilder`].
#[derive(Debug, Clone)]
pub struct AggregateFunctionExpr {
fun: AggregateUDF,
args: Vec<Arc<dyn PhysicalExpr>>,
/// Fields corresponding to args (same order & length)
arg_fields: Vec<FieldRef>,
/// Output / return field of this aggregate
return_field: FieldRef,
/// Output column name that this expression creates
name: String,
/// Simplified name for `tree` explain.
human_display: String,
schema: Schema,
// The physical order by expressions
order_bys: Vec<PhysicalSortExpr>,
// Whether to ignore null values
ignore_nulls: bool,
// fields used for order sensitive aggregation functions
ordering_fields: Vec<FieldRef>,
is_distinct: bool,
is_reversed: bool,
input_fields: Vec<FieldRef>,
is_nullable: bool,
}
impl AggregateFunctionExpr {
/// Return the `AggregateUDF` used by this `AggregateFunctionExpr`
pub fn fun(&self) -> &AggregateUDF {
&self.fun
}
/// expressions that are passed to the Accumulator.
/// Single-column aggregations such as `sum` return a single value, others (e.g. `cov`) return many.
pub fn expressions(&self) -> Vec<Arc<dyn PhysicalExpr>> {
self.args.clone()
}
/// Human readable name such as `"MIN(c2)"`.
pub fn name(&self) -> &str {
&self.name
}
/// Simplified name for `tree` explain.
pub fn human_display(&self) -> &str {
&self.human_display
}
/// Return if the aggregation is distinct
pub fn is_distinct(&self) -> bool {
self.is_distinct
}
/// Return if the aggregation ignores nulls
pub fn ignore_nulls(&self) -> bool {
self.ignore_nulls
}
/// Return if the aggregation is reversed
pub fn is_reversed(&self) -> bool {
self.is_reversed
}
/// Return if the aggregation is nullable
pub fn is_nullable(&self) -> bool {
self.is_nullable
}
/// the field of the final result of this aggregation.
pub fn field(&self) -> FieldRef {
self.return_field
.as_ref()
.clone()
.with_name(&self.name)
.into()
}
/// the accumulator used to accumulate values from the expressions.
/// the accumulator expects the same number of arguments as `expressions` and must
/// return states with the same description as `state_fields`
pub fn create_accumulator(&self) -> Result<Box<dyn Accumulator>> {
let acc_args = AccumulatorArgs {
return_field: Arc::clone(&self.return_field),
schema: &self.schema,
expr_fields: &self.arg_fields,
ignore_nulls: self.ignore_nulls,
order_bys: self.order_bys.as_ref(),
is_distinct: self.is_distinct,
name: &self.name,
is_reversed: self.is_reversed,
exprs: &self.args,
};
self.fun.accumulator(acc_args)
}
/// the field of the final result of this aggregation.
pub fn state_fields(&self) -> Result<Vec<FieldRef>> {
let args = StateFieldsArgs {
name: &self.name,
input_fields: &self.input_fields,
return_field: Arc::clone(&self.return_field),
ordering_fields: &self.ordering_fields,
is_distinct: self.is_distinct,
};
self.fun.state_fields(args)
}
/// Returns the ORDER BY expressions for the aggregate function.
pub fn order_bys(&self) -> &[PhysicalSortExpr] {
if self.order_sensitivity().is_insensitive() {
&[]
} else {
&self.order_bys
}
}
/// Indicates whether aggregator can produce the correct result with any
/// arbitrary input ordering. By default, we assume that aggregate expressions
/// are order insensitive.
pub fn order_sensitivity(&self) -> AggregateOrderSensitivity {
if self.order_bys.is_empty() {
AggregateOrderSensitivity::Insensitive
} else {
// If there is an ORDER BY clause, use the sensitivity of the implementation:
self.fun.order_sensitivity()
}
}
/// Sets the indicator whether ordering requirements of the aggregator is
/// satisfied by its input. If this is not the case, aggregators with order
/// sensitivity `AggregateOrderSensitivity::Beneficial` can still produce
/// the correct result with possibly more work internally.
///
/// # Returns
///
/// Returns `Ok(Some(updated_expr))` if the process completes successfully.
/// If the expression can benefit from existing input ordering, but does
/// not implement the method, returns an error. Order insensitive and hard
/// requirement aggregators return `Ok(None)`.
pub fn with_beneficial_ordering(
self: Arc<Self>,
beneficial_ordering: bool,
) -> Result<Option<AggregateFunctionExpr>> {
let Some(updated_fn) = self
.fun
.clone()
.with_beneficial_ordering(beneficial_ordering)?
else {
return Ok(None);
};
AggregateExprBuilder::new(Arc::new(updated_fn), self.args.to_vec())
.order_by(self.order_bys.clone())
.schema(Arc::new(self.schema.clone()))
.alias(self.name().to_string())
.with_ignore_nulls(self.ignore_nulls)
.with_distinct(self.is_distinct)
.with_reversed(self.is_reversed)
.build()
.map(Some)
}
/// Creates accumulator implementation that supports retract
pub fn create_sliding_accumulator(&self) -> Result<Box<dyn Accumulator>> {
let args = AccumulatorArgs {
return_field: Arc::clone(&self.return_field),
schema: &self.schema,
expr_fields: &self.arg_fields,
ignore_nulls: self.ignore_nulls,
order_bys: self.order_bys.as_ref(),
is_distinct: self.is_distinct,
name: &self.name,
is_reversed: self.is_reversed,
exprs: &self.args,
};
let accumulator = self.fun.create_sliding_accumulator(args)?;
// Accumulators that have window frame startings different
// than `UNBOUNDED PRECEDING`, such as `1 PRECEDING`, need to
// implement retract_batch method in order to run correctly
// currently in DataFusion.
//
// If this `retract_batches` is not present, there is no way
// to calculate result correctly. For example, the query
//
// ```sql
// SELECT
// SUM(a) OVER(ORDER BY a ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS sum_a
// FROM
// t
// ```
//
// 1. First sum value will be the sum of rows between `[0, 1)`,
//
// 2. Second sum value will be the sum of rows between `[0, 2)`
//
// 3. Third sum value will be the sum of rows between `[1, 3)`, etc.
//
// Since the accumulator keeps the running sum:
//
// 1. First sum we add to the state sum value between `[0, 1)`
//
// 2. Second sum we add to the state sum value between `[1, 2)`
// (`[0, 1)` is already in the state sum, hence running sum will
// cover `[0, 2)` range)
//
// 3. Third sum we add to the state sum value between `[2, 3)`
// (`[0, 2)` is already in the state sum). Also we need to
// retract values between `[0, 1)` by this way we can obtain sum
// between [1, 3) which is indeed the appropriate range.
//
// When we use `UNBOUNDED PRECEDING` in the query starting
// index will always be 0 for the desired range, and hence the
// `retract_batch` method will not be called. In this case
// having retract_batch is not a requirement.
//
// This approach is a a bit different than window function
// approach. In window function (when they use a window frame)
// they get all the desired range during evaluation.
if !accumulator.supports_retract_batch() {
return not_impl_err!(
"Aggregate can not be used as a sliding accumulator because \
`retract_batch` is not implemented: {}",
self.name
);
}
Ok(accumulator)
}
/// If the aggregate expression has a specialized
/// [`GroupsAccumulator`] implementation. If this returns true,
/// `[Self::create_groups_accumulator`] will be called.
pub fn groups_accumulator_supported(&self) -> bool {
let args = AccumulatorArgs {
return_field: Arc::clone(&self.return_field),
schema: &self.schema,
expr_fields: &self.arg_fields,
ignore_nulls: self.ignore_nulls,
order_bys: self.order_bys.as_ref(),
is_distinct: self.is_distinct,
name: &self.name,
is_reversed: self.is_reversed,
exprs: &self.args,
};
self.fun.groups_accumulator_supported(args)
}
/// Return a specialized [`GroupsAccumulator`] that manages state
/// for all groups.
///
/// For maximum performance, a [`GroupsAccumulator`] should be
/// implemented in addition to [`Accumulator`].
pub fn create_groups_accumulator(&self) -> Result<Box<dyn GroupsAccumulator>> {
let args = AccumulatorArgs {
return_field: Arc::clone(&self.return_field),
schema: &self.schema,
expr_fields: &self.arg_fields,
ignore_nulls: self.ignore_nulls,
order_bys: self.order_bys.as_ref(),
is_distinct: self.is_distinct,
name: &self.name,
is_reversed: self.is_reversed,
exprs: &self.args,
};
self.fun.create_groups_accumulator(args)
}
/// Construct an expression that calculates the aggregate in reverse.
/// Typically the "reverse" expression is itself (e.g. SUM, COUNT).
/// For aggregates that do not support calculation in reverse,
/// returns None (which is the default value).
pub fn reverse_expr(&self) -> Option<AggregateFunctionExpr> {
match self.fun.reverse_udf() {
ReversedUDAF::NotSupported => None,
ReversedUDAF::Identical => Some(self.clone()),
ReversedUDAF::Reversed(reverse_udf) => {
let mut name = self.name().to_string();
// If the function is changed, we need to reverse order_by clause as well
// i.e. First(a order by b asc null first) -> Last(a order by b desc null last)
if self.fun().name() != reverse_udf.name() {
replace_order_by_clause(&mut name);
}
replace_fn_name_clause(&mut name, self.fun.name(), reverse_udf.name());
AggregateExprBuilder::new(reverse_udf, self.args.to_vec())
.order_by(self.order_bys.iter().map(|e| e.reverse()).collect())
.schema(Arc::new(self.schema.clone()))
.alias(name)
.with_ignore_nulls(self.ignore_nulls)
.with_distinct(self.is_distinct)
.with_reversed(!self.is_reversed)
.build()
.ok()
}
}
}
/// Returns all expressions used in the [`AggregateFunctionExpr`].
/// These expressions are (1)function arguments, (2) order by expressions.
pub fn all_expressions(&self) -> AggregatePhysicalExpressions {
let args = self.expressions();
let order_by_exprs = self
.order_bys()
.iter()
.map(|sort_expr| Arc::clone(&sort_expr.expr))
.collect();
AggregatePhysicalExpressions {
args,
order_by_exprs,
}
}
/// Rewrites [`AggregateFunctionExpr`], with new expressions given. The argument should be consistent
/// with the return value of the [`AggregateFunctionExpr::all_expressions`] method.
/// Returns `Some(Arc<dyn AggregateExpr>)` if re-write is supported, otherwise returns `None`.
pub fn with_new_expressions(
&self,
args: Vec<Arc<dyn PhysicalExpr>>,
order_by_exprs: Vec<Arc<dyn PhysicalExpr>>,
) -> Option<AggregateFunctionExpr> {
if args.len() != self.args.len()
|| (self.order_sensitivity() != AggregateOrderSensitivity::Insensitive
&& order_by_exprs.len() != self.order_bys.len())
{
return None;
}
let new_order_bys = self
.order_bys
.iter()
.zip(order_by_exprs)
.map(|(req, new_expr)| PhysicalSortExpr {
expr: new_expr,
options: req.options,
})
.collect();
Some(AggregateFunctionExpr {
fun: self.fun.clone(),
args,
// TODO: need to align arg_fields here with new args
// https://github.com/apache/datafusion/issues/18149
arg_fields: self.arg_fields.clone(),
return_field: Arc::clone(&self.return_field),
name: self.name.clone(),
// TODO: Human name should be updated after re-write to not mislead
human_display: self.human_display.clone(),
schema: self.schema.clone(),
order_bys: new_order_bys,
ignore_nulls: self.ignore_nulls,
ordering_fields: self.ordering_fields.clone(),
is_distinct: self.is_distinct,
is_reversed: false,
input_fields: self.input_fields.clone(),
is_nullable: self.is_nullable,
})
}
/// If this function is max, return (output_field, true)
/// if the function is min, return (output_field, false)
/// otherwise return None (the default)
///
/// output_field is the name of the column produced by this aggregate
///
/// Note: this is used to use special aggregate implementations in certain conditions
pub fn get_minmax_desc(&self) -> Option<(FieldRef, bool)> {
self.fun.is_descending().map(|flag| (self.field(), flag))
}
/// Returns default value of the function given the input is Null
/// Most of the aggregate function return Null if input is Null,
/// while `count` returns 0 if input is Null
pub fn default_value(&self, data_type: &DataType) -> Result<ScalarValue> {
self.fun.default_value(data_type)
}
/// Indicates whether the aggregation function is monotonic as a set
/// function. See [`SetMonotonicity`] for details.
pub fn set_monotonicity(&self) -> SetMonotonicity {
let field = self.field();
let data_type = field.data_type();
self.fun.inner().set_monotonicity(data_type)
}
/// Returns `PhysicalSortExpr` based on the set monotonicity of the function.
pub fn get_result_ordering(&self, aggr_func_idx: usize) -> Option<PhysicalSortExpr> {
// If the aggregate expressions are set-monotonic, the output data is
// naturally ordered with it per group or partition.
let monotonicity = self.set_monotonicity();
if monotonicity == SetMonotonicity::NotMonotonic {
return None;
}
let expr = Arc::new(Column::new(self.name(), aggr_func_idx));
let options =
SortOptions::new(monotonicity == SetMonotonicity::Decreasing, false);
Some(PhysicalSortExpr { expr, options })
}
}
/// Stores the physical expressions used inside the `AggregateExpr`.
pub struct AggregatePhysicalExpressions {
/// Aggregate function arguments
pub args: Vec<Arc<dyn PhysicalExpr>>,
/// Order by expressions
pub order_by_exprs: Vec<Arc<dyn PhysicalExpr>>,
}
impl PartialEq for AggregateFunctionExpr {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& self.return_field == other.return_field
&& self.fun == other.fun
&& self.args.len() == other.args.len()
&& self
.args
.iter()
.zip(other.args.iter())
.all(|(this_arg, other_arg)| this_arg.eq(other_arg))
}
}
fn replace_order_by_clause(order_by: &mut String) {
let suffixes = [
(" DESC NULLS FIRST]", " ASC NULLS LAST]"),
(" ASC NULLS FIRST]", " DESC NULLS LAST]"),
(" DESC NULLS LAST]", " ASC NULLS FIRST]"),
(" ASC NULLS LAST]", " DESC NULLS FIRST]"),
];
if let Some(start) = order_by.find("ORDER BY [") {
if let Some(end) = order_by[start..].find(']') {
let order_by_start = start + 9;
let order_by_end = start + end;
let column_order = &order_by[order_by_start..=order_by_end];
for (suffix, replacement) in suffixes {
if column_order.ends_with(suffix) {
let new_order = column_order.replace(suffix, replacement);
order_by.replace_range(order_by_start..=order_by_end, &new_order);
break;
}
}
}
}
}
fn replace_fn_name_clause(aggr_name: &mut String, fn_name_old: &str, fn_name_new: &str) {
*aggr_name = aggr_name.replace(fn_name_old, fn_name_new);
}