On Thu, Jan 26, 2012 at 2:25 AM, Robert Haas <robertmh...@gmail.com> wrote: > On Sat, Jan 21, 2012 at 10:11 AM, Simon Riggs <si...@2ndquadrant.com> wrote: >> Your caution is wise. All users of an index have already checked >> whether the index is usable at plan time, so although there is much >> code that assumes they can look at the index itself, that is not >> executed until after the correct checks. >> >> I'll look at VACUUM and other utilities, so thanks for that. >> >> I don't see much point in having the higher level lock, except perhaps >> as a test this code works. > > I thought of another way this can cause a problem: suppose that while > we're dropping the relation with only ShareUpdateExclusiveLock, we get > as far as calling DropRelFileNodeBuffers. Just after we finish, some > other process that holds AccessShareLock or RowShareLock or > RowExclusiveLock reads and perhaps even dirties a page in the > relation.
I can't see any way that situation can occur. The patch *explicitly* waits until all people that can see the index as usable have dropped their lock. So I don't think this is necessary. Having said that, since we are talking about the index and not the whole table, if I believe the above statement then I can't have any reasonable objection to doing as you suggest. Patch now locks index in AccessExclusiveLock in final stage of drop. v3 attached. If you have suggestions to improve grammar issues, they;re most welcome. Otherwise this seems good to go. -- Simon Riggs http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services
diff --git a/doc/src/sgml/ref/drop_index.sgml b/doc/src/sgml/ref/drop_index.sgml index 7177ef2..aeb1531 100644 --- a/doc/src/sgml/ref/drop_index.sgml +++ b/doc/src/sgml/ref/drop_index.sgml @@ -21,7 +21,9 @@ PostgreSQL documentation <refsynopsisdiv> <synopsis> -DROP INDEX [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ] +DROP INDEX + [ IF EXISTS ] <replaceable class="PARAMETER">name</replaceable> [, ...] [ CASCADE | RESTRICT ] + CONCURRENTLY <replaceable class="PARAMETER">name</replaceable> </synopsis> </refsynopsisdiv> @@ -50,6 +52,30 @@ 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. Only one concurrent index drop can occur on a + table at a time and no modifications on the table are allowed meanwhile. + Regular <command>DROP INDEX</> command can be performed within + a transaction block, but <command>DROP INDEX CONCURRENTLY</> cannot. + There is no CASCADE option 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 db86262..58d8f5c 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -173,8 +173,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); @@ -232,7 +232,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 @@ -316,7 +316,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, @@ -350,7 +350,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); } /* @@ -380,7 +384,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 @@ -611,7 +615,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 @@ -706,7 +710,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 @@ -1016,9 +1020,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. @@ -1043,7 +1054,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)) { @@ -1053,8 +1064,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 { @@ -1190,10 +1204,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 bfbe642..c6708d3 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,122 @@ 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); + + /* 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 +1489,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 07dc326..6601c3a 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 */ @@ -735,6 +736,7 @@ RemoveRelations(DropStmt *drop) ObjectAddresses *objects; char relkind; ListCell *cell; + int flags = 0; /* * First we identify all the relations, then we delete them in a single @@ -797,7 +799,14 @@ 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; + if (drop->concurrent) + relOid = RangeVarGetRelidExtended(rel, ShareUpdateExclusiveLock, true, + false, + RangeVarCallbackForDropRelation, + (void *) &state); + else + relOid = RangeVarGetRelidExtended(rel, AccessExclusiveLock, true, false, RangeVarCallbackForDropRelation, (void *) &state); @@ -817,7 +826,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); } @@ -886,7 +908,8 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, { state->heapOid = IndexGetRelation(relOid, true); if (OidIsValid(state->heapOid)) - LockRelationOid(state->heapOid, AccessExclusiveLock); + LockRelationOid(state->heapOid, + (state->concurrent ? ShareUpdateExclusiveLock: AccessExclusiveLock)); } } diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index cc3168d..3c60dfb 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2763,6 +2763,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 2295195..597e2a7 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 62fde67..9beabcd 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -213,7 +213,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, CreatedbStmt DeclareCursorStmt DefineStmt DeleteStmt DiscardStmt DoStmt DropGroupStmt DropOpClassStmt DropOpFamilyStmt DropPLangStmt DropStmt DropAssertStmt DropTrigStmt DropRuleStmt DropCastStmt DropRoleStmt - DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt + DropUserStmt DropdbStmt DropTableSpaceStmt DropFdwStmt DropIndexStmt DropForeignServerStmt DropUserMappingStmt ExplainStmt FetchStmt GrantStmt GrantRoleStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt @@ -307,7 +307,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType, opt_column_list columnList opt_name_list sort_clause opt_sort_clause sortby_list index_params name_list from_clause from_list opt_array_bounds - qualified_name_list any_name any_name_list + qualified_name_list any_name any_name_list any_name_single any_operator expr_list attrs target_list insert_column_list set_target_list set_clause_list set_clause multiple_set_clause @@ -741,6 +741,7 @@ stmt : | DropFdwStmt | DropForeignServerStmt | DropGroupStmt + | DropIndexStmt | DropOpClassStmt | DropOpFamilyStmt | DropOwnedStmt @@ -4803,7 +4804,6 @@ DropStmt: DROP drop_type IF_P EXISTS any_name_list opt_drop_behavior drop_type: TABLE { $$ = OBJECT_TABLE; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } | VIEW { $$ = OBJECT_VIEW; } - | INDEX { $$ = OBJECT_INDEX; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } | TYPE_P { $$ = OBJECT_TYPE; } | DOMAIN_P { $$ = OBJECT_DOMAIN; } @@ -4832,6 +4832,44 @@ attrs: '.' attr_name { $$ = lappend($1, makeString($3)); } ; +any_name_single: + any_name { $$ = list_make1($1); } + ; + +DropIndexStmt: DROP INDEX IF_P EXISTS any_name_list + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_INDEX; + n->missing_ok = TRUE; + n->objects = $5; + n->arguments = NIL; + n->behavior = DROP_RESTRICT; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP INDEX any_name_list + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_INDEX; + n->missing_ok = FALSE; + n->objects = $3; + n->arguments = NIL; + n->behavior = DROP_RESTRICT; + n->concurrent = false; + $$ = (Node *)n; + } + | DROP INDEX CONCURRENTLY any_name_single + { + DropStmt *n = makeNode(DropStmt); + n->removeType = OBJECT_INDEX; + n->missing_ok = FALSE; + n->objects = $4; + n->arguments = NIL; + n->behavior = DROP_RESTRICT; + n->concurrent = true; + $$ = (Node *)n; + } + ; /***************************************************************************** * diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 5b81c0b..1e1d7a4 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/include/catalog/dependency.h b/src/include/catalog/dependency.h index 28e68c5..f0eb564 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 c7f1dd2..3f73a6c 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 1d33ceb..1c09042 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1908,6 +1908,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 b1fcada..b4d2b0d 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -2204,6 +2204,38 @@ Indexes: "concur_index5" btree (f2) WHERE f1 = 'x'::text "std_index" btree (f2) +-- +-- Try some concurrent index drops +-- +DROP INDEX CONCURRENTLY "concur_index2"; +-- failures +DROP INDEX IF EXISTS CONCURRENTLY; +ERROR: syntax error at or near "CONCURRENTLY" +LINE 1: DROP INDEX IF EXISTS CONCURRENTLY; + ^ +DROP INDEX CONCURRENTLY "concur_index2", "concur_index3"; +ERROR: syntax error at or near "," +LINE 1: DROP INDEX CONCURRENTLY "concur_index2", "concur_index3"; + ^ +BEGIN; +DROP INDEX CONCURRENTLY "concur_index5"; +ERROR: DROP INDEX CONCURRENTLY cannot run inside a transaction block +ROLLBACK; +-- successes +DROP INDEX CONCURRENTLY "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 5e5fc22..77c3613 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -695,6 +695,27 @@ COMMIT; \d concur_heap +-- +-- Try some concurrent index drops +-- +DROP INDEX CONCURRENTLY "concur_index2"; + +-- failures +DROP INDEX IF EXISTS CONCURRENTLY; +DROP INDEX CONCURRENTLY "concur_index2", "concur_index3"; +BEGIN; +DROP INDEX CONCURRENTLY "concur_index5"; +ROLLBACK; + +-- successes +DROP INDEX CONCURRENTLY "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; --
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers