akar_processor/processor/mapper/
map_aggregate.rs1use super::ExecutionContext;
2use crate::physical::order_aggregate::resolve_group_by_indices;
3use crate::physical_operator::*;
4use akar_common::error::ProcessorError;
5use akar_common::vector::DataChunk;
6use akar_parser::ast::Expression;
7use akar_planner::logical_operator::LogicalOperator;
8
9pub fn map_and_execute_aggregate(
10 op: &LogicalOperator,
11 current_input: Vec<DataChunk>,
12 ctx: &mut ExecutionContext,
13) -> Result<Vec<DataChunk>, ProcessorError> {
14 match op {
15 LogicalOperator::Aggregate(a) => {
16 let mut funcs: Vec<akar_function::AggregateFunction> = Vec::with_capacity(a.aggregates.len());
20 let mut distinct_flags: Vec<bool> = Vec::with_capacity(a.aggregates.len());
21 for (n, args) in &a.aggregates {
22 let (base, distinct) = split_distinct_name(n);
23 distinct_flags.push(distinct);
24 let effective_name = if base == "COUNT" && args.iter().any(|e| matches!(e, Expression::Star)) {
26 "COUNT(*)"
27 } else {
28 base
29 };
30 funcs.push(crate::physical::order_aggregate::parse_aggregate_function(
31 effective_name,
32 ));
33 }
34 let agg_expressions: Vec<Vec<Expression>> = a.aggregates.iter().map(|(_, args)| args.clone()).collect();
35
36 let field_names = current_input.first().map(|c| c.field_names.as_slice()).unwrap_or(&[]);
38 let group_by_cols = if a.group_by.is_empty() {
39 Vec::new()
40 } else {
41 resolve_group_by_indices(&a.group_by, field_names)
42 };
43
44 let shared_state = std::sync::Arc::new(crate::physical::order_aggregate::SharedAggregateState::new(
45 funcs,
46 group_by_cols,
47 agg_expressions,
48 distinct_flags,
49 ));
50
51 let agg_scan = crate::physical::order_aggregate::PhysicalAggregateScan {
52 shared_state: shared_state.clone(),
53 };
54 let agg_finalize = crate::physical::order_aggregate::PhysicalAggregateFinalize { shared_state };
55
56 let _ = agg_scan.execute(current_input)?;
58
59 let result = agg_finalize.execute(vec![])?;
61
62 let names = aggregate_field_names(a);
69 let result: Vec<DataChunk> = result
70 .into_iter()
71 .map(|chunk| chunk.with_names(names.clone()))
72 .collect();
73
74 Ok(result)
75 }
76 LogicalOperator::CountRelTable(crt) => {
77 let physical = PhysicalCountRelTable {
78 table_name: crt.table_name.clone(),
79 table_id: crt.table_id,
80 table_catalog: ctx.table_catalog.clone(),
81 };
82 let result = physical.execute(vec![])?;
83 Ok(result)
84 }
85 _ => Err(format!("Not an aggregate operator: {:?}", op).into()),
86 }
87}
88
89fn split_distinct_name(name: &str) -> (&str, bool) {
93 match name.strip_suffix("_DISTINCT") {
94 Some(base) => (base, true),
95 None => (name, false),
96 }
97}
98
99fn aggregate_field_names(a: &akar_planner::logical_operator::LogicalAggregate) -> Vec<String> {
104 let mut names: Vec<String> = a
105 .group_by
106 .iter()
107 .map(|e| match e {
108 Expression::Variable(v) => v.clone(),
109 Expression::PropertyAccess(obj, prop) => {
110 if let Expression::Variable(var) = &**obj {
111 format!("{var}.{prop}")
112 } else {
113 prop.clone()
114 }
115 }
116 other => format!("{other:?}"),
117 })
118 .collect();
119 for (fname, args) in &a.aggregates {
120 let (fname, _) = split_distinct_name(fname);
121 let effective = if fname == "COUNT" && args.iter().any(|e| matches!(e, Expression::Star)) {
122 "COUNT(*)".to_string()
123 } else if args.len() == 1 {
124 match &args[0] {
125 Expression::Variable(v) => format!("{fname}({v})"),
126 Expression::PropertyAccess(obj, prop) => {
127 if let Expression::Variable(base) = &**obj {
128 format!("{fname}({base}.{prop})")
129 } else {
130 format!("{fname}({prop})")
131 }
132 }
133 Expression::Star => format!("{fname}(*)"),
134 _ => fname.to_string(),
135 }
136 } else {
137 fname.to_string()
138 };
139 names.push(effective);
140 }
141 names
142}