Clear padding of PgStat_HashKey when handling pgstats entries
[pgsql.git] / src / backend / utils / cache / relfilenumbermap.c
blob8dbccdb551e53fc148da47c6fdfc603e8d2e410a
1 /*-------------------------------------------------------------------------
3 * relfilenumbermap.c
4 * relfilenumber to oid mapping cache.
6 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
9 * IDENTIFICATION
10 * src/backend/utils/cache/relfilenumbermap.c
12 *-------------------------------------------------------------------------
14 #include "postgres.h"
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"
29 /* Hash table for information about each relfilenumber <-> oid pair */
30 static HTAB *RelfilenumberMapHash = NULL;
32 /* built first time through in InitializeRelfilenumberMap */
33 static ScanKeyData relfilenumber_skey[2];
35 typedef struct
37 Oid reltablespace;
38 RelFileNumber relfilenumber;
39 } RelfilenumberMapKey;
41 typedef struct
43 RelfilenumberMapKey key; /* lookup key - must be first */
44 Oid relid; /* pg_class.oid */
45 } RelfilenumberMapEntry;
48 * RelfilenumberMapInvalidateCallback
49 * Flush mapping entries when pg_class is updated in a relevant fashion.
51 static void
52 RelfilenumberMapInvalidateCallback(Datum arg, Oid relid)
54 HASH_SEQ_STATUS status;
55 RelfilenumberMapEntry *entry;
57 /* callback only gets registered after creating the hash */
58 Assert(RelfilenumberMapHash != NULL);
60 hash_seq_init(&status, RelfilenumberMapHash);
61 while ((entry = (RelfilenumberMapEntry *) hash_seq_search(&status)) != NULL)
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.
68 if (relid == InvalidOid || /* complete reset */
69 entry->relid == InvalidOid || /* negative cache entry */
70 entry->relid == relid) /* individual flushed relation */
72 if (hash_search(RelfilenumberMapHash,
73 &entry->key,
74 HASH_REMOVE,
75 NULL) == NULL)
76 elog(ERROR, "hash table corrupted");
82 * InitializeRelfilenumberMap
83 * Initialize cache, either on first use or after a reset.
85 static void
86 InitializeRelfilenumberMap(void)
88 HASHCTL ctl;
89 int i;
91 /* Make sure we've initialized CacheMemoryContext. */
92 if (CacheMemoryContext == NULL)
93 CreateCacheMemoryContext();
95 /* build skey */
96 MemSet(&relfilenumber_skey, 0, sizeof(relfilenumber_skey));
98 for (i = 0; i < 2; i++)
100 fmgr_info_cxt(F_OIDEQ,
101 &relfilenumber_skey[i].sk_func,
102 CacheMemoryContext);
103 relfilenumber_skey[i].sk_strategy = BTEqualStrategyNumber;
104 relfilenumber_skey[i].sk_subtype = InvalidOid;
105 relfilenumber_skey[i].sk_collation = InvalidOid;
108 relfilenumber_skey[0].sk_attno = Anum_pg_class_reltablespace;
109 relfilenumber_skey[1].sk_attno = Anum_pg_class_relfilenode;
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.
116 ctl.keysize = sizeof(RelfilenumberMapKey);
117 ctl.entrysize = sizeof(RelfilenumberMapEntry);
118 ctl.hcxt = CacheMemoryContext;
120 RelfilenumberMapHash =
121 hash_create("RelfilenumberMap cache", 64, &ctl,
122 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
124 /* Watch for invalidation events. */
125 CacheRegisterRelcacheCallback(RelfilenumberMapInvalidateCallback,
126 (Datum) 0);
130 * Map a relation's (tablespace, relfilenumber) to a relation's oid and cache
131 * the result.
133 * Returns InvalidOid if no relation matching the criteria could be found.
136 RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
138 RelfilenumberMapKey key;
139 RelfilenumberMapEntry *entry;
140 bool found;
141 SysScanDesc scandesc;
142 Relation relation;
143 HeapTuple ntp;
144 Oid relid;
146 if (RelfilenumberMapHash == NULL)
147 InitializeRelfilenumberMap();
149 /* pg_class will show 0 when the value is actually MyDatabaseTableSpace */
150 if (reltablespace == MyDatabaseTableSpace)
151 reltablespace = 0;
153 MemSet(&key, 0, sizeof(key));
154 key.reltablespace = reltablespace;
155 key.relfilenumber = relfilenumber;
158 * Check cache and return entry if one is found. Even if no target
159 * relation can be found later on we store the negative match and return a
160 * InvalidOid from cache. That's not really necessary for performance
161 * since querying invalid values isn't supposed to be a frequent thing,
162 * but it's basically free.
164 entry = hash_search(RelfilenumberMapHash, &key, HASH_FIND, &found);
166 if (found)
167 return entry->relid;
169 /* ok, no previous cache entry, do it the hard way */
171 /* initialize empty/negative cache entry before doing the actual lookups */
172 relid = InvalidOid;
174 if (reltablespace == GLOBALTABLESPACE_OID)
177 * Ok, shared table, check relmapper.
179 relid = RelationMapFilenumberToOid(relfilenumber, true);
181 else
183 ScanKeyData skey[2];
186 * Not a shared table, could either be a plain relation or a
187 * non-shared, nailed one, like e.g. pg_class.
190 /* check for plain relations by looking in pg_class */
191 relation = table_open(RelationRelationId, AccessShareLock);
193 /* copy scankey to local copy and set scan arguments */
194 memcpy(skey, relfilenumber_skey, sizeof(skey));
195 skey[0].sk_argument = ObjectIdGetDatum(reltablespace);
196 skey[1].sk_argument = ObjectIdGetDatum(relfilenumber);
198 scandesc = systable_beginscan(relation,
199 ClassTblspcRelfilenodeIndexId,
200 true,
201 NULL,
203 skey);
205 found = false;
207 while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
209 Form_pg_class classform = (Form_pg_class) GETSTRUCT(ntp);
211 if (found)
212 elog(ERROR,
213 "unexpected duplicate for tablespace %u, relfilenumber %u",
214 reltablespace, relfilenumber);
215 found = true;
217 Assert(classform->reltablespace == reltablespace);
218 Assert(classform->relfilenode == relfilenumber);
219 relid = classform->oid;
222 systable_endscan(scandesc);
223 table_close(relation, AccessShareLock);
225 /* check for tables that are mapped but not shared */
226 if (!found)
227 relid = RelationMapFilenumberToOid(relfilenumber, false);
231 * Only enter entry into cache now, our opening of pg_class could have
232 * caused cache invalidations to be executed which would have deleted a
233 * new entry if we had entered it above.
235 entry = hash_search(RelfilenumberMapHash, &key, HASH_ENTER, &found);
236 if (found)
237 elog(ERROR, "corrupted hashtable");
238 entry->relid = relid;
240 return relid;