Hi all,

v3 fixes a doc comment I forgot to fill in; there are no other code
changes. To try to further reduce the activation energy, I've also
attached an attempt at a backport to 11. The main difference is the
absence of catalogIdHash, which showed up in 15, so we don't get the
benefit of that deduplication.

Thanks,
--Jacob
1:  6373939500 = 1:  6373939500 Add failing test for undumped extension table
2:  41db5f9c75 ! 2:  3e997da147 pg_dump: skip lock for extension tables without 
policies
    @@ src/bin/pg_dump/pg_dump.c: dumpLOs(Archive *fout, const void *arg)
      }
      
     +/*
    -+ * getTablesWithPolicies TODO
    ++ * getTablesWithPolicies
    ++ *   retrieve the IDs of all tables with pg_policy entries
     + */
     +void
     +getTablesWithPolicies(Archive *fout)
From 637393950006c3752c6db3ca64f84b18fcaa4896 Mon Sep 17 00:00:00 2001
From: Jacob Champion <jchamp...@timescale.com>
Date: Thu, 22 Jun 2023 16:21:41 -0700
Subject: [PATCH v3 1/2] Add failing test for undumped extension table

Currently, SELECT permission is required for extension tables even
if they're internal (i.e. undumpable) and have no RLS policies. Add a
failing test for this situation.
---
 src/test/modules/test_pg_dump/t/001_base.pl   | 27 +++++++++++++++++++
 .../test_pg_dump/test_pg_dump--1.0.sql        |  2 ++
 2 files changed, 29 insertions(+)

diff --git a/src/test/modules/test_pg_dump/t/001_base.pl b/src/test/modules/test_pg_dump/t/001_base.pl
index d00c3544e9..68a767d2f5 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -175,6 +175,19 @@ my %pgdump_runs = (
 			'postgres',
 		],
 	},
+
+	# regress_dump_login_role shouldn't need SELECT rights on internal
+	# (undumped) extension tables
+	privileged_internals => {
+		dump_cmd => [
+			'pg_dump', '--no-sync', "--file=$tempdir/privileged_internals.sql",
+			# these two tables are irrelevant to the test case
+			'--exclude-table=regress_pg_dump_schema.external_tab',
+			'--exclude-table=regress_pg_dump_schema.extdependtab',
+			'--username=regress_dump_login_role', 'postgres',
+		],
+	},
+
 	schema_only => {
 		dump_cmd => [
 			'pg_dump', '--no-sync', "--file=$tempdir/schema_only.sql",
@@ -284,6 +297,7 @@ my %full_runs = (
 	exclude_table => 1,
 	no_privs => 1,
 	no_owner => 1,
+	privileged_internals => 1,
 	with_extension => 1,
 	without_extension => 1);
 
@@ -321,6 +335,16 @@ my %tests = (
 		like => { pg_dumpall_globals => 1, },
 	},
 
+	'CREATE ROLE regress_dump_login_role' => {
+		create_order => 1,
+		create_sql => 'CREATE ROLE regress_dump_login_role LOGIN;',
+		regexp => qr/^
+			\QCREATE ROLE regress_dump_login_role;\E
+			\n\QALTER ROLE regress_dump_login_role WITH \E.*\Q LOGIN \E.*;
+			\n/xm,
+		like => { pg_dumpall_globals => 1, },
+	},
+
 	'GRANT ALTER SYSTEM ON PARAMETER full_page_writes TO regress_dump_test_role'
 	  => {
 		create_order => 2,
@@ -704,6 +728,7 @@ my %tests = (
 			data_only => 1,
 			extension_schema => 1,
 			pg_dumpall_globals => 1,
+			privileged_internals => 1,
 			section_data => 1,
 			section_pre_data => 1,
 			# Excludes this schema as extension is not listed.
@@ -720,6 +745,7 @@ my %tests = (
 			data_only => 1,
 			extension_schema => 1,
 			pg_dumpall_globals => 1,
+			privileged_internals => 1,
 			section_data => 1,
 			section_pre_data => 1,
 			# Excludes this schema as extension is not listed.
@@ -743,6 +769,7 @@ my %tests = (
 			# Excludes the extension and keeps the schema's data.
 			without_extension_internal_schema => 1,
 		},
+		unlike => { privileged_internals => 1 },
 	},);
 
 #########################################
diff --git a/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql b/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql
index 110f7eef66..1c68e146d9 100644
--- a/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql
+++ b/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql
@@ -12,11 +12,13 @@ CREATE SEQUENCE regress_pg_dump_seq;
 
 CREATE SEQUENCE regress_seq_dumpable;
 SELECT pg_catalog.pg_extension_config_dump('regress_seq_dumpable', '');
+GRANT SELECT ON SEQUENCE regress_seq_dumpable TO public;
 
 CREATE TABLE regress_table_dumpable (
 	col1 int check (col1 > 0)
 );
 SELECT pg_catalog.pg_extension_config_dump('regress_table_dumpable', '');
+GRANT SELECT ON regress_table_dumpable TO public;
 
 CREATE SCHEMA regress_pg_dump_schema;
 
-- 
2.25.1

From 3e997da1472d4c7bc067a2178227c346bee91bfb Mon Sep 17 00:00:00 2001
From: Jacob Champion <jchamp...@timescale.com>
Date: Thu, 16 Mar 2023 11:46:08 -0700
Subject: [PATCH v3 2/2] 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 can determine that no policies exist for a table at the time of
getTables().

Fixes the previous commit's failing test.
---
 src/bin/pg_dump/common.c  | 54 +++++++++++++++++++++++++++++++++++
 src/bin/pg_dump/pg_dump.c | 59 +++++++++++++++++++++++++++++++++++++++
 src/bin/pg_dump/pg_dump.h |  4 +++
 3 files changed, 117 insertions(+)

diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 5d988986ed..69df3567f9 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -59,6 +59,7 @@ typedef struct _catalogIdMapEntry
 	uint32		hashval;		/* hash code for the CatalogId */
 	DumpableObject *dobj;		/* the associated DumpableObject, if any */
 	ExtensionInfo *ext;			/* owning extension, if any */
+	bool		has_policies;	/* referenced by pg_policy? */
 } CatalogIdMapEntry;
 
 #define SH_PREFIX		catalogid
@@ -135,6 +136,13 @@ getSchemaData(Archive *fout, int *numTablesPtr)
 	pg_log_info("identifying extension members");
 	getExtensionMembership(fout, extinfo, numExtensions);
 
+	/*
+	 * Similarly, the existence of RLS policies influences whether some tables
+	 * need to be locked.
+	 */
+	pg_log_info("checking for row-level security policies");
+	getTablesWithPolicies(fout);
+
 	pg_log_info("reading schemas");
 	(void) getNamespaces(fout, &numNamespaces);
 
@@ -686,6 +694,7 @@ AssignDumpId(DumpableObject *dobj)
 		{
 			entry->dobj = NULL;
 			entry->ext = NULL;
+			entry->has_policies = false;
 		}
 		Assert(entry->dobj == NULL);
 		entry->dobj = dobj;
@@ -995,6 +1004,7 @@ recordExtensionMembership(CatalogId catId, ExtensionInfo *ext)
 	{
 		entry->dobj = NULL;
 		entry->ext = NULL;
+		entry->has_policies = false;
 	}
 	Assert(entry->ext == NULL);
 	entry->ext = ext;
@@ -1019,6 +1029,50 @@ findOwningExtension(CatalogId catalogId)
 }
 
 
+/*
+ * recordPoliciesExist
+ *	  Record that the object identified by the given catalog ID has RLS policies
+ */
+void
+recordPoliciesExist(CatalogId catId)
+{
+	CatalogIdMapEntry *entry;
+	bool		found;
+
+	/* Initialize CatalogId hash table if not done yet */
+	if (catalogIdHash == NULL)
+		catalogIdHash = catalogid_create(CATALOGIDHASH_INITIAL_SIZE, NULL);
+
+	/* Add reference to CatalogId hash */
+	entry = catalogid_insert(catalogIdHash, catId, &found);
+	if (!found)
+	{
+		entry->dobj = NULL;
+		entry->ext = NULL;
+		entry->has_policies = false;
+	}
+	entry->has_policies = true;
+}
+
+/*
+ * hasPolicies
+ *	  return whether the specified catalog ID has RLS policies
+ */
+bool
+hasPolicies(CatalogId catId)
+{
+	CatalogIdMapEntry *entry;
+
+	if (catalogIdHash == NULL)
+		return false;			/* no objects exist yet */
+
+	entry = catalogid_lookup(catalogIdHash, catId);
+	if (entry == NULL)
+		return false;
+	return entry->has_policies;
+}
+
+
 /*
  * parseOidArray
  *	  parse a string of numbers delimited by spaces into a character array
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 5dab1ba9ea..6add01ca73 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -3742,6 +3742,54 @@ dumpLOs(Archive *fout, const void *arg)
 	return 1;
 }
 
+/*
+ * getTablesWithPolicies
+ *   retrieve the IDs of all tables with pg_policy entries
+ */
+void
+getTablesWithPolicies(Archive *fout)
+{
+	PQExpBuffer query;
+	PGresult   *res;
+	int			i_classid;
+	int			i_polrelid;
+	int			i,
+				ntups;
+
+	/* No policies before 9.5 */
+	if (fout->remoteVersion < 90500)
+		return;
+
+	query = createPQExpBuffer();
+
+	/* Figure out which tables have RLS policies. */
+	printfPQExpBuffer(query,
+					  "SELECT DISTINCT 'pg_class'::regclass::oid AS classid, "
+					  "                polrelid "
+					  "FROM pg_catalog.pg_policy");
+
+	res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+	ntups = PQntuples(res);
+
+	i_classid = PQfnumber(res, "classid");
+	i_polrelid = PQfnumber(res, "polrelid");
+
+	for (i = 0; i < ntups; i++)
+	{
+		CatalogId	objId;
+
+		objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
+		objId.oid = atooid(PQgetvalue(res, i, i_polrelid));
+
+		recordPoliciesExist(objId);
+	}
+
+	PQclear(res);
+
+	destroyPQExpBuffer(query);
+}
+
 /*
  * getPolicies
  *	  get information about all RLS policies on dumpable tables.
@@ -6658,6 +6706,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 (!hasPolicies(tblinfo[i].dobj.catId))
+			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
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index bc8f2ec36d..5dea0b63d6 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -695,6 +695,9 @@ extern PublicationInfo *findPublicationByOid(Oid oid);
 extern void recordExtensionMembership(CatalogId catId, ExtensionInfo *ext);
 extern ExtensionInfo *findOwningExtension(CatalogId catalogId);
 
+extern void recordPoliciesExist(CatalogId catId);
+extern bool hasPolicies(CatalogId catId);
+
 extern void parseOidArray(const char *str, Oid *array, int arraysize);
 
 extern void sortDumpableObjects(DumpableObject **objs, int numObjs,
@@ -743,6 +746,7 @@ extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
 extern void processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
 								   int numExtensions);
 extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
+extern void getTablesWithPolicies(Archive *fout);
 extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);
 extern PublicationInfo *getPublications(Archive *fout,
 										int *numPublications);
-- 
2.25.1

From 20b1c7f1e5375a7db0136a6332403d199beebd7f Mon Sep 17 00:00:00 2001
From: Jacob Champion <jchamp...@timescale.com>
Date: Thu, 22 Jun 2023 16:21:41 -0700
Subject: [PATCH v3 1/2] Add failing test for undumped extension table

[backpatch to 11]

Currently, SELECT permission is required for extension tables even
if they're internal (i.e. undumpable) and have no RLS policies. Add a
failing test for this situation.
---
 src/test/modules/test_pg_dump/t/001_base.pl   | 63 +++++++++++++------
 .../test_pg_dump/test_pg_dump--1.0.sql        |  2 +
 2 files changed, 47 insertions(+), 18 deletions(-)

diff --git a/src/test/modules/test_pg_dump/t/001_base.pl 
b/src/test/modules/test_pg_dump/t/001_base.pl
index 501aff0920..1f7553f9aa 100644
--- a/src/test/modules/test_pg_dump/t/001_base.pl
+++ b/src/test/modules/test_pg_dump/t/001_base.pl
@@ -169,6 +169,19 @@ my %pgdump_runs = (
                        'postgres',
                ],
        },
+
+       # regress_dump_login_role shouldn't need SELECT rights on internal
+       # (undumped) extension tables
+       privileged_internals => {
+               dump_cmd => [
+                       'pg_dump', '--no-sync', 
"--file=$tempdir/privileged_internals.sql",
+                       # these two tables are irrelevant to the test case
+                       '--exclude-table=regress_pg_dump_schema.external_tab',
+                       '--exclude-table=regress_pg_dump_schema.extdependtab',
+                       '--username=regress_dump_login_role', 'postgres',
+               ],
+       },
+
        schema_only => {
                dump_cmd => [
                        'pg_dump', '--no-sync', 
"--file=$tempdir/schema_only.sql",
@@ -228,14 +241,15 @@ my %pgdump_runs = (
 # Tests which are considered 'full' dumps by pg_dump, but there
 # are flags used to exclude specific items (ACLs, blobs, etc).
 my %full_runs = (
-       binary_upgrade  => 1,
-       clean           => 1,
-       clean_if_exists => 1,
-       createdb        => 1,
-       defaults        => 1,
-       exclude_table   => 1,
-       no_privs        => 1,
-       no_owner        => 1,);
+       binary_upgrade       => 1,
+       clean                => 1,
+       clean_if_exists      => 1,
+       createdb             => 1,
+       defaults             => 1,
+       exclude_table        => 1,
+       no_privs             => 1,
+       no_owner             => 1,
+       privileged_internals => 1,);
 
 my %tests = (
        'ALTER EXTENSION test_pg_dump' => {
@@ -271,6 +285,16 @@ my %tests = (
                like         => { pg_dumpall_globals => 1, },
        },
 
+       'CREATE ROLE regress_dump_login_role' => {
+               create_order => 1,
+               create_sql   => 'CREATE ROLE regress_dump_login_role LOGIN;',
+               regexp       => qr/^
+                       \QCREATE ROLE regress_dump_login_role;\E
+                       \n\QALTER ROLE regress_dump_login_role WITH \E.*\Q 
LOGIN \E.*;
+                       \n/xm,
+               like         => { pg_dumpall_globals => 1, },
+       },
+
        'CREATE SEQUENCE regress_pg_dump_table_col1_seq' => {
                regexp => qr/^
                     \QCREATE SEQUENCE public.regress_pg_dump_table_col1_seq\E
@@ -608,11 +632,12 @@ my %tests = (
                /xms,
                like   => {%pgdump_runs},
                unlike => {
-                       data_only          => 1,
-                       extension_schema   => 1,
-                       pg_dumpall_globals => 1,
-                       section_data       => 1,
-                       section_pre_data   => 1,
+                       data_only            => 1,
+                       extension_schema     => 1,
+                       pg_dumpall_globals   => 1,
+                       privileged_internals => 1,
+                       section_data         => 1,
+                       section_pre_data     => 1,
                },
        },
 
@@ -622,11 +647,12 @@ my %tests = (
                        /xms,
                like   => {%pgdump_runs},
                unlike => {
-                       data_only          => 1,
-                       extension_schema   => 1,
-                       pg_dumpall_globals => 1,
-                       section_data       => 1,
-                       section_pre_data   => 1,
+                       data_only            => 1,
+                       extension_schema     => 1,
+                       pg_dumpall_globals   => 1,
+                       privileged_internals => 1,
+                       section_data         => 1,
+                       section_pre_data     => 1,
                },
        },
 
@@ -644,6 +670,7 @@ my %tests = (
                        schema_only      => 1,
                        section_pre_data => 1,
                },
+               unlike => { privileged_internals => 1 },
        },);
 
 #########################################
diff --git a/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql 
b/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql
index 110f7eef66..1c68e146d9 100644
--- a/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql
+++ b/src/test/modules/test_pg_dump/test_pg_dump--1.0.sql
@@ -12,11 +12,13 @@ CREATE SEQUENCE regress_pg_dump_seq;
 
 CREATE SEQUENCE regress_seq_dumpable;
 SELECT pg_catalog.pg_extension_config_dump('regress_seq_dumpable', '');
+GRANT SELECT ON SEQUENCE regress_seq_dumpable TO public;
 
 CREATE TABLE regress_table_dumpable (
        col1 int check (col1 > 0)
 );
 SELECT pg_catalog.pg_extension_config_dump('regress_table_dumpable', '');
+GRANT SELECT ON regress_table_dumpable TO public;
 
 CREATE SCHEMA regress_pg_dump_schema;
 
-- 
2.25.1

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

[backpatch to 11]

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 can determine that no policies exist for a table at the time of
getTables().

Fixes the previous commit's failing test.
---
 src/bin/pg_dump/common.c  | 92 ++++++++++++++++++++++++++++++++++++++-
 src/bin/pg_dump/pg_dump.c | 66 ++++++++++++++++++++++++++++
 src/bin/pg_dump/pg_dump.h |  4 ++
 3 files changed, 161 insertions(+), 1 deletion(-)

diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 8ceba04bfe..ef94eef87c 100644
--- a/src/bin/pg_dump/common.c
+++ b/src/bin/pg_dump/common.c
@@ -64,9 +64,11 @@ static int   numNamespaces;
 static int     numExtensions;
 static int     numPublications;
 
-/* This is an array of object identities, not actual DumpableObjects */
+/* These are arrays of object identities, not actual DumpableObjects */
 static ExtensionMemberId *extmembers;
+static CatalogId *policytables;
 static int     numextmembers;
+static int     numpolicytables;
 
 static void flagInhTables(Archive *fout, TableInfo *tbinfo, int numTables,
                          InhInfo *inhinfo, int numInherits);
@@ -74,6 +76,7 @@ static void flagInhIndexes(Archive *fout, TableInfo *tblinfo, 
int numTables);
 static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables);
 static DumpableObject **buildIndexArray(void *objArray, int numObjs,
                                Size objSize);
+static int     CatalogIdCompare(const void *p1, const void *p2);
 static int     DOCatalogIdCompare(const void *p1, const void *p2);
 static int     ExtensionMemberIdCompare(const void *p1, const void *p2);
 static void findParentsByOid(TableInfo *self,
@@ -132,6 +135,14 @@ getSchemaData(Archive *fout, int *numTablesPtr)
                write_msg(NULL, "identifying extension members\n");
        getExtensionMembership(fout, extinfo, numExtensions);
 
+       /*
+        * Similarly, the existence of RLS policies influences whether some 
tables
+        * need to be locked.
+        */
+       if (g_verbose)
+               write_msg(NULL, "checking for row-level security policies\n");
+       getTablesWithPolicies(fout);
+
        if (g_verbose)
                write_msg(NULL, "reading schemas\n");
        nspinfo = getNamespaces(fout, &numNamespaces);
@@ -777,6 +788,26 @@ buildIndexArray(void *objArray, int numObjs, Size objSize)
        return ptrs;
 }
 
+/*
+ * qsort comparator for pointers to CatalogIds
+ */
+static int
+CatalogIdCompare(const void *p1, const void *p2)
+{
+       const CatalogId *id1 = (const CatalogId *) p1;
+       const CatalogId *id2 = (const CatalogId *) p2;
+       int                     cmpval;
+
+       /*
+        * Compare OID first since it's usually unique, whereas there will only 
be
+        * a few distinct values of tableoid.
+        */
+       cmpval = oidcmp(id1->oid, id2->oid);
+       if (cmpval == 0)
+               cmpval = oidcmp(id1->tableoid, id2->tableoid);
+       return cmpval;
+}
+
 /*
  * qsort comparator for pointers to DumpableObjects
  */
@@ -1043,6 +1074,65 @@ ExtensionMemberIdCompare(const void *p1, const void *p2)
 }
 
 
+/*
+ * setPolicyExistence
+ *       Store a list of tables which have RLS policies
+ */
+void
+setPolicyExistence(CatalogId *tables, int ntables)
+{
+       /*
+        * Same as setExtensionMembership: sort array in preparation for binary
+        * searches
+        */
+       if (ntables > 1)
+               qsort((void *) tables, ntables, sizeof(CatalogId),
+                         CatalogIdCompare);
+       /* And save */
+       policytables = tables;
+       numpolicytables = ntables;
+}
+
+/*
+ * hasPolicies
+ *       return whether the specified catalog ID has RLS policies
+ */
+bool
+hasPolicies(CatalogId catalogId)
+{
+       CatalogId *low;
+       CatalogId *high;
+
+       /*
+        * We could use bsearch() here, but the notational cruft of calling
+        * bsearch is nearly as bad as doing it ourselves; and the generalized
+        * bsearch function is noticeably slower as well.
+        */
+       if (numpolicytables <= 0)
+               return NULL;
+       low = policytables;
+       high = policytables + (numpolicytables - 1);
+       while (low <= high)
+       {
+               CatalogId  *middle;
+               int                     difference;
+
+               middle = low + (high - low) / 2;
+               /* comparison must match CatalogIdCompare, above */
+               difference = oidcmp(middle->oid, catalogId.oid);
+               if (difference == 0)
+                       difference = oidcmp(middle->tableoid, 
catalogId.tableoid);
+               if (difference == 0)
+                       return true;
+               else if (difference < 0)
+                       low = middle + 1;
+               else
+                       high = middle - 1;
+       }
+       return false;
+}
+
+
 /*
  * findParentsByOid
  *       find a table's parents in tblinfo[]
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 5bc1858f07..2f60b0c94e 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -3498,6 +3498,60 @@ dumpBlobs(Archive *fout, void *arg)
        return 1;
 }
 
+/*
+ * getTablesWithPolicies
+ *   retrieve the IDs of all tables with pg_policy entries
+ */
+void
+getTablesWithPolicies(Archive *fout)
+{
+       PQExpBuffer query;
+       PGresult   *res;
+       int                     i_classid;
+       int                     i_polrelid;
+       int                     i,
+                               ntups;
+       CatalogId  *policytables;
+
+       /* No policies before 9.5 */
+       if (fout->remoteVersion < 90500)
+               return;
+
+       query = createPQExpBuffer();
+
+       /* Figure out which tables have RLS policies. */
+       printfPQExpBuffer(query,
+                                         "SELECT DISTINCT 
'pg_class'::regclass::oid AS classid, "
+                                         "                polrelid "
+                                         "FROM pg_catalog.pg_policy");
+
+       res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+
+       ntups = PQntuples(res);
+
+       i_classid = PQfnumber(res, "classid");
+       i_polrelid = PQfnumber(res, "polrelid");
+
+       policytables = pg_malloc(ntups * sizeof(CatalogId));
+
+       for (i = 0; i < ntups; i++)
+       {
+               CatalogId       objId;
+
+               objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
+               objId.oid = atooid(PQgetvalue(res, i, i_polrelid));
+
+               policytables[i] = objId;
+       }
+
+       PQclear(res);
+
+       /* Remember the data for use later */
+       setPolicyExistence(policytables, ntups);
+
+       destroyPQExpBuffer(query);
+}
+
 /*
  * getPolicies
  *       get information about all RLS policies on dumpable tables.
@@ -6665,6 +6719,18 @@ getTables(Archive *fout, int *numTables)
                        strcmp(PQgetvalue(res, i, i_changed_acl), "f") == 0)
                        tblinfo[i].dobj.dump &= ~DUMP_COMPONENT_ACL;
 
+               /*
+                * If the table has no policies, we don't need to worry about 
those
+                * either.
+                *
+                * 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 (!hasPolicies(tblinfo[i].dobj.catId))
+                       tblinfo[i].dobj.dump &= ~DUMP_COMPONENT_POLICY;
+
                tblinfo[i].interesting = tblinfo[i].dobj.dump ? true : false;
                tblinfo[i].dummy_view = false;  /* might get set during sort */
                tblinfo[i].postponed_def = false;       /* might get set during 
sort */
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 8522b519cd..ecf7662819 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -678,6 +678,9 @@ extern PublicationInfo *findPublicationByOid(Oid oid);
 extern void setExtensionMembership(ExtensionMemberId *extmems, int nextmems);
 extern ExtensionInfo *findOwningExtension(CatalogId catalogId);
 
+extern void setPolicyExistence(CatalogId *tables, int ntables);
+extern bool hasPolicies(CatalogId catId);
+
 extern void parseOidArray(const char *str, Oid *array, int arraysize);
 
 extern void sortDumpableObjects(DumpableObject **objs, int numObjs,
@@ -727,6 +730,7 @@ extern void getExtensionMembership(Archive *fout, 
ExtensionInfo extinfo[],
 extern void processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
                                           int numExtensions);
 extern EventTriggerInfo *getEventTriggers(Archive *fout, int 
*numEventTriggers);
+extern void getTablesWithPolicies(Archive *fout);
 extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);
 extern PublicationInfo *getPublications(Archive *fout,
                                                                                
int *numPublications);
-- 
2.25.1

Reply via email to