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