23
23
#include "access/sysattr.h"
24
24
#include "access/xact.h"
25
25
#include "catalog/pg_constraint_fn.h"
26
+ #include "catalog/pg_type.h"
26
27
#include "executor/executor.h"
27
28
#include "executor/nodeAgg.h"
28
29
#include "foreign/fdwapi.h"
@@ -140,8 +141,8 @@ static RelOptInfo *create_ordered_paths(PlannerInfo *root,
140
141
double limit_tuples );
141
142
static PathTarget * make_group_input_target (PlannerInfo * root ,
142
143
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 );
145
146
static List * postprocess_setop_tlist (List * new_tlist , List * orig_tlist );
146
147
static List * select_active_windows (PlannerInfo * root , WindowFuncLists * wflists );
147
148
static PathTarget * make_window_input_target (PlannerInfo * root ,
@@ -3456,12 +3457,13 @@ create_grouping_paths(PlannerInfo *root,
3456
3457
Path * cheapest_partial_path = linitial (input_rel -> partial_pathlist );
3457
3458
3458
3459
/*
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.
3463
3465
*/
3464
- partial_grouping_target = make_partialgroup_input_target (root , target );
3466
+ partial_grouping_target = make_partial_grouping_target (root , target );
3465
3467
3466
3468
/* Estimate number of partial groups. */
3467
3469
dNumPartialGroups = get_number_of_groups (root ,
@@ -4317,46 +4319,48 @@ make_group_input_target(PlannerInfo *root, PathTarget *final_target)
4317
4319
}
4318
4320
4319
4321
/*
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.
4322
4325
*
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.
4328
4329
*
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.
4333
4336
*/
4334
4337
static PathTarget *
4335
- make_partialgroup_input_target (PlannerInfo * root , PathTarget * final_target )
4338
+ make_partial_grouping_target (PlannerInfo * root , PathTarget * grouping_target )
4336
4339
{
4337
4340
Query * parse = root -> parse ;
4338
- PathTarget * input_target ;
4341
+ PathTarget * partial_target ;
4339
4342
List * non_group_cols ;
4340
4343
List * non_group_exprs ;
4341
4344
int i ;
4342
4345
ListCell * lc ;
4343
4346
4344
- input_target = create_empty_pathtarget ();
4347
+ partial_target = create_empty_pathtarget ();
4345
4348
non_group_cols = NIL ;
4346
4349
4347
4350
i = 0 ;
4348
- foreach (lc , final_target -> exprs )
4351
+ foreach (lc , grouping_target -> exprs )
4349
4352
{
4350
4353
Expr * expr = (Expr * ) lfirst (lc );
4351
- Index sgref = get_pathtarget_sortgroupref (final_target , i );
4354
+ Index sgref = get_pathtarget_sortgroupref (grouping_target , i );
4352
4355
4353
4356
if (sgref && parse -> groupClause &&
4354
4357
get_sortgroupref_clause_noerr (sgref , parse -> groupClause ) != NULL )
4355
4358
{
4356
4359
/*
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.)
4358
4362
*/
4359
- add_column_to_pathtarget (input_target , expr , sgref );
4363
+ add_column_to_pathtarget (partial_target , expr , sgref );
4360
4364
}
4361
4365
else
4362
4366
{
@@ -4371,35 +4375,83 @@ make_partialgroup_input_target(PlannerInfo *root, PathTarget *final_target)
4371
4375
}
4372
4376
4373
4377
/*
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.
4375
4379
*/
4376
4380
if (parse -> havingQual )
4377
4381
non_group_cols = lappend (non_group_cols , parse -> havingQual );
4378
4382
4379
4383
/*
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.
4386
4389
*/
4387
4390
non_group_exprs = pull_var_clause ((Node * ) non_group_cols ,
4388
4391
PVC_INCLUDE_AGGREGATES |
4389
4392
PVC_RECURSE_WINDOWFUNCS |
4390
4393
PVC_INCLUDE_PLACEHOLDERS );
4391
4394
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
+ }
4393
4423
4394
4424
/* clean up cruft */
4395
4425
list_free (non_group_exprs );
4396
4426
list_free (non_group_cols );
4397
4427
4398
- /* Adjust Aggrefs to put them in partial mode. */
4399
- apply_partialaggref_adjustment (input_target );
4400
-
4401
4428
/* 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;
4403
4455
}
4404
4456
4405
4457
/*
0 commit comments