@@ -1372,8 +1372,8 @@ finalize_aggregate(AggState *aggstate,
1372
1372
{
1373
1373
int numFinalArgs = peragg -> numFinalArgs ;
1374
1374
1375
- /* set up aggstate->curpertrans for AggGetAggref() */
1376
- aggstate -> curpertrans = pertrans ;
1375
+ /* set up aggstate->curperagg for AggGetAggref() */
1376
+ aggstate -> curperagg = peragg ;
1377
1377
1378
1378
InitFunctionCallInfoData (fcinfo , & peragg -> finalfn ,
1379
1379
numFinalArgs ,
@@ -1406,7 +1406,7 @@ finalize_aggregate(AggState *aggstate,
1406
1406
* resultVal = FunctionCallInvoke (& fcinfo );
1407
1407
* resultIsNull = fcinfo .isnull ;
1408
1408
}
1409
- aggstate -> curpertrans = NULL ;
1409
+ aggstate -> curperagg = NULL ;
1410
1410
}
1411
1411
else
1412
1412
{
@@ -2391,6 +2391,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
2391
2391
aggstate -> current_set = 0 ;
2392
2392
aggstate -> peragg = NULL ;
2393
2393
aggstate -> pertrans = NULL ;
2394
+ aggstate -> curperagg = NULL ;
2394
2395
aggstate -> curpertrans = NULL ;
2395
2396
aggstate -> input_done = false;
2396
2397
aggstate -> agg_done = false;
@@ -2659,27 +2660,29 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
2659
2660
*
2660
2661
* Scenarios:
2661
2662
*
2662
- * 1. An aggregate function appears more than once in query:
2663
+ * 1. Identical aggregate function calls appear in the query:
2663
2664
*
2664
2665
* SELECT SUM(x) FROM ... HAVING SUM(x) > 0
2665
2666
*
2666
- * Since the aggregates are the identical, we only need to calculate
2667
- * the calculate it once. Both aggregates will share the same 'aggno'
2668
- * value.
2667
+ * Since these aggregates are identical, we only need to calculate
2668
+ * the value once. Both aggregates will share the same 'aggno' value.
2669
2669
*
2670
2670
* 2. Two different aggregate functions appear in the query, but the
2671
- * aggregates have the same transition function and initial value, but
2672
- * different final function :
2671
+ * aggregates have the same arguments, transition functions and
2672
+ * initial values (and, presumably, different final functions) :
2673
2673
*
2674
- * SELECT SUM (x), AVG (x) FROM ...
2674
+ * SELECT AVG (x), STDDEV (x) FROM ...
2675
2675
*
2676
2676
* In this case we must create a new peragg for the varying aggregate,
2677
- * and need to call the final functions separately, but can share the
2678
- * same transition state.
2677
+ * and we need to call the final functions separately, but we need
2678
+ * only run the transition function once. (This requires that the
2679
+ * final functions be nondestructive of the transition state, but
2680
+ * that's required anyway for other reasons.)
2679
2681
*
2680
- * For either of these optimizations to be valid, the aggregate's
2681
- * arguments must be the same, including any modifiers such as ORDER BY,
2682
- * DISTINCT and FILTER, and they mustn't contain any volatile functions.
2682
+ * For either of these optimizations to be valid, all aggregate properties
2683
+ * used in the transition phase must be the same, including any modifiers
2684
+ * such as ORDER BY, DISTINCT and FILTER, and the arguments mustn't
2685
+ * contain any volatile functions.
2683
2686
* -----------------
2684
2687
*/
2685
2688
aggno = -1 ;
@@ -3287,7 +3290,7 @@ GetAggInitVal(Datum textInitVal, Oid transtype)
3287
3290
*
3288
3291
* As a side-effect, this also collects a list of existing per-Trans structs
3289
3292
* with matching inputs. If no identical Aggref is found, the list is passed
3290
- * later to find_compatible_perstate , to see if we can at least reuse the
3293
+ * later to find_compatible_pertrans , to see if we can at least reuse the
3291
3294
* state value of another aggregate.
3292
3295
*/
3293
3296
static int
@@ -3307,11 +3310,12 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
3307
3310
3308
3311
/*
3309
3312
* Search through the list of already seen aggregates. If we find an
3310
- * existing aggregate with the same aggregate function and input
3311
- * parameters as an existing one, then we can re-use that one. While
3313
+ * existing identical aggregate call, then we can re-use that one. While
3312
3314
* searching, we'll also collect a list of Aggrefs with the same input
3313
3315
* parameters. If no matching Aggref is found, the caller can potentially
3314
- * still re-use the transition state of one of them.
3316
+ * still re-use the transition state of one of them. (At this stage we
3317
+ * just compare the parsetrees; whether different aggregates share the
3318
+ * same transition function will be checked later.)
3315
3319
*/
3316
3320
for (aggno = 0 ; aggno <= lastaggno ; aggno ++ )
3317
3321
{
@@ -3360,7 +3364,7 @@ find_compatible_peragg(Aggref *newagg, AggState *aggstate,
3360
3364
* struct
3361
3365
*
3362
3366
* Searches the list of transnos for a per-Trans struct with the same
3363
- * transition state and initial condition. (The inputs have already been
3367
+ * transition function and initial condition. (The inputs have already been
3364
3368
* verified to match.)
3365
3369
*/
3366
3370
static int
@@ -3406,16 +3410,16 @@ find_compatible_pertrans(AggState *aggstate, Aggref *newagg,
3406
3410
aggdeserialfn != pertrans -> deserialfn_oid )
3407
3411
continue ;
3408
3412
3409
- /* Check that the initial condition matches, too. */
3413
+ /*
3414
+ * Check that the initial condition matches, too.
3415
+ */
3410
3416
if (initValueIsNull && pertrans -> initValueIsNull )
3411
3417
return transno ;
3412
3418
3413
3419
if (!initValueIsNull && !pertrans -> initValueIsNull &&
3414
3420
datumIsEqual (initValue , pertrans -> initValue ,
3415
3421
pertrans -> transtypeByVal , pertrans -> transtypeLen ))
3416
- {
3417
3422
return transno ;
3418
- }
3419
3423
}
3420
3424
return -1 ;
3421
3425
}
@@ -3628,6 +3632,13 @@ AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
3628
3632
* If the function is being called as an aggregate support function,
3629
3633
* return the Aggref node for the aggregate call. Otherwise, return NULL.
3630
3634
*
3635
+ * Aggregates sharing the same inputs and transition functions can get
3636
+ * merged into a single transition calculation. If the transition function
3637
+ * calls AggGetAggref, it will get some one of the Aggrefs for which it is
3638
+ * executing. It must therefore not pay attention to the Aggref fields that
3639
+ * relate to the final function, as those are indeterminate. But if a final
3640
+ * function calls AggGetAggref, it will get a precise result.
3641
+ *
3631
3642
* Note that if an aggregate is being used as a window function, this will
3632
3643
* return NULL. We could provide a similar function to return the relevant
3633
3644
* WindowFunc node in such cases, but it's not needed yet.
@@ -3637,8 +3648,16 @@ AggGetAggref(FunctionCallInfo fcinfo)
3637
3648
{
3638
3649
if (fcinfo -> context && IsA (fcinfo -> context , AggState ))
3639
3650
{
3651
+ AggStatePerAgg curperagg ;
3640
3652
AggStatePerTrans curpertrans ;
3641
3653
3654
+ /* check curperagg (valid when in a final function) */
3655
+ curperagg = ((AggState * ) fcinfo -> context )-> curperagg ;
3656
+
3657
+ if (curperagg )
3658
+ return curperagg -> aggref ;
3659
+
3660
+ /* check curpertrans (valid when in a transition function) */
3642
3661
curpertrans = ((AggState * ) fcinfo -> context )-> curpertrans ;
3643
3662
3644
3663
if (curpertrans )
0 commit comments