Commit add85ead authored by Peter Eisentraut's avatar Peter Eisentraut

Fix table lock levels for REINDEX INDEX CONCURRENTLY

REINDEX CONCURRENTLY locks tables with ShareUpdateExclusiveLock rather
than the ShareLock used by a plain REINDEX.  However,
RangeVarCallbackForReindexIndex() was not updated for that and still
used the ShareLock only.  This would lead to lock upgrades later,
leading to possible deadlocks.
Reported-by: default avatarAndres Freund <andres@anarazel.de>
Reviewed-by: default avatarAndres Freund <andres@anarazel.de>
Reviewed-by: default avatarMichael Paquier <michael@paquier.xyz>
Discussion: https://www.postgresql.org/message-id/flat/20190430151735.wi52sxjvxsjvaxxt%40alap3.anarazel.de
parent 8efe710d
...@@ -91,6 +91,15 @@ static bool ReindexRelationConcurrently(Oid relationOid, int options); ...@@ -91,6 +91,15 @@ static bool ReindexRelationConcurrently(Oid relationOid, int options);
static void ReindexPartitionedIndex(Relation parentIdx); static void ReindexPartitionedIndex(Relation parentIdx);
static void update_relispartition(Oid relationId, bool newval); static void update_relispartition(Oid relationId, bool newval);
/*
* callback argument type for RangeVarCallbackForReindexIndex()
*/
struct ReindexIndexCallbackState
{
bool concurrent; /* flag from statement */
Oid locked_table_oid; /* tracks previously locked table */
};
/* /*
* CheckIndexCompatible * CheckIndexCompatible
* Determine whether an existing index definition is compatible with a * Determine whether an existing index definition is compatible with a
...@@ -2303,8 +2312,8 @@ ChooseIndexColumnNames(List *indexElems) ...@@ -2303,8 +2312,8 @@ ChooseIndexColumnNames(List *indexElems)
void void
ReindexIndex(RangeVar *indexRelation, int options, bool concurrent) ReindexIndex(RangeVar *indexRelation, int options, bool concurrent)
{ {
struct ReindexIndexCallbackState state;
Oid indOid; Oid indOid;
Oid heapOid = InvalidOid;
Relation irel; Relation irel;
char persistence; char persistence;
...@@ -2313,11 +2322,13 @@ ReindexIndex(RangeVar *indexRelation, int options, bool concurrent) ...@@ -2313,11 +2322,13 @@ ReindexIndex(RangeVar *indexRelation, int options, bool concurrent)
* obtain lock on table first, to avoid deadlock hazard. The lock level * obtain lock on table first, to avoid deadlock hazard. The lock level
* used here must match the index lock obtained in reindex_index(). * used here must match the index lock obtained in reindex_index().
*/ */
state.concurrent = concurrent;
state.locked_table_oid = InvalidOid;
indOid = RangeVarGetRelidExtended(indexRelation, indOid = RangeVarGetRelidExtended(indexRelation,
concurrent ? ShareUpdateExclusiveLock : AccessExclusiveLock, concurrent ? ShareUpdateExclusiveLock : AccessExclusiveLock,
0, 0,
RangeVarCallbackForReindexIndex, RangeVarCallbackForReindexIndex,
(void *) &heapOid); &state);
/* /*
* Obtain the current persistence of the existing index. We already hold * Obtain the current persistence of the existing index. We already hold
...@@ -2350,7 +2361,15 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, ...@@ -2350,7 +2361,15 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation,
Oid relId, Oid oldRelId, void *arg) Oid relId, Oid oldRelId, void *arg)
{ {
char relkind; char relkind;
Oid *heapOid = (Oid *) arg; struct ReindexIndexCallbackState *state = arg;
LOCKMODE table_lockmode;
/*
* Lock level here should match table lock in reindex_index() for
* non-concurrent case and table locks used by index_concurrently_*() for
* concurrent case.
*/
table_lockmode = state->concurrent ? ShareUpdateExclusiveLock : ShareLock;
/* /*
* If we previously locked some other index's heap, and the name we're * If we previously locked some other index's heap, and the name we're
...@@ -2359,9 +2378,8 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, ...@@ -2359,9 +2378,8 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation,
*/ */
if (relId != oldRelId && OidIsValid(oldRelId)) if (relId != oldRelId && OidIsValid(oldRelId))
{ {
/* lock level here should match reindex_index() heap lock */ UnlockRelationOid(state->locked_table_oid, table_lockmode);
UnlockRelationOid(*heapOid, ShareLock); state->locked_table_oid = InvalidOid;
*heapOid = InvalidOid;
} }
/* If the relation does not exist, there's nothing more to do. */ /* If the relation does not exist, there's nothing more to do. */
...@@ -2389,14 +2407,17 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, ...@@ -2389,14 +2407,17 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation,
/* Lock heap before index to avoid deadlock. */ /* Lock heap before index to avoid deadlock. */
if (relId != oldRelId) if (relId != oldRelId)
{ {
Oid table_oid = IndexGetRelation(relId, true);
/* /*
* Lock level here should match reindex_index() heap lock. If the OID * If the OID isn't valid, it means the index was concurrently
* isn't valid, it means the index as concurrently dropped, which is * dropped, which is not a problem for us; just return normally.
* not a problem for us; just return normally.
*/ */
*heapOid = IndexGetRelation(relId, true); if (OidIsValid(table_oid))
if (OidIsValid(*heapOid)) {
LockRelationOid(*heapOid, ShareLock); LockRelationOid(table_oid, table_lockmode);
state->locked_table_oid = table_oid;
}
} }
} }
......
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