LCOV - code coverage report
Current view: top level - src/backend/utils/adt - orderedsetaggs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 432 488 88.5 %
Date: 2023-12-01 19:11:07 Functions: 20 23 87.0 %
Legend: Lines: hit not hit

          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         660 : 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         660 :     if (AggCheckCallContext(fcinfo, &gcontext) != AGG_CONTEXT_AGGREGATE)
     129           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         660 :     qstate = (OSAPerQueryState *) fcinfo->flinfo->fn_extra;
     136         660 :     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         246 :         aggref = AggGetAggref(fcinfo);
     145         246 :         if (!aggref)
     146           0 :             elog(ERROR, "ordered-set aggregate called in non-aggregate context");
     147         246 :         if (!AGGKIND_IS_ORDERED_SET(aggref->aggkind))
     148           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         246 :         qcontext = fcinfo->flinfo->fn_mcxt;
     156         246 :         oldcontext = MemoryContextSwitchTo(qcontext);
     157             : 
     158         246 :         qstate = (OSAPerQueryState *) palloc0(sizeof(OSAPerQueryState));
     159         246 :         qstate->aggref = aggref;
     160         246 :         qstate->qcontext = qcontext;
     161             : 
     162             :         /* We need to support rescans if the trans state is shared */
     163         246 :         qstate->rescan_needed = AggStateIsShared(fcinfo);
     164             : 
     165             :         /* Extract the sort information */
     166         246 :         sortlist = aggref->aggorder;
     167         246 :         numSortCols = list_length(sortlist);
     168             : 
     169         246 :         if (use_tuples)
     170             :         {
     171         100 :             bool        ishypothetical = (aggref->aggkind == AGGKIND_HYPOTHETICAL);
     172             :             ListCell   *lc;
     173             :             int         i;
     174             : 
     175         100 :             if (ishypothetical)
     176         100 :                 numSortCols++;  /* make space for flag column */
     177         100 :             qstate->numSortCols = numSortCols;
     178         100 :             qstate->sortColIdx = (AttrNumber *) palloc(numSortCols * sizeof(AttrNumber));
     179         100 :             qstate->sortOperators = (Oid *) palloc(numSortCols * sizeof(Oid));
     180         100 :             qstate->eqOperators = (Oid *) palloc(numSortCols * sizeof(Oid));
     181         100 :             qstate->sortCollations = (Oid *) palloc(numSortCols * sizeof(Oid));
     182         100 :             qstate->sortNullsFirsts = (bool *) palloc(numSortCols * sizeof(bool));
     183             : 
     184         100 :             i = 0;
     185         250 :             foreach(lc, sortlist)
     186             :             {
     187         150 :                 SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
     188         150 :                 TargetEntry *tle = get_sortgroupclause_tle(sortcl,
     189             :                                                            aggref->args);
     190             : 
     191             :                 /* the parser should have made sure of this */
     192             :                 Assert(OidIsValid(sortcl->sortop));
     193             : 
     194         150 :                 qstate->sortColIdx[i] = tle->resno;
     195         150 :                 qstate->sortOperators[i] = sortcl->sortop;
     196         150 :                 qstate->eqOperators[i] = sortcl->eqop;
     197         150 :                 qstate->sortCollations[i] = exprCollation((Node *) tle->expr);
     198         150 :                 qstate->sortNullsFirsts[i] = sortcl->nulls_first;
     199         150 :                 i++;
     200             :             }
     201             : 
     202         100 :             if (ishypothetical)
     203             :             {
     204             :                 /* Add an integer flag column as the last sort column */
     205         100 :                 qstate->sortColIdx[i] = list_length(aggref->args) + 1;
     206         100 :                 qstate->sortOperators[i] = Int4LessOperator;
     207         100 :                 qstate->eqOperators[i] = Int4EqualOperator;
     208         100 :                 qstate->sortCollations[i] = InvalidOid;
     209         100 :                 qstate->sortNullsFirsts[i] = false;
     210         100 :                 i++;
     211             :             }
     212             : 
     213             :             Assert(i == numSortCols);
     214             : 
     215             :             /*
     216             :              * Get a tupledesc corresponding to the aggregated inputs
     217             :              * (including sort expressions) of the agg.
     218             :              */
     219         100 :             qstate->tupdesc = ExecTypeFromTL(aggref->args);
     220             : 
     221             :             /* If we need a flag column, hack the tupledesc to include that */
     222         100 :             if (ishypothetical)
     223             :             {
     224             :                 TupleDesc   newdesc;
     225         100 :                 int         natts = qstate->tupdesc->natts;
     226             : 
     227         100 :                 newdesc = CreateTemplateTupleDesc(natts + 1);
     228         250 :                 for (i = 1; i <= natts; i++)
     229         150 :                     TupleDescCopyEntry(newdesc, i, qstate->tupdesc, i);
     230             : 
     231         100 :                 TupleDescInitEntry(newdesc,
     232         100 :                                    (AttrNumber) ++natts,
     233             :                                    "flag",
     234             :                                    INT4OID,
     235             :                                    -1,
     236             :                                    0);
     237             : 
     238         100 :                 FreeTupleDesc(qstate->tupdesc);
     239         100 :                 qstate->tupdesc = newdesc;
     240             :             }
     241             : 
     242             :             /* Create slot we'll use to store/retrieve rows */
     243         100 :             qstate->tupslot = MakeSingleTupleTableSlot(qstate->tupdesc,
     244             :                                                        &TTSOpsMinimalTuple);
     245             :         }
     246             :         else
     247             :         {
     248             :             /* Sort single datums */
     249             :             SortGroupClause *sortcl;
     250             :             TargetEntry *tle;
     251             : 
     252         146 :             if (numSortCols != 1 || aggref->aggkind == AGGKIND_HYPOTHETICAL)
     253           0 :                 elog(ERROR, "ordered-set aggregate support function does not support multiple aggregated columns");
     254             : 
     255         146 :             sortcl = (SortGroupClause *) linitial(sortlist);
     256         146 :             tle = get_sortgroupclause_tle(sortcl, aggref->args);
     257             : 
     258             :             /* the parser should have made sure of this */
     259             :             Assert(OidIsValid(sortcl->sortop));
     260             : 
     261             :             /* Save sort ordering info */
     262         146 :             qstate->sortColType = exprType((Node *) tle->expr);
     263         146 :             qstate->sortOperator = sortcl->sortop;
     264         146 :             qstate->eqOperator = sortcl->eqop;
     265         146 :             qstate->sortCollation = exprCollation((Node *) tle->expr);
     266         146 :             qstate->sortNullsFirst = sortcl->nulls_first;
     267             : 
     268             :             /* Save datatype info */
     269         146 :             get_typlenbyvalalign(qstate->sortColType,
     270             :                                  &qstate->typLen,
     271             :                                  &qstate->typByVal,
     272             :                                  &qstate->typAlign);
     273             :         }
     274             : 
     275         246 :         fcinfo->flinfo->fn_extra = (void *) qstate;
     276             : 
     277         246 :         MemoryContextSwitchTo(oldcontext);
     278             :     }
     279             : 
     280             :     /* Now build the stuff we need in group-lifespan context */
     281         660 :     oldcontext = MemoryContextSwitchTo(gcontext);
     282             : 
     283         660 :     osastate = (OSAPerGroupState *) palloc(sizeof(OSAPerGroupState));
     284         660 :     osastate->qstate = qstate;
     285         660 :     osastate->gcontext = gcontext;
     286             : 
     287         660 :     tuplesortopt = TUPLESORT_NONE;
     288             : 
     289         660 :     if (qstate->rescan_needed)
     290          24 :         tuplesortopt |= TUPLESORT_RANDOMACCESS;
     291             : 
     292             :     /*
     293             :      * Initialize tuplesort object.
     294             :      */
     295         660 :     if (use_tuples)
     296         274 :         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         386 :         osastate->sortstate = tuplesort_begin_datum(qstate->sortColType,
     307             :                                                     qstate->sortOperator,
     308             :                                                     qstate->sortCollation,
     309         386 :                                                     qstate->sortNullsFirst,
     310             :                                                     work_mem,
     311             :                                                     NULL,
     312             :                                                     tuplesortopt);
     313             : 
     314         660 :     osastate->number_of_rows = 0;
     315         660 :     osastate->sort_done = false;
     316             : 
     317             :     /* Now register a shutdown callback to clean things up at end of group */
     318         660 :     AggRegisterCallback(fcinfo,
     319             :                         ordered_set_shutdown,
     320             :                         PointerGetDatum(osastate));
     321             : 
     322         660 :     MemoryContextSwitchTo(oldcontext);
     323             : 
     324         660 :     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         660 : ordered_set_shutdown(Datum arg)
     342             : {
     343         660 :     OSAPerGroupState *osastate = (OSAPerGroupState *) DatumGetPointer(arg);
     344             : 
     345             :     /* Tuplesort object might have temp files. */
     346         660 :     if (osastate->sortstate)
     347         660 :         tuplesort_end(osastate->sortstate);
     348         660 :     osastate->sortstate = NULL;
     349             :     /* The tupleslot probably can't be holding a pin, but let's be safe. */
     350         660 :     if (osastate->qstate->tupslot)
     351         274 :         ExecClearTuple(osastate->qstate->tupslot);
     352         660 : }
     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     1203698 : ordered_set_transition(PG_FUNCTION_ARGS)
     361             : {
     362             :     OSAPerGroupState *osastate;
     363             : 
     364             :     /* If first call, create the transition state workspace */
     365     1203698 :     if (PG_ARGISNULL(0))
     366         386 :         osastate = ordered_set_startup(fcinfo, false);
     367             :     else
     368     1203312 :         osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     369             : 
     370             :     /* Load the datum into the tuplesort object, but only if it's not null */
     371     1203698 :     if (!PG_ARGISNULL(1))
     372             :     {
     373     1203626 :         tuplesort_putdatum(osastate->sortstate, PG_GETARG_DATUM(1), false);
     374     1203626 :         osastate->number_of_rows++;
     375             :     }
     376             : 
     377     1203698 :     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      302788 : 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      302788 :     if (PG_ARGISNULL(0))
     394         274 :         osastate = ordered_set_startup(fcinfo, true);
     395             :     else
     396      302514 :         osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     397             : 
     398             :     /* Form a tuple from all the other inputs besides the transition value */
     399      302788 :     slot = osastate->qstate->tupslot;
     400      302788 :     ExecClearTuple(slot);
     401      302788 :     nargs = PG_NARGS() - 1;
     402     1206226 :     for (i = 0; i < nargs; i++)
     403             :     {
     404      903438 :         slot->tts_values[i] = PG_GETARG_DATUM(i + 1);
     405      903438 :         slot->tts_isnull[i] = PG_ARGISNULL(i + 1);
     406             :     }
     407      302788 :     if (osastate->qstate->aggref->aggkind == AGGKIND_HYPOTHETICAL)
     408             :     {
     409             :         /* Add a zero flag value to mark this row as a normal input row */
     410      302788 :         slot->tts_values[i] = Int32GetDatum(0);
     411      302788 :         slot->tts_isnull[i] = false;
     412      302788 :         i++;
     413             :     }
     414             :     Assert(i == slot->tts_tupleDescriptor->natts);
     415      302788 :     ExecStoreVirtualTuple(slot);
     416             : 
     417             :     /* Load the row into the tuplesort object */
     418      302788 :     tuplesort_puttupleslot(osastate->sortstate, slot);
     419      302788 :     osastate->number_of_rows++;
     420             : 
     421      302788 :     PG_RETURN_POINTER(osastate);
     422             : }
     423             : 
     424             : 
     425             : /*
     426             :  * percentile_disc(float8) within group(anyelement) - discrete percentile
     427             :  */
     428             : Datum
     429         270 : percentile_disc_final(PG_FUNCTION_ARGS)
     430             : {
     431             :     OSAPerGroupState *osastate;
     432             :     double      percentile;
     433             :     Datum       val;
     434             :     bool        isnull;
     435             :     int64       rownum;
     436             : 
     437             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
     438             : 
     439             :     /* Get and check the percentile argument */
     440         270 :     if (PG_ARGISNULL(1))
     441           0 :         PG_RETURN_NULL();
     442             : 
     443         270 :     percentile = PG_GETARG_FLOAT8(1);
     444             : 
     445         270 :     if (percentile < 0 || percentile > 1 || isnan(percentile))
     446           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         270 :     if (PG_ARGISNULL(0))
     453          54 :         PG_RETURN_NULL();
     454             : 
     455         216 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     456             : 
     457             :     /* number_of_rows could be zero if we only saw NULL input values */
     458         216 :     if (osastate->number_of_rows == 0)
     459           0 :         PG_RETURN_NULL();
     460             : 
     461             :     /* Finish the sort, or rescan if we already did */
     462         216 :     if (!osastate->sort_done)
     463             :     {
     464         192 :         tuplesort_performsort(osastate->sortstate);
     465         192 :         osastate->sort_done = true;
     466             :     }
     467             :     else
     468          24 :         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         216 :     rownum = (int64) ceil(percentile * osastate->number_of_rows);
     477             :     Assert(rownum <= osastate->number_of_rows);
     478             : 
     479         216 :     if (rownum > 1)
     480             :     {
     481         156 :         if (!tuplesort_skiptuples(osastate->sortstate, rownum - 1, true))
     482           0 :             elog(ERROR, "missing row in percentile_disc");
     483             :     }
     484             : 
     485         216 :     if (!tuplesort_getdatum(osastate->sortstate, true, true, &val, &isnull,
     486             :                             NULL))
     487           0 :         elog(ERROR, "missing row in percentile_disc");
     488             : 
     489             :     /* We shouldn't have stored any nulls, but do the right thing anyway */
     490         216 :     if (isnull)
     491           0 :         PG_RETURN_NULL();
     492             :     else
     493         216 :         PG_RETURN_DATUM(val);
     494             : }
     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         132 : float8_lerp(Datum lo, Datum hi, double pct)
     506             : {
     507         132 :     double      loval = DatumGetFloat8(lo);
     508         132 :     double      hival = DatumGetFloat8(hi);
     509             : 
     510         132 :     return Float8GetDatum(loval + (pct * (hival - loval)));
     511             : }
     512             : 
     513             : static Datum
     514           0 : interval_lerp(Datum lo, Datum hi, double pct)
     515             : {
     516           0 :     Datum       diff_result = DirectFunctionCall2(interval_mi, hi, lo);
     517           0 :     Datum       mul_result = DirectFunctionCall2(interval_mul,
     518             :                                                  diff_result,
     519             :                                                  Float8GetDatumFast(pct));
     520             : 
     521           0 :     return DirectFunctionCall2(interval_pl, mul_result, lo);
     522             : }
     523             : 
     524             : /*
     525             :  * Continuous percentile
     526             :  */
     527             : static Datum
     528         104 : percentile_cont_final_common(FunctionCallInfo fcinfo,
     529             :                              Oid expect_type,
     530             :                              LerpFunc lerpfunc)
     531             : {
     532             :     OSAPerGroupState *osastate;
     533             :     double      percentile;
     534         104 :     int64       first_row = 0;
     535         104 :     int64       second_row = 0;
     536             :     Datum       val;
     537             :     Datum       first_val;
     538             :     Datum       second_val;
     539             :     double      proportion;
     540             :     bool        isnull;
     541             : 
     542             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
     543             : 
     544             :     /* Get and check the percentile argument */
     545         104 :     if (PG_ARGISNULL(1))
     546           0 :         PG_RETURN_NULL();
     547             : 
     548         104 :     percentile = PG_GETARG_FLOAT8(1);
     549             : 
     550         104 :     if (percentile < 0 || percentile > 1 || isnan(percentile))
     551           0 :         ereport(ERROR,
     552             :                 (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         104 :     if (PG_ARGISNULL(0))
     558           0 :         PG_RETURN_NULL();
     559             : 
     560         104 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     561             : 
     562             :     /* number_of_rows could be zero if we only saw NULL input values */
     563         104 :     if (osastate->number_of_rows == 0)
     564           0 :         PG_RETURN_NULL();
     565             : 
     566             :     Assert(expect_type == osastate->qstate->sortColType);
     567             : 
     568             :     /* Finish the sort, or rescan if we already did */
     569         104 :     if (!osastate->sort_done)
     570             :     {
     571         104 :         tuplesort_performsort(osastate->sortstate);
     572         104 :         osastate->sort_done = true;
     573             :     }
     574             :     else
     575           0 :         tuplesort_rescan(osastate->sortstate);
     576             : 
     577         104 :     first_row = floor(percentile * (osastate->number_of_rows - 1));
     578         104 :     second_row = ceil(percentile * (osastate->number_of_rows - 1));
     579             : 
     580             :     Assert(first_row < osastate->number_of_rows);
     581             : 
     582         104 :     if (!tuplesort_skiptuples(osastate->sortstate, first_row, true))
     583           0 :         elog(ERROR, "missing row in percentile_cont");
     584             : 
     585         104 :     if (!tuplesort_getdatum(osastate->sortstate, true, true, &first_val,
     586             :                             &isnull, NULL))
     587           0 :         elog(ERROR, "missing row in percentile_cont");
     588         104 :     if (isnull)
     589           0 :         PG_RETURN_NULL();
     590             : 
     591         104 :     if (first_row == second_row)
     592             :     {
     593          32 :         val = first_val;
     594             :     }
     595             :     else
     596             :     {
     597          72 :         if (!tuplesort_getdatum(osastate->sortstate, true, true, &second_val,
     598             :                                 &isnull, NULL))
     599           0 :             elog(ERROR, "missing row in percentile_cont");
     600             : 
     601          72 :         if (isnull)
     602           0 :             PG_RETURN_NULL();
     603             : 
     604          72 :         proportion = (percentile * (osastate->number_of_rows - 1)) - first_row;
     605          72 :         val = lerpfunc(first_val, second_val, proportion);
     606             :     }
     607             : 
     608         104 :     PG_RETURN_DATUM(val);
     609             : }
     610             : 
     611             : /*
     612             :  * percentile_cont(float8) within group (float8)    - continuous percentile
     613             :  */
     614             : Datum
     615         104 : percentile_cont_float8_final(PG_FUNCTION_ARGS)
     616             : {
     617         104 :     return percentile_cont_final_common(fcinfo, FLOAT8OID, float8_lerp);
     618             : }
     619             : 
     620             : /*
     621             :  * percentile_cont(float8) within group (interval)  - continuous percentile
     622             :  */
     623             : Datum
     624           0 : percentile_cont_interval_final(PG_FUNCTION_ARGS)
     625             : {
     626           0 :     return percentile_cont_final_common(fcinfo, INTERVALOID, interval_lerp);
     627             : }
     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         300 : pct_info_cmp(const void *pa, const void *pb)
     649             : {
     650         300 :     const struct pct_info *a = (const struct pct_info *) pa;
     651         300 :     const struct pct_info *b = (const struct pct_info *) pb;
     652             : 
     653         300 :     if (a->first_row != b->first_row)
     654         258 :         return (a->first_row < b->first_row) ? -1 : 1;
     655          42 :     if (a->second_row != b->second_row)
     656           6 :         return (a->second_row < b->second_row) ? -1 : 1;
     657          36 :     return 0;
     658             : }
     659             : 
     660             : /*
     661             :  * Construct array showing which rows to sample for percentiles.
     662             :  */
     663             : static struct pct_info *
     664          30 : setup_pct_info(int num_percentiles,
     665             :                Datum *percentiles_datum,
     666             :                bool *percentiles_null,
     667             :                int64 rowcount,
     668             :                bool continuous)
     669             : {
     670             :     struct pct_info *pct_info;
     671             :     int         i;
     672             : 
     673          30 :     pct_info = (struct pct_info *) palloc(num_percentiles * sizeof(struct pct_info));
     674             : 
     675         222 :     for (i = 0; i < num_percentiles; i++)
     676             :     {
     677         192 :         pct_info[i].idx = i;
     678             : 
     679         192 :         if (percentiles_null[i])
     680             :         {
     681             :             /* dummy entry for any NULL in array */
     682          12 :             pct_info[i].first_row = 0;
     683          12 :             pct_info[i].second_row = 0;
     684          12 :             pct_info[i].proportion = 0;
     685             :         }
     686             :         else
     687             :         {
     688         180 :             double      p = DatumGetFloat8(percentiles_datum[i]);
     689             : 
     690         180 :             if (p < 0 || p > 1 || isnan(p))
     691           0 :                 ereport(ERROR,
     692             :                         (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     693             :                          errmsg("percentile value %g is not between 0 and 1",
     694             :                                 p)));
     695             : 
     696         180 :             if (continuous)
     697             :             {
     698          96 :                 pct_info[i].first_row = 1 + floor(p * (rowcount - 1));
     699          96 :                 pct_info[i].second_row = 1 + ceil(p * (rowcount - 1));
     700          96 :                 pct_info[i].proportion = (p * (rowcount - 1)) - floor(p * (rowcount - 1));
     701             :             }
     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          84 :                 int64       row = (int64) ceil(p * rowcount);
     711             : 
     712          84 :                 row = Max(1, row);
     713          84 :                 pct_info[i].first_row = row;
     714          84 :                 pct_info[i].second_row = row;
     715          84 :                 pct_info[i].proportion = 0;
     716             :             }
     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          30 :     qsort(pct_info, num_percentiles, sizeof(struct pct_info), pct_info_cmp);
     725             : 
     726          30 :     return pct_info;
     727             : }
     728             : 
     729             : /*
     730             :  * percentile_disc(float8[]) within group (anyelement)  - discrete percentiles
     731             :  */
     732             : Datum
     733          18 : percentile_disc_multi_final(PG_FUNCTION_ARGS)
     734             : {
     735             :     OSAPerGroupState *osastate;
     736             :     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          18 :     int64       rownum = 0;
     744          18 :     Datum       val = (Datum) 0;
     745          18 :     bool        isnull = true;
     746             :     int         i;
     747             : 
     748             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
     749             : 
     750             :     /* If there were no regular rows, the result is NULL */
     751          18 :     if (PG_ARGISNULL(0))
     752           0 :         PG_RETURN_NULL();
     753             : 
     754          18 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     755             : 
     756             :     /* number_of_rows could be zero if we only saw NULL input values */
     757          18 :     if (osastate->number_of_rows == 0)
     758           0 :         PG_RETURN_NULL();
     759             : 
     760             :     /* Deconstruct the percentile-array input */
     761          18 :     if (PG_ARGISNULL(1))
     762           0 :         PG_RETURN_NULL();
     763          18 :     param = PG_GETARG_ARRAYTYPE_P(1);
     764             : 
     765          18 :     deconstruct_array_builtin(param, FLOAT8OID,
     766             :                               &percentiles_datum,
     767             :                               &percentiles_null,
     768             :                               &num_percentiles);
     769             : 
     770          18 :     if (num_percentiles == 0)
     771           0 :         PG_RETURN_POINTER(construct_empty_array(osastate->qstate->sortColType));
     772             : 
     773          18 :     pct_info = setup_pct_info(num_percentiles,
     774             :                               percentiles_datum,
     775             :                               percentiles_null,
     776             :                               osastate->number_of_rows,
     777             :                               false);
     778             : 
     779          18 :     result_datum = (Datum *) palloc(num_percentiles * sizeof(Datum));
     780          18 :     result_isnull = (bool *) palloc(num_percentiles * sizeof(bool));
     781             : 
     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          30 :     for (i = 0; i < num_percentiles; i++)
     787             :     {
     788          30 :         int         idx = pct_info[i].idx;
     789             : 
     790          30 :         if (pct_info[i].first_row > 0)
     791          18 :             break;
     792             : 
     793          12 :         result_datum[idx] = (Datum) 0;
     794          12 :         result_isnull[idx] = true;
     795             :     }
     796             : 
     797             :     /*
     798             :      * If there's anything left after doing the nulls, then grind the input
     799             :      * and extract the needed values
     800             :      */
     801          18 :     if (i < num_percentiles)
     802             :     {
     803             :         /* Finish the sort, or rescan if we already did */
     804          18 :         if (!osastate->sort_done)
     805             :         {
     806          18 :             tuplesort_performsort(osastate->sortstate);
     807          18 :             osastate->sort_done = true;
     808             :         }
     809             :         else
     810           0 :             tuplesort_rescan(osastate->sortstate);
     811             : 
     812         102 :         for (; i < num_percentiles; i++)
     813             :         {
     814          84 :             int64       target_row = pct_info[i].first_row;
     815          84 :             int         idx = pct_info[i].idx;
     816             : 
     817             :             /* Advance to target row, if not already there */
     818          84 :             if (target_row > rownum)
     819             :             {
     820          84 :                 if (!tuplesort_skiptuples(osastate->sortstate, target_row - rownum - 1, true))
     821           0 :                     elog(ERROR, "missing row in percentile_disc");
     822             : 
     823          84 :                 if (!tuplesort_getdatum(osastate->sortstate, true, true, &val,
     824             :                                         &isnull, NULL))
     825           0 :                     elog(ERROR, "missing row in percentile_disc");
     826             : 
     827          84 :                 rownum = target_row;
     828             :             }
     829             : 
     830          84 :             result_datum[idx] = val;
     831          84 :             result_isnull[idx] = isnull;
     832             :         }
     833             :     }
     834             : 
     835             :     /* We make the output array the same shape as the input */
     836          18 :     PG_RETURN_POINTER(construct_md_array(result_datum, result_isnull,
     837             :                                          ARR_NDIM(param),
     838             :                                          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          12 : percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
     851             :                                    Oid expect_type,
     852             :                                    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          12 :     int64       rownum = 0;
     864          12 :     Datum       first_val = (Datum) 0;
     865          12 :     Datum       second_val = (Datum) 0;
     866             :     bool        isnull;
     867             :     int         i;
     868             : 
     869             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
     870             : 
     871             :     /* If there were no regular rows, the result is NULL */
     872          12 :     if (PG_ARGISNULL(0))
     873           0 :         PG_RETURN_NULL();
     874             : 
     875          12 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
     876             : 
     877             :     /* number_of_rows could be zero if we only saw NULL input values */
     878          12 :     if (osastate->number_of_rows == 0)
     879           0 :         PG_RETURN_NULL();
     880             : 
     881             :     Assert(expect_type == osastate->qstate->sortColType);
     882             : 
     883             :     /* Deconstruct the percentile-array input */
     884          12 :     if (PG_ARGISNULL(1))
     885           0 :         PG_RETURN_NULL();
     886          12 :     param = PG_GETARG_ARRAYTYPE_P(1);
     887             : 
     888          12 :     deconstruct_array_builtin(param, FLOAT8OID,
     889             :                               &percentiles_datum,
     890             :                               &percentiles_null,
     891             :                               &num_percentiles);
     892             : 
     893          12 :     if (num_percentiles == 0)
     894           0 :         PG_RETURN_POINTER(construct_empty_array(osastate->qstate->sortColType));
     895             : 
     896          12 :     pct_info = setup_pct_info(num_percentiles,
     897             :                               percentiles_datum,
     898             :                               percentiles_null,
     899             :                               osastate->number_of_rows,
     900             :                               true);
     901             : 
     902          12 :     result_datum = (Datum *) palloc(num_percentiles * sizeof(Datum));
     903          12 :     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          12 :     for (i = 0; i < num_percentiles; i++)
     910             :     {
     911          12 :         int         idx = pct_info[i].idx;
     912             : 
     913          12 :         if (pct_info[i].first_row > 0)
     914          12 :             break;
     915             : 
     916           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          12 :     if (i < num_percentiles)
     925             :     {
     926             :         /* Finish the sort, or rescan if we already did */
     927          12 :         if (!osastate->sort_done)
     928             :         {
     929          12 :             tuplesort_performsort(osastate->sortstate);
     930          12 :             osastate->sort_done = true;
     931             :         }
     932             :         else
     933           0 :             tuplesort_rescan(osastate->sortstate);
     934             : 
     935         108 :         for (; i < num_percentiles; i++)
     936             :         {
     937          96 :             int64       first_row = pct_info[i].first_row;
     938          96 :             int64       second_row = pct_info[i].second_row;
     939          96 :             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          96 :             if (first_row > rownum)
     948             :             {
     949          48 :                 if (!tuplesort_skiptuples(osastate->sortstate, first_row - rownum - 1, true))
     950           0 :                     elog(ERROR, "missing row in percentile_cont");
     951             : 
     952          48 :                 if (!tuplesort_getdatum(osastate->sortstate, true, true,
     953          48 :                                         &first_val, &isnull, NULL) || isnull)
     954           0 :                     elog(ERROR, "missing row in percentile_cont");
     955             : 
     956          48 :                 rownum = first_row;
     957             :                 /* Always advance second_val to be latest input value */
     958          48 :                 second_val = first_val;
     959             :             }
     960          48 :             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          24 :                 first_val = second_val;
     968             :             }
     969             : 
     970             :             /* Fetch second_row if needed */
     971          96 :             if (second_row > rownum)
     972             :             {
     973          36 :                 if (!tuplesort_getdatum(osastate->sortstate, true, true,
     974          36 :                                         &second_val, &isnull, NULL) || isnull)
     975           0 :                     elog(ERROR, "missing row in percentile_cont");
     976          36 :                 rownum++;
     977             :             }
     978             :             /* We should now certainly be on second_row exactly */
     979             :             Assert(second_row == rownum);
     980             : 
     981             :             /* Compute appropriate result */
     982          96 :             if (second_row > first_row)
     983          60 :                 result_datum[idx] = lerpfunc(first_val, second_val,
     984          60 :                                              pct_info[i].proportion);
     985             :             else
     986          36 :                 result_datum[idx] = first_val;
     987             : 
     988          96 :             result_isnull[idx] = false;
     989             :         }
     990             :     }
     991             : 
     992             :     /* We make the output array the same shape as the input */
     993          12 :     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          12 : percentile_cont_float8_multi_final(PG_FUNCTION_ARGS)
    1007             : {
    1008          12 :     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           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          60 : mode_final(PG_FUNCTION_ARGS)
    1036             : {
    1037             :     OSAPerGroupState *osastate;
    1038             :     Datum       val;
    1039             :     bool        isnull;
    1040          60 :     Datum       mode_val = (Datum) 0;
    1041          60 :     int64       mode_freq = 0;
    1042          60 :     Datum       last_val = (Datum) 0;
    1043          60 :     int64       last_val_freq = 0;
    1044          60 :     bool        last_val_is_mode = false;
    1045             :     FmgrInfo   *equalfn;
    1046          60 :     Datum       abbrev_val = (Datum) 0;
    1047          60 :     Datum       last_abbrev_val = (Datum) 0;
    1048             :     bool        shouldfree;
    1049             : 
    1050             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
    1051             : 
    1052             :     /* If there were no regular rows, the result is NULL */
    1053          60 :     if (PG_ARGISNULL(0))
    1054           0 :         PG_RETURN_NULL();
    1055             : 
    1056          60 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
    1057             : 
    1058             :     /* number_of_rows could be zero if we only saw NULL input values */
    1059          60 :     if (osastate->number_of_rows == 0)
    1060           0 :         PG_RETURN_NULL();
    1061             : 
    1062             :     /* Look up the equality function for the datatype, if we didn't already */
    1063          60 :     equalfn = &(osastate->qstate->equalfn);
    1064          60 :     if (!OidIsValid(equalfn->fn_oid))
    1065           6 :         fmgr_info_cxt(get_opcode(osastate->qstate->eqOperator), equalfn,
    1066           6 :                       osastate->qstate->qcontext);
    1067             : 
    1068          60 :     shouldfree = !(osastate->qstate->typByVal);
    1069             : 
    1070             :     /* Finish the sort, or rescan if we already did */
    1071          60 :     if (!osastate->sort_done)
    1072             :     {
    1073          60 :         tuplesort_performsort(osastate->sortstate);
    1074          60 :         osastate->sort_done = true;
    1075             :     }
    1076             :     else
    1077           0 :         tuplesort_rescan(osastate->sortstate);
    1078             : 
    1079             :     /* Scan tuples and count frequencies */
    1080       60060 :     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       60000 :         if (isnull)
    1085           0 :             continue;
    1086             : 
    1087       60000 :         if (last_val_freq == 0)
    1088             :         {
    1089             :             /* first nonnull value - it's the mode for now */
    1090          60 :             mode_val = last_val = val;
    1091          60 :             mode_freq = last_val_freq = 1;
    1092          60 :             last_val_is_mode = true;
    1093          60 :             last_abbrev_val = abbrev_val;
    1094             :         }
    1095      119880 :         else if (abbrev_val == last_abbrev_val &&
    1096       59940 :                  DatumGetBool(FunctionCall2Coll(equalfn, PG_GET_COLLATION(), val, last_val)))
    1097             :         {
    1098             :             /* value equal to previous value, count it */
    1099       59760 :             if (last_val_is_mode)
    1100       15924 :                 mode_freq++;    /* needn't maintain last_val_freq */
    1101       43836 :             else if (++last_val_freq > mode_freq)
    1102             :             {
    1103             :                 /* last_val becomes new mode */
    1104          66 :                 if (shouldfree)
    1105          66 :                     pfree(DatumGetPointer(mode_val));
    1106          66 :                 mode_val = last_val;
    1107          66 :                 mode_freq = last_val_freq;
    1108          66 :                 last_val_is_mode = true;
    1109             :             }
    1110       59760 :             if (shouldfree)
    1111       59760 :                 pfree(DatumGetPointer(val));
    1112             :         }
    1113             :         else
    1114             :         {
    1115             :             /* val should replace last_val */
    1116         180 :             if (shouldfree && !last_val_is_mode)
    1117          72 :                 pfree(DatumGetPointer(last_val));
    1118         180 :             last_val = val;
    1119             :             /* avoid equality function calls by reusing abbreviated keys */
    1120         180 :             last_abbrev_val = abbrev_val;
    1121         180 :             last_val_freq = 1;
    1122         180 :             last_val_is_mode = false;
    1123             :         }
    1124             : 
    1125       60000 :         CHECK_FOR_INTERRUPTS();
    1126             :     }
    1127             : 
    1128          60 :     if (shouldfree && !last_val_is_mode)
    1129          42 :         pfree(DatumGetPointer(last_val));
    1130             : 
    1131          60 :     if (mode_freq)
    1132          60 :         PG_RETURN_DATUM(mode_val);
    1133             :     else
    1134           0 :         PG_RETURN_NULL();
    1135             : }
    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         274 : hypothetical_check_argtypes(FunctionCallInfo fcinfo, int nargs,
    1145             :                             TupleDesc tupdesc)
    1146             : {
    1147             :     int         i;
    1148             : 
    1149             :     /* check that we have an int4 flag column */
    1150         274 :     if (!tupdesc ||
    1151         274 :         (nargs + 1) != tupdesc->natts ||
    1152         274 :         TupleDescAttr(tupdesc, nargs)->atttypid != INT4OID)
    1153           0 :         elog(ERROR, "type mismatch in hypothetical-set function");
    1154             : 
    1155             :     /* check that direct args match in type with aggregated args */
    1156         838 :     for (i = 0; i < nargs; i++)
    1157             :     {
    1158         564 :         Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
    1159             : 
    1160         564 :         if (get_fn_expr_argtype(fcinfo->flinfo, i + 1) != attr->atttypid)
    1161           0 :             elog(ERROR, "type mismatch in hypothetical-set function");
    1162             :     }
    1163         274 : }
    1164             : 
    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         250 : hypothetical_rank_common(FunctionCallInfo fcinfo, int flag,
    1174             :                          int64 *number_of_rows)
    1175             : {
    1176         250 :     int         nargs = PG_NARGS() - 1;
    1177         250 :     int64       rank = 1;
    1178             :     OSAPerGroupState *osastate;
    1179             :     TupleTableSlot *slot;
    1180             :     int         i;
    1181             : 
    1182             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
    1183             : 
    1184             :     /* If there were no regular rows, the rank is always 1 */
    1185         250 :     if (PG_ARGISNULL(0))
    1186             :     {
    1187           6 :         *number_of_rows = 0;
    1188           6 :         return 1;
    1189             :     }
    1190             : 
    1191         244 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
    1192         244 :     *number_of_rows = osastate->number_of_rows;
    1193             : 
    1194             :     /* Adjust nargs to be the number of direct (or aggregated) args */
    1195         244 :     if (nargs % 2 != 0)
    1196           0 :         elog(ERROR, "wrong number of arguments in hypothetical-set function");
    1197         244 :     nargs /= 2;
    1198             : 
    1199         244 :     hypothetical_check_argtypes(fcinfo, nargs, osastate->qstate->tupdesc);
    1200             : 
    1201             :     /* because we need a hypothetical row, we can't share transition state */
    1202             :     Assert(!osastate->sort_done);
    1203             : 
    1204             :     /* insert the hypothetical row into the sort */
    1205         244 :     slot = osastate->qstate->tupslot;
    1206         244 :     ExecClearTuple(slot);
    1207         778 :     for (i = 0; i < nargs; i++)
    1208             :     {
    1209         534 :         slot->tts_values[i] = PG_GETARG_DATUM(i + 1);
    1210         534 :         slot->tts_isnull[i] = PG_ARGISNULL(i + 1);
    1211             :     }
    1212         244 :     slot->tts_values[i] = Int32GetDatum(flag);
    1213         244 :     slot->tts_isnull[i] = false;
    1214         244 :     ExecStoreVirtualTuple(slot);
    1215             : 
    1216         244 :     tuplesort_puttupleslot(osastate->sortstate, slot);
    1217             : 
    1218             :     /* finish the sort */
    1219         244 :     tuplesort_performsort(osastate->sortstate);
    1220         244 :     osastate->sort_done = true;
    1221             : 
    1222             :     /* iterate till we find the hypothetical row */
    1223        4274 :     while (tuplesort_gettupleslot(osastate->sortstate, true, true, slot, NULL))
    1224             :     {
    1225             :         bool        isnull;
    1226        4274 :         Datum       d = slot_getattr(slot, nargs + 1, &isnull);
    1227             : 
    1228        4274 :         if (!isnull && DatumGetInt32(d) != 0)
    1229         244 :             break;
    1230             : 
    1231        4030 :         rank++;
    1232             : 
    1233        4030 :         CHECK_FOR_INTERRUPTS();
    1234             :     }
    1235             : 
    1236         244 :     ExecClearTuple(slot);
    1237             : 
    1238         244 :     return rank;
    1239             : }
    1240             : 
    1241             : 
    1242             : /*
    1243             :  * rank()  - rank of hypothetical row
    1244             :  */
    1245             : Datum
    1246         232 : hypothetical_rank_final(PG_FUNCTION_ARGS)
    1247             : {
    1248             :     int64       rank;
    1249             :     int64       rowcount;
    1250             : 
    1251         232 :     rank = hypothetical_rank_common(fcinfo, -1, &rowcount);
    1252             : 
    1253         232 :     PG_RETURN_INT64(rank);
    1254             : }
    1255             : 
    1256             : /*
    1257             :  * percent_rank()   - percentile rank of hypothetical row
    1258             :  */
    1259             : Datum
    1260          12 : hypothetical_percent_rank_final(PG_FUNCTION_ARGS)
    1261             : {
    1262             :     int64       rank;
    1263             :     int64       rowcount;
    1264             :     double      result_val;
    1265             : 
    1266          12 :     rank = hypothetical_rank_common(fcinfo, -1, &rowcount);
    1267             : 
    1268          12 :     if (rowcount == 0)
    1269           6 :         PG_RETURN_FLOAT8(0);
    1270             : 
    1271           6 :     result_val = (double) (rank - 1) / (double) (rowcount);
    1272             : 
    1273           6 :     PG_RETURN_FLOAT8(result_val);
    1274             : }
    1275             : 
    1276             : /*
    1277             :  * cume_dist()  - cumulative distribution of hypothetical row
    1278             :  */
    1279             : Datum
    1280           6 : hypothetical_cume_dist_final(PG_FUNCTION_ARGS)
    1281             : {
    1282             :     int64       rank;
    1283             :     int64       rowcount;
    1284             :     double      result_val;
    1285             : 
    1286           6 :     rank = hypothetical_rank_common(fcinfo, 1, &rowcount);
    1287             : 
    1288           6 :     result_val = (double) (rank) / (double) (rowcount + 1);
    1289             : 
    1290           6 :     PG_RETURN_FLOAT8(result_val);
    1291             : }
    1292             : 
    1293             : /*
    1294             :  * dense_rank() - rank of hypothetical row without gaps in ranking
    1295             :  */
    1296             : Datum
    1297          30 : hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
    1298             : {
    1299             :     ExprContext *econtext;
    1300             :     ExprState  *compareTuple;
    1301          30 :     int         nargs = PG_NARGS() - 1;
    1302          30 :     int64       rank = 1;
    1303          30 :     int64       duplicate_count = 0;
    1304             :     OSAPerGroupState *osastate;
    1305             :     int         numDistinctCols;
    1306          30 :     Datum       abbrevVal = (Datum) 0;
    1307          30 :     Datum       abbrevOld = (Datum) 0;
    1308             :     TupleTableSlot *slot;
    1309             :     TupleTableSlot *extraslot;
    1310             :     TupleTableSlot *slot2;
    1311             :     int         i;
    1312             : 
    1313             :     Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);
    1314             : 
    1315             :     /* If there were no regular rows, the rank is always 1 */
    1316          30 :     if (PG_ARGISNULL(0))
    1317           0 :         PG_RETURN_INT64(rank);
    1318             : 
    1319          30 :     osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
    1320          30 :     econtext = osastate->qstate->econtext;
    1321          30 :     if (!econtext)
    1322             :     {
    1323             :         MemoryContext oldcontext;
    1324             : 
    1325             :         /* Make sure to we create econtext under correct parent context. */
    1326          18 :         oldcontext = MemoryContextSwitchTo(osastate->qstate->qcontext);
    1327          18 :         osastate->qstate->econtext = CreateStandaloneExprContext();
    1328          18 :         econtext = osastate->qstate->econtext;
    1329          18 :         MemoryContextSwitchTo(oldcontext);
    1330             :     }
    1331             : 
    1332             :     /* Adjust nargs to be the number of direct (or aggregated) args */
    1333          30 :     if (nargs % 2 != 0)
    1334           0 :         elog(ERROR, "wrong number of arguments in hypothetical-set function");
    1335          30 :     nargs /= 2;
    1336             : 
    1337          30 :     hypothetical_check_argtypes(fcinfo, nargs, osastate->qstate->tupdesc);
    1338             : 
    1339             :     /*
    1340             :      * When comparing tuples, we can omit the flag column since we will only
    1341             :      * compare rows with flag == 0.
    1342             :      */
    1343          30 :     numDistinctCols = osastate->qstate->numSortCols - 1;
    1344             : 
    1345             :     /* Build tuple comparator, if we didn't already */
    1346          30 :     compareTuple = osastate->qstate->compareTuple;
    1347          30 :     if (compareTuple == NULL)
    1348             :     {
    1349          18 :         AttrNumber *sortColIdx = osastate->qstate->sortColIdx;
    1350             :         MemoryContext oldContext;
    1351             : 
    1352          18 :         oldContext = MemoryContextSwitchTo(osastate->qstate->qcontext);
    1353          18 :         compareTuple = execTuplesMatchPrepare(osastate->qstate->tupdesc,
    1354             :                                               numDistinctCols,
    1355             :                                               sortColIdx,
    1356          18 :                                               osastate->qstate->eqOperators,
    1357          18 :                                               osastate->qstate->sortCollations,
    1358             :                                               NULL);
    1359          18 :         MemoryContextSwitchTo(oldContext);
    1360          18 :         osastate->qstate->compareTuple = compareTuple;
    1361             :     }
    1362             : 
    1363             :     /* because we need a hypothetical row, we can't share transition state */
    1364             :     Assert(!osastate->sort_done);
    1365             : 
    1366             :     /* insert the hypothetical row into the sort */
    1367          30 :     slot = osastate->qstate->tupslot;
    1368          30 :     ExecClearTuple(slot);
    1369          60 :     for (i = 0; i < nargs; i++)
    1370             :     {
    1371          30 :         slot->tts_values[i] = PG_GETARG_DATUM(i + 1);
    1372          30 :         slot->tts_isnull[i] = PG_ARGISNULL(i + 1);
    1373             :     }
    1374          30 :     slot->tts_values[i] = Int32GetDatum(-1);
    1375          30 :     slot->tts_isnull[i] = false;
    1376          30 :     ExecStoreVirtualTuple(slot);
    1377             : 
    1378          30 :     tuplesort_puttupleslot(osastate->sortstate, slot);
    1379             : 
    1380             :     /* finish the sort */
    1381          30 :     tuplesort_performsort(osastate->sortstate);
    1382          30 :     osastate->sort_done = true;
    1383             : 
    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          30 :     extraslot = MakeSingleTupleTableSlot(osastate->qstate->tupdesc,
    1390             :                                          &TTSOpsMinimalTuple);
    1391          30 :     slot2 = extraslot;
    1392             : 
    1393             :     /* iterate till we find the hypothetical row */
    1394          66 :     while (tuplesort_gettupleslot(osastate->sortstate, true, true, slot,
    1395             :                                   &abbrevVal))
    1396             :     {
    1397             :         bool        isnull;
    1398          66 :         Datum       d = slot_getattr(slot, nargs + 1, &isnull);
    1399             :         TupleTableSlot *tmpslot;
    1400             : 
    1401          66 :         if (!isnull && DatumGetInt32(d) != 0)
    1402          30 :             break;
    1403             : 
    1404             :         /* count non-distinct tuples */
    1405          36 :         econtext->ecxt_outertuple = slot;
    1406          36 :         econtext->ecxt_innertuple = slot2;
    1407             : 
    1408          36 :         if (!TupIsNull(slot2) &&
    1409          48 :             abbrevVal == abbrevOld &&
    1410          24 :             ExecQualAndReset(compareTuple, econtext))
    1411          12 :             duplicate_count++;
    1412             : 
    1413          36 :         tmpslot = slot2;
    1414          36 :         slot2 = slot;
    1415          36 :         slot = tmpslot;
    1416             :         /* avoid ExecQual() calls by reusing abbreviated keys */
    1417          36 :         abbrevOld = abbrevVal;
    1418             : 
    1419          36 :         rank++;
    1420             : 
    1421          36 :         CHECK_FOR_INTERRUPTS();
    1422             :     }
    1423             : 
    1424          30 :     ExecClearTuple(slot);
    1425          30 :     ExecClearTuple(slot2);
    1426             : 
    1427          30 :     ExecDropSingleTupleTableSlot(extraslot);
    1428             : 
    1429          30 :     rank = rank - duplicate_count;
    1430             : 
    1431          30 :     PG_RETURN_INT64(rank);
    1432             : }

Generated by: LCOV version 1.14