diff --git a/doc/src/sgml/ref/reindex.sgml b/doc/src/sgml/ref/reindex.sgml index ee22c267c1..b0983c7ea1 100644 --- a/doc/src/sgml/ref/reindex.sgml +++ b/doc/src/sgml/ref/reindex.sgml @@ -386,9 +386,10 @@ Indexes: The recommended recovery method in such cases is to drop the invalid index and try again to perform REINDEX CONCURRENTLY. The concurrent index created during the processing has a name ending in - the suffix ccnew, or ccold if it is an old index definition which we failed - to drop. Invalid indexes can be dropped using DROP INDEX, - including invalid toast indexes. + the suffix ccnew, or ccold if it + is an old index definition which we failed to drop. Invalid indexes can + be dropped using DROP INDEX, including invalid toast + indexes. @@ -396,9 +397,9 @@ Indexes: table to occur in parallel, but only one concurrent index build can occur on a table at a time. In both cases, no other types of schema modification on the table are allowed meanwhile. Another difference - is that a regular REINDEX TABLE or REINDEX INDEX - command can be performed within a transaction block, but - REINDEX CONCURRENTLY cannot. + is that a regular REINDEX TABLE or + REINDEX INDEX command can be performed within a + transaction block, but REINDEX CONCURRENTLY cannot. diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 9b3a742663..b84c05736f 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -693,7 +693,6 @@ UpdateIndexRelation(Oid indexoid, * classObjectId: array of index opclass OIDs, one per index column * coloptions: array of per-index-column indoption settings * reloptions: AM-specific options - * tupdesc: Tuple descriptor used for the index if defined * flags: bitmask that can include any combination of these bits: * INDEX_CREATE_IS_PRIMARY * the index is a primary key @@ -734,7 +733,6 @@ index_create(Relation heapRelation, Oid *classObjectId, int16 *coloptions, Datum reloptions, - TupleDesc tupdesc, bits16 flags, bits16 constr_flags, bool allow_system_table_mods, @@ -742,6 +740,7 @@ index_create(Relation heapRelation, Oid *constraintId) { Oid heapRelationId = RelationGetRelid(heapRelation); + Oid heapNamespaceId = get_rel_namespace(heapRelationId); Relation pg_class; Relation indexRelation; TupleDesc indexTupDesc; @@ -794,10 +793,12 @@ index_create(Relation heapRelation, /* * concurrent index build on a system catalog is unsafe because we tend to - * release locks before committing in catalogs + * release locks before committing in catalogs. Toast catalogs are fine + * though as they are associated with a root relation which could be + * reindexed concurrently. */ if (concurrent && - IsSystemNamespace(get_rel_namespace(heapRelationId))) + IsSystemNamespace(heapNamespaceId)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("concurrent index creation on system catalog tables is not supported"))); @@ -865,20 +866,14 @@ index_create(Relation heapRelation, } /* - * construct tuple descriptor for index tuples if not passed by caller + * construct tuple descriptor for index tuples */ - if (!tupdesc) - indexTupDesc = ConstructTupleDescriptor(heapRelation, - indexInfo, - indexColNames, - accessMethodObjectId, - collationObjectId, - classObjectId); - else - { - Assert(indexColNames == NIL); - indexTupDesc = tupdesc; - } + indexTupDesc = ConstructTupleDescriptor(heapRelation, + indexInfo, + indexColNames, + accessMethodObjectId, + collationObjectId, + classObjectId); /* * Allocate an OID for the index, unless we were told what to use. @@ -1210,14 +1205,15 @@ index_create(Relation heapRelation, } /* - * index_concurrently_create_copy + * index_create_copy_concurrent * * Create concurrently an index based on the definition of the one provided by * caller. The index is inserted into catalogs and needs to be built later * on. This is called during concurrent reindex processing. */ Oid -index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char *newName) +index_create_copy_concurrent(Relation heapRelation, Oid oldIndexId, + const char *newName) { Relation indexRelation; IndexInfo *indexInfo; @@ -1231,6 +1227,8 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char oidvector *indclass; int2vector *indcoloptions; bool isnull; + List *indexColNames = NIL; + int i; indexRelation = index_open(oldIndexId, RowExclusiveLock); @@ -1242,9 +1240,6 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; - /* Create a copy of the tuple descriptor to be used for the new entry */ - indexTupDesc = CreateTupleDescCopyConstr(RelationGetDescr(indexRelation)); - /* Get the array of class and column options IDs from index info */ indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(oldIndexId)); if (!HeapTupleIsValid(indexTuple)) @@ -1266,6 +1261,19 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char optionDatum = SysCacheGetAttr(RELOID, classTuple, Anum_pg_class_reloptions, &isnull); + /* + * Extract the list of column names to be used for the index + * creation. + */ + indexTupDesc = RelationGetDescr(indexRelation); + for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) + { + Form_pg_attribute att = TupleDescAttr(indexTupDesc, i); + + /* Grab the column name and save it to the list */ + indexColNames = lappend(indexColNames, NameStr(att->attname)); + } + /* Now create the new index */ newIndexId = index_create(heapRelation, newName, @@ -1274,14 +1282,13 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char InvalidOid, /* parentConstraintId */ InvalidOid, /* relFileNode */ indexInfo, - NIL, + indexColNames, indexRelation->rd_rel->relam, indexRelation->rd_rel->reltablespace, indexRelation->rd_indcollation, indclass->values, indcoloptions->values, optionDatum, - indexTupDesc, INDEX_CREATE_SKIP_BUILD | INDEX_CREATE_CONCURRENT, 0, true, /* allow table to be a system catalog? */ @@ -1297,20 +1304,24 @@ index_concurrently_create_copy(Relation heapRelation, Oid oldIndexId, const char } /* - * index_concurrently_build + * index_build_concurrent * * Build index for a concurrent operation. Low-level locks are taken when - * this operation is performed to prevent only schema changes, but they need to - * be kept until the end of the transaction performing this operation. + * this operation is performed to prevent only schema changes, but they need + * to be kept until the end of the transaction performing this operation. + * 'indexOid' refers to an index relation OID already created as part of + * previous processing, and 'heapOid' refers to its parent heap relation. */ void -index_concurrently_build(Oid heapOid, - Oid indexOid) +index_build_concurrent(Oid heapOid, Oid indexOid) { Relation heapRel, indexRelation; IndexInfo *indexInfo; + /* This had better make sure that a snapshot is active */ + Assert(ActiveSnapshotSet()); + /* Open and lock the parent heap relation */ heapRel = table_open(heapOid, ShareUpdateExclusiveLock); @@ -1344,13 +1355,13 @@ index_concurrently_build(Oid heapOid, } /* - * index_concurrently_swap + * index_swap_concurrent * * Swap name, dependencies, and constraints of the old index over to the new * index, while marking the old index as invalid and the new as valid. */ void -index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName) +index_swap_concurrent(Oid newIndexId, Oid oldIndexId, const char *oldName) { Relation pg_class, pg_index, @@ -1417,8 +1428,8 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName) newIndexForm = (Form_pg_index) GETSTRUCT(newIndexTuple); /* - * Copy constraint flags for old index. This is safe because the old index - * guaranteed uniqueness. + * Copy conntraint flags from the old index. This is safe because the old + * index guaranteed uniqueness. */ newIndexForm->indisprimary = oldIndexForm->indisprimary; oldIndexForm->indisprimary = false; @@ -1509,7 +1520,7 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName) } /* - * Move comment if any + * Move comment if any. */ { Relation description; @@ -1555,7 +1566,7 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName) } /* - * Move all dependencies on the old index to the new + * Move all dependencies on the old index to the new one. */ if (OidIsValid(indexConstraintOid)) @@ -1592,7 +1603,7 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName) } /* - * index_concurrently_set_dead + * index_set_dead_concurrent * * Perform the last invalidation stage of DROP INDEX CONCURRENTLY or REINDEX * CONCURRENTLY before actually dropping the index. After calling this @@ -1601,7 +1612,7 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName) * function. */ void -index_concurrently_set_dead(Oid heapOid, Oid indexOid) +index_set_dead_concurrent(Oid heapOid, Oid indexOid) { Relation heapRelation, indexRelation; @@ -1638,7 +1649,7 @@ index_concurrently_set_dead(Oid heapOid, Oid indexOid) } /* - * index_concurrently_drop + * index_drop_concurrent * * Drop a single index concurrently as the last step of an index concurrent * process. Deletion is done through performDeletion or dependencies of the @@ -1648,7 +1659,7 @@ index_concurrently_set_dead(Oid heapOid, Oid indexOid) * server sessions. */ void -index_concurrently_drop(Oid indexId) +index_drop_concurrent(Oid indexId) { Oid constraintOid = get_index_constraint(indexId); ObjectAddress object; @@ -2089,7 +2100,7 @@ index_drop(Oid indexId, bool concurrent) WaitForLockers(heaplocktag, AccessExclusiveLock); /* Finish invalidation of index and mark it as dead */ - index_concurrently_set_dead(heapId, indexId); + index_set_dead_concurrent(heapId, indexId); /* * Again, commit the transaction to make the pg_index update visible diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 969b34e752..b5b8f62b19 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -395,7 +395,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId, ObjectAddress objAddr; bool newIsPinned; - depRel = heap_open(DependRelationId, RowExclusiveLock); + depRel = table_open(DependRelationId, RowExclusiveLock); /* * If oldRefObjectId is pinned, there won't be any dependency entries on @@ -838,7 +838,7 @@ get_index_ref_constraints(Oid indexId) HeapTuple tup; /* Search the dependency table for the index */ - depRel = heap_open(DependRelationId, AccessShareLock); + depRel = table_open(DependRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_depend_refclassid, diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index fb93c41c88..77be19175a 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -336,7 +336,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, list_make2("chunk_id", "chunk_seq"), BTREE_AM_OID, rel->rd_rel->reltablespace, - collationObjectId, classObjectId, coloptions, (Datum) 0, NULL, + collationObjectId, classObjectId, coloptions, (Datum) 0, INDEX_CREATE_IS_PRIMARY, 0, true, true, NULL); table_close(toast_rel, NoLock); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index a3b6aed0a7..8a80308c3b 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -305,15 +305,15 @@ CheckIndexCompatible(Oid oldId, * Wait for transactions that might have an older snapshot than the given xmin * limit, because it might not contain tuples deleted just before it has * been taken. Obtain a list of VXIDs of such transactions, and wait for them - * individually. + * individually. This is used when building concurrently an index. * * We can exclude any running transactions that have xmin > the xmin given; * their oldest snapshot must be newer than our xmin limit. * We can also exclude any transactions that have xmin = zero, since they * evidently have no live snapshot at all (and any one they might be in * process of taking is certainly newer than ours). Transactions in other - * DBs can be ignored too, since they'll never even be able to see this - * index. + * DBs can be ignored too, since they'll never even be able to see the + * index worked on. * * We can also exclude autovacuum processes and processes running manual * lazy VACUUMs, because they won't be fazed by missing index entries @@ -937,7 +937,7 @@ DefineIndex(Oid relationId, stmt->oldNode, indexInfo, indexColNames, accessMethodId, tablespaceId, collationObjectId, classObjectId, - coloptions, reloptions, NULL, + coloptions, reloptions, flags, constr_flags, allowSystemTableMods, !check_rights, &createdConstraintId); @@ -1237,10 +1237,7 @@ DefineIndex(Oid relationId, PushActiveSnapshot(GetTransactionSnapshot()); /* Perform concurrent build of index */ - index_concurrently_build(RangeVarGetRelid(stmt->relation, - ShareUpdateExclusiveLock, - false), - indexRelationId); + index_build_concurrent(relationId, indexRelationId); /* we can do away with our snapshot */ PopActiveSnapshot(); @@ -2814,9 +2811,9 @@ ReindexRelationConcurrently(Oid relationOid, int options) false); /* Create new index definition based on given index */ - concurrentOid = index_concurrently_create_copy(indexParentRel, - indOid, - concurrentName); + concurrentOid = index_create_copy_concurrent(indexParentRel, + indOid, + concurrentName); /* Now open the relation of the new index, a lock is also needed on it */ indexConcurrentRel = index_open(concurrentOid, ShareUpdateExclusiveLock); @@ -2851,7 +2848,8 @@ ReindexRelationConcurrently(Oid relationOid, int options) */ foreach(lc, parentRelationIds) { - Relation heapRelation = table_open(lfirst_oid(lc), ShareUpdateExclusiveLock); + Relation heapRelation = table_open(lfirst_oid(lc), + ShareUpdateExclusiveLock); LockRelId lockrelid = heapRelation->rd_lockInfo.lockRelId; LOCKTAG *heaplocktag; @@ -2914,7 +2912,6 @@ ReindexRelationConcurrently(Oid relationOid, int options) Relation indexRel; Oid indOid = lfirst_oid(lc); Oid concurrentOid = lfirst_oid(lc2); - Oid tableOid; CHECK_FOR_INTERRUPTS(); @@ -2926,14 +2923,15 @@ ReindexRelationConcurrently(Oid relationOid, int options) /* * Index relation has been closed by previous commit, so reopen it to - * get its information. + * determine if it is used as a primary key. */ indexRel = index_open(indOid, ShareUpdateExclusiveLock); - tableOid = indexRel->rd_index->indrelid; - index_close(indexRel, NoLock); /* Perform concurrent build of new index */ - index_concurrently_build(tableOid, concurrentOid); + index_build_concurrent(indexRel->rd_index->indrelid, concurrentOid); + + /* Keep lock until the end of this transaction */ + index_close(indexRel, NoLock); /* We can do away with our snapshot */ PopActiveSnapshot(); @@ -3045,7 +3043,7 @@ ReindexRelationConcurrently(Oid relationOid, int options) false); /* Swap old index with the new one */ - index_concurrently_swap(concurrentOid, indOid, oldName); + index_swap_concurrent(concurrentOid, indOid, oldName); /* * Invalidate the relcache for the table, so that after this commit @@ -3090,7 +3088,7 @@ ReindexRelationConcurrently(Oid relationOid, int options) relOid = IndexGetRelation(indOid, false); /* Finish the index invalidation and set it as dead. */ - index_concurrently_set_dead(relOid, indOid); + index_set_dead_concurrent(relOid, indOid); } /* Commit this transaction to make the updates visible. */ @@ -3118,14 +3116,14 @@ ReindexRelationConcurrently(Oid relationOid, int options) CHECK_FOR_INTERRUPTS(); - index_concurrently_drop(indOid); + index_drop_concurrent(indOid); } PopActiveSnapshot(); CommitTransactionCommand(); /* - * Finallt release the session-level lock on the parent table. + * Finally release the session-level lock on the parent table. */ foreach(lc, relationLocks) { diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 84dd900dd6..803fa6a2c8 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -65,7 +65,6 @@ extern Oid index_create(Relation heapRelation, Oid *classObjectId, int16 *coloptions, Datum reloptions, - TupleDesc tupdesc, bits16 flags, bits16 constr_flags, bool allow_system_table_mods, @@ -78,22 +77,6 @@ extern Oid index_create(Relation heapRelation, #define INDEX_CONSTR_CREATE_UPDATE_INDEX (1 << 3) #define INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS (1 << 4) -extern Oid index_concurrently_create_copy(Relation heapRelation, - Oid oldIndexId, - const char *newName); - -extern void index_concurrently_build(Oid heapOid, - Oid indexOid); - -extern void index_concurrently_swap(Oid newIndexId, - Oid oldIndexId, - const char *oldName); - -extern void index_concurrently_set_dead(Oid heapOid, - Oid indexOid); - -extern void index_concurrently_drop(Oid indexId); - extern ObjectAddress index_constraint_create(Relation heapRelation, Oid indexRelationId, Oid parentConstraintId, @@ -127,6 +110,21 @@ extern void index_build(Relation heapRelation, bool isreindex, bool parallel); +extern void index_build_concurrent(Oid heapOid, Oid indexOid); + +extern Oid index_create_copy_concurrent(Relation heapRelation, + Oid oldIndexId, + const char *newName); + +extern void index_swap_concurrent(Oid newIndexId, + Oid oldIndexId, + const char *oldName); + +extern void index_set_dead_concurrent(Oid heapOid, + Oid indexOid); + +extern void index_drop_concurrent(Oid indexId); + extern double IndexBuildHeapScan(Relation heapRelation, Relation indexRelation, IndexInfo *indexInfo,