LTP GCOV extension - code coverage report
Current view: directory - access/gin - ginvacuum.c
Test: unnamed
Date: 2008-07-03 Instrumented lines: 325
Code covered: 11.7 % Executed lines: 38
Legend: not executed executed

       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * ginvacuum.c
       4                 :  *        delete & vacuum routines for the postgres GIN
       5                 :  *
       6                 :  *
       7                 :  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
       8                 :  * Portions Copyright (c) 1994, Regents of the University of California
       9                 :  *
      10                 :  * IDENTIFICATION
      11                 :  *                      $PostgreSQL: pgsql/src/backend/access/gin/ginvacuum.c,v 1.18 2007/11/15 21:14:31 momjian Exp $
      12                 :  *-------------------------------------------------------------------------
      13                 :  */
      14                 : 
      15                 : #include "postgres.h"
      16                 : #include "access/genam.h"
      17                 : #include "access/gin.h"
      18                 : #include "access/heapam.h"
      19                 : #include "miscadmin.h"
      20                 : #include "storage/freespace.h"
      21                 : #include "storage/freespace.h"
      22                 : #include "commands/vacuum.h"
      23                 : 
      24                 : typedef struct
      25                 : {
      26                 :         Relation        index;
      27                 :         IndexBulkDeleteResult *result;
      28                 :         IndexBulkDeleteCallback callback;
      29                 :         void       *callback_state;
      30                 :         GinState        ginstate;
      31                 :         BufferAccessStrategy strategy;
      32                 : } GinVacuumState;
      33                 : 
      34                 : 
      35                 : /*
      36                 :  * Cleans array of ItemPointer (removes dead pointers)
      37                 :  * Results are always stored in *cleaned, which will be allocated
      38                 :  * if it's needed. In case of *cleaned!=NULL caller is responsible to
      39                 :  * have allocated enough space. *cleaned and items may point to the same
      40                 :  * memory address.
      41                 :  */
      42                 : 
      43                 : static uint32
      44                 : ginVacuumPostingList(GinVacuumState *gvs, ItemPointerData *items, uint32 nitem, ItemPointerData **cleaned)
      45               0 : {
      46                 :         uint32          i,
      47               0 :                                 j = 0;
      48                 : 
      49                 :         /*
      50                 :          * just scan over ItemPointer array
      51                 :          */
      52                 : 
      53               0 :         for (i = 0; i < nitem; i++)
      54                 :         {
      55               0 :                 if (gvs->callback(items + i, gvs->callback_state))
      56                 :                 {
      57               0 :                         gvs->result->tuples_removed += 1;
      58               0 :                         if (!*cleaned)
      59                 :                         {
      60               0 :                                 *cleaned = (ItemPointerData *) palloc(sizeof(ItemPointerData) * nitem);
      61               0 :                                 if (i != 0)
      62               0 :                                         memcpy(*cleaned, items, sizeof(ItemPointerData) * i);
      63                 :                         }
      64                 :                 }
      65                 :                 else
      66                 :                 {
      67               0 :                         gvs->result->num_index_tuples += 1;
      68               0 :                         if (i != j)
      69               0 :                                 (*cleaned)[j] = items[i];
      70               0 :                         j++;
      71                 :                 }
      72                 :         }
      73                 : 
      74               0 :         return j;
      75                 : }
      76                 : 
      77                 : /*
      78                 :  * fills WAL record for vacuum leaf page
      79                 :  */
      80                 : static void
      81                 : xlogVacuumPage(Relation index, Buffer buffer)
      82               0 : {
      83               0 :         Page            page = BufferGetPage(buffer);
      84                 :         XLogRecPtr      recptr;
      85                 :         XLogRecData rdata[3];
      86                 :         ginxlogVacuumPage data;
      87                 :         char       *backup;
      88                 :         char            itups[BLCKSZ];
      89               0 :         uint32          len = 0;
      90                 : 
      91                 :         Assert(GinPageIsLeaf(page));
      92                 : 
      93               0 :         if (index->rd_istemp)
      94               0 :                 return;
      95                 : 
      96               0 :         data.node = index->rd_node;
      97               0 :         data.blkno = BufferGetBlockNumber(buffer);
      98                 : 
      99               0 :         if (GinPageIsData(page))
     100                 :         {
     101               0 :                 backup = GinDataPageGetData(page);
     102               0 :                 data.nitem = GinPageGetOpaque(page)->maxoff;
     103               0 :                 if (data.nitem)
     104               0 :                         len = MAXALIGN(sizeof(ItemPointerData) * data.nitem);
     105                 :         }
     106                 :         else
     107                 :         {
     108                 :                 char       *ptr;
     109                 :                 OffsetNumber i;
     110                 : 
     111               0 :                 ptr = backup = itups;
     112               0 :                 for (i = FirstOffsetNumber; i <= PageGetMaxOffsetNumber(page); i++)
     113                 :                 {
     114               0 :                         IndexTuple      itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, i));
     115                 : 
     116               0 :                         memcpy(ptr, itup, IndexTupleSize(itup));
     117               0 :                         ptr += MAXALIGN(IndexTupleSize(itup));
     118                 :                 }
     119                 : 
     120               0 :                 data.nitem = PageGetMaxOffsetNumber(page);
     121               0 :                 len = ptr - backup;
     122                 :         }
     123                 : 
     124               0 :         rdata[0].buffer = buffer;
     125               0 :         rdata[0].buffer_std = (GinPageIsData(page)) ? FALSE : TRUE;
     126               0 :         rdata[0].len = 0;
     127               0 :         rdata[0].data = NULL;
     128               0 :         rdata[0].next = rdata + 1;
     129                 : 
     130               0 :         rdata[1].buffer = InvalidBuffer;
     131               0 :         rdata[1].len = sizeof(ginxlogVacuumPage);
     132               0 :         rdata[1].data = (char *) &data;
     133                 : 
     134               0 :         if (len == 0)
     135                 :         {
     136               0 :                 rdata[1].next = NULL;
     137                 :         }
     138                 :         else
     139                 :         {
     140               0 :                 rdata[1].next = rdata + 2;
     141                 : 
     142               0 :                 rdata[2].buffer = InvalidBuffer;
     143               0 :                 rdata[2].len = len;
     144               0 :                 rdata[2].data = backup;
     145               0 :                 rdata[2].next = NULL;
     146                 :         }
     147                 : 
     148               0 :         recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_VACUUM_PAGE, rdata);
     149               0 :         PageSetLSN(page, recptr);
     150               0 :         PageSetTLI(page, ThisTimeLineID);
     151                 : }
     152                 : 
     153                 : static bool
     154                 : ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, Buffer *rootBuffer)
     155               0 : {
     156               0 :         Buffer          buffer = ReadBufferWithStrategy(gvs->index, blkno, gvs->strategy);
     157               0 :         Page            page = BufferGetPage(buffer);
     158               0 :         bool            hasVoidPage = FALSE;
     159                 : 
     160                 :         /*
     161                 :          * We should be sure that we don't concurrent with inserts, insert process
     162                 :          * never release root page until end (but it can unlock it and lock
     163                 :          * again). New scan can't start but previously started ones work
     164                 :          * concurrently.
     165                 :          */
     166                 : 
     167               0 :         if (isRoot)
     168               0 :                 LockBufferForCleanup(buffer);
     169                 :         else
     170               0 :                 LockBuffer(buffer, GIN_EXCLUSIVE);
     171                 : 
     172                 :         Assert(GinPageIsData(page));
     173                 : 
     174               0 :         if (GinPageIsLeaf(page))
     175                 :         {
     176                 :                 OffsetNumber newMaxOff,
     177               0 :                                         oldMaxOff = GinPageGetOpaque(page)->maxoff;
     178               0 :                 ItemPointerData *cleaned = NULL;
     179                 : 
     180               0 :                 newMaxOff = ginVacuumPostingList(gvs,
     181                 :                                 (ItemPointer) GinDataPageGetData(page), oldMaxOff, &cleaned);
     182                 : 
     183                 :                 /* saves changes about deleted tuple ... */
     184               0 :                 if (oldMaxOff != newMaxOff)
     185                 :                 {
     186                 : 
     187               0 :                         START_CRIT_SECTION();
     188                 : 
     189               0 :                         if (newMaxOff > 0)
     190               0 :                                 memcpy(GinDataPageGetData(page), cleaned, sizeof(ItemPointerData) * newMaxOff);
     191               0 :                         pfree(cleaned);
     192               0 :                         GinPageGetOpaque(page)->maxoff = newMaxOff;
     193                 : 
     194               0 :                         MarkBufferDirty(buffer);
     195               0 :                         xlogVacuumPage(gvs->index, buffer);
     196                 : 
     197               0 :                         END_CRIT_SECTION();
     198                 : 
     199                 :                         /* if root is a leaf page, we don't desire further processing */
     200               0 :                         if (!isRoot && GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
     201               0 :                                 hasVoidPage = TRUE;
     202                 :                 }
     203                 :         }
     204                 :         else
     205                 :         {
     206                 :                 OffsetNumber i;
     207               0 :                 bool            isChildHasVoid = FALSE;
     208                 : 
     209               0 :                 for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff; i++)
     210                 :                 {
     211               0 :                         PostingItem *pitem = (PostingItem *) GinDataPageGetItem(page, i);
     212                 : 
     213               0 :                         if (ginVacuumPostingTreeLeaves(gvs, PostingItemGetBlockNumber(pitem), FALSE, NULL))
     214               0 :                                 isChildHasVoid = TRUE;
     215                 :                 }
     216                 : 
     217               0 :                 if (isChildHasVoid)
     218               0 :                         hasVoidPage = TRUE;
     219                 :         }
     220                 : 
     221                 :         /*
     222                 :          * if we have root and theres void pages in tree, then we don't release
     223                 :          * lock to go further processing and guarantee that tree is unused
     224                 :          */
     225               0 :         if (!(isRoot && hasVoidPage))
     226                 :         {
     227               0 :                 UnlockReleaseBuffer(buffer);
     228                 :         }
     229                 :         else
     230                 :         {
     231                 :                 Assert(rootBuffer);
     232               0 :                 *rootBuffer = buffer;
     233                 :         }
     234                 : 
     235               0 :         return hasVoidPage;
     236                 : }
     237                 : 
     238                 : static void
     239                 : ginDeletePage(GinVacuumState *gvs, BlockNumber deleteBlkno, BlockNumber leftBlkno,
     240                 :                           BlockNumber parentBlkno, OffsetNumber myoff, bool isParentRoot)
     241               0 : {
     242               0 :         Buffer          dBuffer = ReadBufferWithStrategy(gvs->index, deleteBlkno, gvs->strategy);
     243                 :         Buffer          lBuffer = (leftBlkno == InvalidBlockNumber) ?
     244               0 :         InvalidBuffer : ReadBufferWithStrategy(gvs->index, leftBlkno, gvs->strategy);
     245               0 :         Buffer          pBuffer = ReadBufferWithStrategy(gvs->index, parentBlkno, gvs->strategy);
     246                 :         Page            page,
     247                 :                                 parentPage;
     248                 : 
     249               0 :         LockBuffer(dBuffer, GIN_EXCLUSIVE);
     250               0 :         if (!isParentRoot)                      /* parent is already locked by
     251                 :                                                                  * LockBufferForCleanup() */
     252               0 :                 LockBuffer(pBuffer, GIN_EXCLUSIVE);
     253               0 :         if (leftBlkno != InvalidBlockNumber)
     254               0 :                 LockBuffer(lBuffer, GIN_EXCLUSIVE);
     255                 : 
     256               0 :         START_CRIT_SECTION();
     257                 : 
     258               0 :         if (leftBlkno != InvalidBlockNumber)
     259                 :         {
     260                 :                 BlockNumber rightlink;
     261                 : 
     262               0 :                 page = BufferGetPage(dBuffer);
     263               0 :                 rightlink = GinPageGetOpaque(page)->rightlink;
     264                 : 
     265               0 :                 page = BufferGetPage(lBuffer);
     266               0 :                 GinPageGetOpaque(page)->rightlink = rightlink;
     267                 :         }
     268                 : 
     269               0 :         parentPage = BufferGetPage(pBuffer);
     270                 : #ifdef USE_ASSERT_CHECKING
     271                 :         do
     272                 :         {
     273                 :                 PostingItem *tod = (PostingItem *) GinDataPageGetItem(parentPage, myoff);
     274                 : 
     275                 :                 Assert(PostingItemGetBlockNumber(tod) == deleteBlkno);
     276                 :         } while (0);
     277                 : #endif
     278               0 :         PageDeletePostingItem(parentPage, myoff);
     279                 : 
     280               0 :         page = BufferGetPage(dBuffer);
     281                 : 
     282                 :         /*
     283                 :          * we shouldn't change rightlink field to save workability of running
     284                 :          * search scan
     285                 :          */
     286               0 :         GinPageGetOpaque(page)->flags = GIN_DELETED;
     287                 : 
     288               0 :         MarkBufferDirty(pBuffer);
     289               0 :         if (leftBlkno != InvalidBlockNumber)
     290               0 :                 MarkBufferDirty(lBuffer);
     291               0 :         MarkBufferDirty(dBuffer);
     292                 : 
     293               0 :         if (!gvs->index->rd_istemp)
     294                 :         {
     295                 :                 XLogRecPtr      recptr;
     296                 :                 XLogRecData rdata[4];
     297                 :                 ginxlogDeletePage data;
     298                 :                 int                     n;
     299                 : 
     300               0 :                 data.node = gvs->index->rd_node;
     301               0 :                 data.blkno = deleteBlkno;
     302               0 :                 data.parentBlkno = parentBlkno;
     303               0 :                 data.parentOffset = myoff;
     304               0 :                 data.leftBlkno = leftBlkno;
     305               0 :                 data.rightLink = GinPageGetOpaque(page)->rightlink;
     306                 : 
     307               0 :                 rdata[0].buffer = dBuffer;
     308               0 :                 rdata[0].buffer_std = FALSE;
     309               0 :                 rdata[0].data = NULL;
     310               0 :                 rdata[0].len = 0;
     311               0 :                 rdata[0].next = rdata + 1;
     312                 : 
     313               0 :                 rdata[1].buffer = pBuffer;
     314               0 :                 rdata[1].buffer_std = FALSE;
     315               0 :                 rdata[1].data = NULL;
     316               0 :                 rdata[1].len = 0;
     317               0 :                 rdata[1].next = rdata + 2;
     318                 : 
     319               0 :                 if (leftBlkno != InvalidBlockNumber)
     320                 :                 {
     321               0 :                         rdata[2].buffer = lBuffer;
     322               0 :                         rdata[2].buffer_std = FALSE;
     323               0 :                         rdata[2].data = NULL;
     324               0 :                         rdata[2].len = 0;
     325               0 :                         rdata[2].next = rdata + 3;
     326               0 :                         n = 3;
     327                 :                 }
     328                 :                 else
     329               0 :                         n = 2;
     330                 : 
     331               0 :                 rdata[n].buffer = InvalidBuffer;
     332               0 :                 rdata[n].buffer_std = FALSE;
     333               0 :                 rdata[n].len = sizeof(ginxlogDeletePage);
     334               0 :                 rdata[n].data = (char *) &data;
     335               0 :                 rdata[n].next = NULL;
     336                 : 
     337               0 :                 recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_PAGE, rdata);
     338               0 :                 PageSetLSN(page, recptr);
     339               0 :                 PageSetTLI(page, ThisTimeLineID);
     340               0 :                 PageSetLSN(parentPage, recptr);
     341               0 :                 PageSetTLI(parentPage, ThisTimeLineID);
     342               0 :                 if (leftBlkno != InvalidBlockNumber)
     343                 :                 {
     344               0 :                         page = BufferGetPage(lBuffer);
     345               0 :                         PageSetLSN(page, recptr);
     346               0 :                         PageSetTLI(page, ThisTimeLineID);
     347                 :                 }
     348                 :         }
     349                 : 
     350               0 :         if (!isParentRoot)
     351               0 :                 LockBuffer(pBuffer, GIN_UNLOCK);
     352               0 :         ReleaseBuffer(pBuffer);
     353                 : 
     354               0 :         if (leftBlkno != InvalidBlockNumber)
     355               0 :                 UnlockReleaseBuffer(lBuffer);
     356                 : 
     357               0 :         UnlockReleaseBuffer(dBuffer);
     358                 : 
     359               0 :         END_CRIT_SECTION();
     360                 : 
     361               0 :         gvs->result->pages_deleted++;
     362               0 : }
     363                 : 
     364                 : typedef struct DataPageDeleteStack
     365                 : {
     366                 :         struct DataPageDeleteStack *child;
     367                 :         struct DataPageDeleteStack *parent;
     368                 : 
     369                 :         BlockNumber blkno;                      /* current block number */
     370                 :         BlockNumber leftBlkno;          /* rightest non-deleted page on left */
     371                 :         bool            isRoot;
     372                 : } DataPageDeleteStack;
     373                 : 
     374                 : /*
     375                 :  * scans posting tree and deletes empty pages
     376                 :  */
     377                 : static bool
     378                 : ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDeleteStack *parent, OffsetNumber myoff)
     379               0 : {
     380                 :         DataPageDeleteStack *me;
     381                 :         Buffer          buffer;
     382                 :         Page            page;
     383               0 :         bool            meDelete = FALSE;
     384                 : 
     385               0 :         if (isRoot)
     386                 :         {
     387               0 :                 me = parent;
     388                 :         }
     389                 :         else
     390                 :         {
     391               0 :                 if (!parent->child)
     392                 :                 {
     393               0 :                         me = (DataPageDeleteStack *) palloc0(sizeof(DataPageDeleteStack));
     394               0 :                         me->parent = parent;
     395               0 :                         parent->child = me;
     396               0 :                         me->leftBlkno = InvalidBlockNumber;
     397                 :                 }
     398                 :                 else
     399               0 :                         me = parent->child;
     400                 :         }
     401                 : 
     402               0 :         buffer = ReadBufferWithStrategy(gvs->index, blkno, gvs->strategy);
     403               0 :         page = BufferGetPage(buffer);
     404                 : 
     405                 :         Assert(GinPageIsData(page));
     406                 : 
     407               0 :         if (!GinPageIsLeaf(page))
     408                 :         {
     409                 :                 OffsetNumber i;
     410                 : 
     411               0 :                 me->blkno = blkno;
     412               0 :                 for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff; i++)
     413                 :                 {
     414               0 :                         PostingItem *pitem = (PostingItem *) GinDataPageGetItem(page, i);
     415                 : 
     416               0 :                         if (ginScanToDelete(gvs, PostingItemGetBlockNumber(pitem), FALSE, me, i))
     417               0 :                                 i--;
     418                 :                 }
     419                 :         }
     420                 : 
     421               0 :         if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
     422                 :         {
     423               0 :                 if (!(me->leftBlkno == InvalidBlockNumber && GinPageRightMost(page)))
     424                 :                 {
     425                 :                         /* we never delete right most branch */
     426                 :                         Assert(!isRoot);
     427               0 :                         if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
     428                 :                         {
     429               0 :                                 ginDeletePage(gvs, blkno, me->leftBlkno, me->parent->blkno, myoff, me->parent->isRoot);
     430               0 :                                 meDelete = TRUE;
     431                 :                         }
     432                 :                 }
     433                 :         }
     434                 : 
     435               0 :         ReleaseBuffer(buffer);
     436                 : 
     437               0 :         if (!meDelete)
     438               0 :                 me->leftBlkno = blkno;
     439                 : 
     440               0 :         return meDelete;
     441                 : }
     442                 : 
     443                 : static void
     444                 : ginVacuumPostingTree(GinVacuumState *gvs, BlockNumber rootBlkno)
     445               0 : {
     446               0 :         Buffer          rootBuffer = InvalidBuffer;
     447                 :         DataPageDeleteStack root,
     448                 :                            *ptr,
     449                 :                            *tmp;
     450                 : 
     451               0 :         if (ginVacuumPostingTreeLeaves(gvs, rootBlkno, TRUE, &rootBuffer) == FALSE)
     452                 :         {
     453                 :                 Assert(rootBuffer == InvalidBuffer);
     454               0 :                 return;
     455                 :         }
     456                 : 
     457               0 :         memset(&root, 0, sizeof(DataPageDeleteStack));
     458               0 :         root.leftBlkno = InvalidBlockNumber;
     459               0 :         root.isRoot = TRUE;
     460                 : 
     461               0 :         vacuum_delay_point();
     462                 : 
     463               0 :         ginScanToDelete(gvs, rootBlkno, TRUE, &root, InvalidOffsetNumber);
     464                 : 
     465               0 :         ptr = root.child;
     466               0 :         while (ptr)
     467                 :         {
     468               0 :                 tmp = ptr->child;
     469               0 :                 pfree(ptr);
     470               0 :                 ptr = tmp;
     471                 :         }
     472                 : 
     473               0 :         UnlockReleaseBuffer(rootBuffer);
     474                 : }
     475                 : 
     476                 : /*
     477                 :  * returns modified page or NULL if page isn't modified.
     478                 :  * Function works with original page until first change is occurred,
     479                 :  * then page is copied into temporary one.
     480                 :  */
     481                 : static Page
     482                 : ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint32 *nroot)
     483               0 : {
     484               0 :         Page            origpage = BufferGetPage(buffer),
     485                 :                                 tmppage;
     486                 :         OffsetNumber i,
     487               0 :                                 maxoff = PageGetMaxOffsetNumber(origpage);
     488                 : 
     489               0 :         tmppage = origpage;
     490                 : 
     491               0 :         *nroot = 0;
     492                 : 
     493               0 :         for (i = FirstOffsetNumber; i <= maxoff; i++)
     494                 :         {
     495               0 :                 IndexTuple      itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
     496                 : 
     497               0 :                 if (GinIsPostingTree(itup))
     498                 :                 {
     499                 :                         /*
     500                 :                          * store posting tree's roots for further processing, we can't
     501                 :                          * vacuum it just now due to risk of deadlocks with scans/inserts
     502                 :                          */
     503               0 :                         roots[*nroot] = GinItemPointerGetBlockNumber(&itup->t_tid);
     504               0 :                         (*nroot)++;
     505                 :                 }
     506               0 :                 else if (GinGetNPosting(itup) > 0)
     507                 :                 {
     508                 :                         /*
     509                 :                          * if we already create temporary page, we will make changes in
     510                 :                          * place
     511                 :                          */
     512               0 :                         ItemPointerData *cleaned = (tmppage == origpage) ? NULL : GinGetPosting(itup);
     513               0 :                         uint32          newN = ginVacuumPostingList(gvs, GinGetPosting(itup), GinGetNPosting(itup), &cleaned);
     514                 : 
     515               0 :                         if (GinGetNPosting(itup) != newN)
     516                 :                         {
     517                 :                                 bool            isnull;
     518                 :                                 Datum           value;
     519                 : 
     520                 :                                 /*
     521                 :                                  * Some ItemPointers was deleted, so we should remake our
     522                 :                                  * tuple
     523                 :                                  */
     524                 : 
     525               0 :                                 if (tmppage == origpage)
     526                 :                                 {
     527                 :                                         /*
     528                 :                                          * On first difference we create temporary page in memory
     529                 :                                          * and copies content in to it.
     530                 :                                          */
     531               0 :                                         tmppage = GinPageGetCopyPage(origpage);
     532                 : 
     533               0 :                                         if (newN > 0)
     534                 :                                         {
     535               0 :                                                 Size            pos = ((char *) GinGetPosting(itup)) - ((char *) origpage);
     536                 : 
     537               0 :                                                 memcpy(tmppage + pos, cleaned, sizeof(ItemPointerData) * newN);
     538                 :                                         }
     539                 : 
     540               0 :                                         pfree(cleaned);
     541                 : 
     542                 :                                         /* set itup pointer to new page */
     543               0 :                                         itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
     544                 :                                 }
     545                 : 
     546               0 :                                 value = index_getattr(itup, FirstOffsetNumber, gvs->ginstate.tupdesc, &isnull);
     547               0 :                                 itup = GinFormTuple(&gvs->ginstate, value, GinGetPosting(itup), newN);
     548               0 :                                 PageIndexTupleDelete(tmppage, i);
     549                 : 
     550               0 :                                 if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false, false) != i)
     551               0 :                                         elog(ERROR, "failed to add item to index page in \"%s\"",
     552                 :                                                  RelationGetRelationName(gvs->index));
     553                 : 
     554               0 :                                 pfree(itup);
     555                 :                         }
     556                 :                 }
     557                 :         }
     558                 : 
     559               0 :         return (tmppage == origpage) ? NULL : tmppage;
     560                 : }
     561                 : 
     562                 : Datum
     563                 : ginbulkdelete(PG_FUNCTION_ARGS)
     564               0 : {
     565               0 :         IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
     566               0 :         IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
     567               0 :         IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
     568               0 :         void       *callback_state = (void *) PG_GETARG_POINTER(3);
     569               0 :         Relation        index = info->index;
     570               0 :         BlockNumber blkno = GIN_ROOT_BLKNO;
     571                 :         GinVacuumState gvs;
     572                 :         Buffer          buffer;
     573                 :         BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
     574                 :         uint32          nRoot;
     575                 : 
     576                 :         /* first time through? */
     577               0 :         if (stats == NULL)
     578               0 :                 stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
     579                 :         /* we'll re-count the tuples each time */
     580               0 :         stats->num_index_tuples = 0;
     581                 : 
     582               0 :         gvs.index = index;
     583               0 :         gvs.result = stats;
     584               0 :         gvs.callback = callback;
     585               0 :         gvs.callback_state = callback_state;
     586               0 :         gvs.strategy = info->strategy;
     587               0 :         initGinState(&gvs.ginstate, index);
     588                 : 
     589               0 :         buffer = ReadBufferWithStrategy(index, blkno, info->strategy);
     590                 : 
     591                 :         /* find leaf page */
     592                 :         for (;;)
     593                 :         {
     594               0 :                 Page            page = BufferGetPage(buffer);
     595                 :                 IndexTuple      itup;
     596                 : 
     597               0 :                 LockBuffer(buffer, GIN_SHARE);
     598                 : 
     599                 :                 Assert(!GinPageIsData(page));
     600                 : 
     601               0 :                 if (GinPageIsLeaf(page))
     602                 :                 {
     603               0 :                         LockBuffer(buffer, GIN_UNLOCK);
     604               0 :                         LockBuffer(buffer, GIN_EXCLUSIVE);
     605                 : 
     606               0 :                         if (blkno == GIN_ROOT_BLKNO && !GinPageIsLeaf(page))
     607                 :                         {
     608               0 :                                 LockBuffer(buffer, GIN_UNLOCK);
     609               0 :                                 continue;               /* check it one more */
     610                 :                         }
     611                 :                         break;
     612                 :                 }
     613                 : 
     614                 :                 Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
     615                 : 
     616               0 :                 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
     617               0 :                 blkno = GinItemPointerGetBlockNumber(&(itup)->t_tid);
     618                 :                 Assert(blkno != InvalidBlockNumber);
     619                 : 
     620               0 :                 UnlockReleaseBuffer(buffer);
     621               0 :                 buffer = ReadBufferWithStrategy(index, blkno, info->strategy);
     622                 :         }
     623                 : 
     624                 :         /* right now we found leftmost page in entry's BTree */
     625                 : 
     626                 :         for (;;)
     627                 :         {
     628               0 :                 Page            page = BufferGetPage(buffer);
     629                 :                 Page            resPage;
     630                 :                 uint32          i;
     631                 : 
     632                 :                 Assert(!GinPageIsData(page));
     633                 : 
     634               0 :                 resPage = ginVacuumEntryPage(&gvs, buffer, rootOfPostingTree, &nRoot);
     635                 : 
     636               0 :                 blkno = GinPageGetOpaque(page)->rightlink;
     637                 : 
     638               0 :                 if (resPage)
     639                 :                 {
     640               0 :                         START_CRIT_SECTION();
     641               0 :                         PageRestoreTempPage(resPage, page);
     642               0 :                         MarkBufferDirty(buffer);
     643               0 :                         xlogVacuumPage(gvs.index, buffer);
     644               0 :                         UnlockReleaseBuffer(buffer);
     645               0 :                         END_CRIT_SECTION();
     646                 :                 }
     647                 :                 else
     648                 :                 {
     649               0 :                         UnlockReleaseBuffer(buffer);
     650                 :                 }
     651                 : 
     652               0 :                 vacuum_delay_point();
     653                 : 
     654               0 :                 for (i = 0; i < nRoot; i++)
     655                 :                 {
     656               0 :                         ginVacuumPostingTree(&gvs, rootOfPostingTree[i]);
     657               0 :                         vacuum_delay_point();
     658                 :                 }
     659                 : 
     660               0 :                 if (blkno == InvalidBlockNumber)                /* rightmost page */
     661               0 :                         break;
     662                 : 
     663               0 :                 buffer = ReadBufferWithStrategy(index, blkno, info->strategy);
     664               0 :                 LockBuffer(buffer, GIN_EXCLUSIVE);
     665               0 :         }
     666                 : 
     667               0 :         PG_RETURN_POINTER(gvs.result);
     668                 : }
     669                 : 
     670                 : Datum
     671                 : ginvacuumcleanup(PG_FUNCTION_ARGS)
     672               2 : {
     673               2 :         IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
     674               2 :         IndexBulkDeleteResult *stats = (IndexBulkDeleteResult *) PG_GETARG_POINTER(1);
     675               2 :         Relation        index = info->index;
     676                 :         bool            needLock;
     677                 :         BlockNumber npages,
     678                 :                                 blkno;
     679                 :         BlockNumber totFreePages,
     680                 :                                 nFreePages,
     681                 :                            *freePages,
     682                 :                                 maxFreePages;
     683               2 :         BlockNumber lastBlock = GIN_ROOT_BLKNO,
     684               2 :                                 lastFilledBlock = GIN_ROOT_BLKNO;
     685                 : 
     686                 :         /* Set up all-zero stats if ginbulkdelete wasn't called */
     687               2 :         if (stats == NULL)
     688               2 :                 stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
     689                 : 
     690                 :         /*
     691                 :          * XXX we always report the heap tuple count as the number of index
     692                 :          * entries.  This is bogus if the index is partial, but it's real hard to
     693                 :          * tell how many distinct heap entries are referenced by a GIN index.
     694                 :          */
     695               2 :         stats->num_index_tuples = info->num_heap_tuples;
     696                 : 
     697                 :         /*
     698                 :          * If vacuum full, we already have exclusive lock on the index. Otherwise,
     699                 :          * need lock unless it's local to this backend.
     700                 :          */
     701               2 :         if (info->vacuum_full)
     702               0 :                 needLock = false;
     703                 :         else
     704               2 :                 needLock = !RELATION_IS_LOCAL(index);
     705                 : 
     706               2 :         if (needLock)
     707               2 :                 LockRelationForExtension(index, ExclusiveLock);
     708               2 :         npages = RelationGetNumberOfBlocks(index);
     709               2 :         if (needLock)
     710               2 :                 UnlockRelationForExtension(index, ExclusiveLock);
     711                 : 
     712               2 :         maxFreePages = npages;
     713               2 :         if (maxFreePages > MaxFSMPages)
     714               0 :                 maxFreePages = MaxFSMPages;
     715                 : 
     716               2 :         totFreePages = nFreePages = 0;
     717               2 :         freePages = (BlockNumber *) palloc(sizeof(BlockNumber) * maxFreePages);
     718                 : 
     719               4 :         for (blkno = GIN_ROOT_BLKNO + 1; blkno < npages; blkno++)
     720                 :         {
     721                 :                 Buffer          buffer;
     722                 :                 Page            page;
     723                 : 
     724               2 :                 vacuum_delay_point();
     725                 : 
     726               2 :                 buffer = ReadBufferWithStrategy(index, blkno, info->strategy);
     727               2 :                 LockBuffer(buffer, GIN_SHARE);
     728               2 :                 page = (Page) BufferGetPage(buffer);
     729                 : 
     730               2 :                 if (GinPageIsDeleted(page))
     731                 :                 {
     732               0 :                         if (nFreePages < maxFreePages)
     733               0 :                                 freePages[nFreePages++] = blkno;
     734               0 :                         totFreePages++;
     735                 :                 }
     736                 :                 else
     737               2 :                         lastFilledBlock = blkno;
     738                 : 
     739               2 :                 UnlockReleaseBuffer(buffer);
     740                 :         }
     741               2 :         lastBlock = npages - 1;
     742                 : 
     743               2 :         if (info->vacuum_full && nFreePages > 0)
     744                 :         {
     745                 :                 /* try to truncate index */
     746                 :                 int                     i;
     747                 : 
     748               0 :                 for (i = 0; i < nFreePages; i++)
     749               0 :                         if (freePages[i] >= lastFilledBlock)
     750                 :                         {
     751               0 :                                 totFreePages = nFreePages = i;
     752               0 :                                 break;
     753                 :                         }
     754                 : 
     755               0 :                 if (lastBlock > lastFilledBlock)
     756               0 :                         RelationTruncate(index, lastFilledBlock + 1);
     757                 : 
     758               0 :                 stats->pages_removed = lastBlock - lastFilledBlock;
     759                 :         }
     760                 : 
     761               2 :         RecordIndexFreeSpace(&index->rd_node, totFreePages, nFreePages, freePages);
     762               2 :         stats->pages_free = totFreePages;
     763                 : 
     764               2 :         if (needLock)
     765               2 :                 LockRelationForExtension(index, ExclusiveLock);
     766               2 :         stats->num_pages = RelationGetNumberOfBlocks(index);
     767               2 :         if (needLock)
     768               2 :                 UnlockRelationForExtension(index, ExclusiveLock);
     769                 : 
     770               2 :         PG_RETURN_POINTER(stats);
     771                 : }

Generated by: LTP GCOV extension version 1.5