PostgreSQL Source Code  git master
relfilenumbermap.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * relfilenumbermap.c
4  * relfilenumber to oid mapping cache.
5  *
6  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  * src/backend/utils/cache/relfilenumbermap.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "access/genam.h"
17 #include "access/htup_details.h"
18 #include "access/table.h"
19 #include "catalog/pg_class.h"
20 #include "catalog/pg_tablespace.h"
21 #include "miscadmin.h"
22 #include "utils/catcache.h"
23 #include "utils/fmgroids.h"
24 #include "utils/hsearch.h"
25 #include "utils/inval.h"
26 #include "utils/relfilenumbermap.h"
27 #include "utils/relmapper.h"
28 
29 /* Hash table for information about each relfilenumber <-> oid pair */
30 static HTAB *RelfilenumberMapHash = NULL;
31 
32 /* built first time through in InitializeRelfilenumberMap */
34 
35 typedef struct
36 {
40 
41 typedef struct
42 {
43  RelfilenumberMapKey key; /* lookup key - must be first */
44  Oid relid; /* pg_class.oid */
46 
47 /*
48  * RelfilenumberMapInvalidateCallback
49  * Flush mapping entries when pg_class is updated in a relevant fashion.
50  */
51 static void
53 {
54  HASH_SEQ_STATUS status;
55  RelfilenumberMapEntry *entry;
56 
57  /* callback only gets registered after creating the hash */
59 
61  while ((entry = (RelfilenumberMapEntry *) hash_seq_search(&status)) != NULL)
62  {
63  /*
64  * If relid is InvalidOid, signaling a complete reset, we must remove
65  * all entries, otherwise just remove the specific relation's entry.
66  * Always remove negative cache entries.
67  */
68  if (relid == InvalidOid || /* complete reset */
69  entry->relid == InvalidOid || /* negative cache entry */
70  entry->relid == relid) /* individual flushed relation */
71  {
73  &entry->key,
75  NULL) == NULL)
76  elog(ERROR, "hash table corrupted");
77  }
78  }
79 }
80 
81 /*
82  * InitializeRelfilenumberMap
83  * Initialize cache, either on first use or after a reset.
84  */
85 static void
87 {
88  HASHCTL ctl;
89  int i;
90 
91  /* Make sure we've initialized CacheMemoryContext. */
92  if (CacheMemoryContext == NULL)
94 
95  /* build skey */
97 
98  for (i = 0; i < 2; i++)
99  {
100  fmgr_info_cxt(F_OIDEQ,
101  &relfilenumber_skey[i].sk_func,
106  }
107 
108  relfilenumber_skey[0].sk_attno = Anum_pg_class_reltablespace;
109  relfilenumber_skey[1].sk_attno = Anum_pg_class_relfilenode;
110 
111  /*
112  * Only create the RelfilenumberMapHash now, so we don't end up partially
113  * initialized when fmgr_info_cxt() above ERRORs out with an out of memory
114  * error.
115  */
116  ctl.keysize = sizeof(RelfilenumberMapKey);
117  ctl.entrysize = sizeof(RelfilenumberMapEntry);
118  ctl.hcxt = CacheMemoryContext;
119 
121  hash_create("RelfilenumberMap cache", 64, &ctl,
123 
124  /* Watch for invalidation events. */
126  (Datum) 0);
127 }
128 
129 /*
130  * Map a relation's (tablespace, relfilenumber) to a relation's oid and cache
131  * the result.
132  *
133  * Returns InvalidOid if no relation matching the criteria could be found.
134  */
135 Oid
136 RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
137 {
139  RelfilenumberMapEntry *entry;
140  bool found;
141  SysScanDesc scandesc;
142  Relation relation;
143  HeapTuple ntp;
144  ScanKeyData skey[2];
145  Oid relid;
146 
147  if (RelfilenumberMapHash == NULL)
149 
150  /* pg_class will show 0 when the value is actually MyDatabaseTableSpace */
151  if (reltablespace == MyDatabaseTableSpace)
152  reltablespace = 0;
153 
154  MemSet(&key, 0, sizeof(key));
155  key.reltablespace = reltablespace;
156  key.relfilenumber = relfilenumber;
157 
158  /*
159  * Check cache and return entry if one is found. Even if no target
160  * relation can be found later on we store the negative match and return a
161  * InvalidOid from cache. That's not really necessary for performance
162  * since querying invalid values isn't supposed to be a frequent thing,
163  * but it's basically free.
164  */
165  entry = hash_search(RelfilenumberMapHash, &key, HASH_FIND, &found);
166 
167  if (found)
168  return entry->relid;
169 
170  /* ok, no previous cache entry, do it the hard way */
171 
172  /* initialize empty/negative cache entry before doing the actual lookups */
173  relid = InvalidOid;
174 
175  if (reltablespace == GLOBALTABLESPACE_OID)
176  {
177  /*
178  * Ok, shared table, check relmapper.
179  */
180  relid = RelationMapFilenumberToOid(relfilenumber, true);
181  }
182  else
183  {
184  /*
185  * Not a shared table, could either be a plain relation or a
186  * non-shared, nailed one, like e.g. pg_class.
187  */
188 
189  /* check for plain relations by looking in pg_class */
190  relation = table_open(RelationRelationId, AccessShareLock);
191 
192  /* copy scankey to local copy, it will be modified during the scan */
193  memcpy(skey, relfilenumber_skey, sizeof(skey));
194 
195  /* set scan arguments */
196  skey[0].sk_argument = ObjectIdGetDatum(reltablespace);
197  skey[1].sk_argument = ObjectIdGetDatum(relfilenumber);
198 
199  scandesc = systable_beginscan(relation,
200  ClassTblspcRelfilenodeIndexId,
201  true,
202  NULL,
203  2,
204  skey);
205 
206  found = false;
207 
208  while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
209  {
210  Form_pg_class classform = (Form_pg_class) GETSTRUCT(ntp);
211 
212  if (found)
213  elog(ERROR,
214  "unexpected duplicate for tablespace %u, relfilenumber %u",
215  reltablespace, relfilenumber);
216  found = true;
217 
218  Assert(classform->reltablespace == reltablespace);
219  Assert(classform->relfilenode == relfilenumber);
220  relid = classform->oid;
221  }
222 
223  systable_endscan(scandesc);
224  table_close(relation, AccessShareLock);
225 
226  /* check for tables that are mapped but not shared */
227  if (!found)
228  relid = RelationMapFilenumberToOid(relfilenumber, false);
229  }
230 
231  /*
232  * Only enter entry into cache now, our opening of pg_class could have
233  * caused cache invalidations to be executed which would have deleted a
234  * new entry if we had entered it above.
235  */
236  entry = hash_search(RelfilenumberMapHash, &key, HASH_ENTER, &found);
237  if (found)
238  elog(ERROR, "corrupted hashtable");
239  entry->relid = relid;
240 
241  return relid;
242 }
#define Assert(condition)
Definition: c.h:858
#define MemSet(start, val, len)
Definition: c.h:1020
void CreateCacheMemoryContext(void)
Definition: catcache.c:679
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1395
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1385
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:596
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:503
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:384
Oid MyDatabaseTableSpace
Definition: globals.c:93
@ HASH_FIND
Definition: hsearch.h:113
@ HASH_REMOVE
Definition: hsearch.h:115
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void CacheRegisterRelcacheCallback(RelcacheCallbackFunction func, Datum arg)
Definition: inval.c:1558
int i
Definition: isn.c:73
#define AccessShareLock
Definition: lockdefs.h:36
MemoryContext CacheMemoryContext
Definition: mcxt.c:152
void * arg
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
uintptr_t Datum
Definition: postgres.h:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
tree ctl
Definition: radixtree.h:1847
static ScanKeyData relfilenumber_skey[2]
static void RelfilenumberMapInvalidateCallback(Datum arg, Oid relid)
static void InitializeRelfilenumberMap(void)
static HTAB * RelfilenumberMapHash
Oid RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
Oid RelationMapFilenumberToOid(RelFileNumber filenumber, bool shared)
Definition: relmapper.c:218
Oid RelFileNumber
Definition: relpath.h:25
#define BTEqualStrategyNumber
Definition: stratnum.h:31
Definition: dynahash.c:220
RelfilenumberMapKey key
RelFileNumber relfilenumber
Datum sk_argument
Definition: skey.h:72
Oid sk_subtype
Definition: skey.h:69
Oid sk_collation
Definition: skey.h:70
StrategyNumber sk_strategy
Definition: skey.h:68
AttrNumber sk_attno
Definition: skey.h:67
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40