Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 59a3795

Browse files
committed
Simplify planner's final setup of Aggrefs for partial aggregation.
Commit e06a389's original coding for constructing the execution-time expression tree for a combining aggregate was rather messy, involving duplicating quite a lot of code in setrefs.c so that it could inject a nonstandard matching rule for Aggrefs. Get rid of that in favor of explicitly constructing a combining Aggref with a partial Aggref as input, then allowing setref's normal matching logic to match the partial Aggref to the output of the lower plan node and hence replace it with a Var. In passing, rename and redocument make_partialgroup_input_target to have some connection to what it actually does.
1 parent e3ad3ff commit 59a3795

File tree

5 files changed

+171
-334
lines changed

5 files changed

+171
-334
lines changed

src/backend/optimizer/plan/planner.c

+89-37
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "access/sysattr.h"
2424
#include "access/xact.h"
2525
#include "catalog/pg_constraint_fn.h"
26+
#include "catalog/pg_type.h"
2627
#include "executor/executor.h"
2728
#include "executor/nodeAgg.h"
2829
#include "foreign/fdwapi.h"
@@ -140,8 +141,8 @@ static RelOptInfo *create_ordered_paths(PlannerInfo *root,
140141
double limit_tuples);
141142
static PathTarget *make_group_input_target(PlannerInfo *root,
142143
PathTarget *final_target);
143-
static PathTarget *make_partialgroup_input_target(PlannerInfo *root,
144-
PathTarget *final_target);
144+
static PathTarget *make_partial_grouping_target(PlannerInfo *root,
145+
PathTarget *grouping_target);
145146
static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
146147
static List *select_active_windows(PlannerInfo *root, WindowFuncLists *wflists);
147148
static PathTarget *make_window_input_target(PlannerInfo *root,
@@ -3456,12 +3457,13 @@ create_grouping_paths(PlannerInfo *root,
34563457
Path *cheapest_partial_path = linitial(input_rel->partial_pathlist);
34573458

34583459
/*
3459-
* Build target list for partial aggregate paths. We cannot reuse the
3460-
* final target as Aggrefs must be set in partial mode, and we must
3461-
* also include Aggrefs from the HAVING clause in the target as these
3462-
* may not be present in the final target.
3460+
* Build target list for partial aggregate paths. These paths cannot
3461+
* just emit the same tlist as regular aggregate paths, because (1) we
3462+
* must include Vars and Aggrefs needed in HAVING, which might not
3463+
* appear in the result tlist, and (2) the Aggrefs must be set in
3464+
* partial mode.
34633465
*/
3464-
partial_grouping_target = make_partialgroup_input_target(root, target);
3466+
partial_grouping_target = make_partial_grouping_target(root, target);
34653467

34663468
/* Estimate number of partial groups. */
34673469
dNumPartialGroups = get_number_of_groups(root,
@@ -4317,46 +4319,48 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
43174319
}
43184320

43194321
/*
4320-
* make_partialgroup_input_target
4321-
* Generate appropriate PathTarget for input for Partial Aggregate nodes.
4322+
* make_partial_grouping_target
4323+
* Generate appropriate PathTarget for output of partial aggregate
4324+
* (or partial grouping, if there are no aggregates) nodes.
43224325
*
4323-
* Similar to make_group_input_target(), only we don't recurse into Aggrefs, as
4324-
* we need these to remain intact so that they can be found later in Combine
4325-
* Aggregate nodes during set_combineagg_references(). Vars will be still
4326-
* pulled out of non-Aggref nodes as these will still be required by the
4327-
* combine aggregate phase.
4326+
* A partial aggregation node needs to emit all the same aggregates that
4327+
* a regular aggregation node would, plus any aggregates used in HAVING;
4328+
* except that the Aggref nodes should be marked as partial aggregates.
43284329
*
4329-
* We also convert any Aggrefs which we do find and put them into partial mode,
4330-
* this adjusts the Aggref's return type so that the partially calculated
4331-
* aggregate value can make its way up the execution tree up to the Finalize
4332-
* Aggregate node.
4330+
* In addition, we'd better emit any Vars and PlaceholderVars that are
4331+
* used outside of Aggrefs in the aggregation tlist and HAVING. (Presumably,
4332+
* these would be Vars that are grouped by or used in grouping expressions.)
4333+
*
4334+
* grouping_target is the tlist to be emitted by the topmost aggregation step.
4335+
* We get the HAVING clause out of *root.
43334336
*/
43344337
static PathTarget *
4335-
make_partialgroup_input_target(PlannerInfo *root, PathTarget *final_target)
4338+
make_partial_grouping_target(PlannerInfo *root, PathTarget *grouping_target)
43364339
{
43374340
Query *parse = root->parse;
4338-
PathTarget *input_target;
4341+
PathTarget *partial_target;
43394342
List *non_group_cols;
43404343
List *non_group_exprs;
43414344
int i;
43424345
ListCell *lc;
43434346

4344-
input_target = create_empty_pathtarget();
4347+
partial_target = create_empty_pathtarget();
43454348
non_group_cols = NIL;
43464349

43474350
i = 0;
4348-
foreach(lc, final_target->exprs)
4351+
foreach(lc, grouping_target->exprs)
43494352
{
43504353
Expr *expr = (Expr *) lfirst(lc);
4351-
Index sgref = get_pathtarget_sortgroupref(final_target, i);
4354+
Index sgref = get_pathtarget_sortgroupref(grouping_target, i);
43524355

43534356
if (sgref && parse->groupClause &&
43544357
get_sortgroupref_clause_noerr(sgref, parse->groupClause) != NULL)
43554358
{
43564359
/*
4357-
* It's a grouping column, so add it to the input target as-is.
4360+
* It's a grouping column, so add it to the partial_target as-is.
4361+
* (This allows the upper agg step to repeat the grouping calcs.)
43584362
*/
4359-
add_column_to_pathtarget(input_target, expr, sgref);
4363+
add_column_to_pathtarget(partial_target, expr, sgref);
43604364
}
43614365
else
43624366
{
@@ -4371,35 +4375,83 @@ make_partialgroup_input_target(PlannerInfo *root, PathTarget *final_target)
43714375
}
43724376

43734377
/*
4374-
* If there's a HAVING clause, we'll need the Aggrefs it uses, too.
4378+
* If there's a HAVING clause, we'll need the Vars/Aggrefs it uses, too.
43754379
*/
43764380
if (parse->havingQual)
43774381
non_group_cols = lappend(non_group_cols, parse->havingQual);
43784382

43794383
/*
4380-
* Pull out all the Vars mentioned in non-group cols (plus HAVING), and
4381-
* add them to the input target if not already present. (A Var used
4382-
* directly as a GROUP BY item will be present already.) Note this
4383-
* includes Vars used in resjunk items, so we are covering the needs of
4384-
* ORDER BY and window specifications. Vars used within Aggrefs will be
4385-
* ignored and the Aggrefs themselves will be added to the PathTarget.
4384+
* Pull out all the Vars, PlaceHolderVars, and Aggrefs mentioned in
4385+
* non-group cols (plus HAVING), and add them to the partial_target if not
4386+
* already present. (An expression used directly as a GROUP BY item will
4387+
* be present already.) Note this includes Vars used in resjunk items, so
4388+
* we are covering the needs of ORDER BY and window specifications.
43864389
*/
43874390
non_group_exprs = pull_var_clause((Node *) non_group_cols,
43884391
PVC_INCLUDE_AGGREGATES |
43894392
PVC_RECURSE_WINDOWFUNCS |
43904393
PVC_INCLUDE_PLACEHOLDERS);
43914394

4392-
add_new_columns_to_pathtarget(input_target, non_group_exprs);
4395+
add_new_columns_to_pathtarget(partial_target, non_group_exprs);
4396+
4397+
/*
4398+
* Adjust Aggrefs to put them in partial mode. At this point all Aggrefs
4399+
* are at the top level of the target list, so we can just scan the list
4400+
* rather than recursing through the expression trees.
4401+
*/
4402+
foreach(lc, partial_target->exprs)
4403+
{
4404+
Aggref *aggref = (Aggref *) lfirst(lc);
4405+
4406+
if (IsA(aggref, Aggref))
4407+
{
4408+
Aggref *newaggref;
4409+
4410+
/*
4411+
* We shouldn't need to copy the substructure of the Aggref node,
4412+
* but flat-copy the node itself to avoid damaging other trees.
4413+
*/
4414+
newaggref = makeNode(Aggref);
4415+
memcpy(newaggref, aggref, sizeof(Aggref));
4416+
4417+
/* XXX assume serialization required */
4418+
mark_partial_aggref(newaggref, true);
4419+
4420+
lfirst(lc) = newaggref;
4421+
}
4422+
}
43934423

43944424
/* clean up cruft */
43954425
list_free(non_group_exprs);
43964426
list_free(non_group_cols);
43974427

4398-
/* Adjust Aggrefs to put them in partial mode. */
4399-
apply_partialaggref_adjustment(input_target);
4400-
44014428
/* XXX this causes some redundant cost calculation ... */
4402-
return set_pathtarget_cost_width(root, input_target);
4429+
return set_pathtarget_cost_width(root, partial_target);
4430+
}
4431+
4432+
/*
4433+
* mark_partial_aggref
4434+
* Adjust an Aggref to make it represent the output of partial aggregation.
4435+
*
4436+
* The Aggref node is modified in-place; caller must do any copying required.
4437+
*/
4438+
void
4439+
mark_partial_aggref(Aggref *agg, bool serialize)
4440+
{
4441+
/* aggtranstype should be computed by this point */
4442+
Assert(OidIsValid(agg->aggtranstype));
4443+
4444+
/*
4445+
* Normally, a partial aggregate returns the aggregate's transition type;
4446+
* but if that's INTERNAL and we're serializing, it returns BYTEA instead.
4447+
*/
4448+
if (agg->aggtranstype == INTERNALOID && serialize)
4449+
agg->aggoutputtype = BYTEAOID;
4450+
else
4451+
agg->aggoutputtype = agg->aggtranstype;
4452+
4453+
/* flag it as partial */
4454+
agg->aggpartial = true;
44034455
}
44044456

44054457
/*

0 commit comments

Comments
 (0)