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 );
141142static 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 );
145146static List * postprocess_setop_tlist (List * new_tlist , List * orig_tlist );
146147static List * select_active_windows (PlannerInfo * root , WindowFuncLists * wflists );
147148static 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 */
43344337static 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