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 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)
 
Buffer _bt_getroot (Relation rel, int access)
 
Buffer _bt_gettrueroot (Relation rel)
 
int _bt_getrootheight (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 *itemnos, int nitems, BlockNumber lastBlockVacuumed)
 
void _bt_delitems_delete (Relation rel, Buffer buf, OffsetNumber *itemnos, int nitems, 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 504 of file nbtpage.c.

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

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

505 {
506  Page page = BufferGetPage(buf);
507 
508  /*
509  * ReadBuffer verifies that every newly-read page passes
510  * PageHeaderIsValid, which means it either contains a reasonably sane
511  * page header or is all-zero. We have to defend against the all-zero
512  * case, however.
513  */
514  if (PageIsNew(page))
515  ereport(ERROR,
516  (errcode(ERRCODE_INDEX_CORRUPTED),
517  errmsg("index \"%s\" contains unexpected zero page at block %u",
520  errhint("Please REINDEX it.")));
521 
522  /*
523  * Additionally check that the special area looks sane.
524  */
525  if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BTPageOpaqueData)))
526  ereport(ERROR,
527  (errcode(ERRCODE_INDEX_CORRUPTED),
528  errmsg("index \"%s\" contains corrupted page at block %u",
531  errhint("Please REINDEX it.")));
532 }
int errhint(const char *fmt,...)
Definition: elog.c:987
int errcode(int sqlerrcode)
Definition: elog.c:575
#define ERROR
Definition: elog.h:43
static char * buf
Definition: pg_test_fsync.c:67
#define RelationGetRelationName(relation)
Definition: rel.h:436
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
#define MAXALIGN(LEN)
Definition: c.h:623
#define PageGetSpecialSize(page)
Definition: bufpage.h:296
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
#define PageIsNew(page)
Definition: bufpage.h:225
int errmsg(const char *fmt,...)
Definition: elog.c:797
Pointer Page
Definition: bufpage.h:74

◆ _bt_delitems_delete()

void _bt_delitems_delete ( Relation  rel,
Buffer  buf,
OffsetNumber itemnos,
int  nitems,
Relation  heapRel 
)

Definition at line 863 of file nbtpage.c.

References Assert, BTP_HAS_GARBAGE, BTPageOpaqueData::btpo_flags, BufferGetPage, END_CRIT_SECTION, xl_btree_delete::hnode, MarkBufferDirty(), xl_btree_delete::nitems, PageGetSpecialPointer, PageIndexMultiDelete(), PageSetLSN, RelationData::rd_node, REGBUF_STANDARD, RelationNeedsWAL, SizeOfBtreeDelete, START_CRIT_SECTION, XLOG_BTREE_DELETE, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_vacuum_one_page().

866 {
867  Page page = BufferGetPage(buf);
868  BTPageOpaque opaque;
869 
870  /* Shouldn't be called unless there's something to do */
871  Assert(nitems > 0);
872 
873  /* No ereport(ERROR) until changes are logged */
875 
876  /* Fix the page */
877  PageIndexMultiDelete(page, itemnos, nitems);
878 
879  /*
880  * Unlike _bt_delitems_vacuum, we *must not* clear the vacuum cycle ID,
881  * because this is not called by VACUUM.
882  */
883 
884  /*
885  * Mark the page as not containing any LP_DEAD items. This is not
886  * certainly true (there might be some that have recently been marked, but
887  * weren't included in our target-item list), but it will almost always be
888  * true and it doesn't seem worth an additional page scan to check it.
889  * Remember that BTP_HAS_GARBAGE is only a hint anyway.
890  */
891  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
892  opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
893 
895 
896  /* XLOG stuff */
897  if (RelationNeedsWAL(rel))
898  {
899  XLogRecPtr recptr;
900  xl_btree_delete xlrec_delete;
901 
902  xlrec_delete.hnode = heapRel->rd_node;
903  xlrec_delete.nitems = nitems;
904 
905  XLogBeginInsert();
907  XLogRegisterData((char *) &xlrec_delete, SizeOfBtreeDelete);
908 
909  /*
910  * We need the target-offsets array whether or not we store the whole
911  * buffer, to allow us to find the latestRemovedXid on a standby
912  * server.
913  */
914  XLogRegisterData((char *) itemnos, nitems * sizeof(OffsetNumber));
915 
916  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_DELETE);
917 
918  PageSetLSN(page, recptr);
919  }
920 
922 }
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
RelFileNode hnode
Definition: nbtxlog.h:120
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
uint16 OffsetNumber
Definition: off.h:24
static char * buf
Definition: pg_test_fsync.c:67
#define REGBUF_STANDARD
Definition: xloginsert.h:34
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#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
RelFileNode rd_node
Definition: rel.h:85
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:670
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:832
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
#define RelationNeedsWAL(relation)
Definition: rel.h:505
#define SizeOfBtreeDelete
Definition: nbtxlog.h:127
void XLogBeginInsert(void)
Definition: xloginsert.c:120
uint16 btpo_flags
Definition: nbtree.h:63
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
#define BTP_HAS_GARBAGE
Definition: nbtree.h:76
Pointer Page
Definition: bufpage.h:74

◆ _bt_delitems_vacuum()

void _bt_delitems_vacuum ( Relation  rel,
Buffer  buf,
OffsetNumber itemnos,
int  nitems,
BlockNumber  lastBlockVacuumed 
)

Definition at line 790 of file nbtpage.c.

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

Referenced by btvacuumpage(), and btvacuumscan().

793 {
794  Page page = BufferGetPage(buf);
795  BTPageOpaque opaque;
796 
797  /* No ereport(ERROR) until changes are logged */
799 
800  /* Fix the page */
801  if (nitems > 0)
802  PageIndexMultiDelete(page, itemnos, nitems);
803 
804  /*
805  * We can clear the vacuum cycle ID since this page has certainly been
806  * processed by the current vacuum scan.
807  */
808  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
809  opaque->btpo_cycleid = 0;
810 
811  /*
812  * Mark the page as not containing any LP_DEAD items. This is not
813  * certainly true (there might be some that have recently been marked, but
814  * weren't included in our target-item list), but it will almost always be
815  * true and it doesn't seem worth an additional page scan to check it.
816  * Remember that BTP_HAS_GARBAGE is only a hint anyway.
817  */
818  opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
819 
821 
822  /* XLOG stuff */
823  if (RelationNeedsWAL(rel))
824  {
825  XLogRecPtr recptr;
826  xl_btree_vacuum xlrec_vacuum;
827 
828  xlrec_vacuum.lastBlockVacuumed = lastBlockVacuumed;
829 
830  XLogBeginInsert();
832  XLogRegisterData((char *) &xlrec_vacuum, SizeOfBtreeVacuum);
833 
834  /*
835  * The target-offsets array is not in the buffer, but pretend that it
836  * is. When XLogInsert stores the whole buffer, the offsets array
837  * need not be stored too.
838  */
839  if (nitems > 0)
840  XLogRegisterBufData(0, (char *) itemnos, nitems * sizeof(OffsetNumber));
841 
842  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_VACUUM);
843 
844  PageSetLSN(page, recptr);
845  }
846 
848 }
BlockNumber lastBlockVacuumed
Definition: nbtxlog.h:166
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:361
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
uint16 OffsetNumber
Definition: off.h:24
#define SizeOfBtreeVacuum
Definition: nbtxlog.h:171
BTCycleId btpo_cycleid
Definition: nbtree.h:64
static char * buf
Definition: pg_test_fsync.c:67
#define REGBUF_STANDARD
Definition: xloginsert.h:34
#define XLOG_BTREE_VACUUM
Definition: nbtxlog.h:37
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
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
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:832
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
#define RelationNeedsWAL(relation)
Definition: rel.h:505
void XLogBeginInsert(void)
Definition: xloginsert.c:120
uint16 btpo_flags
Definition: nbtree.h:63
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
#define BTP_HAS_GARBAGE
Definition: nbtree.h:76
Pointer Page
Definition: bufpage.h:74

◆ _bt_getbuf()

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

Definition at line 571 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_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(), and _bt_walk_left().

572 {
573  Buffer buf;
574 
575  if (blkno != P_NEW)
576  {
577  /* Read an existing block of the relation */
578  buf = ReadBuffer(rel, blkno);
579  LockBuffer(buf, access);
580  _bt_checkpage(rel, buf);
581  }
582  else
583  {
584  bool needLock;
585  Page page;
586 
587  Assert(access == BT_WRITE);
588 
589  /*
590  * First see if the FSM knows of any free pages.
591  *
592  * We can't trust the FSM's report unreservedly; we have to check that
593  * the page is still free. (For example, an already-free page could
594  * have been re-used between the time the last VACUUM scanned it and
595  * the time the VACUUM made its FSM updates.)
596  *
597  * In fact, it's worse than that: we can't even assume that it's safe
598  * to take a lock on the reported page. If somebody else has a lock
599  * on it, or even worse our own caller does, we could deadlock. (The
600  * own-caller scenario is actually not improbable. Consider an index
601  * on a serial or timestamp column. Nearly all splits will be at the
602  * rightmost page, so it's entirely likely that _bt_split will call us
603  * while holding a lock on the page most recently acquired from FSM. A
604  * VACUUM running concurrently with the previous split could well have
605  * placed that page back in FSM.)
606  *
607  * To get around that, we ask for only a conditional lock on the
608  * reported page. If we fail, then someone else is using the page,
609  * and we may reasonably assume it's not free. (If we happen to be
610  * wrong, the worst consequence is the page will be lost to use till
611  * the next VACUUM, which is no big problem.)
612  */
613  for (;;)
614  {
615  blkno = GetFreeIndexPage(rel);
616  if (blkno == InvalidBlockNumber)
617  break;
618  buf = ReadBuffer(rel, blkno);
619  if (ConditionalLockBuffer(buf))
620  {
621  page = BufferGetPage(buf);
622  if (_bt_page_recyclable(page))
623  {
624  /*
625  * If we are generating WAL for Hot Standby then create a
626  * WAL record that will allow us to conflict with queries
627  * running on standby.
628  */
630  {
632 
633  _bt_log_reuse_page(rel, blkno, opaque->btpo.xact);
634  }
635 
636  /* Okay to use page. Re-initialize and return it */
637  _bt_pageinit(page, BufferGetPageSize(buf));
638  return buf;
639  }
640  elog(DEBUG2, "FSM returned nonrecyclable page");
641  _bt_relbuf(rel, buf);
642  }
643  else
644  {
645  elog(DEBUG2, "FSM returned nonlockable page");
646  /* couldn't get lock, so just drop pin */
647  ReleaseBuffer(buf);
648  }
649  }
650 
651  /*
652  * Extend the relation by one page.
653  *
654  * We have to use a lock to ensure no one else is extending the rel at
655  * the same time, else we will both try to initialize the same new
656  * page. We can skip locking for new or temp relations, however,
657  * since no one else could be accessing them.
658  */
659  needLock = !RELATION_IS_LOCAL(rel);
660 
661  if (needLock)
663 
664  buf = ReadBuffer(rel, P_NEW);
665 
666  /* Acquire buffer lock on new page */
667  LockBuffer(buf, BT_WRITE);
668 
669  /*
670  * Release the file-extension lock; it's now OK for someone else to
671  * extend the relation some more. Note that we cannot release this
672  * lock before we have buffer lock on the new page, or we risk a race
673  * condition against btvacuumscan --- see comments therein.
674  */
675  if (needLock)
677 
678  /* Initialize the new page before returning it */
679  page = BufferGetPage(buf);
680  Assert(PageIsNew(page));
681  _bt_pageinit(page, BufferGetPageSize(buf));
682  }
683 
684  /* ref count and lock type are correct */
685  return buf;
686 }
#define ExclusiveLock
Definition: lockdefs.h:44
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:523
union BTPageOpaqueData::@46 btpo
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
#define P_NEW
Definition: bufmgr.h:82
bool _bt_page_recyclable(Page page)
Definition: nbtpage.c:745
TransactionId xact
Definition: nbtree.h:61
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
void _bt_checkpage(Relation rel, Buffer buf)
Definition: nbtpage.c:504
#define DEBUG2
Definition: elog.h:24
static char * buf
Definition: pg_test_fsync.c:67
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3572
static void _bt_log_reuse_page(Relation rel, BlockNumber blkno, TransactionId latestRemovedXid)
Definition: nbtpage.c:538
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:332
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:382
#define BufferGetPageSize(buffer)
Definition: bufmgr.h:147
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
#define XLogStandbyInfoActive()
Definition: xlog.h:160
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:721
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
#define Assert(condition)
Definition: c.h:670
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:594
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
#define InvalidBlockNumber
Definition: block.h:33
#define RelationNeedsWAL(relation)
Definition: rel.h:505
#define PageIsNew(page)
Definition: bufpage.h:225
void _bt_pageinit(Page page, Size size)
Definition: nbtpage.c:733
#define elog
Definition: elog.h:219
#define BT_WRITE
Definition: nbtree.h:239
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:74

◆ _bt_getroot()

Buffer _bt_getroot ( Relation  rel,
int  access 
)

Definition at line 105 of file nbtpage.c.

References _bt_getbuf(), _bt_getroot(), _bt_relandgetbuf(), _bt_relbuf(), Assert, BT_READ, BT_WRITE, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_level, BTMetaPageData::btm_magic, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTP_LEAF, BTP_ROOT, BTPageGetMeta, BTPageOpaqueData::btpo, BTPageOpaqueData::btpo_cycleid, BTPageOpaqueData::btpo_flags, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BTREE_MAGIC, BTREE_METAPAGE, BTREE_VERSION, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage, elog, END_CRIT_SECTION, ereport, errcode(), errmsg(), ERROR, xl_btree_metadata::fastlevel, xl_btree_metadata::fastroot, InvalidBuffer, xl_btree_metadata::level, BTPageOpaqueData::level, xl_btree_newroot::level, LockBuffer(), MarkBufferDirty(), MemoryContextAlloc(), P_IGNORE, P_ISMETA, 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, XLOG_BTREE_NEWROOT, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

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

106 {
107  Buffer metabuf;
108  Page metapg;
109  BTPageOpaque metaopaque;
110  Buffer rootbuf;
111  Page rootpage;
112  BTPageOpaque rootopaque;
113  BlockNumber rootblkno;
114  uint32 rootlevel;
115  BTMetaPageData *metad;
116 
117  /*
118  * Try to use previously-cached metapage data to find the root. This
119  * normally saves one buffer access per index search, which is a very
120  * helpful savings in bufmgr traffic and hence contention.
121  */
122  if (rel->rd_amcache != NULL)
123  {
124  metad = (BTMetaPageData *) rel->rd_amcache;
125  /* We shouldn't have cached it if any of these fail */
126  Assert(metad->btm_magic == BTREE_MAGIC);
127  Assert(metad->btm_version == BTREE_VERSION);
128  Assert(metad->btm_root != P_NONE);
129 
130  rootblkno = metad->btm_fastroot;
131  Assert(rootblkno != P_NONE);
132  rootlevel = metad->btm_fastlevel;
133 
134  rootbuf = _bt_getbuf(rel, rootblkno, BT_READ);
135  rootpage = BufferGetPage(rootbuf);
136  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
137 
138  /*
139  * Since the cache might be stale, we check the page more carefully
140  * here than normal. We *must* check that it's not deleted. If it's
141  * not alone on its level, then we reject too --- this may be overly
142  * paranoid but better safe than sorry. Note we don't check P_ISROOT,
143  * because that's not set in a "fast root".
144  */
145  if (!P_IGNORE(rootopaque) &&
146  rootopaque->btpo.level == rootlevel &&
147  P_LEFTMOST(rootopaque) &&
148  P_RIGHTMOST(rootopaque))
149  {
150  /* OK, accept cached page as the root */
151  return rootbuf;
152  }
153  _bt_relbuf(rel, rootbuf);
154  /* Cache is stale, throw it away */
155  if (rel->rd_amcache)
156  pfree(rel->rd_amcache);
157  rel->rd_amcache = NULL;
158  }
159 
160  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
161  metapg = BufferGetPage(metabuf);
162  metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
163  metad = BTPageGetMeta(metapg);
164 
165  /* sanity-check the metapage */
166  if (!P_ISMETA(metaopaque) ||
167  metad->btm_magic != BTREE_MAGIC)
168  ereport(ERROR,
169  (errcode(ERRCODE_INDEX_CORRUPTED),
170  errmsg("index \"%s\" is not a btree",
171  RelationGetRelationName(rel))));
172 
173  if (metad->btm_version != BTREE_VERSION)
174  ereport(ERROR,
175  (errcode(ERRCODE_INDEX_CORRUPTED),
176  errmsg("version mismatch in index \"%s\": file version %d, code version %d",
178  metad->btm_version, BTREE_VERSION)));
179 
180  /* if no root page initialized yet, do it */
181  if (metad->btm_root == P_NONE)
182  {
183  /* If access = BT_READ, caller doesn't want us to create root yet */
184  if (access == BT_READ)
185  {
186  _bt_relbuf(rel, metabuf);
187  return InvalidBuffer;
188  }
189 
190  /* trade in our read lock for a write lock */
191  LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
192  LockBuffer(metabuf, BT_WRITE);
193 
194  /*
195  * Race condition: if someone else initialized the metadata between
196  * the time we released the read lock and acquired the write lock, we
197  * must avoid doing it again.
198  */
199  if (metad->btm_root != P_NONE)
200  {
201  /*
202  * Metadata initialized by someone else. In order to guarantee no
203  * deadlocks, we have to release the metadata page and start all
204  * over again. (Is that really true? But it's hardly worth trying
205  * to optimize this case.)
206  */
207  _bt_relbuf(rel, metabuf);
208  return _bt_getroot(rel, access);
209  }
210 
211  /*
212  * Get, initialize, write, and leave a lock of the appropriate type on
213  * the new root page. Since this is the first page in the tree, it's
214  * a leaf as well as the root.
215  */
216  rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE);
217  rootblkno = BufferGetBlockNumber(rootbuf);
218  rootpage = BufferGetPage(rootbuf);
219  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
220  rootopaque->btpo_prev = rootopaque->btpo_next = P_NONE;
221  rootopaque->btpo_flags = (BTP_LEAF | BTP_ROOT);
222  rootopaque->btpo.level = 0;
223  rootopaque->btpo_cycleid = 0;
224 
225  /* NO ELOG(ERROR) till meta is updated */
227 
228  metad->btm_root = rootblkno;
229  metad->btm_level = 0;
230  metad->btm_fastroot = rootblkno;
231  metad->btm_fastlevel = 0;
232 
233  MarkBufferDirty(rootbuf);
234  MarkBufferDirty(metabuf);
235 
236  /* XLOG stuff */
237  if (RelationNeedsWAL(rel))
238  {
239  xl_btree_newroot xlrec;
240  XLogRecPtr recptr;
242 
243  XLogBeginInsert();
246 
247  md.root = rootblkno;
248  md.level = 0;
249  md.fastroot = rootblkno;
250  md.fastlevel = 0;
251 
252  XLogRegisterBufData(2, (char *) &md, sizeof(xl_btree_metadata));
253 
254  xlrec.rootblk = rootblkno;
255  xlrec.level = 0;
256 
257  XLogRegisterData((char *) &xlrec, SizeOfBtreeNewroot);
258 
259  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_NEWROOT);
260 
261  PageSetLSN(rootpage, recptr);
262  PageSetLSN(metapg, recptr);
263  }
264 
266 
267  /*
268  * swap root write lock for read lock. There is no danger of anyone
269  * else accessing the new root page while it's unlocked, since no one
270  * else knows where it is yet.
271  */
272  LockBuffer(rootbuf, BUFFER_LOCK_UNLOCK);
273  LockBuffer(rootbuf, BT_READ);
274 
275  /* okay, metadata is correct, release lock on it */
276  _bt_relbuf(rel, metabuf);
277  }
278  else
279  {
280  rootblkno = metad->btm_fastroot;
281  Assert(rootblkno != P_NONE);
282  rootlevel = metad->btm_fastlevel;
283 
284  /*
285  * Cache the metapage data for next time
286  */
288  sizeof(BTMetaPageData));
289  memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
290 
291  /*
292  * We are done with the metapage; arrange to release it via first
293  * _bt_relandgetbuf call
294  */
295  rootbuf = metabuf;
296 
297  for (;;)
298  {
299  rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, BT_READ);
300  rootpage = BufferGetPage(rootbuf);
301  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
302 
303  if (!P_IGNORE(rootopaque))
304  break;
305 
306  /* it's dead, Jim. step right one page */
307  if (P_RIGHTMOST(rootopaque))
308  elog(ERROR, "no live root page found in index \"%s\"",
310  rootblkno = rootopaque->btpo_next;
311  }
312 
313  /* Note: can't check btpo.level on deleted pages */
314  if (rootopaque->btpo.level != rootlevel)
315  elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
316  rootblkno, RelationGetRelationName(rel),
317  rootopaque->btpo.level, rootlevel);
318  }
319 
320  /*
321  * By here, we have a pin and read lock on the root page, and no lock set
322  * on the metadata page. Return the root page's buffer.
323  */
324  return rootbuf;
325 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:361
BlockNumber rootblk
Definition: nbtxlog.h:239
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:87
#define BTP_ROOT
Definition: nbtree.h:71
BlockNumber btpo_next
Definition: nbtree.h:57
#define P_IGNORE(opaque)
Definition: nbtree.h:181
uint32 btm_version
Definition: nbtree.h:99
Buffer _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access)
Definition: nbtpage.c:702
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:571
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define BTREE_VERSION
Definition: nbtree.h:111
uint32 btm_magic
Definition: nbtree.h:98
#define BTP_LEAF
Definition: nbtree.h:70
union BTPageOpaqueData::@46 btpo
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
BlockNumber root
Definition: nbtxlog.h:47
#define P_NONE
Definition: nbtree.h:168
#define InvalidBuffer
Definition: buf.h:25
#define REGBUF_WILL_INIT
Definition: xloginsert.h:32
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
int errcode(int sqlerrcode)
Definition: elog.c:575
uint32 level
Definition: nbtxlog.h:240
uint32 BlockNumber
Definition: block.h:31
#define P_NEW
Definition: bufmgr.h:82
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
#define P_ISMETA(opaque)
Definition: nbtree.h:179
#define BT_READ
Definition: nbtree.h:238
BlockNumber btm_fastroot
Definition: nbtree.h:102
#define XLOG_BTREE_NEWROOT
Definition: nbtxlog.h:35
void pfree(void *pointer)
Definition: mcxt.c:949
#define BTREE_MAGIC
Definition: nbtree.h:110
#define ERROR
Definition: elog.h:43
BTCycleId btpo_cycleid
Definition: nbtree.h:64
#define BTPageGetMeta(p)
Definition: nbtree.h:106
BlockNumber btpo_prev
Definition: nbtree.h:56
#define REGBUF_STANDARD
Definition: xloginsert.h:34
#define RelationGetRelationName(relation)
Definition: rel.h:436
#define P_LEFTMOST(opaque)
Definition: nbtree.h:174
unsigned int uint32
Definition: c.h:296
Buffer _bt_getroot(Relation rel, int access)
Definition: nbtpage.c:105
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
#define BTREE_METAPAGE
Definition: nbtree.h:109
#define SizeOfBtreeNewroot
Definition: nbtxlog.h:243
uint32 btm_fastlevel
Definition: nbtree.h:103
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
uint32 level
Definition: nbtree.h:60
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
BlockNumber btm_root
Definition: nbtree.h:100
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:721
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:670
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
#define RelationNeedsWAL(relation)
Definition: rel.h:505
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
int errmsg(const char *fmt,...)
Definition: elog.c:797
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:706
uint32 fastlevel
Definition: nbtxlog.h:50
uint32 btm_level
Definition: nbtree.h:101
MemoryContext rd_indexcxt
Definition: rel.h:179
uint32 level
Definition: nbtxlog.h:48
BlockNumber fastroot
Definition: nbtxlog.h:49
#define elog
Definition: elog.h:219
void * rd_amcache
Definition: rel.h:192
#define BT_WRITE
Definition: nbtree.h:239
void XLogBeginInsert(void)
Definition: xloginsert.c:120
uint16 btpo_flags
Definition: nbtree.h:63
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:175
Pointer Page
Definition: bufpage.h:74

◆ _bt_getrootheight()

int _bt_getrootheight ( Relation  rel)

Definition at line 436 of file nbtpage.c.

References _bt_getbuf(), _bt_relbuf(), Assert, BT_READ, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_magic, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTPageGetMeta, BTREE_MAGIC, BTREE_METAPAGE, BTREE_VERSION, BufferGetPage, ereport, errcode(), errmsg(), ERROR, MemoryContextAlloc(), P_ISMETA, P_NONE, PageGetSpecialPointer, RelationData::rd_amcache, RelationData::rd_indexcxt, and RelationGetRelationName.

Referenced by get_relation_info().

437 {
438  BTMetaPageData *metad;
439 
440  /*
441  * We can get what we need from the cached metapage data. If it's not
442  * cached yet, load it. Sanity checks here must match _bt_getroot().
443  */
444  if (rel->rd_amcache == NULL)
445  {
446  Buffer metabuf;
447  Page metapg;
448  BTPageOpaque metaopaque;
449 
450  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
451  metapg = BufferGetPage(metabuf);
452  metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
453  metad = BTPageGetMeta(metapg);
454 
455  /* sanity-check the metapage */
456  if (!P_ISMETA(metaopaque) ||
457  metad->btm_magic != BTREE_MAGIC)
458  ereport(ERROR,
459  (errcode(ERRCODE_INDEX_CORRUPTED),
460  errmsg("index \"%s\" is not a btree",
461  RelationGetRelationName(rel))));
462 
463  if (metad->btm_version != BTREE_VERSION)
464  ereport(ERROR,
465  (errcode(ERRCODE_INDEX_CORRUPTED),
466  errmsg("version mismatch in index \"%s\": file version %d, code version %d",
468  metad->btm_version, BTREE_VERSION)));
469 
470  /*
471  * If there's no root page yet, _bt_getroot() doesn't expect a cache
472  * to be made, so just stop here and report the index height is zero.
473  * (XXX perhaps _bt_getroot() should be changed to allow this case.)
474  */
475  if (metad->btm_root == P_NONE)
476  {
477  _bt_relbuf(rel, metabuf);
478  return 0;
479  }
480 
481  /*
482  * Cache the metapage data for next time
483  */
485  sizeof(BTMetaPageData));
486  memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
487 
488  _bt_relbuf(rel, metabuf);
489  }
490 
491  metad = (BTMetaPageData *) rel->rd_amcache;
492  /* We shouldn't have cached it if any of these fail */
493  Assert(metad->btm_magic == BTREE_MAGIC);
494  Assert(metad->btm_version == BTREE_VERSION);
495  Assert(metad->btm_fastroot != P_NONE);
496 
497  return metad->btm_fastlevel;
498 }
uint32 btm_version
Definition: nbtree.h:99
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:571
#define BTREE_VERSION
Definition: nbtree.h:111
uint32 btm_magic
Definition: nbtree.h:98
#define P_NONE
Definition: nbtree.h:168
int errcode(int sqlerrcode)
Definition: elog.c:575
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
#define P_ISMETA(opaque)
Definition: nbtree.h:179
#define BT_READ
Definition: nbtree.h:238
BlockNumber btm_fastroot
Definition: nbtree.h:102
#define BTREE_MAGIC
Definition: nbtree.h:110
#define ERROR
Definition: elog.h:43
#define BTPageGetMeta(p)
Definition: nbtree.h:106
#define RelationGetRelationName(relation)
Definition: rel.h:436
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
#define BTREE_METAPAGE
Definition: nbtree.h:109
uint32 btm_fastlevel
Definition: nbtree.h:103
BlockNumber btm_root
Definition: nbtree.h:100
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:721
#define Assert(condition)
Definition: c.h:670
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
int errmsg(const char *fmt,...)
Definition: elog.c:797
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:706
MemoryContext rd_indexcxt
Definition: rel.h:179
void * rd_amcache
Definition: rel.h:192
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:74

◆ _bt_gettrueroot()

Buffer _bt_gettrueroot ( Relation  rel)

Definition at line 342 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_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().

343 {
344  Buffer metabuf;
345  Page metapg;
346  BTPageOpaque metaopaque;
347  Buffer rootbuf;
348  Page rootpage;
349  BTPageOpaque rootopaque;
350  BlockNumber rootblkno;
351  uint32 rootlevel;
352  BTMetaPageData *metad;
353 
354  /*
355  * We don't try to use cached metapage data here, since (a) this path is
356  * not performance-critical, and (b) if we are here it suggests our cache
357  * is out-of-date anyway. In light of point (b), it's probably safest to
358  * actively flush any cached metapage info.
359  */
360  if (rel->rd_amcache)
361  pfree(rel->rd_amcache);
362  rel->rd_amcache = NULL;
363 
364  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
365  metapg = BufferGetPage(metabuf);
366  metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
367  metad = BTPageGetMeta(metapg);
368 
369  if (!P_ISMETA(metaopaque) ||
370  metad->btm_magic != BTREE_MAGIC)
371  ereport(ERROR,
372  (errcode(ERRCODE_INDEX_CORRUPTED),
373  errmsg("index \"%s\" is not a btree",
374  RelationGetRelationName(rel))));
375 
376  if (metad->btm_version != BTREE_VERSION)
377  ereport(ERROR,
378  (errcode(ERRCODE_INDEX_CORRUPTED),
379  errmsg("version mismatch in index \"%s\": file version %d, code version %d",
381  metad->btm_version, BTREE_VERSION)));
382 
383  /* if no root page initialized yet, fail */
384  if (metad->btm_root == P_NONE)
385  {
386  _bt_relbuf(rel, metabuf);
387  return InvalidBuffer;
388  }
389 
390  rootblkno = metad->btm_root;
391  rootlevel = metad->btm_level;
392 
393  /*
394  * We are done with the metapage; arrange to release it via first
395  * _bt_relandgetbuf call
396  */
397  rootbuf = metabuf;
398 
399  for (;;)
400  {
401  rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, BT_READ);
402  rootpage = BufferGetPage(rootbuf);
403  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
404 
405  if (!P_IGNORE(rootopaque))
406  break;
407 
408  /* it's dead, Jim. step right one page */
409  if (P_RIGHTMOST(rootopaque))
410  elog(ERROR, "no live root page found in index \"%s\"",
412  rootblkno = rootopaque->btpo_next;
413  }
414 
415  /* Note: can't check btpo.level on deleted pages */
416  if (rootopaque->btpo.level != rootlevel)
417  elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
418  rootblkno, RelationGetRelationName(rel),
419  rootopaque->btpo.level, rootlevel);
420 
421  return rootbuf;
422 }
BlockNumber btpo_next
Definition: nbtree.h:57
#define P_IGNORE(opaque)
Definition: nbtree.h:181
uint32 btm_version
Definition: nbtree.h:99
Buffer _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access)
Definition: nbtpage.c:702
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:571
#define BTREE_VERSION
Definition: nbtree.h:111
uint32 btm_magic
Definition: nbtree.h:98
union BTPageOpaqueData::@46 btpo
#define P_NONE
Definition: nbtree.h:168
#define InvalidBuffer
Definition: buf.h:25
int errcode(int sqlerrcode)
Definition: elog.c:575
uint32 BlockNumber
Definition: block.h:31
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
#define P_ISMETA(opaque)
Definition: nbtree.h:179
#define BT_READ
Definition: nbtree.h:238
void pfree(void *pointer)
Definition: mcxt.c:949
#define BTREE_MAGIC
Definition: nbtree.h:110
#define ERROR
Definition: elog.h:43
#define BTPageGetMeta(p)
Definition: nbtree.h:106
#define RelationGetRelationName(relation)
Definition: rel.h:436
unsigned int uint32
Definition: c.h:296
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
#define BTREE_METAPAGE
Definition: nbtree.h:109
uint32 level
Definition: nbtree.h:60
BlockNumber btm_root
Definition: nbtree.h:100
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:721
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
int errmsg(const char *fmt,...)
Definition: elog.c:797
uint32 btm_level
Definition: nbtree.h:101
#define elog
Definition: elog.h:219
void * rd_amcache
Definition: rel.h:192
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:175
Pointer Page
Definition: bufpage.h:74

◆ _bt_initmetapage()

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

Definition at line 49 of file nbtpage.c.

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

Referenced by _bt_uppershutdown(), and btbuildempty().

50 {
51  BTMetaPageData *metad;
52  BTPageOpaque metaopaque;
53 
54  _bt_pageinit(page, BLCKSZ);
55 
56  metad = BTPageGetMeta(page);
57  metad->btm_magic = BTREE_MAGIC;
58  metad->btm_version = BTREE_VERSION;
59  metad->btm_root = rootbknum;
60  metad->btm_level = level;
61  metad->btm_fastroot = rootbknum;
62  metad->btm_fastlevel = level;
63 
64  metaopaque = (BTPageOpaque) PageGetSpecialPointer(page);
65  metaopaque->btpo_flags = BTP_META;
66 
67  /*
68  * Set pd_lower just past the end of the metadata. This is essential,
69  * because without doing so, metadata will be lost if xlog.c compresses
70  * the page.
71  */
72  ((PageHeader) page)->pd_lower =
73  ((char *) metad + sizeof(BTMetaPageData)) - (char *) page;
74 }
uint32 btm_version
Definition: nbtree.h:99
#define BTREE_VERSION
Definition: nbtree.h:111
uint32 btm_magic
Definition: nbtree.h:98
#define BTP_META
Definition: nbtree.h:73
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
BlockNumber btm_fastroot
Definition: nbtree.h:102
#define BTREE_MAGIC
Definition: nbtree.h:110
#define BTPageGetMeta(p)
Definition: nbtree.h:106
uint32 btm_fastlevel
Definition: nbtree.h:103
BlockNumber btm_root
Definition: nbtree.h:100
PageHeaderData * PageHeader
Definition: bufpage.h:162
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
uint32 btm_level
Definition: nbtree.h:101
void _bt_pageinit(Page page, Size size)
Definition: nbtpage.c:733
uint16 btpo_flags
Definition: nbtree.h:63

◆ _bt_is_page_halfdead()

static bool _bt_is_page_halfdead ( Relation  rel,
BlockNumber  blk 
)
static

Definition at line 928 of file nbtpage.c.

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

Referenced by _bt_lock_branch_parent(), and _bt_mark_page_halfdead().

929 {
930  Buffer buf;
931  Page page;
932  BTPageOpaque opaque;
933  bool result;
934 
935  buf = _bt_getbuf(rel, blk, BT_READ);
936  page = BufferGetPage(buf);
937  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
938 
939  result = P_ISHALFDEAD(opaque);
940  _bt_relbuf(rel, buf);
941 
942  return result;
943 }
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:571
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
#define BT_READ
Definition: nbtree.h:238
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:180
static char * buf
Definition: pg_test_fsync.c:67
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:721
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:74

◆ _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 972 of file nbtpage.c.

References _bt_getbuf(), _bt_getstackbuf(), _bt_is_page_halfdead(), _bt_relbuf(), BT_READ, BT_WRITE, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BTStackData::bts_blkno, BTStackData::bts_btentry, BTStackData::bts_offset, BTStackData::bts_parent, BufferGetPage, DEBUG1, elog, ERROR, InvalidBuffer, ItemPointerSet, P_FIRSTDATAKEY, P_HIKEY, P_INCOMPLETE_SPLIT, P_ISROOT, P_NONE, P_RIGHTMOST, PageGetMaxOffsetNumber, PageGetSpecialPointer, RelationGetRelationName, and IndexTupleData::t_tid.

Referenced by _bt_mark_page_halfdead().

975 {
976  BlockNumber parent;
977  OffsetNumber poffset,
978  maxoff;
979  Buffer pbuf;
980  Page page;
981  BTPageOpaque opaque;
982  BlockNumber leftsib;
983 
984  /*
985  * Locate the downlink of "child" in the parent (updating the stack entry
986  * if needed)
987  */
988  ItemPointerSet(&(stack->bts_btentry.t_tid), child, P_HIKEY);
989  pbuf = _bt_getstackbuf(rel, stack, BT_WRITE);
990  if (pbuf == InvalidBuffer)
991  elog(ERROR, "failed to re-find parent key in index \"%s\" for deletion target page %u",
992  RelationGetRelationName(rel), child);
993  parent = stack->bts_blkno;
994  poffset = stack->bts_offset;
995 
996  page = BufferGetPage(pbuf);
997  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
998  maxoff = PageGetMaxOffsetNumber(page);
999 
1000  /*
1001  * If the target is the rightmost child of its parent, then we can't
1002  * delete, unless it's also the only child.
1003  */
1004  if (poffset >= maxoff)
1005  {
1006  /* It's rightmost child... */
1007  if (poffset == P_FIRSTDATAKEY(opaque))
1008  {
1009  /*
1010  * It's only child, so safe if parent would itself be removable.
1011  * We have to check the parent itself, and then recurse to test
1012  * the conditions at the parent's parent.
1013  */
1014  if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) ||
1015  P_INCOMPLETE_SPLIT(opaque))
1016  {
1017  _bt_relbuf(rel, pbuf);
1018  return false;
1019  }
1020 
1021  *target = parent;
1022  *rightsib = opaque->btpo_next;
1023  leftsib = opaque->btpo_prev;
1024 
1025  _bt_relbuf(rel, pbuf);
1026 
1027  /*
1028  * Like in _bt_pagedel, check that the left sibling is not marked
1029  * with INCOMPLETE_SPLIT flag. That would mean that there is no
1030  * downlink to the page to be deleted, and the page deletion
1031  * algorithm isn't prepared to handle that.
1032  */
1033  if (leftsib != P_NONE)
1034  {
1035  Buffer lbuf;
1036  Page lpage;
1037  BTPageOpaque lopaque;
1038 
1039  lbuf = _bt_getbuf(rel, leftsib, BT_READ);
1040  lpage = BufferGetPage(lbuf);
1041  lopaque = (BTPageOpaque) PageGetSpecialPointer(lpage);
1042 
1043  /*
1044  * If the left sibling was concurrently split, so that its
1045  * next-pointer doesn't point to the current page anymore, the
1046  * split that created the current page must be completed. (We
1047  * don't allow splitting an incompletely split page again
1048  * until the previous split has been completed)
1049  */
1050  if (lopaque->btpo_next == parent &&
1051  P_INCOMPLETE_SPLIT(lopaque))
1052  {
1053  _bt_relbuf(rel, lbuf);
1054  return false;
1055  }
1056  _bt_relbuf(rel, lbuf);
1057  }
1058 
1059  /*
1060  * Perform the same check on this internal level that
1061  * _bt_mark_page_halfdead performed on the leaf level.
1062  */
1063  if (_bt_is_page_halfdead(rel, *rightsib))
1064  {
1065  elog(DEBUG1, "could not delete page %u because its right sibling %u is half-dead",
1066  parent, *rightsib);
1067  return false;
1068  }
1069 
1070  return _bt_lock_branch_parent(rel, parent, stack->bts_parent,
1071  topparent, topoff, target, rightsib);
1072  }
1073  else
1074  {
1075  /* Unsafe to delete */
1076  _bt_relbuf(rel, pbuf);
1077  return false;
1078  }
1079  }
1080  else
1081  {
1082  /* Not rightmost child, so safe to delete */
1083  *topparent = pbuf;
1084  *topoff = poffset;
1085  return true;
1086  }
1087 }
Buffer _bt_getstackbuf(Relation rel, BTStack stack, int access)
Definition: nbtinsert.c:1798
BlockNumber btpo_next
Definition: nbtree.h:57
#define DEBUG1
Definition: elog.h:25
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:571
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:205
ItemPointerData t_tid
Definition: itup.h:37
#define P_NONE
Definition: nbtree.h:168
#define InvalidBuffer
Definition: buf.h:25
uint32 BlockNumber
Definition: block.h:31
#define P_INCOMPLETE_SPLIT(opaque)
Definition: nbtree.h:183
IndexTupleData bts_btentry
Definition: nbtree.h:255
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
uint16 OffsetNumber
Definition: off.h:24
#define BT_READ
Definition: nbtree.h:238
#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:972
BlockNumber btpo_prev
Definition: nbtree.h:56
OffsetNumber bts_offset
Definition: nbtree.h:254
#define RelationGetRelationName(relation)
Definition: rel.h:436
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define P_ISROOT(opaque)
Definition: nbtree.h:177
static bool _bt_is_page_halfdead(Relation rel, BlockNumber blk)
Definition: nbtpage.c:928
BlockNumber bts_blkno
Definition: nbtree.h:253
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:721
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
struct BTStackData * bts_parent
Definition: nbtree.h:256
#define P_HIKEY
Definition: nbtree.h:203
#define elog
Definition: elog.h:219
#define BT_WRITE
Definition: nbtree.h:239
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:175
Pointer Page
Definition: bufpage.h:74
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:105

◆ _bt_log_reuse_page()

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

Definition at line 538 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().

539 {
540  xl_btree_reuse_page xlrec_reuse;
541 
542  /*
543  * Note that we don't register the buffer with the record, because this
544  * operation doesn't modify the page. This record only exists to provide a
545  * conflict point for Hot Standby.
546  */
547 
548  /* XLOG stuff */
549  xlrec_reuse.node = rel->rd_node;
550  xlrec_reuse.block = blkno;
551  xlrec_reuse.latestRemovedXid = latestRemovedXid;
552 
553  XLogBeginInsert();
554  XLogRegisterData((char *) &xlrec_reuse, SizeOfBtreeReusePage);
555 
556  XLogInsert(RM_BTREE_ID, XLOG_BTREE_REUSE_PAGE);
557 }
RelFileNode node
Definition: nbtxlog.h:134
#define SizeOfBtreeReusePage
Definition: nbtxlog.h:139
BlockNumber block
Definition: nbtxlog.h:135
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:85
TransactionId latestRemovedXid
Definition: nbtxlog.h:136
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 1322 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, BufferGetBlockNumber(), BufferGetPage, DEBUG1, elog, END_CRIT_SECTION, ERROR, InvalidBlockNumber, InvalidOffsetNumber, ItemPointerGetBlockNumber, ItemPointerSet, ItemPointerSetInvalid, 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, PageAddItem, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageGetSpecialPointer, PageIndexTupleDelete(), 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, IndexTupleData::t_tid, xl_btree_mark_page_halfdead::topparent, XLOG_BTREE_MARK_PAGE_HALFDEAD, XLogBeginInsert(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_pagedel().

1323 {
1324  BlockNumber leafblkno;
1325  BlockNumber leafrightsib;
1326  BlockNumber target;
1327  BlockNumber rightsib;
1328  ItemId itemid;
1329  Page page;
1330  BTPageOpaque opaque;
1331  Buffer topparent;
1332  OffsetNumber topoff;
1333  OffsetNumber nextoffset;
1334  IndexTuple itup;
1335  IndexTupleData trunctuple;
1336 
1337  page = BufferGetPage(leafbuf);
1338  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1339 
1340  Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) && !P_ISDELETED(opaque) &&
1341  !P_ISHALFDEAD(opaque) && P_ISLEAF(opaque) &&
1342  P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
1343 
1344  /*
1345  * Save info about the leaf page.
1346  */
1347  leafblkno = BufferGetBlockNumber(leafbuf);
1348  leafrightsib = opaque->btpo_next;
1349 
1350  /*
1351  * Before attempting to lock the parent page, check that the right sibling
1352  * is not in half-dead state. A half-dead right sibling would have no
1353  * downlink in the parent, which would be highly confusing later when we
1354  * delete the downlink that follows the current page's downlink. (I
1355  * believe the deletion would work correctly, but it would fail the
1356  * cross-check we make that the following downlink points to the right
1357  * sibling of the delete page.)
1358  */
1359  if (_bt_is_page_halfdead(rel, leafrightsib))
1360  {
1361  elog(DEBUG1, "could not delete page %u because its right sibling %u is half-dead",
1362  leafblkno, leafrightsib);
1363  return false;
1364  }
1365 
1366  /*
1367  * We cannot delete a page that is the rightmost child of its immediate
1368  * parent, unless it is the only child --- in which case the parent has to
1369  * be deleted too, and the same condition applies recursively to it. We
1370  * have to check this condition all the way up before trying to delete,
1371  * and lock the final parent of the to-be-deleted branch.
1372  */
1373  rightsib = leafrightsib;
1374  target = leafblkno;
1375  if (!_bt_lock_branch_parent(rel, leafblkno, stack,
1376  &topparent, &topoff, &target, &rightsib))
1377  return false;
1378 
1379  /*
1380  * Check that the parent-page index items we're about to delete/overwrite
1381  * contain what we expect. This can fail if the index has become corrupt
1382  * for some reason. We want to throw any error before entering the
1383  * critical section --- otherwise it'd be a PANIC.
1384  *
1385  * The test on the target item is just an Assert because
1386  * _bt_lock_branch_parent should have guaranteed it has the expected
1387  * contents. The test on the next-child downlink is known to sometimes
1388  * fail in the field, though.
1389  */
1390  page = BufferGetPage(topparent);
1391  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1392 
1393 #ifdef USE_ASSERT_CHECKING
1394  itemid = PageGetItemId(page, topoff);
1395  itup = (IndexTuple) PageGetItem(page, itemid);
1396  Assert(ItemPointerGetBlockNumber(&(itup->t_tid)) == target);
1397 #endif
1398 
1399  nextoffset = OffsetNumberNext(topoff);
1400  itemid = PageGetItemId(page, nextoffset);
1401  itup = (IndexTuple) PageGetItem(page, itemid);
1402  if (ItemPointerGetBlockNumber(&(itup->t_tid)) != rightsib)
1403  elog(ERROR, "right sibling %u of block %u is not next child %u of block %u in index \"%s\"",
1404  rightsib, target, ItemPointerGetBlockNumber(&(itup->t_tid)),
1406 
1407  /*
1408  * Any insert which would have gone on the leaf block will now go to its
1409  * right sibling.
1410  */
1411  PredicateLockPageCombine(rel, leafblkno, leafrightsib);
1412 
1413  /* No ereport(ERROR) until changes are logged */
1415 
1416  /*
1417  * Update parent. The normal case is a tad tricky because we want to
1418  * delete the target's downlink and the *following* key. Easiest way is
1419  * to copy the right sibling's downlink over the target downlink, and then
1420  * delete the following item.
1421  */
1422  page = BufferGetPage(topparent);
1423  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1424 
1425  itemid = PageGetItemId(page, topoff);
1426  itup = (IndexTuple) PageGetItem(page, itemid);
1427  ItemPointerSet(&(itup->t_tid), rightsib, P_HIKEY);
1428 
1429  nextoffset = OffsetNumberNext(topoff);
1430  PageIndexTupleDelete(page, nextoffset);
1431 
1432  /*
1433  * Mark the leaf page as half-dead, and stamp it with a pointer to the
1434  * highest internal page in the branch we're deleting. We use the tid of
1435  * the high key to store it.
1436  */
1437  page = BufferGetPage(leafbuf);
1438  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1439  opaque->btpo_flags |= BTP_HALF_DEAD;
1440 
1442  Assert(PageGetMaxOffsetNumber(page) == 0);
1443  MemSet(&trunctuple, 0, sizeof(IndexTupleData));
1444  trunctuple.t_info = sizeof(IndexTupleData);
1445  if (target != leafblkno)
1446  ItemPointerSet(&trunctuple.t_tid, target, P_HIKEY);
1447  else
1448  ItemPointerSetInvalid(&trunctuple.t_tid);
1449  if (PageAddItem(page, (Item) &trunctuple, sizeof(IndexTupleData), P_HIKEY,
1450  false, false) == InvalidOffsetNumber)
1451  elog(ERROR, "could not add dummy high key to half-dead page");
1452 
1453  /* Must mark buffers dirty before XLogInsert */
1454  MarkBufferDirty(topparent);
1455  MarkBufferDirty(leafbuf);
1456 
1457  /* XLOG stuff */
1458  if (RelationNeedsWAL(rel))
1459  {
1461  XLogRecPtr recptr;
1462 
1463  xlrec.poffset = topoff;
1464  xlrec.leafblk = leafblkno;
1465  if (target != leafblkno)
1466  xlrec.topparent = target;
1467  else
1468  xlrec.topparent = InvalidBlockNumber;
1469 
1470  XLogBeginInsert();
1471  XLogRegisterBuffer(0, leafbuf, REGBUF_WILL_INIT);
1472  XLogRegisterBuffer(1, topparent, REGBUF_STANDARD);
1473 
1474  page = BufferGetPage(leafbuf);
1475  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1476  xlrec.leftblk = opaque->btpo_prev;
1477  xlrec.rightblk = opaque->btpo_next;
1478 
1480 
1481  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_MARK_PAGE_HALFDEAD);
1482 
1483  page = BufferGetPage(topparent);
1484  PageSetLSN(page, recptr);
1485  page = BufferGetPage(leafbuf);
1486  PageSetLSN(page, recptr);
1487  }
1488 
1489  END_CRIT_SECTION();
1490 
1491  _bt_relbuf(rel, topparent);
1492  return true;
1493 }
BlockNumber btpo_next
Definition: nbtree.h:57
#define DEBUG1
Definition: elog.h:25
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:723
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:205
#define SizeOfBtreeMarkPageHalfDead
Definition: nbtxlog.h:194
ItemPointerData t_tid
Definition: itup.h:37
#define BTP_HALF_DEAD
Definition: nbtree.h:74
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
Pointer Item
Definition: item.h:17
#define REGBUF_WILL_INIT
Definition: xloginsert.h:32
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
#define MemSet(start, val, len)
Definition: c.h:853
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:412
uint32 BlockNumber
Definition: block.h:31
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
uint16 OffsetNumber
Definition: off.h:24
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:180
#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:972
BlockNumber btpo_prev
Definition: nbtree.h:56
IndexTupleData * IndexTuple
Definition: itup.h:53
#define REGBUF_STANDARD
Definition: xloginsert.h:34
#define RelationGetRelationName(relation)
Definition: rel.h:436
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define P_ISDELETED(opaque)
Definition: nbtree.h:178
#define P_ISROOT(opaque)
Definition: nbtree.h:177
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:231
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
static bool _bt_is_page_halfdead(Relation rel, BlockNumber blk)
Definition: nbtpage.c:928
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
#define XLOG_BTREE_MARK_PAGE_HALFDEAD
Definition: nbtxlog.h:36
struct IndexTupleData IndexTupleData
#define InvalidOffsetNumber
Definition: off.h:26
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:721
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:670
#define OffsetNumberNext(offsetNumber)
Definition: off.h:53
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
#define InvalidBlockNumber
Definition: block.h:33
#define RelationNeedsWAL(relation)
Definition: rel.h:505
#define P_HIKEY
Definition: nbtree.h:203
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:150
void PredicateLockPageCombine(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3196
#define elog
Definition: elog.h:219
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:76
unsigned short t_info
Definition: itup.h:49
void XLogBeginInsert(void)
Definition: xloginsert.c:120
uint16 btpo_flags
Definition: nbtree.h:63
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:175
#define PageGetItem(page, itemId)
Definition: bufpage.h:336
Pointer Page
Definition: bufpage.h:74
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:105
#define P_ISLEAF(opaque)
Definition: nbtree.h:176

◆ _bt_page_recyclable()

bool _bt_page_recyclable ( Page  page)

Definition at line 745 of file nbtpage.c.

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

Referenced by _bt_getbuf(), and btvacuumpage().

746 {
747  BTPageOpaque opaque;
748 
749  /*
750  * It's possible to find an all-zeroes page in an index --- for example, a
751  * backend might successfully extend the relation one page and then crash
752  * before it is able to make a WAL entry for adding the page. If we find a
753  * zeroed page then reclaim it.
754  */
755  if (PageIsNew(page))
756  return true;
757 
758  /*
759  * Otherwise, recycle if deleted and too old to have any processes
760  * interested in it.
761  */
762  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
763  if (P_ISDELETED(opaque) &&
765  return true;
766  return false;
767 }
union BTPageOpaqueData::@46 btpo
TransactionId xact
Definition: nbtree.h:61
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
TransactionId RecentGlobalXmin
Definition: snapmgr.c:166
#define P_ISDELETED(opaque)
Definition: nbtree.h:178
bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition: transam.c:300
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
#define PageIsNew(page)
Definition: bufpage.h:225

◆ _bt_pagedel()

int _bt_pagedel ( Relation  rel,
Buffer  buf 
)

Definition at line 1110 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, 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, RelationData::rd_rel, RelationGetRelationName, and ReleaseBuffer().

Referenced by btvacuumpage().

1111 {
1112  int ndeleted = 0;
1113  BlockNumber rightsib;
1114  bool rightsib_empty;
1115  Page page;
1116  BTPageOpaque opaque;
1117 
1118  /*
1119  * "stack" is a search stack leading (approximately) to the target page.
1120  * It is initially NULL, but when iterating, we keep it to avoid
1121  * duplicated search effort.
1122  *
1123  * Also, when "stack" is not NULL, we have already checked that the
1124  * current page is not the right half of an incomplete split, i.e. the
1125  * left sibling does not have its INCOMPLETE_SPLIT flag set.
1126  */
1127  BTStack stack = NULL;
1128 
1129  for (;;)
1130  {
1131  page = BufferGetPage(buf);
1132  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1133 
1134  /*
1135  * Internal pages are never deleted directly, only as part of deleting
1136  * the whole branch all the way down to leaf level.
1137  */
1138  if (!P_ISLEAF(opaque))
1139  {
1140  /*
1141  * Pre-9.4 page deletion only marked internal pages as half-dead,
1142  * but now we only use that flag on leaf pages. The old algorithm
1143  * was never supposed to leave half-dead pages in the tree, it was
1144  * just a transient state, but it was nevertheless possible in
1145  * error scenarios. We don't know how to deal with them here. They
1146  * are harmless as far as searches are considered, but inserts
1147  * into the deleted keyspace could add out-of-order downlinks in
1148  * the upper levels. Log a notice, hopefully the admin will notice
1149  * and reindex.
1150  */
1151  if (P_ISHALFDEAD(opaque))
1152  ereport(LOG,
1153  (errcode(ERRCODE_INDEX_CORRUPTED),
1154  errmsg("index \"%s\" contains a half-dead internal page",
1156  errhint("This can be caused by an interrupted VACUUM in version 9.3 or older, before upgrade. Please REINDEX it.")));
1157  _bt_relbuf(rel, buf);
1158  return ndeleted;
1159  }
1160 
1161  /*
1162  * We can never delete rightmost pages nor root pages. While at it,
1163  * check that page is not already deleted and is empty.
1164  *
1165  * To keep the algorithm simple, we also never delete an incompletely
1166  * split page (they should be rare enough that this doesn't make any
1167  * meaningful difference to disk usage):
1168  *
1169  * The INCOMPLETE_SPLIT flag on the page tells us if the page is the
1170  * left half of an incomplete split, but ensuring that it's not the
1171  * right half is more complicated. For that, we have to check that
1172  * the left sibling doesn't have its INCOMPLETE_SPLIT flag set. On
1173  * the first iteration, we temporarily release the lock on the current
1174  * page, and check the left sibling and also construct a search stack
1175  * to. On subsequent iterations, we know we stepped right from a page
1176  * that passed these tests, so it's OK.
1177  */
1178  if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) || P_ISDELETED(opaque) ||
1179  P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) ||
1180  P_INCOMPLETE_SPLIT(opaque))
1181  {
1182  /* Should never fail to delete a half-dead page */
1183  Assert(!P_ISHALFDEAD(opaque));
1184 
1185  _bt_relbuf(rel, buf);
1186  return ndeleted;
1187  }
1188 
1189  /*
1190  * First, remove downlink pointing to the page (or a parent of the
1191  * page, if we are going to delete a taller branch), and mark the page
1192  * as half-dead.
1193  */
1194  if (!P_ISHALFDEAD(opaque))
1195  {
1196  /*
1197  * We need an approximate pointer to the page's parent page. We
1198  * use the standard search mechanism to search for the page's high
1199  * key; this will give us a link to either the current parent or
1200  * someplace to its left (if there are multiple equal high keys).
1201  *
1202  * Also check if this is the right-half of an incomplete split
1203  * (see comment above).
1204  */
1205  if (!stack)
1206  {
1207  ScanKey itup_scankey;
1208  ItemId itemid;
1209  IndexTuple targetkey;
1210  Buffer lbuf;
1211  BlockNumber leftsib;
1212 
1213  itemid = PageGetItemId(page, P_HIKEY);
1214  targetkey = CopyIndexTuple((IndexTuple) PageGetItem(page, itemid));
1215 
1216  leftsib = opaque->btpo_prev;
1217 
1218  /*
1219  * To avoid deadlocks, we'd better drop the leaf page lock
1220  * before going further.
1221  */
1223 
1224  /*
1225  * Fetch the left sibling, to check that it's not marked with
1226  * INCOMPLETE_SPLIT flag. That would mean that the page
1227  * to-be-deleted doesn't have a downlink, and the page
1228  * deletion algorithm isn't prepared to handle that.
1229  */
1230  if (!P_LEFTMOST(opaque))
1231  {
1232  BTPageOpaque lopaque;
1233  Page lpage;
1234 
1235  lbuf = _bt_getbuf(rel, leftsib, BT_READ);
1236  lpage = BufferGetPage(lbuf);
1237  lopaque = (BTPageOpaque) PageGetSpecialPointer(lpage);
1238 
1239  /*
1240  * If the left sibling is split again by another backend,
1241  * after we released the lock, we know that the first
1242  * split must have finished, because we don't allow an
1243  * incompletely-split page to be split again. So we don't
1244  * need to walk right here.
1245  */
1246  if (lopaque->btpo_next == BufferGetBlockNumber(buf) &&
1247  P_INCOMPLETE_SPLIT(lopaque))
1248  {
1249  ReleaseBuffer(buf);
1250  _bt_relbuf(rel, lbuf);
1251  return ndeleted;
1252  }
1253  _bt_relbuf(rel, lbuf);
1254  }
1255 
1256  /* we need an insertion scan key for the search, so build one */
1257  itup_scankey = _bt_mkscankey(rel, targetkey);
1258  /* find the leftmost leaf page containing this key */
1259  stack = _bt_search(rel, rel->rd_rel->relnatts, itup_scankey,
1260  false, &lbuf, BT_READ, NULL);
1261  /* don't need a pin on the page */
1262  _bt_relbuf(rel, lbuf);
1263 
1264  /*
1265  * Re-lock the leaf page, and start over, to re-check that the
1266  * page can still be deleted.
1267  */
1269  continue;
1270  }
1271 
1272  if (!_bt_mark_page_halfdead(rel, buf, stack))
1273  {
1274  _bt_relbuf(rel, buf);
1275  return ndeleted;
1276  }
1277  }
1278 
1279  /*
1280  * Then unlink it from its siblings. Each call to
1281  * _bt_unlink_halfdead_page unlinks the topmost page from the branch,
1282  * making it shallower. Iterate until the leaf page is gone.
1283  */
1284  rightsib_empty = false;
1285  while (P_ISHALFDEAD(opaque))
1286  {
1287  if (!_bt_unlink_halfdead_page(rel, buf, &rightsib_empty))
1288  {
1289  /* _bt_unlink_halfdead_page already released buffer */
1290  return ndeleted;
1291  }
1292  ndeleted++;
1293  }
1294 
1295  rightsib = opaque->btpo_next;
1296 
1297  _bt_relbuf(rel, buf);
1298 
1299  /*
1300  * The page has now been deleted. If its right sibling is completely
1301  * empty, it's possible that the reason we haven't deleted it earlier
1302  * is that it was the rightmost child of the parent. Now that we
1303  * removed the downlink for this page, the right sibling might now be
1304  * the only child of the parent, and could be removed. It would be
1305  * picked up by the next vacuum anyway, but might as well try to
1306  * remove it now, so loop back to process the right sibling.
1307  */
1308  if (!rightsib_empty)
1309  break;
1310 
1311  buf = _bt_getbuf(rel, rightsib, BT_WRITE);
1312  }
1313 
1314  return ndeleted;
1315 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:87
BlockNumber btpo_next
Definition: nbtree.h:57
int errhint(const char *fmt,...)
Definition: elog.c:987
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:571
static bool _bt_mark_page_halfdead(Relation rel, Buffer buf, BTStack stack)
Definition: nbtpage.c:1322
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:205
int errcode(int sqlerrcode)
Definition: elog.c:575
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
#define P_INCOMPLETE_SPLIT(opaque)
Definition: nbtree.h:183
#define LOG
Definition: elog.h:26
Form_pg_class rd_rel
Definition: rel.h:114
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
#define BT_READ
Definition: nbtree.h:238
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:180
BlockNumber btpo_prev
Definition: nbtree.h:56
IndexTuple CopyIndexTuple(IndexTuple source)
Definition: indextuple.c:438
static char * buf
Definition: pg_test_fsync.c:67
#define RelationGetRelationName(relation)
Definition: rel.h:436
#define P_LEFTMOST(opaque)
Definition: nbtree.h:174
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define ereport(elevel, rest)
Definition: elog.h:122
#define P_ISDELETED(opaque)
Definition: nbtree.h:178
#define P_ISROOT(opaque)
Definition: nbtree.h:177
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:231
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
BTStack _bt_search(Relation rel, int keysz, ScanKey scankey, bool nextkey, Buffer *bufP, int access, Snapshot snapshot)
Definition: nbtsearch.c:97
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:721
#define Assert(condition)
Definition: c.h:670
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
#define P_HIKEY
Definition: nbtree.h:203
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
int errmsg(const char *fmt,...)
Definition: elog.c:797
static bool _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty)
Definition: nbtpage.c:1513
ScanKey _bt_mkscankey(Relation rel, IndexTuple itup)
Definition: nbtutils.c:62
#define BT_WRITE
Definition: nbtree.h:239
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:175
#define PageGetItem(page, itemId)
Definition: bufpage.h:336
Pointer Page
Definition: bufpage.h:74
#define P_ISLEAF(opaque)
Definition: nbtree.h:176

◆ _bt_pageinit()

void _bt_pageinit ( Page  page,
Size  size 
)

Definition at line 733 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().

734 {
735  PageInit(page, size, sizeof(BTPageOpaqueData));
736 }
void PageInit(Page page, Size pageSize, Size specialSize)
Definition: bufpage.c:41

◆ _bt_relandgetbuf()

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

Definition at line 702 of file nbtpage.c.

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

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

703 {
704  Buffer buf;
705 
706  Assert(blkno != P_NEW);
707  if (BufferIsValid(obuf))
709  buf = ReleaseAndReadBuffer(obuf, rel, blkno);
710  LockBuffer(buf, access);
711  _bt_checkpage(rel, buf);
712  return buf;
713 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:87
#define P_NEW
Definition: bufmgr.h:82
void _bt_checkpage(Relation rel, Buffer buf)
Definition: nbtpage.c:504
static char * buf
Definition: pg_test_fsync.c:67
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
#define Assert(condition)
Definition: c.h:670
Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation, BlockNumber blockNum)
Definition: bufmgr.c:1513
#define BufferIsValid(bufnum)
Definition: bufmgr.h:114
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 1513 of file nbtpage.c.

References _bt_getbuf(), _bt_relbuf(), Assert, BT_READ, BT_WRITE, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_level, BTMetaPageData::btm_root, 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, buf, BUFFER_LOCK_UNLOCK, BufferGetBlockNumber(), BufferGetPage, BufferIsValid, elog, END_CRIT_SECTION, ERROR, xl_btree_metadata::fastlevel, xl_btree_metadata::fastroot, InvalidBlockNumber, InvalidBuffer, ItemPointerGetBlockNumber, ItemPointerIsValid, ItemPointerSet, ItemPointerSetInvalid, xl_btree_unlink_page::leafleftsib, xl_btree_unlink_page::leafrightsib, xl_btree_unlink_page::leftsib, xl_btree_metadata::level, BTPageOpaqueData::level, LockBuffer(), LOG, MarkBufferDirty(), 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, BTPageOpaqueData::xact, XLOG_BTREE_UNLINK_PAGE, XLOG_BTREE_UNLINK_PAGE_META, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_pagedel().

1514 {
1515  BlockNumber leafblkno = BufferGetBlockNumber(leafbuf);
1516  BlockNumber leafleftsib;
1517  BlockNumber leafrightsib;
1518  BlockNumber target;
1519  BlockNumber leftsib;
1520  BlockNumber rightsib;
1521  Buffer lbuf = InvalidBuffer;
1522  Buffer buf;
1523  Buffer rbuf;
1524  Buffer metabuf = InvalidBuffer;
1525  Page metapg = NULL;
1526  BTMetaPageData *metad = NULL;
1527  ItemId itemid;
1528  Page page;
1529  BTPageOpaque opaque;
1530  bool rightsib_is_rightmost;
1531  int targetlevel;
1532  ItemPointer leafhikey;
1533  BlockNumber nextchild;
1534 
1535  page = BufferGetPage(leafbuf);
1536  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1537 
1538  Assert(P_ISLEAF(opaque) && P_ISHALFDEAD(opaque));
1539 
1540  /*
1541  * Remember some information about the leaf page.
1542  */
1543  itemid = PageGetItemId(page, P_HIKEY);
1544  leafhikey = &((IndexTuple) PageGetItem(page, itemid))->t_tid;
1545  leafleftsib = opaque->btpo_prev;
1546  leafrightsib = opaque->btpo_next;
1547 
1548  LockBuffer(leafbuf, BUFFER_LOCK_UNLOCK);
1549 
1550  /*
1551  * If the leaf page still has a parent pointing to it (or a chain of
1552  * parents), we don't unlink the leaf page yet, but the topmost remaining
1553  * parent in the branch. Set 'target' and 'buf' to reference the page
1554  * actually being unlinked.
1555  */
1556  if (ItemPointerIsValid(leafhikey))
1557  {
1558  target = ItemPointerGetBlockNumber(leafhikey);
1559  Assert(target != leafblkno);
1560 
1561  /* fetch the block number of the topmost parent's left sibling */
1562  buf = _bt_getbuf(rel, target, BT_READ);
1563  page = BufferGetPage(buf);
1564  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1565  leftsib = opaque->btpo_prev;
1566  targetlevel = opaque->btpo.level;
1567 
1568  /*
1569  * To avoid deadlocks, we'd better drop the target page lock before
1570  * going further.
1571  */
1573  }
1574  else
1575  {
1576  target = leafblkno;
1577 
1578  buf = leafbuf;
1579  leftsib = leafleftsib;
1580  targetlevel = 0;
1581  }
1582 
1583  /*
1584  * We have to lock the pages we need to modify in the standard order:
1585  * moving right, then up. Else we will deadlock against other writers.
1586  *
1587  * So, first lock the leaf page, if it's not the target. Then find and
1588  * write-lock the current left sibling of the target page. The sibling
1589  * that was current a moment ago could have split, so we may have to move
1590  * right. This search could fail if either the sibling or the target page
1591  * was deleted by someone else meanwhile; if so, give up. (Right now,
1592  * that should never happen, since page deletion is only done in VACUUM
1593  * and there shouldn't be multiple VACUUMs concurrently on the same
1594  * table.)
1595  */
1596  if (target != leafblkno)
1597  LockBuffer(leafbuf, BT_WRITE);
1598  if (leftsib != P_NONE)
1599  {
1600  lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
1601  page = BufferGetPage(lbuf);
1602  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1603  while (P_ISDELETED(opaque) || opaque->btpo_next != target)
1604  {
1605  /* step right one page */
1606  leftsib = opaque->btpo_next;
1607  _bt_relbuf(rel, lbuf);
1608  if (leftsib == P_NONE)
1609  {
1610  elog(LOG, "no left sibling (concurrent deletion?) of block %u in \"%s\"",
1611  target,
1613  if (target != leafblkno)
1614  {
1615  /* we have only a pin on target, but pin+lock on leafbuf */
1616  ReleaseBuffer(buf);
1617  _bt_relbuf(rel, leafbuf);
1618  }
1619  else
1620  {
1621  /* we have only a pin on leafbuf */
1622  ReleaseBuffer(leafbuf);
1623  }
1624  return false;
1625  }
1626  lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
1627  page = BufferGetPage(lbuf);
1628  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1629  }
1630  }
1631  else
1632  lbuf = InvalidBuffer;
1633 
1634  /*
1635  * Next write-lock the target page itself. It should be okay to take just
1636  * a write lock not a superexclusive lock, since no scans would stop on an
1637  * empty page.
1638  */
1639  LockBuffer(buf, BT_WRITE);
1640  page = BufferGetPage(buf);
1641  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1642 
1643  /*
1644  * Check page is still empty etc, else abandon deletion. This is just for
1645  * paranoia's sake; a half-dead page cannot resurrect because there can be
1646  * only one vacuum process running at a time.
1647  */
1648  if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) || P_ISDELETED(opaque))
1649  {
1650  elog(ERROR, "half-dead page changed status unexpectedly in block %u of index \"%s\"",
1651  target, RelationGetRelationName(rel));
1652  }
1653  if (opaque->btpo_prev != leftsib)
1654  elog(ERROR, "left link changed unexpectedly in block %u of index \"%s\"",
1655  target, RelationGetRelationName(rel));
1656 
1657  if (target == leafblkno)
1658  {
1659  if (P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) ||
1660  !P_ISLEAF(opaque) || !P_ISHALFDEAD(opaque))
1661  elog(ERROR, "half-dead page changed status unexpectedly in block %u of index \"%s\"",
1662  target, RelationGetRelationName(rel));
1663  nextchild = InvalidBlockNumber;
1664  }
1665  else
1666  {
1667  if (P_FIRSTDATAKEY(opaque) != PageGetMaxOffsetNumber(page) ||
1668  P_ISLEAF(opaque))
1669  elog(ERROR, "half-dead page changed status unexpectedly in block %u of index \"%s\"",
1670  target, RelationGetRelationName(rel));
1671 
1672  /* remember the next non-leaf child down in the branch. */
1673  itemid = PageGetItemId(page, P_FIRSTDATAKEY(opaque));
1674  nextchild = ItemPointerGetBlockNumber(&((IndexTuple) PageGetItem(page, itemid))->t_tid);
1675  if (nextchild == leafblkno)
1676  nextchild = InvalidBlockNumber;
1677  }
1678 
1679  /*
1680  * And next write-lock the (current) right sibling.
1681  */
1682  rightsib = opaque->btpo_next;
1683  rbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
1684  page = BufferGetPage(rbuf);
1685  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1686  if (opaque->btpo_prev != target)
1687  elog(ERROR, "right sibling's left-link doesn't match: "
1688  "block %u links to %u instead of expected %u in index \"%s\"",
1689  rightsib, opaque->btpo_prev, target,
1691  rightsib_is_rightmost = P_RIGHTMOST(opaque);
1692  *rightsib_empty = (P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
1693 
1694  /*
1695  * If we are deleting the next-to-last page on the target's level, then
1696  * the rightsib is a candidate to become the new fast root. (In theory, it
1697  * might be possible to push the fast root even further down, but the odds
1698  * of doing so are slim, and the locking considerations daunting.)
1699  *
1700  * We don't support handling this in the case where the parent is becoming
1701  * half-dead, even though it theoretically could occur.
1702  *
1703  * We can safely acquire a lock on the metapage here --- see comments for
1704  * _bt_newroot().
1705  */
1706  if (leftsib == P_NONE && rightsib_is_rightmost)
1707  {
1708  page = BufferGetPage(rbuf);
1709  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1710  if (P_RIGHTMOST(opaque))
1711  {
1712  /* rightsib will be the only one left on the level */
1713  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
1714  metapg = BufferGetPage(metabuf);
1715  metad = BTPageGetMeta(metapg);
1716 
1717  /*
1718  * The expected case here is btm_fastlevel == targetlevel+1; if
1719  * the fastlevel is <= targetlevel, something is wrong, and we
1720  * choose to overwrite it to fix it.
1721  */
1722  if (metad->btm_fastlevel > targetlevel + 1)
1723  {
1724  /* no update wanted */
1725  _bt_relbuf(rel, metabuf);
1726  metabuf = InvalidBuffer;
1727  }
1728  }
1729  }
1730 
1731  /*
1732  * Here we begin doing the deletion.
1733  */
1734 
1735  /* No ereport(ERROR) until changes are logged */
1737 
1738  /*
1739  * Update siblings' side-links. Note the target page's side-links will
1740  * continue to point to the siblings. Asserts here are just rechecking
1741  * things we already verified above.
1742  */
1743  if (BufferIsValid(lbuf))
1744  {
1745  page = BufferGetPage(lbuf);
1746  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1747  Assert(opaque->btpo_next == target);
1748  opaque->btpo_next = rightsib;
1749  }
1750  page = BufferGetPage(rbuf);
1751  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1752  Assert(opaque->btpo_prev == target);
1753  opaque->btpo_prev = leftsib;
1754 
1755  /*
1756  * If we deleted a parent of the targeted leaf page, instead of the leaf
1757  * itself, update the leaf to point to the next remaining child in the
1758  * branch.
1759  */
1760  if (target != leafblkno)
1761  {
1762  if (nextchild == InvalidBlockNumber)
1763  ItemPointerSetInvalid(leafhikey);
1764  else
1765  ItemPointerSet(leafhikey, nextchild, P_HIKEY);
1766  }
1767 
1768  /*
1769  * Mark the page itself deleted. It can be recycled when all current
1770  * transactions are gone. Storing GetTopTransactionId() would work, but
1771  * we're in VACUUM and would not otherwise have an XID. Having already
1772  * updated links to the target, ReadNewTransactionId() suffices as an
1773  * upper bound. Any scan having retained a now-stale link is advertising
1774  * in its PGXACT an xmin less than or equal to the value we read here. It
1775  * will continue to do so, holding back RecentGlobalXmin, for the duration
1776  * of that scan.
1777  */
1778  page = BufferGetPage(buf);
1779  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1780  opaque->btpo_flags &= ~BTP_HALF_DEAD;
1781  opaque->btpo_flags |= BTP_DELETED;
1782  opaque->btpo.xact = ReadNewTransactionId();
1783 
1784  /* And update the metapage, if needed */
1785  if (BufferIsValid(metabuf))
1786  {
1787  metad->btm_fastroot = rightsib;
1788  metad->btm_fastlevel = targetlevel;
1789  MarkBufferDirty(metabuf);
1790  }
1791 
1792  /* Must mark buffers dirty before XLogInsert */
1793  MarkBufferDirty(rbuf);
1794  MarkBufferDirty(buf);
1795  if (BufferIsValid(lbuf))
1796  MarkBufferDirty(lbuf);
1797  if (target != leafblkno)
1798  MarkBufferDirty(leafbuf);
1799 
1800  /* XLOG stuff */
1801  if (RelationNeedsWAL(rel))
1802  {
1803  xl_btree_unlink_page xlrec;
1804  xl_btree_metadata xlmeta;
1805  uint8 xlinfo;
1806  XLogRecPtr recptr;
1807 
1808  XLogBeginInsert();
1809 
1811  if (BufferIsValid(lbuf))
1814  if (target != leafblkno)
1815  XLogRegisterBuffer(3, leafbuf, REGBUF_WILL_INIT);
1816 
1817  /* information on the unlinked block */
1818  xlrec.leftsib = leftsib;
1819  xlrec.rightsib = rightsib;
1820  xlrec.btpo_xact = opaque->btpo.xact;
1821 
1822  /* information needed to recreate the leaf block (if not the target) */
1823  xlrec.leafleftsib = leafleftsib;
1824  xlrec.leafrightsib = leafrightsib;
1825  xlrec.topparent = nextchild;
1826 
1827  XLogRegisterData((char *) &xlrec, SizeOfBtreeUnlinkPage);
1828 
1829  if (BufferIsValid(metabuf))
1830  {
1832 
1833  xlmeta.root = metad->btm_root;
1834  xlmeta.level = metad->btm_level;
1835  xlmeta.fastroot = metad->btm_fastroot;
1836  xlmeta.fastlevel = metad->btm_fastlevel;
1837 
1838  XLogRegisterBufData(4, (char *) &xlmeta, sizeof(xl_btree_metadata));
1839  xlinfo = XLOG_BTREE_UNLINK_PAGE_META;
1840  }
1841  else
1842  xlinfo = XLOG_BTREE_UNLINK_PAGE;
1843 
1844  recptr = XLogInsert(RM_BTREE_ID, xlinfo);
1845 
1846  if (BufferIsValid(metabuf))
1847  {
1848  PageSetLSN(metapg, recptr);
1849  }
1850  page = BufferGetPage(rbuf);
1851  PageSetLSN(page, recptr);
1852  page = BufferGetPage(buf);
1853  PageSetLSN(page, recptr);
1854  if (BufferIsValid(lbuf))
1855  {
1856  page = BufferGetPage(lbuf);
1857  PageSetLSN(page, recptr);
1858  }
1859  if (target != leafblkno)
1860  {
1861  page = BufferGetPage(leafbuf);
1862  PageSetLSN(page, recptr);
1863  }
1864  }
1865 
1866  END_CRIT_SECTION();
1867 
1868  /* release metapage */
1869  if (BufferIsValid(metabuf))
1870  _bt_relbuf(rel, metabuf);
1871 
1872  /* release siblings */
1873  if (BufferIsValid(lbuf))
1874  _bt_relbuf(rel, lbuf);
1875  _bt_relbuf(rel, rbuf);
1876 
1877  /*
1878  * Release the target, if it was not the leaf block. The leaf is always
1879  * kept locked.
1880  */
1881  if (target != leafblkno)
1882  _bt_relbuf(rel, buf);
1883 
1884  return true;
1885 }
#define ItemPointerIsValid(pointer)
Definition: itemptr.h:60
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:361
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:87
BlockNumber btpo_next
Definition: nbtree.h:57
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:571
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:213
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:205
#define BTP_HALF_DEAD
Definition: nbtree.h:74
union BTPageOpaqueData::@46 btpo
#define END_CRIT_SECTION()
Definition: miscadmin.h:133
BlockNumber root
Definition: nbtxlog.h:47
unsigned char uint8
Definition: c.h:294
#define P_NONE
Definition: nbtree.h:168
#define InvalidBuffer
Definition: buf.h:25
#define REGBUF_WILL_INIT
Definition: xloginsert.h:32
#define START_CRIT_SECTION()
Definition: miscadmin.h:131
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3309
#define BTP_DELETED
Definition: nbtree.h:72
#define LOG
Definition: elog.h:26
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:353
TransactionId xact
Definition: nbtree.h:61
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:67
#define BT_READ
Definition: nbtree.h:238
BlockNumber btm_fastroot
Definition: nbtree.h:102
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:180
#define ERROR
Definition: elog.h:43
#define BTPageGetMeta(p)
Definition: nbtree.h:106
BlockNumber btpo_prev
Definition: nbtree.h:56
static char * buf
Definition: pg_test_fsync.c:67
IndexTupleData * IndexTuple
Definition: itup.h:53
#define REGBUF_STANDARD
Definition: xloginsert.h:34
#define RelationGetRelationName(relation)
Definition: rel.h:436
TransactionId ReadNewTransactionId(void)
Definition: varsup.c:250
#define XLOG_BTREE_UNLINK_PAGE
Definition: nbtxlog.h:33
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define BTREE_METAPAGE
Definition: nbtree.h:109
#define P_ISDELETED(opaque)
Definition: nbtree.h:178
#define P_ISROOT(opaque)
Definition: nbtree.h:177
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:231
uint32 btm_fastlevel
Definition: nbtree.h:103
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:323
uint32 level
Definition: nbtree.h:60
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:415
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
BlockNumber btm_root
Definition: nbtree.h:100
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:721
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:670
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
#define InvalidBlockNumber
Definition: block.h:33
#define BufferIsValid(bufnum)
Definition: bufmgr.h:114
#define RelationNeedsWAL(relation)
Definition: rel.h:505
#define P_HIKEY
Definition: nbtree.h:203
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:150
uint32 fastlevel
Definition: nbtxlog.h:50
uint32 btm_level
Definition: nbtree.h:101
uint32 level
Definition: nbtxlog.h:48
#define SizeOfBtreeUnlinkPage
Definition: nbtxlog.h:224
BlockNumber fastroot
Definition: nbtxlog.h:49
#define XLOG_BTREE_UNLINK_PAGE_META
Definition: nbtxlog.h:34
#define elog
Definition: elog.h:219
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:76
#define BT_WRITE
Definition: nbtree.h:239
void XLogBeginInsert(void)
Definition: xloginsert.c:120
uint16 btpo_flags
Definition: nbtree.h:63
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:175
#define PageGetItem(page, itemId)
Definition: bufpage.h:336
Pointer Page
Definition: bufpage.h:74
#define ItemPointerSet(pointer, blockNumber, offNum)
Definition: itemptr.h:105
#define P_ISLEAF(opaque)
Definition: nbtree.h:176