From 94f29c626633fa05050b702b00ff31c04de55de1 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Wed, 3 Jun 2020 11:01:48 +0900
Subject: [PATCH 2/2] Move heap_get_latest_tid() within the heap AM handler

This routine is not getting called in any code path except through the
table AM interface, so let's make the cut clear.
---
 src/include/access/heapam.h              |   2 -
 src/backend/access/heap/heapam.c         | 117 -----------------------
 src/backend/access/heap/heapam_handler.c | 117 +++++++++++++++++++++++
 3 files changed, 117 insertions(+), 119 deletions(-)

diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4a5b14f10e..273b8be02e 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -128,8 +128,6 @@ extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation,
 								   Buffer buffer, Snapshot snapshot, HeapTuple heapTuple,
 								   bool *all_dead, bool first_call);
 
-extern void heap_get_latest_tid(TableScanDesc scan, ItemPointer tid);
-
 extern BulkInsertState GetBulkInsertState(void);
 extern void FreeBulkInsertState(BulkInsertState);
 extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 94eb37d48d..e177065e20 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -1618,123 +1618,6 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
 	return false;
 }
 
-/*
- *	heap_get_latest_tid -  get the latest tid of a specified tuple
- *
- * Actually, this gets the latest version that is visible according to the
- * scan's snapshot.  Create a scan using SnapshotDirty to get the very latest,
- * possibly uncommitted version.
- *
- * *tid is both an input and an output parameter: it is updated to
- * show the latest version of the row.  Note that it will not be changed
- * if no version of the row passes the snapshot test.
- */
-void
-heap_get_latest_tid(TableScanDesc sscan,
-					ItemPointer tid)
-{
-	Relation	relation = sscan->rs_rd;
-	Snapshot	snapshot = sscan->rs_snapshot;
-	ItemPointerData ctid;
-	TransactionId priorXmax;
-
-	/*
-	 * table_get_latest_tid verified that the passed in tid is valid.  Assume
-	 * that t_ctid links are valid however - there shouldn't be invalid ones
-	 * in the table.
-	 */
-	Assert(ItemPointerIsValid(tid));
-
-	/*
-	 * Loop to chase down t_ctid links.  At top of loop, ctid is the tuple we
-	 * need to examine, and *tid is the TID we will return if ctid turns out
-	 * to be bogus.
-	 *
-	 * Note that we will loop until we reach the end of the t_ctid chain.
-	 * Depending on the snapshot passed, there might be at most one visible
-	 * version of the row, but we don't try to optimize for that.
-	 */
-	ctid = *tid;
-	priorXmax = InvalidTransactionId;	/* cannot check first XMIN */
-	for (;;)
-	{
-		Buffer		buffer;
-		Page		page;
-		OffsetNumber offnum;
-		ItemId		lp;
-		HeapTupleData tp;
-		bool		valid;
-
-		/*
-		 * Read, pin, and lock the page.
-		 */
-		buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
-		LockBuffer(buffer, BUFFER_LOCK_SHARE);
-		page = BufferGetPage(buffer);
-		TestForOldSnapshot(snapshot, relation, page);
-
-		/*
-		 * Check for bogus item number.  This is not treated as an error
-		 * condition because it can happen while following a t_ctid link. We
-		 * just assume that the prior tid is OK and return it unchanged.
-		 */
-		offnum = ItemPointerGetOffsetNumber(&ctid);
-		if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
-		{
-			UnlockReleaseBuffer(buffer);
-			break;
-		}
-		lp = PageGetItemId(page, offnum);
-		if (!ItemIdIsNormal(lp))
-		{
-			UnlockReleaseBuffer(buffer);
-			break;
-		}
-
-		/* OK to access the tuple */
-		tp.t_self = ctid;
-		tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
-		tp.t_len = ItemIdGetLength(lp);
-		tp.t_tableOid = RelationGetRelid(relation);
-
-		/*
-		 * After following a t_ctid link, we might arrive at an unrelated
-		 * tuple.  Check for XMIN match.
-		 */
-		if (TransactionIdIsValid(priorXmax) &&
-			!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
-		{
-			UnlockReleaseBuffer(buffer);
-			break;
-		}
-
-		/*
-		 * Check tuple visibility; if visible, set it as the new result
-		 * candidate.
-		 */
-		valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
-		HeapCheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
-		if (valid)
-			*tid = ctid;
-
-		/*
-		 * If there's a valid t_ctid link, follow it, else we're done.
-		 */
-		if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
-			HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
-			HeapTupleHeaderIndicatesMovedPartitions(tp.t_data) ||
-			ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
-		{
-			UnlockReleaseBuffer(buffer);
-			break;
-		}
-
-		ctid = tp.t_data->t_ctid;
-		priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
-		UnlockReleaseBuffer(buffer);
-	}							/* end of loop */
-}
-
 
 /*
  * UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 56b35622f1..84d32bc4ff 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -199,6 +199,123 @@ heapam_fetch_row_version(Relation relation,
 	return false;
 }
 
+/*
+ *	heap_get_latest_tid -  get the latest tid of a specified tuple
+ *
+ * Actually, this gets the latest version that is visible according to the
+ * scan's snapshot.  Create a scan using SnapshotDirty to get the very latest,
+ * possibly uncommitted version.
+ *
+ * *tid is both an input and an output parameter: it is updated to
+ * show the latest version of the row.  Note that it will not be changed
+ * if no version of the row passes the snapshot test.
+ */
+static void
+heap_get_latest_tid(TableScanDesc sscan,
+					ItemPointer tid)
+{
+	Relation	relation = sscan->rs_rd;
+	Snapshot	snapshot = sscan->rs_snapshot;
+	ItemPointerData ctid;
+	TransactionId priorXmax;
+
+	/*
+	 * table_get_latest_tid verified that the passed in tid is valid.  Assume
+	 * that t_ctid links are valid however - there shouldn't be invalid ones
+	 * in the table.
+	 */
+	Assert(ItemPointerIsValid(tid));
+
+	/*
+	 * Loop to chase down t_ctid links.  At top of loop, ctid is the tuple we
+	 * need to examine, and *tid is the TID we will return if ctid turns out
+	 * to be bogus.
+	 *
+	 * Note that we will loop until we reach the end of the t_ctid chain.
+	 * Depending on the snapshot passed, there might be at most one visible
+	 * version of the row, but we don't try to optimize for that.
+	 */
+	ctid = *tid;
+	priorXmax = InvalidTransactionId;	/* cannot check first XMIN */
+	for (;;)
+	{
+		Buffer		buffer;
+		Page		page;
+		OffsetNumber offnum;
+		ItemId		lp;
+		HeapTupleData tp;
+		bool		valid;
+
+		/*
+		 * Read, pin, and lock the page.
+		 */
+		buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid));
+		LockBuffer(buffer, BUFFER_LOCK_SHARE);
+		page = BufferGetPage(buffer);
+		TestForOldSnapshot(snapshot, relation, page);
+
+		/*
+		 * Check for bogus item number.  This is not treated as an error
+		 * condition because it can happen while following a t_ctid link. We
+		 * just assume that the prior tid is OK and return it unchanged.
+		 */
+		offnum = ItemPointerGetOffsetNumber(&ctid);
+		if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(page))
+		{
+			UnlockReleaseBuffer(buffer);
+			break;
+		}
+		lp = PageGetItemId(page, offnum);
+		if (!ItemIdIsNormal(lp))
+		{
+			UnlockReleaseBuffer(buffer);
+			break;
+		}
+
+		/* OK to access the tuple */
+		tp.t_self = ctid;
+		tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+		tp.t_len = ItemIdGetLength(lp);
+		tp.t_tableOid = RelationGetRelid(relation);
+
+		/*
+		 * After following a t_ctid link, we might arrive at an unrelated
+		 * tuple.  Check for XMIN match.
+		 */
+		if (TransactionIdIsValid(priorXmax) &&
+			!TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
+		{
+			UnlockReleaseBuffer(buffer);
+			break;
+		}
+
+		/*
+		 * Check tuple visibility; if visible, set it as the new result
+		 * candidate.
+		 */
+		valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer);
+		HeapCheckForSerializableConflictOut(valid, relation, &tp, buffer, snapshot);
+		if (valid)
+			*tid = ctid;
+
+		/*
+		 * If there's a valid t_ctid link, follow it, else we're done.
+		 */
+		if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
+			HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
+			HeapTupleHeaderIndicatesMovedPartitions(tp.t_data) ||
+			ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
+		{
+			UnlockReleaseBuffer(buffer);
+			break;
+		}
+
+		ctid = tp.t_data->t_ctid;
+		priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
+		UnlockReleaseBuffer(buffer);
+	}							/* end of loop */
+}
+
 static bool
 heapam_tuple_tid_valid(TableScanDesc scan, ItemPointer tid)
 {
-- 
2.27.0.rc0

