Header And Logo

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

Data Structures | Typedefs | Functions | Variables

xlogutils.c File Reference

#include "postgres.h"
#include "access/xlog.h"
#include "access/xlogutils.h"
#include "catalog/catalog.h"
#include "common/relpath.h"
#include "storage/smgr.h"
#include "utils/guc.h"
#include "utils/hsearch.h"
#include "utils/rel.h"
Include dependency graph for xlogutils.c:

Go to the source code of this file.

Data Structures

struct  xl_invalid_page_key
struct  xl_invalid_page
struct  FakeRelCacheEntryData

Typedefs

typedef struct xl_invalid_page_key xl_invalid_page_key
typedef struct xl_invalid_page xl_invalid_page
typedef FakeRelCacheEntryDataFakeRelCacheEntry

Functions

static void report_invalid_page (int elevel, RelFileNode node, ForkNumber forkno, BlockNumber blkno, bool present)
static void log_invalid_page (RelFileNode node, ForkNumber forkno, BlockNumber blkno, bool present)
static void forget_invalid_pages (RelFileNode node, ForkNumber forkno, BlockNumber minblkno)
static void forget_invalid_pages_db (Oid dbid)
bool XLogHaveInvalidPages (void)
void XLogCheckInvalidPages (void)
Buffer XLogReadBuffer (RelFileNode rnode, BlockNumber blkno, bool init)
Buffer XLogReadBufferExtended (RelFileNode rnode, ForkNumber forknum, BlockNumber blkno, ReadBufferMode mode)
Relation CreateFakeRelcacheEntry (RelFileNode rnode)
void FreeFakeRelcacheEntry (Relation fakerel)
void XLogDropRelation (RelFileNode rnode, ForkNumber forknum)
void XLogDropDatabase (Oid dbid)
void XLogTruncateRelation (RelFileNode rnode, ForkNumber forkNum, BlockNumber nblocks)

Variables

static HTABinvalid_page_tab = NULL

Typedef Documentation

Definition at line 378 of file xlogutils.c.


Function Documentation

Relation CreateFakeRelcacheEntry ( RelFileNode  rnode  ) 

Definition at line 393 of file xlogutils.c.

References Assert, LockRelId::dbId, RelFileNode::dbNode, InRecovery, LockInfoData::lockRelId, palloc0(), FakeRelCacheEntryData::pgc, RelationData::rd_backend, RelationData::rd_lockInfo, RelationData::rd_node, RelationData::rd_rel, RelationData::rd_smgr, RelationGetRelationName, LockRelId::relId, and RelFileNode::relNode.

Referenced by btree_xlog_cleanup(), ginContinueSplit(), heap_xlog_delete(), heap_xlog_insert(), heap_xlog_multi_insert(), heap_xlog_update(), heap_xlog_visible(), and smgr_redo().

{
    FakeRelCacheEntry fakeentry;
    Relation    rel;

    Assert(InRecovery);

    /* Allocate the Relation struct and all related space in one block. */
    fakeentry = palloc0(sizeof(FakeRelCacheEntryData));
    rel = (Relation) fakeentry;

    rel->rd_rel = &fakeentry->pgc;
    rel->rd_node = rnode;
    /* We will never be working with temp rels during recovery */
    rel->rd_backend = InvalidBackendId;

    /* It must be a permanent table if we're in recovery. */
    rel->rd_rel->relpersistence = RELPERSISTENCE_PERMANENT;

    /* We don't know the name of the relation; use relfilenode instead */
    sprintf(RelationGetRelationName(rel), "%u", rnode.relNode);

    /*
     * We set up the lockRelId in case anything tries to lock the dummy
     * relation.  Note that this is fairly bogus since relNode may be
     * different from the relation's OID.  It shouldn't really matter though,
     * since we are presumably running by ourselves and can't have any lock
     * conflicts ...
     */
    rel->rd_lockInfo.lockRelId.dbId = rnode.dbNode;
    rel->rd_lockInfo.lockRelId.relId = rnode.relNode;

    rel->rd_smgr = NULL;

    return rel;
}

static void forget_invalid_pages ( RelFileNode  node,
ForkNumber  forkno,
BlockNumber  minblkno 
) [static]

Definition at line 139 of file xlogutils.c.

References xl_invalid_page_key::blkno, client_min_messages, DEBUG2, elog, ERROR, xl_invalid_page_key::forkno, HASH_REMOVE, hash_search(), hash_seq_init(), hash_seq_search(), xl_invalid_page::key, log_min_messages, xl_invalid_page_key::node, NULL, pfree(), RelFileNodeEquals, and relpathperm.

Referenced by XLogDropRelation(), and XLogTruncateRelation().

{
    HASH_SEQ_STATUS status;
    xl_invalid_page *hentry;

    if (invalid_page_tab == NULL)
        return;                 /* nothing to do */

    hash_seq_init(&status, invalid_page_tab);

    while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL)
    {
        if (RelFileNodeEquals(hentry->key.node, node) &&
            hentry->key.forkno == forkno &&
            hentry->key.blkno >= minblkno)
        {
            if (log_min_messages <= DEBUG2 || client_min_messages <= DEBUG2)
            {
                char       *path = relpathperm(hentry->key.node, forkno);

                elog(DEBUG2, "page %u of relation %s has been dropped",
                     hentry->key.blkno, path);
                pfree(path);
            }

            if (hash_search(invalid_page_tab,
                            (void *) &hentry->key,
                            HASH_REMOVE, NULL) == NULL)
                elog(ERROR, "hash table corrupted");
        }
    }
}

static void forget_invalid_pages_db ( Oid  dbid  )  [static]

Definition at line 174 of file xlogutils.c.

References xl_invalid_page_key::blkno, client_min_messages, RelFileNode::dbNode, DEBUG2, elog, ERROR, xl_invalid_page_key::forkno, HASH_REMOVE, hash_search(), hash_seq_init(), hash_seq_search(), xl_invalid_page::key, log_min_messages, xl_invalid_page_key::node, NULL, pfree(), and relpathperm.

Referenced by XLogDropDatabase().

{
    HASH_SEQ_STATUS status;
    xl_invalid_page *hentry;

    if (invalid_page_tab == NULL)
        return;                 /* nothing to do */

    hash_seq_init(&status, invalid_page_tab);

    while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL)
    {
        if (hentry->key.node.dbNode == dbid)
        {
            if (log_min_messages <= DEBUG2 || client_min_messages <= DEBUG2)
            {
                char       *path = relpathperm(hentry->key.node, hentry->key.forkno);

                elog(DEBUG2, "page %u of relation %s has been dropped",
                     hentry->key.blkno, path);
                pfree(path);
            }

            if (hash_search(invalid_page_tab,
                            (void *) &hentry->key,
                            HASH_REMOVE, NULL) == NULL)
                elog(ERROR, "hash table corrupted");
        }
    }
}

void FreeFakeRelcacheEntry ( Relation  fakerel  ) 
static void log_invalid_page ( RelFileNode  node,
ForkNumber  forkno,
BlockNumber  blkno,
bool  present 
) [static]

Definition at line 74 of file xlogutils.c.

References xl_invalid_page_key::blkno, client_min_messages, DEBUG1, elog, HASHCTL::entrysize, xl_invalid_page_key::forkno, HASHCTL::hash, hash_create(), HASH_ELEM, HASH_FUNCTION, hash_search(), HASHCTL::keysize, log_min_messages, xl_invalid_page_key::node, NULL, PANIC, xl_invalid_page::present, reachedConsistency, report_invalid_page(), and WARNING.

Referenced by XLogReadBufferExtended().

{
    xl_invalid_page_key key;
    xl_invalid_page *hentry;
    bool        found;

    /*
     * Once recovery has reached a consistent state, the invalid-page table
     * should be empty and remain so. If a reference to an invalid page is
     * found after consistency is reached, PANIC immediately. This might seem
     * aggressive, but it's better than letting the invalid reference linger
     * in the hash table until the end of recovery and PANIC there, which
     * might come only much later if this is a standby server.
     */
    if (reachedConsistency)
    {
        report_invalid_page(WARNING, node, forkno, blkno, present);
        elog(PANIC, "WAL contains references to invalid pages");
    }

    /*
     * Log references to invalid pages at DEBUG1 level.  This allows some
     * tracing of the cause (note the elog context mechanism will tell us
     * something about the XLOG record that generated the reference).
     */
    if (log_min_messages <= DEBUG1 || client_min_messages <= DEBUG1)
        report_invalid_page(DEBUG1, node, forkno, blkno, present);

    if (invalid_page_tab == NULL)
    {
        /* create hash table when first needed */
        HASHCTL     ctl;

        memset(&ctl, 0, sizeof(ctl));
        ctl.keysize = sizeof(xl_invalid_page_key);
        ctl.entrysize = sizeof(xl_invalid_page);
        ctl.hash = tag_hash;

        invalid_page_tab = hash_create("XLOG invalid-page table",
                                       100,
                                       &ctl,
                                       HASH_ELEM | HASH_FUNCTION);
    }

    /* we currently assume xl_invalid_page_key contains no padding */
    key.node = node;
    key.forkno = forkno;
    key.blkno = blkno;
    hentry = (xl_invalid_page *)
        hash_search(invalid_page_tab, (void *) &key, HASH_ENTER, &found);

    if (!found)
    {
        /* hash_search already filled in the key */
        hentry->present = present;
    }
    else
    {
        /* repeat reference ... leave "present" as it was */
    }
}

static void report_invalid_page ( int  elevel,
RelFileNode  node,
ForkNumber  forkno,
BlockNumber  blkno,
bool  present 
) [static]

Definition at line 58 of file xlogutils.c.

References elog, pfree(), and relpathperm.

Referenced by log_invalid_page(), and XLogCheckInvalidPages().

{
    char       *path = relpathperm(node, forkno);

    if (present)
        elog(elevel, "page %u of relation %s is uninitialized",
             blkno, path);
    else
        elog(elevel, "page %u of relation %s does not exist",
             blkno, path);
    pfree(path);
}

void XLogCheckInvalidPages ( void   ) 

Definition at line 217 of file xlogutils.c.

References xl_invalid_page_key::blkno, elog, xl_invalid_page_key::forkno, hash_destroy(), hash_seq_init(), hash_seq_search(), xl_invalid_page::key, xl_invalid_page_key::node, NULL, PANIC, xl_invalid_page::present, report_invalid_page(), and WARNING.

Referenced by CheckRecoveryConsistency().

{
    HASH_SEQ_STATUS status;
    xl_invalid_page *hentry;
    bool        foundone = false;

    if (invalid_page_tab == NULL)
        return;                 /* nothing to do */

    hash_seq_init(&status, invalid_page_tab);

    /*
     * Our strategy is to emit WARNING messages for all remaining entries and
     * only PANIC after we've dumped all the available info.
     */
    while ((hentry = (xl_invalid_page *) hash_seq_search(&status)) != NULL)
    {
        report_invalid_page(WARNING, hentry->key.node, hentry->key.forkno,
                            hentry->key.blkno, hentry->present);
        foundone = true;
    }

    if (foundone)
        elog(PANIC, "WAL contains references to invalid pages");

    hash_destroy(invalid_page_tab);
    invalid_page_tab = NULL;
}

void XLogDropDatabase ( Oid  dbid  ) 

Definition at line 457 of file xlogutils.c.

References forget_invalid_pages_db(), and smgrcloseall().

Referenced by dbase_redo().

{
    /*
     * This is unnecessarily heavy-handed, as it will close SMgrRelation
     * objects for other databases as well. DROP DATABASE occurs seldom enough
     * that it's not worth introducing a variant of smgrclose for just this
     * purpose. XXX: Or should we rather leave the smgr entries dangling?
     */
    smgrcloseall();

    forget_invalid_pages_db(dbid);
}

void XLogDropRelation ( RelFileNode  rnode,
ForkNumber  forknum 
)

Definition at line 446 of file xlogutils.c.

References forget_invalid_pages().

Referenced by xact_redo_abort(), and xact_redo_commit_internal().

{
    forget_invalid_pages(rnode, forknum, 0);
}

bool XLogHaveInvalidPages ( void   ) 

Definition at line 207 of file xlogutils.c.

References hash_get_num_entries(), and NULL.

Referenced by RecoveryRestartPoint().

{
    if (invalid_page_tab != NULL &&
        hash_get_num_entries(invalid_page_tab) > 0)
        return true;
    return false;
}

Buffer XLogReadBuffer ( RelFileNode  rnode,
BlockNumber  blkno,
bool  init 
)
Buffer XLogReadBufferExtended ( RelFileNode  rnode,
ForkNumber  forknum,
BlockNumber  blkno,
ReadBufferMode  mode 
)

Definition at line 293 of file xlogutils.c.

References Assert, BufferGetBlockNumber(), BufferGetPage, InRecovery, InvalidBackendId, InvalidBuffer, log_invalid_page(), NULL, P_NEW, PageIsNew, RBM_NORMAL, ReadBufferWithoutRelcache(), ReleaseBuffer(), smgrcreate(), smgrnblocks(), and smgropen().

Referenced by btree_xlog_vacuum(), heap_xlog_clean(), heap_xlog_newpage(), heap_xlog_visible(), RestoreBackupBlockContents(), XLogReadBuffer(), and XLogRecordPageWithFreeSpace().

{
    BlockNumber lastblock;
    Buffer      buffer;
    SMgrRelation smgr;

    Assert(blkno != P_NEW);

    /* Open the relation at smgr level */
    smgr = smgropen(rnode, InvalidBackendId);

    /*
     * Create the target file if it doesn't already exist.  This lets us cope
     * if the replay sequence contains writes to a relation that is later
     * deleted.  (The original coding of this routine would instead suppress
     * the writes, but that seems like it risks losing valuable data if the
     * filesystem loses an inode during a crash.  Better to write the data
     * until we are actually told to delete the file.)
     */
    smgrcreate(smgr, forknum, true);

    lastblock = smgrnblocks(smgr, forknum);

    if (blkno < lastblock)
    {
        /* page exists in file */
        buffer = ReadBufferWithoutRelcache(rnode, forknum, blkno,
                                           mode, NULL);
    }
    else
    {
        /* hm, page doesn't exist in file */
        if (mode == RBM_NORMAL)
        {
            log_invalid_page(rnode, forknum, blkno, false);
            return InvalidBuffer;
        }
        /* OK to extend the file */
        /* we do this in recovery only - no rel-extension lock needed */
        Assert(InRecovery);
        buffer = InvalidBuffer;
        while (blkno >= lastblock)
        {
            if (buffer != InvalidBuffer)
                ReleaseBuffer(buffer);
            buffer = ReadBufferWithoutRelcache(rnode, forknum,
                                               P_NEW, mode, NULL);
            lastblock++;
        }
        Assert(BufferGetBlockNumber(buffer) == blkno);
    }

    if (mode == RBM_NORMAL)
    {
        /* check that page has been initialized */
        Page        page = (Page) BufferGetPage(buffer);

        /*
         * We assume that PageIsNew is safe without a lock. During recovery,
         * there should be no other backends that could modify the buffer at
         * the same time.
         */
        if (PageIsNew(page))
        {
            ReleaseBuffer(buffer);
            log_invalid_page(rnode, forknum, blkno, true);
            return InvalidBuffer;
        }
    }

    return buffer;
}

void XLogTruncateRelation ( RelFileNode  rnode,
ForkNumber  forkNum,
BlockNumber  nblocks 
)

Definition at line 476 of file xlogutils.c.

References forget_invalid_pages().

Referenced by smgr_redo().

{
    forget_invalid_pages(rnode, forkNum, nblocks);
}


Variable Documentation

HTAB* invalid_page_tab = NULL [static]

Definition at line 53 of file xlogutils.c.