Skip to content

Commit 44b550e

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 8b1d268 commit 44b550e

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
@@ -1392,23 +1392,27 @@ disassembleLeaf(Page page)
13921392
{
13931393
/*
13941394
* A pre-9.4 format uncompressed page is represented by a single
1395-
* segment, with an array of items.
1395+
* segment, with an array of items. The corner case is uncompressed
1396+
* page containing no items, which is represented as no segments.
13961397
*/
13971398
ItemPointer uncompressed;
13981399
int nuncompressed;
13991400
leafSegmentInfo *seginfo;
14001401

14011402
uncompressed = dataLeafPageGetUncompressed(page, &nuncompressed);
14021403

1403-
seginfo = palloc(sizeof(leafSegmentInfo));
1404+
if (nuncompressed > 0)
1405+
{
1406+
seginfo = palloc(sizeof(leafSegmentInfo));
14041407

1405-
seginfo->action = GIN_SEGMENT_REPLACE;
1406-
seginfo->seg = NULL;
1407-
seginfo->items = palloc(nuncompressed * sizeof(ItemPointerData));
1408-
memcpy(seginfo->items, uncompressed, nuncompressed * sizeof(ItemPointerData));
1409-
seginfo->nitems = nuncompressed;
1408+
seginfo->action = GIN_SEGMENT_REPLACE;
1409+
seginfo->seg = NULL;
1410+
seginfo->items = palloc(nuncompressed * sizeof(ItemPointerData));
1411+
memcpy(seginfo->items, uncompressed, nuncompressed * sizeof(ItemPointerData));
1412+
seginfo->nitems = nuncompressed;
14101413

1411-
dlist_push_tail(&leaf->segments, &seginfo->node);
1414+
dlist_push_tail(&leaf->segments, &seginfo->node);
1415+
}
14121416

14131417
leaf->oldformat = true;
14141418
}

src/backend/access/gin/ginxlog.c

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,30 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
151151
ItemPointer uncompressed = (ItemPointer) GinDataPageGetData(page);
152152
int nuncompressed = GinPageGetOpaque(page)->maxoff;
153153
int npacked;
154-
GinPostingList *plist;
155154

156-
plist = ginCompressPostingList(uncompressed, nuncompressed,
157-
BLCKSZ, &npacked);
158-
Assert(npacked == nuncompressed);
155+
/*
156+
* Empty leaf pages are deleted as part of vacuum, but leftmost and
157+
* rightmost pages are never deleted. So, pg_upgrade'd from pre-9.4
158+
* instances might contain empty leaf pages, and we need to handle
159+
* them correctly.
160+
*/
161+
if (nuncompressed > 0)
162+
{
163+
GinPostingList *plist;
164+
165+
plist = ginCompressPostingList(uncompressed, nuncompressed,
166+
BLCKSZ, &npacked);
167+
totalsize = SizeOfGinPostingList(plist);
168+
169+
Assert(npacked == nuncompressed);
159170

160-
totalsize = SizeOfGinPostingList(plist);
171+
memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize);
172+
}
173+
else
174+
{
175+
totalsize = 0;
176+
}
161177

162-
memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize);
163178
GinDataPageSetDataSize(page, totalsize);
164179
GinPageSetCompressed(page);
165180
GinPageGetOpaque(page)->maxoff = InvalidOffsetNumber;

0 commit comments

Comments
 (0)