Index: src/backend/access/transam/Makefile =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/access/transam/Makefile,v retrieving revision 1.18 diff -c -r1.18 Makefile *** src/backend/access/transam/Makefile 29 Nov 2003 19:51:40 -0000 1.18 --- src/backend/access/transam/Makefile 3 May 2004 18:55:00 -0000 *************** *** 12,18 **** top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global ! OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o all: SUBSYS.o --- 12,18 ---- top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global ! OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o subtrans.o all: SUBSYS.o Index: src/backend/access/transam/clog.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/access/transam/clog.c,v retrieving revision 1.19 diff -c -r1.19 clog.c *** src/backend/access/transam/clog.c 29 Nov 2003 19:51:40 -0000 1.19 --- src/backend/access/transam/clog.c 7 May 2004 05:55:09 -0000 *************** *** 95,114 **** char *byteptr; Assert(status == TRANSACTION_STATUS_COMMITTED || ! status == TRANSACTION_STATUS_ABORTED); LWLockAcquire(ClogCtl->locks->ControlLock, LW_EXCLUSIVE); byteptr = SimpleLruReadPage(ClogCtl, pageno, xid, true); byteptr += byteno; ! /* Current state should be 0 or target state */ Assert(((*byteptr >> bshift) & CLOG_XACT_BITMASK) == 0 || ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status); *byteptr |= (status << bshift); ! /* ...->page_status[slotno] = CLOG_PAGE_DIRTY; already done */ LWLockRelease(ClogCtl->locks->ControlLock); } --- 95,116 ---- char *byteptr; Assert(status == TRANSACTION_STATUS_COMMITTED || ! status == TRANSACTION_STATUS_ABORTED || ! status == TRANSACTION_STATUS_SUB_COMMITTED); LWLockAcquire(ClogCtl->locks->ControlLock, LW_EXCLUSIVE); byteptr = SimpleLruReadPage(ClogCtl, pageno, xid, true); byteptr += byteno; ! /* Current state should be 0, subcommitted or target state */ Assert(((*byteptr >> bshift) & CLOG_XACT_BITMASK) == 0 || + ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == TRANSACTION_STATUS_SUB_COMMITTED || ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status); *byteptr |= (status << bshift); ! /* ...->page_status[slotno] = SLRU_PAGE_DIRTY; already done */ LWLockRelease(ClogCtl->locks->ControlLock); } Index: src/backend/access/transam/transam.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/access/transam/transam.c,v retrieving revision 1.56 diff -c -r1.56 transam.c *** src/backend/access/transam/transam.c 29 Nov 2003 19:51:40 -0000 1.56 --- src/backend/access/transam/transam.c 7 May 2004 18:59:10 -0000 *************** *** 20,25 **** --- 20,26 ---- #include "postgres.h" #include "access/clog.h" + #include "access/subtrans.h" #include "access/transam.h" *************** *** 123,128 **** --- 124,156 ---- cachedTestXidStatus = status; } + /* + * TransactionLogMultiUpdate + * + * Update multiple transaction identifiers to a given status. + * Don't depend on this being atomic; it's not. + */ + static void + TransactionLogMultiUpdate(List *transactionIds, + XidStatus status) + { + List *elt; + + foreach(elt, transactionIds) + { + TransactionId xid = lfirsti(elt); + + TransactionIdSetStatus(xid, status); + } + + /* + * Update out TransactionLogTest cache. We use the first one + * because presumably it's a top level transaction. + */ + TransactionIdStore(lfirsti(transactionIds), &cachedTestXid); + cachedTestXidStatus = status; + } + /* -------------------------------- * AmiTransactionOverride * *************** *** 171,183 **** bool /* true if given transaction committed */ TransactionIdDidCommit(TransactionId transactionId) { if (AMI_OVERRIDE) { Assert(transactionId == BootstrapTransactionId); return true; } ! return TransactionLogTest(transactionId, TRANSACTION_STATUS_COMMITTED); } /* --- 199,231 ---- bool /* true if given transaction committed */ TransactionIdDidCommit(TransactionId transactionId) { + if (AMI_OVERRIDE) { Assert(transactionId == BootstrapTransactionId); return true; } ! /* ! * If it's marked committed, it's committed. ! */ ! if (TransactionLogTest(transactionId, TRANSACTION_STATUS_COMMITTED)) ! return true; ! ! /* ! * If it's marked subcommitted, we have to check the parent recursively. ! */ ! if (TransactionLogTest(transactionId, TRANSACTION_STATUS_SUB_COMMITTED)) ! { ! TransactionId parentXid = SubTransGetParent(transactionId); ! ! return TransactionIdDidCommit(parentXid); ! } ! ! /* ! * It's not committed. ! */ ! return false; } /* *************** *** 196,224 **** return false; } ! return TransactionLogTest(transactionId, TRANSACTION_STATUS_ABORTED); ! } ! /* ! * Now this func in shmem.c and gives quality answer by scanning ! * PGPROC structures of all running backend. - vadim 11/26/96 ! * ! * Old comments: ! * true if given transaction has neither committed nor aborted ! */ ! #ifdef NOT_USED ! bool ! TransactionIdIsInProgress(TransactionId transactionId) ! { ! if (AMI_OVERRIDE) { ! Assert(transactionId == BootstrapTransactionId); ! return false; } ! return TransactionLogTest(transactionId, TRANSACTION_STATUS_IN_PROGRESS); } - #endif /* NOT_USED */ /* -------------------------------- * TransactionId Commit --- 244,271 ---- return false; } ! /* ! * If it's marked aborted, it's aborted. ! */ ! if (TransactionLogTest(transactionId, TRANSACTION_STATUS_ABORTED)) ! return true; ! /* ! * If it's marked subcommitted, we have to check the parent recursively. ! */ ! if (TransactionLogTest(transactionId, TRANSACTION_STATUS_SUB_COMMITTED)) { ! TransactionId parentXid = SubTransGetParent(transactionId); ! ! if (TransactionIdDidAbort(parentXid)) ! return true; } ! /* ! * It's not aborted. ! */ ! return false; } /* -------------------------------- * TransactionId Commit *************** *** 252,257 **** --- 299,335 ---- TransactionLogUpdate(transactionId, TRANSACTION_STATUS_ABORTED); } + /* + * TrasactionIdSubCommit + * Marks the subtransaction associated with the identifier as + * committed. The parent transaction will update the status + * again later, to either committed or aborted. + */ + void + TransactionIdSubCommit(TransactionId transactionId) + { + TransactionLogUpdate(transactionId, TRANSACTION_STATUS_SUB_COMMITTED); + } + + /* + * TransactionIdSubAbort + * Marks the subtransaction and all its children as aborted + */ + void + TransactionIdSubAbort(List *transactionIds) + { + TransactionLogMultiUpdate(transactionIds, TRANSACTION_STATUS_ABORTED); + } + + /* + * TransactionIdCommitTree + * Marks all the transaction ids as committed + */ + void + TransactionIdCommitTree(List *transactionIds) + { + TransactionLogMultiUpdate(transactionIds, TRANSACTION_STATUS_COMMITTED); + } /* * TransactionIdPrecedes --- is id1 logically < id2? Index: src/backend/access/transam/varsup.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/access/transam/varsup.c,v retrieving revision 1.55 diff -c -r1.55 varsup.c *** src/backend/access/transam/varsup.c 26 Jan 2004 19:15:59 -0000 1.55 --- src/backend/access/transam/varsup.c 8 May 2004 18:29:29 -0000 *************** *** 15,20 **** --- 15,21 ---- #include "access/clog.h" #include "access/transam.h" + #include "access/subtrans.h" #include "storage/ipc.h" #include "storage/proc.h" *************** *** 30,36 **** * Allocate the next XID for my new transaction. */ TransactionId ! GetNewTransactionId(void) { TransactionId xid; --- 31,37 ---- * Allocate the next XID for my new transaction. */ TransactionId ! GetNewTransactionId(bool isSubXact) { TransactionId xid; *************** *** 52,59 **** --- 53,63 ---- * commit a later XID before we zero the page. Fortunately, a page of * the commit log holds 32K or more transactions, so we don't have to * do this very often. + * + * Extend pg_subtrans too. */ ExtendCLOG(xid); + ExtendSubTrans(xid); /* * Now advance the nextXid counter. This must not happen until after *************** *** 82,89 **** * its own spinlock used only for fetching/storing that PGPROC's xid. * (SInvalLock would then mean primarily that PGPROCs couldn't be added/ * removed while holding the lock.) */ ! if (MyProc != NULL) MyProc->xid = xid; LWLockRelease(XidGenLock); --- 86,96 ---- * its own spinlock used only for fetching/storing that PGPROC's xid. * (SInvalLock would then mean primarily that PGPROCs couldn't be added/ * removed while holding the lock.) + * + * We don't want a subtransaction to update the stored Xid; we'll check + * if a transaction Xid is a running subxact by checking pg_subtrans. */ ! if (MyProc != NULL && !isSubXact) MyProc->xid = xid; LWLockRelease(XidGenLock); 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 6 May 2004 21:42:53 -0000 *************** *** 148,153 **** --- 148,154 ---- #include "access/nbtree.h" #include "access/rtree.h" #include "access/xact.h" + #include "access/subtrans.h" #include "catalog/heap.h" #include "catalog/index.h" #include "catalog/namespace.h" *************** *** 189,194 **** --- 190,216 ---- static void RecordTransactionAbort(void); static void StartTransaction(void); + static void RecordSubTransactionCommit(void); + 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 AtSubAbort_Memory(void); + static void AtSubCleanup_Memory(void); + static void AtSubCommit_Notifies(void); + + static char *TransStateAsString(TransState); + + static char *BlockStateAsString(TBlockState); + /* * global variables holding the current transaction state. */ *************** *** 198,205 **** 0, /* scan command id */ 0x0, /* start time */ TRANS_DEFAULT, /* transaction state */ ! TBLOCK_DEFAULT /* transaction block state from the client * perspective */ }; static TransactionState CurrentTransactionState = &CurrentTransactionStateData; --- 220,236 ---- 0, /* scan command id */ 0x0, /* start time */ TRANS_DEFAULT, /* transaction state */ ! TBLOCK_DEFAULT, /* transaction block state from the client * perspective */ + 0, /* nesting level */ + NULL, /* commit memory context */ + NULL, /* deferred trigger queue */ + NIL, /* smgr pending deletes */ + NIL, /* async notifies */ + NULL, /* prepared statements */ + NULL, /* lightweight locks */ + NIL, /* regular locks */ + NULL /* parent transaction */ }; static TransactionState CurrentTransactionState = &CurrentTransactionStateData; *************** *** 281,287 **** { TransactionState s = CurrentTransactionState; ! if (s->blockState == TBLOCK_ABORT) return true; return false; --- 312,319 ---- { TransactionState s = CurrentTransactionState; ! if (s->blockState == TBLOCK_ABORT || ! s->blockState == TBLOCK_SUBABORT) return true; return false; *************** *** 337,342 **** --- 369,465 ---- return s->startTime; } + /* + * TransactionGetPendingDeletes + */ + List * + TransactionGetPendingDeletes(void) + { + TransactionState s = CurrentTransactionState; + + return s->pendingDeletes; + } + + /* + * TransactionSetPendingDeletes + */ + void + TransactionSetPendingDeletes(List *pending) + { + TransactionState s = CurrentTransactionState; + + s->pendingDeletes = pending; + } + + /* + * TransactionGetParentPendingDeletes + */ + List * + TransactionGetParentPendingDeletes(void) + { + TransactionState s = CurrentTransactionState; + + Assert(s->parent != NULL); + return s->parent->pendingDeletes; + } + + /* + * TransactionSetParentPendingDeletes + */ + void + TransactionSetParentPendingDeletes(List *pending) + { + TransactionState s = CurrentTransactionState; + + Assert(s->parent != NULL); + s->parent->pendingDeletes = pending; + } + + /* + * TransactionGetNotifies + */ + List * + TransactionGetNotifies(void) + { + TransactionState s = CurrentTransactionState; + + return s->notifies; + } + + /* + * TransactionSetNotifies + */ + void + TransactionSetNotifies(List *notifies) + { + TransactionState s = CurrentTransactionState; + + s->notifies = notifies; + } + + /* + * TransactionGetParentNotifies + */ + List * + TransactionGetParentNotifies(void) + { + TransactionState s = CurrentTransactionState; + + Assert(s->parent != NULL); + return s->parent->notifies; + } + + /* + * TransactionSetParentNotifies + */ + void + TransactionSetParentNotifies(List *notifies) + { + TransactionState s = CurrentTransactionState; + + Assert(s->parent != NULL); + s->parent->notifies = notifies; + } /* * TransactionIdIsCurrentTransactionId *************** *** 357,363 **** return false; } ! return TransactionIdEquals(xid, s->transactionIdData); } --- 480,507 ---- return false; } ! /* ! * The "current transaction Xid" is actually the Xid of the current ! * subtransaction, or one of the Xids in the committed childs Xid, or ! * any of those up the transaction tree. ! */ ! ! while (s != NULL) ! { ! List *childs; ! ! if (TransactionIdEquals(xid, s->transactionIdData)) ! return true; ! foreach (childs, s->childXids) ! { ! if (TransactionIdEquals(xid, lfirsti(childs))) ! return true; ! } ! ! s = s->parent; ! } ! ! return false; } *************** *** 436,448 **** static void AtStart_Memory(void) { /* * We shouldn't have a transaction context already. */ Assert(TopTransactionContext == NULL); /* ! * Create a toplevel context for the transaction, and make it active. */ TopTransactionContext = AllocSetContextCreate(TopMemoryContext, --- 580,594 ---- static void AtStart_Memory(void) { + TransactionState s = CurrentTransactionState; + /* * We shouldn't have a transaction context already. */ Assert(TopTransactionContext == NULL); /* ! * Create a toplevel context for the transaction. */ TopTransactionContext = AllocSetContextCreate(TopMemoryContext, *************** *** 451,459 **** ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); ! MemoryContextSwitchTo(TopTransactionContext); } /* ---------------------------------------------------------------- * CommitTransaction stuff --- 597,651 ---- ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); ! /* ! * Create a CommitContext, which 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); ! ! s->commitContext = CommitContext; ! ! /* Make the CommitContext active. */ ! MemoryContextSwitchTo(CommitContext); } + /* ---------------------------------------------------------------- + * 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); + s->commitContext = CommitContext; + + /* Make the CommitContext active. */ + MemoryContextSwitchTo(CommitContext); + } /* ---------------------------------------------------------------- * CommitTransaction stuff *************** *** 637,642 **** --- 829,905 ---- Assert(TopTransactionContext != NULL); MemoryContextDelete(TopTransactionContext); TopTransactionContext = NULL; + CommitContext = NULL; + } + + /* ---------------------------------------------------------------- + * CommitSubTransaction stuff + * ---------------------------------------------------------------- + */ + + /* + * AtSubCommit_Memory + */ + static void + AtSubCommit_Memory(void) + { + TransactionState s = CurrentTransactionState; + + elog(DEBUG2, "AtSubCommit_Memory"); + Assert(TopTransactionContext != NULL); + + /* Return to a known-good memory context. */ + MemoryContextSwitchTo(s->parent->commitContext); + } + + /* + * AtSubCommit_childXids + * + * Save the child XIDs in the transaction state so we can mark them + * aborted in pg_clog if their parent aborts. + */ + static void + AtSubCommit_childXids(void) + { + TransactionState s = CurrentTransactionState; + MemoryContext old_cxt; + + old_cxt = MemoryContextSwitchTo(TopTransactionContext); + + s->parent->childXids = + lconsi(s->transactionIdData, + nconc(s->parent->childXids, s->childXids)); + + MemoryContextSwitchTo(old_cxt); + } + + /* + * AtSubCommit_Notifies + * + * Reassign the pending notifies to the parent transaction. + */ + static void + AtSubCommit_Notifies(void) + { + TransactionState s = CurrentTransactionState; + + s->parent->notifies = nconc(s->notifies, s->parent->notifies); + } + + /* + * RecordSubTransactionCommit + */ + void + RecordSubTransactionCommit(void) + { + TransactionState s = CurrentTransactionState; + + START_CRIT_SECTION(); + + /* Record subtransaction commit */ + TransactionIdSubCommit(s->transactionIdData); + + END_CRIT_SECTION(); } /* ---------------------------------------------------------------- *************** *** 778,783 **** --- 1041,1071 ---- MemoryContextSwitchTo(TopMemoryContext); } + /* + * AtSubAbort_Memory + */ + static void + AtSubAbort_Memory(void) + { + Assert(TopTransactionContext != NULL); + + MemoryContextSwitchTo(TopTransactionContext); + } + + /* + * RecordSubTransactionAbort + */ + static void + RecordSubTransactionAbort(void) + { + TransactionState s = CurrentTransactionState; + List *xids; + + /* Record subxact abort in pg_clog. */ + xids = lconsi(s->transactionIdData, s->childXids); + + TransactionIdSubAbort(xids); + } /* ---------------------------------------------------------------- * CleanupTransaction stuff *************** *** 807,812 **** --- 1095,1126 ---- /* ---------------------------------------------------------------- + * 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; + } + + /* ---------------------------------------------------------------- * interface routines * ---------------------------------------------------------------- */ *************** *** 841,847 **** /* * generate a new transaction id */ ! s->transactionIdData = GetNewTransactionId(); XactLockTableInsert(s->transactionIdData); --- 1155,1161 ---- /* * generate a new transaction id */ ! s->transactionIdData = GetNewTransactionId(false); XactLockTableInsert(s->transactionIdData); *************** *** 851,856 **** --- 1165,1172 ---- s->commandId = FirstCommandId; s->startTime = GetCurrentAbsoluteTimeUsec(&(s->startTimeUsec)); + s->childXids = NIL; + /* * initialize the various transaction subsystems */ *************** *** 868,873 **** --- 1184,1190 ---- * progress" */ s->state = TRANS_INPROGRESS; + ShowTransactionState("StartTransaction"); } *************** *** 879,884 **** --- 1196,1203 ---- { TransactionState s = CurrentTransactionState; + ShowTransactionState("CommitTransaction"); + /* * check the current transaction state */ *************** *** 929,934 **** --- 1248,1256 ---- */ RecordTransactionCommit(); + if (s->childXids != NIL) + TransactionIdCommitTree(s->childXids); + /* * Let others know about no transaction in progress by me. Note that * this must be done _before_ releasing locks we hold and _after_ *************** *** 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; /* --- 1467,1479 ---- 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; } --- 1483,1503 ---- * 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; } *************** *** 1231,1237 **** * appropiately. */ case TBLOCK_DEFAULT: ! elog(WARNING, "CommitTransactionCommand: unexpected TBLOCK_DEFAULT"); break; /* --- 1525,1531 ---- * appropiately. */ case TBLOCK_DEFAULT: ! elog(FATAL, "CommitTransactionCommand: unexpected TBLOCK_DEFAULT"); break; /* *************** *** 1290,1295 **** --- 1584,1659 ---- 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; } } *************** *** 1361,1366 **** --- 1725,1731 ---- * state. */ case TBLOCK_ABORT: + case TBLOCK_SUBABORT: break; /* *************** *** 1373,1378 **** --- 1738,1784 ---- 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; } } *************** *** 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 */ } --- 1824,1830 ---- /* 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. */ --- 1946,1970 ---- 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: ! 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; } } --- 1972,1984 ---- 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 **** --- 2003,2017 ---- 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 **** --- 2022,2036 ---- 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; } } --- 2050,2062 ---- 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; } /* --- 2069,2137 ---- { 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 **** --- 2167,2196 ---- /* 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 --- 2247,2582 ---- 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 */ } + /* + * 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) + { + TransactionState s = CurrentTransactionState; + + /* + * Generate a new Xid and record it in pg_subtrans. + */ + s->transactionIdData = GetNewTransactionId(true); + + SubTransSetParent(s->transactionIdData, s->parent->transactionIdData); + + s->pendingDeletes = NIL; + s->childXids = NIL; + + CommandCounterIncrement(); + + /* Initialize the memory subsystem */ + AtSubStart_Memory(); + + ShowTransactionState("StartSubTransaction"); + } + + /* + * CommitSubTransaction + */ + void + CommitSubTransaction(void) + { + TransactionState s = CurrentTransactionState; + ShowTransactionState("CommitSubTransaction"); + + CommandCounterIncrement(); + RecordSubTransactionCommit(); + + Assert(s->parent != NULL); + + smgrCommitSubTransaction(); + + AtSubCommit_Portals(s->parent->transactionIdData); + AtSubCommit_Memory(); + AtSubCommit_childXids(); + AtSubCommit_Notifies(); + } + + /* + * AbortSubTransaction + */ + void + AbortSubTransaction(void) + { + ShowTransactionState("AbortSubTransaction"); + + RecordSubTransactionAbort(); + + AtSubAbort_Portals(); + AtSubAbort_Memory(); + + /* I'm not sure this is needed. */ + CommandCounterIncrement(); + + smgrAbortSubTransaction(); + } + + /* + * CleanupSubTransaction + */ + void + CleanupSubTransaction(void) + { + ShowTransactionState("CleanupSubTransaction"); + + AtSubCleanup_Portals(); + 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)); + memcpy(parent, s, sizeof(TransactionStateData)); + + s->parent = parent; + s->nestingLevel = s->parent->nestingLevel + 1; + s->commandId = s->parent->commandId; + + MemoryContextSwitchTo(old_cxt); + } + + /* + * 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; + + /* Free the old parent structure */ + pfree(p); + } + + /* + * ShowTransactionState + */ + void + ShowTransactionState(char *str) + { + TransactionState s = CurrentTransactionState; + + elog(DEBUG2, "%s", str); + ShowTransactionStateRec(s, 2 * s->nestingLevel); + } + + /* + * ShowTransactionStateRec + */ + void + ShowTransactionStateRec(TransactionState s, int indent) + { + char *blockState = BlockStateAsString(s->blockState); + char *state = TransStateAsString(s->state); + List *child; + char *childStr; + char *tmp; + int len; + + if (s->parent) + ShowTransactionStateRec(s->parent, indent - 2); + + len = length(s->childXids); + if (len == 0) + childStr = "(none)"; + else + { + childStr = (char *)calloc(13, len); + tmp = (char *)malloc(13); + + foreach (child, s->childXids) + { + /* there will be an extra space at the end but ... */ + snprintf(tmp, 12, "%d ", lfirsti(child)); + strncat(childStr, tmp, 12); + } + free(tmp); + } + + tmp = (char *)malloc(sizeof(char) * indent + 1); + memset(tmp, ' ', indent); + memset(tmp + indent, '\0', 1); + + elog(DEBUG2, "%sblockState: %19s; state: %10s, xid/cid: %u/%u, nestlvl: %d, childs: %s", + tmp, + blockState, + state, + (unsigned int)s->transactionIdData, + (unsigned int)s->commandId, + s->nestingLevel, + childStr); + free(tmp); + + if (len != 0) + free(childStr); + pfree(blockState); + pfree(state); + } + + + /* + * 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/access/transam/xlog.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/access/transam/xlog.c,v retrieving revision 1.139 diff -c -r1.139 xlog.c *** src/backend/access/transam/xlog.c 19 Apr 2004 17:42:57 -0000 1.139 --- src/backend/access/transam/xlog.c 3 May 2004 19:31:23 -0000 *************** *** 21,26 **** --- 21,27 ---- #include #include "access/clog.h" + #include "access/subtrans.h" #include "access/transam.h" #include "access/xact.h" #include "access/xlog.h" *************** *** 2757,2762 **** --- 2758,2764 ---- /* Bootstrap the commit log, too */ BootStrapCLOG(); + BootStrapSubTrans(); } static char * *************** *** 3162,3167 **** --- 3164,3170 ---- /* Start up the commit log, too */ StartupCLOG(); + StartupSubTrans(); ereport(LOG, (errmsg("database system is ready"))); *************** *** 3318,3323 **** --- 3321,3327 ---- CritSectionCount++; CreateCheckPoint(true, true); ShutdownCLOG(); + ShutdownSubTrans(); CritSectionCount--; ereport(LOG, *************** *** 3507,3512 **** --- 3511,3517 ---- END_CRIT_SECTION(); CheckPointCLOG(); + CheckPointSubTrans(); FlushBufferPool(); START_CRIT_SECTION(); Index: src/backend/commands/async.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/commands/async.c,v retrieving revision 1.109 diff -c -r1.109 async.c *** src/backend/commands/async.c 8 Feb 2004 22:28:56 -0000 1.109 --- src/backend/commands/async.c 28 Apr 2004 15:38:24 -0000 *************** *** 96,104 **** * in the current transaction. We do not actually perform a NOTIFY until * and unless the transaction commits. pendingNotifies is NIL if no * NOTIFYs have been done in the current transaction. The List nodes and ! * referenced strings are all palloc'd in TopTransactionContext. */ - static List *pendingNotifies = NIL; /* * State for inbound notifies consists of two flags: one saying whether --- 96,105 ---- * in the current transaction. We do not actually perform a NOTIFY until * and unless the transaction commits. pendingNotifies is NIL if no * NOTIFYs have been done in the current transaction. The List nodes and ! * referenced strings are all palloc'd in CommitContext. The List is ! * kept in the TransactionState struct, and is handled by ! * access/transam/xact.c; we get and set it from there whenever needed. */ /* * State for inbound notifies consists of two flags: one saying whether *************** *** 159,165 **** oldcontext = MemoryContextSwitchTo(TopTransactionContext); ! pendingNotifies = lcons(pstrdup(relname), pendingNotifies); MemoryContextSwitchTo(oldcontext); } --- 160,167 ---- oldcontext = MemoryContextSwitchTo(TopTransactionContext); ! TransactionSetNotifies(lcons(pstrdup(relname), ! TransactionGetNotifies())); MemoryContextSwitchTo(oldcontext); } *************** *** 433,439 **** char repl[Natts_pg_listener], nulls[Natts_pg_listener]; ! if (pendingNotifies == NIL) return; /* no NOTIFY statements in this * transaction */ --- 435,441 ---- char repl[Natts_pg_listener], nulls[Natts_pg_listener]; ! if (TransactionGetNotifies() == NIL) return; /* no NOTIFY statements in this * transaction */ *************** *** 915,927 **** elog(INFO, "NOTIFY for %s", relname); } ! /* Does pendingNotifies include the given relname? */ static bool AsyncExistsPendingNotify(const char *relname) { List *p; ! foreach(p, pendingNotifies) { /* Use NAMEDATALEN for relname comparison. DZ - 26-08-1996 */ if (strncmp((const char *) lfirst(p), relname, NAMEDATALEN) == 0) --- 917,929 ---- elog(INFO, "NOTIFY for %s", relname); } ! /* Does the pendingNotifies list include the given relname? */ static bool AsyncExistsPendingNotify(const char *relname) { List *p; ! foreach(p, TransactionGetNotifies()) { /* Use NAMEDATALEN for relname comparison. DZ - 26-08-1996 */ if (strncmp((const char *) lfirst(p), relname, NAMEDATALEN) == 0) *************** *** 938,946 **** /* * We used to have to explicitly deallocate the list members and * nodes, because they were malloc'd. Now, since we know they are ! * palloc'd in TopTransactionContext, we need not do that --- they'll * go away automatically at transaction exit. We need only reset the * list head pointer. */ ! pendingNotifies = NIL; } --- 940,948 ---- /* * We used to have to explicitly deallocate the list members and * nodes, because they were malloc'd. Now, since we know they are ! * palloc'd in CommitContext, we need not do that --- they'll * go away automatically at transaction exit. We need only reset the * list head pointer. */ ! TransactionSetNotifies(NIL); } Index: src/backend/storage/ipc/ipci.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/storage/ipc/ipci.c,v retrieving revision 1.66 diff -c -r1.66 ipci.c *** src/backend/storage/ipc/ipci.c 19 Apr 2004 23:27:17 -0000 1.66 --- src/backend/storage/ipc/ipci.c 3 May 2004 20:40:20 -0000 *************** *** 18,23 **** --- 18,24 ---- #include "miscadmin.h" #include "access/clog.h" #include "access/xlog.h" + #include "access/subtrans.h" #include "storage/bufmgr.h" #include "storage/freespace.h" #include "storage/ipc.h" *************** *** 65,70 **** --- 66,72 ---- size += LockShmemSize(maxBackends); size += XLOGShmemSize(); size += CLOGShmemSize(); + size += SubTransShmemSize(); size += LWLockShmemSize(); size += SInvalShmemSize(maxBackends); size += FreeSpaceShmemSize(); *************** *** 123,128 **** --- 125,131 ---- */ XLOGShmemInit(); CLOGShmemInit(); + SubTransShmemInit(); InitBufferPool(); /* Index: src/backend/storage/ipc/sinval.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/storage/ipc/sinval.c,v retrieving revision 1.62 diff -c -r1.62 sinval.c *** src/backend/storage/ipc/sinval.c 29 Nov 2003 19:51:56 -0000 1.62 --- src/backend/storage/ipc/sinval.c 8 May 2004 01:24:20 -0000 *************** *** 14,20 **** */ #include "postgres.h" ! #include "storage/proc.h" #include "storage/sinval.h" #include "storage/sinvaladt.h" --- 14,20 ---- */ #include "postgres.h" ! #include "access/subtrans.h" #include "storage/proc.h" #include "storage/sinval.h" #include "storage/sinvaladt.h" *************** *** 210,215 **** --- 210,223 ---- LWLockAcquire(SInvalLock, LW_SHARED); + /* + * XXX We currently iterate over each backend seeing if the Xid is + * current for the main transaction or each of the subtransactions. + * It would be probably more efficient if we iterated over all backends + * checking the main transaction Xids, and the iterate again checking + * the subtransactions. Unfortunately we can only fetch the proc->xid + * once, so this may not work. This loop could probably be optimized a lot. + */ for (index = 0; index < segP->lastBackend; index++) { SHMEM_OFFSET pOffset = stateP[index].procStruct; *************** *** 218,223 **** --- 226,233 ---- { PGPROC *proc = (PGPROC *) MAKE_PTR(pOffset); + TransactionId parentXid, + previousXid; /* Fetch xid just once - see GetNewTransactionId */ TransactionId pxid = proc->xid; *************** *** 226,236 **** result = true; break; } } } LWLockRelease(SInvalLock); - return result; } --- 236,273 ---- result = true; break; } + /* + * Note we recurse up the subtrans tree, and TransactionIdDidAbort + * will recurse too. This can be optimized a lot, probably by + * modifying TransactionIdDidAbort() signature to return the latest + * xid tested (which would be an ancestor up the subtrans tree, saving + * us some lookups). + * + * XXX this loop is probably very stupid as is. + */ + parentXid = SubTransGetParent(xid); + + while (TransactionIdIsValid(parentXid)) + { + if (TransactionIdDidAbort(parentXid)) + { + result = false; + goto result; + } + previousXid = parentXid; + parentXid = SubTransGetParent(parentXid); + } + + if (TransactionIdEquals(pxid, previousXid)) + { + result = true; + break; + } } } + result: LWLockRelease(SInvalLock); return result; } Index: src/backend/storage/lmgr/lwlock.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/storage/lmgr/lwlock.c,v retrieving revision 1.19 diff -c -r1.19 lwlock.c *** src/backend/storage/lmgr/lwlock.c 20 Dec 2003 17:31:21 -0000 1.19 --- src/backend/storage/lmgr/lwlock.c 3 May 2004 18:53:04 -0000 *************** *** 22,27 **** --- 22,28 ---- #include "postgres.h" #include "access/clog.h" + #include "access/subtrans.h" #include "storage/lwlock.h" #include "storage/proc.h" #include "storage/spin.h" *************** *** 111,116 **** --- 112,120 ---- /* clog.c needs one per CLOG buffer + one control lock */ numLocks += NUM_CLOG_BUFFERS + 1; + /* subtrans.c needs one per SubTrans buffer + one control lock */ + numLocks += NUM_SUBTRANS_BUFFERS + 1; + /* Perhaps create a few more for use by user-defined modules? */ return numLocks; Index: src/backend/storage/smgr/smgr.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/storage/smgr/smgr.c,v retrieving revision 1.70 diff -c -r1.70 smgr.c *** src/backend/storage/smgr/smgr.c 11 Feb 2004 22:55:25 -0000 1.70 --- src/backend/storage/smgr/smgr.c 6 May 2004 20:41:02 -0000 *************** *** 17,22 **** --- 17,23 ---- */ #include "postgres.h" + #include "access/xact.h" #include "storage/bufmgr.h" #include "storage/freespace.h" #include "storage/ipc.h" *************** *** 77,98 **** * executed immediately, but is just entered in the list. When and if * the transaction commits, we can delete the physical file. * ! * NOTE: the list is kept in TopMemoryContext to be sure it won't disappear ! * unbetimes. It'd probably be OK to keep it in TopTransactionContext, ! * but I'm being paranoid. */ ! typedef struct PendingRelDelete { RelFileNode relnode; /* relation that may need to be deleted */ int which; /* which storage manager? */ bool isTemp; /* is it a temporary relation? */ bool atCommit; /* T=delete at commit; F=delete at abort */ ! struct PendingRelDelete *next; /* linked-list link */ ! } PendingRelDelete; ! ! static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */ /* * Declarations for smgr-related XLOG records --- 78,95 ---- * executed immediately, but is just entered in the list. When and if * the transaction commits, we can delete the physical file. * ! * The list is kept in CommitContext. */ ! typedef struct PendingRelDeleteData { RelFileNode relnode; /* relation that may need to be deleted */ int which; /* which storage manager? */ bool isTemp; /* is it a temporary relation? */ bool atCommit; /* T=delete at commit; F=delete at abort */ ! } PendingRelDeleteData; + typedef PendingRelDeleteData *PendingRelDelete; /* * Declarations for smgr-related XLOG records *************** *** 300,306 **** XLogRecPtr lsn; XLogRecData rdata; xl_smgr_create xlrec; ! PendingRelDelete *pending; if (! (*(smgrsw[reln->smgr_which].smgr_create)) (reln, isRedo)) ereport(ERROR, --- 297,304 ---- XLogRecPtr lsn; XLogRecData rdata; xl_smgr_create xlrec; ! PendingRelDelete pending; ! MemoryContext old_cxt; if (! (*(smgrsw[reln->smgr_which].smgr_create)) (reln, isRedo)) ereport(ERROR, *************** *** 326,340 **** lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE | XLOG_NO_TRAN, &rdata); /* Add the relation to the list of stuff to delete at abort */ ! pending = (PendingRelDelete *) ! MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); pending->relnode = reln->smgr_rnode; pending->which = reln->smgr_which; pending->isTemp = isTemp; pending->atCommit = false; /* delete if abort */ ! pending->next = pendingDeletes; ! pendingDeletes = pending; } /* --- 324,342 ---- lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE | XLOG_NO_TRAN, &rdata); + old_cxt = MemoryContextSwitchTo(CommitContext); + /* Add the relation to the list of stuff to delete at abort */ ! pending = (PendingRelDelete) palloc(sizeof(PendingRelDeleteData)); pending->relnode = reln->smgr_rnode; pending->which = reln->smgr_which; pending->isTemp = isTemp; pending->atCommit = false; /* delete if abort */ ! ! TransactionSetPendingDeletes(lcons(pending, ! TransactionGetPendingDeletes())); ! ! MemoryContextSwitchTo(old_cxt); } /* *************** *** 348,364 **** void smgrscheduleunlink(SMgrRelation reln, bool isTemp) { ! PendingRelDelete *pending; /* Add the relation to the list of stuff to delete at commit */ ! pending = (PendingRelDelete *) ! MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); pending->relnode = reln->smgr_rnode; pending->which = reln->smgr_which; pending->isTemp = isTemp; pending->atCommit = true; /* delete if commit */ ! pending->next = pendingDeletes; ! pendingDeletes = pending; /* * NOTE: if the relation was created in this transaction, it will now --- 350,369 ---- void smgrscheduleunlink(SMgrRelation reln, bool isTemp) { ! PendingRelDelete pending; ! MemoryContext old_cxt; ! ! old_cxt = MemoryContextSwitchTo(CommitContext); /* Add the relation to the list of stuff to delete at commit */ ! pending = (PendingRelDelete) palloc(sizeof(PendingRelDeleteData)); pending->relnode = reln->smgr_rnode; pending->which = reln->smgr_which; pending->isTemp = isTemp; pending->atCommit = true; /* delete if commit */ ! ! TransactionSetPendingDeletes(lcons(pending, ! TransactionGetPendingDeletes())); /* * NOTE: if the relation was created in this transaction, it will now *************** *** 372,377 **** --- 377,384 ---- /* Now close the file and throw away the hashtable entry */ smgrclose(reln); + + MemoryContextSwitchTo(old_cxt); } /* *************** *** 573,590 **** void smgrDoPendingDeletes(bool isCommit) { ! while (pendingDeletes != NULL) { ! PendingRelDelete *pending = pendingDeletes; - pendingDeletes = pending->next; if (pending->atCommit == isCommit) smgr_internal_unlink(pending->relnode, pending->which, pending->isTemp, false); - pfree(pending); } } /* --- 580,642 ---- void smgrDoPendingDeletes(bool isCommit) { ! List *p; ! ! foreach (p, TransactionGetPendingDeletes()) { ! PendingRelDelete pending = lfirst(p); if (pending->atCommit == isCommit) smgr_internal_unlink(pending->relnode, pending->which, pending->isTemp, false); } + + TransactionSetPendingDeletes(NIL); + } + + /* + * smgrAbortSubTransaction() --- Take care of subtransaction abort. + * + * Delete created relations and forget about deleted relations. + * We don't care about these operations anymore because we know this + * subtransaction will not commit. + */ + void + smgrAbortSubTransaction(void) + { + List *p; + + foreach (p, TransactionGetPendingDeletes()) + { + PendingRelDelete pending = lfirst(p); + + if (pending->atCommit == false) + smgr_internal_unlink(pending->relnode, + pending->which, + pending->isTemp, + false); + } + + TransactionSetPendingDeletes(NIL); + } + + /* + * smgrCommitSubTransaction() --- Take care of subtransaction commit. + * + * Reassign all items in the pending deletes list to the parent transaction. + */ + void + smgrCommitSubTransaction(void) + { + List *pending, + *parentPending; + + pending = TransactionGetPendingDeletes(); + parentPending = TransactionGetParentPendingDeletes(); + + TransactionSetParentPendingDeletes(nconc(parentPending, pending)); } /* *************** *** 599,609 **** { int nrels; RelFileNode *rptr; ! PendingRelDelete *pending; nrels = 0; ! for (pending = pendingDeletes; pending != NULL; pending = pending->next) { if (pending->atCommit == forCommit) nrels++; } --- 651,663 ---- { int nrels; RelFileNode *rptr; ! List *p; nrels = 0; ! foreach (p, TransactionGetPendingDeletes()) { + PendingRelDelete pending = lfirst(p); + if (pending->atCommit == forCommit) nrels++; } *************** *** 614,621 **** } rptr = (RelFileNode *) palloc(nrels * sizeof(RelFileNode)); *ptr = rptr; ! for (pending = pendingDeletes; pending != NULL; pending = pending->next) { if (pending->atCommit == forCommit) *rptr++ = pending->relnode; } --- 668,677 ---- } rptr = (RelFileNode *) palloc(nrels * sizeof(RelFileNode)); *ptr = rptr; ! foreach (p, TransactionGetPendingDeletes()) { + PendingRelDelete pending = lfirst(p); + if (pending->atCommit == forCommit) *rptr++ = pending->relnode; } Index: src/backend/tcop/postgres.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/tcop/postgres.c,v retrieving revision 1.401 diff -c -r1.401 postgres.c *** src/backend/tcop/postgres.c 25 Apr 2004 18:23:56 -0000 1.401 --- src/backend/tcop/postgres.c 6 May 2004 20:38:06 -0000 *************** *** 841,846 **** --- 841,847 ---- TransactionStmt *stmt = (TransactionStmt *) parsetree; if (stmt->kind == TRANS_STMT_COMMIT || + stmt->kind == TRANS_STMT_BEGIN || stmt->kind == TRANS_STMT_ROLLBACK) allowit = true; } *************** *** 1161,1166 **** --- 1162,1168 ---- TransactionStmt *stmt = (TransactionStmt *) parsetree; if (stmt->kind == TRANS_STMT_COMMIT || + stmt->kind == TRANS_STMT_BEGIN || stmt->kind == TRANS_STMT_ROLLBACK) allowit = true; } *************** *** 1599,1604 **** --- 1601,1607 ---- is_trans_stmt = true; if (stmt->kind == TRANS_STMT_COMMIT || + stmt->kind == TRANS_STMT_BEGIN || stmt->kind == TRANS_STMT_ROLLBACK) is_trans_exit = true; } Index: src/backend/utils/mmgr/README =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/mmgr/README,v retrieving revision 1.5 diff -c -r1.5 README *** src/backend/utils/mmgr/README 29 Nov 2003 19:52:04 -0000 1.5 --- src/backend/utils/mmgr/README 28 Apr 2004 15:38:22 -0000 *************** *** 70,76 **** We could even consider getting rid of CurrentMemoryContext entirely, instead requiring the target memory context for allocation to be specified explicitly. But I think that would be too much notational overhead --- ! we'd have to pass an apppropriate memory context to called routines in many places. For example, the copyObject routines would need to be passed a context, as would function execution routines that return a pass-by-reference datatype. And what of routines that temporarily --- 70,76 ---- We could even consider getting rid of CurrentMemoryContext entirely, instead requiring the target memory context for allocation to be specified explicitly. But I think that would be too much notational overhead --- ! we'd have to pass an appropriate memory context to called routines in many places. For example, the copyObject routines would need to be passed a context, as would function execution routines that return a pass-by-reference datatype. And what of routines that temporarily *************** *** 157,170 **** single transaction or portal. TopTransactionContext --- this holds everything that lives until end of ! transaction (longer than one statement within a transaction!). An example ! of what has to be here is the list of pending NOTIFY messages to be sent ! at xact commit. This context will be reset, and all its children deleted, ! at conclusion of each transaction cycle. Note: this context is NOT ! cleared immediately upon error; its contents will survive until the ! transaction block is exited by COMMIT/ROLLBACK. ! (If we ever implement nested transactions, TopTransactionContext may need ! to be split into a true "top" pointer and a "current transaction" pointer.) QueryContext --- this is not actually a separate context, but a global variable pointing to the context that holds the current command's parse --- 157,173 ---- single transaction or portal. TopTransactionContext --- this holds everything that lives until end of ! transaction (longer than one statement within a transaction!). This context ! will be reset, and all its children deleted, at conclusion of each ! transaction cycle. Note: this context is NOT cleared immediately upon ! error; its contents will survive until the transaction block is exited by ! COMMIT/ROLLBACK. ! ! CommitContext --- this holds data local to a transaction that has to be ! freed whenever a subtransaction aborts, but which should survive if it ! commits. For example, the list of pending NOTIFY messages has to live here, ! because we don't need to keep messages issued within aborted ! subtransactions. QueryContext --- this is not actually a separate context, but a global variable pointing to the context that holds the current command's parse 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 28 Apr 2004 15:38:22 -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/backend/utils/mmgr/portalmem.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/mmgr/portalmem.c,v retrieving revision 1.64 diff -c -r1.64 portalmem.c *** src/backend/utils/mmgr/portalmem.c 3 Feb 2004 17:34:03 -0000 1.64 --- src/backend/utils/mmgr/portalmem.c 6 May 2004 20:38:42 -0000 *************** *** 511,513 **** --- 511,601 ---- PortalDrop(portal, true); } } + + /* + * Subtransaction commit handling for portals. + * + * Reassign all portals created by the current subtransaction to the + * parent transaction. + */ + void + AtSubCommit_Portals(TransactionId parentXid) + { + HASH_SEQ_STATUS status; + PortalHashEnt *hentry; + TransactionId curXid = GetCurrentTransactionId(); + + hash_seq_init(&status, PortalHashTable); + + while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL) + { + Portal portal = hentry->portal; + + if (portal->createXact == curXid) + portal->createXact = parentXid; + } + } + + /* + * Subtransaction abort handling for portals. + * + * Deactivate all portals created during to the finishing subtransaction. + * Note that per AtSubCommit_Portals, this will catch portals created + * in descendents of the finishing subtransaction too. + */ + void + AtSubAbort_Portals(void) + { + HASH_SEQ_STATUS status; + PortalHashEnt *hentry; + TransactionId curXid = GetCurrentTransactionId(); + + hash_seq_init(&status, PortalHashTable); + + while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL) + { + Portal portal = hentry->portal; + + if (portal->createXact != curXid) + continue; + + portal->portalActive = false; + if (PointerIsValid(portal->cleanup)) + { + (*portal->cleanup) (portal, true); + portal->cleanup = NULL; + } + } + } + + /* + * Subtransaction cleanup handling for portals. + * + * Drop all portals created in the aborting subtransaction and all + * its descendents. + */ + void + AtSubCleanup_Portals(void) + { + HASH_SEQ_STATUS status; + PortalHashEnt *hentry; + TransactionId curXid = GetCurrentTransactionId(); + + hash_seq_init(&status, PortalHashTable); + + while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL) + { + Portal portal = hentry->portal; + + if (portal->createXact != curXid) + continue; + + /* + * Let's just make sure no one's active... + */ + portal->portalActive = false; + + /* Zap it with prejudice. */ + PortalDrop(portal, true); + } + } Index: src/backend/utils/time/tqual.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/time/tqual.c,v retrieving revision 1.72 diff -c -r1.72 tqual.c *** src/backend/utils/time/tqual.c 29 Nov 2003 19:52:04 -0000 1.72 --- src/backend/utils/time/tqual.c 8 May 2004 20:35:24 -0000 *************** *** 868,875 **** { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HEAPTUPLE_INSERT_IN_PROGRESS; ! Assert(HeapTupleHeaderGetXmin(tuple) == ! HeapTupleHeaderGetXmax(tuple)); if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return HEAPTUPLE_INSERT_IN_PROGRESS; /* inserted and then deleted by same xact */ --- 868,878 ---- { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HEAPTUPLE_INSERT_IN_PROGRESS; ! elog(DEBUG1, "Assert(): %u and %u should be in the same xact tree", ! HeapTupleHeaderGetXmin(tuple), ! HeapTupleHeaderGetXmax(tuple)); ! /* Assert(HeapTupleHeaderGetXmin(tuple) == ! HeapTupleHeaderGetXmax(tuple));*/ if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE) return HEAPTUPLE_INSERT_IN_PROGRESS; /* inserted and then deleted by same xact */ Index: src/bin/initdb/initdb.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/bin/initdb/initdb.c,v retrieving revision 1.23 diff -c -r1.23 initdb.c *** src/bin/initdb/initdb.c 9 Mar 2004 04:49:02 -0000 1.23 --- src/bin/initdb/initdb.c 3 May 2004 19:32:14 -0000 *************** *** 2044,2050 **** char *pgdenv; /* PGDATA value got from sent to * environment */ char *subdirs[] = ! {"global", "pg_xlog", "pg_clog", "base", "base/1"}; char *lastsep; char *carg0; #if defined(__CYGWIN__) || defined(WIN32) --- 2044,2050 ---- char *pgdenv; /* PGDATA value got from sent to * environment */ char *subdirs[] = ! {"global", "pg_xlog", "pg_clog", "pg_subtrans", "base", "base/1"}; char *lastsep; char *carg0; #if defined(__CYGWIN__) || defined(WIN32) Index: src/include/access/clog.h =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/include/access/clog.h,v retrieving revision 1.8 diff -c -r1.8 clog.h *** src/include/access/clog.h 29 Nov 2003 22:40:55 -0000 1.8 --- src/include/access/clog.h 4 May 2004 20:40:05 -0000 *************** *** 16,28 **** /* * Possible transaction statuses --- note that all-zeroes is the initial * state. */ typedef int XidStatus; #define TRANSACTION_STATUS_IN_PROGRESS 0x00 #define TRANSACTION_STATUS_COMMITTED 0x01 #define TRANSACTION_STATUS_ABORTED 0x02 ! /* 0x03 is available without changing commit log space allocation */ /* exported because lwlock.c needs it */ #define NUM_CLOG_BUFFERS 8 --- 16,31 ---- /* * Possible transaction statuses --- note that all-zeroes is the initial * state. + * + * A "subcommitted" transaction is a committed subtransaction whose parent + * hasn't committed or aborted yet. */ typedef int XidStatus; #define TRANSACTION_STATUS_IN_PROGRESS 0x00 #define TRANSACTION_STATUS_COMMITTED 0x01 #define TRANSACTION_STATUS_ABORTED 0x02 ! #define TRANSACTION_STATUS_SUB_COMMITTED 0x03 /* exported because lwlock.c needs it */ #define NUM_CLOG_BUFFERS 8 Index: src/include/access/rmgr.h =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/include/access/rmgr.h,v retrieving revision 1.10 diff -c -r1.10 rmgr.h *** src/include/access/rmgr.h 29 Nov 2003 22:40:55 -0000 1.10 --- src/include/access/rmgr.h 3 May 2004 16:31:00 -0000 *************** *** 17,22 **** --- 17,23 ---- #define RM_XACT_ID 1 #define RM_SMGR_ID 2 #define RM_CLOG_ID 3 + #define RM_SUBTRANS_ID 4 #define RM_HEAP_ID 10 #define RM_BTREE_ID 11 #define RM_HASH_ID 12 Index: src/include/access/transam.h =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/include/access/transam.h,v retrieving revision 1.48 diff -c -r1.48 transam.h *** src/include/access/transam.h 29 Nov 2003 22:40:55 -0000 1.48 --- src/include/access/transam.h 5 May 2004 01:53:44 -0000 *************** *** 14,19 **** --- 14,20 ---- #ifndef TRANSAM_H #define TRANSAM_H + #include "nodes/pg_list.h" /* ---------------- * Special transaction ID values *************** *** 107,119 **** extern bool TransactionIdDidAbort(TransactionId transactionId); extern void TransactionIdCommit(TransactionId transactionId); extern void TransactionIdAbort(TransactionId transactionId); extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2); extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2); extern bool TransactionIdFollows(TransactionId id1, TransactionId id2); extern bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2); /* in transam/varsup.c */ ! extern TransactionId GetNewTransactionId(void); extern TransactionId ReadNewTransactionId(void); extern Oid GetNewObjectId(void); extern void CheckMaxObjectId(Oid assigned_oid); --- 108,123 ---- extern bool TransactionIdDidAbort(TransactionId transactionId); extern void TransactionIdCommit(TransactionId transactionId); extern void TransactionIdAbort(TransactionId transactionId); + extern void TransactionIdSubCommit(TransactionId transactionId); + extern void TransactionIdSubAbort(List *transactionIds); + extern void TransactionIdCommitTree(List *transactionIds); extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2); extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2); extern bool TransactionIdFollows(TransactionId id1, TransactionId id2); extern bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2); /* in transam/varsup.c */ ! extern TransactionId GetNewTransactionId(bool isSubXact); extern TransactionId ReadNewTransactionId(void); extern Oid GetNewObjectId(void); extern void CheckMaxObjectId(Oid assigned_oid); 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 6 May 2004 20:42:34 -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; + int nestingLevel; + MemoryContext commitContext; + void *deferredTriggers; + List *prepared; + List *pendingDeletes; + List *notifies; + LWLockId *LWLocks; + List *locks; + List *childXids; + struct TransactionStateData *parent; } TransactionStateData; typedef TransactionStateData *TransactionState; *************** *** 130,135 **** --- 149,165 ---- extern CommandId GetCurrentCommandId(void); extern AbsoluteTime GetCurrentTransactionStartTime(void); extern AbsoluteTime GetCurrentTransactionStartTimeUsec(int *usec); + + extern List *TransactionGetPendingDeletes(void); + extern void TransactionSetPendingDeletes(List *); + extern List *TransactionGetParentPendingDeletes(void); + extern void TransactionSetParentPendingDeletes(List *); + + extern List *TransactionGetNotifies(void); + extern void TransactionSetNotifies(List *); + extern List *TransactionGetParentNotifies(void); + extern void TransactionSetParentNotifies(List *); + extern bool TransactionIdIsCurrentTransactionId(TransactionId xid); extern bool CommandIdIsCurrentCommandId(CommandId cid); extern void CommandCounterIncrement(void); Index: src/include/storage/smgr.h =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/include/storage/smgr.h,v retrieving revision 1.41 diff -c -r1.41 smgr.h *** src/include/storage/smgr.h 11 Feb 2004 22:55:26 -0000 1.41 --- src/include/storage/smgr.h 28 Apr 2004 15:38:25 -0000 *************** *** 61,66 **** --- 61,68 ---- extern BlockNumber smgrnblocks(SMgrRelation reln); extern BlockNumber smgrtruncate(SMgrRelation reln, BlockNumber nblocks); extern void smgrDoPendingDeletes(bool isCommit); + extern void smgrAbortSubTransaction(void); + extern void smgrCommitSubTransaction(void); extern int smgrGetPendingDeletes(bool forCommit, RelFileNode **ptr); extern void smgrcommit(void); extern void smgrabort(void); 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 28 Apr 2004 15:38:25 -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; Index: src/include/utils/portal.h =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/include/utils/portal.h,v retrieving revision 1.48 diff -c -r1.48 portal.h *** src/include/utils/portal.h 29 Nov 2003 22:41:16 -0000 1.48 --- src/include/utils/portal.h 6 May 2004 20:41:58 -0000 *************** *** 167,172 **** --- 167,175 ---- extern void AtCommit_Portals(void); extern void AtAbort_Portals(void); extern void AtCleanup_Portals(void); + extern void AtSubCommit_Portals(TransactionId); + extern void AtSubAbort_Portals(void); + extern void AtSubCleanup_Portals(void); extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent); extern Portal CreateNewPortal(void); extern void PortalDrop(Portal portal, bool isError); Index: src/backend/access/transam/rmgr.c =================================================================== RCS file: /home/alvherre/cvs/pgsql-server/src/backend/access/transam/rmgr.c,v retrieving revision 1.12 diff -c -r1.12 rmgr.c *** src/backend/access/transam/rmgr.c 29 Nov 2003 19:51:40 -0000 1.12 --- src/backend/access/transam/rmgr.c 8 May 2004 21:08:25 -0000 *************** *** 24,30 **** {"Transaction", xact_redo, xact_undo, xact_desc, NULL, NULL}, {"Storage", smgr_redo, smgr_undo, smgr_desc, NULL, NULL}, {"CLOG", clog_redo, clog_undo, clog_desc, NULL, NULL}, ! {"Reserved 4", NULL, NULL, NULL, NULL, NULL}, {"Reserved 5", NULL, NULL, NULL, NULL, NULL}, {"Reserved 6", NULL, NULL, NULL, NULL, NULL}, {"Reserved 7", NULL, NULL, NULL, NULL, NULL}, --- 24,30 ---- {"Transaction", xact_redo, xact_undo, xact_desc, NULL, NULL}, {"Storage", smgr_redo, smgr_undo, smgr_desc, NULL, NULL}, {"CLOG", clog_redo, clog_undo, clog_desc, NULL, NULL}, ! {"SubTrans", subtrans_redo, subtrans_undo, subtrans_desc, NULL, NULL}, {"Reserved 5", NULL, NULL, NULL, NULL, NULL}, {"Reserved 6", NULL, NULL, NULL, NULL, NULL}, {"Reserved 7", NULL, NULL, NULL, NULL, NULL},