From a9c4c9cd52b3fde9aed016cbc5d684ab2ad968ef Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 17 May 2005 21:46:11 +0000
Subject: [PATCH] Extend the pg_locks system view so that it can fully display
 all lock types, as per recent discussion.

---
 contrib/userlock/user_locks.c        |   6 +-
 doc/src/sgml/catalogs.sgml           | 113 ++++++++++++++++++++++----
 src/backend/catalog/system_views.sql |   8 +-
 src/backend/utils/adt/lockfuncs.c    | 116 ++++++++++++++++++++++-----
 src/include/catalog/catversion.h     |   4 +-
 src/include/catalog/pg_proc.h        |   4 +-
 src/test/regress/expected/rules.out  |   2 +-
 7 files changed, 204 insertions(+), 49 deletions(-)

diff --git a/contrib/userlock/user_locks.c b/contrib/userlock/user_locks.c
index 207610084b1..418daf212e6 100644
--- a/contrib/userlock/user_locks.c
+++ b/contrib/userlock/user_locks.c
@@ -19,9 +19,9 @@
 
 
 #define SET_LOCKTAG_USERLOCK(locktag,id1,id2) \
-	((locktag).locktag_field1 = (id1), \
-	 (locktag).locktag_field2 = (id2), \
-	 (locktag).locktag_field3 = MyDatabaseId, \
+	((locktag).locktag_field1 = MyDatabaseId, \
+	 (locktag).locktag_field2 = (id1), \
+	 (locktag).locktag_field3 = (id2), \
 	 (locktag).locktag_field4 = 0, \
 	 (locktag).locktag_type = LOCKTAG_USERLOCK)
 
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 0aa12d4eae5..544f9b758d3 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.101 2005/05/06 14:28:53 tgl Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.102 2005/05/17 21:46:09 tgl Exp $
  -->
 
 <chapter id="catalogs">
@@ -4040,15 +4040,18 @@
    lockable object may
    appear many times, if multiple transactions are holding or waiting
    for locks on it.  However, an object that currently has no locks on it
-   will not appear at all.  A lockable object is either a relation (e.g., a
-   table) or a transaction ID.
+   will not appear at all.
   </para>
 
   <para>
-   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.
+   There are several distinct types of lockable objects:
+   whole relations (e.g., tables), individual pages of relations,
+   individual tuples of relations,
+   transaction IDs,
+   and general database objects (identified by class OID and object OID,
+   in the same way as in <structname>pg_description</structname> or
+   <structname>pg_depend</structname>).  Also, the right to extend a
+   relation is represented as a separate lockable object.
   </para>
 
   <table>
@@ -4065,12 +4068,18 @@
     </thead>
     <tbody>
      <row>
-      <entry><structfield>relation</structfield></entry>
-      <entry><type>oid</type></entry>
-      <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+      <entry><structfield>locktype</structfield></entry>
+      <entry><type>text</type></entry>
+      <entry></entry>
       <entry>
-       OID of the locked relation, or NULL if the lockable object
-       is a transaction ID
+       type of the lockable object:
+       <literal>relation</>,
+       <literal>extend</>,
+       <literal>page</>,
+       <literal>tuple</>,
+       <literal>transaction</>,
+       <literal>object</>, or
+       <literal>userlock</>
       </entry>
      </row>
      <row>
@@ -4078,9 +4087,35 @@
       <entry><type>oid</type></entry>
       <entry><literal><link linkend="catalog-pg-database"><structname>pg_database</structname></link>.oid</literal></entry>
       <entry>
-       OID of the database in which the locked relation exists, or
-       zero if the locked relation is a globally-shared table, or
-       NULL if the lockable object is a transaction ID
+       OID of the database in which the object exists, or
+       zero if the object is a globally-shared object, or
+       NULL if the object is a transaction ID
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>relation</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+      <entry>
+       OID of the relation, or NULL if the object is not
+       a relation or part of a relation
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>page</structfield></entry>
+      <entry><type>integer</type></entry>
+      <entry></entry>
+      <entry>
+       page number within the relation, or NULL if the object
+       is not a tuple or relation page
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>tuple</structfield></entry>
+      <entry><type>smallint</type></entry>
+      <entry></entry>
+      <entry>
+       tuple number within the page, or NULL if the object is not a tuple
       </entry>
      </row>
      <row>
@@ -4088,14 +4123,43 @@
       <entry><type>xid</type></entry>
       <entry></entry>
       <entry>
-       ID of a transaction, or NULL if the lockable object is a relation
+       ID of a transaction, or NULL if the object is not a transaction ID
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>classid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+      <entry>
+       OID of the system catalog containing the object, or NULL if the
+       object is not a general database object
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>objid</structfield></entry>
+      <entry><type>oid</type></entry>
+      <entry>any OID column</entry>
+      <entry>
+       OID of the object within its system catalog, or NULL if the
+       object is not a general database object
+      </entry>
+     </row>
+     <row>
+      <entry><structfield>objsubid</structfield></entry>
+      <entry><type>smallint</type></entry>
+      <entry></entry>
+      <entry>
+       For a table column, this is the column number (the
+       <structfield>classid</> and <structfield>objid</> refer to the
+       table itself).  For all other object types, this column is
+       zero.  NULL if the object is not a general database object
       </entry>
      </row>
      <row>
       <entry><structfield>pid</structfield></entry>
       <entry><type>integer</type></entry>
       <entry></entry>
-      <entry>process ID of a server process holding or awaiting this
+      <entry>process ID of the server process holding or awaiting this
       lock</entry>
      </row>
      <row>
@@ -4133,6 +4197,21 @@
    terminates and releases its locks. 
   </para>
 
+  <para>
+   Although tuples are a lockable type of object,
+   information about row-level locks is stored on disk, not in memory,
+   and therefore row-level locks normally do not appear in this view.
+   If a transaction is waiting for a
+   row-level lock, it will usually appear in the view as waiting for the
+   transaction ID of the current holder of that row lock.
+  </para>
+
+  <para>
+   If user-defined locks are in use, they are displayed using the columns
+   for general database objects.  However, the actual meaning of the lock
+   fields in such cases is up to the user.
+  </para>
+
   <para>
    When the <structname>pg_locks</structname> view is accessed, the
    internal lock manager data structures are momentarily locked, and
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 1bf0f1657d5..d66bb780b4f 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 1996-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.12 2005/05/09 11:31:32 neilc Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.13 2005/05/17 21:46:09 tgl Exp $
  */
 
 CREATE VIEW pg_user AS 
@@ -261,8 +261,10 @@ CREATE VIEW pg_stat_database AS
 
 CREATE VIEW pg_locks AS 
     SELECT * 
-    FROM pg_lock_status() AS L(relation oid, database oid, 
-        transaction xid, pid int4, mode text, granted boolean);
+    FROM pg_lock_status() AS L
+    (locktype text, database oid, relation oid, page int4, tuple int2,
+     transaction xid, classid oid, objid oid, objsubid int2,
+     pid int4, mode text, granted boolean);
 
 CREATE VIEW pg_settings AS 
     SELECT * 
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 6878662b79f..e941ec2d3d8 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -6,7 +6,7 @@
  * Copyright (c) 2002-2005, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *		$PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.17 2005/04/29 22:28:24 tgl Exp $
+ *		$PostgreSQL: pgsql/src/backend/utils/adt/lockfuncs.c,v 1.18 2005/05/17 21:46:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,17 @@
 #include "utils/builtins.h"
 
 
+/* This must match enum LockTagType! */
+static const char * const LockTagTypeNames[] = {
+	"relation",
+	"extend",
+	"page",
+	"tuple",
+	"transaction",
+	"object",
+	"userlock"
+};
+
 /* Working status for pg_lock_status */
 typedef struct
 {
@@ -53,18 +64,30 @@ pg_lock_status(PG_FUNCTION_ARGS)
 
 		/* build tupdesc for result tuples */
 		/* this had better match pg_locks view in system_views.sql */
-		tupdesc = CreateTemplateTupleDesc(6, false);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation",
-						   OIDOID, -1, 0);
+		tupdesc = CreateTemplateTupleDesc(12, false);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "locktype",
+						   TEXTOID, -1, 0);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
 						   OIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "transaction",
+		TupleDescInitEntry(tupdesc, (AttrNumber) 3, "relation",
+						   OIDOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "page",
+						   INT4OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "tuple",
+						   INT2OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "transaction",
 						   XIDOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 4, "pid",
+		TupleDescInitEntry(tupdesc, (AttrNumber) 7, "classid",
+						   OIDOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 8, "objid",
+						   OIDOID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objsubid",
+						   INT2OID, -1, 0);
+		TupleDescInitEntry(tupdesc, (AttrNumber) 10, "pid",
 						   INT4OID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "mode",
+		TupleDescInitEntry(tupdesc, (AttrNumber) 11, "mode",
 						   TEXTOID, -1, 0);
-		TupleDescInitEntry(tupdesc, (AttrNumber) 6, "granted",
+		TupleDescInitEntry(tupdesc, (AttrNumber) 12, "granted",
 						   BOOLOID, -1, 0);
 
 		funcctx->tuple_desc = BlessTupleDesc(tupdesc);
@@ -93,8 +116,10 @@ pg_lock_status(PG_FUNCTION_ARGS)
 		PGPROC	   *proc;
 		bool		granted;
 		LOCKMODE	mode = 0;
-		Datum		values[6];
-		char		nulls[6];
+		const char *locktypename;
+		char		tnbuf[32];
+		Datum		values[12];
+		char		nulls[12];
 		HeapTuple	tuple;
 		Datum		result;
 
@@ -155,30 +180,79 @@ pg_lock_status(PG_FUNCTION_ARGS)
 		MemSet(values, 0, sizeof(values));
 		MemSet(nulls, ' ', sizeof(nulls));
 
+		if (lock->tag.locktag_type <= LOCKTAG_USERLOCK)
+			locktypename = LockTagTypeNames[lock->tag.locktag_type];
+		else
+		{
+			snprintf(tnbuf, sizeof(tnbuf), "unknown %d",
+					 (int) lock->tag.locktag_type);
+			locktypename = tnbuf;
+		}
+		values[0] = DirectFunctionCall1(textin,
+										 CStringGetDatum(locktypename));
+
+
 		switch (lock->tag.locktag_type)
 		{
 			case LOCKTAG_RELATION:
 			case LOCKTAG_RELATION_EXTEND:
+				values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
+				values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
+				nulls[3] = 'n';
+				nulls[4] = 'n';
+				nulls[5] = 'n';
+				nulls[6] = 'n';
+				nulls[7] = 'n';
+				nulls[8] = 'n';
+				break;
 			case LOCKTAG_PAGE:
+				values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
+				values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
+				values[3] = UInt32GetDatum(lock->tag.locktag_field3);
+				nulls[4] = 'n';
+				nulls[5] = 'n';
+				nulls[6] = 'n';
+				nulls[7] = 'n';
+				nulls[8] = 'n';
+				break;
 			case LOCKTAG_TUPLE:
-				values[0] = ObjectIdGetDatum(lock->tag.locktag_field2);
 				values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
-				nulls[2] = 'n';
+				values[2] = ObjectIdGetDatum(lock->tag.locktag_field2);
+				values[3] = UInt32GetDatum(lock->tag.locktag_field3);
+				values[4] = UInt16GetDatum(lock->tag.locktag_field4);
+				nulls[5] = 'n';
+				nulls[6] = 'n';
+				nulls[7] = 'n';
+				nulls[8] = 'n';
 				break;
 			case LOCKTAG_TRANSACTION:
-				nulls[0] = 'n';
+				values[5] = TransactionIdGetDatum(lock->tag.locktag_field1);
 				nulls[1] = 'n';
-				values[2] = TransactionIdGetDatum(lock->tag.locktag_field1);
+				nulls[2] = 'n';
+				nulls[3] = 'n';
+				nulls[4] = 'n';
+				nulls[6] = 'n';
+				nulls[7] = 'n';
+				nulls[8] = 'n';
+				break;
+			case LOCKTAG_OBJECT:
+			case LOCKTAG_USERLOCK:
+			default:			/* treat unknown locktags like OBJECT */
+				values[1] = ObjectIdGetDatum(lock->tag.locktag_field1);
+				values[6] = ObjectIdGetDatum(lock->tag.locktag_field2);
+				values[7] = ObjectIdGetDatum(lock->tag.locktag_field3);
+				values[8] = Int16GetDatum(lock->tag.locktag_field4);
+				nulls[2] = 'n';
+				nulls[3] = 'n';
+				nulls[4] = 'n';
+				nulls[5] = 'n';
 				break;
-			default:
-				/* XXX Ignore all other lock types for now */
-				continue;
 		}
 
-		values[3] = Int32GetDatum(proc->pid);
-		values[4] = DirectFunctionCall1(textin,
-								 CStringGetDatum(GetLockmodeName(mode)));
-		values[5] = BoolGetDatum(granted);
+		values[9] = Int32GetDatum(proc->pid);
+		values[10] = DirectFunctionCall1(textin,
+										 CStringGetDatum(GetLockmodeName(mode)));
+		values[11] = BoolGetDatum(granted);
 
 		tuple = heap_formtuple(funcctx->tuple_desc, values, nulls);
 		result = HeapTupleGetDatum(tuple);
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 2579b032a59..dd1c29ab02b 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.267 2005/05/09 11:31:34 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.268 2005/05/17 21:46:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200505091
+#define CATALOG_VERSION_NO	200505171
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 13468fc5beb..a3f50ea8902 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_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/catalog/pg_proc.h,v 1.361 2005/05/09 11:31:34 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.362 2005/05/17 21:46:10 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -2999,7 +2999,7 @@ DATA(insert OID = 2078 (  set_config		PGNSP PGUID 12 f f f f v 3 25 "25 25 16" _
 DESCR("SET X as a function");
 DATA(insert OID = 2084 (  pg_show_all_settings	PGNSP PGUID 12 f f t t s 0 2249 "" _null_ _null_ _null_ show_all_settings - _null_ ));
 DESCR("SHOW ALL as a function");
-DATA(insert OID = 1371 (  pg_lock_status   PGNSP PGUID 12 f f f t v 0 2249 "" _null_ _null_ _null_ pg_lock_status - _null_ ));
+DATA(insert OID = 1371 (  pg_lock_status   PGNSP PGUID 12 f f t t v 0 2249 "" _null_ _null_ _null_ pg_lock_status - _null_ ));
 DESCR("view system lock information");
 
 DATA(insert OID = 2079 (  pg_table_is_visible		PGNSP PGUID 12 f f t f s 1 16 "26" _null_ _null_ _null_	pg_table_is_visible - _null_ ));
diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
index 5e3ca10faeb..5eccf2d44a0 100644
--- a/src/test/regress/expected/rules.out
+++ b/src/test/regress/expected/rules.out
@@ -1278,7 +1278,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
 --------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  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, t.spcname AS "tablespace", 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))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char"));
- 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_locks                 | SELECT l.locktype, l."database", l.relation, l.page, l.tuple, l."transaction", l.classid, l.objid, l.objsubid, l.pid, l."mode", l.granted FROM pg_lock_status() l(locktype text, "database" oid, relation oid, page integer, tuple smallint, "transaction" xid, classid oid, objid oid, objsubid smallint, 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, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val FROM pg_show_all_settings() a(name text, setting text, category text, short_desc text, extra_desc text, context text, vartype text, source text, min_val text, max_val 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, pg_stat_get_backend_activity_start(s.backendid) AS query_start, pg_stat_get_backend_start(s.backendid) AS backend_start, pg_stat_get_backend_client_addr(s.backendid) AS client_addr, pg_stat_get_backend_client_port(s.backendid) AS client_port 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