@@ -1528,8 +1528,8 @@ finalize_aggregate(AggState *aggstate,
15281528 {
15291529 int numFinalArgs = peragg -> numFinalArgs ;
15301530
1531- /* set up aggstate->curpertrans for AggGetAggref() */
1532- aggstate -> curpertrans = pertrans ;
1531+ /* set up aggstate->curperagg for AggGetAggref() */
1532+ aggstate -> curperagg = peragg ;
15331533
15341534 InitFunctionCallInfoData (fcinfo , & peragg -> finalfn ,
15351535 numFinalArgs ,
@@ -1562,7 +1562,7 @@ finalize_aggregate(AggState *aggstate,
15621562 * resultVal = FunctionCallInvoke (& fcinfo );
15631563 * resultIsNull = fcinfo .isnull ;
15641564 }
1565- aggstate -> curpertrans = NULL ;
1565+ aggstate -> curperagg = NULL ;
15661566 }
15671567 else
15681568 {
@@ -2712,6 +2712,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
27122712 aggstate -> current_set = 0 ;
27132713 aggstate -> peragg = NULL ;
27142714 aggstate -> pertrans = NULL ;
2715+ aggstate -> curperagg = NULL ;
27152716 aggstate -> curpertrans = NULL ;
27162717 aggstate -> input_done = false;
27172718 aggstate -> agg_done = false;
@@ -3060,27 +3061,29 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
30603061 *
30613062 * Scenarios:
30623063 *
3063- * 1. An aggregate function appears more than once in query:
3064+ * 1. Identical aggregate function calls appear in the query:
30643065 *
30653066 * SELECT SUM(x) FROM ... HAVING SUM(x) > 0
30663067 *
3067- * Since the aggregates are the identical, we only need to calculate
3068- * the calculate it once. Both aggregates will share the same 'aggno'
3069- * value.
3068+ * Since these aggregates are identical, we only need to calculate
3069+ * the value once. Both aggregates will share the same 'aggno' value.
30703070 *
30713071 * 2. Two different aggregate functions appear in the query, but the
3072- * aggregates have the same transition function and initial value, but
3073- * different final function :
3072+ * aggregates have the same arguments, transition functions and
3073+ * initial values (and, presumably, different final functions) :
30743074 *
3075- * SELECT SUM (x), AVG (x) FROM ...
3075+ * SELECT AVG (x), STDDEV (x) FROM ...
30763076 *
30773077 * In this case we must create a new peragg for the varying aggregate,
3078- * and need to call the final functions separately, but can share the
3079- * same transition state.
3078+ * and we need to call the final functions separately, but we need
3079+ * only run the transition function once. (This requires that the
3080+ * final functions be nondestructive of the transition state, but
3081+ * that's required anyway for other reasons.)
30803082 *
3081- * For either of these optimizations to be valid, the aggregate's
3082- * arguments must be the same, including any modifiers such as ORDER BY,
3083- * DISTINCT and FILTER, and they mustn't contain any volatile functions.
3083+ * For either of these optimizations to be valid, all aggregate properties
3084+ * used in the transition phase must be the same, including any modifiers
3085+ * such as ORDER BY, DISTINCT and FILTER, and the arguments mustn't
3086+ * contain any volatile functions.
30843087 * -----------------
30853088 */
30863089 aggno = -1 ;
@@ -3723,7 +3726,7 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
37233726 *
37243727 * As a side-effect, this also collects a list of existing per-Trans structs
37253728 * with matching inputs. If no identical Aggref is found, the list is passed
3726- * later to find_compatible_perstate , to see if we can at least reuse the
3729+ * later to find_compatible_pertrans , to see if we can at least reuse the
37273730 * state value of another aggregate.
37283731 */
37293732static int
@@ -3743,11 +3746,12 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
37433746
37443747 /*
37453748 * Search through the list of already seen aggregates. If we find an
3746- * existing aggregate with the same aggregate function and input
3747- * parameters as an existing one, then we can re-use that one. While
3749+ * existing identical aggregate call, then we can re-use that one. While
37483750 * searching, we'll also collect a list of Aggrefs with the same input
37493751 * parameters. If no matching Aggref is found, the caller can potentially
3750- * still re-use the transition state of one of them.
3752+ * still re-use the transition state of one of them. (At this stage we
3753+ * just compare the parsetrees; whether different aggregates share the
3754+ * same transition function will be checked later.)
37513755 */
37523756 for (aggno = 0 ; aggno <= lastaggno ; aggno ++ )
37533757 {
@@ -3796,7 +3800,7 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
37963800 * struct
37973801 *
37983802 * Searches the list of transnos for a per-Trans struct with the same
3799- * transition state and initial condition. (The inputs have already been
3803+ * transition function and initial condition. (The inputs have already been
38003804 * verified to match.)
38013805 */
38023806static int
@@ -3842,16 +3846,16 @@ find_compatible_pertrans(AggState *aggstate, Aggref *newagg,
38423846 aggdeserialfn != pertrans -> deserialfn_oid )
38433847 continue ;
38443848
3845- /* Check that the initial condition matches, too. */
3849+ /*
3850+ * Check that the initial condition matches, too.
3851+ */
38463852 if (initValueIsNull && pertrans -> initValueIsNull )
38473853 return transno ;
38483854
38493855 if (!initValueIsNull && !pertrans -> initValueIsNull &&
38503856 datumIsEqual (initValue , pertrans -> initValue ,
38513857 pertrans -> transtypeByVal , pertrans -> transtypeLen ))
3852- {
38533858 return transno ;
3854- }
38553859 }
38563860 return -1 ;
38573861}
@@ -4070,6 +4074,13 @@ AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
40704074 * If the function is being called as an aggregate support function,
40714075 * return the Aggref node for the aggregate call. Otherwise, return NULL.
40724076 *
4077+ * Aggregates sharing the same inputs and transition functions can get
4078+ * merged into a single transition calculation. If the transition function
4079+ * calls AggGetAggref, it will get some one of the Aggrefs for which it is
4080+ * executing. It must therefore not pay attention to the Aggref fields that
4081+ * relate to the final function, as those are indeterminate. But if a final
4082+ * function calls AggGetAggref, it will get a precise result.
4083+ *
40734084 * Note that if an aggregate is being used as a window function, this will
40744085 * return NULL. We could provide a similar function to return the relevant
40754086 * WindowFunc node in such cases, but it's not needed yet.
@@ -4079,8 +4090,16 @@ AggGetAggref(FunctionCallInfo fcinfo)
40794090{
40804091 if (fcinfo -> context && IsA (fcinfo -> context , AggState ))
40814092 {
4093+ AggStatePerAgg curperagg ;
40824094 AggStatePerTrans curpertrans ;
40834095
4096+ /* check curperagg (valid when in a final function) */
4097+ curperagg = ((AggState * ) fcinfo -> context )-> curperagg ;
4098+
4099+ if (curperagg )
4100+ return curperagg -> aggref ;
4101+
4102+ /* check curpertrans (valid when in a transition function) */
40844103 curpertrans = ((AggState * ) fcinfo -> context )-> curpertrans ;
40854104
40864105 if (curpertrans )
0 commit comments