I wrote ten seconds ago:
> This version does. This patch includes both patches I
> posted and a few more changes, and does the following:
I mean this one.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"�Qu� importan los a�os? Lo que realmente importa es comprobar que
a fin de cuentas la mejor edad de la vida es estar vivo" (Mafalda)
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 26 Apr 2004 13:25:30 -0000
***************
*** 189,194 ****
--- 189,227 ----
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 AtSubAbort_Memory(void);
+ static void AtSubCleanup_Memory(void);
+ static void AtSubCommit_Notifies(void);
+
+ static char *TransStateAsString(TransState);
+
+ #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);
+
/*
* 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;
--- 231,251 ----
0, /* scan command id */
0x0, /* start time */
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 */
+ #endif /* SUBTRANSACTIONS */
+ NIL, /* smgr pending deletes */
+ NIL /* async notifies */
+ #ifdef SUBTRANSACTIONS
+ ,
+ 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;
--- 327,334 ----
{
TransactionState s = CurrentTransactionState;
! if (s->blockState == TBLOCK_ABORT ||
! s->blockState == TBLOCK_SUBABORT)
return true;
return false;
***************
*** 337,342 ****
--- 384,484 ----
return s->startTime;
}
+ /*
+ * TransactionGetPendingDeletes
+ */
+ List *
+ TransactionGetPendingDeletes(void)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ return s->pendingDeletes;
+ }
+
+ /*
+ * TransactionSetPendingDeletes
+ */
+ void
+ TransactionSetPendingDeletes(List *pending)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ s->pendingDeletes = pending;
+ }
+
+ #ifdef SUBTRANSACTIONS
+ /*
+ * 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;
+ }
+ #endif /* SUBTRANSACTIONS */
+
+ /*
+ * TransactionGetNotifies
+ */
+ List *
+ TransactionGetNotifies(void)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ return s->notifies;
+ }
+
+ /*
+ * TransactionSetNotifies
+ */
+ void
+ TransactionSetNotifies(List *notifies)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ s->notifies = notifies;
+ }
+
+ #ifdef SUBTRANSACTIONS
+ /*
+ * 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;
+ }
+ #endif /* SUBTRANSACTIONS */
/*
* TransactionIdIsCurrentTransactionId
***************
*** 436,441 ****
--- 578,587 ----
static void
AtStart_Memory(void)
{
+ #ifdef SUBTRANSACTIONS
+ TransactionState s = CurrentTransactionState;
+ #endif /* SUBTRANSACTIONS */
+
/*
* We shouldn't have a transaction context already.
*/
***************
*** 452,459 ****
--- 598,652 ----
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
+ s->commitContext = CommitContext;
+ #endif /* SUBTRANSACTIONS */
}
+ #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);
+ s->commitContext = CommitContext;
+ }
+ #endif /* SUBTRANSACTIONS */
/* ----------------------------------------------------------------
* CommitTransaction stuff
***************
*** 637,644 ****
--- 830,893 ----
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);
+ }
+
+ /*
+ * 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 = s->notifies;
+ }
+
+ #endif /* SUBTRANSACTIONS */
+
/* ----------------------------------------------------------------
* AbortTransaction stuff
* ----------------------------------------------------------------
***************
*** 778,783 ****
--- 1027,1044 ----
MemoryContextSwitchTo(TopMemoryContext);
}
+ #ifdef SUBTRANSACTIONS
+ /*
+ * AtSubAbort_Memory
+ */
+ static void
+ AtSubAbort_Memory(void)
+ {
+ Assert(TopTransactionContext != NULL);
+
+ MemoryContextSwitchTo(TopTransactionContext);
+ }
+ #endif
/* ----------------------------------------------------------------
* CleanupTransaction stuff
***************
*** 806,811 ****
--- 1067,1100 ----
}
+ #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
* ----------------------------------------------------------------
***************
*** 851,856 ****
--- 1140,1149 ----
s->commandId = FirstCommandId;
s->startTime = GetCurrentAbsoluteTimeUsec(&(s->startTimeUsec));
+ #ifdef SUBTRANSACTIONS
+ s->childXids = NIL;
+ #endif
+
/*
* initialize the various transaction subsystems
*/
***************
*** 868,873 ****
--- 1161,1167 ----
* progress"
*/
s->state = TRANS_INPROGRESS;
+ ShowTransactionState("StartTransaction");
}
***************
*** 879,884 ****
--- 1173,1180 ----
{
TransactionState s = CurrentTransactionState;
+ ShowTransactionState("CommitTransaction");
+
/*
* check the current transaction state
*/
***************
*** 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;
/*
--- 1441,1453 ----
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;
}
--- 1457,1477 ----
* 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;
/*
--- 1499,1505 ----
* appropiately.
*/
case TBLOCK_DEFAULT:
! elog(FATAL, "CommitTransactionCommand: unexpected
TBLOCK_DEFAULT");
break;
/*
***************
*** 1290,1295 ****
--- 1558,1633 ----
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 ****
--- 1699,1705 ----
* state.
*/
case TBLOCK_ABORT:
+ case TBLOCK_SUBABORT:
break;
/*
***************
*** 1373,1378 ****
--- 1712,1758 ----
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;
}
}
***************
*** 1391,1396 ****
--- 1771,1777 ----
* 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 */
}
--- 1799,1805 ----
/* 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. */
--- 1921,1951 ----
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;
}
}
--- 1953,1965 ----
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 ****
--- 1984,1998 ----
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 ****
--- 2003,2017 ----
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;
}
}
--- 2031,2043 ----
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;
}
/*
--- 2050,2118 ----
{
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 ****
--- 2148,2177 ----
/* 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
--- 2228,2561 ----
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)
+ {
+ TransactionState s = CurrentTransactionState;
+
+ /*
+ * generate a new transaction id
+ */
+ s->transactionIdData = GetNewTransactionId();
+
+ s->childXids = NIL;
+
+ CommandCounterIncrement();
+
+ /* Initialize the memory subsystem */
+ AtSubStart_Memory();
+
+ ShowTransactionState("StartSubTransaction");
+ }
+
+ /*
+ * CommitSubTransaction
+ */
+ void
+ CommitSubTransaction(void)
+ {
+ TransactionState s = CurrentTransactionState;
+ ShowTransactionState("CommitSubTransaction");
+
+ CommandCounterIncrement();
+
+ Assert(s->parent != NULL);
+
+ smgrCommitSubTransaction();
+
+ AtSubCommit_Portals(s->parent->transactionIdData);
+ AtSubCommit_Memory();
+ AtSubCommit_childXids();
+ AtSubCommit_Notifies();
+ }
+
+ /*
+ * AbortSubTransaction
+ */
+ void
+ AbortSubTransaction(void)
+ {
+ ShowTransactionState("AbortSubTransaction");
+
+ 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);
+ }
+
+ #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);
+ }
+
+ #ifdef SUBTRANSACTIONS
+ /*
+ * 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);
+ }
+ #endif /* SUBTRANSACTIONS */
/*
* XLOG support routines
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 26 Apr 2004 02:54:03 -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/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 26 Apr 2004 04:32:27 -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,592 ****
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);
}
}
/*
* smgrGetPendingDeletes() -- Get a list of relations to be deleted.
*
--- 580,644 ----
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);
}
+ #ifdef SUBTRANSACTIONS
+ /*
+ * 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);
+ }
+ }
+
+ /*
+ * 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));
+ }
+ #endif /* SUBTRANSACTIONS */
+
/*
* smgrGetPendingDeletes() -- Get a list of relations to be deleted.
*
***************
*** 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 25 Apr 2004 21:49:59 -0000
***************
*** 841,846 ****
--- 841,849 ----
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/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 26 Apr 2004 03:48:13 -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 21 Apr 2004 14:26:41 -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 25 Apr 2004 16:51:24 -0000
***************
*** 511,513 ****
--- 511,603 ----
PortalDrop(portal, true);
}
}
+
+ #ifdef SUBTRANSACTIONS
+ /*
+ * 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);
+ }
+ }
+ #endif /* SUBTRANSACTIONS */
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 26 Apr 2004 14:53:01 -0000
***************
*** 235,237 ****
--- 235,239 ----
/* #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 26 Apr 2004 04:19:06 -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,109 ----
int startTimeUsec;
TransState state;
TBlockState blockState;
+ #ifdef SUBTRANSACTIONS
+ int nestingLevel;
+ MemoryContext commitContext;
+ void *deferredTriggers;
+ #endif /* SUBTRANSACTIONS */
+ List *pendingDeletes;
+ List *notifies;
+ #ifdef SUBTRANSACTIONS
+ LWLockId *LWLocks;
+ List *locks;
+ List *childXids;
+ struct TransactionStateData *parent;
+ #endif /* SUBTRANSACTIONS */
} TransactionStateData;
typedef TransactionStateData *TransactionState;
***************
*** 130,135 ****
--- 152,168 ----
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 21 Apr 2004 15:33:45 -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 21 Apr 2004 14:26:44 -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 25 Apr 2004 16:51:03 -0000
***************
*** 167,172 ****
--- 167,177 ----
extern void AtCommit_Portals(void);
extern void AtAbort_Portals(void);
extern void AtCleanup_Portals(void);
+ #ifdef SUBTRANSACTIONS
+ extern void AtSubCommit_Portals(TransactionId);
+ extern void AtSubAbort_Portals(void);
+ extern void AtSubCleanup_Portals(void);
+ #endif /* SUBTRANSACTIONS */
extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent);
extern Portal CreateNewPortal(void);
extern void PortalDrop(Portal portal, bool isError);
---------------------------(end of broadcast)---------------------------
TIP 5: Have you checked our extensive FAQ?
http://www.postgresql.org/docs/faqs/FAQ.html