On 2021-Feb-26, Alvaro Herrera wrote:

> Hmm, but if we take this approach, then we're still vulnerable to the
> problem that somebody can do DETACH CONCURRENTLY and cancel the wait (or
> crash the server), then mess up the state before doing DETACH FINALIZE:
> when they cancel the wait, the lock will be released.
> 
> I think the right fix is to disallow any action on a partition which is
> pending detach other than DETACH FINALIZE.  (Didn't do that here.)

Here's a fixup patch to do it that way.  I tried running the commands
you showed and one of them immediately dies with the new error message;
I can't cause the bogus constraint to show up anymore.

I'll clean this up for a real submission tomorrow.

-- 
Álvaro Herrera                            39°49'30"S 73°17'W
"The Gord often wonders why people threaten never to come back after they've
been told never to return" (www.actsofgord.com)
diff --git a/src/backend/catalog/pg_inherits.c 
b/src/backend/catalog/pg_inherits.c
index 3fd0880ca0..83093f9730 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -535,3 +535,39 @@ DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool 
detached,
 
        return found;
 }
+
+bool
+PartitionHasPendingDetach(Oid partoid)
+{
+       Relation        catalogRelation;
+       ScanKeyData key;
+       SysScanDesc scan;
+       HeapTuple       inheritsTuple;
+
+       /*
+        * Find pg_inherits entries by inhrelid.
+        */
+       catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
+       ScanKeyInit(&key,
+                               Anum_pg_inherits_inhrelid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(partoid));
+       scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
+                                                         true, NULL, 1, &key);
+
+       while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
+       {
+               bool    detached;
+
+               detached = ((Form_pg_inherits) 
GETSTRUCT(inheritsTuple))->inhdetached;
+
+               /* Done */
+               systable_endscan(scan);
+               table_close(catalogRelation, RowExclusiveLock);
+
+               return detached;
+       }
+
+       elog(ERROR, "relation %u is not a partition", partoid);
+       return false;                           /* keep compiler quiet */
+}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 5986b4dd56..5074c0ff2b 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -17106,6 +17106,8 @@ ATExecDetachPartition(List **wqueue, AlteredTableInfo 
*tab, Relation rel,
         */
        partRel = table_openrv(name, concurrent ? ShareLock :
                                                   AccessExclusiveLock);
+       if (PartitionHasPendingDetach(RelationGetRelid(partRel)))
+               elog(ERROR, "hey, don't!");
 
        /*
         * Check inheritance conditions and either delete the pg_inherits row
diff --git a/src/include/catalog/pg_inherits.h 
b/src/include/catalog/pg_inherits.h
index 155596907c..19243ea9e4 100644
--- a/src/include/catalog/pg_inherits.h
+++ b/src/include/catalog/pg_inherits.h
@@ -61,5 +61,6 @@ extern void StoreSingleInheritance(Oid relationId, Oid 
parentOid,
                                                                   int32 
seqNumber);
 extern bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool 
allow_detached,
                                                                const char 
*childname);
+extern bool PartitionHasPendingDetach(Oid partoid);
 
 #endif                                                 /* PG_INHERITS_H */

Reply via email to