Commit d12bdba7 authored by Tom Lane's avatar Tom Lane

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
parent 5836d326
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "access/xlog.h" #include "access/xlog.h"
#include "access/xloginsert.h" #include "access/xloginsert.h"
#include "access/xlogutils.h" #include "access/xlogutils.h"
#include "catalog/index.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_enum.h" #include "catalog/pg_enum.h"
#include "catalog/storage.h" #include "catalog/storage.h"
...@@ -2662,6 +2663,9 @@ AbortTransaction(void) ...@@ -2662,6 +2663,9 @@ AbortTransaction(void)
*/ */
SetUserIdAndSecContext(s->prevUser, s->prevSecContext); SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
/* Forget about any active REINDEX. */
ResetReindexState(s->nestingLevel);
/* If in parallel mode, clean up workers and exit parallel mode. */ /* If in parallel mode, clean up workers and exit parallel mode. */
if (IsInParallelMode()) if (IsInParallelMode())
{ {
...@@ -4961,6 +4965,9 @@ AbortSubTransaction(void) ...@@ -4961,6 +4965,9 @@ AbortSubTransaction(void)
*/ */
SetUserIdAndSecContext(s->prevUser, s->prevSecContext); SetUserIdAndSecContext(s->prevUser, s->prevSecContext);
/* Forget about any active REINDEX. */
ResetReindexState(s->nestingLevel);
/* Exit from parallel mode, if necessary. */ /* Exit from parallel mode, if necessary. */
if (IsInParallelMode()) if (IsInParallelMode())
{ {
......
...@@ -131,7 +131,6 @@ static void SetReindexProcessing(Oid heapOid, Oid indexOid); ...@@ -131,7 +131,6 @@ static void SetReindexProcessing(Oid heapOid, Oid indexOid);
static void ResetReindexProcessing(void); static void ResetReindexProcessing(void);
static void SetReindexPending(List *indexes); static void SetReindexPending(List *indexes);
static void RemoveReindexPending(Oid indexOid); static void RemoveReindexPending(Oid indexOid);
static void ResetReindexPending(void);
/* /*
...@@ -1543,8 +1542,8 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName) ...@@ -1543,8 +1542,8 @@ index_concurrently_swap(Oid newIndexId, Oid oldIndexId, const char *oldName)
newIndexForm->indisclustered = oldIndexForm->indisclustered; newIndexForm->indisclustered = oldIndexForm->indisclustered;
/* /*
* Mark the new index as valid, and the old index as invalid similarly * Mark the new index as valid, and the old index as invalid similarly to
* to what index_set_state_flags() does. * what index_set_state_flags() does.
*/ */
newIndexForm->indisvalid = true; newIndexForm->indisvalid = true;
oldIndexForm->indisvalid = false; oldIndexForm->indisvalid = false;
...@@ -3525,25 +3524,18 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence, ...@@ -3525,25 +3524,18 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence,
indexInfo->ii_ExclusionStrats = NULL; indexInfo->ii_ExclusionStrats = NULL;
} }
/* ensure SetReindexProcessing state isn't leaked */ /* Suppress use of the target index while rebuilding it */
PG_TRY(); SetReindexProcessing(heapId, indexId);
{
/* Suppress use of the target index while rebuilding it */
SetReindexProcessing(heapId, indexId);
/* Create a new physical relation for the index */ /* Create a new physical relation for the index */
RelationSetNewRelfilenode(iRel, persistence); RelationSetNewRelfilenode(iRel, persistence);
/* Initialize the index and rebuild */ /* Initialize the index and rebuild */
/* Note: we do not need to re-establish pkey setting */ /* Note: we do not need to re-establish pkey setting */
index_build(heapRelation, iRel, indexInfo, true, true); index_build(heapRelation, iRel, indexInfo, true, true);
}
PG_FINALLY(); /* Re-allow use of target index */
{ ResetReindexProcessing();
/* Make sure flag gets cleared on error exit */
ResetReindexProcessing();
}
PG_END_TRY();
/* /*
* If the index is marked invalid/not-ready/dead (ie, it's from a failed * If the index is marked invalid/not-ready/dead (ie, it's from a failed
...@@ -3680,7 +3672,9 @@ reindex_relation(Oid relid, int flags, int options) ...@@ -3680,7 +3672,9 @@ reindex_relation(Oid relid, int flags, int options)
Relation rel; Relation rel;
Oid toast_relid; Oid toast_relid;
List *indexIds; List *indexIds;
char persistence;
bool result; bool result;
ListCell *indexId;
int i; int i;
/* /*
...@@ -3715,77 +3709,65 @@ reindex_relation(Oid relid, int flags, int options) ...@@ -3715,77 +3709,65 @@ reindex_relation(Oid relid, int flags, int options)
*/ */
indexIds = RelationGetIndexList(rel); indexIds = RelationGetIndexList(rel);
PG_TRY(); if (flags & REINDEX_REL_SUPPRESS_INDEX_USE)
{ {
ListCell *indexId; /* Suppress use of all the indexes until they are rebuilt */
char persistence; SetReindexPending(indexIds);
if (flags & REINDEX_REL_SUPPRESS_INDEX_USE) /*
{ * Make the new heap contents visible --- now things might be
/* Suppress use of all the indexes until they are rebuilt */ * inconsistent!
SetReindexPending(indexIds); */
CommandCounterIncrement();
}
/* /*
* Make the new heap contents visible --- now things might be * Compute persistence of indexes: same as that of owning rel, unless
* inconsistent! * caller specified otherwise.
*/ */
CommandCounterIncrement(); if (flags & REINDEX_REL_FORCE_INDEXES_UNLOGGED)
} persistence = RELPERSISTENCE_UNLOGGED;
else if (flags & REINDEX_REL_FORCE_INDEXES_PERMANENT)
persistence = RELPERSISTENCE_PERMANENT;
else
persistence = rel->rd_rel->relpersistence;
/* Reindex all the indexes. */
i = 1;
foreach(indexId, indexIds)
{
Oid indexOid = lfirst_oid(indexId);
Oid indexNamespaceId = get_rel_namespace(indexOid);
/* /*
* Compute persistence of indexes: same as that of owning rel, unless * Skip any invalid indexes on a TOAST table. These can only be
* caller specified otherwise. * duplicate leftovers from a failed REINDEX CONCURRENTLY, and if
* rebuilt it would not be possible to drop them anymore.
*/ */
if (flags & REINDEX_REL_FORCE_INDEXES_UNLOGGED) if (IsToastNamespace(indexNamespaceId) &&
persistence = RELPERSISTENCE_UNLOGGED; !get_index_isvalid(indexOid))
else if (flags & REINDEX_REL_FORCE_INDEXES_PERMANENT)
persistence = RELPERSISTENCE_PERMANENT;
else
persistence = rel->rd_rel->relpersistence;
/* Reindex all the indexes. */
i = 1;
foreach(indexId, indexIds)
{ {
Oid indexOid = lfirst_oid(indexId); ereport(WARNING,
Oid indexNamespaceId = get_rel_namespace(indexOid); (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot reindex invalid index \"%s.%s\" on TOAST table, skipping",
/* get_namespace_name(indexNamespaceId),
* Skip any invalid indexes on a TOAST table. These can only be get_rel_name(indexOid))));
* duplicate leftovers from a failed REINDEX CONCURRENTLY, and if continue;
* rebuilt it would not be possible to drop them anymore. }
*/
if (IsToastNamespace(indexNamespaceId) &&
!get_index_isvalid(indexOid))
{
ereport(WARNING,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot reindex invalid index \"%s.%s\" on TOAST table, skipping",
get_namespace_name(indexNamespaceId),
get_rel_name(indexOid))));
continue;
}
reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS), reindex_index(indexOid, !(flags & REINDEX_REL_CHECK_CONSTRAINTS),
persistence, options); persistence, options);
CommandCounterIncrement(); CommandCounterIncrement();
/* Index should no longer be in the pending list */ /* Index should no longer be in the pending list */
Assert(!ReindexIsProcessingIndex(indexOid)); Assert(!ReindexIsProcessingIndex(indexOid));
/* Set index rebuild count */ /* Set index rebuild count */
pgstat_progress_update_param(PROGRESS_CLUSTER_INDEX_REBUILD_COUNT, pgstat_progress_update_param(PROGRESS_CLUSTER_INDEX_REBUILD_COUNT,
i); i);
i++; i++;
}
} }
PG_FINALLY();
{
/* Make sure list gets cleared on error exit */
ResetReindexPending();
}
PG_END_TRY();
/* /*
* Close rel, but continue to hold the lock. * Close rel, but continue to hold the lock.
...@@ -3819,6 +3801,7 @@ reindex_relation(Oid relid, int flags, int options) ...@@ -3819,6 +3801,7 @@ reindex_relation(Oid relid, int flags, int options)
static Oid currentlyReindexedHeap = InvalidOid; static Oid currentlyReindexedHeap = InvalidOid;
static Oid currentlyReindexedIndex = InvalidOid; static Oid currentlyReindexedIndex = InvalidOid;
static List *pendingReindexedIndexes = NIL; static List *pendingReindexedIndexes = NIL;
static int reindexingNestLevel = 0;
/* /*
* ReindexIsProcessingHeap * ReindexIsProcessingHeap
...@@ -3855,8 +3838,6 @@ ReindexIsProcessingIndex(Oid indexOid) ...@@ -3855,8 +3838,6 @@ ReindexIsProcessingIndex(Oid indexOid)
/* /*
* SetReindexProcessing * SetReindexProcessing
* Set flag that specified heap/index are being reindexed. * Set flag that specified heap/index are being reindexed.
*
* NB: caller must use a PG_TRY block to ensure ResetReindexProcessing is done.
*/ */
static void static void
SetReindexProcessing(Oid heapOid, Oid indexOid) SetReindexProcessing(Oid heapOid, Oid indexOid)
...@@ -3869,6 +3850,8 @@ SetReindexProcessing(Oid heapOid, Oid indexOid) ...@@ -3869,6 +3850,8 @@ SetReindexProcessing(Oid heapOid, Oid indexOid)
currentlyReindexedIndex = indexOid; currentlyReindexedIndex = indexOid;
/* Index is no longer "pending" reindex. */ /* Index is no longer "pending" reindex. */
RemoveReindexPending(indexOid); RemoveReindexPending(indexOid);
/* This may have been set already, but in case it isn't, do so now. */
reindexingNestLevel = GetCurrentTransactionNestLevel();
} }
/* /*
...@@ -3878,17 +3861,16 @@ SetReindexProcessing(Oid heapOid, Oid indexOid) ...@@ -3878,17 +3861,16 @@ SetReindexProcessing(Oid heapOid, Oid indexOid)
static void static void
ResetReindexProcessing(void) ResetReindexProcessing(void)
{ {
/* This may be called in leader error path */
currentlyReindexedHeap = InvalidOid; currentlyReindexedHeap = InvalidOid;
currentlyReindexedIndex = InvalidOid; currentlyReindexedIndex = InvalidOid;
/* reindexingNestLevel remains set till end of (sub)transaction */
} }
/* /*
* SetReindexPending * SetReindexPending
* Mark the given indexes as pending reindex. * Mark the given indexes as pending reindex.
* *
* NB: caller must use a PG_TRY block to ensure ResetReindexPending is done. * NB: we assume that the current memory context stays valid throughout.
* Also, we assume that the current memory context stays valid throughout.
*/ */
static void static void
SetReindexPending(List *indexes) SetReindexPending(List *indexes)
...@@ -3899,6 +3881,7 @@ SetReindexPending(List *indexes) ...@@ -3899,6 +3881,7 @@ SetReindexPending(List *indexes)
if (IsInParallelMode()) if (IsInParallelMode())
elog(ERROR, "cannot modify reindex state during a parallel operation"); elog(ERROR, "cannot modify reindex state during a parallel operation");
pendingReindexedIndexes = list_copy(indexes); pendingReindexedIndexes = list_copy(indexes);
reindexingNestLevel = GetCurrentTransactionNestLevel();
} }
/* /*
...@@ -3915,14 +3898,32 @@ RemoveReindexPending(Oid indexOid) ...@@ -3915,14 +3898,32 @@ RemoveReindexPending(Oid indexOid)
} }
/* /*
* ResetReindexPending * ResetReindexState
* Unset reindex-pending status. * Clear all reindexing state during (sub)transaction abort.
*/ */
static void void
ResetReindexPending(void) ResetReindexState(int nestLevel)
{ {
/* This may be called in leader error path */ /*
pendingReindexedIndexes = NIL; * Because reindexing is not re-entrant, we don't need to cope with nested
* reindexing states. We just need to avoid messing up the outer-level
* state in case a subtransaction fails within a REINDEX. So checking the
* current nest level against that of the reindex operation is sufficient.
*/
if (reindexingNestLevel >= nestLevel)
{
currentlyReindexedHeap = InvalidOid;
currentlyReindexedIndex = InvalidOid;
/*
* We needn't try to release the contents of pendingReindexedIndexes;
* that list should be in a transaction-lifespan context, so it will
* go away automatically.
*/
pendingReindexedIndexes = NIL;
reindexingNestLevel = 0;
}
} }
/* /*
...@@ -3975,4 +3976,7 @@ RestoreReindexState(void *reindexstate) ...@@ -3975,4 +3976,7 @@ RestoreReindexState(void *reindexstate)
lappend_oid(pendingReindexedIndexes, lappend_oid(pendingReindexedIndexes,
sistate->pendingReindexedIndexes[c]); sistate->pendingReindexedIndexes[c]);
MemoryContextSwitchTo(oldcontext); MemoryContextSwitchTo(oldcontext);
/* Note the worker has its own transaction nesting level */
reindexingNestLevel = GetCurrentTransactionNestLevel();
} }
...@@ -131,6 +131,8 @@ extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot); ...@@ -131,6 +131,8 @@ extern void validate_index(Oid heapId, Oid indexId, Snapshot snapshot);
extern void index_set_state_flags(Oid indexId, IndexStateFlagsAction action); extern void index_set_state_flags(Oid indexId, IndexStateFlagsAction action);
extern Oid IndexGetRelation(Oid indexId, bool missing_ok);
extern void reindex_index(Oid indexId, bool skip_constraint_checks, extern void reindex_index(Oid indexId, bool skip_constraint_checks,
char relpersistence, int options); char relpersistence, int options);
...@@ -145,8 +147,8 @@ extern bool reindex_relation(Oid relid, int flags, int options); ...@@ -145,8 +147,8 @@ extern bool reindex_relation(Oid relid, int flags, int options);
extern bool ReindexIsProcessingHeap(Oid heapOid); extern bool ReindexIsProcessingHeap(Oid heapOid);
extern bool ReindexIsProcessingIndex(Oid indexOid); extern bool ReindexIsProcessingIndex(Oid indexOid);
extern Oid IndexGetRelation(Oid indexId, bool missing_ok);
extern void ResetReindexState(int nestLevel);
extern Size EstimateReindexStateSpace(void); extern Size EstimateReindexStateSpace(void);
extern void SerializeReindexState(Size maxsize, char *start_address); extern void SerializeReindexState(Size maxsize, char *start_address);
extern void RestoreReindexState(void *reindexstate); extern void RestoreReindexState(void *reindexstate);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment