use sqlparser::ast;
use crate::error::Result;
use crate::functions::registry::FunctionRegistry;
use crate::planner::agg_bind::{BindName, bind_aggregate_calls};
use crate::planner::group_by::{convert_group_by_with_projection, group_by_output_aliases};
use crate::planner::grouping_sets::expand_group_by;
use crate::resolver::columns::TableScope;
use crate::types::{AggOutputSlot, AggregateExpr, ColumnInfo, Filter, SqlExpr};
pub struct AggregateClauses {
pub group_by: Vec<SqlExpr>,
pub grouping_sets: Option<Vec<Vec<usize>>>,
pub aggregates: Vec<AggregateExpr>,
pub having: Vec<Filter>,
pub group_by_aliases: Vec<Option<String>>,
pub output_order: Vec<AggOutputSlot>,
pub hidden_aggregates: bool,
}
pub fn plan_aggregate_clauses(
select: &ast::Select,
order_by: Option<&ast::OrderBy>,
table_columns: &[ColumnInfo],
scope: &TableScope,
functions: &FunctionRegistry,
) -> Result<AggregateClauses> {
let (group_by, grouping_sets) = match expand_group_by(&select.group_by, scope)? {
Some(expansion) => (expansion.canonical_keys, Some(expansion.grouping_sets)),
None => (
convert_group_by_with_projection(
&select.group_by,
&select.projection,
table_columns,
scope,
)?,
None,
),
};
let mut aggregates =
super::aggregate::extract_aggregates_from_projection(&select.projection, functions, scope)?;
let projected = aggregates.len();
let having = match &select.having {
Some(expr) => {
super::having::plan_having(expr, &select.projection, &mut aggregates, functions, scope)?
}
None => Vec::new(),
};
let hidden_aggregates = aggregates.len() > projected
|| sort_names_new_aggregate(order_by, &select.projection, &aggregates, functions, scope)?;
if grouping_sets.is_some() {
let grouping_aggs =
super::aggregate::extract_grouping_calls(&select.projection, &group_by, scope)?;
aggregates.extend(grouping_aggs);
}
let output_order = super::aggregate_order::compute_output_order(
&select.projection,
&group_by,
functions,
scope,
)?;
let group_by_aliases = group_by_output_aliases(&select.projection, &group_by);
Ok(AggregateClauses {
group_by,
grouping_sets,
aggregates,
having,
group_by_aliases,
output_order,
hidden_aggregates,
})
}
fn sort_names_new_aggregate(
order_by: Option<&ast::OrderBy>,
projection: &[ast::SelectItem],
aggregates: &[AggregateExpr],
functions: &FunctionRegistry,
scope: &TableScope,
) -> Result<bool> {
let Some(ast::OrderBy {
kind: ast::OrderByKind::Expressions(exprs),
..
}) = order_by
else {
return Ok(false);
};
let mut extended = aggregates.to_vec();
for o in exprs {
bind_aggregate_calls(
&o.expr,
projection,
&mut extended,
functions,
BindName::Canonical,
scope,
)?;
}
Ok(extended.len() > aggregates.len())
}