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