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