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