Index: src/backend/access/transam/xact.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/access/transam/xact.c,v retrieving revision 1.165 diff -c -r1.165 xact.c *** src/backend/access/transam/xact.c 5 Apr 2004 03:11:39 -0000 1.165 --- src/backend/access/transam/xact.c 20 Apr 2004 05:25:52 -0000 *************** *** 189,194 **** --- 189,224 ---- static void RecordTransactionAbort(void); static void StartTransaction(void); + #ifdef SUBTRANSACTIONS + static void StartSubTransaction(void); + static void CommitSubTransaction(void); + static void AbortSubTransaction(void); + static void CleanupSubTransaction(void); + static void PushCurrentTransaction(void); + static void PopTransaction(void); + static bool IsSubTransaction(void); + static void ShowTransactionState(char *); + static void ShowTransactionStateRec(TransactionState, int); + + static void AtSubStart_Memory(void); + static void AtSubCommit_Memory(void); + static void AtSubCleanup_Memory(void); + + #else /* SUBTRANSACTIONS */ + #define StartSubTransaction() + #define CommitSubTransaction() + #define AbortSubTransaction() + #define CleanupSubTransaction() + #define PushCurrentTransaction() + #define PopTransaction() + #define IsSubTransaction() (false) + #define ShowTransactionState(foo) + #define ShowTransactionStateRec() + #endif /* SUBTRANSACTIONS */ + + static char *BlockStateAsString(TBlockState); + static char *TransStateAsString(TransState); + /* * global variables holding the current transaction state. */ *************** *** 200,205 **** --- 230,247 ---- TRANS_DEFAULT, /* transaction state */ TBLOCK_DEFAULT /* transaction block state from the client * perspective */ + #ifdef SUBTRANSACTIONS + , + 0, /* nesting level */ + NULL, /* commit memory context */ + NULL, /* deferred trigger queue */ + NIL, /* smgr pending deletes */ + NIL, /* async notifies */ + NULL, /* lightweight locks */ + NIL, /* regular locks */ + NULL, /* parent transaction */ + + #endif }; static TransactionState CurrentTransactionState = &CurrentTransactionStateData; *************** *** 281,287 **** { TransactionState s = CurrentTransactionState; ! if (s->blockState == TBLOCK_ABORT) return true; return false; --- 323,330 ---- { TransactionState s = CurrentTransactionState; ! if (s->blockState == TBLOCK_ABORT || ! s->blockState == TBLOCK_SUBABORT) return true; return false; *************** *** 452,459 **** --- 495,544 ---- ALLOCSET_DEFAULT_MAXSIZE); MemoryContextSwitchTo(TopTransactionContext); + + /* + * This context will be used to hold data that survives + * subtransaction commit but disappears on subtransaction end. + * Most if not all of the transaction-local structures should + * live here. + */ + CommitContext = AllocSetContextCreate(TopTransactionContext, + "CommitContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); } + #ifdef SUBTRANSACTIONS + /* ---------------------------------------------------------------- + * StartSubTransaction stuff + * ---------------------------------------------------------------- + */ + + /* + * AtSubStart_Memory + */ + static void + AtSubStart_Memory(void) + { + TransactionState s = CurrentTransactionState; + + elog(DEBUG2, "AtSubStart_Memory"); + Assert(TopTransactionContext != NULL); + + /* + * This context will be used to hold data that survives + * subtransaction commit but disappears on subtransaction end. + * Most if not all of the transaction-local structures should + * live here. + */ + CommitContext = AllocSetContextCreate(TopTransactionContext, + "CommitContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + } + #endif /* SUBTRANSACTIONS */ /* ---------------------------------------------------------------- * CommitTransaction stuff *************** *** 637,644 **** --- 722,750 ---- Assert(TopTransactionContext != NULL); MemoryContextDelete(TopTransactionContext); TopTransactionContext = NULL; + CommitContext = NULL; } + #ifdef SUBTRANSACTIONS + /* ---------------------------------------------------------------- + * StartSubTransaction stuff + * ---------------------------------------------------------------- + */ + + /* + * AtSubCommit_Memory + */ + static void + AtSubCommit_Memory(void) + { + elog(DEBUG2, "AtSubCommit_Memory"); + Assert(TopTransactionContext != NULL); + + /* Return to a known-good memory context. */ + MemoryContextSwitchTo(TopTransactionContext); + } + #endif /* SUBTRANSACTIONS */ + /* ---------------------------------------------------------------- * AbortTransaction stuff * ---------------------------------------------------------------- *************** *** 806,811 **** --- 912,945 ---- } + #ifdef SUBTRANSACTIONS + /* ---------------------------------------------------------------- + * CleanupSubTransaction stuff + * ---------------------------------------------------------------- + */ + + /* + * AtSubCleanup_Memory + */ + static void + AtSubCleanup_Memory(void) + { + elog(DEBUG2, "AtSubEnd_Memory"); + Assert(TopTransactionContext != NULL); + + /* Return to a known-good memory context. */ + MemoryContextSwitchTo(TopTransactionContext); + + /* + * Delete the subxact local memory contexts. The CommitContext + * can go too. + */ + MemoryContextDelete(CommitContext); + + CommitContext = NULL; + } + #endif /* SUBTRANSACTIONS */ + /* ---------------------------------------------------------------- * interface routines * ---------------------------------------------------------------- *************** *** 1133,1138 **** --- 1267,1274 ---- { TransactionState s = CurrentTransactionState; + ShowTransactionState("StartTransactionCommand"); + switch (s->blockState) { /* *************** *** 1145,1189 **** break; /* - * We should never experience this -- it means the STARTED state - * was not changed in the previous CommitTransactionCommand. - */ - case TBLOCK_STARTED: - elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_STARTED"); - break; - - /* - * We should never experience this -- if we do it means the - * BEGIN state was not changed in the previous - * CommitTransactionCommand(). If we get it, we print a - * warning and change to the in-progress state. - */ - case TBLOCK_BEGIN: - elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_BEGIN"); - s->blockState = TBLOCK_INPROGRESS; - break; - - /* * This is the case when are somewhere in a transaction block * and about to start a new command. For now we do nothing * but someday we may do command-local resource * initialization. */ case TBLOCK_INPROGRESS: ! break; ! ! /* ! * As with BEGIN, we should never experience this if we do it ! * means the END state was not changed in the previous ! * CommitTransactionCommand(). If we get it, we print a ! * warning, commit the transaction, start a new transaction ! * and change to the default state. ! */ ! case TBLOCK_END: ! elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_END"); ! CommitTransaction(); ! StartTransaction(); ! s->blockState = TBLOCK_DEFAULT; break; /* --- 1281,1293 ---- break; /* * This is the case when are somewhere in a transaction block * and about to start a new command. For now we do nothing * but someday we may do command-local resource * initialization. */ case TBLOCK_INPROGRESS: ! case TBLOCK_SUBINPROGRESS: break; /* *************** *** 1193,1209 **** * TRANSACTION" which will set things straight. */ case TBLOCK_ABORT: break; ! /* ! * This means we somehow aborted and the last call to ! * CommitTransactionCommand() didn't clear the state so we ! * remain in the ENDABORT state and maybe next time we get to ! * CommitTransactionCommand() the state will get reset to ! * default. ! */ case TBLOCK_ENDABORT: ! elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_ENDABORT"); break; } --- 1297,1317 ---- * TRANSACTION" which will set things straight. */ case TBLOCK_ABORT: + case TBLOCK_SUBABORT: break; ! /* These cases are invalid. */ ! case TBLOCK_STARTED: ! case TBLOCK_BEGIN: ! case TBLOCK_SUBBEGIN: ! case TBLOCK_SUBBEGINABORT: ! case TBLOCK_END: ! case TBLOCK_SUBEND: ! case TBLOCK_SUBENDABORT_OK: ! case TBLOCK_SUBENDABORT_ERROR: case TBLOCK_ENDABORT: ! elog(FATAL, "StartTransactionCommand: unexpected block state %s", ! BlockStateAsString(s->blockState)); break; } *************** *** 1213,1218 **** --- 1321,1327 ---- */ Assert(TopTransactionContext != NULL); MemoryContextSwitchTo(TopTransactionContext); + ShowTransactionState("End StartTransactionCommand"); } /* *************** *** 1222,1227 **** --- 1331,1337 ---- CommitTransactionCommand(void) { TransactionState s = CurrentTransactionState; + ShowTransactionState("CommitTransactionCommand"); switch (s->blockState) { *************** *** 1231,1237 **** * appropiately. */ case TBLOCK_DEFAULT: ! elog(WARNING, "CommitTransactionCommand: unexpected TBLOCK_DEFAULT"); break; /* --- 1341,1347 ---- * appropiately. */ case TBLOCK_DEFAULT: ! elog(FATAL, "CommitTransactionCommand: unexpected TBLOCK_DEFAULT"); break; /* *************** *** 1290,1296 **** --- 1400,1477 ---- CleanupTransaction(); s->blockState = TBLOCK_DEFAULT; break; + + /* + * We were just issued a BEGIN inside a transaction block. + * Start a subtransaction. + */ + case TBLOCK_SUBBEGIN: + StartSubTransaction(); + s->blockState = TBLOCK_SUBINPROGRESS; + break; + + /* + * We were issued a BEGIN inside an aborted transaction block. + * Start a subtransaction, and put it in aborted state. + */ + case TBLOCK_SUBBEGINABORT: + StartSubTransaction(); + AbortSubTransaction(); + s->blockState = TBLOCK_SUBABORT; + break; + + /* + * Inside a subtransaction, increment the command counter. + */ + case TBLOCK_SUBINPROGRESS: + CommandCounterIncrement(); + break; + + /* + * We where issued a COMMIT command, so we end the current + * subtransaction and return to the parent transaction. + */ + case TBLOCK_SUBEND: + CommitSubTransaction(); + PopTransaction(); + break; + + /* + * If we are in an aborted subtransaction, do nothing. + */ + case TBLOCK_SUBABORT: + break; + + /* + * We are ending a subtransaction that aborted nicely, + * so the parent can be allowed to live. + */ + case TBLOCK_SUBENDABORT_OK: + CleanupSubTransaction(); + PopTransaction(); + break; + + /* + * We are ending a subtransaction that aborted in a unclean + * way (e.g. the user issued COMMIT in an aborted subtrasaction.) + * Abort the subtransaction, and abort the parent too. + */ + case TBLOCK_SUBENDABORT_ERROR: + CleanupSubTransaction(); + PopTransaction(); + if (IsSubTransaction()) + { + AbortSubTransaction(); + s->blockState = TBLOCK_SUBABORT; + } + else + { + AbortTransaction(); + s->blockState = TBLOCK_ABORT; + } + break; } + ShowTransactionState("End CommitTransactionCommand"); } /* *************** *** 1301,1306 **** --- 1482,1489 ---- { TransactionState s = CurrentTransactionState; + ShowTransactionState("AbortCurrentTransaction"); + switch (s->blockState) { /* *************** *** 1361,1366 **** --- 1544,1550 ---- * state. */ case TBLOCK_ABORT: + case TBLOCK_SUBABORT: break; /* *************** *** 1373,1379 **** --- 1557,1605 ---- CleanupTransaction(); s->blockState = TBLOCK_DEFAULT; break; + + /* + * If we are just starting a subtransaction, put it + * in aborted state. + */ + case TBLOCK_SUBBEGIN: + case TBLOCK_SUBBEGINABORT: + PushCurrentTransaction(); + StartSubTransaction(); + AbortSubTransaction(); + s->blockState = TBLOCK_SUBABORT; + break; + + case TBLOCK_SUBINPROGRESS: + AbortSubTransaction(); + s->blockState = TBLOCK_SUBABORT; + break; + + /* + * If we are aborting an ending transaction, + * we have to abort the parent transaction too. + */ + case TBLOCK_SUBEND: + AbortSubTransaction(); + CleanupSubTransaction(); + PopTransaction(); + if (IsSubTransaction()) + AbortSubTransaction(); + else + AbortTransaction(); + break; + + case TBLOCK_SUBENDABORT_OK: + case TBLOCK_SUBENDABORT_ERROR: + CleanupSubTransaction(); + PopTransaction(); + if (IsSubTransaction()) + AbortSubTransaction(); + else + AbortTransaction(); + break; } + ShowTransactionState("End AbortCurrentTransaction"); } /* *************** *** 1391,1396 **** --- 1617,1623 ---- * stmtNode: pointer to parameter block for statement; this is used in * a very klugy way to determine whether we are inside a function. * stmtType: statement type name for error messages. + * */ void PreventTransactionChain(void *stmtNode, const char *stmtType) *************** *** 1418,1424 **** /* If we got past IsTransactionBlock test, should be in default state */ if (CurrentTransactionState->blockState != TBLOCK_DEFAULT && CurrentTransactionState->blockState != TBLOCK_STARTED) ! elog(ERROR, "cannot prevent transaction chain"); /* all okay */ } --- 1645,1651 ---- /* If we got past IsTransactionBlock test, should be in default state */ if (CurrentTransactionState->blockState != TBLOCK_DEFAULT && CurrentTransactionState->blockState != TBLOCK_STARTED) ! elog(FATAL, "cannot prevent transaction chain"); /* all okay */ } *************** *** 1540,1557 **** s->blockState = TBLOCK_BEGIN; break; ! /* Already a transaction block in progress. */ case TBLOCK_INPROGRESS: ereport(WARNING, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), errmsg("there is already a transaction in progress"))); /* ! * This shouldn't happen, because a transaction in aborted state ! * will not be allowed to call BeginTransactionBlock. */ case TBLOCK_ABORT: ! elog(WARNING, "BeginTransactionBlock: unexpected TBLOCK_ABORT"); break; /* These cases are invalid. Reject them altogether. */ --- 1767,1797 ---- s->blockState = TBLOCK_BEGIN; break; ! /* ! * Already a transaction block in progress. ! * Start a subtransaction, or squeak if we don't ! * have subtransactions compiled in. ! */ case TBLOCK_INPROGRESS: + #ifndef SUBTRANSACTIONS ereport(WARNING, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), errmsg("there is already a transaction in progress"))); + break; + #endif + case TBLOCK_SUBINPROGRESS: + PushCurrentTransaction(); + s->blockState = TBLOCK_SUBBEGIN; + break; /* ! * An aborted transaction block should be allowed to start ! * a subtransaction, but it must put it in aborted state. */ case TBLOCK_ABORT: ! case TBLOCK_SUBABORT: ! PushCurrentTransaction(); ! s->blockState = TBLOCK_SUBBEGINABORT; break; /* These cases are invalid. Reject them altogether. */ *************** *** 1559,1565 **** case TBLOCK_BEGIN: case TBLOCK_ENDABORT: case TBLOCK_END: ! elog(FATAL, "BeginTransactionBlock: not in a user-allowed state!"); break; } } --- 1799,1811 ---- case TBLOCK_BEGIN: case TBLOCK_ENDABORT: case TBLOCK_END: ! case TBLOCK_SUBBEGIN: ! case TBLOCK_SUBBEGINABORT: ! case TBLOCK_SUBENDABORT_OK: ! case TBLOCK_SUBENDABORT_ERROR: ! case TBLOCK_SUBEND: ! elog(FATAL, "BeginTransactionBlock: unexpected %s", ! BlockStateAsString(s->blockState)); break; } } *************** *** 1584,1589 **** --- 1830,1844 ---- break; /* + * here we are in a subtransaction block. Signal + * CommitTransactionCommand() to end it and return to the + * parent transaction. + */ + case TBLOCK_SUBINPROGRESS: + s->blockState = TBLOCK_SUBEND; + break; + + /* * here, we are in a transaction block which aborted and since the * AbortTransaction() was already done, we do whatever is needed * and change to the special "END ABORT" state. The upcoming *************** *** 1594,1599 **** --- 1849,1863 ---- s->blockState = TBLOCK_ENDABORT; break; + /* + * here we are in an aborted subtransaction. Signal + * CommitTransactionCommand() to clean up and return to the + * parent transaction. + */ + case TBLOCK_SUBABORT: + s->blockState = TBLOCK_SUBENDABORT_ERROR; + break; + case TBLOCK_STARTED: /* * here, the user issued COMMIT when not inside a transaction. Issue a *************** *** 1613,1619 **** case TBLOCK_BEGIN: case TBLOCK_ENDABORT: case TBLOCK_END: ! elog(FATAL, "EndTransactionBlock and not in a user-allowed state"); break; } } --- 1877,1889 ---- case TBLOCK_BEGIN: case TBLOCK_ENDABORT: case TBLOCK_END: ! case TBLOCK_SUBBEGIN: ! case TBLOCK_SUBBEGINABORT: ! case TBLOCK_SUBEND: ! case TBLOCK_SUBENDABORT_OK: ! case TBLOCK_SUBENDABORT_ERROR: ! elog(FATAL, "EndTransactionBlock: unexpected %s", ! BlockStateAsString(s->blockState)); break; } } *************** *** 1626,1667 **** { TransactionState s = CurrentTransactionState; ! /* ! * if the transaction has already been automatically aborted with an ! * error, and the user subsequently types 'abort', allow it. (the ! * behavior is the same as if they had typed 'end'.) ! */ ! if (s->blockState == TBLOCK_ABORT) ! { ! s->blockState = TBLOCK_ENDABORT; ! return; ! } ! ! if (s->blockState == TBLOCK_INPROGRESS) ! { /* * here we were inside a transaction block and we got an abort * command from the user, so we move to the ENDABORT state and * do abort processing so we will end up in the default state * after the upcoming CommitTransactionCommand(). */ ! s->blockState = TBLOCK_ABORT; ! AbortTransaction(); ! s->blockState = TBLOCK_ENDABORT; ! return; } - /* - * here, the user issued ABORT when not inside a transaction. Issue a - * WARNING and go to abort state. The upcoming call to - * CommitTransactionCommand() will then put us back into the default - * state. - */ - ereport(WARNING, - (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), - errmsg("there is no transaction in progress"))); - AbortTransaction(); - s->blockState = TBLOCK_ENDABORT; } /* --- 1896,1964 ---- { TransactionState s = CurrentTransactionState; ! switch (s->blockState) { /* * here we were inside a transaction block and we got an abort * command from the user, so we move to the ENDABORT state and * do abort processing so we will end up in the default state * after the upcoming CommitTransactionCommand(). */ ! case TBLOCK_ABORT: ! s->blockState = TBLOCK_ENDABORT; ! break; ! ! /* Ditto, for a subtransaction. */ ! case TBLOCK_SUBABORT: ! AbortSubTransaction(); ! s->blockState = TBLOCK_SUBENDABORT_OK; ! break; ! ! /* ! * here we were inside a transaction block and we got an abort ! * command from the user, so we move to the ENDABORT state and ! * do abort processing so we will end up in the default state ! * after the upcoming CommitTransactionCommand(). ! */ ! case TBLOCK_INPROGRESS: ! AbortTransaction(); ! s->blockState = TBLOCK_ENDABORT; ! break; ! ! /* Ditto, for a subtransaction. */ ! case TBLOCK_SUBINPROGRESS: ! AbortSubTransaction(); ! s->blockState = TBLOCK_SUBENDABORT_OK; ! break; ! ! /* ! * here, the user issued ABORT when not inside a transaction. Issue a ! * WARNING and go to abort state. The upcoming call to ! * CommitTransactionCommand() will then put us back into the default ! * state. ! */ ! case TBLOCK_STARTED: ! ereport(WARNING, ! (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), ! errmsg("there is no transaction in progress"))); ! AbortTransaction(); ! s->blockState = TBLOCK_ENDABORT; ! break; ! ! /* these cases are invalid. */ ! case TBLOCK_DEFAULT: ! case TBLOCK_BEGIN: ! case TBLOCK_END: ! case TBLOCK_ENDABORT: ! case TBLOCK_SUBEND: ! case TBLOCK_SUBENDABORT_OK: ! case TBLOCK_SUBENDABORT_ERROR: ! case TBLOCK_SUBBEGIN: ! case TBLOCK_SUBBEGINABORT: ! elog(FATAL, "UserAbortTransactionBlock: unexpected %s", ! BlockStateAsString(s->blockState)); ! break; } } /* *************** *** 1697,1702 **** --- 1994,2023 ---- /* AbortTransaction already done, still need Cleanup */ CleanupTransaction(); break; + case TBLOCK_SUBBEGIN: + case TBLOCK_SUBBEGINABORT: + /* + * Just starting a new transaction -- return to parent. + * FIXME -- Is this correct? + */ + PopTransaction(); + AbortOutOfAnyTransaction(); + break; + case TBLOCK_SUBINPROGRESS: + case TBLOCK_SUBEND: + /* In a subtransaction, so clean it up and abort parent too */ + AbortSubTransaction(); + CleanupSubTransaction(); + PopTransaction(); + AbortOutOfAnyTransaction(); + break; + case TBLOCK_SUBABORT: + case TBLOCK_SUBENDABORT_OK: + case TBLOCK_SUBENDABORT_ERROR: + CleanupSubTransaction(); + PopTransaction(); + AbortOutOfAnyTransaction(); + break; } /* *************** *** 1753,1770 **** case TBLOCK_BEGIN: case TBLOCK_INPROGRESS: case TBLOCK_END: return 'T'; /* in transaction */ case TBLOCK_ABORT: case TBLOCK_ENDABORT: return 'E'; /* in failed transaction */ } /* should never get here */ ! elog(ERROR, "invalid transaction block state: %d", ! (int) s->blockState); return 0; /* keep compiler quiet */ } /* * XLOG support routines --- 2074,2351 ---- case TBLOCK_BEGIN: case TBLOCK_INPROGRESS: case TBLOCK_END: + case TBLOCK_SUBINPROGRESS: + case TBLOCK_SUBBEGIN: + case TBLOCK_SUBEND: return 'T'; /* in transaction */ case TBLOCK_ABORT: case TBLOCK_ENDABORT: + case TBLOCK_SUBABORT: + case TBLOCK_SUBENDABORT_OK: + case TBLOCK_SUBENDABORT_ERROR: + case TBLOCK_SUBBEGINABORT: return 'E'; /* in failed transaction */ } /* should never get here */ ! elog(FATAL, "invalid transaction block state: %s", ! BlockStateAsString(s->blockState)); return 0; /* keep compiler quiet */ } + #ifdef SUBTRANSACTIONS + /* + * IsSubTransaction + */ + bool + IsSubTransaction(void) + { + TransactionState s = CurrentTransactionState; + + switch (s->blockState) { + case TBLOCK_DEFAULT: + case TBLOCK_STARTED: + case TBLOCK_BEGIN: + case TBLOCK_INPROGRESS: + case TBLOCK_END: + case TBLOCK_ABORT: + case TBLOCK_ENDABORT: + return false; + case TBLOCK_SUBBEGIN: + case TBLOCK_SUBBEGINABORT: + case TBLOCK_SUBINPROGRESS: + case TBLOCK_SUBABORT: + case TBLOCK_SUBEND: + case TBLOCK_SUBENDABORT_OK: + case TBLOCK_SUBENDABORT_ERROR: + return true; + } + + /* Should not get here */ + Assert(false); + + /* Keep compiler quiet */ + return false; + } + + /* + * StartSubTransaction + */ + void + StartSubTransaction(void) + { + ShowTransactionState("StartSubTransaction"); + + /* Initialize the memory subsystem */ + AtSubStart_Memory(); + + } + + /* + * CommitSubTransaction + */ + void + CommitSubTransaction(void) + { + ShowTransactionState("CommitSubTransaction"); + + AtSubCommit_Memory(); + } + + /* + * AbortSubTransaction + */ + void + AbortSubTransaction(void) + { + ShowTransactionState("AbortSubTransaction"); + } + + /* + * CleanupSubTransaction + */ + void + CleanupSubTransaction(void) + { + ShowTransactionState("CleanupSubTransaction"); + + AtSubCleanup_Memory(); + } + + /* + * PushCurrentTransaction + */ + void + PushCurrentTransaction(void) + { + TransactionState parent; + TransactionState s = CurrentTransactionState; + MemoryContext old_cxt; + + /* + * Make sure the transaction state node is in a long-lived context + */ + old_cxt = MemoryContextSwitchTo(TopMemoryContext); + parent = (TransactionState) palloc(sizeof(TransactionStateData)); + MemoryContextSwitchTo(old_cxt); + + memcpy(parent, s, sizeof(TransactionStateData)); + + parent->commitContext = CommitContext; + s->parent = parent; + s->nestingLevel = s->parent->nestingLevel + 1; + s->commandId = s->parent->commandId; + CommandCounterIncrement(); + } + + /* + * PopTransaction + */ + void + PopTransaction(void) + { + TransactionState s = CurrentTransactionState; + TransactionState p; + + Assert(s->parent != NULL); + + s->parent->commandId = s->commandId; + + p = s->parent; + + memcpy(s, p, sizeof(TransactionStateData)); + CommitContext = s->commitContext; + CommandCounterIncrement(); + + /* Free the old parent structure */ + pfree(p); + } + + /* + * ShowTransactionState + */ + void + ShowTransactionState(char *str) + { + elog(DEBUG2, "%s", str); + ShowTransactionStateRec(CurrentTransactionState, 0); + } + + /* + * ShowTransactionStateRec + */ + void + ShowTransactionStateRec(TransactionState s, int indent) + { + char *i; + char *blockState = BlockStateAsString(s->blockState); + char *state = TransStateAsString(s->state); + + i = (char *)malloc(sizeof(char) * indent + 1); + memset(i, ' ', indent); + memset(i + indent, '\0', 1); + + elog(DEBUG1, "%sblockState: %s; state: %s, tid/cid: %u/%u, nestlvl: %d", + i, + blockState, + state, + (unsigned int)s->transactionIdData, + (unsigned int)s->commandId , + s->nestingLevel); + free(i); + pfree(blockState); + pfree(state); + if (s->parent) + ShowTransactionStateRec(s->parent, indent + 1); + } + + #endif /* SUBTRANSACTIONS */ + + /* + * BlockStateAsString + * + * Returns the block state as a palloc'ed string + */ + char * + BlockStateAsString(TBlockState blockState) + { + char *tmp = ""; + switch (blockState) { + case TBLOCK_DEFAULT: + tmp = "DEFAULT"; + break; + case TBLOCK_STARTED: + tmp = "STARTED"; + break; + case TBLOCK_BEGIN: + tmp = "BEGIN"; + break; + case TBLOCK_INPROGRESS: + tmp = "INPROGRESS"; + break; + case TBLOCK_END: + tmp = "END"; + break; + case TBLOCK_ABORT: + tmp = "ABORT"; + break; + case TBLOCK_ENDABORT: + tmp = "ENDABORT"; + break; + case TBLOCK_SUBBEGIN: + tmp = "SUB BEGIN"; + break; + case TBLOCK_SUBBEGINABORT: + tmp = "SUB BEGIN ABORT"; + break; + case TBLOCK_SUBINPROGRESS: + tmp = "SUB INPROGRESS"; + break; + case TBLOCK_SUBEND: + tmp = "SUB END"; + break; + case TBLOCK_SUBABORT: + tmp = "SUB ABORT"; + break; + case TBLOCK_SUBENDABORT_OK: + tmp = "SUB ENDABORT OK"; + break; + case TBLOCK_SUBENDABORT_ERROR: + tmp = "SUB ENDABORT ERROR"; + break; + } + return pstrdup(tmp); + } + + /* + * TransStateAsString + * + * Returns the transaction internal state as a palloc'ed string + */ + char * + TransStateAsString(TransState state) + { + char *tmp = ""; + + switch (state) { + case TRANS_DEFAULT: + tmp = "DEFAULT"; + break; + case TRANS_START: + tmp = "START"; + break; + case TRANS_COMMIT: + tmp = "COMMIT"; + break; + case TRANS_ABORT: + tmp = "ABORT"; + break; + case TRANS_INPROGRESS: + tmp = "INPROGRESS"; + break; + } + return pstrdup(tmp); + } /* * XLOG support routines Index: src/backend/tcop/postgres.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/tcop/postgres.c,v retrieving revision 1.400 diff -c -r1.400 postgres.c *** src/backend/tcop/postgres.c 19 Apr 2004 17:42:58 -0000 1.400 --- src/backend/tcop/postgres.c 20 Apr 2004 00:11:07 -0000 *************** *** 846,851 **** --- 846,854 ---- TransactionStmt *stmt = (TransactionStmt *) parsetree; if (stmt->kind == TRANS_STMT_COMMIT || + #ifdef SUBTRANSACTIONS + stmt->kind == TRANS_STMT_BEGIN || + #endif stmt->kind == TRANS_STMT_ROLLBACK) allowit = true; } Index: src/backend/utils/mmgr/mcxt.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/mmgr/mcxt.c,v retrieving revision 1.44 diff -c -r1.44 mcxt.c *** src/backend/utils/mmgr/mcxt.c 29 Nov 2003 19:52:04 -0000 1.44 --- src/backend/utils/mmgr/mcxt.c 20 Apr 2004 05:22:10 -0000 *************** *** 45,50 **** --- 45,51 ---- MemoryContext CacheMemoryContext = NULL; MemoryContext MessageContext = NULL; MemoryContext TopTransactionContext = NULL; + MemoryContext CommitContext = NULL; /* These two are transient links to contexts owned by other objects: */ MemoryContext QueryContext = NULL; Index: src/include/pg_config_manual.h =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/include/pg_config_manual.h,v retrieving revision 1.12 diff -c -r1.12 pg_config_manual.h *** src/include/pg_config_manual.h 24 Mar 2004 22:40:29 -0000 1.12 --- src/include/pg_config_manual.h 19 Apr 2004 21:10:13 -0000 *************** *** 235,237 **** --- 235,240 ---- /* #define ACLDEBUG */ /* #define RTDEBUG */ /* #define GISTDEBUG */ + + #define SUBTRANSACTIONS + Index: src/include/access/xact.h =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/include/access/xact.h,v retrieving revision 1.62 diff -c -r1.62 xact.h *** src/include/access/xact.h 5 Apr 2004 03:11:39 -0000 1.62 --- src/include/access/xact.h 20 Apr 2004 05:22:20 -0000 *************** *** 63,69 **** TBLOCK_INPROGRESS, TBLOCK_END, TBLOCK_ABORT, ! TBLOCK_ENDABORT } TBlockState; /* --- 63,78 ---- TBLOCK_INPROGRESS, TBLOCK_END, TBLOCK_ABORT, ! TBLOCK_ENDABORT, ! ! TBLOCK_SUBBEGIN, ! TBLOCK_SUBBEGINABORT, ! TBLOCK_SUBINPROGRESS, ! TBLOCK_SUBEND, ! TBLOCK_SUBABORT, ! TBLOCK_SUBENDABORT_OK, ! TBLOCK_SUBENDABORT_ERROR ! } TBlockState; /* *************** *** 82,87 **** --- 91,106 ---- int startTimeUsec; TransState state; TBlockState blockState; + #ifdef SUBTRANSACTIONS + int nestingLevel; + MemoryContext commitContext; + void *deferredTriggers; + List *pendingDeletes; + List *notifies; + LWLockId *LWLocks; + List *locks; + struct TransactionStateData *parent; + #endif } TransactionStateData; typedef TransactionStateData *TransactionState; Index: src/include/utils/memutils.h =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/include/utils/memutils.h,v retrieving revision 1.54 diff -c -r1.54 memutils.h *** src/include/utils/memutils.h 29 Nov 2003 22:41:15 -0000 1.54 --- src/include/utils/memutils.h 20 Apr 2004 05:23:58 -0000 *************** *** 70,75 **** --- 70,76 ---- extern DLLIMPORT MemoryContext CacheMemoryContext; extern DLLIMPORT MemoryContext MessageContext; extern DLLIMPORT MemoryContext TopTransactionContext; + extern DLLIMPORT MemoryContext CommitContext; /* These two are transient links to contexts owned by other objects: */ extern DLLIMPORT MemoryContext QueryContext;