Age Owner Branch data TLA Line data Source code
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 */
33 : : static ScanKeyData relfilenumber_skey[2];
34 : :
35 : : typedef struct
36 : : {
37 : : Oid reltablespace;
38 : : RelFileNumber relfilenumber;
39 : : } RelfilenumberMapKey;
40 : :
41 : : typedef struct
42 : : {
43 : : RelfilenumberMapKey key; /* lookup key - must be first */
44 : : Oid relid; /* pg_class.oid */
45 : : } RelfilenumberMapEntry;
46 : :
47 : : /*
48 : : * RelfilenumberMapInvalidateCallback
49 : : * Flush mapping entries when pg_class is updated in a relevant fashion.
50 : : */
51 : : static void
648 rhaas@postgresql.org 52 :CBC 18218 : RelfilenumberMapInvalidateCallback(Datum arg, Oid relid)
53 : : {
54 : : HASH_SEQ_STATUS status;
55 : : RelfilenumberMapEntry *entry;
56 : :
57 : : /* callback only gets registered after creating the hash */
58 [ - + ]: 18218 : Assert(RelfilenumberMapHash != NULL);
59 : :
60 : 18218 : hash_seq_init(&status, RelfilenumberMapHash);
61 [ + + ]: 10134866 : 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 : : */
2489 tgl@sss.pgh.pa.us 68 [ + + ]: 10116648 : if (relid == InvalidOid || /* complete reset */
69 [ + + ]: 10116252 : entry->relid == InvalidOid || /* negative cache entry */
70 [ + + ]: 10116199 : entry->relid == relid) /* individual flushed relation */
71 : : {
648 rhaas@postgresql.org 72 [ - + ]: 675 : if (hash_search(RelfilenumberMapHash,
433 peter@eisentraut.org 73 : 675 : &entry->key,
74 : : HASH_REMOVE,
75 : : NULL) == NULL)
3919 rhaas@postgresql.org 76 [ # # ]:UBC 0 : elog(ERROR, "hash table corrupted");
77 : : }
78 : : }
3919 rhaas@postgresql.org 79 :CBC 18218 : }
80 : :
81 : : /*
82 : : * InitializeRelfilenumberMap
83 : : * Initialize cache, either on first use or after a reset.
84 : : */
85 : : static void
648 86 : 200 : InitializeRelfilenumberMap(void)
87 : : {
88 : : HASHCTL ctl;
89 : : int i;
90 : :
91 : : /* Make sure we've initialized CacheMemoryContext. */
3919 92 [ - + ]: 200 : if (CacheMemoryContext == NULL)
3919 rhaas@postgresql.org 93 :UBC 0 : CreateCacheMemoryContext();
94 : :
95 : : /* build skey */
648 rhaas@postgresql.org 96 [ + - + - :CBC 3800 : MemSet(&relfilenumber_skey, 0, sizeof(relfilenumber_skey));
+ - + - +
+ ]
97 : :
564 98 [ + + ]: 600 : for (i = 0; i < 2; i++)
99 : : {
100 : 400 : fmgr_info_cxt(F_OIDEQ,
101 : : &relfilenumber_skey[i].sk_func,
102 : : CacheMemoryContext);
103 : 400 : relfilenumber_skey[i].sk_strategy = BTEqualStrategyNumber;
104 : 400 : relfilenumber_skey[i].sk_subtype = InvalidOid;
105 : 400 : relfilenumber_skey[i].sk_collation = InvalidOid;
106 : : }
107 : :
108 : 200 : relfilenumber_skey[0].sk_attno = Anum_pg_class_reltablespace;
648 109 : 200 : 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 : 200 : ctl.keysize = sizeof(RelfilenumberMapKey);
117 : 200 : ctl.entrysize = sizeof(RelfilenumberMapEntry);
1216 tgl@sss.pgh.pa.us 118 : 200 : ctl.hcxt = CacheMemoryContext;
119 : :
648 rhaas@postgresql.org 120 : 200 : RelfilenumberMapHash =
121 : 200 : hash_create("RelfilenumberMap cache", 64, &ctl,
122 : : HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
123 : :
124 : : /* Watch for invalidation events. */
125 : 200 : CacheRegisterRelcacheCallback(RelfilenumberMapInvalidateCallback,
126 : : (Datum) 0);
3919 127 : 200 : }
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
648 136 : 353010 : RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
137 : : {
138 : : RelfilenumberMapKey key;
139 : : RelfilenumberMapEntry *entry;
140 : : bool found;
141 : : SysScanDesc scandesc;
142 : : Relation relation;
143 : : HeapTuple ntp;
144 : : ScanKeyData skey[2];
145 : : Oid relid;
146 : :
147 [ + + ]: 353010 : if (RelfilenumberMapHash == NULL)
148 : 200 : InitializeRelfilenumberMap();
149 : :
150 : : /* pg_class will show 0 when the value is actually MyDatabaseTableSpace */
3919 151 [ + + ]: 353010 : if (reltablespace == MyDatabaseTableSpace)
152 : 348954 : reltablespace = 0;
153 : :
154 [ + - + - : 706020 : MemSet(&key, 0, sizeof(key));
+ - + - +
+ ]
155 : 353010 : key.reltablespace = reltablespace;
648 156 : 353010 : 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 : : */
433 peter@eisentraut.org 165 : 353010 : entry = hash_search(RelfilenumberMapHash, &key, HASH_FIND, &found);
166 : :
3919 rhaas@postgresql.org 167 [ + + ]: 353010 : if (found)
168 : 347847 : 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 */
3805 173 : 5163 : relid = InvalidOid;
174 : :
3919 175 [ + + ]: 5163 : if (reltablespace == GLOBALTABLESPACE_OID)
176 : : {
177 : : /*
178 : : * Ok, shared table, check relmapper.
179 : : */
648 180 : 159 : 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 */
1910 andres@anarazel.de 190 : 5004 : relation = table_open(RelationRelationId, AccessShareLock);
191 : :
192 : : /* copy scankey to local copy, it will be modified during the scan */
648 rhaas@postgresql.org 193 : 5001 : memcpy(skey, relfilenumber_skey, sizeof(skey));
194 : :
195 : : /* set scan arguments */
3805 196 : 5001 : skey[0].sk_argument = ObjectIdGetDatum(reltablespace);
564 197 : 5001 : skey[1].sk_argument = ObjectIdGetDatum(relfilenumber);
198 : :
3805 199 : 5001 : scandesc = systable_beginscan(relation,
200 : : ClassTblspcRelfilenodeIndexId,
201 : : true,
202 : : NULL,
203 : : 2,
204 : : skey);
205 : :
206 : 5001 : found = false;
207 : :
208 [ + + ]: 9626 : while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
209 : : {
1972 andres@anarazel.de 210 : 4625 : Form_pg_class classform = (Form_pg_class) GETSTRUCT(ntp);
211 : :
3805 rhaas@postgresql.org 212 [ - + ]: 4625 : if (found)
3805 rhaas@postgresql.org 213 [ # # ]:UBC 0 : elog(ERROR,
214 : : "unexpected duplicate for tablespace %u, relfilenumber %u",
215 : : reltablespace, relfilenumber);
3805 rhaas@postgresql.org 216 :CBC 4625 : found = true;
217 : :
1972 andres@anarazel.de 218 [ - + ]: 4625 : Assert(classform->reltablespace == reltablespace);
648 rhaas@postgresql.org 219 [ - + ]: 4625 : Assert(classform->relfilenode == relfilenumber);
1972 andres@anarazel.de 220 : 4625 : relid = classform->oid;
221 : : }
222 : :
3805 rhaas@postgresql.org 223 : 4997 : systable_endscan(scandesc);
1910 andres@anarazel.de 224 : 4997 : table_close(relation, AccessShareLock);
225 : :
226 : : /* check for tables that are mapped but not shared */
3805 rhaas@postgresql.org 227 [ + + ]: 4997 : if (!found)
648 228 : 372 : 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 : : */
433 peter@eisentraut.org 236 : 5156 : entry = hash_search(RelfilenumberMapHash, &key, HASH_ENTER, &found);
3805 rhaas@postgresql.org 237 [ - + ]: 5156 : if (found)
3805 rhaas@postgresql.org 238 [ # # ]:UBC 0 : elog(ERROR, "corrupted hashtable");
3805 rhaas@postgresql.org 239 :CBC 5156 : entry->relid = relid;
240 : :
241 : 5156 : return relid;
242 : : }
|