1/*-------------------------------------------------------------------------
4 * System cache management routines
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/utils/cache/syscache.c
14 * These routines allow the parser/planner/executor to perform
15 * rapid lookups on the contents of the system catalogs.
17 * see utils/syscache.h for a list of the cache IDs
19 *-------------------------------------------------------------------------
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"
41/*---------------------------------------------------------------------------
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
49 (Adding an index requires a catversion.h update, while simply
50 adding/deleting caches only requires a recompile.)
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.
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.
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.
63*---------------------------------------------------------------------------
67 * struct cachedesc: information defining a single syscache
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 */
78/* Macro to provide nkeys and key array with convenient syntax. */
79 #define KEY(...) VA_ARGS_NARGS(__VA_ARGS__), { __VA_ARGS__ }
81#include "catalog/syscache_info.h"
84 "SysCacheSize does not match syscache.c's array");
90/* Sorted array of OIDs of tables that have caches on them */
94/* Sorted array of OIDs of tables and indexes used by caches */
102 * InitCatalogCache - initialize the caches
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
118 for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
121 * Assert that every enumeration value defined in syscache.h has been
122 * populated in the cacheinfo array.
126 /* .nbuckets and .key[] are checked by InitCatCache() */
129 cacheinfo[cacheId].reloid,
130 cacheinfo[cacheId].indoid,
131 cacheinfo[cacheId].nkeys,
132 cacheinfo[cacheId].
key,
133 cacheinfo[cacheId].nbuckets);
135 elog(
ERROR,
"could not initialize cache %u (%d)",
136 cacheinfo[cacheId].reloid, cacheId);
137 /* Accumulate data for OID lists, too */
139 cacheinfo[cacheId].reloid;
141 cacheinfo[cacheId].reloid;
143 cacheinfo[cacheId].indoid;
144 /* see comments for RelationInvalidatesSnapshotsOnly */
151 /* Sort and de-dup OID arrays, so we can use binary search. */
168 * InitCatalogCachePhase2 - finish initializing the caches
170 * Finish initializing all the caches, including necessary database
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
186 for (cacheId = 0; cacheId < SysCacheSize; cacheId++)
194 * A layer on top of SearchCatCache that does the initialization and
195 * key-setting for you.
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!
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.
205 * CAUTION: The tuple that is returned must NOT be freed by the caller!
214 Assert(cacheId >= 0 && cacheId < SysCacheSize &&
SysCache[cacheId]);
223 Assert(cacheId >= 0 && cacheId < SysCacheSize &&
SysCache[cacheId]);
233 Assert(cacheId >= 0 && cacheId < SysCacheSize &&
SysCache[cacheId]);
243 Assert(cacheId >= 0 && cacheId < SysCacheSize &&
SysCache[cacheId]);
253 Assert(cacheId >= 0 && cacheId < SysCacheSize &&
SysCache[cacheId]);
261 * Release previously grabbed reference count on a tuple
270 * SearchSysCacheLocked1
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().
278 * The returned tuple may be the subject of an uncommitted update, so this
279 * doesn't prevent the "tuple concurrently updated" error.
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:
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)
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
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.
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
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().
353 * If an inplace update just finished, ensure we process the syscache
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.
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.
392 * SearchSysCacheLockedCopy1
394 * Meld SearchSysCacheLocked1 with SearchSysCacheCopy(). After the
395 * caller's heap_update(), it should UnlockTuple(InplaceUpdateTupleLock) and
414 * SearchSysCacheExists
416 * A convenience routine that just probes to see if a tuple can be found.
417 * No lock is retained on the syscache entry.
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.
460 Assert(!isNull);
/* columns used as oids should never be NULL */
467 * SearchSysCacheAttName
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.
493 * SearchSysCacheCopyAttName
495 * As above, an attisdropped-aware version of SearchSysCacheCopy.
512 * SearchSysCacheExistsAttName
514 * As above, an attisdropped-aware version of SearchSysCacheExists.
530 * SearchSysCacheAttNum
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.
556 * SearchSysCacheCopyAttNum
558 * As above, an attisdropped-aware version of SearchSysCacheCopy.
578 * Given a tuple previously fetched by SearchSysCache(),
579 * extract a specific attribute.
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/.
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!
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.
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.
605 if (cacheId < 0 || cacheId >= SysCacheSize || !
SysCache[cacheId])
606 elog(
ERROR,
"invalid cache ID: %d", cacheId);
619 * SysCacheGetAttrNotNull
621 * As above, a version of SysCacheGetAttr which knows that the attr cannot
636 "unexpected null value in cached tuple for catalog %s column %s",
645 * GetSysCacheHashValue
647 * Get the hash value that would be used for a tuple in the specified cache
648 * with the given search keys.
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.
661 if (cacheId < 0 || cacheId >= SysCacheSize || !
SysCache[cacheId])
662 elog(
ERROR,
"invalid cache ID: %d", cacheId);
668 * List-search interface
674 if (cacheId < 0 || cacheId >= SysCacheSize || !
SysCache[cacheId])
675 elog(
ERROR,
"invalid cache ID: %d", cacheId);
684 * Invalidate entries in the specified cache, given a hash value.
685 * See CatCacheInvalidate() for more info.
687 * This routine is only quasi-public: it should only be used by inval.c.
692 if (cacheId < 0 || cacheId >= SysCacheSize)
693 elog(
ERROR,
"invalid cache ID: %d", cacheId);
695 /* if this cache isn't initialized yet, no need to do anything */
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.
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.
718 case DbRoleSettingRelationId:
719 case DependRelationId:
720 case SharedDependRelationId:
721 case DescriptionRelationId:
722 case SharedDescriptionRelationId:
723 case SecLabelRelationId:
724 case SharedSecLabelRelationId:
734 * Test whether a relation has a system cache.
744 int middle = low + (high - low) / 2;
758 * Test whether a relation supports a system cache, ie it is either a
759 * cached table or the index used for a cache.
769 int middle = low + (high - low) / 2;
784 * OID comparator for qsort
#define OidIsValid(objectId)
CatCache * InitCatCache(int id, Oid reloid, Oid indexoid, int nkeys, const int *key, int nbuckets)
HeapTuple SearchCatCache2(CatCache *cache, Datum v1, Datum v2)
HeapTuple SearchCatCache3(CatCache *cache, Datum v1, Datum v2, Datum v3)
void InitCatCachePhase2(CatCache *cache, bool touch_index)
CatCList * SearchCatCacheList(CatCache *cache, int nkeys, Datum v1, Datum v2, Datum v3)
uint32 GetCatCacheHashValue(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4)
HeapTuple SearchCatCache4(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4)
void CatCacheInvalidate(CatCache *cache, uint32 hashValue)
HeapTuple SearchCatCache1(CatCache *cache, Datum v1)
void ReleaseCatCache(HeapTuple tuple)
HeapTuple SearchCatCache(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4)
Assert(PointerIsAligned(start, uint64))
HeapTuple heap_copytuple(HeapTuple tuple)
#define HeapTupleIsValid(tuple)
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
static void * GETSTRUCT(const HeapTupleData *tuple)
static int pg_cmp_u32(uint32 a, uint32 b)
void AcceptInvalidationMessages(void)
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
static void ItemPointerSetInvalid(ItemPointerData *pointer)
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
static bool ItemPointerIsValid(const ItemPointerData *pointer)
LockAcquireResult LockAcquire(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock, bool dontWait)
bool LockRelease(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock)
#define SET_LOCKTAG_TUPLE(locktag, dboid, reloid, blocknum, offnum)
#define InplaceUpdateTupleLock
char * get_rel_name(Oid relid)
FormData_pg_attribute * Form_pg_attribute
#define qsort(a, b, c, d)
static Oid DatumGetObjectId(Datum X)
static Datum Int16GetDatum(int16 X)
static Datum ObjectIdGetDatum(Oid X)
static Datum CStringGetDatum(const char *X)
static size_t qunique(void *array, size_t elements, size_t width, int(*compare)(const void *, const void *))
HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname)
void SysCacheInvalidate(int cacheId, uint32 hashValue)
struct catclist * SearchSysCacheList(int cacheId, int nkeys, Datum key1, Datum key2, Datum key3)
bool RelationHasSysCache(Oid relid)
HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
void InitCatalogCache(void)
void ReleaseSysCache(HeapTuple tuple)
static bool CacheInitialized
HeapTuple SearchSysCacheLocked1(int cacheId, Datum key1)
HeapTuple SearchSysCacheLockedCopy1(int cacheId, Datum key1)
static int oid_compare(const void *a, const void *b)
HeapTuple SearchSysCache(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4)
HeapTuple SearchSysCache1(int cacheId, Datum key1)
uint32 GetSysCacheHashValue(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4)
StaticAssertDecl(lengthof(cacheinfo)==SysCacheSize, "SysCacheSize does not match syscache.c's array")
HeapTuple SearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3)
static int SysCacheSupportingRelOidSize
HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum)
bool RelationSupportsSysCache(Oid relid)
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
bool SearchSysCacheExistsAttName(Oid relid, const char *attname)
void InitCatalogCachePhase2(void)
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
bool SearchSysCacheExists(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4)
HeapTuple SearchSysCacheCopy(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4)
static Oid SysCacheRelationOid[SysCacheSize]
static CatCache * SysCache[SysCacheSize]
static Oid SysCacheSupportingRelOid[SysCacheSize *2]
static int SysCacheRelationOidSize
bool RelationInvalidatesSnapshotsOnly(Oid relid)
HeapTuple SearchSysCache4(int cacheId, Datum key1, Datum key2, Datum key3, Datum key4)
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Oid GetSysCacheOid(int cacheId, AttrNumber oidcol, Datum key1, Datum key2, Datum key3, Datum key4)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)