]> granicus.if.org Git - postgresql/commitdiff
Add DROP INDEX CONCURRENTLY [IF EXISTS], uses ShareUpdateExclusiveLock
authorSimon Riggs <simon@2ndQuadrant.com>
Fri, 6 Apr 2012 09:21:40 +0000 (10:21 +0100)
committerSimon Riggs <simon@2ndQuadrant.com>
Fri, 6 Apr 2012 09:21:40 +0000 (10:21 +0100)
14 files changed:
doc/src/sgml/ref/drop_index.sgml
src/backend/catalog/dependency.c
src/backend/catalog/index.c
src/backend/commands/tablecmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/tcop/utility.c
src/backend/utils/cache/relcache.c
src/include/catalog/dependency.h
src/include/catalog/index.h
src/include/nodes/parsenodes.h
src/test/regress/expected/create_index.out
src/test/regress/sql/create_index.sql

index 7177ef2d81b00eddca32b5df8e421d1630d42829..343f7aca003dd1687e7370da663cc26554955dbd 100644 (file)
@@ -21,7 +21,7 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-DROP INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
+DROP INDEX [ CONCURRENTLY ] [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -49,6 +49,29 @@ DROP INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [, ..
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>CONCURRENTLY</literal></term>
+    <listitem>
+     <para>
+      When this option is used, <productname>PostgreSQL</> will drop the
+      index without taking any locks that prevent concurrent selects, inserts,
+      updates, or deletes on the table; whereas a standard index drop
+      waits for a lock that locks out everything on the table until it's done.
+      Concurrent drop index is a two stage process. First, we mark the index
+      both invalid and not ready then commit the change. Next we wait until
+      there are no users locking the table who can see the index.
+     </para>
+     <para>
+      There are several caveats to be aware of when using this option.
+      Only one index name can be specified if the <literal>CONCURRENTLY</literal>
+      parameter is specified.  Regular <command>DROP INDEX</> command can be
+      performed within a transaction block, but
+      <command>DROP INDEX CONCURRENTLY</> cannot.
+      The CASCADE option is not supported when dropping an index concurrently.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="PARAMETER">name</replaceable></term>
     <listitem>
index fed724c51cb8cc178c675277d75a1dd742ba386a..db6769cb90b699313ef12dbb31d6176d6f3e7d7d 100644 (file)
@@ -174,8 +174,8 @@ static void reportDependentObjects(const ObjectAddresses *targetObjects,
                                           const ObjectAddress *origObject);
 static void deleteOneObject(const ObjectAddress *object,
                                                        Relation depRel, int32 flags);
-static void doDeletion(const ObjectAddress *object);
-static void AcquireDeletionLock(const ObjectAddress *object);
+static void doDeletion(const ObjectAddress *object, int flags);
+static void AcquireDeletionLock(const ObjectAddress *object, int flags);
 static void ReleaseDeletionLock(const ObjectAddress *object);
 static bool find_expr_references_walker(Node *node,
                                                        find_expr_references_context *context);
@@ -233,7 +233,7 @@ performDeletion(const ObjectAddress *object,
         * Acquire deletion lock on the target object.  (Ideally the caller has
         * done this already, but many places are sloppy about it.)
         */
-       AcquireDeletionLock(object);
+       AcquireDeletionLock(object, 0);
 
        /*
         * Construct a list of objects to delete (ie, the given object plus
@@ -317,7 +317,7 @@ performMultipleDeletions(const ObjectAddresses *objects,
                 * Acquire deletion lock on each target object.  (Ideally the caller
                 * has done this already, but many places are sloppy about it.)
                 */
-               AcquireDeletionLock(thisobj);
+               AcquireDeletionLock(thisobj, flags);
 
                findDependentObjects(thisobj,
                                                         DEPFLAG_ORIGINAL,
@@ -351,7 +351,11 @@ performMultipleDeletions(const ObjectAddresses *objects,
        /* And clean up */
        free_object_addresses(targetObjects);
 
-       heap_close(depRel, RowExclusiveLock);
+       /*
+        * We closed depRel earlier in deleteOneObject if doing a drop concurrently
+        */
+       if ((flags & PERFORM_DELETION_CONCURRENTLY) != PERFORM_DELETION_CONCURRENTLY)
+               heap_close(depRel, RowExclusiveLock);
 }
 
 /*
@@ -381,7 +385,7 @@ deleteWhatDependsOn(const ObjectAddress *object,
         * Acquire deletion lock on the target object.  (Ideally the caller has
         * done this already, but many places are sloppy about it.)
         */
-       AcquireDeletionLock(object);
+       AcquireDeletionLock(object, 0);
 
        /*
         * Construct a list of objects to delete (ie, the given object plus
@@ -631,7 +635,7 @@ findDependentObjects(const ObjectAddress *object,
                                 * deletion of the owning object.)
                                 */
                                ReleaseDeletionLock(object);
-                               AcquireDeletionLock(&otherObject);
+                               AcquireDeletionLock(&otherObject, 0);
 
                                /*
                                 * The owning object might have been deleted while we waited
@@ -726,7 +730,7 @@ findDependentObjects(const ObjectAddress *object,
                /*
                 * Must lock the dependent object before recursing to it.
                 */
-               AcquireDeletionLock(&otherObject);
+               AcquireDeletionLock(&otherObject, 0);
 
                /*
                 * The dependent object might have been deleted while we waited to
@@ -1044,10 +1048,17 @@ 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);
+       doDeletion(object, flags);
 
        /*
         * Delete any comments or security labels associated with this object.
@@ -1072,7 +1083,7 @@ deleteOneObject(const ObjectAddress *object, Relation depRel, int flags)
  * doDeletion: actually delete a single object
  */
 static void
-doDeletion(const ObjectAddress *object)
+doDeletion(const ObjectAddress *object, int flags)
 {
        switch (getObjectClass(object))
        {
@@ -1082,8 +1093,11 @@ doDeletion(const ObjectAddress *object)
 
                                if (relKind == RELKIND_INDEX)
                                {
+                                       bool concurrent = ((flags & PERFORM_DELETION_CONCURRENTLY)
+                                                                                                       == PERFORM_DELETION_CONCURRENTLY);
+
                                        Assert(object->objectSubId == 0);
-                                       index_drop(object->objectId);
+                                       index_drop(object->objectId, concurrent);
                                }
                                else
                                {
@@ -1219,10 +1233,15 @@ doDeletion(const ObjectAddress *object)
  * shared-across-databases object, so we have no need for LockSharedObject.
  */
 static void
-AcquireDeletionLock(const ObjectAddress *object)
+AcquireDeletionLock(const ObjectAddress *object, int flags)
 {
        if (object->classId == RelationRelationId)
-               LockRelationOid(object->objectId, AccessExclusiveLock);
+       {
+               if ((flags & PERFORM_DELETION_CONCURRENTLY) == PERFORM_DELETION_CONCURRENTLY)
+                       LockRelationOid(object->objectId, ShareUpdateExclusiveLock);
+               else
+                       LockRelationOid(object->objectId, AccessExclusiveLock);
+       }
        else
                /* assume we should lock the whole object not a sub-object */
                LockDatabaseObject(object->classId, object->objectId, 0,
index bfbe642535132b512c6c7f49ce5672e0c2f48a11..5fae488c745ab13e5e9fee1b4297f0bec5708fe6 100644 (file)
@@ -1282,7 +1282,7 @@ index_constraint_create(Relation heapRelation,
  * else associated dependencies won't be cleaned up.
  */
 void
-index_drop(Oid indexId)
+index_drop(Oid indexId, bool concurrent)
 {
        Oid                     heapId;
        Relation        userHeapRelation;
@@ -1290,6 +1290,12 @@ index_drop(Oid indexId)
        Relation        indexRelation;
        HeapTuple       tuple;
        bool            hasexprs;
+       LockRelId       heaprelid,
+                               indexrelid;
+       LOCKTAG         heaplocktag,
+                               indexlocktag;
+       VirtualTransactionId *old_lockholders;
+       Form_pg_index indexForm;
 
        /*
         * To drop an index safely, we must grab exclusive lock on its parent
@@ -1302,16 +1308,128 @@ index_drop(Oid indexId)
         * that will make them update their index lists.
         */
        heapId = IndexGetRelation(indexId, false);
-       userHeapRelation = heap_open(heapId, AccessExclusiveLock);
-
-       userIndexRelation = index_open(indexId, AccessExclusiveLock);
+       if (concurrent)
+       {
+               userHeapRelation = heap_open(heapId, ShareUpdateExclusiveLock);
+               userIndexRelation = index_open(indexId, ShareUpdateExclusiveLock);
+       }
+       else
+       {
+               userHeapRelation = heap_open(heapId, AccessExclusiveLock);
+               userIndexRelation = index_open(indexId, AccessExclusiveLock);
+       }
 
        /*
-        * There can no longer be anyone *else* touching the index, but we might
-        * still have open queries using it in our own session.
+        * We might still have open queries using it in our own session.
         */
        CheckTableNotInUse(userIndexRelation, "DROP INDEX");
 
+       /*
+        * Drop Index concurrently is similar in many ways to creating an
+        * index concurrently, so some actions are similar to DefineIndex()
+        */
+       if (concurrent)
+       {
+               /*
+                * Mark index invalid by updating its pg_index entry
+                *
+                * Don't Assert(indexForm->indisvalid) because we may be trying to
+                * clear up after an error when trying to create an index which left
+                * the index invalid
+                */
+               indexRelation = heap_open(IndexRelationId, RowExclusiveLock);
+
+               tuple = SearchSysCacheCopy1(INDEXRELID,
+                                                                                ObjectIdGetDatum(indexId));
+               if (!HeapTupleIsValid(tuple))
+                       elog(ERROR, "cache lookup failed for index %u", indexId);
+               indexForm = (Form_pg_index) GETSTRUCT(tuple);
+
+               indexForm->indisvalid = false;  /* make unusable for queries */
+               indexForm->indisready = false;  /* make invisible to changes */
+
+               simple_heap_update(indexRelation, &tuple->t_self, tuple);
+               CatalogUpdateIndexes(indexRelation, tuple);
+
+               heap_close(indexRelation, RowExclusiveLock);
+
+               /*
+                * Invalidate the relcache for the table, so that after this
+                * transaction we will refresh the index list. Forgetting just the
+                * index is not enough.
+                */
+               CacheInvalidateRelcache(userHeapRelation);
+
+               /* save lockrelid and locktag for below, then close but keep locks */
+               heaprelid = userHeapRelation->rd_lockInfo.lockRelId;
+               SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId);
+               heap_close(userHeapRelation, NoLock);
+
+               indexrelid = userIndexRelation->rd_lockInfo.lockRelId;
+               SET_LOCKTAG_RELATION(indexlocktag, indexrelid.dbId, indexrelid.relId);
+               index_close(userIndexRelation, NoLock);
+
+               /*
+                * For a concurrent drop, it's important to make the catalog entries
+                * visible to other transactions before we drop the index. The index
+                * will be marked not indisvalid, so that no one else tries to either
+                * insert into it or use it for queries.
+                *
+                * We must commit our current transaction so that the index update becomes
+                * visible; then start another.  Note that all the data structures we just
+                * built are lost in the commit.  The only data we keep past here are the
+                * relation IDs.
+                *
+                * Before committing, get a session-level lock on the table, to ensure
+                * that neither it nor the index can be dropped before we finish. This
+                * cannot block, even if someone else is waiting for access, because we
+                * already have the same lock within our transaction.
+                */
+               LockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
+               LockRelationIdForSession(&indexrelid, ShareUpdateExclusiveLock);
+
+               PopActiveSnapshot();
+               CommitTransactionCommand();
+               StartTransactionCommand();
+
+               /*
+                * Now we must wait until no running transaction could have the table open
+                * with the old list of indexes.  To do this, inquire which xacts
+                * currently would conflict with AccessExclusiveLock on the table -- ie,
+                * which ones have a lock of any kind on the table.     Then wait for each of
+                * these xacts to commit or abort.      Note we do not need to worry about
+                * xacts that open the table for writing after this point; they will see
+                * the index as invalid when they open the relation.
+                *
+                * Note: the reason we use actual lock acquisition here, rather than just
+                * checking the ProcArray and sleeping, is that deadlock is possible if
+                * one of the transactions in question is blocked trying to acquire an
+                * exclusive lock on our table.  The lock code will detect deadlock and
+                * error out properly.
+                *
+                * Note: GetLockConflicts() never reports our own xid, hence we need not
+                * check for that.      Also, prepared xacts are not reported, which is fine
+                * since they certainly aren't going to do anything more.
+                */
+               old_lockholders = GetLockConflicts(&heaplocktag, AccessExclusiveLock);
+
+               while (VirtualTransactionIdIsValid(*old_lockholders))
+               {
+                       VirtualXactLock(*old_lockholders, true);
+                       old_lockholders++;
+               }
+
+               /*
+                * Re-open relations to allow us to complete our actions.
+                *
+                * At this point, nothing should be accessing the index, but lets
+                * leave nothing to chance and grab AccessExclusiveLock on the index
+                * before the physical deletion.
+                */
+               userHeapRelation = heap_open(heapId, ShareUpdateExclusiveLock);
+               userIndexRelation = index_open(indexId, AccessExclusiveLock);
+       }
+
        /*
         * All predicate locks on the index are about to be made invalid. Promote
         * them to relation locks on the heap.
@@ -1378,6 +1496,15 @@ index_drop(Oid indexId)
         * Close owning rel, but keep lock
         */
        heap_close(userHeapRelation, NoLock);
+
+       /*
+        * Release the session locks before we go.
+        */
+       if (concurrent)
+       {
+               UnlockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
+               UnlockRelationIdForSession(&indexrelid, ShareUpdateExclusiveLock);
+       }
 }
 
 /* ----------------------------------------------------------------
index 9853686fe97d9f219e75e1d8b5bda9f85928a313..a35e338cc877337c161ff2424e73a636c0b69578 100644 (file)
@@ -239,6 +239,7 @@ struct DropRelationCallbackState
 {
        char    relkind;
        Oid             heapOid;
+       bool    concurrent;
 };
 
 /* Alter table target-type flags for ATSimplePermissions */
@@ -738,6 +739,21 @@ RemoveRelations(DropStmt *drop)
        ObjectAddresses *objects;
        char            relkind;
        ListCell   *cell;
+       int                     flags = 0;
+       LOCKMODE        lockmode = AccessExclusiveLock;
+
+       if (drop->concurrent)
+       {
+               lockmode = ShareUpdateExclusiveLock;
+               if (list_length(drop->objects) > 1)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
+               if (drop->behavior == DROP_CASCADE)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                        errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
+       }
 
        /*
         * First we identify all the relations, then we delete them in a single
@@ -800,7 +816,8 @@ RemoveRelations(DropStmt *drop)
                /* Look up the appropriate relation using namespace search. */
                state.relkind = relkind;
                state.heapOid = InvalidOid;
-               relOid = RangeVarGetRelidExtended(rel, AccessExclusiveLock, true,
+               state.concurrent = drop->concurrent;
+               relOid = RangeVarGetRelidExtended(rel, lockmode, true,
                                                                                  false,
                                                                                  RangeVarCallbackForDropRelation,
                                                                                  (void *) &state);
@@ -820,7 +837,20 @@ RemoveRelations(DropStmt *drop)
                add_exact_object_address(&obj, objects);
        }
 
-       performMultipleDeletions(objects, drop->behavior, 0);
+       /*
+        * Set options and check further requirements for concurrent drop
+        */
+       if (drop->concurrent)
+       {
+               /*
+                * Confirm that concurrent behaviour is restricted in grammar.
+                */
+               Assert(drop->removeType == OBJECT_INDEX);
+
+               flags |= PERFORM_DELETION_CONCURRENTLY;
+       }
+
+       performMultipleDeletions(objects, drop->behavior, flags);
 
        free_object_addresses(objects);
 }
@@ -837,9 +867,12 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
        struct DropRelationCallbackState *state;
        char            relkind;
        Form_pg_class classform;
+       LOCKMODE        heap_lockmode;
 
        state = (struct DropRelationCallbackState *) arg;
        relkind = state->relkind;
+       heap_lockmode = state->concurrent ?
+               ShareUpdateExclusiveLock : AccessExclusiveLock;
 
        /*
         * If we previously locked some other index's heap, and the name we're
@@ -848,7 +881,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
         */
        if (relOid != oldRelOid && OidIsValid(state->heapOid))
        {
-               UnlockRelationOid(state->heapOid, AccessExclusiveLock);
+               UnlockRelationOid(state->heapOid, heap_lockmode);
                state->heapOid = InvalidOid;
        }
 
@@ -889,7 +922,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
        {
                state->heapOid = IndexGetRelation(relOid, true);
                if (OidIsValid(state->heapOid))
-                       LockRelationOid(state->heapOid, AccessExclusiveLock);
+                       LockRelationOid(state->heapOid, heap_lockmode);
        }
 }
 
index 33ee62f40d05bc08a99934440d7a07970ecf009b..f864af5d667769f51b77f06ca040ba63e4f09ece 100644 (file)
@@ -2751,6 +2751,7 @@ _copyDropStmt(const DropStmt *from)
        COPY_SCALAR_FIELD(removeType);
        COPY_SCALAR_FIELD(behavior);
        COPY_SCALAR_FIELD(missing_ok);
+       COPY_SCALAR_FIELD(concurrent);
 
        return newnode;
 }
index b749e9bbe30b16a16fd954c35f4d25b2153decfc..956421020c7cc148d63c600856f0ddf696ec4e33 100644 (file)
@@ -1189,6 +1189,7 @@ _equalDropStmt(const DropStmt *a, const DropStmt *b)
        COMPARE_SCALAR_FIELD(removeType);
        COMPARE_SCALAR_FIELD(behavior);
        COMPARE_SCALAR_FIELD(missing_ok);
+       COMPARE_SCALAR_FIELD(concurrent);
 
        return true;
 }
index f2151ef49863e9febbd18a3c3a865d8836c60101..ae1658a4ffe04f85b5e47e82d1475258a3b3d4dc 100644 (file)
@@ -3276,6 +3276,7 @@ DropPLangStmt:
                                        n->arguments = NIL;
                                        n->behavior = $5;
                                        n->missing_ok = false;
+                                       n->concurrent = false;
                                        $$ = (Node *)n;
                                }
                        | DROP opt_procedural LANGUAGE IF_P EXISTS ColId_or_Sconst opt_drop_behavior
@@ -3285,6 +3286,7 @@ DropPLangStmt:
                                        n->objects = list_make1(list_make1(makeString($6)));
                                        n->behavior = $7;
                                        n->missing_ok = true;
+                                       n->concurrent = false;
                                        $$ = (Node *)n;
                                }
                ;
@@ -3680,6 +3682,7 @@ DropFdwStmt: DROP FOREIGN DATA_P WRAPPER name opt_drop_behavior
                                        n->arguments = NIL;
                                        n->missing_ok = false;
                                        n->behavior = $6;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                                |  DROP FOREIGN DATA_P WRAPPER IF_P EXISTS name opt_drop_behavior
@@ -3690,6 +3693,7 @@ DropFdwStmt: DROP FOREIGN DATA_P WRAPPER name opt_drop_behavior
                                        n->arguments = NIL;
                                        n->missing_ok = true;
                                        n->behavior = $8;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                ;
@@ -3840,6 +3844,7 @@ DropForeignServerStmt: DROP SERVER name opt_drop_behavior
                                        n->arguments = NIL;
                                        n->missing_ok = false;
                                        n->behavior = $4;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                                |  DROP SERVER IF_P EXISTS name opt_drop_behavior
@@ -3850,6 +3855,7 @@ DropForeignServerStmt: DROP SERVER name opt_drop_behavior
                                        n->arguments = NIL;
                                        n->missing_ok = true;
                                        n->behavior = $6;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                ;
@@ -4237,6 +4243,7 @@ DropTrigStmt:
                                        n->arguments = NIL;
                                        n->behavior = $6;
                                        n->missing_ok = false;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                        | DROP TRIGGER IF_P EXISTS name ON any_name opt_drop_behavior
@@ -4247,6 +4254,7 @@ DropTrigStmt:
                                        n->arguments = NIL;
                                        n->behavior = $8;
                                        n->missing_ok = true;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                ;
@@ -4707,6 +4715,7 @@ DropOpClassStmt:
                                        n->removeType = OBJECT_OPCLASS;
                                        n->behavior = $7;
                                        n->missing_ok = false;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                        | DROP OPERATOR CLASS IF_P EXISTS any_name USING access_method opt_drop_behavior
@@ -4717,6 +4726,7 @@ DropOpClassStmt:
                                        n->removeType = OBJECT_OPCLASS;
                                        n->behavior = $9;
                                        n->missing_ok = true;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                ;
@@ -4730,6 +4740,7 @@ DropOpFamilyStmt:
                                        n->removeType = OBJECT_OPFAMILY;
                                        n->behavior = $7;
                                        n->missing_ok = false;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                        | DROP OPERATOR FAMILY IF_P EXISTS any_name USING access_method opt_drop_behavior
@@ -4740,6 +4751,7 @@ DropOpFamilyStmt:
                                        n->removeType = OBJECT_OPFAMILY;
                                        n->behavior = $9;
                                        n->missing_ok = true;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                ;
@@ -4790,6 +4802,7 @@ DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
                                        n->objects = $5;
                                        n->arguments = NIL;
                                        n->behavior = $6;
+                                       n->concurrent = false;
                                        $$ = (Node *)n;
                                }
                        | DROP drop_type any_name_list opt_drop_behavior
@@ -4800,6 +4813,29 @@ DropStmt:        DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
                                        n->objects = $3;
                                        n->arguments = NIL;
                                        n->behavior = $4;
+                                       n->concurrent = false;
+                                       $$ = (Node *)n;
+                               }
+                       | DROP INDEX CONCURRENTLY any_name_list opt_drop_behavior
+                               {
+                                       DropStmt *n = makeNode(DropStmt);
+                                       n->removeType = OBJECT_INDEX;
+                                       n->missing_ok = FALSE;
+                                       n->objects = $4;
+                                       n->arguments = NIL;
+                                       n->behavior = $5;
+                                       n->concurrent = true;
+                                       $$ = (Node *)n;
+                               }
+                       | DROP INDEX CONCURRENTLY IF_P EXISTS any_name_list opt_drop_behavior
+                               {
+                                       DropStmt *n = makeNode(DropStmt);
+                                       n->removeType = OBJECT_INDEX;
+                                       n->missing_ok = FALSE;
+                                       n->objects = $6;
+                                       n->arguments = NIL;
+                                       n->behavior = $7;
+                                       n->concurrent = true;
                                        $$ = (Node *)n;
                                }
                ;
@@ -6246,6 +6282,7 @@ RemoveFuncStmt:
                                        n->arguments = list_make1(extractArgTypes($4));
                                        n->behavior = $5;
                                        n->missing_ok = false;
+                                       n->concurrent = false;
                                        $$ = (Node *)n;
                                }
                        | DROP FUNCTION IF_P EXISTS func_name func_args opt_drop_behavior
@@ -6256,6 +6293,7 @@ RemoveFuncStmt:
                                        n->arguments = list_make1(extractArgTypes($6));
                                        n->behavior = $7;
                                        n->missing_ok = true;
+                                       n->concurrent = false;
                                        $$ = (Node *)n;
                                }
                ;
@@ -6269,6 +6307,7 @@ RemoveAggrStmt:
                                        n->arguments = list_make1($4);
                                        n->behavior = $5;
                                        n->missing_ok = false;
+                                       n->concurrent = false;
                                        $$ = (Node *)n;
                                }
                        | DROP AGGREGATE IF_P EXISTS func_name aggr_args opt_drop_behavior
@@ -6279,6 +6318,7 @@ RemoveAggrStmt:
                                        n->arguments = list_make1($6);
                                        n->behavior = $7;
                                        n->missing_ok = true;
+                                       n->concurrent = false;
                                        $$ = (Node *)n;
                                }
                ;
@@ -6292,6 +6332,7 @@ RemoveOperStmt:
                                        n->arguments = list_make1($4);
                                        n->behavior = $5;
                                        n->missing_ok = false;
+                                       n->concurrent = false;
                                        $$ = (Node *)n;
                                }
                        | DROP OPERATOR IF_P EXISTS any_operator oper_argtypes opt_drop_behavior
@@ -6302,6 +6343,7 @@ RemoveOperStmt:
                                        n->arguments = list_make1($6);
                                        n->behavior = $7;
                                        n->missing_ok = true;
+                                       n->concurrent = false;
                                        $$ = (Node *)n;
                                }
                ;
@@ -6418,6 +6460,7 @@ DropCastStmt: DROP CAST opt_if_exists '(' Typename AS Typename ')' opt_drop_beha
                                        n->arguments = list_make1(list_make1($7));
                                        n->behavior = $9;
                                        n->missing_ok = $3;
+                                       n->concurrent = false;
                                        $$ = (Node *)n;
                                }
                ;
@@ -7339,6 +7382,7 @@ DropRuleStmt:
                                        n->arguments = NIL;
                                        n->behavior = $6;
                                        n->missing_ok = false;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                        | DROP RULE IF_P EXISTS name ON any_name opt_drop_behavior
@@ -7349,6 +7393,7 @@ DropRuleStmt:
                                        n->arguments = NIL;
                                        n->behavior = $8;
                                        n->missing_ok = true;
+                                       n->concurrent = false;
                                        $$ = (Node *) n;
                                }
                ;
index ea2a6c6a0822c5860278f9371dd18e945d7ae116..89f78f2e82b26d4c8c05ff3ec9a3a5445d1c75aa 100644 (file)
@@ -631,10 +631,15 @@ standard_ProcessUtility(Node *parsetree,
                case T_DropStmt:
                        switch (((DropStmt *) parsetree)->removeType)
                        {
+                               case OBJECT_INDEX:
+                                       if (((DropStmt *) parsetree)->concurrent)
+                                               PreventTransactionChain(isTopLevel,
+                                                                                       "DROP INDEX CONCURRENTLY");
+                                       /* fall through */
+
                                case OBJECT_TABLE:
                                case OBJECT_SEQUENCE:
                                case OBJECT_VIEW:
-                               case OBJECT_INDEX:
                                case OBJECT_FOREIGN_TABLE:
                                        RemoveRelations((DropStmt *) parsetree);
                                        break;
index a59950e45a06d49a1dc3c0b113247ec7aaca3a80..9cadb3f21aa1cb5576c6561f4324f100466ad10d 100644 (file)
@@ -3355,6 +3355,12 @@ RelationGetIndexList(Relation relation)
                oidvector  *indclass;
                bool            isnull;
 
+               /*
+                * Ignore any indexes that are currently being dropped
+                */
+               if (!index->indisvalid && !index->indisready)
+                       continue;
+
                /* Add index's OID to result list in the proper order */
                result = insert_ordered_oid(result, index->indexrelid);
 
index 28e68c5ab59d63fb37a2e4d1976643e751423697..f0eb564ebd931d0efbed9161bb78a920c3f49c50 100644 (file)
@@ -153,6 +153,7 @@ typedef enum ObjectClass
 /* in dependency.c */
 
 #define PERFORM_DELETION_INTERNAL                      0x0001
+#define PERFORM_DELETION_CONCURRENTLY          0x0002
 
 extern void performDeletion(const ObjectAddress *object,
                                DropBehavior behavior, int flags);
index c7f1dd285cde41dd836bd6e5ba7aee5efb14dfdf..3f73a6c58c28a120dcbf312a7c9e9c38c023d724 100644 (file)
@@ -63,7 +63,7 @@ extern void index_constraint_create(Relation heapRelation,
                                                bool update_pgindex,
                                                bool allow_system_table_mods);
 
-extern void index_drop(Oid indexId);
+extern void index_drop(Oid indexId, bool concurrent);
 
 extern IndexInfo *BuildIndexInfo(Relation index);
 
index bc9b6bd774c9a47e50a7cfb29f4b4e7627fe6148..aaa950db265b3b66a34a46c5afe769ce7af264fe 100644 (file)
@@ -1909,6 +1909,7 @@ typedef struct DropStmt
        ObjectType      removeType;             /* object type */
        DropBehavior behavior;          /* RESTRICT or CASCADE behavior */
        bool            missing_ok;             /* skip error if object is missing? */
+       bool            concurrent;             /* drop index concurrently? */
 } DropStmt;
 
 /* ----------------------
index b7497b047f74be705992c89915127a59f1d00058..36609c5bbf9894009934dfeb344171444a973ee3 100644 (file)
@@ -2316,6 +2316,34 @@ Indexes:
     "concur_index5" btree (f2) WHERE f1 = 'x'::text
     "std_index" btree (f2)
 
+--
+-- Try some concurrent index drops
+--
+DROP INDEX CONCURRENTLY "concur_index2";                               -- works
+DROP INDEX CONCURRENTLY IF EXISTS "concur_index2";             -- notice
+ERROR:  index "concur_index2" does not exist
+-- failures
+DROP INDEX CONCURRENTLY "concur_index2", "concur_index3";
+ERROR:  DROP INDEX CONCURRENTLY does not support dropping multiple objects
+BEGIN;
+DROP INDEX CONCURRENTLY "concur_index5";
+ERROR:  DROP INDEX CONCURRENTLY cannot run inside a transaction block
+ROLLBACK;
+-- successes
+DROP INDEX CONCURRENTLY IF EXISTS "concur_index3";
+DROP INDEX CONCURRENTLY "concur_index4";
+DROP INDEX CONCURRENTLY "concur_index5";
+DROP INDEX CONCURRENTLY "concur_index1";
+DROP INDEX CONCURRENTLY "concur_heap_expr_idx";
+\d concur_heap
+Table "public.concur_heap"
+ Column | Type | Modifiers 
+--------+------+-----------
+ f1     | text | 
+ f2     | text | 
+Indexes:
+    "std_index" btree (f2)
+
 DROP TABLE concur_heap;
 --
 -- Test ADD CONSTRAINT USING INDEX
index 57f52612dfa6d377f633a3de1e2dc2763a6fa280..deb198972655f2259af0db5d8215dc972830aa0d 100644 (file)
@@ -727,6 +727,27 @@ COMMIT;
 
 \d concur_heap
 
+--
+-- Try some concurrent index drops
+--
+DROP INDEX CONCURRENTLY "concur_index2";                               -- works
+DROP INDEX CONCURRENTLY IF EXISTS "concur_index2";             -- notice
+
+-- failures
+DROP INDEX CONCURRENTLY "concur_index2", "concur_index3";
+BEGIN;
+DROP INDEX CONCURRENTLY "concur_index5";
+ROLLBACK;
+
+-- successes
+DROP INDEX CONCURRENTLY IF EXISTS "concur_index3";
+DROP INDEX CONCURRENTLY "concur_index4";
+DROP INDEX CONCURRENTLY "concur_index5";
+DROP INDEX CONCURRENTLY "concur_index1";
+DROP INDEX CONCURRENTLY "concur_heap_expr_idx";
+
+\d concur_heap
+
 DROP TABLE concur_heap;
 
 --