LCOV - differential code coverage report
Current view: top level - src/backend/optimizer/prep - prepagg.c (source / functions) Coverage Total Hit UNC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 97.8 % 186 182 1 2 1 1 91 18 72 2 105 5
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 7 7 5 1 1 5
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a