Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * syscache.c
4 : * System cache management routines
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/utils/cache/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 28952 : InitCatalogCache(void)
111 : {
112 : int cacheId;
113 :
114 : Assert(!CacheInitialized);
115 :
116 28952 : SysCacheRelationOidSize = SysCacheSupportingRelOidSize = 0;
117 :
118 2489872 : 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 4921840 : SysCache[cacheId] = InitCatCache(cacheId,
129 : cacheinfo[cacheId].reloid,
130 : cacheinfo[cacheId].indoid,
131 : cacheinfo[cacheId].nkeys,
132 2460920 : cacheinfo[cacheId].key,
133 : cacheinfo[cacheId].nbuckets);
134 2460920 : if (!PointerIsValid(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 2460920 : SysCacheRelationOid[SysCacheRelationOidSize++] =
139 2460920 : cacheinfo[cacheId].reloid;
140 2460920 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
141 2460920 : cacheinfo[cacheId].reloid;
142 2460920 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
143 2460920 : 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 28952 : qsort(SysCacheRelationOid, SysCacheRelationOidSize,
153 : sizeof(Oid), oid_compare);
154 28952 : SysCacheRelationOidSize =
155 28952 : qunique(SysCacheRelationOid, SysCacheRelationOidSize, sizeof(Oid),
156 : oid_compare);
157 :
158 28952 : qsort(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
159 : sizeof(Oid), oid_compare);
160 28952 : SysCacheSupportingRelOidSize =
161 28952 : qunique(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
162 : sizeof(Oid), oid_compare);
163 :
164 28952 : CacheInitialized = true;
165 28952 : }
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 2814 : InitCatalogCachePhase2(void)
181 : {
182 : int cacheId;
183 :
184 : Assert(CacheInitialized);
185 :
186 241928 : for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
187 239118 : InitCatCachePhase2(SysCache[cacheId], true);
188 2810 : }
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 5044964 : SearchSysCache(int cacheId,
209 : Datum key1,
210 : Datum key2,
211 : Datum key3,
212 : Datum key4)
213 : {
214 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
215 : PointerIsValid(SysCache[cacheId]));
216 :
217 5044964 : return SearchCatCache(SysCache[cacheId], key1, key2, key3, key4);
218 : }
219 :
220 : HeapTuple
221 62278932 : SearchSysCache1(int cacheId,
222 : Datum key1)
223 : {
224 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
225 : PointerIsValid(SysCache[cacheId]));
226 : Assert(SysCache[cacheId]->cc_nkeys == 1);
227 :
228 62278932 : return SearchCatCache1(SysCache[cacheId], key1);
229 : }
230 :
231 : HeapTuple
232 5021062 : SearchSysCache2(int cacheId,
233 : Datum key1, Datum key2)
234 : {
235 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
236 : PointerIsValid(SysCache[cacheId]));
237 : Assert(SysCache[cacheId]->cc_nkeys == 2);
238 :
239 5021062 : return SearchCatCache2(SysCache[cacheId], key1, key2);
240 : }
241 :
242 : HeapTuple
243 4731466 : SearchSysCache3(int cacheId,
244 : Datum key1, Datum key2, Datum key3)
245 : {
246 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
247 : PointerIsValid(SysCache[cacheId]));
248 : Assert(SysCache[cacheId]->cc_nkeys == 3);
249 :
250 4731466 : return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
251 : }
252 :
253 : HeapTuple
254 3905676 : SearchSysCache4(int cacheId,
255 : Datum key1, Datum key2, Datum key3, Datum key4)
256 : {
257 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
258 : PointerIsValid(SysCache[cacheId]));
259 : Assert(SysCache[cacheId]->cc_nkeys == 4);
260 :
261 3905676 : return SearchCatCache4(SysCache[cacheId], key1, key2, key3, key4);
262 : }
263 :
264 : /*
265 : * ReleaseSysCache
266 : * Release previously grabbed reference count on a tuple
267 : */
268 : void
269 75676540 : ReleaseSysCache(HeapTuple tuple)
270 : {
271 75676540 : ReleaseCatCache(tuple);
272 75676540 : }
273 :
274 : /*
275 : * SearchSysCacheLocked1
276 : *
277 : * Combine SearchSysCache1() with acquiring a LOCKTAG_TUPLE at mode
278 : * InplaceUpdateTupleLock. This is a tool for complying with the
279 : * README.tuplock section "Locking to write inplace-updated tables". After
280 : * the caller's heap_update(), it should UnlockTuple(InplaceUpdateTupleLock)
281 : * and ReleaseSysCache().
282 : *
283 : * The returned tuple may be the subject of an uncommitted update, so this
284 : * doesn't prevent the "tuple concurrently updated" error.
285 : */
286 : HeapTuple
287 43302 : SearchSysCacheLocked1(int cacheId,
288 : Datum key1)
289 : {
290 43302 : CatCache *cache = SysCache[cacheId];
291 : ItemPointerData tid;
292 : LOCKTAG tag;
293 :
294 : /*----------
295 : * Since inplace updates may happen just before our LockTuple(), we must
296 : * return content acquired after LockTuple() of the TID we return. If we
297 : * just fetched twice instead of looping, the following sequence would
298 : * defeat our locking:
299 : *
300 : * GRANT: SearchSysCache1() = TID (1,5)
301 : * GRANT: LockTuple(pg_class, (1,5))
302 : * [no more inplace update of (1,5) until we release the lock]
303 : * CLUSTER: SearchSysCache1() = TID (1,5)
304 : * CLUSTER: heap_update() = TID (1,8)
305 : * CLUSTER: COMMIT
306 : * GRANT: SearchSysCache1() = TID (1,8)
307 : * GRANT: return (1,8) from SearchSysCacheLocked1()
308 : * VACUUM: SearchSysCache1() = TID (1,8)
309 : * VACUUM: LockTuple(pg_class, (1,8)) # two TIDs now locked for one rel
310 : * VACUUM: inplace update
311 : * GRANT: heap_update() = (1,9) # lose inplace update
312 : *
313 : * In the happy case, this takes two fetches, one to determine the TID to
314 : * lock and another to get the content and confirm the TID didn't change.
315 : *
316 : * This is valid even if the row gets updated to a new TID, the old TID
317 : * becomes LP_UNUSED, and the row gets updated back to its old TID. We'd
318 : * still hold the right LOCKTAG_TUPLE and a copy of the row captured after
319 : * the LOCKTAG_TUPLE.
320 : */
321 43302 : ItemPointerSetInvalid(&tid);
322 : for (;;)
323 43304 : {
324 : HeapTuple tuple;
325 86606 : LOCKMODE lockmode = InplaceUpdateTupleLock;
326 :
327 86606 : tuple = SearchSysCache1(cacheId, key1);
328 86606 : if (ItemPointerIsValid(&tid))
329 : {
330 43304 : if (!HeapTupleIsValid(tuple))
331 : {
332 0 : LockRelease(&tag, lockmode, false);
333 0 : return tuple;
334 : }
335 43304 : if (ItemPointerEquals(&tid, &tuple->t_self))
336 43302 : return tuple;
337 2 : LockRelease(&tag, lockmode, false);
338 : }
339 43302 : else if (!HeapTupleIsValid(tuple))
340 0 : return tuple;
341 :
342 43304 : tid = tuple->t_self;
343 43304 : ReleaseSysCache(tuple);
344 :
345 : /*
346 : * Do like LockTuple(rel, &tid, lockmode). While cc_relisshared won't
347 : * change from one iteration to another, it may have been a temporary
348 : * "false" until our first SearchSysCache1().
349 : */
350 43304 : SET_LOCKTAG_TUPLE(tag,
351 : cache->cc_relisshared ? InvalidOid : MyDatabaseId,
352 : cache->cc_reloid,
353 : ItemPointerGetBlockNumber(&tid),
354 : ItemPointerGetOffsetNumber(&tid));
355 43304 : (void) LockAcquire(&tag, lockmode, false, false);
356 :
357 : /*
358 : * If an inplace update just finished, ensure we process the syscache
359 : * inval.
360 : *
361 : * If a heap_update() call just released its LOCKTAG_TUPLE, we'll
362 : * probably find the old tuple and reach "tuple concurrently updated".
363 : * If that heap_update() aborts, our LOCKTAG_TUPLE blocks inplace
364 : * updates while our caller works.
365 : */
366 43304 : AcceptInvalidationMessages();
367 : }
368 : }
369 :
370 : /*
371 : * SearchSysCacheCopy
372 : *
373 : * A convenience routine that does SearchSysCache and (if successful)
374 : * returns a modifiable copy of the syscache entry. The original
375 : * syscache entry is released before returning. The caller should
376 : * heap_freetuple() the result when done with it.
377 : */
378 : HeapTuple
379 448072 : SearchSysCacheCopy(int cacheId,
380 : Datum key1,
381 : Datum key2,
382 : Datum key3,
383 : Datum key4)
384 : {
385 : HeapTuple tuple,
386 : newtuple;
387 :
388 448072 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
389 448072 : if (!HeapTupleIsValid(tuple))
390 129904 : return tuple;
391 318168 : newtuple = heap_copytuple(tuple);
392 318168 : ReleaseSysCache(tuple);
393 318168 : return newtuple;
394 : }
395 :
396 : /*
397 : * SearchSysCacheLockedCopy1
398 : *
399 : * Meld SearchSysCacheLockedCopy1 with SearchSysCacheCopy(). After the
400 : * caller's heap_update(), it should UnlockTuple(InplaceUpdateTupleLock) and
401 : * heap_freetuple().
402 : */
403 : HeapTuple
404 15842 : SearchSysCacheLockedCopy1(int cacheId,
405 : Datum key1)
406 : {
407 : HeapTuple tuple,
408 : newtuple;
409 :
410 15842 : tuple = SearchSysCacheLocked1(cacheId, key1);
411 15842 : if (!HeapTupleIsValid(tuple))
412 0 : return tuple;
413 15842 : newtuple = heap_copytuple(tuple);
414 15842 : ReleaseSysCache(tuple);
415 15842 : return newtuple;
416 : }
417 :
418 : /*
419 : * SearchSysCacheExists
420 : *
421 : * A convenience routine that just probes to see if a tuple can be found.
422 : * No lock is retained on the syscache entry.
423 : */
424 : bool
425 1227320 : SearchSysCacheExists(int cacheId,
426 : Datum key1,
427 : Datum key2,
428 : Datum key3,
429 : Datum key4)
430 : {
431 : HeapTuple tuple;
432 :
433 1227320 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
434 1227320 : if (!HeapTupleIsValid(tuple))
435 207914 : return false;
436 1019406 : ReleaseSysCache(tuple);
437 1019406 : return true;
438 : }
439 :
440 : /*
441 : * GetSysCacheOid
442 : *
443 : * A convenience routine that does SearchSysCache and returns the OID in the
444 : * oidcol column of the found tuple, or InvalidOid if no tuple could be found.
445 : * No lock is retained on the syscache entry.
446 : */
447 : Oid
448 3369572 : GetSysCacheOid(int cacheId,
449 : AttrNumber oidcol,
450 : Datum key1,
451 : Datum key2,
452 : Datum key3,
453 : Datum key4)
454 : {
455 : HeapTuple tuple;
456 : bool isNull;
457 : Oid result;
458 :
459 3369572 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
460 3369572 : if (!HeapTupleIsValid(tuple))
461 1351642 : return InvalidOid;
462 4035860 : result = heap_getattr(tuple, oidcol,
463 2017930 : SysCache[cacheId]->cc_tupdesc,
464 : &isNull);
465 : Assert(!isNull); /* columns used as oids should never be NULL */
466 2017930 : ReleaseSysCache(tuple);
467 2017930 : return result;
468 : }
469 :
470 :
471 : /*
472 : * SearchSysCacheAttName
473 : *
474 : * This routine is equivalent to SearchSysCache on the ATTNAME cache,
475 : * except that it will return NULL if the found attribute is marked
476 : * attisdropped. This is convenient for callers that want to act as
477 : * though dropped attributes don't exist.
478 : */
479 : HeapTuple
480 101446 : SearchSysCacheAttName(Oid relid, const char *attname)
481 : {
482 : HeapTuple tuple;
483 :
484 101446 : tuple = SearchSysCache2(ATTNAME,
485 : ObjectIdGetDatum(relid),
486 : CStringGetDatum(attname));
487 101446 : if (!HeapTupleIsValid(tuple))
488 924 : return NULL;
489 100522 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
490 : {
491 78 : ReleaseSysCache(tuple);
492 78 : return NULL;
493 : }
494 100444 : return tuple;
495 : }
496 :
497 : /*
498 : * SearchSysCacheCopyAttName
499 : *
500 : * As above, an attisdropped-aware version of SearchSysCacheCopy.
501 : */
502 : HeapTuple
503 8536 : SearchSysCacheCopyAttName(Oid relid, const char *attname)
504 : {
505 : HeapTuple tuple,
506 : newtuple;
507 :
508 8536 : tuple = SearchSysCacheAttName(relid, attname);
509 8536 : if (!HeapTupleIsValid(tuple))
510 654 : return tuple;
511 7882 : newtuple = heap_copytuple(tuple);
512 7882 : ReleaseSysCache(tuple);
513 7882 : return newtuple;
514 : }
515 :
516 : /*
517 : * SearchSysCacheExistsAttName
518 : *
519 : * As above, an attisdropped-aware version of SearchSysCacheExists.
520 : */
521 : bool
522 1014 : SearchSysCacheExistsAttName(Oid relid, const char *attname)
523 : {
524 : HeapTuple tuple;
525 :
526 1014 : tuple = SearchSysCacheAttName(relid, attname);
527 1014 : if (!HeapTupleIsValid(tuple))
528 54 : return false;
529 960 : ReleaseSysCache(tuple);
530 960 : return true;
531 : }
532 :
533 :
534 : /*
535 : * SearchSysCacheAttNum
536 : *
537 : * This routine is equivalent to SearchSysCache on the ATTNUM cache,
538 : * except that it will return NULL if the found attribute is marked
539 : * attisdropped. This is convenient for callers that want to act as
540 : * though dropped attributes don't exist.
541 : */
542 : HeapTuple
543 1580 : SearchSysCacheAttNum(Oid relid, int16 attnum)
544 : {
545 : HeapTuple tuple;
546 :
547 1580 : tuple = SearchSysCache2(ATTNUM,
548 : ObjectIdGetDatum(relid),
549 : Int16GetDatum(attnum));
550 1580 : if (!HeapTupleIsValid(tuple))
551 12 : return NULL;
552 1568 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
553 : {
554 0 : ReleaseSysCache(tuple);
555 0 : return NULL;
556 : }
557 1568 : return tuple;
558 : }
559 :
560 : /*
561 : * SearchSysCacheCopyAttNum
562 : *
563 : * As above, an attisdropped-aware version of SearchSysCacheCopy.
564 : */
565 : HeapTuple
566 1516 : SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
567 : {
568 : HeapTuple tuple,
569 : newtuple;
570 :
571 1516 : tuple = SearchSysCacheAttNum(relid, attnum);
572 1516 : if (!HeapTupleIsValid(tuple))
573 0 : return NULL;
574 1516 : newtuple = heap_copytuple(tuple);
575 1516 : ReleaseSysCache(tuple);
576 1516 : return newtuple;
577 : }
578 :
579 :
580 : /*
581 : * SysCacheGetAttr
582 : *
583 : * Given a tuple previously fetched by SearchSysCache(),
584 : * extract a specific attribute.
585 : *
586 : * This is equivalent to using heap_getattr() on a tuple fetched
587 : * from a non-cached relation. Usually, this is only used for attributes
588 : * that could be NULL or variable length; the fixed-size attributes in
589 : * a system table are accessed just by mapping the tuple onto the C struct
590 : * declarations from include/catalog/.
591 : *
592 : * As with heap_getattr(), if the attribute is of a pass-by-reference type
593 : * then a pointer into the tuple data area is returned --- the caller must
594 : * not modify or pfree the datum!
595 : *
596 : * Note: it is legal to use SysCacheGetAttr() with a cacheId referencing
597 : * a different cache for the same catalog the tuple was fetched from.
598 : */
599 : Datum
600 5033052 : SysCacheGetAttr(int cacheId, HeapTuple tup,
601 : AttrNumber attributeNumber,
602 : bool *isNull)
603 : {
604 : /*
605 : * We just need to get the TupleDesc out of the cache entry, and then we
606 : * can apply heap_getattr(). Normally the cache control data is already
607 : * valid (because the caller recently fetched the tuple via this same
608 : * cache), but there are cases where we have to initialize the cache here.
609 : */
610 5033052 : if (cacheId < 0 || cacheId >= SysCacheSize ||
611 5033052 : !PointerIsValid(SysCache[cacheId]))
612 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
613 5033052 : if (!PointerIsValid(SysCache[cacheId]->cc_tupdesc))
614 : {
615 27488 : InitCatCachePhase2(SysCache[cacheId], false);
616 : Assert(PointerIsValid(SysCache[cacheId]->cc_tupdesc));
617 : }
618 :
619 10066104 : return heap_getattr(tup, attributeNumber,
620 5033052 : SysCache[cacheId]->cc_tupdesc,
621 : isNull);
622 : }
623 :
624 : /*
625 : * SysCacheGetAttrNotNull
626 : *
627 : * As above, a version of SysCacheGetAttr which knows that the attr cannot
628 : * be NULL.
629 : */
630 : Datum
631 2844014 : SysCacheGetAttrNotNull(int cacheId, HeapTuple tup,
632 : AttrNumber attributeNumber)
633 : {
634 : bool isnull;
635 : Datum attr;
636 :
637 2844014 : attr = SysCacheGetAttr(cacheId, tup, attributeNumber, &isnull);
638 :
639 2844014 : if (isnull)
640 : {
641 0 : elog(ERROR,
642 : "unexpected null value in cached tuple for catalog %s column %s",
643 : get_rel_name(cacheinfo[cacheId].reloid),
644 : NameStr(TupleDescAttr(SysCache[cacheId]->cc_tupdesc, attributeNumber - 1)->attname));
645 : }
646 :
647 2844014 : return attr;
648 : }
649 :
650 : /*
651 : * GetSysCacheHashValue
652 : *
653 : * Get the hash value that would be used for a tuple in the specified cache
654 : * with the given search keys.
655 : *
656 : * The reason for exposing this as part of the API is that the hash value is
657 : * exposed in cache invalidation operations, so there are places outside the
658 : * catcache code that need to be able to compute the hash values.
659 : */
660 : uint32
661 994326 : GetSysCacheHashValue(int cacheId,
662 : Datum key1,
663 : Datum key2,
664 : Datum key3,
665 : Datum key4)
666 : {
667 994326 : if (cacheId < 0 || cacheId >= SysCacheSize ||
668 994326 : !PointerIsValid(SysCache[cacheId]))
669 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
670 :
671 994326 : return GetCatCacheHashValue(SysCache[cacheId], key1, key2, key3, key4);
672 : }
673 :
674 : /*
675 : * List-search interface
676 : */
677 : struct catclist *
678 3309392 : SearchSysCacheList(int cacheId, int nkeys,
679 : Datum key1, Datum key2, Datum key3)
680 : {
681 3309392 : if (cacheId < 0 || cacheId >= SysCacheSize ||
682 3309392 : !PointerIsValid(SysCache[cacheId]))
683 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
684 :
685 3309392 : return SearchCatCacheList(SysCache[cacheId], nkeys,
686 : key1, key2, key3);
687 : }
688 :
689 : /*
690 : * SysCacheInvalidate
691 : *
692 : * Invalidate entries in the specified cache, given a hash value.
693 : * See CatCacheInvalidate() for more info.
694 : *
695 : * This routine is only quasi-public: it should only be used by inval.c.
696 : */
697 : void
698 19184256 : SysCacheInvalidate(int cacheId, uint32 hashValue)
699 : {
700 19184256 : if (cacheId < 0 || cacheId >= SysCacheSize)
701 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
702 :
703 : /* if this cache isn't initialized yet, no need to do anything */
704 19184256 : if (!PointerIsValid(SysCache[cacheId]))
705 0 : return;
706 :
707 19184256 : CatCacheInvalidate(SysCache[cacheId], hashValue);
708 : }
709 :
710 : /*
711 : * Certain relations that do not have system caches send snapshot invalidation
712 : * messages in lieu of catcache messages. This is for the benefit of
713 : * GetCatalogSnapshot(), which can then reuse its existing MVCC snapshot
714 : * for scanning one of those catalogs, rather than taking a new one, if no
715 : * invalidation has been received.
716 : *
717 : * Relations that have syscaches need not (and must not) be listed here. The
718 : * catcache invalidation messages will also flush the snapshot. If you add a
719 : * syscache for one of these relations, remove it from this list.
720 : */
721 : bool
722 14188954 : RelationInvalidatesSnapshotsOnly(Oid relid)
723 : {
724 14188954 : switch (relid)
725 : {
726 2408712 : case DbRoleSettingRelationId:
727 : case DependRelationId:
728 : case SharedDependRelationId:
729 : case DescriptionRelationId:
730 : case SharedDescriptionRelationId:
731 : case SecLabelRelationId:
732 : case SharedSecLabelRelationId:
733 2408712 : return true;
734 11780242 : default:
735 11780242 : break;
736 : }
737 :
738 11780242 : return false;
739 : }
740 :
741 : /*
742 : * Test whether a relation has a system cache.
743 : */
744 : bool
745 8866184 : RelationHasSysCache(Oid relid)
746 : {
747 8866184 : int low = 0,
748 8866184 : high = SysCacheRelationOidSize - 1;
749 :
750 39184446 : while (low <= high)
751 : {
752 38741488 : int middle = low + (high - low) / 2;
753 :
754 38741488 : if (SysCacheRelationOid[middle] == relid)
755 8423226 : return true;
756 30318262 : if (SysCacheRelationOid[middle] < relid)
757 10539020 : low = middle + 1;
758 : else
759 19779242 : high = middle - 1;
760 : }
761 :
762 442958 : return false;
763 : }
764 :
765 : /*
766 : * Test whether a relation supports a system cache, ie it is either a
767 : * cached table or the index used for a cache.
768 : */
769 : bool
770 2249358 : RelationSupportsSysCache(Oid relid)
771 : {
772 2249358 : int low = 0,
773 2249358 : high = SysCacheSupportingRelOidSize - 1;
774 :
775 18833024 : while (low <= high)
776 : {
777 17041134 : int middle = low + (high - low) / 2;
778 :
779 17041134 : if (SysCacheSupportingRelOid[middle] == relid)
780 457468 : return true;
781 16583666 : if (SysCacheSupportingRelOid[middle] < relid)
782 15084476 : low = middle + 1;
783 : else
784 1499190 : high = middle - 1;
785 : }
786 :
787 1791890 : return false;
788 : }
789 :
790 :
791 : /*
792 : * OID comparator for qsort
793 : */
794 : static int
795 57238104 : oid_compare(const void *a, const void *b)
796 : {
797 57238104 : Oid oa = *((const Oid *) a);
798 57238104 : Oid ob = *((const Oid *) b);
799 :
800 57238104 : return pg_cmp_u32(oa, ob);
801 : }
|