From 3a0e1fa7c7e4da46a86f7d5b9dd0392549f3b460 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Wed, 22 Jun 2022 18:11:26 +0300 Subject: [PATCH v2 5/6] Reorganize data structures Reported-by: Bug: Discussion: Author: Reviewed-by: Tested-by: Backpatch-through: --- src/backend/utils/sort/tuplesort.c | 762 ++++++++++++++++------------- 1 file changed, 432 insertions(+), 330 deletions(-) diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index 3bf990a1b34..e106e1ff9e2 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -126,8 +126,8 @@ #define CLUSTER_SORT 3 /* Sort parallel code from state for sort__start probes */ -#define PARALLEL_SORT(state) ((state)->shared == NULL ? 0 : \ - (state)->worker >= 0 ? 1 : 2) +#define PARALLEL_SORT(coordinate) ((coordinate)->sharedsort == NULL ? 0 : \ + (coordinate)->isWorker >= 0 ? 1 : 2) /* * Initial size of memtuples array. We're trying to select this size so that @@ -236,37 +236,17 @@ typedef enum #define TAPE_BUFFER_OVERHEAD BLCKSZ #define MERGE_BUFFER_SIZE (BLCKSZ * 32) +typedef struct TuplesortOps TuplesortOps; + typedef int (*SortTupleComparator) (const SortTuple *a, const SortTuple *b, Tuplesortstate *state); -/* - * Private state of a Tuplesort operation. - */ -struct Tuplesortstate +struct TuplesortOps { - TupSortStatus status; /* enumerated value as shown above */ - int nKeys; /* number of columns in sort key */ - int sortopt; /* Bitmask of flags used to setup sort */ - bool bounded; /* did caller specify a maximum number of - * tuples to return? */ - bool boundUsed; /* true if we made use of a bounded heap */ - int bound; /* if bounded, the maximum number of tuples */ - bool tuples; /* Can SortTuple.tuple ever be set? */ - int64 availMem; /* remaining memory available, in bytes */ - int64 allowedMem; /* total memory allowed, in bytes */ - int maxTapes; /* max number of input tapes to merge in each - * pass */ - int64 maxSpace; /* maximum amount of space occupied among sort - * of groups, either in-memory or on-disk */ - bool isMaxSpaceDisk; /* true when maxSpace is value for on-disk - * space, false when it's value for in-memory - * space */ - TupSortStatus maxSpaceStatus; /* sort status when maxSpace was reached */ MemoryContext maincontext; /* memory context for tuple sort metadata that * persists across multiple batches */ MemoryContext sortcontext; /* memory context holding most sort data */ MemoryContext tuplecontext; /* sub-context of sortcontext for tuple data */ - LogicalTapeSet *tapeset; /* logtape.c object for tapes in a temp file */ /* * These function pointers decouple the routines that must know what kind @@ -300,12 +280,116 @@ struct Tuplesortstate void (*readtup) (Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len); + void (*freestate) (Tuplesortstate *state); + /* * Whether SortTuple's datum1 and isnull1 members are maintained by the * above routines. If not, some sort specializations are disabled. */ bool haveDatum1; + /* + * The sortKeys variable is used by every case other than the hash index + * case; it is set by tuplesort_begin_xxx. tupDesc is only used by the + * MinimalTuple and CLUSTER routines, though. + */ + int nKeys; /* number of columns in sort key */ + SortSupport sortKeys; /* array of length nKeys */ + + /* + * This variable is shared by the single-key MinimalTuple case and the + * Datum case (which both use qsort_ssup()). Otherwise, it's NULL. The + * presence of a value in this field is also checked by various sort + * specialization functions as an optimization when comparing the leading + * key in a tiebreak situation to determine if there are any subsequent + * keys to sort on. + */ + SortSupport onlyKey; + + int sortopt; /* Bitmask of flags used to setup sort */ + + bool tuples; /* Can SortTuple.tuple ever be set? */ + + void *arg; +}; + +typedef struct +{ + TupleDesc tupDesc; + + /* + * These variables are specific to the CLUSTER case; they are set by + * tuplesort_begin_cluster. + */ + IndexInfo *indexInfo; /* info about index being used for reference */ + EState *estate; /* for evaluating index expressions */ +} TupleSortClusterArg; + +typedef struct +{ + /* + * These variables are specific to the IndexTuple case; they are set by + * tuplesort_begin_index_xxx and used only by the IndexTuple routines. + */ + Relation heapRel; /* table the index is being built on */ + Relation indexRel; /* index being built */ +} TupleSortIndexArg; + +typedef struct +{ + TupleSortIndexArg index; + + /* These are specific to the index_btree subcase: */ + bool enforceUnique; /* complain if we find duplicate tuples */ + bool uniqueNullsNotDistinct; /* unique constraint null treatment */ +} TupleSortIndexBTreeArg; + +typedef struct +{ + TupleSortIndexArg index; + + /* These are specific to the index_hash subcase: */ + uint32 high_mask; /* masks for sortable part of hash code */ + uint32 low_mask; + uint32 max_buckets; +} TupleSortIndexHashArg; + +typedef struct +{ + TupleSortIndexArg index; + + /* + * These variables are specific to the Datum case; they are set by + * tuplesort_begin_datum and used only by the DatumTuple routines. + */ + Oid datumType; + /* we need typelen in order to know how to copy the Datums. */ + int datumTypeLen; +} TupleSortDatumArg; + +/* + * Private state of a Tuplesort operation. + */ +struct Tuplesortstate +{ + TuplesortOps ops; + TupSortStatus status; /* enumerated value as shown above */ + bool bounded; /* did caller specify a maximum number of + * tuples to return? */ + bool boundUsed; /* true if we made use of a bounded heap */ + int bound; /* if bounded, the maximum number of tuples */ + int64 availMem; /* remaining memory available, in bytes */ + int64 allowedMem; /* total memory allowed, in bytes */ + int maxTapes; /* max number of input tapes to merge in each + * pass */ + int64 maxSpace; /* maximum amount of space occupied among sort + * of groups, either in-memory or on-disk */ + bool isMaxSpaceDisk; /* true when maxSpace is value for on-disk + * space, false when it's value for in-memory + * space */ + TupSortStatus maxSpaceStatus; /* sort status when maxSpace was reached */ + LogicalTapeSet *tapeset; /* logtape.c object for tapes in a temp file */ + /* * This array holds the tuples now in sort memory. If we are in state * INITIAL, the tuples are in no particular order; if we are in state @@ -420,24 +504,6 @@ struct Tuplesortstate Sharedsort *shared; int nParticipants; - /* - * The sortKeys variable is used by every case other than the hash index - * case; it is set by tuplesort_begin_xxx. tupDesc is only used by the - * MinimalTuple and CLUSTER routines, though. - */ - TupleDesc tupDesc; - SortSupport sortKeys; /* array of length nKeys */ - - /* - * This variable is shared by the single-key MinimalTuple case and the - * Datum case (which both use qsort_ssup()). Otherwise, it's NULL. The - * presence of a value in this field is also checked by various sort - * specialization functions as an optimization when comparing the leading - * key in a tiebreak situation to determine if there are any subsequent - * keys to sort on. - */ - SortSupport onlyKey; - /* * Additional state for managing "abbreviated key" sortsupport routines * (which currently may be used by all cases except the hash index case). @@ -447,37 +513,6 @@ struct Tuplesortstate int64 abbrevNext; /* Tuple # at which to next check * applicability */ - /* - * These variables are specific to the CLUSTER case; they are set by - * tuplesort_begin_cluster. - */ - IndexInfo *indexInfo; /* info about index being used for reference */ - EState *estate; /* for evaluating index expressions */ - - /* - * These variables are specific to the IndexTuple case; they are set by - * tuplesort_begin_index_xxx and used only by the IndexTuple routines. - */ - Relation heapRel; /* table the index is being built on */ - Relation indexRel; /* index being built */ - - /* These are specific to the index_btree subcase: */ - bool enforceUnique; /* complain if we find duplicate tuples */ - bool uniqueNullsNotDistinct; /* unique constraint null treatment */ - - /* These are specific to the index_hash subcase: */ - uint32 high_mask; /* masks for sortable part of hash code */ - uint32 low_mask; - uint32 max_buckets; - - /* - * These variables are specific to the Datum case; they are set by - * tuplesort_begin_datum and used only by the DatumTuple routines. - */ - Oid datumType; - /* we need typelen in order to know how to copy the Datums. */ - int datumTypeLen; - /* * Resource snapshot for time of sort start. */ @@ -542,10 +577,13 @@ struct Sharedsort pfree(buf); \ } while(0) -#define GETDATUM1(state,stup) ((*(state)->getdatum1) (state, stup)) -#define COMPARETUP(state,a,b) ((*(state)->comparetup) (a, b, state)) -#define WRITETUP(state,tape,stup) ((*(state)->writetup) (state, tape, stup)) -#define READTUP(state,stup,tape,len) ((*(state)->readtup) (state, stup, tape, len)) +#define TuplesortstateGetOps(state) ((TuplesortOps *) state); + +#define GETDATUM1(state,stup) ((*(state)->ops.getdatum1) (state, stup)) +#define COMPARETUP(state,a,b) ((*(state)->ops.comparetup) (a, b, state)) +#define WRITETUP(state,tape,stup) ((*(state)->ops.writetup) (state, tape, stup)) +#define READTUP(state,stup,tape,len) ((*(state)->ops.readtup) (state, stup, tape, len)) +#define FREESTATE(state) ((state)->ops.freestate ? (*(state)->ops.freestate) (state) : (void) 0) #define LACKMEM(state) ((state)->availMem < 0 && !(state)->slabAllocatorUsed) #define USEMEM(state,amt) ((state)->availMem -= (amt)) #define FREEMEM(state,amt) ((state)->availMem += (amt)) @@ -664,6 +702,7 @@ static void writetup_datum(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup); static void readtup_datum(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len); +static void freestate_cluster(Tuplesortstate *state); static int worker_get_identifier(Tuplesortstate *state); static void worker_freeze_result_tape(Tuplesortstate *state); static void worker_nomergeruns(Tuplesortstate *state); @@ -694,7 +733,7 @@ qsort_tuple_unsigned_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state) compare = ApplyUnsignedSortComparator(a->datum1, a->isnull1, b->datum1, b->isnull1, - &state->sortKeys[0]); + &state->ops.sortKeys[0]); if (compare != 0) return compare; @@ -702,10 +741,10 @@ qsort_tuple_unsigned_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state) * No need to waste effort calling the tiebreak function when there are no * other keys to sort on. */ - if (state->onlyKey != NULL) + if (state->ops.onlyKey != NULL) return 0; - return state->comparetup(a, b, state); + return state->ops.comparetup(a, b, state); } #if SIZEOF_DATUM >= 8 @@ -717,7 +756,7 @@ qsort_tuple_signed_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state) compare = ApplySignedSortComparator(a->datum1, a->isnull1, b->datum1, b->isnull1, - &state->sortKeys[0]); + &state->ops.sortKeys[0]); if (compare != 0) return compare; @@ -726,10 +765,10 @@ qsort_tuple_signed_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state) * No need to waste effort calling the tiebreak function when there are no * other keys to sort on. */ - if (state->onlyKey != NULL) + if (state->ops.onlyKey != NULL) return 0; - return state->comparetup(a, b, state); + return state->ops.comparetup(a, b, state); } #endif @@ -741,7 +780,7 @@ qsort_tuple_int32_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state) compare = ApplyInt32SortComparator(a->datum1, a->isnull1, b->datum1, b->isnull1, - &state->sortKeys[0]); + &state->ops.sortKeys[0]); if (compare != 0) return compare; @@ -750,10 +789,10 @@ qsort_tuple_int32_compare(SortTuple *a, SortTuple *b, Tuplesortstate *state) * No need to waste effort calling the tiebreak function when there are no * other keys to sort on. */ - if (state->onlyKey != NULL) + if (state->ops.onlyKey != NULL) return 0; - return state->comparetup(a, b, state); + return state->ops.comparetup(a, b, state); } /* @@ -880,8 +919,9 @@ tuplesort_begin_common(int workMem, SortCoordinate coordinate, int sortopt) pg_rusage_init(&state->ru_start); #endif - state->sortopt = sortopt; - state->tuples = true; + state->ops.sortopt = sortopt; + state->ops.tuples = true; + state->abbrevNext = 10; /* * workMem is forced to be at least 64KB, the current minimum valid value @@ -890,8 +930,8 @@ tuplesort_begin_common(int workMem, SortCoordinate coordinate, int sortopt) * with very little memory. */ state->allowedMem = Max(workMem, 64) * (int64) 1024; - state->sortcontext = sortcontext; - state->maincontext = maincontext; + state->ops.sortcontext = sortcontext; + state->ops.maincontext = maincontext; /* * Initial size of array must be more than ALLOCSET_SEPARATE_THRESHOLD; @@ -950,7 +990,7 @@ tuplesort_begin_batch(Tuplesortstate *state) { MemoryContext oldcontext; - oldcontext = MemoryContextSwitchTo(state->maincontext); + oldcontext = MemoryContextSwitchTo(state->ops.maincontext); /* * Caller tuple (e.g. IndexTuple) memory context. @@ -965,12 +1005,12 @@ tuplesort_begin_batch(Tuplesortstate *state) * generation.c context as this keeps allocations more compact with less * wastage. Allocations are also slightly more CPU efficient. */ - if (state->sortopt & TUPLESORT_ALLOWBOUNDED) - state->tuplecontext = AllocSetContextCreate(state->sortcontext, + if (state->ops.sortopt & TUPLESORT_ALLOWBOUNDED) + state->ops.tuplecontext = AllocSetContextCreate(state->ops.sortcontext, "Caller tuples", ALLOCSET_DEFAULT_SIZES); else - state->tuplecontext = GenerationContextCreate(state->sortcontext, + state->ops.tuplecontext = GenerationContextCreate(state->ops.sortcontext, "Caller tuples", ALLOCSET_DEFAULT_SIZES); @@ -1028,10 +1068,11 @@ tuplesort_begin_heap(TupleDesc tupDesc, { Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, sortopt); + TuplesortOps *ops = TuplesortstateGetOps(state); MemoryContext oldcontext; int i; - oldcontext = MemoryContextSwitchTo(state->maincontext); + oldcontext = MemoryContextSwitchTo(state->ops.maincontext); AssertArg(nkeys > 0); @@ -1042,30 +1083,28 @@ tuplesort_begin_heap(TupleDesc tupDesc, nkeys, workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f'); #endif - state->nKeys = nkeys; + ops->nKeys = nkeys; TRACE_POSTGRESQL_SORT_START(HEAP_SORT, false, /* no unique check */ nkeys, workMem, sortopt & TUPLESORT_RANDOMACCESS, - PARALLEL_SORT(state)); + PARALLEL_SORT(coordinate)); - state->getdatum1 = getdatum1_heap; - state->comparetup = comparetup_heap; - state->writetup = writetup_heap; - state->readtup = readtup_heap; - state->haveDatum1 = true; - - state->tupDesc = tupDesc; /* assume we need not copy tupDesc */ - state->abbrevNext = 10; + ops->getdatum1 = getdatum1_heap; + ops->comparetup = comparetup_heap; + ops->writetup = writetup_heap; + ops->readtup = readtup_heap; + ops->haveDatum1 = true; + ops->arg = tupDesc; /* assume we need not copy tupDesc */ /* Prepare SortSupport data for each column */ - state->sortKeys = (SortSupport) palloc0(nkeys * sizeof(SortSupportData)); + ops->sortKeys = (SortSupport) palloc0(nkeys * sizeof(SortSupportData)); for (i = 0; i < nkeys; i++) { - SortSupport sortKey = state->sortKeys + i; + SortSupport sortKey = ops->sortKeys + i; AssertArg(attNums[i] != 0); AssertArg(sortOperators[i] != 0); @@ -1075,7 +1114,7 @@ tuplesort_begin_heap(TupleDesc tupDesc, sortKey->ssup_nulls_first = nullsFirstFlags[i]; sortKey->ssup_attno = attNums[i]; /* Convey if abbreviation optimization is applicable in principle */ - sortKey->abbreviate = (i == 0 && state->haveDatum1); + sortKey->abbreviate = (i == 0 && ops->haveDatum1); PrepareSortSupportFromOrderingOp(sortOperators[i], sortKey); } @@ -1086,8 +1125,8 @@ tuplesort_begin_heap(TupleDesc tupDesc, * is only of value to pass-by-value types anyway, whereas abbreviated * keys are typically only of value to pass-by-reference types. */ - if (nkeys == 1 && !state->sortKeys->abbrev_converter) - state->onlyKey = state->sortKeys; + if (nkeys == 1 && !ops->sortKeys->abbrev_converter) + ops->onlyKey = ops->sortKeys; MemoryContextSwitchTo(oldcontext); @@ -1102,13 +1141,16 @@ tuplesort_begin_cluster(TupleDesc tupDesc, { Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, sortopt); + TuplesortOps *ops = TuplesortstateGetOps(state); BTScanInsert indexScanKey; MemoryContext oldcontext; + TupleSortClusterArg *arg; int i; Assert(indexRel->rd_rel->relam == BTREE_AM_OID); - oldcontext = MemoryContextSwitchTo(state->maincontext); + oldcontext = MemoryContextSwitchTo(state->ops.maincontext); + arg = (TupleSortClusterArg *) palloc0(sizeof(TupleSortClusterArg)); #ifdef TRACE_SORT if (trace_sort) @@ -1118,37 +1160,38 @@ tuplesort_begin_cluster(TupleDesc tupDesc, workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f'); #endif - state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); + ops->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); TRACE_POSTGRESQL_SORT_START(CLUSTER_SORT, false, /* no unique check */ - state->nKeys, + ops->nKeys, workMem, sortopt & TUPLESORT_RANDOMACCESS, - PARALLEL_SORT(state)); + PARALLEL_SORT(coordinate)); - state->getdatum1 = getdatum1_cluster; - state->comparetup = comparetup_cluster; - state->writetup = writetup_cluster; - state->readtup = readtup_cluster; - state->abbrevNext = 10; + ops->getdatum1 = getdatum1_cluster; + ops->comparetup = comparetup_cluster; + ops->writetup = writetup_cluster; + ops->readtup = readtup_cluster; + ops->freestate = freestate_cluster; + ops->arg = arg; - state->indexInfo = BuildIndexInfo(indexRel); + arg->indexInfo = BuildIndexInfo(indexRel); /* * If we don't have a simple leading attribute, we don't currently * initialize datum1, so disable optimizations that require it. */ - if (state->indexInfo->ii_IndexAttrNumbers[0] == 0) - state->haveDatum1 = false; + if (arg->indexInfo->ii_IndexAttrNumbers[0] == 0) + ops->haveDatum1 = false; else - state->haveDatum1 = true; + ops->haveDatum1 = true; - state->tupDesc = tupDesc; /* assume we need not copy tupDesc */ + arg->tupDesc = tupDesc; /* assume we need not copy tupDesc */ indexScanKey = _bt_mkscankey(indexRel, NULL); - if (state->indexInfo->ii_Expressions != NULL) + if (arg->indexInfo->ii_Expressions != NULL) { TupleTableSlot *slot; ExprContext *econtext; @@ -1159,19 +1202,19 @@ tuplesort_begin_cluster(TupleDesc tupDesc, * TupleTableSlot to put the table tuples into. The econtext's * scantuple has to point to that slot, too. */ - state->estate = CreateExecutorState(); + arg->estate = CreateExecutorState(); slot = MakeSingleTupleTableSlot(tupDesc, &TTSOpsHeapTuple); - econtext = GetPerTupleExprContext(state->estate); + econtext = GetPerTupleExprContext(arg->estate); econtext->ecxt_scantuple = slot; } /* Prepare SortSupport data for each column */ - state->sortKeys = (SortSupport) palloc0(state->nKeys * + ops->sortKeys = (SortSupport) palloc0(ops->nKeys * sizeof(SortSupportData)); - for (i = 0; i < state->nKeys; i++) + for (i = 0; i < ops->nKeys; i++) { - SortSupport sortKey = state->sortKeys + i; + SortSupport sortKey = ops->sortKeys + i; ScanKey scanKey = indexScanKey->scankeys + i; int16 strategy; @@ -1181,7 +1224,7 @@ tuplesort_begin_cluster(TupleDesc tupDesc, (scanKey->sk_flags & SK_BT_NULLS_FIRST) != 0; sortKey->ssup_attno = scanKey->sk_attno; /* Convey if abbreviation optimization is applicable in principle */ - sortKey->abbreviate = (i == 0 && state->haveDatum1); + sortKey->abbreviate = (i == 0 && ops->haveDatum1); AssertState(sortKey->ssup_attno != 0); @@ -1209,11 +1252,14 @@ tuplesort_begin_index_btree(Relation heapRel, { Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, sortopt); + TuplesortOps *ops = TuplesortstateGetOps(state); BTScanInsert indexScanKey; + TupleSortIndexBTreeArg *arg; MemoryContext oldcontext; int i; - oldcontext = MemoryContextSwitchTo(state->maincontext); + oldcontext = MemoryContextSwitchTo(state->ops.maincontext); + arg = (TupleSortIndexBTreeArg *) palloc(sizeof(TupleSortIndexBTreeArg)); #ifdef TRACE_SORT if (trace_sort) @@ -1223,36 +1269,36 @@ tuplesort_begin_index_btree(Relation heapRel, workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f'); #endif - state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); + ops->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); TRACE_POSTGRESQL_SORT_START(INDEX_SORT, enforceUnique, state->nKeys, workMem, sortopt & TUPLESORT_RANDOMACCESS, - PARALLEL_SORT(state)); + PARALLEL_SORT(coordinate)); - state->getdatum1 = getdatum1_index; - state->comparetup = comparetup_index_btree; - state->writetup = writetup_index; - state->readtup = readtup_index; - state->abbrevNext = 10; - state->haveDatum1 = true; + ops->getdatum1 = getdatum1_index; + ops->comparetup = comparetup_index_btree; + ops->writetup = writetup_index; + ops->readtup = readtup_index; + ops->haveDatum1 = true; + ops->arg = arg; - state->heapRel = heapRel; - state->indexRel = indexRel; - state->enforceUnique = enforceUnique; - state->uniqueNullsNotDistinct = uniqueNullsNotDistinct; + arg->index.heapRel = heapRel; + arg->index.indexRel = indexRel; + arg->enforceUnique = enforceUnique; + arg->uniqueNullsNotDistinct = uniqueNullsNotDistinct; indexScanKey = _bt_mkscankey(indexRel, NULL); /* Prepare SortSupport data for each column */ - state->sortKeys = (SortSupport) palloc0(state->nKeys * - sizeof(SortSupportData)); + ops->sortKeys = (SortSupport) palloc0(ops->nKeys * + sizeof(SortSupportData)); - for (i = 0; i < state->nKeys; i++) + for (i = 0; i < ops->nKeys; i++) { - SortSupport sortKey = state->sortKeys + i; + SortSupport sortKey = ops->sortKeys + i; ScanKey scanKey = indexScanKey->scankeys + i; int16 strategy; @@ -1262,7 +1308,7 @@ tuplesort_begin_index_btree(Relation heapRel, (scanKey->sk_flags & SK_BT_NULLS_FIRST) != 0; sortKey->ssup_attno = scanKey->sk_attno; /* Convey if abbreviation optimization is applicable in principle */ - sortKey->abbreviate = (i == 0 && state->haveDatum1); + sortKey->abbreviate = (i == 0 && ops->haveDatum1); AssertState(sortKey->ssup_attno != 0); @@ -1291,9 +1337,12 @@ tuplesort_begin_index_hash(Relation heapRel, { Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, sortopt); + TuplesortOps *ops = TuplesortstateGetOps(state); MemoryContext oldcontext; + TupleSortIndexHashArg *arg; - oldcontext = MemoryContextSwitchTo(state->maincontext); + oldcontext = MemoryContextSwitchTo(state->ops.maincontext); + arg = (TupleSortIndexHashArg *) palloc(sizeof(TupleSortIndexHashArg)); #ifdef TRACE_SORT if (trace_sort) @@ -1307,20 +1356,21 @@ tuplesort_begin_index_hash(Relation heapRel, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f'); #endif - state->nKeys = 1; /* Only one sort column, the hash code */ + ops->nKeys = 1; /* Only one sort column, the hash code */ - state->getdatum1 = getdatum1_index; - state->comparetup = comparetup_index_hash; - state->writetup = writetup_index; - state->readtup = readtup_index; - state->haveDatum1 = true; + ops->getdatum1 = getdatum1_index; + ops->comparetup = comparetup_index_hash; + ops->writetup = writetup_index; + ops->readtup = readtup_index; + ops->haveDatum1 = true; + ops->arg = arg; - state->heapRel = heapRel; - state->indexRel = indexRel; + arg->index.heapRel = heapRel; + arg->index.indexRel = indexRel; - state->high_mask = high_mask; - state->low_mask = low_mask; - state->max_buckets = max_buckets; + arg->high_mask = high_mask; + arg->low_mask = low_mask; + arg->max_buckets = max_buckets; MemoryContextSwitchTo(oldcontext); @@ -1336,10 +1386,13 @@ tuplesort_begin_index_gist(Relation heapRel, { Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, sortopt); + TuplesortOps *ops = TuplesortstateGetOps(state); MemoryContext oldcontext; + TupleSortIndexBTreeArg *arg; int i; - oldcontext = MemoryContextSwitchTo(state->sortcontext); + oldcontext = MemoryContextSwitchTo(state->ops.maincontext); + arg = (TupleSortIndexBTreeArg *) palloc(sizeof(TupleSortIndexBTreeArg)); #ifdef TRACE_SORT if (trace_sort) @@ -1348,31 +1401,34 @@ tuplesort_begin_index_gist(Relation heapRel, workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f'); #endif - state->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); + ops->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel); - state->getdatum1 = getdatum1_index; - state->comparetup = comparetup_index_btree; - state->writetup = writetup_index; - state->readtup = readtup_index; - state->haveDatum1 = true; + ops->getdatum1 = getdatum1_index; + ops->comparetup = comparetup_index_btree; + ops->writetup = writetup_index; + ops->readtup = readtup_index; + ops->haveDatum1 = true; + ops->arg = arg; - state->heapRel = heapRel; - state->indexRel = indexRel; + arg->index.heapRel = heapRel; + arg->index.indexRel = indexRel; + arg->enforceUnique = false; + arg->uniqueNullsNotDistinct = false; /* Prepare SortSupport data for each column */ - state->sortKeys = (SortSupport) palloc0(state->nKeys * - sizeof(SortSupportData)); + ops->sortKeys = (SortSupport) palloc0(ops->nKeys * + sizeof(SortSupportData)); - for (i = 0; i < state->nKeys; i++) + for (i = 0; i < ops->nKeys; i++) { - SortSupport sortKey = state->sortKeys + i; + SortSupport sortKey = ops->sortKeys + i; sortKey->ssup_cxt = CurrentMemoryContext; sortKey->ssup_collation = indexRel->rd_indcollation[i]; sortKey->ssup_nulls_first = false; sortKey->ssup_attno = i + 1; /* Convey if abbreviation optimization is applicable in principle */ - sortKey->abbreviate = (i == 0 && state->haveDatum1); + sortKey->abbreviate = (i == 0 && ops->haveDatum1); AssertState(sortKey->ssup_attno != 0); @@ -1392,11 +1448,14 @@ tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, { Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate, sortopt); + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortDatumArg *arg; MemoryContext oldcontext; int16 typlen; bool typbyval; - oldcontext = MemoryContextSwitchTo(state->maincontext); + oldcontext = MemoryContextSwitchTo(state->ops.maincontext); + arg = (TupleSortDatumArg *) palloc(sizeof(TupleSortDatumArg)); #ifdef TRACE_SORT if (trace_sort) @@ -1405,35 +1464,36 @@ tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f'); #endif - state->nKeys = 1; /* always a one-column sort */ + ops->nKeys = 1; /* always a one-column sort */ TRACE_POSTGRESQL_SORT_START(DATUM_SORT, false, /* no unique check */ 1, workMem, sortopt & TUPLESORT_RANDOMACCESS, - PARALLEL_SORT(state)); + PARALLEL_SORT(coordinate)); - state->getdatum1 = getdatum1_datum; - state->comparetup = comparetup_datum; - state->writetup = writetup_datum; - state->readtup = readtup_datum; + ops->getdatum1 = getdatum1_datum; + ops->comparetup = comparetup_datum; + ops->writetup = writetup_datum; + ops->readtup = readtup_datum; state->abbrevNext = 10; - state->haveDatum1 = true; + ops->haveDatum1 = true; + ops->arg = arg; - state->datumType = datumType; + arg->datumType = datumType; /* lookup necessary attributes of the datum type */ get_typlenbyval(datumType, &typlen, &typbyval); - state->datumTypeLen = typlen; - state->tuples = !typbyval; + arg->datumTypeLen = typlen; + ops->tuples = !typbyval; /* Prepare SortSupport data */ - state->sortKeys = (SortSupport) palloc0(sizeof(SortSupportData)); + ops->sortKeys = (SortSupport) palloc0(sizeof(SortSupportData)); - state->sortKeys->ssup_cxt = CurrentMemoryContext; - state->sortKeys->ssup_collation = sortCollation; - state->sortKeys->ssup_nulls_first = nullsFirstFlag; + ops->sortKeys->ssup_cxt = CurrentMemoryContext; + ops->sortKeys->ssup_collation = sortCollation; + ops->sortKeys->ssup_nulls_first = nullsFirstFlag; /* * Abbreviation is possible here only for by-reference types. In theory, @@ -1443,9 +1503,9 @@ tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, * can't, because a datum sort only stores a single copy of the datum; the * "tuple" field of each SortTuple is NULL. */ - state->sortKeys->abbreviate = !typbyval; + ops->sortKeys->abbreviate = !typbyval; - PrepareSortSupportFromOrderingOp(sortOperator, state->sortKeys); + PrepareSortSupportFromOrderingOp(sortOperator, ops->sortKeys); /* * The "onlyKey" optimization cannot be used with abbreviated keys, since @@ -1453,8 +1513,8 @@ tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation, * is only of value to pass-by-value types anyway, whereas abbreviated * keys are typically only of value to pass-by-reference types. */ - if (!state->sortKeys->abbrev_converter) - state->onlyKey = state->sortKeys; + if (!ops->sortKeys->abbrev_converter) + ops->onlyKey = ops->sortKeys; MemoryContextSwitchTo(oldcontext); @@ -1479,7 +1539,7 @@ tuplesort_set_bound(Tuplesortstate *state, int64 bound) /* Assert we're called before loading any tuples */ Assert(state->status == TSS_INITIAL && state->memtupcount == 0); /* Assert we allow bounded sorts */ - Assert(state->sortopt & TUPLESORT_ALLOWBOUNDED); + Assert(state->ops.sortopt & TUPLESORT_ALLOWBOUNDED); /* Can't set the bound twice, either */ Assert(!state->bounded); /* Also, this shouldn't be called in a parallel worker */ @@ -1507,13 +1567,13 @@ tuplesort_set_bound(Tuplesortstate *state, int64 bound) * optimization. Disable by setting state to be consistent with no * abbreviation support. */ - state->sortKeys->abbrev_converter = NULL; - if (state->sortKeys->abbrev_full_comparator) - state->sortKeys->comparator = state->sortKeys->abbrev_full_comparator; + state->ops.sortKeys->abbrev_converter = NULL; + if (state->ops.sortKeys->abbrev_full_comparator) + state->ops.sortKeys->comparator = state->ops.sortKeys->abbrev_full_comparator; /* Not strictly necessary, but be tidy */ - state->sortKeys->abbrev_abort = NULL; - state->sortKeys->abbrev_full_comparator = NULL; + state->ops.sortKeys->abbrev_abort = NULL; + state->ops.sortKeys->abbrev_full_comparator = NULL; } /* @@ -1536,7 +1596,7 @@ static void tuplesort_free(Tuplesortstate *state) { /* context swap probably not needed, but let's be safe */ - MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); #ifdef TRACE_SORT long spaceUsed; @@ -1583,21 +1643,13 @@ tuplesort_free(Tuplesortstate *state) TRACE_POSTGRESQL_SORT_DONE(state->tapeset != NULL, 0L); #endif - /* Free any execution state created for CLUSTER case */ - if (state->estate != NULL) - { - ExprContext *econtext = GetPerTupleExprContext(state->estate); - - ExecDropSingleTupleTableSlot(econtext->ecxt_scantuple); - FreeExecutorState(state->estate); - } - + FREESTATE(state); MemoryContextSwitchTo(oldcontext); /* * Free the per-sort memory context, thereby releasing all working memory. */ - MemoryContextReset(state->sortcontext); + MemoryContextReset(state->ops.sortcontext); } /* @@ -1618,7 +1670,7 @@ tuplesort_end(Tuplesortstate *state) * Free the main memory context, including the Tuplesortstate struct * itself. */ - MemoryContextDelete(state->maincontext); + MemoryContextDelete(state->ops.maincontext); } /* @@ -1832,7 +1884,9 @@ noalloc: void tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->tuplecontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.tuplecontext); + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleDesc tupDesc = (TupleDesc) ops->arg; SortTuple stup; MinimalTuple tuple; HeapTupleData htup; @@ -1844,8 +1898,8 @@ tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot) htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET; htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET); stup.datum1 = heap_getattr(&htup, - state->sortKeys[0].ssup_attno, - state->tupDesc, + ops->sortKeys[0].ssup_attno, + tupDesc, &stup.isnull1); puttuple_common(state, &stup); @@ -1862,7 +1916,9 @@ void tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup) { SortTuple stup; - MemoryContext oldcontext = MemoryContextSwitchTo(state->tuplecontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.tuplecontext); + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortClusterArg *arg = (TupleSortClusterArg *) ops->arg; /* copy the tuple into sort storage */ tup = heap_copytuple(tup); @@ -1872,11 +1928,11 @@ tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup) * set up first-column key value, and potentially abbreviate, if it's a * simple column */ - if (state->haveDatum1) + if (ops->haveDatum1) { stup.datum1 = heap_getattr(tup, - state->indexInfo->ii_IndexAttrNumbers[0], - state->tupDesc, + arg->indexInfo->ii_IndexAttrNumbers[0], + arg->tupDesc, &stup.isnull1); } @@ -1894,9 +1950,11 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel, ItemPointer self, Datum *values, bool *isnull) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->tuplecontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.tuplecontext); SortTuple stup; IndexTuple tuple; + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortIndexArg *arg = (TupleSortIndexArg *) ops->arg; stup.tuple = index_form_tuple(RelationGetDescr(rel), values, isnull); tuple = ((IndexTuple) stup.tuple); @@ -1904,7 +1962,7 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel, /* set up first-column key value */ stup.datum1 = index_getattr(tuple, 1, - RelationGetDescr(state->indexRel), + RelationGetDescr(arg->indexRel), &stup.isnull1); puttuple_common(state, &stup); @@ -1920,7 +1978,9 @@ tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel, void tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->tuplecontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.tuplecontext); + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortDatumArg *arg = (TupleSortDatumArg *) ops->arg; SortTuple stup; /* @@ -1935,7 +1995,7 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull) * identical to stup.tuple. */ - if (isNull || !state->tuples) + if (isNull || !state->ops.tuples) { /* * Set datum1 to zeroed representation for NULLs (to be consistent, @@ -1948,7 +2008,7 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull) else { stup.isnull1 = false; - stup.datum1 = datumCopy(val, false, state->datumTypeLen); + stup.datum1 = datumCopy(val, false, arg->datumTypeLen); stup.tuple = DatumGetPointer(stup.datum1); } @@ -1963,15 +2023,15 @@ tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull) static void puttuple_common(Tuplesortstate *state, SortTuple *tuple) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); Assert(!LEADER(state)); if (tuple->tuple != NULL) USEMEM(state, GetMemoryChunkSpace(tuple->tuple)); - if (!state->sortKeys || !state->haveDatum1 || !state->tuples || - !state->sortKeys->abbrev_converter || tuple->isnull1) + if (!state->ops.sortKeys || !state->ops.haveDatum1 || !state->ops.tuples || + !state->ops.sortKeys->abbrev_converter || tuple->isnull1) { /* * Store ordinary Datum representation, or NULL value. If there is a @@ -1985,8 +2045,8 @@ puttuple_common(Tuplesortstate *state, SortTuple *tuple) else if (!consider_abort_common(state)) { /* Store abbreviated key representation */ - tuple->datum1 = state->sortKeys->abbrev_converter(tuple->datum1, - state->sortKeys); + tuple->datum1 = state->ops.sortKeys->abbrev_converter(tuple->datum1, + state->ops.sortKeys); } else { @@ -2130,9 +2190,9 @@ writetuple_common(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup) static bool consider_abort_common(Tuplesortstate *state) { - Assert(state->sortKeys[0].abbrev_converter != NULL); - Assert(state->sortKeys[0].abbrev_abort != NULL); - Assert(state->sortKeys[0].abbrev_full_comparator != NULL); + Assert(state->ops.sortKeys[0].abbrev_converter != NULL); + Assert(state->ops.sortKeys[0].abbrev_abort != NULL); + Assert(state->ops.sortKeys[0].abbrev_full_comparator != NULL); /* * Check effectiveness of abbreviation optimization. Consider aborting @@ -2147,19 +2207,19 @@ consider_abort_common(Tuplesortstate *state) * Check opclass-supplied abbreviation abort routine. It may indicate * that abbreviation should not proceed. */ - if (!state->sortKeys->abbrev_abort(state->memtupcount, - state->sortKeys)) + if (!state->ops.sortKeys->abbrev_abort(state->memtupcount, + state->ops.sortKeys)) return false; /* * Finally, restore authoritative comparator, and indicate that * abbreviation is not in play by setting abbrev_converter to NULL */ - state->sortKeys[0].comparator = state->sortKeys[0].abbrev_full_comparator; - state->sortKeys[0].abbrev_converter = NULL; + state->ops.sortKeys[0].comparator = state->ops.sortKeys[0].abbrev_full_comparator; + state->ops.sortKeys[0].abbrev_converter = NULL; /* Not strictly necessary, but be tidy */ - state->sortKeys[0].abbrev_abort = NULL; - state->sortKeys[0].abbrev_full_comparator = NULL; + state->ops.sortKeys[0].abbrev_abort = NULL; + state->ops.sortKeys[0].abbrev_full_comparator = NULL; /* Give up - expect original pass-by-value representation */ return true; @@ -2174,7 +2234,7 @@ consider_abort_common(Tuplesortstate *state) void tuplesort_performsort(Tuplesortstate *state) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); #ifdef TRACE_SORT if (trace_sort) @@ -2294,7 +2354,7 @@ tuplesort_gettuple_common(Tuplesortstate *state, bool forward, switch (state->status) { case TSS_SORTEDINMEM: - Assert(forward || state->sortopt & TUPLESORT_RANDOMACCESS); + Assert(forward || state->ops.sortopt & TUPLESORT_RANDOMACCESS); Assert(!state->slabAllocatorUsed); if (forward) { @@ -2338,7 +2398,7 @@ tuplesort_gettuple_common(Tuplesortstate *state, bool forward, break; case TSS_SORTEDONTAPE: - Assert(forward || state->sortopt & TUPLESORT_RANDOMACCESS); + Assert(forward || state->ops.sortopt & TUPLESORT_RANDOMACCESS); Assert(state->slabAllocatorUsed); /* @@ -2540,7 +2600,7 @@ bool tuplesort_gettupleslot(Tuplesortstate *state, bool forward, bool copy, TupleTableSlot *slot, Datum *abbrev) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); SortTuple stup; if (!tuplesort_gettuple_common(state, forward, &stup)) @@ -2551,7 +2611,7 @@ tuplesort_gettupleslot(Tuplesortstate *state, bool forward, bool copy, if (stup.tuple) { /* Record abbreviated key for caller */ - if (state->sortKeys->abbrev_converter && abbrev) + if (state->ops.sortKeys->abbrev_converter && abbrev) *abbrev = stup.datum1; if (copy) @@ -2576,7 +2636,7 @@ tuplesort_gettupleslot(Tuplesortstate *state, bool forward, bool copy, HeapTuple tuplesort_getheaptuple(Tuplesortstate *state, bool forward) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); SortTuple stup; if (!tuplesort_gettuple_common(state, forward, &stup)) @@ -2596,7 +2656,7 @@ tuplesort_getheaptuple(Tuplesortstate *state, bool forward) IndexTuple tuplesort_getindextuple(Tuplesortstate *state, bool forward) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); SortTuple stup; if (!tuplesort_gettuple_common(state, forward, &stup)) @@ -2626,7 +2686,9 @@ bool tuplesort_getdatum(Tuplesortstate *state, bool forward, Datum *val, bool *isNull, Datum *abbrev) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortDatumArg *arg = (TupleSortDatumArg *) ops->arg; SortTuple stup; if (!tuplesort_gettuple_common(state, forward, &stup)) @@ -2639,10 +2701,10 @@ tuplesort_getdatum(Tuplesortstate *state, bool forward, MemoryContextSwitchTo(oldcontext); /* Record abbreviated key for caller */ - if (state->sortKeys->abbrev_converter && abbrev) + if (ops->sortKeys->abbrev_converter && abbrev) *abbrev = stup.datum1; - if (stup.isnull1 || !state->tuples) + if (stup.isnull1 || !state->ops.tuples) { *val = stup.datum1; *isNull = stup.isnull1; @@ -2650,7 +2712,7 @@ tuplesort_getdatum(Tuplesortstate *state, bool forward, else { /* use stup.tuple because stup.datum1 may be an abbreviation */ - *val = datumCopy(PointerGetDatum(stup.tuple), false, state->datumTypeLen); + *val = datumCopy(PointerGetDatum(stup.tuple), false, arg->datumTypeLen); *isNull = false; } @@ -2703,7 +2765,7 @@ tuplesort_skiptuples(Tuplesortstate *state, int64 ntuples, bool forward) * We could probably optimize these cases better, but for now it's * not worth the trouble. */ - oldcontext = MemoryContextSwitchTo(state->sortcontext); + oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); while (ntuples-- > 0) { SortTuple stup; @@ -2979,7 +3041,7 @@ mergeruns(Tuplesortstate *state) Assert(state->status == TSS_BUILDRUNS); Assert(state->memtupcount == 0); - if (state->sortKeys != NULL && state->sortKeys->abbrev_converter != NULL) + if (state->ops.sortKeys != NULL && state->ops.sortKeys->abbrev_converter != NULL) { /* * If there are multiple runs to be merged, when we go to read back @@ -2987,19 +3049,19 @@ mergeruns(Tuplesortstate *state) * we don't care to regenerate them. Disable abbreviation from this * point on. */ - state->sortKeys->abbrev_converter = NULL; - state->sortKeys->comparator = state->sortKeys->abbrev_full_comparator; + state->ops.sortKeys->abbrev_converter = NULL; + state->ops.sortKeys->comparator = state->ops.sortKeys->abbrev_full_comparator; /* Not strictly necessary, but be tidy */ - state->sortKeys->abbrev_abort = NULL; - state->sortKeys->abbrev_full_comparator = NULL; + state->ops.sortKeys->abbrev_abort = NULL; + state->ops.sortKeys->abbrev_full_comparator = NULL; } /* * Reset tuple memory. We've freed all the tuples that we previously * allocated. We will use the slab allocator from now on. */ - MemoryContextResetOnly(state->tuplecontext); + MemoryContextResetOnly(state->ops.tuplecontext); /* * We no longer need a large memtuples array. (We will allocate a smaller @@ -3022,7 +3084,7 @@ mergeruns(Tuplesortstate *state) * From this point on, we no longer use the USEMEM()/LACKMEM() mechanism * to track memory usage of individual tuples. */ - if (state->tuples) + if (state->ops.tuples) init_slab_allocator(state, state->nOutputTapes + 1); else init_slab_allocator(state, 0); @@ -3036,7 +3098,7 @@ mergeruns(Tuplesortstate *state) * number of input tapes will not increase between passes.) */ state->memtupsize = state->nOutputTapes; - state->memtuples = (SortTuple *) MemoryContextAlloc(state->maincontext, + state->memtuples = (SortTuple *) MemoryContextAlloc(state->ops.maincontext, state->nOutputTapes * sizeof(SortTuple)); USEMEM(state, GetMemoryChunkSpace(state->memtuples)); @@ -3113,7 +3175,7 @@ mergeruns(Tuplesortstate *state) * sorted tape, we can stop at this point and do the final merge * on-the-fly. */ - if ((state->sortopt & TUPLESORT_RANDOMACCESS) == 0 + if ((state->ops.sortopt & TUPLESORT_RANDOMACCESS) == 0 && state->nInputRuns <= state->nInputTapes && !WORKER(state)) { @@ -3339,7 +3401,7 @@ dumptuples(Tuplesortstate *state, bool alltuples) * AllocSetFree's bucketing by size class might be particularly bad if * this step wasn't taken. */ - MemoryContextReset(state->tuplecontext); + MemoryContextReset(state->ops.tuplecontext); markrunend(state->destTape); @@ -3357,9 +3419,9 @@ dumptuples(Tuplesortstate *state, bool alltuples) void tuplesort_rescan(Tuplesortstate *state) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); - Assert(state->sortopt & TUPLESORT_RANDOMACCESS); + Assert(state->ops.sortopt & TUPLESORT_RANDOMACCESS); switch (state->status) { @@ -3390,9 +3452,9 @@ tuplesort_rescan(Tuplesortstate *state) void tuplesort_markpos(Tuplesortstate *state) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); - Assert(state->sortopt & TUPLESORT_RANDOMACCESS); + Assert(state->ops.sortopt & TUPLESORT_RANDOMACCESS); switch (state->status) { @@ -3421,9 +3483,9 @@ tuplesort_markpos(Tuplesortstate *state) void tuplesort_restorepos(Tuplesortstate *state) { - MemoryContext oldcontext = MemoryContextSwitchTo(state->sortcontext); + MemoryContext oldcontext = MemoryContextSwitchTo(state->ops.sortcontext); - Assert(state->sortopt & TUPLESORT_RANDOMACCESS); + Assert(state->ops.sortopt & TUPLESORT_RANDOMACCESS); switch (state->status) { @@ -3639,9 +3701,9 @@ tuplesort_sort_memtuples(Tuplesortstate *state) * Do we have the leading column's value or abbreviation in datum1, * and is there a specialization for its comparator? */ - if (state->haveDatum1 && state->sortKeys) + if (state->ops.haveDatum1 && state->ops.sortKeys) { - if (state->sortKeys[0].comparator == ssup_datum_unsigned_cmp) + if (state->ops.sortKeys[0].comparator == ssup_datum_unsigned_cmp) { qsort_tuple_unsigned(state->memtuples, state->memtupcount, @@ -3649,7 +3711,7 @@ tuplesort_sort_memtuples(Tuplesortstate *state) return; } #if SIZEOF_DATUM >= 8 - else if (state->sortKeys[0].comparator == ssup_datum_signed_cmp) + else if (state->ops.sortKeys[0].comparator == ssup_datum_signed_cmp) { qsort_tuple_signed(state->memtuples, state->memtupcount, @@ -3657,7 +3719,7 @@ tuplesort_sort_memtuples(Tuplesortstate *state) return; } #endif - else if (state->sortKeys[0].comparator == ssup_datum_int32_cmp) + else if (state->ops.sortKeys[0].comparator == ssup_datum_int32_cmp) { qsort_tuple_int32(state->memtuples, state->memtupcount, @@ -3667,16 +3729,16 @@ tuplesort_sort_memtuples(Tuplesortstate *state) } /* Can we use the single-key sort function? */ - if (state->onlyKey != NULL) + if (state->ops.onlyKey != NULL) { qsort_ssup(state->memtuples, state->memtupcount, - state->onlyKey); + state->ops.onlyKey); } else { qsort_tuple(state->memtuples, state->memtupcount, - state->comparetup, + state->ops.comparetup, state); } } @@ -3793,10 +3855,10 @@ tuplesort_heap_replace_top(Tuplesortstate *state, SortTuple *tuple) static void reversedirection(Tuplesortstate *state) { - SortSupport sortKey = state->sortKeys; + SortSupport sortKey = state->ops.sortKeys; int nkey; - for (nkey = 0; nkey < state->nKeys; nkey++, sortKey++) + for (nkey = 0; nkey < state->ops.nKeys; nkey++, sortKey++) { sortKey->ssup_reverse = !sortKey->ssup_reverse; sortKey->ssup_nulls_first = !sortKey->ssup_nulls_first; @@ -3847,7 +3909,7 @@ readtup_alloc(Tuplesortstate *state, Size tuplen) Assert(state->slabFreeHead); if (tuplen > SLAB_SLOT_SIZE || !state->slabFreeHead) - return MemoryContextAlloc(state->sortcontext, tuplen); + return MemoryContextAlloc(state->ops.sortcontext, tuplen); else { buf = state->slabFreeHead; @@ -3866,6 +3928,7 @@ readtup_alloc(Tuplesortstate *state, Size tuplen) static void getdatum1_heap(Tuplesortstate *state, SortTuple *stup) { + TuplesortOps *ops = TuplesortstateGetOps(state); HeapTupleData htup; htup.t_len = ((MinimalTuple) stup->tuple)->t_len + @@ -3874,8 +3937,8 @@ getdatum1_heap(Tuplesortstate *state, SortTuple *stup) MINIMAL_TUPLE_OFFSET); stup->datum1 = heap_getattr(&htup, - state->sortKeys[0].ssup_attno, - state->tupDesc, + ops->sortKeys[0].ssup_attno, + (TupleDesc) ops->arg, &stup->isnull1); } @@ -3883,7 +3946,8 @@ getdatum1_heap(Tuplesortstate *state, SortTuple *stup) static int comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state) { - SortSupport sortKey = state->sortKeys; + TuplesortOps *ops = TuplesortstateGetOps(state); + SortSupport sortKey = ops->sortKeys; HeapTupleData ltup; HeapTupleData rtup; TupleDesc tupDesc; @@ -3908,7 +3972,7 @@ comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state) ltup.t_data = (HeapTupleHeader) ((char *) a->tuple - MINIMAL_TUPLE_OFFSET); rtup.t_len = ((MinimalTuple) b->tuple)->t_len + MINIMAL_TUPLE_OFFSET; rtup.t_data = (HeapTupleHeader) ((char *) b->tuple - MINIMAL_TUPLE_OFFSET); - tupDesc = state->tupDesc; + tupDesc = (TupleDesc) ops->arg; if (sortKey->abbrev_converter) { @@ -3925,7 +3989,7 @@ comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state) } sortKey++; - for (nkey = 1; nkey < state->nKeys; nkey++, sortKey++) + for (nkey = 1; nkey < ops->nKeys; nkey++, sortKey++) { attno = sortKey->ssup_attno; @@ -3945,6 +4009,7 @@ comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state) static void writetup_heap(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup) { + TuplesortOps *ops = TuplesortstateGetOps(state); MinimalTuple tuple = (MinimalTuple) stup->tuple; /* the part of the MinimalTuple we'll write: */ @@ -3956,7 +4021,7 @@ writetup_heap(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup) LogicalTapeWrite(tape, (void *) &tuplen, sizeof(tuplen)); LogicalTapeWrite(tape, (void *) tupbody, tupbodylen); - if (state->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length + if (ops->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length * word? */ LogicalTapeWrite(tape, (void *) &tuplen, sizeof(tuplen)); } @@ -3969,12 +4034,13 @@ readtup_heap(Tuplesortstate *state, SortTuple *stup, unsigned int tuplen = tupbodylen + MINIMAL_TUPLE_DATA_OFFSET; MinimalTuple tuple = (MinimalTuple) readtup_alloc(state, tuplen); char *tupbody = (char *) tuple + MINIMAL_TUPLE_DATA_OFFSET; + TuplesortOps *ops = TuplesortstateGetOps(state); HeapTupleData htup; /* read in the tuple proper */ tuple->t_len = tuplen; LogicalTapeReadExact(tape, tupbody, tupbodylen); - if (state->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length + if (ops->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length * word? */ LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen)); stup->tuple = (void *) tuple; @@ -3982,8 +4048,8 @@ readtup_heap(Tuplesortstate *state, SortTuple *stup, htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET; htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET); stup->datum1 = heap_getattr(&htup, - state->sortKeys[0].ssup_attno, - state->tupDesc, + ops->sortKeys[0].ssup_attno, + (TupleDesc) ops->arg, &stup->isnull1); } @@ -3995,12 +4061,14 @@ readtup_heap(Tuplesortstate *state, SortTuple *stup, static void getdatum1_cluster(Tuplesortstate *state, SortTuple *stup) { + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortClusterArg *arg = (TupleSortClusterArg *) ops->arg; HeapTuple tup; tup = (HeapTuple) stup->tuple; stup->datum1 = heap_getattr(tup, - state->indexInfo->ii_IndexAttrNumbers[0], - state->tupDesc, + arg->indexInfo->ii_IndexAttrNumbers[0], + arg->tupDesc, &stup->isnull1); } @@ -4008,7 +4076,9 @@ static int comparetup_cluster(const SortTuple *a, const SortTuple *b, Tuplesortstate *state) { - SortSupport sortKey = state->sortKeys; + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortClusterArg *arg = (TupleSortClusterArg *) ops->arg; + SortSupport sortKey = ops->sortKeys; HeapTuple ltup; HeapTuple rtup; TupleDesc tupDesc; @@ -4022,10 +4092,10 @@ comparetup_cluster(const SortTuple *a, const SortTuple *b, /* Be prepared to compare additional sort keys */ ltup = (HeapTuple) a->tuple; rtup = (HeapTuple) b->tuple; - tupDesc = state->tupDesc; + tupDesc = arg->tupDesc; /* Compare the leading sort key, if it's simple */ - if (state->haveDatum1) + if (ops->haveDatum1) { compare = ApplySortComparator(a->datum1, a->isnull1, b->datum1, b->isnull1, @@ -4035,7 +4105,7 @@ comparetup_cluster(const SortTuple *a, const SortTuple *b, if (sortKey->abbrev_converter) { - AttrNumber leading = state->indexInfo->ii_IndexAttrNumbers[0]; + AttrNumber leading = arg->indexInfo->ii_IndexAttrNumbers[0]; datum1 = heap_getattr(ltup, leading, tupDesc, &isnull1); datum2 = heap_getattr(rtup, leading, tupDesc, &isnull2); @@ -4044,7 +4114,7 @@ comparetup_cluster(const SortTuple *a, const SortTuple *b, datum2, isnull2, sortKey); } - if (compare != 0 || state->nKeys == 1) + if (compare != 0 || ops->nKeys == 1) return compare; /* Compare additional columns the hard way */ sortKey++; @@ -4056,13 +4126,13 @@ comparetup_cluster(const SortTuple *a, const SortTuple *b, nkey = 0; } - if (state->indexInfo->ii_Expressions == NULL) + if (arg->indexInfo->ii_Expressions == NULL) { /* If not expression index, just compare the proper heap attrs */ - for (; nkey < state->nKeys; nkey++, sortKey++) + for (; nkey < ops->nKeys; nkey++, sortKey++) { - AttrNumber attno = state->indexInfo->ii_IndexAttrNumbers[nkey]; + AttrNumber attno = arg->indexInfo->ii_IndexAttrNumbers[nkey]; datum1 = heap_getattr(ltup, attno, tupDesc, &isnull1); datum2 = heap_getattr(rtup, attno, tupDesc, &isnull2); @@ -4089,19 +4159,19 @@ comparetup_cluster(const SortTuple *a, const SortTuple *b, TupleTableSlot *ecxt_scantuple; /* Reset context each time to prevent memory leakage */ - ResetPerTupleExprContext(state->estate); + ResetPerTupleExprContext(arg->estate); - ecxt_scantuple = GetPerTupleExprContext(state->estate)->ecxt_scantuple; + ecxt_scantuple = GetPerTupleExprContext(arg->estate)->ecxt_scantuple; ExecStoreHeapTuple(ltup, ecxt_scantuple, false); - FormIndexDatum(state->indexInfo, ecxt_scantuple, state->estate, + FormIndexDatum(arg->indexInfo, ecxt_scantuple, arg->estate, l_index_values, l_index_isnull); ExecStoreHeapTuple(rtup, ecxt_scantuple, false); - FormIndexDatum(state->indexInfo, ecxt_scantuple, state->estate, + FormIndexDatum(arg->indexInfo, ecxt_scantuple, arg->estate, r_index_values, r_index_isnull); - for (; nkey < state->nKeys; nkey++, sortKey++) + for (; nkey < ops->nKeys; nkey++, sortKey++) { compare = ApplySortComparator(l_index_values[nkey], l_index_isnull[nkey], @@ -4119,6 +4189,7 @@ comparetup_cluster(const SortTuple *a, const SortTuple *b, static void writetup_cluster(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup) { + TuplesortOps *ops = TuplesortstateGetOps(state); HeapTuple tuple = (HeapTuple) stup->tuple; unsigned int tuplen = tuple->t_len + sizeof(ItemPointerData) + sizeof(int); @@ -4126,7 +4197,7 @@ writetup_cluster(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup) LogicalTapeWrite(tape, &tuplen, sizeof(tuplen)); LogicalTapeWrite(tape, &tuple->t_self, sizeof(ItemPointerData)); LogicalTapeWrite(tape, tuple->t_data, tuple->t_len); - if (state->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length + if (ops->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length * word? */ LogicalTapeWrite(tape, &tuplen, sizeof(tuplen)); } @@ -4135,6 +4206,8 @@ static void readtup_cluster(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int tuplen) { + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortClusterArg *arg = (TupleSortClusterArg *) ops->arg; unsigned int t_len = tuplen - sizeof(ItemPointerData) - sizeof(int); HeapTuple tuple = (HeapTuple) readtup_alloc(state, t_len + HEAPTUPLESIZE); @@ -4147,18 +4220,34 @@ readtup_cluster(Tuplesortstate *state, SortTuple *stup, tuple->t_tableOid = InvalidOid; /* Read in the tuple body */ LogicalTapeReadExact(tape, tuple->t_data, tuple->t_len); - if (state->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length + if (ops->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length * word? */ LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen)); stup->tuple = (void *) tuple; /* set up first-column key value, if it's a simple column */ - if (state->haveDatum1) + if (ops->haveDatum1) stup->datum1 = heap_getattr(tuple, - state->indexInfo->ii_IndexAttrNumbers[0], - state->tupDesc, + arg->indexInfo->ii_IndexAttrNumbers[0], + arg->tupDesc, &stup->isnull1); } +static void +freestate_cluster(Tuplesortstate *state) +{ + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortClusterArg *arg = (TupleSortClusterArg *) ops->arg; + + /* Free any execution state created for CLUSTER case */ + if (arg->estate != NULL) + { + ExprContext *econtext = GetPerTupleExprContext(arg->estate); + + ExecDropSingleTupleTableSlot(econtext->ecxt_scantuple); + FreeExecutorState(arg->estate); + } +} + /* * Routines specialized for IndexTuple case * @@ -4170,12 +4259,14 @@ readtup_cluster(Tuplesortstate *state, SortTuple *stup, static void getdatum1_index(Tuplesortstate *state, SortTuple *stup) { + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortIndexArg *arg = (TupleSortIndexArg *) ops->arg; IndexTuple tuple; tuple = stup->tuple; stup->datum1 = index_getattr(tuple, 1, - RelationGetDescr(state->indexRel), + RelationGetDescr(arg->indexRel), &stup->isnull1); } @@ -4188,7 +4279,9 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b, * is also special handling for enforcing uniqueness, and special * treatment for equal keys at the end. */ - SortSupport sortKey = state->sortKeys; + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortIndexBTreeArg *arg = (TupleSortIndexBTreeArg *) ops->arg; + SortSupport sortKey = ops->sortKeys; IndexTuple tuple1; IndexTuple tuple2; int keysz; @@ -4212,8 +4305,8 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b, /* Compare additional sort keys */ tuple1 = (IndexTuple) a->tuple; tuple2 = (IndexTuple) b->tuple; - keysz = state->nKeys; - tupDes = RelationGetDescr(state->indexRel); + keysz = ops->nKeys; + tupDes = RelationGetDescr(arg->index.indexRel); if (sortKey->abbrev_converter) { @@ -4258,7 +4351,7 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b, * sort algorithm wouldn't have checked whether one must appear before the * other. */ - if (state->enforceUnique && !(!state->uniqueNullsNotDistinct && equal_hasnull)) + if (arg->enforceUnique && !(!arg->uniqueNullsNotDistinct && equal_hasnull)) { Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; @@ -4274,16 +4367,16 @@ comparetup_index_btree(const SortTuple *a, const SortTuple *b, index_deform_tuple(tuple1, tupDes, values, isnull); - key_desc = BuildIndexValueDescription(state->indexRel, values, isnull); + key_desc = BuildIndexValueDescription(arg->index.indexRel, values, isnull); ereport(ERROR, (errcode(ERRCODE_UNIQUE_VIOLATION), errmsg("could not create unique index \"%s\"", - RelationGetRelationName(state->indexRel)), + RelationGetRelationName(arg->index.indexRel)), key_desc ? errdetail("Key %s is duplicated.", key_desc) : errdetail("Duplicate keys exist."), - errtableconstraint(state->heapRel, - RelationGetRelationName(state->indexRel)))); + errtableconstraint(arg->index.heapRel, + RelationGetRelationName(arg->index.indexRel)))); } /* @@ -4321,6 +4414,8 @@ comparetup_index_hash(const SortTuple *a, const SortTuple *b, Bucket bucket2; IndexTuple tuple1; IndexTuple tuple2; + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortIndexHashArg *arg = (TupleSortIndexHashArg *) ops->arg; /* * Fetch hash keys and mask off bits we don't want to sort by. We know @@ -4328,12 +4423,12 @@ comparetup_index_hash(const SortTuple *a, const SortTuple *b, */ Assert(!a->isnull1); bucket1 = _hash_hashkey2bucket(DatumGetUInt32(a->datum1), - state->max_buckets, state->high_mask, - state->low_mask); + arg->max_buckets, arg->high_mask, + arg->low_mask); Assert(!b->isnull1); bucket2 = _hash_hashkey2bucket(DatumGetUInt32(b->datum1), - state->max_buckets, state->high_mask, - state->low_mask); + arg->max_buckets, arg->high_mask, + arg->low_mask); if (bucket1 > bucket2) return 1; else if (bucket1 < bucket2) @@ -4371,13 +4466,14 @@ comparetup_index_hash(const SortTuple *a, const SortTuple *b, static void writetup_index(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup) { + TuplesortOps *ops = TuplesortstateGetOps(state); IndexTuple tuple = (IndexTuple) stup->tuple; unsigned int tuplen; tuplen = IndexTupleSize(tuple) + sizeof(tuplen); LogicalTapeWrite(tape, (void *) &tuplen, sizeof(tuplen)); LogicalTapeWrite(tape, (void *) tuple, IndexTupleSize(tuple)); - if (state->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length + if (ops->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length * word? */ LogicalTapeWrite(tape, (void *) &tuplen, sizeof(tuplen)); } @@ -4386,18 +4482,20 @@ static void readtup_index(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len) { + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortIndexArg *arg = (TupleSortIndexArg *) ops->arg; unsigned int tuplen = len - sizeof(unsigned int); IndexTuple tuple = (IndexTuple) readtup_alloc(state, tuplen); LogicalTapeReadExact(tape, tuple, tuplen); - if (state->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length + if (ops->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length * word? */ LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen)); stup->tuple = (void *) tuple; /* set up first-column key value */ stup->datum1 = index_getattr(tuple, 1, - RelationGetDescr(state->indexRel), + RelationGetDescr(arg->indexRel), &stup->isnull1); } @@ -4414,20 +4512,21 @@ getdatum1_datum(Tuplesortstate *state, SortTuple *stup) static int comparetup_datum(const SortTuple *a, const SortTuple *b, Tuplesortstate *state) { + TuplesortOps *ops = TuplesortstateGetOps(state); int compare; compare = ApplySortComparator(a->datum1, a->isnull1, b->datum1, b->isnull1, - state->sortKeys); + ops->sortKeys); if (compare != 0) return compare; /* if we have abbreviations, then "tuple" has the original value */ - if (state->sortKeys->abbrev_converter) + if (ops->sortKeys->abbrev_converter) compare = ApplySortAbbrevFullComparator(PointerGetDatum(a->tuple), a->isnull1, PointerGetDatum(b->tuple), b->isnull1, - state->sortKeys); + ops->sortKeys); return compare; } @@ -4435,6 +4534,8 @@ comparetup_datum(const SortTuple *a, const SortTuple *b, Tuplesortstate *state) static void writetup_datum(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup) { + TuplesortOps *ops = TuplesortstateGetOps(state); + TupleSortDatumArg *arg = (TupleSortDatumArg *) ops->arg; void *waddr; unsigned int tuplen; unsigned int writtenlen; @@ -4444,7 +4545,7 @@ writetup_datum(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup) waddr = NULL; tuplen = 0; } - else if (!state->tuples) + else if (!state->ops.tuples) { waddr = &stup->datum1; tuplen = sizeof(Datum); @@ -4452,7 +4553,7 @@ writetup_datum(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup) else { waddr = stup->tuple; - tuplen = datumGetSize(PointerGetDatum(stup->tuple), false, state->datumTypeLen); + tuplen = datumGetSize(PointerGetDatum(stup->tuple), false, arg->datumTypeLen); Assert(tuplen != 0); } @@ -4460,7 +4561,7 @@ writetup_datum(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup) LogicalTapeWrite(tape, (void *) &writtenlen, sizeof(writtenlen)); LogicalTapeWrite(tape, waddr, tuplen); - if (state->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length + if (ops->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length * word? */ LogicalTapeWrite(tape, (void *) &writtenlen, sizeof(writtenlen)); } @@ -4469,6 +4570,7 @@ static void readtup_datum(Tuplesortstate *state, SortTuple *stup, LogicalTape *tape, unsigned int len) { + TuplesortOps *ops = TuplesortstateGetOps(state); unsigned int tuplen = len - sizeof(unsigned int); if (tuplen == 0) @@ -4478,7 +4580,7 @@ readtup_datum(Tuplesortstate *state, SortTuple *stup, stup->isnull1 = true; stup->tuple = NULL; } - else if (!state->tuples) + else if (!state->ops.tuples) { Assert(tuplen == sizeof(Datum)); LogicalTapeReadExact(tape, &stup->datum1, tuplen); @@ -4495,7 +4597,7 @@ readtup_datum(Tuplesortstate *state, SortTuple *stup, stup->tuple = raddr; } - if (state->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length + if (ops->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length * word? */ LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen)); } -- 2.30.2