diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index ad99690..aa8719e 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -134,7 +134,104 @@ static Plan *build_grouping_chain(PlannerInfo *root, AttrNumber *groupColIdx, AggClauseCosts *agg_costs, long numGroups, - Plan *result_plan); + Plan *result_plan, + PartialAggType partialaggtype); + +static List * +make_aggregate_tlist(PlannerInfo *root, + List *tlist, + AttrNumber **groupColIdx) +{ + Query *parse = root->parse; + List *sub_tlist; + List *non_group_cols; + List *non_group_vars; + int numCols; + ListCell *tl; + + *groupColIdx = NULL; + + /* + * Otherwise, we must build a tlist containing all grouping columns, plus + * any other Vars mentioned in the targetlist and HAVING qual. + */ + sub_tlist = NIL; + non_group_cols = NIL; + + numCols = list_length(parse->groupClause); + if (numCols > 0) + { + /* + * If grouping, create sub_tlist entries for all GROUP BY columns, and + * make an array showing where the group columns are in the sub_tlist. + * + * Note: with this implementation, the array entries will always be + * 1..N, but we don't want callers to assume that. + */ + AttrNumber *grpColIdx; + + grpColIdx = (AttrNumber *) palloc0(sizeof(AttrNumber) * numCols); + *groupColIdx = grpColIdx; + + foreach(tl, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(tl); + int colno; + + colno = get_grouping_column_index(parse, tle); + if (colno >= 0) + { + /* + * It's a grouping column, so add it to the result tlist and + * remember its resno in grpColIdx[]. + */ + TargetEntry *newtle; + + newtle = makeTargetEntry((Expr *) copyObject(tle->expr), + list_length(sub_tlist) + 1, + NULL, + tle->resjunk); + newtle->ressortgroupref = tle->ressortgroupref; + sub_tlist = lappend(sub_tlist, newtle); + + Assert(grpColIdx[colno] == 0); /* no dups expected */ + grpColIdx[colno] = newtle->resno; + } + else + { + non_group_cols = lappend(non_group_cols, tle->expr); + } + } + } + else + { + foreach(tl, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(tl); + non_group_cols = lappend(non_group_cols, tle->expr); + } + } + + /* + * If there's a HAVING clause, we'll need need to ensure all Aggrefs from + * there are also in the targetlist + */ + if (parse->havingQual) + non_group_cols = lappend(non_group_cols, parse->havingQual); + + + non_group_vars = pull_var_clause((Node *) non_group_cols, + PVC_INCLUDE_AGGREGATES, + PVC_INCLUDE_PLACEHOLDERS); + + sub_tlist = add_to_flat_tlist(sub_tlist, non_group_vars); + + /* clean up cruft */ + list_free(non_group_vars); + list_free(non_group_cols); + + return sub_tlist; +} /***************************************************************************** * @@ -1896,6 +1993,19 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) AttrNumber *groupColIdx = NULL; bool need_tlist_eval = true; bool need_sort_for_grouping = false; + PartialAggType partialaggtype; + + /* Determine the level of partial aggregation we can use */ + if (parse->groupingSets) + partialaggtype = PAT_DISABLED; + else + { + partialaggtype = aggregates_allow_partial((Node *) tlist); + + if (partialaggtype != PAT_DISABLED) + partialaggtype = Min(partialaggtype, + aggregates_allow_partial(root->parse->havingQual)); + } result_plan = create_plan(root, best_path); current_pathkeys = best_path->pathkeys; @@ -1913,6 +2023,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) &groupColIdx, &need_tlist_eval); + if (partialaggtype != PAT_DISABLED) + need_tlist_eval = true; + /* * create_plan returns a plan with just a "flat" tlist of required * Vars. Usually we need to insert the sub_tlist as the tlist of @@ -1994,21 +2107,74 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) */ if (use_hashed_grouping) { - /* Hashed aggregate plan --- no sort needed */ - result_plan = (Plan *) make_agg(root, - tlist, - (List *) parse->havingQual, - AGG_HASHED, - &agg_costs, - numGroupCols, - groupColIdx, - extract_grouping_ops(parse->groupClause), - NIL, - numGroups, - false, - true, - false, - result_plan); + if (partialaggtype != PAT_DISABLED) + { + AttrNumber *groupColIdx; + List *aggtlist; + + aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx); + + /* Hashed aggregate plan --- no sort needed */ + result_plan = (Plan *) make_agg(root, + aggtlist, + NIL, + AGG_HASHED, + &agg_costs, + numGroupCols, + groupColIdx, + extract_grouping_ops(parse->groupClause), + NIL, + numGroups, + false, + false, + true, + result_plan); + + result_plan->targetlist = aggtlist; + + /* + * Also, account for the cost of evaluation of the sub_tlist. + * See comments for add_tlist_costs_to_plan() for more info. + */ + add_tlist_costs_to_plan(root, result_plan, aggtlist); + + aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx); + + result_plan = (Plan *) make_agg(root, + aggtlist, + (List *) parse->havingQual, + AGG_HASHED, + &agg_costs, + numGroupCols, + groupColIdx, + extract_grouping_ops(parse->groupClause), + NIL, + numGroups, + true, + true, + true, + result_plan); + result_plan->targetlist = tlist; + + } + else + { + /* Hashed aggregate plan --- no sort needed */ + result_plan = (Plan *) make_agg(root, + tlist, + (List *) parse->havingQual, + AGG_HASHED, + &agg_costs, + numGroupCols, + groupColIdx, + extract_grouping_ops(parse->groupClause), + NIL, + numGroups, + false, + true, + false, + result_plan); + } /* Hashed aggregation produces randomly-ordered results */ current_pathkeys = NIL; } @@ -2037,7 +2203,8 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) groupColIdx, &agg_costs, numGroups, - result_plan); + result_plan, + partialaggtype); } else if (parse->groupClause) { @@ -2483,7 +2650,8 @@ build_grouping_chain(PlannerInfo *root, AttrNumber *groupColIdx, AggClauseCosts *agg_costs, long numGroups, - Plan *result_plan) + Plan *result_plan, + PartialAggType partialaggtype) { AttrNumber *top_grpColIdx = groupColIdx; List *chain = NIL; @@ -2587,21 +2755,70 @@ build_grouping_chain(PlannerInfo *root, else numGroupCols = list_length(parse->groupClause); - result_plan = (Plan *) make_agg(root, - tlist, - (List *) parse->havingQual, - (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN, - agg_costs, - numGroupCols, - top_grpColIdx, - extract_grouping_ops(groupClause), - gsets, - numGroups, - false, - true, - false, - result_plan); + if (partialaggtype != PAT_DISABLED) + { + AttrNumber *groupColIdx; + List *aggtlist; + + aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx); + + result_plan = (Plan *) make_agg(root, + aggtlist, + NIL, + (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN, + agg_costs, + numGroupCols, + groupColIdx, + extract_grouping_ops(groupClause), + gsets, + numGroups, + false, + false, + true, + result_plan); + result_plan->targetlist = aggtlist; + + /* + * Also, account for the cost of evaluation of the sub_tlist. + * See comments for add_tlist_costs_to_plan() for more info. + */ + add_tlist_costs_to_plan(root, result_plan, aggtlist); + + aggtlist = make_aggregate_tlist(root, tlist, &groupColIdx); + result_plan = (Plan *) make_agg(root, + aggtlist, + (List *) parse->havingQual, + (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN, + agg_costs, + numGroupCols, + groupColIdx, + extract_grouping_ops(groupClause), + gsets, + numGroups, + true, + true, + true, + result_plan); + result_plan->targetlist = tlist; + } + else + { + result_plan = (Plan *) make_agg(root, + tlist, + (List *) parse->havingQual, + (numGroupCols > 0) ? AGG_SORTED : AGG_PLAIN, + agg_costs, + numGroupCols, + top_grpColIdx, + extract_grouping_ops(groupClause), + gsets, + numGroups, + false, + true, + false, + result_plan); + } ((Agg *) result_plan)->chain = chain; /*