/* * add ItemPointer to a leaf page. */ void GinDataPageAddItemPointer(Page page, ItemPointer data, OffsetNumber offset) { OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff; char *ptr; Assert(ItemPointerIsValid(data)); Assert(GinPageIsLeaf(page)); if (offset == InvalidOffsetNumber) { ptr = (char *) GinDataPageGetItemPointer(page, maxoff + 1); } else { ptr = (char *) GinDataPageGetItemPointer(page, offset); if (maxoff + 1 - offset != 0) memmove(ptr + sizeof(ItemPointerData), ptr, (maxoff - offset + 1) * sizeof(ItemPointerData)); } memcpy(ptr, data, sizeof(ItemPointerData)); GinPageGetOpaque(page)->maxoff++; }
/* * Searches correct position for value on leaf page. * Page should be correctly chosen. * Returns true if value found on page. */ static bool dataLocateLeafItem(GinBtree btree, GinBtreeStack *stack) { Page page = BufferGetPage(stack->buffer); OffsetNumber low, high; int result; Assert(GinPageIsLeaf(page)); Assert(GinPageIsData(page)); if (btree->fullScan) { stack->off = FirstOffsetNumber; return TRUE; } low = FirstOffsetNumber; high = GinPageGetOpaque(page)->maxoff; if (high < low) { stack->off = FirstOffsetNumber; return false; } high++; while (high > low) { OffsetNumber mid = low + ((high - low) / 2); result = ginCompareItemPointers(&btree->itemptr, GinDataPageGetItemPointer(page, mid)); if (result == 0) { stack->off = mid; return true; } else if (result > 0) low = mid + 1; else high = mid; } stack->off = high; return false; }
/* * split page and fills WAL record. original buffer(lbuf) leaves untouched, * returns shadow page of lbuf filled new data. In leaf page and build mode puts all * ItemPointers to pages. Also, in build mode splits data by way to full fulled * left page */ static Page dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, void *insertdata, BlockNumber updateblkno, XLogRecData **prdata) { char *ptr; OffsetNumber separator; ItemPointer bound; Page lpage = PageGetTempPageCopy(BufferGetPage(lbuf)); bool isleaf = GinPageIsLeaf(lpage); ItemPointerData oldbound = *GinDataPageGetRightBound(lpage); int sizeofitem = GinSizeOfDataPageItem(lpage); OffsetNumber maxoff = GinPageGetOpaque(lpage)->maxoff; Page rpage = BufferGetPage(rbuf); Size pageSize = PageGetPageSize(lpage); Size freeSpace; /* these must be static so they can be returned to caller */ static ginxlogSplitData data; static XLogRecData rdata[2]; static char vector[2 * BLCKSZ]; GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize); freeSpace = GinDataPageGetFreeSpace(rpage); *prdata = rdata; /* Update existing downlink to point to next page (on internal page) */ if (!isleaf) { PostingItem *pitem = GinDataPageGetPostingItem(lpage, off); PostingItemSetBlockNumber(pitem, updateblkno); } if (isleaf) { memcpy(vector, GinDataPageGetItemPointer(lpage, FirstOffsetNumber), maxoff * sizeof(ItemPointerData)); } else { memcpy(vector, GinDataPageGetPostingItem(lpage, FirstOffsetNumber), maxoff * sizeof(PostingItem)); } if (isleaf && GinPageRightMost(lpage) && off > GinPageGetOpaque(lpage)->maxoff) { /* append new items to the end */ GinBtreeDataLeafInsertData *items = insertdata; while (items->curitem < items->nitem && maxoff * sizeof(ItemPointerData) < 2 * (freeSpace - sizeof(ItemPointerData))) { memcpy(vector + maxoff * sizeof(ItemPointerData), items->items + items->curitem, sizeof(ItemPointerData)); maxoff++; items->curitem++; } } else { ptr = vector + (off - 1) * sizeofitem; if (maxoff + 1 - off != 0) memmove(ptr + sizeofitem, ptr, (maxoff - off + 1) * sizeofitem); if (isleaf) { GinBtreeDataLeafInsertData *items = insertdata; memcpy(ptr, items->items + items->curitem, sizeofitem); items->curitem++; } else { PostingItem *pitem = insertdata; memcpy(ptr, pitem, sizeofitem); } maxoff++; } /* * we assume that during index creation the table scanned from beginning * to end, so ItemPointers are in monotonically increasing order. */ if (btree->isBuild && GinPageRightMost(lpage)) separator = freeSpace / sizeofitem; else separator = maxoff / 2; GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize); GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize); if (isleaf) memcpy(GinDataPageGetItemPointer(lpage, FirstOffsetNumber), vector, separator * sizeof(ItemPointerData)); else memcpy(GinDataPageGetPostingItem(lpage, FirstOffsetNumber), vector, separator * sizeof(PostingItem)); GinPageGetOpaque(lpage)->maxoff = separator; if (isleaf) memcpy(GinDataPageGetItemPointer(rpage, FirstOffsetNumber), vector + separator * sizeof(ItemPointerData), (maxoff - separator) * sizeof(ItemPointerData)); else memcpy(GinDataPageGetPostingItem(rpage, FirstOffsetNumber), vector + separator * sizeof(PostingItem), (maxoff - separator) * sizeof(PostingItem)); GinPageGetOpaque(rpage)->maxoff = maxoff - separator; /* set up right bound for left page */ bound = GinDataPageGetRightBound(lpage); if (GinPageIsLeaf(lpage)) *bound = *GinDataPageGetItemPointer(lpage, GinPageGetOpaque(lpage)->maxoff); else *bound = GinDataPageGetPostingItem(lpage, GinPageGetOpaque(lpage)->maxoff)->key; /* set up right bound for right page */ bound = GinDataPageGetRightBound(rpage); *bound = oldbound; data.separator = separator; data.nitem = maxoff; data.rightbound = oldbound; rdata[0].buffer = InvalidBuffer; rdata[0].data = (char *) &data; rdata[0].len = sizeof(ginxlogSplitData); rdata[0].next = &rdata[1]; rdata[1].buffer = InvalidBuffer; rdata[1].data = vector; rdata[1].len = maxoff * sizeofitem; rdata[1].next = NULL; return lpage; }
/* * split page and fills WAL record. original buffer(lbuf) leaves untouched, * returns shadow page of lbuf filled new data. In leaf page and build mode puts all * ItemPointers to pages. Also, in build mode splits data by way to full fulled * left page */ static Page dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRecData **prdata) { char *ptr; OffsetNumber separator; ItemPointer bound; Page lpage = PageGetTempPageCopy(BufferGetPage(lbuf)); bool isleaf = GinPageIsLeaf(lpage); ItemPointerData oldbound = *GinDataPageGetRightBound(lpage); int sizeofitem = GinSizeOfDataPageItem(lpage); OffsetNumber maxoff = GinPageGetOpaque(lpage)->maxoff; Page rpage = BufferGetPage(rbuf); Size pageSize = PageGetPageSize(lpage); Size freeSpace; uint32 nCopied = 1; /* these must be static so they can be returned to caller */ static ginxlogSplit data; static XLogRecData rdata[4]; static char vector[2 * BLCKSZ]; GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize); freeSpace = GinDataPageGetFreeSpace(rpage); *prdata = rdata; data.leftChildBlkno = (GinPageIsLeaf(lpage)) ? InvalidOffsetNumber : PostingItemGetBlockNumber(&(btree->pitem)); data.updateBlkno = dataPrepareData(btree, lpage, off); if (isleaf) { memcpy(vector, GinDataPageGetItemPointer(lpage, FirstOffsetNumber), maxoff * sizeof(ItemPointerData)); } else { memcpy(vector, GinDataPageGetPostingItem(lpage, FirstOffsetNumber), maxoff * sizeof(PostingItem)); } if (isleaf && GinPageRightMost(lpage) && off > GinPageGetOpaque(lpage)->maxoff) { nCopied = 0; while (btree->curitem < btree->nitem && maxoff * sizeof(ItemPointerData) < 2 * (freeSpace - sizeof(ItemPointerData))) { memcpy(vector + maxoff * sizeof(ItemPointerData), btree->items + btree->curitem, sizeof(ItemPointerData)); maxoff++; nCopied++; btree->curitem++; } } else { ptr = vector + (off - 1) * sizeofitem; if (maxoff + 1 - off != 0) memmove(ptr + sizeofitem, ptr, (maxoff - off + 1) * sizeofitem); if (isleaf) { memcpy(ptr, btree->items + btree->curitem, sizeofitem); btree->curitem++; } else memcpy(ptr, &(btree->pitem), sizeofitem); maxoff++; } /* * we suppose that during index creation table scaned from begin to end, * so ItemPointers are monotonically increased.. */ if (btree->isBuild && GinPageRightMost(lpage)) separator = freeSpace / sizeofitem; else separator = maxoff / 2; GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize); GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize); if (isleaf) memcpy(GinDataPageGetItemPointer(lpage, FirstOffsetNumber), vector, separator * sizeof(ItemPointerData)); else memcpy(GinDataPageGetPostingItem(lpage, FirstOffsetNumber), vector, separator * sizeof(PostingItem)); GinPageGetOpaque(lpage)->maxoff = separator; if (isleaf) memcpy(GinDataPageGetItemPointer(rpage, FirstOffsetNumber), vector + separator * sizeof(ItemPointerData), (maxoff - separator) * sizeof(ItemPointerData)); else memcpy(GinDataPageGetPostingItem(rpage, FirstOffsetNumber), vector + separator * sizeof(PostingItem), (maxoff - separator) * sizeof(PostingItem)); GinPageGetOpaque(rpage)->maxoff = maxoff - separator; PostingItemSetBlockNumber(&(btree->pitem), BufferGetBlockNumber(lbuf)); if (GinPageIsLeaf(lpage)) btree->pitem.key = *GinDataPageGetItemPointer(lpage, GinPageGetOpaque(lpage)->maxoff); else btree->pitem.key = GinDataPageGetPostingItem(lpage, GinPageGetOpaque(lpage)->maxoff)->key; btree->rightblkno = BufferGetBlockNumber(rbuf); /* set up right bound for left page */ bound = GinDataPageGetRightBound(lpage); *bound = btree->pitem.key; /* set up right bound for right page */ bound = GinDataPageGetRightBound(rpage); *bound = oldbound; data.node = btree->index->rd_node; data.rootBlkno = InvalidBlockNumber; data.lblkno = BufferGetBlockNumber(lbuf); data.rblkno = BufferGetBlockNumber(rbuf); data.separator = separator; data.nitem = maxoff; data.isData = TRUE; data.isLeaf = GinPageIsLeaf(lpage) ? TRUE : FALSE; data.isRootSplit = FALSE; data.rightbound = oldbound; rdata[0].buffer = InvalidBuffer; rdata[0].data = (char *) &data; rdata[0].len = sizeof(ginxlogSplit); rdata[0].next = &rdata[1]; rdata[1].buffer = InvalidBuffer; rdata[1].data = vector; rdata[1].len = MAXALIGN(maxoff * sizeofitem); rdata[1].next = NULL; return lpage; }