LTP GCOV extension - code coverage report
Current view: directory - access/gist - gistxlog.c
Test: unnamed
Date: 2008-07-03 Instrumented lines: 416
Code covered: 19.2 % Executed lines: 80
Legend: not executed executed

       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * gistxlog.c
       4                 :  *        WAL replay logic for GiST.
       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/gistxlog.c,v 1.26 2007/02/01 19:10:25 momjian Exp $
      12                 :  *-------------------------------------------------------------------------
      13                 :  */
      14                 : #include "postgres.h"
      15                 : 
      16                 : #include "access/gist_private.h"
      17                 : #include "access/heapam.h"
      18                 : #include "miscadmin.h"
      19                 : #include "utils/memutils.h"
      20                 : 
      21                 : 
      22                 : typedef struct
      23                 : {
      24                 :         gistxlogPageUpdate *data;
      25                 :         int                     len;
      26                 :         IndexTuple *itup;
      27                 :         OffsetNumber *todelete;
      28                 : } PageUpdateRecord;
      29                 : 
      30                 : typedef struct
      31                 : {
      32                 :         gistxlogPage *header;
      33                 :         IndexTuple *itup;
      34                 : } NewPage;
      35                 : 
      36                 : typedef struct
      37                 : {
      38                 :         gistxlogPageSplit *data;
      39                 :         NewPage    *page;
      40                 : } PageSplitRecord;
      41                 : 
      42                 : /* track for incomplete inserts, idea was taken from nbtxlog.c */
      43                 : 
      44                 : typedef struct gistIncompleteInsert
      45                 : {
      46                 :         RelFileNode node;
      47                 :         BlockNumber origblkno;          /* for splits */
      48                 :         ItemPointerData key;
      49                 :         int                     lenblk;
      50                 :         BlockNumber *blkno;
      51                 :         XLogRecPtr      lsn;
      52                 :         BlockNumber *path;
      53                 :         int                     pathlen;
      54                 : } gistIncompleteInsert;
      55                 : 
      56                 : 
      57                 : static MemoryContext opCtx;             /* working memory for operations */
      58                 : static MemoryContext insertCtx; /* holds incomplete_inserts list */
      59                 : static List *incomplete_inserts;
      60                 : 
      61                 : 
      62                 : #define ItemPointerEQ(a, b) \
      63                 :         ( ItemPointerGetOffsetNumber(a) == ItemPointerGetOffsetNumber(b) && \
      64                 :           ItemPointerGetBlockNumber (a) == ItemPointerGetBlockNumber(b) )
      65                 : 
      66                 : 
      67                 : static void
      68                 : pushIncompleteInsert(RelFileNode node, XLogRecPtr lsn, ItemPointerData key,
      69                 :                                          BlockNumber *blkno, int lenblk,
      70                 :                                          PageSplitRecord *xlinfo /* to extract blkno info */ )
      71               0 : {
      72                 :         MemoryContext oldCxt;
      73                 :         gistIncompleteInsert *ninsert;
      74                 : 
      75               0 :         if (!ItemPointerIsValid(&key))
      76                 : 
      77                 :                 /*
      78                 :                  * if key is null then we should not store insertion as incomplete,
      79                 :                  * because it's a vacuum operation..
      80                 :                  */
      81               0 :                 return;
      82                 : 
      83               0 :         oldCxt = MemoryContextSwitchTo(insertCtx);
      84               0 :         ninsert = (gistIncompleteInsert *) palloc(sizeof(gistIncompleteInsert));
      85                 : 
      86               0 :         ninsert->node = node;
      87               0 :         ninsert->key = key;
      88               0 :         ninsert->lsn = lsn;
      89                 : 
      90               0 :         if (lenblk && blkno)
      91                 :         {
      92               0 :                 ninsert->lenblk = lenblk;
      93               0 :                 ninsert->blkno = (BlockNumber *) palloc(sizeof(BlockNumber) * ninsert->lenblk);
      94               0 :                 memcpy(ninsert->blkno, blkno, sizeof(BlockNumber) * ninsert->lenblk);
      95               0 :                 ninsert->origblkno = *blkno;
      96                 :         }
      97                 :         else
      98                 :         {
      99                 :                 int                     i;
     100                 : 
     101                 :                 Assert(xlinfo);
     102               0 :                 ninsert->lenblk = xlinfo->data->npage;
     103               0 :                 ninsert->blkno = (BlockNumber *) palloc(sizeof(BlockNumber) * ninsert->lenblk);
     104               0 :                 for (i = 0; i < ninsert->lenblk; i++)
     105               0 :                         ninsert->blkno[i] = xlinfo->page[i].header->blkno;
     106               0 :                 ninsert->origblkno = xlinfo->data->origblkno;
     107                 :         }
     108                 :         Assert(ninsert->lenblk > 0);
     109                 : 
     110                 :         /*
     111                 :          * Stick the new incomplete insert onto the front of the list, not the
     112                 :          * back.  This is so that gist_xlog_cleanup will process incompletions in
     113                 :          * last-in-first-out order.
     114                 :          */
     115               0 :         incomplete_inserts = lcons(ninsert, incomplete_inserts);
     116                 : 
     117                 :         MemoryContextSwitchTo(oldCxt);
     118                 : }
     119                 : 
     120                 : static void
     121                 : forgetIncompleteInsert(RelFileNode node, ItemPointerData key)
     122               0 : {
     123                 :         ListCell   *l;
     124                 : 
     125               0 :         if (!ItemPointerIsValid(&key))
     126               0 :                 return;
     127                 : 
     128               0 :         if (incomplete_inserts == NIL)
     129               0 :                 return;
     130                 : 
     131               0 :         foreach(l, incomplete_inserts)
     132                 :         {
     133               0 :                 gistIncompleteInsert *insert = (gistIncompleteInsert *) lfirst(l);
     134                 : 
     135               0 :                 if (RelFileNodeEquals(node, insert->node) && ItemPointerEQ(&(insert->key), &(key)))
     136                 :                 {
     137                 :                         /* found */
     138               0 :                         incomplete_inserts = list_delete_ptr(incomplete_inserts, insert);
     139               0 :                         pfree(insert->blkno);
     140               0 :                         pfree(insert);
     141               0 :                         break;
     142                 :                 }
     143                 :         }
     144                 : }
     145                 : 
     146                 : static void
     147                 : decodePageUpdateRecord(PageUpdateRecord *decoded, XLogRecord *record)
     148               0 : {
     149               0 :         char       *begin = XLogRecGetData(record),
     150                 :                            *ptr;
     151               0 :         int                     i = 0,
     152               0 :                                 addpath = 0;
     153                 : 
     154               0 :         decoded->data = (gistxlogPageUpdate *) begin;
     155                 : 
     156               0 :         if (decoded->data->ntodelete)
     157                 :         {
     158               0 :                 decoded->todelete = (OffsetNumber *) (begin + sizeof(gistxlogPageUpdate) + addpath);
     159               0 :                 addpath = MAXALIGN(sizeof(OffsetNumber) * decoded->data->ntodelete);
     160                 :         }
     161                 :         else
     162               0 :                 decoded->todelete = NULL;
     163                 : 
     164               0 :         decoded->len = 0;
     165               0 :         ptr = begin + sizeof(gistxlogPageUpdate) + addpath;
     166               0 :         while (ptr - begin < record->xl_len)
     167                 :         {
     168               0 :                 decoded->len++;
     169               0 :                 ptr += IndexTupleSize((IndexTuple) ptr);
     170                 :         }
     171                 : 
     172               0 :         decoded->itup = (IndexTuple *) palloc(sizeof(IndexTuple) * decoded->len);
     173                 : 
     174               0 :         ptr = begin + sizeof(gistxlogPageUpdate) + addpath;
     175               0 :         while (ptr - begin < record->xl_len)
     176                 :         {
     177               0 :                 decoded->itup[i] = (IndexTuple) ptr;
     178               0 :                 ptr += IndexTupleSize(decoded->itup[i]);
     179               0 :                 i++;
     180                 :         }
     181               0 : }
     182                 : 
     183                 : /*
     184                 :  * redo any page update (except page split)
     185                 :  */
     186                 : static void
     187                 : gistRedoPageUpdateRecord(XLogRecPtr lsn, XLogRecord *record, bool isnewroot)
     188               0 : {
     189               0 :         gistxlogPageUpdate *xldata = (gistxlogPageUpdate *) XLogRecGetData(record);
     190                 :         PageUpdateRecord xlrec;
     191                 :         Relation        reln;
     192                 :         Buffer          buffer;
     193                 :         Page            page;
     194                 : 
     195                 :         /* we must fix incomplete_inserts list even if XLR_BKP_BLOCK_1 is set */
     196               0 :         forgetIncompleteInsert(xldata->node, xldata->key);
     197                 : 
     198               0 :         if (!isnewroot && xldata->blkno != GIST_ROOT_BLKNO)
     199                 :                 /* operation with root always finalizes insertion */
     200               0 :                 pushIncompleteInsert(xldata->node, lsn, xldata->key,
     201                 :                                                          &(xldata->blkno), 1,
     202                 :                                                          NULL);
     203                 : 
     204                 :         /* nothing else to do if page was backed up (and no info to do it with) */
     205               0 :         if (record->xl_info & XLR_BKP_BLOCK_1)
     206               0 :                 return;
     207                 : 
     208               0 :         decodePageUpdateRecord(&xlrec, record);
     209                 : 
     210               0 :         reln = XLogOpenRelation(xlrec.data->node);
     211               0 :         buffer = XLogReadBuffer(reln, xlrec.data->blkno, false);
     212               0 :         if (!BufferIsValid(buffer))
     213                 :                 return;
     214               0 :         page = (Page) BufferGetPage(buffer);
     215                 : 
     216               0 :         if (XLByteLE(lsn, PageGetLSN(page)))
     217                 :         {
     218               0 :                 UnlockReleaseBuffer(buffer);
     219               0 :                 return;
     220                 :         }
     221                 : 
     222               0 :         if (isnewroot)
     223               0 :                 GISTInitBuffer(buffer, 0);
     224               0 :         else if (xlrec.data->ntodelete)
     225                 :         {
     226                 :                 int                     i;
     227                 : 
     228               0 :                 for (i = 0; i < xlrec.data->ntodelete; i++)
     229               0 :                         PageIndexTupleDelete(page, xlrec.todelete[i]);
     230               0 :                 if (GistPageIsLeaf(page))
     231               0 :                         GistMarkTuplesDeleted(page);
     232                 :         }
     233                 : 
     234                 :         /* add tuples */
     235               0 :         if (xlrec.len > 0)
     236               0 :                 gistfillbuffer(reln, page, xlrec.itup, xlrec.len, InvalidOffsetNumber);
     237                 : 
     238                 :         /*
     239                 :          * special case: leafpage, nothing to insert, nothing to delete, then
     240                 :          * vacuum marks page
     241                 :          */
     242               0 :         if (GistPageIsLeaf(page) && xlrec.len == 0 && xlrec.data->ntodelete == 0)
     243               0 :                 GistClearTuplesDeleted(page);
     244                 : 
     245               0 :         if (!GistPageIsLeaf(page) && PageGetMaxOffsetNumber(page) == InvalidOffsetNumber && xldata->blkno == GIST_ROOT_BLKNO)
     246                 : 
     247                 :                 /*
     248                 :                  * all links on non-leaf root page was deleted by vacuum full, so root
     249                 :                  * page becomes a leaf
     250                 :                  */
     251               0 :                 GistPageSetLeaf(page);
     252                 : 
     253               0 :         GistPageGetOpaque(page)->rightlink = InvalidBlockNumber;
     254               0 :         PageSetLSN(page, lsn);
     255               0 :         PageSetTLI(page, ThisTimeLineID);
     256               0 :         MarkBufferDirty(buffer);
     257               0 :         UnlockReleaseBuffer(buffer);
     258                 : }
     259                 : 
     260                 : static void
     261                 : gistRedoPageDeleteRecord(XLogRecPtr lsn, XLogRecord *record)
     262               0 : {
     263               0 :         gistxlogPageDelete *xldata = (gistxlogPageDelete *) XLogRecGetData(record);
     264                 :         Relation        reln;
     265                 :         Buffer          buffer;
     266                 :         Page            page;
     267                 : 
     268                 :         /* nothing else to do if page was backed up (and no info to do it with) */
     269               0 :         if (record->xl_info & XLR_BKP_BLOCK_1)
     270               0 :                 return;
     271                 : 
     272               0 :         reln = XLogOpenRelation(xldata->node);
     273               0 :         buffer = XLogReadBuffer(reln, xldata->blkno, false);
     274               0 :         if (!BufferIsValid(buffer))
     275                 :                 return;
     276                 : 
     277               0 :         page = (Page) BufferGetPage(buffer);
     278               0 :         GistPageSetDeleted(page);
     279                 : 
     280               0 :         PageSetLSN(page, lsn);
     281               0 :         PageSetTLI(page, ThisTimeLineID);
     282               0 :         MarkBufferDirty(buffer);
     283               0 :         UnlockReleaseBuffer(buffer);
     284                 : }
     285                 : 
     286                 : static void
     287                 : decodePageSplitRecord(PageSplitRecord *decoded, XLogRecord *record)
     288               0 : {
     289               0 :         char       *begin = XLogRecGetData(record),
     290                 :                            *ptr;
     291                 :         int                     j,
     292               0 :                                 i = 0;
     293                 : 
     294               0 :         decoded->data = (gistxlogPageSplit *) begin;
     295               0 :         decoded->page = (NewPage *) palloc(sizeof(NewPage) * decoded->data->npage);
     296                 : 
     297               0 :         ptr = begin + sizeof(gistxlogPageSplit);
     298               0 :         for (i = 0; i < decoded->data->npage; i++)
     299                 :         {
     300                 :                 Assert(ptr - begin < record->xl_len);
     301               0 :                 decoded->page[i].header = (gistxlogPage *) ptr;
     302               0 :                 ptr += sizeof(gistxlogPage);
     303                 : 
     304               0 :                 decoded->page[i].itup = (IndexTuple *)
     305                 :                         palloc(sizeof(IndexTuple) * decoded->page[i].header->num);
     306               0 :                 j = 0;
     307               0 :                 while (j < decoded->page[i].header->num)
     308                 :                 {
     309                 :                         Assert(ptr - begin < record->xl_len);
     310               0 :                         decoded->page[i].itup[j] = (IndexTuple) ptr;
     311               0 :                         ptr += IndexTupleSize((IndexTuple) ptr);
     312               0 :                         j++;
     313                 :                 }
     314                 :         }
     315               0 : }
     316                 : 
     317                 : static void
     318                 : gistRedoPageSplitRecord(XLogRecPtr lsn, XLogRecord *record)
     319               0 : {
     320                 :         PageSplitRecord xlrec;
     321                 :         Relation        reln;
     322                 :         Buffer          buffer;
     323                 :         Page            page;
     324                 :         int                     i;
     325                 :         int                     flags;
     326                 : 
     327               0 :         decodePageSplitRecord(&xlrec, record);
     328               0 :         reln = XLogOpenRelation(xlrec.data->node);
     329               0 :         flags = xlrec.data->origleaf ? F_LEAF : 0;
     330                 : 
     331                 :         /* loop around all pages */
     332               0 :         for (i = 0; i < xlrec.data->npage; i++)
     333                 :         {
     334               0 :                 NewPage    *newpage = xlrec.page + i;
     335                 : 
     336               0 :                 buffer = XLogReadBuffer(reln, newpage->header->blkno, true);
     337                 :                 Assert(BufferIsValid(buffer));
     338               0 :                 page = (Page) BufferGetPage(buffer);
     339                 : 
     340                 :                 /* ok, clear buffer */
     341               0 :                 GISTInitBuffer(buffer, flags);
     342                 : 
     343                 :                 /* and fill it */
     344               0 :                 gistfillbuffer(reln, page, newpage->itup, newpage->header->num, FirstOffsetNumber);
     345                 : 
     346               0 :                 PageSetLSN(page, lsn);
     347               0 :                 PageSetTLI(page, ThisTimeLineID);
     348               0 :                 MarkBufferDirty(buffer);
     349               0 :                 UnlockReleaseBuffer(buffer);
     350                 :         }
     351                 : 
     352               0 :         forgetIncompleteInsert(xlrec.data->node, xlrec.data->key);
     353                 : 
     354               0 :         pushIncompleteInsert(xlrec.data->node, lsn, xlrec.data->key,
     355                 :                                                  NULL, 0,
     356                 :                                                  &xlrec);
     357               0 : }
     358                 : 
     359                 : static void
     360                 : gistRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record)
     361               0 : {
     362               0 :         RelFileNode *node = (RelFileNode *) XLogRecGetData(record);
     363                 :         Relation        reln;
     364                 :         Buffer          buffer;
     365                 :         Page            page;
     366                 : 
     367               0 :         reln = XLogOpenRelation(*node);
     368               0 :         buffer = XLogReadBuffer(reln, GIST_ROOT_BLKNO, true);
     369                 :         Assert(BufferIsValid(buffer));
     370               0 :         page = (Page) BufferGetPage(buffer);
     371                 : 
     372               0 :         GISTInitBuffer(buffer, F_LEAF);
     373                 : 
     374               0 :         PageSetLSN(page, lsn);
     375               0 :         PageSetTLI(page, ThisTimeLineID);
     376                 : 
     377               0 :         MarkBufferDirty(buffer);
     378               0 :         UnlockReleaseBuffer(buffer);
     379               0 : }
     380                 : 
     381                 : static void
     382                 : gistRedoCompleteInsert(XLogRecPtr lsn, XLogRecord *record)
     383               0 : {
     384               0 :         char       *begin = XLogRecGetData(record),
     385                 :                            *ptr;
     386                 :         gistxlogInsertComplete *xlrec;
     387                 : 
     388               0 :         xlrec = (gistxlogInsertComplete *) begin;
     389                 : 
     390               0 :         ptr = begin + sizeof(gistxlogInsertComplete);
     391               0 :         while (ptr - begin < record->xl_len)
     392                 :         {
     393                 :                 Assert(record->xl_len - (ptr - begin) >= sizeof(ItemPointerData));
     394               0 :                 forgetIncompleteInsert(xlrec->node, *((ItemPointerData *) ptr));
     395               0 :                 ptr += sizeof(ItemPointerData);
     396                 :         }
     397               0 : }
     398                 : 
     399                 : void
     400                 : gist_redo(XLogRecPtr lsn, XLogRecord *record)
     401               0 : {
     402               0 :         uint8           info = record->xl_info & ~XLR_INFO_MASK;
     403                 : 
     404                 :         MemoryContext oldCxt;
     405                 : 
     406               0 :         oldCxt = MemoryContextSwitchTo(opCtx);
     407               0 :         switch (info)
     408                 :         {
     409                 :                 case XLOG_GIST_PAGE_UPDATE:
     410               0 :                         gistRedoPageUpdateRecord(lsn, record, false);
     411               0 :                         break;
     412                 :                 case XLOG_GIST_PAGE_DELETE:
     413               0 :                         gistRedoPageDeleteRecord(lsn, record);
     414               0 :                         break;
     415                 :                 case XLOG_GIST_NEW_ROOT:
     416               0 :                         gistRedoPageUpdateRecord(lsn, record, true);
     417               0 :                         break;
     418                 :                 case XLOG_GIST_PAGE_SPLIT:
     419               0 :                         gistRedoPageSplitRecord(lsn, record);
     420               0 :                         break;
     421                 :                 case XLOG_GIST_CREATE_INDEX:
     422               0 :                         gistRedoCreateIndex(lsn, record);
     423               0 :                         break;
     424                 :                 case XLOG_GIST_INSERT_COMPLETE:
     425               0 :                         gistRedoCompleteInsert(lsn, record);
     426               0 :                         break;
     427                 :                 default:
     428               0 :                         elog(PANIC, "gist_redo: unknown op code %u", info);
     429                 :         }
     430                 : 
     431               0 :         MemoryContextSwitchTo(oldCxt);
     432               0 :         MemoryContextReset(opCtx);
     433               0 : }
     434                 : 
     435                 : static void
     436                 : out_target(StringInfo buf, RelFileNode node, ItemPointerData key)
     437               0 : {
     438               0 :         appendStringInfo(buf, "rel %u/%u/%u",
     439                 :                                          node.spcNode, node.dbNode, node.relNode);
     440               0 :         if (ItemPointerIsValid(&key))
     441               0 :                 appendStringInfo(buf, "; tid %u/%u",
     442                 :                                                  ItemPointerGetBlockNumber(&key),
     443                 :                                                  ItemPointerGetOffsetNumber(&key));
     444               0 : }
     445                 : 
     446                 : static void
     447                 : out_gistxlogPageUpdate(StringInfo buf, gistxlogPageUpdate *xlrec)
     448               0 : {
     449               0 :         out_target(buf, xlrec->node, xlrec->key);
     450               0 :         appendStringInfo(buf, "; block number %u", xlrec->blkno);
     451               0 : }
     452                 : 
     453                 : static void
     454                 : out_gistxlogPageDelete(StringInfo buf, gistxlogPageDelete *xlrec)
     455               0 : {
     456               0 :         appendStringInfo(buf, "page_delete: rel %u/%u/%u; blkno %u",
     457                 :                                 xlrec->node.spcNode, xlrec->node.dbNode, xlrec->node.relNode,
     458                 :                                          xlrec->blkno);
     459               0 : }
     460                 : 
     461                 : static void
     462                 : out_gistxlogPageSplit(StringInfo buf, gistxlogPageSplit *xlrec)
     463               0 : {
     464               0 :         appendStringInfo(buf, "page_split: ");
     465               0 :         out_target(buf, xlrec->node, xlrec->key);
     466               0 :         appendStringInfo(buf, "; block number %u splits to %d pages",
     467                 :                                          xlrec->origblkno, xlrec->npage);
     468               0 : }
     469                 : 
     470                 : void
     471                 : gist_desc(StringInfo buf, uint8 xl_info, char *rec)
     472               0 : {
     473               0 :         uint8           info = xl_info & ~XLR_INFO_MASK;
     474                 : 
     475               0 :         switch (info)
     476                 :         {
     477                 :                 case XLOG_GIST_PAGE_UPDATE:
     478               0 :                         appendStringInfo(buf, "page_update: ");
     479               0 :                         out_gistxlogPageUpdate(buf, (gistxlogPageUpdate *) rec);
     480               0 :                         break;
     481                 :                 case XLOG_GIST_PAGE_DELETE:
     482               0 :                         out_gistxlogPageDelete(buf, (gistxlogPageDelete *) rec);
     483               0 :                         break;
     484                 :                 case XLOG_GIST_NEW_ROOT:
     485               0 :                         appendStringInfo(buf, "new_root: ");
     486               0 :                         out_target(buf, ((gistxlogPageUpdate *) rec)->node, ((gistxlogPageUpdate *) rec)->key);
     487               0 :                         break;
     488                 :                 case XLOG_GIST_PAGE_SPLIT:
     489               0 :                         out_gistxlogPageSplit(buf, (gistxlogPageSplit *) rec);
     490               0 :                         break;
     491                 :                 case XLOG_GIST_CREATE_INDEX:
     492               0 :                         appendStringInfo(buf, "create_index: rel %u/%u/%u",
     493                 :                                                          ((RelFileNode *) rec)->spcNode,
     494                 :                                                          ((RelFileNode *) rec)->dbNode,
     495                 :                                                          ((RelFileNode *) rec)->relNode);
     496               0 :                         break;
     497                 :                 case XLOG_GIST_INSERT_COMPLETE:
     498               0 :                         appendStringInfo(buf, "complete_insert: rel %u/%u/%u",
     499                 :                                                          ((gistxlogInsertComplete *) rec)->node.spcNode,
     500                 :                                                          ((gistxlogInsertComplete *) rec)->node.dbNode,
     501                 :                                                          ((gistxlogInsertComplete *) rec)->node.relNode);
     502               0 :                         break;
     503                 :                 default:
     504               0 :                         appendStringInfo(buf, "unknown gist op code %u", info);
     505                 :                         break;
     506                 :         }
     507               0 : }
     508                 : 
     509                 : IndexTuple
     510                 : gist_form_invalid_tuple(BlockNumber blkno)
     511               0 : {
     512                 :         /*
     513                 :          * we don't alloc space for null's bitmap, this is invalid tuple, be
     514                 :          * carefull in read and write code
     515                 :          */
     516               0 :         Size            size = IndexInfoFindDataOffset(0);
     517               0 :         IndexTuple      tuple = (IndexTuple) palloc0(size);
     518                 : 
     519               0 :         tuple->t_info |= size;
     520                 : 
     521               0 :         ItemPointerSetBlockNumber(&(tuple->t_tid), blkno);
     522               0 :         GistTupleSetInvalid(tuple);
     523                 : 
     524               0 :         return tuple;
     525                 : }
     526                 : 
     527                 : 
     528                 : static void
     529                 : gistxlogFindPath(Relation index, gistIncompleteInsert *insert)
     530               0 : {
     531                 :         GISTInsertStack *top;
     532                 : 
     533               0 :         insert->pathlen = 0;
     534               0 :         insert->path = NULL;
     535                 : 
     536               0 :         if ((top = gistFindPath(index, insert->origblkno)) != NULL)
     537                 :         {
     538                 :                 int                     i;
     539                 :                 GISTInsertStack *ptr;
     540                 : 
     541               0 :                 for (ptr = top; ptr; ptr = ptr->parent)
     542               0 :                         insert->pathlen++;
     543                 : 
     544               0 :                 insert->path = (BlockNumber *) palloc(sizeof(BlockNumber) * insert->pathlen);
     545                 : 
     546               0 :                 i = 0;
     547               0 :                 for (ptr = top; ptr; ptr = ptr->parent)
     548               0 :                         insert->path[i++] = ptr->blkno;
     549                 :         }
     550                 :         else
     551               0 :                 elog(ERROR, "lost parent for block %u", insert->origblkno);
     552               0 : }
     553                 : 
     554                 : static SplitedPageLayout *
     555                 : gistMakePageLayout(Buffer *buffers, int nbuffers)
     556               0 : {
     557               0 :         SplitedPageLayout *res = NULL,
     558                 :                            *resptr;
     559                 : 
     560               0 :         while (nbuffers-- > 0)
     561                 :         {
     562               0 :                 Page            page = BufferGetPage(buffers[nbuffers]);
     563                 :                 IndexTuple *vec;
     564                 :                 int                     veclen;
     565                 : 
     566               0 :                 resptr = (SplitedPageLayout *) palloc0(sizeof(SplitedPageLayout));
     567                 : 
     568               0 :                 resptr->block.blkno = BufferGetBlockNumber(buffers[nbuffers]);
     569               0 :                 resptr->block.num = PageGetMaxOffsetNumber(page);
     570                 : 
     571               0 :                 vec = gistextractpage(page, &veclen);
     572               0 :                 resptr->list = gistfillitupvec(vec, veclen, &(resptr->lenlist));
     573                 : 
     574               0 :                 resptr->next = res;
     575               0 :                 res = resptr;
     576                 :         }
     577                 : 
     578               0 :         return res;
     579                 : }
     580                 : 
     581                 : /*
     582                 :  * Continue insert after crash.  In normal situations, there aren't any
     583                 :  * incomplete inserts, but if a crash occurs partway through an insertion
     584                 :  * sequence, we'll need to finish making the index valid at the end of WAL
     585                 :  * replay.
     586                 :  *
     587                 :  * Note that we assume the index is now in a valid state, except for the
     588                 :  * unfinished insertion.  In particular it's safe to invoke gistFindPath();
     589                 :  * there shouldn't be any garbage pages for it to run into.
     590                 :  *
     591                 :  * To complete insert we can't use basic insertion algorithm because
     592                 :  * during insertion we can't call user-defined support functions of opclass.
     593                 :  * So, we insert 'invalid' tuples without real key and do it by separate algorithm.
     594                 :  * 'invalid' tuple should be updated by vacuum full.
     595                 :  */
     596                 : static void
     597                 : gistContinueInsert(gistIncompleteInsert *insert)
     598               0 : {
     599                 :         IndexTuple *itup;
     600                 :         int                     i,
     601                 :                                 lenitup;
     602                 :         Relation        index;
     603                 : 
     604               0 :         index = XLogOpenRelation(insert->node);
     605                 : 
     606                 :         /*
     607                 :          * needed vector itup never will be more than initial lenblkno+2, because
     608                 :          * during this processing Indextuple can be only smaller
     609                 :          */
     610               0 :         lenitup = insert->lenblk;
     611               0 :         itup = (IndexTuple *) palloc(sizeof(IndexTuple) * (lenitup + 2 /* guarantee root split */ ));
     612                 : 
     613               0 :         for (i = 0; i < insert->lenblk; i++)
     614               0 :                 itup[i] = gist_form_invalid_tuple(insert->blkno[i]);
     615                 : 
     616                 :         /*
     617                 :          * any insertion of itup[] should make LOG message about
     618                 :          */
     619                 : 
     620               0 :         if (insert->origblkno == GIST_ROOT_BLKNO)
     621                 :         {
     622                 :                 /*
     623                 :                  * it was split root, so we should only make new root. it can't be
     624                 :                  * simple insert into root, we should replace all content of root.
     625                 :                  */
     626               0 :                 Buffer          buffer = XLogReadBuffer(index, GIST_ROOT_BLKNO, true);
     627                 : 
     628               0 :                 gistnewroot(index, buffer, itup, lenitup, NULL);
     629               0 :                 UnlockReleaseBuffer(buffer);
     630                 :         }
     631                 :         else
     632                 :         {
     633                 :                 Buffer     *buffers;
     634                 :                 Page       *pages;
     635                 :                 int                     numbuffer;
     636                 :                 OffsetNumber *todelete;
     637                 : 
     638                 :                 /* construct path */
     639               0 :                 gistxlogFindPath(index, insert);
     640                 : 
     641                 :                 Assert(insert->pathlen > 0);
     642                 : 
     643               0 :                 buffers = (Buffer *) palloc(sizeof(Buffer) * (insert->lenblk + 2 /* guarantee root split */ ));
     644               0 :                 pages = (Page *) palloc(sizeof(Page) * (insert->lenblk + 2 /* guarantee root split */ ));
     645               0 :                 todelete = (OffsetNumber *) palloc(sizeof(OffsetNumber) * (insert->lenblk + 2 /* guarantee root split */ ));
     646                 : 
     647               0 :                 for (i = 0; i < insert->pathlen; i++)
     648                 :                 {
     649                 :                         int                     j,
     650                 :                                                 k,
     651               0 :                                                 pituplen = 0;
     652                 :                         XLogRecData *rdata;
     653                 :                         XLogRecPtr      recptr;
     654               0 :                         Buffer          tempbuffer = InvalidBuffer;
     655               0 :                         int                     ntodelete = 0;
     656                 : 
     657               0 :                         numbuffer = 1;
     658               0 :                         buffers[0] = ReadBuffer(index, insert->path[i]);
     659               0 :                         LockBuffer(buffers[0], GIST_EXCLUSIVE);
     660                 : 
     661                 :                         /*
     662                 :                          * we check buffer, because we restored page earlier
     663                 :                          */
     664               0 :                         gistcheckpage(index, buffers[0]);
     665                 : 
     666               0 :                         pages[0] = BufferGetPage(buffers[0]);
     667                 :                         Assert(!GistPageIsLeaf(pages[0]));
     668                 : 
     669               0 :                         pituplen = PageGetMaxOffsetNumber(pages[0]);
     670                 : 
     671                 :                         /* find remove old IndexTuples to remove */
     672               0 :                         for (j = 0; j < pituplen && ntodelete < lenitup; j++)
     673                 :                         {
     674                 :                                 BlockNumber blkno;
     675               0 :                                 ItemId          iid = PageGetItemId(pages[0], j + FirstOffsetNumber);
     676               0 :                                 IndexTuple      idxtup = (IndexTuple) PageGetItem(pages[0], iid);
     677                 : 
     678               0 :                                 blkno = ItemPointerGetBlockNumber(&(idxtup->t_tid));
     679                 : 
     680               0 :                                 for (k = 0; k < lenitup; k++)
     681               0 :                                         if (ItemPointerGetBlockNumber(&(itup[k]->t_tid)) == blkno)
     682                 :                                         {
     683               0 :                                                 todelete[ntodelete] = j + FirstOffsetNumber - ntodelete;
     684               0 :                                                 ntodelete++;
     685               0 :                                                 break;
     686                 :                                         }
     687                 :                         }
     688                 : 
     689               0 :                         if (ntodelete == 0)
     690               0 :                                 elog(PANIC, "gistContinueInsert: cannot find pointer to page(s)");
     691                 : 
     692                 :                         /*
     693                 :                          * we check space with subtraction only first tuple to delete,
     694                 :                          * hope, that wiil be enough space....
     695                 :                          */
     696                 : 
     697               0 :                         if (gistnospace(pages[0], itup, lenitup, *todelete, 0))
     698                 :                         {
     699                 : 
     700                 :                                 /* no space left on page, so we must split */
     701               0 :                                 buffers[numbuffer] = ReadBuffer(index, P_NEW);
     702               0 :                                 LockBuffer(buffers[numbuffer], GIST_EXCLUSIVE);
     703               0 :                                 GISTInitBuffer(buffers[numbuffer], 0);
     704               0 :                                 pages[numbuffer] = BufferGetPage(buffers[numbuffer]);
     705               0 :                                 gistfillbuffer(index, pages[numbuffer], itup, lenitup, FirstOffsetNumber);
     706               0 :                                 numbuffer++;
     707                 : 
     708               0 :                                 if (BufferGetBlockNumber(buffers[0]) == GIST_ROOT_BLKNO)
     709                 :                                 {
     710                 :                                         Buffer          tmp;
     711                 : 
     712                 :                                         /*
     713                 :                                          * we split root, just copy content from root to new page
     714                 :                                          */
     715                 : 
     716                 :                                         /* sanity check */
     717               0 :                                         if (i + 1 != insert->pathlen)
     718               0 :                                                 elog(PANIC, "unexpected pathlen in index \"%s\"",
     719                 :                                                          RelationGetRelationName(index));
     720                 : 
     721                 :                                         /* fill new page, root will be changed later */
     722               0 :                                         tempbuffer = ReadBuffer(index, P_NEW);
     723               0 :                                         LockBuffer(tempbuffer, GIST_EXCLUSIVE);
     724               0 :                                         memcpy(BufferGetPage(tempbuffer), pages[0], BufferGetPageSize(tempbuffer));
     725                 : 
     726                 :                                         /* swap buffers[0] (was root) and temp buffer */
     727               0 :                                         tmp = buffers[0];
     728               0 :                                         buffers[0] = tempbuffer;
     729               0 :                                         tempbuffer = tmp;       /* now in tempbuffer GIST_ROOT_BLKNO,
     730                 :                                                                                  * it is still unchanged */
     731                 : 
     732               0 :                                         pages[0] = BufferGetPage(buffers[0]);
     733                 :                                 }
     734                 : 
     735               0 :                                 START_CRIT_SECTION();
     736                 : 
     737               0 :                                 for (j = 0; j < ntodelete; j++)
     738               0 :                                         PageIndexTupleDelete(pages[0], todelete[j]);
     739                 : 
     740               0 :                                 rdata = formSplitRdata(index->rd_node, insert->path[i],
     741                 :                                                                            false, &(insert->key),
     742                 :                                                                          gistMakePageLayout(buffers, numbuffer));
     743                 : 
     744                 :                         }
     745                 :                         else
     746                 :                         {
     747               0 :                                 START_CRIT_SECTION();
     748                 : 
     749               0 :                                 for (j = 0; j < ntodelete; j++)
     750               0 :                                         PageIndexTupleDelete(pages[0], todelete[j]);
     751               0 :                                 gistfillbuffer(index, pages[0], itup, lenitup, InvalidOffsetNumber);
     752                 : 
     753               0 :                                 rdata = formUpdateRdata(index->rd_node, buffers[0],
     754                 :                                                                                 todelete, ntodelete,
     755                 :                                                                                 itup, lenitup, &(insert->key));
     756                 :                         }
     757                 : 
     758                 :                         /*
     759                 :                          * use insert->key as mark for completion of insert (form*Rdata()
     760                 :                          * above) for following possible replays
     761                 :                          */
     762                 : 
     763                 :                         /* write pages, we should mark it dirty befor XLogInsert() */
     764               0 :                         for (j = 0; j < numbuffer; j++)
     765                 :                         {
     766               0 :                                 GistPageGetOpaque(pages[j])->rightlink = InvalidBlockNumber;
     767               0 :                                 MarkBufferDirty(buffers[j]);
     768                 :                         }
     769               0 :                         recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE, rdata);
     770               0 :                         for (j = 0; j < numbuffer; j++)
     771                 :                         {
     772               0 :                                 PageSetLSN(pages[j], recptr);
     773               0 :                                 PageSetTLI(pages[j], ThisTimeLineID);
     774                 :                         }
     775                 : 
     776               0 :                         END_CRIT_SECTION();
     777                 : 
     778               0 :                         lenitup = numbuffer;
     779               0 :                         for (j = 0; j < numbuffer; j++)
     780                 :                         {
     781               0 :                                 itup[j] = gist_form_invalid_tuple(BufferGetBlockNumber(buffers[j]));
     782               0 :                                 UnlockReleaseBuffer(buffers[j]);
     783                 :                         }
     784                 : 
     785               0 :                         if (tempbuffer != InvalidBuffer)
     786                 :                         {
     787                 :                                 /*
     788                 :                                  * it was a root split, so fill it by new values
     789                 :                                  */
     790               0 :                                 gistnewroot(index, tempbuffer, itup, lenitup, &(insert->key));
     791               0 :                                 UnlockReleaseBuffer(tempbuffer);
     792                 :                         }
     793                 :                 }
     794                 :         }
     795                 : 
     796               0 :         ereport(LOG,
     797                 :                         (errmsg("index %u/%u/%u needs VACUUM FULL or REINDEX to finish crash recovery",
     798                 :                         insert->node.spcNode, insert->node.dbNode, insert->node.relNode),
     799                 :                    errdetail("Incomplete insertion detected during crash replay.")));
     800               0 : }
     801                 : 
     802                 : void
     803                 : gist_xlog_startup(void)
     804               0 : {
     805               0 :         incomplete_inserts = NIL;
     806               0 :         insertCtx = AllocSetContextCreate(CurrentMemoryContext,
     807                 :                                                                           "GiST recovery temporary context",
     808                 :                                                                           ALLOCSET_DEFAULT_MINSIZE,
     809                 :                                                                           ALLOCSET_DEFAULT_INITSIZE,
     810                 :                                                                           ALLOCSET_DEFAULT_MAXSIZE);
     811               0 :         opCtx = createTempGistContext();
     812               0 : }
     813                 : 
     814                 : void
     815                 : gist_xlog_cleanup(void)
     816               0 : {
     817                 :         ListCell   *l;
     818                 :         MemoryContext oldCxt;
     819                 : 
     820               0 :         oldCxt = MemoryContextSwitchTo(opCtx);
     821                 : 
     822               0 :         foreach(l, incomplete_inserts)
     823                 :         {
     824               0 :                 gistIncompleteInsert *insert = (gistIncompleteInsert *) lfirst(l);
     825                 : 
     826               0 :                 gistContinueInsert(insert);
     827               0 :                 MemoryContextReset(opCtx);
     828                 :         }
     829               0 :         MemoryContextSwitchTo(oldCxt);
     830                 : 
     831               0 :         MemoryContextDelete(opCtx);
     832               0 :         MemoryContextDelete(insertCtx);
     833               0 : }
     834                 : 
     835                 : bool
     836                 : gist_safe_restartpoint(void)
     837               0 : {
     838               0 :         if (incomplete_inserts)
     839               0 :                 return false;
     840               0 :         return true;
     841                 : }
     842                 : 
     843                 : 
     844                 : XLogRecData *
     845                 : formSplitRdata(RelFileNode node, BlockNumber blkno, bool page_is_leaf,
     846                 :                            ItemPointer key, SplitedPageLayout *dist)
     847              49 : {
     848                 :         XLogRecData *rdata;
     849              49 :         gistxlogPageSplit *xlrec = (gistxlogPageSplit *) palloc(sizeof(gistxlogPageSplit));
     850                 :         SplitedPageLayout *ptr;
     851              49 :         int                     npage = 0,
     852              49 :                                 cur = 1;
     853                 : 
     854              49 :         ptr = dist;
     855             196 :         while (ptr)
     856                 :         {
     857              98 :                 npage++;
     858              98 :                 ptr = ptr->next;
     859                 :         }
     860                 : 
     861              49 :         rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (npage * 2 + 2));
     862                 : 
     863              49 :         xlrec->node = node;
     864              49 :         xlrec->origblkno = blkno;
     865              49 :         xlrec->origleaf = page_is_leaf;
     866              49 :         xlrec->npage = (uint16) npage;
     867              49 :         if (key)
     868              49 :                 xlrec->key = *key;
     869                 :         else
     870               0 :                 ItemPointerSetInvalid(&(xlrec->key));
     871                 : 
     872              49 :         rdata[0].buffer = InvalidBuffer;
     873              49 :         rdata[0].data = (char *) xlrec;
     874              49 :         rdata[0].len = sizeof(gistxlogPageSplit);
     875              49 :         rdata[0].next = NULL;
     876                 : 
     877              49 :         ptr = dist;
     878             196 :         while (ptr)
     879                 :         {
     880              98 :                 rdata[cur].buffer = InvalidBuffer;
     881              98 :                 rdata[cur].data = (char *) &(ptr->block);
     882              98 :                 rdata[cur].len = sizeof(gistxlogPage);
     883              98 :                 rdata[cur - 1].next = &(rdata[cur]);
     884              98 :                 cur++;
     885                 : 
     886              98 :                 rdata[cur].buffer = InvalidBuffer;
     887              98 :                 rdata[cur].data = (char *) (ptr->list);
     888              98 :                 rdata[cur].len = ptr->lenlist;
     889              98 :                 rdata[cur - 1].next = &(rdata[cur]);
     890              98 :                 rdata[cur].next = NULL;
     891              98 :                 cur++;
     892              98 :                 ptr = ptr->next;
     893                 :         }
     894                 : 
     895              49 :         return rdata;
     896                 : }
     897                 : 
     898                 : /*
     899                 :  * Construct the rdata array for an XLOG record describing a page update
     900                 :  * (deletion and/or insertion of tuples on a single index page).
     901                 :  *
     902                 :  * Note that both the todelete array and the tuples are marked as belonging
     903                 :  * to the target buffer; they need not be stored in XLOG if XLogInsert decides
     904                 :  * to log the whole buffer contents instead.  Also, we take care that there's
     905                 :  * at least one rdata item referencing the buffer, even when ntodelete and
     906                 :  * ituplen are both zero; this ensures that XLogInsert knows about the buffer.
     907                 :  */
     908                 : XLogRecData *
     909                 : formUpdateRdata(RelFileNode node, Buffer buffer,
     910                 :                                 OffsetNumber *todelete, int ntodelete,
     911                 :                                 IndexTuple *itup, int ituplen, ItemPointer key)
     912            4445 : {
     913                 :         XLogRecData *rdata;
     914                 :         gistxlogPageUpdate *xlrec;
     915                 :         int                     cur,
     916                 :                                 i;
     917                 : 
     918            4445 :         rdata = (XLogRecData *) palloc(sizeof(XLogRecData) * (3 + ituplen));
     919            4445 :         xlrec = (gistxlogPageUpdate *) palloc(sizeof(gistxlogPageUpdate));
     920                 : 
     921            4445 :         xlrec->node = node;
     922            4445 :         xlrec->blkno = BufferGetBlockNumber(buffer);
     923            4445 :         xlrec->ntodelete = ntodelete;
     924                 : 
     925            4445 :         if (key)
     926            4445 :                 xlrec->key = *key;
     927                 :         else
     928               0 :                 ItemPointerSetInvalid(&(xlrec->key));
     929                 : 
     930            4445 :         rdata[0].buffer = buffer;
     931            4445 :         rdata[0].buffer_std = true;
     932            4445 :         rdata[0].data = NULL;
     933            4445 :         rdata[0].len = 0;
     934            4445 :         rdata[0].next = &(rdata[1]);
     935                 : 
     936            4445 :         rdata[1].data = (char *) xlrec;
     937            4445 :         rdata[1].len = sizeof(gistxlogPageUpdate);
     938            4445 :         rdata[1].buffer = InvalidBuffer;
     939            4445 :         rdata[1].next = &(rdata[2]);
     940                 : 
     941            4445 :         rdata[2].data = (char *) todelete;
     942            4445 :         rdata[2].len = MAXALIGN(sizeof(OffsetNumber) * ntodelete);
     943            4445 :         rdata[2].buffer = buffer;
     944            4445 :         rdata[2].buffer_std = true;
     945            4445 :         rdata[2].next = NULL;
     946                 : 
     947                 :         /* new tuples */
     948            4445 :         cur = 3;
     949            8939 :         for (i = 0; i < ituplen; i++)
     950                 :         {
     951            4494 :                 rdata[cur - 1].next = &(rdata[cur]);
     952            4494 :                 rdata[cur].data = (char *) (itup[i]);
     953            4494 :                 rdata[cur].len = IndexTupleSize(itup[i]);
     954            4494 :                 rdata[cur].buffer = buffer;
     955            4494 :                 rdata[cur].buffer_std = true;
     956            4494 :                 rdata[cur].next = NULL;
     957            4494 :                 cur++;
     958                 :         }
     959                 : 
     960            4445 :         return rdata;
     961                 : }
     962                 : 
     963                 : XLogRecPtr
     964                 : gistxlogInsertCompletion(RelFileNode node, ItemPointerData *keys, int len)
     965            3080 : {
     966                 :         gistxlogInsertComplete xlrec;
     967                 :         XLogRecData rdata[2];
     968                 :         XLogRecPtr      recptr;
     969                 : 
     970                 :         Assert(len > 0);
     971            3080 :         xlrec.node = node;
     972                 : 
     973            3080 :         rdata[0].buffer = InvalidBuffer;
     974            3080 :         rdata[0].data = (char *) &xlrec;
     975            3080 :         rdata[0].len = sizeof(gistxlogInsertComplete);
     976            3080 :         rdata[0].next = &(rdata[1]);
     977                 : 
     978            3080 :         rdata[1].buffer = InvalidBuffer;
     979            3080 :         rdata[1].data = (char *) keys;
     980            3080 :         rdata[1].len = sizeof(ItemPointerData) * len;
     981            3080 :         rdata[1].next = NULL;
     982                 : 
     983            3080 :         START_CRIT_SECTION();
     984                 : 
     985            3080 :         recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_INSERT_COMPLETE, rdata);
     986                 : 
     987            3080 :         END_CRIT_SECTION();
     988                 : 
     989            3080 :         return recptr;
     990                 : }

Generated by: LTP GCOV extension version 1.5