LCOV - code coverage report
Current view: top level - src/backend/utils/adt - orderedsetaggs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 88.5 % 489 433
Test Date: 2026-07-22 12:15:41 Functions: 87.0 % 23 20
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 58.8 % 306 180

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * orderedsetaggs.c
       4                 :             :  *      Ordered-set aggregate functions.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *    src/backend/utils/adt/orderedsetaggs.c
      12                 :             :  *
      13                 :             :  *-------------------------------------------------------------------------
      14                 :             :  */
      15                 :             : #include "postgres.h"
      16                 :             : 
      17                 :             : #include <math.h>
      18                 :             : 
      19                 :             : #include "catalog/pg_aggregate.h"
      20                 :             : #include "catalog/pg_operator.h"
      21                 :             : #include "catalog/pg_type.h"
      22                 :             : #include "executor/executor.h"
      23                 :             : #include "miscadmin.h"
      24                 :             : #include "nodes/nodeFuncs.h"
      25                 :             : #include "optimizer/optimizer.h"
      26                 :             : #include "utils/array.h"
      27                 :             : #include "utils/fmgrprotos.h"
      28                 :             : #include "utils/lsyscache.h"
      29                 :             : #include "utils/tuplesort.h"
      30                 :             : 
      31                 :             : 
      32                 :             : /*
      33                 :             :  * Generic support for ordered-set aggregates
      34                 :             :  *
      35                 :             :  * The state for an ordered-set aggregate is divided into a per-group struct
      36                 :             :  * (which is the internal-type transition state datum returned to nodeAgg.c)
      37                 :             :  * and a per-query struct, which contains data and sub-objects that we can
      38                 :             :  * create just once per query because they will not change across groups.
      39                 :             :  * The per-query struct and subsidiary data live in the executor's per-query
      40                 :             :  * memory context, and go away implicitly at ExecutorEnd().
      41                 :             :  *
      42                 :             :  * These structs are set up during the first call of the transition function.
      43                 :             :  * Because we allow nodeAgg.c to merge ordered-set aggregates (but not
      44                 :             :  * hypothetical aggregates) with identical inputs and transition functions,
      45                 :             :  * this info must not depend on the particular aggregate (ie, particular
      46                 :             :  * final-function), nor on the direct argument(s) of the aggregate.
      47                 :             :  */
      48                 :             : 
      49                 :             : typedef struct OSAPerQueryState
      50                 :             : {
      51                 :             :     /* Representative Aggref for this aggregate: */
      52                 :             :     Aggref     *aggref;
      53                 :             :     /* Memory context containing this struct and other per-query data: */
      54                 :             :     MemoryContext qcontext;
      55                 :             :     /* Context for expression evaluation */
      56                 :             :     ExprContext *econtext;
      57                 :             :     /* Do we expect multiple final-function calls within one group? */
      58                 :             :     bool        rescan_needed;
      59                 :             : 
      60                 :             :     /* These fields are used only when accumulating tuples: */
      61                 :             : 
      62                 :             :     /* Tuple descriptor for tuples inserted into sortstate: */
      63                 :             :     TupleDesc   tupdesc;
      64                 :             :     /* Tuple slot we can use for inserting/extracting tuples: */
      65                 :             :     TupleTableSlot *tupslot;
      66                 :             :     /* Per-sort-column sorting information */
      67                 :             :     int         numSortCols;
      68                 :             :     AttrNumber *sortColIdx;
      69                 :             :     Oid        *sortOperators;
      70                 :             :     Oid        *eqOperators;
      71                 :             :     Oid        *sortCollations;
      72                 :             :     bool       *sortNullsFirsts;
      73                 :             :     /* Equality operator call info, created only if needed: */
      74                 :             :     ExprState  *compareTuple;
      75                 :             : 
      76                 :             :     /* These fields are used only when accumulating datums: */
      77                 :             : 
      78                 :             :     /* Info about datatype of datums being sorted: */
      79                 :             :     Oid         sortColType;
      80                 :             :     int16       typLen;
      81                 :             :     bool        typByVal;
      82                 :             :     char        typAlign;
      83                 :             :     /* Info about sort ordering: */
      84                 :             :     Oid         sortOperator;
      85                 :             :     Oid         eqOperator;
      86                 :             :     Oid         sortCollation;
      87                 :             :     bool        sortNullsFirst;
      88                 :             :     /* Equality operator call info, created only if needed: */
      89                 :             :     FmgrInfo    equalfn;
      90                 :             : } OSAPerQueryState;
      91                 :             : 
      92                 :             : typedef struct OSAPerGroupState
      93                 :             : {
      94                 :             :     /* Link to the per-query state for this aggregate: */
      95                 :             :     OSAPerQueryState *qstate;
      96                 :             :     /* Memory context containing per-group data: */
      97                 :             :     MemoryContext gcontext;
      98                 :             :     /* Sort object we're accumulating data in: */
      99                 :             :     Tuplesortstate *sortstate;
     100                 :             :     /* Number of normal rows inserted into sortstate: */
     101                 :             :     int64       number_of_rows;
     102                 :             :     /* Have we already done tuplesort_performsort? */
     103                 :             :     bool        sort_done;
     104                 :             : } OSAPerGroupState;
     105                 :             : 
     106                 :             : static void ordered_set_shutdown(Datum arg);
     107                 :             : 
     108                 :             : 
     109                 :             : /*
     110                 :             :  * Set up working state for an ordered-set aggregate
     111                 :             :  */
     112                 :             : static OSAPerGroupState *
     113                 :         433 : ordered_set_startup(FunctionCallInfo fcinfo, bool use_tuples)
     114                 :             : {
     115                 :             :     OSAPerGroupState *osastate;
     116                 :             :     OSAPerQueryState *qstate;
     117                 :             :     MemoryContext gcontext;
     118                 :             :     MemoryContext oldcontext;
     119                 :             :     int         tuplesortopt;
     120                 :             : 
     121                 :             :     /*
     122                 :             :      * Check we're called as aggregate (and not a window function), and get
     123                 :             :      * the Agg node's group-lifespan context (which might change from group to
     124                 :             :      * group, so we shouldn't cache it in the per-query state).
     125                 :             :      */
     126         [ -  + ]:         433 :     if (AggCheckCallContext(fcinfo, &gcontext) != AGG_CONTEXT_AGGREGATE)
     127         [ #  # ]:           0 :         elog(ERROR, "ordered-set aggregate called in non-aggregate context");
     128                 :             : 
     129                 :             :     /*
     130                 :             :      * We keep a link to the per-query state in fn_extra; if it's not there,
     131                 :             :      * create it, and do the per-query setup we need.
     132                 :             :      */
     133                 :         433 :     qstate = (OSAPerQueryState *) fcinfo->flinfo->fn_extra;
     134         [ +  + ]:         433 :     if (qstate == NULL)
     135                 :             :     {
     136                 :             :         Aggref     *aggref;
     137                 :             :         MemoryContext qcontext;
     138                 :             :         List       *sortlist;
     139                 :             :         int         numSortCols;
     140                 :             : 
     141                 :             :         /* Get the Aggref so we can examine aggregate's arguments */
     142                 :         163 :         aggref = AggGetAggref(fcinfo);
     143         [ -  + ]:         163 :         if (!aggref)
     144         [ #  # ]:           0 :             elog(ERROR, "ordered-set aggregate called in non-aggregate context");
     145         [ -  + ]:         163 :         if (!AGGKIND_IS_ORDERED_SET(aggref->aggkind))
     146         [ #  # ]:           0 :             elog(ERROR, "ordered-set aggregate support function called for non-ordered-set aggregate");
     147                 :             : 
     148                 :             :         /*
     149                 :             :          * Prepare per-query structures in the fn_mcxt, which we assume is the
     150                 :             :          * executor's per-query context; in any case it's the right place to
     151                 :             :          * keep anything found via fn_extra.
     152                 :             :          */
     153                 :         163 :         qcontext = fcinfo->flinfo->fn_mcxt;
     154                 :         163 :         oldcontext = MemoryContextSwitchTo(qcontext);
     155                 :             : 
     156                 :         163 :         qstate = palloc0_object(OSAPerQueryState);
     157                 :         163 :         qstate->aggref = aggref;
     158                 :         163 :         qstate->qcontext = qcontext;
     159                 :             : 
     160                 :             :         /* We need to support rescans if the trans state is shared */
     161                 :         163 :         qstate->rescan_needed = AggStateIsShared(fcinfo);
     162                 :             : 
     163                 :             :         /* Extract the sort information */
     164                 :         163 :         sortlist = aggref->aggorder;
     165                 :         163 :         numSortCols = list_length(sortlist);
     166                 :             : 
     167         [ +  + ]:         163 :         if (use_tuples)
     168                 :             :         {
     169                 :          66 :             bool        ishypothetical = (aggref->aggkind == AGGKIND_HYPOTHETICAL);
     170                 :             :             ListCell   *lc;
     171                 :             :             int         i;
     172                 :             : 
     173         [ +  - ]:          66 :             if (ishypothetical)
     174                 :          66 :                 numSortCols++;  /* make space for flag column */
     175                 :          66 :             qstate->numSortCols = numSortCols;
     176                 :          66 :             qstate->sortColIdx = (AttrNumber *) palloc(numSortCols * sizeof(AttrNumber));
     177                 :          66 :             qstate->sortOperators = (Oid *) palloc(numSortCols * sizeof(Oid));
     178                 :          66 :             qstate->eqOperators = (Oid *) palloc(numSortCols * sizeof(Oid));
     179                 :          66 :             qstate->sortCollations = (Oid *) palloc(numSortCols * sizeof(Oid));
     180                 :          66 :             qstate->sortNullsFirsts = (bool *) palloc(numSortCols * sizeof(bool));
     181                 :             : 
     182                 :          66 :             i = 0;
     183   [ +  -  +  +  :         165 :             foreach(lc, sortlist)
                   +  + ]
     184                 :             :             {
     185                 :          99 :                 SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
     186                 :          99 :                 TargetEntry *tle = get_sortgroupclause_tle(sortcl,
     187                 :             :                                                            aggref->args);
     188                 :             : 
     189                 :             :                 /* the parser should have made sure of this */
     190                 :             :                 Assert(OidIsValid(sortcl->sortop));
     191                 :             : 
     192                 :          99 :                 qstate->sortColIdx[i] = tle->resno;
     193                 :          99 :                 qstate->sortOperators[i] = sortcl->sortop;
     194                 :          99 :                 qstate->eqOperators[i] = sortcl->eqop;
     195                 :          99 :                 qstate->sortCollations[i] = exprCollation((Node *) tle->expr);
     196                 :          99 :                 qstate->sortNullsFirsts[i] = sortcl->nulls_first;
     197                 :          99 :                 i++;
     198                 :             :             }
     199                 :             : 
     200         [ +  - ]:          66 :             if (ishypothetical)
     201                 :             :             {
     202                 :             :                 /* Add an integer flag column as the last sort column */
     203                 :          66 :                 qstate->sortColIdx[i] = list_length(aggref->args) + 1;
     204                 :          66 :                 qstate->sortOperators[i] = Int4LessOperator;
     205                 :          66 :                 qstate->eqOperators[i] = Int4EqualOperator;
     206                 :          66 :                 qstate->sortCollations[i] = InvalidOid;
     207                 :          66 :                 qstate->sortNullsFirsts[i] = false;
     208                 :          66 :                 i++;
     209                 :             :             }
     210                 :             : 
     211                 :             :             Assert(i == numSortCols);
     212                 :             : 
     213                 :             :             /*
     214                 :             :              * Get a tupledesc corresponding to the aggregated inputs
     215                 :             :              * (including sort expressions) of the agg.
     216                 :             :              */
     217                 :          66 :             qstate->tupdesc = ExecTypeFromTL(aggref->args);
     218                 :             : 
     219                 :             :             /* If we need a flag column, hack the tupledesc to include that */
     220         [ +  - ]:          66 :             if (ishypothetical)
     221                 :             :             {
     222                 :             :                 TupleDesc   newdesc;
     223                 :          66 :                 int         natts = qstate->tupdesc->natts;
     224                 :             : 
     225                 :          66 :                 newdesc = CreateTemplateTupleDesc(natts + 1);
     226         [ +  + ]:         165 :                 for (i = 1; i <= natts; i++)
     227                 :          99 :                     TupleDescCopyEntry(newdesc, i, qstate->tupdesc, i);
     228                 :             : 
     229                 :          66 :                 TupleDescInitEntry(newdesc,
     230                 :          66 :                                    (AttrNumber) ++natts,
     231                 :             :                                    "flag",
     232                 :             :                                    INT4OID,
     233                 :             :                                    -1,
     234                 :             :                                    0);
     235                 :             : 
     236                 :          66 :                 TupleDescFinalize(newdesc);
     237                 :          66 :                 FreeTupleDesc(qstate->tupdesc);
     238                 :          66 :                 qstate->tupdesc = newdesc;
     239                 :             :             }
     240                 :             : 
     241                 :             :             /* Create slot we'll use to store/retrieve rows */
     242                 :          66 :             qstate->tupslot = MakeSingleTupleTableSlot(qstate->tupdesc,
     243                 :             :                                                        &TTSOpsMinimalTuple);
     244                 :             :         }
     245                 :             :         else
     246                 :             :         {
     247                 :             :             /* Sort single datums */
     248                 :             :             SortGroupClause *sortcl;
     249                 :             :             TargetEntry *tle;
     250                 :             : 
     251   [ +  -  -  + ]:          97 :             if (numSortCols != 1 || aggref->aggkind == AGGKIND_HYPOTHETICAL)
     252         [ #  # ]:           0 :                 elog(ERROR, "ordered-set aggregate support function does not support multiple aggregated columns");
     253                 :             : 
     254                 :          97 :             sortcl = (SortGroupClause *) linitial(sortlist);
     255                 :          97 :             tle = get_sortgroupclause_tle(sortcl, aggref->args);
     256                 :             : 
     257                 :             :             /* the parser should have made sure of this */
     258                 :             :             Assert(OidIsValid(sortcl->sortop));
     259                 :             : 
     260                 :             :             /* Save sort ordering info */
     261                 :          97 :             qstate->sortColType = exprType((Node *) tle->expr);
     262                 :          97 :             qstate->sortOperator = sortcl->sortop;
     263                 :          97 :             qstate->eqOperator = sortcl->eqop;
     264                 :          97 :             qstate->sortCollation = exprCollation((Node *) tle->expr);
     265                 :          97 :             qstate->sortNullsFirst = sortcl->nulls_first;
     266                 :             : 
     267                 :             :             /* Save datatype info */
     268                 :          97 :             get_typlenbyvalalign(qstate->sortColType,
     269                 :             :                                  &qstate->typLen,
     270                 :             :                                  &qstate->typByVal,
     271                 :             :                                  &qstate->typAlign);
     272                 :             :         }
     273                 :             : 
     274                 :         163 :         fcinfo->flinfo->fn_extra = qstate;
     275                 :             : 
     276                 :         163 :         MemoryContextSwitchTo(oldcontext);
     277                 :             :     }
     278                 :             : 
     279                 :             :     /* Now build the stuff we need in group-lifespan context */
     280                 :         433 :     oldcontext = MemoryContextSwitchTo(gcontext);
     281                 :             : 
     282                 :         433 :     osastate = palloc_object(OSAPerGroupState);
     283                 :         433 :     osastate->qstate = qstate;
     284                 :         433 :     osastate->gcontext = gcontext;
     285                 :             : 
     286                 :         433 :     tuplesortopt = TUPLESORT_NONE;
     287                 :             : 
     288         [ +  + ]:         433 :     if (qstate->rescan_needed)
     289                 :          16 :         tuplesortopt |= TUPLESORT_RANDOMACCESS;
     290                 :             : 
     291                 :             :     /*
     292                 :             :      * Initialize tuplesort object.
     293                 :             :      */
     294         [ +  + ]:         433 :     if (use_tuples)
     295                 :         179 :         osastate->sortstate = tuplesort_begin_heap(qstate->tupdesc,
     296                 :             :                                                    qstate->numSortCols,
     297                 :             :                                                    qstate->sortColIdx,
     298                 :             :                                                    qstate->sortOperators,
     299                 :             :                                                    qstate->sortCollations,
     300                 :             :                                                    qstate->sortNullsFirsts,
     301                 :             :                                                    work_mem,
     302                 :             :                                                    NULL,
     303                 :             :                                                    tuplesortopt);
     304                 :             :     else
     305                 :         254 :         osastate->sortstate = tuplesort_begin_datum(qstate->sortColType,
     306                 :             :                                                     qstate->sortOperator,
     307                 :             :                                                     qstate->sortCollation,
     308                 :         254 :                                                     qstate->sortNullsFirst,
     309                 :             :                                                     work_mem,
     310                 :             :                                                     NULL,
     311                 :             :                                                     tuplesortopt);
     312                 :             : 
     313                 :         433 :     osastate->number_of_rows = 0;
     314                 :         433 :     osastate->sort_done = false;
     315                 :             : 
     316                 :             :     /* Now register a shutdown callback to clean things up at end of group */
     317                 :         433 :     AggRegisterCallback(fcinfo,
     318                 :             :                         ordered_set_shutdown,
     319                 :             :                         PointerGetDatum(osastate));
     320                 :             : 
     321                 :         433 :     MemoryContextSwitchTo(oldcontext);
     322                 :             : 
     323                 :         433 :     return osastate;
     324                 :             : }
     325                 :             : 
     326                 :             : /*
     327                 :             :  * Clean up when evaluation of an ordered-set aggregate is complete.
     328                 :             :  *
     329                 :             :  * We don't need to bother freeing objects in the per-group memory context,
     330                 :             :  * since that will get reset anyway by nodeAgg.c; nor should we free anything
     331                 :             :  * in the per-query context, which will get cleared (if this was the last
     332                 :             :  * group) by ExecutorEnd.  But we must take care to release any potential
     333                 :             :  * non-memory resources.
     334                 :             :  *
     335                 :             :  * In the case where we're not expecting multiple finalfn calls, we could
     336                 :             :  * arguably rely on the finalfn to clean up; but it's easier and more testable
     337                 :             :  * if we just do it the same way in either case.
     338                 :             :  */
     339                 :             : static void
     340                 :         433 : ordered_set_shutdown(Datum arg)
     341                 :             : {
     342                 :         433 :     OSAPerGroupState *osastate = (OSAPerGroupState *) DatumGetPointer(arg);
     343                 :             : 
     344                 :             :     /* Tuplesort object might have temp files. */
     345         [ +  - ]:         433 :     if (osastate->sortstate)
     346                 :         433 :         tuplesort_end(osastate->sortstate);
     347                 :         433 :     osastate->sortstate = NULL;
     348                 :             :     /* The tupleslot probably can't be holding a pin, but let's be safe. */
     349         [ +  + ]:         433 :     if (osastate->qstate->tupslot)
     350                 :         179 :         ExecClearTuple(osastate->qstate->tupslot);
     351                 :         433 : }
     352                 :             : 
     353                 :             : 
     354                 :             : /*
     355                 :             :  * Generic transition function for ordered-set aggregates
     356                 :             :  * with a single input column in which we want to suppress nulls
     357                 :             :  */
     358                 :             : Datum
     359                 :      802132 : ordered_set_transition(PG_FUNCTION_ARGS)
     360                 :             : {
     361                 :             :     OSAPerGroupState *osastate;
     362                 :             : 
     363                 :             :     /* If first call, create the transition state workspace */
     364         [ +  + ]:      802132 :     if (PG_ARGISNULL(0))
     365                 :         254 :         osastate = ordered_set_startup(fcinfo, false);
     366                 :             :     else
     367                 :      801878 :         osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     368                 :             : 
     369                 :             :     /* Load the datum into the tuplesort object, but only if it's not null */
     370         [ +  + ]:      802132 :     if (!PG_ARGISNULL(1))
     371                 :             :     {
     372                 :      802084 :         tuplesort_putdatum(osastate->sortstate, PG_GETARG_DATUM(1), false);
     373                 :      802084 :         osastate->number_of_rows++;
     374                 :             :     }
     375                 :             : 
     376                 :      802132 :     PG_RETURN_POINTER(osastate);
     377                 :             : }
     378                 :             : 
     379                 :             : /*
     380                 :             :  * Generic transition function for ordered-set aggregates
     381                 :             :  * with (potentially) multiple aggregated input columns
     382                 :             :  */
     383                 :             : Datum
     384                 :      201525 : ordered_set_transition_multi(PG_FUNCTION_ARGS)
     385                 :             : {
     386                 :             :     OSAPerGroupState *osastate;
     387                 :             :     TupleTableSlot *slot;
     388                 :             :     int         nargs;
     389                 :             :     int         i;
     390                 :             : 
     391                 :             :     /* If first call, create the transition state workspace */
     392         [ +  + ]:      201525 :     if (PG_ARGISNULL(0))
     393                 :         179 :         osastate = ordered_set_startup(fcinfo, true);
     394                 :             :     else
     395                 :      201346 :         osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     396                 :             : 
     397                 :             :     /* Form a tuple from all the other inputs besides the transition value */
     398                 :      201525 :     slot = osastate->qstate->tupslot;
     399                 :      201525 :     ExecClearTuple(slot);
     400                 :      201525 :     nargs = PG_NARGS() - 1;
     401         [ +  + ]:      803483 :     for (i = 0; i < nargs; i++)
     402                 :             :     {
     403                 :      601958 :         slot->tts_values[i] = PG_GETARG_DATUM(i + 1);
     404                 :      601958 :         slot->tts_isnull[i] = PG_ARGISNULL(i + 1);
     405                 :             :     }
     406         [ +  - ]:      201525 :     if (osastate->qstate->aggref->aggkind == AGGKIND_HYPOTHETICAL)
     407                 :             :     {
     408                 :             :         /* Add a zero flag value to mark this row as a normal input row */
     409                 :      201525 :         slot->tts_values[i] = Int32GetDatum(0);
     410                 :      201525 :         slot->tts_isnull[i] = false;
     411                 :      201525 :         i++;
     412                 :             :     }
     413                 :             :     Assert(i == slot->tts_tupleDescriptor->natts);
     414                 :      201525 :     ExecStoreVirtualTuple(slot);
     415                 :             : 
     416                 :             :     /* Load the row into the tuplesort object */
     417                 :      201525 :     tuplesort_puttupleslot(osastate->sortstate, slot);
     418                 :      201525 :     osastate->number_of_rows++;
     419                 :             : 
     420                 :      201525 :     PG_RETURN_POINTER(osastate);
     421                 :             : }
     422                 :             : 
     423                 :             : 
     424                 :             : /*
     425                 :             :  * percentile_disc(float8) within group(anyelement) - discrete percentile
     426                 :             :  */
     427                 :             : Datum
     428                 :         180 : percentile_disc_final(PG_FUNCTION_ARGS)
     429                 :             : {
     430                 :             :     OSAPerGroupState *osastate;
     431                 :             :     double      percentile;
     432                 :             :     Datum       val;
     433                 :             :     bool        isnull;
     434                 :             :     int64       rownum;
     435                 :             : 
     436                 :             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
     437                 :             : 
     438                 :             :     /* Get and check the percentile argument */
     439         [ -  + ]:         180 :     if (PG_ARGISNULL(1))
     440                 :           0 :         PG_RETURN_NULL();
     441                 :             : 
     442                 :         180 :     percentile = PG_GETARG_FLOAT8(1);
     443                 :             : 
     444   [ +  -  +  -  :         180 :     if (percentile < 0 || percentile > 1 || isnan(percentile))
                   -  + ]
     445         [ #  # ]:           0 :         ereport(ERROR,
     446                 :             :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     447                 :             :                  errmsg("percentile value %g is not between 0 and 1",
     448                 :             :                         percentile)));
     449                 :             : 
     450                 :             :     /* If there were no regular rows, the result is NULL */
     451         [ +  + ]:         180 :     if (PG_ARGISNULL(0))
     452                 :          36 :         PG_RETURN_NULL();
     453                 :             : 
     454                 :         144 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     455                 :             : 
     456                 :             :     /* number_of_rows could be zero if we only saw NULL input values */
     457         [ -  + ]:         144 :     if (osastate->number_of_rows == 0)
     458                 :           0 :         PG_RETURN_NULL();
     459                 :             : 
     460                 :             :     /* Finish the sort, or rescan if we already did */
     461         [ +  + ]:         144 :     if (!osastate->sort_done)
     462                 :             :     {
     463                 :         128 :         tuplesort_performsort(osastate->sortstate);
     464                 :         128 :         osastate->sort_done = true;
     465                 :             :     }
     466                 :             :     else
     467                 :          16 :         tuplesort_rescan(osastate->sortstate);
     468                 :             : 
     469                 :             :     /*----------
     470                 :             :      * We need the smallest K such that (K/N) >= percentile.
     471                 :             :      * N>0, therefore K >= N*percentile, therefore K = ceil(N*percentile).
     472                 :             :      * So we skip K-1 rows (if K>0) and return the next row fetched.
     473                 :             :      *----------
     474                 :             :      */
     475                 :         144 :     rownum = (int64) ceil(percentile * osastate->number_of_rows);
     476                 :             :     Assert(rownum <= osastate->number_of_rows);
     477                 :             : 
     478         [ +  + ]:         144 :     if (rownum > 1)
     479                 :             :     {
     480         [ -  + ]:         104 :         if (!tuplesort_skiptuples(osastate->sortstate, rownum - 1, true))
     481         [ #  # ]:           0 :             elog(ERROR, "missing row in percentile_disc");
     482                 :             :     }
     483                 :             : 
     484         [ -  + ]:         144 :     if (!tuplesort_getdatum(osastate->sortstate, true, true, &val, &isnull,
     485                 :             :                             NULL))
     486         [ #  # ]:           0 :         elog(ERROR, "missing row in percentile_disc");
     487                 :             : 
     488                 :             :     /* We shouldn't have stored any nulls, but do the right thing anyway */
     489         [ -  + ]:         144 :     if (isnull)
     490                 :           0 :         PG_RETURN_NULL();
     491                 :             :     else
     492                 :         144 :         PG_RETURN_DATUM(val);
     493                 :             : }
     494                 :             : 
     495                 :             : 
     496                 :             : /*
     497                 :             :  * For percentile_cont, we need a way to interpolate between consecutive
     498                 :             :  * values. Use a helper function for that, so that we can share the rest
     499                 :             :  * of the code between types.
     500                 :             :  */
     501                 :             : typedef Datum (*LerpFunc) (Datum lo, Datum hi, double pct);
     502                 :             : 
     503                 :             : static Datum
     504                 :          85 : float8_lerp(Datum lo, Datum hi, double pct)
     505                 :             : {
     506                 :          85 :     double      loval = DatumGetFloat8(lo);
     507                 :          85 :     double      hival = DatumGetFloat8(hi);
     508                 :             : 
     509                 :          85 :     return Float8GetDatum(loval + (pct * (hival - loval)));
     510                 :             : }
     511                 :             : 
     512                 :             : static Datum
     513                 :           0 : interval_lerp(Datum lo, Datum hi, double pct)
     514                 :             : {
     515                 :           0 :     Datum       diff_result = DirectFunctionCall2(interval_mi, hi, lo);
     516                 :           0 :     Datum       mul_result = DirectFunctionCall2(interval_mul,
     517                 :             :                                                  diff_result,
     518                 :             :                                                  Float8GetDatumFast(pct));
     519                 :             : 
     520                 :           0 :     return DirectFunctionCall2(interval_pl, mul_result, lo);
     521                 :             : }
     522                 :             : 
     523                 :             : /*
     524                 :             :  * Continuous percentile
     525                 :             :  */
     526                 :             : static Datum
     527                 :          66 : percentile_cont_final_common(FunctionCallInfo fcinfo,
     528                 :             :                              Oid expect_type,
     529                 :             :                              LerpFunc lerpfunc)
     530                 :             : {
     531                 :             :     OSAPerGroupState *osastate;
     532                 :             :     double      percentile;
     533                 :          66 :     int64       first_row = 0;
     534                 :          66 :     int64       second_row = 0;
     535                 :             :     Datum       val;
     536                 :             :     Datum       first_val;
     537                 :             :     Datum       second_val;
     538                 :             :     double      proportion;
     539                 :             :     bool        isnull;
     540                 :             : 
     541                 :             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
     542                 :             : 
     543                 :             :     /* Get and check the percentile argument */
     544         [ -  + ]:          66 :     if (PG_ARGISNULL(1))
     545                 :           0 :         PG_RETURN_NULL();
     546                 :             : 
     547                 :          66 :     percentile = PG_GETARG_FLOAT8(1);
     548                 :             : 
     549   [ +  -  +  -  :          66 :     if (percentile < 0 || percentile > 1 || isnan(percentile))
                   -  + ]
     550         [ #  # ]:           0 :         ereport(ERROR,
     551                 :             :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     552                 :             :                  errmsg("percentile value %g is not between 0 and 1",
     553                 :             :                         percentile)));
     554                 :             : 
     555                 :             :     /* If there were no regular rows, the result is NULL */
     556         [ -  + ]:          66 :     if (PG_ARGISNULL(0))
     557                 :           0 :         PG_RETURN_NULL();
     558                 :             : 
     559                 :          66 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     560                 :             : 
     561                 :             :     /* number_of_rows could be zero if we only saw NULL input values */
     562         [ -  + ]:          66 :     if (osastate->number_of_rows == 0)
     563                 :           0 :         PG_RETURN_NULL();
     564                 :             : 
     565                 :             :     Assert(expect_type == osastate->qstate->sortColType);
     566                 :             : 
     567                 :             :     /* Finish the sort, or rescan if we already did */
     568         [ +  - ]:          66 :     if (!osastate->sort_done)
     569                 :             :     {
     570                 :          66 :         tuplesort_performsort(osastate->sortstate);
     571                 :          66 :         osastate->sort_done = true;
     572                 :             :     }
     573                 :             :     else
     574                 :           0 :         tuplesort_rescan(osastate->sortstate);
     575                 :             : 
     576                 :          66 :     first_row = floor(percentile * (osastate->number_of_rows - 1));
     577                 :          66 :     second_row = ceil(percentile * (osastate->number_of_rows - 1));
     578                 :             : 
     579                 :             :     Assert(first_row < osastate->number_of_rows);
     580                 :             : 
     581         [ -  + ]:          66 :     if (!tuplesort_skiptuples(osastate->sortstate, first_row, true))
     582         [ #  # ]:           0 :         elog(ERROR, "missing row in percentile_cont");
     583                 :             : 
     584         [ -  + ]:          66 :     if (!tuplesort_getdatum(osastate->sortstate, true, true, &first_val,
     585                 :             :                             &isnull, NULL))
     586         [ #  # ]:           0 :         elog(ERROR, "missing row in percentile_cont");
     587         [ -  + ]:          66 :     if (isnull)
     588                 :           0 :         PG_RETURN_NULL();
     589                 :             : 
     590         [ +  + ]:          66 :     if (first_row == second_row)
     591                 :             :     {
     592                 :          21 :         val = first_val;
     593                 :             :     }
     594                 :             :     else
     595                 :             :     {
     596         [ -  + ]:          45 :         if (!tuplesort_getdatum(osastate->sortstate, true, true, &second_val,
     597                 :             :                                 &isnull, NULL))
     598         [ #  # ]:           0 :             elog(ERROR, "missing row in percentile_cont");
     599                 :             : 
     600         [ -  + ]:          45 :         if (isnull)
     601                 :           0 :             PG_RETURN_NULL();
     602                 :             : 
     603                 :          45 :         proportion = (percentile * (osastate->number_of_rows - 1)) - first_row;
     604                 :          45 :         val = lerpfunc(first_val, second_val, proportion);
     605                 :             :     }
     606                 :             : 
     607                 :          66 :     PG_RETURN_DATUM(val);
     608                 :             : }
     609                 :             : 
     610                 :             : /*
     611                 :             :  * percentile_cont(float8) within group (float8)    - continuous percentile
     612                 :             :  */
     613                 :             : Datum
     614                 :          66 : percentile_cont_float8_final(PG_FUNCTION_ARGS)
     615                 :             : {
     616                 :          66 :     return percentile_cont_final_common(fcinfo, FLOAT8OID, float8_lerp);
     617                 :             : }
     618                 :             : 
     619                 :             : /*
     620                 :             :  * percentile_cont(float8) within group (interval)  - continuous percentile
     621                 :             :  */
     622                 :             : Datum
     623                 :           0 : percentile_cont_interval_final(PG_FUNCTION_ARGS)
     624                 :             : {
     625                 :           0 :     return percentile_cont_final_common(fcinfo, INTERVALOID, interval_lerp);
     626                 :             : }
     627                 :             : 
     628                 :             : 
     629                 :             : /*
     630                 :             :  * Support code for handling arrays of percentiles
     631                 :             :  *
     632                 :             :  * Note: in each pct_info entry, second_row should be equal to or
     633                 :             :  * exactly one more than first_row.
     634                 :             :  */
     635                 :             : struct pct_info
     636                 :             : {
     637                 :             :     int64       first_row;      /* first row to sample */
     638                 :             :     int64       second_row;     /* possible second row to sample */
     639                 :             :     double      proportion;     /* interpolation fraction */
     640                 :             :     int         idx;            /* index of this item in original array */
     641                 :             : };
     642                 :             : 
     643                 :             : /*
     644                 :             :  * Sort comparator to sort pct_infos by first_row then second_row
     645                 :             :  */
     646                 :             : static int
     647                 :         200 : pct_info_cmp(const void *pa, const void *pb)
     648                 :             : {
     649                 :         200 :     const struct pct_info *a = (const struct pct_info *) pa;
     650                 :         200 :     const struct pct_info *b = (const struct pct_info *) pb;
     651                 :             : 
     652         [ +  + ]:         200 :     if (a->first_row != b->first_row)
     653         [ +  + ]:         172 :         return (a->first_row < b->first_row) ? -1 : 1;
     654         [ +  + ]:          28 :     if (a->second_row != b->second_row)
     655         [ -  + ]:           4 :         return (a->second_row < b->second_row) ? -1 : 1;
     656                 :          24 :     return 0;
     657                 :             : }
     658                 :             : 
     659                 :             : /*
     660                 :             :  * Construct array showing which rows to sample for percentiles.
     661                 :             :  */
     662                 :             : static struct pct_info *
     663                 :          20 : setup_pct_info(int num_percentiles,
     664                 :             :                const Datum *percentiles_datum,
     665                 :             :                const bool *percentiles_null,
     666                 :             :                int64 rowcount,
     667                 :             :                bool continuous)
     668                 :             : {
     669                 :             :     struct pct_info *pct_info;
     670                 :             :     int         i;
     671                 :             : 
     672                 :          20 :     pct_info = (struct pct_info *) palloc(num_percentiles * sizeof(struct pct_info));
     673                 :             : 
     674         [ +  + ]:         148 :     for (i = 0; i < num_percentiles; i++)
     675                 :             :     {
     676                 :         128 :         pct_info[i].idx = i;
     677                 :             : 
     678         [ +  + ]:         128 :         if (percentiles_null[i])
     679                 :             :         {
     680                 :             :             /* dummy entry for any NULL in array */
     681                 :           8 :             pct_info[i].first_row = 0;
     682                 :           8 :             pct_info[i].second_row = 0;
     683                 :           8 :             pct_info[i].proportion = 0;
     684                 :             :         }
     685                 :             :         else
     686                 :             :         {
     687                 :         120 :             double      p = DatumGetFloat8(percentiles_datum[i]);
     688                 :             : 
     689   [ +  -  +  -  :         120 :             if (p < 0 || p > 1 || isnan(p))
                   -  + ]
     690         [ #  # ]:           0 :                 ereport(ERROR,
     691                 :             :                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     692                 :             :                          errmsg("percentile value %g is not between 0 and 1",
     693                 :             :                                 p)));
     694                 :             : 
     695         [ +  + ]:         120 :             if (continuous)
     696                 :             :             {
     697                 :          64 :                 pct_info[i].first_row = 1 + floor(p * (rowcount - 1));
     698                 :          64 :                 pct_info[i].second_row = 1 + ceil(p * (rowcount - 1));
     699                 :          64 :                 pct_info[i].proportion = (p * (rowcount - 1)) - floor(p * (rowcount - 1));
     700                 :             :             }
     701                 :             :             else
     702                 :             :             {
     703                 :             :                 /*----------
     704                 :             :                  * We need the smallest K such that (K/N) >= percentile.
     705                 :             :                  * N>0, therefore K >= N*percentile, therefore
     706                 :             :                  * K = ceil(N*percentile); but not less than 1.
     707                 :             :                  *----------
     708                 :             :                  */
     709                 :          56 :                 int64       row = (int64) ceil(p * rowcount);
     710                 :             : 
     711                 :          56 :                 row = Max(1, row);
     712                 :          56 :                 pct_info[i].first_row = row;
     713                 :          56 :                 pct_info[i].second_row = row;
     714                 :          56 :                 pct_info[i].proportion = 0;
     715                 :             :             }
     716                 :             :         }
     717                 :             :     }
     718                 :             : 
     719                 :             :     /*
     720                 :             :      * The parameter array wasn't necessarily in sorted order, but we need to
     721                 :             :      * visit the rows in order, so sort by first_row/second_row.
     722                 :             :      */
     723                 :          20 :     qsort(pct_info, num_percentiles, sizeof(struct pct_info), pct_info_cmp);
     724                 :             : 
     725                 :          20 :     return pct_info;
     726                 :             : }
     727                 :             : 
     728                 :             : /*
     729                 :             :  * percentile_disc(float8[]) within group (anyelement)  - discrete percentiles
     730                 :             :  */
     731                 :             : Datum
     732                 :          12 : percentile_disc_multi_final(PG_FUNCTION_ARGS)
     733                 :             : {
     734                 :             :     OSAPerGroupState *osastate;
     735                 :             :     ArrayType  *param;
     736                 :             :     Datum      *percentiles_datum;
     737                 :             :     bool       *percentiles_null;
     738                 :             :     int         num_percentiles;
     739                 :             :     struct pct_info *pct_info;
     740                 :             :     Datum      *result_datum;
     741                 :             :     bool       *result_isnull;
     742                 :          12 :     int64       rownum = 0;
     743                 :          12 :     Datum       val = (Datum) 0;
     744                 :          12 :     bool        isnull = true;
     745                 :             :     int         i;
     746                 :             : 
     747                 :             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
     748                 :             : 
     749                 :             :     /* If there were no regular rows, the result is NULL */
     750         [ -  + ]:          12 :     if (PG_ARGISNULL(0))
     751                 :           0 :         PG_RETURN_NULL();
     752                 :             : 
     753                 :          12 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     754                 :             : 
     755                 :             :     /* number_of_rows could be zero if we only saw NULL input values */
     756         [ -  + ]:          12 :     if (osastate->number_of_rows == 0)
     757                 :           0 :         PG_RETURN_NULL();
     758                 :             : 
     759                 :             :     /* Deconstruct the percentile-array input */
     760         [ -  + ]:          12 :     if (PG_ARGISNULL(1))
     761                 :           0 :         PG_RETURN_NULL();
     762                 :          12 :     param = PG_GETARG_ARRAYTYPE_P(1);
     763                 :             : 
     764                 :          12 :     deconstruct_array_builtin(param, FLOAT8OID,
     765                 :             :                               &percentiles_datum,
     766                 :             :                               &percentiles_null,
     767                 :             :                               &num_percentiles);
     768                 :             : 
     769         [ -  + ]:          12 :     if (num_percentiles == 0)
     770                 :           0 :         PG_RETURN_POINTER(construct_empty_array(osastate->qstate->sortColType));
     771                 :             : 
     772                 :          12 :     pct_info = setup_pct_info(num_percentiles,
     773                 :             :                               percentiles_datum,
     774                 :             :                               percentiles_null,
     775                 :             :                               osastate->number_of_rows,
     776                 :             :                               false);
     777                 :             : 
     778                 :          12 :     result_datum = (Datum *) palloc(num_percentiles * sizeof(Datum));
     779                 :          12 :     result_isnull = (bool *) palloc(num_percentiles * sizeof(bool));
     780                 :             : 
     781                 :             :     /*
     782                 :             :      * Start by dealing with any nulls in the param array - those are sorted
     783                 :             :      * to the front on row=0, so set the corresponding result indexes to null
     784                 :             :      */
     785         [ +  - ]:          20 :     for (i = 0; i < num_percentiles; i++)
     786                 :             :     {
     787                 :          20 :         int         idx = pct_info[i].idx;
     788                 :             : 
     789         [ +  + ]:          20 :         if (pct_info[i].first_row > 0)
     790                 :          12 :             break;
     791                 :             : 
     792                 :           8 :         result_datum[idx] = (Datum) 0;
     793                 :           8 :         result_isnull[idx] = true;
     794                 :             :     }
     795                 :             : 
     796                 :             :     /*
     797                 :             :      * If there's anything left after doing the nulls, then grind the input
     798                 :             :      * and extract the needed values
     799                 :             :      */
     800         [ +  - ]:          12 :     if (i < num_percentiles)
     801                 :             :     {
     802                 :             :         /* Finish the sort, or rescan if we already did */
     803         [ +  - ]:          12 :         if (!osastate->sort_done)
     804                 :             :         {
     805                 :          12 :             tuplesort_performsort(osastate->sortstate);
     806                 :          12 :             osastate->sort_done = true;
     807                 :             :         }
     808                 :             :         else
     809                 :           0 :             tuplesort_rescan(osastate->sortstate);
     810                 :             : 
     811         [ +  + ]:          68 :         for (; i < num_percentiles; i++)
     812                 :             :         {
     813                 :          56 :             int64       target_row = pct_info[i].first_row;
     814                 :          56 :             int         idx = pct_info[i].idx;
     815                 :             : 
     816                 :             :             /* Advance to target row, if not already there */
     817         [ +  - ]:          56 :             if (target_row > rownum)
     818                 :             :             {
     819         [ -  + ]:          56 :                 if (!tuplesort_skiptuples(osastate->sortstate, target_row - rownum - 1, true))
     820         [ #  # ]:           0 :                     elog(ERROR, "missing row in percentile_disc");
     821                 :             : 
     822         [ -  + ]:          56 :                 if (!tuplesort_getdatum(osastate->sortstate, true, true, &val,
     823                 :             :                                         &isnull, NULL))
     824         [ #  # ]:           0 :                     elog(ERROR, "missing row in percentile_disc");
     825                 :             : 
     826                 :          56 :                 rownum = target_row;
     827                 :             :             }
     828                 :             : 
     829                 :          56 :             result_datum[idx] = val;
     830                 :          56 :             result_isnull[idx] = isnull;
     831                 :             :         }
     832                 :             :     }
     833                 :             : 
     834                 :             :     /* We make the output array the same shape as the input */
     835                 :          12 :     PG_RETURN_POINTER(construct_md_array(result_datum, result_isnull,
     836                 :             :                                          ARR_NDIM(param),
     837                 :             :                                          ARR_DIMS(param),
     838                 :             :                                          ARR_LBOUND(param),
     839                 :             :                                          osastate->qstate->sortColType,
     840                 :             :                                          osastate->qstate->typLen,
     841                 :             :                                          osastate->qstate->typByVal,
     842                 :             :                                          osastate->qstate->typAlign));
     843                 :             : }
     844                 :             : 
     845                 :             : /*
     846                 :             :  * percentile_cont(float8[]) within group ()    - continuous percentiles
     847                 :             :  */
     848                 :             : static Datum
     849                 :           8 : percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
     850                 :             :                                    Oid expect_type,
     851                 :             :                                    int16 typLen, bool typByVal, char typAlign,
     852                 :             :                                    LerpFunc lerpfunc)
     853                 :             : {
     854                 :             :     OSAPerGroupState *osastate;
     855                 :             :     ArrayType  *param;
     856                 :             :     Datum      *percentiles_datum;
     857                 :             :     bool       *percentiles_null;
     858                 :             :     int         num_percentiles;
     859                 :             :     struct pct_info *pct_info;
     860                 :             :     Datum      *result_datum;
     861                 :             :     bool       *result_isnull;
     862                 :           8 :     int64       rownum = 0;
     863                 :           8 :     Datum       first_val = (Datum) 0;
     864                 :           8 :     Datum       second_val = (Datum) 0;
     865                 :             :     bool        isnull;
     866                 :             :     int         i;
     867                 :             : 
     868                 :             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
     869                 :             : 
     870                 :             :     /* If there were no regular rows, the result is NULL */
     871         [ -  + ]:           8 :     if (PG_ARGISNULL(0))
     872                 :           0 :         PG_RETURN_NULL();
     873                 :             : 
     874                 :           8 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     875                 :             : 
     876                 :             :     /* number_of_rows could be zero if we only saw NULL input values */
     877         [ -  + ]:           8 :     if (osastate->number_of_rows == 0)
     878                 :           0 :         PG_RETURN_NULL();
     879                 :             : 
     880                 :             :     Assert(expect_type == osastate->qstate->sortColType);
     881                 :             : 
     882                 :             :     /* Deconstruct the percentile-array input */
     883         [ -  + ]:           8 :     if (PG_ARGISNULL(1))
     884                 :           0 :         PG_RETURN_NULL();
     885                 :           8 :     param = PG_GETARG_ARRAYTYPE_P(1);
     886                 :             : 
     887                 :           8 :     deconstruct_array_builtin(param, FLOAT8OID,
     888                 :             :                               &percentiles_datum,
     889                 :             :                               &percentiles_null,
     890                 :             :                               &num_percentiles);
     891                 :             : 
     892         [ -  + ]:           8 :     if (num_percentiles == 0)
     893                 :           0 :         PG_RETURN_POINTER(construct_empty_array(osastate->qstate->sortColType));
     894                 :             : 
     895                 :           8 :     pct_info = setup_pct_info(num_percentiles,
     896                 :             :                               percentiles_datum,
     897                 :             :                               percentiles_null,
     898                 :             :                               osastate->number_of_rows,
     899                 :             :                               true);
     900                 :             : 
     901                 :           8 :     result_datum = (Datum *) palloc(num_percentiles * sizeof(Datum));
     902                 :           8 :     result_isnull = (bool *) palloc(num_percentiles * sizeof(bool));
     903                 :             : 
     904                 :             :     /*
     905                 :             :      * Start by dealing with any nulls in the param array - those are sorted
     906                 :             :      * to the front on row=0, so set the corresponding result indexes to null
     907                 :             :      */
     908         [ +  - ]:           8 :     for (i = 0; i < num_percentiles; i++)
     909                 :             :     {
     910                 :           8 :         int         idx = pct_info[i].idx;
     911                 :             : 
     912         [ +  - ]:           8 :         if (pct_info[i].first_row > 0)
     913                 :           8 :             break;
     914                 :             : 
     915                 :           0 :         result_datum[idx] = (Datum) 0;
     916                 :           0 :         result_isnull[idx] = true;
     917                 :             :     }
     918                 :             : 
     919                 :             :     /*
     920                 :             :      * If there's anything left after doing the nulls, then grind the input
     921                 :             :      * and extract the needed values
     922                 :             :      */
     923         [ +  - ]:           8 :     if (i < num_percentiles)
     924                 :             :     {
     925                 :             :         /* Finish the sort, or rescan if we already did */
     926         [ +  - ]:           8 :         if (!osastate->sort_done)
     927                 :             :         {
     928                 :           8 :             tuplesort_performsort(osastate->sortstate);
     929                 :           8 :             osastate->sort_done = true;
     930                 :             :         }
     931                 :             :         else
     932                 :           0 :             tuplesort_rescan(osastate->sortstate);
     933                 :             : 
     934         [ +  + ]:          72 :         for (; i < num_percentiles; i++)
     935                 :             :         {
     936                 :          64 :             int64       first_row = pct_info[i].first_row;
     937                 :          64 :             int64       second_row = pct_info[i].second_row;
     938                 :          64 :             int         idx = pct_info[i].idx;
     939                 :             : 
     940                 :             :             /*
     941                 :             :              * Advance to first_row, if not already there.  Note that we might
     942                 :             :              * already have rownum beyond first_row, in which case first_val
     943                 :             :              * is already correct.  (This occurs when interpolating between
     944                 :             :              * the same two input rows as for the previous percentile.)
     945                 :             :              */
     946         [ +  + ]:          64 :             if (first_row > rownum)
     947                 :             :             {
     948         [ -  + ]:          32 :                 if (!tuplesort_skiptuples(osastate->sortstate, first_row - rownum - 1, true))
     949         [ #  # ]:           0 :                     elog(ERROR, "missing row in percentile_cont");
     950                 :             : 
     951         [ +  - ]:          32 :                 if (!tuplesort_getdatum(osastate->sortstate, true, true,
     952         [ -  + ]:          32 :                                         &first_val, &isnull, NULL) || isnull)
     953         [ #  # ]:           0 :                     elog(ERROR, "missing row in percentile_cont");
     954                 :             : 
     955                 :          32 :                 rownum = first_row;
     956                 :             :                 /* Always advance second_val to be latest input value */
     957                 :          32 :                 second_val = first_val;
     958                 :             :             }
     959         [ +  + ]:          32 :             else if (first_row == rownum)
     960                 :             :             {
     961                 :             :                 /*
     962                 :             :                  * We are already at the desired row, so we must previously
     963                 :             :                  * have read its value into second_val (and perhaps first_val
     964                 :             :                  * as well, but this assignment is harmless in that case).
     965                 :             :                  */
     966                 :          16 :                 first_val = second_val;
     967                 :             :             }
     968                 :             : 
     969                 :             :             /* Fetch second_row if needed */
     970         [ +  + ]:          64 :             if (second_row > rownum)
     971                 :             :             {
     972         [ +  - ]:          24 :                 if (!tuplesort_getdatum(osastate->sortstate, true, true,
     973         [ -  + ]:          24 :                                         &second_val, &isnull, NULL) || isnull)
     974         [ #  # ]:           0 :                     elog(ERROR, "missing row in percentile_cont");
     975                 :          24 :                 rownum++;
     976                 :             :             }
     977                 :             :             /* We should now certainly be on second_row exactly */
     978                 :             :             Assert(second_row == rownum);
     979                 :             : 
     980                 :             :             /* Compute appropriate result */
     981         [ +  + ]:          64 :             if (second_row > first_row)
     982                 :          40 :                 result_datum[idx] = lerpfunc(first_val, second_val,
     983                 :          40 :                                              pct_info[i].proportion);
     984                 :             :             else
     985                 :          24 :                 result_datum[idx] = first_val;
     986                 :             : 
     987                 :          64 :             result_isnull[idx] = false;
     988                 :             :         }
     989                 :             :     }
     990                 :             : 
     991                 :             :     /* We make the output array the same shape as the input */
     992                 :           8 :     PG_RETURN_POINTER(construct_md_array(result_datum, result_isnull,
     993                 :             :                                          ARR_NDIM(param),
     994                 :             :                                          ARR_DIMS(param), ARR_LBOUND(param),
     995                 :             :                                          expect_type,
     996                 :             :                                          typLen,
     997                 :             :                                          typByVal,
     998                 :             :                                          typAlign));
     999                 :             : }
    1000                 :             : 
    1001                 :             : /*
    1002                 :             :  * percentile_cont(float8[]) within group (float8)  - continuous percentiles
    1003                 :             :  */
    1004                 :             : Datum
    1005                 :           8 : percentile_cont_float8_multi_final(PG_FUNCTION_ARGS)
    1006                 :             : {
    1007                 :           8 :     return percentile_cont_multi_final_common(fcinfo,
    1008                 :             :                                               FLOAT8OID,
    1009                 :             :     /* hard-wired info on type float8 */
    1010                 :             :                                               sizeof(float8),
    1011                 :             :                                               true,
    1012                 :             :                                               TYPALIGN_DOUBLE,
    1013                 :             :                                               float8_lerp);
    1014                 :             : }
    1015                 :             : 
    1016                 :             : /*
    1017                 :             :  * percentile_cont(float8[]) within group (interval)  - continuous percentiles
    1018                 :             :  */
    1019                 :             : Datum
    1020                 :           0 : percentile_cont_interval_multi_final(PG_FUNCTION_ARGS)
    1021                 :             : {
    1022                 :           0 :     return percentile_cont_multi_final_common(fcinfo,
    1023                 :             :                                               INTERVALOID,
    1024                 :             :     /* hard-wired info on type interval */
    1025                 :             :                                               16, false, TYPALIGN_DOUBLE,
    1026                 :             :                                               interval_lerp);
    1027                 :             : }
    1028                 :             : 
    1029                 :             : 
    1030                 :             : /*
    1031                 :             :  * mode() within group (anyelement) - most common value
    1032                 :             :  */
    1033                 :             : Datum
    1034                 :          40 : mode_final(PG_FUNCTION_ARGS)
    1035                 :             : {
    1036                 :             :     OSAPerGroupState *osastate;
    1037                 :             :     Datum       val;
    1038                 :             :     bool        isnull;
    1039                 :          40 :     Datum       mode_val = (Datum) 0;
    1040                 :          40 :     int64       mode_freq = 0;
    1041                 :          40 :     Datum       last_val = (Datum) 0;
    1042                 :          40 :     int64       last_val_freq = 0;
    1043                 :          40 :     bool        last_val_is_mode = false;
    1044                 :             :     FmgrInfo   *equalfn;
    1045                 :          40 :     Datum       abbrev_val = (Datum) 0;
    1046                 :          40 :     Datum       last_abbrev_val = (Datum) 0;
    1047                 :             :     bool        shouldfree;
    1048                 :             : 
    1049                 :             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
    1050                 :             : 
    1051                 :             :     /* If there were no regular rows, the result is NULL */
    1052         [ -  + ]:          40 :     if (PG_ARGISNULL(0))
    1053                 :           0 :         PG_RETURN_NULL();
    1054                 :             : 
    1055                 :          40 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
    1056                 :             : 
    1057                 :             :     /* number_of_rows could be zero if we only saw NULL input values */
    1058         [ -  + ]:          40 :     if (osastate->number_of_rows == 0)
    1059                 :           0 :         PG_RETURN_NULL();
    1060                 :             : 
    1061                 :             :     /* Look up the equality function for the datatype, if we didn't already */
    1062                 :          40 :     equalfn = &(osastate->qstate->equalfn);
    1063         [ +  + ]:          40 :     if (!OidIsValid(equalfn->fn_oid))
    1064                 :           4 :         fmgr_info_cxt(get_opcode(osastate->qstate->eqOperator), equalfn,
    1065                 :           4 :                       osastate->qstate->qcontext);
    1066                 :             : 
    1067                 :          40 :     shouldfree = !(osastate->qstate->typByVal);
    1068                 :             : 
    1069                 :             :     /* Finish the sort, or rescan if we already did */
    1070         [ +  - ]:          40 :     if (!osastate->sort_done)
    1071                 :             :     {
    1072                 :          40 :         tuplesort_performsort(osastate->sortstate);
    1073                 :          40 :         osastate->sort_done = true;
    1074                 :             :     }
    1075                 :             :     else
    1076                 :           0 :         tuplesort_rescan(osastate->sortstate);
    1077                 :             : 
    1078                 :             :     /* Scan tuples and count frequencies */
    1079         [ +  + ]:       40040 :     while (tuplesort_getdatum(osastate->sortstate, true, true, &val, &isnull,
    1080                 :             :                               &abbrev_val))
    1081                 :             :     {
    1082                 :             :         /* we don't expect any nulls, but ignore them if found */
    1083         [ -  + ]:       40000 :         if (isnull)
    1084                 :           0 :             continue;
    1085                 :             : 
    1086         [ +  + ]:       40000 :         if (last_val_freq == 0)
    1087                 :             :         {
    1088                 :             :             /* first nonnull value - it's the mode for now */
    1089                 :          40 :             mode_val = last_val = val;
    1090                 :          40 :             mode_freq = last_val_freq = 1;
    1091                 :          40 :             last_val_is_mode = true;
    1092                 :          40 :             last_abbrev_val = abbrev_val;
    1093                 :             :         }
    1094   [ +  -  +  + ]:       79920 :         else if (abbrev_val == last_abbrev_val &&
    1095                 :       39960 :                  DatumGetBool(FunctionCall2Coll(equalfn, PG_GET_COLLATION(), val, last_val)))
    1096                 :             :         {
    1097                 :             :             /* value equal to previous value, count it */
    1098         [ +  + ]:       39840 :             if (last_val_is_mode)
    1099                 :       10616 :                 mode_freq++;    /* needn't maintain last_val_freq */
    1100         [ +  + ]:       29224 :             else if (++last_val_freq > mode_freq)
    1101                 :             :             {
    1102                 :             :                 /* last_val becomes new mode */
    1103         [ +  - ]:          44 :                 if (shouldfree)
    1104                 :          44 :                     pfree(DatumGetPointer(mode_val));
    1105                 :          44 :                 mode_val = last_val;
    1106                 :          44 :                 mode_freq = last_val_freq;
    1107                 :          44 :                 last_val_is_mode = true;
    1108                 :             :             }
    1109         [ +  - ]:       39840 :             if (shouldfree)
    1110                 :       39840 :                 pfree(DatumGetPointer(val));
    1111                 :             :         }
    1112                 :             :         else
    1113                 :             :         {
    1114                 :             :             /* val should replace last_val */
    1115   [ +  -  +  + ]:         120 :             if (shouldfree && !last_val_is_mode)
    1116                 :          48 :                 pfree(DatumGetPointer(last_val));
    1117                 :         120 :             last_val = val;
    1118                 :             :             /* avoid equality function calls by reusing abbreviated keys */
    1119                 :         120 :             last_abbrev_val = abbrev_val;
    1120                 :         120 :             last_val_freq = 1;
    1121                 :         120 :             last_val_is_mode = false;
    1122                 :             :         }
    1123                 :             : 
    1124         [ -  + ]:       40000 :         CHECK_FOR_INTERRUPTS();
    1125                 :             :     }
    1126                 :             : 
    1127   [ +  -  +  + ]:          40 :     if (shouldfree && !last_val_is_mode)
    1128                 :          28 :         pfree(DatumGetPointer(last_val));
    1129                 :             : 
    1130         [ +  - ]:          40 :     if (mode_freq)
    1131                 :          40 :         PG_RETURN_DATUM(mode_val);
    1132                 :             :     else
    1133                 :           0 :         PG_RETURN_NULL();
    1134                 :             : }
    1135                 :             : 
    1136                 :             : 
    1137                 :             : /*
    1138                 :             :  * Common code to sanity-check args for hypothetical-set functions. No need
    1139                 :             :  * for friendly errors, these can only happen if someone's messing up the
    1140                 :             :  * aggregate definitions. The checks are needed for security, however.
    1141                 :             :  */
    1142                 :             : static void
    1143                 :         179 : hypothetical_check_argtypes(FunctionCallInfo fcinfo, int nargs,
    1144                 :             :                             TupleDesc tupdesc)
    1145                 :             : {
    1146                 :             :     int         i;
    1147                 :             : 
    1148                 :             :     /* check that we have an int4 flag column */
    1149         [ +  - ]:         179 :     if (!tupdesc ||
    1150         [ +  - ]:         179 :         (nargs + 1) != tupdesc->natts ||
    1151         [ -  + ]:         179 :         TupleDescAttr(tupdesc, nargs)->atttypid != INT4OID)
    1152         [ #  # ]:           0 :         elog(ERROR, "type mismatch in hypothetical-set function");
    1153                 :             : 
    1154                 :             :     /* check that direct args match in type with aggregated args */
    1155         [ +  + ]:         551 :     for (i = 0; i < nargs; i++)
    1156                 :             :     {
    1157                 :         372 :         Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
    1158                 :             : 
    1159         [ -  + ]:         372 :         if (get_fn_expr_argtype(fcinfo->flinfo, i + 1) != attr->atttypid)
    1160         [ #  # ]:           0 :             elog(ERROR, "type mismatch in hypothetical-set function");
    1161                 :             :     }
    1162                 :         179 : }
    1163                 :             : 
    1164                 :             : /*
    1165                 :             :  * compute rank of hypothetical row
    1166                 :             :  *
    1167                 :             :  * flag should be -1 to sort hypothetical row ahead of its peers, or +1
    1168                 :             :  * to sort behind.
    1169                 :             :  * total number of regular rows is returned into *number_of_rows.
    1170                 :             :  */
    1171                 :             : static int64
    1172                 :         163 : hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
    1173                 :             :                          int64 *number_of_rows)
    1174                 :             : {
    1175                 :         163 :     int         nargs = PG_NARGS() - 1;
    1176                 :         163 :     int64       rank = 1;
    1177                 :             :     OSAPerGroupState *osastate;
    1178                 :             :     TupleTableSlot *slot;
    1179                 :             :     int         i;
    1180                 :             : 
    1181                 :             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
    1182                 :             : 
    1183                 :             :     /* If there were no regular rows, the rank is always 1 */
    1184         [ +  + ]:         163 :     if (PG_ARGISNULL(0))
    1185                 :             :     {
    1186                 :           4 :         *number_of_rows = 0;
    1187                 :           4 :         return 1;
    1188                 :             :     }
    1189                 :             : 
    1190                 :         159 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
    1191                 :         159 :     *number_of_rows = osastate->number_of_rows;
    1192                 :             : 
    1193                 :             :     /* Adjust nargs to be the number of direct (or aggregated) args */
    1194         [ -  + ]:         159 :     if (nargs % 2 != 0)
    1195         [ #  # ]:           0 :         elog(ERROR, "wrong number of arguments in hypothetical-set function");
    1196                 :         159 :     nargs /= 2;
    1197                 :             : 
    1198                 :         159 :     hypothetical_check_argtypes(fcinfo, nargs, osastate->qstate->tupdesc);
    1199                 :             : 
    1200                 :             :     /* because we need a hypothetical row, we can't share transition state */
    1201                 :             :     Assert(!osastate->sort_done);
    1202                 :             : 
    1203                 :             :     /* insert the hypothetical row into the sort */
    1204                 :         159 :     slot = osastate->qstate->tupslot;
    1205                 :         159 :     ExecClearTuple(slot);
    1206         [ +  + ]:         511 :     for (i = 0; i < nargs; i++)
    1207                 :             :     {
    1208                 :         352 :         slot->tts_values[i] = PG_GETARG_DATUM(i + 1);
    1209                 :         352 :         slot->tts_isnull[i] = PG_ARGISNULL(i + 1);
    1210                 :             :     }
    1211                 :         159 :     slot->tts_values[i] = Int32GetDatum(flag);
    1212                 :         159 :     slot->tts_isnull[i] = false;
    1213                 :         159 :     ExecStoreVirtualTuple(slot);
    1214                 :             : 
    1215                 :         159 :     tuplesort_puttupleslot(osastate->sortstate, slot);
    1216                 :             : 
    1217                 :             :     /* finish the sort */
    1218                 :         159 :     tuplesort_performsort(osastate->sortstate);
    1219                 :         159 :     osastate->sort_done = true;
    1220                 :             : 
    1221                 :             :     /* iterate till we find the hypothetical row */
    1222         [ +  - ]:        2779 :     while (tuplesort_gettupleslot(osastate->sortstate, true, true, slot, NULL))
    1223                 :             :     {
    1224                 :             :         bool        isnull;
    1225                 :        2779 :         Datum       d = slot_getattr(slot, nargs + 1, &isnull);
    1226                 :             : 
    1227   [ +  -  +  + ]:        2779 :         if (!isnull && DatumGetInt32(d) != 0)
    1228                 :         159 :             break;
    1229                 :             : 
    1230                 :        2620 :         rank++;
    1231                 :             : 
    1232         [ -  + ]:        2620 :         CHECK_FOR_INTERRUPTS();
    1233                 :             :     }
    1234                 :             : 
    1235                 :         159 :     ExecClearTuple(slot);
    1236                 :             : 
    1237                 :         159 :     return rank;
    1238                 :             : }
    1239                 :             : 
    1240                 :             : 
    1241                 :             : /*
    1242                 :             :  * rank()  - rank of hypothetical row
    1243                 :             :  */
    1244                 :             : Datum
    1245                 :         151 : hypothetical_rank_final(PG_FUNCTION_ARGS)
    1246                 :             : {
    1247                 :             :     int64       rank;
    1248                 :             :     int64       rowcount;
    1249                 :             : 
    1250                 :         151 :     rank = hypothetical_rank_common(fcinfo, -1, &rowcount);
    1251                 :             : 
    1252                 :         151 :     PG_RETURN_INT64(rank);
    1253                 :             : }
    1254                 :             : 
    1255                 :             : /*
    1256                 :             :  * percent_rank()   - percentile rank of hypothetical row
    1257                 :             :  */
    1258                 :             : Datum
    1259                 :           8 : hypothetical_percent_rank_final(PG_FUNCTION_ARGS)
    1260                 :             : {
    1261                 :             :     int64       rank;
    1262                 :             :     int64       rowcount;
    1263                 :             :     double      result_val;
    1264                 :             : 
    1265                 :           8 :     rank = hypothetical_rank_common(fcinfo, -1, &rowcount);
    1266                 :             : 
    1267         [ +  + ]:           8 :     if (rowcount == 0)
    1268                 :           4 :         PG_RETURN_FLOAT8(0);
    1269                 :             : 
    1270                 :           4 :     result_val = (double) (rank - 1) / (double) (rowcount);
    1271                 :             : 
    1272                 :           4 :     PG_RETURN_FLOAT8(result_val);
    1273                 :             : }
    1274                 :             : 
    1275                 :             : /*
    1276                 :             :  * cume_dist()  - cumulative distribution of hypothetical row
    1277                 :             :  */
    1278                 :             : Datum
    1279                 :           4 : hypothetical_cume_dist_final(PG_FUNCTION_ARGS)
    1280                 :             : {
    1281                 :             :     int64       rank;
    1282                 :             :     int64       rowcount;
    1283                 :             :     double      result_val;
    1284                 :             : 
    1285                 :           4 :     rank = hypothetical_rank_common(fcinfo, 1, &rowcount);
    1286                 :             : 
    1287                 :           4 :     result_val = (double) (rank) / (double) (rowcount + 1);
    1288                 :             : 
    1289                 :           4 :     PG_RETURN_FLOAT8(result_val);
    1290                 :             : }
    1291                 :             : 
    1292                 :             : /*
    1293                 :             :  * dense_rank() - rank of hypothetical row without gaps in ranking
    1294                 :             :  */
    1295                 :             : Datum
    1296                 :          20 : hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
    1297                 :             : {
    1298                 :             :     ExprContext *econtext;
    1299                 :             :     ExprState  *compareTuple;
    1300                 :          20 :     int         nargs = PG_NARGS() - 1;
    1301                 :          20 :     int64       rank = 1;
    1302                 :          20 :     int64       duplicate_count = 0;
    1303                 :             :     OSAPerGroupState *osastate;
    1304                 :             :     int         numDistinctCols;
    1305                 :          20 :     Datum       abbrevVal = (Datum) 0;
    1306                 :          20 :     Datum       abbrevOld = (Datum) 0;
    1307                 :             :     TupleTableSlot *slot;
    1308                 :             :     TupleTableSlot *extraslot;
    1309                 :             :     TupleTableSlot *slot2;
    1310                 :             :     int         i;
    1311                 :             : 
    1312                 :             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
    1313                 :             : 
    1314                 :             :     /* If there were no regular rows, the rank is always 1 */
    1315         [ -  + ]:          20 :     if (PG_ARGISNULL(0))
    1316                 :           0 :         PG_RETURN_INT64(rank);
    1317                 :             : 
    1318                 :          20 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
    1319                 :          20 :     econtext = osastate->qstate->econtext;
    1320         [ +  + ]:          20 :     if (!econtext)
    1321                 :             :     {
    1322                 :             :         MemoryContext oldcontext;
    1323                 :             : 
    1324                 :             :         /* Make sure to we create econtext under correct parent context. */
    1325                 :          12 :         oldcontext = MemoryContextSwitchTo(osastate->qstate->qcontext);
    1326                 :          12 :         osastate->qstate->econtext = CreateStandaloneExprContext();
    1327                 :          12 :         econtext = osastate->qstate->econtext;
    1328                 :          12 :         MemoryContextSwitchTo(oldcontext);
    1329                 :             :     }
    1330                 :             : 
    1331                 :             :     /* Adjust nargs to be the number of direct (or aggregated) args */
    1332         [ -  + ]:          20 :     if (nargs % 2 != 0)
    1333         [ #  # ]:           0 :         elog(ERROR, "wrong number of arguments in hypothetical-set function");
    1334                 :          20 :     nargs /= 2;
    1335                 :             : 
    1336                 :          20 :     hypothetical_check_argtypes(fcinfo, nargs, osastate->qstate->tupdesc);
    1337                 :             : 
    1338                 :             :     /*
    1339                 :             :      * When comparing tuples, we can omit the flag column since we will only
    1340                 :             :      * compare rows with flag == 0.
    1341                 :             :      */
    1342                 :          20 :     numDistinctCols = osastate->qstate->numSortCols - 1;
    1343                 :             : 
    1344                 :             :     /* Build tuple comparator, if we didn't already */
    1345                 :          20 :     compareTuple = osastate->qstate->compareTuple;
    1346         [ +  + ]:          20 :     if (compareTuple == NULL)
    1347                 :             :     {
    1348                 :          12 :         AttrNumber *sortColIdx = osastate->qstate->sortColIdx;
    1349                 :             :         MemoryContext oldContext;
    1350                 :             : 
    1351                 :          12 :         oldContext = MemoryContextSwitchTo(osastate->qstate->qcontext);
    1352                 :          12 :         compareTuple = execTuplesMatchPrepare(osastate->qstate->tupdesc,
    1353                 :             :                                               numDistinctCols,
    1354                 :             :                                               sortColIdx,
    1355                 :          12 :                                               osastate->qstate->eqOperators,
    1356                 :          12 :                                               osastate->qstate->sortCollations,
    1357                 :             :                                               NULL);
    1358                 :          12 :         MemoryContextSwitchTo(oldContext);
    1359                 :          12 :         osastate->qstate->compareTuple = compareTuple;
    1360                 :             :     }
    1361                 :             : 
    1362                 :             :     /* because we need a hypothetical row, we can't share transition state */
    1363                 :             :     Assert(!osastate->sort_done);
    1364                 :             : 
    1365                 :             :     /* insert the hypothetical row into the sort */
    1366                 :          20 :     slot = osastate->qstate->tupslot;
    1367                 :          20 :     ExecClearTuple(slot);
    1368         [ +  + ]:          40 :     for (i = 0; i < nargs; i++)
    1369                 :             :     {
    1370                 :          20 :         slot->tts_values[i] = PG_GETARG_DATUM(i + 1);
    1371                 :          20 :         slot->tts_isnull[i] = PG_ARGISNULL(i + 1);
    1372                 :             :     }
    1373                 :          20 :     slot->tts_values[i] = Int32GetDatum(-1);
    1374                 :          20 :     slot->tts_isnull[i] = false;
    1375                 :          20 :     ExecStoreVirtualTuple(slot);
    1376                 :             : 
    1377                 :          20 :     tuplesort_puttupleslot(osastate->sortstate, slot);
    1378                 :             : 
    1379                 :             :     /* finish the sort */
    1380                 :          20 :     tuplesort_performsort(osastate->sortstate);
    1381                 :          20 :     osastate->sort_done = true;
    1382                 :             : 
    1383                 :             :     /*
    1384                 :             :      * We alternate fetching into tupslot and extraslot so that we have the
    1385                 :             :      * previous row available for comparisons.  This is accomplished by
    1386                 :             :      * swapping the slot pointer variables after each row.
    1387                 :             :      */
    1388                 :          20 :     extraslot = MakeSingleTupleTableSlot(osastate->qstate->tupdesc,
    1389                 :             :                                          &TTSOpsMinimalTuple);
    1390                 :          20 :     slot2 = extraslot;
    1391                 :             : 
    1392                 :             :     /* iterate till we find the hypothetical row */
    1393         [ +  - ]:          44 :     while (tuplesort_gettupleslot(osastate->sortstate, true, true, slot,
    1394                 :             :                                   &abbrevVal))
    1395                 :             :     {
    1396                 :             :         bool        isnull;
    1397                 :          44 :         Datum       d = slot_getattr(slot, nargs + 1, &isnull);
    1398                 :             :         TupleTableSlot *tmpslot;
    1399                 :             : 
    1400   [ +  -  +  + ]:          44 :         if (!isnull && DatumGetInt32(d) != 0)
    1401                 :          20 :             break;
    1402                 :             : 
    1403                 :             :         /* count non-distinct tuples */
    1404                 :          24 :         econtext->ecxt_outertuple = slot;
    1405                 :          24 :         econtext->ecxt_innertuple = slot2;
    1406                 :             : 
    1407   [ +  -  +  + ]:          24 :         if (!TupIsNull(slot2) &&
    1408   [ +  -  +  + ]:          32 :             abbrevVal == abbrevOld &&
    1409                 :          16 :             ExecQualAndReset(compareTuple, econtext))
    1410                 :           8 :             duplicate_count++;
    1411                 :             : 
    1412                 :          24 :         tmpslot = slot2;
    1413                 :          24 :         slot2 = slot;
    1414                 :          24 :         slot = tmpslot;
    1415                 :             :         /* avoid ExecQual() calls by reusing abbreviated keys */
    1416                 :          24 :         abbrevOld = abbrevVal;
    1417                 :             : 
    1418                 :          24 :         rank++;
    1419                 :             : 
    1420         [ -  + ]:          24 :         CHECK_FOR_INTERRUPTS();
    1421                 :             :     }
    1422                 :             : 
    1423                 :          20 :     ExecClearTuple(slot);
    1424                 :          20 :     ExecClearTuple(slot2);
    1425                 :             : 
    1426                 :          20 :     ExecDropSingleTupleTableSlot(extraslot);
    1427                 :             : 
    1428                 :          20 :     rank = rank - duplicate_count;
    1429                 :             : 
    1430                 :          20 :     PG_RETURN_INT64(rank);
    1431                 :             : }
        

Generated by: LCOV version 2.0-1