LCOV - differential code coverage report
Current view: top level - src/backend/access/gin - ginxlog.c (source / functions) Coverage Total Hit UNC UBC GNC CBC DUB
Current: Differential Code Coverage HEAD vs 15 Lines: 74.3 % 374 278 1 95 278 1
Current Date: 2023-04-08 15:15:32 Functions: 82.4 % 17 14 3 1 13
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * ginxlog.c
       4                 :  *    WAL replay logic for inverted index.
       5                 :  *
       6                 :  *
       7                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
       8                 :  * Portions Copyright (c) 1994, Regents of the University of California
       9                 :  *
      10                 :  * IDENTIFICATION
      11                 :  *           src/backend/access/gin/ginxlog.c
      12                 :  *-------------------------------------------------------------------------
      13                 :  */
      14                 : #include "postgres.h"
      15                 : 
      16                 : #include "access/bufmask.h"
      17                 : #include "access/gin_private.h"
      18                 : #include "access/ginxlog.h"
      19                 : #include "access/xlogutils.h"
      20                 : #include "utils/memutils.h"
      21                 : 
      22                 : static MemoryContext opCtx;     /* working memory for operations */
      23                 : 
      24                 : static void
      25 CBC         139 : ginRedoClearIncompleteSplit(XLogReaderState *record, uint8 block_id)
      26                 : {
      27             139 :     XLogRecPtr  lsn = record->EndRecPtr;
      28                 :     Buffer      buffer;
      29                 :     Page        page;
      30                 : 
      31             139 :     if (XLogReadBufferForRedo(record, block_id, &buffer) == BLK_NEEDS_REDO)
      32                 :     {
      33             139 :         page = (Page) BufferGetPage(buffer);
      34             139 :         GinPageGetOpaque(page)->flags &= ~GIN_INCOMPLETE_SPLIT;
      35                 : 
      36             139 :         PageSetLSN(page, lsn);
      37             139 :         MarkBufferDirty(buffer);
      38                 :     }
      39             139 :     if (BufferIsValid(buffer))
      40             139 :         UnlockReleaseBuffer(buffer);
      41             139 : }
      42                 : 
      43                 : static void
      44               3 : ginRedoCreatePTree(XLogReaderState *record)
      45                 : {
      46               3 :     XLogRecPtr  lsn = record->EndRecPtr;
      47               3 :     ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree *) XLogRecGetData(record);
      48                 :     char       *ptr;
      49                 :     Buffer      buffer;
      50                 :     Page        page;
      51                 : 
      52               3 :     buffer = XLogInitBufferForRedo(record, 0);
      53               3 :     page = (Page) BufferGetPage(buffer);
      54                 : 
      55               3 :     GinInitBuffer(buffer, GIN_DATA | GIN_LEAF | GIN_COMPRESSED);
      56                 : 
      57               3 :     ptr = XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree);
      58                 : 
      59                 :     /* Place page data */
      60               3 :     memcpy(GinDataLeafPageGetPostingList(page), ptr, data->size);
      61                 : 
      62               3 :     GinDataPageSetDataSize(page, data->size);
      63                 : 
      64               3 :     PageSetLSN(page, lsn);
      65                 : 
      66               3 :     MarkBufferDirty(buffer);
      67               3 :     UnlockReleaseBuffer(buffer);
      68               3 : }
      69                 : 
      70                 : static void
      71           23658 : ginRedoInsertEntry(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rdata)
      72                 : {
      73           23658 :     Page        page = BufferGetPage(buffer);
      74           23658 :     ginxlogInsertEntry *data = (ginxlogInsertEntry *) rdata;
      75           23658 :     OffsetNumber offset = data->offset;
      76                 :     IndexTuple  itup;
      77                 : 
      78           23658 :     if (rightblkno != InvalidBlockNumber)
      79                 :     {
      80                 :         /* update link to right page after split */
      81             136 :         Assert(!GinPageIsLeaf(page));
      82             136 :         Assert(offset >= FirstOffsetNumber && offset <= PageGetMaxOffsetNumber(page));
      83             136 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offset));
      84             136 :         GinSetDownlink(itup, rightblkno);
      85                 :     }
      86                 : 
      87           23658 :     if (data->isDelete)
      88                 :     {
      89            3660 :         Assert(GinPageIsLeaf(page));
      90            3660 :         Assert(offset >= FirstOffsetNumber && offset <= PageGetMaxOffsetNumber(page));
      91            3660 :         PageIndexTupleDelete(page, offset);
      92                 :     }
      93                 : 
      94           23658 :     itup = &data->tuple;
      95                 : 
      96           23658 :     if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), offset, false, false) == InvalidOffsetNumber)
      97                 :     {
      98                 :         RelFileLocator locator;
      99                 :         ForkNumber  forknum;
     100                 :         BlockNumber blknum;
     101                 : 
     102 UNC           0 :         BufferGetTag(buffer, &locator, &forknum, &blknum);
     103 UBC           0 :         elog(ERROR, "failed to add item to index page in %u/%u/%u",
     104                 :              locator.spcOid, locator.dbOid, locator.relNumber);
     105                 :     }
     106 CBC       23658 : }
     107                 : 
     108                 : /*
     109                 :  * Redo recompression of posting list.  Doing all the changes in-place is not
     110                 :  * always possible, because it might require more space than we've on the page.
     111                 :  * Instead, once modification is required we copy unprocessed tail of the page
     112                 :  * into separately allocated chunk of memory for further reading original
     113                 :  * versions of segments.  Thanks to that we don't bother about moving page data
     114                 :  * in-place.
     115                 :  */
     116                 : static void
     117            3342 : ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
     118                 : {
     119                 :     int         actionno;
     120                 :     int         segno;
     121                 :     GinPostingList *oldseg;
     122                 :     Pointer     segmentend;
     123                 :     char       *walbuf;
     124                 :     int         totalsize;
     125            3342 :     Pointer     tailCopy = NULL;
     126                 :     Pointer     writePtr;
     127                 :     Pointer     segptr;
     128                 : 
     129                 :     /*
     130                 :      * If the page is in pre-9.4 format, convert to new format first.
     131                 :      */
     132            3342 :     if (!GinPageIsCompressed(page))
     133                 :     {
     134 UBC           0 :         ItemPointer uncompressed = (ItemPointer) GinDataPageGetData(page);
     135               0 :         int         nuncompressed = GinPageGetOpaque(page)->maxoff;
     136                 :         int         npacked;
     137                 : 
     138                 :         /*
     139                 :          * Empty leaf pages are deleted as part of vacuum, but leftmost and
     140                 :          * rightmost pages are never deleted.  So, pg_upgrade'd from pre-9.4
     141                 :          * instances might contain empty leaf pages, and we need to handle
     142                 :          * them correctly.
     143                 :          */
     144               0 :         if (nuncompressed > 0)
     145                 :         {
     146                 :             GinPostingList *plist;
     147                 : 
     148               0 :             plist = ginCompressPostingList(uncompressed, nuncompressed,
     149                 :                                            BLCKSZ, &npacked);
     150               0 :             totalsize = SizeOfGinPostingList(plist);
     151                 : 
     152               0 :             Assert(npacked == nuncompressed);
     153                 : 
     154               0 :             memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize);
     155                 :         }
     156                 :         else
     157                 :         {
     158               0 :             totalsize = 0;
     159                 :         }
     160                 : 
     161               0 :         GinDataPageSetDataSize(page, totalsize);
     162               0 :         GinPageSetCompressed(page);
     163               0 :         GinPageGetOpaque(page)->maxoff = InvalidOffsetNumber;
     164                 :     }
     165                 : 
     166 CBC        3342 :     oldseg = GinDataLeafPageGetPostingList(page);
     167            3342 :     writePtr = (Pointer) oldseg;
     168            3342 :     segmentend = (Pointer) oldseg + GinDataLeafPageGetPostingListSize(page);
     169            3342 :     segno = 0;
     170                 : 
     171            3342 :     walbuf = ((char *) data) + sizeof(ginxlogRecompressDataLeaf);
     172            6687 :     for (actionno = 0; actionno < data->nactions; actionno++)
     173                 :     {
     174            3345 :         uint8       a_segno = *((uint8 *) (walbuf++));
     175            3345 :         uint8       a_action = *((uint8 *) (walbuf++));
     176            3345 :         GinPostingList *newseg = NULL;
     177            3345 :         int         newsegsize = 0;
     178            3345 :         ItemPointerData *items = NULL;
     179            3345 :         uint16      nitems = 0;
     180                 :         ItemPointerData *olditems;
     181                 :         int         nolditems;
     182                 :         ItemPointerData *newitems;
     183                 :         int         nnewitems;
     184                 :         int         segsize;
     185                 : 
     186                 :         /* Extract all the information we need from the WAL record */
     187            3345 :         if (a_action == GIN_SEGMENT_INSERT ||
     188                 :             a_action == GIN_SEGMENT_REPLACE)
     189                 :         {
     190              19 :             newseg = (GinPostingList *) walbuf;
     191              19 :             newsegsize = SizeOfGinPostingList(newseg);
     192              19 :             walbuf += SHORTALIGN(newsegsize);
     193                 :         }
     194                 : 
     195            3345 :         if (a_action == GIN_SEGMENT_ADDITEMS)
     196                 :         {
     197            3326 :             memcpy(&nitems, walbuf, sizeof(uint16));
     198            3326 :             walbuf += sizeof(uint16);
     199            3326 :             items = (ItemPointerData *) walbuf;
     200            3326 :             walbuf += nitems * sizeof(ItemPointerData);
     201                 :         }
     202                 : 
     203                 :         /* Skip to the segment that this action concerns */
     204            3345 :         Assert(segno <= a_segno);
     205           59803 :         while (segno < a_segno)
     206                 :         {
     207                 :             /*
     208                 :              * Once modification is started and page tail is copied, we've to
     209                 :              * copy unmodified segments.
     210                 :              */
     211           56458 :             segsize = SizeOfGinPostingList(oldseg);
     212           56458 :             if (tailCopy)
     213                 :             {
     214 UBC           0 :                 Assert(writePtr + segsize < PageGetSpecialPointer(page));
     215               0 :                 memcpy(writePtr, (Pointer) oldseg, segsize);
     216                 :             }
     217 CBC       56458 :             writePtr += segsize;
     218           56458 :             oldseg = GinNextPostingListSegment(oldseg);
     219           56458 :             segno++;
     220                 :         }
     221                 : 
     222                 :         /*
     223                 :          * ADDITEMS action is handled like REPLACE, but the new segment to
     224                 :          * replace the old one is reconstructed using the old segment from
     225                 :          * disk and the new items from the WAL record.
     226                 :          */
     227            3345 :         if (a_action == GIN_SEGMENT_ADDITEMS)
     228                 :         {
     229                 :             int         npacked;
     230                 : 
     231            3326 :             olditems = ginPostingListDecode(oldseg, &nolditems);
     232                 : 
     233            3326 :             newitems = ginMergeItemPointers(items, nitems,
     234                 :                                             olditems, nolditems,
     235                 :                                             &nnewitems);
     236            3326 :             Assert(nnewitems == nolditems + nitems);
     237                 : 
     238            3326 :             newseg = ginCompressPostingList(newitems, nnewitems,
     239                 :                                             BLCKSZ, &npacked);
     240            3326 :             Assert(npacked == nnewitems);
     241                 : 
     242            3326 :             newsegsize = SizeOfGinPostingList(newseg);
     243            3326 :             a_action = GIN_SEGMENT_REPLACE;
     244                 :         }
     245                 : 
     246            3345 :         segptr = (Pointer) oldseg;
     247            3345 :         if (segptr != segmentend)
     248            3326 :             segsize = SizeOfGinPostingList(oldseg);
     249                 :         else
     250                 :         {
     251                 :             /*
     252                 :              * Positioned after the last existing segment. Only INSERTs
     253                 :              * expected here.
     254                 :              */
     255              19 :             Assert(a_action == GIN_SEGMENT_INSERT);
     256              19 :             segsize = 0;
     257                 :         }
     258                 : 
     259                 :         /*
     260                 :          * We're about to start modification of the page.  So, copy tail of
     261                 :          * the page if it's not done already.
     262                 :          */
     263            3345 :         if (!tailCopy && segptr != segmentend)
     264                 :         {
     265            3326 :             int         tailSize = segmentend - segptr;
     266                 : 
     267            3326 :             tailCopy = (Pointer) palloc(tailSize);
     268            3326 :             memcpy(tailCopy, segptr, tailSize);
     269            3326 :             segptr = tailCopy;
     270            3326 :             oldseg = (GinPostingList *) segptr;
     271            3326 :             segmentend = segptr + tailSize;
     272                 :         }
     273                 : 
     274            3345 :         switch (a_action)
     275                 :         {
     276 UBC           0 :             case GIN_SEGMENT_DELETE:
     277               0 :                 segptr += segsize;
     278               0 :                 segno++;
     279               0 :                 break;
     280                 : 
     281 CBC          19 :             case GIN_SEGMENT_INSERT:
     282                 :                 /* copy the new segment in place */
     283              19 :                 Assert(writePtr + newsegsize <= PageGetSpecialPointer(page));
     284              19 :                 memcpy(writePtr, newseg, newsegsize);
     285              19 :                 writePtr += newsegsize;
     286              19 :                 break;
     287                 : 
     288            3326 :             case GIN_SEGMENT_REPLACE:
     289                 :                 /* copy the new version of segment in place */
     290            3326 :                 Assert(writePtr + newsegsize <= PageGetSpecialPointer(page));
     291            3326 :                 memcpy(writePtr, newseg, newsegsize);
     292            3326 :                 writePtr += newsegsize;
     293            3326 :                 segptr += segsize;
     294            3326 :                 segno++;
     295            3326 :                 break;
     296                 : 
     297 UBC           0 :             default:
     298               0 :                 elog(ERROR, "unexpected GIN leaf action: %u", a_action);
     299                 :         }
     300 CBC        3345 :         oldseg = (GinPostingList *) segptr;
     301                 :     }
     302                 : 
     303                 :     /* Copy the rest of unmodified segments if any. */
     304            3342 :     segptr = (Pointer) oldseg;
     305            3342 :     if (segptr != segmentend && tailCopy)
     306                 :     {
     307 UBC           0 :         int         restSize = segmentend - segptr;
     308                 : 
     309               0 :         Assert(writePtr + restSize <= PageGetSpecialPointer(page));
     310               0 :         memcpy(writePtr, segptr, restSize);
     311               0 :         writePtr += restSize;
     312                 :     }
     313                 : 
     314 CBC        3342 :     totalsize = writePtr - (Pointer) GinDataLeafPageGetPostingList(page);
     315            3342 :     GinDataPageSetDataSize(page, totalsize);
     316            3342 : }
     317                 : 
     318                 : static void
     319            3345 : ginRedoInsertData(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rdata)
     320                 : {
     321            3345 :     Page        page = BufferGetPage(buffer);
     322                 : 
     323            3345 :     if (isLeaf)
     324                 :     {
     325            3342 :         ginxlogRecompressDataLeaf *data = (ginxlogRecompressDataLeaf *) rdata;
     326                 : 
     327            3342 :         Assert(GinPageIsLeaf(page));
     328                 : 
     329            3342 :         ginRedoRecompress(page, data);
     330                 :     }
     331                 :     else
     332                 :     {
     333               3 :         ginxlogInsertDataInternal *data = (ginxlogInsertDataInternal *) rdata;
     334                 :         PostingItem *oldpitem;
     335                 : 
     336               3 :         Assert(!GinPageIsLeaf(page));
     337                 : 
     338                 :         /* update link to right page after split */
     339               3 :         oldpitem = GinDataPageGetPostingItem(page, data->offset);
     340               3 :         PostingItemSetBlockNumber(oldpitem, rightblkno);
     341                 : 
     342               3 :         GinDataPageAddPostingItem(page, &data->newitem, data->offset);
     343                 :     }
     344            3345 : }
     345                 : 
     346                 : static void
     347           27004 : ginRedoInsert(XLogReaderState *record)
     348                 : {
     349           27004 :     XLogRecPtr  lsn = record->EndRecPtr;
     350           27004 :     ginxlogInsert *data = (ginxlogInsert *) XLogRecGetData(record);
     351                 :     Buffer      buffer;
     352                 : #ifdef NOT_USED
     353                 :     BlockNumber leftChildBlkno = InvalidBlockNumber;
     354                 : #endif
     355           27004 :     BlockNumber rightChildBlkno = InvalidBlockNumber;
     356           27004 :     bool        isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0;
     357                 : 
     358                 :     /*
     359                 :      * First clear incomplete-split flag on child page if this finishes a
     360                 :      * split.
     361                 :      */
     362           27004 :     if (!isLeaf)
     363                 :     {
     364             139 :         char       *payload = XLogRecGetData(record) + sizeof(ginxlogInsert);
     365                 : 
     366                 : #ifdef NOT_USED
     367                 :         leftChildBlkno = BlockIdGetBlockNumber((BlockId) payload);
     368                 : #endif
     369             139 :         payload += sizeof(BlockIdData);
     370             139 :         rightChildBlkno = BlockIdGetBlockNumber((BlockId) payload);
     371             139 :         payload += sizeof(BlockIdData);
     372                 : 
     373             139 :         ginRedoClearIncompleteSplit(record, 1);
     374                 :     }
     375                 : 
     376           27004 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
     377                 :     {
     378           27003 :         Page        page = BufferGetPage(buffer);
     379                 :         Size        len;
     380           27003 :         char       *payload = XLogRecGetBlockData(record, 0, &len);
     381                 : 
     382                 :         /* How to insert the payload is tree-type specific */
     383           27003 :         if (data->flags & GIN_INSERT_ISDATA)
     384                 :         {
     385            3345 :             Assert(GinPageIsData(page));
     386            3345 :             ginRedoInsertData(buffer, isLeaf, rightChildBlkno, payload);
     387                 :         }
     388                 :         else
     389                 :         {
     390           23658 :             Assert(!GinPageIsData(page));
     391           23658 :             ginRedoInsertEntry(buffer, isLeaf, rightChildBlkno, payload);
     392                 :         }
     393                 : 
     394           27003 :         PageSetLSN(page, lsn);
     395           27003 :         MarkBufferDirty(buffer);
     396                 :     }
     397           27004 :     if (BufferIsValid(buffer))
     398           27004 :         UnlockReleaseBuffer(buffer);
     399           27004 : }
     400                 : 
     401                 : static void
     402             142 : ginRedoSplit(XLogReaderState *record)
     403                 : {
     404             142 :     ginxlogSplit *data = (ginxlogSplit *) XLogRecGetData(record);
     405                 :     Buffer      lbuffer,
     406                 :                 rbuffer,
     407                 :                 rootbuf;
     408             142 :     bool        isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0;
     409             142 :     bool        isRoot = (data->flags & GIN_SPLIT_ROOT) != 0;
     410                 : 
     411                 :     /*
     412                 :      * First clear incomplete-split flag on child page if this finishes a
     413                 :      * split
     414                 :      */
     415             142 :     if (!isLeaf)
     416 UBC           0 :         ginRedoClearIncompleteSplit(record, 3);
     417                 : 
     418 CBC         142 :     if (XLogReadBufferForRedo(record, 0, &lbuffer) != BLK_RESTORED)
     419 UBC           0 :         elog(ERROR, "GIN split record did not contain a full-page image of left page");
     420                 : 
     421 CBC         142 :     if (XLogReadBufferForRedo(record, 1, &rbuffer) != BLK_RESTORED)
     422 UBC           0 :         elog(ERROR, "GIN split record did not contain a full-page image of right page");
     423                 : 
     424 CBC         142 :     if (isRoot)
     425                 :     {
     426               3 :         if (XLogReadBufferForRedo(record, 2, &rootbuf) != BLK_RESTORED)
     427 UBC           0 :             elog(ERROR, "GIN split record did not contain a full-page image of root page");
     428 CBC           3 :         UnlockReleaseBuffer(rootbuf);
     429                 :     }
     430                 : 
     431             142 :     UnlockReleaseBuffer(rbuffer);
     432             142 :     UnlockReleaseBuffer(lbuffer);
     433             142 : }
     434                 : 
     435                 : /*
     436                 :  * VACUUM_PAGE record contains simply a full image of the page, similar to
     437                 :  * an XLOG_FPI record.
     438                 :  */
     439                 : static void
     440               2 : ginRedoVacuumPage(XLogReaderState *record)
     441                 : {
     442                 :     Buffer      buffer;
     443                 : 
     444               2 :     if (XLogReadBufferForRedo(record, 0, &buffer) != BLK_RESTORED)
     445                 :     {
     446 UBC           0 :         elog(ERROR, "replay of gin entry tree page vacuum did not restore the page");
     447                 :     }
     448 CBC           2 :     UnlockReleaseBuffer(buffer);
     449               2 : }
     450                 : 
     451                 : static void
     452 UBC           0 : ginRedoVacuumDataLeafPage(XLogReaderState *record)
     453                 : {
     454               0 :     XLogRecPtr  lsn = record->EndRecPtr;
     455                 :     Buffer      buffer;
     456                 : 
     457               0 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
     458                 :     {
     459               0 :         Page        page = BufferGetPage(buffer);
     460                 :         Size        len;
     461                 :         ginxlogVacuumDataLeafPage *xlrec;
     462                 : 
     463               0 :         xlrec = (ginxlogVacuumDataLeafPage *) XLogRecGetBlockData(record, 0, &len);
     464                 : 
     465               0 :         Assert(GinPageIsLeaf(page));
     466               0 :         Assert(GinPageIsData(page));
     467                 : 
     468               0 :         ginRedoRecompress(page, &xlrec->data);
     469               0 :         PageSetLSN(page, lsn);
     470               0 :         MarkBufferDirty(buffer);
     471                 :     }
     472               0 :     if (BufferIsValid(buffer))
     473               0 :         UnlockReleaseBuffer(buffer);
     474               0 : }
     475                 : 
     476                 : static void
     477               0 : ginRedoDeletePage(XLogReaderState *record)
     478                 : {
     479               0 :     XLogRecPtr  lsn = record->EndRecPtr;
     480               0 :     ginxlogDeletePage *data = (ginxlogDeletePage *) XLogRecGetData(record);
     481                 :     Buffer      dbuffer;
     482                 :     Buffer      pbuffer;
     483                 :     Buffer      lbuffer;
     484                 :     Page        page;
     485                 : 
     486                 :     /*
     487                 :      * Lock left page first in order to prevent possible deadlock with
     488                 :      * ginStepRight().
     489                 :      */
     490               0 :     if (XLogReadBufferForRedo(record, 2, &lbuffer) == BLK_NEEDS_REDO)
     491                 :     {
     492               0 :         page = BufferGetPage(lbuffer);
     493               0 :         Assert(GinPageIsData(page));
     494               0 :         GinPageGetOpaque(page)->rightlink = data->rightLink;
     495               0 :         PageSetLSN(page, lsn);
     496               0 :         MarkBufferDirty(lbuffer);
     497                 :     }
     498                 : 
     499               0 :     if (XLogReadBufferForRedo(record, 0, &dbuffer) == BLK_NEEDS_REDO)
     500                 :     {
     501               0 :         page = BufferGetPage(dbuffer);
     502               0 :         Assert(GinPageIsData(page));
     503               0 :         GinPageSetDeleted(page);
     504               0 :         GinPageSetDeleteXid(page, data->deleteXid);
     505               0 :         PageSetLSN(page, lsn);
     506               0 :         MarkBufferDirty(dbuffer);
     507                 :     }
     508                 : 
     509               0 :     if (XLogReadBufferForRedo(record, 1, &pbuffer) == BLK_NEEDS_REDO)
     510                 :     {
     511               0 :         page = BufferGetPage(pbuffer);
     512               0 :         Assert(GinPageIsData(page));
     513               0 :         Assert(!GinPageIsLeaf(page));
     514               0 :         GinPageDeletePostingItem(page, data->parentOffset);
     515               0 :         PageSetLSN(page, lsn);
     516               0 :         MarkBufferDirty(pbuffer);
     517                 :     }
     518                 : 
     519               0 :     if (BufferIsValid(lbuffer))
     520               0 :         UnlockReleaseBuffer(lbuffer);
     521               0 :     if (BufferIsValid(pbuffer))
     522               0 :         UnlockReleaseBuffer(pbuffer);
     523               0 :     if (BufferIsValid(dbuffer))
     524               0 :         UnlockReleaseBuffer(dbuffer);
     525               0 : }
     526                 : 
     527                 : static void
     528 CBC       23943 : ginRedoUpdateMetapage(XLogReaderState *record)
     529                 : {
     530           23943 :     XLogRecPtr  lsn = record->EndRecPtr;
     531           23943 :     ginxlogUpdateMeta *data = (ginxlogUpdateMeta *) XLogRecGetData(record);
     532                 :     Buffer      metabuffer;
     533                 :     Page        metapage;
     534                 :     Buffer      buffer;
     535                 : 
     536                 :     /*
     537                 :      * Restore the metapage. This is essentially the same as a full-page
     538                 :      * image, so restore the metapage unconditionally without looking at the
     539                 :      * LSN, to avoid torn page hazards.
     540                 :      */
     541           23943 :     metabuffer = XLogInitBufferForRedo(record, 0);
     542           23943 :     Assert(BufferGetBlockNumber(metabuffer) == GIN_METAPAGE_BLKNO);
     543           23943 :     metapage = BufferGetPage(metabuffer);
     544                 : 
     545           23943 :     GinInitMetabuffer(metabuffer);
     546           23943 :     memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
     547           23943 :     PageSetLSN(metapage, lsn);
     548           23943 :     MarkBufferDirty(metabuffer);
     549                 : 
     550           23943 :     if (data->ntuples > 0)
     551                 :     {
     552                 :         /*
     553                 :          * insert into tail page
     554                 :          */
     555           23756 :         if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
     556                 :         {
     557           23755 :             Page        page = BufferGetPage(buffer);
     558                 :             OffsetNumber off;
     559                 :             int         i;
     560                 :             Size        tupsize;
     561                 :             char       *payload;
     562                 :             IndexTuple  tuples;
     563                 :             Size        totaltupsize;
     564                 : 
     565           23755 :             payload = XLogRecGetBlockData(record, 1, &totaltupsize);
     566           23755 :             tuples = (IndexTuple) payload;
     567                 : 
     568           23755 :             if (PageIsEmpty(page))
     569 UBC           0 :                 off = FirstOffsetNumber;
     570                 :             else
     571 CBC       23755 :                 off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
     572                 : 
     573           95012 :             for (i = 0; i < data->ntuples; i++)
     574                 :             {
     575           71257 :                 tupsize = IndexTupleSize(tuples);
     576                 : 
     577           71257 :                 if (PageAddItem(page, (Item) tuples, tupsize, off,
     578                 :                                 false, false) == InvalidOffsetNumber)
     579 UBC           0 :                     elog(ERROR, "failed to add item to index page");
     580                 : 
     581 CBC       71257 :                 tuples = (IndexTuple) (((char *) tuples) + tupsize);
     582                 : 
     583           71257 :                 off++;
     584                 :             }
     585           23755 :             Assert(payload + totaltupsize == (char *) tuples);
     586                 : 
     587                 :             /*
     588                 :              * Increase counter of heap tuples
     589                 :              */
     590           23755 :             GinPageGetOpaque(page)->maxoff++;
     591                 : 
     592           23755 :             PageSetLSN(page, lsn);
     593           23755 :             MarkBufferDirty(buffer);
     594                 :         }
     595           23756 :         if (BufferIsValid(buffer))
     596           23756 :             UnlockReleaseBuffer(buffer);
     597                 :     }
     598             187 :     else if (data->prevTail != InvalidBlockNumber)
     599                 :     {
     600                 :         /*
     601                 :          * New tail
     602                 :          */
     603             174 :         if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
     604                 :         {
     605             174 :             Page        page = BufferGetPage(buffer);
     606                 : 
     607             174 :             GinPageGetOpaque(page)->rightlink = data->newRightlink;
     608                 : 
     609             174 :             PageSetLSN(page, lsn);
     610             174 :             MarkBufferDirty(buffer);
     611                 :         }
     612             174 :         if (BufferIsValid(buffer))
     613             174 :             UnlockReleaseBuffer(buffer);
     614                 :     }
     615                 : 
     616           23943 :     UnlockReleaseBuffer(metabuffer);
     617           23943 : }
     618                 : 
     619                 : static void
     620             179 : ginRedoInsertListPage(XLogReaderState *record)
     621                 : {
     622             179 :     XLogRecPtr  lsn = record->EndRecPtr;
     623             179 :     ginxlogInsertListPage *data = (ginxlogInsertListPage *) XLogRecGetData(record);
     624                 :     Buffer      buffer;
     625                 :     Page        page;
     626                 :     OffsetNumber l,
     627             179 :                 off = FirstOffsetNumber;
     628                 :     int         i,
     629                 :                 tupsize;
     630                 :     char       *payload;
     631                 :     IndexTuple  tuples;
     632                 :     Size        totaltupsize;
     633                 : 
     634                 :     /* We always re-initialize the page. */
     635             179 :     buffer = XLogInitBufferForRedo(record, 0);
     636             179 :     page = BufferGetPage(buffer);
     637                 : 
     638             179 :     GinInitBuffer(buffer, GIN_LIST);
     639             179 :     GinPageGetOpaque(page)->rightlink = data->rightlink;
     640             179 :     if (data->rightlink == InvalidBlockNumber)
     641                 :     {
     642                 :         /* tail of sublist */
     643             179 :         GinPageSetFullRow(page);
     644             179 :         GinPageGetOpaque(page)->maxoff = 1;
     645                 :     }
     646                 :     else
     647                 :     {
     648 UBC           0 :         GinPageGetOpaque(page)->maxoff = 0;
     649                 :     }
     650                 : 
     651 CBC         179 :     payload = XLogRecGetBlockData(record, 0, &totaltupsize);
     652                 : 
     653             179 :     tuples = (IndexTuple) payload;
     654             713 :     for (i = 0; i < data->ntuples; i++)
     655                 :     {
     656             534 :         tupsize = IndexTupleSize(tuples);
     657                 : 
     658             534 :         l = PageAddItem(page, (Item) tuples, tupsize, off, false, false);
     659                 : 
     660             534 :         if (l == InvalidOffsetNumber)
     661 UBC           0 :             elog(ERROR, "failed to add item to index page");
     662                 : 
     663 CBC         534 :         tuples = (IndexTuple) (((char *) tuples) + tupsize);
     664             534 :         off++;
     665                 :     }
     666             179 :     Assert((char *) tuples == payload + totaltupsize);
     667                 : 
     668             179 :     PageSetLSN(page, lsn);
     669             179 :     MarkBufferDirty(buffer);
     670                 : 
     671             179 :     UnlockReleaseBuffer(buffer);
     672             179 : }
     673                 : 
     674                 : static void
     675              13 : ginRedoDeleteListPages(XLogReaderState *record)
     676                 : {
     677              13 :     XLogRecPtr  lsn = record->EndRecPtr;
     678              13 :     ginxlogDeleteListPages *data = (ginxlogDeleteListPages *) XLogRecGetData(record);
     679                 :     Buffer      metabuffer;
     680                 :     Page        metapage;
     681                 :     int         i;
     682                 : 
     683              13 :     metabuffer = XLogInitBufferForRedo(record, 0);
     684              13 :     Assert(BufferGetBlockNumber(metabuffer) == GIN_METAPAGE_BLKNO);
     685              13 :     metapage = BufferGetPage(metabuffer);
     686                 : 
     687              13 :     GinInitMetabuffer(metabuffer);
     688                 : 
     689              13 :     memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
     690              13 :     PageSetLSN(metapage, lsn);
     691              13 :     MarkBufferDirty(metabuffer);
     692                 : 
     693                 :     /*
     694                 :      * In normal operation, shiftList() takes exclusive lock on all the
     695                 :      * pages-to-be-deleted simultaneously.  During replay, however, it should
     696                 :      * be all right to lock them one at a time.  This is dependent on the fact
     697                 :      * that we are deleting pages from the head of the list, and that readers
     698                 :      * share-lock the next page before releasing the one they are on. So we
     699                 :      * cannot get past a reader that is on, or due to visit, any page we are
     700                 :      * going to delete.  New incoming readers will block behind our metapage
     701                 :      * lock and then see a fully updated page list.
     702                 :      *
     703                 :      * No full-page images are taken of the deleted pages. Instead, they are
     704                 :      * re-initialized as empty, deleted pages. Their right-links don't need to
     705                 :      * be preserved, because no new readers can see the pages, as explained
     706                 :      * above.
     707                 :      */
     708             191 :     for (i = 0; i < data->ndeleted; i++)
     709                 :     {
     710                 :         Buffer      buffer;
     711                 :         Page        page;
     712                 : 
     713             178 :         buffer = XLogInitBufferForRedo(record, i + 1);
     714             178 :         page = BufferGetPage(buffer);
     715             178 :         GinInitBuffer(buffer, GIN_DELETED);
     716                 : 
     717             178 :         PageSetLSN(page, lsn);
     718             178 :         MarkBufferDirty(buffer);
     719                 : 
     720             178 :         UnlockReleaseBuffer(buffer);
     721                 :     }
     722              13 :     UnlockReleaseBuffer(metabuffer);
     723              13 : }
     724                 : 
     725                 : void
     726           51286 : gin_redo(XLogReaderState *record)
     727                 : {
     728           51286 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
     729                 :     MemoryContext oldCtx;
     730                 : 
     731                 :     /*
     732                 :      * GIN indexes do not require any conflict processing. NB: If we ever
     733                 :      * implement a similar optimization as we have in b-tree, and remove
     734                 :      * killed tuples outside VACUUM, we'll need to handle that here.
     735                 :      */
     736                 : 
     737           51286 :     oldCtx = MemoryContextSwitchTo(opCtx);
     738           51286 :     switch (info)
     739                 :     {
     740               3 :         case XLOG_GIN_CREATE_PTREE:
     741               3 :             ginRedoCreatePTree(record);
     742               3 :             break;
     743           27004 :         case XLOG_GIN_INSERT:
     744           27004 :             ginRedoInsert(record);
     745           27004 :             break;
     746             142 :         case XLOG_GIN_SPLIT:
     747             142 :             ginRedoSplit(record);
     748             142 :             break;
     749               2 :         case XLOG_GIN_VACUUM_PAGE:
     750               2 :             ginRedoVacuumPage(record);
     751               2 :             break;
     752 UBC           0 :         case XLOG_GIN_VACUUM_DATA_LEAF_PAGE:
     753               0 :             ginRedoVacuumDataLeafPage(record);
     754               0 :             break;
     755               0 :         case XLOG_GIN_DELETE_PAGE:
     756               0 :             ginRedoDeletePage(record);
     757               0 :             break;
     758 CBC       23943 :         case XLOG_GIN_UPDATE_META_PAGE:
     759           23943 :             ginRedoUpdateMetapage(record);
     760           23943 :             break;
     761             179 :         case XLOG_GIN_INSERT_LISTPAGE:
     762             179 :             ginRedoInsertListPage(record);
     763             179 :             break;
     764              13 :         case XLOG_GIN_DELETE_LISTPAGE:
     765              13 :             ginRedoDeleteListPages(record);
     766              13 :             break;
     767 UBC           0 :         default:
     768               0 :             elog(PANIC, "gin_redo: unknown op code %u", info);
     769                 :     }
     770 CBC       51286 :     MemoryContextSwitchTo(oldCtx);
     771           51286 :     MemoryContextReset(opCtx);
     772           51286 : }
     773                 : 
     774                 : void
     775             141 : gin_xlog_startup(void)
     776                 : {
     777             141 :     opCtx = AllocSetContextCreate(CurrentMemoryContext,
     778                 :                                   "GIN recovery temporary context",
     779                 :                                   ALLOCSET_DEFAULT_SIZES);
     780             141 : }
     781                 : 
     782                 : void
     783             108 : gin_xlog_cleanup(void)
     784                 : {
     785             108 :     MemoryContextDelete(opCtx);
     786             108 :     opCtx = NULL;
     787             108 : }
     788                 : 
     789                 : /*
     790                 :  * Mask a GIN page before running consistency checks on it.
     791                 :  */
     792                 : void
     793 UBC           0 : gin_mask(char *pagedata, BlockNumber blkno)
     794                 : {
     795               0 :     Page        page = (Page) pagedata;
     796               0 :     PageHeader  pagehdr = (PageHeader) page;
     797                 :     GinPageOpaque opaque;
     798                 : 
     799               0 :     mask_page_lsn_and_checksum(page);
     800               0 :     opaque = GinPageGetOpaque(page);
     801                 : 
     802               0 :     mask_page_hint_bits(page);
     803                 : 
     804                 :     /*
     805                 :      * For a GIN_DELETED page, the page is initialized to empty.  Hence, mask
     806                 :      * the whole page content.  For other pages, mask the hole if pd_lower
     807                 :      * appears to have been set correctly.
     808                 :      */
     809               0 :     if (opaque->flags & GIN_DELETED)
     810               0 :         mask_page_content(page);
     811               0 :     else if (pagehdr->pd_lower > SizeOfPageHeaderData)
     812               0 :         mask_unused_space(page);
     813               0 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a