I wrote ten seconds ago:

> This version does.  This patch includes both patches I
> posted and a few more changes, and does the following:

I mean this one.

-- 
Alvaro Herrera (<alvherre[a]dcc.uchile.cl>)
"¿Qué importan los años?  Lo que realmente importa es comprobar que
a fin de cuentas la mejor edad de la vida es estar vivo"  (Mafalda)
Index: src/backend/access/transam/xact.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/access/transam/xact.c,v
retrieving revision 1.165
diff -c -r1.165 xact.c
*** src/backend/access/transam/xact.c   5 Apr 2004 03:11:39 -0000       1.165
--- src/backend/access/transam/xact.c   26 Apr 2004 13:25:30 -0000
***************
*** 189,194 ****
--- 189,227 ----
  static void RecordTransactionAbort(void);
  static void StartTransaction(void);
  
+ #ifdef SUBTRANSACTIONS
+ static void StartSubTransaction(void);
+ static void CommitSubTransaction(void);
+ static void AbortSubTransaction(void);
+ static void CleanupSubTransaction(void);
+ static void PushCurrentTransaction(void);
+ static void PopTransaction(void);
+ static bool IsSubTransaction(void);
+ static void ShowTransactionState(char *);
+ static void ShowTransactionStateRec(TransactionState, int);
+ 
+ static void AtSubStart_Memory(void);
+ static void AtSubCommit_Memory(void);
+ static void AtSubAbort_Memory(void);
+ static void AtSubCleanup_Memory(void);
+ static void AtSubCommit_Notifies(void);
+ 
+ static char *TransStateAsString(TransState);
+ 
+ #else /* SUBTRANSACTIONS */
+ #define StartSubTransaction()
+ #define CommitSubTransaction()
+ #define AbortSubTransaction()
+ #define CleanupSubTransaction()
+ #define PushCurrentTransaction()
+ #define PopTransaction()
+ #define IsSubTransaction() (false)
+ #define ShowTransactionState(foo)
+ #define ShowTransactionStateRec()
+ #endif /* SUBTRANSACTIONS */
+ 
+ static char *BlockStateAsString(TBlockState);
+ 
  /*
   *    global variables holding the current transaction state.
   */
***************
*** 198,205 ****
        0,                                                      /* scan command id */
        0x0,                                            /* start time */
        TRANS_DEFAULT,                          /* transaction state */
!       TBLOCK_DEFAULT                          /* transaction block state from the 
client
                                                                 * perspective */
  };
  
  static TransactionState CurrentTransactionState = &CurrentTransactionStateData;
--- 231,251 ----
        0,                                                      /* scan command id */
        0x0,                                            /* start time */
        TRANS_DEFAULT,                          /* transaction state */
!       TBLOCK_DEFAULT,                         /* transaction block state from the 
client
                                                                 * perspective */
+ #ifdef SUBTRANSACTIONS
+       0,                                                      /* nesting level */
+       NULL,                                           /* commit memory context */
+       NULL,                                           /* deferred trigger queue */
+ #endif /* SUBTRANSACTIONS */
+       NIL,                                            /* smgr pending deletes */
+       NIL                                                     /* async notifies */
+ #ifdef SUBTRANSACTIONS
+       ,
+       NULL,                                           /* lightweight locks */
+       NIL,                                            /* regular locks */
+       NULL                                            /* parent transaction */
+ #endif
  };
  
  static TransactionState CurrentTransactionState = &CurrentTransactionStateData;
***************
*** 281,287 ****
  {
        TransactionState s = CurrentTransactionState;
  
!       if (s->blockState == TBLOCK_ABORT)
                return true;
  
        return false;
--- 327,334 ----
  {
        TransactionState s = CurrentTransactionState;
  
!       if (s->blockState == TBLOCK_ABORT || 
!                       s->blockState == TBLOCK_SUBABORT)
                return true;
  
        return false;
***************
*** 337,342 ****
--- 384,484 ----
        return s->startTime;
  }
  
+ /*
+  * TransactionGetPendingDeletes
+  */
+ List *
+ TransactionGetPendingDeletes(void)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       return s->pendingDeletes;
+ }
+ 
+ /*
+  * TransactionSetPendingDeletes
+  */
+ void
+ TransactionSetPendingDeletes(List *pending)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       s->pendingDeletes = pending;
+ }
+ 
+ #ifdef SUBTRANSACTIONS
+ /*
+  * TransactionGetParentPendingDeletes
+  */
+ List *
+ TransactionGetParentPendingDeletes(void)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       Assert(s->parent != NULL);
+       return s->parent->pendingDeletes;
+ }
+ 
+ /*
+  * TransactionSetParentPendingDeletes
+  */
+ void
+ TransactionSetParentPendingDeletes(List *pending)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       Assert(s->parent != NULL);
+       s->parent->pendingDeletes = pending;
+ }
+ #endif /* SUBTRANSACTIONS */
+ 
+ /*
+  * TransactionGetNotifies
+  */
+ List *
+ TransactionGetNotifies(void)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       return s->notifies;
+ }
+ 
+ /*
+  * TransactionSetNotifies
+  */
+ void
+ TransactionSetNotifies(List *notifies)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       s->notifies = notifies;
+ }
+ 
+ #ifdef SUBTRANSACTIONS
+ /*
+  * TransactionGetParentNotifies
+  */
+ List *
+ TransactionGetParentNotifies(void)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       Assert(s->parent != NULL);
+       return s->parent->notifies;
+ }
+ 
+ /*
+  * TransactionSetParentNotifies
+  */
+ void
+ TransactionSetParentNotifies(List *notifies)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       Assert(s->parent != NULL);
+       s->parent->notifies = notifies;
+ }
+ #endif /* SUBTRANSACTIONS */
  
  /*
   *    TransactionIdIsCurrentTransactionId
***************
*** 436,441 ****
--- 578,587 ----
  static void
  AtStart_Memory(void)
  {
+ #ifdef SUBTRANSACTIONS
+       TransactionState s = CurrentTransactionState;
+ #endif /* SUBTRANSACTIONS */
+ 
        /*
         * We shouldn't have a transaction context already.
         */
***************
*** 452,459 ****
--- 598,652 ----
                                                          ALLOCSET_DEFAULT_MAXSIZE);
  
        MemoryContextSwitchTo(TopTransactionContext);
+ 
+       /*
+        * This context will be used to hold data that survives
+        * subtransaction commit but disappears on subtransaction end.
+        * Most if not all of the transaction-local structures should
+        * live here.
+        */
+       CommitContext = AllocSetContextCreate(TopTransactionContext,
+                                                                                 
"CommitContext",
+                                                                                 
ALLOCSET_DEFAULT_MINSIZE,
+                                                                                 
ALLOCSET_DEFAULT_INITSIZE,
+                                                                                 
ALLOCSET_DEFAULT_MAXSIZE);
+ 
+ #ifdef SUBTRANSACTIONS
+       s->commitContext = CommitContext;
+ #endif /* SUBTRANSACTIONS */
  }
  
+ #ifdef SUBTRANSACTIONS
+ /* ----------------------------------------------------------------
+  *                                            StartSubTransaction stuff
+  * ----------------------------------------------------------------
+  */
+ 
+ /*
+  * AtSubStart_Memory
+  */
+ static void
+ AtSubStart_Memory(void)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       elog(DEBUG2, "AtSubStart_Memory");
+       Assert(TopTransactionContext != NULL);
+ 
+       /*
+        * This context will be used to hold data that survives
+        * subtransaction commit but disappears on subtransaction end.
+        * Most if not all of the transaction-local structures should
+        * live here.
+        */
+       CommitContext = AllocSetContextCreate(TopTransactionContext,
+                                                                                 
"CommitContext",
+                                                                                 
ALLOCSET_DEFAULT_MINSIZE,
+                                                                                 
ALLOCSET_DEFAULT_INITSIZE,
+                                                                                 
ALLOCSET_DEFAULT_MAXSIZE);
+       s->commitContext = CommitContext;
+ }
+ #endif /* SUBTRANSACTIONS */
  
  /* ----------------------------------------------------------------
   *                                            CommitTransaction stuff
***************
*** 637,644 ****
--- 830,893 ----
        Assert(TopTransactionContext != NULL);
        MemoryContextDelete(TopTransactionContext);
        TopTransactionContext = NULL;
+       CommitContext = NULL;
  }
  
+ #ifdef SUBTRANSACTIONS
+ /* ----------------------------------------------------------------
+  *                                            StartSubTransaction stuff
+  * ----------------------------------------------------------------
+  */
+ 
+ /*
+  * AtSubCommit_Memory
+  */
+ static void
+ AtSubCommit_Memory(void)
+ {
+       elog(DEBUG2, "AtSubCommit_Memory");
+       Assert(TopTransactionContext != NULL);
+ 
+       /* Return to a known-good memory context. */
+       MemoryContextSwitchTo(TopTransactionContext);
+ }
+ 
+ /*
+  * AtSubCommit_childXids
+  *
+  * Save the child XIDs in the transaction state so we can mark them
+  * aborted in pg_clog if their parent aborts.
+  */
+ static void
+ AtSubCommit_childXids(void)
+ {
+       TransactionState s = CurrentTransactionState;
+       MemoryContext old_cxt;
+ 
+       old_cxt = MemoryContextSwitchTo(TopTransactionContext);
+ 
+       s->parent->childXids =
+               lconsi(s->transactionIdData,
+                          nconc(s->parent->childXids, s->childXids));
+       
+       MemoryContextSwitchTo(old_cxt);
+ }
+ 
+ /*
+  * AtSubCommit_Notifies
+  *
+  * Reassign the pending notifies to the parent transaction.
+  */
+ static void
+ AtSubCommit_Notifies(void)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       s->parent->notifies = s->notifies;
+ }
+ 
+ #endif /* SUBTRANSACTIONS */
+ 
  /* ----------------------------------------------------------------
   *                                            AbortTransaction stuff
   * ----------------------------------------------------------------
***************
*** 778,783 ****
--- 1027,1044 ----
                MemoryContextSwitchTo(TopMemoryContext);
  }
  
+ #ifdef SUBTRANSACTIONS
+ /*
+  * AtSubAbort_Memory
+  */
+ static void
+ AtSubAbort_Memory(void)
+ {
+       Assert(TopTransactionContext != NULL);
+ 
+       MemoryContextSwitchTo(TopTransactionContext);
+ }
+ #endif
  
  /* ----------------------------------------------------------------
   *                                            CleanupTransaction stuff
***************
*** 806,811 ****
--- 1067,1100 ----
  }
  
  
+ #ifdef SUBTRANSACTIONS
+ /* ----------------------------------------------------------------
+  *                                            CleanupSubTransaction stuff
+  * ----------------------------------------------------------------
+  */
+ 
+ /*
+  * AtSubCleanup_Memory
+  */
+ static void
+ AtSubCleanup_Memory(void)
+ {
+       elog(DEBUG2, "AtSubEnd_Memory");
+       Assert(TopTransactionContext != NULL);
+ 
+       /* Return to a known-good memory context. */
+       MemoryContextSwitchTo(TopTransactionContext);
+ 
+       /*
+        * Delete the subxact local memory contexts. The CommitContext
+        * can go too.
+        */
+       MemoryContextDelete(CommitContext);
+ 
+       CommitContext = NULL;
+ }
+ #endif /* SUBTRANSACTIONS */
+ 
  /* ----------------------------------------------------------------
   *                                            interface routines
   * ----------------------------------------------------------------
***************
*** 851,856 ****
--- 1140,1149 ----
        s->commandId = FirstCommandId;
        s->startTime = GetCurrentAbsoluteTimeUsec(&(s->startTimeUsec));
  
+ #ifdef SUBTRANSACTIONS
+       s->childXids = NIL;
+ #endif
+ 
        /*
         * initialize the various transaction subsystems
         */
***************
*** 868,873 ****
--- 1161,1167 ----
         * progress"
         */
        s->state = TRANS_INPROGRESS;
+       ShowTransactionState("StartTransaction");
  
  }
  
***************
*** 879,884 ****
--- 1173,1180 ----
  {
        TransactionState s = CurrentTransactionState;
  
+       ShowTransactionState("CommitTransaction");
+ 
        /*
         * check the current transaction state
         */
***************
*** 1145,1189 ****
                        break;
  
                        /*
-                        * We should never experience this -- it means the STARTED 
state
-                        * was not changed in the previous CommitTransactionCommand.
-                        */
-               case TBLOCK_STARTED:
-                       elog(WARNING, "StartTransactionCommand: unexpected 
TBLOCK_STARTED");
-                       break;
- 
-                       /*
-                        * We should never experience this -- if we do it means the
-                        * BEGIN state was not changed in the previous
-                        * CommitTransactionCommand().  If we get it, we print a
-                        * warning and change to the in-progress state.
-                        */
-               case TBLOCK_BEGIN:
-                       elog(WARNING, "StartTransactionCommand: unexpected 
TBLOCK_BEGIN");
-                       s->blockState = TBLOCK_INPROGRESS;
-                       break;
- 
-                       /*
                         * This is the case when are somewhere in a transaction block
                         * and about to start a new command.  For now we do nothing
                         * but someday we may do command-local resource
                         * initialization.
                         */
                case TBLOCK_INPROGRESS:
!                       break;
! 
!                       /*
!                        * As with BEGIN, we should never experience this if we do it
!                        * means the END state was not changed in the previous
!                        * CommitTransactionCommand().  If we get it, we print a
!                        * warning, commit the transaction, start a new transaction
!                        * and change to the default state.
!                        */
!               case TBLOCK_END:
!                       elog(WARNING, "StartTransactionCommand: unexpected 
TBLOCK_END");
!                       CommitTransaction();
!                       StartTransaction();
!                       s->blockState = TBLOCK_DEFAULT;
                        break;
  
                        /*
--- 1441,1453 ----
                        break;
  
                        /*
                         * This is the case when are somewhere in a transaction block
                         * and about to start a new command.  For now we do nothing
                         * but someday we may do command-local resource
                         * initialization.
                         */
                case TBLOCK_INPROGRESS:
!               case TBLOCK_SUBINPROGRESS:
                        break;
  
                        /*
***************
*** 1193,1209 ****
                         * TRANSACTION" which will set things straight.
                         */
                case TBLOCK_ABORT:
                        break;
  
!                       /*
!                        * This means we somehow aborted and the last call to
!                        * CommitTransactionCommand() didn't clear the state so we
!                        * remain in the ENDABORT state and maybe next time we get to
!                        * CommitTransactionCommand() the state will get reset to
!                        * default.
!                        */
                case TBLOCK_ENDABORT:
!                       elog(WARNING, "StartTransactionCommand: unexpected 
TBLOCK_ENDABORT");
                        break;
        }
  
--- 1457,1477 ----
                         * TRANSACTION" which will set things straight.
                         */
                case TBLOCK_ABORT:
+               case TBLOCK_SUBABORT:
                        break;
  
!                       /* These cases are invalid. */
!               case TBLOCK_STARTED:
!               case TBLOCK_BEGIN:
!               case TBLOCK_SUBBEGIN:
!               case TBLOCK_SUBBEGINABORT:
!               case TBLOCK_END:
!               case TBLOCK_SUBEND:
!               case TBLOCK_SUBENDABORT_OK:
!               case TBLOCK_SUBENDABORT_ERROR:
                case TBLOCK_ENDABORT:
!                       elog(FATAL, "StartTransactionCommand: unexpected block state 
%s",
!                                       BlockStateAsString(s->blockState));
                        break;
        }
  
***************
*** 1231,1237 ****
                         * appropiately.
                         */
                case TBLOCK_DEFAULT:
!                       elog(WARNING, "CommitTransactionCommand: unexpected 
TBLOCK_DEFAULT");
                        break;
  
                        /*
--- 1499,1505 ----
                         * appropiately.
                         */
                case TBLOCK_DEFAULT:
!                       elog(FATAL, "CommitTransactionCommand: unexpected 
TBLOCK_DEFAULT");
                        break;
  
                        /*
***************
*** 1290,1295 ****
--- 1558,1633 ----
                        CleanupTransaction();
                        s->blockState = TBLOCK_DEFAULT;
                        break;
+ 
+                       /*
+                        * We were just issued a BEGIN inside a transaction block.
+                        * Start a subtransaction.
+                        */
+               case TBLOCK_SUBBEGIN:
+                       StartSubTransaction();
+                       s->blockState = TBLOCK_SUBINPROGRESS;
+                       break;
+ 
+                       /*
+                        * We were issued a BEGIN inside an aborted transaction block.
+                        * Start a subtransaction, and put it in aborted state.
+                        */
+               case TBLOCK_SUBBEGINABORT:
+                       StartSubTransaction();
+                       AbortSubTransaction();
+                       s->blockState = TBLOCK_SUBABORT;
+                       break;
+ 
+                       /*
+                        * Inside a subtransaction, increment the command counter.
+                        */
+               case TBLOCK_SUBINPROGRESS:
+                       CommandCounterIncrement();
+                       break;
+ 
+                       /*
+                        * We where issued a COMMIT command, so we end the current
+                        * subtransaction and return to the parent transaction.
+                        */
+               case TBLOCK_SUBEND:
+                       CommitSubTransaction();
+                       PopTransaction();
+                       break;
+ 
+                       /*
+                        * If we are in an aborted subtransaction, do nothing.
+                        */
+               case TBLOCK_SUBABORT:
+                       break;
+ 
+                       /*
+                        * We are ending a subtransaction that aborted nicely,
+                        * so the parent can be allowed to live.
+                        */
+               case TBLOCK_SUBENDABORT_OK:
+                       CleanupSubTransaction();
+                       PopTransaction();
+                       break;
+ 
+                       /*
+                        * We are ending a subtransaction that aborted in a unclean
+                        * way (e.g. the user issued COMMIT in an aborted 
subtrasaction.)
+                        * Abort the subtransaction, and abort the parent too.
+                        */
+               case TBLOCK_SUBENDABORT_ERROR:
+                       CleanupSubTransaction();
+                       PopTransaction();
+                       if (IsSubTransaction())
+                       {
+                               AbortSubTransaction();
+                               s->blockState = TBLOCK_SUBABORT;
+                       }
+                       else
+                       {
+                               AbortTransaction();
+                               s->blockState = TBLOCK_ABORT;
+                       }
+                       break;
        }
  }
  
***************
*** 1361,1366 ****
--- 1699,1705 ----
                         * state.
                         */
                case TBLOCK_ABORT:
+               case TBLOCK_SUBABORT:
                        break;
  
                        /*
***************
*** 1373,1378 ****
--- 1712,1758 ----
                        CleanupTransaction();
                        s->blockState = TBLOCK_DEFAULT;
                        break;
+ 
+                       /*
+                        * If we are just starting a subtransaction, put it
+                        * in aborted state.
+                        */
+               case TBLOCK_SUBBEGIN:
+               case TBLOCK_SUBBEGINABORT:
+                       PushCurrentTransaction();
+                       StartSubTransaction();
+                       AbortSubTransaction();
+                       s->blockState = TBLOCK_SUBABORT;
+                       break;
+ 
+               case TBLOCK_SUBINPROGRESS:
+                       AbortSubTransaction();
+                       s->blockState = TBLOCK_SUBABORT;
+                       break;
+ 
+                       /*
+                        * If we are aborting an ending transaction,
+                        * we have to abort the parent transaction too.
+                        */
+               case TBLOCK_SUBEND:
+                       AbortSubTransaction();
+                       CleanupSubTransaction();
+                       PopTransaction();
+                       if (IsSubTransaction())
+                               AbortSubTransaction();
+                       else
+                               AbortTransaction();
+                       break;
+ 
+               case TBLOCK_SUBENDABORT_OK:
+               case TBLOCK_SUBENDABORT_ERROR:
+                       CleanupSubTransaction();
+                       PopTransaction();
+                       if (IsSubTransaction())
+                               AbortSubTransaction();
+                       else
+                               AbortTransaction();
+                       break;
        }
  }
  
***************
*** 1391,1396 ****
--- 1771,1777 ----
   *    stmtNode: pointer to parameter block for statement; this is used in
   *    a very klugy way to determine whether we are inside a function.
   *    stmtType: statement type name for error messages.
+  *
   */
  void
  PreventTransactionChain(void *stmtNode, const char *stmtType)
***************
*** 1418,1424 ****
        /* If we got past IsTransactionBlock test, should be in default state */
        if (CurrentTransactionState->blockState != TBLOCK_DEFAULT &&
                        CurrentTransactionState->blockState != TBLOCK_STARTED)
!               elog(ERROR, "cannot prevent transaction chain");
        /* all okay */
  }
  
--- 1799,1805 ----
        /* If we got past IsTransactionBlock test, should be in default state */
        if (CurrentTransactionState->blockState != TBLOCK_DEFAULT &&
                        CurrentTransactionState->blockState != TBLOCK_STARTED)
!               elog(FATAL, "cannot prevent transaction chain");
        /* all okay */
  }
  
***************
*** 1540,1557 ****
                        s->blockState = TBLOCK_BEGIN;
                        break;
  
!                       /* Already a transaction block in progress. */
                case TBLOCK_INPROGRESS:
                        ereport(WARNING,
                                        (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
                                         errmsg("there is already a transaction in 
progress")));
  
                        /*
!                        * This shouldn't happen, because a transaction in aborted 
state
!                        * will not be allowed to call BeginTransactionBlock.
                         */
                case TBLOCK_ABORT:
!                       elog(WARNING, "BeginTransactionBlock: unexpected 
TBLOCK_ABORT");
                        break;
  
                        /* These cases are invalid.  Reject them altogether. */
--- 1921,1951 ----
                        s->blockState = TBLOCK_BEGIN;
                        break;
  
!                       /*
!                        * Already a transaction block in progress.
!                        * Start a subtransaction, or squeak if we don't
!                        * have subtransactions compiled in.
!                        */
                case TBLOCK_INPROGRESS:
+ #ifndef SUBTRANSACTIONS
                        ereport(WARNING,
                                        (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
                                         errmsg("there is already a transaction in 
progress")));
+                       break;
+ #endif
+               case TBLOCK_SUBINPROGRESS:
+                       PushCurrentTransaction();
+                       s->blockState = TBLOCK_SUBBEGIN;
+                       break;
  
                        /*
!                        * An aborted transaction block should be allowed to start
!                        * a subtransaction, but it must put it in aborted state.
                         */
                case TBLOCK_ABORT:
!               case TBLOCK_SUBABORT:
!                       PushCurrentTransaction();
!                       s->blockState = TBLOCK_SUBBEGINABORT;
                        break;
  
                        /* These cases are invalid.  Reject them altogether. */
***************
*** 1559,1565 ****
                case TBLOCK_BEGIN:
                case TBLOCK_ENDABORT:
                case TBLOCK_END:
!                       elog(FATAL, "BeginTransactionBlock: not in a user-allowed 
state!");
                        break;
        }
  }
--- 1953,1965 ----
                case TBLOCK_BEGIN:
                case TBLOCK_ENDABORT:
                case TBLOCK_END:
!               case TBLOCK_SUBBEGIN:
!               case TBLOCK_SUBBEGINABORT:
!               case TBLOCK_SUBENDABORT_OK:
!               case TBLOCK_SUBENDABORT_ERROR:
!               case TBLOCK_SUBEND:
!                       elog(FATAL, "BeginTransactionBlock: unexpected %s",
!                                       BlockStateAsString(s->blockState));
                        break;
        }
  }
***************
*** 1584,1589 ****
--- 1984,1998 ----
                        break;
  
                        /*
+                        * here we are in a subtransaction block.  Signal
+                        * CommitTransactionCommand() to end it and return to the
+                        * parent transaction.
+                        */
+               case TBLOCK_SUBINPROGRESS:
+                       s->blockState = TBLOCK_SUBEND;
+                       break;
+ 
+                       /*
                         * here, we are in a transaction block which aborted and since 
the
                         * AbortTransaction() was already done, we do whatever is 
needed
                         * and change to the special "END ABORT" state.  The upcoming
***************
*** 1594,1599 ****
--- 2003,2017 ----
                        s->blockState = TBLOCK_ENDABORT;
                        break;
  
+                       /*
+                        * here we are in an aborted subtransaction.  Signal
+                        * CommitTransactionCommand() to clean up and return to the
+                        * parent transaction.
+                        */
+               case TBLOCK_SUBABORT:
+                       s->blockState = TBLOCK_SUBENDABORT_ERROR;
+                       break;
+ 
                case TBLOCK_STARTED:
                        /*
                         * here, the user issued COMMIT when not inside a transaction. 
Issue a
***************
*** 1613,1619 ****
                case TBLOCK_BEGIN:
                case TBLOCK_ENDABORT:
                case TBLOCK_END:
!                       elog(FATAL, "EndTransactionBlock and not in a user-allowed 
state");
                        break;
        }
  }
--- 2031,2043 ----
                case TBLOCK_BEGIN:
                case TBLOCK_ENDABORT:
                case TBLOCK_END:
!               case TBLOCK_SUBBEGIN:
!               case TBLOCK_SUBBEGINABORT:
!               case TBLOCK_SUBEND:
!               case TBLOCK_SUBENDABORT_OK:
!               case TBLOCK_SUBENDABORT_ERROR:
!                       elog(FATAL, "EndTransactionBlock: unexpected %s",
!                                       BlockStateAsString(s->blockState));
                        break;
        }
  }
***************
*** 1626,1667 ****
  {
        TransactionState s = CurrentTransactionState;
  
!       /*
!        * if the transaction has already been automatically aborted with an
!        * error, and the user subsequently types 'abort', allow it.  (the
!        * behavior is the same as if they had typed 'end'.)
!        */
!       if (s->blockState == TBLOCK_ABORT)
!       {
!               s->blockState = TBLOCK_ENDABORT;
!               return;
!       }
! 
!       if (s->blockState == TBLOCK_INPROGRESS)
!       {
                /*
                 * here we were inside a transaction block and we got an abort
                 * command from the user, so we move to the ENDABORT state and
                 * do abort processing so we will end up in the default state
                 * after the upcoming CommitTransactionCommand().
                 */
!               s->blockState = TBLOCK_ABORT;
!               AbortTransaction();
!               s->blockState = TBLOCK_ENDABORT;
!               return;
        }
  
-       /*
-        * here, the user issued ABORT when not inside a transaction. Issue a
-        * WARNING and go to abort state.  The upcoming call to
-        * CommitTransactionCommand() will then put us back into the default
-        * state.
-        */
-       ereport(WARNING,
-                       (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
-                        errmsg("there is no transaction in progress")));
-       AbortTransaction();
-       s->blockState = TBLOCK_ENDABORT;
  }
  
  /*
--- 2050,2118 ----
  {
        TransactionState s = CurrentTransactionState;
  
!       switch (s->blockState) {
                /*
                 * here we were inside a transaction block and we got an abort
                 * command from the user, so we move to the ENDABORT state and
                 * do abort processing so we will end up in the default state
                 * after the upcoming CommitTransactionCommand().
                 */
!               case TBLOCK_ABORT:
!                       s->blockState = TBLOCK_ENDABORT;
!                       break;
! 
!                       /* Ditto, for a subtransaction. */
!               case TBLOCK_SUBABORT:
!                       AbortSubTransaction();
!                       s->blockState = TBLOCK_SUBENDABORT_OK;
!                       break;
! 
!                       /*
!                        * here we were inside a transaction block and we got an abort
!                        * command from the user, so we move to the ENDABORT state and
!                        * do abort processing so we will end up in the default state
!                        * after the upcoming CommitTransactionCommand().
!                        */
!               case TBLOCK_INPROGRESS:
!                       AbortTransaction();
!                       s->blockState = TBLOCK_ENDABORT;
!                       break;
! 
!                       /* Ditto, for a subtransaction. */
!               case TBLOCK_SUBINPROGRESS:
!                       AbortSubTransaction();
!                       s->blockState = TBLOCK_SUBENDABORT_OK;
!                       break;
! 
!                       /*
!                        * here, the user issued ABORT when not inside a transaction. 
Issue a
!                        * WARNING and go to abort state.  The upcoming call to
!                        * CommitTransactionCommand() will then put us back into the 
default
!                        * state.
!                        */
!               case TBLOCK_STARTED:
!                       ereport(WARNING,
!                                       (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
!                                        errmsg("there is no transaction in 
progress")));
!                       AbortTransaction();
!                       s->blockState = TBLOCK_ENDABORT;
!                       break;
! 
!                       /* these cases are invalid. */
!               case TBLOCK_DEFAULT:
!               case TBLOCK_BEGIN:
!               case TBLOCK_END:
!               case TBLOCK_ENDABORT:
!               case TBLOCK_SUBEND:
!               case TBLOCK_SUBENDABORT_OK:
!               case TBLOCK_SUBENDABORT_ERROR:
!               case TBLOCK_SUBBEGIN:
!               case TBLOCK_SUBBEGINABORT:
!                       elog(FATAL, "UserAbortTransactionBlock: unexpected %s",
!                                       BlockStateAsString(s->blockState));
!                       break;
        }
  
  }
  
  /*
***************
*** 1697,1702 ****
--- 2148,2177 ----
                        /* AbortTransaction already done, still need Cleanup */
                        CleanupTransaction();
                        break;
+               case TBLOCK_SUBBEGIN:
+               case TBLOCK_SUBBEGINABORT:
+                       /*
+                        * Just starting a new transaction -- return to parent.
+                        * FIXME -- Is this correct?
+                        */
+                       PopTransaction();
+                       AbortOutOfAnyTransaction();
+                       break;
+               case TBLOCK_SUBINPROGRESS:
+               case TBLOCK_SUBEND:
+                       /* In a subtransaction, so clean it up and abort parent too */
+                       AbortSubTransaction();
+                       CleanupSubTransaction();
+                       PopTransaction();
+                       AbortOutOfAnyTransaction();
+                       break;
+               case TBLOCK_SUBABORT:
+               case TBLOCK_SUBENDABORT_OK:
+               case TBLOCK_SUBENDABORT_ERROR:
+                       CleanupSubTransaction();
+                       PopTransaction();
+                       AbortOutOfAnyTransaction();
+                       break;
        }
  
        /*
***************
*** 1753,1770 ****
                case TBLOCK_BEGIN:
                case TBLOCK_INPROGRESS:
                case TBLOCK_END:
                        return 'T';                     /* in transaction */
                case TBLOCK_ABORT:
                case TBLOCK_ENDABORT:
                        return 'E';                     /* in failed transaction */
        }
  
        /* should never get here */
!       elog(ERROR, "invalid transaction block state: %d",
!                (int) s->blockState);
        return 0;                                       /* keep compiler quiet */
  }
  
  
  /*
   *    XLOG support routines
--- 2228,2561 ----
                case TBLOCK_BEGIN:
                case TBLOCK_INPROGRESS:
                case TBLOCK_END:
+               case TBLOCK_SUBINPROGRESS:
+               case TBLOCK_SUBBEGIN:
+               case TBLOCK_SUBEND:
                        return 'T';                     /* in transaction */
                case TBLOCK_ABORT:
                case TBLOCK_ENDABORT:
+               case TBLOCK_SUBABORT:
+               case TBLOCK_SUBENDABORT_OK:
+               case TBLOCK_SUBENDABORT_ERROR:
+               case TBLOCK_SUBBEGINABORT:
                        return 'E';                     /* in failed transaction */
        }
  
        /* should never get here */
!       elog(FATAL, "invalid transaction block state: %s",
!                BlockStateAsString(s->blockState));
        return 0;                                       /* keep compiler quiet */
  }
  
+ #ifdef SUBTRANSACTIONS
+ /*
+  * IsSubTransaction
+  */
+ bool
+ IsSubTransaction(void)
+ {
+       TransactionState s = CurrentTransactionState;
+       
+       switch (s->blockState) {
+               case TBLOCK_DEFAULT:
+               case TBLOCK_STARTED:
+               case TBLOCK_BEGIN:
+               case TBLOCK_INPROGRESS:
+               case TBLOCK_END:
+               case TBLOCK_ABORT:
+               case TBLOCK_ENDABORT:
+                       return false;
+               case TBLOCK_SUBBEGIN:
+               case TBLOCK_SUBBEGINABORT:
+               case TBLOCK_SUBINPROGRESS:
+               case TBLOCK_SUBABORT:
+               case TBLOCK_SUBEND:
+               case TBLOCK_SUBENDABORT_OK:
+               case TBLOCK_SUBENDABORT_ERROR:
+                       return true;
+       }
+ 
+       /* Should not get here */
+       Assert(false);
+ 
+       /* Keep compiler quiet */
+       return false;
+ }
+ 
+ /*
+  * StartSubTransaction
+  */
+ void
+ StartSubTransaction(void)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       /*
+        * generate a new transaction id
+        */
+       s->transactionIdData = GetNewTransactionId();
+ 
+       s->childXids = NIL;
+ 
+       CommandCounterIncrement();
+       
+       /* Initialize the memory subsystem */
+       AtSubStart_Memory();
+ 
+       ShowTransactionState("StartSubTransaction");
+ }
+ 
+ /*
+  * CommitSubTransaction
+  */
+ void
+ CommitSubTransaction(void)
+ {
+       TransactionState s = CurrentTransactionState;
+       ShowTransactionState("CommitSubTransaction");
+ 
+       CommandCounterIncrement();
+ 
+       Assert(s->parent != NULL);
+ 
+       smgrCommitSubTransaction();
+ 
+       AtSubCommit_Portals(s->parent->transactionIdData);
+       AtSubCommit_Memory();
+       AtSubCommit_childXids();
+       AtSubCommit_Notifies();
+ }
+ 
+ /*
+  * AbortSubTransaction
+  */
+ void
+ AbortSubTransaction(void)
+ {
+       ShowTransactionState("AbortSubTransaction");
+ 
+       AtSubAbort_Portals();
+       AtSubAbort_Memory();
+ 
+       /* I'm not sure this is needed. */
+       CommandCounterIncrement();
+ 
+       smgrAbortSubTransaction();
+ }
+ 
+ /*
+  * CleanupSubTransaction
+  */
+ void
+ CleanupSubTransaction(void)
+ {
+       ShowTransactionState("CleanupSubTransaction");
+ 
+       AtSubCleanup_Portals();
+       AtSubCleanup_Memory();
+ }
+ 
+ /*
+  * PushCurrentTransaction
+  */
+ void
+ PushCurrentTransaction(void)
+ {
+       TransactionState    parent;
+       TransactionState    s = CurrentTransactionState;
+       MemoryContext           old_cxt;
+ 
+       /*
+        * Make sure the transaction state node is in a long-lived context
+        */
+       old_cxt = MemoryContextSwitchTo(TopMemoryContext);
+ 
+       parent = (TransactionState) palloc(sizeof(TransactionStateData));
+       memcpy(parent, s, sizeof(TransactionStateData));
+ 
+       s->parent = parent;
+       s->nestingLevel = s->parent->nestingLevel + 1;
+       s->commandId = s->parent->commandId;
+ 
+       MemoryContextSwitchTo(old_cxt);
+ }
+ 
+ /*
+  * PopTransaction
+  */
+ void
+ PopTransaction(void)
+ {
+       TransactionState s = CurrentTransactionState;
+       TransactionState p;
+ 
+       Assert(s->parent != NULL);
+ 
+       s->parent->commandId = s->commandId;
+ 
+       p = s->parent;
+ 
+       memcpy(s, p, sizeof(TransactionStateData));
+       CommitContext = s->commitContext;
+ 
+       /* Free the old parent structure */
+       pfree(p);
+ }
+ 
+ /*
+  * ShowTransactionState
+  */
+ void
+ ShowTransactionState(char *str)
+ {
+       TransactionState s = CurrentTransactionState;
+ 
+       elog(DEBUG2, "%s", str);
+     ShowTransactionStateRec(s, 2 * s->nestingLevel);
+ }
+ 
+ /*
+  * ShowTransactionStateRec
+  */
+ void
+ ShowTransactionStateRec(TransactionState s, int indent)
+ {
+       char   *blockState = BlockStateAsString(s->blockState);
+       char   *state      = TransStateAsString(s->state);
+       List   *child;
+       char   *childStr;
+       char   *tmp;
+       int             len;
+       
+       if (s->parent)
+               ShowTransactionStateRec(s->parent, indent - 2);
+ 
+       len = length(s->childXids);
+       if (len == 0)
+               childStr = "(none)";
+       else
+       {
+               childStr = (char *)calloc(13, len);
+               tmp = (char *)malloc(13);
+ 
+               foreach (child, s->childXids)
+               {
+                       /* there will be an extra space at the end but ... */
+                       snprintf(tmp, 12, "%d ", lfirsti(child));
+                       strncat(childStr, tmp, 12);
+               }
+               free(tmp);
+       }
+ 
+       tmp = (char *)malloc(sizeof(char) * indent + 1);
+       memset(tmp, ' ', indent);
+       memset(tmp + indent, '\0', 1);
+ 
+       elog(DEBUG2, "%sblockState: %19s; state: %10s, xid/cid: %u/%u, nestlvl: %d, 
childs: %s",
+                       tmp,
+                       blockState,
+                       state,
+                       (unsigned int)s->transactionIdData,
+                       (unsigned int)s->commandId,
+                       s->nestingLevel,
+                       childStr);
+       free(tmp);
+ 
+       if (len != 0)
+               free(childStr);
+       pfree(blockState);
+       pfree(state);
+ }
+ 
+ #endif /* SUBTRANSACTIONS */
+ 
+ /*
+  * BlockStateAsString
+  *
+  * Returns the block state as a palloc'ed string
+  */
+ char *
+ BlockStateAsString(TBlockState blockState)
+ {
+       char *tmp = "";
+       switch (blockState) {
+               case TBLOCK_DEFAULT:
+                       tmp = "DEFAULT";
+                       break;
+               case TBLOCK_STARTED:
+                       tmp = "STARTED";
+                       break;
+               case TBLOCK_BEGIN:
+                       tmp = "BEGIN";
+                       break;
+               case TBLOCK_INPROGRESS:
+                       tmp = "INPROGRESS";
+                       break;
+               case TBLOCK_END:
+                       tmp = "END";
+                       break;
+               case TBLOCK_ABORT:
+                       tmp = "ABORT";
+                       break;
+               case TBLOCK_ENDABORT:
+                       tmp = "ENDABORT";
+                       break;
+               case TBLOCK_SUBBEGIN:
+                       tmp = "SUB BEGIN";
+                       break;
+               case TBLOCK_SUBBEGINABORT:
+                       tmp = "SUB BEGIN ABORT";
+                       break;
+               case TBLOCK_SUBINPROGRESS:
+                       tmp = "SUB INPROGRESS";
+                       break;
+               case TBLOCK_SUBEND:
+                       tmp = "SUB END";
+                       break;
+               case TBLOCK_SUBABORT:
+                       tmp = "SUB ABORT";
+                       break;
+               case TBLOCK_SUBENDABORT_OK:
+                       tmp = "SUB ENDABORT OK";
+                       break;
+               case TBLOCK_SUBENDABORT_ERROR:
+                       tmp = "SUB ENDABORT ERROR";
+                       break;
+       }
+       return pstrdup(tmp);
+ }
+ 
+ #ifdef SUBTRANSACTIONS
+ /*
+  * TransStateAsString
+  *
+  * Returns the transaction internal state as a palloc'ed string
+  */
+ char *
+ TransStateAsString(TransState state)
+ {
+       char *tmp = "";
+ 
+       switch (state) {
+               case TRANS_DEFAULT:
+                       tmp = "DEFAULT";
+                       break;
+               case TRANS_START:
+                       tmp = "START";
+                       break;
+               case TRANS_COMMIT:
+                       tmp = "COMMIT";
+                       break;
+               case TRANS_ABORT:
+                       tmp = "ABORT";
+                       break;
+               case TRANS_INPROGRESS:
+                       tmp = "INPROGRESS";
+                       break;
+       }
+       return pstrdup(tmp);
+ }
+ #endif /* SUBTRANSACTIONS */
  
  /*
   *    XLOG support routines
Index: src/backend/commands/async.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/commands/async.c,v
retrieving revision 1.109
diff -c -r1.109 async.c
*** src/backend/commands/async.c        8 Feb 2004 22:28:56 -0000       1.109
--- src/backend/commands/async.c        26 Apr 2004 02:54:03 -0000
***************
*** 96,104 ****
   * in the current transaction.        We do not actually perform a NOTIFY until
   * and unless the transaction commits.        pendingNotifies is NIL if no
   * NOTIFYs have been done in the current transaction.  The List nodes and
!  * referenced strings are all palloc'd in TopTransactionContext.
   */
- static List *pendingNotifies = NIL;
  
  /*
   * State for inbound notifies consists of two flags: one saying whether
--- 96,105 ----
   * in the current transaction.        We do not actually perform a NOTIFY until
   * and unless the transaction commits.        pendingNotifies is NIL if no
   * NOTIFYs have been done in the current transaction.  The List nodes and
!  * referenced strings are all palloc'd in CommitContext.  The List is
!  * kept in the TransactionState struct, and is handled by
!  * access/transam/xact.c; we get and set it from there whenever needed.
   */
  
  /*
   * State for inbound notifies consists of two flags: one saying whether
***************
*** 159,165 ****
  
                oldcontext = MemoryContextSwitchTo(TopTransactionContext);
  
!               pendingNotifies = lcons(pstrdup(relname), pendingNotifies);
  
                MemoryContextSwitchTo(oldcontext);
        }
--- 160,167 ----
  
                oldcontext = MemoryContextSwitchTo(TopTransactionContext);
  
!               TransactionSetNotifies(lcons(pstrdup(relname),
!                                                          TransactionGetNotifies()));
  
                MemoryContextSwitchTo(oldcontext);
        }
***************
*** 433,439 ****
        char            repl[Natts_pg_listener],
                                nulls[Natts_pg_listener];
  
!       if (pendingNotifies == NIL)
                return;                                 /* no NOTIFY statements in this
                                                                 * transaction */
  
--- 435,441 ----
        char            repl[Natts_pg_listener],
                                nulls[Natts_pg_listener];
  
!       if (TransactionGetNotifies() == NIL)
                return;                                 /* no NOTIFY statements in this
                                                                 * transaction */
  
***************
*** 915,927 ****
                elog(INFO, "NOTIFY for %s", relname);
  }
  
! /* Does pendingNotifies include the given relname? */
  static bool
  AsyncExistsPendingNotify(const char *relname)
  {
        List       *p;
  
!       foreach(p, pendingNotifies)
        {
                /* Use NAMEDATALEN for relname comparison.        DZ - 26-08-1996 */
                if (strncmp((const char *) lfirst(p), relname, NAMEDATALEN) == 0)
--- 917,929 ----
                elog(INFO, "NOTIFY for %s", relname);
  }
  
! /* Does the pendingNotifies list include the given relname? */
  static bool
  AsyncExistsPendingNotify(const char *relname)
  {
        List       *p;
  
!       foreach(p, TransactionGetNotifies())
        {
                /* Use NAMEDATALEN for relname comparison.        DZ - 26-08-1996 */
                if (strncmp((const char *) lfirst(p), relname, NAMEDATALEN) == 0)
***************
*** 938,946 ****
        /*
         * We used to have to explicitly deallocate the list members and
         * nodes, because they were malloc'd.  Now, since we know they are
!        * palloc'd in TopTransactionContext, we need not do that --- they'll
         * go away automatically at transaction exit.  We need only reset the
         * list head pointer.
         */
!       pendingNotifies = NIL;
  }
--- 940,948 ----
        /*
         * We used to have to explicitly deallocate the list members and
         * nodes, because they were malloc'd.  Now, since we know they are
!        * palloc'd in CommitContext, we need not do that --- they'll
         * go away automatically at transaction exit.  We need only reset the
         * list head pointer.
         */
!       TransactionSetNotifies(NIL);
  }
Index: src/backend/storage/smgr/smgr.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/storage/smgr/smgr.c,v
retrieving revision 1.70
diff -c -r1.70 smgr.c
*** src/backend/storage/smgr/smgr.c     11 Feb 2004 22:55:25 -0000      1.70
--- src/backend/storage/smgr/smgr.c     26 Apr 2004 04:32:27 -0000
***************
*** 17,22 ****
--- 17,23 ----
   */
  #include "postgres.h"
  
+ #include "access/xact.h"
  #include "storage/bufmgr.h"
  #include "storage/freespace.h"
  #include "storage/ipc.h"
***************
*** 77,98 ****
   * executed immediately, but is just entered in the list.  When and if
   * the transaction commits, we can delete the physical file.
   *
!  * NOTE: the list is kept in TopMemoryContext to be sure it won't disappear
!  * unbetimes.  It'd probably be OK to keep it in TopTransactionContext,
!  * but I'm being paranoid.
   */
  
! typedef struct PendingRelDelete
  {
        RelFileNode relnode;            /* relation that may need to be deleted */
        int                     which;                  /* which storage manager? */
        bool            isTemp;                 /* is it a temporary relation? */
        bool            atCommit;               /* T=delete at commit; F=delete at 
abort */
!       struct PendingRelDelete *next;          /* linked-list link */
! } PendingRelDelete;
! 
! static PendingRelDelete *pendingDeletes = NULL; /* head of linked list */
  
  
  /*
   * Declarations for smgr-related XLOG records
--- 78,95 ----
   * executed immediately, but is just entered in the list.  When and if
   * the transaction commits, we can delete the physical file.
   *
!  * The list is kept in CommitContext.
   */
  
! typedef struct PendingRelDeleteData
  {
        RelFileNode relnode;            /* relation that may need to be deleted */
        int                     which;                  /* which storage manager? */
        bool            isTemp;                 /* is it a temporary relation? */
        bool            atCommit;               /* T=delete at commit; F=delete at 
abort */
! } PendingRelDeleteData;
  
+ typedef PendingRelDeleteData *PendingRelDelete;
  
  /*
   * Declarations for smgr-related XLOG records
***************
*** 300,306 ****
        XLogRecPtr              lsn;
        XLogRecData             rdata;
        xl_smgr_create  xlrec;
!       PendingRelDelete *pending;
  
        if (! (*(smgrsw[reln->smgr_which].smgr_create)) (reln, isRedo))
                ereport(ERROR,
--- 297,304 ----
        XLogRecPtr              lsn;
        XLogRecData             rdata;
        xl_smgr_create  xlrec;
!       PendingRelDelete pending;
!       MemoryContext   old_cxt;
  
        if (! (*(smgrsw[reln->smgr_which].smgr_create)) (reln, isRedo))
                ereport(ERROR,
***************
*** 326,340 ****
  
        lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE | XLOG_NO_TRAN, &rdata);
  
        /* Add the relation to the list of stuff to delete at abort */
!       pending = (PendingRelDelete *)
!               MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
        pending->relnode = reln->smgr_rnode;
        pending->which = reln->smgr_which;
        pending->isTemp = isTemp;
        pending->atCommit = false;      /* delete if abort */
!       pending->next = pendingDeletes;
!       pendingDeletes = pending;
  }
  
  /*
--- 324,342 ----
  
        lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE | XLOG_NO_TRAN, &rdata);
  
+       old_cxt = MemoryContextSwitchTo(CommitContext);
+ 
        /* Add the relation to the list of stuff to delete at abort */
!       pending = (PendingRelDelete) palloc(sizeof(PendingRelDeleteData));
        pending->relnode = reln->smgr_rnode;
        pending->which = reln->smgr_which;
        pending->isTemp = isTemp;
        pending->atCommit = false;      /* delete if abort */
! 
!       TransactionSetPendingDeletes(lcons(pending,
!                               TransactionGetPendingDeletes()));
! 
!       MemoryContextSwitchTo(old_cxt);
  }
  
  /*
***************
*** 348,364 ****
  void
  smgrscheduleunlink(SMgrRelation reln, bool isTemp)
  {
!       PendingRelDelete *pending;
  
        /* Add the relation to the list of stuff to delete at commit */
!       pending = (PendingRelDelete *)
!               MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
        pending->relnode = reln->smgr_rnode;
        pending->which = reln->smgr_which;
        pending->isTemp = isTemp;
        pending->atCommit = true;       /* delete if commit */
!       pending->next = pendingDeletes;
!       pendingDeletes = pending;
  
        /*
         * NOTE: if the relation was created in this transaction, it will now
--- 350,369 ----
  void
  smgrscheduleunlink(SMgrRelation reln, bool isTemp)
  {
!       PendingRelDelete pending;
!       MemoryContext    old_cxt;
! 
!       old_cxt = MemoryContextSwitchTo(CommitContext);
  
        /* Add the relation to the list of stuff to delete at commit */
!       pending = (PendingRelDelete) palloc(sizeof(PendingRelDeleteData));
        pending->relnode = reln->smgr_rnode;
        pending->which = reln->smgr_which;
        pending->isTemp = isTemp;
        pending->atCommit = true;       /* delete if commit */
! 
!       TransactionSetPendingDeletes(lcons(pending,
!                               TransactionGetPendingDeletes()));
  
        /*
         * NOTE: if the relation was created in this transaction, it will now
***************
*** 372,377 ****
--- 377,384 ----
  
        /* Now close the file and throw away the hashtable entry */
        smgrclose(reln);
+ 
+       MemoryContextSwitchTo(old_cxt);
  }
  
  /*
***************
*** 573,592 ****
  void
  smgrDoPendingDeletes(bool isCommit)
  {
!       while (pendingDeletes != NULL)
        {
!               PendingRelDelete *pending = pendingDeletes;
  
-               pendingDeletes = pending->next;
                if (pending->atCommit == isCommit)
                        smgr_internal_unlink(pending->relnode,
                                                                 pending->which,
                                                                 pending->isTemp,
                                                                 false);
-               pfree(pending);
        }
  }
  
  /*
   * smgrGetPendingDeletes() -- Get a list of relations to be deleted.
   *
--- 580,644 ----
  void
  smgrDoPendingDeletes(bool isCommit)
  {
!       List *p;
! 
!       foreach (p, TransactionGetPendingDeletes())
        {
!               PendingRelDelete pending = lfirst(p);
  
                if (pending->atCommit == isCommit)
                        smgr_internal_unlink(pending->relnode,
                                                                 pending->which,
                                                                 pending->isTemp,
                                                                 false);
        }
+ 
+       TransactionSetPendingDeletes(NIL);
  }
  
+ #ifdef SUBTRANSACTIONS
+ /*
+  * smgrAbortSubTransaction() --- Take care of subtransaction abort.
+  *
+  * Delete created relations and forget about deleted relations.
+  * We don't care about these operations anymore because we know this
+  * subtransaction will not commit.
+  */
+ void
+ smgrAbortSubTransaction(void)
+ {
+       List *p;
+ 
+       foreach (p, TransactionGetPendingDeletes())
+       {
+               PendingRelDelete pending = lfirst(p);
+ 
+               if (pending->atCommit == false)
+                       smgr_internal_unlink(pending->relnode,
+                                                                pending->which,
+                                                                pending->isTemp,
+                                                                false);
+       }
+ }
+ 
+ /*
+  * smgrCommitSubTransaction() --- Take care of subtransaction commit.
+  *
+  * Reassign all items in the pending deletes list to the parent transaction.
+  */
+ void
+ smgrCommitSubTransaction(void)
+ {
+       List *pending,
+            *parentPending;
+ 
+       pending = TransactionGetPendingDeletes();
+       parentPending = TransactionGetParentPendingDeletes();
+ 
+       TransactionSetParentPendingDeletes(nconc(parentPending, pending));
+ }
+ #endif /* SUBTRANSACTIONS */
+ 
  /*
   * smgrGetPendingDeletes() -- Get a list of relations to be deleted.
   *
***************
*** 599,609 ****
  {
        int                     nrels;
        RelFileNode *rptr;
!       PendingRelDelete *pending;
  
        nrels = 0;
!       for (pending = pendingDeletes; pending != NULL; pending = pending->next)
        {
                if (pending->atCommit == forCommit)
                        nrels++;
        }
--- 651,663 ----
  {
        int                     nrels;
        RelFileNode *rptr;
!       List            *p;
  
        nrels = 0;
!       foreach (p, TransactionGetPendingDeletes())
        {
+               PendingRelDelete pending = lfirst(p);
+ 
                if (pending->atCommit == forCommit)
                        nrels++;
        }
***************
*** 614,621 ****
        }
        rptr = (RelFileNode *) palloc(nrels * sizeof(RelFileNode));
        *ptr = rptr;
!       for (pending = pendingDeletes; pending != NULL; pending = pending->next)
        {
                if (pending->atCommit == forCommit)
                        *rptr++ = pending->relnode;
        }
--- 668,677 ----
        }
        rptr = (RelFileNode *) palloc(nrels * sizeof(RelFileNode));
        *ptr = rptr;
!       foreach (p, TransactionGetPendingDeletes())
        {
+               PendingRelDelete pending = lfirst(p);
+ 
                if (pending->atCommit == forCommit)
                        *rptr++ = pending->relnode;
        }
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/tcop/postgres.c,v
retrieving revision 1.401
diff -c -r1.401 postgres.c
*** src/backend/tcop/postgres.c 25 Apr 2004 18:23:56 -0000      1.401
--- src/backend/tcop/postgres.c 25 Apr 2004 21:49:59 -0000
***************
*** 841,846 ****
--- 841,849 ----
                                TransactionStmt *stmt = (TransactionStmt *) parsetree;
  
                                if (stmt->kind == TRANS_STMT_COMMIT ||
+ #ifdef SUBTRANSACTIONS
+                                       stmt->kind == TRANS_STMT_BEGIN ||
+ #endif
                                        stmt->kind == TRANS_STMT_ROLLBACK)
                                        allowit = true;
                        }
Index: src/backend/utils/mmgr/README
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/mmgr/README,v
retrieving revision 1.5
diff -c -r1.5 README
*** src/backend/utils/mmgr/README       29 Nov 2003 19:52:04 -0000      1.5
--- src/backend/utils/mmgr/README       26 Apr 2004 03:48:13 -0000
***************
*** 70,76 ****
  We could even consider getting rid of CurrentMemoryContext entirely,
  instead requiring the target memory context for allocation to be specified
  explicitly.  But I think that would be too much notational overhead ---
! we'd have to pass an apppropriate memory context to called routines in
  many places.  For example, the copyObject routines would need to be passed
  a context, as would function execution routines that return a
  pass-by-reference datatype.  And what of routines that temporarily
--- 70,76 ----
  We could even consider getting rid of CurrentMemoryContext entirely,
  instead requiring the target memory context for allocation to be specified
  explicitly.  But I think that would be too much notational overhead ---
! we'd have to pass an appropriate memory context to called routines in
  many places.  For example, the copyObject routines would need to be passed
  a context, as would function execution routines that return a
  pass-by-reference datatype.  And what of routines that temporarily
***************
*** 157,170 ****
  single transaction or portal.
  
  TopTransactionContext --- this holds everything that lives until end of
! transaction (longer than one statement within a transaction!).  An example
! of what has to be here is the list of pending NOTIFY messages to be sent
! at xact commit.  This context will be reset, and all its children deleted,
! at conclusion of each transaction cycle.  Note: this context is NOT
! cleared immediately upon error; its contents will survive until the
! transaction block is exited by COMMIT/ROLLBACK.
! (If we ever implement nested transactions, TopTransactionContext may need
! to be split into a true "top" pointer and a "current transaction" pointer.)
  
  QueryContext --- this is not actually a separate context, but a global
  variable pointing to the context that holds the current command's parse
--- 157,173 ----
  single transaction or portal.
  
  TopTransactionContext --- this holds everything that lives until end of
! transaction (longer than one statement within a transaction!).  This context
! will be reset, and all its children deleted, at conclusion of each
! transaction cycle.  Note: this context is NOT cleared immediately upon
! error; its contents will survive until the transaction block is exited by
! COMMIT/ROLLBACK.
! 
! CommitContext --- this holds data local to a transaction that has to be
! freed whenever a subtransaction aborts, but which should survive if it
! commits.  For example, the list of pending NOTIFY messages has to live here,
! because we don't need to keep messages issued within aborted
! subtransactions.
  
  QueryContext --- this is not actually a separate context, but a global
  variable pointing to the context that holds the current command's parse
Index: src/backend/utils/mmgr/mcxt.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/mmgr/mcxt.c,v
retrieving revision 1.44
diff -c -r1.44 mcxt.c
*** src/backend/utils/mmgr/mcxt.c       29 Nov 2003 19:52:04 -0000      1.44
--- src/backend/utils/mmgr/mcxt.c       21 Apr 2004 14:26:41 -0000
***************
*** 45,50 ****
--- 45,51 ----
  MemoryContext CacheMemoryContext = NULL;
  MemoryContext MessageContext = NULL;
  MemoryContext TopTransactionContext = NULL;
+ MemoryContext CommitContext = NULL;
  
  /* These two are transient links to contexts owned by other objects: */
  MemoryContext QueryContext = NULL;
Index: src/backend/utils/mmgr/portalmem.c
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/backend/utils/mmgr/portalmem.c,v
retrieving revision 1.64
diff -c -r1.64 portalmem.c
*** src/backend/utils/mmgr/portalmem.c  3 Feb 2004 17:34:03 -0000       1.64
--- src/backend/utils/mmgr/portalmem.c  25 Apr 2004 16:51:24 -0000
***************
*** 511,513 ****
--- 511,603 ----
                PortalDrop(portal, true);
        }
  }
+ 
+ #ifdef SUBTRANSACTIONS
+ /*
+  * Subtransaction commit handling for portals.
+  *
+  * Reassign all portals created by the current subtransaction to the
+  * parent transaction.
+  */
+ void
+ AtSubCommit_Portals(TransactionId parentXid)
+ {
+       HASH_SEQ_STATUS status;
+       PortalHashEnt *hentry;
+       TransactionId curXid = GetCurrentTransactionId();
+ 
+       hash_seq_init(&status, PortalHashTable);
+ 
+       while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
+       {
+               Portal  portal = hentry->portal;
+ 
+               if (portal->createXact == curXid)
+                       portal->createXact = parentXid;
+       }
+ }
+ 
+ /*
+  * Subtransaction abort handling for portals.
+  *
+  * Deactivate all portals created during to the finishing subtransaction.
+  * Note that per AtSubCommit_Portals, this will catch portals created
+  * in descendents of the finishing subtransaction too.
+  */
+ void
+ AtSubAbort_Portals(void)
+ {
+       HASH_SEQ_STATUS status;
+       PortalHashEnt *hentry;
+       TransactionId curXid = GetCurrentTransactionId();
+ 
+       hash_seq_init(&status, PortalHashTable);
+ 
+       while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
+       {
+               Portal  portal = hentry->portal;
+ 
+               if (portal->createXact != curXid)
+                       continue;
+ 
+               portal->portalActive = false;
+               if (PointerIsValid(portal->cleanup))
+               {
+                       (*portal->cleanup) (portal, true);
+                       portal->cleanup = NULL;
+               }
+       }
+ }
+ 
+ /*
+  * Subtransaction cleanup handling for portals.
+  *
+  * Drop all portals created in the aborting subtransaction and all
+  * its descendents.
+  */
+ void
+ AtSubCleanup_Portals(void)
+ {
+       HASH_SEQ_STATUS status;
+       PortalHashEnt *hentry;
+       TransactionId curXid = GetCurrentTransactionId();
+ 
+       hash_seq_init(&status, PortalHashTable);
+ 
+       while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
+       {
+               Portal          portal = hentry->portal;
+ 
+               if (portal->createXact != curXid)
+                       continue;
+ 
+               /*
+                * Let's just make sure no one's active...
+                */
+               portal->portalActive = false;
+ 
+               /* Zap it with prejudice. */
+               PortalDrop(portal, true);
+       }
+ }
+ #endif /* SUBTRANSACTIONS */
Index: src/include/pg_config_manual.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/pg_config_manual.h,v
retrieving revision 1.12
diff -c -r1.12 pg_config_manual.h
*** src/include/pg_config_manual.h      24 Mar 2004 22:40:29 -0000      1.12
--- src/include/pg_config_manual.h      26 Apr 2004 14:53:01 -0000
***************
*** 235,237 ****
--- 235,239 ----
  /* #define ACLDEBUG */
  /* #define RTDEBUG */
  /* #define GISTDEBUG */
+ 
+ #define SUBTRANSACTIONS
Index: src/include/access/xact.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/access/xact.h,v
retrieving revision 1.62
diff -c -r1.62 xact.h
*** src/include/access/xact.h   5 Apr 2004 03:11:39 -0000       1.62
--- src/include/access/xact.h   26 Apr 2004 04:19:06 -0000
***************
*** 63,69 ****
        TBLOCK_INPROGRESS,
        TBLOCK_END,
        TBLOCK_ABORT,
!       TBLOCK_ENDABORT
  } TBlockState;
  
  /*
--- 63,78 ----
        TBLOCK_INPROGRESS,
        TBLOCK_END,
        TBLOCK_ABORT,
!       TBLOCK_ENDABORT,
! 
!       TBLOCK_SUBBEGIN,
!       TBLOCK_SUBBEGINABORT,
!       TBLOCK_SUBINPROGRESS,
!       TBLOCK_SUBEND,
!       TBLOCK_SUBABORT,
!       TBLOCK_SUBENDABORT_OK,
!       TBLOCK_SUBENDABORT_ERROR
! 
  } TBlockState;
  
  /*
***************
*** 82,87 ****
--- 91,109 ----
        int                             startTimeUsec;
        TransState              state;
        TBlockState             blockState;
+ #ifdef SUBTRANSACTIONS
+       int                             nestingLevel;
+       MemoryContext   commitContext;
+       void               *deferredTriggers;
+ #endif /* SUBTRANSACTIONS */
+       List               *pendingDeletes;
+       List               *notifies;
+ #ifdef SUBTRANSACTIONS
+       LWLockId           *LWLocks;
+       List               *locks;
+       List               *childXids;
+       struct TransactionStateData *parent;
+ #endif /* SUBTRANSACTIONS */
  } TransactionStateData;
  
  typedef TransactionStateData *TransactionState;
***************
*** 130,135 ****
--- 152,168 ----
  extern CommandId GetCurrentCommandId(void);
  extern AbsoluteTime GetCurrentTransactionStartTime(void);
  extern AbsoluteTime GetCurrentTransactionStartTimeUsec(int *usec);
+ 
+ extern List *TransactionGetPendingDeletes(void);
+ extern void TransactionSetPendingDeletes(List *);
+ extern List *TransactionGetParentPendingDeletes(void);
+ extern void TransactionSetParentPendingDeletes(List *);
+ 
+ extern List *TransactionGetNotifies(void);
+ extern void TransactionSetNotifies(List *);
+ extern List *TransactionGetParentNotifies(void);
+ extern void TransactionSetParentNotifies(List *);
+ 
  extern bool TransactionIdIsCurrentTransactionId(TransactionId xid);
  extern bool CommandIdIsCurrentCommandId(CommandId cid);
  extern void CommandCounterIncrement(void);
Index: src/include/storage/smgr.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/storage/smgr.h,v
retrieving revision 1.41
diff -c -r1.41 smgr.h
*** src/include/storage/smgr.h  11 Feb 2004 22:55:26 -0000      1.41
--- src/include/storage/smgr.h  21 Apr 2004 15:33:45 -0000
***************
*** 61,66 ****
--- 61,68 ----
  extern BlockNumber smgrnblocks(SMgrRelation reln);
  extern BlockNumber smgrtruncate(SMgrRelation reln, BlockNumber nblocks);
  extern void smgrDoPendingDeletes(bool isCommit);
+ extern void smgrAbortSubTransaction(void);
+ extern void smgrCommitSubTransaction(void);
  extern int    smgrGetPendingDeletes(bool forCommit, RelFileNode **ptr);
  extern void smgrcommit(void);
  extern void smgrabort(void);
Index: src/include/utils/memutils.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/utils/memutils.h,v
retrieving revision 1.54
diff -c -r1.54 memutils.h
*** src/include/utils/memutils.h        29 Nov 2003 22:41:15 -0000      1.54
--- src/include/utils/memutils.h        21 Apr 2004 14:26:44 -0000
***************
*** 70,75 ****
--- 70,76 ----
  extern DLLIMPORT MemoryContext CacheMemoryContext;
  extern DLLIMPORT MemoryContext MessageContext;
  extern DLLIMPORT MemoryContext TopTransactionContext;
+ extern DLLIMPORT MemoryContext CommitContext;
  
  /* These two are transient links to contexts owned by other objects: */
  extern DLLIMPORT MemoryContext QueryContext;
Index: src/include/utils/portal.h
===================================================================
RCS file: /home/alvherre/cvs/pgsql-server/src/include/utils/portal.h,v
retrieving revision 1.48
diff -c -r1.48 portal.h
*** src/include/utils/portal.h  29 Nov 2003 22:41:16 -0000      1.48
--- src/include/utils/portal.h  25 Apr 2004 16:51:03 -0000
***************
*** 167,172 ****
--- 167,177 ----
  extern void AtCommit_Portals(void);
  extern void AtAbort_Portals(void);
  extern void AtCleanup_Portals(void);
+ #ifdef SUBTRANSACTIONS
+ extern void AtSubCommit_Portals(TransactionId);
+ extern void AtSubAbort_Portals(void);
+ extern void AtSubCleanup_Portals(void);
+ #endif /* SUBTRANSACTIONS */
  extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent);
  extern Portal CreateNewPortal(void);
  extern void PortalDrop(Portal portal, bool isError);
---------------------------(end of broadcast)---------------------------
TIP 5: Have you checked our extensive FAQ?

               http://www.postgresql.org/docs/faqs/FAQ.html

Reply via email to