Skip to content

Commit 9c6a676

Browse files
committed
Fix handling of empty uncompressed posting list pages in GIN
PostgreSQL 9.4 introduces posting list compression in GIN. This feature supports online upgrade, so that after pg_upgrade uncompressed posting lists are compressed on-the-fly. Underlying code appears to always expect at least one item on uncompressed posting list page. But there could be completely empty pages, because VACUUM never deletes leftmost and rightmost pages from posting trees. This commit fixes that. Reported-by: Sivasubramanian Ramasubramanian Discussion: https://postgr.es/m/1531867212836.63354%40amazon.com Author: Sivasubramanian Ramasubramanian, Alexander Korotkov Backpatch-through: 9.4
1 parent 47d51a5 commit 9c6a676

File tree

2 files changed

+33
-14
lines changed

2 files changed

+33
-14
lines changed

src/backend/access/gin/gindatapage.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,23 +1470,27 @@ disassembleLeaf(Page page)
14701470
{
14711471
/*
14721472
* A pre-9.4 format uncompressed page is represented by a single
1473-
* segment, with an array of items.
1473+
* segment, with an array of items. The corner case is uncompressed
1474+
* page containing no items, which is represented as no segments.
14741475
*/
14751476
ItemPointer uncompressed;
14761477
int nuncompressed;
14771478
leafSegmentInfo *seginfo;
14781479

14791480
uncompressed = dataLeafPageGetUncompressed(page, &nuncompressed);
14801481

1481-
seginfo = palloc(sizeof(leafSegmentInfo));
1482+
if (nuncompressed > 0)
1483+
{
1484+
seginfo = palloc(sizeof(leafSegmentInfo));
14821485

1483-
seginfo->action = GIN_SEGMENT_REPLACE;
1484-
seginfo->seg = NULL;
1485-
seginfo->items = palloc(nuncompressed * sizeof(ItemPointerData));
1486-
memcpy(seginfo->items, uncompressed, nuncompressed * sizeof(ItemPointerData));
1487-
seginfo->nitems = nuncompressed;
1486+
seginfo->action = GIN_SEGMENT_REPLACE;
1487+
seginfo->seg = NULL;
1488+
seginfo->items = palloc(nuncompressed * sizeof(ItemPointerData));
1489+
memcpy(seginfo->items, uncompressed, nuncompressed * sizeof(ItemPointerData));
1490+
seginfo->nitems = nuncompressed;
14881491

1489-
dlist_push_tail(&leaf->segments, &seginfo->node);
1492+
dlist_push_tail(&leaf->segments, &seginfo->node);
1493+
}
14901494

14911495
leaf->oldformat = true;
14921496
}

src/backend/access/gin/ginxlog.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,15 +160,30 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
160160
ItemPointer uncompressed = (ItemPointer) GinDataPageGetData(page);
161161
int nuncompressed = GinPageGetOpaque(page)->maxoff;
162162
int npacked;
163-
GinPostingList *plist;
164163

165-
plist = ginCompressPostingList(uncompressed, nuncompressed,
166-
BLCKSZ, &npacked);
167-
Assert(npacked == nuncompressed);
164+
/*
165+
* Empty leaf pages are deleted as part of vacuum, but leftmost and
166+
* rightmost pages are never deleted. So, pg_upgrade'd from pre-9.4
167+
* instances might contain empty leaf pages, and we need to handle
168+
* them correctly.
169+
*/
170+
if (nuncompressed > 0)
171+
{
172+
GinPostingList *plist;
173+
174+
plist = ginCompressPostingList(uncompressed, nuncompressed,
175+
BLCKSZ, &npacked);
176+
totalsize = SizeOfGinPostingList(plist);
177+
178+
Assert(npacked == nuncompressed);
168179

169-
totalsize = SizeOfGinPostingList(plist);
180+
memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize);
181+
}
182+
else
183+
{
184+
totalsize = 0;
185+
}
170186

171-
memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize);
172187
GinDataPageSetDataSize(page, totalsize);
173188
GinPageSetCompressed(page);
174189
GinPageGetOpaque(page)->maxoff = InvalidOffsetNumber;

0 commit comments

Comments
 (0)