*
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.178 2004/08/11 04:07:15 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.179 2004/08/25 18:43:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
                        /*
                         * We were issued a RELEASE command, so we end the current
                         * subtransaction and return to the parent transaction.
+                        *
+                        * Since RELEASE can exit multiple levels of subtransaction,
+                        * we must loop here until we get out of all SUBEND'ed levels.
                         */
                case TBLOCK_SUBEND:
-                       CommitSubTransaction();
-                       PopTransaction();
-                       s = CurrentTransactionState;            /* changed by pop */
+                       do {
+                               CommitSubTransaction();
+                               PopTransaction();
+                               s = CurrentTransactionState; /* changed by pop */
+                       } while (s->blockState == TBLOCK_SUBEND);
                        break;
 
                        /*
 ReleaseSavepoint(List *options)
 {
        TransactionState        s = CurrentTransactionState;
-       TransactionState        target = s;
-       char                       *name = NULL;
+       TransactionState target,
+                                        xact;
        ListCell                   *cell;
+       char                       *name = NULL;
 
        /*
         * Check valid block state transaction status.
 
        Assert(PointerIsValid(name));
 
-       while (target != NULL)
+       for (target = s; PointerIsValid(target); target = target->parent)
        {
                if (PointerIsValid(target->name) && strcmp(target->name, name) == 0)
                        break;
-               target = target->parent;
        }
 
        if (!PointerIsValid(target))
                                (errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
                                 errmsg("no such savepoint")));
 
-       CommitTransactionToLevel(target->nestingLevel);
+       /* disallow crossing savepoint level boundaries */
+       if (target->savepointLevel != s->savepointLevel)
+               ereport(ERROR,
+                               (errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
+                                errmsg("no such savepoint")));
+
+       /*
+        * Mark "commit pending" all subtransactions up to the target
+        * subtransaction.  The actual commits will happen when control
+        * gets to CommitTransactionCommand.
+        */
+       xact = CurrentTransactionState;
+       for (;;)
+       {
+               Assert(xact->blockState == TBLOCK_SUBINPROGRESS);
+               xact->blockState = TBLOCK_SUBEND;
+               if (xact == target)
+                       break;
+               xact = xact->parent;
+               Assert(PointerIsValid(xact));
+       }
 }
 
 /*
 
        CallXactCallbacks(XACT_EVENT_COMMIT_SUB, s->parent->transactionIdData);
 
-       /*
-        * Note that we just release the resource owner's resources and don't
-        * delete it.  This is because locks are not actually released here.
-        * The owner object continues to exist as a child of its parent owner
-        * (namely my parent transaction's resource owner), and the locks
-        * effectively become that owner object's responsibility.
-        */
        ResourceOwnerRelease(s->curTransactionOwner,
                                                 RESOURCE_RELEASE_BEFORE_LOCKS,
                                                 true, false);
        AtEOSubXact_Inval(true);
-       /* we can skip the LOCKS phase */
+       ResourceOwnerRelease(s->curTransactionOwner,
+                                                RESOURCE_RELEASE_LOCKS,
+                                                true, false);
        ResourceOwnerRelease(s->curTransactionOwner,
                                                 RESOURCE_RELEASE_AFTER_LOCKS,
                                                 true, false);
 
        CurrentResourceOwner = s->parent->curTransactionOwner;
        CurTransactionResourceOwner = s->parent->curTransactionOwner;
+       ResourceOwnerDelete(s->curTransactionOwner);
        s->curTransactionOwner = NULL;
 
        AtSubCommit_Memory();
 
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.68 2004/08/02 21:42:18 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.69 2004/08/25 18:43:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
                ResourceOwnerRelease(portal->resowner,
                                                         RESOURCE_RELEASE_AFTER_LOCKS,
                                                         isCommit, false);
-               if (!isCommit)
-                       ResourceOwnerDelete(portal->resowner);
+               ResourceOwnerDelete(portal->resowner);
        }
        portal->resowner = NULL;
 
 
-$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.2 2004/07/31 00:45:40 tgl Exp $
+$PostgreSQL: pgsql/src/backend/utils/resowner/README,v 1.3 2004/08/25 18:43:43 tgl Exp $
 
 Notes about resource owners
 ---------------------------
 When a Portal is closed, any remaining resources (typically only locks)
 become the responsibility of the current transaction.  This is represented
 by making the Portal's ResourceOwner a child of the current transaction's
-ResourceOwner.  Similarly, subtransaction ResourceOwners are children of
-their immediate parent.
+ResourceOwner.  resowner.c automatically transfers the resources to the
+parent object when releasing the child.  Similarly, subtransaction
+ResourceOwners are children of their immediate parent.
 
 We need transaction-related ResourceOwners as well as Portal-related ones
 because transactions may initiate operations that require resources (such
 * delete a ResourceOwner (including child owner objects); all resources
   must have been released beforehand
 
+Locks are handled specially because in non-error situations a lock should
+be held until end of transaction, even if it was originally taken by a
+subtransaction or portal.  Therefore, the "release" operation on a child
+ResourceOwner transfers lock ownership to the parent instead of actually
+releasing the lock, if isCommit is true.
+
 Currently, ResourceOwners contain direct support for recording ownership
 of buffer pins, lmgr locks, and catcache and relcache references.  Other
 objects can be associated with a ResourceOwner by recording the address of
 
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.2 2004/07/31 00:45:40 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/resowner/resowner.c,v 1.3 2004/08/25 18:43:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
                        /* Mark object as holding no locks, just for sanity */
                        owner->nlocks = 0;
                }
-               else if (!isCommit)
+               else
                {
                        /*
                         * Release locks retail.  Note that LockRelease will remove
                         * the lock entry from my list, so I just have to iterate till
                         * there are none.  Also note that if we are committing a
-                        * subtransaction, we do NOT release its locks yet.
+                        * subtransaction, we do NOT release its locks yet, but transfer
+                        * them to the parent.
                         *
                         * XXX as above, this is a bit inefficient but probably not worth
                         * the trouble to optimize more.
                         */
+                       Assert(owner->parent != NULL);
                        while (owner->nlocks > 0)
                        {
                                LockIdData *lockid = &owner->locks[owner->nlocks - 1];
 
-                               LockRelease(lockid->locktag.lockmethodid,
-                                                       &lockid->locktag,
-                                                       lockid->xid,
-                                                       lockid->lockmode);
+                               if (isCommit)
+                               {
+                                       ResourceOwnerEnlargeLocks(owner->parent);
+                                       ResourceOwnerRememberLock(owner->parent,
+                                                                                         &lockid->locktag,
+                                                                                         lockid->xid,
+                                                                                         lockid->lockmode);
+                                       owner->nlocks--;
+                               }
+                               else
+                               {
+                                       LockRelease(lockid->locktag.lockmethodid,
+                                                               &lockid->locktag,
+                                                               lockid->xid,
+                                                               lockid->lockmode);
+                                       /* LockRelease will have removed the entry from list */
+                               }
                        }
                }
        }