catcache.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,777 行 · 第 1/3 页
C
1,777 行
/* * 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->dead = true; 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->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=%s id=%d nkeys=%d size=%d", \ cp->cc_relname, cp->id, cp->cc_nkeys, cp->cc_nbuckets); \} while(0)#else#define InitCatCache_DEBUG2#endifCatCache *InitCatCache(int id, const char *relname, const char *indname, int reloidattr, int nkeys, const int *key){ CatCache *cp; MemoryContext oldcxt; int i; /* * 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, including * global LRU list header */ if (CacheHdr == NULL) { CacheHdr = (CatCacheHeader *) palloc(sizeof(CatCacheHeader)); CacheHdr->ch_caches = NULL; CacheHdr->ch_ntup = 0; CacheHdr->ch_maxtup = MAXCCTUPLES; DLInitList(&CacheHdr->ch_lrulist);#ifdef CATCACHE_STATS 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) + NCCBUCKETS * 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 = relname; cp->cc_indname = indname; cp->cc_reloid = InvalidOid; /* temporary */ cp->cc_relisshared = false; /* temporary */ cp->cc_tupdesc = (TupleDesc) NULL; cp->cc_reloidattr = reloidattr; cp->cc_ntup = 0; cp->cc_nbuckets = NCCBUCKETS; 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_DEBUG2 \ elog(DEBUG2, "CatalogCacheInitializeCache: cache @%p %s", cache, \ cache->cc_relname)#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_DEBUG2#define CatalogCacheInitializeCache_DEBUG2#endifstatic voidCatalogCacheInitializeCache(CatCache *cache){ Relation relation; MemoryContext oldcxt; TupleDesc tupdesc; int i; CatalogCacheInitializeCache_DEBUG2; /* * Open the relation without locking --- we only need the tupdesc, * which we assume will never change ... */ relation = heap_openr(cache->cc_relname, NoLock); Assert(RelationIsValid(relation)); /* * switch to the cache context so our allocations do not vanish at the * end of a transaction */ Assert(CacheMemoryContext != NULL); oldcxt = MemoryContextSwitchTo(CacheMemoryContext); /* * copy the relcache's tuple descriptor to permanent cache storage */ tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation)); /* * get the relation's OID and relisshared flag, too */ cache->cc_reloid = RelationGetRelid(relation); cache->cc_relisshared = RelationGetForm(relation)->relisshared; /* * return to the caller's memory context and close the rel */ MemoryContextSwitchTo(oldcxt); heap_close(relation, NoLock); CACHE3_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys", cache->cc_relname, cache->cc_nkeys); /* * initialize cache's key information */ for (i = 0; i < cache->cc_nkeys; ++i) { Oid keytype; CatalogCacheInitializeCache_DEBUG2; if (cache->cc_key[i] > 0) keytype = tupdesc->attrs[cache->cc_key[i] - 1]->atttypid; else { if (cache->cc_key[i] != ObjectIdAttributeNumber) elog(FATAL, "only sys attr supported in caches is OID"); keytype = OIDOID; } GetCCHashEqFuncs(keytype, &cache->cc_hashfunc[i], &cache->cc_skey[i].sk_procedure); cache->cc_isname[i] = (keytype == NAMEOID); /* * Do equality-function lookup (we assume this won't need a * catalog lookup for any supported type) */ fmgr_info_cxt(cache->cc_skey[i].sk_procedure, &cache->cc_skey[i].sk_func, CacheMemoryContext); /* Initialize sk_attno suitably for HeapKeyTest() and heap scans */ cache->cc_skey[i].sk_attno = cache->cc_key[i]; CACHE4_elog(DEBUG2, "CatalogCacheInit %s %d %p", cache->cc_relname, i, cache); } /* * mark this cache fully initialized */ cache->cc_tupdesc = tupdesc;}/* * InitCatCachePhase2 -- external interface for CatalogCacheInitializeCache * * The only reason to call this routine is to ensure that the relcache * has created entries for all the catalogs and indexes referenced by * catcaches. Therefore, open the index too. An exception is the indexes * on pg_am, which we don't use (cf. IndexScanOK). */voidInitCatCachePhase2(CatCache *cache){ if (cache->cc_tupdesc == NULL) CatalogCacheInitializeCache(cache); if (cache->id != AMOID && cache->id != AMNAME) { Relation idesc; idesc = index_openr(cache->cc_indname); index_close(idesc); }}/* * IndexScanOK * * This function checks for tuples that will be fetched by * IndexSupportInitialize() during relcache initialization for * certain system indexes that support critical syscaches. * We can't use an indexscan to fetch these, else we'll get into * infinite recursion. A plain heap scan will work, however. * * Once we have completed relcache initialization (signaled by * criticalRelcachesBuilt), we don't have to worry anymore. */static boolIndexScanOK(CatCache *cache, ScanKey cur_skey){ if (cache->id == INDEXRELID) { /* * Since the OIDs of indexes aren't hardwired, it's painful to * figure out which is which. Just force all pg_index searches to * be heap scans while building the relcaches. */ if (!criticalRelcachesBuilt) return false; } else if (cache->id == AMOID || cache->id == AMNAME) { /* * Always do heap scans in pg_am, because it's so small there's * not much point in an indexscan anyway. We *must* do this when * initially building critical relcache entries, but we might as * well just always do it. */ return false; } else if (cache->id == OPEROID) { if (!criticalRelcachesBuilt) { /* Looking for an OID comparison function? */ Oid lookup_oid = DatumGetObjectId(cur_skey[0].sk_argument); if (lookup_oid >= MIN_OIDCMP && lookup_oid <= MAX_OIDCMP) return false; } } /* Normal case, allow index scan */ return true;}/* * SearchCatCache * * This call searches a system cache for a tuple, opening the relation * if necessary (on the first access to a particular cache). * * The result is NULL if not found, or a pointer to a HeapTuple in * the cache. The caller must not modify the tuple, and must call * ReleaseCatCache() when done with it. * * The search key values should be expressed as Datums of the key columns' * datatype(s). (Pass zeroes for any unused parameters.) As a special * exception, the passed-in key for a NAME column can be just a C string; * the caller need not go to the trouble of converting it to a fully * null-padded NAME. */HeapTupleSearchCatCache(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4){ ScanKeyData cur_skey[4]; uint32 hashValue; Index hashIndex; Dlelem *elt; CatCTup *ct; Relation relation; SysScanDesc scandesc; HeapTuple ntp; /* * one-time startup overhead for each cache */ if (cache->cc_tupdesc == NULL) CatalogCacheInitializeCache(cache);#ifdef CATCACHE_STATS cache->cc_searches++;#endif /* * initialize the search key information */ memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey)); cur_skey[0].sk_argument = v1; cur_skey[1].sk_argument = v2; cur_skey[2].sk_argument = v3; cur_skey[3].sk_argument = v4; /* * find the hash bucket in which to look for the tuple */ hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey); hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); /* * scan the hash bucket until we find a match or exhaust our tuples */ for (elt = DLGetHead(&cache->cc_bucket[hashIndex]); elt; elt = DLGetSucc(elt)) { bool res; ct = (CatCTup *) DLE_VAL(elt); if (ct->dead) continue; /* ignore dead entries */ if (ct->hash_value != hashValue) continue; /* quickly skip entry if wrong hash val */ /* * see if the cached tuple matches our key. */ HeapKeyTest(&ct->tuple, cache->cc_tupdesc, cache->cc_nkeys, cur_skey, res); if (!res) continue; /* * we found a match in the cache: move it to the front of the * global LRU list. We also move it to the front of the list for * its hashbucket, in order to speed subsequent searches. (The * most frequently accessed elements in any hashbucket will tend * to be near the front of the hashbucket's list.) */ DLMoveToFront(&ct->lrulist_elem); DLMoveToFront(&ct->cache_elem); /* * If it's a positive entry, bump its refcount and return it. If * it's negative, we can report failure to the caller. */ if (!ct->negative) { ct->refcount++; CACHE3_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d", cache->cc_relname, hashIndex);#ifdef CATCACHE_STATS cache->cc_hits++;#endif return &ct->tuple; } else { CACHE3_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d", cache->cc_relname, hashIndex);#ifdef CATCACHE_STATS cache->cc_neg_hits++;#endif return NULL; } } /* * Tuple was not found in cache, so we have to try to retrieve it * directly from the relation. If found, we will add it to the cache; * if not found, we will add a negative cache entry instead.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?