Header And Logo

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

Data Structures | Typedefs | Functions | Variables

storage.c File Reference

#include "postgres.h"
#include "access/visibilitymap.h"
#include "access/xact.h"
#include "access/xlogutils.h"
#include "catalog/catalog.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "storage/freespace.h"
#include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/rel.h"
Include dependency graph for storage.c:

Go to the source code of this file.

Data Structures

struct  PendingRelDelete

Typedefs

typedef struct PendingRelDelete PendingRelDelete

Functions

void RelationCreateStorage (RelFileNode rnode, char relpersistence)
void log_smgrcreate (RelFileNode *rnode, ForkNumber forkNum)
void RelationDropStorage (Relation rel)
void RelationPreserveStorage (RelFileNode rnode, bool atCommit)
void RelationTruncate (Relation rel, BlockNumber nblocks)
void smgrDoPendingDeletes (bool isCommit)
int smgrGetPendingDeletes (bool forCommit, RelFileNode **ptr)
void PostPrepare_smgr (void)
void AtSubCommit_smgr (void)
void AtSubAbort_smgr (void)
void smgr_redo (XLogRecPtr lsn, XLogRecord *record)

Variables

static PendingRelDeletependingDeletes = NULL

Typedef Documentation


Function Documentation

void AtSubAbort_smgr ( void   ) 

Definition at line 470 of file storage.c.

References smgrDoPendingDeletes().

Referenced by AbortSubTransaction().

void AtSubCommit_smgr ( void   ) 

Definition at line 450 of file storage.c.

References GetCurrentTransactionNestLevel(), PendingRelDelete::nestLevel, and PendingRelDelete::next.

Referenced by CommitSubTransaction().

{
    int         nestLevel = GetCurrentTransactionNestLevel();
    PendingRelDelete *pending;

    for (pending = pendingDeletes; pending != NULL; pending = pending->next)
    {
        if (pending->nestLevel >= nestLevel)
            pending->nestLevel = nestLevel - 1;
    }
}

void log_smgrcreate ( RelFileNode rnode,
ForkNumber  forkNum 
)

Definition at line 123 of file storage.c.

References XLogRecData::buffer, XLogRecData::data, xl_smgr_create::forkNum, XLogRecData::len, XLogRecData::next, xl_smgr_create::rnode, XLOG_SMGR_CREATE, and XLogInsert().

Referenced by heap_create_init_fork(), and RelationCreateStorage().

{
    xl_smgr_create xlrec;
    XLogRecData rdata;

    /*
     * Make an XLOG entry reporting the file creation.
     */
    xlrec.rnode = *rnode;
    xlrec.forkNum = forkNum;

    rdata.data = (char *) &xlrec;
    rdata.len = sizeof(xlrec);
    rdata.buffer = InvalidBuffer;
    rdata.next = NULL;

    XLogInsert(RM_SMGR_ID, XLOG_SMGR_CREATE, &rdata);
}

void PostPrepare_smgr ( void   ) 

Definition at line 429 of file storage.c.

References PendingRelDelete::next, and pfree().

Referenced by PrepareTransaction().

{
    PendingRelDelete *pending;
    PendingRelDelete *next;

    for (pending = pendingDeletes; pending != NULL; pending = next)
    {
        next = pending->next;
        pendingDeletes = next;
        /* must explicitly free the list entry */
        pfree(pending);
    }
}

void RelationCreateStorage ( RelFileNode  rnode,
char  relpersistence 
)

Definition at line 76 of file storage.c.

References PendingRelDelete::atCommit, PendingRelDelete::backend, elog, ERROR, GetCurrentTransactionNestLevel(), log_smgrcreate(), MAIN_FORKNUM, MemoryContextAlloc(), MyBackendId, PendingRelDelete::nestLevel, PendingRelDelete::next, RelFileNodeBackend::node, PendingRelDelete::relnode, RELPERSISTENCE_PERMANENT, RELPERSISTENCE_TEMP, RELPERSISTENCE_UNLOGGED, SMgrRelationData::smgr_rnode, smgrcreate(), smgropen(), and TopMemoryContext.

Referenced by ATExecSetTableSpace(), heap_create(), and RelationSetNewRelfilenode().

{
    PendingRelDelete *pending;
    SMgrRelation srel;
    BackendId   backend;
    bool        needs_wal;

    switch (relpersistence)
    {
        case RELPERSISTENCE_TEMP:
            backend = MyBackendId;
            needs_wal = false;
            break;
        case RELPERSISTENCE_UNLOGGED:
            backend = InvalidBackendId;
            needs_wal = false;
            break;
        case RELPERSISTENCE_PERMANENT:
            backend = InvalidBackendId;
            needs_wal = true;
            break;
        default:
            elog(ERROR, "invalid relpersistence: %c", relpersistence);
            return;             /* placate compiler */
    }

    srel = smgropen(rnode, backend);
    smgrcreate(srel, MAIN_FORKNUM, false);

    if (needs_wal)
        log_smgrcreate(&srel->smgr_rnode.node, MAIN_FORKNUM);

    /* Add the relation to the list of stuff to delete at abort */
    pending = (PendingRelDelete *)
        MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
    pending->relnode = rnode;
    pending->backend = backend;
    pending->atCommit = false;  /* delete if abort */
    pending->nestLevel = GetCurrentTransactionNestLevel();
    pending->next = pendingDeletes;
    pendingDeletes = pending;
}

void RelationDropStorage ( Relation  rel  ) 

Definition at line 147 of file storage.c.

References PendingRelDelete::atCommit, PendingRelDelete::backend, GetCurrentTransactionNestLevel(), MemoryContextAlloc(), PendingRelDelete::nestLevel, PendingRelDelete::next, RelationData::rd_backend, RelationData::rd_node, RelationCloseSmgr, PendingRelDelete::relnode, and TopMemoryContext.

Referenced by ATExecSetTableSpace(), DefineQueryRewrite(), heap_drop_with_catalog(), index_drop(), and RelationSetNewRelfilenode().

{
    PendingRelDelete *pending;

    /* Add the relation to the list of stuff to delete at commit */
    pending = (PendingRelDelete *)
        MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete));
    pending->relnode = rel->rd_node;
    pending->backend = rel->rd_backend;
    pending->atCommit = true;   /* delete if commit */
    pending->nestLevel = GetCurrentTransactionNestLevel();
    pending->next = pendingDeletes;
    pendingDeletes = pending;

    /*
     * NOTE: if the relation was created in this transaction, it will now be
     * present in the pending-delete list twice, once with atCommit true and
     * once with atCommit false.  Hence, it will be physically deleted at end
     * of xact in either case (and the other entry will be ignored by
     * smgrDoPendingDeletes, so no error will occur).  We could instead remove
     * the existing list entry and delete the physical file immediately, but
     * for now I'll keep the logic simple.
     */

    RelationCloseSmgr(rel);
}

void RelationPreserveStorage ( RelFileNode  rnode,
bool  atCommit 
)

Definition at line 192 of file storage.c.

References PendingRelDelete::atCommit, PendingRelDelete::next, pfree(), RelFileNodeEquals, and PendingRelDelete::relnode.

Referenced by ATExecAddIndex(), and write_relmap_file().

{
    PendingRelDelete *pending;
    PendingRelDelete *prev;
    PendingRelDelete *next;

    prev = NULL;
    for (pending = pendingDeletes; pending != NULL; pending = next)
    {
        next = pending->next;
        if (RelFileNodeEquals(rnode, pending->relnode)
            && pending->atCommit == atCommit)
        {
            /* unlink and delete list entry */
            if (prev)
                prev->next = next;
            else
                pendingDeletes = next;
            pfree(pending);
            /* prev does not change */
        }
        else
        {
            /* unrelated entry, don't touch it */
            prev = pending;
        }
    }
}

void RelationTruncate ( Relation  rel,
BlockNumber  nblocks 
)

Definition at line 229 of file storage.c.

References xl_smgr_truncate::blkno, XLogRecData::buffer, XLogRecData::data, FreeSpaceMapTruncateRel(), FSM_FORKNUM, XLogRecData::len, MAIN_FORKNUM, XLogRecData::next, RelationData::rd_node, RelationData::rd_smgr, RelationNeedsWAL, RelationOpenSmgr, xl_smgr_truncate::rnode, SMgrRelationData::smgr_fsm_nblocks, SMgrRelationData::smgr_targblock, SMgrRelationData::smgr_vm_nblocks, smgrexists(), smgrtruncate(), VISIBILITYMAP_FORKNUM, visibilitymap_truncate(), XLOG_SMGR_TRUNCATE, XLogFlush(), and XLogInsert().

Referenced by heap_truncate_one_rel(), lazy_truncate_heap(), RelationTruncateIndexes(), and spgvacuumscan().

{
    bool        fsm;
    bool        vm;

    /* Open it at the smgr level if not already done */
    RelationOpenSmgr(rel);

    /*
     * Make sure smgr_targblock etc aren't pointing somewhere past new end
     */
    rel->rd_smgr->smgr_targblock = InvalidBlockNumber;
    rel->rd_smgr->smgr_fsm_nblocks = InvalidBlockNumber;
    rel->rd_smgr->smgr_vm_nblocks = InvalidBlockNumber;

    /* Truncate the FSM first if it exists */
    fsm = smgrexists(rel->rd_smgr, FSM_FORKNUM);
    if (fsm)
        FreeSpaceMapTruncateRel(rel, nblocks);

    /* Truncate the visibility map too if it exists. */
    vm = smgrexists(rel->rd_smgr, VISIBILITYMAP_FORKNUM);
    if (vm)
        visibilitymap_truncate(rel, nblocks);

    /*
     * We WAL-log the truncation before actually truncating, which means
     * trouble if the truncation fails. If we then crash, the WAL replay
     * likely isn't going to succeed in the truncation either, and cause a
     * PANIC. It's tempting to put a critical section here, but that cure
     * would be worse than the disease. It would turn a usually harmless
     * failure to truncate, that might spell trouble at WAL replay, into a
     * certain PANIC.
     */
    if (RelationNeedsWAL(rel))
    {
        /*
         * Make an XLOG entry reporting the file truncation.
         */
        XLogRecPtr  lsn;
        XLogRecData rdata;
        xl_smgr_truncate xlrec;

        xlrec.blkno = nblocks;
        xlrec.rnode = rel->rd_node;

        rdata.data = (char *) &xlrec;
        rdata.len = sizeof(xlrec);
        rdata.buffer = InvalidBuffer;
        rdata.next = NULL;

        lsn = XLogInsert(RM_SMGR_ID, XLOG_SMGR_TRUNCATE, &rdata);

        /*
         * Flush, because otherwise the truncation of the main relation might
         * hit the disk before the WAL record, and the truncation of the FSM
         * or visibility map. If we crashed during that window, we'd be left
         * with a truncated heap, but the FSM or visibility map would still
         * contain entries for the non-existent heap pages.
         */
        if (fsm || vm)
            XLogFlush(lsn);
    }

    /* Do the real work */
    smgrtruncate(rel->rd_smgr, MAIN_FORKNUM, nblocks);
}

void smgr_redo ( XLogRecPtr  lsn,
XLogRecord record 
)

Definition at line 476 of file storage.c.

References Assert, xl_smgr_truncate::blkno, CreateFakeRelcacheEntry(), elog, xl_smgr_create::forkNum, FreeFakeRelcacheEntry(), FreeSpaceMapTruncateRel(), FSM_FORKNUM, InvalidBackendId, MAIN_FORKNUM, PANIC, xl_smgr_truncate::rnode, xl_smgr_create::rnode, smgrcreate(), smgrexists(), smgropen(), smgrtruncate(), VISIBILITYMAP_FORKNUM, visibilitymap_truncate(), XLogRecord::xl_info, XLOG_SMGR_CREATE, XLOG_SMGR_TRUNCATE, XLogFlush(), XLogRecGetData, XLogTruncateRelation(), and XLR_BKP_BLOCK_MASK.

{
    uint8       info = record->xl_info & ~XLR_INFO_MASK;

    /* Backup blocks are not used in smgr records */
    Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));

    if (info == XLOG_SMGR_CREATE)
    {
        xl_smgr_create *xlrec = (xl_smgr_create *) XLogRecGetData(record);
        SMgrRelation reln;

        reln = smgropen(xlrec->rnode, InvalidBackendId);
        smgrcreate(reln, xlrec->forkNum, true);
    }
    else if (info == XLOG_SMGR_TRUNCATE)
    {
        xl_smgr_truncate *xlrec = (xl_smgr_truncate *) XLogRecGetData(record);
        SMgrRelation reln;
        Relation    rel;

        reln = smgropen(xlrec->rnode, InvalidBackendId);

        /*
         * Forcibly create relation if it doesn't exist (which suggests that
         * it was dropped somewhere later in the WAL sequence).  As in
         * XLogReadBuffer, we prefer to recreate the rel and replay the log as
         * best we can until the drop is seen.
         */
        smgrcreate(reln, MAIN_FORKNUM, true);

        /*
         * Before we perform the truncation, update minimum recovery point
         * to cover this WAL record. Once the relation is truncated, there's
         * no going back. The buffer manager enforces the WAL-first rule
         * for normal updates to relation files, so that the minimum recovery
         * point is always updated before the corresponding change in the
         * data file is flushed to disk. We have to do the same manually
         * here.
         *
         * Doing this before the truncation means that if the truncation fails
         * for some reason, you cannot start up the system even after restart,
         * until you fix the underlying situation so that the truncation will
         * succeed. Alternatively, we could update the minimum recovery point
         * after truncation, but that would leave a small window where the
         * WAL-first rule could be violated.
         */
        XLogFlush(lsn);

        smgrtruncate(reln, MAIN_FORKNUM, xlrec->blkno);

        /* Also tell xlogutils.c about it */
        XLogTruncateRelation(xlrec->rnode, MAIN_FORKNUM, xlrec->blkno);

        /* Truncate FSM and VM too */
        rel = CreateFakeRelcacheEntry(xlrec->rnode);

        if (smgrexists(reln, FSM_FORKNUM))
            FreeSpaceMapTruncateRel(rel, xlrec->blkno);
        if (smgrexists(reln, VISIBILITYMAP_FORKNUM))
            visibilitymap_truncate(rel, xlrec->blkno);

        FreeFakeRelcacheEntry(rel);
    }
    else
        elog(PANIC, "smgr_redo: unknown op code %u", info);
}

void smgrDoPendingDeletes ( bool  isCommit  ) 

Definition at line 309 of file storage.c.

References PendingRelDelete::atCommit, PendingRelDelete::backend, GetCurrentTransactionNestLevel(), i, PendingRelDelete::nestLevel, PendingRelDelete::next, palloc(), pfree(), PendingRelDelete::relnode, repalloc(), smgrclose(), smgrdounlinkall(), and smgropen().

Referenced by AbortTransaction(), AtSubAbort_smgr(), and CommitTransaction().

{
    int         nestLevel = GetCurrentTransactionNestLevel();
    PendingRelDelete *pending;
    PendingRelDelete *prev;
    PendingRelDelete *next;
    int         nrels = 0,
                i = 0,
                maxrels = 8;
    SMgrRelation *srels = palloc(maxrels * sizeof(SMgrRelation));

    prev = NULL;
    for (pending = pendingDeletes; pending != NULL; pending = next)
    {
        next = pending->next;
        if (pending->nestLevel < nestLevel)
        {
            /* outer-level entries should not be processed yet */
            prev = pending;
        }
        else
        {
            /* unlink list entry first, so we don't retry on failure */
            if (prev)
                prev->next = next;
            else
                pendingDeletes = next;
            /* do deletion if called for */
            if (pending->atCommit == isCommit)
            {
                SMgrRelation srel;

                srel = smgropen(pending->relnode, pending->backend);

                /* extend the array if needed (double the size) */
                if (maxrels <= nrels)
                {
                    maxrels *= 2;
                    srels = repalloc(srels, sizeof(SMgrRelation) * maxrels);
                }

                srels[nrels++] = srel;
            }
            /* must explicitly free the list entry */
            pfree(pending);
            /* prev does not change */
        }
    }

    if (nrels > 0)
    {
        smgrdounlinkall(srels, nrels, false);

        for (i = 0; i < nrels; i++)
            smgrclose(srels[i]);
    }

    pfree(srels);

}

int smgrGetPendingDeletes ( bool  forCommit,
RelFileNode **  ptr 
)

Definition at line 388 of file storage.c.

References PendingRelDelete::atCommit, PendingRelDelete::backend, GetCurrentTransactionNestLevel(), InvalidBackendId, PendingRelDelete::nestLevel, PendingRelDelete::next, palloc(), and PendingRelDelete::relnode.

Referenced by RecordTransactionAbort(), RecordTransactionCommit(), and StartPrepare().

{
    int         nestLevel = GetCurrentTransactionNestLevel();
    int         nrels;
    RelFileNode *rptr;
    PendingRelDelete *pending;

    nrels = 0;
    for (pending = pendingDeletes; pending != NULL; pending = pending->next)
    {
        if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit
            && pending->backend == InvalidBackendId)
            nrels++;
    }
    if (nrels == 0)
    {
        *ptr = NULL;
        return 0;
    }
    rptr = (RelFileNode *) palloc(nrels * sizeof(RelFileNode));
    *ptr = rptr;
    for (pending = pendingDeletes; pending != NULL; pending = pending->next)
    {
        if (pending->nestLevel >= nestLevel && pending->atCommit == forCommit
            && pending->backend == InvalidBackendId)
        {
            *rptr = pending->relnode;
            rptr++;
        }
    }
    return nrels;
}


Variable Documentation

PendingRelDelete* pendingDeletes = NULL [static]

Definition at line 62 of file storage.c.