From 0bdf1ef3d53e42c1c050a9f30b663fe72269a129 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 5 Nov 2020 11:44:32 -0500
Subject: [PATCH] Don't throw an error for LOCK TABLE on a self-referential
 view.

LOCK TABLE has complained about "infinite recursion" when applied
to a self-referential view, ever since we made it recurse into views
in v11.  However, that breaks pg_dump's new assumption that it's
okay to lock every relation.  There doesn't seem to be any good
reason to throw an error: if we just abandon the recursion, we've
still satisfied the requirement of locking every referenced relation.

Per bug #16703 from Andrew Bille (via Alexander Lakhin).

Discussion: https://postgr.es/m/16703-e348f58aab3cf6cc@postgresql.org
---
 src/backend/commands/lockcmds.c    | 21 ++++++++++++---------
 src/test/regress/expected/lock.out |  4 +---
 src/test/regress/sql/lock.sql      |  2 +-
 3 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c
index a1ddb31f8e5..88c0e8359cb 100644
--- a/src/backend/commands/lockcmds.c
+++ b/src/backend/commands/lockcmds.c
@@ -32,7 +32,8 @@ static void LockTableRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, Oid use
 static AclResult LockTableAclCheck(Oid relid, LOCKMODE lockmode, Oid userid);
 static void RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid,
 										 Oid oldrelid, void *arg);
-static void LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views);
+static void LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait,
+							List *ancestor_views);
 
 /*
  * LOCK TABLE
@@ -217,12 +218,12 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
 				 strcmp(rte->eref->aliasname, "new") == 0))
 				continue;
 
-			/* Check infinite recursion in the view definition. */
+			/*
+			 * We might be dealing with a self-referential view.  If so, we
+			 * can just stop recursing, since we already locked it.
+			 */
 			if (list_member_oid(context->ancestor_views, relid))
-				ereport(ERROR,
-						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-						 errmsg("infinite recursion detected in rules for relation \"%s\"",
-								get_rel_name(relid))));
+				continue;
 
 			/* Check permissions with the view owner's privilege. */
 			aclresult = LockTableAclCheck(relid, context->lockmode, context->viewowner);
@@ -240,7 +241,8 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
 								get_rel_name(relid))));
 
 			if (rte->relkind == RELKIND_VIEW)
-				LockViewRecurse(relid, context->lockmode, context->nowait, context->ancestor_views);
+				LockViewRecurse(relid, context->lockmode, context->nowait,
+								context->ancestor_views);
 			else if (rte->inh)
 				LockTableRecurse(relid, context->lockmode, context->nowait, context->viewowner);
 		}
@@ -257,13 +259,14 @@ LockViewRecurse_walker(Node *node, LockViewRecurse_context *context)
 }
 
 static void
-LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait, List *ancestor_views)
+LockViewRecurse(Oid reloid, LOCKMODE lockmode, bool nowait,
+				List *ancestor_views)
 {
 	LockViewRecurse_context context;
-
 	Relation	view;
 	Query	   *viewquery;
 
+	/* caller has already locked the view */
 	view = table_open(reloid, NoLock);
 	viewquery = get_view_query(view);
 
diff --git a/src/test/regress/expected/lock.out b/src/test/regress/expected/lock.out
index 9e719abaae0..25117b5f617 100644
--- a/src/test/regress/expected/lock.out
+++ b/src/test/regress/expected/lock.out
@@ -124,16 +124,14 @@ select relname from pg_locks l, pg_class c
 (2 rows)
 
 ROLLBACK;
--- detecting infinite recursions in view definitions
+-- Verify that we cope with infinite recursion in view definitions.
 CREATE OR REPLACE VIEW lock_view2 AS SELECT * from lock_view3;
 BEGIN TRANSACTION;
 LOCK TABLE lock_view2 IN EXCLUSIVE MODE;
-ERROR:  infinite recursion detected in rules for relation "lock_view2"
 ROLLBACK;
 CREATE VIEW lock_view7 AS SELECT * from lock_view2;
 BEGIN TRANSACTION;
 LOCK TABLE lock_view7 IN EXCLUSIVE MODE;
-ERROR:  infinite recursion detected in rules for relation "lock_view2"
 ROLLBACK;
 -- Verify that we can lock a table with inheritance children.
 CREATE TABLE lock_tbl2 (b BIGINT) INHERITS (lock_tbl1);
diff --git a/src/test/regress/sql/lock.sql b/src/test/regress/sql/lock.sql
index 6cfa5865bc6..5fe18a1aff1 100644
--- a/src/test/regress/sql/lock.sql
+++ b/src/test/regress/sql/lock.sql
@@ -87,7 +87,7 @@ select relname from pg_locks l, pg_class c
  where l.relation = c.oid and relname like '%lock_%' and mode = 'ExclusiveLock'
  order by relname;
 ROLLBACK;
--- detecting infinite recursions in view definitions
+-- Verify that we cope with infinite recursion in view definitions.
 CREATE OR REPLACE VIEW lock_view2 AS SELECT * from lock_view3;
 BEGIN TRANSACTION;
 LOCK TABLE lock_view2 IN EXCLUSIVE MODE;