LCOV - differential code coverage report
Current view: top level - src/backend/access/gin - ginvacuum.c (source / functions) Coverage Total Hit UNC LBC UBC GNC CBC DUB DCB
Current: 0e5ff9b9b45a657aea12440478dc002e9b01f138 vs 0123ce131fca454009439dfa3b2266d1d40737d7 Lines: 93.8 % 305 286 19 51 235 50
Current Date: 2026-03-14 14:10:32 -0400 Functions: 100.0 % 10 10 7 3 2
Baseline: lcov-20260315-024220-baseline Branches: 73.5 % 170 125 6 3 36 20 105 5 25
Baseline Date: 2026-03-14 15:27:56 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 100.0 % 6 6 6
(7,30] days: 100.0 % 41 41 40 1
(30,360] days: 100.0 % 5 5 5
(360..) days: 92.5 % 253 234 19 234
Function coverage date bins:
(7,30] days: 100.0 % 2 2 2
(360..) days: 100.0 % 8 8 5 3
Branch coverage date bins:
(1,7] days: 50.0 % 2 1 1 1
(7,30] days: 79.2 % 24 19 4 1 18 1
(30,360] days: 50.0 % 2 1 1 1
(360..) days: 73.2 % 142 104 3 35 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
 4435 heikki.linnakangas@i       48                 :CBC      125937 : ginVacuumItemPointers(GinVacuumState *gvs, ItemPointerData *items,
                                 49                 :                :                       int nitem, int *nremaining)
                                 50                 :                : {
                                 51                 :                :     int         i,
                                 52                 :         125937 :                 remaining = 0;
 4331 bruce@momjian.us           53                 :         125937 :     ItemPointer tmpitems = NULL;
                                 54                 :                : 
                                 55                 :                :     /*
                                 56                 :                :      * Iterate over TIDs array
                                 57                 :                :      */
 7102                            58         [ +  + ]:         547910 :     for (i = 0; i < nitem; i++)
                                 59                 :                :     {
                                 60         [ +  + ]:         421973 :         if (gvs->callback(items + i, gvs->callback_state))
                                 61                 :                :         {
 7257 teodor@sigaev.ru           62                 :         368925 :             gvs->result->tuples_removed += 1;
 4435 heikki.linnakangas@i       63         [ +  + ]:         368925 :             if (!tmpitems)
                                 64                 :                :             {
                                 65                 :                :                 /*
                                 66                 :                :                  * First TID to be deleted: allocate memory to hold the
                                 67                 :                :                  * remaining items.
                                 68                 :                :                  */
   95 michael@paquier.xyz        69                 :GNC      120444 :                 tmpitems = palloc_array(ItemPointerData, nitem);
 4435 heikki.linnakangas@i       70                 :CBC      120444 :                 memcpy(tmpitems, items, sizeof(ItemPointerData) * i);
                                 71                 :                :             }
                                 72                 :                :         }
                                 73                 :                :         else
                                 74                 :                :         {
 7257 teodor@sigaev.ru           75                 :          53048 :             gvs->result->num_index_tuples += 1;
 4435 heikki.linnakangas@i       76         [ +  + ]:          53048 :             if (tmpitems)
                                 77                 :           3726 :                 tmpitems[remaining] = items[i];
                                 78                 :          53048 :             remaining++;
                                 79                 :                :         }
                                 80                 :                :     }
                                 81                 :                : 
                                 82                 :         125937 :     *nremaining = remaining;
                                 83                 :         125937 :     return tmpitems;
                                 84                 :                : }
                                 85                 :                : 
                                 86                 :                : /*
                                 87                 :                :  * Create a WAL record for vacuuming entry tree leaf page.
                                 88                 :                :  */
                                 89                 :                : static void
 7102 bruce@momjian.us           90                 :            862 : xlogVacuumPage(Relation index, Buffer buffer)
                                 91                 :                : {
 3616 kgrittn@postgresql.o       92                 :            862 :     Page        page = BufferGetPage(buffer);
                                 93                 :                :     XLogRecPtr  recptr;
                                 94                 :                : 
                                 95                 :                :     /* This is only used for entry tree leaf pages. */
 4435 heikki.linnakangas@i       96         [ -  + ]:            862 :     Assert(!GinPageIsData(page));
 7102 bruce@momjian.us           97         [ -  + ]:            862 :     Assert(GinPageIsLeaf(page));
                                 98                 :                : 
 5571 rhaas@postgresql.org       99   [ +  +  -  +  :            862 :     if (!RelationNeedsWAL(index))
                                        -  -  -  - ]
 7102 bruce@momjian.us          100                 :            861 :         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                 :                :      */
 4133 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);
 7257 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
   22 akorotkov@postgresql      152                 :GNC           6 : ginDeletePostingPage(GinVacuumState *gvs, Buffer dBuffer, Buffer lBuffer,
                                153                 :                :                      Buffer pBuffer, OffsetNumber myoff, bool isParentRoot)
                                154                 :                : {
                                155                 :                :     Page        page,
                                156                 :                :                 parentPage;
                                157                 :                :     BlockNumber rightlink;
                                158                 :              6 :     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                 :                : 
 2907 teodor@sigaev.ru          167                 :CBC           6 :     page = BufferGetPage(dBuffer);
                                168                 :              6 :     rightlink = GinPageGetOpaque(page)->rightlink;
                                169                 :                : 
                                170                 :                :     /*
                                171                 :                :      * Any insert which would have gone on the leaf block will now go to its
                                172                 :                :      * right sibling.
                                173                 :                :      */
                                174                 :              6 :     PredicateLockPageCombine(gvs->index, deleteBlkno, rightlink);
                                175                 :                : 
 7257                           176                 :              6 :     START_CRIT_SECTION();
                                177                 :                : 
                                178                 :                :     /* Unlink the page by changing left sibling's rightlink */
 3616 kgrittn@postgresql.o      179                 :              6 :     page = BufferGetPage(lBuffer);
 4510 heikki.linnakangas@i      180                 :              6 :     GinPageGetOpaque(page)->rightlink = rightlink;
                                181                 :                : 
                                182                 :                :     /* Delete downlink from parent */
 3616 kgrittn@postgresql.o      183                 :              6 :     parentPage = BufferGetPage(pBuffer);
                                184                 :                : #ifdef USE_ASSERT_CHECKING
                                185                 :                :     do
                                186                 :                :     {
 4546 heikki.linnakangas@i      187                 :              6 :         PostingItem *tod = GinDataPageGetPostingItem(parentPage, myoff);
                                188                 :                : 
 6695 bruce@momjian.us          189         [ -  + ]:              6 :         Assert(PostingItemGetBlockNumber(tod) == deleteBlkno);
                                190                 :                :     } while (0);
                                191                 :                : #endif
 5628 tgl@sss.pgh.pa.us         192                 :              6 :     GinPageDeletePostingItem(parentPage, myoff);
                                193                 :                : 
 3616 kgrittn@postgresql.o      194                 :              6 :     page = BufferGetPage(dBuffer);
                                195                 :                : 
                                196                 :                :     /*
                                197                 :                :      * we shouldn't change rightlink field to save workability of running
                                198                 :                :      * search scan
                                199                 :                :      */
                                200                 :                : 
                                201                 :                :     /*
                                202                 :                :      * Mark page as deleted, and remember last xid which could know its
                                203                 :                :      * address.
                                204                 :                :      */
 2308 akorotkov@postgresql      205                 :              6 :     GinPageSetDeleted(page);
 1854 tmunro@postgresql.or      206                 :              6 :     GinPageSetDeleteXid(page, ReadNextTransactionId());
                                207                 :                : 
 6858 teodor@sigaev.ru          208                 :              6 :     MarkBufferDirty(pBuffer);
 4353 heikki.linnakangas@i      209                 :              6 :     MarkBufferDirty(lBuffer);
 6858 teodor@sigaev.ru          210                 :              6 :     MarkBufferDirty(dBuffer);
                                211                 :                : 
 5571 rhaas@postgresql.org      212   [ -  +  -  -  :              6 :     if (RelationNeedsWAL(gvs->index))
                                        -  -  -  - ]
                                213                 :                :     {
                                214                 :                :         XLogRecPtr  recptr;
                                215                 :                :         ginxlogDeletePage data;
                                216                 :                : 
                                217                 :                :         /*
                                218                 :                :          * We can't pass REGBUF_STANDARD for the deleted page, because we
                                219                 :                :          * didn't set pd_lower on pre-9.4 versions. The page might've been
                                220                 :                :          * binary-upgraded from an older version, and hence not have pd_lower
                                221                 :                :          * set correctly. Ditto for the left page, but removing the item from
                                222                 :                :          * the parent updated its pd_lower, so we know that's OK at this
                                223                 :                :          * point.
                                224                 :                :          */
 4133 heikki.linnakangas@i      225                 :UBC           0 :         XLogBeginInsert();
                                226                 :              0 :         XLogRegisterBuffer(0, dBuffer, 0);
                                227                 :              0 :         XLogRegisterBuffer(1, pBuffer, REGBUF_STANDARD);
                                228                 :              0 :         XLogRegisterBuffer(2, lBuffer, 0);
                                229                 :                : 
 7257 teodor@sigaev.ru          230                 :              0 :         data.parentOffset = myoff;
 7102 bruce@momjian.us          231                 :              0 :         data.rightLink = GinPageGetOpaque(page)->rightlink;
 2649 akorotkov@postgresql      232                 :              0 :         data.deleteXid = GinPageGetDeleteXid(page);
                                233                 :                : 
  397 peter@eisentraut.org      234                 :              0 :         XLogRegisterData(&data, sizeof(ginxlogDeletePage));
                                235                 :                : 
 4133 heikki.linnakangas@i      236                 :              0 :         recptr = XLogInsert(RM_GIN_ID, XLOG_GIN_DELETE_PAGE);
 7257 teodor@sigaev.ru          237                 :              0 :         PageSetLSN(page, recptr);
                                238                 :              0 :         PageSetLSN(parentPage, recptr);
 3616 kgrittn@postgresql.o      239                 :              0 :         PageSetLSN(BufferGetPage(lBuffer), recptr);
                                240                 :                :     }
                                241                 :                : 
   24 michael@paquier.xyz       242         [ -  + ]:CBC           6 :     END_CRIT_SECTION();
                                243                 :                : 
 1844 pg@bowt.ie                244                 :              6 :     gvs->result->pages_newly_deleted++;
 7257 teodor@sigaev.ru          245                 :              6 :     gvs->result->pages_deleted++;
                                246                 :              6 : }
                                247                 :                : 
                                248                 :                : 
                                249                 :                : /*
                                250                 :                :  * Scans a posting tree and deletes empty pages.
                                251                 :                :  *
                                252                 :                :  * The caller must hold a cleanup lock on the root page to prevent concurrent
                                253                 :                :  * inserts.  The entire path from the root down to the current page is kept
                                254                 :                :  * exclusively locked throughout the scan.  The left sibling at each level is
                                255                 :                :  * also kept locked, because ginDeletePostingPage() needs it to update the
                                256                 :                :  * rightlink of the left sibling; re-acquiring the left sibling lock later
                                257                 :                :  * could deadlock with ginStepRight(), which acquires page locks
                                258                 :                :  * left-to-right.
                                259                 :                :  *
                                260                 :                :  * All per-level state is carried in 'myStackItem': the buffer to process
                                261                 :                :  * (must already be pinned and exclusively locked), the left sibling buffer,
                                262                 :                :  * and this page's offset in the parent's downlink array.  The root entry is
                                263                 :                :  * set up by ginVacuumPostingTree(); child entries are populated here before
                                264                 :                :  * recursing.
                                265                 :                :  *
                                266                 :                :  * Returns true if the page was deleted, false otherwise.
                                267                 :                :  */
                                268                 :                : static bool
   22 akorotkov@postgresql      269                 :GNC          24 : ginScanPostingTreeToDelete(GinVacuumState *gvs, DataPageDeleteStack *myStackItem)
                                270                 :                : {
                                271                 :             24 :     Buffer      buffer = myStackItem->buffer;
                                272                 :                :     Page        page;
                                273                 :             24 :     bool        pageWasDeleted = false;
                                274                 :                :     bool        isempty;
                                275                 :                : 
 3616 kgrittn@postgresql.o      276                 :CBC          24 :     page = BufferGetPage(buffer);
                                277                 :                : 
 7102 bruce@momjian.us          278         [ -  + ]:             24 :     Assert(GinPageIsData(page));
                                279                 :                : 
                                280         [ +  + ]:             24 :     if (!GinPageIsLeaf(page))
                                281                 :                :     {
                                282                 :                :         OffsetNumber i;
                                283                 :                : 
   22 akorotkov@postgresql      284         [ +  + ]:GNC          24 :         for (i = FirstOffsetNumber; i <= GinPageGetOpaque(page)->maxoff;)
                                285                 :                :         {
 4546 heikki.linnakangas@i      286                 :CBC          18 :             PostingItem *pitem = GinDataPageGetPostingItem(page, i);
                                287                 :                :             Buffer      childBuffer;
                                288                 :                : 
   22 akorotkov@postgresql      289                 :GNC          18 :             childBuffer = ReadBufferExtended(gvs->index,
                                290                 :                :                                              MAIN_FORKNUM,
                                291                 :             18 :                                              PostingItemGetBlockNumber(pitem),
                                292                 :                :                                              RBM_NORMAL, gvs->strategy);
                                293                 :             18 :             LockBuffer(childBuffer, GIN_EXCLUSIVE);
                                294                 :                : 
                                295                 :                :             /* Allocate a child stack entry on first use; reuse thereafter */
                                296         [ +  + ]:             18 :             if (!myStackItem->child)
                                297                 :                :             {
                                298                 :              6 :                 myStackItem->child = palloc0_object(DataPageDeleteStack);
                                299                 :              6 :                 myStackItem->child->parent = myStackItem;
                                300                 :              6 :                 myStackItem->child->leftBuffer = InvalidBuffer;
                                301                 :                :             }
                                302                 :                : 
                                303                 :             18 :             myStackItem->child->buffer = childBuffer;
                                304                 :             18 :             myStackItem->child->isRoot = false;
                                305                 :             18 :             myStackItem->child->myoff = i;
                                306                 :                : 
                                307                 :                :             /*
                                308                 :                :              * Recurse into child.  If the child page was deleted, its
                                309                 :                :              * downlink was removed from our page, so re-examine the same
                                310                 :                :              * offset; otherwise advance to the next downlink.
                                311                 :                :              */
                                312         [ +  + ]:             18 :             if (!ginScanPostingTreeToDelete(gvs, myStackItem->child))
                                313                 :             12 :                 i++;
                                314                 :                :         }
                                315                 :              6 :         myStackItem->buffer = InvalidBuffer;
                                316                 :                : 
                                317                 :                :         /*
                                318                 :                :          * After processing all children at this level, release the child
                                319                 :                :          * level's leftBuffer if we're at the rightmost page.  There is no
                                320                 :                :          * right sibling that could need it for deletion.
                                321                 :                :          */
                                322   [ +  -  +  - ]:              6 :         if (GinPageRightMost(page) && BufferIsValid(myStackItem->child->leftBuffer))
                                323                 :                :         {
                                324                 :              6 :             UnlockReleaseBuffer(myStackItem->child->leftBuffer);
                                325                 :              6 :             myStackItem->child->leftBuffer = InvalidBuffer;
                                326                 :                :         }
                                327                 :                :     }
                                328                 :                : 
 4435 heikki.linnakangas@i      329         [ +  + ]:CBC          24 :     if (GinPageIsLeaf(page))
                                330         [ +  - ]:             18 :         isempty = GinDataLeafPageIsEmpty(page);
                                331                 :                :     else
                                332                 :              6 :         isempty = GinPageGetOpaque(page)->maxoff < FirstOffsetNumber;
                                333                 :                : 
                                334         [ +  + ]:             24 :     if (isempty)
                                335                 :                :     {
                                336                 :                :         /*
                                337                 :                :          * Proceed to the ginDeletePostingPage() if that's not the leftmost or
                                338                 :                :          * the rightmost page.
                                339                 :                :          */
   22 akorotkov@postgresql      340   [ +  +  +  + ]:GNC          15 :         if (BufferIsValid(myStackItem->leftBuffer) && !GinPageRightMost(page))
                                341                 :                :         {
                                342         [ -  + ]:              6 :             Assert(!myStackItem->isRoot);
                                343                 :              6 :             ginDeletePostingPage(gvs, buffer, myStackItem->leftBuffer,
                                344                 :              6 :                                  myStackItem->parent->buffer,
                                345                 :              6 :                                  myStackItem->myoff,
                                346                 :              6 :                                  myStackItem->parent->isRoot);
                                347                 :              6 :             pageWasDeleted = true;
                                348                 :                :         }
                                349                 :                :     }
                                350                 :                : 
                                351         [ +  + ]:             24 :     if (!pageWasDeleted)
                                352                 :                :     {
                                353                 :                :         /*
                                354                 :                :          * Keep this page as the new leftBuffer for this level: the next
                                355                 :                :          * sibling to the right might need it for deletion.  Release any
                                356                 :                :          * previously held left page first.
                                357                 :                :          */
                                358         [ +  + ]:             18 :         if (BufferIsValid(myStackItem->leftBuffer))
                                359                 :              6 :             UnlockReleaseBuffer(myStackItem->leftBuffer);
                                360                 :             18 :         myStackItem->leftBuffer = buffer;
                                361                 :                :     }
                                362                 :                :     else
                                363                 :                :     {
                                364                 :                :         /*
                                365                 :                :          * Page was deleted; release the buffer.  leftBuffer remains the same.
                                366                 :                :          */
                                367                 :              6 :         UnlockReleaseBuffer(buffer);
                                368                 :                :     }
                                369                 :                : 
                                370                 :             24 :     return pageWasDeleted;
                                371                 :                : }
                                372                 :                : 
                                373                 :                : 
                                374                 :                : /*
                                375                 :                :  * Scan through posting tree leafs, delete empty tuples.  Returns true if there
                                376                 :                :  * is at least one empty page.
                                377                 :                :  */
                                378                 :                : static bool
 2649 akorotkov@postgresql      379                 :CBC          12 : ginVacuumPostingTreeLeaves(GinVacuumState *gvs, BlockNumber blkno)
                                380                 :                : {
                                381                 :                :     Buffer      buffer;
                                382                 :                :     Page        page;
 3133 peter_e@gmx.net           383                 :             12 :     bool        hasVoidPage = false;
                                384                 :                :     MemoryContext oldCxt;
                                385                 :                : 
                                386                 :                :     /* Find leftmost leaf page of posting tree and lock it in exclusive mode */
                                387                 :                :     while (true)
 2649 akorotkov@postgresql      388                 :              6 :     {
                                389                 :                :         PostingItem *pitem;
                                390                 :                : 
                                391                 :             18 :         buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
                                392                 :                :                                     RBM_NORMAL, gvs->strategy);
                                393                 :             18 :         LockBuffer(buffer, GIN_SHARE);
                                394                 :             18 :         page = BufferGetPage(buffer);
                                395                 :                : 
                                396         [ -  + ]:             18 :         Assert(GinPageIsData(page));
                                397                 :                : 
                                398         [ +  + ]:             18 :         if (GinPageIsLeaf(page))
                                399                 :                :         {
                                400                 :             12 :             LockBuffer(buffer, GIN_UNLOCK);
                                401                 :             12 :             LockBuffer(buffer, GIN_EXCLUSIVE);
                                402                 :             12 :             break;
                                403                 :                :         }
                                404                 :                : 
                                405         [ -  + ]:              6 :         Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
                                406                 :                : 
                                407                 :              6 :         pitem = GinDataPageGetPostingItem(page, FirstOffsetNumber);
                                408                 :              6 :         blkno = PostingItemGetBlockNumber(pitem);
                                409         [ -  + ]:              6 :         Assert(blkno != InvalidBlockNumber);
                                410                 :                : 
                                411                 :              6 :         UnlockReleaseBuffer(buffer);
                                412                 :                :     }
                                413                 :                : 
                                414                 :                :     /* Iterate all posting tree leaves using rightlinks and vacuum them */
                                415                 :                :     while (true)
                                416                 :                :     {
 3279 teodor@sigaev.ru          417                 :             24 :         oldCxt = MemoryContextSwitchTo(gvs->tmpCxt);
                                418                 :             24 :         ginVacuumPostingTreeLeaf(gvs->index, buffer, gvs);
                                419                 :             24 :         MemoryContextSwitchTo(oldCxt);
                                420                 :             24 :         MemoryContextReset(gvs->tmpCxt);
                                421                 :                : 
                                422   [ +  -  +  + ]:             24 :         if (GinDataLeafPageIsEmpty(page))
 3133 peter_e@gmx.net           423                 :             15 :             hasVoidPage = true;
                                424                 :                : 
 2649 akorotkov@postgresql      425                 :             24 :         blkno = GinPageGetOpaque(page)->rightlink;
                                426                 :                : 
 3279 teodor@sigaev.ru          427                 :             24 :         UnlockReleaseBuffer(buffer);
                                428                 :                : 
 2649 akorotkov@postgresql      429         [ +  + ]:             24 :         if (blkno == InvalidBlockNumber)
                                430                 :             12 :             break;
                                431                 :                : 
                                432                 :             12 :         buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, blkno,
                                433                 :                :                                     RBM_NORMAL, gvs->strategy);
                                434                 :             12 :         LockBuffer(buffer, GIN_EXCLUSIVE);
                                435                 :             12 :         page = BufferGetPage(buffer);
                                436                 :                :     }
                                437                 :                : 
                                438                 :             12 :     return hasVoidPage;
                                439                 :                : }
                                440                 :                : 
                                441                 :                : static void
                                442                 :             12 : ginVacuumPostingTree(GinVacuumState *gvs, BlockNumber rootBlkno)
                                443                 :                : {
                                444         [ +  + ]:             12 :     if (ginVacuumPostingTreeLeaves(gvs, rootBlkno))
                                445                 :                :     {
                                446                 :                :         /*
                                447                 :                :          * There is at least one empty page.  So we have to rescan the tree
                                448                 :                :          * deleting empty pages.
                                449                 :                :          */
                                450                 :                :         Buffer      buffer;
                                451                 :                :         DataPageDeleteStack root,
                                452                 :                :                    *ptr,
                                453                 :                :                    *tmp;
                                454                 :                :         bool        deleted PG_USED_FOR_ASSERTS_ONLY;
                                455                 :                : 
                                456                 :              6 :         buffer = ReadBufferExtended(gvs->index, MAIN_FORKNUM, rootBlkno,
                                457                 :                :                                     RBM_NORMAL, gvs->strategy);
                                458                 :                : 
                                459                 :                :         /*
                                460                 :                :          * Lock posting tree root for cleanup to ensure there are no
                                461                 :                :          * concurrent inserts.
                                462                 :                :          */
                                463                 :              6 :         LockBufferForCleanup(buffer);
                                464                 :                : 
                                465                 :              6 :         memset(&root, 0, sizeof(DataPageDeleteStack));
   22 akorotkov@postgresql      466                 :GNC           6 :         root.buffer = buffer;
 2308 akorotkov@postgresql      467                 :CBC           6 :         root.leftBuffer = InvalidBuffer;
   22 akorotkov@postgresql      468                 :GNC           6 :         root.myoff = InvalidOffsetNumber;
 2649 akorotkov@postgresql      469                 :CBC           6 :         root.isRoot = true;
                                470                 :                : 
   22 akorotkov@postgresql      471                 :GNC           6 :         deleted = ginScanPostingTreeToDelete(gvs, &root);
                                472         [ -  + ]:              6 :         Assert(!deleted);
                                473                 :                : 
 2649 akorotkov@postgresql      474                 :CBC           6 :         ptr = root.child;
                                475                 :                : 
                                476         [ +  + ]:             12 :         while (ptr)
                                477                 :                :         {
                                478                 :              6 :             tmp = ptr->child;
                                479                 :              6 :             pfree(ptr);
                                480                 :              6 :             ptr = tmp;
                                481                 :                :         }
                                482                 :                : 
                                483                 :              6 :         UnlockReleaseBuffer(buffer);
                                484                 :                :     }
 3279 teodor@sigaev.ru          485                 :             12 : }
                                486                 :                : 
                                487                 :                : /*
                                488                 :                :  * returns modified page or NULL if page isn't modified.
                                489                 :                :  * Function works with original page until first change is occurred,
                                490                 :                :  * then page is copied into temporary one.
                                491                 :                :  */
                                492                 :                : static Page
 7102 bruce@momjian.us          493                 :            937 : ginVacuumEntryPage(GinVacuumState *gvs, Buffer buffer, BlockNumber *roots, uint32 *nroot)
                                494                 :                : {
 3616 kgrittn@postgresql.o      495                 :            937 :     Page        origpage = BufferGetPage(buffer),
                                496                 :                :                 tmppage;
                                497                 :                :     OffsetNumber i,
 7102 bruce@momjian.us          498                 :            937 :                 maxoff = PageGetMaxOffsetNumber(origpage);
                                499                 :                : 
 7257 teodor@sigaev.ru          500                 :            937 :     tmppage = origpage;
                                501                 :                : 
 7102 bruce@momjian.us          502                 :            937 :     *nroot = 0;
                                503                 :                : 
                                504         [ +  + ]:         126409 :     for (i = FirstOffsetNumber; i <= maxoff; i++)
                                505                 :                :     {
                                506                 :         125472 :         IndexTuple  itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
                                507                 :                : 
                                508         [ +  + ]:         125472 :         if (GinIsPostingTree(itup))
                                509                 :                :         {
                                510                 :                :             /*
                                511                 :                :              * store posting tree's roots for further processing, we can't
                                512                 :                :              * vacuum it just now due to risk of deadlocks with scans/inserts
                                513                 :                :              */
 5546 tgl@sss.pgh.pa.us         514                 :             12 :             roots[*nroot] = GinGetDownlink(itup);
 7257 teodor@sigaev.ru          515                 :             12 :             (*nroot)++;
                                516                 :                :         }
 7102 bruce@momjian.us          517         [ +  - ]:         125460 :         else if (GinGetNPosting(itup) > 0)
                                518                 :                :         {
                                519                 :                :             int         nitems;
                                520                 :                :             ItemPointer items_orig;
                                521                 :                :             bool        free_items_orig;
                                522                 :                :             ItemPointer items;
                                523                 :                : 
                                524                 :                :             /* Get list of item pointers from the tuple. */
 4435 heikki.linnakangas@i      525         [ +  - ]:         125460 :             if (GinItupIsCompressed(itup))
                                526                 :                :             {
 4021                           527                 :         125460 :                 items_orig = ginPostingListDecode((GinPostingList *) GinGetPosting(itup), &nitems);
                                528                 :         125460 :                 free_items_orig = true;
                                529                 :                :             }
                                530                 :                :             else
                                531                 :                :             {
 4021 heikki.linnakangas@i      532                 :UBC           0 :                 items_orig = (ItemPointer) GinGetPosting(itup);
 4435                           533                 :              0 :                 nitems = GinGetNPosting(itup);
 4021                           534                 :              0 :                 free_items_orig = false;
                                535                 :                :             }
                                536                 :                : 
                                537                 :                :             /* Remove any items from the list that need to be vacuumed. */
 4021 heikki.linnakangas@i      538                 :CBC      125460 :             items = ginVacuumItemPointers(gvs, items_orig, nitems, &nitems);
                                539                 :                : 
                                540         [ +  - ]:         125460 :             if (free_items_orig)
                                541                 :         125460 :                 pfree(items_orig);
                                542                 :                : 
                                543                 :                :             /* If any item pointers were removed, recreate the tuple. */
                                544         [ +  + ]:         125460 :             if (items)
                                545                 :                :             {
                                546                 :                :                 OffsetNumber attnum;
                                547                 :                :                 Datum       key;
                                548                 :                :                 GinNullCategory category;
                                549                 :                :                 GinPostingList *plist;
                                550                 :                :                 int         plistsize;
                                551                 :                : 
 4435                           552         [ +  + ]:         120003 :                 if (nitems > 0)
                                553                 :                :                 {
 4021                           554                 :             12 :                     plist = ginCompressPostingList(items, nitems, GinMaxItemSize, NULL);
 4435                           555                 :             12 :                     plistsize = SizeOfGinPostingList(plist);
                                556                 :                :                 }
                                557                 :                :                 else
                                558                 :                :                 {
                                559                 :         119991 :                     plist = NULL;
                                560                 :         119991 :                     plistsize = 0;
                                561                 :                :                 }
                                562                 :                : 
                                563                 :                :                 /*
                                564                 :                :                  * if we already created a temporary page, make changes in
                                565                 :                :                  * place
                                566                 :                :                  */
 7102 bruce@momjian.us          567         [ +  + ]:         120003 :                 if (tmppage == origpage)
                                568                 :                :                 {
                                569                 :                :                     /*
                                570                 :                :                      * On first difference, create a temporary copy of the
                                571                 :                :                      * page and copy the tuple's posting list to it.
                                572                 :                :                      */
 6341 tgl@sss.pgh.pa.us         573                 :            862 :                     tmppage = PageGetTempPageCopy(origpage);
                                574                 :                : 
                                575                 :                :                     /* set itup pointer to new page */
 7257 teodor@sigaev.ru          576                 :            862 :                     itup = (IndexTuple) PageGetItem(tmppage, PageGetItemId(tmppage, i));
                                577                 :                :                 }
                                578                 :                : 
 6456 tgl@sss.pgh.pa.us         579                 :         120003 :                 attnum = gintuple_get_attrnum(&gvs->ginstate, itup);
 5546                           580                 :         120003 :                 key = gintuple_get_key(&gvs->ginstate, itup, &category);
                                581                 :         120003 :                 itup = GinFormTuple(&gvs->ginstate, attnum, key, category,
                                582                 :                :                                     (char *) plist, plistsize,
                                583                 :                :                                     nitems, true);
 4435 heikki.linnakangas@i      584         [ +  + ]:         120003 :                 if (plist)
                                585                 :             12 :                     pfree(plist);
 7257 teodor@sigaev.ru          586                 :         120003 :                 PageIndexTupleDelete(tmppage, i);
                                587                 :                : 
  139 peter@eisentraut.org      588         [ -  + ]:GNC      120003 :                 if (PageAddItem(tmppage, itup, IndexTupleSize(itup), i, false, false) != i)
 7102 bruce@momjian.us          589         [ #  # ]:UBC           0 :                     elog(ERROR, "failed to add item to index page in \"%s\"",
                                590                 :                :                          RelationGetRelationName(gvs->index));
                                591                 :                : 
 7102 bruce@momjian.us          592                 :CBC      120003 :                 pfree(itup);
 4021 heikki.linnakangas@i      593                 :         120003 :                 pfree(items);
                                594                 :                :             }
                                595                 :                :         }
                                596                 :                :     }
                                597                 :                : 
 7102 bruce@momjian.us          598         [ +  + ]:            937 :     return (tmppage == origpage) ? NULL : tmppage;
                                599                 :                : }
                                600                 :                : 
                                601                 :                : IndexBulkDeleteResult *
 3710 tgl@sss.pgh.pa.us         602                 :             43 : ginbulkdelete(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
                                603                 :                :               IndexBulkDeleteCallback callback, void *callback_state)
                                604                 :                : {
 7257                           605                 :             43 :     Relation    index = info->index;
 7102 bruce@momjian.us          606                 :             43 :     BlockNumber blkno = GIN_ROOT_BLKNO;
                                607                 :                :     GinVacuumState gvs;
                                608                 :                :     Buffer      buffer;
                                609                 :                :     BlockNumber rootOfPostingTree[BLCKSZ / (sizeof(IndexTupleData) + sizeof(ItemId))];
                                610                 :                :     uint32      nRoot;
                                611                 :                : 
 4435 heikki.linnakangas@i      612                 :             43 :     gvs.tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
                                613                 :                :                                        "Gin vacuum temporary context",
                                614                 :                :                                        ALLOCSET_DEFAULT_SIZES);
 6200 tgl@sss.pgh.pa.us         615                 :             43 :     gvs.index = index;
                                616                 :             43 :     gvs.callback = callback;
                                617                 :             43 :     gvs.callback_state = callback_state;
                                618                 :             43 :     gvs.strategy = info->strategy;
                                619                 :             43 :     initGinState(&gvs.ginstate, index);
                                620                 :                : 
                                621                 :                :     /* first time through? */
 7257                           622         [ +  - ]:             43 :     if (stats == NULL)
                                623                 :                :     {
                                624                 :                :         /* Yes, so initialize stats to zeroes */
   95 michael@paquier.xyz       625                 :GNC          43 :         stats = palloc0_object(IndexBulkDeleteResult);
                                626                 :                : 
                                627                 :                :         /*
                                628                 :                :          * and cleanup any pending inserts
                                629                 :                :          */
  741 heikki.linnakangas@i      630                 :CBC          43 :         ginInsertCleanup(&gvs.ginstate, !AmAutoVacuumWorkerProcess(),
                                631                 :                :                          false, true, stats);
                                632                 :                :     }
                                633                 :                : 
                                634                 :                :     /* we'll re-count the tuples each time */
 7257 tgl@sss.pgh.pa.us         635                 :             43 :     stats->num_index_tuples = 0;
                                636                 :             43 :     gvs.result = stats;
                                637                 :                : 
 6344 heikki.linnakangas@i      638                 :             43 :     buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                639                 :                :                                 RBM_NORMAL, info->strategy);
                                640                 :                : 
                                641                 :                :     /* find leaf page */
                                642                 :                :     for (;;)
 7102 bruce@momjian.us          643                 :              6 :     {
 3616 kgrittn@postgresql.o      644                 :             49 :         Page        page = BufferGetPage(buffer);
                                645                 :                :         IndexTuple  itup;
                                646                 :                : 
 7102 bruce@momjian.us          647                 :             49 :         LockBuffer(buffer, GIN_SHARE);
                                648                 :                : 
                                649         [ -  + ]:             49 :         Assert(!GinPageIsData(page));
                                650                 :                : 
                                651         [ +  + ]:             49 :         if (GinPageIsLeaf(page))
                                652                 :                :         {
                                653                 :             43 :             LockBuffer(buffer, GIN_UNLOCK);
                                654                 :             43 :             LockBuffer(buffer, GIN_EXCLUSIVE);
                                655                 :                : 
                                656   [ +  +  -  + ]:             43 :             if (blkno == GIN_ROOT_BLKNO && !GinPageIsLeaf(page))
                                657                 :                :             {
 7102 bruce@momjian.us          658                 :UBC           0 :                 LockBuffer(buffer, GIN_UNLOCK);
                                659                 :              0 :                 continue;       /* check it one more */
                                660                 :                :             }
 7102 bruce@momjian.us          661                 :CBC          43 :             break;
                                662                 :                :         }
                                663                 :                : 
                                664         [ -  + ]:              6 :         Assert(PageGetMaxOffsetNumber(page) >= FirstOffsetNumber);
                                665                 :                : 
 7257 teodor@sigaev.ru          666                 :              6 :         itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, FirstOffsetNumber));
 5546 tgl@sss.pgh.pa.us         667                 :              6 :         blkno = GinGetDownlink(itup);
 7102 bruce@momjian.us          668         [ -  + ]:              6 :         Assert(blkno != InvalidBlockNumber);
                                669                 :                : 
 6863 teodor@sigaev.ru          670                 :              6 :         UnlockReleaseBuffer(buffer);
 6344 heikki.linnakangas@i      671                 :              6 :         buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                672                 :                :                                     RBM_NORMAL, info->strategy);
                                673                 :                :     }
                                674                 :                : 
                                675                 :                :     /* right now we found leftmost page in entry's BTree */
                                676                 :                : 
                                677                 :                :     for (;;)
 7102 bruce@momjian.us          678                 :            894 :     {
 3616 kgrittn@postgresql.o      679                 :            937 :         Page        page = BufferGetPage(buffer);
                                680                 :                :         Page        resPage;
                                681                 :                :         uint32      i;
                                682                 :                : 
 7102 bruce@momjian.us          683         [ -  + ]:            937 :         Assert(!GinPageIsData(page));
                                684                 :                : 
 7257 teodor@sigaev.ru          685                 :            937 :         resPage = ginVacuumEntryPage(&gvs, buffer, rootOfPostingTree, &nRoot);
                                686                 :                : 
 7102 bruce@momjian.us          687                 :            937 :         blkno = GinPageGetOpaque(page)->rightlink;
                                688                 :                : 
                                689         [ +  + ]:            937 :         if (resPage)
                                690                 :                :         {
 7257 teodor@sigaev.ru          691                 :            862 :             START_CRIT_SECTION();
 7102 bruce@momjian.us          692                 :            862 :             PageRestoreTempPage(resPage, page);
                                693                 :            862 :             MarkBufferDirty(buffer);
 6858 teodor@sigaev.ru          694                 :            862 :             xlogVacuumPage(gvs.index, buffer);
 7257                           695         [ -  + ]:            862 :             END_CRIT_SECTION();
   24 michael@paquier.xyz       696                 :GNC         862 :             UnlockReleaseBuffer(buffer);
                                697                 :                :         }
                                698                 :                :         else
                                699                 :                :         {
 7257 teodor@sigaev.ru          700                 :CBC          75 :             UnlockReleaseBuffer(buffer);
                                701                 :                :         }
                                702                 :                : 
  397 nathan@postgresql.or      703                 :            937 :         vacuum_delay_point(false);
                                704                 :                : 
 7102 bruce@momjian.us          705         [ +  + ]:            949 :         for (i = 0; i < nRoot; i++)
                                706                 :                :         {
                                707                 :             12 :             ginVacuumPostingTree(&gvs, rootOfPostingTree[i]);
  397 nathan@postgresql.or      708                 :             12 :             vacuum_delay_point(false);
                                709                 :                :         }
                                710                 :                : 
 3189 tgl@sss.pgh.pa.us         711         [ +  + ]:            937 :         if (blkno == InvalidBlockNumber)    /* rightmost page */
 7257 teodor@sigaev.ru          712                 :             43 :             break;
                                713                 :                : 
 6344 heikki.linnakangas@i      714                 :            894 :         buffer = ReadBufferExtended(index, MAIN_FORKNUM, blkno,
                                715                 :                :                                     RBM_NORMAL, info->strategy);
 7102 bruce@momjian.us          716                 :            894 :         LockBuffer(buffer, GIN_EXCLUSIVE);
                                717                 :                :     }
                                718                 :                : 
 4435 heikki.linnakangas@i      719                 :             43 :     MemoryContextDelete(gvs.tmpCxt);
                                720                 :                : 
 3710 tgl@sss.pgh.pa.us         721                 :             43 :     return gvs.result;
                                722                 :                : }
                                723                 :                : 
                                724                 :                : IndexBulkDeleteResult *
                                725                 :             38 : ginvacuumcleanup(IndexVacuumInfo *info, IndexBulkDeleteResult *stats)
                                726                 :                : {
 7102 bruce@momjian.us          727                 :             38 :     Relation    index = info->index;
                                728                 :                :     bool        needLock;
                                729                 :                :     BlockNumber npages,
                                730                 :                :                 blkno;
                                731                 :                :     BlockNumber totFreePages;
                                732                 :                :     GinState    ginstate;
                                733                 :                :     GinStatsData idxStat;
                                734                 :                :     BlockRangeReadStreamPrivate p;
                                735                 :                :     ReadStream *stream;
                                736                 :                : 
                                737                 :                :     /*
                                738                 :                :      * In an autovacuum analyze, we want to clean up pending insertions.
                                739                 :                :      * Otherwise, an ANALYZE-only call is a no-op.
                                740                 :                :      */
 6200 tgl@sss.pgh.pa.us         741         [ +  + ]:             38 :     if (info->analyze_only)
                                742                 :                :     {
  741 heikki.linnakangas@i      743         [ +  + ]:             10 :         if (AmAutoVacuumWorkerProcess())
                                744                 :                :         {
 6200 tgl@sss.pgh.pa.us         745                 :              7 :             initGinState(&ginstate, index);
 3041 rhaas@postgresql.org      746                 :              7 :             ginInsertCleanup(&ginstate, false, true, true, stats);
                                747                 :                :         }
 3710 tgl@sss.pgh.pa.us         748                 :             10 :         return stats;
                                749                 :                :     }
                                750                 :                : 
                                751                 :                :     /*
                                752                 :                :      * Set up all-zero stats and cleanup pending inserts if ginbulkdelete
                                753                 :                :      * wasn't called
                                754                 :                :      */
 7257                           755         [ +  + ]:             28 :     if (stats == NULL)
                                756                 :                :     {
   95 michael@paquier.xyz       757                 :GNC          22 :         stats = palloc0_object(IndexBulkDeleteResult);
 6200 tgl@sss.pgh.pa.us         758                 :CBC          22 :         initGinState(&ginstate, index);
  741 heikki.linnakangas@i      759                 :             22 :         ginInsertCleanup(&ginstate, !AmAutoVacuumWorkerProcess(),
                                760                 :                :                          false, true, stats);
                                761                 :                :     }
                                762                 :                : 
 5628 tgl@sss.pgh.pa.us         763                 :             28 :     memset(&idxStat, 0, sizeof(idxStat));
                                764                 :                : 
                                765                 :                :     /*
                                766                 :                :      * XXX we always report the heap tuple count as the number of index
                                767                 :                :      * entries.  This is bogus if the index is partial, but it's real hard to
                                768                 :                :      * tell how many distinct heap entries are referenced by a GIN index.
                                769                 :                :      */
 2023                           770         [ +  - ]:             28 :     stats->num_index_tuples = Max(info->num_heap_tuples, 0);
 6126                           771                 :             28 :     stats->estimated_count = info->estimated_count;
                                772                 :                : 
                                773                 :                :     /*
                                774                 :                :      * Need lock unless it's local to this backend.
                                775                 :                :      */
 5879                           776   [ +  +  +  - ]:             28 :     needLock = !RELATION_IS_LOCAL(index);
                                777                 :                : 
 7257 teodor@sigaev.ru          778         [ +  + ]:             28 :     if (needLock)
                                779                 :             25 :         LockRelationForExtension(index, ExclusiveLock);
                                780                 :             28 :     npages = RelationGetNumberOfBlocks(index);
                                781         [ +  + ]:             28 :     if (needLock)
                                782                 :             25 :         UnlockRelationForExtension(index, ExclusiveLock);
                                783                 :                : 
 6121 bruce@momjian.us          784                 :             28 :     totFreePages = 0;
                                785                 :                : 
                                786                 :                :     /* Scan all blocks starting from the root using streaming reads */
    3 michael@paquier.xyz       787                 :GNC          28 :     p.current_blocknum = GIN_ROOT_BLKNO;
                                788                 :             28 :     p.last_exclusive = npages;
                                789                 :                : 
                                790                 :                :     /*
                                791                 :                :      * It is safe to use batchmode as block_range_read_stream_cb takes no
                                792                 :                :      * locks.
                                793                 :                :      */
                                794                 :             28 :     stream = read_stream_begin_relation(READ_STREAM_MAINTENANCE |
                                795                 :                :                                         READ_STREAM_FULL |
                                796                 :                :                                         READ_STREAM_USE_BATCHING,
                                797                 :                :                                         info->strategy,
                                798                 :                :                                         index,
                                799                 :                :                                         MAIN_FORKNUM,
                                800                 :                :                                         block_range_read_stream_cb,
                                801                 :                :                                         &p,
                                802                 :                :                                         0);
                                803                 :                : 
 5628 tgl@sss.pgh.pa.us         804         [ +  + ]:CBC        4606 :     for (blkno = GIN_ROOT_BLKNO; blkno < npages; blkno++)
                                805                 :                :     {
                                806                 :                :         Buffer      buffer;
                                807                 :                :         Page        page;
                                808                 :                : 
  397 nathan@postgresql.or      809                 :           4578 :         vacuum_delay_point(false);
                                810                 :                : 
    3 michael@paquier.xyz       811                 :GNC        4578 :         buffer = read_stream_next_buffer(stream, NULL);
                                812                 :                : 
 7257 teodor@sigaev.ru          813                 :CBC        4578 :         LockBuffer(buffer, GIN_SHARE);
  198 peter@eisentraut.org      814                 :GNC        4578 :         page = BufferGetPage(buffer);
                                815                 :                : 
 2649 akorotkov@postgresql      816         [ +  + ]:CBC        4578 :         if (GinPageIsRecyclable(page))
                                817                 :                :         {
 5628 tgl@sss.pgh.pa.us         818         [ -  + ]:           2330 :             Assert(blkno != GIN_ROOT_BLKNO);
 6375 heikki.linnakangas@i      819                 :           2330 :             RecordFreeIndexPage(index, blkno);
 7115 tgl@sss.pgh.pa.us         820                 :           2330 :             totFreePages++;
                                821                 :                :         }
 5628                           822         [ +  + ]:           2248 :         else if (GinPageIsData(page))
                                823                 :                :         {
                                824                 :            108 :             idxStat.nDataPages++;
                                825                 :                :         }
                                826         [ +  - ]:           2140 :         else if (!GinPageIsList(page))
                                827                 :                :         {
                                828                 :           2140 :             idxStat.nEntryPages++;
                                829                 :                : 
 5453 bruce@momjian.us          830         [ +  + ]:           2140 :             if (GinPageIsLeaf(page))
 5628 tgl@sss.pgh.pa.us         831                 :           2125 :                 idxStat.nEntries += PageGetMaxOffsetNumber(page);
                                832                 :                :         }
                                833                 :                : 
 7257 teodor@sigaev.ru          834                 :           4578 :         UnlockReleaseBuffer(buffer);
                                835                 :                :     }
                                836                 :                : 
    3 michael@paquier.xyz       837         [ -  + ]:GNC          28 :     Assert(read_stream_next_buffer(stream, NULL) == InvalidBuffer);
                                838                 :             28 :     read_stream_end(stream);
                                839                 :                : 
                                840                 :                :     /* Update the metapage with accurate page and entry counts */
 5628 tgl@sss.pgh.pa.us         841                 :CBC          28 :     idxStat.nTotalPages = npages;
 2538 heikki.linnakangas@i      842                 :             28 :     ginUpdateStats(info->index, &idxStat, false);
                                843                 :                : 
                                844                 :                :     /* Finally, vacuum the FSM */
 6369                           845                 :             28 :     IndexFreeSpaceMapVacuum(info->index);
                                846                 :                : 
 7115 tgl@sss.pgh.pa.us         847                 :             28 :     stats->pages_free = totFreePages;
                                848                 :                : 
 7257 teodor@sigaev.ru          849         [ +  + ]:             28 :     if (needLock)
                                850                 :             25 :         LockRelationForExtension(index, ExclusiveLock);
                                851                 :             28 :     stats->num_pages = RelationGetNumberOfBlocks(index);
                                852         [ +  + ]:             28 :     if (needLock)
                                853                 :             25 :         UnlockRelationForExtension(index, ExclusiveLock);
                                854                 :                : 
 3710 tgl@sss.pgh.pa.us         855                 :             28 :     return stats;
                                856                 :                : }
                                857                 :                : 
                                858                 :                : /*
                                859                 :                :  * Return whether Page can safely be recycled.
                                860                 :                :  */
                                861                 :                : bool
 2041 andres@anarazel.de        862                 :           4631 : GinPageIsRecyclable(Page page)
                                863                 :                : {
                                864                 :                :     TransactionId delete_xid;
                                865                 :                : 
                                866         [ -  + ]:           4631 :     if (PageIsNew(page))
 2041 andres@anarazel.de        867                 :UBC           0 :         return true;
                                868                 :                : 
 2041 andres@anarazel.de        869         [ +  + ]:CBC        4631 :     if (!GinPageIsDeleted(page))
                                870                 :           2242 :         return false;
                                871                 :                : 
                                872                 :           2389 :     delete_xid = GinPageGetDeleteXid(page);
                                873                 :                : 
                                874         [ +  + ]:           2389 :     if (!TransactionIdIsValid(delete_xid))
                                875                 :           2383 :         return true;
                                876                 :                : 
                                877                 :                :     /*
                                878                 :                :      * If no backend still could view delete_xid as in running, all scans
                                879                 :                :      * concurrent with ginDeletePostingPage() must have finished.
                                880                 :                :      */
                                881                 :              6 :     return GlobalVisCheckRemovableXid(NULL, delete_xid);
                                882                 :                : }
        

Generated by: LCOV version 2.4-beta