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