Subject: Re: Avoid orphaned objects dependencies, take 3

From: Robert Haas <[email-masked]>
Date: Wed, 22 May 2024 10:48:12 -0400

On Wed, May 22, 2024 at 6:21 AM Bertrand Drouvot
<[email-masked]> wrote:
> I started initially with [1] but it was focusing on function-schema only.

Yeah, that's what I thought we would want to do. And then just extend
that to the other cases.

> Then I proposed [2] making use of a dirty snapshot when recording the dependency.
> But this approach appeared to be "scary" and it was still failing to close
> some race conditions.

The current patch still seems to be using dirty snapshots for some
reason, which struck me as a bit odd. My intuition is that if we're
relying on dirty snapshots to solve problems, we likely haven't solved
the problems correctly, which seems consistent with your statement
about "failing to close some race conditions". But I don't think I
understand the situation well enough to be sure just yet.

> Then, Tom proposed another approach in [2] which is that "creation DDL will have
> to take a lock on each referenced object that'd conflict with a lock taken by DROP".
> This is the one the current patch is trying to implement.

It's a clever idea, but I'm not sure that I like it.

> I think there is 2 cases here:
>
> First case: the "conflicting" lock mode is for one of our own lock then LockCheckConflicts()
> would report this as a NON conflict.
>
> Second case: the "conflicting" lock mode is NOT for one of our own lock then LockCheckConflicts()
> would report a conflict. But I've the feeling that the existing code would
> already lock those sessions.
>
> Was your concern about "First case" or "Second case" or both?

The second case, I guess. It's bad to take a weaker lock first and
then a stronger lock on the same object later, because it can lead to
deadlocks that would have been avoided if the stronger lock had been
taken at the outset. Here, it seems like things would happen in the
other order: if we took two locks, we'd probably take the stronger
lock in the higher-level code and then the weaker lock in the
dependency code. That shouldn't break anything; it's just a bit
inefficient. My concern was really more about the maintainability of
the code. I fear that if we add code that takes heavyweight locks in
surprising places, we might later find the behavior difficult to
reason about.

Tom, what is your thought about that concern?

-- 
Robert Haas
EDB: http://www.enterprisedb.com

---

From: Bertrand Drouvot <[email-masked]>
Date: Thu, 23 May 2024 04:19:36 +0000

Hi,

> understand the situation well enough to be sure just yet.

The reason why we are using a dirty snapshot here is for the cases where we are
recording a dependency on a referenced object that we are creating at the same
time behind the scene (for example, creating a composite type while creating
a relation). Without the dirty snapshot, then the object we are creating behind
the scene (the composite type) would not be visible and we would wrongly assume
that it has been dropped.

Note that the usage of the dirty snapshot is only when the object is first
reported as "non existing" by the new ObjectByIdExist() function.

> taken at the outset.

In the example I shared up-thread that would be the opposite: the Session 1 would
take an AccessExclusiveLock lock on the object before taking an AccessShareLock
during changeDependencyFor().

> Here, it seems like things would happen in the
> dependency code.

Yeah, I agree.

> That shouldn't break anything; it's just a bit
> inefficient.

Yeah, the second lock is useless in that case (like in the example up-thread).

> My concern was really more about the maintainability of

I think I understand your concern about code maintainability but I'm not sure
that adding locks while recording a dependency is that surprising.

> Tom, what is your thought about that concern?

+1

Regards,

-- 
Bertrand Drouvot
PostgreSQL Contributors Team
RDS Open Source Databases
Amazon Web Services: https://aws.amazon.com

---

From: Robert Haas <[email-masked]>
Date: Thu, 23 May 2024 14:10:54 -0400

> that it has been dropped.

The usual reason for using a dirty snapshot is that you want to see
uncommitted work by other transactions. It sounds like you're saying
you just need to see uncommitted work by the same transaction. If
that's true, I think using HeapTupleSatisfiesSelf would be clearer. Or
maybe we just need to put CommandCounterIncrement() calls in the right
places to avoid having the problem in the first place. Or maybe this
is another sign that we're doing the work at the wrong level.

---

From: Bertrand Drouvot <[email-masked]>
Date: Thu, 23 May 2024 21:12:28 +0000

Hi,

On Thu, May 23, 2024 at 02:10:54PM -0400, Robert Haas wrote:
> you just need to see uncommitted work by the same transaction.

Right.

> If that's true, I think using HeapTupleSatisfiesSelf would be clearer.

Oh thanks! I did not know about the SNAPSHOT_SELF snapshot type (I should have
check all the snapshot types first though) and that's exactly what is needed here.

Please find attached v8 making use of SnapshotSelf instead of a dirty snapshot.

Regards,

>From 2611fb874a476c1a8d665f97d973193beb70292a Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <[email-masked]>
Date: Fri, 29 Mar 2024 15:43:26 +0000
Subject: [PATCH v8] Avoid orphaned objects dependencies

It's currently possible to create orphaned objects dependencies, for example:

Scenario 1:

session 1: begin; drop schema schem;
session 2: create a function in the schema schem
session 1: commit;

With the above, the function created in session 2 would be linked to a non
existing schema.

Scenario 2:

session 1: begin; create a function in the schema schem
session 2: drop schema schem;
session 1: commit;

With the above, the function created in session 1 would be linked to a non
existing schema.

To avoid those scenarios, a new lock (that conflicts with a lock taken by DROP)
has been put in place when the dependencies are being recorded. With this in
place, the drop schema in scenario 2 would be locked.

Also, after the new lock attempt, the patch checks that the object still exists:
with this in place session 2 in scenario 1 would be locked and would report an
error once session 1 committs (that would not be the case should session 1 abort
the transaction).

If the object is dropped before the new lock attempt is triggered then the patch
would also report an error (but with less details).

The patch adds a few tests for some dependency cases (that would currently produce
orphaned objects):

- schema and function (as the above scenarios)
- alter a dependency (function and schema)
- function and arg type
- function and return type
- function and function
- domain and domain
- table and type
- server and foreign data wrapper
---
 src/backend/catalog/dependency.c              |  59 ++++++++
 src/backend/catalog/objectaddress.c           |  66 +++++++++
 src/backend/catalog/pg_depend.c               |  12 ++
 src/backend/utils/errcodes.txt                |   1 +
 src/include/catalog/dependency.h              |   1 +
 src/include/catalog/objectaddress.h           |   1 +
 .../expected/test_dependencies_locks.out      | 129 ++++++++++++++++++
 src/test/isolation/isolation_schedule         |   1 +
 .../specs/test_dependencies_locks.spec        |  89 ++++++++++++
 9 files changed, 359 insertions(+)
  24.3% src/backend/catalog/
  45.3% src/test/isolation/expected/
  28.2% src/test/isolation/specs/

diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index d4b5b2ade1..4271ed7c5b 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1517,6 +1517,65 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
 	}
 }
 
+/*
+ * depLockAndCheckObject
+ *
+ * Lock the object that we are about to record a dependency on.
+ * After it's locked, verify that it hasn't been dropped while we
+ * weren't looking.  If the object has been dropped, this function
+ * does not return!
+ */
+void
+depLockAndCheckObject(const ObjectAddress *object)
+{
+	char	   *object_description;
+
+	/*
+	 * Those don't rely on LockDatabaseObject() when being dropped (see
+	 * AcquireDeletionLock()). Also it looks like they can not produce
+	 * orphaned dependent objects when being dropped.
+	 */
+	if (object->classId == RelationRelationId || object->classId == AuthMemRelationId)
+		return;
+
+	object_description = getObjectDescription(object, true);
+
+	/* assume we should lock the whole object not a sub-object */
+	LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock);
+
+	/* check if object still exists */
+	if (!ObjectByIdExist(object, false))
+	{
+		/*
+		 * It might be possible that we are creating it (for example creating
+		 * a composite type while creating a relation), so bypass the syscache
+		 * lookup and use a SnapshotSelf snapshot instead to cover this
+		 * scenario.
+		 */
+		if (!ObjectByIdExist(object, true))
+		{
+			/*
+			 * If the object has been dropped before we get a chance to get
+			 * its description, then emit a generic error message. That looks
+			 * like a good compromise over extra complexity.
+			 */
+			if (object_description)
+				ereport(ERROR,
+						(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+						 errmsg("%s does not exist", object_description)));
+			else
+				ereport(ERROR,
+						(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+						 errmsg("a dependent object does not exist")));
+		}
+	}
+
+	if (object_description)
+		pfree(object_description);
+
+	return;
+}
+
 /*
  * ReleaseDeletionLock - release an object deletion lock
  *
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7b536ac6fd..115cb1572e 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -2590,6 +2590,72 @@ get_object_namespace(const ObjectAddress *address)
 	return oid;
 }
 
+/*
+ * ObjectByIdExist
+ *
+ * Return whether the given object exists.
+ *
+ * Works for most catalogs, if no special processing is needed.
+ */
+bool
+ObjectByIdExist(const ObjectAddress *address, bool use_snapshot_self)
+{
+	HeapTuple	tuple;
+	int			cache = -1;
+	const ObjectPropertyType *property;
+
+	if (!use_snapshot_self)
+	{
+		property = get_object_property_data(address->classId);
+
+		cache = property->oid_catcache_id;
+	}
+
+	if (cache >= 0)
+	{
+		/* Fetch tuple from syscache. */
+		tuple = SearchSysCache1(cache, ObjectIdGetDatum(address->objectId));
+
+		if (!HeapTupleIsValid(tuple))
+		{
+			return false;
+		}
+
+		ReleaseSysCache(tuple);
+
+		return true;
+	}
+	else
+	{
+		Relation	rel;
+		ScanKeyData skey[1];
+		SysScanDesc scan;
+		Snapshot	snapshot;
+
+		if (use_snapshot_self)
+			snapshot = SnapshotSelf;
+		else
+			snapshot = NULL;
+
+		rel = table_open(address->classId, AccessShareLock);
+
+		ScanKeyInit(&skey[0],
+					get_object_attnum_oid(address->classId),
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(address->objectId));
+
+		scan = systable_beginscan(rel, get_object_oid_index(address->classId), true,
+								  snapshot, 1, skey);
+
+		/* we expect exactly one match */
+		tuple = systable_getnext(scan);
+		systable_endscan(scan);
+		table_close(rel, AccessShareLock);
+
+		return (HeapTupleIsValid(tuple));
+	}
+}
+
 /*
  * Return ObjectType for the given object type as given by
  * getObjectTypeDescription; if no valid ObjectType code exists, but it's a
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 5366f7820c..f16af28429 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -108,6 +108,12 @@ recordMultipleDependencies(const ObjectAddress *depender,
 		if (isObjectPinned(referenced))
 			continue;
 
+		/*
+		 * Acquire a lock and check object still exists while recording the
+		 * dependency. XXX - Should we do so only for DEPENDENCY_NORMAL?
+		 */
+		depLockAndCheckObject(referenced);
+
 		if (slot_init_count < max_slots)
 		{
 			slot[slot_stored_count] = MakeSingleTupleTableSlot(RelationGetDescr(dependDesc),
@@ -506,6 +512,12 @@ changeDependencyFor(Oid classId, Oid objectId,
 		return 1;
 	}
 
+	/*
+	 * Acquire a lock and check object still exists while changing the
+	 * dependency.
+	 */
+	depLockAndCheckObject(&objAddr);
+
 	depRel = table_open(DependRelationId, RowExclusiveLock);
 
 	/* There should be existing dependency record(s), so search. */
diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt
index 3250d539e1..60e8539fe3 100644
--- a/src/backend/utils/errcodes.txt
+++ b/src/backend/utils/errcodes.txt
@@ -271,6 +271,7 @@ Section: Class 28 - Invalid Authorization Specification
 Section: Class 2B - Dependent Privilege Descriptors Still Exist
 
 2B000    E    ERRCODE_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST            dependent_privilege_descriptors_still_exist
+2BP02    E    ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST                       dependent_objects_does_not_exist
 2BP01    E    ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST                          dependent_objects_still_exist
 
 Section: Class 2D - Invalid Transaction Termination
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 7eee66f810..5619b6f55e 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -101,6 +101,7 @@ typedef struct ObjectAddresses ObjectAddresses;
 /* in dependency.c */
 
 extern void AcquireDeletionLock(const ObjectAddress *object, int flags);
+extern void depLockAndCheckObject(const ObjectAddress *object);
 
 extern void ReleaseDeletionLock(const ObjectAddress *object);
 
diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h
index 3a70d80e32..53ee9ad9e6 100644
--- a/src/include/catalog/objectaddress.h
+++ b/src/include/catalog/objectaddress.h
@@ -53,6 +53,7 @@ extern void check_object_ownership(Oid roleid,
 								   Node *object, Relation relation);
 
 extern Oid	get_object_namespace(const ObjectAddress *address);
+extern bool ObjectByIdExist(const ObjectAddress *address, bool use_snapshot_self);
 
 extern bool is_objectclass_supported(Oid class_id);
 extern const char *get_object_class_descr(Oid class_id);
diff --git a/src/test/isolation/expected/test_dependencies_locks.out b/src/test/isolation/expected/test_dependencies_locks.out
new file mode 100644
index 0000000000..9b645d7aa5
--- /dev/null
+++ b/src/test/isolation/expected/test_dependencies_locks.out
@@ -0,0 +1,129 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_create_function_in_schema s2_drop_schema s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+step s2_drop_schema: DROP SCHEMA testschema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_schema: <... completed>
+ERROR:  cannot drop schema testschema because other objects depend on it
+
+starting permutation: s2_begin s2_drop_schema s1_create_function_in_schema s2_commit
+step s2_begin: BEGIN;
+step s2_drop_schema: DROP SCHEMA testschema;
+step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_in_schema: <... completed>
+ERROR:  schema testschema does not exist
+
+starting permutation: s1_begin s1_alter_function_schema s2_drop_alterschema s1_commit
+step s1_begin: BEGIN;
+step s1_alter_function_schema: ALTER FUNCTION public.falter() SET SCHEMA alterschema;
+step s2_drop_alterschema: DROP SCHEMA alterschema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_alterschema: <... completed>
+ERROR:  cannot drop schema alterschema because other objects depend on it
+
+starting permutation: s2_begin s2_drop_alterschema s1_alter_function_schema s2_commit
+step s2_begin: BEGIN;
+step s2_drop_alterschema: DROP SCHEMA alterschema;
+step s1_alter_function_schema: ALTER FUNCTION public.falter() SET SCHEMA alterschema; <waiting ...>
+step s2_commit: COMMIT;
+step s1_alter_function_schema: <... completed>
+ERROR:  schema alterschema does not exist
+
+starting permutation: s1_begin s1_create_function_with_argtype s2_drop_foo_type s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_argtype: CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql;
+step s2_drop_foo_type: DROP TYPE public.foo; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_foo_type: <... completed>
+ERROR:  cannot drop type foo because other objects depend on it
+
+starting permutation: s2_begin s2_drop_foo_type s1_create_function_with_argtype s2_commit
+step s2_begin: BEGIN;
+step s2_drop_foo_type: DROP TYPE public.foo;
+step s1_create_function_with_argtype: CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_argtype: <... completed>
+ERROR:  type foo does not exist
+
+starting permutation: s1_begin s1_create_function_with_rettype s2_drop_foo_rettype s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_rettype: CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1;
+step s2_drop_foo_rettype: DROP DOMAIN id; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_foo_rettype: <... completed>
+ERROR:  cannot drop type id because other objects depend on it
+
+starting permutation: s2_begin s2_drop_foo_rettype s1_create_function_with_rettype s2_commit
+step s2_begin: BEGIN;
+step s2_drop_foo_rettype: DROP DOMAIN id;
+step s1_create_function_with_rettype: CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_rettype: <... completed>
+ERROR:  type id does not exist
+
+starting permutation: s1_begin s1_create_function_with_function s2_drop_function_f s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_function: CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1;
+step s2_drop_function_f: DROP FUNCTION f(); <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_function_f: <... completed>
+ERROR:  cannot drop function f() because other objects depend on it
+
+starting permutation: s2_begin s2_drop_function_f s1_create_function_with_function s2_commit
+step s2_begin: BEGIN;
+step s2_drop_function_f: DROP FUNCTION f();
+step s1_create_function_with_function: CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_function: <... completed>
+ERROR:  function f() does not exist
+
+starting permutation: s1_begin s1_create_domain_with_domain s2_drop_domain_id s1_commit
+step s1_begin: BEGIN;
+step s1_create_domain_with_domain: CREATE DOMAIN idid as id;
+step s2_drop_domain_id: DROP DOMAIN id; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_domain_id: <... completed>
+ERROR:  cannot drop type id because other objects depend on it
+
+starting permutation: s2_begin s2_drop_domain_id s1_create_domain_with_domain s2_commit
+step s2_begin: BEGIN;
+step s2_drop_domain_id: DROP DOMAIN id;
+step s1_create_domain_with_domain: CREATE DOMAIN idid as id; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_domain_with_domain: <... completed>
+ERROR:  type id does not exist
+
+starting permutation: s1_begin s1_create_table_with_type s2_drop_footab_type s1_commit
+step s1_begin: BEGIN;
+step s1_create_table_with_type: CREATE TABLE tabtype(a footab);
+step s2_drop_footab_type: DROP TYPE public.footab; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_footab_type: <... completed>
+ERROR:  cannot drop type footab because other objects depend on it
+
+starting permutation: s2_begin s2_drop_footab_type s1_create_table_with_type s2_commit
+step s2_begin: BEGIN;
+step s2_drop_footab_type: DROP TYPE public.footab;
+step s1_create_table_with_type: CREATE TABLE tabtype(a footab); <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_table_with_type: <... completed>
+ERROR:  type footab does not exist
+
+starting permutation: s1_begin s1_create_server_with_fdw_wrapper s2_drop_fdw_wrapper s1_commit
+step s1_begin: BEGIN;
+step s1_create_server_with_fdw_wrapper: CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper;
+step s2_drop_fdw_wrapper: DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_fdw_wrapper: <... completed>
+ERROR:  cannot drop foreign-data wrapper fdw_wrapper because other objects depend on it
+
+starting permutation: s2_begin s2_drop_fdw_wrapper s1_create_server_with_fdw_wrapper s2_commit
+step s2_begin: BEGIN;
+step s2_drop_fdw_wrapper: DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT;
+step s1_create_server_with_fdw_wrapper: CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_server_with_fdw_wrapper: <... completed>
+ERROR:  foreign-data wrapper fdw_wrapper does not exist
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 0342eb39e4..1b67f0bffe 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -114,3 +114,4 @@ test: serializable-parallel-2
 test: serializable-parallel-3
 test: matview-write-skew
 test: lock-nowait
+test: test_dependencies_locks
diff --git a/src/test/isolation/specs/test_dependencies_locks.spec b/src/test/isolation/specs/test_dependencies_locks.spec
new file mode 100644
index 0000000000..5d04dfe9dc
--- /dev/null
+++ b/src/test/isolation/specs/test_dependencies_locks.spec
@@ -0,0 +1,89 @@
+setup
+{
+  CREATE SCHEMA testschema;
+  CREATE SCHEMA alterschema;
+  CREATE TYPE public.foo as enum ('one', 'two');
+  CREATE TYPE public.footab as enum ('three', 'four');
+  CREATE DOMAIN id AS int;
+  CREATE FUNCTION f() RETURNS int LANGUAGE SQL RETURN 1;
+  CREATE FUNCTION public.falter() RETURNS int LANGUAGE SQL RETURN 1;
+  CREATE FOREIGN DATA WRAPPER fdw_wrapper;
+}
+
+teardown
+{
+  DROP FUNCTION IF EXISTS testschema.foo();
+  DROP FUNCTION IF EXISTS fooargtype(num foo);
+  DROP FUNCTION IF EXISTS footrettype();
+  DROP FUNCTION IF EXISTS foofunc();
+  DROP FUNCTION IF EXISTS public.falter();
+  DROP FUNCTION IF EXISTS alterschema.falter();
+  DROP DOMAIN IF EXISTS idid;
+  DROP SERVER IF EXISTS srv_fdw_wrapper;
+  DROP TABLE IF EXISTS tabtype;
+  DROP SCHEMA IF EXISTS testschema;
+  DROP SCHEMA IF EXISTS alterschema;
+  DROP TYPE IF EXISTS public.foo;
+  DROP TYPE IF EXISTS public.footab;
+  DROP DOMAIN IF EXISTS id;
+  DROP FUNCTION IF EXISTS f();
+  DROP FOREIGN DATA WRAPPER IF EXISTS fdw_wrapper;
+}
+
+session "s1"
+
+step "s1_begin" { BEGIN; }
+step "s1_create_function_in_schema" { CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; }
+step "s1_create_function_with_argtype" { CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; }
+step "s1_create_function_with_rettype" { CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; }
+step "s1_create_function_with_function" { CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; }
+step "s1_alter_function_schema" { ALTER FUNCTION public.falter() SET SCHEMA alterschema; }
+step "s1_create_domain_with_domain" { CREATE DOMAIN idid as id; }
+step "s1_create_table_with_type" { CREATE TABLE tabtype(a footab); }
+step "s1_create_server_with_fdw_wrapper" { CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+
+step "s2_begin" { BEGIN; }
+step "s2_drop_schema" { DROP SCHEMA testschema; }
+step "s2_drop_alterschema" { DROP SCHEMA alterschema; }
+step "s2_drop_foo_type" { DROP TYPE public.foo; }
+step "s2_drop_foo_rettype" { DROP DOMAIN id; }
+step "s2_drop_footab_type" { DROP TYPE public.footab; }
+step "s2_drop_function_f" { DROP FUNCTION f(); }
+step "s2_drop_domain_id" { DROP DOMAIN id; }
+step "s2_drop_fdw_wrapper" { DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; }
+step "s2_commit" { COMMIT; }
+
+# function - schema
+permutation "s1_begin" "s1_create_function_in_schema" "s2_drop_schema" "s1_commit"
+permutation "s2_begin" "s2_drop_schema" "s1_create_function_in_schema" "s2_commit"
+
+# alter function - schema
+permutation "s1_begin" "s1_alter_function_schema" "s2_drop_alterschema" "s1_commit"
+permutation "s2_begin" "s2_drop_alterschema" "s1_alter_function_schema" "s2_commit"
+
+# function - argtype
+permutation "s1_begin" "s1_create_function_with_argtype" "s2_drop_foo_type" "s1_commit"
+permutation "s2_begin" "s2_drop_foo_type" "s1_create_function_with_argtype" "s2_commit"
+
+# function - rettype
+permutation "s1_begin" "s1_create_function_with_rettype" "s2_drop_foo_rettype" "s1_commit"
+permutation "s2_begin" "s2_drop_foo_rettype" "s1_create_function_with_rettype" "s2_commit"
+
+# function - function
+permutation "s1_begin" "s1_create_function_with_function" "s2_drop_function_f" "s1_commit"
+permutation "s2_begin" "s2_drop_function_f" "s1_create_function_with_function" "s2_commit"
+
+# domain - domain
+permutation "s1_begin" "s1_create_domain_with_domain" "s2_drop_domain_id" "s1_commit"
+permutation "s2_begin" "s2_drop_domain_id" "s1_create_domain_with_domain" "s2_commit"
+
+# table - type
+permutation "s1_begin" "s1_create_table_with_type" "s2_drop_footab_type" "s1_commit"
+permutation "s2_begin" "s2_drop_footab_type" "s1_create_table_with_type" "s2_commit"
+
+# server - foreign data wrapper
+permutation "s1_begin" "s1_create_server_with_fdw_wrapper" "s2_drop_fdw_wrapper" "s1_commit"
+permutation "s2_begin" "s2_drop_fdw_wrapper" "s1_create_server_with_fdw_wrapper" "s2_commit"
-- 
2.34.1

---

From: Bertrand Drouvot <[email-masked]>
Date: Thu, 6 Jun 2024 05:56:14 +0000

Hi,

> is another sign that we're doing the work at the wrong level.

Thanks for having discussed your concern with Tom last week during pgconf.dev
and shared the outcome to me. I understand your concern regarding code
maintainability with the current approach.

Please find attached v9 that:

- Acquire the lock and check for object existence at an upper level, means before
calling recordDependencyOn() and recordMultipleDependencies().

- Get rid of the SNAPSHOT_SELF snapshot usage and relies on
CommandCounterIncrement() instead to ensure new entries are visible when
we check for object existence (for the cases where we create additional object
behind the scene: like composite type while creating a relation).

- Add an assertion in recordMultipleDependencies() to ensure that we locked the
object before recording the dependency (to ensure we don't miss any cases now that
the lock is acquired at an upper level).

A few remarks:

My first attempt has been to move eliminate_duplicate_dependencies() out of
record_object_address_dependencies() so that we get the calls in this order:

eliminate_duplicate_dependencies()
depLockAndCheckObjects()
record_object_address_dependencies()

What I'm doing instead in v9 is to rename record_object_address_dependencies()
to lock_record_object_address_dependencies() and add depLockAndCheckObjects()
in it at the right place. That way the caller of
[lock_]record_object_address_dependencies() is not responsible of calling
eliminate_duplicate_dependencies() (which would have been the case with my first
attempt).

We need to setup the LOCKTAG before calling the new Assert in
recordMultipleDependencies(). So, using "#ifdef USE_ASSERT_CHECKING" here to
not setup the LOCKTAG on a non Assert enabled build.

v9 is more invasive (as it changes code in much more places) than v8 but it is
easier to follow (as it is now clear where the new lock is acquired).

Regards,

>From 6b6ee40fab0b011e9858a0a25624935c59732bfd Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <[email-masked]>
Date: Fri, 29 Mar 2024 15:43:26 +0000
Subject: [PATCH v9] Avoid orphaned objects dependencies

Scenario 1:

Scenario 2:

- schema and function (as the above scenarios)
- alter a dependency (function and schema)
- function and arg type
- function and return type
- function and function
- domain and domain
- table and type
- server and foreign data wrapper
---
 src/backend/catalog/aclchk.c                  |   1 +
 src/backend/catalog/dependency.c              |  83 +++++++++--
 src/backend/catalog/heap.c                    |   7 +-
 src/backend/catalog/index.c                   |  16 ++-
 src/backend/catalog/objectaddress.c           |  57 ++++++++
 src/backend/catalog/pg_aggregate.c            |   2 +-
 src/backend/catalog/pg_attrdef.c              |   1 +
 src/backend/catalog/pg_cast.c                 |   2 +-
 src/backend/catalog/pg_collation.c            |   1 +
 src/backend/catalog/pg_constraint.c           |  10 +-
 src/backend/catalog/pg_conversion.c           |   2 +
 src/backend/catalog/pg_depend.c               |  34 ++++-
 src/backend/catalog/pg_operator.c             |   2 +-
 src/backend/catalog/pg_proc.c                 |  12 +-
 src/backend/catalog/pg_publication.c          |   5 +
 src/backend/catalog/pg_range.c                |   3 +-
 src/backend/catalog/pg_type.c                 |  18 ++-
 src/backend/catalog/toasting.c                |   1 +
 src/backend/commands/alter.c                  |   3 +
 src/backend/commands/amcmds.c                 |   1 +
 src/backend/commands/cluster.c                |   2 +
 src/backend/commands/event_trigger.c          |   1 +
 src/backend/commands/extension.c              |   4 +-
 src/backend/commands/foreigncmds.c            |   7 +
 src/backend/commands/functioncmds.c           |   3 +-
 src/backend/commands/indexcmds.c              |   2 +
 src/backend/commands/opclasscmds.c            |  18 +++
 src/backend/commands/policy.c                 |   2 +
 src/backend/commands/proclang.c               |   2 +-
 src/backend/commands/sequence.c               |   1 +
 src/backend/commands/statscmds.c              |   3 +
 src/backend/commands/tablecmds.c              |  10 ++
 src/backend/commands/trigger.c                |  18 ++-
 src/backend/commands/tsearchcmds.c            |   8 +-
 src/backend/commands/typecmds.c               |   4 +
 src/backend/rewrite/rewriteDefine.c           |   1 +
 src/backend/utils/errcodes.txt                |   1 +
 src/include/catalog/dependency.h              |   9 +-
 src/include/catalog/objectaddress.h           |   1 +
 .../expected/test_dependencies_locks.out      | 129 ++++++++++++++++++
 src/test/isolation/isolation_schedule         |   1 +
 .../specs/test_dependencies_locks.spec        |  89 ++++++++++++
 42 files changed, 534 insertions(+), 43 deletions(-)
  34.8% src/backend/catalog/
  16.2% src/backend/commands/
  28.0% src/test/isolation/expected/
  17.4% src/test/isolation/specs/
   3.3% src/

diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 143876b77f..6b91402a9f 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -1413,6 +1413,7 @@ SetDefaultACL(InternalDefaultACL *iacls)
 				referenced.objectId = iacls->nspid;
 				referenced.objectSubId = 0;
 
+				depLockAndCheckObject(&referenced);
 				recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 			}
 		}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index d4b5b2ade1..42293220fd 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1517,6 +1517,68 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
 	}
 }
 
+/*
+ * depLockAndCheckObject
+ *
+ * Lock the object that we are about to record a dependency on.
+ * After it's locked, verify that it hasn't been dropped while we
+ * weren't looking.  If the object has been dropped, this function
+ * does not return!
+ */
+void
+depLockAndCheckObject(const ObjectAddress *object)
+{
+	char	   *object_description;
+
+	if (isObjectPinned(object))
+		return;
+
+	/*
+	 * Those don't rely on LockDatabaseObject() when being dropped (see
+	 * AcquireDeletionLock()). Also it looks like they can not produce
+	 * orphaned dependent objects when being dropped.
+	 */
+	if (object->classId == RelationRelationId || object->classId == AuthMemRelationId)
+		return;
+
+	object_description = getObjectDescription(object, true);
+
+	/* assume we should lock the whole object not a sub-object */
+	LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock);
+
+	/* check if object still exists */
+	if (!ObjectByIdExist(object))
+	{
+		if (object_description)
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+					 errmsg("%s does not exist", object_description)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+					 errmsg("a dependent object does not exist")));
+	}
+
+	if (object_description)
+		pfree(object_description);
+
+	return;
+}
+
+void
+depLockAndCheckObjects(const ObjectAddress *object, int nobject)
+{
+	int			i;
+
+	if (nobject < 0)
+		return;
+
+	for (i = 0; i < nobject; i++, object++)
+		depLockAndCheckObject(object);
+
+	return;
+}
+
 /*
  * ReleaseDeletionLock - release an object deletion lock
  *
@@ -1562,13 +1624,8 @@ recordDependencyOnExpr(const ObjectAddress *depender,
 	/* Scan the expression tree for referenceable objects */
 	find_expr_references_walker(expr, &context);
 
-	/* Remove any duplicates */
-	eliminate_duplicate_dependencies(context.addrs);
-
-	/* And record 'em */
-	recordMultipleDependencies(depender,
-							   context.addrs->refs, context.addrs->numrefs,
-							   behavior);
+	/* Record all of them (this includes duplicate elimination) */
+	lock_record_object_address_dependencies(depender, context.addrs, behavior);
 
 	free_object_addresses(context.addrs);
 }
@@ -1652,9 +1709,12 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
 
 		/* Record the self-dependencies with the appropriate direction */
 		if (!reverse_self)
+		{
+			depLockAndCheckObjects(self_addrs->refs, self_addrs->numrefs);
 			recordMultipleDependencies(depender,
 									   self_addrs->refs, self_addrs->numrefs,
 									   self_behavior);
+		}
 		else
 		{
 			/* Can't use recordMultipleDependencies, so do it the hard way */
@@ -1664,6 +1724,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
 			{
 				ObjectAddress *thisobj = self_addrs->refs + selfref;
 
+				depLockAndCheckObject(depender);
 				recordDependencyOn(thisobj, depender, self_behavior);
 			}
 		}
@@ -1672,6 +1733,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
 	}
 
 	/* Record the external dependencies */
+	depLockAndCheckObjects(context.addrs->refs, context.addrs->numrefs);
 	recordMultipleDependencies(depender,
 							   context.addrs->refs, context.addrs->numrefs,
 							   behavior);
@@ -2737,11 +2799,12 @@ stack_address_present_add_flags(const ObjectAddress *object,
  * removing any duplicates.
  */
 void
-record_object_address_dependencies(const ObjectAddress *depender,
-								   ObjectAddresses *referenced,
-								   DependencyType behavior)
+lock_record_object_address_dependencies(const ObjectAddress *depender,
+										ObjectAddresses *referenced,
+										DependencyType behavior)
 {
 	eliminate_duplicate_dependencies(referenced);
+	depLockAndCheckObjects(referenced->refs, referenced->numrefs);
 	recordMultipleDependencies(depender,
 							   referenced->refs, referenced->numrefs,
 							   behavior);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a122bbffce..113b275fe1 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -843,6 +843,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		ObjectAddressSubSet(myself, RelationRelationId, new_rel_oid, i + 1);
 		ObjectAddressSet(referenced, TypeRelationId,
 						 tupdesc->attrs[i].atttypid);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 		/* The default collation is pinned, so don't bother recording it */
@@ -851,6 +852,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		{
 			ObjectAddressSet(referenced, CollationRelationId,
 							 tupdesc->attrs[i].attcollation);
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 	}
@@ -1471,7 +1473,7 @@ heap_create_with_catalog(const char *relname,
 			add_exact_object_address(&referenced, addrs);
 		}
 
-		record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+		lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 		free_object_addresses(addrs);
 	}
 
@@ -3393,7 +3395,7 @@ StorePartitionKey(Relation rel,
 		}
 	}
 
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 
 	/*
@@ -3409,6 +3411,7 @@ StorePartitionKey(Relation rel,
 
 		ObjectAddressSubSet(referenced, RelationRelationId,
 							RelationGetRelid(rel), partattrs[i]);
+		depLockAndCheckObject(&myself);
 		recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
 	}
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 55fdde4b24..26201e7610 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1144,7 +1144,7 @@ index_create(Relation heapRelation,
 				add_exact_object_address(&referenced, addrs);
 			}
 
-			record_object_address_dependencies(&myself, addrs, DEPENDENCY_AUTO);
+			lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_AUTO);
 			free_object_addresses(addrs);
 		}
 
@@ -1157,9 +1157,11 @@ index_create(Relation heapRelation,
 		if (OidIsValid(parentIndexRelid))
 		{
 			ObjectAddressSet(referenced, RelationRelationId, parentIndexRelid);
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 
 			ObjectAddressSet(referenced, RelationRelationId, heapRelationId);
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 		}
 
@@ -1185,7 +1187,7 @@ index_create(Relation heapRelation,
 			add_exact_object_address(&referenced, addrs);
 		}
 
-		record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+		lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 		free_object_addresses(addrs);
 
 		/* Store dependencies on anything mentioned in index expressions */
@@ -1987,6 +1989,14 @@ index_constraint_create(Relation heapRelation,
 	 */
 	ObjectAddressSet(myself, ConstraintRelationId, conOid);
 	ObjectAddressSet(idxaddr, RelationRelationId, indexRelationId);
+
+	/*
+	 * CommandCounterIncrement here to ensure the new constraint entry is
+	 * visible when we'll check of object existence when recording the
+	 * dependencies.
+	 */
+	CommandCounterIncrement();
+	depLockAndCheckObject(&myself);
 	recordDependencyOn(&idxaddr, &myself, DEPENDENCY_INTERNAL);
 
 	/*
@@ -1998,9 +2008,11 @@ index_constraint_create(Relation heapRelation,
 		ObjectAddress referenced;
 
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 		ObjectAddressSet(referenced, RelationRelationId,
 						 RelationGetRelid(heapRelation));
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 	}
 
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 7b536ac6fd..6d7abd3738 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -2590,6 +2590,63 @@ get_object_namespace(const ObjectAddress *address)
 	return oid;
 }
 
+/*
+ * ObjectByIdExist
+ *
+ * Return whether the given object exists.
+ *
+ * Works for most catalogs, if no special processing is needed.
+ */
+bool
+ObjectByIdExist(const ObjectAddress *address)
+{
+	HeapTuple	tuple;
+	int			cache;
+	const ObjectPropertyType *property;
+
+	property = get_object_property_data(address->classId);
+
+	cache = property->oid_catcache_id;
+
+	if (cache >= 0)
+	{
+		/* Fetch tuple from syscache. */
+		tuple = SearchSysCache1(cache, ObjectIdGetDatum(address->objectId));
+
+		if (!HeapTupleIsValid(tuple))
+		{
+			return false;
+		}
+
+		ReleaseSysCache(tuple);
+
+		return true;
+	}
+	else
+	{
+		Relation	rel;
+		ScanKeyData skey[1];
+		SysScanDesc scan;
+
+		rel = table_open(address->classId, AccessShareLock);
+
+		ScanKeyInit(&skey[0],
+					get_object_attnum_oid(address->classId),
+					BTEqualStrategyNumber, F_OIDEQ,
+					ObjectIdGetDatum(address->objectId));
+
+		scan = systable_beginscan(rel, get_object_oid_index(address->classId), true,
+								  NULL, 1, skey);
+
+		/* we expect exactly one match */
+		tuple = systable_getnext(scan);
+		systable_endscan(scan);
+		table_close(rel, AccessShareLock);
+
+		return (HeapTupleIsValid(tuple));
+	}
+}
+
 /*
  * Return ObjectType for the given object type as given by
  * getObjectTypeDescription; if no valid ObjectType code exists, but it's a
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index 90fc7db949..9b5c8c8fb0 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -805,7 +805,7 @@ AggregateCreate(const char *aggName,
 		add_exact_object_address(&referenced, addrs);
 	}
 
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 	return myself;
 }
diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c
index 003ae70b4d..47b7fe75ad 100644
--- a/src/backend/catalog/pg_attrdef.c
+++ b/src/backend/catalog/pg_attrdef.c
@@ -178,6 +178,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	colobject.objectId = RelationGetRelid(rel);
 	colobject.objectSubId = attnum;
 
+	depLockAndCheckObject(&colobject);
 	recordDependencyOn(&defobject, &colobject,
 					   attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
 
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
index 5a5b855d51..2593bb3634 100644
--- a/src/backend/catalog/pg_cast.c
+++ b/src/backend/catalog/pg_cast.c
@@ -121,7 +121,7 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
 		add_exact_object_address(&referenced, addrs);
 	}
 
-	record_object_address_dependencies(&myself, addrs, behavior);
+	lock_record_object_address_dependencies(&myself, addrs, behavior);
 	free_object_addresses(addrs);
 
 	/* dependency on extension */
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index 7f2f701229..43ceb426d4 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -218,6 +218,7 @@ CollationCreate(const char *collname, Oid collnamespace,
 	referenced.classId = NamespaceRelationId;
 	referenced.objectId = collnamespace;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* create dependency on owner */
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 3baf9231ed..91af823ee5 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -277,8 +277,8 @@ CreateConstraintEntry(const char *constraintName,
 		add_exact_object_address(&domobject, addrs_auto);
 	}
 
-	record_object_address_dependencies(&conobject, addrs_auto,
-									   DEPENDENCY_AUTO);
+	lock_record_object_address_dependencies(&conobject, addrs_auto,
+											DEPENDENCY_AUTO);
 	free_object_addresses(addrs_auto);
 
 	/* Handle set of normal dependencies */
@@ -352,8 +352,8 @@ CreateConstraintEntry(const char *constraintName,
 		}
 	}
 
-	record_object_address_dependencies(&conobject, addrs_normal,
-									   DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&conobject, addrs_normal,
+											DEPENDENCY_NORMAL);
 	free_object_addresses(addrs_normal);
 
 	/*
@@ -858,9 +858,11 @@ ConstraintSetParentConstraint(Oid childConstrId,
 		ObjectAddressSet(depender, ConstraintRelationId, childConstrId);
 
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
 
 		ObjectAddressSet(referenced, RelationRelationId, childTableId);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
 	}
 	else
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 0770878eac..479dd528eb 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -116,12 +116,14 @@ ConversionCreate(const char *conname, Oid connamespace,
 	referenced.classId = ProcedureRelationId;
 	referenced.objectId = conproc;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* create dependency on namespace */
 	referenced.classId = NamespaceRelationId;
 	referenced.objectId = connamespace;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* create dependency on owner */
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index cfd7ef51df..47968ee8c8 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -20,21 +20,20 @@
 #include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
+#include "catalog/pg_auth_members.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
 #include "catalog/pg_extension.h"
 #include "catalog/partition.h"
 #include "commands/extension.h"
 #include "miscadmin.h"
+#include "storage/lock.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 #include "utils/rel.h"

-static bool isObjectPinned(const ObjectAddress *object);
-
-
 /*
  * Record a dependency between 2 objects via their respective objectAddress.
  * The first argument is the dependent object, the second the one it
@@ -100,6 +99,25 @@ recordMultipleDependencies(const ObjectAddress *depender,
 	slot_init_count = 0;
 	for (i = 0; i < nreferenced; i++, referenced++)
 	{
+#ifdef USE_ASSERT_CHECKING
+		LOCKTAG		tag;
+
+		SET_LOCKTAG_OBJECT(tag,
+						   MyDatabaseId,
+						   referenced->classId,
+						   referenced->objectId,
+						   0);
+
+		/*
+		 * Assert the referenced object is locked (see
+		 * depLockAndCheckObject()) when recording the dependency.
+		 */
+		Assert(isObjectPinned(referenced) ||
+			   referenced->classId == RelationRelationId ||
+			   referenced->classId == AuthMemRelationId ||
+			   LockHeldByMe(&tag, AccessShareLock));
+#endif
+
 		/*
 		 * If the referenced object is pinned by the system, there's no real
 		 * need to record dependencies on it.  This saves lots of space in
@@ -239,6 +257,7 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
 		extension.objectId = CurrentExtensionObject;
 		extension.objectSubId = 0;
 
+		depLockAndCheckObject(&extension);
 		recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
 	}
 }
@@ -501,11 +520,18 @@ changeDependencyFor(Oid classId, Oid objectId,
 		depAddr.classId = classId;
 		depAddr.objectId = objectId;
 		depAddr.objectSubId = 0;
+		depLockAndCheckObject(&objAddr);
 		recordDependencyOn(&depAddr, &objAddr, DEPENDENCY_NORMAL);
 
 		return 1;
 	}
 
+	/*
 
 	/* There should be existing dependency record(s), so search. */
@@ -706,7 +732,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
  * The passed subId, if any, is ignored; we assume that only whole objects
  * are pinned (and that this implies pinning their components).
  */
-static bool
+bool
 isObjectPinned(const ObjectAddress *object)
 {
 	return IsPinnedObject(object->classId, object->objectId);
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 65b45a424a..7e40471baf 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -930,7 +930,7 @@ makeOperatorDependencies(HeapTuple tuple,
 		add_exact_object_address(&referenced, addrs);
 	}
 
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 
 	/* Dependency on owner */
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 528c17cd7f..aa36a07648 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -593,6 +593,13 @@ ProcedureCreate(const char *procedureName,
 	if (is_update)
 		deleteDependencyRecordsFor(ProcedureRelationId, retval, true);
 
+	/*
+	 * CommandCounterIncrement here to ensure the new function entry is
+	 * visible when we'll check of object existence when recording the
+	 * dependencies.
+	 */
+	CommandCounterIncrement();
+
 	addrs = new_object_addresses();
 
 	ObjectAddressSet(myself, ProcedureRelationId, retval);
@@ -637,7 +644,7 @@ ProcedureCreate(const char *procedureName,
 		add_exact_object_address(&referenced, addrs);
 	}
 
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 
 	/* dependency on SQL routine body */
@@ -674,9 +681,6 @@ ProcedureCreate(const char *procedureName,
 		ArrayType  *set_items = NULL;
 		int			save_nestlevel = 0;
 
-		/* Advance command counter so new tuple can be seen by validator */
-		CommandCounterIncrement();
-
 		/*
 		 * Set per-function configuration parameters so that the validation is
 		 * done with the environment the function expects.  However, if
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 0602398a54..6654b65f41 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -438,10 +438,12 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 
 	/* Add dependency on the publication */
 	ObjectAddressSet(referenced, PublicationRelationId, pubid);
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Add dependency on the relation */
 	ObjectAddressSet(referenced, RelationRelationId, relid);
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Add dependency on the objects mentioned in the qualifications */
@@ -454,6 +456,7 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 	for (int i = 0; i < natts; i++)
 	{
 		ObjectAddressSubSet(referenced, RelationRelationId, relid, attarray[i]);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -661,10 +664,12 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)
 
 	/* Add dependency on the publication */
 	ObjectAddressSet(referenced, PublicationRelationId, pubid);
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Add dependency on the schema */
 	ObjectAddressSet(referenced, NamespaceRelationId, schemaid);
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Close the table */
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index 501a6ba410..a2f343a81a 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -92,13 +92,14 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
 		add_exact_object_address(&referenced, addrs);
 	}
 
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 
 	/* record multirange type's dependency on the range type */
 	referencing.classId = TypeRelationId;
 	referencing.objectId = multirangeTypeOid;
 	referencing.objectSubId = 0;
+	depLockAndCheckObject(&myself);
 	recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
 
 	table_close(pg_range, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 395dec8ed8..91139e275f 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -494,6 +494,14 @@ TypeCreate(Oid newTypeOid,
 	 * Create dependencies.  We can/must skip this in bootstrap mode.
 	 */
 	if (!IsBootstrapProcessingMode())
+	{
+		/*
+		 * CommandCounterIncrement here to ensure the new type  entry is
+		 * visible when we'll check of object existence when recording the
+		 * dependencies.
+		 */
+		CommandCounterIncrement();
+
 		GenerateTypeDependencies(tup,
 								 pg_type_desc,
 								 (defaultTypeBin ?
@@ -505,6 +513,7 @@ TypeCreate(Oid newTypeOid,
 								 isDependentType,
 								 true,	/* make extension dependency */
 								 rebuildDeps);
+	}
 
 	/* Post creation hook for new type */
 	InvokeObjectPostCreateHook(TypeRelationId, typeObjectId, 0);
@@ -703,7 +712,7 @@ GenerateTypeDependencies(HeapTuple typeTuple,
 		add_exact_object_address(&referenced, addrs_normal);
 	}
 
-	record_object_address_dependencies(&myself, addrs_normal, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs_normal, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs_normal);
 
 	/* Normal dependency on the default expression. */
@@ -724,9 +733,15 @@ GenerateTypeDependencies(HeapTuple typeTuple,
 		ObjectAddressSet(referenced, RelationRelationId, typeForm->typrelid);
 
 		if (relationKind != RELKIND_COMPOSITE_TYPE)
+		{
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+		}
 		else
+		{
+			depLockAndCheckObject(&myself);
 			recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
+		}
 	}
 
 	/*
@@ -737,6 +752,7 @@ GenerateTypeDependencies(HeapTuple typeTuple,
 	if (OidIsValid(typeForm->typelem))
 	{
 		ObjectAddressSet(referenced, TypeRelationId, typeForm->typelem);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced,
 						   isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL);
 	}
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 738bc46ae8..a471833efe 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -367,6 +367,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 		toastobject.objectId = toast_relid;
 		toastobject.objectSubId = 0;
 
+		depLockAndCheckObject(&baseobject);
 		recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
 	}
 
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f99ebb447..e3ad5db529 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -501,7 +501,10 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre
 		currexts = getAutoExtensionsOfObject(address.classId,
 											 address.objectId);
 		if (!list_member_oid(currexts, refAddr.objectId))
+		{
+			depLockAndCheckObject(&refAddr);
 			recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
+		}
 	}
 
 	return address;
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index aaa0f9a1dc..0b4e44131a 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -104,6 +104,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	referenced.objectId = amhandler;
 	referenced.objectSubId = 0;
 
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	recordDependencyOnCurrentExtension(&myself, false);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 78f96789b0..73bcd68a1f 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -1381,6 +1381,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 			{
 				baseobject.objectId = r1;
 				toastobject.objectId = relform1->reltoastrelid;
+				depLockAndCheckObject(&baseobject);
 				recordDependencyOn(&toastobject, &baseobject,
 								   DEPENDENCY_INTERNAL);
 			}
@@ -1389,6 +1390,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 			{
 				baseobject.objectId = r2;
 				toastobject.objectId = relform2->reltoastrelid;
+				depLockAndCheckObject(&baseobject);
 				recordDependencyOn(&toastobject, &baseobject,
 								   DEPENDENCY_INTERNAL);
 			}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 7a5ed6b985..ddcba91c92 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -327,6 +327,7 @@ insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtO
 	referenced.classId = ProcedureRelationId;
 	referenced.objectId = funcoid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* Depend on extension, if any. */
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 1643c8c69a..fdcffcea01 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1935,7 +1935,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 	}
 
 	/* Record all of them (this includes duplicate elimination) */
-	record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, refobjs, DEPENDENCY_NORMAL);
 	free_object_addresses(refobjs);
 
 	/* Post creation hook for new extension */
@@ -3258,6 +3258,7 @@ ApplyExtensionUpdates(Oid extensionOid,
 			otherext.objectId = reqext;
 			otherext.objectSubId = 0;
 
+			depLockAndCheckObject(&otherext);
 			recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
 		}
 
@@ -3414,6 +3415,7 @@ ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt,
 		/*
 		 * OK, add the dependency.
 		 */
+		depLockAndCheckObject(&extension);
 		recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
 
 		/*
diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c
index cf61bbac1f..5e98f3d0d6 100644
--- a/src/backend/commands/foreigncmds.c
+++ b/src/backend/commands/foreigncmds.c
@@ -642,6 +642,7 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
 		referenced.classId = ProcedureRelationId;
 		referenced.objectId = fdwhandler;
 		referenced.objectSubId = 0;
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -650,6 +651,7 @@ CreateForeignDataWrapper(ParseState *pstate, CreateFdwStmt *stmt)
 		referenced.classId = ProcedureRelationId;
 		referenced.objectId = fdwvalidator;
 		referenced.objectSubId = 0;
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -811,6 +813,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
 			referenced.classId = ProcedureRelationId;
 			referenced.objectId = fdwhandler;
 			referenced.objectSubId = 0;
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 
@@ -819,6 +822,7 @@ AlterForeignDataWrapper(ParseState *pstate, AlterFdwStmt *stmt)
 			referenced.classId = ProcedureRelationId;
 			referenced.objectId = fdwvalidator;
 			referenced.objectSubId = 0;
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 	}
@@ -951,6 +955,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt)
 	referenced.classId = ForeignDataWrapperRelationId;
 	referenced.objectId = fdw->fdwid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId);
@@ -1195,6 +1200,7 @@ CreateUserMapping(CreateUserMappingStmt *stmt)
 	referenced.classId = ForeignServerRelationId;
 	referenced.objectId = srv->serverid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	if (OidIsValid(useId))
@@ -1472,6 +1478,7 @@ CreateForeignTable(CreateForeignTableStmt *stmt, Oid relid)
 	referenced.classId = ForeignServerRelationId;
 	referenced.objectId = server->serverid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	table_close(ftrel, RowExclusiveLock);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 6593fd7d81..2a378a53d8 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1459,6 +1459,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 			referenced.classId = ProcedureRelationId;
 			referenced.objectId = newsupport;
 			referenced.objectSubId = 0;
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_NORMAL);
 		}
 
@@ -1979,7 +1980,7 @@ CreateTransform(CreateTransformStmt *stmt)
 		add_exact_object_address(&referenced, addrs);
 	}
 
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 
 	/* dependency on extension */
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 309389e20d..487708d885 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -4377,8 +4377,10 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
 			ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
 			ObjectAddressSet(partitionTbl, RelationRelationId,
 							 partitionIdx->rd_index->indrelid);
+			depLockAndCheckObject(&parentIdx);
 			recordDependencyOn(&partIdx, &parentIdx,
 							   DEPENDENCY_PARTITION_PRI);
+			depLockAndCheckObject(&partitionTbl);
 			recordDependencyOn(&partIdx, &partitionTbl,
 							   DEPENDENCY_PARTITION_SEC);
 		}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index b8b5c147c5..e7b1cb911e 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -298,12 +298,14 @@ CreateOpFamily(CreateOpFamilyStmt *stmt, const char *opfname,
 	referenced.classId = AccessMethodRelationId;
 	referenced.objectId = amoid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* dependency on namespace */
 	referenced.classId = NamespaceRelationId;
 	referenced.objectId = namespaceoid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* dependency on owner */
@@ -725,18 +727,21 @@ DefineOpClass(CreateOpClassStmt *stmt)
 	referenced.classId = NamespaceRelationId;
 	referenced.objectId = namespaceoid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* dependency on opfamily */
 	referenced.classId = OperatorFamilyRelationId;
 	referenced.objectId = opfamilyoid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* dependency on indexed datatype */
 	referenced.classId = TypeRelationId;
 	referenced.objectId = typeoid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* dependency on storage datatype */
@@ -745,6 +750,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
 		referenced.classId = TypeRelationId;
 		referenced.objectId = storageoid;
 		referenced.objectSubId = 0;
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -1486,6 +1492,13 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 
 		heap_freetuple(tup);
 
+		/*
+		 * CommandCounterIncrement here to ensure the new operator entry is
+		 * visible when we'll check of object existence when recording the
+		 * dependencies.
+		 */
+		CommandCounterIncrement();
+
 		/* Make its dependencies */
 		myself.classId = AccessMethodOperatorRelationId;
 		myself.objectId = entryoid;
@@ -1496,6 +1509,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 		referenced.objectSubId = 0;
 
 		/* see comments in amapi.h about dependency strength */
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced,
 						   op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
 
@@ -1504,6 +1518,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 		referenced.objectId = op->refobjid;
 		referenced.objectSubId = 0;
 
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced,
 						   op->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
 
@@ -1514,6 +1529,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 			referenced.objectId = op->sortfamily;
 			referenced.objectSubId = 0;
 
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced,
 							   op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
 		}
@@ -1597,6 +1613,7 @@ storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 		referenced.objectSubId = 0;
 
 		/* see comments in amapi.h about dependency strength */
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced,
 						   proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);
 
@@ -1605,6 +1622,7 @@ storeProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 		referenced.objectId = proc->refobjid;
 		referenced.objectSubId = 0;
 
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced,
 						   proc->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
 
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 6ff3eba824..5a50ad73fb 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -722,6 +722,7 @@ CreatePolicy(CreatePolicyStmt *stmt)
 	myself.objectId = policy_id;
 	myself.objectSubId = 0;
 
+	depLockAndCheckObject(&target);
 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
 
 	recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
@@ -1053,6 +1054,7 @@ AlterPolicy(AlterPolicyStmt *stmt)
 	myself.objectId = policy_id;
 	myself.objectSubId = 0;
 
+	depLockAndCheckObject(&target);
 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
 
 	recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 881f90017e..679155162e 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -205,7 +205,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 		add_exact_object_address(&referenced, addrs);
 	}
 
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 
 	/* Post creation hook for new procedural language */
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 28f8522264..0d37576d22 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1681,6 +1681,7 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
 		depobject.classId = RelationRelationId;
 		depobject.objectId = RelationGetRelid(seqrel);
 		depobject.objectSubId = 0;
+		depLockAndCheckObject(&refobject);
 		recordDependencyOn(&depobject, &refobject, deptype);
 	}
 
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 1db3ef69d2..455ec046f8 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -536,6 +536,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	for (i = 0; i < nattnums; i++)
 	{
 		ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
+		depLockAndCheckObject(&parentobject);
 		recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
 	}
 
@@ -553,6 +554,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	if (!nattnums)
 	{
 		ObjectAddressSet(parentobject, RelationRelationId, relid);
+		depLockAndCheckObject(&parentobject);
 		recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
 	}
 
@@ -573,6 +575,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	 * than the underlying table(s).
 	 */
 	ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId);
+	depLockAndCheckObject(&parentobject);
 	recordDependencyOn(&myself, &parentobject, DEPENDENCY_NORMAL);
 
 	recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7b6c69b7a5..73696a4210 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3437,6 +3437,7 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid,
 	childobject.objectId = relationId;
 	childobject.objectSubId = 0;
 
+	depLockAndCheckObject(&parentobject);
 	recordDependencyOn(&childobject, &parentobject,
 					   child_dependency_type(child_is_partition));
 
@@ -7440,6 +7441,7 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
 	referenced.classId = TypeRelationId;
 	referenced.objectId = typid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 }
 
@@ -7461,6 +7463,7 @@ add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
 		referenced.classId = CollationRelationId;
 		referenced.objectId = collid;
 		referenced.objectSubId = 0;
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 }
@@ -10140,6 +10143,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
 		ObjectAddress referenced;
 
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
 	}
 
@@ -10431,8 +10435,10 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
 			 */
 			ObjectAddressSet(address, ConstraintRelationId, constrOid);
 			ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
 			ObjectAddressSet(referenced, RelationRelationId, partitionId);
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
 
 			/* Make all this visible before recursing */
@@ -10933,9 +10939,11 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
 		/* Set up partition dependencies for the new constraint */
 		ObjectAddressSet(address, ConstraintRelationId, constrOid);
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
 		ObjectAddressSet(referenced, RelationRelationId,
 						 RelationGetRelid(partRel));
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
 
 		/* Done with the cloned constraint's tuple */
@@ -14782,6 +14790,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 		 */
 		ObjectAddressSet(relobj, RelationRelationId, reloid);
 		ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
 	}
 	else if (OidIsValid(oldAccessMethodId) &&
@@ -16400,6 +16409,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	typeobj.classId = TypeRelationId;
 	typeobj.objectId = typeid;
 	typeobj.objectSubId = 0;
+	depLockAndCheckObject(&typeobj);
 	recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
 
 	/* Update pg_class.reloftype */
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 95de402fa6..c0e7ddff1c 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1018,8 +1018,6 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;
 
 		CatalogTupleUpdate(pgrel, &tuple->t_self, tuple);
-
-		CommandCounterIncrement();
 	}
 	else
 		CacheInvalidateRelcacheByTuple(tuple);
@@ -1027,6 +1025,12 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	heap_freetuple(tuple);
 	table_close(pgrel, RowExclusiveLock);
 
+	/*
+	 * CommandCounterIncrement here to ensure the new trigger entry is visible
+	 * when we'll check of object existence when recording the dependencies.
+	 */
+	CommandCounterIncrement();
+
 	/*
 	 * If we're replacing a trigger, flush all the old dependencies before
 	 * recording new ones.
@@ -1045,6 +1049,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	referenced.classId = ProcedureRelationId;
 	referenced.objectId = funcoid;
 	referenced.objectSubId = 0;
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	if (isInternal && OidIsValid(constraintOid))
@@ -1058,6 +1063,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		referenced.classId = ConstraintRelationId;
 		referenced.objectId = constraintOid;
 		referenced.objectSubId = 0;
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	}
 	else
@@ -1070,6 +1076,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		referenced.classId = RelationRelationId;
 		referenced.objectId = RelationGetRelid(rel);
 		referenced.objectSubId = 0;
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 		if (OidIsValid(constrrelid))
@@ -1077,6 +1084,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 			referenced.classId = RelationRelationId;
 			referenced.objectId = constrrelid;
 			referenced.objectSubId = 0;
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 		}
 		/* Not possible to have an index dependency in this case */
@@ -1091,6 +1099,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 			referenced.classId = ConstraintRelationId;
 			referenced.objectId = constraintOid;
 			referenced.objectSubId = 0;
+			depLockAndCheckObject(&myself);
 			recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
 		}
 
@@ -1100,8 +1109,10 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		if (OidIsValid(parentTriggerOid))
 		{
 			ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid);
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 			ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 		}
 	}
@@ -1116,6 +1127,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		for (i = 0; i < ncolumns; i++)
 		{
 			referenced.objectSubId = columns[i];
+			depLockAndCheckObject(&referenced);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 	}
@@ -1255,9 +1267,11 @@ TriggerSetParentTrigger(Relation trigRel,
 		ObjectAddressSet(depender, TriggerRelationId, childTrigId);
 
 		ObjectAddressSet(referenced, TriggerRelationId, parentTrigId);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
 
 		ObjectAddressSet(referenced, RelationRelationId, childTableId);
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
 	}
 	else
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index b7b5019f1e..a62c997725 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -171,7 +171,7 @@ makeParserDependencies(HeapTuple tuple)
 		add_exact_object_address(&referenced, addrs);
 	}
 
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 
 	return myself;
@@ -329,7 +329,7 @@ makeDictionaryDependencies(HeapTuple tuple)
 	ObjectAddressSet(referenced, TSTemplateRelationId, dict->dicttemplate);
 	add_exact_object_address(&referenced, addrs);
 
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 
 	return myself;
@@ -677,7 +677,7 @@ makeTSTemplateDependencies(HeapTuple tuple)
 		add_exact_object_address(&referenced, addrs);
 	}
 
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 	free_object_addresses(addrs);
 
 	return myself;
@@ -885,7 +885,7 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
 	}
 
 	/* Record 'em (this includes duplicate elimination) */
-	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
+	lock_record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
 
 	free_object_addresses(addrs);
 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 2a1e713335..cbb61c1207 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1794,6 +1794,7 @@ makeRangeConstructors(const char *name, Oid namespace,
 		 * that they go away silently when the type is dropped.  Note that
 		 * pg_dump depends on this choice to avoid dumping the constructors.
 		 */
+		depLockAndCheckObject(&referenced);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	}
 }
@@ -1859,6 +1860,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
 	 * that they go away silently when the type is dropped.  Note that pg_dump
 	 * depends on this choice to avoid dumping the constructors.
 	 */
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	pfree(argtypes);
 
@@ -1898,6 +1900,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
 							 1.0,	/* procost */
 							 0.0);	/* prorows */
 	/* ditto */
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	pfree(argtypes);
 	*castFuncOid = myself.objectId;
@@ -1936,6 +1939,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
 							 1.0,	/* procost */
 							 0.0);	/* prorows */
 	/* ditto */
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	pfree(argtypes);
 	pfree(allParameterTypes);
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 6cc9a8d8bf..53308dceb6 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -155,6 +155,7 @@ InsertRule(const char *rulname,
 	referenced.objectId = eventrel_oid;
 	referenced.objectSubId = 0;
 
+	depLockAndCheckObject(&referenced);
 	recordDependencyOn(&myself, &referenced,
 					   (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);

 
 Section: Class 2D - Invalid Transaction Termination
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 7eee66f810..f404a92fb4 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -101,6 +101,8 @@ typedef struct ObjectAddresses ObjectAddresses;
 /* in dependency.c */
 
 extern void AcquireDeletionLock(const ObjectAddress *object, int flags);
+extern void depLockAndCheckObject(const ObjectAddress *object);
+extern void depLockAndCheckObjects(const ObjectAddress *object, int nobject);

@@ -128,9 +130,9 @@ extern void add_exact_object_address(const ObjectAddress *object,
 extern bool object_address_present(const ObjectAddress *object,
 								   const ObjectAddresses *addrs);
 
-extern void record_object_address_dependencies(const ObjectAddress *depender,
-											   ObjectAddresses *referenced,
-											   DependencyType behavior);
+extern void lock_record_object_address_dependencies(const ObjectAddress *depender,
+													ObjectAddresses *referenced,
+													DependencyType behavior);
 
 extern void sort_object_addresses(ObjectAddresses *addrs);
 
@@ -172,6 +174,7 @@ extern long changeDependenciesOf(Oid classId, Oid oldObjectId,
 extern long changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
 								 Oid newRefObjectId);
 
+extern bool isObjectPinned(const ObjectAddress *object);
 extern Oid	getExtensionOfObject(Oid classId, Oid objectId);
 extern List *getAutoExtensionsOfObject(Oid classId, Oid objectId);
 
index 3a70d80e32..56f746264b 100644
 
 extern Oid	get_object_namespace(const ObjectAddress *address);
+extern bool ObjectByIdExist(const ObjectAddress *address);

---

From: Robert Haas <[email-masked]>
Date: Thu, 6 Jun 2024 16:00:23 -0400

> easier to follow (as it is now clear where the new lock is acquired).

Hmm, this definitely isn't what I had in mind. Possibly that's a sign
that what I had in mind was dumb, but for sure it's not what I
imagined. What I thought you were going to do was add calls like
LockDatabaseObject(NamespaceRelationId, schemaid, 0, AccessShareLock)
in various places, or perhaps LockRelationOid(reloid,
AccessShareLock), or whatever the case may be. Here you've got stuff
like this:

...which to me looks like the locking is still pushed down inside the
dependency code.

And you also have stuff like this:

But in depLockAndCheckObject you have:

+ if (object->classId == RelationRelationId || object->classId ==
AuthMemRelationId)
+ return;

That doesn't seem right, because then it seems like the call isn't
doing anything, but there isn't really any reason for it to not be
doing anything. If we're dropping a dependency on a table, then it
seems like we need to have a lock on that table. Presumably the reason
why we don't end up with dangling dependencies in such cases now is
because we're careful about doing LockRelation() in the right places,
but we're not similarly careful about other operations e.g.
ConstraintSetParentConstraint is called by DefineIndex which calls
table_open(childRelId, ...) first, but there's no logic in DefineIndex
to lock the constraint.

Thoughts?

---

From: Bertrand Drouvot <[email-masked]>
Date: Fri, 7 Jun 2024 08:41:36 +0000

Hi,

On Thu, Jun 06, 2024 at 04:00:23PM -0400, Robert Haas wrote:
> AccessShareLock), or whatever the case may be.

I see what you’re saying, doing things like:

LockDatabaseObject(TypeRelationId, returnType, 0, AccessShareLock);
in ProcedureCreate() for example.

> Here you've got stuff

Yes but it’s now located in places where, I think, it’s easier to understand
what’s going on (as compare to v8), except maybe for:

recordDependencyOnExpr()
makeOperatorDependencies()
GenerateTypeDependencies()
makeParserDependencies()
makeDictionaryDependencies()
makeTSTemplateDependencies()
makeConfigurationDependencies()

but probably for:

heap_create_with_catalog()
StorePartitionKey()
index_create()
AggregateCreate()
CastCreate()
CreateConstraintEntry()
ProcedureCreate()
RangeCreate()
InsertExtensionTuple()
CreateTransform()
CreateProceduralLanguage()

The reasons I keep it linked to the dependency code are:

- To ensure we don’t miss anything (well, with the new Assert in place that’s
probably a tangential argument)

- It’s not only about locking the object: it’s also about 1) verifying the object
is pinned, 2) checking it still exists and 3) provide a description in the error
message if we can (in case the object does not exist anymore). Relying on an
already build object (in the dependency code) avoid to 1) define the object(s)
one more time or 2) create new functions that would do the same as isObjectPinned()
and getObjectDescription() with a different set of arguments.

That may sounds like weak arguments but it has been my reasoning.

Do you still find the code hard to maintain with v9?

> because we're careful about doing LockRelation() in the right places,

Yeah, that's what I think: we're already careful when we deal with relations.

> table_open(childRelId, ...) first, but there's no logic in DefineIndex

table_open(childRelId, ...) would lock any "ALTER TABLE <childRelId> DROP CONSTRAINT"
already. Not sure I understand your concern here.

Regards,

---

From: Robert Haas <[email-masked]>
Date: Thu, 13 Jun 2024 10:49:34 -0400

> Do you still find the code hard to maintain with v9?

I don't think it substantially changes my concerns as compared with
the earlier version.

> already. Not sure I understand your concern here.

I believe this is not true. This would take a lock on the table, not
the constraint itself.

---

From: Bertrand Drouvot <[email-masked]>
Date: Thu, 13 Jun 2024 16:52:09 +0000

Hi,

> I don't think it substantially changes my concerns as compared with

Thanks for the feedback, I'll give it more thoughts.

> I believe this is not true. This would take a lock on the table, not

I agree that it would not lock the constraint itself. What I meant to say is that
, nevertheless, the constraint can not be dropped. Indeed, the "ALTER TABLE"
necessary to drop the constraint (ALTER TABLE <childRelId> DROP CONSTRAINT) would
be locked by the table_open(childRelId, ...).

That's why I don't understand your concern with this particular example. But
anyway, I'll double check your related concern:

in depLockAndCheckObject(). 

Regards,

---

From: Robert Haas <[email-masked]>
Date: Thu, 13 Jun 2024 14:27:45 -0400

> be locked by the table_open(childRelId, ...).

Ah, right. So, I was assuming that, with either this version of your
patch or the earlier version, we'd end up locking the constraint
itself. Was I wrong about that?

---

From: Bertrand Drouvot <[email-masked]>
Date: Fri, 14 Jun 2024 07:54:52 +0000

Hi,

> itself. Was I wrong about that?

The child contraint itself is not locked when going through
ConstraintSetParentConstraint().

While at it, let's look at a full example and focus on your concern.

Let's do that with this gdb file:

"
$ cat gdb.txt
b dependency.c:1542
command 1
        printf "Will return for: classId %d and objectId %d\n", object->classId, object->objectId
        c
end
b dependency.c:1547 if object->classId == 2606
command 2
        printf "Will lock constraint: classId %d and objectId %d\n", object->classId, object->objectId
        c
end
"

knowing that:

"
Line 1542 is the return here in depLockAndCheckObject() (your concern):

    if (object->classId == RelationRelationId || object->classId == AuthMemRelationId)
        return;

Line 1547 is the lock here in depLockAndCheckObject():

    /* assume we should lock the whole object not a sub-object */
    LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock);
"

So, with gdb attached to a session let's:

1. Create the parent relation

CREATE TABLE upsert_test (
    a   INT,
    b   TEXT
) PARTITION BY LIST (a);

gdb produces:

---
Will return for: classId 1259 and objectId 16384
Will return for: classId 1259 and objectId 16384
---

Oid 16384 is upsert_test, so I think the return (dependency.c:1542) is fine as
we are creating the object (it can't be dropped as not visible to anyone else).

2. Create another relation (will be the child)

CREATE TABLE upsert_test_2 (b TEXT, a int);

gdb produces:

---
Will return for: classId 1259 and objectId 16391
Will return for: classId 1259 and objectId 16394
Will return for: classId 1259 and objectId 16394
Will return for: classId 1259 and objectId 16391
---

Oid 16391 is upsert_test_2
Oid 16394 is pg_toast_16391

so I think the return (dependency.c:1542) is fine as we are creating those
objects (can't be dropped as not visible to anyone else).

3. Attach the partition

ALTER TABLE upsert_test ATTACH PARTITION upsert_test_2 FOR VALUES IN (2);

gdb produces:

---
Will return for: classId 1259 and objectId 16384
---

That's fine because we'd already had locked the relation 16384 through
AlterTableLookupRelation()->RangeVarGetRelidExtended()->LockRelationOid().

4. Add a constraint on the child relation

ALTER TABLE upsert_test_2 add constraint bdtc2 UNIQUE (a);

gdb produces:

---
Will return for: classId 1259 and objectId 16391
Will lock constraint: classId 2606 and objectId 16397
---

That's fine because we'd already had locked the relation 16391 through
AlterTableLookupRelation()->RangeVarGetRelidExtended()->LockRelationOid().

Oid 16397 is the constraint we're creating (bdtc2).

5. Add a constraint on the parent relation (this goes through
ConstraintSetParentConstraint())

ALTER TABLE upsert_test add constraint bdtc1 UNIQUE (a);

gdb produces:

---
Will return for: classId 1259 and objectId 16384
Will lock constraint: classId 2606 and objectId 16399
Will return for: classId 1259 and objectId 16398
Will return for: classId 1259 and objectId 16391
Will lock constraint: classId 2606 and objectId 16399
Will return for: classId 1259 and objectId 16391
---

Regarding "Will return for: classId 1259 and objectId 16384":
That's fine because we'd already had locked the relation 16384 through
AlterTableLookupRelation()->RangeVarGetRelidExtended()->LockRelationOid().

Regarding "Will lock constraint: classId 2606 and objectId 16399":
Oid 16399 is the constraint that we're creating.

Regarding "Will return for: classId 1259 and objectId 16398":
That's fine because Oid 16398 is an index that we're creating while creating
the constraint (so the index can't be dropped as not visible to anyone else).

Regarding "Will return for: classId 1259 and objectId 16391":
That's fine because 16384 we'd be already locked (as mentioned above). And
I think that's fine because trying to drop "upsert_test_2" (aka 16391) would produce
RemoveRelations()->RangeVarGetRelidExtended()->RangeVarCallbackForDropRelation()
->LockRelationOid(relid=16384, lockmode=8) and so would be locked.

Regarding this example, I don't think that the return in depLockAndCheckObject():

"
if (object->classId == RelationRelationId || object->classId == AuthMemRelationId)
     return;
"

is an issue. Indeed, the example above shows it would return for an object that
we'd be creating (so not visible to anyone else) or for an object that we'd
already have locked.

Is it an issue outside of this example?: I've the feeling it's not as we're
already careful when we deal with relations. That said, to be on the safe side
we could get rid of this return and make use of LockRelationOid() instead.

Regards,

---

From: Bertrand Drouvot <[email-masked]>
Date: Mon, 17 Jun 2024 10:50:56 +0000

Hi,

> Thanks for the feedback, I'll give it more thoughts.

Please find attached v10 that puts the object locking outside of the dependency
code.

It's done that way except for:

recordDependencyOnExpr() 
recordDependencyOnSingleRelExpr()
makeConfigurationDependencies()

The reason is that I think that it would need part of the logic that his inside
the above functions to be duplicated and I'm not sure that's worth it.

For example, we would probably need to:

- make additional calls to find_expr_references_walker() 
- make additional scan on the config map

It's also not done outside of recordDependencyOnCurrentExtension() as:

1. I think it is clear enough that way (as it is clear that the lock is taken on
a ExtensionRelationId object class).
2. why to include "commands/extension.h" in more places (locking would
depend of "creating_extension" and "CurrentExtensionObject"), while 1.?

Remarks:

1. depLockAndCheckObject() and friends in v9 have been renamed to
LockNotPinnedObject() and friends (as the vast majority of their calls are now
done outside of the dependency code).

2. regarding the concern around RelationRelationId (discussed in [1]), v10 adds
a comment "XXX Do we need a lock for RelationRelationId?" at the places we
may want to lock this object class. I did not think about it yet (but will do),
I only added this comment at multiple places.

I think that v10 is easier to follow (as compare to v9) as we can easily see for
which object class we'll put a lock on.

Thoughts?

[1]: https://www.postgresql.org/message-id/Zmv3TPfJAyQXhIdu%40ip-10-97-1-34.eu-west-3.compute.internal

Regards,

>From dc75e3255803617d21c55d77dc218904bd729d81 Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <[email-masked]>
Date: Fri, 29 Mar 2024 15:43:26 +0000
Subject: [PATCH v10] Avoid orphaned objects dependencies

Scenario 1:

Scenario 2:

To avoid those scenarios, a new lock (that conflicts with a lock taken by DROP)
has been put in place before the dependencies are being recorded. With this in
place, the drop schema in scenario 2 would be locked.

- schema and function (as the above scenarios)
- alter a dependency (function and schema)
- function and arg type
- function and return type
- function and function
- domain and domain
- table and type
- server and foreign data wrapper
---
 src/backend/catalog/aclchk.c                  |   1 +
 src/backend/catalog/dependency.c              | 109 ++++++++++++++-
 src/backend/catalog/heap.c                    |   8 ++
 src/backend/catalog/index.c                   |  16 +++
 src/backend/catalog/objectaddress.c           |  57 ++++++++
 src/backend/catalog/pg_aggregate.c            |   9 ++
 src/backend/catalog/pg_attrdef.c              |   1 +
 src/backend/catalog/pg_cast.c                 |   5 +
 src/backend/catalog/pg_collation.c            |   1 +
 src/backend/catalog/pg_constraint.c           |  11 ++
 src/backend/catalog/pg_conversion.c           |   2 +
 src/backend/catalog/pg_depend.c               |  27 +++-
 src/backend/catalog/pg_operator.c             |  19 +++
 src/backend/catalog/pg_proc.c                 |  17 ++-
 src/backend/catalog/pg_publication.c          |   5 +
 src/backend/catalog/pg_range.c                |   6 +
 src/backend/catalog/pg_type.c                 |  33 +++++
 src/backend/catalog/toasting.c                |   1 +
 src/backend/commands/alter.c                  |   4 +
 src/backend/commands/amcmds.c                 |   1 +
 src/backend/commands/cluster.c                |   5 +
 src/backend/commands/event_trigger.c          |   1 +
 src/backend/commands/extension.c              |   5 +
 src/backend/commands/foreigncmds.c            |   7 +
 src/backend/commands/functioncmds.c           |   6 +
 src/backend/commands/indexcmds.c              |   2 +
 src/backend/commands/opclasscmds.c            |  18 +++
 src/backend/commands/operatorcmds.c           |  30 ++++
 src/backend/commands/policy.c                 |   2 +
 src/backend/commands/proclang.c               |   3 +
 src/backend/commands/sequence.c               |   1 +
 src/backend/commands/statscmds.c              |   3 +
 src/backend/commands/tablecmds.c              |  32 +++--
 src/backend/commands/trigger.c                |  18 ++-
 src/backend/commands/tsearchcmds.c            |  81 +++++++----
 src/backend/commands/typecmds.c               |  84 ++++++++++++
 src/backend/rewrite/rewriteDefine.c           |   1 +
 src/backend/utils/errcodes.txt                |   1 +
 src/include/catalog/dependency.h              |   7 +
 src/include/catalog/objectaddress.h           |   1 +
 .../expected/test_dependencies_locks.out      | 129 ++++++++++++++++++
 src/test/isolation/isolation_schedule         |   1 +
 .../specs/test_dependencies_locks.spec        |  89 ++++++++++++
 43 files changed, 813 insertions(+), 47 deletions(-)
  31.4% src/backend/catalog/
  36.4% src/backend/commands/
  18.5% src/test/isolation/expected/
  11.5% src/test/isolation/specs/

index 143876b77f..5a614f25ff 100644
 
+				LockNotPinnedObject(NamespaceRelationId, iacls->nspid);
 				recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 			}
 		}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0489cbabcb..abf8b92a6d 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1519,6 +1519,84 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
 	}
 }
 
+/*
+ * LockNotPinnedObjectById
+ *
+ * Lock the object that we are about to record a dependency on.
+ * After it's locked, verify that it hasn't been dropped while we
+ * weren't looking.  If the object has been dropped, this function
+ * does not return!
+ */
+void
+LockNotPinnedObjectById(const ObjectAddress *object)
+{
+	char	   *object_description;
+
+	if (isObjectPinned(object))
+		return;
+
+	/*
+	 * Those don't rely on LockDatabaseObject() when being dropped (see
+	 * AcquireDeletionLock()). Also it looks like they can not produce
+	 * orphaned dependent objects when being dropped.
+	 */
+	if (object->classId == RelationRelationId || object->classId == AuthMemRelationId)
+		return;
+
+	object_description = getObjectDescription(object, true);
+
+	/* assume we should lock the whole object not a sub-object */
+	LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock);
+
+	/* check if object still exists */
+	if (!ObjectByIdExist(object))
+	{
+		if (object_description)
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+					 errmsg("%s does not exist", object_description)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+					 errmsg("a dependent object does not exist")));
+	}
+
+	if (object_description)
+		pfree(object_description);
+
+	return;
+}
+
+void
+LockNotPinnedObjectsById(const ObjectAddress *object, int nobject)
+{
+	int			i;
+
+	if (nobject < 0)
+		return;
+
+	for (i = 0; i < nobject; i++, object++)
+		LockNotPinnedObjectById(object);
+
+	return;
+}
+
+
+/*
+ * LockNotPinnedObject
+ *
+ * Lock the object that we are about to record a dependency on.
+ */
+void
+LockNotPinnedObject(Oid classid, Oid objid)
+{
+	ObjectAddress object;
+
+	ObjectAddressSet(object, classid, objid);
+
+	LockNotPinnedObjectById(&object);
+}
+
 /*
  * ReleaseDeletionLock - release an object deletion lock
  *
@@ -1564,13 +1642,8 @@ recordDependencyOnExpr(const ObjectAddress *depender,
 	/* Scan the expression tree for referenceable objects */
 	find_expr_references_walker(expr, &context);

 	free_object_addresses(context.addrs);
 }
@@ -1654,14 +1727,19 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
 
+			LockNotPinnedObjectsById(self_addrs->refs, self_addrs->numrefs);
 			int			selfref;
 
+			LockNotPinnedObjectById(depender);
+
 			for (selfref = 0; selfref < self_addrs->numrefs; selfref++)
 			{
 				ObjectAddress *thisobj = self_addrs->refs + selfref;
@@ -1674,6 +1752,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
 	}
 
 	/* Record the external dependencies */
+	LockNotPinnedObjectsById(context.addrs->refs, context.addrs->numrefs);
 	recordMultipleDependencies(depender,
 							   context.addrs->refs, context.addrs->numrefs,
 							   behavior);
@@ -2734,6 +2813,22 @@ stack_address_present_add_flags(const ObjectAddress *object,
 	return result;
 }
 
+/*
+ * Record multiple dependencies from an ObjectAddresses array and lock the
+ * referenced objects, after first removing any duplicates.
+ */
+void
+lock_record_object_address_dependencies(const ObjectAddress *depender,
+										ObjectAddresses *referenced,
+										DependencyType behavior)
+{
+	eliminate_duplicate_dependencies(referenced);
+	LockNotPinnedObjectsById(referenced->refs, referenced->numrefs);
+	recordMultipleDependencies(depender,
+							   referenced->refs, referenced->numrefs,
+							   behavior);
+}
+
 /*
  * Record multiple dependencies from an ObjectAddresses array, after first
  * removing any duplicates.
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a122bbffce..72443710d3 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -843,6 +843,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		ObjectAddressSubSet(myself, RelationRelationId, new_rel_oid, i + 1);
 		ObjectAddressSet(referenced, TypeRelationId,
 						 tupdesc->attrs[i].atttypid);
+		LockNotPinnedObject(TypeRelationId, tupdesc->attrs[i].atttypid);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 		/* The default collation is pinned, so don't bother recording it */
@@ -851,6 +852,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		{
 			ObjectAddressSet(referenced, CollationRelationId,
 							 tupdesc->attrs[i].attcollation);
+			LockNotPinnedObject(CollationRelationId, tupdesc->attrs[i].attcollation);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 	}
@@ -1451,11 +1453,13 @@ heap_create_with_catalog(const char *relname,
 
 		ObjectAddressSet(referenced, NamespaceRelationId, relnamespace);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(NamespaceRelationId, relnamespace);
 
 		if (reloftypeid)
 		{
 			ObjectAddressSet(referenced, TypeRelationId, reloftypeid);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(TypeRelationId, reloftypeid);
 		}
 
 		/*
@@ -1469,6 +1473,7 @@ heap_create_with_catalog(const char *relname,
 		{
 			ObjectAddressSet(referenced, AccessMethodRelationId, accessmtd);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(AccessMethodRelationId, accessmtd);
 		}
 
 		record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -3383,6 +3388,7 @@ StorePartitionKey(Relation rel,
 	{
 		ObjectAddressSet(referenced, OperatorClassRelationId, partopclass[i]);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(OperatorClassRelationId, partopclass[i]);
 
 		/* The default collation is pinned, so don't bother recording it */
 		if (OidIsValid(partcollation[i]) &&
@@ -3390,6 +3396,7 @@ StorePartitionKey(Relation rel,
 		{
 			ObjectAddressSet(referenced, CollationRelationId, partcollation[i]);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(CollationRelationId, partcollation[i]);
 		}
 	}
 
@@ -3409,6 +3416,7 @@ StorePartitionKey(Relation rel,
 
 		ObjectAddressSubSet(referenced, RelationRelationId,
 							RelationGetRelid(rel), partattrs[i]);
+		/* Do we need a lock on RelationRelationId? */
 		recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
 	}
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 55fdde4b24..b89c217a09 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1127,6 +1127,7 @@ index_create(Relation heapRelation,
 										heapRelationId,
 										indexInfo->ii_IndexAttrNumbers[i]);
 					add_exact_object_address(&referenced, addrs);
+					/* XXX Do we need a lock for RelationRelationId? */
 					have_simple_col = true;
 				}
 			}
@@ -1141,6 +1142,7 @@ index_create(Relation heapRelation,
 			{
 				ObjectAddressSet(referenced, RelationRelationId,
 								 heapRelationId);
+				/* XXX Do we need a lock for RelationRelationId? */
 				add_exact_object_address(&referenced, addrs);
 			}
 
@@ -1157,9 +1159,11 @@ index_create(Relation heapRelation,
 		if (OidIsValid(parentIndexRelid))
 		{
 			ObjectAddressSet(referenced, RelationRelationId, parentIndexRelid);
+			/* XXX Do we need a lock for RelationRelationId? */
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 
 			ObjectAddressSet(referenced, RelationRelationId, heapRelationId);
+			/* XXX Do we need a lock for RelationRelationId? */
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 		}
 
@@ -1175,6 +1179,7 @@ index_create(Relation heapRelation,
 			{
 				ObjectAddressSet(referenced, CollationRelationId, collationIds[i]);
 				add_exact_object_address(&referenced, addrs);
+				LockNotPinnedObject(CollationRelationId, collationIds[i]);
 			}
 		}
 
@@ -1183,6 +1188,7 @@ index_create(Relation heapRelation,
 		{
 			ObjectAddressSet(referenced, OperatorClassRelationId, opclassIds[i]);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(OperatorClassRelationId, opclassIds[i]);
 		}
 
 		record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -1987,6 +1993,14 @@ index_constraint_create(Relation heapRelation,
 	 */
 	ObjectAddressSet(myself, ConstraintRelationId, conOid);
 	ObjectAddressSet(idxaddr, RelationRelationId, indexRelationId);
+
+	/*
+	 * CommandCounterIncrement here to ensure the new constraint entry is
+	 * visible when we'll check of object existence when recording the
+	 * dependencies.
+	 */
+	CommandCounterIncrement();
+	LockNotPinnedObject(ConstraintRelationId, conOid);
 	recordDependencyOn(&idxaddr, &myself, DEPENDENCY_INTERNAL);
 
 	/*
@@ -1998,9 +2012,11 @@ index_constraint_create(Relation heapRelation,
 		ObjectAddress referenced;
 
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId);
+		LockNotPinnedObject(ConstraintRelationId, parentConstraintId);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 		ObjectAddressSet(referenced, RelationRelationId,
 						 RelationGetRelid(heapRelation));
+		/* XXX Do we need a lock for RelationRelationId? */
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 	}

+/*
+ * ObjectByIdExist
+ *
index 90fc7db949..a47e3c5507 100644
@@ -748,12 +748,14 @@ AggregateCreate(const char *aggName,
 	/* Depends on transition function */
 	ObjectAddressSet(referenced, ProcedureRelationId, transfn);
+	LockNotPinnedObject(ProcedureRelationId, transfn);
 
 	/* Depends on final function, if any */
 	if (OidIsValid(finalfn))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, finalfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, finalfn);
 	}
 
 	/* Depends on combine function, if any */
@@ -761,6 +763,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, combinefn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, combinefn);
 	}
 
 	/* Depends on serialization function, if any */
@@ -768,6 +771,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, serialfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, serialfn);
 	}
 
 	/* Depends on deserialization function, if any */
@@ -775,6 +779,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, deserialfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, deserialfn);
 	}
 
 	/* Depends on forward transition function, if any */
@@ -782,6 +787,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, mtransfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, mtransfn);
 	}
 
 	/* Depends on inverse transition function, if any */
@@ -789,6 +795,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, minvtransfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, minvtransfn);
 	}
 
 	/* Depends on final function, if any */
@@ -796,6 +803,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, mfinalfn);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, mfinalfn);
 	}
 
 	/* Depends on sort operator, if any */
@@ -803,6 +811,7 @@ AggregateCreate(const char *aggName,
 	{
 		ObjectAddressSet(referenced, OperatorRelationId, sortop);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(OperatorRelationId, sortop);
 	}
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c
index 003ae70b4d..80db2c0c20 100644
--- a/src/backend/catalog/pg_attrdef.c
+++ b/src/backend/catalog/pg_attrdef.c
@@ -178,6 +178,7 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	colobject.objectId = RelationGetRelid(rel);
 	colobject.objectSubId = attnum;
 
+	/* XXX Do we need a lock for RelationRelationId? */
 	recordDependencyOn(&defobject, &colobject,
 					   attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
 
diff --git a/src/backend/catalog/pg_cast.c b/src/backend/catalog/pg_cast.c
index 5a5b855d51..d3707e424c 100644
--- a/src/backend/catalog/pg_cast.c
+++ b/src/backend/catalog/pg_cast.c
@@ -97,16 +97,19 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
 	/* dependency on source type */
 	ObjectAddressSet(referenced, TypeRelationId, sourcetypeid);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(TypeRelationId, sourcetypeid);
 
 	/* dependency on target type */
 	ObjectAddressSet(referenced, TypeRelationId, targettypeid);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(TypeRelationId, targettypeid);
 
 	/* dependency on function */
 	if (OidIsValid(funcid))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, funcid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, funcid);
 	}
 
 	/* dependencies on casts required for function */
@@ -114,11 +117,13 @@ CastCreate(Oid sourcetypeid, Oid targettypeid,
 	{
 		ObjectAddressSet(referenced, CastRelationId, incastid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(CastRelationId, incastid);
 	}
 	if (OidIsValid(outcastid))
 	{
 		ObjectAddressSet(referenced, CastRelationId, outcastid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(CastRelationId, outcastid);
 	}
 
 	record_object_address_dependencies(&myself, addrs, behavior);
diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c
index 7f2f701229..78498b8c20 100644
--- a/src/backend/catalog/pg_collation.c
+++ b/src/backend/catalog/pg_collation.c
@@ -218,6 +218,7 @@ CollationCreate(const char *collname, Oid collnamespace,
 	referenced.classId = NamespaceRelationId;
 	referenced.objectId = collnamespace;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(NamespaceRelationId, collnamespace);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* create dependency on owner */
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 3baf9231ed..4afa9a1d69 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -257,12 +257,14 @@ CreateConstraintEntry(const char *constraintName,
 				ObjectAddressSubSet(relobject, RelationRelationId, relId,
 									constraintKey[i]);
 				add_exact_object_address(&relobject, addrs_auto);
+				/* XXX Do we need a lock for RelationRelationId?? */
 			}
 		}
 		else
 		{
 			ObjectAddressSet(relobject, RelationRelationId, relId);
 			add_exact_object_address(&relobject, addrs_auto);
+			/* XXX Do we need a lock for RelationRelationId?? */
 		}
 	}
 
@@ -275,6 +277,7 @@ CreateConstraintEntry(const char *constraintName,
 
 		ObjectAddressSet(domobject, TypeRelationId, domainId);
 		add_exact_object_address(&domobject, addrs_auto);
+		LockNotPinnedObject(TypeRelationId, domainId);
 	}
 
 	record_object_address_dependencies(&conobject, addrs_auto,
@@ -299,12 +302,14 @@ CreateConstraintEntry(const char *constraintName,
 				ObjectAddressSubSet(relobject, RelationRelationId,
 									foreignRelId, foreignKey[i]);
 				add_exact_object_address(&relobject, addrs_normal);
+				/* XXX Do we need a lock for RelationRelationId? */
 			}
 		}
 		else
 		{
 			ObjectAddressSet(relobject, RelationRelationId, foreignRelId);
 			add_exact_object_address(&relobject, addrs_normal);
+			/* XXX Do we need a lock for RelationRelationId? */
 		}
 	}
 
@@ -320,6 +325,7 @@ CreateConstraintEntry(const char *constraintName,
 
 		ObjectAddressSet(relobject, RelationRelationId, indexRelId);
 		add_exact_object_address(&relobject, addrs_normal);
+		/* XXX Do we need a lock for RelationRelationId?? */
 	}
 
 	if (foreignNKeys > 0)
@@ -339,15 +345,18 @@ CreateConstraintEntry(const char *constraintName,
 		{
 			oprobject.objectId = pfEqOp[i];
 			add_exact_object_address(&oprobject, addrs_normal);
+			LockNotPinnedObject(OperatorRelationId, pfEqOp[i]);
 			if (ppEqOp[i] != pfEqOp[i])
 			{
 				oprobject.objectId = ppEqOp[i];
 				add_exact_object_address(&oprobject, addrs_normal);
+				LockNotPinnedObject(OperatorRelationId, ppEqOp[i]);
 			}
 			if (ffEqOp[i] != pfEqOp[i])
 			{
 				oprobject.objectId = ffEqOp[i];
 				add_exact_object_address(&oprobject, addrs_normal);
+				LockNotPinnedObject(OperatorRelationId, ffEqOp[i]);
 			}
 		}
 	}
@@ -858,9 +867,11 @@ ConstraintSetParentConstraint(Oid childConstrId,
 		ObjectAddressSet(depender, ConstraintRelationId, childConstrId);
 
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstrId);
+		LockNotPinnedObject(ConstraintRelationId, parentConstrId);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
 
 		ObjectAddressSet(referenced, RelationRelationId, childTableId);
+		/* XXX Do we need a lock for RelationRelationId? */
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
 	}
 	else
diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c
index 0770878eac..25881654d6 100644
--- a/src/backend/catalog/pg_conversion.c
+++ b/src/backend/catalog/pg_conversion.c
@@ -116,12 +116,14 @@ ConversionCreate(const char *conname, Oid connamespace,
 	referenced.classId = ProcedureRelationId;
 	referenced.objectId = conproc;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(ProcedureRelationId, conproc);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
+	LockNotPinnedObject(NamespaceRelationId, connamespace);
 
index cfd7ef51df..b97aa2ab91 100644

+		 * LockNotPinnedObjectById()).
 
+		LockNotPinnedObject(ExtensionRelationId, CurrentExtensionObject);
 		recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION);
 	}
 }
@@ -706,7 +725,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
  * The passed subId, if any, is ignored; we assume that only whole objects
  * are pinned (and that this implies pinning their components).
  */
-static bool
+bool
 isObjectPinned(const ObjectAddress *object)
 {
 	return IsPinnedObject(object->classId, object->objectId);
diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 65b45a424a..e8374eec88 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -251,6 +251,16 @@ OperatorShellMake(const char *operatorName,
 	values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
 	values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(InvalidOid);
 
+	/* Lock dependent objects */
+	if (OidIsValid(operatorNamespace))
+		LockNotPinnedObject(NamespaceRelationId, operatorNamespace);
+
+	if (OidIsValid(leftTypeId))
+		LockNotPinnedObject(TypeRelationId, leftTypeId);
+
+	if (OidIsValid(rightTypeId))
+		LockNotPinnedObject(TypeRelationId, rightTypeId);
+
 	/*
 	 * create a new operator tuple
 	 */
@@ -513,6 +523,15 @@ OperatorCreate(const char *operatorName,
 		CatalogTupleInsert(pg_operator_desc, tup);
 	}
 
+	/* Lock dependent objects */
+	LockNotPinnedObject(NamespaceRelationId, operatorNamespace);
+	LockNotPinnedObject(TypeRelationId, leftTypeId);
+	LockNotPinnedObject(TypeRelationId, rightTypeId);
+	LockNotPinnedObject(TypeRelationId, operResultType);
+	LockNotPinnedObject(ProcedureRelationId, procedureId);
+	LockNotPinnedObject(ProcedureRelationId, restrictionId);
+	LockNotPinnedObject(ProcedureRelationId, joinId);
+
 	/* Add dependencies for the entry */
 	address = makeOperatorDependencies(tup, true, isUpdate);
 
index 528c17cd7f..9cceb6413b 100644
 
+	/*
 
 	ObjectAddressSet(myself, ProcedureRelationId, retval);
@@ -600,20 +607,24 @@ ProcedureCreate(const char *procedureName,
 	/* dependency on namespace */
 	ObjectAddressSet(referenced, NamespaceRelationId, procNamespace);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(NamespaceRelationId, procNamespace);
 
 	/* dependency on implementation language */
 	ObjectAddressSet(referenced, LanguageRelationId, languageObjectId);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(LanguageRelationId, languageObjectId);
 
 	/* dependency on return type */
 	ObjectAddressSet(referenced, TypeRelationId, returnType);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(TypeRelationId, returnType);
 
 	/* dependency on transform used by return type, if any */
 	if ((trfid = get_transform_oid(returnType, languageObjectId, true)))
 	{
 		ObjectAddressSet(referenced, TransformRelationId, trfid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(TransformRelationId, trfid);
 	}
 
 	/* dependency on parameter types */
@@ -621,12 +632,14 @@ ProcedureCreate(const char *procedureName,
 	{
 		ObjectAddressSet(referenced, TypeRelationId, allParams[i]);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(TypeRelationId, allParams[i]);
 
 		/* dependency on transform used by parameter type, if any */
 		if ((trfid = get_transform_oid(allParams[i], languageObjectId, true)))
 		{
 			ObjectAddressSet(referenced, TransformRelationId, trfid);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(TransformRelationId, trfid);
 		}
 	}
 
@@ -635,6 +648,7 @@ ProcedureCreate(const char *procedureName,
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, prosupport);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, prosupport);
 	}
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -674,9 +688,6 @@ ProcedureCreate(const char *procedureName,
 		ArrayType  *set_items = NULL;
 		int			save_nestlevel = 0;
 
index 0602398a54..5e16c0fcef 100644
 
 	/* Add dependency on the publication */
 	ObjectAddressSet(referenced, PublicationRelationId, pubid);
+	LockNotPinnedObject(PublicationRelationId, pubid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Add dependency on the relation */
 	ObjectAddressSet(referenced, RelationRelationId, relid);
+	/* XXX Do we need a lock for RelationRelationId? */
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
+		/* XXX Do we need a lock for RelationRelationId? */

 	/* Add dependency on the publication */
 	ObjectAddressSet(referenced, PublicationRelationId, pubid);
+	LockNotPinnedObject(PublicationRelationId, pubid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Add dependency on the schema */
 	ObjectAddressSet(referenced, NamespaceRelationId, schemaid);
+	LockNotPinnedObject(NamespaceRelationId, schemaid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Close the table */
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index 501a6ba410..e5b5a0b6f8 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -70,26 +70,31 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
 
 	ObjectAddressSet(referenced, TypeRelationId, rangeSubType);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(TypeRelationId, rangeSubType);
 
 	ObjectAddressSet(referenced, OperatorClassRelationId, rangeSubOpclass);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(OperatorClassRelationId, rangeSubOpclass);
 
 	if (OidIsValid(rangeCollation))
 	{
 		ObjectAddressSet(referenced, CollationRelationId, rangeCollation);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(CollationRelationId, rangeCollation);
 	}
 
 	if (OidIsValid(rangeCanonical))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, rangeCanonical);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, rangeCanonical);
 	}
 
 	if (OidIsValid(rangeSubDiff))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, rangeSubDiff);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, rangeSubDiff);
 	}
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -99,6 +104,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
 	referencing.classId = TypeRelationId;
 	referencing.objectId = multirangeTypeOid;
 	referencing.objectSubId = 0;
+	LockNotPinnedObject(TypeRelationId, rangeTypeOid);
 	recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
 
 	table_close(pg_range, RowExclusiveLock);
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 395dec8ed8..92614cdaaa 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -157,6 +157,12 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 	 * Create dependencies.  We can/must skip this in bootstrap mode.
 	 */
 	if (!IsBootstrapProcessingMode())
+	{
+		/* Lock dependent objects */
+		LockNotPinnedObject(NamespaceRelationId, typeNamespace);
+		LockNotPinnedObject(ProcedureRelationId, F_SHELL_IN);
+		LockNotPinnedObject(ProcedureRelationId, F_SHELL_OUT);
+
 		GenerateTypeDependencies(tup,
 								 pg_type_desc,
 								 NULL,
@@ -166,6 +172,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
 								 false,
 								 true,	/* make extension dependency */
 								 false);
+	}
 
 	/* Post creation hook for new shell type */
 	InvokeObjectPostCreateHook(TypeRelationId, typoid, 0);
@@ -494,6 +501,31 @@ TypeCreate(Oid newTypeOid,
 	 * Create dependencies.  We can/must skip this in bootstrap mode.
 	 */
 	if (!IsBootstrapProcessingMode())
+	{
+		/*
+		 * CommandCounterIncrement here to ensure the new type  entry is
+		 * visible when we'll check of object existence when recording the
+		 * dependencies.
+		 */
+		CommandCounterIncrement();
+
+		/* Lock dependent objects */
+		LockNotPinnedObject(NamespaceRelationId, typeNamespace);
+		LockNotPinnedObject(ProcedureRelationId, inputProcedure);
+		LockNotPinnedObject(ProcedureRelationId, outputProcedure);
+		LockNotPinnedObject(ProcedureRelationId, receiveProcedure);
+		LockNotPinnedObject(ProcedureRelationId, sendProcedure);
+		LockNotPinnedObject(ProcedureRelationId, typmodinProcedure);
+		LockNotPinnedObject(ProcedureRelationId, typmodoutProcedure);
+		LockNotPinnedObject(ProcedureRelationId, analyzeProcedure);
+		LockNotPinnedObject(ProcedureRelationId, subscriptProcedure);
+		LockNotPinnedObject(TypeRelationId, baseType);
+		LockNotPinnedObject(CollationRelationId, typeCollation);
+		LockNotPinnedObject(TypeRelationId, elementType);
+		/* XXX Do we need a lock for RelationRelationId? */
+		if (relationKind == RELKIND_COMPOSITE_TYPE)
+			LockNotPinnedObject(TypeRelationId, typeObjectId);
+
 		GenerateTypeDependencies(tup,
 								 pg_type_desc,
 								 (defaultTypeBin ?
@@ -505,6 +537,7 @@ TypeCreate(Oid newTypeOid,
 								 isDependentType,
 								 true,	/* make extension dependency */
 								 rebuildDeps);
+	}
 
index 738bc46ae8..4a708030ac 100644
 
+		/* XXX Do we need a lock for RelationRelationId? */
 		recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
 	}
 
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c
index 4f99ebb447..57e86f576a 100644
--- a/src/backend/commands/alter.c
+++ b/src/backend/commands/alter.c
@@ -501,7 +501,10 @@ ExecAlterObjectDependsStmt(AlterObjectDependsStmt *stmt, ObjectAddress *refAddre
 		currexts = getAutoExtensionsOfObject(address.classId,
 											 address.objectId);
 		if (!list_member_oid(currexts, refAddr.objectId))
+		{
+			LockNotPinnedObject(refAddr.classId, refAddr.objectId);
 			recordDependencyOn(&address, &refAddr, DEPENDENCY_AUTO_EXTENSION);
+		}
 	}
 
 	return address;
@@ -807,6 +810,7 @@ AlterObjectNamespace_internal(Relation rel, Oid objid, Oid nspOid)
 	pfree(replaces);
 
 	/* update dependency to point to the new schema */
+	LockNotPinnedObject(NamespaceRelationId, nspOid);
 	if (changeDependencyFor(classId, objid,
 							NamespaceRelationId, oldNspOid, nspOid) != 1)
 		elog(ERROR, "could not change schema dependency for object %u",
diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c
index aaa0f9a1dc..8616a7c9fa 100644
--- a/src/backend/commands/amcmds.c
+++ b/src/backend/commands/amcmds.c
@@ -104,6 +104,7 @@ CreateAccessMethod(CreateAmStmt *stmt)
 	referenced.objectId = amhandler;
 	referenced.objectSubId = 0;
 
+	LockNotPinnedObject(ProcedureRelationId, amhandler);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	recordDependencyOnCurrentExtension(&myself, false);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index 78f96789b0..b54a04f4b0 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -1272,6 +1272,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 	 */
 	if (relam1 != relam2)
 	{
+		LockNotPinnedObject(AccessMethodRelationId, relam2);
 		if (changeDependencyFor(RelationRelationId,
 								r1,
 								AccessMethodRelationId,
@@ -1280,6 +1281,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 			elog(ERROR, "could not change access method dependency for relation \"%s.%s\"",
 				 get_namespace_name(get_rel_namespace(r1)),
 				 get_rel_name(r1));
+
+		LockNotPinnedObject(AccessMethodRelationId, relam1);
 		if (changeDependencyFor(RelationRelationId,
 								r2,
 								AccessMethodRelationId,
@@ -1381,6 +1384,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 			{
 				baseobject.objectId = r1;
 				toastobject.objectId = relform1->reltoastrelid;
+				/* XXX Do we need a lock for RelationRelationId? */
 				recordDependencyOn(&toastobject, &baseobject,
 								   DEPENDENCY_INTERNAL);
 			}
@@ -1389,6 +1393,7 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
 			{
 				baseobject.objectId = r2;
 				toastobject.objectId = relform2->reltoastrelid;
+				/* XXX Do we need a lock for RelationRelationId? */
 				recordDependencyOn(&toastobject, &baseobject,
 								   DEPENDENCY_INTERNAL);
 			}
diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c
index 7a5ed6b985..8d0cdec59e 100644
--- a/src/backend/commands/event_trigger.c
+++ b/src/backend/commands/event_trigger.c
@@ -327,6 +327,7 @@ insert_event_trigger_tuple(const char *trigname, const char *eventname, Oid evtO
 	referenced.classId = ProcedureRelationId;
 	referenced.objectId = funcoid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(ProcedureRelationId, funcoid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	/* Depend on extension, if any. */
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index 1643c8c69a..669a5d6dd8 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1924,6 +1924,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 
 	ObjectAddressSet(nsp, NamespaceRelationId, schemaOid);
 	add_exact_object_address(&nsp, refobjs);
+	LockNotPinnedObject(NamespaceRelationId, schemaOid);
 
 	foreach(lc, requiredExtensions)
 	{
@@ -1932,6 +1933,7 @@ InsertExtensionTuple(const char *extName, Oid extOwner,
 
 		ObjectAddressSet(otherext, ExtensionRelationId, reqext);
 		add_exact_object_address(&otherext, refobjs);
+		LockNotPinnedObject(ExtensionRelationId, reqext);
 	}
 
 	/* Record all of them (this includes duplicate elimination) */
@@ -2968,6 +2970,7 @@ AlterExtensionNamespace(const char *extensionName, const char *newschema, Oid *o
 	table_close(extRel, RowExclusiveLock);
 
 	/* update dependency to point to the new schema */
+	LockNotPinnedObject(NamespaceRelationId, nspOid);
 	if (changeDependencyFor(ExtensionRelationId, extensionOid,
 							NamespaceRelationId, oldNspOid, nspOid) != 1)
 		elog(ERROR, "could not change schema dependency for extension %s",
@@ -3258,6 +3261,7 @@ ApplyExtensionUpdates(Oid extensionOid,
 			otherext.objectId = reqext;
 			otherext.objectSubId = 0;
 
+			LockNotPinnedObject(ExtensionRelationId, reqext);
 			recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL);
 		}
 
@@ -3414,6 +3418,7 @@ ExecAlterExtensionContentsRecurse(AlterExtensionContentsStmt *stmt,
 		/*
 		 * OK, add the dependency.
 		 */
+		LockNotPinnedObject(extension.classId, extension.objectId);
 		recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION);
 
 		/*
index cf61bbac1f..735bca486c 100644
+		LockNotPinnedObject(ProcedureRelationId, fdwhandler);
 
+		LockNotPinnedObject(ProcedureRelationId, fdwvalidator);
 
+			LockNotPinnedObject(ProcedureRelationId, fdwhandler);
 
+			LockNotPinnedObject(ProcedureRelationId, fdwvalidator);
+	LockNotPinnedObject(ForeignDataWrapperRelationId, fdw->fdwid);
 
+	LockNotPinnedObject(ForeignServerRelationId, srv->serverid);
 
 	if (OidIsValid(useId))
+	LockNotPinnedObject(ForeignServerRelationId, server->serverid);
 
 	table_close(ftrel, RowExclusiveLock);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 6593fd7d81..8207ef08b3 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -1446,6 +1446,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 		/* Add or replace dependency on support function */
 		if (OidIsValid(procForm->prosupport))
 		{
+			LockNotPinnedObject(ProcedureRelationId, newsupport);
 			if (changeDependencyFor(ProcedureRelationId, funcOid,
 									ProcedureRelationId, procForm->prosupport,
 									newsupport) != 1)
@@ -1459,6 +1460,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 			referenced.classId = ProcedureRelationId;
 			referenced.objectId = newsupport;
 			referenced.objectSubId = 0;
+			LockNotPinnedObject(ProcedureRelationId, newsupport);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_NORMAL);
 		}
 
@@ -1962,21 +1964,25 @@ CreateTransform(CreateTransformStmt *stmt)
 	/* dependency on language */
 	ObjectAddressSet(referenced, LanguageRelationId, langid);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(LanguageRelationId, langid);
 
 	/* dependency on type */
 	ObjectAddressSet(referenced, TypeRelationId, typeid);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(TypeRelationId, typeid);
 
 	/* dependencies on functions */
 	if (OidIsValid(fromsqlfuncid))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, fromsqlfuncid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, fromsqlfuncid);
 	}
 	if (OidIsValid(tosqlfuncid))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, tosqlfuncid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, tosqlfuncid);
 	}
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 309389e20d..b14eaad7a3 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -4377,8 +4377,10 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
 			ObjectAddressSet(parentIdx, RelationRelationId, parentOid);
 			ObjectAddressSet(partitionTbl, RelationRelationId,
 							 partitionIdx->rd_index->indrelid);
+			/* Do we lock for RelationRelationId?? */
 			recordDependencyOn(&partIdx, &parentIdx,
 							   DEPENDENCY_PARTITION_PRI);
+			/* Do we lock for RelationRelationId?? */
 			recordDependencyOn(&partIdx, &partitionTbl,
 							   DEPENDENCY_PARTITION_SEC);
 		}
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index b8b5c147c5..ca15106ca9 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -298,12 +298,14 @@ CreateOpFamily(CreateOpFamilyStmt *stmt, const char *opfname,
 	referenced.classId = AccessMethodRelationId;
 	referenced.objectId = amoid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(AccessMethodRelationId, amoid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
 
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
 
+	LockNotPinnedObject(OperatorFamilyRelationId, opfamilyoid);
 
+	LockNotPinnedObject(TypeRelationId, typeoid);
 
+		LockNotPinnedObject(TypeRelationId, storageoid);

 		heap_freetuple(tup);
 
+		/*
 
 		/* see comments in amapi.h about dependency strength */
+		LockNotPinnedObject(OperatorRelationId, op->object);
 		recordDependencyOn(&myself, &referenced,
 						   op->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);

+		LockNotPinnedObject(referenced.classId, op->refobjid);
 		recordDependencyOn(&myself, &referenced,
 						   op->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);

+			LockNotPinnedObject(OperatorFamilyRelationId, op->sortfamily);
 
 		/* see comments in amapi.h about dependency strength */
+		LockNotPinnedObject(ProcedureRelationId, proc->object);
 		recordDependencyOn(&myself, &referenced,
 						   proc->ref_is_hard ? DEPENDENCY_NORMAL : DEPENDENCY_AUTO);

+		LockNotPinnedObject(referenced.classId, proc->refobjid);
 		recordDependencyOn(&myself, &referenced,
 						   proc->ref_is_hard ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);
 
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 5872a3e192..58a69e7cc2 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -33,6 +33,7 @@
 
 #include "access/htup_details.h"
 #include "access/table.h"
+#include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/objectaccess.h"
 #include "catalog/pg_namespace.h"
@@ -656,11 +657,15 @@ AlterOperator(AlterOperatorStmt *stmt)
 	{
 		replaces[Anum_pg_operator_oprrest - 1] = true;
 		values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restrictionOid);
+		if (OidIsValid(restrictionOid))
+			LockNotPinnedObject(ProcedureRelationId, restrictionOid);
 	}
 	if (updateJoin)
 	{
 		replaces[Anum_pg_operator_oprjoin - 1] = true;
 		values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid);
+		if (OidIsValid(joinOid))
+			LockNotPinnedObject(ProcedureRelationId, joinOid);
 	}
 	if (OidIsValid(commutatorOid))
 	{
@@ -688,6 +693,31 @@ AlterOperator(AlterOperatorStmt *stmt)
 
 	CatalogTupleUpdate(catalog, &tup->t_self, tup);
 
+
+	/* Lock dependent objects */
+	oprForm = (Form_pg_operator) GETSTRUCT(tup);
+
+	if (OidIsValid(oprForm->oprnamespace))
+		LockNotPinnedObject(NamespaceRelationId, oprForm->oprnamespace);
+
+	if (OidIsValid(oprForm->oprleft))
+		LockNotPinnedObject(TypeRelationId, oprForm->oprleft);
+
+	if (OidIsValid(oprForm->oprright))
+		LockNotPinnedObject(TypeRelationId, oprForm->oprright);
+
+	if (OidIsValid(oprForm->oprresult))
+		LockNotPinnedObject(TypeRelationId, oprForm->oprresult);
+
+	if (OidIsValid(oprForm->oprcode))
+		LockNotPinnedObject(ProcedureRelationId, oprForm->oprcode);
+
+	if (OidIsValid(oprForm->oprrest))
+		LockNotPinnedObject(ProcedureRelationId, oprForm->oprrest);
+
+	if (OidIsValid(oprForm->oprjoin))
+		LockNotPinnedObject(ProcedureRelationId, oprForm->oprjoin);
+
 	address = makeOperatorDependencies(tup, false, true);
 
 	if (OidIsValid(commutatorOid) || OidIsValid(negatorOid))
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 6ff3eba824..31e61e4e67 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -722,6 +722,7 @@ CreatePolicy(CreatePolicyStmt *stmt)
 	myself.objectId = policy_id;
 	myself.objectSubId = 0;
 
+	/* XXX Do we need a lock for RelationRelationId? */
 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);

+	/* XXX Do we need a lock for RelationRelationId? */
 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
 
 	recordDependencyOnExpr(&myself, qual, qual_parse_rtable, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c
index 881f90017e..fadfd9064f 100644
--- a/src/backend/commands/proclang.c
+++ b/src/backend/commands/proclang.c
@@ -190,12 +190,14 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 	/* dependency on the PL handler function */
 	ObjectAddressSet(referenced, ProcedureRelationId, handlerOid);
 	add_exact_object_address(&referenced, addrs);
+	LockNotPinnedObject(ProcedureRelationId, handlerOid);
 
 	/* dependency on the inline handler function, if any */
 	if (OidIsValid(inlineOid))
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, inlineOid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, inlineOid);
 	}
 
 	/* dependency on the validator function, if any */
@@ -203,6 +205,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt)
 	{
 		ObjectAddressSet(referenced, ProcedureRelationId, valOid);
 		add_exact_object_address(&referenced, addrs);
+		LockNotPinnedObject(ProcedureRelationId, valOid);
 	}
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 28f8522264..b1db4e8933 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1681,6 +1681,7 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
 		depobject.classId = RelationRelationId;
 		depobject.objectId = RelationGetRelid(seqrel);
 		depobject.objectSubId = 0;
+		/* XXX Do we need a lock for RelationRelationId? */
 		recordDependencyOn(&depobject, &refobject, deptype);
 	}
 
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 1db3ef69d2..372ae02650 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -536,6 +536,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	for (i = 0; i < nattnums; i++)
 	{
 		ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
+		/* XXX Do we need a lock for RelationRelationId? */
 		recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
 	}
 
@@ -553,6 +554,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	if (!nattnums)
 	{
 		ObjectAddressSet(parentobject, RelationRelationId, relid);
+		/* XXX Do we need a lock for RelationRelationId? */
 		recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
 	}
 
+	LockNotPinnedObject(NamespaceRelationId, namespaceId);
 
 	recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 66cda26a25..c66f110984 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3438,6 +3438,7 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid,
 	childobject.objectId = relationId;
 	childobject.objectSubId = 0;
 
+	/* XXX Do we need a lock for RelationRelationId? */
 	recordDependencyOn(&childobject, &parentobject,
 					   child_dependency_type(child_is_partition));
 
@@ -7349,7 +7350,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/*
 	 * Add needed dependency entries for the new column.
 	 */
+	LockNotPinnedObject(TypeRelationId, attribute->atttypid);
 	add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
+	LockNotPinnedObject(CollationRelationId, attribute->attcollation);
 	add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
 
 	/*
@@ -10174,6 +10177,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
 		ObjectAddress referenced;
 
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
+		LockNotPinnedObject(ConstraintRelationId, parentConstr);
 		recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
 	}
 
@@ -10465,8 +10469,10 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
 			 */
 			ObjectAddressSet(address, ConstraintRelationId, constrOid);
 			ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
+			LockNotPinnedObject(ConstraintRelationId, parentConstr);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
 			ObjectAddressSet(referenced, RelationRelationId, partitionId);
+			/* XXX Do we need a lock for RelationRelationId? */
 			recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
 
 			/* Make all this visible before recursing */
@@ -10967,9 +10973,11 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
 		/* Set up partition dependencies for the new constraint */
 		ObjectAddressSet(address, ConstraintRelationId, constrOid);
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
+		LockDatabaseObject(ConstraintRelationId, parentConstrOid, 0, AccessShareLock);
 		recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
 		ObjectAddressSet(referenced, RelationRelationId,
 						 RelationGetRelid(partRel));
+		/* XXX Do we need a lock for RelationRelationId? */
 		recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
 
 		/* Done with the cloned constraint's tuple */
@@ -13254,7 +13262,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	table_close(attrelation, RowExclusiveLock);
 
 	/* Install dependencies on new datatype and collation */
+	LockNotPinnedObject(TypeRelationId, targettype);
 	add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
+	LockNotPinnedObject(CollationRelationId, targetcollid);
 	add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
 
 	/*
@@ -14816,6 +14826,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 		 */
 		ObjectAddressSet(relobj, RelationRelationId, reloid);
 		ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
+		LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
 		recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
 	}
 	else if (OidIsValid(oldAccessMethodId) &&
@@ -14835,6 +14846,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 			   OidIsValid(rd_rel->relam));
 
 		/* Both are valid, so update the dependency */
+		LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
 		changeDependencyFor(RelationRelationId, reloid,
 							AccessMethodRelationId,
 							oldAccessMethodId, rd_rel->relam);
@@ -16434,6 +16446,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 	typeobj.classId = TypeRelationId;
 	typeobj.objectId = typeid;
 	typeobj.objectSubId = 0;
+	LockNotPinnedObject(TypeRelationId, typeid);
 	recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
 
 	/* Update pg_class.reloftype */
@@ -17192,14 +17205,17 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
 		CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
 
 		/* Update dependency on schema if caller said so */
-		if (hasDependEntry &&
-			changeDependencyFor(RelationRelationId,
-								relOid,
-								NamespaceRelationId,
-								oldNspOid,
-								newNspOid) != 1)
-			elog(ERROR, "could not change schema dependency for relation \"%s\"",
-				 NameStr(classForm->relname));
+		if (hasDependEntry)
+		{
+			LockNotPinnedObject(NamespaceRelationId, newNspOid);
+			if (changeDependencyFor(RelationRelationId,
+									relOid,
+									NamespaceRelationId,
+									oldNspOid,
+									newNspOid) != 1)
+				elog(ERROR, "could not change schema dependency for relation \"%s\"",
+					 NameStr(classForm->relname));
+		}
 	}
 	if (!already_done)
 	{
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 95de402fa6..44b15349b3 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1018,8 +1018,6 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true;

+	/*
+	LockNotPinnedObject(ProcedureRelationId, funcoid);
 
+		LockNotPinnedObject(ConstraintRelationId, constraintOid);
+		/* XXX Do we need a lock for RelationRelationId? */
 
+			/* XXX Do we need a lock for RelationRelationId? */
+			LockNotPinnedObject(TriggerRelationId, trigoid);
 
+			LockNotPinnedObject(TriggerRelationId, parentTriggerOid);
+			/* XXX Do we need a lock for RelationRelationId? */
+			/* XXX Do we need a lock for RelationRelationId? */
 
 		ObjectAddressSet(referenced, TriggerRelationId, parentTrigId);
+		LockNotPinnedObject(TriggerRelationId, parentTrigId);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_PRI);
 
 		ObjectAddressSet(referenced, RelationRelationId, childTableId);
+		/* XXX Do we need a lock for RelationRelationId? */
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
 	}
 	else
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index b7b5019f1e..29e02f4946 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -66,12 +66,12 @@ static DefElem *buildDefItem(const char *name, const char *val,
 /* --------------------- TS Parser commands ------------------------ */
 
 /*
- * lookup a parser support function and return its OID (as a Datum)
+ * lookup a parser support function and return its OID
  *
  * attnum is the pg_ts_parser column the function will go into
  */
-static Datum
-get_ts_parser_func(DefElem *defel, int attnum)
+static Oid
+get_ts_parser_func_oid(DefElem *defel, int attnum)
 {
 	List	   *funcName = defGetQualifiedName(defel);
 	Oid			typeId[3];
@@ -125,7 +125,7 @@ get_ts_parser_func(DefElem *defel, int attnum)
 						func_signature_string(funcName, nargs, NIL, typeId),
 						format_type_be(retTypeId))));
 
-	return ObjectIdGetDatum(procOid);
+	return procOid;
 }
 
 /*
@@ -214,6 +214,7 @@ DefineTSParser(List *names, List *parameters)
 	namestrcpy(&pname, prsname);
 	values[Anum_pg_ts_parser_prsname - 1] = NameGetDatum(&pname);
 	values[Anum_pg_ts_parser_prsnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
 
 	/*
 	 * loop over the definition list and extract the information we need.
@@ -224,28 +225,38 @@ DefineTSParser(List *names, List *parameters)
 
 		if (strcmp(defel->defname, "start") == 0)
 		{
-			values[Anum_pg_ts_parser_prsstart - 1] =
-				get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart);
+			Oid			procoid = get_ts_parser_func_oid(defel, Anum_pg_ts_parser_prsstart);
+
+			values[Anum_pg_ts_parser_prsstart - 1] = ObjectIdGetDatum(procoid);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "gettoken") == 0)
 		{
-			values[Anum_pg_ts_parser_prstoken - 1] =
-				get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken);
+			Oid			procoid = get_ts_parser_func_oid(defel, Anum_pg_ts_parser_prstoken);
+
+			values[Anum_pg_ts_parser_prstoken - 1] = ObjectIdGetDatum(procoid);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "end") == 0)
 		{
-			values[Anum_pg_ts_parser_prsend - 1] =
-				get_ts_parser_func(defel, Anum_pg_ts_parser_prsend);
+			Oid			procoid = get_ts_parser_func_oid(defel, Anum_pg_ts_parser_prsend);
+
+			values[Anum_pg_ts_parser_prsend - 1] = ObjectIdGetDatum(procoid);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "headline") == 0)
 		{
-			values[Anum_pg_ts_parser_prsheadline - 1] =
-				get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline);
+			Oid			procoid = get_ts_parser_func_oid(defel, Anum_pg_ts_parser_prsheadline);
+
+			values[Anum_pg_ts_parser_prsheadline - 1] = ObjectIdGetDatum(procoid);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "lextypes") == 0)
 		{
-			values[Anum_pg_ts_parser_prslextype - 1] =
-				get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype);
+			Oid			procoid = get_ts_parser_func_oid(defel, Anum_pg_ts_parser_prslextype);
+
+			values[Anum_pg_ts_parser_prslextype - 1] = ObjectIdGetDatum(procoid);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else
 			ereport(ERROR,
@@ -474,6 +485,10 @@ DefineTSDictionary(List *names, List *parameters)
 
 	CatalogTupleInsert(dictRel, tup);
 
+	/* Lock objects */
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
+	LockNotPinnedObject(TSTemplateRelationId, templId);
+
 	address = makeDictionaryDependencies(tup);
 
 	/* Post creation hook for new text search dictionary */
@@ -601,12 +616,12 @@ AlterTSDictionary(AlterTSDictionaryStmt *stmt)
 /* ---------------------- TS Template commands -----------------------*/
 
 /*
- * lookup a template support function and return its OID (as a Datum)
+ * lookup a template support function and return its OID
  *
  * attnum is the pg_ts_template column the function will go into
  */
-static Datum
-get_ts_template_func(DefElem *defel, int attnum)
+static Oid
+get_ts_template_func_oid(DefElem *defel, int attnum)
 {
 	List	   *funcName = defGetQualifiedName(defel);
 	Oid			typeId[4];
@@ -642,7 +657,7 @@ get_ts_template_func(DefElem *defel, int attnum)
 						func_signature_string(funcName, nargs, NIL, typeId),
 						format_type_be(retTypeId))));
 
-	return ObjectIdGetDatum(procOid);
+	return procOid;
 }
 
 /*
@@ -723,6 +738,7 @@ DefineTSTemplate(List *names, List *parameters)
 	namestrcpy(&dname, tmplname);
 	values[Anum_pg_ts_template_tmplname - 1] = NameGetDatum(&dname);
 	values[Anum_pg_ts_template_tmplnamespace - 1] = ObjectIdGetDatum(namespaceoid);
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
 
 	/*
 	 * loop over the definition list and extract the information we need.
@@ -733,15 +749,19 @@ DefineTSTemplate(List *names, List *parameters)
 
 		if (strcmp(defel->defname, "init") == 0)
 		{
-			values[Anum_pg_ts_template_tmplinit - 1] =
-				get_ts_template_func(defel, Anum_pg_ts_template_tmplinit);
+			Oid			procoid = get_ts_template_func_oid(defel, Anum_pg_ts_template_tmplinit);
+
+			values[Anum_pg_ts_template_tmplinit - 1] = ObjectIdGetDatum(procoid);
 			nulls[Anum_pg_ts_template_tmplinit - 1] = false;
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "lexize") == 0)
 		{
-			values[Anum_pg_ts_template_tmpllexize - 1] =
-				get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize);
+			Oid			procoid = get_ts_template_func_oid(defel, Anum_pg_ts_template_tmpllexize);
+
+			values[Anum_pg_ts_template_tmpllexize - 1] = ObjectIdGetDatum(procoid);
 			nulls[Anum_pg_ts_template_tmpllexize - 1] = false;
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else
 			ereport(ERROR,
@@ -879,6 +899,7 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld,
 			referenced.objectId = cfgmap->mapdict;
 			referenced.objectSubId = 0;
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(TSDictionaryRelationId, cfgmap->mapdict);
 		}
 
 		systable_endscan(scan);
@@ -998,6 +1019,10 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
 	values[Anum_pg_ts_config_cfgowner - 1] = ObjectIdGetDatum(GetUserId());
 	values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid);
 
+	/* Lock objects */
+	LockNotPinnedObject(NamespaceRelationId, namespaceoid);
+	LockNotPinnedObject(TSParserRelationId, prsOid);
+
 	tup = heap_form_tuple(cfgRel->rd_att, values, nulls);
 
 	CatalogTupleInsert(cfgRel, tup);
@@ -1156,6 +1181,7 @@ ObjectAddress
 AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
 {
 	HeapTuple	tup;
+	Form_pg_ts_config cfg;
 	Oid			cfgId;
 	Relation	relMap;
 	ObjectAddress address;
@@ -1168,7 +1194,8 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
 				 errmsg("text search configuration \"%s\" does not exist",
 						NameListToString(stmt->cfgname))));
 
-	cfgId = ((Form_pg_ts_config) GETSTRUCT(tup))->oid;
+	cfg = (Form_pg_ts_config) GETSTRUCT(tup);
+	cfgId = cfg->oid;
 
 	/* must be owner */
 	if (!object_ownercheck(TSConfigRelationId, cfgId, GetUserId()))
@@ -1183,6 +1210,10 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
 	else if (stmt->tokentype)
 		DropConfigurationMapping(stmt, tup, relMap);
 
+	/* Lock dependent objects */
+	LockNotPinnedObject(NamespaceRelationId, cfg->cfgnamespace);
+	LockNotPinnedObject(TSParserRelationId, cfg->cfgparser);
+
 	/* Update dependencies */
 	makeConfigurationDependencies(tup, true, relMap);
 
@@ -1414,6 +1445,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
 				repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
 				repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
 
+				LockNotPinnedObject(TSDictionaryRelationId, dictNew);
+
 				newtup = heap_modify_tuple(maptup,
 										   RelationGetDescr(relMap),
 										   repl_val, repl_null, repl_repl);
@@ -1456,6 +1489,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
 				slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = Int32GetDatum(j + 1);
 				slot[slotCount]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictIds[j]);
 
+				LockNotPinnedObject(TSDictionaryRelationId, dictIds[j]);
+
 				ExecStoreVirtualTuple(slot[slotCount]);
 				slotCount++;
 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 2a1e713335..9febaa24a7 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1794,6 +1794,7 @@ makeRangeConstructors(const char *name, Oid namespace,
 		 * that they go away silently when the type is dropped.  Note that
 		 * pg_dump depends on this choice to avoid dumping the constructors.
 		 */
+		LockNotPinnedObject(TypeRelationId, rangeOid);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	}
 }
@@ -1859,6 +1860,7 @@ makeMultirangeConstructors(const char *name, Oid namespace,
 	 * that they go away silently when the type is dropped.  Note that pg_dump
 	 * depends on this choice to avoid dumping the constructors.
 	 */
+	LockNotPinnedObject(TypeRelationId, multirangeOid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	pfree(argtypes);
 
@@ -2672,6 +2674,45 @@ AlterDomainDefault(List *names, Node *defaultRaw)
 
 	CatalogTupleUpdate(rel, &tup->t_self, newtuple);
 
+	/* Lock dependent objects */
+	typTup = (Form_pg_type) GETSTRUCT(newtuple);
+
+	if (OidIsValid(typTup->typnamespace))
+		LockNotPinnedObject(NamespaceRelationId, typTup->typnamespace);
+
+	if (OidIsValid(typTup->typinput))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typinput);
+
+	if (OidIsValid(typTup->typoutput))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typoutput);
+
+	if (OidIsValid(typTup->typreceive))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typreceive);
+
+	if (OidIsValid(typTup->typsend))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typsend);
+
+	if (OidIsValid(typTup->typmodin))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typmodin);
+
+	if (OidIsValid(typTup->typmodout))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typmodout);
+
+	if (OidIsValid(typTup->typanalyze))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typanalyze);
+
+	if (OidIsValid(typTup->typsubscript))
+		LockNotPinnedObject(ProcedureRelationId, typTup->typsubscript);
+
+	if (OidIsValid(typTup->typbasetype))
+		LockNotPinnedObject(TypeRelationId, typTup->typbasetype);
+
+	if (OidIsValid(typTup->typcollation))
+		LockNotPinnedObject(CollationRelationId, typTup->typcollation);
+
+	if (OidIsValid(typTup->typelem))
+		LockNotPinnedObject(TypeRelationId, typTup->typelem);
+
 	/* Rebuild dependencies */
 	GenerateTypeDependencies(newtuple,
 							 rel,
@@ -4276,10 +4317,13 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 	if (oldNspOid != nspOid &&
 		(isCompositeType || typform->typtype != TYPTYPE_COMPOSITE) &&
 		!isImplicitArray)
+	{
+		LockNotPinnedObject(NamespaceRelationId, nspOid);
 		if (changeDependencyFor(TypeRelationId, typeOid,
 								NamespaceRelationId, oldNspOid, nspOid) != 1)
 			elog(ERROR, "could not change schema dependency for type \"%s\"",
 				 format_type_be(typeOid));
+	}
 
 	InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);
 
@@ -4571,6 +4615,7 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
 	SysScanDesc scan;
 	ScanKeyData key[1];
 	HeapTuple	domainTup;
+	Form_pg_type typeForm;
 
 	/* Since this function recurses, it could be driven to stack overflow */
 	check_stack_depth();
@@ -4619,6 +4664,45 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
 	newtup = heap_modify_tuple(tup, RelationGetDescr(catalog),
 							   values, nulls, replaces);
 
+	/* Lock dependent objects */
+	typeForm = (Form_pg_type) GETSTRUCT(newtup);
+
+	if (OidIsValid(typeForm->typnamespace))
+		LockNotPinnedObject(NamespaceRelationId, typeForm->typnamespace);
+
+	if (OidIsValid(typeForm->typinput))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typinput);
+
+	if (OidIsValid(typeForm->typoutput))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typoutput);
+
+	if (OidIsValid(typeForm->typreceive))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typreceive);
+
+	if (OidIsValid(typeForm->typsend))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typsend);
+
+	if (OidIsValid(typeForm->typmodin))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typmodin);
+
+	if (OidIsValid(typeForm->typmodout))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typmodout);
+
+	if (OidIsValid(typeForm->typanalyze))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typanalyze);
+
+	if (OidIsValid(typeForm->typsubscript))
+		LockNotPinnedObject(ProcedureRelationId, typeForm->typsubscript);
+
+	if (OidIsValid(typeForm->typbasetype))
+		LockNotPinnedObject(TypeRelationId, typeForm->typbasetype);
+
+	if (OidIsValid(typeForm->typcollation))
+		LockNotPinnedObject(CollationRelationId, typeForm->typcollation);
+
+	if (OidIsValid(typeForm->typelem))
+		LockNotPinnedObject(TypeRelationId, typeForm->typelem);
+
 	CatalogTupleUpdate(catalog, &newtup->t_self, newtup);
 
 	/* Rebuild dependencies for this type */
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 6cc9a8d8bf..ccd03ceeb8 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -155,6 +155,7 @@ InsertRule(const char *rulname,
 	referenced.objectId = eventrel_oid;
 	referenced.objectSubId = 0;
 
+	/* XXX Do we need a lock for RelationRelationId? */
 	recordDependencyOn(&myself, &referenced,
 					   (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);

 
 Section: Class 2D - Invalid Transaction Termination
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 7eee66f810..c57204cc40 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -101,6 +101,9 @@ typedef struct ObjectAddresses ObjectAddresses;
 /* in dependency.c */
 
 extern void AcquireDeletionLock(const ObjectAddress *object, int flags);
+extern void LockNotPinnedObjectById(const ObjectAddress *object);
+extern void LockNotPinnedObjectsById(const ObjectAddress *object, int nobject);
+extern void LockNotPinnedObject(Oid classid, Oid objid);

@@ -128,6 +131,9 @@ extern void add_exact_object_address(const ObjectAddress *object,
 extern bool object_address_present(const ObjectAddress *object,
 								   const ObjectAddresses *addrs);
 
+extern void lock_record_object_address_dependencies(const ObjectAddress *depender,
+													ObjectAddresses *referenced,
+													DependencyType behavior);
 extern void record_object_address_dependencies(const ObjectAddress *depender,
 											   ObjectAddresses *referenced,
 											   DependencyType behavior);
@@ -172,6 +178,7 @@ extern long changeDependenciesOf(Oid classId, Oid oldObjectId,
 extern long changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
 								 Oid newRefObjectId);

---

From: Robert Haas <[email-masked]>
Date: Mon, 17 Jun 2024 12:24:46 -0400

> While at it, let's look at a full example and focus on your concern.

I'm not at the point of having a concern yet, honestly. I'm trying to
understand the design ideas. The commit message just says that we take
a conflicting lock, but it doesn't mention which object types that
principle does or doesn't apply to. I think the idea of skipping it
for cases where it's redundant with the relation lock could be the
right idea, but if that's what we're doing, don't we need to explain
the principle somewhere? And shouldn't we also apply it across all
object types that have the same property?

Along the same lines:

+ /*

"It looks like X cannot happen" is not confidence-inspiring. At the
very least, a better comment is needed here. But also, that relation
has no exception for AuthMemRelationId, only for RelationRelationId.
And also, the exception for RelationRelationId doesn't imply that we
don't need a conflicting lock here: the special case for
RelationRelationId in AcquireDeletionLock() is necessary because the
lock tag format is different for relations than for other database
objects, not because we don't need a lock at all. If the handling here
were really symmetric with what AcquireDeletionLock(), the coding
would be to call either LockRelationOid() or LockDatabaseObject()
depending on whether classid == RelationRelationId. Now, that isn't
actually necessary, because we already have relation-locking calls
elsewhere, but my point is that the rationale this commit gives for
WHY it isn't necessary seems to me to be wrong in multiple ways.

So to try to sum up here: I'm not sure I agree with this design. But I
also feel like the design is not as clear and consistently implemented
as it could be. So even if I just ignored the question of whether it's
the right design, it feels like we're a ways from having something
potentially committable here, because of issues like the ones I
mentioned in the last paragraph.

---

From: Bertrand Drouvot <[email-masked]>
Date: Mon, 17 Jun 2024 17:57:05 +0000

Hi,

> object types that have the same property?

Yeah, I still need to deeply study this area and document it. 

> "It looks like X cannot happen" is not confidence-inspiring.

Yeah, it is not. It is just a "feeling" that I need to work on to remove
any ambiguity and/or adjust the code as needed.

> At the
> depending on whether classid == RelationRelationId.

Agree.

> Now, that isn't

Agree. I'm not done with that part yet (should have made it more clear).

> mentioned in the last paragraph.

Agree. I'll now move on with the "XXX Do we need a lock for RelationRelationId?"
comments that I put in v10 (see [1]) and study all the cases around them.

Once done, I think that it will easier to 1.remove ambiguity, 2.document and
3.do the "right" thing regarding the RelationRelationId object class.

[1]: https://www.postgresql.org/message-id/ZnAVEBhlGvpDDVOD%40ip-10-97-1-34.eu-west-3.compute.internal

Regards,

---

From: Ashutosh Sharma <[email-masked]>
Date: Wed, 19 Jun 2024 17:19:28 +0530

Hi,

If the dependency is more, this can hit max_locks_per_transaction
limit very fast. Won't it? I just tried this little experiment with
and without patch.

1) created some UDTs (I have just chosen some random number, 15)
do $$
declare
    i int := 1;
    type_name text;
begin
    while i <= 15 loop
        type_name := format('ct_%s', i);

        -- check if the type already exists
        if not exists (
            select 1
            from pg_type
            where typname = type_name
        ) then
            execute format('create type %I as (f1 INT, f2 TEXT);', type_name);
        end if;

        i := i + 1;
    end loop;
end $$;

2) started a transaction and tried creating a table that uses all udts
created above:
begin;
create table dep_tab(a ct_1, b ct_2, c ct_3, d ct_4, e ct_5, f ct_6, g
ct_7, h ct_8, i ct_9, j ct_10, k ct_11, l ct_12, m ct_13, n ct_14, o
ct_15);

3) checked the pg_locks entries inside the transaction both with and
without patch:

-- with patch:
select count(*) from pg_locks;
 count
-------
    23
(1 row)

-- without patch:
select count(*) from pg_locks;
 count
-------
     7
(1 row)

With patch, it increased by 3 times. Won't that create a problem if
many concurrent sessions engage in similar activity?

--
With Regards,
Ashutosh Sharma.

---

From: Robert Haas <[email-masked]>
Date: Wed, 19 Jun 2024 08:20:08 -0400

On Wed, Jun 19, 2024 at 7:49 AM Ashutosh Sharma <[email-masked]> wrote:
> limit very fast.

Your experiment doesn't support this conclusion. Very few users would
have 15 separate user-defined types in the same table, and even if
they did, and dropped the table, using 23 locks is no big deal. By
default, max_locks_per_transaction is 64, so the user would need to
have more like 45 separate user-defined types in the same table in
order to use more than 64 locks. So, yes, it is possible that if every
backend in the system were simultaneously trying to drop a table and
all of those tables had an average of at least 45 or so user-defined
types, all different from each other, you might run out of lock table
space.

But probably nobody will ever do that in real life, and if they did,
they could just raise max_locks_per_transaction.

When posting about potential problems like this, it is a good idea to
first do a careful thought experiment to assess how realistic the
problem is. I would consider an issue like this serious if there were
a realistic scenario under which a small number of backends could
exhaust the lock table for the whole system, but I think you can see
that this isn't the case here. Even your original scenario is more
extreme than what most people are likely to hit in real life, and it
only uses 23 locks.

---

From: Ashutosh Sharma <[email-masked]>
Date: Wed, 19 Jun 2024 19:05:36 +0530

Hi Robert,

> extreme than what most people are likely to hit in real life, and it

I agree that based on the experiment I shared (which is somewhat
unrealistic), this doesn't seem to have any significant implications.
However, I was concerned that it could potentially increase the usage
of max_locks_per_transaction, which is why I wanted to mention it
here. Nonetheless, my experiment did not reveal any serious issues
related to this. Sorry for the noise.

--
With Regards,
Ashutosh Sharma.

---

From: Bertrand Drouvot <[email-masked]>
Date: Wed, 19 Jun 2024 14:11:50 +0000

Hi,

> comments that I put in v10 (see [1]) and study all the cases around them.

A. I went through all of them, did some testing for all, and reached the
conclusion that we must be in one of the two following cases that would already
prevent the relation to be dropped:

1. The relation is already locked (could be an existing relation or a relation
that we are creating).

2. The relation is protected indirectly (i.e an index protected by a lock on
its table, a table protected by a lock on a function that depends of
the table...).

So we don't need to add a lock if this is a RelationRelationId object class for
the cases above.

As a consequence, I replaced the "XXX" related comments that were in v10 by
another set of comments in v11 (attached) like "No need to call LockRelationOid()
(through LockNotPinnedObject())....". Reason is to make it clear in the code
and also to ease the review.

B. I explained in [1] (while sharing v10) that the object locking is now outside
of the dependency code except for (and I explained why):

recordDependencyOnExpr() 

So I also did some testing, on the RelationRelationId case, for those and I
reached the same conclusion as the one shared above.

For A. and B. the testing has been done by adding a "ereport(WARNING.." at
those places when a RelationRelationId is involved. Then I run "make check"
and went to the failed tests (output were not the expected ones due to the
extra "WARNING"), reproduced them with gdb and checked for the lock on the
relation producing the "WARNING". All of those were linked to 1. or 2.

Note that adding an assertion on an existing lock would not work for the cases
described in 2.

So, I'm now confident that we must be in 1. or 2. but it's also possible
that I've missed some cases (though I think the way the testing has been done is
not that weak).

To sum up, I did not see any cases that did not lead to 1. or 2., so I think
it's safe to not add an extra lock for the RelationRelationId case. If, for any
reason, there is still cases that are outside 1. or 2. then they may lead to
orphaned dependencies linked to the RelationRelationId class. I think that's
fine to take that "risk" given that a. that would not be worst than currently
and b. I did not see any of those in our fleet currently (while I have seen a non
negligible amount outside of the RelationRelationId case).

> 3.do the "right" thing regarding the RelationRelationId object class.

Please find attached v11, where I added more detailed comments in the commit
message and also in the code (I also removed the useless check on
AuthMemRelationId).

Regards,

>From 50e11432960e0ad5d940d2e7d9557fc4770d8262 Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <[email-masked]>
Date: Fri, 29 Mar 2024 15:43:26 +0000
Subject: [PATCH v11] Avoid orphaned objects dependencies

Scenario 1:

Scenario 2:

The patch takes into account any type of objects except:

- the ones that are pinned
- the ones that belongs to the RelationRelationId class

For the former, that's due to the fact that pinned objects are not droppable
because the system requires it.

For the later, that because we must be in one of the two following cases that
would already prevent the relation to be dropped:

1. The relation is already locked (could be an existing relation or a relation
that we are creating).

2. The relation is protected indirectly (i.e an index protected by a lock on
its table, a table protected by a lock on a function that depends the table...)

- schema and function (as the above scenarios)
- alter a dependency (function and schema)
- function and arg type
- function and return type
- function and function
- domain and domain
- table and type
- server and foreign data wrapper
---
 src/backend/catalog/aclchk.c                  |   1 +
 src/backend/catalog/dependency.c              | 115 +++++++++++++++-
 src/backend/catalog/heap.c                    |   7 +
 src/backend/catalog/index.c                   |  40 ++++++
 src/backend/catalog/objectaddress.c           |  57 ++++++++
 src/backend/catalog/pg_aggregate.c            |   9 ++
 src/backend/catalog/pg_attrdef.c              |   4 +
 src/backend/catalog/pg_cast.c                 |   5 +
 src/backend/catalog/pg_collation.c            |   1 +
 src/backend/catalog/pg_constraint.c           |  37 +++++
 src/backend/catalog/pg_conversion.c           |   2 +
 src/backend/catalog/pg_depend.c               |  26 +++-
 src/backend/catalog/pg_operator.c             |  19 +++
 src/backend/catalog/pg_proc.c                 |  17 ++-
 src/backend/catalog/pg_publication.c          |  13 ++
 src/backend/catalog/pg_range.c                |   6 +
 src/backend/catalog/pg_type.c                 |  38 ++++++
 src/backend/catalog/toasting.c                |   4 +
 src/backend/commands/alter.c                  |   4 +
 src/backend/commands/amcmds.c                 |   1 +
 src/backend/commands/cluster.c                |  13 ++
 src/backend/commands/event_trigger.c          |   1 +
 src/backend/commands/extension.c              |   5 +
 src/backend/commands/foreigncmds.c            |   7 +
 src/backend/commands/functioncmds.c           |   6 +
 src/backend/commands/indexcmds.c              |   2 +
 src/backend/commands/opclasscmds.c            |  18 +++
 src/backend/commands/operatorcmds.c           |  30 ++++
 src/backend/commands/policy.c                 |   8 ++
 src/backend/commands/proclang.c               |   3 +
 src/backend/commands/sequence.c               |   5 +
 src/backend/commands/statscmds.c              |  11 ++
 src/backend/commands/tablecmds.c              |  43 ++++--
 src/backend/commands/trigger.c                |  39 +++++-
 src/backend/commands/tsearchcmds.c            |  81 +++++++----
 src/backend/commands/typecmds.c               |  84 ++++++++++++
 src/backend/rewrite/rewriteDefine.c           |   4 +
 src/backend/utils/errcodes.txt                |   1 +
 src/include/catalog/dependency.h              |   7 +
 src/include/catalog/objectaddress.h           |   1 +
 .../expected/test_dependencies_locks.out      | 129 ++++++++++++++++++
 src/test/isolation/isolation_schedule         |   1 +
 .../specs/test_dependencies_locks.spec        |  89 ++++++++++++
 43 files changed, 947 insertions(+), 47 deletions(-)
  33.4% src/backend/catalog/
  36.6% src/backend/commands/
  17.1% src/test/isolation/expected/
  10.6% src/test/isolation/specs/

+				LockNotPinnedObject(NamespaceRelationId, iacls->nspid);
 				recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 			}
 		}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0489cbabcb..b779872c3e 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1519,6 +1519,90 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
 	}
 }
 
+/*
+ * LockNotPinnedObjectById
+ *
+ * Lock the object that we are about to record a dependency on.
+ * After it's locked, verify that it hasn't been dropped while we
+ * weren't looking.  If the object has been dropped, this function
+ * does not return!
+ */
+void
+LockNotPinnedObjectById(const ObjectAddress *object)
+{
+	char	   *object_description;
+
+	if (isObjectPinned(object))
+		return;
+
+	/*
+	 * We must be in one of the two following cases that would already prevent
+	 * the relation to be dropped: 1. The relation is already locked (could be
+	 * an existing relation or a relation that we are creating). 2. The
+	 * relation is protected indirectly (i.e an index protected by a lock on
+	 * its table, a table protected by a lock on a function that depends of
+	 * the table...). So don't add a lock if this is a RelationRelationId
+	 * object class (would need to make use of LockRelationOid() though). Note
+	 * that, due to 2., adding an assertion that we already hold a lock on the
+	 * relation is not doable.
+	 */
+	if (object->classId == RelationRelationId)
+		return;
+
+	object_description = getObjectDescription(object, true);
+
+	/* assume we should lock the whole object not a sub-object */
+	LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock);
+
+	/* check if object still exists */
+	if (!ObjectByIdExist(object))
+	{
+		if (object_description)
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+					 errmsg("%s does not exist", object_description)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+					 errmsg("a dependent object does not exist")));
+	}
+
+	if (object_description)
+		pfree(object_description);
+
+	return;
+}
+
+void
+LockNotPinnedObjectsById(const ObjectAddress *object, int nobject)
+{
+	int			i;
+
+	if (nobject < 0)
+		return;
+
+	for (i = 0; i < nobject; i++, object++)
+		LockNotPinnedObjectById(object);
+
+	return;
+}
+
+
+/*
+ * LockNotPinnedObject
+ *
+ * Lock the object that we are about to record a dependency on.
+ */
+void
+LockNotPinnedObject(Oid classid, Oid objid)
+{
+	ObjectAddress object;
+
+	ObjectAddressSet(object, classid, objid);
+
+	LockNotPinnedObjectById(&object);
+}
+
 /*
  * ReleaseDeletionLock - release an object deletion lock
  *
@@ -1564,13 +1648,8 @@ recordDependencyOnExpr(const ObjectAddress *depender,
 	/* Scan the expression tree for referenceable objects */
 	find_expr_references_walker(expr, &context);

 	free_object_addresses(context.addrs);
 }
@@ -1654,14 +1733,19 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,

+			LockNotPinnedObjectById(depender);
+
 			for (selfref = 0; selfref < self_addrs->numrefs; selfref++)
 			{
 				ObjectAddress *thisobj = self_addrs->refs + selfref;
@@ -1674,6 +1758,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
 	}
 
@@ -2734,6 +2819,22 @@ stack_address_present_add_flags(const ObjectAddress *object,
 	return result;
 }
 
+/*
index a122bbffce..00977e56c4 100644

 
 		if (reloftypeid)
 		{
 
 		/*

 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 55fdde4b24..445ea5a846 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1127,6 +1127,12 @@ index_create(Relation heapRelation,
 										heapRelationId,
 										indexInfo->ii_IndexAttrNumbers[i]);
 					add_exact_object_address(&referenced, addrs);
+
+					/*
+					 * No need to call LockRelationOid() (through
+					 * LockNotPinnedObject()) on heapRelationId as it is
+					 * already locked.
+					 */
 					have_simple_col = true;
 				}
 			}
@@ -1142,6 +1148,12 @@ index_create(Relation heapRelation,
 				ObjectAddressSet(referenced, RelationRelationId,
 								 heapRelationId);
 				add_exact_object_address(&referenced, addrs);
+
+				/*
+				 * No need to call LockRelationOid() (through
+				 * LockNotPinnedObject()) on heapRelationId as it is already
+				 * locked.
+				 */
 			}
 
 			record_object_address_dependencies(&myself, addrs, DEPENDENCY_AUTO);
@@ -1157,9 +1169,21 @@ index_create(Relation heapRelation,
 		if (OidIsValid(parentIndexRelid))
 		{
 			ObjectAddressSet(referenced, RelationRelationId, parentIndexRelid);
+
+			/*
+			 * No need to call LockRelationOid() (through
+			 * LockNotPinnedObject()) on parentIndexRelid as it is already
+			 * locked.
+			 */
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 
 			ObjectAddressSet(referenced, RelationRelationId, heapRelationId);
+
+			/*
+			 * No need to call LockRelationOid() (through
+			 * LockNotPinnedObject()) on heapRelationId as it is already
+			 * locked.
+			 */
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 		}
 
@@ -1175,6 +1199,7 @@ index_create(Relation heapRelation,
 			{
 				ObjectAddressSet(referenced, CollationRelationId, collationIds[i]);
 				add_exact_object_address(&referenced, addrs);
+				LockNotPinnedObject(CollationRelationId, collationIds[i]);
 			}
 		}
 
@@ -1183,6 +1208,7 @@ index_create(Relation heapRelation,
 		{
 			ObjectAddressSet(referenced, OperatorClassRelationId, opclassIds[i]);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(OperatorClassRelationId, opclassIds[i]);
 		}
 
 		record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
@@ -1987,6 +2013,14 @@ index_constraint_create(Relation heapRelation,
 	 */
 	ObjectAddressSet(myself, ConstraintRelationId, conOid);
 	ObjectAddressSet(idxaddr, RelationRelationId, indexRelationId);
+
+	/*
+	 * CommandCounterIncrement() here to ensure the new constraint entry is
+	 * visible when LockNotPinnedObject() will check its existence before
+	 * recording the dependencies.
+	 */
+	CommandCounterIncrement();
+	LockNotPinnedObject(ConstraintRelationId, conOid);
 	recordDependencyOn(&idxaddr, &myself, DEPENDENCY_INTERNAL);
 
 	/*
@@ -1998,9 +2032,15 @@ index_constraint_create(Relation heapRelation,
 		ObjectAddress referenced;
 
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstraintId);
+		LockNotPinnedObject(ConstraintRelationId, parentConstraintId);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 		ObjectAddressSet(referenced, RelationRelationId,
 						 RelationGetRelid(heapRelation));
+
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on heapRelation as it is already locked.
+		 */
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 	}

+/*
+ * ObjectByIdExist
+ *

 

 

 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/catalog/pg_attrdef.c b/src/backend/catalog/pg_attrdef.c
index 003ae70b4d..21491613e3 100644
--- a/src/backend/catalog/pg_attrdef.c
+++ b/src/backend/catalog/pg_attrdef.c
@@ -178,6 +178,10 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
 	colobject.objectId = RelationGetRelid(rel);
 	colobject.objectSubId = attnum;
 
+	/*
+	 * No need to call LockRelationOid() (through LockNotPinnedObject()) on
+	 * rel as it is already locked.
+	 */
 	recordDependencyOn(&defobject, &colobject,
 					   attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);

 

 
 	/* create dependency on owner */
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 3baf9231ed..c7c1115bec 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -257,12 +257,22 @@ CreateConstraintEntry(const char *constraintName,
 				ObjectAddressSubSet(relobject, RelationRelationId, relId,
 									constraintKey[i]);
 				add_exact_object_address(&relobject, addrs_auto);
+
+				/*
+				 * No need to call LockRelationOid() (through
+				 * LockNotPinnedObject()) on relId as it is already locked.
+				 */
 			}
 		}
 		else
 		{
 			ObjectAddressSet(relobject, RelationRelationId, relId);
 			add_exact_object_address(&relobject, addrs_auto);
+
+			/*
+			 * No need to call LockRelationOid() (through
+			 * LockNotPinnedObject()) on relId as it is already locked.
+			 */
 		}
 	}
 
@@ -275,6 +285,7 @@ CreateConstraintEntry(const char *constraintName,

 	record_object_address_dependencies(&conobject, addrs_auto,
@@ -299,12 +310,23 @@ CreateConstraintEntry(const char *constraintName,
 				ObjectAddressSubSet(relobject, RelationRelationId,
 									foreignRelId, foreignKey[i]);
 				add_exact_object_address(&relobject, addrs_normal);
+
+				/*
+				 * No need to call LockRelationOid() (through
+				 * LockNotPinnedObject()) on foreignRelId as it is already
+				 * locked.
+				 */
 			}
 		}
 		else
 		{
 			ObjectAddressSet(relobject, RelationRelationId, foreignRelId);
 			add_exact_object_address(&relobject, addrs_normal);
+
+			/*
+			 * No need to call LockRelationOid() (through
+			 * LockNotPinnedObject()) on foreignRelId as it is already locked.
+			 */
 		}
 	}
 
@@ -320,6 +342,12 @@ CreateConstraintEntry(const char *constraintName,
 
 		ObjectAddressSet(relobject, RelationRelationId, indexRelId);
 		add_exact_object_address(&relobject, addrs_normal);
+
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on indexRelId as it can't be dropped as its table is already
+		 * locked.
+		 */
 	}
 
 	if (foreignNKeys > 0)
@@ -339,15 +367,18 @@ CreateConstraintEntry(const char *constraintName,
 		{
@@ -858,9 +889,15 @@ ConstraintSetParentConstraint(Oid childConstrId,

+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on childTableId as it is already locked.

index cfd7ef51df..f6f9764ec9 100644

@@ -100,6 +99,24 @@ recordMultipleDependencies(const ObjectAddress *depender,
@@ -239,6 +256,7 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
 
@@ -706,7 +724,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,

 
index 528c17cd7f..116e524390 100644
 
+	/*
+	 * CommandCounterIncrement() here to ensure the new function entry is
+	 * visible when LockNotPinnedObject() will check its existence before
+	 * recording the dependencies.
+	 */
+	CommandCounterIncrement();
+
 	addrs = new_object_addresses();

 

 

 
-		/* Advance command counter so new tuple can be seen by validator */
-		CommandCounterIncrement();
-
 		/*
 		 * Set per-function configuration parameters so that the validation is
 		 * done with the environment the function expects.  However, if
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 0602398a54..e546c0a6fa 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -438,10 +438,16 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,

 	/* Add dependency on the relation */
 	ObjectAddressSet(referenced, RelationRelationId, relid);
+
+	/*
+	 * No need to call LockRelationOid() (through LockNotPinnedObject()) on
+	 * relid as it is already locked.
+	 */
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Add dependency on the objects mentioned in the qualifications */
@@ -454,6 +460,11 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 	for (int i = 0; i < natts; i++)
 	{
 		ObjectAddressSubSet(referenced, RelationRelationId, relid, attarray[i]);
+
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on relid as it is already locked.
+		 */
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -661,10 +672,12 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)

 
 	/* Close the table */

 

 
 
index 395dec8ed8..5b36ff383a 100644
 
 	/* Post creation hook for new shell type */
 	InvokeObjectPostCreateHook(TypeRelationId, typoid, 0);
@@ -494,6 +501,36 @@ TypeCreate(Oid newTypeOid,
 	 * Create dependencies.  We can/must skip this in bootstrap mode.
 	 */
 	if (!IsBootstrapProcessingMode())
+	{
+		/*
+		 * CommandCounterIncrement() here to ensure the new type entry is
+		 * visible when LockNotPinnedObject() will check its existence before
+		 * recording the dependencies.
+		 */
+		CommandCounterIncrement();
+
+		/* Lock dependent objects */
+		LockNotPinnedObject(NamespaceRelationId, typeNamespace);
+		LockNotPinnedObject(ProcedureRelationId, inputProcedure);
+		LockNotPinnedObject(ProcedureRelationId, outputProcedure);
+		LockNotPinnedObject(ProcedureRelationId, receiveProcedure);
+		LockNotPinnedObject(ProcedureRelationId, sendProcedure);
+		LockNotPinnedObject(ProcedureRelationId, typmodinProcedure);
+		LockNotPinnedObject(ProcedureRelationId, typmodoutProcedure);
+		LockNotPinnedObject(ProcedureRelationId, analyzeProcedure);
+		LockNotPinnedObject(ProcedureRelationId, subscriptProcedure);
+		LockNotPinnedObject(TypeRelationId, baseType);
+		LockNotPinnedObject(CollationRelationId, typeCollation);
+		LockNotPinnedObject(TypeRelationId, elementType);
+
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on relationOid as relationOid is set to an InvalidOid or to a new
+		 * Oid.
+		 */
+		if (relationKind == RELKIND_COMPOSITE_TYPE)
+			LockNotPinnedObject(TypeRelationId, typeObjectId);
+
 		GenerateTypeDependencies(tup,
 								 pg_type_desc,
 								 (defaultTypeBin ?
@@ -505,6 +542,7 @@ TypeCreate(Oid newTypeOid,
 								 isDependentType,
 								 true,	/* make extension dependency */
 								 rebuildDeps);
+	}
 
 	/* Post creation hook for new type */
 	InvokeObjectPostCreateHook(TypeRelationId, typeObjectId, 0);
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 738bc46ae8..6705bc24b8 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -367,6 +367,10 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 		toastobject.objectId = toast_relid;
 		toastobject.objectSubId = 0;
 
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on relOid as it is already locked.
+		 */
 		recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
 	}

 	return address;

 
index 78f96789b0..5d8b2948d8 100644
@@ -1381,6 +1384,11 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
+				 * No need to call LockRelationOid() (through
+				 * LockNotPinnedObject()) on r1 as it is already locked.
@@ -1389,6 +1397,11 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
+				 * No need to call LockRelationOid() (through
+				 * LockNotPinnedObject()) on r2 as it is already locked.

 

 

 
 		/*

 

 	if (OidIsValid(useId))

 
 	/* dependency on type */

index b8b5c147c5..e70afd216c 100644

 

 
 
 		heap_freetuple(tup);
 
+		/*
+		 * CommandCounterIncrement() here to ensure the new operator entry is
+		 * visible when LockNotPinnedObject() will check its existence before
+		 * recording the dependencies.
+		 */
+		CommandCounterIncrement();
+
 		/* Make its dependencies */
 		myself.classId = AccessMethodOperatorRelationId;
 		myself.objectId = entryoid;
@@ -1496,6 +1509,7 @@ storeOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
 		referenced.objectSubId = 0;

 

 

 

 
+
 
 	if (OidIsValid(commutatorOid) || OidIsValid(negatorOid))
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index 6ff3eba824..f04e87ee5d 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -722,6 +722,10 @@ CreatePolicy(CreatePolicyStmt *stmt)
 	myself.objectId = policy_id;
 	myself.objectSubId = 0;
 
+	/*
+	 * No need to call LockRelationOid() (through LockNotPinnedObject()) on
+	 * table_id as it is already locked.
+	 */
 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);
 
 	recordDependencyOnExpr(&myself, qual, qual_pstate->p_rtable,
@@ -1053,6 +1057,10 @@ AlterPolicy(AlterPolicyStmt *stmt)
 	myself.objectId = policy_id;
 	myself.objectSubId = 0;
 
+	/*
+	 * No need to call LockRelationOid() (through LockNotPinnedObject()) on
+	 * table_id as it is already locked.
+	 */
 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);

 
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 28f8522264..80c23d98a5 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1681,6 +1681,11 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
 		depobject.classId = RelationRelationId;
 		depobject.objectId = RelationGetRelid(seqrel);
 		depobject.objectSubId = 0;
+
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on tablerel as it is already locked.
+		 */
 		recordDependencyOn(&depobject, &refobject, deptype);
 	}
 
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 1db3ef69d2..23a34aeb79 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -536,6 +536,11 @@ CreateStatistics(CreateStatsStmt *stmt)
 	for (i = 0; i < nattnums; i++)
 	{
 		ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
+
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on relid as it is already locked.
+		 */
 		recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
 	}
 
@@ -553,6 +558,11 @@ CreateStatistics(CreateStatsStmt *stmt)
 	if (!nattnums)
 	{
 		ObjectAddressSet(parentobject, RelationRelationId, relid);
+
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on relid as it is already locked.
+		 */
 		recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
 	}
 
@@ -573,6 +583,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 
 	recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 66cda26a25..4b5fcca399 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3438,6 +3438,10 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid,
 	childobject.objectId = relationId;
 	childobject.objectSubId = 0;
 
+	/*
+	 * No need to call LockRelationOid() (through LockNotPinnedObject()) on
+	 * parentOid as it is already locked.
+	 */
 	recordDependencyOn(&childobject, &parentobject,
 					   child_dependency_type(child_is_partition));
 
@@ -7349,7 +7353,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/*
 
 	/*
@@ -10174,6 +10180,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
 		ObjectAddress referenced;

@@ -10465,8 +10472,14 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
 			 */
 			ObjectAddressSet(address, ConstraintRelationId, constrOid);
 			ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
+			LockNotPinnedObject(ConstraintRelationId, parentConstr);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
 			ObjectAddressSet(referenced, RelationRelationId, partitionId);
+
+			/*
+			 * No need to call LockRelationOid() (through
+			 * LockNotPinnedObject()) on partitionId as it is already locked.
+			 */
 			recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
 
 			/* Make all this visible before recursing */
@@ -10967,9 +10980,15 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
 		/* Set up partition dependencies for the new constraint */
 		ObjectAddressSet(address, ConstraintRelationId, constrOid);
 		ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
+		LockDatabaseObject(ConstraintRelationId, parentConstrOid, 0, AccessShareLock);
 		recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
 		ObjectAddressSet(referenced, RelationRelationId,
 						 RelationGetRelid(partRel));
+
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on partRel as it is already locked.
+		 */
 		recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
 
 		/* Done with the cloned constraint's tuple */
@@ -13254,7 +13273,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	table_close(attrelation, RowExclusiveLock);

 	/*
@@ -14816,6 +14837,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 		 */
 		ObjectAddressSet(relobj, RelationRelationId, reloid);
 		ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
+		LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
 		recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
 	}
 	else if (OidIsValid(oldAccessMethodId) &&
@@ -14835,6 +14857,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 			   OidIsValid(rd_rel->relam));
 
@@ -16434,6 +16457,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 
 	/* Update pg_class.reloftype */
@@ -17192,14 +17216,17 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
 		CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
 
index 95de402fa6..99433cce75 100644
 
@@ -1027,6 +1025,13 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 
+	/*
+	 * CommandCounterIncrement() here to ensure the new trigger entry is
+	 * visible when LockNotPinnedObject() will check its existence before
+	 * recording the dependencies.
+	 */
+	CommandCounterIncrement();
+
 	/*
 	 * If we're replacing a trigger, flush all the old dependencies before
 	 * recording new ones.
@@ -1045,6 +1050,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	referenced.classId = ProcedureRelationId;
 	referenced.objectId = funcoid;
 	referenced.objectSubId = 0;
+	LockNotPinnedObject(ProcedureRelationId, funcoid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 	if (isInternal && OidIsValid(constraintOid))
@@ -1058,6 +1064,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		referenced.classId = ConstraintRelationId;
 		referenced.objectId = constraintOid;
 		referenced.objectSubId = 0;
+		LockNotPinnedObject(ConstraintRelationId, constraintOid);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
 	}
 	else
@@ -1070,6 +1077,11 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		referenced.classId = RelationRelationId;
 		referenced.objectId = RelationGetRelid(rel);
 		referenced.objectSubId = 0;
+
+		/*
+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on rel as it is already locked.
+		 */
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 		if (OidIsValid(constrrelid))
@@ -1077,6 +1089,11 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 			referenced.classId = RelationRelationId;
 			referenced.objectId = constrrelid;
 			referenced.objectSubId = 0;
+
+			/*
+			 * No need to call LockRelationOid() (through
+			 * LockNotPinnedObject()) on constrrelid as it is already locked.
+			 */
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 		}
 		/* Not possible to have an index dependency in this case */
@@ -1091,6 +1108,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 			referenced.classId = ConstraintRelationId;
 			referenced.objectId = constraintOid;
 			referenced.objectSubId = 0;
+			LockNotPinnedObject(TriggerRelationId, trigoid);
 			recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
 		}
 
@@ -1100,8 +1118,14 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		if (OidIsValid(parentTriggerOid))
 		{
 			ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid);
+			LockNotPinnedObject(TriggerRelationId, parentTriggerOid);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 			ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
+
+			/*
+			 * No need to call LockRelationOid() (through
+			 * LockNotPinnedObject()) on rel as it is already locked.
+			 */
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 		}
 	}
@@ -1116,6 +1140,11 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		for (i = 0; i < ncolumns; i++)
 		{
 			referenced.objectSubId = columns[i];
+
+			/*
+			 * No need to call LockRelationOid() (through
+			 * LockNotPinnedObject()) on rel as it is already locked.
+			 */
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 	}
@@ -1255,9 +1284,15 @@ TriggerSetParentTrigger(Relation trigRel,
 		ObjectAddressSet(depender, TriggerRelationId, childTrigId);

+		 * No need to call LockRelationOid() (through LockNotPinnedObject())
+		 * on childTableId as it is already locked.
 
 /*

 /*
 
 	/*

 
+	/* Lock objects */

 /*

 /*
 
 	/*

 		systable_endscan(scan);
 
+	/* Lock objects */

 
 	/* must be owner */

 

 

 

 
 
 	/* Rebuild dependencies for this type */
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index 6cc9a8d8bf..06f6c952e3 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -155,6 +155,10 @@ InsertRule(const char *rulname,
 	referenced.objectId = eventrel_oid;
 	referenced.objectSubId = 0;
 
+	/*
+	 * No need to call LockRelationOid() (through LockNotPinnedObject()) on
+	 * eventrel_oid as it is already locked.
+	 */
 	recordDependencyOn(&myself, &referenced,
 					   (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);

---

From: Bertrand Drouvot <[email-masked]>
Date: Fri, 21 Jun 2024 13:22:43 +0000

Hi,

> negligible amount outside of the RelationRelationId case).

Another thought for the RelationRelationId class case: we could check if there
is a lock first and if there is no lock then acquire one. That way that would
ensure the relation is always locked (so no "risk" anymore), but OTOH it may
add "unecessary" locking (see 2. mentioned previously).

I think I do prefer this approach to be on the safe side of thing, what do
you think?

Regards,

---

From: Bertrand Drouvot <[email-masked]>
Date: Wed, 26 Jun 2024 10:24:41 +0000

Hi,

> add "unecessary" locking (see 2. mentioned previously).

Please find attached v12 implementing this idea for the RelationRelationId class
case. As mentioned, it may add unnecessary locking for 2. but I think that's
worth it to ensure that we are always on the safe side of thing. This idea is
implemented in LockNotPinnedObjectById().

A few remarks:

- there is one place where the relation is not visible (even if
CommandCounterIncrement() is used). That's in TypeCreate(), because the new 
relation Oid is _not_ added to pg_class yet.
Indeed, in heap_create_with_catalog(), AddNewRelationType() is called before
AddNewRelationTuple()). I put a comment in this part of the code explaining why
it's not necessary to call LockRelationOid() here.

- some namespace related stuff is removed from "test_oat_hooks/expected/alter_table.out".
That's due to the logic in cachedNamespacePath() and the fact that the same
namespace related stuff is added prior in alter_table.out.

- the patch touches 37 .c files, but that's mainly due to the fact that
LockNotPinnedObjectById() has to be called in a lot of places.

Regards,

>From d6c91b19a585d0a6f5c0abacb9c59cc5acd795f7 Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <[email-masked]>
Date: Fri, 29 Mar 2024 15:43:26 +0000
Subject: [PATCH v12] Avoid orphaned objects dependencies

Scenario 1:

Scenario 2:

The patch takes into account any type of objects except the ones that are pinned
(they are not droppable because the system requires it).

A special case is done for objects that belong to the RelationRelationId class.
For those, we should be in one of the two following cases that would already
prevent the relation to be dropped:

To avoid any risks for the RelationRelationId class case, we acquire a lock if
there is none. That may add unnecessary lock for 2. but that's worth it.

- schema and function (as the above scenarios)
- alter a dependency (function and schema)
- function and arg type
- function and return type
- function and function
- domain and domain
- table and type
- server and foreign data wrapper
---
 contrib/test_decoding/expected/twophase.out   |   3 +-
 src/backend/catalog/aclchk.c                  |   1 +
 src/backend/catalog/dependency.c              | 125 ++++++++++++++++-
 src/backend/catalog/heap.c                    |   7 +
 src/backend/catalog/index.c                   |  26 ++++
 src/backend/catalog/objectaddress.c           |  57 ++++++++
 src/backend/catalog/pg_aggregate.c            |   9 ++
 src/backend/catalog/pg_attrdef.c              |   1 +
 src/backend/catalog/pg_cast.c                 |   5 +
 src/backend/catalog/pg_collation.c            |   1 +
 src/backend/catalog/pg_constraint.c           |  26 ++++
 src/backend/catalog/pg_conversion.c           |   2 +
 src/backend/catalog/pg_depend.c               |  36 ++++-
 src/backend/catalog/pg_operator.c             |  19 +++
 src/backend/catalog/pg_proc.c                 |  17 ++-
 src/backend/catalog/pg_publication.c          |   7 +
 src/backend/catalog/pg_range.c                |   6 +
 src/backend/catalog/pg_type.c                 |  39 ++++++
 src/backend/catalog/toasting.c                |   1 +
 src/backend/commands/alter.c                  |   4 +
 src/backend/commands/amcmds.c                 |   1 +
 src/backend/commands/cluster.c                |   7 +
 src/backend/commands/event_trigger.c          |   1 +
 src/backend/commands/extension.c              |   5 +
 src/backend/commands/foreigncmds.c            |   7 +
 src/backend/commands/functioncmds.c           |   6 +
 src/backend/commands/indexcmds.c              |   2 +
 src/backend/commands/opclasscmds.c            |  18 +++
 src/backend/commands/operatorcmds.c           |  30 ++++
 src/backend/commands/policy.c                 |   2 +
 src/backend/commands/proclang.c               |   3 +
 src/backend/commands/sequence.c               |   2 +
 src/backend/commands/statscmds.c              |  10 ++
 src/backend/commands/tablecmds.c              |  34 +++--
 src/backend/commands/trigger.c                |  29 +++-
 src/backend/commands/tsearchcmds.c            |  81 +++++++----
 src/backend/commands/typecmds.c               |  84 ++++++++++++
 src/backend/rewrite/rewriteDefine.c           |   1 +
 src/backend/utils/errcodes.txt                |   1 +
 src/include/catalog/dependency.h              |   7 +
 src/include/catalog/objectaddress.h           |   1 +
 src/include/storage/lock.h                    |   9 ++
 .../expected/test_dependencies_locks.out      | 129 ++++++++++++++++++
 src/test/isolation/isolation_schedule         |   1 +
 .../specs/test_dependencies_locks.spec        |  89 ++++++++++++
 .../test_oat_hooks/expected/alter_table.out   |   4 +-
 .../expected/test_oat_hooks.out               |   2 +
 src/test/regress/expected/alter_table.out     |  11 +-
 48 files changed, 914 insertions(+), 55 deletions(-)
  32.8% src/backend/catalog/
  34.6% src/backend/commands/
  17.1% src/test/isolation/expected/
  10.7% src/test/isolation/specs/
   4.3% src/

diff --git a/contrib/test_decoding/expected/twophase.out b/contrib/test_decoding/expected/twophase.out
index 517f20bc37..71581fba34 100644
--- a/contrib/test_decoding/expected/twophase.out
+++ b/contrib/test_decoding/expected/twophase.out
@@ -69,9 +69,10 @@ WHERE locktype = 'relation'
   AND relation = 'test_prepared1'::regclass;
     relation     | locktype |        mode         
 -----------------+----------+---------------------
+ test_prepared_1 | relation | AccessShareLock
  test_prepared_1 | relation | RowExclusiveLock
  test_prepared_1 | relation | AccessExclusiveLock
-(2 rows)
+(3 rows)
 
 -- The insert should show the newly altered column but not the DDL.
 SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index a44ccee3b6..9a24872a30 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -1413,6 +1413,7 @@ SetDefaultACL(InternalDefaultACL *iacls)
 				referenced.objectId = iacls->nspid;
 				referenced.objectSubId = 0;
 
+				LockNotPinnedObject(NamespaceRelationId, iacls->nspid);
 				recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 			}
 		}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0489cbabcb..782bdb580e 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1519,6 +1519,100 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
 	}
 }
 
+/*
+ * LockNotPinnedObjectById
+ *
+ * Lock the object that we are about to record a dependency on.
+ * After it's locked, verify that it hasn't been dropped while we
+ * weren't looking.  If the object has been dropped, this function
+ * does not return!
+ */
+void
+LockNotPinnedObjectById(const ObjectAddress *object)
+{
+	char	   *object_description = NULL;
+
+	if (isObjectPinned(object))
+		return;
+
+	object_description = getObjectDescription(object, true);
+
+	if (object->classId == RelationRelationId)
+	{
+		LOCKTAG		tag;
+
+		Assert(!IsSharedRelation(object->objectId));
+
+		/*
+		 * We must be in one of the two following cases that would already
+		 * prevent the relation to be dropped: 1. The relation is already
+		 * locked (could be an existing relation or a relation that we are
+		 * creating). 2. The relation is protected indirectly (i.e an index
+		 * protected by a lock on its table, a table protected by a lock on a
+		 * function that depends of the table...). To avoid any risks, acquire
+		 * a lock if there is none. That may add unnecessary lock for 2. but
+		 * that's worth it.
+		 */
+		SET_LOCKTAG_RELATION(tag, MyDatabaseId, object->objectId);
+
+		if (!ObjectIsLocked(&tag))
+			LockRelationOid(object->objectId, AccessShareLock);
+	}
+	else
+	{
+		/* assume we should lock the whole object not a sub-object */
+		LockDatabaseObject(object->classId, object->objectId, 0, AccessShareLock);
+	}
+
+	/* check if object still exists */
+	if (!ObjectByIdExist(object))
+	{
+		if (object_description)
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+					 errmsg("%s does not exist", object_description)));
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_DEPENDENT_OBJECTS_DOES_NOT_EXIST),
+					 errmsg("a dependent object does not exist")));
+	}
+
+	if (object_description)
+		pfree(object_description);
+
+	return;
+}
+
+void
+LockNotPinnedObjectsById(const ObjectAddress *object, int nobject)
+{
+	int			i;
+
+	if (nobject < 0)
+		return;
+
+	for (i = 0; i < nobject; i++, object++)
+		LockNotPinnedObjectById(object);
+
+	return;
+}
+
+
+/*
+ * LockNotPinnedObject
+ *
+ * Lock the object that we are about to record a dependency on.
+ */
+void
+LockNotPinnedObject(Oid classid, Oid objid)
+{
+	ObjectAddress object;
+
+	ObjectAddressSet(object, classid, objid);
+
+	LockNotPinnedObjectById(&object);
+}
+
 /*
  * ReleaseDeletionLock - release an object deletion lock
  *
@@ -1564,13 +1658,8 @@ recordDependencyOnExpr(const ObjectAddress *depender,
 	/* Scan the expression tree for referenceable objects */
 	find_expr_references_walker(expr, &context);

 	free_object_addresses(context.addrs);
 }
@@ -1654,14 +1743,19 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,

+			LockNotPinnedObjectById(depender);
+
 			for (selfref = 0; selfref < self_addrs->numrefs; selfref++)
 			{
 				ObjectAddress *thisobj = self_addrs->refs + selfref;
@@ -1674,6 +1768,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
 	}
 
@@ -2734,6 +2829,22 @@ stack_address_present_add_flags(const ObjectAddress *object,
 	return result;
 }
 
+/*

 
 		if (reloftypeid)
 		{
 
 		/*

 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 55fdde4b24..dcd2158422 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1115,6 +1115,7 @@ index_create(Relation heapRelation,
 		else
 		{
 			bool		have_simple_col = false;
+			bool		locked_object = false;

@@ -1127,6 +1128,12 @@ index_create(Relation heapRelation,
 										heapRelationId,
 										indexInfo->ii_IndexAttrNumbers[i]);
 					add_exact_object_address(&referenced, addrs);
+
+					if (!locked_object)
+					{
+						LockNotPinnedObject(RelationRelationId, heapRelationId);
+						locked_object = true;
+					}
 					have_simple_col = true;
 				}
 			}
@@ -1142,6 +1149,8 @@ index_create(Relation heapRelation,
 				ObjectAddressSet(referenced, RelationRelationId,
 								 heapRelationId);
 				add_exact_object_address(&referenced, addrs);
+
+				LockNotPinnedObject(RelationRelationId, heapRelationId);
 			}
 
 			record_object_address_dependencies(&myself, addrs, DEPENDENCY_AUTO);
@@ -1157,9 +1166,13 @@ index_create(Relation heapRelation,
 		if (OidIsValid(parentIndexRelid))
 		{
 			ObjectAddressSet(referenced, RelationRelationId, parentIndexRelid);
+
+			LockNotPinnedObject(RelationRelationId, parentIndexRelid);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 
 			ObjectAddressSet(referenced, RelationRelationId, heapRelationId);
+
+			LockNotPinnedObject(RelationRelationId, heapRelationId);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 		}
 
@@ -1175,6 +1188,7 @@ index_create(Relation heapRelation,
 			{
 				ObjectAddressSet(referenced, CollationRelationId, collationIds[i]);
 				add_exact_object_address(&referenced, addrs);
+				LockNotPinnedObject(CollationRelationId, collationIds[i]);
 			}
 		}
 
@@ -1183,6 +1197,7 @@ index_create(Relation heapRelation,
 		{
 			ObjectAddressSet(referenced, OperatorClassRelationId, opclassIds[i]);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(OperatorClassRelationId, opclassIds[i]);
 		}
 
@@ -1987,6 +2002,14 @@ index_constraint_create(Relation heapRelation,
 
 	/*
@@ -1998,9 +2021,12 @@ index_constraint_create(Relation heapRelation,
 		ObjectAddress referenced;
 
+		LockNotPinnedObject(RelationRelationId, RelationGetRelid(heapRelation));

+/*
+ * ObjectByIdExist
+ *

 

 

 
index 003ae70b4d..dcce454f00 100644
 
+	LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
 	recordDependencyOn(&defobject, &colobject,
 					   attgenerated ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);

 

 
 	/* create dependency on owner */
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 3baf9231ed..c4cdbd7c58 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -252,17 +252,26 @@ CreateConstraintEntry(const char *constraintName,
 
 		if (constraintNTotalKeys > 0)
 		{
+			bool		locked_object = false;
+
 			for (i = 0; i < constraintNTotalKeys; i++)
 			{
 				ObjectAddressSubSet(relobject, RelationRelationId, relId,
 									constraintKey[i]);
 				add_exact_object_address(&relobject, addrs_auto);
+
+				if (!locked_object)
+				{
+					LockNotPinnedObject(RelationRelationId, relId);
+					locked_object = true;
+				}
 			}
 		}
 		else
 		{
 			ObjectAddressSet(relobject, RelationRelationId, relId);
 			add_exact_object_address(&relobject, addrs_auto);
+			LockNotPinnedObject(RelationRelationId, relId);
 		}
 	}
 
@@ -275,6 +284,7 @@ CreateConstraintEntry(const char *constraintName,

 	record_object_address_dependencies(&conobject, addrs_auto,
@@ -294,17 +304,26 @@ CreateConstraintEntry(const char *constraintName,
 
 		if (foreignNKeys > 0)
 		{
+			bool		locked_object = false;
+
 			for (i = 0; i < foreignNKeys; i++)
 			{
 				ObjectAddressSubSet(relobject, RelationRelationId,
 									foreignRelId, foreignKey[i]);
 				add_exact_object_address(&relobject, addrs_normal);
+
+				if (!locked_object)
+				{
+					LockNotPinnedObject(RelationRelationId, foreignRelId);
+					locked_object = true;
+				}
 			}
 		}
 		else
 		{
 			ObjectAddressSet(relobject, RelationRelationId, foreignRelId);
 			add_exact_object_address(&relobject, addrs_normal);
+			LockNotPinnedObject(RelationRelationId, foreignRelId);
 		}
 	}
 
@@ -320,6 +339,7 @@ CreateConstraintEntry(const char *constraintName,
 
 		ObjectAddressSet(relobject, RelationRelationId, indexRelId);
 		add_exact_object_address(&relobject, addrs_normal);
+		LockNotPinnedObject(RelationRelationId, indexRelId);
 	}
 
 	if (foreignNKeys > 0)
@@ -339,15 +359,18 @@ CreateConstraintEntry(const char *constraintName,
 		{
@@ -858,9 +881,12 @@ ConstraintSetParentConstraint(Oid childConstrId,

+		LockNotPinnedObject(RelationRelationId, childTableId);

index cfd7ef51df..538b6c3c2c 100644

-static bool isObjectPinned(const ObjectAddress *object);
-
-
 /*
  * Record a dependency between 2 objects via their respective objectAddress.
  * The first argument is the dependent object, the second the one it
@@ -100,6 +99,34 @@ recordMultipleDependencies(const ObjectAddress *depender,
 	slot_init_count = 0;
 	for (i = 0; i < nreferenced; i++, referenced++)
 	{
+#ifdef USE_ASSERT_CHECKING
+		LOCKTAG		tag;
+
+		if (!isObjectPinned(referenced) && ObjectByIdExist(referenced))
+		{
+			if (referenced->classId != RelationRelationId)
+				SET_LOCKTAG_OBJECT(tag,
+								   MyDatabaseId,
+								   referenced->classId,
+								   referenced->objectId,
+								   0);
+			else
+			{
+				Assert(!IsSharedRelation(referenced->objectId));
+
+				SET_LOCKTAG_RELATION(tag, MyDatabaseId, referenced->objectId);
+			}
+		}
+
+		/*
+		 * Assert the referenced object is locked if it should be visible (see
+		 * the comment related to LockNotPinnedObject() in TypeCreate()) and
+		 * if not pinned.
+		 */
+		Assert(!ObjectByIdExist(referenced) || isObjectPinned(referenced) ||
+			   ObjectIsLocked(&tag));
+#endif
+
 		/*
 		 * If the referenced object is pinned by the system, there's no real
 		 * need to record dependencies on it.  This saves lots of space in
@@ -239,6 +266,7 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
 		extension.objectId = CurrentExtensionObject;
 		extension.objectSubId = 0;
 
@@ -706,7 +734,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,

 
 
+	/*

 

 

 
-		/* Advance command counter so new tuple can be seen by validator */
-		CommandCounterIncrement();
-
 		/*
 		 * Set per-function configuration parameters so that the validation is
 		 * done with the environment the function expects.  However, if
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 0602398a54..b44a7f9d78 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -438,10 +438,13 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,

 	/* Add dependency on the relation */
 	ObjectAddressSet(referenced, RelationRelationId, relid);
+
+	LockNotPinnedObject(RelationRelationId, relid);
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 
 	/* Add dependency on the objects mentioned in the qualifications */
@@ -454,6 +457,8 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 	for (int i = 0; i < natts; i++)
 	{
 		ObjectAddressSubSet(referenced, RelationRelationId, relid, attarray[i]);
+
+		LockNotPinnedObject(RelationRelationId, relid);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -661,10 +666,12 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)

 
 	/* Close the table */

 

 
 
index 395dec8ed8..82ee7bc2e3 100644
 
@@ -494,6 +501,37 @@ TypeCreate(Oid newTypeOid,
+		 * Oid not added to pg_class yet (In heap_create_with_catalog(),
+		 * AddNewRelationType() is called before AddNewRelationTuple()).
@@ -505,6 +543,7 @@ TypeCreate(Oid newTypeOid,
 
index 738bc46ae8..a4d8342ca1 100644
 
+		LockNotPinnedObject(RelationRelationId, relOid);
 		recordDependencyOn(&toastobject, &baseobject, DEPENDENCY_INTERNAL);
 	}

 	return address;

 
index 78f96789b0..fb95d17738 100644
@@ -1381,6 +1384,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
+				LockNotPinnedObject(RelationRelationId, r1);
@@ -1389,6 +1394,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class,
+				LockNotPinnedObject(RelationRelationId, r2);

 

 

 
 		/*

 

 	if (OidIsValid(useId))

 
 	/* dependency on type */

index 309389e20d..76849e558e 100644
+			LockNotPinnedObject(RelationRelationId, parentOid);
+			LockNotPinnedObject(RelationRelationId, partitionIdx->rd_index->indrelid);

 

 
 
 		heap_freetuple(tup);
 
+		/*

 

 

 

 
+
 
index 6ff3eba824..9da98cbeec 100644
 
+	LockNotPinnedObject(RelationRelationId, table_id);
 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);

+	LockNotPinnedObject(RelationRelationId, table_id);
 	recordDependencyOn(&myself, &target, DEPENDENCY_AUTO);

 
 
 	record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 28f8522264..7fbebf2052 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1681,6 +1681,8 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
 		depobject.classId = RelationRelationId;
 		depobject.objectId = RelationGetRelid(seqrel);
 		depobject.objectSubId = 0;
+
+		LockNotPinnedObject(RelationRelationId, RelationGetRelid(tablerel));
 		recordDependencyOn(&depobject, &refobject, deptype);
 	}
 
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 1db3ef69d2..9f0b03388a 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -88,6 +88,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	bool		build_mcv;
 	bool		build_expressions;
 	bool		requested_type = false;
+	bool		locked_object = false;
 	int			i;
 	ListCell   *cell;
 	ListCell   *cell2;
@@ -536,6 +537,12 @@ CreateStatistics(CreateStatsStmt *stmt)
 	for (i = 0; i < nattnums; i++)
 	{
 		ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
+
+		if (!locked_object)
+		{
+			LockNotPinnedObject(RelationRelationId, relid);
+			locked_object = true;
+		}
 		recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
 	}
 
@@ -553,6 +560,8 @@ CreateStatistics(CreateStatsStmt *stmt)
 	if (!nattnums)
 	{
 		ObjectAddressSet(parentobject, RelationRelationId, relid);
+
+		LockNotPinnedObject(RelationRelationId, relid);
 		recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
 	}
 
@@ -573,6 +582,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 
index 66cda26a25..117259fba7 100644
 
+	LockNotPinnedObject(RelationRelationId, parentOid);
 	recordDependencyOn(&childobject, &parentobject,
 					   child_dependency_type(child_is_partition));

 	/*

@@ -10465,8 +10469,11 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
 			 */
 			ObjectAddressSet(address, ConstraintRelationId, constrOid);
 			ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
+			LockNotPinnedObject(ConstraintRelationId, parentConstr);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
 			ObjectAddressSet(referenced, RelationRelationId, partitionId);
+
+			LockNotPinnedObject(RelationRelationId, partitionId);
 			recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
 
@@ -10967,9 +10974,12 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
+		LockNotPinnedObject(RelationRelationId, RelationGetRelid(partRel));
 
 		/* Done with the cloned constraint's tuple */
@@ -13254,7 +13264,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	table_close(attrelation, RowExclusiveLock);

 	/*
@@ -14816,6 +14828,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 		 */
 		ObjectAddressSet(relobj, RelationRelationId, reloid);
 		ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
+		LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
 		recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
 	}
 	else if (OidIsValid(oldAccessMethodId) &&
@@ -14835,6 +14848,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 			   OidIsValid(rd_rel->relam));
 
@@ -16434,6 +16448,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 
 	/* Update pg_class.reloftype */
@@ -17192,14 +17207,17 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
 		CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
 
index 58b7fc5bbd..4e60a3c06f 100644

+	/*
 
@@ -1070,6 +1077,8 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
+		LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
 
 		if (OidIsValid(constrrelid))
@@ -1077,6 +1086,8 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 			referenced.classId = RelationRelationId;
 			referenced.objectId = constrrelid;
 			referenced.objectSubId = 0;
+
+			LockNotPinnedObject(RelationRelationId, constrrelid);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 		}
 		/* Not possible to have an index dependency in this case */
@@ -1091,6 +1102,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 			referenced.classId = ConstraintRelationId;
 			referenced.objectId = constraintOid;
 			referenced.objectSubId = 0;
+			LockNotPinnedObject(TriggerRelationId, trigoid);
 			recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
 		}
 
@@ -1100,8 +1112,11 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 		if (OidIsValid(parentTriggerOid))
 		{
 			ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid);
+			LockNotPinnedObject(TriggerRelationId, parentTriggerOid);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI);
 			ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
+
+			LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC);
 		}
 	}
@@ -1110,12 +1125,19 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
 	if (columns != NULL)
 	{
 		int			i;
+		bool		locked_object = false;
 
 		referenced.classId = RelationRelationId;
 		referenced.objectId = RelationGetRelid(rel);
 		for (i = 0; i < ncolumns; i++)
 		{
 			referenced.objectSubId = columns[i];
+
+			if (!locked_object)
+			{
+				LockNotPinnedObject(RelationRelationId, RelationGetRelid(rel));
+				locked_object = true;
+			}
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 	}
@@ -1255,9 +1277,12 @@ TriggerSetParentTrigger(Relation trigRel,
 		ObjectAddressSet(depender, TriggerRelationId, childTrigId);

 		ObjectAddressSet(referenced, RelationRelationId, childTableId);
+
+		LockNotPinnedObject(RelationRelationId, childTableId);
 		recordDependencyOn(&depender, &referenced, DEPENDENCY_PARTITION_SEC);
 	}
 	else
diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c
index b7b5019f1e..cba2b32a4d 100644
--- a/src/backend/commands/tsearchcmds.c
+++ b/src/backend/commands/tsearchcmds.c
@@ -66,12 +66,12 @@ static DefElem *buildDefItem(const char *name, const char *val,
 /* --------------------- TS Parser commands ------------------------ */
 
 /*

 /*
 
 	/*

 

 /*

 /*
 
 	/*

 		systable_endscan(scan);

 
 
 	/* must be owner */

 

 

 

 
 
index 6cc9a8d8bf..c930eca262 100644
 
+	LockNotPinnedObject(RelationRelationId, eventrel_oid);
 	recordDependencyOn(&myself, &referenced,
 					   (evtype == CMD_SELECT) ? DEPENDENCY_INTERNAL : DEPENDENCY_AUTO);

 
index 6908ca7180..93da8b353e 100644

 

 

 extern bool is_objectclass_supported(Oid class_id);
 extern const char *get_object_class_descr(Oid class_id);
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index 0017d4b868..f7cbb224fc 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -568,6 +568,15 @@ extern void LockReleaseSession(LOCKMETHODID lockmethodid);
 extern void LockReleaseCurrentOwner(LOCALLOCK **locallocks, int nlocks);
 extern void LockReassignCurrentOwner(LOCALLOCK **locallocks, int nlocks);
 extern bool LockHeldByMe(const LOCKTAG *locktag, LOCKMODE lockmode);
+
+#define ObjectIsLocked(tag) \
+	LockHeldByMe(tag,ShareLock) || LockHeldByMe(tag,AccessExclusiveLock) || \
+	LockHeldByMe(tag,RowExclusiveLock) || LockHeldByMe(tag,RowShareLock) || \
+	LockHeldByMe(tag,AccessShareLock) || \
+	LockHeldByMe(tag,ShareRowExclusiveLock) || \
+	LockHeldByMe(tag,ExclusiveLock) || \
+	LockHeldByMe(tag,ShareUpdateExclusiveLock)
+
 #ifdef USE_ASSERT_CHECKING
 extern HTAB *GetLockMethodLocalHash(void);
 #endif
diff --git a/src/test/isolation/expected/test_dependencies_locks.out b/src/test/isolation/expected/test_dependencies_locks.out
new file mode 100644
index 0000000000..9b645d7aa5
--- /dev/null
+++ b/src/test/isolation/expected/test_dependencies_locks.out
@@ -0,0 +1,129 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s1_begin s1_create_function_in_schema s2_drop_schema s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql;
+step s2_drop_schema: DROP SCHEMA testschema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_schema: <... completed>
+ERROR:  cannot drop schema testschema because other objects depend on it
+
+starting permutation: s2_begin s2_drop_schema s1_create_function_in_schema s2_commit
+step s2_begin: BEGIN;
+step s2_drop_schema: DROP SCHEMA testschema;
+step s1_create_function_in_schema: CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_in_schema: <... completed>
+ERROR:  schema testschema does not exist
+
+starting permutation: s1_begin s1_alter_function_schema s2_drop_alterschema s1_commit
+step s1_begin: BEGIN;
+step s1_alter_function_schema: ALTER FUNCTION public.falter() SET SCHEMA alterschema;
+step s2_drop_alterschema: DROP SCHEMA alterschema; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_alterschema: <... completed>
+ERROR:  cannot drop schema alterschema because other objects depend on it
+
+starting permutation: s2_begin s2_drop_alterschema s1_alter_function_schema s2_commit
+step s2_begin: BEGIN;
+step s2_drop_alterschema: DROP SCHEMA alterschema;
+step s1_alter_function_schema: ALTER FUNCTION public.falter() SET SCHEMA alterschema; <waiting ...>
+step s2_commit: COMMIT;
+step s1_alter_function_schema: <... completed>
+ERROR:  schema alterschema does not exist
+
+starting permutation: s1_begin s1_create_function_with_argtype s2_drop_foo_type s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_argtype: CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql;
+step s2_drop_foo_type: DROP TYPE public.foo; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_foo_type: <... completed>
+ERROR:  cannot drop type foo because other objects depend on it
+
+starting permutation: s2_begin s2_drop_foo_type s1_create_function_with_argtype s2_commit
+step s2_begin: BEGIN;
+step s2_drop_foo_type: DROP TYPE public.foo;
+step s1_create_function_with_argtype: CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_argtype: <... completed>
+ERROR:  type foo does not exist
+
+starting permutation: s1_begin s1_create_function_with_rettype s2_drop_foo_rettype s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_rettype: CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1;
+step s2_drop_foo_rettype: DROP DOMAIN id; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_foo_rettype: <... completed>
+ERROR:  cannot drop type id because other objects depend on it
+
+starting permutation: s2_begin s2_drop_foo_rettype s1_create_function_with_rettype s2_commit
+step s2_begin: BEGIN;
+step s2_drop_foo_rettype: DROP DOMAIN id;
+step s1_create_function_with_rettype: CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_rettype: <... completed>
+ERROR:  type id does not exist
+
+starting permutation: s1_begin s1_create_function_with_function s2_drop_function_f s1_commit
+step s1_begin: BEGIN;
+step s1_create_function_with_function: CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1;
+step s2_drop_function_f: DROP FUNCTION f(); <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_function_f: <... completed>
+ERROR:  cannot drop function f() because other objects depend on it
+
+starting permutation: s2_begin s2_drop_function_f s1_create_function_with_function s2_commit
+step s2_begin: BEGIN;
+step s2_drop_function_f: DROP FUNCTION f();
+step s1_create_function_with_function: CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_function_with_function: <... completed>
+ERROR:  function f() does not exist
+
+starting permutation: s1_begin s1_create_domain_with_domain s2_drop_domain_id s1_commit
+step s1_begin: BEGIN;
+step s1_create_domain_with_domain: CREATE DOMAIN idid as id;
+step s2_drop_domain_id: DROP DOMAIN id; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_domain_id: <... completed>
+ERROR:  cannot drop type id because other objects depend on it
+
+starting permutation: s2_begin s2_drop_domain_id s1_create_domain_with_domain s2_commit
+step s2_begin: BEGIN;
+step s2_drop_domain_id: DROP DOMAIN id;
+step s1_create_domain_with_domain: CREATE DOMAIN idid as id; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_domain_with_domain: <... completed>
+ERROR:  type id does not exist
+
+starting permutation: s1_begin s1_create_table_with_type s2_drop_footab_type s1_commit
+step s1_begin: BEGIN;
+step s1_create_table_with_type: CREATE TABLE tabtype(a footab);
+step s2_drop_footab_type: DROP TYPE public.footab; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_footab_type: <... completed>
+ERROR:  cannot drop type footab because other objects depend on it
+
+starting permutation: s2_begin s2_drop_footab_type s1_create_table_with_type s2_commit
+step s2_begin: BEGIN;
+step s2_drop_footab_type: DROP TYPE public.footab;
+step s1_create_table_with_type: CREATE TABLE tabtype(a footab); <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_table_with_type: <... completed>
+ERROR:  type footab does not exist
+
+starting permutation: s1_begin s1_create_server_with_fdw_wrapper s2_drop_fdw_wrapper s1_commit
+step s1_begin: BEGIN;
+step s1_create_server_with_fdw_wrapper: CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper;
+step s2_drop_fdw_wrapper: DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; <waiting ...>
+step s1_commit: COMMIT;
+step s2_drop_fdw_wrapper: <... completed>
+ERROR:  cannot drop foreign-data wrapper fdw_wrapper because other objects depend on it
+
+starting permutation: s2_begin s2_drop_fdw_wrapper s1_create_server_with_fdw_wrapper s2_commit
+step s2_begin: BEGIN;
+step s2_drop_fdw_wrapper: DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT;
+step s1_create_server_with_fdw_wrapper: CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; <waiting ...>
+step s2_commit: COMMIT;
+step s1_create_server_with_fdw_wrapper: <... completed>
+ERROR:  foreign-data wrapper fdw_wrapper does not exist
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 0342eb39e4..1b67f0bffe 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -114,3 +114,4 @@ test: serializable-parallel-2
 test: serializable-parallel-3
 test: matview-write-skew
 test: lock-nowait
+test: test_dependencies_locks
diff --git a/src/test/isolation/specs/test_dependencies_locks.spec b/src/test/isolation/specs/test_dependencies_locks.spec
new file mode 100644
index 0000000000..5d04dfe9dc
--- /dev/null
+++ b/src/test/isolation/specs/test_dependencies_locks.spec
@@ -0,0 +1,89 @@
+setup
+{
+  CREATE SCHEMA testschema;
+  CREATE SCHEMA alterschema;
+  CREATE TYPE public.foo as enum ('one', 'two');
+  CREATE TYPE public.footab as enum ('three', 'four');
+  CREATE DOMAIN id AS int;
+  CREATE FUNCTION f() RETURNS int LANGUAGE SQL RETURN 1;
+  CREATE FUNCTION public.falter() RETURNS int LANGUAGE SQL RETURN 1;
+  CREATE FOREIGN DATA WRAPPER fdw_wrapper;
+}
+
+teardown
+{
+  DROP FUNCTION IF EXISTS testschema.foo();
+  DROP FUNCTION IF EXISTS fooargtype(num foo);
+  DROP FUNCTION IF EXISTS footrettype();
+  DROP FUNCTION IF EXISTS foofunc();
+  DROP FUNCTION IF EXISTS public.falter();
+  DROP FUNCTION IF EXISTS alterschema.falter();
+  DROP DOMAIN IF EXISTS idid;
+  DROP SERVER IF EXISTS srv_fdw_wrapper;
+  DROP TABLE IF EXISTS tabtype;
+  DROP SCHEMA IF EXISTS testschema;
+  DROP SCHEMA IF EXISTS alterschema;
+  DROP TYPE IF EXISTS public.foo;
+  DROP TYPE IF EXISTS public.footab;
+  DROP DOMAIN IF EXISTS id;
+  DROP FUNCTION IF EXISTS f();
+  DROP FOREIGN DATA WRAPPER IF EXISTS fdw_wrapper;
+}
+
+session "s1"
+
+step "s1_begin" { BEGIN; }
+step "s1_create_function_in_schema" { CREATE FUNCTION testschema.foo() RETURNS int AS 'select 1' LANGUAGE sql; }
+step "s1_create_function_with_argtype" { CREATE FUNCTION fooargtype(num foo) RETURNS int AS 'select 1' LANGUAGE sql; }
+step "s1_create_function_with_rettype" { CREATE FUNCTION footrettype() RETURNS id LANGUAGE sql RETURN 1; }
+step "s1_create_function_with_function" { CREATE FUNCTION foofunc() RETURNS int LANGUAGE SQL RETURN f() + 1; }
+step "s1_alter_function_schema" { ALTER FUNCTION public.falter() SET SCHEMA alterschema; }
+step "s1_create_domain_with_domain" { CREATE DOMAIN idid as id; }
+step "s1_create_table_with_type" { CREATE TABLE tabtype(a footab); }
+step "s1_create_server_with_fdw_wrapper" { CREATE SERVER srv_fdw_wrapper FOREIGN DATA WRAPPER fdw_wrapper; }
+step "s1_commit" { COMMIT; }
+
+session "s2"
+
+step "s2_begin" { BEGIN; }
+step "s2_drop_schema" { DROP SCHEMA testschema; }
+step "s2_drop_alterschema" { DROP SCHEMA alterschema; }
+step "s2_drop_foo_type" { DROP TYPE public.foo; }
+step "s2_drop_foo_rettype" { DROP DOMAIN id; }
+step "s2_drop_footab_type" { DROP TYPE public.footab; }
+step "s2_drop_function_f" { DROP FUNCTION f(); }
+step "s2_drop_domain_id" { DROP DOMAIN id; }
+step "s2_drop_fdw_wrapper" { DROP FOREIGN DATA WRAPPER fdw_wrapper RESTRICT; }
+step "s2_commit" { COMMIT; }
+
+# function - schema
+permutation "s1_begin" "s1_create_function_in_schema" "s2_drop_schema" "s1_commit"
+permutation "s2_begin" "s2_drop_schema" "s1_create_function_in_schema" "s2_commit"
+
+# alter function - schema
+permutation "s1_begin" "s1_alter_function_schema" "s2_drop_alterschema" "s1_commit"
+permutation "s2_begin" "s2_drop_alterschema" "s1_alter_function_schema" "s2_commit"
+
+# function - argtype
+permutation "s1_begin" "s1_create_function_with_argtype" "s2_drop_foo_type" "s1_commit"
+permutation "s2_begin" "s2_drop_foo_type" "s1_create_function_with_argtype" "s2_commit"
+
+# function - rettype
+permutation "s1_begin" "s1_create_function_with_rettype" "s2_drop_foo_rettype" "s1_commit"
+permutation "s2_begin" "s2_drop_foo_rettype" "s1_create_function_with_rettype" "s2_commit"
+
+# function - function
+permutation "s1_begin" "s1_create_function_with_function" "s2_drop_function_f" "s1_commit"
+permutation "s2_begin" "s2_drop_function_f" "s1_create_function_with_function" "s2_commit"
+
+# domain - domain
+permutation "s1_begin" "s1_create_domain_with_domain" "s2_drop_domain_id" "s1_commit"
+permutation "s2_begin" "s2_drop_domain_id" "s1_create_domain_with_domain" "s2_commit"
+
+# table - type
+permutation "s1_begin" "s1_create_table_with_type" "s2_drop_footab_type" "s1_commit"
+permutation "s2_begin" "s2_drop_footab_type" "s1_create_table_with_type" "s2_commit"
+
+# server - foreign data wrapper
+permutation "s1_begin" "s1_create_server_with_fdw_wrapper" "s2_drop_fdw_wrapper" "s1_commit"
+permutation "s2_begin" "s2_drop_fdw_wrapper" "s1_create_server_with_fdw_wrapper" "s2_commit"
diff --git a/src/test/modules/test_oat_hooks/expected/alter_table.out b/src/test/modules/test_oat_hooks/expected/alter_table.out
index 8cbacca2c9..df8d276dfc 100644
--- a/src/test/modules/test_oat_hooks/expected/alter_table.out
+++ b/src/test/modules/test_oat_hooks/expected/alter_table.out
@@ -37,6 +37,8 @@ NOTICE:  in object access: superuser attempting create (subId=0x0) [internal]
 NOTICE:  in object access: superuser finished create (subId=0x0) [internal]
 NOTICE:  in object access: superuser attempting create (subId=0x0) [internal]
 NOTICE:  in object access: superuser finished create (subId=0x0) [internal]
+NOTICE:  in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
+NOTICE:  in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
 NOTICE:  in process utility: superuser finished CREATE TABLE
 CREATE RULE test_oat_notify AS
   ON UPDATE TO test_oat_schema.test_oat_tab
@@ -62,8 +64,6 @@ BEGIN
   END IF;
 END; $$;
 NOTICE:  in process utility: superuser attempting CREATE FUNCTION
-NOTICE:  in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
-NOTICE:  in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
 NOTICE:  in object access: superuser attempting create (subId=0x0) [explicit]
 NOTICE:  in object access: superuser finished create (subId=0x0) [explicit]
 NOTICE:  in process utility: superuser finished CREATE FUNCTION
diff --git a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
index effdc49145..da6d931994 100644
--- a/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
+++ b/src/test/modules/test_oat_hooks/expected/test_oat_hooks.out
@@ -86,6 +86,8 @@ NOTICE:  in object access: superuser attempting create (subId=0x0) [internal]
 NOTICE:  in object access: superuser finished create (subId=0x0) [internal]
 NOTICE:  in object access: superuser attempting create (subId=0x0) [internal]
 NOTICE:  in object access: superuser finished create (subId=0x0) [internal]
+NOTICE:  in object access: superuser attempting namespace search (subId=0x0) [no report on violation, allowed]
+NOTICE:  in object access: superuser finished namespace search (subId=0x0) [no report on violation, allowed]
 NOTICE:  in process utility: superuser finished CREATE TABLE
 CREATE INDEX regress_test_table_t_idx ON regress_test_table (t);
 NOTICE:  in process utility: superuser attempting CREATE INDEX
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 673361e840..c2115ea601 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2867,11 +2867,12 @@ begin;
 alter table alterlock2
 add constraint alterlock2nv foreign key (f1) references alterlock (f1) NOT VALID;
 select * from my_locks order by 1;
-  relname   |     max_lockmode      
-------------+-----------------------
- alterlock  | ShareRowExclusiveLock
- alterlock2 | ShareRowExclusiveLock
-(2 rows)
+    relname     |     max_lockmode      
+----------------+-----------------------
+ alterlock      | ShareRowExclusiveLock
+ alterlock2     | ShareRowExclusiveLock
+ alterlock_pkey | AccessShareLock
+(3 rows)
 
 commit;
 begin;
-- 
2.34.1

---

From: Bertrand Drouvot <[email-masked]>
Date: Mon, 1 Jul 2024 09:39:17 +0000

Hi,

> implemented in LockNotPinnedObjectById().

Please find attached v13, mandatory rebase due to 0cecc908e97. In passing, make
use of CheckRelationOidLockedByMe() added in 0cecc908e97.

Regards,

>From 461ae5d2fa037b2b9fd285c2a328c8bc785eaec8 Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <[email-masked]>
Date: Fri, 29 Mar 2024 15:43:26 +0000
Subject: [PATCH v13] Avoid orphaned objects dependencies

Scenario 1:

Scenario 2:

 src/backend/catalog/dependency.c              | 121 +++++++++++++++-
 src/backend/catalog/pg_depend.c               |  40 +++++-
 46 files changed, 903 insertions(+), 54 deletions(-)
  33.2% src/backend/catalog/
  35.0% src/backend/commands/
  17.3% src/test/isolation/expected/
  10.8% src/test/isolation/specs/
   3.4% src/

index a44ccee3b68..9a24872a303 100644
 
+				LockNotPinnedObject(NamespaceRelationId, iacls->nspid);
 				recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 			}
 		}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0489cbabcb8..f002902d1e0 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1519,6 +1519,96 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
 	}
 }
 
+/*
+		if (!CheckRelationOidLockedByMe(object->objectId, AccessShareLock, true))
@@ -1564,13 +1654,8 @@ recordDependencyOnExpr(const ObjectAddress *depender,

 	free_object_addresses(context.addrs);
 }
@@ -1654,14 +1739,19 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,

+			LockNotPinnedObjectById(depender);
+
 			for (selfref = 0; selfref < self_addrs->numrefs; selfref++)
 			{
 				ObjectAddress *thisobj = self_addrs->refs + selfref;
@@ -1674,6 +1764,7 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
 	}
 
@@ -2734,6 +2825,22 @@ stack_address_present_add_flags(const ObjectAddress *object,
 	return result;
 }
 
+/*
index ae2efdc760d..4799331fe10 100644
 
@@ -1458,11 +1460,13 @@ heap_create_with_catalog(const char *relname,

 		if (reloftypeid)
 		{
 
 		/*
@@ -1476,6 +1480,7 @@ heap_create_with_catalog(const char *relname,
 		{
 			ObjectAddressSet(referenced, AccessMethodRelationId, accessmtd);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(AccessMethodRelationId, accessmtd);
 		}
 
@@ -3390,6 +3395,7 @@ StorePartitionKey(Relation rel,
 
@@ -3397,6 +3403,7 @@ StorePartitionKey(Relation rel,
 
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index a819b4197ce..d6d1abfcf5a 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1116,6 +1116,7 @@ index_create(Relation heapRelation,
 		else
 		{
 			bool		have_simple_col = false;
+			bool		locked_object = false;

@@ -1128,6 +1129,12 @@ index_create(Relation heapRelation,
 										heapRelationId,
 										indexInfo->ii_IndexAttrNumbers[i]);
 					add_exact_object_address(&referenced, addrs);
+
+					if (!locked_object)
+					{
+						LockNotPinnedObject(RelationRelationId, heapRelationId);
+						locked_object = true;
+					}
 					have_simple_col = true;
 				}
 			}
@@ -1143,6 +1150,8 @@ index_create(Relation heapRelation,
 				ObjectAddressSet(referenced, RelationRelationId,
 								 heapRelationId);
 				add_exact_object_address(&referenced, addrs);
+
+				LockNotPinnedObject(RelationRelationId, heapRelationId);
 			}
 
@@ -1158,9 +1167,13 @@ index_create(Relation heapRelation,

@@ -1176,6 +1189,7 @@ index_create(Relation heapRelation,
 			{
 				ObjectAddressSet(referenced, CollationRelationId, collationIds[i]);
 				add_exact_object_address(&referenced, addrs);
+				LockNotPinnedObject(CollationRelationId, collationIds[i]);
 			}
 		}
 
@@ -1184,6 +1198,7 @@ index_create(Relation heapRelation,
 		{
 			ObjectAddressSet(referenced, OperatorClassRelationId, opclassIds[i]);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(OperatorClassRelationId, opclassIds[i]);
 		}
 
@@ -1988,6 +2003,14 @@ index_constraint_create(Relation heapRelation,
 
 	/*
@@ -1999,9 +2022,12 @@ index_constraint_create(Relation heapRelation,
 		ObjectAddress referenced;

index 7b536ac6fde..6d7abd37383 100644
 
+/*
+ * ObjectByIdExist
+ *
index 90fc7db949f..a47e3c55070 100644

 

 

 
index 003ae70b4d2..dcce454f000 100644

index 5a5b855d514..d3707e424ca 100644

 
 
index 7f2f7012299..78498b8c20d 100644
 
index 3baf9231ed0..c4cdbd7c583 100644

 

 		if (foreignNKeys > 0)
 		{

 
 	if (foreignNKeys > 0)

index 0770878eac5..25881654d63 100644

index cfd7ef51dfa..ebca5a452b4 100644
@@ -20,21 +20,21 @@
+#include "storage/lmgr.h"

-static bool isObjectPinned(const ObjectAddress *object);
-
-
 /*
  * Record a dependency between 2 objects via their respective objectAddress.
  * The first argument is the dependent object, the second the one it
@@ -100,6 +100,37 @@ recordMultipleDependencies(const ObjectAddress *depender,
 	slot_init_count = 0;
 	for (i = 0; i < nreferenced; i++, referenced++)
 	{
+#ifdef USE_ASSERT_CHECKING
+		if (!isObjectPinned(referenced))
+		{
+			if (referenced->classId != RelationRelationId)
+			{
+				LOCKTAG		tag;
+
+				SET_LOCKTAG_OBJECT(tag,
+								   MyDatabaseId,
+								   referenced->classId,
+								   referenced->objectId,
+								   0);
+				/* assert the referenced object is locked */
+				Assert(LockHeldByMe(&tag, AccessShareLock, false));
+			}
+			else
+			{
+				Assert(!IsSharedRelation(referenced->objectId));
+
+				/*
+				 * Assert the referenced object is locked if it should be
+				 * visible (see the comment related to LockNotPinnedObject()
+				 * in TypeCreate()).
+				 */
+				Assert(!ObjectByIdExist(referenced) ||
+					   CheckRelationOidLockedByMe(referenced->objectId,
+												  AccessShareLock, true));
+			}
+		}
+#endif
+
 		/*
 		 * If the referenced object is pinned by the system, there's no real
 		 * need to record dependencies on it.  This saves lots of space in
@@ -239,6 +270,7 @@ recordDependencyOnCurrentExtension(const ObjectAddress *object,
 		extension.objectId = CurrentExtensionObject;
 		extension.objectSubId = 0;
 
@@ -706,7 +738,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
index 65b45a424a2..e8374eec882 100644

 
index 528c17cd7f6..116e524390b 100644
 
+	/*

 

 

 
index 0602398a545..b44a7f9d78a 100644

 

 
 
 	/* Close the table */
index 501a6ba4106..e5b5a0b6f82 100644

 

 
 
index 395dec8ed88..82ee7bc2e30 100644

index 738bc46ae82..a4d8342ca1f 100644

index 4f99ebb4470..57e86f576a4 100644
 
 	return address;
 
index aaa0f9a1dc8..8616a7c9fab 100644

index 78f96789b0e..fb95d177383 100644
index 7a5ed6b9850..8d0cdec59e0 100644
 
index 1643c8c69a0..669a5d6dd8b 100644

 

 

 		/*
index cf61bbac1fa..735bca486c0 100644

 

 	if (OidIsValid(useId))
 
index 6593fd7d811..8207ef08b3e 100644

 	/* dependency on type */

index 2caab88aa58..e6ff8476e84 100644
@@ -4380,8 +4380,10 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
index b8b5c147c5d..e70afd216c2 100644

 

 
 
 		heap_freetuple(tup);
 
+		/*

 

 

 
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 5872a3e1922..58a69e7cc25 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -33,6 +33,7 @@

 
+
 
index 6ff3eba8249..9da98cbeec4 100644

 
 
index 881f90017ef..fadfd9064fd 100644

 
index b4ad19c0539..0dfabee88e4 100644
@@ -1688,6 +1688,8 @@ process_owned_by(Relation seqrel, List *owned_by, bool for_identity)
 
index 1db3ef69d22..9f0b03388a1 100644

 
index 8fcb1883234..c21e65d86db 100644

@@ -7361,7 +7362,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/*
 
 	/*
@@ -10186,6 +10189,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
 		ObjectAddress referenced;

@@ -10477,8 +10481,11 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
 			 */
 
@@ -10979,9 +10986,12 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
 
 		/* Done with the cloned constraint's tuple */
@@ -13266,7 +13276,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	table_close(attrelation, RowExclusiveLock);

 	/*
@@ -14828,6 +14840,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 		 */
 		ObjectAddressSet(relobj, RelationRelationId, reloid);
 		ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
+		LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
 		recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
 	}
 	else if (OidIsValid(oldAccessMethodId) &&
@@ -14847,6 +14860,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 			   OidIsValid(rd_rel->relam));
 
@@ -16446,6 +16460,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 
 	/* Update pg_class.reloftype */
@@ -17204,14 +17219,17 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
 		CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
 
index 58b7fc5bbd5..4e60a3c06f6 100644

+	/*

 

 
index b7b5019f1e0..cba2b32a4de 100644
 
 /*

 /*
 
 	/*

 

 /*

 /*
 
 	/*

 		systable_endscan(scan);

 
 
 	/* must be owner */

 

index 2a1e7133356..9febaa24a75 100644

 

 

index 6cc9a8d8bfe..c930eca2624 100644

index 3250d539e1c..60e8539fe3f 100644

index 6908ca7180a..93da8b353ea 100644

 

 
index 3a70d80e320..56f746264b0 100644

index 00000000000..9b645d7aa55
index 6da98cffaca..ef6a7075bcb 100644
@@ -117,3 +117,4 @@ test: serializable-parallel-2
index 00000000000..5d04dfe9dc6
index 8cbacca2c9b..df8d276dfcc 100644
index effdc491458..da6d931994d 100644
index 673361e8404..c2115ea6013 100644
 
 commit;
 begin;
-- 
2.34.1

---

From: Bertrand Drouvot <[email-masked]>
Date: Tue, 2 Jul 2024 05:56:23 +0000

Hi,

> use of CheckRelationOidLockedByMe() added in 0cecc908e97.

Please find attached v14, mandatory rebase due to 65b71dec2d5.

Regards,

>From 31767e9eb290909943d03408d9f8e827cf17e0bd Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <[email-masked]>
Date: Fri, 29 Mar 2024 15:43:26 +0000
Subject: [PATCH v14] Avoid orphaned objects dependencies

Scenario 1:

Scenario 2:

 
 
+/*

 

 
+/*
+ * Record multiple dependencies from an ObjectAddresses array and lock the
+ * referenced objects, after first removing any duplicates.
+ */
+void
+lock_record_object_address_dependencies(const ObjectAddress *depender,
+										ObjectAddresses *referenced,
+										DependencyType behavior)
+{
+	eliminate_duplicate_dependencies(referenced);
+	LockNotPinnedObjectsById(referenced->refs, referenced->numrefs);
+	recordMultipleDependencies(depender,
+							   referenced->refs, referenced->numrefs,
+							   behavior);
+}
+
 /*
  * Record multiple dependencies from an ObjectAddresses array, after first
  * removing any duplicates.
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 00074c8a948..1266101d906 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -844,6 +844,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		/* Add dependency info */
 		ObjectAddressSubSet(myself, RelationRelationId, new_rel_oid, i + 1);
 		ObjectAddressSet(referenced, TypeRelationId, attr->atttypid);
+		LockNotPinnedObject(TypeRelationId, attr->atttypid);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 
 		/* The default collation is pinned, so don't bother recording it */
@@ -852,6 +853,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		{
 			ObjectAddressSet(referenced, CollationRelationId,
 							 attr->attcollation);
+			LockNotPinnedObject(CollationRelationId, attr->attcollation);
 			recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 		}
 	}
@@ -1459,11 +1461,13 @@ heap_create_with_catalog(const char *relname,

 		if (reloftypeid)
 		{
 
 		/*
@@ -1477,6 +1481,7 @@ heap_create_with_catalog(const char *relname,
 		{
 			ObjectAddressSet(referenced, AccessMethodRelationId, accessmtd);
 			add_exact_object_address(&referenced, addrs);
+			LockNotPinnedObject(AccessMethodRelationId, accessmtd);
 		}
 
@@ -3391,6 +3396,7 @@ StorePartitionKey(Relation rel,
 
@@ -3398,6 +3404,7 @@ StorePartitionKey(Relation rel,

 

 

 
 	/*

 
+/*
+ * ObjectByIdExist
+ *

 

 

 

 

 

 

 
 		if (foreignNKeys > 0)
 		{

 
 	if (foreignNKeys > 0)

 

 

 
 
+	/*

 

 

 

 

 
 
 	/* Close the table */

 

 

 

 
 	return address;

 

 

 

 
 		/*

 

 	if (OidIsValid(useId))

 
 	/* dependency on type */

 

 

 
 		heap_freetuple(tup);
 
+		/*

 

 

 

 
+

 

 

index 9f28d40466b..c0634d0af90 100644

 
 
 	recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9e9dc5c2c13..155e9ec57df 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3419,6 +3419,7 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid,
 	childobject.objectId = relationId;
 	childobject.objectSubId = 0;

@@ -7342,7 +7343,9 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	/*
 
 	/*
@@ -10167,6 +10170,7 @@ addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
 		ObjectAddress referenced;

@@ -10458,8 +10462,11 @@ addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
 			 */
 
@@ -10960,9 +10967,12 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
 
 		/* Done with the cloned constraint's tuple */
@@ -13247,7 +13257,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	table_close(attrelation, RowExclusiveLock);

 	/*
@@ -14809,6 +14821,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 		 */
 		ObjectAddressSet(relobj, RelationRelationId, reloid);
 		ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
+		LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
 		recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
 	}
 	else if (OidIsValid(oldAccessMethodId) &&
@@ -14828,6 +14841,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 			   OidIsValid(rd_rel->relam));
 
@@ -16427,6 +16441,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 
 	/* Update pg_class.reloftype */
@@ -17185,14 +17200,17 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
 		CatalogTupleUpdate(classRel, &classTup->t_self, classTup);

 
+	/*

 

 
 
 /*

 /*
 
 	/*

 

 /*

 /*
 
 	/*

 		systable_endscan(scan);

 
 
 	/* must be owner */

 

 

 

 

 

 

 

 

 commit;
 begin;
-- 
2.34.1

---

From: Bertrand Drouvot <[email-masked]>
Date: Wed, 10 Jul 2024 07:31:06 +0000

Hi,

> Please find attached v14, mandatory rebase due to 65b71dec2d5.

In [1] I mentioned that the object locking logic has been put outside of the 
dependency code except for:

recordDependencyOnExpr() 

Please find attached v15 that also removes the logic outside of the 3 above 
functions. Well, for recordDependencyOnExpr() and recordDependencyOnSingleRelExpr()
that's now done in find_expr_references_walker(): It's somehow still in the
dependency code but at least it is now clear which objects we are locking (and
I'm not sure how we could do better than that for those 2 functions).

There is still one locking call in recordDependencyOnCurrentExtension() but I
think this one is clear enough and does not need to be put outside (for
the same reason mentioned in [1]).

So, to sum up:

A. Locking is now done exclusively with LockNotPinnedObject(Oid classid, Oid objid)
so that it's now always clear what object we want to acquire a lock for. It means
we are not manipulating directly an object address or a list of objects address
as it was the case when the locking was done "directly" within the dependency code.

B. A special case is done for objects that belong to the RelationRelationId class.
For those, we should be in one of the two following cases that would already
prevent the relation to be dropped:

To avoid any risks for the RelationRelationId class case, we acquire a lock if
there is none. That may add unnecessary lock for 2. but that seems worth it. 

Regards,

>From bc47856d85e9e4f61fc22bc29ec40419e957b7e6 Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <[email-masked]>
Date: Fri, 29 Mar 2024 15:43:26 +0000
Subject: [PATCH v15] Avoid orphaned objects dependencies

Scenario 1:

Scenario 2:

- schema and function (as the above scenarios)
- alter a dependency (function and schema)
- function and arg type
- function and return type
- function and function
- domain and domain
- table and type
- server and foreign data wrapper
---
 src/backend/catalog/aclchk.c                  |   1 +
 src/backend/catalog/dependency.c              | 212 ++++++++++++++++++
 src/backend/catalog/heap.c                    |   7 +
 src/backend/catalog/index.c                   |  26 +++
 src/backend/catalog/objectaddress.c           |  57 +++++
 src/backend/catalog/pg_aggregate.c            |   9 +
 src/backend/catalog/pg_attrdef.c              |   1 +
 src/backend/catalog/pg_cast.c                 |   5 +
 src/backend/catalog/pg_collation.c            |   1 +
 src/backend/catalog/pg_constraint.c           |  26 +++
 src/backend/catalog/pg_conversion.c           |   2 +
 src/backend/catalog/pg_depend.c               |  40 +++-
 src/backend/catalog/pg_operator.c             |  19 ++
 src/backend/catalog/pg_proc.c                 |  17 +-
 src/backend/catalog/pg_publication.c          |   7 +
 src/backend/catalog/pg_range.c                |   6 +
 src/backend/catalog/pg_type.c                 |  39 ++++
 src/backend/catalog/toasting.c                |   1 +
 src/backend/commands/alter.c                  |   4 +
 src/backend/commands/amcmds.c                 |   1 +
 src/backend/commands/cluster.c                |   7 +
 src/backend/commands/event_trigger.c          |   1 +
 src/backend/commands/extension.c              |   5 +
 src/backend/commands/foreigncmds.c            |   7 +
 src/backend/commands/functioncmds.c           |   6 +
 src/backend/commands/indexcmds.c              |   2 +
 src/backend/commands/opclasscmds.c            |  18 ++
 src/backend/commands/operatorcmds.c           |  30 +++
 src/backend/commands/policy.c                 |   2 +
 src/backend/commands/proclang.c               |   3 +
 src/backend/commands/sequence.c               |   2 +
 src/backend/commands/statscmds.c              |  10 +
 src/backend/commands/tablecmds.c              |  34 ++-
 src/backend/commands/trigger.c                |  29 ++-
 src/backend/commands/tsearchcmds.c            |  73 +++++-
 src/backend/commands/typecmds.c               |  84 +++++++
 src/backend/rewrite/rewriteDefine.c           |   1 +
 src/backend/utils/errcodes.txt                |   1 +
 src/include/catalog/dependency.h              |   3 +
 src/include/catalog/objectaddress.h           |   1 +
 .../expected/test_dependencies_locks.out      | 129 +++++++++++
 src/test/isolation/isolation_schedule         |   1 +
 .../specs/test_dependencies_locks.spec        |  89 ++++++++
 .../test_oat_hooks/expected/alter_table.out   |   4 +-
 .../expected/test_oat_hooks.out               |   2 +
 src/test/regress/expected/alter_table.out     |  11 +-
 46 files changed, 1011 insertions(+), 25 deletions(-)
  39.6% src/backend/catalog/
  30.5% src/backend/commands/
  16.7% src/test/isolation/expected/
  10.4% src/test/isolation/specs/

+				LockNotPinnedObject(NamespaceRelationId, iacls->nspid);
 				recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
 			}
 		}
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0489cbabcb..a3770d7206 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1519,6 +1519,81 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
 	}
 }
 
+/*
@@ -1730,6 +1805,7 @@ find_expr_references_walker(Node *node,
 		if (rte->rtekind == RTE_RELATION)
 		{
 			/* If it's a plain relation, reference this column */
+			LockNotPinnedObject(RelationRelationId, rte->relid);
 			add_object_address(RelationRelationId, rte->relid, var->varattno,
 							   context->addrs);
 		}
@@ -1756,6 +1832,7 @@ find_expr_references_walker(Node *node,
 		Oid			objoid;
 
 		/* A constant must depend on the constant's datatype */
+		LockNotPinnedObject(TypeRelationId, con->consttype);
 		add_object_address(TypeRelationId, con->consttype, 0,
 						   context->addrs);
 
@@ -1767,8 +1844,11 @@ find_expr_references_walker(Node *node,
 		 */
 		if (OidIsValid(con->constcollid) &&
 			con->constcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, con->constcollid);
 			add_object_address(CollationRelationId, con->constcollid, 0,
 							   context->addrs);
+		}
 
 		/*
 		 * If it's a regclass or similar literal referring to an existing
@@ -1785,59 +1865,83 @@ find_expr_references_walker(Node *node,
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(PROCOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(ProcedureRelationId, objoid);
 						add_object_address(ProcedureRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGOPEROID:
 				case REGOPERATOROID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(OPEROID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(OperatorRelationId, objoid);
 						add_object_address(OperatorRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGCLASSOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(RELOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(RelationRelationId, objoid);
 						add_object_address(RelationRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGTYPEOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(TYPEOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(TypeRelationId, objoid);
 						add_object_address(TypeRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGCOLLATIONOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(COLLOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(CollationRelationId, objoid);
 						add_object_address(CollationRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGCONFIGOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(TSCONFIGOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(TSConfigRelationId, objoid);
 						add_object_address(TSConfigRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 				case REGDICTIONARYOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(TSDICTOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(TSDictionaryRelationId, objoid);
 						add_object_address(TSDictionaryRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 
 				case REGNAMESPACEOID:
 					objoid = DatumGetObjectId(con->constvalue);
 					if (SearchSysCacheExists1(NAMESPACEOID,
 											  ObjectIdGetDatum(objoid)))
+					{
+						LockNotPinnedObject(NamespaceRelationId, objoid);
 						add_object_address(NamespaceRelationId, objoid, 0,
 										   context->addrs);
+					}
 					break;
 
 					/*
@@ -1859,18 +1963,23 @@ find_expr_references_walker(Node *node,
 		Param	   *param = (Param *) node;
 
 		/* A parameter must depend on the parameter's datatype */
+		LockNotPinnedObject(TypeRelationId, param->paramtype);
 		add_object_address(TypeRelationId, param->paramtype, 0,
 						   context->addrs);
 		/* and its collation, just as for Consts */
 		if (OidIsValid(param->paramcollid) &&
 			param->paramcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, param->paramcollid);
 			add_object_address(CollationRelationId, param->paramcollid, 0,
 							   context->addrs);
+		}
 	}
 	else if (IsA(node, FuncExpr))
 	{
 		FuncExpr   *funcexpr = (FuncExpr *) node;
 
+		LockNotPinnedObject(ProcedureRelationId, funcexpr->funcid);
 		add_object_address(ProcedureRelationId, funcexpr->funcid, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1879,6 +1988,7 @@ find_expr_references_walker(Node *node,
 	{
 		OpExpr	   *opexpr = (OpExpr *) node;
 
+		LockNotPinnedObject(OperatorRelationId, opexpr->opno);
 		add_object_address(OperatorRelationId, opexpr->opno, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1887,6 +1997,7 @@ find_expr_references_walker(Node *node,
 	{
 		DistinctExpr *distinctexpr = (DistinctExpr *) node;
 
+		LockNotPinnedObject(OperatorRelationId, distinctexpr->opno);
 		add_object_address(OperatorRelationId, distinctexpr->opno, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1895,6 +2006,7 @@ find_expr_references_walker(Node *node,
 	{
 		NullIfExpr *nullifexpr = (NullIfExpr *) node;
 
+		LockNotPinnedObject(OperatorRelationId, nullifexpr->opno);
 		add_object_address(OperatorRelationId, nullifexpr->opno, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1903,6 +2015,7 @@ find_expr_references_walker(Node *node,
 	{
 		ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
 
+		LockNotPinnedObject(OperatorRelationId, opexpr->opno);
 		add_object_address(OperatorRelationId, opexpr->opno, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1911,6 +2024,7 @@ find_expr_references_walker(Node *node,
 	{
 		Aggref	   *aggref = (Aggref *) node;
 
+		LockNotPinnedObject(ProcedureRelationId, aggref->aggfnoid);
 		add_object_address(ProcedureRelationId, aggref->aggfnoid, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1919,6 +2033,7 @@ find_expr_references_walker(Node *node,
 	{
 		WindowFunc *wfunc = (WindowFunc *) node;
 
+		LockNotPinnedObject(ProcedureRelationId, wfunc->winfnoid);
 		add_object_address(ProcedureRelationId, wfunc->winfnoid, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -1935,8 +2050,11 @@ find_expr_references_walker(Node *node,
 		 */
 		if (sbsref->refrestype != sbsref->refcontainertype &&
 			sbsref->refrestype != sbsref->refelemtype)
+		{
+			LockNotPinnedObject(TypeRelationId, sbsref->refrestype);
 			add_object_address(TypeRelationId, sbsref->refrestype, 0,
 							   context->addrs);
+		}
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, SubPlan))
@@ -1960,16 +2078,25 @@ find_expr_references_walker(Node *node,
 		 * anywhere else in the expression.
 		 */
 		if (OidIsValid(reltype))
+		{
+			LockNotPinnedObject(RelationRelationId, reltype);
 			add_object_address(RelationRelationId, reltype, fselect->fieldnum,
 							   context->addrs);
+		}
 		else
+		{
+			LockNotPinnedObject(TypeRelationId, fselect->resulttype);
 			add_object_address(TypeRelationId, fselect->resulttype, 0,
 							   context->addrs);
+		}
 		/* the collation might not be referenced anywhere else, either */
 		if (OidIsValid(fselect->resultcollid) &&
 			fselect->resultcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, fselect->resultcollid);
 			add_object_address(CollationRelationId, fselect->resultcollid, 0,
 							   context->addrs);
+		}
 	}
 	else if (IsA(node, FieldStore))
 	{
@@ -1980,53 +2107,76 @@ find_expr_references_walker(Node *node,
 		if (OidIsValid(reltype))
 		{
 			ListCell   *l;
+			bool	locked = false;
 
 			foreach(l, fstore->fieldnums)
+			{
+				if (!locked)
+				{
+					LockNotPinnedObject(RelationRelationId, reltype);
+					locked = true;
+				}
 				add_object_address(RelationRelationId, reltype, lfirst_int(l),
 								   context->addrs);
+			}
 		}
 		else
+		{
+			LockNotPinnedObject(TypeRelationId, fstore->resulttype);
 			add_object_address(TypeRelationId, fstore->resulttype, 0,
 							   context->addrs);
+		}
 	}
 	else if (IsA(node, RelabelType))
 	{
 		RelabelType *relab = (RelabelType *) node;
 
 		/* since there is no function dependency, need to depend on type */
+		LockNotPinnedObject(TypeRelationId, relab->resulttype);
 		add_object_address(TypeRelationId, relab->resulttype, 0,
 						   context->addrs);
 		/* the collation might not be referenced anywhere else, either */
 		if (OidIsValid(relab->resultcollid) &&
 			relab->resultcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, relab->resultcollid);
 			add_object_address(CollationRelationId, relab->resultcollid, 0,
 							   context->addrs);
+		}
 	}
 	else if (IsA(node, CoerceViaIO))
 	{
 		CoerceViaIO *iocoerce = (CoerceViaIO *) node;
 
 		/* since there is no exposed function, need to depend on type */
+		LockNotPinnedObject(TypeRelationId, iocoerce->resulttype);
 		add_object_address(TypeRelationId, iocoerce->resulttype, 0,
 						   context->addrs);
 		/* the collation might not be referenced anywhere else, either */
 		if (OidIsValid(iocoerce->resultcollid) &&
 			iocoerce->resultcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, iocoerce->resultcollid);
 			add_object_address(CollationRelationId, iocoerce->resultcollid, 0,
 							   context->addrs);
+		}
 	}
 	else if (IsA(node, ArrayCoerceExpr))
 	{
 		ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
 
 		/* as above, depend on type */
+		LockNotPinnedObject(TypeRelationId, acoerce->resulttype);
 		add_object_address(TypeRelationId, acoerce->resulttype, 0,
 						   context->addrs);
 		/* the collation might not be referenced anywhere else, either */
 		if (OidIsValid(acoerce->resultcollid) &&
 			acoerce->resultcollid != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, acoerce->resultcollid);
 			add_object_address(CollationRelationId, acoerce->resultcollid, 0,
 							   context->addrs);
+		}
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, ConvertRowtypeExpr))
@@ -2034,6 +2184,7 @@ find_expr_references_walker(Node *node,
 		ConvertRowtypeExpr *cvt = (ConvertRowtypeExpr *) node;
 
 		/* since there is no function dependency, need to depend on type */
+		LockNotPinnedObject(TypeRelationId, cvt->resulttype);
 		add_object_address(TypeRelationId, cvt->resulttype, 0,
 						   context->addrs);
 	}
@@ -2041,6 +2192,7 @@ find_expr_references_walker(Node *node,
 	{
 		CollateExpr *coll = (CollateExpr *) node;
 
+		LockNotPinnedObject(CollationRelationId, coll->collOid);
 		add_object_address(CollationRelationId, coll->collOid, 0,
 						   context->addrs);
 	}
@@ -2048,6 +2200,7 @@ find_expr_references_walker(Node *node,
 	{
 		RowExpr    *rowexpr = (RowExpr *) node;
 
+		LockNotPinnedObject(TypeRelationId, rowexpr->row_typeid);
 		add_object_address(TypeRelationId, rowexpr->row_typeid, 0,
 						   context->addrs);
 	}
@@ -2058,11 +2211,13 @@ find_expr_references_walker(Node *node,
 
 		foreach(l, rcexpr->opnos)
 		{
+			LockNotPinnedObject(OperatorRelationId, lfirst_oid(l));
 			add_object_address(OperatorRelationId, lfirst_oid(l), 0,
 							   context->addrs);
 		}
 		foreach(l, rcexpr->opfamilies)
 		{
+			LockNotPinnedObject(OperatorFamilyRelationId, lfirst_oid(l));
 			add_object_address(OperatorFamilyRelationId, lfirst_oid(l), 0,
 							   context->addrs);
 		}
@@ -2072,6 +2227,7 @@ find_expr_references_walker(Node *node,
 	{
 		CoerceToDomain *cd = (CoerceToDomain *) node;
 
+		LockNotPinnedObject(TypeRelationId, cd->resulttype);
 		add_object_address(TypeRelationId, cd->resulttype, 0,
 						   context->addrs);
 	}
@@ -2079,6 +2235,7 @@ find_expr_references_walker(Node *node,
 	{
 		NextValueExpr *nve = (NextValueExpr *) node;
 
+		LockNotPinnedObject(RelationRelationId, nve->seqid);
 		add_object_address(RelationRelationId, nve->seqid, 0,
 						   context->addrs);
 	}
@@ -2087,19 +2244,26 @@ find_expr_references_walker(Node *node,
 		OnConflictExpr *onconflict = (OnConflictExpr *) node;
 
 		if (OidIsValid(onconflict->constraint))
+		{
+			LockNotPinnedObject(ConstraintRelationId, onconflict->constraint);
 			add_object_address(ConstraintRelationId, onconflict->constraint, 0,
 							   context->addrs);
+		}
 		/* fall through to examine arguments */
 	}
 	else if (IsA(node, SortGroupClause))
 	{
 		SortGroupClause *sgc = (SortGroupClause *) node;
 
+		LockNotPinnedObject(OperatorRelationId, sgc->eqop);
 		add_object_address(OperatorRelationId, sgc->eqop, 0,
 						   context->addrs);
 		if (OidIsValid(sgc->sortop))
+		{
+			LockNotPinnedObject(OperatorRelationId, sgc->sortop);
 			add_object_address(OperatorRelationId, sgc->sortop, 0,
 							   context->addrs);
+		}
 		return false;
 	}
 	else if (IsA(node, WindowClause))
@@ -2107,15 +2271,24 @@ find_expr_references_walker(Node *node,
 		WindowClause *wc = (WindowClause *) node;
 
 		if (OidIsValid(wc->startInRangeFunc))
+		{
+			LockNotPinnedObject(ProcedureRelationId, wc->startInRangeFunc);
 			add_object_address(ProcedureRelationId, wc->startInRangeFunc, 0,
 							   context->addrs);
+		}
 		if (OidIsValid(wc->endInRangeFunc))
+		{
+			LockNotPinnedObject(ProcedureRelationId, wc->endInRangeFunc);
 			add_object_address(ProcedureRelationId, wc->endInRangeFunc, 0,
 							   context->addrs);
+		}
 		if (OidIsValid(wc->inRangeColl) &&
 			wc->inRangeColl != DEFAULT_COLLATION_OID)
+		{
+			LockNotPinnedObject(CollationRelationId, wc->inRangeColl);
 			add_object_address(CollationRelationId, wc->inRangeColl, 0,
 							   context->addrs);
+		}
 		/* fall through to examine substructure */
 	}
 	else if (IsA(node, CTECycleClause))
@@ -2123,14 +2296,23 @@ find_expr_references_walker(Node *node,
 		CTECycleClause *cc = (CTECycleClause *) node;
 
 		if (OidIsValid(cc->cycle_mark_type))
+		{
+			LockNotPinnedObject(TypeRelationId, cc->cycle_mark_type);
 			add_object_address(TypeRelationId, cc->cycle_mark_type, 0,
 							   context->addrs);
+		}
 		if (OidIsValid(cc->cycle_mark_collation))
+		{
+			LockNotPinnedObject(CollationRelationId, cc->cycle_mark_collation);
 			add_object_address(CollationRelationId, cc->cycle_mark_collation, 0,
 							   context->addrs);
+		}
 		if (OidIsValid(cc->cycle_mark_neop))
+		{
+			LockNotPinnedObject(OperatorRelationId, cc->cycle_mark_neop);
 			add_object_address(OperatorRelationId, cc->cycle_mark_neop, 0,
 							   context->addrs);
+		}
 		/* fall through to examine substructure */
 	}
 	else if (IsA(node, Query))
@@ -2163,6 +2345,7 @@ find_expr_references_walker(Node *node,
 			switch (rte->rtekind)
 			{
 				case RTE_RELATION:
+					LockNotPinnedObject(RelationRelationId, rte->relid);
 					add_object_address(RelationRelationId, rte->relid, 0,
 									   context->addrs);
 					break;
@@ -2215,12 +2398,18 @@ find_expr_references_walker(Node *node,
 			rte = rt_fetch(query->resultRelation, query->rtable);
 			if (rte->rtekind == RTE_RELATION)
 			{
+				bool	locked = false;
 				foreach(lc, query->targetList)
 				{
 					TargetEntry *tle = (TargetEntry *) lfirst(lc);
 
 					if (tle->resjunk)
 						continue;	/* ignore junk tlist items */
+					if (!locked)
+					{
+						LockNotPinnedObject(RelationRelationId, rte->relid);
+						locked = true;
+					}
 					add_object_address(RelationRelationId, rte->relid, tle->resno,
 									   context->addrs);
 				}
@@ -2232,6 +2421,7 @@ find_expr_references_walker(Node *node,
 		 */
 		foreach(lc, query->constraintDeps)
 		{
+			LockNotPinnedObject(ConstraintRelationId, lfirst_oid(lc));
 			add_object_address(ConstraintRelationId, lfirst_oid(lc), 0,
 							   context->addrs);
 		}
@@ -2266,16 +2456,25 @@ find_expr_references_walker(Node *node,
 		 */
 		foreach(ct, rtfunc->funccoltypes)
 		{
+			LockNotPinnedObject(TypeRelationId, lfirst_oid(ct));
 			add_object_address(TypeRelationId, lfirst_oid(ct), 0,
 							   context->addrs);
 		}
 		foreach(ct, rtfunc->funccolcollations)
 		{
 			Oid			collid = lfirst_oid(ct);
+			bool	locked = false;
 
 			if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
+			{
+				if (!locked)
+				{
+					LockNotPinnedObject(CollationRelationId, collid);
+					locked = true;
+				}
 				add_object_address(CollationRelationId, collid, 0,
 								   context->addrs);
+			}
 		}
 	}
 	else if (IsA(node, TableFunc))
@@ -2288,22 +2487,32 @@ find_expr_references_walker(Node *node,
 		 */
 		foreach(ct, tf->coltypes)
 		{
+			LockNotPinnedObject(TypeRelationId, lfirst_oid(ct));
 			add_object_address(TypeRelationId, lfirst_oid(ct), 0,
 							   context->addrs);
 		}
 		foreach(ct, tf->colcollations)
 		{
 			Oid			collid = lfirst_oid(ct);
+			bool 	locked = false;
 
 			if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
+			{
+				if (!locked)
+				{
+					LockNotPinnedObject(CollationRelationId, collid);
+					locked = true;
+				}
 				add_object_address(CollationRelationId, collid, 0,
 								   context->addrs);
+			}
 		}
 	}
 	else if (IsA(node, TableSampleClause))
 	{
 		TableSampleClause *tsc = (TableSampleClause *) node;
 
+		LockNotPinnedObject(ProcedureRelationId, tsc->tsmhandler);
 		add_object_address(ProcedureRelationId, tsc->tsmhandler, 0,
 						   context->addrs);
 		/* fall through to examine arguments */
@@ -2354,9 +2563,12 @@ process_function_rte_ref(RangeTblEntry *rte, AttrNumber attnum,
 
 				Assert(attnum - atts_done <= tupdesc->natts);
 				if (OidIsValid(reltype))	/* can this fail? */
+				{
+					LockNotPinnedObject(RelationRelationId, reltype);
 					add_object_address(RelationRelationId, reltype,
 									   attnum - atts_done,
 									   context->addrs);
+				}
 				return;
 			}
 			/* Nothing to do; function's result type is handled elsewhere */
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 00074c8a94..1266101d90 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -844,6 +844,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		/* Add dependency info */
 		ObjectAddressSubSet(myself, RelationRelationId, new_rel_oid, i + 1);
 		ObjectAddressSet(referenced, TypeRelationId, attr->atttypid);
+		LockNotPinnedObject(TypeRelationId, attr->atttypid);
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);

 
 		if (reloftypeid)
 		{
 
 		/*

 
index a819b4197c..d6d1abfcf5 100644

 

 

 	/*

index 2983b9180f..d3af0ae726 100644
 
+/*
+ * ObjectByIdExist
+ *

 

 

 

 

 

 

 
 		if (foreignNKeys > 0)
 		{

 
 	if (foreignNKeys > 0)

 
 
index cfd7ef51df..ebca5a452b 100644

 

 
+	/*

 

 

 

 

 
 
 	/* Close the table */

 

 

 

 
 	return address;

 

 

 

 
 		/*

 

 	if (OidIsValid(useId))

 
 	/* dependency on type */

index 2caab88aa5..e6ff8476e8 100644

 

 
 
 		heap_freetuple(tup);
 
+		/*

 

 

 

 
+

 

 

index 9f28d40466..c0634d0af9 100644

 
 
index dbfe0d6b1c..fc7ddf0968 100644

 
 	/*

 
 
 		/* Done with the cloned constraint's tuple */
@@ -13248,7 +13258,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	table_close(attrelation, RowExclusiveLock);

 	/*
@@ -14810,6 +14822,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 		 */
 		ObjectAddressSet(relobj, RelationRelationId, reloid);
 		ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
+		LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
 		recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
 	}
 	else if (OidIsValid(oldAccessMethodId) &&
@@ -14829,6 +14842,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 			   OidIsValid(rd_rel->relam));
 
@@ -16428,6 +16442,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 
 	/* Update pg_class.reloftype */
@@ -17186,14 +17201,17 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
 		CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
 
index 170360edda..1250673b16 100644

+	/*

 

 
index b7b5019f1e..1b90e187ea 100644
 
 	/*
 	 * loop over the definition list and extract the information we need.
@@ -224,28 +225,48 @@ DefineTSParser(List *names, List *parameters)
 
 		if (strcmp(defel->defname, "start") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_parser_prsstart - 1] =
 				get_ts_parser_func(defel, Anum_pg_ts_parser_prsstart);
+			procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prsstart - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "gettoken") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_parser_prstoken - 1] =
 				get_ts_parser_func(defel, Anum_pg_ts_parser_prstoken);
+			procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prstoken - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "end") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_parser_prsend - 1] =
 				get_ts_parser_func(defel, Anum_pg_ts_parser_prsend);
+			procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prsend - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "headline") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_parser_prsheadline - 1] =
 				get_ts_parser_func(defel, Anum_pg_ts_parser_prsheadline);
+			procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prsheadline - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "lextypes") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_parser_prslextype - 1] =
 				get_ts_parser_func(defel, Anum_pg_ts_parser_prslextype);
+			procoid = DatumGetObjectId(values[Anum_pg_ts_parser_prslextype - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else
 			ereport(ERROR,
@@ -474,6 +495,10 @@ DefineTSDictionary(List *names, List *parameters)

 
@@ -723,6 +748,7 @@ DefineTSTemplate(List *names, List *parameters)
 
 	/*
 	 * loop over the definition list and extract the information we need.
@@ -733,15 +759,23 @@ DefineTSTemplate(List *names, List *parameters)
 
 		if (strcmp(defel->defname, "init") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_template_tmplinit - 1] =
 				get_ts_template_func(defel, Anum_pg_ts_template_tmplinit);
 			nulls[Anum_pg_ts_template_tmplinit - 1] = false;
+			procoid = DatumGetObjectId(values[Anum_pg_ts_template_tmplinit - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else if (strcmp(defel->defname, "lexize") == 0)
 		{
+			Oid			procoid;
+
 			values[Anum_pg_ts_template_tmpllexize - 1] =
 				get_ts_template_func(defel, Anum_pg_ts_template_tmpllexize);
 			nulls[Anum_pg_ts_template_tmpllexize - 1] = false;
+			procoid = DatumGetObjectId(values[Anum_pg_ts_template_tmpllexize - 1]);
+			LockNotPinnedObject(ProcedureRelationId, procoid);
 		}
 		else
 			ereport(ERROR,
@@ -998,6 +1032,10 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
 	values[Anum_pg_ts_config_cfgowner - 1] = ObjectIdGetDatum(GetUserId());
 	values[Anum_pg_ts_config_cfgparser - 1] = ObjectIdGetDatum(prsOid);

 	CatalogTupleInsert(cfgRel, tup);
@@ -1063,6 +1101,7 @@ DefineTSConfiguration(List *names, List *parameters, ObjectAddress *copied)
 			slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapseqno - 1] = cfgmap->mapseqno;
 			slot[slot_stored_count]->tts_values[Anum_pg_ts_config_map_mapdict - 1] = cfgmap->mapdict;
 
+			LockNotPinnedObject(TSDictionaryRelationId, cfgmap->mapdict);
 			ExecStoreVirtualTuple(slot[slot_stored_count]);
 			slot_stored_count++;
 
@@ -1156,9 +1195,13 @@ ObjectAddress
 AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
 {
 	HeapTuple	tup;
+	Form_pg_ts_config cfg;
 	Oid			cfgId;
 	Relation	relMap;
 	ObjectAddress address;
+	ScanKeyData skey;
+	SysScanDesc scan;
+	HeapTuple	maptup;
 
 	/* Find the configuration */
 	tup = GetTSConfigTuple(stmt->cfgname);
@@ -1168,7 +1211,8 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
 				 errmsg("text search configuration \"%s\" does not exist",
 						NameListToString(stmt->cfgname))));

 	/* must be owner */
 	if (!object_ownercheck(TSConfigRelationId, cfgId, GetUserId()))
@@ -1183,6 +1227,28 @@ AlterTSConfiguration(AlterTSConfigurationStmt *stmt)
 	else if (stmt->tokentype)
 		DropConfigurationMapping(stmt, tup, relMap);
 
+	/* Lock dependent objects */
+
+	ScanKeyInit(&skey,
+				Anum_pg_ts_config_map_mapcfg,
+				BTEqualStrategyNumber, F_OIDEQ,
+				ObjectIdGetDatum(cfgId));
+
+	scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
+							  NULL, 1, &skey);
+
+	while (HeapTupleIsValid((maptup = systable_getnext(scan))))
+	{
+		Form_pg_ts_config_map cfgmap = (Form_pg_ts_config_map) GETSTRUCT(maptup);
+
+		LockNotPinnedObject(TSDictionaryRelationId, cfgmap->mapdict);
+	}
+
+	systable_endscan(scan);
+
+	LockNotPinnedObject(NamespaceRelationId, cfg->cfgnamespace);
+	LockNotPinnedObject(TSParserRelationId, cfg->cfgparser);
+
 	/* Update dependencies */
 	makeConfigurationDependencies(tup, true, relMap);
 
@@ -1414,6 +1480,8 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,
 				repl_val[Anum_pg_ts_config_map_mapdict - 1] = ObjectIdGetDatum(dictNew);
 				repl_repl[Anum_pg_ts_config_map_mapdict - 1] = true;
 
@@ -1456,6 +1524,9 @@ MakeConfigurationMapping(AlterTSConfigurationStmt *stmt,

 

 

 

 

index 6908ca7180..0546bcbe16 100644

 

 
 
index 6da98cffac..ef6a7075bc 100644
 
 commit;
 begin;
-- 
2.34.1

---

From: Bertrand Drouvot <[email-masked]>
Date: Mon, 19 Aug 2024 15:35:14 +0000

Hi,

> there is none. That may add unnecessary lock for 2. but that seems worth it. 

Please find attached v16, mandatory rebase due to 80ffcb8427.

Regards,

>From 79c81071b061466ef51ca863d1b0189298430984 Mon Sep 17 00:00:00 2001
From: Bertrand Drouvot <[email-masked]>
Date: Fri, 29 Mar 2024 15:43:26 +0000
Subject: [PATCH v16] Avoid orphaned objects dependencies

Scenario 1:

Scenario 2:

 src/backend/catalog/pg_publication.c          |  11 +
 46 files changed, 1015 insertions(+), 25 deletions(-)
  39.7% src/backend/catalog/
  30.4% src/backend/commands/

 
+/*

 
 		/*
 
 				case REGNAMESPACEOID:
 
 					/*

 

 

 

 

 

 

 

 					if (tle->resjunk)

 
 
index 01b43cc6a8..97600e7a3b 100644

 
 		if (reloftypeid)
 		{
 
 		/*

 

 

 

 	/*

index 85a7b7e641..17c4085948 100644
 
+/*
+ * ObjectByIdExist
+ *

 

 

 

 

 

 

 
 		if (foreignNKeys > 0)
 		{

 
 	if (foreignNKeys > 0)

 

 

 
 
+	/*

 

 

 
-		/* Advance command counter so new tuple can be seen by validator */
-		CommandCounterIncrement();
-
 		/*
 		 * Set per-function configuration parameters so that the validation is
 		 * done with the environment the function expects.  However, if
diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 7fe5fe2b86..d533e4fd31 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -395,6 +395,7 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 				referenced;
 	List	   *relids = NIL;
 	int			i;
+	bool		locked = false;
 
 	rel = table_open(PublicationRelRelationId, RowExclusiveLock);
 
@@ -457,10 +458,13 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,

 
 	/* Add dependency on the objects mentioned in the qualifications */
@@ -474,6 +478,11 @@ publication_add_relation(Oid pubid, PublicationRelInfo *pri,
 	while ((i = bms_next_member(attnums, i)) >= 0)
 	{
 		ObjectAddressSubSet(referenced, RelationRelationId, relid, i);
+		if (!locked)
+		{
+			LockNotPinnedObject(RelationRelationId, relid);
+			locked = true;
+		}
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
@@ -643,10 +652,12 @@ publication_add_schema(Oid pubid, Oid schemaid, bool if_not_exists)

 
 	/* Close the table */

 

 

 

 
 	return address;

 

 

 

 
 		/*

 

 	if (OidIsValid(useId))

 
 	/* dependency on type */

index c5a56c75f6..fc7a28baf9 100644
@@ -4384,8 +4384,10 @@ IndexSetParentIndex(Relation partitionIdx, Oid parentOid)

 

 
 
 		heap_freetuple(tup);
 
+		/*

 

 

 

 
+

 

 

index 8c1131f020..b94677ae65 100644

 
 
index 7a36db6af6..ff605bd0ff 100644

 
 	/*

 
@@ -10984,9 +10991,12 @@ CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
 
 		/* Done with the cloned constraint's tuple */
@@ -13272,7 +13282,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	table_close(attrelation, RowExclusiveLock);

 	/*
@@ -14834,6 +14846,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 		 */
 		ObjectAddressSet(relobj, RelationRelationId, reloid);
 		ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
+		LockNotPinnedObject(AccessMethodRelationId, rd_rel->relam);
 		recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
 	}
 	else if (OidIsValid(oldAccessMethodId) &&
@@ -14853,6 +14866,7 @@ ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
 			   OidIsValid(rd_rel->relam));
 
@@ -16452,6 +16466,7 @@ ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 
 	/* Update pg_class.reloftype */
@@ -17210,14 +17225,17 @@ AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
 		CatalogTupleUpdate(classRel, &classTup->t_self, classTup);

 
+	/*

 

 
 
 	/*

 

 	/*

 

 

 	/* must be owner */

 

 

 

 

 
index b43a24d4bc..ae41f3613c 100644

 

 

 
index 79cf82b5ae..1ed23c54cb 100644
 
 commit;
 begin;
-- 
2.34.1