/* subtransaction states */
TBLOCK_SUBBEGIN, /* starting a subtransaction */
TBLOCK_SUBINPROGRESS, /* live subtransaction */
- TBLOCK_SUBEND, /* RELEASE received */
+ TBLOCK_SUBRELEASE, /* RELEASE received */
+ TBLOCK_SUBCOMMIT, /* COMMIT received while TBLOCK_SUBINPROGRESS */
TBLOCK_SUBABORT, /* failed subxact, awaiting ROLLBACK */
TBLOCK_SUBABORT_END, /* failed subxact, ROLLBACK received */
TBLOCK_SUBABORT_PENDING, /* live subxact, ROLLBACK received */
static void StartTransaction(void);
static void StartSubTransaction(void);
-static void CommitSubTransaction(void);
+static void CommitSubTransaction(bool isTopLevel);
static void AbortSubTransaction(void);
static void CleanupSubTransaction(void);
static void PushTransaction(void);
case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_END:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
break;
/*
- * We were issued a COMMIT or RELEASE command, so we end the
+ * We were issued a RELEASE command, so we end the
* current subtransaction and return to the parent transaction.
- * The parent might be ended too, so repeat till we are all the
- * way out or find an INPROGRESS transaction.
+ * The parent might be ended too, so repeat till we find an
+ * INPROGRESS transaction or subtransaction.
*/
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
do
{
- CommitSubTransaction();
+ CommitSubTransaction(false);
s = CurrentTransactionState; /* changed by pop */
- } while (s->blockState == TBLOCK_SUBEND);
+ } while (s->blockState == TBLOCK_SUBRELEASE);
+
+ Assert(s->blockState == TBLOCK_INPROGRESS ||
+ s->blockState == TBLOCK_SUBINPROGRESS);
+ break;
+
+ /*
+ * We were issued a COMMIT, so we end the current subtransaction
+ * hierarchy and perform final commit.
+ */
+ case TBLOCK_SUBCOMMIT:
+ do
+ {
+ CommitSubTransaction(true);
+ s = CurrentTransactionState; /* changed by pop */
+ } while (s->blockState == TBLOCK_SUBCOMMIT);
/* If we had a COMMIT command, finish off the main xact too */
if (s->blockState == TBLOCK_END)
{
s->blockState = TBLOCK_DEFAULT;
}
else
- {
- Assert(s->blockState == TBLOCK_INPROGRESS ||
- s->blockState == TBLOCK_SUBINPROGRESS);
- }
+ elog(ERROR, "CommitTransactionCommand: unexpected state %s",
+ BlockStateAsString(s->blockState));
break;
/*
* applies if we get a failure while ending a subtransaction.
*/
case TBLOCK_SUBBEGIN:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBRESTART:
AbortSubTransaction();
case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_END:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
while (s->parent != NULL)
{
if (s->blockState == TBLOCK_SUBINPROGRESS)
- s->blockState = TBLOCK_SUBEND;
+ s->blockState = TBLOCK_SUBCOMMIT;
else
elog(FATAL, "EndTransactionBlock: unexpected state %s",
BlockStateAsString(s->blockState));
case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_END:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_END:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_END:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
case TBLOCK_ABORT_END:
case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_END:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
case TBLOCK_ABORT_END:
for (;;)
{
Assert(xact->blockState == TBLOCK_SUBINPROGRESS);
- xact->blockState = TBLOCK_SUBEND;
+ xact->blockState = TBLOCK_SUBRELEASE;
if (xact == target)
break;
xact = xact->parent;
case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
case TBLOCK_END:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END:
case TBLOCK_ABORT_PENDING:
case TBLOCK_DEFAULT:
case TBLOCK_BEGIN:
case TBLOCK_SUBBEGIN:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT:
case TBLOCK_SUBABORT:
case TBLOCK_ABORT_END:
BlockStateAsString(s->blockState));
Assert(s->state == TRANS_INPROGRESS);
MemoryContextSwitchTo(CurTransactionContext);
- CommitSubTransaction();
+ CommitSubTransaction(false);
s = CurrentTransactionState; /* changed by pop */
Assert(s->state == TRANS_INPROGRESS);
}
case TBLOCK_SUBBEGIN:
case TBLOCK_INPROGRESS:
case TBLOCK_END:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_ABORT:
case TBLOCK_ABORT_END:
case TBLOCK_SUBABORT_END:
*/
case TBLOCK_SUBBEGIN:
case TBLOCK_SUBINPROGRESS:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_SUBABORT_PENDING:
case TBLOCK_SUBRESTART:
AbortSubTransaction();
case TBLOCK_INPROGRESS:
case TBLOCK_SUBINPROGRESS:
case TBLOCK_END:
- case TBLOCK_SUBEND:
+ case TBLOCK_SUBRELEASE:
+ case TBLOCK_SUBCOMMIT:
case TBLOCK_PREPARE:
return 'T'; /* in transaction */
case TBLOCK_ABORT:
*
* The caller has to make sure to always reassign CurrentTransactionState
* if it has a local pointer to it after calling this function.
+ *
+ * isTopLevel means that this CommitSubTransaction() is being issued as a
+ * sequence of actions leading directly to a main transaction commit
+ * allowing some actions to be optimised.
*/
static void
-CommitSubTransaction(void)
+CommitSubTransaction(bool isTopLevel)
{
TransactionState s = CurrentTransactionState;
/*
* The only lock we actually release here is the subtransaction XID lock.
- * The rest just get transferred to the parent resource owner.
*/
CurrentResourceOwner = s->curTransactionOwner;
if (TransactionIdIsValid(s->transactionId))
XactLockTableDelete(s->transactionId);
+ /*
+ * Other locks should get transferred to their parent resource owner.
+ * Doing that is an O(N^2) operation, so if isTopLevel then we can just
+ * leave the lock records as they are, knowing they will all get released
+ * by the top level commit using ProcReleaseLocks(). We only optimize
+ * this for commit; aborts may need to do other cleanup.
+ */
ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_LOCKS,
- true, false);
+ true, isTopLevel);
ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_AFTER_LOCKS,
true, false);
return "SUB BEGIN";
case TBLOCK_SUBINPROGRESS:
return "SUB INPROGRS";
- case TBLOCK_SUBEND:
- return "SUB END";
+ case TBLOCK_SUBRELEASE:
+ return "SUB RELEASE";
+ case TBLOCK_SUBCOMMIT:
+ return "SUB COMMIT";
case TBLOCK_SUBABORT:
return "SUB ABORT";
case TBLOCK_SUBABORT_END: