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 874 of file nbtpage.c.

875{
876 Buffer buf;
877 BlockNumber blkno;
878 Page page;
879
880 Assert(heaprel != NULL);
881
882 /*
883 * First see if the FSM knows of any free pages.
884 *
885 * We can't trust the FSM's report unreservedly; we have to check that the
886 * page is still free. (For example, an already-free page could have been
887 * re-used between the time the last VACUUM scanned it and the time the
888 * VACUUM made its FSM updates.)
889 *
890 * In fact, it's worse than that: we can't even assume that it's safe to
891 * take a lock on the reported page. If somebody else has a lock on it,
892 * or even worse our own caller does, we could deadlock. (The own-caller
893 * scenario is actually not improbable. Consider an index on a serial or
894 * timestamp column. Nearly all splits will be at the rightmost page, so
895 * it's entirely likely that _bt_split will call us while holding a lock
896 * on the page most recently acquired from FSM. A VACUUM running
897 * concurrently with the previous split could well have placed that page
898 * back in FSM.)
899 *
900 * To get around that, we ask for only a conditional lock on the reported
901 * page. If we fail, then someone else is using the page, and we may
902 * reasonably assume it's not free. (If we happen to be wrong, the worst
903 * consequence is the page will be lost to use till the next VACUUM, which
904 * is no big problem.)
905 */
906 for (;;)
907 {
908 blkno = GetFreeIndexPage(rel);
909 if (blkno == InvalidBlockNumber)
910 break;
911 buf = ReadBuffer(rel, blkno);
912 if (_bt_conditionallockbuf(rel, buf))
913 {
914 page = BufferGetPage(buf);
915
916 /*
917 * It's possible to find an all-zeroes page in an index. For
918 * example, a backend might successfully extend the relation one
919 * page and then crash before it is able to make a WAL entry for
920 * adding the page. If we find a zeroed page then reclaim it
921 * immediately.
922 */
923 if (PageIsNew(page))
924 {
925 /* Okay to use page. Initialize and return it. */
927 return buf;
928 }
929
930 if (BTPageIsRecyclable(page, heaprel))
931 {
932 /*
933 * If we are generating WAL for Hot Standby then create a WAL
934 * record that will allow us to conflict with queries running
935 * on standby, in case they have snapshots older than safexid
936 * value
937 */
939 {
941
942 /*
943 * Note that we don't register the buffer with the record,
944 * because this operation doesn't modify the page (that
945 * already happened, back when VACUUM deleted the page).
946 * This record only exists to provide a conflict point for
947 * Hot Standby. See record REDO routine comments.
948 */
950 xlrec_reuse.block = blkno;
951 xlrec_reuse.snapshotConflictHorizon = BTPageGetDeleteXid(page);
952 xlrec_reuse.isCatalogRel =
954
957
959 }
960
961 /* Okay to use page. Re-initialize and return it. */
963 return buf;
964 }
965 elog(DEBUG2, "FSM returned nonrecyclable page");
966 _bt_relbuf(rel, buf);
967 }
968 else
969 {
970 elog(DEBUG2, "FSM returned nonlockable page");
971 /* couldn't get lock, so just drop pin */
973 }
974 }
975
976 /*
977 * Extend the relation by one page. Need to use RBM_ZERO_AND_LOCK or we
978 * risk a race condition against btvacuumscan --- see comments therein.
979 * This forces us to repeat the valgrind request that _bt_lockbuf()
980 * otherwise would make, as we can't use _bt_lockbuf() without introducing
981 * a race.
982 */
984 if (!RelationUsesLocalBuffers(rel))
986
987 /* Initialize the new page before returning it */
988 page = BufferGetPage(buf);
989 Assert(PageIsNew(page));
991
992 return buf;
993}
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:974
void ReleaseBuffer(Buffer buffer)
Definition bufmgr.c:5505
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition bufmgr.c:874
static Page BufferGetPage(Buffer buffer)
Definition bufmgr.h:470
static Size BufferGetPageSize(Buffer buffer)
Definition bufmgr.h:459
@ 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:259
PageData * Page
Definition bufpage.h:81
#define Assert(condition)
Definition c.h:945
#define DEBUG2
Definition elog.h:29
#define elog(elevel,...)
Definition elog.h:226
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:1028
void _bt_pageinit(Page page, Size size)
Definition nbtpage.c:1134
bool _bt_conditionallockbuf(Relation rel, Buffer buf)
Definition nbtpage.c:1098
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:693
#define RelationNeedsWAL(relation)
Definition rel.h:637
#define RelationUsesLocalBuffers(relation)
Definition rel.h:646
@ MAIN_FORKNUM
Definition relpath.h:58
RelFileLocator rd_locator
Definition rel.h:57
RelFileLocator locator
Definition nbtxlog.h:185
#define XLogStandbyInfoActive()
Definition xlog.h:125
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition xloginsert.c:479
void XLogRegisterData(const void *data, uint32 len)
Definition xloginsert.c:369
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 802 of file nbtpage.c.

803{
804 Page page = BufferGetPage(buf);
805
806 /*
807 * ReadBuffer verifies that every newly-read page passes
808 * PageHeaderIsValid, which means it either contains a reasonably sane
809 * page header or is all-zero. We have to defend against the all-zero
810 * case, however.
811 */
812 if (PageIsNew(page))
815 errmsg("index \"%s\" contains unexpected zero page at block %u",
818 errhint("Please REINDEX it.")));
819
820 /*
821 * Additionally check that the special area looks sane.
822 */
823 if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BTPageOpaqueData)))
826 errmsg("index \"%s\" contains corrupted page at block %u",
829 errhint("Please REINDEX it.")));
830}
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition bufmgr.c:4357
static uint16 PageGetSpecialSize(const PageData *page)
Definition bufpage.h:342
#define MAXALIGN(LEN)
Definition c.h:898
int errcode(int sqlerrcode)
Definition elog.c:874
int errhint(const char *fmt,...) pg_attribute_printf(1
#define ERROR
Definition elog.h:39
#define ereport(elevel,...)
Definition elog.h:150
static char * errmsg
#define RelationGetRelationName(relation)
Definition rel.h:548

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 1098 of file nbtpage.c.

1099{
1100 /* ConditionalLockBuffer() asserts that pin is held by this backend */
1102 return false;
1103
1104 if (!RelationUsesLocalBuffers(rel))
1106
1107 return true;
1108}
bool ConditionalLockBuffer(Buffer buffer)
Definition bufmgr.c:6484

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 1471 of file nbtpage.c.

1472{
1473 const TM_IndexDelete *indexdelete1 = a;
1474 const TM_IndexDelete *indexdelete2 = b;
1475
1476 Assert(indexdelete1->id != indexdelete2->id);
1477
1478 return pg_cmp_s16(indexdelete1->id, indexdelete2->id);
1479}
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 1290 of file nbtpage.c.

1294{
1295 Page page = BufferGetPage(buf);
1296 BTPageOpaque opaque;
1297 bool needswal = RelationNeedsWAL(rel);
1298 char *updatedbuf = NULL;
1299 Size updatedbuflen = 0;
1302
1303 /* Shouldn't be called unless there's something to do */
1304 Assert(ndeletable > 0 || nupdatable > 0);
1305
1306 /* Generate new versions of posting lists without deleted TIDs */
1307 if (nupdatable > 0)
1310 needswal);
1311
1312 /* No ereport(ERROR) until changes are logged */
1314
1315 /* Handle updates and deletes just like _bt_delitems_vacuum */
1316 for (int i = 0; i < nupdatable; i++)
1317 {
1318 OffsetNumber updatedoffset = updatedoffsets[i];
1319 IndexTuple itup;
1320 Size itemsz;
1321
1322 itup = updatable[i]->itup;
1323 itemsz = MAXALIGN(IndexTupleSize(itup));
1324 if (!PageIndexTupleOverwrite(page, updatedoffset, itup, itemsz))
1325 elog(PANIC, "failed to update partially dead item in block %u of index \"%s\"",
1327 }
1328
1329 if (ndeletable > 0)
1331
1332 /*
1333 * Unlike _bt_delitems_vacuum, we *must not* clear the vacuum cycle ID at
1334 * this point. The VACUUM command alone controls vacuum cycle IDs.
1335 */
1336 opaque = BTPageGetOpaque(page);
1337
1338 /*
1339 * Clear the BTP_HAS_GARBAGE page flag.
1340 *
1341 * This flag indicates the presence of LP_DEAD items on the page (though
1342 * not reliably). Note that we only rely on it with pg_upgrade'd
1343 * !heapkeyspace indexes.
1344 */
1345 opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
1346
1348
1349 /* XLOG stuff */
1350 if (needswal)
1351 {
1353
1354 xlrec_delete.snapshotConflictHorizon = snapshotConflictHorizon;
1355 xlrec_delete.ndeleted = ndeletable;
1356 xlrec_delete.nupdated = nupdatable;
1357 xlrec_delete.isCatalogRel = isCatalogRel;
1358
1362
1363 if (ndeletable > 0)
1365 ndeletable * sizeof(OffsetNumber));
1366
1367 if (nupdatable > 0)
1368 {
1370 nupdatable * sizeof(OffsetNumber));
1372 }
1373
1375 }
1376 else
1377 recptr = XLogGetFakeLSN(rel);
1378
1379 PageSetLSN(page, recptr);
1380
1382
1383 /* can't leak memory here */
1384 if (updatedbuf != NULL)
1386 /* free tuples allocated within _bt_delitems_update() */
1387 for (int i = 0; i < nupdatable; i++)
1388 pfree(updatable[i]->itup);
1389}
void MarkBufferDirty(Buffer buffer)
Definition bufmgr.c:3063
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition bufpage.c:1160
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, const void *newtup, Size newsize)
Definition bufpage.c:1404
static void PageSetLSN(Page page, XLogRecPtr lsn)
Definition bufpage.h:417
size_t Size
Definition c.h:691
#define PANIC
Definition elog.h:42
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:1616
#define START_CRIT_SECTION()
Definition miscadmin.h:150
#define END_CRIT_SECTION()
Definition miscadmin.h:152
static char * _bt_delitems_update(BTVacuumPosting *updatable, int nupdatable, OffsetNumber *updatedoffsets, Size *updatedbuflen, bool needswal)
Definition nbtpage.c:1412
#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:410
void XLogRegisterBuffer(uint8 block_id, Buffer buffer, uint8 flags)
Definition xloginsert.c:246
XLogRecPtr XLogGetFakeLSN(Relation rel)
Definition xloginsert.c:559
#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 1520 of file nbtpage.c.

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

1415{
1416 char *updatedbuf = NULL;
1417 Size buflen = 0;
1418
1419 /* Shouldn't be called unless there's something to do */
1420 Assert(nupdatable > 0);
1421
1422 for (int i = 0; i < nupdatable; i++)
1423 {
1424 BTVacuumPosting vacposting = updatable[i];
1425 Size itemsz;
1426
1427 /* Replace work area IndexTuple with updated version */
1429
1430 /* Keep track of size of xl_btree_update for updatedbuf in passing */
1431 itemsz = SizeOfBtreeUpdate + vacposting->ndeletedtids * sizeof(uint16);
1432 buflen += itemsz;
1433
1434 /* Build updatedoffsets buffer in passing */
1435 updatedoffsets[i] = vacposting->updatedoffset;
1436 }
1437
1438 /* XLOG stuff */
1439 if (needswal)
1440 {
1441 Size offset = 0;
1442
1443 /* Allocate, set final size for caller */
1444 updatedbuf = palloc(buflen);
1445 *updatedbuflen = buflen;
1446 for (int i = 0; i < nupdatable; i++)
1447 {
1448 BTVacuumPosting vacposting = updatable[i];
1449 Size itemsz;
1450 xl_btree_update update;
1451
1452 update.ndeletedtids = vacposting->ndeletedtids;
1453 memcpy(updatedbuf + offset, &update.ndeletedtids,
1455 offset += SizeOfBtreeUpdate;
1456
1457 itemsz = update.ndeletedtids * sizeof(uint16);
1458 memcpy(updatedbuf + offset, vacposting->deletetids, itemsz);
1459 offset += itemsz;
1460 }
1461 }
1462
1463 return updatedbuf;
1464}
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, 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 1159 of file nbtpage.c.

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

851{
852 Buffer buf;
853
855
856 /* Read an existing block of the relation */
857 buf = ReadBuffer(rel, blkno);
858 _bt_lockbuf(rel, buf, access);
859 _bt_checkpage(rel, buf);
860
861 return buf;
862}
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition block.h:71
void _bt_checkpage(Relation rel, Buffer buf)
Definition nbtpage.c:802
void _bt_lockbuf(Relation rel, Buffer buf, int access)
Definition nbtpage.c:1044
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(), 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:618
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1232
Buffer _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access)
Definition nbtpage.c:1008
void _bt_upgrademetapage(Page page)
Definition nbtpage.c:108
Buffer _bt_allocbuf(Relation rel, Relation heaprel)
Definition nbtpage.c:874
static BTMetaPageData * _bt_getmeta(Relation rel, Buffer metabuf)
Definition nbtpage.c:143
Buffer _bt_getbuf(Relation rel, BlockNumber blkno, int access)
Definition nbtpage.c:850
void _bt_unlockbuf(Relation rel, Buffer buf)
Definition nbtpage.c:1075
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(), 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 680 of file nbtpage.c.

681{
683
684 if (rel->rd_amcache == NULL)
685 {
687
689 metad = _bt_getmeta(rel, metabuf);
690
691 /*
692 * If there's no root page yet, _bt_getroot() doesn't expect a cache
693 * to be made, so just stop here and report the index height is zero.
694 * (XXX perhaps _bt_getroot() should be changed to allow this case.)
695 */
696 if (metad->btm_root == P_NONE)
697 {
698 _bt_relbuf(rel, metabuf);
699 return 0;
700 }
701
702 /*
703 * Cache the metapage data for next time
704 */
706 sizeof(BTMetaPageData));
707 memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
708 _bt_relbuf(rel, metabuf);
709 }
710
711 /* Get cached page */
713 /* We shouldn't have cached it if any of these fail */
714 Assert(metad->btm_magic == BTREE_MAGIC);
715 Assert(metad->btm_version >= BTREE_MIN_VERSION);
716 Assert(metad->btm_version <= BTREE_VERSION);
717 Assert(!metad->btm_allequalimage ||
718 metad->btm_version > BTREE_NOVAC_VERSION);
719 Assert(metad->btm_fastroot != P_NONE);
720
721 return metad->btm_fastlevel;
722}

References _bt_getbuf(), _bt_getmeta(), _bt_relbuf(), Assert, BT_READ, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_NOVAC_VERSION, BTREE_VERSION, fb(), 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{
588 Page metapg;
594 uint32 rootlevel;
596
597 /*
598 * We don't try to use cached metapage data here, since (a) this path is
599 * not performance-critical, and (b) if we are here it suggests our cache
600 * is out-of-date anyway. In light of point (b), it's probably safest to
601 * actively flush any cached metapage info.
602 */
603 if (rel->rd_amcache)
604 pfree(rel->rd_amcache);
605 rel->rd_amcache = NULL;
606
611
612 if (!P_ISMETA(metaopaque) ||
613 metad->btm_magic != BTREE_MAGIC)
616 errmsg("index \"%s\" is not a btree",
618
619 if (metad->btm_version < BTREE_MIN_VERSION ||
620 metad->btm_version > BTREE_VERSION)
623 errmsg("version mismatch in index \"%s\": file version %d, "
624 "current version %d, minimal supported version %d",
626 metad->btm_version, BTREE_VERSION, BTREE_MIN_VERSION)));
627
628 /* if no root page initialized yet, fail */
629 if (metad->btm_root == P_NONE)
630 {
631 _bt_relbuf(rel, metabuf);
632 return InvalidBuffer;
633 }
634
635 rootblkno = metad->btm_root;
636 rootlevel = metad->btm_level;
637
638 /*
639 * We are done with the metapage; arrange to release it via first
640 * _bt_relandgetbuf call
641 */
643
644 for (;;)
645 {
649
650 if (!P_IGNORE(rootopaque))
651 break;
652
653 /* it's dead, Jim. step right one page */
655 elog(ERROR, "no live root page found in index \"%s\"",
657 rootblkno = rootopaque->btpo_next;
658 }
659
660 if (rootopaque->btpo_level != rootlevel)
661 elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
663 rootopaque->btpo_level, rootlevel);
664
665 return rootbuf;
666}

References _bt_getbuf(), _bt_relandgetbuf(), _bt_relbuf(), BT_READ, BTPageGetMeta, BTPageGetOpaque, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_VERSION, BufferGetPage(), elog, ereport, errcode(), errmsg, ERROR, fb(), InvalidBuffer, P_IGNORE, P_ISMETA, 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 1702 of file nbtpage.c.

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

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 2827 of file nbtpage.c.

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

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

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 2099 of file nbtpage.c.

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

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 744 of file nbtpage.c.

745{
747
748 if (rel->rd_amcache == NULL)
749 {
751
753 metad = _bt_getmeta(rel, metabuf);
754
755 /*
756 * If there's no root page yet, _bt_getroot() doesn't expect a cache
757 * to be made, so just stop here. (XXX perhaps _bt_getroot() should
758 * be changed to allow this case.)
759 */
760 if (metad->btm_root == P_NONE)
761 {
762 *heapkeyspace = metad->btm_version > BTREE_NOVAC_VERSION;
763 *allequalimage = metad->btm_allequalimage;
764
765 _bt_relbuf(rel, metabuf);
766 return;
767 }
768
769 /*
770 * Cache the metapage data for next time
771 *
772 * An on-the-fly version upgrade performed by _bt_upgrademetapage()
773 * can change the nbtree version for an index without invalidating any
774 * local cache. This is okay because it can only happen when moving
775 * from version 2 to version 3, both of which are !heapkeyspace
776 * versions.
777 */
779 sizeof(BTMetaPageData));
780 memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
781 _bt_relbuf(rel, metabuf);
782 }
783
784 /* Get cached page */
786 /* We shouldn't have cached it if any of these fail */
787 Assert(metad->btm_magic == BTREE_MAGIC);
788 Assert(metad->btm_version >= BTREE_MIN_VERSION);
789 Assert(metad->btm_version <= BTREE_VERSION);
790 Assert(!metad->btm_allequalimage ||
791 metad->btm_version > BTREE_NOVAC_VERSION);
792 Assert(metad->btm_fastroot != P_NONE);
793
794 *heapkeyspace = metad->btm_version > BTREE_NOVAC_VERSION;
795 *allequalimage = metad->btm_allequalimage;
796}

References _bt_getbuf(), _bt_getmeta(), _bt_relbuf(), Assert, BT_READ, BTREE_MAGIC, BTREE_METAPAGE, BTREE_MIN_VERSION, BTREE_NOVAC_VERSION, BTREE_VERSION, fb(), 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 1809 of file nbtpage.c.

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

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 1134 of file nbtpage.c.

1135{
1136 PageInit(page, size, sizeof(BTPageOpaqueData));
1137}
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 3077 of file nbtpage.c.

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

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

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

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 1008 of file nbtpage.c.

1009{
1010 Buffer buf;
1011
1012 Assert(BlockNumberIsValid(blkno));
1013 if (BufferIsValid(obuf))
1014 _bt_unlockbuf(rel, obuf);
1015 buf = ReleaseAndReadBuffer(obuf, rel, blkno);
1016 _bt_lockbuf(rel, buf, access);
1017
1018 _bt_checkpage(rel, buf);
1019 return buf;
1020}
Buffer ReleaseAndReadBuffer(Buffer buffer, Relation relation, BlockNumber blockNum)
Definition bufmgr.c:3128
static bool BufferIsValid(Buffer bufnum)
Definition bufmgr.h:421

References _bt_checkpage(), _bt_lockbuf(), _bt_unlockbuf(), Assert, BlockNumberIsValid(), buf, BufferIsValid(), fb(), and ReleaseAndReadBuffer().

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()

◆ _bt_rightsib_halfdeadflag()

static bool _bt_rightsib_halfdeadflag ( Relation  rel,
BlockNumber  leafrightsib 
)
static

Definition at line 1759 of file nbtpage.c.

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

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

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 2326 of file nbtpage.c.

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

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 1075 of file nbtpage.c.

1076{
1077 /*
1078 * Buffer is pinned and locked, which means that it is expected to be
1079 * defined and addressable. Check that proactively.
1080 */
1082
1083 /* LockBuffer() asserts that pin is held by this backend */
1085
1086 if (!RelationUsesLocalBuffers(rel))
1088}
@ BUFFER_LOCK_UNLOCK
Definition bufmgr.h:205
#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, 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_relbuf(), _bt_search(), _bt_set_cleanup_info(), and _bt_unlink_halfdead_page().

◆ _bt_upgradelockbufcleanup()

void _bt_upgradelockbufcleanup ( Relation  rel,
Buffer  buf 
)

Definition at line 1114 of file nbtpage.c.

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

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:243

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:307

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

Referenced by btvacuumcleanup().