/* * ResetCatalogCaches * * Reset all caches when a shared cache inval event forces it */ void ResetCatalogCaches(void) { CatCache *cache; CACHE1_elog(DEBUG2, "ResetCatalogCaches called"); for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next) ResetCatalogCache(cache); CACHE1_elog(DEBUG2, "end of ResetCatalogCaches call"); }
/* * CatalogCacheFlushCatalog * * Flush all catcache entries that came from the specified system catalog. * This is needed after VACUUM FULL/CLUSTER on the catalog, since the * tuples very likely now have different TIDs than before. (At one point * we also tried to force re-execution of CatalogCacheInitializeCache for * the cache(s) on that catalog. This is a bad idea since it leads to all * kinds of trouble if a cache flush occurs while loading cache entries. * We now avoid the need to do it by copying cc_tupdesc out of the relcache, * rather than relying on the relcache to keep a tupdesc for us. Of course * this assumes the tupdesc of a cachable system table will not change...) */ void CatalogCacheFlushCatalog(Oid catId) { CatCache *cache; CACHE2_elog(DEBUG2, "CatalogCacheFlushCatalog called for %u", catId); for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next) { /* We can ignore uninitialized caches, since they must be empty */ if (cache->cc_tupdesc == NULL) continue; /* Does this cache store tuples of the target catalog? */ if (cache->cc_tupdesc->attrs[0]->attrelid == catId) { /* Yes, so flush all its contents */ ResetCatalogCache(cache); /* Tell inval.c to call syscache callbacks for this cache */ CallSyscacheCallbacks(cache->id, NULL); } } CACHE1_elog(DEBUG2, "end of CatalogCacheFlushCatalog call"); }
/* * PrepareToInvalidateCacheTuple() * * This is part of a rather subtle chain of events, so pay attention: * * When a tuple is inserted or deleted, it cannot be flushed from the * catcaches immediately, for reasons explained at the top of cache/inval.c. * Instead we have to add entry(s) for the tuple to a list of pending tuple * invalidations that will be done at the end of the command or transaction. * * 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, * using the recorded information. * * 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 * to flush --- or other backends' caches might have such entries --- so * we have to make list entries to flush it later. * * Also note that it's not an error if there are no catcaches for the * specified relation. inval.c doesn't know exactly which rels have * catcaches --- it will call this routine for any tuple that's in a * system relation. */ void PrepareToInvalidateCacheTuple(Relation relation, HeapTuple tuple, void (*function) (int, uint32, ItemPointer, Oid)) { CatCache *ccp; Oid reloid; CACHE1_elog(DEBUG2, "PrepareToInvalidateCacheTuple: called"); /* * sanity checks */ Assert(RelationIsValid(relation)); Assert(HeapTupleIsValid(tuple)); Assert(PointerIsValid(function)); Assert(CacheHdr != NULL); reloid = RelationGetRelid(relation); /* ---------------- * for each cache * if the cache contains tuples from the specified relation * compute the tuple's hash value in this cache, * and call the passed function to register the information. * ---------------- */ for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next) { /* Just in case cache hasn't finished initialization yet... */ if (ccp->cc_tupdesc == NULL) CatalogCacheInitializeCache(ccp); if (ccp->cc_reloid != reloid) continue; (*function) (ccp->id, CatalogCacheComputeTupleHashValue(ccp, tuple), &tuple->t_self, ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId); } }
/* * CatalogCacheFlushCatalog * * Flush all catcache entries that came from the specified system catalog. * This is needed after VACUUM FULL/CLUSTER on the catalog, since the * tuples very likely now have different TIDs than before. (At one point * we also tried to force re-execution of CatalogCacheInitializeCache for * the cache(s) on that catalog. This is a bad idea since it leads to all * kinds of trouble if a cache flush occurs while loading cache entries. * We now avoid the need to do it by copying cc_tupdesc out of the relcache, * rather than relying on the relcache to keep a tupdesc for us. Of course * this assumes the tupdesc of a cachable system table will not change...) */ void CatalogCacheFlushCatalog(Oid catId) { CatCache *cache; CACHE2_elog(DEBUG2, "CatalogCacheFlushCatalog called for %u", catId); for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next) { /* Does this cache store tuples of the target catalog? */ if (cache->cc_reloid == catId) { /* Yes, so flush all its contents */ ResetCatalogCache(cache); /* Tell inval.c to call syscache callbacks for this cache */ CallSyscacheCallbacks(cache->id, 0); } } CACHE1_elog(DEBUG2, "end of CatalogCacheFlushCatalog call"); }
/* * CatalogCacheIdInvalidate * * Invalidate entries in the specified cache, given a hash value. * * We delete cache entries that match the hash value, whether positive * or negative. We don't care whether the invalidation is the result * of a tuple insertion or a deletion. * * We used to try to match positive cache entries by TID, but that is * unsafe after a VACUUM FULL on a system catalog: an inval event could * be queued before VACUUM FULL, and then processed afterwards, when the * 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. * * This routine is only quasi-public: it should only be used by inval.c. */ void CatalogCacheIdInvalidate(int cacheId, uint32 hashValue) { CatCache *ccp; CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: called"); /* * inspect caches to find the proper cache */ for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next) { Index hashIndex; Dlelem *elt, *nextelt; if (cacheId != ccp->id) continue; /* * We don't bother to check whether the cache has finished * initialization yet; if not, there will be no entries in it so no * problem. */ /* * Invalidate *all* CatCLists in this cache; it's too hard to tell * which searches might still be correct, so just zap 'em all. */ for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt) { CatCList *cl = (CatCList *) DLE_VAL(elt); nextelt = DLGetSucc(elt); if (cl->refcount > 0) cl->dead = true; else CatCacheRemoveCList(ccp, cl); } /* * inspect the proper hash bucket for tuple matches */ hashIndex = HASH_INDEX(hashValue, ccp->cc_nbuckets); for (elt = DLGetHead(&ccp->cc_bucket[hashIndex]); elt; elt = nextelt) { CatCTup *ct = (CatCTup *) DLE_VAL(elt); nextelt = DLGetSucc(elt); if (hashValue == ct->hash_value) { if (ct->refcount > 0 || (ct->c_list && ct->c_list->refcount > 0)) { ct->dead = true; /* list, if any, was marked dead above */ Assert(ct->c_list == NULL || ct->c_list->dead); } else CatCacheRemoveCTup(ccp, ct); CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: invalidated"); #ifdef CATCACHE_STATS ccp->cc_invals++; #endif /* could be multiple matches, so keep looking! */ } } break; /* need only search this one cache */ } }
/* * CatalogCacheIdInvalidate * * Invalidate entries in the specified cache, given a hash value and * item pointer. Positive entries are deleted if they match the item * pointer. Negative entries must be deleted if they match the hash * value (since we do not have the exact key of the tuple that's being * inserted). But this should only rarely result in loss of a cache * entry that could have been kept. * * Note that it's not very relevant whether the tuple identified by * the item pointer is being inserted or deleted. We don't expect to * find matching positive entries in the one case, and we don't expect * to find matching negative entries in the other; but we will do the * right things in any case. * * This routine is only quasi-public: it should only be used by inval.c. */ void CatalogCacheIdInvalidate(int cacheId, uint32 hashValue, ItemPointer pointer) { CatCache *ccp; /* * sanity checks */ #ifdef USE_ASSERT_CHECKING /* Add some debug info for MPP-5739 */ if (!ItemPointerIsValid(pointer)) { elog(LOG, "CatalogCacheIdInvalidate: cacheId %d, hash %u IP %p", cacheId, hashValue, pointer); if (pointer != NULL) { elog(LOG, "CatalogCacheIdInvalidate: bogus item (?): (blkid.hi %d blkid.lo %d posid %d)", pointer->ip_blkid.bi_hi, pointer->ip_blkid.bi_lo, pointer->ip_posid); } } #endif Assert(ItemPointerIsValid(pointer)); CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: called"); /* * inspect caches to find the proper cache */ for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next) { Index hashIndex; Dlelem *elt, *nextelt; if (cacheId != ccp->id) continue; /* * We don't bother to check whether the cache has finished * initialization yet; if not, there will be no entries in it so no * problem. */ /* * Invalidate *all* CatCLists in this cache; it's too hard to tell * which searches might still be correct, so just zap 'em all. */ for (elt = DLGetHead(&ccp->cc_lists); elt; elt = nextelt) { CatCList *cl = (CatCList *) DLE_VAL(elt); nextelt = DLGetSucc(elt); if (cl->refcount > 0) cl->dead = true; else CatCacheRemoveCList(ccp, cl); } /* * inspect the proper hash bucket for tuple matches */ hashIndex = HASH_INDEX(hashValue, ccp->cc_nbuckets); for (elt = DLGetHead(&ccp->cc_bucket[hashIndex]); elt; elt = nextelt) { CatCTup *ct = (CatCTup *) DLE_VAL(elt); nextelt = DLGetSucc(elt); if (hashValue != ct->hash_value) continue; /* ignore non-matching hash values */ if (ct->negative || ItemPointerEquals(pointer, &ct->tuple.t_self)) { if (ct->refcount > 0 || (ct->c_list && ct->c_list->refcount > 0)) { ct->dead = true; /* list, if any, was marked dead above */ Assert(ct->c_list == NULL || ct->c_list->dead); } else CatCacheRemoveCTup(ccp, ct); CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: invalidated"); #ifdef CATCACHE_STATS ccp->cc_invals++; #endif /* could be multiple matches, so keep looking! */ } } break; /* need only search this one cache */ } }
/* * CatalogCacheFlushRelation * * This is called by RelationFlushRelation() to clear out cached information * about a relation being dropped. (This could be a DROP TABLE command, * or a temp table being dropped at end of transaction, or a table created * during the current transaction that is being dropped because of abort.) * Remove all cache entries relevant to the specified relation OID. * * A special case occurs when relId is itself one of the cacheable system * tables --- although those'll never be dropped, they can get flushed from * the relcache (VACUUM causes this, for example). In that case we need * to flush all cache entries that came from that table. (At one point we * also tried to force re-execution of CatalogCacheInitializeCache for * the cache(s) on that table. This is a bad idea since it leads to all * kinds of trouble if a cache flush occurs while loading cache entries. * We now avoid the need to do it by copying cc_tupdesc out of the relcache, * rather than relying on the relcache to keep a tupdesc for us. Of course * this assumes the tupdesc of a cachable system table will not change...) */ void CatalogCacheFlushRelation(Oid relId) { CatCache *cache; CACHE2_elog(DEBUG2, "CatalogCacheFlushRelation called for %u", relId); for (cache = CacheHdr->ch_caches; cache; cache = cache->cc_next) { int i; /* We can ignore uninitialized caches, since they must be empty */ if (cache->cc_tupdesc == NULL) continue; /* Does this cache store tuples of the target relation itself? */ if (cache->cc_tupdesc->attrs[0]->attrelid == relId) { /* Yes, so flush all its contents */ ResetCatalogCache(cache); continue; } /* Does this cache store tuples associated with relations at all? */ if (cache->cc_reloidattr == 0) continue; /* nope, leave it alone */ /* Yes, scan the tuples and remove those related to relId */ for (i = 0; i < cache->cc_nbuckets; i++) { Dlelem *elt, *nextelt; for (elt = DLGetHead(&cache->cc_bucket[i]); elt; elt = nextelt) { CatCTup *ct = (CatCTup *) DLE_VAL(elt); Oid tupRelid; nextelt = DLGetSucc(elt); /* * Negative entries are never considered related to a rel, * even if the rel is part of their lookup key. */ if (ct->negative) continue; if (cache->cc_reloidattr == ObjectIdAttributeNumber) tupRelid = HeapTupleGetOid(&ct->tuple); else { bool isNull; tupRelid = DatumGetObjectId(fastgetattr(&ct->tuple, cache->cc_reloidattr, cache->cc_tupdesc, &isNull)); Assert(!isNull); } if (tupRelid == relId) { if (ct->refcount > 0) ct->dead = true; else CatCacheRemoveCTup(cache, ct); #ifdef CATCACHE_STATS cache->cc_invals++; #endif } } } } CACHE1_elog(DEBUG2, "end of CatalogCacheFlushRelation call"); }