From 40d43781c41359d2fc834827db55d8edd39710cf Mon Sep 17 00:00:00 2001
From: Tender Wang <tndrwang@gmail.com>
Date: Wed, 12 Jun 2024 13:29:10 +0800
Subject: [PATCH v1] Fix assert failure when detach partition.

---
 src/backend/catalog/pg_inherits.c            | 26 ++++++++++++++++++++
 src/backend/commands/tablecmds.c             | 21 ++++++++++++++++
 src/include/catalog/pg_inherits.h            |  2 ++
 src/test/regress/expected/partition_info.out | 13 ++++++++++
 src/test/regress/sql/partition_info.sql      | 15 +++++++++++
 5 files changed, 77 insertions(+)

diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index 836b4bfd89..c386863699 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -393,6 +393,32 @@ has_superclass(Oid relationId)
 	return result;
 }
 
+/*
+ * get_superclass - returns this relation inherit tuple if it has
+ */
+HeapTuple
+get_superclass(Oid relationId)
+{
+	Relation catalog;
+	SysScanDesc scan;
+	ScanKeyData skey;
+	HeapTuple inhTup,
+			  retval = NULL;
+
+	catalog = table_open(InheritsRelationId, AccessShareLock);
+	ScanKeyInit(&skey, Anum_pg_inherits_inhrelid, BTEqualStrategyNumber,
+				F_OIDEQ, ObjectIdGetDatum(relationId));
+	scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
+							  NULL, 1, &skey);
+	inhTup = systable_getnext(scan);
+	if (HeapTupleIsValid(inhTup))
+		retval = heap_copytuple(inhTup);
+
+	systable_endscan(scan);
+	table_close(catalog, AccessShareLock);
+
+	return retval;
+}
 /*
  * Given two type OIDs, determine whether the first is a complex type
  * (class type) that inherits from the second.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 6adfd87614..a300676e14 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -19289,6 +19289,10 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
 		Oid			idxid = lfirst_oid(cell);
 		Relation	idx;
 		Oid			constrOid;
+		HeapTuple	parentTuple;
+		Oid			parentIdxid;
+		Oid			parentConstrOid;
+		Form_pg_inherits	inhForm;
 
 		if (!has_superclass(idxid))
 			continue;
@@ -19296,15 +19300,32 @@ DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
 		Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
 				RelationGetRelid(rel)));
 
+		/* Get inherit tuple */
+		parentTuple = get_superclass(idxid);
+		Assert(HeapTupleIsValid(parentTuple));
+		inhForm = (Form_pg_inherits) GETSTRUCT(parentTuple);
+		parentIdxid = inhForm->inhparent;
+
+		parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
+														  parentIdxid);
 		idx = index_open(idxid, AccessExclusiveLock);
 		IndexSetParentIndex(idx, InvalidOid);
 
+		/* Skip if there's no a constraint associated with parent index */
+		if (!OidIsValid(parentConstrOid))
+		{
+			heap_freetuple(parentTuple);
+			index_close(idx, NoLock);
+			continue;
+		}
+
 		/* If there's a constraint associated with the index, detach it too */
 		constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
 													idxid);
 		if (OidIsValid(constrOid))
 			ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
 
+		heap_freetuple(parentTuple);
 		index_close(idx, NoLock);
 	}
 
diff --git a/src/include/catalog/pg_inherits.h b/src/include/catalog/pg_inherits.h
index b3da78c24b..d8855a1733 100644
--- a/src/include/catalog/pg_inherits.h
+++ b/src/include/catalog/pg_inherits.h
@@ -18,6 +18,7 @@
 #ifndef PG_INHERITS_H
 #define PG_INHERITS_H
 
+#include "access/htup.h"
 #include "catalog/genbki.h"
 #include "catalog/pg_inherits_d.h"
 
@@ -56,6 +57,7 @@ extern List *find_all_inheritors(Oid parentrelId, LOCKMODE lockmode,
 								 List **numparents);
 extern bool has_subclass(Oid relationId);
 extern bool has_superclass(Oid relationId);
+extern HeapTuple get_superclass(Oid relationId);
 extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
 extern void StoreSingleInheritance(Oid relationId, Oid parentOid,
 								   int32 seqNumber);
diff --git a/src/test/regress/expected/partition_info.out b/src/test/regress/expected/partition_info.out
index 42b6bc77ca..3f689989b9 100644
--- a/src/test/regress/expected/partition_info.out
+++ b/src/test/regress/expected/partition_info.out
@@ -275,6 +275,19 @@ SELECT pg_partition_root('ptif_normal_table');
 (1 row)
 
 DROP TABLE ptif_normal_table;
+-- If parent index doesn't have a constraint tuple
+-- detach partition should not fail.
+CREATE TABLE pt_ind_t (a int) PARTITION BY RANGE (a);
+CREATE TABLE pt_ind_tp (a int PRIMARY KEY);
+ALTER TABLE pt_ind_t ATTACH PARTITION pt_ind_tp FOR VALUES FROM (0) TO (1000);
+CREATE UNIQUE INDEX pt_a_idx ON pt_ind_t (a);
+ALTER INDEX pt_a_idx ATTACH PARTITION pt_ind_tp_pkey;
+ALTER TABLE pt_ind_t DETACH PARTITION pt_ind_tp;
+CREATE TABLE pt_indx_t (a int) PARTITION BY RANGE (a);
+CREATE TABLE pt_indx_tp (a int PRIMARY KEY);
+CREATE UNIQUE INDEX ptx_a_idx ON pt_indx_t (a);
+ALTER TABLE pt_indx_t ATTACH PARTITION pt_indx_tp FOR VALUES FROM (0) TO (1000);
+ALTER TABLE pt_indx_t DETACH PARTITION pt_indx_tp;
 -- Various partitioning-related functions return empty/NULL if passed relations
 -- of types that cannot be part of a partition tree; for example, views,
 -- materialized views, legacy inheritance children or parents, etc.
diff --git a/src/test/regress/sql/partition_info.sql b/src/test/regress/sql/partition_info.sql
index b5060bec7f..a36f8db562 100644
--- a/src/test/regress/sql/partition_info.sql
+++ b/src/test/regress/sql/partition_info.sql
@@ -105,6 +105,21 @@ SELECT * FROM pg_partition_ancestors('ptif_normal_table');
 SELECT pg_partition_root('ptif_normal_table');
 DROP TABLE ptif_normal_table;
 
+-- If parent index doesn't have a constraint tuple
+-- detach partition should not fail.
+CREATE TABLE pt_ind_t (a int) PARTITION BY RANGE (a);
+CREATE TABLE pt_ind_tp (a int PRIMARY KEY);
+ALTER TABLE pt_ind_t ATTACH PARTITION pt_ind_tp FOR VALUES FROM (0) TO (1000);
+CREATE UNIQUE INDEX pt_a_idx ON pt_ind_t (a);
+ALTER INDEX pt_a_idx ATTACH PARTITION pt_ind_tp_pkey;
+ALTER TABLE pt_ind_t DETACH PARTITION pt_ind_tp;
+
+CREATE TABLE pt_indx_t (a int) PARTITION BY RANGE (a);
+CREATE TABLE pt_indx_tp (a int PRIMARY KEY);
+CREATE UNIQUE INDEX ptx_a_idx ON pt_indx_t (a);
+ALTER TABLE pt_indx_t ATTACH PARTITION pt_indx_tp FOR VALUES FROM (0) TO (1000);
+ALTER TABLE pt_indx_t DETACH PARTITION pt_indx_tp;
+
 -- Various partitioning-related functions return empty/NULL if passed relations
 -- of types that cannot be part of a partition tree; for example, views,
 -- materialized views, legacy inheritance children or parents, etc.
-- 
2.34.1

