LCOV - code coverage report
Current view: top level - src/backend/access/nbtree - nbtpage.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 424 592 71.6 %
Date: 2017-09-29 13:40:31 Functions: 16 18 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * nbtpage.c
       4             :  *    BTree-specific page management code for the Postgres btree access
       5             :  *    method.
       6             :  *
       7             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  *
      11             :  * IDENTIFICATION
      12             :  *    src/backend/access/nbtree/nbtpage.c
      13             :  *
      14             :  *  NOTES
      15             :  *     Postgres btree pages look like ordinary relation pages.  The opaque
      16             :  *     data at high addresses includes pointers to left and right siblings
      17             :  *     and flag data describing page state.  The first page in a btree, page
      18             :  *     zero, is special -- it stores meta-information describing the tree.
      19             :  *     Pages one and higher store the actual tree data.
      20             :  *
      21             :  *-------------------------------------------------------------------------
      22             :  */
      23             : #include "postgres.h"
      24             : 
      25             : #include "access/nbtree.h"
      26             : #include "access/nbtxlog.h"
      27             : #include "access/transam.h"
      28             : #include "access/xlog.h"
      29             : #include "access/xloginsert.h"
      30             : #include "miscadmin.h"
      31             : #include "storage/indexfsm.h"
      32             : #include "storage/lmgr.h"
      33             : #include "storage/predicate.h"
      34             : #include "utils/snapmgr.h"
      35             : 
      36             : static bool _bt_mark_page_halfdead(Relation rel, Buffer buf, BTStack stack);
      37             : static bool _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf,
      38             :                          bool *rightsib_empty);
      39             : static bool _bt_lock_branch_parent(Relation rel, BlockNumber child,
      40             :                        BTStack stack, Buffer *topparent, OffsetNumber *topoff,
      41             :                        BlockNumber *target, BlockNumber *rightsib);
      42             : static void _bt_log_reuse_page(Relation rel, BlockNumber blkno,
      43             :                    TransactionId latestRemovedXid);
      44             : 
      45             : /*
      46             :  *  _bt_initmetapage() -- Fill a page buffer with a correct metapage image
      47             :  */
      48             : void
      49        1433 : _bt_initmetapage(Page page, BlockNumber rootbknum, uint32 level)
      50             : {
      51             :     BTMetaPageData *metad;
      52             :     BTPageOpaque metaopaque;
      53             : 
      54        1433 :     _bt_pageinit(page, BLCKSZ);
      55             : 
      56        1433 :     metad = BTPageGetMeta(page);
      57        1433 :     metad->btm_magic = BTREE_MAGIC;
      58        1433 :     metad->btm_version = BTREE_VERSION;
      59        1433 :     metad->btm_root = rootbknum;
      60        1433 :     metad->btm_level = level;
      61        1433 :     metad->btm_fastroot = rootbknum;
      62        1433 :     metad->btm_fastlevel = level;
      63             : 
      64        1433 :     metaopaque = (BTPageOpaque) PageGetSpecialPointer(page);
      65        1433 :     metaopaque->btpo_flags = BTP_META;
      66             : 
      67             :     /*
      68             :      * Set pd_lower just past the end of the metadata.  This is not essential
      69             :      * but it makes the page look compressible to xlog.c.
      70             :      */
      71        1433 :     ((PageHeader) page)->pd_lower =
      72        1433 :         ((char *) metad + sizeof(BTMetaPageData)) - (char *) page;
      73        1433 : }
      74             : 
      75             : /*
      76             :  *  _bt_getroot() -- Get the root page of the btree.
      77             :  *
      78             :  *      Since the root page can move around the btree file, we have to read
      79             :  *      its location from the metadata page, and then read the root page
      80             :  *      itself.  If no root page exists yet, we have to create one.  The
      81             :  *      standard class of race conditions exists here; I think I covered
      82             :  *      them all in the Hopi Indian rain dance of lock requests below.
      83             :  *
      84             :  *      The access type parameter (BT_READ or BT_WRITE) controls whether
      85             :  *      a new root page will be created or not.  If access = BT_READ,
      86             :  *      and no root page exists, we just return InvalidBuffer.  For
      87             :  *      BT_WRITE, we try to create the root page if it doesn't exist.
      88             :  *      NOTE that the returned root page will have only a read lock set
      89             :  *      on it even if access = BT_WRITE!
      90             :  *
      91             :  *      The returned page is not necessarily the true root --- it could be
      92             :  *      a "fast root" (a page that is alone in its level due to deletions).
      93             :  *      Also, if the root page is split while we are "in flight" to it,
      94             :  *      what we will return is the old root, which is now just the leftmost
      95             :  *      page on a probably-not-very-wide level.  For most purposes this is
      96             :  *      as good as or better than the true root, so we do not bother to
      97             :  *      insist on finding the true root.  We do, however, guarantee to
      98             :  *      return a live (not deleted or half-dead) page.
      99             :  *
     100             :  *      On successful return, the root page is pinned and read-locked.
     101             :  *      The metadata page is not locked or pinned on exit.
     102             :  */
     103             : Buffer
     104      593576 : _bt_getroot(Relation rel, int access)
     105             : {
     106             :     Buffer      metabuf;
     107             :     Page        metapg;
     108             :     BTPageOpaque metaopaque;
     109             :     Buffer      rootbuf;
     110             :     Page        rootpage;
     111             :     BTPageOpaque rootopaque;
     112             :     BlockNumber rootblkno;
     113             :     uint32      rootlevel;
     114             :     BTMetaPageData *metad;
     115             : 
     116             :     /*
     117             :      * Try to use previously-cached metapage data to find the root.  This
     118             :      * normally saves one buffer access per index search, which is a very
     119             :      * helpful savings in bufmgr traffic and hence contention.
     120             :      */
     121      593576 :     if (rel->rd_amcache != NULL)
     122             :     {
     123      569772 :         metad = (BTMetaPageData *) rel->rd_amcache;
     124             :         /* We shouldn't have cached it if any of these fail */
     125      569772 :         Assert(metad->btm_magic == BTREE_MAGIC);
     126      569772 :         Assert(metad->btm_version == BTREE_VERSION);
     127      569772 :         Assert(metad->btm_root != P_NONE);
     128             : 
     129      569772 :         rootblkno = metad->btm_fastroot;
     130      569772 :         Assert(rootblkno != P_NONE);
     131      569772 :         rootlevel = metad->btm_fastlevel;
     132             : 
     133      569772 :         rootbuf = _bt_getbuf(rel, rootblkno, BT_READ);
     134      569772 :         rootpage = BufferGetPage(rootbuf);
     135      569772 :         rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
     136             : 
     137             :         /*
     138             :          * Since the cache might be stale, we check the page more carefully
     139             :          * here than normal.  We *must* check that it's not deleted. If it's
     140             :          * not alone on its level, then we reject too --- this may be overly
     141             :          * paranoid but better safe than sorry.  Note we don't check P_ISROOT,
     142             :          * because that's not set in a "fast root".
     143             :          */
     144     1139544 :         if (!P_IGNORE(rootopaque) &&
     145     1139544 :             rootopaque->btpo.level == rootlevel &&
     146     1139544 :             P_LEFTMOST(rootopaque) &&
     147      569772 :             P_RIGHTMOST(rootopaque))
     148             :         {
     149             :             /* OK, accept cached page as the root */
     150      569727 :             return rootbuf;
     151             :         }
     152          45 :         _bt_relbuf(rel, rootbuf);
     153             :         /* Cache is stale, throw it away */
     154          45 :         if (rel->rd_amcache)
     155          45 :             pfree(rel->rd_amcache);
     156          45 :         rel->rd_amcache = NULL;
     157             :     }
     158             : 
     159       23849 :     metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
     160       23849 :     metapg = BufferGetPage(metabuf);
     161       23849 :     metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
     162       23849 :     metad = BTPageGetMeta(metapg);
     163             : 
     164             :     /* sanity-check the metapage */
     165       47698 :     if (!(metaopaque->btpo_flags & BTP_META) ||
     166       23849 :         metad->btm_magic != BTREE_MAGIC)
     167           0 :         ereport(ERROR,
     168             :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     169             :                  errmsg("index \"%s\" is not a btree",
     170             :                         RelationGetRelationName(rel))));
     171             : 
     172       23849 :     if (metad->btm_version != BTREE_VERSION)
     173           0 :         ereport(ERROR,
     174             :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     175             :                  errmsg("version mismatch in index \"%s\": file version %d, code version %d",
     176             :                         RelationGetRelationName(rel),
     177             :                         metad->btm_version, BTREE_VERSION)));
     178             : 
     179             :     /* if no root page initialized yet, do it */
     180       23849 :     if (metad->btm_root == P_NONE)
     181             :     {
     182             :         /* If access = BT_READ, caller doesn't want us to create root yet */
     183       12103 :         if (access == BT_READ)
     184             :         {
     185       11804 :             _bt_relbuf(rel, metabuf);
     186       11804 :             return InvalidBuffer;
     187             :         }
     188             : 
     189             :         /* trade in our read lock for a write lock */
     190         299 :         LockBuffer(metabuf, BUFFER_LOCK_UNLOCK);
     191         299 :         LockBuffer(metabuf, BT_WRITE);
     192             : 
     193             :         /*
     194             :          * Race condition:  if someone else initialized the metadata between
     195             :          * the time we released the read lock and acquired the write lock, we
     196             :          * must avoid doing it again.
     197             :          */
     198         299 :         if (metad->btm_root != P_NONE)
     199             :         {
     200             :             /*
     201             :              * Metadata initialized by someone else.  In order to guarantee no
     202             :              * deadlocks, we have to release the metadata page and start all
     203             :              * over again.  (Is that really true? But it's hardly worth trying
     204             :              * to optimize this case.)
     205             :              */
     206           0 :             _bt_relbuf(rel, metabuf);
     207           0 :             return _bt_getroot(rel, access);
     208             :         }
     209             : 
     210             :         /*
     211             :          * Get, initialize, write, and leave a lock of the appropriate type on
     212             :          * the new root page.  Since this is the first page in the tree, it's
     213             :          * a leaf as well as the root.
     214             :          */
     215         299 :         rootbuf = _bt_getbuf(rel, P_NEW, BT_WRITE);
     216         299 :         rootblkno = BufferGetBlockNumber(rootbuf);
     217         299 :         rootpage = BufferGetPage(rootbuf);
     218         299 :         rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
     219         299 :         rootopaque->btpo_prev = rootopaque->btpo_next = P_NONE;
     220         299 :         rootopaque->btpo_flags = (BTP_LEAF | BTP_ROOT);
     221         299 :         rootopaque->btpo.level = 0;
     222         299 :         rootopaque->btpo_cycleid = 0;
     223             : 
     224             :         /* NO ELOG(ERROR) till meta is updated */
     225         299 :         START_CRIT_SECTION();
     226             : 
     227         299 :         metad->btm_root = rootblkno;
     228         299 :         metad->btm_level = 0;
     229         299 :         metad->btm_fastroot = rootblkno;
     230         299 :         metad->btm_fastlevel = 0;
     231             : 
     232         299 :         MarkBufferDirty(rootbuf);
     233         299 :         MarkBufferDirty(metabuf);
     234             : 
     235             :         /* XLOG stuff */
     236         299 :         if (RelationNeedsWAL(rel))
     237             :         {
     238             :             xl_btree_newroot xlrec;
     239             :             XLogRecPtr  recptr;
     240             :             xl_btree_metadata md;
     241             : 
     242         250 :             XLogBeginInsert();
     243         250 :             XLogRegisterBuffer(0, rootbuf, REGBUF_WILL_INIT);
     244         250 :             XLogRegisterBuffer(2, metabuf, REGBUF_WILL_INIT);
     245             : 
     246         250 :             md.root = rootblkno;
     247         250 :             md.level = 0;
     248         250 :             md.fastroot = rootblkno;
     249         250 :             md.fastlevel = 0;
     250             : 
     251         250 :             XLogRegisterBufData(2, (char *) &md, sizeof(xl_btree_metadata));
     252             : 
     253         250 :             xlrec.rootblk = rootblkno;
     254         250 :             xlrec.level = 0;
     255             : 
     256         250 :             XLogRegisterData((char *) &xlrec, SizeOfBtreeNewroot);
     257             : 
     258         250 :             recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_NEWROOT);
     259             : 
     260         250 :             PageSetLSN(rootpage, recptr);
     261         250 :             PageSetLSN(metapg, recptr);
     262             :         }
     263             : 
     264         299 :         END_CRIT_SECTION();
     265             : 
     266             :         /*
     267             :          * swap root write lock for read lock.  There is no danger of anyone
     268             :          * else accessing the new root page while it's unlocked, since no one
     269             :          * else knows where it is yet.
     270             :          */
     271         299 :         LockBuffer(rootbuf, BUFFER_LOCK_UNLOCK);
     272         299 :         LockBuffer(rootbuf, BT_READ);
     273             : 
     274             :         /* okay, metadata is correct, release lock on it */
     275         299 :         _bt_relbuf(rel, metabuf);
     276             :     }
     277             :     else
     278             :     {
     279       11746 :         rootblkno = metad->btm_fastroot;
     280       11746 :         Assert(rootblkno != P_NONE);
     281       11746 :         rootlevel = metad->btm_fastlevel;
     282             : 
     283             :         /*
     284             :          * Cache the metapage data for next time
     285             :          */
     286       11746 :         rel->rd_amcache = MemoryContextAlloc(rel->rd_indexcxt,
     287             :                                              sizeof(BTMetaPageData));
     288       11746 :         memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
     289             : 
     290             :         /*
     291             :          * We are done with the metapage; arrange to release it via first
     292             :          * _bt_relandgetbuf call
     293             :          */
     294       11746 :         rootbuf = metabuf;
     295             : 
     296             :         for (;;)
     297             :         {
     298       11746 :             rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, BT_READ);
     299       11746 :             rootpage = BufferGetPage(rootbuf);
     300       11746 :             rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
     301             : 
     302       11746 :             if (!P_IGNORE(rootopaque))
     303       11746 :                 break;
     304             : 
     305             :             /* it's dead, Jim.  step right one page */
     306           0 :             if (P_RIGHTMOST(rootopaque))
     307           0 :                 elog(ERROR, "no live root page found in index \"%s\"",
     308             :                      RelationGetRelationName(rel));
     309           0 :             rootblkno = rootopaque->btpo_next;
     310           0 :         }
     311             : 
     312             :         /* Note: can't check btpo.level on deleted pages */
     313       11746 :         if (rootopaque->btpo.level != rootlevel)
     314           0 :             elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
     315             :                  rootblkno, RelationGetRelationName(rel),
     316             :                  rootopaque->btpo.level, rootlevel);
     317             :     }
     318             : 
     319             :     /*
     320             :      * By here, we have a pin and read lock on the root page, and no lock set
     321             :      * on the metadata page.  Return the root page's buffer.
     322             :      */
     323       12045 :     return rootbuf;
     324             : }
     325             : 
     326             : /*
     327             :  *  _bt_gettrueroot() -- Get the true root page of the btree.
     328             :  *
     329             :  *      This is the same as the BT_READ case of _bt_getroot(), except
     330             :  *      we follow the true-root link not the fast-root link.
     331             :  *
     332             :  * By the time we acquire lock on the root page, it might have been split and
     333             :  * not be the true root anymore.  This is okay for the present uses of this
     334             :  * routine; we only really need to be able to move up at least one tree level
     335             :  * from whatever non-root page we were at.  If we ever do need to lock the
     336             :  * one true root page, we could loop here, re-reading the metapage on each
     337             :  * failure.  (Note that it wouldn't do to hold the lock on the metapage while
     338             :  * moving to the root --- that'd deadlock against any concurrent root split.)
     339             :  */
     340             : Buffer
     341           0 : _bt_gettrueroot(Relation rel)
     342             : {
     343             :     Buffer      metabuf;
     344             :     Page        metapg;
     345             :     BTPageOpaque metaopaque;
     346             :     Buffer      rootbuf;
     347             :     Page        rootpage;
     348             :     BTPageOpaque rootopaque;
     349             :     BlockNumber rootblkno;
     350             :     uint32      rootlevel;
     351             :     BTMetaPageData *metad;
     352             : 
     353             :     /*
     354             :      * We don't try to use cached metapage data here, since (a) this path is
     355             :      * not performance-critical, and (b) if we are here it suggests our cache
     356             :      * is out-of-date anyway.  In light of point (b), it's probably safest to
     357             :      * actively flush any cached metapage info.
     358             :      */
     359           0 :     if (rel->rd_amcache)
     360           0 :         pfree(rel->rd_amcache);
     361           0 :     rel->rd_amcache = NULL;
     362             : 
     363           0 :     metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
     364           0 :     metapg = BufferGetPage(metabuf);
     365           0 :     metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
     366           0 :     metad = BTPageGetMeta(metapg);
     367             : 
     368           0 :     if (!(metaopaque->btpo_flags & BTP_META) ||
     369           0 :         metad->btm_magic != BTREE_MAGIC)
     370           0 :         ereport(ERROR,
     371             :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     372             :                  errmsg("index \"%s\" is not a btree",
     373             :                         RelationGetRelationName(rel))));
     374             : 
     375           0 :     if (metad->btm_version != BTREE_VERSION)
     376           0 :         ereport(ERROR,
     377             :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     378             :                  errmsg("version mismatch in index \"%s\": file version %d, code version %d",
     379             :                         RelationGetRelationName(rel),
     380             :                         metad->btm_version, BTREE_VERSION)));
     381             : 
     382             :     /* if no root page initialized yet, fail */
     383           0 :     if (metad->btm_root == P_NONE)
     384             :     {
     385           0 :         _bt_relbuf(rel, metabuf);
     386           0 :         return InvalidBuffer;
     387             :     }
     388             : 
     389           0 :     rootblkno = metad->btm_root;
     390           0 :     rootlevel = metad->btm_level;
     391             : 
     392             :     /*
     393             :      * We are done with the metapage; arrange to release it via first
     394             :      * _bt_relandgetbuf call
     395             :      */
     396           0 :     rootbuf = metabuf;
     397             : 
     398             :     for (;;)
     399             :     {
     400           0 :         rootbuf = _bt_relandgetbuf(rel, rootbuf, rootblkno, BT_READ);
     401           0 :         rootpage = BufferGetPage(rootbuf);
     402           0 :         rootopaque = (BTPageOpaque) PageGetSpecialPointer(rootpage);
     403             : 
     404           0 :         if (!P_IGNORE(rootopaque))
     405           0 :             break;
     406             : 
     407             :         /* it's dead, Jim.  step right one page */
     408           0 :         if (P_RIGHTMOST(rootopaque))
     409           0 :             elog(ERROR, "no live root page found in index \"%s\"",
     410             :                  RelationGetRelationName(rel));
     411           0 :         rootblkno = rootopaque->btpo_next;
     412           0 :     }
     413             : 
     414             :     /* Note: can't check btpo.level on deleted pages */
     415           0 :     if (rootopaque->btpo.level != rootlevel)
     416           0 :         elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
     417             :              rootblkno, RelationGetRelationName(rel),
     418             :              rootopaque->btpo.level, rootlevel);
     419             : 
     420           0 :     return rootbuf;
     421             : }
     422             : 
     423             : /*
     424             :  *  _bt_getrootheight() -- Get the height of the btree search tree.
     425             :  *
     426             :  *      We return the level (counting from zero) of the current fast root.
     427             :  *      This represents the number of tree levels we'd have to descend through
     428             :  *      to start any btree index search.
     429             :  *
     430             :  *      This is used by the planner for cost-estimation purposes.  Since it's
     431             :  *      only an estimate, slightly-stale data is fine, hence we don't worry
     432             :  *      about updating previously cached data.
     433             :  */
     434             : int
     435       21110 : _bt_getrootheight(Relation rel)
     436             : {
     437             :     BTMetaPageData *metad;
     438             : 
     439             :     /*
     440             :      * We can get what we need from the cached metapage data.  If it's not
     441             :      * cached yet, load it.  Sanity checks here must match _bt_getroot().
     442             :      */
     443       21110 :     if (rel->rd_amcache == NULL)
     444             :     {
     445             :         Buffer      metabuf;
     446             :         Page        metapg;
     447             :         BTPageOpaque metaopaque;
     448             : 
     449        1971 :         metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_READ);
     450        1971 :         metapg = BufferGetPage(metabuf);
     451        1971 :         metaopaque = (BTPageOpaque) PageGetSpecialPointer(metapg);
     452        1971 :         metad = BTPageGetMeta(metapg);
     453             : 
     454             :         /* sanity-check the metapage */
     455        3942 :         if (!(metaopaque->btpo_flags & BTP_META) ||
     456        1971 :             metad->btm_magic != BTREE_MAGIC)
     457           0 :             ereport(ERROR,
     458             :                     (errcode(ERRCODE_INDEX_CORRUPTED),
     459             :                      errmsg("index \"%s\" is not a btree",
     460             :                             RelationGetRelationName(rel))));
     461             : 
     462        1971 :         if (metad->btm_version != BTREE_VERSION)
     463           0 :             ereport(ERROR,
     464             :                     (errcode(ERRCODE_INDEX_CORRUPTED),
     465             :                      errmsg("version mismatch in index \"%s\": file version %d, code version %d",
     466             :                             RelationGetRelationName(rel),
     467             :                             metad->btm_version, BTREE_VERSION)));
     468             : 
     469             :         /*
     470             :          * If there's no root page yet, _bt_getroot() doesn't expect a cache
     471             :          * to be made, so just stop here and report the index height is zero.
     472             :          * (XXX perhaps _bt_getroot() should be changed to allow this case.)
     473             :          */
     474        1971 :         if (metad->btm_root == P_NONE)
     475             :         {
     476        1232 :             _bt_relbuf(rel, metabuf);
     477        1232 :             return 0;
     478             :         }
     479             : 
     480             :         /*
     481             :          * Cache the metapage data for next time
     482             :          */
     483         739 :         rel->rd_amcache = MemoryContextAlloc(rel->rd_indexcxt,
     484             :                                              sizeof(BTMetaPageData));
     485         739 :         memcpy(rel->rd_amcache, metad, sizeof(BTMetaPageData));
     486             : 
     487         739 :         _bt_relbuf(rel, metabuf);
     488             :     }
     489             : 
     490       19878 :     metad = (BTMetaPageData *) rel->rd_amcache;
     491             :     /* We shouldn't have cached it if any of these fail */
     492       19878 :     Assert(metad->btm_magic == BTREE_MAGIC);
     493       19878 :     Assert(metad->btm_version == BTREE_VERSION);
     494       19878 :     Assert(metad->btm_fastroot != P_NONE);
     495             : 
     496       19878 :     return metad->btm_fastlevel;
     497             : }
     498             : 
     499             : /*
     500             :  *  _bt_checkpage() -- Verify that a freshly-read page looks sane.
     501             :  */
     502             : void
     503     1099593 : _bt_checkpage(Relation rel, Buffer buf)
     504             : {
     505     1099593 :     Page        page = BufferGetPage(buf);
     506             : 
     507             :     /*
     508             :      * ReadBuffer verifies that every newly-read page passes
     509             :      * PageHeaderIsValid, which means it either contains a reasonably sane
     510             :      * page header or is all-zero.  We have to defend against the all-zero
     511             :      * case, however.
     512             :      */
     513     1099593 :     if (PageIsNew(page))
     514           0 :         ereport(ERROR,
     515             :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     516             :                  errmsg("index \"%s\" contains unexpected zero page at block %u",
     517             :                         RelationGetRelationName(rel),
     518             :                         BufferGetBlockNumber(buf)),
     519             :                  errhint("Please REINDEX it.")));
     520             : 
     521             :     /*
     522             :      * Additionally check that the special area looks sane.
     523             :      */
     524     1099593 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(BTPageOpaqueData)))
     525           0 :         ereport(ERROR,
     526             :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     527             :                  errmsg("index \"%s\" contains corrupted page at block %u",
     528             :                         RelationGetRelationName(rel),
     529             :                         BufferGetBlockNumber(buf)),
     530             :                  errhint("Please REINDEX it.")));
     531     1099593 : }
     532             : 
     533             : /*
     534             :  * Log the reuse of a page from the FSM.
     535             :  */
     536             : static void
     537           0 : _bt_log_reuse_page(Relation rel, BlockNumber blkno, TransactionId latestRemovedXid)
     538             : {
     539             :     xl_btree_reuse_page xlrec_reuse;
     540             : 
     541             :     /*
     542             :      * Note that we don't register the buffer with the record, because this
     543             :      * operation doesn't modify the page. This record only exists to provide a
     544             :      * conflict point for Hot Standby.
     545             :      */
     546             : 
     547             :     /* XLOG stuff */
     548           0 :     xlrec_reuse.node = rel->rd_node;
     549           0 :     xlrec_reuse.block = blkno;
     550           0 :     xlrec_reuse.latestRemovedXid = latestRemovedXid;
     551             : 
     552           0 :     XLogBeginInsert();
     553           0 :     XLogRegisterData((char *) &xlrec_reuse, SizeOfBtreeReusePage);
     554             : 
     555           0 :     XLogInsert(RM_BTREE_ID, XLOG_BTREE_REUSE_PAGE);
     556           0 : }
     557             : 
     558             : /*
     559             :  *  _bt_getbuf() -- Get a buffer by block number for read or write.
     560             :  *
     561             :  *      blkno == P_NEW means to get an unallocated index page.  The page
     562             :  *      will be initialized before returning it.
     563             :  *
     564             :  *      When this routine returns, the appropriate lock is set on the
     565             :  *      requested buffer and its reference count has been incremented
     566             :  *      (ie, the buffer is "locked and pinned").  Also, we apply
     567             :  *      _bt_checkpage to sanity-check the page (except in P_NEW case).
     568             :  */
     569             : Buffer
     570      604413 : _bt_getbuf(Relation rel, BlockNumber blkno, int access)
     571             : {
     572             :     Buffer      buf;
     573             : 
     574      604413 :     if (blkno != P_NEW)
     575             :     {
     576             :         /* Read an existing block of the relation */
     577      603195 :         buf = ReadBuffer(rel, blkno);
     578      603195 :         LockBuffer(buf, access);
     579      603195 :         _bt_checkpage(rel, buf);
     580             :     }
     581             :     else
     582             :     {
     583             :         bool        needLock;
     584             :         Page        page;
     585             : 
     586        1218 :         Assert(access == BT_WRITE);
     587             : 
     588             :         /*
     589             :          * First see if the FSM knows of any free pages.
     590             :          *
     591             :          * We can't trust the FSM's report unreservedly; we have to check that
     592             :          * the page is still free.  (For example, an already-free page could
     593             :          * have been re-used between the time the last VACUUM scanned it and
     594             :          * the time the VACUUM made its FSM updates.)
     595             :          *
     596             :          * In fact, it's worse than that: we can't even assume that it's safe
     597             :          * to take a lock on the reported page.  If somebody else has a lock
     598             :          * on it, or even worse our own caller does, we could deadlock.  (The
     599             :          * own-caller scenario is actually not improbable. Consider an index
     600             :          * on a serial or timestamp column.  Nearly all splits will be at the
     601             :          * rightmost page, so it's entirely likely that _bt_split will call us
     602             :          * while holding a lock on the page most recently acquired from FSM. A
     603             :          * VACUUM running concurrently with the previous split could well have
     604             :          * placed that page back in FSM.)
     605             :          *
     606             :          * To get around that, we ask for only a conditional lock on the
     607             :          * reported page.  If we fail, then someone else is using the page,
     608             :          * and we may reasonably assume it's not free.  (If we happen to be
     609             :          * wrong, the worst consequence is the page will be lost to use till
     610             :          * the next VACUUM, which is no big problem.)
     611             :          */
     612             :         for (;;)
     613             :         {
     614        1218 :             blkno = GetFreeIndexPage(rel);
     615        1218 :             if (blkno == InvalidBlockNumber)
     616        1218 :                 break;
     617           0 :             buf = ReadBuffer(rel, blkno);
     618           0 :             if (ConditionalLockBuffer(buf))
     619             :             {
     620           0 :                 page = BufferGetPage(buf);
     621           0 :                 if (_bt_page_recyclable(page))
     622             :                 {
     623             :                     /*
     624             :                      * If we are generating WAL for Hot Standby then create a
     625             :                      * WAL record that will allow us to conflict with queries
     626             :                      * running on standby.
     627             :                      */
     628           0 :                     if (XLogStandbyInfoActive() && RelationNeedsWAL(rel))
     629             :                     {
     630           0 :                         BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
     631             : 
     632           0 :                         _bt_log_reuse_page(rel, blkno, opaque->btpo.xact);
     633             :                     }
     634             : 
     635             :                     /* Okay to use page.  Re-initialize and return it */
     636           0 :                     _bt_pageinit(page, BufferGetPageSize(buf));
     637           0 :                     return buf;
     638             :                 }
     639           0 :                 elog(DEBUG2, "FSM returned nonrecyclable page");
     640           0 :                 _bt_relbuf(rel, buf);
     641             :             }
     642             :             else
     643             :             {
     644           0 :                 elog(DEBUG2, "FSM returned nonlockable page");
     645             :                 /* couldn't get lock, so just drop pin */
     646           0 :                 ReleaseBuffer(buf);
     647             :             }
     648           0 :         }
     649             : 
     650             :         /*
     651             :          * Extend the relation by one page.
     652             :          *
     653             :          * We have to use a lock to ensure no one else is extending the rel at
     654             :          * the same time, else we will both try to initialize the same new
     655             :          * page.  We can skip locking for new or temp relations, however,
     656             :          * since no one else could be accessing them.
     657             :          */
     658        1218 :         needLock = !RELATION_IS_LOCAL(rel);
     659             : 
     660        1218 :         if (needLock)
     661        1154 :             LockRelationForExtension(rel, ExclusiveLock);
     662             : 
     663        1218 :         buf = ReadBuffer(rel, P_NEW);
     664             : 
     665             :         /* Acquire buffer lock on new page */
     666        1218 :         LockBuffer(buf, BT_WRITE);
     667             : 
     668             :         /*
     669             :          * Release the file-extension lock; it's now OK for someone else to
     670             :          * extend the relation some more.  Note that we cannot release this
     671             :          * lock before we have buffer lock on the new page, or we risk a race
     672             :          * condition against btvacuumscan --- see comments therein.
     673             :          */
     674        1218 :         if (needLock)
     675        1154 :             UnlockRelationForExtension(rel, ExclusiveLock);
     676             : 
     677             :         /* Initialize the new page before returning it */
     678        1218 :         page = BufferGetPage(buf);
     679        1218 :         Assert(PageIsNew(page));
     680        1218 :         _bt_pageinit(page, BufferGetPageSize(buf));
     681             :     }
     682             : 
     683             :     /* ref count and lock type are correct */
     684      604413 :     return buf;
     685             : }
     686             : 
     687             : /*
     688             :  *  _bt_relandgetbuf() -- release a locked buffer and get another one.
     689             :  *
     690             :  * This is equivalent to _bt_relbuf followed by _bt_getbuf, with the
     691             :  * exception that blkno may not be P_NEW.  Also, if obuf is InvalidBuffer
     692             :  * then it reduces to just _bt_getbuf; allowing this case simplifies some
     693             :  * callers.
     694             :  *
     695             :  * The original motivation for using this was to avoid two entries to the
     696             :  * bufmgr when one would do.  However, now it's mainly just a notational
     697             :  * convenience.  The only case where it saves work over _bt_relbuf/_bt_getbuf
     698             :  * is when the target page is the same one already in the buffer.
     699             :  */
     700             : Buffer
     701      494575 : _bt_relandgetbuf(Relation rel, Buffer obuf, BlockNumber blkno, int access)
     702             : {
     703             :     Buffer      buf;
     704             : 
     705      494575 :     Assert(blkno != P_NEW);
     706      494575 :     if (BufferIsValid(obuf))
     707      492740 :         LockBuffer(obuf, BUFFER_LOCK_UNLOCK);
     708      494575 :     buf = ReleaseAndReadBuffer(obuf, rel, blkno);
     709      494575 :     LockBuffer(buf, access);
     710      494575 :     _bt_checkpage(rel, buf);
     711      494575 :     return buf;
     712             : }
     713             : 
     714             : /*
     715             :  *  _bt_relbuf() -- release a locked buffer.
     716             :  *
     717             :  * Lock and pin (refcount) are both dropped.
     718             :  */
     719             : void
     720      226663 : _bt_relbuf(Relation rel, Buffer buf)
     721             : {
     722      226663 :     UnlockReleaseBuffer(buf);
     723      226663 : }
     724             : 
     725             : /*
     726             :  *  _bt_pageinit() -- Initialize a new page.
     727             :  *
     728             :  * On return, the page header is initialized; data space is empty;
     729             :  * special space is zeroed out.
     730             :  */
     731             : void
     732        5061 : _bt_pageinit(Page page, Size size)
     733             : {
     734        5061 :     PageInit(page, size, sizeof(BTPageOpaqueData));
     735        5061 : }
     736             : 
     737             : /*
     738             :  *  _bt_page_recyclable() -- Is an existing page recyclable?
     739             :  *
     740             :  * This exists to make sure _bt_getbuf and btvacuumscan have the same
     741             :  * policy about whether a page is safe to re-use.
     742             :  */
     743             : bool
     744        1679 : _bt_page_recyclable(Page page)
     745             : {
     746             :     BTPageOpaque opaque;
     747             : 
     748             :     /*
     749             :      * It's possible to find an all-zeroes page in an index --- for example, a
     750             :      * backend might successfully extend the relation one page and then crash
     751             :      * before it is able to make a WAL entry for adding the page. If we find a
     752             :      * zeroed page then reclaim it.
     753             :      */
     754        1679 :     if (PageIsNew(page))
     755           0 :         return true;
     756             : 
     757             :     /*
     758             :      * Otherwise, recycle if deleted and too old to have any processes
     759             :      * interested in it.
     760             :      */
     761        1679 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
     762        1679 :     if (P_ISDELETED(opaque) &&
     763           0 :         TransactionIdPrecedes(opaque->btpo.xact, RecentGlobalXmin))
     764           0 :         return true;
     765        1679 :     return false;
     766             : }
     767             : 
     768             : /*
     769             :  * Delete item(s) from a btree page during VACUUM.
     770             :  *
     771             :  * This must only be used for deleting leaf items.  Deleting an item on a
     772             :  * non-leaf page has to be done as part of an atomic action that includes
     773             :  * deleting the page it points to.
     774             :  *
     775             :  * This routine assumes that the caller has pinned and locked the buffer.
     776             :  * Also, the given itemnos *must* appear in increasing order in the array.
     777             :  *
     778             :  * We record VACUUMs and b-tree deletes differently in WAL. InHotStandby
     779             :  * we need to be able to pin all of the blocks in the btree in physical
     780             :  * order when replaying the effects of a VACUUM, just as we do for the
     781             :  * original VACUUM itself. lastBlockVacuumed allows us to tell whether an
     782             :  * intermediate range of blocks has had no changes at all by VACUUM,
     783             :  * and so must be scanned anyway during replay. We always write a WAL record
     784             :  * for the last block in the index, whether or not it contained any items
     785             :  * to be removed. This allows us to scan right up to end of index to
     786             :  * ensure correct locking.
     787             :  */
     788             : void
     789         563 : _bt_delitems_vacuum(Relation rel, Buffer buf,
     790             :                     OffsetNumber *itemnos, int nitems,
     791             :                     BlockNumber lastBlockVacuumed)
     792             : {
     793         563 :     Page        page = BufferGetPage(buf);
     794             :     BTPageOpaque opaque;
     795             : 
     796             :     /* No ereport(ERROR) until changes are logged */
     797         563 :     START_CRIT_SECTION();
     798             : 
     799             :     /* Fix the page */
     800         563 :     if (nitems > 0)
     801         419 :         PageIndexMultiDelete(page, itemnos, nitems);
     802             : 
     803             :     /*
     804             :      * We can clear the vacuum cycle ID since this page has certainly been
     805             :      * processed by the current vacuum scan.
     806             :      */
     807         563 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
     808         563 :     opaque->btpo_cycleid = 0;
     809             : 
     810             :     /*
     811             :      * Mark the page as not containing any LP_DEAD items.  This is not
     812             :      * certainly true (there might be some that have recently been marked, but
     813             :      * weren't included in our target-item list), but it will almost always be
     814             :      * true and it doesn't seem worth an additional page scan to check it.
     815             :      * Remember that BTP_HAS_GARBAGE is only a hint anyway.
     816             :      */
     817         563 :     opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
     818             : 
     819         563 :     MarkBufferDirty(buf);
     820             : 
     821             :     /* XLOG stuff */
     822         563 :     if (RelationNeedsWAL(rel))
     823             :     {
     824             :         XLogRecPtr  recptr;
     825             :         xl_btree_vacuum xlrec_vacuum;
     826             : 
     827         563 :         xlrec_vacuum.lastBlockVacuumed = lastBlockVacuumed;
     828             : 
     829         563 :         XLogBeginInsert();
     830         563 :         XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
     831         563 :         XLogRegisterData((char *) &xlrec_vacuum, SizeOfBtreeVacuum);
     832             : 
     833             :         /*
     834             :          * The target-offsets array is not in the buffer, but pretend that it
     835             :          * is.  When XLogInsert stores the whole buffer, the offsets array
     836             :          * need not be stored too.
     837             :          */
     838         563 :         if (nitems > 0)
     839         419 :             XLogRegisterBufData(0, (char *) itemnos, nitems * sizeof(OffsetNumber));
     840             : 
     841         563 :         recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_VACUUM);
     842             : 
     843         563 :         PageSetLSN(page, recptr);
     844             :     }
     845             : 
     846         563 :     END_CRIT_SECTION();
     847         563 : }
     848             : 
     849             : /*
     850             :  * Delete item(s) from a btree page during single-page cleanup.
     851             :  *
     852             :  * As above, must only be used on leaf pages.
     853             :  *
     854             :  * This routine assumes that the caller has pinned and locked the buffer.
     855             :  * Also, the given itemnos *must* appear in increasing order in the array.
     856             :  *
     857             :  * This is nearly the same as _bt_delitems_vacuum as far as what it does to
     858             :  * the page, but the WAL logging considerations are quite different.  See
     859             :  * comments for _bt_delitems_vacuum.
     860             :  */
     861             : void
     862         262 : _bt_delitems_delete(Relation rel, Buffer buf,
     863             :                     OffsetNumber *itemnos, int nitems,
     864             :                     Relation heapRel)
     865             : {
     866         262 :     Page        page = BufferGetPage(buf);
     867             :     BTPageOpaque opaque;
     868             : 
     869             :     /* Shouldn't be called unless there's something to do */
     870         262 :     Assert(nitems > 0);
     871             : 
     872             :     /* No ereport(ERROR) until changes are logged */
     873         262 :     START_CRIT_SECTION();
     874             : 
     875             :     /* Fix the page */
     876         262 :     PageIndexMultiDelete(page, itemnos, nitems);
     877             : 
     878             :     /*
     879             :      * Unlike _bt_delitems_vacuum, we *must not* clear the vacuum cycle ID,
     880             :      * because this is not called by VACUUM.
     881             :      */
     882             : 
     883             :     /*
     884             :      * Mark the page as not containing any LP_DEAD items.  This is not
     885             :      * certainly true (there might be some that have recently been marked, but
     886             :      * weren't included in our target-item list), but it will almost always be
     887             :      * true and it doesn't seem worth an additional page scan to check it.
     888             :      * Remember that BTP_HAS_GARBAGE is only a hint anyway.
     889             :      */
     890         262 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
     891         262 :     opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
     892             : 
     893         262 :     MarkBufferDirty(buf);
     894             : 
     895             :     /* XLOG stuff */
     896         262 :     if (RelationNeedsWAL(rel))
     897             :     {
     898             :         XLogRecPtr  recptr;
     899             :         xl_btree_delete xlrec_delete;
     900             : 
     901         262 :         xlrec_delete.hnode = heapRel->rd_node;
     902         262 :         xlrec_delete.nitems = nitems;
     903             : 
     904         262 :         XLogBeginInsert();
     905         262 :         XLogRegisterBuffer(0, buf, REGBUF_STANDARD);
     906         262 :         XLogRegisterData((char *) &xlrec_delete, SizeOfBtreeDelete);
     907             : 
     908             :         /*
     909             :          * We need the target-offsets array whether or not we store the whole
     910             :          * buffer, to allow us to find the latestRemovedXid on a standby
     911             :          * server.
     912             :          */
     913         262 :         XLogRegisterData((char *) itemnos, nitems * sizeof(OffsetNumber));
     914             : 
     915         262 :         recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_DELETE);
     916             : 
     917         262 :         PageSetLSN(page, recptr);
     918             :     }
     919             : 
     920         262 :     END_CRIT_SECTION();
     921         262 : }
     922             : 
     923             : /*
     924             :  * Returns true, if the given block has the half-dead flag set.
     925             :  */
     926             : static bool
     927          43 : _bt_is_page_halfdead(Relation rel, BlockNumber blk)
     928             : {
     929             :     Buffer      buf;
     930             :     Page        page;
     931             :     BTPageOpaque opaque;
     932             :     bool        result;
     933             : 
     934          43 :     buf = _bt_getbuf(rel, blk, BT_READ);
     935          43 :     page = BufferGetPage(buf);
     936          43 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
     937             : 
     938          43 :     result = P_ISHALFDEAD(opaque);
     939          43 :     _bt_relbuf(rel, buf);
     940             : 
     941          43 :     return result;
     942             : }
     943             : 
     944             : /*
     945             :  * Subroutine to find the parent of the branch we're deleting.  This climbs
     946             :  * up the tree until it finds a page with more than one child, i.e. a page
     947             :  * that will not be totally emptied by the deletion.  The chain of pages below
     948             :  * it, with one downlink each, will form the branch that we need to delete.
     949             :  *
     950             :  * If we cannot remove the downlink from the parent, because it's the
     951             :  * rightmost entry, returns false.  On success, *topparent and *topoff are set
     952             :  * to the buffer holding the parent, and the offset of the downlink in it.
     953             :  * *topparent is write-locked, the caller is responsible for releasing it when
     954             :  * done.  *target is set to the topmost page in the branch to-be-deleted, i.e.
     955             :  * the page whose downlink *topparent / *topoff point to, and *rightsib to its
     956             :  * right sibling.
     957             :  *
     958             :  * "child" is the leaf page we wish to delete, and "stack" is a search stack
     959             :  * leading to it (approximately).  Note that we will update the stack
     960             :  * entry(s) to reflect current downlink positions --- this is harmless and
     961             :  * indeed saves later search effort in _bt_pagedel.  The caller should
     962             :  * initialize *target and *rightsib to the leaf page and its right sibling.
     963             :  *
     964             :  * Note: it's OK to release page locks on any internal pages between the leaf
     965             :  * and *topparent, because a safe deletion can't become unsafe due to
     966             :  * concurrent activity.  An internal page can only acquire an entry if the
     967             :  * child is split, but that cannot happen as long as we hold a lock on the
     968             :  * leaf.
     969             :  */
     970             : static bool
     971          43 : _bt_lock_branch_parent(Relation rel, BlockNumber child, BTStack stack,
     972             :                        Buffer *topparent, OffsetNumber *topoff,
     973             :                        BlockNumber *target, BlockNumber *rightsib)
     974             : {
     975             :     BlockNumber parent;
     976             :     OffsetNumber poffset,
     977             :                 maxoff;
     978             :     Buffer      pbuf;
     979             :     Page        page;
     980             :     BTPageOpaque opaque;
     981             :     BlockNumber leftsib;
     982             : 
     983             :     /*
     984             :      * Locate the downlink of "child" in the parent (updating the stack entry
     985             :      * if needed)
     986             :      */
     987          43 :     ItemPointerSet(&(stack->bts_btentry.t_tid), child, P_HIKEY);
     988          43 :     pbuf = _bt_getstackbuf(rel, stack, BT_WRITE);
     989          43 :     if (pbuf == InvalidBuffer)
     990           0 :         elog(ERROR, "failed to re-find parent key in index \"%s\" for deletion target page %u",
     991             :              RelationGetRelationName(rel), child);
     992          43 :     parent = stack->bts_blkno;
     993          43 :     poffset = stack->bts_offset;
     994             : 
     995          43 :     page = BufferGetPage(pbuf);
     996          43 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
     997          43 :     maxoff = PageGetMaxOffsetNumber(page);
     998             : 
     999             :     /*
    1000             :      * If the target is the rightmost child of its parent, then we can't
    1001             :      * delete, unless it's also the only child.
    1002             :      */
    1003          43 :     if (poffset >= maxoff)
    1004             :     {
    1005             :         /* It's rightmost child... */
    1006           1 :         if (poffset == P_FIRSTDATAKEY(opaque))
    1007             :         {
    1008             :             /*
    1009             :              * It's only child, so safe if parent would itself be removable.
    1010             :              * We have to check the parent itself, and then recurse to test
    1011             :              * the conditions at the parent's parent.
    1012             :              */
    1013           0 :             if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) ||
    1014           0 :                 P_INCOMPLETE_SPLIT(opaque))
    1015             :             {
    1016           0 :                 _bt_relbuf(rel, pbuf);
    1017           0 :                 return false;
    1018             :             }
    1019             : 
    1020           0 :             *target = parent;
    1021           0 :             *rightsib = opaque->btpo_next;
    1022           0 :             leftsib = opaque->btpo_prev;
    1023             : 
    1024           0 :             _bt_relbuf(rel, pbuf);
    1025             : 
    1026             :             /*
    1027             :              * Like in _bt_pagedel, check that the left sibling is not marked
    1028             :              * with INCOMPLETE_SPLIT flag.  That would mean that there is no
    1029             :              * downlink to the page to be deleted, and the page deletion
    1030             :              * algorithm isn't prepared to handle that.
    1031             :              */
    1032           0 :             if (leftsib != P_NONE)
    1033             :             {
    1034             :                 Buffer      lbuf;
    1035             :                 Page        lpage;
    1036             :                 BTPageOpaque lopaque;
    1037             : 
    1038           0 :                 lbuf = _bt_getbuf(rel, leftsib, BT_READ);
    1039           0 :                 lpage = BufferGetPage(lbuf);
    1040           0 :                 lopaque = (BTPageOpaque) PageGetSpecialPointer(lpage);
    1041             : 
    1042             :                 /*
    1043             :                  * If the left sibling was concurrently split, so that its
    1044             :                  * next-pointer doesn't point to the current page anymore, the
    1045             :                  * split that created the current page must be completed. (We
    1046             :                  * don't allow splitting an incompletely split page again
    1047             :                  * until the previous split has been completed)
    1048             :                  */
    1049           0 :                 if (lopaque->btpo_next == parent &&
    1050           0 :                     P_INCOMPLETE_SPLIT(lopaque))
    1051             :                 {
    1052           0 :                     _bt_relbuf(rel, lbuf);
    1053           0 :                     return false;
    1054             :                 }
    1055           0 :                 _bt_relbuf(rel, lbuf);
    1056             :             }
    1057             : 
    1058             :             /*
    1059             :              * Perform the same check on this internal level that
    1060             :              * _bt_mark_page_halfdead performed on the leaf level.
    1061             :              */
    1062           0 :             if (_bt_is_page_halfdead(rel, *rightsib))
    1063             :             {
    1064           0 :                 elog(DEBUG1, "could not delete page %u because its right sibling %u is half-dead",
    1065             :                      parent, *rightsib);
    1066           0 :                 return false;
    1067             :             }
    1068             : 
    1069           0 :             return _bt_lock_branch_parent(rel, parent, stack->bts_parent,
    1070             :                                           topparent, topoff, target, rightsib);
    1071             :         }
    1072             :         else
    1073             :         {
    1074             :             /* Unsafe to delete */
    1075           1 :             _bt_relbuf(rel, pbuf);
    1076           1 :             return false;
    1077             :         }
    1078             :     }
    1079             :     else
    1080             :     {
    1081             :         /* Not rightmost child, so safe to delete */
    1082          42 :         *topparent = pbuf;
    1083          42 :         *topoff = poffset;
    1084          42 :         return true;
    1085             :     }
    1086             : }
    1087             : 
    1088             : /*
    1089             :  * _bt_pagedel() -- Delete a page from the b-tree, if legal to do so.
    1090             :  *
    1091             :  * This action unlinks the page from the b-tree structure, removing all
    1092             :  * pointers leading to it --- but not touching its own left and right links.
    1093             :  * The page cannot be physically reclaimed right away, since other processes
    1094             :  * may currently be trying to follow links leading to the page; they have to
    1095             :  * be allowed to use its right-link to recover.  See nbtree/README.
    1096             :  *
    1097             :  * On entry, the target buffer must be pinned and locked (either read or write
    1098             :  * lock is OK).  This lock and pin will be dropped before exiting.
    1099             :  *
    1100             :  * Returns the number of pages successfully deleted (zero if page cannot
    1101             :  * be deleted now; could be more than one if parent or sibling pages were
    1102             :  * deleted too).
    1103             :  *
    1104             :  * NOTE: this leaks memory.  Rather than trying to clean up everything
    1105             :  * carefully, it's better to run it in a temp context that can be reset
    1106             :  * frequently.
    1107             :  */
    1108             : int
    1109          52 : _bt_pagedel(Relation rel, Buffer buf)
    1110             : {
    1111          52 :     int         ndeleted = 0;
    1112             :     BlockNumber rightsib;
    1113             :     bool        rightsib_empty;
    1114             :     Page        page;
    1115             :     BTPageOpaque opaque;
    1116             : 
    1117             :     /*
    1118             :      * "stack" is a search stack leading (approximately) to the target page.
    1119             :      * It is initially NULL, but when iterating, we keep it to avoid
    1120             :      * duplicated search effort.
    1121             :      *
    1122             :      * Also, when "stack" is not NULL, we have already checked that the
    1123             :      * current page is not the right half of an incomplete split, i.e. the
    1124             :      * left sibling does not have its INCOMPLETE_SPLIT flag set.
    1125             :      */
    1126          52 :     BTStack     stack = NULL;
    1127             : 
    1128             :     for (;;)
    1129             :     {
    1130          95 :         page = BufferGetPage(buf);
    1131          95 :         opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1132             : 
    1133             :         /*
    1134             :          * Internal pages are never deleted directly, only as part of deleting
    1135             :          * the whole branch all the way down to leaf level.
    1136             :          */
    1137          95 :         if (!P_ISLEAF(opaque))
    1138             :         {
    1139             :             /*
    1140             :              * Pre-9.4 page deletion only marked internal pages as half-dead,
    1141             :              * but now we only use that flag on leaf pages. The old algorithm
    1142             :              * was never supposed to leave half-dead pages in the tree, it was
    1143             :              * just a transient state, but it was nevertheless possible in
    1144             :              * error scenarios. We don't know how to deal with them here. They
    1145             :              * are harmless as far as searches are considered, but inserts
    1146             :              * into the deleted keyspace could add out-of-order downlinks in
    1147             :              * the upper levels. Log a notice, hopefully the admin will notice
    1148             :              * and reindex.
    1149             :              */
    1150           0 :             if (P_ISHALFDEAD(opaque))
    1151           0 :                 ereport(LOG,
    1152             :                         (errcode(ERRCODE_INDEX_CORRUPTED),
    1153             :                          errmsg("index \"%s\" contains a half-dead internal page",
    1154             :                                 RelationGetRelationName(rel)),
    1155             :                          errhint("This can be caused by an interrupted VACUUM in version 9.3 or older, before upgrade. Please REINDEX it.")));
    1156           0 :             _bt_relbuf(rel, buf);
    1157           0 :             return ndeleted;
    1158             :         }
    1159             : 
    1160             :         /*
    1161             :          * We can never delete rightmost pages nor root pages.  While at it,
    1162             :          * check that page is not already deleted and is empty.
    1163             :          *
    1164             :          * To keep the algorithm simple, we also never delete an incompletely
    1165             :          * split page (they should be rare enough that this doesn't make any
    1166             :          * meaningful difference to disk usage):
    1167             :          *
    1168             :          * The INCOMPLETE_SPLIT flag on the page tells us if the page is the
    1169             :          * left half of an incomplete split, but ensuring that it's not the
    1170             :          * right half is more complicated.  For that, we have to check that
    1171             :          * the left sibling doesn't have its INCOMPLETE_SPLIT flag set.  On
    1172             :          * the first iteration, we temporarily release the lock on the current
    1173             :          * page, and check the left sibling and also construct a search stack
    1174             :          * to.  On subsequent iterations, we know we stepped right from a page
    1175             :          * that passed these tests, so it's OK.
    1176             :          */
    1177         181 :         if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) || P_ISDELETED(opaque) ||
    1178         172 :             P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) ||
    1179          86 :             P_INCOMPLETE_SPLIT(opaque))
    1180             :         {
    1181             :             /* Should never fail to delete a half-dead page */
    1182           9 :             Assert(!P_ISHALFDEAD(opaque));
    1183             : 
    1184           9 :             _bt_relbuf(rel, buf);
    1185           9 :             return ndeleted;
    1186             :         }
    1187             : 
    1188             :         /*
    1189             :          * First, remove downlink pointing to the page (or a parent of the
    1190             :          * page, if we are going to delete a taller branch), and mark the page
    1191             :          * as half-dead.
    1192             :          */
    1193          86 :         if (!P_ISHALFDEAD(opaque))
    1194             :         {
    1195             :             /*
    1196             :              * We need an approximate pointer to the page's parent page.  We
    1197             :              * use the standard search mechanism to search for the page's high
    1198             :              * key; this will give us a link to either the current parent or
    1199             :              * someplace to its left (if there are multiple equal high keys).
    1200             :              *
    1201             :              * Also check if this is the right-half of an incomplete split
    1202             :              * (see comment above).
    1203             :              */
    1204          86 :             if (!stack)
    1205             :             {
    1206             :                 ScanKey     itup_scankey;
    1207             :                 ItemId      itemid;
    1208             :                 IndexTuple  targetkey;
    1209             :                 Buffer      lbuf;
    1210             :                 BlockNumber leftsib;
    1211             : 
    1212          43 :                 itemid = PageGetItemId(page, P_HIKEY);
    1213          43 :                 targetkey = CopyIndexTuple((IndexTuple) PageGetItem(page, itemid));
    1214             : 
    1215          43 :                 leftsib = opaque->btpo_prev;
    1216             : 
    1217             :                 /*
    1218             :                  * To avoid deadlocks, we'd better drop the leaf page lock
    1219             :                  * before going further.
    1220             :                  */
    1221          43 :                 LockBuffer(buf, BUFFER_LOCK_UNLOCK);
    1222             : 
    1223             :                 /*
    1224             :                  * Fetch the left sibling, to check that it's not marked with
    1225             :                  * INCOMPLETE_SPLIT flag.  That would mean that the page
    1226             :                  * to-be-deleted doesn't have a downlink, and the page
    1227             :                  * deletion algorithm isn't prepared to handle that.
    1228             :                  */
    1229          43 :                 if (!P_LEFTMOST(opaque))
    1230             :                 {
    1231             :                     BTPageOpaque lopaque;
    1232             :                     Page        lpage;
    1233             : 
    1234          43 :                     lbuf = _bt_getbuf(rel, leftsib, BT_READ);
    1235          43 :                     lpage = BufferGetPage(lbuf);
    1236          43 :                     lopaque = (BTPageOpaque) PageGetSpecialPointer(lpage);
    1237             : 
    1238             :                     /*
    1239             :                      * If the left sibling is split again by another backend,
    1240             :                      * after we released the lock, we know that the first
    1241             :                      * split must have finished, because we don't allow an
    1242             :                      * incompletely-split page to be split again.  So we don't
    1243             :                      * need to walk right here.
    1244             :                      */
    1245          86 :                     if (lopaque->btpo_next == BufferGetBlockNumber(buf) &&
    1246          43 :                         P_INCOMPLETE_SPLIT(lopaque))
    1247             :                     {
    1248           0 :                         ReleaseBuffer(buf);
    1249           0 :                         _bt_relbuf(rel, lbuf);
    1250           0 :                         return ndeleted;
    1251             :                     }
    1252          43 :                     _bt_relbuf(rel, lbuf);
    1253             :                 }
    1254             : 
    1255             :                 /* we need an insertion scan key for the search, so build one */
    1256          43 :                 itup_scankey = _bt_mkscankey(rel, targetkey);
    1257             :                 /* find the leftmost leaf page containing this key */
    1258          43 :                 stack = _bt_search(rel, rel->rd_rel->relnatts, itup_scankey,
    1259             :                                    false, &lbuf, BT_READ, NULL);
    1260             :                 /* don't need a pin on the page */
    1261          43 :                 _bt_relbuf(rel, lbuf);
    1262             : 
    1263             :                 /*
    1264             :                  * Re-lock the leaf page, and start over, to re-check that the
    1265             :                  * page can still be deleted.
    1266             :                  */
    1267          43 :                 LockBuffer(buf, BT_WRITE);
    1268          43 :                 continue;
    1269             :             }
    1270             : 
    1271          43 :             if (!_bt_mark_page_halfdead(rel, buf, stack))
    1272             :             {
    1273           1 :                 _bt_relbuf(rel, buf);
    1274           1 :                 return ndeleted;
    1275             :             }
    1276             :         }
    1277             : 
    1278             :         /*
    1279             :          * Then unlink it from its siblings.  Each call to
    1280             :          * _bt_unlink_halfdead_page unlinks the topmost page from the branch,
    1281             :          * making it shallower.  Iterate until the leaf page is gone.
    1282             :          */
    1283          42 :         rightsib_empty = false;
    1284         126 :         while (P_ISHALFDEAD(opaque))
    1285             :         {
    1286          42 :             if (!_bt_unlink_halfdead_page(rel, buf, &rightsib_empty))
    1287             :             {
    1288             :                 /* _bt_unlink_halfdead_page already released buffer */
    1289           0 :                 return ndeleted;
    1290             :             }
    1291          42 :             ndeleted++;
    1292             :         }
    1293             : 
    1294          42 :         rightsib = opaque->btpo_next;
    1295             : 
    1296          42 :         _bt_relbuf(rel, buf);
    1297             : 
    1298             :         /*
    1299             :          * The page has now been deleted. If its right sibling is completely
    1300             :          * empty, it's possible that the reason we haven't deleted it earlier
    1301             :          * is that it was the rightmost child of the parent. Now that we
    1302             :          * removed the downlink for this page, the right sibling might now be
    1303             :          * the only child of the parent, and could be removed. It would be
    1304             :          * picked up by the next vacuum anyway, but might as well try to
    1305             :          * remove it now, so loop back to process the right sibling.
    1306             :          */
    1307          42 :         if (!rightsib_empty)
    1308          42 :             break;
    1309             : 
    1310           0 :         buf = _bt_getbuf(rel, rightsib, BT_WRITE);
    1311          43 :     }
    1312             : 
    1313          42 :     return ndeleted;
    1314             : }
    1315             : 
    1316             : /*
    1317             :  * First stage of page deletion.  Remove the downlink to the top of the
    1318             :  * branch being deleted, and mark the leaf page as half-dead.
    1319             :  */
    1320             : static bool
    1321          43 : _bt_mark_page_halfdead(Relation rel, Buffer leafbuf, BTStack stack)
    1322             : {
    1323             :     BlockNumber leafblkno;
    1324             :     BlockNumber leafrightsib;
    1325             :     BlockNumber target;
    1326             :     BlockNumber rightsib;
    1327             :     ItemId      itemid;
    1328             :     Page        page;
    1329             :     BTPageOpaque opaque;
    1330             :     Buffer      topparent;
    1331             :     OffsetNumber topoff;
    1332             :     OffsetNumber nextoffset;
    1333             :     IndexTuple  itup;
    1334             :     IndexTupleData trunctuple;
    1335             : 
    1336          43 :     page = BufferGetPage(leafbuf);
    1337          43 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1338             : 
    1339          43 :     Assert(!P_RIGHTMOST(opaque) && !P_ISROOT(opaque) && !P_ISDELETED(opaque) &&
    1340             :            !P_ISHALFDEAD(opaque) && P_ISLEAF(opaque) &&
    1341             :            P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
    1342             : 
    1343             :     /*
    1344             :      * Save info about the leaf page.
    1345             :      */
    1346          43 :     leafblkno = BufferGetBlockNumber(leafbuf);
    1347          43 :     leafrightsib = opaque->btpo_next;
    1348             : 
    1349             :     /*
    1350             :      * Before attempting to lock the parent page, check that the right sibling
    1351             :      * is not in half-dead state.  A half-dead right sibling would have no
    1352             :      * downlink in the parent, which would be highly confusing later when we
    1353             :      * delete the downlink that follows the current page's downlink. (I
    1354             :      * believe the deletion would work correctly, but it would fail the
    1355             :      * cross-check we make that the following downlink points to the right
    1356             :      * sibling of the delete page.)
    1357             :      */
    1358          43 :     if (_bt_is_page_halfdead(rel, leafrightsib))
    1359             :     {
    1360           0 :         elog(DEBUG1, "could not delete page %u because its right sibling %u is half-dead",
    1361             :              leafblkno, leafrightsib);
    1362           0 :         return false;
    1363             :     }
    1364             : 
    1365             :     /*
    1366             :      * We cannot delete a page that is the rightmost child of its immediate
    1367             :      * parent, unless it is the only child --- in which case the parent has to
    1368             :      * be deleted too, and the same condition applies recursively to it. We
    1369             :      * have to check this condition all the way up before trying to delete,
    1370             :      * and lock the final parent of the to-be-deleted branch.
    1371             :      */
    1372          43 :     rightsib = leafrightsib;
    1373          43 :     target = leafblkno;
    1374          43 :     if (!_bt_lock_branch_parent(rel, leafblkno, stack,
    1375             :                                 &topparent, &topoff, &target, &rightsib))
    1376           1 :         return false;
    1377             : 
    1378             :     /*
    1379             :      * Check that the parent-page index items we're about to delete/overwrite
    1380             :      * contain what we expect.  This can fail if the index has become corrupt
    1381             :      * for some reason.  We want to throw any error before entering the
    1382             :      * critical section --- otherwise it'd be a PANIC.
    1383             :      *
    1384             :      * The test on the target item is just an Assert because
    1385             :      * _bt_lock_branch_parent should have guaranteed it has the expected
    1386             :      * contents.  The test on the next-child downlink is known to sometimes
    1387             :      * fail in the field, though.
    1388             :      */
    1389          42 :     page = BufferGetPage(topparent);
    1390          42 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1391             : 
    1392             : #ifdef USE_ASSERT_CHECKING
    1393          42 :     itemid = PageGetItemId(page, topoff);
    1394          42 :     itup = (IndexTuple) PageGetItem(page, itemid);
    1395          42 :     Assert(ItemPointerGetBlockNumber(&(itup->t_tid)) == target);
    1396             : #endif
    1397             : 
    1398          42 :     nextoffset = OffsetNumberNext(topoff);
    1399          42 :     itemid = PageGetItemId(page, nextoffset);
    1400          42 :     itup = (IndexTuple) PageGetItem(page, itemid);
    1401          42 :     if (ItemPointerGetBlockNumber(&(itup->t_tid)) != rightsib)
    1402           0 :         elog(ERROR, "right sibling %u of block %u is not next child %u of block %u in index \"%s\"",
    1403             :              rightsib, target, ItemPointerGetBlockNumber(&(itup->t_tid)),
    1404             :              BufferGetBlockNumber(topparent), RelationGetRelationName(rel));
    1405             : 
    1406             :     /*
    1407             :      * Any insert which would have gone on the leaf block will now go to its
    1408             :      * right sibling.
    1409             :      */
    1410          42 :     PredicateLockPageCombine(rel, leafblkno, leafrightsib);
    1411             : 
    1412             :     /* No ereport(ERROR) until changes are logged */
    1413          42 :     START_CRIT_SECTION();
    1414             : 
    1415             :     /*
    1416             :      * Update parent.  The normal case is a tad tricky because we want to
    1417             :      * delete the target's downlink and the *following* key.  Easiest way is
    1418             :      * to copy the right sibling's downlink over the target downlink, and then
    1419             :      * delete the following item.
    1420             :      */
    1421          42 :     page = BufferGetPage(topparent);
    1422          42 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1423             : 
    1424          42 :     itemid = PageGetItemId(page, topoff);
    1425          42 :     itup = (IndexTuple) PageGetItem(page, itemid);
    1426          42 :     ItemPointerSet(&(itup->t_tid), rightsib, P_HIKEY);
    1427             : 
    1428          42 :     nextoffset = OffsetNumberNext(topoff);
    1429          42 :     PageIndexTupleDelete(page, nextoffset);
    1430             : 
    1431             :     /*
    1432             :      * Mark the leaf page as half-dead, and stamp it with a pointer to the
    1433             :      * highest internal page in the branch we're deleting.  We use the tid of
    1434             :      * the high key to store it.
    1435             :      */
    1436          42 :     page = BufferGetPage(leafbuf);
    1437          42 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1438          42 :     opaque->btpo_flags |= BTP_HALF_DEAD;
    1439             : 
    1440          42 :     PageIndexTupleDelete(page, P_HIKEY);
    1441          42 :     Assert(PageGetMaxOffsetNumber(page) == 0);
    1442          42 :     MemSet(&trunctuple, 0, sizeof(IndexTupleData));
    1443          42 :     trunctuple.t_info = sizeof(IndexTupleData);
    1444          42 :     if (target != leafblkno)
    1445           0 :         ItemPointerSet(&trunctuple.t_tid, target, P_HIKEY);
    1446             :     else
    1447          42 :         ItemPointerSetInvalid(&trunctuple.t_tid);
    1448          42 :     if (PageAddItem(page, (Item) &trunctuple, sizeof(IndexTupleData), P_HIKEY,
    1449             :                     false, false) == InvalidOffsetNumber)
    1450           0 :         elog(ERROR, "could not add dummy high key to half-dead page");
    1451             : 
    1452             :     /* Must mark buffers dirty before XLogInsert */
    1453          42 :     MarkBufferDirty(topparent);
    1454          42 :     MarkBufferDirty(leafbuf);
    1455             : 
    1456             :     /* XLOG stuff */
    1457          42 :     if (RelationNeedsWAL(rel))
    1458             :     {
    1459             :         xl_btree_mark_page_halfdead xlrec;
    1460             :         XLogRecPtr  recptr;
    1461             : 
    1462          42 :         xlrec.poffset = topoff;
    1463          42 :         xlrec.leafblk = leafblkno;
    1464          42 :         if (target != leafblkno)
    1465           0 :             xlrec.topparent = target;
    1466             :         else
    1467          42 :             xlrec.topparent = InvalidBlockNumber;
    1468             : 
    1469          42 :         XLogBeginInsert();
    1470          42 :         XLogRegisterBuffer(0, leafbuf, REGBUF_WILL_INIT);
    1471          42 :         XLogRegisterBuffer(1, topparent, REGBUF_STANDARD);
    1472             : 
    1473          42 :         page = BufferGetPage(leafbuf);
    1474          42 :         opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1475          42 :         xlrec.leftblk = opaque->btpo_prev;
    1476          42 :         xlrec.rightblk = opaque->btpo_next;
    1477             : 
    1478          42 :         XLogRegisterData((char *) &xlrec, SizeOfBtreeMarkPageHalfDead);
    1479             : 
    1480          42 :         recptr = XLogInsert(RM_BTREE_ID, XLOG_BTREE_MARK_PAGE_HALFDEAD);
    1481             : 
    1482          42 :         page = BufferGetPage(topparent);
    1483          42 :         PageSetLSN(page, recptr);
    1484          42 :         page = BufferGetPage(leafbuf);
    1485          42 :         PageSetLSN(page, recptr);
    1486             :     }
    1487             : 
    1488          42 :     END_CRIT_SECTION();
    1489             : 
    1490          42 :     _bt_relbuf(rel, topparent);
    1491          42 :     return true;
    1492             : }
    1493             : 
    1494             : /*
    1495             :  * Unlink a page in a branch of half-dead pages from its siblings.
    1496             :  *
    1497             :  * If the leaf page still has a downlink pointing to it, unlinks the highest
    1498             :  * parent in the to-be-deleted branch instead of the leaf page.  To get rid
    1499             :  * of the whole branch, including the leaf page itself, iterate until the
    1500             :  * leaf page is deleted.
    1501             :  *
    1502             :  * Returns 'false' if the page could not be unlinked (shouldn't happen).
    1503             :  * If the (new) right sibling of the page is empty, *rightsib_empty is set
    1504             :  * to true.
    1505             :  *
    1506             :  * Must hold pin and lock on leafbuf at entry (read or write doesn't matter).
    1507             :  * On success exit, we'll be holding pin and write lock.  On failure exit,
    1508             :  * we'll release both pin and lock before returning (we define it that way
    1509             :  * to avoid having to reacquire a lock we already released).
    1510             :  */
    1511             : static bool
    1512          42 : _bt_unlink_halfdead_page(Relation rel, Buffer leafbuf, bool *rightsib_empty)
    1513             : {
    1514          42 :     BlockNumber leafblkno = BufferGetBlockNumber(leafbuf);
    1515             :     BlockNumber leafleftsib;
    1516             :     BlockNumber leafrightsib;
    1517             :     BlockNumber target;
    1518             :     BlockNumber leftsib;
    1519             :     BlockNumber rightsib;
    1520          42 :     Buffer      lbuf = InvalidBuffer;
    1521             :     Buffer      buf;
    1522             :     Buffer      rbuf;
    1523          42 :     Buffer      metabuf = InvalidBuffer;
    1524          42 :     Page        metapg = NULL;
    1525          42 :     BTMetaPageData *metad = NULL;
    1526             :     ItemId      itemid;
    1527             :     Page        page;
    1528             :     BTPageOpaque opaque;
    1529             :     bool        rightsib_is_rightmost;
    1530             :     int         targetlevel;
    1531             :     ItemPointer leafhikey;
    1532             :     BlockNumber nextchild;
    1533             : 
    1534          42 :     page = BufferGetPage(leafbuf);
    1535          42 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1536             : 
    1537          42 :     Assert(P_ISLEAF(opaque) && P_ISHALFDEAD(opaque));
    1538             : 
    1539             :     /*
    1540             :      * Remember some information about the leaf page.
    1541             :      */
    1542          42 :     itemid = PageGetItemId(page, P_HIKEY);
    1543          42 :     leafhikey = &((IndexTuple) PageGetItem(page, itemid))->t_tid;
    1544          42 :     leafleftsib = opaque->btpo_prev;
    1545          42 :     leafrightsib = opaque->btpo_next;
    1546             : 
    1547          42 :     LockBuffer(leafbuf, BUFFER_LOCK_UNLOCK);
    1548             : 
    1549             :     /*
    1550             :      * If the leaf page still has a parent pointing to it (or a chain of
    1551             :      * parents), we don't unlink the leaf page yet, but the topmost remaining
    1552             :      * parent in the branch.  Set 'target' and 'buf' to reference the page
    1553             :      * actually being unlinked.
    1554             :      */
    1555          42 :     if (ItemPointerIsValid(leafhikey))
    1556             :     {
    1557           0 :         target = ItemPointerGetBlockNumber(leafhikey);
    1558           0 :         Assert(target != leafblkno);
    1559             : 
    1560             :         /* fetch the block number of the topmost parent's left sibling */
    1561           0 :         buf = _bt_getbuf(rel, target, BT_READ);
    1562           0 :         page = BufferGetPage(buf);
    1563           0 :         opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1564           0 :         leftsib = opaque->btpo_prev;
    1565           0 :         targetlevel = opaque->btpo.level;
    1566             : 
    1567             :         /*
    1568             :          * To avoid deadlocks, we'd better drop the target page lock before
    1569             :          * going further.
    1570             :          */
    1571           0 :         LockBuffer(buf, BUFFER_LOCK_UNLOCK);
    1572             :     }
    1573             :     else
    1574             :     {
    1575          42 :         target = leafblkno;
    1576             : 
    1577          42 :         buf = leafbuf;
    1578          42 :         leftsib = leafleftsib;
    1579          42 :         targetlevel = 0;
    1580             :     }
    1581             : 
    1582             :     /*
    1583             :      * We have to lock the pages we need to modify in the standard order:
    1584             :      * moving right, then up.  Else we will deadlock against other writers.
    1585             :      *
    1586             :      * So, first lock the leaf page, if it's not the target.  Then find and
    1587             :      * write-lock the current left sibling of the target page.  The sibling
    1588             :      * that was current a moment ago could have split, so we may have to move
    1589             :      * right.  This search could fail if either the sibling or the target page
    1590             :      * was deleted by someone else meanwhile; if so, give up.  (Right now,
    1591             :      * that should never happen, since page deletion is only done in VACUUM
    1592             :      * and there shouldn't be multiple VACUUMs concurrently on the same
    1593             :      * table.)
    1594             :      */
    1595          42 :     if (target != leafblkno)
    1596           0 :         LockBuffer(leafbuf, BT_WRITE);
    1597          42 :     if (leftsib != P_NONE)
    1598             :     {
    1599          42 :         lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
    1600          42 :         page = BufferGetPage(lbuf);
    1601          42 :         opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1602          84 :         while (P_ISDELETED(opaque) || opaque->btpo_next != target)
    1603             :         {
    1604             :             /* step right one page */
    1605           0 :             leftsib = opaque->btpo_next;
    1606           0 :             _bt_relbuf(rel, lbuf);
    1607           0 :             if (leftsib == P_NONE)
    1608             :             {
    1609           0 :                 elog(LOG, "no left sibling (concurrent deletion?) of block %u in \"%s\"",
    1610             :                      target,
    1611             :                      RelationGetRelationName(rel));
    1612           0 :                 if (target != leafblkno)
    1613             :                 {
    1614             :                     /* we have only a pin on target, but pin+lock on leafbuf */
    1615           0 :                     ReleaseBuffer(buf);
    1616           0 :                     _bt_relbuf(rel, leafbuf);
    1617             :                 }
    1618             :                 else
    1619             :                 {
    1620             :                     /* we have only a pin on leafbuf */
    1621           0 :                     ReleaseBuffer(leafbuf);
    1622             :                 }
    1623           0 :                 return false;
    1624             :             }
    1625           0 :             lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
    1626           0 :             page = BufferGetPage(lbuf);
    1627           0 :             opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1628             :         }
    1629             :     }
    1630             :     else
    1631           0 :         lbuf = InvalidBuffer;
    1632             : 
    1633             :     /*
    1634             :      * Next write-lock the target page itself.  It should be okay to take just
    1635             :      * a write lock not a superexclusive lock, since no scans would stop on an
    1636             :      * empty page.
    1637             :      */
    1638          42 :     LockBuffer(buf, BT_WRITE);
    1639          42 :     page = BufferGetPage(buf);
    1640          42 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1641             : 
    1642             :     /*
    1643             :      * Check page is still empty etc, else abandon deletion.  This is just for
    1644             :      * paranoia's sake; a half-dead page cannot resurrect because there can be
    1645             :      * only one vacuum process running at a time.
    1646             :      */
    1647          42 :     if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) || P_ISDELETED(opaque))
    1648             :     {
    1649           0 :         elog(ERROR, "half-dead page changed status unexpectedly in block %u of index \"%s\"",
    1650             :              target, RelationGetRelationName(rel));
    1651             :     }
    1652          42 :     if (opaque->btpo_prev != leftsib)
    1653           0 :         elog(ERROR, "left link changed unexpectedly in block %u of index \"%s\"",
    1654             :              target, RelationGetRelationName(rel));
    1655             : 
    1656          42 :     if (target == leafblkno)
    1657             :     {
    1658          84 :         if (P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) ||
    1659          84 :             !P_ISLEAF(opaque) || !P_ISHALFDEAD(opaque))
    1660           0 :             elog(ERROR, "half-dead page changed status unexpectedly in block %u of index \"%s\"",
    1661             :                  target, RelationGetRelationName(rel));
    1662          42 :         nextchild = InvalidBlockNumber;
    1663             :     }
    1664             :     else
    1665             :     {
    1666           0 :         if (P_FIRSTDATAKEY(opaque) != PageGetMaxOffsetNumber(page) ||
    1667           0 :             P_ISLEAF(opaque))
    1668           0 :             elog(ERROR, "half-dead page changed status unexpectedly in block %u of index \"%s\"",
    1669             :                  target, RelationGetRelationName(rel));
    1670             : 
    1671             :         /* remember the next non-leaf child down in the branch. */
    1672           0 :         itemid = PageGetItemId(page, P_FIRSTDATAKEY(opaque));
    1673           0 :         nextchild = ItemPointerGetBlockNumber(&((IndexTuple) PageGetItem(page, itemid))->t_tid);
    1674           0 :         if (nextchild == leafblkno)
    1675           0 :             nextchild = InvalidBlockNumber;
    1676             :     }
    1677             : 
    1678             :     /*
    1679             :      * And next write-lock the (current) right sibling.
    1680             :      */
    1681          42 :     rightsib = opaque->btpo_next;
    1682          42 :     rbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
    1683          42 :     page = BufferGetPage(rbuf);
    1684          42 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1685          42 :     if (opaque->btpo_prev != target)
    1686           0 :         elog(ERROR, "right sibling's left-link doesn't match: "
    1687             :              "block %u links to %u instead of expected %u in index \"%s\"",
    1688             :              rightsib, opaque->btpo_prev, target,
    1689             :              RelationGetRelationName(rel));
    1690          42 :     rightsib_is_rightmost = P_RIGHTMOST(opaque);
    1691          42 :     *rightsib_empty = (P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
    1692             : 
    1693             :     /*
    1694             :      * If we are deleting the next-to-last page on the target's level, then
    1695             :      * the rightsib is a candidate to become the new fast root. (In theory, it
    1696             :      * might be possible to push the fast root even further down, but the odds
    1697             :      * of doing so are slim, and the locking considerations daunting.)
    1698             :      *
    1699             :      * We don't support handling this in the case where the parent is becoming
    1700             :      * half-dead, even though it theoretically could occur.
    1701             :      *
    1702             :      * We can safely acquire a lock on the metapage here --- see comments for
    1703             :      * _bt_newroot().
    1704             :      */
    1705          42 :     if (leftsib == P_NONE && rightsib_is_rightmost)
    1706             :     {
    1707           0 :         page = BufferGetPage(rbuf);
    1708           0 :         opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1709           0 :         if (P_RIGHTMOST(opaque))
    1710             :         {
    1711             :             /* rightsib will be the only one left on the level */
    1712           0 :             metabuf = _bt_getbuf(rel, BTREE_METAPAGE, BT_WRITE);
    1713           0 :             metapg = BufferGetPage(metabuf);
    1714           0 :             metad = BTPageGetMeta(metapg);
    1715             : 
    1716             :             /*
    1717             :              * The expected case here is btm_fastlevel == targetlevel+1; if
    1718             :              * the fastlevel is <= targetlevel, something is wrong, and we
    1719             :              * choose to overwrite it to fix it.
    1720             :              */
    1721           0 :             if (metad->btm_fastlevel > targetlevel + 1)
    1722             :             {
    1723             :                 /* no update wanted */
    1724           0 :                 _bt_relbuf(rel, metabuf);
    1725           0 :                 metabuf = InvalidBuffer;
    1726             :             }
    1727             :         }
    1728             :     }
    1729             : 
    1730             :     /*
    1731             :      * Here we begin doing the deletion.
    1732             :      */
    1733             : 
    1734             :     /* No ereport(ERROR) until changes are logged */
    1735          42 :     START_CRIT_SECTION();
    1736             : 
    1737             :     /*
    1738             :      * Update siblings' side-links.  Note the target page's side-links will
    1739             :      * continue to point to the siblings.  Asserts here are just rechecking
    1740             :      * things we already verified above.
    1741             :      */
    1742          42 :     if (BufferIsValid(lbuf))
    1743             :     {
    1744          42 :         page = BufferGetPage(lbuf);
    1745          42 :         opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1746          42 :         Assert(opaque->btpo_next == target);
    1747          42 :         opaque->btpo_next = rightsib;
    1748             :     }
    1749          42 :     page = BufferGetPage(rbuf);
    1750          42 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1751          42 :     Assert(opaque->btpo_prev == target);
    1752          42 :     opaque->btpo_prev = leftsib;
    1753             : 
    1754             :     /*
    1755             :      * If we deleted a parent of the targeted leaf page, instead of the leaf
    1756             :      * itself, update the leaf to point to the next remaining child in the
    1757             :      * branch.
    1758             :      */
    1759          42 :     if (target != leafblkno)
    1760             :     {
    1761           0 :         if (nextchild == InvalidBlockNumber)
    1762           0 :             ItemPointerSetInvalid(leafhikey);
    1763             :         else
    1764           0 :             ItemPointerSet(leafhikey, nextchild, P_HIKEY);
    1765             :     }
    1766             : 
    1767             :     /*
    1768             :      * Mark the page itself deleted.  It can be recycled when all current
    1769             :      * transactions are gone.  Storing GetTopTransactionId() would work, but
    1770             :      * we're in VACUUM and would not otherwise have an XID.  Having already
    1771             :      * updated links to the target, ReadNewTransactionId() suffices as an
    1772             :      * upper bound.  Any scan having retained a now-stale link is advertising
    1773             :      * in its PGXACT an xmin less than or equal to the value we read here.  It
    1774             :      * will continue to do so, holding back RecentGlobalXmin, for the duration
    1775             :      * of that scan.
    1776             :      */
    1777          42 :     page = BufferGetPage(buf);
    1778          42 :     opaque = (BTPageOpaque) PageGetSpecialPointer(page);
    1779          42 :     opaque->btpo_flags &= ~BTP_HALF_DEAD;
    1780          42 :     opaque->btpo_flags |= BTP_DELETED;
    1781          42 :     opaque->btpo.xact = ReadNewTransactionId();
    1782             : 
    1783             :     /* And update the metapage, if needed */
    1784          42 :     if (BufferIsValid(metabuf))
    1785             :     {
    1786           0 :         metad->btm_fastroot = rightsib;
    1787           0 :         metad->btm_fastlevel = targetlevel;
    1788           0 :         MarkBufferDirty(metabuf);
    1789             :     }
    1790             : 
    1791             :     /* Must mark buffers dirty before XLogInsert */
    1792          42 :     MarkBufferDirty(rbuf);
    1793          42 :     MarkBufferDirty(buf);
    1794          42 :     if (BufferIsValid(lbuf))
    1795          42 :         MarkBufferDirty(lbuf);
    1796          42 :     if (target != leafblkno)
    1797           0 :         MarkBufferDirty(leafbuf);
    1798             : 
    1799             :     /* XLOG stuff */
    1800          42 :     if (RelationNeedsWAL(rel))
    1801             :     {
    1802             :         xl_btree_unlink_page xlrec;
    1803             :         xl_btree_metadata xlmeta;
    1804             :         uint8       xlinfo;
    1805             :         XLogRecPtr  recptr;
    1806             : 
    1807          42 :         XLogBeginInsert();
    1808             : 
    1809          42 :         XLogRegisterBuffer(0, buf, REGBUF_WILL_INIT);
    1810          42 :         if (BufferIsValid(lbuf))
    1811          42 :             XLogRegisterBuffer(1, lbuf, REGBUF_STANDARD);
    1812          42 :         XLogRegisterBuffer(2, rbuf, REGBUF_STANDARD);
    1813          42 :         if (target != leafblkno)
    1814           0 :             XLogRegisterBuffer(3, leafbuf, REGBUF_WILL_INIT);
    1815             : 
    1816             :         /* information on the unlinked block */
    1817          42 :         xlrec.leftsib = leftsib;
    1818          42 :         xlrec.rightsib = rightsib;
    1819          42 :         xlrec.btpo_xact = opaque->btpo.xact;
    1820             : 
    1821             :         /* information needed to recreate the leaf block (if not the target) */
    1822          42 :         xlrec.leafleftsib = leafleftsib;
    1823          42 :         xlrec.leafrightsib = leafrightsib;
    1824          42 :         xlrec.topparent = nextchild;
    1825             : 
    1826          42 :         XLogRegisterData((char *) &xlrec, SizeOfBtreeUnlinkPage);
    1827             : 
    1828          42 :         if (BufferIsValid(metabuf))
    1829             :         {
    1830           0 :             XLogRegisterBuffer(4, metabuf, REGBUF_WILL_INIT);
    1831             : 
    1832           0 :             xlmeta.root = metad->btm_root;
    1833           0 :             xlmeta.level = metad->btm_level;
    1834           0 :             xlmeta.fastroot = metad->btm_fastroot;
    1835           0 :             xlmeta.fastlevel = metad->btm_fastlevel;
    1836             : 
    1837           0 :             XLogRegisterBufData(4, (char *) &xlmeta, sizeof(xl_btree_metadata));
    1838           0 :             xlinfo = XLOG_BTREE_UNLINK_PAGE_META;
    1839             :         }
    1840             :         else
    1841          42 :             xlinfo = XLOG_BTREE_UNLINK_PAGE;
    1842             : 
    1843          42 :         recptr = XLogInsert(RM_BTREE_ID, xlinfo);
    1844             : 
    1845          42 :         if (BufferIsValid(metabuf))
    1846             :         {
    1847           0 :             PageSetLSN(metapg, recptr);
    1848             :         }
    1849          42 :         page = BufferGetPage(rbuf);
    1850          42 :         PageSetLSN(page, recptr);
    1851          42 :         page = BufferGetPage(buf);
    1852          42 :         PageSetLSN(page, recptr);
    1853          42 :         if (BufferIsValid(lbuf))
    1854             :         {
    1855          42 :             page = BufferGetPage(lbuf);
    1856          42 :             PageSetLSN(page, recptr);
    1857             :         }
    1858          42 :         if (target != leafblkno)
    1859             :         {
    1860           0 :             page = BufferGetPage(leafbuf);
    1861           0 :             PageSetLSN(page, recptr);
    1862             :         }
    1863             :     }
    1864             : 
    1865          42 :     END_CRIT_SECTION();
    1866             : 
    1867             :     /* release metapage */
    1868          42 :     if (BufferIsValid(metabuf))
    1869           0 :         _bt_relbuf(rel, metabuf);
    1870             : 
    1871             :     /* release siblings */
    1872          42 :     if (BufferIsValid(lbuf))
    1873          42 :         _bt_relbuf(rel, lbuf);
    1874          42 :     _bt_relbuf(rel, rbuf);
    1875             : 
    1876             :     /*
    1877             :      * Release the target, if it was not the leaf block.  The leaf is always
    1878             :      * kept locked.
    1879             :      */
    1880          42 :     if (target != leafblkno)
    1881           0 :         _bt_relbuf(rel, buf);
    1882             : 
    1883          42 :     return true;
    1884             : }

Generated by: LCOV version 1.11