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