Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Typedefs | Functions | Variables

relmapper.c File Reference

#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"
Include dependency graph for relmapper.c:

Go to the source code of this file.

Data Structures

struct  RelMapping
struct  RelMapFile

Defines

#define RELMAPPER_FILENAME   "pg_filenode.map"
#define RELMAPPER_FILEMAGIC   0x592717
#define MAX_MAPPINGS   62

Typedefs

typedef struct RelMapping RelMapping
typedef struct RelMapFile RelMapFile

Functions

static void apply_map_update (RelMapFile *map, Oid relationId, Oid fileNode, bool add_okay)
static void merge_map_updates (RelMapFile *map, const RelMapFile *updates, bool add_okay)
static void load_relmap_file (bool shared)
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 void perform_relmap_update (bool shared, const RelMapFile *updates)
Oid RelationMapOidToFilenode (Oid relationId, bool shared)
void RelationMapUpdateMap (Oid relationId, Oid fileNode, bool shared, bool immediate)
void RelationMapRemoveMapping (Oid relationId)
void RelationMapInvalidate (bool shared)
void RelationMapInvalidateAll (void)
void AtCCI_RelationMap (void)
void AtEOXact_RelationMap (bool isCommit)
void AtPrepare_RelationMap (void)
void CheckPointRelationMap (void)
void RelationMapFinishBootstrap (void)
void RelationMapInitialize (void)
void RelationMapInitializePhase2 (void)
void RelationMapInitializePhase3 (void)
void relmap_redo (XLogRecPtr lsn, XLogRecord *record)

Variables

static RelMapFile shared_map
static RelMapFile local_map
static RelMapFile active_shared_updates
static RelMapFile active_local_updates
static RelMapFile pending_shared_updates
static RelMapFile pending_local_updates

Define Documentation

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

typedef struct RelMapFile RelMapFile
typedef struct RelMapping RelMapping

Function Documentation

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   ) 
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   ) 
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   ) 
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   ) 
Oid RelationMapOidToFilenode ( Oid  relationId,
bool  shared 
)

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);
}

void RelationMapUpdateMap ( Oid  relationId,
Oid  fileNode,
bool  shared,
bool  immediate 
)

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();
}


Variable Documentation

Definition at line 113 of file relmapper.c.

Definition at line 112 of file relmapper.c.

Definition at line 97 of file relmapper.c.

Definition at line 115 of file relmapper.c.

Definition at line 114 of file relmapper.c.

Definition at line 96 of file relmapper.c.