}
}
+/*
+ * 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)
{
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.
}
oldseg = GinDataLeafPageGetPostingList(page);
+ writePtr = (Pointer) oldseg;
segmentend = (Pointer) oldseg + GinDataLeafPageGetPostingListSize(page);
segno = 0;
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 ||
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++;
}
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;
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);
}