From ab9a21492991e0f331ac830862ed557ca2574c44 Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Mon, 4 Jul 2016 10:30:37 +0500
Subject: [PATCH] PageIndexTupleOverwrite

---
 src/backend/access/gist/gist.c     |  20 ++++++-
 src/backend/storage/page/bufpage.c | 104 +++++++++++++++++++++++++++++++++++++
 src/include/storage/bufpage.h      |   1 +
 3 files changed, 123 insertions(+), 2 deletions(-)

diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index fdf0c5a..239971b 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 on-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/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index f2a07f2..e75f42d 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -707,6 +707,110 @@ 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		tup;
+	int		size_diff;
+	int		oldsize;
+	unsigned	offset;
+	int			newsize;
+	int			nline;
+
+	/*
+	 * 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)));
+
+
+
+	nline = PageGetMaxOffsetNumber(page);
+	if ((int) offnum <= 0 || (int) offnum > nline)
+		elog(ERROR, "invalid index offnum: %u", offnum);
+
+	tup = PageGetItemId(page, offnum);
+
+
+	Assert(ItemIdHasStorage(tup));
+
+	newsize = IndexTupleSize(newtup);
+	oldsize = ItemIdGetLength(tup);
+	/*may have negative size here if new tuple is larger*/
+	size_diff = oldsize-newsize;
+	offset = ItemIdGetOffset(tup);
+
+
+	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 pointer: 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;
+
+	//elog(WARNING,"old %u new %u diff %d offset %u offnum %u movesize %d",oldsize,newsize,size_diff,offset, offnum,(int) (offset - phdr->pd_upper));
+
+	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 = 1; i <= nline; i++)
+		{
+			ItemId		ii = PageGetItemId(phdr, i);
+
+			Assert(ItemIdHasStorage(ii));
+			if (ItemIdGetOffset(ii) <= offset)
+				ii->lp_off += size_diff;
+		}
+	}
+
+	/*Fix updated tuple length*/
+	tup = PageGetItemId(page, offnum);
+	tup->lp_len = newsize;
+
+	/*now place new tuple on page*/
+	memmove((char *) page + offset + size_diff, 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

