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

Reply via email to