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 31284 : InitCatalogCache(void)
111 : {
112 : int cacheId;
113 :
114 : Assert(!CacheInitialized);
115 :
116 31284 : SysCacheRelationOidSize = SysCacheSupportingRelOidSize = 0;
117 :
118 2690424 : 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 5318280 : SysCache[cacheId] = InitCatCache(cacheId,
129 : cacheinfo[cacheId].reloid,
130 : cacheinfo[cacheId].indoid,
131 : cacheinfo[cacheId].nkeys,
132 2659140 : cacheinfo[cacheId].key,
133 : cacheinfo[cacheId].nbuckets);
134 2659140 : 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 2659140 : SysCacheRelationOid[SysCacheRelationOidSize++] =
139 2659140 : cacheinfo[cacheId].reloid;
140 2659140 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
141 2659140 : cacheinfo[cacheId].reloid;
142 2659140 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
143 2659140 : 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 31284 : qsort(SysCacheRelationOid, SysCacheRelationOidSize,
153 : sizeof(Oid), oid_compare);
154 31284 : SysCacheRelationOidSize =
155 31284 : qunique(SysCacheRelationOid, SysCacheRelationOidSize, sizeof(Oid),
156 : oid_compare);
157 :
158 31284 : qsort(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
159 : sizeof(Oid), oid_compare);
160 31284 : SysCacheSupportingRelOidSize =
161 31284 : qunique(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
162 : sizeof(Oid), oid_compare);
163 :
164 31284 : CacheInitialized = true;
165 31284 : }
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 2776 : InitCatalogCachePhase2(void)
181 : {
182 : int cacheId;
183 :
184 : Assert(CacheInitialized);
185 :
186 238622 : for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
187 235852 : InitCatCachePhase2(SysCache[cacheId], true);
188 2770 : }
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 5053162 : 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 5053162 : return SearchCatCache(SysCache[cacheId], key1, key2, key3, key4);
218 : }
219 :
220 : HeapTuple
221 62360006 : 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 62360006 : return SearchCatCache1(SysCache[cacheId], key1);
229 : }
230 :
231 : HeapTuple
232 4996478 : 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 4996478 : return SearchCatCache2(SysCache[cacheId], key1, key2);
240 : }
241 :
242 : HeapTuple
243 4755000 : 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 4755000 : return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
251 : }
252 :
253 : HeapTuple
254 3906266 : 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 3906266 : 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 75739388 : ReleaseSysCache(HeapTuple tuple)
270 : {
271 75739388 : ReleaseCatCache(tuple);
272 75739388 : }
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 43260 : SearchSysCacheLocked1(int cacheId,
288 : Datum key1)
289 : {
290 : ItemPointerData tid;
291 : LOCKTAG tag;
292 43260 : Oid dboid =
293 43260 : SysCache[cacheId]->cc_relisshared ? InvalidOid : MyDatabaseId;
294 43260 : Oid reloid = cacheinfo[cacheId].reloid;
295 :
296 : /*----------
297 : * Since inplace updates may happen just before our LockTuple(), we must
298 : * return content acquired after LockTuple() of the TID we return. If we
299 : * just fetched twice instead of looping, the following sequence would
300 : * defeat our locking:
301 : *
302 : * GRANT: SearchSysCache1() = TID (1,5)
303 : * GRANT: LockTuple(pg_class, (1,5))
304 : * [no more inplace update of (1,5) until we release the lock]
305 : * CLUSTER: SearchSysCache1() = TID (1,5)
306 : * CLUSTER: heap_update() = TID (1,8)
307 : * CLUSTER: COMMIT
308 : * GRANT: SearchSysCache1() = TID (1,8)
309 : * GRANT: return (1,8) from SearchSysCacheLocked1()
310 : * VACUUM: SearchSysCache1() = TID (1,8)
311 : * VACUUM: LockTuple(pg_class, (1,8)) # two TIDs now locked for one rel
312 : * VACUUM: inplace update
313 : * GRANT: heap_update() = (1,9) # lose inplace update
314 : *
315 : * In the happy case, this takes two fetches, one to determine the TID to
316 : * lock and another to get the content and confirm the TID didn't change.
317 : *
318 : * This is valid even if the row gets updated to a new TID, the old TID
319 : * becomes LP_UNUSED, and the row gets updated back to its old TID. We'd
320 : * still hold the right LOCKTAG_TUPLE and a copy of the row captured after
321 : * the LOCKTAG_TUPLE.
322 : */
323 43260 : ItemPointerSetInvalid(&tid);
324 : for (;;)
325 43262 : {
326 : HeapTuple tuple;
327 86522 : LOCKMODE lockmode = InplaceUpdateTupleLock;
328 :
329 86522 : tuple = SearchSysCache1(cacheId, key1);
330 86522 : if (ItemPointerIsValid(&tid))
331 : {
332 43262 : if (!HeapTupleIsValid(tuple))
333 : {
334 0 : LockRelease(&tag, lockmode, false);
335 0 : return tuple;
336 : }
337 43262 : if (ItemPointerEquals(&tid, &tuple->t_self))
338 43260 : return tuple;
339 2 : LockRelease(&tag, lockmode, false);
340 : }
341 43260 : else if (!HeapTupleIsValid(tuple))
342 0 : return tuple;
343 :
344 43262 : tid = tuple->t_self;
345 43262 : ReleaseSysCache(tuple);
346 : /* like: LockTuple(rel, &tid, lockmode) */
347 43262 : SET_LOCKTAG_TUPLE(tag, dboid, reloid,
348 : ItemPointerGetBlockNumber(&tid),
349 : ItemPointerGetOffsetNumber(&tid));
350 43262 : (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 43262 : 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 442864 : SearchSysCacheCopy(int cacheId,
375 : Datum key1,
376 : Datum key2,
377 : Datum key3,
378 : Datum key4)
379 : {
380 : HeapTuple tuple,
381 : newtuple;
382 :
383 442864 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
384 442864 : if (!HeapTupleIsValid(tuple))
385 129626 : return tuple;
386 313238 : newtuple = heap_copytuple(tuple);
387 313238 : ReleaseSysCache(tuple);
388 313238 : return newtuple;
389 : }
390 :
391 : /*
392 : * SearchSysCacheLockedCopy1
393 : *
394 : * Meld SearchSysCacheLockedCopy1 with SearchSysCacheCopy(). After the
395 : * caller's heap_update(), it should UnlockTuple(InplaceUpdateTupleLock) and
396 : * heap_freetuple().
397 : */
398 : HeapTuple
399 15770 : SearchSysCacheLockedCopy1(int cacheId,
400 : Datum key1)
401 : {
402 : HeapTuple tuple,
403 : newtuple;
404 :
405 15770 : tuple = SearchSysCacheLocked1(cacheId, key1);
406 15770 : if (!HeapTupleIsValid(tuple))
407 0 : return tuple;
408 15770 : newtuple = heap_copytuple(tuple);
409 15770 : ReleaseSysCache(tuple);
410 15770 : 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 1234154 : SearchSysCacheExists(int cacheId,
421 : Datum key1,
422 : Datum key2,
423 : Datum key3,
424 : Datum key4)
425 : {
426 : HeapTuple tuple;
427 :
428 1234154 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
429 1234154 : if (!HeapTupleIsValid(tuple))
430 222508 : return false;
431 1011646 : ReleaseSysCache(tuple);
432 1011646 : 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 3376144 : 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 3376144 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
455 3376144 : if (!HeapTupleIsValid(tuple))
456 1355550 : return InvalidOid;
457 4041188 : result = heap_getattr(tuple, oidcol,
458 2020594 : SysCache[cacheId]->cc_tupdesc,
459 : &isNull);
460 : Assert(!isNull); /* columns used as oids should never be NULL */
461 2020594 : ReleaseSysCache(tuple);
462 2020594 : 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 101038 : SearchSysCacheAttName(Oid relid, const char *attname)
476 : {
477 : HeapTuple tuple;
478 :
479 101038 : tuple = SearchSysCache2(ATTNAME,
480 : ObjectIdGetDatum(relid),
481 : CStringGetDatum(attname));
482 101038 : if (!HeapTupleIsValid(tuple))
483 918 : return NULL;
484 100120 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
485 : {
486 78 : ReleaseSysCache(tuple);
487 78 : return NULL;
488 : }
489 100042 : return tuple;
490 : }
491 :
492 : /*
493 : * SearchSysCacheCopyAttName
494 : *
495 : * As above, an attisdropped-aware version of SearchSysCacheCopy.
496 : */
497 : HeapTuple
498 8530 : SearchSysCacheCopyAttName(Oid relid, const char *attname)
499 : {
500 : HeapTuple tuple,
501 : newtuple;
502 :
503 8530 : tuple = SearchSysCacheAttName(relid, attname);
504 8530 : if (!HeapTupleIsValid(tuple))
505 654 : return tuple;
506 7876 : newtuple = heap_copytuple(tuple);
507 7876 : ReleaseSysCache(tuple);
508 7876 : return newtuple;
509 : }
510 :
511 : /*
512 : * SearchSysCacheExistsAttName
513 : *
514 : * As above, an attisdropped-aware version of SearchSysCacheExists.
515 : */
516 : bool
517 1014 : SearchSysCacheExistsAttName(Oid relid, const char *attname)
518 : {
519 : HeapTuple tuple;
520 :
521 1014 : tuple = SearchSysCacheAttName(relid, attname);
522 1014 : if (!HeapTupleIsValid(tuple))
523 54 : return false;
524 960 : ReleaseSysCache(tuple);
525 960 : 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 1568 : SearchSysCacheAttNum(Oid relid, int16 attnum)
539 : {
540 : HeapTuple tuple;
541 :
542 1568 : tuple = SearchSysCache2(ATTNUM,
543 : ObjectIdGetDatum(relid),
544 : Int16GetDatum(attnum));
545 1568 : if (!HeapTupleIsValid(tuple))
546 12 : return NULL;
547 1556 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
548 : {
549 0 : ReleaseSysCache(tuple);
550 0 : return NULL;
551 : }
552 1556 : return tuple;
553 : }
554 :
555 : /*
556 : * SearchSysCacheCopyAttNum
557 : *
558 : * As above, an attisdropped-aware version of SearchSysCacheCopy.
559 : */
560 : HeapTuple
561 1504 : SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
562 : {
563 : HeapTuple tuple,
564 : newtuple;
565 :
566 1504 : tuple = SearchSysCacheAttNum(relid, attnum);
567 1504 : if (!HeapTupleIsValid(tuple))
568 0 : return NULL;
569 1504 : newtuple = heap_copytuple(tuple);
570 1504 : ReleaseSysCache(tuple);
571 1504 : 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 5042570 : 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 5042570 : if (cacheId < 0 || cacheId >= SysCacheSize ||
606 5042570 : !PointerIsValid(SysCache[cacheId]))
607 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
608 5042570 : if (!PointerIsValid(SysCache[cacheId]->cc_tupdesc))
609 : {
610 29806 : InitCatCachePhase2(SysCache[cacheId], false);
611 : Assert(PointerIsValid(SysCache[cacheId]->cc_tupdesc));
612 : }
613 :
614 10085140 : return heap_getattr(tup, attributeNumber,
615 5042570 : 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 2848016 : SysCacheGetAttrNotNull(int cacheId, HeapTuple tup,
627 : AttrNumber attributeNumber)
628 : {
629 : bool isnull;
630 : Datum attr;
631 :
632 2848016 : attr = SysCacheGetAttr(cacheId, tup, attributeNumber, &isnull);
633 :
634 2848016 : 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 2848016 : 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 996336 : GetSysCacheHashValue(int cacheId,
657 : Datum key1,
658 : Datum key2,
659 : Datum key3,
660 : Datum key4)
661 : {
662 996336 : if (cacheId < 0 || cacheId >= SysCacheSize ||
663 996336 : !PointerIsValid(SysCache[cacheId]))
664 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
665 :
666 996336 : return GetCatCacheHashValue(SysCache[cacheId], key1, key2, key3, key4);
667 : }
668 :
669 : /*
670 : * List-search interface
671 : */
672 : struct catclist *
673 3310470 : SearchSysCacheList(int cacheId, int nkeys,
674 : Datum key1, Datum key2, Datum key3)
675 : {
676 3310470 : if (cacheId < 0 || cacheId >= SysCacheSize ||
677 3310470 : !PointerIsValid(SysCache[cacheId]))
678 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
679 :
680 3310470 : return SearchCatCacheList(SysCache[cacheId], nkeys,
681 : key1, key2, key3);
682 : }
683 :
684 : /*
685 : * SysCacheInvalidate
686 : *
687 : * Invalidate entries in the specified cache, given a hash value.
688 : * See CatCacheInvalidate() for more info.
689 : *
690 : * This routine is only quasi-public: it should only be used by inval.c.
691 : */
692 : void
693 19204234 : SysCacheInvalidate(int cacheId, uint32 hashValue)
694 : {
695 19204234 : if (cacheId < 0 || cacheId >= SysCacheSize)
696 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
697 :
698 : /* if this cache isn't initialized yet, no need to do anything */
699 19204234 : if (!PointerIsValid(SysCache[cacheId]))
700 0 : return;
701 :
702 19204234 : CatCacheInvalidate(SysCache[cacheId], hashValue);
703 : }
704 :
705 : /*
706 : * Certain relations that do not have system caches send snapshot invalidation
707 : * messages in lieu of catcache messages. This is for the benefit of
708 : * GetCatalogSnapshot(), which can then reuse its existing MVCC snapshot
709 : * for scanning one of those catalogs, rather than taking a new one, if no
710 : * invalidation has been received.
711 : *
712 : * Relations that have syscaches need not (and must not) be listed here. The
713 : * catcache invalidation messages will also flush the snapshot. If you add a
714 : * syscache for one of these relations, remove it from this list.
715 : */
716 : bool
717 14228604 : RelationInvalidatesSnapshotsOnly(Oid relid)
718 : {
719 14228604 : switch (relid)
720 : {
721 2401130 : case DbRoleSettingRelationId:
722 : case DependRelationId:
723 : case SharedDependRelationId:
724 : case DescriptionRelationId:
725 : case SharedDescriptionRelationId:
726 : case SecLabelRelationId:
727 : case SharedSecLabelRelationId:
728 2401130 : return true;
729 11827474 : default:
730 11827474 : break;
731 : }
732 :
733 11827474 : return false;
734 : }
735 :
736 : /*
737 : * Test whether a relation has a system cache.
738 : */
739 : bool
740 8928232 : RelationHasSysCache(Oid relid)
741 : {
742 8928232 : int low = 0,
743 8928232 : high = SysCacheRelationOidSize - 1;
744 :
745 39465162 : while (low <= high)
746 : {
747 39023620 : int middle = low + (high - low) / 2;
748 :
749 39023620 : if (SysCacheRelationOid[middle] == relid)
750 8486690 : return true;
751 30536930 : if (SysCacheRelationOid[middle] < relid)
752 10621336 : low = middle + 1;
753 : else
754 19915594 : high = middle - 1;
755 : }
756 :
757 441542 : return false;
758 : }
759 :
760 : /*
761 : * Test whether a relation supports a system cache, ie it is either a
762 : * cached table or the index used for a cache.
763 : */
764 : bool
765 2232176 : RelationSupportsSysCache(Oid relid)
766 : {
767 2232176 : int low = 0,
768 2232176 : high = SysCacheSupportingRelOidSize - 1;
769 :
770 18699190 : while (low <= high)
771 : {
772 16918072 : int middle = low + (high - low) / 2;
773 :
774 16918072 : if (SysCacheSupportingRelOid[middle] == relid)
775 451058 : return true;
776 16467014 : if (SysCacheSupportingRelOid[middle] < relid)
777 14992194 : low = middle + 1;
778 : else
779 1474820 : high = middle - 1;
780 : }
781 :
782 1781118 : return false;
783 : }
784 :
785 :
786 : /*
787 : * OID comparator for qsort
788 : */
789 : static int
790 61848468 : oid_compare(const void *a, const void *b)
791 : {
792 61848468 : Oid oa = *((const Oid *) a);
793 61848468 : Oid ob = *((const Oid *) b);
794 :
795 61848468 : return pg_cmp_u32(oa, ob);
796 : }
|