postgres.git / summary / log / commit / refs

commit    3c09b032afe548a4f0c3fd0658957650c3e22832
Author:   Alexander Korotkov <akorotkov@postgresql.org>
Date:     Thu Jul 19 18:04:17 2018 +0000
First released in: 12.0, 9.4.19, 9.5.14, 9.6.10, 10.5, 11.0

    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


src/backend/access/gin/gindatapage.c | 20 ++++++++++++-------- src/backend/access/gin/ginxlog.c | 27 +++++++++++++++++++++------ 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c index 93f2d8f350d..d25684949f5 100644 --- a/src/backend/access/gin/gindatapage.c +++ b/src/backend/access/gin/gindatapage.c @@ -1392,7 +1392,8 @@ disassembleLeaf(Page page) { /* * A pre-9.4 format uncompressed page is represented by a single - * segment, with an array of items. + * segment, with an array of items. The corner case is uncompressed + * page containing no items, which is represented as no segments. */ ItemPointer uncompressed; int nuncompressed; @@ -1400,15 +1401,18 @@ disassembleLeaf(Page page) uncompressed = dataLeafPageGetUncompressed(page, &nuncompressed); - seginfo = palloc(sizeof(leafSegmentInfo)); + if (nuncompressed > 0) + { + seginfo = palloc(sizeof(leafSegmentInfo)); - seginfo->action = GIN_SEGMENT_REPLACE; - seginfo->seg = NULL; - seginfo->items = palloc(nuncompressed * sizeof(ItemPointerData)); - memcpy(seginfo->items, uncompressed, nuncompressed * sizeof(ItemPointerData)); - seginfo->nitems = nuncompressed; + seginfo->action = GIN_SEGMENT_REPLACE; + seginfo->seg = NULL; + seginfo->items = palloc(nuncompressed * sizeof(ItemPointerData)); + memcpy(seginfo->items, uncompressed, nuncompressed * sizeof(ItemPointerData)); + seginfo->nitems = nuncompressed; - dlist_push_tail(&leaf->segments, &seginfo->node); + dlist_push_tail(&leaf->segments, &seginfo->node); + } leaf->oldformat = true; } diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c index d9b0ce02ac4..9b57fd4978a 100644 --- a/src/backend/access/gin/ginxlog.c +++ b/src/backend/access/gin/ginxlog.c @@ -151,15 +151,30 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data) ItemPointer uncompressed = (ItemPointer) GinDataPageGetData(page); int nuncompressed = GinPageGetOpaque(page)->maxoff; int npacked; - GinPostingList *plist; - plist = ginCompressPostingList(uncompressed, nuncompressed, - BLCKSZ, &npacked); - Assert(npacked == nuncompressed); + /* + * Empty leaf pages are deleted as part of vacuum, but leftmost and + * rightmost pages are never deleted. So, pg_upgrade'd from pre-9.4 + * instances might contain empty leaf pages, and we need to handle + * them correctly. + */ + if (nuncompressed > 0) + { + GinPostingList *plist; + + plist = ginCompressPostingList(uncompressed, nuncompressed, + BLCKSZ, &npacked); + totalsize = SizeOfGinPostingList(plist); + + Assert(npacked == nuncompressed); - totalsize = SizeOfGinPostingList(plist); + memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize); + } + else + { + totalsize = 0; + } - memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize); GinDataPageSetDataSize(page, totalsize); GinPageSetCompressed(page); GinPageGetOpaque(page)->maxoff = InvalidOffsetNumber; [parent: 3ecd6c4ab9dd]