@@ -133,6 +133,14 @@ ginRedoInsertEntry(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rda
133
133
}
134
134
}
135
135
136
+ /*
137
+ * Redo recompression of posting list. Doing all the changes in-place is not
138
+ * always possible, because it might require more space than we've on the page.
139
+ * Instead, once modification is required we copy unprocessed tail of the page
140
+ * into separately allocated chunk of memory for further reading original
141
+ * versions of segments. Thanks to that we don't bother about moving page data
142
+ * in-place.
143
+ */
136
144
static void
137
145
ginRedoRecompress (Page page , ginxlogRecompressDataLeaf * data )
138
146
{
@@ -142,6 +150,9 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
142
150
Pointer segmentend ;
143
151
char * walbuf ;
144
152
int totalsize ;
153
+ Pointer tailCopy = NULL ;
154
+ Pointer writePtr ;
155
+ Pointer segptr ;
145
156
146
157
/*
147
158
* If the page is in pre-9.4 format, convert to new format first.
@@ -181,6 +192,7 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
181
192
}
182
193
183
194
oldseg = GinDataLeafPageGetPostingList (page );
195
+ writePtr = (Pointer ) oldseg ;
184
196
segmentend = (Pointer ) oldseg + GinDataLeafPageGetPostingListSize (page );
185
197
segno = 0 ;
186
198
@@ -198,8 +210,6 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
198
210
ItemPointerData * newitems ;
199
211
int nnewitems ;
200
212
int segsize ;
201
- Pointer segptr ;
202
- int szleft ;
203
213
204
214
/* Extract all the information we need from the WAL record */
205
215
if (a_action == GIN_SEGMENT_INSERT ||
@@ -222,6 +232,17 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
222
232
Assert (segno <= a_segno );
223
233
while (segno < a_segno )
224
234
{
235
+ /*
236
+ * Once modification is started and page tail is copied, we've
237
+ * to copy unmodified segments.
238
+ */
239
+ segsize = SizeOfGinPostingList (oldseg );
240
+ if (tailCopy )
241
+ {
242
+ Assert (writePtr + segsize < PageGetSpecialPointer (page ));
243
+ memcpy (writePtr , (Pointer ) oldseg , segsize );
244
+ }
245
+ writePtr += segsize ;
225
246
oldseg = GinNextPostingListSegment (oldseg );
226
247
segno ++ ;
227
248
}
@@ -262,36 +283,42 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
262
283
Assert (a_action == GIN_SEGMENT_INSERT );
263
284
segsize = 0 ;
264
285
}
265
- szleft = segmentend - segptr ;
286
+
287
+ /*
288
+ * We're about to start modification of the page. So, copy tail of the
289
+ * page if it's not done already.
290
+ */
291
+ if (!tailCopy && segptr != segmentend )
292
+ {
293
+ int tailSize = segmentend - segptr ;
294
+
295
+ tailCopy = (Pointer ) palloc (tailSize );
296
+ memcpy (tailCopy , segptr , tailSize );
297
+ segptr = tailCopy ;
298
+ oldseg = (GinPostingList * ) segptr ;
299
+ segmentend = segptr + tailSize ;
300
+ }
266
301
267
302
switch (a_action )
268
303
{
269
304
case GIN_SEGMENT_DELETE :
270
- memmove (segptr , segptr + segsize , szleft - segsize );
271
- segmentend -= segsize ;
272
-
305
+ segptr += segsize ;
273
306
segno ++ ;
274
307
break ;
275
308
276
309
case GIN_SEGMENT_INSERT :
277
- /* make room for the new segment */
278
- memmove (segptr + newsegsize , segptr , szleft );
279
310
/* copy the new segment in place */
280
- memcpy ( segptr , newseg , newsegsize );
281
- segmentend += newsegsize ;
282
- segptr += newsegsize ;
311
+ Assert ( writePtr + newsegsize <= PageGetSpecialPointer ( page ) );
312
+ memcpy ( writePtr , newseg , newsegsize ) ;
313
+ writePtr += newsegsize ;
283
314
break ;
284
315
285
316
case GIN_SEGMENT_REPLACE :
286
- /* shift the segments that follow */
287
- memmove (segptr + newsegsize ,
288
- segptr + segsize ,
289
- szleft - segsize );
290
- /* copy the replacement segment in place */
291
- memcpy (segptr , newseg , newsegsize );
292
- segmentend -= segsize ;
293
- segmentend += newsegsize ;
294
- segptr += newsegsize ;
317
+ /* copy the new version of segment in place */
318
+ Assert (writePtr + newsegsize <= PageGetSpecialPointer (page ));
319
+ memcpy (writePtr , newseg , newsegsize );
320
+ writePtr += newsegsize ;
321
+ segptr += segsize ;
295
322
segno ++ ;
296
323
break ;
297
324
@@ -301,7 +328,18 @@ ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
301
328
oldseg = (GinPostingList * ) segptr ;
302
329
}
303
330
304
- totalsize = segmentend - (Pointer ) GinDataLeafPageGetPostingList (page );
331
+ /* Copy the rest of unmodified segments if any. */
332
+ segptr = (Pointer ) oldseg ;
333
+ if (segptr != segmentend && tailCopy )
334
+ {
335
+ int restSize = segmentend - segptr ;
336
+
337
+ Assert (writePtr + restSize <= PageGetSpecialPointer (page ));
338
+ memcpy (writePtr , segptr , restSize );
339
+ writePtr += restSize ;
340
+ }
341
+
342
+ totalsize = writePtr - (Pointer ) GinDataLeafPageGetPostingList (page );
305
343
GinDataPageSetDataSize (page , totalsize );
306
344
}
307
345
0 commit comments