1 /*-------------------------------------------------------------------------
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
10 * src/backend/utils/cache/relfilenumbermap.c
12 *-------------------------------------------------------------------------
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];
38 RelFileNumber relfilenumber
;
39 } RelfilenumberMapKey
;
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.
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
,
76 elog(ERROR
, "hash table corrupted");
82 * InitializeRelfilenumberMap
83 * Initialize cache, either on first use or after a reset.
86 InitializeRelfilenumberMap(void)
91 /* Make sure we've initialized CacheMemoryContext. */
92 if (CacheMemoryContext
== NULL
)
93 CreateCacheMemoryContext();
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
,
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
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
,
130 * Map a relation's (tablespace, relfilenumber) to a relation's oid and cache
133 * Returns InvalidOid if no relation matching the criteria could be found.
136 RelidByRelfilenumber(Oid reltablespace
, RelFileNumber relfilenumber
)
138 RelfilenumberMapKey key
;
139 RelfilenumberMapEntry
*entry
;
141 SysScanDesc scandesc
;
146 if (RelfilenumberMapHash
== NULL
)
147 InitializeRelfilenumberMap();
149 /* pg_class will show 0 when the value is actually MyDatabaseTableSpace */
150 if (reltablespace
== MyDatabaseTableSpace
)
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
);
169 /* ok, no previous cache entry, do it the hard way */
171 /* initialize empty/negative cache entry before doing the actual lookups */
174 if (reltablespace
== GLOBALTABLESPACE_OID
)
177 * Ok, shared table, check relmapper.
179 relid
= RelationMapFilenumberToOid(relfilenumber
, true);
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
,
207 while (HeapTupleIsValid(ntp
= systable_getnext(scandesc
)))
209 Form_pg_class classform
= (Form_pg_class
) GETSTRUCT(ntp
);
213 "unexpected duplicate for tablespace %u, relfilenumber %u",
214 reltablespace
, relfilenumber
);
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 */
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
);
237 elog(ERROR
, "corrupted hashtable");
238 entry
->relid
= relid
;