Skip to content

Commit 0d26812

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 ff4fb4c commit 0d26812

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

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

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

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

1412-
dlist_push_tail(&leaf->segments, &seginfo->node);
1415+
dlist_push_tail(&leaf->segments, &seginfo->node);
1416+
}
14131417

14141418
leaf->oldformat = true;
14151419
}

src/backend/access/gin/ginxlog.c

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

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

162-
totalsize = SizeOfGinPostingList(plist);
173+
memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize);
174+
}
175+
else
176+
{
177+
totalsize = 0;
178+
}
163179

164-
memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize);
165180
GinDataPageSetDataSize(page, totalsize);
166181
GinPageSetCompressed(page);
167182
GinPageGetOpaque(page)->maxoff = InvalidOffsetNumber;

0 commit comments

Comments
 (0)