#include "postgres.h"#include <fcntl.h>#include <sys/stat.h>#include <unistd.h>#include "access/xact.h"#include "catalog/catalog.h"#include "catalog/pg_tablespace.h"#include "catalog/storage.h"#include "miscadmin.h"#include "storage/fd.h"#include "storage/lwlock.h"#include "utils/inval.h"#include "utils/relmapper.h"
Go to the source code of this file.
| #define MAX_MAPPINGS 62 |
Definition at line 74 of file relmapper.c.
Referenced by apply_map_update(), load_relmap_file(), and write_relmap_file().
| #define RELMAPPER_FILEMAGIC 0x592717 |
Definition at line 72 of file relmapper.c.
Referenced by load_relmap_file(), RelationMapInvalidate(), and RelationMapInvalidateAll().
| #define RELMAPPER_FILENAME "pg_filenode.map" |
Definition at line 70 of file relmapper.c.
Referenced by load_relmap_file(), and write_relmap_file().
| typedef struct RelMapFile RelMapFile |
| typedef struct RelMapping RelMapping |
| static void apply_map_update | ( | RelMapFile * | map, | |
| Oid | relationId, | |||
| Oid | fileNode, | |||
| bool | add_okay | |||
| ) | [static] |
Definition at line 247 of file relmapper.c.
References elog, ERROR, i, RelMapping::mapfilenode, RelMapping::mapoid, RelMapFile::mappings, MAX_MAPPINGS, and RelMapFile::num_mappings.
Referenced by merge_map_updates(), and RelationMapUpdateMap().
{
int32 i;
/* Replace any existing mapping */
for (i = 0; i < map->num_mappings; i++)
{
if (relationId == map->mappings[i].mapoid)
{
map->mappings[i].mapfilenode = fileNode;
return;
}
}
/* Nope, need to add a new mapping */
if (!add_okay)
elog(ERROR, "attempt to apply a mapping to unmapped relation %u",
relationId);
if (map->num_mappings >= MAX_MAPPINGS)
elog(ERROR, "ran out of space in relation map");
map->mappings[map->num_mappings].mapoid = relationId;
map->mappings[map->num_mappings].mapfilenode = fileNode;
map->num_mappings++;
}
| void AtCCI_RelationMap | ( | void | ) |
Definition at line 367 of file relmapper.c.
References merge_map_updates(), and RelMapFile::num_mappings.
Referenced by AtCCI_LocalCache().
{
if (pending_shared_updates.num_mappings != 0)
{
merge_map_updates(&active_shared_updates,
&pending_shared_updates,
true);
pending_shared_updates.num_mappings = 0;
}
if (pending_local_updates.num_mappings != 0)
{
merge_map_updates(&active_local_updates,
&pending_local_updates,
true);
pending_local_updates.num_mappings = 0;
}
}
| void AtEOXact_RelationMap | ( | bool | isCommit | ) |
Definition at line 401 of file relmapper.c.
References Assert, RelMapFile::num_mappings, and perform_relmap_update().
Referenced by AbortTransaction(), and CommitTransaction().
{
if (isCommit)
{
/*
* We should not get here with any "pending" updates. (We could
* logically choose to treat such as committed, but in the current
* code this should never happen.)
*/
Assert(pending_shared_updates.num_mappings == 0);
Assert(pending_local_updates.num_mappings == 0);
/*
* Write any active updates to the actual map files, then reset them.
*/
if (active_shared_updates.num_mappings != 0)
{
perform_relmap_update(true, &active_shared_updates);
active_shared_updates.num_mappings = 0;
}
if (active_local_updates.num_mappings != 0)
{
perform_relmap_update(false, &active_local_updates);
active_local_updates.num_mappings = 0;
}
}
else
{
/* Abort --- drop all local and pending updates */
active_shared_updates.num_mappings = 0;
active_local_updates.num_mappings = 0;
pending_shared_updates.num_mappings = 0;
pending_local_updates.num_mappings = 0;
}
}
| void AtPrepare_RelationMap | ( | void | ) |
Definition at line 445 of file relmapper.c.
References ereport, errcode(), errmsg(), ERROR, and RelMapFile::num_mappings.
Referenced by PrepareTransaction().
{
if (active_shared_updates.num_mappings != 0 ||
active_local_updates.num_mappings != 0 ||
pending_shared_updates.num_mappings != 0 ||
pending_local_updates.num_mappings != 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot PREPARE a transaction that modified relation mapping")));
}
| void CheckPointRelationMap | ( | void | ) |
Definition at line 468 of file relmapper.c.
References LW_SHARED, LWLockAcquire(), LWLockRelease(), and RelationMappingLock.
Referenced by CheckPointGuts().
| static void load_relmap_file | ( | bool | shared | ) | [static] |
Definition at line 570 of file relmapper.c.
References CloseTransientFile(), COMP_CRC32, DatabasePath, EQ_CRC32, ereport, errcode_for_file_access(), errmsg(), FATAL, FIN_CRC32, INIT_CRC32, RelMapFile::magic, MAX_MAPPINGS, RelMapFile::num_mappings, offsetof, OpenTransientFile(), PG_BINARY, read, RELMAPPER_FILEMAGIC, RELMAPPER_FILENAME, and snprintf().
Referenced by perform_relmap_update(), RelationMapInitializePhase2(), RelationMapInitializePhase3(), RelationMapInvalidate(), and RelationMapInvalidateAll().
{
RelMapFile *map;
char mapfilename[MAXPGPATH];
pg_crc32 crc;
int fd;
if (shared)
{
snprintf(mapfilename, sizeof(mapfilename), "global/%s",
RELMAPPER_FILENAME);
map = &shared_map;
}
else
{
snprintf(mapfilename, sizeof(mapfilename), "%s/%s",
DatabasePath, RELMAPPER_FILENAME);
map = &local_map;
}
/* Read data ... */
fd = OpenTransientFile(mapfilename,
O_RDONLY | PG_BINARY, S_IRUSR | S_IWUSR);
if (fd < 0)
ereport(FATAL,
(errcode_for_file_access(),
errmsg("could not open relation mapping file \"%s\": %m",
mapfilename)));
/*
* Note: we could take RelationMappingLock in shared mode here, but it
* seems unnecessary since our read() should be atomic against any
* concurrent updater's write(). If the file is updated shortly after we
* look, the sinval signaling mechanism will make us re-read it before we
* are able to access any relation that's affected by the change.
*/
if (read(fd, map, sizeof(RelMapFile)) != sizeof(RelMapFile))
ereport(FATAL,
(errcode_for_file_access(),
errmsg("could not read relation mapping file \"%s\": %m",
mapfilename)));
CloseTransientFile(fd);
/* check for correct magic number, etc */
if (map->magic != RELMAPPER_FILEMAGIC ||
map->num_mappings < 0 ||
map->num_mappings > MAX_MAPPINGS)
ereport(FATAL,
(errmsg("relation mapping file \"%s\" contains invalid data",
mapfilename)));
/* verify the CRC */
INIT_CRC32(crc);
COMP_CRC32(crc, (char *) map, offsetof(RelMapFile, crc));
FIN_CRC32(crc);
if (!EQ_CRC32(crc, map->crc))
ereport(FATAL,
(errmsg("relation mapping file \"%s\" contains incorrect checksum",
mapfilename)));
}
| static void merge_map_updates | ( | RelMapFile * | map, | |
| const RelMapFile * | updates, | |||
| bool | add_okay | |||
| ) | [static] |
Definition at line 279 of file relmapper.c.
References apply_map_update(), i, RelMapping::mapfilenode, RelMapping::mapoid, RelMapFile::mappings, and RelMapFile::num_mappings.
Referenced by AtCCI_RelationMap(), and perform_relmap_update().
{
int32 i;
for (i = 0; i < updates->num_mappings; i++)
{
apply_map_update(map,
updates->mappings[i].mapoid,
updates->mappings[i].mapfilenode,
add_okay);
}
}
| static void perform_relmap_update | ( | bool | shared, | |
| const RelMapFile * | updates | |||
| ) | [static] |
Definition at line 807 of file relmapper.c.
References allowSystemTableMods, DatabasePath, GLOBALTABLESPACE_OID, InvalidOid, load_relmap_file(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), merge_map_updates(), MyDatabaseId, MyDatabaseTableSpace, RelationMappingLock, and write_relmap_file().
Referenced by AtEOXact_RelationMap().
{
RelMapFile newmap;
/*
* Anyone updating a relation's mapping info should take exclusive lock on
* that rel and hold it until commit. This ensures that there will not be
* concurrent updates on the same mapping value; but there could easily be
* concurrent updates on different values in the same file. We cover that
* by acquiring the RelationMappingLock, re-reading the target file to
* ensure it's up to date, applying the updates, and writing the data
* before releasing RelationMappingLock.
*
* There is only one RelationMappingLock. In principle we could try to
* have one per mapping file, but it seems unlikely to be worth the
* trouble.
*/
LWLockAcquire(RelationMappingLock, LW_EXCLUSIVE);
/* Be certain we see any other updates just made */
load_relmap_file(shared);
/* Prepare updated data in a local variable */
if (shared)
memcpy(&newmap, &shared_map, sizeof(RelMapFile));
else
memcpy(&newmap, &local_map, sizeof(RelMapFile));
/*
* Apply the updates to newmap. No new mappings should appear, unless
* somebody is adding indexes to system catalogs.
*/
merge_map_updates(&newmap, updates, allowSystemTableMods);
/* Write out the updated map and do other necessary tasks */
write_relmap_file(shared, &newmap, true, true, true,
(shared ? InvalidOid : MyDatabaseId),
(shared ? GLOBALTABLESPACE_OID : MyDatabaseTableSpace),
DatabasePath);
/* Now we can release the lock */
LWLockRelease(RelationMappingLock);
}
| void RelationMapFinishBootstrap | ( | void | ) |
Definition at line 482 of file relmapper.c.
References Assert, DatabasePath, GLOBALTABLESPACE_OID, InvalidOid, IsBootstrapProcessingMode, MyDatabaseId, MyDatabaseTableSpace, NULL, RelMapFile::num_mappings, and write_relmap_file().
Referenced by BootstrapModeMain().
{
Assert(IsBootstrapProcessingMode());
/* Shouldn't be anything "pending" ... */
Assert(active_shared_updates.num_mappings == 0);
Assert(active_local_updates.num_mappings == 0);
Assert(pending_shared_updates.num_mappings == 0);
Assert(pending_local_updates.num_mappings == 0);
/* Write the files; no WAL or sinval needed */
write_relmap_file(true, &shared_map, false, false, false,
InvalidOid, GLOBALTABLESPACE_OID, NULL);
write_relmap_file(false, &local_map, false, false, false,
MyDatabaseId, MyDatabaseTableSpace, DatabasePath);
}
| void RelationMapInitialize | ( | void | ) |
Definition at line 506 of file relmapper.c.
References RelMapFile::magic, and RelMapFile::num_mappings.
Referenced by RelationCacheInitialize().
{
/* The static variables should initialize to zeroes, but let's be sure */
shared_map.magic = 0; /* mark it not loaded */
local_map.magic = 0;
shared_map.num_mappings = 0;
local_map.num_mappings = 0;
active_shared_updates.num_mappings = 0;
active_local_updates.num_mappings = 0;
pending_shared_updates.num_mappings = 0;
pending_local_updates.num_mappings = 0;
}
| void RelationMapInitializePhase2 | ( | void | ) |
Definition at line 526 of file relmapper.c.
References IsBootstrapProcessingMode, and load_relmap_file().
Referenced by RelationCacheInitializePhase2().
{
/*
* In bootstrap mode, the map file isn't there yet, so do nothing.
*/
if (IsBootstrapProcessingMode())
return;
/*
* Load the shared map file, die on error.
*/
load_relmap_file(true);
}
| void RelationMapInitializePhase3 | ( | void | ) |
Definition at line 547 of file relmapper.c.
References IsBootstrapProcessingMode, and load_relmap_file().
Referenced by RelationCacheInitializePhase3().
{
/*
* In bootstrap mode, the map file isn't there yet, so do nothing.
*/
if (IsBootstrapProcessingMode())
return;
/*
* Load the local map file, die on error.
*/
load_relmap_file(false);
}
| void RelationMapInvalidate | ( | bool | shared | ) |
Definition at line 331 of file relmapper.c.
References load_relmap_file(), RelMapFile::magic, and RELMAPPER_FILEMAGIC.
Referenced by LocalExecuteInvalidationMessage().
{
if (shared)
{
if (shared_map.magic == RELMAPPER_FILEMAGIC)
load_relmap_file(true);
}
else
{
if (local_map.magic == RELMAPPER_FILEMAGIC)
load_relmap_file(false);
}
}
| void RelationMapInvalidateAll | ( | void | ) |
Definition at line 353 of file relmapper.c.
References load_relmap_file(), RelMapFile::magic, and RELMAPPER_FILEMAGIC.
Referenced by RelationCacheInvalidate().
{
if (shared_map.magic == RELMAPPER_FILEMAGIC)
load_relmap_file(true);
if (local_map.magic == RELMAPPER_FILEMAGIC)
load_relmap_file(false);
}
Definition at line 143 of file relmapper.c.
References i, RelMapping::mapfilenode, RelMapping::mapoid, RelMapFile::mappings, and RelMapFile::num_mappings.
Referenced by pg_relation_filenode(), pg_relation_filepath(), RelationInitPhysicalAddr(), and swap_relation_files().
{
const RelMapFile *map;
int32 i;
/* If there are active updates, believe those over the main maps */
if (shared)
{
map = &active_shared_updates;
for (i = 0; i < map->num_mappings; i++)
{
if (relationId == map->mappings[i].mapoid)
return map->mappings[i].mapfilenode;
}
map = &shared_map;
for (i = 0; i < map->num_mappings; i++)
{
if (relationId == map->mappings[i].mapoid)
return map->mappings[i].mapfilenode;
}
}
else
{
map = &active_local_updates;
for (i = 0; i < map->num_mappings; i++)
{
if (relationId == map->mappings[i].mapoid)
return map->mappings[i].mapfilenode;
}
map = &local_map;
for (i = 0; i < map->num_mappings; i++)
{
if (relationId == map->mappings[i].mapoid)
return map->mappings[i].mapfilenode;
}
}
return InvalidOid;
}
| void RelationMapRemoveMapping | ( | Oid | relationId | ) |
Definition at line 301 of file relmapper.c.
References elog, ERROR, i, RelMapping::mapoid, RelMapFile::mappings, and RelMapFile::num_mappings.
Referenced by finish_heap_swap().
{
RelMapFile *map = &active_local_updates;
int32 i;
for (i = 0; i < map->num_mappings; i++)
{
if (relationId == map->mappings[i].mapoid)
{
/* Found it, collapse it out */
map->mappings[i] = map->mappings[map->num_mappings - 1];
map->num_mappings--;
return;
}
}
elog(ERROR, "could not find temporary mapping for relation %u",
relationId);
}
Definition at line 192 of file relmapper.c.
References apply_map_update(), elog, ERROR, GetCurrentTransactionNestLevel(), and IsBootstrapProcessingMode.
Referenced by formrdesc(), RelationBuildLocalRelation(), RelationSetNewRelfilenode(), and swap_relation_files().
{
RelMapFile *map;
if (IsBootstrapProcessingMode())
{
/*
* In bootstrap mode, the mapping gets installed in permanent map.
*/
if (shared)
map = &shared_map;
else
map = &local_map;
}
else
{
/*
* We don't currently support map changes within subtransactions. This
* could be done with more bookkeeping infrastructure, but it doesn't
* presently seem worth it.
*/
if (GetCurrentTransactionNestLevel() > 1)
elog(ERROR, "cannot change relation mapping within subtransaction");
if (immediate)
{
/* Make it active, but only locally */
if (shared)
map = &active_shared_updates;
else
map = &active_local_updates;
}
else
{
/* Make it pending */
if (shared)
map = &pending_shared_updates;
else
map = &pending_local_updates;
}
}
apply_map_update(map, relationId, fileNode, true);
}
| void relmap_redo | ( | XLogRecPtr | lsn, | |
| XLogRecord * | record | |||
| ) |
Definition at line 855 of file relmapper.c.
References Assert, xl_relmap_update::data, xl_relmap_update::dbid, elog, GetDatabasePath(), InvalidOid, xl_relmap_update::nbytes, PANIC, pfree(), xl_relmap_update::tsid, write_relmap_file(), XLogRecord::xl_info, XLOG_RELMAP_UPDATE, XLogRecGetData, and XLR_BKP_BLOCK_MASK.
{
uint8 info = record->xl_info & ~XLR_INFO_MASK;
/* Backup blocks are not used in relmap records */
Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
if (info == XLOG_RELMAP_UPDATE)
{
xl_relmap_update *xlrec = (xl_relmap_update *) XLogRecGetData(record);
RelMapFile newmap;
char *dbpath;
if (xlrec->nbytes != sizeof(RelMapFile))
elog(PANIC, "relmap_redo: wrong size %u in relmap update record",
xlrec->nbytes);
memcpy(&newmap, xlrec->data, sizeof(newmap));
/* We need to construct the pathname for this database */
dbpath = GetDatabasePath(xlrec->dbid, xlrec->tsid);
/*
* Write out the new map and send sinval, but of course don't write a
* new WAL entry. There's no surrounding transaction to tell to
* preserve files, either.
*
* There shouldn't be anyone else updating relmaps during WAL replay,
* so we don't bother to take the RelationMappingLock. We would need
* to do so if load_relmap_file needed to interlock against writers.
*/
write_relmap_file((xlrec->dbid == InvalidOid), &newmap,
false, true, false,
xlrec->dbid, xlrec->tsid, dbpath);
pfree(dbpath);
}
else
elog(PANIC, "relmap_redo: unknown op code %u", info);
}
| static void write_relmap_file | ( | bool | shared, | |
| RelMapFile * | newmap, | |||
| bool | write_wal, | |||
| bool | send_sinval, | |||
| bool | preserve_files, | |||
| Oid | dbid, | |||
| Oid | tsid, | |||
| const char * | dbpath | |||
| ) | [static] |
Definition at line 654 of file relmapper.c.
References XLogRecData::buffer, CacheInvalidateRelmap(), CloseTransientFile(), COMP_CRC32, RelMapFile::crc, XLogRecData::data, xl_relmap_update::dbid, RelFileNode::dbNode, elog, END_CRIT_SECTION, ereport, errcode_for_file_access(), errmsg(), ERROR, FIN_CRC32, i, INIT_CRC32, XLogRecData::len, RelMapFile::magic, MAX_MAPPINGS, xl_relmap_update::nbytes, XLogRecData::next, RelMapFile::num_mappings, offsetof, OpenTransientFile(), PG_BINARY, pg_fsync(), RelationPreserveStorage(), RELMAPPER_FILENAME, RelFileNode::relNode, snprintf(), RelFileNode::spcNode, START_CRIT_SECTION, xl_relmap_update::tsid, write, XLOG_RELMAP_UPDATE, XLogFlush(), and XLogInsert().
Referenced by perform_relmap_update(), RelationMapFinishBootstrap(), and relmap_redo().
{
int fd;
RelMapFile *realmap;
char mapfilename[MAXPGPATH];
/*
* Fill in the overhead fields and update CRC.
*/
newmap->magic = RELMAPPER_FILEMAGIC;
if (newmap->num_mappings < 0 || newmap->num_mappings > MAX_MAPPINGS)
elog(ERROR, "attempt to write bogus relation mapping");
INIT_CRC32(newmap->crc);
COMP_CRC32(newmap->crc, (char *) newmap, offsetof(RelMapFile, crc));
FIN_CRC32(newmap->crc);
/*
* Open the target file. We prefer to do this before entering the
* critical section, so that an open() failure need not force PANIC.
*/
if (shared)
{
snprintf(mapfilename, sizeof(mapfilename), "global/%s",
RELMAPPER_FILENAME);
realmap = &shared_map;
}
else
{
snprintf(mapfilename, sizeof(mapfilename), "%s/%s",
dbpath, RELMAPPER_FILENAME);
realmap = &local_map;
}
fd = OpenTransientFile(mapfilename,
O_WRONLY | O_CREAT | PG_BINARY,
S_IRUSR | S_IWUSR);
if (fd < 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not open relation mapping file \"%s\": %m",
mapfilename)));
if (write_wal)
{
xl_relmap_update xlrec;
XLogRecData rdata[2];
XLogRecPtr lsn;
/* now errors are fatal ... */
START_CRIT_SECTION();
xlrec.dbid = dbid;
xlrec.tsid = tsid;
xlrec.nbytes = sizeof(RelMapFile);
rdata[0].data = (char *) (&xlrec);
rdata[0].len = MinSizeOfRelmapUpdate;
rdata[0].buffer = InvalidBuffer;
rdata[0].next = &(rdata[1]);
rdata[1].data = (char *) newmap;
rdata[1].len = sizeof(RelMapFile);
rdata[1].buffer = InvalidBuffer;
rdata[1].next = NULL;
lsn = XLogInsert(RM_RELMAP_ID, XLOG_RELMAP_UPDATE, rdata);
/* As always, WAL must hit the disk before the data update does */
XLogFlush(lsn);
}
errno = 0;
if (write(fd, newmap, sizeof(RelMapFile)) != sizeof(RelMapFile))
{
/* if write didn't set errno, assume problem is no disk space */
if (errno == 0)
errno = ENOSPC;
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not write to relation mapping file \"%s\": %m",
mapfilename)));
}
/*
* We choose to fsync the data to disk before considering the task done.
* It would be possible to relax this if it turns out to be a performance
* issue, but it would complicate checkpointing --- see notes for
* CheckPointRelationMap.
*/
if (pg_fsync(fd) != 0)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not fsync relation mapping file \"%s\": %m",
mapfilename)));
if (CloseTransientFile(fd))
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not close relation mapping file \"%s\": %m",
mapfilename)));
/*
* Now that the file is safely on disk, send sinval message to let other
* backends know to re-read it. We must do this inside the critical
* section: if for some reason we fail to send the message, we have to
* force a database-wide PANIC. Otherwise other backends might continue
* execution with stale mapping information, which would be catastrophic
* as soon as others began to use the now-committed data.
*/
if (send_sinval)
CacheInvalidateRelmap(dbid);
/*
* Make sure that the files listed in the map are not deleted if the outer
* transaction aborts. This had better be within the critical section
* too: it's not likely to fail, but if it did, we'd arrive at transaction
* abort with the files still vulnerable. PANICing will leave things in a
* good state on-disk.
*
* Note: we're cheating a little bit here by assuming that mapped files
* are either in pg_global or the database's default tablespace.
*/
if (preserve_files)
{
int32 i;
for (i = 0; i < newmap->num_mappings; i++)
{
RelFileNode rnode;
rnode.spcNode = tsid;
rnode.dbNode = dbid;
rnode.relNode = newmap->mappings[i].mapfilenode;
RelationPreserveStorage(rnode, false);
}
}
/* Success, update permanent copy */
memcpy(realmap, newmap, sizeof(RelMapFile));
/* Critical section done */
if (write_wal)
END_CRIT_SECTION();
}
RelMapFile active_local_updates [static] |
Definition at line 113 of file relmapper.c.
RelMapFile active_shared_updates [static] |
Definition at line 112 of file relmapper.c.
RelMapFile local_map [static] |
Definition at line 97 of file relmapper.c.
RelMapFile pending_local_updates [static] |
Definition at line 115 of file relmapper.c.
RelMapFile pending_shared_updates [static] |
Definition at line 114 of file relmapper.c.
RelMapFile shared_map [static] |
Definition at line 96 of file relmapper.c.
1.7.1