@@ -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)
144152 Pointer segmentend ;
145153 char * walbuf ;
146154 int 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
185196 oldseg = GinDataLeafPageGetPostingList (page );
197+ writePtr = (Pointer ) oldseg ;
186198 segmentend = (Pointer ) oldseg + GinDataLeafPageGetPostingListSize (page );
187199 segno = 0 ;
188200
@@ -200,8 +212,6 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
200212 ItemPointerData * newitems ;
201213 int nnewitems ;
202214 int segsize ;
203- Pointer segptr ;
204- int szleft ;
205215
206216 /* Extract all the information we need from the WAL record */
207217 if (a_action == GIN_SEGMENT_INSERT ||
@@ -224,6 +234,17 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
224234 Assert (segno <= a_segno );
225235 while (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 ;
227248 oldseg = GinNextPostingListSegment (oldseg );
228249 segno ++ ;
229250 }
@@ -264,36 +285,42 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
264285 Assert (a_action == GIN_SEGMENT_INSERT );
265286 segsize = 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
269304 switch (a_action )
270305 {
271306 case GIN_SEGMENT_DELETE :
272- memmove (segptr , segptr + segsize , szleft - segsize );
273- segmentend -= segsize ;
274-
307+ segptr += segsize ;
275308 segno ++ ;
276309 break ;
277310
278311 case 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 ;
285316 break ;
286317
287318 case 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 ;
297324 segno ++ ;
298325 break ;
299326
@@ -303,7 +330,18 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
303330 oldseg = (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 );
307345 GinDataPageSetDataSize (page , totalsize );
308346}
309347
0 commit comments