From 1bab464eb40ceb96a9dedbf11393b7b51c55ec0a Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 31 Aug 2002 17:14:28 +0000
Subject: [PATCH] Code review for pg_locks feature.  Make shmemoffset of
 PROCLOCK structs available (else there's no way to interpret the list links).
  Change pg_locks view to show transaction ID locks separately from ordinary
 relation locks.  Avoid showing N duplicate rows when the same lock is held
 multiple times (seems unlikely that users care about exact hold count). 
 Improve documentation.

---
 doc/src/sgml/monitoring.sgml        |  88 ++++++++-----
 src/backend/storage/lmgr/lock.c     |  59 +++++----
 src/backend/utils/adt/lockfuncs.c   | 186 +++++++++++++++-------------
 src/bin/initdb/initdb.sh            |  13 +-
 src/include/catalog/catversion.h    |   4 +-
 src/include/storage/lock.h          |  23 ++--
 src/test/regress/expected/rules.out |   2 +-
 7 files changed, 212 insertions(+), 163 deletions(-)

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index e759789c1f4..6e801174e88 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/monitoring.sgml,v 1.12 2002/08/20 04:47:52 momjian Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/monitoring.sgml,v 1.13 2002/08/31 17:14:27 tgl Exp $
 -->
 
 <chapter id="monitoring">
@@ -593,22 +593,25 @@ FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS S;
 
    <note>
 	<para>
-	 When the <literal>pg_locks</literal> view is accessed, an
-	 exclusive lock on an internal lock manager data structure must be
-	 acquired to ensure that the data produced by the view is
-	 consistent. The lock held on this structure conflicts with normal
-	 database operations, and can therefore have an effect on overall
-	 database performance. Nevertheless, the performance impact of
-	 accessing this view should be minimal in most situations.
+	 When the <literal>pg_locks</literal> view is accessed, the
+	 internal lock manager data structures are momentarily locked,
+	 and a copy is made for the view to display.  This ensures that
+	 the view produces a consistent set of results, while not blocking
+	 normal lock manager operations longer than necessary.  Nonetheless
+	 there could be some impact on database performance if this view is
+	 examined often.
 	</para>
    </note>
 
    <para>
-	The <literal>pg_locks</literal> view contains one row per
-	lock. This means that if there are multiple locks on a single
-	relation (which may or may not conflict with one another), a
-	single relation may show up many times. Furthermore, only
-	table-level locks are displayed (not row-level ones).
+	The <literal>pg_locks</literal> view contains one row per lockable
+	object and requested lock mode.  Thus, the same lockable object
+	may appear many times, if multiple transactions are holding or
+	waiting for locks on it.  A lockable object is either a relation
+	or a transaction ID.  (Note that this view includes only table-level
+	locks, not row-level ones.  If a transaction is waiting for a
+	row-level lock, it will appear in the view as waiting for the
+	transaction ID of the current holder of that row lock.)
    </para>
 
    <table>
@@ -627,31 +630,50 @@ FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS S;
 	  <row>
 	   <entry><structfield>relation</structfield></entry>
 	   <entry><type>oid</type></entry>
-	   <entry>The OID of the locked relation. When querying
-	   <literal>pg_locks</literal>, this column can be joined with the
-	   <literal>pg_class</literal> system catalog to get more
-	   information on the locked relation.</entry>
+	   <entry>The OID of the locked relation, or NULL if the lockable
+	   object is a transaction ID. This column can be joined
+	   with the <literal>pg_class</literal> system catalog to get more
+	   information on the locked relation.  Note however that this will
+	   only work for relations in the current database (those for which
+	   the <structfield>database</structfield> column is either the
+	   current database's OID or zero).
+	   </entry>
 	  </row>
 
 	  <row>
 	   <entry><structfield>database</structfield></entry>
 	   <entry><type>oid</type></entry>
 	   <entry>The OID of the database in which the locked relation
-		exists. If the lock is on a globally-shared object, this value
-		will be 0. When querying <literal>pg_locks</literal>, this
-		column can be joined with the <literal>pg_database</literal>
-		system catalog to get more information on the locked object's
-		database.</entry>
+	    exists, or NULL if the lockable object is a transaction ID.
+	    If the lock is on a globally-shared table, this field will be
+	    zero. This
+	    column can be joined with the <literal>pg_database</literal>
+	    system catalog to get more information on the locked object's
+	    database.
+	   </entry>
 	  </row>
 
 	  <row>
-	   <entry><structfield>backendpid</structfield></entry>
+	   <entry><structfield>transaction</structfield></entry>
+	   <entry><type>xid</type></entry>
+	   <entry>The ID of a transaction, or NULL if the lockable object
+	   is a relation.  Every transaction holds ExclusiveLock on its
+	   transaction ID for its entire duration.  If one transaction finds
+	   it necessary to wait specifically for another transaction, it
+	   does so by attempting to acquire ShareLock on the other transaction
+	   ID.  That will succeed only when the other transaction terminates
+	   and releases its locks.
+	   </entry>
+	  </row>
+
+	  <row>
+	   <entry><structfield>pid</structfield></entry>
 	   <entry><type>int4</type></entry>
 	   <entry>The process ID of the
 		<productname>PostgreSQL</productname> backend that has
 		acquired or is attempting to acquire the lock. If you have
 		enabled the statistics collector, this column can be joined
-		with the <literal>pg_stat_activity</literal> view to access
+		with the <literal>pg_stat_activity</literal> view to get
 		more information on the backend holding or waiting to hold the
 		lock.</entry>
 	  </row>
@@ -659,7 +681,8 @@ FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS S;
 	  <row>
 	   <entry><structfield>mode</structfield></entry>
 	   <entry><type>text</type></entry>
-	   <entry>The mode of the lock. For more information on the
+	   <entry>The mode of the requested or held lock on the lockable
+	   object. For more information on the
 	   different lock modes available in
 	   <productname>PostgreSQL</productname>, refer to the
 	   <citetitle>User's Guide</citetitle>.</entry>
@@ -667,13 +690,14 @@ FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS S;
 
 	  <row>
 	   <entry><structfield>isgranted</structfield></entry>
-	   <entry><type>text</type></entry>
-	   <entry>A boolean column indicating whether or not this
-	   particular lock has been granted. If the lock has not been
-	   granted, the backend atempting to acquire it will sleep until
-	   the lock is released (or a deadlock situation is detected). A
-	   single backend can be waiting to acquire at most one lock at
-	   any given time.</entry>
+	   <entry><type>bool</type></entry>
+	   <entry>True if this lock has been granted (is held by this
+	   backend).  False indicates that this backend is currently
+	   waiting to acquire this lock, which implies that some other
+	   backend is holding a conflicting lock mode on the same lockable
+	   object.  This backend will sleep until the other lock is released
+	   (or a deadlock situation is detected). A single backend can be
+	   waiting to acquire at most one lock at a time.</entry>
 	  </row>
 	 </tbody>
 	</tgroup>
diff --git a/src/backend/storage/lmgr/lock.c b/src/backend/storage/lmgr/lock.c
index 154c49b3e92..db4cf76f2fd 100644
--- a/src/backend/storage/lmgr/lock.c
+++ b/src/backend/storage/lmgr/lock.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.112 2002/08/17 13:04:14 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/lmgr/lock.c,v 1.113 2002/08/31 17:14:27 tgl Exp $
  *
  * NOTES
  *	  Outside modules can create a lock table and acquire/release
@@ -1361,59 +1361,68 @@ LockShmemSize(int maxBackends)
 
 /*
  * GetLockStatusData - Return a summary of the lock manager's internal
- * status, for use in a user-level statistical reporting function.
+ * status, for use in a user-level reporting function.
  *
- * This function should be passed a pointer to a LockData struct. It fills
- * the structure with the appropriate information and returns. The goal
- * is to hold the LockMgrLock for as short a time as possible; thus, the
- * function simply makes a copy of the necessary data and releases the
- * lock, allowing the caller to contemplate and format the data for
- * as long as it pleases.
+ * The return data consists of an array of PROCLOCK objects, with the
+ * associated PGPROC and LOCK objects for each.  Note that multiple
+ * copies of the same PGPROC and/or LOCK objects are likely to appear.
+ * It is the caller's responsibility to match up duplicates if wanted.
+ *
+ * The design goal is to hold the LockMgrLock for as short a time as possible;
+ * thus, this function simply makes a copy of the necessary data and releases
+ * the lock, allowing the caller to contemplate and format the data for as
+ * long as it pleases.
  */
-void
-GetLockStatusData(LockData *data)
+LockData *
+GetLockStatusData(void)
 {
+	LockData	*data;
 	HTAB		*holderTable;
 	PROCLOCK	*holder;
 	HASH_SEQ_STATUS seqstat;
-	int i = 0;
+	int i;
 
-	data->currIdx = 0;
+	data = (LockData *) palloc(sizeof(LockData));
 
 	LWLockAcquire(LockMgrLock, LW_EXCLUSIVE);
 
 	holderTable = LockMethodTable[DEFAULT_LOCKMETHOD]->holderHash;
 
-	data->nelements = holderTable->hctl->nentries;
+	data->nelements = i = holderTable->hctl->nentries;
+
+	if (i == 0)
+		i = 1;					/* avoid palloc(0) if empty table */
 
-	data->procs = (PGPROC *) palloc(sizeof(PGPROC) * data->nelements);
-	data->locks = (LOCK *) palloc(sizeof(LOCK) * data->nelements);
-	data->holders = (PROCLOCK *) palloc(sizeof(PROCLOCK) * data->nelements);
+	data->holderaddrs = (SHMEM_OFFSET *) palloc(sizeof(SHMEM_OFFSET) * i);
+	data->holders = (PROCLOCK *) palloc(sizeof(PROCLOCK) * i);
+	data->procs = (PGPROC *) palloc(sizeof(PGPROC) * i);
+	data->locks = (LOCK *) palloc(sizeof(LOCK) * i);
 
 	hash_seq_init(&seqstat, holderTable);
 
+	i = 0;
 	while ( (holder = hash_seq_search(&seqstat)) )
 	{
-		PGPROC	*proc;
-		LOCK	*lock;
-
-		/* Only do a shallow copy */
-		proc = (PGPROC *) MAKE_PTR(holder->tag.proc);
-		lock = (LOCK *) MAKE_PTR(holder->tag.lock);
+		PGPROC	*proc = (PGPROC *) MAKE_PTR(holder->tag.proc);
+		LOCK	*lock = (LOCK *) MAKE_PTR(holder->tag.lock);
 
+		data->holderaddrs[i] = MAKE_OFFSET(holder);
+		memcpy(&(data->holders[i]), holder, sizeof(PROCLOCK));
 		memcpy(&(data->procs[i]), proc, sizeof(PGPROC));
 		memcpy(&(data->locks[i]), lock, sizeof(LOCK));
-		memcpy(&(data->holders[i]), holder, sizeof(PROCLOCK));
 
 		i++;
 	}
 
+	LWLockRelease(LockMgrLock);
+
 	Assert(i == data->nelements);
 
-	LWLockRelease(LockMgrLock);
+	return data;
 }
 
-char *
+/* Provide the textual name of any lock mode */
+const char *
 GetLockmodeName(LOCKMODE mode)
 {
 	Assert(mode <= MAX_LOCKMODES);
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 199efbacd26..b1ccceebcb9 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -1,36 +1,46 @@
-/*
+/*-------------------------------------------------------------------------
+ *
  * lockfuncs.c
  *		Set-returning functions to view the state of locks within the DB.
  * 
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *		$Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.4 2002/08/29 17:14:33 tgl Exp $
+ *		$Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.5 2002/08/31 17:14:28 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "funcapi.h"
+#include "access/heapam.h"
 #include "catalog/pg_type.h"
-#include "storage/lmgr.h"
 #include "storage/lock.h"
-#include "storage/lwlock.h"
 #include "storage/proc.h"
 #include "utils/builtins.h"
 
 
-static int next_lock(int locks[]);
-
+/* Working status for pg_lock_status */
+typedef struct
+{
+	LockData   *lockData;		/* state data from lmgr */
+	int			currIdx;		/* current PROCLOCK index */
+} PG_Lock_Status;
 
+/*
+ * pg_lock_status - produce a view with one row per held or awaited lock mode
+ */
 Datum
 pg_lock_status(PG_FUNCTION_ARGS)
 {
 	FuncCallContext	   *funcctx;
-	LockData		   *lockData;
-	MemoryContext		oldcontext;
+	PG_Lock_Status	   *mystatus;
+	LockData   *lockData;
 
 	if (SRF_IS_FIRSTCALL())
 	{
 		TupleDesc		tupdesc;
+		MemoryContext	oldcontext;
 
 		/* create a function context for cross-call persistence */
 		funcctx = SRF_FIRSTCALL_INIT();
@@ -38,124 +48,132 @@ pg_lock_status(PG_FUNCTION_ARGS)
 		/* switch to memory context appropriate for multiple function calls */
 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
-		tupdesc = CreateTemplateTupleDesc(5, WITHOUTOID);
+		/* build tupdesc for result tuples */
+		/* this had better match pg_locks view in initdb.sh */
+		tupdesc = CreateTemplateTupleDesc(6, WITHOUTOID);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation",
 						   OIDOID, -1, 0, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
 						   OIDOID, -1, 0, false);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "backendpid",
+		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "transaction",
+						   XIDOID, -1, 0, false);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pid",
 						   INT4OID, -1, 0, false);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "mode",
+		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "mode",
 						   TEXTOID, -1, 0, false);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "isgranted",
+		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "granted",
 						   BOOLOID, -1, 0, false);
 
 		funcctx->slot = TupleDescGetSlot(tupdesc);
-		funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
 
 		/*
-		 * Preload all the locking information that we will eventually format
-		 * and send out as a result set. This is palloc'ed, but since the
-		 * MemoryContext is reset when the SRF finishes, we don't need to
-		 * free it ourselves.
+		 * Collect all the locking information that we will format
+		 * and send out as a result set.
 		 */
-		funcctx->user_fctx = (LockData *) palloc(sizeof(LockData));
+		mystatus = (PG_Lock_Status *) palloc(sizeof(PG_Lock_Status));
+		funcctx->user_fctx = (void *) mystatus;
 
-		GetLockStatusData(funcctx->user_fctx);
+		mystatus->lockData = GetLockStatusData();
+		mystatus->currIdx = 0;
 
 		MemoryContextSwitchTo(oldcontext);
 	}
 
 	funcctx	= SRF_PERCALL_SETUP();
-	lockData = (LockData *) funcctx->user_fctx;
+	mystatus = (PG_Lock_Status *) funcctx->user_fctx;
+	lockData = mystatus->lockData;
 
-	while (lockData->currIdx < lockData->nelements)
+	while (mystatus->currIdx < lockData->nelements)
 	{
 		PROCLOCK		 *holder;
 		LOCK			 *lock;
 		PGPROC			 *proc;
+		bool			granted;
+		LOCKMODE		  mode;
+		Datum			values[6];
+		char			nulls[6];
 		HeapTuple		  tuple;
 		Datum			  result;
-		char			**values;
-		LOCKMODE		  mode;
-		int				  num_attrs;
-		int				  i;
-		int				  currIdx = lockData->currIdx;
-
-		holder		= &(lockData->holders[currIdx]);
-		lock		= &(lockData->locks[currIdx]);
-		proc		= &(lockData->procs[currIdx]);
-		num_attrs	= funcctx->attinmeta->tupdesc->natts;
-
-		values = (char **) palloc(sizeof(*values) * num_attrs);
-
-		for (i = 0; i < num_attrs; i++)
-			values[i] = (char *) palloc(32);
 
-		/* The OID of the locked relation */
-		snprintf(values[0], 32, "%u", lock->tag.relId);
-		/* The database the relation is in */
-		snprintf(values[1], 32, "%u", lock->tag.dbId);
-		/* The PID of the backend holding or waiting for the lock */
-		snprintf(values[2], 32, "%d", proc->pid);
+		holder		= &(lockData->holders[mystatus->currIdx]);
+		lock		= &(lockData->locks[mystatus->currIdx]);
+		proc		= &(lockData->procs[mystatus->currIdx]);
 
 		/*
-		 * We need to report both the locks held (i.e. successfully acquired)
-		 * by this holder, as well as the locks upon which it is still
-		 * waiting, if any. Since a single PROCLOCK struct may contain
-		 * multiple locks, we may need to loop several times before we
-		 * advance the array index and continue on.
+		 * Look to see if there are any held lock modes in this PROCLOCK.
+		 * If so, report, and destructively modify lockData so we don't
+		 * report again.
 		 */
-		if (holder->nHolding > 0)
+		granted = false;
+		for (mode = 0; mode < MAX_LOCKMODES; mode++)
 		{
-			/* Already held locks */
-			mode = next_lock(holder->holding);
-			holder->holding[mode]--;
-			holder->nHolding--;
-
-			strcpy(values[4], "t");
+			if (holder->holding[mode] > 0)
+			{
+				granted = true;
+				holder->holding[mode] = 0;
+				break;
+			}
 		}
-		else if (proc->waitLock != NULL)
+
+		/*
+		 * If no (more) held modes to report, see if PROC is waiting for
+		 * a lock on this lock.
+		 */
+		if (!granted)
 		{
-			/* Lock that is still being waited on */
-			mode = proc->waitLockMode;
-			proc->waitLock = NULL;
-			proc->waitLockMode = NoLock;
+			if (proc->waitLock == (LOCK *) MAKE_PTR(holder->tag.lock))
+			{
+				/* Yes, so report it with proper mode */
+				mode = proc->waitLockMode;
+				/*
+				 * We are now done with this PROCLOCK, so advance pointer
+				 * to continue with next one on next call.
+				 */
+				mystatus->currIdx++;
+			}
+			else
+			{
+				/*
+				 * Okay, we've displayed all the locks associated with this
+				 * PROCLOCK, proceed to the next one.
+				 */
+				mystatus->currIdx++;
+				continue;
+			}
+		}
 
-			strcpy(values[4], "f");
+		/*
+		 * Form tuple with appropriate data.
+		 */
+		MemSet(values, 0, sizeof(values));
+		MemSet(nulls, ' ', sizeof(nulls));
+
+		if (lock->tag.relId == XactLockTableId && lock->tag.dbId == 0)
+		{
+			/* Lock is for transaction ID */
+			nulls[0] = 'n';
+			nulls[1] = 'n';
+			values[2] = TransactionIdGetDatum(lock->tag.objId.xid);
 		}
 		else
 		{
-			/*
-			 * Okay, we've displayed all the lock's belonging to this PROCLOCK,
-			 * procede to the next one.
-			 */
-			lockData->currIdx++;
-			continue;
+			/* Lock is for a relation */
+			values[0] = ObjectIdGetDatum(lock->tag.relId);
+			values[1] = ObjectIdGetDatum(lock->tag.dbId);
+			nulls[2] = 'n';
+
 		}
 
-		strncpy(values[3], GetLockmodeName(mode), 32);
+		values[3] = Int32GetDatum(proc->pid);
+		values[4] = DirectFunctionCall1(textin,
+									CStringGetDatum(GetLockmodeName(mode)));
+		values[5] = BoolGetDatum(granted);
 
-		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
+		tuple = heap_formtuple(funcctx->slot->ttc_tupleDescriptor,
+							   values, nulls);
 		result = TupleGetDatum(funcctx->slot, tuple);
 		SRF_RETURN_NEXT(funcctx, result);
 	}
 
 	SRF_RETURN_DONE(funcctx);
 }
-
-static LOCKMODE
-next_lock(int locks[])
-{
-	LOCKMODE i;
-
-	for (i = 0; i < MAX_LOCKMODES; i++)
-	{
-		if (locks[i] != 0)
-			return i;
-	}
-
-	/* No locks found: this should not occur */
-	Assert(false);
-	return -1;
-}
diff --git a/src/bin/initdb/initdb.sh b/src/bin/initdb/initdb.sh
index 33264050c8e..7441105e000 100644
--- a/src/bin/initdb/initdb.sh
+++ b/src/bin/initdb/initdb.sh
@@ -27,7 +27,7 @@
 # Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
 # Portions Copyright (c) 1994, Regents of the University of California
 #
-# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.169 2002/08/27 04:00:28 momjian Exp $
+# $Header: /cvsroot/pgsql/src/bin/initdb/Attic/initdb.sh,v 1.170 2002/08/31 17:14:28 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -978,15 +978,12 @@ CREATE VIEW pg_stat_database AS \
     FROM pg_database D;
 
 CREATE VIEW pg_locks AS \
-	SELECT \
-		L.relation, L.database, L.backendpid, L.mode, L.isgranted \
-	FROM pg_lock_status() AS L(relation oid, database oid, \
-	backendpid int4, mode text, isgranted boolean);
+    SELECT * \
+    FROM pg_lock_status() AS L(relation oid, database oid, \
+	transaction xid, pid int4, mode text, granted boolean);
 
 CREATE VIEW pg_settings AS \
-    SELECT \
-            A.name, \
-            A.setting \
+    SELECT * \
     FROM pg_show_all_settings() AS A(name text, setting text);
 
 CREATE RULE pg_settings_u AS \
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 15f81094c8e..f561dee66b1 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.155 2002/08/30 19:23:20 tgl Exp $
+ * $Id: catversion.h,v 1.156 2002/08/31 17:14:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200208301
+#define CATALOG_VERSION_NO	200208311
 
 #endif
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index bedf6d7faf7..4c09bdda7c8 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lock.h,v 1.65 2002/08/17 13:04:18 momjian Exp $
+ * $Id: lock.h,v 1.66 2002/08/31 17:14:28 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -204,19 +204,20 @@ typedef struct PROCLOCK
 		(((LOCK *) MAKE_PTR((holder).tag.lock))->tag.lockmethod)
 
 /*
- * This struct is used to encapsulate information passed from lmgr
- * internals to the lock listing statistical functions (lockfuncs.c).
- * It's just a convenient bundle of other lock.h structures. All
- * the information at a given index (holders[i], procs[i], locks[i])
- * is related.
+ * This struct holds information passed from lmgr internals to the lock
+ * listing user-level functions (lockfuncs.c).  For each PROCLOCK in the
+ * system, the SHMEM_OFFSET, PROCLOCK itself, and associated PGPROC and
+ * LOCK objects are stored.  (Note there will often be multiple copies
+ * of the same PGPROC or LOCK.)  We do not store the SHMEM_OFFSET of the
+ * PGPROC or LOCK separately, since they're in the PROCLOCK's tag fields.
  */
 typedef struct
 {
-	int		  nelements;	/* The length of holders, procs, & locks */
-	int		  currIdx;		/* Current element being examined */
+	int		  nelements;	/* The length of each of the arrays */
+	SHMEM_OFFSET *holderaddrs;
+	PROCLOCK *holders;
 	PGPROC	 *procs;
 	LOCK	 *locks;
-	PROCLOCK *holders;
 } LockData;
 
 /*
@@ -242,8 +243,8 @@ extern void RemoveFromWaitQueue(PGPROC *proc);
 extern int	LockShmemSize(int maxBackends);
 extern bool DeadLockCheck(PGPROC *proc);
 extern void InitDeadLockChecking(void);
-extern void GetLockStatusData(LockData *data);
-extern char *GetLockmodeName(LOCKMODE mode);
+extern LockData *GetLockStatusData(void);
+extern const char *GetLockmodeName(LOCKMODE mode);
 
 #ifdef LOCK_DEBUG
 extern void DumpLocks(void);
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 77420cd2b2d..0cfd3f103d4 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1268,7 +1268,7 @@ SELECT viewname, definition FROM pg_views ORDER BY viewname;
 --------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  iexit                    | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
  pg_indexes               | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, pg_get_indexdef(i.oid) AS indexdef FROM (((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char"));
- pg_locks                 | SELECT l.relation, l."database", l.backendpid, l."mode", l.isgranted FROM pg_lock_status() l(relation oid, "database" oid, backendpid integer, "mode" text, isgranted boolean);
+ pg_locks                 | SELECT l.relation, l."database", l."transaction", l.pid, l."mode", l.granted FROM pg_lock_status() l(relation oid, "database" oid, "transaction" xid, pid integer, "mode" text, granted boolean);
  pg_rules                 | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name);
  pg_settings              | SELECT a.name, a.setting FROM pg_show_all_settings() a(name text, setting text);
  pg_stat_activity         | SELECT d.oid AS datid, d.datname, pg_stat_get_backend_pid(s.backendid) AS procpid, pg_stat_get_backend_userid(s.backendid) AS usesysid, u.usename, pg_stat_get_backend_activity(s.backendid) AS current_query FROM pg_database d, (SELECT pg_stat_get_backend_idset() AS backendid) s, pg_shadow u WHERE ((pg_stat_get_backend_dbid(s.backendid) = d.oid) AND (pg_stat_get_backend_userid(s.backendid) = u.usesysid));
-- 
GitLab