Add DROP INDEX CONCURRENTLY [IF EXISTS], uses ShareUpdateExclusiveLock

This commit is contained in:
Simon Riggs 2012-04-06 10:21:40 +01:00
parent 21cc529698
commit 8cb53654db
14 changed files with 337 additions and 26 deletions

View 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>

View 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,

View 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);
}
}
/* ----------------------------------------------------------------

View 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);
}
}

View 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;
}

View 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;
}

View 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;
}
;

View 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;

View 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);

View 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);

View 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);

View 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;
/* ----------------------

View 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

View 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;
--