From ffbba6ee1244901b492fe268ea94f72e35aedf38 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Sat, 5 Apr 2014 22:02:28 +0300
Subject: [PATCH] Fix another palloc in critical section.

Also add a regression test for a GIN index with enough items with the same
key, so that a GIN posting tree gets created. Apparently none of the
existing GIN tests were large enough for that.

This code is new, no backpatching required.
---
 src/backend/access/gin/gindatapage.c       | 35 ++++++++++++----------
 src/test/regress/expected/create_index.out | 14 +++++++++
 src/test/regress/sql/create_index.sql      | 14 +++++++++
 3 files changed, 48 insertions(+), 15 deletions(-)

diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c
index 21ce79fc0b1..70801bd05cb 100644
--- a/src/backend/access/gin/gindatapage.c
+++ b/src/backend/access/gin/gindatapage.c
@@ -1706,22 +1706,16 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
 {
 	BlockNumber blkno;
 	Buffer		buffer;
+	Page		tmppage;
 	Page		page;
 	Pointer		ptr;
 	int			nrootitems;
 	int			rootsize;
 
-	/*
-	 * Create the root page.
-	 */
-	buffer = GinNewBuffer(index);
-	page = BufferGetPage(buffer);
-	blkno = BufferGetBlockNumber(buffer);
-
-	START_CRIT_SECTION();
-
-	GinInitPage(page, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
-	GinPageGetOpaque(page)->rightlink = InvalidBlockNumber;
+	/* Construct the new root page in memory first. */
+	tmppage = (Page) palloc(BLCKSZ);
+	GinInitPage(tmppage, GIN_DATA | GIN_LEAF | GIN_COMPRESSED, BLCKSZ);
+	GinPageGetOpaque(tmppage)->rightlink = InvalidBlockNumber;
 
 	/*
 	 * Write as many of the items to the root page as fit. In segments
@@ -1729,7 +1723,7 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
 	 */
 	nrootitems = 0;
 	rootsize = 0;
-	ptr = (Pointer) GinDataLeafPageGetPostingList(page);
+	ptr = (Pointer) GinDataLeafPageGetPostingList(tmppage);
 	while (nrootitems < nitems)
 	{
 		GinPostingList *segment;
@@ -1750,10 +1744,19 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
 		nrootitems += npacked;
 		pfree(segment);
 	}
-	GinDataLeafPageSetPostingListSize(page, rootsize);
-	MarkBufferDirty(buffer);
+	GinDataLeafPageSetPostingListSize(tmppage, rootsize);
 
-	elog(DEBUG2, "created GIN posting tree with %d items", nrootitems);
+	/*
+	 * All set. Get a new physical page, and copy the in-memory page to it.
+	 */
+	buffer = GinNewBuffer(index);
+	page = BufferGetPage(buffer);
+	blkno = BufferGetBlockNumber(buffer);
+
+	START_CRIT_SECTION();
+
+	PageRestoreTempPage(tmppage, page);
+	MarkBufferDirty(buffer);
 
 	if (RelationNeedsWAL(index))
 	{
@@ -1787,6 +1790,8 @@ createPostingTree(Relation index, ItemPointerData *items, uint32 nitems,
 	if (buildStats)
 		buildStats->nDataPages++;
 
+	elog(DEBUG2, "created GIN posting tree with %d items", nrootitems);
+
 	/*
 	 * Add any remaining TIDs to the newly-created posting tree.
 	 */
diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out
index d10253b9e9b..f13b4f8a08d 100644
--- a/src/test/regress/expected/create_index.out
+++ b/src/test/regress/expected/create_index.out
@@ -2221,6 +2221,20 @@ RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
 --
+-- Try a GIN index with a lot of items with same key. (GIN creates a posting
+-- tree when there are enough duplicates)
+--
+CREATE TABLE array_gin_test (a int[]);
+INSERT INTO array_gin_test SELECT ARRAY[1, g%5, g] FROM generate_series(1, 10000) g;
+CREATE INDEX array_gin_test_idx ON array_gin_test USING gin (a);
+SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}';
+ count 
+-------
+  2000
+(1 row)
+
+DROP TABLE array_gin_test;
+--
 -- HASH
 --
 CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops);
diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql
index 8ac1d1d280f..cd5c58d468c 100644
--- a/src/test/regress/sql/create_index.sql
+++ b/src/test/regress/sql/create_index.sql
@@ -636,6 +636,20 @@ RESET enable_seqscan;
 RESET enable_indexscan;
 RESET enable_bitmapscan;
 
+--
+-- Try a GIN index with a lot of items with same key. (GIN creates a posting
+-- tree when there are enough duplicates)
+--
+CREATE TABLE array_gin_test (a int[]);
+
+INSERT INTO array_gin_test SELECT ARRAY[1, g%5, g] FROM generate_series(1, 10000) g;
+
+CREATE INDEX array_gin_test_idx ON array_gin_test USING gin (a);
+
+SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}';
+
+DROP TABLE array_gin_test;
+
 --
 -- HASH
 --
-- 
GitLab