diff options
Diffstat (limited to 'src/backend/access/transam/xact.c')
-rw-r--r-- | src/backend/access/transam/xact.c | 238 |
1 files changed, 161 insertions, 77 deletions
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index d6551cc9c9c..bc8f968571d 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.67 2000/06/18 22:43:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.68 2000/06/28 03:31:05 tgl Exp $ * * NOTES * Transaction aborts can now occur two ways: @@ -18,14 +18,14 @@ * * These two cases used to be treated identically, but now * we need to distinguish them. Why? consider the following - * two situatuons: + * two situations: * * case 1 case 2 * ------ ------ * 1) user types BEGIN 1) user types BEGIN * 2) user does something 2) user does something * 3) user does not like what 3) system aborts for some reason - * she shes and types ABORT + * she sees and types ABORT * * In case 1, we want to abort the transaction and return to the * default state. In case 2, there may be more commands coming @@ -42,6 +42,15 @@ * * AbortTransactionBlock() leaves us in TBLOCK_ABORT and * * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT * + * Low-level transaction abort handling is divided into two phases: + * * AbortTransaction() executes as soon as we realize the transaction + * has failed. It should release all shared resources (locks etc) + * so that we do not delay other backends unnecessarily. + * * CleanupTransaction() executes when we finally see a user COMMIT + * or ROLLBACK command; it cleans things up and gets us out of + * the transaction internally. In particular, we mustn't destroy + * TransactionCommandContext until this point. + * * NOTES * This file is an attempt at a redesign of the upper layer * of the V1 transaction system which was too poorly thought @@ -70,7 +79,7 @@ * StartTransaction * CommitTransaction * AbortTransaction - * UserAbortTransaction + * CleanupTransaction * * are provided to do the lower level work like recording * the transaction status in the log and doing memory cleanup. @@ -151,13 +160,15 @@ #include "commands/async.h" #include "commands/sequence.h" #include "commands/trigger.h" +#include "executor/spi.h" #include "libpq/be-fsstubs.h" #include "storage/proc.h" #include "storage/sinval.h" -#include "utils/temprel.h" #include "utils/inval.h" +#include "utils/memutils.h" #include "utils/portal.h" #include "utils/relcache.h" +#include "utils/temprel.h" extern bool SharedBufferChanged; @@ -165,6 +176,7 @@ static void AbortTransaction(void); static void AtAbort_Cache(void); static void AtAbort_Locks(void); static void AtAbort_Memory(void); +static void AtCleanup_Memory(void); static void AtCommit_Cache(void); static void AtCommit_LocalCache(void); static void AtCommit_Locks(void); @@ -172,6 +184,7 @@ static void AtCommit_Memory(void); static void AtStart_Cache(void); static void AtStart_Locks(void); static void AtStart_Memory(void); +static void CleanupTransaction(void); static void CommitTransaction(void); static void RecordTransactionAbort(void); static void RecordTransactionCommit(void); @@ -243,7 +256,7 @@ bool AMI_OVERRIDE = false; /* -------------------------------- * TranactionFlushEnabled() - * SetTranactionFlushEnabled() + * SetTransactionFlushEnabled() * * These are used to test and set the "TransactionFlushState" * varable. If this variable is true (the default), then @@ -580,22 +593,35 @@ AtStart_Locks() static void AtStart_Memory() { - Portal portal; - MemoryContext portalContext; + /* ---------------- + * We shouldn't have any transaction contexts already. + * ---------------- + */ + Assert(TopTransactionContext == NULL); + Assert(TransactionCommandContext == NULL); /* ---------------- - * get the blank portal and its memory context + * Create a toplevel context for the transaction. * ---------------- */ - portal = GetPortalByName(NULL); - portalContext = (MemoryContext) PortalGetHeapMemory(portal); + TopTransactionContext = + AllocSetContextCreate(TopMemoryContext, + "TopTransactionContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); /* ---------------- - * tell system to allocate in the blank portal context + * Create a statement-level context and make it active. * ---------------- */ - MemoryContextSwitchTo(portalContext); - StartPortalAllocMode(DefaultAllocMode, 0); + TransactionCommandContext = + AllocSetContextCreate(TopTransactionContext, + "TransactionCommandContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + MemoryContextSwitchTo(TransactionCommandContext); } @@ -711,22 +737,21 @@ AtCommit_Locks() static void AtCommit_Memory() { - Portal portal; - /* ---------------- - * Release all heap memory in the blank portal. + * Now that we're "out" of a transaction, have the + * system allocate things in the top memory context instead + * of per-transaction contexts. * ---------------- */ - portal = GetPortalByName(NULL); - PortalResetHeapMemory(portal); + MemoryContextSwitchTo(TopMemoryContext); /* ---------------- - * Now that we're "out" of a transaction, have the - * system allocate things in the top memory context instead - * of the blank portal memory context. + * Release all transaction-local memory. * ---------------- */ - MemoryContextSwitchTo(TopMemoryContext); + MemoryContextDelete(TopTransactionContext); + TopTransactionContext = NULL; + TransactionCommandContext = NULL; } /* ---------------------------------------------------------------- @@ -798,24 +823,52 @@ AtAbort_Locks() static void AtAbort_Memory() { - Portal portal; + /* ---------------- + * Make sure we are in a valid context (not a child of + * TransactionCommandContext...) + * ---------------- + */ + MemoryContextSwitchTo(TransactionCommandContext); /* ---------------- - * Release all heap memory in the blank portal. + * We do not want to destroy transaction contexts yet, + * but it should be OK to delete any command-local memory. * ---------------- */ - portal = GetPortalByName(NULL); - PortalResetHeapMemory(portal); + MemoryContextResetAndDeleteChildren(TransactionCommandContext); +} + + +/* ---------------------------------------------------------------- + * CleanupTransaction stuff + * ---------------------------------------------------------------- + */ +/* -------------------------------- + * AtCleanup_Memory + * -------------------------------- + */ +static void +AtCleanup_Memory() +{ /* ---------------- * Now that we're "out" of a transaction, have the * system allocate things in the top memory context instead - * of the blank portal memory context. + * of per-transaction contexts. * ---------------- */ MemoryContextSwitchTo(TopMemoryContext); + + /* ---------------- + * Release all transaction-local memory. + * ---------------- + */ + MemoryContextDelete(TopTransactionContext); + TopTransactionContext = NULL; + TransactionCommandContext = NULL; } + /* ---------------------------------------------------------------- * interface routines * ---------------------------------------------------------------- @@ -854,6 +907,7 @@ StartTransaction() s->state = TRANS_START; SetReindexProcessing(false); + /* ---------------- * generate a new transaction id * ---------------- @@ -874,9 +928,9 @@ StartTransaction() * initialize the various transaction subsystems * ---------------- */ + AtStart_Memory(); AtStart_Cache(); AtStart_Locks(); - AtStart_Memory(); /* ---------------- * Tell the trigger manager to we're starting a transaction @@ -974,20 +1028,21 @@ CommitTransaction() } RelationPurgeLocalRelation(true); + AtEOXact_SPI(); AtEOXact_nbtree(); AtCommit_Cache(); AtCommit_Locks(); AtCommit_Memory(); AtEOXact_Files(); + SharedBufferChanged = false; /* safest place to do it */ + /* ---------------- * done with commit processing, set current transaction * state back to default * ---------------- */ s->state = TRANS_DEFAULT; - SharedBufferChanged = false;/* safest place to do it */ - } /* -------------------------------- @@ -1018,7 +1073,7 @@ AbortTransaction() return; if (s->state != TRANS_INPROGRESS) - elog(NOTICE, "AbortTransaction and not in in-progress state "); + elog(NOTICE, "AbortTransaction and not in in-progress state"); /* ---------------- * Tell the trigger manager that this transaction is about to be @@ -1043,24 +1098,56 @@ AbortTransaction() AtAbort_Notify(); CloseSequences(); AtEOXact_portals(); - if (CommonSpecialPortalIsOpen()) - CommonSpecialPortalClose(); RecordTransactionAbort(); RelationPurgeLocalRelation(false); invalidate_temp_relations(); + AtEOXact_SPI(); AtEOXact_nbtree(); AtAbort_Cache(); AtAbort_Locks(); AtAbort_Memory(); AtEOXact_Files(); + SharedBufferChanged = false; /* safest place to do it */ + + /* ---------------- + * State remains TRANS_ABORT until CleanupTransaction(). + * ---------------- + */ +} + +/* -------------------------------- + * CleanupTransaction + * + * -------------------------------- + */ +static void +CleanupTransaction() +{ + TransactionState s = CurrentTransactionState; + + if (s->state == TRANS_DISABLED) + return; + + /* ---------------- + * State should still be TRANS_ABORT from AbortTransaction(). + * ---------------- + */ + if (s->state != TRANS_ABORT) + elog(FATAL, "CleanupTransaction and not in abort state"); + + /* ---------------- + * do abort cleanup processing + * ---------------- + */ + AtCleanup_Memory(); + /* ---------------- * done with abort processing, set current transaction * state back to default * ---------------- */ s->state = TRANS_DEFAULT; - SharedBufferChanged = false;/* safest place to do it */ } /* -------------------------------- @@ -1133,7 +1220,7 @@ StartTransactionCommand() /* ---------------- * This means we somehow aborted and the last call to * CommitTransactionCommand() didn't clear the state so - * we remain in the ENDABORT state and mabey next time + * we remain in the ENDABORT state and maybe next time * we get to CommitTransactionCommand() the state will * get reset to default. * ---------------- @@ -1142,6 +1229,13 @@ StartTransactionCommand() elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT"); break; } + + /* + * We must switch to TransactionCommandContext before returning. + * This is already done if we called StartTransaction, otherwise not. + */ + Assert(TransactionCommandContext != NULL); + MemoryContextSwitchTo(TransactionCommandContext); } /* -------------------------------- @@ -1181,28 +1275,25 @@ CommitTransactionCommand() * command counter and return. Someday we may free resources * local to the command. * - * That someday is today, at least for memory allocated by - * command in the BlankPortal' HeapMemory context. + * That someday is today, at least for memory allocated in + * TransactionCommandContext. * - vadim 03/25/97 * ---------------- */ case TBLOCK_INPROGRESS: CommandCounterIncrement(); -#ifdef TBL_FREE_CMD_MEMORY - EndPortalAllocMode(); - StartPortalAllocMode(DefaultAllocMode, 0); -#endif + MemoryContextResetAndDeleteChildren(TransactionCommandContext); break; /* ---------------- * This is the case when we just got the "END TRANSACTION" - * statement, so we go back to the default state and - * commit the transaction. + * statement, so we commit the transaction and go back to + * the default state. * ---------------- */ case TBLOCK_END: - s->blockState = TBLOCK_DEFAULT; CommitTransaction(); + s->blockState = TBLOCK_DEFAULT; break; /* ---------------- @@ -1218,10 +1309,11 @@ CommitTransactionCommand() /* ---------------- * Here we were in an aborted transaction block which * just processed the "END TRANSACTION" command from the - * user, so now we return the to default state. + * user, so clean up and return to the default state. * ---------------- */ case TBLOCK_ENDABORT: + CleanupTransaction(); s->blockState = TBLOCK_DEFAULT; break; } @@ -1240,11 +1332,12 @@ AbortCurrentTransaction() { /* ---------------- * if we aren't in a transaction block, we - * just do our usual abort transaction. + * just do the basic abort & cleanup transaction. * ---------------- */ case TBLOCK_DEFAULT: AbortTransaction(); + CleanupTransaction(); break; /* ---------------- @@ -1257,6 +1350,7 @@ AbortCurrentTransaction() case TBLOCK_BEGIN: s->blockState = TBLOCK_ABORT; AbortTransaction(); + /* CleanupTransaction happens when we exit TBLOCK_ABORT */ break; /* ---------------- @@ -1269,6 +1363,7 @@ AbortCurrentTransaction() case TBLOCK_INPROGRESS: s->blockState = TBLOCK_ABORT; AbortTransaction(); + /* CleanupTransaction happens when we exit TBLOCK_ABORT */ break; /* ---------------- @@ -1281,6 +1376,7 @@ AbortCurrentTransaction() case TBLOCK_END: s->blockState = TBLOCK_DEFAULT; AbortTransaction(); + CleanupTransaction(); break; /* ---------------- @@ -1297,10 +1393,11 @@ AbortCurrentTransaction() * Here we were in an aborted transaction block which * just processed the "END TRANSACTION" command but somehow * aborted again.. since we must have done the abort - * processing, we return to the default state. + * processing, we clean up and return to the default state. * ---------------- */ case TBLOCK_ENDABORT: + CleanupTransaction(); s->blockState = TBLOCK_DEFAULT; break; } @@ -1394,13 +1491,14 @@ EndTransactionBlock(void) } /* ---------------- - * We should not get here, but if we do, we go to the ENDABORT - * state after printing a warning. The upcoming call to + * here, the user issued COMMIT when not inside a transaction. + * Issue a notice and go to abort state. The upcoming call to * CommitTransactionCommand() will then put us back into the * default state. * ---------------- */ elog(NOTICE, "COMMIT: no transaction in progress"); + AbortTransaction(); s->blockState = TBLOCK_ENDABORT; } @@ -1427,29 +1525,23 @@ AbortTransactionBlock(void) * here we were inside a transaction block something * screwed up inside the system so we enter the abort state, * do the abort processing and then return. - * We remain in the abort state until we see the upcoming + * We remain in the abort state until we see an * END TRANSACTION command. * ---------------- */ s->blockState = TBLOCK_ABORT; - - /* ---------------- - * do abort processing and return - * ---------------- - */ AbortTransaction(); return; } /* ---------------- - * this case should not be possible, because it would mean - * the user entered an "abort" from outside a transaction block. - * So we print an error message, abort the transaction and - * enter the "ENDABORT" state so we will end up in the default - * state after the upcoming CommitTransactionCommand(). + * here, the user issued ABORT when not inside a transaction. + * Issue a notice and go to abort state. The upcoming call to + * CommitTransactionCommand() will then put us back into the + * default state. * ---------------- */ - elog(NOTICE, "AbortTransactionBlock and not in in-progress state"); + elog(NOTICE, "ROLLBACK: no transaction in progress"); AbortTransaction(); s->blockState = TBLOCK_ENDABORT; } @@ -1495,27 +1587,16 @@ UserAbortTransactionBlock() * ---------------- */ s->blockState = TBLOCK_ABORT; - - /* ---------------- - * do abort processing - * ---------------- - */ AbortTransaction(); - - /* ---------------- - * change to the end abort state and return - * ---------------- - */ s->blockState = TBLOCK_ENDABORT; return; } /* ---------------- - * this case should not be possible, because it would mean - * the user entered a "rollback" from outside a transaction block. - * So we print an error message, abort the transaction and - * enter the "ENDABORT" state so we will end up in the default - * state after the upcoming CommitTransactionCommand(). + * here, the user issued ABORT when not inside a transaction. + * Issue a notice and go to abort state. The upcoming call to + * CommitTransactionCommand() will then put us back into the + * default state. * ---------------- */ elog(NOTICE, "ROLLBACK: no transaction in progress"); @@ -1540,7 +1621,10 @@ AbortOutOfAnyTransaction() * Get out of any low-level transaction */ if (s->state != TRANS_DEFAULT) + { AbortTransaction(); + CleanupTransaction(); + } /* * Now reset the high-level state |