1 /*-------------------------------------------------------------------------
4 * WAL replay logic for inverted index.
7 * Portions Copyright (c) 1996-2010, 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.h"
17 #include "access/xlogutils.h"
18 #include "storage/bufmgr.h"
19 #include "utils/memutils.h"
21 static MemoryContext opCtx; /* working memory for operations */
22 static MemoryContext topCtx;
24 typedef struct ginIncompleteSplit
27 BlockNumber leftBlkno;
28 BlockNumber rightBlkno;
29 BlockNumber rootBlkno;
32 static List *incomplete_splits;
35 pushIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber rightBlkno, BlockNumber rootBlkno)
37 ginIncompleteSplit *split;
39 MemoryContextSwitchTo(topCtx);
41 split = palloc(sizeof(ginIncompleteSplit));
44 split->leftBlkno = leftBlkno;
45 split->rightBlkno = rightBlkno;
46 split->rootBlkno = rootBlkno;
48 incomplete_splits = lappend(incomplete_splits, split);
50 MemoryContextSwitchTo(opCtx);
54 forgetIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber updateBlkno)
58 foreach(l, incomplete_splits)
60 ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
62 if (RelFileNodeEquals(node, split->node) && leftBlkno == split->leftBlkno && updateBlkno == split->rightBlkno)
64 incomplete_splits = list_delete_ptr(incomplete_splits, split);
71 ginRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record)
73 RelFileNode *node = (RelFileNode *) XLogRecGetData(record);
78 MetaBuffer = XLogReadBuffer(*node, GIN_METAPAGE_BLKNO, true);
79 Assert(BufferIsValid(MetaBuffer));
80 page = (Page) BufferGetPage(MetaBuffer);
82 GinInitMetabuffer(MetaBuffer);
84 PageSetLSN(page, lsn);
85 PageSetTLI(page, ThisTimeLineID);
86 MarkBufferDirty(MetaBuffer);
88 RootBuffer = XLogReadBuffer(*node, GIN_ROOT_BLKNO, true);
89 Assert(BufferIsValid(RootBuffer));
90 page = (Page) BufferGetPage(RootBuffer);
92 GinInitBuffer(RootBuffer, GIN_LEAF);
94 PageSetLSN(page, lsn);
95 PageSetTLI(page, ThisTimeLineID);
96 MarkBufferDirty(RootBuffer);
98 UnlockReleaseBuffer(RootBuffer);
99 UnlockReleaseBuffer(MetaBuffer);
103 ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record)
105 ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree *) XLogRecGetData(record);
106 ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree));
110 buffer = XLogReadBuffer(data->node, data->blkno, true);
111 Assert(BufferIsValid(buffer));
112 page = (Page) BufferGetPage(buffer);
114 GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
115 memcpy(GinDataPageGetData(page), items, sizeof(ItemPointerData) * data->nitem);
116 GinPageGetOpaque(page)->maxoff = data->nitem;
118 PageSetLSN(page, lsn);
119 PageSetTLI(page, ThisTimeLineID);
121 MarkBufferDirty(buffer);
122 UnlockReleaseBuffer(buffer);
126 ginRedoInsert(XLogRecPtr lsn, XLogRecord *record)
128 ginxlogInsert *data = (ginxlogInsert *) XLogRecGetData(record);
132 /* first, forget any incomplete split this insertion completes */
135 Assert(data->isDelete == FALSE);
136 if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
140 pitem = (PostingItem *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
141 forgetIncompleteSplit(data->node,
142 PostingItemGetBlockNumber(pitem),
149 if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
153 itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
154 forgetIncompleteSplit(data->node,
155 GinItemPointerGetBlockNumber(&itup->t_tid),
160 /* nothing else to do if page was backed up */
161 if (record->xl_info & XLR_BKP_BLOCK_1)
164 buffer = XLogReadBuffer(data->node, data->blkno, false);
165 if (!BufferIsValid(buffer))
166 return; /* page was deleted, nothing to do */
167 page = (Page) BufferGetPage(buffer);
169 if (!XLByteLE(lsn, PageGetLSN(page)))
173 Assert(GinPageIsData(page));
178 ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
180 Assert(GinPageIsLeaf(page));
181 Assert(data->updateBlkno == InvalidBlockNumber);
183 for (i = 0; i < data->nitem; i++)
184 GinDataPageAddItem(page, items + i, data->offset + i);
190 Assert(!GinPageIsLeaf(page));
192 if (data->updateBlkno != InvalidBlockNumber)
194 /* update link to right page after split */
195 pitem = (PostingItem *) GinDataPageGetItem(page, data->offset);
196 PostingItemSetBlockNumber(pitem, data->updateBlkno);
199 pitem = (PostingItem *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
201 GinDataPageAddItem(page, pitem, data->offset);
208 Assert(!GinPageIsData(page));
210 if (data->updateBlkno != InvalidBlockNumber)
212 /* update link to right page after split */
213 Assert(!GinPageIsLeaf(page));
214 Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
215 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, data->offset));
216 ItemPointerSet(&itup->t_tid, data->updateBlkno, InvalidOffsetNumber);
221 Assert(GinPageIsLeaf(page));
222 Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
223 PageIndexTupleDelete(page, data->offset);
226 itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
228 if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), data->offset, false, false) == InvalidOffsetNumber)
229 elog(ERROR, "failed to add item to index page in %u/%u/%u",
230 data->node.spcNode, data->node.dbNode, data->node.relNode);
233 PageSetLSN(page, lsn);
234 PageSetTLI(page, ThisTimeLineID);
236 MarkBufferDirty(buffer);
239 UnlockReleaseBuffer(buffer);
243 ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
245 ginxlogSplit *data = (ginxlogSplit *) XLogRecGetData(record);
257 lbuffer = XLogReadBuffer(data->node, data->lblkno, true);
258 Assert(BufferIsValid(lbuffer));
259 lpage = (Page) BufferGetPage(lbuffer);
260 GinInitBuffer(lbuffer, flags);
262 rbuffer = XLogReadBuffer(data->node, data->rblkno, true);
263 Assert(BufferIsValid(rbuffer));
264 rpage = (Page) BufferGetPage(rbuffer);
265 GinInitBuffer(rbuffer, flags);
267 GinPageGetOpaque(lpage)->rightlink = BufferGetBlockNumber(rbuffer);
268 GinPageGetOpaque(rpage)->rightlink = data->rrlink;
272 char *ptr = XLogRecGetData(record) + sizeof(ginxlogSplit);
273 Size sizeofitem = GinSizeOfItem(lpage);
277 for (i = 0; i < data->separator; i++)
279 GinDataPageAddItem(lpage, ptr, InvalidOffsetNumber);
283 for (i = data->separator; i < data->nitem; i++)
285 GinDataPageAddItem(rpage, ptr, InvalidOffsetNumber);
289 /* set up right key */
290 bound = GinDataPageGetRightBound(lpage);
292 *bound = *(ItemPointerData *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff);
294 *bound = ((PostingItem *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff))->key;
296 bound = GinDataPageGetRightBound(rpage);
297 *bound = data->rightbound;
301 IndexTuple itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogSplit));
304 for (i = 0; i < data->separator; i++)
306 if (PageAddItem(lpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
307 elog(ERROR, "failed to add item to index page in %u/%u/%u",
308 data->node.spcNode, data->node.dbNode, data->node.relNode);
309 itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
312 for (i = data->separator; i < data->nitem; i++)
314 if (PageAddItem(rpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
315 elog(ERROR, "failed to add item to index page in %u/%u/%u",
316 data->node.spcNode, data->node.dbNode, data->node.relNode);
317 itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
321 PageSetLSN(rpage, lsn);
322 PageSetTLI(rpage, ThisTimeLineID);
323 MarkBufferDirty(rbuffer);
325 PageSetLSN(lpage, lsn);
326 PageSetTLI(lpage, ThisTimeLineID);
327 MarkBufferDirty(lbuffer);
329 if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
330 forgetIncompleteSplit(data->node, data->leftChildBlkno, data->updateBlkno);
332 if (data->isRootSplit)
334 Buffer rootBuf = XLogReadBuffer(data->node, data->rootBlkno, true);
335 Page rootPage = BufferGetPage(rootBuf);
337 GinInitBuffer(rootBuf, flags & ~GIN_LEAF);
341 Assert(data->rootBlkno != GIN_ROOT_BLKNO);
342 dataFillRoot(NULL, rootBuf, lbuffer, rbuffer);
346 Assert(data->rootBlkno == GIN_ROOT_BLKNO);
347 entryFillRoot(NULL, rootBuf, lbuffer, rbuffer);
350 PageSetLSN(rootPage, lsn);
351 PageSetTLI(rootPage, ThisTimeLineID);
353 MarkBufferDirty(rootBuf);
354 UnlockReleaseBuffer(rootBuf);
357 pushIncompleteSplit(data->node, data->lblkno, data->rblkno, data->rootBlkno);
359 UnlockReleaseBuffer(rbuffer);
360 UnlockReleaseBuffer(lbuffer);
364 ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record)
366 ginxlogVacuumPage *data = (ginxlogVacuumPage *) XLogRecGetData(record);
370 /* nothing to do if page was backed up (and no info to do it with) */
371 if (record->xl_info & XLR_BKP_BLOCK_1)
374 buffer = XLogReadBuffer(data->node, data->blkno, false);
375 if (!BufferIsValid(buffer))
377 page = (Page) BufferGetPage(buffer);
379 if (!XLByteLE(lsn, PageGetLSN(page)))
381 if (GinPageIsData(page))
383 memcpy(GinDataPageGetData(page), XLogRecGetData(record) + sizeof(ginxlogVacuumPage),
384 GinSizeOfItem(page) *data->nitem);
385 GinPageGetOpaque(page)->maxoff = data->nitem;
391 IndexTuple itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogVacuumPage));
393 tod = (OffsetNumber *) palloc(sizeof(OffsetNumber) * PageGetMaxOffsetNumber(page));
394 for (i = FirstOffsetNumber; i <= PageGetMaxOffsetNumber(page); i++)
397 PageIndexMultiDelete(page, tod, PageGetMaxOffsetNumber(page));
399 for (i = 0; i < data->nitem; i++)
401 if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
402 elog(ERROR, "failed to add item to index page in %u/%u/%u",
403 data->node.spcNode, data->node.dbNode, data->node.relNode);
404 itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
408 PageSetLSN(page, lsn);
409 PageSetTLI(page, ThisTimeLineID);
410 MarkBufferDirty(buffer);
413 UnlockReleaseBuffer(buffer);
417 ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record)
419 ginxlogDeletePage *data = (ginxlogDeletePage *) XLogRecGetData(record);
423 if (!(record->xl_info & XLR_BKP_BLOCK_1))
425 buffer = XLogReadBuffer(data->node, data->blkno, false);
426 if (BufferIsValid(buffer))
428 page = BufferGetPage(buffer);
429 if (!XLByteLE(lsn, PageGetLSN(page)))
431 Assert(GinPageIsData(page));
432 GinPageGetOpaque(page)->flags = GIN_DELETED;
433 PageSetLSN(page, lsn);
434 PageSetTLI(page, ThisTimeLineID);
435 MarkBufferDirty(buffer);
437 UnlockReleaseBuffer(buffer);
441 if (!(record->xl_info & XLR_BKP_BLOCK_2))
443 buffer = XLogReadBuffer(data->node, data->parentBlkno, false);
444 if (BufferIsValid(buffer))
446 page = BufferGetPage(buffer);
447 if (!XLByteLE(lsn, PageGetLSN(page)))
449 Assert(GinPageIsData(page));
450 Assert(!GinPageIsLeaf(page));
451 PageDeletePostingItem(page, data->parentOffset);
452 PageSetLSN(page, lsn);
453 PageSetTLI(page, ThisTimeLineID);
454 MarkBufferDirty(buffer);
456 UnlockReleaseBuffer(buffer);
460 if (!(record->xl_info & XLR_BKP_BLOCK_3) && data->leftBlkno != InvalidBlockNumber)
462 buffer = XLogReadBuffer(data->node, data->leftBlkno, false);
463 if (BufferIsValid(buffer))
465 page = BufferGetPage(buffer);
466 if (!XLByteLE(lsn, PageGetLSN(page)))
468 Assert(GinPageIsData(page));
469 GinPageGetOpaque(page)->rightlink = data->rightLink;
470 PageSetLSN(page, lsn);
471 PageSetTLI(page, ThisTimeLineID);
472 MarkBufferDirty(buffer);
474 UnlockReleaseBuffer(buffer);
480 ginRedoUpdateMetapage(XLogRecPtr lsn, XLogRecord *record)
482 ginxlogUpdateMeta *data = (ginxlogUpdateMeta *) XLogRecGetData(record);
487 metabuffer = XLogReadBuffer(data->node, GIN_METAPAGE_BLKNO, false);
488 if (!BufferIsValid(metabuffer))
489 elog(PANIC, "GIN metapage disappeared");
490 metapage = BufferGetPage(metabuffer);
492 if (!XLByteLE(lsn, PageGetLSN(metapage)))
494 memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
495 PageSetLSN(metapage, lsn);
496 PageSetTLI(metapage, ThisTimeLineID);
497 MarkBufferDirty(metabuffer);
500 if (data->ntuples > 0)
503 * insert into tail page
505 if (!(record->xl_info & XLR_BKP_BLOCK_1))
507 buffer = XLogReadBuffer(data->node, data->metadata.tail, false);
508 if (BufferIsValid(buffer))
510 Page page = BufferGetPage(buffer);
512 if (!XLByteLE(lsn, PageGetLSN(page)))
515 off = (PageIsEmpty(page)) ? FirstOffsetNumber :
516 OffsetNumberNext(PageGetMaxOffsetNumber(page));
519 IndexTuple tuples = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogUpdateMeta));
521 for (i = 0; i < data->ntuples; i++)
523 tupsize = IndexTupleSize(tuples);
525 l = PageAddItem(page, (Item) tuples, tupsize, off, false, false);
527 if (l == InvalidOffsetNumber)
528 elog(ERROR, "failed to add item to index page");
530 tuples = (IndexTuple) (((char *) tuples) + tupsize);
534 * Increase counter of heap tuples
536 GinPageGetOpaque(page)->maxoff++;
538 PageSetLSN(page, lsn);
539 PageSetTLI(page, ThisTimeLineID);
540 MarkBufferDirty(buffer);
542 UnlockReleaseBuffer(buffer);
546 else if (data->prevTail != InvalidBlockNumber)
551 buffer = XLogReadBuffer(data->node, data->prevTail, false);
552 if (BufferIsValid(buffer))
554 Page page = BufferGetPage(buffer);
556 if (!XLByteLE(lsn, PageGetLSN(page)))
558 GinPageGetOpaque(page)->rightlink = data->newRightlink;
560 PageSetLSN(page, lsn);
561 PageSetTLI(page, ThisTimeLineID);
562 MarkBufferDirty(buffer);
564 UnlockReleaseBuffer(buffer);
568 UnlockReleaseBuffer(metabuffer);
572 ginRedoInsertListPage(XLogRecPtr lsn, XLogRecord *record)
574 ginxlogInsertListPage *data = (ginxlogInsertListPage *) XLogRecGetData(record);
578 off = FirstOffsetNumber;
581 IndexTuple tuples = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsertListPage));
583 if (record->xl_info & XLR_BKP_BLOCK_1)
586 buffer = XLogReadBuffer(data->node, data->blkno, true);
587 Assert(BufferIsValid(buffer));
588 page = BufferGetPage(buffer);
590 GinInitBuffer(buffer, GIN_LIST);
591 GinPageGetOpaque(page)->rightlink = data->rightlink;
592 if (data->rightlink == InvalidBlockNumber)
594 /* tail of sublist */
595 GinPageSetFullRow(page);
596 GinPageGetOpaque(page)->maxoff = 1;
600 GinPageGetOpaque(page)->maxoff = 0;
603 for (i = 0; i < data->ntuples; i++)
605 tupsize = IndexTupleSize(tuples);
607 l = PageAddItem(page, (Item) tuples, tupsize, off, false, false);
609 if (l == InvalidOffsetNumber)
610 elog(ERROR, "failed to add item to index page");
612 tuples = (IndexTuple) (((char *) tuples) + tupsize);
615 PageSetLSN(page, lsn);
616 PageSetTLI(page, ThisTimeLineID);
617 MarkBufferDirty(buffer);
619 UnlockReleaseBuffer(buffer);
623 ginRedoDeleteListPages(XLogRecPtr lsn, XLogRecord *record)
625 ginxlogDeleteListPages *data = (ginxlogDeleteListPages *) XLogRecGetData(record);
630 metabuffer = XLogReadBuffer(data->node, GIN_METAPAGE_BLKNO, false);
631 if (!BufferIsValid(metabuffer))
632 elog(PANIC, "GIN metapage disappeared");
633 metapage = BufferGetPage(metabuffer);
635 if (!XLByteLE(lsn, PageGetLSN(metapage)))
637 memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
638 PageSetLSN(metapage, lsn);
639 PageSetTLI(metapage, ThisTimeLineID);
640 MarkBufferDirty(metabuffer);
643 for (i = 0; i < data->ndeleted; i++)
645 Buffer buffer = XLogReadBuffer(data->node, data->toDelete[i], false);
647 if (BufferIsValid(buffer))
649 Page page = BufferGetPage(buffer);
651 if (!XLByteLE(lsn, PageGetLSN(page)))
653 GinPageGetOpaque(page)->flags = GIN_DELETED;
655 PageSetLSN(page, lsn);
656 PageSetTLI(page, ThisTimeLineID);
657 MarkBufferDirty(buffer);
660 UnlockReleaseBuffer(buffer);
663 UnlockReleaseBuffer(metabuffer);
667 gin_redo(XLogRecPtr lsn, XLogRecord *record)
669 uint8 info = record->xl_info & ~XLR_INFO_MASK;
672 * GIN indexes do not require any conflict processing.
675 RestoreBkpBlocks(lsn, record, false);
677 topCtx = MemoryContextSwitchTo(opCtx);
680 case XLOG_GIN_CREATE_INDEX:
681 ginRedoCreateIndex(lsn, record);
683 case XLOG_GIN_CREATE_PTREE:
684 ginRedoCreatePTree(lsn, record);
686 case XLOG_GIN_INSERT:
687 ginRedoInsert(lsn, record);
690 ginRedoSplit(lsn, record);
692 case XLOG_GIN_VACUUM_PAGE:
693 ginRedoVacuumPage(lsn, record);
695 case XLOG_GIN_DELETE_PAGE:
696 ginRedoDeletePage(lsn, record);
698 case XLOG_GIN_UPDATE_META_PAGE:
699 ginRedoUpdateMetapage(lsn, record);
701 case XLOG_GIN_INSERT_LISTPAGE:
702 ginRedoInsertListPage(lsn, record);
704 case XLOG_GIN_DELETE_LISTPAGE:
705 ginRedoDeleteListPages(lsn, record);
708 elog(PANIC, "gin_redo: unknown op code %u", info);
710 MemoryContextSwitchTo(topCtx);
711 MemoryContextReset(opCtx);
715 desc_node(StringInfo buf, RelFileNode node, BlockNumber blkno)
717 appendStringInfo(buf, "node: %u/%u/%u blkno: %u",
718 node.spcNode, node.dbNode, node.relNode, blkno);
722 gin_desc(StringInfo buf, uint8 xl_info, char *rec)
724 uint8 info = xl_info & ~XLR_INFO_MASK;
728 case XLOG_GIN_CREATE_INDEX:
729 appendStringInfo(buf, "Create index, ");
730 desc_node(buf, *(RelFileNode *) rec, GIN_ROOT_BLKNO);
732 case XLOG_GIN_CREATE_PTREE:
733 appendStringInfo(buf, "Create posting tree, ");
734 desc_node(buf, ((ginxlogCreatePostingTree *) rec)->node, ((ginxlogCreatePostingTree *) rec)->blkno);
736 case XLOG_GIN_INSERT:
737 appendStringInfo(buf, "Insert item, ");
738 desc_node(buf, ((ginxlogInsert *) rec)->node, ((ginxlogInsert *) rec)->blkno);
739 appendStringInfo(buf, " offset: %u nitem: %u isdata: %c isleaf %c isdelete %c updateBlkno:%u",
740 ((ginxlogInsert *) rec)->offset,
741 ((ginxlogInsert *) rec)->nitem,
742 (((ginxlogInsert *) rec)->isData) ? 'T' : 'F',
743 (((ginxlogInsert *) rec)->isLeaf) ? 'T' : 'F',
744 (((ginxlogInsert *) rec)->isDelete) ? 'T' : 'F',
745 ((ginxlogInsert *) rec)->updateBlkno
750 appendStringInfo(buf, "Page split, ");
751 desc_node(buf, ((ginxlogSplit *) rec)->node, ((ginxlogSplit *) rec)->lblkno);
752 appendStringInfo(buf, " isrootsplit: %c", (((ginxlogSplit *) rec)->isRootSplit) ? 'T' : 'F');
754 case XLOG_GIN_VACUUM_PAGE:
755 appendStringInfo(buf, "Vacuum page, ");
756 desc_node(buf, ((ginxlogVacuumPage *) rec)->node, ((ginxlogVacuumPage *) rec)->blkno);
758 case XLOG_GIN_DELETE_PAGE:
759 appendStringInfo(buf, "Delete page, ");
760 desc_node(buf, ((ginxlogDeletePage *) rec)->node, ((ginxlogDeletePage *) rec)->blkno);
762 case XLOG_GIN_UPDATE_META_PAGE:
763 appendStringInfo(buf, "Update metapage, ");
764 desc_node(buf, ((ginxlogUpdateMeta *) rec)->node, ((ginxlogUpdateMeta *) rec)->metadata.tail);
766 case XLOG_GIN_INSERT_LISTPAGE:
767 appendStringInfo(buf, "Insert new list page, ");
768 desc_node(buf, ((ginxlogInsertListPage *) rec)->node, ((ginxlogInsertListPage *) rec)->blkno);
770 case XLOG_GIN_DELETE_LISTPAGE:
771 appendStringInfo(buf, "Delete list pages (%d), ", ((ginxlogDeleteListPages *) rec)->ndeleted);
772 desc_node(buf, ((ginxlogDeleteListPages *) rec)->node, ((ginxlogDeleteListPages *) rec)->metadata.head);
775 elog(PANIC, "gin_desc: unknown op code %u", info);
780 gin_xlog_startup(void)
782 incomplete_splits = NIL;
784 opCtx = AllocSetContextCreate(CurrentMemoryContext,
785 "GIN recovery temporary context",
786 ALLOCSET_DEFAULT_MINSIZE,
787 ALLOCSET_DEFAULT_INITSIZE,
788 ALLOCSET_DEFAULT_MAXSIZE);
792 ginContinueSplit(ginIncompleteSplit *split)
800 * elog(NOTICE,"ginContinueSplit root:%u l:%u r:%u", split->rootBlkno,
801 * split->leftBlkno, split->rightBlkno);
803 buffer = XLogReadBuffer(split->node, split->leftBlkno, false);
806 * Failure should be impossible here, because we wrote the page earlier.
808 if (!BufferIsValid(buffer))
809 elog(PANIC, "ginContinueSplit: left block %u not found",
812 reln = CreateFakeRelcacheEntry(split->node);
814 if (split->rootBlkno == GIN_ROOT_BLKNO)
816 prepareEntryScan(&btree, reln, InvalidOffsetNumber, (Datum) 0, NULL);
817 btree.entry = ginPageGetLinkItup(buffer);
821 Page page = BufferGetPage(buffer);
823 prepareDataScan(&btree, reln);
825 PostingItemSetBlockNumber(&(btree.pitem), split->leftBlkno);
826 if (GinPageIsLeaf(page))
827 btree.pitem.key = *(ItemPointerData *) GinDataPageGetItem(page,
828 GinPageGetOpaque(page)->maxoff);
830 btree.pitem.key = ((PostingItem *) GinDataPageGetItem(page,
831 GinPageGetOpaque(page)->maxoff))->key;
834 btree.rightblkno = split->rightBlkno;
836 stack.blkno = split->leftBlkno;
837 stack.buffer = buffer;
838 stack.off = InvalidOffsetNumber;
841 findParents(&btree, &stack, split->rootBlkno);
842 ginInsertValue(&btree, stack.parent);
844 FreeFakeRelcacheEntry(reln);
846 UnlockReleaseBuffer(buffer);
850 gin_xlog_cleanup(void)
853 MemoryContext topCtx;
855 topCtx = MemoryContextSwitchTo(opCtx);
857 foreach(l, incomplete_splits)
859 ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
861 ginContinueSplit(split);
862 MemoryContextReset(opCtx);
865 MemoryContextSwitchTo(topCtx);
866 MemoryContextDelete(opCtx);
867 incomplete_splits = NIL;
871 gin_safe_restartpoint(void)
873 if (incomplete_splits)