From f67b77aeb23317b2f135df188d20b98decabfa0e Mon Sep 17 00:00:00 2001
From: Amit Kapila <amit.kapila@enterprisedb.com>
Date: Thu, 13 Jun 2019 15:42:46 +0530
Subject: [PATCH 13/14] Allow foreground transactions to perform undo actions
 on abort.

We always perform rollback actions after cleaning up the current
(sub)transaction.  This will ensure that we perform the actions immediately
after an error (and release the locks) rather than when the user issues
Rollback command at some later point of time.  We are releasing the locks
after the undo actions are applied.  The reason to delay lock release is
that if we release locks before applying undo actions, then the parallel
session can acquire the lock before us which can lead to deadlock.

Amit Kapila and Dilip Kumar  with inputs from Robert Haas
---
 src/backend/access/transam/twophase.c         |  82 +++-
 src/backend/access/transam/varsup.c           |  24 ++
 src/backend/access/transam/xact.c             | 595 +++++++++++++++++++++++++-
 src/backend/access/undo/README.UndoProcessing |  39 ++
 src/backend/access/undo/undoaccess.c          |   7 +
 src/backend/utils/error/elog.c                |  36 +-
 src/backend/utils/init/globals.c              |   6 +
 src/backend/utils/resowner/resowner.c         |  11 +-
 src/include/access/transam.h                  |   1 +
 src/include/access/twophase.h                 |   3 +-
 src/include/access/xact.h                     |  10 +
 src/include/miscadmin.h                       |   2 +
 src/include/utils/elog.h                      |   3 +
 13 files changed, 798 insertions(+), 21 deletions(-)
 create mode 100644 src/backend/access/undo/README.UndoProcessing

diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 5196d61..d3b221d 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -82,6 +82,7 @@
 #include "access/transam.h"
 #include "access/twophase.h"
 #include "access/twophase_rmgr.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -927,6 +928,16 @@ typedef struct TwoPhaseFileHeader
 	uint16		gidlen;			/* length of the GID - GID follows the header */
 	XLogRecPtr	origin_lsn;		/* lsn of this record at origin node */
 	TimestampTz origin_timestamp;	/* time of prepare at origin node */
+
+	/*
+	 * We need the locations of the start and end undo record pointers when
+	 * rollbacks are to be performed for prepared transactions using undo-based
+	 * relations.  We need to store this information in the file as the user
+	 * might rollback the prepared transaction after recovery and for that we
+	 * need it's start and end undo locations.
+	 */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
 } TwoPhaseFileHeader;
 
 /*
@@ -1001,7 +1012,8 @@ save_state_data(const void *data, uint32 len)
  * Initializes data structure and inserts the 2PC file header record.
  */
 void
-StartPrepare(GlobalTransaction gxact)
+StartPrepare(GlobalTransaction gxact, UndoRecPtr *start_urec_ptr,
+			 UndoRecPtr *end_urec_ptr)
 {
 	PGPROC	   *proc = &ProcGlobal->allProcs[gxact->pgprocno];
 	PGXACT	   *pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
@@ -1032,6 +1044,11 @@ StartPrepare(GlobalTransaction gxact)
 	hdr.database = proc->databaseId;
 	hdr.prepared_at = gxact->prepared_at;
 	hdr.owner = gxact->owner;
+
+	/* save the start and end undo record pointers */
+	memcpy(hdr.start_urec_ptr, start_urec_ptr, sizeof(hdr.start_urec_ptr));
+	memcpy(hdr.end_urec_ptr, end_urec_ptr, sizeof(hdr.end_urec_ptr));
+
 	hdr.nsubxacts = xactGetCommittedChildren(&children);
 	hdr.ncommitrels = smgrGetPendingDeletes(true, &commitrels);
 	hdr.nabortrels = smgrGetPendingDeletes(false, &abortrels);
@@ -1468,6 +1485,12 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	RelFileNode *delrels;
 	int			ndelrels;
 	SharedInvalidationMessage *invalmsgs;
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	end_urec_ptr[UndoPersistenceLevels];
+	bool		undo_action_pushed[UndoPersistenceLevels];
+	uint32		epoch;
+	int			i;
+	FullTransactionId full_xid;
 
 	/*
 	 * Validate the GID, and lock the GXACT to ensure that two backends do not
@@ -1505,6 +1528,10 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	invalmsgs = (SharedInvalidationMessage *) bufptr;
 	bufptr += MAXALIGN(hdr->ninvalmsgs * sizeof(SharedInvalidationMessage));
 
+	/* save the start and end undo record pointers */
+	memcpy(start_urec_ptr, hdr->start_urec_ptr, sizeof(start_urec_ptr));
+	memcpy(end_urec_ptr, hdr->end_urec_ptr, sizeof(end_urec_ptr));
+
 	/* compute latestXid among all children */
 	latestXid = TransactionIdLatest(xid, hdr->nsubxacts, children);
 
@@ -1518,6 +1545,19 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 	 * TransactionIdIsInProgress will stop saying the prepared xact is in
 	 * progress), then run the post-commit or post-abort callbacks. The
 	 * callbacks will release the locks the transaction held.
+	 *
+	 * XXX Note that, unlike non-two-phase transactions, we don't skip
+	 * releasing the locks when we have to perform the undo actions.  The
+	 * reason is that here the locks are not directly associated with current
+	 * transaction, so we need to keep the two phase records unprocessed (or
+	 * find some other way to associate it with current transaction's resource
+	 * owner) till the undo actions are completetly applied.  Also, we might
+	 * need to retain TwoPhaseStateLock till the locks are released which is
+	 * again not a good idea.  The downside is that we might face deadlock
+	 * while applying undo actions which ideally we should try to avoid but
+	 * not sure if that is worth complicating this subsytem.  Anyway,
+	 * currently such deadlock risks are there while executing undo actions
+	 * for toast relations as well.
 	 */
 	if (isCommit)
 		RecordTransactionCommitPrepared(xid,
@@ -1526,10 +1566,36 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 										hdr->ninvalmsgs, invalmsgs,
 										hdr->initfileinval, gid);
 	else
+	{
+		/*
+		 * We don't allow XIDs with an age of more than 2 billion in undo, so
+		 * we can infer the epoch here. (XXX We can add full transaction id in
+		 * TwoPhaseFileHeader instead.)
+		 */
+		epoch = GetEpochForXid(hdr->xid);
+		full_xid = FullTransactionIdFromEpochAndXid(epoch, hdr->xid);
+
+		/*
+		 * Register the rollback request to apply undo actions.  It is
+		 * important to do this before marking it aborted in clog, see
+		 * comments atop PushUndoRequest for further details.
+		 */
+		for (i = 0; i < UndoPersistenceLevels; i++)
+		{
+			if (end_urec_ptr[i] != InvalidUndoRecPtr && i != UNDO_TEMP)
+			{
+				undo_action_pushed[i] = RegisterRollbackReq(end_urec_ptr[i],
+															start_urec_ptr[i],
+															hdr->database,
+															full_xid);
+			}
+		}
+
 		RecordTransactionAbortPrepared(xid,
 									   hdr->nsubxacts, children,
 									   hdr->nabortrels, abortrels,
 									   gid);
+	}
 
 	ProcArrayRemove(proc, latestXid);
 
@@ -1612,6 +1678,20 @@ FinishPreparedTransaction(const char *gid, bool isCommit)
 
 	MyLockedGxact = NULL;
 
+	if (!isCommit)
+	{
+		/*
+		 * Perform undo actions, if there are undologs for this transaction.
+		 * We need to perform undo actions while we are still in a transaction.
+		 */
+		if (!PerformUndoActions(full_xid, hdr->database, end_urec_ptr,
+								start_urec_ptr, undo_action_pushed,
+								false))
+		{
+			FlushErrorState();
+		}
+	}
+
 	RESUME_INTERRUPTS();
 
 	pfree(buf);
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 5b759ec..fd01989 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -566,3 +566,27 @@ GetNewObjectId(void)
 
 	return result;
 }
+
+/*
+ * Get epoch for the given xid.
+ */
+uint32
+GetEpochForXid(TransactionId xid)
+{
+	FullTransactionId next_fxid;
+	TransactionId next_xid;
+	uint32		epoch;
+
+	next_fxid = ReadNextFullTransactionId();
+	next_xid = XidFromFullTransactionId(next_fxid);
+	epoch = EpochFromFullTransactionId(next_fxid);
+
+	/*
+	 * If xid is numerically bigger than next_xid, it has to be from the last
+	 * epoch.
+	 */
+	if (unlikely(xid > next_xid))
+		epoch--;
+
+	return epoch;
+}
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 821652b..3253a0c 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -26,6 +26,7 @@
 #include "access/subtrans.h"
 #include "access/transam.h"
 #include "access/twophase.h"
+#include "access/undorequest.h"
 #include "access/xact.h"
 #include "access/xlog.h"
 #include "access/xloginsert.h"
@@ -128,7 +129,8 @@ typedef enum TransState
 	TRANS_INPROGRESS,			/* inside a valid transaction */
 	TRANS_COMMIT,				/* commit in progress */
 	TRANS_ABORT,				/* abort in progress */
-	TRANS_PREPARE				/* prepare in progress */
+	TRANS_PREPARE,				/* prepare in progress */
+	TRANS_UNDO					/* undo apply in progress */
 } TransState;
 
 /*
@@ -153,6 +155,7 @@ typedef enum TBlockState
 	TBLOCK_ABORT_END,			/* failed xact, ROLLBACK received */
 	TBLOCK_ABORT_PENDING,		/* live xact, ROLLBACK received */
 	TBLOCK_PREPARE,				/* live xact, PREPARE received */
+	TBLOCK_UNDO,				/* failed xact, awaiting undo to be applied */
 
 	/* subtransaction states */
 	TBLOCK_SUBBEGIN,			/* starting a subtransaction */
@@ -163,7 +166,8 @@ typedef enum TBlockState
 	TBLOCK_SUBABORT_END,		/* failed subxact, ROLLBACK received */
 	TBLOCK_SUBABORT_PENDING,	/* live subxact, ROLLBACK received */
 	TBLOCK_SUBRESTART,			/* live subxact, ROLLBACK TO received */
-	TBLOCK_SUBABORT_RESTART		/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBABORT_RESTART,	/* failed subxact, ROLLBACK TO received */
+	TBLOCK_SUBUNDO				/* failed subxact, awaiting undo to be applied */
 } TBlockState;
 
 /*
@@ -191,6 +195,15 @@ typedef struct TransactionStateData
 	bool		didLogXid;		/* has xid been included in WAL record? */
 	int			parallelModeLevel;	/* Enter/ExitParallelMode counter */
 	bool		chain;			/* start a new block after this one */
+
+	/* start and end undo record location for each persistence level */
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];	/* this is 'to' location */
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels]; /* this is 'from'
+														 * location */
+	bool		undo_req_pushed[UndoPersistenceLevels]; /* undo request pushed
+														 * to worker? */
+	bool		performUndoActions;
+
 	struct TransactionStateData *parent;	/* back link to parent */
 } TransactionStateData;
 
@@ -339,6 +352,7 @@ static void ShowTransactionState(const char *str);
 static void ShowTransactionStateRec(const char *str, TransactionState state);
 static const char *BlockStateAsString(TBlockState blockState);
 static const char *TransStateAsString(TransState state);
+static void PushUndoRequest(void);
 
 
 /* ----------------------------------------------------------------
@@ -362,9 +376,9 @@ IsTransactionState(void)
 	 * also reject the startup/shutdown states TRANS_START, TRANS_COMMIT,
 	 * TRANS_PREPARE since it might be too soon or too late within those
 	 * transition states to do anything interesting.  Hence, the only "valid"
-	 * state is TRANS_INPROGRESS.
+	 * state is TRANS_INPROGRESS or TRANS_UNDO.
 	 */
-	return (s->state == TRANS_INPROGRESS);
+	return (s->state == TRANS_INPROGRESS || s->state == TRANS_UNDO);
 }
 
 /*
@@ -723,9 +737,14 @@ SubTransactionIsActive(SubTransactionId subxid)
 {
 	TransactionState s;
 
+	/*
+	 * The subtransaction is not considered active if it is being aborted or
+	 * in undo apply state, even though it may still have an entry on the
+	 * state stack.
+	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (s->subTransactionId == subxid)
 			return true;
@@ -905,15 +924,15 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
 	 * We will return true for the Xid of the current subtransaction, any of
 	 * its subcommitted children, any of its parents, or any of their
 	 * previously subcommitted children.  However, a transaction being aborted
-	 * is no longer "current", even though it may still have an entry on the
-	 * state stack.
+	 * or in undo apply state is no longer "current", even though it may still
+	 * have an entry on the state stack.
 	 */
 	for (s = CurrentTransactionState; s != NULL; s = s->parent)
 	{
 		int			low,
 					high;
 
-		if (s->state == TRANS_ABORT)
+		if (s->state == TRANS_ABORT || s->state == TRANS_UNDO)
 			continue;
 		if (!FullTransactionIdIsValid(s->fullTransactionId))
 			continue;			/* it can't have any child XIDs either */
@@ -1885,6 +1904,7 @@ StartTransaction(void)
 {
 	TransactionState s;
 	VirtualTransactionId vxid;
+	int			i;
 
 	/*
 	 * Let's just make sure the state stack is empty
@@ -1968,6 +1988,15 @@ StartTransaction(void)
 	nUnreportedXids = 0;
 	s->didLogXid = false;
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+		s->undo_req_pushed[i] = false;
+	}
+	s->performUndoActions = false;
+
 	/*
 	 * must initialize resource-management stuff first
 	 */
@@ -2264,6 +2293,8 @@ CommitTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with commit processing, set current transaction state back to
 	 * default
@@ -2280,7 +2311,7 @@ CommitTransaction(void)
  * NB: if you change this routine, better look at CommitTransaction too!
  */
 static void
-PrepareTransaction(void)
+PrepareTransaction(UndoRecPtr *start_urec_ptr, UndoRecPtr *end_urec_ptr)
 {
 	TransactionState s = CurrentTransactionState;
 	TransactionId xid = GetCurrentTransactionId();
@@ -2433,7 +2464,7 @@ PrepareTransaction(void)
 	 * PREPARED; in particular, pay attention to whether things should happen
 	 * before or after releasing the transaction's locks.
 	 */
-	StartPrepare(gxact);
+	StartPrepare(gxact, start_urec_ptr, end_urec_ptr);
 
 	AtPrepare_Notify();
 	AtPrepare_Locks();
@@ -2622,7 +2653,9 @@ AbortTransaction(void)
 	 * check the current transaction state
 	 */
 	is_parallel_worker = (s->blockState == TBLOCK_PARALLEL_INPROGRESS);
-	if (s->state != TRANS_INPROGRESS && s->state != TRANS_PREPARE)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_PREPARE &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortTransaction while in %s state",
 			 TransStateAsString(s->state));
 	Assert(s->parent == NULL);
@@ -2780,6 +2813,8 @@ CleanupTransaction(void)
 	XactTopFullTransactionId = InvalidFullTransactionId;
 	nParallelCurrentXids = 0;
 
+	ResetUndoActionsInfo();
+
 	/*
 	 * done with abort processing, set current transaction state back to
 	 * default
@@ -2845,6 +2880,8 @@ StartTransactionCommand(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(ERROR, "StartTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2906,9 +2943,17 @@ CommitTransactionCommand(void)
 			 * StartTransactionCommand didn't set the STARTED state
 			 * appropriately, while TBLOCK_PARALLEL_INPROGRESS should be ended
 			 * by EndParallelWorkerTransaction(), not this function.
+			 *
+			 * TBLOCK_(SUB)UNDO means the error has occurred while applying
+			 * undo for a (sub)transaction.  We can't reach here as while
+			 * applying undo via top-level transaction, if we get an error,
+			 * then it is handled by ApplyUndoActions and for subtransaction,
+			 * we promote the error to fatal in such a situation.
 			 */
 		case TBLOCK_DEFAULT:
 		case TBLOCK_PARALLEL_INPROGRESS:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "CommitTransactionCommand: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -2987,11 +3032,14 @@ CommitTransactionCommand(void)
 
 			/*
 			 * Here we were in a perfectly good transaction block but the user
-			 * told us to ROLLBACK anyway.  We have to abort the transaction
-			 * and then clean up.
+			 * told us to ROLLBACK anyway.  We have to abort the transaction,
+			 * apply the undo actions if any and then clean up.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			UndoActionsRequired();
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			if (s->chain)
@@ -3008,7 +3056,7 @@ CommitTransactionCommand(void)
 			 * return to the idle state.
 			 */
 		case TBLOCK_PREPARE:
-			PrepareTransaction();
+			PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 			s->blockState = TBLOCK_DEFAULT;
 			break;
 
@@ -3052,6 +3100,24 @@ CommitTransactionCommand(void)
 		case TBLOCK_SUBCOMMIT:
 			do
 			{
+				int			i;
+
+				/*
+				 * Before cleaning up the current sub transaction state,
+				 * overwrite parent transaction's latest_urec_ptr with current
+				 * transaction's latest_urec_ptr so that in case parent
+				 * transaction get aborted we must not skip performing undo
+				 * for this transaction.  Also set the start_urec_ptr if
+				 * parent start_urec_ptr is not valid.
+				 */
+				for (i = 0; i < UndoPersistenceLevels; i++)
+				{
+					if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+						s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+					if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+						s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+				}
+
 				CommitSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
 			} while (s->blockState == TBLOCK_SUBCOMMIT);
@@ -3065,7 +3131,7 @@ CommitTransactionCommand(void)
 			else if (s->blockState == TBLOCK_PREPARE)
 			{
 				Assert(s->parent == NULL);
-				PrepareTransaction();
+				PrepareTransaction(s->start_urec_ptr, s->latest_urec_ptr);
 				s->blockState = TBLOCK_DEFAULT;
 			}
 			else
@@ -3087,7 +3153,10 @@ CommitTransactionCommand(void)
 			 * As above, but it's not dead yet, so abort first.
 			 */
 		case TBLOCK_SUBABORT_PENDING:
+			UndoActionsRequired();
+			PushUndoRequest();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			CommitTransactionCommand();
 			break;
@@ -3107,7 +3176,10 @@ CommitTransactionCommand(void)
 				s->name = NULL;
 				savepointLevel = s->savepointLevel;
 
+				UndoActionsRequired();
+				PushUndoRequest();
 				AbortSubTransaction();
+				ApplyUndoActions();
 				CleanupSubTransaction();
 
 				DefineSavepoint(NULL);
@@ -3160,6 +3232,14 @@ AbortCurrentTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
 
+	/*
+	 * Here, we just detect whether there are any pending undo actions so that
+	 * we can skip releasing the locks during abort transaction.  We don't
+	 * release the locks till we execute undo actions otherwise, there is a
+	 * risk of deadlock.
+	 */
+	UndoActionsRequired();
+
 	switch (s->blockState)
 	{
 		case TBLOCK_DEFAULT:
@@ -3175,7 +3255,11 @@ AbortCurrentTransaction(void)
 				 * incompletely started transaction.  First, adjust the
 				 * low-level state to suppress warning message from
 				 * AbortTransaction.
+				 *
+				 * In this state, we must not have performed any operation
+				 * which can generate undo.
 				 */
+				Assert(!s->performUndoActions);
 				if (s->state == TRANS_START)
 					s->state = TRANS_INPROGRESS;
 				AbortTransaction();
@@ -3190,7 +3274,9 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_STARTED:
 		case TBLOCK_IMPLICIT_INPROGRESS:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3201,8 +3287,12 @@ AbortCurrentTransaction(void)
 			 * will interpret the error as meaning the BEGIN failed to get him
 			 * into a transaction block, so we should abort and return to idle
 			 * state.
+			 *
+			 * In this state, we must not have performed any operation which
+			 * which can generate undo.
 			 */
 		case TBLOCK_BEGIN:
+			Assert(!s->performUndoActions);
 			AbortTransaction();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
@@ -3215,7 +3305,9 @@ AbortCurrentTransaction(void)
 			 */
 		case TBLOCK_INPROGRESS:
 		case TBLOCK_PARALLEL_INPROGRESS:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_ABORT;
 			/* CleanupTransaction happens when we exit TBLOCK_ABORT_END */
 			break;
@@ -3226,7 +3318,9 @@ AbortCurrentTransaction(void)
 			 * the transaction).
 			 */
 		case TBLOCK_END:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3255,7 +3349,9 @@ AbortCurrentTransaction(void)
 			 * Abort, cleanup, go to idle state.
 			 */
 		case TBLOCK_ABORT_PENDING:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3266,7 +3362,9 @@ AbortCurrentTransaction(void)
 			 * the transaction).
 			 */
 		case TBLOCK_PREPARE:
+			PushUndoRequest();
 			AbortTransaction();
+			ApplyUndoActions();
 			CleanupTransaction();
 			s->blockState = TBLOCK_DEFAULT;
 			break;
@@ -3277,7 +3375,9 @@ AbortCurrentTransaction(void)
 			 * we get ROLLBACK.
 			 */
 		case TBLOCK_SUBINPROGRESS:
+			PushUndoRequest();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			s->blockState = TBLOCK_SUBABORT;
 			break;
 
@@ -3291,7 +3391,9 @@ AbortCurrentTransaction(void)
 		case TBLOCK_SUBCOMMIT:
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
+			PushUndoRequest();
 			AbortSubTransaction();
+			ApplyUndoActions();
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
@@ -3304,10 +3406,173 @@ AbortCurrentTransaction(void)
 			CleanupSubTransaction();
 			AbortCurrentTransaction();
 			break;
+
+			/*
+			 * The error occurred while applying undo for a (sub)transaction.
+			 * We can't reach here as while applying undo via top-level
+			 * transaction, if we get an error, then it is handled by
+			 * ApplyUndoActions and for subtransaction, we promote the error
+			 * to fatal in such a situation.
+			 */
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
+			elog(FATAL, "AbortCurrentTransaction: unexpected state %s",
+				 BlockStateAsString(s->blockState));
+			break;
 	}
 }
 
 /*
+ * PushUndoRequest - Register the request for apllying undo actions.
+ *
+ * It sets the transaction state to indicate whether the request is pushed to
+ * the background worker which is used later to decide whether to apply the
+ * actions.
+ *
+ * It is important to do this before marking the transaction as aborted in
+ * clog otherwise, it is quite possible that discard worker miss this rollback
+ * request from the computation of oldestXidHavingUnappliedUndo.  This is
+ * because it might do that computation before backend can register it in the
+ * rollback hash table.  So, neither oldestXmin computation will consider it
+ * nor the hash table pass would have that value.
+ */
+static void
+PushUndoRequest()
+{
+	TransactionState s = CurrentTransactionState;
+	bool	result;
+	volatile int per_level;
+
+	if (!s->performUndoActions)
+		return;
+	/*
+	 * We can't postpone applying undo actions for subtransactions as the
+	 * modifications made by aborted subtransaction must not be visible even if
+	 * the main transaction commits.
+	 */
+	if (IsSubTransaction())
+		return;
+
+	for (per_level = 0; per_level < UndoPersistenceLevels; per_level++)
+	{
+		/*
+		 * We can't push the undo actions for temp table to background
+		 * workers as the the temp tables are only accessible in the
+		 * backend that has created them.
+		 */
+		if (per_level != UNDO_TEMP && s->latest_urec_ptr[per_level])
+		{
+			result = RegisterRollbackReq(s->latest_urec_ptr[per_level],
+										 s->start_urec_ptr[per_level],
+										 MyDatabaseId,
+										 GetTopFullTransactionId());
+			s->undo_req_pushed[per_level] = result;
+		}
+	}
+}
+
+/*
+ * ApplyUndoActions - Execute undo actions for current (sub)xact.
+ *
+ * To execute undo actions during abort, we bring the transaction to a clean
+ * state by releasing the required resources and put it in a new state
+ * TRANS_UNDO.
+ *
+ * Note that we release locks after applying undo actions.  We skip them
+ * during Abort(Sub)Transaction as otherwise there is always a risk of
+ * deadlock when we need to re-take them during processing of undo actions.
+ */
+void
+ApplyUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+	bool		ret;
+
+	if (!s->performUndoActions)
+		return;
+
+	/*
+	 * State should still be TRANS_ABORT from AbortTransaction().
+	 */
+	if (s->state != TRANS_ABORT)
+		elog(FATAL, "ApplyUndoActions: unexpected state %s",
+			 TransStateAsString(s->state));
+
+	/*
+	 * We promote the error level to FATAL if we get an error while applying
+	 * undo for the subtransaction.  See errstart.  So, we should never reach
+	 * here for such a case.
+	 */
+	Assert(!applying_subxact_undo);
+
+	/*
+	 * Do abort cleanup processing before applying the undo actions.  We must
+	 * do this before applying the undo actions to remove the effects of
+	 * failed transaction.
+	 */
+	if (IsSubTransaction())
+	{
+		AtSubCleanup_Portals(s->subTransactionId);
+		s->blockState = TBLOCK_SUBUNDO;
+		applying_subxact_undo = true;
+
+		/* We can't afford to allow cancel of subtransaction's rollback. */
+		HOLD_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		AtCleanup_Portals();	/* now safe to release portal memory */
+		AtEOXact_Snapshot(false, true); /* and release the transaction's
+										 * snapshots */
+		s->fullTransactionId = InvalidFullTransactionId;
+		s->subTransactionId = TopSubTransactionId;
+		s->blockState = TBLOCK_UNDO;
+	}
+
+	s->state = TRANS_UNDO;
+
+	ret = PerformUndoActions(GetTopFullTransactionId(), MyDatabaseId,
+							 s->latest_urec_ptr, s->start_urec_ptr,
+							 s->undo_req_pushed,
+							 IsSubTransaction());
+
+	if (!ret)
+	{
+		/*
+		 * This should take care of releasing the locks held under
+		 * TopTransactionResourceOwner.
+		 */
+		AbortTransaction();
+	}
+
+	/* Reset undo information */
+	ResetUndoActionsInfo();
+
+	applying_subxact_undo = false;
+
+	/* Release the locks after applying undo actions. */
+	if (IsSubTransaction())
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, false);
+		RESUME_CANCEL_INTERRUPTS();
+	}
+	else
+	{
+		ResourceOwnerRelease(s->curTransactionOwner,
+							 RESOURCE_RELEASE_LOCKS,
+							 false, true);
+	}
+
+	/*
+	 * Here we again put back the transaction in abort state so that callers
+	 * can proceed with the cleanup work.
+	 */
+	s->state = TRANS_ABORT;
+}
+
+/*
  *	PreventInTransactionBlock
  *
  *	This routine is to be called by statements that must not run inside
@@ -3633,6 +3898,8 @@ BeginTransactionBlock(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3825,6 +4092,8 @@ EndTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "EndTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -3941,6 +4210,8 @@ UserAbortTransactionBlock(bool chain)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4081,6 +4352,8 @@ DefineSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "DefineSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4099,6 +4372,18 @@ ReleaseSavepoint(const char *name)
 	TransactionState s = CurrentTransactionState;
 	TransactionState target,
 				xact;
+	UndoRecPtr	latest_urec_ptr[UndoPersistenceLevels];
+	UndoRecPtr	start_urec_ptr[UndoPersistenceLevels];
+	int			i = 0;
+
+	/*
+	 * Remember the 'from' and 'to' locations of the current transaction so
+	 * that we can propagate it to parent transaction.  This is required
+	 * because in case the parent transaction get aborted we must not skip
+	 * performing undo for this transaction.
+	 */
+	memcpy(latest_urec_ptr, s->latest_urec_ptr, sizeof(latest_urec_ptr));
+	memcpy(start_urec_ptr, s->start_urec_ptr, sizeof(start_urec_ptr));
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4157,6 +4442,8 @@ ReleaseSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "ReleaseSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4192,8 +4479,37 @@ ReleaseSavepoint(const char *name)
 		if (xact == target)
 			break;
 		xact = xact->parent;
+
+		/*
+		 * Propagate the 'from' and 'to' undo locations to parent transaction.
+		 */
+		for (i = 0; i < UndoPersistenceLevels; i++)
+		{
+			if (!UndoRecPtrIsValid(latest_urec_ptr[i]))
+				latest_urec_ptr[i] = xact->latest_urec_ptr[i];
+
+			if (UndoRecPtrIsValid(xact->start_urec_ptr[i]))
+				start_urec_ptr[i] = xact->start_urec_ptr[i];
+		}
+
+
 		Assert(PointerIsValid(xact));
 	}
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.  Also set the
+	 * start_urec_ptr if parent start_urec_ptr is not valid.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(latest_urec_ptr[i]))
+			xact->parent->latest_urec_ptr[i] = latest_urec_ptr[i];
+		if (!UndoRecPtrIsValid(xact->parent->start_urec_ptr[i]))
+			xact->parent->start_urec_ptr[i] = start_urec_ptr[i];
+	}
 }
 
 /*
@@ -4266,6 +4582,8 @@ RollbackToSavepoint(const char *name)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackToSavepoint: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4384,6 +4702,8 @@ BeginInternalSubTransaction(const char *name)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
@@ -4404,6 +4724,7 @@ void
 ReleaseCurrentSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	/*
 	 * Workers synchronize transaction state at the beginning of each parallel
@@ -4422,6 +4743,22 @@ ReleaseCurrentSubTransaction(void)
 			 BlockStateAsString(s->blockState));
 	Assert(s->state == TRANS_INPROGRESS);
 	MemoryContextSwitchTo(CurTransactionContext);
+
+	/*
+	 * Before cleaning up the current sub transaction state, overwrite parent
+	 * transaction's latest_urec_ptr with current transaction's
+	 * latest_urec_ptr so that in case parent transaction get aborted we will
+	 * not skip performing undo for this transaction.
+	 */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (UndoRecPtrIsValid(s->latest_urec_ptr[i]))
+			s->parent->latest_urec_ptr[i] = s->latest_urec_ptr[i];
+
+		if (!UndoRecPtrIsValid(s->parent->start_urec_ptr[i]))
+			s->parent->start_urec_ptr[i] = s->start_urec_ptr[i];
+	}
+
 	CommitSubTransaction();
 	s = CurrentTransactionState;	/* changed by pop */
 	Assert(s->state == TRANS_INPROGRESS);
@@ -4473,17 +4810,32 @@ RollbackAndReleaseCurrentSubTransaction(void)
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
 		case TBLOCK_PREPARE:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
 				 BlockStateAsString(s->blockState));
 			break;
 	}
 
 	/*
+	 * Set the information required to perform undo actions.  Note that, it
+	 * must be done before AbortSubTransaction as we need to skip releasing
+	 * locks if that is the case.  See ApplyUndoActions.
+	 */
+	UndoActionsRequired();
+
+	/* Try to push rollback request to worker if possible. */
+	PushUndoRequest();
+
+	/*
 	 * Abort the current subtransaction, if needed.
 	 */
 	if (s->blockState == TBLOCK_SUBINPROGRESS)
 		AbortSubTransaction();
 
+	/* Execute undo actions */
+	ApplyUndoActions();
+
 	/* And clean it up, too */
 	CleanupSubTransaction();
 
@@ -4514,6 +4866,14 @@ AbortOutOfAnyTransaction(void)
 	 */
 	do
 	{
+		/*
+		 * Here, we just detect whether there are any pending undo actions so that
+		 * we can skip releasing the locks during abort transaction.  We don't
+		 * release the locks till we execute undo actions otherwise, there is a
+		 * risk of deadlock.
+		 */
+		UndoActionsRequired();
+
 		switch (s->blockState)
 		{
 			case TBLOCK_DEFAULT:
@@ -4529,7 +4889,11 @@ AbortOutOfAnyTransaction(void)
 					 * incompletely started transaction.  First, adjust the
 					 * low-level state to suppress warning message from
 					 * AbortTransaction.
+					 *
+					 * In this state, we must not have performed any operation
+					 * which can generate undo.
 					 */
+					Assert(!s->performUndoActions);
 					if (s->state == TRANS_START)
 						s->state = TRANS_INPROGRESS;
 					AbortTransaction();
@@ -4545,6 +4909,20 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_ABORT_PENDING:
 			case TBLOCK_PREPARE:
 				/* In a transaction, so clean up */
+				PushUndoRequest();
+				AbortTransaction();
+				ApplyUndoActions();
+				CleanupTransaction();
+				s->blockState = TBLOCK_DEFAULT;
+				break;
+			case TBLOCK_UNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
 				AbortTransaction();
 				CleanupTransaction();
 				s->blockState = TBLOCK_DEFAULT;
@@ -4572,6 +4950,20 @@ AbortOutOfAnyTransaction(void)
 			case TBLOCK_SUBCOMMIT:
 			case TBLOCK_SUBABORT_PENDING:
 			case TBLOCK_SUBRESTART:
+				PushUndoRequest();
+				AbortSubTransaction();
+				ApplyUndoActions();
+				CleanupSubTransaction();
+				s = CurrentTransactionState;	/* changed by pop */
+				break;
+			case TBLOCK_SUBUNDO:
+
+				/*
+				 * We reach here when we got error while applying undo
+				 * actions, so we don't want to again start applying it. Undo
+				 * workers can take care of it.
+				 */
+				ResetUndoActionsInfo();
 				AbortSubTransaction();
 				CleanupSubTransaction();
 				s = CurrentTransactionState;	/* changed by pop */
@@ -4666,6 +5058,8 @@ TransactionBlockStatusCode(void)
 		case TBLOCK_SUBABORT_PENDING:
 		case TBLOCK_SUBRESTART:
 		case TBLOCK_SUBABORT_RESTART:
+		case TBLOCK_UNDO:
+		case TBLOCK_SUBUNDO:
 			return 'E';			/* in failed transaction */
 	}
 
@@ -4705,6 +5099,7 @@ static void
 StartSubTransaction(void)
 {
 	TransactionState s = CurrentTransactionState;
+	int			i;
 
 	if (s->state != TRANS_DEFAULT)
 		elog(WARNING, "StartSubTransaction while in %s state",
@@ -4722,6 +5117,15 @@ StartSubTransaction(void)
 	AtSubStart_Notify();
 	AfterTriggerBeginSubXact();
 
+	/* initialize undo record locations for the transaction */
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+		s->undo_req_pushed[i] = false;
+	}
+	s->performUndoActions = false;
+
 	s->state = TRANS_INPROGRESS;
 
 	/*
@@ -4909,7 +5313,8 @@ AbortSubTransaction(void)
 	 */
 	ShowTransactionState("AbortSubTransaction");
 
-	if (s->state != TRANS_INPROGRESS)
+	if (s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_UNDO)
 		elog(WARNING, "AbortSubTransaction while in %s state",
 			 TransStateAsString(s->state));
 
@@ -5336,6 +5741,8 @@ BlockStateAsString(TBlockState blockState)
 			return "ABORT_PENDING";
 		case TBLOCK_PREPARE:
 			return "PREPARE";
+		case TBLOCK_UNDO:
+			return "UNDO";
 		case TBLOCK_SUBBEGIN:
 			return "SUBBEGIN";
 		case TBLOCK_SUBINPROGRESS:
@@ -5354,6 +5761,8 @@ BlockStateAsString(TBlockState blockState)
 			return "SUBRESTART";
 		case TBLOCK_SUBABORT_RESTART:
 			return "SUBABORT_RESTART";
+		case TBLOCK_SUBUNDO:
+			return "SUBUNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5379,6 +5788,8 @@ TransStateAsString(TransState state)
 			return "ABORT";
 		case TRANS_PREPARE:
 			return "PREPARE";
+		case TRANS_UNDO:
+			return "UNDO";
 	}
 	return "UNRECOGNIZED";
 }
@@ -5977,3 +6388,155 @@ xact_redo(XLogReaderState *record)
 	else
 		elog(PANIC, "xact_redo: unknown op code %u", info);
 }
+
+/*
+ * UndoActionsRequired - Set the information required to perform undo actions.
+ *
+ * This function needs to be called before we release the locks during abort
+ * so that we can skip releasing the locks if required.
+ */
+void
+UndoActionsRequired(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		if (s->latest_urec_ptr[i])
+		{
+			s->performUndoActions = true;
+			break;
+		}
+	}
+}
+
+/*
+ * ResetUndoActionsInfo - reset the start and end undo record pointers.
+ */
+void
+ResetUndoActionsInfo(void)
+{
+	TransactionState s = CurrentTransactionState;
+	int			i;
+
+	s->performUndoActions = false;
+	for (i = 0; i < UndoPersistenceLevels; i++)
+	{
+		s->start_urec_ptr[i] = InvalidUndoRecPtr;
+		s->latest_urec_ptr[i] = InvalidUndoRecPtr;
+	}
+}
+
+/*
+ * CanPerformUndoActions - Returns true, if the current transaction can
+ * perform undo actions, false otherwise.
+ */
+bool
+CanPerformUndoActions(void)
+{
+	TransactionState s = CurrentTransactionState;
+
+	return s->performUndoActions;
+}
+
+/*
+ * PerformUndoActions - Perform undo actions for all the undo logs.
+ *
+ * Returns true, if we are able to successfully perform the actions,
+ * false, otherwise.
+ */
+bool
+PerformUndoActions(FullTransactionId fxid, Oid dbid, UndoRecPtr *end_urec_ptr,
+				   UndoRecPtr *start_urec_ptr, bool *undo_req_pushed,
+				   bool isSubTrans)
+{
+	volatile	UndoRequestInfo urinfo;
+	uint32		save_holdoff;
+	int			per_level;
+	bool		success = true;
+
+	for (per_level = 0; per_level < UndoPersistenceLevels; per_level++)
+	{
+		if (end_urec_ptr[per_level] && !undo_req_pushed[per_level])
+		{
+			save_holdoff = InterruptHoldoffCount;
+
+			PG_TRY();
+			{
+				/*
+				 * Prepare required undo request info so that it can be used in
+				 * exception.
+				 */
+				ResetUndoRequestInfo(&urinfo);
+				urinfo.dbid = dbid;
+				urinfo.full_xid = fxid;
+				urinfo.start_urec_ptr = start_urec_ptr[per_level];
+
+				/* for subtransactions, we do partial rollback. */
+				execute_undo_actions(urinfo.full_xid,
+									 end_urec_ptr[per_level],
+									 start_urec_ptr[per_level],
+									 !isSubTrans);
+			}
+			PG_CATCH();
+			{
+				if (per_level == UNDO_TEMP)
+					pg_rethrow_as_fatal();
+
+				/*
+				 * Add the request into an error queue so that it can be
+				 * processed in a timely fashion.
+				 *
+				 * If we fail to add the request in an error queue, then mark
+				 * the entry status as invalid and continue to process the
+				 * remaining undo requests if any.  This request will be later
+				 * added back to the queue by discard worker.
+				 */
+				if (!InsertRequestIntoErrorUndoQueue(&urinfo))
+					RollbackHTMarkEntryInvalid(urinfo.full_xid,
+											   urinfo.start_urec_ptr);
+				/*
+				 * Errors can reset holdoff count, so restore back.  This is
+				 * required because this function can be called after holding
+				 * interrupts.
+				 */
+				InterruptHoldoffCount = save_holdoff;
+
+				/* Send the error only to server log. */
+				err_out_to_client(false);
+				EmitErrorReport();
+
+				success = false;
+
+				/*
+				 * We promote the error level to FATAL if we get an error
+				 * while applying undo for the subtransaction.  See errstart.
+				 * So, we should never reach here for such a case.
+				 */
+				Assert(!applying_subxact_undo);
+			}
+			PG_END_TRY();
+		}
+	}
+
+	return success;
+}
+
+/*
+ * SetCurrentUndoLocation
+ *
+ * Sets the 'from' and 'to' location for the current transaction.
+ */
+void
+SetCurrentUndoLocation(UndoRecPtr urec_ptr, UndoPersistence upersistence)
+{
+	/*
+	 * Set the start undo record pointer for first undo record in a
+	 * subtransaction.
+	 */
+	if (!UndoRecPtrIsValid(CurrentTransactionState->start_urec_ptr[upersistence]))
+		CurrentTransactionState->start_urec_ptr[upersistence] = urec_ptr;
+	CurrentTransactionState->latest_urec_ptr[upersistence] = urec_ptr;
+
+}
diff --git a/src/backend/access/undo/README.UndoProcessing b/src/backend/access/undo/README.UndoProcessing
new file mode 100644
index 0000000..e0caf9e
--- /dev/null
+++ b/src/backend/access/undo/README.UndoProcessing
@@ -0,0 +1,39 @@
+src/backend/access/undo/README.UndoProcessing
+
+Transaction Rollbacks and Undo Processing
+------------------------------------------
+We always perform rollback actions after cleaning up the current
+(sub)transaction.  This will ensure that we perform the actions immediately
+after error rather than when user issues Rollback command at some later point
+of time.  We are releasing the locks after the undo actions are applied.  The
+reason to delay lock release is that if we release locks before applying undo
+actions, then the parallel session can acquire the lock before us which can
+lead to deadlock.  To execute undo actions during abort, we bring the
+transaction to a clean state by releasing the required resources and put it in
+a new state TRANS_UNDO which indicates that undo apply is in progress.  This
+state is considered as a valid state which means that it is safe to initiate a
+database access, acquire heavyweight locks, etc. in this state.  We have also
+introduced new block states TBLOCK_UNDO and TBLOCK_SUBUNDO, so that if we get
+an error while applying undo, we don't restart applying it again and rather
+just perform Abort/Cleanup of transaction.
+
+We promote the error to FATAL error if it occurred while applying undo for a
+subtransaction.  The reason we can't proceed without applying subtransaction's
+undo is that the modifications made in that case must not be visible even if
+the main transaction commits.  Normally, the backends that receive the request
+to perform Rollback (To Savepoint) applies the undo actions, but there are
+cases where it is preferable to push the requests to background workers.  The
+main reasons to push the requests to background workers are (a) The rollback
+request is very large, pushing such a request to background workers will allow
+us to return control to users quickly.  There is a guc rollback_overflow_size
+which indicates that rollbacks greater than the configured size are performed
+lazily by background workers. (b) We got an error while applying the undo
+actions.
+
+We do have some restrictions on which requests can be pushed to the background
+workers.  In single user mode, all the requests are performed in foreground.
+We can't push the undo actions for temp table to background workers as the temp
+tables are only accessible in the backend that has created them.  We can't
+postpone applying undo actions for subtransactions as the modifications
+made by aborted subtransaction must not be visible even if the main transaction
+commits.
diff --git a/src/backend/access/undo/undoaccess.c b/src/backend/access/undo/undoaccess.c
index 62cdfbf..d70ab65 100644
--- a/src/backend/access/undo/undoaccess.c
+++ b/src/backend/access/undo/undoaccess.c
@@ -835,6 +835,13 @@ InsertPreparedUndo(UndoRecordInsertContext *context)
 			Assert(bufidx < MAX_BUFFER_PER_UNDO);
 		} while (true);
 
+		/*
+		 * Set the current undo location for a transaction.  This is required
+		 * to perform rollback during abort of transaction.
+		 */
+		SetCurrentUndoLocation(prepared_undo->urp,
+							   context->alloc_context.persistence);
+
 		/* Advance the insert pointer past this record. */
 		UndoLogAdvanceFinal(prepared_undo->urp, prepared_undo->size);
 	}
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 8b4720e..c665269 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -259,12 +259,18 @@ errstart(int elevel, const char *filename, int lineno,
 		 * 3. the error occurred after proc_exit has begun to run.  (It's
 		 * proc_exit's responsibility to see that this doesn't turn into
 		 * infinite recursion!)
+		 *
+		 * 4. the error occurred while applying undo for a subtransaction. (We
+		 * can't proceed without applying subtransaction's undo as the
+		 * modifications made in that case must not be visible even if the
+		 * main transaction commits.)
 		 */
 		if (elevel == ERROR)
 		{
 			if (PG_exception_stack == NULL ||
 				ExitOnAnyError ||
-				proc_exit_inprogress)
+				proc_exit_inprogress ||
+				applying_subxact_undo)
 				elevel = FATAL;
 		}
 
@@ -1165,6 +1171,22 @@ internalerrquery(const char *query)
 }
 
 /*
+ * err_out_to_client --- sets whether to send error output to client or not.
+ */
+int
+err_out_to_client(bool out_to_client)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	/* we don't bother incrementing recursion_depth */
+	CHECK_STACK_DEPTH();
+
+	edata->output_to_client = out_to_client;
+
+	return 0;					/* return value does not matter */
+}
+
+/*
  * err_generic_string -- used to set individual ErrorData string fields
  * identified by PG_DIAG_xxx codes.
  *
@@ -1762,6 +1784,18 @@ pg_re_throw(void)
 						 __FILE__, __LINE__);
 }
 
+/*
+ * pg_rethrow_as_fatal - Promote the error level to fatal.
+ */
+void
+pg_rethrow_as_fatal(void)
+{
+	ErrorData  *edata = &errordata[errordata_stack_depth];
+
+	Assert(errordata_stack_depth >= 0);
+	edata->elevel = FATAL;
+	PG_RE_THROW();
+}
 
 /*
  * GetErrorContextStack - Return the context stack, for display/diags
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 3bf96de..4e751d0 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -123,6 +123,12 @@ int			maintenance_work_mem = 16384;
 int			max_parallel_maintenance_workers = 2;
 
 /*
+ * We need this variable primarily to promote the error level to FATAL if we
+ * get any error while performing undo actions for a subtransaction.
+ */
+bool		applying_subxact_undo = false;
+
+/*
  * Primary determinants of sizes of shared-memory structures.
  *
  * MaxBackends is computed by PostmasterMain after modules have had a chance to
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index 7be11c4..3b2a28b 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -20,6 +20,7 @@
  */
 #include "postgres.h"
 
+#include "access/xact.h"
 #include "jit/jit.h"
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
@@ -556,6 +557,11 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 	}
 	else if (phase == RESOURCE_RELEASE_LOCKS)
 	{
+		/*
+		 * For aborts, we don't want to release the locks immediately if we have
+		 * some pending undo actions to perform.  Instead, we release them after
+		 * applying undo actions.  See ApplyUndoActions.
+		 */
 		if (isTopLevel)
 		{
 			/*
@@ -565,7 +571,8 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 			 */
 			if (owner == TopTransactionResourceOwner)
 			{
-				ProcReleaseLocks(isCommit);
+				if (!CanPerformUndoActions())
+					ProcReleaseLocks(isCommit);
 				ReleasePredicateLocks(isCommit, false);
 			}
 		}
@@ -598,7 +605,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
 
 			if (isCommit)
 				LockReassignCurrentOwner(locks, nlocks);
-			else
+			else if (!CanPerformUndoActions())
 				LockReleaseCurrentOwner(locks, nlocks);
 		}
 	}
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 0ac7f73..1321d84 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -231,6 +231,7 @@ extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid);
 extern bool ForceTransactionIdLimitUpdate(void);
 extern Oid	GetNewObjectId(void);
+extern uint32 GetEpochForXid(TransactionId xid);
 
 /*
  * Some frontend programs include this header.  For compilers that emit static
diff --git a/src/include/access/twophase.h b/src/include/access/twophase.h
index b9a531c..497b92f 100644
--- a/src/include/access/twophase.h
+++ b/src/include/access/twophase.h
@@ -14,6 +14,7 @@
 #ifndef TWOPHASE_H
 #define TWOPHASE_H
 
+#include "access/undolog.h"
 #include "access/xlogdefs.h"
 #include "access/xact.h"
 #include "datatype/timestamp.h"
@@ -41,7 +42,7 @@ extern GlobalTransaction MarkAsPreparing(TransactionId xid, const char *gid,
 										 TimestampTz prepared_at,
 										 Oid owner, Oid databaseid);
 
-extern void StartPrepare(GlobalTransaction gxact);
+extern void StartPrepare(GlobalTransaction gxact, UndoRecPtr *, UndoRecPtr *);
 extern void EndPrepare(GlobalTransaction gxact);
 extern bool StandbyTransactionIdIsPrepared(TransactionId xid);
 
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index a20726a..4e0099f 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -14,6 +14,7 @@
 #ifndef XACT_H
 #define XACT_H
 
+#include "access/undolog.h"
 #include "access/transam.h"
 #include "access/xlogreader.h"
 #include "lib/stringinfo.h"
@@ -427,6 +428,15 @@ extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
 									 int xactflags, TransactionId twophase_xid,
 									 const char *twophase_gid);
 extern void xact_redo(XLogReaderState *record);
+extern void ApplyUndoActions(void);
+extern void UndoActionsRequired(void);
+extern void ResetUndoActionsInfo(void);
+extern bool CanPerformUndoActions(void);
+extern bool PerformUndoActions(FullTransactionId fxid, Oid dbid,
+				UndoRecPtr *end_urec_ptr, UndoRecPtr *start_urec_ptr,
+				bool *undo_req_pushed, bool isSubTrans);
+extern void SetCurrentUndoLocation(UndoRecPtr urec_ptr,
+				UndoPersistence upersistence);
 
 /* xactdesc.c */
 extern void xact_desc(StringInfo buf, XLogReaderState *record);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 1afc4d3..388dc97 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -246,6 +246,8 @@ extern PGDLLIMPORT int work_mem;
 extern PGDLLIMPORT int maintenance_work_mem;
 extern PGDLLIMPORT int max_parallel_maintenance_workers;
 
+extern bool applying_subxact_undo;
+
 extern int	VacuumCostPageHit;
 extern int	VacuumCostPageMiss;
 extern int	VacuumCostPageDirty;
diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h
index dbfd8ef..0b227ab 100644
--- a/src/include/utils/elog.h
+++ b/src/include/utils/elog.h
@@ -195,6 +195,8 @@ extern int	errposition(int cursorpos);
 extern int	internalerrposition(int cursorpos);
 extern int	internalerrquery(const char *query);
 
+extern int	err_out_to_client(bool out_to_client);
+
 extern int	err_generic_string(int field, const char *str);
 
 extern int	geterrcode(void);
@@ -384,6 +386,7 @@ extern void FlushErrorState(void);
 extern void ReThrowError(ErrorData *edata) pg_attribute_noreturn();
 extern void ThrowErrorData(ErrorData *edata);
 extern void pg_re_throw(void) pg_attribute_noreturn();
+extern void pg_rethrow_as_fatal(void);
 
 extern char *GetErrorContextStack(void);
 
-- 
1.8.3.1

