Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * ginxlog.c
4 : : * WAL replay logic for inverted index.
5 : : *
6 : : *
7 : : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : * IDENTIFICATION
11 : : * src/backend/access/gin/ginxlog.c
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include "access/bufmask.h"
17 : : #include "access/gin_private.h"
18 : : #include "access/ginxlog.h"
19 : : #include "access/xlogutils.h"
20 : : #include "utils/memutils.h"
21 : :
22 : : static MemoryContext opCtx; /* working memory for operations */
23 : :
24 : : static void
3433 heikki.linnakangas@i 25 :CBC 139 : ginRedoClearIncompleteSplit(XLogReaderState *record, uint8 block_id)
26 : : {
27 : 139 : XLogRecPtr lsn = record->EndRecPtr;
28 : : Buffer buffer;
29 : : Page page;
30 : :
31 [ + - ]: 139 : if (XLogReadBufferForRedo(record, block_id, &buffer) == BLK_NEEDS_REDO)
32 : : {
2916 kgrittn@postgresql.o 33 : 139 : page = (Page) BufferGetPage(buffer);
3791 heikki.linnakangas@i 34 : 139 : GinPageGetOpaque(page)->flags &= ~GIN_INCOMPLETE_SPLIT;
35 : :
36 : 139 : PageSetLSN(page, lsn);
37 : 139 : MarkBufferDirty(buffer);
38 : : }
3532 39 [ + - ]: 139 : if (BufferIsValid(buffer))
40 : 139 : UnlockReleaseBuffer(buffer);
6557 teodor@sigaev.ru 41 : 139 : }
42 : :
43 : : static void
3433 heikki.linnakangas@i 44 : 6 : ginRedoCreatePTree(XLogReaderState *record)
45 : : {
46 : 6 : XLogRecPtr lsn = record->EndRecPtr;
6402 bruce@momjian.us 47 : 6 : ginxlogCreatePostingTree *data = (ginxlogCreatePostingTree *) XLogRecGetData(record);
48 : : char *ptr;
49 : : Buffer buffer;
50 : : Page page;
51 : :
3433 heikki.linnakangas@i 52 : 6 : buffer = XLogInitBufferForRedo(record, 0);
2916 kgrittn@postgresql.o 53 : 6 : page = (Page) BufferGetPage(buffer);
54 : :
3735 heikki.linnakangas@i 55 : 6 : GinInitBuffer(buffer, GIN_DATA | GIN_LEAF | GIN_COMPRESSED);
56 : :
57 : 6 : ptr = XLogRecGetData(record) + sizeof(ginxlogCreatePostingTree);
58 : :
59 : : /* Place page data */
60 : 6 : memcpy(GinDataLeafPageGetPostingList(page), ptr, data->size);
61 : :
3653 62 [ - + ]: 6 : GinDataPageSetDataSize(page, data->size);
63 : :
6557 teodor@sigaev.ru 64 : 6 : PageSetLSN(page, lsn);
65 : :
66 : 6 : MarkBufferDirty(buffer);
67 : 6 : UnlockReleaseBuffer(buffer);
68 : 6 : }
69 : :
70 : : static void
3735 heikki.linnakangas@i 71 : 23664 : ginRedoInsertEntry(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rdata)
72 : : {
2916 kgrittn@postgresql.o 73 : 23664 : Page page = BufferGetPage(buffer);
3791 heikki.linnakangas@i 74 : 23664 : ginxlogInsertEntry *data = (ginxlogInsertEntry *) rdata;
3735 75 : 23664 : OffsetNumber offset = data->offset;
76 : : IndexTuple itup;
77 : :
3791 78 [ + + ]: 23664 : if (rightblkno != InvalidBlockNumber)
79 : : {
80 : : /* update link to right page after split */
81 [ - + ]: 136 : Assert(!GinPageIsLeaf(page));
82 [ + - - + ]: 136 : Assert(offset >= FirstOffsetNumber && offset <= PageGetMaxOffsetNumber(page));
83 : 136 : itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offset));
84 : 136 : GinSetDownlink(itup, rightblkno);
85 : : }
86 : :
87 [ + + ]: 23664 : if (data->isDelete)
88 : : {
89 [ - + ]: 3661 : Assert(GinPageIsLeaf(page));
90 [ + - - + ]: 3661 : Assert(offset >= FirstOffsetNumber && offset <= PageGetMaxOffsetNumber(page));
91 : 3661 : PageIndexTupleDelete(page, offset);
92 : : }
93 : :
94 : 23664 : itup = &data->tuple;
95 : :
96 [ - + ]: 23664 : if (PageAddItem(page, (Item) itup, IndexTupleSize(itup), offset, false, false) == InvalidOffsetNumber)
97 : : {
98 : : RelFileLocator locator;
99 : : ForkNumber forknum;
100 : : BlockNumber blknum;
101 : :
648 rhaas@postgresql.org 102 :UBC 0 : BufferGetTag(buffer, &locator, &forknum, &blknum);
564 103 [ # # ]: 0 : elog(ERROR, "failed to add item to index page in %u/%u/%u",
104 : : locator.spcOid, locator.dbOid, locator.relNumber);
105 : : }
3791 heikki.linnakangas@i 106 :CBC 23664 : }
107 : :
108 : : /*
109 : : * Redo recompression of posting list. Doing all the changes in-place is not
110 : : * always possible, because it might require more space than we've on the page.
111 : : * Instead, once modification is required we copy unprocessed tail of the page
112 : : * into separately allocated chunk of memory for further reading original
113 : : * versions of segments. Thanks to that we don't bother about moving page data
114 : : * in-place.
115 : : */
116 : : static void
3735 117 : 3348 : ginRedoRecompress(Page page, ginxlogRecompressDataLeaf *data)
118 : : {
119 : : int actionno;
120 : : int segno;
121 : : GinPostingList *oldseg;
122 : : Pointer segmentend;
123 : : char *walbuf;
124 : : int totalsize;
2044 akorotkov@postgresql 125 : 3348 : Pointer tailCopy = NULL;
126 : : Pointer writePtr;
127 : : Pointer segptr;
128 : :
129 : : /*
130 : : * If the page is in pre-9.4 format, convert to new format first.
131 : : */
3667 heikki.linnakangas@i 132 [ - + ]: 3348 : if (!GinPageIsCompressed(page))
133 : : {
3667 heikki.linnakangas@i 134 :UBC 0 : ItemPointer uncompressed = (ItemPointer) GinDataPageGetData(page);
135 : 0 : int nuncompressed = GinPageGetOpaque(page)->maxoff;
136 : : int npacked;
137 : :
138 : : /*
139 : : * Empty leaf pages are deleted as part of vacuum, but leftmost and
140 : : * rightmost pages are never deleted. So, pg_upgrade'd from pre-9.4
141 : : * instances might contain empty leaf pages, and we need to handle
142 : : * them correctly.
143 : : */
2096 akorotkov@postgresql 144 [ # # ]: 0 : if (nuncompressed > 0)
145 : : {
146 : : GinPostingList *plist;
147 : :
148 : 0 : plist = ginCompressPostingList(uncompressed, nuncompressed,
149 : : BLCKSZ, &npacked);
150 : 0 : totalsize = SizeOfGinPostingList(plist);
151 : :
152 [ # # ]: 0 : Assert(npacked == nuncompressed);
153 : :
154 : 0 : memcpy(GinDataLeafPageGetPostingList(page), plist, totalsize);
155 : : }
156 : : else
157 : : {
158 : 0 : totalsize = 0;
159 : : }
160 : :
3653 heikki.linnakangas@i 161 [ # # ]: 0 : GinDataPageSetDataSize(page, totalsize);
3667 162 : 0 : GinPageSetCompressed(page);
163 : 0 : GinPageGetOpaque(page)->maxoff = InvalidOffsetNumber;
164 : : }
165 : :
3667 heikki.linnakangas@i 166 :CBC 3348 : oldseg = GinDataLeafPageGetPostingList(page);
2044 akorotkov@postgresql 167 : 3348 : writePtr = (Pointer) oldseg;
3667 heikki.linnakangas@i 168 : 3348 : segmentend = (Pointer) oldseg + GinDataLeafPageGetPostingListSize(page);
169 : 3348 : segno = 0;
170 : :
171 : 3348 : walbuf = ((char *) data) + sizeof(ginxlogRecompressDataLeaf);
172 [ + + ]: 6714 : for (actionno = 0; actionno < data->nactions; actionno++)
173 : : {
174 : 3366 : uint8 a_segno = *((uint8 *) (walbuf++));
175 : 3366 : uint8 a_action = *((uint8 *) (walbuf++));
176 : 3366 : GinPostingList *newseg = NULL;
177 : 3366 : int newsegsize = 0;
178 : 3366 : ItemPointerData *items = NULL;
179 : 3366 : uint16 nitems = 0;
180 : : ItemPointerData *olditems;
181 : : int nolditems;
182 : : ItemPointerData *newitems;
183 : : int nnewitems;
184 : : int segsize;
185 : :
186 : : /* Extract all the information we need from the WAL record */
187 [ + + + + ]: 3366 : if (a_action == GIN_SEGMENT_INSERT ||
188 : : a_action == GIN_SEGMENT_REPLACE)
189 : : {
190 : 34 : newseg = (GinPostingList *) walbuf;
191 : 34 : newsegsize = SizeOfGinPostingList(newseg);
192 : 34 : walbuf += SHORTALIGN(newsegsize);
193 : : }
194 : :
195 [ + + ]: 3366 : if (a_action == GIN_SEGMENT_ADDITEMS)
196 : : {
197 : 3326 : memcpy(&nitems, walbuf, sizeof(uint16));
198 : 3326 : walbuf += sizeof(uint16);
199 : 3326 : items = (ItemPointerData *) walbuf;
200 : 3326 : walbuf += nitems * sizeof(ItemPointerData);
201 : : }
202 : :
203 : : /* Skip to the segment that this action concerns */
204 [ - + ]: 3366 : Assert(segno <= a_segno);
205 [ + + ]: 59839 : while (segno < a_segno)
206 : : {
207 : : /*
208 : : * Once modification is started and page tail is copied, we've to
209 : : * copy unmodified segments.
210 : : */
2044 akorotkov@postgresql 211 : 56473 : segsize = SizeOfGinPostingList(oldseg);
212 [ - + ]: 56473 : if (tailCopy)
213 : : {
2044 akorotkov@postgresql 214 [ # # ]:UBC 0 : Assert(writePtr + segsize < PageGetSpecialPointer(page));
215 : 0 : memcpy(writePtr, (Pointer) oldseg, segsize);
216 : : }
2044 akorotkov@postgresql 217 :CBC 56473 : writePtr += segsize;
3667 heikki.linnakangas@i 218 : 56473 : oldseg = GinNextPostingListSegment(oldseg);
219 : 56473 : segno++;
220 : : }
221 : :
222 : : /*
223 : : * ADDITEMS action is handled like REPLACE, but the new segment to
224 : : * replace the old one is reconstructed using the old segment from
225 : : * disk and the new items from the WAL record.
226 : : */
227 [ + + ]: 3366 : if (a_action == GIN_SEGMENT_ADDITEMS)
228 : : {
229 : : int npacked;
230 : :
231 : 3326 : olditems = ginPostingListDecode(oldseg, &nolditems);
232 : :
233 : 3326 : newitems = ginMergeItemPointers(items, nitems,
234 : : olditems, nolditems,
235 : : &nnewitems);
236 [ - + ]: 3326 : Assert(nnewitems == nolditems + nitems);
237 : :
238 : 3326 : newseg = ginCompressPostingList(newitems, nnewitems,
239 : : BLCKSZ, &npacked);
240 [ - + ]: 3326 : Assert(npacked == nnewitems);
241 : :
242 : 3326 : newsegsize = SizeOfGinPostingList(newseg);
243 : 3326 : a_action = GIN_SEGMENT_REPLACE;
244 : : }
245 : :
246 : 3366 : segptr = (Pointer) oldseg;
247 [ + + ]: 3366 : if (segptr != segmentend)
248 : 3338 : segsize = SizeOfGinPostingList(oldseg);
249 : : else
250 : : {
251 : : /*
252 : : * Positioned after the last existing segment. Only INSERTs
253 : : * expected here.
254 : : */
255 [ - + ]: 28 : Assert(a_action == GIN_SEGMENT_INSERT);
256 : 28 : segsize = 0;
257 : : }
258 : :
259 : : /*
260 : : * We're about to start modification of the page. So, copy tail of
261 : : * the page if it's not done already.
262 : : */
2044 akorotkov@postgresql 263 [ + + + + ]: 3366 : if (!tailCopy && segptr != segmentend)
264 : : {
1789 tgl@sss.pgh.pa.us 265 : 3332 : int tailSize = segmentend - segptr;
266 : :
2044 akorotkov@postgresql 267 : 3332 : tailCopy = (Pointer) palloc(tailSize);
268 : 3332 : memcpy(tailCopy, segptr, tailSize);
269 : 3332 : segptr = tailCopy;
270 : 3332 : oldseg = (GinPostingList *) segptr;
271 : 3332 : segmentend = segptr + tailSize;
272 : : }
273 : :
3667 heikki.linnakangas@i 274 [ + + + - ]: 3366 : switch (a_action)
275 : : {
276 : 6 : case GIN_SEGMENT_DELETE:
2044 akorotkov@postgresql 277 : 6 : segptr += segsize;
3667 heikki.linnakangas@i 278 : 6 : segno++;
279 : 6 : break;
280 : :
281 : 28 : case GIN_SEGMENT_INSERT:
282 : : /* copy the new segment in place */
2044 akorotkov@postgresql 283 [ - + ]: 28 : Assert(writePtr + newsegsize <= PageGetSpecialPointer(page));
284 : 28 : memcpy(writePtr, newseg, newsegsize);
285 : 28 : writePtr += newsegsize;
3667 heikki.linnakangas@i 286 : 28 : break;
287 : :
288 : 3332 : case GIN_SEGMENT_REPLACE:
289 : : /* copy the new version of segment in place */
2044 akorotkov@postgresql 290 [ - + ]: 3332 : Assert(writePtr + newsegsize <= PageGetSpecialPointer(page));
291 : 3332 : memcpy(writePtr, newseg, newsegsize);
292 : 3332 : writePtr += newsegsize;
293 : 3332 : segptr += segsize;
3667 heikki.linnakangas@i 294 : 3332 : segno++;
295 : 3332 : break;
296 : :
3667 heikki.linnakangas@i 297 :UBC 0 : default:
298 [ # # ]: 0 : elog(ERROR, "unexpected GIN leaf action: %u", a_action);
299 : : }
3667 heikki.linnakangas@i 300 :CBC 3366 : oldseg = (GinPostingList *) segptr;
301 : : }
302 : :
303 : : /* Copy the rest of unmodified segments if any. */
2044 akorotkov@postgresql 304 : 3348 : segptr = (Pointer) oldseg;
305 [ + + + - ]: 3348 : if (segptr != segmentend && tailCopy)
306 : : {
1789 tgl@sss.pgh.pa.us 307 : 3 : int restSize = segmentend - segptr;
308 : :
2044 akorotkov@postgresql 309 [ - + ]: 3 : Assert(writePtr + restSize <= PageGetSpecialPointer(page));
310 : 3 : memcpy(writePtr, segptr, restSize);
311 : 3 : writePtr += restSize;
312 : : }
313 : :
314 : 3348 : totalsize = writePtr - (Pointer) GinDataLeafPageGetPostingList(page);
3653 heikki.linnakangas@i 315 [ - + ]: 3348 : GinDataPageSetDataSize(page, totalsize);
3735 316 : 3348 : }
317 : :
318 : : static void
319 : 3348 : ginRedoInsertData(Buffer buffer, bool isLeaf, BlockNumber rightblkno, void *rdata)
320 : : {
2916 kgrittn@postgresql.o 321 : 3348 : Page page = BufferGetPage(buffer);
322 : :
3735 heikki.linnakangas@i 323 [ + + ]: 3348 : if (isLeaf)
324 : : {
325 : 3345 : ginxlogRecompressDataLeaf *data = (ginxlogRecompressDataLeaf *) rdata;
326 : :
327 [ - + ]: 3345 : Assert(GinPageIsLeaf(page));
328 : :
329 : 3345 : ginRedoRecompress(page, data);
330 : : }
331 : : else
332 : : {
333 : 3 : ginxlogInsertDataInternal *data = (ginxlogInsertDataInternal *) rdata;
334 : : PostingItem *oldpitem;
335 : :
336 [ - + ]: 3 : Assert(!GinPageIsLeaf(page));
337 : :
338 : : /* update link to right page after split */
339 : 3 : oldpitem = GinDataPageGetPostingItem(page, data->offset);
3791 340 : 3 : PostingItemSetBlockNumber(oldpitem, rightblkno);
341 : :
3735 342 : 3 : GinDataPageAddPostingItem(page, &data->newitem, data->offset);
343 : : }
3791 344 : 3348 : }
345 : :
346 : : static void
3433 347 : 27012 : ginRedoInsert(XLogReaderState *record)
348 : : {
349 : 27012 : XLogRecPtr lsn = record->EndRecPtr;
3791 350 : 27012 : ginxlogInsert *data = (ginxlogInsert *) XLogRecGetData(record);
351 : : Buffer buffer;
352 : : #ifdef NOT_USED
353 : : BlockNumber leftChildBlkno = InvalidBlockNumber;
354 : : #endif
355 : 27012 : BlockNumber rightChildBlkno = InvalidBlockNumber;
356 : 27012 : bool isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0;
357 : :
358 : : /*
359 : : * First clear incomplete-split flag on child page if this finishes a
360 : : * split.
361 : : */
362 [ + + ]: 27012 : if (!isLeaf)
363 : : {
3433 364 : 139 : char *payload = XLogRecGetData(record) + sizeof(ginxlogInsert);
365 : :
366 : : #ifdef NOT_USED
367 : : leftChildBlkno = BlockIdGetBlockNumber((BlockId) payload);
368 : : #endif
3791 369 : 139 : payload += sizeof(BlockIdData);
370 : 139 : rightChildBlkno = BlockIdGetBlockNumber((BlockId) payload);
371 : 139 : payload += sizeof(BlockIdData);
372 : :
3433 373 : 139 : ginRedoClearIncompleteSplit(record, 1);
374 : : }
375 : :
376 [ + - ]: 27012 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
377 : : {
2916 kgrittn@postgresql.o 378 : 27012 : Page page = BufferGetPage(buffer);
379 : : Size len;
3433 heikki.linnakangas@i 380 : 27012 : char *payload = XLogRecGetBlockData(record, 0, &len);
381 : :
382 : : /* How to insert the payload is tree-type specific */
3791 383 [ + + ]: 27012 : if (data->flags & GIN_INSERT_ISDATA)
384 : : {
4934 tgl@sss.pgh.pa.us 385 [ - + ]: 3348 : Assert(GinPageIsData(page));
3735 heikki.linnakangas@i 386 : 3348 : ginRedoInsertData(buffer, isLeaf, rightChildBlkno, payload);
387 : : }
388 : : else
389 : : {
4934 tgl@sss.pgh.pa.us 390 [ - + ]: 23664 : Assert(!GinPageIsData(page));
3735 heikki.linnakangas@i 391 : 23664 : ginRedoInsertEntry(buffer, isLeaf, rightChildBlkno, payload);
392 : : }
393 : :
3791 394 : 27012 : PageSetLSN(page, lsn);
395 : 27012 : MarkBufferDirty(buffer);
396 : : }
3532 397 [ + - ]: 27012 : if (BufferIsValid(buffer))
398 : 27012 : UnlockReleaseBuffer(buffer);
3791 399 : 27012 : }
400 : :
401 : : static void
3433 402 : 142 : ginRedoSplit(XLogReaderState *record)
403 : : {
6402 bruce@momjian.us 404 : 142 : ginxlogSplit *data = (ginxlogSplit *) XLogRecGetData(record);
405 : : Buffer lbuffer,
406 : : rbuffer,
407 : : rootbuf;
3791 heikki.linnakangas@i 408 : 142 : bool isLeaf = (data->flags & GIN_INSERT_ISLEAF) != 0;
409 : 142 : bool isRoot = (data->flags & GIN_SPLIT_ROOT) != 0;
410 : :
411 : : /*
412 : : * First clear incomplete-split flag on child page if this finishes a
413 : : * split
414 : : */
415 [ - + ]: 142 : if (!isLeaf)
3433 heikki.linnakangas@i 416 :UBC 0 : ginRedoClearIncompleteSplit(record, 3);
417 : :
3433 heikki.linnakangas@i 418 [ - + ]:CBC 142 : if (XLogReadBufferForRedo(record, 0, &lbuffer) != BLK_RESTORED)
3433 heikki.linnakangas@i 419 [ # # ]:UBC 0 : elog(ERROR, "GIN split record did not contain a full-page image of left page");
420 : :
3433 heikki.linnakangas@i 421 [ - + ]:CBC 142 : if (XLogReadBufferForRedo(record, 1, &rbuffer) != BLK_RESTORED)
3433 heikki.linnakangas@i 422 [ # # ]:UBC 0 : elog(ERROR, "GIN split record did not contain a full-page image of right page");
423 : :
3791 heikki.linnakangas@i 424 [ + + ]:CBC 142 : if (isRoot)
425 : : {
3433 426 [ - + ]: 3 : if (XLogReadBufferForRedo(record, 2, &rootbuf) != BLK_RESTORED)
3433 heikki.linnakangas@i 427 [ # # ]:UBC 0 : elog(ERROR, "GIN split record did not contain a full-page image of root page");
3433 heikki.linnakangas@i 428 :CBC 3 : UnlockReleaseBuffer(rootbuf);
429 : : }
430 : :
6557 teodor@sigaev.ru 431 : 142 : UnlockReleaseBuffer(rbuffer);
432 : 142 : UnlockReleaseBuffer(lbuffer);
433 : 142 : }
434 : :
435 : : /*
436 : : * VACUUM_PAGE record contains simply a full image of the page, similar to
437 : : * an XLOG_FPI record.
438 : : */
439 : : static void
3433 heikki.linnakangas@i 440 :UBC 0 : ginRedoVacuumPage(XLogReaderState *record)
441 : : {
442 : : Buffer buffer;
443 : :
444 [ # # ]: 0 : if (XLogReadBufferForRedo(record, 0, &buffer) != BLK_RESTORED)
445 : : {
446 [ # # ]: 0 : elog(ERROR, "replay of gin entry tree page vacuum did not restore the page");
447 : : }
3735 448 : 0 : UnlockReleaseBuffer(buffer);
449 : 0 : }
450 : :
451 : : static void
3433 heikki.linnakangas@i 452 :CBC 3 : ginRedoVacuumDataLeafPage(XLogReaderState *record)
453 : : {
454 : 3 : XLogRecPtr lsn = record->EndRecPtr;
455 : : Buffer buffer;
456 : :
457 [ + - ]: 3 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
458 : : {
2916 kgrittn@postgresql.o 459 : 3 : Page page = BufferGetPage(buffer);
460 : : Size len;
461 : : ginxlogVacuumDataLeafPage *xlrec;
462 : :
3433 heikki.linnakangas@i 463 : 3 : xlrec = (ginxlogVacuumDataLeafPage *) XLogRecGetBlockData(record, 0, &len);
464 : :
3532 465 [ - + ]: 3 : Assert(GinPageIsLeaf(page));
466 [ - + ]: 3 : Assert(GinPageIsData(page));
467 : :
3735 468 : 3 : ginRedoRecompress(page, &xlrec->data);
4934 tgl@sss.pgh.pa.us 469 : 3 : PageSetLSN(page, lsn);
470 : 3 : MarkBufferDirty(buffer);
471 : : }
3532 heikki.linnakangas@i 472 [ + - ]: 3 : if (BufferIsValid(buffer))
473 : 3 : UnlockReleaseBuffer(buffer);
6557 teodor@sigaev.ru 474 : 3 : }
475 : :
476 : : static void
3433 heikki.linnakangas@i 477 :UBC 0 : ginRedoDeletePage(XLogReaderState *record)
478 : : {
479 : 0 : XLogRecPtr lsn = record->EndRecPtr;
6402 bruce@momjian.us 480 : 0 : ginxlogDeletePage *data = (ginxlogDeletePage *) XLogRecGetData(record);
481 : : Buffer dbuffer;
482 : : Buffer pbuffer;
483 : : Buffer lbuffer;
484 : : Page page;
485 : :
486 : : /*
487 : : * Lock left page first in order to prevent possible deadlock with
488 : : * ginStepRight().
489 : : */
1949 akorotkov@postgresql 490 [ # # ]: 0 : if (XLogReadBufferForRedo(record, 2, &lbuffer) == BLK_NEEDS_REDO)
491 : : {
492 : 0 : page = BufferGetPage(lbuffer);
493 [ # # ]: 0 : Assert(GinPageIsData(page));
494 : 0 : GinPageGetOpaque(page)->rightlink = data->rightLink;
495 : 0 : PageSetLSN(page, lsn);
496 : 0 : MarkBufferDirty(lbuffer);
497 : : }
498 : :
3433 heikki.linnakangas@i 499 [ # # ]: 0 : if (XLogReadBufferForRedo(record, 0, &dbuffer) == BLK_NEEDS_REDO)
500 : : {
2916 kgrittn@postgresql.o 501 : 0 : page = BufferGetPage(dbuffer);
3532 heikki.linnakangas@i 502 [ # # ]: 0 : Assert(GinPageIsData(page));
1608 akorotkov@postgresql 503 : 0 : GinPageSetDeleted(page);
1949 504 : 0 : GinPageSetDeleteXid(page, data->deleteXid);
3532 heikki.linnakangas@i 505 : 0 : PageSetLSN(page, lsn);
506 : 0 : MarkBufferDirty(dbuffer);
507 : : }
508 : :
3433 509 [ # # ]: 0 : if (XLogReadBufferForRedo(record, 1, &pbuffer) == BLK_NEEDS_REDO)
510 : : {
2916 kgrittn@postgresql.o 511 : 0 : page = BufferGetPage(pbuffer);
3532 heikki.linnakangas@i 512 [ # # ]: 0 : Assert(GinPageIsData(page));
513 [ # # ]: 0 : Assert(!GinPageIsLeaf(page));
514 : 0 : GinPageDeletePostingItem(page, data->parentOffset);
515 : 0 : PageSetLSN(page, lsn);
516 : 0 : MarkBufferDirty(pbuffer);
517 : : }
518 : :
519 [ # # ]: 0 : if (BufferIsValid(lbuffer))
520 : 0 : UnlockReleaseBuffer(lbuffer);
4171 tgl@sss.pgh.pa.us 521 [ # # ]: 0 : if (BufferIsValid(pbuffer))
522 : 0 : UnlockReleaseBuffer(pbuffer);
523 [ # # ]: 0 : if (BufferIsValid(dbuffer))
524 : 0 : UnlockReleaseBuffer(dbuffer);
6557 teodor@sigaev.ru 525 : 0 : }
526 : :
527 : : static void
3433 heikki.linnakangas@i 528 :CBC 23983 : ginRedoUpdateMetapage(XLogReaderState *record)
529 : : {
530 : 23983 : XLogRecPtr lsn = record->EndRecPtr;
5421 bruce@momjian.us 531 : 23983 : ginxlogUpdateMeta *data = (ginxlogUpdateMeta *) XLogRecGetData(record);
532 : : Buffer metabuffer;
533 : : Page metapage;
534 : : Buffer buffer;
535 : :
536 : : /*
537 : : * Restore the metapage. This is essentially the same as a full-page
538 : : * image, so restore the metapage unconditionally without looking at the
539 : : * LSN, to avoid torn page hazards.
540 : : */
3433 heikki.linnakangas@i 541 : 23983 : metabuffer = XLogInitBufferForRedo(record, 0);
542 [ - + ]: 23983 : Assert(BufferGetBlockNumber(metabuffer) == GIN_METAPAGE_BLKNO);
2916 kgrittn@postgresql.o 543 : 23983 : metapage = BufferGetPage(metabuffer);
544 : :
2355 tgl@sss.pgh.pa.us 545 : 23983 : GinInitMetabuffer(metabuffer);
3686 heikki.linnakangas@i 546 : 23983 : memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
547 : 23983 : PageSetLSN(metapage, lsn);
548 : 23983 : MarkBufferDirty(metabuffer);
549 : :
5421 bruce@momjian.us 550 [ + + ]: 23983 : if (data->ntuples > 0)
551 : : {
552 : : /*
553 : : * insert into tail page
554 : : */
3433 heikki.linnakangas@i 555 [ + - ]: 23795 : if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
556 : : {
2916 kgrittn@postgresql.o 557 : 23795 : Page page = BufferGetPage(buffer);
558 : : OffsetNumber off;
559 : : int i;
560 : : Size tupsize;
561 : : char *payload;
562 : : IndexTuple tuples;
563 : : Size totaltupsize;
564 : :
3433 heikki.linnakangas@i 565 : 23795 : payload = XLogRecGetBlockData(record, 1, &totaltupsize);
566 : 23795 : tuples = (IndexTuple) payload;
567 : :
3532 568 [ - + ]: 23795 : if (PageIsEmpty(page))
3532 heikki.linnakangas@i 569 :UBC 0 : off = FirstOffsetNumber;
570 : : else
3532 heikki.linnakangas@i 571 :CBC 23795 : off = OffsetNumberNext(PageGetMaxOffsetNumber(page));
572 : :
573 [ + + ]: 95172 : for (i = 0; i < data->ntuples; i++)
574 : : {
575 : 71377 : tupsize = IndexTupleSize(tuples);
576 : :
577 [ - + ]: 71377 : if (PageAddItem(page, (Item) tuples, tupsize, off,
578 : : false, false) == InvalidOffsetNumber)
3532 heikki.linnakangas@i 579 [ # # ]:UBC 0 : elog(ERROR, "failed to add item to index page");
580 : :
3532 heikki.linnakangas@i 581 :CBC 71377 : tuples = (IndexTuple) (((char *) tuples) + tupsize);
582 : :
583 : 71377 : off++;
584 : : }
3433 585 [ - + ]: 23795 : Assert(payload + totaltupsize == (char *) tuples);
586 : :
587 : : /*
588 : : * Increase counter of heap tuples
589 : : */
3532 590 : 23795 : GinPageGetOpaque(page)->maxoff++;
591 : :
592 : 23795 : PageSetLSN(page, lsn);
593 : 23795 : MarkBufferDirty(buffer);
594 : : }
595 [ + - ]: 23795 : if (BufferIsValid(buffer))
596 : 23795 : UnlockReleaseBuffer(buffer);
597 : : }
5421 bruce@momjian.us 598 [ + + ]: 188 : else if (data->prevTail != InvalidBlockNumber)
599 : : {
600 : : /*
601 : : * New tail
602 : : */
3433 heikki.linnakangas@i 603 [ + - ]: 175 : if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
604 : : {
2916 kgrittn@postgresql.o 605 : 175 : Page page = BufferGetPage(buffer);
606 : :
3532 heikki.linnakangas@i 607 : 175 : GinPageGetOpaque(page)->rightlink = data->newRightlink;
608 : :
609 : 175 : PageSetLSN(page, lsn);
610 : 175 : MarkBufferDirty(buffer);
611 : : }
612 [ + - ]: 175 : if (BufferIsValid(buffer))
613 : 175 : UnlockReleaseBuffer(buffer);
614 : : }
615 : :
5500 tgl@sss.pgh.pa.us 616 : 23983 : UnlockReleaseBuffer(metabuffer);
617 : 23983 : }
618 : :
619 : : static void
3433 heikki.linnakangas@i 620 : 180 : ginRedoInsertListPage(XLogReaderState *record)
621 : : {
622 : 180 : XLogRecPtr lsn = record->EndRecPtr;
5421 bruce@momjian.us 623 : 180 : ginxlogInsertListPage *data = (ginxlogInsertListPage *) XLogRecGetData(record);
624 : : Buffer buffer;
625 : : Page page;
626 : : OffsetNumber l,
627 : 180 : off = FirstOffsetNumber;
628 : : int i,
629 : : tupsize;
630 : : char *payload;
631 : : IndexTuple tuples;
632 : : Size totaltupsize;
633 : :
634 : : /* We always re-initialize the page. */
3433 heikki.linnakangas@i 635 : 180 : buffer = XLogInitBufferForRedo(record, 0);
2916 kgrittn@postgresql.o 636 : 180 : page = BufferGetPage(buffer);
637 : :
5500 tgl@sss.pgh.pa.us 638 : 180 : GinInitBuffer(buffer, GIN_LIST);
639 : 180 : GinPageGetOpaque(page)->rightlink = data->rightlink;
5421 bruce@momjian.us 640 [ + - ]: 180 : if (data->rightlink == InvalidBlockNumber)
641 : : {
642 : : /* tail of sublist */
5500 tgl@sss.pgh.pa.us 643 : 180 : GinPageSetFullRow(page);
644 : 180 : GinPageGetOpaque(page)->maxoff = 1;
645 : : }
646 : : else
647 : : {
5500 tgl@sss.pgh.pa.us 648 :UBC 0 : GinPageGetOpaque(page)->maxoff = 0;
649 : : }
650 : :
3433 heikki.linnakangas@i 651 :CBC 180 : payload = XLogRecGetBlockData(record, 0, &totaltupsize);
652 : :
653 : 180 : tuples = (IndexTuple) payload;
5421 bruce@momjian.us 654 [ + + ]: 717 : for (i = 0; i < data->ntuples; i++)
655 : : {
5500 tgl@sss.pgh.pa.us 656 : 537 : tupsize = IndexTupleSize(tuples);
657 : :
5421 bruce@momjian.us 658 : 537 : l = PageAddItem(page, (Item) tuples, tupsize, off, false, false);
659 : :
5500 tgl@sss.pgh.pa.us 660 [ - + ]: 537 : if (l == InvalidOffsetNumber)
5500 tgl@sss.pgh.pa.us 661 [ # # ]:UBC 0 : elog(ERROR, "failed to add item to index page");
662 : :
5421 bruce@momjian.us 663 :CBC 537 : tuples = (IndexTuple) (((char *) tuples) + tupsize);
3639 heikki.linnakangas@i 664 : 537 : off++;
665 : : }
3433 666 [ - + ]: 180 : Assert((char *) tuples == payload + totaltupsize);
667 : :
5500 tgl@sss.pgh.pa.us 668 : 180 : PageSetLSN(page, lsn);
669 : 180 : MarkBufferDirty(buffer);
670 : :
671 : 180 : UnlockReleaseBuffer(buffer);
672 : 180 : }
673 : :
674 : : static void
3433 heikki.linnakangas@i 675 : 14 : ginRedoDeleteListPages(XLogReaderState *record)
676 : : {
677 : 14 : XLogRecPtr lsn = record->EndRecPtr;
5421 bruce@momjian.us 678 : 14 : ginxlogDeleteListPages *data = (ginxlogDeleteListPages *) XLogRecGetData(record);
679 : : Buffer metabuffer;
680 : : Page metapage;
681 : : int i;
682 : :
3433 heikki.linnakangas@i 683 : 14 : metabuffer = XLogInitBufferForRedo(record, 0);
684 [ - + ]: 14 : Assert(BufferGetBlockNumber(metabuffer) == GIN_METAPAGE_BLKNO);
2916 kgrittn@postgresql.o 685 : 14 : metapage = BufferGetPage(metabuffer);
686 : :
2355 tgl@sss.pgh.pa.us 687 : 14 : GinInitMetabuffer(metabuffer);
688 : :
3686 heikki.linnakangas@i 689 : 14 : memcpy(GinPageGetMeta(metapage), &data->metadata, sizeof(GinMetaPageData));
690 : 14 : PageSetLSN(metapage, lsn);
691 : 14 : MarkBufferDirty(metabuffer);
692 : :
693 : : /*
694 : : * In normal operation, shiftList() takes exclusive lock on all the
695 : : * pages-to-be-deleted simultaneously. During replay, however, it should
696 : : * be all right to lock them one at a time. This is dependent on the fact
697 : : * that we are deleting pages from the head of the list, and that readers
698 : : * share-lock the next page before releasing the one they are on. So we
699 : : * cannot get past a reader that is on, or due to visit, any page we are
700 : : * going to delete. New incoming readers will block behind our metapage
701 : : * lock and then see a fully updated page list.
702 : : *
703 : : * No full-page images are taken of the deleted pages. Instead, they are
704 : : * re-initialized as empty, deleted pages. Their right-links don't need to
705 : : * be preserved, because no new readers can see the pages, as explained
706 : : * above.
707 : : */
5421 bruce@momjian.us 708 [ + + ]: 194 : for (i = 0; i < data->ndeleted; i++)
709 : : {
710 : : Buffer buffer;
711 : : Page page;
712 : :
3433 heikki.linnakangas@i 713 : 180 : buffer = XLogInitBufferForRedo(record, i + 1);
2916 kgrittn@postgresql.o 714 : 180 : page = BufferGetPage(buffer);
3629 heikki.linnakangas@i 715 : 180 : GinInitBuffer(buffer, GIN_DELETED);
716 : :
717 : 180 : PageSetLSN(page, lsn);
718 : 180 : MarkBufferDirty(buffer);
719 : :
720 : 180 : UnlockReleaseBuffer(buffer);
721 : : }
5500 tgl@sss.pgh.pa.us 722 : 14 : UnlockReleaseBuffer(metabuffer);
723 : 14 : }
724 : :
725 : : void
3433 heikki.linnakangas@i 726 : 51340 : gin_redo(XLogReaderState *record)
727 : : {
728 : 51340 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
729 : : MemoryContext oldCtx;
730 : :
731 : : /*
732 : : * GIN indexes do not require any conflict processing. NB: If we ever
733 : : * implement a similar optimization as we have in b-tree, and remove
734 : : * killed tuples outside VACUUM, we'll need to handle that here.
735 : : */
736 : :
3791 737 : 51340 : oldCtx = MemoryContextSwitchTo(opCtx);
6402 bruce@momjian.us 738 [ + + + - : 51340 : switch (info)
+ - + + +
- ]
739 : : {
740 : 6 : case XLOG_GIN_CREATE_PTREE:
3433 heikki.linnakangas@i 741 : 6 : ginRedoCreatePTree(record);
6557 teodor@sigaev.ru 742 : 6 : break;
6402 bruce@momjian.us 743 : 27012 : case XLOG_GIN_INSERT:
3433 heikki.linnakangas@i 744 : 27012 : ginRedoInsert(record);
6557 teodor@sigaev.ru 745 : 27012 : break;
6402 bruce@momjian.us 746 : 142 : case XLOG_GIN_SPLIT:
3433 heikki.linnakangas@i 747 : 142 : ginRedoSplit(record);
6557 teodor@sigaev.ru 748 : 142 : break;
6402 bruce@momjian.us 749 :UBC 0 : case XLOG_GIN_VACUUM_PAGE:
3433 heikki.linnakangas@i 750 : 0 : ginRedoVacuumPage(record);
6557 teodor@sigaev.ru 751 : 0 : break;
3735 heikki.linnakangas@i 752 :CBC 3 : case XLOG_GIN_VACUUM_DATA_LEAF_PAGE:
3433 753 : 3 : ginRedoVacuumDataLeafPage(record);
3735 754 : 3 : break;
6402 bruce@momjian.us 755 :UBC 0 : case XLOG_GIN_DELETE_PAGE:
3433 heikki.linnakangas@i 756 : 0 : ginRedoDeletePage(record);
6557 teodor@sigaev.ru 757 : 0 : break;
5500 tgl@sss.pgh.pa.us 758 :CBC 23983 : case XLOG_GIN_UPDATE_META_PAGE:
3433 heikki.linnakangas@i 759 : 23983 : ginRedoUpdateMetapage(record);
5500 tgl@sss.pgh.pa.us 760 : 23983 : break;
761 : 180 : case XLOG_GIN_INSERT_LISTPAGE:
3433 heikki.linnakangas@i 762 : 180 : ginRedoInsertListPage(record);
5500 tgl@sss.pgh.pa.us 763 : 180 : break;
5421 bruce@momjian.us 764 : 14 : case XLOG_GIN_DELETE_LISTPAGE:
3433 heikki.linnakangas@i 765 : 14 : ginRedoDeleteListPages(record);
5500 tgl@sss.pgh.pa.us 766 : 14 : break;
6557 teodor@sigaev.ru 767 :UBC 0 : default:
768 [ # # ]: 0 : elog(PANIC, "gin_redo: unknown op code %u", info);
769 : : }
3791 heikki.linnakangas@i 770 :CBC 51340 : MemoryContextSwitchTo(oldCtx);
6557 teodor@sigaev.ru 771 : 51340 : MemoryContextReset(opCtx);
772 : 51340 : }
773 : :
774 : : void
6402 bruce@momjian.us 775 : 232 : gin_xlog_startup(void)
776 : : {
6557 teodor@sigaev.ru 777 : 232 : opCtx = AllocSetContextCreate(CurrentMemoryContext,
778 : : "GIN recovery temporary context",
779 : : ALLOCSET_DEFAULT_SIZES);
780 : 232 : }
781 : :
782 : : void
6402 bruce@momjian.us 783 : 140 : gin_xlog_cleanup(void)
784 : : {
6557 teodor@sigaev.ru 785 : 140 : MemoryContextDelete(opCtx);
2787 tgl@sss.pgh.pa.us 786 : 140 : opCtx = NULL;
6460 787 : 140 : }
788 : :
789 : : /*
790 : : * Mask a GIN page before running consistency checks on it.
791 : : */
792 : : void
2622 rhaas@postgresql.org 793 :UBC 0 : gin_mask(char *pagedata, BlockNumber blkno)
794 : : {
795 : 0 : Page page = (Page) pagedata;
2355 tgl@sss.pgh.pa.us 796 : 0 : PageHeader pagehdr = (PageHeader) page;
797 : : GinPageOpaque opaque;
798 : :
2396 rhaas@postgresql.org 799 : 0 : mask_page_lsn_and_checksum(page);
2622 800 : 0 : opaque = GinPageGetOpaque(page);
801 : :
802 : 0 : mask_page_hint_bits(page);
803 : :
804 : : /*
805 : : * For a GIN_DELETED page, the page is initialized to empty. Hence, mask
806 : : * the whole page content. For other pages, mask the hole if pd_lower
807 : : * appears to have been set correctly.
808 : : */
2355 tgl@sss.pgh.pa.us 809 [ # # ]: 0 : if (opaque->flags & GIN_DELETED)
810 : 0 : mask_page_content(page);
811 [ # # ]: 0 : else if (pagehdr->pd_lower > SizeOfPageHeaderData)
812 : 0 : mask_unused_space(page);
2622 rhaas@postgresql.org 813 : 0 : }
|