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

Generated by: LCOV version 1.14