On Mon, Apr 19, 2004 at 11:13:35AM -0400, Alvaro Herrera wrote:

> I noticed that I sent an old version because of a system crash (the
> *one* time I don't review vi -r differences it bites me ... argh).  It
> has several obvious mistakes.  Please do not waste your time reviewing
> that; I'll submit a corrected version later, which will also contain
> some more changes.

Ok, hopefully this one is better.

I'm thinking that I'll to add a new elog level to signal a can't-happen
condition within the transaction machinery, which would abort the whole
transaction tree (more than ERROR) but would not take the whole backend
down (less than FATAL).  What should it be called?  Do people agree that
it's needed?

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

               http://archives.postgresql.org

Reply via email to