diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index ccd3f4fc550..1bd507230fd 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -341,6 +341,9 @@ static void CommitTransaction(void); static TransactionId RecordTransactionAbort(bool isSubXact); static void StartTransaction(void); +static void CommitTransactionCommandInternal(void); +static void AbortCurrentTransactionInternal(void); + static void StartSubTransaction(void); static void CommitSubTransaction(void); static void AbortSubTransaction(void); @@ -3041,16 +3044,58 @@ RestoreTransactionCharacteristics(const SavedTransactionCharacteristics *s) XactDeferrable = s->save_XactDeferrable; } - /* - * CommitTransactionCommand + * CommitTransactionCommand -- a wrapper function handling the + * loop over subtransactions to avoid a potentially dangerous recursion + * in CommitTransactionCommandInternal(). */ void CommitTransactionCommand(void) +{ + while (true) + { + switch (CurrentTransactionState->blockState) + { + /* + * The current already-failed subtransaction is ending due to + * a ROLLBACK or ROLLBACK TO command, so pop it and + * recursively examine the parent (which could be in any of + * several states). + */ + case TBLOCK_SUBABORT_END: + CleanupSubTransaction(); + continue; + + /* + * As above, but it's not dead yet, so abort first. + */ + case TBLOCK_SUBABORT_PENDING: + AbortSubTransaction(); + CleanupSubTransaction(); + continue; + default: + break; + } + CommitTransactionCommandInternal(); + break; + } +} + +/* + * CommitTransactionCommandInternal - a function doing all the material work + * regarding handling the commit transaction command except for loop over + * subtransactions. + */ +static void +CommitTransactionCommandInternal(void) { TransactionState s = CurrentTransactionState; SavedTransactionCharacteristics savetc; + /* This states are handled in CommitTransactionCommand() */ + Assert(s->blockState != TBLOCK_SUBABORT_END && + s->blockState != TBLOCK_SUBABORT_PENDING); + /* Must save in case we need to restore below */ SaveTransactionCharacteristics(&savetc); @@ -3234,25 +3279,6 @@ CommitTransactionCommand(void) BlockStateAsString(s->blockState)); break; - /* - * The current already-failed subtransaction is ending due to a - * ROLLBACK or ROLLBACK TO command, so pop it and recursively - * examine the parent (which could be in any of several states). - */ - case TBLOCK_SUBABORT_END: - CleanupSubTransaction(); - CommitTransactionCommand(); - break; - - /* - * As above, but it's not dead yet, so abort first. - */ - case TBLOCK_SUBABORT_PENDING: - AbortSubTransaction(); - CleanupSubTransaction(); - CommitTransactionCommand(); - break; - /* * The current subtransaction is the target of a ROLLBACK TO * command. Abort and pop it, then start a new subtransaction @@ -3310,17 +3336,73 @@ CommitTransactionCommand(void) s->blockState = TBLOCK_SUBINPROGRESS; } break; + default: + /* Keep compiler quiet */ + break; } } /* - * AbortCurrentTransaction + * AbortCurrentTransaction -- a wrapper function handling the + * loop over subtransactions to avoid potentially dangerous recursion in + * AbortCurrentTransactionInternal(). */ void AbortCurrentTransaction(void) +{ + while (true) + { + switch (CurrentTransactionState->blockState) + { + /* + * If we failed while trying to create a subtransaction, clean + * up the broken subtransaction and abort the parent. The + * same applies if we get a failure while ending a + * subtransaction. + */ + case TBLOCK_SUBBEGIN: + case TBLOCK_SUBRELEASE: + case TBLOCK_SUBCOMMIT: + case TBLOCK_SUBABORT_PENDING: + case TBLOCK_SUBRESTART: + AbortSubTransaction(); + CleanupSubTransaction(); + continue; + + /* + * Same as above, except the Abort() was already done. + */ + case TBLOCK_SUBABORT_END: + case TBLOCK_SUBABORT_RESTART: + CleanupSubTransaction(); + continue; + default: + break; + } + AbortCurrentTransactionInternal(); + break; + } +} + +/* + * AbortCurrentTransactionInternal - a function doing all the material work + * regarding handling the abort transaction command except for loop over + * subtransactions. + */ +static void +AbortCurrentTransactionInternal(void) { TransactionState s = CurrentTransactionState; + /* This states are handled in AbortCurrentTransaction() */ + Assert(s->blockState != TBLOCK_SUBBEGIN && + s->blockState != TBLOCK_SUBRELEASE && + s->blockState != TBLOCK_SUBCOMMIT && + s->blockState != TBLOCK_SUBABORT_PENDING && + s->blockState != TBLOCK_SUBRESTART && + s->blockState != TBLOCK_SUBABORT_END && + s->blockState != TBLOCK_SUBABORT_RESTART); + switch (s->blockState) { case TBLOCK_DEFAULT: @@ -3441,29 +3523,8 @@ AbortCurrentTransaction(void) AbortSubTransaction(); s->blockState = TBLOCK_SUBABORT; break; - - /* - * If we failed while trying to create a subtransaction, clean up - * the broken subtransaction and abort the parent. The same - * applies if we get a failure while ending a subtransaction. - */ - case TBLOCK_SUBBEGIN: - case TBLOCK_SUBRELEASE: - case TBLOCK_SUBCOMMIT: - case TBLOCK_SUBABORT_PENDING: - case TBLOCK_SUBRESTART: - AbortSubTransaction(); - CleanupSubTransaction(); - AbortCurrentTransaction(); - break; - - /* - * Same as above, except the Abort() was already done. - */ - case TBLOCK_SUBABORT_END: - case TBLOCK_SUBABORT_RESTART: - CleanupSubTransaction(); - AbortCurrentTransaction(); + default: + /* Keep compiler quiet */ break; } }