LCOV - code coverage report
Current view: top level - src/backend/optimizer/prep - prepagg.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 180 184 97.8 %
Date: 2024-04-19 21:13:41 Functions: 7 7 100.0 %
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-2024, 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       67284 : preprocess_aggrefs(PlannerInfo *root, Node *clause)
     111             : {
     112       67284 :     (void) preprocess_aggrefs_walker(clause, root);
     113       67284 : }
     114             : 
     115             : static void
     116       38978 : 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       38978 :     aggTuple = SearchSysCache1(AGGFNOID,
     150             :                                ObjectIdGetDatum(aggref->aggfnoid));
     151       38978 :     if (!HeapTupleIsValid(aggTuple))
     152           0 :         elog(ERROR, "cache lookup failed for aggregate %u",
     153             :              aggref->aggfnoid);
     154       38978 :     aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
     155       38978 :     aggtransfn = aggform->aggtransfn;
     156       38978 :     aggfinalfn = aggform->aggfinalfn;
     157       38978 :     aggcombinefn = aggform->aggcombinefn;
     158       38978 :     aggserialfn = aggform->aggserialfn;
     159       38978 :     aggdeserialfn = aggform->aggdeserialfn;
     160       38978 :     aggtranstype = aggform->aggtranstype;
     161       38978 :     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       38978 :     numArguments = get_aggregate_argtypes(aggref, inputTypes);
     169             : 
     170             :     /* resolve actual type of transition state, if polymorphic */
     171       38978 :     aggtranstype = resolve_aggregate_transtype(aggref->aggfnoid,
     172             :                                                aggtranstype,
     173             :                                                inputTypes,
     174             :                                                numArguments);
     175       38978 :     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       38978 :     aggtranstypmod = -1;
     183       38978 :     if (aggref->args)
     184             :     {
     185       27232 :         TargetEntry *tle = (TargetEntry *) linitial(aggref->args);
     186             : 
     187       27232 :         if (aggtranstype == exprType((Node *) tle->expr))
     188        3012 :             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       38978 :     shareable = (aggform->aggfinalmodify != AGGMODIFY_READ_WRITE);
     201             : 
     202             :     /* get info about the output value's datatype */
     203       38978 :     get_typlenbyval(aggref->aggtype,
     204             :                     &resulttypeLen,
     205             :                     &resulttypeByVal);
     206             : 
     207             :     /* get initial value */
     208       38978 :     textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,
     209             :                                   Anum_pg_aggregate_agginitval,
     210             :                                   &initValueIsNull);
     211       38978 :     if (initValueIsNull)
     212       23778 :         initValue = (Datum) 0;
     213             :     else
     214       15200 :         initValue = GetAggInitVal(textInitVal, aggtranstype);
     215             : 
     216       38978 :     ReleaseSysCache(aggTuple);
     217             : 
     218             :     /*
     219             :      * 1. See if this is identical to another aggregate function call that
     220             :      * we've seen already.
     221             :      */
     222       38978 :     aggno = find_compatible_agg(root, aggref, &same_input_transnos);
     223       38978 :     if (aggno != -1)
     224             :     {
     225        1228 :         AggInfo    *agginfo = list_nth_node(AggInfo, root->agginfos, aggno);
     226             : 
     227        1228 :         agginfo->aggrefs = lappend(agginfo->aggrefs, aggref);
     228        1228 :         transno = agginfo->transno;
     229             :     }
     230             :     else
     231             :     {
     232       37750 :         AggInfo    *agginfo = makeNode(AggInfo);
     233             : 
     234       37750 :         agginfo->finalfn_oid = aggfinalfn;
     235       37750 :         agginfo->aggrefs = list_make1(aggref);
     236       37750 :         agginfo->shareable = shareable;
     237             : 
     238       37750 :         aggno = list_length(root->agginfos);
     239       37750 :         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       37750 :         if (aggref->aggorder != NIL || aggref->aggdistinct != NIL)
     247             :         {
     248        2228 :             root->numOrderedAggs++;
     249        2228 :             root->hasNonPartialAggs = true;
     250             :         }
     251             : 
     252       37750 :         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       37750 :         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       37750 :         if (transno == -1)
     268             :         {
     269       37492 :             AggTransInfo *transinfo = makeNode(AggTransInfo);
     270             : 
     271       37492 :             transinfo->args = aggref->args;
     272       37492 :             transinfo->aggfilter = aggref->aggfilter;
     273       37492 :             transinfo->transfn_oid = aggtransfn;
     274       37492 :             transinfo->combinefn_oid = aggcombinefn;
     275       37492 :             transinfo->serialfn_oid = aggserialfn;
     276       37492 :             transinfo->deserialfn_oid = aggdeserialfn;
     277       37492 :             transinfo->aggtranstype = aggtranstype;
     278       37492 :             transinfo->aggtranstypmod = aggtranstypmod;
     279       37492 :             transinfo->transtypeLen = transtypeLen;
     280       37492 :             transinfo->transtypeByVal = transtypeByVal;
     281       37492 :             transinfo->aggtransspace = aggtransspace;
     282       37492 :             transinfo->initValue = initValue;
     283       37492 :             transinfo->initValueIsNull = initValueIsNull;
     284             : 
     285       37492 :             transno = list_length(root->aggtransinfos);
     286       37492 :             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       37492 :             if (!root->hasNonPartialAggs)
     293             :             {
     294             :                 /*
     295             :                  * If there is no combine function, then partial aggregation
     296             :                  * is not possible.
     297             :                  */
     298       35006 :                 if (!OidIsValid(transinfo->combinefn_oid))
     299        1120 :                     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       33886 :                 else if (transinfo->aggtranstype == INTERNALOID)
     308             :                 {
     309             : 
     310       15446 :                     if (!OidIsValid(transinfo->serialfn_oid) ||
     311       15446 :                         !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       15446 :                     if ((transinfo->serialfn_oid == F_ARRAY_AGG_SERIALIZE ||
     327        4342 :                          transinfo->deserialfn_oid == F_ARRAY_AGG_DESERIALIZE) &&
     328       11104 :                         !agg_args_support_sendreceive(aggref))
     329          76 :                         root->hasNonSerialAggs = true;
     330             :                 }
     331             :             }
     332             :         }
     333       37750 :         agginfo->transno = transno;
     334             :     }
     335             : 
     336             :     /*
     337             :      * Fill in the fields in the Aggref (aggtranstype was set above already)
     338             :      */
     339       38978 :     aggref->aggno = aggno;
     340       38978 :     aggref->aggtransno = transno;
     341       38978 : }
     342             : 
     343             : static bool
     344      178700 : preprocess_aggrefs_walker(Node *node, PlannerInfo *root)
     345             : {
     346      178700 :     if (node == NULL)
     347       33728 :         return false;
     348      144972 :     if (IsA(node, Aggref))
     349             :     {
     350       38978 :         Aggref     *aggref = (Aggref *) node;
     351             : 
     352       38978 :         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       38978 :         return false;
     360             :     }
     361             :     Assert(!IsA(node, SubLink));
     362      105994 :     return expression_tree_walker(node, preprocess_aggrefs_walker,
     363             :                                   (void *) root);
     364             : }
     365             : 
     366             : 
     367             : /*
     368             :  * find_compatible_agg - search for a previously initialized per-Agg struct
     369             :  *
     370             :  * Searches the previously looked at aggregates to find one which is compatible
     371             :  * with this one, with the same input parameters.  If no compatible aggregate
     372             :  * can be found, returns -1.
     373             :  *
     374             :  * As a side-effect, this also collects a list of existing, shareable per-Trans
     375             :  * structs with matching inputs.  If no identical Aggref is found, the list is
     376             :  * passed later to find_compatible_trans, to see if we can at least reuse
     377             :  * the state value of another aggregate.
     378             :  */
     379             : static int
     380       38978 : find_compatible_agg(PlannerInfo *root, Aggref *newagg,
     381             :                     List **same_input_transnos)
     382             : {
     383             :     ListCell   *lc;
     384             :     int         aggno;
     385             : 
     386       38978 :     *same_input_transnos = NIL;
     387             : 
     388             :     /* we mustn't reuse the aggref if it contains volatile function calls */
     389       38978 :     if (contain_volatile_functions((Node *) newagg))
     390         374 :         return -1;
     391             : 
     392             :     /*
     393             :      * Search through the list of already seen aggregates.  If we find an
     394             :      * existing identical aggregate call, then we can re-use that one.  While
     395             :      * searching, we'll also collect a list of Aggrefs with the same input
     396             :      * parameters.  If no matching Aggref is found, the caller can potentially
     397             :      * still re-use the transition state of one of them.  (At this stage we
     398             :      * just compare the parsetrees; whether different aggregates share the
     399             :      * same transition function will be checked later.)
     400             :      */
     401       38604 :     aggno = -1;
     402       48402 :     foreach(lc, root->agginfos)
     403             :     {
     404       11026 :         AggInfo    *agginfo = lfirst_node(AggInfo, lc);
     405             :         Aggref     *existingRef;
     406             : 
     407       11026 :         aggno++;
     408             : 
     409       11026 :         existingRef = linitial_node(Aggref, agginfo->aggrefs);
     410             : 
     411             :         /* all of the following must be the same or it's no match */
     412       11026 :         if (newagg->inputcollid != existingRef->inputcollid ||
     413        9812 :             newagg->aggtranstype != existingRef->aggtranstype ||
     414        6190 :             newagg->aggstar != existingRef->aggstar ||
     415        5210 :             newagg->aggvariadic != existingRef->aggvariadic ||
     416        5210 :             newagg->aggkind != existingRef->aggkind ||
     417        5012 :             !equal(newagg->args, existingRef->args) ||
     418        2598 :             !equal(newagg->aggorder, existingRef->aggorder) ||
     419        2592 :             !equal(newagg->aggdistinct, existingRef->aggdistinct) ||
     420        2592 :             !equal(newagg->aggfilter, existingRef->aggfilter))
     421        8642 :             continue;
     422             : 
     423             :         /* if it's the same aggregate function then report exact match */
     424        2384 :         if (newagg->aggfnoid == existingRef->aggfnoid &&
     425        1240 :             newagg->aggtype == existingRef->aggtype &&
     426        2480 :             newagg->aggcollid == existingRef->aggcollid &&
     427        1240 :             equal(newagg->aggdirectargs, existingRef->aggdirectargs))
     428             :         {
     429        1228 :             list_free(*same_input_transnos);
     430        1228 :             *same_input_transnos = NIL;
     431        1228 :             return aggno;
     432             :         }
     433             : 
     434             :         /*
     435             :          * Not identical, but it had the same inputs.  If the final function
     436             :          * permits sharing, return its transno to the caller, in case we can
     437             :          * re-use its per-trans state.  (If there's already sharing going on,
     438             :          * we might report a transno more than once.  find_compatible_trans is
     439             :          * cheap enough that it's not worth spending cycles to avoid that.)
     440             :          */
     441        1156 :         if (agginfo->shareable)
     442        1150 :             *same_input_transnos = lappend_int(*same_input_transnos,
     443             :                                                agginfo->transno);
     444             :     }
     445             : 
     446       37376 :     return -1;
     447             : }
     448             : 
     449             : /*
     450             :  * find_compatible_trans - search for a previously initialized per-Trans
     451             :  * struct
     452             :  *
     453             :  * Searches the list of transnos for a per-Trans struct with the same
     454             :  * transition function and initial condition. (The inputs have already been
     455             :  * verified to match.)
     456             :  */
     457             : static int
     458       37750 : find_compatible_trans(PlannerInfo *root, Aggref *newagg, bool shareable,
     459             :                       Oid aggtransfn, Oid aggtranstype,
     460             :                       int transtypeLen, bool transtypeByVal,
     461             :                       Oid aggcombinefn,
     462             :                       Oid aggserialfn, Oid aggdeserialfn,
     463             :                       Datum initValue, bool initValueIsNull,
     464             :                       List *transnos)
     465             : {
     466             :     ListCell   *lc;
     467             : 
     468             :     /* If this aggregate can't share transition states, give up */
     469       37750 :     if (!shareable)
     470         120 :         return -1;
     471             : 
     472       38468 :     foreach(lc, transnos)
     473             :     {
     474        1096 :         int         transno = lfirst_int(lc);
     475        1096 :         AggTransInfo *pertrans = list_nth_node(AggTransInfo,
     476             :                                                root->aggtransinfos,
     477             :                                                transno);
     478             : 
     479             :         /*
     480             :          * if the transfns or transition state types are not the same then the
     481             :          * state can't be shared.
     482             :          */
     483        1096 :         if (aggtransfn != pertrans->transfn_oid ||
     484         264 :             aggtranstype != pertrans->aggtranstype)
     485         832 :             continue;
     486             : 
     487             :         /*
     488             :          * The serialization and deserialization functions must match, if
     489             :          * present, as we're unable to share the trans state for aggregates
     490             :          * which will serialize or deserialize into different formats.
     491             :          * Remember that these will be InvalidOid if they're not required for
     492             :          * this agg node.
     493             :          */
     494         264 :         if (aggserialfn != pertrans->serialfn_oid ||
     495         264 :             aggdeserialfn != pertrans->deserialfn_oid)
     496           0 :             continue;
     497             : 
     498             :         /*
     499             :          * Combine function must also match.  We only care about the combine
     500             :          * function with partial aggregates, but it's too early in the
     501             :          * planning to know if we will do partial aggregation, so be
     502             :          * conservative.
     503             :          */
     504         264 :         if (aggcombinefn != pertrans->combinefn_oid)
     505           0 :             continue;
     506             : 
     507             :         /*
     508             :          * Check that the initial condition matches, too.
     509             :          */
     510         264 :         if (initValueIsNull && pertrans->initValueIsNull)
     511         258 :             return transno;
     512             : 
     513         336 :         if (!initValueIsNull && !pertrans->initValueIsNull &&
     514         168 :             datumIsEqual(initValue, pertrans->initValue,
     515             :                          transtypeByVal, transtypeLen))
     516         162 :             return transno;
     517             :     }
     518       37372 :     return -1;
     519             : }
     520             : 
     521             : static Datum
     522       15200 : GetAggInitVal(Datum textInitVal, Oid transtype)
     523             : {
     524             :     Oid         typinput,
     525             :                 typioparam;
     526             :     char       *strInitVal;
     527             :     Datum       initVal;
     528             : 
     529       15200 :     getTypeInputInfo(transtype, &typinput, &typioparam);
     530       15200 :     strInitVal = TextDatumGetCString(textInitVal);
     531       15200 :     initVal = OidInputFunctionCall(typinput, strInitVal,
     532             :                                    typioparam, -1);
     533       15200 :     pfree(strInitVal);
     534       15200 :     return initVal;
     535             : }
     536             : 
     537             : 
     538             : /*
     539             :  * get_agg_clause_costs
     540             :  *    Process the PlannerInfo's 'aggtransinfos' and 'agginfos' lists
     541             :  *    accumulating the cost information about them.
     542             :  *
     543             :  * 'aggsplit' tells us the expected partial-aggregation mode, which affects
     544             :  * the cost estimates.
     545             :  *
     546             :  * NOTE that the costs are ADDED to those already in *costs ... so the caller
     547             :  * is responsible for zeroing the struct initially.
     548             :  *
     549             :  * For each AggTransInfo, we add the cost of an aggregate transition using
     550             :  * either the transfn or combinefn depending on the 'aggsplit' value.  We also
     551             :  * account for the costs of any aggfilters and any serializations and
     552             :  * deserializations of the transition state and also estimate the total space
     553             :  * needed for the transition states as if each aggregate's state was stored in
     554             :  * memory concurrently (as would be done in a HashAgg plan).
     555             :  *
     556             :  * For each AggInfo in the 'agginfos' list we add the cost of running the
     557             :  * final function and the direct args, if any.
     558             :  */
     559             : void
     560       36606 : get_agg_clause_costs(PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
     561             : {
     562             :     ListCell   *lc;
     563             : 
     564       78096 :     foreach(lc, root->aggtransinfos)
     565             :     {
     566       41490 :         AggTransInfo *transinfo = lfirst_node(AggTransInfo, lc);
     567             : 
     568             :         /*
     569             :          * Add the appropriate component function execution costs to
     570             :          * appropriate totals.
     571             :          */
     572       41490 :         if (DO_AGGSPLIT_COMBINE(aggsplit))
     573             :         {
     574             :             /* charge for combining previously aggregated states */
     575        1858 :             add_function_cost(root, transinfo->combinefn_oid, NULL,
     576             :                               &costs->transCost);
     577             :         }
     578             :         else
     579       39632 :             add_function_cost(root, transinfo->transfn_oid, NULL,
     580             :                               &costs->transCost);
     581       41490 :         if (DO_AGGSPLIT_DESERIALIZE(aggsplit) &&
     582        1858 :             OidIsValid(transinfo->deserialfn_oid))
     583         122 :             add_function_cost(root, transinfo->deserialfn_oid, NULL,
     584             :                               &costs->transCost);
     585       41490 :         if (DO_AGGSPLIT_SERIALIZE(aggsplit) &&
     586        1858 :             OidIsValid(transinfo->serialfn_oid))
     587         122 :             add_function_cost(root, transinfo->serialfn_oid, NULL,
     588             :                               &costs->finalCost);
     589             : 
     590             :         /*
     591             :          * These costs are incurred only by the initial aggregate node, so we
     592             :          * mustn't include them again at upper levels.
     593             :          */
     594       41490 :         if (!DO_AGGSPLIT_COMBINE(aggsplit))
     595             :         {
     596             :             /* add the input expressions' cost to per-input-row costs */
     597             :             QualCost    argcosts;
     598             : 
     599       39632 :             cost_qual_eval_node(&argcosts, (Node *) transinfo->args, root);
     600       39632 :             costs->transCost.startup += argcosts.startup;
     601       39632 :             costs->transCost.per_tuple += argcosts.per_tuple;
     602             : 
     603             :             /*
     604             :              * Add any filter's cost to per-input-row costs.
     605             :              *
     606             :              * XXX Ideally we should reduce input expression costs according
     607             :              * to filter selectivity, but it's not clear it's worth the
     608             :              * trouble.
     609             :              */
     610       39632 :             if (transinfo->aggfilter)
     611             :             {
     612         762 :                 cost_qual_eval_node(&argcosts, (Node *) transinfo->aggfilter,
     613             :                                     root);
     614         762 :                 costs->transCost.startup += argcosts.startup;
     615         762 :                 costs->transCost.per_tuple += argcosts.per_tuple;
     616             :             }
     617             :         }
     618             : 
     619             :         /*
     620             :          * If the transition type is pass-by-value then it doesn't add
     621             :          * anything to the required size of the hashtable.  If it is
     622             :          * pass-by-reference then we have to add the estimated size of the
     623             :          * value itself, plus palloc overhead.
     624             :          */
     625       41490 :         if (!transinfo->transtypeByVal)
     626             :         {
     627             :             int32       avgwidth;
     628             : 
     629             :             /* Use average width if aggregate definition gave one */
     630        2842 :             if (transinfo->aggtransspace > 0)
     631          90 :                 avgwidth = transinfo->aggtransspace;
     632        2752 :             else if (transinfo->transfn_oid == F_ARRAY_APPEND)
     633             :             {
     634             :                 /*
     635             :                  * If the transition function is array_append(), it'll use an
     636             :                  * expanded array as transvalue, which will occupy at least
     637             :                  * ALLOCSET_SMALL_INITSIZE and possibly more.  Use that as the
     638             :                  * estimate for lack of a better idea.
     639             :                  */
     640           6 :                 avgwidth = ALLOCSET_SMALL_INITSIZE;
     641             :             }
     642             :             else
     643             :             {
     644        2746 :                 avgwidth = get_typavgwidth(transinfo->aggtranstype, transinfo->aggtranstypmod);
     645             :             }
     646             : 
     647        2842 :             avgwidth = MAXALIGN(avgwidth);
     648        2842 :             costs->transitionSpace += avgwidth + 2 * sizeof(void *);
     649             :         }
     650       38648 :         else if (transinfo->aggtranstype == INTERNALOID)
     651             :         {
     652             :             /*
     653             :              * INTERNAL transition type is a special case: although INTERNAL
     654             :              * is pass-by-value, it's almost certainly being used as a pointer
     655             :              * to some large data structure.  The aggregate definition can
     656             :              * provide an estimate of the size.  If it doesn't, then we assume
     657             :              * ALLOCSET_DEFAULT_INITSIZE, which is a good guess if the data is
     658             :              * being kept in a private memory context, as is done by
     659             :              * array_agg() for instance.
     660             :              */
     661       18144 :             if (transinfo->aggtransspace > 0)
     662        1094 :                 costs->transitionSpace += transinfo->aggtransspace;
     663             :             else
     664       17050 :                 costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
     665             :         }
     666             :     }
     667             : 
     668       78354 :     foreach(lc, root->agginfos)
     669             :     {
     670       41748 :         AggInfo    *agginfo = lfirst_node(AggInfo, lc);
     671       41748 :         Aggref     *aggref = linitial_node(Aggref, agginfo->aggrefs);
     672             : 
     673             :         /*
     674             :          * Add the appropriate component function execution costs to
     675             :          * appropriate totals.
     676             :          */
     677       41748 :         if (!DO_AGGSPLIT_SKIPFINAL(aggsplit) &&
     678       39890 :             OidIsValid(agginfo->finalfn_oid))
     679       19874 :             add_function_cost(root, agginfo->finalfn_oid, NULL,
     680             :                               &costs->finalCost);
     681             : 
     682             :         /*
     683             :          * If there are direct arguments, treat their evaluation cost like the
     684             :          * cost of the finalfn.
     685             :          */
     686       41748 :         if (aggref->aggdirectargs)
     687             :         {
     688             :             QualCost    argcosts;
     689             : 
     690         294 :             cost_qual_eval_node(&argcosts, (Node *) aggref->aggdirectargs,
     691             :                                 root);
     692         294 :             costs->finalCost.startup += argcosts.startup;
     693         294 :             costs->finalCost.per_tuple += argcosts.per_tuple;
     694             :         }
     695             :     }
     696       36606 : }

Generated by: LCOV version 1.14