mirror of
https://github.com/postgres/postgres.git
synced 2025-05-28 00:03:23 -04:00
There are still some alignment-related failures in the buildfarm, which might or might not be able to be fixed quickly, but I've also just realized that it increased the size of many WAL records by 4 bytes because a block reference contains a RelFileLocator. The effect of that hasn't been studied or discussed, so revert for now.
245 lines
6.7 KiB
C
245 lines
6.7 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* relfilenumbermap.c
|
|
* relfilenumber to oid mapping cache.
|
|
*
|
|
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/utils/cache/relfilenumbermap.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "access/genam.h"
|
|
#include "access/htup_details.h"
|
|
#include "access/table.h"
|
|
#include "catalog/pg_class.h"
|
|
#include "catalog/pg_tablespace.h"
|
|
#include "miscadmin.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/catcache.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/hsearch.h"
|
|
#include "utils/inval.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/relfilenumbermap.h"
|
|
#include "utils/relmapper.h"
|
|
|
|
/* Hash table for information about each relfilenumber <-> oid pair */
|
|
static HTAB *RelfilenumberMapHash = NULL;
|
|
|
|
/* built first time through in InitializeRelfilenumberMap */
|
|
static ScanKeyData relfilenumber_skey[2];
|
|
|
|
typedef struct
|
|
{
|
|
Oid reltablespace;
|
|
RelFileNumber relfilenumber;
|
|
} RelfilenumberMapKey;
|
|
|
|
typedef struct
|
|
{
|
|
RelfilenumberMapKey key; /* lookup key - must be first */
|
|
Oid relid; /* pg_class.oid */
|
|
} RelfilenumberMapEntry;
|
|
|
|
/*
|
|
* RelfilenumberMapInvalidateCallback
|
|
* Flush mapping entries when pg_class is updated in a relevant fashion.
|
|
*/
|
|
static void
|
|
RelfilenumberMapInvalidateCallback(Datum arg, Oid relid)
|
|
{
|
|
HASH_SEQ_STATUS status;
|
|
RelfilenumberMapEntry *entry;
|
|
|
|
/* callback only gets registered after creating the hash */
|
|
Assert(RelfilenumberMapHash != NULL);
|
|
|
|
hash_seq_init(&status, RelfilenumberMapHash);
|
|
while ((entry = (RelfilenumberMapEntry *) hash_seq_search(&status)) != NULL)
|
|
{
|
|
/*
|
|
* If relid is InvalidOid, signaling a complete reset, we must remove
|
|
* all entries, otherwise just remove the specific relation's entry.
|
|
* Always remove negative cache entries.
|
|
*/
|
|
if (relid == InvalidOid || /* complete reset */
|
|
entry->relid == InvalidOid || /* negative cache entry */
|
|
entry->relid == relid) /* individual flushed relation */
|
|
{
|
|
if (hash_search(RelfilenumberMapHash,
|
|
(void *) &entry->key,
|
|
HASH_REMOVE,
|
|
NULL) == NULL)
|
|
elog(ERROR, "hash table corrupted");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* InitializeRelfilenumberMap
|
|
* Initialize cache, either on first use or after a reset.
|
|
*/
|
|
static void
|
|
InitializeRelfilenumberMap(void)
|
|
{
|
|
HASHCTL ctl;
|
|
int i;
|
|
|
|
/* Make sure we've initialized CacheMemoryContext. */
|
|
if (CacheMemoryContext == NULL)
|
|
CreateCacheMemoryContext();
|
|
|
|
/* build skey */
|
|
MemSet(&relfilenumber_skey, 0, sizeof(relfilenumber_skey));
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
fmgr_info_cxt(F_OIDEQ,
|
|
&relfilenumber_skey[i].sk_func,
|
|
CacheMemoryContext);
|
|
relfilenumber_skey[i].sk_strategy = BTEqualStrategyNumber;
|
|
relfilenumber_skey[i].sk_subtype = InvalidOid;
|
|
relfilenumber_skey[i].sk_collation = InvalidOid;
|
|
}
|
|
|
|
relfilenumber_skey[0].sk_attno = Anum_pg_class_reltablespace;
|
|
relfilenumber_skey[1].sk_attno = Anum_pg_class_relfilenode;
|
|
|
|
/*
|
|
* Only create the RelfilenumberMapHash now, so we don't end up partially
|
|
* initialized when fmgr_info_cxt() above ERRORs out with an out of memory
|
|
* error.
|
|
*/
|
|
ctl.keysize = sizeof(RelfilenumberMapKey);
|
|
ctl.entrysize = sizeof(RelfilenumberMapEntry);
|
|
ctl.hcxt = CacheMemoryContext;
|
|
|
|
RelfilenumberMapHash =
|
|
hash_create("RelfilenumberMap cache", 64, &ctl,
|
|
HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
|
|
|
|
/* Watch for invalidation events. */
|
|
CacheRegisterRelcacheCallback(RelfilenumberMapInvalidateCallback,
|
|
(Datum) 0);
|
|
}
|
|
|
|
/*
|
|
* Map a relation's (tablespace, relfilenumber) to a relation's oid and cache
|
|
* the result.
|
|
*
|
|
* Returns InvalidOid if no relation matching the criteria could be found.
|
|
*/
|
|
Oid
|
|
RelidByRelfilenumber(Oid reltablespace, RelFileNumber relfilenumber)
|
|
{
|
|
RelfilenumberMapKey key;
|
|
RelfilenumberMapEntry *entry;
|
|
bool found;
|
|
SysScanDesc scandesc;
|
|
Relation relation;
|
|
HeapTuple ntp;
|
|
ScanKeyData skey[2];
|
|
Oid relid;
|
|
|
|
if (RelfilenumberMapHash == NULL)
|
|
InitializeRelfilenumberMap();
|
|
|
|
/* 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.relfilenumber = relfilenumber;
|
|
|
|
/*
|
|
* Check cache and return entry if one is 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 it's basically free.
|
|
*/
|
|
entry = hash_search(RelfilenumberMapHash, (void *) &key, HASH_FIND, &found);
|
|
|
|
if (found)
|
|
return entry->relid;
|
|
|
|
/* ok, no previous cache entry, do it the hard way */
|
|
|
|
/* initialize empty/negative cache entry before doing the actual lookups */
|
|
relid = InvalidOid;
|
|
|
|
if (reltablespace == GLOBALTABLESPACE_OID)
|
|
{
|
|
/*
|
|
* Ok, shared table, check relmapper.
|
|
*/
|
|
relid = RelationMapFilenumberToOid(relfilenumber, true);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Not a shared table, could either be a plain relation or a
|
|
* non-shared, nailed one, like e.g. pg_class.
|
|
*/
|
|
|
|
/* check for plain relations by looking in pg_class */
|
|
relation = table_open(RelationRelationId, AccessShareLock);
|
|
|
|
/* copy scankey to local copy, it will be modified during the scan */
|
|
memcpy(skey, relfilenumber_skey, sizeof(skey));
|
|
|
|
/* set scan arguments */
|
|
skey[0].sk_argument = ObjectIdGetDatum(reltablespace);
|
|
skey[1].sk_argument = ObjectIdGetDatum(relfilenumber);
|
|
|
|
scandesc = systable_beginscan(relation,
|
|
ClassTblspcRelfilenodeIndexId,
|
|
true,
|
|
NULL,
|
|
2,
|
|
skey);
|
|
|
|
found = false;
|
|
|
|
while (HeapTupleIsValid(ntp = systable_getnext(scandesc)))
|
|
{
|
|
Form_pg_class classform = (Form_pg_class) GETSTRUCT(ntp);
|
|
|
|
if (found)
|
|
elog(ERROR,
|
|
"unexpected duplicate for tablespace %u, relfilenumber %u",
|
|
reltablespace, relfilenumber);
|
|
found = true;
|
|
|
|
Assert(classform->reltablespace == reltablespace);
|
|
Assert(classform->relfilenode == relfilenumber);
|
|
relid = classform->oid;
|
|
}
|
|
|
|
systable_endscan(scandesc);
|
|
table_close(relation, AccessShareLock);
|
|
|
|
/* check for tables that are mapped but not shared */
|
|
if (!found)
|
|
relid = RelationMapFilenumberToOid(relfilenumber, false);
|
|
}
|
|
|
|
/*
|
|
* Only enter entry into cache now, our opening of pg_class could have
|
|
* caused cache invalidations to be executed which would have deleted a
|
|
* new entry if we had entered it above.
|
|
*/
|
|
entry = hash_search(RelfilenumberMapHash, (void *) &key, HASH_ENTER, &found);
|
|
if (found)
|
|
elog(ERROR, "corrupted hashtable");
|
|
entry->relid = relid;
|
|
|
|
return relid;
|
|
}
|