@@ -135,6 +135,14 @@ ginRedoInsertEntry(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rda
135135}
136136}
137137
138+ /*
139+ * Redo recompression of posting list. Doing all the changes in-place is not
140+ * always possible, because it might require more space than we've on the page.
141+ * Instead, once modification is required we copy unprocessed tail of the page
142+ * into separately allocated chunk of memory for further reading original
143+ * versions of segments. Thanks to that we don't bother about moving page data
144+ * in-place.
145+ */
138146static void
139147ginRedoRecompress (Page page ,ginxlogRecompressDataLeaf * data )
140148{
@@ -144,6 +152,9 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
144152Pointer segmentend ;
145153char * walbuf ;
146154int totalsize ;
155+ Pointer tailCopy = NULL ;
156+ Pointer writePtr ;
157+ Pointer segptr ;
147158
148159/*
149160 * If the page is in pre-9.4 format, convert to new format first.
@@ -183,6 +194,7 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
183194}
184195
185196oldseg = GinDataLeafPageGetPostingList (page );
197+ writePtr = (Pointer )oldseg ;
186198segmentend = (Pointer )oldseg + GinDataLeafPageGetPostingListSize (page );
187199segno = 0 ;
188200
@@ -200,8 +212,6 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
200212ItemPointerData * newitems ;
201213int nnewitems ;
202214int segsize ;
203- Pointer segptr ;
204- int szleft ;
205215
206216/* Extract all the information we need from the WAL record */
207217if (a_action == GIN_SEGMENT_INSERT ||
@@ -224,6 +234,17 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
224234Assert (segno <=a_segno );
225235while (segno < a_segno )
226236{
237+ /*
238+ * Once modification is started and page tail is copied, we've
239+ * to copy unmodified segments.
240+ */
241+ segsize = SizeOfGinPostingList (oldseg );
242+ if (tailCopy )
243+ {
244+ Assert (writePtr + segsize < PageGetSpecialPointer (page ));
245+ memcpy (writePtr , (Pointer )oldseg ,segsize );
246+ }
247+ writePtr += segsize ;
227248oldseg = GinNextPostingListSegment (oldseg );
228249segno ++ ;
229250}
@@ -264,36 +285,42 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
264285Assert (a_action == GIN_SEGMENT_INSERT );
265286segsize = 0 ;
266287}
267- szleft = segmentend - segptr ;
288+
289+ /*
290+ * We're about to start modification of the page. So, copy tail of the
291+ * page if it's not done already.
292+ */
293+ if (!tailCopy && segptr != segmentend )
294+ {
295+ int tailSize = segmentend - segptr ;
296+
297+ tailCopy = (Pointer )palloc (tailSize );
298+ memcpy (tailCopy ,segptr ,tailSize );
299+ segptr = tailCopy ;
300+ oldseg = (GinPostingList * )segptr ;
301+ segmentend = segptr + tailSize ;
302+ }
268303
269304switch (a_action )
270305{
271306case GIN_SEGMENT_DELETE :
272- memmove (segptr ,segptr + segsize ,szleft - segsize );
273- segmentend -= segsize ;
274-
307+ segptr += segsize ;
275308segno ++ ;
276309break ;
277310
278311case GIN_SEGMENT_INSERT :
279- /* make room for the new segment */
280- memmove (segptr + newsegsize ,segptr ,szleft );
281312/* copy the new segment in place */
282- memcpy ( segptr , newseg , newsegsize );
283- segmentend += newsegsize ;
284- segptr += newsegsize ;
313+ Assert ( writePtr + newsegsize <= PageGetSpecialPointer ( page ) );
314+ memcpy ( writePtr , newseg , newsegsize ) ;
315+ writePtr += newsegsize ;
285316break ;
286317
287318case GIN_SEGMENT_REPLACE :
288- /* shift the segments that follow */
289- memmove (segptr + newsegsize ,
290- segptr + segsize ,
291- szleft - segsize );
292- /* copy the replacement segment in place */
293- memcpy (segptr ,newseg ,newsegsize );
294- segmentend -= segsize ;
295- segmentend += newsegsize ;
296- segptr += newsegsize ;
319+ /* copy the new version of segment in place */
320+ Assert (writePtr + newsegsize <=PageGetSpecialPointer (page ));
321+ memcpy (writePtr ,newseg ,newsegsize );
322+ writePtr += newsegsize ;
323+ segptr += segsize ;
297324segno ++ ;
298325break ;
299326
@@ -303,7 +330,18 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
303330oldseg = (GinPostingList * )segptr ;
304331}
305332
306- totalsize = segmentend - (Pointer )GinDataLeafPageGetPostingList (page );
333+ /* Copy the rest of unmodified segments if any. */
334+ segptr = (Pointer )oldseg ;
335+ if (segptr != segmentend && tailCopy )
336+ {
337+ int restSize = segmentend - segptr ;
338+
339+ Assert (writePtr + restSize <=PageGetSpecialPointer (page ));
340+ memcpy (writePtr ,segptr ,restSize );
341+ writePtr += restSize ;
342+ }
343+
344+ totalsize = writePtr - (Pointer )GinDataLeafPageGetPostingList (page );
307345GinDataPageSetDataSize (page ,totalsize );
308346}
309347