diff options
Diffstat (limited to 'src/backend/utils/cache/inval.c')
-rw-r--r-- | src/backend/utils/cache/inval.c | 131 |
1 files changed, 105 insertions, 26 deletions
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 90577cb6e40..3364322dd5b 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -74,7 +74,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.59 2003/11/29 19:52:00 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.60 2004/02/10 01:55:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -83,6 +83,7 @@ #include "catalog/catalog.h" #include "miscadmin.h" #include "storage/sinval.h" +#include "storage/smgr.h" #include "utils/catcache.h" #include "utils/inval.h" #include "utils/memutils.h" @@ -298,19 +299,22 @@ AddCatcacheInvalidationMessage(InvalidationListHeader *hdr, */ static void AddRelcacheInvalidationMessage(InvalidationListHeader *hdr, - Oid dbId, Oid relId) + Oid dbId, Oid relId, RelFileNode physId) { SharedInvalidationMessage msg; /* Don't add a duplicate item */ - /* We assume comparing relId is sufficient, needn't check dbId */ + /* We assume dbId need not be checked because it will never change */ + /* relfilenode fields must be checked to support reassignment */ ProcessMessageList(hdr->rclist, - if (msg->rc.relId == relId) return); + if (msg->rc.relId == relId && + RelFileNodeEquals(msg->rc.physId, physId)) return); /* OK, add the item */ msg.rc.id = SHAREDINVALRELCACHE_ID; msg.rc.dbId = dbId; msg.rc.relId = relId; + msg.rc.physId = physId; AddInvalidationMessage(&hdr->rclist, &msg); } @@ -391,10 +395,10 @@ RegisterCatcacheInvalidation(int cacheId, * As above, but register a relcache invalidation event. */ static void -RegisterRelcacheInvalidation(Oid dbId, Oid relId) +RegisterRelcacheInvalidation(Oid dbId, Oid relId, RelFileNode physId) { AddRelcacheInvalidationMessage(&CurrentCmdInvalidMsgs, - dbId, relId); + dbId, relId, physId); /* * If the relation being invalidated is one of those cached in the @@ -435,9 +439,17 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg) } else if (msg->id == SHAREDINVALRELCACHE_ID) { - if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == 0) + /* + * If the message includes a valid relfilenode, we must ensure that + * smgr cache entry gets zapped. The relcache will handle this if + * called, otherwise we must do it directly. + */ + if (msg->rc.dbId == MyDatabaseId || msg->rc.dbId == InvalidOid) { - RelationIdInvalidateRelationCacheByRelationId(msg->rc.relId); + if (OidIsValid(msg->rc.physId.relNode)) + RelationCacheInvalidateEntry(msg->rc.relId, &msg->rc.physId); + else + RelationCacheInvalidateEntry(msg->rc.relId, NULL); for (i = 0; i < cache_callback_count; i++) { @@ -447,6 +459,12 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg) (*ccitem->function) (ccitem->arg, msg->rc.relId); } } + else + { + /* might have smgr entry even if not in our database */ + if (OidIsValid(msg->rc.physId.relNode)) + smgrclosenode(msg->rc.physId); + } } else elog(FATAL, "unrecognized SI message id: %d", msg->id); @@ -456,7 +474,7 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg) * InvalidateSystemCaches * * This blows away all tuples in the system catalog caches and - * all the cached relation descriptors (and closes their files too). + * all the cached relation descriptors and smgr cache entries. * Relation descriptors that have positive refcounts are then rebuilt. * * We call this when we see a shared-inval-queue overflow signal, @@ -469,7 +487,7 @@ InvalidateSystemCaches(void) int i; ResetCatalogCaches(); - RelationCacheInvalidate(); + RelationCacheInvalidate(); /* gets smgr cache too */ for (i = 0; i < cache_callback_count; i++) { @@ -488,11 +506,15 @@ static void PrepareForTupleInvalidation(Relation relation, HeapTuple tuple, void (*CacheIdRegisterFunc) (int, uint32, ItemPointer, Oid), - void (*RelationIdRegisterFunc) (Oid, Oid)) + void (*RelationIdRegisterFunc) (Oid, Oid, + RelFileNode)) { Oid tupleRelId; + Oid databaseId; Oid relationId; + RelFileNode rnode; + /* Do nothing during bootstrap */ if (IsBootstrapProcessingMode()) return; @@ -524,24 +546,49 @@ PrepareForTupleInvalidation(Relation relation, HeapTuple tuple, tupleRelId = RelationGetRelid(relation); if (tupleRelId == RelOid_pg_class) + { + Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple); + relationId = HeapTupleGetOid(tuple); + if (classtup->relisshared) + databaseId = InvalidOid; + else + databaseId = MyDatabaseId; + rnode.tblNode = databaseId; /* XXX change for tablespaces */ + rnode.relNode = classtup->relfilenode; + /* + * Note: during a pg_class row update that assigns a new relfilenode + * value, we will be called on both the old and new tuples, and thus + * will broadcast invalidation messages showing both the old and new + * relfilenode values. This ensures that other backends will close + * smgr references to the old relfilenode file. + */ + } else if (tupleRelId == RelOid_pg_attribute) - relationId = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid; + { + Form_pg_attribute atttup = (Form_pg_attribute) GETSTRUCT(tuple); + + relationId = atttup->attrelid; + /* + * 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 + * appropriate, since only those backends could see our pg_attribute + * change anyway. It looks a bit ugly though. + */ + databaseId = MyDatabaseId; + /* We assume no smgr cache flush is needed, either */ + rnode.tblNode = InvalidOid; + rnode.relNode = InvalidOid; + } else return; /* - * Yes. We need to register a relcache invalidation event for the - * relation identified by relationId. - * - * KLUGE ALERT: we always send the relcache event with MyDatabaseId, even - * if the rel in question is shared. This essentially means that only - * backends in this same database will react to the relcache flush - * request. This is in fact appropriate, since only those backends - * could see our pg_class or pg_attribute change anyway. It looks a - * bit ugly though. + * Yes. We need to register a relcache invalidation event. */ - (*RelationIdRegisterFunc) (MyDatabaseId, relationId); + (*RelationIdRegisterFunc) (databaseId, relationId, rnode); } @@ -660,7 +707,7 @@ CommandEndInvalidationMessages(bool isCommit) /* * CacheInvalidateHeapTuple * Register the given tuple for invalidation at end of command - * (ie, current command is outdating this tuple). + * (ie, current command is creating or outdating this tuple). */ void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple) @@ -678,12 +725,44 @@ CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple) * This is used in places that need to force relcache rebuild but aren't * changing any of the tuples recognized as contributors to the relcache * entry by PrepareForTupleInvalidation. (An example is dropping an index.) + * We assume in particular that relfilenode isn't changing. */ void -CacheInvalidateRelcache(Oid relationId) +CacheInvalidateRelcache(Relation relation) { - /* See KLUGE ALERT in PrepareForTupleInvalidation */ - RegisterRelcacheInvalidation(MyDatabaseId, relationId); + Oid databaseId; + Oid relationId; + + relationId = RelationGetRelid(relation); + if (relation->rd_rel->relisshared) + databaseId = InvalidOid; + else + databaseId = MyDatabaseId; + + RegisterRelcacheInvalidation(databaseId, relationId, relation->rd_node); +} + +/* + * CacheInvalidateRelcacheByTuple + * As above, but relation is identified by passing its pg_class tuple. + */ +void +CacheInvalidateRelcacheByTuple(HeapTuple classTuple) +{ + Form_pg_class classtup = (Form_pg_class) GETSTRUCT(classTuple); + Oid databaseId; + Oid relationId; + RelFileNode rnode; + + relationId = HeapTupleGetOid(classTuple); + if (classtup->relisshared) + databaseId = InvalidOid; + else + databaseId = MyDatabaseId; + rnode.tblNode = databaseId; /* XXX change for tablespaces */ + rnode.relNode = classtup->relfilenode; + + RegisterRelcacheInvalidation(databaseId, relationId, rnode); } /* |