Skip to content

Commit a03e3e1

Browse files
committed
Prevent incorrect updates of pg_index while reindexing pg_index itself.
The places that attempt to change pg_index.indcheckxmin during a reindexing operation cannot be executed safely if pg_index itself is the subject of the operation. This is the explanation for a couple of recent reports of VACUUM FULL failing with ERROR: duplicate key value violates unique constraint "pg_index_indexrelid_index" DETAIL: Key (indexrelid)=(2678) already exists. However, there isn't any real need to update indcheckxmin in such a situation, if we assume that pg_index can never contain a truly broken HOT chain. This assumption holds if new indexes are never created on it during concurrent operations, which is something we don't consider safe for any system catalog, not just pg_index. Accordingly, modify the code to not manipulate indcheckxmin when reindexing any system catalog. Back-patch to 8.3, where HOT was introduced. The known failure scenarios involve 9.0-style VACUUM FULL, so there might not be any real risk before 9.0, but let's not assume that.
1 parent c1d4238 commit a03e3e1

File tree

1 file changed

+24
-4
lines changed

1 file changed

+24
-4
lines changed

src/backend/catalog/index.c

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,13 @@ index_build(Relation heapRelation,
14621462
HeapTuple indexTuple;
14631463
Form_pg_index indexForm;
14641464

1465+
/*
1466+
* Broken HOT chains should not get reported in system catalogs; in
1467+
* particular it would be quite dangerous to try to modify the index's
1468+
* pg_index entry if we are reindexing pg_index itself.
1469+
*/
1470+
Assert(!IsSystemRelation(heapRelation));
1471+
14651472
pg_index = heap_open(IndexRelationId, RowExclusiveLock);
14661473

14671474
indexTuple = SearchSysCacheCopy1(INDEXRELID,
@@ -1521,7 +1528,13 @@ index_build(Relation heapRelation,
15211528
* A side effect is to set indexInfo->ii_BrokenHotChain to true if we detect
15221529
* any potentially broken HOT chains. Currently, we set this if there are
15231530
* any RECENTLY_DEAD entries in a HOT chain, without trying very hard to
1524-
* detect whether they're really incompatible with the chain tip.
1531+
* detect whether they're really incompatible with the chain tip. However,
1532+
* we do not ever set ii_BrokenHotChain true when the relation is a system
1533+
* catalog. This is to avoid problematic behavior when reindexing pg_index
1534+
* itself: we can't safely change the index's indcheckxmin field when we're
1535+
* partway through such an operation. It should be okay since the set of
1536+
* indexes on a system catalog ought not change during concurrent operations,
1537+
* so that no HOT chain in it could ever become broken.
15251538
*/
15261539
double
15271540
IndexBuildHeapScan(Relation heapRelation,
@@ -1698,7 +1711,8 @@ IndexBuildHeapScan(Relation heapRelation,
16981711
{
16991712
indexIt = false;
17001713
/* mark the index as unsafe for old snapshots */
1701-
indexInfo->ii_BrokenHotChain = true;
1714+
if (!is_system_catalog)
1715+
indexInfo->ii_BrokenHotChain = true;
17021716
}
17031717
else if (indexInfo->ii_BrokenHotChain)
17041718
indexIt = false;
@@ -1786,7 +1800,8 @@ IndexBuildHeapScan(Relation heapRelation,
17861800
{
17871801
indexIt = false;
17881802
/* mark the index as unsafe for old snapshots */
1789-
indexInfo->ii_BrokenHotChain = true;
1803+
if (!is_system_catalog)
1804+
indexInfo->ii_BrokenHotChain = true;
17901805
}
17911806
else if (indexInfo->ii_BrokenHotChain)
17921807
indexIt = false;
@@ -2481,8 +2496,13 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
24812496
* We can also reset indcheckxmin, because we have now done a
24822497
* non-concurrent index build, *except* in the case where index_build
24832498
* found some still-broken HOT chains.
2499+
*
2500+
* When reindexing a system catalog, don't do any of this --- it would be
2501+
* particularly risky to try to modify pg_index while we are reindexing
2502+
* pg_index itself. We don't support CREATE INDEX CONCURRENTLY on system
2503+
* catalogs anyway, and they should never have indcheckxmin set either.
24842504
*/
2485-
if (!skipped_constraint)
2505+
if (!skipped_constraint && !IsSystemRelation(heapRelation))
24862506
{
24872507
pg_index = heap_open(IndexRelationId, RowExclusiveLock);
24882508

0 commit comments

Comments
 (0)