LCOV - differential code coverage report
Current view: top level - src/backend/access/spgist - spgvacuum.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 91.3 % 323 295 28 295
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 11 11 11
Baseline: 16@8cea358b128 Branches: 66.3 % 246 163 83 163
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 91.3 % 323 295 28 295
Function coverage date bins:
(240..) days: 100.0 % 11 11 11
Branch coverage date bins:
(240..) days: 66.3 % 246 163 83 163

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * spgvacuum.c
                                  4                 :                :  *    vacuum for SP-GiST
                                  5                 :                :  *
                                  6                 :                :  *
                                  7                 :                :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
                                  8                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                  9                 :                :  *
                                 10                 :                :  * IDENTIFICATION
                                 11                 :                :  *          src/backend/access/spgist/spgvacuum.c
                                 12                 :                :  *
                                 13                 :                :  *-------------------------------------------------------------------------
                                 14                 :                :  */
                                 15                 :                : 
                                 16                 :                : #include "postgres.h"
                                 17                 :                : 
                                 18                 :                : #include "access/genam.h"
                                 19                 :                : #include "access/spgist_private.h"
                                 20                 :                : #include "access/spgxlog.h"
                                 21                 :                : #include "access/transam.h"
                                 22                 :                : #include "access/xloginsert.h"
                                 23                 :                : #include "commands/vacuum.h"
                                 24                 :                : #include "miscadmin.h"
                                 25                 :                : #include "storage/bufmgr.h"
                                 26                 :                : #include "storage/indexfsm.h"
                                 27                 :                : #include "storage/lmgr.h"
                                 28                 :                : #include "utils/snapmgr.h"
                                 29                 :                : 
                                 30                 :                : 
                                 31                 :                : /* Entry in pending-list of TIDs we need to revisit */
                                 32                 :                : typedef struct spgVacPendingItem
                                 33                 :                : {
                                 34                 :                :     ItemPointerData tid;        /* redirection target to visit */
                                 35                 :                :     bool        done;           /* have we dealt with this? */
                                 36                 :                :     struct spgVacPendingItem *next; /* list link */
                                 37                 :                : } spgVacPendingItem;
                                 38                 :                : 
                                 39                 :                : /* Local state for vacuum operations */
                                 40                 :                : typedef struct spgBulkDeleteState
                                 41                 :                : {
                                 42                 :                :     /* Parameters passed in to spgvacuumscan */
                                 43                 :                :     IndexVacuumInfo *info;
                                 44                 :                :     IndexBulkDeleteResult *stats;
                                 45                 :                :     IndexBulkDeleteCallback callback;
                                 46                 :                :     void       *callback_state;
                                 47                 :                : 
                                 48                 :                :     /* Additional working state */
                                 49                 :                :     SpGistState spgstate;       /* for SPGiST operations that need one */
                                 50                 :                :     spgVacPendingItem *pendingList; /* TIDs we need to (re)visit */
                                 51                 :                :     TransactionId myXmin;       /* for detecting newly-added redirects */
                                 52                 :                :     BlockNumber lastFilledBlock;    /* last non-deletable block */
                                 53                 :                : } spgBulkDeleteState;
                                 54                 :                : 
                                 55                 :                : 
                                 56                 :                : /*
                                 57                 :                :  * Add TID to pendingList, but only if not already present.
                                 58                 :                :  *
                                 59                 :                :  * Note that new items are always appended at the end of the list; this
                                 60                 :                :  * ensures that scans of the list don't miss items added during the scan.
                                 61                 :                :  */
                                 62                 :                : static void
 4416 tgl@sss.pgh.pa.us          63                 :CBC       51879 : spgAddPendingTID(spgBulkDeleteState *bds, ItemPointer tid)
                                 64                 :                : {
                                 65                 :                :     spgVacPendingItem *pitem;
                                 66                 :                :     spgVacPendingItem **listLink;
                                 67                 :                : 
                                 68                 :                :     /* search the list for pre-existing entry */
                                 69                 :          51879 :     listLink = &bds->pendingList;
                                 70         [ +  + ]:        4582908 :     while (*listLink != NULL)
                                 71                 :                :     {
                                 72                 :        4548059 :         pitem = *listLink;
                                 73         [ +  + ]:        4548059 :         if (ItemPointerEquals(tid, &pitem->tid))
                                 74                 :          17030 :             return;             /* already in list, do nothing */
                                 75                 :        4531029 :         listLink = &pitem->next;
                                 76                 :                :     }
                                 77                 :                :     /* not there, so append new entry */
                                 78                 :          34849 :     pitem = (spgVacPendingItem *) palloc(sizeof(spgVacPendingItem));
                                 79                 :          34849 :     pitem->tid = *tid;
                                 80                 :          34849 :     pitem->done = false;
                                 81                 :          34849 :     pitem->next = NULL;
                                 82                 :          34849 :     *listLink = pitem;
                                 83                 :                : }
                                 84                 :                : 
                                 85                 :                : /*
                                 86                 :                :  * Clear pendingList
                                 87                 :                :  */
                                 88                 :                : static void
                                 89                 :            263 : spgClearPendingList(spgBulkDeleteState *bds)
                                 90                 :                : {
                                 91                 :                :     spgVacPendingItem *pitem;
                                 92                 :                :     spgVacPendingItem *nitem;
                                 93                 :                : 
                                 94         [ +  + ]:          35112 :     for (pitem = bds->pendingList; pitem != NULL; pitem = nitem)
                                 95                 :                :     {
                                 96                 :          34849 :         nitem = pitem->next;
                                 97                 :                :         /* All items in list should have been dealt with */
                                 98         [ -  + ]:          34849 :         Assert(pitem->done);
                                 99                 :          34849 :         pfree(pitem);
                                100                 :                :     }
                                101                 :            263 :     bds->pendingList = NULL;
                                102                 :            263 : }
                                103                 :                : 
                                104                 :                : /*
                                105                 :                :  * Vacuum a regular (non-root) leaf page
                                106                 :                :  *
                                107                 :                :  * We must delete tuples that are targeted for deletion by the VACUUM,
                                108                 :                :  * but not move any tuples that are referenced by outside links; we assume
                                109                 :                :  * those are the ones that are heads of chains.
                                110                 :                :  *
                                111                 :                :  * If we find a REDIRECT that was made by a concurrently-running transaction,
                                112                 :                :  * we must add its target TID to pendingList.  (We don't try to visit the
                                113                 :                :  * target immediately, first because we don't want VACUUM locking more than
                                114                 :                :  * one buffer at a time, and second because the duplicate-filtering logic
                                115                 :                :  * in spgAddPendingTID is useful to ensure we can't get caught in an infinite
                                116                 :                :  * loop in the face of continuous concurrent insertions.)
                                117                 :                :  *
                                118                 :                :  * If forPending is true, we are examining the page as a consequence of
                                119                 :                :  * chasing a redirect link, not as part of the normal sequential scan.
                                120                 :                :  * We still vacuum the page normally, but we don't increment the stats
                                121                 :                :  * about live tuples; else we'd double-count those tuples, since the page
                                122                 :                :  * has been or will be visited in the sequential scan as well.
                                123                 :                :  */
                                124                 :                : static void
                                125                 :          19842 : vacuumLeafPage(spgBulkDeleteState *bds, Relation index, Buffer buffer,
                                126                 :                :                bool forPending)
                                127                 :                : {
 2916 kgrittn@postgresql.o      128                 :          19842 :     Page        page = BufferGetPage(buffer);
                                129                 :                :     spgxlogVacuumLeaf xlrec;
                                130                 :                :     OffsetNumber toDead[MaxIndexTuplesPerPage];
                                131                 :                :     OffsetNumber toPlaceholder[MaxIndexTuplesPerPage];
                                132                 :                :     OffsetNumber moveSrc[MaxIndexTuplesPerPage];
                                133                 :                :     OffsetNumber moveDest[MaxIndexTuplesPerPage];
                                134                 :                :     OffsetNumber chainSrc[MaxIndexTuplesPerPage];
                                135                 :                :     OffsetNumber chainDest[MaxIndexTuplesPerPage];
                                136                 :                :     OffsetNumber predecessor[MaxIndexTuplesPerPage + 1];
                                137                 :                :     bool        deletable[MaxIndexTuplesPerPage + 1];
                                138                 :                :     int         nDeletable;
                                139                 :                :     OffsetNumber i,
 4502 tgl@sss.pgh.pa.us         140                 :          19842 :                 max = PageGetMaxOffsetNumber(page);
                                141                 :                : 
                                142                 :          19842 :     memset(predecessor, 0, sizeof(predecessor));
                                143                 :          19842 :     memset(deletable, 0, sizeof(deletable));
                                144                 :          19842 :     nDeletable = 0;
                                145                 :                : 
                                146                 :                :     /* Scan page, identify tuples to delete, accumulate stats */
                                147         [ +  + ]:        3142016 :     for (i = FirstOffsetNumber; i <= max; i++)
                                148                 :                :     {
                                149                 :                :         SpGistLeafTuple lt;
                                150                 :                : 
                                151                 :        3122174 :         lt = (SpGistLeafTuple) PageGetItem(page,
                                152                 :                :                                            PageGetItemId(page, i));
                                153         [ +  + ]:        3122174 :         if (lt->tupstate == SPGIST_LIVE)
                                154                 :                :         {
                                155         [ -  + ]:        2975881 :             Assert(ItemPointerIsValid(&lt->heapPtr));
                                156                 :                : 
                                157         [ +  + ]:        2975881 :             if (bds->callback(&lt->heapPtr, bds->callback_state))
                                158                 :                :             {
                                159                 :          48020 :                 bds->stats->tuples_removed += 1;
                                160                 :          48020 :                 deletable[i] = true;
                                161                 :          48020 :                 nDeletable++;
                                162                 :                :             }
                                163                 :                :             else
                                164                 :                :             {
 4416                           165         [ +  + ]:        2927861 :                 if (!forPending)
                                166                 :         306905 :                     bds->stats->num_index_tuples += 1;
                                167                 :                :             }
                                168                 :                : 
                                169                 :                :             /* Form predecessor map, too */
 1105                           170         [ +  + ]:        2975881 :             if (SGLT_GET_NEXTOFFSET(lt) != InvalidOffsetNumber)
                                171                 :                :             {
                                172                 :                :                 /* paranoia about corrupted chain links */
                                173         [ +  - ]:        2950484 :                 if (SGLT_GET_NEXTOFFSET(lt) < FirstOffsetNumber ||
                                174         [ +  - ]:        2950484 :                     SGLT_GET_NEXTOFFSET(lt) > max ||
                                175         [ -  + ]:        2950484 :                     predecessor[SGLT_GET_NEXTOFFSET(lt)] != InvalidOffsetNumber)
 4502 tgl@sss.pgh.pa.us         176         [ #  # ]:UBC           0 :                     elog(ERROR, "inconsistent tuple chain links in page %u of index \"%s\"",
                                177                 :                :                          BufferGetBlockNumber(buffer),
                                178                 :                :                          RelationGetRelationName(index));
 1105 tgl@sss.pgh.pa.us         179                 :CBC     2950484 :                 predecessor[SGLT_GET_NEXTOFFSET(lt)] = i;
                                180                 :                :             }
                                181                 :                :         }
 4416                           182         [ +  + ]:         146293 :         else if (lt->tupstate == SPGIST_REDIRECT)
                                183                 :                :         {
                                184                 :          17818 :             SpGistDeadTuple dt = (SpGistDeadTuple) lt;
                                185                 :                : 
 1105                           186         [ -  + ]:          17818 :             Assert(SGLT_GET_NEXTOFFSET(dt) == InvalidOffsetNumber);
 4416                           187         [ -  + ]:          17818 :             Assert(ItemPointerIsValid(&dt->pointer));
                                188                 :                : 
                                189                 :                :             /*
                                190                 :                :              * Add target TID to pending list if the redirection could have
                                191                 :                :              * happened since VACUUM started.
                                192                 :                :              *
                                193                 :                :              * Note: we could make a tighter test by seeing if the xid is
                                194                 :                :              * "running" according to the active snapshot; but snapmgr.c
                                195                 :                :              * doesn't currently export a suitable API, and it's not entirely
                                196                 :                :              * clear that a tighter test is worth the cycles anyway.
                                197                 :                :              */
                                198         [ +  + ]:          17818 :             if (TransactionIdFollowsOrEquals(dt->xid, bds->myXmin))
                                199                 :          17293 :                 spgAddPendingTID(bds, &dt->pointer);
                                200                 :                :         }
                                201                 :                :         else
                                202                 :                :         {
 1105                           203         [ -  + ]:         128475 :             Assert(SGLT_GET_NEXTOFFSET(lt) == InvalidOffsetNumber);
                                204                 :                :         }
                                205                 :                :     }
                                206                 :                : 
 4502                           207         [ +  + ]:          19842 :     if (nDeletable == 0)
                                208                 :          19393 :         return;                 /* nothing more to do */
                                209                 :                : 
                                210                 :                :     /*----------
                                211                 :                :      * Figure out exactly what we have to do.  We do this separately from
                                212                 :                :      * actually modifying the page, mainly so that we have a representation
                                213                 :                :      * that can be dumped into WAL and then the replay code can do exactly
                                214                 :                :      * the same thing.  The output of this step consists of six arrays
                                215                 :                :      * describing four kinds of operations, to be performed in this order:
                                216                 :                :      *
                                217                 :                :      * toDead[]: tuple numbers to be replaced with DEAD tuples
                                218                 :                :      * toPlaceholder[]: tuple numbers to be replaced with PLACEHOLDER tuples
                                219                 :                :      * moveSrc[]: tuple numbers that need to be relocated to another offset
                                220                 :                :      * (replacing the tuple there) and then replaced with PLACEHOLDER tuples
                                221                 :                :      * moveDest[]: new locations for moveSrc tuples
                                222                 :                :      * chainSrc[]: tuple numbers whose chain links (nextOffset) need updates
                                223                 :                :      * chainDest[]: new values of nextOffset for chainSrc members
                                224                 :                :      *
                                225                 :                :      * It's easiest to figure out what we have to do by processing tuple
                                226                 :                :      * chains, so we iterate over all the tuples (not just the deletable
                                227                 :                :      * ones!) to identify chain heads, then chase down each chain and make
                                228                 :                :      * work item entries for deletable tuples within the chain.
                                229                 :                :      *----------
                                230                 :                :      */
                                231                 :            449 :     xlrec.nDead = xlrec.nPlaceholder = xlrec.nMove = xlrec.nChain = 0;
                                232                 :                : 
                                233         [ +  + ]:          99338 :     for (i = FirstOffsetNumber; i <= max; i++)
                                234                 :                :     {
                                235                 :                :         SpGistLeafTuple head;
                                236                 :                :         bool        interveningDeletable;
                                237                 :                :         OffsetNumber prevLive;
                                238                 :                :         OffsetNumber j;
                                239                 :                : 
                                240                 :          98889 :         head = (SpGistLeafTuple) PageGetItem(page,
                                241                 :                :                                              PageGetItemId(page, i));
                                242         [ +  + ]:          98889 :         if (head->tupstate != SPGIST_LIVE)
                                243                 :          30922 :             continue;           /* can't be a chain member */
                                244         [ +  + ]:          67967 :         if (predecessor[i] != 0)
                                245                 :          67499 :             continue;           /* not a chain head */
                                246                 :                : 
                                247                 :                :         /* initialize ... */
                                248                 :            468 :         interveningDeletable = false;
                                249         [ +  + ]:            468 :         prevLive = deletable[i] ? InvalidOffsetNumber : i;
                                250                 :                : 
                                251                 :                :         /* scan down the chain ... */
 1105                           252                 :            468 :         j = SGLT_GET_NEXTOFFSET(head);
 4502                           253         [ +  + ]:          67967 :         while (j != InvalidOffsetNumber)
                                254                 :                :         {
                                255                 :                :             SpGistLeafTuple lt;
                                256                 :                : 
                                257                 :          67499 :             lt = (SpGistLeafTuple) PageGetItem(page,
                                258                 :                :                                                PageGetItemId(page, j));
                                259         [ -  + ]:          67499 :             if (lt->tupstate != SPGIST_LIVE)
                                260                 :                :             {
                                261                 :                :                 /* all tuples in chain should be live */
 4502 tgl@sss.pgh.pa.us         262         [ #  # ]:UBC           0 :                 elog(ERROR, "unexpected SPGiST tuple state: %d",
                                263                 :                :                      lt->tupstate);
                                264                 :                :             }
                                265                 :                : 
 4502 tgl@sss.pgh.pa.us         266         [ +  + ]:CBC       67499 :             if (deletable[j])
                                267                 :                :             {
                                268                 :                :                 /* This tuple should be replaced by a placeholder */
                                269                 :          47583 :                 toPlaceholder[xlrec.nPlaceholder] = j;
                                270                 :          47583 :                 xlrec.nPlaceholder++;
                                271                 :                :                 /* previous live tuple's chain link will need an update */
                                272                 :          47583 :                 interveningDeletable = true;
                                273                 :                :             }
                                274         [ +  + ]:          19916 :             else if (prevLive == InvalidOffsetNumber)
                                275                 :                :             {
                                276                 :                :                 /*
                                277                 :                :                  * This is the first live tuple in the chain.  It has to move
                                278                 :                :                  * to the head position.
                                279                 :                :                  */
                                280                 :            437 :                 moveSrc[xlrec.nMove] = j;
                                281                 :            437 :                 moveDest[xlrec.nMove] = i;
                                282                 :            437 :                 xlrec.nMove++;
                                283                 :                :                 /* Chain updates will be applied after the move */
                                284                 :            437 :                 prevLive = i;
                                285                 :            437 :                 interveningDeletable = false;
                                286                 :                :             }
                                287                 :                :             else
                                288                 :                :             {
                                289                 :                :                 /*
                                290                 :                :                  * Second or later live tuple.  Arrange to re-chain it to the
                                291                 :                :                  * previous live one, if there was a gap.
                                292                 :                :                  */
                                293         [ +  + ]:          19479 :                 if (interveningDeletable)
                                294                 :                :                 {
                                295                 :          14620 :                     chainSrc[xlrec.nChain] = prevLive;
                                296                 :          14620 :                     chainDest[xlrec.nChain] = j;
                                297                 :          14620 :                     xlrec.nChain++;
                                298                 :                :                 }
                                299                 :          19479 :                 prevLive = j;
                                300                 :          19479 :                 interveningDeletable = false;
                                301                 :                :             }
                                302                 :                : 
 1105                           303                 :          67499 :             j = SGLT_GET_NEXTOFFSET(lt);
                                304                 :                :         }
                                305                 :                : 
 4502                           306         [ -  + ]:            468 :         if (prevLive == InvalidOffsetNumber)
                                307                 :                :         {
                                308                 :                :             /* The chain is entirely removable, so we need a DEAD tuple */
 4502 tgl@sss.pgh.pa.us         309                 :UBC           0 :             toDead[xlrec.nDead] = i;
                                310                 :              0 :             xlrec.nDead++;
                                311                 :                :         }
 4502 tgl@sss.pgh.pa.us         312         [ +  + ]:CBC         468 :         else if (interveningDeletable)
                                313                 :                :         {
                                314                 :                :             /* One or more deletions at end of chain, so close it off */
                                315                 :            454 :             chainSrc[xlrec.nChain] = prevLive;
                                316                 :            454 :             chainDest[xlrec.nChain] = InvalidOffsetNumber;
                                317                 :            454 :             xlrec.nChain++;
                                318                 :                :         }
                                319                 :                :     }
                                320                 :                : 
                                321                 :                :     /* sanity check ... */
                                322         [ -  + ]:            449 :     if (nDeletable != xlrec.nDead + xlrec.nPlaceholder + xlrec.nMove)
 4502 tgl@sss.pgh.pa.us         323         [ #  # ]:UBC           0 :         elog(ERROR, "inconsistent counts of deletable tuples");
                                324                 :                : 
                                325                 :                :     /* Do the updates */
 4502 tgl@sss.pgh.pa.us         326                 :CBC         449 :     START_CRIT_SECTION();
                                327                 :                : 
                                328                 :            449 :     spgPageIndexMultiDelete(&bds->spgstate, page,
                                329                 :            449 :                             toDead, xlrec.nDead,
                                330                 :                :                             SPGIST_DEAD, SPGIST_DEAD,
                                331                 :                :                             InvalidBlockNumber, InvalidOffsetNumber);
                                332                 :                : 
                                333                 :            449 :     spgPageIndexMultiDelete(&bds->spgstate, page,
                                334                 :            449 :                             toPlaceholder, xlrec.nPlaceholder,
                                335                 :                :                             SPGIST_PLACEHOLDER, SPGIST_PLACEHOLDER,
                                336                 :                :                             InvalidBlockNumber, InvalidOffsetNumber);
                                337                 :                : 
                                338                 :                :     /*
                                339                 :                :      * We implement the move step by swapping the line pointers of the source
                                340                 :                :      * and target tuples, then replacing the newly-source tuples with
                                341                 :                :      * placeholders.  This is perhaps unduly friendly with the page data
                                342                 :                :      * representation, but it's fast and doesn't risk page overflow when a
                                343                 :                :      * tuple to be relocated is large.
                                344                 :                :      */
                                345         [ +  + ]:            886 :     for (i = 0; i < xlrec.nMove; i++)
                                346                 :                :     {
                                347                 :            437 :         ItemId      idSrc = PageGetItemId(page, moveSrc[i]);
                                348                 :            437 :         ItemId      idDest = PageGetItemId(page, moveDest[i]);
                                349                 :                :         ItemIdData  tmp;
                                350                 :                : 
                                351                 :            437 :         tmp = *idSrc;
                                352                 :            437 :         *idSrc = *idDest;
                                353                 :            437 :         *idDest = tmp;
                                354                 :                :     }
                                355                 :                : 
                                356                 :            449 :     spgPageIndexMultiDelete(&bds->spgstate, page,
                                357                 :            449 :                             moveSrc, xlrec.nMove,
                                358                 :                :                             SPGIST_PLACEHOLDER, SPGIST_PLACEHOLDER,
                                359                 :                :                             InvalidBlockNumber, InvalidOffsetNumber);
                                360                 :                : 
                                361         [ +  + ]:          15523 :     for (i = 0; i < xlrec.nChain; i++)
                                362                 :                :     {
                                363                 :                :         SpGistLeafTuple lt;
                                364                 :                : 
                                365                 :          15074 :         lt = (SpGistLeafTuple) PageGetItem(page,
                                366                 :          15074 :                                            PageGetItemId(page, chainSrc[i]));
                                367         [ -  + ]:          15074 :         Assert(lt->tupstate == SPGIST_LIVE);
 1105                           368                 :          15074 :         SGLT_SET_NEXTOFFSET(lt, chainDest[i]);
                                369                 :                :     }
                                370                 :                : 
 4502                           371                 :            449 :     MarkBufferDirty(buffer);
                                372                 :                : 
                                373   [ +  -  +  +  :            449 :     if (RelationNeedsWAL(index))
                                        +  -  +  - ]
                                374                 :                :     {
                                375                 :                :         XLogRecPtr  recptr;
                                376                 :                : 
 3433 heikki.linnakangas@i      377                 :            449 :         XLogBeginInsert();
                                378                 :                : 
                                379                 :            449 :         STORE_STATE(&bds->spgstate, xlrec.stateSrc);
                                380                 :                : 
                                381                 :            449 :         XLogRegisterData((char *) &xlrec, SizeOfSpgxlogVacuumLeaf);
                                382                 :                :         /* sizeof(xlrec) should be a multiple of sizeof(OffsetNumber) */
                                383                 :            449 :         XLogRegisterData((char *) toDead, sizeof(OffsetNumber) * xlrec.nDead);
                                384                 :            449 :         XLogRegisterData((char *) toPlaceholder, sizeof(OffsetNumber) * xlrec.nPlaceholder);
                                385                 :            449 :         XLogRegisterData((char *) moveSrc, sizeof(OffsetNumber) * xlrec.nMove);
                                386                 :            449 :         XLogRegisterData((char *) moveDest, sizeof(OffsetNumber) * xlrec.nMove);
                                387                 :            449 :         XLogRegisterData((char *) chainSrc, sizeof(OffsetNumber) * xlrec.nChain);
                                388                 :            449 :         XLogRegisterData((char *) chainDest, sizeof(OffsetNumber) * xlrec.nChain);
                                389                 :                : 
                                390                 :            449 :         XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
                                391                 :                : 
                                392                 :            449 :         recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_VACUUM_LEAF);
                                393                 :                : 
 4502 tgl@sss.pgh.pa.us         394                 :            449 :         PageSetLSN(page, recptr);
                                395                 :                :     }
                                396                 :                : 
                                397         [ -  + ]:            449 :     END_CRIT_SECTION();
                                398                 :                : }
                                399                 :                : 
                                400                 :                : /*
                                401                 :                :  * Vacuum a root page when it is also a leaf
                                402                 :                :  *
                                403                 :                :  * On the root, we just delete any dead leaf tuples; no fancy business
                                404                 :                :  */
                                405                 :                : static void
                                406                 :             16 : vacuumLeafRoot(spgBulkDeleteState *bds, Relation index, Buffer buffer)
                                407                 :                : {
 2916 kgrittn@postgresql.o      408                 :             16 :     Page        page = BufferGetPage(buffer);
                                409                 :                :     spgxlogVacuumRoot xlrec;
                                410                 :                :     OffsetNumber toDelete[MaxIndexTuplesPerPage];
                                411                 :                :     OffsetNumber i,
 4502 tgl@sss.pgh.pa.us         412                 :             16 :                 max = PageGetMaxOffsetNumber(page);
                                413                 :                : 
                                414                 :             16 :     xlrec.nDelete = 0;
                                415                 :                : 
                                416                 :                :     /* Scan page, identify tuples to delete, accumulate stats */
                                417         [ +  + ]:             82 :     for (i = FirstOffsetNumber; i <= max; i++)
                                418                 :                :     {
                                419                 :                :         SpGistLeafTuple lt;
                                420                 :                : 
                                421                 :             66 :         lt = (SpGistLeafTuple) PageGetItem(page,
                                422                 :                :                                            PageGetItemId(page, i));
                                423         [ +  - ]:             66 :         if (lt->tupstate == SPGIST_LIVE)
                                424                 :                :         {
                                425         [ -  + ]:             66 :             Assert(ItemPointerIsValid(&lt->heapPtr));
                                426                 :                : 
                                427         [ -  + ]:             66 :             if (bds->callback(&lt->heapPtr, bds->callback_state))
                                428                 :                :             {
 4502 tgl@sss.pgh.pa.us         429                 :UBC           0 :                 bds->stats->tuples_removed += 1;
                                430                 :              0 :                 toDelete[xlrec.nDelete] = i;
                                431                 :              0 :                 xlrec.nDelete++;
                                432                 :                :             }
                                433                 :                :             else
                                434                 :                :             {
 4502 tgl@sss.pgh.pa.us         435                 :CBC          66 :                 bds->stats->num_index_tuples += 1;
                                436                 :                :             }
                                437                 :                :         }
                                438                 :                :         else
                                439                 :                :         {
                                440                 :                :             /* all tuples on root should be live */
 4502 tgl@sss.pgh.pa.us         441         [ #  # ]:UBC           0 :             elog(ERROR, "unexpected SPGiST tuple state: %d",
                                442                 :                :                  lt->tupstate);
                                443                 :                :         }
                                444                 :                :     }
                                445                 :                : 
 4502 tgl@sss.pgh.pa.us         446         [ +  - ]:CBC          16 :     if (xlrec.nDelete == 0)
                                447                 :             16 :         return;                 /* nothing more to do */
                                448                 :                : 
                                449                 :                :     /* Do the update */
 4502 tgl@sss.pgh.pa.us         450                 :UBC           0 :     START_CRIT_SECTION();
                                451                 :                : 
                                452                 :                :     /* The tuple numbers are in order, so we can use PageIndexMultiDelete */
                                453                 :              0 :     PageIndexMultiDelete(page, toDelete, xlrec.nDelete);
                                454                 :                : 
                                455                 :              0 :     MarkBufferDirty(buffer);
                                456                 :                : 
                                457   [ #  #  #  #  :              0 :     if (RelationNeedsWAL(index))
                                        #  #  #  # ]
                                458                 :                :     {
                                459                 :                :         XLogRecPtr  recptr;
                                460                 :                : 
 3433 heikki.linnakangas@i      461                 :              0 :         XLogBeginInsert();
                                462                 :                : 
                                463                 :                :         /* Prepare WAL record */
                                464                 :              0 :         STORE_STATE(&bds->spgstate, xlrec.stateSrc);
                                465                 :                : 
                                466                 :              0 :         XLogRegisterData((char *) &xlrec, SizeOfSpgxlogVacuumRoot);
                                467                 :                :         /* sizeof(xlrec) should be a multiple of sizeof(OffsetNumber) */
                                468                 :              0 :         XLogRegisterData((char *) toDelete,
                                469                 :              0 :                          sizeof(OffsetNumber) * xlrec.nDelete);
                                470                 :                : 
                                471                 :              0 :         XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
                                472                 :                : 
                                473                 :              0 :         recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_VACUUM_ROOT);
                                474                 :                : 
 4502 tgl@sss.pgh.pa.us         475                 :              0 :         PageSetLSN(page, recptr);
                                476                 :                :     }
                                477                 :                : 
                                478         [ #  # ]:              0 :     END_CRIT_SECTION();
                                479                 :                : }
                                480                 :                : 
                                481                 :                : /*
                                482                 :                :  * Clean up redirect and placeholder tuples on the given page
                                483                 :                :  *
                                484                 :                :  * Redirect tuples can be marked placeholder once they're old enough.
                                485                 :                :  * Placeholder tuples can be removed if it won't change the offsets of
                                486                 :                :  * non-placeholder ones.
                                487                 :                :  *
                                488                 :                :  * Unlike the routines above, this works on both leaf and inner pages.
                                489                 :                :  */
                                490                 :                : static void
  379 andres@anarazel.de        491                 :CBC       19937 : vacuumRedirectAndPlaceholder(Relation index, Relation heaprel, Buffer buffer)
                                492                 :                : {
 2916 kgrittn@postgresql.o      493                 :          19937 :     Page        page = BufferGetPage(buffer);
 4502 tgl@sss.pgh.pa.us         494                 :          19937 :     SpGistPageOpaque opaque = SpGistPageGetOpaque(page);
                                495                 :                :     OffsetNumber i,
                                496                 :          19937 :                 max = PageGetMaxOffsetNumber(page),
                                497                 :          19937 :                 firstPlaceholder = InvalidOffsetNumber;
                                498                 :          19937 :     bool        hasNonPlaceholder = false;
                                499                 :          19937 :     bool        hasUpdate = false;
                                500                 :                :     OffsetNumber itemToPlaceholder[MaxIndexTuplesPerPage];
                                501                 :                :     OffsetNumber itemnos[MaxIndexTuplesPerPage];
                                502                 :                :     spgxlogVacuumRedirect xlrec;
                                503                 :                :     GlobalVisState *vistest;
                                504                 :                : 
  378 andres@anarazel.de        505   [ -  +  -  -  :          19937 :     xlrec.isCatalogRel = RelationIsAccessibleInLogicalDecoding(heaprel);
                                     -  -  -  -  -  
                                     -  -  -  -  -  
                                     -  -  -  -  -  
                                                 - ]
 4502 tgl@sss.pgh.pa.us         506                 :          19937 :     xlrec.nToPlaceholder = 0;
  514 pg@bowt.ie                507                 :          19937 :     xlrec.snapshotConflictHorizon = InvalidTransactionId;
                                508                 :                : 
  377                           509                 :          19937 :     vistest = GlobalVisTestFor(heaprel);
                                510                 :                : 
 4502 tgl@sss.pgh.pa.us         511                 :          19937 :     START_CRIT_SECTION();
                                512                 :                : 
                                513                 :                :     /*
                                514                 :                :      * Scan backwards to convert old redirection tuples to placeholder tuples,
                                515                 :                :      * and identify location of last non-placeholder tuple while at it.
                                516                 :                :      */
                                517                 :          19937 :     for (i = max;
                                518         [ +  + ]:        2794675 :          i >= FirstOffsetNumber &&
 4326 bruce@momjian.us          519   [ +  +  +  + ]:        2777179 :          (opaque->nRedirection > 0 || !hasNonPlaceholder);
 4502 tgl@sss.pgh.pa.us         520                 :        2774738 :          i--)
                                521                 :                :     {
                                522                 :                :         SpGistDeadTuple dt;
                                523                 :                : 
                                524                 :        2774738 :         dt = (SpGistDeadTuple) PageGetItem(page, PageGetItemId(page, i));
                                525                 :                : 
                                526   [ +  +  +  + ]:        2792559 :         if (dt->tupstate == SPGIST_REDIRECT &&
 1341 andres@anarazel.de        527                 :          17821 :             GlobalVisTestIsRemovableXid(vistest, dt->xid))
                                528                 :                :         {
 4502 tgl@sss.pgh.pa.us         529                 :            368 :             dt->tupstate = SPGIST_PLACEHOLDER;
                                530         [ -  + ]:            368 :             Assert(opaque->nRedirection > 0);
                                531                 :            368 :             opaque->nRedirection--;
                                532                 :            368 :             opaque->nPlaceholder++;
                                533                 :                : 
                                534                 :                :             /* remember newest XID among the removed redirects */
  514 pg@bowt.ie                535   [ +  +  -  + ]:            667 :             if (!TransactionIdIsValid(xlrec.snapshotConflictHorizon) ||
                                536                 :            299 :                 TransactionIdPrecedes(xlrec.snapshotConflictHorizon, dt->xid))
                                537                 :             69 :                 xlrec.snapshotConflictHorizon = dt->xid;
                                538                 :                : 
 4502 tgl@sss.pgh.pa.us         539                 :            368 :             ItemPointerSetInvalid(&dt->pointer);
                                540                 :                : 
                                541                 :            368 :             itemToPlaceholder[xlrec.nToPlaceholder] = i;
                                542                 :            368 :             xlrec.nToPlaceholder++;
                                543                 :                : 
                                544                 :            368 :             hasUpdate = true;
                                545                 :                :         }
                                546                 :                : 
                                547         [ +  + ]:        2774738 :         if (dt->tupstate == SPGIST_PLACEHOLDER)
                                548                 :                :         {
                                549         [ +  + ]:         104504 :             if (!hasNonPlaceholder)
                                550                 :          99662 :                 firstPlaceholder = i;
                                551                 :                :         }
                                552                 :                :         else
                                553                 :                :         {
                                554                 :        2670234 :             hasNonPlaceholder = true;
                                555                 :                :         }
                                556                 :                :     }
                                557                 :                : 
                                558                 :                :     /*
                                559                 :                :      * Any placeholder tuples at the end of page can safely be removed.  We
                                560                 :                :      * can't remove ones before the last non-placeholder, though, because we
                                561                 :                :      * can't alter the offset numbers of non-placeholder tuples.
                                562                 :                :      */
                                563         [ +  + ]:          19937 :     if (firstPlaceholder != InvalidOffsetNumber)
                                564                 :                :     {
                                565                 :                :         /*
                                566                 :                :          * We do not store this array to rdata because it's easy to recreate.
                                567                 :                :          */
                                568         [ +  + ]:         101241 :         for (i = firstPlaceholder; i <= max; i++)
                                569                 :          99662 :             itemnos[i - firstPlaceholder] = i;
                                570                 :                : 
                                571                 :           1579 :         i = max - firstPlaceholder + 1;
                                572         [ -  + ]:           1579 :         Assert(opaque->nPlaceholder >= i);
                                573                 :           1579 :         opaque->nPlaceholder -= i;
                                574                 :                : 
                                575                 :                :         /* The array is surely sorted, so can use PageIndexMultiDelete */
                                576                 :           1579 :         PageIndexMultiDelete(page, itemnos, i);
                                577                 :                : 
                                578                 :           1579 :         hasUpdate = true;
                                579                 :                :     }
                                580                 :                : 
                                581                 :          19937 :     xlrec.firstPlaceholder = firstPlaceholder;
                                582                 :                : 
                                583         [ +  + ]:          19937 :     if (hasUpdate)
                                584                 :           1585 :         MarkBufferDirty(buffer);
                                585                 :                : 
                                586   [ +  +  +  -  :          19937 :     if (hasUpdate && RelationNeedsWAL(index))
                                     +  +  +  -  +  
                                                 - ]
                                587                 :                :     {
                                588                 :                :         XLogRecPtr  recptr;
                                589                 :                : 
 3433 heikki.linnakangas@i      590                 :           1585 :         XLogBeginInsert();
                                591                 :                : 
                                592                 :           1585 :         XLogRegisterData((char *) &xlrec, SizeOfSpgxlogVacuumRedirect);
                                593                 :           1585 :         XLogRegisterData((char *) itemToPlaceholder,
                                594                 :           1585 :                          sizeof(OffsetNumber) * xlrec.nToPlaceholder);
                                595                 :                : 
                                596                 :           1585 :         XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
                                597                 :                : 
                                598                 :           1585 :         recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_VACUUM_REDIRECT);
                                599                 :                : 
 4502 tgl@sss.pgh.pa.us         600                 :           1585 :         PageSetLSN(page, recptr);
                                601                 :                :     }
                                602                 :                : 
                                603         [ -  + ]:          19937 :     END_CRIT_SECTION();
                                604                 :          19937 : }
                                605                 :                : 
                                606                 :                : /*
                                607                 :                :  * Process one page during a bulkdelete scan
                                608                 :                :  */
                                609                 :                : static void
                                610                 :           2725 : spgvacuumpage(spgBulkDeleteState *bds, BlockNumber blkno)
                                611                 :                : {
                                612                 :           2725 :     Relation    index = bds->info->index;
                                613                 :                :     Buffer      buffer;
                                614                 :                :     Page        page;
                                615                 :                : 
                                616                 :                :     /* call vacuum_delay_point while not holding any buffer lock */
                                617                 :           2725 :     vacuum_delay_point();
                                618                 :                : 
                                619                 :           2725 :     buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                620                 :           2725 :                                 RBM_NORMAL, bds->info->strategy);
                                621                 :           2725 :     LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 2916 kgrittn@postgresql.o      622                 :           2725 :     page = (Page) BufferGetPage(buffer);
                                623                 :                : 
 4502 tgl@sss.pgh.pa.us         624         [ +  + ]:           2725 :     if (PageIsNew(page))
                                625                 :                :     {
                                626                 :                :         /*
                                627                 :                :          * We found an all-zero page, which could happen if the database
                                628                 :                :          * crashed just after extending the file.  Recycle it.
                                629                 :                :          */
                                630                 :                :     }
 3184 heikki.linnakangas@i      631         [ +  + ]:           2716 :     else if (PageIsEmpty(page))
                                632                 :                :     {
                                633                 :                :         /* nothing to do */
                                634                 :                :     }
 4502 tgl@sss.pgh.pa.us         635         [ +  + ]:           2660 :     else if (SpGistPageIsLeaf(page))
                                636                 :                :     {
 4417                           637   [ +  +  +  + ]:           2565 :         if (SpGistBlockIsRoot(blkno))
                                638                 :                :         {
 4502                           639                 :             16 :             vacuumLeafRoot(bds, index, buffer);
                                640                 :                :             /* no need for vacuumRedirectAndPlaceholder */
                                641                 :                :         }
                                642                 :                :         else
                                643                 :                :         {
 4416                           644                 :           2549 :             vacuumLeafPage(bds, index, buffer, false);
  379 andres@anarazel.de        645                 :           2549 :             vacuumRedirectAndPlaceholder(index, bds->info->heaprel, buffer);
                                646                 :                :         }
                                647                 :                :     }
                                648                 :                :     else
                                649                 :                :     {
                                650                 :                :         /* inner page */
                                651                 :             95 :         vacuumRedirectAndPlaceholder(index, bds->info->heaprel, buffer);
                                652                 :                :     }
                                653                 :                : 
                                654                 :                :     /*
                                655                 :                :      * The root pages must never be deleted, nor marked as available in FSM,
                                656                 :                :      * because we don't want them ever returned by a search for a place to put
                                657                 :                :      * a new tuple.  Otherwise, check for empty page, and make sure the FSM
                                658                 :                :      * knows about it.
                                659                 :                :      */
 4417 tgl@sss.pgh.pa.us         660   [ +  +  +  + ]:           2725 :     if (!SpGistBlockIsRoot(blkno))
                                661                 :                :     {
 3184 heikki.linnakangas@i      662   [ +  +  +  + ]:           2637 :         if (PageIsNew(page) || PageIsEmpty(page))
                                663                 :                :         {
 4502 tgl@sss.pgh.pa.us         664                 :             36 :             RecordFreeIndexPage(index, blkno);
                                665                 :             36 :             bds->stats->pages_deleted++;
                                666                 :                :         }
                                667                 :                :         else
                                668                 :                :         {
 3184 heikki.linnakangas@i      669                 :           2601 :             SpGistSetLastUsedPage(index, buffer);
 4502 tgl@sss.pgh.pa.us         670                 :           2601 :             bds->lastFilledBlock = blkno;
                                671                 :                :         }
                                672                 :                :     }
                                673                 :                : 
                                674                 :           2725 :     UnlockReleaseBuffer(buffer);
                                675                 :           2725 : }
                                676                 :                : 
                                677                 :                : /*
                                678                 :                :  * Process the pending-TID list between pages of the main scan
                                679                 :                :  */
                                680                 :                : static void
 4416                           681                 :            263 : spgprocesspending(spgBulkDeleteState *bds)
                                682                 :                : {
                                683                 :            263 :     Relation    index = bds->info->index;
                                684                 :                :     spgVacPendingItem *pitem;
                                685                 :                :     spgVacPendingItem *nitem;
                                686                 :                :     BlockNumber blkno;
                                687                 :                :     Buffer      buffer;
                                688                 :                :     Page        page;
                                689                 :                : 
                                690         [ +  + ]:          35112 :     for (pitem = bds->pendingList; pitem != NULL; pitem = pitem->next)
                                691                 :                :     {
                                692         [ +  + ]:          34849 :         if (pitem->done)
                                693                 :          17293 :             continue;           /* ignore already-done items */
                                694                 :                : 
                                695                 :                :         /* call vacuum_delay_point while not holding any buffer lock */
                                696                 :          17556 :         vacuum_delay_point();
                                697                 :                : 
                                698                 :                :         /* examine the referenced page */
                                699                 :          17556 :         blkno = ItemPointerGetBlockNumber(&pitem->tid);
                                700                 :          17556 :         buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                701                 :          17556 :                                     RBM_NORMAL, bds->info->strategy);
                                702                 :          17556 :         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
 2916 kgrittn@postgresql.o      703                 :          17556 :         page = (Page) BufferGetPage(buffer);
                                704                 :                : 
 4416 tgl@sss.pgh.pa.us         705   [ +  -  +  - ]:          17556 :         if (PageIsNew(page) || SpGistPageIsDeleted(page))
                                706                 :                :         {
                                707                 :                :             /* Probably shouldn't happen, but ignore it */
                                708                 :                :         }
                                709         [ +  + ]:          17556 :         else if (SpGistPageIsLeaf(page))
                                710                 :                :         {
                                711   [ +  -  -  + ]:          17293 :             if (SpGistBlockIsRoot(blkno))
                                712                 :                :             {
                                713                 :                :                 /* this should definitely not happen */
 4416 tgl@sss.pgh.pa.us         714         [ #  # ]:UBC           0 :                 elog(ERROR, "redirection leads to root page of index \"%s\"",
                                715                 :                :                      RelationGetRelationName(index));
                                716                 :                :             }
                                717                 :                : 
                                718                 :                :             /* deal with any deletable tuples */
 4416 tgl@sss.pgh.pa.us         719                 :CBC       17293 :             vacuumLeafPage(bds, index, buffer, true);
                                720                 :                :             /* might as well do this while we are here */
  379 andres@anarazel.de        721                 :          17293 :             vacuumRedirectAndPlaceholder(index, bds->info->heaprel, buffer);
                                722                 :                : 
 4416 tgl@sss.pgh.pa.us         723                 :          17293 :             SpGistSetLastUsedPage(index, buffer);
                                724                 :                : 
                                725                 :                :             /*
                                726                 :                :              * We can mark as done not only this item, but any later ones
                                727                 :                :              * pointing at the same page, since we vacuumed the whole page.
                                728                 :                :              */
                                729                 :          17293 :             pitem->done = true;
                                730         [ +  + ]:        1516456 :             for (nitem = pitem->next; nitem != NULL; nitem = nitem->next)
                                731                 :                :             {
                                732         [ +  + ]:        1499163 :                 if (ItemPointerGetBlockNumber(&nitem->tid) == blkno)
                                733                 :            263 :                     nitem->done = true;
                                734                 :                :             }
                                735                 :                :         }
                                736                 :                :         else
                                737                 :                :         {
                                738                 :                :             /*
                                739                 :                :              * On an inner page, visit the referenced inner tuple and add all
                                740                 :                :              * its downlinks to the pending list.  We might have pending items
                                741                 :                :              * for more than one inner tuple on the same page (in fact this is
                                742                 :                :              * pretty likely given the way space allocation works), so get
                                743                 :                :              * them all while we are here.
                                744                 :                :              */
                                745         [ +  + ]:          35112 :             for (nitem = pitem; nitem != NULL; nitem = nitem->next)
                                746                 :                :             {
                                747         [ -  + ]:          34849 :                 if (nitem->done)
 4416 tgl@sss.pgh.pa.us         748                 :UBC           0 :                     continue;
 4416 tgl@sss.pgh.pa.us         749         [ +  + ]:CBC       34849 :                 if (ItemPointerGetBlockNumber(&nitem->tid) == blkno)
                                750                 :                :                 {
                                751                 :                :                     OffsetNumber offset;
                                752                 :                :                     SpGistInnerTuple innerTuple;
                                753                 :                : 
                                754                 :          17293 :                     offset = ItemPointerGetOffsetNumber(&nitem->tid);
                                755                 :          17293 :                     innerTuple = (SpGistInnerTuple) PageGetItem(page,
                                756                 :                :                                                                 PageGetItemId(page, offset));
                                757         [ +  - ]:          17293 :                     if (innerTuple->tupstate == SPGIST_LIVE)
                                758                 :                :                     {
                                759                 :                :                         SpGistNodeTuple node;
                                760                 :                :                         int         i;
                                761                 :                : 
                                762         [ +  + ]:          86465 :                         SGITITERATE(innerTuple, i, node)
                                763                 :                :                         {
                                764         [ +  + ]:          69172 :                             if (ItemPointerIsValid(&node->t_tid))
                                765                 :          34586 :                                 spgAddPendingTID(bds, &node->t_tid);
                                766                 :                :                         }
                                767                 :                :                     }
 4416 tgl@sss.pgh.pa.us         768         [ #  # ]:UBC           0 :                     else if (innerTuple->tupstate == SPGIST_REDIRECT)
                                769                 :                :                     {
                                770                 :                :                         /* transfer attention to redirect point */
                                771                 :              0 :                         spgAddPendingTID(bds,
                                772                 :                :                                          &((SpGistDeadTuple) innerTuple)->pointer);
                                773                 :                :                     }
                                774                 :                :                     else
                                775         [ #  # ]:              0 :                         elog(ERROR, "unexpected SPGiST tuple state: %d",
                                776                 :                :                              innerTuple->tupstate);
                                777                 :                : 
 4416 tgl@sss.pgh.pa.us         778                 :CBC       17293 :                     nitem->done = true;
                                779                 :                :                 }
                                780                 :                :             }
                                781                 :                :         }
                                782                 :                : 
                                783                 :          17556 :         UnlockReleaseBuffer(buffer);
                                784                 :                :     }
                                785                 :                : 
                                786                 :            263 :     spgClearPendingList(bds);
                                787                 :            263 : }
                                788                 :                : 
                                789                 :                : /*
                                790                 :                :  * Perform a bulkdelete scan
                                791                 :                :  */
                                792                 :                : static void
 4502                           793                 :             44 : spgvacuumscan(spgBulkDeleteState *bds)
                                794                 :                : {
                                795                 :             44 :     Relation    index = bds->info->index;
                                796                 :                :     bool        needLock;
                                797                 :                :     BlockNumber num_pages,
                                798                 :                :                 blkno;
                                799                 :                : 
                                800                 :                :     /* Finish setting up spgBulkDeleteState */
                                801                 :             44 :     initSpGistState(&bds->spgstate, index);
 4416                           802                 :             44 :     bds->pendingList = NULL;
                                803                 :             44 :     bds->myXmin = GetActiveSnapshot()->xmin;
 4417                           804                 :             44 :     bds->lastFilledBlock = SPGIST_LAST_FIXED_BLKNO;
                                805                 :                : 
                                806                 :                :     /*
                                807                 :                :      * Reset counts that will be incremented during the scan; needed in case
                                808                 :                :      * of multiple scans during a single VACUUM command
                                809                 :                :      */
 4502                           810                 :             44 :     bds->stats->estimated_count = false;
                                811                 :             44 :     bds->stats->num_index_tuples = 0;
                                812                 :             44 :     bds->stats->pages_deleted = 0;
                                813                 :                : 
                                814                 :                :     /* We can skip locking for new or temp relations */
                                815   [ +  -  +  - ]:             44 :     needLock = !RELATION_IS_LOCAL(index);
                                816                 :                : 
                                817                 :                :     /*
                                818                 :                :      * The outer loop iterates over all index pages except the metapage, in
                                819                 :                :      * physical order (we hope the kernel will cooperate in providing
                                820                 :                :      * read-ahead for speed).  It is critical that we visit all leaf pages,
                                821                 :                :      * including ones added after we start the scan, else we might fail to
                                822                 :                :      * delete some deletable tuples.  See more extensive comments about this
                                823                 :                :      * in btvacuumscan().
                                824                 :                :      */
 4417                           825                 :             44 :     blkno = SPGIST_METAPAGE_BLKNO + 1;
                                826                 :                :     for (;;)
                                827                 :                :     {
                                828                 :                :         /* Get the current relation length */
 4502                           829         [ +  - ]:             88 :         if (needLock)
                                830                 :             88 :             LockRelationForExtension(index, ExclusiveLock);
                                831                 :             88 :         num_pages = RelationGetNumberOfBlocks(index);
                                832         [ +  - ]:             88 :         if (needLock)
                                833                 :             88 :             UnlockRelationForExtension(index, ExclusiveLock);
                                834                 :                : 
                                835                 :                :         /* Quit if we've scanned the whole relation */
                                836         [ +  + ]:             88 :         if (blkno >= num_pages)
                                837                 :             44 :             break;
                                838                 :                :         /* Iterate over pages, then loop back to recheck length */
                                839         [ +  + ]:           2769 :         for (; blkno < num_pages; blkno++)
                                840                 :                :         {
                                841                 :           2725 :             spgvacuumpage(bds, blkno);
                                842                 :                :             /* empty the pending-list after each page */
 4416                           843         [ +  + ]:           2725 :             if (bds->pendingList != NULL)
                                844                 :            263 :                 spgprocesspending(bds);
                                845                 :                :         }
                                846                 :                :     }
                                847                 :                : 
                                848                 :                :     /* Propagate local lastUsedPages cache to metablock */
 4502                           849                 :             44 :     SpGistUpdateMetaPage(index);
                                850                 :                : 
                                851                 :                :     /*
                                852                 :                :      * If we found any empty pages (and recorded them in the FSM), then
                                853                 :                :      * forcibly update the upper-level FSM pages to ensure that searchers can
                                854                 :                :      * find them.  It's possible that the pages were also found during
                                855                 :                :      * previous scans and so this is a waste of time, but it's cheap enough
                                856                 :                :      * relative to scanning the index that it shouldn't matter much, and
                                857                 :                :      * making sure that free pages are available sooner not later seems
                                858                 :                :      * worthwhile.
                                859                 :                :      *
                                860                 :                :      * Note that if no empty pages exist, we don't bother vacuuming the FSM at
                                861                 :                :      * all.
                                862                 :                :      */
 2207                           863         [ +  + ]:             44 :     if (bds->stats->pages_deleted > 0)
                                864                 :             29 :         IndexFreeSpaceMapVacuum(index);
                                865                 :                : 
                                866                 :                :     /*
                                867                 :                :      * Truncate index if possible
                                868                 :                :      *
                                869                 :                :      * XXX disabled because it's unsafe due to possible concurrent inserts.
                                870                 :                :      * We'd have to rescan the pages to make sure they're still empty, and it
                                871                 :                :      * doesn't seem worth it.  Note that btree doesn't do this either.
                                872                 :                :      *
                                873                 :                :      * Another reason not to truncate is that it could invalidate the cached
                                874                 :                :      * pages-with-freespace pointers in the metapage and other backends'
                                875                 :                :      * relation caches, that is leave them pointing to nonexistent pages.
                                876                 :                :      * Adding RelationGetNumberOfBlocks calls to protect the places that use
                                877                 :                :      * those pointers would be unduly expensive.
                                878                 :                :      */
                                879                 :                : #ifdef NOT_USED
                                880                 :                :     if (num_pages > bds->lastFilledBlock + 1)
                                881                 :                :     {
                                882                 :                :         BlockNumber lastBlock = num_pages - 1;
                                883                 :                : 
                                884                 :                :         num_pages = bds->lastFilledBlock + 1;
                                885                 :                :         RelationTruncate(index, num_pages);
                                886                 :                :         bds->stats->pages_removed += lastBlock - bds->lastFilledBlock;
                                887                 :                :         bds->stats->pages_deleted -= lastBlock - bds->lastFilledBlock;
                                888                 :                :     }
                                889                 :                : #endif
                                890                 :                : 
                                891                 :                :     /* Report final stats */
 4502                           892                 :             44 :     bds->stats->num_pages = num_pages;
 1144 pg@bowt.ie                893                 :             44 :     bds->stats->pages_newly_deleted = bds->stats->pages_deleted;
 4502 tgl@sss.pgh.pa.us         894                 :             44 :     bds->stats->pages_free = bds->stats->pages_deleted;
                                895                 :             44 : }
                                896                 :                : 
                                897                 :                : /*
                                898                 :                :  * Bulk deletion of all index entries pointing to a set of heap tuples.
                                899                 :                :  * The set of target tuples is specified via a callback routine that tells
                                900                 :                :  * whether any given heap tuple (identified by ItemPointer) is being deleted.
                                901                 :                :  *
                                902                 :                :  * Result: a palloc'd struct containing statistical info for VACUUM displays.
                                903                 :                :  */
                                904                 :                : IndexBulkDeleteResult *
 3010                           905                 :              6 : spgbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
                                906                 :                :               IndexBulkDeleteCallback callback, void *callback_state)
                                907                 :                : {
                                908                 :                :     spgBulkDeleteState bds;
                                909                 :                : 
                                910                 :                :     /* allocate stats if first time through, else re-use existing struct */
 4502                           911         [ +  - ]:              6 :     if (stats == NULL)
                                912                 :              6 :         stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
                                913                 :              6 :     bds.info = info;
                                914                 :              6 :     bds.stats = stats;
                                915                 :              6 :     bds.callback = callback;
                                916                 :              6 :     bds.callback_state = callback_state;
                                917                 :                : 
                                918                 :              6 :     spgvacuumscan(&bds);
                                919                 :                : 
 3010                           920                 :              6 :     return stats;
                                921                 :                : }
                                922                 :                : 
                                923                 :                : /* Dummy callback to delete no tuples during spgvacuumcleanup */
                                924                 :                : static bool
 4502                           925                 :        2907980 : dummy_callback(ItemPointer itemptr, void *state)
                                926                 :                : {
                                927                 :        2907980 :     return false;
                                928                 :                : }
                                929                 :                : 
                                930                 :                : /*
                                931                 :                :  * Post-VACUUM cleanup.
                                932                 :                :  *
                                933                 :                :  * Result: a palloc'd struct containing statistical info for VACUUM displays.
                                934                 :                :  */
                                935                 :                : IndexBulkDeleteResult *
 3010                           936                 :             62 : spgvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
                                937                 :                : {
                                938                 :                :     spgBulkDeleteState bds;
                                939                 :                : 
                                940                 :                :     /* No-op in ANALYZE ONLY mode */
 4502                           941         [ +  + ]:             62 :     if (info->analyze_only)
 3010                           942                 :             18 :         return stats;
                                943                 :                : 
                                944                 :                :     /*
                                945                 :                :      * We don't need to scan the index if there was a preceding bulkdelete
                                946                 :                :      * pass.  Otherwise, make a pass that won't delete any live tuples, but
                                947                 :                :      * might still accomplish useful stuff with redirect/placeholder cleanup
                                948                 :                :      * and/or FSM housekeeping, and in any case will provide stats.
                                949                 :                :      */
 4502                           950         [ +  + ]:             44 :     if (stats == NULL)
                                951                 :                :     {
                                952                 :             38 :         stats = (IndexBulkDeleteResult *) palloc0(sizeof(IndexBulkDeleteResult));
                                953                 :             38 :         bds.info = info;
                                954                 :             38 :         bds.stats = stats;
                                955                 :             38 :         bds.callback = dummy_callback;
                                956                 :             38 :         bds.callback_state = NULL;
                                957                 :                : 
                                958                 :             38 :         spgvacuumscan(&bds);
                                959                 :                :     }
                                960                 :                : 
                                961                 :                :     /*
                                962                 :                :      * It's quite possible for us to be fooled by concurrent tuple moves into
                                963                 :                :      * double-counting some index tuples, so disbelieve any total that exceeds
                                964                 :                :      * the underlying heap's count ... if we know that accurately.  Otherwise
                                965                 :                :      * this might just make matters worse.
                                966                 :                :      */
                                967         [ +  - ]:             44 :     if (!info->estimated_count)
                                968                 :                :     {
                                969         [ -  + ]:             44 :         if (stats->num_index_tuples > info->num_heap_tuples)
 4502 tgl@sss.pgh.pa.us         970                 :UBC           0 :             stats->num_index_tuples = info->num_heap_tuples;
                                971                 :                :     }
                                972                 :                : 
 3010 tgl@sss.pgh.pa.us         973                 :CBC          44 :     return stats;
                                974                 :                : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622