Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * heapam_xlog.c
4 : * WAL replay logic for heap access method.
5 : *
6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : *
10 : * IDENTIFICATION
11 : * src/backend/access/heap/heapam_xlog.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : #include "postgres.h"
16 :
17 : #include "access/bufmask.h"
18 : #include "access/heapam.h"
19 : #include "access/visibilitymap.h"
20 : #include "access/xlog.h"
21 : #include "access/xlogutils.h"
22 : #include "storage/freespace.h"
23 : #include "storage/standby.h"
24 :
25 :
26 : /*
27 : * Replay XLOG_HEAP2_PRUNE_* records.
28 : */
29 : static void
30 17814 : heap_xlog_prune_freeze(XLogReaderState *record)
31 : {
32 17814 : XLogRecPtr lsn = record->EndRecPtr;
33 17814 : char *maindataptr = XLogRecGetData(record);
34 : xl_heap_prune xlrec;
35 : Buffer buffer;
36 : RelFileLocator rlocator;
37 : BlockNumber blkno;
38 : XLogRedoAction action;
39 :
40 17814 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
41 17814 : memcpy(&xlrec, maindataptr, SizeOfHeapPrune);
42 17814 : maindataptr += SizeOfHeapPrune;
43 :
44 : /*
45 : * We will take an ordinary exclusive lock or a cleanup lock depending on
46 : * whether the XLHP_CLEANUP_LOCK flag is set. With an ordinary exclusive
47 : * lock, we better not be doing anything that requires moving existing
48 : * tuple data.
49 : */
50 : Assert((xlrec.flags & XLHP_CLEANUP_LOCK) != 0 ||
51 : (xlrec.flags & (XLHP_HAS_REDIRECTIONS | XLHP_HAS_DEAD_ITEMS)) == 0);
52 :
53 : /*
54 : * We are about to remove and/or freeze tuples. In Hot Standby mode,
55 : * ensure that there are no queries running for which the removed tuples
56 : * are still visible or which still consider the frozen xids as running.
57 : * The conflict horizon XID comes after xl_heap_prune.
58 : */
59 17814 : if ((xlrec.flags & XLHP_HAS_CONFLICT_HORIZON) != 0)
60 : {
61 : TransactionId snapshot_conflict_horizon;
62 :
63 : /* memcpy() because snapshot_conflict_horizon is stored unaligned */
64 14298 : memcpy(&snapshot_conflict_horizon, maindataptr, sizeof(TransactionId));
65 14298 : maindataptr += sizeof(TransactionId);
66 :
67 14298 : if (InHotStandby)
68 13828 : ResolveRecoveryConflictWithSnapshot(snapshot_conflict_horizon,
69 13828 : (xlrec.flags & XLHP_IS_CATALOG_REL) != 0,
70 : rlocator);
71 : }
72 :
73 : /*
74 : * If we have a full-page image, restore it and we're done.
75 : */
76 17814 : action = XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL,
77 17814 : (xlrec.flags & XLHP_CLEANUP_LOCK) != 0,
78 : &buffer);
79 17814 : if (action == BLK_NEEDS_REDO)
80 : {
81 15564 : Page page = (Page) BufferGetPage(buffer);
82 : OffsetNumber *redirected;
83 : OffsetNumber *nowdead;
84 : OffsetNumber *nowunused;
85 : int nredirected;
86 : int ndead;
87 : int nunused;
88 : int nplans;
89 : Size datalen;
90 : xlhp_freeze_plan *plans;
91 : OffsetNumber *frz_offsets;
92 15564 : char *dataptr = XLogRecGetBlockData(record, 0, &datalen);
93 :
94 15564 : heap_xlog_deserialize_prune_and_freeze(dataptr, xlrec.flags,
95 : &nplans, &plans, &frz_offsets,
96 : &nredirected, &redirected,
97 : &ndead, &nowdead,
98 : &nunused, &nowunused);
99 :
100 : /*
101 : * Update all line pointers per the record, and repair fragmentation
102 : * if needed.
103 : */
104 15564 : if (nredirected > 0 || ndead > 0 || nunused > 0)
105 14612 : heap_page_prune_execute(buffer,
106 14612 : (xlrec.flags & XLHP_CLEANUP_LOCK) == 0,
107 : redirected, nredirected,
108 : nowdead, ndead,
109 : nowunused, nunused);
110 :
111 : /* Freeze tuples */
112 16706 : for (int p = 0; p < nplans; p++)
113 : {
114 : HeapTupleFreeze frz;
115 :
116 : /*
117 : * Convert freeze plan representation from WAL record into
118 : * per-tuple format used by heap_execute_freeze_tuple
119 : */
120 1142 : frz.xmax = plans[p].xmax;
121 1142 : frz.t_infomask2 = plans[p].t_infomask2;
122 1142 : frz.t_infomask = plans[p].t_infomask;
123 1142 : frz.frzflags = plans[p].frzflags;
124 1142 : frz.offset = InvalidOffsetNumber; /* unused, but be tidy */
125 :
126 100760 : for (int i = 0; i < plans[p].ntuples; i++)
127 : {
128 99618 : OffsetNumber offset = *(frz_offsets++);
129 : ItemId lp;
130 : HeapTupleHeader tuple;
131 :
132 99618 : lp = PageGetItemId(page, offset);
133 99618 : tuple = (HeapTupleHeader) PageGetItem(page, lp);
134 99618 : heap_execute_freeze_tuple(tuple, &frz);
135 : }
136 : }
137 :
138 : /* There should be no more data */
139 : Assert((char *) frz_offsets == dataptr + datalen);
140 :
141 : /*
142 : * Note: we don't worry about updating the page's prunability hints.
143 : * At worst this will cause an extra prune cycle to occur soon.
144 : */
145 :
146 15564 : PageSetLSN(page, lsn);
147 15564 : MarkBufferDirty(buffer);
148 : }
149 :
150 : /*
151 : * If we released any space or line pointers, update the free space map.
152 : *
153 : * Do this regardless of a full-page image being applied, since the FSM
154 : * data is not in the page anyway.
155 : */
156 17814 : if (BufferIsValid(buffer))
157 : {
158 17814 : if (xlrec.flags & (XLHP_HAS_REDIRECTIONS |
159 : XLHP_HAS_DEAD_ITEMS |
160 : XLHP_HAS_NOW_UNUSED_ITEMS))
161 : {
162 16860 : Size freespace = PageGetHeapFreeSpace(BufferGetPage(buffer));
163 :
164 16860 : UnlockReleaseBuffer(buffer);
165 :
166 16860 : XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
167 : }
168 : else
169 954 : UnlockReleaseBuffer(buffer);
170 : }
171 17814 : }
172 :
173 : /*
174 : * Replay XLOG_HEAP2_VISIBLE records.
175 : *
176 : * The critical integrity requirement here is that we must never end up with
177 : * a situation where the visibility map bit is set, and the page-level
178 : * PD_ALL_VISIBLE bit is clear. If that were to occur, then a subsequent
179 : * page modification would fail to clear the visibility map bit.
180 : */
181 : static void
182 7622 : heap_xlog_visible(XLogReaderState *record)
183 : {
184 7622 : XLogRecPtr lsn = record->EndRecPtr;
185 7622 : xl_heap_visible *xlrec = (xl_heap_visible *) XLogRecGetData(record);
186 7622 : Buffer vmbuffer = InvalidBuffer;
187 : Buffer buffer;
188 : Page page;
189 : RelFileLocator rlocator;
190 : BlockNumber blkno;
191 : XLogRedoAction action;
192 :
193 : Assert((xlrec->flags & VISIBILITYMAP_XLOG_VALID_BITS) == xlrec->flags);
194 :
195 7622 : XLogRecGetBlockTag(record, 1, &rlocator, NULL, &blkno);
196 :
197 : /*
198 : * If there are any Hot Standby transactions running that have an xmin
199 : * horizon old enough that this page isn't all-visible for them, they
200 : * might incorrectly decide that an index-only scan can skip a heap fetch.
201 : *
202 : * NB: It might be better to throw some kind of "soft" conflict here that
203 : * forces any index-only scan that is in flight to perform heap fetches,
204 : * rather than killing the transaction outright.
205 : */
206 7622 : if (InHotStandby)
207 7274 : ResolveRecoveryConflictWithSnapshot(xlrec->snapshotConflictHorizon,
208 7274 : xlrec->flags & VISIBILITYMAP_XLOG_CATALOG_REL,
209 : rlocator);
210 :
211 : /*
212 : * Read the heap page, if it still exists. If the heap file has dropped or
213 : * truncated later in recovery, we don't need to update the page, but we'd
214 : * better still update the visibility map.
215 : */
216 7622 : action = XLogReadBufferForRedo(record, 1, &buffer);
217 7622 : if (action == BLK_NEEDS_REDO)
218 : {
219 : /*
220 : * We don't bump the LSN of the heap page when setting the visibility
221 : * map bit (unless checksums or wal_hint_bits is enabled, in which
222 : * case we must). This exposes us to torn page hazards, but since
223 : * we're not inspecting the existing page contents in any way, we
224 : * don't care.
225 : */
226 5558 : page = BufferGetPage(buffer);
227 :
228 5558 : PageSetAllVisible(page);
229 :
230 5558 : if (XLogHintBitIsNeeded())
231 5558 : PageSetLSN(page, lsn);
232 :
233 5558 : MarkBufferDirty(buffer);
234 : }
235 : else if (action == BLK_RESTORED)
236 : {
237 : /*
238 : * If heap block was backed up, we already restored it and there's
239 : * nothing more to do. (This can only happen with checksums or
240 : * wal_log_hints enabled.)
241 : */
242 : }
243 :
244 7622 : if (BufferIsValid(buffer))
245 : {
246 7622 : Size space = PageGetFreeSpace(BufferGetPage(buffer));
247 :
248 7622 : UnlockReleaseBuffer(buffer);
249 :
250 : /*
251 : * Since FSM is not WAL-logged and only updated heuristically, it
252 : * easily becomes stale in standbys. If the standby is later promoted
253 : * and runs VACUUM, it will skip updating individual free space
254 : * figures for pages that became all-visible (or all-frozen, depending
255 : * on the vacuum mode,) which is troublesome when FreeSpaceMapVacuum
256 : * propagates too optimistic free space values to upper FSM layers;
257 : * later inserters try to use such pages only to find out that they
258 : * are unusable. This can cause long stalls when there are many such
259 : * pages.
260 : *
261 : * Forestall those problems by updating FSM's idea about a page that
262 : * is becoming all-visible or all-frozen.
263 : *
264 : * Do this regardless of a full-page image being applied, since the
265 : * FSM data is not in the page anyway.
266 : */
267 7622 : if (xlrec->flags & VISIBILITYMAP_VALID_BITS)
268 7622 : XLogRecordPageWithFreeSpace(rlocator, blkno, space);
269 : }
270 :
271 : /*
272 : * Even if we skipped the heap page update due to the LSN interlock, it's
273 : * still safe to update the visibility map. Any WAL record that clears
274 : * the visibility map bit does so before checking the page LSN, so any
275 : * bits that need to be cleared will still be cleared.
276 : */
277 7622 : if (XLogReadBufferForRedoExtended(record, 0, RBM_ZERO_ON_ERROR, false,
278 : &vmbuffer) == BLK_NEEDS_REDO)
279 : {
280 7188 : Page vmpage = BufferGetPage(vmbuffer);
281 : Relation reln;
282 : uint8 vmbits;
283 :
284 : /* initialize the page if it was read as zeros */
285 7188 : if (PageIsNew(vmpage))
286 0 : PageInit(vmpage, BLCKSZ, 0);
287 :
288 : /* remove VISIBILITYMAP_XLOG_* */
289 7188 : vmbits = xlrec->flags & VISIBILITYMAP_VALID_BITS;
290 :
291 : /*
292 : * XLogReadBufferForRedoExtended locked the buffer. But
293 : * visibilitymap_set will handle locking itself.
294 : */
295 7188 : LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
296 :
297 7188 : reln = CreateFakeRelcacheEntry(rlocator);
298 7188 : visibilitymap_pin(reln, blkno, &vmbuffer);
299 :
300 7188 : visibilitymap_set(reln, blkno, InvalidBuffer, lsn, vmbuffer,
301 : xlrec->snapshotConflictHorizon, vmbits);
302 :
303 7188 : ReleaseBuffer(vmbuffer);
304 7188 : FreeFakeRelcacheEntry(reln);
305 : }
306 434 : else if (BufferIsValid(vmbuffer))
307 434 : UnlockReleaseBuffer(vmbuffer);
308 7622 : }
309 :
310 : /*
311 : * Given an "infobits" field from an XLog record, set the correct bits in the
312 : * given infomask and infomask2 for the tuple touched by the record.
313 : *
314 : * (This is the reverse of compute_infobits).
315 : */
316 : static void
317 870540 : fix_infomask_from_infobits(uint8 infobits, uint16 *infomask, uint16 *infomask2)
318 : {
319 870540 : *infomask &= ~(HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY |
320 : HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_EXCL_LOCK);
321 870540 : *infomask2 &= ~HEAP_KEYS_UPDATED;
322 :
323 870540 : if (infobits & XLHL_XMAX_IS_MULTI)
324 4 : *infomask |= HEAP_XMAX_IS_MULTI;
325 870540 : if (infobits & XLHL_XMAX_LOCK_ONLY)
326 108608 : *infomask |= HEAP_XMAX_LOCK_ONLY;
327 870540 : if (infobits & XLHL_XMAX_EXCL_LOCK)
328 107814 : *infomask |= HEAP_XMAX_EXCL_LOCK;
329 : /* note HEAP_XMAX_SHR_LOCK isn't considered here */
330 870540 : if (infobits & XLHL_XMAX_KEYSHR_LOCK)
331 816 : *infomask |= HEAP_XMAX_KEYSHR_LOCK;
332 :
333 870540 : if (infobits & XLHL_KEYS_UPDATED)
334 581766 : *infomask2 |= HEAP_KEYS_UPDATED;
335 870540 : }
336 :
337 : /*
338 : * Replay XLOG_HEAP_DELETE records.
339 : */
340 : static void
341 582660 : heap_xlog_delete(XLogReaderState *record)
342 : {
343 582660 : XLogRecPtr lsn = record->EndRecPtr;
344 582660 : xl_heap_delete *xlrec = (xl_heap_delete *) XLogRecGetData(record);
345 : Buffer buffer;
346 : Page page;
347 582660 : ItemId lp = NULL;
348 : HeapTupleHeader htup;
349 : BlockNumber blkno;
350 : RelFileLocator target_locator;
351 : ItemPointerData target_tid;
352 :
353 582660 : XLogRecGetBlockTag(record, 0, &target_locator, NULL, &blkno);
354 582660 : ItemPointerSetBlockNumber(&target_tid, blkno);
355 582660 : ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
356 :
357 : /*
358 : * The visibility map may need to be fixed even if the heap page is
359 : * already up-to-date.
360 : */
361 582660 : if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
362 : {
363 344 : Relation reln = CreateFakeRelcacheEntry(target_locator);
364 344 : Buffer vmbuffer = InvalidBuffer;
365 :
366 344 : visibilitymap_pin(reln, blkno, &vmbuffer);
367 344 : visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
368 344 : ReleaseBuffer(vmbuffer);
369 344 : FreeFakeRelcacheEntry(reln);
370 : }
371 :
372 582660 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
373 : {
374 579064 : page = BufferGetPage(buffer);
375 :
376 579064 : if (PageGetMaxOffsetNumber(page) >= xlrec->offnum)
377 579064 : lp = PageGetItemId(page, xlrec->offnum);
378 :
379 579064 : if (PageGetMaxOffsetNumber(page) < xlrec->offnum || !ItemIdIsNormal(lp))
380 0 : elog(PANIC, "invalid lp");
381 :
382 579064 : htup = (HeapTupleHeader) PageGetItem(page, lp);
383 :
384 579064 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
385 579064 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
386 579064 : HeapTupleHeaderClearHotUpdated(htup);
387 579064 : fix_infomask_from_infobits(xlrec->infobits_set,
388 : &htup->t_infomask, &htup->t_infomask2);
389 579064 : if (!(xlrec->flags & XLH_DELETE_IS_SUPER))
390 579064 : HeapTupleHeaderSetXmax(htup, xlrec->xmax);
391 : else
392 0 : HeapTupleHeaderSetXmin(htup, InvalidTransactionId);
393 579064 : HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
394 :
395 : /* Mark the page as a candidate for pruning */
396 579064 : PageSetPrunable(page, XLogRecGetXid(record));
397 :
398 579064 : if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
399 6 : PageClearAllVisible(page);
400 :
401 : /* Make sure t_ctid is set correctly */
402 579064 : if (xlrec->flags & XLH_DELETE_IS_PARTITION_MOVE)
403 270 : HeapTupleHeaderSetMovedPartitions(htup);
404 : else
405 578794 : htup->t_ctid = target_tid;
406 579064 : PageSetLSN(page, lsn);
407 579064 : MarkBufferDirty(buffer);
408 : }
409 582660 : if (BufferIsValid(buffer))
410 582660 : UnlockReleaseBuffer(buffer);
411 582660 : }
412 :
413 : /*
414 : * Replay XLOG_HEAP_INSERT records.
415 : */
416 : static void
417 2495992 : heap_xlog_insert(XLogReaderState *record)
418 : {
419 2495992 : XLogRecPtr lsn = record->EndRecPtr;
420 2495992 : xl_heap_insert *xlrec = (xl_heap_insert *) XLogRecGetData(record);
421 : Buffer buffer;
422 : Page page;
423 : union
424 : {
425 : HeapTupleHeaderData hdr;
426 : char data[MaxHeapTupleSize];
427 : } tbuf;
428 : HeapTupleHeader htup;
429 : xl_heap_header xlhdr;
430 : uint32 newlen;
431 2495992 : Size freespace = 0;
432 : RelFileLocator target_locator;
433 : BlockNumber blkno;
434 : ItemPointerData target_tid;
435 : XLogRedoAction action;
436 :
437 2495992 : XLogRecGetBlockTag(record, 0, &target_locator, NULL, &blkno);
438 2495992 : ItemPointerSetBlockNumber(&target_tid, blkno);
439 2495992 : ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
440 :
441 : /*
442 : * The visibility map may need to be fixed even if the heap page is
443 : * already up-to-date.
444 : */
445 2495992 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
446 : {
447 1186 : Relation reln = CreateFakeRelcacheEntry(target_locator);
448 1186 : Buffer vmbuffer = InvalidBuffer;
449 :
450 1186 : visibilitymap_pin(reln, blkno, &vmbuffer);
451 1186 : visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
452 1186 : ReleaseBuffer(vmbuffer);
453 1186 : FreeFakeRelcacheEntry(reln);
454 : }
455 :
456 : /*
457 : * If we inserted the first and only tuple on the page, re-initialize the
458 : * page from scratch.
459 : */
460 2495992 : if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
461 : {
462 31550 : buffer = XLogInitBufferForRedo(record, 0);
463 31550 : page = BufferGetPage(buffer);
464 31550 : PageInit(page, BufferGetPageSize(buffer), 0);
465 31550 : action = BLK_NEEDS_REDO;
466 : }
467 : else
468 2464442 : action = XLogReadBufferForRedo(record, 0, &buffer);
469 2495992 : if (action == BLK_NEEDS_REDO)
470 : {
471 : Size datalen;
472 : char *data;
473 :
474 2491558 : page = BufferGetPage(buffer);
475 :
476 2491558 : if (PageGetMaxOffsetNumber(page) + 1 < xlrec->offnum)
477 0 : elog(PANIC, "invalid max offset number");
478 :
479 2491558 : data = XLogRecGetBlockData(record, 0, &datalen);
480 :
481 2491558 : newlen = datalen - SizeOfHeapHeader;
482 : Assert(datalen > SizeOfHeapHeader && newlen <= MaxHeapTupleSize);
483 2491558 : memcpy((char *) &xlhdr, data, SizeOfHeapHeader);
484 2491558 : data += SizeOfHeapHeader;
485 :
486 2491558 : htup = &tbuf.hdr;
487 2491558 : MemSet((char *) htup, 0, SizeofHeapTupleHeader);
488 : /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
489 2491558 : memcpy((char *) htup + SizeofHeapTupleHeader,
490 : data,
491 : newlen);
492 2491558 : newlen += SizeofHeapTupleHeader;
493 2491558 : htup->t_infomask2 = xlhdr.t_infomask2;
494 2491558 : htup->t_infomask = xlhdr.t_infomask;
495 2491558 : htup->t_hoff = xlhdr.t_hoff;
496 2491558 : HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
497 2491558 : HeapTupleHeaderSetCmin(htup, FirstCommandId);
498 2491558 : htup->t_ctid = target_tid;
499 :
500 2491558 : if (PageAddItem(page, (Item) htup, newlen, xlrec->offnum,
501 : true, true) == InvalidOffsetNumber)
502 0 : elog(PANIC, "failed to add tuple");
503 :
504 2491558 : freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
505 :
506 2491558 : PageSetLSN(page, lsn);
507 :
508 2491558 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
509 376 : PageClearAllVisible(page);
510 :
511 : /* XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible */
512 2491558 : if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
513 0 : PageSetAllVisible(page);
514 :
515 2491558 : MarkBufferDirty(buffer);
516 : }
517 2495992 : if (BufferIsValid(buffer))
518 2495992 : UnlockReleaseBuffer(buffer);
519 :
520 : /*
521 : * If the page is running low on free space, update the FSM as well.
522 : * Arbitrarily, our definition of "low" is less than 20%. We can't do much
523 : * better than that without knowing the fill-factor for the table.
524 : *
525 : * XXX: Don't do this if the page was restored from full page image. We
526 : * don't bother to update the FSM in that case, it doesn't need to be
527 : * totally accurate anyway.
528 : */
529 2495992 : if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
530 490106 : XLogRecordPageWithFreeSpace(target_locator, blkno, freespace);
531 2495992 : }
532 :
533 : /*
534 : * Replay XLOG_HEAP2_MULTI_INSERT records.
535 : */
536 : static void
537 106728 : heap_xlog_multi_insert(XLogReaderState *record)
538 : {
539 106728 : XLogRecPtr lsn = record->EndRecPtr;
540 : xl_heap_multi_insert *xlrec;
541 : RelFileLocator rlocator;
542 : BlockNumber blkno;
543 : Buffer buffer;
544 : Page page;
545 : union
546 : {
547 : HeapTupleHeaderData hdr;
548 : char data[MaxHeapTupleSize];
549 : } tbuf;
550 : HeapTupleHeader htup;
551 : uint32 newlen;
552 106728 : Size freespace = 0;
553 : int i;
554 106728 : bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
555 : XLogRedoAction action;
556 :
557 : /*
558 : * Insertion doesn't overwrite MVCC data, so no conflict processing is
559 : * required.
560 : */
561 106728 : xlrec = (xl_heap_multi_insert *) XLogRecGetData(record);
562 :
563 106728 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
564 :
565 : /* check that the mutually exclusive flags are not both set */
566 : Assert(!((xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) &&
567 : (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)));
568 :
569 : /*
570 : * The visibility map may need to be fixed even if the heap page is
571 : * already up-to-date.
572 : */
573 106728 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
574 : {
575 1002 : Relation reln = CreateFakeRelcacheEntry(rlocator);
576 1002 : Buffer vmbuffer = InvalidBuffer;
577 :
578 1002 : visibilitymap_pin(reln, blkno, &vmbuffer);
579 1002 : visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
580 1002 : ReleaseBuffer(vmbuffer);
581 1002 : FreeFakeRelcacheEntry(reln);
582 : }
583 :
584 106728 : if (isinit)
585 : {
586 4112 : buffer = XLogInitBufferForRedo(record, 0);
587 4112 : page = BufferGetPage(buffer);
588 4112 : PageInit(page, BufferGetPageSize(buffer), 0);
589 4112 : action = BLK_NEEDS_REDO;
590 : }
591 : else
592 102616 : action = XLogReadBufferForRedo(record, 0, &buffer);
593 106728 : if (action == BLK_NEEDS_REDO)
594 : {
595 : char *tupdata;
596 : char *endptr;
597 : Size len;
598 :
599 : /* Tuples are stored as block data */
600 104778 : tupdata = XLogRecGetBlockData(record, 0, &len);
601 104778 : endptr = tupdata + len;
602 :
603 104778 : page = (Page) BufferGetPage(buffer);
604 :
605 505984 : for (i = 0; i < xlrec->ntuples; i++)
606 : {
607 : OffsetNumber offnum;
608 : xl_multi_insert_tuple *xlhdr;
609 :
610 : /*
611 : * If we're reinitializing the page, the tuples are stored in
612 : * order from FirstOffsetNumber. Otherwise there's an array of
613 : * offsets in the WAL record, and the tuples come after that.
614 : */
615 401206 : if (isinit)
616 200080 : offnum = FirstOffsetNumber + i;
617 : else
618 201126 : offnum = xlrec->offsets[i];
619 401206 : if (PageGetMaxOffsetNumber(page) + 1 < offnum)
620 0 : elog(PANIC, "invalid max offset number");
621 :
622 401206 : xlhdr = (xl_multi_insert_tuple *) SHORTALIGN(tupdata);
623 401206 : tupdata = ((char *) xlhdr) + SizeOfMultiInsertTuple;
624 :
625 401206 : newlen = xlhdr->datalen;
626 : Assert(newlen <= MaxHeapTupleSize);
627 401206 : htup = &tbuf.hdr;
628 401206 : MemSet((char *) htup, 0, SizeofHeapTupleHeader);
629 : /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
630 401206 : memcpy((char *) htup + SizeofHeapTupleHeader,
631 : (char *) tupdata,
632 : newlen);
633 401206 : tupdata += newlen;
634 :
635 401206 : newlen += SizeofHeapTupleHeader;
636 401206 : htup->t_infomask2 = xlhdr->t_infomask2;
637 401206 : htup->t_infomask = xlhdr->t_infomask;
638 401206 : htup->t_hoff = xlhdr->t_hoff;
639 401206 : HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
640 401206 : HeapTupleHeaderSetCmin(htup, FirstCommandId);
641 401206 : ItemPointerSetBlockNumber(&htup->t_ctid, blkno);
642 401206 : ItemPointerSetOffsetNumber(&htup->t_ctid, offnum);
643 :
644 401206 : offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
645 401206 : if (offnum == InvalidOffsetNumber)
646 0 : elog(PANIC, "failed to add tuple");
647 : }
648 104778 : if (tupdata != endptr)
649 0 : elog(PANIC, "total tuple length mismatch");
650 :
651 104778 : freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
652 :
653 104778 : PageSetLSN(page, lsn);
654 :
655 104778 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
656 164 : PageClearAllVisible(page);
657 :
658 : /* XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible */
659 104778 : if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
660 8 : PageSetAllVisible(page);
661 :
662 104778 : MarkBufferDirty(buffer);
663 : }
664 106728 : if (BufferIsValid(buffer))
665 106728 : UnlockReleaseBuffer(buffer);
666 :
667 : /*
668 : * If the page is running low on free space, update the FSM as well.
669 : * Arbitrarily, our definition of "low" is less than 20%. We can't do much
670 : * better than that without knowing the fill-factor for the table.
671 : *
672 : * XXX: Don't do this if the page was restored from full page image. We
673 : * don't bother to update the FSM in that case, it doesn't need to be
674 : * totally accurate anyway.
675 : */
676 106728 : if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
677 26698 : XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
678 106728 : }
679 :
680 : /*
681 : * Replay XLOG_HEAP_UPDATE and XLOG_HEAP_HOT_UPDATE records.
682 : */
683 : static void
684 183374 : heap_xlog_update(XLogReaderState *record, bool hot_update)
685 : {
686 183374 : XLogRecPtr lsn = record->EndRecPtr;
687 183374 : xl_heap_update *xlrec = (xl_heap_update *) XLogRecGetData(record);
688 : RelFileLocator rlocator;
689 : BlockNumber oldblk;
690 : BlockNumber newblk;
691 : ItemPointerData newtid;
692 : Buffer obuffer,
693 : nbuffer;
694 : Page page;
695 : OffsetNumber offnum;
696 183374 : ItemId lp = NULL;
697 : HeapTupleData oldtup;
698 : HeapTupleHeader htup;
699 183374 : uint16 prefixlen = 0,
700 183374 : suffixlen = 0;
701 : char *newp;
702 : union
703 : {
704 : HeapTupleHeaderData hdr;
705 : char data[MaxHeapTupleSize];
706 : } tbuf;
707 : xl_heap_header xlhdr;
708 : uint32 newlen;
709 183374 : Size freespace = 0;
710 : XLogRedoAction oldaction;
711 : XLogRedoAction newaction;
712 :
713 : /* initialize to keep the compiler quiet */
714 183374 : oldtup.t_data = NULL;
715 183374 : oldtup.t_len = 0;
716 :
717 183374 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, &newblk);
718 183374 : if (XLogRecGetBlockTagExtended(record, 1, NULL, NULL, &oldblk, NULL))
719 : {
720 : /* HOT updates are never done across pages */
721 : Assert(!hot_update);
722 : }
723 : else
724 76254 : oldblk = newblk;
725 :
726 183374 : ItemPointerSet(&newtid, newblk, xlrec->new_offnum);
727 :
728 : /*
729 : * The visibility map may need to be fixed even if the heap page is
730 : * already up-to-date.
731 : */
732 183374 : if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
733 : {
734 340 : Relation reln = CreateFakeRelcacheEntry(rlocator);
735 340 : Buffer vmbuffer = InvalidBuffer;
736 :
737 340 : visibilitymap_pin(reln, oldblk, &vmbuffer);
738 340 : visibilitymap_clear(reln, oldblk, vmbuffer, VISIBILITYMAP_VALID_BITS);
739 340 : ReleaseBuffer(vmbuffer);
740 340 : FreeFakeRelcacheEntry(reln);
741 : }
742 :
743 : /*
744 : * In normal operation, it is important to lock the two pages in
745 : * page-number order, to avoid possible deadlocks against other update
746 : * operations going the other way. However, during WAL replay there can
747 : * be no other update happening, so we don't need to worry about that. But
748 : * we *do* need to worry that we don't expose an inconsistent state to Hot
749 : * Standby queries --- so the original page can't be unlocked before we've
750 : * added the new tuple to the new page.
751 : */
752 :
753 : /* Deal with old tuple version */
754 183374 : oldaction = XLogReadBufferForRedo(record, (oldblk == newblk) ? 0 : 1,
755 : &obuffer);
756 183374 : if (oldaction == BLK_NEEDS_REDO)
757 : {
758 182868 : page = BufferGetPage(obuffer);
759 182868 : offnum = xlrec->old_offnum;
760 182868 : if (PageGetMaxOffsetNumber(page) >= offnum)
761 182868 : lp = PageGetItemId(page, offnum);
762 :
763 182868 : if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
764 0 : elog(PANIC, "invalid lp");
765 :
766 182868 : htup = (HeapTupleHeader) PageGetItem(page, lp);
767 :
768 182868 : oldtup.t_data = htup;
769 182868 : oldtup.t_len = ItemIdGetLength(lp);
770 :
771 182868 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
772 182868 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
773 182868 : if (hot_update)
774 70132 : HeapTupleHeaderSetHotUpdated(htup);
775 : else
776 112736 : HeapTupleHeaderClearHotUpdated(htup);
777 182868 : fix_infomask_from_infobits(xlrec->old_infobits_set, &htup->t_infomask,
778 : &htup->t_infomask2);
779 182868 : HeapTupleHeaderSetXmax(htup, xlrec->old_xmax);
780 182868 : HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
781 : /* Set forward chain link in t_ctid */
782 182868 : htup->t_ctid = newtid;
783 :
784 : /* Mark the page as a candidate for pruning */
785 182868 : PageSetPrunable(page, XLogRecGetXid(record));
786 :
787 182868 : if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
788 332 : PageClearAllVisible(page);
789 :
790 182868 : PageSetLSN(page, lsn);
791 182868 : MarkBufferDirty(obuffer);
792 : }
793 :
794 : /*
795 : * Read the page the new tuple goes into, if different from old.
796 : */
797 183374 : if (oldblk == newblk)
798 : {
799 76254 : nbuffer = obuffer;
800 76254 : newaction = oldaction;
801 : }
802 107120 : else if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
803 : {
804 1210 : nbuffer = XLogInitBufferForRedo(record, 0);
805 1210 : page = (Page) BufferGetPage(nbuffer);
806 1210 : PageInit(page, BufferGetPageSize(nbuffer), 0);
807 1210 : newaction = BLK_NEEDS_REDO;
808 : }
809 : else
810 105910 : newaction = XLogReadBufferForRedo(record, 0, &nbuffer);
811 :
812 : /*
813 : * The visibility map may need to be fixed even if the heap page is
814 : * already up-to-date.
815 : */
816 183374 : if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
817 : {
818 130 : Relation reln = CreateFakeRelcacheEntry(rlocator);
819 130 : Buffer vmbuffer = InvalidBuffer;
820 :
821 130 : visibilitymap_pin(reln, newblk, &vmbuffer);
822 130 : visibilitymap_clear(reln, newblk, vmbuffer, VISIBILITYMAP_VALID_BITS);
823 130 : ReleaseBuffer(vmbuffer);
824 130 : FreeFakeRelcacheEntry(reln);
825 : }
826 :
827 : /* Deal with new tuple */
828 183374 : if (newaction == BLK_NEEDS_REDO)
829 : {
830 : char *recdata;
831 : char *recdata_end;
832 : Size datalen;
833 : Size tuplen;
834 :
835 182656 : recdata = XLogRecGetBlockData(record, 0, &datalen);
836 182656 : recdata_end = recdata + datalen;
837 :
838 182656 : page = BufferGetPage(nbuffer);
839 :
840 182656 : offnum = xlrec->new_offnum;
841 182656 : if (PageGetMaxOffsetNumber(page) + 1 < offnum)
842 0 : elog(PANIC, "invalid max offset number");
843 :
844 182656 : if (xlrec->flags & XLH_UPDATE_PREFIX_FROM_OLD)
845 : {
846 : Assert(newblk == oldblk);
847 28604 : memcpy(&prefixlen, recdata, sizeof(uint16));
848 28604 : recdata += sizeof(uint16);
849 : }
850 182656 : if (xlrec->flags & XLH_UPDATE_SUFFIX_FROM_OLD)
851 : {
852 : Assert(newblk == oldblk);
853 65630 : memcpy(&suffixlen, recdata, sizeof(uint16));
854 65630 : recdata += sizeof(uint16);
855 : }
856 :
857 182656 : memcpy((char *) &xlhdr, recdata, SizeOfHeapHeader);
858 182656 : recdata += SizeOfHeapHeader;
859 :
860 182656 : tuplen = recdata_end - recdata;
861 : Assert(tuplen <= MaxHeapTupleSize);
862 :
863 182656 : htup = &tbuf.hdr;
864 182656 : MemSet((char *) htup, 0, SizeofHeapTupleHeader);
865 :
866 : /*
867 : * Reconstruct the new tuple using the prefix and/or suffix from the
868 : * old tuple, and the data stored in the WAL record.
869 : */
870 182656 : newp = (char *) htup + SizeofHeapTupleHeader;
871 182656 : if (prefixlen > 0)
872 : {
873 : int len;
874 :
875 : /* copy bitmap [+ padding] [+ oid] from WAL record */
876 28604 : len = xlhdr.t_hoff - SizeofHeapTupleHeader;
877 28604 : memcpy(newp, recdata, len);
878 28604 : recdata += len;
879 28604 : newp += len;
880 :
881 : /* copy prefix from old tuple */
882 28604 : memcpy(newp, (char *) oldtup.t_data + oldtup.t_data->t_hoff, prefixlen);
883 28604 : newp += prefixlen;
884 :
885 : /* copy new tuple data from WAL record */
886 28604 : len = tuplen - (xlhdr.t_hoff - SizeofHeapTupleHeader);
887 28604 : memcpy(newp, recdata, len);
888 28604 : recdata += len;
889 28604 : newp += len;
890 : }
891 : else
892 : {
893 : /*
894 : * copy bitmap [+ padding] [+ oid] + data from record, all in one
895 : * go
896 : */
897 154052 : memcpy(newp, recdata, tuplen);
898 154052 : recdata += tuplen;
899 154052 : newp += tuplen;
900 : }
901 : Assert(recdata == recdata_end);
902 :
903 : /* copy suffix from old tuple */
904 182656 : if (suffixlen > 0)
905 65630 : memcpy(newp, (char *) oldtup.t_data + oldtup.t_len - suffixlen, suffixlen);
906 :
907 182656 : newlen = SizeofHeapTupleHeader + tuplen + prefixlen + suffixlen;
908 182656 : htup->t_infomask2 = xlhdr.t_infomask2;
909 182656 : htup->t_infomask = xlhdr.t_infomask;
910 182656 : htup->t_hoff = xlhdr.t_hoff;
911 :
912 182656 : HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
913 182656 : HeapTupleHeaderSetCmin(htup, FirstCommandId);
914 182656 : HeapTupleHeaderSetXmax(htup, xlrec->new_xmax);
915 : /* Make sure there is no forward chain link in t_ctid */
916 182656 : htup->t_ctid = newtid;
917 :
918 182656 : offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
919 182656 : if (offnum == InvalidOffsetNumber)
920 0 : elog(PANIC, "failed to add tuple");
921 :
922 182656 : if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
923 56 : PageClearAllVisible(page);
924 :
925 182656 : freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
926 :
927 182656 : PageSetLSN(page, lsn);
928 182656 : MarkBufferDirty(nbuffer);
929 : }
930 :
931 183374 : if (BufferIsValid(nbuffer) && nbuffer != obuffer)
932 107120 : UnlockReleaseBuffer(nbuffer);
933 183374 : if (BufferIsValid(obuffer))
934 183374 : UnlockReleaseBuffer(obuffer);
935 :
936 : /*
937 : * If the new page is running low on free space, update the FSM as well.
938 : * Arbitrarily, our definition of "low" is less than 20%. We can't do much
939 : * better than that without knowing the fill-factor for the table.
940 : *
941 : * However, don't update the FSM on HOT updates, because after crash
942 : * recovery, either the old or the new tuple will certainly be dead and
943 : * prunable. After pruning, the page will have roughly as much free space
944 : * as it did before the update, assuming the new tuple is about the same
945 : * size as the old one.
946 : *
947 : * XXX: Don't do this if the page was restored from full page image. We
948 : * don't bother to update the FSM in that case, it doesn't need to be
949 : * totally accurate anyway.
950 : */
951 183374 : if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < BLCKSZ / 5)
952 22744 : XLogRecordPageWithFreeSpace(rlocator, newblk, freespace);
953 183374 : }
954 :
955 : /*
956 : * Replay XLOG_HEAP_CONFIRM records.
957 : */
958 : static void
959 154 : heap_xlog_confirm(XLogReaderState *record)
960 : {
961 154 : XLogRecPtr lsn = record->EndRecPtr;
962 154 : xl_heap_confirm *xlrec = (xl_heap_confirm *) XLogRecGetData(record);
963 : Buffer buffer;
964 : Page page;
965 : OffsetNumber offnum;
966 154 : ItemId lp = NULL;
967 : HeapTupleHeader htup;
968 :
969 154 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
970 : {
971 154 : page = BufferGetPage(buffer);
972 :
973 154 : offnum = xlrec->offnum;
974 154 : if (PageGetMaxOffsetNumber(page) >= offnum)
975 154 : lp = PageGetItemId(page, offnum);
976 :
977 154 : if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
978 0 : elog(PANIC, "invalid lp");
979 :
980 154 : htup = (HeapTupleHeader) PageGetItem(page, lp);
981 :
982 : /*
983 : * Confirm tuple as actually inserted
984 : */
985 154 : ItemPointerSet(&htup->t_ctid, BufferGetBlockNumber(buffer), offnum);
986 :
987 154 : PageSetLSN(page, lsn);
988 154 : MarkBufferDirty(buffer);
989 : }
990 154 : if (BufferIsValid(buffer))
991 154 : UnlockReleaseBuffer(buffer);
992 154 : }
993 :
994 : /*
995 : * Replay XLOG_HEAP_LOCK records.
996 : */
997 : static void
998 108920 : heap_xlog_lock(XLogReaderState *record)
999 : {
1000 108920 : XLogRecPtr lsn = record->EndRecPtr;
1001 108920 : xl_heap_lock *xlrec = (xl_heap_lock *) XLogRecGetData(record);
1002 : Buffer buffer;
1003 : Page page;
1004 : OffsetNumber offnum;
1005 108920 : ItemId lp = NULL;
1006 : HeapTupleHeader htup;
1007 :
1008 : /*
1009 : * The visibility map may need to be fixed even if the heap page is
1010 : * already up-to-date.
1011 : */
1012 108920 : if (xlrec->flags & XLH_LOCK_ALL_FROZEN_CLEARED)
1013 : {
1014 : RelFileLocator rlocator;
1015 54 : Buffer vmbuffer = InvalidBuffer;
1016 : BlockNumber block;
1017 : Relation reln;
1018 :
1019 54 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, &block);
1020 54 : reln = CreateFakeRelcacheEntry(rlocator);
1021 :
1022 54 : visibilitymap_pin(reln, block, &vmbuffer);
1023 54 : visibilitymap_clear(reln, block, vmbuffer, VISIBILITYMAP_ALL_FROZEN);
1024 :
1025 54 : ReleaseBuffer(vmbuffer);
1026 54 : FreeFakeRelcacheEntry(reln);
1027 : }
1028 :
1029 108920 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
1030 : {
1031 108608 : page = (Page) BufferGetPage(buffer);
1032 :
1033 108608 : offnum = xlrec->offnum;
1034 108608 : if (PageGetMaxOffsetNumber(page) >= offnum)
1035 108608 : lp = PageGetItemId(page, offnum);
1036 :
1037 108608 : if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
1038 0 : elog(PANIC, "invalid lp");
1039 :
1040 108608 : htup = (HeapTupleHeader) PageGetItem(page, lp);
1041 :
1042 108608 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
1043 108608 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
1044 108608 : fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
1045 : &htup->t_infomask2);
1046 :
1047 : /*
1048 : * Clear relevant update flags, but only if the modified infomask says
1049 : * there's no update.
1050 : */
1051 108608 : if (HEAP_XMAX_IS_LOCKED_ONLY(htup->t_infomask))
1052 : {
1053 108608 : HeapTupleHeaderClearHotUpdated(htup);
1054 : /* Make sure there is no forward chain link in t_ctid */
1055 108608 : ItemPointerSet(&htup->t_ctid,
1056 : BufferGetBlockNumber(buffer),
1057 : offnum);
1058 : }
1059 108608 : HeapTupleHeaderSetXmax(htup, xlrec->xmax);
1060 108608 : HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
1061 108608 : PageSetLSN(page, lsn);
1062 108608 : MarkBufferDirty(buffer);
1063 : }
1064 108920 : if (BufferIsValid(buffer))
1065 108920 : UnlockReleaseBuffer(buffer);
1066 108920 : }
1067 :
1068 : /*
1069 : * Replay XLOG_HEAP2_LOCK_UPDATED records.
1070 : */
1071 : static void
1072 0 : heap_xlog_lock_updated(XLogReaderState *record)
1073 : {
1074 0 : XLogRecPtr lsn = record->EndRecPtr;
1075 : xl_heap_lock_updated *xlrec;
1076 : Buffer buffer;
1077 : Page page;
1078 : OffsetNumber offnum;
1079 0 : ItemId lp = NULL;
1080 : HeapTupleHeader htup;
1081 :
1082 0 : xlrec = (xl_heap_lock_updated *) XLogRecGetData(record);
1083 :
1084 : /*
1085 : * The visibility map may need to be fixed even if the heap page is
1086 : * already up-to-date.
1087 : */
1088 0 : if (xlrec->flags & XLH_LOCK_ALL_FROZEN_CLEARED)
1089 : {
1090 : RelFileLocator rlocator;
1091 0 : Buffer vmbuffer = InvalidBuffer;
1092 : BlockNumber block;
1093 : Relation reln;
1094 :
1095 0 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, &block);
1096 0 : reln = CreateFakeRelcacheEntry(rlocator);
1097 :
1098 0 : visibilitymap_pin(reln, block, &vmbuffer);
1099 0 : visibilitymap_clear(reln, block, vmbuffer, VISIBILITYMAP_ALL_FROZEN);
1100 :
1101 0 : ReleaseBuffer(vmbuffer);
1102 0 : FreeFakeRelcacheEntry(reln);
1103 : }
1104 :
1105 0 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
1106 : {
1107 0 : page = BufferGetPage(buffer);
1108 :
1109 0 : offnum = xlrec->offnum;
1110 0 : if (PageGetMaxOffsetNumber(page) >= offnum)
1111 0 : lp = PageGetItemId(page, offnum);
1112 :
1113 0 : if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
1114 0 : elog(PANIC, "invalid lp");
1115 :
1116 0 : htup = (HeapTupleHeader) PageGetItem(page, lp);
1117 :
1118 0 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
1119 0 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
1120 0 : fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
1121 : &htup->t_infomask2);
1122 0 : HeapTupleHeaderSetXmax(htup, xlrec->xmax);
1123 :
1124 0 : PageSetLSN(page, lsn);
1125 0 : MarkBufferDirty(buffer);
1126 : }
1127 0 : if (BufferIsValid(buffer))
1128 0 : UnlockReleaseBuffer(buffer);
1129 0 : }
1130 :
1131 : /*
1132 : * Replay XLOG_HEAP_INPLACE records.
1133 : */
1134 : static void
1135 13956 : heap_xlog_inplace(XLogReaderState *record)
1136 : {
1137 13956 : XLogRecPtr lsn = record->EndRecPtr;
1138 13956 : xl_heap_inplace *xlrec = (xl_heap_inplace *) XLogRecGetData(record);
1139 : Buffer buffer;
1140 : Page page;
1141 : OffsetNumber offnum;
1142 13956 : ItemId lp = NULL;
1143 : HeapTupleHeader htup;
1144 : uint32 oldlen;
1145 : Size newlen;
1146 :
1147 13956 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
1148 : {
1149 13668 : char *newtup = XLogRecGetBlockData(record, 0, &newlen);
1150 :
1151 13668 : page = BufferGetPage(buffer);
1152 :
1153 13668 : offnum = xlrec->offnum;
1154 13668 : if (PageGetMaxOffsetNumber(page) >= offnum)
1155 13668 : lp = PageGetItemId(page, offnum);
1156 :
1157 13668 : if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
1158 0 : elog(PANIC, "invalid lp");
1159 :
1160 13668 : htup = (HeapTupleHeader) PageGetItem(page, lp);
1161 :
1162 13668 : oldlen = ItemIdGetLength(lp) - htup->t_hoff;
1163 13668 : if (oldlen != newlen)
1164 0 : elog(PANIC, "wrong tuple length");
1165 :
1166 13668 : memcpy((char *) htup + htup->t_hoff, newtup, newlen);
1167 :
1168 13668 : PageSetLSN(page, lsn);
1169 13668 : MarkBufferDirty(buffer);
1170 : }
1171 13956 : if (BufferIsValid(buffer))
1172 13956 : UnlockReleaseBuffer(buffer);
1173 :
1174 13956 : ProcessCommittedInvalidationMessages(xlrec->msgs,
1175 : xlrec->nmsgs,
1176 13956 : xlrec->relcacheInitFileInval,
1177 : xlrec->dbId,
1178 : xlrec->tsId);
1179 13956 : }
1180 :
1181 : void
1182 3385060 : heap_redo(XLogReaderState *record)
1183 : {
1184 3385060 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1185 :
1186 : /*
1187 : * These operations don't overwrite MVCC data so no conflict processing is
1188 : * required. The ones in heap2 rmgr do.
1189 : */
1190 :
1191 3385060 : switch (info & XLOG_HEAP_OPMASK)
1192 : {
1193 2495992 : case XLOG_HEAP_INSERT:
1194 2495992 : heap_xlog_insert(record);
1195 2495992 : break;
1196 582660 : case XLOG_HEAP_DELETE:
1197 582660 : heap_xlog_delete(record);
1198 582660 : break;
1199 112792 : case XLOG_HEAP_UPDATE:
1200 112792 : heap_xlog_update(record, false);
1201 112792 : break;
1202 4 : case XLOG_HEAP_TRUNCATE:
1203 :
1204 : /*
1205 : * TRUNCATE is a no-op because the actions are already logged as
1206 : * SMGR WAL records. TRUNCATE WAL record only exists for logical
1207 : * decoding.
1208 : */
1209 4 : break;
1210 70582 : case XLOG_HEAP_HOT_UPDATE:
1211 70582 : heap_xlog_update(record, true);
1212 70582 : break;
1213 154 : case XLOG_HEAP_CONFIRM:
1214 154 : heap_xlog_confirm(record);
1215 154 : break;
1216 108920 : case XLOG_HEAP_LOCK:
1217 108920 : heap_xlog_lock(record);
1218 108920 : break;
1219 13956 : case XLOG_HEAP_INPLACE:
1220 13956 : heap_xlog_inplace(record);
1221 13956 : break;
1222 0 : default:
1223 0 : elog(PANIC, "heap_redo: unknown op code %u", info);
1224 : }
1225 3385060 : }
1226 :
1227 : void
1228 133916 : heap2_redo(XLogReaderState *record)
1229 : {
1230 133916 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1231 :
1232 133916 : switch (info & XLOG_HEAP_OPMASK)
1233 : {
1234 17814 : case XLOG_HEAP2_PRUNE_ON_ACCESS:
1235 : case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
1236 : case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
1237 17814 : heap_xlog_prune_freeze(record);
1238 17814 : break;
1239 7622 : case XLOG_HEAP2_VISIBLE:
1240 7622 : heap_xlog_visible(record);
1241 7622 : break;
1242 106728 : case XLOG_HEAP2_MULTI_INSERT:
1243 106728 : heap_xlog_multi_insert(record);
1244 106728 : break;
1245 0 : case XLOG_HEAP2_LOCK_UPDATED:
1246 0 : heap_xlog_lock_updated(record);
1247 0 : break;
1248 1752 : case XLOG_HEAP2_NEW_CID:
1249 :
1250 : /*
1251 : * Nothing to do on a real replay, only used during logical
1252 : * decoding.
1253 : */
1254 1752 : break;
1255 0 : case XLOG_HEAP2_REWRITE:
1256 0 : heap_xlog_logical_rewrite(record);
1257 0 : break;
1258 0 : default:
1259 0 : elog(PANIC, "heap2_redo: unknown op code %u", info);
1260 : }
1261 133916 : }
1262 :
1263 : /*
1264 : * Mask a heap page before performing consistency checks on it.
1265 : */
1266 : void
1267 5593860 : heap_mask(char *pagedata, BlockNumber blkno)
1268 : {
1269 5593860 : Page page = (Page) pagedata;
1270 : OffsetNumber off;
1271 :
1272 5593860 : mask_page_lsn_and_checksum(page);
1273 :
1274 5593860 : mask_page_hint_bits(page);
1275 5593860 : mask_unused_space(page);
1276 :
1277 458054072 : for (off = 1; off <= PageGetMaxOffsetNumber(page); off++)
1278 : {
1279 452460212 : ItemId iid = PageGetItemId(page, off);
1280 : char *page_item;
1281 :
1282 452460212 : page_item = (char *) (page + ItemIdGetOffset(iid));
1283 :
1284 452460212 : if (ItemIdIsNormal(iid))
1285 : {
1286 428691908 : HeapTupleHeader page_htup = (HeapTupleHeader) page_item;
1287 :
1288 : /*
1289 : * If xmin of a tuple is not yet frozen, we should ignore
1290 : * differences in hint bits, since they can be set without
1291 : * emitting WAL.
1292 : */
1293 428691908 : if (!HeapTupleHeaderXminFrozen(page_htup))
1294 417706776 : page_htup->t_infomask &= ~HEAP_XACT_MASK;
1295 : else
1296 : {
1297 : /* Still we need to mask xmax hint bits. */
1298 10985132 : page_htup->t_infomask &= ~HEAP_XMAX_INVALID;
1299 10985132 : page_htup->t_infomask &= ~HEAP_XMAX_COMMITTED;
1300 : }
1301 :
1302 : /*
1303 : * During replay, we set Command Id to FirstCommandId. Hence, mask
1304 : * it. See heap_xlog_insert() for details.
1305 : */
1306 428691908 : page_htup->t_choice.t_heap.t_field3.t_cid = MASK_MARKER;
1307 :
1308 : /*
1309 : * For a speculative tuple, heap_insert() does not set ctid in the
1310 : * caller-passed heap tuple itself, leaving the ctid field to
1311 : * contain a speculative token value - a per-backend monotonically
1312 : * increasing identifier. Besides, it does not WAL-log ctid under
1313 : * any circumstances.
1314 : *
1315 : * During redo, heap_xlog_insert() sets t_ctid to current block
1316 : * number and self offset number. It doesn't care about any
1317 : * speculative insertions on the primary. Hence, we set t_ctid to
1318 : * current block number and self offset number to ignore any
1319 : * inconsistency.
1320 : */
1321 428691908 : if (HeapTupleHeaderIsSpeculative(page_htup))
1322 156 : ItemPointerSet(&page_htup->t_ctid, blkno, off);
1323 :
1324 : /*
1325 : * NB: Not ignoring ctid changes due to the tuple having moved
1326 : * (i.e. HeapTupleHeaderIndicatesMovedPartitions), because that's
1327 : * important information that needs to be in-sync between primary
1328 : * and standby, and thus is WAL logged.
1329 : */
1330 : }
1331 :
1332 : /*
1333 : * Ignore any padding bytes after the tuple, when the length of the
1334 : * item is not MAXALIGNed.
1335 : */
1336 452460212 : if (ItemIdHasStorage(iid))
1337 : {
1338 428691908 : int len = ItemIdGetLength(iid);
1339 428691908 : int padlen = MAXALIGN(len) - len;
1340 :
1341 428691908 : if (padlen > 0)
1342 216400756 : memset(page_item + len, MASK_MARKER, padlen);
1343 : }
1344 : }
1345 5593860 : }
|