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