LCOV - code coverage report
Current view: top level - src/backend/optimizer/prep - prepagg.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 98.5 % 263 259
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 87.3 % 158 138

             Branch data     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                 :        9852 : preprocess_aggrefs(PlannerInfo *root, Node *clause)
     111                 :             : {
     112                 :        9852 :         (void) preprocess_aggrefs_walker(clause, root);
     113                 :        9852 : }
     114                 :             : 
     115                 :             : static void
     116                 :        5618 : preprocess_aggref(Aggref *aggref, PlannerInfo *root)
     117                 :             : {
     118                 :        5618 :         HeapTuple       aggTuple;
     119                 :        5618 :         Form_pg_aggregate aggform;
     120                 :        5618 :         Oid                     aggtransfn;
     121                 :        5618 :         Oid                     aggfinalfn;
     122                 :        5618 :         Oid                     aggcombinefn;
     123                 :        5618 :         Oid                     aggserialfn;
     124                 :        5618 :         Oid                     aggdeserialfn;
     125                 :        5618 :         Oid                     aggtranstype;
     126                 :        5618 :         int32           aggtranstypmod;
     127                 :        5618 :         int32           aggtransspace;
     128                 :        5618 :         bool            shareable;
     129                 :        5618 :         int                     aggno;
     130                 :        5618 :         int                     transno;
     131                 :        5618 :         List       *same_input_transnos;
     132                 :        5618 :         int16           resulttypeLen;
     133                 :        5618 :         bool            resulttypeByVal;
     134                 :        5618 :         Datum           textInitVal;
     135                 :        5618 :         Datum           initValue;
     136                 :        5618 :         bool            initValueIsNull;
     137                 :        5618 :         bool            transtypeByVal;
     138                 :        5618 :         int16           transtypeLen;
     139                 :        5618 :         Oid                     inputTypes[FUNC_MAX_ARGS];
     140                 :        5618 :         int                     numArguments;
     141                 :             : 
     142         [ +  - ]:        5618 :         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                 :        5618 :         aggTuple = SearchSysCache1(AGGFNOID,
     150                 :        5618 :                                                            ObjectIdGetDatum(aggref->aggfnoid));
     151         [ +  - ]:        5618 :         if (!HeapTupleIsValid(aggTuple))
     152   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for aggregate %u",
     153                 :             :                          aggref->aggfnoid);
     154                 :        5618 :         aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
     155                 :        5618 :         aggtransfn = aggform->aggtransfn;
     156                 :        5618 :         aggfinalfn = aggform->aggfinalfn;
     157                 :        5618 :         aggcombinefn = aggform->aggcombinefn;
     158                 :        5618 :         aggserialfn = aggform->aggserialfn;
     159                 :        5618 :         aggdeserialfn = aggform->aggdeserialfn;
     160                 :        5618 :         aggtranstype = aggform->aggtranstype;
     161                 :        5618 :         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                 :        5618 :         numArguments = get_aggregate_argtypes(aggref, inputTypes);
     169                 :             : 
     170                 :             :         /* resolve actual type of transition state, if polymorphic */
     171                 :       11236 :         aggtranstype = resolve_aggregate_transtype(aggref->aggfnoid,
     172                 :        5618 :                                                                                            aggtranstype,
     173                 :        5618 :                                                                                            inputTypes,
     174                 :        5618 :                                                                                            numArguments);
     175                 :        5618 :         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                 :        5618 :         aggtranstypmod = -1;
     183         [ +  + ]:        5618 :         if (aggref->args)
     184                 :             :         {
     185                 :        4002 :                 TargetEntry *tle = (TargetEntry *) linitial(aggref->args);
     186                 :             : 
     187         [ +  + ]:        4002 :                 if (aggtranstype == exprType((Node *) tle->expr))
     188                 :         462 :                         aggtranstypmod = exprTypmod((Node *) tle->expr);
     189                 :        4002 :         }
     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                 :        5618 :         shareable = (aggform->aggfinalmodify != AGGMODIFY_READ_WRITE);
     201                 :             : 
     202                 :             :         /* get info about the output value's datatype */
     203                 :        5618 :         get_typlenbyval(aggref->aggtype,
     204                 :             :                                         &resulttypeLen,
     205                 :             :                                         &resulttypeByVal);
     206                 :             : 
     207                 :             :         /* get initial value */
     208                 :        5618 :         textInitVal = SysCacheGetAttr(AGGFNOID, aggTuple,
     209                 :             :                                                                   Anum_pg_aggregate_agginitval,
     210                 :             :                                                                   &initValueIsNull);
     211         [ +  + ]:        5618 :         if (initValueIsNull)
     212                 :        3588 :                 initValue = (Datum) 0;
     213                 :             :         else
     214                 :        2030 :                 initValue = GetAggInitVal(textInitVal, aggtranstype);
     215                 :             : 
     216                 :        5618 :         ReleaseSysCache(aggTuple);
     217                 :             : 
     218                 :             :         /*
     219                 :             :          * 1. See if this is identical to another aggregate function call that
     220                 :             :          * we've seen already.
     221                 :             :          */
     222                 :        5618 :         aggno = find_compatible_agg(root, aggref, &same_input_transnos);
     223         [ +  + ]:        5618 :         if (aggno != -1)
     224                 :             :         {
     225                 :          63 :                 AggInfo    *agginfo = list_nth_node(AggInfo, root->agginfos, aggno);
     226                 :             : 
     227                 :          63 :                 agginfo->aggrefs = lappend(agginfo->aggrefs, aggref);
     228                 :          63 :                 transno = agginfo->transno;
     229                 :          63 :         }
     230                 :             :         else
     231                 :             :         {
     232                 :        5555 :                 AggInfo    *agginfo = makeNode(AggInfo);
     233                 :             : 
     234                 :        5555 :                 agginfo->finalfn_oid = aggfinalfn;
     235                 :        5555 :                 agginfo->aggrefs = list_make1(aggref);
     236                 :        5555 :                 agginfo->shareable = shareable;
     237                 :             : 
     238                 :        5555 :                 aggno = list_length(root->agginfos);
     239                 :        5555 :                 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   [ +  +  +  + ]:        5555 :                 if (aggref->aggorder != NIL || aggref->aggdistinct != NIL)
     247                 :             :                 {
     248                 :         279 :                         root->numOrderedAggs++;
     249                 :         279 :                         root->hasNonPartialAggs = true;
     250                 :         279 :                 }
     251                 :             : 
     252                 :        5555 :                 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                 :       11110 :                 transno = find_compatible_trans(root, aggref, shareable,
     261                 :        5555 :                                                                                 aggtransfn, aggtranstype,
     262                 :        5555 :                                                                                 transtypeLen, transtypeByVal,
     263                 :        5555 :                                                                                 aggcombinefn,
     264                 :        5555 :                                                                                 aggserialfn, aggdeserialfn,
     265                 :        5555 :                                                                                 initValue, initValueIsNull,
     266                 :        5555 :                                                                                 same_input_transnos);
     267         [ +  + ]:        5555 :                 if (transno == -1)
     268                 :             :                 {
     269                 :        5508 :                         AggTransInfo *transinfo = makeNode(AggTransInfo);
     270                 :             : 
     271                 :        5508 :                         transinfo->args = aggref->args;
     272                 :        5508 :                         transinfo->aggfilter = aggref->aggfilter;
     273                 :        5508 :                         transinfo->transfn_oid = aggtransfn;
     274                 :        5508 :                         transinfo->combinefn_oid = aggcombinefn;
     275                 :        5508 :                         transinfo->serialfn_oid = aggserialfn;
     276                 :        5508 :                         transinfo->deserialfn_oid = aggdeserialfn;
     277                 :        5508 :                         transinfo->aggtranstype = aggtranstype;
     278                 :        5508 :                         transinfo->aggtranstypmod = aggtranstypmod;
     279                 :        5508 :                         transinfo->transtypeLen = transtypeLen;
     280                 :        5508 :                         transinfo->transtypeByVal = transtypeByVal;
     281                 :        5508 :                         transinfo->aggtransspace = aggtransspace;
     282                 :        5508 :                         transinfo->initValue = initValue;
     283                 :        5508 :                         transinfo->initValueIsNull = initValueIsNull;
     284                 :             : 
     285                 :        5508 :                         transno = list_length(root->aggtransinfos);
     286                 :        5508 :                         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         [ +  + ]:        5508 :                         if (!root->hasNonPartialAggs)
     293                 :             :                         {
     294                 :             :                                 /*
     295                 :             :                                  * If there is no combine function, then partial aggregation
     296                 :             :                                  * is not possible.
     297                 :             :                                  */
     298         [ +  + ]:        5184 :                                 if (!OidIsValid(transinfo->combinefn_oid))
     299                 :         202 :                                         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         [ +  + ]:        4982 :                                 else if (transinfo->aggtranstype == INTERNALOID)
     308                 :             :                                 {
     309                 :             : 
     310   [ +  -  +  - ]:        2504 :                                         if (!OidIsValid(transinfo->serialfn_oid) ||
     311                 :        2504 :                                                 !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         [ +  + ]:        2504 :                                         if ((transinfo->serialfn_oid == F_ARRAY_AGG_SERIALIZE ||
     327         [ +  + ]:        2504 :                                                  transinfo->deserialfn_oid == F_ARRAY_AGG_DESERIALIZE) &&
     328                 :        2504 :                                                 !agg_args_support_sendreceive(aggref))
     329                 :           8 :                                                 root->hasNonSerialAggs = true;
     330                 :        2504 :                                 }
     331                 :        5184 :                         }
     332                 :        5508 :                 }
     333                 :        5555 :                 agginfo->transno = transno;
     334                 :        5555 :         }
     335                 :             : 
     336                 :             :         /*
     337                 :             :          * Fill in the fields in the Aggref (aggtranstype was set above already)
     338                 :             :          */
     339                 :        5618 :         aggref->aggno = aggno;
     340                 :        5618 :         aggref->aggtransno = transno;
     341                 :        5618 : }
     342                 :             : 
     343                 :             : static bool
     344                 :       27725 : preprocess_aggrefs_walker(Node *node, PlannerInfo *root)
     345                 :             : {
     346         [ +  + ]:       27725 :         if (node == NULL)
     347                 :        5029 :                 return false;
     348         [ +  + ]:       22696 :         if (IsA(node, Aggref))
     349                 :             :         {
     350                 :        5618 :                 Aggref     *aggref = (Aggref *) node;
     351                 :             : 
     352                 :        5618 :                 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                 :        5618 :                 return false;
     360                 :        5618 :         }
     361         [ +  - ]:       17078 :         Assert(!IsA(node, SubLink));
     362                 :       17078 :         return expression_tree_walker(node, preprocess_aggrefs_walker, root);
     363                 :       27725 : }
     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                 :        5618 : find_compatible_agg(PlannerInfo *root, Aggref *newagg,
     380                 :             :                                         List **same_input_transnos)
     381                 :             : {
     382                 :        5618 :         ListCell   *lc;
     383                 :        5618 :         int                     aggno;
     384                 :             : 
     385                 :        5618 :         *same_input_transnos = NIL;
     386                 :             : 
     387                 :             :         /* we mustn't reuse the aggref if it contains volatile function calls */
     388         [ +  + ]:        5618 :         if (contain_volatile_functions((Node *) newagg))
     389                 :           9 :                 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                 :        5609 :         aggno = -1;
     401   [ +  +  +  +  :        7282 :         foreach(lc, root->agginfos)
             +  +  +  + ]
     402                 :             :         {
     403                 :        1673 :                 AggInfo    *agginfo = lfirst_node(AggInfo, lc);
     404                 :        1673 :                 Aggref     *existingRef;
     405                 :             : 
     406                 :        1673 :                 aggno++;
     407                 :             : 
     408                 :        1673 :                 existingRef = linitial_node(Aggref, agginfo->aggrefs);
     409                 :             : 
     410                 :             :                 /* all of the following must be the same or it's no match */
     411         [ +  + ]:        1673 :                 if (newagg->inputcollid != existingRef->inputcollid ||
     412         [ +  + ]:        1486 :                         newagg->aggtranstype != existingRef->aggtranstype ||
     413         [ +  + ]:         948 :                         newagg->aggstar != existingRef->aggstar ||
     414         [ +  - ]:         795 :                         newagg->aggvariadic != existingRef->aggvariadic ||
     415         [ +  + ]:         795 :                         newagg->aggkind != existingRef->aggkind ||
     416         [ +  + ]:         764 :                         !equal(newagg->args, existingRef->args) ||
     417         [ +  + ]:         268 :                         !equal(newagg->aggorder, existingRef->aggorder) ||
     418   [ +  -  +  + ]:         267 :                         !equal(newagg->aggdistinct, existingRef->aggdistinct) ||
     419                 :         267 :                         !equal(newagg->aggfilter, existingRef->aggfilter))
     420                 :        1427 :                         continue;
     421                 :             : 
     422                 :             :                 /* if it's the same aggregate function then report exact match */
     423         [ +  + ]:         246 :                 if (newagg->aggfnoid == existingRef->aggfnoid &&
     424         [ +  - ]:          65 :                         newagg->aggtype == existingRef->aggtype &&
     425   [ +  -  +  + ]:          65 :                         newagg->aggcollid == existingRef->aggcollid &&
     426                 :          65 :                         equal(newagg->aggdirectargs, existingRef->aggdirectargs))
     427                 :             :                 {
     428                 :          63 :                         list_free(*same_input_transnos);
     429                 :          63 :                         *same_input_transnos = NIL;
     430                 :          63 :                         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         [ +  + ]:         183 :                 if (agginfo->shareable)
     441                 :         364 :                         *same_input_transnos = lappend_int(*same_input_transnos,
     442                 :         182 :                                                                                            agginfo->transno);
     443      [ +  +  + ]:        1673 :         }
     444                 :             : 
     445                 :        5546 :         return -1;
     446                 :        5618 : }
     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                 :        5555 : 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                 :        5555 :         ListCell   *lc;
     466                 :             : 
     467                 :             :         /* If this aggregate can't share transition states, give up */
     468         [ +  + ]:        5555 :         if (!shareable)
     469                 :          18 :                 return -1;
     470                 :             : 
     471   [ +  +  +  +  :        5707 :         foreach(lc, transnos)
             +  +  +  + ]
     472                 :             :         {
     473                 :         170 :                 int                     transno = lfirst_int(lc);
     474                 :         170 :                 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   [ +  +  -  + ]:         170 :                 if (aggtransfn != pertrans->transfn_oid ||
     483                 :          48 :                         aggtranstype != pertrans->aggtranstype)
     484                 :         122 :                         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   [ +  -  -  + ]:          48 :                 if (aggserialfn != pertrans->serialfn_oid ||
     494                 :          48 :                         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         [ -  + ]:          48 :                 if (aggcombinefn != pertrans->combinefn_oid)
     504                 :           0 :                         continue;
     505                 :             : 
     506                 :             :                 /*
     507                 :             :                  * Check that the initial condition matches, too.
     508                 :             :                  */
     509   [ +  +  -  + ]:          48 :                 if (initValueIsNull && pertrans->initValueIsNull)
     510                 :          16 :                         return transno;
     511                 :             : 
     512   [ +  -  +  -  :          32 :                 if (!initValueIsNull && !pertrans->initValueIsNull &&
                   +  + ]
     513                 :          64 :                         datumIsEqual(initValue, pertrans->initValue,
     514                 :          32 :                                                  transtypeByVal, transtypeLen))
     515                 :          31 :                         return transno;
     516      [ +  +  + ]:         170 :         }
     517                 :        5490 :         return -1;
     518                 :        5555 : }
     519                 :             : 
     520                 :             : static Datum
     521                 :        2030 : GetAggInitVal(Datum textInitVal, Oid transtype)
     522                 :             : {
     523                 :        2030 :         Oid                     typinput,
     524                 :             :                                 typioparam;
     525                 :        2030 :         char       *strInitVal;
     526                 :        2030 :         Datum           initVal;
     527                 :             : 
     528                 :        2030 :         getTypeInputInfo(transtype, &typinput, &typioparam);
     529                 :        2030 :         strInitVal = TextDatumGetCString(textInitVal);
     530                 :        4060 :         initVal = OidInputFunctionCall(typinput, strInitVal,
     531                 :        2030 :                                                                    typioparam, -1);
     532                 :        2030 :         pfree(strInitVal);
     533                 :        4060 :         return initVal;
     534                 :        2030 : }
     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                 :        5685 : get_agg_clause_costs(PlannerInfo *root, AggSplit aggsplit, AggClauseCosts *costs)
     560                 :             : {
     561                 :        5685 :         ListCell   *lc;
     562                 :             : 
     563   [ +  +  +  +  :       12224 :         foreach(lc, root->aggtransinfos)
                   +  + ]
     564                 :             :         {
     565                 :        6539 :                 AggTransInfo *transinfo = lfirst_node(AggTransInfo, lc);
     566                 :             : 
     567                 :             :                 /*
     568                 :             :                  * Add the appropriate component function execution costs to
     569                 :             :                  * appropriate totals.
     570                 :             :                  */
     571         [ +  + ]:        6539 :                 if (DO_AGGSPLIT_COMBINE(aggsplit))
     572                 :             :                 {
     573                 :             :                         /* charge for combining previously aggregated states */
     574                 :         762 :                         add_function_cost(root, transinfo->combinefn_oid, NULL,
     575                 :         381 :                                                           &costs->transCost);
     576                 :         381 :                 }
     577                 :             :                 else
     578                 :       12316 :                         add_function_cost(root, transinfo->transfn_oid, NULL,
     579                 :        6158 :                                                           &costs->transCost);
     580   [ +  +  +  + ]:        6539 :                 if (DO_AGGSPLIT_DESERIALIZE(aggsplit) &&
     581                 :         381 :                         OidIsValid(transinfo->deserialfn_oid))
     582                 :          40 :                         add_function_cost(root, transinfo->deserialfn_oid, NULL,
     583                 :          20 :                                                           &costs->transCost);
     584   [ +  +  +  + ]:        6539 :                 if (DO_AGGSPLIT_SERIALIZE(aggsplit) &&
     585                 :         650 :                         OidIsValid(transinfo->serialfn_oid))
     586                 :          40 :                         add_function_cost(root, transinfo->serialfn_oid, NULL,
     587                 :          20 :                                                           &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         [ +  + ]:        6539 :                 if (!DO_AGGSPLIT_COMBINE(aggsplit))
     594                 :             :                 {
     595                 :             :                         /* add the input expressions' cost to per-input-row costs */
     596                 :        6158 :                         QualCost        argcosts;
     597                 :             : 
     598                 :        6158 :                         cost_qual_eval_node(&argcosts, (Node *) transinfo->args, root);
     599                 :        6158 :                         costs->transCost.startup += argcosts.startup;
     600                 :        6158 :                         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         [ +  + ]:        6158 :                         if (transinfo->aggfilter)
     610                 :             :                         {
     611                 :          88 :                                 cost_qual_eval_node(&argcosts, (Node *) transinfo->aggfilter,
     612                 :          44 :                                                                         root);
     613                 :          44 :                                 costs->transCost.startup += argcosts.startup;
     614                 :          44 :                                 costs->transCost.per_tuple += argcosts.per_tuple;
     615                 :          44 :                         }
     616                 :        6158 :                 }
     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         [ +  + ]:        6539 :                 if (!transinfo->transtypeByVal)
     625                 :             :                 {
     626                 :         557 :                         int32           avgwidth;
     627                 :             : 
     628                 :             :                         /* Use average width if aggregate definition gave one */
     629         [ +  + ]:         557 :                         if (transinfo->aggtransspace > 0)
     630                 :          15 :                                 avgwidth = transinfo->aggtransspace;
     631         [ +  + ]:         542 :                         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                 :           1 :                                 avgwidth = ALLOCSET_SMALL_INITSIZE;
     640                 :           1 :                         }
     641                 :             :                         else
     642                 :             :                         {
     643                 :         541 :                                 avgwidth = get_typavgwidth(transinfo->aggtranstype, transinfo->aggtranstypmod);
     644                 :             :                         }
     645                 :             : 
     646                 :         557 :                         avgwidth = MAXALIGN(avgwidth);
     647                 :         557 :                         costs->transitionSpace += avgwidth + 2 * sizeof(void *);
     648                 :         557 :                 }
     649         [ +  + ]:        5982 :                 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         [ +  + ]:        2872 :                         if (transinfo->aggtransspace > 0)
     661                 :         202 :                                 costs->transitionSpace += transinfo->aggtransspace;
     662                 :             :                         else
     663                 :        2670 :                                 costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
     664                 :        2872 :                 }
     665                 :        6539 :         }
     666                 :             : 
     667   [ +  +  +  +  :       12271 :         foreach(lc, root->agginfos)
                   +  + ]
     668                 :             :         {
     669                 :        6586 :                 AggInfo    *agginfo = lfirst_node(AggInfo, lc);
     670                 :        6586 :                 Aggref     *aggref = linitial_node(Aggref, agginfo->aggrefs);
     671                 :             : 
     672                 :             :                 /*
     673                 :             :                  * Add the appropriate component function execution costs to
     674                 :             :                  * appropriate totals.
     675                 :             :                  */
     676   [ +  +  +  + ]:        6586 :                 if (!DO_AGGSPLIT_SKIPFINAL(aggsplit) &&
     677                 :        5936 :                         OidIsValid(agginfo->finalfn_oid))
     678                 :        6382 :                         add_function_cost(root, agginfo->finalfn_oid, NULL,
     679                 :        3191 :                                                           &costs->finalCost);
     680                 :             : 
     681                 :             :                 /*
     682                 :             :                  * If there are direct arguments, treat their evaluation cost like the
     683                 :             :                  * cost of the finalfn.
     684                 :             :                  */
     685         [ +  + ]:        6586 :                 if (aggref->aggdirectargs)
     686                 :             :                 {
     687                 :          44 :                         QualCost        argcosts;
     688                 :             : 
     689                 :          88 :                         cost_qual_eval_node(&argcosts, (Node *) aggref->aggdirectargs,
     690                 :          44 :                                                                 root);
     691                 :          44 :                         costs->finalCost.startup += argcosts.startup;
     692                 :          44 :                         costs->finalCost.per_tuple += argcosts.per_tuple;
     693                 :          44 :                 }
     694                 :        6586 :         }
     695                 :        5685 : }
        

Generated by: LCOV version 2.3.2-1