Avoid crashing if relcache flush occurs while trying to load data into an

index's support-function cache (in index_getprocinfo).  Since none of that
data can change for an index that's in active use, it seems sufficient to
treat all open indexes the same way we were treating "nailed" system indexes
--- that is, just re-read the pg_class row and leave the rest of the relcache
entry strictly alone.  The pg_class re-read might not be strictly necessary
either, but since the reltablespace and relfilenode can change in normal
operation it seems safest to do it.  (We don't support changing any of the
other info about an index at all, at the moment.)

Back-patch as far as 8.0.  It might be possible to adapt the patch to 7.4,
but it would take more work than I care to expend for such a low-probability
problem.  7.3 is out of luck for sure.
This commit is contained in:
Tom Lane 2006-01-19 20:28:43 +00:00
parent 99e0c54290
commit ed69cf5d23
1 changed files with 34 additions and 13 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.236 2006/01/19 00:27:08 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.237 2006/01/19 20:28:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1436,11 +1436,13 @@ RelationClose(Relation relation)
/*
* RelationReloadClassinfo - reload the pg_class row (only)
*
* This function is used only for nailed indexes. Since a REINDEX can
* change the relfilenode value for a nailed index, we have to reread
* the pg_class row anytime we get an SI invalidation on a nailed index
* (without throwing away the whole relcache entry, since we'd be unable
* to rebuild it).
* This function is used only for indexes. We currently allow only the
* pg_class row of an existing index to change (to support changes of
* owner, tablespace, or relfilenode), not its pg_index row or other
* subsidiary index schema information. Therefore it's sufficient to do
* this when we get an SI invalidation. Furthermore, there are cases
* where it's necessary not to throw away the index information, especially
* for "nailed" indexes which we are unable to rebuild on-the-fly.
*
* We can't necessarily reread the pg_class row right away; we might be
* in a failed transaction when we receive the SI notification. If so,
@ -1455,9 +1457,11 @@ RelationReloadClassinfo(Relation relation)
HeapTuple pg_class_tuple;
Form_pg_class relp;
/* Should be called only for invalidated nailed indexes */
Assert(relation->rd_isnailed && !relation->rd_isvalid &&
relation->rd_rel->relkind == RELKIND_INDEX);
/* Should be called only for invalidated indexes */
Assert(relation->rd_rel->relkind == RELKIND_INDEX &&
!relation->rd_isvalid);
/* Should be closed at smgr level */
Assert(relation->rd_smgr == NULL);
/*
* Read the pg_class row
@ -1468,13 +1472,14 @@ RelationReloadClassinfo(Relation relation)
indexOK = (RelationGetRelid(relation) != ClassOidIndexId);
pg_class_tuple = ScanPgRelation(RelationGetRelid(relation), indexOK);
if (!HeapTupleIsValid(pg_class_tuple))
elog(ERROR, "could not find tuple for system relation %u",
elog(ERROR, "could not find pg_class tuple for index %u",
RelationGetRelid(relation));
relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
/* Now we can recalculate physical address */
RelationInitPhysicalAddr(relation);
memcpy(relation->rd_rel, relp, CLASS_TUPLE_SIZE);
heap_freetuple(pg_class_tuple);
/* We must recalculate physical address in case it changed */
RelationInitPhysicalAddr(relation);
/* Make sure targblock is reset in case rel was truncated */
relation->rd_targblock = InvalidBlockNumber;
/* Okay, now it's valid again */
relation->rd_isvalid = true;
@ -1529,6 +1534,22 @@ RelationClearRelation(Relation relation, bool rebuild)
return;
}
/*
* Even non-system indexes should not be blown away if they are open and
* have valid index support information. This avoids problems with active
* use of the index support information. As with nailed indexes, we
* re-read the pg_class row to handle possible physical relocation of
* the index.
*/
if (relation->rd_rel->relkind == RELKIND_INDEX &&
relation->rd_refcnt > 0 &&
relation->rd_indexcxt != NULL)
{
relation->rd_isvalid = false; /* needs to be revalidated */
RelationReloadClassinfo(relation);
return;
}
/*
* Remove relation from hash tables
*