diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c index 21ce79fc0b..70801bd05c 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 d10253b9e9..f13b4f8a08 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 8ac1d1d280..cd5c58d468 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 --