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
30 25280 : heap_xlog_prune_freeze(XLogReaderState *record)
31 : {
32 25280 : XLogRecPtr lsn = record->EndRecPtr;
33 25280 : char *maindataptr = XLogRecGetData(record);
34 : xl_heap_prune xlrec;
35 : Buffer buffer;
36 : RelFileLocator rlocator;
37 : BlockNumber blkno;
38 : XLogRedoAction action;
39 :
40 25280 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
41 25280 : memcpy(&xlrec, maindataptr, SizeOfHeapPrune);
42 25280 : 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 : 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 25280 : 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 19010 : memcpy(&snapshot_conflict_horizon, maindataptr, sizeof(TransactionId));
65 19010 : maindataptr += sizeof(TransactionId);
66 :
67 19010 : if (InHotStandby)
68 18532 : ResolveRecoveryConflictWithSnapshot(snapshot_conflict_horizon,
69 18532 : (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 25280 : action = XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL,
77 25280 : (xlrec.flags & XLHP_CLEANUP_LOCK) != 0,
78 : &buffer);
79 25280 : if (action == BLK_NEEDS_REDO)
80 : {
81 17308 : Page 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;
92 17308 : char *dataptr = XLogRecGetBlockData(record, 0, &datalen);
93 :
94 17308 : 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 17308 : if (nredirected > 0 || ndead > 0 || nunused > 0)
105 16252 : heap_page_prune_execute(buffer,
106 16252 : (xlrec.flags & XLHP_CLEANUP_LOCK) == 0,
107 : redirected, nredirected,
108 : nowdead, ndead,
109 : nowunused, nunused);
110 :
111 : /* Freeze tuples */
112 19328 : 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 2020 : frz.xmax = plans[p].xmax;
121 2020 : frz.t_infomask2 = plans[p].t_infomask2;
122 2020 : frz.t_infomask = plans[p].t_infomask;
123 2020 : frz.frzflags = plans[p].frzflags;
124 2020 : frz.offset = InvalidOffsetNumber; /* unused, but be tidy */
125 :
126 97902 : for (int i = 0; i < plans[p].ntuples; i++)
127 : {
128 95882 : OffsetNumber offset = *(frz_offsets++);
129 : ItemId lp;
130 : HeapTupleHeader tuple;
131 :
132 95882 : lp = PageGetItemId(page, offset);
133 95882 : tuple = (HeapTupleHeader) PageGetItem(page, lp);
134 95882 : heap_execute_freeze_tuple(tuple, &frz);
135 : }
136 : }
137 :
138 : /* There should be no more data */
139 : 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 17308 : PageSetLSN(page, lsn);
147 17308 : 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 25280 : if (BufferIsValid(buffer))
157 : {
158 25280 : if (xlrec.flags & (XLHP_HAS_REDIRECTIONS |
159 : XLHP_HAS_DEAD_ITEMS |
160 : XLHP_HAS_NOW_UNUSED_ITEMS))
161 : {
162 21672 : Size freespace = PageGetHeapFreeSpace(BufferGetPage(buffer));
163 :
164 21672 : UnlockReleaseBuffer(buffer);
165 :
166 21672 : XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
167 : }
168 : else
169 3608 : UnlockReleaseBuffer(buffer);
170 : }
171 25280 : }
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 14610 : heap_xlog_visible(XLogReaderState *record)
183 : {
184 14610 : XLogRecPtr lsn = record->EndRecPtr;
185 14610 : xl_heap_visible *xlrec = (xl_heap_visible *) XLogRecGetData(record);
186 14610 : Buffer vmbuffer = InvalidBuffer;
187 : Buffer buffer;
188 : Page page;
189 : RelFileLocator rlocator;
190 : BlockNumber blkno;
191 : XLogRedoAction action;
192 :
193 : Assert((xlrec->flags & VISIBILITYMAP_XLOG_VALID_BITS) == xlrec->flags);
194 :
195 14610 : 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 14610 : if (InHotStandby)
207 14262 : ResolveRecoveryConflictWithSnapshot(xlrec->snapshotConflictHorizon,
208 14262 : 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 14610 : action = XLogReadBufferForRedo(record, 1, &buffer);
217 14610 : 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 10950 : page = BufferGetPage(buffer);
227 :
228 10950 : PageSetAllVisible(page);
229 :
230 10950 : if (XLogHintBitIsNeeded())
231 10950 : PageSetLSN(page, lsn);
232 :
233 10950 : 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 14610 : if (BufferIsValid(buffer))
245 : {
246 14610 : Size space = PageGetFreeSpace(BufferGetPage(buffer));
247 :
248 14610 : 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 14610 : if (xlrec->flags & VISIBILITYMAP_VALID_BITS)
268 14610 : 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 14610 : if (XLogReadBufferForRedoExtended(record, 0, RBM_ZERO_ON_ERROR, false,
278 : &vmbuffer) == BLK_NEEDS_REDO)
279 : {
280 14072 : Page vmpage = BufferGetPage(vmbuffer);
281 : Relation reln;
282 : uint8 vmbits;
283 :
284 : /* initialize the page if it was read as zeros */
285 14072 : if (PageIsNew(vmpage))
286 0 : PageInit(vmpage, BLCKSZ, 0);
287 :
288 : /* remove VISIBILITYMAP_XLOG_* */
289 14072 : vmbits = xlrec->flags & VISIBILITYMAP_VALID_BITS;
290 :
291 : /*
292 : * XLogReadBufferForRedoExtended locked the buffer. But
293 : * visibilitymap_set will handle locking itself.
294 : */
295 14072 : LockBuffer(vmbuffer, BUFFER_LOCK_UNLOCK);
296 :
297 14072 : reln = CreateFakeRelcacheEntry(rlocator);
298 14072 : visibilitymap_pin(reln, blkno, &vmbuffer);
299 :
300 14072 : visibilitymap_set(reln, blkno, InvalidBuffer, lsn, vmbuffer,
301 : xlrec->snapshotConflictHorizon, vmbits);
302 :
303 14072 : ReleaseBuffer(vmbuffer);
304 14072 : FreeFakeRelcacheEntry(reln);
305 : }
306 538 : else if (BufferIsValid(vmbuffer))
307 538 : UnlockReleaseBuffer(vmbuffer);
308 14610 : }
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 889968 : fix_infomask_from_infobits(uint8 infobits, uint16 *infomask, uint16 *infomask2)
318 : {
319 889968 : *infomask &= ~(HEAP_XMAX_IS_MULTI | HEAP_XMAX_LOCK_ONLY |
320 : HEAP_XMAX_KEYSHR_LOCK | HEAP_XMAX_EXCL_LOCK);
321 889968 : *infomask2 &= ~HEAP_KEYS_UPDATED;
322 :
323 889968 : if (infobits & XLHL_XMAX_IS_MULTI)
324 4 : *infomask |= HEAP_XMAX_IS_MULTI;
325 889968 : if (infobits & XLHL_XMAX_LOCK_ONLY)
326 110004 : *infomask |= HEAP_XMAX_LOCK_ONLY;
327 889968 : if (infobits & XLHL_XMAX_EXCL_LOCK)
328 109206 : *infomask |= HEAP_XMAX_EXCL_LOCK;
329 : /* note HEAP_XMAX_SHR_LOCK isn't considered here */
330 889968 : if (infobits & XLHL_XMAX_KEYSHR_LOCK)
331 820 : *infomask |= HEAP_XMAX_KEYSHR_LOCK;
332 :
333 889968 : if (infobits & XLHL_KEYS_UPDATED)
334 596754 : *infomask2 |= HEAP_KEYS_UPDATED;
335 889968 : }
336 :
337 : /*
338 : * Replay XLOG_HEAP_DELETE records.
339 : */
340 : static void
341 597944 : heap_xlog_delete(XLogReaderState *record)
342 : {
343 597944 : XLogRecPtr lsn = record->EndRecPtr;
344 597944 : xl_heap_delete *xlrec = (xl_heap_delete *) XLogRecGetData(record);
345 : Buffer buffer;
346 : Page page;
347 597944 : ItemId lp = NULL;
348 : HeapTupleHeader htup;
349 : BlockNumber blkno;
350 : RelFileLocator target_locator;
351 : ItemPointerData target_tid;
352 :
353 597944 : XLogRecGetBlockTag(record, 0, &target_locator, NULL, &blkno);
354 597944 : ItemPointerSetBlockNumber(&target_tid, blkno);
355 597944 : 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 597944 : if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
362 : {
363 52 : Relation reln = CreateFakeRelcacheEntry(target_locator);
364 52 : Buffer vmbuffer = InvalidBuffer;
365 :
366 52 : visibilitymap_pin(reln, blkno, &vmbuffer);
367 52 : visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
368 52 : ReleaseBuffer(vmbuffer);
369 52 : FreeFakeRelcacheEntry(reln);
370 : }
371 :
372 597944 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
373 : {
374 593988 : page = BufferGetPage(buffer);
375 :
376 593988 : if (PageGetMaxOffsetNumber(page) >= xlrec->offnum)
377 593988 : lp = PageGetItemId(page, xlrec->offnum);
378 :
379 593988 : if (PageGetMaxOffsetNumber(page) < xlrec->offnum || !ItemIdIsNormal(lp))
380 0 : elog(PANIC, "invalid lp");
381 :
382 593988 : htup = (HeapTupleHeader) PageGetItem(page, lp);
383 :
384 593988 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
385 593988 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
386 593988 : HeapTupleHeaderClearHotUpdated(htup);
387 593988 : fix_infomask_from_infobits(xlrec->infobits_set,
388 : &htup->t_infomask, &htup->t_infomask2);
389 593988 : if (!(xlrec->flags & XLH_DELETE_IS_SUPER))
390 593988 : HeapTupleHeaderSetXmax(htup, xlrec->xmax);
391 : else
392 0 : HeapTupleHeaderSetXmin(htup, InvalidTransactionId);
393 593988 : HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
394 :
395 : /* Mark the page as a candidate for pruning */
396 593988 : PageSetPrunable(page, XLogRecGetXid(record));
397 :
398 593988 : if (xlrec->flags & XLH_DELETE_ALL_VISIBLE_CLEARED)
399 28 : PageClearAllVisible(page);
400 :
401 : /* Make sure t_ctid is set correctly */
402 593988 : if (xlrec->flags & XLH_DELETE_IS_PARTITION_MOVE)
403 288 : HeapTupleHeaderSetMovedPartitions(htup);
404 : else
405 593700 : htup->t_ctid = target_tid;
406 593988 : PageSetLSN(page, lsn);
407 593988 : MarkBufferDirty(buffer);
408 : }
409 597944 : if (BufferIsValid(buffer))
410 597944 : UnlockReleaseBuffer(buffer);
411 597944 : }
412 :
413 : /*
414 : * Replay XLOG_HEAP_INSERT records.
415 : */
416 : static void
417 2562502 : heap_xlog_insert(XLogReaderState *record)
418 : {
419 2562502 : XLogRecPtr lsn = record->EndRecPtr;
420 2562502 : 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 2562502 : Size freespace = 0;
432 : RelFileLocator target_locator;
433 : BlockNumber blkno;
434 : ItemPointerData target_tid;
435 : XLogRedoAction action;
436 :
437 2562502 : XLogRecGetBlockTag(record, 0, &target_locator, NULL, &blkno);
438 2562502 : ItemPointerSetBlockNumber(&target_tid, blkno);
439 2562502 : ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);
440 :
441 : /* No freezing in the heap_insert() code path */
442 : 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 : */
448 2562502 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
449 : {
450 1730 : Relation reln = CreateFakeRelcacheEntry(target_locator);
451 1730 : Buffer vmbuffer = InvalidBuffer;
452 :
453 1730 : visibilitymap_pin(reln, blkno, &vmbuffer);
454 1730 : visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
455 1730 : ReleaseBuffer(vmbuffer);
456 1730 : 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 2562502 : if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
464 : {
465 34156 : buffer = XLogInitBufferForRedo(record, 0);
466 34156 : page = BufferGetPage(buffer);
467 34156 : PageInit(page, BufferGetPageSize(buffer), 0);
468 34156 : action = BLK_NEEDS_REDO;
469 : }
470 : else
471 2528346 : action = XLogReadBufferForRedo(record, 0, &buffer);
472 2562502 : if (action == BLK_NEEDS_REDO)
473 : {
474 : Size datalen;
475 : char *data;
476 :
477 2557134 : page = BufferGetPage(buffer);
478 :
479 2557134 : if (PageGetMaxOffsetNumber(page) + 1 < xlrec->offnum)
480 0 : elog(PANIC, "invalid max offset number");
481 :
482 2557134 : data = XLogRecGetBlockData(record, 0, &datalen);
483 :
484 2557134 : newlen = datalen - SizeOfHeapHeader;
485 : Assert(datalen > SizeOfHeapHeader && newlen <= MaxHeapTupleSize);
486 2557134 : memcpy(&xlhdr, data, SizeOfHeapHeader);
487 2557134 : data += SizeOfHeapHeader;
488 :
489 2557134 : htup = &tbuf.hdr;
490 2557134 : MemSet(htup, 0, SizeofHeapTupleHeader);
491 : /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
492 2557134 : memcpy((char *) htup + SizeofHeapTupleHeader,
493 : data,
494 : newlen);
495 2557134 : newlen += SizeofHeapTupleHeader;
496 2557134 : htup->t_infomask2 = xlhdr.t_infomask2;
497 2557134 : htup->t_infomask = xlhdr.t_infomask;
498 2557134 : htup->t_hoff = xlhdr.t_hoff;
499 2557134 : HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
500 2557134 : HeapTupleHeaderSetCmin(htup, FirstCommandId);
501 2557134 : htup->t_ctid = target_tid;
502 :
503 2557134 : if (PageAddItem(page, (Item) htup, newlen, xlrec->offnum,
504 : true, true) == InvalidOffsetNumber)
505 0 : elog(PANIC, "failed to add tuple");
506 :
507 2557134 : freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
508 :
509 2557134 : PageSetLSN(page, lsn);
510 :
511 2557134 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
512 524 : PageClearAllVisible(page);
513 :
514 2557134 : MarkBufferDirty(buffer);
515 : }
516 2562502 : if (BufferIsValid(buffer))
517 2562502 : 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 2562502 : if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
529 503512 : XLogRecordPageWithFreeSpace(target_locator, blkno, freespace);
530 2562502 : }
531 :
532 : /*
533 : * Replay XLOG_HEAP2_MULTI_INSERT records.
534 : */
535 : static void
536 113982 : heap_xlog_multi_insert(XLogReaderState *record)
537 : {
538 113982 : 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 113982 : Size freespace = 0;
552 : int i;
553 113982 : 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 113982 : xlrec = (xl_heap_multi_insert *) XLogRecGetData(record);
561 :
562 113982 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, &blkno);
563 :
564 : /* check that the mutually exclusive flags are not both set */
565 : 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 113982 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
573 : {
574 1868 : Relation reln = CreateFakeRelcacheEntry(rlocator);
575 1868 : Buffer vmbuffer = InvalidBuffer;
576 :
577 1868 : visibilitymap_pin(reln, blkno, &vmbuffer);
578 1868 : visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
579 1868 : ReleaseBuffer(vmbuffer);
580 1868 : FreeFakeRelcacheEntry(reln);
581 : }
582 :
583 113982 : if (isinit)
584 : {
585 3580 : buffer = XLogInitBufferForRedo(record, 0);
586 3580 : page = BufferGetPage(buffer);
587 3580 : PageInit(page, BufferGetPageSize(buffer), 0);
588 3580 : action = BLK_NEEDS_REDO;
589 : }
590 : else
591 110402 : action = XLogReadBufferForRedo(record, 0, &buffer);
592 113982 : if (action == BLK_NEEDS_REDO)
593 : {
594 : char *tupdata;
595 : char *endptr;
596 : Size len;
597 :
598 : /* Tuples are stored as block data */
599 110890 : tupdata = XLogRecGetBlockData(record, 0, &len);
600 110890 : endptr = tupdata + len;
601 :
602 110890 : page = (Page) BufferGetPage(buffer);
603 :
604 521328 : 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 410438 : if (isinit)
615 198590 : offnum = FirstOffsetNumber + i;
616 : else
617 211848 : offnum = xlrec->offsets[i];
618 410438 : if (PageGetMaxOffsetNumber(page) + 1 < offnum)
619 0 : elog(PANIC, "invalid max offset number");
620 :
621 410438 : xlhdr = (xl_multi_insert_tuple *) SHORTALIGN(tupdata);
622 410438 : tupdata = ((char *) xlhdr) + SizeOfMultiInsertTuple;
623 :
624 410438 : newlen = xlhdr->datalen;
625 : Assert(newlen <= MaxHeapTupleSize);
626 410438 : htup = &tbuf.hdr;
627 410438 : MemSet(htup, 0, SizeofHeapTupleHeader);
628 : /* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
629 410438 : memcpy((char *) htup + SizeofHeapTupleHeader,
630 : tupdata,
631 : newlen);
632 410438 : tupdata += newlen;
633 :
634 410438 : newlen += SizeofHeapTupleHeader;
635 410438 : htup->t_infomask2 = xlhdr->t_infomask2;
636 410438 : htup->t_infomask = xlhdr->t_infomask;
637 410438 : htup->t_hoff = xlhdr->t_hoff;
638 410438 : HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
639 410438 : HeapTupleHeaderSetCmin(htup, FirstCommandId);
640 410438 : ItemPointerSetBlockNumber(&htup->t_ctid, blkno);
641 410438 : ItemPointerSetOffsetNumber(&htup->t_ctid, offnum);
642 :
643 410438 : offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
644 410438 : if (offnum == InvalidOffsetNumber)
645 0 : elog(PANIC, "failed to add tuple");
646 : }
647 110890 : if (tupdata != endptr)
648 0 : elog(PANIC, "total tuple length mismatch");
649 :
650 110890 : freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
651 :
652 110890 : PageSetLSN(page, lsn);
653 :
654 110890 : if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
655 154 : PageClearAllVisible(page);
656 :
657 : /* XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible */
658 110890 : if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
659 8 : PageSetAllVisible(page);
660 :
661 110890 : MarkBufferDirty(buffer);
662 : }
663 113982 : if (BufferIsValid(buffer))
664 113982 : 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 113982 : if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
676 31476 : XLogRecordPageWithFreeSpace(rlocator, blkno, freespace);
677 113982 : }
678 :
679 : /*
680 : * Replay XLOG_HEAP_UPDATE and XLOG_HEAP_HOT_UPDATE records.
681 : */
682 : static void
683 186552 : heap_xlog_update(XLogReaderState *record, bool hot_update)
684 : {
685 186552 : XLogRecPtr lsn = record->EndRecPtr;
686 186552 : 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 186552 : ItemId lp = NULL;
696 : HeapTupleData oldtup;
697 : HeapTupleHeader htup;
698 186552 : uint16 prefixlen = 0,
699 186552 : 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 186552 : Size freespace = 0;
709 : XLogRedoAction oldaction;
710 : XLogRedoAction newaction;
711 :
712 : /* initialize to keep the compiler quiet */
713 186552 : oldtup.t_data = NULL;
714 186552 : oldtup.t_len = 0;
715 :
716 186552 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, &newblk);
717 186552 : if (XLogRecGetBlockTagExtended(record, 1, NULL, NULL, &oldblk, NULL))
718 : {
719 : /* HOT updates are never done across pages */
720 : Assert(!hot_update);
721 : }
722 : else
723 77916 : oldblk = newblk;
724 :
725 186552 : 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 186552 : if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
732 : {
733 416 : Relation reln = CreateFakeRelcacheEntry(rlocator);
734 416 : Buffer vmbuffer = InvalidBuffer;
735 :
736 416 : visibilitymap_pin(reln, oldblk, &vmbuffer);
737 416 : visibilitymap_clear(reln, oldblk, vmbuffer, VISIBILITYMAP_VALID_BITS);
738 416 : ReleaseBuffer(vmbuffer);
739 416 : 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 186552 : oldaction = XLogReadBufferForRedo(record, (oldblk == newblk) ? 0 : 1,
754 : &obuffer);
755 186552 : if (oldaction == BLK_NEEDS_REDO)
756 : {
757 185976 : page = BufferGetPage(obuffer);
758 185976 : offnum = xlrec->old_offnum;
759 185976 : if (PageGetMaxOffsetNumber(page) >= offnum)
760 185976 : lp = PageGetItemId(page, offnum);
761 :
762 185976 : if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
763 0 : elog(PANIC, "invalid lp");
764 :
765 185976 : htup = (HeapTupleHeader) PageGetItem(page, lp);
766 :
767 185976 : oldtup.t_data = htup;
768 185976 : oldtup.t_len = ItemIdGetLength(lp);
769 :
770 185976 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
771 185976 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
772 185976 : if (hot_update)
773 71788 : HeapTupleHeaderSetHotUpdated(htup);
774 : else
775 114188 : HeapTupleHeaderClearHotUpdated(htup);
776 185976 : fix_infomask_from_infobits(xlrec->old_infobits_set, &htup->t_infomask,
777 : &htup->t_infomask2);
778 185976 : HeapTupleHeaderSetXmax(htup, xlrec->old_xmax);
779 185976 : HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
780 : /* Set forward chain link in t_ctid */
781 185976 : htup->t_ctid = newtid;
782 :
783 : /* Mark the page as a candidate for pruning */
784 185976 : PageSetPrunable(page, XLogRecGetXid(record));
785 :
786 185976 : if (xlrec->flags & XLH_UPDATE_OLD_ALL_VISIBLE_CLEARED)
787 394 : PageClearAllVisible(page);
788 :
789 185976 : PageSetLSN(page, lsn);
790 185976 : MarkBufferDirty(obuffer);
791 : }
792 :
793 : /*
794 : * Read the page the new tuple goes into, if different from old.
795 : */
796 186552 : if (oldblk == newblk)
797 : {
798 77916 : nbuffer = obuffer;
799 77916 : newaction = oldaction;
800 : }
801 108636 : else if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
802 : {
803 1188 : nbuffer = XLogInitBufferForRedo(record, 0);
804 1188 : page = (Page) BufferGetPage(nbuffer);
805 1188 : PageInit(page, BufferGetPageSize(nbuffer), 0);
806 1188 : newaction = BLK_NEEDS_REDO;
807 : }
808 : else
809 107448 : 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 186552 : if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
816 : {
817 330 : Relation reln = CreateFakeRelcacheEntry(rlocator);
818 330 : Buffer vmbuffer = InvalidBuffer;
819 :
820 330 : visibilitymap_pin(reln, newblk, &vmbuffer);
821 330 : visibilitymap_clear(reln, newblk, vmbuffer, VISIBILITYMAP_VALID_BITS);
822 330 : ReleaseBuffer(vmbuffer);
823 330 : FreeFakeRelcacheEntry(reln);
824 : }
825 :
826 : /* Deal with new tuple */
827 186552 : if (newaction == BLK_NEEDS_REDO)
828 : {
829 : char *recdata;
830 : char *recdata_end;
831 : Size datalen;
832 : Size tuplen;
833 :
834 185548 : recdata = XLogRecGetBlockData(record, 0, &datalen);
835 185548 : recdata_end = recdata + datalen;
836 :
837 185548 : page = BufferGetPage(nbuffer);
838 :
839 185548 : offnum = xlrec->new_offnum;
840 185548 : if (PageGetMaxOffsetNumber(page) + 1 < offnum)
841 0 : elog(PANIC, "invalid max offset number");
842 :
843 185548 : if (xlrec->flags & XLH_UPDATE_PREFIX_FROM_OLD)
844 : {
845 : Assert(newblk == oldblk);
846 30148 : memcpy(&prefixlen, recdata, sizeof(uint16));
847 30148 : recdata += sizeof(uint16);
848 : }
849 185548 : if (xlrec->flags & XLH_UPDATE_SUFFIX_FROM_OLD)
850 : {
851 : Assert(newblk == oldblk);
852 67116 : memcpy(&suffixlen, recdata, sizeof(uint16));
853 67116 : recdata += sizeof(uint16);
854 : }
855 :
856 185548 : memcpy(&xlhdr, recdata, SizeOfHeapHeader);
857 185548 : recdata += SizeOfHeapHeader;
858 :
859 185548 : tuplen = recdata_end - recdata;
860 : Assert(tuplen <= MaxHeapTupleSize);
861 :
862 185548 : htup = &tbuf.hdr;
863 185548 : 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 : */
869 185548 : newp = (char *) htup + SizeofHeapTupleHeader;
870 185548 : if (prefixlen > 0)
871 : {
872 : int len;
873 :
874 : /* copy bitmap [+ padding] [+ oid] from WAL record */
875 30148 : len = xlhdr.t_hoff - SizeofHeapTupleHeader;
876 30148 : memcpy(newp, recdata, len);
877 30148 : recdata += len;
878 30148 : newp += len;
879 :
880 : /* copy prefix from old tuple */
881 30148 : memcpy(newp, (char *) oldtup.t_data + oldtup.t_data->t_hoff, prefixlen);
882 30148 : newp += prefixlen;
883 :
884 : /* copy new tuple data from WAL record */
885 30148 : len = tuplen - (xlhdr.t_hoff - SizeofHeapTupleHeader);
886 30148 : memcpy(newp, recdata, len);
887 30148 : recdata += len;
888 30148 : newp += len;
889 : }
890 : else
891 : {
892 : /*
893 : * copy bitmap [+ padding] [+ oid] + data from record, all in one
894 : * go
895 : */
896 155400 : memcpy(newp, recdata, tuplen);
897 155400 : recdata += tuplen;
898 155400 : newp += tuplen;
899 : }
900 : Assert(recdata == recdata_end);
901 :
902 : /* copy suffix from old tuple */
903 185548 : if (suffixlen > 0)
904 67116 : memcpy(newp, (char *) oldtup.t_data + oldtup.t_len - suffixlen, suffixlen);
905 :
906 185548 : newlen = SizeofHeapTupleHeader + tuplen + prefixlen + suffixlen;
907 185548 : htup->t_infomask2 = xlhdr.t_infomask2;
908 185548 : htup->t_infomask = xlhdr.t_infomask;
909 185548 : htup->t_hoff = xlhdr.t_hoff;
910 :
911 185548 : HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
912 185548 : HeapTupleHeaderSetCmin(htup, FirstCommandId);
913 185548 : HeapTupleHeaderSetXmax(htup, xlrec->new_xmax);
914 : /* Make sure there is no forward chain link in t_ctid */
915 185548 : htup->t_ctid = newtid;
916 :
917 185548 : offnum = PageAddItem(page, (Item) htup, newlen, offnum, true, true);
918 185548 : if (offnum == InvalidOffsetNumber)
919 0 : elog(PANIC, "failed to add tuple");
920 :
921 185548 : if (xlrec->flags & XLH_UPDATE_NEW_ALL_VISIBLE_CLEARED)
922 138 : PageClearAllVisible(page);
923 :
924 185548 : freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */
925 :
926 185548 : PageSetLSN(page, lsn);
927 185548 : MarkBufferDirty(nbuffer);
928 : }
929 :
930 186552 : if (BufferIsValid(nbuffer) && nbuffer != obuffer)
931 108636 : UnlockReleaseBuffer(nbuffer);
932 186552 : if (BufferIsValid(obuffer))
933 186552 : 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 186552 : if (newaction == BLK_NEEDS_REDO && !hot_update && freespace < BLCKSZ / 5)
951 23270 : XLogRecordPageWithFreeSpace(rlocator, newblk, freespace);
952 186552 : }
953 :
954 : /*
955 : * Replay XLOG_HEAP_CONFIRM records.
956 : */
957 : static void
958 154 : heap_xlog_confirm(XLogReaderState *record)
959 : {
960 154 : XLogRecPtr lsn = record->EndRecPtr;
961 154 : xl_heap_confirm *xlrec = (xl_heap_confirm *) XLogRecGetData(record);
962 : Buffer buffer;
963 : Page page;
964 : OffsetNumber offnum;
965 154 : ItemId lp = NULL;
966 : HeapTupleHeader htup;
967 :
968 154 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
969 : {
970 154 : page = BufferGetPage(buffer);
971 :
972 154 : offnum = xlrec->offnum;
973 154 : if (PageGetMaxOffsetNumber(page) >= offnum)
974 154 : lp = PageGetItemId(page, offnum);
975 :
976 154 : if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
977 0 : elog(PANIC, "invalid lp");
978 :
979 154 : htup = (HeapTupleHeader) PageGetItem(page, lp);
980 :
981 : /*
982 : * Confirm tuple as actually inserted
983 : */
984 154 : ItemPointerSet(&htup->t_ctid, BufferGetBlockNumber(buffer), offnum);
985 :
986 154 : PageSetLSN(page, lsn);
987 154 : MarkBufferDirty(buffer);
988 : }
989 154 : if (BufferIsValid(buffer))
990 154 : UnlockReleaseBuffer(buffer);
991 154 : }
992 :
993 : /*
994 : * Replay XLOG_HEAP_LOCK records.
995 : */
996 : static void
997 110406 : heap_xlog_lock(XLogReaderState *record)
998 : {
999 110406 : XLogRecPtr lsn = record->EndRecPtr;
1000 110406 : xl_heap_lock *xlrec = (xl_heap_lock *) XLogRecGetData(record);
1001 : Buffer buffer;
1002 : Page page;
1003 : OffsetNumber offnum;
1004 110406 : 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 110406 : if (xlrec->flags & XLH_LOCK_ALL_FROZEN_CLEARED)
1012 : {
1013 : RelFileLocator rlocator;
1014 104 : Buffer vmbuffer = InvalidBuffer;
1015 : BlockNumber block;
1016 : Relation reln;
1017 :
1018 104 : XLogRecGetBlockTag(record, 0, &rlocator, NULL, &block);
1019 104 : reln = CreateFakeRelcacheEntry(rlocator);
1020 :
1021 104 : visibilitymap_pin(reln, block, &vmbuffer);
1022 104 : visibilitymap_clear(reln, block, vmbuffer, VISIBILITYMAP_ALL_FROZEN);
1023 :
1024 104 : ReleaseBuffer(vmbuffer);
1025 104 : FreeFakeRelcacheEntry(reln);
1026 : }
1027 :
1028 110406 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
1029 : {
1030 110004 : page = (Page) BufferGetPage(buffer);
1031 :
1032 110004 : offnum = xlrec->offnum;
1033 110004 : if (PageGetMaxOffsetNumber(page) >= offnum)
1034 110004 : lp = PageGetItemId(page, offnum);
1035 :
1036 110004 : if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
1037 0 : elog(PANIC, "invalid lp");
1038 :
1039 110004 : htup = (HeapTupleHeader) PageGetItem(page, lp);
1040 :
1041 110004 : htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
1042 110004 : htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
1043 110004 : 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 110004 : if (HEAP_XMAX_IS_LOCKED_ONLY(htup->t_infomask))
1051 : {
1052 110004 : HeapTupleHeaderClearHotUpdated(htup);
1053 : /* Make sure there is no forward chain link in t_ctid */
1054 110004 : ItemPointerSet(&htup->t_ctid,
1055 : BufferGetBlockNumber(buffer),
1056 : offnum);
1057 : }
1058 110004 : HeapTupleHeaderSetXmax(htup, xlrec->xmax);
1059 110004 : HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
1060 110004 : PageSetLSN(page, lsn);
1061 110004 : MarkBufferDirty(buffer);
1062 : }
1063 110406 : if (BufferIsValid(buffer))
1064 110406 : UnlockReleaseBuffer(buffer);
1065 110406 : }
1066 :
1067 : /*
1068 : * Replay XLOG_HEAP2_LOCK_UPDATED records.
1069 : */
1070 : static void
1071 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
1134 15008 : heap_xlog_inplace(XLogReaderState *record)
1135 : {
1136 15008 : XLogRecPtr lsn = record->EndRecPtr;
1137 15008 : xl_heap_inplace *xlrec = (xl_heap_inplace *) XLogRecGetData(record);
1138 : Buffer buffer;
1139 : Page page;
1140 : OffsetNumber offnum;
1141 15008 : ItemId lp = NULL;
1142 : HeapTupleHeader htup;
1143 : uint32 oldlen;
1144 : Size newlen;
1145 :
1146 15008 : if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
1147 : {
1148 14628 : char *newtup = XLogRecGetBlockData(record, 0, &newlen);
1149 :
1150 14628 : page = BufferGetPage(buffer);
1151 :
1152 14628 : offnum = xlrec->offnum;
1153 14628 : if (PageGetMaxOffsetNumber(page) >= offnum)
1154 14628 : lp = PageGetItemId(page, offnum);
1155 :
1156 14628 : if (PageGetMaxOffsetNumber(page) < offnum || !ItemIdIsNormal(lp))
1157 0 : elog(PANIC, "invalid lp");
1158 :
1159 14628 : htup = (HeapTupleHeader) PageGetItem(page, lp);
1160 :
1161 14628 : oldlen = ItemIdGetLength(lp) - htup->t_hoff;
1162 14628 : if (oldlen != newlen)
1163 0 : elog(PANIC, "wrong tuple length");
1164 :
1165 14628 : memcpy((char *) htup + htup->t_hoff, newtup, newlen);
1166 :
1167 14628 : PageSetLSN(page, lsn);
1168 14628 : MarkBufferDirty(buffer);
1169 : }
1170 15008 : if (BufferIsValid(buffer))
1171 15008 : UnlockReleaseBuffer(buffer);
1172 :
1173 15008 : ProcessCommittedInvalidationMessages(xlrec->msgs,
1174 : xlrec->nmsgs,
1175 15008 : xlrec->relcacheInitFileInval,
1176 : xlrec->dbId,
1177 : xlrec->tsId);
1178 15008 : }
1179 :
1180 : void
1181 3472570 : heap_redo(XLogReaderState *record)
1182 : {
1183 3472570 : 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 3472570 : switch (info & XLOG_HEAP_OPMASK)
1191 : {
1192 2562502 : case XLOG_HEAP_INSERT:
1193 2562502 : heap_xlog_insert(record);
1194 2562502 : break;
1195 597944 : case XLOG_HEAP_DELETE:
1196 597944 : heap_xlog_delete(record);
1197 597944 : break;
1198 114260 : case XLOG_HEAP_UPDATE:
1199 114260 : heap_xlog_update(record, false);
1200 114260 : break;
1201 4 : 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 4 : break;
1209 72292 : case XLOG_HEAP_HOT_UPDATE:
1210 72292 : heap_xlog_update(record, true);
1211 72292 : break;
1212 154 : case XLOG_HEAP_CONFIRM:
1213 154 : heap_xlog_confirm(record);
1214 154 : break;
1215 110406 : case XLOG_HEAP_LOCK:
1216 110406 : heap_xlog_lock(record);
1217 110406 : break;
1218 15008 : case XLOG_HEAP_INPLACE:
1219 15008 : heap_xlog_inplace(record);
1220 15008 : break;
1221 0 : default:
1222 0 : elog(PANIC, "heap_redo: unknown op code %u", info);
1223 : }
1224 3472570 : }
1225 :
1226 : void
1227 155848 : heap2_redo(XLogReaderState *record)
1228 : {
1229 155848 : uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1230 :
1231 155848 : switch (info & XLOG_HEAP_OPMASK)
1232 : {
1233 25280 : case XLOG_HEAP2_PRUNE_ON_ACCESS:
1234 : case XLOG_HEAP2_PRUNE_VACUUM_SCAN:
1235 : case XLOG_HEAP2_PRUNE_VACUUM_CLEANUP:
1236 25280 : heap_xlog_prune_freeze(record);
1237 25280 : break;
1238 14610 : case XLOG_HEAP2_VISIBLE:
1239 14610 : heap_xlog_visible(record);
1240 14610 : break;
1241 113982 : case XLOG_HEAP2_MULTI_INSERT:
1242 113982 : heap_xlog_multi_insert(record);
1243 113982 : break;
1244 0 : case XLOG_HEAP2_LOCK_UPDATED:
1245 0 : heap_xlog_lock_updated(record);
1246 0 : break;
1247 1976 : case XLOG_HEAP2_NEW_CID:
1248 :
1249 : /*
1250 : * Nothing to do on a real replay, only used during logical
1251 : * decoding.
1252 : */
1253 1976 : break;
1254 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 : }
1260 155848 : }
1261 :
1262 : /*
1263 : * Mask a heap page before performing consistency checks on it.
1264 : */
1265 : void
1266 5796044 : heap_mask(char *pagedata, BlockNumber blkno)
1267 : {
1268 5796044 : Page page = (Page) pagedata;
1269 : OffsetNumber off;
1270 :
1271 5796044 : mask_page_lsn_and_checksum(page);
1272 :
1273 5796044 : mask_page_hint_bits(page);
1274 5796044 : mask_unused_space(page);
1275 :
1276 478632612 : for (off = 1; off <= PageGetMaxOffsetNumber(page); off++)
1277 : {
1278 472836568 : ItemId iid = PageGetItemId(page, off);
1279 : char *page_item;
1280 :
1281 472836568 : page_item = (char *) (page + ItemIdGetOffset(iid));
1282 :
1283 472836568 : if (ItemIdIsNormal(iid))
1284 : {
1285 443999208 : 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 443999208 : if (!HeapTupleHeaderXminFrozen(page_htup))
1293 439847392 : page_htup->t_infomask &= ~HEAP_XACT_MASK;
1294 : else
1295 : {
1296 : /* Still we need to mask xmax hint bits. */
1297 4151816 : page_htup->t_infomask &= ~HEAP_XMAX_INVALID;
1298 4151816 : 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 443999208 : 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 443999208 : if (HeapTupleHeaderIsSpeculative(page_htup))
1321 156 : 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 472836568 : if (ItemIdHasStorage(iid))
1336 : {
1337 443999208 : int len = ItemIdGetLength(iid);
1338 443999208 : int padlen = MAXALIGN(len) - len;
1339 :
1340 443999208 : if (padlen > 0)
1341 237877484 : memset(page_item + len, MASK_MARKER, padlen);
1342 : }
1343 : }
1344 5796044 : }
|