diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 2a37cf2..db5917b 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1091,6 +1091,100 @@ index_create(Relation heapRelation, } /* + * index_concurrent_build + * + * Build index for a concurrent operation. Low-level locks are taken when this + * operation is performed to prevent only schema changes. + */ +void +index_concurrent_build(Oid heapOid, + Oid indexOid, + bool isprimary) +{ + Relation rel, indexRelation; + IndexInfo *indexInfo; + + /* Open and lock the parent heap relation */ + rel = heap_open(heapOid, ShareUpdateExclusiveLock); + + /* And the target index relation */ + indexRelation = index_open(indexOid, RowExclusiveLock); + + /* + * We have to re-build the IndexInfo struct, since it was lost in + * commit of transaction where this concurrent index was created + * at the catalog level. + */ + indexInfo = BuildIndexInfo(indexRelation); + Assert(!indexInfo->ii_ReadyForInserts); + indexInfo->ii_Concurrent = true; + indexInfo->ii_BrokenHotChain = false; + + /* Now build the index */ + index_build(rel, indexRelation, indexInfo, isprimary, false); + + /* Close both the relations, but keep the locks */ + heap_close(rel, NoLock); + index_close(indexRelation, NoLock); +} + +/* + * index_concurrent_set_dead + * + * Perform the last invalidation stage of DROP INDEX CONCURRENTLY before + * actually dropping the index. After calling this function the index is + * seen by all the backends as dead. + */ +void +index_concurrent_set_dead(Oid heapOid, Oid indexOid, LOCKTAG locktag) +{ + Relation heapRelation, indexRelation; + + /* + * Now we must wait until no running transaction could be using the + * index for a query if necessary. + * + * 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. + */ + WaitForVirtualLocks(locktag, AccessExclusiveLock); + + /* + * No more predicate locks will be acquired on this index, and we're + * about to stop doing inserts into the index which could show + * conflicts with existing predicate locks, so now is the time to move + * them to the heap relation. + */ + heapRelation = heap_open(heapOid, ShareUpdateExclusiveLock); + indexRelation = index_open(indexOid, ShareUpdateExclusiveLock); + TransferPredicateLocksToHeapRelation(indexRelation); + + /* + * Now we are sure that nobody uses the index for queries; they just + * might have it open for updating it. So now we can unset indisready + * and indislive, then wait till nobody could be using it at all + * anymore. + */ + index_set_state_flags(indexOid, INDEX_DROP_SET_DEAD); + + /* + * Invalidate the relcache for the table, so that after this commit + * all sessions will refresh the table's index list. Forgetting just + * the index's relcache entry is not enough. + */ + CacheInvalidateRelcache(heapRelation); + + /* + * Close the relations again, though still holding session lock. + */ + heap_close(heapRelation, NoLock); + index_close(indexRelation, NoLock); +} + +/* * index_constraint_create * * Set up a constraint associated with an index @@ -1444,50 +1538,8 @@ index_drop(Oid indexId, bool concurrent) CommitTransactionCommand(); StartTransactionCommand(); - /* - * Now we must wait until no running transaction could be using the - * index for a query. Note we do not need to worry about xacts that - * open the table for reading 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. - */ - WaitForVirtualLocks(heaplocktag, AccessExclusiveLock); - - /* - * No more predicate locks will be acquired on this index, and we're - * about to stop doing inserts into the index which could show - * conflicts with existing predicate locks, so now is the time to move - * them to the heap relation. - */ - userHeapRelation = heap_open(heapId, ShareUpdateExclusiveLock); - userIndexRelation = index_open(indexId, ShareUpdateExclusiveLock); - TransferPredicateLocksToHeapRelation(userIndexRelation); - - /* - * Now we are sure that nobody uses the index for queries; they just - * might have it open for updating it. So now we can unset indisready - * and indislive, then wait till nobody could be using it at all - * anymore. - */ - index_set_state_flags(indexId, INDEX_DROP_SET_DEAD); - - /* - * Invalidate the relcache for the table, so that after this commit - * all sessions will refresh the table's index list. Forgetting just - * the index's relcache entry is not enough. - */ - CacheInvalidateRelcache(userHeapRelation); - - /* - * Close the relations again, though still holding session lock. - */ - heap_close(userHeapRelation, NoLock); - index_close(userIndexRelation, NoLock); + /* Finish invalidation of index and mark it as dead */ + index_concurrent_set_dead(heapId, indexId, heaplocktag); /* * Again, commit the transaction to make the pg_index update visible diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index e913475..4ed9812 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -311,7 +311,6 @@ DefineIndex(IndexStmt *stmt, Oid tablespaceId; List *indexColNames; Relation rel; - Relation indexRelation; HeapTuple tuple; Form_pg_am accessMethodForm; bool amcanorder; @@ -678,27 +677,13 @@ DefineIndex(IndexStmt *stmt, * HOT-chain or the extension of the chain is HOT-safe for this index. */ - /* Open and lock the parent heap relation */ - rel = heap_openrv(stmt->relation, ShareUpdateExclusiveLock); - - /* And the target index relation */ - indexRelation = index_open(indexRelationId, RowExclusiveLock); - /* Set ActiveSnapshot since functions in the indexes may need it */ PushActiveSnapshot(GetTransactionSnapshot()); - /* We have to re-build the IndexInfo struct, since it was lost in commit */ - indexInfo = BuildIndexInfo(indexRelation); - Assert(!indexInfo->ii_ReadyForInserts); - indexInfo->ii_Concurrent = true; - indexInfo->ii_BrokenHotChain = false; - - /* Now build the index */ - index_build(rel, indexRelation, indexInfo, stmt->primary, false); - - /* Close both the relations, but keep the locks */ - heap_close(rel, NoLock); - index_close(indexRelation, NoLock); + /* Perform concurrent build of index */ + index_concurrent_build(RangeVarGetRelid(stmt->relation, NoLock, false), + indexRelationId, + stmt->primary); /* * Update the pg_index row to mark the index as ready for inserts. Once we diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index e697275..9f29003 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -62,6 +62,14 @@ extern Oid index_create(Relation heapRelation, bool concurrent, bool is_internal); +extern void index_concurrent_build(Oid heapOid, + Oid indexOid, + bool isprimary); + +extern void index_concurrent_set_dead(Oid heapOid, + Oid indexOid, + LOCKTAG locktag); + extern void index_constraint_create(Relation heapRelation, Oid indexRelationId, IndexInfo *indexInfo,