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