PostgreSQL Source Code  git master
nbtpage.c File Reference
#include "postgres.h"
#include "access/nbtree.h"
#include "access/nbtxlog.h"
#include "access/tableam.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/memdebug.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 void _bt_log_reuse_page (Relation rel, BlockNumber blkno, FullTransactionId safexid)
 
static void _bt_delitems_delete (Relation rel, Buffer buf, TransactionId latestRemovedXid, OffsetNumber *deletable, int ndeletable, BTVacuumPosting *updatable, int nupdatable)
 
static char * _bt_delitems_update (BTVacuumPosting *updatable, int nupdatable, OffsetNumber *updatedoffsets, Size *updatedbuflen, bool needswal)
 
static bool _bt_mark_page_halfdead (Relation rel, Buffer leafbuf, BTStack stack)
 
static bool _bt_unlink_halfdead_page (Relation rel, Buffer leafbuf, BlockNumber scanblkno, bool *rightsib_empty, BTVacState *vstate)
 
static bool _bt_lock_subtree_parent (Relation rel, BlockNumber child, BTStack stack, Buffer *subtreeparent, OffsetNumber *poffset, BlockNumber *topparent, BlockNumber *topparentrightsib)
 
void _bt_initmetapage (Page page, BlockNumber rootbknum, uint32 level, bool allequalimage)
 
void _bt_upgrademetapage (Page page)
 
void _bt_set_cleanup_info (Relation rel, BlockNumber num_delpages, float8 num_heap_tuples)
 
Buffer _bt_getroot (Relation rel, int access)
 
Buffer _bt_gettrueroot (Relation rel)
 
int _bt_getrootheight (Relation rel)
 
void _bt_metaversion (Relation rel, bool *heapkeyspace, bool *allequalimage)
 
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_lockbuf (Relation rel, Buffer buf, int access)
 
void _bt_unlockbuf (Relation rel, Buffer buf)
 
bool _bt_conditionallockbuf (Relation rel, Buffer buf)
 
void _bt_upgradelockbufcleanup (Relation rel, Buffer buf)
 
void _bt_pageinit (Page page, Size size)
 
void _bt_delitems_vacuum (Relation rel, Buffer buf, OffsetNumber *deletable, int ndeletable, BTVacuumPosting *updatable, int nupdatable)
 
static int _bt_delitems_cmp (const void *a, const void *b)
 
void _bt_delitems_delete_check (Relation rel, Buffer buf, Relation heapRel, TM_IndexDeleteOp *delstate)
 
static bool _bt_leftsib_splitflag (Relation rel, BlockNumber leftsib, BlockNumber target)
 
static bool _bt_rightsib_halfdeadflag (Relation rel, BlockNumber leafrightsib)
 
void _bt_pagedel (Relation rel, Buffer leafbuf, BTVacState *vstate)
 

Function Documentation

◆ _bt_checkpage()

void _bt_checkpage ( Relation  rel,
Buffer  buf 
)

Definition at line 755 of file nbtpage.c.

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

Referenced by _bt_getbuf(), _bt_relandgetbuf(), _bt_search_insert(), bt_recheck_sibling_links(), btvacuumpage(), and palloc_btree_page().

756 {
757  Page page = BufferGetPage(buf);
758 
759  /*
760  * ReadBuffer verifies that every newly-read page passes
761  * PageHeaderIsValid, which means it either contains a reasonably sane
762  * page header or is all-zero. We have to defend against the all-zero
763  * case, however.
764  */
765  if (PageIsNew(page))
766  ereport(ERROR,
767  (errcode(ERRCODE_INDEX_CORRUPTED),
768  errmsg("index \"%s\" contains unexpected zero page at block %u",
771  errhint("Please REINDEX it.")));
772 
773  /*
774  * Additionally check that the special area looks sane.
775  */
776  if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BTPageOpaqueData)))
777  ereport(ERROR,
778  (errcode(ERRCODE_INDEX_CORRUPTED),
779  errmsg("index \"%s\" contains corrupted page at block %u",
782  errhint("Please REINDEX it.")));
783 }
int errhint(const char *fmt,...)
Definition: elog.c:1152
int errcode(int sqlerrcode)
Definition: elog.c:694
#define ERROR
Definition: elog.h:45
static char * buf
Definition: pg_test_fsync.c:68
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define ereport(elevel,...)
Definition: elog.h:155
#define MAXALIGN(LEN)
Definition: c.h:757
#define PageGetSpecialSize(page)
Definition: bufpage.h:300
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
#define PageIsNew(page)
Definition: bufpage.h:229
int errmsg(const char *fmt,...)
Definition: elog.c:905
Pointer Page
Definition: bufpage.h:78

◆ _bt_conditionallockbuf()

bool _bt_conditionallockbuf ( Relation  rel,
Buffer  buf 
)

Definition at line 1066 of file nbtpage.c.

References BufferGetPage, ConditionalLockBuffer(), RelationUsesLocalBuffers, and VALGRIND_MAKE_MEM_DEFINED.

Referenced by _bt_getbuf(), and _bt_search_insert().

1067 {
1068  /* ConditionalLockBuffer() asserts that pin is held by this backend */
1069  if (!ConditionalLockBuffer(buf))
1070  return false;
1071 
1072  if (!RelationUsesLocalBuffers(rel))
1074 
1075  return true;
1076 }
#define VALGRIND_MAKE_MEM_DEFINED(addr, size)
Definition: memdebug.h:26
static char * buf
Definition: pg_test_fsync.c:68
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
bool ConditionalLockBuffer(Buffer buffer)
Definition: bufmgr.c:3965
#define RelationUsesLocalBuffers(relation)
Definition: rel.h:573

◆ _bt_delitems_cmp()

static int _bt_delitems_cmp ( const void *  a,
const void *  b 
)
static

Definition at line 1436 of file nbtpage.c.

References Assert, and TM_IndexDelete::id.

Referenced by _bt_delitems_delete_check().

1437 {
1438  TM_IndexDelete *indexdelete1 = (TM_IndexDelete *) a;
1439  TM_IndexDelete *indexdelete2 = (TM_IndexDelete *) b;
1440 
1441  if (indexdelete1->id > indexdelete2->id)
1442  return 1;
1443  if (indexdelete1->id < indexdelete2->id)
1444  return -1;
1445 
1446  Assert(false);
1447 
1448  return 0;
1449 }
#define Assert(condition)
Definition: c.h:804

◆ _bt_delitems_delete()

static void _bt_delitems_delete ( Relation  rel,
Buffer  buf,
TransactionId  latestRemovedXid,
OffsetNumber deletable,
int  ndeletable,
BTVacuumPosting updatable,
int  nupdatable 
)
static

Definition at line 1258 of file nbtpage.c.

References _bt_delitems_update(), Assert, BTP_HAS_GARBAGE, BTPageOpaqueData::btpo_flags, BufferGetBlockNumber(), BufferGetPage, elog, END_CRIT_SECTION, i, IndexTupleSize, BTVacuumPostingData::itup, xl_btree_delete::latestRemovedXid, MarkBufferDirty(), MAXALIGN, MaxIndexTuplesPerPage, xl_btree_delete::ndeleted, xl_btree_delete::nupdated, PageGetSpecialPointer, PageIndexMultiDelete(), PageIndexTupleOverwrite(), PageSetLSN, PANIC, pfree(), REGBUF_STANDARD, RelationGetRelationName, RelationNeedsWAL, SizeOfBtreeDelete, START_CRIT_SECTION, XLOG_BTREE_DELETE, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_delitems_delete_check().

1261 {
1262  Page page = BufferGetPage(buf);
1263  BTPageOpaque opaque;
1264  bool needswal = RelationNeedsWAL(rel);
1265  char *updatedbuf = NULL;
1266  Size updatedbuflen = 0;
1267  OffsetNumber updatedoffsets[MaxIndexTuplesPerPage];
1268 
1269  /* Shouldn't be called unless there's something to do */
1270  Assert(ndeletable > 0 || nupdatable > 0);
1271 
1272  /* Generate new versions of posting lists without deleted TIDs */
1273  if (nupdatable > 0)
1274  updatedbuf = _bt_delitems_update(updatable, nupdatable,
1275  updatedoffsets, &updatedbuflen,
1276  needswal);
1277 
1278  /* No ereport(ERROR) until changes are logged */
1280 
1281  /* Handle updates and deletes just like _bt_delitems_vacuum */
1282  for (int i = 0; i < nupdatable; i++)
1283  {
1284  OffsetNumber updatedoffset = updatedoffsets[i];
1285  IndexTuple itup;
1286  Size itemsz;
1287 
1288  itup = updatable[i]->itup;
1289  itemsz = MAXALIGN(IndexTupleSize(itup));
1290  if (!PageIndexTupleOverwrite(page, updatedoffset, (Item) itup,
1291  itemsz))
1292  elog(PANIC, "failed to update partially dead item in block %u of index \"%s\"",
1294  }
1295 
1296  if (ndeletable > 0)
1297  PageIndexMultiDelete(page, deletable, ndeletable);
1298 
1299  /*
1300  * Unlike _bt_delitems_vacuum, we *must not* clear the vacuum cycle ID at
1301  * this point. The VACUUM command alone controls vacuum cycle IDs.
1302  */
1303  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1304 
1305  /*
1306  * Clear the BTP_HAS_GARBAGE page flag.
1307  *
1308  * This flag indicates the presence of LP_DEAD items on the page (though
1309  * not reliably). Note that we only rely on it with pg_upgrade'd
1310  * !heapkeyspace indexes.
1311  */
1312  opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
1313 
1315 
1316  /* XLOG stuff */
1317  if (needswal)
1318  {
1319  XLogRecPtr recptr;
1320  xl_btree_delete xlrec_delete;
1321 
1322  xlrec_delete.latestRemovedXid = latestRemovedXid;
1323  xlrec_delete.ndeleted = ndeletable;
1324  xlrec_delete.nupdated = nupdatable;
1325 
1326  XLogBeginInsert();
1328  XLogRegisterData((char *) &xlrec_delete, SizeOfBtreeDelete);
1329 
1330  if (ndeletable > 0)
1331  XLogRegisterBufData(0, (char *) deletable,
1332  ndeletable * sizeof(OffsetNumber));
1333 
1334  if (nupdatable > 0)
1335  {
1336  XLogRegisterBufData(0, (char *) updatedoffsets,
1337  nupdatable * sizeof(OffsetNumber));
1338  XLogRegisterBufData(0, updatedbuf, updatedbuflen);
1339  }
1340 
1341  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_DELETE);
1342 
1343  PageSetLSN(page, recptr);
1344  }
1345 
1346  END_CRIT_SECTION();
1347 
1348  /* can't leak memory here */
1349  if (updatedbuf != NULL)
1350  pfree(updatedbuf);
1351  /* free tuples allocated within _bt_delitems_update() */
1352  for (int i = 0; i < nupdatable; i++)
1353  pfree(updatable[i]->itup);
1354 }
uint16 ndeleted
Definition: nbtxlog.h:237
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
TransactionId latestRemovedXid
Definition: nbtxlog.h:236
static char * _bt_delitems_update(BTVacuumPosting *updatable, int nupdatable, OffsetNumber *updatedoffsets, Size *updatedbuflen, bool needswal)
Definition: nbtpage.c:1377
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
IndexTuple itup
Definition: nbtree.h:885
Pointer Item
Definition: item.h:17
uint16 nupdated
Definition: nbtxlog.h:238
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
#define PANIC
Definition: elog.h:55
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
uint16 OffsetNumber
Definition: off.h:24
void pfree(void *pointer)
Definition: mcxt.c:1057
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize)
Definition: bufpage.c:1288
static char * buf
Definition: pg_test_fsync.c:68
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define XLOG_BTREE_DELETE
Definition: nbtxlog.h:34
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:1044
size_t Size
Definition: c.h:540
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define MAXALIGN(LEN)
Definition: c.h:757
#define RelationNeedsWAL(relation)
Definition: rel.h:563
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
#define MaxIndexTuplesPerPage
Definition: itup.h:145
#define elog(elevel,...)
Definition: elog.h:227
int i
#define SizeOfBtreeDelete
Definition: nbtxlog.h:245
void XLogBeginInsert(void)
Definition: xloginsert.c:123
uint16 btpo_flags
Definition: nbtree.h:67
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
#define BTP_HAS_GARBAGE
Definition: nbtree.h:80
Pointer Page
Definition: bufpage.h:78
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ _bt_delitems_delete_check()

void _bt_delitems_delete_check ( Relation  rel,
Buffer  buf,
Relation  heapRel,
TM_IndexDeleteOp delstate 
)

Definition at line 1490 of file nbtpage.c.

References _bt_delitems_cmp(), _bt_delitems_delete(), Assert, TM_IndexDeleteOp::bottomup, BTreeTupleGetHeapTID(), BTreeTupleGetMaxHeapTID(), BTreeTupleGetNPosting(), BTreeTupleGetPostingN(), BTreeTupleIsPosting(), BufferGetPage, BTVacuumPostingData::deletetids, TM_IndexDeleteOp::deltids, i, TM_IndexDelete::id, TM_IndexStatus::idxoffnum, InvalidOffsetNumber, InvalidTransactionId, ItemPointerCompare(), ItemPointerEquals(), BTVacuumPostingData::itup, TM_IndexStatus::knowndeletable, MaxIndexTuplesPerPage, BTVacuumPostingData::ndeletedtids, TM_IndexDeleteOp::ndeltids, OffsetNumberIsValid, offsetof, PageGetItem, PageGetItemId, palloc(), pfree(), qsort, RelationNeedsWAL, TM_IndexDeleteOp::status, IndexTupleData::t_tid, table_index_delete_tuples(), TM_IndexDelete::tid, BTVacuumPostingData::updatedoffset, and XLogStandbyInfoActive.

Referenced by _bt_bottomupdel_pass(), and _bt_simpledel_pass().

1492 {
1493  Page page = BufferGetPage(buf);
1494  TransactionId latestRemovedXid;
1495  OffsetNumber postingidxoffnum = InvalidOffsetNumber;
1496  int ndeletable = 0,
1497  nupdatable = 0;
1500 
1501  /* Use tableam interface to determine which tuples to delete first */
1502  latestRemovedXid = table_index_delete_tuples(heapRel, delstate);
1503 
1504  /* Should not WAL-log latestRemovedXid unless it's required */
1505  if (!XLogStandbyInfoActive() || !RelationNeedsWAL(rel))
1506  latestRemovedXid = InvalidTransactionId;
1507 
1508  /*
1509  * Construct a leaf-page-wise description of what _bt_delitems_delete()
1510  * needs to do to physically delete index tuples from the page.
1511  *
1512  * Must sort deltids array to restore leaf-page-wise order (original order
1513  * before call to tableam). This is the order that the loop expects.
1514  *
1515  * Note that deltids array might be a lot smaller now. It might even have
1516  * no entries at all (with bottom-up deletion caller), in which case there
1517  * is nothing left to do.
1518  */
1519  qsort(delstate->deltids, delstate->ndeltids, sizeof(TM_IndexDelete),
1521  if (delstate->ndeltids == 0)
1522  {
1523  Assert(delstate->bottomup);
1524  return;
1525  }
1526 
1527  /* We definitely have to delete at least one index tuple (or one TID) */
1528  for (int i = 0; i < delstate->ndeltids; i++)
1529  {
1530  TM_IndexStatus *dstatus = delstate->status + delstate->deltids[i].id;
1531  OffsetNumber idxoffnum = dstatus->idxoffnum;
1532  ItemId itemid = PageGetItemId(page, idxoffnum);
1533  IndexTuple itup = (IndexTuple) PageGetItem(page, itemid);
1534  int nestedi,
1535  nitem;
1536  BTVacuumPosting vacposting;
1537 
1538  Assert(OffsetNumberIsValid(idxoffnum));
1539 
1540  if (idxoffnum == postingidxoffnum)
1541  {
1542  /*
1543  * This deltid entry is a TID from a posting list tuple that has
1544  * already been completely processed
1545  */
1546  Assert(BTreeTupleIsPosting(itup));
1548  &delstate->deltids[i].tid) < 0);
1550  &delstate->deltids[i].tid) >= 0);
1551  continue;
1552  }
1553 
1554  if (!BTreeTupleIsPosting(itup))
1555  {
1556  /* Plain non-pivot tuple */
1557  Assert(ItemPointerEquals(&itup->t_tid, &delstate->deltids[i].tid));
1558  if (dstatus->knowndeletable)
1559  deletable[ndeletable++] = idxoffnum;
1560  continue;
1561  }
1562 
1563  /*
1564  * itup is a posting list tuple whose lowest deltids entry (which may
1565  * or may not be for the first TID from itup) is considered here now.
1566  * We should process all of the deltids entries for the posting list
1567  * together now, though (not just the lowest). Remember to skip over
1568  * later itup-related entries during later iterations of outermost
1569  * loop.
1570  */
1571  postingidxoffnum = idxoffnum; /* Remember work in outermost loop */
1572  nestedi = i; /* Initialize for first itup deltids entry */
1573  vacposting = NULL; /* Describes final action for itup */
1574  nitem = BTreeTupleGetNPosting(itup);
1575  for (int p = 0; p < nitem; p++)
1576  {
1577  ItemPointer ptid = BTreeTupleGetPostingN(itup, p);
1578  int ptidcmp = -1;
1579 
1580  /*
1581  * This nested loop reuses work across ptid TIDs taken from itup.
1582  * We take advantage of the fact that both itup's TIDs and deltids
1583  * entries (within a single itup/posting list grouping) must both
1584  * be in ascending TID order.
1585  */
1586  for (; nestedi < delstate->ndeltids; nestedi++)
1587  {
1588  TM_IndexDelete *tcdeltid = &delstate->deltids[nestedi];
1589  TM_IndexStatus *tdstatus = (delstate->status + tcdeltid->id);
1590 
1591  /* Stop once we get past all itup related deltids entries */
1592  Assert(tdstatus->idxoffnum >= idxoffnum);
1593  if (tdstatus->idxoffnum != idxoffnum)
1594  break;
1595 
1596  /* Skip past non-deletable itup related entries up front */
1597  if (!tdstatus->knowndeletable)
1598  continue;
1599 
1600  /* Entry is first partial ptid match (or an exact match)? */
1601  ptidcmp = ItemPointerCompare(&tcdeltid->tid, ptid);
1602  if (ptidcmp >= 0)
1603  {
1604  /* Greater than or equal (partial or exact) match... */
1605  break;
1606  }
1607  }
1608 
1609  /* ...exact ptid match to a deletable deltids entry? */
1610  if (ptidcmp != 0)
1611  continue;
1612 
1613  /* Exact match for deletable deltids entry -- ptid gets deleted */
1614  if (vacposting == NULL)
1615  {
1616  vacposting = palloc(offsetof(BTVacuumPostingData, deletetids) +
1617  nitem * sizeof(uint16));
1618  vacposting->itup = itup;
1619  vacposting->updatedoffset = idxoffnum;
1620  vacposting->ndeletedtids = 0;
1621  }
1622  vacposting->deletetids[vacposting->ndeletedtids++] = p;
1623  }
1624 
1625  /* Final decision on itup, a posting list tuple */
1626 
1627  if (vacposting == NULL)
1628  {
1629  /* No TIDs to delete from itup -- do nothing */
1630  }
1631  else if (vacposting->ndeletedtids == nitem)
1632  {
1633  /* Straight delete of itup (to delete all TIDs) */
1634  deletable[ndeletable++] = idxoffnum;
1635  /* Turns out we won't need granular information */
1636  pfree(vacposting);
1637  }
1638  else
1639  {
1640  /* Delete some (but not all) TIDs from itup */
1641  Assert(vacposting->ndeletedtids > 0 &&
1642  vacposting->ndeletedtids < nitem);
1643  updatable[nupdatable++] = vacposting;
1644  }
1645  }
1646 
1647  /* Physically delete tuples (or TIDs) using deletable (or updatable) */
1648  _bt_delitems_delete(rel, buf, latestRemovedXid, deletable, ndeletable,
1649  updatable, nupdatable);
1650 
1651  /* be tidy */
1652  for (int i = 0; i < nupdatable; i++)
1653  pfree(updatable[i]);
1654 }
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:52
static TransactionId table_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
Definition: tableam.h:1325
TM_IndexDelete * deltids
Definition: tableam.h:228
uint16 ndeletedtids
Definition: nbtree.h:889
uint32 TransactionId
Definition: c.h:587
static ItemPointer BTreeTupleGetHeapTID(IndexTuple itup)
Definition: nbtree.h:612
OffsetNumber updatedoffset
Definition: nbtree.h:886
ItemPointerData t_tid
Definition: itup.h:37
IndexTuple itup
Definition: nbtree.h:885
bool knowndeletable
Definition: tableam.h:196
OffsetNumber idxoffnum
Definition: tableam.h:195
uint16 OffsetNumber
Definition: off.h:24
unsigned short uint16
Definition: c.h:440
void pfree(void *pointer)
Definition: mcxt.c:1057
static char * buf
Definition: pg_test_fsync.c:68
IndexTupleData * IndexTuple
Definition: itup.h:53
#define InvalidTransactionId
Definition: transam.h:31
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
static int _bt_delitems_cmp(const void *a, const void *b)
Definition: nbtpage.c:1436
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
static ItemPointer BTreeTupleGetMaxHeapTID(IndexTuple itup)
Definition: nbtree.h:638
uint16 deletetids[FLEXIBLE_ARRAY_MEMBER]
Definition: nbtree.h:890
TM_IndexStatus * status
Definition: tableam.h:229
#define XLogStandbyInfoActive()
Definition: xlog.h:205
static void _bt_delitems_delete(Relation rel, Buffer buf, TransactionId latestRemovedXid, OffsetNumber *deletable, int ndeletable, BTVacuumPosting *updatable, int nupdatable)
Definition: nbtpage.c:1258
ItemPointerData tid
Definition: tableam.h:189
#define InvalidOffsetNumber
Definition: off.h:26
static ItemPointer BTreeTupleGetPostingN(IndexTuple posting, int n)
Definition: nbtree.h:518
static bool BTreeTupleIsPosting(IndexTuple itup)
Definition: nbtree.h:466
#define Assert(condition)
Definition: c.h:804
#define RelationNeedsWAL(relation)
Definition: rel.h:563
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:29
#define MaxIndexTuplesPerPage
Definition: itup.h:145
void * palloc(Size size)
Definition: mcxt.c:950
int i
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:39
static uint16 BTreeTupleGetNPosting(IndexTuple posting)
Definition: nbtree.h:492
#define qsort(a, b, c, d)
Definition: port.h:504
#define offsetof(type, field)
Definition: c.h:727
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78

◆ _bt_delitems_update()

static char * _bt_delitems_update ( BTVacuumPosting updatable,
int  nupdatable,
OffsetNumber updatedoffsets,
Size updatedbuflen,
bool  needswal 
)
static

Definition at line 1377 of file nbtpage.c.

References _bt_update_posting(), Assert, BTVacuumPostingData::deletetids, i, xl_btree_update::ndeletedtids, BTVacuumPostingData::ndeletedtids, palloc(), SizeOfBtreeUpdate, and BTVacuumPostingData::updatedoffset.

Referenced by _bt_delitems_delete(), and _bt_delitems_vacuum().

1380 {
1381  char *updatedbuf = NULL;
1382  Size buflen = 0;
1383 
1384  /* Shouldn't be called unless there's something to do */
1385  Assert(nupdatable > 0);
1386 
1387  for (int i = 0; i < nupdatable; i++)
1388  {
1389  BTVacuumPosting vacposting = updatable[i];
1390  Size itemsz;
1391 
1392  /* Replace work area IndexTuple with updated version */
1393  _bt_update_posting(vacposting);
1394 
1395  /* Keep track of size of xl_btree_update for updatedbuf in passing */
1396  itemsz = SizeOfBtreeUpdate + vacposting->ndeletedtids * sizeof(uint16);
1397  buflen += itemsz;
1398 
1399  /* Build updatedoffsets buffer in passing */
1400  updatedoffsets[i] = vacposting->updatedoffset;
1401  }
1402 
1403  /* XLOG stuff */
1404  if (needswal)
1405  {
1406  Size offset = 0;
1407 
1408  /* Allocate, set final size for caller */
1409  updatedbuf = palloc(buflen);
1410  *updatedbuflen = buflen;
1411  for (int i = 0; i < nupdatable; i++)
1412  {
1413  BTVacuumPosting vacposting = updatable[i];
1414  Size itemsz;
1415  xl_btree_update update;
1416 
1417  update.ndeletedtids = vacposting->ndeletedtids;
1418  memcpy(updatedbuf + offset, &update.ndeletedtids,
1420  offset += SizeOfBtreeUpdate;
1421 
1422  itemsz = update.ndeletedtids * sizeof(uint16);
1423  memcpy(updatedbuf + offset, vacposting->deletetids, itemsz);
1424  offset += itemsz;
1425  }
1426  }
1427 
1428  return updatedbuf;
1429 }
uint16 ndeletedtids
Definition: nbtree.h:889
void _bt_update_posting(BTVacuumPosting vacposting)
Definition: nbtdedup.c:919
#define SizeOfBtreeUpdate
Definition: nbtxlog.h:260
OffsetNumber updatedoffset
Definition: nbtree.h:886
unsigned short uint16
Definition: c.h:440
uint16 deletetids[FLEXIBLE_ARRAY_MEMBER]
Definition: nbtree.h:890
#define Assert(condition)
Definition: c.h:804
size_t Size
Definition: c.h:540
void * palloc(Size size)
Definition: mcxt.c:950
int i
uint16 ndeletedtids
Definition: nbtxlog.h:255

◆ _bt_delitems_vacuum()

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

Definition at line 1128 of file nbtpage.c.

References _bt_delitems_update(), Assert, BTP_HAS_GARBAGE, BTPageOpaqueData::btpo_cycleid, BTPageOpaqueData::btpo_flags, BufferGetBlockNumber(), BufferGetPage, elog, END_CRIT_SECTION, i, IndexTupleSize, BTVacuumPostingData::itup, MarkBufferDirty(), MAXALIGN, MaxIndexTuplesPerPage, xl_btree_vacuum::ndeleted, xl_btree_vacuum::nupdated, PageGetSpecialPointer, PageIndexMultiDelete(), PageIndexTupleOverwrite(), PageSetLSN, PANIC, pfree(), REGBUF_STANDARD, RelationGetRelationName, RelationNeedsWAL, SizeOfBtreeVacuum, START_CRIT_SECTION, XLOG_BTREE_VACUUM, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by btvacuumpage().

1131 {
1132  Page page = BufferGetPage(buf);
1133  BTPageOpaque opaque;
1134  bool needswal = RelationNeedsWAL(rel);
1135  char *updatedbuf = NULL;
1136  Size updatedbuflen = 0;
1137  OffsetNumber updatedoffsets[MaxIndexTuplesPerPage];
1138 
1139  /* Shouldn't be called unless there's something to do */
1140  Assert(ndeletable > 0 || nupdatable > 0);
1141 
1142  /* Generate new version of posting lists without deleted TIDs */
1143  if (nupdatable > 0)
1144  updatedbuf = _bt_delitems_update(updatable, nupdatable,
1145  updatedoffsets, &updatedbuflen,
1146  needswal);
1147 
1148  /* No ereport(ERROR) until changes are logged */
1150 
1151  /*
1152  * Handle posting tuple updates.
1153  *
1154  * Deliberately do this before handling simple deletes. If we did it the
1155  * other way around (i.e. WAL record order -- simple deletes before
1156  * updates) then we'd have to make compensating changes to the 'updatable'
1157  * array of offset numbers.
1158  *
1159  * PageIndexTupleOverwrite() won't unset each item's LP_DEAD bit when it
1160  * happens to already be set. It's important that we not interfere with
1161  * _bt_delitems_delete().
1162  */
1163  for (int i = 0; i < nupdatable; i++)
1164  {
1165  OffsetNumber updatedoffset = updatedoffsets[i];
1166  IndexTuple itup;
1167  Size itemsz;
1168 
1169  itup = updatable[i]->itup;
1170  itemsz = MAXALIGN(IndexTupleSize(itup));
1171  if (!PageIndexTupleOverwrite(page, updatedoffset, (Item) itup,
1172  itemsz))
1173  elog(PANIC, "failed to update partially dead item in block %u of index \"%s\"",
1175  }
1176 
1177  /* Now handle simple deletes of entire tuples */
1178  if (ndeletable > 0)
1179  PageIndexMultiDelete(page, deletable, ndeletable);
1180 
1181  /*
1182  * We can clear the vacuum cycle ID since this page has certainly been
1183  * processed by the current vacuum scan.
1184  */
1185  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1186  opaque->btpo_cycleid = 0;
1187 
1188  /*
1189  * Clear the BTP_HAS_GARBAGE page flag.
1190  *
1191  * This flag indicates the presence of LP_DEAD items on the page (though
1192  * not reliably). Note that we only rely on it with pg_upgrade'd
1193  * !heapkeyspace indexes. That's why clearing it here won't usually
1194  * interfere with _bt_delitems_delete().
1195  */
1196  opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
1197 
1199 
1200  /* XLOG stuff */
1201  if (needswal)
1202  {
1203  XLogRecPtr recptr;
1204  xl_btree_vacuum xlrec_vacuum;
1205 
1206  xlrec_vacuum.ndeleted = ndeletable;
1207  xlrec_vacuum.nupdated = nupdatable;
1208 
1209  XLogBeginInsert();
1211  XLogRegisterData((char *) &xlrec_vacuum, SizeOfBtreeVacuum);
1212 
1213  if (ndeletable > 0)
1214  XLogRegisterBufData(0, (char *) deletable,
1215  ndeletable * sizeof(OffsetNumber));
1216 
1217  if (nupdatable > 0)
1218  {
1219  XLogRegisterBufData(0, (char *) updatedoffsets,
1220  nupdatable * sizeof(OffsetNumber));
1221  XLogRegisterBufData(0, updatedbuf, updatedbuflen);
1222  }
1223 
1224  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_VACUUM);
1225 
1226  PageSetLSN(page, recptr);
1227  }
1228 
1229  END_CRIT_SECTION();
1230 
1231  /* can't leak memory here */
1232  if (updatedbuf != NULL)
1233  pfree(updatedbuf);
1234  /* free tuples allocated within _bt_delitems_update() */
1235  for (int i = 0; i < nupdatable; i++)
1236  pfree(updatable[i]->itup);
1237 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
static char * _bt_delitems_update(BTVacuumPosting *updatable, int nupdatable, OffsetNumber *updatedoffsets, Size *updatedbuflen, bool needswal)
Definition: nbtpage.c:1377
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
IndexTuple itup
Definition: nbtree.h:885
Pointer Item
Definition: item.h:17
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
uint16 nupdated
Definition: nbtxlog.h:225
#define PANIC
Definition: elog.h:55
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
uint16 OffsetNumber
Definition: off.h:24
void pfree(void *pointer)
Definition: mcxt.c:1057
#define SizeOfBtreeVacuum
Definition: nbtxlog.h:232
BTCycleId btpo_cycleid
Definition: nbtree.h:68
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize)
Definition: bufpage.c:1288
static char * buf
Definition: pg_test_fsync.c:68
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define XLOG_BTREE_VACUUM
Definition: nbtxlog.h:39
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
uint16 ndeleted
Definition: nbtxlog.h:224
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:1044
size_t Size
Definition: c.h:540
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define MAXALIGN(LEN)
Definition: c.h:757
#define RelationNeedsWAL(relation)
Definition: rel.h:563
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
#define MaxIndexTuplesPerPage
Definition: itup.h:145
#define elog(elevel,...)
Definition: elog.h:227
int i
void XLogBeginInsert(void)
Definition: xloginsert.c:123
uint16 btpo_flags
Definition: nbtree.h:67
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
#define BTP_HAS_GARBAGE
Definition: nbtree.h:80
Pointer Page
Definition: bufpage.h:78
#define IndexTupleSize(itup)
Definition: itup.h:71

◆ _bt_getbuf()

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

Definition at line 832 of file nbtpage.c.

References _bt_checkpage(), _bt_conditionallockbuf(), _bt_lockbuf(), _bt_log_reuse_page(), _bt_pageinit(), _bt_relbuf(), Assert, BT_WRITE, BTPageGetDeleteXid(), BTPageIsRecyclable(), buf, BufferGetPage, BufferGetPageSize, DEBUG2, elog, ExclusiveLock, GetFreeIndexPage(), InvalidBlockNumber, LockRelationForExtension(), P_NEW, PageIsNew, ReadBuffer(), RELATION_IS_LOCAL, RelationNeedsWAL, ReleaseBuffer(), UnlockRelationForExtension(), and XLogStandbyInfoActive.

Referenced by _bt_finish_split(), _bt_getroot(), _bt_getrootheight(), _bt_getstackbuf(), _bt_gettrueroot(), _bt_insertonpg(), _bt_killitems(), _bt_leftsib_splitflag(), _bt_metaversion(), _bt_moveright(), _bt_newroot(), _bt_pagedel(), _bt_readnextpage(), _bt_rightsib_halfdeadflag(), _bt_set_cleanup_info(), _bt_split(), _bt_unlink_halfdead_page(), _bt_vacuum_needs_cleanup(), and _bt_walk_left().

833 {
834  Buffer buf;
835 
836  if (blkno != P_NEW)
837  {
838  /* Read an existing block of the relation */
839  buf = ReadBuffer(rel, blkno);
840  _bt_lockbuf(rel, buf, access);
841  _bt_checkpage(rel, buf);
842  }
843  else
844  {
845  bool needLock;
846  Page page;
847 
848  Assert(access == BT_WRITE);
849 
850  /*
851  * First see if the FSM knows of any free pages.
852  *
853  * We can't trust the FSM's report unreservedly; we have to check that
854  * the page is still free. (For example, an already-free page could
855  * have been re-used between the time the last VACUUM scanned it and
856  * the time the VACUUM made its FSM updates.)
857  *
858  * In fact, it's worse than that: we can't even assume that it's safe
859  * to take a lock on the reported page. If somebody else has a lock
860  * on it, or even worse our own caller does, we could deadlock. (The
861  * own-caller scenario is actually not improbable. Consider an index
862  * on a serial or timestamp column. Nearly all splits will be at the
863  * rightmost page, so it's entirely likely that _bt_split will call us
864  * while holding a lock on the page most recently acquired from FSM. A
865  * VACUUM running concurrently with the previous split could well have
866  * placed that page back in FSM.)
867  *
868  * To get around that, we ask for only a conditional lock on the
869  * reported page. If we fail, then someone else is using the page,
870  * and we may reasonably assume it's not free. (If we happen to be
871  * wrong, the worst consequence is the page will be lost to use till
872  * the next VACUUM, which is no big problem.)
873  */
874  for (;;)
875  {
876  blkno = GetFreeIndexPage(rel);
877  if (blkno == InvalidBlockNumber)
878  break;
879  buf = ReadBuffer(rel, blkno);
880  if (_bt_conditionallockbuf(rel, buf))
881  {
882  page = BufferGetPage(buf);
883 
884  /*
885  * It's possible to find an all-zeroes page in an index. For
886  * example, a backend might successfully extend the relation
887  * one page and then crash before it is able to make a WAL
888  * entry for adding the page. If we find a zeroed page then
889  * reclaim it immediately.
890  */
891  if (PageIsNew(page))
892  {
893  /* Okay to use page. Initialize and return it. */
894  _bt_pageinit(page, BufferGetPageSize(buf));
895  return buf;
896  }
897 
898  if (BTPageIsRecyclable(page))
899  {
900  /*
901  * If we are generating WAL for Hot Standby then create a
902  * WAL record that will allow us to conflict with queries
903  * running on standby, in case they have snapshots older
904  * than safexid value
905  */
907  _bt_log_reuse_page(rel, blkno,
908  BTPageGetDeleteXid(page));
909 
910  /* Okay to use page. Re-initialize and return it. */
911  _bt_pageinit(page, BufferGetPageSize(buf));
912  return buf;
913  }
914  elog(DEBUG2, "FSM returned nonrecyclable page");
915  _bt_relbuf(rel, buf);
916  }
917  else
918  {
919  elog(DEBUG2, "FSM returned nonlockable page");
920  /* couldn't get lock, so just drop pin */
921  ReleaseBuffer(buf);
922  }
923  }
924 
925  /*
926  * Extend the relation by one page.
927  *
928  * We have to use a lock to ensure no one else is extending the rel at
929  * the same time, else we will both try to initialize the same new
930  * page. We can skip locking for new or temp relations, however,
931  * since no one else could be accessing them.
932  */
933  needLock = !RELATION_IS_LOCAL(rel);
934 
935  if (needLock)
937 
938  buf = ReadBuffer(rel, P_NEW);
939 
940  /* Acquire buffer lock on new page */
941  _bt_lockbuf(rel, buf, BT_WRITE);
942 
943  /*
944  * Release the file-extension lock; it's now OK for someone else to
945  * extend the relation some more. Note that we cannot release this
946  * lock before we have buffer lock on the new page, or we risk a race
947  * condition against btvacuumscan --- see comments therein.
948  */
949  if (needLock)
951 
952  /* Initialize the new page before returning it */
953  page = BufferGetPage(buf);
954  Assert(PageIsNew(page));
955  _bt_pageinit(page, BufferGetPageSize(buf));
956  }
957 
958  /* ref count and lock type are correct */
959  return buf;
960 }
#define ExclusiveLock
Definition: lockdefs.h:44
#define RELATION_IS_LOCAL(relation)
Definition: rel.h:584
static void _bt_log_reuse_page(Relation rel, BlockNumber blkno, FullTransactionId safexid)
Definition: nbtpage.c:789
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
#define P_NEW
Definition: bufmgr.h:91
void _bt_checkpage(Relation rel, Buffer buf)
Definition: nbtpage.c:755
#define DEBUG2
Definition: elog.h:24
static char * buf
Definition: pg_test_fsync.c:68
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void LockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:402
void _bt_lockbuf(Relation rel, Buffer buf, int access)
Definition: nbtpage.c:1012
void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode)
Definition: lmgr.c:452
#define BufferGetPageSize(buffer)
Definition: bufmgr.h:156
#define XLogStandbyInfoActive()
Definition: xlog.h:205
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
BlockNumber GetFreeIndexPage(Relation rel)
Definition: indexfsm.c:38
#define Assert(condition)
Definition: c.h:804
static FullTransactionId BTPageGetDeleteXid(Page page)
Definition: nbtree.h:259
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:619
#define InvalidBlockNumber
Definition: block.h:33
static bool BTPageIsRecyclable(Page page)
Definition: nbtree.h:289
#define RelationNeedsWAL(relation)
Definition: rel.h:563
#define PageIsNew(page)
Definition: bufpage.h:229
#define elog(elevel,...)
Definition: elog.h:227
void _bt_pageinit(Page page, Size size)
Definition: nbtpage.c:1103
bool _bt_conditionallockbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:1066
#define BT_WRITE
Definition: nbtree.h:694
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 140 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_metaversion().

141 {
142  Page metapg;
143  BTPageOpaque metaopaque;
144  BTMetaPageData *metad;
145 
146  metapg = BufferGetPage(metabuf);
147  metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
148  metad = BTPageGetMeta(metapg);
149 
150  /* sanity-check the metapage */
151  if (!P_ISMETA(metaopaque) ||
152  metad->btm_magic != BTREE_MAGIC)
153  ereport(ERROR,
154  (errcode(ERRCODE_INDEX_CORRUPTED),
155  errmsg("index \"%s\" is not a btree",
156  RelationGetRelationName(rel))));
157 
158  if (metad->btm_version < BTREE_MIN_VERSION ||
159  metad->btm_version > BTREE_VERSION)
160  ereport(ERROR,
161  (errcode(ERRCODE_INDEX_CORRUPTED),
162  errmsg("version mismatch in index \"%s\": file version %d, "
163  "current version %d, minimal supported version %d",
166 
167  return metad;
168 }
uint32 btm_version
Definition: nbtree.h:104
#define BTREE_VERSION
Definition: nbtree.h:148
uint32 btm_magic
Definition: nbtree.h:103
int errcode(int sqlerrcode)
Definition: elog.c:694
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
#define P_ISMETA(opaque)
Definition: nbtree.h:222
#define BTREE_MAGIC
Definition: nbtree.h:147
#define ERROR
Definition: elog.h:45
#define BTPageGetMeta(p)
Definition: nbtree.h:119
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define BTREE_MIN_VERSION
Definition: nbtree.h:149
#define ereport(elevel,...)
Definition: elog.h:155
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
int errmsg(const char *fmt,...)
Definition: elog.c:905
Pointer Page
Definition: bufpage.h:78

◆ _bt_getroot()

Buffer _bt_getroot ( Relation  rel,
int  access 
)

Definition at line 303 of file nbtpage.c.

References _bt_getbuf(), _bt_getmeta(), _bt_getroot(), _bt_lockbuf(), _bt_relandgetbuf(), _bt_relbuf(), _bt_unlockbuf(), _bt_upgrademetapage(), xl_btree_metadata::allequalimage, Assert, BT_READ, BT_WRITE, BTMetaPageData::btm_allequalimage, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_last_cleanup_num_delpages, BTMetaPageData::btm_last_cleanup_num_heap_tuples, BTMetaPageData::btm_level, BTMetaPageData::btm_magic, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTP_LEAF, BTP_ROOT, BTPageOpaqueData::btpo_cycleid, BTPageOpaqueData::btpo_flags, BTPageOpaqueData::btpo_level, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_NOVAC_VERSION, BTREE_VERSION, BufferGetBlockNumber(), BufferGetPage, elog, END_CRIT_SECTION, ERROR, xl_btree_metadata::fastlevel, xl_btree_metadata::fastroot, InvalidBuffer, xl_btree_metadata::last_cleanup_num_delpages, xl_btree_metadata::last_cleanup_num_heap_tuples, xl_btree_metadata::level, xl_btree_newroot::level, MarkBufferDirty(), MemoryContextAlloc(), 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().

304 {
305  Buffer metabuf;
306  Buffer rootbuf;
307  Page rootpage;
308  BTPageOpaque rootopaque;
309  BlockNumber rootblkno;
310  uint32 rootlevel;
311  BTMetaPageData *metad;
312 
313  /*
314  * Try to use previously-cached metapage data to find the root. This
315  * normally saves one buffer access per index search, which is a very
316  * helpful savings in bufmgr traffic and hence contention.
317  */
318  if (rel->rd_amcache != NULL)
319  {
320  metad = (BTMetaPageData *) rel->rd_amcache;
321  /* We shouldn't have cached it if any of these fail */
322  Assert(metad->btm_magic == BTREE_MAGIC);
324  Assert(metad->btm_version <= BTREE_VERSION);
325  Assert(!metad->btm_allequalimage ||
327  Assert(metad->btm_root != P_NONE);
328 
329  rootblkno = metad->btm_fastroot;
330  Assert(rootblkno != P_NONE);
331  rootlevel = metad->btm_fastlevel;
332 
333  rootbuf = _bt_getbuf(rel, rootblkno, BT_READ);
334  rootpage = BufferGetPage(rootbuf);
335  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
336 
337  /*
338  * Since the cache might be stale, we check the page more carefully
339  * here than normal. We *must* check that it's not deleted. If it's
340  * not alone on its level, then we reject too --- this may be overly
341  * paranoid but better safe than sorry. Note we don't check P_ISROOT,
342  * because that's not set in a "fast root".
343  */
344  if (!P_IGNORE(rootopaque) &&
345  rootopaque->btpo_level == rootlevel &&
346  P_LEFTMOST(rootopaque) &&
347  P_RIGHTMOST(rootopaque))
348  {
349  /* OK, accept cached page as the root */
350  return rootbuf;
351  }
352  _bt_relbuf(rel, rootbuf);
353  /* Cache is stale, throw it away */
354  if (rel->rd_amcache)
355  pfree(rel->rd_amcache);
356  rel->rd_amcache = NULL;
357  }
358 
359  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
360  metad = _bt_getmeta(rel, metabuf);
361 
362  /* if no root page initialized yet, do it */
363  if (metad->btm_root == P_NONE)
364  {
365  Page metapg;
366 
367  /* If access = BT_READ, caller doesn't want us to create root yet */
368  if (access == BT_READ)
369  {
370  _bt_relbuf(rel, metabuf);
371  return InvalidBuffer;
372  }
373 
374  /* trade in our read lock for a write lock */
375  _bt_unlockbuf(rel, metabuf);
376  _bt_lockbuf(rel, metabuf, BT_WRITE);
377 
378  /*
379  * Race condition: if someone else initialized the metadata between
380  * the time we released the read lock and acquired the write lock, we
381  * must avoid doing it again.
382  */
383  if (metad->btm_root != P_NONE)
384  {
385  /*
386  * Metadata initialized by someone else. In order to guarantee no
387  * deadlocks, we have to release the metadata page and start all
388  * over again. (Is that really true? But it's hardly worth trying
389  * to optimize this case.)
390  */
391  _bt_relbuf(rel, metabuf);
392  return _bt_getroot(rel, access);
393  }
394 
395  /*
396  * Get, initialize, write, and leave a lock of the appropriate type on
397  * the new root page. Since this is the first page in the tree, it's
398  * a leaf as well as the root.
399  */
400  rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE);
401  rootblkno = BufferGetBlockNumber(rootbuf);
402  rootpage = BufferGetPage(rootbuf);
403  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
404  rootopaque->btpo_prev = rootopaque->btpo_next = P_NONE;
405  rootopaque->btpo_flags = (BTP_LEAF | BTP_ROOT);
406  rootopaque->btpo_level = 0;
407  rootopaque->btpo_cycleid = 0;
408  /* Get raw page pointer for metapage */
409  metapg = BufferGetPage(metabuf);
410 
411  /* NO ELOG(ERROR) till meta is updated */
413 
414  /* upgrade metapage if needed */
415  if (metad->btm_version < BTREE_NOVAC_VERSION)
416  _bt_upgrademetapage(metapg);
417 
418  metad->btm_root = rootblkno;
419  metad->btm_level = 0;
420  metad->btm_fastroot = rootblkno;
421  metad->btm_fastlevel = 0;
423  metad->btm_last_cleanup_num_heap_tuples = -1.0;
424 
425  MarkBufferDirty(rootbuf);
426  MarkBufferDirty(metabuf);
427 
428  /* XLOG stuff */
429  if (RelationNeedsWAL(rel))
430  {
431  xl_btree_newroot xlrec;
432  XLogRecPtr recptr;
434 
435  XLogBeginInsert();
438 
440  md.version = metad->btm_version;
441  md.root = rootblkno;
442  md.level = 0;
443  md.fastroot = rootblkno;
444  md.fastlevel = 0;
447  md.allequalimage = metad->btm_allequalimage;
448 
449  XLogRegisterBufData(2, (char *) &md, sizeof(xl_btree_metadata));
450 
451  xlrec.rootblk = rootblkno;
452  xlrec.level = 0;
453 
454  XLogRegisterData((char *) &xlrec, SizeOfBtreeNewroot);
455 
456  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_NEWROOT);
457 
458  PageSetLSN(rootpage, recptr);
459  PageSetLSN(metapg, recptr);
460  }
461 
463 
464  /*
465  * swap root write lock for read lock. There is no danger of anyone
466  * else accessing the new root page while it's unlocked, since no one
467  * else knows where it is yet.
468  */
469  _bt_unlockbuf(rel, rootbuf);
470  _bt_lockbuf(rel, rootbuf, BT_READ);
471 
472  /* okay, metadata is correct, release lock on it without caching */
473  _bt_relbuf(rel, metabuf);
474  }
475  else
476  {
477  rootblkno = metad->btm_fastroot;
478  Assert(rootblkno != P_NONE);
479  rootlevel = metad->btm_fastlevel;
480 
481  /*
482  * Cache the metapage data for next time
483  */
485  sizeof(BTMetaPageData));
486  memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
487 
488  /*
489  * We are done with the metapage; arrange to release it via first
490  * _bt_relandgetbuf call
491  */
492  rootbuf = metabuf;
493 
494  for (;;)
495  {
496  rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, BT_READ);
497  rootpage = BufferGetPage(rootbuf);
498  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
499 
500  if (!P_IGNORE(rootopaque))
501  break;
502 
503  /* it's dead, Jim. step right one page */
504  if (P_RIGHTMOST(rootopaque))
505  elog(ERROR, "no live root page found in index \"%s\"",
507  rootblkno = rootopaque->btpo_next;
508  }
509 
510  if (rootopaque->btpo_level != rootlevel)
511  elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
512  rootblkno, RelationGetRelationName(rel),
513  rootopaque->btpo_level, rootlevel);
514  }
515 
516  /*
517  * By here, we have a pin and read lock on the root page, and no lock set
518  * on the metadata page. Return the root page's buffer.
519  */
520  return rootbuf;
521 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
bool allequalimage
Definition: nbtxlog.h:58
BlockNumber rootblk
Definition: nbtxlog.h:335
#define BTP_ROOT
Definition: nbtree.h:75
BlockNumber btpo_next
Definition: nbtree.h:65
static BTMetaPageData * _bt_getmeta(Relation rel, Buffer metabuf)
Definition: nbtpage.c:140
uint32 last_cleanup_num_delpages
Definition: nbtxlog.h:56
#define P_IGNORE(opaque)
Definition: nbtree.h:224
void _bt_upgrademetapage(Page page)
Definition: nbtpage.c:105
uint32 btm_version
Definition: nbtree.h:104
Buffer _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access)
Definition: nbtpage.c:976
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:832
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define BTREE_VERSION
Definition: nbtree.h:148
uint32 btm_magic
Definition: nbtree.h:103
#define BTP_LEAF
Definition: nbtree.h:74
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
BlockNumber root
Definition: nbtxlog.h:52
#define P_NONE
Definition: nbtree.h:211
#define InvalidBuffer
Definition: buf.h:25
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
uint32 level
Definition: nbtxlog.h:336
uint32 BlockNumber
Definition: block.h:31
#define P_NEW
Definition: bufmgr.h:91
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
#define BT_READ
Definition: nbtree.h:693
BlockNumber btm_fastroot
Definition: nbtree.h:107
#define XLOG_BTREE_NEWROOT
Definition: nbtxlog.h:37
void pfree(void *pointer)
Definition: mcxt.c:1057
#define BTREE_MAGIC
Definition: nbtree.h:147
#define ERROR
Definition: elog.h:45
float8 last_cleanup_num_heap_tuples
Definition: nbtxlog.h:57
BTCycleId btpo_cycleid
Definition: nbtree.h:68
bool btm_allequalimage
Definition: nbtree.h:116
BlockNumber btpo_prev
Definition: nbtree.h:64
uint32 btpo_level
Definition: nbtree.h:66
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define P_LEFTMOST(opaque)
Definition: nbtree.h:217
unsigned int uint32
Definition: c.h:441
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:150
Buffer _bt_getroot(Relation rel, int access)
Definition: nbtpage.c:303
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
uint32 btm_last_cleanup_num_delpages
Definition: nbtree.h:112
#define BTREE_METAPAGE
Definition: nbtree.h:146
#define SizeOfBtreeNewroot
Definition: nbtxlog.h:339
uint32 version
Definition: nbtxlog.h:51
uint32 btm_fastlevel
Definition: nbtree.h:108
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
void _bt_lockbuf(Relation rel, Buffer buf, int access)
Definition: nbtpage.c:1012
BlockNumber btm_root
Definition: nbtree.h:105
#define BTREE_MIN_VERSION
Definition: nbtree.h:149
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:114
#define RelationNeedsWAL(relation)
Definition: rel.h:563
void _bt_unlockbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:1043
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:797
uint32 fastlevel
Definition: nbtxlog.h:55
uint32 btm_level
Definition: nbtree.h:106
#define elog(elevel,...)
Definition: elog.h:227
MemoryContext rd_indexcxt
Definition: rel.h:187
uint32 level
Definition: nbtxlog.h:53
BlockNumber fastroot
Definition: nbtxlog.h:54
void * rd_amcache
Definition: rel.h:212
#define BT_WRITE
Definition: nbtree.h:694
void XLogBeginInsert(void)
Definition: xloginsert.c:123
uint16 btpo_flags
Definition: nbtree.h:67
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:218
Pointer Page
Definition: bufpage.h:78

◆ _bt_getrootheight()

int _bt_getrootheight ( Relation  rel)

Definition at line 633 of file nbtpage.c.

References _bt_getbuf(), _bt_getmeta(), _bt_relbuf(), Assert, BT_READ, BTMetaPageData::btm_allequalimage, BTMetaPageData::btm_fastlevel, 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_insertonpg(), and get_relation_info().

634 {
635  BTMetaPageData *metad;
636 
637  if (rel->rd_amcache == NULL)
638  {
639  Buffer metabuf;
640 
641  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
642  metad = _bt_getmeta(rel, metabuf);
643 
644  /*
645  * If there's no root page yet, _bt_getroot() doesn't expect a cache
646  * to be made, so just stop here and report the index height is zero.
647  * (XXX perhaps _bt_getroot() should be changed to allow this case.)
648  */
649  if (metad->btm_root == P_NONE)
650  {
651  _bt_relbuf(rel, metabuf);
652  return 0;
653  }
654 
655  /*
656  * Cache the metapage data for next time
657  */
659  sizeof(BTMetaPageData));
660  memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
661  _bt_relbuf(rel, metabuf);
662  }
663 
664  /* Get cached page */
665  metad = (BTMetaPageData *) rel->rd_amcache;
666  /* We shouldn't have cached it if any of these fail */
667  Assert(metad->btm_magic == BTREE_MAGIC);
669  Assert(metad->btm_version <= BTREE_VERSION);
670  Assert(!metad->btm_allequalimage ||
672  Assert(metad->btm_fastroot != P_NONE);
673 
674  return metad->btm_fastlevel;
675 }
static BTMetaPageData * _bt_getmeta(Relation rel, Buffer metabuf)
Definition: nbtpage.c:140
uint32 btm_version
Definition: nbtree.h:104
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:832
#define BTREE_VERSION
Definition: nbtree.h:148
uint32 btm_magic
Definition: nbtree.h:103
#define P_NONE
Definition: nbtree.h:211
#define BT_READ
Definition: nbtree.h:693
BlockNumber btm_fastroot
Definition: nbtree.h:107
#define BTREE_MAGIC
Definition: nbtree.h:147
bool btm_allequalimage
Definition: nbtree.h:116
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:150
#define BTREE_METAPAGE
Definition: nbtree.h:146
uint32 btm_fastlevel
Definition: nbtree.h:108
BlockNumber btm_root
Definition: nbtree.h:105
#define BTREE_MIN_VERSION
Definition: nbtree.h:149
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
#define Assert(condition)
Definition: c.h:804
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:797
MemoryContext rd_indexcxt
Definition: rel.h:187
void * rd_amcache
Definition: rel.h:212
int Buffer
Definition: buf.h:23

◆ _bt_gettrueroot()

Buffer _bt_gettrueroot ( Relation  rel)

Definition at line 538 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_level, BTPageOpaqueData::btpo_next, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_VERSION, BufferGetPage, elog, ereport, errcode(), errmsg(), ERROR, InvalidBuffer, P_IGNORE, P_ISMETA, P_NONE, P_RIGHTMOST, PageGetSpecialPointer, pfree(), RelationData::rd_amcache, and RelationGetRelationName.

Referenced by _bt_get_endpoint().

539 {
540  Buffer metabuf;
541  Page metapg;
542  BTPageOpaque metaopaque;
543  Buffer rootbuf;
544  Page rootpage;
545  BTPageOpaque rootopaque;
546  BlockNumber rootblkno;
547  uint32 rootlevel;
548  BTMetaPageData *metad;
549 
550  /*
551  * We don't try to use cached metapage data here, since (a) this path is
552  * not performance-critical, and (b) if we are here it suggests our cache
553  * is out-of-date anyway. In light of point (b), it's probably safest to
554  * actively flush any cached metapage info.
555  */
556  if (rel->rd_amcache)
557  pfree(rel->rd_amcache);
558  rel->rd_amcache = NULL;
559 
560  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
561  metapg = BufferGetPage(metabuf);
562  metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
563  metad = BTPageGetMeta(metapg);
564 
565  if (!P_ISMETA(metaopaque) ||
566  metad->btm_magic != BTREE_MAGIC)
567  ereport(ERROR,
568  (errcode(ERRCODE_INDEX_CORRUPTED),
569  errmsg("index \"%s\" is not a btree",
570  RelationGetRelationName(rel))));
571 
572  if (metad->btm_version < BTREE_MIN_VERSION ||
573  metad->btm_version > BTREE_VERSION)
574  ereport(ERROR,
575  (errcode(ERRCODE_INDEX_CORRUPTED),
576  errmsg("version mismatch in index \"%s\": file version %d, "
577  "current version %d, minimal supported version %d",
580 
581  /* if no root page initialized yet, fail */
582  if (metad->btm_root == P_NONE)
583  {
584  _bt_relbuf(rel, metabuf);
585  return InvalidBuffer;
586  }
587 
588  rootblkno = metad->btm_root;
589  rootlevel = metad->btm_level;
590 
591  /*
592  * We are done with the metapage; arrange to release it via first
593  * _bt_relandgetbuf call
594  */
595  rootbuf = metabuf;
596 
597  for (;;)
598  {
599  rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, BT_READ);
600  rootpage = BufferGetPage(rootbuf);
601  rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
602 
603  if (!P_IGNORE(rootopaque))
604  break;
605 
606  /* it's dead, Jim. step right one page */
607  if (P_RIGHTMOST(rootopaque))
608  elog(ERROR, "no live root page found in index \"%s\"",
610  rootblkno = rootopaque->btpo_next;
611  }
612 
613  if (rootopaque->btpo_level != rootlevel)
614  elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
615  rootblkno, RelationGetRelationName(rel),
616  rootopaque->btpo_level, rootlevel);
617 
618  return rootbuf;
619 }
BlockNumber btpo_next
Definition: nbtree.h:65
#define P_IGNORE(opaque)
Definition: nbtree.h:224
uint32 btm_version
Definition: nbtree.h:104
Buffer _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access)
Definition: nbtpage.c:976
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:832
#define BTREE_VERSION
Definition: nbtree.h:148
uint32 btm_magic
Definition: nbtree.h:103
#define P_NONE
Definition: nbtree.h:211
#define InvalidBuffer
Definition: buf.h:25
int errcode(int sqlerrcode)
Definition: elog.c:694
uint32 BlockNumber
Definition: block.h:31
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
#define P_ISMETA(opaque)
Definition: nbtree.h:222
#define BT_READ
Definition: nbtree.h:693
void pfree(void *pointer)
Definition: mcxt.c:1057
#define BTREE_MAGIC
Definition: nbtree.h:147
#define ERROR
Definition: elog.h:45
#define BTPageGetMeta(p)
Definition: nbtree.h:119
uint32 btpo_level
Definition: nbtree.h:66
#define RelationGetRelationName(relation)
Definition: rel.h:491
unsigned int uint32
Definition: c.h:441
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define BTREE_METAPAGE
Definition: nbtree.h:146
BlockNumber btm_root
Definition: nbtree.h:105
#define BTREE_MIN_VERSION
Definition: nbtree.h:149
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
#define ereport(elevel,...)
Definition: elog.h:155
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
int errmsg(const char *fmt,...)
Definition: elog.c:905
uint32 btm_level
Definition: nbtree.h:106
#define elog(elevel,...)
Definition: elog.h:227
void * rd_amcache
Definition: rel.h:212
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:218
Pointer Page
Definition: bufpage.h:78

◆ _bt_initmetapage()

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

Definition at line 65 of file nbtpage.c.

References _bt_pageinit(), BTMetaPageData::btm_allequalimage, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_last_cleanup_num_delpages, BTMetaPageData::btm_last_cleanup_num_heap_tuples, 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().

67 {
68  BTMetaPageData *metad;
69  BTPageOpaque metaopaque;
70 
71  _bt_pageinit(page, BLCKSZ);
72 
73  metad = BTPageGetMeta(page);
74  metad->btm_magic = BTREE_MAGIC;
75  metad->btm_version = BTREE_VERSION;
76  metad->btm_root = rootbknum;
77  metad->btm_level = level;
78  metad->btm_fastroot = rootbknum;
79  metad->btm_fastlevel = level;
82  metad->btm_allequalimage = allequalimage;
83 
84  metaopaque = (BTPageOpaque) PageGetSpecialPointer(page);
85  metaopaque->btpo_flags = BTP_META;
86 
87  /*
88  * Set pd_lower just past the end of the metadata. This is essential,
89  * because without doing so, metadata will be lost if xlog.c compresses
90  * the page.
91  */
92  ((PageHeader) page)->pd_lower =
93  ((char *) metad + sizeof(BTMetaPageData)) - (char *) page;
94 }
uint32 btm_version
Definition: nbtree.h:104
#define BTREE_VERSION
Definition: nbtree.h:148
uint32 btm_magic
Definition: nbtree.h:103
#define BTP_META
Definition: nbtree.h:77
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
BlockNumber btm_fastroot
Definition: nbtree.h:107
#define BTREE_MAGIC
Definition: nbtree.h:147
#define BTPageGetMeta(p)
Definition: nbtree.h:119
bool btm_allequalimage
Definition: nbtree.h:116
uint32 btm_last_cleanup_num_delpages
Definition: nbtree.h:112
uint32 btm_fastlevel
Definition: nbtree.h:108
BlockNumber btm_root
Definition: nbtree.h:105
PageHeaderData * PageHeader
Definition: bufpage.h:166
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:114
uint32 btm_level
Definition: nbtree.h:106
void _bt_pageinit(Page page, Size size)
Definition: nbtpage.c:1103
uint16 btpo_flags
Definition: nbtree.h:67

◆ _bt_leftsib_splitflag()

static bool _bt_leftsib_splitflag ( Relation  rel,
BlockNumber  leftsib,
BlockNumber  target 
)
static

Definition at line 1670 of file nbtpage.c.

References _bt_getbuf(), _bt_relbuf(), BT_READ, BTPageOpaqueData::btpo_next, buf, BufferGetPage, P_INCOMPLETE_SPLIT, P_NONE, and PageGetSpecialPointer.

Referenced by _bt_lock_subtree_parent(), and _bt_pagedel().

1671 {
1672  Buffer buf;
1673  Page page;
1674  BTPageOpaque opaque;
1675  bool result;
1676 
1677  /* Easy case: No left sibling */
1678  if (leftsib == P_NONE)
1679  return false;
1680 
1681  buf = _bt_getbuf(rel, leftsib, BT_READ);
1682  page = BufferGetPage(buf);
1683  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1684 
1685  /*
1686  * If the left sibling was concurrently split, so that its next-pointer
1687  * doesn't point to the current page anymore, the split that created
1688  * target must be completed. Caller can reasonably expect that there will
1689  * be a downlink to the target page that it can relocate using its stack.
1690  * (We don't allow splitting an incompletely split page again until the
1691  * previous split has been completed.)
1692  */
1693  result = (opaque->btpo_next == target && P_INCOMPLETE_SPLIT(opaque));
1694  _bt_relbuf(rel, buf);
1695 
1696  return result;
1697 }
BlockNumber btpo_next
Definition: nbtree.h:65
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:832
#define P_NONE
Definition: nbtree.h:211
#define P_INCOMPLETE_SPLIT(opaque)
Definition: nbtree.h:226
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
#define BT_READ
Definition: nbtree.h:693
static char * buf
Definition: pg_test_fsync.c:68
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78

◆ _bt_lock_subtree_parent()

static bool _bt_lock_subtree_parent ( Relation  rel,
BlockNumber  child,
BTStack  stack,
Buffer subtreeparent,
OffsetNumber poffset,
BlockNumber topparent,
BlockNumber topparentrightsib 
)
static

Definition at line 2730 of file nbtpage.c.

References _bt_getstackbuf(), _bt_leftsib_splitflag(), _bt_relbuf(), Assert, 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_RIGHTMOST, PageGetMaxOffsetNumber, PageGetSpecialPointer, and RelationGetRelationName.

Referenced by _bt_mark_page_halfdead().

2733 {
2734  BlockNumber parent,
2735  leftsibparent;
2736  OffsetNumber parentoffset,
2737  maxoff;
2738  Buffer pbuf;
2739  Page page;
2740  BTPageOpaque opaque;
2741 
2742  /*
2743  * Locate the pivot tuple whose downlink points to "child". Write lock
2744  * the parent page itself.
2745  */
2746  pbuf = _bt_getstackbuf(rel, stack, child);
2747  if (pbuf == InvalidBuffer)
2748  ereport(ERROR,
2749  (errcode(ERRCODE_INDEX_CORRUPTED),
2750  errmsg_internal("failed to re-find parent key in index \"%s\" for deletion target page %u",
2751  RelationGetRelationName(rel), child)));
2752  parent = stack->bts_blkno;
2753  parentoffset = stack->bts_offset;
2754 
2755  page = BufferGetPage(pbuf);
2756  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2757  maxoff = PageGetMaxOffsetNumber(page);
2758  leftsibparent = opaque->btpo_prev;
2759 
2760  /*
2761  * _bt_getstackbuf() completes page splits on returned parent buffer when
2762  * required.
2763  *
2764  * In general it's a bad idea for VACUUM to use up more disk space, which
2765  * is why page deletion does not finish incomplete page splits most of the
2766  * time. We allow this limited exception because the risk is much lower,
2767  * and the potential downside of not proceeding is much higher: A single
2768  * internal page with the INCOMPLETE_SPLIT flag set might otherwise
2769  * prevent us from deleting hundreds of empty leaf pages from one level
2770  * down.
2771  */
2772  Assert(!P_INCOMPLETE_SPLIT(opaque));
2773 
2774  if (parentoffset < maxoff)
2775  {
2776  /*
2777  * Child is not the rightmost child in parent, so it's safe to delete
2778  * the subtree whose root/topparent is child page
2779  */
2780  *subtreeparent = pbuf;
2781  *poffset = parentoffset;
2782  return true;
2783  }
2784 
2785  /*
2786  * Child is the rightmost child of parent.
2787  *
2788  * Since it's the rightmost child of parent, deleting the child (or
2789  * deleting the subtree whose root/topparent is the child page) is only
2790  * safe when it's also possible to delete the parent.
2791  */
2792  Assert(parentoffset == maxoff);
2793  if (parentoffset != P_FIRSTDATAKEY(opaque) || P_RIGHTMOST(opaque))
2794  {
2795  /*
2796  * Child isn't parent's only child, or parent is rightmost on its
2797  * entire level. Definitely cannot delete any pages.
2798  */
2799  _bt_relbuf(rel, pbuf);
2800  return false;
2801  }
2802 
2803  /*
2804  * Now make sure that the parent deletion is itself safe by examining the
2805  * child's grandparent page. Recurse, passing the parent page as the
2806  * child page (child's grandparent is the parent on the next level up). If
2807  * parent deletion is unsafe, then child deletion must also be unsafe (in
2808  * which case caller cannot delete any pages at all).
2809  */
2810  *topparent = parent;
2811  *topparentrightsib = opaque->btpo_next;
2812 
2813  /*
2814  * Release lock on parent before recursing.
2815  *
2816  * It's OK to release page locks on parent before recursive call locks
2817  * grandparent. An internal page can only acquire an entry if the child
2818  * is split, but that cannot happen as long as we still hold a lock on the
2819  * leafbuf page.
2820  */
2821  _bt_relbuf(rel, pbuf);
2822 
2823  /*
2824  * Before recursing, check that the left sibling of parent (if any) is not
2825  * marked with INCOMPLETE_SPLIT flag first (must do so after we drop the
2826  * parent lock).
2827  *
2828  * Note: We deliberately avoid completing incomplete splits here.
2829  */
2830  if (_bt_leftsib_splitflag(rel, leftsibparent, parent))
2831  return false;
2832 
2833  /* Recurse to examine child page's grandparent page */
2834  return _bt_lock_subtree_parent(rel, parent, stack->bts_parent,
2835  subtreeparent, poffset,
2836  topparent, topparentrightsib);
2837 }
BlockNumber btpo_next
Definition: nbtree.h:65
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:350
#define InvalidBuffer
Definition: buf.h:25
static bool _bt_lock_subtree_parent(Relation rel, BlockNumber child, BTStack stack, Buffer *subtreeparent, OffsetNumber *poffset, BlockNumber *topparent, BlockNumber *topparentrightsib)
Definition: nbtpage.c:2730
int errcode(int sqlerrcode)
Definition: elog.c:694
uint32 BlockNumber
Definition: block.h:31
#define P_INCOMPLETE_SPLIT(opaque)
Definition: nbtree.h:226
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
uint16 OffsetNumber
Definition: off.h:24
#define ERROR
Definition: elog.h:45
BlockNumber btpo_prev
Definition: nbtree.h:64
OffsetNumber bts_offset
Definition: nbtree.h:709
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
BlockNumber bts_blkno
Definition: nbtree.h:708
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
#define ereport(elevel,...)
Definition: elog.h:155
int errmsg_internal(const char *fmt,...)
Definition: elog.c:992
#define Assert(condition)
Definition: c.h:804
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
struct BTStackData * bts_parent
Definition: nbtree.h:710
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:218
Buffer _bt_getstackbuf(Relation rel, BTStack stack, BlockNumber child)
Definition: nbtinsert.c:2292
Pointer Page
Definition: bufpage.h:78
static bool _bt_leftsib_splitflag(Relation rel, BlockNumber leftsib, BlockNumber target)
Definition: nbtpage.c:1670

◆ _bt_lockbuf()

void _bt_lockbuf ( Relation  rel,
Buffer  buf,
int  access 
)

Definition at line 1012 of file nbtpage.c.

References BufferGetPage, LockBuffer(), RelationUsesLocalBuffers, and VALGRIND_MAKE_MEM_DEFINED.

Referenced by _bt_getbuf(), _bt_getroot(), _bt_killitems(), _bt_moveright(), _bt_pagedel(), _bt_readnextpage(), _bt_relandgetbuf(), _bt_search(), _bt_set_cleanup_info(), _bt_unlink_halfdead_page(), and btvacuumpage().

1013 {
1014  /* LockBuffer() asserts that pin is held by this backend */
1015  LockBuffer(buf, access);
1016 
1017  /*
1018  * It doesn't matter that _bt_unlockbuf() won't get called in the
1019  * event of an nbtree error (e.g. a unique violation error). That
1020  * won't cause Valgrind false positives.
1021  *
1022  * The nbtree client requests are superimposed on top of the
1023  * bufmgr.c buffer pin client requests. In the event of an nbtree
1024  * error the buffer will certainly get marked as defined when the
1025  * backend once again acquires its first pin on the buffer. (Of
1026  * course, if the backend never touches the buffer again then it
1027  * doesn't matter that it remains non-accessible to Valgrind.)
1028  *
1029  * Note: When an IndexTuple C pointer gets computed using an
1030  * ItemId read from a page while a lock was held, the C pointer
1031  * becomes unsafe to dereference forever as soon as the lock is
1032  * released. Valgrind can only detect cases where the pointer
1033  * gets dereferenced with no _current_ lock/pin held, though.
1034  */
1035  if (!RelationUsesLocalBuffers(rel))
1037 }
#define VALGRIND_MAKE_MEM_DEFINED(addr, size)
Definition: memdebug.h:26
static char * buf
Definition: pg_test_fsync.c:68
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
#define RelationUsesLocalBuffers(relation)
Definition: rel.h:573

◆ _bt_log_reuse_page()

static void _bt_log_reuse_page ( Relation  rel,
BlockNumber  blkno,
FullTransactionId  safexid 
)
static

Definition at line 789 of file nbtpage.c.

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

Referenced by _bt_getbuf().

790 {
791  xl_btree_reuse_page xlrec_reuse;
792 
793  /*
794  * Note that we don't register the buffer with the record, because this
795  * operation doesn't modify the page. This record only exists to provide a
796  * conflict point for Hot Standby.
797  */
798 
799  /* XLOG stuff */
800  xlrec_reuse.node = rel->rd_node;
801  xlrec_reuse.block = blkno;
802  xlrec_reuse.latestRemovedFullXid = safexid;
803 
804  XLogBeginInsert();
805  XLogRegisterData((char *) &xlrec_reuse, SizeOfBtreeReusePage);
806 
807  XLogInsert(RM_BTREE_ID, XLOG_BTREE_REUSE_PAGE);
808 }
FullTransactionId latestRemovedFullXid
Definition: nbtxlog.h:191
RelFileNode node
Definition: nbtxlog.h:189
#define SizeOfBtreeReusePage
Definition: nbtxlog.h:194
BlockNumber block
Definition: nbtxlog.h:190
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
#define XLOG_BTREE_REUSE_PAGE
Definition: nbtxlog.h:41
RelFileNode rd_node
Definition: rel.h:55
void XLogBeginInsert(void)
Definition: xloginsert.c:123

◆ _bt_mark_page_halfdead()

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

Definition at line 2048 of file nbtpage.c.

References _bt_lock_subtree_parent(), _bt_relbuf(), _bt_rightsib_halfdeadflag(), 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_IGNORE, 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().

2049 {
2050  BlockNumber leafblkno;
2051  BlockNumber leafrightsib;
2052  BlockNumber topparent;
2053  BlockNumber topparentrightsib;
2054  ItemId itemid;
2055  Page page;
2056  BTPageOpaque opaque;
2057  Buffer subtreeparent;
2058  OffsetNumber poffset;
2059  OffsetNumber nextoffset;
2060  IndexTuple itup;
2061  IndexTupleData trunctuple;
2062 
2063  page = BufferGetPage(leafbuf);
2064  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2065 
2066  Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) &&
2067  P_ISLEAF(opaque) && !P_IGNORE(opaque) &&
2068  P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
2069 
2070  /*
2071  * Save info about the leaf page.
2072  */
2073  leafblkno = BufferGetBlockNumber(leafbuf);
2074  leafrightsib = opaque->btpo_next;
2075 
2076  /*
2077  * Before attempting to lock the parent page, check that the right sibling
2078  * is not in half-dead state. A half-dead right sibling would have no
2079  * downlink in the parent, which would be highly confusing later when we
2080  * delete the downlink. It would fail the "right sibling of target page
2081  * is also the next child in parent page" cross-check below.
2082  */
2083  if (_bt_rightsib_halfdeadflag(rel, leafrightsib))
2084  {
2085  elog(DEBUG1, "could not delete page %u because its right sibling %u is half-dead",
2086  leafblkno, leafrightsib);
2087  return false;
2088  }
2089 
2090  /*
2091  * We cannot delete a page that is the rightmost child of its immediate
2092  * parent, unless it is the only child --- in which case the parent has to
2093  * be deleted too, and the same condition applies recursively to it. We
2094  * have to check this condition all the way up before trying to delete,
2095  * and lock the parent of the root of the to-be-deleted subtree (the
2096  * "subtree parent"). _bt_lock_subtree_parent() locks the subtree parent
2097  * for us. We remove the downlink to the "top parent" page (subtree root
2098  * page) from the subtree parent page below.
2099  *
2100  * Initialize topparent to be leafbuf page now. The final to-be-deleted
2101  * subtree is often a degenerate one page subtree consisting only of the
2102  * leafbuf page. When that happens, the leafbuf page is the final subtree
2103  * root page/top parent page.
2104  */
2105  topparent = leafblkno;
2106  topparentrightsib = leafrightsib;
2107  if (!_bt_lock_subtree_parent(rel, leafblkno, stack,
2108  &subtreeparent, &poffset,
2109  &topparent, &topparentrightsib))
2110  return false;
2111 
2112  /*
2113  * Check that the parent-page index items we're about to delete/overwrite
2114  * in subtree parent page contain what we expect. This can fail if the
2115  * index has become corrupt for some reason. We want to throw any error
2116  * before entering the critical section --- otherwise it'd be a PANIC.
2117  */
2118  page = BufferGetPage(subtreeparent);
2119  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2120 
2121 #ifdef USE_ASSERT_CHECKING
2122 
2123  /*
2124  * This is just an assertion because _bt_lock_subtree_parent should have
2125  * guaranteed tuple has the expected contents
2126  */
2127  itemid = PageGetItemId(page, poffset);
2128  itup = (IndexTuple) PageGetItem(page, itemid);
2129  Assert(BTreeTupleGetDownLink(itup) == topparent);
2130 #endif
2131 
2132  nextoffset = OffsetNumberNext(poffset);
2133  itemid = PageGetItemId(page, nextoffset);
2134  itup = (IndexTuple) PageGetItem(page, itemid);
2135  if (BTreeTupleGetDownLink(itup) != topparentrightsib)
2136  ereport(ERROR,
2137  (errcode(ERRCODE_INDEX_CORRUPTED),
2138  errmsg_internal("right sibling %u of block %u is not next child %u of block %u in index \"%s\"",
2139  topparentrightsib, topparent,
2140  BTreeTupleGetDownLink(itup),
2141  BufferGetBlockNumber(subtreeparent),
2142  RelationGetRelationName(rel))));
2143 
2144  /*
2145  * Any insert which would have gone on the leaf block will now go to its
2146  * right sibling. In other words, the key space moves right.
2147  */
2148  PredicateLockPageCombine(rel, leafblkno, leafrightsib);
2149 
2150  /* No ereport(ERROR) until changes are logged */
2152 
2153  /*
2154  * Update parent of subtree. We want to delete the downlink to the top
2155  * parent page/root of the subtree, and the *following* key. Easiest way
2156  * is to copy the right sibling's downlink over the downlink that points
2157  * to top parent page, and then delete the right sibling's original pivot
2158  * tuple.
2159  *
2160  * Lanin and Shasha make the key space move left when deleting a page,
2161  * whereas the key space moves right here. That's why we cannot simply
2162  * delete the pivot tuple with the downlink to the top parent page. See
2163  * nbtree/README.
2164  */
2165  page = BufferGetPage(subtreeparent);
2166  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2167 
2168  itemid = PageGetItemId(page, poffset);
2169  itup = (IndexTuple) PageGetItem(page, itemid);
2170  BTreeTupleSetDownLink(itup, topparentrightsib);
2171 
2172  nextoffset = OffsetNumberNext(poffset);
2173  PageIndexTupleDelete(page, nextoffset);
2174 
2175  /*
2176  * Mark the leaf page as half-dead, and stamp it with a link to the top
2177  * parent page. When the leaf page is also the top parent page, the link
2178  * is set to InvalidBlockNumber.
2179  */
2180  page = BufferGetPage(leafbuf);
2181  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2182  opaque->btpo_flags |= BTP_HALF_DEAD;
2183 
2185  MemSet(&trunctuple, 0, sizeof(IndexTupleData));
2186  trunctuple.t_info = sizeof(IndexTupleData);
2187  if (topparent != leafblkno)
2188  BTreeTupleSetTopParent(&trunctuple, topparent);
2189  else
2191 
2192  if (!PageIndexTupleOverwrite(page, P_HIKEY, (Item) &trunctuple,
2193  IndexTupleSize(&trunctuple)))
2194  elog(ERROR, "could not overwrite high key in half-dead page");
2195 
2196  /* Must mark buffers dirty before XLogInsert */
2197  MarkBufferDirty(subtreeparent);
2198  MarkBufferDirty(leafbuf);
2199 
2200  /* XLOG stuff */
2201  if (RelationNeedsWAL(rel))
2202  {
2204  XLogRecPtr recptr;
2205 
2206  xlrec.poffset = poffset;
2207  xlrec.leafblk = leafblkno;
2208  if (topparent != leafblkno)
2209  xlrec.topparent = topparent;
2210  else
2211  xlrec.topparent = InvalidBlockNumber;
2212 
2213  XLogBeginInsert();
2214  XLogRegisterBuffer(0, leafbuf, REGBUF_WILL_INIT);
2215  XLogRegisterBuffer(1, subtreeparent, REGBUF_STANDARD);
2216 
2217  page = BufferGetPage(leafbuf);
2218  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2219  xlrec.leftblk = opaque->btpo_prev;
2220  xlrec.rightblk = opaque->btpo_next;
2221 
2223 
2224  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_MARK_PAGE_HALFDEAD);
2225 
2226  page = BufferGetPage(subtreeparent);
2227  PageSetLSN(page, recptr);
2228  page = BufferGetPage(leafbuf);
2229  PageSetLSN(page, recptr);
2230  }
2231 
2232  END_CRIT_SECTION();
2233 
2234  _bt_relbuf(rel, subtreeparent);
2235  return true;
2236 }
BlockNumber btpo_next
Definition: nbtree.h:65
#define DEBUG1
Definition: elog.h:25
#define P_IGNORE(opaque)
Definition: nbtree.h:224
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:935
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:350
#define SizeOfBtreeMarkPageHalfDead
Definition: nbtxlog.h:283
#define BTP_HALF_DEAD
Definition: nbtree.h:78
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
static bool _bt_rightsib_halfdeadflag(Relation rel, BlockNumber leafrightsib)
Definition: nbtpage.c:1727
Pointer Item
Definition: item.h:17
static BlockNumber BTreeTupleGetDownLink(IndexTuple pivot)
Definition: nbtree.h:530
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
static bool _bt_lock_subtree_parent(Relation rel, BlockNumber child, BTStack stack, Buffer *subtreeparent, OffsetNumber *poffset, BlockNumber *topparent, BlockNumber *topparentrightsib)
Definition: nbtpage.c:2730
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
int errcode(int sqlerrcode)
Definition: elog.c:694
#define MemSet(start, val, len)
Definition: c.h:1008
uint32 BlockNumber
Definition: block.h:31
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
uint16 OffsetNumber
Definition: off.h:24
#define ERROR
Definition: elog.h:45
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize)
Definition: bufpage.c:1288
BlockNumber btpo_prev
Definition: nbtree.h:64
IndexTupleData * IndexTuple
Definition: itup.h:53
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define RelationGetRelationName(relation)
Definition: rel.h:491
static void BTreeTupleSetDownLink(IndexTuple pivot, BlockNumber blkno)
Definition: nbtree.h:536
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define P_ISROOT(opaque)
Definition: nbtree.h:220
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
#define XLOG_BTREE_MARK_PAGE_HALFDEAD
Definition: nbtxlog.h:38
struct IndexTupleData IndexTupleData
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
#define ereport(elevel,...)
Definition: elog.h:155
int errmsg_internal(const char *fmt,...)
Definition: elog.c:992
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
#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:563
#define P_HIKEY
Definition: nbtree.h:348
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
#define elog(elevel,...)
Definition: elog.h:227
void PredicateLockPageCombine(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition: predicate.c:3252
unsigned short t_info
Definition: itup.h:49
void XLogBeginInsert(void)
Definition: xloginsert.c:123
uint16 btpo_flags
Definition: nbtree.h:67
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:218
static void BTreeTupleSetTopParent(IndexTuple leafhikey, BlockNumber blkno)
Definition: nbtree.h:600
#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:219

◆ _bt_metaversion()

void _bt_metaversion ( Relation  rel,
bool heapkeyspace,
bool allequalimage 
)

Definition at line 697 of file nbtpage.c.

References _bt_getbuf(), _bt_getmeta(), _bt_relbuf(), Assert, BT_READ, BTMetaPageData::btm_allequalimage, 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().

698 {
699  BTMetaPageData *metad;
700 
701  if (rel->rd_amcache == NULL)
702  {
703  Buffer metabuf;
704 
705  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
706  metad = _bt_getmeta(rel, metabuf);
707 
708  /*
709  * If there's no root page yet, _bt_getroot() doesn't expect a cache
710  * to be made, so just stop here. (XXX perhaps _bt_getroot() should
711  * be changed to allow this case.)
712  */
713  if (metad->btm_root == P_NONE)
714  {
715  *heapkeyspace = metad->btm_version > BTREE_NOVAC_VERSION;
716  *allequalimage = metad->btm_allequalimage;
717 
718  _bt_relbuf(rel, metabuf);
719  return;
720  }
721 
722  /*
723  * Cache the metapage data for next time
724  *
725  * An on-the-fly version upgrade performed by _bt_upgrademetapage()
726  * can change the nbtree version for an index without invalidating any
727  * local cache. This is okay because it can only happen when moving
728  * from version 2 to version 3, both of which are !heapkeyspace
729  * versions.
730  */
732  sizeof(BTMetaPageData));
733  memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
734  _bt_relbuf(rel, metabuf);
735  }
736 
737  /* Get cached page */
738  metad = (BTMetaPageData *) rel->rd_amcache;
739  /* We shouldn't have cached it if any of these fail */
740  Assert(metad->btm_magic == BTREE_MAGIC);
742  Assert(metad->btm_version <= BTREE_VERSION);
743  Assert(!metad->btm_allequalimage ||
745  Assert(metad->btm_fastroot != P_NONE);
746 
747  *heapkeyspace = metad->btm_version > BTREE_NOVAC_VERSION;
748  *allequalimage = metad->btm_allequalimage;
749 }
static BTMetaPageData * _bt_getmeta(Relation rel, Buffer metabuf)
Definition: nbtpage.c:140
uint32 btm_version
Definition: nbtree.h:104
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:832
#define BTREE_VERSION
Definition: nbtree.h:148
uint32 btm_magic
Definition: nbtree.h:103
#define P_NONE
Definition: nbtree.h:211
#define BT_READ
Definition: nbtree.h:693
BlockNumber btm_fastroot
Definition: nbtree.h:107
#define BTREE_MAGIC
Definition: nbtree.h:147
bool btm_allequalimage
Definition: nbtree.h:116
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:150
#define BTREE_METAPAGE
Definition: nbtree.h:146
BlockNumber btm_root
Definition: nbtree.h:105
#define BTREE_MIN_VERSION
Definition: nbtree.h:149
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
#define Assert(condition)
Definition: c.h:804
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:797
MemoryContext rd_indexcxt
Definition: rel.h:187
void * rd_amcache
Definition: rel.h:212
int Buffer
Definition: buf.h:23

◆ _bt_pagedel()

void _bt_pagedel ( Relation  rel,
Buffer  leafbuf,
BTVacState vstate 
)

Definition at line 1777 of file nbtpage.c.

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

Referenced by btvacuumpage().

1778 {
1779  BlockNumber rightsib;
1780  bool rightsib_empty;
1781  Page page;
1782  BTPageOpaque opaque;
1783 
1784  /*
1785  * Save original leafbuf block number from caller. Only deleted blocks
1786  * that are <= scanblkno are added to bulk delete stat's pages_deleted
1787  * count.
1788  */
1789  BlockNumber scanblkno = BufferGetBlockNumber(leafbuf);
1790 
1791  /*
1792  * "stack" is a search stack leading (approximately) to the target page.
1793  * It is initially NULL, but when iterating, we keep it to avoid
1794  * duplicated search effort.
1795  *
1796  * Also, when "stack" is not NULL, we have already checked that the
1797  * current page is not the right half of an incomplete split, i.e. the
1798  * left sibling does not have its INCOMPLETE_SPLIT flag set, including
1799  * when the current target page is to the right of caller's initial page
1800  * (the scanblkno page).
1801  */
1802  BTStack stack = NULL;
1803 
1804  for (;;)
1805  {
1806  page = BufferGetPage(leafbuf);
1807  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1808 
1809  /*
1810  * Internal pages are never deleted directly, only as part of deleting
1811  * the whole subtree all the way down to leaf level.
1812  *
1813  * Also check for deleted pages here. Caller never passes us a fully
1814  * deleted page. Only VACUUM can delete pages, so there can't have
1815  * been a concurrent deletion. Assume that we reached any deleted
1816  * page encountered here by following a sibling link, and that the
1817  * index is corrupt.
1818  */
1819  Assert(!P_ISDELETED(opaque));
1820  if (!P_ISLEAF(opaque) || P_ISDELETED(opaque))
1821  {
1822  /*
1823  * Pre-9.4 page deletion only marked internal pages as half-dead,
1824  * but now we only use that flag on leaf pages. The old algorithm
1825  * was never supposed to leave half-dead pages in the tree, it was
1826  * just a transient state, but it was nevertheless possible in
1827  * error scenarios. We don't know how to deal with them here. They
1828  * are harmless as far as searches are considered, but inserts
1829  * into the deleted keyspace could add out-of-order downlinks in
1830  * the upper levels. Log a notice, hopefully the admin will notice
1831  * and reindex.
1832  */
1833  if (P_ISHALFDEAD(opaque))
1834  ereport(LOG,
1835  (errcode(ERRCODE_INDEX_CORRUPTED),
1836  errmsg("index \"%s\" contains a half-dead internal page",
1838  errhint("This can be caused by an interrupted VACUUM in version 9.3 or older, before upgrade. Please REINDEX it.")));
1839 
1840  if (P_ISDELETED(opaque))
1841  ereport(LOG,
1842  (errcode(ERRCODE_INDEX_CORRUPTED),
1843  errmsg_internal("found deleted block %u while following right link from block %u in index \"%s\"",
1844  BufferGetBlockNumber(leafbuf),
1845  scanblkno,
1846  RelationGetRelationName(rel))));
1847 
1848  _bt_relbuf(rel, leafbuf);
1849  return;
1850  }
1851 
1852  /*
1853  * We can never delete rightmost pages nor root pages. While at it,
1854  * check that page is empty, since it's possible that the leafbuf page
1855  * was empty a moment ago, but has since had some inserts.
1856  *
1857  * To keep the algorithm simple, we also never delete an incompletely
1858  * split page (they should be rare enough that this doesn't make any
1859  * meaningful difference to disk usage):
1860  *
1861  * The INCOMPLETE_SPLIT flag on the page tells us if the page is the
1862  * left half of an incomplete split, but ensuring that it's not the
1863  * right half is more complicated. For that, we have to check that
1864  * the left sibling doesn't have its INCOMPLETE_SPLIT flag set using
1865  * _bt_leftsib_splitflag(). On the first iteration, we temporarily
1866  * release the lock on scanblkno/leafbuf, check the left sibling, and
1867  * construct a search stack to scanblkno. On subsequent iterations,
1868  * we know we stepped right from a page that passed these tests, so
1869  * it's OK.
1870  */
1871  if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) ||
1872  P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) ||
1873  P_INCOMPLETE_SPLIT(opaque))
1874  {
1875  /* Should never fail to delete a half-dead page */
1876  Assert(!P_ISHALFDEAD(opaque));
1877 
1878  _bt_relbuf(rel, leafbuf);
1879  return;
1880  }
1881 
1882  /*
1883  * First, remove downlink pointing to the page (or a parent of the
1884  * page, if we are going to delete a taller subtree), and mark the
1885  * leafbuf page half-dead
1886  */
1887  if (!P_ISHALFDEAD(opaque))
1888  {
1889  /*
1890  * We need an approximate pointer to the page's parent page. We
1891  * use a variant of the standard search mechanism to search for
1892  * the page's high key; this will give us a link to either the
1893  * current parent or someplace to its left (if there are multiple
1894  * equal high keys, which is possible with !heapkeyspace indexes).
1895  *
1896  * Also check if this is the right-half of an incomplete split
1897  * (see comment above).
1898  */
1899  if (!stack)
1900  {
1901  BTScanInsert itup_key;
1902  ItemId itemid;
1903  IndexTuple targetkey;
1904  BlockNumber leftsib,
1905  leafblkno;
1906  Buffer sleafbuf;
1907 
1908  itemid = PageGetItemId(page, P_HIKEY);
1909  targetkey = CopyIndexTuple((IndexTuple) PageGetItem(page, itemid));
1910 
1911  leftsib = opaque->btpo_prev;
1912  leafblkno = BufferGetBlockNumber(leafbuf);
1913 
1914  /*
1915  * To avoid deadlocks, we'd better drop the leaf page lock
1916  * before going further.
1917  */
1918  _bt_unlockbuf(rel, leafbuf);
1919 
1920  /*
1921  * Check that the left sibling of leafbuf (if any) is not
1922  * marked with INCOMPLETE_SPLIT flag before proceeding
1923  */
1924  Assert(leafblkno == scanblkno);
1925  if (_bt_leftsib_splitflag(rel, leftsib, leafblkno))
1926  {
1927  ReleaseBuffer(leafbuf);
1928  return;
1929  }
1930 
1931  /* we need an insertion scan key for the search, so build one */
1932  itup_key = _bt_mkscankey(rel, targetkey);
1933  /* find the leftmost leaf page with matching pivot/high key */
1934  itup_key->pivotsearch = true;
1935  stack = _bt_search(rel, itup_key, &sleafbuf, BT_READ, NULL);
1936  /* won't need a second lock or pin on leafbuf */
1937  _bt_relbuf(rel, sleafbuf);
1938 
1939  /*
1940  * Re-lock the leaf page, and start over to use our stack
1941  * within _bt_mark_page_halfdead. We must do it that way
1942  * because it's possible that leafbuf can no longer be
1943  * deleted. We need to recheck.
1944  *
1945  * Note: We can't simply hold on to the sleafbuf lock instead,
1946  * because it's barely possible that sleafbuf is not the same
1947  * page as leafbuf. This happens when leafbuf split after our
1948  * original lock was dropped, but before _bt_search finished
1949  * its descent. We rely on the assumption that we'll find
1950  * leafbuf isn't safe to delete anymore in this scenario.
1951  * (Page deletion can cope with the stack being to the left of
1952  * leafbuf, but not to the right of leafbuf.)
1953  */
1954  _bt_lockbuf(rel, leafbuf, BT_WRITE);
1955  continue;
1956  }
1957 
1958  /*
1959  * See if it's safe to delete the leaf page, and determine how
1960  * many parent/internal pages above the leaf level will be
1961  * deleted. If it's safe then _bt_mark_page_halfdead will also
1962  * perform the first phase of deletion, which includes marking the
1963  * leafbuf page half-dead.
1964  */
1965  Assert(P_ISLEAF(opaque) && !P_IGNORE(opaque));
1966  if (!_bt_mark_page_halfdead(rel, leafbuf, stack))
1967  {
1968  _bt_relbuf(rel, leafbuf);
1969  return;
1970  }
1971  }
1972 
1973  /*
1974  * Then unlink it from its siblings. Each call to
1975  * _bt_unlink_halfdead_page unlinks the topmost page from the subtree,
1976  * making it shallower. Iterate until the leafbuf page is deleted.
1977  */
1978  rightsib_empty = false;
1979  Assert(P_ISLEAF(opaque) && P_ISHALFDEAD(opaque));
1980  while (P_ISHALFDEAD(opaque))
1981  {
1982  /* Check for interrupts in _bt_unlink_halfdead_page */
1983  if (!_bt_unlink_halfdead_page(rel, leafbuf, scanblkno,
1984  &rightsib_empty, vstate))
1985  {
1986  /*
1987  * _bt_unlink_halfdead_page should never fail, since we
1988  * established that deletion is generally safe in
1989  * _bt_mark_page_halfdead -- index must be corrupt.
1990  *
1991  * Note that _bt_unlink_halfdead_page already released the
1992  * lock and pin on leafbuf for us.
1993  */
1994  Assert(false);
1995  return;
1996  }
1997  }
1998 
1999  Assert(P_ISLEAF(opaque) && P_ISDELETED(opaque));
2000 
2001  rightsib = opaque->btpo_next;
2002 
2003  _bt_relbuf(rel, leafbuf);
2004 
2005  /*
2006  * Check here, as calling loops will have locks held, preventing
2007  * interrupts from being processed.
2008  */
2010 
2011  /*
2012  * The page has now been deleted. If its right sibling is completely
2013  * empty, it's possible that the reason we haven't deleted it earlier
2014  * is that it was the rightmost child of the parent. Now that we
2015  * removed the downlink for this page, the right sibling might now be
2016  * the only child of the parent, and could be removed. It would be
2017  * picked up by the next vacuum anyway, but might as well try to
2018  * remove it now, so loop back to process the right sibling.
2019  *
2020  * Note: This relies on the assumption that _bt_getstackbuf() will be
2021  * able to reuse our original descent stack with a different child
2022  * block (provided that the child block is to the right of the
2023  * original leaf page reached by _bt_search()). It will even update
2024  * the descent stack each time we loop around, avoiding repeated work.
2025  */
2026  if (!rightsib_empty)
2027  break;
2028 
2029  leafbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
2030  }
2031 }
static bool _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, BlockNumber scanblkno, bool *rightsib_empty, BTVacState *vstate)
Definition: nbtpage.c:2264
BlockNumber btpo_next
Definition: nbtree.h:65
int errhint(const char *fmt,...)
Definition: elog.c:1152
#define P_IGNORE(opaque)
Definition: nbtree.h:224
BTScanInsert _bt_mkscankey(Relation rel, IndexTuple itup)
Definition: nbtutils.c:90
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:832
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:350
int errcode(int sqlerrcode)
Definition: elog.c:694
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
#define P_INCOMPLETE_SPLIT(opaque)
Definition: nbtree.h:226
#define LOG
Definition: elog.h:26
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
#define BT_READ
Definition: nbtree.h:693
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:223
static bool _bt_mark_page_halfdead(Relation rel, Buffer leafbuf, BTStack stack)
Definition: nbtpage.c:2048
BlockNumber btpo_prev
Definition: nbtree.h:64
IndexTuple CopyIndexTuple(IndexTuple source)
Definition: indextuple.c:510
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
bool pivotsearch
Definition: nbtree.h:769
#define P_ISDELETED(opaque)
Definition: nbtree.h:221
#define P_ISROOT(opaque)
Definition: nbtree.h:220
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
void _bt_lockbuf(Relation rel, Buffer buf, int access)
Definition: nbtpage.c:1012
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
#define ereport(elevel,...)
Definition: elog.h:155
int errmsg_internal(const char *fmt,...)
Definition: elog.c:992
#define Assert(condition)
Definition: c.h:804
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
void _bt_unlockbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:1043
#define P_HIKEY
Definition: nbtree.h:348
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
int errmsg(const char *fmt,...)
Definition: elog.c:905
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:100
#define BT_WRITE
Definition: nbtree.h:694
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:218
BTStack _bt_search(Relation rel, BTScanInsert key, Buffer *bufP, int access, Snapshot snapshot)
Definition: nbtsearch.c:101
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
static bool _bt_leftsib_splitflag(Relation rel, BlockNumber leftsib, BlockNumber target)
Definition: nbtpage.c:1670
#define P_ISLEAF(opaque)
Definition: nbtree.h:219

◆ _bt_pageinit()

void _bt_pageinit ( Page  page,
Size  size 
)

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

1104 {
1105  PageInit(page, size, sizeof(BTPageOpaqueData));
1106 }
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 976 of file nbtpage.c.

References _bt_checkpage(), _bt_lockbuf(), _bt_unlockbuf(), Assert, buf, BufferIsValid, 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().

977 {
978  Buffer buf;
979 
980  Assert(blkno != P_NEW);
981  if (BufferIsValid(obuf))
982  _bt_unlockbuf(rel, obuf);
983  buf = ReleaseAndReadBuffer(obuf, rel, blkno);
984  _bt_lockbuf(rel, buf, access);
985 
986  _bt_checkpage(rel, buf);
987  return buf;
988 }
#define P_NEW
Definition: bufmgr.h:91
void _bt_checkpage(Relation rel, Buffer buf)
Definition: nbtpage.c:755
static char * buf
Definition: pg_test_fsync.c:68
void _bt_lockbuf(Relation rel, Buffer buf, int access)
Definition: nbtpage.c:1012
#define Assert(condition)
Definition: c.h:804
Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation, BlockNumber blockNum)
Definition: bufmgr.c:1546
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
void _bt_unlockbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:1043
int Buffer
Definition: buf.h:23

◆ _bt_relbuf()

◆ _bt_rightsib_halfdeadflag()

static bool _bt_rightsib_halfdeadflag ( Relation  rel,
BlockNumber  leafrightsib 
)
static

Definition at line 1727 of file nbtpage.c.

References _bt_getbuf(), _bt_relbuf(), Assert, BT_READ, buf, BufferGetPage, P_ISDELETED, P_ISHALFDEAD, P_ISLEAF, P_NONE, and PageGetSpecialPointer.

Referenced by _bt_mark_page_halfdead().

1728 {
1729  Buffer buf;
1730  Page page;
1731  BTPageOpaque opaque;
1732  bool result;
1733 
1734  Assert(leafrightsib != P_NONE);
1735 
1736  buf = _bt_getbuf(rel, leafrightsib, BT_READ);
1737  page = BufferGetPage(buf);
1738  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
1739 
1740  Assert(P_ISLEAF(opaque) && !P_ISDELETED(opaque));
1741  result = P_ISHALFDEAD(opaque);
1742  _bt_relbuf(rel, buf);
1743 
1744  return result;
1745 }
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:832
#define P_NONE
Definition: nbtree.h:211
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
#define BT_READ
Definition: nbtree.h:693
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:223
static char * buf
Definition: pg_test_fsync.c:68
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
#define P_ISDELETED(opaque)
Definition: nbtree.h:221
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
#define Assert(condition)
Definition: c.h:804
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
int Buffer
Definition: buf.h:23
Pointer Page
Definition: bufpage.h:78
#define P_ISLEAF(opaque)
Definition: nbtree.h:219

◆ _bt_set_cleanup_info()

void _bt_set_cleanup_info ( Relation  rel,
BlockNumber  num_delpages,
float8  num_heap_tuples 
)

Definition at line 190 of file nbtpage.c.

References _bt_getbuf(), _bt_lockbuf(), _bt_relbuf(), _bt_unlockbuf(), _bt_upgrademetapage(), xl_btree_metadata::allequalimage, Assert, BT_READ, BT_WRITE, BTMetaPageData::btm_allequalimage, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_last_cleanup_num_delpages, BTMetaPageData::btm_last_cleanup_num_heap_tuples, BTMetaPageData::btm_level, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTPageGetMeta, BTREE_METAPAGE, BTREE_NOVAC_VERSION, BufferGetPage, END_CRIT_SECTION, xl_btree_metadata::fastlevel, xl_btree_metadata::fastroot, xl_btree_metadata::last_cleanup_num_delpages, xl_btree_metadata::last_cleanup_num_heap_tuples, xl_btree_metadata::level, MarkBufferDirty(), 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 btvacuumcleanup().

192 {
193  Buffer metabuf;
194  Page metapg;
195  BTMetaPageData *metad;
196  bool rewrite = false;
197  XLogRecPtr recptr;
198 
199  /*
200  * On-disk compatibility note: The btm_last_cleanup_num_delpages metapage
201  * field started out as a TransactionId field called btm_oldest_btpo_xact.
202  * Both "versions" are just uint32 fields. It was convenient to repurpose
203  * the field when we began to use 64-bit XIDs in deleted pages.
204  *
205  * It's possible that a pg_upgrade'd database will contain an XID value in
206  * what is now recognized as the metapage's btm_last_cleanup_num_delpages
207  * field. _bt_vacuum_needs_cleanup() may even believe that this value
208  * indicates that there are lots of pages that it needs to recycle, when
209  * in reality there are only one or two. The worst that can happen is
210  * that there will be a call to btvacuumscan a little earlier, which will
211  * set btm_last_cleanup_num_delpages to a sane value when we're called.
212  */
213  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
214  metapg = BufferGetPage(metabuf);
215  metad = BTPageGetMeta(metapg);
216 
217  /* Always dynamically upgrade index/metapage when BTREE_MIN_VERSION */
218  if (metad->btm_version < BTREE_NOVAC_VERSION)
219  rewrite = true;
220  else if (metad->btm_last_cleanup_num_delpages != num_delpages)
221  rewrite = true;
222  else if (metad->btm_last_cleanup_num_heap_tuples != num_heap_tuples)
223  rewrite = true;
224 
225  if (!rewrite)
226  {
227  _bt_relbuf(rel, metabuf);
228  return;
229  }
230 
231  /* trade in our read lock for a write lock */
232  _bt_unlockbuf(rel, metabuf);
233  _bt_lockbuf(rel, metabuf, BT_WRITE);
234 
236 
237  /* upgrade meta-page if needed */
238  if (metad->btm_version < BTREE_NOVAC_VERSION)
239  _bt_upgrademetapage(metapg);
240 
241  /* update cleanup-related information */
242  metad->btm_last_cleanup_num_delpages = num_delpages;
243  metad->btm_last_cleanup_num_heap_tuples = num_heap_tuples;
244  MarkBufferDirty(metabuf);
245 
246  /* write wal record if needed */
247  if (RelationNeedsWAL(rel))
248  {
250 
251  XLogBeginInsert();
253 
255  md.version = metad->btm_version;
256  md.root = metad->btm_root;
257  md.level = metad->btm_level;
258  md.fastroot = metad->btm_fastroot;
259  md.fastlevel = metad->btm_fastlevel;
260  md.last_cleanup_num_delpages = num_delpages;
261  md.last_cleanup_num_heap_tuples = num_heap_tuples;
262  md.allequalimage = metad->btm_allequalimage;
263 
264  XLogRegisterBufData(0, (char *) &md, sizeof(xl_btree_metadata));
265 
266  recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_META_CLEANUP);
267 
268  PageSetLSN(metapg, recptr);
269  }
270 
272 
273  _bt_relbuf(rel, metabuf);
274 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
bool allequalimage
Definition: nbtxlog.h:58
uint32 last_cleanup_num_delpages
Definition: nbtxlog.h:56
void _bt_upgrademetapage(Page page)
Definition: nbtpage.c:105
uint32 btm_version
Definition: nbtree.h:104
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:832
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
BlockNumber root
Definition: nbtxlog.h:52
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
#define BT_READ
Definition: nbtree.h:693
BlockNumber btm_fastroot
Definition: nbtree.h:107
float8 last_cleanup_num_heap_tuples
Definition: nbtxlog.h:57
#define BTPageGetMeta(p)
Definition: nbtree.h:119
bool btm_allequalimage
Definition: nbtree.h:116
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:150
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
uint32 btm_last_cleanup_num_delpages
Definition: nbtree.h:112
#define BTREE_METAPAGE
Definition: nbtree.h:146
uint32 version
Definition: nbtxlog.h:51
uint32 btm_fastlevel
Definition: nbtree.h:108
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
void _bt_lockbuf(Relation rel, Buffer buf, int access)
Definition: nbtpage.c:1012
BlockNumber btm_root
Definition: nbtree.h:105
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:114
#define RelationNeedsWAL(relation)
Definition: rel.h:563
void _bt_unlockbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:1043
uint32 fastlevel
Definition: nbtxlog.h:55
uint32 btm_level
Definition: nbtree.h:106
uint32 level
Definition: nbtxlog.h:53
BlockNumber fastroot
Definition: nbtxlog.h:54
#define BT_WRITE
Definition: nbtree.h:694
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define XLOG_BTREE_META_CLEANUP
Definition: nbtxlog.h:43
Pointer Page
Definition: bufpage.h:78

◆ _bt_unlink_halfdead_page()

static bool _bt_unlink_halfdead_page ( Relation  rel,
Buffer  leafbuf,
BlockNumber  scanblkno,
bool rightsib_empty,
BTVacState vstate 
)
static

Definition at line 2264 of file nbtpage.c.

References _bt_getbuf(), _bt_lockbuf(), _bt_relbuf(), _bt_unlockbuf(), _bt_upgrademetapage(), xl_btree_metadata::allequalimage, Assert, BlockNumberIsValid, BT_READ, BT_WRITE, BTMetaPageData::btm_allequalimage, BTMetaPageData::btm_fastlevel, BTMetaPageData::btm_fastroot, BTMetaPageData::btm_last_cleanup_num_delpages, BTMetaPageData::btm_last_cleanup_num_heap_tuples, BTMetaPageData::btm_level, BTMetaPageData::btm_root, BTMetaPageData::btm_version, BTPageGetMeta, BTPageSetDeleted(), BTPageOpaqueData::btpo_cycleid, BTPageOpaqueData::btpo_level, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BTREE_METAPAGE, BTREE_NOVAC_VERSION, BTreeTupleGetDownLink(), BTreeTupleGetTopParent(), BTreeTupleSetTopParent(), buf, 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_delpages, xl_btree_metadata::last_cleanup_num_heap_tuples, xl_btree_unlink_page::leafleftsib, xl_btree_unlink_page::leafrightsib, xl_btree_unlink_page::leaftopparent, xl_btree_unlink_page::leftsib, xl_btree_metadata::level, xl_btree_unlink_page::level, LOG, MarkBufferDirty(), P_FIRSTDATAKEY, P_HIKEY, P_ISDELETED, P_ISHALFDEAD, P_ISLEAF, P_ISROOT, P_NONE, P_RIGHTMOST, PageGetItem, PageGetItemId, PageGetMaxOffsetNumber, PageGetSpecialPointer, IndexBulkDeleteResult::pages_deleted, IndexBulkDeleteResult::pages_newly_deleted, PageSetLSN, ReadNextFullTransactionId(), REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetRelationName, RelationNeedsWAL, ReleaseBuffer(), xl_btree_unlink_page::rightsib, xl_btree_metadata::root, xl_btree_unlink_page::safexid, SizeOfBtreeUnlinkPage, START_CRIT_SECTION, BTVacState::stats, xl_btree_metadata::version, XLOG_BTREE_UNLINK_PAGE, XLOG_BTREE_UNLINK_PAGE_META, XLogBeginInsert(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_pagedel().

2266 {
2267  BlockNumber leafblkno = BufferGetBlockNumber(leafbuf);
2268  IndexBulkDeleteResult *stats = vstate->stats;
2269  BlockNumber leafleftsib;
2270  BlockNumber leafrightsib;
2271  BlockNumber target;
2272  BlockNumber leftsib;
2273  BlockNumber rightsib;
2274  Buffer lbuf = InvalidBuffer;
2275  Buffer buf;
2276  Buffer rbuf;
2277  Buffer metabuf = InvalidBuffer;
2278  Page metapg = NULL;
2279  BTMetaPageData *metad = NULL;
2280  ItemId itemid;
2281  Page page;
2282  BTPageOpaque opaque;
2283  FullTransactionId safexid;
2284  bool rightsib_is_rightmost;
2285  uint32 targetlevel;
2286  IndexTuple leafhikey;
2287  BlockNumber leaftopparent;
2288 
2289  page = BufferGetPage(leafbuf);
2290  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2291 
2292  Assert(P_ISLEAF(opaque) && !P_ISDELETED(opaque) && P_ISHALFDEAD(opaque));
2293 
2294  /*
2295  * Remember some information about the leaf page.
2296  */
2297  itemid = PageGetItemId(page, P_HIKEY);
2298  leafhikey = (IndexTuple) PageGetItem(page, itemid);
2299  target = BTreeTupleGetTopParent(leafhikey);
2300  leafleftsib = opaque->btpo_prev;
2301  leafrightsib = opaque->btpo_next;
2302 
2303  _bt_unlockbuf(rel, leafbuf);
2304 
2305  /*
2306  * Check here, as calling loops will have locks held, preventing
2307  * interrupts from being processed.
2308  */
2310 
2311  /* Unlink the current top parent of the subtree */
2312  if (!BlockNumberIsValid(target))
2313  {
2314  /* Target is leaf page (or leaf page is top parent, if you prefer) */
2315  target = leafblkno;
2316 
2317  buf = leafbuf;
2318  leftsib = leafleftsib;
2319  targetlevel = 0;
2320  }
2321  else
2322  {
2323  /* Target is the internal page taken from leaf's top parent link */
2324  Assert(target != leafblkno);
2325 
2326  /* Fetch the block number of the target's left sibling */
2327  buf = _bt_getbuf(rel, target, BT_READ);
2328  page = BufferGetPage(buf);
2329  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2330  leftsib = opaque->btpo_prev;
2331  targetlevel = opaque->btpo_level;
2332  Assert(targetlevel > 0);
2333 
2334  /*
2335  * To avoid deadlocks, we'd better drop the target page lock before
2336  * going further.
2337  */
2338  _bt_unlockbuf(rel, buf);
2339  }
2340 
2341  /*
2342  * We have to lock the pages we need to modify in the standard order:
2343  * moving right, then up. Else we will deadlock against other writers.
2344  *
2345  * So, first lock the leaf page, if it's not the target. Then find and
2346  * write-lock the current left sibling of the target page. The sibling
2347  * that was current a moment ago could have split, so we may have to move
2348  * right.
2349  */
2350  if (target != leafblkno)
2351  _bt_lockbuf(rel, leafbuf, BT_WRITE);
2352  if (leftsib != P_NONE)
2353  {
2354  lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
2355  page = BufferGetPage(lbuf);
2356  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2357  while (P_ISDELETED(opaque) || opaque->btpo_next != target)
2358  {
2359  bool leftsibvalid = true;
2360 
2361  /*
2362  * Before we follow the link from the page that was the left
2363  * sibling mere moments ago, validate its right link. This
2364  * reduces the opportunities for loop to fail to ever make any
2365  * progress in the presence of index corruption.
2366  *
2367  * Note: we rely on the assumption that there can only be one
2368  * vacuum process running at a time (against the same index).
2369  */
2370  if (P_RIGHTMOST(opaque) || P_ISDELETED(opaque) ||
2371  leftsib == opaque->btpo_next)
2372  leftsibvalid = false;
2373 
2374  leftsib = opaque->btpo_next;
2375  _bt_relbuf(rel, lbuf);
2376 
2377  if (!leftsibvalid)
2378  {
2379  if (target != leafblkno)
2380  {
2381  /* we have only a pin on target, but pin+lock on leafbuf */
2382  ReleaseBuffer(buf);
2383  _bt_relbuf(rel, leafbuf);
2384  }
2385  else
2386  {
2387  /* we have only a pin on leafbuf */
2388  ReleaseBuffer(leafbuf);
2389  }
2390 
2391  ereport(LOG,
2392  (errcode(ERRCODE_INDEX_CORRUPTED),
2393  errmsg_internal("valid left sibling for deletion target could not be located: "
2394  "left sibling %u of target %u with leafblkno %u and scanblkno %u in index \"%s\"",
2395  leftsib, target, leafblkno, scanblkno,
2396  RelationGetRelationName(rel))));
2397 
2398  return false;
2399  }
2400 
2402 
2403  /* step right one page */
2404  lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
2405  page = BufferGetPage(lbuf);
2406  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2407  }
2408  }
2409  else
2410  lbuf = InvalidBuffer;
2411 
2412  /* Next write-lock the target page itself */
2413  _bt_lockbuf(rel, buf, BT_WRITE);
2414  page = BufferGetPage(buf);
2415  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2416 
2417  /*
2418  * Check page is still empty etc, else abandon deletion. This is just for
2419  * paranoia's sake; a half-dead page cannot resurrect because there can be
2420  * only one vacuum process running at a time.
2421  */
2422  if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) || P_ISDELETED(opaque))
2423  elog(ERROR, "target page changed status unexpectedly in block %u of index \"%s\"",
2424  target, RelationGetRelationName(rel));
2425 
2426  if (opaque->btpo_prev != leftsib)
2427  ereport(ERROR,
2428  (errcode(ERRCODE_INDEX_CORRUPTED),
2429  errmsg_internal("target page left link unexpectedly changed from %u to %u in block %u of index \"%s\"",
2430  leftsib, opaque->btpo_prev, target,
2431  RelationGetRelationName(rel))));
2432 
2433  if (target == leafblkno)
2434  {
2435  if (P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) ||
2436  !P_ISLEAF(opaque) || !P_ISHALFDEAD(opaque))
2437  elog(ERROR, "target leaf page changed status unexpectedly in block %u of index \"%s\"",
2438  target, RelationGetRelationName(rel));
2439 
2440  /* Leaf page is also target page: don't set leaftopparent */
2441  leaftopparent = InvalidBlockNumber;
2442  }
2443  else
2444  {
2445  IndexTuple finaldataitem;
2446 
2447  if (P_FIRSTDATAKEY(opaque) != PageGetMaxOffsetNumber(page) ||
2448  P_ISLEAF(opaque))
2449  elog(ERROR, "target internal page on level %u changed status unexpectedly in block %u of index \"%s\"",
2450  targetlevel, target, RelationGetRelationName(rel));
2451 
2452  /* Target is internal: set leaftopparent for next call here... */
2453  itemid = PageGetItemId(page, P_FIRSTDATAKEY(opaque));
2454  finaldataitem = (IndexTuple) PageGetItem(page, itemid);
2455  leaftopparent = BTreeTupleGetDownLink(finaldataitem);
2456  /* ...except when it would be a redundant pointer-to-self */
2457  if (leaftopparent == leafblkno)
2458  leaftopparent = InvalidBlockNumber;
2459  }
2460 
2461  /* No leaftopparent for level 0 (leaf page) or level 1 target */
2462  Assert(!BlockNumberIsValid(leaftopparent) || targetlevel > 1);
2463 
2464  /*
2465  * And next write-lock the (current) right sibling.
2466  */
2467  rightsib = opaque->btpo_next;
2468  rbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
2469  page = BufferGetPage(rbuf);
2470  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2471  if (opaque->btpo_prev != target)
2472  ereport(ERROR,
2473  (errcode(ERRCODE_INDEX_CORRUPTED),
2474  errmsg_internal("right sibling's left-link doesn't match: "
2475  "block %u links to %u instead of expected %u in index \"%s\"",
2476  rightsib, opaque->btpo_prev, target,
2477  RelationGetRelationName(rel))));
2478  rightsib_is_rightmost = P_RIGHTMOST(opaque);
2479  *rightsib_empty = (P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
2480 
2481  /*
2482  * If we are deleting the next-to-last page on the target's level, then
2483  * the rightsib is a candidate to become the new fast root. (In theory, it
2484  * might be possible to push the fast root even further down, but the odds
2485  * of doing so are slim, and the locking considerations daunting.)
2486  *
2487  * We can safely acquire a lock on the metapage here --- see comments for
2488  * _bt_newroot().
2489  */
2490  if (leftsib == P_NONE && rightsib_is_rightmost)
2491  {
2492  page = BufferGetPage(rbuf);
2493  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2494  if (P_RIGHTMOST(opaque))
2495  {
2496  /* rightsib will be the only one left on the level */
2497  metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
2498  metapg = BufferGetPage(metabuf);
2499  metad = BTPageGetMeta(metapg);
2500 
2501  /*
2502  * The expected case here is btm_fastlevel == targetlevel+1; if
2503  * the fastlevel is <= targetlevel, something is wrong, and we
2504  * choose to overwrite it to fix it.
2505  */
2506  if (metad->btm_fastlevel > targetlevel + 1)
2507  {
2508  /* no update wanted */
2509  _bt_relbuf(rel, metabuf);
2510  metabuf = InvalidBuffer;
2511  }
2512  }
2513  }
2514 
2515  /*
2516  * Here we begin doing the deletion.
2517  */
2518 
2519  /* No ereport(ERROR) until changes are logged */
2521 
2522  /*
2523  * Update siblings' side-links. Note the target page's side-links will
2524  * continue to point to the siblings. Asserts here are just rechecking
2525  * things we already verified above.
2526  */
2527  if (BufferIsValid(lbuf))
2528  {
2529  page = BufferGetPage(lbuf);
2530  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2531  Assert(opaque->btpo_next == target);
2532  opaque->btpo_next = rightsib;
2533  }
2534  page = BufferGetPage(rbuf);
2535  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2536  Assert(opaque->btpo_prev == target);
2537  opaque->btpo_prev = leftsib;
2538 
2539  /*
2540  * If we deleted a parent of the targeted leaf page, instead of the leaf
2541  * itself, update the leaf to point to the next remaining child in the
2542  * subtree.
2543  *
2544  * Note: We rely on the fact that a buffer pin on the leaf page has been
2545  * held since leafhikey was initialized. This is safe, though only
2546  * because the page was already half-dead at that point. The leaf page
2547  * cannot have been modified by any other backend during the period when
2548  * no lock was held.
2549  */
2550  if (target != leafblkno)
2551  BTreeTupleSetTopParent(leafhikey, leaftopparent);
2552 
2553  /*
2554  * Mark the page itself deleted. It can be recycled when all current
2555  * transactions are gone. Storing GetTopTransactionId() would work, but
2556  * we're in VACUUM and would not otherwise have an XID. Having already
2557  * updated links to the target, ReadNextFullTransactionId() suffices as an
2558  * upper bound. Any scan having retained a now-stale link is advertising
2559  * in its PGPROC an xmin less than or equal to the value we read here. It
2560  * will continue to do so, holding back the xmin horizon, for the duration
2561  * of that scan.
2562  */
2563  page = BufferGetPage(buf);
2564  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
2565  Assert(P_ISHALFDEAD(opaque) || !P_ISLEAF(opaque));
2566 
2567  /*
2568  * Store upper bound XID that's used to determine when deleted page is no
2569  * longer needed as a tombstone
2570  */
2571  safexid = ReadNextFullTransactionId();
2572  BTPageSetDeleted(page, safexid);
2573  opaque->btpo_cycleid = 0;
2574 
2575  /* And update the metapage, if needed */
2576  if (BufferIsValid(metabuf))
2577  {
2578  /* upgrade metapage if needed */
2579  if (metad->btm_version < BTREE_NOVAC_VERSION)
2580  _bt_upgrademetapage(metapg);
2581  metad->btm_fastroot = rightsib;
2582  metad->btm_fastlevel = targetlevel;
2583  MarkBufferDirty(metabuf);
2584  }
2585 
2586  /* Must mark buffers dirty before XLogInsert */
2587  MarkBufferDirty(rbuf);
2588  MarkBufferDirty(buf);
2589  if (BufferIsValid(lbuf))
2590  MarkBufferDirty(lbuf);
2591  if (target != leafblkno)
2592  MarkBufferDirty(leafbuf);
2593 
2594  /* XLOG stuff */
2595  if (RelationNeedsWAL(rel))
2596  {
2597  xl_btree_unlink_page xlrec;
2598  xl_btree_metadata xlmeta;
2599  uint8 xlinfo;
2600  XLogRecPtr recptr;
2601 
2602  XLogBeginInsert();
2603 
2605  if (BufferIsValid(lbuf))
2608  if (target != leafblkno)
2609  XLogRegisterBuffer(3, leafbuf, REGBUF_WILL_INIT);
2610 
2611  /* information stored on the target/to-be-unlinked block */
2612  xlrec.leftsib = leftsib;
2613  xlrec.rightsib = rightsib;
2614  xlrec.level = targetlevel;
2615  xlrec.safexid = safexid;
2616 
2617  /* information needed to recreate the leaf block (if not the target) */
2618  xlrec.leafleftsib = leafleftsib;
2619  xlrec.leafrightsib = leafrightsib;
2620  xlrec.leaftopparent = leaftopparent;
2621 
2622  XLogRegisterData((char *) &xlrec, SizeOfBtreeUnlinkPage);
2623 
2624  if (BufferIsValid(metabuf))
2625  {
2627 
2629  xlmeta.version = metad->btm_version;
2630  xlmeta.root = metad->btm_root;
2631  xlmeta.level = metad->btm_level;
2632  xlmeta.fastroot = metad->btm_fastroot;
2633  xlmeta.fastlevel = metad->btm_fastlevel;
2636  xlmeta.allequalimage = metad->btm_allequalimage;
2637 
2638  XLogRegisterBufData(4, (char *) &xlmeta, sizeof(xl_btree_metadata));
2639  xlinfo = XLOG_BTREE_UNLINK_PAGE_META;
2640  }
2641  else
2642  xlinfo = XLOG_BTREE_UNLINK_PAGE;
2643 
2644  recptr = XLogInsert(RM_BTREE_ID, xlinfo);
2645 
2646  if (BufferIsValid(metabuf))
2647  {
2648  PageSetLSN(metapg, recptr);
2649  }
2650  page = BufferGetPage(rbuf);
2651  PageSetLSN(page, recptr);
2652  page = BufferGetPage(buf);
2653  PageSetLSN(page, recptr);
2654  if (BufferIsValid(lbuf))
2655  {
2656  page = BufferGetPage(lbuf);
2657  PageSetLSN(page, recptr);
2658  }
2659  if (target != leafblkno)
2660  {
2661  page = BufferGetPage(leafbuf);
2662  PageSetLSN(page, recptr);
2663  }
2664  }
2665 
2666  END_CRIT_SECTION();
2667 
2668  /* release metapage */
2669  if (BufferIsValid(metabuf))
2670  _bt_relbuf(rel, metabuf);
2671 
2672  /* release siblings */
2673  if (BufferIsValid(lbuf))
2674  _bt_relbuf(rel, lbuf);
2675  _bt_relbuf(rel, rbuf);
2676 
2677  /* If the target is not leafbuf, we're done with it now -- release it */
2678  if (target != leafblkno)
2679  _bt_relbuf(rel, buf);
2680 
2681  /*
2682  * Maintain pages_newly_deleted, which is simply the number of pages
2683  * deleted by the ongoing VACUUM operation.
2684  *
2685  * Maintain pages_deleted in a way that takes into account how
2686  * btvacuumpage() will count deleted pages that have yet to become
2687  * scanblkno -- only count page when it's not going to get that treatment
2688  * later on.
2689  */
2690  stats->pages_newly_deleted++;
2691  if (target <= scanblkno)
2692  stats->pages_deleted++;
2693 
2694  return true;
2695 }
void XLogRegisterBufData(uint8 block_id, char *data, int len)
Definition: xloginsert.c:368
bool allequalimage
Definition: nbtxlog.h:58
BlockNumber btpo_next
Definition: nbtree.h:65
uint32 last_cleanup_num_delpages
Definition: nbtxlog.h:56
void _bt_upgrademetapage(Page page)
Definition: nbtpage.c:105
uint32 btm_version
Definition: nbtree.h:104
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition: nbtpage.c:832
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1483
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition: xloginsert.c:220
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:350
#define END_CRIT_SECTION()
Definition: miscadmin.h:135
BlockNumber root
Definition: nbtxlog.h:52
unsigned char uint8
Definition: c.h:439
#define P_NONE
Definition: nbtree.h:211
static BlockNumber BTreeTupleGetDownLink(IndexTuple pivot)
Definition: nbtree.h:530
#define InvalidBuffer
Definition: buf.h:25
#define REGBUF_WILL_INIT
Definition: xloginsert.h:33
#define START_CRIT_SECTION()
Definition: miscadmin.h:133
int errcode(int sqlerrcode)
Definition: elog.c:694
uint32 BlockNumber
Definition: block.h:31
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3700
#define LOG
Definition: elog.h:26
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
#define BT_READ
Definition: nbtree.h:693
BlockNumber btm_fastroot
Definition: nbtree.h:107
#define P_ISHALFDEAD(opaque)
Definition: nbtree.h:223
#define ERROR
Definition: elog.h:45
float8 last_cleanup_num_heap_tuples
Definition: nbtxlog.h:57
BTCycleId btpo_cycleid
Definition: nbtree.h:68
#define BTPageGetMeta(p)
Definition: nbtree.h:119
bool btm_allequalimage
Definition: nbtree.h:116
BlockNumber btpo_prev
Definition: nbtree.h:64
uint32 btpo_level
Definition: nbtree.h:66
static char * buf
Definition: pg_test_fsync.c:68
IndexTupleData * IndexTuple
Definition: itup.h:53
#define REGBUF_STANDARD
Definition: xloginsert.h:35
#define RelationGetRelationName(relation)
Definition: rel.h:491
#define XLOG_BTREE_UNLINK_PAGE
Definition: nbtxlog.h:35
unsigned int uint32
Definition: c.h:441
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:150
BlockNumber pages_deleted
Definition: genam.h:81
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
uint32 btm_last_cleanup_num_delpages
Definition: nbtree.h:112
#define BTREE_METAPAGE
Definition: nbtree.h:146
#define P_ISDELETED(opaque)
Definition: nbtree.h:221
#define P_ISROOT(opaque)
Definition: nbtree.h:220
FullTransactionId ReadNextFullTransactionId(void)
Definition: varsup.c:261
uint32 version
Definition: nbtxlog.h:51
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
uint32 btm_fastlevel
Definition: nbtree.h:108
void XLogRegisterData(char *data, int len)
Definition: xloginsert.c:330
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:422
void _bt_lockbuf(Relation rel, Buffer buf, int access)
Definition: nbtpage.c:1012
BlockNumber btm_root
Definition: nbtree.h:105
void _bt_relbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:996
#define ereport(elevel,...)
Definition: elog.h:155
#define BlockNumberIsValid(blockNumber)
Definition: block.h:70
static void BTPageSetDeleted(Page page, FullTransactionId safexid)
Definition: nbtree.h:238
int errmsg_internal(const char *fmt,...)
Definition: elog.c:992
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:804
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define InvalidBlockNumber
Definition: block.h:33
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:114
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
IndexBulkDeleteResult * stats
Definition: nbtree.h:322
#define RelationNeedsWAL(relation)
Definition: rel.h:563
void _bt_unlockbuf(Relation rel, Buffer buf)
Definition: nbtpage.c:1043
#define P_HIKEY
Definition: nbtree.h:348
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2674
uint32 fastlevel
Definition: nbtxlog.h:55
BlockNumber pages_newly_deleted
Definition: genam.h:80
uint32 btm_level
Definition: nbtree.h:106
#define elog(elevel,...)
Definition: elog.h:227
uint32 level
Definition: nbtxlog.h:53
#define SizeOfBtreeUnlinkPage
Definition: nbtxlog.h:320
static BlockNumber BTreeTupleGetTopParent(IndexTuple leafhikey)
Definition: nbtree.h:594
BlockNumber fastroot
Definition: nbtxlog.h:54
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:100
#define XLOG_BTREE_UNLINK_PAGE_META
Definition: nbtxlog.h:36
#define BT_WRITE
Definition: nbtree.h:694
void XLogBeginInsert(void)
Definition: xloginsert.c:123
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:218
static void BTreeTupleSetTopParent(IndexTuple leafhikey, BlockNumber blkno)
Definition: nbtree.h:600
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define P_ISLEAF(opaque)
Definition: nbtree.h:219

◆ _bt_unlockbuf()

void _bt_unlockbuf ( Relation  rel,
Buffer  buf 
)

Definition at line 1043 of file nbtpage.c.

References BUFFER_LOCK_UNLOCK, BufferGetPage, LockBuffer(), RelationUsesLocalBuffers, VALGRIND_CHECK_MEM_IS_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

Referenced by _bt_drop_lock_and_maybe_pin(), _bt_endpoint(), _bt_first(), _bt_getroot(), _bt_killitems(), _bt_moveright(), _bt_pagedel(), _bt_relandgetbuf(), _bt_relbuf(), _bt_search(), _bt_set_cleanup_info(), and _bt_unlink_halfdead_page().

1044 {
1045  /*
1046  * Buffer is pinned and locked, which means that it is expected to be
1047  * defined and addressable. Check that proactively.
1048  */
1050 
1051  /* LockBuffer() asserts that pin is held by this backend */
1053 
1054  if (!RelationUsesLocalBuffers(rel))
1056 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
Definition: memdebug.h:27
#define VALGRIND_CHECK_MEM_IS_DEFINED(addr, size)
Definition: memdebug.h:23
static char * buf
Definition: pg_test_fsync.c:68
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939
#define RelationUsesLocalBuffers(relation)
Definition: rel.h:573

◆ _bt_upgradelockbufcleanup()

void _bt_upgradelockbufcleanup ( Relation  rel,
Buffer  buf 
)

Definition at line 1083 of file nbtpage.c.

References BUFFER_LOCK_UNLOCK, BufferGetPage, LockBuffer(), LockBufferForCleanup(), and VALGRIND_CHECK_MEM_IS_DEFINED.

Referenced by btvacuumpage().

1084 {
1085  /*
1086  * Buffer is pinned and locked, which means that it is expected to be
1087  * defined and addressable. Check that proactively.
1088  */
1090 
1091  /* LockBuffer() asserts that pin is held by this backend */
1094 }
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:96
void LockBufferForCleanup(Buffer buffer)
Definition: bufmgr.c:3996
#define VALGRIND_CHECK_MEM_IS_DEFINED(addr, size)
Definition: memdebug.h:23
static char * buf
Definition: pg_test_fsync.c:68
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3939

◆ _bt_upgrademetapage()

void _bt_upgrademetapage ( Page  page)

Definition at line 105 of file nbtpage.c.

References Assert, BTMetaPageData::btm_allequalimage, BTMetaPageData::btm_last_cleanup_num_delpages, BTMetaPageData::btm_last_cleanup_num_heap_tuples, BTMetaPageData::btm_version, BTP_META, BTPageGetMeta, BTREE_MIN_VERSION, BTREE_NOVAC_VERSION, PageGetSpecialPointer, and PG_USED_FOR_ASSERTS_ONLY.

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

106 {
107  BTMetaPageData *metad;
109 
110  metad = BTPageGetMeta(page);
111  metaopaque = (BTPageOpaque) PageGetSpecialPointer(page);
112 
113  /* It must be really a meta page of upgradable version */
114  Assert(metaopaque->btpo_flags & BTP_META);
117 
118  /* Set version number and fill extra fields added into version 3 */
121  metad->btm_last_cleanup_num_heap_tuples = -1.0;
122  /* Only a REINDEX can set this field */
123  Assert(!metad->btm_allequalimage);
124  metad->btm_allequalimage = false;
125 
126  /* Adjust pd_lower (see _bt_initmetapage() for details) */
127  ((PageHeader) page)->pd_lower =
128  ((char *) metad + sizeof(BTMetaPageData)) - (char *) page;
129 }
uint32 btm_version
Definition: nbtree.h:104
#define BTP_META
Definition: nbtree.h:77
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:71
#define BTPageGetMeta(p)
Definition: nbtree.h:119
bool btm_allequalimage
Definition: nbtree.h:116
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:150
uint32 btm_last_cleanup_num_delpages
Definition: nbtree.h:112
#define BTREE_MIN_VERSION
Definition: nbtree.h:149
PageHeaderData * PageHeader
Definition: bufpage.h:166
#define Assert(condition)
Definition: c.h:804
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:114
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:155