1 /*-------------------------------------------------------------------------
4 * routines for handling GIN posting tree pages.
7 * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/access/gin/gindatapage.c
12 *-------------------------------------------------------------------------
17 #include "access/gin_private.h"
18 #include "access/xloginsert.h"
19 #include "lib/ilist.h"
20 #include "miscadmin.h"
21 #include "utils/memutils.h"
22 #include "utils/rel.h"
25 * Min, Max and Target size of posting lists stored on leaf pages, in bytes.
27 * The code can deal with any size, but random access is more efficient when
28 * a number of smaller lists are stored, rather than one big list. If a
29 * posting list would become larger than Max size as a result of insertions,
30 * it is split into two. If a posting list would be smaller than minimum
31 * size, it is merged with the next posting list.
33 #define GinPostingListSegmentMaxSize 384
34 #define GinPostingListSegmentTargetSize 256
35 #define GinPostingListSegmentMinSize 128
38 * At least this many items fit in a GinPostingListSegmentMaxSize-bytes
39 * long segment. This is used when estimating how much space is required
40 * for N items, at minimum.
42 #define MinTuplesPerSegment ((GinPostingListSegmentMaxSize - 2) / 6)
45 * A working struct for manipulating a posting tree leaf page.
49 dlist_head segments; /* a list of leafSegmentInfos */
52 * The following fields represent how the segments are split across pages,
53 * if a page split is required. Filled in by leafRepackItems.
55 dlist_node *lastleft; /* last segment on left page */
56 int lsize; /* total size on left page */
57 int rsize; /* total size on right page */
59 bool oldformat; /* page is in pre-9.4 format on disk */
64 dlist_node node; /* linked list pointers */
67 * 'action' indicates the status of this in-memory segment, compared to
68 * what's on disk. It is one of the GIN_SEGMENT_* action codes:
70 * UNMODIFIED no changes
71 * DELETE the segment is to be removed. 'seg' and 'items' are
73 * INSERT this is a completely new segment
74 * REPLACE this replaces an existing segment with new content
75 * ADDITEMS like REPLACE, but no items have been removed, and we track
76 * in detail what items have been added to this segment, in
82 ItemPointerData *modifieditems;
86 * The following fields represent the items in this segment. If 'items' is
87 * not NULL, it contains a palloc'd array of the itemsin this segment. If
88 * 'seg' is not NULL, it contains the items in an already-compressed
89 * format. It can point to an on-disk page (!modified), or a palloc'd
90 * segment in memory. If both are set, they must represent the same items.
94 int nitems; /* # of items in 'items', if items != NULL */
97 static ItemPointer dataLeafPageGetUncompressed(Page page, int *nitems);
98 static void dataSplitPageInternal(GinBtree btree, Buffer origbuf,
100 void *insertdata, BlockNumber updateblkno,
101 Page *newlpage, Page *newrpage);
103 static disassembledLeaf *disassembleLeaf(Page page);
104 static bool leafRepackItems(disassembledLeaf *leaf, ItemPointer remaining);
105 static bool addItemsToLeaf(disassembledLeaf *leaf, ItemPointer newItems,
108 static void registerLeafRecompressWALData(Buffer buf, disassembledLeaf *leaf);
109 static void dataPlaceToPageLeafRecompress(Buffer buf, disassembledLeaf *leaf);
110 static void dataPlaceToPageLeafSplit(Buffer buf,
111 disassembledLeaf *leaf,
112 ItemPointerData lbound, ItemPointerData rbound,
113 Page lpage, Page rpage);
116 * Read TIDs from leaf data page to single uncompressed array. The TIDs are
117 * returned in ascending order.
119 * advancePast is a hint, indicating that the caller is only interested in
120 * TIDs > advancePast. To return all items, use ItemPointerSetMin.
122 * Note: This function can still return items smaller than advancePast that
123 * are in the same posting list as the items of interest, so the caller must
124 * still check all the returned items. But passing it allows this function to
125 * skip whole posting lists.
128 GinDataLeafPageGetItems(Page page, int *nitems, ItemPointerData advancePast)
132 if (GinPageIsCompressed(page))
134 GinPostingList *seg = GinDataLeafPageGetPostingList(page);
135 Size len = GinDataLeafPageGetPostingListSize(page);
136 Pointer endptr = ((Pointer) seg) + len;
137 GinPostingList *next;
139 /* Skip to the segment containing advancePast+1 */
140 if (ItemPointerIsValid(&advancePast))
142 next = GinNextPostingListSegment(seg);
143 while ((Pointer) next < endptr &&
144 ginCompareItemPointers(&next->first, &advancePast) <= 0)
147 next = GinNextPostingListSegment(seg);
149 len = endptr - (Pointer) seg;
153 result = ginPostingListDecodeAllSegments(seg, len, nitems);
162 ItemPointer tmp = dataLeafPageGetUncompressed(page, nitems);
164 result = palloc((*nitems) * sizeof(ItemPointerData));
165 memcpy(result, tmp, (*nitems) * sizeof(ItemPointerData));
172 * Places all TIDs from leaf data page to bitmap.
175 GinDataLeafPageGetItemsToTbm(Page page, TIDBitmap *tbm)
177 ItemPointer uncompressed;
180 if (GinPageIsCompressed(page))
182 GinPostingList *segment = GinDataLeafPageGetPostingList(page);
183 Size len = GinDataLeafPageGetPostingListSize(page);
185 nitems = ginPostingListDecodeAllSegmentsToTbm(segment, len, tbm);
189 uncompressed = dataLeafPageGetUncompressed(page, &nitems);
192 tbm_add_tuples(tbm, uncompressed, nitems, false);
199 * Get pointer to the uncompressed array of items on a pre-9.4 format
200 * uncompressed leaf page. The number of items in the array is returned in
204 dataLeafPageGetUncompressed(Page page, int *nitems)
208 Assert(!GinPageIsCompressed(page));
211 * In the old pre-9.4 page format, the whole page content is used for
212 * uncompressed items, and the number of items is stored in 'maxoff'
214 items = (ItemPointer) GinDataPageGetData(page);
215 *nitems = GinPageGetOpaque(page)->maxoff;
221 * Check if we should follow the right link to find the item we're searching
224 * Compares inserting item pointer with the right bound of the current page.
227 dataIsMoveRight(GinBtree btree, Page page)
229 ItemPointer iptr = GinDataPageGetRightBound(page);
231 if (GinPageRightMost(page))
234 return (ginCompareItemPointers(&btree->itemptr, iptr) > 0) ? TRUE : FALSE;
238 * Find correct PostingItem in non-leaf page. It is assumed that this is
239 * the correct page, and the searched value SHOULD be on the page.
242 dataLocateItem(GinBtree btree, GinBtreeStack *stack)
247 PostingItem *pitem = NULL;
249 Page page = BufferGetPage(stack->buffer);
251 Assert(!GinPageIsLeaf(page));
252 Assert(GinPageIsData(page));
256 stack->off = FirstOffsetNumber;
257 stack->predictNumber *= GinPageGetOpaque(page)->maxoff;
258 return btree->getLeftMostChild(btree, page);
261 low = FirstOffsetNumber;
262 maxoff = high = GinPageGetOpaque(page)->maxoff;
269 OffsetNumber mid = low + ((high - low) / 2);
271 pitem = GinDataPageGetPostingItem(page, mid);
276 * Right infinity, page already correctly chosen with a help of
283 pitem = GinDataPageGetPostingItem(page, mid);
284 result = ginCompareItemPointers(&btree->itemptr, &(pitem->key));
290 return PostingItemGetBlockNumber(pitem);
298 Assert(high >= FirstOffsetNumber && high <= maxoff);
301 pitem = GinDataPageGetPostingItem(page, high);
302 return PostingItemGetBlockNumber(pitem);
306 * Find link to blkno on non-leaf page, returns offset of PostingItem
309 dataFindChildPtr(GinBtree btree, Page page, BlockNumber blkno, OffsetNumber storedOff)
312 maxoff = GinPageGetOpaque(page)->maxoff;
315 Assert(!GinPageIsLeaf(page));
316 Assert(GinPageIsData(page));
318 /* if page isn't changed, we return storedOff */
319 if (storedOff >= FirstOffsetNumber && storedOff <= maxoff)
321 pitem = GinDataPageGetPostingItem(page, storedOff);
322 if (PostingItemGetBlockNumber(pitem) == blkno)
326 * we hope, that needed pointer goes to right. It's true if there
329 for (i = storedOff + 1; i <= maxoff; i++)
331 pitem = GinDataPageGetPostingItem(page, i);
332 if (PostingItemGetBlockNumber(pitem) == blkno)
336 maxoff = storedOff - 1;
340 for (i = FirstOffsetNumber; i <= maxoff; i++)
342 pitem = GinDataPageGetPostingItem(page, i);
343 if (PostingItemGetBlockNumber(pitem) == blkno)
347 return InvalidOffsetNumber;
351 * Return blkno of leftmost child
354 dataGetLeftMostPage(GinBtree btree, Page page)
358 Assert(!GinPageIsLeaf(page));
359 Assert(GinPageIsData(page));
360 Assert(GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber);
362 pitem = GinDataPageGetPostingItem(page, FirstOffsetNumber);
363 return PostingItemGetBlockNumber(pitem);
367 * Add PostingItem to a non-leaf page.
370 GinDataPageAddPostingItem(Page page, PostingItem *data, OffsetNumber offset)
372 OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
375 Assert(PostingItemGetBlockNumber(data) != InvalidBlockNumber);
376 Assert(!GinPageIsLeaf(page));
378 if (offset == InvalidOffsetNumber)
380 ptr = (char *) GinDataPageGetPostingItem(page, maxoff + 1);
384 ptr = (char *) GinDataPageGetPostingItem(page, offset);
385 if (offset != maxoff + 1)
386 memmove(ptr + sizeof(PostingItem),
388 (maxoff - offset + 1) *sizeof(PostingItem));
390 memcpy(ptr, data, sizeof(PostingItem));
393 GinPageGetOpaque(page)->maxoff = maxoff;
396 * Also set pd_lower to the end of the posting items, to follow the
397 * "standard" page layout, so that we can squeeze out the unused space
398 * from full-page images.
400 GinDataPageSetDataSize(page, maxoff * sizeof(PostingItem));
404 * Delete posting item from non-leaf page
407 GinPageDeletePostingItem(Page page, OffsetNumber offset)
409 OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
411 Assert(!GinPageIsLeaf(page));
412 Assert(offset >= FirstOffsetNumber && offset <= maxoff);
414 if (offset != maxoff)
415 memmove(GinDataPageGetPostingItem(page, offset),
416 GinDataPageGetPostingItem(page, offset + 1),
417 sizeof(PostingItem) * (maxoff - offset));
420 GinPageGetOpaque(page)->maxoff = maxoff;
422 GinDataPageSetDataSize(page, maxoff * sizeof(PostingItem));
426 * Places keys to leaf data page and fills WAL record.
428 static GinPlaceToPageRC
429 dataPlaceToPageLeaf(GinBtree btree, Buffer buf, GinBtreeStack *stack,
430 void *insertdata, Page *newlpage, Page *newrpage)
432 GinBtreeDataLeafInsertData *items = insertdata;
433 ItemPointer newItems = &items->items[items->curitem];
434 int maxitems = items->nitem - items->curitem;
435 Page page = BufferGetPage(buf);
437 ItemPointerData rbound;
438 ItemPointerData lbound;
443 MemoryContext tmpCxt;
444 MemoryContext oldCxt;
445 disassembledLeaf *leaf;
446 leafSegmentInfo *lastleftinfo;
447 ItemPointerData maxOldItem;
448 ItemPointerData remaining;
450 Assert(GinPageIsData(page));
452 rbound = *GinDataPageGetRightBound(page);
455 * Count how many of the new items belong to this page.
457 if (!GinPageRightMost(page))
459 for (i = 0; i < maxitems; i++)
461 if (ginCompareItemPointers(&newItems[i], &rbound) > 0)
464 * This needs to go to some other location in the tree. (The
465 * caller should've chosen the insert location so that at
466 * least the first item goes here.)
476 * The following operations do quite a lot of small memory allocations,
477 * create a temporary memory context so that we don't need to keep track
478 * of them individually.
480 tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
481 "Gin split temporary context",
482 ALLOCSET_DEFAULT_MINSIZE,
483 ALLOCSET_DEFAULT_INITSIZE,
484 ALLOCSET_DEFAULT_MAXSIZE);
485 oldCxt = MemoryContextSwitchTo(tmpCxt);
487 leaf = disassembleLeaf(page);
490 * Are we appending to the end of the page? IOW, are all the new items
491 * larger than any of the existing items.
493 if (!dlist_is_empty(&leaf->segments))
495 lastleftinfo = dlist_container(leafSegmentInfo, node,
496 dlist_tail_node(&leaf->segments));
497 if (!lastleftinfo->items)
498 lastleftinfo->items = ginPostingListDecode(lastleftinfo->seg,
499 &lastleftinfo->nitems);
500 maxOldItem = lastleftinfo->items[lastleftinfo->nitems - 1];
501 if (ginCompareItemPointers(&newItems[0], &maxOldItem) >= 0)
508 ItemPointerSetMin(&maxOldItem);
513 * If we're appending to the end of the page, we will append as many items
514 * as we can fit (after splitting), and stop when the pages becomes full.
515 * Otherwise we have to limit the number of new items to insert, because
516 * once we start packing we can't just stop when we run out of space,
517 * because we must make sure that all the old items still fit.
519 if (GinPageIsCompressed(page))
520 freespace = GinDataLeafPageGetFreeSpace(page);
526 * Even when appending, trying to append more items than will fit is
527 * not completely free, because we will merge the new items and old
528 * items into an array below. In the best case, every new item fits in
529 * a single byte, and we can use all the free space on the old page as
530 * well as the new page. For simplicity, ignore segment overhead etc.
532 maxitems = Min(maxitems, freespace + GinDataPageMaxDataSize);
537 * Calculate a conservative estimate of how many new items we can fit
538 * on the two pages after splitting.
540 * We can use any remaining free space on the old page to store full
541 * segments, as well as the new page. Each full-sized segment can hold
542 * at least MinTuplesPerSegment items
546 nnewsegments = freespace / GinPostingListSegmentMaxSize;
547 nnewsegments += GinDataPageMaxDataSize / GinPostingListSegmentMaxSize;
548 maxitems = Min(maxitems, nnewsegments * MinTuplesPerSegment);
551 /* Add the new items to the segments */
552 if (!addItemsToLeaf(leaf, newItems, maxitems))
554 /* all items were duplicates, we have nothing to do */
555 items->curitem += maxitems;
557 MemoryContextSwitchTo(oldCxt);
558 MemoryContextDelete(tmpCxt);
564 * Pack the items back to compressed segments, ready for writing to disk.
566 needsplit = leafRepackItems(leaf, &remaining);
569 * Did all the new items fit?
571 * If we're appending, it's OK if they didn't. But as a sanity check,
572 * verify that all the old items fit.
574 if (ItemPointerIsValid(&remaining))
576 if (!append || ItemPointerCompare(&maxOldItem, &remaining) >= 0)
577 elog(ERROR, "could not split GIN page; all old items didn't fit");
579 /* Count how many of the new items did fit. */
580 for (i = 0; i < maxitems; i++)
582 if (ginCompareItemPointers(&newItems[i], &remaining) >= 0)
586 elog(ERROR, "could not split GIN page; no new items fit");
593 * Great, all the items fit on a single page. Construct a WAL record
594 * describing the changes we made, and write the segments back to the
597 * Once we start modifying the page, there's no turning back. The
598 * caller is responsible for calling END_CRIT_SECTION() after writing
601 MemoryContextSwitchTo(oldCxt);
602 if (RelationNeedsWAL(btree->index))
603 registerLeafRecompressWALData(buf, leaf);
604 START_CRIT_SECTION();
605 dataPlaceToPageLeafRecompress(buf, leaf);
608 elog(DEBUG2, "appended %d new items to block %u; %d bytes (%d to go)",
609 maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize,
610 items->nitem - items->curitem - maxitems);
612 elog(DEBUG2, "inserted %d new items to block %u; %d bytes (%d to go)",
613 maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize,
614 items->nitem - items->curitem - maxitems);
621 * leafRepackItems already divided the segments between the left and
622 * the right page. It filled the left page as full as possible, and
623 * put the rest to the right page. When building a new index, that's
624 * good, because the table is scanned from beginning to end and there
625 * won't be any more insertions to the left page during the build.
626 * This packs the index as tight as possible. But otherwise, split
627 * 50/50, by moving segments from the left page to the right page
628 * until they're balanced.
630 * As a further heuristic, when appending items to the end of the
631 * page, try make the left page 75% full, one the assumption that
632 * subsequent insertions will probably also go to the end. This packs
633 * the index somewhat tighter when appending to a table, which is very
638 while (dlist_has_prev(&leaf->segments, leaf->lastleft))
640 lastleftinfo = dlist_container(leafSegmentInfo, node, leaf->lastleft);
642 /* ignore deleted segments */
643 if (lastleftinfo->action != GIN_SEGMENT_DELETE)
645 segsize = SizeOfGinPostingList(lastleftinfo->seg);
648 * Note that we check that the right page doesn't become
649 * more full than the left page even when appending. It's
650 * possible that we added enough items to make both pages
651 * more than 75% full.
653 if ((leaf->lsize - segsize) - (leaf->rsize + segsize) < 0)
657 if ((leaf->lsize - segsize) < (BLCKSZ * 3) / 4)
661 leaf->lsize -= segsize;
662 leaf->rsize += segsize;
664 leaf->lastleft = dlist_prev_node(&leaf->segments, leaf->lastleft);
667 Assert(leaf->lsize <= GinDataPageMaxDataSize);
668 Assert(leaf->rsize <= GinDataPageMaxDataSize);
671 * Fetch the max item in the left page's last segment; it becomes the
672 * right bound of the page.
674 lastleftinfo = dlist_container(leafSegmentInfo, node, leaf->lastleft);
675 if (!lastleftinfo->items)
676 lastleftinfo->items = ginPostingListDecode(lastleftinfo->seg,
677 &lastleftinfo->nitems);
678 lbound = lastleftinfo->items[lastleftinfo->nitems - 1];
680 *newlpage = MemoryContextAlloc(oldCxt, BLCKSZ);
681 *newrpage = MemoryContextAlloc(oldCxt, BLCKSZ);
683 dataPlaceToPageLeafSplit(buf, leaf, lbound, rbound,
684 *newlpage, *newrpage);
686 Assert(GinPageRightMost(page) ||
687 ginCompareItemPointers(GinDataPageGetRightBound(*newlpage),
688 GinDataPageGetRightBound(*newrpage)) < 0);
691 elog(DEBUG2, "appended %d items to block %u; split %d/%d (%d to go)",
692 maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize, (int) leaf->rsize,
693 items->nitem - items->curitem - maxitems);
695 elog(DEBUG2, "inserted %d items to block %u; split %d/%d (%d to go)",
696 maxitems, BufferGetBlockNumber(buf), (int) leaf->lsize, (int) leaf->rsize,
697 items->nitem - items->curitem - maxitems);
700 MemoryContextSwitchTo(oldCxt);
701 MemoryContextDelete(tmpCxt);
703 items->curitem += maxitems;
705 return needsplit ? SPLIT : INSERTED;
709 * Vacuum a posting tree leaf page.
712 ginVacuumPostingTreeLeaf(Relation indexrel, Buffer buffer, GinVacuumState *gvs)
714 Page page = BufferGetPage(buffer);
715 disassembledLeaf *leaf;
716 bool removedsomething = false;
719 leaf = disassembleLeaf(page);
721 /* Vacuum each segment. */
722 dlist_foreach(iter, &leaf->segments)
724 leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node, iter.cur);
730 seginfo->items = ginPostingListDecode(seginfo->seg,
733 oldsegsize = SizeOfGinPostingList(seginfo->seg);
735 oldsegsize = GinDataPageMaxDataSize;
737 cleaned = ginVacuumItemPointers(gvs,
741 pfree(seginfo->items);
742 seginfo->items = NULL;
750 seginfo->seg = ginCompressPostingList(cleaned,
754 /* Removing an item never increases the size of the segment */
755 if (npacked != ncleaned)
756 elog(ERROR, "could not fit vacuumed posting list");
757 seginfo->action = GIN_SEGMENT_REPLACE;
762 seginfo->items = NULL;
763 seginfo->action = GIN_SEGMENT_DELETE;
765 seginfo->nitems = ncleaned;
767 removedsomething = true;
772 * If we removed any items, reconstruct the page from the pieces.
774 * We don't try to re-encode the segments here, even though some of them
775 * might be really small now that we've removed some items from them. It
776 * seems like a waste of effort, as there isn't really any benefit from
777 * larger segments per se; larger segments only help to pack more items in
778 * the same space. We might as well delay doing that until the next
779 * insertion, which will need to re-encode at least part of the page
782 * Also note if the page was in uncompressed, pre-9.4 format before, it is
783 * now represented as one huge segment that contains all the items. It
784 * might make sense to split that, to speed up random access, but we don't
785 * bother. You'll have to REINDEX anyway if you want the full gain of the
786 * new tighter index format.
788 if (removedsomething)
793 * Make sure we have a palloc'd copy of all segments, after the first
794 * segment that is modified. (dataPlaceToPageLeafRecompress requires
798 dlist_foreach(iter, &leaf->segments)
800 leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
803 if (seginfo->action != GIN_SEGMENT_UNMODIFIED)
805 if (modified && seginfo->action != GIN_SEGMENT_DELETE)
807 int segsize = SizeOfGinPostingList(seginfo->seg);
808 GinPostingList *tmp = (GinPostingList *) palloc(segsize);
810 memcpy(tmp, seginfo->seg, segsize);
815 if (RelationNeedsWAL(indexrel))
818 registerLeafRecompressWALData(buffer, leaf);
820 START_CRIT_SECTION();
821 dataPlaceToPageLeafRecompress(buffer, leaf);
823 MarkBufferDirty(buffer);
825 if (RelationNeedsWAL(indexrel))
829 recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_VACUUM_DATA_LEAF_PAGE);
830 PageSetLSN(page, recptr);
838 * Construct a ginxlogRecompressDataLeaf record representing the changes
842 registerLeafRecompressWALData(Buffer buf, disassembledLeaf *leaf)
849 ginxlogRecompressDataLeaf *recompress_xlog;
851 /* Count the modified segments */
852 dlist_foreach(iter, &leaf->segments)
854 leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
857 if (seginfo->action != GIN_SEGMENT_UNMODIFIED)
862 palloc(sizeof(ginxlogRecompressDataLeaf) +
863 BLCKSZ + /* max size needed to hold the segment data */
864 nmodified * 2 /* (segno + action) per action */
866 walbufend = walbufbegin;
868 recompress_xlog = (ginxlogRecompressDataLeaf *) walbufend;
869 walbufend += sizeof(ginxlogRecompressDataLeaf);
871 recompress_xlog->nactions = nmodified;
874 dlist_foreach(iter, &leaf->segments)
876 leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
880 uint8 action = seginfo->action;
882 if (action == GIN_SEGMENT_UNMODIFIED)
888 if (action != GIN_SEGMENT_DELETE)
889 segsize = SizeOfGinPostingList(seginfo->seg);
892 * If storing the uncompressed list of added item pointers would take
893 * more space than storing the compressed segment as is, do that
896 if (action == GIN_SEGMENT_ADDITEMS &&
897 seginfo->nmodifieditems * sizeof(ItemPointerData) > segsize)
899 action = GIN_SEGMENT_REPLACE;
902 *((uint8 *) (walbufend++)) = segno;
903 *(walbufend++) = action;
907 case GIN_SEGMENT_DELETE:
911 case GIN_SEGMENT_ADDITEMS:
912 datalen = seginfo->nmodifieditems * sizeof(ItemPointerData);
913 memcpy(walbufend, &seginfo->nmodifieditems, sizeof(uint16));
914 memcpy(walbufend + sizeof(uint16), seginfo->modifieditems, datalen);
915 datalen += sizeof(uint16);
918 case GIN_SEGMENT_INSERT:
919 case GIN_SEGMENT_REPLACE:
920 datalen = SHORTALIGN(segsize);
921 memcpy(walbufend, seginfo->seg, segsize);
925 elog(ERROR, "unexpected GIN leaf action %d", action);
927 walbufend += datalen;
929 if (action != GIN_SEGMENT_INSERT)
934 XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
935 XLogRegisterBufData(0, walbufbegin, walbufend - walbufbegin);
940 * Assemble a disassembled posting tree leaf page back to a buffer.
942 * *prdata is filled with WAL information about this operation. The caller
943 * is responsible for inserting to the WAL, along with any other information
944 * about the operation that triggered this recompression.
946 * NOTE: The segment pointers must not point directly to the same buffer,
947 * except for segments that have not been modified and whose preceding
948 * segments have not been modified either.
951 dataPlaceToPageLeafRecompress(Buffer buf, disassembledLeaf *leaf)
953 Page page = BufferGetPage(buf);
956 bool modified = false;
961 * If the page was in pre-9.4 format before, convert the header, and force
962 * all segments to be copied to the page whether they were modified or
965 if (!GinPageIsCompressed(page))
967 Assert(leaf->oldformat);
968 GinPageSetCompressed(page);
969 GinPageGetOpaque(page)->maxoff = InvalidOffsetNumber;
973 ptr = (char *) GinDataLeafPageGetPostingList(page);
975 dlist_foreach(iter, &leaf->segments)
977 leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node, iter.cur);
979 if (seginfo->action != GIN_SEGMENT_UNMODIFIED)
982 if (seginfo->action != GIN_SEGMENT_DELETE)
984 segsize = SizeOfGinPostingList(seginfo->seg);
987 memcpy(ptr, seginfo->seg, segsize);
994 Assert(newsize <= GinDataPageMaxDataSize);
995 GinDataPageSetDataSize(page, newsize);
999 * Like dataPlaceToPageLeafRecompress, but writes the disassembled leaf
1000 * segments to two pages instead of one.
1002 * This is different from the non-split cases in that this does not modify
1003 * the original page directly, but to temporary in-memory copies of the new
1004 * left and right pages.
1007 dataPlaceToPageLeafSplit(Buffer buf, disassembledLeaf *leaf,
1008 ItemPointerData lbound, ItemPointerData rbound,
1009 Page lpage, Page rpage)
1016 dlist_node *firstright;
1017 leafSegmentInfo *seginfo;
1019 /* Initialize temporary pages to hold the new left and right pages */
1020 GinInitPage(lpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
1021 GinInitPage(rpage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
1024 * Copy the segments that go to the left page.
1026 * XXX: We should skip copying the unmodified part of the left page, like
1027 * we do when recompressing.
1030 ptr = (char *) GinDataLeafPageGetPostingList(lpage);
1031 firstright = dlist_next_node(&leaf->segments, leaf->lastleft);
1032 for (node = dlist_head_node(&leaf->segments);
1034 node = dlist_next_node(&leaf->segments, node))
1036 seginfo = dlist_container(leafSegmentInfo, node, node);
1038 if (seginfo->action != GIN_SEGMENT_DELETE)
1040 segsize = SizeOfGinPostingList(seginfo->seg);
1041 memcpy(ptr, seginfo->seg, segsize);
1046 Assert(lsize == leaf->lsize);
1047 GinDataPageSetDataSize(lpage, lsize);
1048 *GinDataPageGetRightBound(lpage) = lbound;
1050 /* Copy the segments that go to the right page */
1051 ptr = (char *) GinDataLeafPageGetPostingList(rpage);
1053 for (node = firstright;
1055 node = dlist_next_node(&leaf->segments, node))
1057 seginfo = dlist_container(leafSegmentInfo, node, node);
1059 if (seginfo->action != GIN_SEGMENT_DELETE)
1061 segsize = SizeOfGinPostingList(seginfo->seg);
1062 memcpy(ptr, seginfo->seg, segsize);
1067 if (!dlist_has_next(&leaf->segments, node))
1070 Assert(rsize == leaf->rsize);
1071 GinDataPageSetDataSize(rpage, rsize);
1072 *GinDataPageGetRightBound(rpage) = rbound;
1076 * Place a PostingItem to page, and fill a WAL record.
1078 * If the item doesn't fit, returns false without modifying the page.
1080 * In addition to inserting the given item, the downlink of the existing item
1081 * at 'off' is updated to point to 'updateblkno'.
1083 * On INSERTED, registers the buffer as buffer ID 0, with data.
1084 * On SPLIT, returns rdata that represents the split pages in *prdata.
1086 static GinPlaceToPageRC
1087 dataPlaceToPageInternal(GinBtree btree, Buffer buf, GinBtreeStack *stack,
1088 void *insertdata, BlockNumber updateblkno,
1089 Page *newlpage, Page *newrpage)
1091 Page page = BufferGetPage(buf);
1092 OffsetNumber off = stack->off;
1095 /* this must be static so it can be returned to caller */
1096 static ginxlogInsertDataInternal data;
1098 /* split if we have to */
1099 if (GinNonLeafDataPageGetFreeSpace(page) < sizeof(PostingItem))
1101 dataSplitPageInternal(btree, buf, stack, insertdata, updateblkno,
1102 newlpage, newrpage);
1106 Assert(GinPageIsData(page));
1108 START_CRIT_SECTION();
1110 /* Update existing downlink to point to next page (on internal page) */
1111 pitem = GinDataPageGetPostingItem(page, off);
1112 PostingItemSetBlockNumber(pitem, updateblkno);
1115 pitem = (PostingItem *) insertdata;
1116 GinDataPageAddPostingItem(page, pitem, off);
1118 if (RelationNeedsWAL(btree->index))
1121 data.newitem = *pitem;
1123 XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
1124 XLogRegisterBufData(0, (char *) &data,
1125 sizeof(ginxlogInsertDataInternal));
1132 * Places an item (or items) to a posting tree. Calls relevant function of
1133 * internal of leaf page because they are handled very differently.
1135 static GinPlaceToPageRC
1136 dataPlaceToPage(GinBtree btree, Buffer buf, GinBtreeStack *stack,
1137 void *insertdata, BlockNumber updateblkno,
1138 Page *newlpage, Page *newrpage)
1140 Page page = BufferGetPage(buf);
1142 Assert(GinPageIsData(page));
1144 if (GinPageIsLeaf(page))
1145 return dataPlaceToPageLeaf(btree, buf, stack, insertdata,
1146 newlpage, newrpage);
1148 return dataPlaceToPageInternal(btree, buf, stack,
1149 insertdata, updateblkno,
1150 newlpage, newrpage);
1154 * Split page and fill WAL record. Returns a new temp buffer filled with data
1155 * that should go to the left page. The original buffer is left untouched.
1158 dataSplitPageInternal(GinBtree btree, Buffer origbuf,
1159 GinBtreeStack *stack,
1160 void *insertdata, BlockNumber updateblkno,
1161 Page *newlpage, Page *newrpage)
1163 Page oldpage = BufferGetPage(origbuf);
1164 OffsetNumber off = stack->off;
1165 int nitems = GinPageGetOpaque(oldpage)->maxoff;
1168 Size pageSize = PageGetPageSize(oldpage);
1169 ItemPointerData oldbound = *GinDataPageGetRightBound(oldpage);
1173 OffsetNumber separator;
1174 PostingItem allitems[(BLCKSZ / sizeof(PostingItem)) + 1];
1176 lpage = PageGetTempPage(oldpage);
1177 rpage = PageGetTempPage(oldpage);
1178 GinInitPage(lpage, GinPageGetOpaque(oldpage)->flags, pageSize);
1179 GinInitPage(rpage, GinPageGetOpaque(oldpage)->flags, pageSize);
1182 * First construct a new list of PostingItems, which includes all the old
1183 * items, and the new item.
1185 memcpy(allitems, GinDataPageGetPostingItem(oldpage, FirstOffsetNumber),
1186 (off - 1) * sizeof(PostingItem));
1188 allitems[off - 1] = *((PostingItem *) insertdata);
1189 memcpy(&allitems[off], GinDataPageGetPostingItem(oldpage, off),
1190 (nitems - (off - 1)) * sizeof(PostingItem));
1193 /* Update existing downlink to point to next page */
1194 PostingItemSetBlockNumber(&allitems[off], updateblkno);
1197 * When creating a new index, fit as many tuples as possible on the left
1198 * page, on the assumption that the table is scanned from beginning to
1199 * end. This packs the index as tight as possible.
1201 if (btree->isBuild && GinPageRightMost(oldpage))
1202 separator = GinNonLeafDataPageGetFreeSpace(rpage) / sizeof(PostingItem);
1204 separator = nitems / 2;
1205 nleftitems = separator;
1206 nrightitems = nitems - separator;
1208 memcpy(GinDataPageGetPostingItem(lpage, FirstOffsetNumber),
1210 nleftitems * sizeof(PostingItem));
1211 GinPageGetOpaque(lpage)->maxoff = nleftitems;
1212 memcpy(GinDataPageGetPostingItem(rpage, FirstOffsetNumber),
1213 &allitems[separator],
1214 nrightitems * sizeof(PostingItem));
1215 GinPageGetOpaque(rpage)->maxoff = nrightitems;
1218 * Also set pd_lower for both pages, like GinDataPageAddPostingItem does.
1220 GinDataPageSetDataSize(lpage, nleftitems * sizeof(PostingItem));
1221 GinDataPageSetDataSize(rpage, nrightitems * sizeof(PostingItem));
1223 /* set up right bound for left page */
1224 bound = GinDataPageGetRightBound(lpage);
1225 *bound = GinDataPageGetPostingItem(lpage, nleftitems)->key;
1227 /* set up right bound for right page */
1228 *GinDataPageGetRightBound(rpage) = oldbound;
1235 * Construct insertion payload for inserting the downlink for given buffer.
1238 dataPrepareDownlink(GinBtree btree, Buffer lbuf)
1240 PostingItem *pitem = palloc(sizeof(PostingItem));
1241 Page lpage = BufferGetPage(lbuf);
1243 PostingItemSetBlockNumber(pitem, BufferGetBlockNumber(lbuf));
1244 pitem->key = *GinDataPageGetRightBound(lpage);
1250 * Fills new root by right bound values from child.
1251 * Also called from ginxlog, should not use btree
1254 ginDataFillRoot(GinBtree btree, Page root, BlockNumber lblkno, Page lpage, BlockNumber rblkno, Page rpage)
1259 li.key = *GinDataPageGetRightBound(lpage);
1260 PostingItemSetBlockNumber(&li, lblkno);
1261 GinDataPageAddPostingItem(root, &li, InvalidOffsetNumber);
1263 ri.key = *GinDataPageGetRightBound(rpage);
1264 PostingItemSetBlockNumber(&ri, rblkno);
1265 GinDataPageAddPostingItem(root, &ri, InvalidOffsetNumber);
1269 /*** Functions to work with disassembled leaf pages ***/
1272 * Disassemble page into a disassembledLeaf struct.
1274 static disassembledLeaf *
1275 disassembleLeaf(Page page)
1277 disassembledLeaf *leaf;
1278 GinPostingList *seg;
1282 leaf = palloc0(sizeof(disassembledLeaf));
1283 dlist_init(&leaf->segments);
1285 if (GinPageIsCompressed(page))
1288 * Create a leafSegment entry for each segment.
1290 seg = GinDataLeafPageGetPostingList(page);
1291 segbegin = (Pointer) seg;
1292 segend = segbegin + GinDataLeafPageGetPostingListSize(page);
1293 while ((Pointer) seg < segend)
1295 leafSegmentInfo *seginfo = palloc(sizeof(leafSegmentInfo));
1297 seginfo->action = GIN_SEGMENT_UNMODIFIED;
1299 seginfo->items = NULL;
1300 seginfo->nitems = 0;
1301 dlist_push_tail(&leaf->segments, &seginfo->node);
1303 seg = GinNextPostingListSegment(seg);
1305 leaf->oldformat = false;
1310 * A pre-9.4 format uncompressed page is represented by a single
1311 * segment, with an array of items.
1313 ItemPointer uncompressed;
1315 leafSegmentInfo *seginfo;
1317 uncompressed = dataLeafPageGetUncompressed(page, &nuncompressed);
1319 seginfo = palloc(sizeof(leafSegmentInfo));
1321 seginfo->action = GIN_SEGMENT_REPLACE;
1322 seginfo->seg = NULL;
1323 seginfo->items = palloc(nuncompressed * sizeof(ItemPointerData));
1324 memcpy(seginfo->items, uncompressed, nuncompressed * sizeof(ItemPointerData));
1325 seginfo->nitems = nuncompressed;
1327 dlist_push_tail(&leaf->segments, &seginfo->node);
1329 leaf->oldformat = true;
1336 * Distribute newItems to the segments.
1338 * Any segments that acquire new items are decoded, and the new items are
1339 * merged with the old items.
1341 * Returns true if any new items were added. False means they were all
1342 * duplicates of existing items on the page.
1345 addItemsToLeaf(disassembledLeaf *leaf, ItemPointer newItems, int nNewItems)
1348 ItemPointer nextnew = newItems;
1349 int newleft = nNewItems;
1350 bool modified = false;
1351 leafSegmentInfo *newseg;
1354 * If the page is completely empty, just construct one new segment to hold
1355 * all the new items.
1357 if (dlist_is_empty(&leaf->segments))
1359 newseg = palloc(sizeof(leafSegmentInfo));
1361 newseg->items = newItems;
1362 newseg->nitems = nNewItems;
1363 newseg->action = GIN_SEGMENT_INSERT;
1364 dlist_push_tail(&leaf->segments, &newseg->node);
1368 dlist_foreach(iter, &leaf->segments)
1370 leafSegmentInfo *cur = (leafSegmentInfo *) dlist_container(leafSegmentInfo, node, iter.cur);
1372 ItemPointer tmpitems;
1376 * How many of the new items fall into this segment?
1378 if (!dlist_has_next(&leaf->segments, iter.cur))
1382 leafSegmentInfo *next;
1383 ItemPointerData next_first;
1385 next = (leafSegmentInfo *) dlist_container(leafSegmentInfo, node,
1386 dlist_next_node(&leaf->segments, iter.cur));
1388 next_first = next->items[0];
1391 Assert(next->seg != NULL);
1392 next_first = next->seg->first;
1396 while (nthis < newleft && ginCompareItemPointers(&nextnew[nthis], &next_first) < 0)
1402 /* Merge the new items with the existing items. */
1404 cur->items = ginPostingListDecode(cur->seg, &cur->nitems);
1407 * Fast path for the important special case that we're appending to
1408 * the end of the page: don't let the last segment on the page grow
1409 * larger than the target, create a new segment before that happens.
1411 if (!dlist_has_next(&leaf->segments, iter.cur) &&
1412 ginCompareItemPointers(&cur->items[cur->nitems - 1], &nextnew[0]) < 0 &&
1414 SizeOfGinPostingList(cur->seg) >= GinPostingListSegmentTargetSize)
1416 newseg = palloc(sizeof(leafSegmentInfo));
1418 newseg->items = nextnew;
1419 newseg->nitems = nthis;
1420 newseg->action = GIN_SEGMENT_INSERT;
1421 dlist_push_tail(&leaf->segments, &newseg->node);
1426 tmpitems = ginMergeItemPointers(cur->items, cur->nitems,
1429 if (ntmpitems != cur->nitems)
1432 * If there are no duplicates, track the added items so that we
1433 * can emit a compact ADDITEMS WAL record later on. (it doesn't
1434 * seem worth re-checking which items were duplicates, if there
1437 if (ntmpitems == nthis + cur->nitems &&
1438 cur->action == GIN_SEGMENT_UNMODIFIED)
1440 cur->action = GIN_SEGMENT_ADDITEMS;
1441 cur->modifieditems = nextnew;
1442 cur->nmodifieditems = nthis;
1445 cur->action = GIN_SEGMENT_REPLACE;
1447 cur->items = tmpitems;
1448 cur->nitems = ntmpitems;
1463 * Recompresses all segments that have been modified.
1465 * If not all the items fit on two pages (ie. after split), we store as
1466 * many items as fit, and set *remaining to the first item that didn't fit.
1467 * If all items fit, *remaining is set to invalid.
1469 * Returns true if the page has to be split.
1472 leafRepackItems(disassembledLeaf *leaf, ItemPointer remaining)
1475 bool needsplit = false;
1478 leafSegmentInfo *nextseg;
1481 dlist_node *cur_node;
1482 dlist_node *next_node;
1484 ItemPointerSetInvalid(remaining);
1487 * cannot use dlist_foreach_modify here because we insert adjacent items
1490 for (cur_node = dlist_head_node(&leaf->segments);
1492 cur_node = next_node)
1494 leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
1497 if (dlist_has_next(&leaf->segments, cur_node))
1498 next_node = dlist_next_node(&leaf->segments, cur_node);
1502 /* Compress the posting list, if necessary */
1503 if (seginfo->action != GIN_SEGMENT_DELETE)
1505 if (seginfo->seg == NULL)
1507 if (seginfo->nitems > GinPostingListSegmentMaxSize)
1508 npacked = 0; /* no chance that it would fit. */
1511 seginfo->seg = ginCompressPostingList(seginfo->items,
1513 GinPostingListSegmentMaxSize,
1516 if (npacked != seginfo->nitems)
1519 * Too large. Compress again to the target size, and
1520 * create a new segment to represent the remaining items.
1521 * The new segment is inserted after this one, so it will
1522 * be processed in the next iteration of this loop.
1525 pfree(seginfo->seg);
1526 seginfo->seg = ginCompressPostingList(seginfo->items,
1528 GinPostingListSegmentTargetSize,
1530 if (seginfo->action != GIN_SEGMENT_INSERT)
1531 seginfo->action = GIN_SEGMENT_REPLACE;
1533 nextseg = palloc(sizeof(leafSegmentInfo));
1534 nextseg->action = GIN_SEGMENT_INSERT;
1535 nextseg->seg = NULL;
1536 nextseg->items = &seginfo->items[npacked];
1537 nextseg->nitems = seginfo->nitems - npacked;
1538 next_node = &nextseg->node;
1539 dlist_insert_after(cur_node, next_node);
1544 * If the segment is very small, merge it with the next segment.
1546 if (SizeOfGinPostingList(seginfo->seg) < GinPostingListSegmentMinSize && next_node)
1550 nextseg = dlist_container(leafSegmentInfo, node, next_node);
1552 if (seginfo->items == NULL)
1553 seginfo->items = ginPostingListDecode(seginfo->seg,
1555 if (nextseg->items == NULL)
1556 nextseg->items = ginPostingListDecode(nextseg->seg,
1559 ginMergeItemPointers(seginfo->items, seginfo->nitems,
1560 nextseg->items, nextseg->nitems,
1562 Assert(nmerged == seginfo->nitems + nextseg->nitems);
1563 nextseg->nitems = nmerged;
1564 nextseg->seg = NULL;
1566 nextseg->action = GIN_SEGMENT_REPLACE;
1567 nextseg->modifieditems = NULL;
1568 nextseg->nmodifieditems = 0;
1570 if (seginfo->action == GIN_SEGMENT_INSERT)
1572 dlist_delete(cur_node);
1577 seginfo->action = GIN_SEGMENT_DELETE;
1578 seginfo->seg = NULL;
1582 seginfo->items = NULL;
1583 seginfo->nitems = 0;
1586 if (seginfo->action == GIN_SEGMENT_DELETE)
1590 * OK, we now have a compressed version of this segment ready for
1591 * copying to the page. Did we exceed the size that fits on one page?
1593 segsize = SizeOfGinPostingList(seginfo->seg);
1594 if (pgused + segsize > GinDataPageMaxDataSize)
1598 /* switch to right page */
1600 leaf->lastleft = dlist_prev_node(&leaf->segments, cur_node);
1602 leaf->lsize = pgused;
1608 * Filled both pages. The last segment we constructed did not
1611 *remaining = seginfo->seg->first;
1614 * remove all segments that did not fit from the list.
1616 while (dlist_has_next(&leaf->segments, cur_node))
1617 dlist_delete(dlist_next_node(&leaf->segments, cur_node));
1618 dlist_delete(cur_node);
1628 leaf->lsize = pgused;
1632 leaf->rsize = pgused;
1634 Assert(leaf->lsize <= GinDataPageMaxDataSize);
1635 Assert(leaf->rsize <= GinDataPageMaxDataSize);
1638 * Make a palloc'd copy of every segment after the first modified one,
1639 * because as we start copying items to the original page, we might
1640 * overwrite an existing segment.
1643 dlist_foreach(iter, &leaf->segments)
1645 leafSegmentInfo *seginfo = dlist_container(leafSegmentInfo, node,
1648 if (!modified && seginfo->action != GIN_SEGMENT_UNMODIFIED)
1652 else if (modified && seginfo->action == GIN_SEGMENT_UNMODIFIED)
1654 GinPostingList *tmp;
1656 segsize = SizeOfGinPostingList(seginfo->seg);
1657 tmp = palloc(segsize);
1658 memcpy(tmp, seginfo->seg, segsize);
1667 /*** Functions that are exported to the rest of the GIN code ***/
1670 * Creates new posting tree containing the given TIDs. Returns the page
1671 * number of the root of the new posting tree.
1673 * items[] must be in sorted order with no duplicates.
1676 createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
1677 GinStatsData *buildStats)
1687 /* Construct the new root page in memory first. */
1688 tmppage = (Page) palloc(BLCKSZ);
1689 GinInitPage(tmppage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
1690 GinPageGetOpaque(tmppage)->rightlink = InvalidBlockNumber;
1693 * Write as many of the items to the root page as fit. In segments of max
1694 * GinPostingListSegmentMaxSize bytes each.
1698 ptr = (Pointer) GinDataLeafPageGetPostingList(tmppage);
1699 while (nrootitems < nitems)
1701 GinPostingList *segment;
1705 segment = ginCompressPostingList(&items[nrootitems],
1706 nitems - nrootitems,
1707 GinPostingListSegmentMaxSize,
1709 segsize = SizeOfGinPostingList(segment);
1710 if (rootsize + segsize > GinDataPageMaxDataSize)
1713 memcpy(ptr, segment, segsize);
1715 rootsize += segsize;
1716 nrootitems += npacked;
1719 GinDataPageSetDataSize(tmppage, rootsize);
1722 * All set. Get a new physical page, and copy the in-memory page to it.
1724 buffer = GinNewBuffer(index);
1725 page = BufferGetPage(buffer);
1726 blkno = BufferGetBlockNumber(buffer);
1728 START_CRIT_SECTION();
1730 PageRestoreTempPage(tmppage, page);
1731 MarkBufferDirty(buffer);
1733 if (RelationNeedsWAL(index))
1736 ginxlogCreatePostingTree data;
1738 data.size = rootsize;
1741 XLogRegisterData((char *) &data, sizeof(ginxlogCreatePostingTree));
1743 XLogRegisterData((char *) GinDataLeafPageGetPostingList(page),
1745 XLogRegisterBuffer(0, buffer, REGBUF_WILL_INIT);
1747 recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_PTREE);
1748 PageSetLSN(page, recptr);
1751 UnlockReleaseBuffer(buffer);
1755 /* During index build, count the newly-added data page */
1757 buildStats->nDataPages++;
1759 elog(DEBUG2, "created GIN posting tree with %d items", nrootitems);
1762 * Add any remaining TIDs to the newly-created posting tree.
1764 if (nitems > nrootitems)
1766 ginInsertItemPointers(index, blkno,
1768 nitems - nrootitems,
1776 ginPrepareDataScan(GinBtree btree, Relation index, BlockNumber rootBlkno)
1778 memset(btree, 0, sizeof(GinBtreeData));
1780 btree->index = index;
1781 btree->rootBlkno = rootBlkno;
1783 btree->findChildPage = dataLocateItem;
1784 btree->getLeftMostChild = dataGetLeftMostPage;
1785 btree->isMoveRight = dataIsMoveRight;
1786 btree->findItem = NULL;
1787 btree->findChildPtr = dataFindChildPtr;
1788 btree->placeToPage = dataPlaceToPage;
1789 btree->fillRoot = ginDataFillRoot;
1790 btree->prepareDownlink = dataPrepareDownlink;
1792 btree->isData = TRUE;
1793 btree->fullScan = FALSE;
1794 btree->isBuild = FALSE;
1798 * Inserts array of item pointers, may execute several tree scan (very rare)
1801 ginInsertItemPointers(Relation index, BlockNumber rootBlkno,
1802 ItemPointerData *items, uint32 nitem,
1803 GinStatsData *buildStats)
1806 GinBtreeDataLeafInsertData insertdata;
1807 GinBtreeStack *stack;
1809 ginPrepareDataScan(&btree, index, rootBlkno);
1810 btree.isBuild = (buildStats != NULL);
1811 insertdata.items = items;
1812 insertdata.nitem = nitem;
1813 insertdata.curitem = 0;
1815 while (insertdata.curitem < insertdata.nitem)
1817 /* search for the leaf page where the first item should go to */
1818 btree.itemptr = insertdata.items[insertdata.curitem];
1819 stack = ginFindLeafPage(&btree, false);
1821 ginInsertValue(&btree, stack, &insertdata, buildStats);
1826 * Starts a new scan on a posting tree.
1829 ginScanBeginPostingTree(GinBtree btree, Relation index, BlockNumber rootBlkno)
1831 GinBtreeStack *stack;
1833 ginPrepareDataScan(btree, index, rootBlkno);
1835 btree->fullScan = TRUE;
1837 stack = ginFindLeafPage(btree, TRUE);