LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - orderedsetaggs.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 88.9 % 506 450 17 16 23 8 160 11 271 24 152 1 10
Current Date: 2023-04-08 15:15:32 Functions: 87.0 % 23 20 2 1 13 2 5 2 13
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

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