Hackers,
Here is a very preliminar patch that allows the user to say "BEGIN"
inside a transaction and have the system react accordingly. This is
only a modification to xact.c (and slightly to other places to allow it
to work); the important functions are empty.
It compiles fine for me with both SUBTRANSACTIONS defined and not
defined; when not defined, the behavior is the same as the current code.
Please note that I have made some errors more fatal than they are now,
as bugs in this code will have much worse effects than a flaw in the
current transaction system.
One quick note: there are two ENDABORT states for a subtransaction,
SUBENDABORT_OK and SUBENDABORT_ERROR. They signal whether the parent
transaction should be aborted after the child transaction finishes or
not: an aborted subtransaction where the user issues COMMIT should
abort the parent transaction; if the user issues ROLLBACK, the parent
can be allowed to continue.
Please have a look and comment. This file does not move a lot so I
don't think it will suffer from a lot of code drift.
--
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"I think my standards have lowered enough that now I think 'good design'
is when the page doesn't irritate the living f*ck out of me." (JWZ)
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 14 Apr 2004 02:50:02 -0000
***************
*** 189,194 ****
--- 189,219 ----
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(void);
+ static void ShowTransactionStateRec(TransactionState, int);
+ #else /* SUBTRANSACTIONS */
+ #define StartSubTransaction()
+ #define CommitSubTransaction()
+ #define AbortSubTransaction()
+ #define CleanupSubTransaction()
+ #define PushCurrentTransaction()
+ #define PopTransaction()
+ #define IsSubTransaction() (false)
+ #define ShowTransactionState()
+ #define ShowTransactionStateRec()
+ #endif /* SUBTRANSACTIONS */
+
+ static char *BlockStateAsString(TBlockState);
+ static char *TransStateAsString(TransState);
+
/*
* global variables holding the current transaction state.
*/
***************
*** 200,205 ****
--- 225,237 ----
TRANS_DEFAULT, /* transaction state */
TBLOCK_DEFAULT /* transaction block state from the
client
* perspective */
+ #ifdef SUBTRANSACTIONS
+ ,
+ 0, /* nesting level */
+ NULL, /* top transaction memory
context */
+ NULL, /* commit memory context */
+ NULL /* parent transaction */
+ #endif
};
static TransactionState CurrentTransactionState = &CurrentTransactionStateData;
***************
*** 281,287 ****
{
TransactionState s = CurrentTransactionState;
! if (s->blockState == TBLOCK_ABORT)
return true;
return false;
--- 313,320 ----
{
TransactionState s = CurrentTransactionState;
! if (s->blockState == TBLOCK_ABORT ||
! s->blockState == TBLOCK_SUBABORT)
return true;
return false;
***************
*** 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;
/*
--- 1178,1190 ----
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;
}
--- 1194,1214 ----
* 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;
/*
--- 1236,1242 ----
* appropiately.
*/
case TBLOCK_DEFAULT:
! elog(FATAL, "CommitTransactionCommand: unexpected
TBLOCK_DEFAULT");
break;
/*
***************
*** 1290,1295 ****
--- 1295,1362 ----
CleanupTransaction();
s->blockState = TBLOCK_DEFAULT;
break;
+
+ /*
+ * We were just issued a BEGIN inside a transaction block.
+ * Start a subtransaction.
+ */
+ case TBLOCK_SUBBEGIN:
+ PushCurrentTransaction();
+ 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:
+ PushCurrentTransaction();
+ 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.
+ * Eventually we will receive a COMMIT or ROLLBACK.
+ */
+ case TBLOCK_SUBABORT:
+ break;
+
+ case TBLOCK_SUBENDABORT_OK:
+ CleanupSubTransaction();
+ PopTransaction();
+ break;
+
+ /*
+ * FIXME -- I think I'm missing an Abort(Sub)?Transaction
+ * after changing the parent's state.
+ */
+ case TBLOCK_SUBENDABORT_ERROR:
+ CleanupSubTransaction();
+ PopTransaction();
+ if (IsSubTransaction())
+ s->blockState = TBLOCK_SUBABORT;
+ else
+ s->blockState = TBLOCK_ABORT;
+ break;
}
}
***************
*** 1361,1366 ****
--- 1428,1434 ----
* state.
*/
case TBLOCK_ABORT:
+ case TBLOCK_SUBABORT:
break;
/*
***************
*** 1373,1378 ****
--- 1441,1487 ----
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 */
}
--- 1527,1533 ----
/* 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. */
--- 1649,1677 ----
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:
+ 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:
! 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;
}
}
--- 1679,1691 ----
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 ****
--- 1710,1724 ----
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 ****
--- 1729,1743 ----
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;
}
}
--- 1757,1769 ----
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;
}
/*
--- 1776,1844 ----
{
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 ****
--- 1874,1903 ----
/* 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
--- 1954,2220 ----
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)
+ {
+ elog(DEBUG1, "StartSubTransaction");
+ ShowTransactionState();
+ }
+
+ /*
+ * CommitSubTransaction
+ */
+ void
+ CommitSubTransaction(void)
+ {
+ elog(DEBUG1, "CommitSubTransaction");
+ ShowTransactionState();
+ }
+
+ /*
+ * AbortSubTransaction
+ */
+ void
+ AbortSubTransaction(void)
+ {
+ elog(DEBUG1, "AbortSubTransaction");
+ ShowTransactionState();
+ }
+
+ /*
+ * CleanupSubTransaction
+ */
+ void
+ CleanupSubTransaction(void)
+ {
+ elog(DEBUG1, "CleanupSubTransaction");
+ ShowTransactionState();
+ }
+
+ /*
+ * PushCurrentTransaction
+ */
+ void
+ PushCurrentTransaction(void)
+ {
+ TransactionState parent;
+ TransactionState s = CurrentTransactionState;
+ MemoryContext old_cxt;
+
+ /*
+ * Make sure the transaction state node is in a long-lived context
+ */
+ old_cxt = MemoryContextSwitchTo(TopMemoryContext);
+ parent = (TransactionState) palloc(sizeof(TransactionStateData));
+ MemoryContextSwitchTo(old_cxt);
+
+ memcpy(parent, s, sizeof(TransactionStateData));
+
+ parent->topTransactionContext = TopTransactionContext;
+ s->parent = parent;
+ s->nestingLevel = s->parent->nestingLevel+1;
+ }
+
+ /*
+ * PopTransaction
+ */
+ void
+ PopTransaction(void)
+ {
+ TransactionState s = CurrentTransactionState;
+ TransactionState p;
+
+ Assert(s->parent != NULL);
+
+ p = s->parent;
+
+ memcpy(s, p, sizeof(TransactionStateData));
+
+ /* Free the old parent structure */
+ pfree(p);
+ }
+
+ /*
+ * ShowTransactionState
+ */
+ void
+ ShowTransactionState(void)
+ {
+ ShowTransactionStateRec(CurrentTransactionState, 0);
+ }
+
+ /*
+ * ShowTransactionStateRec
+ */
+ void
+ ShowTransactionStateRec(TransactionState s, int indent)
+ {
+ char *i;
+ char *blockState = BlockStateAsString(s->blockState);
+ char *state = TransStateAsString(s->state);
+
+ i = (char *)malloc(sizeof(char) * indent + 1);
+ memset(i, ' ', indent);
+ memset(i + indent, '\0', 1);
+
+ elog(DEBUG1, "%sblockState: %s; state: %s, tid/cid: %u/%u, nestlvl: %d",
+ i,
+ blockState,
+ state,
+ (unsigned int)s->transactionIdData,
+ (unsigned int)s->commandId ,
+ s->nestingLevel);
+ free(i);
+ pfree(blockState);
+ pfree(state);
+ if (s->parent)
+ ShowTransactionStateRec(s->parent, indent + 1);
+ }
+
+ #endif /* SUBTRANSACTIONS */
+
+ /*
+ * BlockStateAsString
+ *
+ * Returns the block state as a palloc'ed string
+ */
+ char *
+ BlockStateAsString(TBlockState blockState)
+ {
+ char *tmp;
+ switch (blockState) {
+ case TBLOCK_DEFAULT:
+ tmp = "DEFAULT";
+ break;
+ case TBLOCK_BEGIN:
+ tmp = "BEGIN";
+ break;
+ case TBLOCK_INPROGRESS:
+ tmp = "INPROGRESS";
+ break;
+ case TBLOCK_END:
+ tmp = "END";
+ break;
+ case TBLOCK_ENDABORT:
+ tmp = "ENDABORT";
+ break;
+ case TBLOCK_SUBBEGIN:
+ tmp = "SUB BEGIN";
+ break;
+ case TBLOCK_SUBBEGINABORT:
+ tmp = "SUB BEGIN ABORT";
+ 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;
+ case TBLOCK_ABORT:
+ tmp = "ABORT";
+ break;
+ default:
+ tmp = "UNKNOWN";
+ 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;
+ default:
+ tmp = "UNKNOWN";
+ break;
+ }
+ return pstrdup(tmp);
+ }
/*
* XLOG support routines
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/tcop/postgres.c,v
retrieving revision 1.399
diff -c -r1.399 postgres.c
*** src/backend/tcop/postgres.c 11 Apr 2004 00:54:44 -0000 1.399
--- src/backend/tcop/postgres.c 14 Apr 2004 02:43:43 -0000
***************
*** 846,851 ****
--- 846,854 ----
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
+ #ifdef SUBTRANSACTIONS
+ stmt->kind == TRANS_STMT_BEGIN ||
+ #endif
stmt->kind == TRANS_STMT_ROLLBACK)
allowit = true;
}
Index: src/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 13 Apr 2004 22:21:56 -0000
***************
*** 235,237 ****
--- 235,240 ----
/* #define ACLDEBUG */
/* #define RTDEBUG */
/* #define GISTDEBUG */
+
+ /* #define SUBTRANSACTIONS */
+
Index: src/include/access/xact.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/access/xact.h,v
retrieving revision 1.62
diff -c -r1.62 xact.h
*** src/include/access/xact.h 5 Apr 2004 03:11:39 -0000 1.62
--- src/include/access/xact.h 14 Apr 2004 02:43:46 -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;
/*
***************
*** 74,79 ****
--- 83,89 ----
/*
* transaction state structure
*/
+
typedef struct TransactionStateData
{
TransactionId transactionIdData;
***************
*** 82,87 ****
--- 92,103 ----
int startTimeUsec;
TransState state;
TBlockState blockState;
+ #ifdef SUBTRANSACTIONS
+ int nestingLevel;
+ MemoryContext topTransactionContext;
+ MemoryContext commitContext;
+ struct TransactionStateData *parent;
+ #endif
} TransactionStateData;
typedef TransactionStateData *TransactionState;
---------------------------(end of broadcast)---------------------------
TIP 3: if posting/reading through Usenet, please send an appropriate
subscribe-nomail command to [EMAIL PROTECTED] so that your
message can get through to the mailing list cleanly