From 270ce4cf0dff364ac68ab4f951ca3b2bc4f25068 Mon Sep 17 00:00:00 2001
From: Mark Dilger <mark.dilger@enterprisedb.com>
Date: Wed, 21 Oct 2020 20:26:07 -0700
Subject: [PATCH v20 3/5] Creating non-throwing interface to clog and slru.

---
 src/backend/access/transam/clog.c      | 21 +++---
 src/backend/access/transam/commit_ts.c |  4 +-
 src/backend/access/transam/multixact.c | 16 ++---
 src/backend/access/transam/slru.c      | 23 +++---
 src/backend/access/transam/subtrans.c  |  4 +-
 src/backend/access/transam/transam.c   | 98 +++++++++-----------------
 src/backend/commands/async.c           |  4 +-
 src/backend/storage/lmgr/predicate.c   |  4 +-
 src/include/access/clog.h              | 18 +----
 src/include/access/clogdefs.h          | 33 +++++++++
 src/include/access/slru.h              |  6 +-
 src/include/access/transam.h           |  3 +
 12 files changed, 122 insertions(+), 112 deletions(-)
 create mode 100644 src/include/access/clogdefs.h

diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index 034349aa7b..a2eb3e2983 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -357,7 +357,7 @@ TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids,
 	 * write-busy, since we don't care if the update reaches disk sooner than
 	 * we think.
 	 */
-	slotno = SimpleLruReadPage(XactCtl, pageno, XLogRecPtrIsInvalid(lsn), xid);
+	slotno = SimpleLruReadPage(XactCtl, pageno, XLogRecPtrIsInvalid(lsn), xid, true);
 
 	/*
 	 * Set the main transaction id, if any.
@@ -631,7 +631,7 @@ TransactionIdSetStatusBit(TransactionId xid, XidStatus status, XLogRecPtr lsn, i
  * for most uses; TransactionLogFetch() in transam.c is the intended caller.
  */
 XidStatus
-TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
+TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn, bool throwError)
 {
 	int			pageno = TransactionIdToPage(xid);
 	int			byteno = TransactionIdToByte(xid);
@@ -643,13 +643,18 @@ TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
 
 	/* lock is acquired by SimpleLruReadPage_ReadOnly */
 
-	slotno = SimpleLruReadPage_ReadOnly(XactCtl, pageno, xid);
-	byteptr = XactCtl->shared->page_buffer[slotno] + byteno;
+	slotno = SimpleLruReadPage_ReadOnly(XactCtl, pageno, xid, throwError);
+	if (slotno == InvalidSlotNo)
+		status = TRANSACTION_STATUS_UNKNOWN;
+	else
+	{
+		byteptr = XactCtl->shared->page_buffer[slotno] + byteno;
 
-	status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
+		status = (*byteptr >> bshift) & CLOG_XACT_BITMASK;
 
-	lsnindex = GetLSNIndex(slotno, xid);
-	*lsn = XactCtl->shared->group_lsn[lsnindex];
+		lsnindex = GetLSNIndex(slotno, xid);
+		*lsn = XactCtl->shared->group_lsn[lsnindex];
+	}
 
 	LWLockRelease(XactSLRULock);
 
@@ -796,7 +801,7 @@ TrimCLOG(void)
 		int			slotno;
 		char	   *byteptr;
 
-		slotno = SimpleLruReadPage(XactCtl, pageno, false, xid);
+		slotno = SimpleLruReadPage(XactCtl, pageno, false, xid, true);
 		byteptr = XactCtl->shared->page_buffer[slotno] + byteno;
 
 		/* Zero so-far-unused positions in the current byte */
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index cb8a968801..98c685405c 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -237,7 +237,7 @@ SetXidCommitTsInPage(TransactionId xid, int nsubxids,
 
 	LWLockAcquire(CommitTsSLRULock, LW_EXCLUSIVE);
 
-	slotno = SimpleLruReadPage(CommitTsCtl, pageno, true, xid);
+	slotno = SimpleLruReadPage(CommitTsCtl, pageno, true, xid, true);
 
 	TransactionIdSetCommitTs(xid, ts, nodeid, slotno);
 	for (i = 0; i < nsubxids; i++)
@@ -342,7 +342,7 @@ TransactionIdGetCommitTsData(TransactionId xid, TimestampTz *ts,
 	}
 
 	/* lock is acquired by SimpleLruReadPage_ReadOnly */
-	slotno = SimpleLruReadPage_ReadOnly(CommitTsCtl, pageno, xid);
+	slotno = SimpleLruReadPage_ReadOnly(CommitTsCtl, pageno, xid, true);
 	memcpy(&entry,
 		   CommitTsCtl->shared->page_buffer[slotno] +
 		   SizeOfCommitTimestampEntry * entryno,
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 43653fe572..ed902a9d64 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -881,7 +881,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 	 * enough that a MultiXactId is really involved.  Perhaps someday we'll
 	 * take the trouble to generalize the slru.c error reporting code.
 	 */
-	slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
+	slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi, true);
 	offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 	offptr += entryno;
 
@@ -914,7 +914,7 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
 
 		if (pageno != prev_pageno)
 		{
-			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
+			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi, true);
 			prev_pageno = pageno;
 		}
 
@@ -1345,7 +1345,7 @@ retry:
 	pageno = MultiXactIdToOffsetPage(multi);
 	entryno = MultiXactIdToOffsetEntry(multi);
 
-	slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi);
+	slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, multi, true);
 	offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 	offptr += entryno;
 	offset = *offptr;
@@ -1377,7 +1377,7 @@ retry:
 		entryno = MultiXactIdToOffsetEntry(tmpMXact);
 
 		if (pageno != prev_pageno)
-			slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, tmpMXact);
+			slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, tmpMXact, true);
 
 		offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 		offptr += entryno;
@@ -1418,7 +1418,7 @@ retry:
 
 		if (pageno != prev_pageno)
 		{
-			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi);
+			slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, multi, true);
 			prev_pageno = pageno;
 		}
 
@@ -2063,7 +2063,7 @@ TrimMultiXact(void)
 		int			slotno;
 		MultiXactOffset *offptr;
 
-		slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, nextMXact);
+		slotno = SimpleLruReadPage(MultiXactOffsetCtl, pageno, true, nextMXact, true);
 		offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 		offptr += entryno;
 
@@ -2095,7 +2095,7 @@ TrimMultiXact(void)
 		int			memberoff;
 
 		memberoff = MXOffsetToMemberOffset(offset);
-		slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, offset);
+		slotno = SimpleLruReadPage(MultiXactMemberCtl, pageno, true, offset, true);
 		xidptr = (TransactionId *)
 			(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
 
@@ -2749,7 +2749,7 @@ find_multixact_start(MultiXactId multi, MultiXactOffset *result)
 		return false;
 
 	/* lock is acquired by SimpleLruReadPage_ReadOnly */
-	slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno, multi);
+	slotno = SimpleLruReadPage_ReadOnly(MultiXactOffsetCtl, pageno, multi, true);
 	offptr = (MultiXactOffset *) MultiXactOffsetCtl->shared->page_buffer[slotno];
 	offptr += entryno;
 	offset = *offptr;
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index 16a7898697..daa145eeff 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -385,14 +385,15 @@ SimpleLruWaitIO(SlruCtl ctl, int slotno)
  * The passed-in xid is used only for error reporting, and may be
  * InvalidTransactionId if no specific xid is associated with the action.
  *
- * Return value is the shared-buffer slot number now holding the page.
- * The buffer's LRU access info is updated.
+ * On error, when throwError is false, the return value is negative.
+ * Otherwise, return value is the shared-buffer slot number now holding the
+ * page, and the buffer's LRU access info is updated.
  *
  * Control lock must be held at entry, and will be held at exit.
  */
 int
 SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
-				  TransactionId xid)
+				  TransactionId xid, bool throwError)
 {
 	SlruShared	shared = ctl->shared;
 
@@ -465,7 +466,11 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
 
 		/* Now it's okay to ereport if we failed */
 		if (!ok)
-			SlruReportIOError(ctl, pageno, xid);
+		{
+			if (throwError)
+				SlruReportIOError(ctl, pageno, xid);
+			return InvalidSlotNo;
+		}
 
 		SlruRecentlyUsed(shared, slotno);
 
@@ -484,14 +489,16 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
  * The passed-in xid is used only for error reporting, and may be
  * InvalidTransactionId if no specific xid is associated with the action.
  *
- * Return value is the shared-buffer slot number now holding the page.
- * The buffer's LRU access info is updated.
+ * On error, when throwError is false, the return value is negative.
+ * Otherwise, return value is the shared-buffer slot number now holding the
+ * page, and the buffer's LRU access info is updated.
  *
  * Control lock must NOT be held at entry, but will be held at exit.
  * It is unspecified whether the lock will be shared or exclusive.
  */
 int
-SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid)
+SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid,
+						   bool throwError)
 {
 	SlruShared	shared = ctl->shared;
 	int			slotno;
@@ -520,7 +527,7 @@ SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid)
 	LWLockRelease(shared->ControlLock);
 	LWLockAcquire(shared->ControlLock, LW_EXCLUSIVE);
 
-	return SimpleLruReadPage(ctl, pageno, true, xid);
+	return SimpleLruReadPage(ctl, pageno, true, xid, throwError);
 }
 
 /*
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index 0111e867c7..353b946731 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -83,7 +83,7 @@ SubTransSetParent(TransactionId xid, TransactionId parent)
 
 	LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
 
-	slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid);
+	slotno = SimpleLruReadPage(SubTransCtl, pageno, true, xid, true);
 	ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
 	ptr += entryno;
 
@@ -123,7 +123,7 @@ SubTransGetParent(TransactionId xid)
 
 	/* lock is acquired by SimpleLruReadPage_ReadOnly */
 
-	slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid);
+	slotno = SimpleLruReadPage_ReadOnly(SubTransCtl, pageno, xid, true);
 	ptr = (TransactionId *) SubTransCtl->shared->page_buffer[slotno];
 	ptr += entryno;
 
diff --git a/src/backend/access/transam/transam.c b/src/backend/access/transam/transam.c
index a28918657c..88f867e5ef 100644
--- a/src/backend/access/transam/transam.c
+++ b/src/backend/access/transam/transam.c
@@ -35,7 +35,8 @@ static XidStatus cachedFetchXidStatus;
 static XLogRecPtr cachedCommitLSN;
 
 /* Local functions */
-static XidStatus TransactionLogFetch(TransactionId transactionId);
+static XidStatus TransactionLogFetch(TransactionId transactionId,
+									 bool throwError);
 
 
 /* ----------------------------------------------------------------
@@ -49,7 +50,7 @@ static XidStatus TransactionLogFetch(TransactionId transactionId);
  * TransactionLogFetch --- fetch commit status of specified transaction id
  */
 static XidStatus
-TransactionLogFetch(TransactionId transactionId)
+TransactionLogFetch(TransactionId transactionId, bool throwError)
 {
 	XidStatus	xidstatus;
 	XLogRecPtr	xidlsn;
@@ -76,14 +77,16 @@ TransactionLogFetch(TransactionId transactionId)
 	/*
 	 * Get the transaction status.
 	 */
-	xidstatus = TransactionIdGetStatus(transactionId, &xidlsn);
+	xidstatus = TransactionIdGetStatus(transactionId, &xidlsn, throwError);
 
 	/*
 	 * Cache it, but DO NOT cache status for unfinished or sub-committed
 	 * transactions!  We only cache status that is guaranteed not to change.
+	 * Likewise, DO NOT cache when the status is unknown.
 	 */
 	if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS &&
-		xidstatus != TRANSACTION_STATUS_SUB_COMMITTED)
+		xidstatus != TRANSACTION_STATUS_SUB_COMMITTED &&
+		xidstatus != TRANSACTION_STATUS_UNKNOWN)
 	{
 		cachedFetchXid = transactionId;
 		cachedFetchXidStatus = xidstatus;
@@ -96,6 +99,7 @@ TransactionLogFetch(TransactionId transactionId)
 /* ----------------------------------------------------------------
  *						Interface functions
  *
+ *		TransactionIdResolveStatus
  *		TransactionIdDidCommit
  *		TransactionIdDidAbort
  *		========
@@ -115,24 +119,17 @@ TransactionLogFetch(TransactionId transactionId)
  */
 
 /*
- * TransactionIdDidCommit
- *		True iff transaction associated with the identifier did commit.
- *
- * Note:
- *		Assumes transaction identifier is valid and exists in clog.
+ * TransactionIdResolveStatus
+ * 		Returns the status of the transaction associated with the identifer,
+ *		recursively resolving sub-committed transaction status by checking
+ *		the parent transaction.
  */
-bool							/* true if given transaction committed */
-TransactionIdDidCommit(TransactionId transactionId)
+XidStatus
+TransactionIdResolveStatus(TransactionId transactionId, bool throwError)
 {
 	XidStatus	xidstatus;
 
-	xidstatus = TransactionLogFetch(transactionId);
-
-	/*
-	 * If it's marked committed, it's committed.
-	 */
-	if (xidstatus == TRANSACTION_STATUS_COMMITTED)
-		return true;
+	xidstatus = TransactionLogFetch(transactionId, throwError);
 
 	/*
 	 * If it's marked subcommitted, we have to check the parent recursively.
@@ -153,21 +150,31 @@ TransactionIdDidCommit(TransactionId transactionId)
 		TransactionId parentXid;
 
 		if (TransactionIdPrecedes(transactionId, TransactionXmin))
-			return false;
+			return TRANSACTION_STATUS_ABORTED;
 		parentXid = SubTransGetParent(transactionId);
 		if (!TransactionIdIsValid(parentXid))
 		{
 			elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
 				 transactionId);
-			return false;
+			return TRANSACTION_STATUS_ABORTED;
 		}
-		return TransactionIdDidCommit(parentXid);
+		return TransactionIdResolveStatus(parentXid, throwError);
 	}
+	return xidstatus;
+}
 
-	/*
-	 * It's not committed.
-	 */
-	return false;
+/*
+ * TransactionIdDidCommit
+ *		True iff transaction associated with the identifier did commit.
+ *
+ * Note:
+ *		Assumes transaction identifier is valid and exists in clog.
+ */
+bool							/* true if given transaction committed */
+TransactionIdDidCommit(TransactionId transactionId)
+{
+	return (TransactionIdResolveStatus(transactionId, true) ==
+			TRANSACTION_STATUS_COMMITTED);
 }
 
 /*
@@ -180,43 +187,8 @@ TransactionIdDidCommit(TransactionId transactionId)
 bool							/* true if given transaction aborted */
 TransactionIdDidAbort(TransactionId transactionId)
 {
-	XidStatus	xidstatus;
-
-	xidstatus = TransactionLogFetch(transactionId);
-
-	/*
-	 * If it's marked aborted, it's aborted.
-	 */
-	if (xidstatus == TRANSACTION_STATUS_ABORTED)
-		return true;
-
-	/*
-	 * If it's marked subcommitted, we have to check the parent recursively.
-	 * However, if it's older than TransactionXmin, we can't look at
-	 * pg_subtrans; instead assume that the parent crashed without cleaning up
-	 * its children.
-	 */
-	if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
-	{
-		TransactionId parentXid;
-
-		if (TransactionIdPrecedes(transactionId, TransactionXmin))
-			return true;
-		parentXid = SubTransGetParent(transactionId);
-		if (!TransactionIdIsValid(parentXid))
-		{
-			/* see notes in TransactionIdDidCommit */
-			elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
-				 transactionId);
-			return true;
-		}
-		return TransactionIdDidAbort(parentXid);
-	}
-
-	/*
-	 * It's not aborted.
-	 */
-	return false;
+	return (TransactionIdResolveStatus(transactionId, true) ==
+			TRANSACTION_STATUS_ABORTED);
 }
 
 /*
@@ -419,7 +391,7 @@ TransactionIdGetCommitLSN(TransactionId xid)
 	/*
 	 * Get the transaction status.
 	 */
-	(void) TransactionIdGetStatus(xid, &result);
+	(void) TransactionIdGetStatus(xid, &result, true);
 
 	return result;
 }
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 8dbcace3f9..a49126dba0 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -1477,7 +1477,7 @@ asyncQueueAddEntries(ListCell *nextNotify)
 		slotno = SimpleLruZeroPage(NotifyCtl, pageno);
 	else
 		slotno = SimpleLruReadPage(NotifyCtl, pageno, true,
-								   InvalidTransactionId);
+								   InvalidTransactionId, true);
 
 	/* Note we mark the page dirty before writing in it */
 	NotifyCtl->shared->page_dirty[slotno] = true;
@@ -2010,7 +2010,7 @@ asyncQueueReadAllNotifications(void)
 			 * part of the page we will actually inspect.
 			 */
 			slotno = SimpleLruReadPage_ReadOnly(NotifyCtl, curpage,
-												InvalidTransactionId);
+												InvalidTransactionId, true);
 			if (curpage == QUEUE_POS_PAGE(head))
 			{
 				/* we only want to read as far as head */
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 8a365b400c..6cf12e46f6 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -904,7 +904,7 @@ SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo)
 		slotno = SimpleLruZeroPage(SerialSlruCtl, targetPage);
 	}
 	else
-		slotno = SimpleLruReadPage(SerialSlruCtl, targetPage, true, xid);
+		slotno = SimpleLruReadPage(SerialSlruCtl, targetPage, true, xid, true);
 
 	SerialValue(slotno, xid) = minConflictCommitSeqNo;
 	SerialSlruCtl->shared->page_dirty[slotno] = true;
@@ -946,7 +946,7 @@ SerialGetMinConflictCommitSeqNo(TransactionId xid)
 	 * but will return with that lock held, which must then be released.
 	 */
 	slotno = SimpleLruReadPage_ReadOnly(SerialSlruCtl,
-										SerialPage(xid), xid);
+										SerialPage(xid), xid, true);
 	val = SerialValue(slotno, xid);
 	LWLockRelease(SerialSLRULock);
 	return val;
diff --git a/src/include/access/clog.h b/src/include/access/clog.h
index 6c840cbf29..cf299cd8f6 100644
--- a/src/include/access/clog.h
+++ b/src/include/access/clog.h
@@ -11,24 +11,11 @@
 #ifndef CLOG_H
 #define CLOG_H
 
+#include "access/clogdefs.h"
 #include "access/xlogreader.h"
 #include "storage/sync.h"
 #include "lib/stringinfo.h"
 
-/*
- * Possible transaction statuses --- note that all-zeroes is the initial
- * state.
- *
- * A "subcommitted" transaction is a committed subtransaction whose parent
- * hasn't committed or aborted yet.
- */
-typedef int XidStatus;
-
-#define TRANSACTION_STATUS_IN_PROGRESS		0x00
-#define TRANSACTION_STATUS_COMMITTED		0x01
-#define TRANSACTION_STATUS_ABORTED			0x02
-#define TRANSACTION_STATUS_SUB_COMMITTED	0x03
-
 typedef struct xl_clog_truncate
 {
 	int			pageno;
@@ -38,7 +25,8 @@ typedef struct xl_clog_truncate
 
 extern void TransactionIdSetTreeStatus(TransactionId xid, int nsubxids,
 									   TransactionId *subxids, XidStatus status, XLogRecPtr lsn);
-extern XidStatus TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn);
+extern XidStatus TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn,
+										bool throwError);
 
 extern Size CLOGShmemBuffers(void);
 extern Size CLOGShmemSize(void);
diff --git a/src/include/access/clogdefs.h b/src/include/access/clogdefs.h
new file mode 100644
index 0000000000..0f9996bb08
--- /dev/null
+++ b/src/include/access/clogdefs.h
@@ -0,0 +1,33 @@
+/*
+ * clogdefs.h
+ *
+ * PostgreSQL transaction-commit-log manager
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/clogdefs.h
+ */
+#ifndef CLOGDEFS_H
+#define CLOGDEFS_H
+
+/*
+ * Possible transaction statuses --- note that all-zeroes is the initial
+ * state.
+ *
+ * A "subcommitted" transaction is a committed subtransaction whose parent
+ * hasn't committed or aborted yet.
+ *
+ * An "unknown" status indicates an error condition, such as when the clog has
+ * been erroneously truncated and the commit status of a transaction cannot be
+ * determined.
+ */
+typedef enum XidStatus {
+	TRANSACTION_STATUS_IN_PROGRESS = 0x00,
+	TRANSACTION_STATUS_COMMITTED = 0x01,
+	TRANSACTION_STATUS_ABORTED = 0x02,
+	TRANSACTION_STATUS_SUB_COMMITTED = 0x03,
+	TRANSACTION_STATUS_UNKNOWN = 0x04	/* error condition */
+} XidStatus;
+
+#endif							/* CLOG_H */
diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index b39b43504d..0b6a5669d8 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -133,6 +133,8 @@ typedef struct SlruCtlData
 
 typedef SlruCtlData *SlruCtl;
 
+#define InvalidSlotNo ((int) -1)
+
 
 extern Size SimpleLruShmemSize(int nslots, int nlsns);
 extern void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
@@ -140,9 +142,9 @@ extern void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
 						  SyncRequestHandler sync_handler);
 extern int	SimpleLruZeroPage(SlruCtl ctl, int pageno);
 extern int	SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
-							  TransactionId xid);
+							  TransactionId xid, bool throwError);
 extern int	SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno,
-									   TransactionId xid);
+									   TransactionId xid, bool throwError);
 extern void SimpleLruWritePage(SlruCtl ctl, int slotno);
 extern void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied);
 extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage);
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 2f1f144db4..7d5e2f614d 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -14,6 +14,7 @@
 #ifndef TRANSAM_H
 #define TRANSAM_H
 
+#include "access/clogdefs.h"
 #include "access/xlogdefs.h"
 
 
@@ -264,6 +265,8 @@ extern PGDLLIMPORT VariableCache ShmemVariableCache;
 /*
  * prototypes for functions in transam/transam.c
  */
+extern XidStatus TransactionIdResolveStatus(TransactionId transactionId,
+											bool throwError);
 extern bool TransactionIdDidCommit(TransactionId transactionId);
 extern bool TransactionIdDidAbort(TransactionId transactionId);
 extern bool TransactionIdIsKnownCompleted(TransactionId transactionId);
-- 
2.21.1 (Apple Git-122.3)

