]> granicus.if.org Git - postgresql/commitdiff
Fix handling of empty uncompressed posting list pages in GIN
authorAlexander Korotkov <akorotkov@postgresql.org>
Thu, 19 Jul 2018 18:04:17 +0000 (21:04 +0300)
committerAlexander Korotkov <akorotkov@postgresql.org>
Thu, 19 Jul 2018 20:26:15 +0000 (23:26 +0300)
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
src/backend/access/gin/ginxlog.c

index aeaf8adab0920b298fdd3be4d75ac199dcb8278d..9f20513811e6ac47d15d87c35f25a5d7ccc84ba1 100644 (file)
@@ -1394,7 +1394,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;
@@ -1402,15 +1403,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;
        }
index a6e321d77e498d313067a5c3b01a9a5396a4bfc3..7a1e94a1d561fc4e55e3a9ab0faa0dc21ea87eae 100644 (file)
@@ -153,15 +153,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;