diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/access/transam/clog.c 09withList/src/backend/access/transam/clog.c
*** 00orig/src/backend/access/transam/clog.c	2003-12-18 00:56:09.000000000 -0300
--- 09withList/src/backend/access/transam/clog.c	2004-05-26 11:41:16.000000000 -0400
***************
*** 95,114 ****
  	char	   *byteptr;
  
  	Assert(status == TRANSACTION_STATUS_COMMITTED ||
! 		   status == TRANSACTION_STATUS_ABORTED);
  
  	LWLockAcquire(ClogCtl->locks->ControlLock, LW_EXCLUSIVE);
  
  	byteptr = SimpleLruReadPage(ClogCtl, pageno, xid, true);
  	byteptr += byteno;
  
! 	/* Current state should be 0 or target state */
  	Assert(((*byteptr >> bshift) & CLOG_XACT_BITMASK) == 0 ||
  		   ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status);
  
  	*byteptr |= (status << bshift);
  
! 	/* ...->page_status[slotno] = CLOG_PAGE_DIRTY; already done */
  
  	LWLockRelease(ClogCtl->locks->ControlLock);
  }
--- 95,116 ----
  	char	   *byteptr;
  
  	Assert(status == TRANSACTION_STATUS_COMMITTED ||
! 		   status == TRANSACTION_STATUS_ABORTED ||
! 		   status == TRANSACTION_STATUS_SUB_COMMITTED);
  
  	LWLockAcquire(ClogCtl->locks->ControlLock, LW_EXCLUSIVE);
  
  	byteptr = SimpleLruReadPage(ClogCtl, pageno, xid, true);
  	byteptr += byteno;
  
! 	/* Current state should be 0, subcommitted or target state */
  	Assert(((*byteptr >> bshift) & CLOG_XACT_BITMASK) == 0 ||
+ 		   ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == TRANSACTION_STATUS_SUB_COMMITTED ||
  		   ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status);
  
  	*byteptr |= (status << bshift);
  
! 	/* ...->page_status[slotno] = SLRU_PAGE_DIRTY; already done */
  
  	LWLockRelease(ClogCtl->locks->ControlLock);
  }
***************
*** 333,383 ****
  	rdata.data = (char *) (&pageno);
  	rdata.len = sizeof(int);
  	rdata.next = NULL;
! 	(void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
  }
  
! /*
!  * CLOG resource manager's routines
!  */
  void
! clog_redo(XLogRecPtr lsn, XLogRecord *record)
  {
! 	uint8		info = record->xl_info & ~XLR_INFO_MASK;
! 
! 	if (info == CLOG_ZEROPAGE)
! 	{
! 		int			pageno;
! 		int			slotno;
! 
! 		memcpy(&pageno, XLogRecGetData(record), sizeof(int));
! 
! 		LWLockAcquire(ClogCtl->locks->ControlLock, LW_EXCLUSIVE);
! 
! 		slotno = ZeroCLOGPage(pageno, false);
! 		SimpleLruWritePage(ClogCtl, slotno);
! 		/* Assert(ClogCtl->page_status[slotno] == SLRU_PAGE_CLEAN); */
! 
! 		LWLockRelease(ClogCtl->locks->ControlLock);
! 	}
! }
  
! void
! clog_undo(XLogRecPtr lsn, XLogRecord *record)
! {
! }
  
! void
! clog_desc(char *buf, uint8 xl_info, char *rec)
! {
! 	uint8		info = xl_info & ~XLR_INFO_MASK;
  
! 	if (info == CLOG_ZEROPAGE)
! 	{
! 		int			pageno;
! 
! 		memcpy(&pageno, rec, sizeof(int));
! 		sprintf(buf + strlen(buf), "zeropage: %d", pageno);
! 	}
! 	else
! 		strcat(buf, "UNKNOWN");
  }
--- 335,354 ----
  	rdata.data = (char *) (&pageno);
  	rdata.len = sizeof(int);
  	rdata.next = NULL;
! 	(void) XLogInsert(RM_SLRU_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
  }
  
! /* XLog support */
  void
! clog_zeropage_redo(int pageno)
  {
! 	int			slotno;
  
! 	LWLockAcquire(ClogCtl->locks->ControlLock, LW_EXCLUSIVE);
  
! 	slotno = ZeroCLOGPage(pageno, false);
! 	SimpleLruWritePage(ClogCtl, slotno);
! 	/* Assert(ClogCtl->page_status[slotno] == SLRU_PAGE_CLEAN); */
  
! 	LWLockRelease(ClogCtl->locks->ControlLock);
  }
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/access/transam/Makefile 09withList/src/backend/access/transam/Makefile
*** 00orig/src/backend/access/transam/Makefile	2003-12-18 00:56:09.000000000 -0300
--- 09withList/src/backend/access/transam/Makefile	2004-05-26 11:41:16.000000000 -0400
***************
*** 12,18 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o
  
  all: SUBSYS.o
  
--- 12,18 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global
  
! OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o subtrans.o
  
  all: SUBSYS.o
  
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/access/transam/rmgr.c 09withList/src/backend/access/transam/rmgr.c
*** 00orig/src/backend/access/transam/rmgr.c	2003-12-18 00:56:09.000000000 -0300
--- 09withList/src/backend/access/transam/rmgr.c	2004-05-26 11:41:16.000000000 -0400
***************
*** 7,18 ****
   */
  #include "postgres.h"
  
- #include "access/clog.h"
  #include "access/gist.h"
  #include "access/hash.h"
  #include "access/heapam.h"
  #include "access/nbtree.h"
  #include "access/rtree.h"
  #include "access/xact.h"
  #include "access/xlog.h"
  #include "storage/smgr.h"
--- 7,18 ----
   */
  #include "postgres.h"
  
  #include "access/gist.h"
  #include "access/hash.h"
  #include "access/heapam.h"
  #include "access/nbtree.h"
  #include "access/rtree.h"
+ #include "access/slru.h"
  #include "access/xact.h"
  #include "access/xlog.h"
  #include "storage/smgr.h"
***************
*** 23,29 ****
  	{"XLOG", xlog_redo, xlog_undo, xlog_desc, NULL, NULL},
  	{"Transaction", xact_redo, xact_undo, xact_desc, NULL, NULL},
  	{"Storage", smgr_redo, smgr_undo, smgr_desc, NULL, NULL},
! 	{"CLOG", clog_redo, clog_undo, clog_desc, NULL, NULL},
  	{"Reserved 4", NULL, NULL, NULL, NULL, NULL},
  	{"Reserved 5", NULL, NULL, NULL, NULL, NULL},
  	{"Reserved 6", NULL, NULL, NULL, NULL, NULL},
--- 23,29 ----
  	{"XLOG", xlog_redo, xlog_undo, xlog_desc, NULL, NULL},
  	{"Transaction", xact_redo, xact_undo, xact_desc, NULL, NULL},
  	{"Storage", smgr_redo, smgr_undo, smgr_desc, NULL, NULL},
! 	{"SLRU", slru_redo, slru_undo, slru_desc, NULL, NULL},
  	{"Reserved 4", NULL, NULL, NULL, NULL, NULL},
  	{"Reserved 5", NULL, NULL, NULL, NULL, NULL},
  	{"Reserved 6", NULL, NULL, NULL, NULL, NULL},
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/access/transam/slru.c 09withList/src/backend/access/transam/slru.c
*** 00orig/src/backend/access/transam/slru.c	2004-03-21 13:54:39.000000000 -0400
--- 09withList/src/backend/access/transam/slru.c	2004-05-26 11:41:16.000000000 -0400
***************
*** 929,931 ****
--- 929,983 ----
  
  	return found;
  }
+ 
+ /*
+  * SLRU resource manager's routines
+  */
+ void
+ slru_redo(XLogRecPtr lsn, XLogRecord *record)
+ {
+ 	uint8		info = record->xl_info & ~XLR_INFO_MASK;
+ 	int			pageno;
+ 
+ 	memcpy(&pageno, XLogRecGetData(record), sizeof(int));
+ 
+ 	switch (info)
+ 	{
+ 		case CLOG_ZEROPAGE:
+ 			clog_zeropage_redo(pageno);
+ 			break;
+ 		case SUBTRANS_ZEROPAGE:
+ 			subtrans_zeropage_redo(pageno);
+ 			break;
+ 		default:
+ 			elog(PANIC, "slru_redo: unknown op code %u", info);
+ 	}
+ }
+ 
+ void
+ slru_undo(XLogRecPtr lsn, XLogRecord *record)
+ {
+ }
+ 
+ void
+ slru_desc(char *buf, uint8 xl_info, char *rec)
+ {
+ 	uint8		info = xl_info & ~XLR_INFO_MASK;
+ 
+ 	if (info == CLOG_ZEROPAGE)
+ 	{
+ 		int			pageno;
+ 
+ 		memcpy(&pageno, rec, sizeof(int));
+ 		sprintf(buf + strlen(buf), "zeropage: %d", pageno);
+ 	}
+ 	else if (info == SUBTRANS_ZEROPAGE)
+ 	{
+ 		int			pageno;
+ 
+ 		memcpy(&pageno, rec, sizeof(int));
+ 		sprintf(buf + strlen(buf), "zeropage: %d", pageno);
+ 	}
+ 	else
+ 		strcat(buf, "UNKNOWN");
+ }
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/access/transam/subtrans.c 09withList/src/backend/access/transam/subtrans.c
*** 00orig/src/backend/access/transam/subtrans.c	1969-12-31 21:00:00.000000000 -0300
--- 09withList/src/backend/access/transam/subtrans.c	2004-05-26 11:53:55.000000000 -0400
***************
*** 0 ****
--- 1,385 ----
+ /*-------------------------------------------------------------------------
+  *
+  * subtrans.c
+  *		PostgreSQL subtrans-log manager
+  *
+  * The pg_subtrans manager is a pg_clog-like manager which stores the parent
+  * transaction Id for each transaction.  It is a fundamental part of the
+  * nested transactions implementation.  A main transaction has a parent
+  * of InvalidTransactionId, and each subtransaction has its immediate parent.
+  * The tree can easily be walked from child to parent.
+  *
+  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * $PostgreSQL$
+  *
+  *-------------------------------------------------------------------------
+  */
+ #include "postgres.h"
+ 
+ #include <fcntl.h>
+ #include <dirent.h>
+ #include <errno.h>
+ #include <sys/stat.h>
+ #include <unistd.h>
+ 
+ #include "access/subtrans.h"
+ #include "access/slru.h"
+ #include "access/transam.h"
+ #include "storage/lwlock.h"
+ #include "miscadmin.h"
+ 
+ 
+ /*
+  * Defines for SubTrans page and segment sizes.  A page is the same BLCKSZ
+  * as is used everywhere else in Postgres.
+  *
+  * Note: because TransactionIds are 32 bits and wrap around at 0xFFFFFFFF,
+  * SubTrans page numbering also wraps around at 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE,
+  * and segment numbering at 0xFFFFFFFF/SUBTRANS_XACTS_PER_PAGE/SLRU_SEGMENTS_PER_PAGE.
+  * We need take no explicit notice of that fact in this module, except when
+  * comparing segment and page numbers in TruncateSubTrans (see SubTransPagePrecedes).
+  */
+ 
+ /* We need four bytes per xact */
+ #define SUBTRANS_XACTS_PER_PAGE (BLCKSZ / sizeof(TransactionId))
+ 
+ #define TransactionIdToPage(xid) ((xid) / (TransactionId) SUBTRANS_XACTS_PER_PAGE)
+ #define TransactionIdToByte(xid) (sizeof(TransactionId) * ((xid) % (TransactionId) SUBTRANS_XACTS_PER_PAGE))
+ 
+ /*----------
+  * Shared-memory data structures for SUBTRANS control
+  *
+  * XLOG interactions: this module generates an XLOG record whenever a new
+  * SUBTRANS page is initialized to zeroes.	Other writes of SUBTRANS come from
+  * recording of transaction commit or abort in xact.c, which generates its
+  * own XLOG records for these events and will re-perform the status update
+  * on redo; so we need make no additional XLOG entry here.	Also, the XLOG
+  * is guaranteed flushed through the XLOG commit record before we are called
+  * to log a commit, so the WAL rule "write xlog before data" is satisfied
+  * automatically for commits, and we don't really care for aborts.  Therefore,
+  * we don't need to mark XLOG pages with LSN information; we have enough
+  * synchronization already.
+  *----------
+  */
+ 
+ 
+ static SlruCtlData SubTransCtlData;
+ static SlruCtl SubTransCtl = &SubTransCtlData;
+ 
+ 
+ static int	ZeroSubTransPage(int pageno, bool writeXlog);
+ static bool SubTransPagePrecedes(int page1, int page2);
+ static void WriteZeroPageXlogRec(int pageno);
+ 
+ 
+ /*
+  * Record the parent of a subtransaction in the subtrans log.
+  */
+ void
+ SubTransSetParent(TransactionId xid, TransactionId parent)
+ {
+ 	int			pageno = TransactionIdToPage(xid);
+ 	int			byteno = TransactionIdToByte(xid);
+ 	char	   *byteptr;
+ 
+ 	LWLockAcquire(SubTransCtl->locks->ControlLock, LW_EXCLUSIVE);
+ 
+ 	byteptr = SimpleLruReadPage(SubTransCtl, pageno, xid, true);
+ 	byteptr += byteno;
+ 
+ 	/* XXX We should Assert() the previous parent Xid is zero */
+ 
+ 	memcpy(byteptr, (void *)&parent, sizeof(TransactionId));
+ 
+ 	/* ...->page_status[slotno] = SLRU_PAGE_DIRTY; already done */
+ 
+ 	LWLockRelease(SubTransCtl->locks->ControlLock);
+ }
+ 
+ /*
+  * Interrogate the parent of a transaction in the subtrans log.
+  */
+ TransactionId
+ SubTransGetParent(TransactionId xid)
+ {
+ 	int			pageno = TransactionIdToPage(xid);
+ 	int			byteno = TransactionIdToByte(xid);
+ 	char	   *byteptr;
+ 	TransactionId	parent;
+ 
+ 	LWLockAcquire(SubTransCtl->locks->ControlLock, LW_EXCLUSIVE);
+ 
+ 	byteptr = SimpleLruReadPage(SubTransCtl, pageno, xid, false);
+ 	byteptr += byteno;
+ 
+ 	memcpy((void *)&parent, byteptr, sizeof(TransactionId));
+ 
+ 	LWLockRelease(SubTransCtl->locks->ControlLock);
+ 
+ 	return parent;
+ }
+ 
+ /*
+  * SubTransGetTopmostTransaction
+  *
+  * Returns the topmost transaction of the given transaction id.
+  */
+ TransactionId
+ SubTransGetTopmostTransaction(TransactionId xid)
+ {
+ 	TransactionId parentXid = xid,
+ 				  previousXid = xid;
+ 
+ 	while (TransactionIdIsValid(parentXid))
+ 	{
+ 		previousXid = parentXid;
+ 		parentXid = SubTransGetParent(parentXid);
+ 	}
+ 	return previousXid;
+ }
+ 
+ 
+ /*
+  * SubTransXidsHaveCommonAncestor
+  *
+  * Returns true iff the Xids have a common ancestor
+  */
+ bool
+ SubTransXidsHaveCommonAncestor(TransactionId xid1, TransactionId xid2)
+ {
+ 	TransactionId tmp1, tmp2;
+ 	if (TransactionIdEquals(xid1, xid2))
+ 		return true;
+ 
+ 	tmp1 = xid1;
+ 	tmp2 = xid2;
+ 
+ 	for (;;)
+ 	{
+ 		if (TransactionIdPrecedes(tmp2, tmp1))
+ 			tmp1 = SubTransGetParent(tmp1);
+ 		else
+ 			tmp2 = SubTransGetParent(tmp2);
+ 
+ 		if (!TransactionIdIsValid(tmp1) || !TransactionIdIsValid(tmp2))
+ 			return false;
+ 		if (TransactionIdEquals(tmp1, tmp2))
+ 			return true;
+ 	}
+ 
+ 	/* never reached */
+ 	return false;
+ }
+ 
+ /*
+  * Initialization of shared memory for Subtrans
+  */
+ 
+ int
+ SUBTRANSShmemSize(void)
+ {
+ 	return SimpleLruShmemSize();
+ }
+ 
+ void
+ SUBTRANSShmemInit(void)
+ {
+ 	SimpleLruInit(SubTransCtl, "SUBTRANS Ctl", "pg_subtrans");
+ 	SubTransCtl->PagePrecedes = SubTransPagePrecedes;
+ }
+ 
+ /*
+  * This func must be called ONCE on system install.  It creates
+  * the initial SubTrans segment.  (The SubTrans directory is assumed to
+  * have been created by initdb, and SubTransShmemInit must have been called
+  * already.)
+  */
+ void
+ BootStrapSUBTRANS(void)
+ {
+ 	int			slotno;
+ 
+ 	LWLockAcquire(SubTransCtl->locks->ControlLock, LW_EXCLUSIVE);
+ 
+ 	/* Create and zero the first page of the commit log */
+ 	slotno = ZeroSubTransPage(0, false);
+ 
+ 	/* Make sure it's written out */
+ 	SimpleLruWritePage(SubTransCtl, slotno);
+ 
+ 	LWLockRelease(SubTransCtl->locks->ControlLock);
+ }
+ 
+ /*
+  * Initialize (or reinitialize) a page of SubTrans to zeroes.
+  * If writeXlog is TRUE, also emit an XLOG record saying we did this.
+  *
+  * The page is not actually written, just set up in shared memory.
+  * The slot number of the new page is returned.
+  *
+  * Control lock must be held at entry, and will be held at exit.
+  */
+ static int
+ ZeroSubTransPage(int pageno, bool writeXlog)
+ {
+ 	int			slotno = SimpleLruZeroPage(SubTransCtl, pageno);
+ 
+ 	if (writeXlog)
+ 		WriteZeroPageXlogRec(pageno);
+ 
+ 	return slotno;
+ }
+ 
+ /*
+  * This must be called ONCE during postmaster or standalone-backend startup,
+  * after StartupXLOG has initialized ShmemVariableCache->nextXid.
+  */
+ void
+ StartupSUBTRANS(void)
+ {
+ 	TransactionIdSetSubtransCutoff(ShmemVariableCache->nextXid);
+ 	/*
+ 	 * Initialize our idea of the latest page number.
+ 	 */
+ 	SimpleLruSetLatestPage(SubTransCtl, TransactionIdToPage(ShmemVariableCache->nextXid));
+ }
+ 
+ /*
+  * This must be called ONCE during postmaster or standalone-backend shutdown
+  */
+ void
+ ShutdownSUBTRANS(void)
+ {
+ 	SimpleLruFlush(SubTransCtl, false);
+ }
+ 
+ /*
+  * Perform a checkpoint --- either during shutdown, or on-the-fly
+  */
+ void
+ CheckPointSUBTRANS(void)
+ {
+ 	SimpleLruFlush(SubTransCtl, true);
+ }
+ 
+ 
+ /*
+  * Make sure that SubTrans has room for a newly-allocated XID.
+  *
+  * NB: this is called while holding XidGenLock.  We want it to be very fast
+  * most of the time; even when it's not so fast, no actual I/O need happen
+  * unless we're forced to write out a dirty subtrans or xlog page to make room
+  * in shared memory.
+  */
+ void
+ ExtendSUBTRANS(TransactionId newestXact)
+ {
+ 	int			pageno;
+ 
+ 	/*
+ 	 * No work except at first XID of a page.  But beware: just after
+ 	 * wraparound, the first XID of page zero is FirstNormalTransactionId.
+ 	 */
+ 	if (TransactionIdToByte(newestXact) != 0 &&
+ 		!TransactionIdEquals(newestXact, FirstNormalTransactionId))
+ 		return;
+ 
+ 	pageno = TransactionIdToPage(newestXact);
+ 
+ 	LWLockAcquire(SubTransCtl->locks->ControlLock, LW_EXCLUSIVE);
+ 
+ 	/* Zero the page and make an XLOG entry about it */
+ 	ZeroSubTransPage(pageno, true);
+ 
+ 	LWLockRelease(SubTransCtl->locks->ControlLock);
+ }
+ 
+ 
+ /*
+  * Remove all SubTrans segments before the one holding the passed transaction ID
+  *
+  * When this is called, we know that the database logically contains no
+  * reference to transaction IDs older than oldestXact.	However, we must
+  * not truncate the SubTrans until we have performed a checkpoint, to ensure
+  * that no such references remain on disk either; else a crash just after
+  * the truncation might leave us with a problem.  Since SubTrans segments hold
+  * a large number of transactions, the opportunity to actually remove a
+  * segment is fairly rare, and so it seems best not to do the checkpoint
+  * unless we have confirmed that there is a removable segment.	Therefore
+  * we issue the checkpoint command here, not in higher-level code as might
+  * seem cleaner.
+  */
+ void
+ TruncateSUBTRANS(TransactionId oldestXact)
+ {
+ 	int			cutoffPage;
+ 
+ 	/*
+ 	 * The cutoff point is the start of the segment containing oldestXact.
+ 	 * We pass the *page* containing oldestXact to SimpleLruTruncate.
+ 	 */
+ 	cutoffPage = TransactionIdToPage(oldestXact);
+ 	SimpleLruTruncate(SubTransCtl, cutoffPage);
+ 
+ 	TransactionIdSetSubtransCutoff(oldestXact);
+ }
+ 
+ 
+ /*
+  * Decide which of two SubTrans page numbers is "older" for truncation purposes.
+  *
+  * We need to use comparison of TransactionIds here in order to do the right
+  * thing with wraparound XID arithmetic.  However, if we are asked about
+  * page number zero, we don't want to hand InvalidTransactionId to
+  * TransactionIdPrecedes: it'll get weird about permanent xact IDs.  So,
+  * offset both xids by FirstNormalTransactionId to avoid that.
+  */
+ static bool
+ SubTransPagePrecedes(int page1, int page2)
+ {
+ 	TransactionId xid1;
+ 	TransactionId xid2;
+ 
+ 	xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
+ 	xid1 += FirstNormalTransactionId;
+ 	xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
+ 	xid2 += FirstNormalTransactionId;
+ 
+ 	return TransactionIdPrecedes(xid1, xid2);
+ }
+ 
+ 
+ /*
+  * Write a ZEROPAGE xlog record
+  *
+  * Note: xlog record is marked as outside transaction control, since we
+  * want it to be redone whether the invoking transaction commits or not.
+  * (Besides which, this is normally done just before entering a transaction.)
+  */
+ static void
+ WriteZeroPageXlogRec(int pageno)
+ {
+ 	XLogRecData rdata;
+ 
+ 	rdata.buffer = InvalidBuffer;
+ 	rdata.data = (char *) (&pageno);
+ 	rdata.len = sizeof(int);
+ 	rdata.next = NULL;
+ 	(void) XLogInsert(RM_SLRU_ID, SUBTRANS_ZEROPAGE | XLOG_NO_TRAN, &rdata);
+ }
+ 
+ /* XLog support */
+ void
+ subtrans_zeropage_redo(int pageno)
+ {
+ 	int			slotno;
+ 
+ 	LWLockAcquire(SubTransCtl->locks->ControlLock, LW_EXCLUSIVE);
+ 
+ 	slotno = ZeroSubTransPage(pageno, false);
+ 	SimpleLruWritePage(SubTransCtl, slotno);
+ 
+ 	LWLockRelease(SubTransCtl->locks->ControlLock);
+ }
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/access/transam/transam.c 09withList/src/backend/access/transam/transam.c
*** 00orig/src/backend/access/transam/transam.c	2003-12-18 00:56:09.000000000 -0300
--- 09withList/src/backend/access/transam/transam.c	2004-05-26 11:41:16.000000000 -0400
***************
*** 20,25 ****
--- 20,26 ----
  #include "postgres.h"
  
  #include "access/clog.h"
+ #include "access/subtrans.h"
  #include "access/transam.h"
  
  
***************
*** 46,51 ****
--- 47,57 ----
  static TransactionId cachedTestXid = InvalidTransactionId;
  static XidStatus cachedTestXidStatus;
  
+ /*
+  * We use this to update transactions whose parents are long committed.
+  * After this point, there's no need to check Subtrans again.
+  */
+ static TransactionId SubtransCutoffXid;
  
  /* ----------------------------------------------------------------
   *		postgres log access method interface
***************
*** 123,128 ****
--- 129,159 ----
  	cachedTestXidStatus = status;
  }
  
+ /*
+  * TransactionLogMultiUpdate
+  *
+  * Update multiple transaction identifiers to a given status.
+  * Don't depend on this being atomic; it's not.
+  */
+ static void
+ TransactionLogMultiUpdate(int nchilds, TransactionId *childs,
+ 		XidStatus status)
+ {
+ 	int i;
+ 
+ 	Assert(nchilds != 0);
+ 
+ 	for (i=0; i < nchilds; i++)
+ 		TransactionIdSetStatus(childs[i], status);
+ 
+ 	/*
+ 	 * Update our TransactionLogTest cache.  We use the first one
+ 	 * because presumably it's a top level transaction.
+ 	 */
+ 	TransactionIdStore(childs[0], &cachedTestXid);
+ 	cachedTestXidStatus = status;
+ }
+ 
  /* --------------------------------
   *		AmiTransactionOverride
   *
***************
*** 177,183 ****
  		return true;
  	}
  
! 	return TransactionLogTest(transactionId, TRANSACTION_STATUS_COMMITTED);
  }
  
  /*
--- 208,245 ----
  		return true;
  	}
  
! 	/*
! 	 * If it's marked committed, it's committed.
! 	 */
! 	if (TransactionLogTest(transactionId, TRANSACTION_STATUS_COMMITTED))
! 		return true;
! 
! 	/*
! 	 * If it's marked subcommitted, we have to check the parent recursively.
! 	 */
! 	if (TransactionLogTest(transactionId, TRANSACTION_STATUS_SUB_COMMITTED))
! 	{
! 		TransactionId parentXid;
! 		bool parentCommitted;
! 	   
! 		parentXid = SubTransGetParent(transactionId);
! 		parentCommitted = TransactionIdDidCommit(parentXid);
! 
! 		/*
! 		 * Optimization: if the parent is committed and the given transaction Id
! 		 * is earlier than the cutoff point, mark it as committed.  Future checks
! 		 * will not have to look at Subtrans again.
! 		 */
! 		if (parentCommitted && TransactionIdPrecedes(transactionId, SubtransCutoffXid))
! 			TransactionIdCommit(transactionId);
! 
! 		return parentCommitted;
! 	}
! 
! 	/* 
! 	 * It's not committed.
! 	 */
! 	return false;
  }
  
  /*
***************
*** 196,224 ****
  		return false;
  	}
  
! 	return TransactionLogTest(transactionId, TRANSACTION_STATUS_ABORTED);
! }
  
! /*
!  * Now this func in shmem.c and gives quality answer by scanning
!  * PGPROC structures of all running backend. - vadim 11/26/96
!  *
!  * Old comments:
!  * true if given transaction has neither committed nor aborted
!  */
! #ifdef NOT_USED
! bool
! TransactionIdIsInProgress(TransactionId transactionId)
! {
! 	if (AMI_OVERRIDE)
  	{
! 		Assert(transactionId == BootstrapTransactionId);
! 		return false;
  	}
  
! 	return TransactionLogTest(transactionId, TRANSACTION_STATUS_IN_PROGRESS);
  }
- #endif   /* NOT_USED */
  
  /* --------------------------------
   *		TransactionId Commit
--- 258,288 ----
  		return false;
  	}
  
! 	/*
! 	 * If it's marked aborted, it's aborted.
! 	 */
! 	if (TransactionLogTest(transactionId, TRANSACTION_STATUS_ABORTED))
! 		return true;
  
! 	/*
! 	 * If it's marked subcommitted, we have to check the parent recursively.
! 	 * 
! 	 * There's no need to do the same check as in TransactionIdDidCommit,
! 	 * because an aborted subtransaction will be marked as aborted
! 	 * immediately.
! 	 */
! 	if (TransactionLogTest(transactionId, TRANSACTION_STATUS_SUB_COMMITTED))
  	{
! 		TransactionId parentXid = SubTransGetParent(transactionId);
! 
! 		return TransactionIdDidAbort(parentXid);
  	}
  
! 	/*
! 	 * It's not aborted.
! 	 */
! 	return false;
  }
  
  /* --------------------------------
   *		TransactionId Commit
***************
*** 252,257 ****
--- 316,357 ----
  	TransactionLogUpdate(transactionId, TRANSACTION_STATUS_ABORTED);
  }
  
+ /*
+  * TrasactionIdSubCommit
+  *		Marks the subtransaction associated with the identifier as
+  *		committed.
+  */
+ void
+ TransactionIdSubCommit(TransactionId transactionId)
+ {
+ 	TransactionLogUpdate(transactionId, TRANSACTION_STATUS_SUB_COMMITTED);
+ }
+ 
+ /*
+  * TransactionIdSubAbort
+  *		Marks the subtransaction and all its children as aborted
+  */
+ void
+ TransactionIdSubAbort(int nchilds, TransactionId *childs)
+ {
+ 	if (nchilds == 0)
+ 		return;
+ 
+ 	TransactionLogMultiUpdate(nchilds, childs, TRANSACTION_STATUS_ABORTED);
+ }
+ 
+ /* TransactionIdCommitTree
+  *		Marks all the transaction ids as committed.  This can only be done
+  *		at XLog recovery.
+  */
+ void
+ TransactionIdCommitTree(int nchilds, TransactionId *childs)
+ {
+ 	if (nchilds == 0)
+ 		return;
+ 
+ 	TransactionLogMultiUpdate(nchilds, childs, TRANSACTION_STATUS_COMMITTED);
+ }
  
  /*
   * TransactionIdPrecedes --- is id1 logically < id2?
***************
*** 316,318 ****
--- 416,427 ----
  	diff = (int32) (id1 - id2);
  	return (diff >= 0);
  }
+ 
+ /*
+  * TransactionIdSetSubtransCutoff
+  */
+ void
+ TransactionIdSetSubtransCutoff(TransactionId xid)
+ {
+ 	SubtransCutoffXid = xid;
+ }
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/access/transam/varsup.c 09withList/src/backend/access/transam/varsup.c
*** 00orig/src/backend/access/transam/varsup.c	2004-03-21 13:54:39.000000000 -0400
--- 09withList/src/backend/access/transam/varsup.c	2004-05-26 11:41:16.000000000 -0400
***************
*** 15,20 ****
--- 15,21 ----
  
  #include "access/clog.h"
  #include "access/transam.h"
+ #include "access/subtrans.h"
  #include "storage/ipc.h"
  #include "storage/proc.h"
  
***************
*** 30,36 ****
   * Allocate the next XID for my new transaction.
   */
  TransactionId
! GetNewTransactionId(void)
  {
  	TransactionId xid;
  
--- 31,37 ----
   * Allocate the next XID for my new transaction.
   */
  TransactionId
! GetNewTransactionId(bool isSubXact)
  {
  	TransactionId xid;
  
***************
*** 52,59 ****
--- 53,63 ----
  	 * commit a later XID before we zero the page.	Fortunately, a page of
  	 * the commit log holds 32K or more transactions, so we don't have to
  	 * do this very often.
+ 	 *
+ 	 * Extend pg_subtrans too.
  	 */
  	ExtendCLOG(xid);
+ 	ExtendSUBTRANS(xid);
  
  	/*
  	 * Now advance the nextXid counter.  This must not happen until after
***************
*** 82,89 ****
  	 * its own spinlock used only for fetching/storing that PGPROC's xid.
  	 * (SInvalLock would then mean primarily that PGPROCs couldn't be added/
  	 * removed while holding the lock.)
  	 */
! 	if (MyProc != NULL)
  		MyProc->xid = xid;
  
  	LWLockRelease(XidGenLock);
--- 86,96 ----
  	 * its own spinlock used only for fetching/storing that PGPROC's xid.
  	 * (SInvalLock would then mean primarily that PGPROCs couldn't be added/
  	 * removed while holding the lock.)
+ 	 *
+ 	 * We don't want a subtransaction to update the stored Xid; we'll check
+ 	 * if a transaction Xid is a running subxact by checking pg_subtrans.
  	 */
! 	if (MyProc != NULL && !isSubXact)
  		MyProc->xid = xid;
  
  	LWLockRelease(XidGenLock);
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/access/transam/xact.c 09withList/src/backend/access/transam/xact.c
*** 00orig/src/backend/access/transam/xact.c	2004-05-23 23:31:39.000000000 -0400
--- 09withList/src/backend/access/transam/xact.c	2004-05-26 12:12:42.000000000 -0400
***************
*** 147,152 ****
--- 147,153 ----
  #include "access/hash.h"
  #include "access/nbtree.h"
  #include "access/rtree.h"
+ #include "access/subtrans.h"
  #include "access/xact.h"
  #include "catalog/heap.h"
  #include "catalog/index.h"
***************
*** 189,194 ****
--- 190,216 ----
  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 PushCurrentTransaction(void);
+ static void PopTransaction(void);
+ static bool IsSubTransaction(void);
+ static void ShowTransactionState(char *str);
+ static void ShowTransactionStateRec(TransactionState state, int indent);
+ 
+ static void AtSubAbort_Locks(void);
+ static void AtSubAbort_Memory(void);
+ static void AtSubCleanup_Memory(void);
+ static void AtSubCommit_Memory(void);
+ static void AtSubCommit_Notifies(void);
+ static void AtSubStart_Memory(void);
+ 
+ static char *BlockStateAsString(TBlockState blockState);
+ static char *TransStateAsString(TransState state);
+ 
  /*
   *	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;
--- 220,236 ----
  	0,							/* scan command id */
  	0x0,						/* start time */
  	TRANS_DEFAULT,				/* transaction state */
! 	TBLOCK_DEFAULT,				/* transaction block state from the client
  								 * perspective */
+ 	0,							/* nesting level */
+ 	NULL,						/* commit memory context */
+ 	NULL,						/* deferred trigger queue */
+ 	NIL,						/* smgr pending deletes */
+ 	NIL,						/* async notifies */
+ 	NIL,						/* committed child Xids */
+ 	NIL,						/* bufmgr refcounts */
+ 	0,							/* XLog record offset */
+ 	NULL						/* parent transaction */
  };
  
  static TransactionState CurrentTransactionState = &CurrentTransactionStateData;
***************
*** 281,287 ****
  {
  	TransactionState s = CurrentTransactionState;
  
! 	if (s->blockState == TBLOCK_ABORT)
  		return true;
  
  	return false;
--- 312,319 ----
  {
  	TransactionState s = CurrentTransactionState;
  
! 	if (s->blockState == TBLOCK_ABORT || 
! 			s->blockState == TBLOCK_SUBABORT)
  		return true;
  
  	return false;
***************
*** 311,317 ****
  	return s->commandId;
  }
  
- 
  /*
   *	GetCurrentTransactionStartTime
   */
--- 343,348 ----
***************
*** 337,342 ****
--- 368,486 ----
  	return s->startTime;
  }
  
+ /*
+  * TransactionGetPendingDeletes
+  */
+ List *
+ TransactionGetPendingDeletes(void)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 
+ 	return s->pendingDeletes;
+ }
+ 
+ /*
+  * TransactionSetPendingDeletes
+  */
+ void
+ TransactionSetPendingDeletes(List *pending)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 
+ 	s->pendingDeletes = pending;
+ }
+ 
+ /*
+  * 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;
+ }
+ 
+ /*
+  * TransactionGetNotifies
+  */
+ List *
+ TransactionGetNotifies(void)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 
+ 	return s->notifies;
+ }
+ 
+ /*
+  * TransactionSetNotifies
+  */
+ void
+ TransactionSetNotifies(List *notifies)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 
+ 	s->notifies = notifies;
+ }
+ 
+ /*
+  * 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;
+ }
+ 
+ /*
+  * TransactionSetBufferRefcounts
+  */
+ void
+ TransactionSetBufferRefcounts(List *counts)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 
+ 	s->bufRefCnts = counts;
+ }
+ 
+ /*
+  * TransactionGetBufferRefcounts
+  */
+ List *
+ TransactionGetBufferRefcounts(void)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 
+ 	return s->bufRefCnts;
+ }
  
  /*
   *	TransactionIdIsCurrentTransactionId
***************
*** 357,377 ****
  		return false;
  	}
  
! 	return TransactionIdEquals(xid, s->transactionIdData);
! }
  
  
! /*
!  *	CommandIdIsCurrentCommandId
!  */
! bool
! CommandIdIsCurrentCommandId(CommandId cid)
! {
! 	TransactionState s = CurrentTransactionState;
  
! 	return (cid == s->commandId);
! }
  
  
  /*
   *	CommandCounterIncrement
--- 501,529 ----
  		return false;
  	}
  
! 	/*
! 	 * The "current transaction Xid" is actually the Xid of the current
! 	 * subtransaction, or one of the Xids in the committed childs Xid, or
! 	 * any of those up the transaction tree.
! 	 */
  
+ 	while (s != NULL)
+ 	{
+ 		ListCell *childs;
  
! 		if (TransactionIdEquals(xid, s->transactionIdData))
! 			return true;
! 		foreach (childs, s->childXids)
! 		{
! 			if (TransactionIdEquals(xid, lfirsti(childs)))
! 				return true;
! 		}
  
! 		s = s->parent;
! 	}
  
+ 	return false;
+ }
  
  /*
   *	CommandCounterIncrement
***************
*** 436,448 ****
  static void
  AtStart_Memory(void)
  {
  	/*
  	 * We shouldn't have a transaction context already.
  	 */
  	Assert(TopTransactionContext == NULL);
  
  	/*
! 	 * Create a toplevel context for the transaction, and make it active.
  	 */
  	TopTransactionContext =
  		AllocSetContextCreate(TopMemoryContext,
--- 588,602 ----
  static void
  AtStart_Memory(void)
  {
+ 	TransactionState s = CurrentTransactionState;
+ 
  	/*
  	 * We shouldn't have a transaction context already.
  	 */
  	Assert(TopTransactionContext == NULL);
  
  	/*
! 	 * Create a toplevel context for the transaction.
  	 */
  	TopTransactionContext =
  		AllocSetContextCreate(TopMemoryContext,
***************
*** 451,459 ****
  							  ALLOCSET_DEFAULT_INITSIZE,
  							  ALLOCSET_DEFAULT_MAXSIZE);
  
! 	MemoryContextSwitchTo(TopTransactionContext);
  }
  
  
  /* ----------------------------------------------------------------
   *						CommitTransaction stuff
--- 605,658 ----
  							  ALLOCSET_DEFAULT_INITSIZE,
  							  ALLOCSET_DEFAULT_MAXSIZE);
  
! 	/*
! 	 * Create a CommitContext, which 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;
! 
! 	/* Make the CommitContext active. */
! 	MemoryContextSwitchTo(CommitContext);
  }
  
+ /* ----------------------------------------------------------------
+  *						StartSubTransaction stuff
+  * ----------------------------------------------------------------
+  */
+ 
+ /*
+  * AtSubStart_Memory
+  */
+ static void
+ AtSubStart_Memory(void)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 
+ 	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;
+ 
+ 	/* Make the CommitContext active. */
+ 	MemoryContextSwitchTo(CommitContext);
+ }
  
  /* ----------------------------------------------------------------
   *						CommitTransaction stuff
***************
*** 474,479 ****
--- 673,680 ----
  	{
  		TransactionId xid = GetCurrentTransactionId();
  		XLogRecPtr	recptr;
+ 		int			nchilds = 0;
+ 		TransactionId *childs = NULL;
  
  		/* Tell bufmgr and smgr to prepare for commit */
  		BufmgrCommit();
***************
*** 489,515 ****
  		if (MyLastRecPtr.xrecoff != 0)
  		{
  			/* Need to emit a commit record */
! 			XLogRecData rdata[2];
  			xl_xact_commit xlrec;
  			int			nrels;
  			RelFileNode *rptr;
  
  			nrels = smgrGetPendingDeletes(true, &rptr);
  
  			xlrec.xtime = time(NULL);
  			rdata[0].buffer = InvalidBuffer;
  			rdata[0].data = (char *) (&xlrec);
  			rdata[0].len = MinSizeOfXactCommit;
  			if (nrels > 0)
  			{
  				rdata[0].next = &(rdata[1]);
  				rdata[1].buffer = InvalidBuffer;
  				rdata[1].data = (char *) rptr;
  				rdata[1].len = nrels * sizeof(RelFileNode);
! 				rdata[1].next = NULL;
  			}
  			else
  				rdata[0].next = NULL;
  
  			recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT, rdata);
  
--- 690,731 ----
  		if (MyLastRecPtr.xrecoff != 0)
  		{
  			/* Need to emit a commit record */
! 			XLogRecData rdata[3];
  			xl_xact_commit xlrec;
  			int			nrels;
+ 			int			latest = 0;
  			RelFileNode *rptr;
  
  			nrels = smgrGetPendingDeletes(true, &rptr);
+ 			nchilds = xactGetCommittedChilds(&childs, false);
  
  			xlrec.xtime = time(NULL);
+ 			xlrec.nrels = nrels;
  			rdata[0].buffer = InvalidBuffer;
  			rdata[0].data = (char *) (&xlrec);
  			rdata[0].len = MinSizeOfXactCommit;
+ 			/* dump rels to delete */
  			if (nrels > 0)
  			{
  				rdata[0].next = &(rdata[1]);
  				rdata[1].buffer = InvalidBuffer;
  				rdata[1].data = (char *) rptr;
  				rdata[1].len = nrels * sizeof(RelFileNode);
! 				latest = 1;
  			}
  			else
  				rdata[0].next = NULL;
+ 			/* dump committed child Xids */
+ 			if (nchilds > 0)
+ 			{
+ 				rdata[latest].next = &(rdata[2]);
+ 				rdata[2].buffer = InvalidBuffer;
+ 				rdata[2].data = (char *)childs;
+ 				rdata[2].len = nchilds * sizeof(TransactionId);
+ 				rdata[2].next = NULL;
+ 			}
+ 			else
+ 				rdata[latest].next = NULL;
  
  			recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT, rdata);
  
***************
*** 562,567 ****
--- 778,790 ----
  		if (MyLastRecPtr.xrecoff != 0 || MyXactMadeTempRelUpdate)
  			TransactionIdCommit(xid);
  
+ 		/*
+ 		 * We can't mark committed subtransaction as committed, because
+ 		 * concurrent transactions would see them as committed and not as
+ 		 * in-progress.  Delay this until the parent transaction is below
+ 		 * OldestXmin, per VACUUM.
+ 		 */
+ 
  		END_CRIT_SECTION();
  	}
  
***************
*** 615,621 ****
  	 *
  	 * Then you're up a creek! -mer 5/24/92
  	 */
! 	ProcReleaseLocks(true);
  }
  
  /*
--- 838,844 ----
  	 *
  	 * Then you're up a creek! -mer 5/24/92
  	 */
! 	ProcReleaseLocks(ReleaseAllExceptZero, 0, NULL);
  }
  
  /*
***************
*** 637,642 ****
--- 860,935 ----
  	Assert(TopTransactionContext != NULL);
  	MemoryContextDelete(TopTransactionContext);
  	TopTransactionContext = NULL;
+ 	CommitContext = NULL;
+ }
+ 
+ /* ----------------------------------------------------------------
+  *						CommitSubTransaction stuff
+  * ----------------------------------------------------------------
+  */
+ 
+ /*
+  * AtSubCommit_Memory
+  */
+ static void
+ AtSubCommit_Memory(void)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 
+ 	Assert(TopTransactionContext != NULL);
+ 
+ 	/* Return to a known-good memory context. */
+ 	MemoryContextSwitchTo(s->parent->commitContext);
+ }
+ 
+ /*
+  * 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 = nconc(s->notifies, s->parent->notifies);
+ }
+ 
+ /*
+  * RecordSubTransactionCommit
+  */
+ void
+ RecordSubTransactionCommit(void)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 
+ 	START_CRIT_SECTION();
+ 
+ 	/* Record subtransaction commit */
+ 	TransactionIdSubCommit(s->transactionIdData);
+ 
+ 	END_CRIT_SECTION();
  }
  
  /* ----------------------------------------------------------------
***************
*** 657,663 ****
  	 */
  	if (MyLastRecPtr.xrecoff != 0 || MyXactMadeTempRelUpdate)
  	{
! 		TransactionId xid = GetCurrentTransactionId();
  
  		/*
  		 * Catch the scenario where we aborted partway through
--- 950,958 ----
  	 */
  	if (MyLastRecPtr.xrecoff != 0 || MyXactMadeTempRelUpdate)
  	{
! 		TransactionId	xid = GetCurrentTransactionId();
! 		int 			nchilds = 0;
! 		TransactionId  *childs = NULL;
  
  		/*
  		 * Catch the scenario where we aborted partway through
***************
*** 678,709 ****
  		 */
  		if (MyLastRecPtr.xrecoff != 0)
  		{
! 			XLogRecData rdata[2];
  			xl_xact_abort xlrec;
  			int			nrels;
  			RelFileNode *rptr;
  			XLogRecPtr	recptr;
  
  			nrels = smgrGetPendingDeletes(false, &rptr);
  
  			xlrec.xtime = time(NULL);
  			rdata[0].buffer = InvalidBuffer;
  			rdata[0].data = (char *) (&xlrec);
  			rdata[0].len = MinSizeOfXactAbort;
  			if (nrels > 0)
  			{
  				rdata[0].next = &(rdata[1]);
  				rdata[1].buffer = InvalidBuffer;
  				rdata[1].data = (char *) rptr;
  				rdata[1].len = nrels * sizeof(RelFileNode);
! 				rdata[1].next = NULL;
  			}
  			else
  				rdata[0].next = NULL;
  
  			recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, rdata);
  
! 			if (nrels > 0)
  				XLogFlush(recptr);
  
  			if (rptr)
--- 973,1020 ----
  		 */
  		if (MyLastRecPtr.xrecoff != 0)
  		{
! 			XLogRecData rdata[3];
  			xl_xact_abort xlrec;
  			int			nrels;
+ 			int			latest = 0;
  			RelFileNode *rptr;
  			XLogRecPtr	recptr;
  
  			nrels = smgrGetPendingDeletes(false, &rptr);
+ 			nchilds = xactGetCommittedChilds(&childs, false);
  
  			xlrec.xtime = time(NULL);
+ 			xlrec.nrels = nrels;
  			rdata[0].buffer = InvalidBuffer;
  			rdata[0].data = (char *) (&xlrec);
  			rdata[0].len = MinSizeOfXactAbort;
+ 			/* dump rels to delete */
  			if (nrels > 0)
  			{
  				rdata[0].next = &(rdata[1]);
  				rdata[1].buffer = InvalidBuffer;
  				rdata[1].data = (char *) rptr;
  				rdata[1].len = nrels * sizeof(RelFileNode);
! 				latest = 1;
  			}
  			else
  				rdata[0].next = NULL;
+ 			/* dump committed child Xids */
+ 			if (nchilds > 0)
+ 			{
+ 				rdata[latest].next = &(rdata[2]);
+ 				rdata[2].buffer = InvalidBuffer;
+ 				rdata[2].data = (char *)childs;
+ 				rdata[2].len = nchilds * sizeof(TransactionId);
+ 				rdata[2].next = NULL;
+ 			}
+ 			else
+ 				rdata[latest].next = NULL;
  
  			recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, rdata);
  
! 			/* XXX do I really need to flush if nchilds > 0 ? */
! 			if (nrels > 0 || nchilds > 0)
  				XLogFlush(recptr);
  
  			if (rptr)
***************
*** 713,720 ****
--- 1024,1035 ----
  		/*
  		 * Mark the transaction aborted in clog.  This is not absolutely
  		 * necessary but we may as well do it while we are here.
+ 		 *
+ 		 * XXX Is the ordering important?  (Race condition against
+ 		 * concurrent checks?)
  		 */
  		TransactionIdAbort(xid);
+ 		TransactionIdSubAbort(nchilds, childs);
  
  		END_CRIT_SECTION();
  	}
***************
*** 749,755 ****
  	 *
  	 * Then you're up a creek without a paddle! -mer
  	 */
! 	ProcReleaseLocks(false);
  }
  
  
--- 1064,1070 ----
  	 *
  	 * Then you're up a creek without a paddle! -mer
  	 */
! 	ProcReleaseLocks(ReleaseAll, 0, NULL);
  }
  
  
***************
*** 778,783 ****
--- 1093,1187 ----
  		MemoryContextSwitchTo(TopMemoryContext);
  }
  
+ /*
+  * AtSubAbort_Locks
+  */
+ static void
+ AtSubAbort_Locks(void)
+ {
+ 	int nxids;
+ 	TransactionId *xids;
+ 
+ 	nxids = xactGetCommittedChilds(&xids, true);
+ 
+ 	ProcReleaseLocks(ReleaseGivenXids, nxids, xids);
+ }
+ 
+ 
+ /*
+  * AtSubAbort_Memory
+  */
+ static void
+ AtSubAbort_Memory(void)
+ {
+ 	Assert(TopTransactionContext != NULL);
+ 
+ 	MemoryContextSwitchTo(TopTransactionContext);
+ }
+ 
+ /*
+  * RecordSubTransactionAbort
+  */
+ static void
+ RecordSubTransactionAbort(void)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 	TransactionId *childs;
+ 	int nchilds = xactGetCommittedChilds(&childs, false);
+ 
+ 	/*
+ 	 * We only need to do this if the subtransaction emitted
+ 	 * any XLog record.
+ 	 */
+ 	if (MyLastRecPtr.xrecoff != s->XLogRecOff)
+ 	{
+ 		XLogRecData rdata[3];
+ 		xl_xact_abort xlrec;
+ 		int nrels;
+ 		int latest = 0;
+ 		RelFileNode *rptr;
+ 		XLogRecPtr      recptr;
+ 
+ 		nrels = smgrGetPendingDeletes(false, &rptr);
+ 
+ 		xlrec.xtime = time(NULL);
+ 		xlrec.nrels = nrels;
+ 		rdata[0].buffer = InvalidBuffer;
+ 		rdata[0].data = (char *) (&xlrec);
+ 		rdata[0].len = MinSizeOfXactAbort;
+ 		/* dump rels to delete */
+ 		if (nrels > 0)
+ 		{
+ 			rdata[0].next = &(rdata[1]);
+ 			rdata[1].buffer = InvalidBuffer;
+ 			rdata[1].data = (char *) rptr;
+ 			rdata[1].len = nrels * sizeof(RelFileNode);
+ 			latest = 1;
+ 		}
+ 		else
+ 			rdata[0].next = NULL;
+ 		/* dump committed child Xids */
+ 		if (nchilds > 0)
+ 		{
+ 			rdata[latest].next = &(rdata[2]);
+ 			rdata[2].buffer = InvalidBuffer;
+ 			rdata[2].data = (char *)childs;
+ 			rdata[2].len = nchilds * sizeof(TransactionId);
+ 			rdata[2].next = NULL;
+ 		}
+ 		else
+ 			rdata[latest].next = NULL;
+ 
+ 		recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, rdata);
+ 
+ 		if (nrels > 0 || nchilds > 0)
+ 			XLogFlush(recptr);
+ 
+ 	}
+ 
+ 	TransactionIdAbort(s->transactionIdData);
+ 	TransactionIdSubAbort(nchilds, childs);
+ }
  
  /* ----------------------------------------------------------------
   *						CleanupTransaction stuff
***************
*** 807,812 ****
--- 1211,1241 ----
  
  
  /* ----------------------------------------------------------------
+  *						CleanupSubTransaction stuff
+  * ----------------------------------------------------------------
+  */
+ 
+ /*
+  * AtSubCleanup_Memory
+  */
+ static void
+ AtSubCleanup_Memory(void)
+ {
+ 	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;
+ }
+ 
+ /* ----------------------------------------------------------------
   *						interface routines
   * ----------------------------------------------------------------
   */
***************
*** 841,847 ****
  	/*
  	 * generate a new transaction id
  	 */
! 	s->transactionIdData = GetNewTransactionId();
  
  	XactLockTableInsert(s->transactionIdData);
  
--- 1270,1276 ----
  	/*
  	 * generate a new transaction id
  	 */
! 	s->transactionIdData = GetNewTransactionId(false);
  
  	XactLockTableInsert(s->transactionIdData);
  
***************
*** 851,856 ****
--- 1280,1287 ----
  	s->commandId = FirstCommandId;
  	s->startTime = GetCurrentAbsoluteTimeUsec(&(s->startTimeUsec));
  
+ 	s->childXids = NIL;
+ 
  	/*
  	 * initialize the various transaction subsystems
  	 */
***************
*** 868,873 ****
--- 1299,1305 ----
  	 * progress"
  	 */
  	s->state = TRANS_INPROGRESS;
+ 	ShowTransactionState("StartTransaction");
  
  }
  
***************
*** 879,884 ****
--- 1311,1318 ----
  {
  	TransactionState s = CurrentTransactionState;
  
+ 	ShowTransactionState("CommitTransaction");
+ 
  	/*
  	 * check the current transaction state
  	 */
***************
*** 975,981 ****
  	AtEOXact_hash();
  	AtEOXact_nbtree();
  	AtEOXact_rtree();
! 	AtEOXact_on_commit_actions(true);
  	AtEOXact_Namespace(true);
  	AtEOXact_CatCache(true);
  	AtEOXact_Files();
--- 1409,1415 ----
  	AtEOXact_hash();
  	AtEOXact_nbtree();
  	AtEOXact_rtree();
! 	AtEOXact_on_commit_actions(true, false);
  	AtEOXact_Namespace(true);
  	AtEOXact_CatCache(true);
  	AtEOXact_Files();
***************
*** 1085,1091 ****
  	AtEOXact_hash();
  	AtEOXact_nbtree();
  	AtEOXact_rtree();
! 	AtEOXact_on_commit_actions(false);
  	AtEOXact_Namespace(false);
  	AtEOXact_CatCache(false);
  	AtEOXact_Files();
--- 1519,1525 ----
  	AtEOXact_hash();
  	AtEOXact_nbtree();
  	AtEOXact_rtree();
! 	AtEOXact_on_commit_actions(false, false);
  	AtEOXact_Namespace(false);
  	AtEOXact_CatCache(false);
  	AtEOXact_Files();
***************
*** 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;
  
  			/*
--- 1579,1591 ----
  			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;
  	}
  
--- 1595,1615 ----
  			 * 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;
  
  			/*
--- 1637,1643 ----
  			 * appropiately.
  			 */
  		case TBLOCK_DEFAULT:
! 			elog(FATAL, "CommitTransactionCommand: unexpected TBLOCK_DEFAULT");
  			break;
  
  			/*
***************
*** 1290,1301 ****
  			CleanupTransaction();
  			s->blockState = TBLOCK_DEFAULT;
  			break;
- 	}
- }
  
! /*
!  *	AbortCurrentTransaction
!  */
  void
  AbortCurrentTransaction(void)
  {
--- 1696,1777 ----
  			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;
! 	}
! }
! 
! /*
!  *	AbortCurrentTransaction
!  */
  void
  AbortCurrentTransaction(void)
  {
***************
*** 1361,1366 ****
--- 1837,1843 ----
  			 * state.
  			 */
  		case TBLOCK_ABORT:
+ 		case TBLOCK_SUBABORT:
  			break;
  
  			/*
***************
*** 1373,1378 ****
--- 1850,1896 ----
  			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;
  	}
  }
  
***************
*** 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 */
  }
  
--- 1936,1942 ----
  	/* 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 */
  }
  
***************
*** 1570,1587 ****
  			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. */
--- 2088,2111 ----
  			s->blockState = TBLOCK_BEGIN;
  			break;
  
! 			/*
! 			 * Already a transaction block in progress.
! 			 * Start a subtransaction.
! 			 */
  		case TBLOCK_INPROGRESS:
! 		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. */
***************
*** 1589,1595 ****
  		case TBLOCK_BEGIN:
  		case TBLOCK_ENDABORT:
  		case TBLOCK_END:
! 			elog(FATAL, "BeginTransactionBlock: not in a user-allowed state!");
  			break;
  	}
  }
--- 2113,2125 ----
  		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;
  	}
  }
***************
*** 1614,1619 ****
--- 2144,2158 ----
  			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
***************
*** 1624,1629 ****
--- 2163,2177 ----
  			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
***************
*** 1643,1649 ****
  		case TBLOCK_BEGIN:
  		case TBLOCK_ENDABORT:
  		case TBLOCK_END:
! 			elog(FATAL, "EndTransactionBlock and not in a user-allowed state");
  			break;
  	}
  }
--- 2191,2203 ----
  		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;
  	}
  }
***************
*** 1656,1697 ****
  {
  	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;
  }
  
  /*
--- 2210,2278 ----
  {
  	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;
  	}
  
  }
  
  /*
***************
*** 1727,1732 ****
--- 2308,2337 ----
  			/* 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;
  	}
  
  	/*
***************
*** 1783,1800 ****
  		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
--- 2388,2773 ----
  		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 */
  }
  
+ /*
+  * 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 Xid and record it in pg_subtrans.
+ 	 */
+ 	s->transactionIdData = GetNewTransactionId(true);
+ 
+ 	SubTransSetParent(s->transactionIdData, s->parent->transactionIdData);
+ 
+ 	s->pendingDeletes = NIL;
+ 	s->childXids = NIL;
+ 	s->bufRefCnts = NIL;
+ 	s->XLogRecOff = MyLastRecPtr.xrecoff;
+ 	
+ 	/* Initialize the memory subsystem */
+ 	AtSubStart_Memory();
+ 	AtSubStart_RelationCache();
+ 	AtSubStart_CatCache();
+ 	AtSubStart_Buffers();
+ 
+ 	ShowTransactionState("StartSubTransaction");
+ }
+ 
+ /*
+  * CommitSubTransaction
+  */
+ void
+ CommitSubTransaction(void)
+ {
+ 	TransactionState s = CurrentTransactionState;
+ 	ShowTransactionState("CommitSubTransaction");
+ 
+ 	CommandCounterIncrement();
+ 	RecordSubTransactionCommit();
+ 
+ 	Assert(s->parent != NULL);
+ 
+ 	smgrCommitSubTransaction();
+ 
+ 	AtSubCommit_Portals(s->parent->transactionIdData);
+ 	AtSubCommit_Memory();
+ 	AtSubCommit_childXids();
+ 	AtSubCommit_Notifies();
+ 	AtEOSubXact_CatCache(true);
+ 	AtEOSubXact_RelationCache(true);
+ 	AtEOSubXact_Buffers(true);
+ }
+ 
+ /*
+  * AbortSubTransaction
+  */
+ void
+ AbortSubTransaction(void)
+ {
+ 	ShowTransactionState("AbortSubTransaction");
+ 
+ 	HOLD_INTERRUPTS();
+ 
+ 	/*
+ 	 * Release any LW locks we might be holding as quickly as possible.
+ 	 * (Regular locks, however, must be held till we finish aborting.)
+ 	 * Releasing LW locks is critical since we might try to grab them
+ 	 * again while cleaning up!
+ 	 *
+ 	 * FIXME This may be incorrect --- Are there some locks we should keep?
+ 	 * Buffer locks, for example?  I don't think so but I'm not sure.
+ 	 */
+ 	LWLockReleaseAll();
+ 
+ 	AbortBufferIO();
+ 	UnlockBuffers();
+ 
+ 	LockWaitCancel();
+ 
+ 	RecordSubTransactionAbort();
+ 
+ 	/* We only want to release locks with my own Xid */
+ 	AtSubAbort_Locks();
+ 	AtSubAbort_Portals();
+ 	AtSubAbort_Memory();
+ 	AtEOSubXact_RelationCache(false);
+ 	AtEOSubXact_CatCache(false);
+ 	AtEOXact_on_commit_actions(false, true);
+ 	AtEOSubXact_Buffers(false);
+ 
+ 	smgrAbortSubTransaction();
+ 
+ 	CommandCounterIncrement();
+ 
+ 	RESUME_INTERRUPTS();
+ }
+ 
+ /*
+  * 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);
+ 	char   *tmp;
+ 	
+ 	if (s->parent)
+ 		ShowTransactionStateRec(s->parent, indent - 2);
+ 
+ 	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,
+ 			nodeToString(s->childXids));
+ 	free(tmp);
+ 
+ 	pfree(blockState);
+ 	pfree(state);
+ }
+ 
+ 
+ /*
+  * BlockStateAsString
+  *
+  * Returns the block state as a palloc'ed string
+  */
+ char *
+ BlockStateAsString(TBlockState blockState)
+ {
+ 	char *tmp = "";
+ 	switch (blockState) {
+ 		case TBLOCK_DEFAULT:
+ 			tmp = "DEFAULT";
+ 			break;
+ 		case TBLOCK_STARTED:
+ 			tmp = "STARTED";
+ 			break;
+ 		case TBLOCK_BEGIN:
+ 			tmp = "BEGIN";
+ 			break;
+ 		case TBLOCK_INPROGRESS:
+ 			tmp = "INPROGRESS";
+ 			break;
+ 		case TBLOCK_END:
+ 			tmp = "END";
+ 			break;
+ 		case TBLOCK_ABORT:
+ 			tmp = "ABORT";
+ 			break;
+ 		case TBLOCK_ENDABORT:
+ 			tmp = "ENDABORT";
+ 			break;
+ 		case TBLOCK_SUBBEGIN:
+ 			tmp = "SUB BEGIN";
+ 			break;
+ 		case TBLOCK_SUBBEGINABORT:
+ 			tmp = "SUB BEGIN ABORT";
+ 			break;
+ 		case TBLOCK_SUBINPROGRESS:
+ 			tmp = "SUB INPROGRESS";
+ 			break;
+ 		case TBLOCK_SUBEND:
+ 			tmp = "SUB END";
+ 			break;
+ 		case TBLOCK_SUBABORT:
+ 			tmp = "SUB ABORT";
+ 			break;
+ 		case TBLOCK_SUBENDABORT_OK:
+ 			tmp = "SUB ENDABORT OK";
+ 			break;
+ 		case TBLOCK_SUBENDABORT_ERROR:
+ 			tmp = "SUB ENDABORT ERROR";
+ 			break;
+ 	}
+ 	return pstrdup(tmp);
+ }
+ 
+ /*
+  * TransStateAsString
+  *
+  * Returns the transaction internal state as a palloc'ed string
+  */
+ char *
+ TransStateAsString(TransState state)
+ {
+ 	char *tmp = "";
+ 
+ 	switch (state) {
+ 		case TRANS_DEFAULT:
+ 			tmp = "DEFAULT";
+ 			break;
+ 		case TRANS_START:
+ 			tmp = "START";
+ 			break;
+ 		case TRANS_COMMIT:
+ 			tmp = "COMMIT";
+ 			break;
+ 		case TRANS_ABORT:
+ 			tmp = "ABORT";
+ 			break;
+ 		case TRANS_INPROGRESS:
+ 			tmp = "INPROGRESS";
+ 			break;
+ 	}
+ 	return pstrdup(tmp);
+ }
+ 
+ /*
+  * xactGetCommittedChilds
+  *
+  * Returns the list of committed childs of the current transaction.  The return
+  * value is the number of child transactions.  *childs is set to point to a
+  * palloc'd array of TransactionIds.  If there are no subxacts, *childs is set
+  * to NULL.
+  *
+  * If metoo is true, include the current TransactionId.
+  */
+ int
+ xactGetCommittedChilds(TransactionId **ptr, bool metoo)
+ {
+ 	TransactionState	s = CurrentTransactionState;
+ 	int					nchilds;
+ 	TransactionId	   *childs;
+ 	ListCell		   *p;
+ 
+ 	nchilds = list_length(s->childXids);
+ 	if (metoo)
+ 		nchilds ++;
+ 	if (nchilds == 0)
+ 	{
+ 		*ptr = NULL;
+ 		return 0;
+ 	}
+ 
+ 	childs = (TransactionId *) palloc(nchilds * sizeof(TransactionId));
+ 	*ptr = childs;
+ 
+ 	foreach(p, s->childXids)
+ 	{
+ 		TransactionId child = lfirsti(p);
+ 		*childs++ = (TransactionId)child;
+ 	}
+ 	if (metoo)
+ 		*childs = s->transactionIdData;
+ 
+ 	return nchilds;
+ }
  
  /*
   *	XLOG support routines
***************
*** 1808,1839 ****
  	if (info == XLOG_XACT_COMMIT)
  	{
  		xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(record);
! 		int		nfiles;
  		int		i;
  
  		TransactionIdCommit(record->xl_xid);
  		/* Make sure files supposed to be dropped are dropped */
! 		nfiles = (record->xl_len - MinSizeOfXactCommit) / sizeof(RelFileNode);
! 		for (i = 0; i < nfiles; i++)
  		{
  			XLogCloseRelation(xlrec->xnodes[i]);
  			smgrdounlink(smgropen(xlrec->xnodes[i]), false, true);
  		}
  	}
  	else if (info == XLOG_XACT_ABORT)
  	{
  		xl_xact_abort *xlrec = (xl_xact_abort *) XLogRecGetData(record);
! 		int		nfiles;
  		int		i;
  
  		TransactionIdAbort(record->xl_xid);
  		/* Make sure files supposed to be dropped are dropped */
! 		nfiles = (record->xl_len - MinSizeOfXactAbort) / sizeof(RelFileNode);
! 		for (i = 0; i < nfiles; i++)
  		{
  			XLogCloseRelation(xlrec->xnodes[i]);
  			smgrdounlink(smgropen(xlrec->xnodes[i]), false, true);
  		}
  	}
  	else
  		elog(PANIC, "xact_redo: unknown op code %u", info);
--- 2781,2819 ----
  	if (info == XLOG_XACT_COMMIT)
  	{
  		xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(record);
! 		int		nchilds;
  		int		i;
  
  		TransactionIdCommit(record->xl_xid);
  		/* Make sure files supposed to be dropped are dropped */
! 		for (i = 0; i < xlrec->nrels; i++)
  		{
  			XLogCloseRelation(xlrec->xnodes[i]);
  			smgrdounlink(smgropen(xlrec->xnodes[i]), false, true);
  		}
+ 		/* mark committed subtransactions as committed */
+ 		nchilds = (record->xl_len - MinSizeOfXactCommit - xlrec->nrels * sizeof(RelFileNode))
+ 			/ sizeof(TransactionId);
+ 
+ 		TransactionIdCommitTree(nchilds, (TransactionId *)&(xlrec->xnodes[xlrec->nrels]));
  	}
  	else if (info == XLOG_XACT_ABORT)
  	{
  		xl_xact_abort *xlrec = (xl_xact_abort *) XLogRecGetData(record);
! 		int		nchilds;
  		int		i;
  
  		TransactionIdAbort(record->xl_xid);
  		/* Make sure files supposed to be dropped are dropped */
! 		for (i = 0; i < xlrec->nrels; i++)
  		{
  			XLogCloseRelation(xlrec->xnodes[i]);
  			smgrdounlink(smgropen(xlrec->xnodes[i]), false, true);
  		}
+ 		/* mark aborted child Xids */
+ 		nchilds = (record->xl_len - MinSizeOfXactCommit - xlrec->nrels * sizeof(RelFileNode))
+ 			/ sizeof(TransactionId);
+ 		TransactionIdSubAbort(nchilds, (TransactionId *)&(xlrec->xnodes[xlrec->nrels]));
  	}
  	else
  		elog(PANIC, "xact_redo: unknown op code %u", info);
***************
*** 1863,1869 ****
  		sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
  				tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
  				tm->tm_hour, tm->tm_min, tm->tm_sec);
! 		/* XXX can't show RelFileNodes for lack of access to record length */
  	}
  	else if (info == XLOG_XACT_ABORT)
  	{
--- 2843,2862 ----
  		sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
  				tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
  				tm->tm_hour, tm->tm_min, tm->tm_sec);
! 		if (xlrec->nrels > 0)
! 		{
! 			int i;
! 			sprintf(buf + strlen(buf), "; %d rels:", xlrec->nrels);
! 			for (i = 0; i < xlrec->nrels; i++)
! 			{
! 				RelFileNode rnode = xlrec->xnodes[i];
! 				sprintf(buf + strlen(buf), " %u/%u", rnode.tblNode, rnode.relNode);
! 			}
! 		}
! 		/*
! 		 * XXX Can't show list of committed subtransactions for lack of
! 		 * access to record length 
! 		 */
  	}
  	else if (info == XLOG_XACT_ABORT)
  	{
***************
*** 1873,1879 ****
  		sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
  				tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
  				tm->tm_hour, tm->tm_min, tm->tm_sec);
! 		/* XXX can't show RelFileNodes for lack of access to record length */
  	}
  	else
  		strcat(buf, "UNKNOWN");
--- 2866,2885 ----
  		sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
  				tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
  				tm->tm_hour, tm->tm_min, tm->tm_sec);
! 		if (xlrec->nrels > 0)
! 		{
! 			int i;
! 			sprintf(buf + strlen(buf), "; %d rels:", xlrec->nrels);
! 			for (i = 0; i < xlrec->nrels; i++)
! 			{
! 				RelFileNode rnode = xlrec->xnodes[i];
! 				sprintf(buf + strlen(buf), " %u/%u", rnode.tblNode, rnode.relNode);
! 			}
! 		}
! 		/*
! 		 * XXX Can't show list of committed subtransactions for lack of
! 		 * access to record length 
! 		 */
  	}
  	else
  		strcat(buf, "UNKNOWN");
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/access/transam/xlog.c 09withList/src/backend/access/transam/xlog.c
*** 00orig/src/backend/access/transam/xlog.c	2004-05-21 12:54:10.000000000 -0400
--- 09withList/src/backend/access/transam/xlog.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 21,26 ****
--- 21,27 ----
  #include <sys/time.h>
  
  #include "access/clog.h"
+ #include "access/subtrans.h"
  #include "access/transam.h"
  #include "access/xact.h"
  #include "access/xlog.h"
***************
*** 2757,2762 ****
--- 2758,2764 ----
  
  	/* Bootstrap the commit log, too */
  	BootStrapCLOG();
+ 	BootStrapSUBTRANS();
  }
  
  static char *
***************
*** 3162,3167 ****
--- 3164,3170 ----
  
  	/* Start up the commit log, too */
  	StartupCLOG();
+ 	StartupSUBTRANS();
  
  	ereport(LOG,
  			(errmsg("database system is ready")));
***************
*** 3318,3323 ****
--- 3321,3327 ----
  	CritSectionCount++;
  	CreateCheckPoint(true, true);
  	ShutdownCLOG();
+ 	ShutdownSUBTRANS();
  	CritSectionCount--;
  
  	ereport(LOG,
***************
*** 3507,3512 ****
--- 3511,3517 ----
  	END_CRIT_SECTION();
  
  	CheckPointCLOG();
+ 	CheckPointSUBTRANS();
  	FlushBufferPool();
  
  	START_CRIT_SECTION();
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/commands/async.c 09withList/src/backend/commands/async.c
*** 00orig/src/backend/commands/async.c	2004-05-26 10:27:07.000000000 -0400
--- 09withList/src/backend/commands/async.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 98,106 ****
   * 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
--- 98,107 ----
   * 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
***************
*** 161,167 ****
  
  		oldcontext = MemoryContextSwitchTo(TopTransactionContext);
  
! 		pendingNotifies = lcons(pstrdup(relname), pendingNotifies);
  
  		MemoryContextSwitchTo(oldcontext);
  	}
--- 162,169 ----
  
  		oldcontext = MemoryContextSwitchTo(TopTransactionContext);
  
! 		TransactionSetNotifies(lcons(pstrdup(relname),
! 							   TransactionGetNotifies()));
  
  		MemoryContextSwitchTo(oldcontext);
  	}
***************
*** 435,441 ****
  	char		repl[Natts_pg_listener],
  				nulls[Natts_pg_listener];
  
! 	if (pendingNotifies == NIL)
  		return;					/* no NOTIFY statements in this
  								 * transaction */
  
--- 437,443 ----
  	char		repl[Natts_pg_listener],
  				nulls[Natts_pg_listener];
  
! 	if (TransactionGetNotifies() == NIL)
  		return;					/* no NOTIFY statements in this
  								 * transaction */
  
***************
*** 934,940 ****
  {
  	ListCell   *p;
  
! 	foreach(p, pendingNotifies)
  	{
  		/* Use NAMEDATALEN for relname comparison.	  DZ - 26-08-1996 */
  		if (strncmp((const char *) lfirst(p), relname, NAMEDATALEN) == 0)
--- 936,942 ----
  {
  	ListCell   *p;
  
! 	foreach(p, TransactionGetNotifies())
  	{
  		/* Use NAMEDATALEN for relname comparison.	  DZ - 26-08-1996 */
  		if (strncmp((const char *) lfirst(p), relname, NAMEDATALEN) == 0)
***************
*** 951,959 ****
  	/*
  	 * 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;
  }
--- 953,961 ----
  	/*
  	 * 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);
  }
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/commands/tablecmds.c 09withList/src/backend/commands/tablecmds.c
*** 00orig/src/backend/commands/tablecmds.c	2004-05-26 10:27:07.000000000 -0400
--- 09withList/src/backend/commands/tablecmds.c	2004-05-26 17:41:48.931820496 -0400
***************
*** 77,82 ****
--- 77,87 ----
  	 */
  	bool		created_in_cur_xact;
  	bool		deleted_in_cur_xact;
+ 	/*
+ 	 * We record the creating transaction id so we can remove the item if
+ 	 * the creating subtransaction aborts.
+ 	 */
+ 	TransactionId	creating_xid;
  } OnCommitItem;
  
  static List *on_commits = NIL;
***************
*** 5340,5345 ****
--- 5345,5351 ----
  	oc->oncommit = action;
  	oc->created_in_cur_xact = true;
  	oc->deleted_in_cur_xact = false;
+ 	oc->creating_xid = GetCurrentTransactionId();
  
  	on_commits = lcons(oc, on_commits);
  
***************
*** 5425,5446 ****
   *
   * During commit, remove entries that were deleted during this transaction;
   * during abort, remove those created during this transaction.
   */
  void
! AtEOXact_on_commit_actions(bool isCommit)
  {
! 	ListCell *cur_item;
! 	ListCell *prev_item;
  
  	prev_item = NULL;
  	cur_item = list_head(on_commits);
  
  	while (cur_item != NULL)
  	{
! 		OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
  
! 		if (isCommit ? oc->deleted_in_cur_xact :
! 			oc->created_in_cur_xact)
  		{
  			/* cur_item must be removed */
  			on_commits = list_delete_cell(on_commits, cur_item, prev_item);
--- 5431,5480 ----
   *
   * During commit, remove entries that were deleted during this transaction;
   * during abort, remove those created during this transaction.
+  *
+  * During subtransaction abort, remove entries marked created with any of
+  * the child Xids.  We shouldn't be called at subtransaction commit.
   */
  void
! AtEOXact_on_commit_actions(bool isCommit, bool isSubtrans)
  {
! 	ListCell 	   *cur_item;
! 	ListCell 	   *prev_item;
! 	int				nxids = 0;
! 	TransactionId  *xids = NULL;
! 
! 	Assert(!(isSubtrans && isCommit));
! 
! 	if (isSubtrans)
! 		nxids = xactGetCommittedChilds(&xids, true);
  
  	prev_item = NULL;
  	cur_item = list_head(on_commits);
  
  	while (cur_item != NULL)
  	{
! 		OnCommitItem   *oc = (OnCommitItem *) lfirst(cur_item);
! 		bool			destroy_it = false;
! 
! 		if (isSubtrans)
! 		{
! 			int i;
! 
! 			for (i = 0; i < nxids; i++)
! 			{
! 				if (oc->creating_xid == xids[i] && oc->created_in_cur_xact)
! 				{
! 					destroy_it = true;
! 					break;
! 				}
! 			}
! 		}
! 		else
! 			if (isCommit ? oc->deleted_in_cur_xact :
! 					oc->created_in_cur_xact)
! 				destroy_it = true;
  
! 		if (destroy_it)
  		{
  			/* cur_item must be removed */
  			on_commits = list_delete_cell(on_commits, cur_item, prev_item);
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/commands/vacuum.c 09withList/src/backend/commands/vacuum.c
*** 00orig/src/backend/commands/vacuum.c	2004-05-26 10:27:07.000000000 -0400
--- 09withList/src/backend/commands/vacuum.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 25,30 ****
--- 25,31 ----
  #include "access/clog.h"
  #include "access/genam.h"
  #include "access/heapam.h"
+ #include "access/subtrans.h"
  #include "access/xlog.h"
  #include "catalog/catalog.h"
  #include "catalog/catname.h"
***************
*** 730,737 ****
  		return;
  	}
  
! 	/* Truncate CLOG to the oldest vacuumxid */
  	TruncateCLOG(vacuumXID);
  
  	/* Give warning about impending wraparound problems */
  	if (frozenAlreadyWrapped)
--- 731,739 ----
  		return;
  	}
  
! 	/* Truncate CLOG and SUBTRANS to the oldest vacuumxid */
  	TruncateCLOG(vacuumXID);
+ 	TruncateSUBTRANS(vacuumXID);
  
  	/* Give warning about impending wraparound problems */
  	if (frozenAlreadyWrapped)
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/storage/buffer/bufmgr.c 09withList/src/backend/storage/buffer/bufmgr.c
*** 00orig/src/backend/storage/buffer/bufmgr.c	2004-05-12 00:06:09.000000000 -0400
--- 09withList/src/backend/storage/buffer/bufmgr.c	2004-05-26 17:44:16.918109592 -0400
***************
*** 38,43 ****
--- 38,44 ----
  #include <sys/file.h>
  #include <unistd.h>
  
+ #include "access/xact.h"
  #include "lib/stringinfo.h"
  #include "miscadmin.h"
  #include "storage/buf_internals.h"
***************
*** 46,57 ****
--- 47,68 ----
  #include "storage/proc.h"
  #include "storage/smgr.h"
  #include "utils/relcache.h"
+ #include "utils/memutils.h"
  #include "pgstat.h"
  
  
  #define BufferGetLSN(bufHdr)	\
  	(*((XLogRecPtr*) MAKE_PTR((bufHdr)->data)))
  
+ /*
+  * We will use a list of this structs to keep track of buffer reference
+  * count checking at subtransaction boundaries.
+  */
+ typedef struct BufferRefCount 
+ {
+ 	Buffer	buffer;
+ 	int		refcount;
+ } BufferRefCount;
  
  /* GUC variable */
  bool		zero_damaged_pages = false;
***************
*** 71,76 ****
--- 82,88 ----
  
  static void PinBuffer(BufferDesc *buf);
  static void UnpinBuffer(BufferDesc *buf);
+ static inline void BufferFixLeak(Buffer buffer, int should, bool warn);
  static void WaitIO(BufferDesc *buf);
  static void StartBufferIO(BufferDesc *buf, bool forInput);
  static void TerminateBufferIO(BufferDesc *buf, int err_flag);
***************
*** 825,846 ****
  	{
  		if (PrivateRefCount[i] != 0)
  		{
! 			BufferDesc *buf = &(BufferDescriptors[i]);
  
! 			if (isCommit)
! 				elog(WARNING,
! 					 "buffer refcount leak: [%03d] "
! 					 "(rel=%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d)",
! 					 i,
! 					 buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
! 					 buf->tag.blockNum, buf->flags,
! 					 buf->refcount, PrivateRefCount[i]);
! 
! 			PrivateRefCount[i] = 1;		/* make sure we release shared pin */
! 			LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
! 			UnpinBuffer(buf);
! 			LWLockRelease(BufMgrLock);
! 			Assert(PrivateRefCount[i] == 0);
  		}
  	}
  
--- 837,845 ----
  	{
  		if (PrivateRefCount[i] != 0)
  		{
! 			bool warn = isCommit ? true : false;
  
! 			BufferFixLeak(i, 0, warn);
  		}
  	}
  
***************
*** 848,853 ****
--- 847,977 ----
  }
  
  /*
+  * During subtransaction start, save buffer reference counts
+  * that are nonzero.
+  */
+ void
+ AtSubStart_Buffers(void)
+ {
+ 	List		   *refcounts = NIL;
+ 	MemoryContext	old_cxt;
+ 	int				i;
+ 
+ 	/*
+ 	 * XXX It would be better to have a per-subxact memory context
+ 	 * where we could keep this things.  We don't have one ATM
+ 	 * so we free them by hand afterwards.
+ 	 */
+ 	old_cxt = MemoryContextSwitchTo(TopTransactionContext);
+ 
+ 	for (i = 0; i < NBuffers; i++)
+ 	{
+ 		BufferRefCount *brc;
+ 
+ 		if (PrivateRefCount[i] == 0)
+ 			continue;
+ 
+ 		brc = (BufferRefCount *)palloc(sizeof(BufferRefCount));
+ 		brc->buffer = i;
+ 		brc->refcount = PrivateRefCount[i];
+ 
+ 		refcounts = lappend(refcounts, brc);
+ 	}
+ 
+ 	TransactionSetBufferRefcounts(refcounts);
+ 
+ 	MemoryContextSwitchTo(old_cxt);
+ }
+ 
+ /*
+  * AtEOSubXact_Buffers
+  *
+  * At subtransaction end, we restore the saved counts.  If committing, we croak
+  * if the refcounts don't match; if aborting, just restore silently.
+  *
+  * This only works because we know the list is in ascending order.
+  */
+ void
+ AtEOSubXact_Buffers(bool commit)
+ {
+ 	List	   *counts = TransactionGetBufferRefcounts();
+ 	ListCell   *nextElt;
+ 	int			i, j;
+ 	Buffer		nextSaved;
+ 
+ 	nextElt   = list_head(counts);
+ 
+ 	for (i = 0; i < NBuffers; i++)
+ 	{
+ 		BufferRefCount	   *brc = NULL;
+ 
+ 		if (nextElt)
+ 		{
+ 			brc = (BufferRefCount *) lfirst(nextElt);
+ 			nextSaved = brc->buffer;
+ 		}
+ 		else
+ 			nextSaved = NBuffers;
+ 
+ 		/*
+ 		 * Buffers that are not on the saved list are supposed
+ 		 * to have refcount 0.  If they do not, unpin them.
+ 		 * In the commit case, emit a message.
+ 		 */
+ 		for (j = i; j < nextSaved; j++, i++)
+ 		{
+ 			if (PrivateRefCount[j] != 0)
+ 			{
+ 				bool warn = commit ? true : false;
+ 
+ 				BufferFixLeak(j, 0, warn);
+ 			}
+ 		}
+ 
+ 		/* No more buffers to check. */
+ 		if (nextSaved == NBuffers)
+ 			break;
+ 
+ 		if (PrivateRefCount[i] != brc->refcount)
+ 		{
+ 			bool warn = commit ? true : false;
+ 			if (commit)
+ 				BufferFixLeak(i, brc->refcount, warn);
+ 		}
+ 
+ 		if (nextElt)
+ 			nextElt = nextElt->next;
+ 	}
+ 
+ 	/* Free the whole list, including the elements. */
+ 	list_free_deep(counts);
+ }
+ 
+ /*
+  * Fix a buffer refcount leak.
+  */
+ inline void
+ BufferFixLeak(Buffer buffer, int should, bool emitWarning)
+ {
+ 	BufferDesc	*buf = &(BufferDescriptors[buffer]);
+ 
+ 	if (emitWarning)
+ 		elog(WARNING,
+ 				"buffer refcount leak: [%03d] "
+ 				"(rel=%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d, should be=%d)",
+ 				buffer,
+ 				buf->tag.rnode.tblNode, buf->tag.rnode.relNode,
+ 				buf->tag.blockNum, buf->flags,
+ 				buf->refcount, PrivateRefCount[buffer], should);
+ 
+ 	PrivateRefCount[buffer] = 1;		/* make sure we release shared pin */
+ 	LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
+ 	UnpinBuffer(buf);
+ 	LWLockRelease(BufMgrLock);
+ 	Assert(PrivateRefCount[buffer] == 0);
+ }
+ 
+ /*
   * FlushBufferPool
   *
   * Flush all dirty blocks in buffer pool to disk at the checkpoint time.
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/storage/ipc/ipci.c 09withList/src/backend/storage/ipc/ipci.c
*** 00orig/src/backend/storage/ipc/ipci.c	2004-04-23 15:36:07.000000000 -0400
--- 09withList/src/backend/storage/ipc/ipci.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 18,23 ****
--- 18,24 ----
  #include "miscadmin.h"
  #include "access/clog.h"
  #include "access/xlog.h"
+ #include "access/subtrans.h"
  #include "storage/bufmgr.h"
  #include "storage/freespace.h"
  #include "storage/ipc.h"
***************
*** 65,70 ****
--- 66,72 ----
  		size += LockShmemSize(maxBackends);
  		size += XLOGShmemSize();
  		size += CLOGShmemSize();
+ 		size += SUBTRANSShmemSize();
  		size += LWLockShmemSize();
  		size += SInvalShmemSize(maxBackends);
  		size += FreeSpaceShmemSize();
***************
*** 123,128 ****
--- 125,131 ----
  	 */
  	XLOGShmemInit();
  	CLOGShmemInit();
+ 	SUBTRANSShmemInit();
  	InitBufferPool();
  
  	/*
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/storage/ipc/sinval.c 09withList/src/backend/storage/ipc/sinval.c
*** 00orig/src/backend/storage/ipc/sinval.c	2004-05-23 23:31:40.000000000 -0400
--- 09withList/src/backend/storage/ipc/sinval.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 16,21 ****
--- 16,23 ----
  
  #include <signal.h>
  
+ #include "access/subtrans.h"
+ #include "access/transam.h"
  #include "commands/async.h"
  #include "storage/ipc.h"
  #include "storage/proc.h"
***************
*** 439,444 ****
--- 441,454 ----
  
  	LWLockAcquire(SInvalLock, LW_SHARED);
  
+ 	/*
+ 	 * XXX We currently iterate over each backend seeing if the Xid is
+ 	 * current for the main transaction or each of the subtransactions.
+ 	 * It would be probably more efficient if we iterated over all backends
+ 	 * checking the main transaction Xids, and the iterate again checking
+ 	 * the subtransactions.  Unfortunately we can only fetch the proc->xid
+ 	 * once, so this may not work.  This loop could probably be optimized a lot.
+ 	 */
  	for (index = 0; index < segP->lastBackend; index++)
  	{
  		SHMEM_OFFSET pOffset = stateP[index].procStruct;
***************
*** 455,460 ****
--- 465,482 ----
  				result = true;
  				break;
  			}
+ 
+ 			/*
+ 			 * XXX Too bad both calls have to recurse up the subtrans tree.
+ 			 */
+ 			if (SubTransXidsHaveCommonAncestor(xid, pxid))
+ 			{
+ 				if (TransactionIdDidAbort(xid))
+ 					result = false;
+ 				else
+ 					result = true;
+ 				break;
+ 			}
  		}
  	}
  
***************
*** 672,677 ****
--- 694,702 ----
  	RecentGlobalXmin = globalxmin;
  	RecentXmin = xmin;
  
+ 	/* Update Subtrans cutoff Xid */
+ 	TransactionIdSetSubtransCutoff(globalxmin);
+ 
  	snapshot->xmin = xmin;
  	snapshot->xmax = xmax;
  	snapshot->xcnt = count;
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/storage/lmgr/lmgr.c 09withList/src/backend/storage/lmgr/lmgr.c
*** 00orig/src/backend/storage/lmgr/lmgr.c	2004-01-15 23:32:23.000000000 -0300
--- 09withList/src/backend/storage/lmgr/lmgr.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 16,21 ****
--- 16,22 ----
  #include "postgres.h"
  
  #include "access/transam.h"
+ #include "access/subtrans.h"
  #include "access/xact.h"
  #include "catalog/catalog.h"
  #include "miscadmin.h"
***************
*** 326,344 ****
   *		XactLockTableWait
   *
   * Wait for the specified transaction to commit or abort.
   */
  void
  XactLockTableWait(TransactionId xid)
  {
  	LOCKTAG		tag;
  	TransactionId myxid = GetCurrentTransactionId();
  
! 	Assert(!TransactionIdEquals(xid, myxid));
  
  	MemSet(&tag, 0, sizeof(tag));
  	tag.relId = XactLockTableId;
  	tag.dbId = InvalidOid;
! 	tag.objId.xid = xid;
  
  	if (!LockAcquire(LockTableId, &tag, myxid,
  					 ShareLock, false))
--- 327,347 ----
   *		XactLockTableWait
   *
   * Wait for the specified transaction to commit or abort.
+  * We actually wait on the topmost transaction of the transaction tree.
   */
  void
  XactLockTableWait(TransactionId xid)
  {
  	LOCKTAG		tag;
  	TransactionId myxid = GetCurrentTransactionId();
+ 	TransactionId waitXid = SubTransGetTopmostTransaction(xid);
  
! 	Assert(!SubTransXidsHaveCommonAncestor(waitXid, myxid));
  
  	MemSet(&tag, 0, sizeof(tag));
  	tag.relId = XactLockTableId;
  	tag.dbId = InvalidOid;
! 	tag.objId.xid = waitXid;
  
  	if (!LockAcquire(LockTableId, &tag, myxid,
  					 ShareLock, false))
***************
*** 348,355 ****
  
  	/*
  	 * Transaction was committed/aborted/crashed - we have to update
! 	 * pg_clog if transaction is still marked as running.
  	 */
! 	if (!TransactionIdDidCommit(xid) && !TransactionIdDidAbort(xid))
! 		TransactionIdAbort(xid);
  }
--- 351,363 ----
  
  	/*
  	 * Transaction was committed/aborted/crashed - we have to update
! 	 * pg_clog if transaction is still marked as running.  If it's a
! 	 * subtransaction, we can update the parent status too.
  	 */
! 	if (!TransactionIdDidCommit(waitXid) && !TransactionIdDidAbort(waitXid))
! 	{
! 		TransactionIdAbort(waitXid);
! 		if (waitXid != xid)
! 			TransactionIdAbort(xid);
! 	}
  }
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/storage/lmgr/lock.c 09withList/src/backend/storage/lmgr/lock.c
*** 00orig/src/backend/storage/lmgr/lock.c	2004-01-15 23:32:23.000000000 -0300
--- 09withList/src/backend/storage/lmgr/lock.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 1143,1161 ****
  }
  
  /*
!  * LockReleaseAll -- Release all locks in a process's lock list.
   *
   * Well, not really *all* locks.
   *
!  * If 'allxids' is TRUE, all locks of the specified lock method are
!  * released, regardless of transaction affiliation.
   *
!  * If 'allxids' is FALSE, all locks of the specified lock method and
!  * specified XID are released.
   */
  bool
  LockReleaseAll(LOCKMETHODID lockmethodid, PGPROC *proc,
! 			   bool allxids, TransactionId xid)
  {
  	SHM_QUEUE  *procHolders = &(proc->procHolders);
  	PROCLOCK   *proclock;
--- 1143,1164 ----
  }
  
  /*
!  * LockReleaseAll -- Release all locks in a process's lock list
!  * of the specified lock method.
   *
   * Well, not really *all* locks.
   *
!  * If 'which' is All, release all locks regardless of transaction
!  * affiliation.
!  *
!  * If 'which' is AllExceptZero, release all locks with Xid != 0.
   *
!  * If 'which is GivenXids, release locks with the given Xid are
!  * released, regardless of transaction affiliation.
   */
  bool
  LockReleaseAll(LOCKMETHODID lockmethodid, PGPROC *proc,
! 			   LockReleaseWhich which, int nxids, TransactionId *xids)
  {
  	SHM_QUEUE  *procHolders = &(proc->procHolders);
  	PROCLOCK   *proclock;
***************
*** 1204,1212 ****
  		if (LOCK_LOCKMETHOD(*lock) != lockmethodid)
  			goto next_item;
  
! 		/* If not allxids, ignore items that are of the wrong xid */
! 		if (!allxids && !TransactionIdEquals(xid, proclock->tag.xid))
  			goto next_item;
  
  		PROCLOCK_PRINT("LockReleaseAll", proclock);
  		LOCK_PRINT("LockReleaseAll", lock, 0);
--- 1207,1231 ----
  		if (LOCK_LOCKMETHOD(*lock) != lockmethodid)
  			goto next_item;
  
! 		/* Ignore locks with Xid=0 unless we are asked to release All locks */
! 		if (proclock->tag.xid == 0 && which != ReleaseAll)
  			goto next_item;
+ 		/* Ignore locks with a Xid not in the list */
+ 		if (which == ReleaseGivenXids)
+ 		{
+ 			int i;
+ 			bool release = false;
+ 			for (i = 0; i < nxids; i++)
+ 			{
+ 				if (TransactionIdEquals(proclock->tag.xid, xids[i]))
+ 				{
+ 					release = true;
+ 					break;
+ 				}
+ 			}
+ 			if (!release)
+ 				goto next_item;
+ 		}
  
  		PROCLOCK_PRINT("LockReleaseAll", proclock);
  		LOCK_PRINT("LockReleaseAll", lock, 0);
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/storage/lmgr/lwlock.c 09withList/src/backend/storage/lmgr/lwlock.c
*** 00orig/src/backend/storage/lmgr/lwlock.c	2004-01-15 23:32:23.000000000 -0300
--- 09withList/src/backend/storage/lmgr/lwlock.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 22,27 ****
--- 22,28 ----
  #include "postgres.h"
  
  #include "access/clog.h"
+ #include "access/subtrans.h"
  #include "storage/lwlock.h"
  #include "storage/proc.h"
  #include "storage/spin.h"
***************
*** 111,116 ****
--- 112,120 ----
  	/* clog.c needs one per CLOG buffer + one control lock */
  	numLocks += NUM_CLOG_BUFFERS + 1;
  
+ 	/* subtrans.c needs one per SubTrans buffer + one control lock */
+ 	numLocks += NUM_SUBTRANS_BUFFERS + 1;
+ 
  	/* Perhaps create a few more for use by user-defined modules? */
  
  	return numLocks;
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/storage/lmgr/proc.c 09withList/src/backend/storage/lmgr/proc.c
*** 00orig/src/backend/storage/lmgr/proc.c	2004-03-21 13:54:43.000000000 -0400
--- 09withList/src/backend/storage/lmgr/proc.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 380,397 ****
  
  /*
   * ProcReleaseLocks() -- release locks associated with current transaction
!  *			at transaction commit or abort
   *
!  * At commit, we release only locks tagged with the current transaction's XID,
!  * leaving those marked with XID 0 (ie, session locks) undisturbed.  At abort,
!  * we release all locks including XID 0, because we need to clean up after
!  * a failure.  This logic will need extension if we ever support nested
!  * transactions.
   *
!  * Note that user locks are not released in either case.
   */
  void
! ProcReleaseLocks(bool isCommit)
  {
  	if (!MyProc)
  		return;
--- 380,399 ----
  
  /*
   * ProcReleaseLocks() -- release locks associated with current transaction
!  *			at main transaction and subtransaction commit or abort
   *
!  * At subtransaction commit, we don't want to release any locks; we will defer
!  * the releasing to the parent transaction.  At subtransaction abort, we
!  * release all locks affiliated to any of the Xids in the given array.
   *
!  * At main transaction commit, we release all locks except those marked with
!  * XID 0 (ie, session locks).  At main transaction abort, we release all locks
!  * including XID 0, because we need to clean up after a failure.
!  *
!  * Note that user locks are not released in any case.
   */
  void
! ProcReleaseLocks(LockReleaseWhich which, int nxids, TransactionId *xids)
  {
  	if (!MyProc)
  		return;
***************
*** 399,405 ****
  	LockWaitCancel();
  	/* Release locks */
  	LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc,
! 				   !isCommit, GetCurrentTransactionId());
  }
  
  
--- 401,407 ----
  	LockWaitCancel();
  	/* Release locks */
  	LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc,
! 				   which, nxids, xids);
  }
  
  
***************
*** 432,442 ****
  	LockWaitCancel();
  
  	/* Remove from the standard lock table */
! 	LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, true, InvalidTransactionId);
  
  #ifdef USER_LOCKS
  	/* Remove from the user lock table */
! 	LockReleaseAll(USER_LOCKMETHOD, MyProc, true, InvalidTransactionId);
  #endif
  
  	SpinLockAcquire(ProcStructLock);
--- 434,444 ----
  	LockWaitCancel();
  
  	/* Remove from the standard lock table */
! 	LockReleaseAll(DEFAULT_LOCKMETHOD, MyProc, ReleaseAll, 0, NULL);
  
  #ifdef USER_LOCKS
  	/* Remove from the user lock table */
! 	LockReleaseAll(USER_LOCKMETHOD, MyProc, ReleaseAll, 0, NULL);
  #endif
  
  	SpinLockAcquire(ProcStructLock);
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/storage/smgr/smgr.c 09withList/src/backend/storage/smgr/smgr.c
*** 00orig/src/backend/storage/smgr/smgr.c	2004-03-21 13:54:43.000000000 -0400
--- 09withList/src/backend/storage/smgr/smgr.c	2004-05-26 12:13:30.000000000 -0400
***************
*** 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,590 ****
  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);
  	}
  }
  
  /*
--- 580,642 ----
  void
  smgrDoPendingDeletes(bool isCommit)
  {
! 	ListCell *p;
! 
! 	foreach (p, TransactionGetPendingDeletes())
  	{
! 		PendingRelDelete pending = lfirst(p);
  
  		if (pending->atCommit == isCommit)
  			smgr_internal_unlink(pending->relnode,
  								 pending->which,
  								 pending->isTemp,
  								 false);
  	}
+ 
+ 	TransactionSetPendingDeletes(NIL);
+ }
+ 
+ /*
+  * 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)
+ {
+ 	ListCell *p;
+ 
+ 	foreach (p, TransactionGetPendingDeletes())
+ 	{
+ 		PendingRelDelete pending = lfirst(p);
+ 
+ 		if (pending->atCommit == false)
+ 			smgr_internal_unlink(pending->relnode,
+ 								 pending->which,
+ 								 pending->isTemp,
+ 								 false);
+ 	}
+ 
+ 	TransactionSetPendingDeletes(NIL);
+ }
+ 
+ /*
+  * 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(list_concat(parentPending, pending));
  }
  
  /*
***************
*** 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;
! 	ListCell	*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;
  	}
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/tcop/postgres.c 09withList/src/backend/tcop/postgres.c
*** 00orig/src/backend/tcop/postgres.c	2004-05-26 10:27:11.000000000 -0400
--- 09withList/src/backend/tcop/postgres.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 841,846 ****
--- 841,847 ----
  				TransactionStmt *stmt = (TransactionStmt *) parsetree;
  
  				if (stmt->kind == TRANS_STMT_COMMIT ||
+ 					stmt->kind == TRANS_STMT_BEGIN ||
  					stmt->kind == TRANS_STMT_ROLLBACK)
  					allowit = true;
  			}
***************
*** 1161,1166 ****
--- 1162,1168 ----
  				TransactionStmt *stmt = (TransactionStmt *) parsetree;
  
  				if (stmt->kind == TRANS_STMT_COMMIT ||
+ 					stmt->kind == TRANS_STMT_BEGIN ||
  					stmt->kind == TRANS_STMT_ROLLBACK)
  					allowit = true;
  			}
***************
*** 1599,1604 ****
--- 1601,1607 ----
  
  			is_trans_stmt = true;
  			if (stmt->kind == TRANS_STMT_COMMIT ||
+ 				stmt->kind == TRANS_STMT_BEGIN ||
  				stmt->kind == TRANS_STMT_ROLLBACK)
  				is_trans_exit = true;
  		}
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/utils/cache/catcache.c 09withList/src/backend/utils/cache/catcache.c
*** 00orig/src/backend/utils/cache/catcache.c	2004-05-26 10:27:11.000000000 -0400
--- 09withList/src/backend/utils/cache/catcache.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 360,365 ****
--- 360,367 ----
  	/* free associated tuple data */
  	if (ct->tuple.t_data != NULL)
  		pfree(ct->tuple.t_data);
+ 	if (ct->prev_refcount != NULL)
+ 		pfree(ct->prev_refcount);
  	pfree(ct);
  
  	--cache->cc_ntup;
***************
*** 394,399 ****
--- 396,403 ----
  	/* free associated tuple data */
  	if (cl->tuple.t_data != NULL)
  		pfree(cl->tuple.t_data);
+ 	if (cl->prev_refcount != NULL)
+ 		pfree(cl->prev_refcount);
  	pfree(cl);
  }
  
***************
*** 518,526 ****
  	if (!CacheMemoryContext)
  		CacheMemoryContext = AllocSetContextCreate(TopMemoryContext,
  												   "CacheMemoryContext",
! 												ALLOCSET_DEFAULT_MINSIZE,
! 											   ALLOCSET_DEFAULT_INITSIZE,
! 											   ALLOCSET_DEFAULT_MAXSIZE);
  }
  
  
--- 522,530 ----
  	if (!CacheMemoryContext)
  		CacheMemoryContext = AllocSetContextCreate(TopMemoryContext,
  												   "CacheMemoryContext",
! 												   ALLOCSET_DEFAULT_MINSIZE,
! 												   ALLOCSET_DEFAULT_INITSIZE,
! 												   ALLOCSET_DEFAULT_MAXSIZE);
  }
  
  
***************
*** 560,565 ****
--- 564,576 ----
  				cl->refcount = 0;
  			}
  
+ 			/*
+ 			 * Reset the refcount stack.  Drop the item count to zero,
+ 			 * but don't deallocate the stack itself, so it can be used by
+ 			 * future subtransactions.
+ 			 */
+ 			cl->numpushes = 0;
+ 
  			/* Clean up any now-deletable dead entries */
  			if (cl->dead)
  				CatCacheRemoveCList(ccp, cl);
***************
*** 585,590 ****
--- 596,608 ----
  			ct->refcount = 0;
  		}
  
+ 		/*
+ 		 * Reset the refcount stack.  Drop the item count to zero,
+ 		 * but don't deallocate the stack itself, so it can be used by
+ 		 * future subtransactions.
+ 		 */
+ 		ct->numpushes = 0;
+ 
  		/* Clean up any now-deletable dead entries */
  		if (ct->dead)
  			CatCacheRemoveCTup(ct->my_cache, ct);
***************
*** 592,597 ****
--- 610,770 ----
  }
  
  /*
+  * AtSubStart_CatCache
+  *
+  * Saves reference counts of each entry at subtransaction start so they
+  * can be restored if the subtransaction later aborts.
+  */
+ void
+ AtSubStart_CatCache(void)
+ {
+ 	CatCache   *ccp;
+ 	Dlelem	   *elt,
+ 			   *nextelt;
+ 	MemoryContext old_cxt;
+    
+ 
+ 	old_cxt = MemoryContextSwitchTo(CacheMemoryContext);
+ 
+ 	/*
+ 	 * Prepare CLists
+ 	 */
+ 	for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
+ 	{
+ 		for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt)
+ 		{
+ 			CatCList   *cl = (CatCList *) DLE_VAL(elt);
+ 
+ 			nextelt = DLGetSucc(elt);
+ 
+ 			if (cl->numpushes == cl->numalloc)
+ 			{
+ 				if (cl->numalloc == 0)
+ 				{
+ 					cl->numalloc = 8;
+ 					cl->prev_refcount = palloc(sizeof(int) * cl->numalloc);
+ 				}
+ 				else
+ 				{
+ 					cl->numalloc *= 2;
+ 					cl->prev_refcount = repalloc(cl->prev_refcount, cl->numalloc * sizeof(int));
+ 				}
+ 			}
+ 
+ 			cl->prev_refcount[cl->numpushes++] = cl->refcount;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * Prepare CTuples
+ 	 */
+ 	for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = nextelt)
+ 	{
+ 		CatCTup    *ct = (CatCTup *) DLE_VAL(elt);
+ 
+ 		nextelt = DLGetSucc(elt);
+ 
+ 		if (ct->numpushes == ct->numalloc)
+ 		{
+ 			if (ct->numalloc == 0)
+ 			{
+ 				ct->numalloc = 8;
+ 				ct->prev_refcount = palloc(sizeof(int) * ct->numalloc);
+ 			}
+ 			else
+ 			{
+ 				ct->numalloc *= 2;
+ 				ct->prev_refcount = repalloc(ct->prev_refcount, sizeof(int) * ct->numalloc);
+ 			}
+ 		}
+ 
+ 		ct->prev_refcount[ct->numpushes++] = ct->refcount;
+ 	}
+ 
+ 	MemoryContextSwitchTo(old_cxt);
+ }
+ 
+ void
+ AtEOSubXact_CatCache(bool commit)
+ {
+ 	CatCache   *ccp;
+ 	Dlelem	   *elt,
+ 			   *nextelt;
+ 	
+ 	/*
+ 	 * Restore CLists
+ 	 */
+ 	for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next)
+ 	{
+ 		for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt)
+ 		{
+ 			CatCList   *cl = (CatCList *) DLE_VAL(elt);
+ 
+ 			nextelt = DLGetSucc(elt);
+ 
+ 			/*
+ 			 * During commit, check whether the count is what
+ 			 * we expect.
+ 			 */
+ 			if (commit)
+ 			{
+ 				int expected_refcount;
+ 				if (cl->numpushes > 0)
+ 					expected_refcount = cl->prev_refcount[cl->numpushes - 1];
+ 				else
+ 					expected_refcount = 0;
+ 
+ 				if (cl->refcount != expected_refcount)
+ 					elog(WARNING, "catcache reference leak");
+ 			}
+ 
+ 			/*
+ 			 * During abort we have to restore the original count;
+ 			 * during commit, we have to restore in case of a leak,
+ 			 * and it won't harm if this is the expected count.
+ 			 */
+ 			if (cl->numpushes > 0)
+ 				cl->refcount = cl->prev_refcount[--cl->numpushes];
+ 			else
+ 				cl->refcount = 0;
+ 		}
+ 	}
+ 
+ 	/*
+ 	 * Prepare CTuples
+ 	 */
+ 	for (elt = DLGetHead(&CacheHdr->ch_lrulist); elt; elt = nextelt)
+ 	{
+ 		CatCTup    *ct = (CatCTup *) DLE_VAL(elt);
+ 
+ 		nextelt = DLGetSucc(elt);
+ 
+ 		if (commit)
+ 		{
+ 			int expected_refcount;
+ 
+ 			if (ct->numpushes > 0)
+ 				expected_refcount = ct->prev_refcount[ct->numpushes - 1];
+ 			else
+ 				expected_refcount = 0;
+ 
+ 			if (ct->refcount != expected_refcount)
+ 				elog(WARNING, "catcache reference leak");
+ 		}
+ 
+ 		/*
+ 		 * During abort we have to restore the original count;
+ 		 * during commit, we have to restore in case of a leak,
+ 		 * and it won't harm if this is the expected count.
+ 		 */
+ 		if (ct->numpushes > 0)
+ 			ct->refcount = ct->prev_refcount[--ct->numpushes];
+ 		else
+ 			ct->refcount = 0;
+ 	}
+ }
+ 
+ /*
   *		ResetCatalogCache
   *
   * Reset one catalog cache to empty.
***************
*** 1505,1510 ****
--- 1678,1686 ----
  	cl->my_cache = cache;
  	DLInitElem(&cl->cache_elem, (void *) cl);
  	cl->refcount = 1;			/* count this first reference */
+ 	cl->prev_refcount = NULL;
+ 	cl->numpushes = 0;
+ 	cl->numalloc = 0;
  	cl->dead = false;
  	cl->ordered = ordered;
  	cl->nkeys = nkeys;
***************
*** 1603,1608 ****
--- 1779,1787 ----
  	ct->dead = false;
  	ct->negative = negative;
  	ct->hash_value = hashValue;
+ 	ct->prev_refcount = NULL;
+ 	ct->numpushes = 0;
+ 	ct->numalloc = 0;
  
  	DLAddHead(&CacheHdr->ch_lrulist, &ct->lrulist_elem);
  	DLAddHead(&cache->cc_bucket[hashIndex], &ct->cache_elem);
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/utils/cache/relcache.c 09withList/src/backend/utils/cache/relcache.c
*** 00orig/src/backend/utils/cache/relcache.c	2004-05-26 10:27:11.000000000 -0400
--- 09withList/src/backend/utils/cache/relcache.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 274,279 ****
--- 274,281 ----
  				  StrategyNumber numSupport);
  
  
+ static inline void RelationPushReferenceCount(Relation rel);
+ static inline void RelationPopReferenceCount(Relation rel);
  /*
   *		ScanPgRelation
   *
***************
*** 1661,1666 ****
--- 1663,1670 ----
  	freeList(relation->rd_indexlist);
  	if (relation->rd_indexcxt)
  		MemoryContextDelete(relation->rd_indexcxt);
+ 	if (relation->rd_prevrefcnt)
+ 		pfree(relation->rd_prevrefcnt);
  
  	/*
  	 * If we're really done with the relcache entry, blow it away. But if
***************
*** 2020,2025 ****
--- 2024,2035 ----
  		}
  
  		/*
+ 		 * Reset the refcount stack.  Just drop the item count; don't deallocate
+ 		 * the stack itself so it can be reused by future subtransactions.
+ 		 */
+ 		relation->rd_numpushed = 0;
+ 
+ 		/*
  		 * Flush any temporary index list.
  		 */
  		if (relation->rd_indexvalid == 2)
***************
*** 2032,2037 ****
--- 2042,2172 ----
  }
  
  /*
+  * RelationPushReferenceCount
+  *
+  * Push the current reference count into the stack.  Don't modify the
+  * reference count itself.
+  */
+ static inline void
+ RelationPushReferenceCount(Relation rel)
+ {
+ 	/* Enlarge the stack if we run out of space. */
+ 	if (rel->rd_numpushed == rel->rd_numalloc)
+ 	{
+ 		MemoryContext	old_cxt = MemoryContextSwitchTo(CacheMemoryContext);
+ 
+ 		if (rel->rd_numalloc == 0)
+ 		{
+ 			rel->rd_numalloc = 8;
+ 			rel->rd_prevrefcnt = palloc(rel->rd_numalloc * sizeof(int));
+ 		}
+ 		else
+ 		{
+ 			rel->rd_numalloc *= 2;
+ 			rel->rd_prevrefcnt = repalloc(rel->rd_prevrefcnt, rel->rd_numalloc * sizeof(int));
+ 		}
+ 
+ 		MemoryContextSwitchTo(old_cxt);
+ 	}
+ 
+ 	rel->rd_prevrefcnt[rel->rd_numpushed++] = rel->rd_refcnt;
+ }
+ 
+ /*
+  * RelationPopReferenceCount
+  *
+  * Pop the latest stored reference count.  If there is none, drop it
+  * to zero; the entry was created in the current subtransaction.
+  */
+ static inline void
+ RelationPopReferenceCount(Relation rel)
+ {
+ 	if (rel->rd_numpushed == 0)
+ 	{
+ 		rel->rd_refcnt = rel->rd_isnailed ? 1 : 0;
+ 		return;
+ 	}
+ 
+ 	rel->rd_refcnt = rel->rd_prevrefcnt[--rel->rd_numpushed];
+ }
+ 
+ /*
+  * AtEOSubXact_RelationCache
+  */
+ void
+ AtEOSubXact_RelationCache(bool commit)
+ {
+ 	HASH_SEQ_STATUS status;
+ 	RelIdCacheEnt *idhentry;
+ 
+ 	/* We'd better not be bootstrapping. */
+ 	Assert(!IsBootstrapProcessingMode());
+ 
+ 	hash_seq_init(&status, RelationIdCache);
+ 
+ 	while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
+ 	{
+ 		Relation	relation = idhentry->reldesc;
+ 
+ 		/*
+ 		 * During subtransaction commit, we first check whether the
+ 		 * current refcount is correct: if there is no item in the stack,
+ 		 * the relcache entry was created during this subtransaction, it should
+ 		 * be 0 (or 1 for nailed relations).  If the stack has at least one
+ 		 * item, the expected count is whatever that item is.
+ 		 */
+ 		if (commit)
+ 		{
+ 			int expected_refcnt;
+ 
+ 			if (relation->rd_numpushed == 0)
+ 				expected_refcnt = relation->rd_isnailed ? 1 : 0;
+ 			else
+ 				expected_refcnt = relation->rd_prevrefcnt[relation->rd_numpushed - 1];
+ 
+ 			if (relation->rd_refcnt != expected_refcnt)
+ 			{
+ 				elog(WARNING, "relcache reference leak: relation \"%s\" has refcnt %d instead of %d",
+ 						RelationGetRelationName(relation),
+ 						relation->rd_refcnt, expected_refcnt);
+ 			}
+ 		}
+ 
+ 		/*
+ 		 * On commit, the expected count is stored so there's no harm in
+ 		 * popping it (and we may need to fix if there was a leak); and during
+ 		 * abort, the correct refcount has to be restored.
+ 		 */
+ 		RelationPopReferenceCount(relation);
+ 	}
+ }
+ 
+ /*
+  * AtSubStart_RelationCache
+  *
+  * At subtransaction start, we push the current reference count into
+  * the refcount stack, so it can be restored if the subtransaction aborts.
+  */
+ void
+ AtSubStart_RelationCache(void)
+ {
+ 	HASH_SEQ_STATUS status;
+ 	RelIdCacheEnt *idhentry;
+ 
+ 	/* We'd better not be bootstrapping. */
+ 	Assert(!IsBootstrapProcessingMode());
+ 
+ 	hash_seq_init(&status, RelationIdCache);
+ 
+ 	while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
+ 	{
+ 		Relation	relation = idhentry->reldesc;
+ 
+ 		RelationPushReferenceCount(relation);
+ 	}
+ }
+ 
+ /*
   *		RelationBuildLocalRelation
   *			Build a relcache entry for an about-to-be-created relation,
   *			and enter it into the relcache.
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/utils/hash/dynahash.c 09withList/src/backend/utils/hash/dynahash.c
*** 00orig/src/backend/utils/hash/dynahash.c	2004-04-23 15:36:08.000000000 -0400
--- 09withList/src/backend/utils/hash/dynahash.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 410,415 ****
--- 410,530 ----
  	return nDirEntries;
  }
  
+ /*
+  * hash_copy
+  *
+  * Create an identical copy of the supplied hash table.
+  */
+ HTAB *
+ hash_copy(HTAB *orig_hash)
+ {
+ 	HTAB *newhash;
+ 	HASHHDR *hctl;
+ 	HASHSEGMENT *segp;
+ 	int nsegs, elementSize;
+ 	HASHELEMENT *tmpElems;
+ 
+ 	if (orig_hash->isshared)
+ 		elog(ERROR, "hash_copy: cannot copy a shared hash");
+ 
+ 	CurrentDynaHashCxt = DynaHashCxt;
+ 
+ 	/* Copy the top control structure */
+ 	newhash = (HTAB *) MEM_ALLOC(sizeof(HTAB));
+ 	memcpy(newhash, orig_hash, sizeof(HTAB));
+ 	newhash->tabname = (char *) MEM_ALLOC(strlen(orig_hash->tabname) + 1);
+ 	strcpy(newhash->tabname, orig_hash->tabname);
+ 
+ 	/* Create a new memory context */
+ 	newhash->hcxt = AllocSetContextCreate(DynaHashCxt,
+ 										  newhash->tabname,
+ 										  ALLOCSET_SMALL_MINSIZE,
+ 										  ALLOCSET_SMALL_INITSIZE,
+ 										  ALLOCSET_SMALL_MAXSIZE);
+ 
+ 	/* Copy the header structure */
+ 	hctl = (HASHHDR *) MEM_ALLOC(sizeof(HASHHDR));
+ 	memcpy(hctl, orig_hash->hctl, sizeof(HASHHDR));
+ 	newhash->hctl = hctl;
+ 	hctl->freeList = NULL;
+ 
+ 	CurrentDynaHashCxt = newhash->hcxt;
+ 
+ 	/* Allocate a directory */
+ 	newhash->dir = (HASHSEGMENT *)
+ 		newhash->alloc(hctl->dsize * sizeof(HASHSEGMENT));
+ 	if (!newhash->dir)
+ 		elog(ERROR, "out of memory");
+ 
+ 	/* Allocate initial segments */
+ 	for (segp = newhash->dir, nsegs = 0; nsegs < hctl->nsegs; segp++, nsegs++)
+ 	{
+ 		*segp = seg_alloc(newhash);
+ 		if (*segp == NULL)
+ 			return false;
+ 	}
+ 
+ 	elementSize = MAXALIGN(sizeof(HASHELEMENT)) + MAXALIGN(hctl->entrysize);
+ 
+ 	tmpElems = (HASHELEMENT *)
+ 		newhash->alloc(elementSize * newhash->hctl->nentries);
+ 
+ 	/* now copy the elements */
+ 	{
+ 		uint32 curBucket = 0;
+ 		HASHELEMENT *curEntry = NULL;
+ 		HASHELEMENT *prevElem = NULL;
+ 		int elements = 0;
+ 
+ 		/* This code is copied almost verbatim from hash_seq_search */
+ 		while (curBucket <= orig_hash->hctl->max_bucket)
+ 		{
+ 			long		segment_num;
+ 			long		segment_ndx;
+ 			HASHSEGMENT seg;
+ 			segment_num = curBucket >> orig_hash->hctl->sshift;
+ 			segment_ndx = MOD(curBucket, orig_hash->hctl->ssize);
+ 
+ 			if (curEntry != NULL)
+ 			{
+ 				memcpy(tmpElems, curEntry, elementSize);
+ 				tmpElems->link = NULL;
+ 
+ 				if (prevElem != NULL)
+ 					prevElem->link = tmpElems;
+ 				prevElem = tmpElems;
+ 
+ 				/* insert the element into the new hash structure */
+ 				seg = newhash->dir[segment_num];
+ 				seg[segment_ndx] = tmpElems;
+ 
+ 				tmpElems = (HASHELEMENT *) (((char *) tmpElems) + elementSize);
+ 
+ 				curEntry = curEntry->link;
+ 				if (curEntry == NULL)
+ 				{
+ 					curBucket++;
+ 					prevElem = NULL;
+ 				}
+ 				continue;
+ 			}
+ 
+ 			seg = orig_hash->dir[segment_num];
+ 			if (seg == NULL)
+ 				hash_corrupted(orig_hash);
+ 
+ 			curEntry = seg[segment_ndx];
+ 			if (curEntry == NULL)
+ 			{
+ 				curBucket++;
+ 				prevElem = NULL;
+ 			}
+ 		}
+ 	}
+ 
+ 	return newhash;
+ }
+ 
  
  /********************** DESTROY ROUTINES ************************/
  
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/utils/mmgr/mcxt.c 09withList/src/backend/utils/mmgr/mcxt.c
*** 00orig/src/backend/utils/mmgr/mcxt.c	2003-12-18 00:56:35.000000000 -0300
--- 09withList/src/backend/utils/mmgr/mcxt.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 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;
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/utils/mmgr/portalmem.c 09withList/src/backend/utils/mmgr/portalmem.c
*** 00orig/src/backend/utils/mmgr/portalmem.c	2004-03-21 13:54:46.000000000 -0400
--- 09withList/src/backend/utils/mmgr/portalmem.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 511,513 ****
--- 511,601 ----
  		PortalDrop(portal, true);
  	}
  }
+ 
+ /*
+  * 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);
+ 	}
+ }
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/utils/mmgr/README 09withList/src/backend/utils/mmgr/README
*** 00orig/src/backend/utils/mmgr/README	2003-12-18 00:56:35.000000000 -0300
--- 09withList/src/backend/utils/mmgr/README	2004-05-26 11:41:17.000000000 -0400
***************
*** 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
diff -Ncr --exclude-from=diff-ignore 00orig/src/backend/utils/time/tqual.c 09withList/src/backend/utils/time/tqual.c
*** 00orig/src/backend/utils/time/tqual.c	2003-12-18 00:56:35.000000000 -0300
--- 09withList/src/backend/utils/time/tqual.c	2004-05-26 12:40:28.000000000 -0400
***************
*** 23,28 ****
--- 23,29 ----
  
  #include "postgres.h"
  
+ #include "access/subtrans.h"
  #include "storage/sinval.h"
  #include "utils/tqual.h"
  
***************
*** 115,120 ****
--- 116,126 ----
  			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
  				return true;
  
+ 			/* deleting subtransaction aborted */
+ 			if (HeapTupleHeaderHasXmax(tuple))
+ 				if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
+ 					return true;
+ 
  			Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
  
  			if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
***************
*** 255,260 ****
--- 261,271 ----
  		}
  		else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
  		{
+ 			/* deleting subtransaction aborted */
+ 			if (HeapTupleHeaderHasXmax(tuple))
+ 				if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
+ 					return true;
+ 
  			if (HeapTupleHeaderGetCmin(tuple) >= GetCurrentCommandId())
  				return false;	/* inserted after scan started */
  
***************
*** 434,439 ****
--- 445,455 ----
  		}
  		else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
  		{
+ 			/* deleting subtransaction aborted */
+ 			if (HeapTupleHeaderHasXmax(tuple))
+ 				if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
+ 					return HeapTupleMayBeUpdated;
+ 
  			if (HeapTupleHeaderGetCmin(tuple) >= curcid)
  				return HeapTupleInvisible;		/* inserted after scan
  												 * started */
***************
*** 575,580 ****
--- 591,601 ----
  			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
  				return true;
  
+ 			/* deleting subtransaction aborted */
+ 			if (HeapTupleHeaderHasXmax(tuple))
+ 				if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
+ 					return true;
+ 
  			Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)));
  
  			if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
***************
*** 706,711 ****
--- 727,736 ----
  		}
  		else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))
  		{
+ 			if (HeapTupleHeaderHasXmax(tuple))
+ 				if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
+ 					return true;
+ 
  			if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
  				return false;	/* inserted after scan started */
  
***************
*** 747,753 ****
  
  		for (i = 0; i < snapshot->xcnt; i++)
  		{
! 			if (TransactionIdEquals(HeapTupleHeaderGetXmin(tuple),
  									snapshot->xip[i]))
  				return false;
  		}
--- 772,778 ----
  
  		for (i = 0; i < snapshot->xcnt; i++)
  		{
! 			if (SubTransXidsHaveCommonAncestor(HeapTupleHeaderGetXmin(tuple),
  									snapshot->xip[i]))
  				return false;
  		}
***************
*** 792,798 ****
  			return true;
  		for (i = 0; i < snapshot->xcnt; i++)
  		{
! 			if (TransactionIdEquals(HeapTupleHeaderGetXmax(tuple), snapshot->xip[i]))
  				return true;
  		}
  	}
--- 817,823 ----
  			return true;
  		for (i = 0; i < snapshot->xcnt; i++)
  		{
! 			if (SubTransXidsHaveCommonAncestor(HeapTupleHeaderGetXmax(tuple), snapshot->xip[i]))
  				return true;
  		}
  	}
***************
*** 868,875 ****
  		{
  			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
  				return HEAPTUPLE_INSERT_IN_PROGRESS;
! 			Assert(HeapTupleHeaderGetXmin(tuple) ==
! 				   HeapTupleHeaderGetXmax(tuple));
  			if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
  				return HEAPTUPLE_INSERT_IN_PROGRESS;
  			/* inserted and then deleted by same xact */
--- 893,904 ----
  		{
  			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */
  				return HEAPTUPLE_INSERT_IN_PROGRESS;
! 			/* deleting subtransaction aborted */
! 			if (HeapTupleHeaderHasXmax(tuple))
! 				if (TransactionIdDidAbort(HeapTupleHeaderGetXmax(tuple)))
! 					return HEAPTUPLE_INSERT_IN_PROGRESS;
! 			Assert(SubTransXidsHaveCommonAncestor(HeapTupleHeaderGetXmin(tuple),
! 						HeapTupleHeaderGetXmax(tuple)));
  			if (tuple->t_infomask & HEAP_MARKED_FOR_UPDATE)
  				return HEAPTUPLE_INSERT_IN_PROGRESS;
  			/* inserted and then deleted by same xact */
***************
*** 943,949 ****
  	 * Deleter committed, but check special cases.
  	 */
  
! 	if (TransactionIdEquals(HeapTupleHeaderGetXmin(tuple),
  							HeapTupleHeaderGetXmax(tuple)))
  	{
  		/*
--- 972,978 ----
  	 * Deleter committed, but check special cases.
  	 */
  
! 	if (SubTransXidsHaveCommonAncestor(HeapTupleHeaderGetXmin(tuple),
  							HeapTupleHeaderGetXmax(tuple)))
  	{
  		/*
diff -Ncr --exclude-from=diff-ignore 00orig/src/bin/initdb/initdb.c 09withList/src/bin/initdb/initdb.c
*** 00orig/src/bin/initdb/initdb.c	2004-05-25 15:13:51.000000000 -0400
--- 09withList/src/bin/initdb/initdb.c	2004-05-26 11:41:17.000000000 -0400
***************
*** 1796,1802 ****
  	char	   *pgdenv;			/* PGDATA value got from sent to
  								 * environment */
  	char	   *subdirs[] =
! 	{"global", "pg_xlog", "pg_clog", "base", "base/1"};
  	init_nls(argv[0]);
  
  	progname = get_progname(argv[0]);
--- 1796,1802 ----
  	char	   *pgdenv;			/* PGDATA value got from sent to
  								 * environment */
  	char	   *subdirs[] =
! 	{"global", "pg_xlog", "pg_clog", "pg_subtrans", "base", "base/1" };
  	init_nls(argv[0]);
  
  	progname = get_progname(argv[0]);
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/access/clog.h 09withList/src/include/access/clog.h
*** 00orig/src/include/access/clog.h	2004-01-15 23:32:29.000000000 -0300
--- 09withList/src/include/access/clog.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 16,28 ****
  /*
   * Possible transaction statuses --- note that all-zeroes is the initial
   * state.
   */
  typedef int XidStatus;
  
  #define TRANSACTION_STATUS_IN_PROGRESS		0x00
  #define TRANSACTION_STATUS_COMMITTED		0x01
  #define TRANSACTION_STATUS_ABORTED			0x02
! /* 0x03 is available without changing commit log space allocation */
  
  /* exported because lwlock.c needs it */
  #define NUM_CLOG_BUFFERS	8
--- 16,31 ----
  /*
   * Possible transaction statuses --- note that all-zeroes is the initial
   * state.
+  *
+  * A "subcommitted" transaction is a committed subtransaction whose parent
+  * hasn't committed or aborted yet.
   */
  typedef int XidStatus;
  
  #define TRANSACTION_STATUS_IN_PROGRESS		0x00
  #define TRANSACTION_STATUS_COMMITTED		0x01
  #define TRANSACTION_STATUS_ABORTED			0x02
! #define TRANSACTION_STATUS_SUB_COMMITTED	0x03
  
  /* exported because lwlock.c needs it */
  #define NUM_CLOG_BUFFERS	8
***************
*** 40,50 ****
  extern void ExtendCLOG(TransactionId newestXact);
  extern void TruncateCLOG(TransactionId oldestXact);
  
- /* XLOG stuff */
- #define CLOG_ZEROPAGE		0x00
- 
- extern void clog_redo(XLogRecPtr lsn, XLogRecord *record);
- extern void clog_undo(XLogRecPtr lsn, XLogRecord *record);
- extern void clog_desc(char *buf, uint8 xl_info, char *rec);
- 
  #endif   /* CLOG_H */
--- 43,46 ----
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/access/htup.h 09withList/src/include/access/htup.h
*** 00orig/src/include/access/htup.h	2004-04-14 16:45:58.000000000 -0400
--- 09withList/src/include/access/htup.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 172,178 ****
  										 * attribute(s) */
  #define HEAP_HASEXTENDED		0x000C	/* the two above combined */
  #define HEAP_HASOID				0x0010	/* has an object-id field */
! /* bit 0x0020 is presently unused */
  #define HEAP_XMAX_IS_XMIN		0x0040	/* created and deleted in the same
  										 * transaction */
  #define HEAP_XMAX_UNLOGGED		0x0080	/* to lock tuple for update
--- 172,178 ----
  										 * attribute(s) */
  #define HEAP_HASEXTENDED		0x000C	/* the two above combined */
  #define HEAP_HASOID				0x0010	/* has an object-id field */
! #define HEAP_CMIN_OVERWRITTEN	0x0020	/* cmin is overwritten with xmax */
  #define HEAP_XMAX_IS_XMIN		0x0040	/* created and deleted in the same
  										 * transaction */
  #define HEAP_XMAX_UNLOGGED		0x0080	/* to lock tuple for update
***************
*** 225,230 ****
--- 225,231 ----
  	else \
  	{ \
  		(tup)->t_infomask &= ~HEAP_XMAX_IS_XMIN; \
+ 		(tup)->t_infomask |= HEAP_CMIN_OVERWRITTEN; \
  		TransactionIdStore(_newxid, &(tup)->t_choice.t_heap.t_field2.t_xmax); \
  	} \
  } while (0)
***************
*** 236,241 ****
--- 237,244 ----
   * bit will be set again if the deleting transaction aborts, there'd be no
   * real gain in safety from the extra test.  So, just rely on the caller not
   * to trust the value unless it's meaningful.
+  *
+  * XXX We could check HEAP_CMIN_OVERWRITTEN.
   */
  #define HeapTupleHeaderGetCmin(tup) \
  ( \
***************
*** 271,276 ****
--- 274,284 ----
  		InvalidTransactionId \
  )
  
+ #define HeapTupleHeaderHasXmax(tup) \
+ ( \
+     (tup)->t_infomask & HEAP_CMIN_OVERWRITTEN \
+ )
+ 
  #define HeapTupleHeaderSetXvac(tup, xid) \
  do { \
  	Assert((tup)->t_infomask & HEAP_MOVED); \
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/access/rmgr.h 09withList/src/include/access/rmgr.h
*** 00orig/src/include/access/rmgr.h	2004-01-15 23:32:29.000000000 -0300
--- 09withList/src/include/access/rmgr.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 16,22 ****
  #define RM_XLOG_ID				0
  #define RM_XACT_ID				1
  #define RM_SMGR_ID				2
! #define RM_CLOG_ID				3
  #define RM_HEAP_ID				10
  #define RM_BTREE_ID				11
  #define RM_HASH_ID				12
--- 16,22 ----
  #define RM_XLOG_ID				0
  #define RM_XACT_ID				1
  #define RM_SMGR_ID				2
! #define RM_SLRU_ID				3
  #define RM_HEAP_ID				10
  #define RM_BTREE_ID				11
  #define RM_HASH_ID				12
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/access/slru.h 09withList/src/include/access/slru.h
*** 00orig/src/include/access/slru.h	2004-01-15 23:32:29.000000000 -0300
--- 09withList/src/include/access/slru.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 59,62 ****
--- 59,73 ----
  extern void SimpleLruFlush(SlruCtl ctl, bool checkpoint);
  extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage);
  
+ /* XLOG stuff */
+ #define CLOG_ZEROPAGE		0x00
+ #define SUBTRANS_ZEROPAGE	0x10
+ 
+ extern void slru_redo(XLogRecPtr lsn, XLogRecord *record);
+ extern void slru_undo(XLogRecPtr lsn, XLogRecord *record);
+ extern void slru_desc(char *buf, uint8 xl_info, char *rec);
+ 
+ extern void clog_zeropage_redo(int pageno);
+ extern void subtrans_zeropage_redo(int pageno);
+ 
  #endif   /* SLRU_H */
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/access/subtrans.h 09withList/src/include/access/subtrans.h
*** 00orig/src/include/access/subtrans.h	1969-12-31 21:00:00.000000000 -0300
--- 09withList/src/include/access/subtrans.h	2004-05-26 11:54:13.000000000 -0400
***************
*** 0 ****
--- 1,33 ----
+ /*
+  * subtrans.h
+  *
+  * PostgreSQL subtrans-log manager
+  *
+  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+  * Portions Copyright (c) 1994, Regents of the University of California
+  *
+  * $PostgreSQL$
+  */
+ #ifndef SUBTRANS_H
+ #define SUBTRANS_H
+ 
+ #include "access/xlog.h"
+ 
+ /* exported because lwlock.c needs it */
+ #define NUM_SUBTRANS_BUFFERS	8
+ 
+ extern void SubTransSetParent(TransactionId xid, TransactionId parent);
+ extern TransactionId SubTransGetParent(TransactionId xid);
+ extern TransactionId SubTransGetTopmostTransaction(TransactionId xid);
+ extern bool SubTransXidsHaveCommonAncestor(TransactionId xid1, TransactionId xid2);
+ 
+ extern int	SUBTRANSShmemSize(void);
+ extern void SUBTRANSShmemInit(void);
+ extern void BootStrapSUBTRANS(void);
+ extern void StartupSUBTRANS(void);
+ extern void ShutdownSUBTRANS(void);
+ extern void CheckPointSUBTRANS(void);
+ extern void ExtendSUBTRANS(TransactionId newestXact);
+ extern void TruncateSUBTRANS(TransactionId oldestXact);
+ 
+ #endif   /* SUBTRANS_H */
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/access/transam.h 09withList/src/include/access/transam.h
*** 00orig/src/include/access/transam.h	2004-01-15 23:32:29.000000000 -0300
--- 09withList/src/include/access/transam.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 14,20 ****
  #ifndef TRANSAM_H
  #define TRANSAM_H
  
- 
  /* ----------------
   *		Special transaction ID values
   *
--- 14,19 ----
***************
*** 107,119 ****
  extern bool TransactionIdDidAbort(TransactionId transactionId);
  extern void TransactionIdCommit(TransactionId transactionId);
  extern void TransactionIdAbort(TransactionId transactionId);
  extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2);
  extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2);
  extern bool TransactionIdFollows(TransactionId id1, TransactionId id2);
  extern bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2);
  
  /* in transam/varsup.c */
! extern TransactionId GetNewTransactionId(void);
  extern TransactionId ReadNewTransactionId(void);
  extern Oid	GetNewObjectId(void);
  extern void CheckMaxObjectId(Oid assigned_oid);
--- 106,122 ----
  extern bool TransactionIdDidAbort(TransactionId transactionId);
  extern void TransactionIdCommit(TransactionId transactionId);
  extern void TransactionIdAbort(TransactionId transactionId);
+ extern void TransactionIdSubCommit(TransactionId transactionId);
+ extern void TransactionIdSubAbort(int nchilds, TransactionId *childs);
+ extern void TransactionIdCommitTree(int nchilds, TransactionId *childs);
  extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2);
  extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2);
  extern bool TransactionIdFollows(TransactionId id1, TransactionId id2);
  extern bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2);
+ extern void TransactionIdSetSubtransCutoff(TransactionId xid);
  
  /* in transam/varsup.c */
! extern TransactionId GetNewTransactionId(bool isSubXact);
  extern TransactionId ReadNewTransactionId(void);
  extern Oid	GetNewObjectId(void);
  extern void CheckMaxObjectId(Oid assigned_oid);
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/access/xact.h 09withList/src/include/access/xact.h
*** 00orig/src/include/access/xact.h	2004-05-23 23:31:41.000000000 -0400
--- 09withList/src/include/access/xact.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 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,105 ----
  	int				startTimeUsec;
  	TransState		state;
  	TBlockState		blockState;
+ 	int				nestingLevel;
+ 	MemoryContext	commitContext;
+ 	void		   *deferredTriggers;
+ 	List		   *pendingDeletes;
+ 	List 		   *notifies;
+ 	List		   *childXids;
+ 	List		   *bufRefCnts;
+ 	uint32			XLogRecOff;
+ 	struct TransactionStateData *parent;
  } TransactionStateData;
  
  typedef TransactionStateData *TransactionState;
***************
*** 103,110 ****
  {
  	time_t		xtime;
  	/* Array of RelFileNode(s) to drop at commit */
! 	/* The XLOG record length determines how many there are */
  	RelFileNode	xnodes[1];		/* VARIABLE LENGTH ARRAY */
  } xl_xact_commit;
  
  #define MinSizeOfXactCommit	offsetof(xl_xact_commit, xnodes)
--- 121,130 ----
  {
  	time_t		xtime;
  	/* Array of RelFileNode(s) to drop at commit */
! 	int			nrels;			/* number of RelFileNodes */
  	RelFileNode	xnodes[1];		/* VARIABLE LENGTH ARRAY */
+ 	/* ARRAY OF COMMITTED XIDs FOLLOWS */
+ 	/* The XLOG record length determines how many there are */
  } xl_xact_commit;
  
  #define MinSizeOfXactCommit	offsetof(xl_xact_commit, xnodes)
***************
*** 112,125 ****
  typedef struct xl_xact_abort
  {
  	time_t		xtime;
  	/* Array of RelFileNode(s) to drop at abort */
- 	/* The XLOG record length determines how many there are */
  	RelFileNode	xnodes[1];		/* VARIABLE LENGTH ARRAY */
  } xl_xact_abort;
  
  #define MinSizeOfXactAbort offsetof(xl_xact_abort, xnodes)
  
- 
  /* ----------------
   *		extern definitions
   * ----------------
--- 132,146 ----
  typedef struct xl_xact_abort
  {
  	time_t		xtime;
+ 	int			nrels;			/* number of RelFileNodes */
  	/* Array of RelFileNode(s) to drop at abort */
  	RelFileNode	xnodes[1];		/* VARIABLE LENGTH ARRAY */
+ 	/* The XLOG record length determines how many Xids there are */
+ 	/* ARRAY OF ABORTED XIDs FOLLOWS */
  } xl_xact_abort;
  
  #define MinSizeOfXactAbort offsetof(xl_xact_abort, xnodes)
  
  /* ----------------
   *		extern definitions
   * ----------------
***************
*** 127,137 ****
  extern bool IsTransactionState(void);
  extern bool IsAbortedTransactionBlockState(void);
  extern TransactionId GetCurrentTransactionId(void);
  extern CommandId GetCurrentCommandId(void);
  extern AbsoluteTime GetCurrentTransactionStartTime(void);
  extern AbsoluteTime GetCurrentTransactionStartTimeUsec(int *usec);
  extern bool TransactionIdIsCurrentTransactionId(TransactionId xid);
- extern bool CommandIdIsCurrentCommandId(CommandId cid);
  extern void CommandCounterIncrement(void);
  extern void StartTransactionCommand(void);
  extern void CommitTransactionCommand(void);
--- 148,171 ----
  extern bool IsTransactionState(void);
  extern bool IsAbortedTransactionBlockState(void);
  extern TransactionId GetCurrentTransactionId(void);
+ extern TransactionId GetCurrentTransactionTreeId(void);
  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 List *TransactionGetBufferRefcounts(void);
+ extern void TransactionSetBufferRefcounts(List *);
+ 
  extern bool TransactionIdIsCurrentTransactionId(TransactionId xid);
  extern void CommandCounterIncrement(void);
  extern void StartTransactionCommand(void);
  extern void CommitTransactionCommand(void);
***************
*** 151,156 ****
--- 185,192 ----
  
  extern void RecordTransactionCommit(void);
  
+ extern int xactGetCommittedChilds(TransactionId **ptr, bool metoo);
+ 
  extern void XactPushRollback(void (*func) (void *), void *data);
  extern void XactPopRollback(void);
  
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/commands/tablecmds.h 09withList/src/include/commands/tablecmds.h
*** 00orig/src/include/commands/tablecmds.h	2004-05-05 15:40:46.000000000 -0400
--- 09withList/src/include/commands/tablecmds.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 42,47 ****
  extern void remove_on_commit_action(Oid relid);
  
  extern void PreCommit_on_commit_actions(void);
! extern void AtEOXact_on_commit_actions(bool isCommit);
  
  #endif   /* TABLECMDS_H */
--- 42,47 ----
  extern void remove_on_commit_action(Oid relid);
  
  extern void PreCommit_on_commit_actions(void);
! extern void AtEOXact_on_commit_actions(bool isCommit, bool isSubtrans);
  
  #endif   /* TABLECMDS_H */
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/storage/bufmgr.h 09withList/src/include/storage/bufmgr.h
*** 00orig/src/include/storage/bufmgr.h	2004-05-12 00:06:16.000000000 -0400
--- 09withList/src/include/storage/bufmgr.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 153,158 ****
--- 153,160 ----
  extern char *ShowBufferUsage(void);
  extern void ResetBufferUsage(void);
  extern void AtEOXact_Buffers(bool isCommit);
+ extern void AtSubStart_Buffers(void);
+ extern void AtEOSubXact_Buffers(bool commit);
  extern void FlushBufferPool(void);
  extern BlockNumber BufferGetBlockNumber(Buffer buffer);
  extern BlockNumber RelationGetNumberOfBlocks(Relation relation);
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/storage/lock.h 09withList/src/include/storage/lock.h
*** 00orig/src/include/storage/lock.h	2004-01-15 23:32:30.000000000 -0300
--- 09withList/src/include/storage/lock.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 26,31 ****
--- 26,41 ----
  	int			size;			/* number of entries in list */
  } PROC_QUEUE;
  
+ /*
+  * Locks to release on transaction abort/commit 
+  */
+ typedef enum
+ {
+ 	ReleaseAll,						/* All locks */
+ 	ReleaseAllExceptZero,			/* All locks except Xid = 0 */
+ 	ReleaseGivenXids				/* Only locks with Xids in the given array */
+ } LockReleaseWhich;
+ 
  /* struct PGPROC is declared in storage/proc.h, but must forward-reference it */
  typedef struct PGPROC PGPROC;
  
***************
*** 236,242 ****
  extern bool LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
  			TransactionId xid, LOCKMODE lockmode);
  extern bool LockReleaseAll(LOCKMETHODID lockmethodid, PGPROC *proc,
! 			   bool allxids, TransactionId xid);
  extern int LockCheckConflicts(LockMethod lockMethodTable,
  				   LOCKMODE lockmode,
  				   LOCK *lock, PROCLOCK *proclock, PGPROC *proc,
--- 246,252 ----
  extern bool LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
  			TransactionId xid, LOCKMODE lockmode);
  extern bool LockReleaseAll(LOCKMETHODID lockmethodid, PGPROC *proc,
! 			   LockReleaseWhich which, int nxids, TransactionId *xids);
  extern int LockCheckConflicts(LockMethod lockMethodTable,
  				   LOCKMODE lockmode,
  				   LOCK *lock, PROCLOCK *proclock, PGPROC *proc,
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/storage/proc.h 09withList/src/include/storage/proc.h
*** 00orig/src/include/storage/proc.h	2004-01-15 23:32:30.000000000 -0300
--- 09withList/src/include/storage/proc.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 103,109 ****
  extern void InitProcGlobal(int maxBackends);
  extern void InitProcess(void);
  extern void InitDummyProcess(int proctype);
! extern void ProcReleaseLocks(bool isCommit);
  
  extern void ProcQueueInit(PROC_QUEUE *queue);
  extern int ProcSleep(LockMethod lockMethodTable, LOCKMODE lockmode,
--- 103,109 ----
  extern void InitProcGlobal(int maxBackends);
  extern void InitProcess(void);
  extern void InitDummyProcess(int proctype);
! extern void ProcReleaseLocks(LockReleaseWhich which, int nxids, TransactionId *xids);
  
  extern void ProcQueueInit(PROC_QUEUE *queue);
  extern int ProcSleep(LockMethod lockMethodTable, LOCKMODE lockmode,
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/storage/smgr.h 09withList/src/include/storage/smgr.h
*** 00orig/src/include/storage/smgr.h	2004-03-21 13:54:50.000000000 -0400
--- 09withList/src/include/storage/smgr.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 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);
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/utils/catcache.h 09withList/src/include/utils/catcache.h
*** 00orig/src/include/utils/catcache.h	2004-01-15 23:32:30.000000000 -0300
--- 09withList/src/include/utils/catcache.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 101,106 ****
--- 101,109 ----
  	 * and negative entries is identical.
  	 */
  	int			refcount;		/* number of active references */
+ 	int		   *prev_refcount;	/* refcounts for upper subtransactions */
+ 	int			numpushes;		/* number of used refcounts in the array */
+ 	int			numalloc;		/* allocated size of array */
  	bool		dead;			/* dead but not yet removed? */
  	bool		negative;		/* negative cache entry? */
  	uint32		hash_value;		/* hash value for this tuple's keys */
***************
*** 139,144 ****
--- 142,150 ----
  	 */
  	Dlelem		cache_elem;		/* list member of per-catcache list */
  	int			refcount;		/* number of active references */
+ 	int		   *prev_refcount;	/* refcounts for upper subtransactions */
+ 	int			numpushes;		/* number of used refcounts in the array */
+ 	int			numalloc;		/* allocated size of array */
  	bool		dead;			/* dead but not yet removed? */
  	bool		ordered;		/* members listed in index order? */
  	short		nkeys;			/* number of lookup keys specified */
***************
*** 163,168 ****
--- 169,177 ----
  
  extern void CreateCacheMemoryContext(void);
  extern void AtEOXact_CatCache(bool isCommit);
+ extern void AtSubStart_CatCache(void);
+ extern void AtEOSubXact_CatCache(bool commit);
+ 
  
  extern CatCache *InitCatCache(int id, const char *relname, const char *indname,
  			 int reloidattr,
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/utils/hsearch.h 09withList/src/include/utils/hsearch.h
*** 00orig/src/include/utils/hsearch.h	2004-01-15 23:32:30.000000000 -0300
--- 09withList/src/include/utils/hsearch.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 18,25 ****
  /*
   * Hash and comparison functions must have these signatures.  Comparison
   * functions return zero for match, nonzero for no match.  (The comparison
!  * function definition is designed to allow memcmp() and strncmp() to be
!  * used directly as key comparison functions.)
   */
  typedef uint32 (*HashValueFunc) (const void *key, Size keysize);
  typedef int (*HashCompareFunc) (const void *key1, const void *key2,
--- 18,25 ----
  /*
   * Hash and comparison functions must have these signatures.  Comparison
   * functions return zero for match, nonzero for no match.  (The comparison
!  * function definition is designed to allow memcmp() and strncmp() to be used
!  * directly as key comparison functions.)
   */
  typedef uint32 (*HashValueFunc) (const void *key, Size keysize);
  typedef int (*HashCompareFunc) (const void *key1, const void *key2,
***************
*** 171,176 ****
--- 171,177 ----
  extern HTAB *hash_create(const char *tabname, long nelem,
  			HASHCTL *info, int flags);
  extern void hash_destroy(HTAB *hashp);
+ extern HTAB *hash_copy(HTAB *hashp);
  extern void hash_stats(const char *where, HTAB *hashp);
  extern void *hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action,
  			bool *foundPtr);
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/utils/memutils.h 09withList/src/include/utils/memutils.h
*** 00orig/src/include/utils/memutils.h	2004-01-15 23:32:30.000000000 -0300
--- 09withList/src/include/utils/memutils.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 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;
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/utils/portal.h 09withList/src/include/utils/portal.h
*** 00orig/src/include/utils/portal.h	2004-01-15 23:32:30.000000000 -0300
--- 09withList/src/include/utils/portal.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 167,172 ****
--- 167,175 ----
  extern void AtCommit_Portals(void);
  extern void AtAbort_Portals(void);
  extern void AtCleanup_Portals(void);
+ extern void AtSubCommit_Portals(TransactionId);
+ extern void AtSubAbort_Portals(void);
+ extern void AtSubCleanup_Portals(void);
  extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent);
  extern Portal CreateNewPortal(void);
  extern void PortalDrop(Portal portal, bool isError);
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/utils/relcache.h 09withList/src/include/utils/relcache.h
*** 00orig/src/include/utils/relcache.h	2004-03-21 13:54:50.000000000 -0400
--- 09withList/src/include/utils/relcache.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 65,70 ****
--- 65,72 ----
  extern void RelationCacheInvalidate(void);
  
  extern void AtEOXact_RelationCache(bool commit);
+ extern void AtSubStart_RelationCache(void);
+ extern void AtEOSubXact_RelationCache(bool commit);
  
  /*
   * Routines to help manage rebuilding of relcache init file
diff -Ncr --exclude-from=diff-ignore 00orig/src/include/utils/rel.h 09withList/src/include/utils/rel.h
*** 00orig/src/include/utils/rel.h	2004-05-12 00:06:16.000000000 -0400
--- 09withList/src/include/utils/rel.h	2004-05-26 11:41:17.000000000 -0400
***************
*** 110,115 ****
--- 110,118 ----
  	BlockNumber rd_targblock;	/* current insertion target block, or
  								 * InvalidBlockNumber */
  	int			rd_refcnt;		/* reference count */
+ 	int		   *rd_prevrefcnt;	/* reference count stack */
+ 	int			rd_numalloc;	/* stack allocated size */
+ 	int			rd_numpushed;	/* stack used size */
  	bool		rd_isnew;		/* rel was created in current xact */
  
  	/*
diff -Ncr --exclude-from=diff-ignore 00orig/src/test/regress/expected/transactions.out 09withList/src/test/regress/expected/transactions.out
*** 00orig/src/test/regress/expected/transactions.out	2003-01-10 19:03:30.000000000 -0300
--- 09withList/src/test/regress/expected/transactions.out	2004-05-26 15:41:37.000000000 -0400
***************
*** 68,70 ****
--- 68,137 ----
  START TRANSACTION READ WRITE;
  DROP TABLE writetest; -- ok
  COMMIT;
+ -- Subtransactions, basic tests
+ -- create & drop tables
+ SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE;
+ 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;
+ -- should exist: barbaz, baz, foo
+ SELECT * FROM foo;		-- should be empty
+  a 
+ ---
+ (0 rows)
+ 
+ SELECT * FROM bar;		-- shouldn't exist
+ ERROR:  relation "bar" does not exist
+ SELECT * FROM barbaz;	-- should be empty
+  a 
+ ---
+ (0 rows)
+ 
+ SELECT * FROM baz;		-- should be empty
+  a 
+ ---
+ (0 rows)
+ 
+ -- 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
+  a 
+ ---
+  1
+  3
+ (2 rows)
+ 
+ SELECT * FROM barbaz;	-- should have 1
+  a 
+ ---
+  1
+ (1 row)
+ 
+ DROP TABLE foo;
+ DROP TABLE baz;
+ DROP TABLE barbaz;
diff -Ncr --exclude-from=diff-ignore 00orig/src/test/regress/sql/transactions.sql 09withList/src/test/regress/sql/transactions.sql
*** 00orig/src/test/regress/sql/transactions.sql	2003-01-10 19:03:30.000000000 -0300
--- 09withList/src/test/regress/sql/transactions.sql	2004-05-26 15:37:16.000000000 -0400
***************
*** 54,56 ****
--- 54,101 ----
  START TRANSACTION READ WRITE;
  DROP TABLE writetest; -- ok
  COMMIT;
+ 
+ -- Subtransactions, basic tests
+ -- create & drop tables
+ SET SESSION CHARACTERISTICS AS TRANSACTION READ WRITE;
+ 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;
+ -- should exist: barbaz, baz, foo
+ SELECT * FROM foo;		-- should be empty
+ SELECT * FROM bar;		-- shouldn't exist
+ SELECT * FROM barbaz;	-- should be empty
+ SELECT * FROM baz;		-- should be empty
+ 
+ -- 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
+ SELECT * FROM barbaz;	-- should have 1
+ 
+ DROP TABLE foo;
+ DROP TABLE baz;
+ DROP TABLE barbaz;
