aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Korotkov <akorotkov@postgresql.org>2018-07-19 21:04:17 +0300
committerAlexander Korotkov <akorotkov@postgresql.org>2018-07-19 23:26:15 +0300
commit608793266dbbc4f9064e6737ab3363adf9584892 (patch)
tree2250b70e573a414cedf6e04fcf942e326b18aaa5
parentb1af4bcc42f7e95abb9e8bceb894e0ef2acfc55a (diff)
downloadpostgresql-608793266dbbc4f9064e6737ab3363adf9584892.tar.gz
postgresql-608793266dbbc4f9064e6737ab3363adf9584892.zip
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
-rw-r--r--src/backend/access/gin/gindatapage.c20
-rw-r--r--src/backend/access/gin/ginxlog.c27
2 files changed, 33 insertions, 14 deletions
diff --git a/src/backend/access/gin/gindatapage.c b/src/backend/access/gin/gindatapage.c
index aeaf8adab09..9f20513811e 100644
--- a/src/backend/access/gin/gindatapage.c
+++ b/src/backend/access/gin/gindatapage.c
@@ -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;
}
diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c
index a6e321d77e4..7a1e94a1d56 100644
--- a/src/backend/access/gin/ginxlog.c
+++ b/src/backend/access/gin/ginxlog.c
@@ -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;