PostgreSQL Source Code  git master
catcache.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * catcache.c
4  * System catalog cache for tuples matching a key.
5  *
6  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/utils/cache/catcache.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "access/genam.h"
18 #include "access/heaptoast.h"
19 #include "access/relscan.h"
20 #include "access/table.h"
21 #include "access/xact.h"
22 #include "catalog/pg_collation.h"
23 #include "catalog/pg_type.h"
24 #include "common/hashfn.h"
25 #include "common/pg_prng.h"
26 #include "miscadmin.h"
27 #include "port/pg_bitutils.h"
28 #ifdef CATCACHE_STATS
29 #include "storage/ipc.h" /* for on_proc_exit */
30 #endif
31 #include "storage/lmgr.h"
32 #include "utils/builtins.h"
33 #include "utils/catcache.h"
34 #include "utils/datum.h"
35 #include "utils/fmgroids.h"
36 #include "utils/inval.h"
37 #include "utils/memutils.h"
38 #include "utils/rel.h"
39 #include "utils/resowner.h"
40 #include "utils/syscache.h"
41 
42 
43  /* #define CACHEDEBUG */ /* turns DEBUG elogs on */
44 
45 /*
46  * Given a hash value and the size of the hash table, find the bucket
47  * in which the hash value belongs. Since the hash table must contain
48  * a power-of-2 number of elements, this is a simple bitmask.
49  */
50 #define HASH_INDEX(h, sz) ((Index) ((h) & ((sz) - 1)))
51 
52 
53 /*
54  * variables, macros and other stuff
55  */
56 
57 #ifdef CACHEDEBUG
58 #define CACHE_elog(...) elog(__VA_ARGS__)
59 #else
60 #define CACHE_elog(...)
61 #endif
62 
63 /* Cache management header --- pointer is NULL until created */
64 static CatCacheHeader *CacheHdr = NULL;
65 
66 static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
67  int nkeys,
68  Datum v1, Datum v2,
69  Datum v3, Datum v4);
70 
72  int nkeys,
73  uint32 hashValue,
74  Index hashIndex,
75  Datum v1, Datum v2,
76  Datum v3, Datum v4);
77 
78 static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
79  Datum v1, Datum v2, Datum v3, Datum v4);
80 static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys,
81  HeapTuple tuple);
82 static inline bool CatalogCacheCompareTuple(const CatCache *cache, int nkeys,
83  const Datum *cachekeys,
84  const Datum *searchkeys);
85 
86 #ifdef CATCACHE_STATS
87 static void CatCachePrintStats(int code, Datum arg);
88 #endif
89 static void CatCacheRemoveCTup(CatCache *cache, CatCTup *ct);
90 static void CatCacheRemoveCList(CatCache *cache, CatCList *cl);
91 static void RehashCatCache(CatCache *cp);
92 static void RehashCatCacheLists(CatCache *cp);
93 static void CatalogCacheInitializeCache(CatCache *cache);
95  HeapTuple ntp, SysScanDesc scandesc,
97  uint32 hashValue, Index hashIndex);
98 
99 static void ReleaseCatCacheWithOwner(HeapTuple tuple, ResourceOwner resowner);
101 static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
102  Datum *keys);
103 static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
104  Datum *srckeys, Datum *dstkeys);
105 
106 
107 /*
108  * internal support functions
109  */
110 
111 /* ResourceOwner callbacks to hold catcache references */
112 
113 static void ResOwnerReleaseCatCache(Datum res);
114 static char *ResOwnerPrintCatCache(Datum res);
116 static char *ResOwnerPrintCatCacheList(Datum res);
117 
119 {
120  /* catcache references */
121  .name = "catcache reference",
122  .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
123  .release_priority = RELEASE_PRIO_CATCACHE_REFS,
124  .ReleaseResource = ResOwnerReleaseCatCache,
125  .DebugPrint = ResOwnerPrintCatCache
126 };
127 
129 {
130  /* catcache-list pins */
131  .name = "catcache list reference",
132  .release_phase = RESOURCE_RELEASE_AFTER_LOCKS,
133  .release_priority = RELEASE_PRIO_CATCACHE_LIST_REFS,
134  .ReleaseResource = ResOwnerReleaseCatCacheList,
135  .DebugPrint = ResOwnerPrintCatCacheList
136 };
137 
138 /* Convenience wrappers over ResourceOwnerRemember/Forget */
139 static inline void
141 {
143 }
144 static inline void
146 {
148 }
149 static inline void
151 {
153 }
154 static inline void
156 {
158 }
159 
160 
161 /*
162  * Hash and equality functions for system types that are used as cache key
163  * fields. In some cases, we just call the regular SQL-callable functions for
164  * the appropriate data type, but that tends to be a little slow, and the
165  * speed of these functions is performance-critical. Therefore, for data
166  * types that frequently occur as catcache keys, we hard-code the logic here.
167  * Avoiding the overhead of DirectFunctionCallN(...) is a substantial win, and
168  * in certain cases (like int4) we can adopt a faster hash algorithm as well.
169  */
170 
171 static bool
173 {
174  return DatumGetChar(a) == DatumGetChar(b);
175 }
176 
177 static uint32
179 {
180  return murmurhash32((int32) DatumGetChar(datum));
181 }
182 
183 static bool
185 {
186  char *ca = NameStr(*DatumGetName(a));
187  char *cb = NameStr(*DatumGetName(b));
188 
189  return strncmp(ca, cb, NAMEDATALEN) == 0;
190 }
191 
192 static uint32
194 {
195  char *key = NameStr(*DatumGetName(datum));
196 
197  return hash_any((unsigned char *) key, strlen(key));
198 }
199 
200 static bool
202 {
203  return DatumGetInt16(a) == DatumGetInt16(b);
204 }
205 
206 static uint32
208 {
209  return murmurhash32((int32) DatumGetInt16(datum));
210 }
211 
212 static bool
214 {
215  return DatumGetInt32(a) == DatumGetInt32(b);
216 }
217 
218 static uint32
220 {
221  return murmurhash32((int32) DatumGetInt32(datum));
222 }
223 
224 static bool
226 {
227  /*
228  * The use of DEFAULT_COLLATION_OID is fairly arbitrary here. We just
229  * want to take the fast "deterministic" path in texteq().
230  */
231  return DatumGetBool(DirectFunctionCall2Coll(texteq, DEFAULT_COLLATION_OID, a, b));
232 }
233 
234 static uint32
236 {
237  /* analogously here as in texteqfast() */
238  return DatumGetInt32(DirectFunctionCall1Coll(hashtext, DEFAULT_COLLATION_OID, datum));
239 }
240 
241 static bool
243 {
245 }
246 
247 static uint32
249 {
251 }
252 
253 /* Lookup support functions for a type. */
254 static void
255 GetCCHashEqFuncs(Oid keytype, CCHashFN *hashfunc, RegProcedure *eqfunc, CCFastEqualFN *fasteqfunc)
256 {
257  switch (keytype)
258  {
259  case BOOLOID:
260  *hashfunc = charhashfast;
261  *fasteqfunc = chareqfast;
262  *eqfunc = F_BOOLEQ;
263  break;
264  case CHAROID:
265  *hashfunc = charhashfast;
266  *fasteqfunc = chareqfast;
267  *eqfunc = F_CHAREQ;
268  break;
269  case NAMEOID:
270  *hashfunc = namehashfast;
271  *fasteqfunc = nameeqfast;
272  *eqfunc = F_NAMEEQ;
273  break;
274  case INT2OID:
275  *hashfunc = int2hashfast;
276  *fasteqfunc = int2eqfast;
277  *eqfunc = F_INT2EQ;
278  break;
279  case INT4OID:
280  *hashfunc = int4hashfast;
281  *fasteqfunc = int4eqfast;
282  *eqfunc = F_INT4EQ;
283  break;
284  case TEXTOID:
285  *hashfunc = texthashfast;
286  *fasteqfunc = texteqfast;
287  *eqfunc = F_TEXTEQ;
288  break;
289  case OIDOID:
290  case REGPROCOID:
291  case REGPROCEDUREOID:
292  case REGOPEROID:
293  case REGOPERATOROID:
294  case REGCLASSOID:
295  case REGTYPEOID:
296  case REGCOLLATIONOID:
297  case REGCONFIGOID:
298  case REGDICTIONARYOID:
299  case REGROLEOID:
300  case REGNAMESPACEOID:
301  *hashfunc = int4hashfast;
302  *fasteqfunc = int4eqfast;
303  *eqfunc = F_OIDEQ;
304  break;
305  case OIDVECTOROID:
306  *hashfunc = oidvectorhashfast;
307  *fasteqfunc = oidvectoreqfast;
308  *eqfunc = F_OIDVECTOREQ;
309  break;
310  default:
311  elog(FATAL, "type %u not supported as catcache key", keytype);
312  *hashfunc = NULL; /* keep compiler quiet */
313 
314  *eqfunc = InvalidOid;
315  break;
316  }
317 }
318 
319 /*
320  * CatalogCacheComputeHashValue
321  *
322  * Compute the hash value associated with a given set of lookup keys
323  */
324 static uint32
326  Datum v1, Datum v2, Datum v3, Datum v4)
327 {
328  uint32 hashValue = 0;
329  uint32 oneHash;
330  CCHashFN *cc_hashfunc = cache->cc_hashfunc;
331 
332  CACHE_elog(DEBUG2, "CatalogCacheComputeHashValue %s %d %p",
333  cache->cc_relname, nkeys, cache);
334 
335  switch (nkeys)
336  {
337  case 4:
338  oneHash = (cc_hashfunc[3]) (v4);
339  hashValue ^= pg_rotate_left32(oneHash, 24);
340  /* FALLTHROUGH */
341  case 3:
342  oneHash = (cc_hashfunc[2]) (v3);
343  hashValue ^= pg_rotate_left32(oneHash, 16);
344  /* FALLTHROUGH */
345  case 2:
346  oneHash = (cc_hashfunc[1]) (v2);
347  hashValue ^= pg_rotate_left32(oneHash, 8);
348  /* FALLTHROUGH */
349  case 1:
350  oneHash = (cc_hashfunc[0]) (v1);
351  hashValue ^= oneHash;
352  break;
353  default:
354  elog(FATAL, "wrong number of hash keys: %d", nkeys);
355  break;
356  }
357 
358  return hashValue;
359 }
360 
361 /*
362  * CatalogCacheComputeTupleHashValue
363  *
364  * Compute the hash value associated with a given tuple to be cached
365  */
366 static uint32
368 {
369  Datum v1 = 0,
370  v2 = 0,
371  v3 = 0,
372  v4 = 0;
373  bool isNull = false;
374  int *cc_keyno = cache->cc_keyno;
375  TupleDesc cc_tupdesc = cache->cc_tupdesc;
376 
377  /* Now extract key fields from tuple, insert into scankey */
378  switch (nkeys)
379  {
380  case 4:
381  v4 = fastgetattr(tuple,
382  cc_keyno[3],
383  cc_tupdesc,
384  &isNull);
385  Assert(!isNull);
386  /* FALLTHROUGH */
387  case 3:
388  v3 = fastgetattr(tuple,
389  cc_keyno[2],
390  cc_tupdesc,
391  &isNull);
392  Assert(!isNull);
393  /* FALLTHROUGH */
394  case 2:
395  v2 = fastgetattr(tuple,
396  cc_keyno[1],
397  cc_tupdesc,
398  &isNull);
399  Assert(!isNull);
400  /* FALLTHROUGH */
401  case 1:
402  v1 = fastgetattr(tuple,
403  cc_keyno[0],
404  cc_tupdesc,
405  &isNull);
406  Assert(!isNull);
407  break;
408  default:
409  elog(FATAL, "wrong number of hash keys: %d", nkeys);
410  break;
411  }
412 
413  return CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
414 }
415 
416 /*
417  * CatalogCacheCompareTuple
418  *
419  * Compare a tuple to the passed arguments.
420  */
421 static inline bool
422 CatalogCacheCompareTuple(const CatCache *cache, int nkeys,
423  const Datum *cachekeys,
424  const Datum *searchkeys)
425 {
426  const CCFastEqualFN *cc_fastequal = cache->cc_fastequal;
427  int i;
428 
429  for (i = 0; i < nkeys; i++)
430  {
431  if (!(cc_fastequal[i]) (cachekeys[i], searchkeys[i]))
432  return false;
433  }
434  return true;
435 }
436 
437 
438 #ifdef CATCACHE_STATS
439 
440 static void
441 CatCachePrintStats(int code, Datum arg)
442 {
443  slist_iter iter;
444  long cc_searches = 0;
445  long cc_hits = 0;
446  long cc_neg_hits = 0;
447  long cc_newloads = 0;
448  long cc_invals = 0;
449  long cc_nlists = 0;
450  long cc_lsearches = 0;
451  long cc_lhits = 0;
452 
454  {
455  CatCache *cache = slist_container(CatCache, cc_next, iter.cur);
456 
457  if (cache->cc_ntup == 0 && cache->cc_searches == 0)
458  continue; /* don't print unused caches */
459  elog(DEBUG2, "catcache %s/%u: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %d lists, %ld lsrch, %ld lhits",
460  cache->cc_relname,
461  cache->cc_indexoid,
462  cache->cc_ntup,
463  cache->cc_searches,
464  cache->cc_hits,
465  cache->cc_neg_hits,
466  cache->cc_hits + cache->cc_neg_hits,
467  cache->cc_newloads,
468  cache->cc_searches - cache->cc_hits - cache->cc_neg_hits - cache->cc_newloads,
469  cache->cc_searches - cache->cc_hits - cache->cc_neg_hits,
470  cache->cc_invals,
471  cache->cc_nlist,
472  cache->cc_lsearches,
473  cache->cc_lhits);
474  cc_searches += cache->cc_searches;
475  cc_hits += cache->cc_hits;
476  cc_neg_hits += cache->cc_neg_hits;
477  cc_newloads += cache->cc_newloads;
478  cc_invals += cache->cc_invals;
479  cc_nlists += cache->cc_nlist;
480  cc_lsearches += cache->cc_lsearches;
481  cc_lhits += cache->cc_lhits;
482  }
483  elog(DEBUG2, "catcache totals: %d tup, %ld srch, %ld+%ld=%ld hits, %ld+%ld=%ld loads, %ld invals, %ld lists, %ld lsrch, %ld lhits",
484  CacheHdr->ch_ntup,
485  cc_searches,
486  cc_hits,
487  cc_neg_hits,
488  cc_hits + cc_neg_hits,
489  cc_newloads,
490  cc_searches - cc_hits - cc_neg_hits - cc_newloads,
491  cc_searches - cc_hits - cc_neg_hits,
492  cc_invals,
493  cc_nlists,
494  cc_lsearches,
495  cc_lhits);
496 }
497 #endif /* CATCACHE_STATS */
498 
499 
500 /*
501  * CatCacheRemoveCTup
502  *
503  * Unlink and delete the given cache entry
504  *
505  * NB: if it is a member of a CatCList, the CatCList is deleted too.
506  * Both the cache entry and the list had better have zero refcount.
507  */
508 static void
510 {
511  Assert(ct->refcount == 0);
512  Assert(ct->my_cache == cache);
513 
514  if (ct->c_list)
515  {
516  /*
517  * The cleanest way to handle this is to call CatCacheRemoveCList,
518  * which will recurse back to me, and the recursive call will do the
519  * work. Set the "dead" flag to make sure it does recurse.
520  */
521  ct->dead = true;
522  CatCacheRemoveCList(cache, ct->c_list);
523  return; /* nothing left to do */
524  }
525 
526  /* delink from linked list */
527  dlist_delete(&ct->cache_elem);
528 
529  /*
530  * Free keys when we're dealing with a negative entry, normal entries just
531  * point into tuple, allocated together with the CatCTup.
532  */
533  if (ct->negative)
534  CatCacheFreeKeys(cache->cc_tupdesc, cache->cc_nkeys,
535  cache->cc_keyno, ct->keys);
536 
537  pfree(ct);
538 
539  --cache->cc_ntup;
540  --CacheHdr->ch_ntup;
541 }
542 
543 /*
544  * CatCacheRemoveCList
545  *
546  * Unlink and delete the given cache list entry
547  *
548  * NB: any dead member entries that become unreferenced are deleted too.
549  */
550 static void
552 {
553  int i;
554 
555  Assert(cl->refcount == 0);
556  Assert(cl->my_cache == cache);
557 
558  /* delink from member tuples */
559  for (i = cl->n_members; --i >= 0;)
560  {
561  CatCTup *ct = cl->members[i];
562 
563  Assert(ct->c_list == cl);
564  ct->c_list = NULL;
565  /* if the member is dead and now has no references, remove it */
566  if (
567 #ifndef CATCACHE_FORCE_RELEASE
568  ct->dead &&
569 #endif
570  ct->refcount == 0)
571  CatCacheRemoveCTup(cache, ct);
572  }
573 
574  /* delink from linked list */
575  dlist_delete(&cl->cache_elem);
576 
577  /* free associated column data */
578  CatCacheFreeKeys(cache->cc_tupdesc, cl->nkeys,
579  cache->cc_keyno, cl->keys);
580 
581  pfree(cl);
582 
583  --cache->cc_nlist;
584 }
585 
586 
587 /*
588  * CatCacheInvalidate
589  *
590  * Invalidate entries in the specified cache, given a hash value.
591  *
592  * We delete cache entries that match the hash value, whether positive
593  * or negative. We don't care whether the invalidation is the result
594  * of a tuple insertion or a deletion.
595  *
596  * We used to try to match positive cache entries by TID, but that is
597  * unsafe after a VACUUM FULL on a system catalog: an inval event could
598  * be queued before VACUUM FULL, and then processed afterwards, when the
599  * target tuple that has to be invalidated has a different TID than it
600  * did when the event was created. So now we just compare hash values and
601  * accept the small risk of unnecessary invalidations due to false matches.
602  *
603  * This routine is only quasi-public: it should only be used by inval.c.
604  */
605 void
607 {
608  Index hashIndex;
609  dlist_mutable_iter iter;
610 
611  CACHE_elog(DEBUG2, "CatCacheInvalidate: called");
612 
613  /*
614  * We don't bother to check whether the cache has finished initialization
615  * yet; if not, there will be no entries in it so no problem.
616  */
617 
618  /*
619  * Invalidate *all* CatCLists in this cache; it's too hard to tell which
620  * searches might still be correct, so just zap 'em all.
621  */
622  for (int i = 0; i < cache->cc_nlbuckets; i++)
623  {
624  dlist_head *bucket = &cache->cc_lbucket[i];
625 
626  dlist_foreach_modify(iter, bucket)
627  {
628  CatCList *cl = dlist_container(CatCList, cache_elem, iter.cur);
629 
630  if (cl->refcount > 0)
631  cl->dead = true;
632  else
633  CatCacheRemoveCList(cache, cl);
634  }
635  }
636 
637  /*
638  * inspect the proper hash bucket for tuple matches
639  */
640  hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
641  dlist_foreach_modify(iter, &cache->cc_bucket[hashIndex])
642  {
643  CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
644 
645  if (hashValue == ct->hash_value)
646  {
647  if (ct->refcount > 0 ||
648  (ct->c_list && ct->c_list->refcount > 0))
649  {
650  ct->dead = true;
651  /* list, if any, was marked dead above */
652  Assert(ct->c_list == NULL || ct->c_list->dead);
653  }
654  else
655  CatCacheRemoveCTup(cache, ct);
656  CACHE_elog(DEBUG2, "CatCacheInvalidate: invalidated");
657 #ifdef CATCACHE_STATS
658  cache->cc_invals++;
659 #endif
660  /* could be multiple matches, so keep looking! */
661  }
662  }
663 }
664 
665 /* ----------------------------------------------------------------
666  * public functions
667  * ----------------------------------------------------------------
668  */
669 
670 
671 /*
672  * Standard routine for creating cache context if it doesn't exist yet
673  *
674  * There are a lot of places (probably far more than necessary) that check
675  * whether CacheMemoryContext exists yet and want to create it if not.
676  * We centralize knowledge of exactly how to create it here.
677  */
678 void
680 {
681  /*
682  * Purely for paranoia, check that context doesn't exist; caller probably
683  * did so already.
684  */
685  if (!CacheMemoryContext)
687  "CacheMemoryContext",
689 }
690 
691 
692 /*
693  * ResetCatalogCache
694  *
695  * Reset one catalog cache to empty.
696  *
697  * This is not very efficient if the target cache is nearly empty.
698  * However, it shouldn't need to be efficient; we don't invoke it often.
699  */
700 static void
702 {
703  dlist_mutable_iter iter;
704  int i;
705 
706  /* Remove each list in this cache, or at least mark it dead */
707  for (i = 0; i < cache->cc_nlbuckets; i++)
708  {
709  dlist_head *bucket = &cache->cc_lbucket[i];
710 
711  dlist_foreach_modify(iter, bucket)
712  {
713  CatCList *cl = dlist_container(CatCList, cache_elem, iter.cur);
714 
715  if (cl->refcount > 0)
716  cl->dead = true;
717  else
718  CatCacheRemoveCList(cache, cl);
719  }
720  }
721 
722  /* Remove each tuple in this cache, or at least mark it dead */
723  for (i = 0; i < cache->cc_nbuckets; i++)
724  {
725  dlist_head *bucket = &cache->cc_bucket[i];
726 
727  dlist_foreach_modify(iter, bucket)
728  {
729  CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
730 
731  if (ct->refcount > 0 ||
732  (ct->c_list && ct->c_list->refcount > 0))
733  {
734  ct->dead = true;
735  /* list, if any, was marked dead above */
736  Assert(ct->c_list == NULL || ct->c_list->dead);
737  }
738  else
739  CatCacheRemoveCTup(cache, ct);
740 #ifdef CATCACHE_STATS
741  cache->cc_invals++;
742 #endif
743  }
744  }
745 }
746 
747 /*
748  * ResetCatalogCaches
749  *
750  * Reset all caches when a shared cache inval event forces it
751  */
752 void
754 {
755  slist_iter iter;
756 
757  CACHE_elog(DEBUG2, "ResetCatalogCaches called");
758 
760  {
761  CatCache *cache = slist_container(CatCache, cc_next, iter.cur);
762 
763  ResetCatalogCache(cache);
764  }
765 
766  CACHE_elog(DEBUG2, "end of ResetCatalogCaches call");
767 }
768 
769 /*
770  * CatalogCacheFlushCatalog
771  *
772  * Flush all catcache entries that came from the specified system catalog.
773  * This is needed after VACUUM FULL/CLUSTER on the catalog, since the
774  * tuples very likely now have different TIDs than before. (At one point
775  * we also tried to force re-execution of CatalogCacheInitializeCache for
776  * the cache(s) on that catalog. This is a bad idea since it leads to all
777  * kinds of trouble if a cache flush occurs while loading cache entries.
778  * We now avoid the need to do it by copying cc_tupdesc out of the relcache,
779  * rather than relying on the relcache to keep a tupdesc for us. Of course
780  * this assumes the tupdesc of a cachable system table will not change...)
781  */
782 void
784 {
785  slist_iter iter;
786 
787  CACHE_elog(DEBUG2, "CatalogCacheFlushCatalog called for %u", catId);
788 
790  {
791  CatCache *cache = slist_container(CatCache, cc_next, iter.cur);
792 
793  /* Does this cache store tuples of the target catalog? */
794  if (cache->cc_reloid == catId)
795  {
796  /* Yes, so flush all its contents */
797  ResetCatalogCache(cache);
798 
799  /* Tell inval.c to call syscache callbacks for this cache */
800  CallSyscacheCallbacks(cache->id, 0);
801  }
802  }
803 
804  CACHE_elog(DEBUG2, "end of CatalogCacheFlushCatalog call");
805 }
806 
807 /*
808  * InitCatCache
809  *
810  * This allocates and initializes a cache for a system catalog relation.
811  * Actually, the cache is only partially initialized to avoid opening the
812  * relation. The relation will be opened and the rest of the cache
813  * structure initialized on the first access.
814  */
815 #ifdef CACHEDEBUG
816 #define InitCatCache_DEBUG2 \
817 do { \
818  elog(DEBUG2, "InitCatCache: rel=%u ind=%u id=%d nkeys=%d size=%d", \
819  cp->cc_reloid, cp->cc_indexoid, cp->id, \
820  cp->cc_nkeys, cp->cc_nbuckets); \
821 } while(0)
822 #else
823 #define InitCatCache_DEBUG2
824 #endif
825 
826 CatCache *
828  Oid reloid,
829  Oid indexoid,
830  int nkeys,
831  const int *key,
832  int nbuckets)
833 {
834  CatCache *cp;
835  MemoryContext oldcxt;
836  int i;
837 
838  /*
839  * nbuckets is the initial number of hash buckets to use in this catcache.
840  * It will be enlarged later if it becomes too full.
841  *
842  * nbuckets must be a power of two. We check this via Assert rather than
843  * a full runtime check because the values will be coming from constant
844  * tables.
845  *
846  * If you're confused by the power-of-two check, see comments in
847  * bitmapset.c for an explanation.
848  */
849  Assert(nbuckets > 0 && (nbuckets & -nbuckets) == nbuckets);
850 
851  /*
852  * first switch to the cache context so our allocations do not vanish at
853  * the end of a transaction
854  */
855  if (!CacheMemoryContext)
857 
859 
860  /*
861  * if first time through, initialize the cache group header
862  */
863  if (CacheHdr == NULL)
864  {
867  CacheHdr->ch_ntup = 0;
868 #ifdef CATCACHE_STATS
869  /* set up to dump stats at backend exit */
870  on_proc_exit(CatCachePrintStats, 0);
871 #endif
872  }
873 
874  /*
875  * Allocate a new cache structure, aligning to a cacheline boundary
876  *
877  * Note: we rely on zeroing to initialize all the dlist headers correctly
878  */
881  cp->cc_bucket = palloc0(nbuckets * sizeof(dlist_head));
882 
883  /*
884  * Many catcaches never receive any list searches. Therefore, we don't
885  * allocate the cc_lbuckets till we get a list search.
886  */
887  cp->cc_lbucket = NULL;
888 
889  /*
890  * initialize the cache's relation information for the relation
891  * corresponding to this cache, and initialize some of the new cache's
892  * other internal fields. But don't open the relation yet.
893  */
894  cp->id = id;
895  cp->cc_relname = "(not known yet)";
896  cp->cc_reloid = reloid;
897  cp->cc_indexoid = indexoid;
898  cp->cc_relisshared = false; /* temporary */
899  cp->cc_tupdesc = (TupleDesc) NULL;
900  cp->cc_ntup = 0;
901  cp->cc_nlist = 0;
902  cp->cc_nbuckets = nbuckets;
903  cp->cc_nlbuckets = 0;
904  cp->cc_nkeys = nkeys;
905  for (i = 0; i < nkeys; ++i)
906  {
908  cp->cc_keyno[i] = key[i];
909  }
910 
911  /*
912  * new cache is initialized as far as we can go for now. print some
913  * debugging information, if appropriate.
914  */
916 
917  /*
918  * add completed cache to top of group header's list
919  */
921 
922  /*
923  * back to the old context before we return...
924  */
925  MemoryContextSwitchTo(oldcxt);
926 
927  return cp;
928 }
929 
930 /*
931  * Enlarge a catcache, doubling the number of buckets.
932  */
933 static void
935 {
936  dlist_head *newbucket;
937  int newnbuckets;
938  int i;
939 
940  elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
941  cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
942 
943  /* Allocate a new, larger, hash table. */
944  newnbuckets = cp->cc_nbuckets * 2;
945  newbucket = (dlist_head *) MemoryContextAllocZero(CacheMemoryContext, newnbuckets * sizeof(dlist_head));
946 
947  /* Move all entries from old hash table to new. */
948  for (i = 0; i < cp->cc_nbuckets; i++)
949  {
950  dlist_mutable_iter iter;
951 
952  dlist_foreach_modify(iter, &cp->cc_bucket[i])
953  {
954  CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
955  int hashIndex = HASH_INDEX(ct->hash_value, newnbuckets);
956 
957  dlist_delete(iter.cur);
958  dlist_push_head(&newbucket[hashIndex], &ct->cache_elem);
959  }
960  }
961 
962  /* Switch to the new array. */
963  pfree(cp->cc_bucket);
964  cp->cc_nbuckets = newnbuckets;
965  cp->cc_bucket = newbucket;
966 }
967 
968 /*
969  * Enlarge a catcache's list storage, doubling the number of buckets.
970  */
971 static void
973 {
974  dlist_head *newbucket;
975  int newnbuckets;
976  int i;
977 
978  elog(DEBUG1, "rehashing catalog cache id %d for %s; %d lists, %d buckets",
979  cp->id, cp->cc_relname, cp->cc_nlist, cp->cc_nlbuckets);
980 
981  /* Allocate a new, larger, hash table. */
982  newnbuckets = cp->cc_nlbuckets * 2;
983  newbucket = (dlist_head *) MemoryContextAllocZero(CacheMemoryContext, newnbuckets * sizeof(dlist_head));
984 
985  /* Move all entries from old hash table to new. */
986  for (i = 0; i < cp->cc_nlbuckets; i++)
987  {
988  dlist_mutable_iter iter;
989 
990  dlist_foreach_modify(iter, &cp->cc_lbucket[i])
991  {
992  CatCList *cl = dlist_container(CatCList, cache_elem, iter.cur);
993  int hashIndex = HASH_INDEX(cl->hash_value, newnbuckets);
994 
995  dlist_delete(iter.cur);
996  dlist_push_head(&newbucket[hashIndex], &cl->cache_elem);
997  }
998  }
999 
1000  /* Switch to the new array. */
1001  pfree(cp->cc_lbucket);
1002  cp->cc_nlbuckets = newnbuckets;
1003  cp->cc_lbucket = newbucket;
1004 }
1005 
1006 /*
1007  * CatalogCacheInitializeCache
1008  *
1009  * This function does final initialization of a catcache: obtain the tuple
1010  * descriptor and set up the hash and equality function links. We assume
1011  * that the relcache entry can be opened at this point!
1012  */
1013 #ifdef CACHEDEBUG
1014 #define CatalogCacheInitializeCache_DEBUG1 \
1015  elog(DEBUG2, "CatalogCacheInitializeCache: cache @%p rel=%u", cache, \
1016  cache->cc_reloid)
1017 
1018 #define CatalogCacheInitializeCache_DEBUG2 \
1019 do { \
1020  if (cache->cc_keyno[i] > 0) { \
1021  elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d, %u", \
1022  i+1, cache->cc_nkeys, cache->cc_keyno[i], \
1023  TupleDescAttr(tupdesc, cache->cc_keyno[i] - 1)->atttypid); \
1024  } else { \
1025  elog(DEBUG2, "CatalogCacheInitializeCache: load %d/%d w/%d", \
1026  i+1, cache->cc_nkeys, cache->cc_keyno[i]); \
1027  } \
1028 } while(0)
1029 #else
1030 #define CatalogCacheInitializeCache_DEBUG1
1031 #define CatalogCacheInitializeCache_DEBUG2
1032 #endif
1033 
1034 static void
1036 {
1037  Relation relation;
1038  MemoryContext oldcxt;
1039  TupleDesc tupdesc;
1040  int i;
1041 
1043 
1044  relation = table_open(cache->cc_reloid, AccessShareLock);
1045 
1046  /*
1047  * switch to the cache context so our allocations do not vanish at the end
1048  * of a transaction
1049  */
1050  Assert(CacheMemoryContext != NULL);
1051 
1053 
1054  /*
1055  * copy the relcache's tuple descriptor to permanent cache storage
1056  */
1057  tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
1058 
1059  /*
1060  * save the relation's name and relisshared flag, too (cc_relname is used
1061  * only for debugging purposes)
1062  */
1063  cache->cc_relname = pstrdup(RelationGetRelationName(relation));
1064  cache->cc_relisshared = RelationGetForm(relation)->relisshared;
1065 
1066  /*
1067  * return to the caller's memory context and close the rel
1068  */
1069  MemoryContextSwitchTo(oldcxt);
1070 
1071  table_close(relation, AccessShareLock);
1072 
1073  CACHE_elog(DEBUG2, "CatalogCacheInitializeCache: %s, %d keys",
1074  cache->cc_relname, cache->cc_nkeys);
1075 
1076  /*
1077  * initialize cache's key information
1078  */
1079  for (i = 0; i < cache->cc_nkeys; ++i)
1080  {
1081  Oid keytype;
1082  RegProcedure eqfunc;
1083 
1085 
1086  if (cache->cc_keyno[i] > 0)
1087  {
1088  Form_pg_attribute attr = TupleDescAttr(tupdesc,
1089  cache->cc_keyno[i] - 1);
1090 
1091  keytype = attr->atttypid;
1092  /* cache key columns should always be NOT NULL */
1093  Assert(attr->attnotnull);
1094  }
1095  else
1096  {
1097  if (cache->cc_keyno[i] < 0)
1098  elog(FATAL, "sys attributes are not supported in caches");
1099  keytype = OIDOID;
1100  }
1101 
1102  GetCCHashEqFuncs(keytype,
1103  &cache->cc_hashfunc[i],
1104  &eqfunc,
1105  &cache->cc_fastequal[i]);
1106 
1107  /*
1108  * Do equality-function lookup (we assume this won't need a catalog
1109  * lookup for any supported type)
1110  */
1111  fmgr_info_cxt(eqfunc,
1112  &cache->cc_skey[i].sk_func,
1114 
1115  /* Initialize sk_attno suitably for HeapKeyTest() and heap scans */
1116  cache->cc_skey[i].sk_attno = cache->cc_keyno[i];
1117 
1118  /* Fill in sk_strategy as well --- always standard equality */
1120  cache->cc_skey[i].sk_subtype = InvalidOid;
1121  /* If a catcache key requires a collation, it must be C collation */
1122  cache->cc_skey[i].sk_collation = C_COLLATION_OID;
1123 
1124  CACHE_elog(DEBUG2, "CatalogCacheInitializeCache %s %d %p",
1125  cache->cc_relname, i, cache);
1126  }
1127 
1128  /*
1129  * mark this cache fully initialized
1130  */
1131  cache->cc_tupdesc = tupdesc;
1132 }
1133 
1134 /*
1135  * InitCatCachePhase2 -- external interface for CatalogCacheInitializeCache
1136  *
1137  * One reason to call this routine is to ensure that the relcache has
1138  * created entries for all the catalogs and indexes referenced by catcaches.
1139  * Therefore, provide an option to open the index as well as fixing the
1140  * cache itself. An exception is the indexes on pg_am, which we don't use
1141  * (cf. IndexScanOK).
1142  */
1143 void
1144 InitCatCachePhase2(CatCache *cache, bool touch_index)
1145 {
1146  if (cache->cc_tupdesc == NULL)
1148 
1149  if (touch_index &&
1150  cache->id != AMOID &&
1151  cache->id != AMNAME)
1152  {
1153  Relation idesc;
1154 
1155  /*
1156  * We must lock the underlying catalog before opening the index to
1157  * avoid deadlock, since index_open could possibly result in reading
1158  * this same catalog, and if anyone else is exclusive-locking this
1159  * catalog and index they'll be doing it in that order.
1160  */
1162  idesc = index_open(cache->cc_indexoid, AccessShareLock);
1163 
1164  /*
1165  * While we've got the index open, let's check that it's unique (and
1166  * not just deferrable-unique, thank you very much). This is just to
1167  * catch thinkos in definitions of new catcaches, so we don't worry
1168  * about the pg_am indexes not getting tested.
1169  */
1170  Assert(idesc->rd_index->indisunique &&
1171  idesc->rd_index->indimmediate);
1172 
1173  index_close(idesc, AccessShareLock);
1175  }
1176 }
1177 
1178 
1179 /*
1180  * IndexScanOK
1181  *
1182  * This function checks for tuples that will be fetched by
1183  * IndexSupportInitialize() during relcache initialization for
1184  * certain system indexes that support critical syscaches.
1185  * We can't use an indexscan to fetch these, else we'll get into
1186  * infinite recursion. A plain heap scan will work, however.
1187  * Once we have completed relcache initialization (signaled by
1188  * criticalRelcachesBuilt), we don't have to worry anymore.
1189  *
1190  * Similarly, during backend startup we have to be able to use the
1191  * pg_authid, pg_auth_members and pg_database syscaches for
1192  * authentication even if we don't yet have relcache entries for those
1193  * catalogs' indexes.
1194  */
1195 static bool
1196 IndexScanOK(CatCache *cache, ScanKey cur_skey)
1197 {
1198  switch (cache->id)
1199  {
1200  case INDEXRELID:
1201 
1202  /*
1203  * Rather than tracking exactly which indexes have to be loaded
1204  * before we can use indexscans (which changes from time to time),
1205  * just force all pg_index searches to be heap scans until we've
1206  * built the critical relcaches.
1207  */
1209  return false;
1210  break;
1211 
1212  case AMOID:
1213  case AMNAME:
1214 
1215  /*
1216  * Always do heap scans in pg_am, because it's so small there's
1217  * not much point in an indexscan anyway. We *must* do this when
1218  * initially building critical relcache entries, but we might as
1219  * well just always do it.
1220  */
1221  return false;
1222 
1223  case AUTHNAME:
1224  case AUTHOID:
1225  case AUTHMEMMEMROLE:
1226  case DATABASEOID:
1227 
1228  /*
1229  * Protect authentication lookups occurring before relcache has
1230  * collected entries for shared indexes.
1231  */
1233  return false;
1234  break;
1235 
1236  default:
1237  break;
1238  }
1239 
1240  /* Normal case, allow index scan */
1241  return true;
1242 }
1243 
1244 /*
1245  * SearchCatCache
1246  *
1247  * This call searches a system cache for a tuple, opening the relation
1248  * if necessary (on the first access to a particular cache).
1249  *
1250  * The result is NULL if not found, or a pointer to a HeapTuple in
1251  * the cache. The caller must not modify the tuple, and must call
1252  * ReleaseCatCache() when done with it.
1253  *
1254  * The search key values should be expressed as Datums of the key columns'
1255  * datatype(s). (Pass zeroes for any unused parameters.) As a special
1256  * exception, the passed-in key for a NAME column can be just a C string;
1257  * the caller need not go to the trouble of converting it to a fully
1258  * null-padded NAME.
1259  */
1260 HeapTuple
1262  Datum v1,
1263  Datum v2,
1264  Datum v3,
1265  Datum v4)
1266 {
1267  return SearchCatCacheInternal(cache, cache->cc_nkeys, v1, v2, v3, v4);
1268 }
1269 
1270 
1271 /*
1272  * SearchCatCacheN() are SearchCatCache() versions for a specific number of
1273  * arguments. The compiler can inline the body and unroll loops, making them a
1274  * bit faster than SearchCatCache().
1275  */
1276 
1277 HeapTuple
1279  Datum v1)
1280 {
1281  return SearchCatCacheInternal(cache, 1, v1, 0, 0, 0);
1282 }
1283 
1284 
1285 HeapTuple
1287  Datum v1, Datum v2)
1288 {
1289  return SearchCatCacheInternal(cache, 2, v1, v2, 0, 0);
1290 }
1291 
1292 
1293 HeapTuple
1295  Datum v1, Datum v2, Datum v3)
1296 {
1297  return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
1298 }
1299 
1300 
1301 HeapTuple
1303  Datum v1, Datum v2, Datum v3, Datum v4)
1304 {
1305  return SearchCatCacheInternal(cache, 4, v1, v2, v3, v4);
1306 }
1307 
1308 /*
1309  * Work-horse for SearchCatCache/SearchCatCacheN.
1310  */
1311 static inline HeapTuple
1313  int nkeys,
1314  Datum v1,
1315  Datum v2,
1316  Datum v3,
1317  Datum v4)
1318 {
1320  uint32 hashValue;
1321  Index hashIndex;
1322  dlist_iter iter;
1323  dlist_head *bucket;
1324  CatCTup *ct;
1325 
1326  /* Make sure we're in an xact, even if this ends up being a cache hit */
1328 
1329  Assert(cache->cc_nkeys == nkeys);
1330 
1331  /*
1332  * one-time startup overhead for each cache
1333  */
1334  if (unlikely(cache->cc_tupdesc == NULL))
1336 
1337 #ifdef CATCACHE_STATS
1338  cache->cc_searches++;
1339 #endif
1340 
1341  /* Initialize local parameter array */
1342  arguments[0] = v1;
1343  arguments[1] = v2;
1344  arguments[2] = v3;
1345  arguments[3] = v4;
1346 
1347  /*
1348  * find the hash bucket in which to look for the tuple
1349  */
1350  hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
1351  hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
1352 
1353  /*
1354  * scan the hash bucket until we find a match or exhaust our tuples
1355  *
1356  * Note: it's okay to use dlist_foreach here, even though we modify the
1357  * dlist within the loop, because we don't continue the loop afterwards.
1358  */
1359  bucket = &cache->cc_bucket[hashIndex];
1360  dlist_foreach(iter, bucket)
1361  {
1362  ct = dlist_container(CatCTup, cache_elem, iter.cur);
1363 
1364  if (ct->dead)
1365  continue; /* ignore dead entries */
1366 
1367  if (ct->hash_value != hashValue)
1368  continue; /* quickly skip entry if wrong hash val */
1369 
1370  if (!CatalogCacheCompareTuple(cache, nkeys, ct->keys, arguments))
1371  continue;
1372 
1373  /*
1374  * We found a match in the cache. Move it to the front of the list
1375  * for its hashbucket, in order to speed subsequent searches. (The
1376  * most frequently accessed elements in any hashbucket will tend to be
1377  * near the front of the hashbucket's list.)
1378  */
1379  dlist_move_head(bucket, &ct->cache_elem);
1380 
1381  /*
1382  * If it's a positive entry, bump its refcount and return it. If it's
1383  * negative, we can report failure to the caller.
1384  */
1385  if (!ct->negative)
1386  {
1388  ct->refcount++;
1390 
1391  CACHE_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d",
1392  cache->cc_relname, hashIndex);
1393 
1394 #ifdef CATCACHE_STATS
1395  cache->cc_hits++;
1396 #endif
1397 
1398  return &ct->tuple;
1399  }
1400  else
1401  {
1402  CACHE_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d",
1403  cache->cc_relname, hashIndex);
1404 
1405 #ifdef CATCACHE_STATS
1406  cache->cc_neg_hits++;
1407 #endif
1408 
1409  return NULL;
1410  }
1411  }
1412 
1413  return SearchCatCacheMiss(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);
1414 }
1415 
1416 /*
1417  * Search the actual catalogs, rather than the cache.
1418  *
1419  * This is kept separate from SearchCatCacheInternal() to keep the fast-path
1420  * as small as possible. To avoid that effort being undone by a helpful
1421  * compiler, try to explicitly forbid inlining.
1422  */
1423 static pg_noinline HeapTuple
1425  int nkeys,
1426  uint32 hashValue,
1427  Index hashIndex,
1428  Datum v1,
1429  Datum v2,
1430  Datum v3,
1431  Datum v4)
1432 {
1433  ScanKeyData cur_skey[CATCACHE_MAXKEYS];
1434  Relation relation;
1435  SysScanDesc scandesc;
1436  HeapTuple ntp;
1437  CatCTup *ct;
1438  bool stale;
1440 
1441  /* Initialize local parameter array */
1442  arguments[0] = v1;
1443  arguments[1] = v2;
1444  arguments[2] = v3;
1445  arguments[3] = v4;
1446 
1447  /*
1448  * Tuple was not found in cache, so we have to try to retrieve it directly
1449  * from the relation. If found, we will add it to the cache; if not
1450  * found, we will add a negative cache entry instead.
1451  *
1452  * NOTE: it is possible for recursive cache lookups to occur while reading
1453  * the relation --- for example, due to shared-cache-inval messages being
1454  * processed during table_open(). This is OK. It's even possible for one
1455  * of those lookups to find and enter the very same tuple we are trying to
1456  * fetch here. If that happens, we will enter a second copy of the tuple
1457  * into the cache. The first copy will never be referenced again, and
1458  * will eventually age out of the cache, so there's no functional problem.
1459  * This case is rare enough that it's not worth expending extra cycles to
1460  * detect.
1461  *
1462  * Another case, which we *must* handle, is that the tuple could become
1463  * outdated during CatalogCacheCreateEntry's attempt to detoast it (since
1464  * AcceptInvalidationMessages can run during TOAST table access). We do
1465  * not want to return already-stale catcache entries, so we loop around
1466  * and do the table scan again if that happens.
1467  */
1468  relation = table_open(cache->cc_reloid, AccessShareLock);
1469 
1470  do
1471  {
1472  /*
1473  * Ok, need to make a lookup in the relation, copy the scankey and
1474  * fill out any per-call fields. (We must re-do this when retrying,
1475  * because systable_beginscan scribbles on the scankey.)
1476  */
1477  memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * nkeys);
1478  cur_skey[0].sk_argument = v1;
1479  cur_skey[1].sk_argument = v2;
1480  cur_skey[2].sk_argument = v3;
1481  cur_skey[3].sk_argument = v4;
1482 
1483  scandesc = systable_beginscan(relation,
1484  cache->cc_indexoid,
1485  IndexScanOK(cache, cur_skey),
1486  NULL,
1487  nkeys,
1488  cur_skey);
1489 
1490  ct = NULL;
1491  stale = false;
1492 
1493  while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
1494  {
1495  ct = CatalogCacheCreateEntry(cache, ntp, scandesc, NULL,
1496  hashValue, hashIndex);
1497  /* upon failure, we must start the scan over */
1498  if (ct == NULL)
1499  {
1500  stale = true;
1501  break;
1502  }
1503  /* immediately set the refcount to 1 */
1505  ct->refcount++;
1507  break; /* assume only one match */
1508  }
1509 
1510  systable_endscan(scandesc);
1511  } while (stale);
1512 
1513  table_close(relation, AccessShareLock);
1514 
1515  /*
1516  * If tuple was not found, we need to build a negative cache entry
1517  * containing a fake tuple. The fake tuple has the correct key columns,
1518  * but nulls everywhere else.
1519  *
1520  * In bootstrap mode, we don't build negative entries, because the cache
1521  * invalidation mechanism isn't alive and can't clear them if the tuple
1522  * gets created later. (Bootstrap doesn't do UPDATEs, so it doesn't need
1523  * cache inval for that.)
1524  */
1525  if (ct == NULL)
1526  {
1528  return NULL;
1529 
1530  ct = CatalogCacheCreateEntry(cache, NULL, NULL, arguments,
1531  hashValue, hashIndex);
1532 
1533  /* Creating a negative cache entry shouldn't fail */
1534  Assert(ct != NULL);
1535 
1536  CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
1537  cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
1538  CACHE_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d",
1539  cache->cc_relname, hashIndex);
1540 
1541  /*
1542  * We are not returning the negative entry to the caller, so leave its
1543  * refcount zero.
1544  */
1545 
1546  return NULL;
1547  }
1548 
1549  CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples",
1550  cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup);
1551  CACHE_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d",
1552  cache->cc_relname, hashIndex);
1553 
1554 #ifdef CATCACHE_STATS
1555  cache->cc_newloads++;
1556 #endif
1557 
1558  return &ct->tuple;
1559 }
1560 
1561 /*
1562  * ReleaseCatCache
1563  *
1564  * Decrement the reference count of a catcache entry (releasing the
1565  * hold grabbed by a successful SearchCatCache).
1566  *
1567  * NOTE: if compiled with -DCATCACHE_FORCE_RELEASE then catcache entries
1568  * will be freed as soon as their refcount goes to zero. In combination
1569  * with aset.c's CLOBBER_FREED_MEMORY option, this provides a good test
1570  * to catch references to already-released catcache entries.
1571  */
1572 void
1574 {
1576 }
1577 
1578 static void
1580 {
1581  CatCTup *ct = (CatCTup *) (((char *) tuple) -
1582  offsetof(CatCTup, tuple));
1583 
1584  /* Safety checks to ensure we were handed a cache entry */
1585  Assert(ct->ct_magic == CT_MAGIC);
1586  Assert(ct->refcount > 0);
1587 
1588  ct->refcount--;
1589  if (resowner)
1591 
1592  if (
1593 #ifndef CATCACHE_FORCE_RELEASE
1594  ct->dead &&
1595 #endif
1596  ct->refcount == 0 &&
1597  (ct->c_list == NULL || ct->c_list->refcount == 0))
1598  CatCacheRemoveCTup(ct->my_cache, ct);
1599 }
1600 
1601 
1602 /*
1603  * GetCatCacheHashValue
1604  *
1605  * Compute the hash value for a given set of search keys.
1606  *
1607  * The reason for exposing this as part of the API is that the hash value is
1608  * exposed in cache invalidation operations, so there are places outside the
1609  * catcache code that need to be able to compute the hash values.
1610  */
1611 uint32
1613  Datum v1,
1614  Datum v2,
1615  Datum v3,
1616  Datum v4)
1617 {
1618  /*
1619  * one-time startup overhead for each cache
1620  */
1621  if (cache->cc_tupdesc == NULL)
1623 
1624  /*
1625  * calculate the hash value
1626  */
1627  return CatalogCacheComputeHashValue(cache, cache->cc_nkeys, v1, v2, v3, v4);
1628 }
1629 
1630 
1631 /*
1632  * SearchCatCacheList
1633  *
1634  * Generate a list of all tuples matching a partial key (that is,
1635  * a key specifying just the first K of the cache's N key columns).
1636  *
1637  * It doesn't make any sense to specify all of the cache's key columns
1638  * here: since the key is unique, there could be at most one match, so
1639  * you ought to use SearchCatCache() instead. Hence this function takes
1640  * one fewer Datum argument than SearchCatCache() does.
1641  *
1642  * The caller must not modify the list object or the pointed-to tuples,
1643  * and must call ReleaseCatCacheList() when done with the list.
1644  */
1645 CatCList *
1647  int nkeys,
1648  Datum v1,
1649  Datum v2,
1650  Datum v3)
1651 {
1652  Datum v4 = 0; /* dummy last-column value */
1654  uint32 lHashValue;
1655  Index lHashIndex;
1656  dlist_iter iter;
1657  dlist_head *lbucket;
1658  CatCList *cl;
1659  CatCTup *ct;
1660  List *volatile ctlist;
1661  ListCell *ctlist_item;
1662  int nmembers;
1663  bool ordered;
1664  HeapTuple ntp;
1665  MemoryContext oldcxt;
1666  int i;
1667 
1668  /*
1669  * one-time startup overhead for each cache
1670  */
1671  if (unlikely(cache->cc_tupdesc == NULL))
1673 
1674  Assert(nkeys > 0 && nkeys < cache->cc_nkeys);
1675 
1676 #ifdef CATCACHE_STATS
1677  cache->cc_lsearches++;
1678 #endif
1679 
1680  /* Initialize local parameter array */
1681  arguments[0] = v1;
1682  arguments[1] = v2;
1683  arguments[2] = v3;
1684  arguments[3] = v4;
1685 
1686  /*
1687  * If we haven't previously done a list search in this cache, create the
1688  * bucket header array; otherwise, consider whether it's time to enlarge
1689  * it.
1690  */
1691  if (cache->cc_lbucket == NULL)
1692  {
1693  /* Arbitrary initial size --- must be a power of 2 */
1694  int nbuckets = 16;
1695 
1696  cache->cc_lbucket = (dlist_head *)
1698  nbuckets * sizeof(dlist_head));
1699  /* Don't set cc_nlbuckets if we get OOM allocating cc_lbucket */
1700  cache->cc_nlbuckets = nbuckets;
1701  }
1702  else
1703  {
1704  /*
1705  * If the hash table has become too full, enlarge the buckets array.
1706  * Quite arbitrarily, we enlarge when fill factor > 2.
1707  */
1708  if (cache->cc_nlist > cache->cc_nlbuckets * 2)
1709  RehashCatCacheLists(cache);
1710  }
1711 
1712  /*
1713  * Find the hash bucket in which to look for the CatCList.
1714  */
1715  lHashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4);
1716  lHashIndex = HASH_INDEX(lHashValue, cache->cc_nlbuckets);
1717 
1718  /*
1719  * scan the items until we find a match or exhaust our list
1720  *
1721  * Note: it's okay to use dlist_foreach here, even though we modify the
1722  * dlist within the loop, because we don't continue the loop afterwards.
1723  */
1724  lbucket = &cache->cc_lbucket[lHashIndex];
1725  dlist_foreach(iter, lbucket)
1726  {
1727  cl = dlist_container(CatCList, cache_elem, iter.cur);
1728 
1729  if (cl->dead)
1730  continue; /* ignore dead entries */
1731 
1732  if (cl->hash_value != lHashValue)
1733  continue; /* quickly skip entry if wrong hash val */
1734 
1735  /*
1736  * see if the cached list matches our key.
1737  */
1738  if (cl->nkeys != nkeys)
1739  continue;
1740 
1741  if (!CatalogCacheCompareTuple(cache, nkeys, cl->keys, arguments))
1742  continue;
1743 
1744  /*
1745  * We found a matching list. Move the list to the front of the list
1746  * for its hashbucket, so as to speed subsequent searches. (We do not
1747  * move the members to the fronts of their hashbucket lists, however,
1748  * since there's no point in that unless they are searched for
1749  * individually.)
1750  */
1751  dlist_move_head(lbucket, &cl->cache_elem);
1752 
1753  /* Bump the list's refcount and return it */
1755  cl->refcount++;
1757 
1758  CACHE_elog(DEBUG2, "SearchCatCacheList(%s): found list",
1759  cache->cc_relname);
1760 
1761 #ifdef CATCACHE_STATS
1762  cache->cc_lhits++;
1763 #endif
1764 
1765  return cl;
1766  }
1767 
1768  /*
1769  * List was not found in cache, so we have to build it by reading the
1770  * relation. For each matching tuple found in the relation, use an
1771  * existing cache entry if possible, else build a new one.
1772  *
1773  * We have to bump the member refcounts temporarily to ensure they won't
1774  * get dropped from the cache while loading other members. We use a PG_TRY
1775  * block to ensure we can undo those refcounts if we get an error before
1776  * we finish constructing the CatCList. ctlist must be valid throughout
1777  * the PG_TRY block.
1778  */
1779  ctlist = NIL;
1780 
1781  PG_TRY();
1782  {
1783  ScanKeyData cur_skey[CATCACHE_MAXKEYS];
1784  Relation relation;
1785  SysScanDesc scandesc;
1786  bool stale;
1787 
1788  relation = table_open(cache->cc_reloid, AccessShareLock);
1789 
1790  do
1791  {
1792  /*
1793  * Ok, need to make a lookup in the relation, copy the scankey and
1794  * fill out any per-call fields. (We must re-do this when
1795  * retrying, because systable_beginscan scribbles on the scankey.)
1796  */
1797  memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * cache->cc_nkeys);
1798  cur_skey[0].sk_argument = v1;
1799  cur_skey[1].sk_argument = v2;
1800  cur_skey[2].sk_argument = v3;
1801  cur_skey[3].sk_argument = v4;
1802 
1803  scandesc = systable_beginscan(relation,
1804  cache->cc_indexoid,
1805  IndexScanOK(cache, cur_skey),
1806  NULL,
1807  nkeys,
1808  cur_skey);
1809 
1810  /* The list will be ordered iff we are doing an index scan */
1811  ordered = (scandesc->irel != NULL);
1812 
1813  stale = false;
1814 
1815  while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
1816  {
1817  uint32 hashValue;
1818  Index hashIndex;
1819  bool found = false;
1820  dlist_head *bucket;
1821 
1822  /*
1823  * See if there's an entry for this tuple already.
1824  */
1825  ct = NULL;
1826  hashValue = CatalogCacheComputeTupleHashValue(cache, cache->cc_nkeys, ntp);
1827  hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets);
1828 
1829  bucket = &cache->cc_bucket[hashIndex];
1830  dlist_foreach(iter, bucket)
1831  {
1832  ct = dlist_container(CatCTup, cache_elem, iter.cur);
1833 
1834  if (ct->dead || ct->negative)
1835  continue; /* ignore dead and negative entries */
1836 
1837  if (ct->hash_value != hashValue)
1838  continue; /* quickly skip entry if wrong hash val */
1839 
1840  if (!ItemPointerEquals(&(ct->tuple.t_self), &(ntp->t_self)))
1841  continue; /* not same tuple */
1842 
1843  /*
1844  * Found a match, but can't use it if it belongs to
1845  * another list already
1846  */
1847  if (ct->c_list)
1848  continue;
1849 
1850  found = true;
1851  break; /* A-OK */
1852  }
1853 
1854  if (!found)
1855  {
1856  /* We didn't find a usable entry, so make a new one */
1857  ct = CatalogCacheCreateEntry(cache, ntp, scandesc, NULL,
1858  hashValue, hashIndex);
1859  /* upon failure, we must start the scan over */
1860  if (ct == NULL)
1861  {
1862  /*
1863  * Release refcounts on any items we already had. We
1864  * dare not try to free them if they're now
1865  * unreferenced, since an error while doing that would
1866  * result in the PG_CATCH below doing extra refcount
1867  * decrements. Besides, we'll likely re-adopt those
1868  * items in the next iteration, so it's not worth
1869  * complicating matters to try to get rid of them.
1870  */
1871  foreach(ctlist_item, ctlist)
1872  {
1873  ct = (CatCTup *) lfirst(ctlist_item);
1874  Assert(ct->c_list == NULL);
1875  Assert(ct->refcount > 0);
1876  ct->refcount--;
1877  }
1878  /* Reset ctlist in preparation for new try */
1879  ctlist = NIL;
1880  stale = true;
1881  break;
1882  }
1883  }
1884 
1885  /* Careful here: add entry to ctlist, then bump its refcount */
1886  /* This way leaves state correct if lappend runs out of memory */
1887  ctlist = lappend(ctlist, ct);
1888  ct->refcount++;
1889  }
1890 
1891  systable_endscan(scandesc);
1892  } while (stale);
1893 
1894  table_close(relation, AccessShareLock);
1895 
1896  /* Make sure the resource owner has room to remember this entry. */
1898 
1899  /* Now we can build the CatCList entry. */
1901  nmembers = list_length(ctlist);
1902  cl = (CatCList *)
1903  palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *));
1904 
1905  /* Extract key values */
1906  CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno,
1907  arguments, cl->keys);
1908  MemoryContextSwitchTo(oldcxt);
1909 
1910  /*
1911  * We are now past the last thing that could trigger an elog before we
1912  * have finished building the CatCList and remembering it in the
1913  * resource owner. So it's OK to fall out of the PG_TRY, and indeed
1914  * we'd better do so before we start marking the members as belonging
1915  * to the list.
1916  */
1917  }
1918  PG_CATCH();
1919  {
1920  foreach(ctlist_item, ctlist)
1921  {
1922  ct = (CatCTup *) lfirst(ctlist_item);
1923  Assert(ct->c_list == NULL);
1924  Assert(ct->refcount > 0);
1925  ct->refcount--;
1926  if (
1927 #ifndef CATCACHE_FORCE_RELEASE
1928  ct->dead &&
1929 #endif
1930  ct->refcount == 0 &&
1931  (ct->c_list == NULL || ct->c_list->refcount == 0))
1932  CatCacheRemoveCTup(cache, ct);
1933  }
1934 
1935  PG_RE_THROW();
1936  }
1937  PG_END_TRY();
1938 
1939  cl->cl_magic = CL_MAGIC;
1940  cl->my_cache = cache;
1941  cl->refcount = 0; /* for the moment */
1942  cl->dead = false;
1943  cl->ordered = ordered;
1944  cl->nkeys = nkeys;
1945  cl->hash_value = lHashValue;
1946  cl->n_members = nmembers;
1947 
1948  i = 0;
1949  foreach(ctlist_item, ctlist)
1950  {
1951  cl->members[i++] = ct = (CatCTup *) lfirst(ctlist_item);
1952  Assert(ct->c_list == NULL);
1953  ct->c_list = cl;
1954  /* release the temporary refcount on the member */
1955  Assert(ct->refcount > 0);
1956  ct->refcount--;
1957  /* mark list dead if any members already dead */
1958  if (ct->dead)
1959  cl->dead = true;
1960  }
1961  Assert(i == nmembers);
1962 
1963  /*
1964  * Add the CatCList to the appropriate bucket, and count it.
1965  */
1966  dlist_push_head(lbucket, &cl->cache_elem);
1967 
1968  cache->cc_nlist++;
1969 
1970  /* Finally, bump the list's refcount and return it */
1971  cl->refcount++;
1973 
1974  CACHE_elog(DEBUG2, "SearchCatCacheList(%s): made list of %d members",
1975  cache->cc_relname, nmembers);
1976 
1977  return cl;
1978 }
1979 
1980 /*
1981  * ReleaseCatCacheList
1982  *
1983  * Decrement the reference count of a catcache list.
1984  */
1985 void
1987 {
1989 }
1990 
1991 static void
1993 {
1994  /* Safety checks to ensure we were handed a cache entry */
1995  Assert(list->cl_magic == CL_MAGIC);
1996  Assert(list->refcount > 0);
1997  list->refcount--;
1998  if (resowner)
2000 
2001  if (
2002 #ifndef CATCACHE_FORCE_RELEASE
2003  list->dead &&
2004 #endif
2005  list->refcount == 0)
2006  CatCacheRemoveCList(list->my_cache, list);
2007 }
2008 
2009 
2010 /*
2011  * CatalogCacheCreateEntry
2012  * Create a new CatCTup entry, copying the given HeapTuple and other
2013  * supplied data into it. The new entry initially has refcount 0.
2014  *
2015  * To create a normal cache entry, ntp must be the HeapTuple just fetched
2016  * from scandesc, and "arguments" is not used. To create a negative cache
2017  * entry, pass NULL for ntp and scandesc; then "arguments" is the cache
2018  * keys to use. In either case, hashValue/hashIndex are the hash values
2019  * computed from the cache keys.
2020  *
2021  * Returns NULL if we attempt to detoast the tuple and observe that it
2022  * became stale. (This cannot happen for a negative entry.) Caller must
2023  * retry the tuple lookup in that case.
2024  */
2025 static CatCTup *
2027  Datum *arguments,
2028  uint32 hashValue, Index hashIndex)
2029 {
2030  CatCTup *ct;
2031  HeapTuple dtp;
2032  MemoryContext oldcxt;
2033 
2034  if (ntp)
2035  {
2036  int i;
2037 
2038  /*
2039  * The visibility recheck below essentially never fails during our
2040  * regression tests, and there's no easy way to force it to fail for
2041  * testing purposes. To ensure we have test coverage for the retry
2042  * paths in our callers, make debug builds randomly fail about 0.1% of
2043  * the times through this code path, even when there's no toasted
2044  * fields.
2045  */
2046 #ifdef USE_ASSERT_CHECKING
2048  return NULL;
2049 #endif
2050 
2051  /*
2052  * If there are any out-of-line toasted fields in the tuple, expand
2053  * them in-line. This saves cycles during later use of the catcache
2054  * entry, and also protects us against the possibility of the toast
2055  * tuples being freed before we attempt to fetch them, in case of
2056  * something using a slightly stale catcache entry.
2057  */
2058  if (HeapTupleHasExternal(ntp))
2059  {
2060  dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc);
2061 
2062  /*
2063  * The tuple could become stale while we are doing toast table
2064  * access (since AcceptInvalidationMessages can run then), so we
2065  * must recheck its visibility afterwards.
2066  */
2067  if (!systable_recheck_tuple(scandesc, ntp))
2068  {
2069  heap_freetuple(dtp);
2070  return NULL;
2071  }
2072  }
2073  else
2074  dtp = ntp;
2075 
2076  /* Allocate memory for CatCTup and the cached tuple in one go */
2078 
2079  ct = (CatCTup *) palloc(sizeof(CatCTup) +
2080  MAXIMUM_ALIGNOF + dtp->t_len);
2081  ct->tuple.t_len = dtp->t_len;
2082  ct->tuple.t_self = dtp->t_self;
2083  ct->tuple.t_tableOid = dtp->t_tableOid;
2084  ct->tuple.t_data = (HeapTupleHeader)
2085  MAXALIGN(((char *) ct) + sizeof(CatCTup));
2086  /* copy tuple contents */
2087  memcpy((char *) ct->tuple.t_data,
2088  (const char *) dtp->t_data,
2089  dtp->t_len);
2090  MemoryContextSwitchTo(oldcxt);
2091 
2092  if (dtp != ntp)
2093  heap_freetuple(dtp);
2094 
2095  /* extract keys - they'll point into the tuple if not by-value */
2096  for (i = 0; i < cache->cc_nkeys; i++)
2097  {
2098  Datum atp;
2099  bool isnull;
2100 
2101  atp = heap_getattr(&ct->tuple,
2102  cache->cc_keyno[i],
2103  cache->cc_tupdesc,
2104  &isnull);
2105  Assert(!isnull);
2106  ct->keys[i] = atp;
2107  }
2108  }
2109  else
2110  {
2111  /* Set up keys for a negative cache entry */
2113  ct = (CatCTup *) palloc(sizeof(CatCTup));
2114 
2115  /*
2116  * Store keys - they'll point into separately allocated memory if not
2117  * by-value.
2118  */
2119  CatCacheCopyKeys(cache->cc_tupdesc, cache->cc_nkeys, cache->cc_keyno,
2120  arguments, ct->keys);
2121  MemoryContextSwitchTo(oldcxt);
2122  }
2123 
2124  /*
2125  * Finish initializing the CatCTup header, and add it to the cache's
2126  * linked list and counts.
2127  */
2128  ct->ct_magic = CT_MAGIC;
2129  ct->my_cache = cache;
2130  ct->c_list = NULL;
2131  ct->refcount = 0; /* for the moment */
2132  ct->dead = false;
2133  ct->negative = (ntp == NULL);
2134  ct->hash_value = hashValue;
2135 
2136  dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
2137 
2138  cache->cc_ntup++;
2139  CacheHdr->ch_ntup++;
2140 
2141  /*
2142  * If the hash table has become too full, enlarge the buckets array. Quite
2143  * arbitrarily, we enlarge when fill factor > 2.
2144  */
2145  if (cache->cc_ntup > cache->cc_nbuckets * 2)
2146  RehashCatCache(cache);
2147 
2148  return ct;
2149 }
2150 
2151 /*
2152  * Helper routine that frees keys stored in the keys array.
2153  */
2154 static void
2155 CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos, Datum *keys)
2156 {
2157  int i;
2158 
2159  for (i = 0; i < nkeys; i++)
2160  {
2161  int attnum = attnos[i];
2162  Form_pg_attribute att;
2163 
2164  /* system attribute are not supported in caches */
2165  Assert(attnum > 0);
2166 
2167  att = TupleDescAttr(tupdesc, attnum - 1);
2168 
2169  if (!att->attbyval)
2170  pfree(DatumGetPointer(keys[i]));
2171  }
2172 }
2173 
2174 /*
2175  * Helper routine that copies the keys in the srckeys array into the dstkeys
2176  * one, guaranteeing that the datums are fully allocated in the current memory
2177  * context.
2178  */
2179 static void
2180 CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
2181  Datum *srckeys, Datum *dstkeys)
2182 {
2183  int i;
2184 
2185  /*
2186  * XXX: memory and lookup performance could possibly be improved by
2187  * storing all keys in one allocation.
2188  */
2189 
2190  for (i = 0; i < nkeys; i++)
2191  {
2192  int attnum = attnos[i];
2193  Form_pg_attribute att = TupleDescAttr(tupdesc, attnum - 1);
2194  Datum src = srckeys[i];
2195  NameData srcname;
2196 
2197  /*
2198  * Must be careful in case the caller passed a C string where a NAME
2199  * is wanted: convert the given argument to a correctly padded NAME.
2200  * Otherwise the memcpy() done by datumCopy() could fall off the end
2201  * of memory.
2202  */
2203  if (att->atttypid == NAMEOID)
2204  {
2205  namestrcpy(&srcname, DatumGetCString(src));
2206  src = NameGetDatum(&srcname);
2207  }
2208 
2209  dstkeys[i] = datumCopy(src,
2210  att->attbyval,
2211  att->attlen);
2212  }
2213 }
2214 
2215 /*
2216  * PrepareToInvalidateCacheTuple()
2217  *
2218  * This is part of a rather subtle chain of events, so pay attention:
2219  *
2220  * When a tuple is inserted or deleted, it cannot be flushed from the
2221  * catcaches immediately, for reasons explained at the top of cache/inval.c.
2222  * Instead we have to add entry(s) for the tuple to a list of pending tuple
2223  * invalidations that will be done at the end of the command or transaction.
2224  *
2225  * The lists of tuples that need to be flushed are kept by inval.c. This
2226  * routine is a helper routine for inval.c. Given a tuple belonging to
2227  * the specified relation, find all catcaches it could be in, compute the
2228  * correct hash value for each such catcache, and call the specified
2229  * function to record the cache id and hash value in inval.c's lists.
2230  * SysCacheInvalidate will be called later, if appropriate,
2231  * using the recorded information.
2232  *
2233  * For an insert or delete, tuple is the target tuple and newtuple is NULL.
2234  * For an update, we are called just once, with tuple being the old tuple
2235  * version and newtuple the new version. We should make two list entries
2236  * if the tuple's hash value changed, but only one if it didn't.
2237  *
2238  * Note that it is irrelevant whether the given tuple is actually loaded
2239  * into the catcache at the moment. Even if it's not there now, it might
2240  * be by the end of the command, or there might be a matching negative entry
2241  * to flush --- or other backends' caches might have such entries --- so
2242  * we have to make list entries to flush it later.
2243  *
2244  * Also note that it's not an error if there are no catcaches for the
2245  * specified relation. inval.c doesn't know exactly which rels have
2246  * catcaches --- it will call this routine for any tuple that's in a
2247  * system relation.
2248  */
2249 void
2251  HeapTuple tuple,
2252  HeapTuple newtuple,
2253  void (*function) (int, uint32, Oid))
2254 {
2255  slist_iter iter;
2256  Oid reloid;
2257 
2258  CACHE_elog(DEBUG2, "PrepareToInvalidateCacheTuple: called");
2259 
2260  /*
2261  * sanity checks
2262  */
2263  Assert(RelationIsValid(relation));
2264  Assert(HeapTupleIsValid(tuple));
2265  Assert(PointerIsValid(function));
2266  Assert(CacheHdr != NULL);
2267 
2268  reloid = RelationGetRelid(relation);
2269 
2270  /* ----------------
2271  * for each cache
2272  * if the cache contains tuples from the specified relation
2273  * compute the tuple's hash value(s) in this cache,
2274  * and call the passed function to register the information.
2275  * ----------------
2276  */
2277 
2279  {
2280  CatCache *ccp = slist_container(CatCache, cc_next, iter.cur);
2281  uint32 hashvalue;
2282  Oid dbid;
2283 
2284  if (ccp->cc_reloid != reloid)
2285  continue;
2286 
2287  /* Just in case cache hasn't finished initialization yet... */
2288  if (ccp->cc_tupdesc == NULL)
2290 
2291  hashvalue = CatalogCacheComputeTupleHashValue(ccp, ccp->cc_nkeys, tuple);
2292  dbid = ccp->cc_relisshared ? (Oid) 0 : MyDatabaseId;
2293 
2294  (*function) (ccp->id, hashvalue, dbid);
2295 
2296  if (newtuple)
2297  {
2298  uint32 newhashvalue;
2299 
2300  newhashvalue = CatalogCacheComputeTupleHashValue(ccp, ccp->cc_nkeys, newtuple);
2301 
2302  if (newhashvalue != hashvalue)
2303  (*function) (ccp->id, newhashvalue, dbid);
2304  }
2305  }
2306 }
2307 
2308 /* ResourceOwner callbacks */
2309 
2310 static void
2312 {
2314 }
2315 
2316 static char *
2318 {
2320  CatCTup *ct = (CatCTup *) (((char *) tuple) -
2321  offsetof(CatCTup, tuple));
2322 
2323  /* Safety check to ensure we were handed a cache entry */
2324  Assert(ct->ct_magic == CT_MAGIC);
2325 
2326  return psprintf("cache %s (%d), tuple %u/%u has count %d",
2327  ct->my_cache->cc_relname, ct->my_cache->id,
2328  ItemPointerGetBlockNumber(&(tuple->t_self)),
2330  ct->refcount);
2331 }
2332 
2333 static void
2335 {
2337 }
2338 
2339 static char *
2341 {
2343 
2344  return psprintf("cache %s (%d), list %p has count %d",
2345  list->my_cache->cc_relname, list->my_cache->id,
2346  list, list->refcount);
2347 }
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
#define NameStr(name)
Definition: c.h:733
unsigned int uint32
Definition: c.h:493
#define pg_noinline
Definition: c.h:237
#define MAXALIGN(LEN)
Definition: c.h:798
#define PG_UINT32_MAX
Definition: c.h:577
signed int int32
Definition: c.h:481
#define PointerIsValid(pointer)
Definition: c.h:750
regproc RegProcedure
Definition: c.h:637
#define unlikely(x)
Definition: c.h:298
unsigned int Index
Definition: c.h:601
static bool chareqfast(Datum a, Datum b)
Definition: catcache.c:172
HeapTuple SearchCatCache2(CatCache *cache, Datum v1, Datum v2)
Definition: catcache.c:1286
static bool int4eqfast(Datum a, Datum b)
Definition: catcache.c:213
HeapTuple SearchCatCache3(CatCache *cache, Datum v1, Datum v2, Datum v3)
Definition: catcache.c:1294
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1986
static void CatalogCacheInitializeCache(CatCache *cache)
Definition: catcache.c:1035
static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache, int nkeys, uint32 hashValue, Index hashIndex, Datum v1, Datum v2, Datum v3, Datum v4)
Definition: catcache.c:1424
static bool int2eqfast(Datum a, Datum b)
Definition: catcache.c:201
static void ReleaseCatCacheWithOwner(HeapTuple tuple, ResourceOwner resowner)
Definition: catcache.c:1579
static uint32 int4hashfast(Datum datum)
Definition: catcache.c:219
void InitCatCachePhase2(CatCache *cache, bool touch_index)
Definition: catcache.c:1144
void ResetCatalogCaches(void)
Definition: catcache.c:753
static void ResOwnerReleaseCatCache(Datum res)
Definition: catcache.c:2311
uint32 GetCatCacheHashValue(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4)
Definition: catcache.c:1612
static void CatCacheRemoveCTup(CatCache *cache, CatCTup *ct)
Definition: catcache.c:509
static char * ResOwnerPrintCatCache(Datum res)
Definition: catcache.c:2317
static void RehashCatCache(CatCache *cp)
Definition: catcache.c:934
static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys, HeapTuple tuple)
Definition: catcache.c:367
HeapTuple SearchCatCache4(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4)
Definition: catcache.c:1302
static void ResourceOwnerForgetCatCacheListRef(ResourceOwner owner, CatCList *list)
Definition: catcache.c:155
static CatCTup * CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, SysScanDesc scandesc, Datum *arguments, uint32 hashValue, Index hashIndex)
Definition: catcache.c:2026
static const ResourceOwnerDesc catcache_resowner_desc
Definition: catcache.c:118
static void ResOwnerReleaseCatCacheList(Datum res)
Definition: catcache.c:2334
static void ResourceOwnerRememberCatCacheRef(ResourceOwner owner, HeapTuple tuple)
Definition: catcache.c:140
#define CatalogCacheInitializeCache_DEBUG1
Definition: catcache.c:1030
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos, Datum *srckeys, Datum *dstkeys)
Definition: catcache.c:2180
static HeapTuple SearchCatCacheInternal(CatCache *cache, int nkeys, Datum v1, Datum v2, Datum v3, Datum v4)
Definition: catcache.c:1312
CatCache * InitCatCache(int id, Oid reloid, Oid indexoid, int nkeys, const int *key, int nbuckets)
Definition: catcache.c:827
CatCList * SearchCatCacheList(CatCache *cache, int nkeys, Datum v1, Datum v2, Datum v3)
Definition: catcache.c:1646
static char * ResOwnerPrintCatCacheList(Datum res)
Definition: catcache.c:2340
static void ReleaseCatCacheListWithOwner(CatCList *list, ResourceOwner resowner)
Definition: catcache.c:1992
static CatCacheHeader * CacheHdr
Definition: catcache.c:64
static uint32 namehashfast(Datum datum)
Definition: catcache.c:193
void CreateCacheMemoryContext(void)
Definition: catcache.c:679
static void ResetCatalogCache(CatCache *cache)
Definition: catcache.c:701
static const ResourceOwnerDesc catlistref_resowner_desc
Definition: catcache.c:128
void PrepareToInvalidateCacheTuple(Relation relation, HeapTuple tuple, HeapTuple newtuple, void(*function)(int, uint32, Oid))
Definition: catcache.c:2250
static void GetCCHashEqFuncs(Oid keytype, CCHashFN *hashfunc, RegProcedure *eqfunc, CCFastEqualFN *fasteqfunc)
Definition: catcache.c:255
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys, Datum v1, Datum v2, Datum v3, Datum v4)
Definition: catcache.c:325
static bool CatalogCacheCompareTuple(const CatCache *cache, int nkeys, const Datum *cachekeys, const Datum *searchkeys)
Definition: catcache.c:422
void CatCacheInvalidate(CatCache *cache, uint32 hashValue)
Definition: catcache.c:606
static void ResourceOwnerForgetCatCacheRef(ResourceOwner owner, HeapTuple tuple)
Definition: catcache.c:145
static bool nameeqfast(Datum a, Datum b)
Definition: catcache.c:184
static uint32 charhashfast(Datum datum)
Definition: catcache.c:178
static void RehashCatCacheLists(CatCache *cp)
Definition: catcache.c:972
HeapTuple SearchCatCache1(CatCache *cache, Datum v1)
Definition: catcache.c:1278
#define InitCatCache_DEBUG2
Definition: catcache.c:823
static uint32 oidvectorhashfast(Datum datum)
Definition: catcache.c:248
static void ResourceOwnerRememberCatCacheListRef(ResourceOwner owner, CatCList *list)
Definition: catcache.c:150
static bool texteqfast(Datum a, Datum b)
Definition: catcache.c:225
#define CACHE_elog(...)
Definition: catcache.c:60
static bool oidvectoreqfast(Datum a, Datum b)
Definition: catcache.c:242
void CatalogCacheFlushCatalog(Oid catId)
Definition: catcache.c:783
static uint32 int2hashfast(Datum datum)
Definition: catcache.c:207
#define CatalogCacheInitializeCache_DEBUG2
Definition: catcache.c:1031
static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos, Datum *keys)
Definition: catcache.c:2155
static void CatCacheRemoveCList(CatCache *cache, CatCList *cl)
Definition: catcache.c:551
#define HASH_INDEX(h, sz)
Definition: catcache.c:50
static bool IndexScanOK(CatCache *cache, ScanKey cur_skey)
Definition: catcache.c:1196
static uint32 texthashfast(Datum datum)
Definition: catcache.c:235
void ReleaseCatCache(HeapTuple tuple)
Definition: catcache.c:1573
HeapTuple SearchCatCache(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4)
Definition: catcache.c:1261
#define CT_MAGIC
Definition: catcache.h:91
uint32(* CCHashFN)(Datum datum)
Definition: catcache.h:39
#define CATCACHE_MAXKEYS
Definition: catcache.h:35
bool(* CCFastEqualFN)(Datum a, Datum b)
Definition: catcache.h:42
#define CL_MAGIC
Definition: catcache.h:162
Datum datumCopy(Datum value, bool typByVal, int typLen)
Definition: datum.c:132
#define PG_RE_THROW()
Definition: elog.h:411
#define FATAL
Definition: elog.h:41
#define PG_TRY(...)
Definition: elog.h:370
#define DEBUG2
Definition: elog.h:29
#define PG_END_TRY(...)
Definition: elog.h:395
#define DEBUG1
Definition: elog.h:30
#define PG_CATCH(...)
Definition: elog.h:380
#define elog(elevel,...)
Definition: elog.h:224
#define MCXT_ALLOC_ZERO
Definition: fe_memutils.h:18
Datum DirectFunctionCall2Coll(PGFunction func, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:812
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
Datum DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
Definition: fmgr.c:792
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:644
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:642
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:596
bool systable_recheck_tuple(SysScanDesc sysscan, HeapTuple tup)
Definition: genam.c:562
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:503
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:384
Oid MyDatabaseId
Definition: globals.c:91
static uint32 murmurhash32(uint32 data)
Definition: hashfn.h:92
static Datum hash_any(const unsigned char *k, int keylen)
Definition: hashfn.h:31
Datum hashoidvector(PG_FUNCTION_ARGS)
Definition: hashfunc.c:232
Datum hashtext(PG_FUNCTION_ARGS)
Definition: hashfunc.c:267
HeapTuple toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
Definition: heaptoast.c:350
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
HeapTupleData * HeapTuple
Definition: htup.h:71
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:792
#define HeapTupleHasExternal(tuple)
Definition: htup_details.h:671
static Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:749
#define dlist_foreach(iter, lhead)
Definition: ilist.h:623
static void dlist_delete(dlist_node *node)
Definition: ilist.h:405
static void slist_init(slist_head *head)
Definition: ilist.h:986
static void dlist_push_head(dlist_head *head, dlist_node *node)
Definition: ilist.h:347
#define dlist_foreach_modify(iter, lhead)
Definition: ilist.h:640
static void slist_push_head(slist_head *head, slist_node *node)
Definition: ilist.h:1006
#define slist_container(type, membername, ptr)
Definition: ilist.h:1106
static void dlist_move_head(dlist_head *head, dlist_node *node)
Definition: ilist.h:467
#define slist_foreach(iter, lhead)
Definition: ilist.h:1132
#define dlist_container(type, membername, ptr)
Definition: ilist.h:593
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
void CallSyscacheCallbacks(int cacheid, uint32 hashvalue)
Definition: inval.c:1577
void on_proc_exit(pg_on_exit_callback function, Datum arg)
Definition: ipc.c:309
int b
Definition: isn.c:70
int a
Definition: isn.c:69
int i
Definition: isn.c:73
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition: itemptr.h:124
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
Assert(fmt[strlen(fmt) - 1] !='\n')
List * lappend(List *list, void *datum)
Definition: list.c:339
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:227
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:108
#define AccessShareLock
Definition: lockdefs.h:36
char * pstrdup(const char *in)
Definition: mcxt.c:1683
void pfree(void *pointer)
Definition: mcxt.c:1508
MemoryContext TopMemoryContext
Definition: mcxt.c:137
void * palloc0(Size size)
Definition: mcxt.c:1334
void * MemoryContextAllocZero(MemoryContext context, Size size)
Definition: mcxt.c:1202
void * palloc_aligned(Size size, Size alignto, int flags)
Definition: mcxt.c:1498
MemoryContext CacheMemoryContext
Definition: mcxt.c:140
void * palloc(Size size)
Definition: mcxt.c:1304
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
#define IsBootstrapProcessingMode()
Definition: miscadmin.h:454
void namestrcpy(Name name, const char *str)
Definition: name.c:233
Datum oidvectoreq(PG_FUNCTION_ARGS)
Definition: oid.c:344
int16 attnum
Definition: pg_attribute.h:74
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
void * arg
static uint32 pg_rotate_left32(uint32 word, int n)
Definition: pg_bitutils.h:325
#define NAMEDATALEN
#define PG_CACHE_LINE_SIZE
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
uint32 pg_prng_uint32(pg_prng_state *state)
Definition: pg_prng.c:227
pg_prng_state pg_global_prng_state
Definition: pg_prng.c:34
static bool DatumGetBool(Datum X)
Definition: postgres.h:90
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
static Name DatumGetName(Datum X)
Definition: postgres.h:360
static char * DatumGetCString(Datum X)
Definition: postgres.h:335
uintptr_t Datum
Definition: postgres.h:64
static Datum NameGetDatum(const NameData *X)
Definition: postgres.h:373
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
static char DatumGetChar(Datum X)
Definition: postgres.h:112
static int16 DatumGetInt16(Datum X)
Definition: postgres.h:162
static int32 DatumGetInt32(Datum X)
Definition: postgres.h:202
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
MemoryContextSwitchTo(old_ctx)
#define RelationGetForm(relation)
Definition: rel.h:501
#define RelationGetRelid(relation)
Definition: rel.h:507
#define RelationGetDescr(relation)
Definition: rel.h:533
#define RelationGetRelationName(relation)
Definition: rel.h:541
#define RelationIsValid(relation)
Definition: rel.h:480
bool criticalRelcachesBuilt
Definition: relcache.c:140
bool criticalSharedRelcachesBuilt
Definition: relcache.c:146
ResourceOwner CurrentResourceOwner
Definition: resowner.c:165
void ResourceOwnerForget(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:554
void ResourceOwnerRemember(ResourceOwner owner, Datum value, const ResourceOwnerDesc *kind)
Definition: resowner.c:514
void ResourceOwnerEnlarge(ResourceOwner owner)
Definition: resowner.c:442
#define RELEASE_PRIO_CATCACHE_LIST_REFS
Definition: resowner.h:72
@ RESOURCE_RELEASE_AFTER_LOCKS
Definition: resowner.h:56
#define RELEASE_PRIO_CATCACHE_REFS
Definition: resowner.h:71
#define BTEqualStrategyNumber
Definition: stratnum.h:31
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
HeapTupleHeader t_data
Definition: htup.h:68
Oid t_tableOid
Definition: htup.h:66
Definition: pg_list.h:54
Form_pg_index rd_index
Definition: rel.h:192
const char * name
Definition: resowner.h:93
Datum sk_argument
Definition: skey.h:72
FmgrInfo sk_func
Definition: skey.h:71
Oid sk_subtype
Definition: skey.h:69
Oid sk_collation
Definition: skey.h:70
StrategyNumber sk_strategy
Definition: skey.h:68
AttrNumber sk_attno
Definition: skey.h:67
Relation irel
Definition: relscan.h:184
const char * cc_relname
Definition: catcache.h:59
CCHashFN cc_hashfunc[CATCACHE_MAXKEYS]
Definition: catcache.h:50
dlist_head * cc_bucket
Definition: catcache.h:49
slist_node cc_next
Definition: catcache.h:63
Oid cc_reloid
Definition: catcache.h:60
int cc_nkeys
Definition: catcache.h:54
int cc_keyno[CATCACHE_MAXKEYS]
Definition: catcache.h:53
CCFastEqualFN cc_fastequal[CATCACHE_MAXKEYS]
Definition: catcache.h:51
Oid cc_indexoid
Definition: catcache.h:61
int cc_nbuckets
Definition: catcache.h:47
bool cc_relisshared
Definition: catcache.h:62
int cc_ntup
Definition: catcache.h:55
ScanKeyData cc_skey[CATCACHE_MAXKEYS]
Definition: catcache.h:64
int cc_nlist
Definition: catcache.h:56
int id
Definition: catcache.h:46
TupleDesc cc_tupdesc
Definition: catcache.h:48
int cc_nlbuckets
Definition: catcache.h:57
dlist_head * cc_lbucket
Definition: catcache.h:58
slist_head ch_caches
Definition: catcache.h:186
dlist_node cache_elem
Definition: catcache.h:166
int refcount
Definition: catcache.h:174
CatCache * my_cache
Definition: catcache.h:179
int cl_magic
Definition: catcache.h:161
bool dead
Definition: catcache.h:175
short nkeys
Definition: catcache.h:177
Datum keys[CATCACHE_MAXKEYS]
Definition: catcache.h:172
bool ordered
Definition: catcache.h:176
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:180
uint32 hash_value
Definition: catcache.h:164
int n_members
Definition: catcache.h:178
int ct_magic
Definition: catcache.h:90
int refcount
Definition: catcache.h:120
bool negative
Definition: catcache.h:122
dlist_node cache_elem
Definition: catcache.h:106
HeapTupleData tuple
Definition: catcache.h:123
CatCache * my_cache
Definition: catcache.h:134
struct catclist * c_list
Definition: catcache.h:132
Datum keys[CATCACHE_MAXKEYS]
Definition: catcache.h:99
bool dead
Definition: catcache.h:121
uint32 hash_value
Definition: catcache.h:93
dlist_node * cur
Definition: ilist.h:179
dlist_node * cur
Definition: ilist.h:200
Definition: c.h:728
slist_node * cur
Definition: ilist.h:259
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:173
struct TupleDescData * TupleDesc
Definition: tupdesc.h:89
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
Datum texteq(PG_FUNCTION_ARGS)
Definition: varlena.c:1619
bool IsTransactionState(void)
Definition: xact.c:384