LCOV - differential code coverage report
Current view: top level - src/backend/access/gin - ginvacuum.c (source / functions) Coverage Total Hit UBC CBC
Current: ba12a202ce1b5581dc0ed149cf3f637d7897ad5d vs 2866d8c7dbfc9d882a7d80fef93fbbe763709932 Lines: 93.3 % 312 291 21 291
Current Date: 2026-08-27 14:31:44 +0300 Functions: 100.0 % 10 10 10
Baseline: lcov-20260827-baseline Branches: 73.0 % 178 130 48 130
Baseline Date: 2026-08-27 14:31:58 +0300 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 100.0 % 4 4 4
(7,30] days: 60.0 % 5 3 2 3
(30,360] days: 100.0 % 50 50 50
(360..) days: 92.5 % 253 234 19 234
Function coverage date bins:
(30,360] days: 100.0 % 2 2 2
(360..) days: 100.0 % 8 8 8
Branch coverage date bins:
(1,7] days: 80.0 % 10 8 2 8
(7,30] days: 50.0 % 2 1 1 1
(30,360] days: 70.8 % 24 17 7 17
(360..) days: 73.2 % 142 104 38 104

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * ginvacuum.c
                                  4                 :                :  *    delete & vacuum routines for the postgres GIN
                                  5                 :                :  *
                                  6                 :                :  *
                                  7                 :                :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
                                  8                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                  9                 :                :  *
                                 10                 :                :  * IDENTIFICATION
                                 11                 :                :  *          src/backend/access/gin/ginvacuum.c
                                 12                 :                :  *-------------------------------------------------------------------------
                                 13                 :                :  */
                                 14                 :                : 
                                 15                 :                : #include "postgres.h"
                                 16                 :                : 
                                 17                 :                : #include "access/gin_private.h"
                                 18                 :                : #include "access/ginxlog.h"
                                 19                 :                : #include "access/xloginsert.h"
                                 20                 :                : #include "commands/vacuum.h"
                                 21                 :                : #include "miscadmin.h"
                                 22                 :                : #include "storage/indexfsm.h"
                                 23                 :                : #include "storage/lmgr.h"
                                 24                 :                : #include "storage/predicate.h"
                                 25                 :                : #include "storage/read_stream.h"
                                 26                 :                : #include "utils/memutils.h"
                                 27                 :                : 
                                 28                 :                : struct GinVacuumState
                                 29                 :                : {
                                 30                 :                :     Relation    index;
                                 31                 :                :     IndexBulkDeleteResult *result;
                                 32                 :                :     IndexBulkDeleteCallback callback;
                                 33                 :                :     void       *callback_state;
                                 34                 :                :     GinState    ginstate;
                                 35                 :                :     BufferAccessStrategy strategy;
                                 36                 :                :     MemoryContext tmpCxt;
                                 37                 :                : };
                                 38                 :                : 
                                 39                 :                : /*
                                 40                 :                :  * Vacuums an uncompressed posting list. The size of the must can be specified
                                 41                 :                :  * in number of items (nitems).
                                 42                 :                :  *
                                 43                 :                :  * If none of the items need to be removed, returns NULL. Otherwise returns
                                 44                 :                :  * a new palloc'd array with the remaining items. The number of remaining
                                 45                 :                :  * items is returned in *nremaining.
                                 46                 :                :  */
                                 47                 :                : ItemPointer
 4600 heikki.linnakangas@i       48                 :CBC      167896 : ginVacuumItemPointers(GinVacuumState *gvs, ItemPointerData *items,
                                 49                 :                :                       int nitem, int *nremaining)
                                 50                 :                : {
                                 51                 :                :     int         i,
                                 52                 :         167896 :                 remaining = 0;
 4496 bruce@momjian.us           53                 :         167896 :     ItemPointer tmpitems = NULL;
                                 54                 :                : 
                                 55                 :                :     /*
                                 56                 :                :      * Iterate over TIDs array
                                 57                 :                :      */
 7267                            58         [ +  + ]:         813374 :     for (i = 0; i < nitem; i++)
                                 59                 :                :     {
                                 60         [ +  + ]:         645478 :         if (gvs->callback(items + i, gvs->callback_state))
                                 61                 :                :         {
 7422 teodor@sigaev.ru           62                 :         519981 :             gvs->result->tuples_removed += 1;
 4600 heikki.linnakangas@i       63         [ +  + ]:         519981 :             if (!tmpitems)
                                 64                 :                :             {
                                 65                 :                :                 /*
                                 66                 :                :                  * First TID to be deleted: allocate memory to hold the
                                 67                 :                :                  * remaining items.
                                 68                 :                :                  */
  260 michael@paquier.xyz        69                 :         160707 :                 tmpitems = palloc_array(ItemPointerData, nitem);
 4600 heikki.linnakangas@i       70                 :         160707 :                 memcpy(tmpitems, items, sizeof(ItemPointerData) * i);
                                 71                 :                :             }
                                 72                 :                :         }
                                 73                 :                :         else
                                 74                 :                :         {
 7422 teodor@sigaev.ru           75                 :         125497 :             gvs->result->num_index_tuples += 1;
 4600 heikki.linnakangas@i       76         [ +  + ]:         125497 :             if (tmpitems)
                                 77                 :           4292 :                 tmpitems[remaining] = items[i];
                                 78                 :         125497 :             remaining++;
                                 79                 :                :         }
                                 80                 :                :     }
                                 81                 :                : 
                                 82                 :         167896 :     *nremaining = remaining;
                                 83                 :         167896 :     return tmpitems;
                                 84                 :                : }
                                 85                 :                : 
                                 86                 :                : /*
                                 87                 :                :  * Create a WAL record for vacuuming entry tree leaf page.
                                 88                 :                :  */
                                 89                 :                : static void
 7267 bruce@momjian.us           90                 :           1149 : xlogVacuumPage(Relation index, Buffer buffer)
                                 91                 :                : {
 3781 kgrittn@postgresql.o       92                 :           1149 :     Page        page = BufferGetPage(buffer);
                                 93                 :                :     XLogRecPtr  recptr;
                                 94                 :                : 
                                 95                 :                :     /* This is only used for entry tree leaf pages. */
 4600 heikki.linnakangas@i       96         [ -  + ]:           1149 :     Assert(!GinPageIsData(page));
 7267 bruce@momjian.us           97         [ -  + ]:           1149 :     Assert(GinPageIsLeaf(page));
                                 98                 :                : 
 5736 rhaas@postgresql.org       99   [ +  +  -  +  :           1149 :     if (!RelationNeedsWAL(index))
                                        -  -  -  - ]
 7267 bruce@momjian.us          100                 :           1148 :         return;
                                101                 :                : 
                                102                 :                :     /*
                                103                 :                :      * Always create a full image, we don't track the changes on the page at
                                104                 :                :      * any more fine-grained level. This could obviously be improved...
                                105                 :                :      */
 4298 heikki.linnakangas@i      106                 :              1 :     XLogBeginInsert();
                                107                 :              1 :     XLogRegisterBuffer(0, buffer, REGBUF_FORCE_IMAGE | REGBUF_STANDARD);
                                108                 :                : 
                                109                 :              1 :     recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_VACUUM_PAGE);
 7422 teodor@sigaev.ru          110                 :              1 :     PageSetLSN(page, recptr);
                                111                 :                : }
                                112                 :                : 
                                113                 :                : 
                                114                 :                : /*
                                115                 :                :  * Stack entry used during posting tree empty-page deletion scan.
                                116                 :                :  *
                                117                 :                :  * One DataPageDeleteStack entry is allocated per tree level.  As
                                118                 :                :  * ginScanPostingTreeToDelete() recurses down the tree, each entry tracks
                                119                 :                :  * the buffer of the page currently being visited at that level ('buffer'),
                                120                 :                :  * and the buffer of its left sibling ('leftBuffer').  The left page is kept
                                121                 :                :  * pinned and exclusively locked because ginDeletePostingPage() needs it to
                                122                 :                :  * update the sibling chain; acquiring it later could deadlock with
                                123                 :                :  * ginStepRight(), which locks pages left-to-right.
                                124                 :                :  */
                                125                 :                : typedef struct DataPageDeleteStack
                                126                 :                : {
                                127                 :                :     struct DataPageDeleteStack *child;
                                128                 :                :     struct DataPageDeleteStack *parent;
                                129                 :                : 
                                130                 :                :     Buffer      buffer;         /* buffer for the page being visited at this
                                131                 :                :                                  * tree level */
                                132                 :                :     Buffer      leftBuffer;     /* pinned and locked rightmost non-deleted
                                133                 :                :                                  * sibling to the left of the current page */
                                134                 :                :     OffsetNumber myoff;         /* offset of this page's downlink in the
                                135                 :                :                                  * parent */
                                136                 :                :     bool        isRoot;
                                137                 :                : } DataPageDeleteStack;
                                138                 :                : 
                                139                 :                : 
                                140                 :                : /*
                                141                 :                :  * Delete a posting tree page.
                                142                 :                :  *
                                143                 :                :  * Removes the page identified by dBuffer from the posting tree by updating
                                144                 :                :  * the left sibling's rightlink (in lBuffer) to skip over the deleted page,
                                145                 :                :  * and removing the downlink from the parent page (in pBuffer).  All three
                                146                 :                :  * buffers must already have been pinned and exclusively locked by the caller.
                                147                 :                :  *
                                148                 :                :  * The buffers are NOT released nor unlocked here; the caller is responsible
                                149                 :                :  * for this.
                                150                 :                :  */
                                151                 :                : static void
  187 akorotkov@postgresql      152                 :              9 : ginDeletePostingPage(GinVacuumState *gvs, Buffer dBuffer, Buffer lBuffer,
                                153                 :                :                      Buffer pBuffer, OffsetNumber myoff, bool isParentRoot)
                                154                 :                : {
                                155                 :                :     Page        page,
                                156                 :                :                 parentPage;
                                157                 :                :     BlockNumber rightlink;
                                158                 :              9 :     BlockNumber deleteBlkno = BufferGetBlockNumber(dBuffer);
                                159                 :                : 
                                160                 :                :     /*
                                161                 :                :      * This function MUST be called only if someone of parent pages hold
                                162                 :                :      * exclusive cleanup lock. This guarantees that no insertions currently
                                163                 :                :      * happen in this subtree. Caller also acquires Exclusive locks on
                                164                 :                :      * deletable, parent and left pages.
                                165                 :                :      */
                                166                 :                : 
 3072 teodor@sigaev.ru          167                 :              9 :     page = BufferGetPage(dBuffer);
                                168                 :              9 :     rightlink = GinPageGetOpaque(page)->rightlink;
                                169                 :                : 
    5 pg@bowt.ie                170         [ -  + ]:              9 :     Assert(GinPageGetOpaque(BufferGetPage(lBuffer))->rightlink == deleteBlkno);
                                171                 :                : 
                                172                 :                :     /*
                                173                 :                :      * Any insert which would have gone on the leaf block will now go to its
                                174                 :                :      * right sibling.
                                175                 :                :      */
 3072 teodor@sigaev.ru          176                 :              9 :     PredicateLockPageCombine(gvs->index, deleteBlkno, rightlink);
                                177                 :                : 
 7422                           178                 :              9 :     START_CRIT_SECTION();
                                179                 :                : 
                                180                 :                :     /* Unlink the page by changing left sibling's rightlink */
 3781 kgrittn@postgresql.o      181                 :              9 :     page = BufferGetPage(lBuffer);
 4675 heikki.linnakangas@i      182                 :              9 :     GinPageGetOpaque(page)->rightlink = rightlink;
                                183                 :                : 
                                184                 :                :     /* Delete downlink from parent */
 3781 kgrittn@postgresql.o      185                 :              9 :     parentPage = BufferGetPage(pBuffer);
                                186                 :                : #ifdef USE_ASSERT_CHECKING
                                187                 :                :     do
                                188                 :                :     {
 4711 heikki.linnakangas@i      189                 :              9 :         PostingItem *tod = GinDataPageGetPostingItem(parentPage, myoff);
                                190                 :                : 
 6860 bruce@momjian.us          191         [ -  + ]:              9 :         Assert(PostingItemGetBlockNumber(tod) == deleteBlkno);
                                192                 :                :     } while (0);
                                193                 :                : #endif
 5793 tgl@sss.pgh.pa.us         194                 :              9 :     GinPageDeletePostingItem(parentPage, myoff);
                                195                 :                : 
 3781 kgrittn@postgresql.o      196                 :              9 :     page = BufferGetPage(dBuffer);
                                197                 :                : 
                                198                 :                :     /*
                                199                 :                :      * we shouldn't change rightlink field to save workability of running
                                200                 :                :      * search scan
                                201                 :                :      */
                                202                 :                : 
                                203                 :                :     /*
                                204                 :                :      * Mark page as deleted, and remember last xid which could know its
                                205                 :                :      * address.
                                206                 :                :      */
 2473 akorotkov@postgresql      207                 :              9 :     GinPageSetDeleted(page);
 2019 tmunro@postgresql.or      208                 :              9 :     GinPageSetDeleteXid(page, ReadNextTransactionId());
                                209                 :                : 
 7023 teodor@sigaev.ru          210                 :              9 :     MarkBufferDirty(pBuffer);
 4518 heikki.linnakangas@i      211                 :              9 :     MarkBufferDirty(lBuffer);
 7023 teodor@sigaev.ru          212                 :              9 :     MarkBufferDirty(dBuffer);
                                213                 :                : 
 5736 rhaas@postgresql.org      214   [ -  +  -  -  :              9 :     if (RelationNeedsWAL(gvs->index))
                                        -  -  -  - ]
                                215                 :                :     {
                                216                 :                :         XLogRecPtr  recptr;
                                217                 :                :         ginxlogDeletePage data;
                                218                 :                : 
                                219                 :                :         /*
                                220                 :                :          * We can't pass REGBUF_STANDARD for the deleted page, because we
                                221                 :                :          * didn't set pd_lower on pre-9.4 versions. The page might've been
                                222                 :                :          * binary-upgraded from an older version, and hence not have pd_lower
                                223                 :                :          * set correctly. Ditto for the left page, but removing the item from
                                224                 :                :          * the parent updated its pd_lower, so we know that's OK at this
                                225                 :                :          * point.
                                226                 :                :          */
 4298 heikki.linnakangas@i      227                 :UBC           0 :         XLogBeginInsert();
                                228                 :              0 :         XLogRegisterBuffer(0, dBuffer, 0);
                                229                 :              0 :         XLogRegisterBuffer(1, pBuffer, REGBUF_STANDARD);
                                230                 :              0 :         XLogRegisterBuffer(2, lBuffer, 0);
                                231                 :                : 
 7422 teodor@sigaev.ru          232                 :              0 :         data.parentOffset = myoff;
 7267 bruce@momjian.us          233                 :              0 :         data.rightLink = GinPageGetOpaque(page)->rightlink;
 2814 akorotkov@postgresql      234                 :              0 :         data.deleteXid = GinPageGetDeleteXid(page);
                                235                 :                : 
  562 peter@eisentraut.org      236                 :              0 :         XLogRegisterData(&data, sizeof(ginxlogDeletePage));
                                237                 :                : 
 4298 heikki.linnakangas@i      238                 :              0 :         recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_PAGE);
 7422 teodor@sigaev.ru          239                 :              0 :         PageSetLSN(page, recptr);
                                240                 :              0 :         PageSetLSN(parentPage, recptr);
 3781 kgrittn@postgresql.o      241                 :              0 :         PageSetLSN(BufferGetPage(lBuffer), recptr);
                                242                 :                :     }
                                243                 :                : 
  189 michael@paquier.xyz       244         [ -  + ]:CBC           9 :     END_CRIT_SECTION();
                                245                 :                : 
 2009 pg@bowt.ie                246                 :              9 :     gvs->result->pages_newly_deleted++;
 7422 teodor@sigaev.ru          247                 :              9 :     gvs->result->pages_deleted++;
                                248                 :              9 : }
                                249                 :                : 
                                250                 :                : 
                                251                 :                : /*
                                252                 :                :  * Scans a posting tree and deletes empty pages.
                                253                 :                :  *
                                254                 :                :  * The caller must hold a cleanup lock on the root page to prevent concurrent
                                255                 :                :  * inserts.  The entire path from the root down to the current page is kept
                                256                 :                :  * exclusively locked throughout the scan.  The left sibling at each level is
                                257                 :                :  * also kept locked, because ginDeletePostingPage() needs it to update the
                                258                 :                :  * rightlink of the left sibling; re-acquiring the left sibling lock later
                                259                 :                :  * could deadlock with ginStepRight(), which acquires page locks
                                260                 :                :  * left-to-right.
                                261                 :                :  *
                                262                 :                :  * All per-level state is carried in 'myStackItem': the buffer to process
                                263                 :                :  * (must already be pinned and exclusively locked), the left sibling buffer,
                                264                 :                :  * and this page's offset in the parent's downlink array.  The root entry is
                                265                 :                :  * set up by ginVacuumPostingTree(); child entries are populated here before
                                266                 :                :  * recursing.
                                267                 :                :  *
                                268                 :                :  * Returns true if the page was deleted, false otherwise.
                                269                 :                :  */
                                270                 :                : static bool
  187 akorotkov@postgresql      271                 :             49 : ginScanPostingTreeToDelete(GinVacuumState *gvs, DataPageDeleteStack *myStackItem)
                                272                 :                : {
                                273                 :             49 :     Buffer      buffer = myStackItem->buffer;
                                274                 :                :     Page        page;
                                275                 :             49 :     bool        pageWasDeleted = false;
                                276                 :                :     bool        isempty;
                                277                 :                : 
 3781 kgrittn@postgresql.o      278                 :             49 :     page = BufferGetPage(buffer);
                                279                 :                : 
 7267 bruce@momjian.us          280         [ -  + ]:             49 :     Assert(GinPageIsData(page));
                                281                 :                : 
                                282         [ +  + ]:             49 :     if (!GinPageIsLeaf(page))
                                283                 :                :     {
                                284                 :                :         OffsetNumber i;
                                285                 :                : 
  187 akorotkov@postgresql      286         [ +  + ]:             49 :         for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff;)
                                287                 :                :         {
 4711 heikki.linnakangas@i      288                 :             39 :             PostingItem *pitem = GinDataPageGetPostingItem(page, i);
                                289                 :                :             Buffer      childBuffer;
                                290                 :                : 
  187 akorotkov@postgresql      291                 :             39 :             childBuffer = ReadBufferExtended(gvs->index,
                                292                 :                :                                              MAIN_FORKNUM,
                                293                 :             39 :                                              PostingItemGetBlockNumber(pitem),
                                294                 :                :                                              RBM_NORMAL, gvs->strategy);
                                295                 :             39 :             LockBuffer(childBuffer, GIN_EXCLUSIVE);
                                296                 :                : 
                                297                 :                :             /* Allocate a child stack entry on first use; reuse thereafter */
                                298         [ +  + ]:             39 :             if (!myStackItem->child)
                                299                 :                :             {
                                300                 :             10 :                 myStackItem->child = palloc0_object(DataPageDeleteStack);
                                301                 :             10 :                 myStackItem->child->parent = myStackItem;
                                302                 :             10 :                 myStackItem->child->leftBuffer = InvalidBuffer;
                                303                 :                :             }
                                304                 :                : 
                                305                 :             39 :             myStackItem->child->buffer = childBuffer;
                                306                 :             39 :             myStackItem->child->isRoot = false;
                                307                 :             39 :             myStackItem->child->myoff = i;
                                308                 :                : 
                                309                 :                :             /*
                                310                 :                :              * Recurse into child.  If the child page was deleted, its
                                311                 :                :              * downlink was removed from our page, so re-examine the same
                                312                 :                :              * offset; otherwise advance to the next downlink.
                                313                 :                :              */
                                314         [ +  + ]:             39 :             if (!ginScanPostingTreeToDelete(gvs, myStackItem->child))
                                315                 :             30 :                 i++;
                                316                 :                :         }
                                317                 :             10 :         myStackItem->buffer = InvalidBuffer;
                                318                 :                : 
                                319                 :                :         /*
                                320                 :                :          * After processing all children at this level, release the child
                                321                 :                :          * level's leftBuffer if we're at the rightmost page.  There is no
                                322                 :                :          * right sibling that could need it for deletion.
                                323                 :                :          */
                                324   [ +  -  +  - ]:             10 :         if (GinPageRightMost(page) && BufferIsValid(myStackItem->child->leftBuffer))
                                325                 :                :         {
                                326                 :             10 :             UnlockReleaseBuffer(myStackItem->child->leftBuffer);
                                327                 :             10 :             myStackItem->child->leftBuffer = InvalidBuffer;
                                328                 :                :         }
                                329                 :                :     }
                                330                 :                : 
 4600 heikki.linnakangas@i      331         [ +  + ]:             49 :     if (GinPageIsLeaf(page))
                                332         [ +  - ]:             39 :         isempty = GinDataLeafPageIsEmpty(page);
                                333                 :                :     else
                                334                 :             10 :         isempty = GinPageGetOpaque(page)->maxoff < FirstOffsetNumber;
                                335                 :                : 
                                336         [ +  + ]:             49 :     if (isempty)
                                337                 :                :     {
                                338                 :                :         /*
                                339                 :                :          * Proceed to the ginDeletePostingPage() if target page is not the
                                340                 :                :          * leftmost or the rightmost page.
                                341                 :                :          *
                                342                 :                :          * leftBuffer is the target's left sibling according to the parent
                                343                 :                :          * level, which is not necessarily its left sibling in the sibling
                                344                 :                :          * link chain (the rightlinks stored on pages): the new right half of
                                345                 :                :          * an incompletely split page is in the sibling chain, but has no
                                346                 :                :          * downlink yet.  ginDeletePostingPage isn't prepared to deal with
                                347                 :                :          * that, so we must refuse to delete when either the target or its
                                348                 :                :          * left sibling page is marked incompletely split.
                                349                 :                :          */
    5 pg@bowt.ie                350   [ +  +  +  + ]:             22 :         if (BufferIsValid(myStackItem->leftBuffer) && !GinPageRightMost(page) &&
                                351         [ +  - ]:             10 :             !GinPageIsIncompleteSplit(page) &&
                                352         [ +  + ]:             10 :             !GinPageIsIncompleteSplit(BufferGetPage(myStackItem->leftBuffer)))
                                353                 :                :         {
  187 akorotkov@postgresql      354         [ -  + ]:              9 :             Assert(!myStackItem->isRoot);
                                355                 :              9 :             ginDeletePostingPage(gvs, buffer, myStackItem->leftBuffer,
                                356                 :              9 :                                  myStackItem->parent->buffer,
                                357                 :              9 :                                  myStackItem->myoff,
                                358                 :              9 :                                  myStackItem->parent->isRoot);
                                359                 :              9 :             pageWasDeleted = true;
                                360                 :                :         }
                                361                 :                :     }
                                362                 :                : 
                                363         [ +  + ]:             49 :     if (!pageWasDeleted)
                                364                 :                :     {
                                365                 :                :         /*
                                366                 :                :          * Keep this page as the new leftBuffer for this level: the next
                                367                 :                :          * sibling to the right might need it for deletion.  Release any
                                368                 :                :          * previously held left page first.
                                369                 :                :          */
                                370         [ +  + ]:             40 :         if (BufferIsValid(myStackItem->leftBuffer))
                                371                 :             20 :             UnlockReleaseBuffer(myStackItem->leftBuffer);
                                372                 :             40 :         myStackItem->leftBuffer = buffer;
                                373                 :                :     }
                                374                 :                :     else
                                375                 :                :     {
                                376                 :                :         /*
                                377                 :                :          * Page was deleted; release the buffer.  leftBuffer remains the same.
                                378                 :                :          */
                                379                 :              9 :         UnlockReleaseBuffer(buffer);
                                380                 :                :     }
                                381                 :                : 
                                382                 :             49 :     return pageWasDeleted;
                                383                 :                : }
                                384                 :                : 
                                385                 :                : 
                                386                 :                : /*
                                387                 :                :  * Scan through posting tree leafs, delete empty tuples.  Returns true if there
                                388                 :                :  * is at least one empty page.
                                389                 :                :  */
                                390                 :                : static bool
 2814                           391                 :             19 : ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno)
                                392                 :                : {
                                393                 :                :     Buffer      buffer;
                                394                 :                :     Page        page;
 3298 peter_e@gmx.net           395                 :             19 :     bool        hasVoidPage = false;
                                396                 :                :     MemoryContext oldCxt;
                                397                 :                : 
                                398                 :                :     /* Find leftmost leaf page of posting tree and lock it in exclusive mode */
                                399                 :                :     while (true)
 2814 akorotkov@postgresql      400                 :             10 :     {
                                401                 :                :         PostingItem *pitem;
                                402                 :                : 
                                403                 :             29 :         buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
                                404                 :                :                                     RBM_NORMAL, gvs->strategy);
                                405                 :             29 :         LockBuffer(buffer, GIN_SHARE);
                                406                 :             29 :         page = BufferGetPage(buffer);
                                407                 :                : 
                                408         [ -  + ]:             29 :         Assert(GinPageIsData(page));
                                409                 :                : 
                                410         [ +  + ]:             29 :         if (GinPageIsLeaf(page))
                                411                 :                :         {
                                412                 :             19 :             LockBuffer(buffer, GIN_UNLOCK);
                                413                 :             19 :             LockBuffer(buffer, GIN_EXCLUSIVE);
                                414                 :                : 
    8 pg@bowt.ie                415         [ -  + ]:             19 :             if (!GinPageIsLeaf(page))
                                416                 :                :             {
                                417                 :                :                 /*
                                418                 :                :                  * The root page was a leaf page, but became an internal page
                                419                 :                :                  * while no lock was held.  Unlock and reacquire a share lock.
                                420                 :                :                  */
    8 pg@bowt.ie                421                 :UBC           0 :                 UnlockReleaseBuffer(buffer);
                                422                 :              0 :                 continue;
                                423                 :                :             }
 2814 akorotkov@postgresql      424                 :CBC          19 :             break;
                                425                 :                :         }
                                426                 :                : 
                                427         [ -  + ]:             10 :         Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
                                428                 :                : 
                                429                 :             10 :         pitem = GinDataPageGetPostingItem(page, FirstOffsetNumber);
                                430                 :             10 :         blkno = PostingItemGetBlockNumber(pitem);
                                431         [ -  + ]:             10 :         Assert(blkno != InvalidBlockNumber);
                                432                 :                : 
                                433                 :             10 :         UnlockReleaseBuffer(buffer);
                                434                 :                :     }
                                435                 :                : 
                                436                 :                :     /* Iterate all posting tree leaves using rightlinks and vacuum them */
                                437                 :                :     while (true)
                                438                 :                :     {
 3444 teodor@sigaev.ru          439                 :             49 :         oldCxt = MemoryContextSwitchTo(gvs->tmpCxt);
                                440                 :             49 :         ginVacuumPostingTreeLeaf(gvs->index, buffer, gvs);
                                441                 :             49 :         MemoryContextSwitchTo(oldCxt);
                                442                 :             49 :         MemoryContextReset(gvs->tmpCxt);
                                443                 :                : 
                                444   [ +  -  +  + ]:             49 :         if (GinDataLeafPageIsEmpty(page))
 3298 peter_e@gmx.net           445                 :             22 :             hasVoidPage = true;
                                446                 :                : 
 2814 akorotkov@postgresql      447                 :             49 :         blkno = GinPageGetOpaque(page)->rightlink;
                                448                 :                : 
 3444 teodor@sigaev.ru          449                 :             49 :         UnlockReleaseBuffer(buffer);
                                450                 :                : 
 2814 akorotkov@postgresql      451         [ +  + ]:             49 :         if (blkno == InvalidBlockNumber)
                                452                 :             19 :             break;
                                453                 :                : 
                                454                 :                :         /*
                                455                 :                :          * A safe point to delay/accept interrupts: the previous page has been
                                456                 :                :          * unlocked and released, so we hold no buffer content lock (nor any
                                457                 :                :          * other LWLock) here and CHECK_FOR_INTERRUPTS() can do its job.
                                458                 :                :          */
   30                           459                 :             30 :         vacuum_delay_point(false);
                                460                 :                : 
 2814                           461                 :             30 :         buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
                                462                 :                :                                     RBM_NORMAL, gvs->strategy);
                                463                 :             30 :         LockBuffer(buffer, GIN_EXCLUSIVE);
                                464                 :             30 :         page = BufferGetPage(buffer);
                                465                 :                :     }
                                466                 :                : 
                                467                 :             19 :     return hasVoidPage;
                                468                 :                : }
                                469                 :                : 
                                470                 :                : static void
                                471                 :             19 : ginVacuumPostingTree(GinVacuumState *gvs, BlockNumber rootBlkno)
                                472                 :                : {
                                473         [ +  + ]:             19 :     if (ginVacuumPostingTreeLeaves(gvs, rootBlkno))
                                474                 :                :     {
                                475                 :                :         /*
                                476                 :                :          * There is at least one empty page.  So we have to rescan the tree
                                477                 :                :          * deleting empty pages.
                                478                 :                :          */
                                479                 :                :         Buffer      buffer;
                                480                 :                :         DataPageDeleteStack root,
                                481                 :                :                    *ptr,
                                482                 :                :                    *tmp;
                                483                 :                :         bool        deleted PG_USED_FOR_ASSERTS_ONLY;
                                484                 :                : 
                                485                 :             10 :         buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, rootBlkno,
                                486                 :                :                                     RBM_NORMAL, gvs->strategy);
                                487                 :                : 
                                488                 :                :         /*
                                489                 :                :          * Lock posting tree root for cleanup to ensure there are no
                                490                 :                :          * concurrent inserts.
                                491                 :                :          */
                                492                 :             10 :         LockBufferForCleanup(buffer);
                                493                 :                : 
                                494                 :             10 :         memset(&root, 0, sizeof(DataPageDeleteStack));
  187                           495                 :             10 :         root.buffer = buffer;
 2473                           496                 :             10 :         root.leftBuffer = InvalidBuffer;
  187                           497                 :             10 :         root.myoff = InvalidOffsetNumber;
 2814                           498                 :             10 :         root.isRoot = true;
                                499                 :                : 
  187                           500                 :             10 :         deleted = ginScanPostingTreeToDelete(gvs, &root);
                                501         [ -  + ]:             10 :         Assert(!deleted);
                                502                 :                : 
 2814                           503                 :             10 :         ptr = root.child;
                                504                 :                : 
                                505         [ +  + ]:             20 :         while (ptr)
                                506                 :                :         {
                                507                 :             10 :             tmp = ptr->child;
                                508                 :             10 :             pfree(ptr);
                                509                 :             10 :             ptr = tmp;
                                510                 :                :         }
                                511                 :                : 
                                512                 :             10 :         UnlockReleaseBuffer(buffer);
                                513                 :                :     }
 3444 teodor@sigaev.ru          514                 :             19 : }
                                515                 :                : 
                                516                 :                : /*
                                517                 :                :  * returns modified page or NULL if page isn't modified.
                                518                 :                :  * Function works with original page until first change is occurred,
                                519                 :                :  * then page is copied into temporary one.
                                520                 :                :  */
                                521                 :                : static Page
 7267 bruce@momjian.us          522                 :           1222 : ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint32 *nroot)
                                523                 :                : {
 3781 kgrittn@postgresql.o      524                 :           1222 :     Page        origpage = BufferGetPage(buffer),
                                525                 :                :                 tmppage;
                                526                 :                :     OffsetNumber i,
 7267 bruce@momjian.us          527                 :           1222 :                 maxoff = PageGetMaxOffsetNumber(origpage);
                                528                 :                : 
 7422 teodor@sigaev.ru          529                 :           1222 :     tmppage = origpage;
                                530                 :                : 
 7267 bruce@momjian.us          531                 :           1222 :     *nroot = 0;
                                532                 :                : 
                                533         [ +  + ]:         168097 :     for (i = FirstOffsetNumber; i <= maxoff; i++)
                                534                 :                :     {
                                535                 :         166875 :         IndexTuple  itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
                                536                 :                : 
                                537         [ +  + ]:         166875 :         if (GinIsPostingTree(itup))
                                538                 :                :         {
                                539                 :                :             /*
                                540                 :                :              * store posting tree's roots for further processing, we can't
                                541                 :                :              * vacuum it just now due to risk of deadlocks with scans/inserts
                                542                 :                :              */
 5711 tgl@sss.pgh.pa.us         543                 :             19 :             roots[*nroot] = GinGetDownlink(itup);
 7422 teodor@sigaev.ru          544                 :             19 :             (*nroot)++;
                                545                 :                :         }
 7267 bruce@momjian.us          546         [ +  - ]:         166856 :         else if (GinGetNPosting(itup) > 0)
                                547                 :                :         {
                                548                 :                :             int         nitems;
                                549                 :                :             ItemPointer items_orig;
                                550                 :                :             bool        free_items_orig;
                                551                 :                :             ItemPointer items;
                                552                 :                : 
                                553                 :                :             /* Get list of item pointers from the tuple. */
 4600 heikki.linnakangas@i      554         [ +  - ]:         166856 :             if (GinItupIsCompressed(itup))
                                555                 :                :             {
 4186                           556                 :         166856 :                 items_orig = ginPostingListDecode((GinPostingList *) GinGetPosting(itup), &nitems);
                                557                 :         166856 :                 free_items_orig = true;
                                558                 :                :             }
                                559                 :                :             else
                                560                 :                :             {
 4186 heikki.linnakangas@i      561                 :UBC           0 :                 items_orig = (ItemPointer) GinGetPosting(itup);
 4600                           562                 :              0 :                 nitems = GinGetNPosting(itup);
 4186                           563                 :              0 :                 free_items_orig = false;
                                564                 :                :             }
                                565                 :                : 
                                566                 :                :             /* Remove any items from the list that need to be vacuumed. */
 4186 heikki.linnakangas@i      567                 :CBC      166856 :             items = ginVacuumItemPointers(gvs, items_orig, nitems, &nitems);
                                568                 :                : 
                                569         [ +  - ]:         166856 :             if (free_items_orig)
                                570                 :         166856 :                 pfree(items_orig);
                                571                 :                : 
                                572                 :                :             /* If any item pointers were removed, recreate the tuple. */
                                573         [ +  + ]:         166856 :             if (items)
                                574                 :                :             {
                                575                 :                :                 OffsetNumber attnum;
                                576                 :                :                 Datum       key;
                                577                 :                :                 GinNullCategory category;
                                578                 :                :                 GinPostingList *plist;
                                579                 :                :                 int         plistsize;
                                580                 :                : 
 4600                           581         [ +  + ]:         160003 :                 if (nitems > 0)
                                582                 :                :                 {
 4186                           583                 :             15 :                     plist = ginCompressPostingList(items, nitems, GinMaxItemSize, NULL);
 4600                           584                 :             15 :                     plistsize = SizeOfGinPostingList(plist);
                                585                 :                :                 }
                                586                 :                :                 else
                                587                 :                :                 {
                                588                 :         159988 :                     plist = NULL;
                                589                 :         159988 :                     plistsize = 0;
                                590                 :                :                 }
                                591                 :                : 
                                592                 :                :                 /*
                                593                 :                :                  * if we already created a temporary page, make changes in
                                594                 :                :                  * place
                                595                 :                :                  */
 7267 bruce@momjian.us          596         [ +  + ]:         160003 :                 if (tmppage == origpage)
                                597                 :                :                 {
                                598                 :                :                     /*
                                599                 :                :                      * On first difference, create a temporary copy of the
                                600                 :                :                      * page and copy the tuple's posting list to it.
                                601                 :                :                      */
 6506 tgl@sss.pgh.pa.us         602                 :           1149 :                     tmppage = PageGetTempPageCopy(origpage);
                                603                 :                : 
                                604                 :                :                     /* set itup pointer to new page */
 7422 teodor@sigaev.ru          605                 :           1149 :                     itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
                                606                 :                :                 }
                                607                 :                : 
 6621 tgl@sss.pgh.pa.us         608                 :         160003 :                 attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
 5711                           609                 :         160003 :                 key = gintuple_get_key(&gvs->ginstate, itup, &category);
                                610                 :         160003 :                 itup = GinFormTuple(&gvs->ginstate, attnum, key, category,
                                611                 :                :                                     (char *) plist, plistsize,
                                612                 :                :                                     nitems, true);
 4600 heikki.linnakangas@i      613         [ +  + ]:         160003 :                 if (plist)
                                614                 :             15 :                     pfree(plist);
 7422 teodor@sigaev.ru          615                 :         160003 :                 PageIndexTupleDelete(tmppage, i);
                                616                 :                : 
  304 peter@eisentraut.org      617         [ -  + ]:         160003 :                 if (PageAddItem(tmppage, itup, IndexTupleSize(itup), i, false, false) != i)
 7267 bruce@momjian.us          618         [ #  # ]:UBC           0 :                     elog(ERROR, "failed to add item to index page in \"%s\"",
                                619                 :                :                          RelationGetRelationName(gvs->index));
                                620                 :                : 
 7267 bruce@momjian.us          621                 :CBC      160003 :                 pfree(itup);
 4186 heikki.linnakangas@i      622                 :         160003 :                 pfree(items);
                                623                 :                :             }
                                624                 :                :         }
                                625                 :                :     }
                                626                 :                : 
 7267 bruce@momjian.us          627         [ +  + ]:           1222 :     return (tmppage == origpage) ? NULL : tmppage;
                                628                 :                : }
                                629                 :                : 
                                630                 :                : IndexBulkDeleteResult *
 3875 tgl@sss.pgh.pa.us         631                 :             30 : ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
                                632                 :                :               IndexBulkDeleteCallback callback, void *callback_state)
                                633                 :                : {
 7422                           634                 :             30 :     Relation    index = info->index;
 7267 bruce@momjian.us          635                 :             30 :     BlockNumber blkno = GIN_ROOT_BLKNO;
                                636                 :                :     GinVacuumState gvs;
                                637                 :                :     Buffer      buffer;
                                638                 :                :     BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
                                639                 :                :     uint32      nRoot;
                                640                 :                : 
 4600 heikki.linnakangas@i      641                 :             30 :     gvs.tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
                                642                 :                :                                        "Gin vacuum temporary context",
                                643                 :                :                                        ALLOCSET_DEFAULT_SIZES);
 6365 tgl@sss.pgh.pa.us         644                 :             30 :     gvs.index = index;
                                645                 :             30 :     gvs.callback = callback;
                                646                 :             30 :     gvs.callback_state = callback_state;
                                647                 :             30 :     gvs.strategy = info->strategy;
                                648                 :             30 :     initGinState(&gvs.ginstate, index);
                                649                 :                : 
                                650                 :                :     /* first time through? */
 7422                           651         [ +  - ]:             30 :     if (stats == NULL)
                                652                 :                :     {
                                653                 :                :         /* Yes, so initialize stats to zeroes */
  260 michael@paquier.xyz       654                 :             30 :         stats = palloc0_object(IndexBulkDeleteResult);
                                655                 :                :     }
                                656                 :                : 
                                657                 :                :     /*
                                658                 :                :      * The pending list might have already-dead TIDs that VACUUM now requires
                                659                 :                :      * us to remove from the index.  We must force cleanup of the pending list
                                660                 :                :      * now, before vacuuming proper begins, to make sure nothing is missed.
                                661                 :                :      *
                                662                 :                :      * When running in an autovacuum worker, we won't necessarily _fully_
                                663                 :                :      * empty the pending list.  This is still safe; concurrent inserters
                                664                 :                :      * cannot insert new tuples whose TIDs VACUUM needs us to remove.
                                665                 :                :      */
    8 pg@bowt.ie                666                 :             30 :     ginInsertCleanup(&gvs.ginstate, !AmAutoVacuumWorkerProcess(),
                                667                 :                :                      false, true, stats);
                                668                 :                : 
                                669                 :                :     /* we'll re-count the tuples each time */
 7422 tgl@sss.pgh.pa.us         670                 :             30 :     stats->num_index_tuples = 0;
                                671                 :             30 :     gvs.result = stats;
                                672                 :                : 
 6509 heikki.linnakangas@i      673                 :             30 :     buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                674                 :                :                                 RBM_NORMAL, info->strategy);
                                675                 :                : 
                                676                 :                :     /* find leaf page */
                                677                 :                :     for (;;)
 7267 bruce@momjian.us          678                 :              8 :     {
 3781 kgrittn@postgresql.o      679                 :             38 :         Page        page = BufferGetPage(buffer);
                                680                 :                :         IndexTuple  itup;
                                681                 :                : 
 7267 bruce@momjian.us          682                 :             38 :         LockBuffer(buffer, GIN_SHARE);
                                683                 :                : 
                                684         [ -  + ]:             38 :         Assert(!GinPageIsData(page));
                                685                 :                : 
                                686         [ +  + ]:             38 :         if (GinPageIsLeaf(page))
                                687                 :                :         {
                                688                 :             30 :             LockBuffer(buffer, GIN_UNLOCK);
                                689                 :             30 :             LockBuffer(buffer, GIN_EXCLUSIVE);
                                690                 :                : 
                                691   [ +  +  -  + ]:             30 :             if (blkno == GIN_ROOT_BLKNO && !GinPageIsLeaf(page))
                                692                 :                :             {
 7267 bruce@momjian.us          693                 :UBC           0 :                 LockBuffer(buffer, GIN_UNLOCK);
                                694                 :              0 :                 continue;       /* check it one more */
                                695                 :                :             }
 7267 bruce@momjian.us          696                 :CBC          30 :             break;
                                697                 :                :         }
                                698                 :                : 
                                699         [ -  + ]:              8 :         Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
                                700                 :                : 
 7422 teodor@sigaev.ru          701                 :              8 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
 5711 tgl@sss.pgh.pa.us         702                 :              8 :         blkno = GinGetDownlink(itup);
 7267 bruce@momjian.us          703         [ -  + ]:              8 :         Assert(blkno != InvalidBlockNumber);
                                704                 :                : 
 7028 teodor@sigaev.ru          705                 :              8 :         UnlockReleaseBuffer(buffer);
 6509 heikki.linnakangas@i      706                 :              8 :         buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                707                 :                :                                     RBM_NORMAL, info->strategy);
                                708                 :                :     }
                                709                 :                : 
                                710                 :                :     /* right now we found leftmost page in entry's BTree */
                                711                 :                : 
                                712                 :                :     for (;;)
 7267 bruce@momjian.us          713                 :           1192 :     {
 3781 kgrittn@postgresql.o      714                 :           1222 :         Page        page = BufferGetPage(buffer);
                                715                 :                :         Page        resPage;
                                716                 :                :         uint32      i;
                                717                 :                : 
 7267 bruce@momjian.us          718         [ -  + ]:           1222 :         Assert(!GinPageIsData(page));
                                719                 :                : 
 7422 teodor@sigaev.ru          720                 :           1222 :         resPage = ginVacuumEntryPage(&gvs, buffer, rootOfPostingTree, &nRoot);
                                721                 :                : 
 7267 bruce@momjian.us          722                 :           1222 :         blkno = GinPageGetOpaque(page)->rightlink;
                                723                 :                : 
                                724         [ +  + ]:           1222 :         if (resPage)
                                725                 :                :         {
 7422 teodor@sigaev.ru          726                 :           1149 :             START_CRIT_SECTION();
 7267 bruce@momjian.us          727                 :           1149 :             PageRestoreTempPage(resPage, page);
                                728                 :           1149 :             MarkBufferDirty(buffer);
 7023 teodor@sigaev.ru          729                 :           1149 :             xlogVacuumPage(gvs.index, buffer);
 7422                           730         [ -  + ]:           1149 :             END_CRIT_SECTION();
  189 michael@paquier.xyz       731                 :           1149 :             UnlockReleaseBuffer(buffer);
                                732                 :                :         }
                                733                 :                :         else
                                734                 :                :         {
 7422 teodor@sigaev.ru          735                 :             73 :             UnlockReleaseBuffer(buffer);
                                736                 :                :         }
                                737                 :                : 
  562 nathan@postgresql.or      738                 :           1222 :         vacuum_delay_point(false);
                                739                 :                : 
 7267 bruce@momjian.us          740         [ +  + ]:           1241 :         for (i = 0; i < nRoot; i++)
                                741                 :                :         {
                                742                 :             19 :             ginVacuumPostingTree(&gvs, rootOfPostingTree[i]);
  562 nathan@postgresql.or      743                 :             19 :             vacuum_delay_point(false);
                                744                 :                :         }
                                745                 :                : 
 3354 tgl@sss.pgh.pa.us         746         [ +  + ]:           1222 :         if (blkno == InvalidBlockNumber)    /* rightmost page */
 7422 teodor@sigaev.ru          747                 :             30 :             break;
                                748                 :                : 
 6509 heikki.linnakangas@i      749                 :           1192 :         buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                750                 :                :                                     RBM_NORMAL, info->strategy);
 7267 bruce@momjian.us          751                 :           1192 :         LockBuffer(buffer, GIN_EXCLUSIVE);
                                752                 :                :     }
                                753                 :                : 
 4600 heikki.linnakangas@i      754                 :             30 :     MemoryContextDelete(gvs.tmpCxt);
                                755                 :                : 
 3875 tgl@sss.pgh.pa.us         756                 :             30 :     return gvs.result;
                                757                 :                : }
                                758                 :                : 
                                759                 :                : IndexBulkDeleteResult *
                                760                 :             54 : ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
                                761                 :                : {
 7267 bruce@momjian.us          762                 :             54 :     Relation    index = info->index;
                                763                 :                :     bool        needLock;
                                764                 :                :     BlockNumber npages,
                                765                 :                :                 blkno;
                                766                 :                :     BlockNumber totFreePages;
                                767                 :                :     GinState    ginstate;
                                768                 :                :     GinStatsData idxStat;
                                769                 :                :     BlockRangeReadStreamPrivate p;
                                770                 :                :     ReadStream *stream;
                                771                 :                : 
                                772                 :                :     /*
                                773                 :                :      * In an autovacuum analyze, we want to clean up pending insertions.
                                774                 :                :      * Otherwise, an ANALYZE-only call is a no-op.
                                775                 :                :      */
 6365 tgl@sss.pgh.pa.us         776         [ +  + ]:             54 :     if (info->analyze_only)
                                777                 :                :     {
  906 heikki.linnakangas@i      778         [ +  + ]:             15 :         if (AmAutoVacuumWorkerProcess())
                                779                 :                :         {
 6365 tgl@sss.pgh.pa.us         780                 :             11 :             initGinState(&ginstate, index);
 3206 rhaas@postgresql.org      781                 :             11 :             ginInsertCleanup(&ginstate, false, true, true, stats);
                                782                 :                :         }
 3875 tgl@sss.pgh.pa.us         783                 :             15 :         return stats;
                                784                 :                :     }
                                785                 :                : 
                                786                 :                :     /*
                                787                 :                :      * Set up all-zero stats and cleanup pending inserts if ginbulkdelete
                                788                 :                :      * wasn't called
                                789                 :                :      */
 7422                           790         [ +  + ]:             39 :     if (stats == NULL)
                                791                 :                :     {
  260 michael@paquier.xyz       792                 :             29 :         stats = palloc0_object(IndexBulkDeleteResult);
 6365 tgl@sss.pgh.pa.us         793                 :             29 :         initGinState(&ginstate, index);
  906 heikki.linnakangas@i      794                 :             29 :         ginInsertCleanup(&ginstate, !AmAutoVacuumWorkerProcess(),
                                795                 :                :                          false, true, stats);
                                796                 :                :     }
                                797                 :                : 
 5793 tgl@sss.pgh.pa.us         798                 :             39 :     memset(&idxStat, 0, sizeof(idxStat));
                                799                 :                : 
                                800                 :                :     /*
                                801                 :                :      * XXX we always report the heap tuple count as the number of index
                                802                 :                :      * entries.  This is bogus if the index is partial, but it's real hard to
                                803                 :                :      * tell how many distinct heap entries are referenced by a GIN index.
                                804                 :                :      */
 2188                           805         [ +  - ]:             39 :     stats->num_index_tuples = Max(info->num_heap_tuples, 0);
 6291                           806                 :             39 :     stats->estimated_count = info->estimated_count;
                                807                 :                : 
                                808                 :                :     /*
                                809                 :                :      * Need lock unless it's local to this backend.
                                810                 :                :      */
 6044                           811   [ +  +  +  - ]:             39 :     needLock = !RELATION_IS_LOCAL(index);
                                812                 :                : 
 7422 teodor@sigaev.ru          813         [ +  + ]:             39 :     if (needLock)
                                814                 :             33 :         LockRelationForExtension(index, ExclusiveLock);
                                815                 :             39 :     npages = RelationGetNumberOfBlocks(index);
                                816         [ +  + ]:             39 :     if (needLock)
                                817                 :             33 :         UnlockRelationForExtension(index, ExclusiveLock);
                                818                 :                : 
 6286 bruce@momjian.us          819                 :             39 :     totFreePages = 0;
                                820                 :                : 
                                821                 :                :     /* Scan all blocks starting from the root using streaming reads */
  168 michael@paquier.xyz       822                 :             39 :     p.current_blocknum = GIN_ROOT_BLKNO;
                                823                 :             39 :     p.last_exclusive = npages;
                                824                 :                : 
                                825                 :                :     /*
                                826                 :                :      * It is safe to use batchmode as block_range_read_stream_cb takes no
                                827                 :                :      * locks.
                                828                 :                :      */
                                829                 :             39 :     stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
                                830                 :                :                                         READ_STREAM_FULL |
                                831                 :                :                                         READ_STREAM_USE_BATCHING,
                                832                 :                :                                         info->strategy,
                                833                 :                :                                         index,
                                834                 :                :                                         MAIN_FORKNUM,
                                835                 :                :                                         block_range_read_stream_cb,
                                836                 :                :                                         &p,
                                837                 :                :                                         0);
                                838                 :                : 
 5793 tgl@sss.pgh.pa.us         839         [ +  + ]:           6162 :     for (blkno = GIN_ROOT_BLKNO; blkno < npages; blkno++)
                                840                 :                :     {
                                841                 :                :         Buffer      buffer;
                                842                 :                :         Page        page;
                                843                 :                : 
  562 nathan@postgresql.or      844                 :           6123 :         vacuum_delay_point(false);
                                845                 :                : 
  168 michael@paquier.xyz       846                 :           6123 :         buffer = read_stream_next_buffer(stream, NULL);
                                847                 :                : 
 7422 teodor@sigaev.ru          848                 :           6123 :         LockBuffer(buffer, GIN_SHARE);
  363 peter@eisentraut.org      849                 :           6123 :         page = BufferGetPage(buffer);
                                850                 :                : 
 2814 akorotkov@postgresql      851         [ +  + ]:           6123 :         if (GinPageIsRecyclable(page))
                                852                 :                :         {
 5793 tgl@sss.pgh.pa.us         853         [ -  + ]:           3105 :             Assert(blkno != GIN_ROOT_BLKNO);
 6540 heikki.linnakangas@i      854                 :           3105 :             RecordFreeIndexPage(index, blkno);
 7280 tgl@sss.pgh.pa.us         855                 :           3105 :             totFreePages++;
                                856                 :                :         }
 5793                           857         [ +  + ]:           3018 :         else if (GinPageIsData(page))
                                858                 :                :         {
                                859                 :            163 :             idxStat.nDataPages++;
                                860                 :                :         }
                                861         [ +  - ]:           2855 :         else if (!GinPageIsList(page))
                                862                 :                :         {
                                863                 :           2855 :             idxStat.nEntryPages++;
                                864                 :                : 
 5618 bruce@momjian.us          865         [ +  + ]:           2855 :             if (GinPageIsLeaf(page))
 5793 tgl@sss.pgh.pa.us         866                 :           2835 :                 idxStat.nEntries += PageGetMaxOffsetNumber(page);
                                867                 :                :         }
                                868                 :                : 
 7422 teodor@sigaev.ru          869                 :           6123 :         UnlockReleaseBuffer(buffer);
                                870                 :                :     }
                                871                 :                : 
  168 michael@paquier.xyz       872         [ -  + ]:             39 :     Assert(read_stream_next_buffer(stream, NULL) == InvalidBuffer);
                                873                 :             39 :     read_stream_end(stream);
                                874                 :                : 
                                875                 :                :     /* Update the metapage with accurate page and entry counts */
 5793 tgl@sss.pgh.pa.us         876                 :             39 :     idxStat.nTotalPages = npages;
 2703 heikki.linnakangas@i      877                 :             39 :     ginUpdateStats(info->index, &idxStat, false);
                                878                 :                : 
                                879                 :                :     /* Finally, vacuum the FSM */
 6534                           880                 :             39 :     IndexFreeSpaceMapVacuum(info->index);
                                881                 :                : 
 7280 tgl@sss.pgh.pa.us         882                 :             39 :     stats->pages_free = totFreePages;
                                883                 :                : 
 7422 teodor@sigaev.ru          884         [ +  + ]:             39 :     if (needLock)
                                885                 :             33 :         LockRelationForExtension(index, ExclusiveLock);
                                886                 :             39 :     stats->num_pages = RelationGetNumberOfBlocks(index);
                                887         [ +  + ]:             39 :     if (needLock)
                                888                 :             33 :         UnlockRelationForExtension(index, ExclusiveLock);
                                889                 :                : 
 3875 tgl@sss.pgh.pa.us         890                 :             39 :     return stats;
                                891                 :                : }
                                892                 :                : 
                                893                 :                : /*
                                894                 :                :  * Return whether Page can safely be recycled.
                                895                 :                :  */
                                896                 :                : bool
 2206 andres@anarazel.de        897                 :           6194 : GinPageIsRecyclable(Page page)
                                898                 :                : {
                                899                 :                :     TransactionId delete_xid;
                                900                 :                : 
                                901         [ -  + ]:           6194 :     if (PageIsNew(page))
 2206 andres@anarazel.de        902                 :UBC           0 :         return true;
                                903                 :                : 
 2206 andres@anarazel.de        904         [ +  + ]:CBC        6194 :     if (!GinPageIsDeleted(page))
                                905                 :           3009 :         return false;
                                906                 :                : 
                                907                 :           3185 :     delete_xid = GinPageGetDeleteXid(page);
                                908                 :                : 
                                909         [ +  + ]:           3185 :     if (!TransactionIdIsValid(delete_xid))
                                910                 :           3175 :         return true;
                                911                 :                : 
                                912                 :                :     /*
                                913                 :                :      * If no backend still could view delete_xid as in running, all scans
                                914                 :                :      * concurrent with ginDeletePostingPage() must have finished.
                                915                 :                :      */
                                916                 :             10 :     return GlobalVisCheckRemovableXid(NULL, delete_xid);
                                917                 :                : }
        

Generated by: LCOV version 2.0-1