Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * syscache.c
4 : * System cache management routines
5 : *
6 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/cache/syscache.c
12 : *
13 : * NOTES
14 : * These routines allow the parser/planner/executor to perform
15 : * rapid lookups on the contents of the system catalogs.
16 : *
17 : * see utils/syscache.h for a list of the cache IDs
18 : *
19 : *-------------------------------------------------------------------------
20 : */
21 : #include "postgres.h"
22 :
23 : #include "access/htup_details.h"
24 : #include "catalog/pg_db_role_setting_d.h"
25 : #include "catalog/pg_depend_d.h"
26 : #include "catalog/pg_description_d.h"
27 : #include "catalog/pg_seclabel_d.h"
28 : #include "catalog/pg_shdepend_d.h"
29 : #include "catalog/pg_shdescription_d.h"
30 : #include "catalog/pg_shseclabel_d.h"
31 : #include "common/int.h"
32 : #include "lib/qunique.h"
33 : #include "miscadmin.h"
34 : #include "storage/lmgr.h"
35 : #include "utils/catcache.h"
36 : #include "utils/inval.h"
37 : #include "utils/lsyscache.h"
38 : #include "utils/rel.h"
39 : #include "utils/syscache.h"
40 :
41 : /*---------------------------------------------------------------------------
42 :
43 : Adding system caches:
44 :
45 : There must be a unique index underlying each syscache (ie, an index
46 : whose key is the same as that of the cache). If there is not one
47 : already, add the definition for it to include/catalog/pg_*.h using
48 : DECLARE_UNIQUE_INDEX.
49 : (Adding an index requires a catversion.h update, while simply
50 : adding/deleting caches only requires a recompile.)
51 :
52 : Add a MAKE_SYSCACHE call to the same pg_*.h file specifying the name of
53 : your cache, the underlying index, and the initial number of hash buckets.
54 :
55 : The number of hash buckets must be a power of 2. It's reasonable to
56 : set this to the number of entries that might be in the particular cache
57 : in a medium-size database.
58 :
59 : Finally, any place your relation gets heap_insert() or
60 : heap_update() calls, use CatalogTupleInsert() or CatalogTupleUpdate()
61 : instead, which also update indexes. The heap_* calls do not do that.
62 :
63 : *---------------------------------------------------------------------------
64 : */
65 :
66 : /*
67 : * struct cachedesc: information defining a single syscache
68 : */
69 : struct cachedesc
70 : {
71 : Oid reloid; /* OID of the relation being cached */
72 : Oid indoid; /* OID of index relation for this cache */
73 : int nkeys; /* # of keys needed for cache lookup */
74 : int key[4]; /* attribute numbers of key attrs */
75 : int nbuckets; /* number of hash buckets for this cache */
76 : };
77 :
78 : /* Macro to provide nkeys and key array with convenient syntax. */
79 : #define KEY(...) VA_ARGS_NARGS(__VA_ARGS__), { __VA_ARGS__ }
80 :
81 : #include "catalog/syscache_info.h"
82 :
83 : StaticAssertDecl(lengthof(cacheinfo) == SysCacheSize,
84 : "SysCacheSize does not match syscache.c's array");
85 :
86 : static CatCache *SysCache[SysCacheSize];
87 :
88 : static bool CacheInitialized = false;
89 :
90 : /* Sorted array of OIDs of tables that have caches on them */
91 : static Oid SysCacheRelationOid[SysCacheSize];
92 : static int SysCacheRelationOidSize;
93 :
94 : /* Sorted array of OIDs of tables and indexes used by caches */
95 : static Oid SysCacheSupportingRelOid[SysCacheSize * 2];
96 : static int SysCacheSupportingRelOidSize;
97 :
98 : static int oid_compare(const void *a, const void *b);
99 :
100 :
101 : /*
102 : * InitCatalogCache - initialize the caches
103 : *
104 : * Note that no database access is done here; we only allocate memory
105 : * and initialize the cache structure. Interrogation of the database
106 : * to complete initialization of a cache happens upon first use
107 : * of that cache.
108 : */
109 : void
110 36058 : InitCatalogCache(void)
111 : {
112 : int cacheId;
113 :
114 : Assert(!CacheInitialized);
115 :
116 36058 : SysCacheRelationOidSize = SysCacheSupportingRelOidSize = 0;
117 :
118 3100988 : for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
119 : {
120 : /*
121 : * Assert that every enumeration value defined in syscache.h has been
122 : * populated in the cacheinfo array.
123 : */
124 : Assert(OidIsValid(cacheinfo[cacheId].reloid));
125 : Assert(OidIsValid(cacheinfo[cacheId].indoid));
126 : /* .nbuckets and .key[] are checked by InitCatCache() */
127 :
128 6129860 : SysCache[cacheId] = InitCatCache(cacheId,
129 3064930 : cacheinfo[cacheId].reloid,
130 3064930 : cacheinfo[cacheId].indoid,
131 3064930 : cacheinfo[cacheId].nkeys,
132 3064930 : cacheinfo[cacheId].key,
133 3064930 : cacheinfo[cacheId].nbuckets);
134 3064930 : if (!SysCache[cacheId])
135 0 : elog(ERROR, "could not initialize cache %u (%d)",
136 : cacheinfo[cacheId].reloid, cacheId);
137 : /* Accumulate data for OID lists, too */
138 3064930 : SysCacheRelationOid[SysCacheRelationOidSize++] =
139 3064930 : cacheinfo[cacheId].reloid;
140 3064930 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
141 3064930 : cacheinfo[cacheId].reloid;
142 3064930 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
143 3064930 : cacheinfo[cacheId].indoid;
144 : /* see comments for RelationInvalidatesSnapshotsOnly */
145 : Assert(!RelationInvalidatesSnapshotsOnly(cacheinfo[cacheId].reloid));
146 : }
147 :
148 : Assert(SysCacheRelationOidSize <= lengthof(SysCacheRelationOid));
149 : Assert(SysCacheSupportingRelOidSize <= lengthof(SysCacheSupportingRelOid));
150 :
151 : /* Sort and de-dup OID arrays, so we can use binary search. */
152 36058 : qsort(SysCacheRelationOid, SysCacheRelationOidSize,
153 : sizeof(Oid), oid_compare);
154 36058 : SysCacheRelationOidSize =
155 36058 : qunique(SysCacheRelationOid, SysCacheRelationOidSize, sizeof(Oid),
156 : oid_compare);
157 :
158 36058 : qsort(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
159 : sizeof(Oid), oid_compare);
160 36058 : SysCacheSupportingRelOidSize =
161 36058 : qunique(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
162 : sizeof(Oid), oid_compare);
163 :
164 36058 : CacheInitialized = true;
165 36058 : }
166 :
167 : /*
168 : * InitCatalogCachePhase2 - finish initializing the caches
169 : *
170 : * Finish initializing all the caches, including necessary database
171 : * access.
172 : *
173 : * This is *not* essential; normally we allow syscaches to be initialized
174 : * on first use. However, it is useful as a mechanism to preload the
175 : * relcache with entries for the most-commonly-used system catalogs.
176 : * Therefore, we invoke this routine when we need to write a new relcache
177 : * init file.
178 : */
179 : void
180 3706 : InitCatalogCachePhase2(void)
181 : {
182 : int cacheId;
183 :
184 : Assert(CacheInitialized);
185 :
186 318716 : for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
187 315010 : InitCatCachePhase2(SysCache[cacheId], true);
188 3706 : }
189 :
190 :
191 : /*
192 : * SearchSysCache
193 : *
194 : * A layer on top of SearchCatCache that does the initialization and
195 : * key-setting for you.
196 : *
197 : * Returns the cache copy of the tuple if one is found, NULL if not.
198 : * The tuple is the 'cache' copy and must NOT be modified!
199 : *
200 : * When the caller is done using the tuple, call ReleaseSysCache()
201 : * to release the reference count grabbed by SearchSysCache(). If this
202 : * is not done, the tuple will remain locked in cache until end of
203 : * transaction, which is tolerable but not desirable.
204 : *
205 : * CAUTION: The tuple that is returned must NOT be freed by the caller!
206 : */
207 : HeapTuple
208 5805956 : SearchSysCache(int cacheId,
209 : Datum key1,
210 : Datum key2,
211 : Datum key3,
212 : Datum key4)
213 : {
214 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
215 :
216 5805956 : return SearchCatCache(SysCache[cacheId], key1, key2, key3, key4);
217 : }
218 :
219 : HeapTuple
220 73242718 : SearchSysCache1(int cacheId,
221 : Datum key1)
222 : {
223 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
224 : Assert(SysCache[cacheId]->cc_nkeys == 1);
225 :
226 73242718 : return SearchCatCache1(SysCache[cacheId], key1);
227 : }
228 :
229 : HeapTuple
230 6110412 : SearchSysCache2(int cacheId,
231 : Datum key1, Datum key2)
232 : {
233 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
234 : Assert(SysCache[cacheId]->cc_nkeys == 2);
235 :
236 6110412 : return SearchCatCache2(SysCache[cacheId], key1, key2);
237 : }
238 :
239 : HeapTuple
240 5887318 : SearchSysCache3(int cacheId,
241 : Datum key1, Datum key2, Datum key3)
242 : {
243 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
244 : Assert(SysCache[cacheId]->cc_nkeys == 3);
245 :
246 5887318 : return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
247 : }
248 :
249 : HeapTuple
250 4559710 : SearchSysCache4(int cacheId,
251 : Datum key1, Datum key2, Datum key3, Datum key4)
252 : {
253 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
254 : Assert(SysCache[cacheId]->cc_nkeys == 4);
255 :
256 4559710 : return SearchCatCache4(SysCache[cacheId], key1, key2, key3, key4);
257 : }
258 :
259 : /*
260 : * ReleaseSysCache
261 : * Release previously grabbed reference count on a tuple
262 : */
263 : void
264 89500708 : ReleaseSysCache(HeapTuple tuple)
265 : {
266 89500708 : ReleaseCatCache(tuple);
267 89500708 : }
268 :
269 : /*
270 : * SearchSysCacheLocked1
271 : *
272 : * Combine SearchSysCache1() with acquiring a LOCKTAG_TUPLE at mode
273 : * InplaceUpdateTupleLock. This is a tool for complying with the
274 : * README.tuplock section "Locking to write inplace-updated tables". After
275 : * the caller's heap_update(), it should UnlockTuple(InplaceUpdateTupleLock)
276 : * and ReleaseSysCache().
277 : *
278 : * The returned tuple may be the subject of an uncommitted update, so this
279 : * doesn't prevent the "tuple concurrently updated" error.
280 : */
281 : HeapTuple
282 45926 : SearchSysCacheLocked1(int cacheId,
283 : Datum key1)
284 : {
285 45926 : CatCache *cache = SysCache[cacheId];
286 : ItemPointerData tid;
287 : LOCKTAG tag;
288 :
289 : /*----------
290 : * Since inplace updates may happen just before our LockTuple(), we must
291 : * return content acquired after LockTuple() of the TID we return. If we
292 : * just fetched twice instead of looping, the following sequence would
293 : * defeat our locking:
294 : *
295 : * GRANT: SearchSysCache1() = TID (1,5)
296 : * GRANT: LockTuple(pg_class, (1,5))
297 : * [no more inplace update of (1,5) until we release the lock]
298 : * CLUSTER: SearchSysCache1() = TID (1,5)
299 : * CLUSTER: heap_update() = TID (1,8)
300 : * CLUSTER: COMMIT
301 : * GRANT: SearchSysCache1() = TID (1,8)
302 : * GRANT: return (1,8) from SearchSysCacheLocked1()
303 : * VACUUM: SearchSysCache1() = TID (1,8)
304 : * VACUUM: LockTuple(pg_class, (1,8)) # two TIDs now locked for one rel
305 : * VACUUM: inplace update
306 : * GRANT: heap_update() = (1,9) # lose inplace update
307 : *
308 : * In the happy case, this takes two fetches, one to determine the TID to
309 : * lock and another to get the content and confirm the TID didn't change.
310 : *
311 : * This is valid even if the row gets updated to a new TID, the old TID
312 : * becomes LP_UNUSED, and the row gets updated back to its old TID. We'd
313 : * still hold the right LOCKTAG_TUPLE and a copy of the row captured after
314 : * the LOCKTAG_TUPLE.
315 : */
316 45926 : ItemPointerSetInvalid(&tid);
317 : for (;;)
318 45928 : {
319 : HeapTuple tuple;
320 91854 : LOCKMODE lockmode = InplaceUpdateTupleLock;
321 :
322 91854 : tuple = SearchSysCache1(cacheId, key1);
323 91854 : if (ItemPointerIsValid(&tid))
324 : {
325 45928 : if (!HeapTupleIsValid(tuple))
326 : {
327 2 : LockRelease(&tag, lockmode, false);
328 2 : return tuple;
329 : }
330 45926 : if (ItemPointerEquals(&tid, &tuple->t_self))
331 45924 : return tuple;
332 2 : LockRelease(&tag, lockmode, false);
333 : }
334 45926 : else if (!HeapTupleIsValid(tuple))
335 0 : return tuple;
336 :
337 45928 : tid = tuple->t_self;
338 45928 : ReleaseSysCache(tuple);
339 :
340 : /*
341 : * Do like LockTuple(rel, &tid, lockmode). While cc_relisshared won't
342 : * change from one iteration to another, it may have been a temporary
343 : * "false" until our first SearchSysCache1().
344 : */
345 45928 : SET_LOCKTAG_TUPLE(tag,
346 : cache->cc_relisshared ? InvalidOid : MyDatabaseId,
347 : cache->cc_reloid,
348 : ItemPointerGetBlockNumber(&tid),
349 : ItemPointerGetOffsetNumber(&tid));
350 45928 : (void) LockAcquire(&tag, lockmode, false, false);
351 :
352 : /*
353 : * If an inplace update just finished, ensure we process the syscache
354 : * inval.
355 : *
356 : * If a heap_update() call just released its LOCKTAG_TUPLE, we'll
357 : * probably find the old tuple and reach "tuple concurrently updated".
358 : * If that heap_update() aborts, our LOCKTAG_TUPLE blocks inplace
359 : * updates while our caller works.
360 : */
361 45928 : AcceptInvalidationMessages();
362 : }
363 : }
364 :
365 : /*
366 : * SearchSysCacheCopy
367 : *
368 : * A convenience routine that does SearchSysCache and (if successful)
369 : * returns a modifiable copy of the syscache entry. The original
370 : * syscache entry is released before returning. The caller should
371 : * heap_freetuple() the result when done with it.
372 : */
373 : HeapTuple
374 631398 : SearchSysCacheCopy(int cacheId,
375 : Datum key1,
376 : Datum key2,
377 : Datum key3,
378 : Datum key4)
379 : {
380 : HeapTuple tuple,
381 : newtuple;
382 :
383 631398 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
384 631398 : if (!HeapTupleIsValid(tuple))
385 143112 : return tuple;
386 488286 : newtuple = heap_copytuple(tuple);
387 488286 : ReleaseSysCache(tuple);
388 488286 : return newtuple;
389 : }
390 :
391 : /*
392 : * SearchSysCacheLockedCopy1
393 : *
394 : * Meld SearchSysCacheLocked1 with SearchSysCacheCopy(). After the
395 : * caller's heap_update(), it should UnlockTuple(InplaceUpdateTupleLock) and
396 : * heap_freetuple().
397 : */
398 : HeapTuple
399 16850 : SearchSysCacheLockedCopy1(int cacheId,
400 : Datum key1)
401 : {
402 : HeapTuple tuple,
403 : newtuple;
404 :
405 16850 : tuple = SearchSysCacheLocked1(cacheId, key1);
406 16850 : if (!HeapTupleIsValid(tuple))
407 0 : return tuple;
408 16850 : newtuple = heap_copytuple(tuple);
409 16850 : ReleaseSysCache(tuple);
410 16850 : return newtuple;
411 : }
412 :
413 : /*
414 : * SearchSysCacheExists
415 : *
416 : * A convenience routine that just probes to see if a tuple can be found.
417 : * No lock is retained on the syscache entry.
418 : */
419 : bool
420 1498420 : SearchSysCacheExists(int cacheId,
421 : Datum key1,
422 : Datum key2,
423 : Datum key3,
424 : Datum key4)
425 : {
426 : HeapTuple tuple;
427 :
428 1498420 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
429 1498420 : if (!HeapTupleIsValid(tuple))
430 234024 : return false;
431 1264396 : ReleaseSysCache(tuple);
432 1264396 : return true;
433 : }
434 :
435 : /*
436 : * GetSysCacheOid
437 : *
438 : * A convenience routine that does SearchSysCache and returns the OID in the
439 : * oidcol column of the found tuple, or InvalidOid if no tuple could be found.
440 : * No lock is retained on the syscache entry.
441 : */
442 : Oid
443 3676138 : GetSysCacheOid(int cacheId,
444 : AttrNumber oidcol,
445 : Datum key1,
446 : Datum key2,
447 : Datum key3,
448 : Datum key4)
449 : {
450 : HeapTuple tuple;
451 : bool isNull;
452 : Oid result;
453 :
454 3676138 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
455 3676138 : if (!HeapTupleIsValid(tuple))
456 1394610 : return InvalidOid;
457 2281528 : result = DatumGetObjectId(heap_getattr(tuple, oidcol,
458 2281528 : SysCache[cacheId]->cc_tupdesc,
459 : &isNull));
460 : Assert(!isNull); /* columns used as oids should never be NULL */
461 2281528 : ReleaseSysCache(tuple);
462 2281528 : return result;
463 : }
464 :
465 :
466 : /*
467 : * SearchSysCacheAttName
468 : *
469 : * This routine is equivalent to SearchSysCache on the ATTNAME cache,
470 : * except that it will return NULL if the found attribute is marked
471 : * attisdropped. This is convenient for callers that want to act as
472 : * though dropped attributes don't exist.
473 : */
474 : HeapTuple
475 111788 : SearchSysCacheAttName(Oid relid, const char *attname)
476 : {
477 : HeapTuple tuple;
478 :
479 111788 : tuple = SearchSysCache2(ATTNAME,
480 : ObjectIdGetDatum(relid),
481 : CStringGetDatum(attname));
482 111788 : if (!HeapTupleIsValid(tuple))
483 1032 : return NULL;
484 110756 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
485 : {
486 84 : ReleaseSysCache(tuple);
487 84 : return NULL;
488 : }
489 110672 : return tuple;
490 : }
491 :
492 : /*
493 : * SearchSysCacheCopyAttName
494 : *
495 : * As above, an attisdropped-aware version of SearchSysCacheCopy.
496 : */
497 : HeapTuple
498 9526 : SearchSysCacheCopyAttName(Oid relid, const char *attname)
499 : {
500 : HeapTuple tuple,
501 : newtuple;
502 :
503 9526 : tuple = SearchSysCacheAttName(relid, attname);
504 9526 : if (!HeapTupleIsValid(tuple))
505 744 : return tuple;
506 8782 : newtuple = heap_copytuple(tuple);
507 8782 : ReleaseSysCache(tuple);
508 8782 : return newtuple;
509 : }
510 :
511 : /*
512 : * SearchSysCacheExistsAttName
513 : *
514 : * As above, an attisdropped-aware version of SearchSysCacheExists.
515 : */
516 : bool
517 1102 : SearchSysCacheExistsAttName(Oid relid, const char *attname)
518 : {
519 : HeapTuple tuple;
520 :
521 1102 : tuple = SearchSysCacheAttName(relid, attname);
522 1102 : if (!HeapTupleIsValid(tuple))
523 54 : return false;
524 1048 : ReleaseSysCache(tuple);
525 1048 : return true;
526 : }
527 :
528 :
529 : /*
530 : * SearchSysCacheAttNum
531 : *
532 : * This routine is equivalent to SearchSysCache on the ATTNUM cache,
533 : * except that it will return NULL if the found attribute is marked
534 : * attisdropped. This is convenient for callers that want to act as
535 : * though dropped attributes don't exist.
536 : */
537 : HeapTuple
538 1882 : SearchSysCacheAttNum(Oid relid, int16 attnum)
539 : {
540 : HeapTuple tuple;
541 :
542 1882 : tuple = SearchSysCache2(ATTNUM,
543 : ObjectIdGetDatum(relid),
544 : Int16GetDatum(attnum));
545 1882 : if (!HeapTupleIsValid(tuple))
546 12 : return NULL;
547 1870 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
548 : {
549 0 : ReleaseSysCache(tuple);
550 0 : return NULL;
551 : }
552 1870 : return tuple;
553 : }
554 :
555 : /*
556 : * SearchSysCacheCopyAttNum
557 : *
558 : * As above, an attisdropped-aware version of SearchSysCacheCopy.
559 : */
560 : HeapTuple
561 1818 : SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
562 : {
563 : HeapTuple tuple,
564 : newtuple;
565 :
566 1818 : tuple = SearchSysCacheAttNum(relid, attnum);
567 1818 : if (!HeapTupleIsValid(tuple))
568 0 : return NULL;
569 1818 : newtuple = heap_copytuple(tuple);
570 1818 : ReleaseSysCache(tuple);
571 1818 : return newtuple;
572 : }
573 :
574 :
575 : /*
576 : * SysCacheGetAttr
577 : *
578 : * Given a tuple previously fetched by SearchSysCache(),
579 : * extract a specific attribute.
580 : *
581 : * This is equivalent to using heap_getattr() on a tuple fetched
582 : * from a non-cached relation. Usually, this is only used for attributes
583 : * that could be NULL or variable length; the fixed-size attributes in
584 : * a system table are accessed just by mapping the tuple onto the C struct
585 : * declarations from include/catalog/.
586 : *
587 : * As with heap_getattr(), if the attribute is of a pass-by-reference type
588 : * then a pointer into the tuple data area is returned --- the caller must
589 : * not modify or pfree the datum!
590 : *
591 : * Note: it is legal to use SysCacheGetAttr() with a cacheId referencing
592 : * a different cache for the same catalog the tuple was fetched from.
593 : */
594 : Datum
595 6041892 : SysCacheGetAttr(int cacheId, HeapTuple tup,
596 : AttrNumber attributeNumber,
597 : bool *isNull)
598 : {
599 : /*
600 : * We just need to get the TupleDesc out of the cache entry, and then we
601 : * can apply heap_getattr(). Normally the cache control data is already
602 : * valid (because the caller recently fetched the tuple via this same
603 : * cache), but there are cases where we have to initialize the cache here.
604 : */
605 6041892 : if (cacheId < 0 || cacheId >= SysCacheSize || !SysCache[cacheId])
606 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
607 6041892 : if (!SysCache[cacheId]->cc_tupdesc)
608 : {
609 34458 : InitCatCachePhase2(SysCache[cacheId], false);
610 : Assert(SysCache[cacheId]->cc_tupdesc);
611 : }
612 :
613 12083784 : return heap_getattr(tup, attributeNumber,
614 6041892 : SysCache[cacheId]->cc_tupdesc,
615 : isNull);
616 : }
617 :
618 : /*
619 : * SysCacheGetAttrNotNull
620 : *
621 : * As above, a version of SysCacheGetAttr which knows that the attr cannot
622 : * be NULL.
623 : */
624 : Datum
625 3493022 : SysCacheGetAttrNotNull(int cacheId, HeapTuple tup,
626 : AttrNumber attributeNumber)
627 : {
628 : bool isnull;
629 : Datum attr;
630 :
631 3493022 : attr = SysCacheGetAttr(cacheId, tup, attributeNumber, &isnull);
632 :
633 3493022 : if (isnull)
634 : {
635 0 : elog(ERROR,
636 : "unexpected null value in cached tuple for catalog %s column %s",
637 : get_rel_name(cacheinfo[cacheId].reloid),
638 : NameStr(TupleDescAttr(SysCache[cacheId]->cc_tupdesc, attributeNumber - 1)->attname));
639 : }
640 :
641 3493022 : return attr;
642 : }
643 :
644 : /*
645 : * GetSysCacheHashValue
646 : *
647 : * Get the hash value that would be used for a tuple in the specified cache
648 : * with the given search keys.
649 : *
650 : * The reason for exposing this as part of the API is that the hash value is
651 : * exposed in cache invalidation operations, so there are places outside the
652 : * catcache code that need to be able to compute the hash values.
653 : */
654 : uint32
655 1109990 : GetSysCacheHashValue(int cacheId,
656 : Datum key1,
657 : Datum key2,
658 : Datum key3,
659 : Datum key4)
660 : {
661 1109990 : if (cacheId < 0 || cacheId >= SysCacheSize || !SysCache[cacheId])
662 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
663 :
664 1109990 : return GetCatCacheHashValue(SysCache[cacheId], key1, key2, key3, key4);
665 : }
666 :
667 : /*
668 : * List-search interface
669 : */
670 : struct catclist *
671 3945014 : SearchSysCacheList(int cacheId, int nkeys,
672 : Datum key1, Datum key2, Datum key3)
673 : {
674 3945014 : if (cacheId < 0 || cacheId >= SysCacheSize || !SysCache[cacheId])
675 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
676 :
677 3945014 : return SearchCatCacheList(SysCache[cacheId], nkeys,
678 : key1, key2, key3);
679 : }
680 :
681 : /*
682 : * SysCacheInvalidate
683 : *
684 : * Invalidate entries in the specified cache, given a hash value.
685 : * See CatCacheInvalidate() for more info.
686 : *
687 : * This routine is only quasi-public: it should only be used by inval.c.
688 : */
689 : void
690 22438732 : SysCacheInvalidate(int cacheId, uint32 hashValue)
691 : {
692 22438732 : if (cacheId < 0 || cacheId >= SysCacheSize)
693 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
694 :
695 : /* if this cache isn't initialized yet, no need to do anything */
696 22438732 : if (!SysCache[cacheId])
697 0 : return;
698 :
699 22438732 : CatCacheInvalidate(SysCache[cacheId], hashValue);
700 : }
701 :
702 : /*
703 : * Certain relations that do not have system caches send snapshot invalidation
704 : * messages in lieu of catcache messages. This is for the benefit of
705 : * GetCatalogSnapshot(), which can then reuse its existing MVCC snapshot
706 : * for scanning one of those catalogs, rather than taking a new one, if no
707 : * invalidation has been received.
708 : *
709 : * Relations that have syscaches need not (and must not) be listed here. The
710 : * catcache invalidation messages will also flush the snapshot. If you add a
711 : * syscache for one of these relations, remove it from this list.
712 : */
713 : bool
714 17238356 : RelationInvalidatesSnapshotsOnly(Oid relid)
715 : {
716 17238356 : switch (relid)
717 : {
718 2657904 : case DbRoleSettingRelationId:
719 : case DependRelationId:
720 : case SharedDependRelationId:
721 : case DescriptionRelationId:
722 : case SharedDescriptionRelationId:
723 : case SecLabelRelationId:
724 : case SharedSecLabelRelationId:
725 2657904 : return true;
726 14580452 : default:
727 14580452 : break;
728 : }
729 :
730 14580452 : return false;
731 : }
732 :
733 : /*
734 : * Test whether a relation has a system cache.
735 : */
736 : bool
737 11299130 : RelationHasSysCache(Oid relid)
738 : {
739 11299130 : int low = 0,
740 11299130 : high = SysCacheRelationOidSize - 1;
741 :
742 49927180 : while (low <= high)
743 : {
744 49434782 : int middle = low + (high - low) / 2;
745 :
746 49434782 : if (SysCacheRelationOid[middle] == relid)
747 10806732 : return true;
748 38628050 : if (SysCacheRelationOid[middle] < relid)
749 13422744 : low = middle + 1;
750 : else
751 25205306 : high = middle - 1;
752 : }
753 :
754 492398 : return false;
755 : }
756 :
757 : /*
758 : * Test whether a relation supports a system cache, ie it is either a
759 : * cached table or the index used for a cache.
760 : */
761 : bool
762 2659590 : RelationSupportsSysCache(Oid relid)
763 : {
764 2659590 : int low = 0,
765 2659590 : high = SysCacheSupportingRelOidSize - 1;
766 :
767 21923794 : while (low <= high)
768 : {
769 19906190 : int middle = low + (high - low) / 2;
770 :
771 19906190 : if (SysCacheSupportingRelOid[middle] == relid)
772 641986 : return true;
773 19264204 : if (SysCacheSupportingRelOid[middle] < relid)
774 17047952 : low = middle + 1;
775 : else
776 2216252 : high = middle - 1;
777 : }
778 :
779 2017604 : return false;
780 : }
781 :
782 :
783 : /*
784 : * OID comparator for qsort
785 : */
786 : static int
787 71286666 : oid_compare(const void *a, const void *b)
788 : {
789 71286666 : Oid oa = *((const Oid *) a);
790 71286666 : Oid ob = *((const Oid *) b);
791 :
792 71286666 : return pg_cmp_u32(oa, ob);
793 : }
|