summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Riggs2012-04-06 09:21:40 +0000
committerSimon Riggs2012-04-06 09:21:40 +0000
commit8cb53654dbdb4c386369eb988062d0bbb6de725e (patch)
treee422c04c90b21ae2c4180f796297a4a5ef509f38
parent21cc529698c8d10c6f7c76874d4adc98d27c6187 (diff)
Add DROP INDEX CONCURRENTLY [IF EXISTS], uses ShareUpdateExclusiveLock
-rw-r--r--doc/src/sgml/ref/drop_index.sgml25
-rw-r--r--src/backend/catalog/dependency.c45
-rw-r--r--src/backend/catalog/index.c139
-rw-r--r--src/backend/commands/tablecmds.c41
-rw-r--r--src/backend/nodes/copyfuncs.c1
-rw-r--r--src/backend/nodes/equalfuncs.c1
-rw-r--r--src/backend/parser/gram.y45
-rw-r--r--src/backend/tcop/utility.c7
-rw-r--r--src/backend/utils/cache/relcache.c6
-rw-r--r--src/include/catalog/dependency.h1
-rw-r--r--src/include/catalog/index.h2
-rw-r--r--src/include/nodes/parsenodes.h1
-rw-r--r--src/test/regress/expected/create_index.out28
-rw-r--r--src/test/regress/sql/create_index.sql21
14 files changed, 337 insertions, 26 deletions
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
<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>
@@ -50,6 +50,29 @@ DROP INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [, ..
</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>
<para>
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
@@ -1045,9 +1049,16 @@ deleteOneObject(const ObjectAddress *object, Relation depRel, int flags)
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,17 +1308,129 @@ 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;
--