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