LCOV - differential code coverage report
Current view: top level - src/backend/optimizer/prep - prepagg.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 97.8 % 186 182 4 182
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 7 7 7
Baseline: 16@8cea358b128 Branches: 87.2 % 148 129 19 129
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 97.8 % 186 182 4 182
Function coverage date bins:
(240..) days: 100.0 % 7 7 7
Branch coverage date bins:
(240..) days: 87.2 % 148 129 19 129

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

Generated by: LCOV version 2.1-beta2-3-g6141622