diff options
Diffstat (limited to 'src/backend/utils/cache')
-rw-r--r-- | src/backend/utils/cache/attoptcache.c | 2 | ||||
-rw-r--r-- | src/backend/utils/cache/catcache.c | 48 | ||||
-rw-r--r-- | src/backend/utils/cache/inval.c | 221 | ||||
-rw-r--r-- | src/backend/utils/cache/plancache.c | 20 | ||||
-rw-r--r-- | src/backend/utils/cache/spccache.c | 2 | ||||
-rw-r--r-- | src/backend/utils/cache/ts_cache.c | 2 |
6 files changed, 153 insertions, 142 deletions
diff --git a/src/backend/utils/cache/attoptcache.c b/src/backend/utils/cache/attoptcache.c index 7018ccfe62a..ba39aa8ee7f 100644 --- a/src/backend/utils/cache/attoptcache.c +++ b/src/backend/utils/cache/attoptcache.c @@ -53,7 +53,7 @@ typedef struct * query execution), this seems OK. */ static void -InvalidateAttoptCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr) +InvalidateAttoptCacheCallback(Datum arg, int cacheid, uint32 hashvalue) { HASH_SEQ_STATUS status; AttoptCacheEntry *attopt; diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 6a0c020ff97..f43e4181e78 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -435,21 +435,14 @@ CatCacheRemoveCList(CatCache *cache, CatCList *cl) * target tuple that has to be invalidated has a different TID than it * did when the event was created. So now we just compare hash values and * accept the small risk of unnecessary invalidations due to false matches. - * (The ItemPointer argument is therefore useless and should get removed.) * * This routine is only quasi-public: it should only be used by inval.c. */ void -CatalogCacheIdInvalidate(int cacheId, - uint32 hashValue, - ItemPointer pointer) +CatalogCacheIdInvalidate(int cacheId, uint32 hashValue) { CatCache *ccp; - /* - * sanity checks - */ - Assert(ItemPointerIsValid(pointer)); CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: called"); /* @@ -699,7 +692,7 @@ CatalogCacheFlushCatalog(Oid catId) ResetCatalogCache(cache); /* Tell inval.c to call syscache callbacks for this cache */ - CallSyscacheCallbacks(cache->id, NULL); + CallSyscacheCallbacks(cache->id, 0); } } @@ -1708,11 +1701,16 @@ build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys) * The lists of tuples that need to be flushed are kept by inval.c. This * routine is a helper routine for inval.c. Given a tuple belonging to * the specified relation, find all catcaches it could be in, compute the - * correct hash value for each such catcache, and call the specified function - * to record the cache id, hash value, and tuple ItemPointer in inval.c's - * lists. CatalogCacheIdInvalidate will be called later, if appropriate, + * correct hash value for each such catcache, and call the specified + * function to record the cache id and hash value in inval.c's lists. + * CatalogCacheIdInvalidate will be called later, if appropriate, * using the recorded information. * + * For an insert or delete, tuple is the target tuple and newtuple is NULL. + * For an update, we are called just once, with tuple being the old tuple + * version and newtuple the new version. We should make two list entries + * if the tuple's hash value changed, but only one if it didn't. + * * Note that it is irrelevant whether the given tuple is actually loaded * into the catcache at the moment. Even if it's not there now, it might * be by the end of the command, or there might be a matching negative entry @@ -1727,7 +1725,8 @@ build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys) void PrepareToInvalidateCacheTuple(Relation relation, HeapTuple tuple, - void (*function) (int, uint32, ItemPointer, Oid)) + HeapTuple newtuple, + void (*function) (int, uint32, Oid)) { CatCache *ccp; Oid reloid; @@ -1747,13 +1746,16 @@ PrepareToInvalidateCacheTuple(Relation relation, /* ---------------- * for each cache * if the cache contains tuples from the specified relation - * compute the tuple's hash value in this cache, + * compute the tuple's hash value(s) in this cache, * and call the passed function to register the information. * ---------------- */ for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next) { + uint32 hashvalue; + Oid dbid; + if (ccp->cc_reloid != reloid) continue; @@ -1761,10 +1763,20 @@ PrepareToInvalidateCacheTuple(Relation relation, if (ccp->cc_tupdesc == NULL) CatalogCacheInitializeCache(ccp); - (*function) (ccp->id, - CatalogCacheComputeTupleHashValue(ccp, tuple), - &tuple->t_self, - ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId); + hashvalue = CatalogCacheComputeTupleHashValue(ccp, tuple); + dbid = ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId; + + (*function) (ccp->id, hashvalue, dbid); + + if (newtuple) + { + uint32 newhashvalue; + + newhashvalue = CatalogCacheComputeTupleHashValue(ccp, newtuple); + + if (newhashvalue != hashvalue) + (*function) (ccp->id, newhashvalue, dbid); + } } } diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c index 4249bd33765..8792ec40842 100644 --- a/src/backend/utils/cache/inval.c +++ b/src/backend/utils/cache/inval.c @@ -39,8 +39,8 @@ * * In short, we need to remember until xact end every insert or delete * of a tuple that might be in the system caches. Updates are treated as - * two events, delete + insert, for simplicity. (There are cases where - * it'd be possible to record just one event, but we don't currently try.) + * two events, delete + insert, for simplicity. (If the update doesn't + * change the tuple hash value, catcache.c optimizes this into one event.) * * We do not need to register EVERY tuple operation in this way, just those * on tuples in relations that have associated catcaches. We do, however, @@ -314,14 +314,12 @@ AppendInvalidationMessageList(InvalidationChunk **destHdr, */ static void AddCatcacheInvalidationMessage(InvalidationListHeader *hdr, - int id, uint32 hashValue, - ItemPointer tuplePtr, Oid dbId) + int id, uint32 hashValue, Oid dbId) { SharedInvalidationMessage msg; Assert(id < CHAR_MAX); msg.cc.id = (int8) id; - msg.cc.tuplePtr = *tuplePtr; msg.cc.dbId = dbId; msg.cc.hashValue = hashValue; AddInvalidationMessage(&hdr->cclist, &msg); @@ -416,11 +414,10 @@ ProcessInvalidationMessagesMulti(InvalidationListHeader *hdr, static void RegisterCatcacheInvalidation(int cacheId, uint32 hashValue, - ItemPointer tuplePtr, Oid dbId) { AddCatcacheInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs, - cacheId, hashValue, tuplePtr, dbId); + cacheId, hashValue, dbId); } /* @@ -476,11 +473,9 @@ LocalExecuteInvalidationMessage(SharedInvalidationMessage *msg) { if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid) { - CatalogCacheIdInvalidate(msg->cc.id, - msg->cc.hashValue, - &msg->cc.tuplePtr); + CatalogCacheIdInvalidate(msg->cc.id, msg->cc.hashValue); - CallSyscacheCallbacks(msg->cc.id, &msg->cc.tuplePtr); + CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue); } } else if (msg->id == SHAREDINVALCATALOG_ID) @@ -555,7 +550,7 @@ InvalidateSystemCaches(void) { struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i; - (*ccitem->function) (ccitem->arg, ccitem->id, NULL); + (*ccitem->function) (ccitem->arg, ccitem->id, 0); } for (i = 0; i < relcache_callback_count; i++) @@ -566,98 +561,6 @@ InvalidateSystemCaches(void) } } -/* - * PrepareForTupleInvalidation - * Detect whether invalidation of this tuple implies invalidation - * of catalog/relation cache entries; if so, register inval events. - */ -static void -PrepareForTupleInvalidation(Relation relation, HeapTuple tuple) -{ - Oid tupleRelId; - Oid databaseId; - Oid relationId; - - /* Do nothing during bootstrap */ - if (IsBootstrapProcessingMode()) - return; - - /* - * We only need to worry about invalidation for tuples that are in system - * relations; user-relation tuples are never in catcaches and can't affect - * the relcache either. - */ - if (!IsSystemRelation(relation)) - return; - - /* - * TOAST tuples can likewise be ignored here. Note that TOAST tables are - * considered system relations so they are not filtered by the above test. - */ - if (IsToastRelation(relation)) - return; - - /* - * First let the catcache do its thing - */ - PrepareToInvalidateCacheTuple(relation, tuple, - RegisterCatcacheInvalidation); - - /* - * Now, is this tuple one of the primary definers of a relcache entry? - */ - tupleRelId = RelationGetRelid(relation); - - if (tupleRelId == RelationRelationId) - { - Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple); - - relationId = HeapTupleGetOid(tuple); - if (classtup->relisshared) - databaseId = InvalidOid; - else - databaseId = MyDatabaseId; - } - else if (tupleRelId == AttributeRelationId) - { - 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. (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; - - /* - * Yes. We need to register a relcache invalidation event. - */ - RegisterRelcacheInvalidation(databaseId, relationId); -} - /* ---------------------------------------------------------------- * public functions @@ -1056,11 +959,103 @@ CommandEndInvalidationMessages(void) * CacheInvalidateHeapTuple * Register the given tuple for invalidation at end of command * (ie, current command is creating or outdating this tuple). + * Also, detect whether a relcache invalidation is implied. + * + * For an insert or delete, tuple is the target tuple and newtuple is NULL. + * For an update, we are called just once, with tuple being the old tuple + * version and newtuple the new version. This allows avoidance of duplicate + * effort during an update. */ void -CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple) +CacheInvalidateHeapTuple(Relation relation, + HeapTuple tuple, + HeapTuple newtuple) { - PrepareForTupleInvalidation(relation, tuple); + Oid tupleRelId; + Oid databaseId; + Oid relationId; + + /* Do nothing during bootstrap */ + if (IsBootstrapProcessingMode()) + return; + + /* + * We only need to worry about invalidation for tuples that are in system + * relations; user-relation tuples are never in catcaches and can't affect + * the relcache either. + */ + if (!IsSystemRelation(relation)) + return; + + /* + * TOAST tuples can likewise be ignored here. Note that TOAST tables are + * considered system relations so they are not filtered by the above test. + */ + if (IsToastRelation(relation)) + return; + + /* + * First let the catcache do its thing + */ + PrepareToInvalidateCacheTuple(relation, tuple, newtuple, + RegisterCatcacheInvalidation); + + /* + * Now, is this tuple one of the primary definers of a relcache entry? + * + * Note we ignore newtuple here; we assume an update cannot move a tuple + * from being part of one relcache entry to being part of another. + */ + tupleRelId = RelationGetRelid(relation); + + if (tupleRelId == RelationRelationId) + { + Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple); + + relationId = HeapTupleGetOid(tuple); + if (classtup->relisshared) + databaseId = InvalidOid; + else + databaseId = MyDatabaseId; + } + else if (tupleRelId == AttributeRelationId) + { + 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. (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; + + /* + * Yes. We need to register a relcache invalidation event. + */ + RegisterRelcacheInvalidation(databaseId, relationId); } /* @@ -1094,7 +1089,7 @@ CacheInvalidateCatalog(Oid catalogId) * * 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.) + * entry by CacheInvalidateHeapTuple. (An example is dropping an index.) */ void CacheInvalidateRelcache(Relation relation) @@ -1216,10 +1211,14 @@ CacheInvalidateRelmap(Oid databaseId) * CacheRegisterSyscacheCallback * Register the specified function to be called for all future * invalidation events in the specified cache. The cache ID and the - * TID of the tuple being invalidated will be passed to the function. + * hash value of the tuple being invalidated will be passed to the + * function. * - * NOTE: NULL will be passed for the TID if a cache reset request is received. + * NOTE: Hash value zero will be passed if a cache reset request is received. * In this case the called routines should flush all cached state. + * Yes, there's a possibility of a false match to zero, but it doesn't seem + * worth troubling over, especially since most of the current callees just + * flush all cached state anyway. */ void CacheRegisterSyscacheCallback(int cacheid, @@ -1265,7 +1264,7 @@ CacheRegisterRelcacheCallback(RelcacheCallbackFunction func, * this module from knowing which catcache IDs correspond to which catalogs. */ void -CallSyscacheCallbacks(int cacheid, ItemPointer tuplePtr) +CallSyscacheCallbacks(int cacheid, uint32 hashvalue) { int i; @@ -1274,6 +1273,6 @@ CallSyscacheCallbacks(int cacheid, ItemPointer tuplePtr) struct SYSCACHECALLBACK *ccitem = syscache_callback_list + i; if (ccitem->id == cacheid) - (*ccitem->function) (ccitem->arg, cacheid, tuplePtr); + (*ccitem->function) (ccitem->arg, cacheid, hashvalue); } } diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index 08ddfa9bcba..1410dec1e90 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -71,8 +71,8 @@ static void ScanQueryForLocks(Query *parsetree, bool acquire); static bool ScanQueryWalker(Node *node, bool *acquire); static bool plan_list_is_transient(List *stmt_list); static void PlanCacheRelCallback(Datum arg, Oid relid); -static void PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr); -static void PlanCacheSysCallback(Datum arg, int cacheid, ItemPointer tuplePtr); +static void PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue); +static void PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue); /* @@ -1029,14 +1029,14 @@ PlanCacheRelCallback(Datum arg, Oid relid) * PlanCacheFuncCallback * Syscache inval callback function for PROCOID cache * - * Invalidate all plans mentioning the given catalog entry, or all plans - * mentioning any member of this cache if tuplePtr == NULL. + * Invalidate all plans mentioning the object with the specified hash value, + * or all plans mentioning any member of this cache if hashvalue == 0. * * Note that the coding would support use for multiple caches, but right * now only user-defined functions are tracked this way. */ static void -PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr) +PlanCacheFuncCallback(Datum arg, int cacheid, uint32 hashvalue) { ListCell *lc1; @@ -1060,8 +1060,8 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr) if (item->cacheId != cacheid) continue; - if (tuplePtr == NULL || - ItemPointerEquals(tuplePtr, &item->tupleId)) + if (hashvalue == 0 || + item->hashValue == hashvalue) { /* Invalidate the plan! */ plan->dead = true; @@ -1086,8 +1086,8 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr) if (item->cacheId != cacheid) continue; - if (tuplePtr == NULL || - ItemPointerEquals(tuplePtr, &item->tupleId)) + if (hashvalue == 0 || + item->hashValue == hashvalue) { /* Invalidate the plan! */ plan->dead = true; @@ -1108,7 +1108,7 @@ PlanCacheFuncCallback(Datum arg, int cacheid, ItemPointer tuplePtr) * Just invalidate everything... */ static void -PlanCacheSysCallback(Datum arg, int cacheid, ItemPointer tuplePtr) +PlanCacheSysCallback(Datum arg, int cacheid, uint32 hashvalue) { ResetPlanCache(); } diff --git a/src/backend/utils/cache/spccache.c b/src/backend/utils/cache/spccache.c index 57e5d0342a6..b505f219243 100644 --- a/src/backend/utils/cache/spccache.c +++ b/src/backend/utils/cache/spccache.c @@ -50,7 +50,7 @@ typedef struct * tablespaces, nor do we expect them to be frequently modified. */ static void -InvalidateTableSpaceCacheCallback(Datum arg, int cacheid, ItemPointer tuplePtr) +InvalidateTableSpaceCacheCallback(Datum arg, int cacheid, uint32 hashvalue) { HASH_SEQ_STATUS status; TableSpaceCacheEntry *spc; diff --git a/src/backend/utils/cache/ts_cache.c b/src/backend/utils/cache/ts_cache.c index a8c4d76565a..cffa2384385 100644 --- a/src/backend/utils/cache/ts_cache.c +++ b/src/backend/utils/cache/ts_cache.c @@ -90,7 +90,7 @@ static Oid TSCurrentConfigCache = InvalidOid; * table address as the "arg". */ static void -InvalidateTSCacheCallBack(Datum arg, int cacheid, ItemPointer tuplePtr) +InvalidateTSCacheCallBack(Datum arg, int cacheid, uint32 hashvalue) { HTAB *hash = (HTAB *) DatumGetPointer(arg); HASH_SEQ_STATUS status; |