diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 60f744001fd34fa2c9a94a5e25ba5783b749f773..bffd56dd54c1c2b5dca8f22186e1ba5de937a52c 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.190 2005/05/03 19:42:40 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/heap/heapam.c,v 1.191 2005/05/19 21:35:44 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -47,7 +47,7 @@
 #include "catalog/catalog.h"
 #include "catalog/namespace.h"
 #include "miscadmin.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 #include "utils/inval.h"
 #include "utils/relcache.h"
 #include "pgstat.h"
diff --git a/src/backend/access/transam/README b/src/backend/access/transam/README
index a11f5707c73cafcc14e9c522fd49936559fe7b27..177ba26cf3c843ff915282afe5144c7e62d196d6 100644
--- a/src/backend/access/transam/README
+++ b/src/backend/access/transam/README
@@ -1,4 +1,4 @@
-$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.2 2004/09/16 16:58:26 tgl Exp $
+$PostgreSQL: pgsql/src/backend/access/transam/README,v 1.3 2005/05/19 21:35:45 tgl Exp $
 
 The Transaction System
 ----------------------
@@ -246,7 +246,7 @@ but since we allow arbitrary nesting of subtransactions, we can't fit all Xids
 in shared memory, so we have to store them on disk.  Note, however, that for
 each transaction we keep a "cache" of Xids that are known to be part of the
 transaction tree, so we can skip looking at pg_subtrans unless we know the
-cache has been overflowed.  See storage/ipc/sinval.c for the gory details.
+cache has been overflowed.  See storage/ipc/procarray.c for the gory details.
 
 slru.c is the supporting mechanism for both pg_clog and pg_subtrans.  It
 implements the LRU policy for in-memory buffer pages.  The high-level routines
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 44c9541209eb13f3b1011b5e5d1650835a3144cf..85acfe2cc0207e4c52d69123f325c67bf9eae099 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -31,7 +31,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.3 2005/05/07 18:14:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/multixact.c,v 1.4 2005/05/19 21:35:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,7 +44,7 @@
 #include "utils/memutils.h"
 #include "storage/backendid.h"
 #include "storage/lmgr.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 
 
 /*
@@ -383,8 +383,8 @@ MultiXactIdIsRunning(MultiXactId multi)
 	}
 
 	/*
-	 * This could be made better by having a special entry point in sinval.c,
-	 * walking the PGPROC array only once for the whole array.  But in most
+	 * This could be made faster by having another entry point in procarray.c,
+	 * walking the PGPROC array only once for all the members.  But in most
 	 * cases nmembers should be small enough that it doesn't much matter.
 	 */
 	for (i = 0; i < nmembers; i++)
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index b92e38fc38a09836d6c627d102f48f90367b8134..0b774363888d949e35853f8eef6b76d34bb9e6e7 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/access/transam/subtrans.c,v 1.7 2004/12/31 21:59:29 pgsql Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/subtrans.c,v 1.8 2005/05/19 21:35:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,6 @@
 
 #include "access/slru.h"
 #include "access/subtrans.h"
-#include "storage/sinval.h"
 #include "utils/tqual.h"
 
 
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index cb532d8df344f7b09b44373baf765ad5c326ef54..5c51e69253e8a572b91a3267da0954f5cae3fb7a 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -6,7 +6,7 @@
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.63 2005/04/13 18:54:56 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.64 2005/05/19 21:35:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -107,7 +107,7 @@ GetNewTransactionId(bool isSubXact)
 	 * nextXid are already present in PGPROC.  Else we have a race
 	 * condition.
 	 *
-	 * XXX by storing xid into MyProc without acquiring SInvalLock, we are
+	 * XXX by storing xid into MyProc without acquiring ProcArrayLock, we are
 	 * relying on fetch/store of an xid to be atomic, else other backends
 	 * might see a partially-set xid here.	But holding both locks at once
 	 * would be a nasty concurrency hit (and in fact could cause a
@@ -120,8 +120,7 @@ GetNewTransactionId(bool isSubXact)
 	 *
 	 * A solution to the atomic-store problem would be to give each PGPROC
 	 * its own spinlock used only for fetching/storing that PGPROC's xid
-	 * and related fields.	(SInvalLock would then mean primarily that
-	 * PGPROCs couldn't be added/removed while holding the lock.)
+	 * and related fields.
 	 *
 	 * If there's no room to fit a subtransaction XID into PGPROC, set the
 	 * cache-overflowed flag instead.  This forces readers to look in
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a318db613434a79c8555b0c4c5f27c3138826b8b..81fba82b4895af1008320983578ae1915dba66bf 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.200 2005/04/28 21:47:10 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.201 2005/05/19 21:35:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,7 @@
 #include "miscadmin.h"
 #include "storage/fd.h"
 #include "storage/proc.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 #include "storage/smgr.h"
 #include "utils/flatfiles.h"
 #include "utils/guc.h"
@@ -1503,16 +1503,18 @@ CommitTransaction(void)
 	 * this must be done _before_ releasing locks we hold and _after_
 	 * RecordTransactionCommit.
 	 *
-	 * LWLockAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked by
-	 * xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot - if
-	 * xid 2' GetSnapshotData sees xid 1 as running then it must see xid 0
-	 * as running as well or it will see two tuple versions - one deleted
-	 * by xid 1 and one inserted by xid 0.	See notes in GetSnapshotData.
+	 * LWLockAcquire(ProcArrayLock) is required; consider this example:
+	 *		UPDATE with xid 0 is blocked by xid 1's UPDATE.
+	 *		xid 1 is doing commit while xid 2 gets snapshot.
+	 * If xid 2's GetSnapshotData sees xid 1 as running then it must see
+	 * xid 0 as running as well, or it will be able to see two tuple versions
+	 * - one deleted by xid 1 and one inserted by xid 0.  See notes in
+	 * GetSnapshotData.
 	 */
 	if (MyProc != NULL)
 	{
-		/* Lock SInvalLock because that's what GetSnapshotData uses. */
-		LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
+		/* Lock ProcArrayLock because that's what GetSnapshotData uses. */
+		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 		MyProc->xid = InvalidTransactionId;
 		MyProc->xmin = InvalidTransactionId;
 
@@ -1520,7 +1522,7 @@ CommitTransaction(void)
 		MyProc->subxids.nxids = 0;
 		MyProc->subxids.overflowed = false;
 
-		LWLockRelease(SInvalLock);
+		LWLockRelease(ProcArrayLock);
 	}
 
 	/*
@@ -1688,8 +1690,8 @@ AbortTransaction(void)
 	 */
 	if (MyProc != NULL)
 	{
-		/* Lock SInvalLock because that's what GetSnapshotData uses. */
-		LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
+		/* Lock ProcArrayLock because that's what GetSnapshotData uses. */
+		LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
 		MyProc->xid = InvalidTransactionId;
 		MyProc->xmin = InvalidTransactionId;
 
@@ -1697,7 +1699,7 @@ AbortTransaction(void)
 		MyProc->subxids.nxids = 0;
 		MyProc->subxids.overflowed = false;
 
-		LWLockRelease(SInvalLock);
+		LWLockRelease(ProcArrayLock);
 	}
 
 	/*
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index fdee576911859717aa82a8f139e379b48a1e0ea1..bff12a14375079e61703cb843be02af0d1b05aaa 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.191 2005/05/10 22:27:29 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.192 2005/05/19 21:35:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,7 +38,7 @@
 #include "storage/lwlock.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 #include "storage/spin.h"
 #include "utils/builtins.h"
 #include "utils/guc.h"
@@ -776,7 +776,7 @@ begin:;
 	if (MyLastRecPtr.xrecoff == 0 && !no_tran)
 	{
 		/*
-		 * We do not acquire SInvalLock here because of possible deadlock.
+		 * We do not acquire ProcArrayLock here because of possible deadlock.
 		 * Anyone who wants to inspect other procs' logRec must acquire
 		 * WALInsertLock, instead.	A better solution would be a per-PROC
 		 * spinlock, but no time for that before 7.2 --- tgl 12/19/01.
@@ -4887,11 +4887,11 @@ CreateCheckPoint(bool shutdown, bool force)
 	 * commits after REDO point).
 	 *
 	 * XXX temporarily ifdef'd out to avoid three-way deadlock condition:
-	 * GetUndoRecPtr needs to grab SInvalLock to ensure that it is looking
-	 * at a stable set of proc records, but grabbing SInvalLock while
+	 * GetUndoRecPtr needs to grab ProcArrayLock to ensure that it is looking
+	 * at a stable set of proc records, but grabbing ProcArrayLock while
 	 * holding WALInsertLock is no good.  GetNewTransactionId may cause a
 	 * WAL record to be written while holding XidGenLock, and
-	 * GetSnapshotData needs to get XidGenLock while holding SInvalLock,
+	 * GetSnapshotData needs to get XidGenLock while holding ProcArrayLock,
 	 * so there's a risk of deadlock. Need to find a better solution.  See
 	 * pgsql-hackers discussion of 17-Dec-01.
 	 *
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 9ad6b343a34efbca3031e6d75b378858a35c650f..ee4f8c6800ac59bacc7b03d07037b01072688d9d 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.255 2005/05/11 06:24:54 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.256 2005/05/19 21:35:45 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -38,7 +38,7 @@
 #include "miscadmin.h"
 #include "optimizer/clauses.h"
 #include "parser/parse_expr.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 #include "storage/smgr.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index ec04619175bdd34e9a92e853610ecfe8d6f2a8a8..22a9afd07680a58968f5cdd8a69976f52c322dbb 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.156 2005/04/14 20:03:23 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/dbcommands.c,v 1.157 2005/05/19 21:35:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,7 +40,7 @@
 #include "postmaster/bgwriter.h"
 #include "storage/fd.h"
 #include "storage/freespace.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 #include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index f9dbd3f4615ce33d5b8e909c7940bc73dfd797dd..7ccbb70cbb73ed766738206fdd08e9eba88e9d8f 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.308 2005/05/06 17:24:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.309 2005/05/19 21:35:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,7 +36,7 @@
 #include "executor/executor.h"
 #include "miscadmin.h"
 #include "storage/freespace.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 #include "storage/smgr.h"
 #include "tcop/pquery.h"
 #include "utils/acl.h"
diff --git a/src/backend/commands/vacuumlazy.c b/src/backend/commands/vacuumlazy.c
index d2b6da9ef8d63f415dbc54e1f091c401a793d4e1..52207000ee3216a7dd076fde9ed928e87d5e7d40 100644
--- a/src/backend/commands/vacuumlazy.c
+++ b/src/backend/commands/vacuumlazy.c
@@ -31,7 +31,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.53 2005/05/07 21:32:24 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/vacuumlazy.c,v 1.54 2005/05/19 21:35:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,7 +45,6 @@
 #include "commands/vacuum.h"
 #include "miscadmin.h"
 #include "storage/freespace.h"
-#include "storage/sinval.h"
 #include "storage/smgr.h"
 #include "utils/lsyscache.h"
 
diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c
index 1b8f042079107d65ec2a4c0058c1136b2f5f4943..b2537cbc0714ab0cd6eadc32cf1f2877e3645e35 100644
--- a/src/backend/storage/buffer/buf_init.c
+++ b/src/backend/storage/buffer/buf_init.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/buffer/buf_init.c,v 1.72 2005/03/04 20:21:06 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/buffer/buf_init.c,v 1.73 2005/05/19 21:35:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -115,7 +115,7 @@ InitBufferPool(void)
 			buf->flags = 0;
 			buf->usage_count = 0;
 			buf->refcount = 0;
-			buf->wait_backend_id = 0;
+			buf->wait_backend_pid = 0;
 
 			SpinLockInit(&buf->buf_hdr_lock);
 
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index e3a60612e3010c2f13bcdf971a49d8671f722fa2..1fe681e1aba9c25635024f902168b91536b36d05 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.188 2005/03/20 22:00:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.189 2005/05/19 21:35:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -825,11 +825,11 @@ UnpinBuffer(BufferDesc *buf, bool fixOwner, bool trashOK)
 			buf->refcount == 1)
 		{
 			/* we just released the last pin other than the waiter's */
-			BackendId	wait_backend_id = buf->wait_backend_id;
+			int		wait_backend_pid = buf->wait_backend_pid;
 
 			buf->flags &= ~BM_PIN_COUNT_WAITER;
 			UnlockBufHdr_NoHoldoff(buf);
-			ProcSendSignal(wait_backend_id);
+			ProcSendSignal(wait_backend_pid);
 		}
 		else
 			UnlockBufHdr_NoHoldoff(buf);
@@ -1678,7 +1678,7 @@ UnlockBuffers(void)
 		 * signal.
 		 */
 		if ((buf->flags & BM_PIN_COUNT_WAITER) != 0 &&
-			buf->wait_backend_id == MyBackendId)
+			buf->wait_backend_pid == MyProcPid)
 			buf->flags &= ~BM_PIN_COUNT_WAITER;
 
 		UnlockBufHdr_NoHoldoff(buf);
@@ -1820,7 +1820,7 @@ LockBufferForCleanup(Buffer buffer)
 			LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
 			elog(ERROR, "multiple backends attempting to wait for pincount 1");
 		}
-		bufHdr->wait_backend_id = MyBackendId;
+		bufHdr->wait_backend_pid = MyProcPid;
 		bufHdr->flags |= BM_PIN_COUNT_WAITER;
 		PinCountWaitBuf = bufHdr;
 		UnlockBufHdr_NoHoldoff(bufHdr);
diff --git a/src/backend/storage/ipc/Makefile b/src/backend/storage/ipc/Makefile
index 1e85fb62f25eaa0f892236d50890c27c64955eab..d7ff6f8d304dceefde643a3cbde16ca855ca367f 100644
--- a/src/backend/storage/ipc/Makefile
+++ b/src/backend/storage/ipc/Makefile
@@ -1,7 +1,7 @@
 #
 # Makefile for storage/ipc
 #
-# $PostgreSQL: pgsql/src/backend/storage/ipc/Makefile,v 1.18 2003/11/29 19:51:56 pgsql Exp $
+# $PostgreSQL: pgsql/src/backend/storage/ipc/Makefile,v 1.19 2005/05/19 21:35:46 tgl Exp $
 #
 
 subdir = src/backend/storage/ipc
@@ -15,7 +15,8 @@ override CFLAGS+= -fno-inline
 endif
 endif
 
-OBJS = ipc.o ipci.o pmsignal.o shmem.o shmqueue.o sinval.o sinvaladt.o
+OBJS = ipc.o ipci.o pmsignal.o procarray.o shmem.o shmqueue.o \
+	sinval.o sinvaladt.o
 
 all: SUBSYS.o
 
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 975d5f131d7d184e88dacabbb2b9908cd8dcd593..22333a1f558a07c68ea0e8c4a1099e6d39d18987 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.75 2005/04/28 21:47:15 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.76 2005/05/19 21:35:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@
 #include "storage/pg_shmem.h"
 #include "storage/pmsignal.h"
 #include "storage/proc.h"
+#include "storage/procarray.h"
 #include "storage/sinval.h"
 #include "storage/spin.h"
 
@@ -78,6 +79,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
 		size += SUBTRANSShmemSize();
 		size += MultiXactShmemSize();
 		size += LWLockShmemSize();
+		size += ProcArrayShmemSize(maxBackends);
 		size += SInvalShmemSize(maxBackends);
 		size += FreeSpaceShmemSize();
 		size += BgWriterShmemSize();
@@ -155,6 +157,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
 	 * Set up process table
 	 */
 	InitProcGlobal(maxBackends);
+	CreateSharedProcArray(maxBackends);
 
 	/*
 	 * Set up shared-inval messaging
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
new file mode 100644
index 0000000000000000000000000000000000000000..85ae3c762baf517719ce79d3379106b686ebc16b
--- /dev/null
+++ b/src/backend/storage/ipc/procarray.c
@@ -0,0 +1,787 @@
+/*-------------------------------------------------------------------------
+ *
+ * procarray.c
+ *	  POSTGRES process array code.
+ *
+ *
+ * This module maintains an unsorted array of the PGPROC structures for all
+ * active backends.  Although there are several uses for this, the principal
+ * one is as a means of determining the set of currently running transactions.
+ *
+ * Because of various subtle race conditions it is critical that a backend
+ * hold the correct locks while setting or clearing its MyProc->xid field.
+ * See notes in GetSnapshotData.
+ * 
+ *
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.1 2005/05/19 21:35:46 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/subtrans.h"
+#include "miscadmin.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "utils/tqual.h"
+
+
+/* Our shared memory area */
+typedef struct ProcArrayStruct
+{
+	int			numProcs;		/* number of valid procs entries */
+	int			maxProcs;		/* allocated size of procs array */
+
+	/*
+	 * We declare procs[] as 1 entry because C wants a fixed-size array,
+	 * but actually it is maxProcs entries long.
+	 */
+	PGPROC	   *procs[1];		/* VARIABLE LENGTH ARRAY */
+} ProcArrayStruct;
+
+static ProcArrayStruct *procArray;
+
+
+#ifdef XIDCACHE_DEBUG
+
+/* counters for XidCache measurement */
+static long xc_by_recent_xmin = 0;
+static long xc_by_main_xid = 0;
+static long xc_by_child_xid = 0;
+static long xc_slow_answer = 0;
+
+#define xc_by_recent_xmin_inc()		(xc_by_recent_xmin++)
+#define xc_by_main_xid_inc()		(xc_by_main_xid++)
+#define xc_by_child_xid_inc()		(xc_by_child_xid++)
+#define xc_slow_answer_inc()		(xc_slow_answer++)
+
+static void DisplayXidCache(void);
+
+#else							/* !XIDCACHE_DEBUG */
+
+#define xc_by_recent_xmin_inc()		((void) 0)
+#define xc_by_main_xid_inc()		((void) 0)
+#define xc_by_child_xid_inc()		((void) 0)
+#define xc_slow_answer_inc()		((void) 0)
+
+#endif   /* XIDCACHE_DEBUG */
+
+
+/*
+ * Report shared-memory space needed by CreateSharedProcArray.
+ */
+int
+ProcArrayShmemSize(int maxBackends)
+{
+	/* sizeof(ProcArrayStruct) includes the first array element */
+	return MAXALIGN(sizeof(ProcArrayStruct) +
+					(maxBackends - 1) * sizeof(PGPROC *));
+}
+
+/*
+ * Initialize the shared PGPROC array during postmaster startup.
+ */
+void
+CreateSharedProcArray(int maxBackends)
+{
+	bool		found;
+
+	/* Create or attach to the ProcArray shared structure */
+	procArray = (ProcArrayStruct *)
+		ShmemInitStruct("Proc Array", ProcArrayShmemSize(maxBackends),
+						&found);
+
+	if (!found)
+	{
+		/*
+		 * We're the first - initialize.
+		 */
+		procArray->numProcs = 0;
+		procArray->maxProcs = maxBackends;
+	}
+}
+
+/*
+ * Add my own PGPROC (found in the global MyProc) to the shared array.
+ *
+ * This must be called during backend startup, after fully initializing
+ * the contents of MyProc.
+ */
+void
+ProcArrayAddMyself(void)
+{
+	ProcArrayStruct *arrayP = procArray;
+
+	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+
+	if (arrayP->numProcs >= arrayP->maxProcs)
+	{
+		/*
+		 * Ooops, no room.  (This really shouldn't happen, since there is
+		 * a fixed supply of PGPROC structs too, and so we should have
+		 * failed earlier.)
+		 */
+		LWLockRelease(ProcArrayLock);
+		ereport(FATAL,
+				(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
+				 errmsg("sorry, too many clients already")));
+	}
+
+	arrayP->procs[arrayP->numProcs] = MyProc;
+	arrayP->numProcs++;
+
+	LWLockRelease(ProcArrayLock);
+}
+
+/*
+ * Remove my own PGPROC (found in the global MyProc) from the shared array.
+ *
+ * This must be called during backend shutdown.
+ */
+void
+ProcArrayRemoveMyself(void)
+{
+	ProcArrayStruct *arrayP = procArray;
+	int			index;
+
+#ifdef XIDCACHE_DEBUG
+	DisplayXidCache();
+#endif
+
+	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		if (arrayP->procs[index] == MyProc)
+		{
+			arrayP->procs[index] = arrayP->procs[arrayP->numProcs - 1];
+			arrayP->numProcs--;
+			LWLockRelease(ProcArrayLock);
+			return;
+		}
+	}
+
+	/* Ooops */
+	LWLockRelease(ProcArrayLock);
+
+	elog(LOG, "failed to find my own proc %p in ProcArray", MyProc);
+}
+
+
+/*
+ * TransactionIdIsInProgress -- is given transaction running in some backend
+ *
+ * There are three possibilities for finding a running transaction:
+ *
+ * 1. the given Xid is a main transaction Id.  We will find this out cheaply
+ * by looking at the PGPROC struct for each backend.
+ *
+ * 2. the given Xid is one of the cached subxact Xids in the PGPROC array.
+ * We can find this out cheaply too.
+ *
+ * 3. Search the SubTrans tree to find the Xid's topmost parent, and then
+ * see if that is running according to PGPROC.	This is the slowest, but
+ * sadly it has to be done always if the other two failed, unless we see
+ * that the cached subxact sets are complete (none have overflowed).
+ *
+ * ProcArrayLock has to be held while we do 1 and 2.  If we save the top Xids
+ * while doing 1, we can release the ProcArrayLock while we do 3.  This buys
+ * back some concurrency (we can't retrieve the main Xids from PGPROC again
+ * anyway; see GetNewTransactionId).
+ */
+bool
+TransactionIdIsInProgress(TransactionId xid)
+{
+	bool		result = false;
+	ProcArrayStruct *arrayP = procArray;
+	int			i,
+				j;
+	int			nxids = 0;
+	TransactionId *xids;
+	TransactionId topxid;
+	bool		locked;
+
+	/*
+	 * Don't bother checking a transaction older than RecentXmin; it
+	 * could not possibly still be running.
+	 */
+	if (TransactionIdPrecedes(xid, RecentXmin))
+	{
+		xc_by_recent_xmin_inc();
+		return false;
+	}
+
+	/* Get workspace to remember main XIDs in */
+	xids = (TransactionId *) palloc(sizeof(TransactionId) * arrayP->maxProcs);
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+	locked = true;
+
+	for (i = 0; i < arrayP->numProcs; i++)
+	{
+		PGPROC	   *proc = arrayP->procs[i];
+
+		/* Fetch xid just once - see GetNewTransactionId */
+		TransactionId pxid = proc->xid;
+
+		if (!TransactionIdIsValid(pxid))
+			continue;
+
+		/*
+		 * Step 1: check the main Xid
+		 */
+		if (TransactionIdEquals(pxid, xid))
+		{
+			xc_by_main_xid_inc();
+			result = true;
+			goto result_known;
+		}
+
+		/*
+		 * We can ignore main Xids that are younger than the target
+		 * Xid, since the target could not possibly be their child.
+		 */
+		if (TransactionIdPrecedes(xid, pxid))
+			continue;
+
+		/*
+		 * Step 2: check the cached child-Xids arrays
+		 */
+		for (j = proc->subxids.nxids - 1; j >= 0; j--)
+		{
+			/* Fetch xid just once - see GetNewTransactionId */
+			TransactionId cxid = proc->subxids.xids[j];
+
+			if (TransactionIdEquals(cxid, xid))
+			{
+				xc_by_child_xid_inc();
+				result = true;
+				goto result_known;
+			}
+		}
+
+		/*
+		 * Save the main Xid for step 3.  We only need to remember
+		 * main Xids that have uncached children.  (Note: there is no
+		 * race condition here because the overflowed flag cannot be
+		 * cleared, only set, while we hold ProcArrayLock.  So we can't
+		 * miss an Xid that we need to worry about.)
+		 */
+		if (proc->subxids.overflowed)
+			xids[nxids++] = pxid;
+	}
+
+	LWLockRelease(ProcArrayLock);
+	locked = false;
+
+	/*
+	 * If none of the relevant caches overflowed, we know the Xid is not
+	 * running without looking at pg_subtrans.
+	 */
+	if (nxids == 0)
+		goto result_known;
+
+	/*
+	 * Step 3: have to check pg_subtrans.
+	 *
+	 * At this point, we know it's either a subtransaction of one of the Xids
+	 * in xids[], or it's not running.  If it's an already-failed
+	 * subtransaction, we want to say "not running" even though its parent
+	 * may still be running.  So first, check pg_clog to see if it's been
+	 * aborted.
+	 */
+	xc_slow_answer_inc();
+
+	if (TransactionIdDidAbort(xid))
+		goto result_known;
+
+	/*
+	 * It isn't aborted, so check whether the transaction tree it belongs
+	 * to is still running (or, more precisely, whether it was running
+	 * when this routine started -- note that we already released
+	 * ProcArrayLock).
+	 */
+	topxid = SubTransGetTopmostTransaction(xid);
+	Assert(TransactionIdIsValid(topxid));
+	if (!TransactionIdEquals(topxid, xid))
+	{
+		for (i = 0; i < nxids; i++)
+		{
+			if (TransactionIdEquals(xids[i], topxid))
+			{
+				result = true;
+				break;
+			}
+		}
+	}
+
+result_known:
+	if (locked)
+		LWLockRelease(ProcArrayLock);
+
+	pfree(xids);
+
+	return result;
+}
+
+/*
+ * GetOldestXmin -- returns oldest transaction that was running
+ *					when any current transaction was started.
+ *
+ * If allDbs is TRUE then all backends are considered; if allDbs is FALSE
+ * then only backends running in my own database are considered.
+ *
+ * This is used by VACUUM to decide which deleted tuples must be preserved
+ * in a table.	allDbs = TRUE is needed for shared relations, but allDbs =
+ * FALSE is sufficient for non-shared relations, since only backends in my
+ * own database could ever see the tuples in them.
+ *
+ * This is also used to determine where to truncate pg_subtrans.  allDbs
+ * must be TRUE for that case.
+ *
+ * Note: we include the currently running xids in the set of considered xids.
+ * This ensures that if a just-started xact has not yet set its snapshot,
+ * when it does set the snapshot it cannot set xmin less than what we compute.
+ */
+TransactionId
+GetOldestXmin(bool allDbs)
+{
+	ProcArrayStruct *arrayP = procArray;
+	TransactionId result;
+	int			index;
+
+	/*
+	 * Normally we start the min() calculation with our own XID.  But if
+	 * called by checkpointer, we will not be inside a transaction, so use
+	 * next XID as starting point for min() calculation.  (Note that if
+	 * there are no xacts running at all, that will be the subtrans
+	 * truncation point!)
+	 */
+	if (IsTransactionState())
+		result = GetTopTransactionId();
+	else
+		result = ReadNewTransactionId();
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		PGPROC	   *proc = arrayP->procs[index];
+
+		if (allDbs || proc->databaseId == MyDatabaseId)
+		{
+			/* Fetch xid just once - see GetNewTransactionId */
+			TransactionId xid = proc->xid;
+
+			if (TransactionIdIsNormal(xid))
+			{
+				if (TransactionIdPrecedes(xid, result))
+					result = xid;
+				xid = proc->xmin;
+				if (TransactionIdIsNormal(xid))
+					if (TransactionIdPrecedes(xid, result))
+						result = xid;
+			}
+		}
+	}
+
+	LWLockRelease(ProcArrayLock);
+
+	return result;
+}
+
+/*----------
+ * GetSnapshotData -- returns information about running transactions.
+ *
+ * The returned snapshot includes xmin (lowest still-running xact ID),
+ * xmax (next xact ID to be assigned), and a list of running xact IDs
+ * in the range xmin <= xid < xmax.  It is used as follows:
+ *		All xact IDs < xmin are considered finished.
+ *		All xact IDs >= xmax are considered still running.
+ *		For an xact ID xmin <= xid < xmax, consult list to see whether
+ *		it is considered running or not.
+ * This ensures that the set of transactions seen as "running" by the
+ * current xact will not change after it takes the snapshot.
+ *
+ * Note that only top-level XIDs are included in the snapshot.  We can
+ * still apply the xmin and xmax limits to subtransaction XIDs, but we
+ * need to work a bit harder to see if XIDs in [xmin..xmax) are running.
+ *
+ * We also update the following backend-global variables:
+ *		TransactionXmin: the oldest xmin of any snapshot in use in the
+ *			current transaction (this is the same as MyProc->xmin).  This
+ *			is just the xmin computed for the first, serializable snapshot.
+ *		RecentXmin: the xmin computed for the most recent snapshot.  XIDs
+ *			older than this are known not running any more.
+ *		RecentGlobalXmin: the global xmin (oldest TransactionXmin across all
+ *			running transactions).  This is the same computation done by
+ *			GetOldestXmin(TRUE).
+ *----------
+ */
+Snapshot
+GetSnapshotData(Snapshot snapshot, bool serializable)
+{
+	ProcArrayStruct *arrayP = procArray;
+	TransactionId xmin;
+	TransactionId xmax;
+	TransactionId globalxmin;
+	int			index;
+	int			count = 0;
+
+	Assert(snapshot != NULL);
+
+	/* Serializable snapshot must be computed before any other... */
+	Assert(serializable ?
+		   !TransactionIdIsValid(MyProc->xmin) :
+		   TransactionIdIsValid(MyProc->xmin));
+
+	/*
+	 * Allocating space for MaxBackends xids is usually overkill;
+	 * lastBackend would be sufficient.  But it seems better to do the
+	 * malloc while not holding the lock, so we can't look at lastBackend.
+	 *
+	 * This does open a possibility for avoiding repeated malloc/free: since
+	 * MaxBackends does not change at runtime, we can simply reuse the
+	 * previous xip array if any.  (This relies on the fact that all
+	 * callers pass static SnapshotData structs.)
+	 */
+	if (snapshot->xip == NULL)
+	{
+		/*
+		 * First call for this snapshot
+		 */
+		snapshot->xip = (TransactionId *)
+			malloc(MaxBackends * sizeof(TransactionId));
+		if (snapshot->xip == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_OUT_OF_MEMORY),
+					 errmsg("out of memory")));
+	}
+
+	globalxmin = xmin = GetTopTransactionId();
+
+	/*
+	 * If we are going to set MyProc->xmin then we'd better get exclusive
+	 * lock; if not, this is a read-only operation so it can be shared.
+	 */
+	LWLockAcquire(ProcArrayLock, serializable ? LW_EXCLUSIVE : LW_SHARED);
+
+	/*--------------------
+	 * Unfortunately, we have to call ReadNewTransactionId() after acquiring
+	 * ProcArrayLock above.  It's not good because ReadNewTransactionId() does
+	 * LWLockAcquire(XidGenLock), but *necessary*.	We need to be sure that
+	 * no transactions exit the set of currently-running transactions
+	 * between the time we fetch xmax and the time we finish building our
+	 * snapshot.  Otherwise we could have a situation like this:
+	 *
+	 *		1. Tx Old is running (in Read Committed mode).
+	 *		2. Tx S reads new transaction ID into xmax, then
+	 *		   is swapped out before acquiring ProcArrayLock.
+	 *		3. Tx New gets new transaction ID (>= S' xmax),
+	 *		   makes changes and commits.
+	 *		4. Tx Old changes some row R changed by Tx New and commits.
+	 *		5. Tx S finishes getting its snapshot data.  It sees Tx Old as
+	 *		   done, but sees Tx New as still running (since New >= xmax).
+	 *
+	 * Now S will see R changed by both Tx Old and Tx New, *but* does not
+	 * see other changes made by Tx New.  If S is supposed to be in
+	 * Serializable mode, this is wrong.
+	 *
+	 * By locking ProcArrayLock before we read xmax, we ensure that TX Old
+	 * cannot exit the set of running transactions seen by Tx S.  Therefore
+	 * both Old and New will be seen as still running => no inconsistency.
+	 *--------------------
+	 */
+
+	xmax = ReadNewTransactionId();
+
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		PGPROC	   *proc = arrayP->procs[index];
+
+		/* Fetch xid just once - see GetNewTransactionId */
+		TransactionId xid = proc->xid;
+
+		/*
+		 * Ignore my own proc (dealt with my xid above), procs not
+		 * running a transaction, and xacts started since we read the
+		 * next transaction ID.  There's no need to store XIDs above
+		 * what we got from ReadNewTransactionId, since we'll treat
+		 * them as running anyway.	We also assume that such xacts
+		 * can't compute an xmin older than ours, so they needn't be
+		 * considered in computing globalxmin.
+		 */
+		if (proc == MyProc ||
+			!TransactionIdIsNormal(xid) ||
+			TransactionIdFollowsOrEquals(xid, xmax))
+			continue;
+
+		if (TransactionIdPrecedes(xid, xmin))
+			xmin = xid;
+		snapshot->xip[count] = xid;
+		count++;
+
+		/* Update globalxmin to be the smallest valid xmin */
+		xid = proc->xmin;
+		if (TransactionIdIsNormal(xid))
+			if (TransactionIdPrecedes(xid, globalxmin))
+				globalxmin = xid;
+	}
+
+	if (serializable)
+		MyProc->xmin = TransactionXmin = xmin;
+
+	LWLockRelease(ProcArrayLock);
+
+	/*
+	 * Update globalxmin to include actual process xids.  This is a
+	 * slightly different way of computing it than GetOldestXmin uses, but
+	 * should give the same result.
+	 */
+	if (TransactionIdPrecedes(xmin, globalxmin))
+		globalxmin = xmin;
+
+	/* Update global variables too */
+	RecentGlobalXmin = globalxmin;
+	RecentXmin = xmin;
+
+	snapshot->xmin = xmin;
+	snapshot->xmax = xmax;
+	snapshot->xcnt = count;
+
+	snapshot->curcid = GetCurrentCommandId();
+
+	return snapshot;
+}
+
+/*
+ * DatabaseHasActiveBackends -- are there any backends running in the given DB
+ *
+ * If 'ignoreMyself' is TRUE, ignore this particular backend while checking
+ * for backends in the target database.
+ *
+ * This function is used to interlock DROP DATABASE against there being
+ * any active backends in the target DB --- dropping the DB while active
+ * backends remain would be a Bad Thing.  Note that we cannot detect here
+ * the possibility of a newly-started backend that is trying to connect
+ * to the doomed database, so additional interlocking is needed during
+ * backend startup.
+ */
+bool
+DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself)
+{
+	bool		result = false;
+	ProcArrayStruct *arrayP = procArray;
+	int			index;
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		PGPROC	   *proc = arrayP->procs[index];
+
+		if (proc->databaseId == databaseId)
+		{
+			if (ignoreMyself && proc == MyProc)
+				continue;
+
+			result = true;
+			break;
+		}
+	}
+
+	LWLockRelease(ProcArrayLock);
+
+	return result;
+}
+
+/*
+ * BackendPidGetProc -- get a backend's PGPROC given its PID
+ */
+struct PGPROC *
+BackendPidGetProc(int pid)
+{
+	PGPROC	   *result = NULL;
+	ProcArrayStruct *arrayP = procArray;
+	int			index;
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		PGPROC	   *proc = arrayP->procs[index];
+
+		if (proc->pid == pid)
+		{
+			result = proc;
+			break;
+		}
+	}
+
+	LWLockRelease(ProcArrayLock);
+
+	return result;
+}
+
+/*
+ * IsBackendPid -- is a given pid a running backend
+ */
+bool
+IsBackendPid(int pid)
+{
+	return (BackendPidGetProc(pid) != NULL);
+}
+
+/*
+ * CountActiveBackends --- count backends (other than myself) that are in
+ *		active transactions.  This is used as a heuristic to decide if
+ *		a pre-XLOG-flush delay is worthwhile during commit.
+ *
+ * An active transaction is something that has written at least one XLOG
+ * record; read-only transactions don't count.  Also, do not count backends
+ * that are blocked waiting for locks, since they are not going to get to
+ * run until someone else commits.
+ */
+int
+CountActiveBackends(void)
+{
+	ProcArrayStruct *arrayP = procArray;
+	int			count = 0;
+	int			index;
+
+	/*
+	 * Note: for speed, we don't acquire ProcArrayLock.  This is a little bit
+	 * bogus, but since we are only testing xrecoff for zero or nonzero,
+	 * it should be OK.  The result is only used for heuristic purposes
+	 * anyway...
+	 */
+	for (index = 0; index < arrayP->numProcs; index++)
+	{
+		PGPROC	   *proc = arrayP->procs[index];
+
+		if (proc == MyProc)
+			continue;			/* do not count myself */
+		if (proc->logRec.xrecoff == 0)
+			continue;			/* do not count if not in a transaction */
+		if (proc->waitLock != NULL)
+			continue;			/* do not count if blocked on a lock */
+		count++;
+	}
+
+	return count;
+}
+
+/*
+ * CountEmptyBackendSlots - count empty slots in backend process table
+ *
+ * Acquiring the lock here is almost certainly overkill, but just in
+ * case fetching an int is not atomic on your machine ...
+ */
+int
+CountEmptyBackendSlots(void)
+{
+	int			count;
+
+	LWLockAcquire(ProcArrayLock, LW_SHARED);
+
+	count = procArray->maxProcs - procArray->numProcs;
+
+	LWLockRelease(ProcArrayLock);
+
+	return count;
+}
+
+#define XidCacheRemove(i) \
+	do { \
+		MyProc->subxids.xids[i] = MyProc->subxids.xids[MyProc->subxids.nxids - 1]; \
+		MyProc->subxids.nxids--; \
+	} while (0)
+
+/*
+ * XidCacheRemoveRunningXids
+ *
+ * Remove a bunch of TransactionIds from the list of known-running
+ * subtransactions for my backend.	Both the specified xid and those in
+ * the xids[] array (of length nxids) are removed from the subxids cache.
+ */
+void
+XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids)
+{
+	int			i,
+				j;
+
+	Assert(!TransactionIdEquals(xid, InvalidTransactionId));
+
+	/*
+	 * We must hold ProcArrayLock exclusively in order to remove transactions
+	 * from the PGPROC array.  (See notes in GetSnapshotData.)	It's
+	 * possible this could be relaxed since we know this routine is only
+	 * used to abort subtransactions, but pending closer analysis we'd
+	 * best be conservative.
+	 */
+	LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
+
+	/*
+	 * Under normal circumstances xid and xids[] will be in increasing
+	 * order, as will be the entries in subxids.  Scan backwards to avoid
+	 * O(N^2) behavior when removing a lot of xids.
+	 */
+	for (i = nxids - 1; i >= 0; i--)
+	{
+		TransactionId anxid = xids[i];
+
+		for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
+		{
+			if (TransactionIdEquals(MyProc->subxids.xids[j], anxid))
+			{
+				XidCacheRemove(j);
+				break;
+			}
+		}
+		/*
+		 * Ordinarily we should have found it, unless the cache has overflowed.
+		 * However it's also possible for this routine to be invoked multiple
+		 * times for the same subtransaction, in case of an error during
+		 * AbortSubTransaction.  So instead of Assert, emit a debug warning.
+		 */
+		if (j < 0 && !MyProc->subxids.overflowed)
+			elog(WARNING, "did not find subXID %u in MyProc", anxid);
+	}
+
+	for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
+	{
+		if (TransactionIdEquals(MyProc->subxids.xids[j], xid))
+		{
+			XidCacheRemove(j);
+			break;
+		}
+	}
+	/* Ordinarily we should have found it, unless the cache has overflowed */
+	if (j < 0 && !MyProc->subxids.overflowed)
+		elog(WARNING, "did not find subXID %u in MyProc", xid);
+
+	LWLockRelease(ProcArrayLock);
+}
+
+#ifdef XIDCACHE_DEBUG
+
+/*
+ * Print stats about effectiveness of XID cache
+ */
+static void
+DisplayXidCache(void)
+{
+	fprintf(stderr,
+			"XidCache: xmin: %ld, mainxid: %ld, childxid: %ld, slow: %ld\n",
+			xc_by_recent_xmin,
+			xc_by_main_xid,
+			xc_by_child_xid,
+			xc_slow_answer);
+}
+
+#endif   /* XIDCACHE_DEBUG */
diff --git a/src/backend/storage/ipc/sinval.c b/src/backend/storage/ipc/sinval.c
index 27716516b725a3e8ecd1ac3e6e64e77ce57f95bf..e771eea196e6f5d8d328842384de42dc4843d2c5 100644
--- a/src/backend/storage/ipc/sinval.c
+++ b/src/backend/storage/ipc/sinval.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.75 2004/12/31 22:00:56 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.76 2005/05/19 21:35:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,40 +16,16 @@
 
 #include <signal.h>
 
-#include "access/subtrans.h"
-#include "access/transam.h"
+#include "access/xact.h"
 #include "commands/async.h"
+#include "miscadmin.h"
+#include "storage/backendid.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
 #include "storage/sinval.h"
 #include "storage/sinvaladt.h"
 #include "utils/inval.h"
-#include "utils/tqual.h"
-#include "miscadmin.h"
-
-
-#ifdef XIDCACHE_DEBUG
-
-/* counters for XidCache measurement */
-static long xc_by_recent_xmin = 0;
-static long xc_by_main_xid = 0;
-static long xc_by_child_xid = 0;
-static long xc_slow_answer = 0;
 
-#define xc_by_recent_xmin_inc()		(xc_by_recent_xmin++)
-#define xc_by_main_xid_inc()		(xc_by_main_xid++)
-#define xc_by_child_xid_inc()		(xc_by_child_xid++)
-#define xc_slow_answer_inc()		(xc_slow_answer++)
-
-static void DisplayXidCache(int code, Datum arg);
-
-#else							/* !XIDCACHE_DEBUG */
-
-#define xc_by_recent_xmin_inc()		((void) 0)
-#define xc_by_main_xid_inc()		((void) 0)
-#define xc_by_child_xid_inc()		((void) 0)
-#define xc_slow_answer_inc()		((void) 0)
-#endif   /* XIDCACHE_DEBUG */
 
 /*
  * Because backends sitting idle will not be reading sinval events, we
@@ -103,10 +79,6 @@ InitBackendSharedInvalidationState(void)
 		ereport(FATAL,
 				(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
 				 errmsg("sorry, too many clients already")));
-
-#ifdef XIDCACHE_DEBUG
-	on_proc_exit(DisplayXidCache, (Datum) 0);
-#endif   /* XIDCACHE_DEBUG */
 }
 
 /*
@@ -161,12 +133,6 @@ ReceiveSharedInvalidMessages(
 		 * this is not exactly the normal (read-only) interpretation of a
 		 * shared lock! Look closely at the interactions before allowing
 		 * SInvalLock to be grabbed in shared mode for any other reason!
-		 *
-		 * The routines later in this file that use shared mode are okay with
-		 * this, because they aren't looking at the ProcState fields
-		 * associated with SI message transfer; they only use the
-		 * ProcState array as an easy way to find all the PGPROC
-		 * structures.
 		 */
 		LWLockAcquire(SInvalLock, LW_SHARED);
 		getResult = SIGetDataEntry(shmInvalBuffer, MyBackendId, &data);
@@ -391,725 +357,3 @@ ProcessCatchupEvent(void)
 	if (notify_enabled)
 		EnableNotifyInterrupt();
 }
-
-
-/****************************************************************************/
-/* Functions that need to scan the PGPROC structures of all running backends. */
-/* It's a bit strange to keep these in sinval.c, since they don't have any	*/
-/* direct relationship to shared-cache invalidation.  But the procState		*/
-/* array in the SI segment is the only place in the system where we have	*/
-/* an array of per-backend data, so it is the most convenient place to keep */
-/* pointers to the backends' PGPROC structures.  We used to implement these	*/
-/* functions with a slow, ugly search through the ShmemIndex hash table --- */
-/* now they are simple loops over the SI ProcState array.					*/
-/****************************************************************************/
-
-
-/*
- * DatabaseHasActiveBackends -- are there any backends running in the given DB
- *
- * If 'ignoreMyself' is TRUE, ignore this particular backend while checking
- * for backends in the target database.
- *
- * This function is used to interlock DROP DATABASE against there being
- * any active backends in the target DB --- dropping the DB while active
- * backends remain would be a Bad Thing.  Note that we cannot detect here
- * the possibility of a newly-started backend that is trying to connect
- * to the doomed database, so additional interlocking is needed during
- * backend startup.
- */
-bool
-DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself)
-{
-	bool		result = false;
-	SISeg	   *segP = shmInvalBuffer;
-	ProcState  *stateP = segP->procState;
-	int			index;
-
-	LWLockAcquire(SInvalLock, LW_SHARED);
-
-	for (index = 0; index < segP->lastBackend; index++)
-	{
-		SHMEM_OFFSET pOffset = stateP[index].procStruct;
-
-		if (pOffset != INVALID_OFFSET)
-		{
-			PGPROC	   *proc = (PGPROC *) MAKE_PTR(pOffset);
-
-			if (proc->databaseId == databaseId)
-			{
-				if (ignoreMyself && proc == MyProc)
-					continue;
-
-				result = true;
-				break;
-			}
-		}
-	}
-
-	LWLockRelease(SInvalLock);
-
-	return result;
-}
-
-/*
- * IsBackendPid -- is a given pid a running backend
- */
-bool
-IsBackendPid(int pid)
-{
-	bool		result = false;
-	SISeg	   *segP = shmInvalBuffer;
-	ProcState  *stateP = segP->procState;
-	int			index;
-
-	LWLockAcquire(SInvalLock, LW_SHARED);
-
-	for (index = 0; index < segP->lastBackend; index++)
-	{
-		SHMEM_OFFSET pOffset = stateP[index].procStruct;
-
-		if (pOffset != INVALID_OFFSET)
-		{
-			PGPROC	   *proc = (PGPROC *) MAKE_PTR(pOffset);
-
-			if (proc->pid == pid)
-			{
-				result = true;
-				break;
-			}
-		}
-	}
-
-	LWLockRelease(SInvalLock);
-
-	return result;
-}
-
-/*
- * TransactionIdIsInProgress -- is given transaction running in some backend
- *
- * There are three possibilities for finding a running transaction:
- *
- * 1. the given Xid is a main transaction Id.  We will find this out cheaply
- * by looking at the PGPROC struct for each backend.
- *
- * 2. the given Xid is one of the cached subxact Xids in the PGPROC array.
- * We can find this out cheaply too.
- *
- * 3. Search the SubTrans tree to find the Xid's topmost parent, and then
- * see if that is running according to PGPROC.	This is the slowest, but
- * sadly it has to be done always if the other two failed, unless we see
- * that the cached subxact sets are complete (none have overflowed).
- *
- * SInvalLock has to be held while we do 1 and 2.  If we save the top Xids
- * while doing 1, we can release the SInvalLock while we do 3.	This buys back
- * some concurrency (we can't retrieve the main Xids from PGPROC again anyway;
- * see GetNewTransactionId).
- */
-bool
-TransactionIdIsInProgress(TransactionId xid)
-{
-	bool		result = false;
-	SISeg	   *segP = shmInvalBuffer;
-	ProcState  *stateP = segP->procState;
-	int			i,
-				j;
-	int			nxids = 0;
-	TransactionId *xids;
-	TransactionId topxid;
-	bool		locked;
-
-	/*
-	 * Don't bother checking a transaction older than RecentXmin; it
-	 * could not possibly still be running.
-	 */
-	if (TransactionIdPrecedes(xid, RecentXmin))
-	{
-		xc_by_recent_xmin_inc();
-		return false;
-	}
-
-	/* Get workspace to remember main XIDs in */
-	xids = (TransactionId *) palloc(sizeof(TransactionId) * segP->maxBackends);
-
-	LWLockAcquire(SInvalLock, LW_SHARED);
-	locked = true;
-
-	for (i = 0; i < segP->lastBackend; i++)
-	{
-		SHMEM_OFFSET pOffset = stateP[i].procStruct;
-
-		if (pOffset != INVALID_OFFSET)
-		{
-			PGPROC	   *proc = (PGPROC *) MAKE_PTR(pOffset);
-
-			/* Fetch xid just once - see GetNewTransactionId */
-			TransactionId pxid = proc->xid;
-
-			if (!TransactionIdIsValid(pxid))
-				continue;
-
-			/*
-			 * Step 1: check the main Xid
-			 */
-			if (TransactionIdEquals(pxid, xid))
-			{
-				xc_by_main_xid_inc();
-				result = true;
-				goto result_known;
-			}
-
-			/*
-			 * We can ignore main Xids that are younger than the target
-			 * Xid, since the target could not possibly be their child.
-			 */
-			if (TransactionIdPrecedes(xid, pxid))
-				continue;
-
-			/*
-			 * Step 2: check the cached child-Xids arrays
-			 */
-			for (j = proc->subxids.nxids - 1; j >= 0; j--)
-			{
-				/* Fetch xid just once - see GetNewTransactionId */
-				TransactionId cxid = proc->subxids.xids[j];
-
-				if (TransactionIdEquals(cxid, xid))
-				{
-					xc_by_child_xid_inc();
-					result = true;
-					goto result_known;
-				}
-			}
-
-			/*
-			 * Save the main Xid for step 3.  We only need to remember
-			 * main Xids that have uncached children.  (Note: there is no
-			 * race condition here because the overflowed flag cannot be
-			 * cleared, only set, while we hold SInvalLock.  So we can't
-			 * miss an Xid that we need to worry about.)
-			 */
-			if (proc->subxids.overflowed)
-				xids[nxids++] = pxid;
-		}
-	}
-
-	LWLockRelease(SInvalLock);
-	locked = false;
-
-	/*
-	 * If none of the relevant caches overflowed, we know the Xid is not
-	 * running without looking at pg_subtrans.
-	 */
-	if (nxids == 0)
-		goto result_known;
-
-	/*
-	 * Step 3: have to check pg_subtrans.
-	 *
-	 * At this point, we know it's either a subtransaction of one of the Xids
-	 * in xids[], or it's not running.  If it's an already-failed
-	 * subtransaction, we want to say "not running" even though its parent
-	 * may still be running.  So first, check pg_clog to see if it's been
-	 * aborted.
-	 */
-	xc_slow_answer_inc();
-
-	if (TransactionIdDidAbort(xid))
-		goto result_known;
-
-	/*
-	 * It isn't aborted, so check whether the transaction tree it belongs
-	 * to is still running (or, more precisely, whether it was running
-	 * when this routine started -- note that we already released
-	 * SInvalLock).
-	 */
-	topxid = SubTransGetTopmostTransaction(xid);
-	Assert(TransactionIdIsValid(topxid));
-	if (!TransactionIdEquals(topxid, xid))
-	{
-		for (i = 0; i < nxids; i++)
-		{
-			if (TransactionIdEquals(xids[i], topxid))
-			{
-				result = true;
-				break;
-			}
-		}
-	}
-
-result_known:
-	if (locked)
-		LWLockRelease(SInvalLock);
-
-	pfree(xids);
-
-	return result;
-}
-
-/*
- * GetOldestXmin -- returns oldest transaction that was running
- *					when any current transaction was started.
- *
- * If allDbs is TRUE then all backends are considered; if allDbs is FALSE
- * then only backends running in my own database are considered.
- *
- * This is used by VACUUM to decide which deleted tuples must be preserved
- * in a table.	allDbs = TRUE is needed for shared relations, but allDbs =
- * FALSE is sufficient for non-shared relations, since only backends in my
- * own database could ever see the tuples in them.
- *
- * This is also used to determine where to truncate pg_subtrans.  allDbs
- * must be TRUE for that case.
- *
- * Note: we include the currently running xids in the set of considered xids.
- * This ensures that if a just-started xact has not yet set its snapshot,
- * when it does set the snapshot it cannot set xmin less than what we compute.
- */
-TransactionId
-GetOldestXmin(bool allDbs)
-{
-	SISeg	   *segP = shmInvalBuffer;
-	ProcState  *stateP = segP->procState;
-	TransactionId result;
-	int			index;
-
-	/*
-	 * Normally we start the min() calculation with our own XID.  But if
-	 * called by checkpointer, we will not be inside a transaction, so use
-	 * next XID as starting point for min() calculation.  (Note that if
-	 * there are no xacts running at all, that will be the subtrans
-	 * truncation point!)
-	 */
-	if (IsTransactionState())
-		result = GetTopTransactionId();
-	else
-		result = ReadNewTransactionId();
-
-	LWLockAcquire(SInvalLock, LW_SHARED);
-
-	for (index = 0; index < segP->lastBackend; index++)
-	{
-		SHMEM_OFFSET pOffset = stateP[index].procStruct;
-
-		if (pOffset != INVALID_OFFSET)
-		{
-			PGPROC	   *proc = (PGPROC *) MAKE_PTR(pOffset);
-
-			if (allDbs || proc->databaseId == MyDatabaseId)
-			{
-				/* Fetch xid just once - see GetNewTransactionId */
-				TransactionId xid = proc->xid;
-
-				if (TransactionIdIsNormal(xid))
-				{
-					if (TransactionIdPrecedes(xid, result))
-						result = xid;
-					xid = proc->xmin;
-					if (TransactionIdIsNormal(xid))
-						if (TransactionIdPrecedes(xid, result))
-							result = xid;
-				}
-			}
-		}
-	}
-
-	LWLockRelease(SInvalLock);
-
-	return result;
-}
-
-/*----------
- * GetSnapshotData -- returns information about running transactions.
- *
- * The returned snapshot includes xmin (lowest still-running xact ID),
- * xmax (next xact ID to be assigned), and a list of running xact IDs
- * in the range xmin <= xid < xmax.  It is used as follows:
- *		All xact IDs < xmin are considered finished.
- *		All xact IDs >= xmax are considered still running.
- *		For an xact ID xmin <= xid < xmax, consult list to see whether
- *		it is considered running or not.
- * This ensures that the set of transactions seen as "running" by the
- * current xact will not change after it takes the snapshot.
- *
- * Note that only top-level XIDs are included in the snapshot.  We can
- * still apply the xmin and xmax limits to subtransaction XIDs, but we
- * need to work a bit harder to see if XIDs in [xmin..xmax) are running.
- *
- * We also update the following backend-global variables:
- *		TransactionXmin: the oldest xmin of any snapshot in use in the
- *			current transaction (this is the same as MyProc->xmin).  This
- *			is just the xmin computed for the first, serializable snapshot.
- *		RecentXmin: the xmin computed for the most recent snapshot.  XIDs
- *			older than this are known not running any more.
- *		RecentGlobalXmin: the global xmin (oldest TransactionXmin across all
- *			running transactions).  This is the same computation done by
- *			GetOldestXmin(TRUE).
- *----------
- */
-Snapshot
-GetSnapshotData(Snapshot snapshot, bool serializable)
-{
-	SISeg	   *segP = shmInvalBuffer;
-	ProcState  *stateP = segP->procState;
-	TransactionId xmin;
-	TransactionId xmax;
-	TransactionId globalxmin;
-	int			index;
-	int			count = 0;
-
-	Assert(snapshot != NULL);
-
-	/* Serializable snapshot must be computed before any other... */
-	Assert(serializable ?
-		   !TransactionIdIsValid(MyProc->xmin) :
-		   TransactionIdIsValid(MyProc->xmin));
-
-	/*
-	 * Allocating space for MaxBackends xids is usually overkill;
-	 * lastBackend would be sufficient.  But it seems better to do the
-	 * malloc while not holding the lock, so we can't look at lastBackend.
-	 *
-	 * This does open a possibility for avoiding repeated malloc/free: since
-	 * MaxBackends does not change at runtime, we can simply reuse the
-	 * previous xip array if any.  (This relies on the fact that all
-	 * callers pass static SnapshotData structs.)
-	 */
-	if (snapshot->xip == NULL)
-	{
-		/*
-		 * First call for this snapshot
-		 */
-		snapshot->xip = (TransactionId *)
-			malloc(MaxBackends * sizeof(TransactionId));
-		if (snapshot->xip == NULL)
-			ereport(ERROR,
-					(errcode(ERRCODE_OUT_OF_MEMORY),
-					 errmsg("out of memory")));
-	}
-
-	globalxmin = xmin = GetTopTransactionId();
-
-	/*
-	 * If we are going to set MyProc->xmin then we'd better get exclusive
-	 * lock; if not, this is a read-only operation so it can be shared.
-	 */
-	LWLockAcquire(SInvalLock, serializable ? LW_EXCLUSIVE : LW_SHARED);
-
-	/*--------------------
-	 * Unfortunately, we have to call ReadNewTransactionId() after acquiring
-	 * SInvalLock above.  It's not good because ReadNewTransactionId() does
-	 * LWLockAcquire(XidGenLock), but *necessary*.	We need to be sure that
-	 * no transactions exit the set of currently-running transactions
-	 * between the time we fetch xmax and the time we finish building our
-	 * snapshot.  Otherwise we could have a situation like this:
-	 *
-	 *		1. Tx Old is running (in Read Committed mode).
-	 *		2. Tx S reads new transaction ID into xmax, then
-	 *		   is swapped out before acquiring SInvalLock.
-	 *		3. Tx New gets new transaction ID (>= S' xmax),
-	 *		   makes changes and commits.
-	 *		4. Tx Old changes some row R changed by Tx New and commits.
-	 *		5. Tx S finishes getting its snapshot data.  It sees Tx Old as
-	 *		   done, but sees Tx New as still running (since New >= xmax).
-	 *
-	 * Now S will see R changed by both Tx Old and Tx New, *but* does not
-	 * see other changes made by Tx New.  If S is supposed to be in
-	 * Serializable mode, this is wrong.
-	 *
-	 * By locking SInvalLock before we read xmax, we ensure that TX Old
-	 * cannot exit the set of running transactions seen by Tx S.  Therefore
-	 * both Old and New will be seen as still running => no inconsistency.
-	 *--------------------
-	 */
-
-	xmax = ReadNewTransactionId();
-
-	for (index = 0; index < segP->lastBackend; index++)
-	{
-		SHMEM_OFFSET pOffset = stateP[index].procStruct;
-
-		if (pOffset != INVALID_OFFSET)
-		{
-			PGPROC	   *proc = (PGPROC *) MAKE_PTR(pOffset);
-
-			/* Fetch xid just once - see GetNewTransactionId */
-			TransactionId xid = proc->xid;
-
-			/*
-			 * Ignore my own proc (dealt with my xid above), procs not
-			 * running a transaction, and xacts started since we read the
-			 * next transaction ID.  There's no need to store XIDs above
-			 * what we got from ReadNewTransactionId, since we'll treat
-			 * them as running anyway.	We also assume that such xacts
-			 * can't compute an xmin older than ours, so they needn't be
-			 * considered in computing globalxmin.
-			 */
-			if (proc == MyProc ||
-				!TransactionIdIsNormal(xid) ||
-				TransactionIdFollowsOrEquals(xid, xmax))
-				continue;
-
-			if (TransactionIdPrecedes(xid, xmin))
-				xmin = xid;
-			snapshot->xip[count] = xid;
-			count++;
-
-			/* Update globalxmin to be the smallest valid xmin */
-			xid = proc->xmin;
-			if (TransactionIdIsNormal(xid))
-				if (TransactionIdPrecedes(xid, globalxmin))
-					globalxmin = xid;
-		}
-	}
-
-	if (serializable)
-		MyProc->xmin = TransactionXmin = xmin;
-
-	LWLockRelease(SInvalLock);
-
-	/*
-	 * Update globalxmin to include actual process xids.  This is a
-	 * slightly different way of computing it than GetOldestXmin uses, but
-	 * should give the same result.
-	 */
-	if (TransactionIdPrecedes(xmin, globalxmin))
-		globalxmin = xmin;
-
-	/* Update global variables too */
-	RecentGlobalXmin = globalxmin;
-	RecentXmin = xmin;
-
-	snapshot->xmin = xmin;
-	snapshot->xmax = xmax;
-	snapshot->xcnt = count;
-
-	snapshot->curcid = GetCurrentCommandId();
-
-	return snapshot;
-}
-
-/*
- * CountActiveBackends --- count backends (other than myself) that are in
- *		active transactions.  This is used as a heuristic to decide if
- *		a pre-XLOG-flush delay is worthwhile during commit.
- *
- * An active transaction is something that has written at least one XLOG
- * record; read-only transactions don't count.  Also, do not count backends
- * that are blocked waiting for locks, since they are not going to get to
- * run until someone else commits.
- */
-int
-CountActiveBackends(void)
-{
-	SISeg	   *segP = shmInvalBuffer;
-	ProcState  *stateP = segP->procState;
-	int			count = 0;
-	int			index;
-
-	/*
-	 * Note: for speed, we don't acquire SInvalLock.  This is a little bit
-	 * bogus, but since we are only testing xrecoff for zero or nonzero,
-	 * it should be OK.  The result is only used for heuristic purposes
-	 * anyway...
-	 */
-	for (index = 0; index < segP->lastBackend; index++)
-	{
-		SHMEM_OFFSET pOffset = stateP[index].procStruct;
-
-		if (pOffset != INVALID_OFFSET)
-		{
-			PGPROC	   *proc = (PGPROC *) MAKE_PTR(pOffset);
-
-			if (proc == MyProc)
-				continue;		/* do not count myself */
-			if (proc->logRec.xrecoff == 0)
-				continue;		/* do not count if not in a transaction */
-			if (proc->waitLock != NULL)
-				continue;		/* do not count if blocked on a lock */
-			count++;
-		}
-	}
-
-	return count;
-}
-
-#ifdef NOT_USED
-/*
- * GetUndoRecPtr -- returns oldest PGPROC->logRec.
- */
-XLogRecPtr
-GetUndoRecPtr(void)
-{
-	SISeg	   *segP = shmInvalBuffer;
-	ProcState  *stateP = segP->procState;
-	XLogRecPtr	urec = {0, 0};
-	XLogRecPtr	tempr;
-	int			index;
-
-	LWLockAcquire(SInvalLock, LW_SHARED);
-
-	for (index = 0; index < segP->lastBackend; index++)
-	{
-		SHMEM_OFFSET pOffset = stateP[index].procStruct;
-
-		if (pOffset != INVALID_OFFSET)
-		{
-			PGPROC	   *proc = (PGPROC *) MAKE_PTR(pOffset);
-
-			tempr = proc->logRec;
-			if (tempr.xrecoff == 0)
-				continue;
-			if (urec.xrecoff != 0 && XLByteLT(urec, tempr))
-				continue;
-			urec = tempr;
-		}
-	}
-
-	LWLockRelease(SInvalLock);
-
-	return (urec);
-}
-#endif   /* NOT_USED */
-
-/*
- * BackendIdGetProc - given a BackendId, find its PGPROC structure
- *
- * This is a trivial lookup in the ProcState array.  We assume that the caller
- * knows that the backend isn't going to go away, so we do not bother with
- * locking.
- */
-struct PGPROC *
-BackendIdGetProc(BackendId procId)
-{
-	SISeg	   *segP = shmInvalBuffer;
-
-	if (procId > 0 && procId <= segP->lastBackend)
-	{
-		ProcState  *stateP = &segP->procState[procId - 1];
-		SHMEM_OFFSET pOffset = stateP->procStruct;
-
-		if (pOffset != INVALID_OFFSET)
-		{
-			PGPROC	   *proc = (PGPROC *) MAKE_PTR(pOffset);
-
-			return proc;
-		}
-	}
-
-	return NULL;
-}
-
-/*
- * CountEmptyBackendSlots - count empty slots in backend process table
- *
- * We don't actually need to count, since sinvaladt.c maintains a
- * freeBackends counter in the SI segment.
- *
- * Acquiring the lock here is almost certainly overkill, but just in
- * case fetching an int is not atomic on your machine ...
- */
-int
-CountEmptyBackendSlots(void)
-{
-	int			count;
-
-	LWLockAcquire(SInvalLock, LW_SHARED);
-
-	count = shmInvalBuffer->freeBackends;
-
-	LWLockRelease(SInvalLock);
-
-	return count;
-}
-
-#define XidCacheRemove(i) \
-	do { \
-		MyProc->subxids.xids[i] = MyProc->subxids.xids[MyProc->subxids.nxids - 1]; \
-		MyProc->subxids.nxids--; \
-	} while (0)
-
-/*
- * XidCacheRemoveRunningXids
- *
- * Remove a bunch of TransactionIds from the list of known-running
- * subtransactions for my backend.	Both the specified xid and those in
- * the xids[] array (of length nxids) are removed from the subxids cache.
- */
-void
-XidCacheRemoveRunningXids(TransactionId xid, int nxids, TransactionId *xids)
-{
-	int			i,
-				j;
-
-	Assert(!TransactionIdEquals(xid, InvalidTransactionId));
-
-	/*
-	 * We must hold SInvalLock exclusively in order to remove transactions
-	 * from the PGPROC array.  (See notes in GetSnapshotData.)	It's
-	 * possible this could be relaxed since we know this routine is only
-	 * used to abort subtransactions, but pending closer analysis we'd
-	 * best be conservative.
-	 */
-	LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
-
-	/*
-	 * Under normal circumstances xid and xids[] will be in increasing
-	 * order, as will be the entries in subxids.  Scan backwards to avoid
-	 * O(N^2) behavior when removing a lot of xids.
-	 */
-	for (i = nxids - 1; i >= 0; i--)
-	{
-		TransactionId anxid = xids[i];
-
-		for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
-		{
-			if (TransactionIdEquals(MyProc->subxids.xids[j], anxid))
-			{
-				XidCacheRemove(j);
-				break;
-			}
-		}
-		/*
-		 * Ordinarily we should have found it, unless the cache has overflowed.
-		 * However it's also possible for this routine to be invoked multiple
-		 * times for the same subtransaction, in case of an error during
-		 * AbortSubTransaction.  So instead of Assert, emit a debug warning.
-		 */
-		if (j < 0 && !MyProc->subxids.overflowed)
-			elog(WARNING, "did not find subXID %u in MyProc", anxid);
-	}
-
-	for (j = MyProc->subxids.nxids - 1; j >= 0; j--)
-	{
-		if (TransactionIdEquals(MyProc->subxids.xids[j], xid))
-		{
-			XidCacheRemove(j);
-			break;
-		}
-	}
-	/* Ordinarily we should have found it, unless the cache has overflowed */
-	if (j < 0 && !MyProc->subxids.overflowed)
-		elog(WARNING, "did not find subXID %u in MyProc", xid);
-
-	LWLockRelease(SInvalLock);
-}
-
-#ifdef XIDCACHE_DEBUG
-
-/*
- * on_proc_exit hook to print stats about effectiveness of XID cache
- */
-static void
-DisplayXidCache(int code, Datum arg)
-{
-	fprintf(stderr,
-		 "XidCache: xmin: %ld, mainxid: %ld, childxid: %ld, slow: %ld\n",
-			xc_by_recent_xmin,
-			xc_by_main_xid,
-			xc_by_child_xid,
-			xc_slow_answer);
-}
-
-#endif   /* XIDCACHE_DEBUG */
diff --git a/src/backend/storage/ipc/sinvaladt.c b/src/backend/storage/ipc/sinvaladt.c
index 98b1e8fd6a63543214b37be0e0a8cf71954209a0..64a9672b30881bc9d56f4da3d2409e4ea0f6a1bf 100644
--- a/src/backend/storage/ipc/sinvaladt.c
+++ b/src/backend/storage/ipc/sinvaladt.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.58 2004/12/31 22:00:56 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/ipc/sinvaladt.c,v 1.59 2005/05/19 21:35:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,10 +17,12 @@
 #include "miscadmin.h"
 #include "storage/backendid.h"
 #include "storage/ipc.h"
+#include "storage/lwlock.h"
 #include "storage/pmsignal.h"
-#include "storage/proc.h"
+#include "storage/shmem.h"
 #include "storage/sinvaladt.h"
 
+
 SISeg	   *shmInvalBuffer;
 
 static void CleanupInvalidationState(int status, Datum arg);
@@ -72,7 +74,6 @@ SIBufferInit(int maxBackends)
 	{
 		segP->procState[i].nextMsgNum = -1;		/* inactive */
 		segP->procState[i].resetState = false;
-		segP->procState[i].procStruct = INVALID_OFFSET;
 	}
 }
 
@@ -133,7 +134,6 @@ SIBackendInit(SISeg *segP)
 	/* mark myself active, with all extant messages already read */
 	stateP->nextMsgNum = segP->maxMsgNum;
 	stateP->resetState = false;
-	stateP->procStruct = MAKE_OFFSET(MyProc);
 
 	/* register exit routine to mark my entry inactive at exit */
 	on_shmem_exit(CleanupInvalidationState, PointerGetDatum(segP));
@@ -163,7 +163,6 @@ CleanupInvalidationState(int status, Datum arg)
 	/* Mark myself inactive */
 	segP->procState[MyBackendId - 1].nextMsgNum = -1;
 	segP->procState[MyBackendId - 1].resetState = false;
-	segP->procState[MyBackendId - 1].procStruct = INVALID_OFFSET;
 
 	/* Recompute index of last active backend */
 	for (i = segP->lastBackend; i > 0; i--)
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index f0e2dadfa8adcde1149ce0da427108c86eb91ef0..716ccf29035a39a6c20c55c9acdfd915eb9dd0dc 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.73 2005/04/30 19:03:33 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.74 2005/05/19 21:35:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,7 +21,7 @@
 #include "catalog/catalog.h"
 #include "miscadmin.h"
 #include "storage/lmgr.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 #include "utils/inval.h"
 
 
diff --git a/src/backend/storage/lmgr/proc.c b/src/backend/storage/lmgr/proc.c
index c3ba65442168cc0d27ea02eaece66343c23cfbe2..597e97a5b0ec9a28308eaede089e3cccd03c0ec7 100644
--- a/src/backend/storage/lmgr/proc.c
+++ b/src/backend/storage/lmgr/proc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.157 2005/04/15 04:18:10 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.158 2005/05/19 21:35:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,7 +49,7 @@
 #include "storage/bufmgr.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 #include "storage/spin.h"
 
 
@@ -116,8 +116,7 @@ ProcGlobalSemas(int maxBackends)
 
 /*
  * InitProcGlobal -
- *	  initializes the global process table. We put it here so that
- *	  the postmaster can do this initialization.
+ *	  Initialize the global process table during postmaster startup.
  *
  *	  We also create all the per-process semaphores we will need to support
  *	  the requested number of backends.  We used to allocate semaphores
@@ -263,6 +262,11 @@ InitProcess(void)
 	MyProc->waitProcLock = NULL;
 	SHMQueueInit(&(MyProc->procLocks));
 
+	/*
+	 * Add our PGPROC to the PGPROC array in shared memory.
+	 */
+	ProcArrayAddMyself();
+
 	/*
 	 * Arrange to clean up at backend exit.
 	 */
@@ -473,6 +477,9 @@ ProcKill(int code, Datum arg)
 	LockReleaseAll(USER_LOCKMETHOD, true);
 #endif
 
+	/* Remove our PGPROC from the PGPROC array in shared memory */
+	ProcArrayRemoveMyself();
+
 	SpinLockAcquire(ProcStructLock);
 
 	/* Return PGPROC structure (and semaphore) to freelist */
@@ -978,12 +985,12 @@ ProcCancelWaitForSignal(void)
 }
 
 /*
- * ProcSendSignal - send a signal to a backend identified by BackendId
+ * ProcSendSignal - send a signal to a backend identified by PID
  */
 void
-ProcSendSignal(BackendId procId)
+ProcSendSignal(int pid)
 {
-	PGPROC	   *proc = BackendIdGetProc(procId);
+	PGPROC	   *proc = BackendPidGetProc(pid);
 
 	if (proc != NULL)
 		PGSemaphoreUnlock(&proc->sem);
diff --git a/src/backend/utils/adt/misc.c b/src/backend/utils/adt/misc.c
index 2b1c5e7406ffd86ccd48cc2c27e20b0e60800f5a..886ff011221ba6ae2783505ea297342893878ee5 100644
--- a/src/backend/utils/adt/misc.c
+++ b/src/backend/utils/adt/misc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/misc.c,v 1.42 2005/05/10 22:27:30 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/misc.c,v 1.43 2005/05/19 21:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,7 @@
 
 #include "commands/dbcommands.h"
 #include "miscadmin.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 #include "storage/fd.h"
 #include "utils/builtins.h"
 #include "funcapi.h"
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index fef1565a04baf965cdd7fb4b33e048e1655358d4..7130365d4fd061c3b963615831205046240a023e 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.146 2005/05/05 19:53:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.147 2005/05/19 21:35:47 tgl Exp $
  *
  *
  *-------------------------------------------------------------------------
@@ -30,9 +30,11 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "postmaster/postmaster.h"
+#include "storage/backendid.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
+#include "storage/procarray.h"
 #include "storage/sinval.h"
 #include "storage/smgr.h"
 #include "utils/flatfiles.h"
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index c66a3d0e1f799f770194977fcae6ff576eca93ce..94633249641298025fb43fa5f14388e0d099bbab 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -32,7 +32,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.88 2005/05/07 21:22:01 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.89 2005/05/19 21:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,7 +41,7 @@
 
 #include "access/multixact.h"
 #include "access/subtrans.h"
-#include "storage/sinval.h"
+#include "storage/procarray.h"
 #include "utils/tqual.h"
 
 /*
diff --git a/src/include/storage/buf_internals.h b/src/include/storage/buf_internals.h
index 17687510cdec1374491a9d59b6da0bda6a38b1ab..2bd210dfb52e5534cce5428f7d61d8534598b583 100644
--- a/src/include/storage/buf_internals.h
+++ b/src/include/storage/buf_internals.h
@@ -8,14 +8,13 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/buf_internals.h,v 1.77 2005/03/04 20:21:07 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/buf_internals.h,v 1.78 2005/05/19 21:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef BUFMGR_INTERNALS_H
 #define BUFMGR_INTERNALS_H
 
-#include "storage/backendid.h"
 #include "storage/buf.h"
 #include "storage/lwlock.h"
 #include "storage/shmem.h"
@@ -94,7 +93,7 @@ typedef struct buftag
  *	BufferDesc -- shared descriptor/state data for a single shared buffer.
  *
  * Note: buf_hdr_lock must be held to examine or change the tag, flags,
- * usage_count, refcount, or wait_backend_id fields.  buf_id field never
+ * usage_count, refcount, or wait_backend_pid fields.  buf_id field never
  * changes after initialization, so does not need locking.  freeNext is
  * protected by the BufFreelistLock not buf_hdr_lock.  The LWLocks can take
  * care of themselves.  The buf_hdr_lock is *not* used to control access to
@@ -108,8 +107,8 @@ typedef struct buftag
  *
  * We can't physically remove items from a disk page if another backend has
  * the buffer pinned.  Hence, a backend may need to wait for all other pins
- * to go away.  This is signaled by storing its own backend ID into
- * wait_backend_id and setting flag bit BM_PIN_COUNT_WAITER.  At present,
+ * to go away.  This is signaled by storing its own PID into
+ * wait_backend_pid and setting flag bit BM_PIN_COUNT_WAITER.  At present,
  * there can be only one such waiter per buffer.
  *
  * We use this same struct for local buffer headers, but the lock fields
@@ -121,7 +120,7 @@ typedef struct sbufdesc
 	BufFlags	flags;			/* see bit definitions above */
 	uint16		usage_count;	/* usage counter for clock sweep code */
 	unsigned	refcount;		/* # of backends holding pins on buffer */
-	BackendId	wait_backend_id;	/* backend ID of pin-count waiter */
+	int			wait_backend_pid; /* backend PID of pin-count waiter */
 
 	slock_t		buf_hdr_lock;	/* protects the above fields */
 
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 18215f48381f8a3c0e2d8702353c61a8b56c6498..eb45f3e39aef8c5b1003edacb8ea3937ea1ce9e4 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/lwlock.h,v 1.18 2005/04/28 21:47:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/lwlock.h,v 1.19 2005/05/19 21:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,6 +30,7 @@ typedef enum LWLockId
 	LockMgrLock,
 	OidGenLock,
 	XidGenLock,
+	ProcArrayLock,
 	SInvalLock,
 	FreeSpaceLock,
 	MMCacheLock,
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index 8d514fce94d9120c8406dfd61d676b65eeecb329..f771d71933c3c1ab37cb0ba05f86defbeb20371a 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.77 2004/12/31 22:03:42 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.78 2005/05/19 21:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,7 +15,6 @@
 #define _PROC_H_
 
 #include "access/xlog.h"
-#include "storage/backendid.h"
 #include "storage/lock.h"
 #include "storage/pg_sema.h"
 
@@ -137,7 +136,7 @@ extern bool LockWaitCancel(void);
 
 extern void ProcWaitForSignal(void);
 extern void ProcCancelWaitForSignal(void);
-extern void ProcSendSignal(BackendId procId);
+extern void ProcSendSignal(int pid);
 
 extern bool enable_sig_alarm(int delayms, bool is_statement_timeout);
 extern bool disable_sig_alarm(bool is_statement_timeout);
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
new file mode 100644
index 0000000000000000000000000000000000000000..437ac306e055439068a4fa163aa1ad454c90c467
--- /dev/null
+++ b/src/include/storage/procarray.h
@@ -0,0 +1,36 @@
+/*-------------------------------------------------------------------------
+ *
+ * procarray.h
+ *	  POSTGRES process array definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.1 2005/05/19 21:35:47 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PROCARRAY_H
+#define PROCARRAY_H
+
+extern int	ProcArrayShmemSize(int maxBackends);
+extern void CreateSharedProcArray(int maxBackends);
+extern void ProcArrayAddMyself(void);
+extern void ProcArrayRemoveMyself(void);
+
+extern bool TransactionIdIsInProgress(TransactionId xid);
+extern TransactionId GetOldestXmin(bool allDbs);
+
+/* Use "struct PGPROC", not PGPROC, to avoid including proc.h here */
+extern struct PGPROC *BackendPidGetProc(int pid);
+extern bool IsBackendPid(int pid);
+extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself);
+
+extern int	CountActiveBackends(void);
+extern int	CountEmptyBackendSlots(void);
+
+extern void XidCacheRemoveRunningXids(TransactionId xid,
+						  int nxids, TransactionId *xids);
+
+#endif   /* PROCARRAY_H */
diff --git a/src/include/storage/sinval.h b/src/include/storage/sinval.h
index 092b722048817bdb2f424f18ceb81ea1f0b7d1d9..0f4c0f68f3f20792f500cc59f1f6a7af665000c6 100644
--- a/src/include/storage/sinval.h
+++ b/src/include/storage/sinval.h
@@ -7,14 +7,13 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/sinval.h,v 1.40 2005/01/10 21:57:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/storage/sinval.h,v 1.41 2005/05/19 21:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef SINVAL_H
 #define SINVAL_H
 
-#include "storage/backendid.h"
 #include "storage/itemptr.h"
 #include "storage/relfilenode.h"
 
@@ -87,24 +86,12 @@ typedef union
 extern int	SInvalShmemSize(int maxBackends);
 extern void CreateSharedInvalidationState(int maxBackends);
 extern void InitBackendSharedInvalidationState(void);
+
 extern void SendSharedInvalidMessage(SharedInvalidationMessage *msg);
 extern void ReceiveSharedInvalidMessages(
 				  void (*invalFunction) (SharedInvalidationMessage *msg),
 							 void (*resetFunction) (void));
 
-extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself);
-extern bool TransactionIdIsInProgress(TransactionId xid);
-extern bool IsBackendPid(int pid);
-extern TransactionId GetOldestXmin(bool allDbs);
-extern int	CountActiveBackends(void);
-extern int	CountEmptyBackendSlots(void);
-
-/* Use "struct PGPROC", not PGPROC, to avoid including proc.h here */
-extern struct PGPROC *BackendIdGetProc(BackendId procId);
-
-extern void XidCacheRemoveRunningXids(TransactionId xid,
-						  int nxids, TransactionId *xids);
-
 /* signal handler for catchup events (SIGUSR1) */
 extern void CatchupInterruptHandler(SIGNAL_ARGS);
 
diff --git a/src/include/storage/sinvaladt.h b/src/include/storage/sinvaladt.h
index 7fce6070da0971cb416760bfffef466004a069f9..0be6a3865081345389ba269b0d0528758c72460d 100644
--- a/src/include/storage/sinvaladt.h
+++ b/src/include/storage/sinvaladt.h
@@ -7,16 +7,16 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/storage/sinvaladt.h,v 1.37 2004/12/31 22:03:42 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/storage/sinvaladt.h,v 1.38 2005/05/19 21:35:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef SINVALADT_H
 #define SINVALADT_H
 
-#include "storage/shmem.h"
 #include "storage/sinval.h"
 
+
 /*
  * The shared cache invalidation manager is responsible for transmitting
  * invalidation messages between backends.	Any message sent by any backend
@@ -71,7 +71,6 @@ typedef struct ProcState
 	/* nextMsgNum is -1 in an inactive ProcState array entry. */
 	int			nextMsgNum;		/* next message number to read, or -1 */
 	bool		resetState;		/* true, if backend has to reset its state */
-	SHMEM_OFFSET procStruct;	/* location of backend's PGPROC struct */
 } ProcState;
 
 /* Shared cache invalidation memory segment */
diff --git a/src/include/utils/tqual.h b/src/include/utils/tqual.h
index e553cfedf81e2f4d5847b8aab2c584cbc9c1c57a..f12ae2233fd29c8b93ad9086270919b25a73fbfd 100644
--- a/src/include/utils/tqual.h
+++ b/src/include/utils/tqual.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/tqual.h,v 1.56 2005/03/20 23:40:34 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/utils/tqual.h,v 1.57 2005/05/19 21:35:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -133,7 +133,7 @@ extern Snapshot CopySnapshot(Snapshot snapshot);
 extern void FreeSnapshot(Snapshot snapshot);
 extern void FreeXactSnapshot(void);
 
-/* in sinval.c; declared here to avoid including tqual.h in sinval.h: */
+/* in procarray.c; declared here to avoid including tqual.h in procarray.h: */
 extern Snapshot GetSnapshotData(Snapshot snapshot, bool serializable);
 
 #endif   /* TQUAL_H */