Header And Logo

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

Data Structures | Typedefs | Functions

gistvacuum.c File Reference

#include "postgres.h"
#include "access/genam.h"
#include "access/gist_private.h"
#include "commands/vacuum.h"
#include "miscadmin.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
Include dependency graph for gistvacuum.c:

Go to the source code of this file.

Data Structures

struct  GistBDItem

Typedefs

typedef struct GistBDItem GistBDItem

Functions

Datum gistvacuumcleanup (PG_FUNCTION_ARGS)
static void pushStackIfSplited (Page page, GistBDItem *stack)
Datum gistbulkdelete (PG_FUNCTION_ARGS)

Typedef Documentation

typedef struct GistBDItem GistBDItem

Function Documentation

Datum gistbulkdelete ( PG_FUNCTION_ARGS   ) 

Definition at line 141 of file gistvacuum.c.

References GistBDItem::blkno, BufferGetPage, callback(), END_CRIT_SECTION, ereport, errdetail(), errhint(), errmsg(), IndexBulkDeleteResult::estimated_count, FirstOffsetNumber, GIST_EXCLUSIVE, GIST_ROOT_BLKNO, GIST_SHARE, GIST_UNLOCK, gistcheckpage(), gistGetFakeLSN(), GistMarkTuplesDeleted, GistPageIsLeaf, GistTupleIsInvalid, gistXLogUpdate(), i, IndexVacuumInfo::index, InvalidBuffer, ItemPointerGetBlockNumber, LockBuffer(), LOG, MAIN_FORKNUM, MarkBufferDirty(), GistBDItem::next, NULL, IndexBulkDeleteResult::num_index_tuples, OffsetNumberNext, PageGetItem, PageGetItemId, PageGetLSN, PageGetMaxOffsetNumber, PageIndexTupleDelete(), PageSetLSN, palloc(), palloc0(), GistBDItem::parentlsn, pfree(), PG_GETARG_POINTER, PG_RETURN_POINTER, pushStackIfSplited(), RBM_NORMAL, RelationData::rd_node, ReadBufferExtended(), RelationGetRelationName, RelationNeedsWAL, START_CRIT_SECTION, IndexVacuumInfo::strategy, IndexTupleData::t_tid, IndexBulkDeleteResult::tuples_removed, UnlockReleaseBuffer(), and vacuum_delay_point().

{
    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    rel = info->index;
    GistBDItem *stack,
               *ptr;

    /* first time through? */
    if (stats == NULL)
        stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
    /* we'll re-count the tuples each time */
    stats->estimated_count = false;
    stats->num_index_tuples = 0;

    stack = (GistBDItem *) palloc0(sizeof(GistBDItem));
    stack->blkno = GIST_ROOT_BLKNO;

    while (stack)
    {
        Buffer      buffer;
        Page        page;
        OffsetNumber i,
                    maxoff;
        IndexTuple  idxtuple;
        ItemId      iid;

        buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
                                    RBM_NORMAL, info->strategy);
        LockBuffer(buffer, GIST_SHARE);
        gistcheckpage(rel, buffer);
        page = (Page) BufferGetPage(buffer);

        if (GistPageIsLeaf(page))
        {
            OffsetNumber todelete[MaxOffsetNumber];
            int         ntodelete = 0;

            LockBuffer(buffer, GIST_UNLOCK);
            LockBuffer(buffer, GIST_EXCLUSIVE);

            page = (Page) BufferGetPage(buffer);
            if (stack->blkno == GIST_ROOT_BLKNO && !GistPageIsLeaf(page))
            {
                /* only the root can become non-leaf during relock */
                UnlockReleaseBuffer(buffer);
                /* one more check */
                continue;
            }

            /*
             * check for split proceeded after look at parent, we should check
             * it after relock
             */
            pushStackIfSplited(page, stack);

            /*
             * Remove deletable tuples from page
             */

            maxoff = PageGetMaxOffsetNumber(page);

            for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
            {
                iid = PageGetItemId(page, i);
                idxtuple = (IndexTuple) PageGetItem(page, iid);

                if (callback(&(idxtuple->t_tid), callback_state))
                {
                    todelete[ntodelete] = i - ntodelete;
                    ntodelete++;
                    stats->tuples_removed += 1;
                }
                else
                    stats->num_index_tuples += 1;
            }

            if (ntodelete)
            {
                START_CRIT_SECTION();

                MarkBufferDirty(buffer);

                for (i = 0; i < ntodelete; i++)
                    PageIndexTupleDelete(page, todelete[i]);
                GistMarkTuplesDeleted(page);

                if (RelationNeedsWAL(rel))
                {
                    XLogRecPtr  recptr;

                    recptr = gistXLogUpdate(rel->rd_node, buffer,
                                            todelete, ntodelete,
                                            NULL, 0, InvalidBuffer);
                    PageSetLSN(page, recptr);
                }
                else
                    PageSetLSN(page, gistGetFakeLSN(rel));

                END_CRIT_SECTION();
            }

        }
        else
        {
            /* check for split proceeded after look at parent */
            pushStackIfSplited(page, stack);

            maxoff = PageGetMaxOffsetNumber(page);

            for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
            {
                iid = PageGetItemId(page, i);
                idxtuple = (IndexTuple) PageGetItem(page, iid);

                ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
                ptr->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
                ptr->parentlsn = PageGetLSN(page);
                ptr->next = stack->next;
                stack->next = ptr;

                if (GistTupleIsInvalid(idxtuple))
                    ereport(LOG,
                            (errmsg("index \"%s\" contains an inner tuple marked as invalid",
                                    RelationGetRelationName(rel)),
                             errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
                             errhint("Please REINDEX it.")));
            }
        }

        UnlockReleaseBuffer(buffer);

        ptr = stack->next;
        pfree(stack);
        stack = ptr;

        vacuum_delay_point();
    }

    PG_RETURN_POINTER(stats);
}

Datum gistvacuumcleanup ( PG_FUNCTION_ARGS   ) 

Definition at line 29 of file gistvacuum.c.

References IndexVacuumInfo::analyze_only, BufferGetPage, IndexVacuumInfo::estimated_count, IndexBulkDeleteResult::estimated_count, ExclusiveLock, GIST_ROOT_BLKNO, GIST_SHARE, GistPageIsDeleted, IndexVacuumInfo::index, IndexFreeSpaceMapVacuum(), LockBuffer(), LockRelationForExtension(), MAIN_FORKNUM, NULL, IndexVacuumInfo::num_heap_tuples, IndexBulkDeleteResult::num_index_tuples, IndexBulkDeleteResult::num_pages, PageIsNew, 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    rel = info->index;
    BlockNumber npages,
                blkno;
    BlockNumber totFreePages;
    bool        needLock;

    /* No-op in ANALYZE ONLY mode */
    if (info->analyze_only)
        PG_RETURN_POINTER(stats);

    /* Set up all-zero stats if gistbulkdelete wasn't called */
    if (stats == NULL)
    {
        stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
        /* use heap's tuple count */
        stats->num_index_tuples = info->num_heap_tuples;
        stats->estimated_count = info->estimated_count;

        /*
         * XXX the above is wrong if index is partial.  Would it be OK to just
         * return NULL, or is there work we must do below?
         */
    }

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

    /* try to find deleted pages */
    if (needLock)
        LockRelationForExtension(rel, ExclusiveLock);
    npages = RelationGetNumberOfBlocks(rel);
    if (needLock)
        UnlockRelationForExtension(rel, ExclusiveLock);

    totFreePages = 0;
    for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
    {
        Buffer      buffer;
        Page        page;

        vacuum_delay_point();

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

        if (PageIsNew(page) || GistPageIsDeleted(page))
        {
            totFreePages++;
            RecordFreeIndexPage(rel, blkno);
        }
        UnlockReleaseBuffer(buffer);
    }

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

    /* return statistics */
    stats->pages_free = totFreePages;
    if (needLock)
        LockRelationForExtension(rel, ExclusiveLock);
    stats->num_pages = RelationGetNumberOfBlocks(rel);
    if (needLock)
        UnlockRelationForExtension(rel, ExclusiveLock);

    PG_RETURN_POINTER(stats);
}

static void pushStackIfSplited ( Page  page,
GistBDItem stack 
) [static]

Definition at line 112 of file gistvacuum.c.

References GistBDItem::blkno, GIST_ROOT_BLKNO, GistFollowRight, GistPageGetNSN, GistPageGetOpaque, InvalidBlockNumber, GistBDItem::next, palloc(), GistBDItem::parentlsn, GISTPageOpaqueData::rightlink, and XLogRecPtrIsInvalid.

Referenced by gistbulkdelete().

{
    GISTPageOpaque opaque = GistPageGetOpaque(page);

    if (stack->blkno != GIST_ROOT_BLKNO && !XLogRecPtrIsInvalid(stack->parentlsn) &&
        (GistFollowRight(page) || stack->parentlsn < GistPageGetNSN(page)) &&
        opaque->rightlink != InvalidBlockNumber /* sanity check */ )
    {
        /* split page detected, install right link to the stack */

        GistBDItem *ptr = (GistBDItem *) palloc(sizeof(GistBDItem));

        ptr->blkno = opaque->rightlink;
        ptr->parentlsn = stack->parentlsn;
        ptr->next = stack->next;
        stack->next = ptr;
    }
}