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

Generated by: LCOV version 1.14