📄 catcache.c
字号:
* 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 */ }}/* ---------------------------------------------------------------- * public functions * ---------------------------------------------------------------- *//* * Standard routine for creating cache context if it doesn't exist yet * * There are a lot of places (probably far more than necessary) that check * whether CacheMemoryContext exists yet and want to create it if not. * We centralize knowledge of exactly how to create it here. */voidCreateCacheMemoryContext(void){ /* * Purely for paranoia, check that context doesn't exist; caller probably * did so already. */ if (!CacheMemoryContext) CacheMemoryContext = AllocSetContextCreate(TopMemoryContext, "CacheMemoryContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);}/* * AtEOXact_CatCache * * Clean up catcaches at end of main transaction (either commit or abort) * * As of PostgreSQL 8.1, catcache pins should get released by the * ResourceOwner mechanism. This routine is just a debugging * cross-check that no pins remain. */voidAtEOXact_CatCache(bool isCommit){#ifdef USE_ASSERT_CHECKING if (assert_enabled) { CatCache *ccp; for (ccp = CacheHdr->ch_caches; ccp; ccp = ccp->cc_next) { Dlelem *elt; int i; /* Check CatCLists */ for (elt = DLGetHead(&ccp->cc_lists); elt; elt = DLGetSucc(elt)) { CatCList *cl = (CatCList *) DLE_VAL(elt); Assert(cl->cl_magic == CL_MAGIC); Assert(cl->refcount == 0); Assert(!cl->dead); } /* Check individual tuples */ for (i = 0; i < ccp->cc_nbuckets; i++) { for (elt = DLGetHead(&ccp->cc_bucket[i]); elt; elt = DLGetSucc(elt)) { CatCTup *ct = (CatCTup *) DLE_VAL(elt); Assert(ct->ct_magic == CT_MAGIC); Assert(ct->refcount == 0); Assert(!ct->dead); } } } }#endif}/* * ResetCatalogCache * * Reset one catalog cache to empty. * * This is not very efficient if the target cache is nearly empty. * However, it shouldn't need to be efficient; we don't invoke it often. */static voidResetCatalogCache(CatCache *cache){ Dlelem *elt, *nextelt; int i; /* Remove each list in this cache, or at least mark it dead */ for (elt = DLGetHead(&cache->cc_lists); elt; elt = nextelt) { CatCList *cl = (CatCList *) DLE_VAL(elt); nextelt = DLGetSucc(elt); if (cl->refcount > 0) cl->dead = true; else CatCacheRemoveCList(cache, cl); } /* Remove each tuple in this cache, or at least mark it dead */ for (i = 0; i < cache->cc_nbuckets; i++) { for (elt = DLGetHead(&cache->cc_bucket[i]); elt; elt = nextelt) { CatCTup *ct = (CatCTup *) DLE_VAL(elt); nextelt = DLGetSucc(elt); 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(cache, ct);#ifdef CATCACHE_STATS cache->cc_invals++;#endif } }}/* * ResetCatalogCaches * * Reset all caches when a shared cache inval event forces it */voidResetCatalogCaches(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");}/* * 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...) */voidCatalogCacheFlushRelation(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->c_list && ct->c_list->refcount > 0)) { ct->dead = true; /* parent list must be considered dead too */ if (ct->c_list) ct->c_list->dead = true; } else CatCacheRemoveCTup(cache, ct);#ifdef CATCACHE_STATS cache->cc_invals++;#endif } } } } CACHE1_elog(DEBUG2, "end of CatalogCacheFlushRelation call");}/* * InitCatCache * * This allocates and initializes a cache for a system catalog relation. * Actually, the cache is only partially initialized to avoid opening the * relation. The relation will be opened and the rest of the cache * structure initialized on the first access. */#ifdef CACHEDEBUG#define InitCatCache_DEBUG2 \do { \ elog(DEBUG2, "InitCatCache: rel=%u ind=%u id=%d nkeys=%d size=%d", \ cp->cc_reloid, cp->cc_indexoid, cp->id, \ cp->cc_nkeys, cp->cc_nbuckets); \} while(0)#else#define InitCatCache_DEBUG2#endifCatCache *InitCatCache(int id, Oid reloid, Oid indexoid, int reloidattr, int nkeys, const int *key, int nbuckets){ CatCache *cp; MemoryContext oldcxt; int i; /* * nbuckets is the number of hash buckets to use in this catcache. * Currently we just use a hard-wired estimate of an appropriate size for * each cache; maybe later make them dynamically resizable? * * nbuckets must be a power of two. We check this via Assert rather than * a full runtime check because the values will be coming from constant * tables. * * If you're confused by the power-of-two check, see comments in * bitmapset.c for an explanation. */ Assert(nbuckets > 0 && (nbuckets & -nbuckets) == nbuckets); /* * first switch to the cache context so our allocations do not vanish at * the end of a transaction */ if (!CacheMemoryContext) CreateCacheMemoryContext(); oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* * if first time through, initialize the cache group header */ if (CacheHdr == NULL) { CacheHdr = (CatCacheHeader *) palloc(sizeof(CatCacheHeader)); CacheHdr->ch_caches = NULL; CacheHdr->ch_ntup = 0;#ifdef CATCACHE_STATS /* set up to dump stats at backend exit */ on_proc_exit(CatCachePrintStats, 0);#endif } /* * allocate a new cache structure * * Note: we assume zeroing initializes the Dllist headers correctly */ cp = (CatCache *) palloc0(sizeof(CatCache) + nbuckets * sizeof(Dllist)); /* * initialize the cache's relation information for the relation * corresponding to this cache, and initialize some of the new cache's * other internal fields. But don't open the relation yet. */ cp->id = id; cp->cc_relname = "(not known yet)"; cp->cc_reloid = reloid; cp->cc_indexoid = indexoid; cp->cc_relisshared = false; /* temporary */ cp->cc_tupdesc = (TupleDesc) NULL; cp->cc_reloidattr = reloidattr; cp->cc_ntup = 0; cp->cc_nbuckets = nbuckets; cp->cc_nkeys = nkeys; for (i = 0; i < nkeys; ++i) cp->cc_key[i] = key[i]; /* * new cache is initialized as far as we can go for now. print some * debugging information, if appropriate. */ InitCatCache_DEBUG2; /* * add completed cache to top of group header's list */ cp->cc_next = CacheHdr->ch_caches; CacheHdr->ch_caches = cp; /* * back to the old context before we return... */ MemoryContextSwitchTo(oldcxt); return cp;}/* * CatalogCacheInitializeCache * * This function does final initialization of a catcache: obtain the tuple * descriptor and set up the hash and equality function links. We assume * that the relcache entry can be opened at this point! */#ifdef CACHEDEBUG#define CatalogCacheInitializeCache_DEBUG1 \ elog(DEBUG2, "CatalogCacheInitializeCache: cache @%p rel=%u", cache, \ cache->cc_reloid)#define CatalogCacheInitializeCache_DEBUG2 \do { \ if (cache->cc_key[i] > 0) { \ elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d, %u", \ i+1, cache->cc_nkeys, cache->cc_key[i], \ tupdesc->attrs[cache->cc_key[i] - 1]->atttypid); \ } else { \ elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d", \ i+1, cache->cc_nkeys, cache->cc_key[i]); \ } \} while(0)#else#define CatalogCacheInitializeCache_DEBUG1#define CatalogCacheInitializeCache_DEBUG2#endifstatic voidCatalogCacheInitializeCache(CatCache *cache){ Relation relation; MemoryContext oldcxt; TupleDesc tupdesc; int i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -