From 8ba82796a82354a10ab5adedfec0d823d07eefd8 Mon Sep 17 00:00:00 2001
From: ChangAo Chen <cca5507@qq.com>
Date: Sun, 14 Jun 2026 14:37:12 +0800
Subject: [PATCH v1] Handle concurrent drop when doing whole database vacuum.

When doing a whole database vacuum, we scan pg_class to construct
a list of vacuumable tables. For each vacuumable table, we call
vacuum_is_permitted_for_relation() to check permissions. If a
concurrent drop happens, the pg_class_aclcheck() might report an
error because of failing to search the syscache.

To fix it, we use pg_class_aclcheck_ext() to detect the concurrent
drop and report a warning instead.
---
 src/backend/commands/vacuum.c | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index a4abb29cf64..4291cb8410c 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -721,6 +721,7 @@ vacuum_is_permitted_for_relation(Oid relid, Form_pg_class reltuple,
 								 uint32 options)
 {
 	char	   *relname;
+	bool		is_missing = false;
 
 	Assert((options & (VACOPT_VACUUM | VACOPT_ANALYZE)) != 0);
 
@@ -733,16 +734,21 @@ vacuum_is_permitted_for_relation(Oid relid, Form_pg_class reltuple,
 	 */
 	if ((object_ownercheck(DatabaseRelationId, MyDatabaseId, GetUserId()) &&
 		 !reltuple->relisshared) ||
-		pg_class_aclcheck(relid, GetUserId(), ACL_MAINTAIN) == ACLCHECK_OK)
+		pg_class_aclcheck_ext(relid, GetUserId(), ACL_MAINTAIN, &is_missing) == ACLCHECK_OK)
 		return true;
 
 	relname = NameStr(reltuple->relname);
 
 	if ((options & VACOPT_VACUUM) != 0)
 	{
-		ereport(WARNING,
-				(errmsg("permission denied to vacuum \"%s\", skipping it",
-						relname)));
+		if (is_missing)
+			ereport(WARNING,
+					(errmsg("skipping vacuum of \"%s\" --- relation no longer exists",
+							relname)));
+		else
+			ereport(WARNING,
+					(errmsg("permission denied to vacuum \"%s\", skipping it",
+							relname)));
 
 		/*
 		 * For VACUUM ANALYZE, both logs could show up, but just generate
@@ -753,9 +759,16 @@ vacuum_is_permitted_for_relation(Oid relid, Form_pg_class reltuple,
 	}
 
 	if ((options & VACOPT_ANALYZE) != 0)
-		ereport(WARNING,
-				(errmsg("permission denied to analyze \"%s\", skipping it",
-						relname)));
+	{
+		if (is_missing)
+			ereport(WARNING,
+					(errmsg("skipping analyze of \"%s\" --- relation no longer exists",
+							relname)));
+		else
+			ereport(WARNING,
+					(errmsg("permission denied to analyze \"%s\", skipping it",
+							relname)));
+	}
 
 	return false;
 }
-- 
2.54.0

