Skip to content

Commit 56259c3

Browse files
committed
Fix possible crash during FATAL exit from reindexing.
index.c supposed that it could just use a PG_TRY block to clean up the state associated with an active REINDEX operation. However, that code doesn't run if we do a FATAL exit --- for example, due to a SIGTERM shutdown signal --- while the REINDEX is happening. And that state does get consulted during catalog accesses, which makes it problematic if we do any catalog accesses during shutdown --- for example, to clean up any temp tables created in the session. If this combination of circumstances occurred, we could find ourselves trying to access already-freed memory. In debug builds that'd fairly reliably cause an assertion failure. In production we might often get away with it, but with some bad luck it could cause a core dump. Another possible bad outcome is an erroneous conclusion that an index-to-be-accessed is being reindexed; but it looks like that would be unlikely to have any consequences worse than failing to drop temp tables right away. (They'd still get dropped by the next session that uses that temp schema.) To fix, get rid of the use of PG_TRY here, and instead hook into the transaction abort mechanisms to clean up reindex state. Per bug #16378 from Alexander Lakhin. This has been wrong for a very long time, so back-patch to all supported branches. Discussion: https://postgr.es/m/16378-7a70ca41b3ec2009@postgresql.org
1 parent 6ab98db commit 56259c3

File tree

3 files changed

+82
-73
lines changed

3 files changed

+82
-73
lines changed

src/backend/access/transam/xact.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "access/xlog.h"
3131
#include "access/xloginsert.h"
3232
#include "access/xlogutils.h"
33+
#include "catalog/index.h"
3334
#include "catalog/namespace.h"
3435
#include "catalog/storage.h"
3536
#include "commands/async.h"
@@ -2576,6 +2577,9 @@ AbortTransaction(void)
25762577
*/
25772578
SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
25782579

2580+
/* Forget about any active REINDEX. */
2581+
ResetReindexState(s->nestingLevel);
2582+
25792583
/* If in parallel mode, clean up workers and exit parallel mode. */
25802584
if (IsInParallelMode())
25812585
{
@@ -4785,6 +4789,9 @@ AbortSubTransaction(void)
47854789
*/
47864790
SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
47874791

4792+
/* Forget about any active REINDEX. */
4793+
ResetReindexState(s->nestingLevel);
4794+
47884795
/* Exit from parallel mode, if necessary. */
47894796
if (IsInParallelMode())
47904797
{

src/backend/catalog/index.c

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,6 @@ static void SetReindexProcessing(Oid heapOid, Oid indexOid);
144144
static void ResetReindexProcessing(void);
145145
static void SetReindexPending(List *indexes);
146146
static void RemoveReindexPending(Oid indexOid);
147-
static void ResetReindexPending(void);
148147

149148

150149
/*
@@ -3799,27 +3798,18 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
37993798
indexInfo->ii_ExclusionStrats = NULL;
38003799
}
38013800

3802-
/* ensure SetReindexProcessing state isn't leaked */
3803-
PG_TRY();
3804-
{
3805-
/* Suppress use of the target index while rebuilding it */
3806-
SetReindexProcessing(heapId, indexId);
3801+
/* Suppress use of the target index while rebuilding it */
3802+
SetReindexProcessing(heapId, indexId);
38073803

3808-
/* Create a new physical relation for the index */
3809-
RelationSetNewRelfilenode(iRel, persistence, InvalidTransactionId,
3810-
InvalidMultiXactId);
3804+
/* Create a new physical relation for the index */
3805+
RelationSetNewRelfilenode(iRel, persistence, InvalidTransactionId,
3806+
InvalidMultiXactId);
38113807

3812-
/* Initialize the index and rebuild */
3813-
/* Note: we do not need to re-establish pkey setting */
3814-
index_build(heapRelation, iRel, indexInfo, false, true, true);
3815-
}
3816-
PG_CATCH();
3817-
{
3818-
/* Make sure flag gets cleared on error exit */
3819-
ResetReindexProcessing();
3820-
PG_RE_THROW();
3821-
}
3822-
PG_END_TRY();
3808+
/* Initialize the index and rebuild */
3809+
/* Note: we do not need to re-establish pkey setting */
3810+
index_build(heapRelation, iRel, indexInfo, false, true, true);
3811+
3812+
/* Re-allow use of target index */
38233813
ResetReindexProcessing();
38243814

38253815
/*
@@ -3954,7 +3944,9 @@ reindex_relation(Oid relid, int flags, int options)
39543944
Relation rel;
39553945
Oid toast_relid;
39563946
List *indexIds;
3947+
char persistence;
39573948
bool result;
3949+
ListCell *indexId;
39583950

39593951
/*
39603952
* Open and lock the relation. ShareLock is sufficient since we only need
@@ -3988,56 +3980,42 @@ reindex_relation(Oid relid, int flags, int options)
39883980
*/
39893981
indexIds = RelationGetIndexList(rel);
39903982

3991-
PG_TRY();
3983+
if (flags & REINDEX_REL_SUPPRESS_INDEX_USE)
39923984
{
3993-
ListCell *indexId;
3994-
char persistence;
3995-
3996-
if (flags & REINDEX_REL_SUPPRESS_INDEX_USE)
3997-
{
3998-
/* Suppress use of all the indexes until they are rebuilt */
3999-
SetReindexPending(indexIds);
4000-
4001-
/*
4002-
* Make the new heap contents visible --- now things might be
4003-
* inconsistent!
4004-
*/
4005-
CommandCounterIncrement();
4006-
}
3985+
/* Suppress use of all the indexes until they are rebuilt */
3986+
SetReindexPending(indexIds);
40073987

40083988
/*
4009-
* Compute persistence of indexes: same as that of owning rel, unless
4010-
* caller specified otherwise.
3989+
* Make the new heap contents visible --- now things might be
3990+
* inconsistent!
40113991
*/
4012-
if (flags & REINDEX_REL_FORCE_INDEXES_UNLOGGED)
4013-
persistence = RELPERSISTENCE_UNLOGGED;
4014-
else if (flags & REINDEX_REL_FORCE_INDEXES_PERMANENT)
4015-
persistence = RELPERSISTENCE_PERMANENT;
4016-
else
4017-
persistence = rel->rd_rel->relpersistence;
3992+
CommandCounterIncrement();
3993+
}
40183994

4019-
/* Reindex all the indexes. */
4020-
foreach(indexId, indexIds)
4021-
{
4022-
Oid indexOid = lfirst_oid(indexId);
3995+
/*
3996+
* Compute persistence of indexes: same as that of owning rel, unless
3997+
* caller specified otherwise.
3998+
*/
3999+
if (flags & REINDEX_REL_FORCE_INDEXES_UNLOGGED)
4000+
persistence = RELPERSISTENCE_UNLOGGED;
4001+
else if (flags & REINDEX_REL_FORCE_INDEXES_PERMANENT)
4002+
persistence = RELPERSISTENCE_PERMANENT;
4003+
else
4004+
persistence = rel->rd_rel->relpersistence;
40234005

4024-
reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS),
4025-
persistence, options);
4006+
/* Reindex all the indexes. */
4007+
foreach(indexId, indexIds)
4008+
{
4009+
Oid indexOid = lfirst_oid(indexId);
40264010

4027-
CommandCounterIncrement();
4011+
reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS),
4012+
persistence, options);
40284013

4029-
/* Index should no longer be in the pending list */
4030-
Assert(!ReindexIsProcessingIndex(indexOid));
4031-
}
4032-
}
4033-
PG_CATCH();
4034-
{
4035-
/* Make sure list gets cleared on error exit */
4036-
ResetReindexPending();
4037-
PG_RE_THROW();
4014+
CommandCounterIncrement();
4015+
4016+
/* Index should no longer be in the pending list */
4017+
Assert(!ReindexIsProcessingIndex(indexOid));
40384018
}
4039-
PG_END_TRY();
4040-
ResetReindexPending();
40414019

40424020
/*
40434021
* Close rel, but continue to hold the lock.
@@ -4071,6 +4049,7 @@ reindex_relation(Oid relid, int flags, int options)
40714049
static Oid currentlyReindexedHeap = InvalidOid;
40724050
static Oid currentlyReindexedIndex = InvalidOid;
40734051
static List *pendingReindexedIndexes = NIL;
4052+
static int reindexingNestLevel = 0;
40744053

40754054
/*
40764055
* ReindexIsProcessingHeap
@@ -4107,8 +4086,6 @@ ReindexIsProcessingIndex(Oid indexOid)
41074086
/*
41084087
* SetReindexProcessing
41094088
* Set flag that specified heap/index are being reindexed.
4110-
*
4111-
* NB: caller must use a PG_TRY block to ensure ResetReindexProcessing is done.
41124089
*/
41134090
static void
41144091
SetReindexProcessing(Oid heapOid, Oid indexOid)
@@ -4121,6 +4098,8 @@ SetReindexProcessing(Oid heapOid, Oid indexOid)
41214098
currentlyReindexedIndex = indexOid;
41224099
/* Index is no longer "pending" reindex. */
41234100
RemoveReindexPending(indexOid);
4101+
/* This may have been set already, but in case it isn't, do so now. */
4102+
reindexingNestLevel = GetCurrentTransactionNestLevel();
41244103
}
41254104

41264105
/*
@@ -4130,17 +4109,16 @@ SetReindexProcessing(Oid heapOid, Oid indexOid)
41304109
static void
41314110
ResetReindexProcessing(void)
41324111
{
4133-
/* This may be called in leader error path */
41344112
currentlyReindexedHeap = InvalidOid;
41354113
currentlyReindexedIndex = InvalidOid;
4114+
/* reindexingNestLevel remains set till end of (sub)transaction */
41364115
}
41374116

41384117
/*
41394118
* SetReindexPending
41404119
* Mark the given indexes as pending reindex.
41414120
*
4142-
* NB: caller must use a PG_TRY block to ensure ResetReindexPending is done.
4143-
* Also, we assume that the current memory context stays valid throughout.
4121+
* NB: we assume that the current memory context stays valid throughout.
41444122
*/
41454123
static void
41464124
SetReindexPending(List *indexes)
@@ -4151,6 +4129,7 @@ SetReindexPending(List *indexes)
41514129
if (IsInParallelMode())
41524130
elog(ERROR, "cannot modify reindex state during a parallel operation");
41534131
pendingReindexedIndexes = list_copy(indexes);
4132+
reindexingNestLevel = GetCurrentTransactionNestLevel();
41544133
}
41554134

41564135
/*
@@ -4167,14 +4146,32 @@ RemoveReindexPending(Oid indexOid)
41674146
}
41684147

41694148
/*
4170-
* ResetReindexPending
4171-
* Unset reindex-pending status.
4149+
* ResetReindexState
4150+
* Clear all reindexing state during (sub)transaction abort.
41724151
*/
4173-
static void
4174-
ResetReindexPending(void)
4152+
void
4153+
ResetReindexState(int nestLevel)
41754154
{
4176-
/* This may be called in leader error path */
4177-
pendingReindexedIndexes = NIL;
4155+
/*
4156+
* Because reindexing is not re-entrant, we don't need to cope with nested
4157+
* reindexing states. We just need to avoid messing up the outer-level
4158+
* state in case a subtransaction fails within a REINDEX. So checking the
4159+
* current nest level against that of the reindex operation is sufficient.
4160+
*/
4161+
if (reindexingNestLevel >= nestLevel)
4162+
{
4163+
currentlyReindexedHeap = InvalidOid;
4164+
currentlyReindexedIndex = InvalidOid;
4165+
4166+
/*
4167+
* We needn't try to release the contents of pendingReindexedIndexes;
4168+
* that list should be in a transaction-lifespan context, so it will
4169+
* go away automatically.
4170+
*/
4171+
pendingReindexedIndexes = NIL;
4172+
4173+
reindexingNestLevel = 0;
4174+
}
41784175
}
41794176

41804177
/*
@@ -4227,4 +4224,7 @@ RestoreReindexState(void *reindexstate)
42274224
lappend_oid(pendingReindexedIndexes,
42284225
sistate->pendingReindexedIndexes[c]);
42294226
MemoryContextSwitchTo(oldcontext);
4227+
4228+
/* Note the worker has its own transaction nesting level */
4229+
reindexingNestLevel = GetCurrentTransactionNestLevel();
42304230
}

src/include/catalog/index.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot);
135135

136136
extern void index_set_state_flags(Oid indexId, IndexStateFlagsAction action);
137137

138+
extern Oid IndexGetRelation(Oid indexId, bool missing_ok);
139+
138140
extern void reindex_index(Oid indexId, bool skip_constraint_checks,
139141
char relpersistence, int options);
140142

@@ -149,8 +151,8 @@ extern bool reindex_relation(Oid relid, int flags, int options);
149151

150152
extern bool ReindexIsProcessingHeap(Oid heapOid);
151153
extern bool ReindexIsProcessingIndex(Oid indexOid);
152-
extern Oid IndexGetRelation(Oid indexId, bool missing_ok);
153154

155+
extern void ResetReindexState(int nestLevel);
154156
extern Size EstimateReindexStateSpace(void);
155157
extern void SerializeReindexState(Size maxsize, char *start_address);
156158
extern void RestoreReindexState(void *reindexstate);

0 commit comments

Comments
 (0)