Age Owner Branch data TLA Line data Source code
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-2025, 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_tuple.h"
24 : : #include "access/hash.h"
25 : : #include "access/htup_details.h"
26 : : #include "access/nbtree.h"
27 : : #include "catalog/index.h"
28 : : #include "catalog/pg_collation.h"
29 : : #include "executor/executor.h"
30 : : #include "pg_trace.h"
31 : : #include "utils/datum.h"
32 : : #include "utils/guc.h"
33 : : #include "utils/lsyscache.h"
34 : : #include "utils/rel.h"
35 : : #include "utils/tuplesort.h"
36 : :
37 : :
38 : : /* sort-type codes for sort__start probes */
39 : : #define HEAP_SORT 0
40 : : #define INDEX_SORT 1
41 : : #define DATUM_SORT 2
42 : : #define CLUSTER_SORT 3
43 : :
44 : : static void removeabbrev_heap(Tuplesortstate *state, SortTuple *stups,
45 : : int count);
46 : : static void removeabbrev_cluster(Tuplesortstate *state, SortTuple *stups,
47 : : int count);
48 : : static void removeabbrev_index(Tuplesortstate *state, SortTuple *stups,
49 : : int count);
50 : : static void removeabbrev_index_brin(Tuplesortstate *state, SortTuple *stups,
51 : : int count);
52 : : static void removeabbrev_index_gin(Tuplesortstate *state, SortTuple *stups,
53 : : int count);
54 : : static void removeabbrev_datum(Tuplesortstate *state, SortTuple *stups,
55 : : int count);
56 : : static int comparetup_heap(const SortTuple *a, const SortTuple *b,
57 : : Tuplesortstate *state);
58 : : static int comparetup_heap_tiebreak(const SortTuple *a, const SortTuple *b,
59 : : Tuplesortstate *state);
60 : : static void writetup_heap(Tuplesortstate *state, LogicalTape *tape,
61 : : SortTuple *stup);
62 : : static void readtup_heap(Tuplesortstate *state, SortTuple *stup,
63 : : LogicalTape *tape, unsigned int len);
64 : : static int comparetup_cluster(const SortTuple *a, const SortTuple *b,
65 : : Tuplesortstate *state);
66 : : static int comparetup_cluster_tiebreak(const SortTuple *a, const SortTuple *b,
67 : : Tuplesortstate *state);
68 : : static void writetup_cluster(Tuplesortstate *state, LogicalTape *tape,
69 : : SortTuple *stup);
70 : : static void readtup_cluster(Tuplesortstate *state, SortTuple *stup,
71 : : LogicalTape *tape, unsigned int tuplen);
72 : : static int comparetup_index_btree(const SortTuple *a, const SortTuple *b,
73 : : Tuplesortstate *state);
74 : : static int comparetup_index_btree_tiebreak(const SortTuple *a, const SortTuple *b,
75 : : Tuplesortstate *state);
76 : : static int comparetup_index_hash(const SortTuple *a, const SortTuple *b,
77 : : Tuplesortstate *state);
78 : : static int comparetup_index_hash_tiebreak(const SortTuple *a, const SortTuple *b,
79 : : Tuplesortstate *state);
80 : : static int comparetup_index_brin(const SortTuple *a, const SortTuple *b,
81 : : Tuplesortstate *state);
82 : : static int comparetup_index_gin(const SortTuple *a, const SortTuple *b,
83 : : Tuplesortstate *state);
84 : : static void writetup_index(Tuplesortstate *state, LogicalTape *tape,
85 : : SortTuple *stup);
86 : : static void readtup_index(Tuplesortstate *state, SortTuple *stup,
87 : : LogicalTape *tape, unsigned int len);
88 : : static void writetup_index_brin(Tuplesortstate *state, LogicalTape *tape,
89 : : SortTuple *stup);
90 : : static void readtup_index_brin(Tuplesortstate *state, SortTuple *stup,
91 : : LogicalTape *tape, unsigned int len);
92 : : static void writetup_index_gin(Tuplesortstate *state, LogicalTape *tape,
93 : : SortTuple *stup);
94 : : static void readtup_index_gin(Tuplesortstate *state, SortTuple *stup,
95 : : LogicalTape *tape, unsigned int len);
96 : : static int comparetup_datum(const SortTuple *a, const SortTuple *b,
97 : : Tuplesortstate *state);
98 : : static int comparetup_datum_tiebreak(const SortTuple *a, const SortTuple *b,
99 : : Tuplesortstate *state);
100 : : static void writetup_datum(Tuplesortstate *state, LogicalTape *tape,
101 : : SortTuple *stup);
102 : : static void readtup_datum(Tuplesortstate *state, SortTuple *stup,
103 : : LogicalTape *tape, unsigned int len);
104 : : static void freestate_cluster(Tuplesortstate *state);
105 : :
106 : : /*
107 : : * Data structure pointed by "TuplesortPublic.arg" for the CLUSTER case. Set by
108 : : * the tuplesort_begin_cluster.
109 : : */
110 : : typedef struct
111 : : {
112 : : TupleDesc tupDesc;
113 : :
114 : : IndexInfo *indexInfo; /* info about index being used for reference */
115 : : EState *estate; /* for evaluating index expressions */
116 : : } TuplesortClusterArg;
117 : :
118 : : /*
119 : : * Data structure pointed by "TuplesortPublic.arg" for the IndexTuple case.
120 : : * Set by tuplesort_begin_index_xxx and used only by the IndexTuple routines.
121 : : */
122 : : typedef struct
123 : : {
124 : : Relation heapRel; /* table the index is being built on */
125 : : Relation indexRel; /* index being built */
126 : : } TuplesortIndexArg;
127 : :
128 : : /*
129 : : * Data structure pointed by "TuplesortPublic.arg" for the index_btree subcase.
130 : : */
131 : : typedef struct
132 : : {
133 : : TuplesortIndexArg index;
134 : :
135 : : bool enforceUnique; /* complain if we find duplicate tuples */
136 : : bool uniqueNullsNotDistinct; /* unique constraint null treatment */
137 : : } TuplesortIndexBTreeArg;
138 : :
139 : : /*
140 : : * Data structure pointed by "TuplesortPublic.arg" for the index_hash subcase.
141 : : */
142 : : typedef struct
143 : : {
144 : : TuplesortIndexArg index;
145 : :
146 : : uint32 high_mask; /* masks for sortable part of hash code */
147 : : uint32 low_mask;
148 : : uint32 max_buckets;
149 : : } TuplesortIndexHashArg;
150 : :
151 : : /*
152 : : * Data structure pointed by "TuplesortPublic.arg" for the Datum case.
153 : : * Set by tuplesort_begin_datum and used only by the DatumTuple routines.
154 : : */
155 : : typedef struct
156 : : {
157 : : /* the datatype oid of Datum's to be sorted */
158 : : Oid datumType;
159 : : /* we need typelen in order to know how to copy the Datums. */
160 : : int datumTypeLen;
161 : : } TuplesortDatumArg;
162 : :
163 : : /*
164 : : * Computing BrinTuple size with only the tuple is difficult, so we want to track
165 : : * the length referenced by the SortTuple. That's what BrinSortTuple is meant
166 : : * to do - it's essentially a BrinTuple prefixed by its length.
167 : : */
168 : : typedef struct BrinSortTuple
169 : : {
170 : : Size tuplen;
171 : : BrinTuple tuple;
172 : : } BrinSortTuple;
173 : :
174 : : /* Size of the BrinSortTuple, given length of the BrinTuple. */
175 : : #define BRINSORTTUPLE_SIZE(len) (offsetof(BrinSortTuple, tuple) + (len))
176 : :
177 : :
178 : : Tuplesortstate *
1239 akorotkov@postgresql 179 :CBC 59356 : tuplesort_begin_heap(TupleDesc tupDesc,
180 : : int nkeys, AttrNumber *attNums,
181 : : Oid *sortOperators, Oid *sortCollations,
182 : : bool *nullsFirstFlags,
183 : : int workMem, SortCoordinate coordinate, int sortopt)
184 : : {
185 : 59356 : Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
186 : : sortopt);
187 : 59356 : TuplesortPublic *base = TuplesortstateGetPublic(state);
188 : : MemoryContext oldcontext;
189 : : int i;
190 : :
191 : 59356 : oldcontext = MemoryContextSwitchTo(base->maincontext);
192 : :
1146 peter@eisentraut.org 193 [ - + ]: 59356 : Assert(nkeys > 0);
194 : :
1239 akorotkov@postgresql 195 [ - + ]: 59356 : if (trace_sort)
1239 akorotkov@postgresql 196 [ # # # # ]:UBC 0 : elog(LOG,
197 : : "begin tuple sort: nkeys = %d, workMem = %d, randomAccess = %c",
198 : : nkeys, workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
199 : :
1239 akorotkov@postgresql 200 :CBC 59356 : base->nKeys = nkeys;
201 : :
202 : : TRACE_POSTGRESQL_SORT_START(HEAP_SORT,
203 : : false, /* no unique check */
204 : : nkeys,
205 : : workMem,
206 : : sortopt & TUPLESORT_RANDOMACCESS,
207 : : PARALLEL_SORT(coordinate));
208 : :
209 : 59356 : base->removeabbrev = removeabbrev_heap;
210 : 59356 : base->comparetup = comparetup_heap;
854 john.naylor@postgres 211 : 59356 : base->comparetup_tiebreak = comparetup_heap_tiebreak;
1239 akorotkov@postgresql 212 : 59356 : base->writetup = writetup_heap;
213 : 59356 : base->readtup = readtup_heap;
214 : 59356 : base->haveDatum1 = true;
215 : 59356 : base->arg = tupDesc; /* assume we need not copy tupDesc */
216 : :
217 : : /* Prepare SortSupport data for each column */
218 : 59356 : base->sortKeys = (SortSupport) palloc0(nkeys * sizeof(SortSupportData));
219 : :
220 [ + + ]: 146551 : for (i = 0; i < nkeys; i++)
221 : : {
222 : 87201 : SortSupport sortKey = base->sortKeys + i;
223 : :
1146 peter@eisentraut.org 224 [ - + ]: 87201 : Assert(attNums[i] != 0);
225 [ - + ]: 87201 : Assert(sortOperators[i] != 0);
226 : :
1239 akorotkov@postgresql 227 : 87201 : sortKey->ssup_cxt = CurrentMemoryContext;
228 : 87201 : sortKey->ssup_collation = sortCollations[i];
229 : 87201 : sortKey->ssup_nulls_first = nullsFirstFlags[i];
230 : 87201 : sortKey->ssup_attno = attNums[i];
231 : : /* Convey if abbreviation optimization is applicable in principle */
232 [ + + + - ]: 87201 : sortKey->abbreviate = (i == 0 && base->haveDatum1);
233 : :
234 : 87201 : PrepareSortSupportFromOrderingOp(sortOperators[i], sortKey);
235 : : }
236 : :
237 : : /*
238 : : * The "onlyKey" optimization cannot be used with abbreviated keys, since
239 : : * tie-breaker comparisons may be required. Typically, the optimization
240 : : * is only of value to pass-by-value types anyway, whereas abbreviated
241 : : * keys are typically only of value to pass-by-reference types.
242 : : */
243 [ + + + + ]: 59350 : if (nkeys == 1 && !base->sortKeys->abbrev_converter)
244 : 33820 : base->onlyKey = base->sortKeys;
245 : :
246 : 59350 : MemoryContextSwitchTo(oldcontext);
247 : :
248 : 59350 : return state;
249 : : }
250 : :
251 : : Tuplesortstate *
252 : 57 : tuplesort_begin_cluster(TupleDesc tupDesc,
253 : : Relation indexRel,
254 : : int workMem,
255 : : SortCoordinate coordinate, int sortopt)
256 : : {
257 : 57 : Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
258 : : sortopt);
259 : 57 : TuplesortPublic *base = TuplesortstateGetPublic(state);
260 : : BTScanInsert indexScanKey;
261 : : MemoryContext oldcontext;
262 : : TuplesortClusterArg *arg;
263 : : int i;
264 : :
265 [ - + ]: 57 : Assert(indexRel->rd_rel->relam == BTREE_AM_OID);
266 : :
267 : 57 : oldcontext = MemoryContextSwitchTo(base->maincontext);
7 michael@paquier.xyz 268 :GNC 57 : arg = palloc0_object(TuplesortClusterArg);
269 : :
1239 akorotkov@postgresql 270 [ - + ]:CBC 57 : if (trace_sort)
1239 akorotkov@postgresql 271 [ # # # # ]:UBC 0 : elog(LOG,
272 : : "begin tuple sort: nkeys = %d, workMem = %d, randomAccess = %c",
273 : : RelationGetNumberOfAttributes(indexRel),
274 : : workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
275 : :
1239 akorotkov@postgresql 276 :CBC 57 : base->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel);
277 : :
278 : : TRACE_POSTGRESQL_SORT_START(CLUSTER_SORT,
279 : : false, /* no unique check */
280 : : base->nKeys,
281 : : workMem,
282 : : sortopt & TUPLESORT_RANDOMACCESS,
283 : : PARALLEL_SORT(coordinate));
284 : :
285 : 57 : base->removeabbrev = removeabbrev_cluster;
286 : 57 : base->comparetup = comparetup_cluster;
854 john.naylor@postgres 287 : 57 : base->comparetup_tiebreak = comparetup_cluster_tiebreak;
1239 akorotkov@postgresql 288 : 57 : base->writetup = writetup_cluster;
289 : 57 : base->readtup = readtup_cluster;
290 : 57 : base->freestate = freestate_cluster;
291 : 57 : base->arg = arg;
292 : :
293 : 57 : arg->indexInfo = BuildIndexInfo(indexRel);
294 : :
295 : : /*
296 : : * If we don't have a simple leading attribute, we don't currently
297 : : * initialize datum1, so disable optimizations that require it.
298 : : */
299 [ + + ]: 57 : if (arg->indexInfo->ii_IndexAttrNumbers[0] == 0)
300 : 12 : base->haveDatum1 = false;
301 : : else
302 : 45 : base->haveDatum1 = true;
303 : :
304 : 57 : arg->tupDesc = tupDesc; /* assume we need not copy tupDesc */
305 : :
921 pg@bowt.ie 306 : 57 : indexScanKey = _bt_mkscankey(indexRel, NULL);
307 : :
1239 akorotkov@postgresql 308 [ + + ]: 57 : if (arg->indexInfo->ii_Expressions != NULL)
309 : : {
310 : : TupleTableSlot *slot;
311 : : ExprContext *econtext;
312 : :
313 : : /*
314 : : * We will need to use FormIndexDatum to evaluate the index
315 : : * expressions. To do that, we need an EState, as well as a
316 : : * TupleTableSlot to put the table tuples into. The econtext's
317 : : * scantuple has to point to that slot, too.
318 : : */
319 : 12 : arg->estate = CreateExecutorState();
320 : 12 : slot = MakeSingleTupleTableSlot(tupDesc, &TTSOpsHeapTuple);
321 [ - + ]: 12 : econtext = GetPerTupleExprContext(arg->estate);
322 : 12 : econtext->ecxt_scantuple = slot;
323 : : }
324 : :
325 : : /* Prepare SortSupport data for each column */
326 : 57 : base->sortKeys = (SortSupport) palloc0(base->nKeys *
327 : : sizeof(SortSupportData));
328 : :
329 [ + + ]: 126 : for (i = 0; i < base->nKeys; i++)
330 : : {
331 : 69 : SortSupport sortKey = base->sortKeys + i;
332 : 69 : ScanKey scanKey = indexScanKey->scankeys + i;
333 : : bool reverse;
334 : :
335 : 69 : sortKey->ssup_cxt = CurrentMemoryContext;
336 : 69 : sortKey->ssup_collation = scanKey->sk_collation;
337 : 69 : sortKey->ssup_nulls_first =
338 : 69 : (scanKey->sk_flags & SK_BT_NULLS_FIRST) != 0;
339 : 69 : sortKey->ssup_attno = scanKey->sk_attno;
340 : : /* Convey if abbreviation optimization is applicable in principle */
341 [ + + + + ]: 69 : sortKey->abbreviate = (i == 0 && base->haveDatum1);
342 : :
1146 peter@eisentraut.org 343 [ - + ]: 69 : Assert(sortKey->ssup_attno != 0);
344 : :
278 345 : 69 : reverse = (scanKey->sk_flags & SK_BT_DESC) != 0;
346 : :
347 : 69 : PrepareSortSupportFromIndexRel(indexRel, reverse, sortKey);
348 : : }
349 : :
1239 akorotkov@postgresql 350 : 57 : pfree(indexScanKey);
351 : :
352 : 57 : MemoryContextSwitchTo(oldcontext);
353 : :
354 : 57 : return state;
355 : : }
356 : :
357 : : Tuplesortstate *
358 : 47618 : tuplesort_begin_index_btree(Relation heapRel,
359 : : Relation indexRel,
360 : : bool enforceUnique,
361 : : bool uniqueNullsNotDistinct,
362 : : int workMem,
363 : : SortCoordinate coordinate,
364 : : int sortopt)
365 : : {
366 : 47618 : Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
367 : : sortopt);
368 : 47618 : TuplesortPublic *base = TuplesortstateGetPublic(state);
369 : : BTScanInsert indexScanKey;
370 : : TuplesortIndexBTreeArg *arg;
371 : : MemoryContext oldcontext;
372 : : int i;
373 : :
374 : 47618 : oldcontext = MemoryContextSwitchTo(base->maincontext);
7 michael@paquier.xyz 375 :GNC 47618 : arg = palloc_object(TuplesortIndexBTreeArg);
376 : :
1239 akorotkov@postgresql 377 [ - + ]:CBC 47618 : if (trace_sort)
1239 akorotkov@postgresql 378 [ # # # # :UBC 0 : elog(LOG,
# # ]
379 : : "begin index sort: unique = %c, workMem = %d, randomAccess = %c",
380 : : enforceUnique ? 't' : 'f',
381 : : workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
382 : :
1239 akorotkov@postgresql 383 :CBC 47618 : base->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel);
384 : :
385 : : TRACE_POSTGRESQL_SORT_START(INDEX_SORT,
386 : : enforceUnique,
387 : : base->nKeys,
388 : : workMem,
389 : : sortopt & TUPLESORT_RANDOMACCESS,
390 : : PARALLEL_SORT(coordinate));
391 : :
392 : 47618 : base->removeabbrev = removeabbrev_index;
393 : 47618 : base->comparetup = comparetup_index_btree;
854 john.naylor@postgres 394 : 47618 : base->comparetup_tiebreak = comparetup_index_btree_tiebreak;
1239 akorotkov@postgresql 395 : 47618 : base->writetup = writetup_index;
396 : 47618 : base->readtup = readtup_index;
397 : 47618 : base->haveDatum1 = true;
398 : 47618 : base->arg = arg;
399 : :
400 : 47618 : arg->index.heapRel = heapRel;
401 : 47618 : arg->index.indexRel = indexRel;
402 : 47618 : arg->enforceUnique = enforceUnique;
403 : 47618 : arg->uniqueNullsNotDistinct = uniqueNullsNotDistinct;
404 : :
921 pg@bowt.ie 405 : 47618 : indexScanKey = _bt_mkscankey(indexRel, NULL);
406 : :
407 : : /* Prepare SortSupport data for each column */
1239 akorotkov@postgresql 408 : 47618 : base->sortKeys = (SortSupport) palloc0(base->nKeys *
409 : : sizeof(SortSupportData));
410 : :
411 [ + + ]: 126251 : for (i = 0; i < base->nKeys; i++)
412 : : {
413 : 78633 : SortSupport sortKey = base->sortKeys + i;
414 : 78633 : ScanKey scanKey = indexScanKey->scankeys + i;
415 : : bool reverse;
416 : :
417 : 78633 : sortKey->ssup_cxt = CurrentMemoryContext;
418 : 78633 : sortKey->ssup_collation = scanKey->sk_collation;
419 : 78633 : sortKey->ssup_nulls_first =
420 : 78633 : (scanKey->sk_flags & SK_BT_NULLS_FIRST) != 0;
421 : 78633 : sortKey->ssup_attno = scanKey->sk_attno;
422 : : /* Convey if abbreviation optimization is applicable in principle */
423 [ + + + - ]: 78633 : sortKey->abbreviate = (i == 0 && base->haveDatum1);
424 : :
1146 peter@eisentraut.org 425 [ - + ]: 78633 : Assert(sortKey->ssup_attno != 0);
426 : :
278 427 : 78633 : reverse = (scanKey->sk_flags & SK_BT_DESC) != 0;
428 : :
429 : 78633 : PrepareSortSupportFromIndexRel(indexRel, reverse, sortKey);
430 : : }
431 : :
1239 akorotkov@postgresql 432 : 47618 : pfree(indexScanKey);
433 : :
434 : 47618 : MemoryContextSwitchTo(oldcontext);
435 : :
436 : 47618 : return state;
437 : : }
438 : :
439 : : Tuplesortstate *
440 : 4 : tuplesort_begin_index_hash(Relation heapRel,
441 : : Relation indexRel,
442 : : uint32 high_mask,
443 : : uint32 low_mask,
444 : : uint32 max_buckets,
445 : : int workMem,
446 : : SortCoordinate coordinate,
447 : : int sortopt)
448 : : {
449 : 4 : Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
450 : : sortopt);
451 : 4 : TuplesortPublic *base = TuplesortstateGetPublic(state);
452 : : MemoryContext oldcontext;
453 : : TuplesortIndexHashArg *arg;
454 : :
455 : 4 : oldcontext = MemoryContextSwitchTo(base->maincontext);
7 michael@paquier.xyz 456 :GNC 4 : arg = palloc_object(TuplesortIndexHashArg);
457 : :
1239 akorotkov@postgresql 458 [ - + ]:CBC 4 : if (trace_sort)
1239 akorotkov@postgresql 459 [ # # # # ]:UBC 0 : elog(LOG,
460 : : "begin index sort: high_mask = 0x%x, low_mask = 0x%x, "
461 : : "max_buckets = 0x%x, workMem = %d, randomAccess = %c",
462 : : high_mask,
463 : : low_mask,
464 : : max_buckets,
465 : : workMem,
466 : : sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
467 : :
1239 akorotkov@postgresql 468 :CBC 4 : base->nKeys = 1; /* Only one sort column, the hash code */
469 : :
470 : 4 : base->removeabbrev = removeabbrev_index;
471 : 4 : base->comparetup = comparetup_index_hash;
854 john.naylor@postgres 472 : 4 : base->comparetup_tiebreak = comparetup_index_hash_tiebreak;
1239 akorotkov@postgresql 473 : 4 : base->writetup = writetup_index;
474 : 4 : base->readtup = readtup_index;
475 : 4 : base->haveDatum1 = true;
476 : 4 : base->arg = arg;
477 : :
478 : 4 : arg->index.heapRel = heapRel;
479 : 4 : arg->index.indexRel = indexRel;
480 : :
481 : 4 : arg->high_mask = high_mask;
482 : 4 : arg->low_mask = low_mask;
483 : 4 : arg->max_buckets = max_buckets;
484 : :
485 : 4 : MemoryContextSwitchTo(oldcontext);
486 : :
487 : 4 : return state;
488 : : }
489 : :
490 : : Tuplesortstate *
491 : 412 : tuplesort_begin_index_gist(Relation heapRel,
492 : : Relation indexRel,
493 : : int workMem,
494 : : SortCoordinate coordinate,
495 : : int sortopt)
496 : : {
497 : 412 : Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
498 : : sortopt);
499 : 412 : TuplesortPublic *base = TuplesortstateGetPublic(state);
500 : : MemoryContext oldcontext;
501 : : TuplesortIndexBTreeArg *arg;
502 : : int i;
503 : :
504 : 412 : oldcontext = MemoryContextSwitchTo(base->maincontext);
7 michael@paquier.xyz 505 :GNC 412 : arg = palloc_object(TuplesortIndexBTreeArg);
506 : :
1239 akorotkov@postgresql 507 [ - + ]:CBC 412 : if (trace_sort)
1239 akorotkov@postgresql 508 [ # # # # ]:UBC 0 : elog(LOG,
509 : : "begin index sort: workMem = %d, randomAccess = %c",
510 : : workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
511 : :
1239 akorotkov@postgresql 512 :CBC 412 : base->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel);
513 : :
514 : 412 : base->removeabbrev = removeabbrev_index;
515 : 412 : base->comparetup = comparetup_index_btree;
854 john.naylor@postgres 516 : 412 : base->comparetup_tiebreak = comparetup_index_btree_tiebreak;
1239 akorotkov@postgresql 517 : 412 : base->writetup = writetup_index;
518 : 412 : base->readtup = readtup_index;
519 : 412 : base->haveDatum1 = true;
520 : 412 : base->arg = arg;
521 : :
522 : 412 : arg->index.heapRel = heapRel;
523 : 412 : arg->index.indexRel = indexRel;
524 : 412 : arg->enforceUnique = false;
525 : 412 : arg->uniqueNullsNotDistinct = false;
526 : :
527 : : /* Prepare SortSupport data for each column */
528 : 412 : base->sortKeys = (SortSupport) palloc0(base->nKeys *
529 : : sizeof(SortSupportData));
530 : :
531 [ + + ]: 1131 : for (i = 0; i < base->nKeys; i++)
532 : : {
533 : 719 : SortSupport sortKey = base->sortKeys + i;
534 : :
535 : 719 : sortKey->ssup_cxt = CurrentMemoryContext;
536 : 719 : sortKey->ssup_collation = indexRel->rd_indcollation[i];
537 : 719 : sortKey->ssup_nulls_first = false;
538 : 719 : sortKey->ssup_attno = i + 1;
539 : : /* Convey if abbreviation optimization is applicable in principle */
540 [ + + + - ]: 719 : sortKey->abbreviate = (i == 0 && base->haveDatum1);
541 : :
1146 peter@eisentraut.org 542 [ - + ]: 719 : Assert(sortKey->ssup_attno != 0);
543 : :
544 : : /* Look for a sort support function */
1239 akorotkov@postgresql 545 : 719 : PrepareSortSupportFromGistIndexRel(indexRel, sortKey);
546 : : }
547 : :
548 : 412 : MemoryContextSwitchTo(oldcontext);
549 : :
550 : 412 : return state;
551 : : }
552 : :
553 : : Tuplesortstate *
718 tomas.vondra@postgre 554 : 14 : tuplesort_begin_index_brin(int workMem,
555 : : SortCoordinate coordinate,
556 : : int sortopt)
557 : : {
740 558 : 14 : Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
559 : : sortopt);
560 : 14 : TuplesortPublic *base = TuplesortstateGetPublic(state);
561 : :
562 [ - + ]: 14 : if (trace_sort)
740 tomas.vondra@postgre 563 [ # # # # ]:UBC 0 : elog(LOG,
564 : : "begin index sort: workMem = %d, randomAccess = %c",
565 : : workMem,
566 : : sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
567 : :
740 tomas.vondra@postgre 568 :CBC 14 : base->nKeys = 1; /* Only one sort column, the block number */
569 : :
570 : 14 : base->removeabbrev = removeabbrev_index_brin;
571 : 14 : base->comparetup = comparetup_index_brin;
572 : 14 : base->writetup = writetup_index_brin;
573 : 14 : base->readtup = readtup_index_brin;
574 : 14 : base->haveDatum1 = true;
718 575 : 14 : base->arg = NULL;
576 : :
740 577 : 14 : return state;
578 : : }
579 : :
580 : : Tuplesortstate *
289 tomas.vondra@postgre 581 :UBC 0 : tuplesort_begin_index_gin(Relation heapRel,
582 : : Relation indexRel,
583 : : int workMem, SortCoordinate coordinate,
584 : : int sortopt)
585 : : {
586 : 0 : Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
587 : : sortopt);
588 : 0 : TuplesortPublic *base = TuplesortstateGetPublic(state);
589 : : MemoryContext oldcontext;
590 : : int i;
591 : 0 : TupleDesc desc = RelationGetDescr(indexRel);
592 : :
593 : 0 : oldcontext = MemoryContextSwitchTo(base->maincontext);
594 : :
595 : : #ifdef TRACE_SORT
596 : : if (trace_sort)
597 : : elog(LOG,
598 : : "begin index sort: workMem = %d, randomAccess = %c",
599 : : workMem,
600 : : sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
601 : : #endif
602 : :
603 : : /*
604 : : * Multi-column GIN indexes expand the row into a separate index entry for
605 : : * attribute, and that's what we write into the tuplesort. But we still
606 : : * need to initialize sortsupport for all the attributes.
607 : : */
608 : 0 : base->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel);
609 : :
610 : : /* Prepare SortSupport data for each column */
611 : 0 : base->sortKeys = (SortSupport) palloc0(base->nKeys *
612 : : sizeof(SortSupportData));
613 : :
614 [ # # ]: 0 : for (i = 0; i < base->nKeys; i++)
615 : : {
616 : 0 : SortSupport sortKey = base->sortKeys + i;
617 : 0 : Form_pg_attribute att = TupleDescAttr(desc, i);
618 : : TypeCacheEntry *typentry;
619 : :
620 : 0 : sortKey->ssup_cxt = CurrentMemoryContext;
621 : 0 : sortKey->ssup_collation = indexRel->rd_indcollation[i];
622 : 0 : sortKey->ssup_nulls_first = false;
623 : 0 : sortKey->ssup_attno = i + 1;
624 : 0 : sortKey->abbreviate = false;
625 : :
626 [ # # ]: 0 : Assert(sortKey->ssup_attno != 0);
627 : :
628 [ # # ]: 0 : if (!OidIsValid(sortKey->ssup_collation))
629 : 0 : sortKey->ssup_collation = DEFAULT_COLLATION_OID;
630 : :
631 : : /*
632 : : * Look for a ordering for the index key data type, and then the sort
633 : : * support function.
634 : : */
635 : 0 : typentry = lookup_type_cache(att->atttypid, TYPECACHE_LT_OPR);
636 : 0 : PrepareSortSupportFromOrderingOp(typentry->lt_opr, sortKey);
637 : : }
638 : :
639 : 0 : base->removeabbrev = removeabbrev_index_gin;
640 : 0 : base->comparetup = comparetup_index_gin;
641 : 0 : base->writetup = writetup_index_gin;
642 : 0 : base->readtup = readtup_index_gin;
643 : 0 : base->haveDatum1 = false;
644 : 0 : base->arg = NULL;
645 : :
646 : 0 : MemoryContextSwitchTo(oldcontext);
647 : :
648 : 0 : return state;
649 : : }
650 : :
651 : : Tuplesortstate *
1239 akorotkov@postgresql 652 :CBC 30573 : tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation,
653 : : bool nullsFirstFlag, int workMem,
654 : : SortCoordinate coordinate, int sortopt)
655 : : {
656 : 30573 : Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
657 : : sortopt);
658 : 30573 : TuplesortPublic *base = TuplesortstateGetPublic(state);
659 : : TuplesortDatumArg *arg;
660 : : MemoryContext oldcontext;
661 : : int16 typlen;
662 : : bool typbyval;
663 : :
664 : 30573 : oldcontext = MemoryContextSwitchTo(base->maincontext);
7 michael@paquier.xyz 665 :GNC 30573 : arg = palloc_object(TuplesortDatumArg);
666 : :
1239 akorotkov@postgresql 667 [ - + ]:CBC 30573 : if (trace_sort)
1239 akorotkov@postgresql 668 [ # # # # ]:UBC 0 : elog(LOG,
669 : : "begin datum sort: workMem = %d, randomAccess = %c",
670 : : workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
671 : :
1239 akorotkov@postgresql 672 :CBC 30573 : base->nKeys = 1; /* always a one-column sort */
673 : :
674 : : TRACE_POSTGRESQL_SORT_START(DATUM_SORT,
675 : : false, /* no unique check */
676 : : 1,
677 : : workMem,
678 : : sortopt & TUPLESORT_RANDOMACCESS,
679 : : PARALLEL_SORT(coordinate));
680 : :
681 : 30573 : base->removeabbrev = removeabbrev_datum;
682 : 30573 : base->comparetup = comparetup_datum;
854 john.naylor@postgres 683 : 30573 : base->comparetup_tiebreak = comparetup_datum_tiebreak;
1239 akorotkov@postgresql 684 : 30573 : base->writetup = writetup_datum;
685 : 30573 : base->readtup = readtup_datum;
686 : 30573 : base->haveDatum1 = true;
687 : 30573 : base->arg = arg;
688 : :
689 : 30573 : arg->datumType = datumType;
690 : :
691 : : /* lookup necessary attributes of the datum type */
692 : 30573 : get_typlenbyval(datumType, &typlen, &typbyval);
693 : 30573 : arg->datumTypeLen = typlen;
694 : 30573 : base->tuples = !typbyval;
695 : :
696 : : /* Prepare SortSupport data */
7 michael@paquier.xyz 697 :GNC 30573 : base->sortKeys = palloc0_object(SortSupportData);
698 : :
1239 akorotkov@postgresql 699 :CBC 30573 : base->sortKeys->ssup_cxt = CurrentMemoryContext;
700 : 30573 : base->sortKeys->ssup_collation = sortCollation;
701 : 30573 : base->sortKeys->ssup_nulls_first = nullsFirstFlag;
702 : :
703 : : /*
704 : : * Abbreviation is possible here only for by-reference types. In theory,
705 : : * a pass-by-value datatype could have an abbreviated form that is cheaper
706 : : * to compare. In a tuple sort, we could support that, because we can
707 : : * always extract the original datum from the tuple as needed. Here, we
708 : : * can't, because a datum sort only stores a single copy of the datum; the
709 : : * "tuple" field of each SortTuple is NULL.
710 : : */
711 : 30573 : base->sortKeys->abbreviate = !typbyval;
712 : :
713 : 30573 : PrepareSortSupportFromOrderingOp(sortOperator, base->sortKeys);
714 : :
715 : : /*
716 : : * The "onlyKey" optimization cannot be used with abbreviated keys, since
717 : : * tie-breaker comparisons may be required. Typically, the optimization
718 : : * is only of value to pass-by-value types anyway, whereas abbreviated
719 : : * keys are typically only of value to pass-by-reference types.
720 : : */
721 [ + + ]: 30573 : if (!base->sortKeys->abbrev_converter)
722 : 30165 : base->onlyKey = base->sortKeys;
723 : :
724 : 30573 : MemoryContextSwitchTo(oldcontext);
725 : :
726 : 30573 : return state;
727 : : }
728 : :
729 : : /*
730 : : * Accept one tuple while collecting input data for sort.
731 : : *
732 : : * Note that the input data is always copied; the caller need not save it.
733 : : */
734 : : void
735 : 6630710 : tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
736 : : {
737 : 6630710 : TuplesortPublic *base = TuplesortstateGetPublic(state);
738 : 6630710 : MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
739 : 6630710 : TupleDesc tupDesc = (TupleDesc) base->arg;
740 : : SortTuple stup;
741 : : MinimalTuple tuple;
742 : : HeapTupleData htup;
743 : : Size tuplen;
744 : :
745 : : /* copy the tuple into sort storage */
746 : 6630710 : tuple = ExecCopySlotMinimalTuple(slot);
384 peter@eisentraut.org 747 : 6630710 : stup.tuple = tuple;
748 : : /* set up first-column key value */
1239 akorotkov@postgresql 749 : 6630710 : htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
750 : 6630710 : htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
751 : 13261420 : stup.datum1 = heap_getattr(&htup,
752 : 6630710 : base->sortKeys[0].ssup_attno,
753 : : tupDesc,
754 : : &stup.isnull1);
755 : :
756 : : /* GetMemoryChunkSpace is not supported for bump contexts */
618 drowley@postgresql.o 757 [ + + ]: 6630710 : if (TupleSortUseBumpTupleCxt(base->sortopt))
758 : 4877428 : tuplen = MAXALIGN(tuple->t_len);
759 : : else
760 : 1753282 : tuplen = GetMemoryChunkSpace(tuple);
761 : :
1239 akorotkov@postgresql 762 : 6630710 : tuplesort_puttuple_common(state, &stup,
763 [ + + ]: 7192066 : base->sortKeys->abbrev_converter &&
618 drowley@postgresql.o 764 [ + + ]: 561356 : !stup.isnull1, tuplen);
765 : :
1239 akorotkov@postgresql 766 : 6630710 : MemoryContextSwitchTo(oldcontext);
767 : 6630710 : }
768 : :
769 : : /*
770 : : * Accept one tuple while collecting input data for sort.
771 : : *
772 : : * Note that the input data is always copied; the caller need not save it.
773 : : */
774 : : void
775 : 273707 : tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
776 : : {
777 : : SortTuple stup;
778 : 273707 : TuplesortPublic *base = TuplesortstateGetPublic(state);
779 : 273707 : MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
780 : 273707 : TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
781 : : Size tuplen;
782 : :
783 : : /* copy the tuple into sort storage */
784 : 273707 : tup = heap_copytuple(tup);
384 peter@eisentraut.org 785 : 273707 : stup.tuple = tup;
786 : :
787 : : /*
788 : : * set up first-column key value, and potentially abbreviate, if it's a
789 : : * simple column
790 : : */
1239 akorotkov@postgresql 791 [ + + ]: 273707 : if (base->haveDatum1)
792 : : {
793 : 272894 : stup.datum1 = heap_getattr(tup,
794 : 272894 : arg->indexInfo->ii_IndexAttrNumbers[0],
795 : : arg->tupDesc,
796 : : &stup.isnull1);
797 : : }
798 : :
799 : : /* GetMemoryChunkSpace is not supported for bump contexts */
618 drowley@postgresql.o 800 [ + - ]: 273707 : if (TupleSortUseBumpTupleCxt(base->sortopt))
801 : 273707 : tuplen = MAXALIGN(HEAPTUPLESIZE + tup->t_len);
802 : : else
618 drowley@postgresql.o 803 :UBC 0 : tuplen = GetMemoryChunkSpace(tup);
804 : :
1239 akorotkov@postgresql 805 :CBC 273707 : tuplesort_puttuple_common(state, &stup,
806 : 546601 : base->haveDatum1 &&
807 [ + + + + ]: 455251 : base->sortKeys->abbrev_converter &&
618 drowley@postgresql.o 808 [ + + ]: 181544 : !stup.isnull1, tuplen);
809 : :
1239 akorotkov@postgresql 810 : 273707 : MemoryContextSwitchTo(oldcontext);
811 : 273707 : }
812 : :
813 : : /*
814 : : * Collect one index tuple while collecting input data for sort, building
815 : : * it from caller-supplied values.
816 : : */
817 : : void
818 : 6650929 : tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
819 : : const ItemPointerData *self, const Datum *values,
820 : : const bool *isnull)
821 : : {
822 : : SortTuple stup;
823 : : IndexTuple tuple;
824 : 6650929 : TuplesortPublic *base = TuplesortstateGetPublic(state);
825 : 6650929 : TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
826 : : Size tuplen;
827 : :
828 : 6650929 : stup.tuple = index_form_tuple_context(RelationGetDescr(rel), values,
829 : : isnull, base->tuplecontext);
830 : 6650929 : tuple = ((IndexTuple) stup.tuple);
831 : 6650929 : tuple->t_tid = *self;
832 : : /* set up first-column key value */
833 : 13301858 : stup.datum1 = index_getattr(tuple,
834 : : 1,
835 : 6650929 : RelationGetDescr(arg->indexRel),
836 : : &stup.isnull1);
837 : :
838 : : /* GetMemoryChunkSpace is not supported for bump contexts */
618 drowley@postgresql.o 839 [ + - ]: 6650929 : if (TupleSortUseBumpTupleCxt(base->sortopt))
840 : 6650929 : tuplen = MAXALIGN(tuple->t_info & INDEX_SIZE_MASK);
841 : : else
618 drowley@postgresql.o 842 :UBC 0 : tuplen = GetMemoryChunkSpace(tuple);
843 : :
1239 akorotkov@postgresql 844 :CBC 6650929 : tuplesort_puttuple_common(state, &stup,
845 : 13241358 : base->sortKeys &&
846 [ + + + + ]: 7712388 : base->sortKeys->abbrev_converter &&
618 drowley@postgresql.o 847 [ + + ]: 1061459 : !stup.isnull1, tuplen);
1239 akorotkov@postgresql 848 : 6650929 : }
849 : :
850 : : /*
851 : : * Collect one BRIN tuple while collecting input data for sort.
852 : : */
853 : : void
740 tomas.vondra@postgre 854 : 46 : tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
855 : : {
856 : : SortTuple stup;
857 : : BrinSortTuple *bstup;
858 : 46 : TuplesortPublic *base = TuplesortstateGetPublic(state);
859 : 46 : MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
860 : : Size tuplen;
861 : :
862 : : /* allocate space for the whole BRIN sort tuple */
863 : 46 : bstup = palloc(BRINSORTTUPLE_SIZE(size));
864 : :
865 : 46 : bstup->tuplen = size;
866 : 46 : memcpy(&bstup->tuple, tuple, size);
867 : :
868 : 46 : stup.tuple = bstup;
131 peter@eisentraut.org 869 :GNC 46 : stup.datum1 = UInt32GetDatum(tuple->bt_blkno);
740 tomas.vondra@postgre 870 :CBC 46 : stup.isnull1 = false;
871 : :
872 : : /* GetMemoryChunkSpace is not supported for bump contexts */
618 drowley@postgresql.o 873 [ + - ]: 46 : if (TupleSortUseBumpTupleCxt(base->sortopt))
874 : 46 : tuplen = MAXALIGN(BRINSORTTUPLE_SIZE(size));
875 : : else
618 drowley@postgresql.o 876 :UBC 0 : tuplen = GetMemoryChunkSpace(bstup);
877 : :
740 tomas.vondra@postgre 878 :CBC 46 : tuplesort_puttuple_common(state, &stup,
879 : 46 : base->sortKeys &&
880 [ - + - - ]: 46 : base->sortKeys->abbrev_converter &&
618 drowley@postgresql.o 881 [ # # ]:UBC 0 : !stup.isnull1, tuplen);
882 : :
740 tomas.vondra@postgre 883 :CBC 46 : MemoryContextSwitchTo(oldcontext);
884 : 46 : }
885 : :
886 : : void
289 tomas.vondra@postgre 887 :UBC 0 : tuplesort_putgintuple(Tuplesortstate *state, GinTuple *tuple, Size size)
888 : : {
889 : : SortTuple stup;
890 : : GinTuple *ctup;
891 : 0 : TuplesortPublic *base = TuplesortstateGetPublic(state);
892 : 0 : MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
893 : : Size tuplen;
894 : :
895 : : /* copy the GinTuple into the right memory context */
896 : 0 : ctup = palloc(size);
897 : 0 : memcpy(ctup, tuple, size);
898 : :
899 : 0 : stup.tuple = ctup;
900 : 0 : stup.datum1 = (Datum) 0;
901 : 0 : stup.isnull1 = false;
902 : :
903 : : /* GetMemoryChunkSpace is not supported for bump contexts */
904 [ # # ]: 0 : if (TupleSortUseBumpTupleCxt(base->sortopt))
905 : 0 : tuplen = MAXALIGN(size);
906 : : else
907 : 0 : tuplen = GetMemoryChunkSpace(ctup);
908 : :
909 : 0 : tuplesort_puttuple_common(state, &stup,
910 : 0 : base->sortKeys &&
911 [ # # # # ]: 0 : base->sortKeys->abbrev_converter &&
912 [ # # ]: 0 : !stup.isnull1, tuplen);
913 : :
914 : 0 : MemoryContextSwitchTo(oldcontext);
915 : 0 : }
916 : :
917 : : /*
918 : : * Accept one Datum while collecting input data for sort.
919 : : *
920 : : * If the Datum is pass-by-ref type, the value will be copied.
921 : : */
922 : : void
1239 akorotkov@postgresql 923 :CBC 1990581 : tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
924 : : {
925 : 1990581 : TuplesortPublic *base = TuplesortstateGetPublic(state);
926 : 1990581 : MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
927 : 1990581 : TuplesortDatumArg *arg = (TuplesortDatumArg *) base->arg;
928 : : SortTuple stup;
929 : :
930 : : /*
931 : : * Pass-by-value types or null values are just stored directly in
932 : : * stup.datum1 (and stup.tuple is not used and set to NULL).
933 : : *
934 : : * Non-null pass-by-reference values need to be copied into memory we
935 : : * control, and possibly abbreviated. The copied value is pointed to by
936 : : * stup.tuple and is treated as the canonical copy (e.g. to return via
937 : : * tuplesort_getdatum or when writing to tape); stup.datum1 gets the
938 : : * abbreviated value if abbreviation is happening, otherwise it's
939 : : * identical to stup.tuple.
940 : : */
941 : :
942 [ + + + + ]: 1990581 : if (isNull || !base->tuples)
943 : : {
944 : : /*
945 : : * Set datum1 to zeroed representation for NULLs (to be consistent,
946 : : * and to support cheap inequality tests for NULL abbreviated keys).
947 : : */
948 [ + + ]: 1112540 : stup.datum1 = !isNull ? val : (Datum) 0;
949 : 1112540 : stup.isnull1 = isNull;
950 : 1112540 : stup.tuple = NULL; /* no separate storage */
951 : : }
952 : : else
953 : : {
954 : 878041 : stup.isnull1 = false;
955 : 878041 : stup.datum1 = datumCopy(val, false, arg->datumTypeLen);
956 : 878041 : stup.tuple = DatumGetPointer(stup.datum1);
957 : : }
958 : :
959 : 1990581 : tuplesort_puttuple_common(state, &stup,
960 : 2869035 : base->tuples &&
618 drowley@postgresql.o 961 [ + + + + : 1990581 : base->sortKeys->abbrev_converter && !isNull, 0);
+ + ]
962 : :
1239 akorotkov@postgresql 963 : 1990581 : MemoryContextSwitchTo(oldcontext);
964 : 1990581 : }
965 : :
966 : : /*
967 : : * Fetch the next tuple in either forward or back direction.
968 : : * If successful, put tuple in slot and return true; else, clear the slot
969 : : * and return false.
970 : : *
971 : : * Caller may optionally be passed back abbreviated value (on true return
972 : : * value) when abbreviation was used, which can be used to cheaply avoid
973 : : * equality checks that might otherwise be required. Caller can safely make a
974 : : * determination of "non-equal tuple" based on simple binary inequality. A
975 : : * NULL value in leading attribute will set abbreviated value to zeroed
976 : : * representation, which caller may rely on in abbreviated inequality check.
977 : : *
978 : : * If copy is true, the slot receives a tuple that's been copied into the
979 : : * caller's memory context, so that it will stay valid regardless of future
980 : : * manipulations of the tuplesort's state (up to and including deleting the
981 : : * tuplesort). If copy is false, the slot will just receive a pointer to a
982 : : * tuple held within the tuplesort, which is more efficient, but only safe for
983 : : * callers that are prepared to have any subsequent manipulation of the
984 : : * tuplesort's state invalidate slot contents.
985 : : */
986 : : bool
987 : 5982561 : tuplesort_gettupleslot(Tuplesortstate *state, bool forward, bool copy,
988 : : TupleTableSlot *slot, Datum *abbrev)
989 : : {
990 : 5982561 : TuplesortPublic *base = TuplesortstateGetPublic(state);
991 : 5982561 : MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
992 : : SortTuple stup;
993 : :
994 [ + + ]: 5982561 : if (!tuplesort_gettuple_common(state, forward, &stup))
995 : 61012 : stup.tuple = NULL;
996 : :
997 : 5982561 : MemoryContextSwitchTo(oldcontext);
998 : :
999 [ + + ]: 5982561 : if (stup.tuple)
1000 : : {
1001 : : /* Record abbreviated key for caller */
1002 [ + + - + ]: 5921549 : if (base->sortKeys->abbrev_converter && abbrev)
1239 akorotkov@postgresql 1003 :UBC 0 : *abbrev = stup.datum1;
1004 : :
1239 akorotkov@postgresql 1005 [ + + ]:CBC 5921549 : if (copy)
268 jdavis@postgresql.or 1006 : 2278 : stup.tuple = heap_copy_minimal_tuple((MinimalTuple) stup.tuple, 0);
1007 : :
1239 akorotkov@postgresql 1008 : 5921549 : ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, copy);
1009 : 5921549 : return true;
1010 : : }
1011 : : else
1012 : : {
1013 : 61012 : ExecClearTuple(slot);
1014 : 61012 : return false;
1015 : : }
1016 : : }
1017 : :
1018 : : /*
1019 : : * Fetch the next tuple in either forward or back direction.
1020 : : * Returns NULL if no more tuples. Returned tuple belongs to tuplesort memory
1021 : : * context, and must not be freed by caller. Caller may not rely on tuple
1022 : : * remaining valid after any further manipulation of tuplesort.
1023 : : */
1024 : : HeapTuple
1025 : 273764 : tuplesort_getheaptuple(Tuplesortstate *state, bool forward)
1026 : : {
1027 : 273764 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1028 : 273764 : MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
1029 : : SortTuple stup;
1030 : :
1031 [ + + ]: 273764 : if (!tuplesort_gettuple_common(state, forward, &stup))
1032 : 57 : stup.tuple = NULL;
1033 : :
1034 : 273764 : MemoryContextSwitchTo(oldcontext);
1035 : :
1036 : 273764 : return stup.tuple;
1037 : : }
1038 : :
1039 : : /*
1040 : : * Fetch the next index tuple in either forward or back direction.
1041 : : * Returns NULL if no more tuples. Returned tuple belongs to tuplesort memory
1042 : : * context, and must not be freed by caller. Caller may not rely on tuple
1043 : : * remaining valid after any further manipulation of tuplesort.
1044 : : */
1045 : : IndexTuple
1046 : 6677245 : tuplesort_getindextuple(Tuplesortstate *state, bool forward)
1047 : : {
1048 : 6677245 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1049 : 6677245 : MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
1050 : : SortTuple stup;
1051 : :
1052 [ + + ]: 6677245 : if (!tuplesort_gettuple_common(state, forward, &stup))
1053 : 26505 : stup.tuple = NULL;
1054 : :
1055 : 6677245 : MemoryContextSwitchTo(oldcontext);
1056 : :
1057 : 6677245 : return (IndexTuple) stup.tuple;
1058 : : }
1059 : :
1060 : : /*
1061 : : * Fetch the next BRIN tuple in either forward or back direction.
1062 : : * Returns NULL if no more tuples. Returned tuple belongs to tuplesort memory
1063 : : * context, and must not be freed by caller. Caller may not rely on tuple
1064 : : * remaining valid after any further manipulation of tuplesort.
1065 : : */
1066 : : BrinTuple *
740 tomas.vondra@postgre 1067 : 50 : tuplesort_getbrintuple(Tuplesortstate *state, Size *len, bool forward)
1068 : : {
1069 : 50 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1070 : 50 : MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
1071 : : SortTuple stup;
1072 : : BrinSortTuple *btup;
1073 : :
1074 [ + + ]: 50 : if (!tuplesort_gettuple_common(state, forward, &stup))
1075 : 4 : stup.tuple = NULL;
1076 : :
1077 : 50 : MemoryContextSwitchTo(oldcontext);
1078 : :
1079 [ + + ]: 50 : if (!stup.tuple)
1080 : 4 : return NULL;
1081 : :
1082 : 46 : btup = (BrinSortTuple *) stup.tuple;
1083 : :
1084 : 46 : *len = btup->tuplen;
1085 : :
1086 : 46 : return &btup->tuple;
1087 : : }
1088 : :
1089 : : GinTuple *
289 tomas.vondra@postgre 1090 :UBC 0 : tuplesort_getgintuple(Tuplesortstate *state, Size *len, bool forward)
1091 : : {
1092 : 0 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1093 : 0 : MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
1094 : : SortTuple stup;
1095 : : GinTuple *tup;
1096 : :
1097 [ # # ]: 0 : if (!tuplesort_gettuple_common(state, forward, &stup))
1098 : 0 : stup.tuple = NULL;
1099 : :
1100 : 0 : MemoryContextSwitchTo(oldcontext);
1101 : :
1102 [ # # ]: 0 : if (!stup.tuple)
260 peter@eisentraut.org 1103 : 0 : return NULL;
1104 : :
289 tomas.vondra@postgre 1105 : 0 : tup = (GinTuple *) stup.tuple;
1106 : :
1107 : 0 : *len = tup->tuplen;
1108 : :
1109 : 0 : return tup;
1110 : : }
1111 : :
1112 : : /*
1113 : : * Fetch the next Datum in either forward or back direction.
1114 : : * Returns false if no more datums.
1115 : : *
1116 : : * If the Datum is pass-by-ref type, the returned value is freshly palloc'd
1117 : : * in caller's context, and is now owned by the caller (this differs from
1118 : : * similar routines for other types of tuplesorts).
1119 : : *
1120 : : * Caller may optionally be passed back abbreviated value (on true return
1121 : : * value) when abbreviation was used, which can be used to cheaply avoid
1122 : : * equality checks that might otherwise be required. Caller can safely make a
1123 : : * determination of "non-equal tuple" based on simple binary inequality. A
1124 : : * NULL value will have a zeroed abbreviated value representation, which caller
1125 : : * may rely on in abbreviated inequality check.
1126 : : *
1127 : : * For byref Datums, if copy is true, *val is set to a copy of the Datum
1128 : : * copied into the caller's memory context, so that it will stay valid
1129 : : * regardless of future manipulations of the tuplesort's state (up to and
1130 : : * including deleting the tuplesort). If copy is false, *val will just be
1131 : : * set to a pointer to the Datum held within the tuplesort, which is more
1132 : : * efficient, but only safe for callers that are prepared to have any
1133 : : * subsequent manipulation of the tuplesort's state invalidate slot contents.
1134 : : * For byval Datums, the value of the 'copy' parameter has no effect.
1135 : : */
1136 : : bool
1146 drowley@postgresql.o 1137 :CBC 1302777 : tuplesort_getdatum(Tuplesortstate *state, bool forward, bool copy,
1138 : : Datum *val, bool *isNull, Datum *abbrev)
1139 : : {
1239 akorotkov@postgresql 1140 : 1302777 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1141 : 1302777 : MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
1142 : 1302777 : TuplesortDatumArg *arg = (TuplesortDatumArg *) base->arg;
1143 : : SortTuple stup;
1144 : :
1145 [ + + ]: 1302777 : if (!tuplesort_gettuple_common(state, forward, &stup))
1146 : : {
1147 : 29905 : MemoryContextSwitchTo(oldcontext);
1148 : 29905 : return false;
1149 : : }
1150 : :
1151 : : /* Ensure we copy into caller's memory context */
1152 : 1272872 : MemoryContextSwitchTo(oldcontext);
1153 : :
1154 : : /* Record abbreviated key for caller */
1155 [ + + + + ]: 1272872 : if (base->sortKeys->abbrev_converter && abbrev)
1156 : 27512 : *abbrev = stup.datum1;
1157 : :
1158 [ + + + + ]: 1272872 : if (stup.isnull1 || !base->tuples)
1159 : : {
1160 : 701671 : *val = stup.datum1;
1161 : 701671 : *isNull = stup.isnull1;
1162 : : }
1163 : : else
1164 : : {
1165 : : /* use stup.tuple because stup.datum1 may be an abbreviation */
1146 drowley@postgresql.o 1166 [ + + ]: 571201 : if (copy)
1167 : 30024 : *val = datumCopy(PointerGetDatum(stup.tuple), false,
1168 : : arg->datumTypeLen);
1169 : : else
1170 : 541177 : *val = PointerGetDatum(stup.tuple);
1239 akorotkov@postgresql 1171 : 571201 : *isNull = false;
1172 : : }
1173 : :
1174 : 1272872 : return true;
1175 : : }
1176 : :
1177 : :
1178 : : /*
1179 : : * Routines specialized for HeapTuple (actually MinimalTuple) case
1180 : : */
1181 : :
1182 : : static void
1183 : 6 : removeabbrev_heap(Tuplesortstate *state, SortTuple *stups, int count)
1184 : : {
1185 : : int i;
1186 : 6 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1187 : :
1188 [ + + ]: 61446 : for (i = 0; i < count; i++)
1189 : : {
1190 : : HeapTupleData htup;
1191 : :
1192 : 61440 : htup.t_len = ((MinimalTuple) stups[i].tuple)->t_len +
1193 : : MINIMAL_TUPLE_OFFSET;
1194 : 61440 : htup.t_data = (HeapTupleHeader) ((char *) stups[i].tuple -
1195 : : MINIMAL_TUPLE_OFFSET);
1196 : 61440 : stups[i].datum1 = heap_getattr(&htup,
1197 : 61440 : base->sortKeys[0].ssup_attno,
1198 : 61440 : (TupleDesc) base->arg,
1199 : 61440 : &stups[i].isnull1);
1200 : : }
1201 : 6 : }
1202 : :
1203 : : static int
1204 : 24376552 : comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
1205 : : {
1206 : 24376552 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1207 : 24376552 : SortSupport sortKey = base->sortKeys;
1208 : : int32 compare;
1209 : :
1210 : :
1211 : : /* Compare the leading sort key */
1212 : 24376552 : compare = ApplySortComparator(a->datum1, a->isnull1,
1213 : 24376552 : b->datum1, b->isnull1,
1214 : : sortKey);
1215 [ + + ]: 24376552 : if (compare != 0)
1216 : 8895172 : return compare;
1217 : :
1218 : : /* Compare additional sort keys */
854 john.naylor@postgres 1219 : 15481380 : return comparetup_heap_tiebreak(a, b, state);
1220 : : }
1221 : :
1222 : : static int
1223 : 17039416 : comparetup_heap_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
1224 : : {
1225 : 17039416 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1226 : 17039416 : SortSupport sortKey = base->sortKeys;
1227 : : HeapTupleData ltup;
1228 : : HeapTupleData rtup;
1229 : : TupleDesc tupDesc;
1230 : : int nkey;
1231 : : int32 compare;
1232 : : AttrNumber attno;
1233 : : Datum datum1,
1234 : : datum2;
1235 : : bool isnull1,
1236 : : isnull2;
1237 : :
1239 akorotkov@postgresql 1238 : 17039416 : ltup.t_len = ((MinimalTuple) a->tuple)->t_len + MINIMAL_TUPLE_OFFSET;
1239 : 17039416 : ltup.t_data = (HeapTupleHeader) ((char *) a->tuple - MINIMAL_TUPLE_OFFSET);
1240 : 17039416 : rtup.t_len = ((MinimalTuple) b->tuple)->t_len + MINIMAL_TUPLE_OFFSET;
1241 : 17039416 : rtup.t_data = (HeapTupleHeader) ((char *) b->tuple - MINIMAL_TUPLE_OFFSET);
1242 : 17039416 : tupDesc = (TupleDesc) base->arg;
1243 : :
1244 [ + + ]: 17039416 : if (sortKey->abbrev_converter)
1245 : : {
1246 : 527875 : attno = sortKey->ssup_attno;
1247 : :
1248 : 527875 : datum1 = heap_getattr(<up, attno, tupDesc, &isnull1);
1249 : 527875 : datum2 = heap_getattr(&rtup, attno, tupDesc, &isnull2);
1250 : :
1251 : 527875 : compare = ApplySortAbbrevFullComparator(datum1, isnull1,
1252 : : datum2, isnull2,
1253 : : sortKey);
1254 [ + + ]: 527875 : if (compare != 0)
1255 : 441346 : return compare;
1256 : : }
1257 : :
1258 : 16598070 : sortKey++;
1259 [ + + ]: 17502077 : for (nkey = 1; nkey < base->nKeys; nkey++, sortKey++)
1260 : : {
1261 : 16708156 : attno = sortKey->ssup_attno;
1262 : :
1263 : 16708156 : datum1 = heap_getattr(<up, attno, tupDesc, &isnull1);
1264 : 16708156 : datum2 = heap_getattr(&rtup, attno, tupDesc, &isnull2);
1265 : :
1266 : 16708156 : compare = ApplySortComparator(datum1, isnull1,
1267 : : datum2, isnull2,
1268 : : sortKey);
1269 [ + + ]: 16708156 : if (compare != 0)
1270 : 15804149 : return compare;
1271 : : }
1272 : :
1273 : 793921 : return 0;
1274 : : }
1275 : :
1276 : : static void
1277 : 545225 : writetup_heap(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
1278 : : {
1279 : 545225 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1280 : 545225 : MinimalTuple tuple = (MinimalTuple) stup->tuple;
1281 : :
1282 : : /* the part of the MinimalTuple we'll write: */
1283 : 545225 : char *tupbody = (char *) tuple + MINIMAL_TUPLE_DATA_OFFSET;
1284 : 545225 : unsigned int tupbodylen = tuple->t_len - MINIMAL_TUPLE_DATA_OFFSET;
1285 : :
1286 : : /* total on-disk footprint: */
1287 : 545225 : unsigned int tuplen = tupbodylen + sizeof(int);
1288 : :
1083 peter@eisentraut.org 1289 : 545225 : LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1290 : 545225 : LogicalTapeWrite(tape, tupbody, tupbodylen);
1239 akorotkov@postgresql 1291 [ + + ]: 545225 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1083 peter@eisentraut.org 1292 : 15000 : LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1239 akorotkov@postgresql 1293 : 545225 : }
1294 : :
1295 : : static void
1296 : 494090 : readtup_heap(Tuplesortstate *state, SortTuple *stup,
1297 : : LogicalTape *tape, unsigned int len)
1298 : : {
1299 : 494090 : unsigned int tupbodylen = len - sizeof(int);
1300 : 494090 : unsigned int tuplen = tupbodylen + MINIMAL_TUPLE_DATA_OFFSET;
1301 : 494090 : MinimalTuple tuple = (MinimalTuple) tuplesort_readtup_alloc(state, tuplen);
1302 : 494090 : char *tupbody = (char *) tuple + MINIMAL_TUPLE_DATA_OFFSET;
1303 : 494090 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1304 : : HeapTupleData htup;
1305 : :
1306 : : /* read in the tuple proper */
1307 : 494090 : tuple->t_len = tuplen;
1308 [ - + - - ]: 494090 : LogicalTapeReadExact(tape, tupbody, tupbodylen);
1309 [ + + ]: 494090 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1310 [ - + - - ]: 23892 : LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
384 peter@eisentraut.org 1311 : 494090 : stup->tuple = tuple;
1312 : : /* set up first-column key value */
1239 akorotkov@postgresql 1313 : 494090 : htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
1314 : 494090 : htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
1315 : 988180 : stup->datum1 = heap_getattr(&htup,
1316 : 494090 : base->sortKeys[0].ssup_attno,
1317 : 494090 : (TupleDesc) base->arg,
1318 : : &stup->isnull1);
1319 : 494090 : }
1320 : :
1321 : : /*
1322 : : * Routines specialized for the CLUSTER case (HeapTuple data, with
1323 : : * comparisons per a btree index definition)
1324 : : */
1325 : :
1326 : : static void
1327 : 6 : removeabbrev_cluster(Tuplesortstate *state, SortTuple *stups, int count)
1328 : : {
1329 : : int i;
1330 : 6 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1331 : 6 : TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
1332 : :
1333 [ + + ]: 61446 : for (i = 0; i < count; i++)
1334 : : {
1335 : : HeapTuple tup;
1336 : :
1337 : 61440 : tup = (HeapTuple) stups[i].tuple;
1338 : 61440 : stups[i].datum1 = heap_getattr(tup,
1339 : 61440 : arg->indexInfo->ii_IndexAttrNumbers[0],
1340 : : arg->tupDesc,
1341 : 61440 : &stups[i].isnull1);
1342 : : }
1343 : 6 : }
1344 : :
1345 : : static int
1346 : 2998472 : comparetup_cluster(const SortTuple *a, const SortTuple *b,
1347 : : Tuplesortstate *state)
1348 : : {
854 john.naylor@postgres 1349 : 2998472 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1350 : 2998472 : SortSupport sortKey = base->sortKeys;
1351 : : int32 compare;
1352 : :
1353 : : /* Compare the leading sort key, if it's simple */
1354 [ + + ]: 2998472 : if (base->haveDatum1)
1355 : : {
1356 : 2992577 : compare = ApplySortComparator(a->datum1, a->isnull1,
1357 : 2992577 : b->datum1, b->isnull1,
1358 : : sortKey);
1359 [ + + ]: 2992577 : if (compare != 0)
1360 : 2922026 : return compare;
1361 : : }
1362 : :
1363 : 76446 : return comparetup_cluster_tiebreak(a, b, state);
1364 : : }
1365 : :
1366 : : static int
1367 : 293567 : comparetup_cluster_tiebreak(const SortTuple *a, const SortTuple *b,
1368 : : Tuplesortstate *state)
1369 : : {
1239 akorotkov@postgresql 1370 : 293567 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1371 : 293567 : TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
1372 : 293567 : SortSupport sortKey = base->sortKeys;
1373 : : HeapTuple ltup;
1374 : : HeapTuple rtup;
1375 : : TupleDesc tupDesc;
1376 : : int nkey;
854 john.naylor@postgres 1377 : 293567 : int32 compare = 0;
1378 : : Datum datum1,
1379 : : datum2;
1380 : : bool isnull1,
1381 : : isnull2;
1382 : :
1239 akorotkov@postgresql 1383 : 293567 : ltup = (HeapTuple) a->tuple;
1384 : 293567 : rtup = (HeapTuple) b->tuple;
1385 : 293567 : tupDesc = arg->tupDesc;
1386 : :
1387 : : /* Compare the leading sort key, if it's simple */
1388 [ + + ]: 293567 : if (base->haveDatum1)
1389 : : {
1390 [ + + ]: 287672 : if (sortKey->abbrev_converter)
1391 : : {
1392 : 72226 : AttrNumber leading = arg->indexInfo->ii_IndexAttrNumbers[0];
1393 : :
1394 : 72226 : datum1 = heap_getattr(ltup, leading, tupDesc, &isnull1);
1395 : 72226 : datum2 = heap_getattr(rtup, leading, tupDesc, &isnull2);
1396 : :
1397 : 72226 : compare = ApplySortAbbrevFullComparator(datum1, isnull1,
1398 : : datum2, isnull2,
1399 : : sortKey);
1400 : : }
1401 [ + + + + ]: 287672 : if (compare != 0 || base->nKeys == 1)
1402 : 73892 : return compare;
1403 : : /* Compare additional columns the hard way */
1404 : 213780 : sortKey++;
1405 : 213780 : nkey = 1;
1406 : : }
1407 : : else
1408 : : {
1409 : : /* Must compare all keys the hard way */
1410 : 5895 : nkey = 0;
1411 : : }
1412 : :
1413 [ + + ]: 219675 : if (arg->indexInfo->ii_Expressions == NULL)
1414 : : {
1415 : : /* If not expression index, just compare the proper heap attrs */
1416 : :
1417 [ + - ]: 296760 : for (; nkey < base->nKeys; nkey++, sortKey++)
1418 : : {
1419 : 296760 : AttrNumber attno = arg->indexInfo->ii_IndexAttrNumbers[nkey];
1420 : :
1421 : 296760 : datum1 = heap_getattr(ltup, attno, tupDesc, &isnull1);
1422 : 296760 : datum2 = heap_getattr(rtup, attno, tupDesc, &isnull2);
1423 : :
1424 : 296760 : compare = ApplySortComparator(datum1, isnull1,
1425 : : datum2, isnull2,
1426 : : sortKey);
1427 [ + + ]: 296760 : if (compare != 0)
1428 : 213780 : return compare;
1429 : : }
1430 : : }
1431 : : else
1432 : : {
1433 : : /*
1434 : : * In the expression index case, compute the whole index tuple and
1435 : : * then compare values. It would perhaps be faster to compute only as
1436 : : * many columns as we need to compare, but that would require
1437 : : * duplicating all the logic in FormIndexDatum.
1438 : : */
1439 : : Datum l_index_values[INDEX_MAX_KEYS];
1440 : : bool l_index_isnull[INDEX_MAX_KEYS];
1441 : : Datum r_index_values[INDEX_MAX_KEYS];
1442 : : bool r_index_isnull[INDEX_MAX_KEYS];
1443 : : TupleTableSlot *ecxt_scantuple;
1444 : :
1445 : : /* Reset context each time to prevent memory leakage */
1446 [ + - ]: 5895 : ResetPerTupleExprContext(arg->estate);
1447 : :
1448 [ + - ]: 5895 : ecxt_scantuple = GetPerTupleExprContext(arg->estate)->ecxt_scantuple;
1449 : :
1450 : 5895 : ExecStoreHeapTuple(ltup, ecxt_scantuple, false);
1451 : 5895 : FormIndexDatum(arg->indexInfo, ecxt_scantuple, arg->estate,
1452 : : l_index_values, l_index_isnull);
1453 : :
1454 : 5895 : ExecStoreHeapTuple(rtup, ecxt_scantuple, false);
1455 : 5895 : FormIndexDatum(arg->indexInfo, ecxt_scantuple, arg->estate,
1456 : : r_index_values, r_index_isnull);
1457 : :
1458 [ + - ]: 6357 : for (; nkey < base->nKeys; nkey++, sortKey++)
1459 : : {
1460 : 6357 : compare = ApplySortComparator(l_index_values[nkey],
1461 : 6357 : l_index_isnull[nkey],
1462 : : r_index_values[nkey],
1463 : 6357 : r_index_isnull[nkey],
1464 : : sortKey);
1465 [ + + ]: 6357 : if (compare != 0)
1466 : 5895 : return compare;
1467 : : }
1468 : : }
1469 : :
1239 akorotkov@postgresql 1470 :UBC 0 : return 0;
1471 : : }
1472 : :
1473 : : static void
1239 akorotkov@postgresql 1474 :CBC 30000 : writetup_cluster(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
1475 : : {
1476 : 30000 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1477 : 30000 : HeapTuple tuple = (HeapTuple) stup->tuple;
1478 : 30000 : unsigned int tuplen = tuple->t_len + sizeof(ItemPointerData) + sizeof(int);
1479 : :
1480 : : /* We need to store t_self, but not other fields of HeapTupleData */
1481 : 30000 : LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1482 : 30000 : LogicalTapeWrite(tape, &tuple->t_self, sizeof(ItemPointerData));
1483 : 30000 : LogicalTapeWrite(tape, tuple->t_data, tuple->t_len);
1484 [ - + ]: 30000 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1239 akorotkov@postgresql 1485 :UBC 0 : LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1239 akorotkov@postgresql 1486 :CBC 30000 : }
1487 : :
1488 : : static void
1489 : 30000 : readtup_cluster(Tuplesortstate *state, SortTuple *stup,
1490 : : LogicalTape *tape, unsigned int tuplen)
1491 : : {
1492 : 30000 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1493 : 30000 : TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
1494 : 30000 : unsigned int t_len = tuplen - sizeof(ItemPointerData) - sizeof(int);
1495 : 30000 : HeapTuple tuple = (HeapTuple) tuplesort_readtup_alloc(state,
1496 : : t_len + HEAPTUPLESIZE);
1497 : :
1498 : : /* Reconstruct the HeapTupleData header */
1499 : 30000 : tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
1500 : 30000 : tuple->t_len = t_len;
1501 [ - + - - ]: 30000 : LogicalTapeReadExact(tape, &tuple->t_self, sizeof(ItemPointerData));
1502 : : /* We don't currently bother to reconstruct t_tableOid */
1503 : 30000 : tuple->t_tableOid = InvalidOid;
1504 : : /* Read in the tuple body */
1505 [ - + - - ]: 30000 : LogicalTapeReadExact(tape, tuple->t_data, tuple->t_len);
1506 [ - + ]: 30000 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1239 akorotkov@postgresql 1507 [ # # # # ]:UBC 0 : LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
384 peter@eisentraut.org 1508 :CBC 30000 : stup->tuple = tuple;
1509 : : /* set up first-column key value, if it's a simple column */
1239 akorotkov@postgresql 1510 [ + - ]: 30000 : if (base->haveDatum1)
1511 : 30000 : stup->datum1 = heap_getattr(tuple,
1512 : 30000 : arg->indexInfo->ii_IndexAttrNumbers[0],
1513 : : arg->tupDesc,
1514 : : &stup->isnull1);
1515 : 30000 : }
1516 : :
1517 : : static void
1518 : 57 : freestate_cluster(Tuplesortstate *state)
1519 : : {
1520 : 57 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1521 : 57 : TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
1522 : :
1523 : : /* Free any execution state created for CLUSTER case */
1524 [ + + ]: 57 : if (arg->estate != NULL)
1525 : : {
1526 [ + - ]: 12 : ExprContext *econtext = GetPerTupleExprContext(arg->estate);
1527 : :
1528 : 12 : ExecDropSingleTupleTableSlot(econtext->ecxt_scantuple);
1529 : 12 : FreeExecutorState(arg->estate);
1530 : : }
1531 : 57 : }
1532 : :
1533 : : /*
1534 : : * Routines specialized for IndexTuple case
1535 : : *
1536 : : * The btree and hash cases require separate comparison functions, but the
1537 : : * IndexTuple representation is the same so the copy/write/read support
1538 : : * functions can be shared.
1539 : : */
1540 : :
1541 : : static void
1542 : 30 : removeabbrev_index(Tuplesortstate *state, SortTuple *stups, int count)
1543 : : {
1544 : 30 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1545 : 30 : TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
1546 : : int i;
1547 : :
1548 [ + + ]: 307230 : for (i = 0; i < count; i++)
1549 : : {
1550 : : IndexTuple tuple;
1551 : :
1552 : 307200 : tuple = stups[i].tuple;
1553 : 307200 : stups[i].datum1 = index_getattr(tuple,
1554 : : 1,
1555 : 307200 : RelationGetDescr(arg->indexRel),
1556 : 307200 : &stups[i].isnull1);
1557 : : }
1558 : 30 : }
1559 : :
1560 : : static int
1561 : 29804678 : comparetup_index_btree(const SortTuple *a, const SortTuple *b,
1562 : : Tuplesortstate *state)
1563 : : {
1564 : : /*
1565 : : * This is similar to comparetup_heap(), but expects index tuples. There
1566 : : * is also special handling for enforcing uniqueness, and special
1567 : : * treatment for equal keys at the end.
1568 : : */
1569 : 29804678 : TuplesortPublic *base = TuplesortstateGetPublic(state);
854 john.naylor@postgres 1570 : 29804678 : SortSupport sortKey = base->sortKeys;
1571 : : int32 compare;
1572 : :
1573 : : /* Compare the leading sort key */
1574 : 29804678 : compare = ApplySortComparator(a->datum1, a->isnull1,
1575 : 29804678 : b->datum1, b->isnull1,
1576 : : sortKey);
1577 [ + + ]: 29804678 : if (compare != 0)
1578 : 26931174 : return compare;
1579 : :
1580 : : /* Compare additional sort keys */
1581 : 2873504 : return comparetup_index_btree_tiebreak(a, b, state);
1582 : : }
1583 : :
1584 : : static int
1585 : 9002538 : comparetup_index_btree_tiebreak(const SortTuple *a, const SortTuple *b,
1586 : : Tuplesortstate *state)
1587 : : {
1588 : 9002538 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1239 akorotkov@postgresql 1589 : 9002538 : TuplesortIndexBTreeArg *arg = (TuplesortIndexBTreeArg *) base->arg;
1590 : 9002538 : SortSupport sortKey = base->sortKeys;
1591 : : IndexTuple tuple1;
1592 : : IndexTuple tuple2;
1593 : : int keysz;
1594 : : TupleDesc tupDes;
1595 : 9002538 : bool equal_hasnull = false;
1596 : : int nkey;
1597 : : int32 compare;
1598 : : Datum datum1,
1599 : : datum2;
1600 : : bool isnull1,
1601 : : isnull2;
1602 : :
1603 : 9002538 : tuple1 = (IndexTuple) a->tuple;
1604 : 9002538 : tuple2 = (IndexTuple) b->tuple;
1605 : 9002538 : keysz = base->nKeys;
1606 : 9002538 : tupDes = RelationGetDescr(arg->index.indexRel);
1607 : :
1608 [ + + ]: 9002538 : if (sortKey->abbrev_converter)
1609 : : {
1610 : 431999 : datum1 = index_getattr(tuple1, 1, tupDes, &isnull1);
1611 : 431999 : datum2 = index_getattr(tuple2, 1, tupDes, &isnull2);
1612 : :
1613 : 431999 : compare = ApplySortAbbrevFullComparator(datum1, isnull1,
1614 : : datum2, isnull2,
1615 : : sortKey);
1616 [ + + ]: 431999 : if (compare != 0)
1617 : 402556 : return compare;
1618 : : }
1619 : :
1620 : : /* they are equal, so we only need to examine one null flag */
1621 [ + + ]: 8599982 : if (a->isnull1)
1622 : 7519 : equal_hasnull = true;
1623 : :
1624 : 8599982 : sortKey++;
1625 [ + + ]: 9904939 : for (nkey = 2; nkey <= keysz; nkey++, sortKey++)
1626 : : {
1627 : 3785230 : datum1 = index_getattr(tuple1, nkey, tupDes, &isnull1);
1628 : 3785230 : datum2 = index_getattr(tuple2, nkey, tupDes, &isnull2);
1629 : :
1630 : 3785230 : compare = ApplySortComparator(datum1, isnull1,
1631 : : datum2, isnull2,
1632 : : sortKey);
1633 [ + + ]: 3785230 : if (compare != 0)
1634 : 2480273 : return compare; /* done when we find unequal attributes */
1635 : :
1636 : : /* they are equal, so we only need to examine one null flag */
1637 [ + + ]: 1304957 : if (isnull1)
1638 : 12996 : equal_hasnull = true;
1639 : : }
1640 : :
1641 : : /*
1642 : : * If btree has asked us to enforce uniqueness, complain if two equal
1643 : : * tuples are detected (unless there was at least one NULL field and NULLS
1644 : : * NOT DISTINCT was not set).
1645 : : *
1646 : : * It is sufficient to make the test here, because if two tuples are equal
1647 : : * they *must* get compared at some stage of the sort --- otherwise the
1648 : : * sort algorithm wouldn't have checked whether one must appear before the
1649 : : * other.
1650 : : */
1651 [ + + + + : 6119709 : if (arg->enforceUnique && !(!arg->uniqueNullsNotDistinct && equal_hasnull))
+ + ]
1652 : : {
1653 : : Datum values[INDEX_MAX_KEYS];
1654 : : bool isnull[INDEX_MAX_KEYS];
1655 : : char *key_desc;
1656 : :
1657 : : /*
1658 : : * Some rather brain-dead implementations of qsort (such as the one in
1659 : : * QNX 4) will sometimes call the comparison routine to compare a
1660 : : * value to itself, but we always use our own implementation, which
1661 : : * does not.
1662 : : */
1663 [ - + ]: 42 : Assert(tuple1 != tuple2);
1664 : :
1665 : 42 : index_deform_tuple(tuple1, tupDes, values, isnull);
1666 : :
1667 : 42 : key_desc = BuildIndexValueDescription(arg->index.indexRel, values, isnull);
1668 : :
1669 [ + - + - ]: 42 : ereport(ERROR,
1670 : : (errcode(ERRCODE_UNIQUE_VIOLATION),
1671 : : errmsg("could not create unique index \"%s\"",
1672 : : RelationGetRelationName(arg->index.indexRel)),
1673 : : key_desc ? errdetail("Key %s is duplicated.", key_desc) :
1674 : : errdetail("Duplicate keys exist."),
1675 : : errtableconstraint(arg->index.heapRel,
1676 : : RelationGetRelationName(arg->index.indexRel))));
1677 : : }
1678 : :
1679 : : /*
1680 : : * If key values are equal, we sort on ItemPointer. This is required for
1681 : : * btree indexes, since heap TID is treated as an implicit last key
1682 : : * attribute in order to ensure that all keys in the index are physically
1683 : : * unique.
1684 : : */
1685 : : {
1686 : 6119667 : BlockNumber blk1 = ItemPointerGetBlockNumber(&tuple1->t_tid);
1687 : 6119667 : BlockNumber blk2 = ItemPointerGetBlockNumber(&tuple2->t_tid);
1688 : :
1689 [ + + ]: 6119667 : if (blk1 != blk2)
1690 [ + + ]: 5606535 : return (blk1 < blk2) ? -1 : 1;
1691 : : }
1692 : : {
1693 : 513132 : OffsetNumber pos1 = ItemPointerGetOffsetNumber(&tuple1->t_tid);
1694 : 513132 : OffsetNumber pos2 = ItemPointerGetOffsetNumber(&tuple2->t_tid);
1695 : :
1696 [ + - ]: 513132 : if (pos1 != pos2)
1697 [ + + ]: 513132 : return (pos1 < pos2) ? -1 : 1;
1698 : : }
1699 : :
1700 : : /* ItemPointer values should never be equal */
1239 akorotkov@postgresql 1701 :UBC 0 : Assert(false);
1702 : :
1703 : : return 0;
1704 : : }
1705 : :
1706 : : static int
1239 akorotkov@postgresql 1707 :CBC 913962 : comparetup_index_hash(const SortTuple *a, const SortTuple *b,
1708 : : Tuplesortstate *state)
1709 : : {
1710 : : Bucket bucket1;
1711 : : Bucket bucket2;
1712 : : uint32 hash1;
1713 : : uint32 hash2;
1714 : : IndexTuple tuple1;
1715 : : IndexTuple tuple2;
1716 : 913962 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1717 : 913962 : TuplesortIndexHashArg *arg = (TuplesortIndexHashArg *) base->arg;
1718 : :
1719 : : /*
1720 : : * Fetch hash keys and mask off bits we don't want to sort by, so that the
1721 : : * initial sort is just on the bucket number. We know that the first
1722 : : * column of the index tuple is the hash key.
1723 : : */
1724 [ - + ]: 913962 : Assert(!a->isnull1);
1725 : 913962 : bucket1 = _hash_hashkey2bucket(DatumGetUInt32(a->datum1),
1726 : : arg->max_buckets, arg->high_mask,
1727 : : arg->low_mask);
1728 [ - + ]: 913962 : Assert(!b->isnull1);
1729 : 913962 : bucket2 = _hash_hashkey2bucket(DatumGetUInt32(b->datum1),
1730 : : arg->max_buckets, arg->high_mask,
1731 : : arg->low_mask);
1732 [ + + ]: 913962 : if (bucket1 > bucket2)
1733 : 270487 : return 1;
1734 [ + + ]: 643475 : else if (bucket1 < bucket2)
1735 : 253917 : return -1;
1736 : :
1737 : : /*
1738 : : * If bucket values are equal, sort by hash values. This allows us to
1739 : : * insert directly onto bucket/overflow pages, where the index tuples are
1740 : : * stored in hash order to allow fast binary search within each page.
1741 : : */
1238 tgl@sss.pgh.pa.us 1742 : 389558 : hash1 = DatumGetUInt32(a->datum1);
1743 : 389558 : hash2 = DatumGetUInt32(b->datum1);
1744 [ + + ]: 389558 : if (hash1 > hash2)
1745 : 97508 : return 1;
1746 [ + + ]: 292050 : else if (hash1 < hash2)
1747 : 87091 : return -1;
1748 : :
1749 : : /*
1750 : : * If hash values are equal, we sort on ItemPointer. This does not affect
1751 : : * validity of the finished index, but it may be useful to have index
1752 : : * scans in physical order.
1753 : : */
1239 akorotkov@postgresql 1754 : 204959 : tuple1 = (IndexTuple) a->tuple;
1755 : 204959 : tuple2 = (IndexTuple) b->tuple;
1756 : :
1757 : : {
1758 : 204959 : BlockNumber blk1 = ItemPointerGetBlockNumber(&tuple1->t_tid);
1759 : 204959 : BlockNumber blk2 = ItemPointerGetBlockNumber(&tuple2->t_tid);
1760 : :
1761 [ + + ]: 204959 : if (blk1 != blk2)
1762 [ + + ]: 136543 : return (blk1 < blk2) ? -1 : 1;
1763 : : }
1764 : : {
1765 : 68416 : OffsetNumber pos1 = ItemPointerGetOffsetNumber(&tuple1->t_tid);
1766 : 68416 : OffsetNumber pos2 = ItemPointerGetOffsetNumber(&tuple2->t_tid);
1767 : :
1768 [ + - ]: 68416 : if (pos1 != pos2)
1769 [ + + ]: 68416 : return (pos1 < pos2) ? -1 : 1;
1770 : : }
1771 : :
1772 : : /* ItemPointer values should never be equal */
1239 akorotkov@postgresql 1773 :UBC 0 : Assert(false);
1774 : :
1775 : : return 0;
1776 : : }
1777 : :
1778 : : /*
1779 : : * Sorting for hash indexes only uses one sort key, so this shouldn't ever be
1780 : : * called. It's only here for consistency.
1781 : : */
1782 : : static int
854 john.naylor@postgres 1783 : 0 : comparetup_index_hash_tiebreak(const SortTuple *a, const SortTuple *b,
1784 : : Tuplesortstate *state)
1785 : : {
1786 : 0 : Assert(false);
1787 : :
1788 : : return 0;
1789 : : }
1790 : :
1791 : : static void
1239 akorotkov@postgresql 1792 :CBC 1852007 : writetup_index(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
1793 : : {
1794 : 1852007 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1795 : 1852007 : IndexTuple tuple = (IndexTuple) stup->tuple;
1796 : : unsigned int tuplen;
1797 : :
1798 : 1852007 : tuplen = IndexTupleSize(tuple) + sizeof(tuplen);
1083 peter@eisentraut.org 1799 : 1852007 : LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1800 : 1852007 : LogicalTapeWrite(tape, tuple, IndexTupleSize(tuple));
1239 akorotkov@postgresql 1801 [ - + ]: 1852007 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1083 peter@eisentraut.org 1802 :UBC 0 : LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1239 akorotkov@postgresql 1803 :CBC 1852007 : }
1804 : :
1805 : : static void
1806 : 1852007 : readtup_index(Tuplesortstate *state, SortTuple *stup,
1807 : : LogicalTape *tape, unsigned int len)
1808 : : {
1809 : 1852007 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1810 : 1852007 : TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
1811 : 1852007 : unsigned int tuplen = len - sizeof(unsigned int);
1812 : 1852007 : IndexTuple tuple = (IndexTuple) tuplesort_readtup_alloc(state, tuplen);
1813 : :
1814 [ - + - - ]: 1852007 : LogicalTapeReadExact(tape, tuple, tuplen);
1815 [ - + ]: 1852007 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1239 akorotkov@postgresql 1816 [ # # # # ]:UBC 0 : LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
384 peter@eisentraut.org 1817 :CBC 1852007 : stup->tuple = tuple;
1818 : : /* set up first-column key value */
1239 akorotkov@postgresql 1819 : 3704014 : stup->datum1 = index_getattr(tuple,
1820 : : 1,
1821 : 1852007 : RelationGetDescr(arg->indexRel),
1822 : : &stup->isnull1);
1823 : 1852007 : }
1824 : :
1825 : : /*
1826 : : * Routines specialized for BrinTuple case
1827 : : */
1828 : :
1829 : : static void
740 tomas.vondra@postgre 1830 :UBC 0 : removeabbrev_index_brin(Tuplesortstate *state, SortTuple *stups, int count)
1831 : : {
1832 : : int i;
1833 : :
1834 [ # # ]: 0 : for (i = 0; i < count; i++)
1835 : : {
1836 : : BrinSortTuple *tuple;
1837 : :
1838 : 0 : tuple = stups[i].tuple;
131 peter@eisentraut.org 1839 :UNC 0 : stups[i].datum1 = UInt32GetDatum(tuple->tuple.bt_blkno);
1840 : : }
740 tomas.vondra@postgre 1841 :UBC 0 : }
1842 : :
1843 : : static int
740 tomas.vondra@postgre 1844 :CBC 151 : comparetup_index_brin(const SortTuple *a, const SortTuple *b,
1845 : : Tuplesortstate *state)
1846 : : {
1847 [ - + ]: 151 : Assert(TuplesortstateGetPublic(state)->haveDatum1);
1848 : :
1849 [ + + ]: 151 : if (DatumGetUInt32(a->datum1) > DatumGetUInt32(b->datum1))
1850 : 41 : return 1;
1851 : :
1852 [ + + ]: 110 : if (DatumGetUInt32(a->datum1) < DatumGetUInt32(b->datum1))
1853 : 78 : return -1;
1854 : :
1855 : : /* silence compilers */
1856 : 32 : return 0;
1857 : : }
1858 : :
1859 : : static void
1860 : 46 : writetup_index_brin(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
1861 : : {
1862 : 46 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1863 : 46 : BrinSortTuple *tuple = (BrinSortTuple *) stup->tuple;
1864 : 46 : unsigned int tuplen = tuple->tuplen;
1865 : :
1866 : 46 : tuplen = tuplen + sizeof(tuplen);
1867 : 46 : LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1868 : 46 : LogicalTapeWrite(tape, &tuple->tuple, tuple->tuplen);
1869 [ - + ]: 46 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
740 tomas.vondra@postgre 1870 :UBC 0 : LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
740 tomas.vondra@postgre 1871 :CBC 46 : }
1872 : :
1873 : : static void
1874 : 46 : readtup_index_brin(Tuplesortstate *state, SortTuple *stup,
1875 : : LogicalTape *tape, unsigned int len)
1876 : : {
1877 : : BrinSortTuple *tuple;
1878 : 46 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1879 : 46 : unsigned int tuplen = len - sizeof(unsigned int);
1880 : :
1881 : : /*
1882 : : * Allocate space for the BRIN sort tuple, which is BrinTuple with an
1883 : : * extra length field.
1884 : : */
1885 : 46 : tuple = (BrinSortTuple *) tuplesort_readtup_alloc(state,
1886 : : BRINSORTTUPLE_SIZE(tuplen));
1887 : :
1888 : 46 : tuple->tuplen = tuplen;
1889 : :
1890 [ - + - - ]: 46 : LogicalTapeReadExact(tape, &tuple->tuple, tuplen);
1891 [ - + ]: 46 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
740 tomas.vondra@postgre 1892 [ # # # # ]:UBC 0 : LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
384 peter@eisentraut.org 1893 :CBC 46 : stup->tuple = tuple;
1894 : :
1895 : : /* set up first-column key value, which is block number */
131 peter@eisentraut.org 1896 :GNC 46 : stup->datum1 = UInt32GetDatum(tuple->tuple.bt_blkno);
740 tomas.vondra@postgre 1897 :CBC 46 : }
1898 : :
1899 : : /*
1900 : : * Routines specialized for GIN case
1901 : : */
1902 : :
1903 : : static void
289 tomas.vondra@postgre 1904 :UBC 0 : removeabbrev_index_gin(Tuplesortstate *state, SortTuple *stups, int count)
1905 : : {
1906 : 0 : Assert(false);
1907 : : elog(ERROR, "removeabbrev_index_gin not implemented");
1908 : : }
1909 : :
1910 : : static int
1911 : 0 : comparetup_index_gin(const SortTuple *a, const SortTuple *b,
1912 : : Tuplesortstate *state)
1913 : : {
1914 : 0 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1915 : :
1916 [ # # ]: 0 : Assert(!TuplesortstateGetPublic(state)->haveDatum1);
1917 : :
1918 : 0 : return _gin_compare_tuples((GinTuple *) a->tuple,
1919 : 0 : (GinTuple *) b->tuple,
1920 : : base->sortKeys);
1921 : : }
1922 : :
1923 : : static void
1924 : 0 : writetup_index_gin(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
1925 : : {
1926 : 0 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1927 : 0 : GinTuple *tuple = (GinTuple *) stup->tuple;
1928 : 0 : unsigned int tuplen = tuple->tuplen;
1929 : :
1930 : 0 : tuplen = tuplen + sizeof(tuplen);
1931 : 0 : LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1932 : 0 : LogicalTapeWrite(tape, tuple, tuple->tuplen);
1933 [ # # ]: 0 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1934 : 0 : LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
1935 : 0 : }
1936 : :
1937 : : static void
1938 : 0 : readtup_index_gin(Tuplesortstate *state, SortTuple *stup,
1939 : : LogicalTape *tape, unsigned int len)
1940 : : {
1941 : : GinTuple *tuple;
1942 : 0 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1943 : 0 : unsigned int tuplen = len - sizeof(unsigned int);
1944 : :
1945 : : /*
1946 : : * Allocate space for the GIN sort tuple, which already has the proper
1947 : : * length included in the header.
1948 : : */
1949 : 0 : tuple = (GinTuple *) tuplesort_readtup_alloc(state, tuplen);
1950 : :
1951 : 0 : tuple->tuplen = tuplen;
1952 : :
1953 [ # # # # ]: 0 : LogicalTapeReadExact(tape, tuple, tuplen);
1954 [ # # ]: 0 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1955 [ # # # # ]: 0 : LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
26 peter@eisentraut.org 1956 :UNC 0 : stup->tuple = tuple;
1957 : :
1958 : : /* no abbreviations (FIXME maybe use attrnum for this?) */
289 tomas.vondra@postgre 1959 :UBC 0 : stup->datum1 = (Datum) 0;
1960 : 0 : }
1961 : :
1962 : : /*
1963 : : * Routines specialized for DatumTuple case
1964 : : */
1965 : :
1966 : : static void
1239 akorotkov@postgresql 1967 :CBC 6 : removeabbrev_datum(Tuplesortstate *state, SortTuple *stups, int count)
1968 : : {
1969 : : int i;
1970 : :
1971 [ + + ]: 61446 : for (i = 0; i < count; i++)
1972 : 61440 : stups[i].datum1 = PointerGetDatum(stups[i].tuple);
1973 : 6 : }
1974 : :
1975 : : static int
1976 : 3442351 : comparetup_datum(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
1977 : : {
1978 : 3442351 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1979 : : int compare;
1980 : :
1981 : 3442351 : compare = ApplySortComparator(a->datum1, a->isnull1,
1982 : 3442351 : b->datum1, b->isnull1,
1983 : : base->sortKeys);
1984 [ + + ]: 3442351 : if (compare != 0)
1985 : 3342222 : return compare;
1986 : :
854 john.naylor@postgres 1987 : 100129 : return comparetup_datum_tiebreak(a, b, state);
1988 : : }
1989 : :
1990 : : static int
1991 : 1351995 : comparetup_datum_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
1992 : : {
1993 : 1351995 : TuplesortPublic *base = TuplesortstateGetPublic(state);
1994 : 1351995 : int32 compare = 0;
1995 : :
1996 : : /* if we have abbreviations, then "tuple" has the original value */
1239 akorotkov@postgresql 1997 [ + + ]: 1351995 : if (base->sortKeys->abbrev_converter)
1998 : 1251878 : compare = ApplySortAbbrevFullComparator(PointerGetDatum(a->tuple), a->isnull1,
1999 : 1251878 : PointerGetDatum(b->tuple), b->isnull1,
2000 : : base->sortKeys);
2001 : :
2002 : 1351995 : return compare;
2003 : : }
2004 : :
2005 : : static void
2006 : 590261 : writetup_datum(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
2007 : : {
2008 : 590261 : TuplesortPublic *base = TuplesortstateGetPublic(state);
2009 : 590261 : TuplesortDatumArg *arg = (TuplesortDatumArg *) base->arg;
2010 : : void *waddr;
2011 : : unsigned int tuplen;
2012 : : unsigned int writtenlen;
2013 : :
2014 [ + + ]: 590261 : if (stup->isnull1)
2015 : : {
2016 : 33 : waddr = NULL;
2017 : 33 : tuplen = 0;
2018 : : }
2019 [ + + ]: 590228 : else if (!base->tuples)
2020 : : {
2021 : 200066 : waddr = &stup->datum1;
2022 : 200066 : tuplen = sizeof(Datum);
2023 : : }
2024 : : else
2025 : : {
2026 : 390162 : waddr = stup->tuple;
2027 : 390162 : tuplen = datumGetSize(PointerGetDatum(stup->tuple), false, arg->datumTypeLen);
2028 [ - + ]: 390162 : Assert(tuplen != 0);
2029 : : }
2030 : :
2031 : 590261 : writtenlen = tuplen + sizeof(unsigned int);
2032 : :
1083 peter@eisentraut.org 2033 : 590261 : LogicalTapeWrite(tape, &writtenlen, sizeof(writtenlen));
1239 akorotkov@postgresql 2034 : 590261 : LogicalTapeWrite(tape, waddr, tuplen);
2035 [ + + ]: 590261 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
1083 peter@eisentraut.org 2036 : 260132 : LogicalTapeWrite(tape, &writtenlen, sizeof(writtenlen));
1239 akorotkov@postgresql 2037 : 590261 : }
2038 : :
2039 : : static void
2040 : 535276 : readtup_datum(Tuplesortstate *state, SortTuple *stup,
2041 : : LogicalTape *tape, unsigned int len)
2042 : : {
2043 : 535276 : TuplesortPublic *base = TuplesortstateGetPublic(state);
2044 : 535276 : unsigned int tuplen = len - sizeof(unsigned int);
2045 : :
2046 [ + + ]: 535276 : if (tuplen == 0)
2047 : : {
2048 : : /* it's NULL */
2049 : 45 : stup->datum1 = (Datum) 0;
2050 : 45 : stup->isnull1 = true;
2051 : 45 : stup->tuple = NULL;
2052 : : }
2053 [ + + ]: 535231 : else if (!base->tuples)
2054 : : {
2055 [ - + ]: 205069 : Assert(tuplen == sizeof(Datum));
2056 [ - + - - ]: 205069 : LogicalTapeReadExact(tape, &stup->datum1, tuplen);
2057 : 205069 : stup->isnull1 = false;
2058 : 205069 : stup->tuple = NULL;
2059 : : }
2060 : : else
2061 : : {
2062 : 330162 : void *raddr = tuplesort_readtup_alloc(state, tuplen);
2063 : :
2064 [ - + - - ]: 330162 : LogicalTapeReadExact(tape, raddr, tuplen);
2065 : 330162 : stup->datum1 = PointerGetDatum(raddr);
2066 : 330162 : stup->isnull1 = false;
2067 : 330162 : stup->tuple = raddr;
2068 : : }
2069 : :
2070 [ + + ]: 535276 : if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
2071 [ - + - - ]: 265156 : LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
2072 : 535276 : }
|