At the moment, amgettuple only returns pointers to heap tuples. There is
no way to return data from the index tuples. That needs to be changed to
support index-only scans.

I propose that we split index_getnext into two steps: fetching the next
match from the index (index_next()), and fetching the corresponding heap
tuple (index_fetch()). Patch attached. There is still a shorthand
index_getnext() that is compatible with the old function, but it now
just calls index_next()+index_fetch().

The new index_fetch() function can only fetch one match from a HOT
chain. That greatly simplifies the code in indexam.c. The only caller
needing to fetch more than one tuple from a HOT chain (= using
SnapshotAny) is CLUSTER, so I moved the HOT-chain following logic into
cluster.c, with small changes to heap_hot_search_buffer().

-- 
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com
*** a/src/backend/access/heap/heapam.c
--- b/src/backend/access/heap/heapam.c
***************
*** 1490,1497 **** heap_fetch(Relation relation,
   * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
   * of a HOT chain), and buffer is the buffer holding this tuple.  We search
   * for the first chain member satisfying the given snapshot.  If one is
!  * found, we update *tid to reference that tuple's offset number, and
!  * return TRUE.  If no match, return FALSE without modifying *tid.
   *
   * If all_dead is not NULL, we check non-visible tuples to see if they are
   * globally dead; *all_dead is set TRUE if all members of the HOT chain
--- 1490,1503 ----
   * On entry, *tid is the TID of a tuple (either a simple tuple, or the root
   * of a HOT chain), and buffer is the buffer holding this tuple.  We search
   * for the first chain member satisfying the given snapshot.  If one is
!  * found, we update *tid and *heapTuple to reference that tuple, and
!  * return TRUE.  If no match, return FALSE without modifying *tid (*heapTuple
!  * is undefined in that case).
!  *
!  * To fetch the next matching tuple in the chain, call again with 'firstCall'
!  * set to FALSE and *tid still pointing to the tuple returned by previous call.
!  * (normally only one tuple in a chain can be visible at a time, but that does
!  * not hold for special snapshots like SnapshotAny)
   *
   * If all_dead is not NULL, we check non-visible tuples to see if they are
   * globally dead; *all_dead is set TRUE if all members of the HOT chain
***************
*** 1503,1529 **** heap_fetch(Relation relation,
   */
  bool
  heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
! 					   bool *all_dead)
  {
  	Page		dp = (Page) BufferGetPage(buffer);
  	TransactionId prev_xmax = InvalidTransactionId;
  	OffsetNumber offnum;
  	bool		at_chain_start;
  
! 	if (all_dead)
  		*all_dead = true;
  
  	Assert(TransactionIdIsValid(RecentGlobalXmin));
  
  	Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
  	offnum = ItemPointerGetOffsetNumber(tid);
! 	at_chain_start = true;
  
  	/* Scan through possible multiple members of HOT-chain */
  	for (;;)
  	{
  		ItemId		lp;
- 		HeapTupleData heapTuple;
  
  		/* check for bogus TID */
  		if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
--- 1509,1537 ----
   */
  bool
  heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
! 					   HeapTuple heapTuple, bool *all_dead, bool firstCall)
  {
  	Page		dp = (Page) BufferGetPage(buffer);
  	TransactionId prev_xmax = InvalidTransactionId;
  	OffsetNumber offnum;
  	bool		at_chain_start;
+ 	bool		skip;
  
! 	if (all_dead && firstCall)
  		*all_dead = true;
  
  	Assert(TransactionIdIsValid(RecentGlobalXmin));
  
  	Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer));
  	offnum = ItemPointerGetOffsetNumber(tid);
! 
! 	at_chain_start = firstCall;
! 	skip = !firstCall;
  
  	/* Scan through possible multiple members of HOT-chain */
  	for (;;)
  	{
  		ItemId		lp;
  
  		/* check for bogus TID */
  		if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
***************
*** 1546,1558 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
  			break;
  		}
  
! 		heapTuple.t_data = (HeapTupleHeader) PageGetItem(dp, lp);
! 		heapTuple.t_len = ItemIdGetLength(lp);
  
  		/*
  		 * Shouldn't see a HEAP_ONLY tuple at chain start.
  		 */
! 		if (at_chain_start && HeapTupleIsHeapOnly(&heapTuple))
  			break;
  
  		/*
--- 1554,1566 ----
  			break;
  		}
  
! 		heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
! 		heapTuple->t_len = ItemIdGetLength(lp);
  
  		/*
  		 * Shouldn't see a HEAP_ONLY tuple at chain start.
  		 */
! 		if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
  			break;
  
  		/*
***************
*** 1561,1577 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
  		 */
  		if (TransactionIdIsValid(prev_xmax) &&
  			!TransactionIdEquals(prev_xmax,
! 								 HeapTupleHeaderGetXmin(heapTuple.t_data)))
  			break;
  
  		/* If it's visible per the snapshot, we must return it */
! 		if (HeapTupleSatisfiesVisibility(&heapTuple, snapshot, buffer))
  		{
  			ItemPointerSetOffsetNumber(tid, offnum);
  			if (all_dead)
  				*all_dead = false;
  			return true;
  		}
  
  		/*
  		 * If we can't see it, maybe no one else can either.  At caller
--- 1569,1586 ----
  		 */
  		if (TransactionIdIsValid(prev_xmax) &&
  			!TransactionIdEquals(prev_xmax,
! 								 HeapTupleHeaderGetXmin(heapTuple->t_data)))
  			break;
  
  		/* If it's visible per the snapshot, we must return it */
! 		if (!skip && HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer))
  		{
  			ItemPointerSetOffsetNumber(tid, offnum);
  			if (all_dead)
  				*all_dead = false;
  			return true;
  		}
+ 		skip = false;
  
  		/*
  		 * If we can't see it, maybe no one else can either.  At caller
***************
*** 1579,1585 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
  		 * transactions.
  		 */
  		if (all_dead && *all_dead &&
! 			HeapTupleSatisfiesVacuum(heapTuple.t_data, RecentGlobalXmin,
  									 buffer) != HEAPTUPLE_DEAD)
  			*all_dead = false;
  
--- 1588,1594 ----
  		 * transactions.
  		 */
  		if (all_dead && *all_dead &&
! 			HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
  									 buffer) != HEAPTUPLE_DEAD)
  			*all_dead = false;
  
***************
*** 1587,1599 **** heap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot,
  		 * Check to see if HOT chain continues past this tuple; if so fetch
  		 * the next offnum and loop around.
  		 */
! 		if (HeapTupleIsHotUpdated(&heapTuple))
  		{
! 			Assert(ItemPointerGetBlockNumber(&heapTuple.t_data->t_ctid) ==
  				   ItemPointerGetBlockNumber(tid));
! 			offnum = ItemPointerGetOffsetNumber(&heapTuple.t_data->t_ctid);
  			at_chain_start = false;
! 			prev_xmax = HeapTupleHeaderGetXmax(heapTuple.t_data);
  		}
  		else
  			break;				/* end of chain */
--- 1596,1608 ----
  		 * Check to see if HOT chain continues past this tuple; if so fetch
  		 * the next offnum and loop around.
  		 */
! 		if (HeapTupleIsHotUpdated(heapTuple))
  		{
! 			Assert(ItemPointerGetBlockNumber(&heapTuple->t_data->t_ctid) ==
  				   ItemPointerGetBlockNumber(tid));
! 			offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
  			at_chain_start = false;
! 			prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
  		}
  		else
  			break;				/* end of chain */
***************
*** 1615,1624 **** heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot,
  {
  	bool		result;
  	Buffer		buffer;
  
  	buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
  	LockBuffer(buffer, BUFFER_LOCK_SHARE);
! 	result = heap_hot_search_buffer(tid, buffer, snapshot, all_dead);
  	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
  	ReleaseBuffer(buffer);
  	return result;
--- 1624,1635 ----
  {
  	bool		result;
  	Buffer		buffer;
+ 	HeapTupleData heapTuple;
  
  	buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));
  	LockBuffer(buffer, BUFFER_LOCK_SHARE);
! 	result = heap_hot_search_buffer(tid, buffer, snapshot, &heapTuple,
! 									all_dead, true);
  	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
  	ReleaseBuffer(buffer);
  	return result;
*** a/src/backend/access/index/genam.c
--- b/src/backend/access/index/genam.c
***************
*** 97,105 **** RelationGetIndexScan(Relation indexRelation,
  	ItemPointerSetInvalid(&scan->xs_ctup.t_self);
  	scan->xs_ctup.t_data = NULL;
  	scan->xs_cbuf = InvalidBuffer;
- 	scan->xs_hot_dead = false;
- 	scan->xs_next_hot = InvalidOffsetNumber;
- 	scan->xs_prev_xmax = InvalidTransactionId;
  
  	/*
  	 * Let the AM fill in the key and any opaque data it wants.
--- 97,102 ----
*** a/src/backend/access/index/indexam.c
--- b/src/backend/access/index/indexam.c
***************
*** 20,26 ****
   *		index_insert	- insert an index tuple into a relation
   *		index_markpos	- mark a scan position
   *		index_restrpos	- restore a scan position
!  *		index_getnext	- get the next tuple from a scan
   *		index_getbitmap - get all tuples from a scan
   *		index_bulk_delete	- bulk deletion of index tuples
   *		index_vacuum_cleanup	- post-deletion cleanup of an index
--- 20,27 ----
   *		index_insert	- insert an index tuple into a relation
   *		index_markpos	- mark a scan position
   *		index_restrpos	- restore a scan position
!  *		index_next		- advance index scan to next match
!  *		index_fetch		- fetch heap tuple for current index scan position
   *		index_getbitmap - get all tuples from a scan
   *		index_bulk_delete	- bulk deletion of index tuples
   *		index_vacuum_cleanup	- post-deletion cleanup of an index
***************
*** 310,317 **** index_rescan(IndexScanDesc scan, ScanKey key)
  		scan->xs_cbuf = InvalidBuffer;
  	}
  
- 	scan->xs_next_hot = InvalidOffsetNumber;
- 
  	scan->kill_prior_tuple = false;		/* for safety */
  
  	FunctionCall2(procedure,
--- 311,316 ----
***************
*** 389,420 **** index_restrpos(IndexScanDesc scan)
  	SCAN_CHECKS;
  	GET_SCAN_PROCEDURE(amrestrpos);
  
- 	scan->xs_next_hot = InvalidOffsetNumber;
- 
  	scan->kill_prior_tuple = false;		/* for safety */
  
  	FunctionCall1(procedure, PointerGetDatum(scan));
  }
  
  /* ----------------
!  *		index_getnext - get the next heap tuple from a scan
   *
!  * The result is the next heap tuple satisfying the scan keys and the
!  * snapshot, or NULL if no more matching tuples exist.	On success,
!  * the buffer containing the heap tuple is pinned (the pin will be dropped
!  * at the next index_getnext or index_endscan).
   *
   * Note: caller must check scan->xs_recheck, and perform rechecking of the
   * scan keys if required.  We do not do that here because we don't have
   * enough information to do it efficiently in the general case.
   * ----------------
   */
! HeapTuple
! index_getnext(IndexScanDesc scan, ScanDirection direction)
  {
- 	HeapTuple	heapTuple = &scan->xs_ctup;
- 	ItemPointer tid = &heapTuple->t_self;
  	FmgrInfo   *procedure;
  
  	SCAN_CHECKS;
  	GET_SCAN_PROCEDURE(amgettuple);
--- 388,415 ----
  	SCAN_CHECKS;
  	GET_SCAN_PROCEDURE(amrestrpos);
  
  	scan->kill_prior_tuple = false;		/* for safety */
  
  	FunctionCall1(procedure, PointerGetDatum(scan));
  }
  
  /* ----------------
!  *		index_next - get the next index tuple from a scan
   *
!  * Advances to the next index tuple satisfying the scan keys, returning TRUE
!  * if there was one, FALSE otherwise. The heap TID pointer of the next match
!  * is stored in scan->xs_ctup.self.
   *
   * Note: caller must check scan->xs_recheck, and perform rechecking of the
   * scan keys if required.  We do not do that here because we don't have
   * enough information to do it efficiently in the general case.
   * ----------------
   */
! bool
! index_next(IndexScanDesc scan, ScanDirection direction)
  {
  	FmgrInfo   *procedure;
+ 	bool 		found;
  
  	SCAN_CHECKS;
  	GET_SCAN_PROCEDURE(amgettuple);
***************
*** 422,641 **** index_getnext(IndexScanDesc scan, ScanDirection direction)
  	Assert(TransactionIdIsValid(RecentGlobalXmin));
  
  	/*
! 	 * We always reset xs_hot_dead; if we are here then either we are just
! 	 * starting the scan, or we previously returned a visible tuple, and in
! 	 * either case it's inappropriate to kill the prior index entry.
  	 */
! 	scan->xs_hot_dead = false;
  
! 	for (;;)
! 	{
! 		OffsetNumber offnum;
! 		bool		at_chain_start;
! 		Page		dp;
  
! 		if (scan->xs_next_hot != InvalidOffsetNumber)
! 		{
! 			/*
! 			 * We are resuming scan of a HOT chain after having returned an
! 			 * earlier member.	Must still hold pin on current heap page.
! 			 */
! 			Assert(BufferIsValid(scan->xs_cbuf));
! 			Assert(ItemPointerGetBlockNumber(tid) ==
! 				   BufferGetBlockNumber(scan->xs_cbuf));
! 			Assert(TransactionIdIsValid(scan->xs_prev_xmax));
! 			offnum = scan->xs_next_hot;
! 			at_chain_start = false;
! 			scan->xs_next_hot = InvalidOffsetNumber;
! 		}
! 		else
  		{
! 			bool		found;
! 			Buffer		prev_buf;
! 
! 			/*
! 			 * If we scanned a whole HOT chain and found only dead tuples,
! 			 * tell index AM to kill its entry for that TID.
! 			 */
! 			scan->kill_prior_tuple = scan->xs_hot_dead;
! 
! 			/*
! 			 * The AM's gettuple proc finds the next index entry matching the
! 			 * scan keys, and puts the TID in xs_ctup.t_self (ie, *tid). It
! 			 * should also set scan->xs_recheck, though we pay no attention to
! 			 * that here.
! 			 */
! 			found = DatumGetBool(FunctionCall2(procedure,
! 											   PointerGetDatum(scan),
! 											   Int32GetDatum(direction)));
! 
! 			/* Reset kill flag immediately for safety */
! 			scan->kill_prior_tuple = false;
! 
! 			/* If we're out of index entries, break out of outer loop */
! 			if (!found)
! 				break;
! 
! 			pgstat_count_index_tuples(scan->indexRelation, 1);
! 
! 			/* Switch to correct buffer if we don't have it already */
! 			prev_buf = scan->xs_cbuf;
! 			scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf,
! 												 scan->heapRelation,
! 											 ItemPointerGetBlockNumber(tid));
! 
! 			/*
! 			 * Prune page, but only if we weren't already on this page
! 			 */
! 			if (prev_buf != scan->xs_cbuf)
! 				heap_page_prune_opt(scan->heapRelation, scan->xs_cbuf,
! 									RecentGlobalXmin);
! 
! 			/* Prepare to scan HOT chain starting at index-referenced offnum */
! 			offnum = ItemPointerGetOffsetNumber(tid);
! 			at_chain_start = true;
! 
! 			/* We don't know what the first tuple's xmin should be */
! 			scan->xs_prev_xmax = InvalidTransactionId;
! 
! 			/* Initialize flag to detect if all entries are dead */
! 			scan->xs_hot_dead = true;
  		}
  
! 		/* Obtain share-lock on the buffer so we can examine visibility */
! 		LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
  
! 		dp = (Page) BufferGetPage(scan->xs_cbuf);
  
- 		/* Scan through possible multiple members of HOT-chain */
- 		for (;;)
- 		{
- 			ItemId		lp;
- 			ItemPointer ctid;
- 
- 			/* check for bogus TID */
- 			if (offnum < FirstOffsetNumber ||
- 				offnum > PageGetMaxOffsetNumber(dp))
- 				break;
- 
- 			lp = PageGetItemId(dp, offnum);
- 
- 			/* check for unused, dead, or redirected items */
- 			if (!ItemIdIsNormal(lp))
- 			{
- 				/* We should only see a redirect at start of chain */
- 				if (ItemIdIsRedirected(lp) && at_chain_start)
- 				{
- 					/* Follow the redirect */
- 					offnum = ItemIdGetRedirect(lp);
- 					at_chain_start = false;
- 					continue;
- 				}
- 				/* else must be end of chain */
- 				break;
- 			}
- 
- 			/*
- 			 * We must initialize all of *heapTuple (ie, scan->xs_ctup) since
- 			 * it is returned to the executor on success.
- 			 */
- 			heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
- 			heapTuple->t_len = ItemIdGetLength(lp);
- 			ItemPointerSetOffsetNumber(tid, offnum);
- 			heapTuple->t_tableOid = RelationGetRelid(scan->heapRelation);
- 			ctid = &heapTuple->t_data->t_ctid;
- 
- 			/*
- 			 * Shouldn't see a HEAP_ONLY tuple at chain start.  (This test
- 			 * should be unnecessary, since the chain root can't be removed
- 			 * while we have pin on the index entry, but let's make it
- 			 * anyway.)
- 			 */
- 			if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
- 				break;
- 
- 			/*
- 			 * The xmin should match the previous xmax value, else chain is
- 			 * broken.	(Note: this test is not optional because it protects
- 			 * us against the case where the prior chain member's xmax aborted
- 			 * since we looked at it.)
- 			 */
- 			if (TransactionIdIsValid(scan->xs_prev_xmax) &&
- 				!TransactionIdEquals(scan->xs_prev_xmax,
- 								  HeapTupleHeaderGetXmin(heapTuple->t_data)))
- 				break;
- 
- 			/* If it's visible per the snapshot, we must return it */
- 			if (HeapTupleSatisfiesVisibility(heapTuple, scan->xs_snapshot,
- 											 scan->xs_cbuf))
- 			{
- 				/*
- 				 * If the snapshot is MVCC, we know that it could accept at
- 				 * most one member of the HOT chain, so we can skip examining
- 				 * any more members.  Otherwise, check for continuation of the
- 				 * HOT-chain, and set state for next time.
- 				 */
- 				if (IsMVCCSnapshot(scan->xs_snapshot))
- 					scan->xs_next_hot = InvalidOffsetNumber;
- 				else if (HeapTupleIsHotUpdated(heapTuple))
- 				{
- 					Assert(ItemPointerGetBlockNumber(ctid) ==
- 						   ItemPointerGetBlockNumber(tid));
- 					scan->xs_next_hot = ItemPointerGetOffsetNumber(ctid);
- 					scan->xs_prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
- 				}
- 				else
- 					scan->xs_next_hot = InvalidOffsetNumber;
- 
- 				LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
- 
- 				pgstat_count_heap_fetch(scan->indexRelation);
- 
- 				return heapTuple;
- 			}
- 
- 			/*
- 			 * If we can't see it, maybe no one else can either.  Check to see
- 			 * if the tuple is dead to all transactions.  If we find that all
- 			 * the tuples in the HOT chain are dead, we'll signal the index AM
- 			 * to not return that TID on future indexscans.
- 			 */
- 			if (scan->xs_hot_dead &&
- 				HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
- 										 scan->xs_cbuf) != HEAPTUPLE_DEAD)
- 				scan->xs_hot_dead = false;
- 
- 			/*
- 			 * Check to see if HOT chain continues past this tuple; if so
- 			 * fetch the next offnum (we don't bother storing it into
- 			 * xs_next_hot, but must store xs_prev_xmax), and loop around.
- 			 */
- 			if (HeapTupleIsHotUpdated(heapTuple))
- 			{
- 				Assert(ItemPointerGetBlockNumber(ctid) ==
- 					   ItemPointerGetBlockNumber(tid));
- 				offnum = ItemPointerGetOffsetNumber(ctid);
- 				at_chain_start = false;
- 				scan->xs_prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
- 			}
- 			else
- 				break;			/* end of chain */
- 		}						/* loop over a single HOT chain */
- 
- 		LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
- 
- 		/* Loop around to ask index AM for another TID */
- 		scan->xs_next_hot = InvalidOffsetNumber;
- 	}
  
! 	/* Release any held pin on a heap page */
! 	if (BufferIsValid(scan->xs_cbuf))
  	{
! 		ReleaseBuffer(scan->xs_cbuf);
! 		scan->xs_cbuf = InvalidBuffer;
  	}
  
! 	return NULL;				/* failure exit */
  }
  
  /* ----------------
--- 417,541 ----
  	Assert(TransactionIdIsValid(RecentGlobalXmin));
  
  	/*
! 	 * The AM's gettuple proc finds the next index entry matching the scan
! 	 * keys, and puts the TID in xs_ctup.t_self. It should also set
! 	 * scan->xs_recheck, though we pay no attention to that here.
  	 */
! 	found = DatumGetBool(FunctionCall2(procedure,
! 									   PointerGetDatum(scan),
! 									   Int32GetDatum(direction)));
  
! 	/* Reset kill flag immediately for safety */
! 	scan->kill_prior_tuple = false;
  
! 	/* If we're out of index entries, release any held pin on a heap page */
! 	if (!found)
! 	{
! 		if (BufferIsValid(scan->xs_cbuf))
  		{
! 			ReleaseBuffer(scan->xs_cbuf);
! 			scan->xs_cbuf = InvalidBuffer;
  		}
+ 		return false;
+ 	}
  
! 	pgstat_count_index_tuples(scan->indexRelation, 1);
  
! 	return true;
! }
  
  
! /* ----------------
!  *		index_fetch - fetch the heap tuple the current index pointer points to
!  *
!  * The result is the heap tuple the current index tuple points to if it
!  * matches the snapshot, or NULL if it doesn't. On success, the buffer
!  * containing the heap tuple is pinned (the pin will be dropped at the next
!  * index_fetch or index_endscan).
!  * ----------------
!  */
! HeapTuple
! index_fetch(IndexScanDesc scan)
! {
! 	ItemPointer tid = &scan->xs_ctup.t_self;
! 	bool		found;
! 	Buffer		prev_buf;
! 
! 	/*
! 	 * We only fetch the first matching tuple in a chain, so we can't support
! 	 * SnapshotAny. All other snapshots only consider one tuple in a HOT chain
! 	 * as visible at any given moment.
! 	 */
! 	Assert(scan->xs_snapshot != SnapshotAny);
! 
! 	/* Switch to correct buffer if we don't have it already */
! 	prev_buf = scan->xs_cbuf;
! 	scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf,
! 										 scan->heapRelation,
! 										 ItemPointerGetBlockNumber(tid));
! 
! 	/*
! 	 * Prune page, but only if we weren't already on this page
! 	 */
! 	if (prev_buf != scan->xs_cbuf)
! 		heap_page_prune_opt(scan->heapRelation, scan->xs_cbuf,
! 							RecentGlobalXmin);
! 
! 	/* Obtain share-lock on the buffer so we can examine visibility */
! 	LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
! 
! 	found = heap_hot_search_buffer(tid, scan->xs_cbuf,
! 								   scan->xs_snapshot, &scan->xs_ctup,
! 								   &scan->kill_prior_tuple, true);
! 	LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
! 
! 	if (found)
  	{
! 		Assert(!scan->kill_prior_tuple);
! 
! 		/*
! 		 * We must initialize all of *heapTuple (ie, scan->xs_ctup) since
! 		 * it is returned to the executor on success.
! 		 */
! 		scan->xs_ctup.t_tableOid = RelationGetRelid(scan->heapRelation);
! 
! 		pgstat_count_heap_fetch(scan->indexRelation);
! 
! 		return &scan->xs_ctup;
  	}
+ 	else
+ 		return NULL;
+ }
  
! /* ----------------
!  *		index_getnext - get the next heap tuple from a scan
!  *
!  * This is a shorthand for index_next() + index_fetch().
!  *
!  * The result is the next heap tuple satisfying the scan keys and the
!  * snapshot, or NULL if no more matching tuples exist.	On success,
!  * the buffer containing the heap tuple is pinned (the pin will be dropped
!  * at the next index_getnext or index_endscan).
!  *
!  * Note: caller must check scan->xs_recheck, and perform rechecking of the
!  * scan keys if required.  We do not do that here because we don't have
!  * enough information to do it efficiently in the general case.
!  * ----------------
!  */
! HeapTuple
! index_getnext(IndexScanDesc scan, ScanDirection direction)
! {
! 	for(;;)
! 	{
! 		HeapTuple tup;
! 
! 		if (!index_next(scan, direction))
! 			return NULL;
! 		
! 		tup = index_fetch(scan);
! 		if (tup != NULL)
! 			return tup;
! 	}
  }
  
  /* ----------------
*** a/src/backend/commands/cluster.c
--- b/src/backend/commands/cluster.c
***************
*** 772,777 **** copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
--- 772,778 ----
  	TransactionId OldestXmin;
  	TransactionId FreezeXid;
  	RewriteState rwstate;
+ 	ItemPointerData tid;
  
  	/*
  	 * Open the relations we need.
***************
*** 829,847 **** copy_heap_data(Oid OIDNewHeap, Oid OIDOldHeap, Oid OIDOldIndex)
  	scan = index_beginscan(OldHeap, OldIndex,
  						   SnapshotAny, 0, (ScanKey) NULL);
  
! 	while ((tuple = index_getnext(scan, ForwardScanDirection)) != NULL)
  	{
  		HeapTuple	copiedTuple;
  		bool		isdead;
  		int			i;
  
  		CHECK_FOR_INTERRUPTS();
  
! 		/* Since we used no scan keys, should never need to recheck */
! 		if (scan->xs_recheck)
! 			elog(ERROR, "CLUSTER does not support lossy index conditions");
  
! 		LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
  
  		switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin,
  										 scan->xs_cbuf))
--- 830,885 ----
  	scan = index_beginscan(OldHeap, OldIndex,
  						   SnapshotAny, 0, (ScanKey) NULL);
  
! 	tuple = NULL;
! 	for(;;)
  	{
  		HeapTuple	copiedTuple;
  		bool		isdead;
  		int			i;
+ 		bool		found;
  
  		CHECK_FOR_INTERRUPTS();
  
! 		/* Advance to next index tuple if we're done with current HOT chain */
! 		if (tuple == NULL)
! 		{
! 			if (!index_next(scan, ForwardScanDirection))
! 				break;
! 
! 			/* Since we used no scan keys, should never need to recheck */
! 			if (scan->xs_recheck)
! 				elog(ERROR, "CLUSTER does not support lossy index conditions");
! 
! 			tid = scan->xs_ctup.t_self;
  
! 			scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf, OldHeap,
! 												 ItemPointerGetBlockNumber(&tid));
! 			/* Obtain share-lock on the buffer so we can examine visibility */
! 			LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
! 			found = heap_hot_search_buffer(&tid, scan->xs_cbuf,
! 										   scan->xs_snapshot, &scan->xs_ctup,
! 										   &scan->kill_prior_tuple, true);
! 			if (!found)
! 			{
! 				LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
! 				break;
! 			}
! 		}
! 		else
! 		{
! 			/* Fetch next tuple from current HOT chain */
! 			LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
! 			found = heap_hot_search_buffer(&tid, scan->xs_cbuf,
! 										   scan->xs_snapshot, &scan->xs_ctup,
! 										   &scan->kill_prior_tuple, false);
! 			if (!found)
! 			{
! 				LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
! 				tuple = NULL;
! 				continue;
! 			}
! 		}
! 		tuple = &scan->xs_ctup;
  
  		switch (HeapTupleSatisfiesVacuum(tuple->t_data, OldestXmin,
  										 scan->xs_cbuf))
*** a/src/backend/executor/nodeBitmapHeapscan.c
--- b/src/backend/executor/nodeBitmapHeapscan.c
***************
*** 382,390 **** bitgetpage(HeapScanDesc scan, TBMIterateResult *tbmres)
  		{
  			OffsetNumber offnum = tbmres->offsets[curslot];
  			ItemPointerData tid;
  
  			ItemPointerSet(&tid, page, offnum);
! 			if (heap_hot_search_buffer(&tid, buffer, snapshot, NULL))
  				scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
  		}
  	}
--- 382,392 ----
  		{
  			OffsetNumber offnum = tbmres->offsets[curslot];
  			ItemPointerData tid;
+ 			HeapTupleData heapTuple;
  
  			ItemPointerSet(&tid, page, offnum);
! 			if (heap_hot_search_buffer(&tid, buffer, snapshot, &heapTuple,
! 									   NULL, true))
  				scan->rs_vistuples[ntup++] = ItemPointerGetOffsetNumber(&tid);
  		}
  	}
*** a/src/include/access/genam.h
--- b/src/include/access/genam.h
***************
*** 117,122 **** extern void index_endscan(IndexScanDesc scan);
--- 117,125 ----
  extern void index_markpos(IndexScanDesc scan);
  extern void index_restrpos(IndexScanDesc scan);
  extern HeapTuple index_getnext(IndexScanDesc scan, ScanDirection direction);
+ extern bool index_next(IndexScanDesc scan, ScanDirection direction);
+ extern HeapTuple index_fetch(IndexScanDesc scan);
+ 
  extern int64 index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap);
  
  extern IndexBulkDeleteResult *index_bulk_delete(IndexVacuumInfo *info,
*** a/src/include/access/heapam.h
--- b/src/include/access/heapam.h
***************
*** 83,89 **** extern bool heap_fetch(Relation relation, Snapshot snapshot,
  		   HeapTuple tuple, Buffer *userbuf, bool keep_buf,
  		   Relation stats_relation);
  extern bool heap_hot_search_buffer(ItemPointer tid, Buffer buffer,
! 					   Snapshot snapshot, bool *all_dead);
  extern bool heap_hot_search(ItemPointer tid, Relation relation,
  				Snapshot snapshot, bool *all_dead);
  
--- 83,90 ----
  		   HeapTuple tuple, Buffer *userbuf, bool keep_buf,
  		   Relation stats_relation);
  extern bool heap_hot_search_buffer(ItemPointer tid, Buffer buffer,
! 					   Snapshot snapshot, HeapTuple tuple,
! 					   bool *all_dead, bool firstCall);
  extern bool heap_hot_search(ItemPointer tid, Relation relation,
  				Snapshot snapshot, bool *all_dead);
  
*** a/src/include/access/relscan.h
--- b/src/include/access/relscan.h
***************
*** 77,87 **** typedef struct IndexScanDescData
  	Buffer		xs_cbuf;		/* current heap buffer in scan, if any */
  	/* NB: if xs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
  	bool		xs_recheck;		/* T means scan keys must be rechecked */
- 
- 	/* state data for traversing HOT chains in index_getnext */
- 	bool		xs_hot_dead;	/* T if all members of HOT chain are dead */
- 	OffsetNumber xs_next_hot;	/* next member of HOT chain, if any */
- 	TransactionId xs_prev_xmax; /* previous HOT chain member's XMAX, if any */
  } IndexScanDescData;
  
  /* Struct for heap-or-index scans of system tables */
--- 77,82 ----
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to