Wiki / Plan Nodes / Aggregation
Aggregate
Agg
Computes aggregate results using the selected grouping strategy and aggregation stage.
Reading PostgreSQL 18.6.
Description
Computes aggregate results using the selected grouping strategy and aggregation stage.
- Core node tag
- T_Agg
- Structured EXPLAIN Node Type
- Aggregate
- Inputs
- One child plan
- Output
- Aggregate result tuples
- Executor initializer
- ExecInitAgg
- Memory mechanism
- aggregate-spill
- EXPLAIN strategies
- Plain, Sorted, Hashed, Mixed
- Aggregation stages
- Partial, Finalize, Simple
EXPLAIN names and attributes
Structured formats use the Node Type above. Text-format spellings can also include operation, strategy, join type, scan direction or aggregation-stage attributes.
Text names recorded by this source: Aggregate, GroupAggregate, HashAggregate, MixedAggregate.
Parallel-aware and parallel-safe are different plan properties. A node running inside a parallel worker is not necessarily a parallel-aware node.
Memory and temporary storage
This implementation contains a disk-spill path for hashed aggregation. Aggregate input sorting and transition-state allocations have their own behavior; one spill policy does not describe every aggregation strategy.
When performing hash aggregation, if the hash table memory exceeds the limit (see hash_agg_check_limits()), we enter "spill mode". In spill mode, we advance the transition states only for groups already in the hash table. For tuples that would need to create a new hash table entries (and initialize new transition states), we instead spill them to disk to be processed later. The tuples are spilled in a partitioned manner, so that subsequent batches are smaller and less likely to exceed hash_mem (if a batch does exceed hash_mem, it must be spilled recursively).
Spilled data is written to logical tapes. These provide better control over memory usage, disk space, and the number of files than if we were to use a BufFile for each spill. We don't know the number of tapes needed at the start of the algorithm (because it can recurse), so a tape set is allocated at the beginning, and individual tapes are created as needed. As a particular tape is read, logtape.c recycles its disk space. When a tape is read to completion, it is destroyed entirely.
Control how many partitions are created when spilling HashAgg to disk.
We also specify a min and max number of partitions per spill. Too few might mean a lot of wasted I/O from repeated spilling of the same tuples. Too many will result in lots of memory wasted buffering the spill files (which could instead be spent on a larger hash table).
Parallel execution and instrumentation
The source callbacks below can coordinate execution or collect worker instrumentation. Their presence is not a blanket claim that this node supports a shared parallel scan or shared state.
Callbacks in this build: ExecAggEstimate, ExecAggInitializeDSM, ExecAggInitializeWorker, ExecAggRetrieveInstrumentation.
Same-version manual discussion
PostgreSQL supports parallel aggregation by aggregating in two stages. First, each process participating in the parallel portion of the query performs an aggregation step, producing a partial result for each group of which that process is aware. This is reflected in the plan as a Partial Aggregate node. Second, the partial results are transferred to the leader via Gather or Gather Merge . Finally, the leader re-aggregates the results across all workers in order to produce the final result. This is reflected in the plan as a Finalize Aggregate node.
Because the Finalize Aggregate node runs on the leader process, queries that produce a relatively large number of groups in comparison to the number of input rows will appear less favorable to the query planner. For example, in the worst-case scenario the number of groups seen by the Finalize Aggregate node could be as many as the number of input rows that were seen by all worker processes in the Partial Aggregate stage. For such cases, there is clearly going to be no performance benefit to using parallel aggregation. The query planner takes this into account during the planning process and is unlikely to choose parallel aggregate in this scenario.
Parallel aggregation is not supported in all situations. Each aggregate must be safe for parallelism and must have a combine function. If the aggregate has a transition state of type internal , it must have serialization and deserialization functions. See CREATE AGGREGATE for more details. Parallel aggregation is not supported if any aggregate function call contains DISTINCT or ORDER BY clause and is also not supported for ordered set aggregates or when the query involves GROUPING SETS . It can only be used when all joins involved in the query are also part of the parallel portion of the plan.
Executor implementation notes
ExecAgg normally evaluates each aggregate in the following steps:
transvalue = initcond foreach input_tuple do transvalue = transfunc(transvalue, input_value(s)) result = finalfunc(transvalue, direct_argument(s))
If a finalfunc is not supplied then the result is just the ending value of transvalue.
Other behaviors can be selected by the "aggsplit" mode, which exists to support partial aggregation. It is possible to: * Skip running the finalfunc, so that the output is always the final transvalue state. * Substitute the combinefunc for the transfunc, so that transvalue states (propagated up from a child partial-aggregation step) are merged rather than processing raw input rows. (The statements below about the transfunc apply equally to the combinefunc, when it's selected.) * Apply the serializefunc to the output values (this only makes sense when skipping the finalfunc, since the serializefunc works on the transvalue data type). * Apply the deserializefunc to the input values (this only makes sense when using the combinefunc, for similar reasons). It is the planner's responsibility to connect up Agg nodes using these alternate behaviors in a way that makes sense, with partial aggregation results being fed to nodes that expect them.
If a normal aggregate call specifies DISTINCT or ORDER BY, we sort the input tuples and eliminate duplicates (if required) before performing the above-depicted process. (However, we don't do that for ordered-set aggregates; their "ORDER BY" inputs are ordinary aggregate arguments so far as this module is concerned.) Note that partial aggregation is not supported in these cases, since we couldn't ensure global ordering or distinctness of the inputs.
EXPLAIN identity in core source
case T_Agg:
{
Agg *agg = (Agg *) plan;
sname = "Aggregate";
switch (agg->aggstrategy)
{
case AGG_PLAIN:
pname = "Aggregate";
strategy = "Plain";
break;
case AGG_SORTED:
pname = "GroupAggregate";
strategy = "Sorted";
break;
case AGG_HASHED:
pname = "HashAggregate";
strategy = "Hashed";
break;
case AGG_MIXED:
pname = "MixedAggregate";
strategy = "Mixed";
break;
default:
pname = "Aggregate ???";
strategy = "???";
break;
}
if (DO_AGGSPLIT_SKIPFINAL(agg->aggsplit))
{
partialmode = "Partial";
pname = psprintf("%s %s", partialmode, pname);
}
else if (DO_AGGSPLIT_COMBINE(agg->aggsplit))
{
partialmode = "Finalize";
pname = psprintf("%s %s", partialmode, pname);
}
else
partialmode = "Simple";
}
break;EXPLAIN labels in this source build
| Text-format label | Structured node identity |
|---|---|
| Aggregate | Aggregate |
| GroupAggregate | Aggregate |
| HashAggregate | Aggregate |
| MixedAggregate | Aggregate |
Related entries
Documentation and source
- src/backend/commands/explain.c:1531
- src/backend/executor/execProcnode.c:340
- src/backend/executor/nodeAgg.c
- src/include/nodes/plannodes.h
- PostgreSQL 18.6 · parallel-plans
Source build
- Version
- 18.6
- Build
- PostgreSQL 18.6 source archive
- Source fingerprint
555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f
Compare versions
PostgreSQL 17 → 18: unchanged.
Compares recorded interfaces and attributes. Source fingerprints and build metadata are excluded; an absent sample is not proof of the introduction or removal release.
Related entries
Export JSON · Back to Plan Nodes · Recorded in PostgreSQL 10 through 20; the first sample is not necessarily its introduction.