@@ -1528,8 +1528,8 @@ finalize_aggregate(AggState *aggstate,
1528
1528
{
1529
1529
int numFinalArgs = peragg -> numFinalArgs ;
1530
1530
1531
- /* set up aggstate->curpertrans for AggGetAggref() */
1532
- aggstate -> curpertrans = pertrans ;
1531
+ /* set up aggstate->curperagg for AggGetAggref() */
1532
+ aggstate -> curperagg = peragg ;
1533
1533
1534
1534
InitFunctionCallInfoData (fcinfo , & peragg -> finalfn ,
1535
1535
numFinalArgs ,
@@ -1562,7 +1562,7 @@ finalize_aggregate(AggState *aggstate,
1562
1562
* resultVal = FunctionCallInvoke (& fcinfo );
1563
1563
* resultIsNull = fcinfo .isnull ;
1564
1564
}
1565
- aggstate -> curpertrans = NULL ;
1565
+ aggstate -> curperagg = NULL ;
1566
1566
}
1567
1567
else
1568
1568
{
@@ -2712,6 +2712,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
2712
2712
aggstate -> current_set = 0 ;
2713
2713
aggstate -> peragg = NULL ;
2714
2714
aggstate -> pertrans = NULL ;
2715
+ aggstate -> curperagg = NULL ;
2715
2716
aggstate -> curpertrans = NULL ;
2716
2717
aggstate -> input_done = false;
2717
2718
aggstate -> agg_done = false;
@@ -3060,27 +3061,29 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
3060
3061
*
3061
3062
* Scenarios:
3062
3063
*
3063
- * 1. An aggregate function appears more than once in query:
3064
+ * 1. Identical aggregate function calls appear in the query:
3064
3065
*
3065
3066
* SELECT SUM(x) FROM ... HAVING SUM(x) > 0
3066
3067
*
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.
3070
3070
*
3071
3071
* 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) :
3074
3074
*
3075
- * SELECT SUM (x), AVG (x) FROM ...
3075
+ * SELECT AVG (x), STDDEV (x) FROM ...
3076
3076
*
3077
3077
* 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.)
3080
3082
*
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.
3084
3087
* -----------------
3085
3088
*/
3086
3089
aggno = -1 ;
@@ -3723,7 +3726,7 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
3723
3726
*
3724
3727
* As a side-effect, this also collects a list of existing per-Trans structs
3725
3728
* 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
3727
3730
* state value of another aggregate.
3728
3731
*/
3729
3732
static int
@@ -3743,11 +3746,12 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
3743
3746
3744
3747
/*
3745
3748
* 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
3748
3750
* searching, we'll also collect a list of Aggrefs with the same input
3749
3751
* 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.)
3751
3755
*/
3752
3756
for (aggno = 0 ; aggno <= lastaggno ; aggno ++ )
3753
3757
{
@@ -3796,7 +3800,7 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
3796
3800
* struct
3797
3801
*
3798
3802
* 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
3800
3804
* verified to match.)
3801
3805
*/
3802
3806
static int
@@ -3842,16 +3846,16 @@ find_compatible_pertrans(AggState *aggstate, Aggref *newagg,
3842
3846
aggdeserialfn != pertrans -> deserialfn_oid )
3843
3847
continue ;
3844
3848
3845
- /* Check that the initial condition matches, too. */
3849
+ /*
3850
+ * Check that the initial condition matches, too.
3851
+ */
3846
3852
if (initValueIsNull && pertrans -> initValueIsNull )
3847
3853
return transno ;
3848
3854
3849
3855
if (!initValueIsNull && !pertrans -> initValueIsNull &&
3850
3856
datumIsEqual (initValue , pertrans -> initValue ,
3851
3857
pertrans -> transtypeByVal , pertrans -> transtypeLen ))
3852
- {
3853
3858
return transno ;
3854
- }
3855
3859
}
3856
3860
return -1 ;
3857
3861
}
@@ -4070,6 +4074,13 @@ AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
4070
4074
* If the function is being called as an aggregate support function,
4071
4075
* return the Aggref node for the aggregate call. Otherwise, return NULL.
4072
4076
*
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
+ *
4073
4084
* Note that if an aggregate is being used as a window function, this will
4074
4085
* return NULL. We could provide a similar function to return the relevant
4075
4086
* WindowFunc node in such cases, but it's not needed yet.
@@ -4079,8 +4090,16 @@ AggGetAggref(FunctionCallInfo fcinfo)
4079
4090
{
4080
4091
if (fcinfo -> context && IsA (fcinfo -> context , AggState ))
4081
4092
{
4093
+ AggStatePerAgg curperagg ;
4082
4094
AggStatePerTrans curpertrans ;
4083
4095
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) */
4084
4103
curpertrans = ((AggState * ) fcinfo -> context )-> curpertrans ;
4085
4104
4086
4105
if (curpertrans )
0 commit comments