From: Simon Riggs Date: Fri, 6 Apr 2012 09:21:40 +0000 (+0100) Subject: Add DROP INDEX CONCURRENTLY [IF EXISTS], uses ShareUpdateExclusiveLock X-Git-Tag: REL9_2_BETA1~179 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8cb53654dbdb4c386369eb988062d0bbb6de725e;p=postgresql Add DROP INDEX CONCURRENTLY [IF EXISTS], uses ShareUpdateExclusiveLock --- diff --git a/doc/src/sgml/ref/drop_index.sgml b/doc/src/sgml/ref/drop_index.sgml index 7177ef2d81..343f7aca00 100644 --- a/doc/src/sgml/ref/drop_index.sgml +++ b/doc/src/sgml/ref/drop_index.sgml @@ -21,7 +21,7 @@ PostgreSQL documentation -DROP INDEX [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] +DROP INDEX [ CONCURRENTLY ] [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ] @@ -49,6 +49,29 @@ DROP INDEX [ IF EXISTS ] name [, .. + + CONCURRENTLY + + + When this option is used, 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. + + + There are several caveats to be aware of when using this option. + Only one index name can be specified if the CONCURRENTLY + parameter is specified. Regular DROP INDEX command can be + performed within a transaction block, but + DROP INDEX CONCURRENTLY cannot. + The CASCADE option is not supported when dropping an index concurrently. + + + + name diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index fed724c51c..db6769cb90 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -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, diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index bfbe642535..5fae488c74 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -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); + } } /* ---------------------------------------------------------------- diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 9853686fe9..a35e338cc8 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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); } } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 33ee62f40d..f864af5d66 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index b749e9bbe3..956421020c 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index f2151ef498..ae1658a4ff 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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; } ; diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index ea2a6c6a08..89f78f2e82 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -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; diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index a59950e45a..9cadb3f21a 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -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); diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index 28e68c5ab5..f0eb564ebd 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -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); diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index c7f1dd285c..3f73a6c58c 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -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); diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index bc9b6bd774..aaa950db26 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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; /* ---------------------- diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index b7497b047f..36609c5bbf 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -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 diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index 57f52612df..deb1989726 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -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; --