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