LCOV - code coverage report
Current view: top level - src/backend/optimizer/prep - prepagg.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 97.8 % 184 180
Test Date: 2026-03-02 10:14:48 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * prepagg.c
       4              :  *    Routines to preprocess aggregate function calls
       5              :  *
       6              :  * If there are identical aggregate calls in the query, they only need to
       7              :  * be computed once.  Also, some aggregate functions can share the same
       8              :  * transition state, so that we only need to call the final function for
       9              :  * them separately.  These optimizations are independent of how the
      10              :  * aggregates are executed.
      11              :  *
      12              :  * preprocess_aggrefs() detects those cases, creates AggInfo and
      13              :  * AggTransInfo structs for each aggregate and transition state that needs
      14              :  * to be computed, and sets the 'aggno' and 'transno' fields in the Aggrefs
      15              :  * accordingly.  It also resolves polymorphic transition types, and sets
      16              :  * the 'aggtranstype' fields accordingly.
      17              :  *
      18              :  * XXX: The AggInfo and AggTransInfo structs are thrown away after
      19              :  * planning, so executor startup has to perform some of the same lookups
      20              :  * of transition functions and initial values that we do here.  One day, we
      21              :  * might want to carry that information to the Agg nodes to save the effort
      22              :  * at executor startup.  The Agg nodes are constructed much later in the
      23              :  * planning, however, so it's not trivial.
      24              :  *
      25              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      26              :  * Portions Copyright (c) 1994, Regents of the University of California
      27              :  *
      28              :  *
      29              :  * IDENTIFICATION
      30              :  *    src/backend/optimizer/prep/prepagg.c
      31              :  *
      32              :  *-------------------------------------------------------------------------
      33              :  */
      34              : 
      35              : #include "postgres.h"
      36              : 
      37              : #include "access/htup_details.h"
      38              : #include "catalog/pg_aggregate.h"
      39              : #include "catalog/pg_type.h"
      40              : #include "nodes/nodeFuncs.h"
      41              : #include "nodes/pathnodes.h"
      42              : #include "optimizer/cost.h"
      43              : #include "optimizer/optimizer.h"
      44              : #include "optimizer/plancat.h"
      45              : #include "optimizer/prep.h"
      46              : #include "parser/parse_agg.h"
      47              : #include "utils/builtins.h"
      48              : #include "utils/datum.h"
      49              : #include "utils/fmgroids.h"
      50              : #include "utils/lsyscache.h"
      51              : #include "utils/memutils.h"
      52              : #include "utils/syscache.h"
      53              : 
      54              : static bool preprocess_aggrefs_walker(Node *node, PlannerInfo *root);
      55              : static int  find_compatible_agg(PlannerInfo *root, Aggref *newagg,
      56              :                                 List **same_input_transnos);
      57              : static int  find_compatible_trans(PlannerInfo *root, Aggref *newagg,
      58              :                                   bool shareable,
      59              :                                   Oid aggtransfn, Oid aggtranstype,
      60              :                                   int transtypeLen, bool transtypeByVal,
      61              :                                   Oid aggcombinefn,
      62              :                                   Oid aggserialfn, Oid aggdeserialfn,
      63              :                                   Datum initValue, bool initValueIsNull,
      64              :                                   List *transnos);
      65              : static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
      66              : 
      67              : /* -----------------
      68              :  * Resolve the transition type of all Aggrefs, and determine which Aggrefs
      69              :  * can share aggregate or transition state.
      70              :  *
      71              :  * Information about the aggregates and transition functions are collected
      72              :  * in the root->agginfos and root->aggtransinfos lists.  The 'aggtranstype',
      73              :  * 'aggno', and 'aggtransno' fields of each Aggref are filled in.
      74              :  *
      75              :  * NOTE: This modifies the Aggrefs in the input expression in-place!
      76              :  *
      77              :  * We try to optimize by detecting duplicate aggregate functions so that
      78              :  * their state and final values are re-used, rather than needlessly being
      79              :  * re-calculated independently.  We also detect aggregates that are not
      80              :  * the same, but which can share the same transition state.
      81              :  *
      82              :  * Scenarios:
      83              :  *
      84              :  * 1. Identical aggregate function calls appear in the query:
      85              :  *
      86              :  *    SELECT SUM(x) FROM ... HAVING SUM(x) > 0
      87              :  *
      88              :  *    Since these aggregates are identical, we only need to calculate
      89              :  *    the value once.  Both aggregates will share the same 'aggno' value.
      90              :  *
      91              :  * 2. Two different aggregate functions appear in the query, but the
      92              :  *    aggregates have the same arguments, transition functions and
      93              :  *    initial values (and, presumably, different final functions):
      94              :  *
      95              :  *    SELECT AVG(x), STDDEV(x) FROM ...
      96              :  *
      97              :  *    In this case we must create a new AggInfo for the varying aggregate,
      98              :  *    and we need to call the final functions separately, but we need
      99              :  *    only run the transition function once.  (This requires that the
     100              :  *    final functions be nondestructive of the transition state, but
     101              :  *    that's required anyway for other reasons.)
     102              :  *
     103              :  * For either of these optimizations to be valid, all aggregate properties
     104              :  * used in the transition phase must be the same, including any modifiers
     105              :  * such as ORDER BY, DISTINCT and FILTER, and the arguments mustn't
     106              :  * contain any volatile functions.
     107              :  * -----------------
     108              :  */
     109              : void
     110        45752 : preprocess_aggrefs(PlannerInfo *root, Node *clause)
     111              : {
     112        45752 :     (void) preprocess_aggrefs_walker(clause, root);
     113        45752 : }
     114              : 
     115              : static void
     116        26155 : preprocess_aggref(Aggref *aggref, PlannerInfo *root)
     117              : {
     118              :     HeapTuple   aggTuple;
     119              :     Form_pg_aggregate aggform;
     120              :     Oid         aggtransfn;
     121              :     Oid         aggfinalfn;
     122              :     Oid         aggcombinefn;
     123              :     Oid         aggserialfn;
     124              :     Oid         aggdeserialfn;
     125              :     Oid         aggtranstype;
     126              :     int32       aggtranstypmod;
     127              :     int32       aggtransspace;
     128              :     bool        shareable;
     129              :     int         aggno;
     130              :     int         transno;
     131              :     List       *same_input_transnos;
     132              :     int16       resulttypeLen;
     133              :     bool        resulttypeByVal;
     134              :     Datum       textInitVal;
     135              :     Datum       initValue;
     136              :     bool        initValueIsNull;
     137              :     bool        transtypeByVal;
     138              :     int16       transtypeLen;
     139              :     Oid         inputTypes[FUNC_MAX_ARGS];
     140              :     int         numArguments;
     141              : 
     142              :     Assert(aggref->agglevelsup == 0);
     143              : 
     144              :     /*
     145              :      * Fetch info about the aggregate from pg_aggregate.  Note it's correct to
     146              :      * ignore the moving-aggregate variant, since what we're concerned with
     147              :      * here is aggregates not window functions.
     148              :      */
     149        26155 :     aggTuple = SearchSysCache1(AGGFNOID,
     150              :                                ObjectIdGetDatum(aggref->aggfnoid));
     151        26155 :     if (!HeapTupleIsValid(aggTuple))
     152            0 :         elog(ERROR, "cache lookup failed for aggregate %u",
     153              :              aggref->aggfnoid);
     154        26155 :     aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
     155        26155 :     aggtransfn = aggform->aggtransfn;
     156        26155 :     aggfinalfn = aggform->aggfinalfn;
     157        26155 :     aggcombinefn = aggform->aggcombinefn;
     158        26155 :     aggserialfn = aggform->aggserialfn;
     159        26155 :     aggdeserialfn = aggform->aggdeserialfn;
     160        26155 :     aggtranstype = aggform->aggtranstype;
     161        26155 :     aggtransspace = aggform->aggtransspace;
     162              : 
     163              :     /*
     164              :      * Resolve the possibly-polymorphic aggregate transition type.
     165              :      */
     166              : 
     167              :     /* extract argument types (ignoring any ORDER BY expressions) */
     168        26155 :     numArguments = get_aggregate_argtypes(aggref, inputTypes);
     169              : 
     170              :     /* resolve actual type of transition state, if polymorphic */
     171        26155 :     aggtranstype = resolve_aggregate_transtype(aggref->aggfnoid,
     172              :                                                aggtranstype,
     173              :                                                inputTypes,
     174              :                                                numArguments);
     175        26155 :     aggref->aggtranstype = aggtranstype;
     176              : 
     177              :     /*
     178              :      * If transition state is of same type as first aggregated input, assume
     179              :      * it's the same typmod (same width) as well.  This works for cases like
     180              :      * MAX/MIN and is probably somewhat reasonable otherwise.
     181              :      */
     182        26155 :     aggtranstypmod = -1;
     183        26155 :     if (aggref->args)
     184              :     {
     185        16321 :         TargetEntry *tle = (TargetEntry *) linitial(aggref->args);
     186              : 
     187        16321 :         if (aggtranstype == exprType((Node *) tle->expr))
     188         1696 :             aggtranstypmod = exprTypmod((Node *) tle->expr);
     189              :     }
     190              : 
     191              :     /*
     192              :      * If finalfn is marked read-write, we can't share transition states; but
     193              :      * it is okay to share states for AGGMODIFY_SHAREABLE aggs.
     194              :      *
     195              :      * In principle, in a partial aggregate, we could share the transition
     196              :      * state even if the final function is marked as read-write, because the
     197              :      * partial aggregate doesn't execute the final function.  But it's too
     198              :      * early to know whether we're going perform a partial aggregate.
     199              :      */
     200        26155 :     shareable = (aggform->aggfinalmodify != AGGMODIFY_READ_WRITE);
     201              : 
     202              :     /* get info about the output value's datatype */
     203        26155 :     get_typlenbyval(aggref->aggtype,
     204              :                     &resulttypeLen,
     205              :                     &resulttypeByVal);
     206              : 
     207              :     /* get initial value */
     208        26155 :     textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,
     209              :                                   Anum_pg_aggregate_agginitval,
     210              :                                   &initValueIsNull);
     211        26155 :     if (initValueIsNull)
     212        14850 :         initValue = (Datum) 0;
     213              :     else
     214        11305 :         initValue = GetAggInitVal(textInitVal, aggtranstype);
     215              : 
     216        26155 :     ReleaseSysCache(aggTuple);
     217              : 
     218              :     /*
     219              :      * 1. See if this is identical to another aggregate function call that
     220              :      * we've seen already.
     221              :      */
     222        26155 :     aggno = find_compatible_agg(root, aggref, &same_input_transnos);
     223        26155 :     if (aggno != -1)
     224              :     {
     225          718 :         AggInfo    *agginfo = list_nth_node(AggInfo, root->agginfos, aggno);
     226              : 
     227          718 :         agginfo->aggrefs = lappend(agginfo->aggrefs, aggref);
     228          718 :         transno = agginfo->transno;
     229              :     }
     230              :     else
     231              :     {
     232        25437 :         AggInfo    *agginfo = makeNode(AggInfo);
     233              : 
     234        25437 :         agginfo->finalfn_oid = aggfinalfn;
     235        25437 :         agginfo->aggrefs = list_make1(aggref);
     236        25437 :         agginfo->shareable = shareable;
     237              : 
     238        25437 :         aggno = list_length(root->agginfos);
     239        25437 :         root->agginfos = lappend(root->agginfos, agginfo);
     240              : 
     241              :         /*
     242              :          * Count it, and check for cases requiring ordered input.  Note that
     243              :          * ordered-set aggs always have nonempty aggorder.  Any ordered-input
     244              :          * case also defeats partial aggregation.
     245              :          */
     246        25437 :         if (aggref->aggorder != NIL || aggref->aggdistinct != NIL)
     247              :         {
     248         1630 :             root->numOrderedAggs++;
     249         1630 :             root->hasNonPartialAggs = true;
     250              :         }
     251              : 
     252        25437 :         get_typlenbyval(aggtranstype,
     253              :                         &transtypeLen,
     254              :                         &transtypeByVal);
     255              : 
     256              :         /*
     257              :          * 2. See if this aggregate can share transition state with another
     258              :          * aggregate that we've initialized already.
     259              :          */
     260        25437 :         transno = find_compatible_trans(root, aggref, shareable,
     261              :                                         aggtransfn, aggtranstype,
     262              :                                         transtypeLen, transtypeByVal,
     263              :                                         aggcombinefn,
     264              :                                         aggserialfn, aggdeserialfn,
     265              :                                         initValue, initValueIsNull,
     266              :                                         same_input_transnos);
     267        25437 :         if (transno == -1)
     268              :         {
     269        25296 :             AggTransInfo *transinfo = makeNode(AggTransInfo);
     270              : 
     271        25296 :             transinfo->args = aggref->args;
     272        25296 :             transinfo->aggfilter = aggref->aggfilter;
     273        25296 :             transinfo->transfn_oid = aggtransfn;
     274        25296 :             transinfo->combinefn_oid = aggcombinefn;
     275        25296 :             transinfo->serialfn_oid = aggserialfn;
     276        25296 :             transinfo->deserialfn_oid = aggdeserialfn;
     277        25296 :             transinfo->aggtranstype = aggtranstype;
     278        25296 :             transinfo->aggtranstypmod = aggtranstypmod;
     279        25296 :             transinfo->transtypeLen = transtypeLen;
     280        25296 :             transinfo->transtypeByVal = transtypeByVal;
     281        25296 :             transinfo->aggtransspace = aggtransspace;
     282        25296 :             transinfo->initValue = initValue;
     283        25296 :             transinfo->initValueIsNull = initValueIsNull;
     284              : 
     285        25296 :             transno = list_length(root->aggtransinfos);
     286        25296 :             root->aggtransinfos = lappend(root->aggtransinfos, transinfo);
     287              : 
     288              :             /*
     289              :              * Check whether partial aggregation is feasible, unless we
     290              :              * already found out that we can't do it.
     291              :              */
     292        25296 :             if (!root->hasNonPartialAggs)
     293              :             {
     294              :                 /*
     295              :                  * If there is no combine function, then partial aggregation
     296              :                  * is not possible.
     297              :                  */
     298        23531 :                 if (!OidIsValid(transinfo->combinefn_oid))
     299          663 :                     root->hasNonPartialAggs = true;
     300              : 
     301              :                 /*
     302              :                  * If we have any aggs with transtype INTERNAL then we must
     303              :                  * check whether they have serialization/deserialization
     304              :                  * functions; if not, we can't serialize partial-aggregation
     305              :                  * results.
     306              :                  */
     307        22868 :                 else if (transinfo->aggtranstype == INTERNALOID)
     308              :                 {
     309              : 
     310         9687 :                     if (!OidIsValid(transinfo->serialfn_oid) ||
     311         9687 :                         !OidIsValid(transinfo->deserialfn_oid))
     312            0 :                         root->hasNonSerialAggs = true;
     313              : 
     314              :                     /*
     315              :                      * array_agg_serialize and array_agg_deserialize make use
     316              :                      * of the aggregate non-byval input type's send and
     317              :                      * receive functions.  There's a chance that the type
     318              :                      * being aggregated has one or both of these functions
     319              :                      * missing.  In this case we must not allow the
     320              :                      * aggregate's serial and deserial functions to be used.
     321              :                      * It would be nice not to have special case this and
     322              :                      * instead provide some sort of supporting function within
     323              :                      * the aggregate to do this, but for now, that seems like
     324              :                      * overkill for this one case.
     325              :                      */
     326         9687 :                     if ((transinfo->serialfn_oid == F_ARRAY_AGG_SERIALIZE ||
     327         2722 :                          transinfo->deserialfn_oid == F_ARRAY_AGG_DESERIALIZE) &&
     328         6965 :                         !agg_args_support_sendreceive(aggref))
     329           70 :                         root->hasNonSerialAggs = true;
     330              :                 }
     331              :             }
     332              :         }
     333        25437 :         agginfo->transno = transno;
     334              :     }
     335              : 
     336              :     /*
     337              :      * Fill in the fields in the Aggref (aggtranstype was set above already)
     338              :      */
     339        26155 :     aggref->aggno = aggno;
     340        26155 :     aggref->aggtransno = transno;
     341        26155 : }
     342              : 
     343              : static bool
     344       122721 : preprocess_aggrefs_walker(Node *node, PlannerInfo *root)
     345              : {
     346       122721 :     if (node == NULL)
     347        23165 :         return false;
     348        99556 :     if (IsA(node, Aggref))
     349              :     {
     350        26155 :         Aggref     *aggref = (Aggref *) node;
     351              : 
     352        26155 :         preprocess_aggref(aggref, root);
     353              : 
     354              :         /*
     355              :          * We assume that the parser checked that there are no aggregates (of
     356              :          * this level anyway) in the aggregated arguments, direct arguments,
     357              :          * or filter clause.  Hence, we need not recurse into any of them.
     358              :          */
     359        26155 :         return false;
     360              :     }
     361              :     Assert(!IsA(node, SubLink));
     362        73401 :     return expression_tree_walker(node, preprocess_aggrefs_walker, root);
     363              : }
     364              : 
     365              : 
     366              : /*
     367              :  * find_compatible_agg - search for a previously initialized per-Agg struct
     368              :  *
     369              :  * Searches the previously looked at aggregates to find one which is compatible
     370              :  * with this one, with the same input parameters.  If no compatible aggregate
     371              :  * can be found, returns -1.
     372              :  *
     373              :  * As a side-effect, this also collects a list of existing, shareable per-Trans
     374              :  * structs with matching inputs.  If no identical Aggref is found, the list is
     375              :  * passed later to find_compatible_trans, to see if we can at least reuse
     376              :  * the state value of another aggregate.
     377              :  */
     378              : static int
     379        26155 : find_compatible_agg(PlannerInfo *root, Aggref *newagg,
     380              :                     List **same_input_transnos)
     381              : {
     382              :     ListCell   *lc;
     383              :     int         aggno;
     384              : 
     385        26155 :     *same_input_transnos = NIL;
     386              : 
     387              :     /* we mustn't reuse the aggref if it contains volatile function calls */
     388        26155 :     if (contain_volatile_functions((Node *) newagg))
     389          224 :         return -1;
     390              : 
     391              :     /*
     392              :      * Search through the list of already seen aggregates.  If we find an
     393              :      * existing identical aggregate call, then we can re-use that one.  While
     394              :      * searching, we'll also collect a list of Aggrefs with the same input
     395              :      * parameters.  If no matching Aggref is found, the caller can potentially
     396              :      * still re-use the transition state of one of them.  (At this stage we
     397              :      * just compare the parsetrees; whether different aggregates share the
     398              :      * same transition function will be checked later.)
     399              :      */
     400        25931 :     aggno = -1;
     401        31865 :     foreach(lc, root->agginfos)
     402              :     {
     403         6652 :         AggInfo    *agginfo = lfirst_node(AggInfo, lc);
     404              :         Aggref     *existingRef;
     405              : 
     406         6652 :         aggno++;
     407              : 
     408         6652 :         existingRef = linitial_node(Aggref, agginfo->aggrefs);
     409              : 
     410              :         /* all of the following must be the same or it's no match */
     411         6652 :         if (newagg->inputcollid != existingRef->inputcollid ||
     412         5708 :             newagg->aggtranstype != existingRef->aggtranstype ||
     413         3828 :             newagg->aggstar != existingRef->aggstar ||
     414         3264 :             newagg->aggvariadic != existingRef->aggvariadic ||
     415         3264 :             newagg->aggkind != existingRef->aggkind ||
     416         3165 :             !equal(newagg->args, existingRef->args) ||
     417         1445 :             !equal(newagg->aggorder, existingRef->aggorder) ||
     418         1442 :             !equal(newagg->aggdistinct, existingRef->aggdistinct) ||
     419         1442 :             !equal(newagg->aggfilter, existingRef->aggfilter))
     420         5314 :             continue;
     421              : 
     422              :         /* if it's the same aggregate function then report exact match */
     423         1338 :         if (newagg->aggfnoid == existingRef->aggfnoid &&
     424          724 :             newagg->aggtype == existingRef->aggtype &&
     425         1448 :             newagg->aggcollid == existingRef->aggcollid &&
     426          724 :             equal(newagg->aggdirectargs, existingRef->aggdirectargs))
     427              :         {
     428          718 :             list_free(*same_input_transnos);
     429          718 :             *same_input_transnos = NIL;
     430          718 :             return aggno;
     431              :         }
     432              : 
     433              :         /*
     434              :          * Not identical, but it had the same inputs.  If the final function
     435              :          * permits sharing, return its transno to the caller, in case we can
     436              :          * re-use its per-trans state.  (If there's already sharing going on,
     437              :          * we might report a transno more than once.  find_compatible_trans is
     438              :          * cheap enough that it's not worth spending cycles to avoid that.)
     439              :          */
     440          620 :         if (agginfo->shareable)
     441          617 :             *same_input_transnos = lappend_int(*same_input_transnos,
     442              :                                                agginfo->transno);
     443              :     }
     444              : 
     445        25213 :     return -1;
     446              : }
     447              : 
     448              : /*
     449              :  * find_compatible_trans - search for a previously initialized per-Trans
     450              :  * struct
     451              :  *
     452              :  * Searches the list of transnos for a per-Trans struct with the same
     453              :  * transition function and initial condition. (The inputs have already been
     454              :  * verified to match.)
     455              :  */
     456              : static int
     457        25437 : find_compatible_trans(PlannerInfo *root, Aggref *newagg, bool shareable,
     458              :                       Oid aggtransfn, Oid aggtranstype,
     459              :                       int transtypeLen, bool transtypeByVal,
     460              :                       Oid aggcombinefn,
     461              :                       Oid aggserialfn, Oid aggdeserialfn,
     462              :                       Datum initValue, bool initValueIsNull,
     463              :                       List *transnos)
     464              : {
     465              :     ListCell   *lc;
     466              : 
     467              :     /* If this aggregate can't share transition states, give up */
     468        25437 :     if (!shareable)
     469           60 :         return -1;
     470              : 
     471        25817 :     foreach(lc, transnos)
     472              :     {
     473          581 :         int         transno = lfirst_int(lc);
     474          581 :         AggTransInfo *pertrans = list_nth_node(AggTransInfo,
     475              :                                                root->aggtransinfos,
     476              :                                                transno);
     477              : 
     478              :         /*
     479              :          * if the transfns or transition state types are not the same then the
     480              :          * state can't be shared.
     481              :          */
     482          581 :         if (aggtransfn != pertrans->transfn_oid ||
     483          144 :             aggtranstype != pertrans->aggtranstype)
     484          437 :             continue;
     485              : 
     486              :         /*
     487              :          * The serialization and deserialization functions must match, if
     488              :          * present, as we're unable to share the trans state for aggregates
     489              :          * which will serialize or deserialize into different formats.
     490              :          * Remember that these will be InvalidOid if they're not required for
     491              :          * this agg node.
     492              :          */
     493          144 :         if (aggserialfn != pertrans->serialfn_oid ||
     494          144 :             aggdeserialfn != pertrans->deserialfn_oid)
     495            0 :             continue;
     496              : 
     497              :         /*
     498              :          * Combine function must also match.  We only care about the combine
     499              :          * function with partial aggregates, but it's too early in the
     500              :          * planning to know if we will do partial aggregation, so be
     501              :          * conservative.
     502              :          */
     503          144 :         if (aggcombinefn != pertrans->combinefn_oid)
     504            0 :             continue;
     505              : 
     506              :         /*
     507              :          * Check that the initial condition matches, too.
     508              :          */
     509          144 :         if (initValueIsNull && pertrans->initValueIsNull)
     510          141 :             return transno;
     511              : 
     512          192 :         if (!initValueIsNull && !pertrans->initValueIsNull &&
     513           96 :             datumIsEqual(initValue, pertrans->initValue,
     514              :                          transtypeByVal, transtypeLen))
     515           93 :             return transno;
     516              :     }
     517        25236 :     return -1;
     518              : }
     519              : 
     520              : static Datum
     521        11305 : GetAggInitVal(Datum textInitVal, Oid transtype)
     522              : {
     523              :     Oid         typinput,
     524              :                 typioparam;
     525              :     char       *strInitVal;
     526              :     Datum       initVal;
     527              : 
     528        11305 :     getTypeInputInfo(transtype, &typinput, &typioparam);
     529        11305 :     strInitVal = TextDatumGetCString(textInitVal);
     530        11305 :     initVal = OidInputFunctionCall(typinput, strInitVal,
     531              :                                    typioparam, -1);
     532        11305 :     pfree(strInitVal);
     533        11305 :     return initVal;
     534              : }
     535              : 
     536              : 
     537              : /*
     538              :  * get_agg_clause_costs
     539              :  *    Process the PlannerInfo's 'aggtransinfos' and 'agginfos' lists
     540              :  *    accumulating the cost information about them.
     541              :  *
     542              :  * 'aggsplit' tells us the expected partial-aggregation mode, which affects
     543              :  * the cost estimates.
     544              :  *
     545              :  * NOTE that the costs are ADDED to those already in *costs ... so the caller
     546              :  * is responsible for zeroing the struct initially.
     547              :  *
     548              :  * For each AggTransInfo, we add the cost of an aggregate transition using
     549              :  * either the transfn or combinefn depending on the 'aggsplit' value.  We also
     550              :  * account for the costs of any aggfilters and any serializations and
     551              :  * deserializations of the transition state and also estimate the total space
     552              :  * needed for the transition states as if each aggregate's state was stored in
     553              :  * memory concurrently (as would be done in a HashAgg plan).
     554              :  *
     555              :  * For each AggInfo in the 'agginfos' list we add the cost of running the
     556              :  * final function and the direct args, if any.
     557              :  */
     558              : void
     559        25989 : get_agg_clause_costs(PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
     560              : {
     561              :     ListCell   *lc;
     562              : 
     563        55257 :     foreach(lc, root->aggtransinfos)
     564              :     {
     565        29268 :         AggTransInfo *transinfo = lfirst_node(AggTransInfo, lc);
     566              : 
     567              :         /*
     568              :          * Add the appropriate component function execution costs to
     569              :          * appropriate totals.
     570              :          */
     571        29268 :         if (DO_AGGSPLIT_COMBINE(aggsplit))
     572              :         {
     573              :             /* charge for combining previously aggregated states */
     574         1508 :             add_function_cost(root, transinfo->combinefn_oid, NULL,
     575              :                               &costs->transCost);
     576              :         }
     577              :         else
     578        27760 :             add_function_cost(root, transinfo->transfn_oid, NULL,
     579              :                               &costs->transCost);
     580        29268 :         if (DO_AGGSPLIT_DESERIALIZE(aggsplit) &&
     581         1508 :             OidIsValid(transinfo->deserialfn_oid))
     582           61 :             add_function_cost(root, transinfo->deserialfn_oid, NULL,
     583              :                               &costs->transCost);
     584        29268 :         if (DO_AGGSPLIT_SERIALIZE(aggsplit) &&
     585         2317 :             OidIsValid(transinfo->serialfn_oid))
     586           61 :             add_function_cost(root, transinfo->serialfn_oid, NULL,
     587              :                               &costs->finalCost);
     588              : 
     589              :         /*
     590              :          * These costs are incurred only by the initial aggregate node, so we
     591              :          * mustn't include them again at upper levels.
     592              :          */
     593        29268 :         if (!DO_AGGSPLIT_COMBINE(aggsplit))
     594              :         {
     595              :             /* add the input expressions' cost to per-input-row costs */
     596              :             QualCost    argcosts;
     597              : 
     598        27760 :             cost_qual_eval_node(&argcosts, (Node *) transinfo->args, root);
     599        27760 :             costs->transCost.startup += argcosts.startup;
     600        27760 :             costs->transCost.per_tuple += argcosts.per_tuple;
     601              : 
     602              :             /*
     603              :              * Add any filter's cost to per-input-row costs.
     604              :              *
     605              :              * XXX Ideally we should reduce input expression costs according
     606              :              * to filter selectivity, but it's not clear it's worth the
     607              :              * trouble.
     608              :              */
     609        27760 :             if (transinfo->aggfilter)
     610              :             {
     611          394 :                 cost_qual_eval_node(&argcosts, (Node *) transinfo->aggfilter,
     612              :                                     root);
     613          394 :                 costs->transCost.startup += argcosts.startup;
     614          394 :                 costs->transCost.per_tuple += argcosts.per_tuple;
     615              :             }
     616              :         }
     617              : 
     618              :         /*
     619              :          * If the transition type is pass-by-value then it doesn't add
     620              :          * anything to the required size of the hashtable.  If it is
     621              :          * pass-by-reference then we have to add the estimated size of the
     622              :          * value itself, plus palloc overhead.
     623              :          */
     624        29268 :         if (!transinfo->transtypeByVal)
     625              :         {
     626              :             int32       avgwidth;
     627              : 
     628              :             /* Use average width if aggregate definition gave one */
     629         1745 :             if (transinfo->aggtransspace > 0)
     630           45 :                 avgwidth = transinfo->aggtransspace;
     631         1700 :             else if (transinfo->transfn_oid == F_ARRAY_APPEND)
     632              :             {
     633              :                 /*
     634              :                  * If the transition function is array_append(), it'll use an
     635              :                  * expanded array as transvalue, which will occupy at least
     636              :                  * ALLOCSET_SMALL_INITSIZE and possibly more.  Use that as the
     637              :                  * estimate for lack of a better idea.
     638              :                  */
     639            3 :                 avgwidth = ALLOCSET_SMALL_INITSIZE;
     640              :             }
     641              :             else
     642              :             {
     643         1697 :                 avgwidth = get_typavgwidth(transinfo->aggtranstype, transinfo->aggtranstypmod);
     644              :             }
     645              : 
     646         1745 :             avgwidth = MAXALIGN(avgwidth);
     647         1745 :             costs->transitionSpace += avgwidth + 2 * sizeof(void *);
     648              :         }
     649        27523 :         else if (transinfo->aggtranstype == INTERNALOID)
     650              :         {
     651              :             /*
     652              :              * INTERNAL transition type is a special case: although INTERNAL
     653              :              * is pass-by-value, it's almost certainly being used as a pointer
     654              :              * to some large data structure.  The aggregate definition can
     655              :              * provide an estimate of the size.  If it doesn't, then we assume
     656              :              * ALLOCSET_DEFAULT_INITSIZE, which is a good guess if the data is
     657              :              * being kept in a private memory context, as is done by
     658              :              * array_agg() for instance.
     659              :              */
     660        11646 :             if (transinfo->aggtransspace > 0)
     661          683 :                 costs->transitionSpace += transinfo->aggtransspace;
     662              :             else
     663        10963 :                 costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
     664              :         }
     665              :     }
     666              : 
     667        55398 :     foreach(lc, root->agginfos)
     668              :     {
     669        29409 :         AggInfo    *agginfo = lfirst_node(AggInfo, lc);
     670        29409 :         Aggref     *aggref = linitial_node(Aggref, agginfo->aggrefs);
     671              : 
     672              :         /*
     673              :          * Add the appropriate component function execution costs to
     674              :          * appropriate totals.
     675              :          */
     676        29409 :         if (!DO_AGGSPLIT_SKIPFINAL(aggsplit) &&
     677        27092 :             OidIsValid(agginfo->finalfn_oid))
     678        12667 :             add_function_cost(root, agginfo->finalfn_oid, NULL,
     679              :                               &costs->finalCost);
     680              : 
     681              :         /*
     682              :          * If there are direct arguments, treat their evaluation cost like the
     683              :          * cost of the finalfn.
     684              :          */
     685        29409 :         if (aggref->aggdirectargs)
     686              :         {
     687              :             QualCost    argcosts;
     688              : 
     689          147 :             cost_qual_eval_node(&argcosts, (Node *) aggref->aggdirectargs,
     690              :                                 root);
     691          147 :             costs->finalCost.startup += argcosts.startup;
     692          147 :             costs->finalCost.per_tuple += argcosts.per_tuple;
     693              :         }
     694              :     }
     695        25989 : }
        

Generated by: LCOV version 2.0-1