From d4f7938dfbec8e00f3aacf741f342ece50ae9c12 Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Wed, 12 Jul 2023 18:57:28 +0200
Subject: [PATCH v17 1/3] Remember PK oid for partitioned tables even when it's
 invalid

---
 src/backend/utils/cache/relcache.c | 34 ++++++++++++++++++++++++------
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 8e08ca1c68..7234cb3da6 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4789,19 +4789,41 @@ RelationGetIndexList(Relation relation)
 		result = lappend_oid(result, index->indexrelid);
 
 		/*
-		 * Invalid, non-unique, non-immediate or predicate indexes aren't
-		 * interesting for either oid indexes or replication identity indexes,
-		 * so don't check them.
+		 * Non-unique, non-immediate or predicate indexes aren't interesting
+		 * for either oid indexes or replication identity indexes, so don't
+		 * check them.
 		 */
-		if (!index->indisvalid || !index->indisunique ||
+		if (!index->indisunique ||
 			!index->indimmediate ||
 			!heap_attisnull(htup, Anum_pg_index_indpred, NULL))
 			continue;
 
-		/* remember primary key index if any */
-		if (index->indisprimary)
+		/*
+		 * Remember primary key index, if any.  We do this only if the index
+		 * is valid; but if the table is partitioned, then we do it even if
+		 * it's invalid.
+		 *
+		 * The reason for returning invalid primary keys for foreign tables is
+		 * because of pg_dump of NOT NULL constraints, and the fact that PKs
+		 * remain marked invalid until the partitions' PKs are attached to it.
+		 * If we make rd_pkindex invalid, then the attnotnull flag is reset
+		 * after the PK is created, which causes the ALTER INDEX ATTACH
+		 * PARTITION to fail with 'column ... is not marked NOT NULL'.  With
+		 * this, dropconstraint_internal() will believe that the columns must
+		 * not have attnotnull reset, so the PKs-on-partitions can be attached
+		 * correctly, until finally the PK-on-parent is marked valid.
+		 *
+		 * Also, this doesn't harm anything, because rd_pkindex is not a
+		 * "real" index anyway, but a RELKIND_PARTITIONED_INDEX.
+		 */
+		if (index->indisprimary &&
+			(index->indisvalid ||
+			 relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE))
 			pkeyIndex = index->indexrelid;
 
+		if (!index->indisvalid)
+			continue;
+
 		/* remember explicitly chosen replica index */
 		if (index->indisreplident)
 			candidateIndex = index->indexrelid;
-- 
2.39.2

