Header And Logo

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

Functions | Variables

spgxlog.c File Reference

#include "postgres.h"
#include "access/spgist_private.h"
#include "access/transam.h"
#include "access/xlogutils.h"
#include "storage/standby.h"
#include "utils/memutils.h"
Include dependency graph for spgxlog.c:

Go to the source code of this file.

Functions

static void fillFakeState (SpGistState *state, spgxlogState stateSrc)
static void addOrReplaceTuple (Page page, Item tuple, int size, OffsetNumber offset)
static void spgRedoCreateIndex (XLogRecPtr lsn, XLogRecord *record)
static void spgRedoAddLeaf (XLogRecPtr lsn, XLogRecord *record)
static void spgRedoMoveLeafs (XLogRecPtr lsn, XLogRecord *record)
static void spgRedoAddNode (XLogRecPtr lsn, XLogRecord *record)
static void spgRedoSplitTuple (XLogRecPtr lsn, XLogRecord *record)
static void spgRedoPickSplit (XLogRecPtr lsn, XLogRecord *record)
static void spgRedoVacuumLeaf (XLogRecPtr lsn, XLogRecord *record)
static void spgRedoVacuumRoot (XLogRecPtr lsn, XLogRecord *record)
static void spgRedoVacuumRedirect (XLogRecPtr lsn, XLogRecord *record)
void spg_redo (XLogRecPtr lsn, XLogRecord *record)
void spg_xlog_startup (void)
void spg_xlog_cleanup (void)

Variables

static MemoryContext opCtx

Function Documentation

static void addOrReplaceTuple ( Page  page,
Item  tuple,
int  size,
OffsetNumber  offset 
) [static]

Definition at line 49 of file spgxlog.c.

References Assert, elog, ERROR, PageAddItem(), PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageIndexTupleDelete(), SPGIST_PLACEHOLDER, SpGistPageGetOpaque, and SpGistDeadTupleData::tupstate.

Referenced by spgRedoAddLeaf(), spgRedoAddNode(), spgRedoMoveLeafs(), spgRedoPickSplit(), and spgRedoSplitTuple().

{
    if (offset <= PageGetMaxOffsetNumber(page))
    {
        SpGistDeadTuple dt = (SpGistDeadTuple) PageGetItem(page,
                                                PageGetItemId(page, offset));

        if (dt->tupstate != SPGIST_PLACEHOLDER)
            elog(ERROR, "SPGiST tuple to be replaced is not a placeholder");

        Assert(SpGistPageGetOpaque(page)->nPlaceholder > 0);
        SpGistPageGetOpaque(page)->nPlaceholder--;

        PageIndexTupleDelete(page, offset);
    }

    Assert(offset <= PageGetMaxOffsetNumber(page) + 1);

    if (PageAddItem(page, tuple, size, offset, false, false) != offset)
        elog(ERROR, "failed to add item of size %u to SPGiST index page",
             size);
}

static void fillFakeState ( SpGistState state,
spgxlogState  stateSrc 
) [static]

Definition at line 34 of file spgxlog.c.

References SpGistState::deadTupleStorage, spgxlogState::isBuild, SpGistState::isBuild, spgxlogState::myXid, SpGistState::myXid, palloc0(), and SGDTSIZE.

Referenced by spgRedoAddNode(), spgRedoMoveLeafs(), spgRedoPickSplit(), and spgRedoVacuumLeaf().

{
    memset(state, 0, sizeof(*state));

    state->myXid = stateSrc.myXid;
    state->isBuild = stateSrc.isBuild;
    state->deadTupleStorage = palloc0(SGDTSIZE);
}

void spg_redo ( XLogRecPtr  lsn,
XLogRecord record 
)
void spg_xlog_cleanup ( void   ) 

Definition at line 1106 of file spgxlog.c.

References MemoryContextDelete().

void spg_xlog_startup ( void   ) 
static void spgRedoAddLeaf ( XLogRecPtr  lsn,
XLogRecord record 
) [static]

Definition at line 108 of file spgxlog.c.

References addOrReplaceTuple(), Assert, spgxlogAddLeaf::blknoLeaf, spgxlogAddLeaf::blknoParent, BufferGetPage, BufferIsValid, elog, ERROR, InvalidBlockNumber, InvalidOffsetNumber, MarkBufferDirty(), spgxlogAddLeaf::newPage, SpGistLeafTupleData::nextOffset, spgxlogAddLeaf::node, spgxlogAddLeaf::nodeI, spgxlogAddLeaf::offnumHeadLeaf, spgxlogAddLeaf::offnumLeaf, spgxlogAddLeaf::offnumParent, PageAddItem(), PageGetItem, PageGetItemId, PageGetLSN, PageIndexTupleDelete(), PageSetLSN, RestoreBackupBlock(), SpGistLeafTupleData::size, SPGIST_LEAF, SPGIST_NULLS, SpGistInitBuffer(), spgUpdateNodeLink(), spgxlogAddLeaf::storesNulls, UnlockReleaseBuffer(), XLogRecord::xl_info, XLogReadBuffer(), XLogRecGetData, and XLR_BKP_BLOCK.

Referenced by spg_redo().

{
    char       *ptr = XLogRecGetData(record);
    spgxlogAddLeaf *xldata = (spgxlogAddLeaf *) ptr;
    SpGistLeafTuple leafTuple;
    Buffer      buffer;
    Page        page;

    /* we assume this is adequately aligned */
    ptr += sizeof(spgxlogAddLeaf);
    leafTuple = (SpGistLeafTuple) ptr;

    /*
     * In normal operation we would have both current and parent pages locked
     * simultaneously; but in WAL replay it should be safe to update the leaf
     * page before updating the parent.
     */
    if (record->xl_info & XLR_BKP_BLOCK(0))
        (void) RestoreBackupBlock(lsn, record, 0, false, false);
    else
    {
        buffer = XLogReadBuffer(xldata->node, xldata->blknoLeaf,
                                xldata->newPage);
        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);

            if (xldata->newPage)
                SpGistInitBuffer(buffer,
                     SPGIST_LEAF | (xldata->storesNulls ? SPGIST_NULLS : 0));

            if (lsn > PageGetLSN(page))
            {
                /* insert new tuple */
                if (xldata->offnumLeaf != xldata->offnumHeadLeaf)
                {
                    /* normal cases, tuple was added by SpGistPageAddNewItem */
                    addOrReplaceTuple(page, (Item) leafTuple, leafTuple->size,
                                      xldata->offnumLeaf);

                    /* update head tuple's chain link if needed */
                    if (xldata->offnumHeadLeaf != InvalidOffsetNumber)
                    {
                        SpGistLeafTuple head;

                        head = (SpGistLeafTuple) PageGetItem(page,
                                PageGetItemId(page, xldata->offnumHeadLeaf));
                        Assert(head->nextOffset == leafTuple->nextOffset);
                        head->nextOffset = xldata->offnumLeaf;
                    }
                }
                else
                {
                    /* replacing a DEAD tuple */
                    PageIndexTupleDelete(page, xldata->offnumLeaf);
                    if (PageAddItem(page,
                                    (Item) leafTuple, leafTuple->size,
                     xldata->offnumLeaf, false, false) != xldata->offnumLeaf)
                        elog(ERROR, "failed to add item of size %u to SPGiST index page",
                             leafTuple->size);
                }

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }
            UnlockReleaseBuffer(buffer);
        }
    }

    /* update parent downlink if necessary */
    if (record->xl_info & XLR_BKP_BLOCK(1))
        (void) RestoreBackupBlock(lsn, record, 1, false, false);
    else if (xldata->blknoParent != InvalidBlockNumber)
    {
        buffer = XLogReadBuffer(xldata->node, xldata->blknoParent, false);
        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);
            if (lsn > PageGetLSN(page))
            {
                SpGistInnerTuple tuple;

                tuple = (SpGistInnerTuple) PageGetItem(page,
                                  PageGetItemId(page, xldata->offnumParent));

                spgUpdateNodeLink(tuple, xldata->nodeI,
                                  xldata->blknoLeaf, xldata->offnumLeaf);

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }
            UnlockReleaseBuffer(buffer);
        }
    }
}

static void spgRedoAddNode ( XLogRecPtr  lsn,
XLogRecord record 
) [static]

Definition at line 320 of file spgxlog.c.

References addOrReplaceTuple(), Assert, spgxlogAddNode::blkno, spgxlogAddNode::blknoNew, spgxlogAddNode::blknoParent, BufferGetPage, BufferIsValid, elog, ERROR, fillFakeState(), InvalidBlockNumber, InvalidOffsetNumber, SpGistState::isBuild, MarkBufferDirty(), spgxlogAddNode::newPage, spgxlogAddNode::node, spgxlogAddNode::nodeI, spgxlogAddNode::offnum, spgxlogAddNode::offnumNew, spgxlogAddNode::offnumParent, PageAddItem(), PageGetItem, PageGetItemId, PageGetLSN, PageIndexTupleDelete(), PageSetLSN, RestoreBackupBlock(), SpGistDeadTupleData::size, SpGistInnerTupleData::size, spgFormDeadTuple(), SPGIST_PLACEHOLDER, SPGIST_REDIRECT, SpGistInitBuffer(), SpGistPageGetOpaque, spgUpdateNodeLink(), spgxlogAddNode::stateSrc, UnlockReleaseBuffer(), XLogRecord::xl_info, XLogReadBuffer(), XLogRecGetData, and XLR_BKP_BLOCK.

Referenced by spg_redo().

{
    char       *ptr = XLogRecGetData(record);
    spgxlogAddNode *xldata = (spgxlogAddNode *) ptr;
    SpGistInnerTuple innerTuple;
    SpGistState state;
    Buffer      buffer;
    Page        page;
    int         bbi;

    /* we assume this is adequately aligned */
    ptr += sizeof(spgxlogAddNode);
    innerTuple = (SpGistInnerTuple) ptr;

    fillFakeState(&state, xldata->stateSrc);

    if (xldata->blknoNew == InvalidBlockNumber)
    {
        /* update in place */
        Assert(xldata->blknoParent == InvalidBlockNumber);
        if (record->xl_info & XLR_BKP_BLOCK(0))
            (void) RestoreBackupBlock(lsn, record, 0, false, false);
        else
        {
            buffer = XLogReadBuffer(xldata->node, xldata->blkno, false);
            if (BufferIsValid(buffer))
            {
                page = BufferGetPage(buffer);
                if (lsn > PageGetLSN(page))
                {
                    PageIndexTupleDelete(page, xldata->offnum);
                    if (PageAddItem(page, (Item) innerTuple, innerTuple->size,
                                    xldata->offnum,
                                    false, false) != xldata->offnum)
                        elog(ERROR, "failed to add item of size %u to SPGiST index page",
                             innerTuple->size);

                    PageSetLSN(page, lsn);
                    MarkBufferDirty(buffer);
                }
                UnlockReleaseBuffer(buffer);
            }
        }
    }
    else
    {
        /*
         * In normal operation we would have all three pages (source, dest,
         * and parent) locked simultaneously; but in WAL replay it should be
         * safe to update them one at a time, as long as we do it in the right
         * order.
         *
         * The logic here depends on the assumption that blkno != blknoNew,
         * else we can't tell which BKP bit goes with which page, and the LSN
         * checks could go wrong too.
         */
        Assert(xldata->blkno != xldata->blknoNew);

        /* Install new tuple first so redirect is valid */
        if (record->xl_info & XLR_BKP_BLOCK(1))
            (void) RestoreBackupBlock(lsn, record, 1, false, false);
        else
        {
            buffer = XLogReadBuffer(xldata->node, xldata->blknoNew,
                                    xldata->newPage);
            if (BufferIsValid(buffer))
            {
                page = BufferGetPage(buffer);

                /* AddNode is not used for nulls pages */
                if (xldata->newPage)
                    SpGistInitBuffer(buffer, 0);

                if (lsn > PageGetLSN(page))
                {
                    addOrReplaceTuple(page, (Item) innerTuple,
                                      innerTuple->size, xldata->offnumNew);

                    /*
                     * If parent is in this same page, don't advance LSN;
                     * doing so would fool us into not applying the parent
                     * downlink update below.  We'll update the LSN when we
                     * fix the parent downlink.
                     */
                    if (xldata->blknoParent != xldata->blknoNew)
                    {
                        PageSetLSN(page, lsn);
                    }
                    MarkBufferDirty(buffer);
                }
                UnlockReleaseBuffer(buffer);
            }
        }

        /* Delete old tuple, replacing it with redirect or placeholder tuple */
        if (record->xl_info & XLR_BKP_BLOCK(0))
            (void) RestoreBackupBlock(lsn, record, 0, false, false);
        else
        {
            buffer = XLogReadBuffer(xldata->node, xldata->blkno, false);
            if (BufferIsValid(buffer))
            {
                page = BufferGetPage(buffer);
                if (lsn > PageGetLSN(page))
                {
                    SpGistDeadTuple dt;

                    if (state.isBuild)
                        dt = spgFormDeadTuple(&state, SPGIST_PLACEHOLDER,
                                              InvalidBlockNumber,
                                              InvalidOffsetNumber);
                    else
                        dt = spgFormDeadTuple(&state, SPGIST_REDIRECT,
                                              xldata->blknoNew,
                                              xldata->offnumNew);

                    PageIndexTupleDelete(page, xldata->offnum);
                    if (PageAddItem(page, (Item) dt, dt->size,
                                    xldata->offnum,
                                    false, false) != xldata->offnum)
                        elog(ERROR, "failed to add item of size %u to SPGiST index page",
                             dt->size);

                    if (state.isBuild)
                        SpGistPageGetOpaque(page)->nPlaceholder++;
                    else
                        SpGistPageGetOpaque(page)->nRedirection++;

                    /*
                     * If parent is in this same page, don't advance LSN;
                     * doing so would fool us into not applying the parent
                     * downlink update below.  We'll update the LSN when we
                     * fix the parent downlink.
                     */
                    if (xldata->blknoParent != xldata->blkno)
                    {
                        PageSetLSN(page, lsn);
                    }
                    MarkBufferDirty(buffer);
                }
                UnlockReleaseBuffer(buffer);
            }
        }

        /*
         * Update parent downlink.  Since parent could be in either of the
         * previous two buffers, it's a bit tricky to determine which BKP bit
         * applies.
         */
        if (xldata->blknoParent == xldata->blkno)
            bbi = 0;
        else if (xldata->blknoParent == xldata->blknoNew)
            bbi = 1;
        else
            bbi = 2;

        if (record->xl_info & XLR_BKP_BLOCK(bbi))
        {
            if (bbi == 2)       /* else we already did it */
                (void) RestoreBackupBlock(lsn, record, bbi, false, false);
        }
        else
        {
            buffer = XLogReadBuffer(xldata->node, xldata->blknoParent, false);
            if (BufferIsValid(buffer))
            {
                page = BufferGetPage(buffer);
                if (lsn > PageGetLSN(page))
                {
                    SpGistInnerTuple innerTuple;

                    innerTuple = (SpGistInnerTuple) PageGetItem(page,
                                  PageGetItemId(page, xldata->offnumParent));

                    spgUpdateNodeLink(innerTuple, xldata->nodeI,
                                      xldata->blknoNew, xldata->offnumNew);

                    PageSetLSN(page, lsn);
                    MarkBufferDirty(buffer);
                }
                UnlockReleaseBuffer(buffer);
            }
        }
    }
}

static void spgRedoCreateIndex ( XLogRecPtr  lsn,
XLogRecord record 
) [static]

Definition at line 73 of file spgxlog.c.

References Assert, BufferGetPage, BufferIsValid, MarkBufferDirty(), PageSetLSN, SPGIST_LEAF, SPGIST_METAPAGE_BLKNO, SPGIST_NULL_BLKNO, SPGIST_NULLS, SPGIST_ROOT_BLKNO, SpGistInitBuffer(), SpGistInitMetapage(), UnlockReleaseBuffer(), XLogRecord::xl_info, XLogReadBuffer(), XLogRecGetData, and XLR_BKP_BLOCK_MASK.

Referenced by spg_redo().

{
    RelFileNode *node = (RelFileNode *) XLogRecGetData(record);
    Buffer      buffer;
    Page        page;

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

    buffer = XLogReadBuffer(*node, SPGIST_METAPAGE_BLKNO, true);
    Assert(BufferIsValid(buffer));
    page = (Page) BufferGetPage(buffer);
    SpGistInitMetapage(page);
    PageSetLSN(page, lsn);
    MarkBufferDirty(buffer);
    UnlockReleaseBuffer(buffer);

    buffer = XLogReadBuffer(*node, SPGIST_ROOT_BLKNO, true);
    Assert(BufferIsValid(buffer));
    SpGistInitBuffer(buffer, SPGIST_LEAF);
    page = (Page) BufferGetPage(buffer);
    PageSetLSN(page, lsn);
    MarkBufferDirty(buffer);
    UnlockReleaseBuffer(buffer);

    buffer = XLogReadBuffer(*node, SPGIST_NULL_BLKNO, true);
    Assert(BufferIsValid(buffer));
    SpGistInitBuffer(buffer, SPGIST_LEAF | SPGIST_NULLS);
    page = (Page) BufferGetPage(buffer);
    PageSetLSN(page, lsn);
    MarkBufferDirty(buffer);
    UnlockReleaseBuffer(buffer);
}

static void spgRedoMoveLeafs ( XLogRecPtr  lsn,
XLogRecord record 
) [static]

Definition at line 205 of file spgxlog.c.

References addOrReplaceTuple(), spgxlogMoveLeafs::blknoDst, spgxlogMoveLeafs::blknoParent, spgxlogMoveLeafs::blknoSrc, BufferGetPage, BufferIsValid, fillFakeState(), i, SpGistState::isBuild, MarkBufferDirty(), MAXALIGN, spgxlogMoveLeafs::newPage, spgxlogMoveLeafs::nMoves, spgxlogMoveLeafs::node, spgxlogMoveLeafs::nodeI, spgxlogMoveLeafs::offnumParent, PageGetItem, PageGetItemId, PageGetLSN, PageSetLSN, spgxlogMoveLeafs::replaceDead, RestoreBackupBlock(), SpGistLeafTupleData::size, SPGIST_LEAF, SPGIST_NULLS, SPGIST_PLACEHOLDER, SPGIST_REDIRECT, SpGistInitBuffer(), spgPageIndexMultiDelete(), spgUpdateNodeLink(), spgxlogMoveLeafs::stateSrc, spgxlogMoveLeafs::storesNulls, UnlockReleaseBuffer(), XLogRecord::xl_info, XLogReadBuffer(), XLogRecGetData, and XLR_BKP_BLOCK.

Referenced by spg_redo().

{
    char       *ptr = XLogRecGetData(record);
    spgxlogMoveLeafs *xldata = (spgxlogMoveLeafs *) ptr;
    SpGistState state;
    OffsetNumber *toDelete;
    OffsetNumber *toInsert;
    int         nInsert;
    Buffer      buffer;
    Page        page;

    fillFakeState(&state, xldata->stateSrc);

    nInsert = xldata->replaceDead ? 1 : xldata->nMoves + 1;

    ptr += MAXALIGN(sizeof(spgxlogMoveLeafs));
    toDelete = (OffsetNumber *) ptr;
    ptr += MAXALIGN(sizeof(OffsetNumber) * xldata->nMoves);
    toInsert = (OffsetNumber *) ptr;
    ptr += MAXALIGN(sizeof(OffsetNumber) * nInsert);

    /* now ptr points to the list of leaf tuples */

    /*
     * In normal operation we would have all three pages (source, dest, and
     * parent) locked simultaneously; but in WAL replay it should be safe to
     * update them one at a time, as long as we do it in the right order.
     */

    /* Insert tuples on the dest page (do first, so redirect is valid) */
    if (record->xl_info & XLR_BKP_BLOCK(1))
        (void) RestoreBackupBlock(lsn, record, 1, false, false);
    else
    {
        buffer = XLogReadBuffer(xldata->node, xldata->blknoDst,
                                xldata->newPage);
        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);

            if (xldata->newPage)
                SpGistInitBuffer(buffer,
                     SPGIST_LEAF | (xldata->storesNulls ? SPGIST_NULLS : 0));

            if (lsn > PageGetLSN(page))
            {
                int         i;

                for (i = 0; i < nInsert; i++)
                {
                    SpGistLeafTuple lt = (SpGistLeafTuple) ptr;

                    addOrReplaceTuple(page, (Item) lt, lt->size, toInsert[i]);
                    ptr += lt->size;
                }

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }
            UnlockReleaseBuffer(buffer);
        }
    }

    /* Delete tuples from the source page, inserting a redirection pointer */
    if (record->xl_info & XLR_BKP_BLOCK(0))
        (void) RestoreBackupBlock(lsn, record, 0, false, false);
    else
    {
        buffer = XLogReadBuffer(xldata->node, xldata->blknoSrc, false);
        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);
            if (lsn > PageGetLSN(page))
            {
                spgPageIndexMultiDelete(&state, page, toDelete, xldata->nMoves,
                        state.isBuild ? SPGIST_PLACEHOLDER : SPGIST_REDIRECT,
                                        SPGIST_PLACEHOLDER,
                                        xldata->blknoDst,
                                        toInsert[nInsert - 1]);

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }
            UnlockReleaseBuffer(buffer);
        }
    }

    /* And update the parent downlink */
    if (record->xl_info & XLR_BKP_BLOCK(2))
        (void) RestoreBackupBlock(lsn, record, 2, false, false);
    else
    {
        buffer = XLogReadBuffer(xldata->node, xldata->blknoParent, false);
        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);
            if (lsn > PageGetLSN(page))
            {
                SpGistInnerTuple tuple;

                tuple = (SpGistInnerTuple) PageGetItem(page,
                                  PageGetItemId(page, xldata->offnumParent));

                spgUpdateNodeLink(tuple, xldata->nodeI,
                                  xldata->blknoDst, toInsert[nInsert - 1]);

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }
            UnlockReleaseBuffer(buffer);
        }
    }
}

static void spgRedoPickSplit ( XLogRecPtr  lsn,
XLogRecord record 
) [static]

Definition at line 586 of file spgxlog.c.

References addOrReplaceTuple(), Assert, spgxlogPickSplit::blknoDest, spgxlogPickSplit::blknoInner, spgxlogPickSplit::blknoParent, spgxlogPickSplit::blknoSrc, BufferGetPage, BufferIsValid, fillFakeState(), i, spgxlogPickSplit::initDest, spgxlogPickSplit::initInner, spgxlogPickSplit::initSrc, InvalidBlockNumber, InvalidOffsetNumber, SpGistState::isBuild, MarkBufferDirty(), MAXALIGN, spgxlogPickSplit::nDelete, spgxlogPickSplit::nInsert, spgxlogPickSplit::node, spgxlogPickSplit::nodeI, NULL, spgxlogPickSplit::offnumInner, spgxlogPickSplit::offnumParent, PageGetItem, PageGetItemId, PageGetLSN, PageSetLSN, RestoreBackupBlock(), SpGistLeafTupleData::size, SpGistInnerTupleData::size, SPGIST_LEAF, SPGIST_NULLS, SPGIST_PLACEHOLDER, SPGIST_REDIRECT, SpGistBlockIsRoot, SpGistInitBuffer(), spgPageIndexMultiDelete(), spgUpdateNodeLink(), spgxlogPickSplit::stateSrc, spgxlogPickSplit::storesNulls, UnlockReleaseBuffer(), XLogRecord::xl_info, XLogReadBuffer(), XLogRecGetData, and XLR_BKP_BLOCK.

Referenced by spg_redo().

{
    char       *ptr = XLogRecGetData(record);
    spgxlogPickSplit *xldata = (spgxlogPickSplit *) ptr;
    SpGistInnerTuple innerTuple;
    SpGistState state;
    OffsetNumber *toDelete;
    OffsetNumber *toInsert;
    uint8      *leafPageSelect;
    Buffer      srcBuffer;
    Buffer      destBuffer;
    Page        srcPage;
    Page        destPage;
    Page        page;
    int         bbi;
    int         i;

    fillFakeState(&state, xldata->stateSrc);

    ptr += MAXALIGN(sizeof(spgxlogPickSplit));
    innerTuple = (SpGistInnerTuple) ptr;
    ptr += innerTuple->size;
    toDelete = (OffsetNumber *) ptr;
    ptr += MAXALIGN(sizeof(OffsetNumber) * xldata->nDelete);
    toInsert = (OffsetNumber *) ptr;
    ptr += MAXALIGN(sizeof(OffsetNumber) * xldata->nInsert);
    leafPageSelect = (uint8 *) ptr;
    ptr += MAXALIGN(sizeof(uint8) * xldata->nInsert);

    /* now ptr points to the list of leaf tuples */

    /*
     * It's a bit tricky to identify which pages have been handled as
     * full-page images, so we explicitly count each referenced buffer.
     */
    bbi = 0;

    if (SpGistBlockIsRoot(xldata->blknoSrc))
    {
        /* when splitting root, we touch it only in the guise of new inner */
        srcBuffer = InvalidBuffer;
        srcPage = NULL;
    }
    else if (xldata->initSrc)
    {
        /* just re-init the source page */
        srcBuffer = XLogReadBuffer(xldata->node, xldata->blknoSrc, true);
        Assert(BufferIsValid(srcBuffer));
        srcPage = (Page) BufferGetPage(srcBuffer);

        SpGistInitBuffer(srcBuffer,
                     SPGIST_LEAF | (xldata->storesNulls ? SPGIST_NULLS : 0));
        /* don't update LSN etc till we're done with it */
    }
    else
    {
        /*
         * Delete the specified tuples from source page.  (In case we're in
         * Hot Standby, we need to hold lock on the page till we're done
         * inserting leaf tuples and the new inner tuple, else the added
         * redirect tuple will be a dangling link.)
         */
        if (record->xl_info & XLR_BKP_BLOCK(bbi))
        {
            srcBuffer = RestoreBackupBlock(lsn, record, bbi, false, true);
            srcPage = NULL;     /* don't need to do any page updates */
        }
        else
        {
            srcBuffer = XLogReadBuffer(xldata->node, xldata->blknoSrc, false);
            if (BufferIsValid(srcBuffer))
            {
                srcPage = BufferGetPage(srcBuffer);
                if (lsn > PageGetLSN(srcPage))
                {
                    /*
                     * We have it a bit easier here than in doPickSplit(),
                     * because we know the inner tuple's location already, so
                     * we can inject the correct redirection tuple now.
                     */
                    if (!state.isBuild)
                        spgPageIndexMultiDelete(&state, srcPage,
                                                toDelete, xldata->nDelete,
                                                SPGIST_REDIRECT,
                                                SPGIST_PLACEHOLDER,
                                                xldata->blknoInner,
                                                xldata->offnumInner);
                    else
                        spgPageIndexMultiDelete(&state, srcPage,
                                                toDelete, xldata->nDelete,
                                                SPGIST_PLACEHOLDER,
                                                SPGIST_PLACEHOLDER,
                                                InvalidBlockNumber,
                                                InvalidOffsetNumber);

                    /* don't update LSN etc till we're done with it */
                }
                else
                    srcPage = NULL;     /* don't do any page updates */
            }
            else
                srcPage = NULL;
        }
        bbi++;
    }

    /* try to access dest page if any */
    if (xldata->blknoDest == InvalidBlockNumber)
    {
        destBuffer = InvalidBuffer;
        destPage = NULL;
    }
    else if (xldata->initDest)
    {
        /* just re-init the dest page */
        destBuffer = XLogReadBuffer(xldata->node, xldata->blknoDest, true);
        Assert(BufferIsValid(destBuffer));
        destPage = (Page) BufferGetPage(destBuffer);

        SpGistInitBuffer(destBuffer,
                     SPGIST_LEAF | (xldata->storesNulls ? SPGIST_NULLS : 0));
        /* don't update LSN etc till we're done with it */
    }
    else
    {
        /*
         * We could probably release the page lock immediately in the
         * full-page-image case, but for safety let's hold it till later.
         */
        if (record->xl_info & XLR_BKP_BLOCK(bbi))
        {
            destBuffer = RestoreBackupBlock(lsn, record, bbi, false, true);
            destPage = NULL;    /* don't need to do any page updates */
        }
        else
        {
            destBuffer = XLogReadBuffer(xldata->node, xldata->blknoDest, false);
            if (BufferIsValid(destBuffer))
            {
                destPage = (Page) BufferGetPage(destBuffer);
                if (lsn <= PageGetLSN(destPage))
                    destPage = NULL;    /* don't do any page updates */
            }
            else
                destPage = NULL;
        }
        bbi++;
    }

    /* restore leaf tuples to src and/or dest page */
    for (i = 0; i < xldata->nInsert; i++)
    {
        SpGistLeafTuple lt = (SpGistLeafTuple) ptr;

        ptr += lt->size;

        page = leafPageSelect[i] ? destPage : srcPage;
        if (page == NULL)
            continue;           /* no need to touch this page */

        addOrReplaceTuple(page, (Item) lt, lt->size, toInsert[i]);
    }

    /* Now update src and dest page LSNs if needed */
    if (srcPage != NULL)
    {
        PageSetLSN(srcPage, lsn);
        MarkBufferDirty(srcBuffer);
    }
    if (destPage != NULL)
    {
        PageSetLSN(destPage, lsn);
        MarkBufferDirty(destBuffer);
    }

    /* restore new inner tuple */
    if (record->xl_info & XLR_BKP_BLOCK(bbi))
        (void) RestoreBackupBlock(lsn, record, bbi, false, false);
    else
    {
        Buffer      buffer = XLogReadBuffer(xldata->node, xldata->blknoInner,
                                            xldata->initInner);

        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);

            if (xldata->initInner)
                SpGistInitBuffer(buffer,
                                 (xldata->storesNulls ? SPGIST_NULLS : 0));

            if (lsn > PageGetLSN(page))
            {
                addOrReplaceTuple(page, (Item) innerTuple, innerTuple->size,
                                  xldata->offnumInner);

                /* if inner is also parent, update link while we're here */
                if (xldata->blknoInner == xldata->blknoParent)
                {
                    SpGistInnerTuple parent;

                    parent = (SpGistInnerTuple) PageGetItem(page,
                                  PageGetItemId(page, xldata->offnumParent));
                    spgUpdateNodeLink(parent, xldata->nodeI,
                                    xldata->blknoInner, xldata->offnumInner);
                }

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }
            UnlockReleaseBuffer(buffer);
        }
    }
    bbi++;

    /*
     * Now we can release the leaf-page locks.  It's okay to do this before
     * updating the parent downlink.
     */
    if (BufferIsValid(srcBuffer))
        UnlockReleaseBuffer(srcBuffer);
    if (BufferIsValid(destBuffer))
        UnlockReleaseBuffer(destBuffer);

    /* update parent downlink, unless we did it above */
    if (xldata->blknoParent == InvalidBlockNumber)
    {
        /* no parent cause we split the root */
        Assert(SpGistBlockIsRoot(xldata->blknoInner));
    }
    else if (xldata->blknoInner != xldata->blknoParent)
    {
        if (record->xl_info & XLR_BKP_BLOCK(bbi))
            (void) RestoreBackupBlock(lsn, record, bbi, false, false);
        else
        {
            Buffer      buffer = XLogReadBuffer(xldata->node, xldata->blknoParent, false);

            if (BufferIsValid(buffer))
            {
                page = BufferGetPage(buffer);

                if (lsn > PageGetLSN(page))
                {
                    SpGistInnerTuple parent;

                    parent = (SpGistInnerTuple) PageGetItem(page,
                                  PageGetItemId(page, xldata->offnumParent));
                    spgUpdateNodeLink(parent, xldata->nodeI,
                                    xldata->blknoInner, xldata->offnumInner);

                    PageSetLSN(page, lsn);
                    MarkBufferDirty(buffer);
                }
                UnlockReleaseBuffer(buffer);
            }
        }
    }
}

static void spgRedoSplitTuple ( XLogRecPtr  lsn,
XLogRecord record 
) [static]

Definition at line 507 of file spgxlog.c.

References addOrReplaceTuple(), spgxlogSplitTuple::blknoPostfix, spgxlogSplitTuple::blknoPrefix, BufferGetPage, BufferIsValid, elog, ERROR, MarkBufferDirty(), spgxlogSplitTuple::newPage, spgxlogSplitTuple::node, spgxlogSplitTuple::offnumPostfix, spgxlogSplitTuple::offnumPrefix, PageAddItem(), PageGetLSN, PageIndexTupleDelete(), PageSetLSN, RestoreBackupBlock(), SpGistInnerTupleData::size, SpGistInitBuffer(), UnlockReleaseBuffer(), XLogRecord::xl_info, XLogReadBuffer(), XLogRecGetData, and XLR_BKP_BLOCK.

Referenced by spg_redo().

{
    char       *ptr = XLogRecGetData(record);
    spgxlogSplitTuple *xldata = (spgxlogSplitTuple *) ptr;
    SpGistInnerTuple prefixTuple;
    SpGistInnerTuple postfixTuple;
    Buffer      buffer;
    Page        page;

    /* we assume this is adequately aligned */
    ptr += sizeof(spgxlogSplitTuple);
    prefixTuple = (SpGistInnerTuple) ptr;
    ptr += prefixTuple->size;
    postfixTuple = (SpGistInnerTuple) ptr;

    /*
     * In normal operation we would have both pages locked simultaneously; but
     * in WAL replay it should be safe to update them one at a time, as long
     * as we do it in the right order.
     */

    /* insert postfix tuple first to avoid dangling link */
    if (record->xl_info & XLR_BKP_BLOCK(1))
        (void) RestoreBackupBlock(lsn, record, 1, false, false);
    else if (xldata->blknoPostfix != xldata->blknoPrefix)
    {
        buffer = XLogReadBuffer(xldata->node, xldata->blknoPostfix,
                                xldata->newPage);
        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);

            /* SplitTuple is not used for nulls pages */
            if (xldata->newPage)
                SpGistInitBuffer(buffer, 0);

            if (lsn > PageGetLSN(page))
            {
                addOrReplaceTuple(page, (Item) postfixTuple,
                                  postfixTuple->size, xldata->offnumPostfix);

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }
            UnlockReleaseBuffer(buffer);
        }
    }

    /* now handle the original page */
    if (record->xl_info & XLR_BKP_BLOCK(0))
        (void) RestoreBackupBlock(lsn, record, 0, false, false);
    else
    {
        buffer = XLogReadBuffer(xldata->node, xldata->blknoPrefix, false);
        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);
            if (lsn > PageGetLSN(page))
            {
                PageIndexTupleDelete(page, xldata->offnumPrefix);
                if (PageAddItem(page, (Item) prefixTuple, prefixTuple->size,
                 xldata->offnumPrefix, false, false) != xldata->offnumPrefix)
                    elog(ERROR, "failed to add item of size %u to SPGiST index page",
                         prefixTuple->size);

                if (xldata->blknoPostfix == xldata->blknoPrefix)
                    addOrReplaceTuple(page, (Item) postfixTuple,
                                      postfixTuple->size,
                                      xldata->offnumPostfix);

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }
            UnlockReleaseBuffer(buffer);
        }
    }
}

static void spgRedoVacuumLeaf ( XLogRecPtr  lsn,
XLogRecord record 
) [static]

Definition at line 847 of file spgxlog.c.

References Assert, spgxlogVacuumLeaf::blkno, BufferGetPage, BufferIsValid, fillFakeState(), i, InvalidBlockNumber, InvalidOffsetNumber, MarkBufferDirty(), spgxlogVacuumLeaf::nChain, spgxlogVacuumLeaf::nDead, SpGistLeafTupleData::nextOffset, spgxlogVacuumLeaf::nMove, spgxlogVacuumLeaf::node, spgxlogVacuumLeaf::nPlaceholder, PageGetItem, PageGetItemId, PageGetLSN, PageSetLSN, RestoreBackupBlock(), SPGIST_DEAD, SPGIST_LIVE, SPGIST_PLACEHOLDER, spgPageIndexMultiDelete(), spgxlogVacuumLeaf::stateSrc, SpGistLeafTupleData::tupstate, UnlockReleaseBuffer(), XLogRecord::xl_info, XLogReadBuffer(), XLogRecGetData, and XLR_BKP_BLOCK.

Referenced by spg_redo().

{
    char       *ptr = XLogRecGetData(record);
    spgxlogVacuumLeaf *xldata = (spgxlogVacuumLeaf *) ptr;
    OffsetNumber *toDead;
    OffsetNumber *toPlaceholder;
    OffsetNumber *moveSrc;
    OffsetNumber *moveDest;
    OffsetNumber *chainSrc;
    OffsetNumber *chainDest;
    SpGistState state;
    Buffer      buffer;
    Page        page;
    int         i;

    fillFakeState(&state, xldata->stateSrc);

    ptr += sizeof(spgxlogVacuumLeaf);
    toDead = (OffsetNumber *) ptr;
    ptr += sizeof(OffsetNumber) * xldata->nDead;
    toPlaceholder = (OffsetNumber *) ptr;
    ptr += sizeof(OffsetNumber) * xldata->nPlaceholder;
    moveSrc = (OffsetNumber *) ptr;
    ptr += sizeof(OffsetNumber) * xldata->nMove;
    moveDest = (OffsetNumber *) ptr;
    ptr += sizeof(OffsetNumber) * xldata->nMove;
    chainSrc = (OffsetNumber *) ptr;
    ptr += sizeof(OffsetNumber) * xldata->nChain;
    chainDest = (OffsetNumber *) ptr;

    if (record->xl_info & XLR_BKP_BLOCK(0))
        (void) RestoreBackupBlock(lsn, record, 0, false, false);
    else
    {
        buffer = XLogReadBuffer(xldata->node, xldata->blkno, false);
        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);
            if (lsn > PageGetLSN(page))
            {
                spgPageIndexMultiDelete(&state, page,
                                        toDead, xldata->nDead,
                                        SPGIST_DEAD, SPGIST_DEAD,
                                        InvalidBlockNumber,
                                        InvalidOffsetNumber);

                spgPageIndexMultiDelete(&state, page,
                                        toPlaceholder, xldata->nPlaceholder,
                                      SPGIST_PLACEHOLDER, SPGIST_PLACEHOLDER,
                                        InvalidBlockNumber,
                                        InvalidOffsetNumber);

                /* see comments in vacuumLeafPage() */
                for (i = 0; i < xldata->nMove; i++)
                {
                    ItemId      idSrc = PageGetItemId(page, moveSrc[i]);
                    ItemId      idDest = PageGetItemId(page, moveDest[i]);
                    ItemIdData  tmp;

                    tmp = *idSrc;
                    *idSrc = *idDest;
                    *idDest = tmp;
                }

                spgPageIndexMultiDelete(&state, page,
                                        moveSrc, xldata->nMove,
                                      SPGIST_PLACEHOLDER, SPGIST_PLACEHOLDER,
                                        InvalidBlockNumber,
                                        InvalidOffsetNumber);

                for (i = 0; i < xldata->nChain; i++)
                {
                    SpGistLeafTuple lt;

                    lt = (SpGistLeafTuple) PageGetItem(page,
                                           PageGetItemId(page, chainSrc[i]));
                    Assert(lt->tupstate == SPGIST_LIVE);
                    lt->nextOffset = chainDest[i];
                }

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }
            UnlockReleaseBuffer(buffer);
        }
    }
}

static void spgRedoVacuumRedirect ( XLogRecPtr  lsn,
XLogRecord record 
) [static]

Definition at line 969 of file spgxlog.c.

References Assert, spgxlogVacuumRedirect::blkno, BufferGetPage, BufferIsValid, spgxlogVacuumRedirect::firstPlaceholder, i, InHotStandby, InvalidOffsetNumber, ItemPointerSetInvalid, MarkBufferDirty(), spgxlogVacuumRedirect::newestRedirectXid, spgxlogVacuumRedirect::node, SpGistPageOpaqueData::nPlaceholder, SpGistPageOpaqueData::nRedirection, spgxlogVacuumRedirect::nToPlaceholder, PageGetItem, PageGetItemId, PageGetLSN, PageGetMaxOffsetNumber, PageIndexMultiDelete(), PageSetLSN, palloc(), pfree(), SpGistDeadTupleData::pointer, ResolveRecoveryConflictWithSnapshot(), RestoreBackupBlock(), SPGIST_REDIRECT, SpGistPageGetOpaque, TransactionIdIsValid, SpGistDeadTupleData::tupstate, UnlockReleaseBuffer(), XLogRecord::xl_info, XLogReadBuffer(), XLogRecGetData, and XLR_BKP_BLOCK.

Referenced by spg_redo().

{
    char       *ptr = XLogRecGetData(record);
    spgxlogVacuumRedirect *xldata = (spgxlogVacuumRedirect *) ptr;
    OffsetNumber *itemToPlaceholder;
    Buffer      buffer;
    Page        page;

    ptr += sizeof(spgxlogVacuumRedirect);
    itemToPlaceholder = (OffsetNumber *) ptr;

    /*
     * If any redirection tuples are being removed, make sure there are no
     * live Hot Standby transactions that might need to see them.
     */
    if (InHotStandby)
    {
        if (TransactionIdIsValid(xldata->newestRedirectXid))
            ResolveRecoveryConflictWithSnapshot(xldata->newestRedirectXid,
                                                xldata->node);
    }

    if (record->xl_info & XLR_BKP_BLOCK(0))
        (void) RestoreBackupBlock(lsn, record, 0, false, false);
    else
    {
        buffer = XLogReadBuffer(xldata->node, xldata->blkno, false);

        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);
            if (lsn > PageGetLSN(page))
            {
                SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
                int         i;

                /* Convert redirect pointers to plain placeholders */
                for (i = 0; i < xldata->nToPlaceholder; i++)
                {
                    SpGistDeadTuple dt;

                    dt = (SpGistDeadTuple) PageGetItem(page,
                                  PageGetItemId(page, itemToPlaceholder[i]));
                    Assert(dt->tupstate == SPGIST_REDIRECT);
                    dt->tupstate = SPGIST_PLACEHOLDER;
                    ItemPointerSetInvalid(&dt->pointer);
                }

                Assert(opaque->nRedirection >= xldata->nToPlaceholder);
                opaque->nRedirection -= xldata->nToPlaceholder;
                opaque->nPlaceholder += xldata->nToPlaceholder;

                /* Remove placeholder tuples at end of page */
                if (xldata->firstPlaceholder != InvalidOffsetNumber)
                {
                    int         max = PageGetMaxOffsetNumber(page);
                    OffsetNumber *toDelete;

                    toDelete = palloc(sizeof(OffsetNumber) * max);

                    for (i = xldata->firstPlaceholder; i <= max; i++)
                        toDelete[i - xldata->firstPlaceholder] = i;

                    i = max - xldata->firstPlaceholder + 1;
                    Assert(opaque->nPlaceholder >= i);
                    opaque->nPlaceholder -= i;

                    /* The array is sorted, so can use PageIndexMultiDelete */
                    PageIndexMultiDelete(page, toDelete, i);

                    pfree(toDelete);
                }

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }

            UnlockReleaseBuffer(buffer);
        }
    }
}

static void spgRedoVacuumRoot ( XLogRecPtr  lsn,
XLogRecord record 
) [static]

Definition at line 936 of file spgxlog.c.

References spgxlogVacuumRoot::blkno, BufferGetPage, BufferIsValid, MarkBufferDirty(), spgxlogVacuumRoot::nDelete, spgxlogVacuumRoot::node, PageGetLSN, PageIndexMultiDelete(), PageSetLSN, RestoreBackupBlock(), UnlockReleaseBuffer(), XLogRecord::xl_info, XLogReadBuffer(), XLogRecGetData, and XLR_BKP_BLOCK.

Referenced by spg_redo().

{
    char       *ptr = XLogRecGetData(record);
    spgxlogVacuumRoot *xldata = (spgxlogVacuumRoot *) ptr;
    OffsetNumber *toDelete;
    Buffer      buffer;
    Page        page;

    ptr += sizeof(spgxlogVacuumRoot);
    toDelete = (OffsetNumber *) ptr;

    if (record->xl_info & XLR_BKP_BLOCK(0))
        (void) RestoreBackupBlock(lsn, record, 0, false, false);
    else
    {
        buffer = XLogReadBuffer(xldata->node, xldata->blkno, false);
        if (BufferIsValid(buffer))
        {
            page = BufferGetPage(buffer);
            if (lsn > PageGetLSN(page))
            {
                /* The tuple numbers are in order */
                PageIndexMultiDelete(page, toDelete, xldata->nDelete);

                PageSetLSN(page, lsn);
                MarkBufferDirty(buffer);
            }
            UnlockReleaseBuffer(buffer);
        }
    }
}


Variable Documentation

MemoryContext opCtx [static]

Definition at line 24 of file spgxlog.c.