diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 8c1621d949..92913fbb94 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1823,6 +1823,12 @@ StartTransaction(void)
 	s->state = TRANS_START;
 	s->transactionId = InvalidTransactionId;	/* until assigned */
 
+	{
+		struct stat statbuf;
+		if (stat("/tmp/error_start", &statbuf) == 0)
+			elog(ERROR, "error injected in StartTransaction");
+	}
+
 	/*
 	 * Make sure we've reset xact state variables
 	 *
@@ -1919,9 +1925,6 @@ StartTransaction(void)
 	s->childXids = NULL;
 	s->nChildXids = 0;
 	s->maxChildXids = 0;
-	GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
-	/* SecurityRestrictionContext should never be set outside a transaction */
-	Assert(s->prevSecContext == 0);
 
 	/*
 	 * initialize other subsystems for new transaction
@@ -1930,6 +1933,15 @@ StartTransaction(void)
 	AtStart_Cache();
 	AfterTriggerBeginXact();
 
+	/*
+	 * Security context initialization needs to happen just before switching
+	 * the transaction state as resetting it to its previous state depends
+	 * on the transaction status being TRANS_START.
+	 */
+	GetUserIdAndSecContext(&s->prevUser, &s->prevSecContext);
+	/* SecurityRestrictionContext should never be set outside a transaction */
+	Assert(s->prevSecContext == 0);
+
 	/*
 	 * done with start processing, set current transaction state to "in
 	 * progress"
@@ -2520,28 +2532,34 @@ 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_START &&
+		s->state != TRANS_INPROGRESS &&
+		s->state != TRANS_PREPARE)
 		elog(WARNING, "AbortTransaction while in %s state",
 			 TransStateAsString(s->state));
 	Assert(s->parent == NULL);
 
-	/*
-	 * set the current transaction state information appropriately during the
-	 * abort processing
-	 */
-	s->state = TRANS_ABORT;
-
 	/*
 	 * Reset user ID which might have been changed transiently.  We need this
 	 * to clean up in case control escaped out of a SECURITY DEFINER function
 	 * or other local change of CurrentUserId; therefore, the prior value of
 	 * SecurityRestrictionContext also needs to be restored.
 	 *
+	 * If the transaction is aborted when it has been partially started, then
+	 * the user ID does not need to be set to its previous value.
+	 *
 	 * (Note: it is not necessary to restore session authorization or role
 	 * settings here because those can only be changed via GUC, and GUC will
 	 * take care of rolling them back if need be.)
 	 */
-	SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
+	if (s->state != TRANS_START)
+		SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
+
+	/*
+	 * set the current transaction state information appropriately during the
+	 * abort processing
+	 */
+	s->state = TRANS_ABORT;
 
 	/* If in parallel mode, clean up workers and exit parallel mode. */
 	if (IsInParallelMode())
@@ -3013,15 +3031,6 @@ AbortCurrentTransaction(void)
 			}
 			else
 			{
-				/*
-				 * We can get here after an error during transaction start
-				 * (state will be TRANS_START).  Need to clean up the
-				 * incompletely started transaction.  First, adjust the
-				 * low-level state to suppress warning message from
-				 * AbortTransaction.
-				 */
-				if (s->state == TRANS_START)
-					s->state = TRANS_INPROGRESS;
 				AbortTransaction();
 				CleanupTransaction();
 			}
@@ -4355,15 +4364,6 @@ AbortOutOfAnyTransaction(void)
 				}
 				else
 				{
-					/*
-					 * We can get here after an error during transaction start
-					 * (state will be TRANS_START).  Need to clean up the
-					 * incompletely started transaction.  First, adjust the
-					 * low-level state to suppress warning message from
-					 * AbortTransaction.
-					 */
-					if (s->state == TRANS_START)
-						s->state = TRANS_INPROGRESS;
 					AbortTransaction();
 					CleanupTransaction();
 				}
@@ -4544,6 +4544,12 @@ StartSubTransaction(void)
 
 	s->state = TRANS_START;
 
+	{
+		struct stat statbuf;
+		if (stat("/tmp/error_sub", &statbuf) == 0)
+			elog(ERROR, "error injected in StartSubTransaction");
+	}
+
 	/*
 	 * Initialize subsystems for new subtransaction
 	 *
@@ -4741,7 +4747,8 @@ AbortSubTransaction(void)
 	 */
 	ShowTransactionState("AbortSubTransaction");
 
-	if (s->state != TRANS_INPROGRESS)
+	if (s->state != TRANS_START &&
+		s->state != TRANS_INPROGRESS)
 		elog(WARNING, "AbortSubTransaction while in %s state",
 			 TransStateAsString(s->state));
 
@@ -4750,6 +4757,9 @@ AbortSubTransaction(void)
 	/*
 	 * Reset user ID which might have been changed transiently.  (See notes in
 	 * AbortTransaction.)
+	 *
+	 * Note that for a subtransaction DefineSavepoint() has already called
+	 * PushTransaction(), which has set GetUserIdAndSecContext().
 	 */
 	SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
 
@@ -4912,6 +4922,12 @@ PushTransaction(void)
 	 * assume that it necessarily has a transaction context, resource owner,
 	 * or XID.
 	 */
+
+	{
+		struct stat statbuf;
+		if (stat("/tmp/error_push", &statbuf) == 0)
+			elog(ERROR, "error injected in PushTransaction");
+	}
 }
 
 /*
