LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistvacuum.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 89 101 88.1 %
Date: 2017-09-29 15:12:54 Functions: 3 3 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * gistvacuum.c
       4             :  *    vacuuming routines for the postgres GiST index access method.
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/access/gist/gistvacuum.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/genam.h"
      18             : #include "access/gist_private.h"
      19             : #include "commands/vacuum.h"
      20             : #include "miscadmin.h"
      21             : #include "storage/indexfsm.h"
      22             : #include "storage/lmgr.h"
      23             : 
      24             : 
      25             : /*
      26             :  * VACUUM cleanup: update FSM
      27             :  */
      28             : IndexBulkDeleteResult *
      29          17 : gistvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
      30             : {
      31          17 :     Relation    rel = info->index;
      32             :     BlockNumber npages,
      33             :                 blkno;
      34             :     BlockNumber totFreePages;
      35             :     bool        needLock;
      36             : 
      37             :     /* No-op in ANALYZE ONLY mode */
      38          17 :     if (info->analyze_only)
      39           2 :         return stats;
      40             : 
      41             :     /* Set up all-zero stats if gistbulkdelete wasn't called */
      42          15 :     if (stats == NULL)
      43             :     {
      44           9 :         stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
      45             :         /* use heap's tuple count */
      46           9 :         stats->num_index_tuples = info->num_heap_tuples;
      47           9 :         stats->estimated_count = info->estimated_count;
      48             : 
      49             :         /*
      50             :          * XXX the above is wrong if index is partial.  Would it be OK to just
      51             :          * return NULL, or is there work we must do below?
      52             :          */
      53             :     }
      54             : 
      55             :     /*
      56             :      * Need lock unless it's local to this backend.
      57             :      */
      58          15 :     needLock = !RELATION_IS_LOCAL(rel);
      59             : 
      60             :     /* try to find deleted pages */
      61          15 :     if (needLock)
      62          15 :         LockRelationForExtension(rel, ExclusiveLock);
      63          15 :     npages = RelationGetNumberOfBlocks(rel);
      64          15 :     if (needLock)
      65          15 :         UnlockRelationForExtension(rel, ExclusiveLock);
      66             : 
      67          15 :     totFreePages = 0;
      68        1471 :     for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
      69             :     {
      70             :         Buffer      buffer;
      71             :         Page        page;
      72             : 
      73        1456 :         vacuum_delay_point();
      74             : 
      75        1456 :         buffer = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL,
      76             :                                     info->strategy);
      77        1456 :         LockBuffer(buffer, GIST_SHARE);
      78        1456 :         page = (Page) BufferGetPage(buffer);
      79             : 
      80        1456 :         if (PageIsNew(page) || GistPageIsDeleted(page))
      81             :         {
      82           0 :             totFreePages++;
      83           0 :             RecordFreeIndexPage(rel, blkno);
      84             :         }
      85        1456 :         UnlockReleaseBuffer(buffer);
      86             :     }
      87             : 
      88             :     /* Finally, vacuum the FSM */
      89          15 :     IndexFreeSpaceMapVacuum(info->index);
      90             : 
      91             :     /* return statistics */
      92          15 :     stats->pages_free = totFreePages;
      93          15 :     if (needLock)
      94          15 :         LockRelationForExtension(rel, ExclusiveLock);
      95          15 :     stats->num_pages = RelationGetNumberOfBlocks(rel);
      96          15 :     if (needLock)
      97          15 :         UnlockRelationForExtension(rel, ExclusiveLock);
      98             : 
      99          15 :     return stats;
     100             : }
     101             : 
     102             : typedef struct GistBDItem
     103             : {
     104             :     GistNSN     parentlsn;
     105             :     BlockNumber blkno;
     106             :     struct GistBDItem *next;
     107             : } GistBDItem;
     108             : 
     109             : static void
     110         703 : pushStackIfSplited(Page page, GistBDItem *stack)
     111             : {
     112         703 :     GISTPageOpaque opaque = GistPageGetOpaque(page);
     113             : 
     114        1400 :     if (stack->blkno != GIST_ROOT_BLKNO && !XLogRecPtrIsInvalid(stack->parentlsn) &&
     115        1394 :         (GistFollowRight(page) || stack->parentlsn < GistPageGetNSN(page)) &&
     116           0 :         opaque->rightlink != InvalidBlockNumber /* sanity check */ )
     117             :     {
     118             :         /* split page detected, install right link to the stack */
     119             : 
     120           0 :         GistBDItem *ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
     121             : 
     122           0 :         ptr->blkno = opaque->rightlink;
     123           0 :         ptr->parentlsn = stack->parentlsn;
     124           0 :         ptr->next = stack->next;
     125           0 :         stack->next = ptr;
     126             :     }
     127         703 : }
     128             : 
     129             : 
     130             : /*
     131             :  * Bulk deletion of all index entries pointing to a set of heap tuples and
     132             :  * check invalid tuples left after upgrade.
     133             :  * The set of target tuples is specified via a callback routine that tells
     134             :  * whether any given heap tuple (identified by ItemPointer) is being deleted.
     135             :  *
     136             :  * Result: a palloc'd struct containing statistical info for VACUUM displays.
     137             :  */
     138             : IndexBulkDeleteResult *
     139           6 : gistbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
     140             :                IndexBulkDeleteCallback callback, void *callback_state)
     141             : {
     142           6 :     Relation    rel = info->index;
     143             :     GistBDItem *stack,
     144             :                *ptr;
     145             : 
     146             :     /* first time through? */
     147           6 :     if (stats == NULL)
     148           6 :         stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
     149             :     /* we'll re-count the tuples each time */
     150           6 :     stats->estimated_count = false;
     151           6 :     stats->num_index_tuples = 0;
     152             : 
     153           6 :     stack = (GistBDItem *) palloc0(sizeof(GistBDItem));
     154           6 :     stack->blkno = GIST_ROOT_BLKNO;
     155             : 
     156         715 :     while (stack)
     157             :     {
     158             :         Buffer      buffer;
     159             :         Page        page;
     160             :         OffsetNumber i,
     161             :                     maxoff;
     162             :         IndexTuple  idxtuple;
     163             :         ItemId      iid;
     164             : 
     165         703 :         buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
     166             :                                     RBM_NORMAL, info->strategy);
     167         703 :         LockBuffer(buffer, GIST_SHARE);
     168         703 :         gistcheckpage(rel, buffer);
     169         703 :         page = (Page) BufferGetPage(buffer);
     170             : 
     171         703 :         if (GistPageIsLeaf(page))
     172             :         {
     173             :             OffsetNumber todelete[MaxOffsetNumber];
     174         692 :             int         ntodelete = 0;
     175             : 
     176         692 :             LockBuffer(buffer, GIST_UNLOCK);
     177         692 :             LockBuffer(buffer, GIST_EXCLUSIVE);
     178             : 
     179         692 :             page = (Page) BufferGetPage(buffer);
     180         692 :             if (stack->blkno == GIST_ROOT_BLKNO && !GistPageIsLeaf(page))
     181             :             {
     182             :                 /* only the root can become non-leaf during relock */
     183           0 :                 UnlockReleaseBuffer(buffer);
     184             :                 /* one more check */
     185           0 :                 continue;
     186             :             }
     187             : 
     188             :             /*
     189             :              * check for split proceeded after look at parent, we should check
     190             :              * it after relock
     191             :              */
     192         692 :             pushStackIfSplited(page, stack);
     193             : 
     194             :             /*
     195             :              * Remove deletable tuples from page
     196             :              */
     197             : 
     198         692 :             maxoff = PageGetMaxOffsetNumber(page);
     199             : 
     200       70701 :             for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     201             :             {
     202       70009 :                 iid = PageGetItemId(page, i);
     203       70009 :                 idxtuple = (IndexTuple) PageGetItem(page, iid);
     204             : 
     205       70009 :                 if (callback(&(idxtuple->t_tid), callback_state))
     206       49999 :                     todelete[ntodelete++] = i;
     207             :                 else
     208       20010 :                     stats->num_index_tuples += 1;
     209             :             }
     210             : 
     211         692 :             stats->tuples_removed += ntodelete;
     212             : 
     213         692 :             if (ntodelete)
     214             :             {
     215         692 :                 START_CRIT_SECTION();
     216             : 
     217         692 :                 MarkBufferDirty(buffer);
     218             : 
     219         692 :                 PageIndexMultiDelete(page, todelete, ntodelete);
     220         692 :                 GistMarkTuplesDeleted(page);
     221             : 
     222         692 :                 if (RelationNeedsWAL(rel))
     223             :                 {
     224             :                     XLogRecPtr  recptr;
     225             : 
     226         692 :                     recptr = gistXLogUpdate(buffer,
     227             :                                             todelete, ntodelete,
     228             :                                             NULL, 0, InvalidBuffer);
     229         692 :                     PageSetLSN(page, recptr);
     230             :                 }
     231             :                 else
     232           0 :                     PageSetLSN(page, gistGetFakeLSN(rel));
     233             : 
     234         692 :                 END_CRIT_SECTION();
     235             :             }
     236             : 
     237             :         }
     238             :         else
     239             :         {
     240             :             /* check for split proceeded after look at parent */
     241          11 :             pushStackIfSplited(page, stack);
     242             : 
     243          11 :             maxoff = PageGetMaxOffsetNumber(page);
     244             : 
     245         708 :             for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     246             :             {
     247         697 :                 iid = PageGetItemId(page, i);
     248         697 :                 idxtuple = (IndexTuple) PageGetItem(page, iid);
     249             : 
     250         697 :                 ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
     251         697 :                 ptr->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
     252         697 :                 ptr->parentlsn = PageGetLSN(page);
     253         697 :                 ptr->next = stack->next;
     254         697 :                 stack->next = ptr;
     255             : 
     256         697 :                 if (GistTupleIsInvalid(idxtuple))
     257           0 :                     ereport(LOG,
     258             :                             (errmsg("index \"%s\" contains an inner tuple marked as invalid",
     259             :                                     RelationGetRelationName(rel)),
     260             :                              errdetail("This is caused by an incomplete page split at crash recovery before upgrading to PostgreSQL 9.1."),
     261             :                              errhint("Please REINDEX it.")));
     262             :             }
     263             :         }
     264             : 
     265         703 :         UnlockReleaseBuffer(buffer);
     266             : 
     267         703 :         ptr = stack->next;
     268         703 :         pfree(stack);
     269         703 :         stack = ptr;
     270             : 
     271         703 :         vacuum_delay_point();
     272             :     }
     273             : 
     274           6 :     return stats;
     275             : }

Generated by: LCOV version 1.11