Age Owner Branch data TLA 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
9112 tgl@sss.pgh.pa.us 110 :CBC 15098 : InitCatalogCache(void)
111 : : {
112 : : int cacheId;
113 : :
114 [ - + ]: 15098 : Assert(!CacheInitialized);
115 : :
3796 116 : 15098 : SysCacheRelationOidSize = SysCacheSupportingRelOidSize = 0;
117 : :
9112 118 [ + + ]: 1298428 : 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 : : */
824 michael@paquier.xyz 124 [ - + ]: 1283330 : Assert(OidIsValid(cacheinfo[cacheId].reloid));
125 [ - + ]: 1283330 : Assert(OidIsValid(cacheinfo[cacheId].indoid));
126 : : /* .nbuckets and .key[] are checked by InitCatCache() */
127 : :
9112 tgl@sss.pgh.pa.us 128 : 2566660 : SysCache[cacheId] = InitCatCache(cacheId,
7502 129 : 1283330 : cacheinfo[cacheId].reloid,
130 : 1283330 : cacheinfo[cacheId].indoid,
9112 131 : 1283330 : cacheinfo[cacheId].nkeys,
7075 132 : 1283330 : cacheinfo[cacheId].key,
133 : 1283330 : cacheinfo[cacheId].nbuckets);
34 peter@eisentraut.org 134 [ - + ]:GNC 1283330 : if (!SysCache[cacheId])
7502 tgl@sss.pgh.pa.us 135 [ # # ]:UBC 0 : elog(ERROR, "could not initialize cache %u (%d)",
136 : : cacheinfo[cacheId].reloid, cacheId);
137 : : /* Accumulate data for OID lists, too */
4501 rhaas@postgresql.org 138 :CBC 1283330 : SysCacheRelationOid[SysCacheRelationOidSize++] =
139 : 1283330 : cacheinfo[cacheId].reloid;
3796 tgl@sss.pgh.pa.us 140 : 1283330 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
141 : 1283330 : cacheinfo[cacheId].reloid;
142 : 1283330 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
143 : 1283330 : cacheinfo[cacheId].indoid;
144 : : /* see comments for RelationInvalidatesSnapshotsOnly */
4501 rhaas@postgresql.org 145 [ - + ]: 1283330 : Assert(!RelationInvalidatesSnapshotsOnly(cacheinfo[cacheId].reloid));
146 : : }
147 : :
3796 tgl@sss.pgh.pa.us 148 [ - + ]: 15098 : Assert(SysCacheRelationOidSize <= lengthof(SysCacheRelationOid));
149 [ - + ]: 15098 : Assert(SysCacheSupportingRelOidSize <= lengthof(SysCacheSupportingRelOid));
150 : :
151 : : /* Sort and de-dup OID arrays, so we can use binary search. */
620 nathan@postgresql.or 152 : 15098 : qsort(SysCacheRelationOid, SysCacheRelationOidSize,
153 : : sizeof(Oid), oid_compare);
2182 tmunro@postgresql.or 154 : 15098 : SysCacheRelationOidSize =
155 : 15098 : qunique(SysCacheRelationOid, SysCacheRelationOidSize, sizeof(Oid),
156 : : oid_compare);
157 : :
620 nathan@postgresql.or 158 : 15098 : qsort(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
159 : : sizeof(Oid), oid_compare);
2182 tmunro@postgresql.or 160 : 15098 : SysCacheSupportingRelOidSize =
161 : 15098 : qunique(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
162 : : sizeof(Oid), oid_compare);
163 : :
9384 inoue@tpf.co.jp 164 : 15098 : CacheInitialized = true;
10703 scrappy@hub.org 165 : 15098 : }
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
8652 tgl@sss.pgh.pa.us 180 : 1517 : InitCatalogCachePhase2(void)
181 : : {
182 : : int cacheId;
183 : :
184 [ - + ]: 1517 : Assert(CacheInitialized);
185 : :
186 [ + + ]: 130462 : for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
6962 187 : 128945 : InitCatCachePhase2(SysCache[cacheId], true);
8652 188 : 1517 : }
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
9112 208 : 2738187 : SearchSysCache(int cacheId,
209 : : Datum key1,
210 : : Datum key2,
211 : : Datum key3,
212 : : Datum key4)
213 : : {
34 peter@eisentraut.org 214 [ + - + - :GNC 2738187 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
- + ]
215 : :
9112 tgl@sss.pgh.pa.us 216 :CBC 2738187 : return SearchCatCache(SysCache[cacheId], key1, key2, key3, key4);
217 : : }
218 : :
219 : : HeapTuple
2937 andres@anarazel.de 220 : 34931657 : SearchSysCache1(int cacheId,
221 : : Datum key1)
222 : : {
34 peter@eisentraut.org 223 [ + - + - :GNC 34931657 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
- + ]
2937 andres@anarazel.de 224 [ - + ]:CBC 34931657 : Assert(SysCache[cacheId]->cc_nkeys == 1);
225 : :
226 : 34931657 : return SearchCatCache1(SysCache[cacheId], key1);
227 : : }
228 : :
229 : : HeapTuple
230 : 2880078 : SearchSysCache2(int cacheId,
231 : : Datum key1, Datum key2)
232 : : {
34 peter@eisentraut.org 233 [ + - + - :GNC 2880078 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
- + ]
2937 andres@anarazel.de 234 [ - + ]:CBC 2880078 : Assert(SysCache[cacheId]->cc_nkeys == 2);
235 : :
236 : 2880078 : return SearchCatCache2(SysCache[cacheId], key1, key2);
237 : : }
238 : :
239 : : HeapTuple
240 : 2958081 : SearchSysCache3(int cacheId,
241 : : Datum key1, Datum key2, Datum key3)
242 : : {
34 peter@eisentraut.org 243 [ + - + - :GNC 2958081 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
- + ]
2937 andres@anarazel.de 244 [ - + ]:CBC 2958081 : Assert(SysCache[cacheId]->cc_nkeys == 3);
245 : :
246 : 2958081 : return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
247 : : }
248 : :
249 : : HeapTuple
250 : 2301168 : SearchSysCache4(int cacheId,
251 : : Datum key1, Datum key2, Datum key3, Datum key4)
252 : : {
34 peter@eisentraut.org 253 [ + - + - :GNC 2301168 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
- + ]
2937 andres@anarazel.de 254 [ - + ]:CBC 2301168 : Assert(SysCache[cacheId]->cc_nkeys == 4);
255 : :
256 : 2301168 : 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
9112 tgl@sss.pgh.pa.us 264 : 42759767 : ReleaseSysCache(HeapTuple tuple)
265 : : {
266 : 42759767 : ReleaseCatCache(tuple);
267 : 42759767 : }
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
399 noah@leadboat.com 282 : 23772 : SearchSysCacheLocked1(int cacheId,
283 : : Datum key1)
284 : : {
337 285 : 23772 : 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 : : */
399 316 : 23772 : ItemPointerSetInvalid(&tid);
317 : : for (;;)
318 : 23773 : {
319 : : HeapTuple tuple;
320 : 47545 : LOCKMODE lockmode = InplaceUpdateTupleLock;
321 : :
322 : 47545 : tuple = SearchSysCache1(cacheId, key1);
323 [ + + ]: 47545 : if (ItemPointerIsValid(&tid))
324 : : {
325 [ + + ]: 23773 : if (!HeapTupleIsValid(tuple))
326 : : {
327 : 1 : LockRelease(&tag, lockmode, false);
328 : 1 : return tuple;
329 : : }
330 [ + + ]: 23772 : if (ItemPointerEquals(&tid, &tuple->t_self))
331 : 23771 : return tuple;
332 : 1 : LockRelease(&tag, lockmode, false);
333 : : }
334 [ - + ]: 23772 : else if (!HeapTupleIsValid(tuple))
399 noah@leadboat.com 335 :UBC 0 : return tuple;
336 : :
399 noah@leadboat.com 337 :CBC 23773 : tid = tuple->t_self;
338 : 23773 : 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 : : */
337 345 [ + + ]: 23773 : SET_LOCKTAG_TUPLE(tag,
346 : : cache->cc_relisshared ? InvalidOid : MyDatabaseId,
347 : : cache->cc_reloid,
348 : : ItemPointerGetBlockNumber(&tid),
349 : : ItemPointerGetOffsetNumber(&tid));
399 350 : 23773 : (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 : 23773 : 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
9112 tgl@sss.pgh.pa.us 374 : 204658 : SearchSysCacheCopy(int cacheId,
375 : : Datum key1,
376 : : Datum key2,
377 : : Datum key3,
378 : : Datum key4)
379 : : {
380 : : HeapTuple tuple,
381 : : newtuple;
382 : :
383 : 204658 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
384 [ + + ]: 204658 : if (!HeapTupleIsValid(tuple))
385 : 67719 : return tuple;
386 : 136939 : newtuple = heap_copytuple(tuple);
387 : 136939 : ReleaseSysCache(tuple);
388 : 136939 : 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 noah@leadboat.com 399 : 8560 : SearchSysCacheLockedCopy1(int cacheId,
400 : : Datum key1)
401 : : {
402 : : HeapTuple tuple,
403 : : newtuple;
404 : :
405 : 8560 : tuple = SearchSysCacheLocked1(cacheId, key1);
406 [ - + ]: 8560 : if (!HeapTupleIsValid(tuple))
399 noah@leadboat.com 407 :UBC 0 : return tuple;
399 noah@leadboat.com 408 :CBC 8560 : newtuple = heap_copytuple(tuple);
409 : 8560 : ReleaseSysCache(tuple);
410 : 8560 : 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
8845 tgl@sss.pgh.pa.us 420 : 638395 : SearchSysCacheExists(int cacheId,
421 : : Datum key1,
422 : : Datum key2,
423 : : Datum key3,
424 : : Datum key4)
425 : : {
426 : : HeapTuple tuple;
427 : :
428 : 638395 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
429 [ + + ]: 638395 : if (!HeapTupleIsValid(tuple))
430 : 117145 : return false;
431 : 521250 : ReleaseSysCache(tuple);
432 : 521250 : 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
9112 443 : 1895134 : 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 : 1895134 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
455 [ + + ]: 1895134 : if (!HeapTupleIsValid(tuple))
456 : 754595 : return InvalidOid;
81 peter@eisentraut.org 457 :GNC 1140539 : result = DatumGetObjectId(heap_getattr(tuple, oidcol,
458 : 1140539 : SysCache[cacheId]->cc_tupdesc,
459 : : &isNull));
2351 tgl@sss.pgh.pa.us 460 [ - + ]:CBC 1140539 : Assert(!isNull); /* columns used as oids should never be NULL */
9112 461 : 1140539 : ReleaseSysCache(tuple);
462 : 1140539 : 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
8488 475 : 56012 : SearchSysCacheAttName(Oid relid, const char *attname)
476 : : {
477 : : HeapTuple tuple;
478 : :
5735 rhaas@postgresql.org 479 : 56012 : tuple = SearchSysCache2(ATTNAME,
480 : : ObjectIdGetDatum(relid),
481 : : CStringGetDatum(attname));
8488 tgl@sss.pgh.pa.us 482 [ + + ]: 56012 : if (!HeapTupleIsValid(tuple))
483 : 516 : return NULL;
484 [ + + ]: 55496 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
485 : : {
486 : 42 : ReleaseSysCache(tuple);
487 : 42 : return NULL;
488 : : }
489 : 55454 : return tuple;
490 : : }
491 : :
492 : : /*
493 : : * SearchSysCacheCopyAttName
494 : : *
495 : : * As above, an attisdropped-aware version of SearchSysCacheCopy.
496 : : */
497 : : HeapTuple
498 : 4776 : SearchSysCacheCopyAttName(Oid relid, const char *attname)
499 : : {
500 : : HeapTuple tuple,
501 : : newtuple;
502 : :
503 : 4776 : tuple = SearchSysCacheAttName(relid, attname);
504 [ + + ]: 4776 : if (!HeapTupleIsValid(tuple))
505 : 372 : return tuple;
506 : 4404 : newtuple = heap_copytuple(tuple);
507 : 4404 : ReleaseSysCache(tuple);
508 : 4404 : return newtuple;
509 : : }
510 : :
511 : : /*
512 : : * SearchSysCacheExistsAttName
513 : : *
514 : : * As above, an attisdropped-aware version of SearchSysCacheExists.
515 : : */
516 : : bool
517 : 551 : SearchSysCacheExistsAttName(Oid relid, const char *attname)
518 : : {
519 : : HeapTuple tuple;
520 : :
521 : 551 : tuple = SearchSysCacheAttName(relid, attname);
522 [ + + ]: 551 : if (!HeapTupleIsValid(tuple))
523 : 27 : return false;
524 : 524 : ReleaseSysCache(tuple);
525 : 524 : 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
2974 simon@2ndQuadrant.co 538 : 944 : SearchSysCacheAttNum(Oid relid, int16 attnum)
539 : : {
540 : : HeapTuple tuple;
541 : :
542 : 944 : tuple = SearchSysCache2(ATTNUM,
543 : : ObjectIdGetDatum(relid),
544 : : Int16GetDatum(attnum));
545 [ + + ]: 944 : if (!HeapTupleIsValid(tuple))
546 : 6 : return NULL;
547 [ - + ]: 938 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
548 : : {
2974 simon@2ndQuadrant.co 549 :UBC 0 : ReleaseSysCache(tuple);
550 : 0 : return NULL;
551 : : }
2974 simon@2ndQuadrant.co 552 :CBC 938 : return tuple;
553 : : }
554 : :
555 : : /*
556 : : * SearchSysCacheCopyAttNum
557 : : *
558 : : * As above, an attisdropped-aware version of SearchSysCacheCopy.
559 : : */
560 : : HeapTuple
561 : 912 : SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
562 : : {
563 : : HeapTuple tuple,
564 : : newtuple;
565 : :
566 : 912 : tuple = SearchSysCacheAttNum(relid, attnum);
567 [ - + ]: 912 : if (!HeapTupleIsValid(tuple))
2974 simon@2ndQuadrant.co 568 :UBC 0 : return NULL;
2974 simon@2ndQuadrant.co 569 :CBC 912 : newtuple = heap_copytuple(tuple);
570 : 912 : ReleaseSysCache(tuple);
571 : 912 : 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
9410 tgl@sss.pgh.pa.us 595 : 2840451 : 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 : : */
34 peter@eisentraut.org 605 [ + - + - :GNC 2840451 : if (cacheId < 0 || cacheId >= SysCacheSize || !SysCache[cacheId])
- + ]
5245 peter_e@gmx.net 606 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
34 peter@eisentraut.org 607 [ + + ]:GNC 2840451 : if (!SysCache[cacheId]->cc_tupdesc)
608 : : {
6962 tgl@sss.pgh.pa.us 609 :CBC 14224 : InitCatCachePhase2(SysCache[cacheId], false);
34 peter@eisentraut.org 610 [ - + ]:GNC 14224 : Assert(SysCache[cacheId]->cc_tupdesc);
611 : : }
612 : :
9410 tgl@sss.pgh.pa.us 613 :CBC 5680902 : return heap_getattr(tup, attributeNumber,
614 : 2840451 : 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
948 dgustafsson@postgres 625 : 1748933 : SysCacheGetAttrNotNull(int cacheId, HeapTuple tup,
626 : : AttrNumber attributeNumber)
627 : : {
628 : : bool isnull;
629 : : Datum attr;
630 : :
631 : 1748933 : attr = SysCacheGetAttr(cacheId, tup, attributeNumber, &isnull);
632 : :
633 [ - + ]: 1748933 : if (isnull)
634 : : {
948 dgustafsson@postgres 635 [ # # ]:UBC 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 : :
948 dgustafsson@postgres 641 :CBC 1748933 : 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
4983 tgl@sss.pgh.pa.us 655 : 555628 : GetSysCacheHashValue(int cacheId,
656 : : Datum key1,
657 : : Datum key2,
658 : : Datum key3,
659 : : Datum key4)
660 : : {
34 peter@eisentraut.org 661 [ + - + - :GNC 555628 : if (cacheId < 0 || cacheId >= SysCacheSize || !SysCache[cacheId])
- + ]
4983 tgl@sss.pgh.pa.us 662 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
663 : :
4983 tgl@sss.pgh.pa.us 664 :CBC 555628 : return GetCatCacheHashValue(SysCache[cacheId], key1, key2, key3, key4);
665 : : }
666 : :
667 : : /*
668 : : * List-search interface
669 : : */
670 : : struct catclist *
8606 671 : 1977712 : SearchSysCacheList(int cacheId, int nkeys,
672 : : Datum key1, Datum key2, Datum key3)
673 : : {
34 peter@eisentraut.org 674 [ + - + - :GNC 1977712 : if (cacheId < 0 || cacheId >= SysCacheSize || !SysCache[cacheId])
- + ]
5245 peter_e@gmx.net 675 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
676 : :
8606 tgl@sss.pgh.pa.us 677 :CBC 1977712 : 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
3091 690 : 11597114 : SysCacheInvalidate(int cacheId, uint32 hashValue)
691 : : {
692 [ + - - + ]: 11597114 : if (cacheId < 0 || cacheId >= SysCacheSize)
3091 tgl@sss.pgh.pa.us 693 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
694 : :
695 : : /* if this cache isn't initialized yet, no need to do anything */
34 peter@eisentraut.org 696 [ - + ]:GNC 11597114 : if (!SysCache[cacheId])
3091 tgl@sss.pgh.pa.us 697 :UBC 0 : return;
698 : :
3091 tgl@sss.pgh.pa.us 699 :CBC 11597114 : 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
4501 rhaas@postgresql.org 714 : 8957278 : RelationInvalidatesSnapshotsOnly(Oid relid)
715 : : {
716 [ + + ]: 8957278 : switch (relid)
717 : : {
718 : 1278351 : case DbRoleSettingRelationId:
719 : : case DependRelationId:
720 : : case SharedDependRelationId:
721 : : case DescriptionRelationId:
722 : : case SharedDescriptionRelationId:
723 : : case SecLabelRelationId:
724 : : case SharedSecLabelRelationId:
725 : 1278351 : return true;
726 : 7678927 : default:
727 : 7678927 : break;
728 : : }
729 : :
730 : 7678927 : return false;
731 : : }
732 : :
733 : : /*
734 : : * Test whether a relation has a system cache.
735 : : */
736 : : bool
737 : 4860684 : RelationHasSysCache(Oid relid)
738 : : {
4193 bruce@momjian.us 739 : 4860684 : int low = 0,
740 : 4860684 : high = SysCacheRelationOidSize - 1;
741 : :
4501 rhaas@postgresql.org 742 [ + + ]: 21589671 : while (low <= high)
743 : : {
4193 bruce@momjian.us 744 : 21355196 : int middle = low + (high - low) / 2;
745 : :
4501 rhaas@postgresql.org 746 [ + + ]: 21355196 : if (SysCacheRelationOid[middle] == relid)
747 : 4626209 : return true;
748 [ + + ]: 16728987 : if (SysCacheRelationOid[middle] < relid)
749 : 5939302 : low = middle + 1;
750 : : else
751 : 10789685 : high = middle - 1;
752 : : }
753 : :
754 : 234475 : 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
3796 tgl@sss.pgh.pa.us 762 : 1166005 : RelationSupportsSysCache(Oid relid)
763 : : {
764 : 1166005 : int low = 0,
765 : 1166005 : high = SysCacheSupportingRelOidSize - 1;
766 : :
767 [ + + ]: 9814539 : while (low <= high)
768 : : {
769 : 8873545 : int middle = low + (high - low) / 2;
770 : :
771 [ + + ]: 8873545 : if (SysCacheSupportingRelOid[middle] == relid)
772 : 225011 : return true;
773 [ + + ]: 8648534 : if (SysCacheSupportingRelOid[middle] < relid)
774 : 7960242 : low = middle + 1;
775 : : else
776 : 688292 : high = middle - 1;
777 : : }
778 : :
779 : 940994 : return false;
780 : : }
781 : :
782 : :
783 : : /*
784 : : * OID comparator for qsort
785 : : */
786 : : static int
4501 rhaas@postgresql.org 787 : 29848746 : oid_compare(const void *a, const void *b)
788 : : {
3796 tgl@sss.pgh.pa.us 789 : 29848746 : Oid oa = *((const Oid *) a);
790 : 29848746 : Oid ob = *((const Oid *) b);
791 : :
620 nathan@postgresql.or 792 : 29848746 : return pg_cmp_u32(oa, ob);
793 : : }
|