Index: src/backend/utils/sort/tuplesort.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v
retrieving revision 1.59
diff -c -r1.59 tuplesort.c
*** src/backend/utils/sort/tuplesort.c	19 Feb 2006 19:59:53 -0000	1.59
--- src/backend/utils/sort/tuplesort.c	20 Feb 2006 02:23:06 -0000
***************
*** 120,125 ****
--- 120,158 ----
  
  
  /*
+  * The objects we actually sort are SortTuple structs.  These contain
+  * a pointer to the tuple proper (might be a HeapTuple or IndexTuple),
+  * which is a separate palloc chunk --- we assume this is just one chunk
+  * and can be freed by a simple pfree().  SortTuples also contain the tuple's
+  * first key column in Datum/nullflag format, and an index integer.
+  *
+  * Storing the first key column lets us save heap_getattr or index_getattr
+  * calls during tuple comparisons.  We could extract and save all the key
+  * columns not just the first, but this would increase code complexity and
+  * overhead, and wouldn't actually save any comparison cycles in the common
+  * case where the first key determines the comparison result.
+  *
+  * When sorting single Datums, the "tuple" field is unused; the data value
+  * is represented entirely by datum1/isnull1.  (datum1 will point to a
+  * separately palloc'd data value when the datatype is pass-by-reference
+  * and isnull1 is false.)
+  *
+  * While building initial runs, tupindex holds the tuple's run number.  During
+  * merge passes, we re-use it to hold the input tape number that each tuple in
+  * the heap was read from, or to hold the index of the next tuple pre-read
+  * from the same tape in the case of pre-read entries.  tupindex goes unused
+  * if the sort occurs entirely in memory.
+  */
+ typedef struct
+ {
+ 	void	   *tuple;			/* the tuple proper */
+ 	Datum		datum1;			/* value of first key column */
+ 	bool		isnull1;		/* is first key column NULL? */
+ 	int			tupindex;		/* see notes above */
+ } SortTuple;
+ 
+ 
+ /*
   * Possible states of a Tuplesort object.  These denote the states that
   * persist between calls of Tuplesort routines.
   */
***************
*** 152,157 ****
--- 185,191 ----
  struct Tuplesortstate
  {
  	TupSortStatus status;		/* enumerated value as shown above */
+ 	int			nKeys;			/* number of columns in sort key */
  	bool		randomAccess;	/* did caller request random access? */
  	long		availMem;		/* remaining memory available, in bytes */
  	long		allowedMem;		/* total memory allowed, in bytes */
***************
*** 168,202 ****
  	 *
  	 * <0, 0, >0 according as a<b, a=b, a>b.
  	 */
! 	int			(*comparetup) (Tuplesortstate *state, const void *a, const void *b);
  
  	/*
! 	 * Function to copy a supplied input tuple into palloc'd space. (NB: we
! 	 * assume that a single pfree() is enough to release the tuple later, so
! 	 * the representation must be "flat" in one palloc chunk.) state->availMem
! 	 * must be decreased by the amount of space used.
  	 */
! 	void	   *(*copytup) (Tuplesortstate *state, void *tup);
  
  	/*
  	 * Function to write a stored tuple onto tape.	The representation of the
  	 * tuple on tape need not be the same as it is in memory; requirements on
  	 * the tape representation are given below.  After writing the tuple,
! 	 * pfree() it, and increase state->availMem by the amount of memory space
! 	 * thereby released.
  	 */
! 	void		(*writetup) (Tuplesortstate *state, int tapenum, void *tup);
  
  	/*
  	 * Function to read a stored tuple from tape back into memory. 'len' is
! 	 * the already-read length of the stored tuple.  Create and return a
! 	 * palloc'd copy, and decrease state->availMem by the amount of memory
! 	 * space consumed.
  	 */
! 	void	   *(*readtup) (Tuplesortstate *state, int tapenum, unsigned int len);
  
  	/*
! 	 * This array holds pointers to tuples in sort memory.	If we are in state
  	 * INITIAL, the tuples are in no particular order; if we are in state
  	 * SORTEDINMEM, the tuples are in final sorted order; in states BUILDRUNS
  	 * and FINALMERGE, the tuples are organized in "heap" order per Algorithm
--- 202,239 ----
  	 *
  	 * <0, 0, >0 according as a<b, a=b, a>b.
  	 */
! 	int			(*comparetup) (Tuplesortstate *state,
! 							   const SortTuple *a, const SortTuple *b);
  
  	/*
! 	 * Function to copy a supplied input tuple into palloc'd space and set up
! 	 * its SortTuple representation (ie, set tuple/datum1/isnull1).  Also,
! 	 * state->availMem must be decreased by the amount of space used for the
! 	 * tuple copy (note the SortTuple struct itself is not counted).
  	 */
! 	void		(*copytup) (Tuplesortstate *state, SortTuple *stup, void *tup);
  
  	/*
  	 * Function to write a stored tuple onto tape.	The representation of the
  	 * tuple on tape need not be the same as it is in memory; requirements on
  	 * the tape representation are given below.  After writing the tuple,
! 	 * pfree() the out-of-line data (not the SortTuple struct!), and increase
! 	 * state->availMem by the amount of memory space thereby released.
  	 */
! 	void		(*writetup) (Tuplesortstate *state, int tapenum,
! 							 SortTuple *stup);
  
  	/*
  	 * Function to read a stored tuple from tape back into memory. 'len' is
! 	 * the already-read length of the stored tuple.  Create a palloc'd copy,
! 	 * initialize tuple/datum1/isnull1 in the target SortTuple struct,
! 	 * and decrease state->availMem by the amount of memory space consumed.
  	 */
! 	void		(*readtup) (Tuplesortstate *state, SortTuple *stup,
! 							int tapenum, unsigned int len);
  
  	/*
! 	 * 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
  	 * SORTEDINMEM, the tuples are in final sorted order; in states BUILDRUNS
  	 * and FINALMERGE, the tuples are organized in "heap" order per Algorithm
***************
*** 205,226 ****
  	 * never in the heap and are used to hold pre-read tuples.)  In state
  	 * SORTEDONTAPE, the array is not used.
  	 */
! 	void	  **memtuples;		/* array of pointers to palloc'd tuples */
  	int			memtupcount;	/* number of tuples currently present */
  	int			memtupsize;		/* allocated length of memtuples array */
  
  	/*
- 	 * While building initial runs, this array holds the run number for each
- 	 * tuple in memtuples[].  During merge passes, we re-use it to hold the
- 	 * input tape number that each tuple in the heap was read from, or to hold
- 	 * the index of the next tuple pre-read from the same tape in the case of
- 	 * pre-read entries.  This array is never allocated unless we need to use
- 	 * tapes.  Whenever it is allocated, it has the same length as
- 	 * memtuples[].
- 	 */
- 	int		   *memtupindex;	/* index value associated with memtuples[i] */
- 
- 	/*
  	 * While building initial runs, this is the current output run number
  	 * (starting at 0).  Afterwards, it is the number of initial runs we made.
  	 */
--- 242,252 ----
  	 * never in the heap and are used to hold pre-read tuples.)  In state
  	 * SORTEDONTAPE, the array is not used.
  	 */
! 	SortTuple  *memtuples;		/* array of SortTuple structs */
  	int			memtupcount;	/* number of tuples currently present */
  	int			memtupsize;		/* allocated length of memtuples array */
  
  	/*
  	 * While building initial runs, this is the current output run number
  	 * (starting at 0).  Afterwards, it is the number of initial runs we made.
  	 */
***************
*** 237,249 ****
  	 * have not yet exhausted that run.  mergenext[i] is the memtuples index
  	 * of the next pre-read tuple (next to be loaded into the heap) for tape
  	 * i, or 0 if we are out of pre-read tuples.  mergelast[i] similarly
! 	 * points to the last pre-read tuple from each tape. mergeavailmem[i] is
! 	 * the amount of unused space allocated for tape i. mergefreelist and
  	 * mergefirstfree keep track of unused locations in the memtuples[] array.
! 	 * memtupindex[] links together pre-read tuples for each tape as well as
! 	 * recycled locations in mergefreelist. It is OK to use 0 as a null link
! 	 * in these lists, because memtuples[0] is part of the merge heap and is
! 	 * never a pre-read tuple.
  	 */
  	bool	   *mergeactive;	/* Active input run source? */
  	int		   *mergenext;		/* first preread tuple for each source */
--- 263,275 ----
  	 * have not yet exhausted that run.  mergenext[i] is the memtuples index
  	 * of the next pre-read tuple (next to be loaded into the heap) for tape
  	 * i, or 0 if we are out of pre-read tuples.  mergelast[i] similarly
! 	 * points to the last pre-read tuple from each tape.  mergeavailmem[i] is
! 	 * the amount of unused space allocated for tape i.  mergefreelist and
  	 * mergefirstfree keep track of unused locations in the memtuples[] array.
! 	 * The memtuples[].tupindex fields link together pre-read tuples for each
! 	 * tape as well as recycled locations in mergefreelist. It is OK to use 0
! 	 * as a null link in these lists, because memtuples[0] is part of the
! 	 * merge heap and is never a pre-read tuple.
  	 */
  	bool	   *mergeactive;	/* Active input run source? */
  	int		   *mergenext;		/* first preread tuple for each source */
***************
*** 284,290 ****
  	 * tuplesort_begin_heap and used only by the HeapTuple routines.
  	 */
  	TupleDesc	tupDesc;
- 	int			nKeys;
  	ScanKey		scanKeys;			/* array of length nKeys */
  	SortFunctionKind *sortFnKinds;	/* array of length nKeys */
  
--- 310,315 ----
***************
*** 317,325 ****
  };
  
  #define COMPARETUP(state,a,b)	((*(state)->comparetup) (state, a, b))
! #define COPYTUP(state,tup)	((*(state)->copytup) (state, tup))
! #define WRITETUP(state,tape,tup)	((*(state)->writetup) (state, tape, tup))
! #define READTUP(state,tape,len) ((*(state)->readtup) (state, tape, len))
  #define LACKMEM(state)		((state)->availMem < 0)
  #define USEMEM(state,amt)	((state)->availMem -= (amt))
  #define FREEMEM(state,amt)	((state)->availMem += (amt))
--- 342,350 ----
  };
  
  #define COMPARETUP(state,a,b)	((*(state)->comparetup) (state, a, b))
! #define COPYTUP(state,stup,tup)	((*(state)->copytup) (state, stup, tup))
! #define WRITETUP(state,tape,stup)	((*(state)->writetup) (state, tape, stup))
! #define READTUP(state,stup,tape,len) ((*(state)->readtup) (state, stup, tape, len))
  #define LACKMEM(state)		((state)->availMem < 0)
  #define USEMEM(state,amt)	((state)->availMem -= (amt))
  #define FREEMEM(state,amt)	((state)->availMem += (amt))
***************
*** 355,362 ****
   * NOTES about memory consumption calculations:
   *
   * We count space allocated for tuples against the workMem limit, plus
!  * the space used by the variable-size arrays memtuples and memtupindex.
!  * Fixed-size space is not counted; it's small enough to not be interesting.
   *
   * Note that we count actual space used (as shown by GetMemoryChunkSpace)
   * rather than the originally-requested size.  This is important since
--- 380,387 ----
   * NOTES about memory consumption calculations:
   *
   * We count space allocated for tuples against the workMem limit, plus
!  * the space used by the variable-size memtuples array.  Fixed-size space
!  * is not counted; it's small enough to not be interesting.
   *
   * Note that we count actual space used (as shown by GetMemoryChunkSpace)
   * rather than the originally-requested size.  This is important since
***************
*** 365,386 ****
   * a lot better than what we were doing before 7.3.
   */
  
- /*
-  * For sorting single Datums, we build "pseudo tuples" that just carry
-  * the datum's value and null flag.  For pass-by-reference data types,
-  * the actual data value appears after the DatumTupleHeader (MAXALIGNed,
-  * of course), and the value field in the header is just a pointer to it.
-  */
- 
- typedef struct
- {
- 	Datum		val;
- 	bool		isNull;
- } DatumTuple;
- 
  
  static Tuplesortstate *tuplesort_begin_common(int workMem, bool randomAccess);
! static void puttuple_common(Tuplesortstate *state, void *tuple);
  static void inittapes(Tuplesortstate *state);
  static void selectnewtape(Tuplesortstate *state);
  static void mergeruns(Tuplesortstate *state);
--- 390,398 ----
   * a lot better than what we were doing before 7.3.
   */
  
  
  static Tuplesortstate *tuplesort_begin_common(int workMem, bool randomAccess);
! static void puttuple_common(Tuplesortstate *state, SortTuple *tuple);
  static void inittapes(Tuplesortstate *state);
  static void selectnewtape(Tuplesortstate *state);
  static void mergeruns(Tuplesortstate *state);
***************
*** 388,417 ****
  static void beginmerge(Tuplesortstate *state);
  static void mergepreread(Tuplesortstate *state);
  static void dumptuples(Tuplesortstate *state, bool alltuples);
! static void tuplesort_heap_insert(Tuplesortstate *state, void *tuple,
  					  int tupleindex, bool checkIndex);
  static void tuplesort_heap_siftup(Tuplesortstate *state, bool checkIndex);
  static unsigned int getlen(Tuplesortstate *state, int tapenum, bool eofOK);
  static void markrunend(Tuplesortstate *state, int tapenum);
  static int	qsort_comparetup(const void *a, const void *b);
  static int comparetup_heap(Tuplesortstate *state,
! 				const void *a, const void *b);
! static void *copytup_heap(Tuplesortstate *state, void *tup);
! static void writetup_heap(Tuplesortstate *state, int tapenum, void *tup);
! static void *readtup_heap(Tuplesortstate *state, int tapenum,
! 			 unsigned int len);
  static int comparetup_index(Tuplesortstate *state,
! 				 const void *a, const void *b);
! static void *copytup_index(Tuplesortstate *state, void *tup);
! static void writetup_index(Tuplesortstate *state, int tapenum, void *tup);
! static void *readtup_index(Tuplesortstate *state, int tapenum,
! 			  unsigned int len);
  static int comparetup_datum(Tuplesortstate *state,
! 				 const void *a, const void *b);
! static void *copytup_datum(Tuplesortstate *state, void *tup);
! static void writetup_datum(Tuplesortstate *state, int tapenum, void *tup);
! static void *readtup_datum(Tuplesortstate *state, int tapenum,
! 			  unsigned int len);
  
  /*
   * Since qsort(3) will not pass any context info to qsort_comparetup(),
--- 400,432 ----
  static void beginmerge(Tuplesortstate *state);
  static void mergepreread(Tuplesortstate *state);
  static void dumptuples(Tuplesortstate *state, bool alltuples);
! static void tuplesort_heap_insert(Tuplesortstate *state, SortTuple *tuple,
  					  int tupleindex, bool checkIndex);
  static void tuplesort_heap_siftup(Tuplesortstate *state, bool checkIndex);
  static unsigned int getlen(Tuplesortstate *state, int tapenum, bool eofOK);
  static void markrunend(Tuplesortstate *state, int tapenum);
  static int	qsort_comparetup(const void *a, const void *b);
  static int comparetup_heap(Tuplesortstate *state,
! 				const SortTuple *a, const SortTuple *b);
! static void copytup_heap(Tuplesortstate *state, SortTuple *stup, void *tup);
! static void writetup_heap(Tuplesortstate *state, int tapenum,
! 						  SortTuple *stup);
! static void readtup_heap(Tuplesortstate *state, SortTuple *stup,
! 						 int tapenum, unsigned int len);
  static int comparetup_index(Tuplesortstate *state,
! 				 const SortTuple *a, const SortTuple *b);
! static void copytup_index(Tuplesortstate *state, SortTuple *stup, void *tup);
! static void writetup_index(Tuplesortstate *state, int tapenum,
! 						   SortTuple *stup);
! static void readtup_index(Tuplesortstate *state, SortTuple *stup,
! 						  int tapenum, unsigned int len);
  static int comparetup_datum(Tuplesortstate *state,
! 				 const SortTuple *a, const SortTuple *b);
! static void copytup_datum(Tuplesortstate *state, SortTuple *stup, void *tup);
! static void writetup_datum(Tuplesortstate *state, int tapenum,
! 						   SortTuple *stup);
! static void readtup_datum(Tuplesortstate *state, SortTuple *stup,
! 						  int tapenum, unsigned int len);
  
  /*
   * Since qsort(3) will not pass any context info to qsort_comparetup(),
***************
*** 462,470 ****
  
  	state->memtupcount = 0;
  	state->memtupsize = 1024;	/* initial guess */
! 	state->memtuples = (void **) palloc(state->memtupsize * sizeof(void *));
! 
! 	state->memtupindex = NULL;	/* until and unless needed */
  
  	USEMEM(state, GetMemoryChunkSpace(state->memtuples));
  
--- 477,483 ----
  
  	state->memtupcount = 0;
  	state->memtupsize = 1024;	/* initial guess */
! 	state->memtuples = (SortTuple *) palloc(state->memtupsize * sizeof(SortTuple));
  
  	USEMEM(state, GetMemoryChunkSpace(state->memtuples));
  
***************
*** 498,510 ****
  			 nkeys, workMem, randomAccess ? 't' : 'f');
  #endif
  
  	state->comparetup = comparetup_heap;
  	state->copytup = copytup_heap;
  	state->writetup = writetup_heap;
  	state->readtup = readtup_heap;
  
  	state->tupDesc = tupDesc;
- 	state->nKeys = nkeys;
  	state->scanKeys = (ScanKey) palloc0(nkeys * sizeof(ScanKeyData));
  	state->sortFnKinds = (SortFunctionKind *)
  		palloc0(nkeys * sizeof(SortFunctionKind));
--- 511,524 ----
  			 nkeys, workMem, randomAccess ? 't' : 'f');
  #endif
  
+ 	state->nKeys = nkeys;
+ 
  	state->comparetup = comparetup_heap;
  	state->copytup = copytup_heap;
  	state->writetup = writetup_heap;
  	state->readtup = readtup_heap;
  
  	state->tupDesc = tupDesc;
  	state->scanKeys = (ScanKey) palloc0(nkeys * sizeof(ScanKeyData));
  	state->sortFnKinds = (SortFunctionKind *)
  		palloc0(nkeys * sizeof(SortFunctionKind));
***************
*** 549,554 ****
--- 563,570 ----
  			 workMem, randomAccess ? 't' : 'f');
  #endif
  
+ 	state->nKeys = RelationGetNumberOfAttributes(indexRel);
+ 
  	state->comparetup = comparetup_index;
  	state->copytup = copytup_index;
  	state->writetup = writetup_index;
***************
*** 579,584 ****
--- 595,602 ----
  			 workMem, randomAccess ? 't' : 'f');
  #endif
  
+ 	state->nKeys = 1;			/* always a one-column sort */
+ 
  	state->comparetup = comparetup_datum;
  	state->copytup = copytup_datum;
  	state->writetup = writetup_datum;
***************
*** 630,641 ****
  
  	if (state->memtuples)
  	{
  		for (i = 0; i < state->memtupcount; i++)
  			pfree(state->memtuples[i]);
  		pfree(state->memtuples);
  	}
- 	if (state->memtupindex)
- 		pfree(state->memtupindex);
  
  	/*
  	 * this stuff might better belong in a variant-specific shutdown routine
--- 648,659 ----
  
  	if (state->memtuples)
  	{
+ #if 0							/* XXX */
  		for (i = 0; i < state->memtupcount; i++)
  			pfree(state->memtuples[i]);
+ #endif
  		pfree(state->memtuples);
  	}
  
  	/*
  	 * this stuff might better belong in a variant-specific shutdown routine
***************
*** 668,680 ****
  void
  tuplesort_puttuple(Tuplesortstate *state, void *tuple)
  {
  	/*
  	 * Copy the given tuple into memory we control, and decrease availMem.
  	 * Then call the code shared with the Datum case.
  	 */
! 	tuple = COPYTUP(state, tuple);
  
! 	puttuple_common(state, tuple);
  }
  
  /*
--- 686,700 ----
  void
  tuplesort_puttuple(Tuplesortstate *state, void *tuple)
  {
+ 	SortTuple	stup;
+ 
  	/*
  	 * Copy the given tuple into memory we control, and decrease availMem.
  	 * Then call the code shared with the Datum case.
  	 */
! 	COPYTUP(state, &stup, tuple);
  
! 	puttuple_common(state, &stup);
  }
  
  /*
***************
*** 685,745 ****
  void
  tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
  {
! 	DatumTuple *tuple;
  
  	/*
! 	 * Build pseudo-tuple carrying the datum, and decrease availMem.
  	 */
  	if (isNull || state->datumTypeByVal)
  	{
! 		tuple = (DatumTuple *) palloc(sizeof(DatumTuple));
! 		tuple->val = val;
! 		tuple->isNull = isNull;
  	}
  	else
  	{
! 		Size		datalen;
! 		Size		tuplelen;
! 		char	   *newVal;
! 
! 		datalen = datumGetSize(val, false, state->datumTypeLen);
! 		tuplelen = datalen + MAXALIGN(sizeof(DatumTuple));
! 		tuple = (DatumTuple *) palloc(tuplelen);
! 		newVal = ((char *) tuple) + MAXALIGN(sizeof(DatumTuple));
! 		memcpy(newVal, DatumGetPointer(val), datalen);
! 		tuple->val = PointerGetDatum(newVal);
! 		tuple->isNull = false;
  	}
  
! 	USEMEM(state, GetMemoryChunkSpace(tuple));
! 
! 	puttuple_common(state, (void *) tuple);
  }
  
  /*
   * Shared code for tuple and datum cases.
   */
  static void
! puttuple_common(Tuplesortstate *state, void *tuple)
  {
  	switch (state->status)
  	{
  		case TSS_INITIAL:
  
  			/*
! 			 * Save the copied tuple into the unsorted array.
  			 */
  			if (state->memtupcount >= state->memtupsize)
  			{
  				/* Grow the unsorted array as needed. */
  				FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
  				state->memtupsize *= 2;
! 				state->memtuples = (void **)
  					repalloc(state->memtuples,
! 							 state->memtupsize * sizeof(void *));
  				USEMEM(state, GetMemoryChunkSpace(state->memtuples));
  			}
! 			state->memtuples[state->memtupcount++] = tuple;
  
  			/*
  			 * Done if we still fit in available memory.
--- 705,756 ----
  void
  tuplesort_putdatum(Tuplesortstate *state, Datum val, bool isNull)
  {
! 	SortTuple	stup;
  
  	/*
! 	 * If it's a pass-by-reference value, copy it into memory we control,
! 	 * and decrease availMem.  Then call the code shared with the tuple case.
  	 */
  	if (isNull || state->datumTypeByVal)
  	{
! 		stup.datum1 = val;
! 		stup.isnull1 = isNull;
  	}
  	else
  	{
! 		stup.datum1 = datumCopy(val, false, state->datumTypeLen);
! 		stup.isnull1 = false;
! 		USEMEM(state, GetMemoryChunkSpace(DatumGetPointer(stup.datum1)));
  	}
+ 	stup.tuple = NULL;			/* not used in Datum case */
  
! 	puttuple_common(state, &stup);
  }
  
  /*
   * Shared code for tuple and datum cases.
   */
  static void
! puttuple_common(Tuplesortstate *state, SortTuple *tuple)
  {
  	switch (state->status)
  	{
  		case TSS_INITIAL:
  
  			/*
! 			 * Save the tuple into the unsorted array.
  			 */
  			if (state->memtupcount >= state->memtupsize)
  			{
  				/* Grow the unsorted array as needed. */
  				FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
  				state->memtupsize *= 2;
! 				state->memtuples = (SortTuple *)
  					repalloc(state->memtuples,
! 							 state->memtupsize * sizeof(SortTuple));
  				USEMEM(state, GetMemoryChunkSpace(state->memtuples));
  			}
! 			state->memtuples[state->memtupcount++] = *tuple;
  
  			/*
  			 * Done if we still fit in available memory.
***************
*** 760,766 ****
  		case TSS_BUILDRUNS:
  
  			/*
! 			 * Insert the copied tuple into the heap, with run number
  			 * currentRun if it can go into the current run, else run number
  			 * currentRun+1.  The tuple can go into the current run if it is
  			 * >= the first not-yet-output tuple.  (Actually, it could go into
--- 771,777 ----
  		case TSS_BUILDRUNS:
  
  			/*
! 			 * Insert the tuple into the heap, with run number
  			 * currentRun if it can go into the current run, else run number
  			 * currentRun+1.  The tuple can go into the current run if it is
  			 * >= the first not-yet-output tuple.  (Actually, it could go into
***************
*** 773,779 ****
  			 * this point; see dumptuples.
  			 */
  			Assert(state->memtupcount > 0);
! 			if (COMPARETUP(state, tuple, state->memtuples[0]) >= 0)
  				tuplesort_heap_insert(state, tuple, state->currentRun, true);
  			else
  				tuplesort_heap_insert(state, tuple, state->currentRun + 1, true);
--- 784,790 ----
  			 * this point; see dumptuples.
  			 */
  			Assert(state->memtupcount > 0);
! 			if (COMPARETUP(state, tuple, &state->memtuples[0]) >= 0)
  				tuplesort_heap_insert(state, tuple, state->currentRun, true);
  			else
  				tuplesort_heap_insert(state, tuple, state->currentRun + 1, true);
***************
*** 813,819 ****
  			{
  				qsort_tuplesortstate = state;
  				qsort((void *) state->memtuples, state->memtupcount,
! 					  sizeof(void *), qsort_comparetup);
  			}
  			state->current = 0;
  			state->eof_reached = false;
--- 824,830 ----
  			{
  				qsort_tuplesortstate = state;
  				qsort((void *) state->memtuples, state->memtupcount,
! 					  sizeof(SortTuple), qsort_comparetup);
  			}
  			state->current = 0;
  			state->eof_reached = false;
***************
*** 850,865 ****
  }
  
  /*
!  * Fetch the next tuple in either forward or back direction.
!  * Returns NULL if no more tuples.	If should_free is set, the
!  * caller must pfree the returned tuple when done with it.
!  */
! void *
! tuplesort_gettuple(Tuplesortstate *state, bool forward,
! 				   bool *should_free)
  {
  	unsigned int tuplen;
- 	void	   *tup;
  
  	switch (state->status)
  	{
--- 861,875 ----
  }
  
  /*
!  * Internal routine to fetch the next tuple in either forward or back
!  * direction into *stup.  Returns FALSE if no more tuples.
!  * If *should_free is set, the caller must pfree stup.tuple when done with it.
!  */
! static bool
! tuplesort_gettuple_common(Tuplesortstate *state, bool forward,
! 						  SortTuple *stup, bool *should_free)
  {
  	unsigned int tuplen;
  
  	switch (state->status)
  	{
***************
*** 869,882 ****
  			if (forward)
  			{
  				if (state->current < state->memtupcount)
! 					return state->memtuples[state->current++];
  				state->eof_reached = true;
! 				return NULL;
  			}
  			else
  			{
  				if (state->current <= 0)
! 					return NULL;
  
  				/*
  				 * if all tuples are fetched already then we return last
--- 879,895 ----
  			if (forward)
  			{
  				if (state->current < state->memtupcount)
! 				{
! 					*stup = state->memtuples[state->current++];
! 					return true;
! 				}
  				state->eof_reached = true;
! 				return false;
  			}
  			else
  			{
  				if (state->current <= 0)
! 					return false;
  
  				/*
  				 * if all tuples are fetched already then we return last
***************
*** 888,896 ****
  				{
  					state->current--;	/* last returned tuple */
  					if (state->current <= 0)
! 						return NULL;
  				}
! 				return state->memtuples[state->current - 1];
  			}
  			break;
  
--- 901,910 ----
  				{
  					state->current--;	/* last returned tuple */
  					if (state->current <= 0)
! 						return false;
  				}
! 				*stup = state->memtuples[state->current - 1];
! 				return true;
  			}
  			break;
  
***************
*** 900,915 ****
  			if (forward)
  			{
  				if (state->eof_reached)
! 					return NULL;
  				if ((tuplen = getlen(state, state->result_tape, true)) != 0)
  				{
! 					tup = READTUP(state, state->result_tape, tuplen);
! 					return tup;
  				}
  				else
  				{
  					state->eof_reached = true;
! 					return NULL;
  				}
  			}
  
--- 914,929 ----
  			if (forward)
  			{
  				if (state->eof_reached)
! 					return false;
  				if ((tuplen = getlen(state, state->result_tape, true)) != 0)
  				{
! 					READTUP(state, stup, state->result_tape, tuplen);
! 					return true;
  				}
  				else
  				{
  					state->eof_reached = true;
! 					return false;
  				}
  			}
  
***************
*** 929,935 ****
  				if (!LogicalTapeBackspace(state->tapeset,
  										  state->result_tape,
  										  2 * sizeof(unsigned int)))
! 					return NULL;
  				state->eof_reached = false;
  			}
  			else
--- 943,949 ----
  				if (!LogicalTapeBackspace(state->tapeset,
  										  state->result_tape,
  										  2 * sizeof(unsigned int)))
! 					return false;
  				state->eof_reached = false;
  			}
  			else
***************
*** 941,947 ****
  				if (!LogicalTapeBackspace(state->tapeset,
  										  state->result_tape,
  										  sizeof(unsigned int)))
! 					return NULL;
  				tuplen = getlen(state, state->result_tape, false);
  
  				/*
--- 955,961 ----
  				if (!LogicalTapeBackspace(state->tapeset,
  										  state->result_tape,
  										  sizeof(unsigned int)))
! 					return false;
  				tuplen = getlen(state, state->result_tape, false);
  
  				/*
***************
*** 961,967 ****
  											  state->result_tape,
  											  tuplen + sizeof(unsigned int)))
  						elog(ERROR, "bogus tuple length in backward scan");
! 					return NULL;
  				}
  			}
  
--- 975,981 ----
  											  state->result_tape,
  											  tuplen + sizeof(unsigned int)))
  						elog(ERROR, "bogus tuple length in backward scan");
! 					return false;
  				}
  			}
  
***************
*** 976,983 ****
  									  state->result_tape,
  									  tuplen))
  				elog(ERROR, "bogus tuple length in backward scan");
! 			tup = READTUP(state, state->result_tape, tuplen);
! 			return tup;
  
  		case TSS_FINALMERGE:
  			Assert(forward);
--- 990,997 ----
  									  state->result_tape,
  									  tuplen))
  				elog(ERROR, "bogus tuple length in backward scan");
! 			READTUP(state, stup, state->result_tape, tuplen);
! 			return true;
  
  		case TSS_FINALMERGE:
  			Assert(forward);
***************
*** 988,1003 ****
  			 */
  			if (state->memtupcount > 0)
  			{
! 				int			srcTape = state->memtupindex[0];
  				Size		tuplen;
  				int			tupIndex;
! 				void	   *newtup;
  
! 				tup = state->memtuples[0];
  				/* returned tuple is no longer counted in our memory space */
! 				tuplen = GetMemoryChunkSpace(tup);
! 				state->availMem += tuplen;
! 				state->mergeavailmem[srcTape] += tuplen;
  				tuplesort_heap_siftup(state, false);
  				if ((tupIndex = state->mergenext[srcTape]) == 0)
  				{
--- 1002,1020 ----
  			 */
  			if (state->memtupcount > 0)
  			{
! 				int			srcTape = state->memtuples[0].tupindex;
  				Size		tuplen;
  				int			tupIndex;
! 				SortTuple  *newtup;
  
! 				*stup = state->memtuples[0];
  				/* returned tuple is no longer counted in our memory space */
! 				if (stup->tuple)
! 				{
! 					tuplen = GetMemoryChunkSpace(stup->tuple);
! 					state->availMem += tuplen;
! 					state->mergeavailmem[srcTape] += tuplen;
! 				}
  				tuplesort_heap_siftup(state, false);
  				if ((tupIndex = state->mergenext[srcTape]) == 0)
  				{
***************
*** 1010,1036 ****
  					 * if still no data, we've reached end of run on this tape
  					 */
  					if ((tupIndex = state->mergenext[srcTape]) == 0)
! 						return tup;
  				}
  				/* pull next preread tuple from list, insert in heap */
! 				newtup = state->memtuples[tupIndex];
! 				state->mergenext[srcTape] = state->memtupindex[tupIndex];
  				if (state->mergenext[srcTape] == 0)
  					state->mergelast[srcTape] = 0;
- 				state->memtupindex[tupIndex] = state->mergefreelist;
- 				state->mergefreelist = tupIndex;
  				tuplesort_heap_insert(state, newtup, srcTape, false);
! 				return tup;
  			}
! 			return NULL;
  
  		default:
  			elog(ERROR, "invalid tuplesort state");
! 			return NULL;		/* keep compiler quiet */
  	}
  }
  
  /*
   * Fetch the next Datum in either forward or back direction.
   * Returns FALSE if no more datums.
   *
--- 1027,1070 ----
  					 * if still no data, we've reached end of run on this tape
  					 */
  					if ((tupIndex = state->mergenext[srcTape]) == 0)
! 						return true;
  				}
  				/* pull next preread tuple from list, insert in heap */
! 				newtup = &state->memtuples[tupIndex];
! 				state->mergenext[srcTape] = newtup->tupindex;
  				if (state->mergenext[srcTape] == 0)
  					state->mergelast[srcTape] = 0;
  				tuplesort_heap_insert(state, newtup, srcTape, false);
! 				/* put the now-unused memtuples entry on the freelist */
! 				newtup->tupindex = state->mergefreelist;
! 				state->mergefreelist = tupIndex;
! 				return true;
  			}
! 			return false;
  
  		default:
  			elog(ERROR, "invalid tuplesort state");
! 			return false;		/* keep compiler quiet */
  	}
  }
  
  /*
+  * Fetch the next tuple in either forward or back direction.
+  * Returns NULL if no more tuples.	If should_free is set, the
+  * caller must pfree the returned tuple when done with it.
+  */
+ void *
+ tuplesort_gettuple(Tuplesortstate *state, bool forward,
+ 				   bool *should_free)
+ {
+ 	SortTuple	stup;
+ 
+ 	if (!tuplesort_gettuple_common(state, forward, &stup, should_free))
+ 		return NULL;
+ 	return stup.tuple;
+ }
+ 
+ /*
   * Fetch the next Datum in either forward or back direction.
   * Returns FALSE if no more datums.
   *
***************
*** 1041,1068 ****
  tuplesort_getdatum(Tuplesortstate *state, bool forward,
  				   Datum *val, bool *isNull)
  {
! 	DatumTuple *tuple;
  	bool		should_free;
  
! 	tuple = (DatumTuple *) tuplesort_gettuple(state, forward, &should_free);
! 
! 	if (tuple == NULL)
  		return false;
  
! 	if (tuple->isNull || state->datumTypeByVal)
  	{
! 		*val = tuple->val;
! 		*isNull = tuple->isNull;
  	}
  	else
  	{
! 		*val = datumCopy(tuple->val, false, state->datumTypeLen);
  		*isNull = false;
  	}
  
- 	if (should_free)
- 		pfree(tuple);
- 
  	return true;
  }
  
--- 1075,1100 ----
  tuplesort_getdatum(Tuplesortstate *state, bool forward,
  				   Datum *val, bool *isNull)
  {
! 	SortTuple	stup;
  	bool		should_free;
  
! 	if (!tuplesort_gettuple_common(state, forward, &stup, &should_free))
  		return false;
  
! 	if (stup.isnull1 || state->datumTypeByVal)
  	{
! 		*val = stup.datum1;
! 		*isNull = stup.isnull1;
  	}
  	else
  	{
! 		if (should_free)
! 			*val = stup.datum1;
! 		else
! 			*val = datumCopy(stup.datum1, false, state->datumTypeLen);
  		*isNull = false;
  	}
  
  	return true;
  }
  
***************
*** 1158,1170 ****
  	state->tp_tapenum = (int *) palloc0(maxTapes * sizeof(int));
  
  	/*
- 	 * Allocate the memtupindex array, same size as memtuples.
- 	 */
- 	state->memtupindex = (int *) palloc(state->memtupsize * sizeof(int));
- 
- 	USEMEM(state, GetMemoryChunkSpace(state->memtupindex));
- 
- 	/*
  	 * Convert the unsorted contents of memtuples[] into a heap. Each tuple is
  	 * marked as belonging to run number zero.
  	 *
--- 1190,1195 ----
***************
*** 1175,1181 ****
  	ntuples = state->memtupcount;
  	state->memtupcount = 0;		/* make the heap empty */
  	for (j = 0; j < ntuples; j++)
! 		tuplesort_heap_insert(state, state->memtuples[j], 0, false);
  	Assert(state->memtupcount == ntuples);
  
  	state->currentRun = 0;
--- 1200,1211 ----
  	ntuples = state->memtupcount;
  	state->memtupcount = 0;		/* make the heap empty */
  	for (j = 0; j < ntuples; j++)
! 	{
! 		/* Must copy source tuple to avoid possible overwrite */
! 		SortTuple	stup = state->memtuples[j];
! 
! 		tuplesort_heap_insert(state, &stup, 0, false);
! 	}
  	Assert(state->memtupcount == ntuples);
  
  	state->currentRun = 0;
***************
*** 1360,1366 ****
  	int			destTape = state->tp_tapenum[state->tapeRange];
  	int			srcTape;
  	int			tupIndex;
! 	void	   *tup;
  	long		priorAvail,
  				spaceFreed;
  
--- 1390,1396 ----
  	int			destTape = state->tp_tapenum[state->tapeRange];
  	int			srcTape;
  	int			tupIndex;
! 	SortTuple  *tup;
  	long		priorAvail,
  				spaceFreed;
  
***************
*** 1380,1387 ****
  		CHECK_FOR_INTERRUPTS();
  		/* write the tuple to destTape */
  		priorAvail = state->availMem;
! 		srcTape = state->memtupindex[0];
! 		WRITETUP(state, destTape, state->memtuples[0]);
  		/* writetup adjusted total free space, now fix per-tape space */
  		spaceFreed = state->availMem - priorAvail;
  		state->mergeavailmem[srcTape] += spaceFreed;
--- 1410,1417 ----
  		CHECK_FOR_INTERRUPTS();
  		/* write the tuple to destTape */
  		priorAvail = state->availMem;
! 		srcTape = state->memtuples[0].tupindex;
! 		WRITETUP(state, destTape, &state->memtuples[0]);
  		/* writetup adjusted total free space, now fix per-tape space */
  		spaceFreed = state->availMem - priorAvail;
  		state->mergeavailmem[srcTape] += spaceFreed;
***************
*** 1396,1408 ****
  				continue;
  		}
  		/* pull next preread tuple from list, insert in heap */
! 		tup = state->memtuples[tupIndex];
! 		state->mergenext[srcTape] = state->memtupindex[tupIndex];
  		if (state->mergenext[srcTape] == 0)
  			state->mergelast[srcTape] = 0;
- 		state->memtupindex[tupIndex] = state->mergefreelist;
- 		state->mergefreelist = tupIndex;
  		tuplesort_heap_insert(state, tup, srcTape, false);
  	}
  
  	/*
--- 1426,1439 ----
  				continue;
  		}
  		/* pull next preread tuple from list, insert in heap */
! 		tup = &state->memtuples[tupIndex];
! 		state->mergenext[srcTape] = tup->tupindex;
  		if (state->mergenext[srcTape] == 0)
  			state->mergelast[srcTape] = 0;
  		tuplesort_heap_insert(state, tup, srcTape, false);
+ 		/* put the now-unused memtuples entry on the freelist */
+ 		tup->tupindex = state->mergefreelist;
+ 		state->mergefreelist = tupIndex;
  	}
  
  	/*
***************
*** 1483,1499 ****
  	for (srcTape = 0; srcTape < state->maxTapes; srcTape++)
  	{
  		int			tupIndex = state->mergenext[srcTape];
! 		void	   *tup;
  
  		if (tupIndex)
  		{
! 			tup = state->memtuples[tupIndex];
! 			state->mergenext[srcTape] = state->memtupindex[tupIndex];
  			if (state->mergenext[srcTape] == 0)
  				state->mergelast[srcTape] = 0;
- 			state->memtupindex[tupIndex] = state->mergefreelist;
- 			state->mergefreelist = tupIndex;
  			tuplesort_heap_insert(state, tup, srcTape, false);
  		}
  	}
  }
--- 1514,1531 ----
  	for (srcTape = 0; srcTape < state->maxTapes; srcTape++)
  	{
  		int			tupIndex = state->mergenext[srcTape];
! 		SortTuple  *tup;
  
  		if (tupIndex)
  		{
! 			tup = &state->memtuples[tupIndex];
! 			state->mergenext[srcTape] = tup->tupindex;
  			if (state->mergenext[srcTape] == 0)
  				state->mergelast[srcTape] = 0;
  			tuplesort_heap_insert(state, tup, srcTape, false);
+ 			/* put the now-unused memtuples entry on the freelist */
+ 			tup->tupindex = state->mergefreelist;
+ 			state->mergefreelist = tupIndex;
  		}
  	}
  }
***************
*** 1512,1518 ****
  {
  	int			srcTape;
  	unsigned int tuplen;
! 	void	   *tup;
  	int			tupIndex;
  	long		priorAvail,
  				spaceUsed;
--- 1544,1550 ----
  {
  	int			srcTape;
  	unsigned int tuplen;
! 	SortTuple	stup;
  	int			tupIndex;
  	long		priorAvail,
  				spaceUsed;
***************
*** 1546,1580 ****
  				state->mergeactive[srcTape] = false;
  				break;
  			}
! 			tup = READTUP(state, srcTape, tuplen);
  			/* find or make a free slot in memtuples[] for it */
  			tupIndex = state->mergefreelist;
  			if (tupIndex)
! 				state->mergefreelist = state->memtupindex[tupIndex];
  			else
  			{
  				tupIndex = state->mergefirstfree++;
! 				/* Might need to enlarge arrays! */
  				if (tupIndex >= state->memtupsize)
  				{
  					FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
- 					FREEMEM(state, GetMemoryChunkSpace(state->memtupindex));
  					state->memtupsize *= 2;
! 					state->memtuples = (void **)
  						repalloc(state->memtuples,
! 								 state->memtupsize * sizeof(void *));
! 					state->memtupindex = (int *)
! 						repalloc(state->memtupindex,
! 								 state->memtupsize * sizeof(int));
  					USEMEM(state, GetMemoryChunkSpace(state->memtuples));
- 					USEMEM(state, GetMemoryChunkSpace(state->memtupindex));
  				}
  			}
  			/* store tuple, append to list for its tape */
! 			state->memtuples[tupIndex] = tup;
! 			state->memtupindex[tupIndex] = 0;
  			if (state->mergelast[srcTape])
! 				state->memtupindex[state->mergelast[srcTape]] = tupIndex;
  			else
  				state->mergenext[srcTape] = tupIndex;
  			state->mergelast[srcTape] = tupIndex;
--- 1578,1607 ----
  				state->mergeactive[srcTape] = false;
  				break;
  			}
! 			READTUP(state, &stup, srcTape, tuplen);
  			/* find or make a free slot in memtuples[] for it */
  			tupIndex = state->mergefreelist;
  			if (tupIndex)
! 				state->mergefreelist = state->memtuples[tupIndex].tupindex;
  			else
  			{
  				tupIndex = state->mergefirstfree++;
! 				/* Might need to enlarge array! */
  				if (tupIndex >= state->memtupsize)
  				{
  					FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
  					state->memtupsize *= 2;
! 					state->memtuples = (SortTuple *)
  						repalloc(state->memtuples,
! 								 state->memtupsize * sizeof(SortTuple));
  					USEMEM(state, GetMemoryChunkSpace(state->memtuples));
  				}
  			}
  			/* store tuple, append to list for its tape */
! 			stup.tupindex = 0;
! 			state->memtuples[tupIndex] = stup;
  			if (state->mergelast[srcTape])
! 				state->memtuples[state->mergelast[srcTape]].tupindex = tupIndex;
  			else
  				state->mergenext[srcTape] = tupIndex;
  			state->mergelast[srcTape] = tupIndex;
***************
*** 1614,1620 ****
  		 */
  		Assert(state->memtupcount > 0);
  		WRITETUP(state, state->tp_tapenum[state->destTape],
! 				 state->memtuples[0]);
  		tuplesort_heap_siftup(state, true);
  
  		/*
--- 1641,1647 ----
  		 */
  		Assert(state->memtupcount > 0);
  		WRITETUP(state, state->tp_tapenum[state->destTape],
! 				 &state->memtuples[0]);
  		tuplesort_heap_siftup(state, true);
  
  		/*
***************
*** 1622,1628 ****
  		 * finished the current run.
  		 */
  		if (state->memtupcount == 0 ||
! 			state->currentRun != state->memtupindex[0])
  		{
  			markrunend(state, state->tp_tapenum[state->destTape]);
  			state->currentRun++;
--- 1649,1655 ----
  		 * finished the current run.
  		 */
  		if (state->memtupcount == 0 ||
! 			state->currentRun != state->memtuples[0].tupindex)
  		{
  			markrunend(state, state->tp_tapenum[state->destTape]);
  			state->currentRun++;
***************
*** 1642,1648 ****
  			 */
  			if (state->memtupcount == 0)
  				break;
! 			Assert(state->currentRun == state->memtupindex[0]);
  			selectnewtape(state);
  		}
  	}
--- 1669,1675 ----
  			 */
  			if (state->memtupcount == 0)
  				break;
! 			Assert(state->currentRun == state->memtuples[0].tupindex);
  			selectnewtape(state);
  		}
  	}
***************
*** 1739,1751 ****
  /*
   * Heap manipulation routines, per Knuth's Algorithm 5.2.3H.
   *
!  * The heap lives in state->memtuples[], with parallel data storage
!  * for indexes in state->memtupindex[].  If checkIndex is true, use
!  * the tuple index as the front of the sort key; otherwise, no.
   */
  
! #define HEAPCOMPARE(tup1,index1,tup2,index2) \
! 	(checkIndex && (index1 != index2) ? (index1) - (index2) : \
  	 COMPARETUP(state, tup1, tup2))
  
  /*
--- 1766,1778 ----
  /*
   * Heap manipulation routines, per Knuth's Algorithm 5.2.3H.
   *
!  * Compare two SortTuples.  If checkIndex is true, use the tuple index
!  * as the front of the sort key; otherwise, no.
   */
  
! #define HEAPCOMPARE(tup1,tup2) \
! 	(checkIndex && ((tup1)->tupindex != (tup2)->tupindex) ? \
! 	 ((tup1)->tupindex) - ((tup2)->tupindex) : \
  	 COMPARETUP(state, tup1, tup2))
  
  /*
***************
*** 1753,1784 ****
   * heap invariant.
   */
  static void
! tuplesort_heap_insert(Tuplesortstate *state, void *tuple,
  					  int tupleindex, bool checkIndex)
  {
! 	void	  **memtuples;
! 	int		   *memtupindex;
  	int			j;
  
  	/*
  	 * Make sure memtuples[] can handle another entry.
  	 */
  	if (state->memtupcount >= state->memtupsize)
  	{
  		FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
- 		FREEMEM(state, GetMemoryChunkSpace(state->memtupindex));
  		state->memtupsize *= 2;
! 		state->memtuples = (void **)
  			repalloc(state->memtuples,
! 					 state->memtupsize * sizeof(void *));
! 		state->memtupindex = (int *)
! 			repalloc(state->memtupindex,
! 					 state->memtupsize * sizeof(int));
  		USEMEM(state, GetMemoryChunkSpace(state->memtuples));
- 		USEMEM(state, GetMemoryChunkSpace(state->memtupindex));
  	}
  	memtuples = state->memtuples;
- 	memtupindex = state->memtupindex;
  
  	/*
  	 * Sift-up the new entry, per Knuth 5.2.3 exercise 16. Note that Knuth is
--- 1780,1806 ----
   * heap invariant.
   */
  static void
! tuplesort_heap_insert(Tuplesortstate *state, SortTuple *tuple,
  					  int tupleindex, bool checkIndex)
  {
! 	SortTuple  *memtuples;
  	int			j;
  
+ 	tuple->tupindex = tupleindex;
+ 
  	/*
  	 * Make sure memtuples[] can handle another entry.
  	 */
  	if (state->memtupcount >= state->memtupsize)
  	{
  		FREEMEM(state, GetMemoryChunkSpace(state->memtuples));
  		state->memtupsize *= 2;
! 		state->memtuples = (SortTuple *)
  			repalloc(state->memtuples,
! 					 state->memtupsize * sizeof(SortTuple));
  		USEMEM(state, GetMemoryChunkSpace(state->memtuples));
  	}
  	memtuples = state->memtuples;
  
  	/*
  	 * Sift-up the new entry, per Knuth 5.2.3 exercise 16. Note that Knuth is
***************
*** 1789,1803 ****
  	{
  		int			i = (j - 1) >> 1;
  
! 		if (HEAPCOMPARE(tuple, tupleindex,
! 						memtuples[i], memtupindex[i]) >= 0)
  			break;
  		memtuples[j] = memtuples[i];
- 		memtupindex[j] = memtupindex[i];
  		j = i;
  	}
! 	memtuples[j] = tuple;
! 	memtupindex[j] = tupleindex;
  }
  
  /*
--- 1811,1822 ----
  	{
  		int			i = (j - 1) >> 1;
  
! 		if (HEAPCOMPARE(tuple, &memtuples[i]) >= 0)
  			break;
  		memtuples[j] = memtuples[i];
  		j = i;
  	}
! 	memtuples[j] = *tuple;
  }
  
  /*
***************
*** 1807,1824 ****
  static void
  tuplesort_heap_siftup(Tuplesortstate *state, bool checkIndex)
  {
! 	void	  **memtuples = state->memtuples;
! 	int		   *memtupindex = state->memtupindex;
! 	void	   *tuple;
! 	int			tupindex,
! 				i,
  				n;
  
  	if (--state->memtupcount <= 0)
  		return;
  	n = state->memtupcount;
! 	tuple = memtuples[n];		/* tuple that must be reinserted */
! 	tupindex = memtupindex[n];
  	i = 0;						/* i is where the "hole" is */
  	for (;;)
  	{
--- 1826,1840 ----
  static void
  tuplesort_heap_siftup(Tuplesortstate *state, bool checkIndex)
  {
! 	SortTuple  *memtuples = state->memtuples;
! 	SortTuple  *tuple;
! 	int			i,
  				n;
  
  	if (--state->memtupcount <= 0)
  		return;
  	n = state->memtupcount;
! 	tuple = &memtuples[n];		/* tuple that must be reinserted */
  	i = 0;						/* i is where the "hole" is */
  	for (;;)
  	{
***************
*** 1827,1844 ****
  		if (j >= n)
  			break;
  		if (j + 1 < n &&
! 			HEAPCOMPARE(memtuples[j], memtupindex[j],
! 						memtuples[j + 1], memtupindex[j + 1]) > 0)
  			j++;
! 		if (HEAPCOMPARE(tuple, tupindex,
! 						memtuples[j], memtupindex[j]) <= 0)
  			break;
  		memtuples[i] = memtuples[j];
- 		memtupindex[i] = memtupindex[j];
  		i = j;
  	}
! 	memtuples[i] = tuple;
! 	memtupindex[i] = tupindex;
  }
  
  
--- 1843,1856 ----
  		if (j >= n)
  			break;
  		if (j + 1 < n &&
! 			HEAPCOMPARE(&memtuples[j], &memtuples[j + 1]) > 0)
  			j++;
! 		if (HEAPCOMPARE(tuple, &memtuples[j]) <= 0)
  			break;
  		memtuples[i] = memtuples[j];
  		i = j;
  	}
! 	memtuples[i] = *tuple;
  }
  
  
***************
*** 1875,1883 ****
  static int
  qsort_comparetup(const void *a, const void *b)
  {
! 	/* The passed pointers are pointers to void * ... */
! 
! 	return COMPARETUP(qsort_tuplesortstate, *(void **) a, *(void **) b);
  }
  
  
--- 1887,1896 ----
  static int
  qsort_comparetup(const void *a, const void *b)
  {
! 	/* The passed pointers are pointers to SortTuple ... */
! 	return COMPARETUP(qsort_tuplesortstate,
! 					  (const SortTuple *) a,
! 					  (const SortTuple *) b);
  }
  
  
***************
*** 2095,2116 ****
   */
  
  static int
! comparetup_heap(Tuplesortstate *state, const void *a, const void *b)
  {
! 	HeapTuple	ltup = (HeapTuple) a;
! 	HeapTuple	rtup = (HeapTuple) b;
! 	TupleDesc	tupDesc = state->tupDesc;
  	int			nkey;
  
! 	for (nkey = 0; nkey < state->nKeys; nkey++)
  	{
- 		ScanKey		scanKey = state->scanKeys + nkey;
  		AttrNumber	attno = scanKey->sk_attno;
  		Datum		datum1,
  					datum2;
  		bool		isnull1,
  					isnull2;
- 		int32		compare;
  
  		datum1 = heap_getattr(ltup, attno, tupDesc, &isnull1);
  		datum2 = heap_getattr(rtup, attno, tupDesc, &isnull2);
--- 2108,2142 ----
   */
  
  static int
! comparetup_heap(Tuplesortstate *state, const SortTuple *a, const SortTuple *b)
  {
! 	ScanKey		scanKey = state->scanKeys;
! 	HeapTuple	ltup;
! 	HeapTuple	rtup;
! 	TupleDesc	tupDesc;
  	int			nkey;
+ 	int32		compare;
  
! 	/* Compare the leading sort key */
! 	compare = inlineApplySortFunction(&scanKey->sk_func,
! 									  state->sortFnKinds[0],
! 									  a->datum1, a->isnull1,
! 									  b->datum1, b->isnull1);
! 	if (compare != 0)
! 		return compare;
! 
! 	/* Compare additional sort keys */
! 	ltup = (HeapTuple) a->tuple;
! 	rtup = (HeapTuple) b->tuple;
! 	tupDesc = state->tupDesc;
! 	scanKey++;
! 	for (nkey = 1; nkey < state->nKeys; nkey++, scanKey++)
  	{
  		AttrNumber	attno = scanKey->sk_attno;
  		Datum		datum1,
  					datum2;
  		bool		isnull1,
  					isnull2;
  
  		datum1 = heap_getattr(ltup, attno, tupDesc, &isnull1);
  		datum2 = heap_getattr(rtup, attno, tupDesc, &isnull2);
***************
*** 2126,2139 ****
  	return 0;
  }
  
! static void *
! copytup_heap(Tuplesortstate *state, void *tup)
  {
  	HeapTuple	tuple = (HeapTuple) tup;
  
! 	tuple = heap_copytuple(tuple);
! 	USEMEM(state, GetMemoryChunkSpace(tuple));
! 	return (void *) tuple;
  }
  
  /*
--- 2152,2170 ----
  	return 0;
  }
  
! static void
! copytup_heap(Tuplesortstate *state, SortTuple *stup, void *tup)
  {
  	HeapTuple	tuple = (HeapTuple) tup;
  
! 	/* copy the tuple into sort storage */
! 	stup->tuple = (void *) heap_copytuple(tuple);
! 	USEMEM(state, GetMemoryChunkSpace(stup->tuple));
! 	/* set up first-column key value */
! 	stup->datum1 = heap_getattr((HeapTuple) stup->tuple,
! 								state->scanKeys[0].sk_attno,
! 								state->tupDesc,
! 								&stup->isnull1);
  }
  
  /*
***************
*** 2141,2149 ****
   */
  
  static void
! writetup_heap(Tuplesortstate *state, int tapenum, void *tup)
  {
! 	HeapTuple	tuple = (HeapTuple) tup;
  	unsigned int tuplen;
  
  	tuplen = tuple->t_len + sizeof(tuplen);
--- 2172,2180 ----
   */
  
  static void
! writetup_heap(Tuplesortstate *state, int tapenum, SortTuple *stup)
  {
! 	HeapTuple	tuple = (HeapTuple) stup->tuple;
  	unsigned int tuplen;
  
  	tuplen = tuple->t_len + sizeof(tuplen);
***************
*** 2159,2166 ****
  	heap_freetuple(tuple);
  }
  
! static void *
! readtup_heap(Tuplesortstate *state, int tapenum, unsigned int len)
  {
  	unsigned int tuplen = len - sizeof(unsigned int) + HEAPTUPLESIZE;
  	HeapTuple	tuple = (HeapTuple) palloc(tuplen);
--- 2190,2198 ----
  	heap_freetuple(tuple);
  }
  
! static void
! readtup_heap(Tuplesortstate *state, SortTuple *stup,
! 			 int tapenum, unsigned int len)
  {
  	unsigned int tuplen = len - sizeof(unsigned int) + HEAPTUPLESIZE;
  	HeapTuple	tuple = (HeapTuple) palloc(tuplen);
***************
*** 2169,2174 ****
--- 2201,2207 ----
  	/* reconstruct the HeapTupleData portion */
  	tuple->t_len = len - sizeof(unsigned int);
  	ItemPointerSetInvalid(&(tuple->t_self));
+ 	tuple->t_tableOid = InvalidOid;
  	tuple->t_data = (HeapTupleHeader) (((char *) tuple) + HEAPTUPLESIZE);
  	/* read in the tuple proper */
  	if (LogicalTapeRead(state->tapeset, tapenum, (void *) tuple->t_data,
***************
*** 2178,2184 ****
  		if (LogicalTapeRead(state->tapeset, tapenum, (void *) &tuplen,
  							sizeof(tuplen)) != sizeof(tuplen))
  			elog(ERROR, "unexpected end of data");
! 	return (void *) tuple;
  }
  
  
--- 2211,2222 ----
  		if (LogicalTapeRead(state->tapeset, tapenum, (void *) &tuplen,
  							sizeof(tuplen)) != sizeof(tuplen))
  			elog(ERROR, "unexpected end of data");
! 	stup->tuple = (void *) tuple;
! 	/* set up first-column key value */
! 	stup->datum1 = heap_getattr(tuple,
! 								state->scanKeys[0].sk_attno,
! 								state->tupDesc,
! 								&stup->isnull1);
  }
  
  
***************
*** 2192,2237 ****
   */
  
  static int
! comparetup_index(Tuplesortstate *state, const void *a, const void *b)
  {
  	/*
! 	 * This is almost the same as _bt_tuplecompare(), but we need to keep
! 	 * track of whether any null fields are present.  Also see the special
! 	 * treatment for equal keys at the end.
! 	 */
! 	IndexTuple	tuple1 = (IndexTuple) a;
! 	IndexTuple	tuple2 = (IndexTuple) b;
! 	Relation	rel = state->indexRel;
! 	int			keysz = RelationGetNumberOfAttributes(rel);
! 	ScanKey		scankey = state->indexScanKey;
  	TupleDesc	tupDes;
- 	int			i;
  	bool		equal_hasnull = false;
  
! 	tupDes = RelationGetDescr(rel);
! 
! 	for (i = 1; i <= keysz; i++)
  	{
- 		ScanKey		entry = &scankey[i - 1];
  		Datum		datum1,
  					datum2;
  		bool		isnull1,
  					isnull2;
- 		int32		compare;
  
! 		datum1 = index_getattr(tuple1, i, tupDes, &isnull1);
! 		datum2 = index_getattr(tuple2, i, tupDes, &isnull2);
  
  		/* see comments about NULLs handling in btbuild */
  
  		/* the comparison function is always of CMP type */
! 		compare = inlineApplySortFunction(&entry->sk_func, SORTFUNC_CMP,
  										  datum1, isnull1,
  										  datum2, isnull2);
  
  		if (compare != 0)
! 			return (int) compare;		/* done when we find unequal
! 										 * attributes */
  
  		/* they are equal, so we only need to examine one null flag */
  		if (isnull1)
--- 2230,2290 ----
   */
  
  static int
! comparetup_index(Tuplesortstate *state, const SortTuple *a, const SortTuple *b)
  {
  	/*
! 	 * This is similar to _bt_tuplecompare(), but we have already done the
! 	 * index_getattr calls for the first column, and we need to keep track
! 	 * of whether any null fields are present.  Also see the special treatment
! 	 * for equal keys at the end.
! 	 */
! 	ScanKey		scanKey = state->indexScanKey;
! 	IndexTuple	tuple1;
! 	IndexTuple	tuple2;
! 	int			keysz;
  	TupleDesc	tupDes;
  	bool		equal_hasnull = false;
+ 	int			nkey;
+ 	int32		compare;
  
! 	/* Compare the leading sort key */
! 	compare = inlineApplySortFunction(&scanKey->sk_func,
! 									  SORTFUNC_CMP,
! 									  a->datum1, a->isnull1,
! 									  b->datum1, b->isnull1);
! 	if (compare != 0)
! 		return compare;
! 
! 	/* they are equal, so we only need to examine one null flag */
! 	if (a->isnull1)
! 		equal_hasnull = true;
! 
! 	/* Compare additional sort keys */
! 	tuple1 = (IndexTuple) a->tuple;
! 	tuple2 = (IndexTuple) b->tuple;
! 	keysz = state->nKeys;
! 	tupDes = RelationGetDescr(state->indexRel);
! 	scanKey++;
! 	for (nkey = 2; nkey <= keysz; nkey++, scanKey++)
  	{
  		Datum		datum1,
  					datum2;
  		bool		isnull1,
  					isnull2;
  
! 		datum1 = index_getattr(tuple1, nkey, tupDes, &isnull1);
! 		datum2 = index_getattr(tuple2, nkey, tupDes, &isnull2);
  
  		/* see comments about NULLs handling in btbuild */
  
  		/* the comparison function is always of CMP type */
! 		compare = inlineApplySortFunction(&scanKey->sk_func,
! 										  SORTFUNC_CMP,
  										  datum1, isnull1,
  										  datum2, isnull2);
  
  		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)
***************
*** 2249,2256 ****
  	 *
  	 * Some rather brain-dead implementations of qsort will sometimes call the
  	 * comparison routine to compare a value to itself.  (At this writing only
! 	 * QNX 4 is known to do such silly things.)  Don't raise a bogus error in
! 	 * that case.   Update: The QNX port is gone.
  	 */
  	if (state->enforceUnique && !equal_hasnull && tuple1 != tuple2)
  		ereport(ERROR,
--- 2302,2310 ----
  	 *
  	 * Some rather brain-dead implementations of qsort will sometimes call the
  	 * comparison routine to compare a value to itself.  (At this writing only
! 	 * QNX 4 is known to do such silly things; we don't support QNX anymore,
! 	 * but perhaps the behavior still exists elsewhere.)  Don't raise a bogus
! 	 * error in that case.
  	 */
  	if (state->enforceUnique && !equal_hasnull && tuple1 != tuple2)
  		ereport(ERROR,
***************
*** 2282,2306 ****
  	return 0;
  }
  
! static void *
! copytup_index(Tuplesortstate *state, void *tup)
  {
  	IndexTuple	tuple = (IndexTuple) tup;
  	unsigned int tuplen = IndexTupleSize(tuple);
  	IndexTuple	newtuple;
  
  	newtuple = (IndexTuple) palloc(tuplen);
- 	USEMEM(state, GetMemoryChunkSpace(newtuple));
- 
  	memcpy(newtuple, tuple, tuplen);
! 
! 	return (void *) newtuple;
  }
  
  static void
! writetup_index(Tuplesortstate *state, int tapenum, void *tup)
  {
! 	IndexTuple	tuple = (IndexTuple) tup;
  	unsigned int tuplen;
  
  	tuplen = IndexTupleSize(tuple) + sizeof(tuplen);
--- 2336,2364 ----
  	return 0;
  }
  
! static void
! copytup_index(Tuplesortstate *state, SortTuple *stup, void *tup)
  {
  	IndexTuple	tuple = (IndexTuple) tup;
  	unsigned int tuplen = IndexTupleSize(tuple);
  	IndexTuple	newtuple;
  
+ 	/* copy the tuple into sort storage */
  	newtuple = (IndexTuple) palloc(tuplen);
  	memcpy(newtuple, tuple, tuplen);
! 	USEMEM(state, GetMemoryChunkSpace(newtuple));
! 	stup->tuple = (void *) newtuple;
! 	/* set up first-column key value */
! 	stup->datum1 = index_getattr(newtuple,
! 								 1,
! 								 RelationGetDescr(state->indexRel),
! 								 &stup->isnull1);
  }
  
  static void
! writetup_index(Tuplesortstate *state, int tapenum, SortTuple *stup)
  {
! 	IndexTuple	tuple = (IndexTuple) stup->tuple;
  	unsigned int tuplen;
  
  	tuplen = IndexTupleSize(tuple) + sizeof(tuplen);
***************
*** 2316,2323 ****
  	pfree(tuple);
  }
  
! static void *
! readtup_index(Tuplesortstate *state, int tapenum, unsigned int len)
  {
  	unsigned int tuplen = len - sizeof(unsigned int);
  	IndexTuple	tuple = (IndexTuple) palloc(tuplen);
--- 2374,2382 ----
  	pfree(tuple);
  }
  
! static void
! readtup_index(Tuplesortstate *state, SortTuple *stup,
! 			  int tapenum, unsigned int len)
  {
  	unsigned int tuplen = len - sizeof(unsigned int);
  	IndexTuple	tuple = (IndexTuple) palloc(tuplen);
***************
*** 2330,2336 ****
  		if (LogicalTapeRead(state->tapeset, tapenum, (void *) &tuplen,
  							sizeof(tuplen)) != sizeof(tuplen))
  			elog(ERROR, "unexpected end of data");
! 	return (void *) tuple;
  }
  
  
--- 2389,2400 ----
  		if (LogicalTapeRead(state->tapeset, tapenum, (void *) &tuplen,
  							sizeof(tuplen)) != sizeof(tuplen))
  			elog(ERROR, "unexpected end of data");
! 	stup->tuple = (void *) tuple;
! 	/* set up first-column key value */
! 	stup->datum1 = index_getattr(tuple,
! 								 1,
! 								 RelationGetDescr(state->indexRel),
! 								 &stup->isnull1);
  }
  
  
***************
*** 2339,2377 ****
   */
  
  static int
! comparetup_datum(Tuplesortstate *state, const void *a, const void *b)
  {
- 	DatumTuple *ltup = (DatumTuple *) a;
- 	DatumTuple *rtup = (DatumTuple *) b;
- 
  	return inlineApplySortFunction(&state->sortOpFn, state->sortFnKind,
! 								   ltup->val, ltup->isNull,
! 								   rtup->val, rtup->isNull);
  }
  
! static void *
! copytup_datum(Tuplesortstate *state, void *tup)
  {
  	/* Not currently needed */
  	elog(ERROR, "copytup_datum() should not be called");
- 	return NULL;
  }
  
  static void
! writetup_datum(Tuplesortstate *state, int tapenum, void *tup)
  {
! 	DatumTuple *tuple = (DatumTuple *) tup;
  	unsigned int tuplen;
  	unsigned int writtenlen;
  
! 	if (tuple->isNull || state->datumTypeByVal)
! 		tuplen = sizeof(DatumTuple);
  	else
  	{
! 		Size		datalen;
! 
! 		datalen = datumGetSize(tuple->val, false, state->datumTypeLen);
! 		tuplen = datalen + MAXALIGN(sizeof(DatumTuple));
  	}
  
  	writtenlen = tuplen + sizeof(unsigned int);
--- 2403,2444 ----
   */
  
  static int
! comparetup_datum(Tuplesortstate *state, const SortTuple *a, const SortTuple *b)
  {
  	return inlineApplySortFunction(&state->sortOpFn, state->sortFnKind,
! 								   a->datum1, a->isnull1,
! 								   b->datum1, b->isnull1);
  }
  
! static void
! copytup_datum(Tuplesortstate *state, SortTuple *stup, void *tup)
  {
  	/* Not currently needed */
  	elog(ERROR, "copytup_datum() should not be called");
  }
  
  static void
! writetup_datum(Tuplesortstate *state, int tapenum, SortTuple *stup)
  {
! 	void	   *waddr;
  	unsigned int tuplen;
  	unsigned int writtenlen;
  
! 	if (stup->isnull1)
! 	{
! 		waddr = NULL;
! 		tuplen = 0;
! 	}
! 	else if (state->datumTypeByVal)
! 	{
! 		waddr = &stup->datum1;
! 		tuplen = sizeof(Datum);
! 	}
  	else
  	{
! 		waddr = DatumGetPointer(stup->datum1);
! 		tuplen = datumGetSize(stup->datum1, false, state->datumTypeLen);
! 		Assert(tuplen != 0);
  	}
  
  	writtenlen = tuplen + sizeof(unsigned int);
***************
*** 2379,2411 ****
  	LogicalTapeWrite(state->tapeset, tapenum,
  					 (void *) &writtenlen, sizeof(writtenlen));
  	LogicalTapeWrite(state->tapeset, tapenum,
! 					 (void *) tuple, tuplen);
  	if (state->randomAccess)	/* need trailing length word? */
  		LogicalTapeWrite(state->tapeset, tapenum,
  						 (void *) &writtenlen, sizeof(writtenlen));
  
! 	FREEMEM(state, GetMemoryChunkSpace(tuple));
! 	pfree(tuple);
  }
  
! static void *
! readtup_datum(Tuplesortstate *state, int tapenum, unsigned int len)
  {
  	unsigned int tuplen = len - sizeof(unsigned int);
- 	DatumTuple *tuple = (DatumTuple *) palloc(tuplen);
  
! 	USEMEM(state, GetMemoryChunkSpace(tuple));
! 	if (LogicalTapeRead(state->tapeset, tapenum, (void *) tuple,
! 						tuplen) != tuplen)
! 		elog(ERROR, "unexpected end of data");
  	if (state->randomAccess)	/* need trailing length word? */
  		if (LogicalTapeRead(state->tapeset, tapenum, (void *) &tuplen,
  							sizeof(tuplen)) != sizeof(tuplen))
  			elog(ERROR, "unexpected end of data");
- 
- 	/* if pass-by-ref data type, must recompute the Datum pointer */
- 	if (!tuple->isNull && !state->datumTypeByVal)
- 		tuple->val = PointerGetDatum(((char *) tuple) +
- 									 MAXALIGN(sizeof(DatumTuple)));
- 	return (void *) tuple;
  }
--- 2446,2498 ----
  	LogicalTapeWrite(state->tapeset, tapenum,
  					 (void *) &writtenlen, sizeof(writtenlen));
  	LogicalTapeWrite(state->tapeset, tapenum,
! 					 waddr, tuplen);
  	if (state->randomAccess)	/* need trailing length word? */
  		LogicalTapeWrite(state->tapeset, tapenum,
  						 (void *) &writtenlen, sizeof(writtenlen));
  
! 	if (waddr && !state->datumTypeByVal)
! 	{
! 		FREEMEM(state, GetMemoryChunkSpace(waddr));
! 		pfree(waddr);
! 	}
  }
  
! static void
! readtup_datum(Tuplesortstate *state, SortTuple *stup,
! 			  int tapenum, unsigned int len)
  {
  	unsigned int tuplen = len - sizeof(unsigned int);
  
! 	stup->tuple = NULL;
! 	if (tuplen == 0)
! 	{
! 		/* it's NULL */
! 		stup->datum1 = (Datum) 0;
! 		stup->isnull1 = true;
! 	}
! 	else if (state->datumTypeByVal)
! 	{
! 		Assert(tuplen == sizeof(Datum));
! 		if (LogicalTapeRead(state->tapeset, tapenum, (void *) &stup->datum1,
! 							tuplen) != tuplen)
! 			elog(ERROR, "unexpected end of data");
! 		stup->isnull1 = false;
! 	}
! 	else
! 	{
! 		void   *raddr = palloc(tuplen);
! 
! 		if (LogicalTapeRead(state->tapeset, tapenum, raddr,
! 							tuplen) != tuplen)
! 			elog(ERROR, "unexpected end of data");
! 		stup->datum1 = PointerGetDatum(raddr);
! 		stup->isnull1 = false;
! 		USEMEM(state, GetMemoryChunkSpace(raddr));
! 	}
! 
  	if (state->randomAccess)	/* need trailing length word? */
  		if (LogicalTapeRead(state->tapeset, tapenum, (void *) &tuplen,
  							sizeof(tuplen)) != sizeof(tuplen))
  			elog(ERROR, "unexpected end of data");
  }
