Header And Logo

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

Data Structures | Typedefs | Functions

ginvacuum.c File Reference

#include "postgres.h"
#include "access/gin_private.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "postmaster/autovacuum.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
Include dependency graph for ginvacuum.c:

Go to the source code of this file.

Data Structures

struct  GinVacuumState
struct  DataPageDeleteStack

Typedefs

typedef struct DataPageDeleteStack DataPageDeleteStack

Functions

static uint32 ginVacuumPostingList (GinVacuumState *gvs, ItemPointerData *items, uint32 nitem, ItemPointerData **cleaned)
static void xlogVacuumPage (Relation index, Buffer buffer)
static bool ginVacuumPostingTreeLeaves (GinVacuumState *gvs, BlockNumber blkno, bool isRoot, Buffer *rootBuffer)
static void ginDeletePage (GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkno, BlockNumber parentBlkno, OffsetNumber myoff, bool isParentRoot)
static bool ginScanToDelete (GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDeleteStack *parent, OffsetNumber myoff)
static void ginVacuumPostingTree (GinVacuumState *gvs, BlockNumber rootBlkno)
static Page ginVacuumEntryPage (GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint32 *nroot)
Datum ginbulkdelete (PG_FUNCTION_ARGS)
Datum ginvacuumcleanup (PG_FUNCTION_ARGS)

Typedef Documentation


Function Documentation

Datum ginbulkdelete ( PG_FUNCTION_ARGS   ) 

Definition at line 577 of file ginvacuum.c.

References Assert, DataPageDeleteStack::blkno, BufferGetPage, GinVacuumState::callback, callback(), GinVacuumState::callback_state, END_CRIT_SECTION, FirstOffsetNumber, GIN_EXCLUSIVE, GIN_ROOT_BLKNO, GIN_SHARE, GIN_UNLOCK, GinGetDownlink, ginInsertCleanup(), GinPageGetOpaque, GinPageIsData, GinPageIsLeaf, GinVacuumState::ginstate, ginVacuumEntryPage(), ginVacuumPostingTree(), i, GinVacuumState::index, IndexVacuumInfo::index, initGinState(), InvalidBlockNumber, LockBuffer(), MAIN_FORKNUM, MarkBufferDirty(), NULL, IndexBulkDeleteResult::num_index_tuples, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageRestoreTempPage(), palloc0(), PG_GETARG_POINTER, PG_RETURN_POINTER, RBM_NORMAL, ReadBufferExtended(), GinVacuumState::result, START_CRIT_SECTION, IndexVacuumInfo::strategy, GinVacuumState::strategy, UnlockReleaseBuffer(), vacuum_delay_point(), and xlogVacuumPage().

{
    IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
    IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
    IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
    void       *callback_state = (void *) PG_GETARG_POINTER(3);
    Relation    index = info->index;
    BlockNumber blkno = GIN_ROOT_BLKNO;
    GinVacuumState gvs;
    Buffer      buffer;
    BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
    uint32      nRoot;

    gvs.index = index;
    gvs.callback = callback;
    gvs.callback_state = callback_state;
    gvs.strategy = info->strategy;
    initGinState(&gvs.ginstate, index);

    /* first time through? */
    if (stats == NULL)
    {
        /* Yes, so initialize stats to zeroes */
        stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
        /* and cleanup any pending inserts */
        ginInsertCleanup(&gvs.ginstate, true, stats);
    }

    /* we'll re-count the tuples each time */
    stats->num_index_tuples = 0;
    gvs.result = stats;

    buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                RBM_NORMAL, info->strategy);

    /* find leaf page */
    for (;;)
    {
        Page        page = BufferGetPage(buffer);
        IndexTuple  itup;

        LockBuffer(buffer, GIN_SHARE);

        Assert(!GinPageIsData(page));

        if (GinPageIsLeaf(page))
        {
            LockBuffer(buffer, GIN_UNLOCK);
            LockBuffer(buffer, GIN_EXCLUSIVE);

            if (blkno == GIN_ROOT_BLKNO && !GinPageIsLeaf(page))
            {
                LockBuffer(buffer, GIN_UNLOCK);
                continue;       /* check it one more */
            }
            break;
        }

        Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);

        itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
        blkno = GinGetDownlink(itup);
        Assert(blkno != InvalidBlockNumber);

        UnlockReleaseBuffer(buffer);
        buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                    RBM_NORMAL, info->strategy);
    }

    /* right now we found leftmost page in entry's BTree */

    for (;;)
    {
        Page        page = BufferGetPage(buffer);
        Page        resPage;
        uint32      i;

        Assert(!GinPageIsData(page));

        resPage = ginVacuumEntryPage(&gvs, buffer, rootOfPostingTree, &nRoot);

        blkno = GinPageGetOpaque(page)->rightlink;

        if (resPage)
        {
            START_CRIT_SECTION();
            PageRestoreTempPage(resPage, page);
            MarkBufferDirty(buffer);
            xlogVacuumPage(gvs.index, buffer);
            UnlockReleaseBuffer(buffer);
            END_CRIT_SECTION();
        }
        else
        {
            UnlockReleaseBuffer(buffer);
        }

        vacuum_delay_point();

        for (i = 0; i < nRoot; i++)
        {
            ginVacuumPostingTree(&gvs, rootOfPostingTree[i]);
            vacuum_delay_point();
        }

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

        buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                    RBM_NORMAL, info->strategy);
        LockBuffer(buffer, GIN_EXCLUSIVE);
    }

    PG_RETURN_POINTER(gvs.result);
}

static void ginDeletePage ( GinVacuumState gvs,
BlockNumber  deleteBlkno,
BlockNumber  leftBlkno,
BlockNumber  parentBlkno,
OffsetNumber  myoff,
bool  isParentRoot 
) [static]

Definition at line 241 of file ginvacuum.c.

References Assert, ginxlogDeletePage::blkno, XLogRecData::buffer, XLogRecData::buffer_std, BufferGetPage, XLogRecData::data, END_CRIT_SECTION, GIN_EXCLUSIVE, GIN_UNLOCK, GinDataPageGetItem, GinPageDeletePostingItem(), GinPageGetOpaque, GinVacuumState::index, InvalidBlockNumber, ginxlogDeletePage::leftBlkno, XLogRecData::len, LockBuffer(), MAIN_FORKNUM, MarkBufferDirty(), XLogRecData::next, ginxlogDeletePage::node, IndexBulkDeleteResult::pages_deleted, PageSetLSN, ginxlogDeletePage::parentBlkno, ginxlogDeletePage::parentOffset, PostingItemGetBlockNumber, RBM_NORMAL, RelationData::rd_node, ReadBufferExtended(), RelationNeedsWAL, ReleaseBuffer(), GinVacuumState::result, ginxlogDeletePage::rightLink, START_CRIT_SECTION, GinVacuumState::strategy, UnlockReleaseBuffer(), XLOG_GIN_DELETE_PAGE, and XLogInsert().

Referenced by ginScanToDelete().

{
    Buffer      dBuffer;
    Buffer      lBuffer;
    Buffer      pBuffer;
    Page        page,
                parentPage;

    dBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, deleteBlkno,
                                 RBM_NORMAL, gvs->strategy);

    if (leftBlkno != InvalidBlockNumber)
        lBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, leftBlkno,
                                     RBM_NORMAL, gvs->strategy);
    else
        lBuffer = InvalidBuffer;

    pBuffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, parentBlkno,
                                 RBM_NORMAL, gvs->strategy);

    LockBuffer(dBuffer, GIN_EXCLUSIVE);
    if (!isParentRoot)          /* parent is already locked by
                                 * LockBufferForCleanup() */
        LockBuffer(pBuffer, GIN_EXCLUSIVE);
    if (leftBlkno != InvalidBlockNumber)
        LockBuffer(lBuffer, GIN_EXCLUSIVE);

    START_CRIT_SECTION();

    if (leftBlkno != InvalidBlockNumber)
    {
        BlockNumber rightlink;

        page = BufferGetPage(dBuffer);
        rightlink = GinPageGetOpaque(page)->rightlink;

        page = BufferGetPage(lBuffer);
        GinPageGetOpaque(page)->rightlink = rightlink;
    }

    parentPage = BufferGetPage(pBuffer);
#ifdef USE_ASSERT_CHECKING
    do
    {
        PostingItem *tod = (PostingItem *) GinDataPageGetItem(parentPage, myoff);

        Assert(PostingItemGetBlockNumber(tod) == deleteBlkno);
    } while (0);
#endif
    GinPageDeletePostingItem(parentPage, myoff);

    page = BufferGetPage(dBuffer);

    /*
     * we shouldn't change rightlink field to save workability of running
     * search scan
     */
    GinPageGetOpaque(page)->flags = GIN_DELETED;

    MarkBufferDirty(pBuffer);
    if (leftBlkno != InvalidBlockNumber)
        MarkBufferDirty(lBuffer);
    MarkBufferDirty(dBuffer);

    if (RelationNeedsWAL(gvs->index))
    {
        XLogRecPtr  recptr;
        XLogRecData rdata[4];
        ginxlogDeletePage data;
        int         n;

        data.node = gvs->index->rd_node;
        data.blkno = deleteBlkno;
        data.parentBlkno = parentBlkno;
        data.parentOffset = myoff;
        data.leftBlkno = leftBlkno;
        data.rightLink = GinPageGetOpaque(page)->rightlink;

        rdata[0].buffer = dBuffer;
        rdata[0].buffer_std = FALSE;
        rdata[0].data = NULL;
        rdata[0].len = 0;
        rdata[0].next = rdata + 1;

        rdata[1].buffer = pBuffer;
        rdata[1].buffer_std = FALSE;
        rdata[1].data = NULL;
        rdata[1].len = 0;
        rdata[1].next = rdata + 2;

        if (leftBlkno != InvalidBlockNumber)
        {
            rdata[2].buffer = lBuffer;
            rdata[2].buffer_std = FALSE;
            rdata[2].data = NULL;
            rdata[2].len = 0;
            rdata[2].next = rdata + 3;
            n = 3;
        }
        else
            n = 2;

        rdata[n].buffer = InvalidBuffer;
        rdata[n].buffer_std = FALSE;
        rdata[n].len = sizeof(ginxlogDeletePage);
        rdata[n].data = (char *) &data;
        rdata[n].next = NULL;

        recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_PAGE, rdata);
        PageSetLSN(page, recptr);
        PageSetLSN(parentPage, recptr);
        if (leftBlkno != InvalidBlockNumber)
        {
            page = BufferGetPage(lBuffer);
            PageSetLSN(page, recptr);
        }
    }

    if (!isParentRoot)
        LockBuffer(pBuffer, GIN_UNLOCK);
    ReleaseBuffer(pBuffer);

    if (leftBlkno != InvalidBlockNumber)
        UnlockReleaseBuffer(lBuffer);

    UnlockReleaseBuffer(dBuffer);

    END_CRIT_SECTION();

    gvs->result->pages_deleted++;
}

static bool ginScanToDelete ( GinVacuumState gvs,
BlockNumber  blkno,
bool  isRoot,
DataPageDeleteStack parent,
OffsetNumber  myoff 
) [static]

Definition at line 388 of file ginvacuum.c.

References Assert, DataPageDeleteStack::blkno, BufferGetPage, DataPageDeleteStack::child, FirstOffsetNumber, GinDataPageGetItem, ginDeletePage(), GinPageGetOpaque, GinPageIsData, GinPageIsLeaf, GinPageRightMost, i, GinVacuumState::index, InvalidBlockNumber, DataPageDeleteStack::isRoot, DataPageDeleteStack::leftBlkno, MAIN_FORKNUM, palloc0(), DataPageDeleteStack::parent, PostingItemGetBlockNumber, RBM_NORMAL, ReadBufferExtended(), ReleaseBuffer(), and GinVacuumState::strategy.

Referenced by ginVacuumPostingTree().

{
    DataPageDeleteStack *me;
    Buffer      buffer;
    Page        page;
    bool        meDelete = FALSE;

    if (isRoot)
    {
        me = parent;
    }
    else
    {
        if (!parent->child)
        {
            me = (DataPageDeleteStack *) palloc0(sizeof(DataPageDeleteStack));
            me->parent = parent;
            parent->child = me;
            me->leftBlkno = InvalidBlockNumber;
        }
        else
            me = parent->child;
    }

    buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
                                RBM_NORMAL, gvs->strategy);
    page = BufferGetPage(buffer);

    Assert(GinPageIsData(page));

    if (!GinPageIsLeaf(page))
    {
        OffsetNumber i;

        me->blkno = blkno;
        for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff; i++)
        {
            PostingItem *pitem = (PostingItem *) GinDataPageGetItem(page, i);

            if (ginScanToDelete(gvs, PostingItemGetBlockNumber(pitem), FALSE, me, i))
                i--;
        }
    }

    if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
    {
        if (!(me->leftBlkno == InvalidBlockNumber && GinPageRightMost(page)))
        {
            /* we never delete right most branch */
            Assert(!isRoot);
            if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
            {
                ginDeletePage(gvs, blkno, me->leftBlkno, me->parent->blkno, myoff, me->parent->isRoot);
                meDelete = TRUE;
            }
        }
    }

    ReleaseBuffer(buffer);

    if (!meDelete)
        me->leftBlkno = blkno;

    return meDelete;
}

Datum ginvacuumcleanup ( PG_FUNCTION_ARGS   ) 

Definition at line 694 of file ginvacuum.c.

References IndexVacuumInfo::analyze_only, Assert, DataPageDeleteStack::blkno, BufferGetPage, IndexVacuumInfo::estimated_count, IndexBulkDeleteResult::estimated_count, ExclusiveLock, GIN_ROOT_BLKNO, GIN_SHARE, ginInsertCleanup(), GinPageIsData, GinPageIsDeleted, GinPageIsLeaf, GinPageIsList, ginUpdateStats(), IndexVacuumInfo::index, IndexFreeSpaceMapVacuum(), initGinState(), IsAutoVacuumWorkerProcess(), LockBuffer(), LockRelationForExtension(), MAIN_FORKNUM, GinStatsData::nDataPages, GinStatsData::nEntries, GinStatsData::nEntryPages, GinStatsData::nTotalPages, NULL, IndexVacuumInfo::num_heap_tuples, IndexBulkDeleteResult::num_index_tuples, IndexBulkDeleteResult::num_pages, PageGetMaxOffsetNumber, IndexBulkDeleteResult::pages_free, palloc0(), PG_GETARG_POINTER, PG_RETURN_POINTER, RBM_NORMAL, ReadBufferExtended(), RecordFreeIndexPage(), RELATION_IS_LOCAL, RelationGetNumberOfBlocks, IndexVacuumInfo::strategy, UnlockRelationForExtension(), UnlockReleaseBuffer(), and vacuum_delay_point().

{
    IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
    IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
    Relation    index = info->index;
    bool        needLock;
    BlockNumber npages,
                blkno;
    BlockNumber totFreePages;
    GinState    ginstate;
    GinStatsData idxStat;

    /*
     * In an autovacuum analyze, we want to clean up pending insertions.
     * Otherwise, an ANALYZE-only call is a no-op.
     */
    if (info->analyze_only)
    {
        if (IsAutoVacuumWorkerProcess())
        {
            initGinState(&ginstate, index);
            ginInsertCleanup(&ginstate, true, stats);
        }
        PG_RETURN_POINTER(stats);
    }

    /*
     * Set up all-zero stats and cleanup pending inserts if ginbulkdelete
     * wasn't called
     */
    if (stats == NULL)
    {
        stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
        initGinState(&ginstate, index);
        ginInsertCleanup(&ginstate, true, stats);
    }

    memset(&idxStat, 0, sizeof(idxStat));

    /*
     * XXX we always report the heap tuple count as the number of index
     * entries.  This is bogus if the index is partial, but it's real hard to
     * tell how many distinct heap entries are referenced by a GIN index.
     */
    stats->num_index_tuples = info->num_heap_tuples;
    stats->estimated_count = info->estimated_count;

    /*
     * Need lock unless it's local to this backend.
     */
    needLock = !RELATION_IS_LOCAL(index);

    if (needLock)
        LockRelationForExtension(index, ExclusiveLock);
    npages = RelationGetNumberOfBlocks(index);
    if (needLock)
        UnlockRelationForExtension(index, ExclusiveLock);

    totFreePages = 0;

    for (blkno = GIN_ROOT_BLKNO; blkno < npages; blkno++)
    {
        Buffer      buffer;
        Page        page;

        vacuum_delay_point();

        buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                    RBM_NORMAL, info->strategy);
        LockBuffer(buffer, GIN_SHARE);
        page = (Page) BufferGetPage(buffer);

        if (GinPageIsDeleted(page))
        {
            Assert(blkno != GIN_ROOT_BLKNO);
            RecordFreeIndexPage(index, blkno);
            totFreePages++;
        }
        else if (GinPageIsData(page))
        {
            idxStat.nDataPages++;
        }
        else if (!GinPageIsList(page))
        {
            idxStat.nEntryPages++;

            if (GinPageIsLeaf(page))
                idxStat.nEntries += PageGetMaxOffsetNumber(page);
        }

        UnlockReleaseBuffer(buffer);
    }

    /* Update the metapage with accurate page and entry counts */
    idxStat.nTotalPages = npages;
    ginUpdateStats(info->index, &idxStat);

    /* Finally, vacuum the FSM */
    IndexFreeSpaceMapVacuum(info->index);

    stats->pages_free = totFreePages;

    if (needLock)
        LockRelationForExtension(index, ExclusiveLock);
    stats->num_pages = RelationGetNumberOfBlocks(index);
    if (needLock)
        UnlockRelationForExtension(index, ExclusiveLock);

    PG_RETURN_POINTER(stats);
}

static Page ginVacuumEntryPage ( GinVacuumState gvs,
Buffer  buffer,
BlockNumber roots,
uint32 nroot 
) [static]

Definition at line 493 of file ginvacuum.c.

References BufferGetPage, elog, ERROR, FirstOffsetNumber, GinFormTuple(), GinGetDownlink, GinGetNPosting, GinGetPosting, GinIsPostingTree, GinVacuumState::ginstate, gintuple_get_attrnum(), gintuple_get_key(), ginVacuumPostingList(), i, GinVacuumState::index, IndexTupleSize, NULL, PageAddItem(), PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageGetTempPageCopy(), PageIndexTupleDelete(), pfree(), and RelationGetRelationName.

Referenced by ginbulkdelete().

{
    Page        origpage = BufferGetPage(buffer),
                tmppage;
    OffsetNumber i,
                maxoff = PageGetMaxOffsetNumber(origpage);

    tmppage = origpage;

    *nroot = 0;

    for (i = FirstOffsetNumber; i <= maxoff; i++)
    {
        IndexTuple  itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));

        if (GinIsPostingTree(itup))
        {
            /*
             * store posting tree's roots for further processing, we can't
             * vacuum it just now due to risk of deadlocks with scans/inserts
             */
            roots[*nroot] = GinGetDownlink(itup);
            (*nroot)++;
        }
        else if (GinGetNPosting(itup) > 0)
        {
            /*
             * if we already create temporary page, we will make changes in
             * place
             */
            ItemPointerData *cleaned = (tmppage == origpage) ? NULL : GinGetPosting(itup);
            uint32      newN = ginVacuumPostingList(gvs, GinGetPosting(itup), GinGetNPosting(itup), &cleaned);

            if (GinGetNPosting(itup) != newN)
            {
                OffsetNumber attnum;
                Datum       key;
                GinNullCategory category;

                /*
                 * Some ItemPointers was deleted, so we should remake our
                 * tuple
                 */

                if (tmppage == origpage)
                {
                    /*
                     * On first difference we create temporary page in memory
                     * and copies content in to it.
                     */
                    tmppage = PageGetTempPageCopy(origpage);

                    if (newN > 0)
                    {
                        Size        pos = ((char *) GinGetPosting(itup)) - ((char *) origpage);

                        memcpy(tmppage + pos, cleaned, sizeof(ItemPointerData) * newN);
                    }

                    pfree(cleaned);

                    /* set itup pointer to new page */
                    itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
                }

                attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
                key = gintuple_get_key(&gvs->ginstate, itup, &category);
                itup = GinFormTuple(&gvs->ginstate, attnum, key, category,
                                    GinGetPosting(itup), newN, true);
                PageIndexTupleDelete(tmppage, i);

                if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false, false) != i)
                    elog(ERROR, "failed to add item to index page in \"%s\"",
                         RelationGetRelationName(gvs->index));

                pfree(itup);
            }
        }
    }

    return (tmppage == origpage) ? NULL : tmppage;
}

static uint32 ginVacuumPostingList ( GinVacuumState gvs,
ItemPointerData items,
uint32  nitem,
ItemPointerData **  cleaned 
) [static]

Definition at line 44 of file ginvacuum.c.

References GinVacuumState::callback, GinVacuumState::callback_state, i, IndexBulkDeleteResult::num_index_tuples, palloc(), GinVacuumState::result, and IndexBulkDeleteResult::tuples_removed.

Referenced by ginVacuumEntryPage(), and ginVacuumPostingTreeLeaves().

{
    uint32      i,
                j = 0;

    /*
     * just scan over ItemPointer array
     */

    for (i = 0; i < nitem; i++)
    {
        if (gvs->callback(items + i, gvs->callback_state))
        {
            gvs->result->tuples_removed += 1;
            if (!*cleaned)
            {
                *cleaned = (ItemPointerData *) palloc(sizeof(ItemPointerData) * nitem);
                if (i != 0)
                    memcpy(*cleaned, items, sizeof(ItemPointerData) * i);
            }
        }
        else
        {
            gvs->result->num_index_tuples += 1;
            if (i != j)
                (*cleaned)[j] = items[i];
            j++;
        }
    }

    return j;
}

static void ginVacuumPostingTree ( GinVacuumState gvs,
BlockNumber  rootBlkno 
) [static]

Definition at line 455 of file ginvacuum.c.

References Assert, DataPageDeleteStack::child, FALSE, ginScanToDelete(), ginVacuumPostingTreeLeaves(), InvalidBuffer, InvalidOffsetNumber, DataPageDeleteStack::isRoot, DataPageDeleteStack::leftBlkno, pfree(), TRUE, UnlockReleaseBuffer(), and vacuum_delay_point().

Referenced by ginbulkdelete().

{
    Buffer      rootBuffer = InvalidBuffer;
    DataPageDeleteStack root,
               *ptr,
               *tmp;

    if (ginVacuumPostingTreeLeaves(gvs, rootBlkno, TRUE, &rootBuffer) == FALSE)
    {
        Assert(rootBuffer == InvalidBuffer);
        return;
    }

    memset(&root, 0, sizeof(DataPageDeleteStack));
    root.leftBlkno = InvalidBlockNumber;
    root.isRoot = TRUE;

    vacuum_delay_point();

    ginScanToDelete(gvs, rootBlkno, TRUE, &root, InvalidOffsetNumber);

    ptr = root.child;
    while (ptr)
    {
        tmp = ptr->child;
        pfree(ptr);
        ptr = tmp;
    }

    UnlockReleaseBuffer(rootBuffer);
}

static bool ginVacuumPostingTreeLeaves ( GinVacuumState gvs,
BlockNumber  blkno,
bool  isRoot,
Buffer rootBuffer 
) [static]

Definition at line 153 of file ginvacuum.c.

References Assert, BufferGetPage, END_CRIT_SECTION, FirstOffsetNumber, GIN_EXCLUSIVE, GinDataPageGetData, GinDataPageGetItem, GinPageGetOpaque, GinPageIsData, GinPageIsLeaf, ginVacuumPostingList(), i, GinVacuumState::index, LockBuffer(), LockBufferForCleanup(), MAIN_FORKNUM, MarkBufferDirty(), pfree(), PostingItemGetBlockNumber, RBM_NORMAL, ReadBufferExtended(), START_CRIT_SECTION, GinVacuumState::strategy, UnlockReleaseBuffer(), and xlogVacuumPage().

Referenced by ginVacuumPostingTree().

{
    Buffer      buffer;
    Page        page;
    bool        hasVoidPage = FALSE;

    buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
                                RBM_NORMAL, gvs->strategy);
    page = BufferGetPage(buffer);

    /*
     * We should be sure that we don't concurrent with inserts, insert process
     * never release root page until end (but it can unlock it and lock
     * again). New scan can't start but previously started ones work
     * concurrently.
     */

    if (isRoot)
        LockBufferForCleanup(buffer);
    else
        LockBuffer(buffer, GIN_EXCLUSIVE);

    Assert(GinPageIsData(page));

    if (GinPageIsLeaf(page))
    {
        OffsetNumber newMaxOff,
                    oldMaxOff = GinPageGetOpaque(page)->maxoff;
        ItemPointerData *cleaned = NULL;

        newMaxOff = ginVacuumPostingList(gvs,
                (ItemPointer) GinDataPageGetData(page), oldMaxOff, &cleaned);

        /* saves changes about deleted tuple ... */
        if (oldMaxOff != newMaxOff)
        {
            START_CRIT_SECTION();

            if (newMaxOff > 0)
                memcpy(GinDataPageGetData(page), cleaned, sizeof(ItemPointerData) * newMaxOff);
            pfree(cleaned);
            GinPageGetOpaque(page)->maxoff = newMaxOff;

            MarkBufferDirty(buffer);
            xlogVacuumPage(gvs->index, buffer);

            END_CRIT_SECTION();

            /* if root is a leaf page, we don't desire further processing */
            if (!isRoot && GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
                hasVoidPage = TRUE;
        }
    }
    else
    {
        OffsetNumber i;
        bool        isChildHasVoid = FALSE;

        for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff; i++)
        {
            PostingItem *pitem = (PostingItem *) GinDataPageGetItem(page, i);

            if (ginVacuumPostingTreeLeaves(gvs, PostingItemGetBlockNumber(pitem), FALSE, NULL))
                isChildHasVoid = TRUE;
        }

        if (isChildHasVoid)
            hasVoidPage = TRUE;
    }

    /*
     * if we have root and theres void pages in tree, then we don't release
     * lock to go further processing and guarantee that tree is unused
     */
    if (!(isRoot && hasVoidPage))
    {
        UnlockReleaseBuffer(buffer);
    }
    else
    {
        Assert(rootBuffer);
        *rootBuffer = buffer;
    }

    return hasVoidPage;
}

static void xlogVacuumPage ( Relation  index,
Buffer  buffer 
) [static]

Definition at line 81 of file ginvacuum.c.

References Assert, ginxlogVacuumPage::blkno, XLogRecData::buffer, XLogRecData::buffer_std, BufferGetBlockNumber(), BufferGetPage, XLogRecData::data, FALSE, FirstOffsetNumber, GinDataPageGetData, GinPageGetOpaque, GinPageIsData, GinPageIsLeaf, i, IndexTupleSize, XLogRecData::len, MAXALIGN, XLogRecData::next, ginxlogVacuumPage::nitem, ginxlogVacuumPage::node, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageSetLSN, RelationData::rd_node, RelationNeedsWAL, TRUE, XLOG_GIN_VACUUM_PAGE, and XLogInsert().

Referenced by ginbulkdelete(), and ginVacuumPostingTreeLeaves().

{
    Page        page = BufferGetPage(buffer);
    XLogRecPtr  recptr;
    XLogRecData rdata[3];
    ginxlogVacuumPage data;
    char       *backup;
    char        itups[BLCKSZ];
    uint32      len = 0;

    Assert(GinPageIsLeaf(page));

    if (!RelationNeedsWAL(index))
        return;

    data.node = index->rd_node;
    data.blkno = BufferGetBlockNumber(buffer);

    if (GinPageIsData(page))
    {
        backup = GinDataPageGetData(page);
        data.nitem = GinPageGetOpaque(page)->maxoff;
        if (data.nitem)
            len = MAXALIGN(sizeof(ItemPointerData) * data.nitem);
    }
    else
    {
        char       *ptr;
        OffsetNumber i;

        ptr = backup = itups;
        for (i = FirstOffsetNumber; i <= PageGetMaxOffsetNumber(page); i++)
        {
            IndexTuple  itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));

            memcpy(ptr, itup, IndexTupleSize(itup));
            ptr += MAXALIGN(IndexTupleSize(itup));
        }

        data.nitem = PageGetMaxOffsetNumber(page);
        len = ptr - backup;
    }

    rdata[0].buffer = buffer;
    rdata[0].buffer_std = (GinPageIsData(page)) ? FALSE : TRUE;
    rdata[0].len = 0;
    rdata[0].data = NULL;
    rdata[0].next = rdata + 1;

    rdata[1].buffer = InvalidBuffer;
    rdata[1].len = sizeof(ginxlogVacuumPage);
    rdata[1].data = (char *) &data;

    if (len == 0)
    {
        rdata[1].next = NULL;
    }
    else
    {
        rdata[1].next = rdata + 2;

        rdata[2].buffer = InvalidBuffer;
        rdata[2].len = len;
        rdata[2].data = backup;
        rdata[2].next = NULL;
    }

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