mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Add DROP INDEX CONCURRENTLY [IF EXISTS], uses ShareUpdateExclusiveLock
This commit is contained in:
@ -21,7 +21,7 @@ PostgreSQL documentation
|
|||||||
|
|
||||||
<refsynopsisdiv>
|
<refsynopsisdiv>
|
||||||
<synopsis>
|
<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>
|
</synopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
|
||||||
@ -49,6 +49,29 @@ DROP INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [, ..
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><replaceable class="PARAMETER">name</replaceable></term>
|
<term><replaceable class="PARAMETER">name</replaceable></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -174,8 +174,8 @@ static void reportDependentObjects(const ObjectAddresses *targetObjects,
|
|||||||
const ObjectAddress *origObject);
|
const ObjectAddress *origObject);
|
||||||
static void deleteOneObject(const ObjectAddress *object,
|
static void deleteOneObject(const ObjectAddress *object,
|
||||||
Relation depRel, int32 flags);
|
Relation depRel, int32 flags);
|
||||||
static void doDeletion(const ObjectAddress *object);
|
static void doDeletion(const ObjectAddress *object, int flags);
|
||||||
static void AcquireDeletionLock(const ObjectAddress *object);
|
static void AcquireDeletionLock(const ObjectAddress *object, int flags);
|
||||||
static void ReleaseDeletionLock(const ObjectAddress *object);
|
static void ReleaseDeletionLock(const ObjectAddress *object);
|
||||||
static bool find_expr_references_walker(Node *node,
|
static bool find_expr_references_walker(Node *node,
|
||||||
find_expr_references_context *context);
|
find_expr_references_context *context);
|
||||||
@ -233,7 +233,7 @@ performDeletion(const ObjectAddress *object,
|
|||||||
* Acquire deletion lock on the target object. (Ideally the caller has
|
* Acquire deletion lock on the target object. (Ideally the caller has
|
||||||
* done this already, but many places are sloppy about it.)
|
* 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
|
* 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
|
* Acquire deletion lock on each target object. (Ideally the caller
|
||||||
* has done this already, but many places are sloppy about it.)
|
* has done this already, but many places are sloppy about it.)
|
||||||
*/
|
*/
|
||||||
AcquireDeletionLock(thisobj);
|
AcquireDeletionLock(thisobj, flags);
|
||||||
|
|
||||||
findDependentObjects(thisobj,
|
findDependentObjects(thisobj,
|
||||||
DEPFLAG_ORIGINAL,
|
DEPFLAG_ORIGINAL,
|
||||||
@ -351,7 +351,11 @@ performMultipleDeletions(const ObjectAddresses *objects,
|
|||||||
/* And clean up */
|
/* And clean up */
|
||||||
free_object_addresses(targetObjects);
|
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
|
* Acquire deletion lock on the target object. (Ideally the caller has
|
||||||
* done this already, but many places are sloppy about it.)
|
* 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
|
* 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.)
|
* deletion of the owning object.)
|
||||||
*/
|
*/
|
||||||
ReleaseDeletionLock(object);
|
ReleaseDeletionLock(object);
|
||||||
AcquireDeletionLock(&otherObject);
|
AcquireDeletionLock(&otherObject, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The owning object might have been deleted while we waited
|
* 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.
|
* 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
|
* 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,
|
deleteSharedDependencyRecordsFor(object->classId, object->objectId,
|
||||||
object->objectSubId);
|
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.
|
* 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.
|
* 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
|
* doDeletion: actually delete a single object
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
doDeletion(const ObjectAddress *object)
|
doDeletion(const ObjectAddress *object, int flags)
|
||||||
{
|
{
|
||||||
switch (getObjectClass(object))
|
switch (getObjectClass(object))
|
||||||
{
|
{
|
||||||
@ -1082,8 +1093,11 @@ doDeletion(const ObjectAddress *object)
|
|||||||
|
|
||||||
if (relKind == RELKIND_INDEX)
|
if (relKind == RELKIND_INDEX)
|
||||||
{
|
{
|
||||||
|
bool concurrent = ((flags & PERFORM_DELETION_CONCURRENTLY)
|
||||||
|
== PERFORM_DELETION_CONCURRENTLY);
|
||||||
|
|
||||||
Assert(object->objectSubId == 0);
|
Assert(object->objectSubId == 0);
|
||||||
index_drop(object->objectId);
|
index_drop(object->objectId, concurrent);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1219,10 +1233,15 @@ doDeletion(const ObjectAddress *object)
|
|||||||
* shared-across-databases object, so we have no need for LockSharedObject.
|
* shared-across-databases object, so we have no need for LockSharedObject.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
AcquireDeletionLock(const ObjectAddress *object)
|
AcquireDeletionLock(const ObjectAddress *object, int flags)
|
||||||
{
|
{
|
||||||
if (object->classId == RelationRelationId)
|
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
|
else
|
||||||
/* assume we should lock the whole object not a sub-object */
|
/* assume we should lock the whole object not a sub-object */
|
||||||
LockDatabaseObject(object->classId, object->objectId, 0,
|
LockDatabaseObject(object->classId, object->objectId, 0,
|
||||||
|
@ -1282,7 +1282,7 @@ index_constraint_create(Relation heapRelation,
|
|||||||
* else associated dependencies won't be cleaned up.
|
* else associated dependencies won't be cleaned up.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
index_drop(Oid indexId)
|
index_drop(Oid indexId, bool concurrent)
|
||||||
{
|
{
|
||||||
Oid heapId;
|
Oid heapId;
|
||||||
Relation userHeapRelation;
|
Relation userHeapRelation;
|
||||||
@ -1290,6 +1290,12 @@ index_drop(Oid indexId)
|
|||||||
Relation indexRelation;
|
Relation indexRelation;
|
||||||
HeapTuple tuple;
|
HeapTuple tuple;
|
||||||
bool hasexprs;
|
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
|
* 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.
|
* that will make them update their index lists.
|
||||||
*/
|
*/
|
||||||
heapId = IndexGetRelation(indexId, false);
|
heapId = IndexGetRelation(indexId, false);
|
||||||
userHeapRelation = heap_open(heapId, AccessExclusiveLock);
|
if (concurrent)
|
||||||
|
{
|
||||||
userIndexRelation = index_open(indexId, AccessExclusiveLock);
|
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
|
* We might still have open queries using it in our own session.
|
||||||
* still have open queries using it in our own session.
|
|
||||||
*/
|
*/
|
||||||
CheckTableNotInUse(userIndexRelation, "DROP INDEX");
|
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
|
* All predicate locks on the index are about to be made invalid. Promote
|
||||||
* them to relation locks on the heap.
|
* them to relation locks on the heap.
|
||||||
@ -1378,6 +1496,15 @@ index_drop(Oid indexId)
|
|||||||
* Close owning rel, but keep lock
|
* Close owning rel, but keep lock
|
||||||
*/
|
*/
|
||||||
heap_close(userHeapRelation, NoLock);
|
heap_close(userHeapRelation, NoLock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Release the session locks before we go.
|
||||||
|
*/
|
||||||
|
if (concurrent)
|
||||||
|
{
|
||||||
|
UnlockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock);
|
||||||
|
UnlockRelationIdForSession(&indexrelid, ShareUpdateExclusiveLock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
|
@ -239,6 +239,7 @@ struct DropRelationCallbackState
|
|||||||
{
|
{
|
||||||
char relkind;
|
char relkind;
|
||||||
Oid heapOid;
|
Oid heapOid;
|
||||||
|
bool concurrent;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Alter table target-type flags for ATSimplePermissions */
|
/* Alter table target-type flags for ATSimplePermissions */
|
||||||
@ -738,6 +739,21 @@ RemoveRelations(DropStmt *drop)
|
|||||||
ObjectAddresses *objects;
|
ObjectAddresses *objects;
|
||||||
char relkind;
|
char relkind;
|
||||||
ListCell *cell;
|
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
|
* 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. */
|
/* Look up the appropriate relation using namespace search. */
|
||||||
state.relkind = relkind;
|
state.relkind = relkind;
|
||||||
state.heapOid = InvalidOid;
|
state.heapOid = InvalidOid;
|
||||||
relOid = RangeVarGetRelidExtended(rel, AccessExclusiveLock, true,
|
state.concurrent = drop->concurrent;
|
||||||
|
relOid = RangeVarGetRelidExtended(rel, lockmode, true,
|
||||||
false,
|
false,
|
||||||
RangeVarCallbackForDropRelation,
|
RangeVarCallbackForDropRelation,
|
||||||
(void *) &state);
|
(void *) &state);
|
||||||
@ -820,7 +837,20 @@ RemoveRelations(DropStmt *drop)
|
|||||||
add_exact_object_address(&obj, objects);
|
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);
|
free_object_addresses(objects);
|
||||||
}
|
}
|
||||||
@ -837,9 +867,12 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
|
|||||||
struct DropRelationCallbackState *state;
|
struct DropRelationCallbackState *state;
|
||||||
char relkind;
|
char relkind;
|
||||||
Form_pg_class classform;
|
Form_pg_class classform;
|
||||||
|
LOCKMODE heap_lockmode;
|
||||||
|
|
||||||
state = (struct DropRelationCallbackState *) arg;
|
state = (struct DropRelationCallbackState *) arg;
|
||||||
relkind = state->relkind;
|
relkind = state->relkind;
|
||||||
|
heap_lockmode = state->concurrent ?
|
||||||
|
ShareUpdateExclusiveLock : AccessExclusiveLock;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we previously locked some other index's heap, and the name we're
|
* 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))
|
if (relOid != oldRelOid && OidIsValid(state->heapOid))
|
||||||
{
|
{
|
||||||
UnlockRelationOid(state->heapOid, AccessExclusiveLock);
|
UnlockRelationOid(state->heapOid, heap_lockmode);
|
||||||
state->heapOid = InvalidOid;
|
state->heapOid = InvalidOid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -889,7 +922,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
|
|||||||
{
|
{
|
||||||
state->heapOid = IndexGetRelation(relOid, true);
|
state->heapOid = IndexGetRelation(relOid, true);
|
||||||
if (OidIsValid(state->heapOid))
|
if (OidIsValid(state->heapOid))
|
||||||
LockRelationOid(state->heapOid, AccessExclusiveLock);
|
LockRelationOid(state->heapOid, heap_lockmode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2751,6 +2751,7 @@ _copyDropStmt(const DropStmt *from)
|
|||||||
COPY_SCALAR_FIELD(removeType);
|
COPY_SCALAR_FIELD(removeType);
|
||||||
COPY_SCALAR_FIELD(behavior);
|
COPY_SCALAR_FIELD(behavior);
|
||||||
COPY_SCALAR_FIELD(missing_ok);
|
COPY_SCALAR_FIELD(missing_ok);
|
||||||
|
COPY_SCALAR_FIELD(concurrent);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
@ -1189,6 +1189,7 @@ _equalDropStmt(const DropStmt *a, const DropStmt *b)
|
|||||||
COMPARE_SCALAR_FIELD(removeType);
|
COMPARE_SCALAR_FIELD(removeType);
|
||||||
COMPARE_SCALAR_FIELD(behavior);
|
COMPARE_SCALAR_FIELD(behavior);
|
||||||
COMPARE_SCALAR_FIELD(missing_ok);
|
COMPARE_SCALAR_FIELD(missing_ok);
|
||||||
|
COMPARE_SCALAR_FIELD(concurrent);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -3276,6 +3276,7 @@ DropPLangStmt:
|
|||||||
n->arguments = NIL;
|
n->arguments = NIL;
|
||||||
n->behavior = $5;
|
n->behavior = $5;
|
||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| DROP opt_procedural LANGUAGE IF_P EXISTS ColId_or_Sconst opt_drop_behavior
|
| 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->objects = list_make1(list_make1(makeString($6)));
|
||||||
n->behavior = $7;
|
n->behavior = $7;
|
||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -3680,6 +3682,7 @@ DropFdwStmt: DROP FOREIGN DATA_P WRAPPER name opt_drop_behavior
|
|||||||
n->arguments = NIL;
|
n->arguments = NIL;
|
||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
n->behavior = $6;
|
n->behavior = $6;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| DROP FOREIGN DATA_P WRAPPER IF_P EXISTS name opt_drop_behavior
|
| 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->arguments = NIL;
|
||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
n->behavior = $8;
|
n->behavior = $8;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -3840,6 +3844,7 @@ DropForeignServerStmt: DROP SERVER name opt_drop_behavior
|
|||||||
n->arguments = NIL;
|
n->arguments = NIL;
|
||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
n->behavior = $4;
|
n->behavior = $4;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| DROP SERVER IF_P EXISTS name opt_drop_behavior
|
| DROP SERVER IF_P EXISTS name opt_drop_behavior
|
||||||
@ -3850,6 +3855,7 @@ DropForeignServerStmt: DROP SERVER name opt_drop_behavior
|
|||||||
n->arguments = NIL;
|
n->arguments = NIL;
|
||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
n->behavior = $6;
|
n->behavior = $6;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -4237,6 +4243,7 @@ DropTrigStmt:
|
|||||||
n->arguments = NIL;
|
n->arguments = NIL;
|
||||||
n->behavior = $6;
|
n->behavior = $6;
|
||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| DROP TRIGGER IF_P EXISTS name ON any_name opt_drop_behavior
|
| DROP TRIGGER IF_P EXISTS name ON any_name opt_drop_behavior
|
||||||
@ -4247,6 +4254,7 @@ DropTrigStmt:
|
|||||||
n->arguments = NIL;
|
n->arguments = NIL;
|
||||||
n->behavior = $8;
|
n->behavior = $8;
|
||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -4707,6 +4715,7 @@ DropOpClassStmt:
|
|||||||
n->removeType = OBJECT_OPCLASS;
|
n->removeType = OBJECT_OPCLASS;
|
||||||
n->behavior = $7;
|
n->behavior = $7;
|
||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| DROP OPERATOR CLASS IF_P EXISTS any_name USING access_method opt_drop_behavior
|
| DROP OPERATOR CLASS IF_P EXISTS any_name USING access_method opt_drop_behavior
|
||||||
@ -4717,6 +4726,7 @@ DropOpClassStmt:
|
|||||||
n->removeType = OBJECT_OPCLASS;
|
n->removeType = OBJECT_OPCLASS;
|
||||||
n->behavior = $9;
|
n->behavior = $9;
|
||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -4730,6 +4740,7 @@ DropOpFamilyStmt:
|
|||||||
n->removeType = OBJECT_OPFAMILY;
|
n->removeType = OBJECT_OPFAMILY;
|
||||||
n->behavior = $7;
|
n->behavior = $7;
|
||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| DROP OPERATOR FAMILY IF_P EXISTS any_name USING access_method opt_drop_behavior
|
| DROP OPERATOR FAMILY IF_P EXISTS any_name USING access_method opt_drop_behavior
|
||||||
@ -4740,6 +4751,7 @@ DropOpFamilyStmt:
|
|||||||
n->removeType = OBJECT_OPFAMILY;
|
n->removeType = OBJECT_OPFAMILY;
|
||||||
n->behavior = $9;
|
n->behavior = $9;
|
||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -4790,6 +4802,7 @@ DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior
|
|||||||
n->objects = $5;
|
n->objects = $5;
|
||||||
n->arguments = NIL;
|
n->arguments = NIL;
|
||||||
n->behavior = $6;
|
n->behavior = $6;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| DROP drop_type any_name_list opt_drop_behavior
|
| 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->objects = $3;
|
||||||
n->arguments = NIL;
|
n->arguments = NIL;
|
||||||
n->behavior = $4;
|
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;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -6246,6 +6282,7 @@ RemoveFuncStmt:
|
|||||||
n->arguments = list_make1(extractArgTypes($4));
|
n->arguments = list_make1(extractArgTypes($4));
|
||||||
n->behavior = $5;
|
n->behavior = $5;
|
||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| DROP FUNCTION IF_P EXISTS func_name func_args opt_drop_behavior
|
| DROP FUNCTION IF_P EXISTS func_name func_args opt_drop_behavior
|
||||||
@ -6256,6 +6293,7 @@ RemoveFuncStmt:
|
|||||||
n->arguments = list_make1(extractArgTypes($6));
|
n->arguments = list_make1(extractArgTypes($6));
|
||||||
n->behavior = $7;
|
n->behavior = $7;
|
||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -6269,6 +6307,7 @@ RemoveAggrStmt:
|
|||||||
n->arguments = list_make1($4);
|
n->arguments = list_make1($4);
|
||||||
n->behavior = $5;
|
n->behavior = $5;
|
||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| DROP AGGREGATE IF_P EXISTS func_name aggr_args opt_drop_behavior
|
| DROP AGGREGATE IF_P EXISTS func_name aggr_args opt_drop_behavior
|
||||||
@ -6279,6 +6318,7 @@ RemoveAggrStmt:
|
|||||||
n->arguments = list_make1($6);
|
n->arguments = list_make1($6);
|
||||||
n->behavior = $7;
|
n->behavior = $7;
|
||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -6292,6 +6332,7 @@ RemoveOperStmt:
|
|||||||
n->arguments = list_make1($4);
|
n->arguments = list_make1($4);
|
||||||
n->behavior = $5;
|
n->behavior = $5;
|
||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
| DROP OPERATOR IF_P EXISTS any_operator oper_argtypes opt_drop_behavior
|
| DROP OPERATOR IF_P EXISTS any_operator oper_argtypes opt_drop_behavior
|
||||||
@ -6302,6 +6343,7 @@ RemoveOperStmt:
|
|||||||
n->arguments = list_make1($6);
|
n->arguments = list_make1($6);
|
||||||
n->behavior = $7;
|
n->behavior = $7;
|
||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (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->arguments = list_make1(list_make1($7));
|
||||||
n->behavior = $9;
|
n->behavior = $9;
|
||||||
n->missing_ok = $3;
|
n->missing_ok = $3;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *)n;
|
$$ = (Node *)n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -7339,6 +7382,7 @@ DropRuleStmt:
|
|||||||
n->arguments = NIL;
|
n->arguments = NIL;
|
||||||
n->behavior = $6;
|
n->behavior = $6;
|
||||||
n->missing_ok = false;
|
n->missing_ok = false;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
| DROP RULE IF_P EXISTS name ON any_name opt_drop_behavior
|
| DROP RULE IF_P EXISTS name ON any_name opt_drop_behavior
|
||||||
@ -7349,6 +7393,7 @@ DropRuleStmt:
|
|||||||
n->arguments = NIL;
|
n->arguments = NIL;
|
||||||
n->behavior = $8;
|
n->behavior = $8;
|
||||||
n->missing_ok = true;
|
n->missing_ok = true;
|
||||||
|
n->concurrent = false;
|
||||||
$$ = (Node *) n;
|
$$ = (Node *) n;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -631,10 +631,15 @@ standard_ProcessUtility(Node *parsetree,
|
|||||||
case T_DropStmt:
|
case T_DropStmt:
|
||||||
switch (((DropStmt *) parsetree)->removeType)
|
switch (((DropStmt *) parsetree)->removeType)
|
||||||
{
|
{
|
||||||
|
case OBJECT_INDEX:
|
||||||
|
if (((DropStmt *) parsetree)->concurrent)
|
||||||
|
PreventTransactionChain(isTopLevel,
|
||||||
|
"DROP INDEX CONCURRENTLY");
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
case OBJECT_TABLE:
|
case OBJECT_TABLE:
|
||||||
case OBJECT_SEQUENCE:
|
case OBJECT_SEQUENCE:
|
||||||
case OBJECT_VIEW:
|
case OBJECT_VIEW:
|
||||||
case OBJECT_INDEX:
|
|
||||||
case OBJECT_FOREIGN_TABLE:
|
case OBJECT_FOREIGN_TABLE:
|
||||||
RemoveRelations((DropStmt *) parsetree);
|
RemoveRelations((DropStmt *) parsetree);
|
||||||
break;
|
break;
|
||||||
|
6
src/backend/utils/cache/relcache.c
vendored
6
src/backend/utils/cache/relcache.c
vendored
@ -3355,6 +3355,12 @@ RelationGetIndexList(Relation relation)
|
|||||||
oidvector *indclass;
|
oidvector *indclass;
|
||||||
bool isnull;
|
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 */
|
/* Add index's OID to result list in the proper order */
|
||||||
result = insert_ordered_oid(result, index->indexrelid);
|
result = insert_ordered_oid(result, index->indexrelid);
|
||||||
|
|
||||||
|
@ -153,6 +153,7 @@ typedef enum ObjectClass
|
|||||||
/* in dependency.c */
|
/* in dependency.c */
|
||||||
|
|
||||||
#define PERFORM_DELETION_INTERNAL 0x0001
|
#define PERFORM_DELETION_INTERNAL 0x0001
|
||||||
|
#define PERFORM_DELETION_CONCURRENTLY 0x0002
|
||||||
|
|
||||||
extern void performDeletion(const ObjectAddress *object,
|
extern void performDeletion(const ObjectAddress *object,
|
||||||
DropBehavior behavior, int flags);
|
DropBehavior behavior, int flags);
|
||||||
|
@ -63,7 +63,7 @@ extern void index_constraint_create(Relation heapRelation,
|
|||||||
bool update_pgindex,
|
bool update_pgindex,
|
||||||
bool allow_system_table_mods);
|
bool allow_system_table_mods);
|
||||||
|
|
||||||
extern void index_drop(Oid indexId);
|
extern void index_drop(Oid indexId, bool concurrent);
|
||||||
|
|
||||||
extern IndexInfo *BuildIndexInfo(Relation index);
|
extern IndexInfo *BuildIndexInfo(Relation index);
|
||||||
|
|
||||||
|
@ -1909,6 +1909,7 @@ typedef struct DropStmt
|
|||||||
ObjectType removeType; /* object type */
|
ObjectType removeType; /* object type */
|
||||||
DropBehavior behavior; /* RESTRICT or CASCADE behavior */
|
DropBehavior behavior; /* RESTRICT or CASCADE behavior */
|
||||||
bool missing_ok; /* skip error if object is missing? */
|
bool missing_ok; /* skip error if object is missing? */
|
||||||
|
bool concurrent; /* drop index concurrently? */
|
||||||
} DropStmt;
|
} DropStmt;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
|
@ -2316,6 +2316,34 @@ Indexes:
|
|||||||
"concur_index5" btree (f2) WHERE f1 = 'x'::text
|
"concur_index5" btree (f2) WHERE f1 = 'x'::text
|
||||||
"std_index" btree (f2)
|
"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;
|
DROP TABLE concur_heap;
|
||||||
--
|
--
|
||||||
-- Test ADD CONSTRAINT USING INDEX
|
-- Test ADD CONSTRAINT USING INDEX
|
||||||
|
@ -727,6 +727,27 @@ COMMIT;
|
|||||||
|
|
||||||
\d concur_heap
|
\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;
|
DROP TABLE concur_heap;
|
||||||
|
|
||||||
--
|
--
|
||||||
|
Reference in New Issue
Block a user