1 /*-------------------------------------------------------------------------
4 * WAL replay logic for inverted index.
7 * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/access/gin/ginxlog.c
12 *-------------------------------------------------------------------------
16 #include "access/gin_private.h"
17 #include "access/xlogutils.h"
18 #include "utils/memutils.h"
20 static MemoryContext opCtx; /* working memory for operations */
23 ginRedoClearIncompleteSplit(XLogRecPtr lsn, RelFileNode node, BlockNumber blkno)
28 buffer = XLogReadBuffer(node, blkno, false);
29 if (!BufferIsValid(buffer))
30 return; /* page was deleted, nothing to do */
31 page = (Page) BufferGetPage(buffer);
33 if (lsn > PageGetLSN(page))
35 GinPageGetOpaque(page)->flags &= ~GIN_INCOMPLETE_SPLIT;
37 PageSetLSN(page, lsn);
38 MarkBufferDirty(buffer);
41 UnlockReleaseBuffer(buffer);
45 ginRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record)
47 RelFileNode *node = (RelFileNode *) XLogRecGetData(record);
52 /* Backup blocks are not used in create_index records */
53 Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
55 MetaBuffer = XLogReadBuffer(*node, GIN_METAPAGE_BLKNO, true);
56 Assert(BufferIsValid(MetaBuffer));
57 page = (Page) BufferGetPage(MetaBuffer);
59 GinInitMetabuffer(MetaBuffer);
61 PageSetLSN(page, lsn);
62 MarkBufferDirty(MetaBuffer);
64 RootBuffer = XLogReadBuffer(*node, GIN_ROOT_BLKNO, true);
65 Assert(BufferIsValid(RootBuffer));
66 page = (Page) BufferGetPage(RootBuffer);
68 GinInitBuffer(RootBuffer, GIN_LEAF);
70 PageSetLSN(page, lsn);
71 MarkBufferDirty(RootBuffer);
73 UnlockReleaseBuffer(RootBuffer);
74 UnlockReleaseBuffer(MetaBuffer);
78 ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record)
80 ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree *) XLogRecGetData(record);
85 /* Backup blocks are not used in create_ptree records */
86 Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
88 buffer = XLogReadBuffer(data->node, data->blkno, true);
89 Assert(BufferIsValid(buffer));
90 page = (Page) BufferGetPage(buffer);
92 GinInitBuffer(buffer, GIN_DATA | GIN_LEAF | GIN_COMPRESSED);
94 ptr = XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree);
97 memcpy(GinDataLeafPageGetPostingList(page), ptr, data->size);
99 GinDataLeafPageSetPostingListSize(page, data->size);
101 PageSetLSN(page, lsn);
103 MarkBufferDirty(buffer);
104 UnlockReleaseBuffer(buffer);
108 ginRedoInsertEntry(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rdata)
110 Page page = BufferGetPage(buffer);
111 ginxlogInsertEntry *data = (ginxlogInsertEntry *) rdata;
112 OffsetNumber offset = data->offset;
115 if (rightblkno != InvalidBlockNumber)
117 /* update link to right page after split */
118 Assert(!GinPageIsLeaf(page));
119 Assert(offset >= FirstOffsetNumber && offset <= PageGetMaxOffsetNumber(page));
120 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offset));
121 GinSetDownlink(itup, rightblkno);
126 Assert(GinPageIsLeaf(page));
127 Assert(offset >= FirstOffsetNumber && offset <= PageGetMaxOffsetNumber(page));
128 PageIndexTupleDelete(page, offset);
133 if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), offset, false, false) == InvalidOffsetNumber)
139 BufferGetTag(buffer, &node, &forknum, &blknum);
140 elog(ERROR, "failed to add item to index page in %u/%u/%u",
141 node.spcNode, node.dbNode, node.relNode);
146 ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
150 GinPostingList *oldseg;
156 * If the page is in pre-9.4 format, convert to new format first.
158 if (!GinPageIsCompressed(page))
160 ItemPointer uncompressed = (ItemPointer) GinDataPageGetData(page);
161 int nuncompressed = GinPageGetOpaque(page)->maxoff;
163 GinPostingList *plist;
165 plist = ginCompressPostingList(uncompressed, nuncompressed,
167 Assert(npacked == nuncompressed);
169 totalsize = SizeOfGinPostingList(plist);
171 memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize);
172 GinDataLeafPageSetPostingListSize(page, totalsize);
173 GinPageSetCompressed(page);
174 GinPageGetOpaque(page)->maxoff = InvalidOffsetNumber;
177 oldseg = GinDataLeafPageGetPostingList(page);
178 segmentend = (Pointer) oldseg + GinDataLeafPageGetPostingListSize(page);
181 walbuf = ((char *) data) + sizeof(ginxlogRecompressDataLeaf);
182 for (actionno = 0; actionno < data->nactions; actionno++)
184 uint8 a_segno = *((uint8 *) (walbuf++));
185 uint8 a_action = *((uint8 *) (walbuf++));
186 GinPostingList *newseg = NULL;
188 ItemPointerData *items = NULL;
190 ItemPointerData *olditems;
192 ItemPointerData *newitems;
198 /* Extract all the information we need from the WAL record */
199 if (a_action == GIN_SEGMENT_INSERT ||
200 a_action == GIN_SEGMENT_REPLACE)
202 newseg = (GinPostingList *) walbuf;
203 newsegsize = SizeOfGinPostingList(newseg);
204 walbuf += SHORTALIGN(newsegsize);
207 if (a_action == GIN_SEGMENT_ADDITEMS)
209 memcpy(&nitems, walbuf, sizeof(uint16));
210 walbuf += sizeof(uint16);
211 items = (ItemPointerData *) walbuf;
212 walbuf += nitems * sizeof(ItemPointerData);
215 /* Skip to the segment that this action concerns */
216 Assert(segno <= a_segno);
217 while (segno < a_segno)
219 oldseg = GinNextPostingListSegment(oldseg);
224 * ADDITEMS action is handled like REPLACE, but the new segment to
225 * replace the old one is reconstructed using the old segment from
226 * disk and the new items from the WAL record.
228 if (a_action == GIN_SEGMENT_ADDITEMS)
232 olditems = ginPostingListDecode(oldseg, &nolditems);
234 newitems = ginMergeItemPointers(items, nitems,
237 Assert(nnewitems == nolditems + nitems);
239 newseg = ginCompressPostingList(newitems, nnewitems,
241 Assert(npacked == nnewitems);
243 newsegsize = SizeOfGinPostingList(newseg);
244 a_action = GIN_SEGMENT_REPLACE;
247 segptr = (Pointer) oldseg;
248 if (segptr != segmentend)
249 segsize = SizeOfGinPostingList(oldseg);
253 * Positioned after the last existing segment. Only INSERTs
256 Assert(a_action == GIN_SEGMENT_INSERT);
259 szleft = segmentend - segptr;
263 case GIN_SEGMENT_DELETE:
264 memmove(segptr, segptr + segsize, szleft - segsize);
265 segmentend -= segsize;
270 case GIN_SEGMENT_INSERT:
271 /* make room for the new segment */
272 memmove(segptr + newsegsize, segptr, szleft);
273 /* copy the new segment in place */
274 memcpy(segptr, newseg, newsegsize);
275 segmentend += newsegsize;
276 segptr += newsegsize;
279 case GIN_SEGMENT_REPLACE:
280 /* shift the segments that follow */
281 memmove(segptr + newsegsize,
284 /* copy the replacement segment in place */
285 memcpy(segptr, newseg, newsegsize);
286 segmentend -= segsize;
287 segmentend += newsegsize;
288 segptr += newsegsize;
293 elog(ERROR, "unexpected GIN leaf action: %u", a_action);
295 oldseg = (GinPostingList *) segptr;
298 totalsize = segmentend - (Pointer) GinDataLeafPageGetPostingList(page);
299 GinDataLeafPageSetPostingListSize(page, totalsize);
303 ginRedoInsertData(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rdata)
305 Page page = BufferGetPage(buffer);
309 ginxlogRecompressDataLeaf *data = (ginxlogRecompressDataLeaf *) rdata;
311 Assert(GinPageIsLeaf(page));
313 ginRedoRecompress(page, data);
317 ginxlogInsertDataInternal *data = (ginxlogInsertDataInternal *) rdata;
318 PostingItem *oldpitem;
320 Assert(!GinPageIsLeaf(page));
322 /* update link to right page after split */
323 oldpitem = GinDataPageGetPostingItem(page, data->offset);
324 PostingItemSetBlockNumber(oldpitem, rightblkno);
326 GinDataPageAddPostingItem(page, &data->newitem, data->offset);
331 ginRedoInsert(XLogRecPtr lsn, XLogRecord *record)
333 ginxlogInsert *data = (ginxlogInsert *) XLogRecGetData(record);
337 BlockNumber leftChildBlkno = InvalidBlockNumber;
338 BlockNumber rightChildBlkno = InvalidBlockNumber;
339 bool isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0;
341 payload = XLogRecGetData(record) + sizeof(ginxlogInsert);
344 * First clear incomplete-split flag on child page if this finishes
349 leftChildBlkno = BlockIdGetBlockNumber((BlockId) payload);
350 payload += sizeof(BlockIdData);
351 rightChildBlkno = BlockIdGetBlockNumber((BlockId) payload);
352 payload += sizeof(BlockIdData);
354 if (record->xl_info & XLR_BKP_BLOCK(0))
355 (void) RestoreBackupBlock(lsn, record, 0, false, false);
357 ginRedoClearIncompleteSplit(lsn, data->node, leftChildBlkno);
360 /* If we have a full-page image, restore it and we're done */
361 if (record->xl_info & XLR_BKP_BLOCK(isLeaf ? 0 : 1))
363 (void) RestoreBackupBlock(lsn, record, isLeaf ? 0 : 1, false, false);
367 buffer = XLogReadBuffer(data->node, data->blkno, false);
368 if (!BufferIsValid(buffer))
369 return; /* page was deleted, nothing to do */
370 page = (Page) BufferGetPage(buffer);
372 if (lsn > PageGetLSN(page))
374 /* How to insert the payload is tree-type specific */
375 if (data->flags & GIN_INSERT_ISDATA)
377 Assert(GinPageIsData(page));
378 ginRedoInsertData(buffer, isLeaf, rightChildBlkno, payload);
382 Assert(!GinPageIsData(page));
383 ginRedoInsertEntry(buffer, isLeaf, rightChildBlkno, payload);
386 PageSetLSN(page, lsn);
387 MarkBufferDirty(buffer);
390 UnlockReleaseBuffer(buffer);
394 ginRedoSplitEntry(Page lpage, Page rpage, void *rdata)
396 ginxlogSplitEntry *data = (ginxlogSplitEntry *) rdata;
397 IndexTuple itup = (IndexTuple) ((char *) rdata + sizeof(ginxlogSplitEntry));
400 for (i = 0; i < data->separator; i++)
402 if (PageAddItem(lpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
403 elog(ERROR, "failed to add item to gin index page");
404 itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
407 for (i = data->separator; i < data->nitem; i++)
409 if (PageAddItem(rpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
410 elog(ERROR, "failed to add item to gin index page");
411 itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
416 ginRedoSplitData(Page lpage, Page rpage, void *rdata)
418 bool isleaf = GinPageIsLeaf(lpage);
422 ginxlogSplitDataLeaf *data = (ginxlogSplitDataLeaf *) rdata;
423 Pointer lptr = (Pointer) rdata + sizeof(ginxlogSplitDataLeaf);
424 Pointer rptr = lptr + data->lsize;
426 Assert(data->lsize > 0 && data->lsize <= GinDataLeafMaxContentSize);
427 Assert(data->rsize > 0 && data->rsize <= GinDataLeafMaxContentSize);
429 memcpy(GinDataLeafPageGetPostingList(lpage), lptr, data->lsize);
430 memcpy(GinDataLeafPageGetPostingList(rpage), rptr, data->rsize);
432 GinDataLeafPageSetPostingListSize(lpage, data->lsize);
433 GinDataLeafPageSetPostingListSize(rpage, data->rsize);
434 *GinDataPageGetRightBound(lpage) = data->lrightbound;
435 *GinDataPageGetRightBound(rpage) = data->rrightbound;
439 ginxlogSplitDataInternal *data = (ginxlogSplitDataInternal *) rdata;
440 PostingItem *items = (PostingItem *) ((char *) rdata + sizeof(ginxlogSplitDataInternal));
444 for (i = 0; i < data->separator; i++)
445 GinDataPageAddPostingItem(lpage, &items[i], InvalidOffsetNumber);
446 for (i = data->separator; i < data->nitem; i++)
447 GinDataPageAddPostingItem(rpage, &items[i], InvalidOffsetNumber);
449 /* set up right key */
450 maxoff = GinPageGetOpaque(lpage)->maxoff;
451 *GinDataPageGetRightBound(lpage) = GinDataPageGetPostingItem(lpage, maxoff)->key;
452 *GinDataPageGetRightBound(rpage) = data->rightbound;
457 ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
459 ginxlogSplit *data = (ginxlogSplit *) XLogRecGetData(record);
466 bool isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0;
467 bool isData = (data->flags & GIN_INSERT_ISDATA) != 0;
468 bool isRoot = (data->flags & GIN_SPLIT_ROOT) != 0;
470 payload = XLogRecGetData(record) + sizeof(ginxlogSplit);
473 * First clear incomplete-split flag on child page if this finishes
478 if (record->xl_info & XLR_BKP_BLOCK(0))
479 (void) RestoreBackupBlock(lsn, record, 0, false, false);
481 ginRedoClearIncompleteSplit(lsn, data->node, data->leftChildBlkno);
488 if (isLeaf && isData)
489 flags |= GIN_COMPRESSED;
491 lbuffer = XLogReadBuffer(data->node, data->lblkno, true);
492 Assert(BufferIsValid(lbuffer));
493 lpage = (Page) BufferGetPage(lbuffer);
494 GinInitBuffer(lbuffer, flags);
496 rbuffer = XLogReadBuffer(data->node, data->rblkno, true);
497 Assert(BufferIsValid(rbuffer));
498 rpage = (Page) BufferGetPage(rbuffer);
499 GinInitBuffer(rbuffer, flags);
501 GinPageGetOpaque(lpage)->rightlink = BufferGetBlockNumber(rbuffer);
502 GinPageGetOpaque(rpage)->rightlink = isRoot ? InvalidBlockNumber : data->rrlink;
504 /* Do the tree-type specific portion to restore the page contents */
506 ginRedoSplitData(lpage, rpage, payload);
508 ginRedoSplitEntry(lpage, rpage, payload);
510 PageSetLSN(rpage, lsn);
511 MarkBufferDirty(rbuffer);
513 PageSetLSN(lpage, lsn);
514 MarkBufferDirty(lbuffer);
518 BlockNumber rootBlkno = data->rrlink;
519 Buffer rootBuf = XLogReadBuffer(data->node, rootBlkno, true);
520 Page rootPage = BufferGetPage(rootBuf);
522 GinInitBuffer(rootBuf, flags & ~GIN_LEAF & ~GIN_COMPRESSED);
526 Assert(rootBlkno != GIN_ROOT_BLKNO);
527 ginDataFillRoot(NULL, BufferGetPage(rootBuf),
528 BufferGetBlockNumber(lbuffer),
529 BufferGetPage(lbuffer),
530 BufferGetBlockNumber(rbuffer),
531 BufferGetPage(rbuffer));
535 Assert(rootBlkno == GIN_ROOT_BLKNO);
536 ginEntryFillRoot(NULL, BufferGetPage(rootBuf),
537 BufferGetBlockNumber(lbuffer),
538 BufferGetPage(lbuffer),
539 BufferGetBlockNumber(rbuffer),
540 BufferGetPage(rbuffer));
543 PageSetLSN(rootPage, lsn);
545 MarkBufferDirty(rootBuf);
546 UnlockReleaseBuffer(rootBuf);
549 UnlockReleaseBuffer(rbuffer);
550 UnlockReleaseBuffer(lbuffer);
554 * This is functionally the same as heap_xlog_newpage.
557 ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record)
559 ginxlogVacuumPage *xlrec = (ginxlogVacuumPage *) XLogRecGetData(record);
560 char *blk = ((char *) xlrec) + sizeof(ginxlogVacuumPage);
564 Assert(xlrec->hole_offset < BLCKSZ);
565 Assert(xlrec->hole_length < BLCKSZ);
567 /* If we have a full-page image, restore it and we're done */
568 if (record->xl_info & XLR_BKP_BLOCK(0))
570 (void) RestoreBackupBlock(lsn, record, 0, false, false);
574 buffer = XLogReadBuffer(xlrec->node, xlrec->blkno, true);
575 if (!BufferIsValid(buffer))
577 page = (Page) BufferGetPage(buffer);
579 if (xlrec->hole_length == 0)
581 memcpy((char *) page, blk, BLCKSZ);
585 memcpy((char *) page, blk, xlrec->hole_offset);
586 /* must zero-fill the hole */
587 MemSet((char *) page + xlrec->hole_offset, 0, xlrec->hole_length);
588 memcpy((char *) page + (xlrec->hole_offset + xlrec->hole_length),
589 blk + xlrec->hole_offset,
590 BLCKSZ - (xlrec->hole_offset + xlrec->hole_length));
593 PageSetLSN(page, lsn);
595 MarkBufferDirty(buffer);
596 UnlockReleaseBuffer(buffer);
600 ginRedoVacuumDataLeafPage(XLogRecPtr lsn, XLogRecord *record)
602 ginxlogVacuumDataLeafPage *xlrec = (ginxlogVacuumDataLeafPage *) XLogRecGetData(record);
606 /* If we have a full-page image, restore it and we're done */
607 if (record->xl_info & XLR_BKP_BLOCK(0))
609 (void) RestoreBackupBlock(lsn, record, 0, false, false);
613 buffer = XLogReadBuffer(xlrec->node, xlrec->blkno, false);
614 if (!BufferIsValid(buffer))
616 page = (Page) BufferGetPage(buffer);
618 Assert(GinPageIsLeaf(page));
619 Assert(GinPageIsData(page));
621 if (lsn > PageGetLSN(page))
623 ginRedoRecompress(page, &xlrec->data);
624 PageSetLSN(page, lsn);
625 MarkBufferDirty(buffer);
628 UnlockReleaseBuffer(buffer);
632 ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record)
634 ginxlogDeletePage *data = (ginxlogDeletePage *) XLogRecGetData(record);
640 if (record->xl_info & XLR_BKP_BLOCK(0))
641 dbuffer = RestoreBackupBlock(lsn, record, 0, false, true);
644 dbuffer = XLogReadBuffer(data->node, data->blkno, false);
645 if (BufferIsValid(dbuffer))
647 page = BufferGetPage(dbuffer);
648 if (lsn > PageGetLSN(page))
650 Assert(GinPageIsData(page));
651 GinPageGetOpaque(page)->flags = GIN_DELETED;
652 PageSetLSN(page, lsn);
653 MarkBufferDirty(dbuffer);
658 if (record->xl_info & XLR_BKP_BLOCK(1))
659 pbuffer = RestoreBackupBlock(lsn, record, 1, false, true);
662 pbuffer = XLogReadBuffer(data->node, data->parentBlkno, false);
663 if (BufferIsValid(pbuffer))
665 page = BufferGetPage(pbuffer);
666 if (lsn > PageGetLSN(page))
668 Assert(GinPageIsData(page));
669 Assert(!GinPageIsLeaf(page));
670 GinPageDeletePostingItem(page, data->parentOffset);
671 PageSetLSN(page, lsn);
672 MarkBufferDirty(pbuffer);
677 if (record->xl_info & XLR_BKP_BLOCK(2))
678 (void) RestoreBackupBlock(lsn, record, 2, false, false);
679 else if (data->leftBlkno != InvalidBlockNumber)
681 lbuffer = XLogReadBuffer(data->node, data->leftBlkno, false);
682 if (BufferIsValid(lbuffer))
684 page = BufferGetPage(lbuffer);
685 if (lsn > PageGetLSN(page))
687 Assert(GinPageIsData(page));
688 GinPageGetOpaque(page)->rightlink = data->rightLink;
689 PageSetLSN(page, lsn);
690 MarkBufferDirty(lbuffer);
692 UnlockReleaseBuffer(lbuffer);
696 if (BufferIsValid(pbuffer))
697 UnlockReleaseBuffer(pbuffer);
698 if (BufferIsValid(dbuffer))
699 UnlockReleaseBuffer(dbuffer);
703 ginRedoUpdateMetapage(XLogRecPtr lsn, XLogRecord *record)
705 ginxlogUpdateMeta *data = (ginxlogUpdateMeta *) XLogRecGetData(record);
711 * Restore the metapage. This is essentially the same as a full-page image,
712 * so restore the metapage unconditionally without looking at the LSN, to
713 * avoid torn page hazards.
715 metabuffer = XLogReadBuffer(data->node, GIN_METAPAGE_BLKNO, false);
716 if (!BufferIsValid(metabuffer))
717 return; /* assume index was deleted, nothing to do */
718 metapage = BufferGetPage(metabuffer);
720 memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
721 PageSetLSN(metapage, lsn);
722 MarkBufferDirty(metabuffer);
724 if (data->ntuples > 0)
727 * insert into tail page
729 if (record->xl_info & XLR_BKP_BLOCK(0))
730 (void) RestoreBackupBlock(lsn, record, 0, false, false);
733 buffer = XLogReadBuffer(data->node, data->metadata.tail, false);
734 if (BufferIsValid(buffer))
736 Page page = BufferGetPage(buffer);
738 if (lsn > PageGetLSN(page))
741 off = (PageIsEmpty(page)) ? FirstOffsetNumber :
742 OffsetNumberNext(PageGetMaxOffsetNumber(page));
745 IndexTuple tuples = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogUpdateMeta));
747 for (i = 0; i < data->ntuples; i++)
749 tupsize = IndexTupleSize(tuples);
751 l = PageAddItem(page, (Item) tuples, tupsize, off, false, false);
753 if (l == InvalidOffsetNumber)
754 elog(ERROR, "failed to add item to index page");
756 tuples = (IndexTuple) (((char *) tuples) + tupsize);
762 * Increase counter of heap tuples
764 GinPageGetOpaque(page)->maxoff++;
766 PageSetLSN(page, lsn);
767 MarkBufferDirty(buffer);
769 UnlockReleaseBuffer(buffer);
773 else if (data->prevTail != InvalidBlockNumber)
778 if (record->xl_info & XLR_BKP_BLOCK(0))
779 (void) RestoreBackupBlock(lsn, record, 0, false, false);
782 buffer = XLogReadBuffer(data->node, data->prevTail, false);
783 if (BufferIsValid(buffer))
785 Page page = BufferGetPage(buffer);
787 if (lsn > PageGetLSN(page))
789 GinPageGetOpaque(page)->rightlink = data->newRightlink;
791 PageSetLSN(page, lsn);
792 MarkBufferDirty(buffer);
794 UnlockReleaseBuffer(buffer);
799 UnlockReleaseBuffer(metabuffer);
803 ginRedoInsertListPage(XLogRecPtr lsn, XLogRecord *record)
805 ginxlogInsertListPage *data = (ginxlogInsertListPage *) XLogRecGetData(record);
809 off = FirstOffsetNumber;
812 IndexTuple tuples = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsertListPage));
814 /* If we have a full-page image, restore it and we're done */
815 if (record->xl_info & XLR_BKP_BLOCK(0))
817 (void) RestoreBackupBlock(lsn, record, 0, false, false);
821 buffer = XLogReadBuffer(data->node, data->blkno, true);
822 Assert(BufferIsValid(buffer));
823 page = BufferGetPage(buffer);
825 GinInitBuffer(buffer, GIN_LIST);
826 GinPageGetOpaque(page)->rightlink = data->rightlink;
827 if (data->rightlink == InvalidBlockNumber)
829 /* tail of sublist */
830 GinPageSetFullRow(page);
831 GinPageGetOpaque(page)->maxoff = 1;
835 GinPageGetOpaque(page)->maxoff = 0;
838 for (i = 0; i < data->ntuples; i++)
840 tupsize = IndexTupleSize(tuples);
842 l = PageAddItem(page, (Item) tuples, tupsize, off, false, false);
844 if (l == InvalidOffsetNumber)
845 elog(ERROR, "failed to add item to index page");
847 tuples = (IndexTuple) (((char *) tuples) + tupsize);
850 PageSetLSN(page, lsn);
851 MarkBufferDirty(buffer);
853 UnlockReleaseBuffer(buffer);
857 ginRedoDeleteListPages(XLogRecPtr lsn, XLogRecord *record)
859 ginxlogDeleteListPages *data = (ginxlogDeleteListPages *) XLogRecGetData(record);
864 /* Backup blocks are not used in delete_listpage records */
865 Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
867 metabuffer = XLogReadBuffer(data->node, GIN_METAPAGE_BLKNO, false);
868 if (!BufferIsValid(metabuffer))
869 return; /* assume index was deleted, nothing to do */
870 metapage = BufferGetPage(metabuffer);
872 memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
873 PageSetLSN(metapage, lsn);
874 MarkBufferDirty(metabuffer);
877 * In normal operation, shiftList() takes exclusive lock on all the
878 * pages-to-be-deleted simultaneously. During replay, however, it should
879 * be all right to lock them one at a time. This is dependent on the fact
880 * that we are deleting pages from the head of the list, and that readers
881 * share-lock the next page before releasing the one they are on. So we
882 * cannot get past a reader that is on, or due to visit, any page we are
883 * going to delete. New incoming readers will block behind our metapage
884 * lock and then see a fully updated page list.
886 for (i = 0; i < data->ndeleted; i++)
888 Buffer buffer = XLogReadBuffer(data->node, data->toDelete[i], false);
890 if (BufferIsValid(buffer))
892 Page page = BufferGetPage(buffer);
894 if (lsn > PageGetLSN(page))
896 GinPageGetOpaque(page)->flags = GIN_DELETED;
898 PageSetLSN(page, lsn);
899 MarkBufferDirty(buffer);
902 UnlockReleaseBuffer(buffer);
905 UnlockReleaseBuffer(metabuffer);
909 gin_redo(XLogRecPtr lsn, XLogRecord *record)
911 uint8 info = record->xl_info & ~XLR_INFO_MASK;
912 MemoryContext oldCtx;
915 * GIN indexes do not require any conflict processing. NB: If we ever
916 * implement a similar optimization as we have in b-tree, and remove
917 * killed tuples outside VACUUM, we'll need to handle that here.
920 oldCtx = MemoryContextSwitchTo(opCtx);
923 case XLOG_GIN_CREATE_INDEX:
924 ginRedoCreateIndex(lsn, record);
926 case XLOG_GIN_CREATE_PTREE:
927 ginRedoCreatePTree(lsn, record);
929 case XLOG_GIN_INSERT:
930 ginRedoInsert(lsn, record);
933 ginRedoSplit(lsn, record);
935 case XLOG_GIN_VACUUM_PAGE:
936 ginRedoVacuumPage(lsn, record);
938 case XLOG_GIN_VACUUM_DATA_LEAF_PAGE:
939 ginRedoVacuumDataLeafPage(lsn, record);
941 case XLOG_GIN_DELETE_PAGE:
942 ginRedoDeletePage(lsn, record);
944 case XLOG_GIN_UPDATE_META_PAGE:
945 ginRedoUpdateMetapage(lsn, record);
947 case XLOG_GIN_INSERT_LISTPAGE:
948 ginRedoInsertListPage(lsn, record);
950 case XLOG_GIN_DELETE_LISTPAGE:
951 ginRedoDeleteListPages(lsn, record);
954 elog(PANIC, "gin_redo: unknown op code %u", info);
956 MemoryContextSwitchTo(oldCtx);
957 MemoryContextReset(opCtx);
961 gin_xlog_startup(void)
963 opCtx = AllocSetContextCreate(CurrentMemoryContext,
964 "GIN recovery temporary context",
965 ALLOCSET_DEFAULT_MINSIZE,
966 ALLOCSET_DEFAULT_INITSIZE,
967 ALLOCSET_DEFAULT_MAXSIZE);
971 gin_xlog_cleanup(void)
973 MemoryContextDelete(opCtx);