| 1011 | /// the aggregate |
| 1012 | #[expect(clippy::too_many_arguments)] |
| 1013 | fn aggregate( |
| 1014 | &self, |
| 1015 | input: &LogicalPlan, |
| 1016 | select_exprs: &[Expr], |
| 1017 | having_expr_opt: Option<&Expr>, |
| 1018 | qualify_expr_opt: Option<&Expr>, |
| 1019 | order_by_exprs: &[SortExpr], |
| 1020 | group_by_exprs: &[Expr], |
| 1021 | aggr_exprs: &[Expr], |
| 1022 | ) -> Result<AggregatePlanResult> { |
| 1023 | // create the aggregate plan |
| 1024 | let options = |
| 1025 | LogicalPlanBuilderOptions::new().with_add_implicit_group_by_exprs(true); |
| 1026 | let plan = LogicalPlanBuilder::from(input.clone()) |
| 1027 | .with_options(options) |
| 1028 | .aggregate(group_by_exprs.to_vec(), aggr_exprs.to_vec())? |
| 1029 | .build()?; |
| 1030 | let group_by_exprs = if let LogicalPlan::Aggregate(agg) = &plan { |
| 1031 | &agg.group_expr |
| 1032 | } else { |
| 1033 | unreachable!(); |
| 1034 | }; |
| 1035 | |
| 1036 | // in this next section of code we are re-writing the projection to refer to columns |
| 1037 | // output by the aggregate plan. For example, if the projection contains the expression |
| 1038 | // `SUM(a)` then we replace that with a reference to a column `SUM(a)` produced by |
| 1039 | // the aggregate plan. |
| 1040 | |
| 1041 | // combine the original grouping and aggregate expressions into one list (note that |
| 1042 | // we do not add the "having" expression since that is not part of the projection) |
| 1043 | let mut aggr_projection_exprs = vec![]; |
| 1044 | for expr in group_by_exprs { |
| 1045 | match expr { |
| 1046 | Expr::GroupingSet(GroupingSet::Rollup(exprs)) => { |
| 1047 | aggr_projection_exprs.extend_from_slice(exprs) |
| 1048 | } |
| 1049 | Expr::GroupingSet(GroupingSet::Cube(exprs)) => { |
| 1050 | aggr_projection_exprs.extend_from_slice(exprs) |
| 1051 | } |
| 1052 | Expr::GroupingSet(GroupingSet::GroupingSets(lists_of_exprs)) => { |
| 1053 | for exprs in lists_of_exprs { |
| 1054 | aggr_projection_exprs.extend_from_slice(exprs) |
| 1055 | } |
| 1056 | } |
| 1057 | _ => aggr_projection_exprs.push(expr.clone()), |
| 1058 | } |
| 1059 | } |
| 1060 | aggr_projection_exprs.extend_from_slice(aggr_exprs); |
| 1061 | |
| 1062 | // now attempt to resolve columns and replace with fully-qualified columns |
| 1063 | let aggr_projection_exprs = aggr_projection_exprs |
| 1064 | .iter() |
| 1065 | .map(|expr| resolve_columns(expr, input)) |
| 1066 | .collect::<Result<Vec<Expr>>>()?; |
| 1067 | |
| 1068 | // next we replace any expressions that are not a column with a column referencing |
| 1069 | // an output column from the aggregate schema |
| 1070 | let column_exprs_post_aggr = aggr_projection_exprs |