diff --git a/src/backend/access/gin/ginxlog.c b/src/backend/access/gin/ginxlog.c index 7515f8bc16..7a21780121 100644 --- a/src/backend/access/gin/ginxlog.c +++ b/src/backend/access/gin/ginxlog.c @@ -133,6 +133,14 @@ ginRedoInsertEntry(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rda } } +/* + * Redo recompression of posting list. Doing all the changes in-place is not + * always possible, because it might require more space than we've on the page. + * Instead, once modification is required we copy unprocessed tail of the page + * into separately allocated chunk of memory for further reading original + * versions of segments. Thanks to that we don't bother about moving page data + * in-place. + */ static void ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data) { @@ -142,6 +150,9 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data) Pointer segmentend; char *walbuf; int totalsize; + Pointer tailCopy = NULL; + Pointer writePtr; + Pointer segptr; /* * If the page is in pre-9.4 format, convert to new format first. @@ -181,6 +192,7 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data) } oldseg = GinDataLeafPageGetPostingList(page); + writePtr = (Pointer) oldseg; segmentend = (Pointer) oldseg + GinDataLeafPageGetPostingListSize(page); segno = 0; @@ -198,8 +210,6 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data) ItemPointerData *newitems; int nnewitems; int segsize; - Pointer segptr; - int szleft; /* Extract all the information we need from the WAL record */ if (a_action == GIN_SEGMENT_INSERT || @@ -222,6 +232,17 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data) Assert(segno <= a_segno); while (segno < a_segno) { + /* + * Once modification is started and page tail is copied, we've + * to copy unmodified segments. + */ + segsize = SizeOfGinPostingList(oldseg); + if (tailCopy) + { + Assert(writePtr + segsize < PageGetSpecialPointer(page)); + memcpy(writePtr, (Pointer) oldseg, segsize); + } + writePtr += segsize; oldseg = GinNextPostingListSegment(oldseg); segno++; } @@ -262,36 +283,42 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data) Assert(a_action == GIN_SEGMENT_INSERT); segsize = 0; } - szleft = segmentend - segptr; + + /* + * We're about to start modification of the page. So, copy tail of the + * page if it's not done already. + */ + if (!tailCopy && segptr != segmentend) + { + int tailSize = segmentend - segptr; + + tailCopy = (Pointer) palloc(tailSize); + memcpy(tailCopy, segptr, tailSize); + segptr = tailCopy; + oldseg = (GinPostingList *) segptr; + segmentend = segptr + tailSize; + } switch (a_action) { case GIN_SEGMENT_DELETE: - memmove(segptr, segptr + segsize, szleft - segsize); - segmentend -= segsize; - + segptr += segsize; segno++; break; case GIN_SEGMENT_INSERT: - /* make room for the new segment */ - memmove(segptr + newsegsize, segptr, szleft); /* copy the new segment in place */ - memcpy(segptr, newseg, newsegsize); - segmentend += newsegsize; - segptr += newsegsize; + Assert(writePtr + newsegsize <= PageGetSpecialPointer(page)); + memcpy(writePtr, newseg, newsegsize); + writePtr += newsegsize; break; case GIN_SEGMENT_REPLACE: - /* shift the segments that follow */ - memmove(segptr + newsegsize, - segptr + segsize, - szleft - segsize); - /* copy the replacement segment in place */ - memcpy(segptr, newseg, newsegsize); - segmentend -= segsize; - segmentend += newsegsize; - segptr += newsegsize; + /* copy the new version of segment in place */ + Assert(writePtr + newsegsize <= PageGetSpecialPointer(page)); + memcpy(writePtr, newseg, newsegsize); + writePtr += newsegsize; + segptr += segsize; segno++; break; @@ -301,7 +328,18 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data) oldseg = (GinPostingList *) segptr; } - totalsize = segmentend - (Pointer) GinDataLeafPageGetPostingList(page); + /* Copy the rest of unmodified segments if any. */ + segptr = (Pointer) oldseg; + if (segptr != segmentend && tailCopy) + { + int restSize = segmentend - segptr; + + Assert(writePtr + restSize <= PageGetSpecialPointer(page)); + memcpy(writePtr, segptr, restSize); + writePtr += restSize; + } + + totalsize = writePtr - (Pointer) GinDataLeafPageGetPostingList(page); GinDataPageSetDataSize(page, totalsize); }