/* * Fills new root by right bound values from child. * Also called from ginxlog, should not use btree */ void ginDataFillRoot(GinBtree btree, Page root, BlockNumber lblkno, Page lpage, BlockNumber rblkno, Page rpage) { PostingItem li, ri; li.key = *GinDataPageGetRightBound(lpage); PostingItemSetBlockNumber(&li, lblkno); GinDataPageAddPostingItem(root, &li, InvalidOffsetNumber); ri.key = *GinDataPageGetRightBound(rpage); PostingItemSetBlockNumber(&ri, rblkno); GinDataPageAddPostingItem(root, &ri, InvalidOffsetNumber); }
static void ginRedoInsertData(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rdata) { Page page = BufferGetPage(buffer); if (isLeaf) { ginxlogRecompressDataLeaf *data = (ginxlogRecompressDataLeaf *) rdata; Assert(GinPageIsLeaf(page)); ginRedoRecompress(page, data); } else { ginxlogInsertDataInternal *data = (ginxlogInsertDataInternal *) rdata; PostingItem *oldpitem; Assert(!GinPageIsLeaf(page)); /* update link to right page after split */ oldpitem = GinDataPageGetPostingItem(page, data->offset); PostingItemSetBlockNumber(oldpitem, rightblkno); GinDataPageAddPostingItem(page, &data->newitem, data->offset); } }
/* * Fills new root by right bound values from child. * Also called from ginxlog, should not use btree */ void ginDataFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf) { Page page = BufferGetPage(root), lpage = BufferGetPage(lbuf), rpage = BufferGetPage(rbuf); PostingItem li, ri; li.key = *GinDataPageGetRightBound(lpage); PostingItemSetBlockNumber(&li, BufferGetBlockNumber(lbuf)); GinDataPageAddItem(page, &li, InvalidOffsetNumber); ri.key = *GinDataPageGetRightBound(rpage); PostingItemSetBlockNumber(&ri, BufferGetBlockNumber(rbuf)); GinDataPageAddItem(page, &ri, InvalidOffsetNumber); }
/* * Construct insertion payload for inserting the downlink for given buffer. */ static void * dataPrepareDownlink(GinBtree btree, Buffer lbuf) { PostingItem *pitem = palloc(sizeof(PostingItem)); Page lpage = BufferGetPage(lbuf); PostingItemSetBlockNumber(pitem, BufferGetBlockNumber(lbuf)); pitem->key = *GinDataPageGetRightBound(lpage); return pitem; }
/* * In case of previous split update old child blkno to * new right page * item pointer never deletes! */ static BlockNumber dataPrepareData(GinBtree btree, Page page, OffsetNumber off) { BlockNumber ret = InvalidBlockNumber; Assert(GinPageIsData(page)); if (!GinPageIsLeaf(page) && btree->rightblkno != InvalidBlockNumber) { PostingItem *pitem = (PostingItem *) GinDataPageGetItem(page, off); PostingItemSetBlockNumber(pitem, btree->rightblkno); ret = btree->rightblkno; } btree->rightblkno = InvalidBlockNumber; return ret; }
/* * 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)); 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); memcpy(vector, GinDataPageGetItem(lpage, FirstOffsetNumber), maxoff * sizeofitem); if (GinPageIsLeaf(lpage) && 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 (GinPageIsLeaf(lpage)) { 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); memcpy(GinDataPageGetItem(lpage, FirstOffsetNumber), vector, separator * sizeofitem); GinPageGetOpaque(lpage)->maxoff = separator; memcpy(GinDataPageGetItem(rpage, FirstOffsetNumber), vector + separator * sizeofitem, (maxoff - separator) * sizeofitem); GinPageGetOpaque(rpage)->maxoff = maxoff - separator; PostingItemSetBlockNumber(&(btree->pitem), BufferGetBlockNumber(lbuf)); if (GinPageIsLeaf(lpage)) btree->pitem.key = *(ItemPointerData *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff); else btree->pitem.key = ((PostingItem *) GinDataPageGetItem(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; }
/* * 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; }
/* * Places keys to page and fills WAL record. In case leaf page and * build mode puts all ItemPointers to page. * * If none of the keys fit, returns false without modifying the page. * * On insertion to an internal node, in addition to inserting the given item, * the downlink of the existing item at 'off' is updated to point to * 'updateblkno'. */ static bool dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, void *insertdata, BlockNumber updateblkno, XLogRecData **prdata) { Page page = BufferGetPage(buf); /* these must be static so they can be returned to caller */ static XLogRecData rdata[2]; /* quick exit if it doesn't fit */ if (!dataIsEnoughSpace(btree, buf, off, insertdata)) return false; *prdata = rdata; Assert(GinPageIsData(page)); /* Update existing downlink to point to next page (on internal page) */ if (!GinPageIsLeaf(page)) { PostingItem *pitem = GinDataPageGetPostingItem(page, off); PostingItemSetBlockNumber(pitem, updateblkno); } if (GinPageIsLeaf(page)) { GinBtreeDataLeafInsertData *items = insertdata; static ginxlogInsertDataLeaf data; uint32 savedPos = items->curitem; if (GinPageRightMost(page) && off > GinPageGetOpaque(page)->maxoff) { /* usually, create index... */ while (items->curitem < items->nitem) { GinDataPageAddItemPointer(page, items->items + items->curitem, off); off++; items->curitem++; } data.nitem = items->curitem - savedPos; } else { GinDataPageAddItemPointer(page, items->items + items->curitem, off); items->curitem++; data.nitem = 1; } rdata[0].buffer = buf; rdata[0].buffer_std = false; rdata[0].data = (char *) &data; rdata[0].len = offsetof(ginxlogInsertDataLeaf, items); rdata[0].next = &rdata[1]; rdata[1].buffer = buf; rdata[1].buffer_std = false; rdata[1].data = (char *) &items->items[savedPos]; rdata[1].len = sizeof(ItemPointerData) * data.nitem; rdata[1].next = NULL; } else { PostingItem *pitem = insertdata; GinDataPageAddPostingItem(page, pitem, off); rdata[0].buffer = buf; rdata[0].buffer_std = false; rdata[0].data = (char *) pitem; rdata[0].len = sizeof(PostingItem); rdata[0].next = NULL; } return true; }