PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
nbtxlog.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * nbtxlog.c
4 * WAL replay logic for btrees.
5 *
6 *
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 * IDENTIFICATION
11 * src/backend/access/nbtree/nbtxlog.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include "access/bufmask.h"
18#include "access/nbtree.h"
19#include "access/nbtxlog.h"
20#include "access/transam.h"
21#include "access/xlogutils.h"
22#include "storage/standby.h"
23#include "utils/memutils.h"
24
25static MemoryContext opCtx; /* working memory for operations */
26
27/*
28 * _bt_restore_page -- re-enter all the index tuples on a page
29 *
30 * The page is freshly init'd, and *from (length len) is a copy of what
31 * had been its upper part (pd_upper to pd_special). We assume that the
32 * tuples had been added to the page in item-number order, and therefore
33 * the one with highest item number appears first (lowest on the page).
34 */
35static void
36_bt_restore_page(Page page, char *from, int len)
37{
38 IndexTupleData itupdata;
39 Size itemsz;
40 char *end = from + len;
43 int i;
44 int nitems;
45
46 /*
47 * To get the items back in the original order, we add them to the page in
48 * reverse. To figure out where one tuple ends and another begins, we
49 * have to scan them in forward order first.
50 */
51 i = 0;
52 while (from < end)
53 {
54 /*
55 * As we step through the items, 'from' won't always be properly
56 * aligned, so we need to use memcpy(). Further, we use void * here
57 * for our items array for the same reason; wouldn't want the compiler
58 * or anyone thinking that an item is aligned when it isn't.
59 */
60 memcpy(&itupdata, from, sizeof(IndexTupleData));
61 itemsz = IndexTupleSize(&itupdata);
62 itemsz = MAXALIGN(itemsz);
63
64 items[i] = from;
65 itemsizes[i] = itemsz;
66 i++;
67
68 from += itemsz;
69 }
70 nitems = i;
71
72 for (i = nitems - 1; i >= 0; i--)
73 {
74 if (PageAddItem(page, items[i], itemsizes[i], nitems - i, false, false) == InvalidOffsetNumber)
75 elog(PANIC, "_bt_restore_page: cannot add item to page");
76 }
77}
78
79static void
81{
82 XLogRecPtr lsn = record->EndRecPtr;
83 Buffer metabuf;
84 Page metapg;
86 BTPageOpaque pageop;
87 xl_btree_metadata *xlrec;
88 char *ptr;
89 Size len;
90
91 metabuf = XLogInitBufferForRedo(record, block_id);
92 ptr = XLogRecGetBlockData(record, block_id, &len);
93
94 Assert(len == sizeof(xl_btree_metadata));
96 xlrec = (xl_btree_metadata *) ptr;
97 metapg = BufferGetPage(metabuf);
98
99 _bt_pageinit(metapg, BufferGetPageSize(metabuf));
100
101 md = BTPageGetMeta(metapg);
103 md->btm_version = xlrec->version;
104 md->btm_root = xlrec->root;
105 md->btm_level = xlrec->level;
106 md->btm_fastroot = xlrec->fastroot;
107 md->btm_fastlevel = xlrec->fastlevel;
108 /* Cannot log BTREE_MIN_VERSION index metapage without upgrade */
112 md->btm_allequalimage = xlrec->allequalimage;
113
114 pageop = BTPageGetOpaque(metapg);
115 pageop->btpo_flags = BTP_META;
116
117 /*
118 * Set pd_lower just past the end of the metadata. This is essential,
119 * because without doing so, metadata will be lost if xlog.c compresses
120 * the page.
121 */
122 ((PageHeader) metapg)->pd_lower =
123 ((char *) md + sizeof(BTMetaPageData)) - (char *) metapg;
124
125 PageSetLSN(metapg, lsn);
126 MarkBufferDirty(metabuf);
127 UnlockReleaseBuffer(metabuf);
128}
129
130/*
131 * _bt_clear_incomplete_split -- clear INCOMPLETE_SPLIT flag on a page
132 *
133 * This is a common subroutine of the redo functions of all the WAL record
134 * types that can insert a downlink: insert, split, and newroot.
135 */
136static void
138{
139 XLogRecPtr lsn = record->EndRecPtr;
140 Buffer buf;
141
142 if (XLogReadBufferForRedo(record, block_id, &buf) == BLK_NEEDS_REDO)
143 {
144 Page page = BufferGetPage(buf);
145 BTPageOpaque pageop = BTPageGetOpaque(page);
146
147 Assert(P_INCOMPLETE_SPLIT(pageop));
148 pageop->btpo_flags &= ~BTP_INCOMPLETE_SPLIT;
149
150 PageSetLSN(page, lsn);
152 }
153 if (BufferIsValid(buf))
155}
156
157static void
158btree_xlog_insert(bool isleaf, bool ismeta, bool posting,
159 XLogReaderState *record)
160{
161 XLogRecPtr lsn = record->EndRecPtr;
163 Buffer buffer;
164 Page page;
165
166 /*
167 * Insertion to an internal page finishes an incomplete split at the child
168 * level. Clear the incomplete-split flag in the child. Note: during
169 * normal operation, the child and parent pages are locked at the same
170 * time (the locks are coupled), so that clearing the flag and inserting
171 * the downlink appear atomic to other backends. We don't bother with
172 * that during replay, because readers don't care about the
173 * incomplete-split flag and there cannot be updates happening.
174 */
175 if (!isleaf)
177 if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
178 {
179 Size datalen;
180 char *datapos = XLogRecGetBlockData(record, 0, &datalen);
181
182 page = BufferGetPage(buffer);
183
184 if (!posting)
185 {
186 /* Simple retail insertion */
187 if (PageAddItem(page, datapos, datalen, xlrec->offnum, false, false) == InvalidOffsetNumber)
188 elog(PANIC, "failed to add new item");
189 }
190 else
191 {
192 ItemId itemid;
193 IndexTuple oposting,
194 newitem,
195 nposting;
196 uint16 postingoff;
197
198 /*
199 * A posting list split occurred during leaf page insertion. WAL
200 * record data will start with an offset number representing the
201 * point in an existing posting list that a split occurs at.
202 *
203 * Use _bt_swap_posting() to repeat posting list split steps from
204 * primary. Note that newitem from WAL record is 'orignewitem',
205 * not the final version of newitem that is actually inserted on
206 * page.
207 */
208 postingoff = *((uint16 *) datapos);
209 datapos += sizeof(uint16);
210 datalen -= sizeof(uint16);
211
212 itemid = PageGetItemId(page, OffsetNumberPrev(xlrec->offnum));
213 oposting = (IndexTuple) PageGetItem(page, itemid);
214
215 /* Use mutable, aligned newitem copy in _bt_swap_posting() */
216 Assert(isleaf && postingoff > 0);
217 newitem = CopyIndexTuple((IndexTuple) datapos);
218 nposting = _bt_swap_posting(newitem, oposting, postingoff);
219
220 /* Replace existing posting list with post-split version */
221 memcpy(oposting, nposting, MAXALIGN(IndexTupleSize(nposting)));
222
223 /* Insert "final" new item (not orignewitem from WAL stream) */
224 Assert(IndexTupleSize(newitem) == datalen);
225 if (PageAddItem(page, newitem, datalen, xlrec->offnum, false, false) == InvalidOffsetNumber)
226 elog(PANIC, "failed to add posting split new item");
227 }
228
229 PageSetLSN(page, lsn);
230 MarkBufferDirty(buffer);
231 }
232 if (BufferIsValid(buffer))
233 UnlockReleaseBuffer(buffer);
234
235 /*
236 * Note: in normal operation, we'd update the metapage while still holding
237 * lock on the page we inserted into. But during replay it's not
238 * necessary to hold that lock, since no other index updates can be
239 * happening concurrently, and readers will cope fine with following an
240 * obsolete link from the metapage.
241 */
242 if (ismeta)
243 _bt_restore_meta(record, 2);
244}
245
246static void
247btree_xlog_split(bool newitemonleft, XLogReaderState *record)
248{
249 XLogRecPtr lsn = record->EndRecPtr;
250 xl_btree_split *xlrec = (xl_btree_split *) XLogRecGetData(record);
251 bool isleaf = (xlrec->level == 0);
252 Buffer buf;
253 Buffer rbuf;
254 Page rpage;
255 BTPageOpaque ropaque;
256 char *datapos;
257 Size datalen;
258 BlockNumber origpagenumber;
259 BlockNumber rightpagenumber;
260 BlockNumber spagenumber;
261
262 XLogRecGetBlockTag(record, 0, NULL, NULL, &origpagenumber);
263 XLogRecGetBlockTag(record, 1, NULL, NULL, &rightpagenumber);
264 if (!XLogRecGetBlockTagExtended(record, 2, NULL, NULL, &spagenumber, NULL))
265 spagenumber = P_NONE;
266
267 /*
268 * Clear the incomplete split flag on the appropriate child page one level
269 * down when origpage/buf is an internal page (there must have been
270 * cascading page splits during original execution in the event of an
271 * internal page split). This is like the corresponding btree_xlog_insert
272 * call for internal pages. We're not clearing the incomplete split flag
273 * for the current page split here (you can think of this as part of the
274 * insert of newitem that the page split action needs to perform in
275 * passing).
276 *
277 * Like in btree_xlog_insert, this can be done before locking other pages.
278 * We never need to couple cross-level locks in REDO routines.
279 */
280 if (!isleaf)
282
283 /* Reconstruct right (new) sibling page from scratch */
284 rbuf = XLogInitBufferForRedo(record, 1);
285 datapos = XLogRecGetBlockData(record, 1, &datalen);
286 rpage = BufferGetPage(rbuf);
287
288 _bt_pageinit(rpage, BufferGetPageSize(rbuf));
289 ropaque = BTPageGetOpaque(rpage);
290
291 ropaque->btpo_prev = origpagenumber;
292 ropaque->btpo_next = spagenumber;
293 ropaque->btpo_level = xlrec->level;
294 ropaque->btpo_flags = isleaf ? BTP_LEAF : 0;
295 ropaque->btpo_cycleid = 0;
296
297 _bt_restore_page(rpage, datapos, datalen);
298
299 PageSetLSN(rpage, lsn);
300 MarkBufferDirty(rbuf);
301
302 /* Now reconstruct original page (left half of split) */
303 if (XLogReadBufferForRedo(record, 0, &buf) == BLK_NEEDS_REDO)
304 {
305 /*
306 * To retain the same physical order of the tuples that they had, we
307 * initialize a temporary empty page for the left page and add all the
308 * items to that in item number order. This mirrors how _bt_split()
309 * works. Retaining the same physical order makes WAL consistency
310 * checking possible. See also _bt_restore_page(), which does the
311 * same for the right page.
312 */
313 Page origpage = BufferGetPage(buf);
314 BTPageOpaque oopaque = BTPageGetOpaque(origpage);
315 OffsetNumber off;
316 IndexTuple newitem = NULL,
317 left_hikey = NULL,
318 nposting = NULL;
319 Size newitemsz = 0,
320 left_hikeysz = 0;
321 Page leftpage;
322 OffsetNumber leftoff,
323 replacepostingoff = InvalidOffsetNumber;
324
325 datapos = XLogRecGetBlockData(record, 0, &datalen);
326
327 if (newitemonleft || xlrec->postingoff != 0)
328 {
329 newitem = (IndexTuple) datapos;
330 newitemsz = MAXALIGN(IndexTupleSize(newitem));
331 datapos += newitemsz;
332 datalen -= newitemsz;
333
334 if (xlrec->postingoff != 0)
335 {
336 ItemId itemid;
337 IndexTuple oposting;
338
339 /* Posting list must be at offset number before new item's */
340 replacepostingoff = OffsetNumberPrev(xlrec->newitemoff);
341
342 /* Use mutable, aligned newitem copy in _bt_swap_posting() */
343 newitem = CopyIndexTuple(newitem);
344 itemid = PageGetItemId(origpage, replacepostingoff);
345 oposting = (IndexTuple) PageGetItem(origpage, itemid);
346 nposting = _bt_swap_posting(newitem, oposting,
347 xlrec->postingoff);
348 }
349 }
350
351 /*
352 * Extract left hikey and its size. We assume that 16-bit alignment
353 * is enough to apply IndexTupleSize (since it's fetching from a
354 * uint16 field).
355 */
356 left_hikey = (IndexTuple) datapos;
357 left_hikeysz = MAXALIGN(IndexTupleSize(left_hikey));
358 datapos += left_hikeysz;
359 datalen -= left_hikeysz;
360
361 Assert(datalen == 0);
362
363 leftpage = PageGetTempPageCopySpecial(origpage);
364
365 /* Add high key tuple from WAL record to temp page */
366 leftoff = P_HIKEY;
367 if (PageAddItem(leftpage, left_hikey, left_hikeysz, P_HIKEY, false, false) == InvalidOffsetNumber)
368 elog(ERROR, "failed to add high key to left page after split");
369 leftoff = OffsetNumberNext(leftoff);
370
371 for (off = P_FIRSTDATAKEY(oopaque); off < xlrec->firstrightoff; off++)
372 {
373 ItemId itemid;
374 Size itemsz;
375 IndexTuple item;
376
377 /* Add replacement posting list when required */
378 if (off == replacepostingoff)
379 {
380 Assert(newitemonleft ||
381 xlrec->firstrightoff == xlrec->newitemoff);
382 if (PageAddItem(leftpage, nposting, MAXALIGN(IndexTupleSize(nposting)), leftoff, false, false) == InvalidOffsetNumber)
383 elog(ERROR, "failed to add new posting list item to left page after split");
384 leftoff = OffsetNumberNext(leftoff);
385 continue; /* don't insert oposting */
386 }
387
388 /* add the new item if it was inserted on left page */
389 else if (newitemonleft && off == xlrec->newitemoff)
390 {
391 if (PageAddItem(leftpage, newitem, newitemsz, leftoff, false, false) == InvalidOffsetNumber)
392 elog(ERROR, "failed to add new item to left page after split");
393 leftoff = OffsetNumberNext(leftoff);
394 }
395
396 itemid = PageGetItemId(origpage, off);
397 itemsz = ItemIdGetLength(itemid);
398 item = (IndexTuple) PageGetItem(origpage, itemid);
399 if (PageAddItem(leftpage, item, itemsz, leftoff, false, false) == InvalidOffsetNumber)
400 elog(ERROR, "failed to add old item to left page after split");
401 leftoff = OffsetNumberNext(leftoff);
402 }
403
404 /* cope with possibility that newitem goes at the end */
405 if (newitemonleft && off == xlrec->newitemoff)
406 {
407 if (PageAddItem(leftpage, newitem, newitemsz, leftoff, false, false) == InvalidOffsetNumber)
408 elog(ERROR, "failed to add new item to left page after split");
409 leftoff = OffsetNumberNext(leftoff);
410 }
411
412 PageRestoreTempPage(leftpage, origpage);
413
414 /* Fix opaque fields */
416 if (isleaf)
417 oopaque->btpo_flags |= BTP_LEAF;
418 oopaque->btpo_next = rightpagenumber;
419 oopaque->btpo_cycleid = 0;
420
421 PageSetLSN(origpage, lsn);
423 }
424
425 /* Fix left-link of the page to the right of the new right sibling */
426 if (spagenumber != P_NONE)
427 {
428 Buffer sbuf;
429
430 if (XLogReadBufferForRedo(record, 2, &sbuf) == BLK_NEEDS_REDO)
431 {
432 Page spage = BufferGetPage(sbuf);
433 BTPageOpaque spageop = BTPageGetOpaque(spage);
434
435 spageop->btpo_prev = rightpagenumber;
436
437 PageSetLSN(spage, lsn);
438 MarkBufferDirty(sbuf);
439 }
440 if (BufferIsValid(sbuf))
442 }
443
444 /*
445 * Finally, release the remaining buffers. sbuf, rbuf, and buf must be
446 * released together, so that readers cannot observe inconsistencies.
447 */
449 if (BufferIsValid(buf))
451}
452
453static void
455{
456 XLogRecPtr lsn = record->EndRecPtr;
457 xl_btree_dedup *xlrec = (xl_btree_dedup *) XLogRecGetData(record);
458 Buffer buf;
459
460 if (XLogReadBufferForRedo(record, 0, &buf) == BLK_NEEDS_REDO)
461 {
462 char *ptr = XLogRecGetBlockData(record, 0, NULL);
463 Page page = BufferGetPage(buf);
464 BTPageOpaque opaque = BTPageGetOpaque(page);
465 OffsetNumber offnum,
466 minoff,
467 maxoff;
470 Page newpage;
471
473 state->deduplicate = true; /* unused */
474 state->nmaxitems = 0; /* unused */
475 /* Conservatively use larger maxpostingsize than primary */
476 state->maxpostingsize = BTMaxItemSize;
477 state->base = NULL;
478 state->baseoff = InvalidOffsetNumber;
479 state->basetupsize = 0;
480 state->htids = palloc(state->maxpostingsize);
481 state->nhtids = 0;
482 state->nitems = 0;
483 state->phystupsize = 0;
484 state->nintervals = 0;
485
486 minoff = P_FIRSTDATAKEY(opaque);
487 maxoff = PageGetMaxOffsetNumber(page);
488 newpage = PageGetTempPageCopySpecial(page);
489
490 if (!P_RIGHTMOST(opaque))
491 {
492 ItemId itemid = PageGetItemId(page, P_HIKEY);
493 Size itemsz = ItemIdGetLength(itemid);
494 IndexTuple item = (IndexTuple) PageGetItem(page, itemid);
495
496 if (PageAddItem(newpage, item, itemsz, P_HIKEY, false, false) == InvalidOffsetNumber)
497 elog(ERROR, "deduplication failed to add highkey");
498 }
499
500 intervals = (BTDedupInterval *) ptr;
501 for (offnum = minoff;
502 offnum <= maxoff;
503 offnum = OffsetNumberNext(offnum))
504 {
505 ItemId itemid = PageGetItemId(page, offnum);
506 IndexTuple itup = (IndexTuple) PageGetItem(page, itemid);
507
508 if (offnum == minoff)
509 _bt_dedup_start_pending(state, itup, offnum);
510 else if (state->nintervals < xlrec->nintervals &&
511 state->baseoff == intervals[state->nintervals].baseoff &&
512 state->nitems < intervals[state->nintervals].nitems)
513 {
514 if (!_bt_dedup_save_htid(state, itup))
515 elog(ERROR, "deduplication failed to add heap tid to pending posting list");
516 }
517 else
518 {
520 _bt_dedup_start_pending(state, itup, offnum);
521 }
522 }
523
525 Assert(state->nintervals == xlrec->nintervals);
526 Assert(memcmp(state->intervals, intervals,
527 state->nintervals * sizeof(BTDedupInterval)) == 0);
528
529 if (P_HAS_GARBAGE(opaque))
530 {
531 BTPageOpaque nopaque = BTPageGetOpaque(newpage);
532
533 nopaque->btpo_flags &= ~BTP_HAS_GARBAGE;
534 }
535
536 PageRestoreTempPage(newpage, page);
537 PageSetLSN(page, lsn);
539 }
540
541 if (BufferIsValid(buf))
543}
544
545static void
547 xl_btree_update *updates, int nupdated)
548{
549 BTVacuumPosting vacposting;
550 IndexTuple origtuple;
551 ItemId itemid;
552 Size itemsz;
553
554 for (int i = 0; i < nupdated; i++)
555 {
556 itemid = PageGetItemId(page, updatedoffsets[i]);
557 origtuple = (IndexTuple) PageGetItem(page, itemid);
558
559 vacposting = palloc(offsetof(BTVacuumPostingData, deletetids) +
560 updates->ndeletedtids * sizeof(uint16));
561 vacposting->updatedoffset = updatedoffsets[i];
562 vacposting->itup = origtuple;
563 vacposting->ndeletedtids = updates->ndeletedtids;
564 memcpy(vacposting->deletetids,
565 (char *) updates + SizeOfBtreeUpdate,
566 updates->ndeletedtids * sizeof(uint16));
567
568 _bt_update_posting(vacposting);
569
570 /* Overwrite updated version of tuple */
571 itemsz = MAXALIGN(IndexTupleSize(vacposting->itup));
572 if (!PageIndexTupleOverwrite(page, updatedoffsets[i], vacposting->itup, itemsz))
573 elog(PANIC, "failed to update partially dead item");
574
575 pfree(vacposting->itup);
576 pfree(vacposting);
577
578 /* advance to next xl_btree_update from array */
579 updates = (xl_btree_update *)
580 ((char *) updates + SizeOfBtreeUpdate +
581 updates->ndeletedtids * sizeof(uint16));
582 }
583}
584
585static void
587{
588 XLogRecPtr lsn = record->EndRecPtr;
590 Buffer buffer;
591 Page page;
592 BTPageOpaque opaque;
593
594 /*
595 * We need to take a cleanup lock here, just like btvacuumpage(). However,
596 * it isn't necessary to exhaustively get a cleanup lock on every block in
597 * the index during recovery (just getting a cleanup lock on pages with
598 * items to kill suffices). See nbtree/README for details.
599 */
600 if (XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &buffer)
602 {
603 char *ptr = XLogRecGetBlockData(record, 0, NULL);
604
605 page = BufferGetPage(buffer);
606
607 if (xlrec->nupdated > 0)
608 {
609 OffsetNumber *updatedoffsets;
610 xl_btree_update *updates;
611
612 updatedoffsets = (OffsetNumber *)
613 (ptr + xlrec->ndeleted * sizeof(OffsetNumber));
614 updates = (xl_btree_update *) ((char *) updatedoffsets +
615 xlrec->nupdated *
616 sizeof(OffsetNumber));
617
618 btree_xlog_updates(page, updatedoffsets, updates, xlrec->nupdated);
619 }
620
621 if (xlrec->ndeleted > 0)
622 PageIndexMultiDelete(page, (OffsetNumber *) ptr, xlrec->ndeleted);
623
624 /*
625 * Clear the vacuum cycle ID, and mark the page as not containing any
626 * LP_DEAD items
627 */
628 opaque = BTPageGetOpaque(page);
629 opaque->btpo_cycleid = 0;
630 opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
631
632 PageSetLSN(page, lsn);
633 MarkBufferDirty(buffer);
634 }
635 if (BufferIsValid(buffer))
636 UnlockReleaseBuffer(buffer);
637}
638
639static void
641{
642 XLogRecPtr lsn = record->EndRecPtr;
644 Buffer buffer;
645 Page page;
646 BTPageOpaque opaque;
647
648 /*
649 * If we have any conflict processing to do, it must happen before we
650 * update the page
651 */
652 if (InHotStandby)
653 {
654 RelFileLocator rlocator;
655
656 XLogRecGetBlockTag(record, 0, &rlocator, NULL, NULL);
657
659 xlrec->isCatalogRel,
660 rlocator);
661 }
662
663 /*
664 * We don't need to take a cleanup lock to apply these changes. See
665 * nbtree/README for details.
666 */
667 if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
668 {
669 char *ptr = XLogRecGetBlockData(record, 0, NULL);
670
671 page = BufferGetPage(buffer);
672
673 if (xlrec->nupdated > 0)
674 {
675 OffsetNumber *updatedoffsets;
676 xl_btree_update *updates;
677
678 updatedoffsets = (OffsetNumber *)
679 (ptr + xlrec->ndeleted * sizeof(OffsetNumber));
680 updates = (xl_btree_update *) ((char *) updatedoffsets +
681 xlrec->nupdated *
682 sizeof(OffsetNumber));
683
684 btree_xlog_updates(page, updatedoffsets, updates, xlrec->nupdated);
685 }
686
687 if (xlrec->ndeleted > 0)
688 PageIndexMultiDelete(page, (OffsetNumber *) ptr, xlrec->ndeleted);
689
690 /*
691 * Do *not* clear the vacuum cycle ID, but do mark the page as not
692 * containing any LP_DEAD items
693 */
694 opaque = BTPageGetOpaque(page);
695 opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
696
697 PageSetLSN(page, lsn);
698 MarkBufferDirty(buffer);
699 }
700 if (BufferIsValid(buffer))
701 UnlockReleaseBuffer(buffer);
702}
703
704static void
706{
707 XLogRecPtr lsn = record->EndRecPtr;
709 Buffer buffer;
710 Page page;
711 BTPageOpaque pageop;
712 IndexTupleData trunctuple;
713
714 /*
715 * In normal operation, we would lock all the pages this WAL record
716 * touches before changing any of them. In WAL replay, it should be okay
717 * to lock just one page at a time, since no concurrent index updates can
718 * be happening, and readers should not care whether they arrive at the
719 * target page or not (since it's surely empty).
720 */
721
722 /* to-be-deleted subtree's parent page */
723 if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
724 {
725 OffsetNumber poffset;
726 ItemId itemid;
727 IndexTuple itup;
728 OffsetNumber nextoffset;
729 BlockNumber rightsib;
730
731 page = BufferGetPage(buffer);
732 pageop = BTPageGetOpaque(page);
733
734 poffset = xlrec->poffset;
735
736 nextoffset = OffsetNumberNext(poffset);
737 itemid = PageGetItemId(page, nextoffset);
738 itup = (IndexTuple) PageGetItem(page, itemid);
739 rightsib = BTreeTupleGetDownLink(itup);
740
741 itemid = PageGetItemId(page, poffset);
742 itup = (IndexTuple) PageGetItem(page, itemid);
743 BTreeTupleSetDownLink(itup, rightsib);
744 nextoffset = OffsetNumberNext(poffset);
745 PageIndexTupleDelete(page, nextoffset);
746
747 PageSetLSN(page, lsn);
748 MarkBufferDirty(buffer);
749 }
750
751 /*
752 * Don't need to couple cross-level locks in REDO routines, so release
753 * lock on internal page immediately
754 */
755 if (BufferIsValid(buffer))
756 UnlockReleaseBuffer(buffer);
757
758 /* Rewrite the leaf page as a halfdead page */
759 buffer = XLogInitBufferForRedo(record, 0);
760 page = BufferGetPage(buffer);
761
762 _bt_pageinit(page, BufferGetPageSize(buffer));
763 pageop = BTPageGetOpaque(page);
764
765 pageop->btpo_prev = xlrec->leftblk;
766 pageop->btpo_next = xlrec->rightblk;
767 pageop->btpo_level = 0;
769 pageop->btpo_cycleid = 0;
770
771 /*
772 * Construct a dummy high key item that points to top parent page (value
773 * is InvalidBlockNumber when the top parent page is the leaf page itself)
774 */
775 MemSet(&trunctuple, 0, sizeof(IndexTupleData));
776 trunctuple.t_info = sizeof(IndexTupleData);
777 BTreeTupleSetTopParent(&trunctuple, xlrec->topparent);
778
779 if (PageAddItem(page, &trunctuple, sizeof(IndexTupleData), P_HIKEY, false, false) == InvalidOffsetNumber)
780 elog(ERROR, "could not add dummy high key to half-dead page");
781
782 PageSetLSN(page, lsn);
783 MarkBufferDirty(buffer);
784 UnlockReleaseBuffer(buffer);
785}
786
787
788static void
790{
791 XLogRecPtr lsn = record->EndRecPtr;
793 BlockNumber leftsib;
794 BlockNumber rightsib;
795 uint32 level;
796 bool isleaf;
797 FullTransactionId safexid;
798 Buffer leftbuf;
799 Buffer target;
800 Buffer rightbuf;
801 Page page;
802 BTPageOpaque pageop;
803
804 leftsib = xlrec->leftsib;
805 rightsib = xlrec->rightsib;
806 level = xlrec->level;
807 isleaf = (level == 0);
808 safexid = xlrec->safexid;
809
810 /* No leaftopparent for level 0 (leaf page) or level 1 target */
811 Assert(!BlockNumberIsValid(xlrec->leaftopparent) || level > 1);
812
813 /*
814 * In normal operation, we would lock all the pages this WAL record
815 * touches before changing any of them. In WAL replay, we at least lock
816 * the pages in the same standard left-to-right order (leftsib, target,
817 * rightsib), and don't release the sibling locks until the target is
818 * marked deleted.
819 */
820
821 /* Fix right-link of left sibling, if any */
822 if (leftsib != P_NONE)
823 {
824 if (XLogReadBufferForRedo(record, 1, &leftbuf) == BLK_NEEDS_REDO)
825 {
826 page = BufferGetPage(leftbuf);
827 pageop = BTPageGetOpaque(page);
828 pageop->btpo_next = rightsib;
829
830 PageSetLSN(page, lsn);
831 MarkBufferDirty(leftbuf);
832 }
833 }
834 else
835 leftbuf = InvalidBuffer;
836
837 /* Rewrite target page as empty deleted page */
838 target = XLogInitBufferForRedo(record, 0);
839 page = BufferGetPage(target);
840
841 _bt_pageinit(page, BufferGetPageSize(target));
842 pageop = BTPageGetOpaque(page);
843
844 pageop->btpo_prev = leftsib;
845 pageop->btpo_next = rightsib;
846 pageop->btpo_level = level;
847 BTPageSetDeleted(page, safexid);
848 if (isleaf)
849 pageop->btpo_flags |= BTP_LEAF;
850 pageop->btpo_cycleid = 0;
851
852 PageSetLSN(page, lsn);
853 MarkBufferDirty(target);
854
855 /* Fix left-link of right sibling */
856 if (XLogReadBufferForRedo(record, 2, &rightbuf) == BLK_NEEDS_REDO)
857 {
858 page = BufferGetPage(rightbuf);
859 pageop = BTPageGetOpaque(page);
860 pageop->btpo_prev = leftsib;
861
862 PageSetLSN(page, lsn);
863 MarkBufferDirty(rightbuf);
864 }
865
866 /* Release siblings */
867 if (BufferIsValid(leftbuf))
868 UnlockReleaseBuffer(leftbuf);
869 if (BufferIsValid(rightbuf))
870 UnlockReleaseBuffer(rightbuf);
871
872 /* Release target */
873 UnlockReleaseBuffer(target);
874
875 /*
876 * If we deleted a parent of the targeted leaf page, instead of the leaf
877 * itself, update the leaf to point to the next remaining child in the
878 * to-be-deleted subtree
879 */
880 if (XLogRecHasBlockRef(record, 3))
881 {
882 /*
883 * There is no real data on the page, so we just re-create it from
884 * scratch using the information from the WAL record.
885 *
886 * Note that we don't end up here when the target page is also the
887 * leafbuf page. There is no need to add a dummy hikey item with a
888 * top parent link when deleting leafbuf because it's the last page
889 * we'll delete in the subtree undergoing deletion.
890 */
891 Buffer leafbuf;
892 IndexTupleData trunctuple;
893
894 Assert(!isleaf);
895
896 leafbuf = XLogInitBufferForRedo(record, 3);
897 page = BufferGetPage(leafbuf);
898
899 _bt_pageinit(page, BufferGetPageSize(leafbuf));
900 pageop = BTPageGetOpaque(page);
901
903 pageop->btpo_prev = xlrec->leafleftsib;
904 pageop->btpo_next = xlrec->leafrightsib;
905 pageop->btpo_level = 0;
906 pageop->btpo_cycleid = 0;
907
908 /* Add a dummy hikey item */
909 MemSet(&trunctuple, 0, sizeof(IndexTupleData));
910 trunctuple.t_info = sizeof(IndexTupleData);
911 BTreeTupleSetTopParent(&trunctuple, xlrec->leaftopparent);
912
913 if (PageAddItem(page, &trunctuple, sizeof(IndexTupleData), P_HIKEY, false, false) == InvalidOffsetNumber)
914 elog(ERROR, "could not add dummy high key to half-dead page");
915
916 PageSetLSN(page, lsn);
917 MarkBufferDirty(leafbuf);
918 UnlockReleaseBuffer(leafbuf);
919 }
920
921 /* Update metapage if needed */
922 if (info == XLOG_BTREE_UNLINK_PAGE_META)
923 _bt_restore_meta(record, 4);
924}
925
926static void
928{
929 XLogRecPtr lsn = record->EndRecPtr;
931 Buffer buffer;
932 Page page;
933 BTPageOpaque pageop;
934 char *ptr;
935 Size len;
936
937 buffer = XLogInitBufferForRedo(record, 0);
938 page = BufferGetPage(buffer);
939
940 _bt_pageinit(page, BufferGetPageSize(buffer));
941 pageop = BTPageGetOpaque(page);
942
943 pageop->btpo_flags = BTP_ROOT;
944 pageop->btpo_prev = pageop->btpo_next = P_NONE;
945 pageop->btpo_level = xlrec->level;
946 if (xlrec->level == 0)
947 pageop->btpo_flags |= BTP_LEAF;
948 pageop->btpo_cycleid = 0;
949
950 if (xlrec->level > 0)
951 {
952 ptr = XLogRecGetBlockData(record, 0, &len);
953 _bt_restore_page(page, ptr, len);
954
955 /* Clear the incomplete-split flag in left child */
957 }
958
959 PageSetLSN(page, lsn);
960 MarkBufferDirty(buffer);
961 UnlockReleaseBuffer(buffer);
962
963 _bt_restore_meta(record, 2);
964}
965
966/*
967 * In general VACUUM must defer recycling as a way of avoiding certain race
968 * conditions. Deleted pages contain a safexid value that is used by VACUUM
969 * to determine whether or not it's safe to place a page that was deleted by
970 * VACUUM earlier into the FSM now. See nbtree/README.
971 *
972 * As far as any backend operating during original execution is concerned, the
973 * FSM is a cache of recycle-safe pages; the mere presence of the page in the
974 * FSM indicates that the page must already be safe to recycle (actually,
975 * _bt_allocbuf() verifies it's safe using BTPageIsRecyclable(), but that's
976 * just because it would be unwise to completely trust the FSM, given its
977 * current limitations).
978 *
979 * This isn't sufficient to prevent similar concurrent recycling race
980 * conditions during Hot Standby, though. For that we need to log a
981 * xl_btree_reuse_page record at the point that a page is actually recycled
982 * and reused for an entirely unrelated page inside _bt_split(). These
983 * records include the same safexid value from the original deleted page,
984 * stored in the record's snapshotConflictHorizon field.
985 *
986 * The GlobalVisCheckRemovableFullXid() test in BTPageIsRecyclable() is used
987 * to determine if it's safe to recycle a page. This mirrors our own test:
988 * the PGPROC->xmin > limitXmin test inside GetConflictingVirtualXIDs().
989 * Consequently, one XID value achieves the same exclusion effect on primary
990 * and standby.
991 */
992static void
994{
996
997 if (InHotStandby)
999 xlrec->isCatalogRel,
1000 xlrec->locator);
1001}
1002
1003void
1005{
1006 uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
1007 MemoryContext oldCtx;
1008
1009 oldCtx = MemoryContextSwitchTo(opCtx);
1010 switch (info)
1011 {
1013 btree_xlog_insert(true, false, false, record);
1014 break;
1016 btree_xlog_insert(false, false, false, record);
1017 break;
1019 btree_xlog_insert(false, true, false, record);
1020 break;
1021 case XLOG_BTREE_SPLIT_L:
1022 btree_xlog_split(true, record);
1023 break;
1024 case XLOG_BTREE_SPLIT_R:
1025 btree_xlog_split(false, record);
1026 break;
1028 btree_xlog_insert(true, false, true, record);
1029 break;
1030 case XLOG_BTREE_DEDUP:
1031 btree_xlog_dedup(record);
1032 break;
1033 case XLOG_BTREE_VACUUM:
1034 btree_xlog_vacuum(record);
1035 break;
1036 case XLOG_BTREE_DELETE:
1037 btree_xlog_delete(record);
1038 break;
1040 btree_xlog_mark_page_halfdead(info, record);
1041 break;
1044 btree_xlog_unlink_page(info, record);
1045 break;
1046 case XLOG_BTREE_NEWROOT:
1047 btree_xlog_newroot(record);
1048 break;
1050 btree_xlog_reuse_page(record);
1051 break;
1053 _bt_restore_meta(record, 0);
1054 break;
1055 default:
1056 elog(PANIC, "btree_redo: unknown op code %u", info);
1057 }
1058 MemoryContextSwitchTo(oldCtx);
1060}
1061
1062void
1064{
1066 "Btree recovery temporary context",
1068}
1069
1070void
1072{
1074 opCtx = NULL;
1075}
1076
1077/*
1078 * Mask a btree page before performing consistency checks on it.
1079 */
1080void
1081btree_mask(char *pagedata, BlockNumber blkno)
1082{
1083 Page page = (Page) pagedata;
1084 BTPageOpaque maskopaq;
1085
1087
1088 mask_page_hint_bits(page);
1089 mask_unused_space(page);
1090
1091 maskopaq = BTPageGetOpaque(page);
1092
1093 if (P_ISLEAF(maskopaq))
1094 {
1095 /*
1096 * In btree leaf pages, it is possible to modify the LP_FLAGS without
1097 * emitting any WAL record. Hence, mask the line pointer flags. See
1098 * _bt_killitems(), _bt_check_unique() for details.
1099 */
1100 mask_lp_flags(page);
1101 }
1102
1103 /*
1104 * BTP_HAS_GARBAGE is just an un-logged hint bit. So, mask it. See
1105 * _bt_delete_or_dedup_one_page(), _bt_killitems(), and _bt_check_unique()
1106 * for details.
1107 */
1108 maskopaq->btpo_flags &= ~BTP_HAS_GARBAGE;
1109
1110 /*
1111 * During replay of a btree page split, we don't set the BTP_SPLIT_END
1112 * flag of the right sibling and initialize the cycle_id to 0 for the same
1113 * page. See btree_xlog_split() for details.
1114 */
1115 maskopaq->btpo_flags &= ~BTP_SPLIT_END;
1116 maskopaq->btpo_cycleid = 0;
1117}
uint32 BlockNumber
Definition: block.h:31
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition: block.h:71
int Buffer
Definition: buf.h:23
#define InvalidBuffer
Definition: buf.h:25
void mask_lp_flags(Page page)
Definition: bufmask.c:95
void mask_page_lsn_and_checksum(Page page)
Definition: bufmask.c:31
void mask_unused_space(Page page)
Definition: bufmask.c:71
void mask_page_hint_bits(Page page)
Definition: bufmask.c:46
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:4224
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5388
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:2937
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:425
static Size BufferGetPageSize(Buffer buffer)
Definition: bufmgr.h:414
@ RBM_NORMAL
Definition: bufmgr.h:46
static bool BufferIsValid(Buffer bufnum)
Definition: bufmgr.h:376
void PageRestoreTempPage(Page tempPage, Page oldPage)
Definition: bufpage.c:423
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:1160
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, const void *newtup, Size newsize)
Definition: bufpage.c:1404
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:1051
Page PageGetTempPageCopySpecial(const PageData *page)
Definition: bufpage.c:401
PageHeaderData * PageHeader
Definition: bufpage.h:173
static void * PageGetItem(const PageData *page, const ItemIdData *itemId)
Definition: bufpage.h:353
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:243
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition: bufpage.h:390
PageData * Page
Definition: bufpage.h:81
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:471
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:371
#define MAXALIGN(LEN)
Definition: c.h:814
uint8_t uint8
Definition: c.h:540
uint16_t uint16
Definition: c.h:541
uint32_t uint32
Definition: c.h:542
#define MemSet(start, val, len)
Definition: c.h:1023
size_t Size
Definition: c.h:614
#define PANIC
Definition: elog.h:42
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
Assert(PointerIsAligned(start, uint64))
#define nitems(x)
Definition: indent.h:31
IndexTuple CopyIndexTuple(IndexTuple source)
Definition: indextuple.c:547
int i
Definition: isn.c:77
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
IndexTupleData * IndexTuple
Definition: itup.h:53
struct IndexTupleData IndexTupleData
static Size IndexTupleSize(const IndexTupleData *itup)
Definition: itup.h:71
#define MaxIndexTuplesPerPage
Definition: itup.h:181
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:400
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc(Size size)
Definition: mcxt.c:1365
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
IndexTuple _bt_swap_posting(IndexTuple newitem, IndexTuple oposting, int postingoff)
Definition: nbtdedup.c:1020
void _bt_update_posting(BTVacuumPosting vacposting)
Definition: nbtdedup.c:922
bool _bt_dedup_save_htid(BTDedupState state, IndexTuple itup)
Definition: nbtdedup.c:484
void _bt_dedup_start_pending(BTDedupState state, IndexTuple base, OffsetNumber baseoff)
Definition: nbtdedup.c:433
Size _bt_dedup_finish_pending(Page newpage, BTDedupState state)
Definition: nbtdedup.c:555
void _bt_pageinit(Page page, Size size)
Definition: nbtpage.c:1129
#define BTPageGetMeta(p)
Definition: nbtree.h:122
#define P_ISLEAF(opaque)
Definition: nbtree.h:221
#define BTP_LEAF
Definition: nbtree.h:77
#define BTP_HALF_DEAD
Definition: nbtree.h:81
#define P_HIKEY
Definition: nbtree.h:368
#define P_HAS_GARBAGE(opaque)
Definition: nbtree.h:227
static void BTreeTupleSetTopParent(IndexTuple leafhikey, BlockNumber blkno)
Definition: nbtree.h:627
#define BTPageGetOpaque(page)
Definition: nbtree.h:74
#define BTREE_MAGIC
Definition: nbtree.h:150
#define BTP_META
Definition: nbtree.h:80
#define BTP_ROOT
Definition: nbtree.h:78
static void BTreeTupleSetDownLink(IndexTuple pivot, BlockNumber blkno)
Definition: nbtree.h:563
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:370
#define P_NONE
Definition: nbtree.h:213
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:220
#define P_INCOMPLETE_SPLIT(opaque)
Definition: nbtree.h:228
#define BTREE_METAPAGE
Definition: nbtree.h:149
#define BTP_INCOMPLETE_SPLIT
Definition: nbtree.h:84
static BlockNumber BTreeTupleGetDownLink(IndexTuple pivot)
Definition: nbtree.h:557
static void BTPageSetDeleted(Page page, FullTransactionId safexid)
Definition: nbtree.h:240
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:153
BTDedupStateData * BTDedupState
Definition: nbtree.h:904
#define BTMaxItemSize
Definition: nbtree.h:165
static void btree_xlog_delete(XLogReaderState *record)
Definition: nbtxlog.c:640
void btree_redo(XLogReaderState *record)
Definition: nbtxlog.c:1004
static void _bt_restore_meta(XLogReaderState *record, uint8 block_id)
Definition: nbtxlog.c:80
void btree_xlog_cleanup(void)
Definition: nbtxlog.c:1071
static void btree_xlog_newroot(XLogReaderState *record)
Definition: nbtxlog.c:927
static void btree_xlog_updates(Page page, OffsetNumber *updatedoffsets, xl_btree_update *updates, int nupdated)
Definition: nbtxlog.c:546
static void btree_xlog_dedup(XLogReaderState *record)
Definition: nbtxlog.c:454
static void btree_xlog_insert(bool isleaf, bool ismeta, bool posting, XLogReaderState *record)
Definition: nbtxlog.c:158
static void btree_xlog_split(bool newitemonleft, XLogReaderState *record)
Definition: nbtxlog.c:247
static void btree_xlog_reuse_page(XLogReaderState *record)
Definition: nbtxlog.c:993
static void _bt_clear_incomplete_split(XLogReaderState *record, uint8 block_id)
Definition: nbtxlog.c:137
static void btree_xlog_mark_page_halfdead(uint8 info, XLogReaderState *record)
Definition: nbtxlog.c:705
static void _bt_restore_page(Page page, char *from, int len)
Definition: nbtxlog.c:36
void btree_mask(char *pagedata, BlockNumber blkno)
Definition: nbtxlog.c:1081
static MemoryContext opCtx
Definition: nbtxlog.c:25
void btree_xlog_startup(void)
Definition: nbtxlog.c:1063
static void btree_xlog_vacuum(XLogReaderState *record)
Definition: nbtxlog.c:586
static void btree_xlog_unlink_page(uint8 info, XLogReaderState *record)
Definition: nbtxlog.c:789
#define XLOG_BTREE_META_CLEANUP
Definition: nbtxlog.h:41
#define XLOG_BTREE_INSERT_POST
Definition: nbtxlog.h:32
#define SizeOfBtreeUpdate
Definition: nbtxlog.h:268
#define XLOG_BTREE_VACUUM
Definition: nbtxlog.h:39
#define XLOG_BTREE_SPLIT_R
Definition: nbtxlog.h:31
#define XLOG_BTREE_INSERT_LEAF
Definition: nbtxlog.h:27
#define XLOG_BTREE_INSERT_UPPER
Definition: nbtxlog.h:28
#define XLOG_BTREE_DEDUP
Definition: nbtxlog.h:33
#define XLOG_BTREE_UNLINK_PAGE
Definition: nbtxlog.h:35
#define XLOG_BTREE_UNLINK_PAGE_META
Definition: nbtxlog.h:36
#define XLOG_BTREE_INSERT_META
Definition: nbtxlog.h:29
#define XLOG_BTREE_MARK_PAGE_HALFDEAD
Definition: nbtxlog.h:38
#define XLOG_BTREE_REUSE_PAGE
Definition: nbtxlog.h:40
#define XLOG_BTREE_SPLIT_L
Definition: nbtxlog.h:30
#define XLOG_BTREE_NEWROOT
Definition: nbtxlog.h:37
#define XLOG_BTREE_DELETE
Definition: nbtxlog.h:34
#define InvalidOffsetNumber
Definition: off.h:26
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
uint16 OffsetNumber
Definition: off.h:24
#define OffsetNumberPrev(offsetNumber)
Definition: off.h:54
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
const void size_t len
static char * buf
Definition: pg_test_fsync.c:72
char * intervals[]
void ResolveRecoveryConflictWithSnapshotFullXid(FullTransactionId snapshotConflictHorizon, bool isCatalogRel, RelFileLocator locator)
Definition: standby.c:512
void ResolveRecoveryConflictWithSnapshot(TransactionId snapshotConflictHorizon, bool isCatalogRel, RelFileLocator locator)
Definition: standby.c:468
uint32 btm_last_cleanup_num_delpages
Definition: nbtree.h:115
uint32 btm_level
Definition: nbtree.h:109
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:117
BlockNumber btm_fastroot
Definition: nbtree.h:110
uint32 btm_version
Definition: nbtree.h:107
uint32 btm_magic
Definition: nbtree.h:106
BlockNumber btm_root
Definition: nbtree.h:108
bool btm_allequalimage
Definition: nbtree.h:119
uint32 btm_fastlevel
Definition: nbtree.h:111
BlockNumber btpo_next
Definition: nbtree.h:66
BlockNumber btpo_prev
Definition: nbtree.h:65
uint16 btpo_flags
Definition: nbtree.h:68
uint32 btpo_level
Definition: nbtree.h:67
BTCycleId btpo_cycleid
Definition: nbtree.h:69
uint16 deletetids[FLEXIBLE_ARRAY_MEMBER]
Definition: nbtree.h:922
uint16 ndeletedtids
Definition: nbtree.h:921
IndexTuple itup
Definition: nbtree.h:917
OffsetNumber updatedoffset
Definition: nbtree.h:918
unsigned short t_info
Definition: itup.h:49
XLogRecPtr EndRecPtr
Definition: xlogreader.h:206
Definition: regguts.h:323
uint16 nintervals
Definition: nbtxlog.h:169
TransactionId snapshotConflictHorizon
Definition: nbtxlog.h:238
bool isCatalogRel
Definition: nbtxlog.h:241
uint16 ndeleted
Definition: nbtxlog.h:239
uint16 nupdated
Definition: nbtxlog.h:240
OffsetNumber offnum
Definition: nbtxlog.h:78
uint32 level
Definition: nbtxlog.h:50
uint32 version
Definition: nbtxlog.h:48
bool allequalimage
Definition: nbtxlog.h:54
BlockNumber fastroot
Definition: nbtxlog.h:51
uint32 fastlevel
Definition: nbtxlog.h:52
BlockNumber root
Definition: nbtxlog.h:49
uint32 last_cleanup_num_delpages
Definition: nbtxlog.h:53
uint32 level
Definition: nbtxlog.h:344
FullTransactionId snapshotConflictHorizon
Definition: nbtxlog.h:187
RelFileLocator locator
Definition: nbtxlog.h:185
uint16 postingoff
Definition: nbtxlog.h:155
OffsetNumber firstrightoff
Definition: nbtxlog.h:153
uint32 level
Definition: nbtxlog.h:152
OffsetNumber newitemoff
Definition: nbtxlog.h:154
uint16 ndeletedtids
Definition: nbtxlog.h:263
uint16 ndeleted
Definition: nbtxlog.h:222
uint16 nupdated
Definition: nbtxlog.h:223
static ItemArray items
Definition: test_tidstore.c:48
uint64 XLogRecPtr
Definition: xlogdefs.h:21
bool XLogRecGetBlockTagExtended(XLogReaderState *record, uint8 block_id, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum, Buffer *prefetch_buffer)
Definition: xlogreader.c:2017
char * XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len)
Definition: xlogreader.c:2045
void XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum)
Definition: xlogreader.c:1991
#define XLogRecGetInfo(decoder)
Definition: xlogreader.h:409
#define XLogRecGetData(decoder)
Definition: xlogreader.h:414
#define XLogRecHasBlockRef(decoder, block_id)
Definition: xlogreader.h:419
XLogRedoAction XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id, Buffer *buf)
Definition: xlogutils.c:303
Buffer XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id)
Definition: xlogutils.c:315
XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record, uint8 block_id, ReadBufferMode mode, bool get_cleanup_lock, Buffer *buf)
Definition: xlogutils.c:340
#define InHotStandby
Definition: xlogutils.h:60
@ BLK_NEEDS_REDO
Definition: xlogutils.h:74