Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/backend/utils/sort/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 60 kB image not shown  

Quelle  tuplesortvariants.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * tuplesortvariants.c
 *   Implementation of tuple sorting variants.
 *
 * This module handles the sorting of heap tuples, index tuples, or single
 * Datums.  The implementation is based on the generalized tuple sorting
 * facility given in tuplesort.c.  Support other kinds of sortable objects
 * could be easily added here, another module, or even an extension.
 *
 *
 * Copyright (c) 2022-2025, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *   src/backend/utils/sort/tuplesortvariants.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres.h"

#include "access/brin_tuple.h"
#include "access/gin.h"
#include "access/gin_tuple.h"
#include "access/hash.h"
#include "access/htup_details.h"
#include "access/nbtree.h"
#include "catalog/index.h"
#include "catalog/pg_collation.h"
#include "executor/executor.h"
#include "pg_trace.h"
#include "utils/builtins.h"
#include "utils/datum.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/tuplesort.h"


/* sort-type codes for sort__start probes */
#define HEAP_SORT  0
#define INDEX_SORT  1
#define DATUM_SORT  2
#define CLUSTER_SORT 3

static void removeabbrev_heap(Tuplesortstate *state, SortTuple *stups,
         int count);
static void removeabbrev_cluster(Tuplesortstate *state, SortTuple *stups,
         int count);
static void removeabbrev_index(Tuplesortstate *state, SortTuple *stups,
          int count);
static void removeabbrev_index_brin(Tuplesortstate *state, SortTuple *stups,
         int count);
static void removeabbrev_index_gin(Tuplesortstate *state, SortTuple *stups,
           int count);
static void removeabbrev_datum(Tuplesortstate *state, SortTuple *stups,
          int count);
static int comparetup_heap(const SortTuple *a, const SortTuple *b,
       Tuplesortstate *state);
static int comparetup_heap_tiebreak(const SortTuple *a, const SortTuple *b,
          Tuplesortstate *state);
static void writetup_heap(Tuplesortstate *state, LogicalTape *tape,
        SortTuple *stup);
static void readtup_heap(Tuplesortstate *state, SortTuple *stup,
       LogicalTape *tape, unsigned int len);
static int comparetup_cluster(const SortTuple *a, const SortTuple *b,
          Tuplesortstate *state);
static int comparetup_cluster_tiebreak(const SortTuple *a, const SortTuple *b,
          Tuplesortstate *state);
static void writetup_cluster(Tuplesortstate *state, LogicalTape *tape,
        SortTuple *stup);
static void readtup_cluster(Tuplesortstate *state, SortTuple *stup,
       LogicalTape *tape, unsigned int tuplen);
static int comparetup_index_btree(const SortTuple *a, const SortTuple *b,
           Tuplesortstate *state);
static int comparetup_index_btree_tiebreak(const SortTuple *a, const SortTuple *b,
           Tuplesortstate *state);
static int comparetup_index_hash(const SortTuple *a, const SortTuple *b,
          Tuplesortstate *state);
static int comparetup_index_hash_tiebreak(const SortTuple *a, const SortTuple *b,
             Tuplesortstate *state);
static int comparetup_index_brin(const SortTuple *a, const SortTuple *b,
          Tuplesortstate *state);
static int comparetup_index_gin(const SortTuple *a, const SortTuple *b,
         Tuplesortstate *state);
static void writetup_index(Tuplesortstate *state, LogicalTape *tape,
         SortTuple *stup);
static void readtup_index(Tuplesortstate *state, SortTuple *stup,
        LogicalTape *tape, unsigned int len);
static void writetup_index_brin(Tuplesortstate *state, LogicalTape *tape,
        SortTuple *stup);
static void readtup_index_brin(Tuplesortstate *state, SortTuple *stup,
          LogicalTape *tape, unsigned int len);
static void writetup_index_gin(Tuplesortstate *state, LogicalTape *tape,
          SortTuple *stup);
static void readtup_index_gin(Tuplesortstate *state, SortTuple *stup,
         LogicalTape *tape, unsigned int len);
static int comparetup_datum(const SortTuple *a, const SortTuple *b,
        Tuplesortstate *state);
static int comparetup_datum_tiebreak(const SortTuple *a, const SortTuple *b,
           Tuplesortstate *state);
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);

/*
 * Data structure pointed by "TuplesortPublic.arg" for the CLUSTER case.  Set by
 * the tuplesort_begin_cluster.
 */

typedef struct
{
 TupleDesc tupDesc;

 IndexInfo  *indexInfo;  /* info about index being used for reference */
 EState    *estate;   /* for evaluating index expressions */
} TuplesortClusterArg;

/*
 * Data structure pointed by "TuplesortPublic.arg" for the IndexTuple case.
 * Set by tuplesort_begin_index_xxx and used only by the IndexTuple routines.
 */

typedef struct
{
 Relation heapRel;  /* table the index is being built on */
 Relation indexRel;  /* index being built */
} TuplesortIndexArg;

/*
 * Data structure pointed by "TuplesortPublic.arg" for the index_btree subcase.
 */

typedef struct
{
 TuplesortIndexArg index;

 bool  enforceUnique; /* complain if we find duplicate tuples */
 bool  uniqueNullsNotDistinct; /* unique constraint null treatment */
} TuplesortIndexBTreeArg;

/*
 * Data structure pointed by "TuplesortPublic.arg" for the index_hash subcase.
 */

typedef struct
{
 TuplesortIndexArg index;

 uint32  high_mask;  /* masks for sortable part of hash code */
 uint32  low_mask;
 uint32  max_buckets;
} TuplesortIndexHashArg;

/*
 * Data structure pointed by "TuplesortPublic.arg" for the Datum case.
 * Set by tuplesort_begin_datum and used only by the DatumTuple routines.
 */

typedef struct
{
 /* the datatype oid of Datum's to be sorted */
 Oid   datumType;
 /* we need typelen in order to know how to copy the Datums. */
 int   datumTypeLen;
} TuplesortDatumArg;

/*
 * Computing BrinTuple size with only the tuple is difficult, so we want to track
 * the length referenced by the SortTuple. That's what BrinSortTuple is meant
 * to do - it's essentially a BrinTuple prefixed by its length.
 */

typedef struct BrinSortTuple
{
 Size  tuplen;
 BrinTuple tuple;
} BrinSortTuple;

/* Size of the BrinSortTuple, given length of the BrinTuple. */
#define BRINSORTTUPLE_SIZE(len)  (offsetof(BrinSortTuple, tuple) + (len))


Tuplesortstate *
tuplesort_begin_heap(TupleDesc tupDesc,
      int nkeys, AttrNumber *attNums,
      Oid *sortOperators, Oid *sortCollations,
      bool *nullsFirstFlags,
      int workMem, SortCoordinate coordinate, int sortopt)
{
 Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
               sortopt);
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext;
 int   i;

 oldcontext = MemoryContextSwitchTo(base->maincontext);

 Assert(nkeys > 0);

 if (trace_sort)
  elog(LOG,
    "begin tuple sort: nkeys = %d, workMem = %d, randomAccess = %c",
    nkeys, workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');

 base->nKeys = nkeys;

 TRACE_POSTGRESQL_SORT_START(HEAP_SORT,
        false/* no unique check */
        nkeys,
        workMem,
        sortopt & TUPLESORT_RANDOMACCESS,
        PARALLEL_SORT(coordinate));

 base->removeabbrev = removeabbrev_heap;
 base->comparetup = comparetup_heap;
 base->comparetup_tiebreak = comparetup_heap_tiebreak;
 base->writetup = writetup_heap;
 base->readtup = readtup_heap;
 base->haveDatum1 = true;
 base->arg = tupDesc;  /* assume we need not copy tupDesc */

 /* Prepare SortSupport data for each column */
 base->sortKeys = (SortSupport) palloc0(nkeys * sizeof(SortSupportData));

 for (i = 0; i < nkeys; i++)
 {
  SortSupport sortKey = base->sortKeys + i;

  Assert(attNums[i] != 0);
  Assert(sortOperators[i] != 0);

  sortKey->ssup_cxt = CurrentMemoryContext;
  sortKey->ssup_collation = sortCollations[i];
  sortKey->ssup_nulls_first = nullsFirstFlags[i];
  sortKey->ssup_attno = attNums[i];
  /* Convey if abbreviation optimization is applicable in principle */
  sortKey->abbreviate = (i == 0 && base->haveDatum1);

  PrepareSortSupportFromOrderingOp(sortOperators[i], sortKey);
 }

 /*
  * The "onlyKey" optimization cannot be used with abbreviated keys, since
  * tie-breaker comparisons may be required.  Typically, the optimization
  * 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 && !base->sortKeys->abbrev_converter)
  base->onlyKey = base->sortKeys;

 MemoryContextSwitchTo(oldcontext);

 return state;
}

Tuplesortstate *
tuplesort_begin_cluster(TupleDesc tupDesc,
      Relation indexRel,
      int workMem,
      SortCoordinate coordinate, int sortopt)
{
 Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
               sortopt);
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 BTScanInsert indexScanKey;
 MemoryContext oldcontext;
 TuplesortClusterArg *arg;
 int   i;

 Assert(indexRel->rd_rel->relam == BTREE_AM_OID);

 oldcontext = MemoryContextSwitchTo(base->maincontext);
 arg = (TuplesortClusterArg *) palloc0(sizeof(TuplesortClusterArg));

 if (trace_sort)
  elog(LOG,
    "begin tuple sort: nkeys = %d, workMem = %d, randomAccess = %c",
    RelationGetNumberOfAttributes(indexRel),
    workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');

 base->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel);

 TRACE_POSTGRESQL_SORT_START(CLUSTER_SORT,
        false/* no unique check */
        base->nKeys,
        workMem,
        sortopt & TUPLESORT_RANDOMACCESS,
        PARALLEL_SORT(coordinate));

 base->removeabbrev = removeabbrev_cluster;
 base->comparetup = comparetup_cluster;
 base->comparetup_tiebreak = comparetup_cluster_tiebreak;
 base->writetup = writetup_cluster;
 base->readtup = readtup_cluster;
 base->freestate = freestate_cluster;
 base->arg = arg;

 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 (arg->indexInfo->ii_IndexAttrNumbers[0] == 0)
  base->haveDatum1 = false;
 else
  base->haveDatum1 = true;

 arg->tupDesc = tupDesc;  /* assume we need not copy tupDesc */

 indexScanKey = _bt_mkscankey(indexRel, NULL);

 if (arg->indexInfo->ii_Expressions != NULL)
 {
  TupleTableSlot *slot;
  ExprContext *econtext;

  /*
   * We will need to use FormIndexDatum to evaluate the index
   * expressions.  To do that, we need an EState, as well as a
   * TupleTableSlot to put the table tuples into.  The econtext's
   * scantuple has to point to that slot, too.
 */

  arg->estate = CreateExecutorState();
  slot = MakeSingleTupleTableSlot(tupDesc, &TTSOpsHeapTuple);
  econtext = GetPerTupleExprContext(arg->estate);
  econtext->ecxt_scantuple = slot;
 }

 /* Prepare SortSupport data for each column */
 base->sortKeys = (SortSupport) palloc0(base->nKeys *
             sizeof(SortSupportData));

 for (i = 0; i < base->nKeys; i++)
 {
  SortSupport sortKey = base->sortKeys + i;
  ScanKey  scanKey = indexScanKey->scankeys + i;
  bool  reverse;

  sortKey->ssup_cxt = CurrentMemoryContext;
  sortKey->ssup_collation = scanKey->sk_collation;
  sortKey->ssup_nulls_first =
   (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 && base->haveDatum1);

  Assert(sortKey->ssup_attno != 0);

  reverse = (scanKey->sk_flags & SK_BT_DESC) != 0;

  PrepareSortSupportFromIndexRel(indexRel, reverse, sortKey);
 }

 pfree(indexScanKey);

 MemoryContextSwitchTo(oldcontext);

 return state;
}

Tuplesortstate *
tuplesort_begin_index_btree(Relation heapRel,
       Relation indexRel,
       bool enforceUnique,
       bool uniqueNullsNotDistinct,
       int workMem,
       SortCoordinate coordinate,
       int sortopt)
{
 Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
               sortopt);
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 BTScanInsert indexScanKey;
 TuplesortIndexBTreeArg *arg;
 MemoryContext oldcontext;
 int   i;

 oldcontext = MemoryContextSwitchTo(base->maincontext);
 arg = (TuplesortIndexBTreeArg *) palloc(sizeof(TuplesortIndexBTreeArg));

 if (trace_sort)
  elog(LOG,
    "begin index sort: unique = %c, workMem = %d, randomAccess = %c",
    enforceUnique ? 't' : 'f',
    workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');

 base->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel);

 TRACE_POSTGRESQL_SORT_START(INDEX_SORT,
        enforceUnique,
        base->nKeys,
        workMem,
        sortopt & TUPLESORT_RANDOMACCESS,
        PARALLEL_SORT(coordinate));

 base->removeabbrev = removeabbrev_index;
 base->comparetup = comparetup_index_btree;
 base->comparetup_tiebreak = comparetup_index_btree_tiebreak;
 base->writetup = writetup_index;
 base->readtup = readtup_index;
 base->haveDatum1 = true;
 base->arg = arg;

 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 */
 base->sortKeys = (SortSupport) palloc0(base->nKeys *
             sizeof(SortSupportData));

 for (i = 0; i < base->nKeys; i++)
 {
  SortSupport sortKey = base->sortKeys + i;
  ScanKey  scanKey = indexScanKey->scankeys + i;
  bool  reverse;

  sortKey->ssup_cxt = CurrentMemoryContext;
  sortKey->ssup_collation = scanKey->sk_collation;
  sortKey->ssup_nulls_first =
   (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 && base->haveDatum1);

  Assert(sortKey->ssup_attno != 0);

  reverse = (scanKey->sk_flags & SK_BT_DESC) != 0;

  PrepareSortSupportFromIndexRel(indexRel, reverse, sortKey);
 }

 pfree(indexScanKey);

 MemoryContextSwitchTo(oldcontext);

 return state;
}

Tuplesortstate *
tuplesort_begin_index_hash(Relation heapRel,
         Relation indexRel,
         uint32 high_mask,
         uint32 low_mask,
         uint32 max_buckets,
         int workMem,
         SortCoordinate coordinate,
         int sortopt)
{
 Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
               sortopt);
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext;
 TuplesortIndexHashArg *arg;

 oldcontext = MemoryContextSwitchTo(base->maincontext);
 arg = (TuplesortIndexHashArg *) palloc(sizeof(TuplesortIndexHashArg));

 if (trace_sort)
  elog(LOG,
    "begin index sort: high_mask = 0x%x, low_mask = 0x%x, "
    "max_buckets = 0x%x, workMem = %d, randomAccess = %c",
    high_mask,
    low_mask,
    max_buckets,
    workMem,
    sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');

 base->nKeys = 1;   /* Only one sort column, the hash code */

 base->removeabbrev = removeabbrev_index;
 base->comparetup = comparetup_index_hash;
 base->comparetup_tiebreak = comparetup_index_hash_tiebreak;
 base->writetup = writetup_index;
 base->readtup = readtup_index;
 base->haveDatum1 = true;
 base->arg = arg;

 arg->index.heapRel = heapRel;
 arg->index.indexRel = indexRel;

 arg->high_mask = high_mask;
 arg->low_mask = low_mask;
 arg->max_buckets = max_buckets;

 MemoryContextSwitchTo(oldcontext);

 return state;
}

Tuplesortstate *
tuplesort_begin_index_gist(Relation heapRel,
         Relation indexRel,
         int workMem,
         SortCoordinate coordinate,
         int sortopt)
{
 Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
               sortopt);
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext;
 TuplesortIndexBTreeArg *arg;
 int   i;

 oldcontext = MemoryContextSwitchTo(base->maincontext);
 arg = (TuplesortIndexBTreeArg *) palloc(sizeof(TuplesortIndexBTreeArg));

 if (trace_sort)
  elog(LOG,
    "begin index sort: workMem = %d, randomAccess = %c",
    workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');

 base->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel);

 base->removeabbrev = removeabbrev_index;
 base->comparetup = comparetup_index_btree;
 base->comparetup_tiebreak = comparetup_index_btree_tiebreak;
 base->writetup = writetup_index;
 base->readtup = readtup_index;
 base->haveDatum1 = true;
 base->arg = arg;

 arg->index.heapRel = heapRel;
 arg->index.indexRel = indexRel;
 arg->enforceUnique = false;
 arg->uniqueNullsNotDistinct = false;

 /* Prepare SortSupport data for each column */
 base->sortKeys = (SortSupport) palloc0(base->nKeys *
             sizeof(SortSupportData));

 for (i = 0; i < base->nKeys; i++)
 {
  SortSupport sortKey = base->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 && base->haveDatum1);

  Assert(sortKey->ssup_attno != 0);

  /* Look for a sort support function */
  PrepareSortSupportFromGistIndexRel(indexRel, sortKey);
 }

 MemoryContextSwitchTo(oldcontext);

 return state;
}

Tuplesortstate *
tuplesort_begin_index_brin(int workMem,
         SortCoordinate coordinate,
         int sortopt)
{
 Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
               sortopt);
 TuplesortPublic *base = TuplesortstateGetPublic(state);

 if (trace_sort)
  elog(LOG,
    "begin index sort: workMem = %d, randomAccess = %c",
    workMem,
    sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');

 base->nKeys = 1;   /* Only one sort column, the block number */

 base->removeabbrev = removeabbrev_index_brin;
 base->comparetup = comparetup_index_brin;
 base->writetup = writetup_index_brin;
 base->readtup = readtup_index_brin;
 base->haveDatum1 = true;
 base->arg = NULL;

 return state;
}

Tuplesortstate *
tuplesort_begin_index_gin(Relation heapRel,
        Relation indexRel,
        int workMem, SortCoordinate coordinate,
        int sortopt)
{
 Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
               sortopt);
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext;
 int   i;
 TupleDesc desc = RelationGetDescr(indexRel);

 oldcontext = MemoryContextSwitchTo(base->maincontext);

#ifdef TRACE_SORT
 if (trace_sort)
  elog(LOG,
    "begin index sort: workMem = %d, randomAccess = %c",
    workMem,
    sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');
#endif

 /*
  * Multi-column GIN indexes expand the row into a separate index entry for
  * attribute, and that's what we write into the tuplesort. But we still
  * need to initialize sortsupport for all the attributes.
 */

 base->nKeys = IndexRelationGetNumberOfKeyAttributes(indexRel);

 /* Prepare SortSupport data for each column */
 base->sortKeys = (SortSupport) palloc0(base->nKeys *
             sizeof(SortSupportData));

 for (i = 0; i < base->nKeys; i++)
 {
  SortSupport sortKey = base->sortKeys + i;
  Form_pg_attribute att = TupleDescAttr(desc, i);
  Oid   cmpFunc;

  sortKey->ssup_cxt = CurrentMemoryContext;
  sortKey->ssup_collation = indexRel->rd_indcollation[i];
  sortKey->ssup_nulls_first = false;
  sortKey->ssup_attno = i + 1;
  sortKey->abbreviate = false;

  Assert(sortKey->ssup_attno != 0);

  if (!OidIsValid(sortKey->ssup_collation))
   sortKey->ssup_collation = DEFAULT_COLLATION_OID;

  /*
   * If the compare proc isn't specified in the opclass definition, look
   * up the index key type's default btree comparator.
 */

  cmpFunc = index_getprocid(indexRel, i + 1, GIN_COMPARE_PROC);
  if (cmpFunc == InvalidOid)
  {
   TypeCacheEntry *typentry;

   typentry = lookup_type_cache(att->atttypid,
           TYPECACHE_CMP_PROC_FINFO);
   if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
    ereport(ERROR,
      (errcode(ERRCODE_UNDEFINED_FUNCTION),
       errmsg("could not identify a comparison function for type %s",
        format_type_be(att->atttypid))));

   cmpFunc = typentry->cmp_proc_finfo.fn_oid;
  }

  PrepareSortSupportComparisonShim(cmpFunc, sortKey);
 }

 base->removeabbrev = removeabbrev_index_gin;
 base->comparetup = comparetup_index_gin;
 base->writetup = writetup_index_gin;
 base->readtup = readtup_index_gin;
 base->haveDatum1 = false;
 base->arg = NULL;

 MemoryContextSwitchTo(oldcontext);

 return state;
}

Tuplesortstate *
tuplesort_begin_datum(Oid datumType, Oid sortOperator, Oid sortCollation,
       bool nullsFirstFlag, int workMem,
       SortCoordinate coordinate, int sortopt)
{
 Tuplesortstate *state = tuplesort_begin_common(workMem, coordinate,
               sortopt);
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortDatumArg *arg;
 MemoryContext oldcontext;
 int16  typlen;
 bool  typbyval;

 oldcontext = MemoryContextSwitchTo(base->maincontext);
 arg = (TuplesortDatumArg *) palloc(sizeof(TuplesortDatumArg));

 if (trace_sort)
  elog(LOG,
    "begin datum sort: workMem = %d, randomAccess = %c",
    workMem, sortopt & TUPLESORT_RANDOMACCESS ? 't' : 'f');

 base->nKeys = 1;   /* always a one-column sort */

 TRACE_POSTGRESQL_SORT_START(DATUM_SORT,
        false/* no unique check */
        1,
        workMem,
        sortopt & TUPLESORT_RANDOMACCESS,
        PARALLEL_SORT(coordinate));

 base->removeabbrev = removeabbrev_datum;
 base->comparetup = comparetup_datum;
 base->comparetup_tiebreak = comparetup_datum_tiebreak;
 base->writetup = writetup_datum;
 base->readtup = readtup_datum;
 base->haveDatum1 = true;
 base->arg = arg;

 arg->datumType = datumType;

 /* lookup necessary attributes of the datum type */
 get_typlenbyval(datumType, &typlen, &typbyval);
 arg->datumTypeLen = typlen;
 base->tuples = !typbyval;

 /* Prepare SortSupport data */
 base->sortKeys = (SortSupport) palloc0(sizeof(SortSupportData));

 base->sortKeys->ssup_cxt = CurrentMemoryContext;
 base->sortKeys->ssup_collation = sortCollation;
 base->sortKeys->ssup_nulls_first = nullsFirstFlag;

 /*
  * Abbreviation is possible here only for by-reference types.  In theory,
  * a pass-by-value datatype could have an abbreviated form that is cheaper
  * to compare.  In a tuple sort, we could support that, because we can
  * always extract the original datum from the tuple as needed.  Here, we
  * can't, because a datum sort only stores a single copy of the datum; the
  * "tuple" field of each SortTuple is NULL.
 */

 base->sortKeys->abbreviate = !typbyval;

 PrepareSortSupportFromOrderingOp(sortOperator, base->sortKeys);

 /*
  * The "onlyKey" optimization cannot be used with abbreviated keys, since
  * tie-breaker comparisons may be required.  Typically, the optimization
  * is only of value to pass-by-value types anyway, whereas abbreviated
  * keys are typically only of value to pass-by-reference types.
 */

 if (!base->sortKeys->abbrev_converter)
  base->onlyKey = base->sortKeys;

 MemoryContextSwitchTo(oldcontext);

 return state;
}

/*
 * Accept one tuple while collecting input data for sort.
 *
 * Note that the input data is always copied; the caller need not save it.
 */

void
tuplesort_puttupleslot(Tuplesortstate *state, TupleTableSlot *slot)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 TupleDesc tupDesc = (TupleDesc) base->arg;
 SortTuple stup;
 MinimalTuple tuple;
 HeapTupleData htup;
 Size  tuplen;

 /* copy the tuple into sort storage */
 tuple = ExecCopySlotMinimalTuple(slot);
 stup.tuple = tuple;
 /* set up first-column key value */
 htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
 htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
 stup.datum1 = heap_getattr(&htup,
          base->sortKeys[0].ssup_attno,
          tupDesc,
          &stup.isnull1);

 /* GetMemoryChunkSpace is not supported for bump contexts */
 if (TupleSortUseBumpTupleCxt(base->sortopt))
  tuplen = MAXALIGN(tuple->t_len);
 else
  tuplen = GetMemoryChunkSpace(tuple);

 tuplesort_puttuple_common(state, &stup,
         base->sortKeys->abbrev_converter &&
         !stup.isnull1, tuplen);

 MemoryContextSwitchTo(oldcontext);
}

/*
 * Accept one tuple while collecting input data for sort.
 *
 * Note that the input data is always copied; the caller need not save it.
 */

void
tuplesort_putheaptuple(Tuplesortstate *state, HeapTuple tup)
{
 SortTuple stup;
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
 Size  tuplen;

 /* copy the tuple into sort storage */
 tup = heap_copytuple(tup);
 stup.tuple = tup;

 /*
  * set up first-column key value, and potentially abbreviate, if it's a
  * simple column
 */

 if (base->haveDatum1)
 {
  stup.datum1 = heap_getattr(tup,
           arg->indexInfo->ii_IndexAttrNumbers[0],
           arg->tupDesc,
           &stup.isnull1);
 }

 /* GetMemoryChunkSpace is not supported for bump contexts */
 if (TupleSortUseBumpTupleCxt(base->sortopt))
  tuplen = MAXALIGN(HEAPTUPLESIZE + tup->t_len);
 else
  tuplen = GetMemoryChunkSpace(tup);

 tuplesort_puttuple_common(state, &stup,
         base->haveDatum1 &&
         base->sortKeys->abbrev_converter &&
         !stup.isnull1, tuplen);

 MemoryContextSwitchTo(oldcontext);
}

/*
 * Collect one index tuple while collecting input data for sort, building
 * it from caller-supplied values.
 */

void
tuplesort_putindextuplevalues(Tuplesortstate *state, Relation rel,
         ItemPointer self, const Datum *values,
         const bool *isnull)
{
 SortTuple stup;
 IndexTuple tuple;
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
 Size  tuplen;

 stup.tuple = index_form_tuple_context(RelationGetDescr(rel), values,
            isnull, base->tuplecontext);
 tuple = ((IndexTuple) stup.tuple);
 tuple->t_tid = *self;
 /* set up first-column key value */
 stup.datum1 = index_getattr(tuple,
        1,
        RelationGetDescr(arg->indexRel),
        &stup.isnull1);

 /* GetMemoryChunkSpace is not supported for bump contexts */
 if (TupleSortUseBumpTupleCxt(base->sortopt))
  tuplen = MAXALIGN(tuple->t_info & INDEX_SIZE_MASK);
 else
  tuplen = GetMemoryChunkSpace(tuple);

 tuplesort_puttuple_common(state, &stup,
         base->sortKeys &&
         base->sortKeys->abbrev_converter &&
         !stup.isnull1, tuplen);
}

/*
 * Collect one BRIN tuple while collecting input data for sort.
 */

void
tuplesort_putbrintuple(Tuplesortstate *state, BrinTuple *tuple, Size size)
{
 SortTuple stup;
 BrinSortTuple *bstup;
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 Size  tuplen;

 /* allocate space for the whole BRIN sort tuple */
 bstup = palloc(BRINSORTTUPLE_SIZE(size));

 bstup->tuplen = size;
 memcpy(&bstup->tuple, tuple, size);

 stup.tuple = bstup;
 stup.datum1 = tuple->bt_blkno;
 stup.isnull1 = false;

 /* GetMemoryChunkSpace is not supported for bump contexts */
 if (TupleSortUseBumpTupleCxt(base->sortopt))
  tuplen = MAXALIGN(BRINSORTTUPLE_SIZE(size));
 else
  tuplen = GetMemoryChunkSpace(bstup);

 tuplesort_puttuple_common(state, &stup,
         base->sortKeys &&
         base->sortKeys->abbrev_converter &&
         !stup.isnull1, tuplen);

 MemoryContextSwitchTo(oldcontext);
}

void
tuplesort_putgintuple(Tuplesortstate *state, GinTuple *tuple, Size size)
{
 SortTuple stup;
 GinTuple   *ctup;
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 Size  tuplen;

 /* copy the GinTuple into the right memory context */
 ctup = palloc(size);
 memcpy(ctup, tuple, size);

 stup.tuple = ctup;
 stup.datum1 = (Datum) 0;
 stup.isnull1 = false;

 /* GetMemoryChunkSpace is not supported for bump contexts */
 if (TupleSortUseBumpTupleCxt(base->sortopt))
  tuplen = MAXALIGN(size);
 else
  tuplen = GetMemoryChunkSpace(ctup);

 tuplesort_puttuple_common(state, &stup,
         base->sortKeys &&
         base->sortKeys->abbrev_converter &&
         !stup.isnull1, tuplen);

 MemoryContextSwitchTo(oldcontext);
}

/*
 * Accept one Datum while collecting input data for sort.
 *
 * If the Datum is pass-by-ref type, the value will be copied.
 */

void
tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->tuplecontext);
 TuplesortDatumArg *arg = (TuplesortDatumArg *) base->arg;
 SortTuple stup;

 /*
  * Pass-by-value types or null values are just stored directly in
  * stup.datum1 (and stup.tuple is not used and set to NULL).
  *
  * Non-null pass-by-reference values need to be copied into memory we
  * control, and possibly abbreviated. The copied value is pointed to by
  * stup.tuple and is treated as the canonical copy (e.g. to return via
  * tuplesort_getdatum or when writing to tape); stup.datum1 gets the
  * abbreviated value if abbreviation is happening, otherwise it's
  * identical to stup.tuple.
 */


 if (isNull || !base->tuples)
 {
  /*
   * Set datum1 to zeroed representation for NULLs (to be consistent,
   * and to support cheap inequality tests for NULL abbreviated keys).
 */

  stup.datum1 = !isNull ? val : (Datum) 0;
  stup.isnull1 = isNull;
  stup.tuple = NULL;  /* no separate storage */
 }
 else
 {
  stup.isnull1 = false;
  stup.datum1 = datumCopy(val, false, arg->datumTypeLen);
  stup.tuple = DatumGetPointer(stup.datum1);
 }

 tuplesort_puttuple_common(state, &stup,
         base->tuples &&
         base->sortKeys->abbrev_converter && !isNull, 0);

 MemoryContextSwitchTo(oldcontext);
}

/*
 * Fetch the next tuple in either forward or back direction.
 * If successful, put tuple in slot and return true; else, clear the slot
 * and return false.
 *
 * Caller may optionally be passed back abbreviated value (on true return
 * value) when abbreviation was used, which can be used to cheaply avoid
 * equality checks that might otherwise be required.  Caller can safely make a
 * determination of "non-equal tuple" based on simple binary inequality.  A
 * NULL value in leading attribute will set abbreviated value to zeroed
 * representation, which caller may rely on in abbreviated inequality check.
 *
 * If copy is true, the slot receives a tuple that's been copied into the
 * caller's memory context, so that it will stay valid regardless of future
 * manipulations of the tuplesort's state (up to and including deleting the
 * tuplesort).  If copy is false, the slot will just receive a pointer to a
 * tuple held within the tuplesort, which is more efficient, but only safe for
 * callers that are prepared to have any subsequent manipulation of the
 * tuplesort's state invalidate slot contents.
 */

bool
tuplesort_gettupleslot(Tuplesortstate *state, bool forward, bool copy,
        TupleTableSlot *slot, Datum *abbrev)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
 SortTuple stup;

 if (!tuplesort_gettuple_common(state, forward, &stup))
  stup.tuple = NULL;

 MemoryContextSwitchTo(oldcontext);

 if (stup.tuple)
 {
  /* Record abbreviated key for caller */
  if (base->sortKeys->abbrev_converter && abbrev)
   *abbrev = stup.datum1;

  if (copy)
   stup.tuple = heap_copy_minimal_tuple((MinimalTuple) stup.tuple, 0);

  ExecStoreMinimalTuple((MinimalTuple) stup.tuple, slot, copy);
  return true;
 }
 else
 {
  ExecClearTuple(slot);
  return false;
 }
}

/*
 * Fetch the next tuple in either forward or back direction.
 * Returns NULL if no more tuples.  Returned tuple belongs to tuplesort memory
 * context, and must not be freed by caller.  Caller may not rely on tuple
 * remaining valid after any further manipulation of tuplesort.
 */

HeapTuple
tuplesort_getheaptuple(Tuplesortstate *state, bool forward)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
 SortTuple stup;

 if (!tuplesort_gettuple_common(state, forward, &stup))
  stup.tuple = NULL;

 MemoryContextSwitchTo(oldcontext);

 return stup.tuple;
}

/*
 * Fetch the next index tuple in either forward or back direction.
 * Returns NULL if no more tuples.  Returned tuple belongs to tuplesort memory
 * context, and must not be freed by caller.  Caller may not rely on tuple
 * remaining valid after any further manipulation of tuplesort.
 */

IndexTuple
tuplesort_getindextuple(Tuplesortstate *state, bool forward)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
 SortTuple stup;

 if (!tuplesort_gettuple_common(state, forward, &stup))
  stup.tuple = NULL;

 MemoryContextSwitchTo(oldcontext);

 return (IndexTuple) stup.tuple;
}

/*
 * Fetch the next BRIN tuple in either forward or back direction.
 * Returns NULL if no more tuples.  Returned tuple belongs to tuplesort memory
 * context, and must not be freed by caller.  Caller may not rely on tuple
 * remaining valid after any further manipulation of tuplesort.
 */

BrinTuple *
tuplesort_getbrintuple(Tuplesortstate *state, Size *len, bool forward)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
 SortTuple stup;
 BrinSortTuple *btup;

 if (!tuplesort_gettuple_common(state, forward, &stup))
  stup.tuple = NULL;

 MemoryContextSwitchTo(oldcontext);

 if (!stup.tuple)
  return NULL;

 btup = (BrinSortTuple *) stup.tuple;

 *len = btup->tuplen;

 return &btup->tuple;
}

GinTuple *
tuplesort_getgintuple(Tuplesortstate *state, Size *len, bool forward)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
 SortTuple stup;
 GinTuple   *tup;

 if (!tuplesort_gettuple_common(state, forward, &stup))
  stup.tuple = NULL;

 MemoryContextSwitchTo(oldcontext);

 if (!stup.tuple)
  return NULL;

 tup = (GinTuple *) stup.tuple;

 *len = tup->tuplen;

 return tup;
}

/*
 * Fetch the next Datum in either forward or back direction.
 * Returns false if no more datums.
 *
 * If the Datum is pass-by-ref type, the returned value is freshly palloc'd
 * in caller's context, and is now owned by the caller (this differs from
 * similar routines for other types of tuplesorts).
 *
 * Caller may optionally be passed back abbreviated value (on true return
 * value) when abbreviation was used, which can be used to cheaply avoid
 * equality checks that might otherwise be required.  Caller can safely make a
 * determination of "non-equal tuple" based on simple binary inequality.  A
 * NULL value will have a zeroed abbreviated value representation, which caller
 * may rely on in abbreviated inequality check.
 *
 * For byref Datums, if copy is true, *val is set to a copy of the Datum
 * copied into the caller's memory context, so that it will stay valid
 * regardless of future manipulations of the tuplesort's state (up to and
 * including deleting the tuplesort).  If copy is false, *val will just be
 * set to a pointer to the Datum held within the tuplesort, which is more
 * efficient, but only safe for callers that are prepared to have any
 * subsequent manipulation of the tuplesort's state invalidate slot contents.
 * For byval Datums, the value of the 'copy' parameter has no effect.

 */

bool
tuplesort_getdatum(Tuplesortstate *state, bool forward, bool copy,
       Datum *val, bool *isNull, Datum *abbrev)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MemoryContext oldcontext = MemoryContextSwitchTo(base->sortcontext);
 TuplesortDatumArg *arg = (TuplesortDatumArg *) base->arg;
 SortTuple stup;

 if (!tuplesort_gettuple_common(state, forward, &stup))
 {
  MemoryContextSwitchTo(oldcontext);
  return false;
 }

 /* Ensure we copy into caller's memory context */
 MemoryContextSwitchTo(oldcontext);

 /* Record abbreviated key for caller */
 if (base->sortKeys->abbrev_converter && abbrev)
  *abbrev = stup.datum1;

 if (stup.isnull1 || !base->tuples)
 {
  *val = stup.datum1;
  *isNull = stup.isnull1;
 }
 else
 {
  /* use stup.tuple because stup.datum1 may be an abbreviation */
  if (copy)
   *val = datumCopy(PointerGetDatum(stup.tuple), false,
        arg->datumTypeLen);
  else
   *val = PointerGetDatum(stup.tuple);
  *isNull = false;
 }

 return true;
}


/*
 * Routines specialized for HeapTuple (actually MinimalTuple) case
 */


static void
removeabbrev_heap(Tuplesortstate *state, SortTuple *stups, int count)
{
 int   i;
 TuplesortPublic *base = TuplesortstateGetPublic(state);

 for (i = 0; i < count; i++)
 {
  HeapTupleData htup;

  htup.t_len = ((MinimalTuple) stups[i].tuple)->t_len +
   MINIMAL_TUPLE_OFFSET;
  htup.t_data = (HeapTupleHeader) ((char *) stups[i].tuple -
           MINIMAL_TUPLE_OFFSET);
  stups[i].datum1 = heap_getattr(&htup,
            base->sortKeys[0].ssup_attno,
            (TupleDesc) base->arg,
            &stups[i].isnull1);
 }
}

static int
comparetup_heap(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 SortSupport sortKey = base->sortKeys;
 int32  compare;


 /* Compare the leading sort key */
 compare = ApplySortComparator(a->datum1, a->isnull1,
          b->datum1, b->isnull1,
          sortKey);
 if (compare != 0)
  return compare;

 /* Compare additional sort keys */
 return comparetup_heap_tiebreak(a, b, state);
}

static int
comparetup_heap_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 SortSupport sortKey = base->sortKeys;
 HeapTupleData ltup;
 HeapTupleData rtup;
 TupleDesc tupDesc;
 int   nkey;
 int32  compare;
 AttrNumber attno;
 Datum  datum1,
    datum2;
 bool  isnull1,
    isnull2;

 ltup.t_len = ((MinimalTuple) a->tuple)->t_len + MINIMAL_TUPLE_OFFSET;
 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 = (TupleDesc) base->arg;

 if (sortKey->abbrev_converter)
 {
  attno = sortKey->ssup_attno;

  datum1 = heap_getattr(<up, attno, tupDesc, &isnull1);
  datum2 = heap_getattr(&rtup, attno, tupDesc, &isnull2);

  compare = ApplySortAbbrevFullComparator(datum1, isnull1,
            datum2, isnull2,
            sortKey);
  if (compare != 0)
   return compare;
 }

 sortKey++;
 for (nkey = 1; nkey < base->nKeys; nkey++, sortKey++)
 {
  attno = sortKey->ssup_attno;

  datum1 = heap_getattr(<up, attno, tupDesc, &isnull1);
  datum2 = heap_getattr(&rtup, attno, tupDesc, &isnull2);

  compare = ApplySortComparator(datum1, isnull1,
           datum2, isnull2,
           sortKey);
  if (compare != 0)
   return compare;
 }

 return 0;
}

static void
writetup_heap(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 MinimalTuple tuple = (MinimalTuple) stup->tuple;

 /* the part of the MinimalTuple we'll write: */
 char    *tupbody = (char *) tuple + MINIMAL_TUPLE_DATA_OFFSET;
 unsigned int tupbodylen = tuple->t_len - MINIMAL_TUPLE_DATA_OFFSET;

 /* total on-disk footprint: */
 unsigned int tuplen = tupbodylen + sizeof(int);

 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
 LogicalTapeWrite(tape, tupbody, tupbodylen);
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
}

static void
readtup_heap(Tuplesortstate *state, SortTuple *stup,
    LogicalTape *tape, unsigned int len)
{
 unsigned int tupbodylen = len - sizeof(int);
 unsigned int tuplen = tupbodylen + MINIMAL_TUPLE_DATA_OFFSET;
 MinimalTuple tuple = (MinimalTuple) tuplesort_readtup_alloc(state, tuplen);
 char    *tupbody = (char *) tuple + MINIMAL_TUPLE_DATA_OFFSET;
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 HeapTupleData htup;

 /* read in the tuple proper */
 tuple->t_len = tuplen;
 LogicalTapeReadExact(tape, tupbody, tupbodylen);
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
 stup->tuple = tuple;
 /* set up first-column key value */
 htup.t_len = tuple->t_len + MINIMAL_TUPLE_OFFSET;
 htup.t_data = (HeapTupleHeader) ((char *) tuple - MINIMAL_TUPLE_OFFSET);
 stup->datum1 = heap_getattr(&htup,
        base->sortKeys[0].ssup_attno,
        (TupleDesc) base->arg,
        &stup->isnull1);
}

/*
 * Routines specialized for the CLUSTER case (HeapTuple data, with
 * comparisons per a btree index definition)
 */


static void
removeabbrev_cluster(Tuplesortstate *state, SortTuple *stups, int count)
{
 int   i;
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;

 for (i = 0; i < count; i++)
 {
  HeapTuple tup;

  tup = (HeapTuple) stups[i].tuple;
  stups[i].datum1 = heap_getattr(tup,
            arg->indexInfo->ii_IndexAttrNumbers[0],
            arg->tupDesc,
            &stups[i].isnull1);
 }
}

static int
comparetup_cluster(const SortTuple *a, const SortTuple *b,
       Tuplesortstate *state)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 SortSupport sortKey = base->sortKeys;
 int32  compare;

 /* Compare the leading sort key, if it's simple */
 if (base->haveDatum1)
 {
  compare = ApplySortComparator(a->datum1, a->isnull1,
           b->datum1, b->isnull1,
           sortKey);
  if (compare != 0)
   return compare;
 }

 return comparetup_cluster_tiebreak(a, b, state);
}

static int
comparetup_cluster_tiebreak(const SortTuple *a, const SortTuple *b,
       Tuplesortstate *state)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
 SortSupport sortKey = base->sortKeys;
 HeapTuple ltup;
 HeapTuple rtup;
 TupleDesc tupDesc;
 int   nkey;
 int32  compare = 0;
 Datum  datum1,
    datum2;
 bool  isnull1,
    isnull2;

 ltup = (HeapTuple) a->tuple;
 rtup = (HeapTuple) b->tuple;
 tupDesc = arg->tupDesc;

 /* Compare the leading sort key, if it's simple */
 if (base->haveDatum1)
 {
  if (sortKey->abbrev_converter)
  {
   AttrNumber leading = arg->indexInfo->ii_IndexAttrNumbers[0];

   datum1 = heap_getattr(ltup, leading, tupDesc, &isnull1);
   datum2 = heap_getattr(rtup, leading, tupDesc, &isnull2);

   compare = ApplySortAbbrevFullComparator(datum1, isnull1,
             datum2, isnull2,
             sortKey);
  }
  if (compare != 0 || base->nKeys == 1)
   return compare;
  /* Compare additional columns the hard way */
  sortKey++;
  nkey = 1;
 }
 else
 {
  /* Must compare all keys the hard way */
  nkey = 0;
 }

 if (arg->indexInfo->ii_Expressions == NULL)
 {
  /* If not expression index, just compare the proper heap attrs */

  for (; nkey < base->nKeys; nkey++, sortKey++)
  {
   AttrNumber attno = arg->indexInfo->ii_IndexAttrNumbers[nkey];

   datum1 = heap_getattr(ltup, attno, tupDesc, &isnull1);
   datum2 = heap_getattr(rtup, attno, tupDesc, &isnull2);

   compare = ApplySortComparator(datum1, isnull1,
            datum2, isnull2,
            sortKey);
   if (compare != 0)
    return compare;
  }
 }
 else
 {
  /*
   * In the expression index case, compute the whole index tuple and
   * then compare values.  It would perhaps be faster to compute only as
   * many columns as we need to compare, but that would require
   * duplicating all the logic in FormIndexDatum.
 */

  Datum  l_index_values[INDEX_MAX_KEYS];
  bool  l_index_isnull[INDEX_MAX_KEYS];
  Datum  r_index_values[INDEX_MAX_KEYS];
  bool  r_index_isnull[INDEX_MAX_KEYS];
  TupleTableSlot *ecxt_scantuple;

  /* Reset context each time to prevent memory leakage */
  ResetPerTupleExprContext(arg->estate);

  ecxt_scantuple = GetPerTupleExprContext(arg->estate)->ecxt_scantuple;

  ExecStoreHeapTuple(ltup, ecxt_scantuple, false);
  FormIndexDatum(arg->indexInfo, ecxt_scantuple, arg->estate,
        l_index_values, l_index_isnull);

  ExecStoreHeapTuple(rtup, ecxt_scantuple, false);
  FormIndexDatum(arg->indexInfo, ecxt_scantuple, arg->estate,
        r_index_values, r_index_isnull);

  for (; nkey < base->nKeys; nkey++, sortKey++)
  {
   compare = ApplySortComparator(l_index_values[nkey],
            l_index_isnull[nkey],
            r_index_values[nkey],
            r_index_isnull[nkey],
            sortKey);
   if (compare != 0)
    return compare;
  }
 }

 return 0;
}

static void
writetup_cluster(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 HeapTuple tuple = (HeapTuple) stup->tuple;
 unsigned int tuplen = tuple->t_len + sizeof(ItemPointerData) + sizeof(int);

 /* We need to store t_self, but not other fields of HeapTupleData */
 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
 LogicalTapeWrite(tape, &tuple->t_self, sizeof(ItemPointerData));
 LogicalTapeWrite(tape, tuple->t_data, tuple->t_len);
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
}

static void
readtup_cluster(Tuplesortstate *state, SortTuple *stup,
    LogicalTape *tape, unsigned int tuplen)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortClusterArg *arg = (TuplesortClusterArg *) base->arg;
 unsigned int t_len = tuplen - sizeof(ItemPointerData) - sizeof(int);
 HeapTuple tuple = (HeapTuple) tuplesort_readtup_alloc(state,
               t_len + HEAPTUPLESIZE);

 /* Reconstruct the HeapTupleData header */
 tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
 tuple->t_len = t_len;
 LogicalTapeReadExact(tape, &tuple->t_self, sizeof(ItemPointerData));
 /* We don't currently bother to reconstruct t_tableOid */
 tuple->t_tableOid = InvalidOid;
 /* Read in the tuple body */
 LogicalTapeReadExact(tape, tuple->t_data, tuple->t_len);
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
 stup->tuple = tuple;
 /* set up first-column key value, if it's a simple column */
 if (base->haveDatum1)
  stup->datum1 = heap_getattr(tuple,
         arg->indexInfo->ii_IndexAttrNumbers[0],
         arg->tupDesc,
         &stup->isnull1);
}

static void
freestate_cluster(Tuplesortstate *state)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortClusterArg *arg = (TuplesortClusterArg *) base->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
 *
 * The btree and hash cases require separate comparison functions, but the
 * IndexTuple representation is the same so the copy/write/read support
 * functions can be shared.
 */


static void
removeabbrev_index(Tuplesortstate *state, SortTuple *stups, int count)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
 int   i;

 for (i = 0; i < count; i++)
 {
  IndexTuple tuple;

  tuple = stups[i].tuple;
  stups[i].datum1 = index_getattr(tuple,
          1,
          RelationGetDescr(arg->indexRel),
          &stups[i].isnull1);
 }
}

static int
comparetup_index_btree(const SortTuple *a, const SortTuple *b,
        Tuplesortstate *state)
{
 /*
  * This is similar to comparetup_heap(), but expects index tuples.  There
  * is also special handling for enforcing uniqueness, and special
  * treatment for equal keys at the end.
 */

 TuplesortPublic *base = TuplesortstateGetPublic(state);
 SortSupport sortKey = base->sortKeys;
 int32  compare;

 /* Compare the leading sort key */
 compare = ApplySortComparator(a->datum1, a->isnull1,
          b->datum1, b->isnull1,
          sortKey);
 if (compare != 0)
  return compare;

 /* Compare additional sort keys */
 return comparetup_index_btree_tiebreak(a, b, state);
}

static int
comparetup_index_btree_tiebreak(const SortTuple *a, const SortTuple *b,
        Tuplesortstate *state)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortIndexBTreeArg *arg = (TuplesortIndexBTreeArg *) base->arg;
 SortSupport sortKey = base->sortKeys;
 IndexTuple tuple1;
 IndexTuple tuple2;
 int   keysz;
 TupleDesc tupDes;
 bool  equal_hasnull = false;
 int   nkey;
 int32  compare;
 Datum  datum1,
    datum2;
 bool  isnull1,
    isnull2;

 tuple1 = (IndexTuple) a->tuple;
 tuple2 = (IndexTuple) b->tuple;
 keysz = base->nKeys;
 tupDes = RelationGetDescr(arg->index.indexRel);

 if (sortKey->abbrev_converter)
 {
  datum1 = index_getattr(tuple1, 1, tupDes, &isnull1);
  datum2 = index_getattr(tuple2, 1, tupDes, &isnull2);

  compare = ApplySortAbbrevFullComparator(datum1, isnull1,
            datum2, isnull2,
            sortKey);
  if (compare != 0)
   return compare;
 }

 /* they are equal, so we only need to examine one null flag */
 if (a->isnull1)
  equal_hasnull = true;

 sortKey++;
 for (nkey = 2; nkey <= keysz; nkey++, sortKey++)
 {
  datum1 = index_getattr(tuple1, nkey, tupDes, &isnull1);
  datum2 = index_getattr(tuple2, nkey, tupDes, &isnull2);

  compare = ApplySortComparator(datum1, isnull1,
           datum2, isnull2,
           sortKey);
  if (compare != 0)
   return compare;  /* done when we find unequal attributes */

  /* they are equal, so we only need to examine one null flag */
  if (isnull1)
   equal_hasnull = true;
 }

 /*
  * If btree has asked us to enforce uniqueness, complain if two equal
  * tuples are detected (unless there was at least one NULL field and NULLS
  * NOT DISTINCT was not set).
  *
  * It is sufficient to make the test here, because if two tuples are equal
  * they *must* get compared at some stage of the sort --- otherwise the
  * sort algorithm wouldn't have checked whether one must appear before the
  * other.
 */

 if (arg->enforceUnique && !(!arg->uniqueNullsNotDistinct && equal_hasnull))
 {
  Datum  values[INDEX_MAX_KEYS];
  bool  isnull[INDEX_MAX_KEYS];
  char    *key_desc;

  /*
   * Some rather brain-dead implementations of qsort (such as the one in
   * QNX 4) will sometimes call the comparison routine to compare a
   * value to itself, but we always use our own implementation, which
   * does not.
 */

  Assert(tuple1 != tuple2);

  index_deform_tuple(tuple1, tupDes, values, isnull);

  key_desc = BuildIndexValueDescription(arg->index.indexRel, values, isnull);

  ereport(ERROR,
    (errcode(ERRCODE_UNIQUE_VIOLATION),
     errmsg("could not create unique index \"%s\"",
      RelationGetRelationName(arg->index.indexRel)),
     key_desc ? errdetail("Key %s is duplicated.", key_desc) :
     errdetail("Duplicate keys exist."),
     errtableconstraint(arg->index.heapRel,
         RelationGetRelationName(arg->index.indexRel))));
 }

 /*
  * If key values are equal, we sort on ItemPointer.  This is required for
  * btree indexes, since heap TID is treated as an implicit last key
  * attribute in order to ensure that all keys in the index are physically
  * unique.
 */

 {
  BlockNumber blk1 = ItemPointerGetBlockNumber(&tuple1->t_tid);
  BlockNumber blk2 = ItemPointerGetBlockNumber(&tuple2->t_tid);

  if (blk1 != blk2)
   return (blk1 < blk2) ? -1 : 1;
 }
 {
  OffsetNumber pos1 = ItemPointerGetOffsetNumber(&tuple1->t_tid);
  OffsetNumber pos2 = ItemPointerGetOffsetNumber(&tuple2->t_tid);

  if (pos1 != pos2)
   return (pos1 < pos2) ? -1 : 1;
 }

 /* ItemPointer values should never be equal */
 Assert(false);

 return 0;
}

static int
comparetup_index_hash(const SortTuple *a, const SortTuple *b,
       Tuplesortstate *state)
{
 Bucket  bucket1;
 Bucket  bucket2;
 uint32  hash1;
 uint32  hash2;
 IndexTuple tuple1;
 IndexTuple tuple2;
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortIndexHashArg *arg = (TuplesortIndexHashArg *) base->arg;

 /*
  * Fetch hash keys and mask off bits we don't want to sort by, so that the
  * initial sort is just on the bucket number.  We know that the first
  * column of the index tuple is the hash key.
 */

 Assert(!a->isnull1);
 bucket1 = _hash_hashkey2bucket(DatumGetUInt32(a->datum1),
           arg->max_buckets, arg->high_mask,
           arg->low_mask);
 Assert(!b->isnull1);
 bucket2 = _hash_hashkey2bucket(DatumGetUInt32(b->datum1),
           arg->max_buckets, arg->high_mask,
           arg->low_mask);
 if (bucket1 > bucket2)
  return 1;
 else if (bucket1 < bucket2)
  return -1;

 /*
  * If bucket values are equal, sort by hash values.  This allows us to
  * insert directly onto bucket/overflow pages, where the index tuples are
  * stored in hash order to allow fast binary search within each page.
 */

 hash1 = DatumGetUInt32(a->datum1);
 hash2 = DatumGetUInt32(b->datum1);
 if (hash1 > hash2)
  return 1;
 else if (hash1 < hash2)
  return -1;

 /*
  * If hash values are equal, we sort on ItemPointer.  This does not affect
  * validity of the finished index, but it may be useful to have index
  * scans in physical order.
 */

 tuple1 = (IndexTuple) a->tuple;
 tuple2 = (IndexTuple) b->tuple;

 {
  BlockNumber blk1 = ItemPointerGetBlockNumber(&tuple1->t_tid);
  BlockNumber blk2 = ItemPointerGetBlockNumber(&tuple2->t_tid);

  if (blk1 != blk2)
   return (blk1 < blk2) ? -1 : 1;
 }
 {
  OffsetNumber pos1 = ItemPointerGetOffsetNumber(&tuple1->t_tid);
  OffsetNumber pos2 = ItemPointerGetOffsetNumber(&tuple2->t_tid);

  if (pos1 != pos2)
   return (pos1 < pos2) ? -1 : 1;
 }

 /* ItemPointer values should never be equal */
 Assert(false);

 return 0;
}

/*
 * Sorting for hash indexes only uses one sort key, so this shouldn't ever be
 * called. It's only here for consistency.
 */

static int
comparetup_index_hash_tiebreak(const SortTuple *a, const SortTuple *b,
          Tuplesortstate *state)
{
 Assert(false);

 return 0;
}

static void
writetup_index(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 IndexTuple tuple = (IndexTuple) stup->tuple;
 unsigned int tuplen;

 tuplen = IndexTupleSize(tuple) + sizeof(tuplen);
 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
 LogicalTapeWrite(tape, tuple, IndexTupleSize(tuple));
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
}

static void
readtup_index(Tuplesortstate *state, SortTuple *stup,
     LogicalTape *tape, unsigned int len)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortIndexArg *arg = (TuplesortIndexArg *) base->arg;
 unsigned int tuplen = len - sizeof(unsigned int);
 IndexTuple tuple = (IndexTuple) tuplesort_readtup_alloc(state, tuplen);

 LogicalTapeReadExact(tape, tuple, tuplen);
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
 stup->tuple = tuple;
 /* set up first-column key value */
 stup->datum1 = index_getattr(tuple,
         1,
         RelationGetDescr(arg->indexRel),
         &stup->isnull1);
}

/*
 * Routines specialized for BrinTuple case
 */


static void
removeabbrev_index_brin(Tuplesortstate *state, SortTuple *stups, int count)
{
 int   i;

 for (i = 0; i < count; i++)
 {
  BrinSortTuple *tuple;

  tuple = stups[i].tuple;
  stups[i].datum1 = tuple->tuple.bt_blkno;
 }
}

static int
comparetup_index_brin(const SortTuple *a, const SortTuple *b,
       Tuplesortstate *state)
{
 Assert(TuplesortstateGetPublic(state)->haveDatum1);

 if (DatumGetUInt32(a->datum1) > DatumGetUInt32(b->datum1))
  return 1;

 if (DatumGetUInt32(a->datum1) < DatumGetUInt32(b->datum1))
  return -1;

 /* silence compilers */
 return 0;
}

static void
writetup_index_brin(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 BrinSortTuple *tuple = (BrinSortTuple *) stup->tuple;
 unsigned int tuplen = tuple->tuplen;

 tuplen = tuplen + sizeof(tuplen);
 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
 LogicalTapeWrite(tape, &tuple->tuple, tuple->tuplen);
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
}

static void
readtup_index_brin(Tuplesortstate *state, SortTuple *stup,
       LogicalTape *tape, unsigned int len)
{
 BrinSortTuple *tuple;
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 unsigned int tuplen = len - sizeof(unsigned int);

 /*
  * Allocate space for the BRIN sort tuple, which is BrinTuple with an
  * extra length field.
 */

 tuple = (BrinSortTuple *) tuplesort_readtup_alloc(state,
               BRINSORTTUPLE_SIZE(tuplen));

 tuple->tuplen = tuplen;

 LogicalTapeReadExact(tape, &tuple->tuple, tuplen);
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
 stup->tuple = tuple;

 /* set up first-column key value, which is block number */
 stup->datum1 = tuple->tuple.bt_blkno;
}

/*
 * Routines specialized for GIN case
 */


static void
removeabbrev_index_gin(Tuplesortstate *state, SortTuple *stups, int count)
{
 Assert(false);
 elog(ERROR, "removeabbrev_index_gin not implemented");
}

static int
comparetup_index_gin(const SortTuple *a, const SortTuple *b,
      Tuplesortstate *state)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);

 Assert(!TuplesortstateGetPublic(state)->haveDatum1);

 return _gin_compare_tuples((GinTuple *) a->tuple,
          (GinTuple *) b->tuple,
          base->sortKeys);
}

static void
writetup_index_gin(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 GinTuple   *tuple = (GinTuple *) stup->tuple;
 unsigned int tuplen = tuple->tuplen;

 tuplen = tuplen + sizeof(tuplen);
 LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
 LogicalTapeWrite(tape, tuple, tuple->tuplen);
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
}

static void
readtup_index_gin(Tuplesortstate *state, SortTuple *stup,
      LogicalTape *tape, unsigned int len)
{
 GinTuple   *tuple;
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 unsigned int tuplen = len - sizeof(unsigned int);

 /*
  * Allocate space for the GIN sort tuple, which already has the proper
  * length included in the header.
 */

 tuple = (GinTuple *) tuplesort_readtup_alloc(state, tuplen);

 tuple->tuplen = tuplen;

 LogicalTapeReadExact(tape, tuple, tuplen);
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
 stup->tuple = (void *) tuple;

 /* no abbreviations (FIXME maybe use attrnum for this?) */
 stup->datum1 = (Datum) 0;
}

/*
 * Routines specialized for DatumTuple case
 */


static void
removeabbrev_datum(Tuplesortstate *state, SortTuple *stups, int count)
{
 int   i;

 for (i = 0; i < count; i++)
  stups[i].datum1 = PointerGetDatum(stups[i].tuple);
}

static int
comparetup_datum(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 int   compare;

 compare = ApplySortComparator(a->datum1, a->isnull1,
          b->datum1, b->isnull1,
          base->sortKeys);
 if (compare != 0)
  return compare;

 return comparetup_datum_tiebreak(a, b, state);
}

static int
comparetup_datum_tiebreak(const SortTuple *a, const SortTuple *b, Tuplesortstate *state)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 int32  compare = 0;

 /* if we have abbreviations, then "tuple" has the original value */
 if (base->sortKeys->abbrev_converter)
  compare = ApplySortAbbrevFullComparator(PointerGetDatum(a->tuple), a->isnull1,
            PointerGetDatum(b->tuple), b->isnull1,
            base->sortKeys);

 return compare;
}

static void
writetup_datum(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 TuplesortDatumArg *arg = (TuplesortDatumArg *) base->arg;
 void    *waddr;
 unsigned int tuplen;
 unsigned int writtenlen;

 if (stup->isnull1)
 {
  waddr = NULL;
  tuplen = 0;
 }
 else if (!base->tuples)
 {
  waddr = &stup->datum1;
  tuplen = sizeof(Datum);
 }
 else
 {
  waddr = stup->tuple;
  tuplen = datumGetSize(PointerGetDatum(stup->tuple), false, arg->datumTypeLen);
  Assert(tuplen != 0);
 }

 writtenlen = tuplen + sizeof(unsigned int);

 LogicalTapeWrite(tape, &writtenlen, sizeof(writtenlen));
 LogicalTapeWrite(tape, waddr, tuplen);
 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeWrite(tape, &writtenlen, sizeof(writtenlen));
}

static void
readtup_datum(Tuplesortstate *state, SortTuple *stup,
     LogicalTape *tape, unsigned int len)
{
 TuplesortPublic *base = TuplesortstateGetPublic(state);
 unsigned int tuplen = len - sizeof(unsigned int);

 if (tuplen == 0)
 {
  /* it's NULL */
  stup->datum1 = (Datum) 0;
  stup->isnull1 = true;
  stup->tuple = NULL;
 }
 else if (!base->tuples)
 {
  Assert(tuplen == sizeof(Datum));
  LogicalTapeReadExact(tape, &stup->datum1, tuplen);
  stup->isnull1 = false;
  stup->tuple = NULL;
 }
 else
 {
  void    *raddr = tuplesort_readtup_alloc(state, tuplen);

  LogicalTapeReadExact(tape, raddr, tuplen);
  stup->datum1 = PointerGetDatum(raddr);
  stup->isnull1 = false;
  stup->tuple = raddr;
 }

 if (base->sortopt & TUPLESORT_RANDOMACCESS) /* need trailing length word? */
  LogicalTapeReadExact(tape, &tuplen, sizeof(tuplen));
}

Messung V0.5 in Prozent
C=95 H=97 G=95

¤ Dauer der Verarbeitung: 0.311 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.