LTP GCOV extension - code coverage report
Current view: directory - access/gist - gistvacuum.c
Test: unnamed
Date: 2008-07-03 Instrumented lines: 352
Code covered: 11.6 % Executed lines: 41
Legend: not executed executed

       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * gistvacuum.c
       4                 :  *        interface routines for the postgres GiST index access method.
       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/gist/gistvacuum.c,v 1.33 2007/11/15 21:14:31 momjian Exp $
      12                 :  *
      13                 :  *-------------------------------------------------------------------------
      14                 :  */
      15                 : #include "postgres.h"
      16                 : 
      17                 : #include "access/genam.h"
      18                 : #include "access/gist_private.h"
      19                 : #include "access/heapam.h"
      20                 : #include "commands/vacuum.h"
      21                 : #include "miscadmin.h"
      22                 : #include "storage/freespace.h"
      23                 : #include "utils/memutils.h"
      24                 : 
      25                 : 
      26                 : typedef struct GistBulkDeleteResult
      27                 : {
      28                 :         IndexBulkDeleteResult std;      /* common state */
      29                 :         bool            needFullVacuum;
      30                 : } GistBulkDeleteResult;
      31                 : 
      32                 : typedef struct
      33                 : {
      34                 :         GISTSTATE       giststate;
      35                 :         Relation        index;
      36                 :         MemoryContext opCtx;
      37                 :         GistBulkDeleteResult *result;
      38                 :         BufferAccessStrategy strategy;
      39                 : } GistVacuum;
      40                 : 
      41                 : typedef struct
      42                 : {
      43                 :         IndexTuple *itup;
      44                 :         int                     ituplen;
      45                 :         bool            emptypage;
      46                 : } ArrayTuple;
      47                 : 
      48                 : /*
      49                 :  * Make union of keys on page
      50                 :  */
      51                 : static IndexTuple
      52                 : PageMakeUnionKey(GistVacuum *gv, Buffer buffer)
      53               0 : {
      54               0 :         Page            page = BufferGetPage(buffer);
      55                 :         IndexTuple *vec,
      56                 :                                 tmp,
      57                 :                                 res;
      58               0 :         int                     veclen = 0;
      59               0 :         MemoryContext oldCtx = MemoryContextSwitchTo(gv->opCtx);
      60                 : 
      61               0 :         vec = gistextractpage(page, &veclen);
      62                 : 
      63                 :         /*
      64                 :          * we call gistunion() in temprorary context because user-defined
      65                 :          * functions called in gistunion() may do not free all memory
      66                 :          */
      67               0 :         tmp = gistunion(gv->index, vec, veclen, &(gv->giststate));
      68                 :         MemoryContextSwitchTo(oldCtx);
      69                 : 
      70               0 :         res = (IndexTuple) palloc(IndexTupleSize(tmp));
      71               0 :         memcpy(res, tmp, IndexTupleSize(tmp));
      72                 : 
      73               0 :         ItemPointerSetBlockNumber(&(res->t_tid), BufferGetBlockNumber(buffer));
      74               0 :         GistTupleSetValid(res);
      75                 : 
      76               0 :         MemoryContextReset(gv->opCtx);
      77                 : 
      78               0 :         return res;
      79                 : }
      80                 : 
      81                 : static void
      82                 : gistDeleteSubtree(GistVacuum *gv, BlockNumber blkno)
      83               0 : {
      84                 :         Buffer          buffer;
      85                 :         Page            page;
      86                 : 
      87               0 :         buffer = ReadBufferWithStrategy(gv->index, blkno, gv->strategy);
      88               0 :         LockBuffer(buffer, GIST_EXCLUSIVE);
      89               0 :         page = (Page) BufferGetPage(buffer);
      90                 : 
      91               0 :         if (!GistPageIsLeaf(page))
      92                 :         {
      93                 :                 int                     i;
      94                 : 
      95               0 :                 for (i = FirstOffsetNumber; i <= PageGetMaxOffsetNumber(page); i = OffsetNumberNext(i))
      96                 :                 {
      97               0 :                         ItemId          iid = PageGetItemId(page, i);
      98               0 :                         IndexTuple      idxtuple = (IndexTuple) PageGetItem(page, iid);
      99                 : 
     100               0 :                         gistDeleteSubtree(gv, ItemPointerGetBlockNumber(&(idxtuple->t_tid)));
     101                 :                 }
     102                 :         }
     103                 : 
     104               0 :         START_CRIT_SECTION();
     105                 : 
     106               0 :         MarkBufferDirty(buffer);
     107                 : 
     108               0 :         page = (Page) BufferGetPage(buffer);
     109               0 :         GistPageSetDeleted(page);
     110               0 :         gv->result->std.pages_deleted++;
     111                 : 
     112               0 :         if (!gv->index->rd_istemp)
     113                 :         {
     114                 :                 XLogRecData rdata[2];
     115                 :                 XLogRecPtr      recptr;
     116                 :                 gistxlogPageDelete xlrec;
     117                 : 
     118               0 :                 xlrec.node = gv->index->rd_node;
     119               0 :                 xlrec.blkno = blkno;
     120                 : 
     121               0 :                 rdata[0].buffer = buffer;
     122               0 :                 rdata[0].buffer_std = true;
     123               0 :                 rdata[0].data = NULL;
     124               0 :                 rdata[0].len = 0;
     125               0 :                 rdata[0].next = &(rdata[1]);
     126                 : 
     127               0 :                 rdata[1].buffer = InvalidBuffer;
     128               0 :                 rdata[1].data = (char *) &xlrec;
     129               0 :                 rdata[1].len = sizeof(gistxlogPageDelete);
     130               0 :                 rdata[1].next = NULL;
     131                 : 
     132               0 :                 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_DELETE, rdata);
     133               0 :                 PageSetLSN(page, recptr);
     134               0 :                 PageSetTLI(page, ThisTimeLineID);
     135                 :         }
     136                 :         else
     137               0 :                 PageSetLSN(page, XLogRecPtrForTemp);
     138                 : 
     139               0 :         END_CRIT_SECTION();
     140                 : 
     141               0 :         UnlockReleaseBuffer(buffer);
     142               0 : }
     143                 : 
     144                 : static Page
     145                 : GistPageGetCopyPage(Page page)
     146               0 : {
     147               0 :         Size            pageSize = PageGetPageSize(page);
     148                 :         Page            tmppage;
     149                 : 
     150               0 :         tmppage = (Page) palloc(pageSize);
     151               0 :         memcpy(tmppage, page, pageSize);
     152                 : 
     153               0 :         return tmppage;
     154                 : }
     155                 : 
     156                 : static ArrayTuple
     157                 : vacuumSplitPage(GistVacuum *gv, Page tempPage, Buffer buffer, IndexTuple *addon, int curlenaddon)
     158               0 : {
     159               0 :         ArrayTuple      res = {NULL, 0, false};
     160                 :         IndexTuple *vec;
     161               0 :         SplitedPageLayout *dist = NULL,
     162                 :                            *ptr;
     163                 :         int                     i,
     164               0 :                                 veclen = 0;
     165               0 :         BlockNumber blkno = BufferGetBlockNumber(buffer);
     166               0 :         MemoryContext oldCtx = MemoryContextSwitchTo(gv->opCtx);
     167                 : 
     168               0 :         vec = gistextractpage(tempPage, &veclen);
     169               0 :         vec = gistjoinvector(vec, &veclen, addon, curlenaddon);
     170               0 :         dist = gistSplit(gv->index, tempPage, vec, veclen, &(gv->giststate));
     171                 : 
     172                 :         MemoryContextSwitchTo(oldCtx);
     173                 : 
     174               0 :         if (blkno != GIST_ROOT_BLKNO)
     175                 :         {
     176                 :                 /* if non-root split then we should not allocate new buffer */
     177               0 :                 dist->buffer = buffer;
     178               0 :                 dist->page = tempPage;
     179                 :                 /* during vacuum we never split leaf page */
     180               0 :                 GistPageGetOpaque(dist->page)->flags = 0;
     181                 :         }
     182                 :         else
     183               0 :                 pfree(tempPage);
     184                 : 
     185               0 :         res.itup = (IndexTuple *) palloc(sizeof(IndexTuple) * veclen);
     186               0 :         res.ituplen = 0;
     187                 : 
     188                 :         /* make new pages and fills them */
     189               0 :         for (ptr = dist; ptr; ptr = ptr->next)
     190                 :         {
     191                 :                 char       *data;
     192                 : 
     193               0 :                 if (ptr->buffer == InvalidBuffer)
     194                 :                 {
     195               0 :                         ptr->buffer = gistNewBuffer(gv->index);
     196               0 :                         GISTInitBuffer(ptr->buffer, 0);
     197               0 :                         ptr->page = BufferGetPage(ptr->buffer);
     198                 :                 }
     199               0 :                 ptr->block.blkno = BufferGetBlockNumber(ptr->buffer);
     200                 : 
     201               0 :                 data = (char *) (ptr->list);
     202               0 :                 for (i = 0; i < ptr->block.num; i++)
     203                 :                 {
     204               0 :                         if (PageAddItem(ptr->page, (Item) data, IndexTupleSize((IndexTuple) data), i + FirstOffsetNumber, false, false) == InvalidOffsetNumber)
     205               0 :                                 elog(ERROR, "failed to add item to index page in \"%s\"", RelationGetRelationName(gv->index));
     206               0 :                         data += IndexTupleSize((IndexTuple) data);
     207                 :                 }
     208                 : 
     209               0 :                 ItemPointerSetBlockNumber(&(ptr->itup->t_tid), ptr->block.blkno);
     210               0 :                 res.itup[res.ituplen] = (IndexTuple) palloc(IndexTupleSize(ptr->itup));
     211               0 :                 memcpy(res.itup[res.ituplen], ptr->itup, IndexTupleSize(ptr->itup));
     212               0 :                 res.ituplen++;
     213                 :         }
     214                 : 
     215               0 :         START_CRIT_SECTION();
     216                 : 
     217               0 :         for (ptr = dist; ptr; ptr = ptr->next)
     218                 :         {
     219               0 :                 MarkBufferDirty(ptr->buffer);
     220               0 :                 GistPageGetOpaque(ptr->page)->rightlink = InvalidBlockNumber;
     221                 :         }
     222                 : 
     223                 :         /* restore splitted non-root page */
     224               0 :         if (blkno != GIST_ROOT_BLKNO)
     225                 :         {
     226               0 :                 PageRestoreTempPage(dist->page, BufferGetPage(dist->buffer));
     227               0 :                 dist->page = BufferGetPage(dist->buffer);
     228                 :         }
     229                 : 
     230               0 :         if (!gv->index->rd_istemp)
     231                 :         {
     232                 :                 XLogRecPtr      recptr;
     233                 :                 XLogRecData *rdata;
     234                 :                 ItemPointerData key;    /* set key for incomplete insert */
     235                 :                 char       *xlinfo;
     236                 : 
     237               0 :                 ItemPointerSet(&key, blkno, TUPLE_IS_VALID);
     238                 : 
     239               0 :                 rdata = formSplitRdata(gv->index->rd_node, blkno,
     240                 :                                                            false, &key, dist);
     241               0 :                 xlinfo = rdata->data;
     242                 : 
     243               0 :                 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT, rdata);
     244               0 :                 for (ptr = dist; ptr; ptr = ptr->next)
     245                 :                 {
     246               0 :                         PageSetLSN(BufferGetPage(ptr->buffer), recptr);
     247               0 :                         PageSetTLI(BufferGetPage(ptr->buffer), ThisTimeLineID);
     248                 :                 }
     249                 : 
     250               0 :                 pfree(xlinfo);
     251               0 :                 pfree(rdata);
     252                 :         }
     253                 :         else
     254                 :         {
     255               0 :                 for (ptr = dist; ptr; ptr = ptr->next)
     256               0 :                         PageSetLSN(BufferGetPage(ptr->buffer), XLogRecPtrForTemp);
     257                 :         }
     258                 : 
     259               0 :         for (ptr = dist; ptr; ptr = ptr->next)
     260                 :         {
     261                 :                 /* we must keep the buffer pin on the head page */
     262               0 :                 if (BufferGetBlockNumber(ptr->buffer) != blkno)
     263               0 :                         UnlockReleaseBuffer(ptr->buffer);
     264                 :         }
     265                 : 
     266               0 :         if (blkno == GIST_ROOT_BLKNO)
     267                 :         {
     268                 :                 ItemPointerData key;    /* set key for incomplete insert */
     269                 : 
     270               0 :                 ItemPointerSet(&key, blkno, TUPLE_IS_VALID);
     271                 : 
     272               0 :                 gistnewroot(gv->index, buffer, res.itup, res.ituplen, &key);
     273                 :         }
     274                 : 
     275               0 :         END_CRIT_SECTION();
     276                 : 
     277               0 :         MemoryContextReset(gv->opCtx);
     278                 : 
     279               0 :         return res;
     280                 : }
     281                 : 
     282                 : static ArrayTuple
     283                 : gistVacuumUpdate(GistVacuum *gv, BlockNumber blkno, bool needunion)
     284               0 : {
     285               0 :         ArrayTuple      res = {NULL, 0, false};
     286                 :         Buffer          buffer;
     287                 :         Page            page,
     288               0 :                                 tempPage = NULL;
     289                 :         OffsetNumber i,
     290                 :                                 maxoff;
     291                 :         ItemId          iid;
     292               0 :         int                     lenaddon = 4,
     293               0 :                                 curlenaddon = 0,
     294               0 :                                 nOffToDelete = 0,
     295               0 :                                 nBlkToDelete = 0;
     296                 :         IndexTuple      idxtuple,
     297               0 :                            *addon = NULL;
     298               0 :         bool            needwrite = false;
     299                 :         OffsetNumber offToDelete[MaxOffsetNumber];
     300                 :         BlockNumber blkToDelete[MaxOffsetNumber];
     301               0 :         ItemPointerData *completed = NULL;
     302               0 :         int                     ncompleted = 0,
     303               0 :                                 lencompleted = 16;
     304                 : 
     305               0 :         vacuum_delay_point();
     306                 : 
     307               0 :         buffer = ReadBufferWithStrategy(gv->index, blkno, gv->strategy);
     308               0 :         LockBuffer(buffer, GIST_EXCLUSIVE);
     309               0 :         gistcheckpage(gv->index, buffer);
     310               0 :         page = (Page) BufferGetPage(buffer);
     311               0 :         maxoff = PageGetMaxOffsetNumber(page);
     312                 : 
     313               0 :         if (GistPageIsLeaf(page))
     314                 :         {
     315               0 :                 if (GistTuplesDeleted(page))
     316               0 :                         needunion = needwrite = true;
     317                 :         }
     318                 :         else
     319                 :         {
     320               0 :                 completed = (ItemPointerData *) palloc(sizeof(ItemPointerData) * lencompleted);
     321               0 :                 addon = (IndexTuple *) palloc(sizeof(IndexTuple) * lenaddon);
     322                 : 
     323                 :                 /* get copy of page to work */
     324               0 :                 tempPage = GistPageGetCopyPage(page);
     325                 : 
     326               0 :                 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     327                 :                 {
     328                 :                         ArrayTuple      chldtuple;
     329                 :                         bool            needchildunion;
     330                 : 
     331               0 :                         iid = PageGetItemId(tempPage, i);
     332               0 :                         idxtuple = (IndexTuple) PageGetItem(tempPage, iid);
     333               0 :                         needchildunion = (GistTupleIsInvalid(idxtuple)) ? true : false;
     334                 : 
     335               0 :                         if (needchildunion)
     336               0 :                                 elog(DEBUG2, "gistVacuumUpdate: need union for block %u",
     337                 :                                          ItemPointerGetBlockNumber(&(idxtuple->t_tid)));
     338                 : 
     339               0 :                         chldtuple = gistVacuumUpdate(gv, ItemPointerGetBlockNumber(&(idxtuple->t_tid)),
     340                 :                                                                                  needchildunion);
     341               0 :                         if (chldtuple.ituplen || chldtuple.emptypage)
     342                 :                         {
     343                 :                                 /* update tuple or/and inserts new */
     344               0 :                                 if (chldtuple.emptypage)
     345               0 :                                         blkToDelete[nBlkToDelete++] = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
     346               0 :                                 offToDelete[nOffToDelete++] = i;
     347               0 :                                 PageIndexTupleDelete(tempPage, i);
     348               0 :                                 i--;
     349               0 :                                 maxoff--;
     350               0 :                                 needwrite = needunion = true;
     351                 : 
     352               0 :                                 if (chldtuple.ituplen)
     353                 :                                 {
     354                 : 
     355                 :                                         Assert(chldtuple.emptypage == false);
     356               0 :                                         while (curlenaddon + chldtuple.ituplen >= lenaddon)
     357                 :                                         {
     358               0 :                                                 lenaddon *= 2;
     359               0 :                                                 addon = (IndexTuple *) repalloc(addon, sizeof(IndexTuple) * lenaddon);
     360                 :                                         }
     361                 : 
     362               0 :                                         memcpy(addon + curlenaddon, chldtuple.itup, chldtuple.ituplen * sizeof(IndexTuple));
     363                 : 
     364               0 :                                         curlenaddon += chldtuple.ituplen;
     365                 : 
     366               0 :                                         if (chldtuple.ituplen > 1)
     367                 :                                         {
     368                 :                                                 /*
     369                 :                                                  * child was split, so we need mark completion
     370                 :                                                  * insert(split)
     371                 :                                                  */
     372                 :                                                 int                     j;
     373                 : 
     374               0 :                                                 while (ncompleted + chldtuple.ituplen > lencompleted)
     375                 :                                                 {
     376               0 :                                                         lencompleted *= 2;
     377               0 :                                                         completed = (ItemPointerData *) repalloc(completed, sizeof(ItemPointerData) * lencompleted);
     378                 :                                                 }
     379               0 :                                                 for (j = 0; j < chldtuple.ituplen; j++)
     380                 :                                                 {
     381               0 :                                                         ItemPointerCopy(&(chldtuple.itup[j]->t_tid), completed + ncompleted);
     382               0 :                                                         ncompleted++;
     383                 :                                                 }
     384                 :                                         }
     385               0 :                                         pfree(chldtuple.itup);
     386                 :                                 }
     387                 :                         }
     388                 :                 }
     389                 : 
     390                 :                 Assert(maxoff == PageGetMaxOffsetNumber(tempPage));
     391                 : 
     392               0 :                 if (curlenaddon)
     393                 :                 {
     394                 :                         /* insert updated tuples */
     395               0 :                         if (gistnospace(tempPage, addon, curlenaddon, InvalidOffsetNumber, 0))
     396                 :                         {
     397                 :                                 /* there is no space on page to insert tuples */
     398               0 :                                 res = vacuumSplitPage(gv, tempPage, buffer, addon, curlenaddon);
     399               0 :                                 tempPage = NULL;        /* vacuumSplitPage() free tempPage */
     400               0 :                                 needwrite = needunion = false;  /* gistSplit already forms
     401                 :                                                                                                  * unions and writes pages */
     402                 :                         }
     403                 :                         else
     404                 :                                 /* enough free space */
     405               0 :                                 gistfillbuffer(gv->index, tempPage, addon, curlenaddon, InvalidOffsetNumber);
     406                 :                 }
     407                 :         }
     408                 : 
     409                 :         /*
     410                 :          * If page is empty, we should remove pointer to it before deleting page
     411                 :          * (except root)
     412                 :          */
     413                 : 
     414               0 :         if (blkno != GIST_ROOT_BLKNO && (PageIsEmpty(page) || (tempPage && PageIsEmpty(tempPage))))
     415                 :         {
     416                 :                 /*
     417                 :                  * New version of page is empty, so leave it unchanged, upper call
     418                 :                  * will mark our page as deleted. In case of page split we never will
     419                 :                  * be here...
     420                 :                  *
     421                 :                  * If page was empty it can't become non-empty during processing
     422                 :                  */
     423               0 :                 res.emptypage = true;
     424               0 :                 UnlockReleaseBuffer(buffer);
     425                 :         }
     426                 :         else
     427                 :         {
     428                 :                 /* write page and remove its childs if it need */
     429                 : 
     430               0 :                 START_CRIT_SECTION();
     431                 : 
     432               0 :                 if (tempPage && needwrite)
     433                 :                 {
     434               0 :                         PageRestoreTempPage(tempPage, page);
     435               0 :                         tempPage = NULL;
     436                 :                 }
     437                 : 
     438                 :                 /* Empty index */
     439               0 :                 if (PageIsEmpty(page) && blkno == GIST_ROOT_BLKNO)
     440                 :                 {
     441               0 :                         needwrite = true;
     442               0 :                         GistPageSetLeaf(page);
     443                 :                 }
     444                 : 
     445                 : 
     446               0 :                 if (needwrite)
     447                 :                 {
     448               0 :                         MarkBufferDirty(buffer);
     449               0 :                         GistClearTuplesDeleted(page);
     450                 : 
     451               0 :                         if (!gv->index->rd_istemp)
     452                 :                         {
     453                 :                                 XLogRecData *rdata;
     454                 :                                 XLogRecPtr      recptr;
     455                 :                                 char       *xlinfo;
     456                 : 
     457               0 :                                 rdata = formUpdateRdata(gv->index->rd_node, buffer,
     458                 :                                                                                 offToDelete, nOffToDelete,
     459                 :                                                                                 addon, curlenaddon, NULL);
     460               0 :                                 xlinfo = rdata->next->data;
     461                 : 
     462               0 :                                 recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE, rdata);
     463               0 :                                 PageSetLSN(page, recptr);
     464               0 :                                 PageSetTLI(page, ThisTimeLineID);
     465                 : 
     466               0 :                                 pfree(xlinfo);
     467               0 :                                 pfree(rdata);
     468                 :                         }
     469                 :                         else
     470               0 :                                 PageSetLSN(page, XLogRecPtrForTemp);
     471                 :                 }
     472                 : 
     473               0 :                 END_CRIT_SECTION();
     474                 : 
     475               0 :                 if (needunion && !PageIsEmpty(page))
     476                 :                 {
     477               0 :                         res.itup = (IndexTuple *) palloc(sizeof(IndexTuple));
     478               0 :                         res.ituplen = 1;
     479               0 :                         res.itup[0] = PageMakeUnionKey(gv, buffer);
     480                 :                 }
     481                 : 
     482               0 :                 UnlockReleaseBuffer(buffer);
     483                 : 
     484                 :                 /* delete empty children, now we havn't any links to pointed subtrees */
     485               0 :                 for (i = 0; i < nBlkToDelete; i++)
     486               0 :                         gistDeleteSubtree(gv, blkToDelete[i]);
     487                 : 
     488               0 :                 if (ncompleted && !gv->index->rd_istemp)
     489               0 :                         gistxlogInsertCompletion(gv->index->rd_node, completed, ncompleted);
     490                 :         }
     491                 : 
     492                 : 
     493               0 :         for (i = 0; i < curlenaddon; i++)
     494               0 :                 pfree(addon[i]);
     495               0 :         if (addon)
     496               0 :                 pfree(addon);
     497               0 :         if (completed)
     498               0 :                 pfree(completed);
     499               0 :         if (tempPage)
     500               0 :                 pfree(tempPage);
     501                 : 
     502               0 :         return res;
     503                 : }
     504                 : 
     505                 : /*
     506                 :  * For usual vacuum just update FSM, for full vacuum
     507                 :  * reforms parent tuples if some of childs was deleted or changed,
     508                 :  * update invalid tuples (they can exist from last crash recovery only),
     509                 :  * tries to get smaller index
     510                 :  */
     511                 : 
     512                 : Datum
     513                 : gistvacuumcleanup(PG_FUNCTION_ARGS)
     514               3 : {
     515               3 :         IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
     516               3 :         GistBulkDeleteResult *stats = (GistBulkDeleteResult *) PG_GETARG_POINTER(1);
     517               3 :         Relation        rel = info->index;
     518                 :         BlockNumber npages,
     519                 :                                 blkno;
     520                 :         BlockNumber totFreePages,
     521                 :                                 nFreePages,
     522                 :                            *freePages,
     523                 :                                 maxFreePages;
     524               3 :         BlockNumber lastBlock = GIST_ROOT_BLKNO,
     525               3 :                                 lastFilledBlock = GIST_ROOT_BLKNO;
     526                 :         bool            needLock;
     527                 : 
     528                 :         /* Set up all-zero stats if gistbulkdelete wasn't called */
     529               3 :         if (stats == NULL)
     530                 :         {
     531               3 :                 stats = (GistBulkDeleteResult *) palloc0(sizeof(GistBulkDeleteResult));
     532                 :                 /* use heap's tuple count */
     533                 :                 Assert(info->num_heap_tuples >= 0);
     534               3 :                 stats->std.num_index_tuples = info->num_heap_tuples;
     535                 : 
     536                 :                 /*
     537                 :                  * XXX the above is wrong if index is partial.  Would it be OK to just
     538                 :                  * return NULL, or is there work we must do below?
     539                 :                  */
     540                 :         }
     541                 : 
     542                 :         /* gistVacuumUpdate may cause hard work */
     543               3 :         if (info->vacuum_full)
     544                 :         {
     545                 :                 GistVacuum      gv;
     546                 :                 ArrayTuple      res;
     547                 : 
     548                 :                 /* note: vacuum.c already acquired AccessExclusiveLock on index */
     549                 : 
     550               0 :                 gv.index = rel;
     551               0 :                 initGISTstate(&(gv.giststate), rel);
     552               0 :                 gv.opCtx = createTempGistContext();
     553               0 :                 gv.result = stats;
     554               0 :                 gv.strategy = info->strategy;
     555                 : 
     556                 :                 /* walk through the entire index for update tuples */
     557               0 :                 res = gistVacuumUpdate(&gv, GIST_ROOT_BLKNO, false);
     558                 :                 /* cleanup */
     559               0 :                 if (res.itup)
     560                 :                 {
     561                 :                         int                     i;
     562                 : 
     563               0 :                         for (i = 0; i < res.ituplen; i++)
     564               0 :                                 pfree(res.itup[i]);
     565               0 :                         pfree(res.itup);
     566                 :                 }
     567               0 :                 freeGISTstate(&(gv.giststate));
     568               0 :                 MemoryContextDelete(gv.opCtx);
     569                 :         }
     570               3 :         else if (stats->needFullVacuum)
     571               0 :                 ereport(NOTICE,
     572                 :                                 (errmsg("index \"%s\" needs VACUUM FULL or REINDEX to finish crash recovery",
     573                 :                                                 RelationGetRelationName(rel))));
     574                 : 
     575                 :         /*
     576                 :          * If vacuum full, we already have exclusive lock on the index. Otherwise,
     577                 :          * need lock unless it's local to this backend.
     578                 :          */
     579               3 :         if (info->vacuum_full)
     580               0 :                 needLock = false;
     581                 :         else
     582               3 :                 needLock = !RELATION_IS_LOCAL(rel);
     583                 : 
     584                 :         /* try to find deleted pages */
     585               3 :         if (needLock)
     586               3 :                 LockRelationForExtension(rel, ExclusiveLock);
     587               3 :         npages = RelationGetNumberOfBlocks(rel);
     588               3 :         if (needLock)
     589               3 :                 UnlockRelationForExtension(rel, ExclusiveLock);
     590                 : 
     591               3 :         maxFreePages = npages;
     592               3 :         if (maxFreePages > MaxFSMPages)
     593               0 :                 maxFreePages = MaxFSMPages;
     594                 : 
     595               3 :         totFreePages = nFreePages = 0;
     596               3 :         freePages = (BlockNumber *) palloc(sizeof(BlockNumber) * maxFreePages);
     597                 : 
     598              35 :         for (blkno = GIST_ROOT_BLKNO + 1; blkno < npages; blkno++)
     599                 :         {
     600                 :                 Buffer          buffer;
     601                 :                 Page            page;
     602                 : 
     603              32 :                 vacuum_delay_point();
     604                 : 
     605              32 :                 buffer = ReadBufferWithStrategy(rel, blkno, info->strategy);
     606              32 :                 LockBuffer(buffer, GIST_SHARE);
     607              32 :                 page = (Page) BufferGetPage(buffer);
     608                 : 
     609              32 :                 if (PageIsNew(page) || GistPageIsDeleted(page))
     610                 :                 {
     611               0 :                         if (nFreePages < maxFreePages)
     612               0 :                                 freePages[nFreePages++] = blkno;
     613               0 :                         totFreePages++;
     614                 :                 }
     615                 :                 else
     616              32 :                         lastFilledBlock = blkno;
     617              32 :                 UnlockReleaseBuffer(buffer);
     618                 :         }
     619               3 :         lastBlock = npages - 1;
     620                 : 
     621               3 :         if (info->vacuum_full && nFreePages > 0)
     622                 :         {                                                       /* try to truncate index */
     623                 :                 int                     i;
     624                 : 
     625               0 :                 for (i = 0; i < nFreePages; i++)
     626               0 :                         if (freePages[i] >= lastFilledBlock)
     627                 :                         {
     628               0 :                                 totFreePages = nFreePages = i;
     629               0 :                                 break;
     630                 :                         }
     631                 : 
     632               0 :                 if (lastBlock > lastFilledBlock)
     633               0 :                         RelationTruncate(rel, lastFilledBlock + 1);
     634               0 :                 stats->std.pages_removed = lastBlock - lastFilledBlock;
     635                 :         }
     636                 : 
     637               3 :         RecordIndexFreeSpace(&rel->rd_node, totFreePages, nFreePages, freePages);
     638               3 :         pfree(freePages);
     639                 : 
     640                 :         /* return statistics */
     641               3 :         stats->std.pages_free = totFreePages;
     642               3 :         if (needLock)
     643               3 :                 LockRelationForExtension(rel, ExclusiveLock);
     644               3 :         stats->std.num_pages = RelationGetNumberOfBlocks(rel);
     645               3 :         if (needLock)
     646               3 :                 UnlockRelationForExtension(rel, ExclusiveLock);
     647                 : 
     648               3 :         PG_RETURN_POINTER(stats);
     649                 : }
     650                 : 
     651                 : typedef struct GistBDItem
     652                 : {
     653                 :         GistNSN         parentlsn;
     654                 :         BlockNumber blkno;
     655                 :         struct GistBDItem *next;
     656                 : } GistBDItem;
     657                 : 
     658                 : static void
     659                 : pushStackIfSplited(Page page, GistBDItem *stack)
     660               0 : {
     661               0 :         GISTPageOpaque opaque = GistPageGetOpaque(page);
     662                 : 
     663               0 :         if (stack->blkno != GIST_ROOT_BLKNO && !XLogRecPtrIsInvalid(stack->parentlsn) &&
     664                 :                 XLByteLT(stack->parentlsn, opaque->nsn) &&
     665                 :                 opaque->rightlink != InvalidBlockNumber /* sanity check */ )
     666                 :         {
     667                 :                 /* split page detected, install right link to the stack */
     668                 : 
     669               0 :                 GistBDItem *ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
     670                 : 
     671               0 :                 ptr->blkno = opaque->rightlink;
     672               0 :                 ptr->parentlsn = stack->parentlsn;
     673               0 :                 ptr->next = stack->next;
     674               0 :                 stack->next = ptr;
     675                 :         }
     676               0 : }
     677                 : 
     678                 : 
     679                 : /*
     680                 :  * Bulk deletion of all index entries pointing to a set of heap tuples and
     681                 :  * check invalid tuples after crash recovery.
     682                 :  * The set of target tuples is specified via a callback routine that tells
     683                 :  * whether any given heap tuple (identified by ItemPointer) is being deleted.
     684                 :  *
     685                 :  * Result: a palloc'd struct containing statistical info for VACUUM displays.
     686                 :  */
     687                 : Datum
     688                 : gistbulkdelete(PG_FUNCTION_ARGS)
     689               0 : {
     690               0 :         IndexVacuumInfo *info = (IndexVacuumInfo *) PG_GETARG_POINTER(0);
     691               0 :         GistBulkDeleteResult *stats = (GistBulkDeleteResult *) PG_GETARG_POINTER(1);
     692               0 :         IndexBulkDeleteCallback callback = (IndexBulkDeleteCallback) PG_GETARG_POINTER(2);
     693               0 :         void       *callback_state = (void *) PG_GETARG_POINTER(3);
     694               0 :         Relation        rel = info->index;
     695                 :         GistBDItem *stack,
     696                 :                            *ptr;
     697                 : 
     698                 :         /* first time through? */
     699               0 :         if (stats == NULL)
     700               0 :                 stats = (GistBulkDeleteResult *) palloc0(sizeof(GistBulkDeleteResult));
     701                 :         /* we'll re-count the tuples each time */
     702               0 :         stats->std.num_index_tuples = 0;
     703                 : 
     704               0 :         stack = (GistBDItem *) palloc0(sizeof(GistBDItem));
     705               0 :         stack->blkno = GIST_ROOT_BLKNO;
     706                 : 
     707               0 :         while (stack)
     708                 :         {
     709               0 :                 Buffer          buffer = ReadBufferWithStrategy(rel, stack->blkno, info->strategy);
     710                 :                 Page            page;
     711                 :                 OffsetNumber i,
     712                 :                                         maxoff;
     713                 :                 IndexTuple      idxtuple;
     714                 :                 ItemId          iid;
     715                 : 
     716               0 :                 LockBuffer(buffer, GIST_SHARE);
     717               0 :                 gistcheckpage(rel, buffer);
     718               0 :                 page = (Page) BufferGetPage(buffer);
     719                 : 
     720               0 :                 if (GistPageIsLeaf(page))
     721                 :                 {
     722                 :                         OffsetNumber todelete[MaxOffsetNumber];
     723               0 :                         int                     ntodelete = 0;
     724                 : 
     725               0 :                         LockBuffer(buffer, GIST_UNLOCK);
     726               0 :                         LockBuffer(buffer, GIST_EXCLUSIVE);
     727                 : 
     728               0 :                         page = (Page) BufferGetPage(buffer);
     729               0 :                         if (stack->blkno == GIST_ROOT_BLKNO && !GistPageIsLeaf(page))
     730                 :                         {
     731                 :                                 /* only the root can become non-leaf during relock */
     732               0 :                                 UnlockReleaseBuffer(buffer);
     733                 :                                 /* one more check */
     734               0 :                                 continue;
     735                 :                         }
     736                 : 
     737                 :                         /*
     738                 :                          * check for split proceeded after look at parent, we should check
     739                 :                          * it after relock
     740                 :                          */
     741               0 :                         pushStackIfSplited(page, stack);
     742                 : 
     743                 :                         /*
     744                 :                          * Remove deletable tuples from page
     745                 :                          */
     746                 : 
     747               0 :                         maxoff = PageGetMaxOffsetNumber(page);
     748                 : 
     749               0 :                         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     750                 :                         {
     751               0 :                                 iid = PageGetItemId(page, i);
     752               0 :                                 idxtuple = (IndexTuple) PageGetItem(page, iid);
     753                 : 
     754               0 :                                 if (callback(&(idxtuple->t_tid), callback_state))
     755                 :                                 {
     756               0 :                                         todelete[ntodelete] = i - ntodelete;
     757               0 :                                         ntodelete++;
     758               0 :                                         stats->std.tuples_removed += 1;
     759                 :                                 }
     760                 :                                 else
     761               0 :                                         stats->std.num_index_tuples += 1;
     762                 :                         }
     763                 : 
     764               0 :                         if (ntodelete)
     765                 :                         {
     766               0 :                                 START_CRIT_SECTION();
     767                 : 
     768               0 :                                 MarkBufferDirty(buffer);
     769                 : 
     770               0 :                                 for (i = 0; i < ntodelete; i++)
     771               0 :                                         PageIndexTupleDelete(page, todelete[i]);
     772               0 :                                 GistMarkTuplesDeleted(page);
     773                 : 
     774               0 :                                 if (!rel->rd_istemp)
     775                 :                                 {
     776                 :                                         XLogRecData *rdata;
     777                 :                                         XLogRecPtr      recptr;
     778                 :                                         gistxlogPageUpdate *xlinfo;
     779                 : 
     780               0 :                                         rdata = formUpdateRdata(rel->rd_node, buffer,
     781                 :                                                                                         todelete, ntodelete,
     782                 :                                                                                         NULL, 0,
     783                 :                                                                                         NULL);
     784               0 :                                         xlinfo = (gistxlogPageUpdate *) rdata->next->data;
     785                 : 
     786               0 :                                         recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE, rdata);
     787               0 :                                         PageSetLSN(page, recptr);
     788               0 :                                         PageSetTLI(page, ThisTimeLineID);
     789                 : 
     790               0 :                                         pfree(xlinfo);
     791               0 :                                         pfree(rdata);
     792                 :                                 }
     793                 :                                 else
     794               0 :                                         PageSetLSN(page, XLogRecPtrForTemp);
     795                 : 
     796               0 :                                 END_CRIT_SECTION();
     797                 :                         }
     798                 : 
     799                 :                 }
     800                 :                 else
     801                 :                 {
     802                 :                         /* check for split proceeded after look at parent */
     803               0 :                         pushStackIfSplited(page, stack);
     804                 : 
     805               0 :                         maxoff = PageGetMaxOffsetNumber(page);
     806                 : 
     807               0 :                         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     808                 :                         {
     809               0 :                                 iid = PageGetItemId(page, i);
     810               0 :                                 idxtuple = (IndexTuple) PageGetItem(page, iid);
     811                 : 
     812               0 :                                 ptr = (GistBDItem *) palloc(sizeof(GistBDItem));
     813               0 :                                 ptr->blkno = ItemPointerGetBlockNumber(&(idxtuple->t_tid));
     814               0 :                                 ptr->parentlsn = PageGetLSN(page);
     815               0 :                                 ptr->next = stack->next;
     816               0 :                                 stack->next = ptr;
     817                 : 
     818               0 :                                 if (GistTupleIsInvalid(idxtuple))
     819               0 :                                         stats->needFullVacuum = true;
     820                 :                         }
     821                 :                 }
     822                 : 
     823               0 :                 UnlockReleaseBuffer(buffer);
     824                 : 
     825               0 :                 ptr = stack->next;
     826               0 :                 pfree(stack);
     827               0 :                 stack = ptr;
     828                 : 
     829               0 :                 vacuum_delay_point();
     830                 :         }
     831                 : 
     832               0 :         PG_RETURN_POINTER(stats);
     833                 : }

Generated by: LTP GCOV extension version 1.5