From 8deefb638df270cf26e5649b1a99f218474821fa Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 7 Jun 2024 11:25:03 +0800
Subject: [PATCH v1 1/1] trying to resolve drop extension deletion order

sometimes, drop extension cascade cannot resolve the internal deletion order correctly.
e.g.
drop table if exist test__int cascade;
create extension intarray;
CREATE TABLE test__int( a int[] );
CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 1));
drop extension intarray cascade;
----

the index "text_idx" only have a normal dependency with pg_opclass.
even though the index can be dropped separately without affecting the "pg_opclass".

but CASCADE means that we drop the pg_opclass and pg_opclass's inner dependency
first, then drop the index.

while drop index (sub functions RelationGetIndexAttOptions,index_opclass_options, index_getprocinfo)
requires that pg_opclass and its inner dependencies are not dropped first.

Resorting the deleted objects in deleteObjectsInList using sort_object_addresses seems like a hack.
but it works for now.

discussion: https://www.postgresql.org/message-id/CACJufxEspPKC7oxVLci7oUddUmcAGNKJnWWSD7-B03bGtT9gDg%40mail.gmail.com
---
 src/backend/catalog/dependency.c  | 33 +++++++++++++++++++++++++++----
 src/backend/catalog/pg_shdepend.c |  2 +-
 src/backend/commands/dropcmds.c   |  2 +-
 src/backend/commands/indexcmds.c  |  2 +-
 src/backend/commands/tablecmds.c  | 13 +++++++-----
 src/include/catalog/dependency.h  |  2 +-
 6 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index d4b5b2ad..d0c2454b 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -183,7 +183,7 @@ static void DeleteInitPrivs(const ObjectAddress *object);
  */
 static void
 deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
-					int flags)
+					int flags, bool sort_objects)
 {
 	int			i;
 
@@ -213,6 +213,8 @@ deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
 		}
 	}
 
+	if (sort_objects)
+		sort_object_addresses(targetObjects);
 	/*
 	 * Delete all the objects in the proper order, except that if told to, we
 	 * should skip the original object(s).
@@ -311,7 +313,7 @@ performDeletion(const ObjectAddress *object,
 						   object);
 
 	/* do the deed */
-	deleteObjectsInList(targetObjects, &depRel, flags);
+	deleteObjectsInList(targetObjects, &depRel, flags, false);
 
 	/* And clean up */
 	free_object_addresses(targetObjects);
@@ -330,16 +332,39 @@ performDeletion(const ObjectAddress *object,
  */
 void
 performMultipleDeletions(const ObjectAddresses *objects,
-						 DropBehavior behavior, int flags)
+						 DropBehavior behavior, int flags, bool sortable)
 {
 	Relation	depRel;
 	ObjectAddresses *targetObjects;
 	int			i;
+	bool		sort_objects = true;
+	char		relkind = '\0';
 
 	/* No work if no objects... */
 	if (objects->numrefs <= 0)
 		return;
 
+	for (int j = 0; j < objects->numrefs; j++)
+	{
+		const ObjectAddress *thisobj = objects->refs + j;
+		if (thisobj->classId != RelationRelationId)
+			continue;
+		relkind = get_rel_relkind(thisobj->objectId);
+
+		/* 
+		 * for partitioned table, sequence findDependentObjects can resolve
+		 * the delete objects order correctly, othercase, we can sort the
+		 * to be deleted objects via sort_object_addresses in deleteObjectsInList
+		*/
+		if (relkind == RELKIND_PARTITIONED_TABLE || relkind == RELKIND_SEQUENCE)
+		{
+			sort_objects = false;
+			break;
+		}
+	}
+
+	if (!(sortable && sort_objects))
+		sort_objects = false;
 	/*
 	 * We save some cycles by opening pg_depend just once and passing the
 	 * Relation pointer down to all the recursive deletion steps.
@@ -387,7 +412,7 @@ performMultipleDeletions(const ObjectAddresses *objects,
 						   (objects->numrefs == 1 ? objects->refs : NULL));
 
 	/* do the deed */
-	deleteObjectsInList(targetObjects, &depRel, flags);
+	deleteObjectsInList(targetObjects, &depRel, flags, sort_objects);
 
 	/* And clean up */
 	free_object_addresses(targetObjects);
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index 20bcfd77..8aef6596 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -1498,7 +1498,7 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
 	sort_object_addresses(deleteobjs);
 
 	/* the dependency mechanism does the actual work */
-	performMultipleDeletions(deleteobjs, behavior, 0);
+	performMultipleDeletions(deleteobjs, behavior, 0, true);
 
 	table_close(sdepRel, RowExclusiveLock);
 
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 85eec7e3..7aadd71b 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -120,7 +120,7 @@ RemoveObjects(DropStmt *stmt)
 	}
 
 	/* Here we really delete them. */
-	performMultipleDeletions(objects, stmt->behavior, 0);
+	performMultipleDeletions(objects, stmt->behavior, 0, true);
 
 	free_object_addresses(objects);
 }
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 309389e2..d70a112a 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -4209,7 +4209,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
 		 * right lock level.
 		 */
 		performMultipleDeletions(objects, DROP_RESTRICT,
-								 PERFORM_DELETION_CONCURRENT_LOCK | PERFORM_DELETION_INTERNAL);
+								 PERFORM_DELETION_CONCURRENT_LOCK | PERFORM_DELETION_INTERNAL, true);
 	}
 
 	PopActiveSnapshot();
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7b6c69b7..ab005cc2 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1459,6 +1459,7 @@ RemoveRelations(DropStmt *drop)
 	ListCell   *cell;
 	int			flags = 0;
 	LOCKMODE	lockmode = AccessExclusiveLock;
+	bool		sortable = true;
 
 	/* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
 	if (drop->concurrent)
@@ -1604,7 +1605,9 @@ RemoveRelations(DropStmt *drop)
 		add_exact_object_address(&obj, objects);
 	}
 
-	performMultipleDeletions(objects, drop->behavior, flags);
+	if (list_length(drop->objects) > 1)
+		sortable = false;
+	performMultipleDeletions(objects, drop->behavior, flags, sortable);
 
 	free_object_addresses(objects);
 }
@@ -9100,7 +9103,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
 	if (!recursing)
 	{
 		/* Recursion has ended, drop everything that was collected */
-		performMultipleDeletions(addrs, behavior, 0);
+		performMultipleDeletions(addrs, behavior, 0, true);
 		free_object_addresses(addrs);
 	}
 
@@ -13812,7 +13815,7 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 	 * It should be okay to use DROP_RESTRICT here, since nothing else should
 	 * be depending on these objects.
 	 */
-	performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+	performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL, true);
 
 	free_object_addresses(objects);
 
@@ -17448,7 +17451,7 @@ PreCommit_on_commit_actions(void)
 		 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
 		 */
 		performMultipleDeletions(targetObjects, DROP_CASCADE,
-								 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
+								 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY, true);
 
 		PopActiveSnapshot();
 
@@ -19488,7 +19491,7 @@ DropClonedTriggersFromPartition(Oid partitionId)
 
 	/* make the dependency removal visible to the deletion below */
 	CommandCounterIncrement();
-	performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+	performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL, true);
 
 	/* done */
 	free_object_addresses(objects);
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 7eee66f8..f9739f87 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -108,7 +108,7 @@ extern void performDeletion(const ObjectAddress *object,
 							DropBehavior behavior, int flags);
 
 extern void performMultipleDeletions(const ObjectAddresses *objects,
-									 DropBehavior behavior, int flags);
+									 DropBehavior behavior, int flags, bool sortable);
 
 extern void recordDependencyOnExpr(const ObjectAddress *depender,
 								   Node *expr, List *rtable,
-- 
2.34.1

