Fix orphan on cancel of drop index concurrently.
authorSimon Riggs <simon@2ndQuadrant.com>
Fri, 19 Oct 2012 08:56:29 +0000 (09:56 +0100)
committerSimon Riggs <simon@2ndQuadrant.com>
Fri, 19 Oct 2012 08:56:29 +0000 (09:56 +0100)
Canceling DROP INDEX CONCURRENTLY during
wait could allow an orphaned index to be
left behind which could not be dropped.

Backpatch to 9.2

Andres Freund, tested by Abhijit Menon-Sen

src/backend/catalog/dependency.c

index 87d6f0242149a0cebc9b82545566eca9ddeb909c..b9cfee2e81246e638b43c844fba0b985b054db25 100644 (file)
@@ -355,12 +355,7 @@ performMultipleDeletions(const ObjectAddresses *objects,
    /* And clean up */
    free_object_addresses(targetObjects);
 
-   /*
-    * We closed depRel earlier in deleteOneObject if doing a drop
-    * concurrently
-    */
-   if ((flags & PERFORM_DELETION_CONCURRENTLY) != PERFORM_DELETION_CONCURRENTLY)
-       heap_close(depRel, RowExclusiveLock);
+   heap_close(depRel, RowExclusiveLock);
 }
 
 /*
@@ -1000,6 +995,7 @@ deleteOneObject(const ObjectAddress *object, Relation depRel, int flags)
    int         nkeys;
    SysScanDesc scan;
    HeapTuple   tup;
+   Oid         depRelOid = depRel->rd_id;
 
    /* DROP hook of the objects being removed */
    if (object_access_hook)
@@ -1012,7 +1008,33 @@ deleteOneObject(const ObjectAddress *object, Relation depRel, int flags)
    }
 
    /*
-    * First remove any pg_depend records that link from this object to
+    * Close depRel if we are doing a drop concurrently. The individual
+    * deletion has to commit the transaction and we don't want dangling
+    * references.
+    */
+   if (flags & PERFORM_DELETION_CONCURRENTLY)
+       heap_close(depRel, RowExclusiveLock);
+
+   /*
+    * Delete the object itself, in an object-type-dependent way.
+    *
+    * Do this before removing outgoing dependencies as deletions can be
+    * happening in concurrent mode. That will only happen for a single object
+    * at once and if so the object will be invalidated inside a separate
+    * transaction and only dropped inside a transaction thats in-progress when
+    * doDeletion returns. This way no observer can see dangling dependency
+    * entries.
+    */
+   doDeletion(object, flags);
+
+   /*
+    * Reopen depRel if we closed it before
+    */
+   if (flags & PERFORM_DELETION_CONCURRENTLY)
+       depRel = heap_open(depRelOid, RowExclusiveLock);
+
+   /*
+    * Then remove any pg_depend records that link from this object to
     * others.  (Any records linking to this object should be gone already.)
     *
     * When dropping a whole object (subId = 0), remove all pg_depend records
@@ -1054,17 +1076,6 @@ deleteOneObject(const ObjectAddress *object, Relation depRel, int flags)
    deleteSharedDependencyRecordsFor(object->classId, object->objectId,
                                     object->objectSubId);
 
-   /*
-    * Close depRel if we are doing a drop concurrently because it commits the
-    * transaction, so we don't want dangling references.
-    */
-   if ((flags & PERFORM_DELETION_CONCURRENTLY) == PERFORM_DELETION_CONCURRENTLY)
-       heap_close(depRel, RowExclusiveLock);
-
-   /*
-    * Now delete the object itself, in an object-type-dependent way.
-    */
-   doDeletion(object, flags);
 
    /*
     * Delete any comments or security labels associated with this object.
@@ -1247,7 +1258,7 @@ AcquireDeletionLock(const ObjectAddress *object, int flags)
 {
    if (object->classId == RelationRelationId)
    {
-       if ((flags & PERFORM_DELETION_CONCURRENTLY) == PERFORM_DELETION_CONCURRENTLY)
+       if (flags & PERFORM_DELETION_CONCURRENTLY)
            LockRelationOid(object->objectId, ShareUpdateExclusiveLock);
        else
            LockRelationOid(object->objectId, AccessExclusiveLock);