Fix things so that when CREATE INDEX CONCURRENTLY sets pg_index.indisvalid

true at the very end of its processing, the update is broadcast via a
shared-cache-inval message for the index; without this, existing backends that
already have relcache entries for the index might never see it become valid.
Also, force a relcache inval on the index's parent table at the same time,
so that any cached plans for that table are re-planned; this ensures that
the newly valid index will be used if appropriate.  Aside from making
C.I.C. behave more reasonably, this is necessary infrastructure for some
aspects of the HOT patch.  Pavan Deolasee, with a little further stuff from
me.
This commit is contained in:
Tom Lane 2007-05-02 21:08:46 +00:00
parent 229d33801d
commit 8ec943856a
3 changed files with 75 additions and 21 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.157 2007/03/13 00:33:39 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.158 2007/05/02 21:08:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -41,6 +41,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/relcache.h"
@ -514,7 +515,9 @@ DefineIndex(RangeVar *heapRelation,
for (ixcnt = 0; ixcnt < snapshot->xcnt; ixcnt++)
XactLockTableWait(snapshot->xip[ixcnt]);
/* Index can now be marked valid -- update its pg_index entry */
/*
* Index can now be marked valid -- update its pg_index entry
*/
pg_index = heap_open(IndexRelationId, RowExclusiveLock);
indexTuple = SearchSysCacheCopy(INDEXRELID,
@ -534,6 +537,15 @@ DefineIndex(RangeVar *heapRelation,
heap_close(pg_index, RowExclusiveLock);
/*
* The pg_index update will cause backends (including this one) to update
* relcache entries for the index itself, but we should also send a
* relcache inval on the parent table to force replanning of cached plans.
* Otherwise existing sessions might fail to use the new index where it
* would be useful.
*/
CacheInvalidateRelcacheByRelid(heaprelid.relId);
/*
* Last thing to do is release the session-level lock on the parent table.
*/

View File

@ -80,7 +80,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.79 2007/01/05 22:19:43 momjian Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.80 2007/05/02 21:08:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -590,12 +590,27 @@ PrepareForTupleInvalidation(Relation relation, HeapTuple tuple)
* KLUGE ALERT: we always send the relcache event with MyDatabaseId,
* even if the rel in question is shared (which we can't easily tell).
* This essentially means that only backends in this same database
* will react to the relcache flush request. This is in fact
* will react to the relcache flush request. This is in fact
* appropriate, since only those backends could see our pg_attribute
* change anyway. It looks a bit ugly though.
* change anyway. It looks a bit ugly though. (In practice, shared
* relations can't have schema changes after bootstrap, so we should
* never come here for a shared rel anyway.)
*/
databaseId = MyDatabaseId;
}
else if (tupleRelId == IndexRelationId)
{
Form_pg_index indextup = (Form_pg_index) GETSTRUCT(tuple);
/*
* When a pg_index row is updated, we should send out a relcache inval
* for the index relation. As above, we don't know the shared status
* of the index, but in practice it doesn't matter since indexes of
* shared catalogs can't have such updates.
*/
relationId = indextup->indexrelid;
databaseId = MyDatabaseId;
}
else
return;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.259 2007/03/29 00:15:38 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.260 2007/05/02 21:08:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -181,7 +181,7 @@ static HTAB *OpClassCache = NULL;
static void RelationClearRelation(Relation relation, bool rebuild);
static void RelationReloadClassinfo(Relation relation);
static void RelationReloadIndexInfo(Relation relation);
static void RelationFlushRelation(Relation relation);
static bool load_relcache_init_file(void);
static void write_relcache_init_file(void);
@ -1504,7 +1504,7 @@ RelationIdGetRelation(Oid relationId)
RelationIncrementReferenceCount(rd);
/* revalidate nailed index if necessary */
if (!rd->rd_isvalid)
RelationReloadClassinfo(rd);
RelationReloadIndexInfo(rd);
return rd;
}
@ -1579,24 +1579,24 @@ RelationClose(Relation relation)
}
/*
* RelationReloadClassinfo - reload the pg_class row (only)
* RelationReloadIndexInfo - reload minimal information for an open index
*
* 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.
* This function is used only for indexes. A relcache inval on an index
* can mean that its pg_class or pg_index row changed. There are only
* very limited changes that are allowed to an existing index's schema,
* so we can update the relcache entry without a complete rebuild; which
* is fortunate because we can't rebuild an index entry that is "nailed"
* and/or in active use. We support full replacement of the pg_class row,
* as well as updates of a few simple fields of the pg_index row.
*
* We can't necessarily reread the pg_class row right away; we might be
* We can't necessarily reread the catalog rows right away; we might be
* in a failed transaction when we receive the SI notification. If so,
* RelationClearRelation just marks the entry as invalid by setting
* rd_isvalid to false. This routine is called to fix the entry when it
* is next needed.
*/
static void
RelationReloadClassinfo(Relation relation)
RelationReloadIndexInfo(Relation relation)
{
bool indexOK;
HeapTuple pg_class_tuple;
@ -1635,6 +1635,33 @@ RelationReloadClassinfo(Relation relation)
if (relation->rd_amcache)
pfree(relation->rd_amcache);
relation->rd_amcache = NULL;
/*
* For a non-system index, there are fields of the pg_index row that are
* allowed to change, so re-read that row and update the relcache entry.
* Most of the info derived from pg_index (such as support function lookup
* info) cannot change, and indeed the whole point of this routine is to
* update the relcache entry without clobbering that data; so wholesale
* replacement is not appropriate.
*/
if (!IsSystemRelation(relation))
{
HeapTuple tuple;
Form_pg_index index;
tuple = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(RelationGetRelid(relation)),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for index %u",
RelationGetRelid(relation));
index = (Form_pg_index) GETSTRUCT(tuple);
relation->rd_index->indisvalid = index->indisvalid;
ReleaseSysCache(tuple);
}
/* Okay, now it's valid again */
relation->rd_isvalid = true;
}
@ -1683,7 +1710,7 @@ RelationClearRelation(Relation relation, bool rebuild)
{
relation->rd_isvalid = false; /* needs to be revalidated */
if (relation->rd_refcnt > 1)
RelationReloadClassinfo(relation);
RelationReloadIndexInfo(relation);
}
return;
}
@ -1693,14 +1720,14 @@ RelationClearRelation(Relation relation, bool rebuild)
* 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.
* index, and we check for pg_index updates too.
*/
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);
RelationReloadIndexInfo(relation);
return;
}