From b631ead5be1035131ef1a2c9e95b540bb4af5ac5 Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Wed, 6 Jul 2016 22:04:41 +0500
Subject: [PATCH] temp7

---
 src/backend/access/gist/gist.c     |  20 ++++++-
 src/backend/access/gist/gistxlog.c |  22 +++++++-
 src/backend/storage/page/bufpage.c | 106 +++++++++++++++++++++++++++++++++++++
 src/include/storage/bufpage.h      |   1 +
 4 files changed, 145 insertions(+), 4 deletions(-)

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index fdf0c5a..169566c 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -504,8 +504,24 @@ gistplacetopage(Relation rel, Size freespace, GISTSTATE *giststate,
 		 * gistRedoPageUpdateRecord()
 		 */
 		if (OffsetNumberIsValid(oldoffnum))
-			PageIndexTupleDelete(page, oldoffnum);
-		gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
+		{
+			/* if we have just one tuple to update we replace it in-place on page */
+			if(ntup == 1)
+			{
+				PageIndexTupleOverwrite(page, oldoffnum, *itup);
+			}
+			else
+			{
+				/* this corner case is here to support mix calls case (see comment above) */
+				PageIndexTupleDelete(page, oldoffnum);
+				gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
+			}
+		}
+		else
+		{
+			/* just append all tuples at the end of a page */
+			gistfillbuffer(page, itup, ntup, InvalidOffsetNumber);
+		}
 
 		MarkBufferDirty(buffer);
 
diff --git a/src/backend/access/gist/gistxlog.c b/src/backend/access/gist/gistxlog.c
index b48e97c..934d50d 100644
--- a/src/backend/access/gist/gistxlog.c
+++ b/src/backend/access/gist/gistxlog.c
@@ -80,9 +80,26 @@ gistRedoPageUpdateRecord(XLogReaderState *record)
 
 		page = (Page) BufferGetPage(buffer);
 
-		/* Delete old tuples */
-		if (xldata->ntodelete > 0)
+		/* In case of single tuple update GiST calls overwrite
+		 * In all other cases function gistplacetopage deletes
+		 * old tuples and place updated at the end
+		 *  */
+		if ( xldata->ntodelete == 1 && xldata->ntoinsert == 1 )
 		{
+			IndexTuple		itup;
+			OffsetNumber	offnum = *((OffsetNumber *) data);
+			data += sizeof(OffsetNumber);
+			itup = (IndexTuple) data;
+			PageIndexTupleOverwrite(page,offnum,itup);
+			/* set up data pointer to skip PageAddItem loop */
+			data +=IndexTupleSize(itup);
+			Assert(data - begin == datalen);
+			/* correct insertion count for following assert check */
+			ninserted++;
+		}
+		else if (xldata->ntodelete > 0)
+		{
+			/* if it's not in-place update we proceed with deleting old tuples */
 			OffsetNumber *todelete = (OffsetNumber *) data;
 
 			data += sizeof(OffsetNumber) * xldata->ntodelete;
@@ -115,6 +132,7 @@ gistRedoPageUpdateRecord(XLogReaderState *record)
 			}
 		}
 
+		/* check that buffer had exactly same count of tuples */
 		Assert(ninserted == xldata->ntoinsert);
 
 		PageSetLSN(page, lsn);
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index f2a07f2..b196a05 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -707,6 +707,112 @@ PageGetHeapFreeSpace(Page page)
 
 
 /*
+ * PageIndexTupleOverwrite
+ *
+ * This routine does the work of overwriting a tuple on an index page.
+ *
+ * Unlike heap pages, we keep compacted line pointers.
+ */
+void
+PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup)
+{
+	PageHeader	phdr = (PageHeader) page;
+	char		*addr;
+	ItemId		tupid;
+	int			size_diff;
+	int			oldsize;
+	unsigned	offset;
+	int			newsize;
+	unsigned	alignednewsize;
+	int			itemcount;
+
+	/*
+	 * As with PageIndexTupleDelete, paranoia is told to be justified.
+	 */
+	if (phdr->pd_lower < SizeOfPageHeaderData ||
+		phdr->pd_lower > phdr->pd_upper ||
+		phdr->pd_upper > phdr->pd_special ||
+		phdr->pd_special > BLCKSZ)
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg("corrupted page pointers: lower = %u, upper = %u, special = %u",
+						phdr->pd_lower, phdr->pd_upper, phdr->pd_special)));
+
+
+
+	itemcount = PageGetMaxOffsetNumber(page);
+	if ((int) offnum <= 0 || (int) offnum > itemcount)
+		elog(ERROR, "invalid index offnum: %u", offnum);
+
+	tupid = PageGetItemId(page, offnum);
+	/* set the item pointer */
+
+
+	Assert(ItemIdHasStorage(tupid));
+
+	newsize = IndexTupleSize(newtup);
+	alignednewsize = MAXALIGN(newsize);
+	oldsize = ItemIdGetLength(tupid);
+	/* may have negative size here if new tuple is larger */
+	size_diff = oldsize - alignednewsize;
+	offset = ItemIdGetOffset(tupid);
+
+
+	if (offset < phdr->pd_upper || (offset + size_diff) > phdr->pd_special ||
+		offset != MAXALIGN(offset) || size_diff != MAXALIGN(size_diff))
+		ereport(ERROR,
+				(errcode(ERRCODE_DATA_CORRUPTED),
+				 errmsg("corrupted item offset: offset = %u, size = %u",
+						offset, (unsigned int) size_diff)));
+
+
+	/*
+	 * Now move everything between the old upper bound (beginning of tuple
+	 * space) and the end of the overwritten tuple forward, so that space in
+	 * the middle of the page is left free.  If we've just deleted the tuple
+	 * at the beginning of tuple space, then there's no need to do the copy
+	 * (and bcopy on some architectures SEGV's if asked to move zero bytes).
+	 */
+
+	/* beginning of tuple space */
+	addr = (char *) page + phdr->pd_upper;
+
+	/* skip modifications if nothing has to be changed actually */
+	if (size_diff != 0)
+	{
+		int			i;
+		memmove(addr + size_diff, addr, (int) (offset - phdr->pd_upper));
+
+		/* adjust free space boundary pointers */
+		phdr->pd_upper += size_diff;
+
+		/*
+		 * we need to adjust the linp entries that remain.
+		 *
+		 * Anything that used to be before the deleted tuple's data was moved
+		 * forward by the size of the deleted tuple.
+		 */
+
+		for (i = FirstOffsetNumber; i <= itemcount; i++)
+		{
+			ItemId		ii = PageGetItemId(phdr, i);
+
+			Assert(ItemIdHasStorage(ii));
+			if (ItemIdGetOffset(ii) <= offset)
+				ii->lp_off += size_diff;
+		}
+	}
+
+	/* Fix updated tuple length */
+	ItemIdSetNormal(tupid, offset + size_diff, newsize);
+
+
+	/* now place new tuple on page */
+	memmove(PageGetItem(page,tupid), newtup, newsize);
+}
+
+
+/*
  * PageIndexTupleDelete
  *
  * This routine does the work of removing a tuple from an index page.
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index 15cebfc..780e982 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -426,6 +426,7 @@ extern Size PageGetFreeSpace(Page page);
 extern Size PageGetExactFreeSpace(Page page);
 extern Size PageGetHeapFreeSpace(Page page);
 extern void PageIndexTupleDelete(Page page, OffsetNumber offset);
+extern void PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup);
 extern void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems);
 extern void PageIndexDeleteNoCompact(Page page, OffsetNumber *itemnos,
 						 int nitems);
-- 
2.7.0.windows.1

