catcache.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,777 行 · 第 1/3 页
C
1,777 行
* * NOTE: it is possible for recursive cache lookups to occur while * reading the relation --- for example, due to shared-cache-inval * messages being processed during heap_open(). This is OK. It's * even possible for one of those lookups to find and enter the very * same tuple we are trying to fetch here. If that happens, we will * enter a second copy of the tuple into the cache. The first copy * will never be referenced again, and will eventually age out of the * cache, so there's no functional problem. This case is rare enough * that it's not worth expending extra cycles to detect. */ relation = heap_open(cache->cc_reloid, AccessShareLock); scandesc = systable_beginscan(relation, cache->cc_indname, IndexScanOK(cache, cur_skey), SnapshotNow, cache->cc_nkeys, cur_skey); ct = NULL; while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) { ct = CatalogCacheCreateEntry(cache, ntp, hashValue, hashIndex, false); break; /* assume only one match */ } systable_endscan(scandesc); heap_close(relation, AccessShareLock); /* * If tuple was not found, we need to build a negative cache entry * containing a fake tuple. The fake tuple has the correct key * columns, but nulls everywhere else. */ if (ct == NULL) { ntp = build_dummy_tuple(cache, cache->cc_nkeys, cur_skey); ct = CatalogCacheCreateEntry(cache, ntp, hashValue, hashIndex, true); heap_freetuple(ntp); CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples", cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup); CACHE3_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d", cache->cc_relname, hashIndex); /* * We are not returning the new entry to the caller, so reset its * refcount. */ ct->refcount = 0; /* negative entries never have refs */ return NULL; } CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples", cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup); CACHE3_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d", cache->cc_relname, hashIndex);#ifdef CATCACHE_STATS cache->cc_newloads++;#endif return &ct->tuple;}/* * ReleaseCatCache * * Decrement the reference count of a catcache entry (releasing the * hold grabbed by a successful SearchCatCache). * * NOTE: if compiled with -DCATCACHE_FORCE_RELEASE then catcache entries * will be freed as soon as their refcount goes to zero. In combination * with aset.c's CLOBBER_FREED_MEMORY option, this provides a good test * to catch references to already-released catcache entries. */voidReleaseCatCache(HeapTuple tuple){ CatCTup *ct = (CatCTup *) (((char *) tuple) - offsetof(CatCTup, tuple)); /* Safety checks to ensure we were handed a cache entry */ Assert(ct->ct_magic == CT_MAGIC); Assert(ct->refcount > 0); ct->refcount--; if (ct->refcount == 0#ifndef CATCACHE_FORCE_RELEASE && ct->dead#endif ) CatCacheRemoveCTup(ct->my_cache, ct);}/* * SearchCatCacheList * * Generate a list of all tuples matching a partial key (that is, * a key specifying just the first K of the cache's N key columns). * * The caller must not modify the list object or the pointed-to tuples, * and must call ReleaseCatCacheList() when done with the list. */CatCList *SearchCatCacheList(CatCache *cache, int nkeys, Datum v1, Datum v2, Datum v3, Datum v4){ ScanKeyData cur_skey[4]; uint32 lHashValue; Dlelem *elt; CatCList *cl; CatCTup *ct; List *ctlist; int nmembers; Relation relation; SysScanDesc scandesc; bool ordered; HeapTuple ntp; MemoryContext oldcxt; int i; /* * one-time startup overhead for each cache */ if (cache->cc_tupdesc == NULL) CatalogCacheInitializeCache(cache); Assert(nkeys > 0 && nkeys < cache->cc_nkeys);#ifdef CATCACHE_STATS cache->cc_lsearches++;#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; /* * compute a hash value of the given keys for faster search. We don't * presently divide the CatCList items into buckets, but this still * lets us skip non-matching items quickly most of the time. */ lHashValue = CatalogCacheComputeHashValue(cache, nkeys, cur_skey); /* * scan the items until we find a match or exhaust our list */ for (elt = DLGetHead(&cache->cc_lists); elt; elt = DLGetSucc(elt)) { bool res; cl = (CatCList *) DLE_VAL(elt); if (cl->dead) continue; /* ignore dead entries */ if (cl->hash_value != lHashValue) continue; /* quickly skip entry if wrong hash val */ /* * see if the cached list matches our key. */ if (cl->nkeys != nkeys) continue; HeapKeyTest(&cl->tuple, cache->cc_tupdesc, nkeys, cur_skey, res); if (!res) continue; /* * we found a matching list: move each of its members to the front * of the global LRU list. Also move the list itself to the front * of the cache's list-of-lists, to speed subsequent searches. (We * do not move the members to the fronts of their hashbucket * lists, however, since there's no point in that unless they are * searched for individually.) Also bump the members' refcounts. */ for (i = 0; i < cl->n_members; i++) { cl->members[i]->refcount++; DLMoveToFront(&cl->members[i]->lrulist_elem); } DLMoveToFront(&cl->cache_elem); /* Bump the list's refcount and return it */ cl->refcount++; CACHE2_elog(DEBUG2, "SearchCatCacheList(%s): found list", cache->cc_relname);#ifdef CATCACHE_STATS cache->cc_lhits++;#endif return cl; } /* * List was not found in cache, so we have to build it by reading the * relation. For each matching tuple found in the relation, use an * existing cache entry if possible, else build a new one. */ relation = heap_open(cache->cc_reloid, AccessShareLock); scandesc = systable_beginscan(relation, cache->cc_indname, true, SnapshotNow, nkeys, cur_skey); /* The list will be ordered iff we are doing an index scan */ ordered = (scandesc->irel != NULL); ctlist = NIL; nmembers = 0; while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) { uint32 hashValue; Index hashIndex; /* * See if there's an entry for this tuple already. */ ct = NULL; hashValue = CatalogCacheComputeTupleHashValue(cache, ntp); hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); for (elt = DLGetHead(&cache->cc_bucket[hashIndex]); elt; elt = DLGetSucc(elt)) { ct = (CatCTup *) DLE_VAL(elt); if (ct->dead || ct->negative) continue; /* ignore dead and negative entries */ if (ct->hash_value != hashValue) continue; /* quickly skip entry if wrong hash val */ if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self))) continue; /* not same tuple */ /* * Found a match, but can't use it if it belongs to another * list already */ if (ct->c_list) continue; /* Found a match, so bump its refcount and move to front */ ct->refcount++; DLMoveToFront(&ct->lrulist_elem); break; } if (elt == NULL) { /* We didn't find a usable entry, so make a new one */ ct = CatalogCacheCreateEntry(cache, ntp, hashValue, hashIndex, false); } ctlist = lcons(ct, ctlist); nmembers++; } systable_endscan(scandesc); heap_close(relation, AccessShareLock); /* * Now we can build the CatCList entry. First we need a dummy tuple * containing the key values... */ ntp = build_dummy_tuple(cache, nkeys, cur_skey); oldcxt = MemoryContextSwitchTo(CacheMemoryContext); cl = (CatCList *) palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *)); heap_copytuple_with_tuple(ntp, &cl->tuple); MemoryContextSwitchTo(oldcxt); heap_freetuple(ntp); cl->cl_magic = CL_MAGIC; cl->my_cache = cache; DLInitElem(&cl->cache_elem, (void *) cl); cl->refcount = 1; /* count this first reference */ cl->dead = false; cl->ordered = ordered; cl->nkeys = nkeys; cl->hash_value = lHashValue; cl->n_members = nmembers; /* The list is backwards because we built it with lcons */ for (i = nmembers; --i >= 0;) { cl->members[i] = ct = (CatCTup *) lfirst(ctlist); Assert(ct->c_list == NULL); ct->c_list = cl; /* mark list dead if any members already dead */ if (ct->dead) cl->dead = true; ctlist = lnext(ctlist); } DLAddHead(&cache->cc_lists, &cl->cache_elem); CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members", cache->cc_relname, nmembers); return cl;}/* * ReleaseCatCacheList * * Decrement the reference counts of a catcache list. */voidReleaseCatCacheList(CatCList *list){ int i; /* Safety checks to ensure we were handed a cache entry */ Assert(list->cl_magic == CL_MAGIC); Assert(list->refcount > 0); for (i = list->n_members; --i >= 0;) { CatCTup *ct = list->members[i]; Assert(ct->refcount > 0); ct->refcount--; if (ct->dead) list->dead = true; /* can't remove tuple before list is removed */ } list->refcount--; if (list->refcount == 0#ifndef CATCACHE_FORCE_RELEASE && list->dead#endif ) CatCacheRemoveCList(list->my_cache, list);}/* * CatalogCacheCreateEntry * Create a new CatCTup entry, copying the given HeapTuple and other * supplied data into it. The new entry is given refcount 1. */static CatCTup *CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, uint32 hashValue, Index hashIndex, bool negative){ CatCTup *ct; MemoryContext oldcxt; /* * Allocate CatCTup header in cache memory, and copy the tuple there * too. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); ct = (CatCTup *) palloc(sizeof(CatCTup)); heap_copytuple_with_tuple(ntp, &ct->tuple); MemoryContextSwitchTo(oldcxt); /* * Finish initializing the CatCTup header, and add it to the cache's * linked lists and counts. */ ct->ct_magic = CT_MAGIC; ct->my_cache = cache; DLInitElem(&ct->lrulist_elem, (void *) ct); DLInitElem(&ct->cache_elem, (void *) ct); ct->c_list = NULL; ct->refcount = 1; /* count this first reference */ ct->dead = false; ct->negative = negative; ct->hash_value = hashValue; DLAddHead(&CacheHdr->ch_lrulist, &ct->lrulist_elem); DLAddHead(&cache->cc_bucket[hashIndex], &ct->cache_elem); cache->cc_ntup++; CacheHdr->ch_ntup++; /* * If we've exceeded the desired size of the caches, try to throw away * the least recently used entry. NB: the newly-built entry cannot * get thrown away here, because it has positive refcount. */ if (CacheHdr->ch_ntup > CacheHdr->ch_maxtup) { Dlelem *elt, *prevelt; for (elt = DLGetTail(&CacheHdr->ch_lrulist); elt; elt = prevelt) { CatCTup *oldct = (CatCTup *) DLE_VAL(elt); prevelt = DLGetPred(elt); if (oldct->refcount == 0) { CACHE2_elog(DEBUG2, "CatCacheCreateEntry(%s): Overflow, LRU removal", cache->cc_relname);#ifdef CATCACHE_STATS oldct->my_cache->cc_discards++;#endif CatCacheRemoveCTup(oldct->my_cache, oldct); if (CacheHdr->ch_ntup <= CacheHdr->ch_maxtup) break; } } } return ct;}/* * build_dummy_tuple * Generate a palloc'd HeapTuple that contains the specified key * columns, and NULLs for other columns. * * This is used to store the keys for negative cache entries and CatCList * entries, which don't have real tuples associated with them. */static HeapTuplebuild_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys){ HeapTuple ntp; TupleDesc tupDesc = cache->cc_tupdesc; Datum *values; char *nulls; Oid tupOid = InvalidOid; NameData tempNames[4]; int i; values = (Datum *) palloc(tupDesc->natts * sizeof(Datum)); nulls = (char *) palloc(tupDesc->natts * sizeof(char)); memset(values, 0, tupDesc->natts * sizeof(Datum)); memset(nulls, 'n', tupDesc->natts * sizeof(char)); for (i = 0; i < nkeys; i++) { int attindex = cache->cc_key[i]; Datum keyval = skeys[i].sk_argument; if (attindex > 0) { /* * Here we must be careful in case the caller passed a C * string where a NAME is wanted: convert the given argument * to a correctly padded NAME. Otherwise the memcpy() done in * heap_formtuple could fall off the end of memory. */ if (cache->cc_isname[i]) { Name newval = &tempNames[i]; namestrcpy(newval, DatumGetCString(keyval)); keyval = NameGetDatum(newval); } values[attindex - 1] = keyval; nulls[attindex - 1] = ' '; } else { Assert(attindex == ObjectIdAttributeNumber); tupOid = DatumGetObjectId(keyval); } } ntp = heap_formtuple(tupDesc, values, nulls); if (tupOid != InvalidOid) HeapTupleSetOid(ntp, tupOid); pfree(values); pfree(nulls); return ntp;}/* * 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. */voidPrepareToInvalidateCacheTuple(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); }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?