From 27707c89a7946018169adc840db4f5aa34e03d10 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Tue, 21 Apr 2020 09:40:34 -0700
Subject: [PATCH v1] Fix nbtdedup.c single value strategy issue.

---
 src/include/access/nbtree.h          |  1 +
 src/backend/access/nbtree/nbtdedup.c | 20 +++++++++++++++-----
 src/backend/access/nbtree/nbtsort.c  |  1 +
 src/backend/access/nbtree/nbtxlog.c  |  1 +
 4 files changed, 18 insertions(+), 5 deletions(-)

diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 3b2bcb22a7..79506c748b 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -739,6 +739,7 @@ typedef struct BTDedupStateData
 {
 	/* Deduplication status info for entire pass over page */
 	bool		deduplicate;	/* Still deduplicating page? */
+	int			nmaxitems;		/* Number of max-sized tuples so far */
 	Size		maxpostingsize; /* Limit on size of final tuple */
 
 	/* Metadata about base tuple of current pending posting list */
diff --git a/src/backend/access/nbtree/nbtdedup.c b/src/backend/access/nbtree/nbtdedup.c
index b20faf693d..f1a884a220 100644
--- a/src/backend/access/nbtree/nbtdedup.c
+++ b/src/backend/access/nbtree/nbtdedup.c
@@ -62,7 +62,6 @@ _bt_dedup_one_page(Relation rel, Buffer buf, Relation heapRel,
 	Page		page = BufferGetPage(buf);
 	BTPageOpaque opaque;
 	Page		newpage;
-	int			newpagendataitems = 0;
 	OffsetNumber deletable[MaxIndexTuplesPerPage];
 	BTDedupState state;
 	int			ndeletable = 0;
@@ -124,6 +123,7 @@ _bt_dedup_one_page(Relation rel, Buffer buf, Relation heapRel,
 	 */
 	state = (BTDedupState) palloc(sizeof(BTDedupStateData));
 	state->deduplicate = true;
+	state->nmaxitems = 0;
 	state->maxpostingsize = Min(BTMaxItemSize(page) / 2, INDEX_SIZE_MASK);
 	/* Metadata about base tuple of current pending posting list */
 	state->base = NULL;
@@ -204,7 +204,6 @@ _bt_dedup_one_page(Relation rel, Buffer buf, Relation heapRel,
 			 * reset the state and move on without modifying the page.
 			 */
 			pagesaving += _bt_dedup_finish_pending(newpage, state);
-			newpagendataitems++;
 
 			if (singlevalstrat)
 			{
@@ -221,9 +220,9 @@ _bt_dedup_one_page(Relation rel, Buffer buf, Relation heapRel,
 				 * current call generated the maxpostingsize-capped duplicate
 				 * tuples at the start of the page.
 				 */
-				if (newpagendataitems == 5)
+				if (state->nmaxitems == 5)
 					_bt_singleval_fillfactor(page, state, newitemsz);
-				else if (newpagendataitems == 6)
+				else if (state->nmaxitems == 6)
 				{
 					state->deduplicate = false;
 					singlevalstrat = false; /* won't be back here */
@@ -237,7 +236,6 @@ _bt_dedup_one_page(Relation rel, Buffer buf, Relation heapRel,
 
 	/* Handle the last item */
 	pagesaving += _bt_dedup_finish_pending(newpage, state);
-	newpagendataitems++;
 
 	/*
 	 * If no items suitable for deduplication were found, newpage must be
@@ -404,7 +402,19 @@ _bt_dedup_save_htid(BTDedupState state, IndexTuple itup)
 						   (state->nhtids + nhtids) * sizeof(ItemPointerData));
 
 	if (mergedtupsz > state->maxpostingsize)
+	{
+		/*
+		 * Count this as an oversized item for single value strategy, though
+		 * only when there will be 20 TIDs in final posting list tuple.  This
+		 * limit (which is arbitrary) avoids confusion about how many 1/6 of a
+		 * page tuples have really been encountered/created by the current
+		 * deduplication pass.
+		 */
+		if (state->nhtids > 20)
+			state->nmaxitems++;
+
 		return false;
+	}
 
 	/*
 	 * Save heap TIDs to pending posting list tuple -- itup can be merged into
diff --git a/src/backend/access/nbtree/nbtsort.c b/src/backend/access/nbtree/nbtsort.c
index 15f10a29d3..719887b133 100644
--- a/src/backend/access/nbtree/nbtsort.c
+++ b/src/backend/access/nbtree/nbtsort.c
@@ -1310,6 +1310,7 @@ _bt_load(BTWriteState *wstate, BTSpool *btspool, BTSpool *btspool2)
 
 		dstate = (BTDedupState) palloc(sizeof(BTDedupStateData));
 		dstate->deduplicate = true; /* unused */
+		dstate->nmaxitems = 0;	/* unused */
 		dstate->maxpostingsize = 0; /* set later */
 		/* Metadata about base tuple of current pending posting list */
 		dstate->base = NULL;
diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
index 87a8612c28..5bec59d448 100644
--- a/src/backend/access/nbtree/nbtxlog.c
+++ b/src/backend/access/nbtree/nbtxlog.c
@@ -483,6 +483,7 @@ btree_xlog_dedup(XLogReaderState *record)
 
 		state = (BTDedupState) palloc(sizeof(BTDedupStateData));
 		state->deduplicate = true;	/* unused */
+		state->nmaxitems = 0;	/* unused */
 		/* Conservatively use larger maxpostingsize than primary */
 		state->maxpostingsize = BTMaxItemSize(page);
 		state->base = NULL;
-- 
2.25.1

