@@ -1524,8 +1524,8 @@ finalize_aggregate(AggState *aggstate,
1524
1524
{
1525
1525
int numFinalArgs = peragg -> numFinalArgs ;
1526
1526
1527
- /* set up aggstate->curpertrans for AggGetAggref() */
1528
- aggstate -> curpertrans = pertrans ;
1527
+ /* set up aggstate->curperagg for AggGetAggref() */
1528
+ aggstate -> curperagg = peragg ;
1529
1529
1530
1530
InitFunctionCallInfoData (fcinfo , & peragg -> finalfn ,
1531
1531
numFinalArgs ,
@@ -1558,7 +1558,7 @@ finalize_aggregate(AggState *aggstate,
1558
1558
* resultVal = FunctionCallInvoke (& fcinfo );
1559
1559
* resultIsNull = fcinfo .isnull ;
1560
1560
}
1561
- aggstate -> curpertrans = NULL ;
1561
+ aggstate -> curperagg = NULL ;
1562
1562
}
1563
1563
else
1564
1564
{
@@ -2708,6 +2708,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
2708
2708
aggstate -> current_set = 0 ;
2709
2709
aggstate -> peragg = NULL ;
2710
2710
aggstate -> pertrans = NULL ;
2711
+ aggstate -> curperagg = NULL ;
2711
2712
aggstate -> curpertrans = NULL ;
2712
2713
aggstate -> input_done = false;
2713
2714
aggstate -> agg_done = false;
@@ -3056,27 +3057,29 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
3056
3057
*
3057
3058
* Scenarios:
3058
3059
*
3059
- * 1. An aggregate function appears more than once in query:
3060
+ * 1. Identical aggregate function calls appear in the query:
3060
3061
*
3061
3062
* SELECT SUM(x) FROM ... HAVING SUM(x) > 0
3062
3063
*
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.
3066
3066
*
3067
3067
* 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) :
3070
3070
*
3071
- * SELECT SUM (x), AVG (x) FROM ...
3071
+ * SELECT AVG (x), STDDEV (x) FROM ...
3072
3072
*
3073
3073
* 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.)
3076
3078
*
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.
3080
3083
* -----------------
3081
3084
*/
3082
3085
aggno = -1 ;
@@ -3719,7 +3722,7 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
3719
3722
*
3720
3723
* As a side-effect, this also collects a list of existing per-Trans structs
3721
3724
* 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
3723
3726
* state value of another aggregate.
3724
3727
*/
3725
3728
static int
@@ -3739,11 +3742,12 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
3739
3742
3740
3743
/*
3741
3744
* 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
3744
3746
* searching, we'll also collect a list of Aggrefs with the same input
3745
3747
* 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.)
3747
3751
*/
3748
3752
for (aggno = 0 ; aggno <= lastaggno ; aggno ++ )
3749
3753
{
@@ -3792,7 +3796,7 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
3792
3796
* struct
3793
3797
*
3794
3798
* 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
3796
3800
* verified to match.)
3797
3801
*/
3798
3802
static int
@@ -3838,16 +3842,16 @@ find_compatible_pertrans(AggState *aggstate, Aggref *newagg,
3838
3842
aggdeserialfn != pertrans -> deserialfn_oid )
3839
3843
continue ;
3840
3844
3841
- /* Check that the initial condition matches, too. */
3845
+ /*
3846
+ * Check that the initial condition matches, too.
3847
+ */
3842
3848
if (initValueIsNull && pertrans -> initValueIsNull )
3843
3849
return transno ;
3844
3850
3845
3851
if (!initValueIsNull && !pertrans -> initValueIsNull &&
3846
3852
datumIsEqual (initValue , pertrans -> initValue ,
3847
3853
pertrans -> transtypeByVal , pertrans -> transtypeLen ))
3848
- {
3849
3854
return transno ;
3850
- }
3851
3855
}
3852
3856
return -1 ;
3853
3857
}
@@ -4066,6 +4070,13 @@ AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
4066
4070
* If the function is being called as an aggregate support function,
4067
4071
* return the Aggref node for the aggregate call. Otherwise, return NULL.
4068
4072
*
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
+ *
4069
4080
* Note that if an aggregate is being used as a window function, this will
4070
4081
* return NULL. We could provide a similar function to return the relevant
4071
4082
* WindowFunc node in such cases, but it's not needed yet.
@@ -4075,8 +4086,16 @@ AggGetAggref(FunctionCallInfo fcinfo)
4075
4086
{
4076
4087
if (fcinfo -> context && IsA (fcinfo -> context , AggState ))
4077
4088
{
4089
+ AggStatePerAgg curperagg ;
4078
4090
AggStatePerTrans curpertrans ;
4079
4091
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) */
4080
4099
curpertrans = ((AggState * ) fcinfo -> context )-> curpertrans ;
4081
4100
4082
4101
if (curpertrans )
0 commit comments