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

Generated by: LCOV version 1.11