PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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 "common/int.h"
#include "miscadmin.h"
#include "storage/indexfsm.h"
#include "storage/predicate.h"
#include "storage/procarray.h"
#include "utils/injection_point.h"
#include "utils/memdebug.h"
#include "utils/memutils.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_delitems_delete (Relation rel, Buffer buf, TransactionId snapshotConflictHorizon, bool isCatalogRel, 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, Relation heaprel, 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, Relation heaprel, BlockNumber child, BTStack stack, Buffer *subtreeparent, OffsetNumber *poffset, BlockNumber *topparent, BlockNumber *topparentrightsib)
 
static void _bt_pendingfsm_add (BTVacState *vstate, BlockNumber target, FullTransactionId safexid)
 
void _bt_initmetapage (Page page, BlockNumber rootbknum, uint32 level, bool allequalimage)
 
void _bt_upgrademetapage (Page page)
 
bool _bt_vacuum_needs_cleanup (Relation rel)
 
void _bt_set_cleanup_info (Relation rel, BlockNumber num_delpages)
 
Buffer _bt_getroot (Relation rel, Relation heaprel, 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_allocbuf (Relation rel, Relation heaprel)
 
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)
 
void _bt_pendingfsm_init (Relation rel, BTVacState *vstate, bool cleanuponly)
 
void _bt_pendingfsm_finalize (Relation rel, BTVacState *vstate)
 

Function Documentation

◆ _bt_allocbuf()

Buffer _bt_allocbuf ( Relation  rel,
Relation  heaprel 
)

Definition at line 854 of file nbtpage.c.

855{
856 Buffer buf;
857 BlockNumber blkno;
858 Page page;
859
860 Assert(heaprel != NULL);
861
862 /*
863 * First see if the FSM knows of any free pages.
864 *
865 * We can't trust the FSM's report unreservedly; we have to check that the
866 * page is still free. (For example, an already-free page could have been
867 * re-used between the time the last VACUUM scanned it and the time the
868 * VACUUM made its FSM updates.)
869 *
870 * In fact, it's worse than that: we can't even assume that it's safe to
871 * take a lock on the reported page. If somebody else has a lock on it,
872 * or even worse our own caller does, we could deadlock. (The own-caller
873 * scenario is actually not improbable. Consider an index on a serial or
874 * timestamp column. Nearly all splits will be at the rightmost page, so
875 * it's entirely likely that _bt_split will call us while holding a lock
876 * on the page most recently acquired from FSM. A VACUUM running
877 * concurrently with the previous split could well have placed that page
878 * back in FSM.)
879 *
880 * To get around that, we ask for only a conditional lock on the reported
881 * page. If we fail, then someone else is using the page, and we may
882 * reasonably assume it's not free. (If we happen to be wrong, the worst
883 * consequence is the page will be lost to use till the next VACUUM, which
884 * is no big problem.)
885 */
886 for (;;)
887 {
888 blkno = GetFreeIndexPage(rel);
889 if (blkno == InvalidBlockNumber)
890 break;
891 buf = ReadBuffer(rel, blkno);
892 if (_bt_conditionallockbuf(rel, buf))
893 {
894 page = BufferGetPage(buf);
895
896 /*
897 * It's possible to find an all-zeroes page in an index. For
898 * example, a backend might successfully extend the relation one
899 * page and then crash before it is able to make a WAL entry for
900 * adding the page. If we find a zeroed page then reclaim it
901 * immediately.
902 */
903 if (PageIsNew(page))
904 {
905 /* Okay to use page. Initialize and return it. */
907 return buf;
908 }
909
910 if (BTPageIsRecyclable(page, heaprel))
911 {
912 /*
913 * If we are generating WAL for Hot Standby then create a WAL
914 * record that will allow us to conflict with queries running
915 * on standby, in case they have snapshots older than safexid
916 * value
917 */
919 {
921
922 /*
923 * Note that we don't register the buffer with the record,
924 * because this operation doesn't modify the page (that
925 * already happened, back when VACUUM deleted the page).
926 * This record only exists to provide a conflict point for
927 * Hot Standby. See record REDO routine comments.
928 */
930 xlrec_reuse.block = blkno;
931 xlrec_reuse.snapshotConflictHorizon = BTPageGetDeleteXid(page);
932 xlrec_reuse.isCatalogRel =
934
937
939 }
940
941 /* Okay to use page. Re-initialize and return it. */
943 return buf;
944 }
945 elog(DEBUG2, "FSM returned nonrecyclable page");
946 _bt_relbuf(rel, buf);
947 }
948 else
949 {
950 elog(DEBUG2, "FSM returned nonlockable page");
951 /* couldn't get lock, so just drop pin */
953 }
954 }
955
956 /*
957 * Extend the relation by one page. Need to use RBM_ZERO_AND_LOCK or we
958 * risk a race condition against btvacuumscan --- see comments therein.
959 * This forces us to repeat the valgrind request that _bt_lockbuf()
960 * otherwise would make, as we can't use _bt_lockbuf() without introducing
961 * a race.
962 */
964 if (!RelationUsesLocalBuffers(rel))
966
967 /* Initialize the new page before returning it */
968 page = BufferGetPage(buf);
969 Assert(PageIsNew(page));
971
972 return buf;
973}
uint32 BlockNumber
Definition block.h:31
#define InvalidBlockNumber
Definition block.h:33
int Buffer
Definition buf.h:23
Buffer ExtendBufferedRel(BufferManagerRelation bmr, ForkNumber forkNum, BufferAccessStrategy strategy, uint32 flags)
Definition bufmgr.c:970
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5609
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition bufmgr.c:879
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:468
static Size BufferGetPageSize(Buffer buffer)
Definition bufmgr.h:457
@ EB_LOCK_FIRST
Definition bufmgr.h:87
#define BMR_REL(p_rel)
Definition bufmgr.h:114
static bool PageIsNew(const PageData *page)
Definition bufpage.h:258
PageData * Page
Definition bufpage.h:81
#define Assert(condition)
Definition c.h:1002
#define DEBUG2
Definition elog.h:30
#define elog(elevel,...)
Definition elog.h:228
BlockNumber GetFreeIndexPage(Relation rel)
Definition indexfsm.c:38
#define VALGRIND_MAKE_MEM_DEFINED(addr, size)
Definition memdebug.h:26
void _bt_relbuf(Relation rel, Buffer buf)
Definition nbtpage.c:1024
void _bt_pageinit(Page page, Size size)
Definition nbtpage.c:1137
bool _bt_conditionallockbuf(Relation rel, Buffer buf)
Definition nbtpage.c:1101
static FullTransactionId BTPageGetDeleteXid(Page page)
Definition nbtree.h:261
static bool BTPageIsRecyclable(Page page, Relation heaprel)
Definition nbtree.h:292
#define XLOG_BTREE_REUSE_PAGE
Definition nbtxlog.h:40
#define SizeOfBtreeReusePage
Definition nbtxlog.h:192
static char buf[DEFAULT_XLOG_SEG_SIZE]
static int fb(int x)
#define RelationIsAccessibleInLogicalDecoding(relation)
Definition rel.h:704
#define RelationNeedsWAL(relation)
Definition rel.h:639
#define RelationUsesLocalBuffers(relation)
Definition rel.h:648
@ MAIN_FORKNUM
Definition relpath.h:58
RelFileLocator rd_locator
Definition rel.h:57
RelFileLocator locator
Definition nbtxlog.h:185
#define XLogStandbyInfoActive()
Definition xlog.h:126
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition xloginsert.c:482
void XLogRegisterData(const void *data, uint32 len)
Definition xloginsert.c:372
void XLogBeginInsert(void)
Definition xloginsert.c:153

References _bt_conditionallockbuf(), _bt_pageinit(), _bt_relbuf(), Assert, BMR_REL, BTPageGetDeleteXid(), BTPageIsRecyclable(), buf, BufferGetPage(), BufferGetPageSize(), DEBUG2, EB_LOCK_FIRST, elog, ExtendBufferedRel(), fb(), GetFreeIndexPage(), InvalidBlockNumber, xl_btree_reuse_page::locator, MAIN_FORKNUM, PageIsNew(), RelationData::rd_locator, ReadBuffer(), RelationIsAccessibleInLogicalDecoding, RelationNeedsWAL, RelationUsesLocalBuffers, ReleaseBuffer(), SizeOfBtreeReusePage, VALGRIND_MAKE_MEM_DEFINED, XLOG_BTREE_REUSE_PAGE, XLogBeginInsert(), XLogInsert(), XLogRegisterData(), and XLogStandbyInfoActive.

Referenced by _bt_getroot(), _bt_newlevel(), and _bt_split().

◆ _bt_checkpage()

void _bt_checkpage ( Relation  rel,
Buffer  buf 
)

Definition at line 782 of file nbtpage.c.

783{
784 Page page = BufferGetPage(buf);
785
786 /*
787 * ReadBuffer verifies that every newly-read page passes
788 * PageHeaderIsValid, which means it either contains a reasonably sane
789 * page header or is all-zero. We have to defend against the all-zero
790 * case, however.
791 */
792 if (PageIsNew(page))
795 errmsg("index \"%s\" contains unexpected zero page at block %u",
798 errhint("Please REINDEX it.")));
799
800 /*
801 * Additionally check that the special area looks sane.
802 */
803 if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BTPageOpaqueData)))
806 errmsg("index \"%s\" contains corrupted page at block %u",
809 errhint("Please REINDEX it.")));
810}
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition bufmgr.c:4469
static uint16 PageGetSpecialSize(const PageData *page)
Definition bufpage.h:341
#define MAXALIGN(LEN)
Definition c.h:955
int errcode(int sqlerrcode)
Definition elog.c:875
int errhint(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:40
#define ereport(elevel,...)
Definition elog.h:152
static char * errmsg
#define RelationGetRelationName(relation)
Definition rel.h:550

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

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

◆ _bt_conditionallockbuf()

bool _bt_conditionallockbuf ( Relation  rel,
Buffer  buf 
)

Definition at line 1101 of file nbtpage.c.

1102{
1103 /* ConditionalLockBuffer() asserts that pin is held by this backend */
1105 return false;
1106
1107 if (!RelationUsesLocalBuffers(rel))
1109
1110 return true;
1111}
bool ConditionalLockBuffer(Buffer buffer)
Definition bufmgr.c:6640

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

Referenced by _bt_allocbuf(), and _bt_search_insert().

◆ _bt_delitems_cmp()

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

Definition at line 1474 of file nbtpage.c.

1475{
1476 const TM_IndexDelete *indexdelete1 = a;
1477 const TM_IndexDelete *indexdelete2 = b;
1478
1479 Assert(indexdelete1->id != indexdelete2->id);
1480
1481 return pg_cmp_s16(indexdelete1->id, indexdelete2->id);
1482}
static int pg_cmp_s16(int16 a, int16 b)
Definition int.h:701
int b
Definition isn.c:74
int a
Definition isn.c:73

References a, Assert, b, fb(), and pg_cmp_s16().

Referenced by _bt_delitems_delete_check().

◆ _bt_delitems_delete()

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

Definition at line 1293 of file nbtpage.c.

1297{
1298 Page page = BufferGetPage(buf);
1299 BTPageOpaque opaque;
1300 bool needswal = RelationNeedsWAL(rel);
1301 char *updatedbuf = NULL;
1302 Size updatedbuflen = 0;
1305
1306 /* Shouldn't be called unless there's something to do */
1307 Assert(ndeletable > 0 || nupdatable > 0);
1308
1309 /* Generate new versions of posting lists without deleted TIDs */
1310 if (nupdatable > 0)
1313 needswal);
1314
1315 /* No ereport(ERROR) until changes are logged */
1317
1318 /* Handle updates and deletes just like _bt_delitems_vacuum */
1319 for (int i = 0; i < nupdatable; i++)
1320 {
1321 OffsetNumber updatedoffset = updatedoffsets[i];
1322 IndexTuple itup;
1323 Size itemsz;
1324
1325 itup = updatable[i]->itup;
1326 itemsz = MAXALIGN(IndexTupleSize(itup));
1327 if (!PageIndexTupleOverwrite(page, updatedoffset, itup, itemsz))
1328 elog(PANIC, "failed to update partially dead item in block %u of index \"%s\"",
1330 }
1331
1332 if (ndeletable > 0)
1334
1335 /*
1336 * Unlike _bt_delitems_vacuum, we *must not* clear the vacuum cycle ID at
1337 * this point. The VACUUM command alone controls vacuum cycle IDs.
1338 */
1339 opaque = BTPageGetOpaque(page);
1340
1341 /*
1342 * Clear the BTP_HAS_GARBAGE page flag.
1343 *
1344 * This flag indicates the presence of LP_DEAD items on the page (though
1345 * not reliably). Note that we only rely on it with pg_upgrade'd
1346 * !heapkeyspace indexes.
1347 */
1348 opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
1349
1351
1352 /* XLOG stuff */
1353 if (needswal)
1354 {
1356
1357 xlrec_delete.snapshotConflictHorizon = snapshotConflictHorizon;
1358 xlrec_delete.ndeleted = ndeletable;
1359 xlrec_delete.nupdated = nupdatable;
1360 xlrec_delete.isCatalogRel = isCatalogRel;
1361
1365
1366 if (ndeletable > 0)
1368 ndeletable * sizeof(OffsetNumber));
1369
1370 if (nupdatable > 0)
1371 {
1373 nupdatable * sizeof(OffsetNumber));
1375 }
1376
1378 }
1379 else
1380 recptr = XLogGetFakeLSN(rel);
1381
1382 PageSetLSN(page, recptr);
1383
1385
1386 /* can't leak memory here */
1387 if (updatedbuf != NULL)
1389 /* free tuples allocated within _bt_delitems_update() */
1390 for (int i = 0; i < nupdatable; i++)
1391 pfree(updatable[i]->itup);
1392}
void MarkBufferDirty(Buffer buffer)
Definition bufmgr.c:3170
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition bufpage.c:1170
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, const void *newtup, Size newsize)
Definition bufpage.c:1414
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition bufpage.h:416
size_t Size
Definition c.h:748
#define PANIC
Definition elog.h:44
int i
Definition isn.c:77
static Size IndexTupleSize(const IndexTupleData *itup)
Definition itup.h:71
#define MaxIndexTuplesPerPage
Definition itup.h:181
void pfree(void *pointer)
Definition mcxt.c:1619
#define START_CRIT_SECTION()
Definition miscadmin.h:152
#define END_CRIT_SECTION()
Definition miscadmin.h:154
static char * _bt_delitems_update(BTVacuumPosting *updatable, int nupdatable, OffsetNumber *updatedoffsets, Size *updatedbuflen, bool needswal)
Definition nbtpage.c:1415
#define BTPageGetOpaque(page)
Definition nbtree.h:74
#define SizeOfBtreeDelete
Definition nbtxlog.h:253
#define XLOG_BTREE_DELETE
Definition nbtxlog.h:34
uint16 OffsetNumber
Definition off.h:24
uint16 btpo_flags
Definition nbtree.h:68
IndexTuple itup
Definition nbtree.h:917
TransactionId snapshotConflictHorizon
Definition nbtxlog.h:238
uint64 XLogRecPtr
Definition xlogdefs.h:21
void XLogRegisterBufData(uint8 block_id, const void *data, uint32 len)
Definition xloginsert.c:413
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition xloginsert.c:246
XLogRecPtr XLogGetFakeLSN(Relation rel)
Definition xloginsert.c:562
#define REGBUF_STANDARD
Definition xloginsert.h:35

References _bt_delitems_update(), Assert, BTPageGetOpaque, BTPageOpaqueData::btpo_flags, buf, BufferGetBlockNumber(), BufferGetPage(), elog, END_CRIT_SECTION, fb(), i, IndexTupleSize(), BTVacuumPostingData::itup, MarkBufferDirty(), MAXALIGN, MaxIndexTuplesPerPage, PageIndexMultiDelete(), PageIndexTupleOverwrite(), PageSetLSN(), PANIC, pfree(), REGBUF_STANDARD, RelationGetRelationName, RelationNeedsWAL, SizeOfBtreeDelete, xl_btree_delete::snapshotConflictHorizon, START_CRIT_SECTION, XLOG_BTREE_DELETE, XLogBeginInsert(), XLogGetFakeLSN(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_delitems_delete_check().

◆ _bt_delitems_delete_check()

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

Definition at line 1523 of file nbtpage.c.

1525{
1526 Page page = BufferGetPage(buf);
1527 TransactionId snapshotConflictHorizon;
1528 bool isCatalogRel;
1530 int ndeletable = 0,
1531 nupdatable = 0;
1534
1535 /* Use tableam interface to determine which tuples to delete first */
1536 snapshotConflictHorizon = table_index_delete_tuples(heapRel, delstate);
1537 isCatalogRel = RelationIsAccessibleInLogicalDecoding(heapRel);
1538
1539 /* Should not WAL-log snapshotConflictHorizon unless it's required */
1540 if (!XLogStandbyInfoActive())
1541 snapshotConflictHorizon = InvalidTransactionId;
1542
1543 /*
1544 * Construct a leaf-page-wise description of what _bt_delitems_delete()
1545 * needs to do to physically delete index tuples from the page.
1546 *
1547 * Must sort deltids array to restore leaf-page-wise order (original order
1548 * before call to tableam). This is the order that the loop expects.
1549 *
1550 * Note that deltids array might be a lot smaller now. It might even have
1551 * no entries at all (with bottom-up deletion caller), in which case there
1552 * is nothing left to do.
1553 */
1554 qsort(delstate->deltids, delstate->ndeltids, sizeof(TM_IndexDelete),
1556 if (delstate->ndeltids == 0)
1557 {
1558 Assert(delstate->bottomup);
1559 return;
1560 }
1561
1562 /* We definitely have to delete at least one index tuple (or one TID) */
1563 for (int i = 0; i < delstate->ndeltids; i++)
1564 {
1565 TM_IndexStatus *dstatus = delstate->status + delstate->deltids[i].id;
1566 OffsetNumber idxoffnum = dstatus->idxoffnum;
1567 ItemId itemid = PageGetItemId(page, idxoffnum);
1568 IndexTuple itup = (IndexTuple) PageGetItem(page, itemid);
1569 int nestedi,
1570 nitem;
1572
1573 Assert(OffsetNumberIsValid(idxoffnum));
1574
1575 if (idxoffnum == postingidxoffnum)
1576 {
1577 /*
1578 * This deltid entry is a TID from a posting list tuple that has
1579 * already been completely processed
1580 */
1583 &delstate->deltids[i].tid) < 0);
1585 &delstate->deltids[i].tid) >= 0);
1586 continue;
1587 }
1588
1589 if (!BTreeTupleIsPosting(itup))
1590 {
1591 /* Plain non-pivot tuple */
1592 Assert(ItemPointerEquals(&itup->t_tid, &delstate->deltids[i].tid));
1593 if (dstatus->knowndeletable)
1594 deletable[ndeletable++] = idxoffnum;
1595 continue;
1596 }
1597
1598 /*
1599 * itup is a posting list tuple whose lowest deltids entry (which may
1600 * or may not be for the first TID from itup) is considered here now.
1601 * We should process all of the deltids entries for the posting list
1602 * together now, though (not just the lowest). Remember to skip over
1603 * later itup-related entries during later iterations of outermost
1604 * loop.
1605 */
1606 postingidxoffnum = idxoffnum; /* Remember work in outermost loop */
1607 nestedi = i; /* Initialize for first itup deltids entry */
1608 vacposting = NULL; /* Describes final action for itup */
1609 nitem = BTreeTupleGetNPosting(itup);
1610 for (int p = 0; p < nitem; p++)
1611 {
1613 int ptidcmp = -1;
1614
1615 /*
1616 * This nested loop reuses work across ptid TIDs taken from itup.
1617 * We take advantage of the fact that both itup's TIDs and deltids
1618 * entries (within a single itup/posting list grouping) must both
1619 * be in ascending TID order.
1620 */
1621 for (; nestedi < delstate->ndeltids; nestedi++)
1622 {
1624 TM_IndexStatus *tdstatus = (delstate->status + tcdeltid->id);
1625
1626 /* Stop once we get past all itup related deltids entries */
1627 Assert(tdstatus->idxoffnum >= idxoffnum);
1628 if (tdstatus->idxoffnum != idxoffnum)
1629 break;
1630
1631 /* Skip past non-deletable itup related entries up front */
1632 if (!tdstatus->knowndeletable)
1633 continue;
1634
1635 /* Entry is first partial ptid match (or an exact match)? */
1637 if (ptidcmp >= 0)
1638 {
1639 /* Greater than or equal (partial or exact) match... */
1640 break;
1641 }
1642 }
1643
1644 /* ...exact ptid match to a deletable deltids entry? */
1645 if (ptidcmp != 0)
1646 continue;
1647
1648 /* Exact match for deletable deltids entry -- ptid gets deleted */
1649 if (vacposting == NULL)
1650 {
1652 nitem * sizeof(uint16));
1653 vacposting->itup = itup;
1654 vacposting->updatedoffset = idxoffnum;
1655 vacposting->ndeletedtids = 0;
1656 }
1657 vacposting->deletetids[vacposting->ndeletedtids++] = p;
1658 }
1659
1660 /* Final decision on itup, a posting list tuple */
1661
1662 if (vacposting == NULL)
1663 {
1664 /* No TIDs to delete from itup -- do nothing */
1665 }
1666 else if (vacposting->ndeletedtids == nitem)
1667 {
1668 /* Straight delete of itup (to delete all TIDs) */
1669 deletable[ndeletable++] = idxoffnum;
1670 /* Turns out we won't need granular information */
1672 }
1673 else
1674 {
1675 /* Delete some (but not all) TIDs from itup */
1676 Assert(vacposting->ndeletedtids > 0 &&
1677 vacposting->ndeletedtids < nitem);
1678 updatable[nupdatable++] = vacposting;
1679 }
1680 }
1681
1682 /* Physically delete tuples (or TIDs) using deletable (or updatable) */
1683 _bt_delitems_delete(rel, buf, snapshotConflictHorizon, isCatalogRel,
1684 deletable, ndeletable, updatable, nupdatable);
1685
1686 /* be tidy */
1687 for (int i = 0; i < nupdatable; i++)
1688 pfree(updatable[i]);
1689}
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition bufpage.h:268
static void * PageGetItem(PageData *page, const ItemIdData *itemId)
Definition bufpage.h:378
uint16_t uint16
Definition c.h:682
uint32 TransactionId
Definition c.h:795
int32 ItemPointerCompare(const ItemPointerData *arg1, const ItemPointerData *arg2)
Definition itemptr.c:51
bool ItemPointerEquals(const ItemPointerData *pointer1, const ItemPointerData *pointer2)
Definition itemptr.c:35
IndexTupleData * IndexTuple
Definition itup.h:53
void * palloc(Size size)
Definition mcxt.c:1390
static void _bt_delitems_delete(Relation rel, Buffer buf, TransactionId snapshotConflictHorizon, bool isCatalogRel, OffsetNumber *deletable, int ndeletable, BTVacuumPosting *updatable, int nupdatable)
Definition nbtpage.c:1293
static int _bt_delitems_cmp(const void *a, const void *b)
Definition nbtpage.c:1474
static uint16 BTreeTupleGetNPosting(IndexTuple posting)
Definition nbtree.h:519
static ItemPointer BTreeTupleGetPostingN(IndexTuple posting, int n)
Definition nbtree.h:545
static ItemPointer BTreeTupleGetMaxHeapTID(IndexTuple itup)
Definition nbtree.h:665
static bool BTreeTupleIsPosting(IndexTuple itup)
Definition nbtree.h:493
static ItemPointer BTreeTupleGetHeapTID(IndexTuple itup)
Definition nbtree.h:639
#define InvalidOffsetNumber
Definition off.h:26
#define OffsetNumberIsValid(offsetNumber)
Definition off.h:39
#define qsort(a, b, c, d)
Definition port.h:496
ItemPointerData t_tid
Definition itup.h:37
OffsetNumber idxoffnum
Definition tableam.h:240
static TransactionId table_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
Definition tableam.h:1412
#define InvalidTransactionId
Definition transam.h:31

References _bt_delitems_cmp(), _bt_delitems_delete(), Assert, BTreeTupleGetHeapTID(), BTreeTupleGetMaxHeapTID(), BTreeTupleGetNPosting(), BTreeTupleGetPostingN(), BTreeTupleIsPosting(), buf, BufferGetPage(), fb(), i, TM_IndexStatus::idxoffnum, InvalidOffsetNumber, InvalidTransactionId, ItemPointerCompare(), ItemPointerEquals(), MaxIndexTuplesPerPage, OffsetNumberIsValid, PageGetItem(), PageGetItemId(), palloc(), pfree(), qsort, RelationIsAccessibleInLogicalDecoding, IndexTupleData::t_tid, table_index_delete_tuples(), and XLogStandbyInfoActive.

Referenced by _bt_bottomupdel_pass(), and _bt_simpledel_pass().

◆ _bt_delitems_update()

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

Definition at line 1415 of file nbtpage.c.

1418{
1419 char *updatedbuf = NULL;
1420 Size buflen = 0;
1421
1422 /* Shouldn't be called unless there's something to do */
1423 Assert(nupdatable > 0);
1424
1425 for (int i = 0; i < nupdatable; i++)
1426 {
1427 BTVacuumPosting vacposting = updatable[i];
1428 Size itemsz;
1429
1430 /* Replace work area IndexTuple with updated version */
1432
1433 /* Keep track of size of xl_btree_update for updatedbuf in passing */
1434 itemsz = SizeOfBtreeUpdate + vacposting->ndeletedtids * sizeof(uint16);
1435 buflen += itemsz;
1436
1437 /* Build updatedoffsets buffer in passing */
1438 updatedoffsets[i] = vacposting->updatedoffset;
1439 }
1440
1441 /* XLOG stuff */
1442 if (needswal)
1443 {
1444 Size offset = 0;
1445
1446 /* Allocate, set final size for caller */
1447 updatedbuf = palloc(buflen);
1448 *updatedbuflen = buflen;
1449 for (int i = 0; i < nupdatable; i++)
1450 {
1451 BTVacuumPosting vacposting = updatable[i];
1452 Size itemsz;
1453 xl_btree_update update;
1454
1455 update.ndeletedtids = vacposting->ndeletedtids;
1456 memcpy(updatedbuf + offset, &update.ndeletedtids,
1458 offset += SizeOfBtreeUpdate;
1459
1460 itemsz = update.ndeletedtids * sizeof(uint16);
1461 memcpy(updatedbuf + offset, vacposting->deletetids, itemsz);
1462 offset += itemsz;
1463 }
1464 }
1465
1466 return updatedbuf;
1467}
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
void _bt_update_posting(BTVacuumPosting vacposting)
Definition nbtdedup.c:924
#define SizeOfBtreeUpdate
Definition nbtxlog.h:268
uint16 ndeletedtids
Definition nbtxlog.h:263

References _bt_update_posting(), Assert, fb(), i, memcpy(), xl_btree_update::ndeletedtids, palloc(), and SizeOfBtreeUpdate.

Referenced by _bt_delitems_delete(), and _bt_delitems_vacuum().

◆ _bt_delitems_vacuum()

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

Definition at line 1162 of file nbtpage.c.

1165{
1166 Page page = BufferGetPage(buf);
1167 BTPageOpaque opaque;
1168 bool needswal = RelationNeedsWAL(rel);
1169 char *updatedbuf = NULL;
1170 Size updatedbuflen = 0;
1173
1174 /* Shouldn't be called unless there's something to do */
1175 Assert(ndeletable > 0 || nupdatable > 0);
1176
1177 /* Generate new version of posting lists without deleted TIDs */
1178 if (nupdatable > 0)
1181 needswal);
1182
1183 /* No ereport(ERROR) until changes are logged */
1185
1186 /*
1187 * Handle posting tuple updates.
1188 *
1189 * Deliberately do this before handling simple deletes. If we did it the
1190 * other way around (i.e. WAL record order -- simple deletes before
1191 * updates) then we'd have to make compensating changes to the 'updatable'
1192 * array of offset numbers.
1193 *
1194 * PageIndexTupleOverwrite() won't unset each item's LP_DEAD bit when it
1195 * happens to already be set. It's important that we not interfere with
1196 * any future simple index tuple deletion operations.
1197 */
1198 for (int i = 0; i < nupdatable; i++)
1199 {
1200 OffsetNumber updatedoffset = updatedoffsets[i];
1201 IndexTuple itup;
1202 Size itemsz;
1203
1204 itup = updatable[i]->itup;
1205 itemsz = MAXALIGN(IndexTupleSize(itup));
1206 if (!PageIndexTupleOverwrite(page, updatedoffset, itup, itemsz))
1207 elog(PANIC, "failed to update partially dead item in block %u of index \"%s\"",
1209 }
1210
1211 /* Now handle simple deletes of entire tuples */
1212 if (ndeletable > 0)
1214
1215 /*
1216 * We can clear the vacuum cycle ID since this page has certainly been
1217 * processed by the current vacuum scan.
1218 */
1219 opaque = BTPageGetOpaque(page);
1220 opaque->btpo_cycleid = 0;
1221
1222 /*
1223 * Clear the BTP_HAS_GARBAGE page flag.
1224 *
1225 * This flag indicates the presence of LP_DEAD items on the page (though
1226 * not reliably). Note that we only rely on it with pg_upgrade'd
1227 * !heapkeyspace indexes. That's why clearing it here won't usually
1228 * interfere with simple index tuple deletion.
1229 */
1230 opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
1231
1233
1234 /* XLOG stuff */
1235 if (needswal)
1236 {
1238
1240 xlrec_vacuum.nupdated = nupdatable;
1241
1245
1246 if (ndeletable > 0)
1248 ndeletable * sizeof(OffsetNumber));
1249
1250 if (nupdatable > 0)
1251 {
1253 nupdatable * sizeof(OffsetNumber));
1255 }
1256
1258 }
1259 else
1260 recptr = XLogGetFakeLSN(rel);
1261
1262 PageSetLSN(page, recptr);
1263
1265
1266 /* can't leak memory here */
1267 if (updatedbuf != NULL)
1269 /* free tuples allocated within _bt_delitems_update() */
1270 for (int i = 0; i < nupdatable; i++)
1271 pfree(updatable[i]->itup);
1272}
#define SizeOfBtreeVacuum
Definition nbtxlog.h:234
#define XLOG_BTREE_VACUUM
Definition nbtxlog.h:39
BTCycleId btpo_cycleid
Definition nbtree.h:69
uint16 ndeleted
Definition nbtxlog.h:222

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

Referenced by btvacuumpage().

◆ _bt_getbuf()

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

Definition at line 830 of file nbtpage.c.

831{
832 Buffer buf;
833
835
836 /* Read an existing block of the relation */
837 buf = ReadBuffer(rel, blkno);
838 _bt_lockbuf(rel, buf, access);
839 _bt_checkpage(rel, buf);
840
841 return buf;
842}
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition block.h:71
void _bt_checkpage(Relation rel, Buffer buf)
Definition nbtpage.c:782
void _bt_lockbuf(Relation rel, Buffer buf, int access)
Definition nbtpage.c:1047
short access

References _bt_checkpage(), _bt_lockbuf(), Assert, BlockNumberIsValid(), buf, and ReadBuffer().

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

◆ _bt_getmeta()

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

Definition at line 143 of file nbtpage.c.

144{
145 Page metapg;
148
152
153 /* sanity-check the metapage */
154 if (!P_ISMETA(metaopaque) ||
155 metad->btm_magic != BTREE_MAGIC)
158 errmsg("index \"%s\" is not a btree",
160
161 if (metad->btm_version < BTREE_MIN_VERSION ||
162 metad->btm_version > BTREE_VERSION)
165 errmsg("version mismatch in index \"%s\": file version %d, "
166 "current version %d, minimal supported version %d",
168 metad->btm_version, BTREE_VERSION, BTREE_MIN_VERSION)));
169
170 return metad;
171}
#define BTPageGetMeta(p)
Definition nbtree.h:122
#define BTREE_MIN_VERSION
Definition nbtree.h:152
#define P_ISMETA(opaque)
Definition nbtree.h:224
#define BTREE_MAGIC
Definition nbtree.h:150
#define BTREE_VERSION
Definition nbtree.h:151

References BTPageGetMeta, BTPageGetOpaque, BTREE_MAGIC, BTREE_MIN_VERSION, BTREE_VERSION, BufferGetPage(), ereport, errcode(), errmsg, ERROR, fb(), P_ISMETA, and RelationGetRelationName.

Referenced by _bt_getroot(), _bt_getrootheight(), _bt_gettrueroot(), and _bt_metaversion().

◆ _bt_getroot()

Buffer _bt_getroot ( Relation  rel,
Relation  heaprel,
int  access 
)

Definition at line 347 of file nbtpage.c.

348{
354 uint32 rootlevel;
357
358 Assert(access == BT_READ || heaprel != NULL);
359
360 /*
361 * Try to use previously-cached metapage data to find the root. This
362 * normally saves one buffer access per index search, which is a very
363 * helpful savings in bufmgr traffic and hence contention.
364 */
365 if (rel->rd_amcache != NULL)
366 {
368 /* We shouldn't have cached it if any of these fail */
369 Assert(metad->btm_magic == BTREE_MAGIC);
370 Assert(metad->btm_version >= BTREE_MIN_VERSION);
371 Assert(metad->btm_version <= BTREE_VERSION);
372 Assert(!metad->btm_allequalimage ||
373 metad->btm_version > BTREE_NOVAC_VERSION);
374 Assert(metad->btm_root != P_NONE);
375
376 rootblkno = metad->btm_fastroot;
378 rootlevel = metad->btm_fastlevel;
379
383
384 /*
385 * Since the cache might be stale, we check the page more carefully
386 * here than normal. We *must* check that it's not deleted. If it's
387 * not alone on its level, then we reject too --- this may be overly
388 * paranoid but better safe than sorry. Note we don't check P_ISROOT,
389 * because that's not set in a "fast root".
390 */
391 if (!P_IGNORE(rootopaque) &&
392 rootopaque->btpo_level == rootlevel &&
395 {
396 /* OK, accept cached page as the root */
397 return rootbuf;
398 }
399 _bt_relbuf(rel, rootbuf);
400 /* Cache is stale, throw it away */
401 if (rel->rd_amcache)
402 pfree(rel->rd_amcache);
403 rel->rd_amcache = NULL;
404 }
405
407 metad = _bt_getmeta(rel, metabuf);
408
409 /* if no root page initialized yet, do it */
410 if (metad->btm_root == P_NONE)
411 {
412 Page metapg;
413
414 /* If access = BT_READ, caller doesn't want us to create root yet */
415 if (access == BT_READ)
416 {
417 _bt_relbuf(rel, metabuf);
418 return InvalidBuffer;
419 }
420
421 /* trade in our read lock for a write lock */
424
425 /*
426 * Race condition: if someone else initialized the metadata between
427 * the time we released the read lock and acquired the write lock, we
428 * must avoid doing it again.
429 */
430 if (metad->btm_root != P_NONE)
431 {
432 /*
433 * Metadata initialized by someone else. In order to guarantee no
434 * deadlocks, we have to release the metadata page and start all
435 * over again. (Is that really true? But it's hardly worth trying
436 * to optimize this case.)
437 */
438 _bt_relbuf(rel, metabuf);
439 return _bt_getroot(rel, heaprel, access);
440 }
441
442 /*
443 * Get, initialize, write, and leave a lock of the appropriate type on
444 * the new root page. Since this is the first page in the tree, it's
445 * a leaf as well as the root.
446 */
447 rootbuf = _bt_allocbuf(rel, heaprel);
451 rootopaque->btpo_prev = rootopaque->btpo_next = P_NONE;
452 rootopaque->btpo_flags = (BTP_LEAF | BTP_ROOT);
453 rootopaque->btpo_level = 0;
454 rootopaque->btpo_cycleid = 0;
455 /* Get raw page pointer for metapage */
457
458 /* NO ELOG(ERROR) till meta is updated */
460
461 /* upgrade metapage if needed */
462 if (metad->btm_version < BTREE_NOVAC_VERSION)
464
465 metad->btm_root = rootblkno;
466 metad->btm_level = 0;
467 metad->btm_fastroot = rootblkno;
468 metad->btm_fastlevel = 0;
469 metad->btm_last_cleanup_num_delpages = 0;
470 metad->btm_last_cleanup_num_heap_tuples = -1.0;
471
474
475 /* XLOG stuff */
476 if (RelationNeedsWAL(rel))
477 {
480
484
485 Assert(metad->btm_version >= BTREE_NOVAC_VERSION);
486 md.version = metad->btm_version;
487 md.root = rootblkno;
488 md.level = 0;
489 md.fastroot = rootblkno;
490 md.fastlevel = 0;
492 md.allequalimage = metad->btm_allequalimage;
493
494 XLogRegisterBufData(2, &md, sizeof(xl_btree_metadata));
495
496 xlrec.rootblk = rootblkno;
497 xlrec.level = 0;
498
500
502 }
503 else
504 recptr = XLogGetFakeLSN(rel);
505
508
510
511 /*
512 * swap root write lock for read lock. There is no danger of anyone
513 * else accessing the new root page while it's unlocked, since no one
514 * else knows where it is yet.
515 */
518
519 /* okay, metadata is correct, release lock on it without caching */
520 _bt_relbuf(rel, metabuf);
521 }
522 else
523 {
524 rootblkno = metad->btm_fastroot;
526 rootlevel = metad->btm_fastlevel;
527
528 /*
529 * Cache the metapage data for next time
530 */
532 sizeof(BTMetaPageData));
533 memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
534
535 /*
536 * We are done with the metapage; arrange to release it via first
537 * _bt_relandgetbuf call
538 */
540
541 for (;;)
542 {
546
547 if (!P_IGNORE(rootopaque))
548 break;
549
550 /* it's dead, Jim. step right one page */
552 elog(ERROR, "no live root page found in index \"%s\"",
554 rootblkno = rootopaque->btpo_next;
555 }
556
557 if (rootopaque->btpo_level != rootlevel)
558 elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
560 rootopaque->btpo_level, rootlevel);
561 }
562
563 /*
564 * By here, we have a pin and read lock on the root page, and no lock set
565 * on the metadata page. Return the root page's buffer.
566 */
567 return rootbuf;
568}
#define InvalidBuffer
Definition buf.h:25
uint32_t uint32
Definition c.h:683
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1235
Buffer _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access)
Definition nbtpage.c:988
void _bt_upgrademetapage(Page page)
Definition nbtpage.c:108
Buffer _bt_allocbuf(Relation rel, Relation heaprel)
Definition nbtpage.c:854
static BTMetaPageData * _bt_getmeta(Relation rel, Buffer metabuf)
Definition nbtpage.c:143
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition nbtpage.c:830
void _bt_unlockbuf(Relation rel, Buffer buf)
Definition nbtpage.c:1078
Buffer _bt_getroot(Relation rel, Relation heaprel, int access)
Definition nbtpage.c:347
#define BTP_LEAF
Definition nbtree.h:77
#define P_LEFTMOST(opaque)
Definition nbtree.h:219
#define BTP_ROOT
Definition nbtree.h:78
#define P_NONE
Definition nbtree.h:213
#define P_RIGHTMOST(opaque)
Definition nbtree.h:220
#define BTREE_METAPAGE
Definition nbtree.h:149
#define BT_READ
Definition nbtree.h:730
#define P_IGNORE(opaque)
Definition nbtree.h:226
#define BTREE_NOVAC_VERSION
Definition nbtree.h:153
#define BT_WRITE
Definition nbtree.h:731
#define SizeOfBtreeNewroot
Definition nbtxlog.h:347
#define XLOG_BTREE_NEWROOT
Definition nbtxlog.h:37
void * rd_amcache
Definition rel.h:229
MemoryContext rd_indexcxt
Definition rel.h:204
BlockNumber fastroot
Definition nbtxlog.h:51
uint32 fastlevel
Definition nbtxlog.h:52
BlockNumber root
Definition nbtxlog.h:49
uint32 last_cleanup_num_delpages
Definition nbtxlog.h:53
#define REGBUF_WILL_INIT
Definition xloginsert.h:34

References _bt_allocbuf(), _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, BTP_LEAF, BTP_ROOT, BTPageGetOpaque, 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, fb(), InvalidBuffer, xl_btree_metadata::last_cleanup_num_delpages, xl_btree_metadata::level, MarkBufferDirty(), memcpy(), MemoryContextAlloc(), P_IGNORE, P_LEFTMOST, P_NONE, P_RIGHTMOST, PageSetLSN(), pfree(), RelationData::rd_amcache, RelationData::rd_indexcxt, REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetRelationName, RelationNeedsWAL, xl_btree_metadata::root, SizeOfBtreeNewroot, START_CRIT_SECTION, xl_btree_metadata::version, XLOG_BTREE_NEWROOT, XLogBeginInsert(), XLogGetFakeLSN(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

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

◆ _bt_getrootheight()

int _bt_getrootheight ( Relation  rel)

Definition at line 660 of file nbtpage.c.

661{
663
664 if (rel->rd_amcache == NULL)
665 {
667
669 metad = _bt_getmeta(rel, metabuf);
670
671 /*
672 * If there's no root page yet, _bt_getroot() doesn't expect a cache
673 * to be made, so just stop here and report the index height is zero.
674 * (XXX perhaps _bt_getroot() should be changed to allow this case.)
675 */
676 if (metad->btm_root == P_NONE)
677 {
678 _bt_relbuf(rel, metabuf);
679 return 0;
680 }
681
682 /*
683 * Cache the metapage data for next time
684 */
686 sizeof(BTMetaPageData));
687 memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
688 _bt_relbuf(rel, metabuf);
689 }
690
691 /* Get cached page */
693 /* We shouldn't have cached it if any of these fail */
694 Assert(metad->btm_magic == BTREE_MAGIC);
695 Assert(metad->btm_version >= BTREE_MIN_VERSION);
696 Assert(metad->btm_version <= BTREE_VERSION);
697 Assert(!metad->btm_allequalimage ||
698 metad->btm_version > BTREE_NOVAC_VERSION);
699 Assert(metad->btm_fastroot != P_NONE);
700
701 return metad->btm_fastlevel;
702}

References _bt_getbuf(), _bt_getmeta(), _bt_relbuf(), Assert, BT_READ, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_NOVAC_VERSION, BTREE_VERSION, fb(), memcpy(), MemoryContextAlloc(), P_NONE, RelationData::rd_amcache, and RelationData::rd_indexcxt.

Referenced by _bt_insertonpg(), and btgettreeheight().

◆ _bt_gettrueroot()

Buffer _bt_gettrueroot ( Relation  rel)

Definition at line 585 of file nbtpage.c.

586{
592 uint32 rootlevel;
594
595 /*
596 * We don't try to use cached metapage data here, since (a) this path is
597 * not performance-critical, and (b) if we are here it suggests our cache
598 * is out-of-date anyway. In light of point (b), it's probably safest to
599 * actively flush any cached metapage info.
600 */
601 if (rel->rd_amcache)
602 pfree(rel->rd_amcache);
603 rel->rd_amcache = NULL;
604
606 metad = _bt_getmeta(rel, metabuf);
607
608 /* if no root page initialized yet, fail */
609 if (metad->btm_root == P_NONE)
610 {
611 _bt_relbuf(rel, metabuf);
612 return InvalidBuffer;
613 }
614
615 rootblkno = metad->btm_root;
616 rootlevel = metad->btm_level;
617
618 /*
619 * We are done with the metapage; arrange to release it via first
620 * _bt_relandgetbuf call
621 */
623
624 for (;;)
625 {
629
630 if (!P_IGNORE(rootopaque))
631 break;
632
633 /* it's dead, Jim. step right one page */
635 elog(ERROR, "no live root page found in index \"%s\"",
637 rootblkno = rootopaque->btpo_next;
638 }
639
640 if (rootopaque->btpo_level != rootlevel)
641 elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
643 rootopaque->btpo_level, rootlevel);
644
645 return rootbuf;
646}

References _bt_getbuf(), _bt_getmeta(), _bt_relandgetbuf(), _bt_relbuf(), BT_READ, BTPageGetOpaque, BTREE_METAPAGE, BufferGetPage(), elog, ERROR, fb(), InvalidBuffer, P_IGNORE, P_NONE, P_RIGHTMOST, pfree(), RelationData::rd_amcache, and RelationGetRelationName.

Referenced by _bt_get_endpoint().

◆ _bt_initmetapage()

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

Definition at line 68 of file nbtpage.c.

70{
73
74 _bt_pageinit(page, BLCKSZ);
75
76 metad = BTPageGetMeta(page);
77 metad->btm_magic = BTREE_MAGIC;
78 metad->btm_version = BTREE_VERSION;
79 metad->btm_root = rootbknum;
80 metad->btm_level = level;
81 metad->btm_fastroot = rootbknum;
82 metad->btm_fastlevel = level;
83 metad->btm_last_cleanup_num_delpages = 0;
84 metad->btm_last_cleanup_num_heap_tuples = -1.0;
85 metad->btm_allequalimage = allequalimage;
86
88 metaopaque->btpo_flags = BTP_META;
89
90 /*
91 * Set pd_lower just past the end of the metadata. This is essential,
92 * because without doing so, metadata will be lost if xlog.c compresses
93 * the page.
94 */
95 ((PageHeader) page)->pd_lower =
96 ((char *) metad + sizeof(BTMetaPageData)) - (char *) page;
97}
PageHeaderData * PageHeader
Definition bufpage.h:199
#define BTP_META
Definition nbtree.h:80

References _bt_pageinit(), BTP_META, BTPageGetMeta, BTPageGetOpaque, BTREE_MAGIC, BTREE_VERSION, and fb().

Referenced by _bt_uppershutdown(), and btbuildempty().

◆ _bt_leftsib_splitflag()

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

Definition at line 1705 of file nbtpage.c.

1706{
1707 Buffer buf;
1708 Page page;
1709 BTPageOpaque opaque;
1710 bool result;
1711
1712 /* Easy case: No left sibling */
1713 if (leftsib == P_NONE)
1714 return false;
1715
1716 buf = _bt_getbuf(rel, leftsib, BT_READ);
1717 page = BufferGetPage(buf);
1718 opaque = BTPageGetOpaque(page);
1719
1720 /*
1721 * If the left sibling was concurrently split, so that its next-pointer
1722 * doesn't point to the current page anymore, the split that created
1723 * target must be completed. Caller can reasonably expect that there will
1724 * be a downlink to the target page that it can relocate using its stack.
1725 * (We don't allow splitting an incompletely split page again until the
1726 * previous split has been completed.)
1727 */
1728 result = (opaque->btpo_next == target && P_INCOMPLETE_SPLIT(opaque));
1729 _bt_relbuf(rel, buf);
1730
1731 return result;
1732}
uint32 result
#define P_INCOMPLETE_SPLIT(opaque)
Definition nbtree.h:228
BlockNumber btpo_next
Definition nbtree.h:66

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

Referenced by _bt_lock_subtree_parent(), and _bt_pagedel().

◆ _bt_lock_subtree_parent()

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

Definition at line 2830 of file nbtpage.c.

2834{
2835 BlockNumber parent,
2838 maxoff;
2839 Buffer pbuf;
2840 Page page;
2841 BTPageOpaque opaque;
2842
2843 /*
2844 * Locate the pivot tuple whose downlink points to "child". Write lock
2845 * the parent page itself.
2846 */
2847 pbuf = _bt_getstackbuf(rel, heaprel, stack, child);
2848 if (pbuf == InvalidBuffer)
2849 {
2850 /*
2851 * Failed to "re-find" a pivot tuple whose downlink matched our child
2852 * block number on the parent level -- the index must be corrupt.
2853 * Don't even try to delete the leafbuf subtree. Just report the
2854 * issue and press on with vacuuming the index.
2855 *
2856 * Note: _bt_getstackbuf() recovers from concurrent page splits that
2857 * take place on the parent level. Its approach is a near-exhaustive
2858 * linear search. This also gives it a surprisingly good chance of
2859 * recovering in the event of a buggy or inconsistent opclass. But we
2860 * don't rely on that here.
2861 */
2862 ereport(LOG,
2864 errmsg_internal("failed to re-find parent key in index \"%s\" for deletion target page %u",
2865 RelationGetRelationName(rel), child)));
2866 Assert(false);
2867 return false;
2868 }
2869
2870 parent = stack->bts_blkno;
2871 parentoffset = stack->bts_offset;
2872
2873 page = BufferGetPage(pbuf);
2874 opaque = BTPageGetOpaque(page);
2875 maxoff = PageGetMaxOffsetNumber(page);
2876 leftsibparent = opaque->btpo_prev;
2877
2878 /*
2879 * _bt_getstackbuf() completes page splits on returned parent buffer when
2880 * required.
2881 *
2882 * In general it's a bad idea for VACUUM to use up more disk space, which
2883 * is why page deletion does not finish incomplete page splits most of the
2884 * time. We allow this limited exception because the risk is much lower,
2885 * and the potential downside of not proceeding is much higher: A single
2886 * internal page with the INCOMPLETE_SPLIT flag set might otherwise
2887 * prevent us from deleting hundreds of empty leaf pages from one level
2888 * down.
2889 */
2890 Assert(!P_INCOMPLETE_SPLIT(opaque));
2891
2892 if (parentoffset < maxoff)
2893 {
2894 /*
2895 * Child is not the rightmost child in parent, so it's safe to delete
2896 * the subtree whose root/topparent is child page
2897 */
2899 *poffset = parentoffset;
2900 return true;
2901 }
2902
2903 /*
2904 * Child is the rightmost child of parent.
2905 *
2906 * Since it's the rightmost child of parent, deleting the child (or
2907 * deleting the subtree whose root/topparent is the child page) is only
2908 * safe when it's also possible to delete the parent.
2909 */
2910 Assert(parentoffset == maxoff);
2911 if (parentoffset != P_FIRSTDATAKEY(opaque) || P_RIGHTMOST(opaque))
2912 {
2913 /*
2914 * Child isn't parent's only child, or parent is rightmost on its
2915 * entire level. Definitely cannot delete any pages.
2916 */
2917 _bt_relbuf(rel, pbuf);
2918 return false;
2919 }
2920
2921 /*
2922 * Now make sure that the parent deletion is itself safe by examining the
2923 * child's grandparent page. Recurse, passing the parent page as the
2924 * child page (child's grandparent is the parent on the next level up). If
2925 * parent deletion is unsafe, then child deletion must also be unsafe (in
2926 * which case caller cannot delete any pages at all).
2927 */
2928 *topparent = parent;
2929 *topparentrightsib = opaque->btpo_next;
2930
2931 /*
2932 * Release lock on parent before recursing.
2933 *
2934 * It's OK to release page locks on parent before recursive call locks
2935 * grandparent. An internal page can only acquire an entry if the child
2936 * is split, but that cannot happen as long as we still hold a lock on the
2937 * leafbuf page.
2938 */
2939 _bt_relbuf(rel, pbuf);
2940
2941 /*
2942 * Before recursing, check that the left sibling of parent (if any) is not
2943 * marked with INCOMPLETE_SPLIT flag first (must do so after we drop the
2944 * parent lock).
2945 *
2946 * Note: We deliberately avoid completing incomplete splits here.
2947 */
2948 if (_bt_leftsib_splitflag(rel, leftsibparent, parent))
2949 return false;
2950
2951 /* Recurse to examine child page's grandparent page */
2952 return _bt_lock_subtree_parent(rel, heaprel, parent, stack->bts_parent,
2953 subtreeparent, poffset,
2954 topparent, topparentrightsib);
2955}
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition bufpage.h:396
#define LOG
Definition elog.h:32
int int errmsg_internal(const char *fmt,...) pg_attribute_printf(1
Buffer _bt_getstackbuf(Relation rel, Relation heaprel, BTStack stack, BlockNumber child)
Definition nbtinsert.c:2351
static bool _bt_lock_subtree_parent(Relation rel, Relation heaprel, BlockNumber child, BTStack stack, Buffer *subtreeparent, OffsetNumber *poffset, BlockNumber *topparent, BlockNumber *topparentrightsib)
Definition nbtpage.c:2830
static bool _bt_leftsib_splitflag(Relation rel, BlockNumber leftsib, BlockNumber target)
Definition nbtpage.c:1705
#define P_FIRSTDATAKEY(opaque)
Definition nbtree.h:370
BlockNumber btpo_prev
Definition nbtree.h:65
BlockNumber bts_blkno
Definition nbtree.h:745
struct BTStackData * bts_parent
Definition nbtree.h:747
OffsetNumber bts_offset
Definition nbtree.h:746

References _bt_getstackbuf(), _bt_leftsib_splitflag(), _bt_lock_subtree_parent(), _bt_relbuf(), Assert, BTPageGetOpaque, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BTStackData::bts_blkno, BTStackData::bts_offset, BTStackData::bts_parent, BufferGetPage(), ereport, errcode(), errmsg_internal(), fb(), InvalidBuffer, LOG, P_FIRSTDATAKEY, P_INCOMPLETE_SPLIT, P_RIGHTMOST, PageGetMaxOffsetNumber(), and RelationGetRelationName.

Referenced by _bt_lock_subtree_parent(), and _bt_mark_page_halfdead().

◆ _bt_lockbuf()

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

Definition at line 1047 of file nbtpage.c.

1048{
1049 /* LockBuffer() asserts that pin is held by this backend */
1051
1052 /*
1053 * It doesn't matter that _bt_unlockbuf() won't get called in the event of
1054 * an nbtree error (e.g. a unique violation error). That won't cause
1055 * Valgrind false positives.
1056 *
1057 * The nbtree client requests are superimposed on top of the bufmgr.c
1058 * buffer pin client requests. In the event of an nbtree error the buffer
1059 * will certainly get marked as defined when the backend once again
1060 * acquires its first pin on the buffer. (Of course, if the backend never
1061 * touches the buffer again then it doesn't matter that it remains
1062 * non-accessible to Valgrind.)
1063 *
1064 * Note: When an IndexTuple C pointer gets computed using an ItemId read
1065 * from a page while a lock was held, the C pointer becomes unsafe to
1066 * dereference forever as soon as the lock is released. Valgrind can only
1067 * detect cases where the pointer gets dereferenced with no _current_
1068 * lock/pin held, though.
1069 */
1070 if (!RelationUsesLocalBuffers(rel))
1072}
static void LockBuffer(Buffer buffer, BufferLockMode mode)
Definition bufmgr.h:334

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

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

◆ _bt_mark_page_halfdead()

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

Definition at line 2102 of file nbtpage.c.

2104{
2106 BlockNumber leafrightsib;
2107 BlockNumber topparent;
2109 ItemId itemid;
2110 Page page;
2111 BTPageOpaque opaque;
2113 OffsetNumber poffset;
2115 IndexTuple itup;
2118
2119 page = BufferGetPage(leafbuf);
2120 opaque = BTPageGetOpaque(page);
2121
2122 Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) &&
2123 P_ISLEAF(opaque) && !P_IGNORE(opaque) &&
2124 P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
2125 Assert(heaprel != NULL);
2126
2127 /*
2128 * Save info about the leaf page.
2129 */
2131 leafrightsib = opaque->btpo_next;
2132
2133 /*
2134 * Before attempting to lock the parent page, check that the right sibling
2135 * is not in half-dead state. A half-dead right sibling would have no
2136 * downlink in the parent, which would be highly confusing later when we
2137 * delete the downlink. It would fail the "right sibling of target page
2138 * is also the next child in parent page" cross-check below.
2139 */
2140 if (_bt_rightsib_halfdeadflag(rel, leafrightsib))
2141 {
2142 elog(DEBUG1, "could not delete page %u because its right sibling %u is half-dead",
2143 leafblkno, leafrightsib);
2144 return false;
2145 }
2146
2147 /*
2148 * We cannot delete a page that is the rightmost child of its immediate
2149 * parent, unless it is the only child --- in which case the parent has to
2150 * be deleted too, and the same condition applies recursively to it. We
2151 * have to check this condition all the way up before trying to delete,
2152 * and lock the parent of the root of the to-be-deleted subtree (the
2153 * "subtree parent"). _bt_lock_subtree_parent() locks the subtree parent
2154 * for us. We remove the downlink to the "top parent" page (subtree root
2155 * page) from the subtree parent page below.
2156 *
2157 * Initialize topparent to be leafbuf page now. The final to-be-deleted
2158 * subtree is often a degenerate one page subtree consisting only of the
2159 * leafbuf page. When that happens, the leafbuf page is the final subtree
2160 * root page/top parent page.
2161 */
2162 topparent = leafblkno;
2163 topparentrightsib = leafrightsib;
2164 if (!_bt_lock_subtree_parent(rel, heaprel, leafblkno, stack,
2165 &subtreeparent, &poffset,
2166 &topparent, &topparentrightsib))
2167 return false;
2168
2170 opaque = BTPageGetOpaque(page);
2171
2172#ifdef USE_ASSERT_CHECKING
2173
2174 /*
2175 * This is just an assertion because _bt_lock_subtree_parent should have
2176 * guaranteed tuple has the expected contents
2177 */
2178 itemid = PageGetItemId(page, poffset);
2179 itup = (IndexTuple) PageGetItem(page, itemid);
2180 Assert(BTreeTupleGetDownLink(itup) == topparent);
2181#endif
2182
2183 nextoffset = OffsetNumberNext(poffset);
2184 itemid = PageGetItemId(page, nextoffset);
2185 itup = (IndexTuple) PageGetItem(page, itemid);
2186
2187 /*
2188 * Check that the parent-page index items we're about to delete/overwrite
2189 * in subtree parent page contain what we expect. This can fail if the
2190 * index has become corrupt for some reason. When that happens we back
2191 * out of deletion of the leafbuf subtree. (This is just like the case
2192 * where _bt_lock_subtree_parent() cannot "re-find" leafbuf's downlink.)
2193 */
2195 {
2196 ereport(LOG,
2198 errmsg_internal("right sibling %u of block %u is not next child %u of block %u in index \"%s\"",
2199 topparentrightsib, topparent,
2203
2205 Assert(false);
2206 return false;
2207 }
2208
2209 /*
2210 * Any insert which would have gone on the leaf block will now go to its
2211 * right sibling. In other words, the key space moves right.
2212 */
2213 PredicateLockPageCombine(rel, leafblkno, leafrightsib);
2214
2215 /* No ereport(ERROR) until changes are logged */
2217
2218 /*
2219 * Update parent of subtree. We want to delete the downlink to the top
2220 * parent page/root of the subtree, and the *following* key. Easiest way
2221 * is to copy the right sibling's downlink over the downlink that points
2222 * to top parent page, and then delete the right sibling's original pivot
2223 * tuple.
2224 *
2225 * Lanin and Shasha make the key space move left when deleting a page,
2226 * whereas the key space moves right here. That's why we cannot simply
2227 * delete the pivot tuple with the downlink to the top parent page. See
2228 * nbtree/README.
2229 */
2231 opaque = BTPageGetOpaque(page);
2232
2233 itemid = PageGetItemId(page, poffset);
2234 itup = (IndexTuple) PageGetItem(page, itemid);
2236
2237 nextoffset = OffsetNumberNext(poffset);
2239
2240 /*
2241 * Mark the leaf page as half-dead, and stamp it with a link to the top
2242 * parent page. When the leaf page is also the top parent page, the link
2243 * is set to InvalidBlockNumber.
2244 */
2245 page = BufferGetPage(leafbuf);
2246 opaque = BTPageGetOpaque(page);
2247 opaque->btpo_flags |= BTP_HALF_DEAD;
2248
2250 MemSet(&trunctuple, 0, sizeof(IndexTupleData));
2251 trunctuple.t_info = sizeof(IndexTupleData);
2252 if (topparent != leafblkno)
2254 else
2256
2258 elog(ERROR, "could not overwrite high key in half-dead page");
2259
2260 /* Must mark buffers dirty before XLogInsert */
2263
2264 /* XLOG stuff */
2265 if (RelationNeedsWAL(rel))
2266 {
2268
2269 xlrec.poffset = poffset;
2270 xlrec.leafblk = leafblkno;
2271 if (topparent != leafblkno)
2272 xlrec.topparent = topparent;
2273 else
2274 xlrec.topparent = InvalidBlockNumber;
2275
2279
2280 page = BufferGetPage(leafbuf);
2281 opaque = BTPageGetOpaque(page);
2282 xlrec.leftblk = opaque->btpo_prev;
2283 xlrec.rightblk = opaque->btpo_next;
2284
2286
2288 }
2289 else
2290 recptr = XLogGetFakeLSN(rel);
2291
2293 PageSetLSN(page, recptr);
2294 page = BufferGetPage(leafbuf);
2295 PageSetLSN(page, recptr);
2296
2298
2300 return true;
2301}
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition bufpage.c:1061
#define MemSet(start, val, len)
Definition c.h:1147
#define DEBUG1
Definition elog.h:31
static bool _bt_rightsib_halfdeadflag(Relation rel, BlockNumber leafrightsib)
Definition nbtpage.c:1762
#define P_ISLEAF(opaque)
Definition nbtree.h:221
#define BTP_HALF_DEAD
Definition nbtree.h:81
#define P_HIKEY
Definition nbtree.h:368
static void BTreeTupleSetTopParent(IndexTuple leafhikey, BlockNumber blkno)
Definition nbtree.h:627
static void BTreeTupleSetDownLink(IndexTuple pivot, BlockNumber blkno)
Definition nbtree.h:563
#define P_ISROOT(opaque)
Definition nbtree.h:222
static BlockNumber BTreeTupleGetDownLink(IndexTuple pivot)
Definition nbtree.h:557
#define SizeOfBtreeMarkPageHalfDead
Definition nbtxlog.h:291
#define XLOG_BTREE_MARK_PAGE_HALFDEAD
Definition nbtxlog.h:38
#define OffsetNumberNext(offsetNumber)
Definition off.h:52
void PredicateLockPageCombine(Relation relation, BlockNumber oldblkno, BlockNumber newblkno)
Definition predicate.c:3158

References _bt_lock_subtree_parent(), _bt_relbuf(), _bt_rightsib_halfdeadflag(), Assert, BTP_HALF_DEAD, BTPageGetOpaque, BTPageOpaqueData::btpo_flags, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BTreeTupleGetDownLink(), BTreeTupleSetDownLink(), BTreeTupleSetTopParent(), BufferGetBlockNumber(), BufferGetPage(), DEBUG1, elog, END_CRIT_SECTION, ereport, errcode(), errmsg_internal(), ERROR, fb(), IndexTupleSize(), InvalidBlockNumber, LOG, MarkBufferDirty(), MemSet, OffsetNumberNext, P_FIRSTDATAKEY, P_HIKEY, P_IGNORE, P_ISLEAF, P_ISROOT, P_RIGHTMOST, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), PageIndexTupleDelete(), PageIndexTupleOverwrite(), PageSetLSN(), xl_btree_mark_page_halfdead::poffset, PredicateLockPageCombine(), REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetRelationName, RelationNeedsWAL, SizeOfBtreeMarkPageHalfDead, START_CRIT_SECTION, XLOG_BTREE_MARK_PAGE_HALFDEAD, XLogBeginInsert(), XLogGetFakeLSN(), XLogInsert(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_pagedel().

◆ _bt_metaversion()

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

Definition at line 724 of file nbtpage.c.

725{
727
728 if (rel->rd_amcache == NULL)
729 {
731
733 metad = _bt_getmeta(rel, metabuf);
734
735 /*
736 * If there's no root page yet, _bt_getroot() doesn't expect a cache
737 * to be made, so just stop here. (XXX perhaps _bt_getroot() should
738 * be changed to allow this case.)
739 */
740 if (metad->btm_root == P_NONE)
741 {
742 *heapkeyspace = metad->btm_version > BTREE_NOVAC_VERSION;
743 *allequalimage = metad->btm_allequalimage;
744
745 _bt_relbuf(rel, metabuf);
746 return;
747 }
748
749 /*
750 * Cache the metapage data for next time
751 *
752 * An on-the-fly version upgrade performed by _bt_upgrademetapage()
753 * can change the nbtree version for an index without invalidating any
754 * local cache. This is okay because it can only happen when moving
755 * from version 2 to version 3, both of which are !heapkeyspace
756 * versions.
757 */
759 sizeof(BTMetaPageData));
760 memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
761 _bt_relbuf(rel, metabuf);
762 }
763
764 /* Get cached page */
766 /* We shouldn't have cached it if any of these fail */
767 Assert(metad->btm_magic == BTREE_MAGIC);
768 Assert(metad->btm_version >= BTREE_MIN_VERSION);
769 Assert(metad->btm_version <= BTREE_VERSION);
770 Assert(!metad->btm_allequalimage ||
771 metad->btm_version > BTREE_NOVAC_VERSION);
772 Assert(metad->btm_fastroot != P_NONE);
773
774 *heapkeyspace = metad->btm_version > BTREE_NOVAC_VERSION;
775 *allequalimage = metad->btm_allequalimage;
776}

References _bt_getbuf(), _bt_getmeta(), _bt_relbuf(), Assert, BT_READ, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_NOVAC_VERSION, BTREE_VERSION, fb(), memcpy(), MemoryContextAlloc(), P_NONE, RelationData::rd_amcache, and RelationData::rd_indexcxt.

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

◆ _bt_pagedel()

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

Definition at line 1812 of file nbtpage.c.

1813{
1814 BlockNumber rightsib;
1815 bool rightsib_empty;
1816 Page page;
1817 BTPageOpaque opaque;
1818
1819 /*
1820 * Save original leafbuf block number from caller. Only deleted blocks
1821 * that are <= scanblkno are added to bulk delete stat's pages_deleted
1822 * count.
1823 */
1825
1826 /*
1827 * "stack" is a search stack leading (approximately) to the target page.
1828 * It is initially NULL, but when iterating, we keep it to avoid
1829 * duplicated search effort.
1830 *
1831 * Also, when "stack" is not NULL, we have already checked that the
1832 * current page is not the right half of an incomplete split, i.e. the
1833 * left sibling does not have its INCOMPLETE_SPLIT flag set, including
1834 * when the current target page is to the right of caller's initial page
1835 * (the scanblkno page).
1836 */
1837 BTStack stack = NULL;
1838
1839 for (;;)
1840 {
1841 page = BufferGetPage(leafbuf);
1842 opaque = BTPageGetOpaque(page);
1843
1844 /*
1845 * Internal pages are never deleted directly, only as part of deleting
1846 * the whole subtree all the way down to leaf level.
1847 *
1848 * Also check for deleted pages here. Caller never passes us a fully
1849 * deleted page. Only VACUUM can delete pages, so there can't have
1850 * been a concurrent deletion. Assume that we reached any deleted
1851 * page encountered here by following a sibling link, and that the
1852 * index is corrupt.
1853 */
1854 Assert(!P_ISDELETED(opaque));
1855 if (!P_ISLEAF(opaque) || P_ISDELETED(opaque))
1856 {
1857 /*
1858 * Pre-9.4 page deletion only marked internal pages as half-dead,
1859 * but now we only use that flag on leaf pages. The old algorithm
1860 * was never supposed to leave half-dead pages in the tree, it was
1861 * just a transient state, but it was nevertheless possible in
1862 * error scenarios. We don't know how to deal with them here. They
1863 * are harmless as far as searches are considered, but inserts
1864 * into the deleted keyspace could add out-of-order downlinks in
1865 * the upper levels. Log a notice, hopefully the admin will notice
1866 * and reindex.
1867 */
1868 if (P_ISHALFDEAD(opaque))
1869 ereport(LOG,
1871 errmsg("index \"%s\" contains a half-dead internal page",
1873 errhint("This can be caused by an interrupted VACUUM in version 9.3 or older, before upgrade. Please REINDEX it.")));
1874
1875 if (P_ISDELETED(opaque))
1876 ereport(LOG,
1878 errmsg_internal("found deleted block %u while following right link from block %u in index \"%s\"",
1880 scanblkno,
1882
1883 _bt_relbuf(rel, leafbuf);
1884 return;
1885 }
1886
1887 /*
1888 * We can never delete rightmost pages nor root pages. While at it,
1889 * check that page is empty, since it's possible that the leafbuf page
1890 * was empty a moment ago, but has since had some inserts.
1891 *
1892 * To keep the algorithm simple, we also never delete an incompletely
1893 * split page (they should be rare enough that this doesn't make any
1894 * meaningful difference to disk usage):
1895 *
1896 * The INCOMPLETE_SPLIT flag on the page tells us if the page is the
1897 * left half of an incomplete split, but ensuring that it's not the
1898 * right half is more complicated. For that, we have to check that
1899 * the left sibling doesn't have its INCOMPLETE_SPLIT flag set using
1900 * _bt_leftsib_splitflag(). On the first iteration, we temporarily
1901 * release the lock on scanblkno/leafbuf, check the left sibling, and
1902 * construct a search stack to scanblkno. On subsequent iterations,
1903 * we know we stepped right from a page that passed these tests, so
1904 * it's OK.
1905 */
1906 if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) ||
1907 P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) ||
1908 P_INCOMPLETE_SPLIT(opaque))
1909 {
1910 /* Should never fail to delete a half-dead page */
1911 Assert(!P_ISHALFDEAD(opaque));
1912
1913 _bt_relbuf(rel, leafbuf);
1914 return;
1915 }
1916
1917 /*
1918 * First, remove downlink pointing to the page (or a parent of the
1919 * page, if we are going to delete a taller subtree), and mark the
1920 * leafbuf page half-dead
1921 */
1922 if (!P_ISHALFDEAD(opaque))
1923 {
1924 /*
1925 * We need an approximate pointer to the page's parent page. We
1926 * use a variant of the standard search mechanism to search for
1927 * the page's high key; this will give us a link to either the
1928 * current parent or someplace to its left (if there are multiple
1929 * equal high keys, which is possible with !heapkeyspace indexes).
1930 *
1931 * Also check if this is the right-half of an incomplete split
1932 * (see comment above).
1933 */
1934 if (!stack)
1935 {
1936 BTScanInsert itup_key;
1937 ItemId itemid;
1939 BlockNumber leftsib,
1940 leafblkno;
1942
1943 itemid = PageGetItemId(page, P_HIKEY);
1945
1946 leftsib = opaque->btpo_prev;
1948
1949 /*
1950 * To avoid deadlocks, we'd better drop the leaf page lock
1951 * before going further.
1952 */
1953 _bt_unlockbuf(rel, leafbuf);
1954
1955 /*
1956 * Check that the left sibling of leafbuf (if any) is not
1957 * marked with INCOMPLETE_SPLIT flag before proceeding
1958 */
1960 if (_bt_leftsib_splitflag(rel, leftsib, leafblkno))
1961 {
1963 return;
1964 }
1965
1966 /*
1967 * We need an insertion scan key, so build one.
1968 *
1969 * _bt_search searches for the leaf page that contains any
1970 * matching non-pivot tuples, but we need it to "search" for
1971 * the high key pivot from the page that we're set to delete.
1972 * Compensate for the mismatch by having _bt_search locate the
1973 * last position < equal-to-untruncated-prefix non-pivots.
1974 */
1975 itup_key = _bt_mkscankey(rel, targetkey);
1976
1977 /* Set up a BTLessStrategyNumber-like insertion scan key */
1978 itup_key->nextkey = false;
1979 itup_key->backward = true;
1980 stack = _bt_search(rel, NULL, itup_key, &sleafbuf, BT_READ, true);
1981 /* won't need a second lock or pin on leafbuf */
1982 _bt_relbuf(rel, sleafbuf);
1983
1984 /*
1985 * Re-lock the leaf page, and start over to use our stack
1986 * within _bt_mark_page_halfdead. We must do it that way
1987 * because it's possible that leafbuf can no longer be
1988 * deleted. We need to recheck.
1989 *
1990 * Note: We can't simply hold on to the sleafbuf lock instead,
1991 * because it's barely possible that sleafbuf is not the same
1992 * page as leafbuf. This happens when leafbuf split after our
1993 * original lock was dropped, but before _bt_search finished
1994 * its descent. We rely on the assumption that we'll find
1995 * leafbuf isn't safe to delete anymore in this scenario.
1996 * (Page deletion can cope with the stack being to the left of
1997 * leafbuf, but not to the right of leafbuf.)
1998 */
2000 continue;
2001 }
2002
2003 /*
2004 * See if it's safe to delete the leaf page, and determine how
2005 * many parent/internal pages above the leaf level will be
2006 * deleted. If it's safe then _bt_mark_page_halfdead will also
2007 * perform the first phase of deletion, which includes marking the
2008 * leafbuf page half-dead.
2009 */
2010 Assert(P_ISLEAF(opaque) && !P_IGNORE(opaque));
2011 if (!_bt_mark_page_halfdead(rel, vstate->info->heaprel, leafbuf,
2012 stack))
2013 {
2014 _bt_relbuf(rel, leafbuf);
2015 return;
2016 }
2017 }
2018 else
2019 {
2020 INJECTION_POINT("nbtree-finish-half-dead-page-vacuum", NULL);
2021 }
2022
2023 /*
2024 * Then unlink it from its siblings. Each call to
2025 * _bt_unlink_halfdead_page unlinks the topmost page from the subtree,
2026 * making it shallower. Iterate until the leafbuf page is deleted.
2027 */
2028 rightsib_empty = false;
2029 Assert(P_ISLEAF(opaque) && P_ISHALFDEAD(opaque));
2030 while (P_ISHALFDEAD(opaque))
2031 {
2032 /* Check for interrupts in _bt_unlink_halfdead_page */
2035 {
2036 /*
2037 * _bt_unlink_halfdead_page should never fail, since we
2038 * established that deletion is generally safe in
2039 * _bt_mark_page_halfdead -- index must be corrupt.
2040 *
2041 * Note that _bt_unlink_halfdead_page already released the
2042 * lock and pin on leafbuf for us.
2043 */
2044 Assert(false);
2045 return;
2046 }
2047 }
2048
2049 Assert(P_ISLEAF(opaque) && P_ISDELETED(opaque));
2050
2051 rightsib = opaque->btpo_next;
2052
2053 _bt_relbuf(rel, leafbuf);
2054
2055 /*
2056 * Check here, as calling loops will have locks held, preventing
2057 * interrupts from being processed.
2058 */
2060
2061 /*
2062 * The page has now been deleted. If its right sibling is completely
2063 * empty, it's possible that the reason we haven't deleted it earlier
2064 * is that it was the rightmost child of the parent. Now that we
2065 * removed the downlink for this page, the right sibling might now be
2066 * the only child of the parent, and could be removed. It would be
2067 * picked up by the next vacuum anyway, but might as well try to
2068 * remove it now, so loop back to process the right sibling.
2069 *
2070 * Note: This relies on the assumption that _bt_getstackbuf() will be
2071 * able to reuse our original descent stack with a different child
2072 * block (provided that the child block is to the right of the
2073 * original leaf page reached by _bt_search()). It will even update
2074 * the descent stack each time we loop around, avoiding repeated work.
2075 */
2076 if (!rightsib_empty)
2077 break;
2078
2079 leafbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
2080 }
2081}
IndexTuple CopyIndexTuple(IndexTuple source)
Definition indextuple.c:479
#define INJECTION_POINT(name, arg)
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
static bool _bt_mark_page_halfdead(Relation rel, Relation heaprel, Buffer leafbuf, BTStack stack)
Definition nbtpage.c:2102
static bool _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, BlockNumber scanblkno, bool *rightsib_empty, BTVacState *vstate)
Definition nbtpage.c:2329
#define P_ISHALFDEAD(opaque)
Definition nbtree.h:225
#define P_ISDELETED(opaque)
Definition nbtree.h:223
BTStack _bt_search(Relation rel, Relation heaprel, BTScanInsert key, Buffer *bufP, int access, bool returnstack)
Definition nbtsearch.c:100
BTScanInsert _bt_mkscankey(Relation rel, IndexTuple itup)
Definition nbtutils.c:61

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, BTScanInsertData::backward, BT_READ, BT_WRITE, BTPageGetOpaque, BTPageOpaqueData::btpo_next, BTPageOpaqueData::btpo_prev, BufferGetBlockNumber(), BufferGetPage(), CHECK_FOR_INTERRUPTS, CopyIndexTuple(), ereport, errcode(), errhint(), errmsg, errmsg_internal(), fb(), INJECTION_POINT, LOG, BTScanInsertData::nextkey, P_FIRSTDATAKEY, P_HIKEY, P_IGNORE, P_INCOMPLETE_SPLIT, P_ISDELETED, P_ISHALFDEAD, P_ISLEAF, P_ISROOT, P_RIGHTMOST, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), RelationGetRelationName, and ReleaseBuffer().

Referenced by btvacuumpage().

◆ _bt_pageinit()

void _bt_pageinit ( Page  page,
Size  size 
)

Definition at line 1137 of file nbtpage.c.

1138{
1139 PageInit(page, size, sizeof(BTPageOpaqueData));
1140}
void PageInit(Page page, Size pageSize, Size specialSize)
Definition bufpage.c:42

References PageInit().

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

◆ _bt_pendingfsm_add()

static void _bt_pendingfsm_add ( BTVacState vstate,
BlockNumber  target,
FullTransactionId  safexid 
)
static

Definition at line 3080 of file nbtpage.c.

3083{
3084 Assert(vstate->npendingpages <= vstate->bufsize);
3085 Assert(vstate->bufsize <= vstate->maxbufsize);
3086
3087#ifdef USE_ASSERT_CHECKING
3088
3089 /*
3090 * Verify an assumption made by _bt_pendingfsm_finalize(): pages from the
3091 * array will always be in safexid order (since that is the order that we
3092 * save them in here)
3093 */
3094 if (vstate->npendingpages > 0)
3095 {
3097 vstate->pendingpages[vstate->npendingpages - 1].safexid;
3098
3100 }
3101#endif
3102
3103 /*
3104 * If temp buffer reaches maxbufsize/work_mem capacity then we discard
3105 * information about this page.
3106 *
3107 * Note that this also covers the case where we opted to not use the
3108 * optimization in _bt_pendingfsm_init().
3109 */
3110 if (vstate->npendingpages == vstate->maxbufsize)
3111 return;
3112
3113 /* Consider enlarging buffer */
3114 if (vstate->npendingpages == vstate->bufsize)
3115 {
3116 int newbufsize = vstate->bufsize * 2;
3117
3118 /* Respect work_mem */
3119 if (newbufsize > vstate->maxbufsize)
3120 newbufsize = vstate->maxbufsize;
3121
3122 vstate->bufsize = newbufsize;
3123 vstate->pendingpages =
3124 repalloc(vstate->pendingpages,
3125 sizeof(BTPendingFSM) * vstate->bufsize);
3126 }
3127
3128 /* Save metadata for newly deleted page */
3129 vstate->pendingpages[vstate->npendingpages].target = target;
3130 vstate->pendingpages[vstate->npendingpages].safexid = safexid;
3131 vstate->npendingpages++;
3132}
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1635
#define FullTransactionIdFollowsOrEquals(a, b)
Definition transam.h:54

References Assert, fb(), FullTransactionIdFollowsOrEquals, and repalloc().

Referenced by _bt_unlink_halfdead_page().

◆ _bt_pendingfsm_finalize()

void _bt_pendingfsm_finalize ( Relation  rel,
BTVacState vstate 
)

Definition at line 3013 of file nbtpage.c.

3014{
3015 IndexBulkDeleteResult *stats = vstate->stats;
3016 Relation heaprel = vstate->info->heaprel;
3017
3018 Assert(stats->pages_newly_deleted >= vstate->npendingpages);
3019 Assert(heaprel != NULL);
3020
3021 if (vstate->npendingpages == 0)
3022 {
3023 /* Just free memory when nothing to do */
3024 if (vstate->pendingpages)
3025 pfree(vstate->pendingpages);
3026
3027 return;
3028 }
3029
3030#ifdef DEBUG_BTREE_PENDING_FSM
3031
3032 /*
3033 * Debugging aid: Sleep for 5 seconds to greatly increase the chances of
3034 * placing pending pages in the FSM. Note that the optimization will
3035 * never be effective without some other backend concurrently consuming an
3036 * XID.
3037 */
3038 pg_usleep(5000000L);
3039#endif
3040
3041 /*
3042 * Recompute VACUUM XID boundaries.
3043 *
3044 * We don't actually care about the oldest non-removable XID. Computing
3045 * the oldest such XID has a useful side-effect that we rely on: it
3046 * forcibly updates the XID horizon state for this backend. This step is
3047 * essential; GlobalVisCheckRemovableFullXid() will not reliably recognize
3048 * that it is now safe to recycle newly deleted pages without this step.
3049 */
3051
3052 for (unsigned int i = 0; i < vstate->npendingpages; i++)
3053 {
3054 BlockNumber target = vstate->pendingpages[i].target;
3055 FullTransactionId safexid = vstate->pendingpages[i].safexid;
3056
3057 /*
3058 * Do the equivalent of checking BTPageIsRecyclable(), but without
3059 * accessing the page again a second time.
3060 *
3061 * Give up on finding the first non-recyclable page -- all later pages
3062 * must be non-recyclable too, since _bt_pendingfsm_add() adds pages
3063 * to the array in safexid order.
3064 */
3065 if (!GlobalVisCheckRemovableFullXid(heaprel, safexid))
3066 break;
3067
3068 RecordFreeIndexPage(rel, target);
3069 stats->pages_free++;
3070 }
3071
3072 pfree(vstate->pendingpages);
3073}
void RecordFreeIndexPage(Relation rel, BlockNumber freeBlock)
Definition indexfsm.c:52
TransactionId GetOldestNonRemovableTransactionId(Relation rel)
Definition procarray.c:1944
bool GlobalVisCheckRemovableFullXid(Relation rel, FullTransactionId fxid)
Definition procarray.c:4326
void pg_usleep(long microsec)
Definition signal.c:53
BlockNumber pages_newly_deleted
Definition genam.h:89
BlockNumber pages_free
Definition genam.h:91

References Assert, fb(), GetOldestNonRemovableTransactionId(), GlobalVisCheckRemovableFullXid(), i, IndexBulkDeleteResult::pages_free, IndexBulkDeleteResult::pages_newly_deleted, pfree(), pg_usleep(), and RecordFreeIndexPage().

Referenced by btvacuumscan().

◆ _bt_pendingfsm_init()

void _bt_pendingfsm_init ( Relation  rel,
BTVacState vstate,
bool  cleanuponly 
)

Definition at line 2971 of file nbtpage.c.

2972{
2973 Size maxbufsize;
2974
2975 /*
2976 * Don't bother with optimization in cleanup-only case -- we don't expect
2977 * any newly deleted pages. Besides, cleanup-only calls to btvacuumscan()
2978 * can only take place because this optimization didn't work out during
2979 * the last VACUUM.
2980 */
2981 if (cleanuponly)
2982 return;
2983
2984 /*
2985 * Cap maximum size of array so that we always respect work_mem. Avoid
2986 * int overflow here.
2987 */
2988 vstate->bufsize = 256;
2989 maxbufsize = (work_mem * (Size) 1024) / sizeof(BTPendingFSM);
2990 maxbufsize = Min(maxbufsize, MaxAllocSize / sizeof(BTPendingFSM));
2991 /* BTVacState.maxbufsize has type int */
2992 maxbufsize = Min(maxbufsize, INT_MAX);
2993 /* Stay sane with small work_mem */
2994 maxbufsize = Max(maxbufsize, vstate->bufsize);
2995 vstate->maxbufsize = (int) maxbufsize;
2996
2997 /* Allocate buffer, indicate that there are currently 0 pending pages */
2998 vstate->pendingpages = palloc_array(BTPendingFSM, vstate->bufsize);
2999 vstate->npendingpages = 0;
3000}
#define Min(x, y)
Definition c.h:1131
#define Max(x, y)
Definition c.h:1125
#define MaxAllocSize
Definition fe_memutils.h:22
#define palloc_array(type, count)
Definition fe_memutils.h:91
int work_mem
Definition globals.c:133

References fb(), Max, MaxAllocSize, Min, palloc_array, and work_mem.

Referenced by btvacuumscan().

◆ _bt_relandgetbuf()

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

Definition at line 988 of file nbtpage.c.

989{
990 Buffer buf;
991
993 if (BufferIsValid(obuf))
994 {
995 if (BufferGetBlockNumber(obuf) == blkno)
996 {
997 /* trade in old lock mode for new lock */
998 _bt_unlockbuf(rel, obuf);
999 buf = obuf;
1000 }
1001 else
1002 {
1003 /* release lock and pin at once, that's a bit more efficient */
1004 _bt_relbuf(rel, obuf);
1005 buf = ReadBuffer(rel, blkno);
1006 }
1007 }
1008 else
1009 buf = ReadBuffer(rel, blkno);
1010
1011 _bt_lockbuf(rel, buf, access);
1012 _bt_checkpage(rel, buf);
1013
1014 return buf;
1015}
static bool BufferIsValid(Buffer bufnum)
Definition bufmgr.h:419

References _bt_checkpage(), _bt_lockbuf(), _bt_relbuf(), _bt_unlockbuf(), Assert, BlockNumberIsValid(), buf, BufferGetBlockNumber(), BufferIsValid(), fb(), and ReadBuffer().

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

◆ _bt_relbuf()

void _bt_relbuf ( Relation  rel,
Buffer  buf 
)

Definition at line 1024 of file nbtpage.c.

1025{
1026 /*
1027 * Buffer is pinned and locked, which means that it is expected to be
1028 * defined and addressable. Check that proactively.
1029 */
1031 if (!RelationUsesLocalBuffers(rel))
1033
1035}
void UnlockReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5626
#define VALGRIND_CHECK_MEM_IS_DEFINED(addr, size)
Definition memdebug.h:23
#define VALGRIND_MAKE_MEM_NOACCESS(addr, size)
Definition memdebug.h:27

References buf, BufferGetPage(), fb(), RelationUsesLocalBuffers, UnlockReleaseBuffer(), VALGRIND_CHECK_MEM_IS_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

Referenced by _bt_allocbuf(), _bt_check_unique(), _bt_doinsert(), _bt_drop_lock_and_maybe_pin(), _bt_finish_split(), _bt_getroot(), _bt_getrootheight(), _bt_getstackbuf(), _bt_gettrueroot(), _bt_insert_parent(), _bt_insertonpg(), _bt_killitems(), _bt_leftsib_splitflag(), _bt_lock_and_validate_left(), _bt_lock_subtree_parent(), _bt_mark_page_halfdead(), _bt_metaversion(), _bt_moveright(), _bt_newlevel(), _bt_pagedel(), _bt_readnextpage(), _bt_relandgetbuf(), _bt_rightsib_halfdeadflag(), _bt_search_insert(), _bt_set_cleanup_info(), _bt_split(), _bt_stepright(), _bt_unlink_halfdead_page(), _bt_vacuum_needs_cleanup(), bt_rootdescend(), btvacuumpage(), and pgstat_btree_page().

◆ _bt_rightsib_halfdeadflag()

static bool _bt_rightsib_halfdeadflag ( Relation  rel,
BlockNumber  leafrightsib 
)
static

Definition at line 1762 of file nbtpage.c.

1763{
1764 Buffer buf;
1765 Page page;
1766 BTPageOpaque opaque;
1767 bool result;
1768
1769 Assert(leafrightsib != P_NONE);
1770
1771 buf = _bt_getbuf(rel, leafrightsib, BT_READ);
1772 page = BufferGetPage(buf);
1773 opaque = BTPageGetOpaque(page);
1774
1775 Assert(P_ISLEAF(opaque) && !P_ISDELETED(opaque));
1776 result = P_ISHALFDEAD(opaque);
1777 _bt_relbuf(rel, buf);
1778
1779 return result;
1780}

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

Referenced by _bt_mark_page_halfdead().

◆ _bt_set_cleanup_info()

void _bt_set_cleanup_info ( Relation  rel,
BlockNumber  num_delpages 
)

Definition at line 233 of file nbtpage.c.

234{
236 Page metapg;
239
240 /*
241 * On-disk compatibility note: The btm_last_cleanup_num_delpages metapage
242 * field started out as a TransactionId field called btm_oldest_btpo_xact.
243 * Both "versions" are just uint32 fields. It was convenient to repurpose
244 * the field when we began to use 64-bit XIDs in deleted pages.
245 *
246 * It's possible that a pg_upgrade'd database will contain an XID value in
247 * what is now recognized as the metapage's btm_last_cleanup_num_delpages
248 * field. _bt_vacuum_needs_cleanup() may even believe that this value
249 * indicates that there are lots of pages that it needs to recycle, when
250 * in reality there are only one or two. The worst that can happen is
251 * that there will be a call to btvacuumscan a little earlier, which will
252 * set btm_last_cleanup_num_delpages to a sane value when we're called.
253 *
254 * Note also that the metapage's btm_last_cleanup_num_heap_tuples field is
255 * no longer used as of PostgreSQL 14. We set it to -1.0 on rewrite, just
256 * to be consistent.
257 */
261
262 /* Don't miss chance to upgrade index/metapage when BTREE_MIN_VERSION */
263 if (metad->btm_version >= BTREE_NOVAC_VERSION &&
264 metad->btm_last_cleanup_num_delpages == num_delpages)
265 {
266 /* Usually means index continues to have num_delpages of 0 */
267 _bt_relbuf(rel, metabuf);
268 return;
269 }
270
271 /* trade in our read lock for a write lock */
274
276
277 /* upgrade meta-page if needed */
278 if (metad->btm_version < BTREE_NOVAC_VERSION)
280
281 /* update cleanup-related information */
282 metad->btm_last_cleanup_num_delpages = num_delpages;
283 metad->btm_last_cleanup_num_heap_tuples = -1.0;
285
286 /* write wal record if needed */
287 if (RelationNeedsWAL(rel))
288 {
290
293
294 Assert(metad->btm_version >= BTREE_NOVAC_VERSION);
295 md.version = metad->btm_version;
296 md.root = metad->btm_root;
297 md.level = metad->btm_level;
298 md.fastroot = metad->btm_fastroot;
299 md.fastlevel = metad->btm_fastlevel;
301 md.allequalimage = metad->btm_allequalimage;
302
303 XLogRegisterBufData(0, &md, sizeof(xl_btree_metadata));
304
306 }
307 else
308 recptr = XLogGetFakeLSN(rel);
309
311
313
314 _bt_relbuf(rel, metabuf);
315}
#define XLOG_BTREE_META_CLEANUP
Definition nbtxlog.h:41

References _bt_getbuf(), _bt_lockbuf(), _bt_relbuf(), _bt_unlockbuf(), _bt_upgrademetapage(), xl_btree_metadata::allequalimage, Assert, BT_READ, BT_WRITE, BTPageGetMeta, BTREE_METAPAGE, BTREE_NOVAC_VERSION, BufferGetPage(), END_CRIT_SECTION, xl_btree_metadata::fastlevel, xl_btree_metadata::fastroot, fb(), xl_btree_metadata::last_cleanup_num_delpages, 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(), XLogGetFakeLSN(), XLogInsert(), XLogRegisterBufData(), and XLogRegisterBuffer().

Referenced by btvacuumcleanup().

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

2331{
2333 IndexBulkDeleteResult *stats = vstate->stats;
2334 BlockNumber leafleftsib;
2335 BlockNumber leafrightsib;
2336 BlockNumber target;
2337 BlockNumber leftsib;
2338 BlockNumber rightsib;
2340 Buffer buf;
2341 Buffer rbuf;
2343 Page metapg = NULL;
2345 ItemId itemid;
2346 Page page;
2347 BTPageOpaque opaque;
2348 FullTransactionId safexid;
2352 BlockNumber leaftopparent;
2354
2355 page = BufferGetPage(leafbuf);
2356 opaque = BTPageGetOpaque(page);
2357
2358 Assert(P_ISLEAF(opaque) && !P_ISDELETED(opaque) && P_ISHALFDEAD(opaque));
2359
2360 /*
2361 * Remember some information about the leaf page.
2362 */
2363 itemid = PageGetItemId(page, P_HIKEY);
2364 leafhikey = (IndexTuple) PageGetItem(page, itemid);
2366 leafleftsib = opaque->btpo_prev;
2367 leafrightsib = opaque->btpo_next;
2368
2369 _bt_unlockbuf(rel, leafbuf);
2370
2371 INJECTION_POINT("nbtree-leave-page-half-dead", NULL);
2372
2373 /*
2374 * Check here, as calling loops will have locks held, preventing
2375 * interrupts from being processed.
2376 */
2378
2379 /* Unlink the current top parent of the subtree */
2380 if (!BlockNumberIsValid(target))
2381 {
2382 /* Target is leaf page (or leaf page is top parent, if you prefer) */
2383 target = leafblkno;
2384
2385 buf = leafbuf;
2386 leftsib = leafleftsib;
2387 targetlevel = 0;
2388 }
2389 else
2390 {
2391 /* Target is the internal page taken from leaf's top parent link */
2392 Assert(target != leafblkno);
2393
2394 /* Fetch the block number of the target's left sibling */
2395 buf = _bt_getbuf(rel, target, BT_READ);
2396 page = BufferGetPage(buf);
2397 opaque = BTPageGetOpaque(page);
2398 leftsib = opaque->btpo_prev;
2399 targetlevel = opaque->btpo_level;
2400 Assert(targetlevel > 0);
2401
2402 /*
2403 * To avoid deadlocks, we'd better drop the target page lock before
2404 * going further.
2405 */
2406 _bt_unlockbuf(rel, buf);
2407 }
2408
2409 /*
2410 * We have to lock the pages we need to modify in the standard order:
2411 * moving right, then up. Else we will deadlock against other writers.
2412 *
2413 * So, first lock the leaf page, if it's not the target. Then find and
2414 * write-lock the current left sibling of the target page. The sibling
2415 * that was current a moment ago could have split, so we may have to move
2416 * right.
2417 */
2418 if (target != leafblkno)
2420 if (leftsib != P_NONE)
2421 {
2422 lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
2423 page = BufferGetPage(lbuf);
2424 opaque = BTPageGetOpaque(page);
2425 while (P_ISDELETED(opaque) || opaque->btpo_next != target)
2426 {
2427 bool leftsibvalid = true;
2428
2429 /*
2430 * Before we follow the link from the page that was the left
2431 * sibling mere moments ago, validate its right link. This
2432 * reduces the opportunities for loop to fail to ever make any
2433 * progress in the presence of index corruption.
2434 *
2435 * Note: we rely on the assumption that there can only be one
2436 * vacuum process running at a time (against the same index).
2437 */
2438 if (P_RIGHTMOST(opaque) || P_ISDELETED(opaque) ||
2439 leftsib == opaque->btpo_next)
2440 leftsibvalid = false;
2441
2442 leftsib = opaque->btpo_next;
2443 _bt_relbuf(rel, lbuf);
2444
2445 if (!leftsibvalid)
2446 {
2447 /*
2448 * This is known to fail in the field; sibling link corruption
2449 * is relatively common. Press on with vacuuming rather than
2450 * just throwing an ERROR.
2451 */
2452 ereport(LOG,
2454 errmsg_internal("valid left sibling for deletion target could not be located: "
2455 "left sibling %u of target %u with leafblkno %u and scanblkno %u on level %u of index \"%s\"",
2456 leftsib, target, leafblkno, scanblkno,
2458
2459 /* Must release all pins and locks on failure exit */
2461 if (target != leafblkno)
2462 _bt_relbuf(rel, leafbuf);
2463
2464 return false;
2465 }
2466
2468
2469 /* step right one page */
2470 lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
2471 page = BufferGetPage(lbuf);
2472 opaque = BTPageGetOpaque(page);
2473 }
2474 }
2475 else
2477
2478 /* Next write-lock the target page itself */
2479 _bt_lockbuf(rel, buf, BT_WRITE);
2480 page = BufferGetPage(buf);
2481 opaque = BTPageGetOpaque(page);
2482
2483 /*
2484 * Check page is still empty etc, else abandon deletion. This is just for
2485 * paranoia's sake; a half-dead page cannot resurrect because there can be
2486 * only one vacuum process running at a time.
2487 */
2488 if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) || P_ISDELETED(opaque))
2489 elog(ERROR, "target page changed status unexpectedly in block %u of index \"%s\"",
2490 target, RelationGetRelationName(rel));
2491
2492 if (opaque->btpo_prev != leftsib)
2493 ereport(ERROR,
2495 errmsg_internal("target page left link unexpectedly changed from %u to %u in block %u of index \"%s\"",
2496 leftsib, opaque->btpo_prev, target,
2498
2499 if (target == leafblkno)
2500 {
2501 if (P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) ||
2502 !P_ISLEAF(opaque) || !P_ISHALFDEAD(opaque))
2503 elog(ERROR, "target leaf page changed status unexpectedly in block %u of index \"%s\"",
2504 target, RelationGetRelationName(rel));
2505
2506 /* Leaf page is also target page: don't set leaftopparent */
2507 leaftopparent = InvalidBlockNumber;
2508 }
2509 else
2510 {
2512
2513 if (P_FIRSTDATAKEY(opaque) != PageGetMaxOffsetNumber(page) ||
2514 P_ISLEAF(opaque))
2515 elog(ERROR, "target internal page on level %u changed status unexpectedly in block %u of index \"%s\"",
2516 targetlevel, target, RelationGetRelationName(rel));
2517
2518 /* Target is internal: set leaftopparent for next call here... */
2519 itemid = PageGetItemId(page, P_FIRSTDATAKEY(opaque));
2520 finaldataitem = (IndexTuple) PageGetItem(page, itemid);
2521 leaftopparent = BTreeTupleGetDownLink(finaldataitem);
2522 /* ...except when it would be a redundant pointer-to-self */
2523 if (leaftopparent == leafblkno)
2524 leaftopparent = InvalidBlockNumber;
2525 }
2526
2527 /* No leaftopparent for level 0 (leaf page) or level 1 target */
2528 Assert(!BlockNumberIsValid(leaftopparent) || targetlevel > 1);
2529
2530 /*
2531 * And next write-lock the (current) right sibling.
2532 */
2533 rightsib = opaque->btpo_next;
2534 rbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
2535 page = BufferGetPage(rbuf);
2536 opaque = BTPageGetOpaque(page);
2537
2538 /*
2539 * Validate target's right sibling page. Its left link must point back to
2540 * the target page.
2541 */
2542 if (opaque->btpo_prev != target)
2543 {
2544 /*
2545 * This is known to fail in the field; sibling link corruption is
2546 * relatively common. Press on with vacuuming rather than just
2547 * throwing an ERROR (same approach used for left-sibling's-right-link
2548 * validation check a moment ago).
2549 */
2550 ereport(LOG,
2552 errmsg_internal("right sibling's left-link doesn't match: "
2553 "right sibling %u of target %u with leafblkno %u "
2554 "and scanblkno %u spuriously links to non-target %u "
2555 "on level %u of index \"%s\"",
2556 rightsib, target, leafblkno,
2557 scanblkno, opaque->btpo_prev,
2559
2560 /* Must release all pins and locks on failure exit */
2561 if (BufferIsValid(lbuf))
2562 _bt_relbuf(rel, lbuf);
2563 _bt_relbuf(rel, rbuf);
2564 _bt_relbuf(rel, buf);
2565 if (target != leafblkno)
2566 _bt_relbuf(rel, leafbuf);
2567
2568 return false;
2569 }
2570
2573
2574 /*
2575 * If we are deleting the next-to-last page on the target's level, then
2576 * the rightsib is a candidate to become the new fast root. (In theory, it
2577 * might be possible to push the fast root even further down, but the odds
2578 * of doing so are slim, and the locking considerations daunting.)
2579 *
2580 * We can safely acquire a lock on the metapage here --- see comments for
2581 * _bt_newlevel().
2582 */
2583 if (leftsib == P_NONE && rightsib_is_rightmost)
2584 {
2585 page = BufferGetPage(rbuf);
2586 opaque = BTPageGetOpaque(page);
2587 if (P_RIGHTMOST(opaque))
2588 {
2589 /* rightsib will be the only one left on the level */
2593
2594 /*
2595 * The expected case here is btm_fastlevel == targetlevel+1; if
2596 * the fastlevel is <= targetlevel, something is wrong, and we
2597 * choose to overwrite it to fix it.
2598 */
2599 if (metad->btm_fastlevel > targetlevel + 1)
2600 {
2601 /* no update wanted */
2602 _bt_relbuf(rel, metabuf);
2604 }
2605 }
2606 }
2607
2608 /*
2609 * Here we begin doing the deletion.
2610 */
2611
2612 /* No ereport(ERROR) until changes are logged */
2614
2615 /*
2616 * Update siblings' side-links. Note the target page's side-links will
2617 * continue to point to the siblings. Asserts here are just rechecking
2618 * things we already verified above.
2619 */
2620 if (BufferIsValid(lbuf))
2621 {
2622 page = BufferGetPage(lbuf);
2623 opaque = BTPageGetOpaque(page);
2624 Assert(opaque->btpo_next == target);
2625 opaque->btpo_next = rightsib;
2626 }
2627 page = BufferGetPage(rbuf);
2628 opaque = BTPageGetOpaque(page);
2629 Assert(opaque->btpo_prev == target);
2630 opaque->btpo_prev = leftsib;
2631
2632 /*
2633 * If we deleted a parent of the targeted leaf page, instead of the leaf
2634 * itself, update the leaf to point to the next remaining child in the
2635 * subtree.
2636 *
2637 * Note: We rely on the fact that a buffer pin on the leaf page has been
2638 * held since leafhikey was initialized. This is safe, though only
2639 * because the page was already half-dead at that point. The leaf page
2640 * cannot have been modified by any other backend during the period when
2641 * no lock was held.
2642 */
2643 if (target != leafblkno)
2644 BTreeTupleSetTopParent(leafhikey, leaftopparent);
2645
2646 /*
2647 * Mark the page itself deleted. It can be recycled when all current
2648 * transactions are gone. Storing GetTopTransactionId() would work, but
2649 * we're in VACUUM and would not otherwise have an XID. Having already
2650 * updated links to the target, ReadNextFullTransactionId() suffices as an
2651 * upper bound. Any scan having retained a now-stale link is advertising
2652 * in its PGPROC an xmin less than or equal to the value we read here. It
2653 * will continue to do so, holding back the xmin horizon, for the duration
2654 * of that scan.
2655 */
2656 page = BufferGetPage(buf);
2657 opaque = BTPageGetOpaque(page);
2658 Assert(P_ISHALFDEAD(opaque) || !P_ISLEAF(opaque));
2659
2660 /*
2661 * Store upper bound XID that's used to determine when deleted page is no
2662 * longer needed as a tombstone
2663 */
2664 safexid = ReadNextFullTransactionId();
2665 BTPageSetDeleted(page, safexid);
2666 opaque->btpo_cycleid = 0;
2667
2668 /* And update the metapage, if needed */
2670 {
2671 /* upgrade metapage if needed */
2672 if (metad->btm_version < BTREE_NOVAC_VERSION)
2674 metad->btm_fastroot = rightsib;
2675 metad->btm_fastlevel = targetlevel;
2677 }
2678
2679 /* Must mark buffers dirty before XLogInsert */
2682 if (BufferIsValid(lbuf))
2684 if (target != leafblkno)
2686
2687 /* XLOG stuff */
2688 if (RelationNeedsWAL(rel))
2689 {
2692 uint8 xlinfo;
2693
2695
2697 if (BufferIsValid(lbuf))
2700 if (target != leafblkno)
2702
2703 /* information stored on the target/to-be-unlinked block */
2704 xlrec.leftsib = leftsib;
2705 xlrec.rightsib = rightsib;
2706 xlrec.level = targetlevel;
2707 xlrec.safexid = safexid;
2708
2709 /* information needed to recreate the leaf block (if not the target) */
2710 xlrec.leafleftsib = leafleftsib;
2711 xlrec.leafrightsib = leafrightsib;
2712 xlrec.leaftopparent = leaftopparent;
2713
2715
2717 {
2719
2720 Assert(metad->btm_version >= BTREE_NOVAC_VERSION);
2721 xlmeta.version = metad->btm_version;
2722 xlmeta.root = metad->btm_root;
2723 xlmeta.level = metad->btm_level;
2724 xlmeta.fastroot = metad->btm_fastroot;
2725 xlmeta.fastlevel = metad->btm_fastlevel;
2726 xlmeta.last_cleanup_num_delpages = metad->btm_last_cleanup_num_delpages;
2727 xlmeta.allequalimage = metad->btm_allequalimage;
2728
2731 }
2732 else
2734
2736 }
2737 else
2738 recptr = XLogGetFakeLSN(rel);
2739
2742 page = BufferGetPage(rbuf);
2743 PageSetLSN(page, recptr);
2744 page = BufferGetPage(buf);
2745 PageSetLSN(page, recptr);
2746 if (BufferIsValid(lbuf))
2747 {
2748 page = BufferGetPage(lbuf);
2749 PageSetLSN(page, recptr);
2750 }
2751 if (target != leafblkno)
2752 {
2753 page = BufferGetPage(leafbuf);
2754 PageSetLSN(page, recptr);
2755 }
2756
2758
2759 /* release metapage */
2761 _bt_relbuf(rel, metabuf);
2762
2763 /* release siblings */
2764 if (BufferIsValid(lbuf))
2765 _bt_relbuf(rel, lbuf);
2766 _bt_relbuf(rel, rbuf);
2767
2768 /* If the target is not leafbuf, we're done with it now -- release it */
2769 if (target != leafblkno)
2770 _bt_relbuf(rel, buf);
2771
2772 /*
2773 * Maintain pages_newly_deleted, which is simply the number of pages
2774 * deleted by the ongoing VACUUM operation.
2775 *
2776 * Maintain pages_deleted in a way that takes into account how
2777 * btvacuumpage() will count deleted pages that have yet to become
2778 * scanblkno -- only count page when it's not going to get that treatment
2779 * later on.
2780 */
2781 stats->pages_newly_deleted++;
2782 if (target <= scanblkno)
2783 stats->pages_deleted++;
2784
2785 /*
2786 * Remember information about the target page (now a newly deleted page)
2787 * in dedicated vstate space for later. The page will be considered as a
2788 * candidate to place in the FSM at the end of the current btvacuumscan()
2789 * call.
2790 */
2791 _bt_pendingfsm_add(vstate, target, safexid);
2792
2793 /* Success - hold on to lock on leafbuf (might also have been target) */
2794 return true;
2795}
uint8_t uint8
Definition c.h:681
static void _bt_pendingfsm_add(BTVacState *vstate, BlockNumber target, FullTransactionId safexid)
Definition nbtpage.c:3080
static BlockNumber BTreeTupleGetTopParent(IndexTuple leafhikey)
Definition nbtree.h:621
static void BTPageSetDeleted(Page page, FullTransactionId safexid)
Definition nbtree.h:240
#define XLOG_BTREE_UNLINK_PAGE
Definition nbtxlog.h:35
#define XLOG_BTREE_UNLINK_PAGE_META
Definition nbtxlog.h:36
#define SizeOfBtreeUnlinkPage
Definition nbtxlog.h:328
uint32 btpo_level
Definition nbtree.h:67
BlockNumber pages_deleted
Definition genam.h:90
FullTransactionId ReadNextFullTransactionId(void)
Definition varsup.c:283

References _bt_getbuf(), _bt_lockbuf(), _bt_pendingfsm_add(), _bt_relbuf(), _bt_unlockbuf(), _bt_upgrademetapage(), Assert, BlockNumberIsValid(), BT_READ, BT_WRITE, BTPageGetMeta, BTPageGetOpaque, 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, fb(), INJECTION_POINT, InvalidBlockNumber, InvalidBuffer, LOG, MarkBufferDirty(), P_FIRSTDATAKEY, P_HIKEY, P_ISDELETED, P_ISHALFDEAD, P_ISLEAF, P_ISROOT, P_NONE, P_RIGHTMOST, PageGetItem(), PageGetItemId(), PageGetMaxOffsetNumber(), IndexBulkDeleteResult::pages_deleted, IndexBulkDeleteResult::pages_newly_deleted, PageSetLSN(), ReadNextFullTransactionId(), REGBUF_STANDARD, REGBUF_WILL_INIT, RelationGetRelationName, RelationNeedsWAL, ReleaseBuffer(), SizeOfBtreeUnlinkPage, START_CRIT_SECTION, XLOG_BTREE_UNLINK_PAGE, XLOG_BTREE_UNLINK_PAGE_META, XLogBeginInsert(), XLogGetFakeLSN(), XLogInsert(), XLogRegisterBufData(), XLogRegisterBuffer(), and XLogRegisterData().

Referenced by _bt_pagedel().

◆ _bt_unlockbuf()

void _bt_unlockbuf ( Relation  rel,
Buffer  buf 
)

Definition at line 1078 of file nbtpage.c.

1079{
1080 /*
1081 * Buffer is pinned and locked, which means that it is expected to be
1082 * defined and addressable. Check that proactively.
1083 */
1085
1086 /* LockBuffer() asserts that pin is held by this backend */
1088
1089 if (!RelationUsesLocalBuffers(rel))
1091}
@ BUFFER_LOCK_UNLOCK
Definition bufmgr.h:207

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

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

◆ _bt_upgradelockbufcleanup()

void _bt_upgradelockbufcleanup ( Relation  rel,
Buffer  buf 
)

Definition at line 1117 of file nbtpage.c.

1118{
1119 /*
1120 * Buffer is pinned and locked, which means that it is expected to be
1121 * defined and addressable. Check that proactively.
1122 */
1124
1125 /* LockBuffer() asserts that pin is held by this backend */
1128}
void LockBufferForCleanup(Buffer buffer)
Definition bufmgr.c:6693

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

Referenced by btvacuumpage().

◆ _bt_upgrademetapage()

void _bt_upgrademetapage ( Page  page)

Definition at line 108 of file nbtpage.c.

109{
112
113 metad = BTPageGetMeta(page);
115
116 /* It must be really a meta page of upgradable version */
117 Assert(metaopaque->btpo_flags & BTP_META);
118 Assert(metad->btm_version < BTREE_NOVAC_VERSION);
119 Assert(metad->btm_version >= BTREE_MIN_VERSION);
120
121 /* Set version number and fill extra fields added into version 3 */
122 metad->btm_version = BTREE_NOVAC_VERSION;
123 metad->btm_last_cleanup_num_delpages = 0;
124 metad->btm_last_cleanup_num_heap_tuples = -1.0;
125 /* Only a REINDEX can set this field */
126 Assert(!metad->btm_allequalimage);
127 metad->btm_allequalimage = false;
128
129 /* Adjust pd_lower (see _bt_initmetapage() for details) */
130 ((PageHeader) page)->pd_lower =
131 ((char *) metad + sizeof(BTMetaPageData)) - (char *) page;
132}
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:308

References Assert, BTP_META, BTPageGetMeta, BTPageGetOpaque, BTREE_MIN_VERSION, BTREE_NOVAC_VERSION, fb(), and PG_USED_FOR_ASSERTS_ONLY.

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

◆ _bt_vacuum_needs_cleanup()

bool _bt_vacuum_needs_cleanup ( Relation  rel)

Definition at line 180 of file nbtpage.c.

181{
183 Page metapg;
185 uint32 btm_version;
187
188 /*
189 * Copy details from metapage to local variables quickly.
190 *
191 * Note that we deliberately avoid using cached version of metapage here.
192 */
196 btm_version = metad->btm_version;
197
198 if (btm_version < BTREE_NOVAC_VERSION)
199 {
200 /*
201 * Metapage needs to be dynamically upgraded to store fields that are
202 * only present when btm_version >= BTREE_NOVAC_VERSION
203 */
204 _bt_relbuf(rel, metabuf);
205 return true;
206 }
207
208 prev_num_delpages = metad->btm_last_cleanup_num_delpages;
209 _bt_relbuf(rel, metabuf);
210
211 /*
212 * Trigger cleanup in rare cases where prev_num_delpages exceeds 5% of the
213 * total size of the index. We can reasonably expect (though are not
214 * guaranteed) to be able to recycle this many pages if we decide to do a
215 * btvacuumscan call during the ongoing btvacuumcleanup. For further
216 * details see the nbtree/README section on placing deleted pages in the
217 * FSM.
218 */
219 if (prev_num_delpages > 0 &&
221 return true;
222
223 return false;
224}
#define RelationGetNumberOfBlocks(reln)
Definition bufmgr.h:309

References _bt_getbuf(), _bt_relbuf(), BT_READ, BTPageGetMeta, BTREE_METAPAGE, BTREE_NOVAC_VERSION, BufferGetPage(), fb(), and RelationGetNumberOfBlocks.

Referenced by btvacuumcleanup().