#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.