mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-12 18:34:36 +08:00
Fix past pd_upper write in ginRedoRecompress()
ginRedoRecompress() replays actions over compressed segments of posting list in-place. However, it might lead to write past pg_upper, because intermediate state during playing the changes can take more space than both original state and final state. This commit fixes that by refuse from in-place modification. Instead page tail is copied once modification is started, and then it's used as the source of original segments. Backpatch to 9.4 where posting list compression was introduced. Reported-by: Sivasubramanian Ramasubramanian Discussion: https://postgr.es/m/1536091151804.6588%40amazon.com Author: Alexander Korotkov based on patch from and ideas by Sivasubramanian Ramasubramanian Review: Sivasubramanian Ramasubramanian Backpatch-through: 9.4
This commit is contained in:
parent
ff47d4bf1f
commit
5f08accdad
@ -135,6 +135,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)
|
||||
{
|
||||
@ -144,6 +152,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.
|
||||
@ -183,6 +194,7 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
|
||||
}
|
||||
|
||||
oldseg = GinDataLeafPageGetPostingList(page);
|
||||
writePtr = (Pointer) oldseg;
|
||||
segmentend = (Pointer) oldseg + GinDataLeafPageGetPostingListSize(page);
|
||||
segno = 0;
|
||||
|
||||
@ -200,8 +212,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 ||
|
||||
@ -224,6 +234,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++;
|
||||
}
|
||||
@ -264,36 +285,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;
|
||||
|
||||
@ -303,7 +330,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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user