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-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
9301 tgl@sss.pgh.pa.us 111 :CBC 18666 : InitCatalogCache(void)
112 : : {
113 : : SysCacheIdentifier cacheId;
114 : :
115 [ - + ]: 18666 : Assert(!CacheInitialized);
116 : :
3985 117 : 18666 : SysCacheRelationOidSize = SysCacheSupportingRelOidSize = 0;
118 : :
9301 119 [ + + ]: 1754604 : 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 : : */
1013 michael@paquier.xyz 125 [ - + ]: 1735938 : Assert(OidIsValid(cacheinfo[cacheId].reloid));
126 [ - + ]: 1735938 : Assert(OidIsValid(cacheinfo[cacheId].indoid));
127 : : /* .nbuckets and .key[] are checked by InitCatCache() */
128 : :
9301 tgl@sss.pgh.pa.us 129 : 3471876 : SysCache[cacheId] = InitCatCache(cacheId,
7691 130 : 1735938 : cacheinfo[cacheId].reloid,
131 : 1735938 : cacheinfo[cacheId].indoid,
9301 132 : 1735938 : cacheinfo[cacheId].nkeys,
7264 133 : 1735938 : cacheinfo[cacheId].key,
134 : 1735938 : cacheinfo[cacheId].nbuckets);
223 peter@eisentraut.org 135 [ - + ]:GNC 1735938 : if (!SysCache[cacheId])
7691 tgl@sss.pgh.pa.us 136 [ # # ]:UBC 0 : elog(ERROR, "could not initialize cache %u (%d)",
137 : : cacheinfo[cacheId].reloid, cacheId);
138 : : /* Accumulate data for OID lists, too */
4690 rhaas@postgresql.org 139 :CBC 1735938 : SysCacheRelationOid[SysCacheRelationOidSize++] =
140 : 1735938 : cacheinfo[cacheId].reloid;
3985 tgl@sss.pgh.pa.us 141 : 1735938 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
142 : 1735938 : cacheinfo[cacheId].reloid;
143 : 1735938 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
144 : 1735938 : cacheinfo[cacheId].indoid;
145 : : /* see comments for RelationInvalidatesSnapshotsOnly */
4690 rhaas@postgresql.org 146 [ - + ]: 1735938 : Assert(!RelationInvalidatesSnapshotsOnly(cacheinfo[cacheId].reloid));
147 : : }
148 : :
3985 tgl@sss.pgh.pa.us 149 [ - + ]: 18666 : Assert(SysCacheRelationOidSize <= lengthof(SysCacheRelationOid));
150 [ - + ]: 18666 : Assert(SysCacheSupportingRelOidSize <= lengthof(SysCacheSupportingRelOid));
151 : :
152 : : /* Sort and de-dup OID arrays, so we can use binary search. */
809 nathan@postgresql.or 153 : 18666 : qsort(SysCacheRelationOid, SysCacheRelationOidSize,
154 : : sizeof(Oid), oid_compare);
2371 tmunro@postgresql.or 155 : 18666 : SysCacheRelationOidSize =
156 : 18666 : qunique(SysCacheRelationOid, SysCacheRelationOidSize, sizeof(Oid),
157 : : oid_compare);
158 : :
809 nathan@postgresql.or 159 : 18666 : qsort(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
160 : : sizeof(Oid), oid_compare);
2371 tmunro@postgresql.or 161 : 18666 : SysCacheSupportingRelOidSize =
162 : 18666 : qunique(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
163 : : sizeof(Oid), oid_compare);
164 : :
9573 inoue@tpf.co.jp 165 : 18666 : CacheInitialized = true;
10892 scrappy@hub.org 166 : 18666 : }
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
8841 tgl@sss.pgh.pa.us 181 : 1932 : InitCatalogCachePhase2(void)
182 : : {
183 : : SysCacheIdentifier cacheId;
184 : :
185 [ - + ]: 1932 : Assert(CacheInitialized);
186 : :
187 [ + + ]: 181608 : for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
7151 188 : 179676 : InitCatCachePhase2(SysCache[cacheId], true);
8841 189 : 1932 : }
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
76 michael@paquier.xyz 209 :GNC 3690033 : SearchSysCache(SysCacheIdentifier cacheId,
210 : : Datum key1,
211 : : Datum key2,
212 : : Datum key3,
213 : : Datum key4)
214 : : {
223 peter@eisentraut.org 215 [ + - + - : 3690033 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
- + ]
216 : :
9301 tgl@sss.pgh.pa.us 217 :CBC 3690033 : return SearchCatCache(SysCache[cacheId], key1, key2, key3, key4);
218 : : }
219 : :
220 : : HeapTuple
76 michael@paquier.xyz 221 :GNC 49877714 : SearchSysCache1(SysCacheIdentifier cacheId,
222 : : Datum key1)
223 : : {
223 peter@eisentraut.org 224 [ + - + - : 49877714 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
- + ]
3126 andres@anarazel.de 225 [ - + ]:CBC 49877714 : Assert(SysCache[cacheId]->cc_nkeys == 1);
226 : :
227 : 49877714 : return SearchCatCache1(SysCache[cacheId], key1);
228 : : }
229 : :
230 : : HeapTuple
76 michael@paquier.xyz 231 :GNC 4221914 : SearchSysCache2(SysCacheIdentifier cacheId,
232 : : Datum key1, Datum key2)
233 : : {
223 peter@eisentraut.org 234 [ + - + - : 4221914 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
- + ]
3126 andres@anarazel.de 235 [ - + ]:CBC 4221914 : Assert(SysCache[cacheId]->cc_nkeys == 2);
236 : :
237 : 4221914 : return SearchCatCache2(SysCache[cacheId], key1, key2);
238 : : }
239 : :
240 : : HeapTuple
76 michael@paquier.xyz 241 :GNC 4619068 : SearchSysCache3(SysCacheIdentifier cacheId,
242 : : Datum key1, Datum key2, Datum key3)
243 : : {
223 peter@eisentraut.org 244 [ + - + - : 4619068 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
- + ]
3126 andres@anarazel.de 245 [ - + ]:CBC 4619068 : Assert(SysCache[cacheId]->cc_nkeys == 3);
246 : :
247 : 4619068 : return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
248 : : }
249 : :
250 : : HeapTuple
76 michael@paquier.xyz 251 :GNC 3571653 : SearchSysCache4(SysCacheIdentifier cacheId,
252 : : Datum key1, Datum key2, Datum key3, Datum key4)
253 : : {
223 peter@eisentraut.org 254 [ + - + - : 3571653 : Assert(cacheId >= 0 && cacheId < SysCacheSize && SysCache[cacheId]);
- + ]
3126 andres@anarazel.de 255 [ - + ]:CBC 3571653 : Assert(SysCache[cacheId]->cc_nkeys == 4);
256 : :
257 : 3571653 : 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
9301 tgl@sss.pgh.pa.us 265 : 61539338 : ReleaseSysCache(HeapTuple tuple)
266 : : {
267 : 61539338 : ReleaseCatCache(tuple);
268 : 61539338 : }
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
76 michael@paquier.xyz 283 :GNC 24482 : SearchSysCacheLocked1(SysCacheIdentifier cacheId,
284 : : Datum key1)
285 : : {
526 noah@leadboat.com 286 :CBC 24482 : 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 : : */
588 317 : 24482 : ItemPointerSetInvalid(&tid);
318 : : for (;;)
319 : 24483 : {
320 : : HeapTuple tuple;
321 : 48965 : LOCKMODE lockmode = InplaceUpdateTupleLock;
322 : :
323 : 48965 : tuple = SearchSysCache1(cacheId, key1);
324 [ + + ]: 48965 : if (ItemPointerIsValid(&tid))
325 : : {
326 [ + + ]: 24483 : if (!HeapTupleIsValid(tuple))
327 : : {
328 : 1 : LockRelease(&tag, lockmode, false);
329 : 1 : return tuple;
330 : : }
331 [ + + ]: 24482 : if (ItemPointerEquals(&tid, &tuple->t_self))
332 : 24481 : return tuple;
333 : 1 : LockRelease(&tag, lockmode, false);
334 : : }
335 [ - + ]: 24482 : else if (!HeapTupleIsValid(tuple))
588 noah@leadboat.com 336 :UBC 0 : return tuple;
337 : :
588 noah@leadboat.com 338 :CBC 24483 : tid = tuple->t_self;
339 : 24483 : 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 : : */
526 346 [ + + ]: 24483 : SET_LOCKTAG_TUPLE(tag,
347 : : cache->cc_relisshared ? InvalidOid : MyDatabaseId,
348 : : cache->cc_reloid,
349 : : ItemPointerGetBlockNumber(&tid),
350 : : ItemPointerGetOffsetNumber(&tid));
588 351 : 24483 : (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 : 24483 : 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
76 michael@paquier.xyz 375 :GNC 274564 : SearchSysCacheCopy(SysCacheIdentifier cacheId,
376 : : Datum key1,
377 : : Datum key2,
378 : : Datum key3,
379 : : Datum key4)
380 : : {
381 : : HeapTuple tuple,
382 : : newtuple;
383 : :
9301 tgl@sss.pgh.pa.us 384 :CBC 274564 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
385 [ + + ]: 274564 : if (!HeapTupleIsValid(tuple))
386 : 94844 : return tuple;
387 : 179720 : newtuple = heap_copytuple(tuple);
388 : 179720 : ReleaseSysCache(tuple);
389 : 179720 : 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
76 michael@paquier.xyz 400 :GNC 11059 : SearchSysCacheLockedCopy1(SysCacheIdentifier cacheId,
401 : : Datum key1)
402 : : {
403 : : HeapTuple tuple,
404 : : newtuple;
405 : :
588 noah@leadboat.com 406 :CBC 11059 : tuple = SearchSysCacheLocked1(cacheId, key1);
407 [ - + ]: 11059 : if (!HeapTupleIsValid(tuple))
588 noah@leadboat.com 408 :UBC 0 : return tuple;
588 noah@leadboat.com 409 :CBC 11059 : newtuple = heap_copytuple(tuple);
410 : 11059 : ReleaseSysCache(tuple);
411 : 11059 : 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
76 michael@paquier.xyz 421 :GNC 917562 : SearchSysCacheExists(SysCacheIdentifier cacheId,
422 : : Datum key1,
423 : : Datum key2,
424 : : Datum key3,
425 : : Datum key4)
426 : : {
427 : : HeapTuple tuple;
428 : :
9034 tgl@sss.pgh.pa.us 429 :CBC 917562 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
430 [ + + ]: 917562 : if (!HeapTupleIsValid(tuple))
431 : 163126 : return false;
432 : 754436 : ReleaseSysCache(tuple);
433 : 754436 : 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
76 michael@paquier.xyz 444 :GNC 2497907 : 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 : :
9301 tgl@sss.pgh.pa.us 455 :CBC 2497907 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
456 [ + + ]: 2497907 : if (!HeapTupleIsValid(tuple))
457 : 1019955 : return InvalidOid;
270 peter@eisentraut.org 458 :GNC 1477952 : result = DatumGetObjectId(heap_getattr(tuple, oidcol,
459 : 1477952 : SysCache[cacheId]->cc_tupdesc,
460 : : &isNull));
2540 tgl@sss.pgh.pa.us 461 [ - + ]:CBC 1477952 : Assert(!isNull); /* columns used as oids should never be NULL */
9301 462 : 1477952 : ReleaseSysCache(tuple);
463 : 1477952 : 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
8677 476 : 78791 : SearchSysCacheAttName(Oid relid, const char *attname)
477 : : {
478 : : HeapTuple tuple;
479 : :
5924 rhaas@postgresql.org 480 : 78791 : tuple = SearchSysCache2(ATTNAME,
481 : : ObjectIdGetDatum(relid),
482 : : CStringGetDatum(attname));
8677 tgl@sss.pgh.pa.us 483 [ + + ]: 78791 : if (!HeapTupleIsValid(tuple))
484 : 713 : return NULL;
485 [ + + ]: 78078 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
486 : : {
487 : 56 : ReleaseSysCache(tuple);
488 : 56 : return NULL;
489 : : }
490 : 78022 : return tuple;
491 : : }
492 : :
493 : : /*
494 : : * SearchSysCacheCopyAttName
495 : : *
496 : : * As above, an attisdropped-aware version of SearchSysCacheCopy.
497 : : */
498 : : HeapTuple
499 : 7630 : SearchSysCacheCopyAttName(Oid relid, const char *attname)
500 : : {
501 : : HeapTuple tuple,
502 : : newtuple;
503 : :
504 : 7630 : tuple = SearchSysCacheAttName(relid, attname);
505 [ + + ]: 7630 : if (!HeapTupleIsValid(tuple))
506 : 518 : return tuple;
507 : 7112 : newtuple = heap_copytuple(tuple);
508 : 7112 : ReleaseSysCache(tuple);
509 : 7112 : return newtuple;
510 : : }
511 : :
512 : : /*
513 : : * SearchSysCacheExistsAttName
514 : : *
515 : : * As above, an attisdropped-aware version of SearchSysCacheExists.
516 : : */
517 : : bool
518 : 1685 : SearchSysCacheExistsAttName(Oid relid, const char *attname)
519 : : {
520 : : HeapTuple tuple;
521 : :
522 : 1685 : tuple = SearchSysCacheAttName(relid, attname);
523 [ + + ]: 1685 : if (!HeapTupleIsValid(tuple))
524 : 36 : return false;
525 : 1649 : ReleaseSysCache(tuple);
526 : 1649 : 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
3163 simon@2ndQuadrant.co 539 : 1267 : SearchSysCacheAttNum(Oid relid, int16 attnum)
540 : : {
541 : : HeapTuple tuple;
542 : :
543 : 1267 : tuple = SearchSysCache2(ATTNUM,
544 : : ObjectIdGetDatum(relid),
545 : : Int16GetDatum(attnum));
546 [ + + ]: 1267 : if (!HeapTupleIsValid(tuple))
547 : 8 : return NULL;
548 [ - + ]: 1259 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
549 : : {
3163 simon@2ndQuadrant.co 550 :UBC 0 : ReleaseSysCache(tuple);
551 : 0 : return NULL;
552 : : }
3163 simon@2ndQuadrant.co 553 :CBC 1259 : return tuple;
554 : : }
555 : :
556 : : /*
557 : : * SearchSysCacheCopyAttNum
558 : : *
559 : : * As above, an attisdropped-aware version of SearchSysCacheCopy.
560 : : */
561 : : HeapTuple
562 : 1225 : SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
563 : : {
564 : : HeapTuple tuple,
565 : : newtuple;
566 : :
567 : 1225 : tuple = SearchSysCacheAttNum(relid, attnum);
568 [ - + ]: 1225 : if (!HeapTupleIsValid(tuple))
3163 simon@2ndQuadrant.co 569 :UBC 0 : return NULL;
3163 simon@2ndQuadrant.co 570 :CBC 1225 : newtuple = heap_copytuple(tuple);
571 : 1225 : ReleaseSysCache(tuple);
572 : 1225 : 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
76 michael@paquier.xyz 596 :GNC 4866889 : 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 : : */
223 peter@eisentraut.org 606 [ + - + - : 4866889 : if (cacheId < 0 || cacheId >= SysCacheSize || !SysCache[cacheId])
- + ]
5434 peter_e@gmx.net 607 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
223 peter@eisentraut.org 608 [ + + ]:GNC 4866889 : if (!SysCache[cacheId]->cc_tupdesc)
609 : : {
7151 tgl@sss.pgh.pa.us 610 :CBC 17507 : InitCatCachePhase2(SysCache[cacheId], false);
223 peter@eisentraut.org 611 [ - + ]:GNC 17507 : Assert(SysCache[cacheId]->cc_tupdesc);
612 : : }
613 : :
9599 tgl@sss.pgh.pa.us 614 :CBC 9733778 : return heap_getattr(tup, attributeNumber,
615 : 4866889 : 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
76 michael@paquier.xyz 626 :GNC 3016920 : SysCacheGetAttrNotNull(SysCacheIdentifier cacheId, HeapTuple tup,
627 : : AttrNumber attributeNumber)
628 : : {
629 : : bool isnull;
630 : : Datum attr;
631 : :
1137 dgustafsson@postgres 632 :CBC 3016920 : attr = SysCacheGetAttr(cacheId, tup, attributeNumber, &isnull);
633 : :
634 [ - + ]: 3016920 : if (isnull)
635 : : {
1137 dgustafsson@postgres 636 [ # # ]:UBC 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 : :
1137 dgustafsson@postgres 642 :CBC 3016920 : 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
76 michael@paquier.xyz 656 :GNC 781289 : GetSysCacheHashValue(SysCacheIdentifier cacheId,
657 : : Datum key1,
658 : : Datum key2,
659 : : Datum key3,
660 : : Datum key4)
661 : : {
223 peter@eisentraut.org 662 [ + - + - : 781289 : if (cacheId < 0 || cacheId >= SysCacheSize || !SysCache[cacheId])
- + ]
5172 tgl@sss.pgh.pa.us 663 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
664 : :
5172 tgl@sss.pgh.pa.us 665 :CBC 781289 : return GetCatCacheHashValue(SysCache[cacheId], key1, key2, key3, key4);
666 : : }
667 : :
668 : : /*
669 : : * List-search interface
670 : : */
671 : : struct catclist *
76 michael@paquier.xyz 672 :GNC 3033291 : SearchSysCacheList(SysCacheIdentifier cacheId, int nkeys,
673 : : Datum key1, Datum key2, Datum key3)
674 : : {
223 peter@eisentraut.org 675 [ + - + - : 3033291 : if (cacheId < 0 || cacheId >= SysCacheSize || !SysCache[cacheId])
- + ]
5434 peter_e@gmx.net 676 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
677 : :
8795 tgl@sss.pgh.pa.us 678 :CBC 3033291 : 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
76 michael@paquier.xyz 691 :GNC 15945573 : SysCacheInvalidate(SysCacheIdentifier cacheId, uint32 hashValue)
692 : : {
3280 tgl@sss.pgh.pa.us 693 [ + - - + ]:CBC 15945573 : if (cacheId < 0 || cacheId >= SysCacheSize)
3280 tgl@sss.pgh.pa.us 694 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
695 : :
696 : : /* if this cache isn't initialized yet, no need to do anything */
223 peter@eisentraut.org 697 [ - + ]:GNC 15945573 : if (!SysCache[cacheId])
3280 tgl@sss.pgh.pa.us 698 :UBC 0 : return;
699 : :
3280 tgl@sss.pgh.pa.us 700 :CBC 15945573 : 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
4690 rhaas@postgresql.org 715 : 12149241 : RelationInvalidatesSnapshotsOnly(Oid relid)
716 : : {
717 [ + + ]: 12149241 : switch (relid)
718 : : {
719 : 1795980 : case DbRoleSettingRelationId:
720 : : case DependRelationId:
721 : : case SharedDependRelationId:
722 : : case DescriptionRelationId:
723 : : case SharedDescriptionRelationId:
724 : : case SecLabelRelationId:
725 : : case SharedSecLabelRelationId:
726 : 1795980 : return true;
727 : 10353261 : default:
728 : 10353261 : break;
729 : : }
730 : :
731 : 10353261 : return false;
732 : : }
733 : :
734 : : /*
735 : : * Test whether a relation has a system cache.
736 : : */
737 : : bool
738 : 6650281 : RelationHasSysCache(Oid relid)
739 : : {
4382 bruce@momjian.us 740 : 6650281 : int low = 0,
741 : 6650281 : high = SysCacheRelationOidSize - 1;
742 : :
4690 rhaas@postgresql.org 743 [ + + ]: 36821985 : while (low <= high)
744 : : {
4382 bruce@momjian.us 745 : 36486519 : int middle = low + (high - low) / 2;
746 : :
4690 rhaas@postgresql.org 747 [ + + ]: 36486519 : if (SysCacheRelationOid[middle] == relid)
748 : 6314815 : return true;
749 [ + + ]: 30171704 : if (SysCacheRelationOid[middle] < relid)
750 : 11016086 : low = middle + 1;
751 : : else
752 : 19155618 : high = middle - 1;
753 : : }
754 : :
755 : 335466 : 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
3985 tgl@sss.pgh.pa.us 763 : 1590658 : RelationSupportsSysCache(Oid relid)
764 : : {
765 : 1590658 : int low = 0,
766 : 1590658 : high = SysCacheSupportingRelOidSize - 1;
767 : :
768 [ + + ]: 13457080 : while (low <= high)
769 : : {
770 : 12177175 : int middle = low + (high - low) / 2;
771 : :
772 [ + + ]: 12177175 : if (SysCacheSupportingRelOid[middle] == relid)
773 : 310753 : return true;
774 [ + + ]: 11866422 : if (SysCacheSupportingRelOid[middle] < relid)
775 : 10947389 : low = middle + 1;
776 : : else
777 : 919033 : high = middle - 1;
778 : : }
779 : :
780 : 1279905 : return false;
781 : : }
782 : :
783 : :
784 : : /*
785 : : * OID comparator for qsort
786 : : */
787 : : static int
4690 rhaas@postgresql.org 788 : 41046534 : oid_compare(const void *a, const void *b)
789 : : {
3985 tgl@sss.pgh.pa.us 790 : 41046534 : Oid oa = *((const Oid *) a);
791 : 41046534 : Oid ob = *((const Oid *) b);
792 : :
809 nathan@postgresql.or 793 : 41046534 : return pg_cmp_u32(oa, ob);
794 : : }
|