00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "postgres.h"
00016
00017 #include "access/genam.h"
00018 #include "access/hash.h"
00019 #include "access/heapam.h"
00020 #include "access/relscan.h"
00021 #include "access/sysattr.h"
00022 #include "access/tuptoaster.h"
00023 #include "access/valid.h"
00024 #include "catalog/pg_operator.h"
00025 #include "catalog/pg_type.h"
00026 #include "miscadmin.h"
00027 #ifdef CATCACHE_STATS
00028 #include "storage/ipc.h"
00029 #endif
00030 #include "storage/lmgr.h"
00031 #include "utils/builtins.h"
00032 #include "utils/fmgroids.h"
00033 #include "utils/inval.h"
00034 #include "utils/memutils.h"
00035 #include "utils/rel.h"
00036 #include "utils/resowner_private.h"
00037 #include "utils/syscache.h"
00038 #include "utils/tqual.h"
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 #define HASH_INDEX(h, sz) ((Index) ((h) & ((sz) - 1)))
00049
00050
00051
00052
00053
00054
00055 #ifdef CACHEDEBUG
00056 #define CACHE1_elog(a,b) elog(a,b)
00057 #define CACHE2_elog(a,b,c) elog(a,b,c)
00058 #define CACHE3_elog(a,b,c,d) elog(a,b,c,d)
00059 #define CACHE4_elog(a,b,c,d,e) elog(a,b,c,d,e)
00060 #define CACHE5_elog(a,b,c,d,e,f) elog(a,b,c,d,e,f)
00061 #define CACHE6_elog(a,b,c,d,e,f,g) elog(a,b,c,d,e,f,g)
00062 #else
00063 #define CACHE1_elog(a,b)
00064 #define CACHE2_elog(a,b,c)
00065 #define CACHE3_elog(a,b,c,d)
00066 #define CACHE4_elog(a,b,c,d,e)
00067 #define CACHE5_elog(a,b,c,d,e,f)
00068 #define CACHE6_elog(a,b,c,d,e,f,g)
00069 #endif
00070
00071
00072 static CatCacheHeader *CacheHdr = NULL;
00073
00074
00075 static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
00076 ScanKey cur_skey);
00077 static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache,
00078 HeapTuple tuple);
00079
00080 #ifdef CATCACHE_STATS
00081 static void CatCachePrintStats(int code, Datum arg);
00082 #endif
00083 static void CatCacheRemoveCTup(CatCache *cache, CatCTup *ct);
00084 static void CatCacheRemoveCList(CatCache *cache, CatCList *cl);
00085 static void CatalogCacheInitializeCache(CatCache *cache);
00086 static CatCTup *CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
00087 uint32 hashValue, Index hashIndex,
00088 bool negative);
00089 static HeapTuple build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys);
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103 static void
00104 GetCCHashEqFuncs(Oid keytype, PGFunction *hashfunc, RegProcedure *eqfunc)
00105 {
00106 switch (keytype)
00107 {
00108 case BOOLOID:
00109 *hashfunc = hashchar;
00110
00111 *eqfunc = F_BOOLEQ;
00112 break;
00113 case CHAROID:
00114 *hashfunc = hashchar;
00115
00116 *eqfunc = F_CHAREQ;
00117 break;
00118 case NAMEOID:
00119 *hashfunc = hashname;
00120
00121 *eqfunc = F_NAMEEQ;
00122 break;
00123 case INT2OID:
00124 *hashfunc = hashint2;
00125
00126 *eqfunc = F_INT2EQ;
00127 break;
00128 case INT2VECTOROID:
00129 *hashfunc = hashint2vector;
00130
00131 *eqfunc = F_INT2VECTOREQ;
00132 break;
00133 case INT4OID:
00134 *hashfunc = hashint4;
00135
00136 *eqfunc = F_INT4EQ;
00137 break;
00138 case TEXTOID:
00139 *hashfunc = hashtext;
00140
00141 *eqfunc = F_TEXTEQ;
00142 break;
00143 case OIDOID:
00144 case REGPROCOID:
00145 case REGPROCEDUREOID:
00146 case REGOPEROID:
00147 case REGOPERATOROID:
00148 case REGCLASSOID:
00149 case REGTYPEOID:
00150 case REGCONFIGOID:
00151 case REGDICTIONARYOID:
00152 *hashfunc = hashoid;
00153
00154 *eqfunc = F_OIDEQ;
00155 break;
00156 case OIDVECTOROID:
00157 *hashfunc = hashoidvector;
00158
00159 *eqfunc = F_OIDVECTOREQ;
00160 break;
00161 default:
00162 elog(FATAL, "type %u not supported as catcache key", keytype);
00163 *hashfunc = NULL;
00164
00165 *eqfunc = InvalidOid;
00166 break;
00167 }
00168 }
00169
00170
00171
00172
00173
00174
00175 static uint32
00176 CatalogCacheComputeHashValue(CatCache *cache, int nkeys, ScanKey cur_skey)
00177 {
00178 uint32 hashValue = 0;
00179 uint32 oneHash;
00180
00181 CACHE4_elog(DEBUG2, "CatalogCacheComputeHashValue %s %d %p",
00182 cache->cc_relname,
00183 nkeys,
00184 cache);
00185
00186 switch (nkeys)
00187 {
00188 case 4:
00189 oneHash =
00190 DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[3],
00191 cur_skey[3].sk_argument));
00192 hashValue ^= oneHash << 24;
00193 hashValue ^= oneHash >> 8;
00194
00195 case 3:
00196 oneHash =
00197 DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[2],
00198 cur_skey[2].sk_argument));
00199 hashValue ^= oneHash << 16;
00200 hashValue ^= oneHash >> 16;
00201
00202 case 2:
00203 oneHash =
00204 DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[1],
00205 cur_skey[1].sk_argument));
00206 hashValue ^= oneHash << 8;
00207 hashValue ^= oneHash >> 24;
00208
00209 case 1:
00210 oneHash =
00211 DatumGetUInt32(DirectFunctionCall1(cache->cc_hashfunc[0],
00212 cur_skey[0].sk_argument));
00213 hashValue ^= oneHash;
00214 break;
00215 default:
00216 elog(FATAL, "wrong number of hash keys: %d", nkeys);
00217 break;
00218 }
00219
00220 return hashValue;
00221 }
00222
00223
00224
00225
00226
00227
00228 static uint32
00229 CatalogCacheComputeTupleHashValue(CatCache *cache, HeapTuple tuple)
00230 {
00231 ScanKeyData cur_skey[CATCACHE_MAXKEYS];
00232 bool isNull = false;
00233
00234
00235 memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
00236
00237
00238 switch (cache->cc_nkeys)
00239 {
00240 case 4:
00241 cur_skey[3].sk_argument =
00242 (cache->cc_key[3] == ObjectIdAttributeNumber)
00243 ? ObjectIdGetDatum(HeapTupleGetOid(tuple))
00244 : fastgetattr(tuple,
00245 cache->cc_key[3],
00246 cache->cc_tupdesc,
00247 &isNull);
00248 Assert(!isNull);
00249
00250 case 3:
00251 cur_skey[2].sk_argument =
00252 (cache->cc_key[2] == ObjectIdAttributeNumber)
00253 ? ObjectIdGetDatum(HeapTupleGetOid(tuple))
00254 : fastgetattr(tuple,
00255 cache->cc_key[2],
00256 cache->cc_tupdesc,
00257 &isNull);
00258 Assert(!isNull);
00259
00260 case 2:
00261 cur_skey[1].sk_argument =
00262 (cache->cc_key[1] == ObjectIdAttributeNumber)
00263 ? ObjectIdGetDatum(HeapTupleGetOid(tuple))
00264 : fastgetattr(tuple,
00265 cache->cc_key[1],
00266 cache->cc_tupdesc,
00267 &isNull);
00268 Assert(!isNull);
00269
00270 case 1:
00271 cur_skey[0].sk_argument =
00272 (cache->cc_key[0] == ObjectIdAttributeNumber)
00273 ? ObjectIdGetDatum(HeapTupleGetOid(tuple))
00274 : fastgetattr(tuple,
00275 cache->cc_key[0],
00276 cache->cc_tupdesc,
00277 &isNull);
00278 Assert(!isNull);
00279 break;
00280 default:
00281 elog(FATAL, "wrong number of hash keys: %d", cache->cc_nkeys);
00282 break;
00283 }
00284
00285 return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);
00286 }
00287
00288
00289 #ifdef CATCACHE_STATS
00290
00291 static void
00292 CatCachePrintStats(int code, Datum arg)
00293 {
00294 slist_iter iter;
00295 long cc_searches = 0;
00296 long cc_hits = 0;
00297 long cc_neg_hits = 0;
00298 long cc_newloads = 0;
00299 long cc_invals = 0;
00300 long cc_lsearches = 0;
00301 long cc_lhits = 0;
00302
00303 slist_foreach(iter, &CacheHdr->ch_caches)
00304 {
00305 CatCache *cache = slist_container(CatCache, cc_next, iter.cur);
00306
00307 if (cache->cc_ntup == 0 && cache->cc_searches == 0)
00308 continue;
00309 elog(DEBUG2, "catcache %s/%u: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld lsrch, %ld lhits",
00310 cache->cc_relname,
00311 cache->cc_indexoid,
00312 cache->cc_ntup,
00313 cache->cc_searches,
00314 cache->cc_hits,
00315 cache->cc_neg_hits,
00316 cache->cc_hits + cache->cc_neg_hits,
00317 cache->cc_newloads,
00318 cache->cc_searches - cache->cc_hits - cache->cc_neg_hits - cache->cc_newloads,
00319 cache->cc_searches - cache->cc_hits - cache->cc_neg_hits,
00320 cache->cc_invals,
00321 cache->cc_lsearches,
00322 cache->cc_lhits);
00323 cc_searches += cache->cc_searches;
00324 cc_hits += cache->cc_hits;
00325 cc_neg_hits += cache->cc_neg_hits;
00326 cc_newloads += cache->cc_newloads;
00327 cc_invals += cache->cc_invals;
00328 cc_lsearches += cache->cc_lsearches;
00329 cc_lhits += cache->cc_lhits;
00330 }
00331 elog(DEBUG2, "catcache totals: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld lsrch, %ld lhits",
00332 CacheHdr->ch_ntup,
00333 cc_searches,
00334 cc_hits,
00335 cc_neg_hits,
00336 cc_hits + cc_neg_hits,
00337 cc_newloads,
00338 cc_searches - cc_hits - cc_neg_hits - cc_newloads,
00339 cc_searches - cc_hits - cc_neg_hits,
00340 cc_invals,
00341 cc_lsearches,
00342 cc_lhits);
00343 }
00344 #endif
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355 static void
00356 CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
00357 {
00358 Assert(ct->refcount == 0);
00359 Assert(ct->my_cache == cache);
00360
00361 if (ct->c_list)
00362 {
00363
00364
00365
00366
00367
00368 ct->dead = true;
00369 CatCacheRemoveCList(cache, ct->c_list);
00370 return;
00371 }
00372
00373
00374 dlist_delete(&ct->cache_elem);
00375
00376
00377 if (ct->tuple.t_data != NULL)
00378 pfree(ct->tuple.t_data);
00379 pfree(ct);
00380
00381 --cache->cc_ntup;
00382 --CacheHdr->ch_ntup;
00383 }
00384
00385
00386
00387
00388
00389
00390
00391
00392 static void
00393 CatCacheRemoveCList(CatCache *cache, CatCList *cl)
00394 {
00395 int i;
00396
00397 Assert(cl->refcount == 0);
00398 Assert(cl->my_cache == cache);
00399
00400
00401 for (i = cl->n_members; --i >= 0;)
00402 {
00403 CatCTup *ct = cl->members[i];
00404
00405 Assert(ct->c_list == cl);
00406 ct->c_list = NULL;
00407
00408 if (
00409 #ifndef CATCACHE_FORCE_RELEASE
00410 ct->dead &&
00411 #endif
00412 ct->refcount == 0)
00413 CatCacheRemoveCTup(cache, ct);
00414 }
00415
00416
00417 dlist_delete(&cl->cache_elem);
00418
00419
00420 if (cl->tuple.t_data != NULL)
00421 pfree(cl->tuple.t_data);
00422 pfree(cl);
00423 }
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444 void
00445 CatalogCacheIdInvalidate(int cacheId, uint32 hashValue)
00446 {
00447 slist_iter cache_iter;
00448
00449 CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: called");
00450
00451
00452
00453
00454 slist_foreach(cache_iter, &CacheHdr->ch_caches)
00455 {
00456 CatCache *ccp = slist_container(CatCache, cc_next, cache_iter.cur);
00457 Index hashIndex;
00458 dlist_mutable_iter iter;
00459
00460 if (cacheId != ccp->id)
00461 continue;
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473 dlist_foreach_modify(iter, &ccp->cc_lists)
00474 {
00475 CatCList *cl = dlist_container(CatCList, cache_elem, iter.cur);
00476
00477 if (cl->refcount > 0)
00478 cl->dead = true;
00479 else
00480 CatCacheRemoveCList(ccp, cl);
00481 }
00482
00483
00484
00485
00486 hashIndex = HASH_INDEX(hashValue, ccp->cc_nbuckets);
00487 dlist_foreach_modify(iter, &ccp->cc_bucket[hashIndex])
00488 {
00489 CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
00490
00491 if (hashValue == ct->hash_value)
00492 {
00493 if (ct->refcount > 0 ||
00494 (ct->c_list && ct->c_list->refcount > 0))
00495 {
00496 ct->dead = true;
00497
00498 Assert(ct->c_list == NULL || ct->c_list->dead);
00499 }
00500 else
00501 CatCacheRemoveCTup(ccp, ct);
00502 CACHE1_elog(DEBUG2, "CatalogCacheIdInvalidate: invalidated");
00503 #ifdef CATCACHE_STATS
00504 ccp->cc_invals++;
00505 #endif
00506
00507 }
00508 }
00509 break;
00510 }
00511 }
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526 void
00527 CreateCacheMemoryContext(void)
00528 {
00529
00530
00531
00532
00533 if (!CacheMemoryContext)
00534 CacheMemoryContext = AllocSetContextCreate(TopMemoryContext,
00535 "CacheMemoryContext",
00536 ALLOCSET_DEFAULT_MINSIZE,
00537 ALLOCSET_DEFAULT_INITSIZE,
00538 ALLOCSET_DEFAULT_MAXSIZE);
00539 }
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551 void
00552 AtEOXact_CatCache(bool isCommit)
00553 {
00554 #ifdef USE_ASSERT_CHECKING
00555 if (assert_enabled)
00556 {
00557 slist_iter cache_iter;
00558
00559 slist_foreach(cache_iter, &CacheHdr->ch_caches)
00560 {
00561 CatCache *ccp = slist_container(CatCache, cc_next, cache_iter.cur);
00562 dlist_iter iter;
00563 int i;
00564
00565
00566 dlist_foreach(iter, &ccp->cc_lists)
00567 {
00568 CatCList *cl = dlist_container(CatCList, cache_elem, iter.cur);
00569
00570 Assert(cl->cl_magic == CL_MAGIC);
00571 Assert(cl->refcount == 0);
00572 Assert(!cl->dead);
00573 }
00574
00575
00576 for (i = 0; i < ccp->cc_nbuckets; i++)
00577 {
00578 dlist_head *bucket = &ccp->cc_bucket[i];
00579
00580 dlist_foreach(iter, bucket)
00581 {
00582 CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
00583
00584 Assert(ct->ct_magic == CT_MAGIC);
00585 Assert(ct->refcount == 0);
00586 Assert(!ct->dead);
00587 }
00588 }
00589 }
00590 }
00591 #endif
00592 }
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602 static void
00603 ResetCatalogCache(CatCache *cache)
00604 {
00605 dlist_mutable_iter iter;
00606 int i;
00607
00608
00609 dlist_foreach_modify(iter, &cache->cc_lists)
00610 {
00611 CatCList *cl = dlist_container(CatCList, cache_elem, iter.cur);
00612
00613 if (cl->refcount > 0)
00614 cl->dead = true;
00615 else
00616 CatCacheRemoveCList(cache, cl);
00617 }
00618
00619
00620 for (i = 0; i < cache->cc_nbuckets; i++)
00621 {
00622 dlist_head *bucket = &cache->cc_bucket[i];
00623
00624 dlist_foreach_modify(iter, bucket)
00625 {
00626 CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
00627
00628 if (ct->refcount > 0 ||
00629 (ct->c_list && ct->c_list->refcount > 0))
00630 {
00631 ct->dead = true;
00632
00633 Assert(ct->c_list == NULL || ct->c_list->dead);
00634 }
00635 else
00636 CatCacheRemoveCTup(cache, ct);
00637 #ifdef CATCACHE_STATS
00638 cache->cc_invals++;
00639 #endif
00640 }
00641 }
00642 }
00643
00644
00645
00646
00647
00648
00649 void
00650 ResetCatalogCaches(void)
00651 {
00652 slist_iter iter;
00653
00654 CACHE1_elog(DEBUG2, "ResetCatalogCaches called");
00655
00656 slist_foreach(iter, &CacheHdr->ch_caches)
00657 {
00658 CatCache *cache = slist_container(CatCache, cc_next, iter.cur);
00659
00660 ResetCatalogCache(cache);
00661 }
00662
00663 CACHE1_elog(DEBUG2, "end of ResetCatalogCaches call");
00664 }
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679 void
00680 CatalogCacheFlushCatalog(Oid catId)
00681 {
00682 slist_iter iter;
00683
00684 CACHE2_elog(DEBUG2, "CatalogCacheFlushCatalog called for %u", catId);
00685
00686 slist_foreach(iter, &CacheHdr->ch_caches)
00687 {
00688 CatCache *cache = slist_container(CatCache, cc_next, iter.cur);
00689
00690
00691 if (cache->cc_reloid == catId)
00692 {
00693
00694 ResetCatalogCache(cache);
00695
00696
00697 CallSyscacheCallbacks(cache->id, 0);
00698 }
00699 }
00700
00701 CACHE1_elog(DEBUG2, "end of CatalogCacheFlushCatalog call");
00702 }
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712 #ifdef CACHEDEBUG
00713 #define InitCatCache_DEBUG2 \
00714 do { \
00715 elog(DEBUG2, "InitCatCache: rel=%u ind=%u id=%d nkeys=%d size=%d", \
00716 cp->cc_reloid, cp->cc_indexoid, cp->id, \
00717 cp->cc_nkeys, cp->cc_nbuckets); \
00718 } while(0)
00719 #else
00720 #define InitCatCache_DEBUG2
00721 #endif
00722
00723 CatCache *
00724 InitCatCache(int id,
00725 Oid reloid,
00726 Oid indexoid,
00727 int nkeys,
00728 const int *key,
00729 int nbuckets)
00730 {
00731 CatCache *cp;
00732 MemoryContext oldcxt;
00733 int i;
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747 Assert(nbuckets > 0 && (nbuckets & -nbuckets) == nbuckets);
00748
00749
00750
00751
00752
00753 if (!CacheMemoryContext)
00754 CreateCacheMemoryContext();
00755
00756 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
00757
00758
00759
00760
00761 if (CacheHdr == NULL)
00762 {
00763 CacheHdr = (CatCacheHeader *) palloc(sizeof(CatCacheHeader));
00764 slist_init(&CacheHdr->ch_caches);
00765 CacheHdr->ch_ntup = 0;
00766 #ifdef CATCACHE_STATS
00767
00768 on_proc_exit(CatCachePrintStats, 0);
00769 #endif
00770 }
00771
00772
00773
00774
00775
00776
00777 cp = (CatCache *) palloc0(sizeof(CatCache) + nbuckets * sizeof(dlist_head));
00778
00779
00780
00781
00782
00783
00784 cp->id = id;
00785 cp->cc_relname = "(not known yet)";
00786 cp->cc_reloid = reloid;
00787 cp->cc_indexoid = indexoid;
00788 cp->cc_relisshared = false;
00789 cp->cc_tupdesc = (TupleDesc) NULL;
00790 cp->cc_ntup = 0;
00791 cp->cc_nbuckets = nbuckets;
00792 cp->cc_nkeys = nkeys;
00793 for (i = 0; i < nkeys; ++i)
00794 cp->cc_key[i] = key[i];
00795
00796
00797
00798
00799
00800 InitCatCache_DEBUG2;
00801
00802
00803
00804
00805 slist_push_head(&CacheHdr->ch_caches, &cp->cc_next);
00806
00807
00808
00809
00810 MemoryContextSwitchTo(oldcxt);
00811
00812 return cp;
00813 }
00814
00815
00816
00817
00818
00819
00820
00821
00822 #ifdef CACHEDEBUG
00823 #define CatalogCacheInitializeCache_DEBUG1 \
00824 elog(DEBUG2, "CatalogCacheInitializeCache: cache @%p rel=%u", cache, \
00825 cache->cc_reloid)
00826
00827 #define CatalogCacheInitializeCache_DEBUG2 \
00828 do { \
00829 if (cache->cc_key[i] > 0) { \
00830 elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d, %u", \
00831 i+1, cache->cc_nkeys, cache->cc_key[i], \
00832 tupdesc->attrs[cache->cc_key[i] - 1]->atttypid); \
00833 } else { \
00834 elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d", \
00835 i+1, cache->cc_nkeys, cache->cc_key[i]); \
00836 } \
00837 } while(0)
00838 #else
00839 #define CatalogCacheInitializeCache_DEBUG1
00840 #define CatalogCacheInitializeCache_DEBUG2
00841 #endif
00842
00843 static void
00844 CatalogCacheInitializeCache(CatCache *cache)
00845 {
00846 Relation relation;
00847 MemoryContext oldcxt;
00848 TupleDesc tupdesc;
00849 int i;
00850
00851 CatalogCacheInitializeCache_DEBUG1;
00852
00853 relation = heap_open(cache->cc_reloid, AccessShareLock);
00854
00855
00856
00857
00858
00859 Assert(CacheMemoryContext != NULL);
00860
00861 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
00862
00863
00864
00865
00866 tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
00867
00868
00869
00870
00871
00872 cache->cc_relname = pstrdup(RelationGetRelationName(relation));
00873 cache->cc_relisshared = RelationGetForm(relation)->relisshared;
00874
00875
00876
00877
00878 MemoryContextSwitchTo(oldcxt);
00879
00880 heap_close(relation, AccessShareLock);
00881
00882 CACHE3_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys",
00883 cache->cc_relname, cache->cc_nkeys);
00884
00885
00886
00887
00888 for (i = 0; i < cache->cc_nkeys; ++i)
00889 {
00890 Oid keytype;
00891 RegProcedure eqfunc;
00892
00893 CatalogCacheInitializeCache_DEBUG2;
00894
00895 if (cache->cc_key[i] > 0)
00896 keytype = tupdesc->attrs[cache->cc_key[i] - 1]->atttypid;
00897 else
00898 {
00899 if (cache->cc_key[i] != ObjectIdAttributeNumber)
00900 elog(FATAL, "only sys attr supported in caches is OID");
00901 keytype = OIDOID;
00902 }
00903
00904 GetCCHashEqFuncs(keytype,
00905 &cache->cc_hashfunc[i],
00906 &eqfunc);
00907
00908 cache->cc_isname[i] = (keytype == NAMEOID);
00909
00910
00911
00912
00913
00914 fmgr_info_cxt(eqfunc,
00915 &cache->cc_skey[i].sk_func,
00916 CacheMemoryContext);
00917
00918
00919 cache->cc_skey[i].sk_attno = cache->cc_key[i];
00920
00921
00922 cache->cc_skey[i].sk_strategy = BTEqualStrategyNumber;
00923 cache->cc_skey[i].sk_subtype = InvalidOid;
00924
00925 cache->cc_skey[i].sk_collation = InvalidOid;
00926
00927 CACHE4_elog(DEBUG2, "CatalogCacheInitializeCache %s %d %p",
00928 cache->cc_relname,
00929 i,
00930 cache);
00931 }
00932
00933
00934
00935
00936 cache->cc_tupdesc = tupdesc;
00937 }
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947
00948 void
00949 InitCatCachePhase2(CatCache *cache, bool touch_index)
00950 {
00951 if (cache->cc_tupdesc == NULL)
00952 CatalogCacheInitializeCache(cache);
00953
00954 if (touch_index &&
00955 cache->id != AMOID &&
00956 cache->id != AMNAME)
00957 {
00958 Relation idesc;
00959
00960
00961
00962
00963
00964
00965
00966 LockRelationOid(cache->cc_reloid, AccessShareLock);
00967 idesc = index_open(cache->cc_indexoid, AccessShareLock);
00968 index_close(idesc, AccessShareLock);
00969 UnlockRelationOid(cache->cc_reloid, AccessShareLock);
00970 }
00971 }
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988
00989 static bool
00990 IndexScanOK(CatCache *cache, ScanKey cur_skey)
00991 {
00992 switch (cache->id)
00993 {
00994 case INDEXRELID:
00995
00996
00997
00998
00999
01000
01001
01002 if (!criticalRelcachesBuilt)
01003 return false;
01004 break;
01005
01006 case AMOID:
01007 case AMNAME:
01008
01009
01010
01011
01012
01013
01014
01015 return false;
01016
01017 case AUTHNAME:
01018 case AUTHOID:
01019 case AUTHMEMMEMROLE:
01020
01021
01022
01023
01024
01025 if (!criticalSharedRelcachesBuilt)
01026 return false;
01027 break;
01028
01029 default:
01030 break;
01031 }
01032
01033
01034 return true;
01035 }
01036
01037
01038
01039
01040
01041
01042
01043
01044
01045
01046
01047
01048
01049
01050
01051
01052
01053 HeapTuple
01054 SearchCatCache(CatCache *cache,
01055 Datum v1,
01056 Datum v2,
01057 Datum v3,
01058 Datum v4)
01059 {
01060 ScanKeyData cur_skey[CATCACHE_MAXKEYS];
01061 uint32 hashValue;
01062 Index hashIndex;
01063 dlist_iter iter;
01064 dlist_head *bucket;
01065 CatCTup *ct;
01066 Relation relation;
01067 SysScanDesc scandesc;
01068 HeapTuple ntp;
01069
01070
01071
01072
01073 if (cache->cc_tupdesc == NULL)
01074 CatalogCacheInitializeCache(cache);
01075
01076 #ifdef CATCACHE_STATS
01077 cache->cc_searches++;
01078 #endif
01079
01080
01081
01082
01083 memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
01084 cur_skey[0].sk_argument = v1;
01085 cur_skey[1].sk_argument = v2;
01086 cur_skey[2].sk_argument = v3;
01087 cur_skey[3].sk_argument = v4;
01088
01089
01090
01091
01092 hashValue = CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);
01093 hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
01094
01095
01096
01097
01098
01099
01100
01101 bucket = &cache->cc_bucket[hashIndex];
01102 dlist_foreach(iter, bucket)
01103 {
01104 bool res;
01105
01106 ct = dlist_container(CatCTup, cache_elem, iter.cur);
01107
01108 if (ct->dead)
01109 continue;
01110
01111 if (ct->hash_value != hashValue)
01112 continue;
01113
01114
01115
01116
01117 HeapKeyTest(&ct->tuple,
01118 cache->cc_tupdesc,
01119 cache->cc_nkeys,
01120 cur_skey,
01121 res);
01122 if (!res)
01123 continue;
01124
01125
01126
01127
01128
01129
01130
01131 dlist_move_head(bucket, &ct->cache_elem);
01132
01133
01134
01135
01136
01137 if (!ct->negative)
01138 {
01139 ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
01140 ct->refcount++;
01141 ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
01142
01143 CACHE3_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",
01144 cache->cc_relname, hashIndex);
01145
01146 #ifdef CATCACHE_STATS
01147 cache->cc_hits++;
01148 #endif
01149
01150 return &ct->tuple;
01151 }
01152 else
01153 {
01154 CACHE3_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d",
01155 cache->cc_relname, hashIndex);
01156
01157 #ifdef CATCACHE_STATS
01158 cache->cc_neg_hits++;
01159 #endif
01160
01161 return NULL;
01162 }
01163 }
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176
01177
01178
01179
01180 relation = heap_open(cache->cc_reloid, AccessShareLock);
01181
01182 scandesc = systable_beginscan(relation,
01183 cache->cc_indexoid,
01184 IndexScanOK(cache, cur_skey),
01185 SnapshotNow,
01186 cache->cc_nkeys,
01187 cur_skey);
01188
01189 ct = NULL;
01190
01191 while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
01192 {
01193 ct = CatalogCacheCreateEntry(cache, ntp,
01194 hashValue, hashIndex,
01195 false);
01196
01197 ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner);
01198 ct->refcount++;
01199 ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple);
01200 break;
01201 }
01202
01203 systable_endscan(scandesc);
01204
01205 heap_close(relation, AccessShareLock);
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217 if (ct == NULL)
01218 {
01219 if (IsBootstrapProcessingMode())
01220 return NULL;
01221
01222 ntp = build_dummy_tuple(cache, cache->cc_nkeys, cur_skey);
01223 ct = CatalogCacheCreateEntry(cache, ntp,
01224 hashValue, hashIndex,
01225 true);
01226 heap_freetuple(ntp);
01227
01228 CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
01229 cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
01230 CACHE3_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d",
01231 cache->cc_relname, hashIndex);
01232
01233
01234
01235
01236
01237
01238 return NULL;
01239 }
01240
01241 CACHE4_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
01242 cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
01243 CACHE3_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d",
01244 cache->cc_relname, hashIndex);
01245
01246 #ifdef CATCACHE_STATS
01247 cache->cc_newloads++;
01248 #endif
01249
01250 return &ct->tuple;
01251 }
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264 void
01265 ReleaseCatCache(HeapTuple tuple)
01266 {
01267 CatCTup *ct = (CatCTup *) (((char *) tuple) -
01268 offsetof(CatCTup, tuple));
01269
01270
01271 Assert(ct->ct_magic == CT_MAGIC);
01272 Assert(ct->refcount > 0);
01273
01274 ct->refcount--;
01275 ResourceOwnerForgetCatCacheRef(CurrentResourceOwner, &ct->tuple);
01276
01277 if (
01278 #ifndef CATCACHE_FORCE_RELEASE
01279 ct->dead &&
01280 #endif
01281 ct->refcount == 0 &&
01282 (ct->c_list == NULL || ct->c_list->refcount == 0))
01283 CatCacheRemoveCTup(ct->my_cache, ct);
01284 }
01285
01286
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296 uint32
01297 GetCatCacheHashValue(CatCache *cache,
01298 Datum v1,
01299 Datum v2,
01300 Datum v3,
01301 Datum v4)
01302 {
01303 ScanKeyData cur_skey[CATCACHE_MAXKEYS];
01304
01305
01306
01307
01308 if (cache->cc_tupdesc == NULL)
01309 CatalogCacheInitializeCache(cache);
01310
01311
01312
01313
01314 memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
01315 cur_skey[0].sk_argument = v1;
01316 cur_skey[1].sk_argument = v2;
01317 cur_skey[2].sk_argument = v3;
01318 cur_skey[3].sk_argument = v4;
01319
01320
01321
01322
01323 return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, cur_skey);
01324 }
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336 CatCList *
01337 SearchCatCacheList(CatCache *cache,
01338 int nkeys,
01339 Datum v1,
01340 Datum v2,
01341 Datum v3,
01342 Datum v4)
01343 {
01344 ScanKeyData cur_skey[CATCACHE_MAXKEYS];
01345 uint32 lHashValue;
01346 dlist_iter iter;
01347 CatCList *cl;
01348 CatCTup *ct;
01349 List *volatile ctlist;
01350 ListCell *ctlist_item;
01351 int nmembers;
01352 bool ordered;
01353 HeapTuple ntp;
01354 MemoryContext oldcxt;
01355 int i;
01356
01357
01358
01359
01360 if (cache->cc_tupdesc == NULL)
01361 CatalogCacheInitializeCache(cache);
01362
01363 Assert(nkeys > 0 && nkeys < cache->cc_nkeys);
01364
01365 #ifdef CATCACHE_STATS
01366 cache->cc_lsearches++;
01367 #endif
01368
01369
01370
01371
01372 memcpy(cur_skey, cache->cc_skey, sizeof(cur_skey));
01373 cur_skey[0].sk_argument = v1;
01374 cur_skey[1].sk_argument = v2;
01375 cur_skey[2].sk_argument = v3;
01376 cur_skey[3].sk_argument = v4;
01377
01378
01379
01380
01381
01382
01383 lHashValue = CatalogCacheComputeHashValue(cache, nkeys, cur_skey);
01384
01385
01386
01387
01388
01389
01390
01391 dlist_foreach(iter, &cache->cc_lists)
01392 {
01393 bool res;
01394
01395 cl = dlist_container(CatCList, cache_elem, iter.cur);
01396
01397 if (cl->dead)
01398 continue;
01399
01400 if (cl->hash_value != lHashValue)
01401 continue;
01402
01403
01404
01405
01406 if (cl->nkeys != nkeys)
01407 continue;
01408 HeapKeyTest(&cl->tuple,
01409 cache->cc_tupdesc,
01410 nkeys,
01411 cur_skey,
01412 res);
01413 if (!res)
01414 continue;
01415
01416
01417
01418
01419
01420
01421
01422
01423 dlist_move_head(&cache->cc_lists, &cl->cache_elem);
01424
01425
01426 ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
01427 cl->refcount++;
01428 ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
01429
01430 CACHE2_elog(DEBUG2, "SearchCatCacheList(%s): found list",
01431 cache->cc_relname);
01432
01433 #ifdef CATCACHE_STATS
01434 cache->cc_lhits++;
01435 #endif
01436
01437 return cl;
01438 }
01439
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449
01450 ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner);
01451
01452 ctlist = NIL;
01453
01454 PG_TRY();
01455 {
01456 Relation relation;
01457 SysScanDesc scandesc;
01458
01459 relation = heap_open(cache->cc_reloid, AccessShareLock);
01460
01461 scandesc = systable_beginscan(relation,
01462 cache->cc_indexoid,
01463 IndexScanOK(cache, cur_skey),
01464 SnapshotNow,
01465 nkeys,
01466 cur_skey);
01467
01468
01469 ordered = (scandesc->irel != NULL);
01470
01471 while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
01472 {
01473 uint32 hashValue;
01474 Index hashIndex;
01475 bool found = false;
01476 dlist_head *bucket;
01477
01478
01479
01480
01481 ct = NULL;
01482 hashValue = CatalogCacheComputeTupleHashValue(cache, ntp);
01483 hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
01484
01485 bucket = &cache->cc_bucket[hashIndex];
01486 dlist_foreach(iter, bucket)
01487 {
01488 ct = dlist_container(CatCTup, cache_elem, iter.cur);
01489
01490 if (ct->dead || ct->negative)
01491 continue;
01492
01493 if (ct->hash_value != hashValue)
01494 continue;
01495
01496 if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self)))
01497 continue;
01498
01499
01500
01501
01502
01503 if (ct->c_list)
01504 continue;
01505
01506 found = true;
01507 break;
01508 }
01509
01510 if (!found)
01511 {
01512
01513 ct = CatalogCacheCreateEntry(cache, ntp,
01514 hashValue, hashIndex,
01515 false);
01516 }
01517
01518
01519
01520 ctlist = lappend(ctlist, ct);
01521 ct->refcount++;
01522 }
01523
01524 systable_endscan(scandesc);
01525
01526 heap_close(relation, AccessShareLock);
01527
01528
01529
01530
01531
01532 ntp = build_dummy_tuple(cache, nkeys, cur_skey);
01533 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
01534 nmembers = list_length(ctlist);
01535 cl = (CatCList *)
01536 palloc(sizeof(CatCList) + nmembers * sizeof(CatCTup *));
01537 heap_copytuple_with_tuple(ntp, &cl->tuple);
01538 MemoryContextSwitchTo(oldcxt);
01539 heap_freetuple(ntp);
01540
01541
01542
01543
01544
01545
01546
01547
01548
01549 }
01550 PG_CATCH();
01551 {
01552 foreach(ctlist_item, ctlist)
01553 {
01554 ct = (CatCTup *) lfirst(ctlist_item);
01555 Assert(ct->c_list == NULL);
01556 Assert(ct->refcount > 0);
01557 ct->refcount--;
01558 if (
01559 #ifndef CATCACHE_FORCE_RELEASE
01560 ct->dead &&
01561 #endif
01562 ct->refcount == 0 &&
01563 (ct->c_list == NULL || ct->c_list->refcount == 0))
01564 CatCacheRemoveCTup(cache, ct);
01565 }
01566
01567 PG_RE_THROW();
01568 }
01569 PG_END_TRY();
01570
01571 cl->cl_magic = CL_MAGIC;
01572 cl->my_cache = cache;
01573 cl->refcount = 0;
01574 cl->dead = false;
01575 cl->ordered = ordered;
01576 cl->nkeys = nkeys;
01577 cl->hash_value = lHashValue;
01578 cl->n_members = nmembers;
01579
01580 i = 0;
01581 foreach(ctlist_item, ctlist)
01582 {
01583 cl->members[i++] = ct = (CatCTup *) lfirst(ctlist_item);
01584 Assert(ct->c_list == NULL);
01585 ct->c_list = cl;
01586
01587 Assert(ct->refcount > 0);
01588 ct->refcount--;
01589
01590 if (ct->dead)
01591 cl->dead = true;
01592 }
01593 Assert(i == nmembers);
01594
01595 dlist_push_head(&cache->cc_lists, &cl->cache_elem);
01596
01597
01598 cl->refcount++;
01599 ResourceOwnerRememberCatCacheListRef(CurrentResourceOwner, cl);
01600
01601 CACHE3_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",
01602 cache->cc_relname, nmembers);
01603
01604 return cl;
01605 }
01606
01607
01608
01609
01610
01611
01612 void
01613 ReleaseCatCacheList(CatCList *list)
01614 {
01615
01616 Assert(list->cl_magic == CL_MAGIC);
01617 Assert(list->refcount > 0);
01618 list->refcount--;
01619 ResourceOwnerForgetCatCacheListRef(CurrentResourceOwner, list);
01620
01621 if (
01622 #ifndef CATCACHE_FORCE_RELEASE
01623 list->dead &&
01624 #endif
01625 list->refcount == 0)
01626 CatCacheRemoveCList(list->my_cache, list);
01627 }
01628
01629
01630
01631
01632
01633
01634
01635 static CatCTup *
01636 CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp,
01637 uint32 hashValue, Index hashIndex, bool negative)
01638 {
01639 CatCTup *ct;
01640 HeapTuple dtp;
01641 MemoryContext oldcxt;
01642
01643
01644
01645
01646
01647
01648
01649
01650 if (HeapTupleHasExternal(ntp))
01651 dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc);
01652 else
01653 dtp = ntp;
01654
01655
01656
01657
01658 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
01659 ct = (CatCTup *) palloc(sizeof(CatCTup));
01660 heap_copytuple_with_tuple(dtp, &ct->tuple);
01661 MemoryContextSwitchTo(oldcxt);
01662
01663 if (dtp != ntp)
01664 heap_freetuple(dtp);
01665
01666
01667
01668
01669
01670 ct->ct_magic = CT_MAGIC;
01671 ct->my_cache = cache;
01672 ct->c_list = NULL;
01673 ct->refcount = 0;
01674 ct->dead = false;
01675 ct->negative = negative;
01676 ct->hash_value = hashValue;
01677
01678 dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
01679
01680 cache->cc_ntup++;
01681 CacheHdr->ch_ntup++;
01682
01683 return ct;
01684 }
01685
01686
01687
01688
01689
01690
01691
01692
01693
01694 static HeapTuple
01695 build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys)
01696 {
01697 HeapTuple ntp;
01698 TupleDesc tupDesc = cache->cc_tupdesc;
01699 Datum *values;
01700 bool *nulls;
01701 Oid tupOid = InvalidOid;
01702 NameData tempNames[4];
01703 int i;
01704
01705 values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
01706 nulls = (bool *) palloc(tupDesc->natts * sizeof(bool));
01707
01708 memset(values, 0, tupDesc->natts * sizeof(Datum));
01709 memset(nulls, true, tupDesc->natts * sizeof(bool));
01710
01711 for (i = 0; i < nkeys; i++)
01712 {
01713 int attindex = cache->cc_key[i];
01714 Datum keyval = skeys[i].sk_argument;
01715
01716 if (attindex > 0)
01717 {
01718
01719
01720
01721
01722
01723
01724 if (cache->cc_isname[i])
01725 {
01726 Name newval = &tempNames[i];
01727
01728 namestrcpy(newval, DatumGetCString(keyval));
01729 keyval = NameGetDatum(newval);
01730 }
01731 values[attindex - 1] = keyval;
01732 nulls[attindex - 1] = false;
01733 }
01734 else
01735 {
01736 Assert(attindex == ObjectIdAttributeNumber);
01737 tupOid = DatumGetObjectId(keyval);
01738 }
01739 }
01740
01741 ntp = heap_form_tuple(tupDesc, values, nulls);
01742 if (tupOid != InvalidOid)
01743 HeapTupleSetOid(ntp, tupOid);
01744
01745 pfree(values);
01746 pfree(nulls);
01747
01748 return ntp;
01749 }
01750
01751
01752
01753
01754
01755
01756
01757
01758
01759
01760
01761
01762
01763
01764
01765
01766
01767
01768
01769
01770
01771
01772
01773
01774
01775
01776
01777
01778
01779
01780
01781
01782
01783
01784
01785
01786 void
01787 PrepareToInvalidateCacheTuple(Relation relation,
01788 HeapTuple tuple,
01789 HeapTuple newtuple,
01790 void (*function) (int, uint32, Oid))
01791 {
01792 slist_iter iter;
01793 Oid reloid;
01794
01795 CACHE1_elog(DEBUG2, "PrepareToInvalidateCacheTuple: called");
01796
01797
01798
01799
01800 Assert(RelationIsValid(relation));
01801 Assert(HeapTupleIsValid(tuple));
01802 Assert(PointerIsValid(function));
01803 Assert(CacheHdr != NULL);
01804
01805 reloid = RelationGetRelid(relation);
01806
01807
01808
01809
01810
01811
01812
01813
01814
01815 slist_foreach(iter, &CacheHdr->ch_caches)
01816 {
01817 CatCache *ccp = slist_container(CatCache, cc_next, iter.cur);
01818 uint32 hashvalue;
01819 Oid dbid;
01820
01821 if (ccp->cc_reloid != reloid)
01822 continue;
01823
01824
01825 if (ccp->cc_tupdesc == NULL)
01826 CatalogCacheInitializeCache(ccp);
01827
01828 hashvalue = CatalogCacheComputeTupleHashValue(ccp, tuple);
01829 dbid = ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId;
01830
01831 (*function) (ccp->id, hashvalue, dbid);
01832
01833 if (newtuple)
01834 {
01835 uint32 newhashvalue;
01836
01837 newhashvalue = CatalogCacheComputeTupleHashValue(ccp, newtuple);
01838
01839 if (newhashvalue != hashvalue)
01840 (*function) (ccp->id, newhashvalue, dbid);
01841 }
01842 }
01843 }
01844
01845
01846
01847
01848
01849
01850 void
01851 PrintCatCacheLeakWarning(HeapTuple tuple)
01852 {
01853 CatCTup *ct = (CatCTup *) (((char *) tuple) -
01854 offsetof(CatCTup, tuple));
01855
01856
01857 Assert(ct->ct_magic == CT_MAGIC);
01858
01859 elog(WARNING, "cache reference leak: cache %s (%d), tuple %u/%u has count %d",
01860 ct->my_cache->cc_relname, ct->my_cache->id,
01861 ItemPointerGetBlockNumber(&(tuple->t_self)),
01862 ItemPointerGetOffsetNumber(&(tuple->t_self)),
01863 ct->refcount);
01864 }
01865
01866 void
01867 PrintCatCacheListLeakWarning(CatCList *list)
01868 {
01869 elog(WARNING, "cache reference leak: cache %s (%d), list %p has count %d",
01870 list->my_cache->cc_relname, list->my_cache->id,
01871 list, list->refcount);
01872 }