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:188
#define MAXALIGN(LEN)
Definition c.h:898
#define Assert(condition)
Definition c.h:945
int16_t int16
Definition c.h:613
int32_t int32
Definition c.h:614
uint32_t uint32
Definition c.h:618
#define OidIsValid(objectId)
Definition c.h:860
size_t Size
Definition c.h:691
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition datum.c:132
Size datumGetSize(Datum value, bool typByVal, int typLen)
Definition datum.c:65
Datum arg
Definition elog.c:1322
int errcode(int sqlerrcode)
Definition elog.c:874
#define LOG
Definition elog.h:31
int errdetail(const char *fmt,...) pg_attribute_printf(1
#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:197
EState * CreateExecutorState(void)
Definition execUtils.c:90
struct SortSupportData * SortSupport
Definition execnodes.h:60
#define ResetPerTupleExprContext(estate)
Definition executor.h:669
#define GetPerTupleExprContext(estate)
Definition executor.h:660
#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:2443
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:698
MinimalTuple heap_copy_minimal_tuple(MinimalTuple mtup, Size extra)
Definition heaptuple.c:1490
#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:2429
void FormIndexDatum(IndexInfo *indexInfo, TupleTableSlot *slot, EState *estate, Datum *values, bool *isnull)
Definition index.c:2731
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:364
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:2471
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
static char * errmsg
uint16 OffsetNumber
Definition off.h:24
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
FormData_pg_attribute * Form_pg_attribute
#define INDEX_MAX_KEYS
const void size_t len
static uint32 DatumGetUInt32(Datum X)
Definition postgres.h:222
static Datum PointerGetDatum(const void *X)
Definition postgres.h:342
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
static Datum UInt32GetDatum(uint32 X)
Definition postgres.h:232
#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:6116
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)
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:284
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:213
MemoryContext maincontext
Definition tuplesort.h:186
void(* writetup)(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
Definition tuplesort.h:162
void(* removeabbrev)(Tuplesortstate *state, SortTuple *stups, int count)
Definition tuplesort.h:155
void(* freestate)(Tuplesortstate *state)
Definition tuplesort.h:180
MemoryContext tuplecontext
Definition tuplesort.h:189
MemoryContext sortcontext
Definition tuplesort.h:188
void(* readtup)(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len)
Definition tuplesort.h:171
SortTupleComparator comparetup
Definition tuplesort.h:142
SortSupport sortKeys
Definition tuplesort.h:203
SortTupleComparator comparetup_tiebreak
Definition tuplesort.h:149
FmgrInfo cmp_proc_finfo
Definition typcache.h:77
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
struct TupleDescData * TupleDesc
Definition tupdesc.h:163
Tuplesortstate * tuplesort_begin_common(int workMem, SortCoordinate coordinate, int sortopt)
Definition tuplesort.c:546
void tuplesort_puttuple_common(Tuplesortstate *state, SortTuple *tuple, bool useAbbrev, Size tuplen)
Definition tuplesort.c:1065
bool tuplesort_gettuple_common(Tuplesortstate *state, bool forward, SortTuple *stup)
Definition tuplesort.c:1366
bool trace_sort
Definition tuplesort.c:122
void * tuplesort_readtup_alloc(Tuplesortstate *state, Size tuplen)
Definition tuplesort.c:3155
#define TupleSortUseBumpTupleCxt(opt)
Definition tuplesort.h:82
#define PARALLEL_SORT(coordinate)
Definition tuplesort.h:223
#define TUPLESORT_RANDOMACCESS
Definition tuplesort.h:70
#define LogicalTapeReadExact(tape, ptr, len)
Definition tuplesort.h:230
#define TuplesortstateGetPublic(state)
Definition tuplesort.h:227
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:514
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition typcache.c:389
#define TYPECACHE_CMP_PROC_FINFO
Definition typcache.h:144