From 48667c560fcc1d8c37b118f684a129db01f0d346 Mon Sep 17 00:00:00 2001
From: Nikita Malakhov <n.malakhov@postgrespro.ru>
Date: Mon, 16 Jan 2023 17:14:07 +0300
Subject: [PATCH] The following test case found by PostgresPro team:

In the following example, partitioned tables and regular tables behave
differently:

CREATE TABLE vacuum_tab (a int) PARTITION BY HASH (a);
CREATE TABLE vacuum_tab_1 PARTITION OF vacuum_tab FOR VALUES WITH
(MODULUS 2, REMAINDER 0);
CREATE TABLE vacuum_tab_2 PARTITION OF vacuum_tab FOR VALUES WITH
(MODULUS 2, REMAINDER 1);
CREATE ROLE regress_vacuum_conflict;

In the first session:

begin;
  LOCK vacuum_tab IN SHARE UPDATE EXCLUSIVE MODE;

In the second:
SET ROLE regress_vacuum_conflict;
  VACUUM vacuum_tab;
  WARNING:  permission denied to vacuum "vacuum_tab", skipping it <----
hangs here, trying to lock vacuum_tab_1

In non-partitioned case second session exits after emitting warning. In
partitioned case, it hangs, trying to get locks.
This is due to the fact that in expand_vacuum_rel() we skip parent table
if vacuum_is_permitted_for_relation(), but don't perform such check for
its child.

The fix adds vacuum_is_permitted_for_relation() check before adding
partition relation to the vacuum list, and while performing vacuum
this emits warning that permission for vacuum is denied, and goes on
without trying to acquire the lock.
---
 src/backend/commands/vacuum.c | 29 +++++++++++++++++++++++------
 1 file changed, 23 insertions(+), 6 deletions(-)

diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index c4ed7efce3..99f6e334ee 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -849,7 +849,9 @@ expand_vacuum_rel(VacuumRelation *vrel, int options)
 
 			foreach(part_lc, part_oids)
 			{
-				Oid			part_oid = lfirst_oid(part_lc);
+				Oid				part_oid = lfirst_oid(part_lc);
+				HeapTuple		part_tuple;
+				Form_pg_class	part_classForm;
 
 				if (part_oid == relid)
 					continue;	/* ignore original table */
@@ -859,11 +861,26 @@ expand_vacuum_rel(VacuumRelation *vrel, int options)
 				 * complain about failure to open one of these relations
 				 * later.
 				 */
-				oldcontext = MemoryContextSwitchTo(vac_context);
-				vacrels = lappend(vacrels, makeVacuumRelation(NULL,
-															  part_oid,
-															  vrel->va_cols));
-				MemoryContextSwitchTo(oldcontext);
+
+				/*
+				 * Check partition relations for vacuum permit. Do not add
+				 * them to the list if vacuum is not permitted.
+				 */
+				part_tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(part_oid));
+				if (HeapTupleIsValid(part_tuple))
+				{
+					part_classForm = (Form_pg_class) GETSTRUCT(part_tuple);
+
+					if (vacuum_is_permitted_for_relation(part_oid, part_classForm, options))
+					{
+						oldcontext = MemoryContextSwitchTo(vac_context);
+						vacrels = lappend(vacrels, makeVacuumRelation(NULL,
+																	  part_oid,
+																	  vrel->va_cols));
+						MemoryContextSwitchTo(oldcontext);
+					}
+				}
+				ReleaseSysCache(part_tuple);
 			}
 		}
 
-- 
2.25.1

