On Wed, Jan 18, 2012 at 11:12 PM, Robert Haas <robertmh...@gmail.com> wrote: > On Wed, Jan 18, 2012 at 5:49 PM, Simon Riggs <si...@2ndquadrant.com> wrote: >> Can I just check with you that the only review comment is a one line >> change? Seems better to make any additional review comments in one go. > > No, I haven't done a full review yet - I just noticed that right at > the outset and wanted to be sure that got addressed. I can look it > over more carefully, and test it.
Corrected your point, and another found during retest. Works as advertised, docs corrected. -- 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 0b3d489..6884fdb 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -171,9 +171,10 @@ static void reportDependentObjects(const ObjectAddresses *targetObjects, DropBehavior behavior, int msglevel, const ObjectAddress *origObject); -static void deleteOneObject(const ObjectAddress *object, Relation depRel); -static void doDeletion(const ObjectAddress *object); -static void AcquireDeletionLock(const ObjectAddress *object); +static void deleteOneObject(const ObjectAddress *object, Relation depRel, + int option_flags); +static void doDeletion(const ObjectAddress *object, int option_flags); +static void AcquireDeletionLock(const ObjectAddress *object, int option_flags); static void ReleaseDeletionLock(const ObjectAddress *object); static bool find_expr_references_walker(Node *node, find_expr_references_context *context); @@ -224,7 +225,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 @@ -254,7 +255,7 @@ performDeletion(const ObjectAddress *object, { ObjectAddress *thisobj = targetObjects->refs + i; - deleteOneObject(thisobj, depRel); + deleteOneObject(thisobj, depRel, 0); } /* And clean up */ @@ -276,6 +277,13 @@ void performMultipleDeletions(const ObjectAddresses *objects, DropBehavior behavior) { + performMultipleDeletionsWithOptions(objects, behavior, 0); +} + +void +performMultipleDeletionsWithOptions(const ObjectAddresses *objects, + DropBehavior behavior, int option_flags) +{ Relation depRel; ObjectAddresses *targetObjects; int i; @@ -308,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, option_flags); findDependentObjects(thisobj, DEPFLAG_ORIGINAL, @@ -336,13 +344,17 @@ performMultipleDeletions(const ObjectAddresses *objects, { ObjectAddress *thisobj = targetObjects->refs + i; - deleteOneObject(thisobj, depRel); + deleteOneObject(thisobj, depRel, option_flags); } /* And clean up */ free_object_addresses(targetObjects); - heap_close(depRel, RowExclusiveLock); + /* + * We closed depRel earlier in deleteOneObject if doing a drop concurrently + */ + if ((option_flags & OPT_CONCURRENTLY) != OPT_CONCURRENTLY) + heap_close(depRel, RowExclusiveLock); } /* @@ -372,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 @@ -407,7 +419,7 @@ deleteWhatDependsOn(const ObjectAddress *object, if (thisextra->flags & DEPFLAG_ORIGINAL) continue; - deleteOneObject(thisobj, depRel); + deleteOneObject(thisobj, depRel, 0); } /* And clean up */ @@ -596,7 +608,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 @@ -691,7 +703,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 @@ -950,7 +962,7 @@ reportDependentObjects(const ObjectAddresses *targetObjects, * depRel is the already-open pg_depend relation. */ static void -deleteOneObject(const ObjectAddress *object, Relation depRel) +deleteOneObject(const ObjectAddress *object, Relation depRel, int option_flags) { ScanKeyData key[3]; int nkeys; @@ -1001,9 +1013,16 @@ deleteOneObject(const ObjectAddress *object, Relation depRel) object->objectSubId); /* + * Close depRel if we are doing a drop concurrently because it + * commits the transaction, so we don't want dangling references. + */ + if ((option_flags & OPT_CONCURRENTLY) == OPT_CONCURRENTLY) + heap_close(depRel, RowExclusiveLock); + + /* * Now delete the object itself, in an object-type-dependent way. */ - doDeletion(object); + doDeletion(object, option_flags); /* * Delete any comments or security labels associated with this object. @@ -1028,7 +1047,7 @@ deleteOneObject(const ObjectAddress *object, Relation depRel) * doDeletion: actually delete a single object */ static void -doDeletion(const ObjectAddress *object) +doDeletion(const ObjectAddress *object, int option_flags) { switch (getObjectClass(object)) { @@ -1038,8 +1057,11 @@ doDeletion(const ObjectAddress *object) if (relKind == RELKIND_INDEX) { + bool concurrent = ((option_flags & OPT_CONCURRENTLY) + == OPT_CONCURRENTLY); + Assert(object->objectSubId == 0); - index_drop(object->objectId); + index_drop(object->objectId, concurrent); } else { @@ -1175,10 +1197,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 option_flags) { if (object->classId == RelationRelationId) - LockRelationOid(object->objectId, AccessExclusiveLock); + { + if ((option_flags & OPT_CONCURRENTLY) == OPT_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..5b7d5f0 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,118 @@ 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 + */ + userHeapRelation = heap_open(heapId, ShareUpdateExclusiveLock); + userIndexRelation = index_open(indexId, ShareUpdateExclusiveLock); + } + + /* * All predicate locks on the index are about to be made invalid. Promote * them to relation locks on the heap. */ @@ -1378,6 +1485,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 cc210f0..b142816 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 */ @@ -734,6 +735,7 @@ RemoveRelations(DropStmt *drop) ObjectAddresses *objects; char relkind; ListCell *cell; + int option_flags = 0; /* * First we identify all the relations, then we delete them in a single @@ -796,7 +798,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); @@ -816,7 +825,20 @@ RemoveRelations(DropStmt *drop) add_exact_object_address(&obj, objects); } - performMultipleDeletions(objects, drop->behavior); + /* + * 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); + + option_flags |= OPT_CONCURRENTLY; + } + + performMultipleDeletionsWithOptions(objects, drop->behavior, option_flags); free_object_addresses(objects); } @@ -885,7 +907,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 71da0d8..6f19ec3 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2762,6 +2762,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 ba949db..1e6700e 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1188,6 +1188,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 0ec039b..198cc6f 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 @@ -4743,7 +4744,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; } @@ -4772,6 +4772,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 de16a61..7549666 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 087341e..467e385 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -158,6 +158,11 @@ extern void performDeletion(const ObjectAddress *object, extern void performMultipleDeletions(const ObjectAddresses *objects, DropBehavior behavior); +extern void performMultipleDeletionsWithOptions(const ObjectAddresses *objects, + DropBehavior behavior, int option_flags); +/* options specified for object deletion */ +#define OPT_CONCURRENTLY 0x01 + extern void deleteWhatDependsOn(const ObjectAddress *object, bool showNotices); 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 dce0e72..82c4397 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1906,6 +1906,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