1 /*-------------------------------------------------------------------------
4 * WAL replay logic for inverted index.
7 * Portions Copyright (c) 1996-2013, 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 */
21 static MemoryContext topCtx;
23 typedef struct ginIncompleteSplit
26 BlockNumber leftBlkno;
27 BlockNumber rightBlkno;
28 BlockNumber rootBlkno;
31 static List *incomplete_splits;
34 pushIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber rightBlkno, BlockNumber rootBlkno)
36 ginIncompleteSplit *split;
38 MemoryContextSwitchTo(topCtx);
40 split = palloc(sizeof(ginIncompleteSplit));
43 split->leftBlkno = leftBlkno;
44 split->rightBlkno = rightBlkno;
45 split->rootBlkno = rootBlkno;
47 incomplete_splits = lappend(incomplete_splits, split);
49 MemoryContextSwitchTo(opCtx);
53 forgetIncompleteSplit(RelFileNode node, BlockNumber leftBlkno, BlockNumber updateBlkno)
57 foreach(l, incomplete_splits)
59 ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
61 if (RelFileNodeEquals(node, split->node) &&
62 leftBlkno == split->leftBlkno &&
63 updateBlkno == split->rightBlkno)
65 incomplete_splits = list_delete_ptr(incomplete_splits, split);
73 ginRedoCreateIndex(XLogRecPtr lsn, XLogRecord *record)
75 RelFileNode *node = (RelFileNode *) XLogRecGetData(record);
80 /* Backup blocks are not used in create_index records */
81 Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
83 MetaBuffer = XLogReadBuffer(*node, GIN_METAPAGE_BLKNO, true);
84 Assert(BufferIsValid(MetaBuffer));
85 page = (Page) BufferGetPage(MetaBuffer);
87 GinInitMetabuffer(MetaBuffer);
89 PageSetLSN(page, lsn);
90 MarkBufferDirty(MetaBuffer);
92 RootBuffer = XLogReadBuffer(*node, GIN_ROOT_BLKNO, true);
93 Assert(BufferIsValid(RootBuffer));
94 page = (Page) BufferGetPage(RootBuffer);
96 GinInitBuffer(RootBuffer, GIN_LEAF);
98 PageSetLSN(page, lsn);
99 MarkBufferDirty(RootBuffer);
101 UnlockReleaseBuffer(RootBuffer);
102 UnlockReleaseBuffer(MetaBuffer);
106 ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record)
108 ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree *) XLogRecGetData(record);
109 ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree));
113 /* Backup blocks are not used in create_ptree records */
114 Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
116 buffer = XLogReadBuffer(data->node, data->blkno, true);
117 Assert(BufferIsValid(buffer));
118 page = (Page) BufferGetPage(buffer);
120 GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
121 memcpy(GinDataPageGetData(page), items, sizeof(ItemPointerData) * data->nitem);
122 GinPageGetOpaque(page)->maxoff = data->nitem;
124 PageSetLSN(page, lsn);
126 MarkBufferDirty(buffer);
127 UnlockReleaseBuffer(buffer);
131 ginRedoInsert(XLogRecPtr lsn, XLogRecord *record)
133 ginxlogInsert *data = (ginxlogInsert *) XLogRecGetData(record);
137 /* first, forget any incomplete split this insertion completes */
140 Assert(data->isDelete == FALSE);
141 if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
145 pitem = (PostingItem *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
146 forgetIncompleteSplit(data->node,
147 PostingItemGetBlockNumber(pitem),
154 if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
158 itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
159 forgetIncompleteSplit(data->node,
160 GinGetDownlink(itup),
165 /* If we have a full-page image, restore it and we're done */
166 if (record->xl_info & XLR_BKP_BLOCK(0))
168 (void) RestoreBackupBlock(lsn, record, 0, false, false);
172 buffer = XLogReadBuffer(data->node, data->blkno, false);
173 if (!BufferIsValid(buffer))
174 return; /* page was deleted, nothing to do */
175 page = (Page) BufferGetPage(buffer);
177 if (lsn > PageGetLSN(page))
181 Assert(GinPageIsData(page));
186 ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
188 Assert(GinPageIsLeaf(page));
189 Assert(data->updateBlkno == InvalidBlockNumber);
191 for (i = 0; i < data->nitem; i++)
192 GinDataPageAddItemPointer(page, &items[i], data->offset + i);
198 Assert(!GinPageIsLeaf(page));
200 if (data->updateBlkno != InvalidBlockNumber)
202 /* update link to right page after split */
203 pitem = GinDataPageGetPostingItem(page, data->offset);
204 PostingItemSetBlockNumber(pitem, data->updateBlkno);
207 pitem = (PostingItem *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
209 GinDataPageAddPostingItem(page, pitem, data->offset);
216 Assert(!GinPageIsData(page));
218 if (data->updateBlkno != InvalidBlockNumber)
220 /* update link to right page after split */
221 Assert(!GinPageIsLeaf(page));
222 Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
223 itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, data->offset));
224 GinSetDownlink(itup, data->updateBlkno);
229 Assert(GinPageIsLeaf(page));
230 Assert(data->offset >= FirstOffsetNumber && data->offset <= PageGetMaxOffsetNumber(page));
231 PageIndexTupleDelete(page, data->offset);
234 itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsert));
236 if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), data->offset, false, false) == InvalidOffsetNumber)
237 elog(ERROR, "failed to add item to index page in %u/%u/%u",
238 data->node.spcNode, data->node.dbNode, data->node.relNode);
241 PageSetLSN(page, lsn);
243 MarkBufferDirty(buffer);
246 UnlockReleaseBuffer(buffer);
250 ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
252 ginxlogSplit *data = (ginxlogSplit *) XLogRecGetData(record);
264 /* Backup blocks are not used in split records */
265 Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
267 lbuffer = XLogReadBuffer(data->node, data->lblkno, true);
268 Assert(BufferIsValid(lbuffer));
269 lpage = (Page) BufferGetPage(lbuffer);
270 GinInitBuffer(lbuffer, flags);
272 rbuffer = XLogReadBuffer(data->node, data->rblkno, true);
273 Assert(BufferIsValid(rbuffer));
274 rpage = (Page) BufferGetPage(rbuffer);
275 GinInitBuffer(rbuffer, flags);
277 GinPageGetOpaque(lpage)->rightlink = BufferGetBlockNumber(rbuffer);
278 GinPageGetOpaque(rpage)->rightlink = data->rrlink;
282 char *ptr = XLogRecGetData(record) + sizeof(ginxlogSplit);
283 Size sizeofitem = GinSizeOfDataPageItem(lpage);
287 for (i = 0; i < data->separator; i++)
290 GinDataPageAddItemPointer(lpage, (ItemPointer) ptr, InvalidOffsetNumber);
292 GinDataPageAddPostingItem(lpage, (PostingItem *) ptr, InvalidOffsetNumber);
296 for (i = data->separator; i < data->nitem; i++)
299 GinDataPageAddItemPointer(rpage, (ItemPointer) ptr, InvalidOffsetNumber);
301 GinDataPageAddPostingItem(rpage, (PostingItem *) ptr, InvalidOffsetNumber);
305 /* set up right key */
306 bound = GinDataPageGetRightBound(lpage);
308 *bound = *GinDataPageGetItemPointer(lpage, GinPageGetOpaque(lpage)->maxoff);
310 *bound = GinDataPageGetPostingItem(lpage, GinPageGetOpaque(lpage)->maxoff)->key;
312 bound = GinDataPageGetRightBound(rpage);
313 *bound = data->rightbound;
317 IndexTuple itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogSplit));
320 for (i = 0; i < data->separator; i++)
322 if (PageAddItem(lpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
323 elog(ERROR, "failed to add item to index page in %u/%u/%u",
324 data->node.spcNode, data->node.dbNode, data->node.relNode);
325 itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
328 for (i = data->separator; i < data->nitem; i++)
330 if (PageAddItem(rpage, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
331 elog(ERROR, "failed to add item to index page in %u/%u/%u",
332 data->node.spcNode, data->node.dbNode, data->node.relNode);
333 itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
337 PageSetLSN(rpage, lsn);
338 MarkBufferDirty(rbuffer);
340 PageSetLSN(lpage, lsn);
341 MarkBufferDirty(lbuffer);
343 if (!data->isLeaf && data->updateBlkno != InvalidBlockNumber)
344 forgetIncompleteSplit(data->node, data->leftChildBlkno, data->updateBlkno);
346 if (data->isRootSplit)
348 Buffer rootBuf = XLogReadBuffer(data->node, data->rootBlkno, true);
349 Page rootPage = BufferGetPage(rootBuf);
351 GinInitBuffer(rootBuf, flags & ~GIN_LEAF);
355 Assert(data->rootBlkno != GIN_ROOT_BLKNO);
356 ginDataFillRoot(NULL, rootBuf, lbuffer, rbuffer);
360 Assert(data->rootBlkno == GIN_ROOT_BLKNO);
361 ginEntryFillRoot(NULL, rootBuf, lbuffer, rbuffer);
364 PageSetLSN(rootPage, lsn);
366 MarkBufferDirty(rootBuf);
367 UnlockReleaseBuffer(rootBuf);
370 pushIncompleteSplit(data->node, data->lblkno, data->rblkno, data->rootBlkno);
372 UnlockReleaseBuffer(rbuffer);
373 UnlockReleaseBuffer(lbuffer);
377 ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record)
379 ginxlogVacuumPage *data = (ginxlogVacuumPage *) XLogRecGetData(record);
383 /* If we have a full-page image, restore it and we're done */
384 if (record->xl_info & XLR_BKP_BLOCK(0))
386 (void) RestoreBackupBlock(lsn, record, 0, false, false);
390 buffer = XLogReadBuffer(data->node, data->blkno, false);
391 if (!BufferIsValid(buffer))
393 page = (Page) BufferGetPage(buffer);
395 if (lsn > PageGetLSN(page))
397 if (GinPageIsData(page))
399 memcpy(GinDataPageGetData(page),
400 XLogRecGetData(record) + sizeof(ginxlogVacuumPage),
401 data->nitem * GinSizeOfDataPageItem(page));
402 GinPageGetOpaque(page)->maxoff = data->nitem;
408 IndexTuple itup = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogVacuumPage));
410 tod = (OffsetNumber *) palloc(sizeof(OffsetNumber) * PageGetMaxOffsetNumber(page));
411 for (i = FirstOffsetNumber; i <= PageGetMaxOffsetNumber(page); i++)
414 PageIndexMultiDelete(page, tod, PageGetMaxOffsetNumber(page));
416 for (i = 0; i < data->nitem; i++)
418 if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), InvalidOffsetNumber, false, false) == InvalidOffsetNumber)
419 elog(ERROR, "failed to add item to index page in %u/%u/%u",
420 data->node.spcNode, data->node.dbNode, data->node.relNode);
421 itup = (IndexTuple) (((char *) itup) + MAXALIGN(IndexTupleSize(itup)));
425 PageSetLSN(page, lsn);
426 MarkBufferDirty(buffer);
429 UnlockReleaseBuffer(buffer);
433 ginRedoDeletePage(XLogRecPtr lsn, XLogRecord *record)
435 ginxlogDeletePage *data = (ginxlogDeletePage *) XLogRecGetData(record);
441 if (record->xl_info & XLR_BKP_BLOCK(0))
442 dbuffer = RestoreBackupBlock(lsn, record, 0, false, true);
445 dbuffer = XLogReadBuffer(data->node, data->blkno, false);
446 if (BufferIsValid(dbuffer))
448 page = BufferGetPage(dbuffer);
449 if (lsn > PageGetLSN(page))
451 Assert(GinPageIsData(page));
452 GinPageGetOpaque(page)->flags = GIN_DELETED;
453 PageSetLSN(page, lsn);
454 MarkBufferDirty(dbuffer);
459 if (record->xl_info & XLR_BKP_BLOCK(1))
460 pbuffer = RestoreBackupBlock(lsn, record, 1, false, true);
463 pbuffer = XLogReadBuffer(data->node, data->parentBlkno, false);
464 if (BufferIsValid(pbuffer))
466 page = BufferGetPage(pbuffer);
467 if (lsn > PageGetLSN(page))
469 Assert(GinPageIsData(page));
470 Assert(!GinPageIsLeaf(page));
471 GinPageDeletePostingItem(page, data->parentOffset);
472 PageSetLSN(page, lsn);
473 MarkBufferDirty(pbuffer);
478 if (record->xl_info & XLR_BKP_BLOCK(2))
479 (void) RestoreBackupBlock(lsn, record, 2, false, false);
480 else if (data->leftBlkno != InvalidBlockNumber)
482 lbuffer = XLogReadBuffer(data->node, data->leftBlkno, false);
483 if (BufferIsValid(lbuffer))
485 page = BufferGetPage(lbuffer);
486 if (lsn > PageGetLSN(page))
488 Assert(GinPageIsData(page));
489 GinPageGetOpaque(page)->rightlink = data->rightLink;
490 PageSetLSN(page, lsn);
491 MarkBufferDirty(lbuffer);
493 UnlockReleaseBuffer(lbuffer);
497 if (BufferIsValid(pbuffer))
498 UnlockReleaseBuffer(pbuffer);
499 if (BufferIsValid(dbuffer))
500 UnlockReleaseBuffer(dbuffer);
504 ginRedoUpdateMetapage(XLogRecPtr lsn, XLogRecord *record)
506 ginxlogUpdateMeta *data = (ginxlogUpdateMeta *) XLogRecGetData(record);
511 metabuffer = XLogReadBuffer(data->node, GIN_METAPAGE_BLKNO, false);
512 if (!BufferIsValid(metabuffer))
513 return; /* assume index was deleted, nothing to do */
514 metapage = BufferGetPage(metabuffer);
516 if (lsn > PageGetLSN(metapage))
518 memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
519 PageSetLSN(metapage, lsn);
520 MarkBufferDirty(metabuffer);
523 if (data->ntuples > 0)
526 * insert into tail page
528 if (record->xl_info & XLR_BKP_BLOCK(0))
529 (void) RestoreBackupBlock(lsn, record, 0, false, false);
532 buffer = XLogReadBuffer(data->node, data->metadata.tail, false);
533 if (BufferIsValid(buffer))
535 Page page = BufferGetPage(buffer);
537 if (lsn > PageGetLSN(page))
540 off = (PageIsEmpty(page)) ? FirstOffsetNumber :
541 OffsetNumberNext(PageGetMaxOffsetNumber(page));
544 IndexTuple tuples = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogUpdateMeta));
546 for (i = 0; i < data->ntuples; i++)
548 tupsize = IndexTupleSize(tuples);
550 l = PageAddItem(page, (Item) tuples, tupsize, off, false, false);
552 if (l == InvalidOffsetNumber)
553 elog(ERROR, "failed to add item to index page");
555 tuples = (IndexTuple) (((char *) tuples) + tupsize);
561 * Increase counter of heap tuples
563 GinPageGetOpaque(page)->maxoff++;
565 PageSetLSN(page, lsn);
566 MarkBufferDirty(buffer);
568 UnlockReleaseBuffer(buffer);
572 else if (data->prevTail != InvalidBlockNumber)
577 if (record->xl_info & XLR_BKP_BLOCK(0))
578 (void) RestoreBackupBlock(lsn, record, 0, false, false);
581 buffer = XLogReadBuffer(data->node, data->prevTail, false);
582 if (BufferIsValid(buffer))
584 Page page = BufferGetPage(buffer);
586 if (lsn > PageGetLSN(page))
588 GinPageGetOpaque(page)->rightlink = data->newRightlink;
590 PageSetLSN(page, lsn);
591 MarkBufferDirty(buffer);
593 UnlockReleaseBuffer(buffer);
598 UnlockReleaseBuffer(metabuffer);
602 ginRedoInsertListPage(XLogRecPtr lsn, XLogRecord *record)
604 ginxlogInsertListPage *data = (ginxlogInsertListPage *) XLogRecGetData(record);
608 off = FirstOffsetNumber;
611 IndexTuple tuples = (IndexTuple) (XLogRecGetData(record) + sizeof(ginxlogInsertListPage));
613 /* If we have a full-page image, restore it and we're done */
614 if (record->xl_info & XLR_BKP_BLOCK(0))
616 (void) RestoreBackupBlock(lsn, record, 0, false, false);
620 buffer = XLogReadBuffer(data->node, data->blkno, true);
621 Assert(BufferIsValid(buffer));
622 page = BufferGetPage(buffer);
624 GinInitBuffer(buffer, GIN_LIST);
625 GinPageGetOpaque(page)->rightlink = data->rightlink;
626 if (data->rightlink == InvalidBlockNumber)
628 /* tail of sublist */
629 GinPageSetFullRow(page);
630 GinPageGetOpaque(page)->maxoff = 1;
634 GinPageGetOpaque(page)->maxoff = 0;
637 for (i = 0; i < data->ntuples; i++)
639 tupsize = IndexTupleSize(tuples);
641 l = PageAddItem(page, (Item) tuples, tupsize, off, false, false);
643 if (l == InvalidOffsetNumber)
644 elog(ERROR, "failed to add item to index page");
646 tuples = (IndexTuple) (((char *) tuples) + tupsize);
649 PageSetLSN(page, lsn);
650 MarkBufferDirty(buffer);
652 UnlockReleaseBuffer(buffer);
656 ginRedoDeleteListPages(XLogRecPtr lsn, XLogRecord *record)
658 ginxlogDeleteListPages *data = (ginxlogDeleteListPages *) XLogRecGetData(record);
663 /* Backup blocks are not used in delete_listpage records */
664 Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
666 metabuffer = XLogReadBuffer(data->node, GIN_METAPAGE_BLKNO, false);
667 if (!BufferIsValid(metabuffer))
668 return; /* assume index was deleted, nothing to do */
669 metapage = BufferGetPage(metabuffer);
671 if (lsn > PageGetLSN(metapage))
673 memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
674 PageSetLSN(metapage, lsn);
675 MarkBufferDirty(metabuffer);
679 * In normal operation, shiftList() takes exclusive lock on all the
680 * pages-to-be-deleted simultaneously. During replay, however, it should
681 * be all right to lock them one at a time. This is dependent on the fact
682 * that we are deleting pages from the head of the list, and that readers
683 * share-lock the next page before releasing the one they are on. So we
684 * cannot get past a reader that is on, or due to visit, any page we are
685 * going to delete. New incoming readers will block behind our metapage
686 * lock and then see a fully updated page list.
688 for (i = 0; i < data->ndeleted; i++)
690 Buffer buffer = XLogReadBuffer(data->node, data->toDelete[i], false);
692 if (BufferIsValid(buffer))
694 Page page = BufferGetPage(buffer);
696 if (lsn > PageGetLSN(page))
698 GinPageGetOpaque(page)->flags = GIN_DELETED;
700 PageSetLSN(page, lsn);
701 MarkBufferDirty(buffer);
704 UnlockReleaseBuffer(buffer);
707 UnlockReleaseBuffer(metabuffer);
711 gin_redo(XLogRecPtr lsn, XLogRecord *record)
713 uint8 info = record->xl_info & ~XLR_INFO_MASK;
716 * GIN indexes do not require any conflict processing. NB: If we ever
717 * implement a similar optimization as we have in b-tree, and remove
718 * killed tuples outside VACUUM, we'll need to handle that here.
721 topCtx = MemoryContextSwitchTo(opCtx);
724 case XLOG_GIN_CREATE_INDEX:
725 ginRedoCreateIndex(lsn, record);
727 case XLOG_GIN_CREATE_PTREE:
728 ginRedoCreatePTree(lsn, record);
730 case XLOG_GIN_INSERT:
731 ginRedoInsert(lsn, record);
734 ginRedoSplit(lsn, record);
736 case XLOG_GIN_VACUUM_PAGE:
737 ginRedoVacuumPage(lsn, record);
739 case XLOG_GIN_DELETE_PAGE:
740 ginRedoDeletePage(lsn, record);
742 case XLOG_GIN_UPDATE_META_PAGE:
743 ginRedoUpdateMetapage(lsn, record);
745 case XLOG_GIN_INSERT_LISTPAGE:
746 ginRedoInsertListPage(lsn, record);
748 case XLOG_GIN_DELETE_LISTPAGE:
749 ginRedoDeleteListPages(lsn, record);
752 elog(PANIC, "gin_redo: unknown op code %u", info);
754 MemoryContextSwitchTo(topCtx);
755 MemoryContextReset(opCtx);
759 gin_xlog_startup(void)
761 incomplete_splits = NIL;
763 opCtx = AllocSetContextCreate(CurrentMemoryContext,
764 "GIN recovery temporary context",
765 ALLOCSET_DEFAULT_MINSIZE,
766 ALLOCSET_DEFAULT_INITSIZE,
767 ALLOCSET_DEFAULT_MAXSIZE);
771 ginContinueSplit(ginIncompleteSplit *split)
780 * elog(NOTICE,"ginContinueSplit root:%u l:%u r:%u", split->rootBlkno,
781 * split->leftBlkno, split->rightBlkno);
783 buffer = XLogReadBuffer(split->node, split->leftBlkno, false);
786 * Failure should be impossible here, because we wrote the page earlier.
788 if (!BufferIsValid(buffer))
789 elog(PANIC, "ginContinueSplit: left block %u not found",
792 reln = CreateFakeRelcacheEntry(split->node);
794 if (split->rootBlkno == GIN_ROOT_BLKNO)
796 MemSet(&ginstate, 0, sizeof(ginstate));
797 ginstate.index = reln;
799 ginPrepareEntryScan(&btree,
800 InvalidOffsetNumber, (Datum) 0, GIN_CAT_NULL_KEY,
805 ginPrepareDataScan(&btree, reln);
808 stack.blkno = split->leftBlkno;
809 stack.buffer = buffer;
810 stack.off = InvalidOffsetNumber;
813 ginFindParents(&btree, &stack, split->rootBlkno);
815 btree.prepareDownlink(&btree, buffer);
816 ginInsertValue(&btree, stack.parent, NULL);
818 FreeFakeRelcacheEntry(reln);
820 UnlockReleaseBuffer(buffer);
824 gin_xlog_cleanup(void)
827 MemoryContext topCtx;
829 topCtx = MemoryContextSwitchTo(opCtx);
831 foreach(l, incomplete_splits)
833 ginIncompleteSplit *split = (ginIncompleteSplit *) lfirst(l);
835 ginContinueSplit(split);
836 MemoryContextReset(opCtx);
839 MemoryContextSwitchTo(topCtx);
840 MemoryContextDelete(opCtx);
841 incomplete_splits = NIL;
845 gin_safe_restartpoint(void)
847 if (incomplete_splits)