Hackers,

Here is a patch covering the syntax change.  This changes the current
subtransaction-initiating command to SUBBEGIN instead of BEGIN;
similarly SUBCOMMIT and SUBABORT.

I did not add a SUBROLLBACK command ... rather I want to use the
standard syntax "SAVEPOINT <foo>" and "ROLLBACK TO <foo>" and keep our
nonstandard syntax small.


Note that with this patch it is possible to start a subtransaction when
not in a transaction block.  This makes the new commands to behave
almost exactly like toplevel BEGIN/COMMIT (the difference is not visible
to the user: the server is in nesting level 2 rather than 1, but the
outer level will automatically commit or roll back).

This also means that a single COMMIT will commit the whole transaction
tree:

BEGIN;
        create table foo (a int);
        SUBBEGIN;
                insert into foo values (1);
                SUBBEGIN;
                        insert into foo values (2);
COMMIT;

Also a single ABORT/ROLLBACK aborts the whole thing.

Included in this patch is the ability to "ignore errors" in a subcommit,
so this works:

begin;
        subbegin;
                drop table foo;
                -- error: table does not exist
        subcommit ignore errors;
        create table foo (...);
commit;

The point is that this can be executed in a dumb script without worrying
about whether the subtransaction will cause an error or not.


I'm not sure if the grammar modifications are good.  I thought about
using two Sconst and comparing them to "ignore errors" ... right now,
"ignore" and "errors" are in the unreserved keywords list and I get no
errors/warnings from bison, but please check this.

I had to add a new transaction block state to support rolling back a
whole transaction tree.  Also I moved the TransactionState declaration
to xact.c because it has no business being in the xact.h header file
that I can see.

I made some changes to SPI so that it forbids to close a subtransaction
that the _SPI_connection did not open, by saving the nesting level at
SPI_connect() time and checking when SPI_execute is called.  I had
thought that it would be easy to return to that nesting level if the
function errored out, but I was quite wrong (because the SPI code stops
executing immediately as soon as an error is encountered).  Now I don't
know how to do that at all.  I also thought about adding something to
the sigsetjmp() block but I don't have a clue how to handle this.


Regression tests pass; I had to change the transaction test to adopt the
new syntax.  I also added a couple of tests to verify the new
functionality.

-- 
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"La experiencia nos dice que el hombre peló millones de veces las patatas,
pero era forzoso admitir la posibilidad de que en un caso entre millones,
las patatas pelarían al hombre" (Ijon Tichy)
Index: src/backend/access/transam/xact.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/access/transam/xact.c,v
retrieving revision 1.170
diff -c -r1.170 xact.c
*** src/backend/access/transam/xact.c   1 Jul 2004 20:11:02 -0000       1.170
--- src/backend/access/transam/xact.c   5 Jul 2004 03:40:48 -0000
***************
*** 173,215 ****
  #include "pgstat.h"
  
  
! static void AbortTransaction(void);
! static void AtAbort_Cache(void);
! static void AtAbort_Locks(void);
! static void AtAbort_Memory(void);
! static void AtCleanup_Memory(void);
! static void AtCommit_Cache(void);
! static void AtCommit_LocalCache(void);
! static void AtCommit_Locks(void);
! static void AtCommit_Memory(void);
! static void AtStart_Cache(void);
! static void AtStart_Locks(void);
! static void AtStart_Memory(void);
! static void CallEOXactCallbacks(bool isCommit);
! static void CleanupTransaction(void);
! static void CommitTransaction(void);
! static void RecordTransactionAbort(void);
! static void StartTransaction(void);
! 
! static void RecordSubTransactionCommit(void);
! static void StartSubTransaction(void);
! static void CommitSubTransaction(void);
! static void AbortSubTransaction(void);
! static void CleanupSubTransaction(void);
! static void StartAbortedSubTransaction(void);
! static void PushTransaction(void);
! static void PopTransaction(void);
! 
! static void AtSubAbort_Locks(void);
! static void AtSubAbort_Memory(void);
! static void AtSubCleanup_Memory(void);
! static void AtSubCommit_Memory(void);
! static void AtSubStart_Memory(void);
  
! static void ShowTransactionState(const char *str);
! static void ShowTransactionStateRec(TransactionState state);
! static const char *BlockStateAsString(TBlockState blockState);
! static const char *TransStateAsString(TransState state);
  
  /*
   * CurrentTransactionState always points to the current transaction state
--- 173,234 ----
  #include "pgstat.h"
  
  
! /*
!  *    transaction states - transaction state from server perspective
!  */
! typedef enum TransState
! {
!       TRANS_DEFAULT,
!       TRANS_START,
!       TRANS_INPROGRESS,
!       TRANS_COMMIT,
!       TRANS_ABORT
! } TransState;
! 
! /*
!  *    transaction block states - transaction state of client queries
!  */
! typedef enum TBlockState
! {
!       /* not-in-transaction-block states */
!       TBLOCK_DEFAULT,
!       TBLOCK_STARTED,
! 
!       /* transaction block states */
!       TBLOCK_BEGIN,
!       TBLOCK_INPROGRESS,
!       TBLOCK_END,
!       TBLOCK_ABORT,
!       TBLOCK_ENDABORT,
! 
!       /* subtransaction states */
!       TBLOCK_SUBBEGIN,
!       TBLOCK_SUBBEGINABORT,
!       TBLOCK_SUBINPROGRESS,
!       TBLOCK_SUBEND,
!       TBLOCK_SUBABORT,
!       TBLOCK_SUBENDABORT_ALL,
!       TBLOCK_SUBENDABORT_OK,
!       TBLOCK_SUBENDABORT_ERROR
! } TBlockState;
! 
! /*
!  *    transaction state structure
!  */
! typedef struct TransactionStateData
! {
!       TransactionId   transactionIdData;              /* my XID */
!       CommandId               commandId;                              /* current CID 
*/
!       TransState              state;                                  /* low-level 
state */
!       TBlockState             blockState;                             /* high-level 
state */
!       int                             nestingLevel;                   /* nest depth 
*/
!       MemoryContext   curTransactionContext;  /* my xact-lifetime context */
!       List               *childXids;                          /* subcommitted child 
XIDs */
!       AclId                   currentUser;                    /* subxact start 
current_user */
!       struct TransactionStateData *parent;    /* back link to parent */
! } TransactionStateData;
  
! typedef TransactionStateData *TransactionState;
  
  /*
   * CurrentTransactionState always points to the current transaction state
***************
*** 270,275 ****
--- 289,333 ----
  static void *_RollbackData = NULL;
  
  
+ /* private functions declarations */
+ static void AbortTransaction(void);
+ static void AtAbort_Cache(void);
+ static void AtAbort_Locks(void);
+ static void AtAbort_Memory(void);
+ static void AtCleanup_Memory(void);
+ static void AtCommit_Cache(void);
+ static void AtCommit_LocalCache(void);
+ static void AtCommit_Locks(void);
+ static void AtCommit_Memory(void);
+ static void AtStart_Cache(void);
+ static void AtStart_Locks(void);
+ static void AtStart_Memory(void);
+ static void CallEOXactCallbacks(bool isCommit);
+ static void CleanupTransaction(void);
+ static void CommitTransaction(void);
+ static void RecordTransactionAbort(void);
+ static void StartTransaction(void);
+ 
+ static void RecordSubTransactionCommit(void);
+ static void StartSubTransaction(void);
+ static void CommitSubTransaction(void);
+ static void AbortSubTransaction(void);
+ static void CleanupSubTransaction(void);
+ static void StartAbortedSubTransaction(void);
+ static void PushTransaction(void);
+ static void PopTransaction(void);
+ 
+ static void AtSubAbort_Locks(void);
+ static void AtSubAbort_Memory(void);
+ static void AtSubCleanup_Memory(void);
+ static void AtSubCommit_Memory(void);
+ static void AtSubStart_Memory(void);
+ 
+ static void ShowTransactionState(const char *str);
+ static void ShowTransactionStateRec(TransactionState state);
+ static const char *BlockStateAsString(TBlockState blockState);
+ static const char *TransStateAsString(TransState state);
+ 
  /* ----------------------------------------------------------------
   *    transaction state accessors
   * ----------------------------------------------------------------
***************
*** 1605,1610 ****
--- 1663,1669 ----
                case TBLOCK_SUBBEGINABORT:
                case TBLOCK_END:
                case TBLOCK_SUBEND:
+               case TBLOCK_SUBENDABORT_ALL:
                case TBLOCK_SUBENDABORT_OK:
                case TBLOCK_SUBENDABORT_ERROR:
                case TBLOCK_ENDABORT:
***************
*** 1631,1641 ****
  
        switch (s->blockState)
        {
!                       /*
!                        * This shouldn't happen, because it means the previous
!                        * StartTransactionCommand didn't set the STARTED state
!                        * appropiately.
!                        */
                case TBLOCK_DEFAULT:
                        elog(FATAL, "CommitTransactionCommand: unexpected 
TBLOCK_DEFAULT");
                        break;
--- 1690,1700 ----
  
        switch (s->blockState)
        {
!               /*
!                * This shouldn't happen, because it means the previous
!                * StartTransactionCommand didn't set the STARTED state
!                * appropiately.
!                */
                case TBLOCK_DEFAULT:
                        elog(FATAL, "CommitTransactionCommand: unexpected 
TBLOCK_DEFAULT");
                        break;
***************
*** 1674,1679 ****
--- 1733,1744 ----
                         * default state.
                         */
                case TBLOCK_END:
+                       while (GetCurrentTransactionNestLevel() > 1)
+                       {
+                               CommitSubTransaction();
+                               PopTransaction();
+                               s = CurrentTransactionState;            /* changed by 
pop */
+                       }
                        CommitTransaction();
                        s->blockState = TBLOCK_DEFAULT;
                        break;
***************
*** 1698,1704 ****
                        break;
  
                        /*
!                        * We were just issued a BEGIN inside a transaction block.
                         * Start a subtransaction.  (BeginTransactionBlock already
                         * did PushTransaction, so as to have someplace to put the
                         * SUBBEGIN state.)
--- 1763,1778 ----
                        break;
  
                        /*
!                        * Ditto, but in a subtransaction.  AbortOutOfAnyTransaction
!                        * will do the dirty work.
!                        */
!               case TBLOCK_SUBENDABORT_ALL:
!                       AbortOutOfAnyTransaction();
!                       /* AbortOutOfAnyTransaction sets the blockState */
!                       break;
! 
!                       /*
!                        * We were just issued a SUBBEGIN inside a transaction block.
                         * Start a subtransaction.  (BeginTransactionBlock already
                         * did PushTransaction, so as to have someplace to put the
                         * SUBBEGIN state.)
***************
*** 1709,1715 ****
                        break;
  
                        /*
!                        * We were issued a BEGIN inside an aborted transaction block.
                         * Start a subtransaction, and put it in aborted state.
                         */
                case TBLOCK_SUBBEGINABORT:
--- 1783,1789 ----
                        break;
  
                        /*
!                        * We were issued a SUBBEGIN inside an aborted transaction 
block.
                         * Start a subtransaction, and put it in aborted state.
                         */
                case TBLOCK_SUBBEGINABORT:
***************
*** 1725,1731 ****
                        break;
  
                        /*
!                        * We were issued a COMMIT command, so we end the current
                         * subtransaction and return to the parent transaction.
                         */
                case TBLOCK_SUBEND:
--- 1799,1805 ----
                        break;
  
                        /*
!                        * We were issued a SUBCOMMIT command, so we end the current
                         * subtransaction and return to the parent transaction.
                         */
                case TBLOCK_SUBEND:
***************
*** 1741,1747 ****
                        break;
  
                        /*
!                        * We are ending an aborted subtransaction via ROLLBACK,
                         * so the parent can be allowed to live.
                         */
                case TBLOCK_SUBENDABORT_OK:
--- 1815,1821 ----
                        break;
  
                        /*
!                        * We are ending an aborted subtransaction via SUBABORT,
                         * so the parent can be allowed to live.
                         */
                case TBLOCK_SUBENDABORT_OK:
***************
*** 1751,1764 ****
                        break;
  
                        /*
!                        * We are ending an aborted subtransaction via COMMIT.
!                        * End the subtransaction, and abort the parent too.
                         */
                case TBLOCK_SUBENDABORT_ERROR:
                        CleanupSubTransaction();
                        PopTransaction();
                        s = CurrentTransactionState;            /* changed by pop */
!                       Assert(s->blockState != TBLOCK_SUBENDABORT_ERROR);
                        AbortCurrentTransaction();
                        break;
        }
--- 1825,1839 ----
                        break;
  
                        /*
!                        * We are ending an aborted subtransaction via SUBCOMMIT
!                        * without IGNORE ERRORS. End the subtransaction, and
!                        * abort the parent too.
                         */
                case TBLOCK_SUBENDABORT_ERROR:
                        CleanupSubTransaction();
                        PopTransaction();
                        s = CurrentTransactionState;            /* changed by pop */
!                       AssertState(s->blockState != TBLOCK_SUBENDABORT_ERROR);
                        AbortCurrentTransaction();
                        break;
        }
***************
*** 1889,1894 ****
--- 1964,1977 ----
                                        s->blockState != TBLOCK_SUBENDABORT_ERROR);
                        AbortCurrentTransaction();
                        break;
+ 
+                       /*
+                        * We are already aborting the whole transaction tree.
+                        * Do nothing, CommitTransactionCommand will call
+                        * AbortOutOfAnyTransaction and set things straight.
+                        */
+               case TBLOCK_SUBENDABORT_ALL:
+                       break;
        }
  }
  
***************
*** 2091,2096 ****
--- 2174,2227 ----
   */
  
  /*
+  * BeginSubTransactionBlock
+  *            This executes a SUBBEGIN command.
+  */
+ void
+ BeginSubTransactionBlock(void)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       switch (s->blockState)
+       {
+               case TBLOCK_STARTED:
+               case TBLOCK_INPROGRESS:
+               case TBLOCK_SUBINPROGRESS:
+                       /* Normal subtransaction start */
+                       PushTransaction();
+                       s = CurrentTransactionState;    /* changed by push */
+                       s->blockState = TBLOCK_SUBBEGIN;
+                       break;
+ 
+               case TBLOCK_ABORT:
+               case TBLOCK_SUBABORT:
+                       /*
+                        * An aborted transaction block should be allowed to start
+                        * a subtransaction, but it must put it in aborted state.
+                        */
+                       PushTransaction();
+                       s = CurrentTransactionState;    /* changed by push */
+                       s->blockState = TBLOCK_SUBBEGINABORT;
+                       break;
+ 
+                       /* These cases are invalid.  Reject them altogether. */
+               case TBLOCK_DEFAULT:
+               case TBLOCK_BEGIN:
+               case TBLOCK_SUBBEGIN:
+               case TBLOCK_SUBBEGINABORT:
+               case TBLOCK_ENDABORT:
+               case TBLOCK_END:
+               case TBLOCK_SUBENDABORT_ALL:
+               case TBLOCK_SUBENDABORT_OK:
+               case TBLOCK_SUBENDABORT_ERROR:
+               case TBLOCK_SUBEND:
+                       elog(FATAL, "BeginTransactionBlock: unexpected state %s",
+                                BlockStateAsString(s->blockState));
+                       break;
+       }
+ }
+ 
+ /*
   *    BeginTransactionBlock
   *            This executes a BEGIN command.
   */
***************
*** 2099,2105 ****
  {
        TransactionState s = CurrentTransactionState;
  
!       switch (s->blockState) {
                        /*
                         * We are not inside a transaction block, so allow one
                         * to begin.
--- 2230,2237 ----
  {
        TransactionState s = CurrentTransactionState;
  
!       switch (s->blockState)
!       {
                        /*
                         * We are not inside a transaction block, so allow one
                         * to begin.
***************
*** 2110,2133 ****
  
                        /*
                         * Already a transaction block in progress.
-                        * Start a subtransaction.
                         */
                case TBLOCK_INPROGRESS:
                case TBLOCK_SUBINPROGRESS:
-                       PushTransaction();
-                       s = CurrentTransactionState;            /* changed by push */
-                       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:
!                       PushTransaction();
!                       s = CurrentTransactionState;            /* changed by push */
!                       s->blockState = TBLOCK_SUBBEGINABORT;
                        break;
  
                        /* These cases are invalid.  Reject them altogether. */
--- 2242,2255 ----
  
                        /*
                         * Already a transaction block in progress.
                         */
                case TBLOCK_INPROGRESS:
                case TBLOCK_SUBINPROGRESS:
                case TBLOCK_ABORT:
                case TBLOCK_SUBABORT:
!                       ereport(WARNING,
!                                       (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
!                                        errmsg("there is already a transaction in 
progress")));
                        break;
  
                        /* These cases are invalid.  Reject them altogether. */
***************
*** 2137,2142 ****
--- 2259,2265 ----
                case TBLOCK_SUBBEGINABORT:
                case TBLOCK_ENDABORT:
                case TBLOCK_END:
+               case TBLOCK_SUBENDABORT_ALL:
                case TBLOCK_SUBENDABORT_OK:
                case TBLOCK_SUBENDABORT_ERROR:
                case TBLOCK_SUBEND:
***************
*** 2147,2152 ****
--- 2270,2348 ----
  }
  
  /*
+  * EndSubTransactionBlock
+  *            This executes a SUBEND command.
+  */
+ void
+ EndSubTransactionBlock(List *options)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       switch (s->blockState)
+       {
+               case TBLOCK_INPROGRESS:
+               case TBLOCK_ABORT:
+               case TBLOCK_STARTED:
+                       /* XXX is this the right ERRCODE? */
+                       ereport(WARNING,
+                                       (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
+                                        errmsg("there is no subtransaction in 
progress")));
+                       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 an aborted subtransaction.  Signal
+                        * CommitTransactionCommand() to clean up and return to the
+                        * parent transaction.  If the user didn't specify to ignore
+                        * the errors then make the parent abort too; else it can be
+                        * allowed to live.
+                        */
+               case TBLOCK_SUBABORT:
+                       {
+                               ListCell   *cell;
+                               bool            ignore = false;
+ 
+                               foreach (cell, options)
+                               {
+                                       DefElem *elem = lfirst(cell);
+ 
+                                       if (strcmp(elem->defname, "ignore_errors") == 
0)
+                                               ignore = true;
+                               }
+ 
+                               if (ignore)
+                                       s->blockState = TBLOCK_SUBENDABORT_OK;
+                               else
+                                       s->blockState = TBLOCK_SUBENDABORT_ERROR;
+                       }
+                       break;
+ 
+                       /* these cases are invalid. */
+               case TBLOCK_DEFAULT:
+               case TBLOCK_BEGIN:
+               case TBLOCK_ENDABORT:
+               case TBLOCK_END:
+               case TBLOCK_SUBBEGIN:
+               case TBLOCK_SUBBEGINABORT:
+               case TBLOCK_SUBEND:
+               case TBLOCK_SUBENDABORT_ALL:
+               case TBLOCK_SUBENDABORT_OK:
+               case TBLOCK_SUBENDABORT_ERROR:
+                       elog(FATAL, "EndTransactionBlock: unexpected state %s",
+                                BlockStateAsString(s->blockState));
+                       break;
+       }
+ }
+ 
+ /*
   *    EndTransactionBlock
   *            This executes a COMMIT command.
   */
***************
*** 2155,2161 ****
  {
        TransactionState s = CurrentTransactionState;
  
!       switch (s->blockState) {
                /*
                 * here we are in a transaction block which should commit when we
                 * get to the upcoming CommitTransactionCommand() so we set the
--- 2351,2358 ----
  {
        TransactionState s = CurrentTransactionState;
  
!       switch (s->blockState)
!       {
                /*
                 * here we are in a transaction block which should commit when we
                 * get to the upcoming CommitTransactionCommand() so we set the
***************
*** 2163,2178 ****
                 * and commit the transaction and return us to the default state
                 */
                case TBLOCK_INPROGRESS:
-                       s->blockState = TBLOCK_END;
-                       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;
  
                        /*
--- 2360,2367 ----
                 * and commit the transaction and return us to the default state
                 */
                case TBLOCK_INPROGRESS:
                case TBLOCK_SUBINPROGRESS:
!                       s->blockState = TBLOCK_END;
                        break;
  
                        /*
***************
*** 2187,2199 ****
                        break;
  
                        /*
!                        * here we are in an aborted subtransaction.  Signal
!                        * CommitTransactionCommand() to clean up and return to the
!                        * parent transaction.  Since the user said COMMIT, we must
!                        * fail the parent transaction.
                         */
                case TBLOCK_SUBABORT:
!                       s->blockState = TBLOCK_SUBENDABORT_ERROR;
                        break;
  
                case TBLOCK_STARTED:
--- 2376,2387 ----
                        break;
  
                        /*
!                        * Ditto, but in a subtransaction.  Go to the "abort the whole 
!                        * tree" state so that CommitTransactionCommand calls
!                        * AbortOutOfAnyTransaction.
                         */
                case TBLOCK_SUBABORT:
!                       s->blockState = TBLOCK_SUBENDABORT_ALL;
                        break;
  
                case TBLOCK_STARTED:
***************
*** 2218,2223 ****
--- 2406,2412 ----
                case TBLOCK_SUBBEGIN:
                case TBLOCK_SUBBEGINABORT:
                case TBLOCK_SUBEND:
+               case TBLOCK_SUBENDABORT_ALL:
                case TBLOCK_SUBENDABORT_OK:
                case TBLOCK_SUBENDABORT_ERROR:
                        elog(FATAL, "EndTransactionBlock: unexpected state %s",
***************
*** 2227,2232 ****
--- 2416,2470 ----
  }
  
  /*
+  * UserAbortSubTransactionBlock
+  *            This executes a SUBABORT command.
+  */
+ void
+ UserAbortSubTransactionBlock(void)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       switch (s->blockState)
+       {
+               case TBLOCK_ABORT:
+               case TBLOCK_INPROGRESS:
+               case TBLOCK_STARTED:
+                       /*
+                        * XXX is this the correct ERRCODE?
+                        * Is an ERROR ok or should it be WARNING?
+                        */
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
+                                        errmsg("there is no subtransaction in 
progress")));
+                       break;
+ 
+               case TBLOCK_SUBABORT:
+                       s->blockState = TBLOCK_SUBENDABORT_OK;
+                       break;
+ 
+               case TBLOCK_SUBINPROGRESS:
+                       AbortSubTransaction();
+                       s->blockState = TBLOCK_SUBENDABORT_OK;
+                       break;
+ 
+                       /* these cases are invalid. */
+               case TBLOCK_DEFAULT:
+               case TBLOCK_BEGIN:
+               case TBLOCK_END:
+               case TBLOCK_ENDABORT:
+               case TBLOCK_SUBEND:
+               case TBLOCK_SUBENDABORT_ALL:
+               case TBLOCK_SUBENDABORT_OK:
+               case TBLOCK_SUBENDABORT_ERROR:
+               case TBLOCK_SUBBEGIN:
+               case TBLOCK_SUBBEGINABORT:
+                       elog(FATAL, "UserAbortSubTransactionBlock: unexpected state 
%s",
+                                BlockStateAsString(s->blockState));
+                       break;
+       }
+ }
+ 
+ /*
   *    UserAbortTransactionBlock
   *            This executes a ROLLBACK command.
   */
***************
*** 2235,2241 ****
  {
        TransactionState s = CurrentTransactionState;
  
!       switch (s->blockState) {
                /*
                 * here we are inside a failed transaction block and we got an abort
                 * command from the user.  Abort processing is already done, we just
--- 2473,2480 ----
  {
        TransactionState s = CurrentTransactionState;
  
!       switch (s->blockState)
!       {
                /*
                 * here we are inside a failed transaction block and we got an abort
                 * command from the user.  Abort processing is already done, we just
***************
*** 2246,2257 ****
                        s->blockState = TBLOCK_ENDABORT;
                        break;
  
!                       /*
!                        * Ditto, for a subtransaction.  Here it is okay to allow the
!                        * parent transaction to continue.
                         */
                case TBLOCK_SUBABORT:
!                       s->blockState = TBLOCK_SUBENDABORT_OK;
                        break;
  
                        /*
--- 2485,2498 ----
                        s->blockState = TBLOCK_ENDABORT;
                        break;
  
!                       /* Here we are inside a failed subtransaction and we got
!                        * an abort command from the user.  Abort processing is already
!                        * done, so go to the "abort all"
!                        * state and CommitTransactionCommand will call
!                        * AbortOutOfAnyTransaction to set things straight.
                         */
                case TBLOCK_SUBABORT:
!                       s->blockState = TBLOCK_SUBENDABORT_ALL;
                        break;
  
                        /*
***************
*** 2268,2274 ****
                        /* Ditto, for a subtransaction. */
                case TBLOCK_SUBINPROGRESS:
                        AbortSubTransaction();
!                       s->blockState = TBLOCK_SUBENDABORT_OK;
                        break;
  
                        /*
--- 2509,2515 ----
                        /* Ditto, for a subtransaction. */
                case TBLOCK_SUBINPROGRESS:
                        AbortSubTransaction();
!                       s->blockState = TBLOCK_SUBENDABORT_ALL;
                        break;
  
                        /*
***************
*** 2291,2296 ****
--- 2532,2538 ----
                case TBLOCK_END:
                case TBLOCK_ENDABORT:
                case TBLOCK_SUBEND:
+               case TBLOCK_SUBENDABORT_ALL:
                case TBLOCK_SUBENDABORT_OK:
                case TBLOCK_SUBENDABORT_ERROR:
                case TBLOCK_SUBBEGIN:
***************
*** 2299,2305 ****
                                 BlockStateAsString(s->blockState));
                        break;
        }
- 
  }
  
  /*
--- 2541,2546 ----
***************
*** 2356,2361 ****
--- 2597,2603 ----
                                s = CurrentTransactionState;            /* changed by 
pop */
                                break;
                        case TBLOCK_SUBABORT:
+                       case TBLOCK_SUBENDABORT_ALL:
                        case TBLOCK_SUBENDABORT_OK:
                        case TBLOCK_SUBENDABORT_ERROR:
                                /* As above, but AbortSubTransaction already done */
***************
*** 2425,2430 ****
--- 2667,2673 ----
                case TBLOCK_ABORT:
                case TBLOCK_ENDABORT:
                case TBLOCK_SUBABORT:
+               case TBLOCK_SUBENDABORT_ALL:
                case TBLOCK_SUBENDABORT_OK:
                case TBLOCK_SUBENDABORT_ERROR:
                case TBLOCK_SUBBEGINABORT:
***************
*** 2445,2451 ****
  {
        TransactionState s = CurrentTransactionState;
        
!       switch (s->blockState) {
                case TBLOCK_DEFAULT:
                case TBLOCK_STARTED:
                case TBLOCK_BEGIN:
--- 2688,2695 ----
  {
        TransactionState s = CurrentTransactionState;
        
!       switch (s->blockState)
!       {
                case TBLOCK_DEFAULT:
                case TBLOCK_STARTED:
                case TBLOCK_BEGIN:
***************
*** 2459,2464 ****
--- 2703,2709 ----
                case TBLOCK_SUBINPROGRESS:
                case TBLOCK_SUBABORT:
                case TBLOCK_SUBEND:
+               case TBLOCK_SUBENDABORT_ALL:
                case TBLOCK_SUBENDABORT_OK:
                case TBLOCK_SUBENDABORT_ERROR:
                        return true;
***************
*** 2696,2701 ****
--- 2941,2949 ----
  /*
   * PushTransaction
   *            Set up transaction state for a subtransaction
+  *
+  *    The caller has to make sure to always reassign CurrentTransactionState
+  *    if it has a local pointer to it after calling this function.
   */
  static void
  PushTransaction(void)
***************
*** 2731,2736 ****
--- 2979,2987 ----
  /*
   * PopTransaction
   *            Pop back to parent transaction state
+  *
+  *    The caller has to make sure to always reassign CurrentTransactionState
+  *    if it has a local pointer to it after calling this function.
   */
  static void
  PopTransaction(void)
***************
*** 2799,2805 ****
  static const char *
  BlockStateAsString(TBlockState blockState)
  {
!       switch (blockState) {
                case TBLOCK_DEFAULT:
                        return "DEFAULT";
                case TBLOCK_STARTED:
--- 3050,3057 ----
  static const char *
  BlockStateAsString(TBlockState blockState)
  {
!       switch (blockState)
!       {
                case TBLOCK_DEFAULT:
                        return "DEFAULT";
                case TBLOCK_STARTED:
***************
*** 2824,2829 ****
--- 3076,3083 ----
                        return "SUB END";
                case TBLOCK_SUBABORT:
                        return "SUB ABORT";
+               case TBLOCK_SUBENDABORT_ALL:
+                       return "SUB ENDAB ALL";
                case TBLOCK_SUBENDABORT_OK:
                        return "SUB ENDAB OK";
                case TBLOCK_SUBENDABORT_ERROR:
***************
*** 2839,2845 ****
  static const char *
  TransStateAsString(TransState state)
  {
!       switch (state) {
                case TRANS_DEFAULT:
                        return "DEFAULT";
                case TRANS_START:
--- 3093,3100 ----
  static const char *
  TransStateAsString(TransState state)
  {
!       switch (state)
!       {
                case TRANS_DEFAULT:
                        return "DEFAULT";
                case TRANS_START:
Index: src/backend/executor/spi.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/executor/spi.c,v
retrieving revision 1.120
diff -c -r1.120 spi.c
*** src/backend/executor/spi.c  1 Jul 2004 21:17:13 -0000       1.120
--- src/backend/executor/spi.c  5 Jul 2004 03:43:50 -0000
***************
*** 103,108 ****
--- 103,109 ----
        _SPI_current->processed = 0;
        _SPI_current->tuptable = NULL;
        _SPI_current->connectXid = GetCurrentTransactionId();
+       _SPI_current->nestLevel = GetCurrentTransactionNestLevel();
  
        /*
         * Create memory contexts for this procedure
***************
*** 1181,1186 ****
--- 1182,1211 ----
                                        res = SPI_ERROR_CURSOR;
                                        goto fail;
                                }
+                               else if (IsA(queryTree->utilityStmt, TransactionStmt))
+                               {
+                                       TransactionStmt *XactStmt = (TransactionStmt 
*)queryTree->utilityStmt;
+ 
+                                       if (XactStmt->kind != TRANS_STMT_SUBBEGIN &&
+                                               XactStmt->kind != TRANS_STMT_SUBCOMMIT 
&&
+                                               XactStmt->kind != TRANS_STMT_SUBABORT)
+                                       {
+                                               res = SPI_ERROR_TRANSACTION;
+                                               goto fail;
+                                       }
+ 
+                                       /*
+                                        * Disallow closing the transaction that 
created the
+                                        * connection.
+                                        */
+                                       if ((XactStmt->kind == TRANS_STMT_SUBCOMMIT ||
+                                                               XactStmt->kind == 
TRANS_STMT_SUBABORT) &&
+                                                       _SPI_current->nestLevel >= 
GetCurrentTransactionNestLevel())
+                                       {
+                                               res = SPI_ERROR_TRANSACTION;
+                                               goto fail;
+                                       }
+                               }
                                res = SPI_OK_UTILITY;
                                if (plan == NULL)
                                {
***************
*** 1306,1311 ****
--- 1331,1361 ----
                        dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, 
NULL);
                        if (queryTree->commandType == CMD_UTILITY)
                        {
+                               if (IsA(queryTree->utilityStmt, TransactionStmt))
+                               {
+                                       TransactionStmt *XactStmt = (TransactionStmt 
*)queryTree->utilityStmt;
+ 
+                                       if (XactStmt->kind != TRANS_STMT_SUBBEGIN &&
+                                               XactStmt->kind != TRANS_STMT_SUBCOMMIT 
&&
+                                               XactStmt->kind != TRANS_STMT_SUBABORT)
+                                       {
+                                               res = SPI_ERROR_TRANSACTION;
+                                               goto fail;
+                                       }
+ 
+                                       /*
+                                        * Disallow closing the transaction that 
created the
+                                        * connection.
+                                        */
+                                       if ((XactStmt->kind == TRANS_STMT_SUBCOMMIT ||
+                                                               XactStmt->kind == 
TRANS_STMT_SUBABORT) &&
+                                                       _SPI_current->nestLevel >= 
GetCurrentTransactionNestLevel())
+                                       {
+                                               res = SPI_ERROR_TRANSACTION;
+                                               goto fail;
+                                       }
+                               }
+ 
                                ProcessUtility(queryTree->utilityStmt, dest, NULL);
                                res = SPI_OK_UTILITY;
  
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/parser/gram.y,v
retrieving revision 2.465
diff -c -r2.465 gram.y
*** src/backend/parser/gram.y   28 Jun 2004 01:19:11 -0000      2.465
--- src/backend/parser/gram.y   4 Jul 2004 18:18:28 -0000
***************
*** 222,229 ****
                                target_list update_target_list insert_column_list
                                insert_target_list def_list indirection opt_indirection
                                group_clause TriggerFuncArgs select_limit
!                               opt_select_limit opclass_item_list 
transaction_mode_list
!                               transaction_mode_list_or_empty
                                TableFuncElementList
                                prep_type_clause prep_type_list
                                execute_param_clause
--- 222,229 ----
                                target_list update_target_list insert_column_list
                                insert_target_list def_list indirection opt_indirection
                                group_clause TriggerFuncArgs select_limit
!                               opt_select_limit opclass_item_list 
transaction_subcommit_opts
!                               transaction_mode_list transaction_mode_list_or_empty
                                TableFuncElementList
                                prep_type_clause prep_type_list
                                execute_param_clause
***************
*** 348,354 ****
        DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
        DESC DISTINCT DO DOMAIN_P DOUBLE_P DROP
  
!       EACH ELSE ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
        EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
  
        FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
--- 348,354 ----
        DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
        DESC DISTINCT DO DOMAIN_P DOUBLE_P DROP
  
!       EACH ELSE ENCODING ENCRYPTED END_P ERRORS ESCAPE EXCEPT EXCLUDING
        EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
  
        FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
***************
*** 358,364 ****
  
        HANDLER HAVING HOLD HOUR_P
  
!       ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
        INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P
        INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT
        INTERVAL INTO INVOKER IS ISNULL ISOLATION
--- 358,364 ----
  
        HANDLER HAVING HOLD HOUR_P
  
!       IGNORE ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
        INDEX INHERITS INITIALLY INNER_P INOUT INPUT_P
        INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT
        INTERVAL INTO INVOKER IS ISNULL ISOLATION
***************
*** 393,399 ****
        SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
        SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
        SHOW SIMILAR SIMPLE SMALLINT SOME STABLE START STATEMENT
!       STATISTICS STDIN STDOUT STORAGE STRICT_P SUBSTRING SYSID
  
        TABLE TABLESPACE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP
        TO TOAST TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
--- 393,400 ----
        SCHEMA SCROLL SECOND_P SECURITY SELECT SEQUENCE
        SERIALIZABLE SESSION SESSION_USER SET SETOF SHARE
        SHOW SIMILAR SIMPLE SMALLINT SOME STABLE START STATEMENT
!       STATISTICS STDIN STDOUT STORAGE STRICT_P SUBABORT SUBBEGIN SUBCOMMIT
!       SUBSTRING SYSID
  
        TABLE TABLESPACE TEMP TEMPLATE TEMPORARY THEN TIME TIMESTAMP
        TO TOAST TRAILING TRANSACTION TREAT TRIGGER TRIM TRUE_P
***************
*** 3954,3959 ****
--- 3955,3981 ----
                                        n->options = NIL;
                                        $$ = (Node *)n;
                                }
+                       | SUBBEGIN opt_transaction
+                               {
+                                       TransactionStmt *n = makeNode(TransactionStmt);
+                                       n->kind = TRANS_STMT_SUBBEGIN;
+                                       n->options = NIL;
+                                       $$ = (Node *)n;
+                               }
+                       | SUBABORT opt_transaction
+                               {
+                                       TransactionStmt *n = makeNode(TransactionStmt);
+                                       n->kind = TRANS_STMT_SUBABORT;
+                                       n->options = NIL;
+                                       $$ = (Node *)n;
+                               }
+                       | SUBCOMMIT opt_transaction transaction_subcommit_opts
+                               {
+                                       TransactionStmt *n = makeNode(TransactionStmt);
+                                       n->kind = TRANS_STMT_SUBCOMMIT;
+                                       n->options = $3;
+                                       $$ = (Node *)n;
+                               }
                ;
  
  opt_transaction:      WORK                                                    {}
***************
*** 3995,4000 ****
--- 4017,4031 ----
                        | READ WRITE { $$ = FALSE; }
                ;
  
+ transaction_subcommit_opts:
+                       IGNORE ERRORS
+                               {
+                                       $$ = list_make1(makeDefElem("ignore_errors",
+                                                                                      
         makeBoolConst(true, false)));
+                               }
+                       | /* EMPTY */
+                               { $$ = NIL; }
+                       ;
  
  /*****************************************************************************
   *
***************
*** 7608,7613 ****
--- 7639,7645 ----
                        | EACH
                        | ENCODING
                        | ENCRYPTED
+                       | ERRORS
                        | ESCAPE
                        | EXCLUDING
                        | EXCLUSIVE
***************
*** 7623,7628 ****
--- 7655,7661 ----
                        | HANDLER
                        | HOLD
                        | HOUR_P
+                       | IGNORE
                        | IMMEDIATE
                        | IMMUTABLE
                        | IMPLICIT_P
***************
*** 7713,7718 ****
--- 7746,7754 ----
                        | STORAGE
                        | SYSID
                        | STRICT_P
+                       | SUBABORT
+                       | SUBBEGIN
+                       | SUBCOMMIT
                        | TABLESPACE
                        | TEMP
                        | TEMPLATE
Index: src/backend/parser/keywords.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/parser/keywords.c,v
retrieving revision 1.150
diff -c -r1.150 keywords.c
*** src/backend/parser/keywords.c       18 Jun 2004 06:13:31 -0000      1.150
--- src/backend/parser/keywords.c       4 Jul 2004 18:18:16 -0000
***************
*** 122,127 ****
--- 122,128 ----
        {"encoding", ENCODING},
        {"encrypted", ENCRYPTED},
        {"end", END_P},
+       {"errors", ERRORS},
        {"escape", ESCAPE},
        {"except", EXCEPT},
        {"excluding", EXCLUDING},
***************
*** 150,155 ****
--- 151,157 ----
        {"having", HAVING},
        {"hold", HOLD},
        {"hour", HOUR_P},
+       {"ignore", IGNORE},
        {"ilike", ILIKE},
        {"immediate", IMMEDIATE},
        {"immutable", IMMUTABLE},
***************
*** 294,299 ****
--- 296,304 ----
        {"stdout", STDOUT},
        {"storage", STORAGE},
        {"strict", STRICT_P},
+       {"subabort", SUBABORT},
+       {"subbegin", SUBBEGIN},
+       {"subcommit", SUBCOMMIT},
        {"substring", SUBSTRING},
        {"sysid", SYSID},
        {"table", TABLE},
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/tcop/postgres.c,v
retrieving revision 1.422
diff -c -r1.422 postgres.c
*** src/backend/tcop/postgres.c 1 Jul 2004 00:51:11 -0000       1.422
--- src/backend/tcop/postgres.c 4 Jul 2004 00:03:41 -0000
***************
*** 841,847 ****
                                TransactionStmt *stmt = (TransactionStmt *) parsetree;
  
                                if (stmt->kind == TRANS_STMT_COMMIT ||
!                                       stmt->kind == TRANS_STMT_BEGIN ||
                                        stmt->kind == TRANS_STMT_ROLLBACK)
                                        allowit = true;
                        }
--- 841,849 ----
                                TransactionStmt *stmt = (TransactionStmt *) parsetree;
  
                                if (stmt->kind == TRANS_STMT_COMMIT ||
!                                       stmt->kind == TRANS_STMT_SUBBEGIN ||
!                                       stmt->kind == TRANS_STMT_SUBCOMMIT ||
!                                       stmt->kind == TRANS_STMT_SUBABORT ||
                                        stmt->kind == TRANS_STMT_ROLLBACK)
                                        allowit = true;
                        }
***************
*** 1162,1168 ****
                                TransactionStmt *stmt = (TransactionStmt *) parsetree;
  
                                if (stmt->kind == TRANS_STMT_COMMIT ||
!                                       stmt->kind == TRANS_STMT_BEGIN ||
                                        stmt->kind == TRANS_STMT_ROLLBACK)
                                        allowit = true;
                        }
--- 1164,1172 ----
                                TransactionStmt *stmt = (TransactionStmt *) parsetree;
  
                                if (stmt->kind == TRANS_STMT_COMMIT ||
!                                       stmt->kind == TRANS_STMT_SUBBEGIN ||
!                                       stmt->kind == TRANS_STMT_SUBCOMMIT ||
!                                       stmt->kind == TRANS_STMT_SUBABORT ||
                                        stmt->kind == TRANS_STMT_ROLLBACK)
                                        allowit = true;
                        }
***************
*** 1625,1631 ****
  
                        is_trans_stmt = true;
                        if (stmt->kind == TRANS_STMT_COMMIT ||
!                               stmt->kind == TRANS_STMT_BEGIN ||
                                stmt->kind == TRANS_STMT_ROLLBACK)
                                is_trans_exit = true;
                }
--- 1629,1637 ----
  
                        is_trans_stmt = true;
                        if (stmt->kind == TRANS_STMT_COMMIT ||
!                               stmt->kind == TRANS_STMT_SUBBEGIN ||
!                               stmt->kind == TRANS_STMT_SUBCOMMIT ||
!                               stmt->kind == TRANS_STMT_SUBABORT ||
                                stmt->kind == TRANS_STMT_ROLLBACK)
                                is_trans_exit = true;
                }
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/tcop/utility.c,v
retrieving revision 1.220
diff -c -r1.220 utility.c
*** src/backend/tcop/utility.c  25 Jun 2004 21:55:57 -0000      1.220
--- src/backend/tcop/utility.c  4 Jul 2004 20:48:11 -0000
***************
*** 360,365 ****
--- 360,378 ----
                                        case TRANS_STMT_ROLLBACK:
                                                UserAbortTransactionBlock();
                                                break;
+ 
+                                       case TRANS_STMT_SUBBEGIN:
+                                               BeginSubTransactionBlock();
+                                               break;
+ 
+                                       case TRANS_STMT_SUBABORT:
+                                               UserAbortSubTransactionBlock();
+                                               break;
+ 
+                                               /* Let EndSubTransactionBlock parse 
the options list */
+                                       case TRANS_STMT_SUBCOMMIT:
+                                               EndSubTransactionBlock(stmt->options);
+                                               break;
                                }
                        }
                        break;
***************
*** 1117,1122 ****
--- 1130,1147 ----
                                                tag = "ROLLBACK";
                                                break;
  
+                                       case TRANS_STMT_SUBBEGIN:
+                                               tag = "SUBBEGIN";
+                                               break;
+ 
+                                       case TRANS_STMT_SUBABORT:
+                                               tag = "SUBABORT";
+                                               break;
+ 
+                                       case TRANS_STMT_SUBCOMMIT:
+                                               tag = "SUBCOMMIT";
+                                               break;
+ 
                                        default:
                                                tag = "???";
                                                break;
Index: src/backend/utils/mmgr/portalmem.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/mmgr/portalmem.c,v
retrieving revision 1.66
diff -c -r1.66 portalmem.c
*** src/backend/utils/mmgr/portalmem.c  1 Jul 2004 00:51:29 -0000       1.66
--- src/backend/utils/mmgr/portalmem.c  4 Jul 2004 05:48:12 -0000
***************
*** 302,308 ****
  
        /* Not sure if this case can validly happen or not... */
        if (portal->portalActive)
!               elog(ERROR, "cannot drop active portal");
  
        /*
         * Remove portal from hash table.  Because we do this first, we will
--- 302,308 ----
  
        /* Not sure if this case can validly happen or not... */
        if (portal->portalActive)
!               elog(FATAL, "cannot drop active portal");
  
        /*
         * Remove portal from hash table.  Because we do this first, we will
Index: src/include/access/xact.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/access/xact.h,v
retrieving revision 1.64
diff -c -r1.64 xact.h
*** src/include/access/xact.h   1 Jul 2004 00:51:38 -0000       1.64
--- src/include/access/xact.h   5 Jul 2004 03:40:30 -0000
***************
*** 41,103 ****
  extern bool XactReadOnly;
  
  /*
-  *    transaction states - transaction state from server perspective
-  */
- typedef enum TransState
- {
-       TRANS_DEFAULT,
-       TRANS_START,
-       TRANS_INPROGRESS,
-       TRANS_COMMIT,
-       TRANS_ABORT
- } TransState;
- 
- /*
-  *    transaction block states - transaction state of client queries
-  */
- typedef enum TBlockState
- {
-       TBLOCK_DEFAULT,
-       TBLOCK_STARTED,
-       TBLOCK_BEGIN,
-       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;
- 
- /*
   *    end-of-transaction cleanup callbacks for dynamically loaded modules
   */
  typedef void (*EOXactCallback) (bool isCommit, void *arg);
  
- /*
-  *    transaction state structure
-  */
- typedef struct TransactionStateData
- {
-       TransactionId   transactionIdData;              /* my XID */
-       CommandId               commandId;                              /* current CID 
*/
-       TransState              state;                                  /* low-level 
state */
-       TBlockState             blockState;                             /* high-level 
state */
-       int                             nestingLevel;                   /* nest depth 
*/
-       MemoryContext   curTransactionContext;  /* my xact-lifetime context */
-       List               *childXids;                          /* subcommitted child 
XIDs */
-       AclId                   currentUser;                    /* subxact start 
current_user */
-       struct TransactionStateData *parent;    /* back link to parent */
- } TransactionStateData;
- 
- typedef TransactionStateData *TransactionState;
- 
- 
  /* ----------------
   *            transaction-related XLOG entries
   * ----------------
--- 41,50 ----
***************
*** 152,163 ****
--- 99,113 ----
  extern void StartTransactionCommand(void);
  extern void CommitTransactionCommand(void);
  extern void AbortCurrentTransaction(void);
+ extern void BeginSubTransactionBlock(void);
  extern void BeginTransactionBlock(void);
+ extern void EndSubTransactionBlock(List *options);
  extern void EndTransactionBlock(void);
  extern bool IsSubTransaction(void);
  extern bool IsTransactionBlock(void);
  extern bool IsTransactionOrTransactionBlock(void);
  extern char TransactionBlockStatusCode(void);
+ extern void UserAbortSubTransactionBlock(void);
  extern void UserAbortTransactionBlock(void);
  extern void AbortOutOfAnyTransaction(void);
  extern void PreventTransactionChain(void *stmtNode, const char *stmtType);
Index: src/include/executor/spi_priv.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/executor/spi_priv.h,v
retrieving revision 1.19
diff -c -r1.19 spi_priv.h
*** src/include/executor/spi_priv.h     1 Jul 2004 00:51:42 -0000       1.19
--- src/include/executor/spi_priv.h     4 Jul 2004 21:40:25 -0000
***************
*** 24,29 ****
--- 24,30 ----
        MemoryContext execCxt;          /* executor context */
        MemoryContext savedcxt;
        TransactionId connectXid;       /* Xid of connecting transaction */
+       int                       nestLevel;    /* nesting level of connecting 
transaction */
  } _SPI_connection;
  
  typedef struct
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/nodes/parsenodes.h,v
retrieving revision 1.260
diff -c -r1.260 parsenodes.h
*** src/include/nodes/parsenodes.h      25 Jun 2004 21:55:59 -0000      1.260
--- src/include/nodes/parsenodes.h      3 Jul 2004 21:02:18 -0000
***************
*** 1514,1527 ****
        TRANS_STMT_BEGIN,
        TRANS_STMT_START,                       /* semantically identical to BEGIN */
        TRANS_STMT_COMMIT,
!       TRANS_STMT_ROLLBACK
  } TransactionStmtKind;
  
  typedef struct TransactionStmt
  {
        NodeTag         type;
        TransactionStmtKind kind;       /* see above */
!       List       *options;            /* for BEGIN/START only */
  } TransactionStmt;
  
  /* ----------------------
--- 1514,1530 ----
        TRANS_STMT_BEGIN,
        TRANS_STMT_START,                       /* semantically identical to BEGIN */
        TRANS_STMT_COMMIT,
!       TRANS_STMT_ROLLBACK,
!       TRANS_STMT_SUBBEGIN,
!       TRANS_STMT_SUBCOMMIT,
!       TRANS_STMT_SUBABORT
  } TransactionStmtKind;
  
  typedef struct TransactionStmt
  {
        NodeTag         type;
        TransactionStmtKind kind;       /* see above */
!       List       *options;            /* for BEGIN/START/SUBBEGIN only */
  } TransactionStmt;
  
  /* ----------------------
Index: src/test/regress/expected/transactions.out
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/test/regress/expected/transactions.out,v
retrieving revision 1.5
diff -c -r1.5 transactions.out
*** src/test/regress/expected/transactions.out  1 Jul 2004 20:11:02 -0000       1.5
--- src/test/regress/expected/transactions.out  4 Jul 2004 21:23:23 -0000
***************
*** 74,86 ****
  CREATE TABLE foobar (a int);
  BEGIN;
        CREATE TABLE foo (a int);
!       BEGIN;
                DROP TABLE foo;
                CREATE TABLE bar (a int);
!       ROLLBACK;
!       BEGIN;
                CREATE TABLE baz (a int);
!       COMMIT;
        drop TABLE foobar;
        CREATE TABLE barbaz (a int);
  COMMIT;
--- 74,86 ----
  CREATE TABLE foobar (a int);
  BEGIN;
        CREATE TABLE foo (a int);
!       SUBBEGIN;
                DROP TABLE foo;
                CREATE TABLE bar (a int);
!       SUBABORT;
!       SUBBEGIN;
                CREATE TABLE baz (a int);
!       SUBCOMMIT;
        drop TABLE foobar;
        CREATE TABLE barbaz (a int);
  COMMIT;
***************
*** 105,122 ****
  -- inserts
  BEGIN;
        INSERT INTO foo VALUES (1);
!       BEGIN;
                INSERT into bar VALUES (1);
  ERROR:  relation "bar" does not exist
!       ROLLBACK;
!       BEGIN;
                INSERT into barbaz VALUES (1);
!       COMMIT;
!       BEGIN;
!               BEGIN;
                        INSERT INTO foo VALUES (2);
!               COMMIT;
!       ROLLBACK;
        INSERT INTO foo VALUES (3);
  COMMIT;
  SELECT * FROM foo;            -- should have 1 and 3
--- 105,122 ----
  -- inserts
  BEGIN;
        INSERT INTO foo VALUES (1);
!       SUBBEGIN;
                INSERT into bar VALUES (1);
  ERROR:  relation "bar" does not exist
!       SUBABORT;
!       SUBBEGIN;
                INSERT into barbaz VALUES (1);
!       SUBCOMMIT;
!       SUBBEGIN;
!               SUBBEGIN;
                        INSERT INTO foo VALUES (2);
!               SUBCOMMIT;
!       SUBABORT;
        INSERT INTO foo VALUES (3);
  COMMIT;
  SELECT * FROM foo;            -- should have 1 and 3
***************
*** 136,151 ****
  BEGIN;
        SELECT 0/0;             -- fail the outer xact
  ERROR:  division by zero
!       BEGIN;
                SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!       COMMIT;
        SELECT 1;               -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!       BEGIN;
                SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!       ROLLBACK;
        SELECT 1;               -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
  COMMIT;
--- 136,151 ----
  BEGIN;
        SELECT 0/0;             -- fail the outer xact
  ERROR:  division by zero
!       SUBBEGIN;
                SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!       SUBCOMMIT;
        SELECT 1;               -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!       SUBBEGIN;
                SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!       SUBABORT;
        SELECT 1;               -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
  COMMIT;
***************
*** 156,162 ****
  (1 row)
  
  BEGIN;
!       BEGIN;
                SELECT 1;       -- this should work
   ?column? 
  ----------
--- 156,162 ----
  (1 row)
  
  BEGIN;
!       SUBBEGIN;
                SELECT 1;       -- this should work
   ?column? 
  ----------
***************
*** 167,183 ****
  ERROR:  division by zero
                SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!               BEGIN;
                        SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!               ROLLBACK;
!               BEGIN;
                        SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!               COMMIT;
                SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!       ROLLBACK;
        SELECT 1;               -- this should work
   ?column? 
  ----------
--- 167,183 ----
  ERROR:  division by zero
                SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!               SUBBEGIN;
                        SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!               SUBABORT;
!               SUBBEGIN;
                        SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!               SUBCOMMIT;
                SELECT 1;       -- this should NOT work
  ERROR:  current transaction is aborted, commands ignored until end of transaction 
block
!       SUBABORT;
        SELECT 1;               -- this should work
   ?column? 
  ----------
***************
*** 191,196 ****
--- 191,255 ----
          1
  (1 row)
  
+ -- test "subcommit ignore errors" and whole-tree commit
+ BEGIN;
+       SUBBEGIN;
+               SELECT foo;
+ ERROR:  column "foo" does not exist
+       SUBCOMMIT IGNORE ERRORS;
+       SUBBEGIN;
+               CREATE TABLE bazbaz (a int);
+               SUBBEGIN;
+                       INSERT INTO bazbaz VALUES (1);
+                       SUBBEGIN;
+                               INSERT INTO bazbaz VALUES (2);
+                               SUBBEGIN;
+                                       INSERT INTO bazbaz VALUES (3);
+                               SUBABORT;
+ COMMIT;
+ COMMIT;               -- should not be in a transaction block
+ WARNING:  there is no transaction in progress
+ SELECT * FROM bazbaz;
+  a 
+ ---
+  1
+  2
+ (2 rows)
+ 
+ -- test whole-tree rollback
+ BEGIN;
+       SUBBEGIN;
+               DELETE FROM bazbaz WHERE a=1;
+       SUBCOMMIT;
+       SUBBEGIN;
+               DELETE FROM bazbaz WHERE a=1;
+               SUBBEGIN;
+                       DELETE FROM bazbaz WHERE a=2;
+ ROLLBACK;
+               
+ SELECT * FROM bazbaz;
+  a 
+ ---
+  1
+  2
+ (2 rows)
+ 
+ -- test whole-tree commit on an aborted subtransaction
+ BEGIN;
+   INSERT INTO bazbaz VALUES (1);
+   SUBBEGIN;
+     INSERT INTO bazbaz VALUES (2);
+     SELECT foo;
+ ERROR:  column "foo" does not exist
+ COMMIT;
+ SELECT * FROM bazbaz;
+  a 
+ ---
+  1
+  2
+ (2 rows)
+ 
  DROP TABLE foo;
  DROP TABLE baz;
  DROP TABLE barbaz;
+ DROP TABLE bazbaz;
Index: src/test/regress/sql/transactions.sql
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/test/regress/sql/transactions.sql,v
retrieving revision 1.5
diff -c -r1.5 transactions.sql
*** src/test/regress/sql/transactions.sql       1 Jul 2004 20:11:03 -0000       1.5
--- src/test/regress/sql/transactions.sql       4 Jul 2004 21:21:43 -0000
***************
*** 61,73 ****
  CREATE TABLE foobar (a int);
  BEGIN;
        CREATE TABLE foo (a int);
!       BEGIN;
                DROP TABLE foo;
                CREATE TABLE bar (a int);
!       ROLLBACK;
!       BEGIN;
                CREATE TABLE baz (a int);
!       COMMIT;
        drop TABLE foobar;
        CREATE TABLE barbaz (a int);
  COMMIT;
--- 61,73 ----
  CREATE TABLE foobar (a int);
  BEGIN;
        CREATE TABLE foo (a int);
!       SUBBEGIN;
                DROP TABLE foo;
                CREATE TABLE bar (a int);
!       SUBABORT;
!       SUBBEGIN;
                CREATE TABLE baz (a int);
!       SUBCOMMIT;
        drop TABLE foobar;
        CREATE TABLE barbaz (a int);
  COMMIT;
***************
*** 80,96 ****
  -- inserts
  BEGIN;
        INSERT INTO foo VALUES (1);
!       BEGIN;
                INSERT into bar VALUES (1);
!       ROLLBACK;
!       BEGIN;
                INSERT into barbaz VALUES (1);
!       COMMIT;
!       BEGIN;
!               BEGIN;
                        INSERT INTO foo VALUES (2);
!               COMMIT;
!       ROLLBACK;
        INSERT INTO foo VALUES (3);
  COMMIT;
  SELECT * FROM foo;            -- should have 1 and 3
--- 80,96 ----
  -- inserts
  BEGIN;
        INSERT INTO foo VALUES (1);
!       SUBBEGIN;
                INSERT into bar VALUES (1);
!       SUBABORT;
!       SUBBEGIN;
                INSERT into barbaz VALUES (1);
!       SUBCOMMIT;
!       SUBBEGIN;
!               SUBBEGIN;
                        INSERT INTO foo VALUES (2);
!               SUBCOMMIT;
!       SUBABORT;
        INSERT INTO foo VALUES (3);
  COMMIT;
  SELECT * FROM foo;            -- should have 1 and 3
***************
*** 99,133 ****
  -- check that starting a subxact in a failed xact or subxact works
  BEGIN;
        SELECT 0/0;             -- fail the outer xact
!       BEGIN;
                SELECT 1;       -- this should NOT work
!       COMMIT;
        SELECT 1;               -- this should NOT work
!       BEGIN;
                SELECT 1;       -- this should NOT work
!       ROLLBACK;
        SELECT 1;               -- this should NOT work
  COMMIT;
  SELECT 1;                     -- this should work
  
  BEGIN;
!       BEGIN;
                SELECT 1;       -- this should work
                SELECT 0/0;     -- fail the subxact
                SELECT 1;       -- this should NOT work
!               BEGIN;
                        SELECT 1;       -- this should NOT work
!               ROLLBACK;
!               BEGIN;
                        SELECT 1;       -- this should NOT work
!               COMMIT;
                SELECT 1;       -- this should NOT work
!       ROLLBACK;
        SELECT 1;               -- this should work
  COMMIT;
  SELECT 1;                     -- this should work
  
  
  DROP TABLE foo;
  DROP TABLE baz;
  DROP TABLE barbaz;
--- 99,173 ----
  -- check that starting a subxact in a failed xact or subxact works
  BEGIN;
        SELECT 0/0;             -- fail the outer xact
!       SUBBEGIN;
                SELECT 1;       -- this should NOT work
!       SUBCOMMIT;
        SELECT 1;               -- this should NOT work
!       SUBBEGIN;
                SELECT 1;       -- this should NOT work
!       SUBABORT;
        SELECT 1;               -- this should NOT work
  COMMIT;
  SELECT 1;                     -- this should work
  
  BEGIN;
!       SUBBEGIN;
                SELECT 1;       -- this should work
                SELECT 0/0;     -- fail the subxact
                SELECT 1;       -- this should NOT work
!               SUBBEGIN;
                        SELECT 1;       -- this should NOT work
!               SUBABORT;
!               SUBBEGIN;
                        SELECT 1;       -- this should NOT work
!               SUBCOMMIT;
                SELECT 1;       -- this should NOT work
!       SUBABORT;
        SELECT 1;               -- this should work
  COMMIT;
  SELECT 1;                     -- this should work
  
+ -- test "subcommit ignore errors" and whole-tree commit
+ BEGIN;
+       SUBBEGIN;
+               SELECT foo;
+       SUBCOMMIT IGNORE ERRORS;
+       SUBBEGIN;
+               CREATE TABLE bazbaz (a int);
+               SUBBEGIN;
+                       INSERT INTO bazbaz VALUES (1);
+                       SUBBEGIN;
+                               INSERT INTO bazbaz VALUES (2);
+                               SUBBEGIN;
+                                       INSERT INTO bazbaz VALUES (3);
+                               SUBABORT;
+ COMMIT;
+ COMMIT;               -- should not be in a transaction block
+ SELECT * FROM bazbaz;
+ 
+ -- test whole-tree rollback
+ BEGIN;
+       SUBBEGIN;
+               DELETE FROM bazbaz WHERE a=1;
+       SUBCOMMIT;
+       SUBBEGIN;
+               DELETE FROM bazbaz WHERE a=1;
+               SUBBEGIN;
+                       DELETE FROM bazbaz WHERE a=2;
+ ROLLBACK;
+               
+ SELECT * FROM bazbaz;
+ 
+ -- test whole-tree commit on an aborted subtransaction
+ BEGIN;
+   INSERT INTO bazbaz VALUES (1);
+   SUBBEGIN;
+     INSERT INTO bazbaz VALUES (2);
+     SELECT foo;
+ COMMIT;
+ SELECT * FROM bazbaz;
  
  DROP TABLE foo;
  DROP TABLE baz;
  DROP TABLE barbaz;
+ DROP TABLE bazbaz;
---------------------------(end of broadcast)---------------------------
TIP 8: explain analyze is your friend

Reply via email to