Skip to content

Commit 5f8e84f

Browse files
committed
Fix reindexing of pg_class indexes some more.
Commits 3dbb317 et al failed under CLOBBER_CACHE_ALWAYS testing. Investigation showed that to reindex pg_class_oid_index, we must suppress accesses to the index (via SetReindexProcessing) before we call RelationSetNewRelfilenode, or at least before we do CommandCounterIncrement therein; otherwise, relcache reloads happening within the CCI may try to fetch pg_class rows using the index's new relfilenode value, which is as yet an empty file. Of course, the point of 3dbb317 was that that ordering didn't work either, because then RelationSetNewRelfilenode's own update of the index's pg_class row cannot access the index, should it need to. There are various ways we might have got around that, but Andres Freund came up with a brilliant solution: for a mapped index, we can really just skip the pg_class update altogether. The only fields it was actually changing were relpages etc, but it was just setting them to zeroes which is useless make-work. (Correct new values will be installed at the end of index build.) All pg_class indexes are mapped and probably always will be, so this eliminates the problem by removing work rather than adding it, always a pleasant outcome. Having taught RelationSetNewRelfilenode to do it that way, we can revert the code reordering in reindex_index. (But I left the moved setup code where it was; there seems no reason why it has to run without use of the old index. If you're trying to fix a busted pg_class index, you'll have had to disable system index use altogether to get this far.) Moreover, this means we don't need RelationSetIndexList at all, because reindex_relation's hacking to make "REINDEX TABLE pg_class" work is likewise now unnecessary. We'll leave that code in place in the back branches, but a follow-on patch will remove it in HEAD. In passing, do some minor cleanup for commit 5c15606 (in HEAD only), notably removing a duplicate newrnode assignment. Patch by me, using a core idea due to Andres Freund. Back-patch to all supported branches, as 3dbb317 was. Discussion: https://postgr.es/m/28926.1556664156@sss.pgh.pa.us
1 parent 856bc0c commit 5f8e84f

File tree

2 files changed

+48
-65
lines changed

2 files changed

+48
-65
lines changed

src/backend/catalog/index.c

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3157,21 +3157,16 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
31573157
indexInfo->ii_ExclusionStrats = NULL;
31583158
}
31593159

3160-
/*
3161-
* Build a new physical relation for the index. Need to do that before
3162-
* "officially" starting the reindexing with SetReindexProcessing -
3163-
* otherwise the necessary pg_class changes cannot be made with
3164-
* encountering assertions.
3165-
*/
3166-
RelationSetNewRelfilenode(iRel, InvalidTransactionId,
3167-
InvalidMultiXactId);
3168-
31693160
/* ensure SetReindexProcessing state isn't leaked */
31703161
PG_TRY();
31713162
{
31723163
/* Suppress use of the target index while rebuilding it */
31733164
SetReindexProcessing(heapId, indexId);
31743165

3166+
/* Create a new physical relation for the index */
3167+
RelationSetNewRelfilenode(iRel, InvalidTransactionId,
3168+
InvalidMultiXactId);
3169+
31753170
/* Initialize the index and rebuild */
31763171
/* Note: we do not need to re-establish pkey setting */
31773172
index_build(heapRelation, iRel, indexInfo, false, true);
@@ -3297,7 +3292,6 @@ reindex_relation(Oid relid, int flags)
32973292
Relation rel;
32983293
Oid toast_relid;
32993294
List *indexIds;
3300-
bool is_pg_class;
33013295
bool result;
33023296

33033297
/*
@@ -3316,37 +3310,8 @@ reindex_relation(Oid relid, int flags)
33163310
*/
33173311
indexIds = RelationGetIndexList(rel);
33183312

3319-
/*
3320-
* reindex_index will attempt to update the pg_class rows for the relation
3321-
* and index. If we are processing pg_class itself, we want to make sure
3322-
* that the updates do not try to insert index entries into indexes we
3323-
* have not processed yet. (When we are trying to recover from corrupted
3324-
* indexes, that could easily cause a crash.) We can accomplish this
3325-
* because CatalogUpdateIndexes will use the relcache's index list to know
3326-
* which indexes to update. We just force the index list to be only the
3327-
* stuff we've processed.
3328-
*
3329-
* It is okay to not insert entries into the indexes we have not processed
3330-
* yet because all of this is transaction-safe. If we fail partway
3331-
* through, the updated rows are dead and it doesn't matter whether they
3332-
* have index entries. Also, a new pg_class index will be created with a
3333-
* correct entry for its own pg_class row because we do
3334-
* RelationSetNewRelfilenode() before we do index_build().
3335-
*
3336-
* Note that we also clear pg_class's rd_oidindex until the loop is done,
3337-
* so that that index can't be accessed either. This means we cannot
3338-
* safely generate new relation OIDs while in the loop; shouldn't be a
3339-
* problem.
3340-
*/
3341-
is_pg_class = (RelationGetRelid(rel) == RelationRelationId);
3342-
3343-
/* Ensure rd_indexattr is valid; see comments for RelationSetIndexList */
3344-
if (is_pg_class)
3345-
(void) RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_ALL);
3346-
33473313
PG_TRY();
33483314
{
3349-
List *doneIndexes;
33503315
ListCell *indexId;
33513316

33523317
if (flags & REINDEX_REL_SUPPRESS_INDEX_USE)
@@ -3362,23 +3327,16 @@ reindex_relation(Oid relid, int flags)
33623327
}
33633328

33643329
/* Reindex all the indexes. */
3365-
doneIndexes = NIL;
33663330
foreach(indexId, indexIds)
33673331
{
33683332
Oid indexOid = lfirst_oid(indexId);
33693333

3370-
if (is_pg_class)
3371-
RelationSetIndexList(rel, doneIndexes, InvalidOid);
3372-
33733334
reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS));
33743335

33753336
CommandCounterIncrement();
33763337

33773338
/* Index should no longer be in the pending list */
33783339
Assert(!ReindexIsProcessingIndex(indexOid));
3379-
3380-
if (is_pg_class)
3381-
doneIndexes = lappend_oid(doneIndexes, indexOid);
33823340
}
33833341
}
33843342
PG_CATCH();
@@ -3390,9 +3348,6 @@ reindex_relation(Oid relid, int flags)
33903348
PG_END_TRY();
33913349
ResetReindexPending();
33923350

3393-
if (is_pg_class)
3394-
RelationSetIndexList(rel, indexIds, ClassOidIndexId);
3395-
33963351
/*
33973352
* Close rel, but continue to hold the lock.
33983353
*/

src/backend/utils/cache/relcache.c

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3069,38 +3069,66 @@ RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid,
30693069
RelationDropStorage(relation);
30703070

30713071
/*
3072-
* Now update the pg_class row. However, if we're dealing with a mapped
3073-
* index, pg_class.relfilenode doesn't change; instead we have to send the
3074-
* update to the relation mapper.
3072+
* If we're dealing with a mapped index, pg_class.relfilenode doesn't
3073+
* change; instead we have to send the update to the relation mapper.
3074+
*
3075+
* For mapped indexes, we don't actually change the pg_class entry at all;
3076+
* this is essential when reindexing pg_class itself. That leaves us with
3077+
* possibly-inaccurate values of relpages etc, but those will be fixed up
3078+
* later.
30753079
*/
30763080
if (RelationIsMapped(relation))
3081+
{
3082+
/* This case is only supported for indexes */
3083+
Assert(relation->rd_rel->relkind == RELKIND_INDEX);
3084+
3085+
/* Since we're not updating pg_class, these had better not change */
3086+
Assert(classform->relfrozenxid == freezeXid);
3087+
Assert(classform->relminmxid == minmulti);
3088+
3089+
/*
3090+
* In some code paths it's possible that the tuple update we'd
3091+
* otherwise do here is the only thing that would assign an XID for
3092+
* the current transaction. However, we must have an XID to delete
3093+
* files, so make sure one is assigned.
3094+
*/
3095+
(void) GetCurrentTransactionId();
3096+
3097+
/* Do the deed */
30773098
RelationMapUpdateMap(RelationGetRelid(relation),
30783099
newrelfilenode,
30793100
relation->rd_rel->relisshared,
30803101
false);
3102+
3103+
/* Since we're not updating pg_class, must trigger inval manually */
3104+
CacheInvalidateRelcache(relation);
3105+
}
30813106
else
3107+
{
3108+
/* Normal case, update the pg_class entry */
30823109
classform->relfilenode = newrelfilenode;
30833110

3084-
/* These changes are safe even for a mapped relation */
3085-
if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
3086-
{
3087-
classform->relpages = 0; /* it's empty until further notice */
3088-
classform->reltuples = 0;
3089-
classform->relallvisible = 0;
3090-
}
3091-
classform->relfrozenxid = freezeXid;
3092-
classform->relminmxid = minmulti;
3111+
/* relpages etc. never change for sequences */
3112+
if (relation->rd_rel->relkind != RELKIND_SEQUENCE)
3113+
{
3114+
classform->relpages = 0; /* it's empty until further notice */
3115+
classform->reltuples = 0;
3116+
classform->relallvisible = 0;
3117+
}
3118+
classform->relfrozenxid = freezeXid;
3119+
classform->relminmxid = minmulti;
30933120

3094-
simple_heap_update(pg_class, &tuple->t_self, tuple);
3095-
CatalogUpdateIndexes(pg_class, tuple);
3121+
simple_heap_update(pg_class, &tuple->t_self, tuple);
3122+
CatalogUpdateIndexes(pg_class, tuple);
3123+
}
30963124

30973125
heap_freetuple(tuple);
30983126

30993127
heap_close(pg_class, RowExclusiveLock);
31003128

31013129
/*
3102-
* Make the pg_class row change visible, as well as the relation map
3103-
* change if any. This will cause the relcache entry to get updated, too.
3130+
* Make the pg_class row change or relation map change visible. This will
3131+
* cause the relcache entry to get updated, too.
31043132
*/
31053133
CommandCounterIncrement();
31063134

0 commit comments

Comments
 (0)