Commit ecaa4708 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Misc GIN refactoring.

Merge the isEnoughSpace and placeToPage functions in the b-tree interface
into one function that tries to put a tuple on page, and returns false if
it doesn't fit.

Move createPostingTree function to gindatapage.c, and change its contract
so that it can be passed more items than fit on the root page. It's in a
better position than the callers to know how many items fit.

Move ginMergeItemPointers out of gindatapage.c, into a separate file.

These changes make no difference now, but reduce the footprint of Alexander
Korotkov's upcoming patch to pack item pointers more tightly.
parent 920c8261
...@@ -14,6 +14,6 @@ include $(top_builddir)/src/Makefile.global ...@@ -14,6 +14,6 @@ include $(top_builddir)/src/Makefile.global
OBJS = ginutil.o gininsert.o ginxlog.o ginentrypage.o gindatapage.o \ OBJS = ginutil.o gininsert.o ginxlog.o ginentrypage.o gindatapage.o \
ginbtree.o ginscan.o ginget.o ginvacuum.o ginarrayproc.o \ ginbtree.o ginscan.o ginget.o ginvacuum.o ginarrayproc.o \
ginbulk.o ginfast.o ginbulk.o ginfast.o ginpostinglist.o
include $(top_srcdir)/src/backend/common.mk include $(top_srcdir)/src/backend/common.mk
...@@ -104,7 +104,7 @@ a few thousand entries can be much faster than retail insertion. (The win ...@@ -104,7 +104,7 @@ a few thousand entries can be much faster than retail insertion. (The win
comes mainly from not having to do multiple searches/insertions when the comes mainly from not having to do multiple searches/insertions when the
same key appears in multiple new heap tuples.) same key appears in multiple new heap tuples.)
Key entries are nominally of the same IndexEntry format as used in other Key entries are nominally of the same IndexTuple format as used in other
index types, but since a leaf key entry typically refers to multiple heap index types, but since a leaf key entry typically refers to multiple heap
tuples, there are significant differences. (See GinFormTuple, which works tuples, there are significant differences. (See GinFormTuple, which works
by building a "normal" index tuple and then modifying it.) The points to by building a "normal" index tuple and then modifying it.) The points to
......
...@@ -264,7 +264,7 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack, ...@@ -264,7 +264,7 @@ ginFindParents(GinBtree btree, GinBtreeStack *stack,
* Insert value (stored in GinBtree) to tree described by stack * Insert value (stored in GinBtree) to tree described by stack
* *
* During an index build, buildStats is non-null and the counters * During an index build, buildStats is non-null and the counters
* it contains should be incremented as needed. * it contains are incremented as needed.
* *
* NB: the passed-in stack is freed, as though by freeGinBtreeStack. * NB: the passed-in stack is freed, as though by freeGinBtreeStack.
*/ */
...@@ -290,15 +290,15 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats) ...@@ -290,15 +290,15 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats)
{ {
XLogRecData *rdata; XLogRecData *rdata;
BlockNumber savedRightLink; BlockNumber savedRightLink;
bool fit;
page = BufferGetPage(stack->buffer); page = BufferGetPage(stack->buffer);
savedRightLink = GinPageGetOpaque(page)->rightlink; savedRightLink = GinPageGetOpaque(page)->rightlink;
if (btree->isEnoughSpace(btree, stack->buffer, stack->off)) START_CRIT_SECTION();
fit = btree->placeToPage(btree, stack->buffer, stack->off, &rdata);
if (fit)
{ {
START_CRIT_SECTION();
btree->placeToPage(btree, stack->buffer, stack->off, &rdata);
MarkBufferDirty(stack->buffer); MarkBufferDirty(stack->buffer);
if (RelationNeedsWAL(btree->index)) if (RelationNeedsWAL(btree->index))
...@@ -318,12 +318,17 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats) ...@@ -318,12 +318,17 @@ ginInsertValue(GinBtree btree, GinBtreeStack *stack, GinStatsData *buildStats)
} }
else else
{ {
Buffer rbuffer = GinNewBuffer(btree->index); /* Didn't fit, have to split */
Buffer rbuffer;
Page newlpage; Page newlpage;
END_CRIT_SECTION();
rbuffer = GinNewBuffer(btree->index);
/* /*
* newlpage is a pointer to memory page, it doesn't associate with * newlpage is a pointer to memory page, it is not associated with
* buffer, stack->buffer should be untouched * a buffer. stack->buffer is not touched yet.
*/ */
newlpage = btree->splitPage(btree, stack->buffer, rbuffer, stack->off, &rdata); newlpage = btree->splitPage(btree, stack->buffer, rbuffer, stack->off, &rdata);
......
...@@ -15,47 +15,9 @@ ...@@ -15,47 +15,9 @@
#include "postgres.h" #include "postgres.h"
#include "access/gin_private.h" #include "access/gin_private.h"
#include "miscadmin.h"
#include "utils/rel.h" #include "utils/rel.h"
/*
* Merge two ordered arrays of itempointers, eliminating any duplicates.
* Returns the number of items in the result.
* Caller is responsible that there is enough space at *dst.
*/
uint32
ginMergeItemPointers(ItemPointerData *dst,
ItemPointerData *a, uint32 na,
ItemPointerData *b, uint32 nb)
{
ItemPointerData *dptr = dst;
ItemPointerData *aptr = a,
*bptr = b;
while (aptr - a < na && bptr - b < nb)
{
int cmp = ginCompareItemPointers(aptr, bptr);
if (cmp > 0)
*dptr++ = *bptr++;
else if (cmp == 0)
{
/* we want only one copy of the identical items */
*dptr++ = *bptr++;
aptr++;
}
else
*dptr++ = *aptr++;
}
while (aptr - a < na)
*dptr++ = *aptr++;
while (bptr - b < nb)
*dptr++ = *bptr++;
return dptr - dst;
}
/* /*
* Checks, should we move to right link... * Checks, should we move to right link...
* Compares inserting itemp pointer with right bound of current page * Compares inserting itemp pointer with right bound of current page
...@@ -387,9 +349,12 @@ dataPrepareData(GinBtree btree, Page page, OffsetNumber off) ...@@ -387,9 +349,12 @@ dataPrepareData(GinBtree btree, Page page, OffsetNumber off)
/* /*
* Places keys to page and fills WAL record. In case leaf page and * Places keys to page and fills WAL record. In case leaf page and
* build mode puts all ItemPointers to page. * build mode puts all ItemPointers to page.
*
* If none of the keys fit, returns false without modifying the page.
*/ */
static void static bool
dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata) dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off,
XLogRecData **prdata)
{ {
Page page = BufferGetPage(buf); Page page = BufferGetPage(buf);
int sizeofitem = GinSizeOfDataPageItem(page); int sizeofitem = GinSizeOfDataPageItem(page);
...@@ -399,6 +364,10 @@ dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prda ...@@ -399,6 +364,10 @@ dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prda
static XLogRecData rdata[3]; static XLogRecData rdata[3];
static ginxlogInsert data; static ginxlogInsert data;
/* quick exit if it doesn't fit */
if (!dataIsEnoughSpace(btree, buf, off))
return false;
*prdata = rdata; *prdata = rdata;
Assert(GinPageIsData(page)); Assert(GinPageIsData(page));
...@@ -464,6 +433,8 @@ dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prda ...@@ -464,6 +433,8 @@ dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prda
} }
else else
GinDataPageAddPostingItem(page, &(btree->pitem), off); GinDataPageAddPostingItem(page, &(btree->pitem), off);
return true;
} }
/* /*
...@@ -545,8 +516,8 @@ dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe ...@@ -545,8 +516,8 @@ dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe
} }
/* /*
* we suppose that during index creation table scaned from begin to end, * we assume that during index creation the table scanned from beginning
* so ItemPointers are monotonically increased.. * to end, so ItemPointers are in monotonically increasing order.
*/ */
if (btree->isBuild && GinPageRightMost(lpage)) if (btree->isBuild && GinPageRightMost(lpage))
separator = freeSpace / sizeofitem; separator = freeSpace / sizeofitem;
...@@ -575,15 +546,6 @@ dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe ...@@ -575,15 +546,6 @@ dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe
GinPageGetOpaque(rpage)->maxoff = maxoff - separator; 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 */ /* set up right bound for left page */
bound = GinDataPageGetRightBound(lpage); bound = GinDataPageGetRightBound(lpage);
*bound = btree->pitem.key; *bound = btree->pitem.key;
...@@ -613,6 +575,16 @@ dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe ...@@ -613,6 +575,16 @@ dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe
rdata[1].len = MAXALIGN(maxoff * sizeofitem); rdata[1].len = MAXALIGN(maxoff * sizeofitem);
rdata[1].next = NULL; rdata[1].next = NULL;
/* Prepare a downlink tuple for insertion to the parent */
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);
return lpage; return lpage;
} }
...@@ -638,6 +610,92 @@ ginDataFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf) ...@@ -638,6 +610,92 @@ ginDataFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf)
GinDataPageAddPostingItem(page, &ri, InvalidOffsetNumber); GinDataPageAddPostingItem(page, &ri, InvalidOffsetNumber);
} }
/*
* Creates new posting tree containing the given TIDs. Returns the page
* number of the root of the new posting tree.
*
* items[] must be in sorted order with no duplicates.
*/
BlockNumber
createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
GinStatsData *buildStats)
{
BlockNumber blkno;
Buffer buffer;
Page page;
int itemsCount;
/* Calculate how many TIDs will fit on first page. */
itemsCount = Min(nitems, GinMaxLeafDataItems);
/*
* Create the root page.
*/
buffer = GinNewBuffer(index);
page = BufferGetPage(buffer);
blkno = BufferGetBlockNumber(buffer);
START_CRIT_SECTION();
GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
memcpy(GinDataPageGetData(page), items, sizeof(ItemPointerData) * nitems);
GinPageGetOpaque(page)->maxoff = nitems;
MarkBufferDirty(buffer);
if (RelationNeedsWAL(index))
{
XLogRecPtr recptr;
XLogRecData rdata[2];
ginxlogCreatePostingTree data;
data.node = index->rd_node;
data.blkno = blkno;
data.nitem = nitems;
rdata[0].buffer = InvalidBuffer;
rdata[0].data = (char *) &data;
rdata[0].len = sizeof(ginxlogCreatePostingTree);
rdata[0].next = &rdata[1];
rdata[1].buffer = InvalidBuffer;
rdata[1].data = (char *) items;
rdata[1].len = sizeof(ItemPointerData) * itemsCount;
rdata[1].next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_PTREE, rdata);
PageSetLSN(page, recptr);
}
UnlockReleaseBuffer(buffer);
END_CRIT_SECTION();
/* During index build, count the newly-added data page */
if (buildStats)
buildStats->nDataPages++;
/*
* Add any remaining TIDs to the newly-created posting tree.
*/
if (itemsCount < nitems)
{
GinPostingTreeScan *gdi;
gdi = ginPrepareScanPostingTree(index, blkno, FALSE);
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointers(gdi,
items + itemsCount,
nitems - itemsCount,
buildStats);
pfree(gdi);
}
return blkno;
}
void void
ginPrepareDataScan(GinBtree btree, Relation index) ginPrepareDataScan(GinBtree btree, Relation index)
{ {
...@@ -650,7 +708,6 @@ ginPrepareDataScan(GinBtree btree, Relation index) ...@@ -650,7 +708,6 @@ ginPrepareDataScan(GinBtree btree, Relation index)
btree->findItem = dataLocateLeafItem; btree->findItem = dataLocateLeafItem;
btree->findChildPtr = dataFindChildPtr; btree->findChildPtr = dataFindChildPtr;
btree->getLeftMostPage = dataGetLeftMostPage; btree->getLeftMostPage = dataGetLeftMostPage;
btree->isEnoughSpace = dataIsEnoughSpace;
btree->placeToPage = dataPlaceToPage; btree->placeToPage = dataPlaceToPage;
btree->splitPage = dataSplitPage; btree->splitPage = dataSplitPage;
btree->fillRoot = ginDataFillRoot; btree->fillRoot = ginDataFillRoot;
......
...@@ -486,9 +486,12 @@ entryPreparePage(GinBtree btree, Page page, OffsetNumber off) ...@@ -486,9 +486,12 @@ entryPreparePage(GinBtree btree, Page page, OffsetNumber off)
/* /*
* Place tuple on page and fills WAL record * Place tuple on page and fills WAL record
*
* If the tuple doesn't fit, returns false without modifying the page.
*/ */
static void static bool
entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata) entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off,
XLogRecData **prdata)
{ {
Page page = BufferGetPage(buf); Page page = BufferGetPage(buf);
OffsetNumber placed; OffsetNumber placed;
...@@ -498,6 +501,10 @@ entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prd ...@@ -498,6 +501,10 @@ entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prd
static XLogRecData rdata[3]; static XLogRecData rdata[3];
static ginxlogInsert data; static ginxlogInsert data;
/* quick exit if it doesn't fit */
if (!entryIsEnoughSpace(btree, buf, off))
return false;
*prdata = rdata; *prdata = rdata;
data.updateBlkno = entryPreparePage(btree, page, off); data.updateBlkno = entryPreparePage(btree, page, off);
...@@ -543,6 +550,8 @@ entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prd ...@@ -543,6 +550,8 @@ entryPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prd
rdata[cnt].next = NULL; rdata[cnt].next = NULL;
btree->entry = NULL; btree->entry = NULL;
return true;
} }
/* /*
...@@ -724,7 +733,6 @@ ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum, ...@@ -724,7 +733,6 @@ ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
btree->findItem = entryLocateLeafEntry; btree->findItem = entryLocateLeafEntry;
btree->findChildPtr = entryFindChildPtr; btree->findChildPtr = entryFindChildPtr;
btree->getLeftMostPage = entryGetLeftMostPage; btree->getLeftMostPage = entryGetLeftMostPage;
btree->isEnoughSpace = entryIsEnoughSpace;
btree->placeToPage = entryPlaceToPage; btree->placeToPage = entryPlaceToPage;
btree->splitPage = entrySplitPage; btree->splitPage = entrySplitPage;
btree->fillRoot = ginEntryFillRoot; btree->fillRoot = ginEntryFillRoot;
......
...@@ -35,64 +35,6 @@ typedef struct ...@@ -35,64 +35,6 @@ typedef struct
BuildAccumulator accum; BuildAccumulator accum;
} GinBuildState; } GinBuildState;
/*
* Creates new posting tree with one page, containing the given TIDs.
* Returns the page number (which will be the root of this posting tree).
*
* items[] must be in sorted order with no duplicates.
*/
static BlockNumber
createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
{
BlockNumber blkno;
Buffer buffer = GinNewBuffer(index);
Page page;
/* Assert that the items[] array will fit on one page */
Assert(nitems <= GinMaxLeafDataItems);
START_CRIT_SECTION();
GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
page = BufferGetPage(buffer);
blkno = BufferGetBlockNumber(buffer);
memcpy(GinDataPageGetData(page), items, sizeof(ItemPointerData) * nitems);
GinPageGetOpaque(page)->maxoff = nitems;
MarkBufferDirty(buffer);
if (RelationNeedsWAL(index))
{
XLogRecPtr recptr;
XLogRecData rdata[2];
ginxlogCreatePostingTree data;
data.node = index->rd_node;
data.blkno = blkno;
data.nitem = nitems;
rdata[0].buffer = InvalidBuffer;
rdata[0].data = (char *) &data;
rdata[0].len = sizeof(ginxlogCreatePostingTree);
rdata[0].next = &rdata[1];
rdata[1].buffer = InvalidBuffer;
rdata[1].data = (char *) items;
rdata[1].len = sizeof(ItemPointerData) * nitems;
rdata[1].next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_PTREE, rdata);
PageSetLSN(page, recptr);
}
UnlockReleaseBuffer(buffer);
END_CRIT_SECTION();
return blkno;
}
/* /*
* Adds array of item pointers to tuple's posting list, or * Adds array of item pointers to tuple's posting list, or
...@@ -148,11 +90,8 @@ addItemPointersToLeafTuple(GinState *ginstate, ...@@ -148,11 +90,8 @@ addItemPointersToLeafTuple(GinState *ginstate,
*/ */
postingRoot = createPostingTree(ginstate->index, postingRoot = createPostingTree(ginstate->index,
GinGetPosting(old), GinGetPosting(old),
GinGetNPosting(old)); GinGetNPosting(old),
buildStats);
/* During index build, count the newly-added data page */
if (buildStats)
buildStats->nDataPages++;
/* Now insert the TIDs-to-be-added into the posting tree */ /* Now insert the TIDs-to-be-added into the posting tree */
gdi = ginPrepareScanPostingTree(ginstate->index, postingRoot, FALSE); gdi = ginPrepareScanPostingTree(ginstate->index, postingRoot, FALSE);
...@@ -186,7 +125,7 @@ buildFreshLeafTuple(GinState *ginstate, ...@@ -186,7 +125,7 @@ buildFreshLeafTuple(GinState *ginstate,
{ {
IndexTuple res; IndexTuple res;
/* try to build tuple with room for all the items */ /* try to build a posting list tuple with all the items */
res = GinFormTuple(ginstate, attnum, key, category, res = GinFormTuple(ginstate, attnum, key, category,
items, nitem, false); items, nitem, false);
...@@ -202,32 +141,9 @@ buildFreshLeafTuple(GinState *ginstate, ...@@ -202,32 +141,9 @@ buildFreshLeafTuple(GinState *ginstate,
res = GinFormTuple(ginstate, attnum, key, category, NULL, 0, true); res = GinFormTuple(ginstate, attnum, key, category, NULL, 0, true);
/* /*
* Initialize posting tree with as many TIDs as will fit on the first * Initialize a new posting tree with the TIDs.
* page.
*/ */
postingRoot = createPostingTree(ginstate->index, postingRoot = createPostingTree(ginstate->index, items, nitem);
items,
Min(nitem, GinMaxLeafDataItems));
/* During index build, count the newly-added data page */
if (buildStats)
buildStats->nDataPages++;
/* Add any remaining TIDs to the posting tree */
if (nitem > GinMaxLeafDataItems)
{
GinPostingTreeScan *gdi;
gdi = ginPrepareScanPostingTree(ginstate->index, postingRoot, FALSE);
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointers(gdi,
items + GinMaxLeafDataItems,
nitem - GinMaxLeafDataItems,
buildStats);
pfree(gdi);
}
/* And save the root link in the result tuple */ /* And save the root link in the result tuple */
GinSetPostingTree(res, postingRoot); GinSetPostingTree(res, postingRoot);
......
/*-------------------------------------------------------------------------
*
* ginpostinglist.c
* routines for dealing with posting lists.
*
*
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/access/gin/ginpostinglist.c
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/gin_private.h"
/*
* Merge two ordered arrays of itempointers, eliminating any duplicates.
* Returns the number of items in the result.
* Caller is responsible that there is enough space at *dst.
*/
uint32
ginMergeItemPointers(ItemPointerData *dst,
ItemPointerData *a, uint32 na,
ItemPointerData *b, uint32 nb)
{
ItemPointerData *dptr = dst;
ItemPointerData *aptr = a,
*bptr = b;
while (aptr - a < na && bptr - b < nb)
{
int cmp = ginCompareItemPointers(aptr, bptr);
if (cmp > 0)
*dptr++ = *bptr++;
else if (cmp == 0)
{
/* we want only one copy of the identical items */
*dptr++ = *bptr++;
aptr++;
}
else
*dptr++ = *aptr++;
}
while (aptr - a < na)
*dptr++ = *aptr++;
while (bptr - b < nb)
*dptr++ = *bptr++;
return dptr - dst;
}
...@@ -33,23 +33,26 @@ typedef struct ...@@ -33,23 +33,26 @@ typedef struct
/* /*
* Cleans array of ItemPointer (removes dead pointers) * Vacuums a list of item pointers. The original size of the list is 'nitem',
* Results are always stored in *cleaned, which will be allocated * returns the number of items remaining afterwards.
* if it's needed. In case of *cleaned!=NULL caller is responsible to *
* have allocated enough space. *cleaned and items may point to the same * If *cleaned == NULL on entry, the original array is left unmodified; if
* memory address. * any items are removed, a palloc'd copy of the result is stored in *cleaned.
* Otherwise *cleaned should point to the original array, in which case it's
* modified directly.
*/ */
static int
static uint32 ginVacuumPostingList(GinVacuumState *gvs, ItemPointerData *items, int nitem,
ginVacuumPostingList(GinVacuumState *gvs, ItemPointerData *items, uint32 nitem, ItemPointerData **cleaned) ItemPointerData **cleaned)
{ {
uint32 i, int i,
j = 0; j = 0;
Assert(*cleaned == NULL || *cleaned == items);
/* /*
* just scan over ItemPointer array * just scan over ItemPointer array
*/ */
for (i = 0; i < nitem; i++) for (i = 0; i < nitem; i++)
{ {
if (gvs->callback(items + i, gvs->callback_state)) if (gvs->callback(items + i, gvs->callback_state))
...@@ -385,7 +388,8 @@ typedef struct DataPageDeleteStack ...@@ -385,7 +388,8 @@ typedef struct DataPageDeleteStack
* scans posting tree and deletes empty pages * scans posting tree and deletes empty pages
*/ */
static bool static bool
ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDeleteStack *parent, OffsetNumber myoff) ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot,
DataPageDeleteStack *parent, OffsetNumber myoff)
{ {
DataPageDeleteStack *me; DataPageDeleteStack *me;
Buffer buffer; Buffer buffer;
...@@ -431,15 +435,13 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDel ...@@ -431,15 +435,13 @@ ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot, DataPageDel
if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber) if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber)
{ {
/* the page is empty */
if (!(me->leftBlkno == InvalidBlockNumber && GinPageRightMost(page))) if (!(me->leftBlkno == InvalidBlockNumber && GinPageRightMost(page)))
{ {
/* we never delete right most branch */ /* we never delete right most branch */
Assert(!isRoot); Assert(!isRoot);
if (GinPageGetOpaque(page)->maxoff < FirstOffsetNumber) ginDeletePage(gvs, blkno, me->leftBlkno, me->parent->blkno, myoff, me->parent->isRoot);
{ meDelete = TRUE;
ginDeletePage(gvs, blkno, me->leftBlkno, me->parent->blkno, myoff, me->parent->isRoot);
meDelete = TRUE;
}
} }
} }
...@@ -517,11 +519,12 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3 ...@@ -517,11 +519,12 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
else if (GinGetNPosting(itup) > 0) else if (GinGetNPosting(itup) > 0)
{ {
/* /*
* if we already create temporary page, we will make changes in * if we already created a temporary page, make changes in place
* place
*/ */
ItemPointerData *cleaned = (tmppage == origpage) ? NULL : GinGetPosting(itup); ItemPointerData *cleaned = (tmppage == origpage) ? NULL : GinGetPosting(itup);
uint32 newN = ginVacuumPostingList(gvs, GinGetPosting(itup), GinGetNPosting(itup), &cleaned); int newN;
newN = ginVacuumPostingList(gvs, GinGetPosting(itup), GinGetNPosting(itup), &cleaned);
if (GinGetNPosting(itup) != newN) if (GinGetNPosting(itup) != newN)
{ {
...@@ -530,15 +533,13 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3 ...@@ -530,15 +533,13 @@ ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
GinNullCategory category; GinNullCategory category;
/* /*
* Some ItemPointers was deleted, so we should remake our * Some ItemPointers were deleted, recreate tuple.
* tuple
*/ */
if (tmppage == origpage) if (tmppage == origpage)
{ {
/* /*
* On first difference we create temporary page in memory * On first difference, create a temporary copy of the
* and copies content in to it. * page and copy the tuple's posting list to it.
*/ */
tmppage = PageGetTempPageCopy(origpage); tmppage = PageGetTempPageCopy(origpage);
......
...@@ -485,8 +485,7 @@ typedef struct GinBtreeData ...@@ -485,8 +485,7 @@ typedef struct GinBtreeData
/* insert methods */ /* insert methods */
OffsetNumber (*findChildPtr) (GinBtree, Page, BlockNumber, OffsetNumber); OffsetNumber (*findChildPtr) (GinBtree, Page, BlockNumber, OffsetNumber);
BlockNumber (*getLeftMostPage) (GinBtree, Page); BlockNumber (*getLeftMostPage) (GinBtree, Page);
bool (*isEnoughSpace) (GinBtree, Buffer, OffsetNumber); bool (*placeToPage) (GinBtree, Buffer, OffsetNumber, XLogRecData **);
void (*placeToPage) (GinBtree, Buffer, OffsetNumber, XLogRecData **);
Page (*splitPage) (GinBtree, Buffer, Buffer, OffsetNumber, XLogRecData **); Page (*splitPage) (GinBtree, Buffer, Buffer, OffsetNumber, XLogRecData **);
void (*fillRoot) (GinBtree, Buffer, Buffer, Buffer); void (*fillRoot) (GinBtree, Buffer, Buffer, Buffer);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment