PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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-2026, 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 * Clear visibility map bits for a single heap block during heap redo.
27 *
28 * Used by records that modify one heap block and, at most, its corresponding
29 * VM block (insert, delete, multi_insert, lock). Records that can touch
30 * multiple heap or VM blocks (e.g. updates) replay the VM changes inline
31 * instead.
32 *
33 * 'record' is the WAL record being replayed
34 * 'target_locator' identifies the relation whose VM is being updated
35 * 'heap_blkno' is the heap block whose VM bits should be cleared
36 * 'wal_vm_block_id' is the WAL block reference id of the VM page
37 * 'flags' specifies which visibility map bits to clear
38 */
39static void
41 RelFileLocator target_locator,
44{
45 XLogRecPtr lsn = record->EndRecPtr;
46 Buffer vmbuffer = InvalidBuffer;
47
49 return;
50
51 /*
52 * If the vmbuffer was registered, use the recovery-specific routines to
53 * read it. These will either apply an FPI or indicate that we should
54 * clear the requested bits ourselves.
55 */
57 &vmbuffer) == BLK_NEEDS_REDO)
58 {
59 if (visibilitymap_clear(target_locator, heap_blkno, vmbuffer, flags))
60 PageSetLSN(BufferGetPage(vmbuffer), lsn);
61 }
62 if (BufferIsValid(vmbuffer))
63 UnlockReleaseBuffer(vmbuffer);
64}
65
66/*
67 * Replay XLOG_HEAP2_PRUNE_* records.
68 */
69static void
71{
72 XLogRecPtr lsn = record->EndRecPtr;
73 char *maindataptr = XLogRecGetData(record);
75 Buffer buffer;
76 RelFileLocator rlocator;
77 BlockNumber blkno;
78 Buffer vmbuffer = InvalidBuffer;
79 uint8 vmflags = 0;
80 Size freespace = 0;
81 bool do_update_fsm = false;
82
83 XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
86
87 /*
88 * We will take an ordinary exclusive lock or a cleanup lock depending on
89 * whether the XLHP_CLEANUP_LOCK flag is set. With an ordinary exclusive
90 * lock, we better not be doing anything that requires moving existing
91 * tuple data.
92 */
93 Assert((xlrec.flags & XLHP_CLEANUP_LOCK) != 0 ||
95
96 if (xlrec.flags & XLHP_VM_ALL_VISIBLE)
97 {
99 if (xlrec.flags & XLHP_VM_ALL_FROZEN)
101 }
102
103 /*
104 * After xl_heap_prune is the optional snapshot conflict horizon.
105 *
106 * In Hot Standby mode, we must ensure that there are no running queries
107 * which would conflict with the changes in this record. That means we
108 * can't replay this record if it removes tuples that are still visible to
109 * transactions on the standby, freeze tuples with xids that are still
110 * considered running on the standby, or set a page as all-visible in the
111 * VM if it isn't all-visible to all transactions on the standby.
112 */
113 if ((xlrec.flags & XLHP_HAS_CONFLICT_HORIZON) != 0)
114 {
116
117 /* memcpy() because snapshot_conflict_horizon is stored unaligned */
119 maindataptr += sizeof(TransactionId);
120
121 if (InHotStandby)
123 (xlrec.flags & XLHP_IS_CATALOG_REL) != 0,
124 rlocator);
125 }
126
127 /*
128 * If we have a full-page image of the heap block, restore it and we're
129 * done with the heap block.
130 */
132 (xlrec.flags & XLHP_CLEANUP_LOCK) != 0,
133 &buffer) == BLK_NEEDS_REDO)
134 {
135 Page page = BufferGetPage(buffer);
136 OffsetNumber *redirected;
137 OffsetNumber *nowdead;
138 OffsetNumber *nowunused;
139 int nredirected;
140 int ndead;
141 int nunused;
142 int nplans;
143 Size datalen;
144 xlhp_freeze_plan *plans;
146 char *dataptr = XLogRecGetBlockData(record, 0, &datalen);
147 bool do_prune;
148
150 &nplans, &plans, &frz_offsets,
151 &nredirected, &redirected,
152 &ndead, &nowdead,
153 &nunused, &nowunused);
154
155 do_prune = nredirected > 0 || ndead > 0 || nunused > 0;
156
157 /* Ensure the record does something */
159
160 /*
161 * Update all line pointers per the record, and repair fragmentation
162 * if needed.
163 */
164 if (do_prune)
166 (xlrec.flags & XLHP_CLEANUP_LOCK) == 0,
167 redirected, nredirected,
168 nowdead, ndead,
169 nowunused, nunused);
170
171 /* Freeze tuples */
172 for (int p = 0; p < nplans; p++)
173 {
175
176 /*
177 * Convert freeze plan representation from WAL record into
178 * per-tuple format used by heap_execute_freeze_tuple
179 */
180 frz.xmax = plans[p].xmax;
181 frz.t_infomask2 = plans[p].t_infomask2;
182 frz.t_infomask = plans[p].t_infomask;
183 frz.frzflags = plans[p].frzflags;
184 frz.offset = InvalidOffsetNumber; /* unused, but be tidy */
185
186 for (int i = 0; i < plans[p].ntuples; i++)
187 {
188 OffsetNumber offset = *(frz_offsets++);
189 ItemId lp;
190 HeapTupleHeader tuple;
191
192 lp = PageGetItemId(page, offset);
193 tuple = (HeapTupleHeader) PageGetItem(page, lp);
195 }
196 }
197
198 /* There should be no more data */
199 Assert((char *) frz_offsets == dataptr + datalen);
200
201 /*
202 * The critical integrity requirement here is that we must never end
203 * up with the visibility map bit set and the page-level
204 * PD_ALL_VISIBLE bit unset. If that were to occur, a subsequent page
205 * modification would fail to clear the visibility map bit.
206 */
208 {
209 PageSetAllVisible(page);
210 PageClearPrunable(page);
211 }
212
213 MarkBufferDirty(buffer);
214
215 /*
216 * See log_heap_prune_and_freeze() for commentary on when we set the
217 * heap page LSN.
218 */
219 if (do_prune || nplans > 0 ||
221 PageSetLSN(page, lsn);
222
223 /*
224 * Note: we don't worry about updating the page's prunability hints.
225 * At worst this will cause an extra prune cycle to occur soon.
226 */
227 }
228
229 /*
230 * If we 1) released any space or line pointers or 2) set PD_ALL_VISIBLE
231 * or the VM, update the freespace map.
232 *
233 * Even when no actual space is freed (when only marking the page
234 * all-visible or frozen), we still update the FSM. Because the FSM is
235 * unlogged and maintained heuristically, it often becomes stale on
236 * standbys. If such a standby is later promoted and runs VACUUM, it will
237 * skip recalculating free space for pages that were marked
238 * all-visible/all-frozen. FreeSpaceMapVacuum() can then propagate overly
239 * optimistic free space values upward, causing future insertions to
240 * select pages that turn out to be unusable. In bulk, this can lead to
241 * long stalls.
242 *
243 * To prevent this, always update the FSM even when only marking a page
244 * all-visible/all-frozen.
245 *
246 * Do this regardless of whether a full-page image is logged, since FSM
247 * data is not part of the page itself.
248 */
249 if (BufferIsValid(buffer))
250 {
251 if ((xlrec.flags & (XLHP_HAS_REDIRECTIONS |
255 {
256 freespace = PageGetHeapFreeSpace(BufferGetPage(buffer));
257 do_update_fsm = true;
258 }
259
260 /*
261 * We want to avoid holding an exclusive lock on the heap buffer while
262 * doing IO (either of the FSM or the VM), so we'll release it now.
263 */
264 UnlockReleaseBuffer(buffer);
265 }
266
267 /*
268 * Now read and update the VM block.
269 *
270 * We must redo changes to the VM even if the heap page was skipped due to
271 * LSN interlock. See comment in heap_xlog_multi_insert() for more details
272 * on replaying changes to the VM.
273 */
277 false,
278 &vmbuffer) == BLK_NEEDS_REDO)
279 {
280 Page vmpage = BufferGetPage(vmbuffer);
281
282 /* initialize the page if it was read as zeros */
283 if (PageIsNew(vmpage))
285
286 visibilitymap_set(blkno, vmbuffer, vmflags, rlocator);
287
288 Assert(BufferIsDirty(vmbuffer));
289 PageSetLSN(vmpage, lsn);
290 }
291
292 if (BufferIsValid(vmbuffer))
293 UnlockReleaseBuffer(vmbuffer);
294
295 if (do_update_fsm)
296 XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
297}
298
299/*
300 * Given an "infobits" field from an XLog record, set the correct bits in the
301 * given infomask and infomask2 for the tuple touched by the record.
302 *
303 * (This is the reverse of compute_infobits).
304 */
305static void
325
326/*
327 * Replay XLOG_HEAP_DELETE records.
328 */
329static void
331{
332 XLogRecPtr lsn = record->EndRecPtr;
334 Buffer buffer;
335 Page page;
336 ItemId lp;
337 HeapTupleHeader htup;
338 BlockNumber blkno;
339 RelFileLocator target_locator;
340 ItemPointerData target_tid;
341
342 XLogRecGetBlockTag(record, HEAP_DELETE_BLKREF_HEAP, &target_locator, NULL,
343 &blkno);
344 ItemPointerSetBlockNumber(&target_tid, blkno);
345 ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
346
347 /*
348 * The visibility map may need to be fixed even if the heap page is
349 * already up-to-date.
350 */
352 heap_xlog_vm_clear(record, target_locator,
355
357 &buffer) == BLK_NEEDS_REDO)
358 {
359 page = BufferGetPage(buffer);
360
361 if (xlrec->offnum < 1 || xlrec->offnum > PageGetMaxOffsetNumber(page))
362 elog(PANIC, "offnum out of range");
363 lp = PageGetItemId(page, xlrec->offnum);
364 if (!ItemIdIsNormal(lp))
365 elog(PANIC, "invalid lp");
366
367 htup = (HeapTupleHeader) PageGetItem(page, lp);
368
372 fix_infomask_from_infobits(xlrec->infobits_set,
373 &htup->t_infomask, &htup->t_infomask2);
374 if (!(xlrec->flags & XLH_DELETE_IS_SUPER))
375 HeapTupleHeaderSetXmax(htup, xlrec->xmax);
376 else
379
380 /* Mark the page as a candidate for pruning */
381 PageSetPrunable(page, XLogRecGetXid(record));
382
385
386 /* Make sure t_ctid is set correctly */
389 else
390 htup->t_ctid = target_tid;
391 PageSetLSN(page, lsn);
392 MarkBufferDirty(buffer);
393 }
394 if (BufferIsValid(buffer))
395 UnlockReleaseBuffer(buffer);
396}
397
398/*
399 * Replay XLOG_HEAP_INSERT records.
400 */
401static void
403{
404 XLogRecPtr lsn = record->EndRecPtr;
406 Buffer buffer;
407 Page page;
408 union
409 {
412 } tbuf;
413 HeapTupleHeader htup;
416 Size freespace = 0;
417 RelFileLocator target_locator;
418 BlockNumber blkno;
419 ItemPointerData target_tid;
420 XLogRedoAction action;
421
422 XLogRecGetBlockTag(record, HEAP_INSERT_BLKREF_HEAP, &target_locator, NULL,
423 &blkno);
424 ItemPointerSetBlockNumber(&target_tid, blkno);
425 ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
426
427 /* No freezing in the heap_insert() code path */
429
430 /*
431 * The visibility map may need to be fixed even if the heap page is
432 * already up-to-date.
433 */
435 heap_xlog_vm_clear(record, target_locator,
438
439 /*
440 * If we inserted the first and only tuple on the page, re-initialize the
441 * page from scratch.
442 */
444 {
446 page = BufferGetPage(buffer);
447 PageInit(page, BufferGetPageSize(buffer), 0);
448 action = BLK_NEEDS_REDO;
449 }
450 else
452 &buffer);
453 if (action == BLK_NEEDS_REDO)
454 {
455 Size datalen;
456 char *data;
457
458 page = BufferGetPage(buffer);
459
460 if (PageGetMaxOffsetNumber(page) + 1 < xlrec->offnum)
461 elog(PANIC, "invalid max offset number");
462
464
465 newlen = datalen - SizeOfHeapHeader;
469
470 htup = &tbuf.hdr;
472 /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
473 memcpy((char *) htup + SizeofHeapTupleHeader,
474 data,
475 newlen);
477 htup->t_infomask2 = xlhdr.t_infomask2;
478 htup->t_infomask = xlhdr.t_infomask;
479 htup->t_hoff = xlhdr.t_hoff;
482 htup->t_ctid = target_tid;
483
484 if (PageAddItem(page, htup, newlen, xlrec->offnum, true, true) == InvalidOffsetNumber)
485 elog(PANIC, "failed to add tuple");
486
487 freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
488
489 /*
490 * Set the page prunable to trigger on-access pruning later, which may
491 * set the page all-visible in the VM. See comments in heap_insert().
492 */
495 PageSetPrunable(page, XLogRecGetXid(record));
496
497 PageSetLSN(page, lsn);
498
501
502 MarkBufferDirty(buffer);
503 }
504 if (BufferIsValid(buffer))
505 UnlockReleaseBuffer(buffer);
506
507 /*
508 * If the page is running low on free space, update the FSM as well.
509 * Arbitrarily, our definition of "low" is less than 20%. We can't do much
510 * better than that without knowing the fill-factor for the table.
511 *
512 * XXX: Don't do this if the page was restored from full page image. We
513 * don't bother to update the FSM in that case, it doesn't need to be
514 * totally accurate anyway.
515 */
516 if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
517 XLogRecordPageWithFreeSpace(target_locator, blkno, freespace);
518}
519
520/*
521 * Replay XLOG_HEAP2_MULTI_INSERT records.
522 */
523static void
525{
526 XLogRecPtr lsn = record->EndRecPtr;
528 RelFileLocator rlocator;
529 BlockNumber blkno;
530 Buffer buffer;
531 Page page;
532 union
533 {
536 } tbuf;
537 HeapTupleHeader htup;
539 Size freespace = 0;
540 int i;
541 bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
542 XLogRedoAction action;
543 Buffer vmbuffer = InvalidBuffer;
544
545 /*
546 * Insertion doesn't overwrite MVCC data, so no conflict processing is
547 * required.
548 */
550
552 &blkno);
553
554 /* check that the mutually exclusive flags are not both set */
556 (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)));
557
558 /*
559 * The visibility map may need to be fixed even if the heap page is
560 * already up-to-date.
561 *
562 * Clear the VM (if needed) before clearing the heap page-level visibility
563 * flag (PD_ALL_VISIBLE) to prevent the heap page from being marked
564 * all-visible in the VM while its PD_ALL_VISIBLE is clear.
565 */
567 heap_xlog_vm_clear(record, rlocator,
570
571 if (isinit)
572 {
574 page = BufferGetPage(buffer);
575 PageInit(page, BufferGetPageSize(buffer), 0);
576 action = BLK_NEEDS_REDO;
577 }
578 else
580 &buffer);
581 if (action == BLK_NEEDS_REDO)
582 {
583 char *tupdata;
584 char *endptr;
585 Size len;
586
587 /* Tuples are stored as block data */
589 &len);
590 endptr = tupdata + len;
591
592 page = BufferGetPage(buffer);
593
594 for (i = 0; i < xlrec->ntuples; i++)
595 {
596 OffsetNumber offnum;
598
599 /*
600 * If we're reinitializing the page, the tuples are stored in
601 * order from FirstOffsetNumber. Otherwise there's an array of
602 * offsets in the WAL record, and the tuples come after that.
603 */
604 if (isinit)
605 offnum = FirstOffsetNumber + i;
606 else
607 offnum = xlrec->offsets[i];
608 if (PageGetMaxOffsetNumber(page) + 1 < offnum)
609 elog(PANIC, "invalid max offset number");
610
612 tupdata = ((char *) xlhdr) + SizeOfMultiInsertTuple;
613
614 newlen = xlhdr->datalen;
616 htup = &tbuf.hdr;
618 /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
619 memcpy((char *) htup + SizeofHeapTupleHeader,
620 tupdata,
621 newlen);
622 tupdata += newlen;
623
625 htup->t_infomask2 = xlhdr->t_infomask2;
626 htup->t_infomask = xlhdr->t_infomask;
627 htup->t_hoff = xlhdr->t_hoff;
630 ItemPointerSetBlockNumber(&htup->t_ctid, blkno);
631 ItemPointerSetOffsetNumber(&htup->t_ctid, offnum);
632
633 offnum = PageAddItem(page, htup, newlen, offnum, true, true);
634 if (offnum == InvalidOffsetNumber)
635 elog(PANIC, "failed to add tuple");
636 }
637 if (tupdata != endptr)
638 elog(PANIC, "total tuple length mismatch");
639
640 freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
641
642 PageSetLSN(page, lsn);
643
646
647 /*
648 * XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible. If
649 * we are not setting the page frozen, then set the page's prunable
650 * hint so that we trigger on-access pruning later which may set the
651 * page all-visible in the VM.
652 */
653 if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
654 {
655 PageSetAllVisible(page);
656 PageClearPrunable(page);
657 }
658 else
659 PageSetPrunable(page, XLogRecGetXid(record));
660
661 MarkBufferDirty(buffer);
662 }
663 if (BufferIsValid(buffer))
664 UnlockReleaseBuffer(buffer);
665
666 buffer = InvalidBuffer;
667
668 /*
669 * Read and update the visibility map (VM) block to set it frozen.
670 *
671 * We must always redo VM changes, even if the corresponding heap page
672 * update was skipped due to the LSN interlock. Each VM block covers
673 * multiple heap pages, so later WAL records may update other bits in the
674 * same block. If this record includes an FPI (full-page image),
675 * subsequent WAL records may depend on it to guard against torn pages.
676 *
677 * Heap page changes are replayed first to preserve the invariant:
678 * PD_ALL_VISIBLE must be set on the heap page if the VM bit is set.
679 *
680 * Note that we released the heap page lock above. During normal
681 * operation, this would be unsafe — a concurrent modification could
682 * clear PD_ALL_VISIBLE while the VM bit remained set, violating the
683 * invariant.
684 *
685 * During recovery, however, no concurrent writers exist. Therefore,
686 * updating the VM without holding the heap page lock is safe enough. This
687 * same approach is taken when replaying XLOG_HEAP2_PRUNE* records (see
688 * heap_xlog_prune_freeze()).
689 */
690 if ((xlrec->flags & XLH_INSERT_ALL_FROZEN_SET) &&
692 &vmbuffer) == BLK_NEEDS_REDO)
693 {
694 Page vmpage = BufferGetPage(vmbuffer);
695
696 /* initialize the page if it was read as zeros */
697 if (PageIsNew(vmpage))
699
700 visibilitymap_set(blkno,
701 vmbuffer,
704 rlocator);
705
706 Assert(BufferIsDirty(vmbuffer));
707 PageSetLSN(vmpage, lsn);
708 }
709
710 if (BufferIsValid(vmbuffer))
711 UnlockReleaseBuffer(vmbuffer);
712
713 /*
714 * If the page is running low on free space, update the FSM as well.
715 * Arbitrarily, our definition of "low" is less than 20%. We can't do much
716 * better than that without knowing the fill-factor for the table.
717 *
718 * XXX: Don't do this if the page was restored from full page image. We
719 * don't bother to update the FSM in that case, it doesn't need to be
720 * totally accurate anyway.
721 */
722 if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
723 XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
724}
725
726/*
727 * Replay XLOG_HEAP_UPDATE and XLOG_HEAP_HOT_UPDATE records.
728 */
729static void
731{
732 XLogRecPtr lsn = record->EndRecPtr;
734 RelFileLocator rlocator;
739 nbuffer;
740 Page opage,
741 npage;
742 bool has_vm_old,
744 OffsetNumber offnum;
745 ItemId lp;
747 HeapTupleHeader htup;
748 uint16 prefixlen = 0,
749 suffixlen = 0;
750 char *newp;
751 union
752 {
755 } tbuf;
758 Size freespace = 0;
761
762 /* initialize to keep the compiler quiet */
763 oldtup.t_data = NULL;
764 oldtup.t_len = 0;
765
767 &newblk);
769 &oldblk, NULL))
770 {
771 /* HOT updates are never done across pages */
773 }
774 else
775 oldblk = newblk;
776
777 ItemPointerSet(&newtid, newblk, xlrec->new_offnum);
778
779 /*
780 * The visibility map may need to be fixed even if the heap page is
781 * already up-to-date.
782 */
785
786 if (has_vm_new)
787 {
789
791
794 {
795 /*
796 * If both the old and new heap pages were all-visible and their
797 * VM bits are on the same VM page, that single VM page is
798 * registered as HEAP_UPDATE_BLKREF_VM_NEW. Clear both heap
799 * blocks' VM bits from the single provided VM buffer. It's
800 * possible that one of the page's VM bits were already clear, but
801 * visibilitymap_clear() is harmless as long as we provide it the
802 * correct bits.
803 *
804 * We must verify that oldblk's VM bits really are on this VM
805 * page, rather than relying on the absence of a separate VM_OLD
806 * block reference: VM_OLD is also omitted when oldblk is on a
807 * different VM page but its bit was already clear.
808 */
811 {
815 }
816 /* If VM_NEW is registered, we are sure newblk is on VM_NEW */
820 }
823 }
824 if (has_vm_old)
825 {
827
829
832 {
836 }
839 }
840
841 /*
842 * In normal operation, it is important to lock the two pages in
843 * page-number order, to avoid possible deadlocks against other update
844 * operations going the other way. However, during WAL replay there can
845 * be no other update happening, so we don't need to worry about that.
846 * Notice we also don't worry about this when locking VM buffers above.
847 *
848 * But we *do* need to worry that we don't expose an inconsistent state to
849 * Hot Standby queries --- so the original page can't be unlocked before
850 * we've added the new tuple to the new page.
851 */
852
853 /* Deal with old tuple version */
856 &obuffer);
858 {
860 offnum = xlrec->old_offnum;
862 elog(PANIC, "offnum out of range");
863 lp = PageGetItemId(opage, offnum);
864 if (!ItemIdIsNormal(lp))
865 elog(PANIC, "invalid lp");
866
868
869 oldtup.t_data = htup;
870 oldtup.t_len = ItemIdGetLength(lp);
871
874 if (hot_update)
876 else
878 fix_infomask_from_infobits(xlrec->old_infobits_set, &htup->t_infomask,
879 &htup->t_infomask2);
880 HeapTupleHeaderSetXmax(htup, xlrec->old_xmax);
882 /* Set forward chain link in t_ctid */
883 htup->t_ctid = newtid;
884
885 /* Mark the page as a candidate for pruning */
887
890
891 PageSetLSN(opage, lsn);
893 }
894
895 /*
896 * Read the page the new tuple goes into, if different from old.
897 */
898 if (oldblk == newblk)
899 {
902 }
903 else if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
904 {
906 npage = BufferGetPage(nbuffer);
909 }
910 else
912 &nbuffer);
913
914 /* Deal with new tuple */
916 {
917 char *recdata;
918 char *recdata_end;
919 Size datalen;
920 Size tuplen;
921
923 &datalen);
924 recdata_end = recdata + datalen;
925
926 npage = BufferGetPage(nbuffer);
927
928 offnum = xlrec->new_offnum;
929 if (PageGetMaxOffsetNumber(npage) + 1 < offnum)
930 elog(PANIC, "invalid max offset number");
931
933 {
934 Assert(newblk == oldblk);
935 memcpy(&prefixlen, recdata, sizeof(uint16));
936 recdata += sizeof(uint16);
937 }
939 {
940 Assert(newblk == oldblk);
941 memcpy(&suffixlen, recdata, sizeof(uint16));
942 recdata += sizeof(uint16);
943 }
944
947
948 tuplen = recdata_end - recdata;
949 Assert(tuplen <= MaxHeapTupleSize);
950
951 htup = &tbuf.hdr;
953
954 /*
955 * Reconstruct the new tuple using the prefix and/or suffix from the
956 * old tuple, and the data stored in the WAL record.
957 */
958 newp = (char *) htup + SizeofHeapTupleHeader;
959 if (prefixlen > 0)
960 {
961 int len;
962
963 /* copy bitmap [+ padding] [+ oid] from WAL record */
966 recdata += len;
967 newp += len;
968
969 /* copy prefix from old tuple */
970 memcpy(newp, (char *) oldtup.t_data + oldtup.t_data->t_hoff, prefixlen);
971 newp += prefixlen;
972
973 /* copy new tuple data from WAL record */
974 len = tuplen - (xlhdr.t_hoff - SizeofHeapTupleHeader);
976 recdata += len;
977 newp += len;
978 }
979 else
980 {
981 /*
982 * copy bitmap [+ padding] [+ oid] + data from record, all in one
983 * go
984 */
985 memcpy(newp, recdata, tuplen);
986 recdata += tuplen;
987 newp += tuplen;
988 }
990
991 /* copy suffix from old tuple */
992 if (suffixlen > 0)
993 memcpy(newp, (char *) oldtup.t_data + oldtup.t_len - suffixlen, suffixlen);
994
996 htup->t_infomask2 = xlhdr.t_infomask2;
997 htup->t_infomask = xlhdr.t_infomask;
998 htup->t_hoff = xlhdr.t_hoff;
999
1002 HeapTupleHeaderSetXmax(htup, xlrec->new_xmax);
1003 /* Make sure there is no forward chain link in t_ctid */
1004 htup->t_ctid = newtid;
1005
1006 offnum = PageAddItem(npage, htup, newlen, offnum, true, true);
1007 if (offnum == InvalidOffsetNumber)
1008 elog(PANIC, "failed to add tuple");
1009
1011 PageClearAllVisible(npage);
1012
1013 /* needed to update FSM below */
1014 freespace = PageGetHeapFreeSpace(npage);
1015
1016 PageSetLSN(npage, lsn);
1017 /* See heap_insert() for why we set pd_prune_xid on insert */
1018 PageSetPrunable(npage, XLogRecGetXid(record));
1020 }
1021
1026
1027 /*
1028 * If the new page is running low on free space, update the FSM as well.
1029 * Arbitrarily, our definition of "low" is less than 20%. We can't do much
1030 * better than that without knowing the fill-factor for the table.
1031 *
1032 * However, don't update the FSM on HOT updates, because after crash
1033 * recovery, either the old or the new tuple will certainly be dead and
1034 * prunable. After pruning, the page will have roughly as much free space
1035 * as it did before the update, assuming the new tuple is about the same
1036 * size as the old one.
1037 *
1038 * XXX: Don't do this if the page was restored from full page image. We
1039 * don't bother to update the FSM in that case, it doesn't need to be
1040 * totally accurate anyway.
1041 */
1042 if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < BLCKSZ / 5)
1043 XLogRecordPageWithFreeSpace(rlocator, newblk, freespace);
1044}
1045
1046/*
1047 * Replay XLOG_HEAP_CONFIRM records.
1048 */
1049static void
1051{
1052 XLogRecPtr lsn = record->EndRecPtr;
1054 Buffer buffer;
1055 Page page;
1056 OffsetNumber offnum;
1057 ItemId lp;
1058 HeapTupleHeader htup;
1059
1060 if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
1061 {
1062 page = BufferGetPage(buffer);
1063
1064 offnum = xlrec->offnum;
1066 elog(PANIC, "offnum out of range");
1067 lp = PageGetItemId(page, offnum);
1068 if (!ItemIdIsNormal(lp))
1069 elog(PANIC, "invalid lp");
1070
1071 htup = (HeapTupleHeader) PageGetItem(page, lp);
1072
1073 /*
1074 * Confirm tuple as actually inserted
1075 */
1076 ItemPointerSet(&htup->t_ctid, BufferGetBlockNumber(buffer), offnum);
1077
1078 PageSetLSN(page, lsn);
1079 MarkBufferDirty(buffer);
1080 }
1081 if (BufferIsValid(buffer))
1082 UnlockReleaseBuffer(buffer);
1083}
1084
1085/*
1086 * Replay XLOG_HEAP_LOCK records.
1087 */
1088static void
1090{
1091 XLogRecPtr lsn = record->EndRecPtr;
1093 Buffer buffer;
1094 Page page;
1095 OffsetNumber offnum;
1096 ItemId lp;
1097 HeapTupleHeader htup;
1098
1099 /*
1100 * The visibility map may need to be fixed even if the heap page is
1101 * already up-to-date.
1102 */
1104 {
1105 RelFileLocator rlocator;
1106 BlockNumber block;
1107
1108 XLogRecGetBlockTag(record, HEAP_LOCK_BLKREF_HEAP, &rlocator, NULL,
1109 &block);
1110
1111 heap_xlog_vm_clear(record, rlocator,
1112 block, HEAP_LOCK_BLKREF_VM,
1114 }
1115
1117 &buffer) == BLK_NEEDS_REDO)
1118 {
1119 page = BufferGetPage(buffer);
1120
1121 offnum = xlrec->offnum;
1123 elog(PANIC, "offnum out of range");
1124 lp = PageGetItemId(page, offnum);
1125 if (!ItemIdIsNormal(lp))
1126 elog(PANIC, "invalid lp");
1127
1128 htup = (HeapTupleHeader) PageGetItem(page, lp);
1129
1130 htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
1132 fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
1133 &htup->t_infomask2);
1134
1135 /*
1136 * Clear relevant update flags, but only if the modified infomask says
1137 * there's no update.
1138 */
1140 {
1142 /* Make sure there is no forward chain link in t_ctid */
1143 ItemPointerSet(&htup->t_ctid,
1144 BufferGetBlockNumber(buffer),
1145 offnum);
1146 }
1147 HeapTupleHeaderSetXmax(htup, xlrec->xmax);
1149 PageSetLSN(page, lsn);
1150 MarkBufferDirty(buffer);
1151 }
1152 if (BufferIsValid(buffer))
1153 UnlockReleaseBuffer(buffer);
1154}
1155
1156/*
1157 * Replay XLOG_HEAP2_LOCK_UPDATED records.
1158 */
1159static void
1161{
1162 XLogRecPtr lsn = record->EndRecPtr;
1164 Buffer buffer;
1165 Page page;
1166 OffsetNumber offnum;
1167 ItemId lp;
1168 HeapTupleHeader htup;
1169
1171
1172 /*
1173 * The visibility map may need to be fixed even if the heap page is
1174 * already up-to-date.
1175 */
1177 {
1178 RelFileLocator rlocator;
1179 BlockNumber block;
1180
1181 XLogRecGetBlockTag(record, HEAP_LOCK_BLKREF_HEAP, &rlocator, NULL,
1182 &block);
1183
1184 heap_xlog_vm_clear(record, rlocator,
1185 block, HEAP_LOCK_BLKREF_VM,
1187 }
1188
1190 &buffer) == BLK_NEEDS_REDO)
1191 {
1192 page = BufferGetPage(buffer);
1193
1194 offnum = xlrec->offnum;
1196 elog(PANIC, "offnum out of range");
1197 lp = PageGetItemId(page, offnum);
1198 if (!ItemIdIsNormal(lp))
1199 elog(PANIC, "invalid lp");
1200
1201 htup = (HeapTupleHeader) PageGetItem(page, lp);
1202
1203 htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
1205 fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
1206 &htup->t_infomask2);
1207 HeapTupleHeaderSetXmax(htup, xlrec->xmax);
1208
1209 PageSetLSN(page, lsn);
1210 MarkBufferDirty(buffer);
1211 }
1212 if (BufferIsValid(buffer))
1213 UnlockReleaseBuffer(buffer);
1214}
1215
1216/*
1217 * Replay XLOG_HEAP_INPLACE records.
1218 */
1219static void
1221{
1222 XLogRecPtr lsn = record->EndRecPtr;
1224 Buffer buffer;
1225 Page page;
1226 OffsetNumber offnum;
1227 ItemId lp;
1228 HeapTupleHeader htup;
1229 uint32 oldlen;
1230 Size newlen;
1231
1232 if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
1233 {
1234 char *newtup = XLogRecGetBlockData(record, 0, &newlen);
1235
1236 page = BufferGetPage(buffer);
1237
1238 offnum = xlrec->offnum;
1240 elog(PANIC, "offnum out of range");
1241 lp = PageGetItemId(page, offnum);
1242 if (!ItemIdIsNormal(lp))
1243 elog(PANIC, "invalid lp");
1244
1245 htup = (HeapTupleHeader) PageGetItem(page, lp);
1246
1247 oldlen = ItemIdGetLength(lp) - htup->t_hoff;
1248 if (oldlen != newlen)
1249 elog(PANIC, "wrong tuple length");
1250
1251 memcpy((char *) htup + htup->t_hoff, newtup, newlen);
1252
1253 PageSetLSN(page, lsn);
1254 MarkBufferDirty(buffer);
1255 }
1256 if (BufferIsValid(buffer))
1257 UnlockReleaseBuffer(buffer);
1258
1260 xlrec->nmsgs,
1261 xlrec->relcacheInitFileInval,
1262 xlrec->dbId,
1263 xlrec->tsId);
1264}
1265
1266void
1268{
1269 uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1270
1271 /*
1272 * These operations don't overwrite MVCC data so no conflict processing is
1273 * required. The ones in heap2 rmgr do.
1274 */
1275
1276 switch (info & XLOG_HEAP_OPMASK)
1277 {
1278 case XLOG_HEAP_INSERT:
1279 heap_xlog_insert(record);
1280 break;
1281 case XLOG_HEAP_DELETE:
1282 heap_xlog_delete(record);
1283 break;
1284 case XLOG_HEAP_UPDATE:
1285 heap_xlog_update(record, false);
1286 break;
1287 case XLOG_HEAP_TRUNCATE:
1288
1289 /*
1290 * TRUNCATE is a no-op because the actions are already logged as
1291 * SMGR WAL records. TRUNCATE WAL record only exists for logical
1292 * decoding.
1293 */
1294 break;
1296 heap_xlog_update(record, true);
1297 break;
1298 case XLOG_HEAP_CONFIRM:
1299 heap_xlog_confirm(record);
1300 break;
1301 case XLOG_HEAP_LOCK:
1302 heap_xlog_lock(record);
1303 break;
1304 case XLOG_HEAP_INPLACE:
1305 heap_xlog_inplace(record);
1306 break;
1307 default:
1308 elog(PANIC, "heap_redo: unknown op code %u", info);
1309 }
1310}
1311
1312void
1314{
1315 uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1316
1317 switch (info & XLOG_HEAP_OPMASK)
1318 {
1322 heap_xlog_prune_freeze(record);
1323 break;
1325 heap_xlog_multi_insert(record);
1326 break;
1328 heap_xlog_lock_updated(record);
1329 break;
1330 case XLOG_HEAP2_NEW_CID:
1331
1332 /*
1333 * Nothing to do on a real replay, only used during logical
1334 * decoding.
1335 */
1336 break;
1337 case XLOG_HEAP2_REWRITE:
1339 break;
1340 default:
1341 elog(PANIC, "heap2_redo: unknown op code %u", info);
1342 }
1343}
1344
1345/*
1346 * Mask a heap page before performing consistency checks on it.
1347 */
1348void
1350{
1351 Page page = (Page) pagedata;
1352 OffsetNumber off;
1353
1355
1356 mask_page_hint_bits(page);
1357 mask_unused_space(page);
1358
1359 for (off = 1; off <= PageGetMaxOffsetNumber(page); off++)
1360 {
1361 ItemId iid = PageGetItemId(page, off);
1362 char *page_item;
1363
1364 page_item = (char *) (page + ItemIdGetOffset(iid));
1365
1366 if (ItemIdIsNormal(iid))
1367 {
1369
1370 /*
1371 * If xmin of a tuple is not yet frozen, we should ignore
1372 * differences in hint bits, since they can be set without
1373 * emitting WAL.
1374 */
1377 else
1378 {
1379 /* Still we need to mask xmax hint bits. */
1380 page_htup->t_infomask &= ~HEAP_XMAX_INVALID;
1381 page_htup->t_infomask &= ~HEAP_XMAX_COMMITTED;
1382 }
1383
1384 /*
1385 * During replay, we set Command Id to FirstCommandId. Hence, mask
1386 * it. See heap_xlog_insert() for details.
1387 */
1388 page_htup->t_choice.t_heap.t_field3.t_cid = MASK_MARKER;
1389
1390 /*
1391 * For a speculative tuple, heap_insert() does not set ctid in the
1392 * caller-passed heap tuple itself, leaving the ctid field to
1393 * contain a speculative token value - a per-backend monotonically
1394 * increasing identifier. Besides, it does not WAL-log ctid under
1395 * any circumstances.
1396 *
1397 * During redo, heap_xlog_insert() sets t_ctid to current block
1398 * number and self offset number. It doesn't care about any
1399 * speculative insertions on the primary. Hence, we set t_ctid to
1400 * current block number and self offset number to ignore any
1401 * inconsistency.
1402 */
1404 ItemPointerSet(&page_htup->t_ctid, blkno, off);
1405
1406 /*
1407 * NB: Not ignoring ctid changes due to the tuple having moved
1408 * (i.e. HeapTupleHeaderIndicatesMovedPartitions), because that's
1409 * important information that needs to be in-sync between primary
1410 * and standby, and thus is WAL logged.
1411 */
1412 }
1413
1414 /*
1415 * Ignore any padding bytes after the tuple, when the length of the
1416 * item is not MAXALIGNed.
1417 */
1418 if (ItemIdHasStorage(iid))
1419 {
1420 int len = ItemIdGetLength(iid);
1421 int padlen = MAXALIGN(len) - len;
1422
1423 if (padlen > 0)
1425 }
1426 }
1427}
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:70
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:4469
bool BufferIsDirty(Buffer buffer)
Definition bufmgr.c:3137
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5626
void MarkBufferDirty(Buffer buffer)
Definition bufmgr.c:3170
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:468
static Size BufferGetPageSize(Buffer buffer)
Definition bufmgr.h:457
@ RBM_ZERO_ON_ERROR
Definition bufmgr.h:51
@ RBM_NORMAL
Definition bufmgr.h:46
static bool BufferIsValid(Buffer bufnum)
Definition bufmgr.h:419
Size PageGetHeapFreeSpace(const PageData *page)
Definition bufpage.c:1000
void PageInit(Page page, Size pageSize, Size specialSize)
Definition bufpage.c:42
static void PageClearAllVisible(Page page)
Definition bufpage.h:464
static bool PageIsNew(const PageData *page)
Definition bufpage.h:258
static void PageSetAllVisible(Page page)
Definition bufpage.h:459
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition bufpage.h:268
static void * PageGetItem(PageData *page, const ItemIdData *itemId)
Definition bufpage.h:378
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition bufpage.h:416
PageData * Page
Definition bufpage.h:81
#define PageClearPrunable(page)
Definition bufpage.h:485
#define PageSetPrunable(page, xid)
Definition bufpage.h:478
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition bufpage.h:504
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition bufpage.h:396
#define MAXALIGN(LEN)
Definition c.h:955
uint8_t uint8
Definition c.h:681
#define Assert(condition)
Definition c.h:1002
#define FirstCommandId
Definition c.h:811
#define SHORTALIGN(LEN)
Definition c.h:951
uint16_t uint16
Definition c.h:682
uint32_t uint32
Definition c.h:683
#define MemSet(start, val, len)
Definition c.h:1147
uint32 TransactionId
Definition c.h:795
size_t Size
Definition c.h:748
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
#define PANIC
Definition elog.h:44
#define elog(elevel,...)
Definition elog.h:228
void XLogRecordPageWithFreeSpace(RelFileLocator rlocator, BlockNumber heapBlk, Size spaceAvail)
Definition freespace.c:211
static void heap_execute_freeze_tuple(HeapTupleHeader tuple, HeapTupleFreeze *frz)
Definition heapam.h:533
void heap_redo(XLogReaderState *record)
static void heap_xlog_prune_freeze(XLogReaderState *record)
Definition heapam_xlog.c:70
void heap_mask(char *pagedata, BlockNumber blkno)
static void heap_xlog_insert(XLogReaderState *record)
static void fix_infomask_from_infobits(uint8 infobits, uint16 *infomask, uint16 *infomask2)
static void heap_xlog_update(XLogReaderState *record, bool hot_update)
static void heap_xlog_delete(XLogReaderState *record)
static void heap_xlog_lock_updated(XLogReaderState *record)
static void heap_xlog_lock(XLogReaderState *record)
static void heap_xlog_multi_insert(XLogReaderState *record)
static void heap_xlog_inplace(XLogReaderState *record)
static void heap_xlog_confirm(XLogReaderState *record)
void heap2_redo(XLogReaderState *record)
static void heap_xlog_vm_clear(XLogReaderState *record, RelFileLocator target_locator, BlockNumber heap_blkno, uint8 wal_vm_block_id, uint8 flags)
Definition heapam_xlog.c:40
#define HEAP_DELETE_BLKREF_HEAP
#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
#define XLOG_HEAP2_REWRITE
Definition heapam_xlog.h:59
#define XLH_LOCK_ALL_FROZEN_CLEARED
#define XLOG_HEAP_TRUNCATE
Definition heapam_xlog.h:36
#define HEAP_MULTI_INSERT_BLKREF_HEAP
#define XLHP_VM_ALL_VISIBLE
#define HEAP_LOCK_BLKREF_VM
#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
#define XLHL_XMAX_KEYSHR_LOCK
#define XLH_DELETE_ALL_VISIBLE_CLEARED
#define HEAP_INSERT_BLKREF_VM
#define HEAP_UPDATE_BLKREF_HEAP_NEW
#define XLHP_HAS_NOW_UNUSED_ITEMS
#define HEAP_LOCK_BLKREF_HEAP
#define HEAP_DELETE_BLKREF_VM
#define XLHL_XMAX_IS_MULTI
#define XLHP_VM_ALL_FROZEN
#define XLHP_HAS_REDIRECTIONS
#define XLH_INSERT_ALL_VISIBLE_CLEARED
Definition heapam_xlog.h:72
#define SizeOfHeapHeader
#define XLOG_HEAP2_PRUNE_VACUUM_SCAN
Definition heapam_xlog.h:61
#define XLH_DELETE_IS_PARTITION_MOVE
#define XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED
Definition heapam_xlog.h:85
#define XLHL_XMAX_LOCK_ONLY
#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 HEAP_UPDATE_BLKREF_HEAP_OLD
#define XLH_UPDATE_PREFIX_FROM_OLD
Definition heapam_xlog.h:91
#define SizeOfMultiInsertTuple
#define XLHL_XMAX_EXCL_LOCK
#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
#define HEAP_INSERT_BLKREF_HEAP
#define HEAP_UPDATE_BLKREF_VM_OLD
#define HEAP_MULTI_INSERT_BLKREF_VM
#define XLHP_HAS_DEAD_ITEMS
#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 HEAP_UPDATE_BLKREF_VM_NEW
#define XLH_DELETE_IS_SUPER
#define XLHL_KEYS_UPDATED
#define XLHP_IS_CATALOG_REL
#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)
#define SizeofHeapTupleHeader
#define HEAP_KEYS_UPDATED
static bool HEAP_XMAX_IS_LOCKED_ONLY(uint16 infomask)
static void HeapTupleHeaderSetCmax(HeapTupleHeaderData *tup, CommandId cid, bool iscombo)
#define HEAP_XMAX_LOCK_ONLY
static void HeapTupleHeaderClearHotUpdated(HeapTupleHeaderData *tup)
static void HeapTupleHeaderSetCmin(HeapTupleHeaderData *tup, CommandId cid)
#define HEAP_XMAX_BITS
#define HEAP_MOVED
#define HEAP_XMAX_IS_MULTI
#define HEAP_XACT_MASK
#define HEAP_XMAX_EXCL_LOCK
static bool HeapTupleHeaderIsSpeculative(const HeapTupleHeaderData *tup)
static void HeapTupleHeaderSetXmin(HeapTupleHeaderData *tup, TransactionId xid)
#define HEAP_XMAX_KEYSHR_LOCK
static void HeapTupleHeaderSetMovedPartitions(HeapTupleHeaderData *tup)
#define MaxHeapTupleSize
static void HeapTupleHeaderSetXmax(HeapTupleHeaderData *tup, TransactionId xid)
static void HeapTupleHeaderSetHotUpdated(HeapTupleHeaderData *tup)
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
static int fb(int x)
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:2087
void heap_xlog_logical_rewrite(XLogReaderState *r)
void ResolveRecoveryConflictWithSnapshot(TransactionId snapshotConflictHorizon, bool isCatalogRel, RelFileLocator locator)
Definition standby.c:470
TransactionId xmax
Definition heapam.h:156
ItemPointerData t_ctid
XLogRecPtr EndRecPtr
Definition xlogreader.h:206
TransactionId xmax
#define InvalidTransactionId
Definition transam.h:31
#define TransactionIdIsNormal(xid)
Definition transam.h:42
bool visibilitymap_clear(RelFileLocator rlocator, BlockNumber heapBlk, Buffer vmbuf, uint8 flags)
bool visibilitymap_pin_ok(BlockNumber heapBlk, Buffer vmbuf)
void visibilitymap_set(BlockNumber heapBlk, Buffer vmBuf, uint8 flags, RelFileLocator rlocator)
#define VISIBILITYMAP_VALID_BITS
#define VISIBILITYMAP_ALL_FROZEN
#define VISIBILITYMAP_ALL_VISIBLE
#define XLogHintBitIsNeeded()
Definition xlog.h:123
uint64 XLogRecPtr
Definition xlogdefs.h:21
bool XLogRecGetBlockTagExtended(XLogReaderState *record, uint8 block_id, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum, Buffer *prefetch_buffer)
char * XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len)
void XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum)
#define XLogRecGetInfo(decoder)
Definition xlogreader.h:410
#define XLogRecGetData(decoder)
Definition xlogreader.h:415
#define XLogRecGetXid(decoder)
Definition xlogreader.h:412
#define XLogRecHasBlockRef(decoder, block_id)
Definition xlogreader.h:420
XLogRedoAction XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id, Buffer *buf)
Definition xlogutils.c:303
Buffer XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id)
Definition xlogutils.c:315
XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record, uint8 block_id, ReadBufferMode mode, bool get_cleanup_lock, Buffer *buf)
Definition xlogutils.c:362
#define InHotStandby
Definition xlogutils.h:60
XLogRedoAction
Definition xlogutils.h:73
@ BLK_NEEDS_REDO
Definition xlogutils.h:74