pg.ddx.io pgsql-hackers@postgresql.org mailing list archive
help / color / mirror / Atom feed From: Heikki Linnakangas <hlinnakangas@vmware.com>
To: Alexander Korotkov <aekorotkov@gmail.com>
Cc: pgsql-hackers <pgsql-hackers@postgresql.org>
Subject: Re: GIN improvements part 1: additional information
Date: Sat, 29 Jun 2013 11:56:28 +0300
Message-ID: <51CEA13C.8040103@vmware.com> (raw )
In-Reply-To: <CAPpHfdu8=5spaS7c9mxwH7bNMDR6n378fZCv6c2Y3YM7iE5KiQ@mail.gmail.com >
References: <CAPpHfduxv-iL7aedwPW0W5fXrWGAKfxijWM63_hZujaCRxnmFQ@mail.gmail.com >
<CAPpHfduQrtC4B+MC9_z3QB8oJsK1Ws-H8ivEsy_cmDnfoJqkeQ@mail.gmail.com >
<CAPpHfdszEJhSynTPX6=tB3aKq1EA1JXpwx4xiFYM097K7PhQLA@mail.gmail.com >
<CAPpHfdu8=5spaS7c9mxwH7bNMDR6n378fZCv6c2Y3YM7iE5KiQ@mail.gmail.com >
List-Unsubscribe: <mailto:majordomo@postgresql.org?body=unsub%20pgsql-hackers>
On 25.06.2013 01:03, Alexander Korotkov wrote:
> New revision of patch is attached. Now it includes some docs.
Thanks! I'm looking into this in detail now.
First, this patch actually contains two major things:
1. Pack item pointers more tightly on posting data leaf pages.
2. Allow opclass implementation to attach "additional information" to
each item pointer.
These are two very distinct features, so this patch needs to be split
into two. I extracted the 1st part into a separate patch, attached, and
am going to focus on that now.
I made one significant change: I removed the 'freespace' field you added
to GinpageOpaque. Instead, on data leaf pages the offset from the
beginning of the page where the packed items end is stored in place of
the 'maxoff' field. This allows for quick calculation of the free space,
but there is no count of item pointers stored on the page anymore, so
some code that looped through all the item pointers relying on 'maxoff'
had to be changed to work with the end offset instead. I'm not 100%
wedded on this, but I'd like to avoid adding the redundant freespace
field on pages that don't need it, because it's confusing and you have
to remember to keep them in sync.
The patch needs a lot of cleanup still, and I may well have broken some
stuff, but I'm quite pleased with the performance. I tested this with
two tables; one is the titles from the DBLP dataset. Another is integer
arrays, created with this:
create function randomintarr() returns int[] as $$ select
array_agg((random() * 1000.0)::int4) from generate_series(1,10) $$
language sql;
create table intarrtbl as select randomintarr() as ii from
generate_series(1, 10000000);
The effect on the index sizes is quite dramatic:
postgres=# \di+
List of relations
Schema | Name | Type | Owner | Table | Size |
--------+--------------------+-------+--------+-------------+--------+
public | gin_intarr_master | index | heikki | intarrtbl | 585 MB |
public | gin_intarr_patched | index | heikki | intarrtbl | 211 MB |
public | gin_title | index | heikki | dblp_titles | 93 MB |
public | gin_title_master | index | heikki | dblp_titles | 180 MB |
(4 rows)
Tomas Vondra tested the search performance of an earlier version of this
patch: http://www.postgresql.org/message-id/50BFF89A.7080908@fuzzy.cz ).
He initially saw a huge slowdown, but could not reproduce it with a
later version of the patch. I did not see much difference in a few quick
queries I ran, so we're probably good on that front.
There's a few open questions:
1. How are we going to handle pg_upgrade? It would be nice to be able to
read the old page format, or convert on-the-fly . OTOH, if it gets too
complicated, might not be worth it. The indexes are much smaller with
the patch, so anyone using GIN probably wants to rebuild them anyway,
sooner or later. Still, I'd like to give it a shot.
2. The patch introduces a small fixed 32-entry index into the packed
items. Is that an optimal number?
3. I'd like to see some performance testing of insertions, deletions,
and vacuum. I suspect that maintaining the 32-entry index might be
fairly expensive, as it's rewritten on every update to a leaf page.
- Heikki
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
[text/x-diff] gin-packed-postinglists-1.patch (70.9K, ../51CEA13C.8040103@vmware.com/2-gin-packed-postinglists-1.patch)
download | inline diff:
*** a/src/backend/access/gin/README
--- b/src/backend/access/gin/README
***************
*** 145,150 **** none appear in the key entry itself. The separate pages are called a
--- 145,151 ----
Note that in either case, the ItemPointers associated with a key can
easily be read out in sorted order; this is relied on by the scan
algorithms.
+ FIXME: Update above paragraph!
* The index tuple header fields of a leaf key entry are abused as follows:
*** a/src/backend/access/gin/gindatapage.c
--- b/src/backend/access/gin/gindatapage.c
***************
*** 17,22 ****
--- 17,113 ----
#include "access/gin_private.h"
#include "utils/rel.h"
+ /*
+ * Write item pointer into leaf data page using varbyte encoding. Since
+ * BlockNumber is stored in incremental manner we also need a previous item
+ * pointer.
+ */
+ char *
+ ginDataPageLeafWriteItemPointer(char *ptr, ItemPointer iptr, ItemPointer prev)
+ {
+ uint32 blockNumberIncr;
+ uint16 offset;
+ uint8 v;
+
+ blockNumberIncr = iptr->ip_blkid.bi_lo + (iptr->ip_blkid.bi_hi << 16) -
+ (prev->ip_blkid.bi_lo + (prev->ip_blkid.bi_hi << 16));
+ for (;;)
+ {
+ if (blockNumberIncr < HIGHBIT)
+ {
+ v = (uint8) blockNumberIncr;
+ *ptr = v;
+ ptr++;
+ break;
+ }
+ else
+ {
+ v = ((uint8) blockNumberIncr) | HIGHBIT;
+ *ptr = v;
+ ptr++;
+ blockNumberIncr >>= 7;
+ }
+ }
+
+ offset = iptr->ip_posid;
+ for (;;)
+ {
+ if (offset < HIGHBIT)
+ {
+ v = (uint8) offset;
+ *ptr = v;
+ ptr++;
+ break;
+ }
+ else
+ {
+ v = ((uint8) offset) | HIGHBIT;
+ *ptr = v;
+ ptr++;
+ offset >>= 7;
+ }
+ }
+
+ return ptr;
+ }
+
+ /*
+ * Calculate size of incremental varbyte encoding of item pointer.
+ */
+ static int
+ ginDataPageLeafGetItemPointerSize(ItemPointer iptr, ItemPointer prev)
+ {
+ uint32 blockNumberIncr;
+ uint16 offset;
+ int size = 0;
+
+ blockNumberIncr = iptr->ip_blkid.bi_lo + (iptr->ip_blkid.bi_hi << 16) -
+ (prev->ip_blkid.bi_lo + (prev->ip_blkid.bi_hi << 16));
+ do
+ {
+ size++;
+ blockNumberIncr >>= 7;
+ } while (blockNumberIncr > 0);
+
+ offset = iptr->ip_posid;
+ do
+ {
+ size++;
+ offset >>= 7;
+ } while (offset > 0);
+
+ return size;
+ }
+
+ /*
+ * Returns size of item pointers if leaf data page after inserting another one.
+ */
+ Size
+ ginCheckPlaceToDataPageLeaf(ItemPointer iptr, ItemPointer prev, Size size)
+ {
+ return size + ginDataPageLeafGetItemPointerSize(iptr, prev);
+ }
+
int
ginCompareItemPointers(ItemPointer a, ItemPointer b)
{
***************
*** 159,164 **** dataLocateItem(GinBtree btree, GinBtreeStack *stack)
--- 250,322 ----
}
/*
+ * Find item pointer in leaf data page. Returns true if given item pointer is
+ * found and false if it's not. Sets offset and iptrOut to last item pointer
+ * which is less than given one. Sets ptrOut ahead that item pointer.
+ */
+ static bool
+ findInLeafPage(GinBtree btree, Page page, OffsetNumber *offset,
+ ItemPointerData *iptrOut, Pointer *ptrOut)
+ {
+ Pointer ptr = GinDataPageGetData(page);
+ OffsetNumber i, maxoff, first = FirstOffsetNumber;
+ ItemPointerData iptr = {{0,0},0};
+ int cmp;
+
+ maxoff = GinPageGetOpaque(page)->maxoff;
+
+ /*
+ * At first, search index at the end of page. As the result we narrow
+ * [first, maxoff] range.
+ */
+ for (i = 0; i < GinDataLeafIndexCount; i++)
+ {
+ GinDataLeafItemIndex *index = &GinPageGetIndexes(page)[i];
+ if (index->offsetNumer == InvalidOffsetNumber)
+ break;
+
+ cmp = ginCompareItemPointers(&index->iptr, btree->items + btree->curitem);
+ if (cmp < 0)
+ {
+ ptr = GinDataPageGetData(page) + index->pageOffset;
+ first = index->offsetNumer;
+ iptr = index->iptr;
+ }
+ else
+ {
+ maxoff = index->offsetNumer - 1;
+ break;
+ }
+ }
+
+ /* Search page in [first, maxoff] range found by page index */
+ for (i = first; i <= maxoff; i++)
+ {
+ *ptrOut = ptr;
+ *iptrOut = iptr;
+ ptr = ginDataPageLeafReadItemPointer(ptr, &iptr);
+
+ cmp = ginCompareItemPointers(btree->items + btree->curitem, &iptr);
+ if (cmp == 0)
+ {
+ *offset = i;
+ return true;
+ }
+ if (cmp < 0)
+ {
+ *offset = i;
+ return false;
+ }
+ }
+
+ *ptrOut = ptr;
+ *iptrOut = iptr;
+ *offset = GinPageGetOpaque(page)->maxoff + 1;
+ return false;
+ }
+
+
+ /*
* Searches correct position for value on leaf page.
* Page should be correctly chosen.
* Returns true if value found on page.
***************
*** 167,175 **** static bool
dataLocateLeafItem(GinBtree btree, GinBtreeStack *stack)
{
Page page = BufferGetPage(stack->buffer);
! OffsetNumber low,
! high;
! int result;
Assert(GinPageIsLeaf(page));
Assert(GinPageIsData(page));
--- 325,332 ----
dataLocateLeafItem(GinBtree btree, GinBtreeStack *stack)
{
Page page = BufferGetPage(stack->buffer);
! ItemPointerData iptr;
! Pointer ptr;
Assert(GinPageIsLeaf(page));
Assert(GinPageIsData(page));
***************
*** 180,215 **** dataLocateLeafItem(GinBtree btree, GinBtreeStack *stack)
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->items + btree->curitem, (ItemPointer) GinDataPageGetItem(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;
}
/*
--- 337,344 ----
return TRUE;
}
! return findInLeafPage(btree, page, &stack->off, &iptr, &ptr);
}
/*
***************
*** 333,345 **** dataIsEnoughSpace(GinBtree btree, Buffer buf, OffsetNumber off)
if (GinPageIsLeaf(page))
{
! if (GinPageRightMost(page) && off > GinPageGetOpaque(page)->maxoff)
{
! if ((btree->nitem - btree->curitem) * sizeof(ItemPointerData) <= GinDataPageGetFreeSpace(page))
! return true;
}
! else if (sizeof(ItemPointerData) <= GinDataPageGetFreeSpace(page))
return true;
}
else if (sizeof(PostingItem) <= GinDataPageGetFreeSpace(page))
return true;
--- 462,498 ----
if (GinPageIsLeaf(page))
{
! int i, n, j;
! ItemPointerData iptr = {{0,0},0};
! Size size = 0;
!
! /*
! * Calculate additional size using worst case assumption: varbyte
! * encoding from zero item pointer. Also use worst case assumption about
! * alignment.
! */
! n = GinPageGetOpaque(page)->maxoff;
!
! if (GinPageRightMost(page) && off > n)
! {
! for (j = btree->curitem; j < btree->nitem; j++)
! {
! size = ginCheckPlaceToDataPageLeaf(&btree->items[j],
! (j == btree->curitem) ? (&iptr) : &btree->items[j - 1],
! size);
! i++;
! }
! }
! else
{
! j = btree->curitem;
! size = ginCheckPlaceToDataPageLeaf(&btree->items[j], &iptr, size);
}
! size += MAXIMUM_ALIGNOF;
!
! if (GinDataPageGetFreeSpace(page) >= size)
return true;
+
}
else if (sizeof(PostingItem) <= GinDataPageGetFreeSpace(page))
return true;
***************
*** 380,391 **** static void
dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata)
{
Page page = BufferGetPage(buf);
- int sizeofitem = GinSizeOfDataPageItem(page);
int cnt = 0;
/* these must be static so they can be returned to caller */
static XLogRecData rdata[3];
static ginxlogInsert data;
*prdata = rdata;
Assert(GinPageIsData(page));
--- 533,544 ----
dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prdata)
{
Page page = BufferGetPage(buf);
int cnt = 0;
/* these must be static so they can be returned to caller */
static XLogRecData rdata[3];
static ginxlogInsert data;
+ static char insertData[BLCKSZ];
*prdata = rdata;
Assert(GinPageIsData(page));
***************
*** 395,401 **** dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prda
data.node = btree->index->rd_node;
data.blkno = BufferGetBlockNumber(buf);
data.offset = off;
! data.nitem = 1;
data.isDelete = FALSE;
data.isData = TRUE;
data.isLeaf = GinPageIsLeaf(page) ? TRUE : FALSE;
--- 548,554 ----
data.node = btree->index->rd_node;
data.blkno = BufferGetBlockNumber(buf);
data.offset = off;
! data.nitem = 0;
data.isDelete = FALSE;
data.isData = TRUE;
data.isLeaf = GinPageIsLeaf(page) ? TRUE : FALSE;
***************
*** 423,457 **** dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prda
rdata[cnt].next = &rdata[cnt + 1];
cnt++;
- rdata[cnt].buffer = InvalidBuffer;
- rdata[cnt].data = (GinPageIsLeaf(page)) ? ((char *) (btree->items + btree->curitem)) : ((char *) &(btree->pitem));
- rdata[cnt].len = sizeofitem;
- rdata[cnt].next = NULL;
-
if (GinPageIsLeaf(page))
{
! if (GinPageRightMost(page) && off > GinPageGetOpaque(page)->maxoff)
{
! /* usually, create index... */
! uint32 savedPos = btree->curitem;
! while (btree->curitem < btree->nitem)
{
! GinDataPageAddItem(page, btree->items + btree->curitem, off);
! off++;
! btree->curitem++;
}
- data.nitem = btree->curitem - savedPos;
- rdata[cnt].len = sizeofitem * data.nitem;
}
else
{
! GinDataPageAddItem(page, btree->items + btree->curitem, off);
btree->curitem++;
}
}
! else
! GinDataPageAddItem(page, &(btree->pitem), off);
}
/*
--- 576,908 ----
rdata[cnt].next = &rdata[cnt + 1];
cnt++;
if (GinPageIsLeaf(page))
{
! int i = 0, j, max_j;
! Pointer ptr = GinDataPageGetData(page), next_ptr, insertStart;
! ItemPointerData iptr = {{0,0},0}, next_iptr;
! char pageCopy[BLCKSZ];
! int maxoff = GinPageGetOpaque(page)->maxoff;
! int copySize = 0;
!
! /*
! * We're going to prevent var-byte re-encoding of whole page.
! * Find position in page using page indexes.
! */
! findInLeafPage(btree, page, &off, &iptr, &ptr);
!
! Assert(GinDataPageFreeSpacePre(page,ptr) >= 0);
!
! if (off <= maxoff)
! {
! /*
! * Read next item pointer: we'll have to re-encode it. Copy
! * previous part of page
! */
! next_iptr = iptr;
! next_ptr = ginDataPageLeafReadItemPointer(ptr, &next_iptr);
! copySize = GinDataPageSize - GinDataPageGetFreeSpace(page) -
! (next_ptr - GinDataPageGetData(page));
! memcpy(pageCopy, next_ptr, copySize);
! }
!
! /* Check how many items we're going to add */
! if (GinPageRightMost(page) && off > maxoff)
! max_j = btree->nitem;
! else
! max_j = btree->curitem + 1;
!
! /* Place items to the page while we have enough of space */
! *((ItemPointerData *)insertData) = iptr;
! insertStart = ptr;
! i = 0;
! for (j = btree->curitem; j < max_j; j++)
! {
! Pointer ptr2;
!
! ptr2 = page + ginCheckPlaceToDataPageLeaf(&btree->items[j],
! &iptr, ptr - page);
!
! if (GinDataPageFreeSpacePre(page, ptr2) < 0)
! break;
!
! ptr = ginDataPageLeafWriteItemPointer(ptr, &btree->items[j], &iptr);
! Assert(GinDataPageFreeSpacePre(page,ptr) >= 0);
!
! iptr = btree->items[j];
! btree->curitem++;
! data.nitem++;
! i++;
! }
!
! /* Put WAL data */
! memcpy(insertData + sizeof(ItemPointerData), insertStart,
! ptr - insertStart);
! rdata[cnt].buffer = InvalidBuffer;
! rdata[cnt].data = insertData;
! rdata[cnt].len = sizeof(ItemPointerData) + (ptr - insertStart);
! rdata[cnt].next = NULL;
!
! /* Place rest of the page back */
! if (off <= maxoff)
{
! ptr = ginDataPageLeafWriteItemPointer(ptr, &next_iptr, &iptr);
! Assert(GinDataPageFreeSpacePre(page,ptr) >= 0);
! memcpy(ptr, pageCopy, copySize);
! }
! GinPageGetOpaque(page)->maxoff += i;
!
! if (GinDataPageFreeSpacePre(page,ptr) < 0)
! elog(ERROR, "Not enough of space in leaf page!");
!
! /* Update indexes in the end of page */
! updateItemIndexes(page, btree->ginstate);
! }
! else
! {
! rdata[cnt].buffer = InvalidBuffer;
! rdata[cnt].data = (char *) &(btree->pitem);
! rdata[cnt].len = sizeof(PostingItem);
! rdata[cnt].next = NULL;
! data.nitem = 1;
!
! GinDataPageAddItem(page, &(btree->pitem), off);
! }
! }
!
! /* Macro for leaf data page split: switch to right page if needed. */
! #define CHECK_SWITCH_TO_RPAGE \
! do { \
! if (ptr - GinDataPageGetData(page) > \
! totalsize / 2 && page == lpage) \
! { \
! maxLeftIptr = iptr; \
! prevIptr.ip_blkid.bi_hi = 0; \
! prevIptr.ip_blkid.bi_lo = 0; \
! prevIptr.ip_posid = 0; \
! GinPageGetOpaque(lpage)->maxoff = j; \
! page = rpage; \
! ptr = GinDataPageGetData(rpage); \
! j = FirstOffsetNumber; \
! } \
! else \
! { \
! j++; \
! } \
! } while (0)
!
!
!
! /*
! * Place tuple and split page, original buffer(lbuf) leaves untouched,
! * returns shadow page of lbuf filled new data.
! */
! static Page
! dataSplitPageLeaf(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off,
! XLogRecData **prdata)
! {
! OffsetNumber i, j,
! maxoff;
! Size totalsize = 0, prevTotalsize;
! Pointer ptr, copyPtr;
! Page page;
! Page lpage = PageGetTempPageCopy(BufferGetPage(lbuf));
! Page rpage = BufferGetPage(rbuf);
! Size pageSize = PageGetPageSize(lpage);
! Size maxItemSize = 0;
! ItemPointerData iptr, prevIptr, maxLeftIptr;
! int totalCount = 0;
! int maxItemIndex = btree->curitem;
!
! /* these must be static so they can be returned to caller */
! static XLogRecData rdata[3];
! static ginxlogSplit data;
! static char lpageCopy[BLCKSZ];
!
! *prdata = rdata;
! data.leftChildBlkno = (GinPageIsLeaf(lpage)) ?
! InvalidOffsetNumber : GinGetDownlink(btree->entry);
! data.updateBlkno = dataPrepareData(btree, lpage, off);
!
! maxoff = GinPageGetOpaque(lpage)->maxoff;
!
! /* Copy original data of the page */
! memcpy(lpageCopy, lpage, BLCKSZ);
!
! /* Reinitialize pages */
! GinInitPage(rpage, GinPageGetOpaque(lpage)->flags, pageSize);
! GinInitPage(lpage, GinPageGetOpaque(rpage)->flags, pageSize);
!
! GinPageGetOpaque(lpage)->maxoff = 0;
! GinPageGetOpaque(rpage)->maxoff = 0;
!
! /* Calculate the whole size we're going to place */
! copyPtr = GinDataPageGetData(lpageCopy);
! iptr.ip_blkid.bi_hi = 0;
! iptr.ip_blkid.bi_lo = 0;
! iptr.ip_posid = 0;
! for (i = FirstOffsetNumber; i <= maxoff; i++)
! {
! if (i == off)
! {
! prevIptr = iptr;
! iptr = btree->items[maxItemIndex];
!
! prevTotalsize = totalsize;
! totalsize = ginCheckPlaceToDataPageLeaf(&iptr, &prevIptr, totalsize);
!
! maxItemIndex++;
! totalCount++;
! maxItemSize = Max(maxItemSize, totalsize - prevTotalsize);
! }
!
! prevIptr = iptr;
! copyPtr = ginDataPageLeafReadItemPointer(copyPtr, &iptr);
!
! prevTotalsize = totalsize;
! totalsize = ginCheckPlaceToDataPageLeaf(&iptr, &prevIptr, totalsize);
!
! totalCount++;
! maxItemSize = Max(maxItemSize, totalsize - prevTotalsize);
! }
!
! if (off == maxoff + 1)
! {
! prevIptr = iptr;
! iptr = btree->items[maxItemIndex];
! if (GinPageRightMost(lpage))
! {
! Size newTotalsize;
!
! /*
! * Found how many new item pointer we're going to add using
! * worst case assumptions about odd placement and alignment.
! */
! while (maxItemIndex < btree->nitem &&
! (newTotalsize = ginCheckPlaceToDataPageLeaf(&iptr, &prevIptr, totalsize)) <
! 2 * GinDataPageSize - 2 * maxItemSize - 2 * MAXIMUM_ALIGNOF
! )
{
! maxItemIndex++;
! totalCount++;
! maxItemSize = Max(maxItemSize, newTotalsize - totalsize);
! totalsize = newTotalsize;
!
! prevIptr = iptr;
! if (maxItemIndex < btree->nitem)
! iptr = btree->items[maxItemIndex];
}
}
else
{
! prevTotalsize = totalsize;
! totalsize = ginCheckPlaceToDataPageLeaf(&iptr, &prevIptr, totalsize);
! maxItemIndex++;
!
! totalCount++;
! maxItemSize = Max(maxItemSize, totalsize - prevTotalsize);
! }
! }
!
! /*
! * Place item pointers with additional information to the pages using
! * previous calculations. XXX: what does this do now that I removed the
! * additional information stuff from the patch?
! */
! ptr = GinDataPageGetData(lpage);
! page = lpage;
! j = FirstOffsetNumber;
! iptr.ip_blkid.bi_hi = 0;
! iptr.ip_blkid.bi_lo = 0;
! iptr.ip_posid = 0;
! prevIptr = iptr;
! copyPtr = GinDataPageGetData(lpageCopy);
! for (i = FirstOffsetNumber; i <= maxoff; i++)
! {
! if (i == off)
! {
! while (btree->curitem < maxItemIndex)
! {
! iptr = btree->items[btree->curitem];
!
! ptr = ginDataPageLeafWriteItemPointer(ptr, &iptr, &prevIptr);
! Assert(GinDataPageFreeSpacePre(page, ptr) >= 0);
!
! btree->curitem++;
! prevIptr = iptr;
!
! CHECK_SWITCH_TO_RPAGE;
! }
! }
!
! copyPtr = ginDataPageLeafReadItemPointer(copyPtr, &iptr);
!
! ptr = ginDataPageLeafWriteItemPointer(ptr, &iptr, &prevIptr);
! Assert(GinDataPageFreeSpacePre(page, ptr) >= 0);
!
! prevIptr = iptr;
!
! CHECK_SWITCH_TO_RPAGE;
! }
!
! if (off == maxoff + 1)
! {
! while (btree->curitem < maxItemIndex)
! {
! iptr = btree->items[btree->curitem];
!
! ptr = ginDataPageLeafWriteItemPointer(ptr, &iptr, &prevIptr);
! Assert(GinDataPageFreeSpacePre(page, ptr) >= 0);
!
btree->curitem++;
+
+ prevIptr = iptr;
+
+ CHECK_SWITCH_TO_RPAGE;
}
}
!
! GinPageGetOpaque(rpage)->maxoff = j - 1;
!
! PostingItemSetBlockNumber(&(btree->pitem), BufferGetBlockNumber(lbuf));
! btree->pitem.key = maxLeftIptr;
! btree->rightblkno = BufferGetBlockNumber(rbuf);
!
! *GinDataPageGetRightBound(rpage) = *GinDataPageGetRightBound(lpage);
! *GinDataPageGetRightBound(lpage) = maxLeftIptr;
!
! /* Fill indexes at the end of pages */
! updateItemIndexes(lpage, btree->ginstate);
! updateItemIndexes(rpage, btree->ginstate);
!
! data.node = btree->index->rd_node;
! data.rootBlkno = InvalidBlockNumber;
! data.lblkno = BufferGetBlockNumber(lbuf);
! data.rblkno = BufferGetBlockNumber(rbuf);
! data.separator = GinPageGetOpaque(lpage)->maxoff;
! data.nitem = GinPageGetOpaque(lpage)->maxoff + GinPageGetOpaque(rpage)->maxoff;
! data.isData = TRUE;
! data.isLeaf = TRUE;
! data.isRootSplit = FALSE;
! data.rightbound = *GinDataPageGetRightBound(rpage);
!
! 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 = GinDataPageGetData(lpage);
! rdata[1].len = GinDataPageSize - GinDataPageGetFreeSpace(lpage);
! rdata[1].next = &rdata[2];
!
! rdata[2].buffer = InvalidBuffer;
! rdata[2].data = GinDataPageGetData(rpage);
! rdata[2].len = GinDataPageSize - GinDataPageGetFreeSpace(rpage);
! rdata[2].next = NULL;
!
! return lpage;
}
/*
***************
*** 461,467 **** dataPlaceToPage(GinBtree btree, Buffer buf, OffsetNumber off, XLogRecData **prda
* left page
*/
static Page
! dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRecData **prdata)
{
char *ptr;
OffsetNumber separator;
--- 912,919 ----
* left page
*/
static Page
! dataSplitPageInternal(GinBtree btree, Buffer lbuf, Buffer rbuf,
! OffsetNumber off, XLogRecData **prdata)
{
char *ptr;
OffsetNumber separator;
***************
*** 563,569 **** dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe
data.separator = separator;
data.nitem = maxoff;
data.isData = TRUE;
! data.isLeaf = GinPageIsLeaf(lpage) ? TRUE : FALSE;
data.isRootSplit = FALSE;
data.rightbound = oldbound;
--- 1015,1021 ----
data.separator = separator;
data.nitem = maxoff;
data.isData = TRUE;
! data.isLeaf = FALSE;
data.isRootSplit = FALSE;
data.rightbound = oldbound;
***************
*** 581,586 **** dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off, XLogRe
--- 1033,1092 ----
}
/*
+ * Split page of posting tree. Calls relevant function of internal of leaf page
+ * because they are handled very different.
+ */
+ static Page
+ dataSplitPage(GinBtree btree, Buffer lbuf, Buffer rbuf, OffsetNumber off,
+ XLogRecData **prdata)
+ {
+ if (GinPageIsLeaf(BufferGetPage(lbuf)))
+ return dataSplitPageLeaf(btree, lbuf, rbuf, off, prdata);
+ else
+ return dataSplitPageInternal(btree, lbuf, rbuf, off, prdata);
+ }
+
+ /*
+ * Updates indexes in the end of leaf page which are used for faster search.
+ * Also updates freespace opaque field of page. Returns last item pointer of
+ * page.
+ */
+ ItemPointerData
+ updateItemIndexes(Page page, GinState *ginstate)
+ {
+ Pointer ptr;
+ ItemPointerData iptr;
+ int j = 0, maxoff, i;
+
+ /* Iterate over page */
+
+ maxoff = GinPageGetOpaque(page)->maxoff;
+ ptr = GinDataPageGetData(page);
+ iptr.ip_blkid.bi_lo = 0;
+ iptr.ip_blkid.bi_hi = 0;
+ iptr.ip_posid = 0;
+
+ for (i = FirstOffsetNumber; i <= maxoff; i++)
+ {
+ /* Place next page index entry if it's time to */
+ if (i * (GinDataLeafIndexCount + 1) > (j + 1) * maxoff)
+ {
+ GinPageGetIndexes(page)[j].iptr = iptr;
+ GinPageGetIndexes(page)[j].offsetNumer = i;
+ GinPageGetIndexes(page)[j].pageOffset = ptr - GinDataPageGetData(page);
+ j++;
+ }
+ ptr = ginDataPageLeafReadItemPointer(ptr, &iptr);
+ }
+ /* Fill rest of page indexes with InvalidOffsetNumber if any */
+ for (; j < GinDataLeafIndexCount; j++)
+ {
+ GinPageGetIndexes(page)[j].offsetNumer = InvalidOffsetNumber;
+ }
+ return iptr;
+ }
+
+ /*
* Fills new root by right bound values from child.
* Also called from ginxlog, should not use btree
*/
*** a/src/backend/access/gin/ginentrypage.c
--- b/src/backend/access/gin/ginentrypage.c
***************
*** 18,162 ****
#include "utils/rel.h"
/*
! * Form a tuple for entry tree.
! *
! * If the tuple would be too big to be stored, function throws a suitable
! * error if errorTooBig is TRUE, or returns NULL if errorTooBig is FALSE.
! *
! * See src/backend/access/gin/README for a description of the index tuple
! * format that is being built here. We build on the assumption that we
! * are making a leaf-level key entry containing a posting list of nipd items.
! * If the caller is actually trying to make a posting-tree entry, non-leaf
! * entry, or pending-list entry, it should pass nipd = 0 and then overwrite
! * the t_tid fields as necessary. In any case, ipd can be NULL to skip
! * copying any itempointers into the posting list; the caller is responsible
! * for filling the posting list afterwards, if ipd = NULL and nipd > 0.
*/
! IndexTuple
! GinFormTuple(GinState *ginstate,
! OffsetNumber attnum, Datum key, GinNullCategory category,
! ItemPointerData *ipd, uint32 nipd,
! bool errorTooBig)
{
! Datum datums[2];
! bool isnull[2];
! IndexTuple itup;
! uint32 newsize;
!
! /* Build the basic tuple: optional column number, plus key datum */
! if (ginstate->oneCol)
! {
! datums[0] = key;
! isnull[0] = (category != GIN_CAT_NORM_KEY);
! }
! else
! {
! datums[0] = UInt16GetDatum(attnum);
! isnull[0] = false;
! datums[1] = key;
! isnull[1] = (category != GIN_CAT_NORM_KEY);
! }
!
! itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);
!
! /*
! * Determine and store offset to the posting list, making sure there is
! * room for the category byte if needed.
! *
! * Note: because index_form_tuple MAXALIGNs the tuple size, there may well
! * be some wasted pad space. Is it worth recomputing the data length to
! * prevent that? That would also allow us to Assert that the real data
! * doesn't overlap the GinNullCategory byte, which this code currently
! * takes on faith.
! */
! newsize = IndexTupleSize(itup);
!
! if (IndexTupleHasNulls(itup))
! {
! uint32 minsize;
!
! Assert(category != GIN_CAT_NORM_KEY);
! minsize = GinCategoryOffset(itup, ginstate) + sizeof(GinNullCategory);
! newsize = Max(newsize, minsize);
! }
!
! newsize = SHORTALIGN(newsize);
!
! GinSetPostingOffset(itup, newsize);
!
! GinSetNPosting(itup, nipd);
!
! /*
! * Add space needed for posting list, if any. Then check that the tuple
! * won't be too big to store.
! */
! newsize += sizeof(ItemPointerData) * nipd;
! newsize = MAXALIGN(newsize);
! if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
! {
! if (errorTooBig)
! ereport(ERROR,
! (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
! errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
! (unsigned long) newsize,
! (unsigned long) Min(INDEX_SIZE_MASK,
! GinMaxItemSize),
! RelationGetRelationName(ginstate->index))));
! pfree(itup);
! return NULL;
! }
! /*
! * Resize tuple if needed
! */
! if (newsize != IndexTupleSize(itup))
! {
! itup = repalloc(itup, newsize);
! /* set new size in tuple header */
! itup->t_info &= ~INDEX_SIZE_MASK;
! itup->t_info |= newsize;
! }
!
! /*
! * Insert category byte, if needed
! */
! if (category != GIN_CAT_NORM_KEY)
{
! Assert(IndexTupleHasNulls(itup));
! GinSetNullCategory(itup, ginstate, category);
}
-
- /*
- * Copy in the posting list, if provided
- */
- if (ipd)
- memcpy(GinGetPosting(itup), ipd, sizeof(ItemPointerData) * nipd);
-
- return itup;
- }
-
- /*
- * Sometimes we reduce the number of posting list items in a tuple after
- * having built it with GinFormTuple. This function adjusts the size
- * fields to match.
- */
- void
- GinShortenTuple(IndexTuple itup, uint32 nipd)
- {
- uint32 newsize;
-
- Assert(nipd <= GinGetNPosting(itup));
-
- newsize = GinGetPostingOffset(itup) + sizeof(ItemPointerData) * nipd;
- newsize = MAXALIGN(newsize);
-
- Assert(newsize <= (itup->t_info & INDEX_SIZE_MASK));
-
- itup->t_info &= ~INDEX_SIZE_MASK;
- itup->t_info |= newsize;
-
- GinSetNPosting(itup, nipd);
}
/*
--- 18,41 ----
#include "utils/rel.h"
/*
! * Read item pointers from leaf data page. Information is stored in the same
! * manner as in leaf data pages.
*/
! void
! ginReadTuple(GinState *ginstate, OffsetNumber attnum,
! IndexTuple itup, ItemPointerData *ipd)
{
! Pointer ptr;
! int nipd = GinGetNPosting(itup), i;
! ItemPointerData ip = {{0,0},0};
! ptr = GinGetPosting(itup);
! for (i = 0; i < nipd; i++)
{
! ptr = ginDataPageLeafReadItemPointer(ptr, &ip);
! ipd[i] = ip;
}
}
/*
*** a/src/backend/access/gin/ginfast.c
--- b/src/backend/access/gin/ginfast.c
***************
*** 23,28 ****
--- 23,29 ----
#include "miscadmin.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+ #include "access/htup_details.h"
#define GIN_PAGE_FREESIZE \
***************
*** 437,442 **** ginHeapTupleFastInsert(GinState *ginstate, GinTupleCollector *collector)
--- 438,526 ----
ginInsertCleanup(ginstate, false, NULL);
}
+ static IndexTuple
+ GinFastFormTuple(GinState *ginstate,
+ OffsetNumber attnum, Datum key, GinNullCategory category)
+ {
+ Datum datums[2];
+ bool isnull[2];
+ IndexTuple itup;
+ uint32 newsize;
+
+ /* Build the basic tuple: optional column number, plus key datum */
+
+ if (ginstate->oneCol)
+ {
+ datums[0] = key;
+ isnull[0] = (category != GIN_CAT_NORM_KEY);
+ }
+ else
+ {
+ datums[0] = UInt16GetDatum(attnum);
+ isnull[0] = false;
+ datums[1] = key;
+ isnull[1] = (category != GIN_CAT_NORM_KEY);
+ }
+
+ itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);
+
+ /*
+ * Place category to the last byte of index tuple extending it's size if
+ * needed
+ */
+ newsize = IndexTupleSize(itup);
+
+ if (category != GIN_CAT_NORM_KEY)
+ {
+ uint32 minsize;
+
+ Assert(IndexTupleHasNulls(itup));
+ minsize = IndexInfoFindDataOffset(itup->t_info) +
+ heap_compute_data_size(ginstate->tupdesc[attnum - 1], datums, isnull) +
+ sizeof(GinNullCategory);
+ newsize = Max(newsize, minsize);
+ }
+
+ newsize = MAXALIGN(newsize);
+
+ if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
+ (unsigned long) newsize,
+ (unsigned long) Min(INDEX_SIZE_MASK,
+ GinMaxItemSize),
+ RelationGetRelationName(ginstate->index))));
+ pfree(itup);
+ return NULL;
+ }
+
+ /*
+ * Resize tuple if needed
+ */
+ if (newsize != IndexTupleSize(itup))
+ {
+ itup = repalloc(itup, newsize);
+
+ /* set new size in tuple header */
+ itup->t_info &= ~INDEX_SIZE_MASK;
+ itup->t_info |= newsize;
+ }
+
+ /*
+ * Insert category byte, if needed
+ */
+ if (category != GIN_CAT_NORM_KEY)
+ {
+ Assert(IndexTupleHasNulls(itup));
+ GinSetNullCategory(itup, ginstate, category);
+ }
+
+ return itup;
+ }
+
+
/*
* Create temporary index tuples for a single indexable item (one index column
* for the heap tuple specified by ht_ctid), and append them to the array
***************
*** 486,493 **** ginHeapTupleFastCollect(GinState *ginstate,
{
IndexTuple itup;
! itup = GinFormTuple(ginstate, attnum, entries[i], categories[i],
! NULL, 0, true);
itup->t_tid = *ht_ctid;
collector->tuples[collector->ntuples++] = itup;
collector->sumsize += IndexTupleSize(itup);
--- 570,576 ----
{
IndexTuple itup;
! itup = GinFastFormTuple(ginstate, attnum, entries[i], categories[i]);
itup->t_tid = *ht_ctid;
collector->tuples[collector->ntuples++] = itup;
collector->sumsize += IndexTupleSize(itup);
*** a/src/backend/access/gin/ginget.c
--- b/src/backend/access/gin/ginget.c
***************
*** 73,90 **** findItemInPostingPage(Page page, ItemPointer item, OffsetNumber *off)
{
OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
int res;
if (GinPageGetOpaque(page)->flags & GIN_DELETED)
/* page was deleted by concurrent vacuum */
return false;
/*
* scan page to find equal or first greater value
*/
for (*off = FirstOffsetNumber; *off <= maxoff; (*off)++)
{
! res = ginCompareItemPointers(item, (ItemPointer) GinDataPageGetItem(page, *off));
if (res <= 0)
return true;
}
--- 73,94 ----
{
OffsetNumber maxoff = GinPageGetOpaque(page)->maxoff;
int res;
+ Pointer ptr;
+ ItemPointerData iptr = {{0, 0}, 0};
if (GinPageGetOpaque(page)->flags & GIN_DELETED)
/* page was deleted by concurrent vacuum */
return false;
+ ptr = GinDataPageGetData(page);
/*
* scan page to find equal or first greater value
*/
for (*off = FirstOffsetNumber; *off <= maxoff; (*off)++)
{
! ptr = ginDataPageLeafReadItemPointer(ptr, &iptr);
+ res = ginCompareItemPointers(item, &iptr);
if (res <= 0)
return true;
}
***************
*** 148,162 **** scanPostingTree(Relation index, GinScanEntry scanEntry,
*/
for (;;)
{
page = BufferGetPage(buffer);
if ((GinPageGetOpaque(page)->flags & GIN_DELETED) == 0 &&
! GinPageGetOpaque(page)->maxoff >= FirstOffsetNumber)
{
! tbm_add_tuples(scanEntry->matchBitmap,
! (ItemPointer) GinDataPageGetItem(page, FirstOffsetNumber),
! GinPageGetOpaque(page)->maxoff, false);
! scanEntry->predictNumberResult += GinPageGetOpaque(page)->maxoff;
}
if (GinPageRightMost(page))
--- 152,176 ----
*/
for (;;)
{
+ OffsetNumber maxoff, i;
+
page = BufferGetPage(buffer);
+ maxoff = GinPageGetOpaque(page)->maxoff;
if ((GinPageGetOpaque(page)->flags & GIN_DELETED) == 0 &&
! maxoff >= FirstOffsetNumber)
{
! ItemPointerData iptr = {{0, 0}, 0};
! Pointer ptr;
!
! ptr = GinDataPageGetData(page);
! for (i = FirstOffsetNumber; i <= maxoff; i++)
! {
! ptr = ginDataPageLeafReadItemPointer(ptr, &iptr);
! tbm_add_tuples(scanEntry->matchBitmap, &iptr, 1, false);
! }
!
! scanEntry->predictNumberResult += maxoff;
}
if (GinPageRightMost(page))
***************
*** 344,351 **** collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
}
else
{
tbm_add_tuples(scanEntry->matchBitmap,
! GinGetPosting(itup), GinGetNPosting(itup), false);
scanEntry->predictNumberResult += GinGetNPosting(itup);
}
--- 358,369 ----
}
else
{
+ ItemPointerData *ipd = (ItemPointerData *)palloc(
+ sizeof(ItemPointerData) * GinGetNPosting(itup));
+ ginReadTuple(btree->ginstate, scanEntry->attnum, itup, ipd);
+
tbm_add_tuples(scanEntry->matchBitmap,
! ipd, GinGetNPosting(itup), false);
scanEntry->predictNumberResult += GinGetNPosting(itup);
}
***************
*** 438,443 **** restartScanEntry:
--- 456,464 ----
BlockNumber rootPostingTree = GinGetPostingTree(itup);
GinPostingTreeScan *gdi;
Page page;
+ OffsetNumber maxoff, i;
+ Pointer ptr;
+ ItemPointerData iptr = {{0,0},0};
/*
* We should unlock entry page before touching posting tree to
***************
*** 465,474 **** restartScanEntry:
/*
* Keep page content in memory to prevent durable page locking
*/
! entry->list = (ItemPointerData *) palloc(BLCKSZ);
! entry->nlist = GinPageGetOpaque(page)->maxoff;
! memcpy(entry->list, GinDataPageGetItem(page, FirstOffsetNumber),
! GinPageGetOpaque(page)->maxoff * sizeof(ItemPointerData));
LockBuffer(entry->buffer, GIN_UNLOCK);
freeGinBtreeStack(gdi->stack);
--- 486,502 ----
/*
* Keep page content in memory to prevent durable page locking
*/
! entry->list = (ItemPointerData *) palloc(BLCKSZ * sizeof(ItemPointerData));
! maxoff = GinPageGetOpaque(page)->maxoff;
! entry->nlist = maxoff;
!
! ptr = GinDataPageGetData(page);
!
! for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
! {
! ptr = ginDataPageLeafReadItemPointer(ptr, &iptr);
! entry->list[i - FirstOffsetNumber] = iptr;
! }
LockBuffer(entry->buffer, GIN_UNLOCK);
freeGinBtreeStack(gdi->stack);
***************
*** 478,485 **** restartScanEntry:
else if (GinGetNPosting(itup) > 0)
{
entry->nlist = GinGetNPosting(itup);
entry->list = (ItemPointerData *) palloc(sizeof(ItemPointerData) * entry->nlist);
! memcpy(entry->list, GinGetPosting(itup), sizeof(ItemPointerData) * entry->nlist);
entry->isFinished = FALSE;
}
}
--- 506,516 ----
else if (GinGetNPosting(itup) > 0)
{
entry->nlist = GinGetNPosting(itup);
+ entry->predictNumberResult = entry->nlist;
entry->list = (ItemPointerData *) palloc(sizeof(ItemPointerData) * entry->nlist);
!
! ginReadTuple(ginstate, entry->attnum, itup, entry->list);
!
entry->isFinished = FALSE;
}
}
***************
*** 583,594 **** entryGetNextItem(GinState *ginstate, GinScanEntry entry)
if (!ItemPointerIsValid(&entry->curItem) ||
findItemInPostingPage(page, &entry->curItem, &entry->offset))
{
/*
* Found position equal to or greater than stored
*/
! entry->nlist = GinPageGetOpaque(page)->maxoff;
! memcpy(entry->list, GinDataPageGetItem(page, FirstOffsetNumber),
! GinPageGetOpaque(page)->maxoff * sizeof(ItemPointerData));
LockBuffer(entry->buffer, GIN_UNLOCK);
--- 614,636 ----
if (!ItemPointerIsValid(&entry->curItem) ||
findItemInPostingPage(page, &entry->curItem, &entry->offset))
{
+ OffsetNumber maxoff, i;
+ Pointer ptr;
+ ItemPointerData iptr = {{0,0},0};
+
/*
* Found position equal to or greater than stored
*/
! maxoff = GinPageGetOpaque(page)->maxoff;
! entry->nlist = maxoff;
!
! ptr = GinDataPageGetData(page);
!
! for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
! {
! ptr = ginDataPageLeafReadItemPointer(ptr, &iptr);
! entry->list[i - FirstOffsetNumber] = iptr;
! }
LockBuffer(entry->buffer, GIN_UNLOCK);
*** a/src/backend/access/gin/gininsert.c
--- b/src/backend/access/gin/gininsert.c
***************
*** 42,55 **** typedef struct
* 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();
--- 42,57 ----
* items[] must be in sorted order with no duplicates.
*/
static BlockNumber
! createPostingTree(GinState *ginstate, ItemPointerData *items, uint32 nitems)
{
BlockNumber blkno;
! Buffer buffer = GinNewBuffer(ginstate->index);
Page page;
+ int i;
+ Pointer ptr;
+ ItemPointerData prev_iptr = {{0,0},0};
/* Assert that the items[] array will fit on one page */
START_CRIT_SECTION();
***************
*** 57,74 **** createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
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;
--- 59,84 ----
page = BufferGetPage(buffer);
blkno = BufferGetBlockNumber(buffer);
GinPageGetOpaque(page)->maxoff = nitems;
+ ptr = GinDataPageGetData(page);
+ for (i = 0; i < nitems; i++)
+ {
+ if (i > 0)
+ prev_iptr = items[i - 1];
+ ptr = ginDataPageLeafWriteItemPointer(ptr, &items[i], &prev_iptr);
+ }
+ Assert(GinDataPageFreeSpacePre(page, ptr) >= 0);
+ updateItemIndexes(page, ginstate);
MarkBufferDirty(buffer);
! if (RelationNeedsWAL(ginstate->index))
{
XLogRecPtr recptr;
XLogRecData rdata[2];
ginxlogCreatePostingTree data;
! data.node = ginstate->index->rd_node;
data.blkno = blkno;
data.nitem = nitems;
***************
*** 78,85 **** createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
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);
--- 88,95 ----
rdata[0].next = &rdata[1];
rdata[1].buffer = InvalidBuffer;
! rdata[1].data = GinDataPageGetData(page);
! rdata[1].len = GinDataPageSize - GinDataPageGetFreeSpace(page);
rdata[1].next = NULL;
recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_CREATE_PTREE, rdata);
***************
*** 93,98 **** createPostingTree(Relation index, ItemPointerData *items, uint32 nitems)
--- 103,239 ----
return blkno;
}
+ /*
+ * Form a tuple for entry tree.
+ *
+ * If the tuple would be too big to be stored, function throws a suitable
+ * error if errorTooBig is TRUE, or returns NULL if errorTooBig is FALSE.
+ *
+ * See src/backend/access/gin/README for a description of the index tuple
+ * format that is being built here. We build on the assumption that we
+ * are making a leaf-level key entry containing a posting list of nipd items.
+ * If the caller is actually trying to make a posting-tree entry, non-leaf
+ * entry, or pending-list entry, it should pass nipd = 0 and then overwrite
+ * the t_tid fields as necessary. In any case, ipd can be NULL to skip
+ * copying any itempointers into the posting list; the caller is responsible
+ * for filling the posting list afterwards, if ipd = NULL and nipd > 0.
+ */
+ static IndexTuple
+ GinFormTuple(GinState *ginstate,
+ OffsetNumber attnum, Datum key, GinNullCategory category,
+ ItemPointerData *ipd,
+ uint32 nipd,
+ bool errorTooBig)
+ {
+ Datum datums[3];
+ bool isnull[3];
+ IndexTuple itup;
+ uint32 newsize;
+ int i;
+ ItemPointerData nullItemPointer = {{0,0},0};
+
+ /* Build the basic tuple: optional column number, plus key datum */
+ if (ginstate->oneCol)
+ {
+ datums[0] = key;
+ isnull[0] = (category != GIN_CAT_NORM_KEY);
+ isnull[1] = true;
+ }
+ else
+ {
+ datums[0] = UInt16GetDatum(attnum);
+ isnull[0] = false;
+ datums[1] = key;
+ isnull[1] = (category != GIN_CAT_NORM_KEY);
+ isnull[2] = true;
+ }
+
+ itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);
+
+ /*
+ * Determine and store offset to the posting list, making sure there is
+ * room for the category byte if needed.
+ *
+ * Note: because index_form_tuple MAXALIGNs the tuple size, there may well
+ * be some wasted pad space. Is it worth recomputing the data length to
+ * prevent that? That would also allow us to Assert that the real data
+ * doesn't overlap the GinNullCategory byte, which this code currently
+ * takes on faith.
+ */
+ newsize = IndexTupleSize(itup);
+
+ GinSetPostingOffset(itup, newsize);
+
+ GinSetNPosting(itup, nipd);
+
+ /*
+ * Add space needed for posting list, if any. Then check that the tuple
+ * won't be too big to store.
+ */
+
+ if (nipd > 0)
+ {
+ newsize = ginCheckPlaceToDataPageLeaf(&ipd[0], &nullItemPointer, newsize);
+ for (i = 1; i < nipd; i++)
+ {
+ newsize = ginCheckPlaceToDataPageLeaf(&ipd[i], &ipd[i - 1], newsize);
+ }
+ }
+
+ if (category != GIN_CAT_NORM_KEY)
+ {
+ Assert(IndexTupleHasNulls(itup));
+ newsize = newsize + sizeof(GinNullCategory);
+ }
+ newsize = MAXALIGN(newsize);
+
+ if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
+ {
+ if (errorTooBig)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
+ (unsigned long) newsize,
+ (unsigned long) Min(INDEX_SIZE_MASK,
+ GinMaxItemSize),
+ RelationGetRelationName(ginstate->index))));
+ pfree(itup);
+ return NULL;
+ }
+
+ /*
+ * Resize tuple if needed
+ */
+ if (newsize != IndexTupleSize(itup))
+ {
+ itup = repalloc(itup, newsize);
+
+ /* set new size in tuple header */
+ itup->t_info &= ~INDEX_SIZE_MASK;
+ itup->t_info |= newsize;
+ }
+
+ /*
+ * Copy in the posting list, if provided
+ */
+ if (nipd > 0)
+ {
+ char *ptr = GinGetPosting(itup);
+ ptr = ginDataPageLeafWriteItemPointer(ptr, &ipd[0], &nullItemPointer);
+ for (i = 1; i < nipd; i++)
+ ptr = ginDataPageLeafWriteItemPointer(ptr, &ipd[i], &ipd[i-1]);
+ }
+
+ /*
+ * Insert category byte, if needed
+ */
+ if (category != GIN_CAT_NORM_KEY)
+ {
+ Assert(IndexTupleHasNulls(itup));
+ GinSetNullCategory(itup, ginstate, category);
+ }
+ return itup;
+ }
/*
* Adds array of item pointers to tuple's posting list, or
***************
*** 111,141 **** addItemPointersToLeafTuple(GinState *ginstate,
Datum key;
GinNullCategory category;
IndexTuple res;
Assert(!GinIsPostingTree(old));
attnum = gintuple_get_attrnum(ginstate, old);
key = gintuple_get_key(ginstate, old, &category);
/* try to build tuple with room for all the items */
res = GinFormTuple(ginstate, attnum, key, category,
! NULL, nitem + GinGetNPosting(old),
! false);
!
! if (res)
! {
! /* good, small enough */
! uint32 newnitem;
!
! /* fill in the posting list with union of old and new TIDs */
! newnitem = ginMergeItemPointers(GinGetPosting(res),
! GinGetPosting(old),
! GinGetNPosting(old),
! items, nitem);
! /* merge might have eliminated some duplicate items */
! GinShortenTuple(res, newnitem);
! }
! else
{
/* posting list would be too big, convert to posting tree */
BlockNumber postingRoot;
--- 252,281 ----
Datum key;
GinNullCategory category;
IndexTuple res;
+ ItemPointerData *newItems, *oldItems;
+ int oldNPosting, newNPosting;
Assert(!GinIsPostingTree(old));
attnum = gintuple_get_attrnum(ginstate, old);
key = gintuple_get_key(ginstate, old, &category);
+ oldNPosting = GinGetNPosting(old);
+ oldItems = (ItemPointerData *) palloc(sizeof(ItemPointerData) * oldNPosting);
+
+ newNPosting = oldNPosting + nitem;
+ newItems = (ItemPointerData *) palloc(sizeof(ItemPointerData) * newNPosting);
+
+ ginReadTuple(ginstate, attnum, old, oldItems);
+
+ newNPosting = ginMergeItemPointers(newItems,
+ items, nitem,
+ oldItems, oldNPosting);
+
/* try to build tuple with room for all the items */
res = GinFormTuple(ginstate, attnum, key, category,
! newItems, newNPosting, false);
! if (!res)
{
/* posting list would be too big, convert to posting tree */
BlockNumber postingRoot;
***************
*** 146,154 **** addItemPointersToLeafTuple(GinState *ginstate,
* surely small enough to fit on one posting-tree page, and should
* already be in order with no duplicates.
*/
! postingRoot = createPostingTree(ginstate->index,
! GinGetPosting(old),
! GinGetNPosting(old));
/* During index build, count the newly-added data page */
if (buildStats)
--- 286,294 ----
* surely small enough to fit on one posting-tree page, and should
* already be in order with no duplicates.
*/
! postingRoot = createPostingTree(ginstate,
! oldItems,
! oldNPosting);
/* During index build, count the newly-added data page */
if (buildStats)
***************
*** 194,199 **** buildFreshLeafTuple(GinState *ginstate,
--- 334,353 ----
{
/* posting list would be too big, build posting tree */
BlockNumber postingRoot;
+ ItemPointerData prevIptr = {{0,0},0};
+ Size size = 0;
+ int itemsCount = 0;
+
+ do
+ {
+ size = ginCheckPlaceToDataPageLeaf(&items[itemsCount], &prevIptr,
+ size);
+ prevIptr = items[itemsCount];
+ itemsCount++;
+ }
+ while (itemsCount < nitem && size < GinDataPageSize);
+ itemsCount--;
+
/*
* Build posting-tree-only result tuple. We do this first so as to
***************
*** 205,220 **** buildFreshLeafTuple(GinState *ginstate,
* Initialize posting tree with as many TIDs as will fit on the first
* page.
*/
! postingRoot = createPostingTree(ginstate->index,
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;
--- 359,374 ----
* Initialize posting tree with as many TIDs as will fit on the first
* page.
*/
! postingRoot = createPostingTree(ginstate,
items,
! itemsCount);
/* During index build, count the newly-added data page */
if (buildStats)
buildStats->nDataPages++;
/* Add any remaining TIDs to the posting tree */
! if (nitem > itemsCount)
{
GinPostingTreeScan *gdi;
***************
*** 222,229 **** buildFreshLeafTuple(GinState *ginstate,
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointers(gdi,
! items + GinMaxLeafDataItems,
! nitem - GinMaxLeafDataItems,
buildStats);
pfree(gdi);
--- 376,383 ----
gdi->btree.isBuild = (buildStats != NULL);
ginInsertItemPointers(gdi,
! items + itemsCount,
! nitem - itemsCount,
buildStats);
pfree(gdi);
*** a/src/backend/access/gin/ginvacuum.c
--- b/src/backend/access/gin/ginvacuum.c
***************
*** 41,80 **** typedef struct
*/
static uint32
! ginVacuumPostingList(GinVacuumState *gvs, ItemPointerData *items, uint32 nitem, ItemPointerData **cleaned)
{
uint32 i,
j = 0;
/*
* just scan over ItemPointer array
*/
for (i = 0; i < nitem; i++)
{
! if (gvs->callback(items + i, gvs->callback_state))
{
gvs->result->tuples_removed += 1;
! if (!*cleaned)
{
! *cleaned = (ItemPointerData *) palloc(sizeof(ItemPointerData) * nitem);
if (i != 0)
! memcpy(*cleaned, items, sizeof(ItemPointerData) * i);
}
}
else
{
gvs->result->num_index_tuples += 1;
if (i != j)
! (*cleaned)[j] = items[i];
j++;
}
}
return j;
}
/*
* fills WAL record for vacuum leaf page
*/
static void
--- 41,206 ----
*/
static uint32
! ginVacuumPostingList(GinVacuumState *gvs, Pointer src, uint32 nitem, Pointer *cleaned, Size size, Size *newSize)
{
uint32 i,
j = 0;
+ ItemPointerData iptr = {{0,0},0}, prevIptr;
+ Pointer dst = NULL, prev, ptr = src;
/*
* just scan over ItemPointer array
*/
+ prevIptr = iptr;
for (i = 0; i < nitem; i++)
{
! prev = ptr;
! ptr = ginDataPageLeafReadItemPointer(ptr, &iptr);
! if (gvs->callback(&iptr, gvs->callback_state))
{
gvs->result->tuples_removed += 1;
! if (!dst)
{
! dst = (Pointer) palloc(size);
! *cleaned = dst;
if (i != 0)
! {
! memcpy(dst, src, prev - src);
! dst += prev - src;
! }
}
}
else
{
gvs->result->num_index_tuples += 1;
if (i != j)
! dst = ginDataPageLeafWriteItemPointer(dst, &iptr, &prevIptr);
j++;
+ prevIptr = iptr;
}
}
+ if (i != j)
+ *newSize = dst - *cleaned;
return j;
}
/*
+ * Form a tuple for entry tree based on already encoded array of item pointers
+ * with additional information.
+ */
+ static IndexTuple
+ GinFormTuple(GinState *ginstate,
+ OffsetNumber attnum, Datum key, GinNullCategory category,
+ Pointer data,
+ Size dataSize,
+ uint32 nipd,
+ bool errorTooBig)
+ {
+ Datum datums[3];
+ bool isnull[3];
+ IndexTuple itup;
+ uint32 newsize;
+
+ /* Build the basic tuple: optional column number, plus key datum */
+ if (ginstate->oneCol)
+ {
+ datums[0] = key;
+ isnull[0] = (category != GIN_CAT_NORM_KEY);
+ isnull[1] = true;
+ }
+ else
+ {
+ datums[0] = UInt16GetDatum(attnum);
+ isnull[0] = false;
+ datums[1] = key;
+ isnull[1] = (category != GIN_CAT_NORM_KEY);
+ isnull[2] = true;
+ }
+
+ itup = index_form_tuple(ginstate->tupdesc[attnum - 1], datums, isnull);
+
+ /*
+ * Determine and store offset to the posting list, making sure there is
+ * room for the category byte if needed.
+ *
+ * Note: because index_form_tuple MAXALIGNs the tuple size, there may well
+ * be some wasted pad space. Is it worth recomputing the data length to
+ * prevent that? That would also allow us to Assert that the real data
+ * doesn't overlap the GinNullCategory byte, which this code currently
+ * takes on faith.
+ */
+ newsize = IndexTupleSize(itup);
+
+ GinSetPostingOffset(itup, newsize);
+
+ GinSetNPosting(itup, nipd);
+
+ /*
+ * Add space needed for posting list, if any. Then check that the tuple
+ * won't be too big to store.
+ */
+
+ if (nipd > 0)
+ {
+ newsize += dataSize;
+ }
+
+ if (category != GIN_CAT_NORM_KEY)
+ {
+ Assert(IndexTupleHasNulls(itup));
+ newsize = newsize + sizeof(GinNullCategory);
+ }
+ newsize = MAXALIGN(newsize);
+
+ if (newsize > Min(INDEX_SIZE_MASK, GinMaxItemSize))
+ {
+ if (errorTooBig)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("index row size %lu exceeds maximum %lu for index \"%s\"",
+ (unsigned long) newsize,
+ (unsigned long) Min(INDEX_SIZE_MASK,
+ GinMaxItemSize),
+ RelationGetRelationName(ginstate->index))));
+ pfree(itup);
+ return NULL;
+ }
+
+ /*
+ * Resize tuple if needed
+ */
+ if (newsize != IndexTupleSize(itup))
+ {
+ itup = repalloc(itup, newsize);
+
+ /* set new size in tuple header */
+ itup->t_info &= ~INDEX_SIZE_MASK;
+ itup->t_info |= newsize;
+ }
+
+ /*
+ * Copy in the posting list, if provided
+ */
+ if (nipd > 0)
+ {
+ char *ptr = GinGetPosting(itup);
+ memcpy(ptr, data, dataSize);
+ }
+
+ /*
+ * Insert category byte, if needed
+ */
+ if (category != GIN_CAT_NORM_KEY)
+ {
+ Assert(IndexTupleHasNulls(itup));
+ GinSetNullCategory(itup, ginstate, category);
+ }
+ return itup;
+ }
+
+ /*
* fills WAL record for vacuum leaf page
*/
static void
***************
*** 101,107 **** xlogVacuumPage(Relation index, Buffer buffer)
backup = GinDataPageGetData(page);
data.nitem = GinPageGetOpaque(page)->maxoff;
if (data.nitem)
! len = MAXALIGN(sizeof(ItemPointerData) * data.nitem);
}
else
{
--- 227,233 ----
backup = GinDataPageGetData(page);
data.nitem = GinPageGetOpaque(page)->maxoff;
if (data.nitem)
! len = MAXALIGN(GinDataPageSize - GinDataPageGetFreeSpace(page));
}
else
{
***************
*** 178,187 **** ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno, bool isRoot,
{
OffsetNumber newMaxOff,
oldMaxOff = GinPageGetOpaque(page)->maxoff;
! ItemPointerData *cleaned = NULL;
newMaxOff = ginVacuumPostingList(gvs,
! (ItemPointer) GinDataPageGetData(page), oldMaxOff, &cleaned);
/* saves changes about deleted tuple ... */
if (oldMaxOff != newMaxOff)
--- 304,315 ----
{
OffsetNumber newMaxOff,
oldMaxOff = GinPageGetOpaque(page)->maxoff;
! Pointer cleaned = NULL;
! Size newSize;
newMaxOff = ginVacuumPostingList(gvs,
! GinDataPageGetData(page), oldMaxOff, &cleaned,
! GinDataPageSize - GinDataPageGetFreeSpace(page), &newSize);
/* saves changes about deleted tuple ... */
if (oldMaxOff != newMaxOff)
***************
*** 189,197 **** ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno, bool isRoot,
START_CRIT_SECTION();
if (newMaxOff > 0)
! memcpy(GinDataPageGetData(page), cleaned, sizeof(ItemPointerData) * newMaxOff);
pfree(cleaned);
GinPageGetOpaque(page)->maxoff = newMaxOff;
MarkBufferDirty(buffer);
xlogVacuumPage(gvs->index, buffer);
--- 317,327 ----
START_CRIT_SECTION();
if (newMaxOff > 0)
! memcpy(GinDataPageGetData(page), cleaned, newSize);
!
pfree(cleaned);
GinPageGetOpaque(page)->maxoff = newMaxOff;
+ updateItemIndexes(page, &gvs->ginstate);
MarkBufferDirty(buffer);
xlogVacuumPage(gvs->index, buffer);
***************
*** 520,527 **** ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
* if we already create temporary page, we will make changes in
* place
*/
! ItemPointerData *cleaned = (tmppage == origpage) ? NULL : GinGetPosting(itup);
! uint32 newN = ginVacuumPostingList(gvs, GinGetPosting(itup), GinGetNPosting(itup), &cleaned);
if (GinGetNPosting(itup) != newN)
{
--- 650,662 ----
* if we already create temporary page, we will make changes in
* place
*/
! Size cleanedSize;
! Pointer cleaned = NULL;
! uint32 newN =
! ginVacuumPostingList(gvs,
! GinGetPosting(itup), GinGetNPosting(itup), &cleaned,
! IndexTupleSize(itup) - GinGetPostingOffset(itup),
! &cleanedSize);
if (GinGetNPosting(itup) != newN)
{
***************
*** 542,564 **** ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint3
*/
tmppage = PageGetTempPageCopy(origpage);
- if (newN > 0)
- {
- Size pos = ((char *) GinGetPosting(itup)) - ((char *) origpage);
-
- memcpy(tmppage + pos, cleaned, sizeof(ItemPointerData) * newN);
- }
-
- pfree(cleaned);
-
/* set itup pointer to new page */
itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
}
attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
key = gintuple_get_key(&gvs->ginstate, itup, &category);
itup = GinFormTuple(&gvs->ginstate, attnum, key, category,
! GinGetPosting(itup), newN, true);
PageIndexTupleDelete(tmppage, i);
if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false, false) != i)
--- 677,692 ----
*/
tmppage = PageGetTempPageCopy(origpage);
/* set itup pointer to new page */
itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
}
attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
key = gintuple_get_key(&gvs->ginstate, itup, &category);
+ /* FIXME */
itup = GinFormTuple(&gvs->ginstate, attnum, key, category,
! cleaned, cleanedSize, newN, true);
! pfree(cleaned);
PageIndexTupleDelete(tmppage, i);
if (PageAddItem(tmppage, (Item) itup, IndexTupleSize(itup), i, false, false) != i)
*** a/src/backend/access/gin/ginxlog.c
--- b/src/backend/access/gin/ginxlog.c
***************
*** 106,114 **** static void
ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record)
{
ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree *) XLogRecGetData(record);
! ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree));
Buffer buffer;
Page page;
/* Backup blocks are not used in create_ptree records */
Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
--- 106,117 ----
ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record)
{
ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree *) XLogRecGetData(record);
! Pointer ptr = XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree), tmp;
Buffer buffer;
Page page;
+ GinState ginstate;
+ ItemPointerData iptr = {{0, 0}, 0};
+ OffsetNumber i;
/* Backup blocks are not used in create_ptree records */
Assert(!(record->xl_info & XLR_BKP_BLOCK_MASK));
***************
*** 118,128 **** ginRedoCreatePTree(XLogRecPtr lsn, XLogRecord *record)
page = (Page) BufferGetPage(buffer);
GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
! memcpy(GinDataPageGetData(page), items, sizeof(ItemPointerData) * data->nitem);
GinPageGetOpaque(page)->maxoff = data->nitem;
PageSetLSN(page, lsn);
MarkBufferDirty(buffer);
UnlockReleaseBuffer(buffer);
}
--- 121,139 ----
page = (Page) BufferGetPage(buffer);
GinInitBuffer(buffer, GIN_DATA | GIN_LEAF);
!
! tmp = ptr;
! for (i = 1; i <= data->nitem; i++)
! tmp = ginDataPageLeafReadItemPointer(tmp, &iptr);
!
! memcpy(GinDataPageGetData(page), ptr, tmp - ptr);
!
GinPageGetOpaque(page)->maxoff = data->nitem;
PageSetLSN(page, lsn);
+ updateItemIndexes(page, &ginstate);
+
MarkBufferDirty(buffer);
UnlockReleaseBuffer(buffer);
}
***************
*** 182,195 **** ginRedoInsert(XLogRecPtr lsn, XLogRecord *record)
if (data->isLeaf)
{
! OffsetNumber i;
! ItemPointerData *items = (ItemPointerData *) (XLogRecGetData(record) + sizeof(ginxlogInsert));
Assert(GinPageIsLeaf(page));
Assert(data->updateBlkno == InvalidBlockNumber);
! for (i = 0; i < data->nitem; i++)
! GinDataPageAddItem(page, items + i, data->offset + i);
}
else
{
--- 193,241 ----
if (data->isLeaf)
{
! ItemPointer startIptr = (ItemPointer) (XLogRecGetData(record) + sizeof(ginxlogInsert));
! OffsetNumber i, maxoff = GinPageGetOpaque(page)->maxoff, j;
! Pointer dataPtr = (Pointer)(startIptr + 1);
! GinState ginstate;
! ItemPointerData iptr = {{0, 0}, 0}, prev_iptr;
! char pageCopy[BLCKSZ];
! Pointer ptr, destPtr, dataFinish;
Assert(GinPageIsLeaf(page));
Assert(data->updateBlkno == InvalidBlockNumber);
! memcpy(pageCopy, page, BLCKSZ);
! ptr = GinDataPageGetData(page);
! for (i = 1; i < data->offset ; i++)
! {
! ptr = ginDataPageLeafReadItemPointer(ptr, &iptr);
! }
!
! ptr = GinDataPageGetData(pageCopy);
! for (i = 1; i < data->offset ; i++)
! {
! ptr = ginDataPageLeafReadItemPointer(ptr, &iptr);
! }
!
! prev_iptr = iptr;
! destPtr = page + (ptr - pageCopy);
!
! dataFinish = dataPtr;
! for (j = 0; j < data->nitem; j++)
! dataFinish = ginDataPageLeafReadItemPointer(dataFinish, &prev_iptr);
!
! memcpy(destPtr, dataPtr, dataFinish - dataPtr);
! destPtr += dataFinish - dataPtr;
!
! for (; i <= maxoff; i++)
! {
! ptr = ginDataPageLeafReadItemPointer(ptr, &iptr);
! destPtr = ginDataPageLeafWriteItemPointer(destPtr, &iptr, &prev_iptr);
! prev_iptr = iptr;
! }
!
! GinPageGetOpaque(page)->maxoff = maxoff + data->nitem;
! updateItemIndexes(page, &ginstate);
}
else
{
***************
*** 255,260 **** ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
--- 301,307 ----
Page lpage,
rpage;
uint32 flags = 0;
+ GinState ginstate;
if (data->isLeaf)
flags |= GIN_LEAF;
***************
*** 284,310 **** ginRedoSplit(XLogRecPtr lsn, XLogRecord *record)
OffsetNumber i;
ItemPointer bound;
! for (i = 0; i < data->separator; i++)
{
! GinDataPageAddItem(lpage, ptr, InvalidOffsetNumber);
! ptr += sizeofitem;
}
!
! for (i = data->separator; i < data->nitem; i++)
{
! GinDataPageAddItem(rpage, ptr, InvalidOffsetNumber);
! ptr += sizeofitem;
! }
! /* set up right key */
! bound = GinDataPageGetRightBound(lpage);
! if (data->isLeaf)
! *bound = *(ItemPointerData *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff);
! else
! *bound = ((PostingItem *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff))->key;
! bound = GinDataPageGetRightBound(rpage);
! *bound = data->rightbound;
}
else
{
--- 331,391 ----
OffsetNumber i;
ItemPointer bound;
! if (data->isLeaf)
{
! Pointer tmp, ptr2;
! ItemPointerData iptr = {{0, 0}, 0};
! Size lsize, rsize;
!
! tmp = ptr;
! for (i = 1; i <= data->separator; i++)
! tmp = ginDataPageLeafReadItemPointer(tmp, &iptr);
! lsize = tmp - ptr;
! ptr2 = ptr + MAXALIGN(lsize);
! tmp = ptr2;
! for (; i <= data->nitem; i++)
! tmp = ginDataPageLeafReadItemPointer(tmp, &iptr);
! rsize = tmp - ptr2;
!
! Assert(lsize < GinDataPageSize);
! Assert(rsize < GinDataPageSize);
!
! memcpy(GinDataPageGetData(lpage), ptr, lsize);
! memcpy(GinDataPageGetData(rpage), ptr2, rsize);
!
! GinPageGetOpaque(lpage)->maxoff = data->separator;
! GinPageGetOpaque(rpage)->maxoff = data->nitem - data->separator;
! *GinDataPageGetRightBound(lpage) = updateItemIndexes(lpage, &ginstate);
! updateItemIndexes(rpage, &ginstate);
!
! *GinDataPageGetRightBound(rpage) = data->rightbound;
!
! Assert(GinPageGetOpaque(lpage)->flags == flags);
! Assert(GinPageGetOpaque(rpage)->flags == flags);
}
! else
{
! for (i = 0; i < data->separator; i++)
! {
! GinDataPageAddItem(lpage, ptr, InvalidOffsetNumber);
! ptr += sizeofitem;
! }
! for (i = data->separator; i < data->nitem; i++)
! {
! GinDataPageAddItem(rpage, ptr, InvalidOffsetNumber);
! ptr += sizeofitem;
! }
! /* set up right key */
! bound = GinDataPageGetRightBound(lpage);
! if (data->isLeaf)
! *bound = *(ItemPointerData *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff);
! else
! *bound = ((PostingItem *) GinDataPageGetItem(lpage, GinPageGetOpaque(lpage)->maxoff))->key;
! bound = GinDataPageGetRightBound(rpage);
! *bound = data->rightbound;
! }
}
else
{
***************
*** 390,399 **** ginRedoVacuumPage(XLogRecPtr lsn, XLogRecord *record)
{
if (GinPageIsData(page))
{
! memcpy(GinDataPageGetData(page),
! XLogRecGetData(record) + sizeof(ginxlogVacuumPage),
! data->nitem * GinSizeOfDataPageItem(page));
! GinPageGetOpaque(page)->maxoff = data->nitem;
}
else
{
--- 471,500 ----
{
if (GinPageIsData(page))
{
! if (GinPageIsLeaf(page))
! {
! GinState ginstate;
! ItemPointerData iptr = {{0, 0}, 0};
! Pointer ptr, tmp;
! OffsetNumber i;
!
! ptr = XLogRecGetData(record) + sizeof(ginxlogVacuumPage);
! tmp = ptr;
! for (i = 1; i <= data->nitem; i++)
! tmp = ginDataPageLeafReadItemPointer(tmp, &iptr);
!
! memcpy(GinDataPageGetData(page), ptr, tmp - ptr);
!
! GinPageGetOpaque(page)->maxoff = data->nitem;
! updateItemIndexes(page, &ginstate);
! }
! else
! {
! memcpy(GinDataPageGetData(page),
! XLogRecGetData(record) + sizeof(ginxlogVacuumPage),
! data->nitem * GinSizeOfDataPageItem(page));
! GinPageGetOpaque(page)->maxoff = data->nitem;
! }
}
else
{
***************
*** 802,813 **** ginContinueSplit(ginIncompleteSplit *split)
ginPrepareDataScan(&btree, reln);
PostingItemSetBlockNumber(&(btree.pitem), split->leftBlkno);
! if (GinPageIsLeaf(page))
! btree.pitem.key = *(ItemPointerData *) GinDataPageGetItem(page,
! GinPageGetOpaque(page)->maxoff);
! else
! btree.pitem.key = ((PostingItem *) GinDataPageGetItem(page,
! GinPageGetOpaque(page)->maxoff))->key;
}
btree.rightblkno = split->rightBlkno;
--- 903,909 ----
ginPrepareDataScan(&btree, reln);
PostingItemSetBlockNumber(&(btree.pitem), split->leftBlkno);
! btree.pitem.key = *GinDataPageGetRightBound(page);
}
btree.rightblkno = split->rightBlkno;
*** a/src/include/access/gin_private.h
--- b/src/include/access/gin_private.h
***************
*** 196,205 **** typedef signed char GinNullCategory;
#define GinCategoryOffset(itup,ginstate) \
(IndexInfoFindDataOffset((itup)->t_info) + \
((ginstate)->oneCol ? 0 : sizeof(int16)))
! #define GinGetNullCategory(itup,ginstate) \
(*((GinNullCategory *) ((char*)(itup) + GinCategoryOffset(itup,ginstate))))
#define GinSetNullCategory(itup,ginstate,c) \
! (*((GinNullCategory *) ((char*)(itup) + GinCategoryOffset(itup,ginstate))) = (c))
/*
* Access macros for leaf-page entry tuples (see discussion in README)
--- 196,211 ----
#define GinCategoryOffset(itup,ginstate) \
(IndexInfoFindDataOffset((itup)->t_info) + \
((ginstate)->oneCol ? 0 : sizeof(int16)))
! /*#define GinGetNullCategory(itup,ginstate) \
(*((GinNullCategory *) ((char*)(itup) + GinCategoryOffset(itup,ginstate))))
#define GinSetNullCategory(itup,ginstate,c) \
! (*((GinNullCategory *) ((char*)(itup) + GinCategoryOffset(itup,ginstate))) = (c))*/
!
! #define GinGetNullCategory(itup,ginstate) \
! (*((GinNullCategory *) ((char*)(itup) + IndexTupleSize(itup) - sizeof(GinNullCategory))))
! #define GinSetNullCategory(itup,ginstate,c) \
! (*((GinNullCategory *) ((char*)(itup) + IndexTupleSize(itup) - sizeof(GinNullCategory))) = (c))
!
/*
* Access macros for leaf-page entry tuples (see discussion in README)
***************
*** 213,223 **** typedef signed char GinNullCategory;
#define GinGetPostingOffset(itup) GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinSetPostingOffset(itup,n) ItemPointerSetBlockNumber(&(itup)->t_tid,n)
! #define GinGetPosting(itup) ((ItemPointer) ((char*)(itup) + GinGetPostingOffset(itup)))
#define GinMaxItemSize \
MAXALIGN_DOWN(((BLCKSZ - SizeOfPageHeaderData - \
! MAXALIGN(sizeof(GinPageOpaqueData))) / 3 - sizeof(ItemIdData)))
/*
* Access macros for non-leaf entry tuples
--- 219,229 ----
#define GinGetPostingOffset(itup) GinItemPointerGetBlockNumber(&(itup)->t_tid)
#define GinSetPostingOffset(itup,n) ItemPointerSetBlockNumber(&(itup)->t_tid,n)
! #define GinGetPosting(itup) ((Pointer) ((char*)(itup) + GinGetPostingOffset(itup)))
#define GinMaxItemSize \
MAXALIGN_DOWN(((BLCKSZ - SizeOfPageHeaderData - \
! MAXALIGN(sizeof(GinPageOpaqueData))) / 6 - sizeof(ItemIdData)))
/*
* Access macros for non-leaf entry tuples
***************
*** 255,260 **** typedef signed char GinNullCategory;
--- 261,289 ----
#define GinListPageSize \
( BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(GinPageOpaqueData)) )
+ typedef struct
+ {
+ ItemPointerData iptr;
+ OffsetNumber offsetNumer;
+ uint16 pageOffset;
+ } GinDataLeafItemIndex;
+
+ #define GinDataLeafIndexCount 32
+
+ #define GinDataPageSize \
+ (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) \
+ - MAXALIGN(sizeof(ItemPointerData)) \
+ - MAXALIGN(sizeof(GinPageOpaqueData)) \
+ - MAXALIGN(sizeof(GinDataLeafItemIndex) * GinDataLeafIndexCount))
+
+ #define GinDataPageFreeSpacePre(page,ptr) \
+ (GinDataPageSize \
+ - ((ptr) - GinDataPageGetData(page)))
+
+ #define GinPageGetIndexes(page) \
+ ((GinDataLeafItemIndex *)(GinDataPageGetData(page) + GinDataPageSize))
+
+
/*
* Storage type for GIN's reloptions
*/
***************
*** 519,536 **** extern void ginInsertValue(GinBtree btree, GinBtreeStack *stack,
extern void ginFindParents(GinBtree btree, GinBtreeStack *stack, BlockNumber rootBlkno);
/* ginentrypage.c */
- extern IndexTuple GinFormTuple(GinState *ginstate,
- OffsetNumber attnum, Datum key, GinNullCategory category,
- ItemPointerData *ipd, uint32 nipd, bool errorTooBig);
- extern void GinShortenTuple(IndexTuple itup, uint32 nipd);
extern void ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
Datum key, GinNullCategory category,
GinState *ginstate);
extern void ginEntryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf);
extern IndexTuple ginPageGetLinkItup(Buffer buf);
/* gindatapage.c */
extern int ginCompareItemPointers(ItemPointer a, ItemPointer b);
extern uint32 ginMergeItemPointers(ItemPointerData *dst,
ItemPointerData *a, uint32 na,
ItemPointerData *b, uint32 nb);
--- 548,567 ----
extern void ginFindParents(GinBtree btree, GinBtreeStack *stack, BlockNumber rootBlkno);
/* ginentrypage.c */
extern void ginPrepareEntryScan(GinBtree btree, OffsetNumber attnum,
Datum key, GinNullCategory category,
GinState *ginstate);
extern void ginEntryFillRoot(GinBtree btree, Buffer root, Buffer lbuf, Buffer rbuf);
extern IndexTuple ginPageGetLinkItup(Buffer buf);
+ extern void ginReadTuple(GinState *ginstate, OffsetNumber attnum,
+ IndexTuple itup, ItemPointerData *ipd);
+ extern ItemPointerData updateItemIndexes(Page page, GinState *ginstate);
/* gindatapage.c */
extern int ginCompareItemPointers(ItemPointer a, ItemPointer b);
+ extern char *ginDataPageLeafWriteItemPointer(char *ptr, ItemPointer iptr, ItemPointer prev);
+ extern Size ginCheckPlaceToDataPageLeaf(ItemPointer iptr, ItemPointer prev,
+ Size size);
extern uint32 ginMergeItemPointers(ItemPointerData *dst,
ItemPointerData *a, uint32 na,
ItemPointerData *b, uint32 nb);
***************
*** 724,727 **** extern void ginHeapTupleFastCollect(GinState *ginstate,
--- 755,805 ----
extern void ginInsertCleanup(GinState *ginstate,
bool vac_delay, IndexBulkDeleteResult *stats);
+ /*
+ * Function for reading packed ItemPointers. Used in various .c files and
+ * have to be inline for being fast.
+ *
+ * Read next item pointer from leaf data page. Replaces current item pointer
+ * with the next one. Zero item pointer should be passed in order to read the
+ * first item pointer.
+ */
+ static inline char *
+ ginDataPageLeafReadItemPointer(char *ptr, ItemPointer iptr)
+ {
+ uint32 blockNumberIncr;
+ uint16 offset;
+ int i;
+ uint8 v;
+
+ i = 0;
+ blockNumberIncr = 0;
+ do
+ {
+ v = *ptr;
+ ptr++;
+ blockNumberIncr |= (uint32) (v & (~HIGHBIT)) << i;
+ i += 7;
+ }
+ while (IS_HIGHBIT_SET(v));
+
+ blockNumberIncr += iptr->ip_blkid.bi_lo + (iptr->ip_blkid.bi_hi << 16);
+
+ iptr->ip_blkid.bi_lo = blockNumberIncr & 0xFFFF;
+ iptr->ip_blkid.bi_hi = (blockNumberIncr >> 16) & 0xFFFF;
+
+ i = 0;
+ offset = 0;
+ do
+ {
+ v = *ptr;
+ ptr++;
+ offset |= (uint32) (v & (~HIGHBIT)) << i;
+ i += 7;
+ } while(IS_HIGHBIT_SET(v));
+
+ iptr->ip_posid = offset;
+
+ return ptr;
+ }
+
#endif /* GIN_PRIVATE_H */
view thread (119+ messages) latest in thread
Message-ID: <51CEA13C.8040103@vmware.com>
Permalink: ../51CEA13C.8040103@vmware.com/
Also on: postgresql.org/message-id/51CEA13C.8040103@vmware.com
copy link · copy postgr.es · copy git am
reply Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: hlinnakangas@vmware.com, aekorotkov@gmail.com
Subject: Re: GIN improvements part 1: additional information
In-Reply-To: <51CEA13C.8040103@vmware.com>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox