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

Generated by: LCOV version 1.13