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