@@ -1524,8 +1524,8 @@ finalize_aggregate(AggState *aggstate,
15241524 {
15251525 int numFinalArgs = peragg -> numFinalArgs ;
15261526
1527- /* set up aggstate->curpertrans for AggGetAggref() */
1528- aggstate -> curpertrans = pertrans ;
1527+ /* set up aggstate->curperagg for AggGetAggref() */
1528+ aggstate -> curperagg = peragg ;
15291529
15301530 InitFunctionCallInfoData (fcinfo , & peragg -> finalfn ,
15311531 numFinalArgs ,
@@ -1558,7 +1558,7 @@ finalize_aggregate(AggState *aggstate,
15581558 * resultVal = FunctionCallInvoke (& fcinfo );
15591559 * resultIsNull = fcinfo .isnull ;
15601560 }
1561- aggstate -> curpertrans = NULL ;
1561+ aggstate -> curperagg = NULL ;
15621562 }
15631563 else
15641564 {
@@ -2708,6 +2708,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
27082708 aggstate -> current_set = 0 ;
27092709 aggstate -> peragg = NULL ;
27102710 aggstate -> pertrans = NULL ;
2711+ aggstate -> curperagg = NULL ;
27112712 aggstate -> curpertrans = NULL ;
27122713 aggstate -> input_done = false;
27132714 aggstate -> agg_done = false;
@@ -3056,27 +3057,29 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
30563057 *
30573058 * Scenarios:
30583059 *
3059- * 1. An aggregate function appears more than once in query:
3060+ * 1. Identical aggregate function calls appear in the query:
30603061 *
30613062 * SELECT SUM(x) FROM ... HAVING SUM(x) > 0
30623063 *
3063- * Since the aggregates are the identical, we only need to calculate
3064- * the calculate it once. Both aggregates will share the same 'aggno'
3065- * value.
3064+ * Since these aggregates are identical, we only need to calculate
3065+ * the value once. Both aggregates will share the same 'aggno' value.
30663066 *
30673067 * 2. Two different aggregate functions appear in the query, but the
3068- * aggregates have the same transition function and initial value, but
3069- * different final function :
3068+ * aggregates have the same arguments, transition functions and
3069+ * initial values (and, presumably, different final functions) :
30703070 *
3071- * SELECT SUM (x), AVG (x) FROM ...
3071+ * SELECT AVG (x), STDDEV (x) FROM ...
30723072 *
30733073 * In this case we must create a new peragg for the varying aggregate,
3074- * and need to call the final functions separately, but can share the
3075- * same transition state.
3074+ * and we need to call the final functions separately, but we need
3075+ * only run the transition function once. (This requires that the
3076+ * final functions be nondestructive of the transition state, but
3077+ * that's required anyway for other reasons.)
30763078 *
3077- * For either of these optimizations to be valid, the aggregate's
3078- * arguments must be the same, including any modifiers such as ORDER BY,
3079- * DISTINCT and FILTER, and they mustn't contain any volatile functions.
3079+ * For either of these optimizations to be valid, all aggregate properties
3080+ * used in the transition phase must be the same, including any modifiers
3081+ * such as ORDER BY, DISTINCT and FILTER, and the arguments mustn't
3082+ * contain any volatile functions.
30803083 * -----------------
30813084 */
30823085 aggno = -1 ;
@@ -3719,7 +3722,7 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
37193722 *
37203723 * As a side-effect, this also collects a list of existing per-Trans structs
37213724 * with matching inputs. If no identical Aggref is found, the list is passed
3722- * later to find_compatible_perstate , to see if we can at least reuse the
3725+ * later to find_compatible_pertrans , to see if we can at least reuse the
37233726 * state value of another aggregate.
37243727 */
37253728static int
@@ -3739,11 +3742,12 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
37393742
37403743 /*
37413744 * Search through the list of already seen aggregates. If we find an
3742- * existing aggregate with the same aggregate function and input
3743- * parameters as an existing one, then we can re-use that one. While
3745+ * existing identical aggregate call, then we can re-use that one. While
37443746 * searching, we'll also collect a list of Aggrefs with the same input
37453747 * parameters. If no matching Aggref is found, the caller can potentially
3746- * still re-use the transition state of one of them.
3748+ * still re-use the transition state of one of them. (At this stage we
3749+ * just compare the parsetrees; whether different aggregates share the
3750+ * same transition function will be checked later.)
37473751 */
37483752 for (aggno = 0 ; aggno <= lastaggno ; aggno ++ )
37493753 {
@@ -3792,7 +3796,7 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
37923796 * struct
37933797 *
37943798 * Searches the list of transnos for a per-Trans struct with the same
3795- * transition state and initial condition. (The inputs have already been
3799+ * transition function and initial condition. (The inputs have already been
37963800 * verified to match.)
37973801 */
37983802static int
@@ -3838,16 +3842,16 @@ find_compatible_pertrans(AggState *aggstate, Aggref *newagg,
38383842 aggdeserialfn != pertrans -> deserialfn_oid )
38393843 continue ;
38403844
3841- /* Check that the initial condition matches, too. */
3845+ /*
3846+ * Check that the initial condition matches, too.
3847+ */
38423848 if (initValueIsNull && pertrans -> initValueIsNull )
38433849 return transno ;
38443850
38453851 if (!initValueIsNull && !pertrans -> initValueIsNull &&
38463852 datumIsEqual (initValue , pertrans -> initValue ,
38473853 pertrans -> transtypeByVal , pertrans -> transtypeLen ))
3848- {
38493854 return transno ;
3850- }
38513855 }
38523856 return -1 ;
38533857}
@@ -4066,6 +4070,13 @@ AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
40664070 * If the function is being called as an aggregate support function,
40674071 * return the Aggref node for the aggregate call. Otherwise, return NULL.
40684072 *
4073+ * Aggregates sharing the same inputs and transition functions can get
4074+ * merged into a single transition calculation. If the transition function
4075+ * calls AggGetAggref, it will get some one of the Aggrefs for which it is
4076+ * executing. It must therefore not pay attention to the Aggref fields that
4077+ * relate to the final function, as those are indeterminate. But if a final
4078+ * function calls AggGetAggref, it will get a precise result.
4079+ *
40694080 * Note that if an aggregate is being used as a window function, this will
40704081 * return NULL. We could provide a similar function to return the relevant
40714082 * WindowFunc node in such cases, but it's not needed yet.
@@ -4075,8 +4086,16 @@ AggGetAggref(FunctionCallInfo fcinfo)
40754086{
40764087 if (fcinfo -> context && IsA (fcinfo -> context , AggState ))
40774088 {
4089+ AggStatePerAgg curperagg ;
40784090 AggStatePerTrans curpertrans ;
40794091
4092+ /* check curperagg (valid when in a final function) */
4093+ curperagg = ((AggState * ) fcinfo -> context )-> curperagg ;
4094+
4095+ if (curperagg )
4096+ return curperagg -> aggref ;
4097+
4098+ /* check curpertrans (valid when in a transition function) */
40804099 curpertrans = ((AggState * ) fcinfo -> context )-> curpertrans ;
40814100
40824101 if (curpertrans )
0 commit comments