From 86517f376339a65fcf432caa21dbc68d8c2a93a4 Mon Sep 17 00:00:00 2001
From: Jacob Champion <jchampion@timescale.com>
Date: Thu, 16 Mar 2023 11:46:08 -0700
Subject: [PATCH] pg_dump: skip lock for extension tables without policies

If a user without SELECT permissions on an internal extension table
tries to dump the extension, the dump will fail while trying to lock the
table with ACCESS SHARE, even though the user doesn't want or need to
dump the table in question. (The lock is taken to allow later
pg_get_expr() calls on pg_policy to remain consistent in the face of
concurrent schema changes.)

It'd be ideal not to require SELECT permissions on a table to be able to
dump its policies, but I don't have a great idea for how to implement
that without races. As a workaround, skip the policy queries entirely if
we determine that no policies exist for a table at the time of
getTables().
---
 src/bin/pg_dump/pg_dump.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2e068c6620..49f01247c1 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6317,6 +6317,7 @@ getTables(Archive *fout, int *numTables)
 	int			i_relacl;
 	int			i_acldefault;
 	int			i_ispartition;
+	int			i_has_policies;
 
 	/*
 	 * Find all the tables and table-like objects.
@@ -6358,6 +6359,13 @@ getTables(Archive *fout, int *numTables)
 						 "d.refobjsubid AS owning_col, "
 						 "tsp.spcname AS reltablespace, ");
 
+	if (fout->remoteVersion >= 90500)
+		appendPQExpBufferStr(query,
+							 "EXISTS (SELECT 1 FROM pg_policy WHERE polrelid = c.oid) AS has_policies, ");
+	else
+		appendPQExpBufferStr(query,
+							 "false AS has_policies, ");
+
 	if (fout->remoteVersion >= 120000)
 		appendPQExpBufferStr(query,
 							 "false AS relhasoids, ");
@@ -6531,6 +6539,7 @@ getTables(Archive *fout, int *numTables)
 	i_relacl = PQfnumber(res, "relacl");
 	i_acldefault = PQfnumber(res, "acldefault");
 	i_ispartition = PQfnumber(res, "ispartition");
+	i_has_policies = PQfnumber(res, "has_policies");
 
 	if (dopt->lockWaitTimeout)
 	{
@@ -6621,6 +6630,17 @@ getTables(Archive *fout, int *numTables)
 		else
 			selectDumpableTable(&tblinfo[i], fout);
 
+		/*
+		 * If the table has no policies, we don't need to worry about those.
+		 *
+		 * For tables internal to an extension, this may mean we don't need to
+		 * take an ACCESS SHARE lock, which in turn allows less privileged users
+		 * to successfully perform a dump if they don't have SELECT access to
+		 * those tables (which they weren't trying to dump in the first place).
+		 */
+		if (strcmp(PQgetvalue(res, i, i_has_policies), "f") == 0)
+			tblinfo[i].dobj.dump &= ~DUMP_COMPONENT_POLICY;
+
 		/*
 		 * Now, consider the table "interesting" if we need to dump its
 		 * definition or its data.  Later on, we'll skip a lot of data
-- 
2.25.1

