Header And Logo

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

Functions

ginbtree.c File Reference

#include "postgres.h"
#include "access/gin_private.h"
#include "miscadmin.h"
#include "utils/rel.h"
Include dependency graph for ginbtree.c:

Go to the source code of this file.

Functions

static int ginTraverseLock (Buffer buffer, bool searchMode)
GinBtreeStackginPrepareFindLeafPage (GinBtree btree, BlockNumber blkno)
GinBtreeStackginFindLeafPage (GinBtree btree, GinBtreeStack *stack)
void freeGinBtreeStack (GinBtreeStack *stack)
void ginFindParents (GinBtree btree, GinBtreeStack *stack, BlockNumber rootBlkno)
void ginInsertValue (GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats)

Function Documentation

void freeGinBtreeStack ( GinBtreeStack stack  ) 

Definition at line 152 of file ginbtree.c.

References GinBtreeStack::buffer, InvalidBuffer, GinBtreeStack::parent, pfree(), and ReleaseBuffer().

Referenced by ginEntryInsert(), ginInsertItemPointers(), ginInsertValue(), scanPostingTree(), and startScanEntry().

{
    while (stack)
    {
        GinBtreeStack *tmp = stack->parent;

        if (stack->buffer != InvalidBuffer)
            ReleaseBuffer(stack->buffer);

        pfree(stack);
        stack = tmp;
    }
}

GinBtreeStack* ginFindLeafPage ( GinBtree  btree,
GinBtreeStack stack 
)

Definition at line 74 of file ginbtree.c.

References Assert, GinBtreeStack::blkno, GinBtreeStack::buffer, BufferGetPage, FALSE, GinBtreeData::findChildPage, GinBtreeData::fullScan, GIN_ROOT_BLKNO, GIN_UNLOCK, GinPageGetOpaque, GinPageIsLeaf, ginPrepareFindLeafPage(), ginTraverseLock(), GinBtreeData::index, InvalidBlockNumber, GinBtreeData::isMoveRight, LockBuffer(), GinBtreeStack::off, palloc(), GinBtreeStack::parent, GinBtreeStack::predictNumber, ReadBuffer(), ReleaseAndReadBuffer(), and GinBtreeData::searchMode.

Referenced by ginEntryInsert(), ginInsertItemPointers(), ginScanBeginPostingTree(), and startScanEntry().

{
    bool        isfirst = TRUE;
    BlockNumber rootBlkno;

    if (!stack)
        stack = ginPrepareFindLeafPage(btree, GIN_ROOT_BLKNO);
    rootBlkno = stack->blkno;

    for (;;)
    {
        Page        page;
        BlockNumber child;
        int         access = GIN_SHARE;

        stack->off = InvalidOffsetNumber;

        page = BufferGetPage(stack->buffer);

        if (isfirst)
        {
            if (GinPageIsLeaf(page) && !btree->searchMode)
                access = GIN_EXCLUSIVE;
            isfirst = FALSE;
        }
        else
            access = ginTraverseLock(stack->buffer, btree->searchMode);

        /*
         * ok, page is correctly locked, we should check to move right ..,
         * root never has a right link, so small optimization
         */
        while (btree->fullScan == FALSE && stack->blkno != rootBlkno &&
               btree->isMoveRight(btree, page))
        {
            BlockNumber rightlink = GinPageGetOpaque(page)->rightlink;

            if (rightlink == InvalidBlockNumber)
                /* rightmost page */
                break;

            stack->blkno = rightlink;
            LockBuffer(stack->buffer, GIN_UNLOCK);
            stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno);
            LockBuffer(stack->buffer, access);
            page = BufferGetPage(stack->buffer);
        }

        if (GinPageIsLeaf(page))    /* we found, return locked page */
            return stack;

        /* now we have correct buffer, try to find child */
        child = btree->findChildPage(btree, stack);

        LockBuffer(stack->buffer, GIN_UNLOCK);
        Assert(child != InvalidBlockNumber);
        Assert(stack->blkno != child);

        if (btree->searchMode)
        {
            /* in search mode we may forget path to leaf */
            stack->blkno = child;
            stack->buffer = ReleaseAndReadBuffer(stack->buffer, btree->index, stack->blkno);
        }
        else
        {
            GinBtreeStack *ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));

            ptr->parent = stack;
            stack = ptr;
            stack->blkno = child;
            stack->buffer = ReadBuffer(btree->index, stack->blkno);
            stack->predictNumber = 1;
        }
    }
}

void ginFindParents ( GinBtree  btree,
GinBtreeStack stack,
BlockNumber  rootBlkno 
)

Definition at line 173 of file ginbtree.c.

References Assert, GinBtreeStack::blkno, GinBtreeStack::buffer, BufferGetBlockNumber(), BufferGetPage, elog, ERROR, GinBtreeData::findChildPtr, GinBtreeData::getLeftMostPage, GIN_EXCLUSIVE, GIN_UNLOCK, GinPageGetOpaque, GinPageIsLeaf, GinBtreeData::index, InvalidBlockNumber, InvalidOffsetNumber, LockBuffer(), GinBtreeStack::off, palloc(), GinBtreeStack::parent, ReadBuffer(), and ReleaseBuffer().

Referenced by ginContinueSplit(), and ginInsertValue().

{

    Page        page;
    Buffer      buffer;
    BlockNumber blkno,
                leftmostBlkno;
    OffsetNumber offset;
    GinBtreeStack *root = stack->parent;
    GinBtreeStack *ptr;

    if (!root)
    {
        /* XLog mode... */
        root = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
        root->blkno = rootBlkno;
        root->buffer = ReadBuffer(btree->index, rootBlkno);
        LockBuffer(root->buffer, GIN_EXCLUSIVE);
        root->parent = NULL;
    }
    else
    {
        /*
         * find root, we should not release root page until update is
         * finished!!
         */
        while (root->parent)
        {
            ReleaseBuffer(root->buffer);
            root = root->parent;
        }

        Assert(root->blkno == rootBlkno);
        Assert(BufferGetBlockNumber(root->buffer) == rootBlkno);
        LockBuffer(root->buffer, GIN_EXCLUSIVE);
    }
    root->off = InvalidOffsetNumber;

    page = BufferGetPage(root->buffer);
    Assert(!GinPageIsLeaf(page));

    /* check trivial case */
    if ((root->off = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber)) != InvalidOffsetNumber)
    {
        stack->parent = root;
        return;
    }

    leftmostBlkno = blkno = btree->getLeftMostPage(btree, page);
    LockBuffer(root->buffer, GIN_UNLOCK);
    Assert(blkno != InvalidBlockNumber);

    for (;;)
    {
        buffer = ReadBuffer(btree->index, blkno);
        LockBuffer(buffer, GIN_EXCLUSIVE);
        page = BufferGetPage(buffer);
        if (GinPageIsLeaf(page))
            elog(ERROR, "Lost path");

        leftmostBlkno = btree->getLeftMostPage(btree, page);

        while ((offset = btree->findChildPtr(btree, page, stack->blkno, InvalidOffsetNumber)) == InvalidOffsetNumber)
        {
            blkno = GinPageGetOpaque(page)->rightlink;
            LockBuffer(buffer, GIN_UNLOCK);
            ReleaseBuffer(buffer);
            if (blkno == InvalidBlockNumber)
                break;
            buffer = ReadBuffer(btree->index, blkno);
            LockBuffer(buffer, GIN_EXCLUSIVE);
            page = BufferGetPage(buffer);
        }

        if (blkno != InvalidBlockNumber)
        {
            ptr = (GinBtreeStack *) palloc(sizeof(GinBtreeStack));
            ptr->blkno = blkno;
            ptr->buffer = buffer;
            ptr->parent = root; /* it's may be wrong, but in next call we will
                                 * correct */
            ptr->off = offset;
            stack->parent = ptr;
            return;
        }

        blkno = leftmostBlkno;
    }
}

void ginInsertValue ( GinBtree  btree,
GinBtreeStack stack,
GinStatsData buildStats 
)

Definition at line 273 of file ginbtree.c.

References Assert, GinBtreeStack::blkno, BlockNumberIsValid, GinBtreeStack::buffer, BufferGetBlockNumber(), BufferGetPage, XLogRecData::data, END_CRIT_SECTION, FALSE, GinBtreeData::fillRoot, GinBtreeData::findChildPtr, freeGinBtreeStack(), GIN_EXCLUSIVE, GIN_LEAF, GIN_UNLOCK, ginFindParents(), GinInitBuffer(), GinNewBuffer(), GinPageGetOpaque, GinBtreeData::index, InvalidBlockNumber, InvalidOffsetNumber, GinBtreeData::isData, GinBtreeData::isDelete, GinBtreeData::isEnoughSpace, LockBuffer(), MarkBufferDirty(), GinStatsData::nDataPages, GinStatsData::nEntryPages, NULL, GinBtreeStack::off, PageRestoreTempPage(), PageSetLSN, GinBtreeStack::parent, pfree(), GinBtreeData::placeToPage, RelationNeedsWAL, ReleaseAndReadBuffer(), GinBtreeData::splitPage, START_CRIT_SECTION, TRUE, UnlockReleaseBuffer(), XLOG_GIN_INSERT, XLOG_GIN_SPLIT, and XLogInsert().

Referenced by ginContinueSplit(), ginEntryInsert(), and ginInsertItemPointers().

{
    GinBtreeStack *parent;
    BlockNumber rootBlkno;
    Page        page,
                rpage,
                lpage;

    /* extract root BlockNumber from stack */
    Assert(stack != NULL);
    parent = stack;
    while (parent->parent)
        parent = parent->parent;
    rootBlkno = parent->blkno;
    Assert(BlockNumberIsValid(rootBlkno));

    /* this loop crawls up the stack until the insertion is complete */
    for (;;)
    {
        XLogRecData *rdata;
        BlockNumber savedRightLink;

        page = BufferGetPage(stack->buffer);
        savedRightLink = GinPageGetOpaque(page)->rightlink;

        if (btree->isEnoughSpace(btree, stack->buffer, stack->off))
        {
            START_CRIT_SECTION();
            btree->placeToPage(btree, stack->buffer, stack->off, &rdata);

            MarkBufferDirty(stack->buffer);

            if (RelationNeedsWAL(btree->index))
            {
                XLogRecPtr  recptr;

                recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_INSERT, rdata);
                PageSetLSN(page, recptr);
            }

            LockBuffer(stack->buffer, GIN_UNLOCK);
            END_CRIT_SECTION();

            freeGinBtreeStack(stack);

            return;
        }
        else
        {
            Buffer      rbuffer = GinNewBuffer(btree->index);
            Page        newlpage;

            /*
             * newlpage is a pointer to memory page, it doesn't associate with
             * buffer, stack->buffer should be untouched
             */
            newlpage = btree->splitPage(btree, stack->buffer, rbuffer, stack->off, &rdata);

            ((ginxlogSplit *) (rdata->data))->rootBlkno = rootBlkno;

            /* During index build, count the newly-split page */
            if (buildStats)
            {
                if (btree->isData)
                    buildStats->nDataPages++;
                else
                    buildStats->nEntryPages++;
            }

            parent = stack->parent;

            if (parent == NULL)
            {
                /*
                 * split root, so we need to allocate new left page and place
                 * pointer on root to left and right page
                 */
                Buffer      lbuffer = GinNewBuffer(btree->index);

                ((ginxlogSplit *) (rdata->data))->isRootSplit = TRUE;
                ((ginxlogSplit *) (rdata->data))->rrlink = InvalidBlockNumber;

                page = BufferGetPage(stack->buffer);
                lpage = BufferGetPage(lbuffer);
                rpage = BufferGetPage(rbuffer);

                GinPageGetOpaque(rpage)->rightlink = InvalidBlockNumber;
                GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);
                ((ginxlogSplit *) (rdata->data))->lblkno = BufferGetBlockNumber(lbuffer);

                START_CRIT_SECTION();

                GinInitBuffer(stack->buffer, GinPageGetOpaque(newlpage)->flags & ~GIN_LEAF);
                PageRestoreTempPage(newlpage, lpage);
                btree->fillRoot(btree, stack->buffer, lbuffer, rbuffer);

                MarkBufferDirty(rbuffer);
                MarkBufferDirty(lbuffer);
                MarkBufferDirty(stack->buffer);

                if (RelationNeedsWAL(btree->index))
                {
                    XLogRecPtr  recptr;

                    recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_SPLIT, rdata);
                    PageSetLSN(page, recptr);
                    PageSetLSN(lpage, recptr);
                    PageSetLSN(rpage, recptr);
                }

                UnlockReleaseBuffer(rbuffer);
                UnlockReleaseBuffer(lbuffer);
                LockBuffer(stack->buffer, GIN_UNLOCK);
                END_CRIT_SECTION();

                freeGinBtreeStack(stack);

                /* During index build, count the newly-added root page */
                if (buildStats)
                {
                    if (btree->isData)
                        buildStats->nDataPages++;
                    else
                        buildStats->nEntryPages++;
                }

                return;
            }
            else
            {
                /* split non-root page */
                ((ginxlogSplit *) (rdata->data))->isRootSplit = FALSE;
                ((ginxlogSplit *) (rdata->data))->rrlink = savedRightLink;

                lpage = BufferGetPage(stack->buffer);
                rpage = BufferGetPage(rbuffer);

                GinPageGetOpaque(rpage)->rightlink = savedRightLink;
                GinPageGetOpaque(newlpage)->rightlink = BufferGetBlockNumber(rbuffer);

                START_CRIT_SECTION();
                PageRestoreTempPage(newlpage, lpage);

                MarkBufferDirty(rbuffer);
                MarkBufferDirty(stack->buffer);

                if (RelationNeedsWAL(btree->index))
                {
                    XLogRecPtr  recptr;

                    recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_SPLIT, rdata);
                    PageSetLSN(lpage, recptr);
                    PageSetLSN(rpage, recptr);
                }
                UnlockReleaseBuffer(rbuffer);
                END_CRIT_SECTION();
            }
        }

        btree->isDelete = FALSE;

        /* search parent to lock */
        LockBuffer(parent->buffer, GIN_EXCLUSIVE);

        /* move right if it's needed */
        page = BufferGetPage(parent->buffer);
        while ((parent->off = btree->findChildPtr(btree, page, stack->blkno, parent->off)) == InvalidOffsetNumber)
        {
            BlockNumber rightlink = GinPageGetOpaque(page)->rightlink;

            LockBuffer(parent->buffer, GIN_UNLOCK);

            if (rightlink == InvalidBlockNumber)
            {
                /*
                 * rightmost page, but we don't find parent, we should use
                 * plain search...
                 */
                ginFindParents(btree, stack, rootBlkno);
                parent = stack->parent;
                Assert(parent != NULL);
                break;
            }

            parent->blkno = rightlink;
            parent->buffer = ReleaseAndReadBuffer(parent->buffer, btree->index, parent->blkno);
            LockBuffer(parent->buffer, GIN_EXCLUSIVE);
            page = BufferGetPage(parent->buffer);
        }

        UnlockReleaseBuffer(stack->buffer);
        pfree(stack);
        stack = parent;
    }
}

GinBtreeStack* ginPrepareFindLeafPage ( GinBtree  btree,
BlockNumber  blkno 
)
static int ginTraverseLock ( Buffer  buffer,
bool  searchMode 
) [static]

Definition at line 25 of file ginbtree.c.

References BufferGetPage, FALSE, GIN_EXCLUSIVE, GIN_SHARE, GIN_UNLOCK, GinPageIsLeaf, and LockBuffer().

Referenced by ginFindLeafPage(), and ginPrepareFindLeafPage().

{
    Page        page;
    int         access = GIN_SHARE;

    LockBuffer(buffer, GIN_SHARE);
    page = BufferGetPage(buffer);
    if (GinPageIsLeaf(page))
    {
        if (searchMode == FALSE)
        {
            /* we should relock our page */
            LockBuffer(buffer, GIN_UNLOCK);
            LockBuffer(buffer, GIN_EXCLUSIVE);

            /* But root can become non-leaf during relock */
            if (!GinPageIsLeaf(page))
            {
                /* restore old lock type (very rare) */
                LockBuffer(buffer, GIN_UNLOCK);
                LockBuffer(buffer, GIN_SHARE);
            }
            else
                access = GIN_EXCLUSIVE;
        }
    }

    return access;
}