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
9060 tgl@sss.pgh.pa.us 110 :CBC 14897 : InitCatalogCache(void)
111 : : {
112 : : int cacheId;
113 : :
114 [ - + ]: 14897 : Assert(!CacheInitialized);
115 : :
3744 116 : 14897 : SysCacheRelationOidSize = SysCacheSupportingRelOidSize = 0;
117 : :
9060 118 [ + + ]: 1281142 : 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 : : */
772 michael@paquier.xyz 124 [ - + ]: 1266245 : Assert(OidIsValid(cacheinfo[cacheId].reloid));
125 [ - + ]: 1266245 : Assert(OidIsValid(cacheinfo[cacheId].indoid));
126 : : /* .nbuckets and .key[] are checked by InitCatCache() */
127 : :
9060 tgl@sss.pgh.pa.us 128 : 2532490 : SysCache[cacheId] = InitCatCache(cacheId,
7450 129 : 1266245 : cacheinfo[cacheId].reloid,
130 : 1266245 : cacheinfo[cacheId].indoid,
9060 131 : 1266245 : cacheinfo[cacheId].nkeys,
7023 132 : 1266245 : cacheinfo[cacheId].key,
133 : 1266245 : cacheinfo[cacheId].nbuckets);
9060 134 [ - + ]: 1266245 : if (!PointerIsValid(SysCache[cacheId]))
7450 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 */
4449 rhaas@postgresql.org 138 :CBC 1266245 : SysCacheRelationOid[SysCacheRelationOidSize++] =
139 : 1266245 : cacheinfo[cacheId].reloid;
3744 tgl@sss.pgh.pa.us 140 : 1266245 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
141 : 1266245 : cacheinfo[cacheId].reloid;
142 : 1266245 : SysCacheSupportingRelOid[SysCacheSupportingRelOidSize++] =
143 : 1266245 : cacheinfo[cacheId].indoid;
144 : : /* see comments for RelationInvalidatesSnapshotsOnly */
4449 rhaas@postgresql.org 145 [ - + ]: 1266245 : Assert(!RelationInvalidatesSnapshotsOnly(cacheinfo[cacheId].reloid));
146 : : }
147 : :
3744 tgl@sss.pgh.pa.us 148 [ - + ]: 14897 : Assert(SysCacheRelationOidSize <= lengthof(SysCacheRelationOid));
149 [ - + ]: 14897 : Assert(SysCacheSupportingRelOidSize <= lengthof(SysCacheSupportingRelOid));
150 : :
151 : : /* Sort and de-dup OID arrays, so we can use binary search. */
568 nathan@postgresql.or 152 : 14897 : qsort(SysCacheRelationOid, SysCacheRelationOidSize,
153 : : sizeof(Oid), oid_compare);
2130 tmunro@postgresql.or 154 : 14897 : SysCacheRelationOidSize =
155 : 14897 : qunique(SysCacheRelationOid, SysCacheRelationOidSize, sizeof(Oid),
156 : : oid_compare);
157 : :
568 nathan@postgresql.or 158 : 14897 : qsort(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
159 : : sizeof(Oid), oid_compare);
2130 tmunro@postgresql.or 160 : 14897 : SysCacheSupportingRelOidSize =
161 : 14897 : qunique(SysCacheSupportingRelOid, SysCacheSupportingRelOidSize,
162 : : sizeof(Oid), oid_compare);
163 : :
9332 inoue@tpf.co.jp 164 : 14897 : CacheInitialized = true;
10651 scrappy@hub.org 165 : 14897 : }
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
8600 tgl@sss.pgh.pa.us 180 : 1514 : InitCatalogCachePhase2(void)
181 : : {
182 : : int cacheId;
183 : :
184 [ - + ]: 1514 : Assert(CacheInitialized);
185 : :
186 [ + + ]: 130189 : for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
6910 187 : 128676 : InitCatCachePhase2(SysCache[cacheId], true);
8600 188 : 1513 : }
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
9060 208 : 2706827 : SearchSysCache(int cacheId,
209 : : Datum key1,
210 : : Datum key2,
211 : : Datum key3,
212 : : Datum key4)
213 : : {
2885 andres@anarazel.de 214 [ + - + - : 2706827 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
- + ]
215 : : PointerIsValid(SysCache[cacheId]));
216 : :
9060 tgl@sss.pgh.pa.us 217 : 2706827 : return SearchCatCache(SysCache[cacheId], key1, key2, key3, key4);
218 : : }
219 : :
220 : : HeapTuple
2885 andres@anarazel.de 221 : 33847673 : SearchSysCache1(int cacheId,
222 : : Datum key1)
223 : : {
224 [ + - + - : 33847673 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
- + ]
225 : : PointerIsValid(SysCache[cacheId]));
226 [ - + ]: 33847673 : Assert(SysCache[cacheId]->cc_nkeys == 1);
227 : :
228 : 33847673 : return SearchCatCache1(SysCache[cacheId], key1);
229 : : }
230 : :
231 : : HeapTuple
232 : 2747719 : SearchSysCache2(int cacheId,
233 : : Datum key1, Datum key2)
234 : : {
235 [ + - + - : 2747719 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
- + ]
236 : : PointerIsValid(SysCache[cacheId]));
237 [ - + ]: 2747719 : Assert(SysCache[cacheId]->cc_nkeys == 2);
238 : :
239 : 2747719 : return SearchCatCache2(SysCache[cacheId], key1, key2);
240 : : }
241 : :
242 : : HeapTuple
243 : 2748622 : SearchSysCache3(int cacheId,
244 : : Datum key1, Datum key2, Datum key3)
245 : : {
246 [ + - + - : 2748622 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
- + ]
247 : : PointerIsValid(SysCache[cacheId]));
248 [ - + ]: 2748622 : Assert(SysCache[cacheId]->cc_nkeys == 3);
249 : :
250 : 2748622 : return SearchCatCache3(SysCache[cacheId], key1, key2, key3);
251 : : }
252 : :
253 : : HeapTuple
254 : 2146626 : SearchSysCache4(int cacheId,
255 : : Datum key1, Datum key2, Datum key3, Datum key4)
256 : : {
257 [ + - + - : 2146626 : Assert(cacheId >= 0 && cacheId < SysCacheSize &&
- + ]
258 : : PointerIsValid(SysCache[cacheId]));
259 [ - + ]: 2146626 : Assert(SysCache[cacheId]->cc_nkeys == 4);
260 : :
261 : 2146626 : 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
9060 tgl@sss.pgh.pa.us 269 : 41255409 : ReleaseSysCache(HeapTuple tuple)
270 : : {
271 : 41255409 : ReleaseCatCache(tuple);
272 : 41255409 : }
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
347 noah@leadboat.com 287 : 23580 : SearchSysCacheLocked1(int cacheId,
288 : : Datum key1)
289 : : {
285 290 : 23580 : CatCache *cache = SysCache[cacheId];
291 : : ItemPointerData tid;
292 : : LOCKTAG tag;
293 : :
294 : : /*----------
295 : : * Since inplace updates may happen just before our LockTuple(), we must
296 : : * return content acquired after LockTuple() of the TID we return. If we
297 : : * just fetched twice instead of looping, the following sequence would
298 : : * defeat our locking:
299 : : *
300 : : * GRANT: SearchSysCache1() = TID (1,5)
301 : : * GRANT: LockTuple(pg_class, (1,5))
302 : : * [no more inplace update of (1,5) until we release the lock]
303 : : * CLUSTER: SearchSysCache1() = TID (1,5)
304 : : * CLUSTER: heap_update() = TID (1,8)
305 : : * CLUSTER: COMMIT
306 : : * GRANT: SearchSysCache1() = TID (1,8)
307 : : * GRANT: return (1,8) from SearchSysCacheLocked1()
308 : : * VACUUM: SearchSysCache1() = TID (1,8)
309 : : * VACUUM: LockTuple(pg_class, (1,8)) # two TIDs now locked for one rel
310 : : * VACUUM: inplace update
311 : : * GRANT: heap_update() = (1,9) # lose inplace update
312 : : *
313 : : * In the happy case, this takes two fetches, one to determine the TID to
314 : : * lock and another to get the content and confirm the TID didn't change.
315 : : *
316 : : * This is valid even if the row gets updated to a new TID, the old TID
317 : : * becomes LP_UNUSED, and the row gets updated back to its old TID. We'd
318 : : * still hold the right LOCKTAG_TUPLE and a copy of the row captured after
319 : : * the LOCKTAG_TUPLE.
320 : : */
347 321 : 23580 : ItemPointerSetInvalid(&tid);
322 : : for (;;)
323 : 23581 : {
324 : : HeapTuple tuple;
325 : 47161 : LOCKMODE lockmode = InplaceUpdateTupleLock;
326 : :
327 : 47161 : tuple = SearchSysCache1(cacheId, key1);
328 [ + + ]: 47161 : if (ItemPointerIsValid(&tid))
329 : : {
330 [ + + ]: 23581 : if (!HeapTupleIsValid(tuple))
331 : : {
332 : 1 : LockRelease(&tag, lockmode, false);
333 : 1 : return tuple;
334 : : }
335 [ + + ]: 23580 : if (ItemPointerEquals(&tid, &tuple->t_self))
336 : 23579 : return tuple;
337 : 1 : LockRelease(&tag, lockmode, false);
338 : : }
339 [ - + ]: 23580 : else if (!HeapTupleIsValid(tuple))
347 noah@leadboat.com 340 :UBC 0 : return tuple;
341 : :
347 noah@leadboat.com 342 :CBC 23581 : tid = tuple->t_self;
343 : 23581 : ReleaseSysCache(tuple);
344 : :
345 : : /*
346 : : * Do like LockTuple(rel, &tid, lockmode). While cc_relisshared won't
347 : : * change from one iteration to another, it may have been a temporary
348 : : * "false" until our first SearchSysCache1().
349 : : */
285 350 [ + + ]: 23581 : SET_LOCKTAG_TUPLE(tag,
351 : : cache->cc_relisshared ? InvalidOid : MyDatabaseId,
352 : : cache->cc_reloid,
353 : : ItemPointerGetBlockNumber(&tid),
354 : : ItemPointerGetOffsetNumber(&tid));
347 355 : 23581 : (void) LockAcquire(&tag, lockmode, false, false);
356 : :
357 : : /*
358 : : * If an inplace update just finished, ensure we process the syscache
359 : : * inval.
360 : : *
361 : : * If a heap_update() call just released its LOCKTAG_TUPLE, we'll
362 : : * probably find the old tuple and reach "tuple concurrently updated".
363 : : * If that heap_update() aborts, our LOCKTAG_TUPLE blocks inplace
364 : : * updates while our caller works.
365 : : */
366 : 23581 : AcceptInvalidationMessages();
367 : : }
368 : : }
369 : :
370 : : /*
371 : : * SearchSysCacheCopy
372 : : *
373 : : * A convenience routine that does SearchSysCache and (if successful)
374 : : * returns a modifiable copy of the syscache entry. The original
375 : : * syscache entry is released before returning. The caller should
376 : : * heap_freetuple() the result when done with it.
377 : : */
378 : : HeapTuple
9060 tgl@sss.pgh.pa.us 379 : 201285 : SearchSysCacheCopy(int cacheId,
380 : : Datum key1,
381 : : Datum key2,
382 : : Datum key3,
383 : : Datum key4)
384 : : {
385 : : HeapTuple tuple,
386 : : newtuple;
387 : :
388 : 201285 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
389 [ + + ]: 201285 : if (!HeapTupleIsValid(tuple))
390 : 67054 : return tuple;
391 : 134231 : newtuple = heap_copytuple(tuple);
392 : 134231 : ReleaseSysCache(tuple);
393 : 134231 : return newtuple;
394 : : }
395 : :
396 : : /*
397 : : * SearchSysCacheLockedCopy1
398 : : *
399 : : * Meld SearchSysCacheLocked1 with SearchSysCacheCopy(). After the
400 : : * caller's heap_update(), it should UnlockTuple(InplaceUpdateTupleLock) and
401 : : * heap_freetuple().
402 : : */
403 : : HeapTuple
347 noah@leadboat.com 404 : 8299 : SearchSysCacheLockedCopy1(int cacheId,
405 : : Datum key1)
406 : : {
407 : : HeapTuple tuple,
408 : : newtuple;
409 : :
410 : 8299 : tuple = SearchSysCacheLocked1(cacheId, key1);
411 [ - + ]: 8299 : if (!HeapTupleIsValid(tuple))
347 noah@leadboat.com 412 :UBC 0 : return tuple;
347 noah@leadboat.com 413 :CBC 8299 : newtuple = heap_copytuple(tuple);
414 : 8299 : ReleaseSysCache(tuple);
415 : 8299 : return newtuple;
416 : : }
417 : :
418 : : /*
419 : : * SearchSysCacheExists
420 : : *
421 : : * A convenience routine that just probes to see if a tuple can be found.
422 : : * No lock is retained on the syscache entry.
423 : : */
424 : : bool
8793 tgl@sss.pgh.pa.us 425 : 622422 : SearchSysCacheExists(int cacheId,
426 : : Datum key1,
427 : : Datum key2,
428 : : Datum key3,
429 : : Datum key4)
430 : : {
431 : : HeapTuple tuple;
432 : :
433 : 622422 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
434 [ + + ]: 622422 : if (!HeapTupleIsValid(tuple))
435 : 116575 : return false;
436 : 505847 : ReleaseSysCache(tuple);
437 : 505847 : return true;
438 : : }
439 : :
440 : : /*
441 : : * GetSysCacheOid
442 : : *
443 : : * A convenience routine that does SearchSysCache and returns the OID in the
444 : : * oidcol column of the found tuple, or InvalidOid if no tuple could be found.
445 : : * No lock is retained on the syscache entry.
446 : : */
447 : : Oid
9060 448 : 1883120 : GetSysCacheOid(int cacheId,
449 : : AttrNumber oidcol,
450 : : Datum key1,
451 : : Datum key2,
452 : : Datum key3,
453 : : Datum key4)
454 : : {
455 : : HeapTuple tuple;
456 : : bool isNull;
457 : : Oid result;
458 : :
459 : 1883120 : tuple = SearchSysCache(cacheId, key1, key2, key3, key4);
460 [ + + ]: 1883120 : if (!HeapTupleIsValid(tuple))
461 : 747253 : return InvalidOid;
29 peter@eisentraut.org 462 :GNC 1135867 : result = DatumGetObjectId(heap_getattr(tuple, oidcol,
463 : 1135867 : SysCache[cacheId]->cc_tupdesc,
464 : : &isNull));
2299 tgl@sss.pgh.pa.us 465 [ - + ]:CBC 1135867 : Assert(!isNull); /* columns used as oids should never be NULL */
9060 466 : 1135867 : ReleaseSysCache(tuple);
467 : 1135867 : return result;
468 : : }
469 : :
470 : :
471 : : /*
472 : : * SearchSysCacheAttName
473 : : *
474 : : * This routine is equivalent to SearchSysCache on the ATTNAME cache,
475 : : * except that it will return NULL if the found attribute is marked
476 : : * attisdropped. This is convenient for callers that want to act as
477 : : * though dropped attributes don't exist.
478 : : */
479 : : HeapTuple
8436 480 : 55586 : SearchSysCacheAttName(Oid relid, const char *attname)
481 : : {
482 : : HeapTuple tuple;
483 : :
5683 rhaas@postgresql.org 484 : 55586 : tuple = SearchSysCache2(ATTNAME,
485 : : ObjectIdGetDatum(relid),
486 : : CStringGetDatum(attname));
8436 tgl@sss.pgh.pa.us 487 [ + + ]: 55586 : if (!HeapTupleIsValid(tuple))
488 : 516 : return NULL;
489 [ + + ]: 55070 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
490 : : {
491 : 42 : ReleaseSysCache(tuple);
492 : 42 : return NULL;
493 : : }
494 : 55028 : return tuple;
495 : : }
496 : :
497 : : /*
498 : : * SearchSysCacheCopyAttName
499 : : *
500 : : * As above, an attisdropped-aware version of SearchSysCacheCopy.
501 : : */
502 : : HeapTuple
503 : 4760 : SearchSysCacheCopyAttName(Oid relid, const char *attname)
504 : : {
505 : : HeapTuple tuple,
506 : : newtuple;
507 : :
508 : 4760 : tuple = SearchSysCacheAttName(relid, attname);
509 [ + + ]: 4760 : if (!HeapTupleIsValid(tuple))
510 : 372 : return tuple;
511 : 4388 : newtuple = heap_copytuple(tuple);
512 : 4388 : ReleaseSysCache(tuple);
513 : 4388 : return newtuple;
514 : : }
515 : :
516 : : /*
517 : : * SearchSysCacheExistsAttName
518 : : *
519 : : * As above, an attisdropped-aware version of SearchSysCacheExists.
520 : : */
521 : : bool
522 : 551 : SearchSysCacheExistsAttName(Oid relid, const char *attname)
523 : : {
524 : : HeapTuple tuple;
525 : :
526 : 551 : tuple = SearchSysCacheAttName(relid, attname);
527 [ + + ]: 551 : if (!HeapTupleIsValid(tuple))
528 : 27 : return false;
529 : 524 : ReleaseSysCache(tuple);
530 : 524 : return true;
531 : : }
532 : :
533 : :
534 : : /*
535 : : * SearchSysCacheAttNum
536 : : *
537 : : * This routine is equivalent to SearchSysCache on the ATTNUM cache,
538 : : * except that it will return NULL if the found attribute is marked
539 : : * attisdropped. This is convenient for callers that want to act as
540 : : * though dropped attributes don't exist.
541 : : */
542 : : HeapTuple
2922 simon@2ndQuadrant.co 543 : 941 : SearchSysCacheAttNum(Oid relid, int16 attnum)
544 : : {
545 : : HeapTuple tuple;
546 : :
547 : 941 : tuple = SearchSysCache2(ATTNUM,
548 : : ObjectIdGetDatum(relid),
549 : : Int16GetDatum(attnum));
550 [ + + ]: 941 : if (!HeapTupleIsValid(tuple))
551 : 6 : return NULL;
552 [ - + ]: 935 : if (((Form_pg_attribute) GETSTRUCT(tuple))->attisdropped)
553 : : {
2922 simon@2ndQuadrant.co 554 :UBC 0 : ReleaseSysCache(tuple);
555 : 0 : return NULL;
556 : : }
2922 simon@2ndQuadrant.co 557 :CBC 935 : return tuple;
558 : : }
559 : :
560 : : /*
561 : : * SearchSysCacheCopyAttNum
562 : : *
563 : : * As above, an attisdropped-aware version of SearchSysCacheCopy.
564 : : */
565 : : HeapTuple
566 : 909 : SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
567 : : {
568 : : HeapTuple tuple,
569 : : newtuple;
570 : :
571 : 909 : tuple = SearchSysCacheAttNum(relid, attnum);
572 [ - + ]: 909 : if (!HeapTupleIsValid(tuple))
2922 simon@2ndQuadrant.co 573 :UBC 0 : return NULL;
2922 simon@2ndQuadrant.co 574 :CBC 909 : newtuple = heap_copytuple(tuple);
575 : 909 : ReleaseSysCache(tuple);
576 : 909 : return newtuple;
577 : : }
578 : :
579 : :
580 : : /*
581 : : * SysCacheGetAttr
582 : : *
583 : : * Given a tuple previously fetched by SearchSysCache(),
584 : : * extract a specific attribute.
585 : : *
586 : : * This is equivalent to using heap_getattr() on a tuple fetched
587 : : * from a non-cached relation. Usually, this is only used for attributes
588 : : * that could be NULL or variable length; the fixed-size attributes in
589 : : * a system table are accessed just by mapping the tuple onto the C struct
590 : : * declarations from include/catalog/.
591 : : *
592 : : * As with heap_getattr(), if the attribute is of a pass-by-reference type
593 : : * then a pointer into the tuple data area is returned --- the caller must
594 : : * not modify or pfree the datum!
595 : : *
596 : : * Note: it is legal to use SysCacheGetAttr() with a cacheId referencing
597 : : * a different cache for the same catalog the tuple was fetched from.
598 : : */
599 : : Datum
9358 tgl@sss.pgh.pa.us 600 : 2676952 : SysCacheGetAttr(int cacheId, HeapTuple tup,
601 : : AttrNumber attributeNumber,
602 : : bool *isNull)
603 : : {
604 : : /*
605 : : * We just need to get the TupleDesc out of the cache entry, and then we
606 : : * can apply heap_getattr(). Normally the cache control data is already
607 : : * valid (because the caller recently fetched the tuple via this same
608 : : * cache), but there are cases where we have to initialize the cache here.
609 : : */
6910 610 [ + - + - ]: 2676952 : if (cacheId < 0 || cacheId >= SysCacheSize ||
611 [ - + ]: 2676952 : !PointerIsValid(SysCache[cacheId]))
5193 peter_e@gmx.net 612 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
6910 tgl@sss.pgh.pa.us 613 [ + + ]:CBC 2676952 : if (!PointerIsValid(SysCache[cacheId]->cc_tupdesc))
614 : : {
615 : 14072 : InitCatCachePhase2(SysCache[cacheId], false);
616 [ - + ]: 14072 : Assert(PointerIsValid(SysCache[cacheId]->cc_tupdesc));
617 : : }
618 : :
9358 619 : 5353904 : return heap_getattr(tup, attributeNumber,
620 : 2676952 : SysCache[cacheId]->cc_tupdesc,
621 : : isNull);
622 : : }
623 : :
624 : : /*
625 : : * SysCacheGetAttrNotNull
626 : : *
627 : : * As above, a version of SysCacheGetAttr which knows that the attr cannot
628 : : * be NULL.
629 : : */
630 : : Datum
896 dgustafsson@postgres 631 : 1596309 : SysCacheGetAttrNotNull(int cacheId, HeapTuple tup,
632 : : AttrNumber attributeNumber)
633 : : {
634 : : bool isnull;
635 : : Datum attr;
636 : :
637 : 1596309 : attr = SysCacheGetAttr(cacheId, tup, attributeNumber, &isnull);
638 : :
639 [ - + ]: 1596309 : if (isnull)
640 : : {
896 dgustafsson@postgres 641 [ # # ]:UBC 0 : elog(ERROR,
642 : : "unexpected null value in cached tuple for catalog %s column %s",
643 : : get_rel_name(cacheinfo[cacheId].reloid),
644 : : NameStr(TupleDescAttr(SysCache[cacheId]->cc_tupdesc, attributeNumber - 1)->attname));
645 : : }
646 : :
896 dgustafsson@postgres 647 :CBC 1596309 : return attr;
648 : : }
649 : :
650 : : /*
651 : : * GetSysCacheHashValue
652 : : *
653 : : * Get the hash value that would be used for a tuple in the specified cache
654 : : * with the given search keys.
655 : : *
656 : : * The reason for exposing this as part of the API is that the hash value is
657 : : * exposed in cache invalidation operations, so there are places outside the
658 : : * catcache code that need to be able to compute the hash values.
659 : : */
660 : : uint32
4931 tgl@sss.pgh.pa.us 661 : 538074 : GetSysCacheHashValue(int cacheId,
662 : : Datum key1,
663 : : Datum key2,
664 : : Datum key3,
665 : : Datum key4)
666 : : {
667 [ + - + - ]: 538074 : if (cacheId < 0 || cacheId >= SysCacheSize ||
668 [ - + ]: 538074 : !PointerIsValid(SysCache[cacheId]))
4931 tgl@sss.pgh.pa.us 669 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
670 : :
4931 tgl@sss.pgh.pa.us 671 :CBC 538074 : return GetCatCacheHashValue(SysCache[cacheId], key1, key2, key3, key4);
672 : : }
673 : :
674 : : /*
675 : : * List-search interface
676 : : */
677 : : struct catclist *
8554 678 : 1883908 : SearchSysCacheList(int cacheId, int nkeys,
679 : : Datum key1, Datum key2, Datum key3)
680 : : {
681 [ + - + - ]: 1883908 : if (cacheId < 0 || cacheId >= SysCacheSize ||
8403 bruce@momjian.us 682 [ - + ]: 1883908 : !PointerIsValid(SysCache[cacheId]))
5193 peter_e@gmx.net 683 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
684 : :
8554 tgl@sss.pgh.pa.us 685 :CBC 1883908 : return SearchCatCacheList(SysCache[cacheId], nkeys,
686 : : key1, key2, key3);
687 : : }
688 : :
689 : : /*
690 : : * SysCacheInvalidate
691 : : *
692 : : * Invalidate entries in the specified cache, given a hash value.
693 : : * See CatCacheInvalidate() for more info.
694 : : *
695 : : * This routine is only quasi-public: it should only be used by inval.c.
696 : : */
697 : : void
3039 698 : 11414494 : SysCacheInvalidate(int cacheId, uint32 hashValue)
699 : : {
700 [ + - - + ]: 11414494 : if (cacheId < 0 || cacheId >= SysCacheSize)
3039 tgl@sss.pgh.pa.us 701 [ # # ]:UBC 0 : elog(ERROR, "invalid cache ID: %d", cacheId);
702 : :
703 : : /* if this cache isn't initialized yet, no need to do anything */
3039 tgl@sss.pgh.pa.us 704 [ - + ]:CBC 11414494 : if (!PointerIsValid(SysCache[cacheId]))
3039 tgl@sss.pgh.pa.us 705 :UBC 0 : return;
706 : :
3039 tgl@sss.pgh.pa.us 707 :CBC 11414494 : CatCacheInvalidate(SysCache[cacheId], hashValue);
708 : : }
709 : :
710 : : /*
711 : : * Certain relations that do not have system caches send snapshot invalidation
712 : : * messages in lieu of catcache messages. This is for the benefit of
713 : : * GetCatalogSnapshot(), which can then reuse its existing MVCC snapshot
714 : : * for scanning one of those catalogs, rather than taking a new one, if no
715 : : * invalidation has been received.
716 : : *
717 : : * Relations that have syscaches need not (and must not) be listed here. The
718 : : * catcache invalidation messages will also flush the snapshot. If you add a
719 : : * syscache for one of these relations, remove it from this list.
720 : : */
721 : : bool
4449 rhaas@postgresql.org 722 : 8804210 : RelationInvalidatesSnapshotsOnly(Oid relid)
723 : : {
724 [ + + ]: 8804210 : switch (relid)
725 : : {
726 : 1263535 : case DbRoleSettingRelationId:
727 : : case DependRelationId:
728 : : case SharedDependRelationId:
729 : : case DescriptionRelationId:
730 : : case SharedDescriptionRelationId:
731 : : case SecLabelRelationId:
732 : : case SharedSecLabelRelationId:
733 : 1263535 : return true;
734 : 7540675 : default:
735 : 7540675 : break;
736 : : }
737 : :
738 : 7540675 : return false;
739 : : }
740 : :
741 : : /*
742 : : * Test whether a relation has a system cache.
743 : : */
744 : : bool
745 : 4755452 : RelationHasSysCache(Oid relid)
746 : : {
4141 bruce@momjian.us 747 : 4755452 : int low = 0,
748 : 4755452 : high = SysCacheRelationOidSize - 1;
749 : :
4449 rhaas@postgresql.org 750 [ + + ]: 21086621 : while (low <= high)
751 : : {
4141 bruce@momjian.us 752 : 20856676 : int middle = low + (high - low) / 2;
753 : :
4449 rhaas@postgresql.org 754 [ + + ]: 20856676 : if (SysCacheRelationOid[middle] == relid)
755 : 4525507 : return true;
756 [ + + ]: 16331169 : if (SysCacheRelationOid[middle] < relid)
757 : 5809214 : low = middle + 1;
758 : : else
759 : 10521955 : high = middle - 1;
760 : : }
761 : :
762 : 229945 : return false;
763 : : }
764 : :
765 : : /*
766 : : * Test whether a relation supports a system cache, ie it is either a
767 : : * cached table or the index used for a cache.
768 : : */
769 : : bool
3744 tgl@sss.pgh.pa.us 770 : 1153105 : RelationSupportsSysCache(Oid relid)
771 : : {
772 : 1153105 : int low = 0,
773 : 1153105 : high = SysCacheSupportingRelOidSize - 1;
774 : :
775 [ + + ]: 9698668 : while (low <= high)
776 : : {
777 : 8770719 : int middle = low + (high - low) / 2;
778 : :
779 [ + + ]: 8770719 : if (SysCacheSupportingRelOid[middle] == relid)
780 : 225156 : return true;
781 [ + + ]: 8545563 : if (SysCacheSupportingRelOid[middle] < relid)
782 : 7859403 : low = middle + 1;
783 : : else
784 : 686160 : high = middle - 1;
785 : : }
786 : :
787 : 927949 : return false;
788 : : }
789 : :
790 : :
791 : : /*
792 : : * OID comparator for qsort
793 : : */
794 : : static int
4449 rhaas@postgresql.org 795 : 29451369 : oid_compare(const void *a, const void *b)
796 : : {
3744 tgl@sss.pgh.pa.us 797 : 29451369 : Oid oa = *((const Oid *) a);
798 : 29451369 : Oid ob = *((const Oid *) b);
799 : :
568 nathan@postgresql.or 800 : 29451369 : return pg_cmp_u32(oa, ob);
801 : : }
|