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