Age Owner Branch data TLA Line data Source code
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"
19 : : #include "access/visibilitymap.h"
20 : : #include "access/xlog.h"
21 : : #include "access/xlogutils.h"
22 : : #include "storage/freespace.h"
23 : : #include "storage/standby.h"
24 : :
25 : : /*
26 : : * 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 : : */
39 : : static void
67 melanieplageman@gmai 40 :CBC 2522 : heap_xlog_vm_clear(XLogReaderState *record,
41 : : RelFileLocator target_locator,
42 : : BlockNumber heap_blkno,
43 : : uint8 wal_vm_block_id, uint8 flags)
44 : : {
45 : 2522 : XLogRecPtr lsn = record->EndRecPtr;
46 : 2522 : Buffer vmbuffer = InvalidBuffer;
47 : :
67 melanieplageman@gmai 48 [ + + - + ]:GNC 2522 : if (!XLogRecHasBlockRef(record, wal_vm_block_id))
49 : 4 : 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 : : */
56 [ + + ]: 2518 : if (XLogReadBufferForRedo(record, wal_vm_block_id,
57 : : &vmbuffer) == BLK_NEEDS_REDO)
58 : : {
59 [ + + ]: 1765 : if (visibilitymap_clear(target_locator, heap_blkno, vmbuffer, flags))
60 : 1684 : PageSetLSN(BufferGetPage(vmbuffer), lsn);
61 : : }
62 [ + - ]: 2518 : if (BufferIsValid(vmbuffer))
63 : 2518 : UnlockReleaseBuffer(vmbuffer);
67 melanieplageman@gmai 64 :ECB (2553) : }
65 : :
66 : : /*
67 : : * Replay XLOG_HEAP2_PRUNE_* records.
68 : : */
69 : : static void
738 michael@paquier.xyz 70 :CBC 21118 : heap_xlog_prune_freeze(XLogReaderState *record)
71 : : {
72 : 21118 : XLogRecPtr lsn = record->EndRecPtr;
73 : 21118 : char *maindataptr = XLogRecGetData(record);
74 : : xl_heap_prune xlrec;
75 : : Buffer buffer;
76 : : RelFileLocator rlocator;
77 : : BlockNumber blkno;
342 melanieplageman@gmai 78 : 21118 : Buffer vmbuffer = InvalidBuffer;
79 : 21118 : uint8 vmflags = 0;
80 : 21118 : Size freespace = 0;
157 81 : 21118 : bool do_update_fsm = false;
82 : :
738 michael@paquier.xyz 83 : 21118 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
84 : 21118 : memcpy(&xlrec, maindataptr, SizeOfHeapPrune);
85 : 21118 : maindataptr += SizeOfHeapPrune;
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 [ + + - + ]: 21118 : Assert((xlrec.flags & XLHP_CLEANUP_LOCK) != 0 ||
94 : : (xlrec.flags & (XLHP_HAS_REDIRECTIONS | XLHP_HAS_DEAD_ITEMS)) == 0);
95 : :
342 melanieplageman@gmai 96 [ + + ]: 21118 : if (xlrec.flags & XLHP_VM_ALL_VISIBLE)
97 : : {
98 : 10568 : vmflags = VISIBILITYMAP_ALL_VISIBLE;
99 [ + + ]: 10568 : if (xlrec.flags & XLHP_VM_ALL_FROZEN)
100 : 6539 : vmflags |= VISIBILITYMAP_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 : : */
738 michael@paquier.xyz 113 [ + + ]: 21118 : if ((xlrec.flags & XLHP_HAS_CONFLICT_HORIZON) != 0)
114 : : {
115 : : TransactionId snapshot_conflict_horizon;
116 : :
117 : : /* memcpy() because snapshot_conflict_horizon is stored unaligned */
118 : 16359 : memcpy(&snapshot_conflict_horizon, maindataptr, sizeof(TransactionId));
119 : 16359 : maindataptr += sizeof(TransactionId);
120 : :
121 [ + + ]: 16359 : if (InHotStandby)
122 : 15898 : ResolveRecoveryConflictWithSnapshot(snapshot_conflict_horizon,
123 : 15898 : (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 : : */
342 melanieplageman@gmai 131 [ + + ]: 21118 : if (XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL,
132 : 21118 : (xlrec.flags & XLHP_CLEANUP_LOCK) != 0,
133 : : &buffer) == BLK_NEEDS_REDO)
134 : : {
387 peter@eisentraut.org 135 : 14608 : 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;
145 : : OffsetNumber *frz_offsets;
738 michael@paquier.xyz 146 : 14608 : char *dataptr = XLogRecGetBlockData(record, 0, &datalen);
147 : : bool do_prune;
148 : :
149 : 14608 : heap_xlog_deserialize_prune_and_freeze(dataptr, xlrec.flags,
150 : : &nplans, &plans, &frz_offsets,
151 : : &nredirected, &redirected,
152 : : &ndead, &nowdead,
153 : : &nunused, &nowunused);
154 : :
342 melanieplageman@gmai 155 [ + + + + : 14608 : do_prune = nredirected > 0 || ndead > 0 || nunused > 0;
+ + ]
156 : :
157 : : /* Ensure the record does something */
158 [ + + + + : 14608 : Assert(do_prune || nplans > 0 || vmflags & VISIBILITYMAP_VALID_BITS);
- + ]
159 : :
160 : : /*
161 : : * Update all line pointers per the record, and repair fragmentation
162 : : * if needed.
163 : : */
164 [ + + ]: 14608 : if (do_prune)
738 michael@paquier.xyz 165 : 11608 : heap_page_prune_execute(buffer,
166 : 11608 : (xlrec.flags & XLHP_CLEANUP_LOCK) == 0,
167 : : redirected, nredirected,
168 : : nowdead, ndead,
169 : : nowunused, nunused);
170 : :
171 : : /* Freeze tuples */
172 [ + + ]: 16250 : for (int p = 0; p < nplans; p++)
173 : : {
174 : : HeapTupleFreeze frz;
175 : :
176 : : /*
177 : : * Convert freeze plan representation from WAL record into
178 : : * per-tuple format used by heap_execute_freeze_tuple
179 : : */
180 : 1642 : frz.xmax = plans[p].xmax;
181 : 1642 : frz.t_infomask2 = plans[p].t_infomask2;
182 : 1642 : frz.t_infomask = plans[p].t_infomask;
183 : 1642 : frz.frzflags = plans[p].frzflags;
184 : 1642 : frz.offset = InvalidOffsetNumber; /* unused, but be tidy */
185 : :
186 [ + + ]: 81832 : for (int i = 0; i < plans[p].ntuples; i++)
187 : : {
188 : 80190 : OffsetNumber offset = *(frz_offsets++);
189 : : ItemId lp;
190 : : HeapTupleHeader tuple;
191 : :
192 : 80190 : lp = PageGetItemId(page, offset);
193 : 80190 : tuple = (HeapTupleHeader) PageGetItem(page, lp);
194 : 80190 : heap_execute_freeze_tuple(tuple, &frz);
195 : : }
196 : : }
197 : :
198 : : /* There should be no more data */
199 [ - + ]: 14608 : 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 : : */
342 melanieplageman@gmai 207 [ + + ]: 14608 : if (vmflags & VISIBILITYMAP_VALID_BITS)
208 : : {
209 : 5886 : PageSetAllVisible(page);
202 210 : 5886 : PageClearPrunable(page);
211 : : }
212 : :
342 213 : 14608 : MarkBufferDirty(buffer);
214 : :
215 : : /*
216 : : * See log_heap_prune_and_freeze() for commentary on when we set the
217 : : * heap page LSN.
218 : : */
219 [ + + + + ]: 14608 : if (do_prune || nplans > 0 ||
220 [ + - + + : 1905 : ((vmflags & VISIBILITYMAP_VALID_BITS) && XLogHintBitIsNeeded()))
+ - ]
221 : 14608 : 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 : : */
738 michael@paquier.xyz 249 [ + - ]: 21118 : if (BufferIsValid(buffer))
250 : : {
342 melanieplageman@gmai 251 [ + + ]: 21118 : if ((xlrec.flags & (XLHP_HAS_REDIRECTIONS |
252 : : XLHP_HAS_DEAD_ITEMS |
253 : 5911 : XLHP_HAS_NOW_UNUSED_ITEMS)) ||
254 [ + + ]: 5911 : (vmflags & VISIBILITYMAP_VALID_BITS))
255 : : {
256 : 21095 : freespace = PageGetHeapFreeSpace(BufferGetPage(buffer));
157 257 : 21095 : 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 : : */
342 264 : 21118 : 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 : : */
274 [ + + + + ]: 31686 : if ((vmflags & VISIBILITYMAP_VALID_BITS) &&
275 : 10568 : XLogReadBufferForRedoExtended(record, 1,
276 : : RBM_ZERO_ON_ERROR,
277 : : false,
278 : : &vmbuffer) == BLK_NEEDS_REDO)
279 : : {
280 : 10206 : Page vmpage = BufferGetPage(vmbuffer);
281 : :
282 : : /* initialize the page if it was read as zeros */
283 [ + + ]: 10206 : if (PageIsNew(vmpage))
284 : 2 : PageInit(vmpage, BLCKSZ, 0);
285 : :
6 286 [ + - ]: 10206 : if (visibilitymap_set(blkno, vmbuffer, vmflags, rlocator) != vmflags)
287 : 10206 : PageSetLSN(vmpage, lsn);
288 : : }
289 : :
342 290 [ + + ]: 21118 : if (BufferIsValid(vmbuffer))
291 : 10568 : UnlockReleaseBuffer(vmbuffer);
292 : :
157 293 [ + + ]: 21118 : if (do_update_fsm)
342 294 : 21095 : XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
738 michael@paquier.xyz 295 : 21118 : }
296 : :
297 : : /*
298 : : * Given an "infobits" field from an XLog record, set the correct bits in the
299 : : * given infomask and infomask2 for the tuple touched by the record.
300 : : *
301 : : * (This is the reverse of compute_infobits).
302 : : */
303 : : static void
304 : 466914 : fix_infomask_from_infobits(uint8 infobits, uint16 *infomask, uint16 *infomask2)
305 : : {
306 : 466914 : *infomask &= ~(HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY |
307 : : HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_EXCL_LOCK);
308 : 466914 : *infomask2 &= ~HEAP_KEYS_UPDATED;
309 : :
310 [ + + ]: 466914 : if (infobits & XLHL_XMAX_IS_MULTI)
311 : 3 : *infomask |= HEAP_XMAX_IS_MULTI;
312 [ + + ]: 466914 : if (infobits & XLHL_XMAX_LOCK_ONLY)
313 : 57803 : *infomask |= HEAP_XMAX_LOCK_ONLY;
314 [ + + ]: 466914 : if (infobits & XLHL_XMAX_EXCL_LOCK)
315 : 56785 : *infomask |= HEAP_XMAX_EXCL_LOCK;
316 : : /* note HEAP_XMAX_SHR_LOCK isn't considered here */
317 [ + + ]: 466914 : if (infobits & XLHL_XMAX_KEYSHR_LOCK)
318 : 1032 : *infomask |= HEAP_XMAX_KEYSHR_LOCK;
319 : :
320 [ + + ]: 466914 : if (infobits & XLHL_KEYS_UPDATED)
321 : 311729 : *infomask2 |= HEAP_KEYS_UPDATED;
322 : 466914 : }
323 : :
324 : : /*
325 : : * Replay XLOG_HEAP_DELETE records.
326 : : */
327 : : static void
328 : 312358 : heap_xlog_delete(XLogReaderState *record)
329 : : {
330 : 312358 : XLogRecPtr lsn = record->EndRecPtr;
331 : 312358 : xl_heap_delete *xlrec = (xl_heap_delete *) XLogRecGetData(record);
332 : : Buffer buffer;
333 : : Page page;
334 : : ItemId lp;
335 : : HeapTupleHeader htup;
336 : : BlockNumber blkno;
337 : : RelFileLocator target_locator;
338 : : ItemPointerData target_tid;
339 : :
67 melanieplageman@gmai 340 : 312358 : XLogRecGetBlockTag(record, HEAP_DELETE_BLKREF_HEAP, &target_locator, NULL,
341 : : &blkno);
738 michael@paquier.xyz 342 : 312358 : ItemPointerSetBlockNumber(&target_tid, blkno);
343 : 312358 : ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
344 : :
345 : : /*
346 : : * The visibility map may need to be fixed even if the heap page is
347 : : * already up-to-date.
348 : : */
349 [ + + ]: 312358 : if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
67 melanieplageman@gmai 350 : 50 : heap_xlog_vm_clear(record, target_locator,
351 : : blkno, HEAP_DELETE_BLKREF_VM,
352 : : VISIBILITYMAP_VALID_BITS);
353 : :
354 [ + + ]: 312358 : if (XLogReadBufferForRedo(record, HEAP_DELETE_BLKREF_HEAP,
355 : : &buffer) == BLK_NEEDS_REDO)
356 : : {
738 michael@paquier.xyz 357 : 310330 : page = BufferGetPage(buffer);
358 : :
279 tgl@sss.pgh.pa.us 359 [ + - - + ]: 310330 : if (xlrec->offnum < 1 || xlrec->offnum > PageGetMaxOffsetNumber(page))
279 tgl@sss.pgh.pa.us 360 [ # # ]:UBC 0 : elog(PANIC, "offnum out of range");
279 tgl@sss.pgh.pa.us 361 :CBC 310330 : lp = PageGetItemId(page, xlrec->offnum);
362 [ - + ]: 310330 : if (!ItemIdIsNormal(lp))
738 michael@paquier.xyz 363 [ # # ]:UBC 0 : elog(PANIC, "invalid lp");
364 : :
738 michael@paquier.xyz 365 :CBC 310330 : htup = (HeapTupleHeader) PageGetItem(page, lp);
366 : :
367 : 310330 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
368 : 310330 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
369 : 310330 : HeapTupleHeaderClearHotUpdated(htup);
370 : 310330 : fix_infomask_from_infobits(xlrec->infobits_set,
371 : : &htup->t_infomask, &htup->t_infomask2);
372 [ + - ]: 310330 : if (!(xlrec->flags & XLH_DELETE_IS_SUPER))
373 : 310330 : HeapTupleHeaderSetXmax(htup, xlrec->xmax);
374 : : else
738 michael@paquier.xyz 375 :UBC 0 : HeapTupleHeaderSetXmin(htup, InvalidTransactionId);
738 michael@paquier.xyz 376 :CBC 310330 : HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
377 : :
378 : : /* Mark the page as a candidate for pruning */
379 [ - + + + : 310330 : PageSetPrunable(page, XLogRecGetXid(record));
- + ]
380 : :
381 [ + + ]: 310330 : if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
382 : 29 : PageClearAllVisible(page);
383 : :
384 : : /* Make sure t_ctid is set correctly */
385 [ + + ]: 310330 : if (xlrec->flags & XLH_DELETE_IS_PARTITION_MOVE)
386 : 144 : HeapTupleHeaderSetMovedPartitions(htup);
387 : : else
388 : 310186 : htup->t_ctid = target_tid;
389 : 310330 : PageSetLSN(page, lsn);
390 : 310330 : MarkBufferDirty(buffer);
391 : : }
392 [ + - ]: 312358 : if (BufferIsValid(buffer))
393 : 312358 : UnlockReleaseBuffer(buffer);
394 : 312358 : }
395 : :
396 : : /*
397 : : * Replay XLOG_HEAP_INSERT records.
398 : : */
399 : : static void
400 : 1327763 : heap_xlog_insert(XLogReaderState *record)
401 : : {
402 : 1327763 : XLogRecPtr lsn = record->EndRecPtr;
403 : 1327763 : xl_heap_insert *xlrec = (xl_heap_insert *) XLogRecGetData(record);
404 : : Buffer buffer;
405 : : Page page;
406 : : union
407 : : {
408 : : HeapTupleHeaderData hdr;
409 : : char data[MaxHeapTupleSize];
410 : : } tbuf;
411 : : HeapTupleHeader htup;
412 : : xl_heap_header xlhdr;
413 : : uint32 newlen;
414 : 1327763 : Size freespace = 0;
415 : : RelFileLocator target_locator;
416 : : BlockNumber blkno;
417 : : ItemPointerData target_tid;
418 : : XLogRedoAction action;
419 : :
67 melanieplageman@gmai 420 : 1327763 : XLogRecGetBlockTag(record, HEAP_INSERT_BLKREF_HEAP, &target_locator, NULL,
421 : : &blkno);
738 michael@paquier.xyz 422 : 1327763 : ItemPointerSetBlockNumber(&target_tid, blkno);
423 : 1327763 : ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
424 : :
425 : : /* No freezing in the heap_insert() code path */
451 melanieplageman@gmai 426 [ - + ]: 1327763 : Assert(!(xlrec->flags & XLH_INSERT_ALL_FROZEN_SET));
427 : :
428 : : /*
429 : : * The visibility map may need to be fixed even if the heap page is
430 : : * already up-to-date.
431 : : */
738 michael@paquier.xyz 432 [ + + ]: 1327763 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
67 melanieplageman@gmai 433 : 1099 : heap_xlog_vm_clear(record, target_locator,
434 : : blkno, HEAP_INSERT_BLKREF_VM,
435 : : VISIBILITYMAP_VALID_BITS);
436 : :
437 : : /*
438 : : * If we inserted the first and only tuple on the page, re-initialize the
439 : : * page from scratch.
440 : : */
738 michael@paquier.xyz 441 [ + + ]: 1327763 : if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
442 : : {
67 melanieplageman@gmai 443 : 18014 : buffer = XLogInitBufferForRedo(record, HEAP_INSERT_BLKREF_HEAP);
738 michael@paquier.xyz 444 : 18014 : page = BufferGetPage(buffer);
445 : 18014 : PageInit(page, BufferGetPageSize(buffer), 0);
446 : 18014 : action = BLK_NEEDS_REDO;
447 : : }
448 : : else
67 melanieplageman@gmai 449 : 1309749 : action = XLogReadBufferForRedo(record, HEAP_INSERT_BLKREF_HEAP,
450 : : &buffer);
738 michael@paquier.xyz 451 [ + + ]: 1327763 : if (action == BLK_NEEDS_REDO)
452 : : {
453 : : Size datalen;
454 : : char *data;
455 : :
456 : 1324811 : page = BufferGetPage(buffer);
457 : :
458 [ - + ]: 1324811 : if (PageGetMaxOffsetNumber(page) + 1 < xlrec->offnum)
738 michael@paquier.xyz 459 [ # # ]:UBC 0 : elog(PANIC, "invalid max offset number");
460 : :
67 melanieplageman@gmai 461 :CBC 1324811 : data = XLogRecGetBlockData(record, HEAP_INSERT_BLKREF_HEAP, &datalen);
462 : :
738 michael@paquier.xyz 463 : 1324811 : newlen = datalen - SizeOfHeapHeader;
464 [ + - - + ]: 1324811 : Assert(datalen > SizeOfHeapHeader && newlen <= MaxHeapTupleSize);
585 peter@eisentraut.org 465 : 1324811 : memcpy(&xlhdr, data, SizeOfHeapHeader);
738 michael@paquier.xyz 466 : 1324811 : data += SizeOfHeapHeader;
467 : :
468 : 1324811 : htup = &tbuf.hdr;
585 peter@eisentraut.org 469 [ + - - + : 1324811 : MemSet(htup, 0, SizeofHeapTupleHeader);
- - - - -
- ]
470 : : /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
738 michael@paquier.xyz 471 : 1324811 : memcpy((char *) htup + SizeofHeapTupleHeader,
472 : : data,
473 : : newlen);
474 : 1324811 : newlen += SizeofHeapTupleHeader;
475 : 1324811 : htup->t_infomask2 = xlhdr.t_infomask2;
476 : 1324811 : htup->t_infomask = xlhdr.t_infomask;
477 : 1324811 : htup->t_hoff = xlhdr.t_hoff;
478 : 1324811 : HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
479 : 1324811 : HeapTupleHeaderSetCmin(htup, FirstCommandId);
480 : 1324811 : htup->t_ctid = target_tid;
481 : :
328 peter@eisentraut.org 482 [ - + ]: 1324811 : if (PageAddItem(page, htup, newlen, xlrec->offnum, true, true) == InvalidOffsetNumber)
738 michael@paquier.xyz 483 [ # # ]:UBC 0 : elog(PANIC, "failed to add tuple");
484 : :
738 michael@paquier.xyz 485 :CBC 1324811 : freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
486 : :
487 : : /*
488 : : * Set the page prunable to trigger on-access pruning later, which may
489 : : * set the page all-visible in the VM. See comments in heap_insert().
490 : : */
174 melanieplageman@gmai 491 [ + - ]: 1324811 : if (TransactionIdIsNormal(XLogRecGetXid(record)) &&
492 [ + + ]: 1324811 : !HeapTupleHeaderXminFrozen(htup))
493 [ - + + + : 1324281 : PageSetPrunable(page, XLogRecGetXid(record));
+ + ]
494 : :
738 michael@paquier.xyz 495 : 1324811 : PageSetLSN(page, lsn);
496 : :
497 [ + + ]: 1324811 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
498 : 344 : PageClearAllVisible(page);
499 : :
500 : 1324811 : MarkBufferDirty(buffer);
501 : : }
502 [ + - ]: 1327763 : if (BufferIsValid(buffer))
503 : 1327763 : UnlockReleaseBuffer(buffer);
504 : :
505 : : /*
506 : : * If the page is running low on free space, update the FSM as well.
507 : : * Arbitrarily, our definition of "low" is less than 20%. We can't do much
508 : : * better than that without knowing the fill-factor for the table.
509 : : *
510 : : * XXX: Don't do this if the page was restored from full page image. We
511 : : * don't bother to update the FSM in that case, it doesn't need to be
512 : : * totally accurate anyway.
513 : : */
514 [ + + + + ]: 1327763 : if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
515 : 260853 : XLogRecordPageWithFreeSpace(target_locator, blkno, freespace);
516 : 1327763 : }
517 : :
518 : : /*
519 : : * Replay XLOG_HEAP2_MULTI_INSERT records.
520 : : */
521 : : static void
522 : 63429 : heap_xlog_multi_insert(XLogReaderState *record)
523 : : {
524 : 63429 : XLogRecPtr lsn = record->EndRecPtr;
525 : : xl_heap_multi_insert *xlrec;
526 : : RelFileLocator rlocator;
527 : : BlockNumber blkno;
528 : : Buffer buffer;
529 : : Page page;
530 : : union
531 : : {
532 : : HeapTupleHeaderData hdr;
533 : : char data[MaxHeapTupleSize];
534 : : } tbuf;
535 : : HeapTupleHeader htup;
536 : : uint32 newlen;
537 : 63429 : Size freespace = 0;
538 : : int i;
539 : 63429 : bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
540 : : XLogRedoAction action;
346 melanieplageman@gmai 541 : 63429 : Buffer vmbuffer = InvalidBuffer;
542 : :
543 : : /*
544 : : * Insertion doesn't overwrite MVCC data, so no conflict processing is
545 : : * required.
546 : : */
738 michael@paquier.xyz 547 : 63429 : xlrec = (xl_heap_multi_insert *) XLogRecGetData(record);
548 : :
67 melanieplageman@gmai 549 : 63429 : XLogRecGetBlockTag(record, HEAP_MULTI_INSERT_BLKREF_HEAP, &rlocator, NULL,
550 : : &blkno);
551 : :
552 : : /* check that the mutually exclusive flags are not both set */
738 michael@paquier.xyz 553 [ + + - + ]: 63429 : Assert(!((xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED) &&
554 : : (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)));
555 : :
556 : : /*
557 : : * The visibility map may need to be fixed even if the heap page is
558 : : * already up-to-date.
559 : : *
560 : : * Clear the VM (if needed) before clearing the heap page-level visibility
561 : : * flag (PD_ALL_VISIBLE) to prevent the heap page from being marked
562 : : * all-visible in the VM while its PD_ALL_VISIBLE is clear.
563 : : */
564 [ + + ]: 63429 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
67 melanieplageman@gmai 565 : 1341 : heap_xlog_vm_clear(record, rlocator,
566 : : blkno, HEAP_MULTI_INSERT_BLKREF_VM,
567 : : VISIBILITYMAP_VALID_BITS);
568 : :
738 michael@paquier.xyz 569 [ + + ]: 63429 : if (isinit)
570 : : {
67 melanieplageman@gmai 571 : 1688 : buffer = XLogInitBufferForRedo(record, HEAP_MULTI_INSERT_BLKREF_HEAP);
738 michael@paquier.xyz 572 : 1688 : page = BufferGetPage(buffer);
573 : 1688 : PageInit(page, BufferGetPageSize(buffer), 0);
574 : 1688 : action = BLK_NEEDS_REDO;
575 : : }
576 : : else
67 melanieplageman@gmai 577 : 61741 : action = XLogReadBufferForRedo(record, HEAP_MULTI_INSERT_BLKREF_HEAP,
578 : : &buffer);
738 michael@paquier.xyz 579 [ + + ]: 63429 : if (action == BLK_NEEDS_REDO)
580 : : {
581 : : char *tupdata;
582 : : char *endptr;
583 : : Size len;
6 melanieplageman@gmai 584 : 61441 : bool inserted_tuples_frozen = false;
585 : :
586 : : /* Tuples are stored as block data */
67 587 : 61441 : tupdata = XLogRecGetBlockData(record, HEAP_MULTI_INSERT_BLKREF_HEAP,
588 : : &len);
738 michael@paquier.xyz 589 : 61441 : endptr = tupdata + len;
590 : :
387 peter@eisentraut.org 591 : 61441 : page = BufferGetPage(buffer);
592 : :
738 michael@paquier.xyz 593 [ + + ]: 277500 : for (i = 0; i < xlrec->ntuples; i++)
594 : : {
595 : : OffsetNumber offnum;
596 : : xl_multi_insert_tuple *xlhdr;
597 : :
598 : : /*
599 : : * If we're reinitializing the page, the tuples are stored in
600 : : * order from FirstOffsetNumber. Otherwise there's an array of
601 : : * offsets in the WAL record, and the tuples come after that.
602 : : */
603 [ + + ]: 216059 : if (isinit)
604 : 99203 : offnum = FirstOffsetNumber + i;
605 : : else
606 : 116856 : offnum = xlrec->offsets[i];
607 [ - + ]: 216059 : if (PageGetMaxOffsetNumber(page) + 1 < offnum)
738 michael@paquier.xyz 608 [ # # ]:UBC 0 : elog(PANIC, "invalid max offset number");
609 : :
738 michael@paquier.xyz 610 :CBC 216059 : xlhdr = (xl_multi_insert_tuple *) SHORTALIGN(tupdata);
611 : 216059 : tupdata = ((char *) xlhdr) + SizeOfMultiInsertTuple;
612 : :
613 : 216059 : newlen = xlhdr->datalen;
614 [ - + ]: 216059 : Assert(newlen <= MaxHeapTupleSize);
615 : 216059 : htup = &tbuf.hdr;
585 peter@eisentraut.org 616 [ + - - + : 216059 : MemSet(htup, 0, SizeofHeapTupleHeader);
- - - - -
- ]
617 : : /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
738 michael@paquier.xyz 618 : 216059 : memcpy((char *) htup + SizeofHeapTupleHeader,
619 : : tupdata,
620 : : newlen);
621 : 216059 : tupdata += newlen;
622 : :
623 : 216059 : newlen += SizeofHeapTupleHeader;
624 : 216059 : htup->t_infomask2 = xlhdr->t_infomask2;
625 : 216059 : htup->t_infomask = xlhdr->t_infomask;
626 : 216059 : htup->t_hoff = xlhdr->t_hoff;
627 : 216059 : HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
628 : 216059 : HeapTupleHeaderSetCmin(htup, FirstCommandId);
629 : 216059 : ItemPointerSetBlockNumber(&htup->t_ctid, blkno);
630 : 216059 : ItemPointerSetOffsetNumber(&htup->t_ctid, offnum);
631 : :
632 : : /* If one inserted tuple was frozen, they all were */
6 melanieplageman@gmai 633 [ + + ]: 216059 : if (i == 0)
634 : 61441 : inserted_tuples_frozen = HeapTupleHeaderXminFrozen(htup);
635 : :
328 peter@eisentraut.org 636 : 216059 : offnum = PageAddItem(page, htup, newlen, offnum, true, true);
738 michael@paquier.xyz 637 [ - + ]: 216059 : if (offnum == InvalidOffsetNumber)
738 michael@paquier.xyz 638 [ # # ]:UBC 0 : elog(PANIC, "failed to add tuple");
639 : : }
738 michael@paquier.xyz 640 [ - + ]:CBC 61441 : if (tupdata != endptr)
738 michael@paquier.xyz 641 [ # # ]:UBC 0 : elog(PANIC, "total tuple length mismatch");
642 : :
738 michael@paquier.xyz 643 :CBC 61441 : PageSetLSN(page, lsn);
644 : :
645 [ + + ]: 61441 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
646 : 111 : PageClearAllVisible(page);
647 : :
648 : : /*
649 : : * XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible, so
650 : : * set PD_ALL_VISIBLE and clear pd_prune_xid.
651 : : *
652 : : * If the page isn't being set all-frozen and we aren't inserting
653 : : * frozen tuples, set pd_prune_xid so that the page gets on-access
654 : : * pruned.
655 : : *
656 : : * Frozen tuples may be added to an already all-frozen page or to a
657 : : * page containing non-frozen tuples, but they introduce nothing new
658 : : * for on-access pruning, so preserve the existing hint.
659 : : */
660 [ + + ]: 61441 : if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
661 : : {
662 : 4 : PageSetAllVisible(page);
202 melanieplageman@gmai 663 : 4 : PageClearPrunable(page);
664 : : }
6 665 [ + - ]: 61437 : else if (!inserted_tuples_frozen)
174 666 [ - + + + : 61437 : PageSetPrunable(page, XLogRecGetXid(record));
+ + ]
667 : :
738 michael@paquier.xyz 668 : 61441 : MarkBufferDirty(buffer);
669 : : }
670 : :
671 [ + - ]: 63429 : if (BufferIsValid(buffer))
672 : : {
673 : : /*
674 : : * If we are marking the page all-frozen or the page is running low on
675 : : * free space, update the FSM as well. Arbitrarily, our definition of
676 : : * "low" is less than 20%. We can't do much better than that without
677 : : * knowing the fill-factor for the table.
678 : : *
679 : : * XXX: Unless setting the page all-frozen, we don't do this if the
680 : : * page was restored from full page image. We don't bother to update
681 : : * the FSM in that case, it doesn't need to be totally accurate
682 : : * anyway.
683 : : *
684 : : * If setting the page all-frozen, we update the FSM regardless since,
685 : : * once frozen, we lose the chance to update it during vacuum after
686 : : * promotion. See comment in heap_xlog_prune_freeze() for details.
687 : : */
6 melanieplageman@gmai 688 : 63429 : bool update_fsm = false;
689 : :
690 [ + + + + ]: 63429 : if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET ||
691 : : action == BLK_NEEDS_REDO)
692 : : {
693 : 61441 : freespace = PageGetHeapFreeSpace(BufferGetPage(buffer));
694 [ + + + + ]: 61441 : if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET ||
695 : : freespace < BLCKSZ / 5)
696 : 17999 : update_fsm = true;
697 : : }
698 : :
738 michael@paquier.xyz 699 : 63429 : UnlockReleaseBuffer(buffer);
700 : :
6 melanieplageman@gmai 701 [ + + ]: 63429 : if (update_fsm)
702 : 17999 : XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
703 : : }
704 : :
346 705 : 63429 : buffer = InvalidBuffer;
706 : :
707 : : /*
708 : : * Read and update the visibility map (VM) block to set it frozen.
709 : : *
710 : : * We must always redo VM changes, even if the corresponding heap page
711 : : * update was skipped due to the LSN interlock. Each VM block covers
712 : : * multiple heap pages, so later WAL records may update other bits in the
713 : : * same block. If this record includes an FPI (full-page image),
714 : : * subsequent WAL records may depend on it to guard against torn pages.
715 : : *
716 : : * Heap page changes are replayed first to preserve the invariant:
717 : : * PD_ALL_VISIBLE must be set on the heap page if the VM bit is set.
718 : : *
719 : : * Note that we released the heap page lock above. During normal
720 : : * operation, this would be unsafe — a concurrent modification could
721 : : * clear PD_ALL_VISIBLE while the VM bit remained set, violating the
722 : : * invariant.
723 : : *
724 : : * During recovery, however, no concurrent writers exist. Therefore,
725 : : * updating the VM without holding the heap page lock is safe enough. This
726 : : * same approach is taken when replaying XLOG_HEAP2_PRUNE* records (see
727 : : * heap_xlog_prune_freeze()).
728 : : */
729 [ + + - + ]: 63433 : if ((xlrec->flags & XLH_INSERT_ALL_FROZEN_SET) &&
730 : 4 : XLogReadBufferForRedoExtended(record, 1, RBM_ZERO_ON_ERROR, false,
731 : : &vmbuffer) == BLK_NEEDS_REDO)
732 : : {
346 melanieplageman@gmai 733 :UBC 0 : Page vmpage = BufferGetPage(vmbuffer);
6 734 : 0 : uint8 vmflags = VISIBILITYMAP_ALL_VISIBLE |
735 : : VISIBILITYMAP_ALL_FROZEN;
736 : :
737 : : /* initialize the page if it was read as zeros */
346 738 [ # # ]: 0 : if (PageIsNew(vmpage))
739 : 0 : PageInit(vmpage, BLCKSZ, 0);
740 : :
6 741 [ # # ]: 0 : if (visibilitymap_set(blkno, vmbuffer, vmflags, rlocator) != vmflags)
742 : 0 : PageSetLSN(vmpage, lsn);
743 : : }
744 : :
346 melanieplageman@gmai 745 [ + + ]:CBC 63429 : if (BufferIsValid(vmbuffer))
746 : 4 : UnlockReleaseBuffer(vmbuffer);
738 michael@paquier.xyz 747 : 63429 : }
748 : :
749 : : /*
750 : : * Replay XLOG_HEAP_UPDATE and XLOG_HEAP_HOT_UPDATE records.
751 : : */
752 : : static void
753 : 99058 : heap_xlog_update(XLogReaderState *record, bool hot_update)
754 : : {
755 : 99058 : XLogRecPtr lsn = record->EndRecPtr;
756 : 99058 : xl_heap_update *xlrec = (xl_heap_update *) XLogRecGetData(record);
757 : : RelFileLocator rlocator;
758 : : BlockNumber oldblk;
759 : : BlockNumber newblk;
760 : : ItemPointerData newtid;
761 : : Buffer obuffer,
762 : : nbuffer;
763 : : Page opage,
764 : : npage;
765 : : bool has_vm_old,
766 : : has_vm_new;
767 : : OffsetNumber offnum;
768 : : ItemId lp;
769 : : HeapTupleData oldtup;
770 : : HeapTupleHeader htup;
771 : 99058 : uint16 prefixlen = 0,
772 : 99058 : suffixlen = 0;
773 : : char *newp;
774 : : union
775 : : {
776 : : HeapTupleHeaderData hdr;
777 : : char data[MaxHeapTupleSize];
778 : : } tbuf;
779 : : xl_heap_header xlhdr;
780 : : uint32 newlen;
781 : 99058 : Size freespace = 0;
782 : : XLogRedoAction oldaction;
783 : : XLogRedoAction newaction;
784 : :
785 : : /* initialize to keep the compiler quiet */
786 : 99058 : oldtup.t_data = NULL;
787 : 99058 : oldtup.t_len = 0;
788 : :
67 melanieplageman@gmai 789 : 99058 : XLogRecGetBlockTag(record, HEAP_UPDATE_BLKREF_HEAP_NEW, &rlocator, NULL,
790 : : &newblk);
791 [ + + ]: 99058 : if (XLogRecGetBlockTagExtended(record, HEAP_UPDATE_BLKREF_HEAP_OLD, NULL, NULL,
792 : : &oldblk, NULL))
793 : : {
794 : : /* HOT updates are never done across pages */
738 michael@paquier.xyz 795 [ - + ]: 56497 : Assert(!hot_update);
796 : : }
797 : : else
798 : 42561 : oldblk = newblk;
799 : :
800 : 99058 : ItemPointerSet(&newtid, newblk, xlrec->new_offnum);
801 : :
802 : : /*
803 : : * The visibility map may need to be fixed even if the heap page is
804 : : * already up-to-date.
805 : : */
67 melanieplageman@gmai 806 [ + + + - ]: 99058 : has_vm_old = XLogRecHasBlockRef(record, HEAP_UPDATE_BLKREF_VM_OLD);
807 [ + + + + ]: 99058 : has_vm_new = XLogRecHasBlockRef(record, HEAP_UPDATE_BLKREF_VM_NEW);
808 : :
809 [ + + ]: 99058 : if (has_vm_new)
810 : : {
811 : 237 : Buffer vmbuffer_new = InvalidBuffer;
812 : :
813 [ - + ]: 237 : Assert(xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED);
814 : :
815 [ + + ]: 237 : if (XLogReadBufferForRedo(record, HEAP_UPDATE_BLKREF_VM_NEW,
816 : : &vmbuffer_new) == BLK_NEEDS_REDO)
817 : : {
818 : : /*
819 : : * If both the old and new heap pages were all-visible and their
820 : : * VM bits are on the same VM page, that single VM page is
821 : : * registered as HEAP_UPDATE_BLKREF_VM_NEW. Clear both heap
822 : : * blocks' VM bits from the single provided VM buffer. It's
823 : : * possible that one of the page's VM bits were already clear, but
824 : : * visibilitymap_clear() is harmless as long as we provide it the
825 : : * correct bits.
826 : : *
827 : : * We must verify that oldblk's VM bits really are on this VM
828 : : * page, rather than relying on the absence of a separate VM_OLD
829 : : * block reference: VM_OLD is also omitted when oldblk is on a
830 : : * different VM page but its bit was already clear.
831 : : */
832 [ + + + - ]: 196 : if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED &&
833 : 9 : visibilitymap_pin_ok(oldblk, vmbuffer_new))
834 : : {
67 melanieplageman@gmai 835 [ + - ]:GNC 9 : if (visibilitymap_clear(rlocator, oldblk, vmbuffer_new,
836 : : VISIBILITYMAP_VALID_BITS))
67 melanieplageman@gmai 837 :CBC 9 : PageSetLSN(BufferGetPage(vmbuffer_new), lsn);
838 : : }
839 : : /* If VM_NEW is registered, we are sure newblk is on VM_NEW */
67 melanieplageman@gmai 840 [ + - ]:GNC 187 : if (visibilitymap_clear(rlocator, newblk, vmbuffer_new,
841 : : VISIBILITYMAP_VALID_BITS))
67 melanieplageman@gmai 842 :CBC 187 : PageSetLSN(BufferGetPage(vmbuffer_new), lsn);
843 : : }
844 [ + - ]: 237 : if (BufferIsValid(vmbuffer_new))
845 : 237 : UnlockReleaseBuffer(vmbuffer_new);
846 : : }
847 [ + + ]: 99058 : if (has_vm_old)
848 : : {
849 : 330 : Buffer vmbuffer_old = InvalidBuffer;
850 : :
851 [ - + ]: 330 : Assert(xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED);
852 : :
853 [ + + ]: 330 : if (XLogReadBufferForRedo(record, HEAP_UPDATE_BLKREF_VM_OLD,
854 : : &vmbuffer_old) == BLK_NEEDS_REDO)
855 : : {
67 melanieplageman@gmai 856 [ + + ]:GNC 273 : if (visibilitymap_clear(rlocator, oldblk, vmbuffer_old,
857 : : VISIBILITYMAP_VALID_BITS))
67 melanieplageman@gmai 858 :CBC 272 : PageSetLSN(BufferGetPage(vmbuffer_old), lsn);
859 : : }
860 [ + - ]: 330 : if (BufferIsValid(vmbuffer_old))
861 : 330 : UnlockReleaseBuffer(vmbuffer_old);
862 : : }
863 : :
864 : : /*
865 : : * In normal operation, it is important to lock the two pages in
866 : : * page-number order, to avoid possible deadlocks against other update
867 : : * operations going the other way. However, during WAL replay there can
868 : : * be no other update happening, so we don't need to worry about that.
869 : : * Notice we also don't worry about this when locking VM buffers above.
870 : : *
871 : : * But we *do* need to worry that we don't expose an inconsistent state to
872 : : * Hot Standby queries --- so the original page can't be unlocked before
873 : : * we've added the new tuple to the new page.
874 : : */
875 : :
876 : : /* Deal with old tuple version */
877 : 99058 : oldaction = XLogReadBufferForRedo(record, (oldblk == newblk) ?
878 : : HEAP_UPDATE_BLKREF_HEAP_NEW : HEAP_UPDATE_BLKREF_HEAP_OLD,
879 : : &obuffer);
738 michael@paquier.xyz 880 [ + + ]: 99058 : if (oldaction == BLK_NEEDS_REDO)
881 : : {
189 melanieplageman@gmai 882 : 98781 : opage = BufferGetPage(obuffer);
738 michael@paquier.xyz 883 : 98781 : offnum = xlrec->old_offnum;
189 melanieplageman@gmai 884 [ + - - + ]: 98781 : if (offnum < 1 || offnum > PageGetMaxOffsetNumber(opage))
279 tgl@sss.pgh.pa.us 885 [ # # ]:UBC 0 : elog(PANIC, "offnum out of range");
189 melanieplageman@gmai 886 :CBC 98781 : lp = PageGetItemId(opage, offnum);
279 tgl@sss.pgh.pa.us 887 [ - + ]: 98781 : if (!ItemIdIsNormal(lp))
738 michael@paquier.xyz 888 [ # # ]:UBC 0 : elog(PANIC, "invalid lp");
889 : :
189 melanieplageman@gmai 890 :CBC 98781 : htup = (HeapTupleHeader) PageGetItem(opage, lp);
891 : :
738 michael@paquier.xyz 892 : 98781 : oldtup.t_data = htup;
893 : 98781 : oldtup.t_len = ItemIdGetLength(lp);
894 : :
895 : 98781 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
896 : 98781 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
897 [ + + ]: 98781 : if (hot_update)
898 : 39236 : HeapTupleHeaderSetHotUpdated(htup);
899 : : else
900 : 59545 : HeapTupleHeaderClearHotUpdated(htup);
901 : 98781 : fix_infomask_from_infobits(xlrec->old_infobits_set, &htup->t_infomask,
902 : : &htup->t_infomask2);
903 : 98781 : HeapTupleHeaderSetXmax(htup, xlrec->old_xmax);
904 : 98781 : HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
905 : : /* Set forward chain link in t_ctid */
906 : 98781 : htup->t_ctid = newtid;
907 : :
908 : : /* Mark the page as a candidate for pruning */
189 melanieplageman@gmai 909 [ - + + + : 98781 : PageSetPrunable(opage, XLogRecGetXid(record));
- + ]
910 : :
738 michael@paquier.xyz 911 [ + + ]: 98781 : if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
189 melanieplageman@gmai 912 : 313 : PageClearAllVisible(opage);
913 : :
914 : 98781 : PageSetLSN(opage, lsn);
738 michael@paquier.xyz 915 : 98781 : MarkBufferDirty(obuffer);
916 : : }
917 : :
918 : : /*
919 : : * Read the page the new tuple goes into, if different from old.
920 : : */
921 [ + + ]: 99058 : if (oldblk == newblk)
922 : : {
923 : 42561 : nbuffer = obuffer;
924 : 42561 : newaction = oldaction;
925 : : }
926 [ + + ]: 56497 : else if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
927 : : {
67 melanieplageman@gmai 928 : 639 : nbuffer = XLogInitBufferForRedo(record, HEAP_UPDATE_BLKREF_HEAP_NEW);
189 929 : 639 : npage = BufferGetPage(nbuffer);
930 : 639 : PageInit(npage, BufferGetPageSize(nbuffer), 0);
738 michael@paquier.xyz 931 : 639 : newaction = BLK_NEEDS_REDO;
932 : : }
933 : : else
67 melanieplageman@gmai 934 : 55858 : newaction = XLogReadBufferForRedo(record, HEAP_UPDATE_BLKREF_HEAP_NEW,
935 : : &nbuffer);
936 : :
937 : : /* Deal with new tuple */
738 michael@paquier.xyz 938 [ + + ]: 99058 : if (newaction == BLK_NEEDS_REDO)
939 : : {
940 : : char *recdata;
941 : : char *recdata_end;
942 : : Size datalen;
943 : : Size tuplen;
944 : :
67 melanieplageman@gmai 945 : 98487 : recdata = XLogRecGetBlockData(record, HEAP_UPDATE_BLKREF_HEAP_NEW,
946 : : &datalen);
738 michael@paquier.xyz 947 : 98487 : recdata_end = recdata + datalen;
948 : :
189 melanieplageman@gmai 949 : 98487 : npage = BufferGetPage(nbuffer);
950 : :
738 michael@paquier.xyz 951 : 98487 : offnum = xlrec->new_offnum;
189 melanieplageman@gmai 952 [ - + ]: 98487 : if (PageGetMaxOffsetNumber(npage) + 1 < offnum)
738 michael@paquier.xyz 953 [ # # ]:UBC 0 : elog(PANIC, "invalid max offset number");
954 : :
738 michael@paquier.xyz 955 [ + + ]:CBC 98487 : if (xlrec->flags & XLH_UPDATE_PREFIX_FROM_OLD)
956 : : {
957 [ - + ]: 18682 : Assert(newblk == oldblk);
958 : 18682 : memcpy(&prefixlen, recdata, sizeof(uint16));
959 : 18682 : recdata += sizeof(uint16);
960 : : }
961 [ + + ]: 98487 : if (xlrec->flags & XLH_UPDATE_SUFFIX_FROM_OLD)
962 : : {
963 [ - + ]: 34978 : Assert(newblk == oldblk);
964 : 34978 : memcpy(&suffixlen, recdata, sizeof(uint16));
965 : 34978 : recdata += sizeof(uint16);
966 : : }
967 : :
585 peter@eisentraut.org 968 : 98487 : memcpy(&xlhdr, recdata, SizeOfHeapHeader);
738 michael@paquier.xyz 969 : 98487 : recdata += SizeOfHeapHeader;
970 : :
971 : 98487 : tuplen = recdata_end - recdata;
972 [ - + ]: 98487 : Assert(tuplen <= MaxHeapTupleSize);
973 : :
974 : 98487 : htup = &tbuf.hdr;
585 peter@eisentraut.org 975 [ + - - + : 98487 : MemSet(htup, 0, SizeofHeapTupleHeader);
- - - - -
- ]
976 : :
977 : : /*
978 : : * Reconstruct the new tuple using the prefix and/or suffix from the
979 : : * old tuple, and the data stored in the WAL record.
980 : : */
738 michael@paquier.xyz 981 : 98487 : newp = (char *) htup + SizeofHeapTupleHeader;
982 [ + + ]: 98487 : if (prefixlen > 0)
983 : : {
984 : : int len;
985 : :
986 : : /* copy bitmap [+ padding] [+ oid] from WAL record */
987 : 18682 : len = xlhdr.t_hoff - SizeofHeapTupleHeader;
988 : 18682 : memcpy(newp, recdata, len);
989 : 18682 : recdata += len;
990 : 18682 : newp += len;
991 : :
992 : : /* copy prefix from old tuple */
993 : 18682 : memcpy(newp, (char *) oldtup.t_data + oldtup.t_data->t_hoff, prefixlen);
994 : 18682 : newp += prefixlen;
995 : :
996 : : /* copy new tuple data from WAL record */
997 : 18682 : len = tuplen - (xlhdr.t_hoff - SizeofHeapTupleHeader);
998 : 18682 : memcpy(newp, recdata, len);
999 : 18682 : recdata += len;
1000 : 18682 : newp += len;
1001 : : }
1002 : : else
1003 : : {
1004 : : /*
1005 : : * copy bitmap [+ padding] [+ oid] + data from record, all in one
1006 : : * go
1007 : : */
1008 : 79805 : memcpy(newp, recdata, tuplen);
1009 : 79805 : recdata += tuplen;
1010 : 79805 : newp += tuplen;
1011 : : }
1012 [ - + ]: 98487 : Assert(recdata == recdata_end);
1013 : :
1014 : : /* copy suffix from old tuple */
1015 [ + + ]: 98487 : if (suffixlen > 0)
1016 : 34978 : memcpy(newp, (char *) oldtup.t_data + oldtup.t_len - suffixlen, suffixlen);
1017 : :
1018 : 98487 : newlen = SizeofHeapTupleHeader + tuplen + prefixlen + suffixlen;
1019 : 98487 : htup->t_infomask2 = xlhdr.t_infomask2;
1020 : 98487 : htup->t_infomask = xlhdr.t_infomask;
1021 : 98487 : htup->t_hoff = xlhdr.t_hoff;
1022 : :
1023 : 98487 : HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
1024 : 98487 : HeapTupleHeaderSetCmin(htup, FirstCommandId);
1025 : 98487 : HeapTupleHeaderSetXmax(htup, xlrec->new_xmax);
1026 : : /* Make sure there is no forward chain link in t_ctid */
1027 : 98487 : htup->t_ctid = newtid;
1028 : :
189 melanieplageman@gmai 1029 : 98487 : offnum = PageAddItem(npage, htup, newlen, offnum, true, true);
738 michael@paquier.xyz 1030 [ - + ]: 98487 : if (offnum == InvalidOffsetNumber)
738 michael@paquier.xyz 1031 [ # # ]:UBC 0 : elog(PANIC, "failed to add tuple");
1032 : :
738 michael@paquier.xyz 1033 [ + + ]:CBC 98487 : if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
189 melanieplageman@gmai 1034 : 85 : PageClearAllVisible(npage);
1035 : :
1036 : : /* needed to update FSM below */
1037 : 98487 : freespace = PageGetHeapFreeSpace(npage);
1038 : :
1039 : 98487 : PageSetLSN(npage, lsn);
1040 : : /* See heap_insert() for why we set pd_prune_xid on insert */
174 1041 [ - + + + : 98487 : PageSetPrunable(npage, XLogRecGetXid(record));
+ + ]
738 michael@paquier.xyz 1042 : 98487 : MarkBufferDirty(nbuffer);
1043 : : }
1044 : :
1045 [ + - + + ]: 99058 : if (BufferIsValid(nbuffer) && nbuffer != obuffer)
1046 : 56497 : UnlockReleaseBuffer(nbuffer);
1047 [ + - ]: 99058 : if (BufferIsValid(obuffer))
1048 : 99058 : UnlockReleaseBuffer(obuffer);
1049 : :
1050 : : /*
1051 : : * If the new page is running low on free space, update the FSM as well.
1052 : : * Arbitrarily, our definition of "low" is less than 20%. We can't do much
1053 : : * better than that without knowing the fill-factor for the table.
1054 : : *
1055 : : * However, don't update the FSM on HOT updates, because after crash
1056 : : * recovery, either the old or the new tuple will certainly be dead and
1057 : : * prunable. After pruning, the page will have roughly as much free space
1058 : : * as it did before the update, assuming the new tuple is about the same
1059 : : * size as the old one.
1060 : : *
1061 : : * XXX: Don't do this if the page was restored from full page image. We
1062 : : * don't bother to update the FSM in that case, it doesn't need to be
1063 : : * totally accurate anyway.
1064 : : */
1065 [ + + + + : 99058 : if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < BLCKSZ / 5)
+ + ]
1066 : 12316 : XLogRecordPageWithFreeSpace(rlocator, newblk, freespace);
1067 : 99058 : }
1068 : :
1069 : : /*
1070 : : * Replay XLOG_HEAP_CONFIRM records.
1071 : : */
1072 : : static void
1073 : 98 : heap_xlog_confirm(XLogReaderState *record)
1074 : : {
1075 : 98 : XLogRecPtr lsn = record->EndRecPtr;
1076 : 98 : xl_heap_confirm *xlrec = (xl_heap_confirm *) XLogRecGetData(record);
1077 : : Buffer buffer;
1078 : : Page page;
1079 : : OffsetNumber offnum;
1080 : : ItemId lp;
1081 : : HeapTupleHeader htup;
1082 : :
1083 [ + - ]: 98 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
1084 : : {
1085 : 98 : page = BufferGetPage(buffer);
1086 : :
1087 : 98 : offnum = xlrec->offnum;
279 tgl@sss.pgh.pa.us 1088 [ + - - + ]: 98 : if (offnum < 1 || offnum > PageGetMaxOffsetNumber(page))
279 tgl@sss.pgh.pa.us 1089 [ # # ]:UBC 0 : elog(PANIC, "offnum out of range");
279 tgl@sss.pgh.pa.us 1090 :CBC 98 : lp = PageGetItemId(page, offnum);
1091 [ - + ]: 98 : if (!ItemIdIsNormal(lp))
738 michael@paquier.xyz 1092 [ # # ]:UBC 0 : elog(PANIC, "invalid lp");
1093 : :
738 michael@paquier.xyz 1094 :CBC 98 : htup = (HeapTupleHeader) PageGetItem(page, lp);
1095 : :
1096 : : /*
1097 : : * Confirm tuple as actually inserted
1098 : : */
1099 : 98 : ItemPointerSet(&htup->t_ctid, BufferGetBlockNumber(buffer), offnum);
1100 : :
1101 : 98 : PageSetLSN(page, lsn);
1102 : 98 : MarkBufferDirty(buffer);
1103 : : }
1104 [ + - ]: 98 : if (BufferIsValid(buffer))
1105 : 98 : UnlockReleaseBuffer(buffer);
1106 : 98 : }
1107 : :
1108 : : /*
1109 : : * Replay XLOG_HEAP_LOCK records.
1110 : : */
1111 : : static void
1112 : 58070 : heap_xlog_lock(XLogReaderState *record)
1113 : : {
1114 : 58070 : XLogRecPtr lsn = record->EndRecPtr;
1115 : 58070 : xl_heap_lock *xlrec = (xl_heap_lock *) XLogRecGetData(record);
1116 : : Buffer buffer;
1117 : : Page page;
1118 : : OffsetNumber offnum;
1119 : : ItemId lp;
1120 : : HeapTupleHeader htup;
1121 : :
1122 : : /*
1123 : : * The visibility map may need to be fixed even if the heap page is
1124 : : * already up-to-date.
1125 : : */
1126 [ + + ]: 58070 : if (xlrec->flags & XLH_LOCK_ALL_FROZEN_CLEARED)
1127 : : {
1128 : : RelFileLocator rlocator;
1129 : : BlockNumber block;
1130 : :
67 melanieplageman@gmai 1131 : 32 : XLogRecGetBlockTag(record, HEAP_LOCK_BLKREF_HEAP, &rlocator, NULL,
1132 : : &block);
1133 : :
1134 : 32 : heap_xlog_vm_clear(record, rlocator,
1135 : : block, HEAP_LOCK_BLKREF_VM,
1136 : : VISIBILITYMAP_ALL_FROZEN);
1137 : : }
1138 : :
1139 [ + + ]: 58070 : if (XLogReadBufferForRedo(record, HEAP_LOCK_BLKREF_HEAP,
1140 : : &buffer) == BLK_NEEDS_REDO)
1141 : : {
387 peter@eisentraut.org 1142 : 57803 : page = BufferGetPage(buffer);
1143 : :
738 michael@paquier.xyz 1144 : 57803 : offnum = xlrec->offnum;
279 tgl@sss.pgh.pa.us 1145 [ + - - + ]: 57803 : if (offnum < 1 || offnum > PageGetMaxOffsetNumber(page))
279 tgl@sss.pgh.pa.us 1146 [ # # ]:UBC 0 : elog(PANIC, "offnum out of range");
279 tgl@sss.pgh.pa.us 1147 :CBC 57803 : lp = PageGetItemId(page, offnum);
1148 [ - + ]: 57803 : if (!ItemIdIsNormal(lp))
738 michael@paquier.xyz 1149 [ # # ]:UBC 0 : elog(PANIC, "invalid lp");
1150 : :
738 michael@paquier.xyz 1151 :CBC 57803 : htup = (HeapTupleHeader) PageGetItem(page, lp);
1152 : :
1153 : 57803 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
1154 : 57803 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
1155 : 57803 : fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
1156 : : &htup->t_infomask2);
1157 : :
1158 : : /*
1159 : : * Clear relevant update flags, but only if the modified infomask says
1160 : : * there's no update.
1161 : : */
1162 [ + - ]: 57803 : if (HEAP_XMAX_IS_LOCKED_ONLY(htup->t_infomask))
1163 : : {
1164 : 57803 : HeapTupleHeaderClearHotUpdated(htup);
1165 : : /* Make sure there is no forward chain link in t_ctid */
1166 : 57803 : ItemPointerSet(&htup->t_ctid,
1167 : : BufferGetBlockNumber(buffer),
1168 : : offnum);
1169 : : }
1170 : 57803 : HeapTupleHeaderSetXmax(htup, xlrec->xmax);
1171 : 57803 : HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
1172 : 57803 : PageSetLSN(page, lsn);
1173 : 57803 : MarkBufferDirty(buffer);
1174 : : }
1175 [ + - ]: 58070 : if (BufferIsValid(buffer))
1176 : 58070 : UnlockReleaseBuffer(buffer);
1177 : 58070 : }
1178 : :
1179 : : /*
1180 : : * Replay XLOG_HEAP2_LOCK_UPDATED records.
1181 : : */
1182 : : static void
738 michael@paquier.xyz 1183 :UBC 0 : heap_xlog_lock_updated(XLogReaderState *record)
1184 : : {
1185 : 0 : XLogRecPtr lsn = record->EndRecPtr;
1186 : : xl_heap_lock_updated *xlrec;
1187 : : Buffer buffer;
1188 : : Page page;
1189 : : OffsetNumber offnum;
1190 : : ItemId lp;
1191 : : HeapTupleHeader htup;
1192 : :
1193 : 0 : xlrec = (xl_heap_lock_updated *) XLogRecGetData(record);
1194 : :
1195 : : /*
1196 : : * The visibility map may need to be fixed even if the heap page is
1197 : : * already up-to-date.
1198 : : */
1199 [ # # ]: 0 : if (xlrec->flags & XLH_LOCK_ALL_FROZEN_CLEARED)
1200 : : {
1201 : : RelFileLocator rlocator;
1202 : : BlockNumber block;
1203 : :
67 melanieplageman@gmai 1204 : 0 : XLogRecGetBlockTag(record, HEAP_LOCK_BLKREF_HEAP, &rlocator, NULL,
1205 : : &block);
1206 : :
1207 : 0 : heap_xlog_vm_clear(record, rlocator,
1208 : : block, HEAP_LOCK_BLKREF_VM,
1209 : : VISIBILITYMAP_ALL_FROZEN);
1210 : : }
1211 : :
1212 [ # # ]: 0 : if (XLogReadBufferForRedo(record, HEAP_LOCK_BLKREF_HEAP,
1213 : : &buffer) == BLK_NEEDS_REDO)
1214 : : {
738 michael@paquier.xyz 1215 : 0 : page = BufferGetPage(buffer);
1216 : :
1217 : 0 : offnum = xlrec->offnum;
279 tgl@sss.pgh.pa.us 1218 [ # # # # ]: 0 : if (offnum < 1 || offnum > PageGetMaxOffsetNumber(page))
1219 [ # # ]: 0 : elog(PANIC, "offnum out of range");
1220 : 0 : lp = PageGetItemId(page, offnum);
1221 [ # # ]: 0 : if (!ItemIdIsNormal(lp))
738 michael@paquier.xyz 1222 [ # # ]: 0 : elog(PANIC, "invalid lp");
1223 : :
1224 : 0 : htup = (HeapTupleHeader) PageGetItem(page, lp);
1225 : :
1226 : 0 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
1227 : 0 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
1228 : 0 : fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
1229 : : &htup->t_infomask2);
1230 : 0 : HeapTupleHeaderSetXmax(htup, xlrec->xmax);
1231 : :
1232 : 0 : PageSetLSN(page, lsn);
1233 : 0 : MarkBufferDirty(buffer);
1234 : : }
1235 [ # # ]: 0 : if (BufferIsValid(buffer))
1236 : 0 : UnlockReleaseBuffer(buffer);
1237 : 0 : }
1238 : :
1239 : : /*
1240 : : * Replay XLOG_HEAP_INPLACE records.
1241 : : */
1242 : : static void
738 michael@paquier.xyz 1243 :CBC 8339 : heap_xlog_inplace(XLogReaderState *record)
1244 : : {
1245 : 8339 : XLogRecPtr lsn = record->EndRecPtr;
1246 : 8339 : xl_heap_inplace *xlrec = (xl_heap_inplace *) XLogRecGetData(record);
1247 : : Buffer buffer;
1248 : : Page page;
1249 : : OffsetNumber offnum;
1250 : : ItemId lp;
1251 : : HeapTupleHeader htup;
1252 : : uint32 oldlen;
1253 : : Size newlen;
1254 : :
1255 [ + + ]: 8339 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
1256 : : {
1257 : 8079 : char *newtup = XLogRecGetBlockData(record, 0, &newlen);
1258 : :
1259 : 8079 : page = BufferGetPage(buffer);
1260 : :
1261 : 8079 : offnum = xlrec->offnum;
279 tgl@sss.pgh.pa.us 1262 [ + - - + ]: 8079 : if (offnum < 1 || offnum > PageGetMaxOffsetNumber(page))
279 tgl@sss.pgh.pa.us 1263 [ # # ]:UBC 0 : elog(PANIC, "offnum out of range");
279 tgl@sss.pgh.pa.us 1264 :CBC 8079 : lp = PageGetItemId(page, offnum);
1265 [ - + ]: 8079 : if (!ItemIdIsNormal(lp))
738 michael@paquier.xyz 1266 [ # # ]:UBC 0 : elog(PANIC, "invalid lp");
1267 : :
738 michael@paquier.xyz 1268 :CBC 8079 : htup = (HeapTupleHeader) PageGetItem(page, lp);
1269 : :
1270 : 8079 : oldlen = ItemIdGetLength(lp) - htup->t_hoff;
1271 [ - + ]: 8079 : if (oldlen != newlen)
738 michael@paquier.xyz 1272 [ # # ]:UBC 0 : elog(PANIC, "wrong tuple length");
1273 : :
738 michael@paquier.xyz 1274 :CBC 8079 : memcpy((char *) htup + htup->t_hoff, newtup, newlen);
1275 : :
1276 : 8079 : PageSetLSN(page, lsn);
1277 : 8079 : MarkBufferDirty(buffer);
1278 : : }
1279 [ + - ]: 8339 : if (BufferIsValid(buffer))
1280 : 8339 : UnlockReleaseBuffer(buffer);
1281 : :
695 noah@leadboat.com 1282 : 8339 : ProcessCommittedInvalidationMessages(xlrec->msgs,
1283 : : xlrec->nmsgs,
1284 : 8339 : xlrec->relcacheInitFileInval,
1285 : : xlrec->dbId,
1286 : : xlrec->tsId);
738 michael@paquier.xyz 1287 : 8339 : }
1288 : :
1289 : : void
1290 : 1805688 : heap_redo(XLogReaderState *record)
1291 : : {
1292 : 1805688 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1293 : :
1294 : : /*
1295 : : * These operations don't overwrite MVCC data so no conflict processing is
1296 : : * required. The ones in heap2 rmgr do.
1297 : : */
1298 : :
1299 [ + + + + : 1805688 : switch (info & XLOG_HEAP_OPMASK)
+ + + +
- ]
1300 : : {
1301 : 1327763 : case XLOG_HEAP_INSERT:
1302 : 1327763 : heap_xlog_insert(record);
1303 : 1327763 : break;
1304 : 312358 : case XLOG_HEAP_DELETE:
1305 : 312358 : heap_xlog_delete(record);
1306 : 312358 : break;
1307 : 59571 : case XLOG_HEAP_UPDATE:
1308 : 59571 : heap_xlog_update(record, false);
1309 : 59571 : break;
1310 : 2 : case XLOG_HEAP_TRUNCATE:
1311 : :
1312 : : /*
1313 : : * TRUNCATE is a no-op because the actions are already logged as
1314 : : * SMGR WAL records. TRUNCATE WAL record only exists for logical
1315 : : * decoding.
1316 : : */
1317 : 2 : break;
1318 : 39487 : case XLOG_HEAP_HOT_UPDATE:
1319 : 39487 : heap_xlog_update(record, true);
1320 : 39487 : break;
1321 : 98 : case XLOG_HEAP_CONFIRM:
1322 : 98 : heap_xlog_confirm(record);
1323 : 98 : break;
1324 : 58070 : case XLOG_HEAP_LOCK:
1325 : 58070 : heap_xlog_lock(record);
1326 : 58070 : break;
1327 : 8339 : case XLOG_HEAP_INPLACE:
1328 : 8339 : heap_xlog_inplace(record);
1329 : 8339 : break;
738 michael@paquier.xyz 1330 :UBC 0 : default:
1331 [ # # ]: 0 : elog(PANIC, "heap_redo: unknown op code %u", info);
1332 : : }
738 michael@paquier.xyz 1333 :CBC 1805688 : }
1334 : :
1335 : : void
1336 : 85695 : heap2_redo(XLogReaderState *record)
1337 : : {
1338 : 85695 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1339 : :
1340 [ + + - + : 85695 : switch (info & XLOG_HEAP_OPMASK)
- - ]
1341 : : {
1342 : 21118 : case XLOG_HEAP2_PRUNE_ON_ACCESS:
1343 : : case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
1344 : : case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
1345 : 21118 : heap_xlog_prune_freeze(record);
1346 : 21118 : break;
1347 : 63429 : case XLOG_HEAP2_MULTI_INSERT:
1348 : 63429 : heap_xlog_multi_insert(record);
1349 : 63429 : break;
738 michael@paquier.xyz 1350 :UBC 0 : case XLOG_HEAP2_LOCK_UPDATED:
1351 : 0 : heap_xlog_lock_updated(record);
1352 : 0 : break;
738 michael@paquier.xyz 1353 :CBC 1148 : case XLOG_HEAP2_NEW_CID:
1354 : :
1355 : : /*
1356 : : * Nothing to do on a real replay, only used during logical
1357 : : * decoding.
1358 : : */
1359 : 1148 : break;
738 michael@paquier.xyz 1360 :UBC 0 : case XLOG_HEAP2_REWRITE:
1361 : 0 : heap_xlog_logical_rewrite(record);
1362 : 0 : break;
1363 : 0 : default:
1364 [ # # ]: 0 : elog(PANIC, "heap2_redo: unknown op code %u", info);
1365 : : }
738 michael@paquier.xyz 1366 :CBC 85695 : }
1367 : :
1368 : : /*
1369 : : * Mask a heap page before performing consistency checks on it.
1370 : : */
1371 : : void
1372 : 3000764 : heap_mask(char *pagedata, BlockNumber blkno)
1373 : : {
1374 : 3000764 : Page page = (Page) pagedata;
1375 : : OffsetNumber off;
1376 : :
1377 : 3000764 : mask_page_lsn_and_checksum(page);
1378 : :
1379 : 3000764 : mask_page_hint_bits(page);
1380 : 3000764 : mask_unused_space(page);
1381 : :
1382 [ + + ]: 247463788 : for (off = 1; off <= PageGetMaxOffsetNumber(page); off++)
1383 : : {
1384 : 244463024 : ItemId iid = PageGetItemId(page, off);
1385 : : char *page_item;
1386 : :
1387 : 244463024 : page_item = (char *) (page + ItemIdGetOffset(iid));
1388 : :
1389 [ + + ]: 244463024 : if (ItemIdIsNormal(iid))
1390 : : {
1391 : 228137230 : HeapTupleHeader page_htup = (HeapTupleHeader) page_item;
1392 : :
1393 : : /*
1394 : : * If xmin of a tuple is not yet frozen, we should ignore
1395 : : * differences in hint bits, since they can be set without
1396 : : * emitting WAL.
1397 : : */
1398 [ + + ]: 228137230 : if (!HeapTupleHeaderXminFrozen(page_htup))
1399 : 224195198 : page_htup->t_infomask &= ~HEAP_XACT_MASK;
1400 : : else
1401 : : {
1402 : : /* Still we need to mask xmax hint bits. */
1403 : 3942032 : page_htup->t_infomask &= ~HEAP_XMAX_INVALID;
1404 : 3942032 : page_htup->t_infomask &= ~HEAP_XMAX_COMMITTED;
1405 : : }
1406 : :
1407 : : /*
1408 : : * During replay, we set Command Id to FirstCommandId. Hence, mask
1409 : : * it. See heap_xlog_insert() for details.
1410 : : */
1411 : 228137230 : page_htup->t_choice.t_heap.t_field3.t_cid = MASK_MARKER;
1412 : :
1413 : : /*
1414 : : * For a speculative tuple, heap_insert() does not set ctid in the
1415 : : * caller-passed heap tuple itself, leaving the ctid field to
1416 : : * contain a speculative token value - a per-backend monotonically
1417 : : * increasing identifier. Besides, it does not WAL-log ctid under
1418 : : * any circumstances.
1419 : : *
1420 : : * During redo, heap_xlog_insert() sets t_ctid to current block
1421 : : * number and self offset number. It doesn't care about any
1422 : : * speculative insertions on the primary. Hence, we set t_ctid to
1423 : : * current block number and self offset number to ignore any
1424 : : * inconsistency.
1425 : : */
1426 [ + + ]: 228137230 : if (HeapTupleHeaderIsSpeculative(page_htup))
1427 : 115 : ItemPointerSet(&page_htup->t_ctid, blkno, off);
1428 : :
1429 : : /*
1430 : : * NB: Not ignoring ctid changes due to the tuple having moved
1431 : : * (i.e. HeapTupleHeaderIndicatesMovedPartitions), because that's
1432 : : * important information that needs to be in-sync between primary
1433 : : * and standby, and thus is WAL logged.
1434 : : */
1435 : : }
1436 : :
1437 : : /*
1438 : : * Ignore any padding bytes after the tuple, when the length of the
1439 : : * item is not MAXALIGNed.
1440 : : */
1441 [ + + ]: 244463024 : if (ItemIdHasStorage(iid))
1442 : : {
1443 : 228137230 : int len = ItemIdGetLength(iid);
1444 : 228137230 : int padlen = MAXALIGN(len) - len;
1445 : :
1446 [ + + ]: 228137230 : if (padlen > 0)
1447 : 122707046 : memset(page_item + len, MASK_MARKER, padlen);
1448 : : }
1449 : : }
1450 : 3000764 : }
|