PostgreSQL Source Code  git master
nbtpage.c File Reference
#include "postgres.h"
#include "access/nbtree.h"
#include "access/nbtxlog.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "miscadmin.h"
#include "storage/indexfsm.h"
#include "storage/lmgr.h"
#include "storage/predicate.h"
#include "utils/snapmgr.h"
Include dependency graph for nbtpage.c:

Go to the source code of this file.

Functions

static BTMetaPageData_bt_getmeta (Relation rel, Buffer metabuf)
 
static bool _bt_mark_page_halfdead (Relation rel, Buffer buf, BTStack stack)
 
static bool _bt_unlink_halfdead_page (Relation rel, Buffer leafbuf, bool *rightsib_empty)
 
static bool _bt_lock_branch_parent (Relation rel, BlockNumber child, BTStack stack, Buffer *topparent, OffsetNumber *topoff, BlockNumber *target, BlockNumber *rightsib)
 
static void _bt_log_reuse_page (Relation rel, BlockNumber blkno, TransactionId latestRemovedXid)
 
void _bt_initmetapage (Page page, BlockNumber rootbknum, uint32 level)
 
void _bt_upgrademetapage (Page page)
 
void _bt_update_meta_cleanup_info (Relation rel, TransactionId oldestBtpoXact, float8 numHeapTuples)
 
Buffer _bt_getroot (Relation rel, int access)
 
Buffer _bt_gettrueroot (Relation rel)
 
int _bt_getrootheight (Relation rel)
 
bool _bt_heapkeyspace (Relation rel)
 
void _bt_checkpage (Relation rel, Buffer buf)
 
Buffer _bt_getbuf (Relation rel, BlockNumber blkno, int access)
 
Buffer _bt_relandgetbuf (Relation rel, Buffer obuf, BlockNumber blkno, int access)
 
void _bt_relbuf (Relation rel, Buffer buf)
 
void _bt_pageinit (Page page, Size size)
 
bool _bt_page_recyclable (Page page)
 
void _bt_delitems_vacuum (Relation rel, Buffer buf, OffsetNumber *deletable, int ndeletable)
 
void _bt_delitems_delete (Relation rel, Buffer buf, OffsetNumber *deletable, int ndeletable, Relation heapRel)
 
static bool _bt_is_page_halfdead (Relation rel, BlockNumber blk)
 
int _bt_pagedel (Relation rel, Buffer buf)
 

Function Documentation

◆ _bt_checkpage()

void _bt_checkpage ( Relation  rel,
Buffer  buf 
)

Definition at line 690 of file nbtpage.c.

References BufferGetBlockNumber(), BufferGetPage, ereport, errcode(), errhint(), errmsg(), ERROR, MAXALIGN, PageGetSpecialSize, PageIsNew, and RelationGetRelationName.

Referenced by _bt_doinsert(), _bt_getbuf(), _bt_relandgetbuf(), btvacuumpage(), and palloc_btree_page().

691 {
692  Page page = BufferGetPage(buf);
693 
694  /*
695  * ReadBuffer verifies that every newly-read page passes
696  * PageHeaderIsValid, which means it either contains a reasonably sane
697  * page header or is all-zero. We have to defend against the all-zero
698  * case, however.
699  */
700  if (PageIsNew(page))
701  ereport(ERROR,
702  (errcode(ERRCODE_INDEX_CORRUPTED),
703  errmsg("index \"%s\" contains unexpected zero page at block %u",
706  errhint("Please REINDEX it.")));
707 
708  /*
709  * Additionally check that the special area looks sane.
710  */
711  if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BTPageOpaqueData)))
712  ereport(ERROR,
713  (errcode(ERRCODE_INDEX_CORRUPTED),
714  errmsg("index \"%s\" contains corrupted page at block %u",
717  errhint("Please REINDEX it.")));
718 }
int errhint(const char *fmt,...)
Definition: elog.c:1069
int errcode(int sqlerrcode)
Definition: elog.c:608
#define ERROR
Definition: elog.h:43
static char * buf
Definition: pg_test_fsync.c:67
#define RelationGetRelationName(relation)
Definition: rel.h:462
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define ereport(elevel, rest)
Definition: elog.h:141
#define MAXALIGN(LEN)
Definition: c.h:692
#define PageGetSpecialSize(page)
Definition: bufpage.h:300
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2623
#define PageIsNew(page)
Definition: bufpage.h:229
int errmsg(const char *fmt,...)
Definition: elog.c:822
Pointer Page
Definition: bufpage.h:78

◆ _bt_delitems_delete()

void _bt_delitems_delete ( Relation  rel,
Buffer  buf,
OffsetNumber deletable,
int  ndeletable,
Relation  heapRel 
)

Definition at line 1055 of file nbtpage.c.

References Assert, BTP_HAS_GARBAGE, BTPageOpaqueData::btpo_flags, BufferGetPage, END_CRIT_SECTION, index_compute_xid_horizon_for_tuples(), InvalidTransactionId, xl_btree_delete::latestRemovedXid, MarkBufferDirty(), xl_btree_delete::ndeleted, PageGetSpecialPointer, PageIndexMultiDelete(), PageSetLSN, REGBUF_STANDARD, RelationNeedsWAL, SizeOfBtreeDelete, START_CRIT_SECTION, XLOG_BTREE_DELETE, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), XLogRegisterData(), and XLogStandbyInfoActive.

Referenced by _bt_vacuum_one_page().

1058 {
1059  Page page = BufferGetPage(buf);
1060  BTPageOpaque opaque;
1061  TransactionId latestRemovedXid = InvalidTransactionId;
1062 
1063  /* Shouldn't be called unless there's something to do */
1064  Assert(ndeletable > 0);
1065 
1067  latestRemovedXid =
1069  deletable, ndeletable);
1070 
1071  /* No ereport(ERROR) until changes are logged */
1073 
1074  /* Fix the page */
1075  PageIndexMultiDelete(page, deletable, ndeletable);
1076 
1077  /*
1078  * Unlike _bt_delitems_vacuum, we *must not* clear the vacuum cycle ID,
1079  * because this is not called by VACUUM. Just clear the BTP_HAS_GARBAGE
1080  * page flag, since we deleted all items with their LP_DEAD bit set.
1081  */
1082  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1083  opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
1084 
1086 
1087  /* XLOG stuff */
1088  if (RelationNeedsWAL(rel))
1089  {
1090  XLogRecPtr recptr;
1091  xl_btree_delete xlrec_delete;
1092 
1093  xlrec_delete.latestRemovedXid = latestRemovedXid;
1094  xlrec_delete.ndeleted = ndeletable;
1095 
1096  XLogBeginInsert();
1098  XLogRegisterData((char *) &xlrec_delete, SizeOfBtreeDelete);
1099 
1100  /*
1101  * The deletable array is not in the buffer, but pretend that it is.
1102  * When XLogInsert stores the whole buffer, the array need not be
1103  * stored too.
1104  */
1105  XLogRegisterBufData(0, (char *) deletable,
1106  ndeletable * sizeof(OffsetNumber));
1107 
1108  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_DELETE);
1109 
1110  PageSetLSN(page, recptr);
1111  }
1112 
1113  END_CRIT_SECTION();
1114 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:361
TransactionId latestRemovedXid
Definition: nbtxlog.h:128
uint32 TransactionId
Definition: c.h:514
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1458
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
uint32 ndeleted
Definition: nbtxlog.h:129
uint16 OffsetNumber
Definition: off.h:24
static char * buf
Definition: pg_test_fsync.c:67
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define InvalidTransactionId
Definition: transam.h:31
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define XLOG_BTREE_DELETE
Definition: nbtxlog.h:32
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
#define XLogStandbyInfoActive()
Definition: xlog.h:195
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:739
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:835
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define RelationNeedsWAL(relation)
Definition: rel.h:530
#define SizeOfBtreeDelete
Definition: nbtxlog.h:134
void XLogBeginInsert(void)
Definition: xloginsert.c:120
uint16 btpo_flags
Definition: nbtree.h:65
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
#define BTP_HAS_GARBAGE
Definition: nbtree.h:78
Pointer Page
Definition: bufpage.h:78
TransactionId index_compute_xid_horizon_for_tuples(Relation irel, Relation hrel, Buffer ibuf, OffsetNumber *itemnos, int nitems)
Definition: genam.c:281

◆ _bt_delitems_vacuum()

void _bt_delitems_vacuum ( Relation  rel,
Buffer  buf,
OffsetNumber deletable,
int  ndeletable 
)

Definition at line 975 of file nbtpage.c.

References Assert, BTP_HAS_GARBAGE, BTPageOpaqueData::btpo_cycleid, BTPageOpaqueData::btpo_flags, BufferGetPage, END_CRIT_SECTION, MarkBufferDirty(), xl_btree_vacuum::ndeleted, PageGetSpecialPointer, PageIndexMultiDelete(), PageSetLSN, REGBUF_STANDARD, RelationNeedsWAL, SizeOfBtreeVacuum, START_CRIT_SECTION, XLOG_BTREE_VACUUM, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by btvacuumpage().

977 {
978  Page page = BufferGetPage(buf);
979  BTPageOpaque opaque;
980 
981  /* Shouldn't be called unless there's something to do */
982  Assert(ndeletable > 0);
983 
984  /* No ereport(ERROR) until changes are logged */
986 
987  /* Fix the page */
988  PageIndexMultiDelete(page, deletable, ndeletable);
989 
990  /*
991  * We can clear the vacuum cycle ID since this page has certainly been
992  * processed by the current vacuum scan.
993  */
994  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
995  opaque->btpo_cycleid = 0;
996 
997  /*
998  * Mark the page as not containing any LP_DEAD items. This is not
999  * certainly true (there might be some that have recently been marked, but
1000  * weren't targeted by VACUUM's heap scan), but it will be true often
1001  * enough. VACUUM does not delete items purely because they have their
1002  * LP_DEAD bit set, since doing so would necessitate explicitly logging a
1003  * latestRemovedXid cutoff (this is how _bt_delitems_delete works).
1004  *
1005  * The consequences of falsely unsetting BTP_HAS_GARBAGE should be fairly
1006  * limited, since we never falsely unset an LP_DEAD bit. Workloads that
1007  * are particularly dependent on LP_DEAD bits being set quickly will
1008  * usually manage to set the BTP_HAS_GARBAGE flag before the page fills up
1009  * again anyway.
1010  */
1011  opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
1012 
1014 
1015  /* XLOG stuff */
1016  if (RelationNeedsWAL(rel))
1017  {
1018  XLogRecPtr recptr;
1019  xl_btree_vacuum xlrec_vacuum;
1020 
1021  xlrec_vacuum.ndeleted = ndeletable;
1022 
1023  XLogBeginInsert();
1025  XLogRegisterData((char *) &xlrec_vacuum, SizeOfBtreeVacuum);
1026 
1027  /*
1028  * The deletable array is not in the buffer, but pretend that it is.
1029  * When XLogInsert stores the whole buffer, the array need not be
1030  * stored too.
1031  */
1032  XLogRegisterBufData(0, (char *) deletable,
1033  ndeletable * sizeof(OffsetNumber));
1034 
1035  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_VACUUM);
1036 
1037  PageSetLSN(page, recptr);
1038  }
1039 
1040  END_CRIT_SECTION();
1041 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:361
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1458
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
uint16 OffsetNumber
Definition: off.h:24
#define SizeOfBtreeVacuum
Definition: nbtxlog.h:167
BTCycleId btpo_cycleid
Definition: nbtree.h:66
static char * buf
Definition: pg_test_fsync.c:67
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define XLOG_BTREE_VACUUM
Definition: nbtxlog.h:37
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:739
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:835
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define RelationNeedsWAL(relation)
Definition: rel.h:530
void XLogBeginInsert(void)
Definition: xloginsert.c:120
uint16 btpo_flags
Definition: nbtree.h:65
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
uint32 ndeleted
Definition: nbtxlog.h:162
#define BTP_HAS_GARBAGE
Definition: nbtree.h:78
Pointer Page
Definition: bufpage.h:78

◆ _bt_getbuf()

Buffer _bt_getbuf ( Relation  rel,
BlockNumber  blkno,
int  access 
)

Definition at line 757 of file nbtpage.c.

References _bt_checkpage(), _bt_log_reuse_page(), _bt_page_recyclable(), _bt_pageinit(), _bt_relbuf(), Assert, BT_WRITE, BTPageOpaqueData::btpo, buf, BufferGetPage, BufferGetPageSize, ConditionalLockBuffer(), DEBUG2, elog, ExclusiveLock, GetFreeIndexPage(), InvalidBlockNumber, LockBuffer(), LockRelationForExtension(), P_NEW, PageGetSpecialPointer, PageIsNew, ReadBuffer(), RELATION_IS_LOCAL, RelationNeedsWAL, ReleaseBuffer(), UnlockRelationForExtension(), BTPageOpaqueData::xact, and XLogStandbyInfoActive.

Referenced by _bt_finish_split(), _bt_getroot(), _bt_getrootheight(), _bt_getstackbuf(), _bt_gettrueroot(), _bt_heapkeyspace(), _bt_insertonpg(), _bt_is_page_halfdead(), _bt_killitems(), _bt_lock_branch_parent(), _bt_moveright(), _bt_newroot(), _bt_pagedel(), _bt_readnextpage(), _bt_split(), _bt_unlink_halfdead_page(), _bt_update_meta_cleanup_info(), _bt_vacuum_needs_cleanup(), and _bt_walk_left().

758 {
759  Buffer buf;
760 
761  if (blkno != P_NEW)
762  {
763  /* Read an existing block of the relation */
764  buf = ReadBuffer(rel, blkno);
765  LockBuffer(buf, access);
766  _bt_checkpage(rel, buf);
767  }
768  else
769  {
770  bool needLock;
771  Page page;
772 
773  Assert(access == BT_WRITE);
774 
775  /*
776  * First see if the FSM knows of any free pages.
777  *
778  * We can't trust the FSM's report unreservedly; we have to check that
779  * the page is still free. (For example, an already-free page could
780  * have been re-used between the time the last VACUUM scanned it and
781  * the time the VACUUM made its FSM updates.)
782  *
783  * In fact, it's worse than that: we can't even assume that it's safe
784  * to take a lock on the reported page. If somebody else has a lock
785  * on it, or even worse our own caller does, we could deadlock. (The
786  * own-caller scenario is actually not improbable. Consider an index
787  * on a serial or timestamp column. Nearly all splits will be at the
788  * rightmost page, so it's entirely likely that _bt_split will call us
789  * while holding a lock on the page most recently acquired from FSM. A
790  * VACUUM running concurrently with the previous split could well have
791  * placed that page back in FSM.)
792  *
793  * To get around that, we ask for only a conditional lock on the
794  * reported page. If we fail, then someone else is using the page,
795  * and we may reasonably assume it's not free. (If we happen to be
796  * wrong, the worst consequence is the page will be lost to use till
797  * the next VACUUM, which is no big problem.)
798  */
799  for (;;)
800  {
801  blkno = GetFreeIndexPage(rel);
802  if (blkno == InvalidBlockNumber)
803  break;
804  buf = ReadBuffer(rel, blkno);
805  if (ConditionalLockBuffer(buf))
806  {
807  page = BufferGetPage(buf);
808  if (_bt_page_recyclable(page))
809  {
810  /*
811  * If we are generating WAL for Hot Standby then create a
812  * WAL record that will allow us to conflict with queries
813  * running on standby, in case they have snapshots older
814  * than btpo.xact. This can only apply if the page does
815  * have a valid btpo.xact value, ie not if it's new. (We
816  * must check that because an all-zero page has no special
817  * space.)
818  */
819  if (XLogStandbyInfoActive() && RelationNeedsWAL(rel) &&
820  !PageIsNew(page))
821  {
823 
824  _bt_log_reuse_page(rel, blkno, opaque->btpo.xact);
825  }
826 
827  /* Okay to use page. Re-initialize and return it */
828  _bt_pageinit(page, BufferGetPageSize(buf));
829  return buf;
830  }
831  elog(DEBUG2, "FSM returned nonrecyclable page");
832  _bt_relbuf(rel, buf);
833  }
834  else
835  {
836  elog(DEBUG2, "FSM returned nonlockable page");
837  /* couldn't get lock, so just drop pin */
838  ReleaseBuffer(buf);
839  }
840  }
841 
842  /*
843  * Extend the relation by one page.
844  *
845  * We have to use a lock to ensure no one else is extending the rel at
846  * the same time, else we will both try to initialize the same new
847  * page. We can skip locking for new or temp relations, however,
848  * since no one else could be accessing them.
849  */
850  needLock = !RELATION_IS_LOCAL(rel);
851 
852  if (needLock)
854 
855  buf = ReadBuffer(rel, P_NEW);
856 
857  /* Acquire buffer lock on new page */
858  LockBuffer(buf, BT_WRITE);
859 
860  /*
861  * Release the file-extension lock; it's now OK for someone else to
862  * extend the relation some more. Note that we cannot release this
863  * lock before we have buffer lock on the new page, or we risk a race
864  * condition against btvacuumscan --- see comments therein.
865  */
866  if (needLock)
868 
869  /* Initialize the new page before returning it */
870  page = BufferGetPage(buf);
871  Assert(PageIsNew(page));
872  _bt_pageinit(page, BufferGetPageSize(buf));
873  }
874 
875  /* ref count and lock type are correct */
876  return buf;
877 }
#define ExclusiveLock
Definition: lockdefs.h:44
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:548
union BTPageOpaqueData::@46 btpo
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3375
#define P_NEW
Definition: bufmgr.h:81
bool _bt_page_recyclable(Page page)
Definition: nbtpage.c:939
TransactionId xact
Definition: nbtree.h:63
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
void _bt_checkpage(Relation rel, Buffer buf)
Definition: nbtpage.c:690
#define DEBUG2
Definition: elog.h:24
static char * buf
Definition: pg_test_fsync.c:67
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3638
static void _bt_log_reuse_page(Relation rel, BlockNumber blkno, TransactionId latestRemovedXid)
Definition: nbtpage.c:724
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:402
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:452
#define BufferGetPageSize(buffer)
Definition: bufmgr.h:146
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3612
#define XLogStandbyInfoActive()
Definition: xlog.h:195
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
#define Assert(condition)
Definition: c.h:739
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:596
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define InvalidBlockNumber
Definition: block.h:33
#define RelationNeedsWAL(relation)
Definition: rel.h:530
#define PageIsNew(page)
Definition: bufpage.h:229
#define elog(elevel,...)
Definition: elog.h:228
void _bt_pageinit(Page page, Size size)
Definition: nbtpage.c:924
#define BT_WRITE
Definition: nbtree.h:403
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78

◆ _bt_getmeta()

static BTMetaPageData * _bt_getmeta ( Relation  rel,
Buffer  metabuf 
)
static

Definition at line 120 of file nbtpage.c.

References BTMetaPageData::btm_magic, BTMetaPageData::btm_version, BTPageGetMeta, BTREE_MAGIC, BTREE_MIN_VERSION, BTREE_VERSION, BufferGetPage, ereport, errcode(), errmsg(), ERROR, P_ISMETA, PageGetSpecialPointer, and RelationGetRelationName.

Referenced by _bt_getroot(), _bt_getrootheight(), and _bt_heapkeyspace().

121 {
122  Page metapg;
123  BTPageOpaque metaopaque;
124  BTMetaPageData *metad;
125 
126  metapg = BufferGetPage(metabuf);
127  metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
128  metad = BTPageGetMeta(metapg);
129 
130  /* sanity-check the metapage */
131  if (!P_ISMETA(metaopaque) ||
132  metad->btm_magic != BTREE_MAGIC)
133  ereport(ERROR,
134  (errcode(ERRCODE_INDEX_CORRUPTED),
135  errmsg("index \"%s\" is not a btree",
136  RelationGetRelationName(rel))));
137 
138  if (metad->btm_version < BTREE_MIN_VERSION ||
139  metad->btm_version > BTREE_VERSION)
140  ereport(ERROR,
141  (errcode(ERRCODE_INDEX_CORRUPTED),
142  errmsg("version mismatch in index \"%s\": file version %d, "
143  "current version %d, minimal supported version %d",
146 
147  return metad;
148 }
uint32 btm_version
Definition: nbtree.h:101
#define BTREE_VERSION
Definition: nbtree.h:134
uint32 btm_magic
Definition: nbtree.h:100
int errcode(int sqlerrcode)
Definition: elog.c:608
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
#define P_ISMETA(opaque)
Definition: nbtree.h:193
#define BTREE_MAGIC
Definition: nbtree.h:133
#define ERROR
Definition: elog.h:43
#define BTPageGetMeta(p)
Definition: nbtree.h:113
#define RelationGetRelationName(relation)
Definition: rel.h:462
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define ereport(elevel, rest)
Definition: elog.h:141
#define BTREE_MIN_VERSION
Definition: nbtree.h:135
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
int errmsg(const char *fmt,...)
Definition: elog.c:822
Pointer Page
Definition: bufpage.h:78

◆ _bt_getroot()

Buffer _bt_getroot ( Relation  rel,
int  access 
)

Definition at line 255 of file nbtpage.c.

References _bt_getbuf(), _bt_getmeta(), _bt_getroot(), _bt_relandgetbuf(), _bt_relbuf(), _bt_upgrademetapage(), Assert, BT_READ, BT_WRITE, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_last_cleanup_num_heap_tuples, BTMetaPageData::btm_level, BTMetaPageData::btm_magic, BTMetaPageData::btm_oldest_btpo_xact, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTP_LEAF, BTP_ROOT, BTPageOpaqueData::btpo, BTPageOpaqueData::btpo_cycleid, BTPageOpaqueData::btpo_flags, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_NOVAC_VERSION, BTREE_VERSION, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage, elog, END_CRIT_SECTION, ERROR, xl_btree_metadata::fastlevel, xl_btree_metadata::fastroot, InvalidBuffer, InvalidTransactionId, xl_btree_metadata::last_cleanup_num_heap_tuples, xl_btree_metadata::level, BTPageOpaqueData::level, xl_btree_newroot::level, LockBuffer(), MarkBufferDirty(), MemoryContextAlloc(), xl_btree_metadata::oldest_btpo_xact, P_IGNORE, P_LEFTMOST, P_NEW, P_NONE, P_RIGHTMOST, PageGetSpecialPointer, PageSetLSN, pfree(), RelationData::rd_amcache, RelationData::rd_indexcxt, REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetRelationName, RelationNeedsWAL, xl_btree_metadata::root, xl_btree_newroot::rootblk, SizeOfBtreeNewroot, START_CRIT_SECTION, xl_btree_metadata::version, XLOG_BTREE_NEWROOT, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_get_endpoint(), _bt_getroot(), and _bt_search().

256 {
257  Buffer metabuf;
258  Buffer rootbuf;
259  Page rootpage;
260  BTPageOpaque rootopaque;
261  BlockNumber rootblkno;
262  uint32 rootlevel;
263  BTMetaPageData *metad;
264 
265  /*
266  * Try to use previously-cached metapage data to find the root. This
267  * normally saves one buffer access per index search, which is a very
268  * helpful savings in bufmgr traffic and hence contention.
269  */
270  if (rel->rd_amcache != NULL)
271  {
272  metad = (BTMetaPageData *) rel->rd_amcache;
273  /* We shouldn't have cached it if any of these fail */
274  Assert(metad->btm_magic == BTREE_MAGIC);
276  Assert(metad->btm_version <= BTREE_VERSION);
277  Assert(metad->btm_root != P_NONE);
278 
279  rootblkno = metad->btm_fastroot;
280  Assert(rootblkno != P_NONE);
281  rootlevel = metad->btm_fastlevel;
282 
283  rootbuf = _bt_getbuf(rel, rootblkno, BT_READ);
284  rootpage = BufferGetPage(rootbuf);
285  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
286 
287  /*
288  * Since the cache might be stale, we check the page more carefully
289  * here than normal. We *must* check that it's not deleted. If it's
290  * not alone on its level, then we reject too --- this may be overly
291  * paranoid but better safe than sorry. Note we don't check P_ISROOT,
292  * because that's not set in a "fast root".
293  */
294  if (!P_IGNORE(rootopaque) &&
295  rootopaque->btpo.level == rootlevel &&
296  P_LEFTMOST(rootopaque) &&
297  P_RIGHTMOST(rootopaque))
298  {
299  /* OK, accept cached page as the root */
300  return rootbuf;
301  }
302  _bt_relbuf(rel, rootbuf);
303  /* Cache is stale, throw it away */
304  if (rel->rd_amcache)
305  pfree(rel->rd_amcache);
306  rel->rd_amcache = NULL;
307  }
308 
309  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
310  metad = _bt_getmeta(rel, metabuf);
311 
312  /* if no root page initialized yet, do it */
313  if (metad->btm_root == P_NONE)
314  {
315  Page metapg;
316 
317  /* If access = BT_READ, caller doesn't want us to create root yet */
318  if (access == BT_READ)
319  {
320  _bt_relbuf(rel, metabuf);
321  return InvalidBuffer;
322  }
323 
324  /* trade in our read lock for a write lock */
325  LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
326  LockBuffer(metabuf, BT_WRITE);
327 
328  /*
329  * Race condition: if someone else initialized the metadata between
330  * the time we released the read lock and acquired the write lock, we
331  * must avoid doing it again.
332  */
333  if (metad->btm_root != P_NONE)
334  {
335  /*
336  * Metadata initialized by someone else. In order to guarantee no
337  * deadlocks, we have to release the metadata page and start all
338  * over again. (Is that really true? But it's hardly worth trying
339  * to optimize this case.)
340  */
341  _bt_relbuf(rel, metabuf);
342  return _bt_getroot(rel, access);
343  }
344 
345  /*
346  * Get, initialize, write, and leave a lock of the appropriate type on
347  * the new root page. Since this is the first page in the tree, it's
348  * a leaf as well as the root.
349  */
350  rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE);
351  rootblkno = BufferGetBlockNumber(rootbuf);
352  rootpage = BufferGetPage(rootbuf);
353  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
354  rootopaque->btpo_prev = rootopaque->btpo_next = P_NONE;
355  rootopaque->btpo_flags = (BTP_LEAF | BTP_ROOT);
356  rootopaque->btpo.level = 0;
357  rootopaque->btpo_cycleid = 0;
358  /* Get raw page pointer for metapage */
359  metapg = BufferGetPage(metabuf);
360 
361  /* NO ELOG(ERROR) till meta is updated */
363 
364  /* upgrade metapage if needed */
365  if (metad->btm_version < BTREE_NOVAC_VERSION)
366  _bt_upgrademetapage(metapg);
367 
368  metad->btm_root = rootblkno;
369  metad->btm_level = 0;
370  metad->btm_fastroot = rootblkno;
371  metad->btm_fastlevel = 0;
373  metad->btm_last_cleanup_num_heap_tuples = -1.0;
374 
375  MarkBufferDirty(rootbuf);
376  MarkBufferDirty(metabuf);
377 
378  /* XLOG stuff */
379  if (RelationNeedsWAL(rel))
380  {
381  xl_btree_newroot xlrec;
382  XLogRecPtr recptr;
384 
385  XLogBeginInsert();
388 
390  md.version = metad->btm_version;
391  md.root = rootblkno;
392  md.level = 0;
393  md.fastroot = rootblkno;
394  md.fastlevel = 0;
397 
398  XLogRegisterBufData(2, (char *) &md, sizeof(xl_btree_metadata));
399 
400  xlrec.rootblk = rootblkno;
401  xlrec.level = 0;
402 
403  XLogRegisterData((char *) &xlrec, SizeOfBtreeNewroot);
404 
405  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_NEWROOT);
406 
407  PageSetLSN(rootpage, recptr);
408  PageSetLSN(metapg, recptr);
409  }
410 
412 
413  /*
414  * swap root write lock for read lock. There is no danger of anyone
415  * else accessing the new root page while it's unlocked, since no one
416  * else knows where it is yet.
417  */
418  LockBuffer(rootbuf, BUFFER_LOCK_UNLOCK);
419  LockBuffer(rootbuf, BT_READ);
420 
421  /* okay, metadata is correct, release lock on it without caching */
422  _bt_relbuf(rel, metabuf);
423  }
424  else
425  {
426  rootblkno = metad->btm_fastroot;
427  Assert(rootblkno != P_NONE);
428  rootlevel = metad->btm_fastlevel;
429 
430  /*
431  * Cache the metapage data for next time
432  */
434  sizeof(BTMetaPageData));
435  memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
436 
437  /*
438  * We are done with the metapage; arrange to release it via first
439  * _bt_relandgetbuf call
440  */
441  rootbuf = metabuf;
442 
443  for (;;)
444  {
445  rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, BT_READ);
446  rootpage = BufferGetPage(rootbuf);
447  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
448 
449  if (!P_IGNORE(rootopaque))
450  break;
451 
452  /* it's dead, Jim. step right one page */
453  if (P_RIGHTMOST(rootopaque))
454  elog(ERROR, "no live root page found in index \"%s\"",
456  rootblkno = rootopaque->btpo_next;
457  }
458 
459  /* Note: can't check btpo.level on deleted pages */
460  if (rootopaque->btpo.level != rootlevel)
461  elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
462  rootblkno, RelationGetRelationName(rel),
463  rootopaque->btpo.level, rootlevel);
464  }
465 
466  /*
467  * By here, we have a pin and read lock on the root page, and no lock set
468  * on the metadata page. Return the root page's buffer.
469  */
470  return rootbuf;
471 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:361
BlockNumber rootblk
Definition: nbtxlog.h:235
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:86
#define BTP_ROOT
Definition: nbtree.h:73
BlockNumber btpo_next
Definition: nbtree.h:59
static BTMetaPageData * _bt_getmeta(Relation rel, Buffer metabuf)
Definition: nbtpage.c:120
#define P_IGNORE(opaque)
Definition: nbtree.h:195
void _bt_upgrademetapage(Page page)
Definition: nbtpage.c:88
uint32 btm_version
Definition: nbtree.h:101
Buffer _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access)
Definition: nbtpage.c:893
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:757
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1458
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define BTREE_VERSION
Definition: nbtree.h:134
uint32 btm_magic
Definition: nbtree.h:100
#define BTP_LEAF
Definition: nbtree.h:72
union BTPageOpaqueData::@46 btpo
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
BlockNumber root
Definition: nbtxlog.h:50
#define P_NONE
Definition: nbtree.h:182
#define InvalidBuffer
Definition: buf.h:25
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
uint32 level
Definition: nbtxlog.h:236
uint32 BlockNumber
Definition: block.h:31
#define P_NEW
Definition: bufmgr.h:81
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
#define BT_READ
Definition: nbtree.h:402
BlockNumber btm_fastroot
Definition: nbtree.h:104
#define XLOG_BTREE_NEWROOT
Definition: nbtxlog.h:35
void pfree(void *pointer)
Definition: mcxt.c:1056
#define BTREE_MAGIC
Definition: nbtree.h:133
#define ERROR
Definition: elog.h:43
float8 last_cleanup_num_heap_tuples
Definition: nbtxlog.h:55
BTCycleId btpo_cycleid
Definition: nbtree.h:66
TransactionId oldest_btpo_xact
Definition: nbtxlog.h:54
BlockNumber btpo_prev
Definition: nbtree.h:58
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define InvalidTransactionId
Definition: transam.h:31
#define RelationGetRelationName(relation)
Definition: rel.h:462
#define P_LEFTMOST(opaque)
Definition: nbtree.h:188
unsigned int uint32
Definition: c.h:359
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:136
Buffer _bt_getroot(Relation rel, int access)
Definition: nbtpage.c:255
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define BTREE_METAPAGE
Definition: nbtree.h:132
#define SizeOfBtreeNewroot
Definition: nbtxlog.h:239
uint32 version
Definition: nbtxlog.h:49
uint32 btm_fastlevel
Definition: nbtree.h:105
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
uint32 level
Definition: nbtree.h:62
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3612
BlockNumber btm_root
Definition: nbtree.h:102
#define BTREE_MIN_VERSION
Definition: nbtree.h:135
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:739
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:109
#define RelationNeedsWAL(relation)
Definition: rel.h:530
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2623
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
uint32 fastlevel
Definition: nbtxlog.h:53
uint32 btm_level
Definition: nbtree.h:103
#define elog(elevel,...)
Definition: elog.h:228
MemoryContext rd_indexcxt
Definition: rel.h:161
uint32 level
Definition: nbtxlog.h:51
BlockNumber fastroot
Definition: nbtxlog.h:52
TransactionId btm_oldest_btpo_xact
Definition: nbtree.h:107
void * rd_amcache
Definition: rel.h:185
#define BT_WRITE
Definition: nbtree.h:403
void XLogBeginInsert(void)
Definition: xloginsert.c:120
uint16 btpo_flags
Definition: nbtree.h:65
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:189
Pointer Page
Definition: bufpage.h:78

◆ _bt_getrootheight()

int _bt_getrootheight ( Relation  rel)

Definition at line 584 of file nbtpage.c.

References _bt_getbuf(), _bt_getmeta(), _bt_relbuf(), Assert, BT_READ, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_magic, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_VERSION, MemoryContextAlloc(), P_NONE, RelationData::rd_amcache, and RelationData::rd_indexcxt.

Referenced by _bt_insertonpg(), and get_relation_info().

585 {
586  BTMetaPageData *metad;
587 
588  if (rel->rd_amcache == NULL)
589  {
590  Buffer metabuf;
591 
592  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
593  metad = _bt_getmeta(rel, metabuf);
594 
595  /*
596  * If there's no root page yet, _bt_getroot() doesn't expect a cache
597  * to be made, so just stop here and report the index height is zero.
598  * (XXX perhaps _bt_getroot() should be changed to allow this case.)
599  */
600  if (metad->btm_root == P_NONE)
601  {
602  _bt_relbuf(rel, metabuf);
603  return 0;
604  }
605 
606  /*
607  * Cache the metapage data for next time
608  */
610  sizeof(BTMetaPageData));
611  memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
612  _bt_relbuf(rel, metabuf);
613  }
614 
615  /* Get cached page */
616  metad = (BTMetaPageData *) rel->rd_amcache;
617  /* We shouldn't have cached it if any of these fail */
618  Assert(metad->btm_magic == BTREE_MAGIC);
620  Assert(metad->btm_version <= BTREE_VERSION);
621  Assert(metad->btm_fastroot != P_NONE);
622 
623  return metad->btm_fastlevel;
624 }
static BTMetaPageData * _bt_getmeta(Relation rel, Buffer metabuf)
Definition: nbtpage.c:120
uint32 btm_version
Definition: nbtree.h:101
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:757
#define BTREE_VERSION
Definition: nbtree.h:134
uint32 btm_magic
Definition: nbtree.h:100
#define P_NONE
Definition: nbtree.h:182
#define BT_READ
Definition: nbtree.h:402
BlockNumber btm_fastroot
Definition: nbtree.h:104
#define BTREE_MAGIC
Definition: nbtree.h:133
#define BTREE_METAPAGE
Definition: nbtree.h:132
uint32 btm_fastlevel
Definition: nbtree.h:105
BlockNumber btm_root
Definition: nbtree.h:102
#define BTREE_MIN_VERSION
Definition: nbtree.h:135
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
#define Assert(condition)
Definition: c.h:739
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
MemoryContext rd_indexcxt
Definition: rel.h:161
void * rd_amcache
Definition: rel.h:185
int Buffer
Definition: buf.h:23

◆ _bt_gettrueroot()

Buffer _bt_gettrueroot ( Relation  rel)

Definition at line 488 of file nbtpage.c.

References _bt_getbuf(), _bt_relandgetbuf(), _bt_relbuf(), BT_READ, BTMetaPageData::btm_level, BTMetaPageData::btm_magic, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTPageGetMeta, BTPageOpaqueData::btpo, BTPageOpaqueData::btpo_next, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_VERSION, BufferGetPage, elog, ereport, errcode(), errmsg(), ERROR, InvalidBuffer, BTPageOpaqueData::level, P_IGNORE, P_ISMETA, P_NONE, P_RIGHTMOST, PageGetSpecialPointer, pfree(), RelationData::rd_amcache, and RelationGetRelationName.

Referenced by _bt_get_endpoint().

489 {
490  Buffer metabuf;
491  Page metapg;
492  BTPageOpaque metaopaque;
493  Buffer rootbuf;
494  Page rootpage;
495  BTPageOpaque rootopaque;
496  BlockNumber rootblkno;
497  uint32 rootlevel;
498  BTMetaPageData *metad;
499 
500  /*
501  * We don't try to use cached metapage data here, since (a) this path is
502  * not performance-critical, and (b) if we are here it suggests our cache
503  * is out-of-date anyway. In light of point (b), it's probably safest to
504  * actively flush any cached metapage info.
505  */
506  if (rel->rd_amcache)
507  pfree(rel->rd_amcache);
508  rel->rd_amcache = NULL;
509 
510  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
511  metapg = BufferGetPage(metabuf);
512  metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
513  metad = BTPageGetMeta(metapg);
514 
515  if (!P_ISMETA(metaopaque) ||
516  metad->btm_magic != BTREE_MAGIC)
517  ereport(ERROR,
518  (errcode(ERRCODE_INDEX_CORRUPTED),
519  errmsg("index \"%s\" is not a btree",
520  RelationGetRelationName(rel))));
521 
522  if (metad->btm_version < BTREE_MIN_VERSION ||
523  metad->btm_version > BTREE_VERSION)
524  ereport(ERROR,
525  (errcode(ERRCODE_INDEX_CORRUPTED),
526  errmsg("version mismatch in index \"%s\": file version %d, "
527  "current version %d, minimal supported version %d",
530 
531  /* if no root page initialized yet, fail */
532  if (metad->btm_root == P_NONE)
533  {
534  _bt_relbuf(rel, metabuf);
535  return InvalidBuffer;
536  }
537 
538  rootblkno = metad->btm_root;
539  rootlevel = metad->btm_level;
540 
541  /*
542  * We are done with the metapage; arrange to release it via first
543  * _bt_relandgetbuf call
544  */
545  rootbuf = metabuf;
546 
547  for (;;)
548  {
549  rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, BT_READ);
550  rootpage = BufferGetPage(rootbuf);
551  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
552 
553  if (!P_IGNORE(rootopaque))
554  break;
555 
556  /* it's dead, Jim. step right one page */
557  if (P_RIGHTMOST(rootopaque))
558  elog(ERROR, "no live root page found in index \"%s\"",
560  rootblkno = rootopaque->btpo_next;
561  }
562 
563  /* Note: can't check btpo.level on deleted pages */
564  if (rootopaque->btpo.level != rootlevel)
565  elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
566  rootblkno, RelationGetRelationName(rel),
567  rootopaque->btpo.level, rootlevel);
568 
569  return rootbuf;
570 }
BlockNumber btpo_next
Definition: nbtree.h:59
#define P_IGNORE(opaque)
Definition: nbtree.h:195
uint32 btm_version
Definition: nbtree.h:101
Buffer _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access)
Definition: nbtpage.c:893
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:757
#define BTREE_VERSION
Definition: nbtree.h:134
uint32 btm_magic
Definition: nbtree.h:100
union BTPageOpaqueData::@46 btpo
#define P_NONE
Definition: nbtree.h:182
#define InvalidBuffer
Definition: buf.h:25
int errcode(int sqlerrcode)
Definition: elog.c:608
uint32 BlockNumber
Definition: block.h:31
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
#define P_ISMETA(opaque)
Definition: nbtree.h:193
#define BT_READ
Definition: nbtree.h:402
void pfree(void *pointer)
Definition: mcxt.c:1056
#define BTREE_MAGIC
Definition: nbtree.h:133
#define ERROR
Definition: elog.h:43
#define BTPageGetMeta(p)
Definition: nbtree.h:113
#define RelationGetRelationName(relation)
Definition: rel.h:462
unsigned int uint32
Definition: c.h:359
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define ereport(elevel, rest)
Definition: elog.h:141
#define BTREE_METAPAGE
Definition: nbtree.h:132
uint32 level
Definition: nbtree.h:62
BlockNumber btm_root
Definition: nbtree.h:102
#define BTREE_MIN_VERSION
Definition: nbtree.h:135
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
int errmsg(const char *fmt,...)
Definition: elog.c:822
uint32 btm_level
Definition: nbtree.h:103
#define elog(elevel,...)
Definition: elog.h:228
void * rd_amcache
Definition: rel.h:185
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:189
Pointer Page
Definition: bufpage.h:78

◆ _bt_heapkeyspace()

bool _bt_heapkeyspace ( Relation  rel)

Definition at line 636 of file nbtpage.c.

References _bt_getbuf(), _bt_getmeta(), _bt_relbuf(), Assert, BT_READ, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_magic, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_NOVAC_VERSION, BTREE_VERSION, MemoryContextAlloc(), P_NONE, RelationData::rd_amcache, and RelationData::rd_indexcxt.

Referenced by _bt_first(), _bt_mkscankey(), and bt_index_check_internal().

637 {
638  BTMetaPageData *metad;
639 
640  if (rel->rd_amcache == NULL)
641  {
642  Buffer metabuf;
643 
644  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
645  metad = _bt_getmeta(rel, metabuf);
646 
647  /*
648  * If there's no root page yet, _bt_getroot() doesn't expect a cache
649  * to be made, so just stop here. (XXX perhaps _bt_getroot() should
650  * be changed to allow this case.)
651  */
652  if (metad->btm_root == P_NONE)
653  {
654  uint32 btm_version = metad->btm_version;
655 
656  _bt_relbuf(rel, metabuf);
657  return btm_version > BTREE_NOVAC_VERSION;
658  }
659 
660  /*
661  * Cache the metapage data for next time
662  *
663  * An on-the-fly version upgrade performed by _bt_upgrademetapage()
664  * can change the nbtree version for an index without invalidating any
665  * local cache. This is okay because it can only happen when moving
666  * from version 2 to version 3, both of which are !heapkeyspace
667  * versions.
668  */
670  sizeof(BTMetaPageData));
671  memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
672  _bt_relbuf(rel, metabuf);
673  }
674 
675  /* Get cached page */
676  metad = (BTMetaPageData *) rel->rd_amcache;
677  /* We shouldn't have cached it if any of these fail */
678  Assert(metad->btm_magic == BTREE_MAGIC);
680  Assert(metad->btm_version <= BTREE_VERSION);
681  Assert(metad->btm_fastroot != P_NONE);
682 
683  return metad->btm_version > BTREE_NOVAC_VERSION;
684 }
static BTMetaPageData * _bt_getmeta(Relation rel, Buffer metabuf)
Definition: nbtpage.c:120
uint32 btm_version
Definition: nbtree.h:101
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:757
#define BTREE_VERSION
Definition: nbtree.h:134
uint32 btm_magic
Definition: nbtree.h:100
#define P_NONE
Definition: nbtree.h:182
#define BT_READ
Definition: nbtree.h:402
BlockNumber btm_fastroot
Definition: nbtree.h:104
#define BTREE_MAGIC
Definition: nbtree.h:133
unsigned int uint32
Definition: c.h:359
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:136
#define BTREE_METAPAGE
Definition: nbtree.h:132
BlockNumber btm_root
Definition: nbtree.h:102
#define BTREE_MIN_VERSION
Definition: nbtree.h:135
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
#define Assert(condition)
Definition: c.h:739
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:796
MemoryContext rd_indexcxt
Definition: rel.h:161
void * rd_amcache
Definition: rel.h:185
int Buffer
Definition: buf.h:23

◆ _bt_initmetapage()

void _bt_initmetapage ( Page  page,
BlockNumber  rootbknum,
uint32  level 
)

Definition at line 50 of file nbtpage.c.

References _bt_pageinit(), BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_last_cleanup_num_heap_tuples, BTMetaPageData::btm_level, BTMetaPageData::btm_magic, BTMetaPageData::btm_oldest_btpo_xact, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTP_META, BTPageGetMeta, BTPageOpaqueData::btpo_flags, BTREE_MAGIC, BTREE_VERSION, InvalidTransactionId, and PageGetSpecialPointer.

Referenced by _bt_uppershutdown(), and btbuildempty().

51 {
52  BTMetaPageData *metad;
53  BTPageOpaque metaopaque;
54 
55  _bt_pageinit(page, BLCKSZ);
56 
57  metad = BTPageGetMeta(page);
58  metad->btm_magic = BTREE_MAGIC;
59  metad->btm_version = BTREE_VERSION;
60  metad->btm_root = rootbknum;
61  metad->btm_level = level;
62  metad->btm_fastroot = rootbknum;
63  metad->btm_fastlevel = level;
66 
67  metaopaque = (BTPageOpaque) PageGetSpecialPointer(page);
68  metaopaque->btpo_flags = BTP_META;
69 
70  /*
71  * Set pd_lower just past the end of the metadata. This is essential,
72  * because without doing so, metadata will be lost if xlog.c compresses
73  * the page.
74  */
75  ((PageHeader) page)->pd_lower =
76  ((char *) metad + sizeof(BTMetaPageData)) - (char *) page;
77 }
uint32 btm_version
Definition: nbtree.h:101
#define BTREE_VERSION
Definition: nbtree.h:134
uint32 btm_magic
Definition: nbtree.h:100
#define BTP_META
Definition: nbtree.h:75
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
BlockNumber btm_fastroot
Definition: nbtree.h:104
#define BTREE_MAGIC
Definition: nbtree.h:133
#define BTPageGetMeta(p)
Definition: nbtree.h:113
#define InvalidTransactionId
Definition: transam.h:31
uint32 btm_fastlevel
Definition: nbtree.h:105
BlockNumber btm_root
Definition: nbtree.h:102
PageHeaderData * PageHeader
Definition: bufpage.h:166
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:109
uint32 btm_level
Definition: nbtree.h:103
void _bt_pageinit(Page page, Size size)
Definition: nbtpage.c:924
TransactionId btm_oldest_btpo_xact
Definition: nbtree.h:107
uint16 btpo_flags
Definition: nbtree.h:65

◆ _bt_is_page_halfdead()

static bool _bt_is_page_halfdead ( Relation  rel,
BlockNumber  blk 
)
static

Definition at line 1120 of file nbtpage.c.

References _bt_getbuf(), _bt_relbuf(), BT_READ, buf, BufferGetPage, P_ISHALFDEAD, and PageGetSpecialPointer.

Referenced by _bt_mark_page_halfdead().

1121 {
1122  Buffer buf;
1123  Page page;
1124  BTPageOpaque opaque;
1125  bool result;
1126 
1127  buf = _bt_getbuf(rel, blk, BT_READ);
1128  page = BufferGetPage(buf);
1129  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1130 
1131  result = P_ISHALFDEAD(opaque);
1132  _bt_relbuf(rel, buf);
1133 
1134  return result;
1135 }
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:757
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
#define BT_READ
Definition: nbtree.h:402
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:194
static char * buf
Definition: pg_test_fsync.c:67
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78

◆ _bt_lock_branch_parent()

static bool _bt_lock_branch_parent ( Relation  rel,
BlockNumber  child,
BTStack  stack,
Buffer topparent,
OffsetNumber topoff,
BlockNumber target,
BlockNumber rightsib 
)
static

Definition at line 1166 of file nbtpage.c.

References _bt_getbuf(), _bt_getstackbuf(), _bt_relbuf(), BT_READ, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BTStackData::bts_blkno, BTStackData::bts_offset, BTStackData::bts_parent, BufferGetPage, ereport, errcode(), errmsg_internal(), ERROR, InvalidBuffer, P_FIRSTDATAKEY, P_INCOMPLETE_SPLIT, P_ISROOT, P_NONE, P_RIGHTMOST, PageGetMaxOffsetNumber, PageGetSpecialPointer, and RelationGetRelationName.

Referenced by _bt_mark_page_halfdead().

1169 {
1170  BlockNumber parent;
1171  OffsetNumber poffset,
1172  maxoff;
1173  Buffer pbuf;
1174  Page page;
1175  BTPageOpaque opaque;
1176  BlockNumber leftsib;
1177 
1178  /*
1179  * Locate the downlink of "child" in the parent, updating the stack entry
1180  * if needed. This is how !heapkeyspace indexes deal with having
1181  * non-unique high keys in leaf level pages. Even heapkeyspace indexes
1182  * can have a stale stack due to insertions into the parent.
1183  */
1184  pbuf = _bt_getstackbuf(rel, stack, child);
1185  if (pbuf == InvalidBuffer)
1186  ereport(ERROR,
1187  (errcode(ERRCODE_INDEX_CORRUPTED),
1188  errmsg_internal("failed to re-find parent key in index \"%s\" for deletion target page %u",
1189  RelationGetRelationName(rel), child)));
1190  parent = stack->bts_blkno;
1191  poffset = stack->bts_offset;
1192 
1193  page = BufferGetPage(pbuf);
1194  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1195  maxoff = PageGetMaxOffsetNumber(page);
1196 
1197  /*
1198  * If the target is the rightmost child of its parent, then we can't
1199  * delete, unless it's also the only child.
1200  */
1201  if (poffset >= maxoff)
1202  {
1203  /* It's rightmost child... */
1204  if (poffset == P_FIRSTDATAKEY(opaque))
1205  {
1206  /*
1207  * It's only child, so safe if parent would itself be removable.
1208  * We have to check the parent itself, and then recurse to test
1209  * the conditions at the parent's parent.
1210  */
1211  if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) ||
1212  P_INCOMPLETE_SPLIT(opaque))
1213  {
1214  _bt_relbuf(rel, pbuf);
1215  return false;
1216  }
1217 
1218  *target = parent;
1219  *rightsib = opaque->btpo_next;
1220  leftsib = opaque->btpo_prev;
1221 
1222  _bt_relbuf(rel, pbuf);
1223 
1224  /*
1225  * Like in _bt_pagedel, check that the left sibling is not marked
1226  * with INCOMPLETE_SPLIT flag. That would mean that there is no
1227  * downlink to the page to be deleted, and the page deletion
1228  * algorithm isn't prepared to handle that.
1229  */
1230  if (leftsib != P_NONE)
1231  {
1232  Buffer lbuf;
1233  Page lpage;
1234  BTPageOpaque lopaque;
1235 
1236  lbuf = _bt_getbuf(rel, leftsib, BT_READ);
1237  lpage = BufferGetPage(lbuf);
1238  lopaque = (BTPageOpaque) PageGetSpecialPointer(lpage);
1239 
1240  /*
1241  * If the left sibling was concurrently split, so that its
1242  * next-pointer doesn't point to the current page anymore, the
1243  * split that created the current page must be completed. (We
1244  * don't allow splitting an incompletely split page again
1245  * until the previous split has been completed)
1246  */
1247  if (lopaque->btpo_next == parent &&
1248  P_INCOMPLETE_SPLIT(lopaque))
1249  {
1250  _bt_relbuf(rel, lbuf);
1251  return false;
1252  }
1253  _bt_relbuf(rel, lbuf);
1254  }
1255 
1256  return _bt_lock_branch_parent(rel, parent, stack->bts_parent,
1257  topparent, topoff, target, rightsib);
1258  }
1259  else
1260  {
1261  /* Unsafe to delete */
1262  _bt_relbuf(rel, pbuf);
1263  return false;
1264  }
1265  }
1266  else
1267  {
1268  /* Not rightmost child, so safe to delete */
1269  *topparent = pbuf;
1270  *topoff = poffset;
1271  return true;
1272  }
1273 }
BlockNumber btpo_next
Definition: nbtree.h:59
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:757
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:220
#define P_NONE
Definition: nbtree.h:182
#define InvalidBuffer
Definition: buf.h:25
int errcode(int sqlerrcode)
Definition: elog.c:608
uint32 BlockNumber
Definition: block.h:31
#define P_INCOMPLETE_SPLIT(opaque)
Definition: nbtree.h:197
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
uint16 OffsetNumber
Definition: off.h:24
#define BT_READ
Definition: nbtree.h:402
#define ERROR
Definition: elog.h:43
static bool _bt_lock_branch_parent(Relation rel, BlockNumber child, BTStack stack, Buffer *topparent, OffsetNumber *topoff, BlockNumber *target, BlockNumber *rightsib)
Definition: nbtpage.c:1166
BlockNumber btpo_prev
Definition: nbtree.h:58
OffsetNumber bts_offset
Definition: nbtree.h:418
#define RelationGetRelationName(relation)
Definition: rel.h:462
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define ereport(elevel, rest)
Definition: elog.h:141
#define P_ISROOT(opaque)
Definition: nbtree.h:191
BlockNumber bts_blkno
Definition: nbtree.h:417
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
int errmsg_internal(const char *fmt,...)
Definition: elog.c:909
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
struct BTStackData * bts_parent
Definition: nbtree.h:419
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:189
Buffer _bt_getstackbuf(Relation rel, BTStack stack, BlockNumber child)
Definition: nbtinsert.c:1927
Pointer Page
Definition: bufpage.h:78

◆ _bt_log_reuse_page()

static void _bt_log_reuse_page ( Relation  rel,
BlockNumber  blkno,
TransactionId  latestRemovedXid 
)
static

Definition at line 724 of file nbtpage.c.

References xl_btree_reuse_page::block, xl_btree_reuse_page::latestRemovedXid, xl_btree_reuse_page::node, RelationData::rd_node, SizeOfBtreeReusePage, XLOG_BTREE_REUSE_PAGE, XLogBeginInsert(), XLogInsert(), and XLogRegisterData().

Referenced by _bt_getbuf().

725 {
726  xl_btree_reuse_page xlrec_reuse;
727 
728  /*
729  * Note that we don't register the buffer with the record, because this
730  * operation doesn't modify the page. This record only exists to provide a
731  * conflict point for Hot Standby.
732  */
733 
734  /* XLOG stuff */
735  xlrec_reuse.node = rel->rd_node;
736  xlrec_reuse.block = blkno;
737  xlrec_reuse.latestRemovedXid = latestRemovedXid;
738 
739  XLogBeginInsert();
740  XLogRegisterData((char *) &xlrec_reuse, SizeOfBtreeReusePage);
741 
742  XLogInsert(RM_BTREE_ID, XLOG_BTREE_REUSE_PAGE);
743 }
RelFileNode node
Definition: nbtxlog.h:145
#define SizeOfBtreeReusePage
Definition: nbtxlog.h:150
BlockNumber block
Definition: nbtxlog.h:146
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
#define XLOG_BTREE_REUSE_PAGE
Definition: nbtxlog.h:39
RelFileNode rd_node
Definition: rel.h:55
TransactionId latestRemovedXid
Definition: nbtxlog.h:147
void XLogBeginInsert(void)
Definition: xloginsert.c:120

◆ _bt_mark_page_halfdead()

static bool _bt_mark_page_halfdead ( Relation  rel,
Buffer  buf,
BTStack  stack 
)
static

Definition at line 1516 of file nbtpage.c.

References _bt_is_page_halfdead(), _bt_lock_branch_parent(), _bt_relbuf(), Assert, BTP_HALF_DEAD, BTPageOpaqueData::btpo_flags, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BTreeTupleGetDownLink, BTreeTupleSetDownLink, BTreeTupleSetTopParent, BufferGetBlockNumber(), BufferGetPage, DEBUG1, elog, END_CRIT_SECTION, ereport, errcode(), errmsg_internal(), ERROR, IndexTupleSize, InvalidBlockNumber, xl_btree_mark_page_halfdead::leafblk, xl_btree_mark_page_halfdead::leftblk, MarkBufferDirty(), MemSet, OffsetNumberNext, P_FIRSTDATAKEY, P_HIKEY, P_ISDELETED, P_ISHALFDEAD, P_ISLEAF, P_ISROOT, P_RIGHTMOST, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageGetSpecialPointer, PageIndexTupleDelete(), PageIndexTupleOverwrite(), PageSetLSN, xl_btree_mark_page_halfdead::poffset, PredicateLockPageCombine(), REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetRelationName, RelationNeedsWAL, xl_btree_mark_page_halfdead::rightblk, SizeOfBtreeMarkPageHalfDead, START_CRIT_SECTION, IndexTupleData::t_info, xl_btree_mark_page_halfdead::topparent, XLOG_BTREE_MARK_PAGE_HALFDEAD, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_pagedel().

1517 {
1518  BlockNumber leafblkno;
1519  BlockNumber leafrightsib;
1520  BlockNumber target;
1521  BlockNumber rightsib;
1522  ItemId itemid;
1523  Page page;
1524  BTPageOpaque opaque;
1525  Buffer topparent;
1526  OffsetNumber topoff;
1527  OffsetNumber nextoffset;
1528  IndexTuple itup;
1529  IndexTupleData trunctuple;
1530 
1531  page = BufferGetPage(leafbuf);
1532  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1533 
1534  Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) && !P_ISDELETED(opaque) &&
1535  !P_ISHALFDEAD(opaque) && P_ISLEAF(opaque) &&
1536  P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
1537 
1538  /*
1539  * Save info about the leaf page.
1540  */
1541  leafblkno = BufferGetBlockNumber(leafbuf);
1542  leafrightsib = opaque->btpo_next;
1543 
1544  /*
1545  * Before attempting to lock the parent page, check that the right sibling
1546  * is not in half-dead state. A half-dead right sibling would have no
1547  * downlink in the parent, which would be highly confusing later when we
1548  * delete the downlink that follows the current page's downlink. (I
1549  * believe the deletion would work correctly, but it would fail the
1550  * cross-check we make that the following downlink points to the right
1551  * sibling of the delete page.)
1552  */
1553  if (_bt_is_page_halfdead(rel, leafrightsib))
1554  {
1555  elog(DEBUG1, "could not delete page %u because its right sibling %u is half-dead",
1556  leafblkno, leafrightsib);
1557  return false;
1558  }
1559 
1560  /*
1561  * We cannot delete a page that is the rightmost child of its immediate
1562  * parent, unless it is the only child --- in which case the parent has to
1563  * be deleted too, and the same condition applies recursively to it. We
1564  * have to check this condition all the way up before trying to delete,
1565  * and lock the final parent of the to-be-deleted subtree.
1566  *
1567  * However, we won't need to repeat the above _bt_is_page_halfdead() check
1568  * for parent/ancestor pages because of the rightmost restriction. The
1569  * leaf check will apply to a right "cousin" leaf page rather than a
1570  * simple right sibling leaf page in cases where we actually go on to
1571  * perform internal page deletion. The right cousin leaf page is
1572  * representative of the left edge of the subtree to the right of the
1573  * to-be-deleted subtree as a whole. (Besides, internal pages are never
1574  * marked half-dead, so it isn't even possible to directly assess if an
1575  * internal page is part of some other to-be-deleted subtree.)
1576  */
1577  rightsib = leafrightsib;
1578  target = leafblkno;
1579  if (!_bt_lock_branch_parent(rel, leafblkno, stack,
1580  &topparent, &topoff, &target, &rightsib))
1581  return false;
1582 
1583  /*
1584  * Check that the parent-page index items we're about to delete/overwrite
1585  * contain what we expect. This can fail if the index has become corrupt
1586  * for some reason. We want to throw any error before entering the
1587  * critical section --- otherwise it'd be a PANIC.
1588  *
1589  * The test on the target item is just an Assert because
1590  * _bt_lock_branch_parent should have guaranteed it has the expected
1591  * contents. The test on the next-child downlink is known to sometimes
1592  * fail in the field, though.
1593  */
1594  page = BufferGetPage(topparent);
1595  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1596 
1597 #ifdef USE_ASSERT_CHECKING
1598  itemid = PageGetItemId(page, topoff);
1599  itup = (IndexTuple) PageGetItem(page, itemid);
1600  Assert(BTreeTupleGetDownLink(itup) == target);
1601 #endif
1602 
1603  nextoffset = OffsetNumberNext(topoff);
1604  itemid = PageGetItemId(page, nextoffset);
1605  itup = (IndexTuple) PageGetItem(page, itemid);
1606  if (BTreeTupleGetDownLink(itup) != rightsib)
1607  ereport(ERROR,
1608  (errcode(ERRCODE_INDEX_CORRUPTED),
1609  errmsg_internal("right sibling %u of block %u is not next child %u of block %u in index \"%s\"",
1610  rightsib, target, BTreeTupleGetDownLink(itup),
1611  BufferGetBlockNumber(topparent), RelationGetRelationName(rel))));
1612 
1613  /*
1614  * Any insert which would have gone on the leaf block will now go to its
1615  * right sibling.
1616  */
1617  PredicateLockPageCombine(rel, leafblkno, leafrightsib);
1618 
1619  /* No ereport(ERROR) until changes are logged */
1621 
1622  /*
1623  * Update parent. The normal case is a tad tricky because we want to
1624  * delete the target's downlink and the *following* key. Easiest way is
1625  * to copy the right sibling's downlink over the target downlink, and then
1626  * delete the following item.
1627  */
1628  page = BufferGetPage(topparent);
1629  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1630 
1631  itemid = PageGetItemId(page, topoff);
1632  itup = (IndexTuple) PageGetItem(page, itemid);
1633  BTreeTupleSetDownLink(itup, rightsib);
1634 
1635  nextoffset = OffsetNumberNext(topoff);
1636  PageIndexTupleDelete(page, nextoffset);
1637 
1638  /*
1639  * Mark the leaf page as half-dead, and stamp it with a pointer to the
1640  * highest internal page in the branch we're deleting. We use the tid of
1641  * the high key to store it.
1642  */
1643  page = BufferGetPage(leafbuf);
1644  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1645  opaque->btpo_flags |= BTP_HALF_DEAD;
1646 
1648  MemSet(&trunctuple, 0, sizeof(IndexTupleData));
1649  trunctuple.t_info = sizeof(IndexTupleData);
1650  if (target != leafblkno)
1651  BTreeTupleSetTopParent(&trunctuple, target);
1652  else
1654 
1655  if (!PageIndexTupleOverwrite(page, P_HIKEY, (Item) &trunctuple,
1656  IndexTupleSize(&trunctuple)))
1657  elog(ERROR, "could not overwrite high key in half-dead page");
1658 
1659  /* Must mark buffers dirty before XLogInsert */
1660  MarkBufferDirty(topparent);
1661  MarkBufferDirty(leafbuf);
1662 
1663  /* XLOG stuff */
1664  if (RelationNeedsWAL(rel))
1665  {
1667  XLogRecPtr recptr;
1668 
1669  xlrec.poffset = topoff;
1670  xlrec.leafblk = leafblkno;
1671  if (target != leafblkno)
1672  xlrec.topparent = target;
1673  else
1674  xlrec.topparent = InvalidBlockNumber;
1675 
1676  XLogBeginInsert();
1677  XLogRegisterBuffer(0, leafbuf, REGBUF_WILL_INIT);
1678  XLogRegisterBuffer(1, topparent, REGBUF_STANDARD);
1679 
1680  page = BufferGetPage(leafbuf);
1681  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1682  xlrec.leftblk = opaque->btpo_prev;
1683  xlrec.rightblk = opaque->btpo_next;
1684 
1686 
1687  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_MARK_PAGE_HALFDEAD);
1688 
1689  page = BufferGetPage(topparent);
1690  PageSetLSN(page, recptr);
1691  page = BufferGetPage(leafbuf);
1692  PageSetLSN(page, recptr);
1693  }
1694 
1695  END_CRIT_SECTION();
1696 
1697  _bt_relbuf(rel, topparent);
1698  return true;
1699 }
BlockNumber btpo_next
Definition: nbtree.h:59
#define DEBUG1
Definition: elog.h:25
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:726
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1458
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define BTreeTupleGetDownLink(itup)
Definition: nbtree.h:301
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:220
#define SizeOfBtreeMarkPageHalfDead
Definition: nbtxlog.h:190
#define BTP_HALF_DEAD
Definition: nbtree.h:76
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
Pointer Item
Definition: item.h:17
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
int errcode(int sqlerrcode)
Definition: elog.c:608
#define MemSet(start, val, len)
Definition: c.h:962
uint32 BlockNumber
Definition: block.h:31
#define BTreeTupleSetDownLink(itup, blkno)
Definition: nbtree.h:303
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
uint16 OffsetNumber
Definition: off.h:24
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:194
#define ERROR
Definition: elog.h:43
#define BTreeTupleSetTopParent(itup, blkno)
Definition: nbtree.h:314
static bool _bt_lock_branch_parent(Relation rel, BlockNumber child, BTStack stack, Buffer *topparent, OffsetNumber *topoff, BlockNumber *target, BlockNumber *rightsib)
Definition: nbtpage.c:1166
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize)
Definition: bufpage.c:1065
BlockNumber btpo_prev
Definition: nbtree.h:58
IndexTupleData * IndexTuple
Definition: itup.h:53
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define RelationGetRelationName(relation)
Definition: rel.h:462
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define ereport(elevel, rest)
Definition: elog.h:141
#define P_ISDELETED(opaque)
Definition: nbtree.h:192
#define P_ISROOT(opaque)
Definition: nbtree.h:191
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
static bool _bt_is_page_halfdead(Relation rel, BlockNumber blk)
Definition: nbtpage.c:1120
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
#define XLOG_BTREE_MARK_PAGE_HALFDEAD
Definition: nbtxlog.h:36
struct IndexTupleData IndexTupleData
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
int errmsg_internal(const char *fmt,...)
Definition: elog.c:909
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:739
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define InvalidBlockNumber
Definition: block.h:33
#define RelationNeedsWAL(relation)
Definition: rel.h:530
#define P_HIKEY
Definition: nbtree.h:218
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2623
#define elog(elevel,...)
Definition: elog.h:228
void PredicateLockPageCombine(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3187
unsigned short t_info
Definition: itup.h:49
void XLogBeginInsert(void)
Definition: xloginsert.c:120
uint16 btpo_flags
Definition: nbtree.h:65
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:189
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define IndexTupleSize(itup)
Definition: itup.h:71
#define P_ISLEAF(opaque)
Definition: nbtree.h:190

◆ _bt_page_recyclable()

bool _bt_page_recyclable ( Page  page)

Definition at line 939 of file nbtpage.c.

References BTPageOpaqueData::btpo, P_ISDELETED, PageGetSpecialPointer, PageIsNew, RecentGlobalXmin, TransactionIdPrecedes(), and BTPageOpaqueData::xact.

Referenced by _bt_getbuf(), and btvacuumpage().

940 {
941  BTPageOpaque opaque;
942 
943  /*
944  * It's possible to find an all-zeroes page in an index --- for example, a
945  * backend might successfully extend the relation one page and then crash
946  * before it is able to make a WAL entry for adding the page. If we find a
947  * zeroed page then reclaim it.
948  */
949  if (PageIsNew(page))
950  return true;
951 
952  /*
953  * Otherwise, recycle if deleted and too old to have any processes
954  * interested in it.
955  */
956  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
957  if (P_ISDELETED(opaque) &&
959  return true;
960  return false;
961 }
union BTPageOpaqueData::@46 btpo
TransactionId xact
Definition: nbtree.h:63
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
TransactionId RecentGlobalXmin
Definition: snapmgr.c:168
#define P_ISDELETED(opaque)
Definition: nbtree.h:192
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define PageIsNew(page)
Definition: bufpage.h:229

◆ _bt_pagedel()

int _bt_pagedel ( Relation  rel,
Buffer  buf 
)

Definition at line 1296 of file nbtpage.c.

References _bt_getbuf(), _bt_mark_page_halfdead(), _bt_mkscankey(), _bt_relbuf(), _bt_search(), _bt_unlink_halfdead_page(), Assert, BT_READ, BT_WRITE, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage, CHECK_FOR_INTERRUPTS, CopyIndexTuple(), ereport, errcode(), errhint(), errmsg(), LockBuffer(), LOG, P_FIRSTDATAKEY, P_HIKEY, P_INCOMPLETE_SPLIT, P_ISDELETED, P_ISHALFDEAD, P_ISLEAF, P_ISROOT, P_LEFTMOST, P_RIGHTMOST, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageGetSpecialPointer, BTScanInsertData::pivotsearch, RelationGetRelationName, and ReleaseBuffer().

Referenced by btvacuumpage().

1297 {
1298  int ndeleted = 0;
1299  BlockNumber rightsib;
1300  bool rightsib_empty;
1301  Page page;
1302  BTPageOpaque opaque;
1303 
1304  /*
1305  * "stack" is a search stack leading (approximately) to the target page.
1306  * It is initially NULL, but when iterating, we keep it to avoid
1307  * duplicated search effort.
1308  *
1309  * Also, when "stack" is not NULL, we have already checked that the
1310  * current page is not the right half of an incomplete split, i.e. the
1311  * left sibling does not have its INCOMPLETE_SPLIT flag set.
1312  */
1313  BTStack stack = NULL;
1314 
1315  for (;;)
1316  {
1317  page = BufferGetPage(buf);
1318  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1319 
1320  /*
1321  * Internal pages are never deleted directly, only as part of deleting
1322  * the whole branch all the way down to leaf level.
1323  */
1324  if (!P_ISLEAF(opaque))
1325  {
1326  /*
1327  * Pre-9.4 page deletion only marked internal pages as half-dead,
1328  * but now we only use that flag on leaf pages. The old algorithm
1329  * was never supposed to leave half-dead pages in the tree, it was
1330  * just a transient state, but it was nevertheless possible in
1331  * error scenarios. We don't know how to deal with them here. They
1332  * are harmless as far as searches are considered, but inserts
1333  * into the deleted keyspace could add out-of-order downlinks in
1334  * the upper levels. Log a notice, hopefully the admin will notice
1335  * and reindex.
1336  */
1337  if (P_ISHALFDEAD(opaque))
1338  ereport(LOG,
1339  (errcode(ERRCODE_INDEX_CORRUPTED),
1340  errmsg("index \"%s\" contains a half-dead internal page",
1342  errhint("This can be caused by an interrupted VACUUM in version 9.3 or older, before upgrade. Please REINDEX it.")));
1343  _bt_relbuf(rel, buf);
1344  return ndeleted;
1345  }
1346 
1347  /*
1348  * We can never delete rightmost pages nor root pages. While at it,
1349  * check that page is not already deleted and is empty.
1350  *
1351  * To keep the algorithm simple, we also never delete an incompletely
1352  * split page (they should be rare enough that this doesn't make any
1353  * meaningful difference to disk usage):
1354  *
1355  * The INCOMPLETE_SPLIT flag on the page tells us if the page is the
1356  * left half of an incomplete split, but ensuring that it's not the
1357  * right half is more complicated. For that, we have to check that
1358  * the left sibling doesn't have its INCOMPLETE_SPLIT flag set. On
1359  * the first iteration, we temporarily release the lock on the current
1360  * page, and check the left sibling and also construct a search stack
1361  * to. On subsequent iterations, we know we stepped right from a page
1362  * that passed these tests, so it's OK.
1363  */
1364  if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) || P_ISDELETED(opaque) ||
1365  P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) ||
1366  P_INCOMPLETE_SPLIT(opaque))
1367  {
1368  /* Should never fail to delete a half-dead page */
1369  Assert(!P_ISHALFDEAD(opaque));
1370 
1371  _bt_relbuf(rel, buf);
1372  return ndeleted;
1373  }
1374 
1375  /*
1376  * First, remove downlink pointing to the page (or a parent of the
1377  * page, if we are going to delete a taller branch), and mark the page
1378  * as half-dead.
1379  */
1380  if (!P_ISHALFDEAD(opaque))
1381  {
1382  /*
1383  * We need an approximate pointer to the page's parent page. We
1384  * use a variant of the standard search mechanism to search for
1385  * the page's high key; this will give us a link to either the
1386  * current parent or someplace to its left (if there are multiple
1387  * equal high keys, which is possible with !heapkeyspace indexes).
1388  *
1389  * Also check if this is the right-half of an incomplete split
1390  * (see comment above).
1391  */
1392  if (!stack)
1393  {
1394  BTScanInsert itup_key;
1395  ItemId itemid;
1396  IndexTuple targetkey;
1397  Buffer lbuf;
1398  BlockNumber leftsib;
1399 
1400  itemid = PageGetItemId(page, P_HIKEY);
1401  targetkey = CopyIndexTuple((IndexTuple) PageGetItem(page, itemid));
1402 
1403  leftsib = opaque->btpo_prev;
1404 
1405  /*
1406  * To avoid deadlocks, we'd better drop the leaf page lock
1407  * before going further.
1408  */
1410 
1411  /*
1412  * Fetch the left sibling, to check that it's not marked with
1413  * INCOMPLETE_SPLIT flag. That would mean that the page
1414  * to-be-deleted doesn't have a downlink, and the page
1415  * deletion algorithm isn't prepared to handle that.
1416  */
1417  if (!P_LEFTMOST(opaque))
1418  {
1419  BTPageOpaque lopaque;
1420  Page lpage;
1421 
1422  lbuf = _bt_getbuf(rel, leftsib, BT_READ);
1423  lpage = BufferGetPage(lbuf);
1424  lopaque = (BTPageOpaque) PageGetSpecialPointer(lpage);
1425 
1426  /*
1427  * If the left sibling is split again by another backend,
1428  * after we released the lock, we know that the first
1429  * split must have finished, because we don't allow an
1430  * incompletely-split page to be split again. So we don't
1431  * need to walk right here.
1432  */
1433  if (lopaque->btpo_next == BufferGetBlockNumber(buf) &&
1434  P_INCOMPLETE_SPLIT(lopaque))
1435  {
1436  ReleaseBuffer(buf);
1437  _bt_relbuf(rel, lbuf);
1438  return ndeleted;
1439  }
1440  _bt_relbuf(rel, lbuf);
1441  }
1442 
1443  /* we need an insertion scan key for the search, so build one */
1444  itup_key = _bt_mkscankey(rel, targetkey);
1445  /* find the leftmost leaf page with matching pivot/high key */
1446  itup_key->pivotsearch = true;
1447  stack = _bt_search(rel, itup_key, &lbuf, BT_READ, NULL);
1448  /* don't need a lock or second pin on the page */
1449  _bt_relbuf(rel, lbuf);
1450 
1451  /*
1452  * Re-lock the leaf page, and start over, to re-check that the
1453  * page can still be deleted.
1454  */
1456  continue;
1457  }
1458 
1459  if (!_bt_mark_page_halfdead(rel, buf, stack))
1460  {
1461  _bt_relbuf(rel, buf);
1462  return ndeleted;
1463  }
1464  }
1465 
1466  /*
1467  * Then unlink it from its siblings. Each call to
1468  * _bt_unlink_halfdead_page unlinks the topmost page from the branch,
1469  * making it shallower. Iterate until the leaf page is gone.
1470  */
1471  rightsib_empty = false;
1472  while (P_ISHALFDEAD(opaque))
1473  {
1474  /* will check for interrupts, once lock is released */
1475  if (!_bt_unlink_halfdead_page(rel, buf, &rightsib_empty))
1476  {
1477  /* _bt_unlink_halfdead_page already released buffer */
1478  return ndeleted;
1479  }
1480  ndeleted++;
1481  }
1482 
1483  rightsib = opaque->btpo_next;
1484 
1485  _bt_relbuf(rel, buf);
1486 
1487  /*
1488  * Check here, as calling loops will have locks held, preventing
1489  * interrupts from being processed.
1490  */
1492 
1493  /*
1494  * The page has now been deleted. If its right sibling is completely
1495  * empty, it's possible that the reason we haven't deleted it earlier
1496  * is that it was the rightmost child of the parent. Now that we
1497  * removed the downlink for this page, the right sibling might now be
1498  * the only child of the parent, and could be removed. It would be
1499  * picked up by the next vacuum anyway, but might as well try to
1500  * remove it now, so loop back to process the right sibling.
1501  */
1502  if (!rightsib_empty)
1503  break;
1504 
1505  buf = _bt_getbuf(rel, rightsib, BT_WRITE);
1506  }
1507 
1508  return ndeleted;
1509 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:86
BlockNumber btpo_next
Definition: nbtree.h:59
int errhint(const char *fmt,...)
Definition: elog.c:1069
BTScanInsert _bt_mkscankey(Relation rel, IndexTuple itup)
Definition: nbtutils.c:86
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:757
static bool _bt_mark_page_halfdead(Relation rel, Buffer buf, BTStack stack)
Definition: nbtpage.c:1516
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:220
int errcode(int sqlerrcode)
Definition: elog.c:608
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3375
#define P_INCOMPLETE_SPLIT(opaque)
Definition: nbtree.h:197
#define LOG
Definition: elog.h:26
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
#define BT_READ
Definition: nbtree.h:402
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:194
BlockNumber btpo_prev
Definition: nbtree.h:58
IndexTuple CopyIndexTuple(IndexTuple source)
Definition: indextuple.c:509
static char * buf
Definition: pg_test_fsync.c:67
#define RelationGetRelationName(relation)
Definition: rel.h:462
#define P_LEFTMOST(opaque)
Definition: nbtree.h:188
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define ereport(elevel, rest)
Definition: elog.h:141
bool pivotsearch
Definition: nbtree.h:474
#define P_ISDELETED(opaque)
Definition: nbtree.h:192
#define P_ISROOT(opaque)
Definition: nbtree.h:191
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3612
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
#define Assert(condition)
Definition: c.h:739
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define P_HIKEY
Definition: nbtree.h:218
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2623
int errmsg(const char *fmt,...)
Definition: elog.c:822
static bool _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty)
Definition: nbtpage.c:1719
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
#define BT_WRITE
Definition: nbtree.h:403
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:189
BTStack _bt_search(Relation rel, BTScanInsert key, Buffer *bufP, int access, Snapshot snapshot)
Definition: nbtsearch.c:92
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define P_ISLEAF(opaque)
Definition: nbtree.h:190

◆ _bt_pageinit()

void _bt_pageinit ( Page  page,
Size  size 
)

Definition at line 924 of file nbtpage.c.

References PageInit().

Referenced by _bt_blnewpage(), _bt_getbuf(), _bt_initmetapage(), _bt_restore_meta(), _bt_split(), btree_xlog_mark_page_halfdead(), btree_xlog_newroot(), btree_xlog_split(), and btree_xlog_unlink_page().

925 {
926  PageInit(page, size, sizeof(BTPageOpaqueData));
927 }
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:42

◆ _bt_relandgetbuf()

Buffer _bt_relandgetbuf ( Relation  rel,
Buffer  obuf,
BlockNumber  blkno,
int  access 
)

Definition at line 893 of file nbtpage.c.

References _bt_checkpage(), Assert, buf, BUFFER_LOCK_UNLOCK, BufferIsValid, LockBuffer(), P_NEW, and ReleaseAndReadBuffer().

Referenced by _bt_check_unique(), _bt_get_endpoint(), _bt_getroot(), _bt_gettrueroot(), _bt_moveright(), _bt_search(), _bt_stepright(), and _bt_walk_left().

894 {
895  Buffer buf;
896 
897  Assert(blkno != P_NEW);
898  if (BufferIsValid(obuf))
900  buf = ReleaseAndReadBuffer(obuf, rel, blkno);
901  LockBuffer(buf, access);
902  _bt_checkpage(rel, buf);
903  return buf;
904 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:86
#define P_NEW
Definition: bufmgr.h:81
void _bt_checkpage(Relation rel, Buffer buf)
Definition: nbtpage.c:690
static char * buf
Definition: pg_test_fsync.c:67
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3612
#define Assert(condition)
Definition: c.h:739
Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation, BlockNumber blockNum)
Definition: bufmgr.c:1521
#define BufferIsValid(bufnum)
Definition: bufmgr.h:113
int Buffer
Definition: buf.h:23

◆ _bt_relbuf()

◆ _bt_unlink_halfdead_page()

static bool _bt_unlink_halfdead_page ( Relation  rel,
Buffer  leafbuf,
bool rightsib_empty 
)
static

Definition at line 1719 of file nbtpage.c.

References _bt_getbuf(), _bt_relbuf(), _bt_upgrademetapage(), Assert, BT_READ, BT_WRITE, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_last_cleanup_num_heap_tuples, BTMetaPageData::btm_level, BTMetaPageData::btm_oldest_btpo_xact, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTP_DELETED, BTP_HALF_DEAD, BTPageGetMeta, BTPageOpaqueData::btpo, BTPageOpaqueData::btpo_flags, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, xl_btree_unlink_page::btpo_xact, BTREE_METAPAGE, BTREE_NOVAC_VERSION, BTreeTupleGetDownLink, BTreeTupleGetTopParent, BTreeTupleSetTopParent, buf, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage, BufferIsValid, CHECK_FOR_INTERRUPTS, elog, END_CRIT_SECTION, ereport, errcode(), errmsg_internal(), ERROR, xl_btree_metadata::fastlevel, xl_btree_metadata::fastroot, InvalidBlockNumber, InvalidBuffer, xl_btree_metadata::last_cleanup_num_heap_tuples, xl_btree_unlink_page::leafleftsib, xl_btree_unlink_page::leafrightsib, xl_btree_unlink_page::leftsib, xl_btree_metadata::level, BTPageOpaqueData::level, LockBuffer(), LOG, MarkBufferDirty(), xl_btree_metadata::oldest_btpo_xact, P_FIRSTDATAKEY, P_HIKEY, P_ISDELETED, P_ISHALFDEAD, P_ISLEAF, P_ISROOT, P_NONE, P_RIGHTMOST, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageGetSpecialPointer, PageSetLSN, ReadNewTransactionId(), REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetRelationName, RelationNeedsWAL, ReleaseBuffer(), xl_btree_unlink_page::rightsib, xl_btree_metadata::root, SizeOfBtreeUnlinkPage, START_CRIT_SECTION, xl_btree_unlink_page::topparent, xl_btree_metadata::version, BTPageOpaqueData::xact, XLOG_BTREE_UNLINK_PAGE, XLOG_BTREE_UNLINK_PAGE_META, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_pagedel().

1720 {
1721  BlockNumber leafblkno = BufferGetBlockNumber(leafbuf);
1722  BlockNumber leafleftsib;
1723  BlockNumber leafrightsib;
1724  BlockNumber target;
1725  BlockNumber leftsib;
1726  BlockNumber rightsib;
1727  Buffer lbuf = InvalidBuffer;
1728  Buffer buf;
1729  Buffer rbuf;
1730  Buffer metabuf = InvalidBuffer;
1731  Page metapg = NULL;
1732  BTMetaPageData *metad = NULL;
1733  ItemId itemid;
1734  Page page;
1735  BTPageOpaque opaque;
1736  bool rightsib_is_rightmost;
1737  int targetlevel;
1738  IndexTuple leafhikey;
1739  BlockNumber nextchild;
1740 
1741  page = BufferGetPage(leafbuf);
1742  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1743 
1744  Assert(P_ISLEAF(opaque) && P_ISHALFDEAD(opaque));
1745 
1746  /*
1747  * Remember some information about the leaf page.
1748  */
1749  itemid = PageGetItemId(page, P_HIKEY);
1750  leafhikey = (IndexTuple) PageGetItem(page, itemid);
1751  leafleftsib = opaque->btpo_prev;
1752  leafrightsib = opaque->btpo_next;
1753 
1754  LockBuffer(leafbuf, BUFFER_LOCK_UNLOCK);
1755 
1756  /*
1757  * Check here, as calling loops will have locks held, preventing
1758  * interrupts from being processed.
1759  */
1761 
1762  /*
1763  * If the leaf page still has a parent pointing to it (or a chain of
1764  * parents), we don't unlink the leaf page yet, but the topmost remaining
1765  * parent in the branch. Set 'target' and 'buf' to reference the page
1766  * actually being unlinked.
1767  */
1768  target = BTreeTupleGetTopParent(leafhikey);
1769 
1770  if (target != InvalidBlockNumber)
1771  {
1772  Assert(target != leafblkno);
1773 
1774  /* fetch the block number of the topmost parent's left sibling */
1775  buf = _bt_getbuf(rel, target, BT_READ);
1776  page = BufferGetPage(buf);
1777  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1778  leftsib = opaque->btpo_prev;
1779  targetlevel = opaque->btpo.level;
1780 
1781  /*
1782  * To avoid deadlocks, we'd better drop the target page lock before
1783  * going further.
1784  */
1786  }
1787  else
1788  {
1789  target = leafblkno;
1790 
1791  buf = leafbuf;
1792  leftsib = leafleftsib;
1793  targetlevel = 0;
1794  }
1795 
1796  /*
1797  * We have to lock the pages we need to modify in the standard order:
1798  * moving right, then up. Else we will deadlock against other writers.
1799  *
1800  * So, first lock the leaf page, if it's not the target. Then find and
1801  * write-lock the current left sibling of the target page. The sibling
1802  * that was current a moment ago could have split, so we may have to move
1803  * right. This search could fail if either the sibling or the target page
1804  * was deleted by someone else meanwhile; if so, give up. (Right now,
1805  * that should never happen, since page deletion is only done in VACUUM
1806  * and there shouldn't be multiple VACUUMs concurrently on the same
1807  * table.)
1808  */
1809  if (target != leafblkno)
1810  LockBuffer(leafbuf, BT_WRITE);
1811  if (leftsib != P_NONE)
1812  {
1813  lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
1814  page = BufferGetPage(lbuf);
1815  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1816  while (P_ISDELETED(opaque) || opaque->btpo_next != target)
1817  {
1818  /* step right one page */
1819  leftsib = opaque->btpo_next;
1820  _bt_relbuf(rel, lbuf);
1821 
1822  /*
1823  * It'd be good to check for interrupts here, but it's not easy to
1824  * do so because a lock is always held. This block isn't
1825  * frequently reached, so hopefully the consequences of not
1826  * checking interrupts aren't too bad.
1827  */
1828 
1829  if (leftsib == P_NONE)
1830  {
1831  elog(LOG, "no left sibling (concurrent deletion?) of block %u in \"%s\"",
1832  target,
1834  if (target != leafblkno)
1835  {
1836  /* we have only a pin on target, but pin+lock on leafbuf */
1837  ReleaseBuffer(buf);
1838  _bt_relbuf(rel, leafbuf);
1839  }
1840  else
1841  {
1842  /* we have only a pin on leafbuf */
1843  ReleaseBuffer(leafbuf);
1844  }
1845  return false;
1846  }
1847  lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
1848  page = BufferGetPage(lbuf);
1849  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1850  }
1851  }
1852  else
1853  lbuf = InvalidBuffer;
1854 
1855  /*
1856  * Next write-lock the target page itself. It should be okay to take just
1857  * a write lock not a superexclusive lock, since no scans would stop on an
1858  * empty page.
1859  */
1860  LockBuffer(buf, BT_WRITE);
1861  page = BufferGetPage(buf);
1862  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1863 
1864  /*
1865  * Check page is still empty etc, else abandon deletion. This is just for
1866  * paranoia's sake; a half-dead page cannot resurrect because there can be
1867  * only one vacuum process running at a time.
1868  */
1869  if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) || P_ISDELETED(opaque))
1870  {
1871  elog(ERROR, "half-dead page changed status unexpectedly in block %u of index \"%s\"",
1872  target, RelationGetRelationName(rel));
1873  }
1874  if (opaque->btpo_prev != leftsib)
1875  ereport(ERROR,
1876  (errcode(ERRCODE_INDEX_CORRUPTED),
1877  errmsg_internal("left link changed unexpectedly in block %u of index \"%s\"",
1878  target, RelationGetRelationName(rel))));
1879 
1880  if (target == leafblkno)
1881  {
1882  if (P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) ||
1883  !P_ISLEAF(opaque) || !P_ISHALFDEAD(opaque))
1884  elog(ERROR, "half-dead page changed status unexpectedly in block %u of index \"%s\"",
1885  target, RelationGetRelationName(rel));
1886  nextchild = InvalidBlockNumber;
1887  }
1888  else
1889  {
1890  if (P_FIRSTDATAKEY(opaque) != PageGetMaxOffsetNumber(page) ||
1891  P_ISLEAF(opaque))
1892  elog(ERROR, "half-dead page changed status unexpectedly in block %u of index \"%s\"",
1893  target, RelationGetRelationName(rel));
1894 
1895  /* remember the next non-leaf child down in the branch. */
1896  itemid = PageGetItemId(page, P_FIRSTDATAKEY(opaque));
1897  nextchild = BTreeTupleGetDownLink((IndexTuple) PageGetItem(page, itemid));
1898  if (nextchild == leafblkno)
1899  nextchild = InvalidBlockNumber;
1900  }
1901 
1902  /*
1903  * And next write-lock the (current) right sibling.
1904  */
1905  rightsib = opaque->btpo_next;
1906  rbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
1907  page = BufferGetPage(rbuf);
1908  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1909  if (opaque->btpo_prev != target)
1910  ereport(ERROR,
1911  (errcode(ERRCODE_INDEX_CORRUPTED),
1912  errmsg_internal("right sibling's left-link doesn't match: "
1913  "block %u links to %u instead of expected %u in index \"%s\"",
1914  rightsib, opaque->btpo_prev, target,
1915  RelationGetRelationName(rel))));
1916  rightsib_is_rightmost = P_RIGHTMOST(opaque);
1917  *rightsib_empty = (P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
1918 
1919  /*
1920  * If we are deleting the next-to-last page on the target's level, then
1921  * the rightsib is a candidate to become the new fast root. (In theory, it
1922  * might be possible to push the fast root even further down, but the odds
1923  * of doing so are slim, and the locking considerations daunting.)
1924  *
1925  * We can safely acquire a lock on the metapage here --- see comments for
1926  * _bt_newroot().
1927  */
1928  if (leftsib == P_NONE && rightsib_is_rightmost)
1929  {
1930  page = BufferGetPage(rbuf);
1931  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1932  if (P_RIGHTMOST(opaque))
1933  {
1934  /* rightsib will be the only one left on the level */
1935  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
1936  metapg = BufferGetPage(metabuf);
1937  metad = BTPageGetMeta(metapg);
1938 
1939  /*
1940  * The expected case here is btm_fastlevel == targetlevel+1; if
1941  * the fastlevel is <= targetlevel, something is wrong, and we
1942  * choose to overwrite it to fix it.
1943  */
1944  if (metad->btm_fastlevel > targetlevel + 1)
1945  {
1946  /* no update wanted */
1947  _bt_relbuf(rel, metabuf);
1948  metabuf = InvalidBuffer;
1949  }
1950  }
1951  }
1952 
1953  /*
1954  * Here we begin doing the deletion.
1955  */
1956 
1957  /* No ereport(ERROR) until changes are logged */
1959 
1960  /*
1961  * Update siblings' side-links. Note the target page's side-links will
1962  * continue to point to the siblings. Asserts here are just rechecking
1963  * things we already verified above.
1964  */
1965  if (BufferIsValid(lbuf))
1966  {
1967  page = BufferGetPage(lbuf);
1968  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1969  Assert(opaque->btpo_next == target);
1970  opaque->btpo_next = rightsib;
1971  }
1972  page = BufferGetPage(rbuf);
1973  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1974  Assert(opaque->btpo_prev == target);
1975  opaque->btpo_prev = leftsib;
1976 
1977  /*
1978  * If we deleted a parent of the targeted leaf page, instead of the leaf
1979  * itself, update the leaf to point to the next remaining child in the
1980  * branch.
1981  */
1982  if (target != leafblkno)
1983  BTreeTupleSetTopParent(leafhikey, nextchild);
1984 
1985  /*
1986  * Mark the page itself deleted. It can be recycled when all current
1987  * transactions are gone. Storing GetTopTransactionId() would work, but
1988  * we're in VACUUM and would not otherwise have an XID. Having already
1989  * updated links to the target, ReadNewTransactionId() suffices as an
1990  * upper bound. Any scan having retained a now-stale link is advertising
1991  * in its PGXACT an xmin less than or equal to the value we read here. It
1992  * will continue to do so, holding back RecentGlobalXmin, for the duration
1993  * of that scan.
1994  */
1995  page = BufferGetPage(buf);
1996  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1997  opaque->btpo_flags &= ~BTP_HALF_DEAD;
1998  opaque->btpo_flags |= BTP_DELETED;
1999  opaque->btpo.xact = ReadNewTransactionId();
2000 
2001  /* And update the metapage, if needed */
2002  if (BufferIsValid(metabuf))
2003  {
2004  /* upgrade metapage if needed */
2005  if (metad->btm_version < BTREE_NOVAC_VERSION)
2006  _bt_upgrademetapage(metapg);
2007  metad->btm_fastroot = rightsib;
2008  metad->btm_fastlevel = targetlevel;
2009  MarkBufferDirty(metabuf);
2010  }
2011 
2012  /* Must mark buffers dirty before XLogInsert */
2013  MarkBufferDirty(rbuf);
2014  MarkBufferDirty(buf);
2015  if (BufferIsValid(lbuf))
2016  MarkBufferDirty(lbuf);
2017  if (target != leafblkno)
2018  MarkBufferDirty(leafbuf);
2019 
2020  /* XLOG stuff */
2021  if (RelationNeedsWAL(rel))
2022  {
2023  xl_btree_unlink_page xlrec;
2024  xl_btree_metadata xlmeta;
2025  uint8 xlinfo;
2026  XLogRecPtr recptr;
2027 
2028  XLogBeginInsert();
2029 
2031  if (BufferIsValid(lbuf))
2034  if (target != leafblkno)
2035  XLogRegisterBuffer(3, leafbuf, REGBUF_WILL_INIT);
2036 
2037  /* information on the unlinked block */
2038  xlrec.leftsib = leftsib;
2039  xlrec.rightsib = rightsib;
2040  xlrec.btpo_xact = opaque->btpo.xact;
2041 
2042  /* information needed to recreate the leaf block (if not the target) */
2043  xlrec.leafleftsib = leafleftsib;
2044  xlrec.leafrightsib = leafrightsib;
2045  xlrec.topparent = nextchild;
2046 
2047  XLogRegisterData((char *) &xlrec, SizeOfBtreeUnlinkPage);
2048 
2049  if (BufferIsValid(metabuf))
2050  {
2052 
2054  xlmeta.version = metad->btm_version;
2055  xlmeta.root = metad->btm_root;
2056  xlmeta.level = metad->btm_level;
2057  xlmeta.fastroot = metad->btm_fastroot;
2058  xlmeta.fastlevel = metad->btm_fastlevel;
2059  xlmeta.oldest_btpo_xact = metad->btm_oldest_btpo_xact;
2061 
2062  XLogRegisterBufData(4, (char *) &xlmeta, sizeof(xl_btree_metadata));
2063  xlinfo = XLOG_BTREE_UNLINK_PAGE_META;
2064  }
2065  else
2066  xlinfo = XLOG_BTREE_UNLINK_PAGE;
2067 
2068  recptr = XLogInsert(RM_BTREE_ID, xlinfo);
2069 
2070  if (BufferIsValid(metabuf))
2071  {
2072  PageSetLSN(metapg, recptr);
2073  }
2074  page = BufferGetPage(rbuf);
2075  PageSetLSN(page, recptr);
2076  page = BufferGetPage(buf);
2077  PageSetLSN(page, recptr);
2078  if (BufferIsValid(lbuf))
2079  {
2080  page = BufferGetPage(lbuf);
2081  PageSetLSN(page, recptr);
2082  }
2083  if (target != leafblkno)
2084  {
2085  page = BufferGetPage(leafbuf);
2086  PageSetLSN(page, recptr);
2087  }
2088  }
2089 
2090  END_CRIT_SECTION();
2091 
2092  /* release metapage */
2093  if (BufferIsValid(metabuf))
2094  _bt_relbuf(rel, metabuf);
2095 
2096  /* release siblings */
2097  if (BufferIsValid(lbuf))
2098  _bt_relbuf(rel, lbuf);
2099  _bt_relbuf(rel, rbuf);
2100 
2101  /*
2102  * Release the target, if it was not the leaf block. The leaf is always
2103  * kept locked.
2104  */
2105  if (target != leafblkno)
2106  _bt_relbuf(rel, buf);
2107 
2108  return true;
2109 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:361
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:86
BlockNumber btpo_next
Definition: nbtree.h:59
void _bt_upgrademetapage(Page page)
Definition: nbtpage.c:88
uint32 btm_version
Definition: nbtree.h:101
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:757
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1458
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define BTreeTupleGetDownLink(itup)
Definition: nbtree.h:301
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:220
#define BTP_HALF_DEAD
Definition: nbtree.h:76
union BTPageOpaqueData::@46 btpo
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
BlockNumber root
Definition: nbtxlog.h:50
unsigned char uint8
Definition: c.h:357
#define P_NONE
Definition: nbtree.h:182
#define InvalidBuffer
Definition: buf.h:25
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
int errcode(int sqlerrcode)
Definition: elog.c:608
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3375
#define BTP_DELETED
Definition: nbtree.h:74
#define LOG
Definition: elog.h:26
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
TransactionId xact
Definition: nbtree.h:63
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
#define BT_READ
Definition: nbtree.h:402
BlockNumber btm_fastroot
Definition: nbtree.h:104
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:194
#define ERROR
Definition: elog.h:43
float8 last_cleanup_num_heap_tuples
Definition: nbtxlog.h:55
#define BTreeTupleSetTopParent(itup, blkno)
Definition: nbtree.h:314
TransactionId oldest_btpo_xact
Definition: nbtxlog.h:54
#define BTPageGetMeta(p)
Definition: nbtree.h:113
BlockNumber btpo_prev
Definition: nbtree.h:58
static char * buf
Definition: pg_test_fsync.c:67
IndexTupleData * IndexTuple
Definition: itup.h:53
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define RelationGetRelationName(relation)
Definition: rel.h:462
#define XLOG_BTREE_UNLINK_PAGE
Definition: nbtxlog.h:33
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:136
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define ereport(elevel, rest)
Definition: elog.h:141
#define BTREE_METAPAGE
Definition: nbtree.h:132
#define P_ISDELETED(opaque)
Definition: nbtree.h:192
#define P_ISROOT(opaque)
Definition: nbtree.h:191
uint32 version
Definition: nbtxlog.h:49
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
uint32 btm_fastlevel
Definition: nbtree.h:105
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
uint32 level
Definition: nbtree.h:62
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3612
BlockNumber btm_root
Definition: nbtree.h:102
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
int errmsg_internal(const char *fmt,...)
Definition: elog.c:909
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:739
#define BTreeTupleGetTopParent(itup)
Definition: nbtree.h:312
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define InvalidBlockNumber
Definition: block.h:33
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:109
#define BufferIsValid(bufnum)
Definition: bufmgr.h:113
#define RelationNeedsWAL(relation)
Definition: rel.h:530
static TransactionId ReadNewTransactionId(void)
Definition: transam.h:244
#define P_HIKEY
Definition: nbtree.h:218
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2623
uint32 fastlevel
Definition: nbtxlog.h:53
uint32 btm_level
Definition: nbtree.h:103
#define elog(elevel,...)
Definition: elog.h:228
uint32 level
Definition: nbtxlog.h:51
#define SizeOfBtreeUnlinkPage
Definition: nbtxlog.h:220
BlockNumber fastroot
Definition: nbtxlog.h:52
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:99
#define XLOG_BTREE_UNLINK_PAGE_META
Definition: nbtxlog.h:34
TransactionId btm_oldest_btpo_xact
Definition: nbtree.h:107
#define BT_WRITE
Definition: nbtree.h:403
void XLogBeginInsert(void)
Definition: xloginsert.c:120
uint16 btpo_flags
Definition: nbtree.h:65
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:189
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define P_ISLEAF(opaque)
Definition: nbtree.h:190

◆ _bt_update_meta_cleanup_info()

void _bt_update_meta_cleanup_info ( Relation  rel,
TransactionId  oldestBtpoXact,
float8  numHeapTuples 
)

Definition at line 158 of file nbtpage.c.

References _bt_getbuf(), _bt_relbuf(), _bt_upgrademetapage(), Assert, BT_READ, BT_WRITE, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_last_cleanup_num_heap_tuples, BTMetaPageData::btm_level, BTMetaPageData::btm_oldest_btpo_xact, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTPageGetMeta, BTREE_METAPAGE, BTREE_NOVAC_VERSION, BUFFER_LOCK_UNLOCK, BufferGetPage, END_CRIT_SECTION, xl_btree_metadata::fastlevel, xl_btree_metadata::fastroot, xl_btree_metadata::last_cleanup_num_heap_tuples, xl_btree_metadata::level, LockBuffer(), MarkBufferDirty(), xl_btree_metadata::oldest_btpo_xact, PageSetLSN, REGBUF_STANDARD, REGBUF_WILL_INIT, RelationNeedsWAL, xl_btree_metadata::root, START_CRIT_SECTION, xl_btree_metadata::version, XLOG_BTREE_META_CLEANUP, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), and XLogRegisterBuffer().

Referenced by btbulkdelete(), and btvacuumcleanup().

160 {
161  Buffer metabuf;
162  Page metapg;
163  BTMetaPageData *metad;
164  bool needsRewrite = false;
165  XLogRecPtr recptr;
166 
167  /* read the metapage and check if it needs rewrite */
168  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
169  metapg = BufferGetPage(metabuf);
170  metad = BTPageGetMeta(metapg);
171 
172  /* outdated version of metapage always needs rewrite */
173  if (metad->btm_version < BTREE_NOVAC_VERSION)
174  needsRewrite = true;
175  else if (metad->btm_oldest_btpo_xact != oldestBtpoXact ||
176  metad->btm_last_cleanup_num_heap_tuples != numHeapTuples)
177  needsRewrite = true;
178 
179  if (!needsRewrite)
180  {
181  _bt_relbuf(rel, metabuf);
182  return;
183  }
184 
185  /* trade in our read lock for a write lock */
186  LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
187  LockBuffer(metabuf, BT_WRITE);
188 
190 
191  /* upgrade meta-page if needed */
192  if (metad->btm_version < BTREE_NOVAC_VERSION)
193  _bt_upgrademetapage(metapg);
194 
195  /* update cleanup-related information */
196  metad->btm_oldest_btpo_xact = oldestBtpoXact;
197  metad->btm_last_cleanup_num_heap_tuples = numHeapTuples;
198  MarkBufferDirty(metabuf);
199 
200  /* write wal record if needed */
201  if (RelationNeedsWAL(rel))
202  {
204 
205  XLogBeginInsert();
207 
209  md.version = metad->btm_version;
210  md.root = metad->btm_root;
211  md.level = metad->btm_level;
212  md.fastroot = metad->btm_fastroot;
213  md.fastlevel = metad->btm_fastlevel;
214  md.oldest_btpo_xact = oldestBtpoXact;
215  md.last_cleanup_num_heap_tuples = numHeapTuples;
216 
217  XLogRegisterBufData(0, (char *) &md, sizeof(xl_btree_metadata));
218 
219  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_META_CLEANUP);
220 
221  PageSetLSN(metapg, recptr);
222  }
223 
225  _bt_relbuf(rel, metabuf);
226 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:361
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:86
void _bt_upgrademetapage(Page page)
Definition: nbtpage.c:88
uint32 btm_version
Definition: nbtree.h:101
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:757
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1458
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
BlockNumber root
Definition: nbtxlog.h:50
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
#define BT_READ
Definition: nbtree.h:402
BlockNumber btm_fastroot
Definition: nbtree.h:104
float8 last_cleanup_num_heap_tuples
Definition: nbtxlog.h:55
TransactionId oldest_btpo_xact
Definition: nbtxlog.h:54
#define BTPageGetMeta(p)
Definition: nbtree.h:113
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:136
#define BufferGetPage(buffer)
Definition: bufmgr.h:159
#define BTREE_METAPAGE
Definition: nbtree.h:132
uint32 version
Definition: nbtxlog.h:49
uint32 btm_fastlevel
Definition: nbtree.h:105
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3612
BlockNumber btm_root
Definition: nbtree.h:102
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:912
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:739
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:109
#define RelationNeedsWAL(relation)
Definition: rel.h:530
uint32 fastlevel
Definition: nbtxlog.h:53
uint32 btm_level
Definition: nbtree.h:103
uint32 level
Definition: nbtxlog.h:51
BlockNumber fastroot
Definition: nbtxlog.h:52
TransactionId btm_oldest_btpo_xact
Definition: nbtree.h:107
#define BT_WRITE
Definition: nbtree.h:403
void XLogBeginInsert(void)
Definition: xloginsert.c:120
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define XLOG_BTREE_META_CLEANUP
Definition: nbtxlog.h:41
Pointer Page
Definition: bufpage.h:78

◆ _bt_upgrademetapage()

void _bt_upgrademetapage ( Page  page)

Definition at line 88 of file nbtpage.c.

References Assert, BTMetaPageData::btm_last_cleanup_num_heap_tuples, BTMetaPageData::btm_oldest_btpo_xact, BTMetaPageData::btm_version, BTP_META, BTPageGetMeta, BTREE_MIN_VERSION, BTREE_NOVAC_VERSION, InvalidTransactionId, PageGetSpecialPointer, and PG_USED_FOR_ASSERTS_ONLY.

Referenced by _bt_getroot(), _bt_insertonpg(), _bt_newroot(), _bt_unlink_halfdead_page(), and _bt_update_meta_cleanup_info().

89 {
90  BTMetaPageData *metad;
92 
93  metad = BTPageGetMeta(page);
94  metaopaque = (BTPageOpaque) PageGetSpecialPointer(page);
95 
96  /* It must be really a meta page of upgradable version */
97  Assert(metaopaque->btpo_flags & BTP_META);
100 
101  /* Set version number and fill extra fields added into version 3 */
104  metad->btm_last_cleanup_num_heap_tuples = -1.0;
105 
106  /* Adjust pd_lower (see _bt_initmetapage() for details) */
107  ((PageHeader) page)->pd_lower =
108  ((char *) metad + sizeof(BTMetaPageData)) - (char *) page;
109 }
uint32 btm_version
Definition: nbtree.h:101
#define BTP_META
Definition: nbtree.h:75
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
#define BTPageGetMeta(p)
Definition: nbtree.h:113
#define InvalidTransactionId
Definition: transam.h:31
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:136
#define BTREE_MIN_VERSION
Definition: nbtree.h:135
PageHeaderData * PageHeader
Definition: bufpage.h:166
#define Assert(condition)
Definition: c.h:739
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:109
TransactionId btm_oldest_btpo_xact
Definition: nbtree.h:107
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:123