PostgreSQL Source Code git master
Loading...
Searching...
No Matches
tuplesortvariants.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * tuplesortvariants.c
4 * Implementation of tuple sorting variants.
5 *
6 * This module handles the sorting of heap tuples, index tuples, or single
7 * Datums. The implementation is based on the generalized tuple sorting
8 * facility given in tuplesort.c. Support other kinds of sortable objects
9 * could be easily added here, another module, or even an extension.
10 *
11 *
12 * Copyright (c) 2022-2026, PostgreSQL Global Development Group
13 *
14 * IDENTIFICATION
15 * src/backend/utils/sort/tuplesortvariants.c
16 *
17 *-------------------------------------------------------------------------
18 */
19
20#include "postgres.h"
21
22#include "access/brin_tuple.h"
23#include "access/gin.h"
24#include "access/gin_tuple.h"
25#include "access/hash.h"
26#include "access/htup_details.h"
27#include "access/nbtree.h"
28#include "catalog/index.h"
30#include "executor/executor.h"
31#include "pg_trace.h"
32#include "utils/builtins.h"
33#include "utils/datum.h"
34#include "utils/guc.h"
35#include "utils/lsyscache.h"
36#include "utils/rel.h"
37#include "utils/tuplesort.h"
38
39
40/* sort-type codes for sort__start probes */
41#define HEAP_SORT 0
42#define INDEX_SORT 1
43#define DATUM_SORT 2
44#define CLUSTER_SORT 3
45
47 int count);
49 int count);
51 int count);
53 int count);
55 int count);
57 int count);
58static int comparetup_heap(const SortTuple *a, const SortTuple *b,
60static int comparetup_heap_tiebreak(const SortTuple *a, const SortTuple *b,
65 LogicalTape *tape, unsigned int len);
66static int comparetup_cluster(const SortTuple *a, const SortTuple *b,
68static int comparetup_cluster_tiebreak(const SortTuple *a, const SortTuple *b,
73 LogicalTape *tape, unsigned int tuplen);
74static int comparetup_index_btree(const SortTuple *a, const SortTuple *b,
76static int comparetup_index_btree_tiebreak(const SortTuple *a, const SortTuple *b,
78static int comparetup_index_hash(const SortTuple *a, const SortTuple *b,
80static int comparetup_index_hash_tiebreak(const SortTuple *a, const SortTuple *b,
82static int comparetup_index_brin(const SortTuple *a, const SortTuple *b,
84static int comparetup_index_gin(const SortTuple *a, const SortTuple *b,
89 LogicalTape *tape, unsigned int len);
93 LogicalTape *tape, unsigned int len);
97 LogicalTape *tape, unsigned int len);
98static int comparetup_datum(const SortTuple *a, const SortTuple *b,
100static int comparetup_datum_tiebreak(const SortTuple *a, const SortTuple *b,
103 SortTuple *stup);
105 LogicalTape *tape, unsigned int len);
107
108/*
109 * Data structure pointed by "TuplesortPublic.arg" for the CLUSTER case. Set by
110 * the tuplesort_begin_cluster.
111 */
112typedef struct
113{
115
116 IndexInfo *indexInfo; /* info about index being used for reference */
117 EState *estate; /* for evaluating index expressions */
119
120/*
121 * Data structure pointed by "TuplesortPublic.arg" for the IndexTuple case.
122 * Set by tuplesort_begin_index_xxx and used only by the IndexTuple routines.
123 */
124typedef struct
125{
126 Relation heapRel; /* table the index is being built on */
127 Relation indexRel; /* index being built */
129
130/*
131 * Data structure pointed by "TuplesortPublic.arg" for the index_btree subcase.
132 */
133typedef struct
134{
136
137 bool enforceUnique; /* complain if we find duplicate tuples */
138 bool uniqueNullsNotDistinct; /* unique constraint null treatment */
140
141/*
142 * Data structure pointed by "TuplesortPublic.arg" for the index_hash subcase.
143 */
144typedef struct
145{
147
148 uint32 high_mask; /* masks for sortable part of hash code */
152
153/*
154 * Data structure pointed by "TuplesortPublic.arg" for the Datum case.
155 * Set by tuplesort_begin_datum and used only by the DatumTuple routines.
156 */
157typedef struct
158{
159 /* the datatype oid of Datum's to be sorted */
161 /* we need typelen in order to know how to copy the Datums. */
164
165/*
166 * Computing BrinTuple size with only the tuple is difficult, so we want to track
167 * the length referenced by the SortTuple. That's what BrinSortTuple is meant
168 * to do - it's essentially a BrinTuple prefixed by its length.
169 */
175
176/* Size of the BrinSortTuple, given length of the BrinTuple. */
177#define BRINSORTTUPLE_SIZE(len) (offsetof(BrinSortTuple, tuple) + (len))
178
179
182 int nkeys, AttrNumber *attNums,
183 Oid *sortOperators, Oid *sortCollations,
184 bool *nullsFirstFlags,
185 int workMem, SortCoordinate coordinate, int sortopt)
186{
188 sortopt);
190 MemoryContext oldcontext;
191 int i;
192
193 oldcontext = MemoryContextSwitchTo(base->maincontext);
194
195 Assert(nkeys > 0);
196
197 if (trace_sort)
198 elog(LOG,
199 "begin tuple sort: nkeys = %d, workMem = %d, randomAccess = %c",
200 nkeys, workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
201
202 base->nKeys = nkeys;
203
205 false, /* no unique check */
206 nkeys,
207 workMem,
208 sortopt & TUPLESORT_RANDOMACCESS,
210
214 base->writetup = writetup_heap;
215 base->readtup = readtup_heap;
216 base->haveDatum1 = true;
217 base->arg = tupDesc; /* assume we need not copy tupDesc */
218
219 /* Prepare SortSupport data for each column */
220 base->sortKeys = (SortSupport) palloc0(nkeys * sizeof(SortSupportData));
221
222 for (i = 0; i < nkeys; i++)
223 {
224 SortSupport sortKey = base->sortKeys + i;
225
226 Assert(attNums[i] != 0);
227 Assert(sortOperators[i] != 0);
228
229 sortKey->ssup_cxt = CurrentMemoryContext;
230 sortKey->ssup_collation = sortCollations[i];
231 sortKey->ssup_nulls_first = nullsFirstFlags[i];
232 sortKey->ssup_attno = attNums[i];
233 /* Convey if abbreviation optimization is applicable in principle */
234 sortKey->abbreviate = (i == 0 && base->haveDatum1);
235
237 }
238
239 /*
240 * The "onlyKey" optimization cannot be used with abbreviated keys, since
241 * tie-breaker comparisons may be required. Typically, the optimization
242 * is only of value to pass-by-value types anyway, whereas abbreviated
243 * keys are typically only of value to pass-by-reference types.
244 */
245 if (nkeys == 1 && !base->sortKeys->abbrev_converter)
246 base->onlyKey = base->sortKeys;
247
248 MemoryContextSwitchTo(oldcontext);
249
250 return state;
251}
252
255 Relation indexRel,
256 int workMem,
257 SortCoordinate coordinate, int sortopt)
258{
260 sortopt);
263 MemoryContext oldcontext;
265 int i;
266
267 Assert(indexRel->rd_rel->relam == BTREE_AM_OID);
268
269 oldcontext = MemoryContextSwitchTo(base->maincontext);
271
272 if (trace_sort)
273 elog(LOG,
274 "begin tuple sort: nkeys = %d, workMem = %d, randomAccess = %c",
276 workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
277
279
281 false, /* no unique check */
282 base->nKeys,
283 workMem,
284 sortopt & TUPLESORT_RANDOMACCESS,
286
291 base->readtup = readtup_cluster;
293 base->arg = arg;
294
295 arg->indexInfo = BuildIndexInfo(indexRel);
296
297 /*
298 * If we don't have a simple leading attribute, we don't currently
299 * initialize datum1, so disable optimizations that require it.
300 */
301 if (arg->indexInfo->ii_IndexAttrNumbers[0] == 0)
302 base->haveDatum1 = false;
303 else
304 base->haveDatum1 = true;
305
306 arg->tupDesc = tupDesc; /* assume we need not copy tupDesc */
307
308 indexScanKey = _bt_mkscankey(indexRel, NULL);
309
310 if (arg->indexInfo->ii_Expressions != NULL)
311 {
312 TupleTableSlot *slot;
313 ExprContext *econtext;
314
315 /*
316 * We will need to use FormIndexDatum to evaluate the index
317 * expressions. To do that, we need an EState, as well as a
318 * TupleTableSlot to put the table tuples into. The econtext's
319 * scantuple has to point to that slot, too.
320 */
321 arg->estate = CreateExecutorState();
323 econtext = GetPerTupleExprContext(arg->estate);
324 econtext->ecxt_scantuple = slot;
325 }
326
327 /* Prepare SortSupport data for each column */
328 base->sortKeys = (SortSupport) palloc0(base->nKeys *
329 sizeof(SortSupportData));
330
331 for (i = 0; i < base->nKeys; i++)
332 {
333 SortSupport sortKey = base->sortKeys + i;
334 ScanKey scanKey = indexScanKey->scankeys + i;
335 bool reverse;
336
337 sortKey->ssup_cxt = CurrentMemoryContext;
338 sortKey->ssup_collation = scanKey->sk_collation;
339 sortKey->ssup_nulls_first =
340 (scanKey->sk_flags & SK_BT_NULLS_FIRST) != 0;
341 sortKey->ssup_attno = scanKey->sk_attno;
342 /* Convey if abbreviation optimization is applicable in principle */
343 sortKey->abbreviate = (i == 0 && base->haveDatum1);
344
345 Assert(sortKey->ssup_attno != 0);
346
347 reverse = (scanKey->sk_flags & SK_BT_DESC) != 0;
348
349 PrepareSortSupportFromIndexRel(indexRel, reverse, sortKey);
350 }
351
353
354 MemoryContextSwitchTo(oldcontext);
355
356 return state;
357}
358
361 Relation indexRel,
362 bool enforceUnique,
363 bool uniqueNullsNotDistinct,
364 int workMem,
366 int sortopt)
367{
369 sortopt);
373 MemoryContext oldcontext;
374 int i;
375
376 oldcontext = MemoryContextSwitchTo(base->maincontext);
378
379 if (trace_sort)
380 elog(LOG,
381 "begin index sort: unique = %c, workMem = %d, randomAccess = %c",
382 enforceUnique ? 't' : 'f',
383 workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
384
386
388 enforceUnique,
389 base->nKeys,
390 workMem,
391 sortopt & TUPLESORT_RANDOMACCESS,
393
397 base->writetup = writetup_index;
398 base->readtup = readtup_index;
399 base->haveDatum1 = true;
400 base->arg = arg;
401
402 arg->index.heapRel = heapRel;
403 arg->index.indexRel = indexRel;
404 arg->enforceUnique = enforceUnique;
405 arg->uniqueNullsNotDistinct = uniqueNullsNotDistinct;
406
407 indexScanKey = _bt_mkscankey(indexRel, NULL);
408
409 /* Prepare SortSupport data for each column */
410 base->sortKeys = (SortSupport) palloc0(base->nKeys *
411 sizeof(SortSupportData));
412
413 for (i = 0; i < base->nKeys; i++)
414 {
415 SortSupport sortKey = base->sortKeys + i;
416 ScanKey scanKey = indexScanKey->scankeys + i;
417 bool reverse;
418
419 sortKey->ssup_cxt = CurrentMemoryContext;
420 sortKey->ssup_collation = scanKey->sk_collation;
421 sortKey->ssup_nulls_first =
422 (scanKey->sk_flags & SK_BT_NULLS_FIRST) != 0;
423 sortKey->ssup_attno = scanKey->sk_attno;
424 /* Convey if abbreviation optimization is applicable in principle */
425 sortKey->abbreviate = (i == 0 && base->haveDatum1);
426
427 Assert(sortKey->ssup_attno != 0);
428
429 reverse = (scanKey->sk_flags & SK_BT_DESC) != 0;
430
431 PrepareSortSupportFromIndexRel(indexRel, reverse, sortKey);
432 }
433
435
436 MemoryContextSwitchTo(oldcontext);
437
438 return state;
439}
440
443 Relation indexRel,
444 uint32 high_mask,
445 uint32 low_mask,
446 uint32 max_buckets,
447 int workMem,
449 int sortopt)
450{
452 sortopt);
454 MemoryContext oldcontext;
456
457 oldcontext = MemoryContextSwitchTo(base->maincontext);
459
460 if (trace_sort)
461 elog(LOG,
462 "begin index sort: high_mask = 0x%x, low_mask = 0x%x, "
463 "max_buckets = 0x%x, workMem = %d, randomAccess = %c",
464 high_mask,
465 low_mask,
466 max_buckets,
467 workMem,
468 sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
469
470 base->nKeys = 1; /* Only one sort column, the hash code */
471
475 base->writetup = writetup_index;
476 base->readtup = readtup_index;
477 base->haveDatum1 = true;
478 base->arg = arg;
479
480 arg->index.heapRel = heapRel;
481 arg->index.indexRel = indexRel;
482
483 arg->high_mask = high_mask;
484 arg->low_mask = low_mask;
485 arg->max_buckets = max_buckets;
486
487 MemoryContextSwitchTo(oldcontext);
488
489 return state;
490}
491
494 Relation indexRel,
495 int workMem,
497 int sortopt)
498{
500 sortopt);
502 MemoryContext oldcontext;
504 int i;
505
506 oldcontext = MemoryContextSwitchTo(base->maincontext);
508
509 if (trace_sort)
510 elog(LOG,
511 "begin index sort: workMem = %d, randomAccess = %c",
512 workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
513
515
519 base->writetup = writetup_index;
520 base->readtup = readtup_index;
521 base->haveDatum1 = true;
522 base->arg = arg;
523
524 arg->index.heapRel = heapRel;
525 arg->index.indexRel = indexRel;
526 arg->enforceUnique = false;
527 arg->uniqueNullsNotDistinct = false;
528
529 /* Prepare SortSupport data for each column */
530 base->sortKeys = (SortSupport) palloc0(base->nKeys *
531 sizeof(SortSupportData));
532
533 for (i = 0; i < base->nKeys; i++)
534 {
535 SortSupport sortKey = base->sortKeys + i;
536
538 sortKey->ssup_collation = indexRel->rd_indcollation[i];
539 sortKey->ssup_nulls_first = false;
540 sortKey->ssup_attno = i + 1;
541 /* Convey if abbreviation optimization is applicable in principle */
542 sortKey->abbreviate = (i == 0 && base->haveDatum1);
543
544 Assert(sortKey->ssup_attno != 0);
545
546 /* Look for a sort support function */
548 }
549
550 MemoryContextSwitchTo(oldcontext);
551
552 return state;
553}
554
558 int sortopt)
559{
561 sortopt);
563
564 if (trace_sort)
565 elog(LOG,
566 "begin index sort: workMem = %d, randomAccess = %c",
567 workMem,
568 sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
569
570 base->nKeys = 1; /* Only one sort column, the block number */
571
576 base->haveDatum1 = true;
577 base->arg = NULL;
578
579 return state;
580}
581
584 Relation indexRel,
586 int sortopt)
587{
589 sortopt);
591 MemoryContext oldcontext;
592 int i;
593 TupleDesc desc = RelationGetDescr(indexRel);
594
595 oldcontext = MemoryContextSwitchTo(base->maincontext);
596
597#ifdef TRACE_SORT
598 if (trace_sort)
599 elog(LOG,
600 "begin index sort: workMem = %d, randomAccess = %c",
601 workMem,
602 sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
603#endif
604
605 /*
606 * Multi-column GIN indexes expand the row into a separate index entry for
607 * attribute, and that's what we write into the tuplesort. But we still
608 * need to initialize sortsupport for all the attributes.
609 */
611
612 /* Prepare SortSupport data for each column */
613 base->sortKeys = (SortSupport) palloc0(base->nKeys *
614 sizeof(SortSupportData));
615
616 for (i = 0; i < base->nKeys; i++)
617 {
618 SortSupport sortKey = base->sortKeys + i;
620 Oid cmpFunc;
621
622 sortKey->ssup_cxt = CurrentMemoryContext;
623 sortKey->ssup_collation = indexRel->rd_indcollation[i];
624 sortKey->ssup_nulls_first = false;
625 sortKey->ssup_attno = i + 1;
626 sortKey->abbreviate = false;
627
628 Assert(sortKey->ssup_attno != 0);
629
630 if (!OidIsValid(sortKey->ssup_collation))
631 sortKey->ssup_collation = DEFAULT_COLLATION_OID;
632
633 /*
634 * If the compare proc isn't specified in the opclass definition, look
635 * up the index key type's default btree comparator.
636 */
637 cmpFunc = index_getprocid(indexRel, i + 1, GIN_COMPARE_PROC);
638 if (cmpFunc == InvalidOid)
639 {
640 TypeCacheEntry *typentry;
641
642 typentry = lookup_type_cache(att->atttypid,
644 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
647 errmsg("could not identify a comparison function for type %s",
648 format_type_be(att->atttypid))));
649
650 cmpFunc = typentry->cmp_proc_finfo.fn_oid;
651 }
652
654 }
655
660 base->haveDatum1 = false;
661 base->arg = NULL;
662
663 MemoryContextSwitchTo(oldcontext);
664
665 return state;
666}
667
669tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation,
670 bool nullsFirstFlag, int workMem,
671 SortCoordinate coordinate, int sortopt)
672{
674 sortopt);
677 MemoryContext oldcontext;
678 int16 typlen;
679 bool typbyval;
680
681 oldcontext = MemoryContextSwitchTo(base->maincontext);
683
684 if (trace_sort)
685 elog(LOG,
686 "begin datum sort: workMem = %d, randomAccess = %c",
687 workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
688
689 base->nKeys = 1; /* always a one-column sort */
690
692 false, /* no unique check */
693 1,
694 workMem,
695 sortopt & TUPLESORT_RANDOMACCESS,
697
701 base->writetup = writetup_datum;
702 base->readtup = readtup_datum;
703 base->haveDatum1 = true;
704 base->arg = arg;
705
706 arg->datumType = datumType;
707
708 /* lookup necessary attributes of the datum type */
709 get_typlenbyval(datumType, &typlen, &typbyval);
710 arg->datumTypeLen = typlen;
711 base->tuples = !typbyval;
712
713 /* Prepare SortSupport data */
715
717 base->sortKeys->ssup_collation = sortCollation;
719
720 /*
721 * Abbreviation is possible here only for by-reference types. In theory,
722 * a pass-by-value datatype could have an abbreviated form that is cheaper
723 * to compare. In a tuple sort, we could support that, because we can
724 * always extract the original datum from the tuple as needed. Here, we
725 * can't, because a datum sort only stores a single copy of the datum; the
726 * "tuple" field of each SortTuple is NULL.
727 */
728 base->sortKeys->abbreviate = !typbyval;
729
730 PrepareSortSupportFromOrderingOp(sortOperator, base->sortKeys);
731
732 /*
733 * The "onlyKey" optimization cannot be used with abbreviated keys, since
734 * tie-breaker comparisons may be required. Typically, the optimization
735 * is only of value to pass-by-value types anyway, whereas abbreviated
736 * keys are typically only of value to pass-by-reference types.
737 */
738 if (!base->sortKeys->abbrev_converter)
739 base->onlyKey = base->sortKeys;
740
741 MemoryContextSwitchTo(oldcontext);
742
743 return state;
744}
745
746/*
747 * Accept one tuple while collecting input data for sort.
748 *
749 * Note that the input data is always copied; the caller need not save it.
750 */
751void
753{
756 TupleDesc tupDesc = (TupleDesc) base->arg;
758 MinimalTuple tuple;
759 HeapTupleData htup;
760 Size tuplen;
761
762 /* copy the tuple into sort storage */
763 tuple = ExecCopySlotMinimalTuple(slot);
764 stup.tuple = tuple;
765 /* set up first-column key value */
766 htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
767 htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
768 stup.datum1 = heap_getattr(&htup,
769 base->sortKeys[0].ssup_attno,
770 tupDesc,
771 &stup.isnull1);
772
773 /* GetMemoryChunkSpace is not supported for bump contexts */
775 tuplen = MAXALIGN(tuple->t_len);
776 else
777 tuplen = GetMemoryChunkSpace(tuple);
778
780 base->sortKeys->abbrev_converter &&
781 !stup.isnull1, tuplen);
782
783 MemoryContextSwitchTo(oldcontext);
784}
785
786/*
787 * Accept one tuple while collecting input data for sort.
788 *
789 * Note that the input data is always copied; the caller need not save it.
790 */
791void
793{
798 Size tuplen;
799
800 /* copy the tuple into sort storage */
802 stup.tuple = tup;
803
804 /*
805 * set up first-column key value, and potentially abbreviate, if it's a
806 * simple column
807 */
808 if (base->haveDatum1)
809 {
810 stup.datum1 = heap_getattr(tup,
811 arg->indexInfo->ii_IndexAttrNumbers[0],
812 arg->tupDesc,
813 &stup.isnull1);
814 }
815
816 /* GetMemoryChunkSpace is not supported for bump contexts */
818 tuplen = MAXALIGN(HEAPTUPLESIZE + tup->t_len);
819 else
820 tuplen = GetMemoryChunkSpace(tup);
821
823 base->haveDatum1 &&
824 base->sortKeys->abbrev_converter &&
825 !stup.isnull1, tuplen);
826
827 MemoryContextSwitchTo(oldcontext);
828}
829
830/*
831 * Collect one index tuple while collecting input data for sort, building
832 * it from caller-supplied values.
833 */
834void
836 const ItemPointerData *self, const Datum *values,
837 const bool *isnull)
838{
840 IndexTuple tuple;
843 Size tuplen;
844
846 isnull, base->tuplecontext);
847 tuple = ((IndexTuple) stup.tuple);
848 tuple->t_tid = *self;
849 /* set up first-column key value */
850 stup.datum1 = index_getattr(tuple,
851 1,
852 RelationGetDescr(arg->indexRel),
853 &stup.isnull1);
854
855 /* GetMemoryChunkSpace is not supported for bump contexts */
857 tuplen = MAXALIGN(tuple->t_info & INDEX_SIZE_MASK);
858 else
859 tuplen = GetMemoryChunkSpace(tuple);
860
862 base->sortKeys &&
863 base->sortKeys->abbrev_converter &&
864 !stup.isnull1, tuplen);
865}
866
867/*
868 * Collect one BRIN tuple while collecting input data for sort.
869 */
870void
872{
877 Size tuplen;
878
879 /* allocate space for the whole BRIN sort tuple */
881
882 bstup->tuplen = size;
883 memcpy(&bstup->tuple, tuple, size);
884
885 stup.tuple = bstup;
886 stup.datum1 = UInt32GetDatum(tuple->bt_blkno);
887 stup.isnull1 = false;
888
889 /* GetMemoryChunkSpace is not supported for bump contexts */
891 tuplen = MAXALIGN(BRINSORTTUPLE_SIZE(size));
892 else
893 tuplen = GetMemoryChunkSpace(bstup);
894
896 base->sortKeys &&
897 base->sortKeys->abbrev_converter &&
898 !stup.isnull1, tuplen);
899
900 MemoryContextSwitchTo(oldcontext);
901}
902
903void
905{
907 GinTuple *ctup;
910 Size tuplen;
911
912 /* copy the GinTuple into the right memory context */
913 ctup = palloc(size);
914 memcpy(ctup, tuple, size);
915
916 stup.tuple = ctup;
917 stup.datum1 = (Datum) 0;
918 stup.isnull1 = false;
919
920 /* GetMemoryChunkSpace is not supported for bump contexts */
922 tuplen = MAXALIGN(size);
923 else
924 tuplen = GetMemoryChunkSpace(ctup);
925
927 base->sortKeys &&
928 base->sortKeys->abbrev_converter &&
929 !stup.isnull1, tuplen);
930
931 MemoryContextSwitchTo(oldcontext);
932}
933
934/*
935 * Accept one Datum while collecting input data for sort.
936 *
937 * If the Datum is pass-by-ref type, the value will be copied.
938 */
939void
941{
946
947 /*
948 * Pass-by-value types or null values are just stored directly in
949 * stup.datum1 (and stup.tuple is not used and set to NULL).
950 *
951 * Non-null pass-by-reference values need to be copied into memory we
952 * control, and possibly abbreviated. The copied value is pointed to by
953 * stup.tuple and is treated as the canonical copy (e.g. to return via
954 * tuplesort_getdatum or when writing to tape); stup.datum1 gets the
955 * abbreviated value if abbreviation is happening, otherwise it's
956 * identical to stup.tuple.
957 */
958
959 if (isNull || !base->tuples)
960 {
961 /*
962 * Set datum1 to zeroed representation for NULLs (to be consistent,
963 * and to support cheap inequality tests for NULL abbreviated keys).
964 */
965 stup.datum1 = !isNull ? val : (Datum) 0;
966 stup.isnull1 = isNull;
967 stup.tuple = NULL; /* no separate storage */
968 }
969 else
970 {
971 stup.isnull1 = false;
972 stup.datum1 = datumCopy(val, false, arg->datumTypeLen);
973 stup.tuple = DatumGetPointer(stup.datum1);
974 }
975
977 base->tuples &&
978 base->sortKeys->abbrev_converter && !isNull, 0);
979
980 MemoryContextSwitchTo(oldcontext);
981}
982
983/*
984 * Fetch the next tuple in either forward or back direction.
985 * If successful, put tuple in slot and return true; else, clear the slot
986 * and return false.
987 *
988 * Caller may optionally be passed back abbreviated value (on true return
989 * value) when abbreviation was used, which can be used to cheaply avoid
990 * equality checks that might otherwise be required. Caller can safely make a
991 * determination of "non-equal tuple" based on simple binary inequality. A
992 * NULL value in leading attribute will set abbreviated value to zeroed
993 * representation, which caller may rely on in abbreviated inequality check.
994 *
995 * If copy is true, the slot receives a tuple that's been copied into the
996 * caller's memory context, so that it will stay valid regardless of future
997 * manipulations of the tuplesort's state (up to and including deleting the
998 * tuplesort). If copy is false, the slot will just receive a pointer to a
999 * tuple held within the tuplesort, which is more efficient, but only safe for
1000 * callers that are prepared to have any subsequent manipulation of the
1001 * tuplesort's state invalidate slot contents.
1002 */
1003bool
1005 TupleTableSlot *slot, Datum *abbrev)
1006{
1010
1012 stup.tuple = NULL;
1013
1014 MemoryContextSwitchTo(oldcontext);
1015
1016 if (stup.tuple)
1017 {
1018 /* Record abbreviated key for caller */
1019 if (base->sortKeys->abbrev_converter && abbrev)
1020 *abbrev = stup.datum1;
1021
1022 if (copy)
1023 stup.tuple = heap_copy_minimal_tuple((MinimalTuple) stup.tuple, 0);
1024
1026 return true;
1027 }
1028 else
1029 {
1030 ExecClearTuple(slot);
1031 return false;
1032 }
1033}
1034
1035/*
1036 * Fetch the next tuple in either forward or back direction.
1037 * Returns NULL if no more tuples. Returned tuple belongs to tuplesort memory
1038 * context, and must not be freed by caller. Caller may not rely on tuple
1039 * remaining valid after any further manipulation of tuplesort.
1040 */
1043{
1047
1049 stup.tuple = NULL;
1050
1051 MemoryContextSwitchTo(oldcontext);
1052
1053 return stup.tuple;
1054}
1055
1056/*
1057 * Fetch the next index tuple in either forward or back direction.
1058 * Returns NULL if no more tuples. Returned tuple belongs to tuplesort memory
1059 * context, and must not be freed by caller. Caller may not rely on tuple
1060 * remaining valid after any further manipulation of tuplesort.
1061 */
1064{
1068
1070 stup.tuple = NULL;
1071
1072 MemoryContextSwitchTo(oldcontext);
1073
1074 return (IndexTuple) stup.tuple;
1075}
1076
1077/*
1078 * Fetch the next BRIN tuple in either forward or back direction.
1079 * Returns NULL if no more tuples. Returned tuple belongs to tuplesort memory
1080 * context, and must not be freed by caller. Caller may not rely on tuple
1081 * remaining valid after any further manipulation of tuplesort.
1082 */
1083BrinTuple *
1085{
1090
1092 stup.tuple = NULL;
1093
1094 MemoryContextSwitchTo(oldcontext);
1095
1096 if (!stup.tuple)
1097 return NULL;
1098
1099 btup = (BrinSortTuple *) stup.tuple;
1100
1101 *len = btup->tuplen;
1102
1103 return &btup->tuple;
1104}
1105
1106GinTuple *
1108{
1112 GinTuple *tup;
1113
1115 stup.tuple = NULL;
1116
1117 MemoryContextSwitchTo(oldcontext);
1118
1119 if (!stup.tuple)
1120 return NULL;
1121
1122 tup = (GinTuple *) stup.tuple;
1123
1124 *len = tup->tuplen;
1125
1126 return tup;
1127}
1128
1129/*
1130 * Fetch the next Datum in either forward or back direction.
1131 * Returns false if no more datums.
1132 *
1133 * If the Datum is pass-by-ref type, the returned value is freshly palloc'd
1134 * in caller's context, and is now owned by the caller (this differs from
1135 * similar routines for other types of tuplesorts).
1136 *
1137 * Caller may optionally be passed back abbreviated value (on true return
1138 * value) when abbreviation was used, which can be used to cheaply avoid
1139 * equality checks that might otherwise be required. Caller can safely make a
1140 * determination of "non-equal tuple" based on simple binary inequality. A
1141 * NULL value will have a zeroed abbreviated value representation, which caller
1142 * may rely on in abbreviated inequality check.
1143 *
1144 * For byref Datums, if copy is true, *val is set to a copy of the Datum
1145 * copied into the caller's memory context, so that it will stay valid
1146 * regardless of future manipulations of the tuplesort's state (up to and
1147 * including deleting the tuplesort). If copy is false, *val will just be
1148 * set to a pointer to the Datum held within the tuplesort, which is more
1149 * efficient, but only safe for callers that are prepared to have any
1150 * subsequent manipulation of the tuplesort's state invalidate slot contents.
1151 * For byval Datums, the value of the 'copy' parameter has no effect.
1152 */
1153bool
1155 Datum *val, bool *isNull, Datum *abbrev)
1156{
1161
1163 {
1164 MemoryContextSwitchTo(oldcontext);
1165 return false;
1166 }
1167
1168 /* Ensure we copy into caller's memory context */
1169 MemoryContextSwitchTo(oldcontext);
1170
1171 /* Record abbreviated key for caller */
1172 if (base->sortKeys->abbrev_converter && abbrev)
1173 *abbrev = stup.datum1;
1174
1175 if (stup.isnull1 || !base->tuples)
1176 {
1177 *val = stup.datum1;
1178 *isNull = stup.isnull1;
1179 }
1180 else
1181 {
1182 /* use stup.tuple because stup.datum1 may be an abbreviation */
1183 if (copy)
1184 *val = datumCopy(PointerGetDatum(stup.tuple), false,
1185 arg->datumTypeLen);
1186 else
1187 *val = PointerGetDatum(stup.tuple);
1188 *isNull = false;
1189 }
1190
1191 return true;
1192}
1193
1194
1195/*
1196 * Routines specialized for HeapTuple (actually MinimalTuple) case
1197 */
1198
1199static void
1201{
1202 int i;
1204
1205 for (i = 0; i < count; i++)
1206 {
1207 HeapTupleData htup;
1208
1209 htup.t_len = ((MinimalTuple) stups[i].tuple)->t_len +
1211 htup.t_data = (HeapTupleHeader) ((char *) stups[i].tuple -
1213 stups[i].datum1 = heap_getattr(&htup,
1214 base->sortKeys[0].ssup_attno,
1215 (TupleDesc) base->arg,
1216 &stups[i].isnull1);
1217 }
1218}
1219
1220static int
1222{
1225 int32 compare;
1226
1227
1228 /* Compare the leading sort key */
1229 compare = ApplySortComparator(a->datum1, a->isnull1,
1230 b->datum1, b->isnull1,
1231 sortKey);
1232 if (compare != 0)
1233 return compare;
1234
1235 /* Compare additional sort keys */
1237}
1238
1239static int
1241{
1246 TupleDesc tupDesc;
1247 int nkey;
1248 int32 compare;
1249 AttrNumber attno;
1250 Datum datum1,
1251 datum2;
1252 bool isnull1,
1253 isnull2;
1254
1255 ltup.t_len = ((MinimalTuple) a->tuple)->t_len + MINIMAL_TUPLE_OFFSET;
1256 ltup.t_data = (HeapTupleHeader) ((char *) a->tuple - MINIMAL_TUPLE_OFFSET);
1257 rtup.t_len = ((MinimalTuple) b->tuple)->t_len + MINIMAL_TUPLE_OFFSET;
1258 rtup.t_data = (HeapTupleHeader) ((char *) b->tuple - MINIMAL_TUPLE_OFFSET);
1259 tupDesc = (TupleDesc) base->arg;
1260
1261 if (sortKey->abbrev_converter)
1262 {
1263 attno = sortKey->ssup_attno;
1264
1265 datum1 = heap_getattr(&ltup, attno, tupDesc, &isnull1);
1266 datum2 = heap_getattr(&rtup, attno, tupDesc, &isnull2);
1267
1268 compare = ApplySortAbbrevFullComparator(datum1, isnull1,
1269 datum2, isnull2,
1270 sortKey);
1271 if (compare != 0)
1272 return compare;
1273 }
1274
1275 sortKey++;
1276 for (nkey = 1; nkey < base->nKeys; nkey++, sortKey++)
1277 {
1278 attno = sortKey->ssup_attno;
1279
1280 datum1 = heap_getattr(&ltup, attno, tupDesc, &isnull1);
1281 datum2 = heap_getattr(&rtup, attno, tupDesc, &isnull2);
1282
1283 compare = ApplySortComparator(datum1, isnull1,
1284 datum2, isnull2,
1285 sortKey);
1286 if (compare != 0)
1287 return compare;
1288 }
1289
1290 return 0;
1291}
1292
1293static void
1295{
1297 MinimalTuple tuple = (MinimalTuple) stup->tuple;
1298
1299 /* the part of the MinimalTuple we'll write: */
1300 char *tupbody = (char *) tuple + MINIMAL_TUPLE_DATA_OFFSET;
1301 unsigned int tupbodylen = tuple->t_len - MINIMAL_TUPLE_DATA_OFFSET;
1302
1303 /* total on-disk footprint: */
1304 unsigned int tuplen = tupbodylen + sizeof(int);
1305
1306 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1308 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1309 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1310}
1311
1312static void
1314 LogicalTape *tape, unsigned int len)
1315{
1316 unsigned int tupbodylen = len - sizeof(int);
1317 unsigned int tuplen = tupbodylen + MINIMAL_TUPLE_DATA_OFFSET;
1319 char *tupbody = (char *) tuple + MINIMAL_TUPLE_DATA_OFFSET;
1321 HeapTupleData htup;
1322
1323 /* read in the tuple proper */
1324 tuple->t_len = tuplen;
1326 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1327 LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
1328 stup->tuple = tuple;
1329 /* set up first-column key value */
1330 htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
1331 htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
1332 stup->datum1 = heap_getattr(&htup,
1333 base->sortKeys[0].ssup_attno,
1334 (TupleDesc) base->arg,
1335 &stup->isnull1);
1336}
1337
1338/*
1339 * Routines specialized for the CLUSTER case (HeapTuple data, with
1340 * comparisons per a btree index definition)
1341 */
1342
1343static void
1345{
1346 int i;
1349
1350 for (i = 0; i < count; i++)
1351 {
1352 HeapTuple tup;
1353
1354 tup = (HeapTuple) stups[i].tuple;
1355 stups[i].datum1 = heap_getattr(tup,
1356 arg->indexInfo->ii_IndexAttrNumbers[0],
1357 arg->tupDesc,
1358 &stups[i].isnull1);
1359 }
1360}
1361
1362static int
1365{
1368 int32 compare;
1369
1370 /* Compare the leading sort key, if it's simple */
1371 if (base->haveDatum1)
1372 {
1373 compare = ApplySortComparator(a->datum1, a->isnull1,
1374 b->datum1, b->isnull1,
1375 sortKey);
1376 if (compare != 0)
1377 return compare;
1378 }
1379
1381}
1382
1383static int
1386{
1392 TupleDesc tupDesc;
1393 int nkey;
1394 int32 compare = 0;
1395 Datum datum1,
1396 datum2;
1397 bool isnull1,
1398 isnull2;
1399
1400 ltup = (HeapTuple) a->tuple;
1401 rtup = (HeapTuple) b->tuple;
1402 tupDesc = arg->tupDesc;
1403
1404 /* Compare the leading sort key, if it's simple */
1405 if (base->haveDatum1)
1406 {
1407 if (sortKey->abbrev_converter)
1408 {
1409 AttrNumber leading = arg->indexInfo->ii_IndexAttrNumbers[0];
1410
1411 datum1 = heap_getattr(ltup, leading, tupDesc, &isnull1);
1412 datum2 = heap_getattr(rtup, leading, tupDesc, &isnull2);
1413
1414 compare = ApplySortAbbrevFullComparator(datum1, isnull1,
1415 datum2, isnull2,
1416 sortKey);
1417 }
1418 if (compare != 0 || base->nKeys == 1)
1419 return compare;
1420 /* Compare additional columns the hard way */
1421 sortKey++;
1422 nkey = 1;
1423 }
1424 else
1425 {
1426 /* Must compare all keys the hard way */
1427 nkey = 0;
1428 }
1429
1430 if (arg->indexInfo->ii_Expressions == NULL)
1431 {
1432 /* If not expression index, just compare the proper heap attrs */
1433
1434 for (; nkey < base->nKeys; nkey++, sortKey++)
1435 {
1436 AttrNumber attno = arg->indexInfo->ii_IndexAttrNumbers[nkey];
1437
1438 datum1 = heap_getattr(ltup, attno, tupDesc, &isnull1);
1439 datum2 = heap_getattr(rtup, attno, tupDesc, &isnull2);
1440
1441 compare = ApplySortComparator(datum1, isnull1,
1442 datum2, isnull2,
1443 sortKey);
1444 if (compare != 0)
1445 return compare;
1446 }
1447 }
1448 else
1449 {
1450 /*
1451 * In the expression index case, compute the whole index tuple and
1452 * then compare values. It would perhaps be faster to compute only as
1453 * many columns as we need to compare, but that would require
1454 * duplicating all the logic in FormIndexDatum.
1455 */
1460 TupleTableSlot *ecxt_scantuple;
1461
1462 /* Reset context each time to prevent memory leakage */
1464
1465 ecxt_scantuple = GetPerTupleExprContext(arg->estate)->ecxt_scantuple;
1466
1467 ExecStoreHeapTuple(ltup, ecxt_scantuple, false);
1468 FormIndexDatum(arg->indexInfo, ecxt_scantuple, arg->estate,
1470
1471 ExecStoreHeapTuple(rtup, ecxt_scantuple, false);
1472 FormIndexDatum(arg->indexInfo, ecxt_scantuple, arg->estate,
1474
1475 for (; nkey < base->nKeys; nkey++, sortKey++)
1476 {
1481 sortKey);
1482 if (compare != 0)
1483 return compare;
1484 }
1485 }
1486
1487 return 0;
1488}
1489
1490static void
1492{
1494 HeapTuple tuple = (HeapTuple) stup->tuple;
1495 unsigned int tuplen = tuple->t_len + sizeof(ItemPointerData) + sizeof(int);
1496
1497 /* We need to store t_self, but not other fields of HeapTupleData */
1498 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1499 LogicalTapeWrite(tape, &tuple->t_self, sizeof(ItemPointerData));
1500 LogicalTapeWrite(tape, tuple->t_data, tuple->t_len);
1501 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1502 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1503}
1504
1505static void
1507 LogicalTape *tape, unsigned int tuplen)
1508{
1511 unsigned int t_len = tuplen - sizeof(ItemPointerData) - sizeof(int);
1513 t_len + HEAPTUPLESIZE);
1514
1515 /* Reconstruct the HeapTupleData header */
1516 tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
1517 tuple->t_len = t_len;
1519 /* We don't currently bother to reconstruct t_tableOid */
1520 tuple->t_tableOid = InvalidOid;
1521 /* Read in the tuple body */
1522 LogicalTapeReadExact(tape, tuple->t_data, tuple->t_len);
1523 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1524 LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
1525 stup->tuple = tuple;
1526 /* set up first-column key value, if it's a simple column */
1527 if (base->haveDatum1)
1528 stup->datum1 = heap_getattr(tuple,
1529 arg->indexInfo->ii_IndexAttrNumbers[0],
1530 arg->tupDesc,
1531 &stup->isnull1);
1532}
1533
1534static void
1536{
1539
1540 /* Free any execution state created for CLUSTER case */
1541 if (arg->estate != NULL)
1542 {
1543 ExprContext *econtext = GetPerTupleExprContext(arg->estate);
1544
1546 FreeExecutorState(arg->estate);
1547 }
1548}
1549
1550/*
1551 * Routines specialized for IndexTuple case
1552 *
1553 * The btree and hash cases require separate comparison functions, but the
1554 * IndexTuple representation is the same so the copy/write/read support
1555 * functions can be shared.
1556 */
1557
1558static void
1560{
1563 int i;
1564
1565 for (i = 0; i < count; i++)
1566 {
1567 IndexTuple tuple;
1568
1569 tuple = stups[i].tuple;
1570 stups[i].datum1 = index_getattr(tuple,
1571 1,
1572 RelationGetDescr(arg->indexRel),
1573 &stups[i].isnull1);
1574 }
1575}
1576
1577static int
1580{
1581 /*
1582 * This is similar to comparetup_heap(), but expects index tuples. There
1583 * is also special handling for enforcing uniqueness, and special
1584 * treatment for equal keys at the end.
1585 */
1588 int32 compare;
1589
1590 /* Compare the leading sort key */
1591 compare = ApplySortComparator(a->datum1, a->isnull1,
1592 b->datum1, b->isnull1,
1593 sortKey);
1594 if (compare != 0)
1595 return compare;
1596
1597 /* Compare additional sort keys */
1599}
1600
1601static int
1604{
1610 int keysz;
1612 bool equal_hasnull = false;
1613 int nkey;
1614 int32 compare;
1615 Datum datum1,
1616 datum2;
1617 bool isnull1,
1618 isnull2;
1619
1620 tuple1 = (IndexTuple) a->tuple;
1621 tuple2 = (IndexTuple) b->tuple;
1622 keysz = base->nKeys;
1623 tupDes = RelationGetDescr(arg->index.indexRel);
1624
1625 if (sortKey->abbrev_converter)
1626 {
1627 datum1 = index_getattr(tuple1, 1, tupDes, &isnull1);
1629
1630 compare = ApplySortAbbrevFullComparator(datum1, isnull1,
1631 datum2, isnull2,
1632 sortKey);
1633 if (compare != 0)
1634 return compare;
1635 }
1636
1637 /* they are equal, so we only need to examine one null flag */
1638 if (a->isnull1)
1639 equal_hasnull = true;
1640
1641 sortKey++;
1642 for (nkey = 2; nkey <= keysz; nkey++, sortKey++)
1643 {
1644 datum1 = index_getattr(tuple1, nkey, tupDes, &isnull1);
1646
1647 compare = ApplySortComparator(datum1, isnull1,
1648 datum2, isnull2,
1649 sortKey);
1650 if (compare != 0)
1651 return compare; /* done when we find unequal attributes */
1652
1653 /* they are equal, so we only need to examine one null flag */
1654 if (isnull1)
1655 equal_hasnull = true;
1656 }
1657
1658 /*
1659 * If btree has asked us to enforce uniqueness, complain if two equal
1660 * tuples are detected (unless there was at least one NULL field and NULLS
1661 * NOT DISTINCT was not set).
1662 *
1663 * It is sufficient to make the test here, because if two tuples are equal
1664 * they *must* get compared at some stage of the sort --- otherwise the
1665 * sort algorithm wouldn't have checked whether one must appear before the
1666 * other.
1667 */
1668 if (arg->enforceUnique && !(!arg->uniqueNullsNotDistinct && equal_hasnull))
1669 {
1671 bool isnull[INDEX_MAX_KEYS];
1672 char *key_desc;
1673
1674 /*
1675 * Some rather brain-dead implementations of qsort (such as the one in
1676 * QNX 4) will sometimes call the comparison routine to compare a
1677 * value to itself, but we always use our own implementation, which
1678 * does not.
1679 */
1680 Assert(tuple1 != tuple2);
1681
1683
1684 key_desc = BuildIndexValueDescription(arg->index.indexRel, values, isnull);
1685
1686 ereport(ERROR,
1688 errmsg("could not create unique index \"%s\"",
1689 RelationGetRelationName(arg->index.indexRel)),
1690 key_desc ? errdetail("Key %s is duplicated.", key_desc) :
1691 errdetail("Duplicate keys exist."),
1692 errtableconstraint(arg->index.heapRel,
1693 RelationGetRelationName(arg->index.indexRel))));
1694 }
1695
1696 /*
1697 * If key values are equal, we sort on ItemPointer. This is required for
1698 * btree indexes, since heap TID is treated as an implicit last key
1699 * attribute in order to ensure that all keys in the index are physically
1700 * unique.
1701 */
1702 {
1705
1706 if (blk1 != blk2)
1707 return (blk1 < blk2) ? -1 : 1;
1708 }
1709 {
1712
1713 if (pos1 != pos2)
1714 return (pos1 < pos2) ? -1 : 1;
1715 }
1716
1717 /* ItemPointer values should never be equal */
1718 Assert(false);
1719
1720 return 0;
1721}
1722
1723static int
1726{
1729 uint32 hash1;
1730 uint32 hash2;
1735
1736 /*
1737 * Fetch hash keys and mask off bits we don't want to sort by, so that the
1738 * initial sort is just on the bucket number. We know that the first
1739 * column of the index tuple is the hash key.
1740 */
1741 Assert(!a->isnull1);
1743 arg->max_buckets, arg->high_mask,
1744 arg->low_mask);
1745 Assert(!b->isnull1);
1747 arg->max_buckets, arg->high_mask,
1748 arg->low_mask);
1749 if (bucket1 > bucket2)
1750 return 1;
1751 else if (bucket1 < bucket2)
1752 return -1;
1753
1754 /*
1755 * If bucket values are equal, sort by hash values. This allows us to
1756 * insert directly onto bucket/overflow pages, where the index tuples are
1757 * stored in hash order to allow fast binary search within each page.
1758 */
1759 hash1 = DatumGetUInt32(a->datum1);
1760 hash2 = DatumGetUInt32(b->datum1);
1761 if (hash1 > hash2)
1762 return 1;
1763 else if (hash1 < hash2)
1764 return -1;
1765
1766 /*
1767 * If hash values are equal, we sort on ItemPointer. This does not affect
1768 * validity of the finished index, but it may be useful to have index
1769 * scans in physical order.
1770 */
1771 tuple1 = (IndexTuple) a->tuple;
1772 tuple2 = (IndexTuple) b->tuple;
1773
1774 {
1777
1778 if (blk1 != blk2)
1779 return (blk1 < blk2) ? -1 : 1;
1780 }
1781 {
1784
1785 if (pos1 != pos2)
1786 return (pos1 < pos2) ? -1 : 1;
1787 }
1788
1789 /* ItemPointer values should never be equal */
1790 Assert(false);
1791
1792 return 0;
1793}
1794
1795/*
1796 * Sorting for hash indexes only uses one sort key, so this shouldn't ever be
1797 * called. It's only here for consistency.
1798 */
1799static int
1802{
1803 Assert(false);
1804
1805 return 0;
1806}
1807
1808static void
1810{
1812 IndexTuple tuple = (IndexTuple) stup->tuple;
1813 unsigned int tuplen;
1814
1815 tuplen = IndexTupleSize(tuple) + sizeof(tuplen);
1816 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1817 LogicalTapeWrite(tape, tuple, IndexTupleSize(tuple));
1818 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1819 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1820}
1821
1822static void
1824 LogicalTape *tape, unsigned int len)
1825{
1828 unsigned int tuplen = len - sizeof(unsigned int);
1830
1831 LogicalTapeReadExact(tape, tuple, tuplen);
1832 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1833 LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
1834 stup->tuple = tuple;
1835 /* set up first-column key value */
1836 stup->datum1 = index_getattr(tuple,
1837 1,
1838 RelationGetDescr(arg->indexRel),
1839 &stup->isnull1);
1840}
1841
1842/*
1843 * Routines specialized for BrinTuple case
1844 */
1845
1846static void
1848{
1849 int i;
1850
1851 for (i = 0; i < count; i++)
1852 {
1853 BrinSortTuple *tuple;
1854
1855 tuple = stups[i].tuple;
1856 stups[i].datum1 = UInt32GetDatum(tuple->tuple.bt_blkno);
1857 }
1858}
1859
1860static int
1863{
1864 Assert(TuplesortstateGetPublic(state)->haveDatum1);
1865
1866 if (DatumGetUInt32(a->datum1) > DatumGetUInt32(b->datum1))
1867 return 1;
1868
1869 if (DatumGetUInt32(a->datum1) < DatumGetUInt32(b->datum1))
1870 return -1;
1871
1872 /* silence compilers */
1873 return 0;
1874}
1875
1876static void
1878{
1880 BrinSortTuple *tuple = (BrinSortTuple *) stup->tuple;
1881 unsigned int tuplen = tuple->tuplen;
1882
1883 tuplen = tuplen + sizeof(tuplen);
1884 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1885 LogicalTapeWrite(tape, &tuple->tuple, tuple->tuplen);
1886 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1887 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1888}
1889
1890static void
1892 LogicalTape *tape, unsigned int len)
1893{
1894 BrinSortTuple *tuple;
1896 unsigned int tuplen = len - sizeof(unsigned int);
1897
1898 /*
1899 * Allocate space for the BRIN sort tuple, which is BrinTuple with an
1900 * extra length field.
1901 */
1903 BRINSORTTUPLE_SIZE(tuplen));
1904
1905 tuple->tuplen = tuplen;
1906
1907 LogicalTapeReadExact(tape, &tuple->tuple, tuplen);
1908 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1909 LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
1910 stup->tuple = tuple;
1911
1912 /* set up first-column key value, which is block number */
1913 stup->datum1 = UInt32GetDatum(tuple->tuple.bt_blkno);
1914}
1915
1916/*
1917 * Routines specialized for GIN case
1918 */
1919
1920static void
1922{
1923 Assert(false);
1924 elog(ERROR, "removeabbrev_index_gin not implemented");
1925}
1926
1927static int
1930{
1932
1933 Assert(!TuplesortstateGetPublic(state)->haveDatum1);
1934
1935 return _gin_compare_tuples((GinTuple *) a->tuple,
1936 (GinTuple *) b->tuple,
1937 base->sortKeys);
1938}
1939
1940static void
1942{
1944 GinTuple *tuple = (GinTuple *) stup->tuple;
1945 unsigned int tuplen = tuple->tuplen;
1946
1947 tuplen = tuplen + sizeof(tuplen);
1948 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1949 LogicalTapeWrite(tape, tuple, tuple->tuplen);
1950 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1951 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1952}
1953
1954static void
1956 LogicalTape *tape, unsigned int len)
1957{
1958 GinTuple *tuple;
1960 unsigned int tuplen = len - sizeof(unsigned int);
1961
1962 /*
1963 * Allocate space for the GIN sort tuple, which already has the proper
1964 * length included in the header.
1965 */
1966 tuple = (GinTuple *) tuplesort_readtup_alloc(state, tuplen);
1967
1968 tuple->tuplen = tuplen;
1969
1970 LogicalTapeReadExact(tape, tuple, tuplen);
1971 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1972 LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
1973 stup->tuple = tuple;
1974
1975 /* no abbreviations (FIXME maybe use attrnum for this?) */
1976 stup->datum1 = (Datum) 0;
1977}
1978
1979/*
1980 * Routines specialized for DatumTuple case
1981 */
1982
1983static void
1985{
1986 int i;
1987
1988 for (i = 0; i < count; i++)
1989 stups[i].datum1 = PointerGetDatum(stups[i].tuple);
1990}
1991
1992static int
1994{
1996 int compare;
1997
1998 compare = ApplySortComparator(a->datum1, a->isnull1,
1999 b->datum1, b->isnull1,
2000 base->sortKeys);
2001 if (compare != 0)
2002 return compare;
2003
2005}
2006
2007static int
2009{
2011 int32 compare = 0;
2012
2013 /* if we have abbreviations, then "tuple" has the original value */
2014 if (base->sortKeys->abbrev_converter)
2016 PointerGetDatum(b->tuple), b->isnull1,
2017 base->sortKeys);
2018
2019 return compare;
2020}
2021
2022static void
2024{
2027 void *waddr;
2028 unsigned int tuplen;
2029 unsigned int writtenlen;
2030
2031 if (stup->isnull1)
2032 {
2033 waddr = NULL;
2034 tuplen = 0;
2035 }
2036 else if (!base->tuples)
2037 {
2038 waddr = &stup->datum1;
2039 tuplen = sizeof(Datum);
2040 }
2041 else
2042 {
2043 waddr = stup->tuple;
2044 tuplen = datumGetSize(PointerGetDatum(stup->tuple), false, arg->datumTypeLen);
2045 Assert(tuplen != 0);
2046 }
2047
2048 writtenlen = tuplen + sizeof(unsigned int);
2049
2051 LogicalTapeWrite(tape, waddr, tuplen);
2052 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
2054}
2055
2056static void
2058 LogicalTape *tape, unsigned int len)
2059{
2061 unsigned int tuplen = len - sizeof(unsigned int);
2062
2063 if (tuplen == 0)
2064 {
2065 /* it's NULL */
2066 stup->datum1 = (Datum) 0;
2067 stup->isnull1 = true;
2068 stup->tuple = NULL;
2069 }
2070 else if (!base->tuples)
2071 {
2072 Assert(tuplen == sizeof(Datum));
2073 LogicalTapeReadExact(tape, &stup->datum1, tuplen);
2074 stup->isnull1 = false;
2075 stup->tuple = NULL;
2076 }
2077 else
2078 {
2079 void *raddr = tuplesort_readtup_alloc(state, tuplen);
2080
2081 LogicalTapeReadExact(tape, raddr, tuplen);
2082 stup->datum1 = PointerGetDatum(raddr);
2083 stup->isnull1 = false;
2084 stup->tuple = raddr;
2085 }
2086
2087 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
2088 LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
2089}
int16 AttrNumber
Definition attnum.h:21
uint32 BlockNumber
Definition block.h:31
static Datum values[MAXATTR]
Definition bootstrap.c:155
#define MAXALIGN(LEN)
Definition c.h:826
#define Assert(condition)
Definition c.h:873
int16_t int16
Definition c.h:541
int32_t int32
Definition c.h:542
uint32_t uint32
Definition c.h:546
#define OidIsValid(objectId)
Definition c.h:788
size_t Size
Definition c.h:619
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition datum.c:132
Size datumGetSize(Datum value, bool typByVal, int typLen)
Definition datum.c:65
int errdetail(const char *fmt,...)
Definition elog.c:1216
int errcode(int sqlerrcode)
Definition elog.c:863
int errmsg(const char *fmt,...)
Definition elog.c:1080
#define LOG
Definition elog.h:31
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
TupleTableSlot * ExecStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot, bool shouldFree)
const TupleTableSlotOps TTSOpsHeapTuple
Definition execTuples.c:85
TupleTableSlot * ExecStoreHeapTuple(HeapTuple tuple, TupleTableSlot *slot, bool shouldFree)
void FreeExecutorState(EState *estate)
Definition execUtils.c:192
EState * CreateExecutorState(void)
Definition execUtils.c:88
#define ResetPerTupleExprContext(estate)
Definition executor.h:665
#define GetPerTupleExprContext(estate)
Definition executor.h:656
#define palloc_object(type)
Definition fe_memutils.h:74
#define palloc0_object(type)
Definition fe_memutils.h:75
char * format_type_be(Oid type_oid)
char * BuildIndexValueDescription(Relation indexRelation, const Datum *values, const bool *isnull)
Definition genam.c:178
static int compare(const void *arg1, const void *arg2)
Definition geqo_pool.c:144
#define GIN_COMPARE_PROC
Definition gin.h:24
int _gin_compare_tuples(GinTuple *a, GinTuple *b, SortSupport ssup)
Definition gininsert.c:2434
uint32 Bucket
Definition hash.h:35
Bucket _hash_hashkey2bucket(uint32 hashkey, uint32 maxbucket, uint32 highmask, uint32 lowmask)
Definition hashutil.c:125
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:778
MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup, Size extra)
Definition heaptuple.c:1541
#define HEAPTUPLESIZE
Definition htup.h:73
HeapTupleData * HeapTuple
Definition htup.h:71
MinimalTupleData * MinimalTuple
Definition htup.h:27
HeapTupleHeaderData * HeapTupleHeader
Definition htup.h:23
#define MINIMAL_TUPLE_OFFSET
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
#define MINIMAL_TUPLE_DATA_OFFSET
IndexInfo * BuildIndexInfo(Relation index)
Definition index.c:2426
void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition index.c:2728
RegProcedure index_getprocid(Relation irel, AttrNumber attnum, uint16 procnum)
Definition indexam.c:883
void index_deform_tuple(IndexTuple tup, TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition indextuple.c:456
IndexTuple index_form_tuple_context(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull, MemoryContext context)
Definition indextuple.c:65
long val
Definition informix.c:689
int b
Definition isn.c:74
int a
Definition isn.c:73
int i
Definition isn.c:77
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition itemptr.h:124
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition itemptr.h:103
IndexTupleData * IndexTuple
Definition itup.h:53
static Datum index_getattr(IndexTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition itup.h:131
static Size IndexTupleSize(const IndexTupleData *itup)
Definition itup.h:71
#define INDEX_SIZE_MASK
Definition itup.h:65
void LogicalTapeWrite(LogicalTape *lt, const void *ptr, size_t size)
Definition logtape.c:761
void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval)
Definition lsyscache.c:2401
void pfree(void *pointer)
Definition mcxt.c:1616
Size GetMemoryChunkSpace(void *pointer)
Definition mcxt.c:770
void * palloc0(Size size)
Definition mcxt.c:1417
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define SK_BT_NULLS_FIRST
Definition nbtree.h:1117
#define SK_BT_DESC
Definition nbtree.h:1116
BTScanInsert _bt_mkscankey(Relation rel, IndexTuple itup)
Definition nbtutils.c:59
uint16 OffsetNumber
Definition off.h:24
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
FormData_pg_attribute * Form_pg_attribute
void * arg
#define INDEX_MAX_KEYS
const void size_t len
static uint32 DatumGetUInt32(Datum X)
Definition postgres.h:232
static Datum PointerGetDatum(const void *X)
Definition postgres.h:352
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:342
static Datum UInt32GetDatum(uint32 X)
Definition postgres.h:242
#define InvalidOid
unsigned int Oid
static int fb(int x)
#define RelationGetDescr(relation)
Definition rel.h:540
#define RelationGetNumberOfAttributes(relation)
Definition rel.h:520
#define RelationGetRelationName(relation)
Definition rel.h:548
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition rel.h:533
int errtableconstraint(Relation rel, const char *conname)
Definition relcache.c:6098
void PrepareSortSupportFromGistIndexRel(Relation indexRel, SortSupport ssup)
void PrepareSortSupportComparisonShim(Oid cmpFunc, SortSupport ssup)
Definition sortsupport.c:68
void PrepareSortSupportFromIndexRel(Relation indexRel, bool reverse, SortSupport ssup)
void PrepareSortSupportFromOrderingOp(Oid orderingOp, SortSupport ssup)
static int ApplySortAbbrevFullComparator(Datum datum1, bool isNull1, Datum datum2, bool isNull2, SortSupport ssup)
struct SortSupportData * SortSupport
Definition sortsupport.h:58
static int ApplySortComparator(Datum datum1, bool isNull1, Datum datum2, bool isNull2, SortSupport ssup)
BlockNumber bt_blkno
Definition brin_tuple.h:66
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:275
Oid fn_oid
Definition fmgr.h:59
int tuplen
Definition gin_tuple.h:24
ItemPointerData t_self
Definition htup.h:65
uint32 t_len
Definition htup.h:64
HeapTupleHeader t_data
Definition htup.h:68
Oid t_tableOid
Definition htup.h:66
ItemPointerData t_tid
Definition itup.h:37
unsigned short t_info
Definition itup.h:49
Oid * rd_indcollation
Definition rel.h:217
Form_pg_class rd_rel
Definition rel.h:111
AttrNumber ssup_attno
Definition sortsupport.h:81
Datum(* abbrev_converter)(Datum original, SortSupport ssup)
MemoryContext ssup_cxt
Definition sortsupport.h:66
void * tuple
Definition tuplesort.h:116
Datum datum1
Definition tuplesort.h:117
TuplesortIndexArg index
SortSupport onlyKey
Definition tuplesort.h:212
MemoryContext maincontext
Definition tuplesort.h:185
void(* writetup)(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
Definition tuplesort.h:161
void(* removeabbrev)(Tuplesortstate *state, SortTuple *stups, int count)
Definition tuplesort.h:154
void(* freestate)(Tuplesortstate *state)
Definition tuplesort.h:179
MemoryContext tuplecontext
Definition tuplesort.h:188
MemoryContext sortcontext
Definition tuplesort.h:187
void(* readtup)(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len)
Definition tuplesort.h:170
SortTupleComparator comparetup
Definition tuplesort.h:141
SortSupport sortKeys
Definition tuplesort.h:202
SortTupleComparator comparetup_tiebreak
Definition tuplesort.h:148
FmgrInfo cmp_proc_finfo
Definition typcache.h:77
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:160
struct TupleDescData * TupleDesc
Definition tupdesc.h:145
Tuplesortstate * tuplesort_begin_common(int workMem, SortCoordinate coordinate, int sortopt)
Definition tuplesort.c:635
void tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple, bool useAbbrev, Size tuplen)
Definition tuplesort.c:1154
bool tuplesort_gettuple_common(Tuplesortstate *state, bool forward, SortTuple *stup)
Definition tuplesort.c:1455
bool trace_sort
Definition tuplesort.c:122
void * tuplesort_readtup_alloc(Tuplesortstate *state, Size tuplen)
Definition tuplesort.c:2866
#define TupleSortUseBumpTupleCxt(opt)
Definition tuplesort.h:82
#define PARALLEL_SORT(coordinate)
Definition tuplesort.h:222
#define TUPLESORT_RANDOMACCESS
Definition tuplesort.h:70
#define LogicalTapeReadExact(tape, ptr, len)
Definition tuplesort.h:229
#define TuplesortstateGetPublic(state)
Definition tuplesort.h:226
IndexTuple tuplesort_getindextuple(Tuplesortstate *state, bool forward)
static void writetup_index_brin(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
Tuplesortstate * tuplesort_begin_index_gin(Relation heapRel, Relation indexRel, int workMem, SortCoordinate coordinate, int sortopt)
HeapTuple tuplesort_getheaptuple(Tuplesortstate *state, bool forward)
static void removeabbrev_datum(Tuplesortstate *state, SortTuple *stups, int count)
static int comparetup_index_btree_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
void tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
static int comparetup_index_btree(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
GinTuple * tuplesort_getgintuple(Tuplesortstate *state, Size *len, bool forward)
static void readtup_index(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len)
static int comparetup_cluster_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
static void writetup_index_gin(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
void tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
static int comparetup_datum(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
Tuplesortstate * tuplesort_begin_index_brin(int workMem, SortCoordinate coordinate, int sortopt)
Tuplesortstate * tuplesort_begin_heap(TupleDesc tupDesc, int nkeys, AttrNumber *attNums, Oid *sortOperators, Oid *sortCollations, bool *nullsFirstFlags, int workMem, SortCoordinate coordinate, int sortopt)
Tuplesortstate * tuplesort_begin_cluster(TupleDesc tupDesc, Relation indexRel, int workMem, SortCoordinate coordinate, int sortopt)
BrinTuple * tuplesort_getbrintuple(Tuplesortstate *state, Size *len, bool forward)
static void removeabbrev_index(Tuplesortstate *state, SortTuple *stups, int count)
Tuplesortstate * tuplesort_begin_index_btree(Relation heapRel, Relation indexRel, bool enforceUnique, bool uniqueNullsNotDistinct, int workMem, SortCoordinate coordinate, int sortopt)
static int comparetup_datum_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
static void readtup_datum(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len)
#define INDEX_SORT
static void readtup_heap(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len)
static void readtup_cluster(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int tuplen)
Tuplesortstate * tuplesort_begin_index_gist(Relation heapRel, Relation indexRel, int workMem, SortCoordinate coordinate, int sortopt)
static void writetup_datum(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
void tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel, const ItemPointerData *self, const Datum *values, const bool *isnull)
#define CLUSTER_SORT
static int comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
bool tuplesort_gettupleslot(Tuplesortstate *state, bool forward, bool copy, TupleTableSlot *slot, Datum *abbrev)
static void removeabbrev_index_brin(Tuplesortstate *state, SortTuple *stups, int count)
#define BRINSORTTUPLE_SIZE(len)
#define DATUM_SORT
void tuplesort_putgintuple(Tuplesortstate *state, GinTuple *tuple, Size size)
static int comparetup_index_hash(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
static void readtup_index_brin(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len)
Tuplesortstate * tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, SortCoordinate coordinate, int sortopt)
void tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
static void writetup_heap(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
static void writetup_index(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
static void readtup_index_gin(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len)
static void removeabbrev_index_gin(Tuplesortstate *state, SortTuple *stups, int count)
Tuplesortstate * tuplesort_begin_index_hash(Relation heapRel, Relation indexRel, uint32 high_mask, uint32 low_mask, uint32 max_buckets, int workMem, SortCoordinate coordinate, int sortopt)
static int comparetup_heap_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
static void freestate_cluster(Tuplesortstate *state)
static void removeabbrev_heap(Tuplesortstate *state, SortTuple *stups, int count)
void tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
static int comparetup_cluster(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
static int comparetup_index_brin(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
static int comparetup_index_hash_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
bool tuplesort_getdatum(Tuplesortstate *state, bool forward, bool copy, Datum *val, bool *isNull, Datum *abbrev)
static void writetup_cluster(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
static void removeabbrev_cluster(Tuplesortstate *state, SortTuple *stups, int count)
#define HEAP_SORT
static int comparetup_index_gin(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
static MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Definition tuptable.h:495
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:457
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition typcache.c:386
#define TYPECACHE_CMP_PROC_FINFO
Definition typcache.h:144