On Thu, Apr 18, 2019 at 10:14:30AM +0900, Michael Paquier wrote:
> Doing a REINDEX TABLE directly on pg_class proves to work correctly,
> and CONCURRENTLY is not supported for catalog tables.
> 
> Bisecting my way through it, the first commit causing the breakage is
> that:
> commit: 01e386a325549b7755739f31308de4be8eea110d
> author: Tom Lane <t...@sss.pgh.pa.us>
> date: Wed, 23 Dec 2015 20:09:01 -0500
> Avoid VACUUM FULL altogether in initdb.

This brings down to a first, simple, solution which is to issue a
VACUUM FULL on pg_class at the end of make_template0() in initdb.c to
avoid any subsequent problems if trying to issue a REINDEX on anything
related to pg_class, and it won't fix any existing deployments:
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2042,6 +2042,11 @@ make_template0(FILE *cmdfd)
                 * Finally vacuum to clean up dead rows in pg_database
                 */
                "VACUUM pg_database;\n\n",
+
+               /*
+                * And rebuild pg_class.
+                */
+               "VACUUM FULL pg_class;\n\n",
                NULL
        };
Now...

> The reason why this does not work is that CatalogIndexInsert() tries
> to do an index_insert directly on the index worked on.  And the reason
> why this works at table level is that we have tweaks in
> reindex_relation() to enforce the list of valid indexes in the
> relation cache with RelationSetIndexList().  It seems to me that the
> logic in reindex_index() is wrong from the start, and that all the
> index list handling done in reindex_relation() should just be in
> reindex_index() so as REINDEX INDEX gets the right call.

I got to wonder if this dance with the relation cache is actually
necessary, because we could directly tell CatalogIndexInsert() to not
insert a tuple into an index which is bring rebuilt, and the index
rebuild would cause an entry to be added to pg_class anyway thanks to
RelationSetNewRelfilenode().  This can obviously only happen for
pg_class indexes.

Any thoughts about both approaches?
--
Michael
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index e9399bef14..d4284ba08a 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -3502,7 +3502,6 @@ reindex_relation(Oid relid, int flags, int options)
 	Relation	rel;
 	Oid			toast_relid;
 	List	   *indexIds;
-	bool		is_pg_class;
 	bool		result;
 	int			i;
 
@@ -3538,32 +3537,8 @@ reindex_relation(Oid relid, int flags, int options)
 	 */
 	indexIds = RelationGetIndexList(rel);
 
-	/*
-	 * reindex_index will attempt to update the pg_class rows for the relation
-	 * and index.  If we are processing pg_class itself, we want to make sure
-	 * that the updates do not try to insert index entries into indexes we
-	 * have not processed yet.  (When we are trying to recover from corrupted
-	 * indexes, that could easily cause a crash.) We can accomplish this
-	 * because CatalogTupleInsert/CatalogTupleUpdate will use the relcache's
-	 * index list to know which indexes to update. We just force the index
-	 * list to be only the stuff we've processed.
-	 *
-	 * It is okay to not insert entries into the indexes we have not processed
-	 * yet because all of this is transaction-safe.  If we fail partway
-	 * through, the updated rows are dead and it doesn't matter whether they
-	 * have index entries.  Also, a new pg_class index will be created with a
-	 * correct entry for its own pg_class row because we do
-	 * RelationSetNewRelfilenode() before we do index_build().
-	 */
-	is_pg_class = (RelationGetRelid(rel) == RelationRelationId);
-
-	/* Ensure rd_indexattr is valid; see comments for RelationSetIndexList */
-	if (is_pg_class)
-		(void) RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_ALL);
-
 	PG_TRY();
 	{
-		List	   *doneIndexes;
 		ListCell   *indexId;
 		char		persistence;
 
@@ -3591,15 +3566,11 @@ reindex_relation(Oid relid, int flags, int options)
 			persistence = rel->rd_rel->relpersistence;
 
 		/* Reindex all the indexes. */
-		doneIndexes = NIL;
 		i = 1;
 		foreach(indexId, indexIds)
 		{
 			Oid			indexOid = lfirst_oid(indexId);
 
-			if (is_pg_class)
-				RelationSetIndexList(rel, doneIndexes);
-
 			reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS),
 						  persistence, options);
 
@@ -3608,9 +3579,6 @@ reindex_relation(Oid relid, int flags, int options)
 			/* Index should no longer be in the pending list */
 			Assert(!ReindexIsProcessingIndex(indexOid));
 
-			if (is_pg_class)
-				doneIndexes = lappend_oid(doneIndexes, indexOid);
-
 			/* Set index rebuild count */
 			pgstat_progress_update_param(PROGRESS_CLUSTER_INDEX_REBUILD_COUNT,
 										 i);
@@ -3626,9 +3594,6 @@ reindex_relation(Oid relid, int flags, int options)
 	PG_END_TRY();
 	ResetReindexPending();
 
-	if (is_pg_class)
-		RelationSetIndexList(rel, indexIds);
-
 	/*
 	 * Close rel, but continue to hold the lock.
 	 */
diff --git a/src/backend/catalog/indexing.c b/src/backend/catalog/indexing.c
index 0c994122d8..d102fcecae 100644
--- a/src/backend/catalog/indexing.c
+++ b/src/backend/catalog/indexing.c
@@ -114,6 +114,22 @@ CatalogIndexInsert(CatalogIndexState indstate, HeapTuple heapTuple)
 		if (!indexInfo->ii_ReadyForInserts)
 			continue;
 
+		/*
+		 * If trying to insert a tuple for an index which is being rebuilt,
+		 * ignore it.  This can happen only for an index related to pg_class.
+		 *
+		 * A REINDEX being transaction-safe, it is fine to fail partway as the
+		 * inserted rows are dead and it doesn't matter whether they have index
+		 * entries.  A new pg_class index will be created with a correct entry
+		 * for its own pg_class row because we do RelationSetNewRelfilenode()
+		 * before we do index_build() in reindex_index().
+		 */
+		if (ReindexIsProcessingIndex(RelationGetRelid(relationDescs[i])))
+		{
+			Assert(RelationGetRelid(heapRelation) == RelationRelationId);
+			continue;
+		}
+
 		/*
 		 * Expressional and partial indexes on system catalogs are not
 		 * supported, nor exclusion constraints, nor deferred uniqueness
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index bab59f16e6..e5cb9eb916 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -4544,52 +4544,6 @@ insert_ordered_oid(List *list, Oid datum)
 	return list;
 }
 
-/*
- * RelationSetIndexList -- externally force the index list contents
- *
- * This is used to temporarily override what we think the set of valid
- * indexes is (including the presence or absence of an OID index).
- * The forcing will be valid only until transaction commit or abort.
- *
- * This should only be applied to nailed relations, because in a non-nailed
- * relation the hacked index list could be lost at any time due to SI
- * messages.  In practice it is only used on pg_class (see REINDEX).
- *
- * It is up to the caller to make sure the given list is correctly ordered.
- *
- * We deliberately do not change rd_indexattr here: even when operating
- * with a temporary partial index list, HOT-update decisions must be made
- * correctly with respect to the full index set.  It is up to the caller
- * to ensure that a correct rd_indexattr set has been cached before first
- * calling RelationSetIndexList; else a subsequent inquiry might cause a
- * wrong rd_indexattr set to get computed and cached.  Likewise, we do not
- * touch rd_keyattr, rd_pkattr or rd_idattr.
- */
-void
-RelationSetIndexList(Relation relation, List *indexIds)
-{
-	MemoryContext oldcxt;
-
-	Assert(relation->rd_isnailed);
-	/* Copy the list into the cache context (could fail for lack of mem) */
-	oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
-	indexIds = list_copy(indexIds);
-	MemoryContextSwitchTo(oldcxt);
-	/* Okay to replace old list */
-	list_free(relation->rd_indexlist);
-	relation->rd_indexlist = indexIds;
-
-	/*
-	 * For the moment, assume the target rel hasn't got a pk or replica index.
-	 * We'll load them on demand in the API that wraps access to them.
-	 */
-	relation->rd_pkindex = InvalidOid;
-	relation->rd_replidindex = InvalidOid;
-	relation->rd_indexvalid = 2;	/* mark list as forced */
-	/* Flag relation as needing eoxact cleanup (to reset the list) */
-	EOXactListAdd(relation);
-}
-
 /*
  * RelationGetPrimaryKeyIndex -- get OID of the relation's primary key index
  *
diff --git a/src/include/utils/relcache.h b/src/include/utils/relcache.h
index 809d6aa123..364495a5f0 100644
--- a/src/include/utils/relcache.h
+++ b/src/include/utils/relcache.h
@@ -66,9 +66,6 @@ extern void RelationGetExclusionInfo(Relation indexRelation,
 						 Oid **procs,
 						 uint16 **strategies);
 
-extern void RelationSetIndexList(Relation relation,
-					 List *indexIds);
-
 extern void RelationInitIndexAccessInfo(Relation relation);
 
 /* caller must include pg_publication.h */

Attachment: signature.asc
Description: PGP signature

Reply via email to