1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
|
/*-------------------------------------------------------------------------
*
* relfilenodemap.c
* relfilenode to oid mapping cache.
*
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/utils/cache/relfilenode.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/htup_details.h"
#include "catalog/indexing.h"
#include "catalog/pg_class.h"
#include "catalog/pg_tablespace.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/hsearch.h"
#include "utils/inval.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "utils/relfilenodemap.h"
#include "utils/relmapper.h"
/* Hash table for informations about each relfilenode <-> oid pair */
static HTAB *RelfilenodeMapHash = NULL;
/* built first time through in InitializeRelfilenodeMap */
ScanKeyData relfilenode_skey[2];
typedef struct
{
Oid reltablespace;
Oid relfilenode;
} RelfilenodeMapKey;
typedef struct
{
RelfilenodeMapKey key; /* lookup key - must be first */
Oid relid; /* pg_class.oid */
} RelfilenodeMapEntry;
/*
* RelfilenodeMapInvalidateCallback
* Flush mapping entries when pg_class is updated in a relevant fashion.
*/
static void
RelfilenodeMapInvalidateCallback(Datum arg, Oid relid)
{
HASH_SEQ_STATUS status;
RelfilenodeMapEntry *entry;
/* nothing to do if not active or deleted */
if (RelfilenodeMapHash == NULL)
return;
/* if relid is InvalidOid, we must invalidate the entire cache */
if (relid == InvalidOid)
{
hash_destroy(RelfilenodeMapHash);
RelfilenodeMapHash = NULL;
return;
}
hash_seq_init(&status, RelfilenodeMapHash);
while ((entry = (RelfilenodeMapEntry *) hash_seq_search(&status)) != NULL)
{
/* Same OID may occur in more than one tablespace. */
if (entry->relid == relid)
{
if (hash_search(RelfilenodeMapHash,
(void *) &entry->key,
HASH_REMOVE,
NULL) == NULL)
elog(ERROR, "hash table corrupted");
}
}
}
/*
* RelfilenodeMapInvalidateCallback
* Initialize cache, either on first use or after a reset.
*/
static void
InitializeRelfilenodeMap(void)
{
HASHCTL ctl;
static bool initial_init_done = false;
int i;
/* Make sure we've initialized CacheMemoryContext. */
if (CacheMemoryContext == NULL)
CreateCacheMemoryContext();
/* Initialize the hash table. */
MemSet(&ctl, 0, sizeof(ctl));
ctl.keysize = sizeof(RelfilenodeMapKey);
ctl.entrysize = sizeof(RelfilenodeMapEntry);
ctl.hash = tag_hash;
ctl.hcxt = CacheMemoryContext;
RelfilenodeMapHash =
hash_create("RelfilenodeMap cache", 1024, &ctl,
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
/*
* For complete resets we simply delete the entire hash, but there's no
* need to do the other stuff multiple times. Especially the initialization
* of the relcche invalidation should only be done once.
*/
if (initial_init_done)
return;
/* build skey */
MemSet(&relfilenode_skey, 0, sizeof(relfilenode_skey));
for (i = 0; i < 2; i++)
{
fmgr_info_cxt(F_OIDEQ,
&relfilenode_skey[i].sk_func,
CacheMemoryContext);
relfilenode_skey[i].sk_strategy = BTEqualStrategyNumber;
relfilenode_skey[i].sk_subtype = InvalidOid;
relfilenode_skey[i].sk_collation = InvalidOid;
}
relfilenode_skey[0].sk_attno = Anum_pg_class_reltablespace;
relfilenode_skey[1].sk_attno = Anum_pg_class_relfilenode;
/* Watch for invalidation events. */
CacheRegisterRelcacheCallback(RelfilenodeMapInvalidateCallback,
(Datum) 0);
initial_init_done = true;
}
/*
* Map a relation's (tablespace, filenode) to a relation's oid and cache the
* result.
*
* Returns InvalidOid if no relation matching the criteria could be found.
*/
Oid
RelidByRelfilenode(Oid reltablespace, Oid relfilenode)
{
RelfilenodeMapKey key;
RelfilenodeMapEntry *entry;
bool found;
SysScanDesc scandesc;
Relation relation;
HeapTuple ntp;
ScanKeyData skey[2];
if (RelfilenodeMapHash == NULL)
InitializeRelfilenodeMap();
/* pg_class will show 0 when the value is actually MyDatabaseTableSpace */
if (reltablespace == MyDatabaseTableSpace)
reltablespace = 0;
MemSet(&key, 0, sizeof(key));
key.reltablespace = reltablespace;
key.relfilenode = relfilenode;
/*
* Check cache and enter entry if nothing could be found. Even if no target
* relation can be found later on we store the negative match and return a
* InvalidOid from cache. That's not really necessary for performance since
* querying invalid values isn't supposed to be a frequent thing, but the
* implementation is simpler this way.
*/
entry = hash_search(RelfilenodeMapHash, (void *) &key, HASH_ENTER, &found);
if (found)
return entry->relid;
/* initialize empty/negative cache entry before doing the actual lookup */
entry->relid = InvalidOid;
/* ok, no previous cache entry, do it the hard way */
/* check shared tables */
if (reltablespace == GLOBALTABLESPACE_OID)
{
entry->relid = RelationMapFilenodeToOid(relfilenode, true);
return entry->relid;
}
/* check plain relations by looking in pg_class */
relation = heap_open(RelationRelationId, AccessShareLock);
/* copy scankey to local copy, it will be modified during the scan */
memcpy(skey, relfilenode_skey, sizeof(skey));
/* set scan arguments */
skey[0].sk_argument = ObjectIdGetDatum(reltablespace);
skey[1].sk_argument = ObjectIdGetDatum(relfilenode);
scandesc = systable_beginscan(relation,
ClassTblspcRelfilenodeIndexId,
true,
NULL,
2,
skey);
found = false;
while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
{
bool isnull PG_USED_FOR_ASSERTS_ONLY;
if (found)
elog(ERROR,
"unexpected duplicate for tablespace %u, relfilenode %u",
reltablespace, relfilenode);
found = true;
#ifdef USE_ASSERT_CHECKING
if (assert_enabled)
{
Oid check;
check = fastgetattr(ntp, Anum_pg_class_reltablespace,
RelationGetDescr(relation),
&isnull);
Assert(!isnull && check == reltablespace);
check = fastgetattr(ntp, Anum_pg_class_relfilenode,
RelationGetDescr(relation),
&isnull);
Assert(!isnull && check == relfilenode);
}
#endif
entry->relid = HeapTupleGetOid(ntp);
}
systable_endscan(scandesc);
heap_close(relation, AccessShareLock);
/* check for tables that are mapped but not shared */
if (!found)
entry->relid = RelationMapFilenodeToOid(relfilenode, false);
return entry->relid;
}
|